summaryrefslogtreecommitdiffstats
path: root/knode
diff options
context:
space:
mode:
Diffstat (limited to 'knode')
-rw-r--r--knode/.kateconfig2
-rw-r--r--knode/AUTHORS50
-rw-r--r--knode/ChangeLog294
-rw-r--r--knode/KNode.desktop85
-rw-r--r--knode/Makefile.am184
-rw-r--r--knode/README15
-rw-r--r--knode/TODO4
-rw-r--r--knode/aboutdata.cpp64
-rw-r--r--knode/aboutdata.h34
-rw-r--r--knode/articlewidget.cpp1474
-rw-r--r--knode/articlewidget.h269
-rw-r--r--knode/csshelper.cpp42
-rw-r--r--knode/csshelper.h30
-rw-r--r--knode/filters/1.fltr42
-rw-r--r--knode/filters/2.fltr42
-rw-r--r--knode/filters/3.fltr42
-rw-r--r--knode/filters/4.fltr42
-rw-r--r--knode/filters/5.fltr42
-rw-r--r--knode/filters/6.fltr42
-rw-r--r--knode/filters/7.fltr42
-rw-r--r--knode/filters/8.fltr42
-rw-r--r--knode/filters/Makefile.am4
-rw-r--r--knode/filters/filters.rc2
-rw-r--r--knode/headers.rc35
-rw-r--r--knode/headerview.cpp617
-rw-r--r--knode/headerview.h120
-rw-r--r--knode/hi128-app-knode.pngbin0 -> 9963 bytes
-rw-r--r--knode/hi128-app-knode2.pngbin0 -> 20423 bytes
-rw-r--r--knode/hi16-app-knode.pngbin0 -> 768 bytes
-rw-r--r--knode/hi16-app-knode2.pngbin0 -> 914 bytes
-rw-r--r--knode/hi32-app-knode.pngbin0 -> 1646 bytes
-rw-r--r--knode/hi32-app-knode2.pngbin0 -> 2429 bytes
-rw-r--r--knode/hi48-app-knode.pngbin0 -> 2956 bytes
-rw-r--r--knode/hi48-app-knode2.pngbin0 -> 4811 bytes
-rw-r--r--knode/hi64-app-knode.pngbin0 -> 4414 bytes
-rw-r--r--knode/hi64-app-knode2.pngbin0 -> 7300 bytes
-rw-r--r--knode/knaccountmanager.cpp305
-rw-r--r--knode/knaccountmanager.h93
-rw-r--r--knode/knapplication.cpp88
-rw-r--r--knode/knapplication.h34
-rw-r--r--knode/knarticle.cpp576
-rw-r--r--knode/knarticle.h337
-rw-r--r--knode/knarticlecollection.cpp403
-rw-r--r--knode/knarticlecollection.h120
-rw-r--r--knode/knarticlefactory.cpp1111
-rw-r--r--knode/knarticlefactory.h124
-rw-r--r--knode/knarticlefilter.cpp383
-rw-r--r--knode/knarticlefilter.h80
-rw-r--r--knode/knarticlemanager.cpp1090
-rw-r--r--knode/knarticlemanager.h122
-rw-r--r--knode/knarticlewindow.cpp131
-rw-r--r--knode/knarticlewindow.h47
-rw-r--r--knode/kncleanup.cpp319
-rw-r--r--knode/kncleanup.h72
-rw-r--r--knode/kncollection.cpp50
-rw-r--r--knode/kncollection.h70
-rw-r--r--knode/kncollectionview.cpp457
-rw-r--r--knode/kncollectionview.h93
-rw-r--r--knode/kncollectionviewitem.cpp172
-rw-r--r--knode/kncollectionviewitem.h56
-rw-r--r--knode/kncomposer.cpp2661
-rw-r--r--knode/kncomposer.h384
-rw-r--r--knode/kncomposerui.rc102
-rw-r--r--knode/knconfig.cpp1252
-rw-r--r--knode/knconfig.h566
-rw-r--r--knode/knconfigmanager.cpp133
-rw-r--r--knode/knconfigmanager.h83
-rw-r--r--knode/knconfigpages.cpp198
-rw-r--r--knode/knconfigpages.h79
-rw-r--r--knode/knconfigwidgets.cpp2627
-rw-r--r--knode/knconfigwidgets.h730
-rw-r--r--knode/knconvert.cpp447
-rw-r--r--knode/knconvert.h126
-rw-r--r--knode/kndisplayedheader.cpp175
-rw-r--r--knode/kndisplayedheader.h63
-rw-r--r--knode/knewsservice.protocol13
-rw-r--r--knode/knfilterconfigwidget.cpp93
-rw-r--r--knode/knfilterconfigwidget.h53
-rw-r--r--knode/knfilterdialog.cpp125
-rw-r--r--knode/knfilterdialog.h54
-rw-r--r--knode/knfiltermanager.cpp396
-rw-r--r--knode/knfiltermanager.h106
-rw-r--r--knode/knfolder.cpp601
-rw-r--r--knode/knfolder.h103
-rw-r--r--knode/knfoldermanager.cpp482
-rw-r--r--knode/knfoldermanager.h91
-rw-r--r--knode/knglobals.cpp114
-rw-r--r--knode/knglobals.h95
-rw-r--r--knode/kngroup.cpp1112
-rw-r--r--knode/kngroup.h200
-rw-r--r--knode/kngroupbrowser.cpp481
-rw-r--r--knode/kngroupbrowser.h117
-rw-r--r--knode/kngroupdialog.cpp335
-rw-r--r--knode/kngroupdialog.h60
-rw-r--r--knode/kngroupmanager.cpp704
-rw-r--r--knode/kngroupmanager.h142
-rw-r--r--knode/kngrouppropdlg.cpp181
-rw-r--r--knode/kngrouppropdlg.h59
-rw-r--r--knode/kngroupselectdialog.cpp172
-rw-r--r--knode/kngroupselectdialog.h63
-rw-r--r--knode/knhdrviewitem.cpp294
-rw-r--r--knode/knhdrviewitem.h67
-rw-r--r--knode/knjobdata.cpp147
-rw-r--r--knode/knjobdata.h141
-rw-r--r--knode/knmainwidget.cpp2215
-rw-r--r--knode/knmainwidget.h426
-rw-r--r--knode/knmemorymanager.cpp217
-rw-r--r--knode/knmemorymanager.h74
-rw-r--r--knode/knnetaccess.cpp545
-rw-r--r--knode/knnetaccess.h102
-rw-r--r--knode/knnntpaccount.cpp229
-rw-r--r--knode/knnntpaccount.h117
-rw-r--r--knode/knnntpclient.cpp748
-rw-r--r--knode/knnntpclient.h57
-rw-r--r--knode/knode.cpp122
-rw-r--r--knode/knode.h70
-rw-r--r--knode/knode_config_accounts.desktop118
-rw-r--r--knode/knode_config_appearance.desktop122
-rw-r--r--knode/knode_config_cleanup.desktop113
-rw-r--r--knode/knode_config_identity.desktop124
-rw-r--r--knode/knode_config_post_news.desktop62
-rw-r--r--knode/knode_config_privacy.desktop107
-rw-r--r--knode/knode_config_read_news.desktop66
-rw-r--r--knode/knode_options.h30
-rw-r--r--knode/knode_part.cpp125
-rw-r--r--knode/knode_part.h64
-rw-r--r--knode/knodecomposeriface.h14
-rw-r--r--knode/knodeiface.h152
-rw-r--r--knode/knodeui.rc256
-rw-r--r--knode/knprotocolclient.cpp634
-rw-r--r--knode/knprotocolclient.h122
-rw-r--r--knode/knrangefilter.cpp228
-rw-r--r--knode/knrangefilter.h88
-rw-r--r--knode/knreaderui.rc81
-rw-r--r--knode/knscoring.cpp142
-rw-r--r--knode/knscoring.h76
-rw-r--r--knode/knsearchdialog.cpp122
-rw-r--r--knode/knsearchdialog.h58
-rw-r--r--knode/knserverinfo.cpp188
-rw-r--r--knode/knserverinfo.h91
-rw-r--r--knode/knsourceviewwindow.cpp62
-rw-r--r--knode/knsourceviewwindow.h33
-rw-r--r--knode/knstatusfilter.cpp232
-rw-r--r--knode/knstatusfilter.h99
-rw-r--r--knode/knstringfilter.cpp165
-rw-r--r--knode/knstringfilter.h82
-rw-r--r--knode/knwidgets.cpp159
-rw-r--r--knode/knwidgets.h88
-rw-r--r--knode/main.cpp44
-rw-r--r--knode/pics/Makefile.am12
-rw-r--r--knode/pics/article.pngbin0 -> 751 bytes
-rw-r--r--knode/pics/cr16-action-mail_get_all.pngbin0 -> 902 bytes
-rw-r--r--knode/pics/cr16-action-message_reply.pngbin0 -> 1052 bytes
-rw-r--r--knode/pics/cr22-action-mail_get_all.pngbin0 -> 1406 bytes
-rw-r--r--knode/pics/cr22-action-message_reply.pngbin0 -> 1457 bytes
-rw-r--r--knode/pics/cr32-action-mail_get_all.pngbin0 -> 2401 bytes
-rw-r--r--knode/pics/cr32-action-message_reply.pngbin0 -> 2444 bytes
-rw-r--r--knode/pics/crsc-action-mail_get_all.svg184
-rw-r--r--knode/pics/ctlart.pngbin0 -> 338 bytes
-rw-r--r--knode/pics/eyes.pngbin0 -> 480 bytes
-rw-r--r--knode/pics/greyball.pngbin0 -> 449 bytes
-rw-r--r--knode/pics/greyballchk.pngbin0 -> 478 bytes
-rw-r--r--knode/pics/group.pngbin0 -> 751 bytes
-rw-r--r--knode/pics/group_big.pngbin0 -> 1150 bytes
-rw-r--r--knode/pics/ignore.pngbin0 -> 596 bytes
-rw-r--r--knode/pics/mail.pngbin0 -> 1001 bytes
-rw-r--r--knode/pics/newsubs.pngbin0 -> 413 bytes
-rw-r--r--knode/pics/pgp-keys.pngbin0 -> 3877 bytes
-rw-r--r--knode/pics/posting.pngbin0 -> 725 bytes
-rw-r--r--knode/pics/snderr.pngbin0 -> 395 bytes
-rw-r--r--knode/pics/stat_cncl.pngbin0 -> 516 bytes
-rw-r--r--knode/pics/stat_edit.pngbin0 -> 437 bytes
-rw-r--r--knode/pics/stat_saved.pngbin0 -> 337 bytes
-rw-r--r--knode/pics/stat_sent.pngbin0 -> 468 bytes
-rw-r--r--knode/resource.h43
-rw-r--r--knode/smtpaccountwidget_base.ui232
-rw-r--r--knode/utilities.cpp478
-rw-r--r--knode/utilities.h166
178 files changed, 36405 insertions, 0 deletions
diff --git a/knode/.kateconfig b/knode/.kateconfig
new file mode 100644
index 000000000..125718d3e
--- /dev/null
+++ b/knode/.kateconfig
@@ -0,0 +1,2 @@
+// folder-wide kate configuration
+// kate: space-indent on; indent-width 2; remove-trailing-space on; remove-trailing-space-save on;
diff --git a/knode/AUTHORS b/knode/AUTHORS
new file mode 100644
index 000000000..2f9258c85
--- /dev/null
+++ b/knode/AUTHORS
@@ -0,0 +1,50 @@
+Developers:
+* Christian Thurner <cthurner@freepage.de>
+* Christian Gebauer <gebauer@kde.org>
+* Mathias Waack <mathias@atoll-net.de>
+* Dirk Mueller <mueller@kde.org>
+* Matthias Kalle Dalheimer <kalle@kde.org>
+* Roberto Teixeira <maragato@kde.org>
+* Volker Krause <volker.krause@rwth-aachen.de>
+
+Documentation:
+* Stephan Johach <lucardus@onlinehome.de>
+* Rainer Endres <endres@physos.com>
+* Thomas Schuetz <Thomas.Schuetz@gmx.li>
+* Nils Holland <nils@nightcastleproductions.org>
+
+Translators:
+* Wolfram Diestel <wolfram@steloj.de>
+* Norbert Popiolek <kdesktop@enter.net.pl>
+* Chih-Wei Huang <cwhuang@linux.org.tw>
+* Otto Bruggeman <otto.bruggeman@home.nl>
+* Gaute Hvoslef Kvalnes <ai98ghk@stud.hib.no>
+* Mattias Newzella <newzella@linux.nu>
+* Andris Maziks <andris.m@delfi.lv>
+* Jaime Robles <jaime@kde.org>
+* Hasso Tepper <hasso@estpak.ee>
+* José Carlos Monteiro <jcm@netcabo.pt>
+* Alessandro Astarita <aleast@capri.it>
+* Gregor Rakar <gregor.rakar@kiss.uni-lj.si>
+* Andrey S. Cherepanov <andrey_tiger@i.am>
+* Eugenijus Paulauskas <filija@klaipeda.omnitel.net>
+* Vasif İsmayıloğlu <azerb_linux@hotmail.com>
+* Li Zongliang <li-zongliang@21cn.com>
+* Claudiu Costin <claudiuc@geocities.com>
+* Erik K. Pedersen <erik@binghamton.edu>
+* Stanislav Visnovsky <visnovsky@nenya.ms.mff.cuni.cz>
+* Andy Rysin <arysin@yahoo.com>
+* Elvis Pfützenreuter <epx@conectiva.com>
+* Delafond <g.delafond@medsyn.fr>
+* Ludovic Grossard <ludovic.grossard@libertysurf.fr>
+* Dimitris Kamenopoulos <el97146@mail.ntua.gr>
+* Meni Livne <meni@mail.com>
+* Stephan Johach <lucardus@onlinehome.de>
+* Kim Enkovaara <kim.enkovaara@iki.fi>
+* Christian A Strømmen [Number1/NumeroUno]
+* Milan Hejpetr <mhejpetr@iss.cz>
+* Sinohara <shinobo@leo.bekkoame.ne.jp>
+* Sebastià Pla <sastia@retemail.es>
+* Tamas Szanto <tszanto@mol.hu>
+* Bjarni R. Einarsson <bre@netverjar.is>
+* Oğuz YILMAZ <o.yilmaz@ieee.org>
diff --git a/knode/ChangeLog b/knode/ChangeLog
new file mode 100644
index 000000000..f9585f5f5
--- /dev/null
+++ b/knode/ChangeLog
@@ -0,0 +1,294 @@
+changes between 0.6 and 0.7
++ due personal reasons KNode 0.7 is just a port of
+ KNode 0.6 to KDE3
+
+changes between 0.4 and 0.6
++ New scoring system:
+ - User definable scoring rules with conditions based
+ on the overview data and configurable actions (currently
+ score value adjustment and notification)
+ - Full-fledged regular expressions which can be used in
+ scoring and filter conditions.
++ Reduced memory consumption. Its now configurable how much RAM
+ is used to cache article bodies and headers.
++ KNode now uses dock widgets for its views, the user can
+ arrange them in any way he wants. Added the shortcuts
+ 'g', 'h' and 'j' to switch to group, header and article view.
++ Improved article handling:
+ - User-defined folders
+ - DnD between folders and groups
+ - Convenient mbox-folder import/export
+ - Nice in-place renaming of accounts, folders and groups
+ - New menu option that expires all groups of the current account
+ and a option that compacts all folders on demand.
+ - New expire-feature: delete article headers that
+ aren't available on the server
+ - Implemented "Smart Scrolling" (TM) in the listviews and in
+ the groupbrowser
+ - added option to expand all threads by default
+ - a new tab "Navigation" in the configuration dialog:
+ + its possible emulate cursor key behavior of KMail
+ + "mark all read", "mark thread as read" and "ignore thread"
+ can now trigger additional actions. (close thread, go to
+ next thread/group)
++ Reading news articles:
+ - Proper handling of crossposted articles, they are marked
+ as read simutaneously in all groups.
+ - ignored articles are now automatically marked as read
+ - Added option to retrieve the article source
+ - New menu option: fetch an article by message-id
+ - Added option to retrieve an article from groups.google.com
+ if it's unavailable on the local server
+ - Trailing empty lines can be stripped from incoming articles
+ - Highlighting of "news:" and "mailto:" urls, all email-addresses
+ and msgids
+ - Its now configurable what characters are recognized as quote signs
+ - Its no possible to disable the fancy header decorations,
+ saves space on small displays
+ - The word wrap behavior of the article widget is
+ now configurable
+ - added a menuoption and a keyboard shortcut that switches
+ from proportional to fixed font in the article viewer
+ - its now possible to deactivate the tree view in the group subscription dialog.
++ Enhancements of the article composer
+ - Optional integration of KMail and other external
+ mail programs
+ - Full support for the Mail-Copies-To header
+ - Added placeholder for the group name in the attribution line
+ - Support for dynamically generated signatures
+ - Added option to place the cursor below the attribution line,
+ not above the signature (off by default)
+ - Implemented forwarding of articles with attachments
+ - Files can now be attached to an article with Drag&Drop
++ Improved article filters and search function:
+ - Filter rules for numerical values are more flexible now,
+ its possible to filter something like "x < value" or
+ "x > value", not just ranges.
+ - Its now possible to filter on the message-id and references header
+ - The search function can show complete threads now (optionally)
+ - Its now possible to search in folders
++ Network related improvements:
+ - Articles are no fetched via article number instead of article-id
+ to avoid problems with broken newsservers
+ - Enhanced dupe protection, utilizing the "recommened id" feature
+ of newer inn's
+ - Shows a password-dialog when the server requests authentication
+ - Support for IPv6 and SOCKS-proxies
+
+changes between 0.3.3 and 0.4
+- full support for non iso-8859-x charsets
+- basic PGP/GnuPG support
+- full GNKSA compliance
+- followup and reply at same time now possible
+- the moderation status of newsgroups is detected and displayed
+- rot13 de-/encoding
+- server specific identities
+- selection of multiple articles/groups in the listview
+- improved keyboard handling in the configuration dialog
+- selection dialogs for sort column (F7) and filter (F6) that
+ can be reached with a keyboard shortcut
+- the article line count can now be displayed in the listview
+- improved color and font configuration
+- the interpretation of text format tags can now be disabled
+- support for other webbrowsers (Mozilla, Opera, user-defined)
+- added a context menu for the article pane
+- improvements for the composer:
+ + switching from news article to mail and back on the fly
+ + paste as quotation
+ + add/remove quotation
+ + box quote support
+ + status bar
+ + word wrap can now be disabled
+ + it's now possible to get the original text if
+ the automatic rewrap has gone wrong
+ + the user can determine which part of the original
+ message is quoted by selecting this part before hitting reply
+ + implemented a lot of sanity checks.
+- changed the way the draft folder works, articles
+ can now be saved as draft without any sanity checks,
+ but they have to be edited in the composer again
+ before the are sent.
+- '>' instead of '> ' as quote prefix for quote levels >= 2
+- numerous bugfixes
+- code cleanups
+
+changes between 0.3.2 and 0.3.3
+- bugfix for networking on solaris
+- bugfix for continuous reconnection on
+ news servers with authentication
+- sane default window sizes
+- correct default for the smtp-server port
+- bugfix for a problem with some smtp servers
+- fixes for non-iso-8859-x users
+- buxfix for the display of plain text attachments
+
+changes between 0.1.13 and 0.3.2
+- ported to KDE2, new style guide compilant XML-GUI
+- multithreaded network-code
+- correct MIME-handling including multipart-messages
+- support for uuencoded binaries
+- ability to display attachments inline
+- posting of MIME multipart-messages (attachments)
+- significantly improved composer:
+ + all standard editor features (find, replace, select all, ...)
+ + spellchecker
+ + optional external editor
+ + nice attachment handling
+ + insert file functionality
+ + quoted text can be rewarped to preserve the original quoting.
+ + improved signature handling,
+ exiting sig dashes are detected
+ optional: direct entry of the signature in the configuration dialog
+ + tab key works ;-)
+- improved X-Header configuration, its possible to disable
+ the User-Agent identification header now.
+- rewritten configuration dialog
+- support for multiple newsservers
+- usage of the standard mbox format for folders
+- cancel/supersede function
+- forward articles as e-mail
+- rewritten subscribe dialog
+ + nicer interface with treeview
+ + shows group descriptions
+ + can check for new newsgroups now (NEWGROUPS)
+- ability to freely adjust the displayed article headers,
+ including custom names and format options.
+- improved font & color configuration,
+ default colors adapt to all color schemes (including inverse ones)
+- markup like *word* _word_ and /word/ is supported now
+- you can use netscape instead of konqueror for links
+- support for news://server/group urls as commmand line argument
+- vastly improved new documentation (with screenshots)
+ written by Stephan Johach
+- implemented search functionality for newsgroups
+- added placeholders for own name and email to the filter configuration
+- new standard filters (own articles, threads with own articles)
+- header names and standard filter names are translatable now
+- many new translations, thanks to the numerous translators ;-)
+
+changes between 0.1.12 and 0.1.13
+- fixed the "wheelmouse-bug"
+- fixed a bug that produced doubled subjects
+- fixed some minor bugs
+- added "allow 8-bit characters in header" as an option
+- the whole keyboard stuff has been greatly improved:
+ Christian Gebauer has rewritten almost the whole hotkey
+ management, making it much more convinient to control
+ KNode with the keyboard.
+ The changes are:
+ + a cursor has been added, that let's you scroll through
+ the articles without selecting each of them
+ + if you hit the return-key the article, the cursor currently
+ points at is selected and displayed
+ + the space-key jumps to the next unread article AND scrolls
+ the article view if it's necessary
+- added two new functions :
+ + watch thread (set score=100), hotkey 'W'
+ + ignore thread (set score=0), hotkey 'I'
+- added a new standard-filter for watched threads (score=100)
+- added translations: german, italian, spanish
+
+changes between 0.1.11 and 0.1.12
+- fixed numerous bugs
+- fixed that annoying "Unknown charset"-bug
+- 8bit characters in the header of outgoing messages
+ are now encoded correctly (=?<chareset>?Q?<encoded word>?=)
+- added "Resort" to the Group-menu
+- added a new function : "Open article in own window" :
+ + Added an Option to the Article-menu
+ + A doubleclick on an article opens it in a new window
+ + If you click on a "reference-link" using the middle button, the
+ reference is opened in a new window. This behavior is similar to
+ the "open link in new window"-function of a webbrowser.
+- added a "focus-indicator" marking the pane that currently
+ has got the focus
+- added support for the common commandline-arguments -h and -v
+ (patch by Christian Gebauer)
+- now KNode uses "X-Newsreader" for postings and "X-Mailer" for emails
+ (patch by Christian Gebauer)
+- finally some documentation has been added :-)
+
+changes between 0.1.10 and 0.1.11
+- fixed some minor bugs
+- nntp-authentication works now
+- an article can now be saved as a text-file
+- rewrite of the "FromLineParser"
+- fixed a bug in the expire-mechanism
+- fixed a bug concerning additional X-Headers
+- added "charset" to the pref-dialog (tab "post news")
+- added properties for groups : nicknames, group-specific settings
+ for name, email, reply-to and organization
+- the Message-View can display different charsets
+
+changes between 0.1.9 and 0.1.10
+- added support for servers that require athentication (EXPERIMENTAL !!)
+- fixed some bugs in the message view
+- fixed two bugs in the "FromLineParser"
+- now all hotkeys are disabled/enabled correctly
+- some changes in the pref-dialog
+- added support for custom X-Headers
+- added new option "show whole thread on expanding"
+- now all dialogs remember their size
+- added "next/prev group" to the "Goto-Menu"
+- the signature-file can now be chosen
+- a lot of minor changes and bugfixes
+
+changes between 0.1.8 and 0.1.9:
+- the read articles counter works correctly again
+- fixed a bug in the folder-manager, that made knode loose
+ saved articles
+- added "goto next/prev article"
+- added an option to use the same font in the message-view
+ and composer
+- added support for regular expressions in filtering
+- some minor changes and bugfixes
+
+changes between 0.1.7 and 0.1.8:
+- a lot of bugfixes
+- now the message-view handles links (http)
+- added support for cursor-keys (scrolling)
+- added wheelmouse-support (imwheel)
+- added support for the ~./.signature file
+- some other little improvements
+
+changes between 0.1.6 and 0.1.7:
+- added scoring support
+- added configurable key bindings
+- added article-navigation (next unread article, next unread thread)
+- added "expand all / collapse all"
+- added "download all"
+- added "-lz" to makefile.am (needed for libpng)
+- fixed some bugs in the message-view
+- fixed a bug in the pref-dialog
+- fixed a bug in the group-list : now the unread articles are
+ *really* counted correctly
+
+changes between 0.1.5 and 0.1.6:
+- only one bugfix : now the unread articles are counted correctly again
+
+changes between 0.1.1 and 0.1.5 :
+- fixed a bug in the message view, that made KNode crash under
+ certain circumstances
+- fixed a bug in the nntp-client; knode now works with inn
+- the groups in the left-hand window are sorted alphabetically
+ now
+- changed the behavior of the clickable From-Header in the
+ message view; now the reply-To address is used if present
+- added the item "mail reply" to the popup-menu in the
+ message list
+- improved the message list:
+ * new messages are displayed as bold
+ * threads with no unread articles get grey text
+ * threads with new messages are marked with a little
+ arrow
+- added printing-support
+- seleceted text in the message view is now copied into the
+ x-clipboard
+- the message list now remembers it's sorting
+- added customizable filters
+- added an "expire now" item to the group-menu
+- added "Fonts & Colors" to the preferences-dialog
+
+changes between 0.1 and 0.1.1 :
+- the menu-item "View->show Threads" behaves now like it should
+- the mailto- and references-links in the message view work now
diff --git a/knode/KNode.desktop b/knode/KNode.desktop
new file mode 100644
index 000000000..dda0bb6b9
--- /dev/null
+++ b/knode/KNode.desktop
@@ -0,0 +1,85 @@
+[Desktop Entry]
+Type=Application
+Exec=knode -caption "%c" %i %m
+Icon=knode
+Terminal=false
+DocPath=knode/index.html
+Name=KNode
+Name[eo]=Diskutrondoj
+Name[hi]=के-नोड
+Name[ne]=केडीई नोड
+Name[ro]=Ştiri Internet
+Name[sv]=Knode
+Name[ta]=Kநோடு
+Name[th]=อ่านข่าว - K
+Name[zh_TW]=KNode 新聞閱讀器
+GenericName=News Reader
+GenericName[af]=Nuus leser
+GenericName[ar]=قارئ أخبار
+GenericName[az]=Xəbər Oxuyucu
+GenericName[bg]=Четец на новини
+GenericName[br]=Lenner keleier
+GenericName[bs]=Program za čitanje USENet grupa
+GenericName[ca]=Lector de notícies
+GenericName[cs]=Klient pro čtení diskusních skupin
+GenericName[cy]=Darllenydd Newyddion
+GenericName[da]=Nyhedslæser
+GenericName[de]=Usenet-Newsreader
+GenericName[el]=Αναγνώστης νέων
+GenericName[eo]=Legilo por diskutrondoj
+GenericName[es]=Lector de noticias
+GenericName[et]=Uudistelugeja
+GenericName[eu]=Berri Irakurlea
+GenericName[fa]=خوانندۀ اخبار
+GenericName[fi]=Uutisryhmien lukuohjelma
+GenericName[fr]=Lecteur de forums de discussion
+GenericName[fy]=Nijslêzer
+GenericName[ga]=Léitheoir Nuachta
+GenericName[gl]=Lector de Noticias
+GenericName[he]=קורא חדשות
+GenericName[hi]=समाचार वाचक
+GenericName[hr]=Program za čitanje USENet grupa
+GenericName[hu]=NNTP hírolvasó
+GenericName[id]=Pembaca Berita
+GenericName[is]=Fréttaforrit
+GenericName[it]=Lettore newsgroup
+GenericName[ja]=ニュースリーダー
+GenericName[ka]=სიახლეების წამკითხველი
+GenericName[kk]=Жаңалықтарды оқу құралы
+GenericName[km]=កម្មវិធី​អាន​ព័ត៌មាន
+GenericName[lt]=Naujienų skaityklė
+GenericName[lv]=Ziņu Lasītājs
+GenericName[mk]=Читач на вести
+GenericName[ms]=Pembaca Berita
+GenericName[mt]=Qarrej tal-aħbarijiet (news)
+GenericName[nb]=Njusleser
+GenericName[nds]=Usenet-Narichtenkieker
+GenericName[ne]=समाचार वाचक
+GenericName[nl]=Nieuwslezer
+GenericName[nn]=Nyheitslesar
+GenericName[pl]=Program do czytania list dyskusyjnych
+GenericName[pt]=Leitor de Notícias
+GenericName[pt_BR]=Leitor de Notícias
+GenericName[ro]=Cititor de ştiri USENET
+GenericName[ru]=Чтение новостей
+GenericName[rw]=Musoma Amakuru
+GenericName[se]=Ođaslogan
+GenericName[sk]=Prehliadač správ
+GenericName[sl]=Bralnik novic
+GenericName[sr]=Читач вести
+GenericName[sr@Latn]=Čitač vesti
+GenericName[sv]=Nyhetsläsare
+GenericName[ta]=செய்தி வாசிப்பவர்
+GenericName[tg]=Барнома барои хондани иттилоот
+GenericName[th]=เครื่องมืออ่านข่าว
+GenericName[tr]=Haber Okuyucu
+GenericName[uk]=Програма для перегляду новин
+GenericName[ven]=Muvhali wa mafhungo
+GenericName[vi]=Trình đọc News
+GenericName[xh]=Umfundi weendaba
+GenericName[zh_CN]=新闻阅读器
+GenericName[zh_TW]=新聞閱讀器
+GenericName[zu]=Umfundi wezindaba
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Unique
+Categories=Qt;KDE;Network;News;
diff --git a/knode/Makefile.am b/knode/Makefile.am
new file mode 100644
index 000000000..f17631453
--- /dev/null
+++ b/knode/Makefile.am
@@ -0,0 +1,184 @@
+KDE_CXXFLAGS = $(USE_THREADS)
+
+INCLUDES= -I$(top_srcdir)/libkmime \
+ -I$(top_srcdir)/libkdepim \
+ -I$(top_srcdir)/libkpgp \
+ -I$(top_srcdir)/libemailfunctions \
+ -I$(top_srcdir) \
+ $(all_includes)
+
+lib_LTLIBRARIES = libknodecommon.la
+
+kde_module_LTLIBRARIES = kcm_knode.la libknodepart.la
+libknodepart_la_LDFLAGS = -module -avoid-version -no-undefined $(all_libraries) $(KDE_RPATH) $(KDE_PLUGIN)
+libknodepart_la_LIBADD = libknodecommon.la
+
+SUBDIRS = pics filters
+
+bin_PROGRAMS = knode
+
+knode_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+knode_LDADD = libknodecommon.la
+
+kcm_knode_la_SOURCES = knconfigpages.cpp
+kcm_knode_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+kcm_knode_la_LIBADD = libknodecommon.la $(LIB_KDECORE)
+knconfigpages.lo: smtpaccountwidget_base.h
+
+libknodecommon_la_LDFLAGS = -version-info 3:0:0 -no-undefined $(all_libraries)
+libknodecommon_la_LIBADD = $(top_builddir)/libkmime/libkmime.la $(top_builddir)/libkpgp/libkpgp.la $(top_builddir)/libkdepim/libkdepim.la $(LIB_KSPELL) $(LIB_KABC) $(LIB_KFILE) $(LIB_KUTILS) $(LIBRESOLV) $(LIB_KHTML)
+libknodecommon_la_SOURCES = knconfigmanager.cpp \
+ knconfig.cpp \
+ knconfigwidgets.cpp \
+ \
+ knnetaccess.cpp \
+ knprotocolclient.cpp \
+ knnntpclient.cpp \
+ knjobdata.cpp \
+ \
+ knaccountmanager.cpp \
+ kncollection.cpp \
+ kncollectionviewitem.cpp \
+ knserverinfo.cpp \
+ knnntpaccount.cpp \
+ \
+ kngroupmanager.cpp \
+ knarticlecollection.cpp \
+ kngroup.cpp \
+ kngroupbrowser.cpp \
+ kngroupselectdialog.cpp \
+ kngroupdialog.cpp \
+ kngrouppropdlg.cpp \
+ \
+ knfoldermanager.cpp \
+ knfolder.cpp \
+ \
+ knmemorymanager.cpp \
+ kncleanup.cpp \
+ knconvert.cpp \
+ \
+ knarticlemanager.cpp \
+ knarticle.cpp \
+ kndisplayedheader.cpp \
+ knsourceviewwindow.cpp \
+ knarticlewindow.cpp \
+ knhdrviewitem.cpp \
+ \
+ kncomposer.cpp \
+ knarticlefactory.cpp \
+ \
+ knfiltermanager.cpp \
+ knstatusfilter.cpp \
+ knstringfilter.cpp \
+ knrangefilter.cpp \
+ knarticlefilter.cpp \
+ knfilterconfigwidget.cpp \
+ knfilterdialog.cpp \
+ knsearchdialog.cpp \
+ \
+ utilities.cpp \
+ \
+ knscoring.cpp \
+ \
+ knwidgets.cpp \
+ headerview.cpp \
+ knmainwidget.cpp \
+ aboutdata.cpp \
+ knglobals.cpp \
+ knodecomposeriface.skel \
+ knodeiface.skel \
+ kncollectionview.cpp \
+ articlewidget.cpp \
+ csshelper.cpp \
+ smtpaccountwidget_base.ui
+libknodecommon_la_COMPILE_FIRST = smtpaccountwidget_base.h
+
+knode_SOURCES = knode.cpp knapplication.cpp main.cpp
+
+libknodepart_la_SOURCES = knode_part.cpp
+
+
+noinst_HEADERS = knconfigmanager.h \
+ knconfig.h \
+ knconfigwidgets.h \
+ \
+ knnetaccess.h \
+ knprotocolclient.h \
+ knnntpclient.h \
+ knjobdata.h \
+ \
+ knaccountmanager.h \
+ kncollection.h \
+ kncollectionviewitem.h \
+ knserverinfo.h \
+ knnntpaccount.h \
+ \
+ kngroupmanager.h \
+ knarticlecollection.h \
+ kngroup.h \
+ kngroupbrowser.h \
+ kngroupselectdialog.h \
+ kngroupdialog.h \
+ kngrouppropdlg.h \
+ \
+ knfoldermanager.h \
+ knfolder.h \
+ \
+ knmemorymanager.h \
+ kncleanup.h \
+ knconvert.h \
+ \
+ knarticlemanager.h \
+ knarticle.h \
+ kndisplayedheader.h \
+ knsourceviewwindow.h \
+ knarticlewindow.h \
+ knhdrviewitem.h \
+ \
+ kncomposer.h \
+ knarticlefactory.h \
+ \
+ knfiltermanager.h \
+ knstatusfilter.h \
+ knstringfilter.h \
+ knrangefilter.h \
+ knarticlefilter.h \
+ knfilterconfigwidget.h \
+ knfilterdialog.h \
+ knsearchdialog.h \
+ \
+ utilities.h \
+ \
+ knscoring.h \
+ \
+ knwidgets.h \
+ headerview.h \
+ knode.h \
+ knapplication.h \
+ resource.h \
+ knglobals.h \
+ aboutdata.h \
+ knmainwidget.h \
+ kncollectionview.h \
+ articlewidget.h \
+ csshelper.h
+
+
+knode_METASOURCES = AUTO
+
+xdg_apps_DATA = KNode.desktop
+
+rcdir = $(kde_datadir)/knode
+rc_DATA = headers.rc knodeui.rc knreaderui.rc kncomposerui.rc
+
+knewsprotocol_DATA = knewsservice.protocol
+knewsprotocoldir = $(kde_servicesdir)
+
+kde_services_DATA = knode_config_identity.desktop knode_config_accounts.desktop knode_config_appearance.desktop knode_config_read_news.desktop knode_config_post_news.desktop knode_config_privacy.desktop knode_config_cleanup.desktop
+
+KDE_ICON = AUTO
+
+EXTRA_DIST = KNode.desktop
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/knode.pot
diff --git a/knode/README b/knode/README
new file mode 100644
index 000000000..c821dc9f5
--- /dev/null
+++ b/knode/README
@@ -0,0 +1,15 @@
+KNode is a newsreader for the K Desktop Environment.
+
+It is GNKSA compliant (unfortunally a review is still
+pending), and has support for MIME and multiple servers.
+
+It is a online-reader, but in combination with a
+local newsserver like leafnode also usable with dial-up
+connections.
+
+KNode is written in C++ using KDevelop, a new IDE for KDE
+(thanx to the whole KDevelop-team for their excellent work).
+It is published under the GNU General Public License
+and comes without any warranty.
+
+The KNode team. \ No newline at end of file
diff --git a/knode/TODO b/knode/TODO
new file mode 100644
index 000000000..4a2197107
--- /dev/null
+++ b/knode/TODO
@@ -0,0 +1,4 @@
+* improvements for the attachment handling
+ (multipart articles, saving attachments automatically)
+* rewrite search dialog
+* ...
diff --git a/knode/aboutdata.cpp b/knode/aboutdata.cpp
new file mode 100644
index 000000000..714f2613b
--- /dev/null
+++ b/knode/aboutdata.cpp
@@ -0,0 +1,64 @@
+/*
+ aboutdata.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include "aboutdata.h"
+
+#include "resource.h"
+
+namespace KNode
+{
+ struct about_authors {
+ const char* name;
+ const char* desc;
+ const char* email;
+ };
+
+ static const about_authors authors[] = {
+ { "Volker Krause", I18N_NOOP("Maintainer"), "volker.krause@rwth-aachen.de" },
+ { "Roberto Selbach Teixeira", I18N_NOOP("Former maintainer"), "roberto@kde.org" },
+ { "Christian Gebauer", 0, "gebauer@kde.org" },
+ { "Christian Thurner", 0, "cthurner@web.de" },
+ { "Dirk Mueller", 0, "mueller@kde.org" },
+ { "Marc Mutz", 0, "mutz@kde.org" },
+ { "Mathias Waack", 0, "mathias@atoll-net.de" },
+ { "Laurent Montel", 0, "montel@kde.org" },
+ { "Stephan Johach", 0, "lucardus@onlinehome.de" },
+ { "Matthias Kalle Dalheimer", 0, "kalle@kde.org" },
+ { "Zack Rusin", 0, "zack@kde.org" }
+ };
+
+ AboutData::AboutData()
+ : KAboutData( "knode",
+ I18N_NOOP("KNode"),
+ KNODE_VERSION,
+ I18N_NOOP("A newsreader for KDE"),
+ KAboutData::License_GPL,
+ I18N_NOOP("Copyright (c) 1999-2005 the KNode authors"),
+ 0,
+ "http://knode.sourceforge.net/" )
+ {
+ using KNode::authors;
+ for ( unsigned int i = 0 ; i < sizeof authors / sizeof *authors ; ++i )
+ addAuthor( authors[i].name, authors[i].desc, authors[i].email );
+
+ addCredit( "Jakob Schroeter", 0, "js@camaya.net" );
+ }
+
+ AboutData::~AboutData()
+ {
+ }
+
+} // namespace KNode
diff --git a/knode/aboutdata.h b/knode/aboutdata.h
new file mode 100644
index 000000000..443877a86
--- /dev/null
+++ b/knode/aboutdata.h
@@ -0,0 +1,34 @@
+/*
+ aboutdata.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <kaboutdata.h>
+#include <kdepimmacros.h>
+
+#ifndef KNODE_ABOUTDATA_H
+#define KNODE_ABOUTDATA_H
+
+namespace KNode
+{
+ class KDE_EXPORT AboutData : public KAboutData
+ {
+ public:
+ AboutData();
+ ~AboutData();
+ };
+
+} // namespace KNode
+
+#endif
diff --git a/knode/articlewidget.cpp b/knode/articlewidget.cpp
new file mode 100644
index 000000000..805632c24
--- /dev/null
+++ b/knode/articlewidget.cpp
@@ -0,0 +1,1474 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 2005 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <qbuffer.h>
+#include <qclipboard.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qpaintdevicemetrics.h>
+#include <qpopupmenu.h>
+#include <qstringlist.h>
+#include <qtextcodec.h>
+#include <qtimer.h>
+
+#include <kaction.h>
+#include <kaddrbook.h>
+#include <kapplication.h>
+#include <kbookmarkmanager.h>
+#include <kcharsets.h>
+#include <khtml_part.h>
+#include <khtmlview.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmdcodec.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <krun.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+#include <kurl.h>
+
+#include <libemailfunctions/email.h>
+#include <libemailfunctions/kasciistringtools.h>
+
+#include <libkpgp/kpgp.h>
+#include <libkpgp/kpgpblock.h>
+
+#include <libkdepim/kfileio.h>
+#include <libkdepim/kxface.h>
+#include <libkdepim/linklocator.h>
+
+#include "articlewidget.h"
+#include "csshelper.h"
+#include "knarticle.h"
+#include "knarticlecollection.h"
+#include "knarticlefactory.h"
+#include "knarticlemanager.h"
+#include "knconfig.h"
+#include "knconfigmanager.h"
+#include "kndisplayedheader.h"
+#include "knfolder.h"
+#include "knfoldermanager.h"
+#include "knglobals.h"
+#include "kngroup.h"
+#include "knmainwidget.h"
+#include "knnntpaccount.h"
+#include "knsourceviewwindow.h"
+
+using namespace KNode;
+
+QValueList<ArticleWidget*> ArticleWidget::mInstances;
+
+ArticleWidget::ArticleWidget( QWidget *parent,
+ KXMLGUIClient *guiClient,
+ KActionCollection *actionCollection,
+ const char *name ) :
+ QWidget( parent, name ),
+ mArticle( 0 ),
+ mViewer( 0 ),
+ mCSSHelper( 0 ),
+ mHeaderStyle( "fancy" ),
+ mAttachmentStyle( "inline" ),
+ mShowHtml( false ),
+ mRot13( false ),
+ mForceCharset( false ),
+ mOverrideCharset( KMime::Headers::Latin1 ),
+ mTimer( 0 ),
+ mGuiClient( guiClient ),
+ mActionCollection( actionCollection )
+{
+ mInstances.append( this );
+
+ QHBoxLayout *box = new QHBoxLayout( this );
+ mViewer = new KHTMLPart( this, "mViewer" );
+ box->addWidget( mViewer->widget() );
+ mViewer->widget()->setFocusPolicy( WheelFocus );
+ mViewer->setPluginsEnabled( false );
+ mViewer->setJScriptEnabled( false );
+ mViewer->setJavaEnabled( false );
+ mViewer->setMetaRefreshEnabled( false );
+ mViewer->setOnlyLocalReferences( true );
+ mViewer->view()->setFocusPolicy( QWidget::WheelFocus );
+ connect( mViewer->browserExtension(), SIGNAL(openURLRequestDelayed(const KURL&, const KParts::URLArgs&)),
+ SLOT(slotURLClicked(const KURL&)) );
+ connect( mViewer, SIGNAL(popupMenu(const QString&, const QPoint&)),
+ SLOT(slotURLPopup(const QString&, const QPoint&)) );
+
+ mTimer = new QTimer( this );
+ connect( mTimer, SIGNAL(timeout()), SLOT(slotTimeout()) );
+
+ initActions();
+ readConfig();
+ clear();
+
+ installEventFilter( this );
+}
+
+
+ArticleWidget::~ArticleWidget()
+{
+ mInstances.remove( this );
+ delete mTimer;
+ delete mCSSHelper;
+ if ( mArticle && mArticle->isOrphant() )
+ delete mArticle;
+ removeTempFiles();
+}
+
+
+void ArticleWidget::initActions()
+{
+ mSaveAction = KStdAction::save( this, SLOT(slotSave()), mActionCollection );
+ mSaveAction->setText( KStdGuiItem::saveAs().text() );
+ mPrintAction = KStdAction::print( this, SLOT(slotPrint()), mActionCollection );
+ mCopySelectionAction = KStdAction::copy( this, SLOT(slotCopySelection()), mActionCollection );
+ mSelectAllAction = KStdAction::selectAll( this, SLOT(slotSelectAll()), mActionCollection );
+ mFindAction = KStdAction::find( this, SLOT(slotFind()), mActionCollection, "find_in_article" );
+ mFindAction->setText( i18n("F&ind in Article...") );
+ mViewSourceAction = new KAction( i18n("&View Source"), Key_V , this,
+ SLOT(slotViewSource()), mActionCollection, "article_viewSource" );
+ mReplyAction = new KAction( i18n("&Followup to Newsgroup..."), "message_reply",
+ Key_R, this, SLOT(slotReply()), mActionCollection, "article_postReply" );
+ mRemailAction = new KAction( i18n("Reply by E&mail..."), "mail_reply",
+ Key_A, this, SLOT(slotRemail()), mActionCollection, "article_mailReply" );
+ mForwardAction = new KAction( i18n("Forw&ard by Email..."), "mail_forward",
+ Key_F, this, SLOT(slotForward()), mActionCollection, "article_forward" );
+ mCancelAction = new KAction( i18n("article","&Cancel Article"),
+ 0, this, SLOT(slotCancel()), mActionCollection, "article_cancel" );
+ mSupersedeAction = new KAction(i18n("S&upersede Article"),
+ 0, this, SLOT(slotSupersede()), mActionCollection, "article_supersede" );
+ mFixedFontToggle = new KToggleAction( i18n("U&se Fixed Font"),
+ Key_X ,this, SLOT(slotToggleFixedFont()), mActionCollection, "view_useFixedFont" );
+ mFancyToggle = new KToggleAction( i18n("Fancy Formating"),
+ Key_Y, this, SLOT(slotToggleFancyFormating()), mActionCollection, "view_fancyFormating" );
+ mRot13Toggle = new KToggleAction( i18n("&Unscramble (Rot 13)"), "decrypted", 0 , this,
+ SLOT(slotToggleRot13()), mActionCollection, "view_rot13" );
+ mRot13Toggle->setChecked( false );
+
+ KRadioAction *ra;
+ mHeaderStyleMenu = new KActionMenu( i18n("&Headers"), mActionCollection, "view_headers" );
+ ra = new KRadioAction( i18n("&Fancy Headers"), 0, this, SLOT(slotFancyHeaders()),
+ mActionCollection, "view_headers_fancy" );
+ ra->setExclusiveGroup( "view_headers" );
+ mHeaderStyleMenu->insert( ra );
+ ra = new KRadioAction( i18n("&Standard Headers"), 0, this, SLOT(slotStandardHeaders()),
+ mActionCollection, "view_headers_standard" );
+ ra->setExclusiveGroup( "view_headers" );
+ mHeaderStyleMenu->insert( ra );
+ ra = new KRadioAction( i18n("&All Headers"), 0, this, SLOT(slotAllHeaders()),
+ mActionCollection, "view_headers_all" );
+ ra->setExclusiveGroup( "view_headers" );
+ mHeaderStyleMenu->insert( ra );
+
+ mAttachmentStyleMenu = new KActionMenu( i18n("&Attachments"), mActionCollection, "view_attachments" );
+ ra = new KRadioAction( i18n("&As Icon"), 0, this, SLOT(slotIconAttachments()),
+ mActionCollection, "view_attachments_icon" );
+ ra->setExclusiveGroup( "view_attachments" );
+ mAttachmentStyleMenu->insert( ra );
+ ra = new KRadioAction( i18n("&Inline"), 0, this, SLOT(slotInlineAttachments()),
+ mActionCollection, "view_attachments_inline" );
+ ra->setExclusiveGroup( "view_attachments" );
+ mAttachmentStyleMenu->insert( ra );
+ ra = new KRadioAction( i18n("&Hide"), 0, this, SLOT(slotHideAttachments()),
+ mActionCollection, "view_attachments_hide" );
+ ra->setExclusiveGroup( "view_attachments" );
+ mAttachmentStyleMenu->insert( ra );
+
+ mCharsetSelect = new KSelectAction( i18n("Chars&et"), 0, mActionCollection, "set_charset" );
+ mCharsetSelect->setShortcutConfigurable( false );
+ QStringList cs = KGlobal::charsets()->descriptiveEncodingNames();
+ cs.prepend( i18n("Automatic") );
+ mCharsetSelect->setItems( cs );
+ mCharsetSelect->setCurrentItem( 0 );
+ connect( mCharsetSelect, SIGNAL(activated(const QString&)),SLOT(slotSetCharset(const QString&)) );
+ mCharsetSelectKeyb = new KAction( i18n("Charset"), Key_C, this,
+ SLOT(slotSetCharsetKeyboard()), mActionCollection, "set_charset_keyboard" );
+
+ new KAction( i18n("&Open URL"), "fileopen", 0, this, SLOT(slotOpenURL()),
+ mActionCollection, "open_url" );
+ new KAction( i18n("&Copy Link Address"), "editcopy", 0, this, SLOT( slotCopyURL()),
+ mActionCollection, "copy_url" );
+ new KAction( i18n("&Bookmark This Link"), "bookmark_add", 0, this, SLOT(slotAddBookmark()),
+ mActionCollection, "add_bookmark" );
+ new KAction( i18n("&Add to Address Book"), 0, this, SLOT(slotAddToAddressBook()),
+ mActionCollection, "add_addr_book" );
+ new KAction( i18n("&Open in Address Book"), 0, this, SLOT(slotOpenInAddressBook()),
+ mActionCollection, "openin_addr_book" );
+ new KAction( i18n("&Open Attachment"), "fileopen", 0, this, SLOT(slotOpenAttachment()),
+ mActionCollection, "open_attachment" );
+ new KAction( i18n("&Save Attachment As..."), "filesaveas", 0, this, SLOT(slotSaveAttachment()),
+ mActionCollection, "save_attachment" );
+}
+
+
+
+void ArticleWidget::enableActions()
+{
+ if ( !mArticle ) {
+ disableActions();
+ return;
+ }
+
+ mSaveAction->setEnabled( true );
+ mPrintAction->setEnabled( true );
+ mCopySelectionAction->setEnabled( true );
+ mSelectAllAction->setEnabled( true );
+ mFindAction->setEnabled( true );
+ mForwardAction->setEnabled( true );
+ mHeaderStyleMenu->setEnabled( true );
+ mAttachmentStyleMenu->setEnabled( true );
+ mRot13Toggle->setEnabled( true );
+ mViewSourceAction->setEnabled( true );
+ mCharsetSelect->setEnabled( true );
+ mCharsetSelectKeyb->setEnabled( true );
+ mFixedFontToggle->setEnabled( true );
+ mFancyToggle->setEnabled( true );
+
+ // only valid for remote articles
+ bool enabled = ( mArticle->type() == KMime::Base::ATremote );
+ mReplyAction->setEnabled( enabled );
+ mRemailAction->setEnabled( enabled );
+
+ enabled = ( mArticle->type() == KMime::Base::ATremote
+ || mArticle->collection() == knGlobals.folderManager()->sent() );
+ mCancelAction->setEnabled( enabled );
+ mSupersedeAction->setEnabled( enabled );
+}
+
+
+void ArticleWidget::disableActions()
+{
+ mSaveAction->setEnabled( false );
+ mPrintAction->setEnabled( false );
+ mCopySelectionAction->setEnabled( false );
+ mSelectAllAction->setEnabled( false );
+ mFindAction->setEnabled( false );
+ mReplyAction->setEnabled( false );
+ mRemailAction->setEnabled( false );
+ mForwardAction->setEnabled( false );
+ mCancelAction->setEnabled( false );
+ mSupersedeAction->setEnabled( false );
+ mHeaderStyleMenu->setEnabled( false );
+ mAttachmentStyleMenu->setEnabled( false );
+ mRot13Toggle->setEnabled( false );
+ mViewSourceAction->setEnabled( false );
+ mCharsetSelect->setEnabled( false );
+ mCharsetSelectKeyb->setEnabled( false );
+ mFixedFontToggle->setEnabled( false );
+ mFancyToggle->setEnabled( false );
+}
+
+
+
+void ArticleWidget::readConfig()
+{
+ mFixedFontToggle->setChecked( knGlobals.configManager()->readNewsViewer()->useFixedFont() );
+ mFancyToggle->setChecked( knGlobals.configManager()->readNewsViewer()->interpretFormatTags() );
+
+ mShowHtml = knGlobals.configManager()->readNewsViewer()->alwaysShowHTML();
+
+ KConfig *conf = knGlobals.config();
+ conf->setGroup( "READNEWS" );
+ mAttachmentStyle = conf->readEntry( "attachmentStyle", "inline" );
+ mHeaderStyle = conf->readEntry( "headerStyle", "fancy" );
+ KRadioAction *ra = 0;
+ ra = static_cast<KRadioAction*>( mActionCollection->action( QString("view_attachments_%1").arg(mAttachmentStyle).latin1() ) );
+ ra->setChecked( true );
+ ra = static_cast<KRadioAction*>( mActionCollection->action( QString("view_headers_%1").arg(mHeaderStyle).latin1() ) );
+ ra->setChecked( true );
+
+ delete mCSSHelper;
+ mCSSHelper = new CSSHelper( QPaintDeviceMetrics( mViewer->view() ) );
+
+ if ( !knGlobals.configManager()->readNewsGeneral()->autoMark() )
+ mTimer->stop();
+}
+
+
+void ArticleWidget::writeConfig()
+{
+ // main viewer determines the settings
+ if ( knGlobals.artWidget != this )
+ return;
+
+ KConfig *conf = knGlobals.config();
+ conf->setGroup( "READNEWS" );
+ conf->writeEntry( "attachmentStyle", mAttachmentStyle );
+ conf->writeEntry( "headerStyle", mHeaderStyle );
+
+ knGlobals.configManager()->readNewsViewer()->setUseFixedFont( mFixedFontToggle->isChecked() );
+ knGlobals.configManager()->readNewsViewer()->setInterpretFormatTags( mFancyToggle->isChecked() );
+}
+
+
+
+void ArticleWidget::setArticle( KNArticle *article )
+{
+ // don't leak orphant articles
+ if ( mArticle && mArticle->isOrphant() )
+ delete mArticle;
+
+ mShowHtml = knGlobals.configManager()->readNewsViewer()->alwaysShowHTML();
+ mRot13 = false;
+ mRot13Toggle->setChecked( false );
+ mTimer->stop();
+
+ mArticle = article;
+
+ if ( !mArticle )
+ clear();
+ else {
+ if ( mArticle->hasContent() ) { // article is already loaded => just show it
+ displayArticle();
+ } else {
+ if( !knGlobals.articleManager()->loadArticle( mArticle ) )
+ articleLoadError( mArticle, i18n("Unable to load the article.") );
+ else
+ // try again for local articles
+ if( mArticle->hasContent() && !( mArticle->type() == KMime::Base::ATremote ) )
+ displayArticle();
+ }
+ }
+}
+
+
+void ArticleWidget::clear()
+{
+ disableActions();
+ mViewer->begin();
+ mViewer->setUserStyleSheet( mCSSHelper->cssDefinitions( mFixedFontToggle->isChecked() ) );
+ mViewer->write( mCSSHelper->htmlHead( mFixedFontToggle->isChecked() ) );
+ mViewer->write( "</body></html>" );
+ mViewer->end();
+}
+
+
+void ArticleWidget::displayArticle()
+{
+ if ( !mArticle) {
+ clear();
+ return;
+ }
+
+ // scroll back to top
+ mViewer->view()->ensureVisible( 0, 0 );
+
+ if ( !mArticle->hasContent() ) {
+ displayErrorMessage( i18n("The article contains no data.") );
+ return;
+ }
+
+ if ( mForceCharset != mArticle->forceDefaultCS()
+ || ( mForceCharset && mArticle->defaultCharset() != mOverrideCharset ) ) {
+ mArticle->setDefaultCharset( mOverrideCharset );
+ mArticle->setForceDefaultCS( mForceCharset );
+ }
+
+ KNConfig::ReadNewsViewer *rnv = knGlobals.configManager()->readNewsViewer();
+ removeTempFiles();
+
+ mViewer->begin();
+ mViewer->setUserStyleSheet( mCSSHelper->cssDefinitions( mFixedFontToggle->isChecked() ) );
+ mViewer->write( mCSSHelper->htmlHead( mFixedFontToggle->isChecked() ) );
+
+ // headers
+ displayHeader();
+
+ // body
+ QString html;
+ KMime::Content *text = mArticle->textContent();
+
+ // check if codec is available
+ if ( text && !canDecodeText( text->contentType()->charset() ) ) {
+ html += QString("<table width=\"100%\" border=\"0\"><tr><td bgcolor=\"#FF0000\">%1</td></tr></table>")
+ .arg( i18n("Unknown charset. Default charset is used instead.") );
+ kdDebug(5003) << k_funcinfo << "unknown charset = " << text->contentType()->charset() << endl;
+ }
+
+ // if the article is pgp signed and the user asked for verifying the
+ // signature, we show a nice header:
+ QPtrList<Kpgp::Block> pgpBlocks;
+ QStrList nonPgpBlocks;
+ bool containsPGP = Kpgp::Module::prepareMessageForDecryption( mArticle->body(), pgpBlocks, nonPgpBlocks );
+
+ mViewer->write ( html );
+ html = QString();
+
+ if ( containsPGP ) {
+ QPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
+ QStrListIterator npbit( nonPgpBlocks );
+ QTextCodec *codec;
+ if ( text )
+ codec = KGlobal::charsets()->codecForName( text->contentType()->charset() );
+ else
+ codec = KGlobal::locale()->codecForEncoding();
+
+ for( ; *pbit != 0; ++pbit, ++npbit ) {
+ // handle non-pgp block
+ QCString str( *npbit );
+ if( !str.isEmpty() ) {
+ QStringList lines = QStringList::split( '\n', codec->toUnicode( str ), true );
+ displayBodyBlock( lines );
+ }
+ // handle pgp block
+ Kpgp::Block* block = *pbit;
+ if ( block->type() == Kpgp::ClearsignedBlock )
+ block->verify();
+ QStringList lines = QStringList::split( '\n', codec->toUnicode( block->text() ), true );
+ if ( block->isSigned() ) {
+ QString signClass = displaySigHeader( block );
+ displayBodyBlock( lines );
+ displaySigFooter( signClass );
+ } else {
+ displayBodyBlock( lines );
+ }
+ }
+ // deal with the last non-pgp block
+ QCString str( *npbit );
+ if( !str.isEmpty() ) {
+ QStringList lines = QStringList::split( '\n', codec->toUnicode( str ), true );
+ displayBodyBlock( lines );
+ }
+ }
+
+ KMime::Headers::ContentType *ct = mArticle->contentType();
+
+ // get attachments
+ mAttachments.clear();
+ mAttachementMap.clear();
+ if( !text || ct->isMultipart() )
+ mArticle->attachments( &mAttachments, rnv->showAlternativeContents() );
+
+ // partial message
+ if(ct->isPartial()) {
+ mViewer->write( i18n("<br/><b>This article has the MIME type &quot;message/partial&quot;, which KNode cannot handle yet.<br>Meanwhile you can save the article as a text file and reassemble it by hand.</b>") );
+ }
+
+ // display body text
+ if ( text && text->hasContent() && !ct->isPartial() ) {
+ // handle HTML messages
+ if ( text->contentType()->isHTMLText() ) {
+ QString htmlTxt;
+ text->decodedText( htmlTxt, true, knGlobals.configManager()->readNewsViewer()->removeTrailingNewlines() );
+ if ( mShowHtml ) {
+ // strip </html> & </body>
+ int i = kMin( htmlTxt.findRev( "</html>", -1, false ), htmlTxt.findRev( "</body>", -1, false ) );
+ if ( i >= 0 )
+ htmlTxt.truncate( i );
+ html += htmlTxt;
+ } else {
+ html += "<div class=\"htmlWarn\">\n";
+ html += i18n("<b>Note:</b> This is an HTML message. For "
+ "security reasons, only the raw HTML code "
+ "is shown. If you trust the sender of this "
+ "message then you can activate formatted "
+ "HTML display for this message "
+ "<a href=\"knode:showHTML\">by clicking here</a>.");
+ html += "</div><br><br>";
+ html += toHtmlString( htmlTxt );
+ }
+ }
+ else {
+ if ( !containsPGP ) {
+ QStringList lines;
+ text->decodedText( lines, true, knGlobals.configManager()->readNewsViewer()->removeTrailingNewlines() );
+ displayBodyBlock( lines );
+ }
+ }
+
+ }
+ mViewer->write( html );
+
+ // display attachments
+ if( !mAttachments.isEmpty() && !ct->isPartial() ) {
+ int attCnt = 0;
+ for( KMime::Content *var = mAttachments.first(); var; var = mAttachments.next() ) {
+ displayAttachment( var, attCnt );
+ attCnt++;
+ }
+ }
+
+ mViewer->write("</body></html>");
+ mViewer->end();
+
+ enableActions();
+ if( mArticle->type() == KMime::Base::ATremote && knGlobals.configManager()->readNewsGeneral()->autoMark() )
+ mTimer->start( knGlobals.configManager()->readNewsGeneral()->autoMarkSeconds() * 1000, true );
+}
+
+
+void ArticleWidget::displayErrorMessage( const QString &msg )
+{
+ mViewer->begin();
+ mViewer->setUserStyleSheet( mCSSHelper->cssDefinitions( mFixedFontToggle->isChecked() ) );
+ mViewer->write( mCSSHelper->htmlHead( mFixedFontToggle->isChecked() ) );
+ QString errMsg = msg;
+ mViewer->write( "<b><font size=\"+1\" color=\"red\">" );
+ mViewer->write( i18n("An error occurred.") );
+ mViewer->write( "</font></b><hr/><br/>" );
+ mViewer->write( errMsg.replace( "\n", "<br/>" ) );
+ mViewer->write( "</body></html>");
+ mViewer->end();
+
+ // mark article as read if there is a negative reply from the server
+ if ( knGlobals.configManager()->readNewsGeneral()->autoMark() &&
+ mArticle && mArticle->type() == KMime::Base::ATremote && !mArticle->isOrphant() &&
+ ( msg.find("430") != -1 || msg.find("423") != -1 ) ) {
+ KNRemoteArticle::List l;
+ l.append( static_cast<KNRemoteArticle*>( mArticle ) );
+ knGlobals.articleManager()->setRead( l, true );
+ }
+
+ disableActions();
+}
+
+
+
+void ArticleWidget::displayHeader()
+{
+ QString headerHtml;
+
+ // full header style
+ if ( mHeaderStyle == "all" ) {
+ QCString head = mArticle->head();
+ KMime::Headers::Generic *header = 0;
+
+ while ( !head.isEmpty() ) {
+ header = mArticle->getNextHeader( head );
+ if ( header ) {
+ headerHtml += "<tr>";
+ headerHtml+=QString( "<td align=\"right\" valign=\"top\"><b>%1</b></td><td width=\"100%\">%2</td></tr>" )
+ .arg( toHtmlString( header->type(), None ) + ": " )
+ .arg( toHtmlString( header->asUnicodeString() , ParseURL ) );
+ delete header;
+ }
+ }
+
+ mViewer->write( "<div class=\"header\">" );
+ mViewer->write( "<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"> " );
+ mViewer->write( headerHtml );
+ mViewer->write( "</table></div>" );
+ return;
+ }
+
+ // standard & fancy header style
+ KMime::Headers::Base *hb;
+ QValueList<KNDisplayedHeader*> dhs = knGlobals.configManager()->displayedHeaders()->headers();
+ for ( QValueList<KNDisplayedHeader*>::Iterator it = dhs.begin(); it != dhs.end(); ++it ) {
+ KNDisplayedHeader *dh = (*it);
+ hb = mArticle->getHeaderByType(dh->header().latin1());
+ if ( !hb || hb->is("Subject") || hb->is("Organization") )
+ continue;
+
+ if ( dh->hasName() ) {
+ headerHtml += "<tr>";
+ if ( mHeaderStyle == "fancy" )
+ headerHtml += "<th>";
+ else
+ headerHtml += "<th align=\"right\">";
+ headerHtml += toHtmlString( dh->translatedName(), None );
+ headerHtml += ":</th><td width=\"100%\">";
+ }
+ else
+ headerHtml+="<tr><td colspan=\"2\">";
+
+ if ( hb->is("From") ) {
+ headerHtml += QString( "<a href=\"mailto:%1\">%2</a>")
+ .arg( KPIM::getEmailAddress( hb->asUnicodeString() ) )
+ .arg( toHtmlString( hb->asUnicodeString(), None ) );
+ KMime::Headers::Base *orgHdr = mArticle->getHeaderByType( "Organization" );
+ if ( orgHdr && !orgHdr->isEmpty() ) {
+ headerHtml += "&nbsp;&nbsp;(";
+ headerHtml += toHtmlString( orgHdr->asUnicodeString() );
+ headerHtml += ")";
+ }
+ } else if ( hb->is("Date") ) {
+ KMime::Headers::Date *date=static_cast<KMime::Headers::Date*>(hb);
+ headerHtml += toHtmlString( KGlobal::locale()->formatDateTime(date->qdt(), false, true), None );
+ } else if ( hb->is("Newsgroups") ) {
+ QString groups = hb->asUnicodeString();
+ groups.replace( ',', ", " );
+ headerHtml += toHtmlString( groups, ParseURL );
+ } else
+ headerHtml += toHtmlString( hb->asUnicodeString(), ParseURL );
+
+ headerHtml += "</td></tr>";
+ }
+
+ // standard header style
+ if ( mHeaderStyle == "standard" ) {
+ mViewer->write( "<b style=\"font-size:130%\">" + toHtmlString( mArticle->subject()->asUnicodeString() ) + "</b>" );
+ mViewer->write( "<div class=\"header\"" );
+ mViewer->write( "<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>" + headerHtml );
+ mViewer->write( "</tr></table></div>" );
+ return;
+ }
+
+ // X-Face support
+ QString xfhead;
+ KMime::Headers::Base *temp = mArticle->getHeaderByType("X-Face");
+ if (temp)
+ xfhead = temp->asUnicodeString();
+ QString xface = "";
+ if ( !xfhead.isEmpty() ) {
+ KPIM::KXFace xf;
+ xface = QString::fromLatin1( "<div class=\"senderpic\"><img src=\"%1\" width=\"48\" height=\"48\"/></div>" )
+ .arg( imgToDataUrl( xf.toImage( xfhead ), "PNG" ) );
+ }
+
+ // fancy header style
+ mViewer->write( "<div class=\"fancy header\"" );
+ mViewer->write( QString("<div>") );
+ mViewer->write( toHtmlString( mArticle->subject()->asUnicodeString(), ParseURL | FancyFormatting ) );
+ mViewer->write( QString("</div>") );
+
+ QString html = QString("<table class=\"outer\"><tr><td width=\"100%\"><table>");
+
+ html += headerHtml;
+ html+="</td></tr></table></td>";
+ html += "<td align=\"center\">" + xface + "</td>";
+ html += "</tr></table>";
+
+ // references
+ KMime::Headers::References *refs = mArticle->references( false );
+ if ( mArticle->type() == KMime::Base::ATremote && refs
+ && knGlobals.configManager()->readNewsViewer()->showRefBar() ) {
+ html += "<div class=\"spamheader\">";
+ int refCnt = refs->count(), i = 1;
+ QCString id = refs->first();
+ id = id.mid( 1, id.length() - 2 ); // remove <>
+ html += QString( "<b>%1</b>" ).arg( i18n("References:") );
+
+ while ( i <= refCnt ) {
+ html += " <a href=\"news:" + QString::fromLatin1( id ) + "\">" + QString::number( i ) + "</a>";
+ id = refs->next();
+ id = id.mid( 1, id.length() - 2 ); // remove <>
+ i++;
+ }
+ html += "</div>";
+ }
+
+ mViewer->write( html );
+ mViewer->write( "</div>" );
+}
+
+
+void ArticleWidget::displayBodyBlock( const QStringList &lines )
+{
+ int oldLevel = -2, newLevel = -2;
+ bool isSig = false;
+ QString line, html;
+ KNConfig::ReadNewsViewer *rnv = knGlobals.configManager()->readNewsViewer();
+ QString quoteChars = rnv->quoteCharacters().simplifyWhiteSpace();
+ if (quoteChars.isEmpty())
+ quoteChars = ">";
+
+ for ( QStringList::const_iterator it = lines.begin(); it != lines.end(); ++it) {
+ line = (*it);
+ if ( !line.isEmpty() ) {
+ // signature found
+ if ( !isSig && line == "-- " ) {
+ isSig = true;
+ // close previous body tag (if any) and open new one
+ if ( newLevel != -2 )
+ html += "</div>";
+ html += mCSSHelper->nonQuotedFontTag();
+ newLevel = -1;
+ if ( rnv->showSignature() ) {
+ html += "<hr size=\"1\"/>";
+ continue;
+ }
+ else break;
+ }
+ // look for quoting characters
+ if ( !isSig ) {
+ oldLevel = newLevel;
+ newLevel = quotingDepth( line, quoteChars );
+ if ( newLevel >= 3 )
+ newLevel = 2; // no more than three levels supported (0-2)
+
+ // quoting level changed
+ if ( newLevel != oldLevel ) {
+ if ( oldLevel != -2 )
+ html += "</div>"; // close previous level
+ // open new level
+ if ( newLevel == -1 )
+ html += mCSSHelper->nonQuotedFontTag();
+ else
+ html += mCSSHelper->quoteFontTag( newLevel );
+ }
+ // output the actual line
+ html += toHtmlString( line, ParseURL | FancyFormatting | AllowROT13 ) + "<br/>";
+ } else {
+ // signature
+ html += toHtmlString( line, ParseURL | AllowROT13 ) + "<br/>";
+ }
+ } else {
+ // empty line
+ html += "<br/>";
+ }
+ }
+ // close body quoting level tags
+ if ( newLevel != -2 )
+ html += "</div>";
+
+ mViewer->write( html );
+}
+
+
+QString ArticleWidget::displaySigHeader( Kpgp::Block* block )
+{
+ QString signClass = "signErr";
+ QString signer = block->signatureUserId();
+ QCString signerKey = block->signatureKeyId();
+ QString message;
+ if ( signer.isEmpty() ) {
+ message = i18n( "Message was signed with unknown key 0x%1." )
+ .arg( signerKey );
+ message += "<br/>";
+ message += i18n( "The validity of the signature cannot be verified." );
+ signClass = "signWarn";
+ } else {
+ // determine the validity of the key
+ Kpgp::Module *pgp = knGlobals.pgp;
+ Kpgp::Validity keyTrust;
+ if( !signerKey.isEmpty() )
+ keyTrust = pgp->keyTrust( signerKey );
+ else
+ // This is needed for the PGP 6 support because PGP 6 doesn't
+ // print the key id of the signing key if the key is known.
+ keyTrust = pgp->keyTrust( signer );
+
+ // HTMLize the signer's user id and create mailto: link
+ signer = toHtmlString( signer, None );
+ signer = "<a href=\"mailto:" + KPIM::getEmailAddress( signer ) + "\">" + signer + "</a>";
+
+ if( !signerKey.isEmpty() )
+ message += i18n( "Message was signed by %1 (Key ID: 0x%2)." )
+ .arg( signer )
+ .arg( signerKey );
+ else
+ message += i18n( "Message was signed by %1." ).arg( signer );
+ message += "<br/>";
+
+ if( block->goodSignature() ) {
+ if ( keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
+ signClass = "signOkKeyBad";
+ else
+ signClass = "signOkKeyOk";
+ switch( keyTrust ) {
+ case Kpgp::KPGP_VALIDITY_UNKNOWN:
+ message += i18n( "The signature is valid, but the key's "
+ "validity is unknown." );
+ break;
+ case Kpgp::KPGP_VALIDITY_MARGINAL:
+ message += i18n( "The signature is valid and the key is "
+ "marginally trusted." );
+ break;
+ case Kpgp::KPGP_VALIDITY_FULL:
+ message += i18n( "The signature is valid and the key is "
+ "fully trusted." );
+ break;
+ case Kpgp::KPGP_VALIDITY_ULTIMATE:
+ message += i18n( "The signature is valid and the key is "
+ "ultimately trusted." );
+ break;
+ default:
+ message += i18n( "The signature is valid, but the key is "
+ "untrusted." );
+ }
+ } else {
+ message += i18n("Warning: The signature is bad.");
+ signClass = "signErr";
+ }
+ }
+
+ QString html = "<table cellspacing=\"1\" cellpadding=\"1\" class=\"" + signClass + "\">";
+ html += "<tr class=\"" + signClass + "H\"><td>";
+ html += message;
+ html += "</td></tr><tr class=\"" + signClass + "B\"><td>";
+ mViewer->write( html );
+ return signClass;
+}
+
+
+void ArticleWidget::displaySigFooter( const QString &signClass )
+{
+ QString html = "</td></tr><tr class=\"" + signClass + "H\">";
+ html += "<td>" + i18n( "End of signed message" ) + "</td></tr></table>";
+ mViewer->write( html );
+}
+
+
+void ArticleWidget::displayAttachment( KMime::Content *att, int partNum )
+{
+ if ( mAttachmentStyle == "hide" )
+ return;
+
+ QString html;
+ KMime::Headers::ContentType *ct = att->contentType();
+
+ // attachment label
+ QString label = ct->name();
+ if ( label.isEmpty() )
+ label = i18n("unnamed" );
+ // if label consists of only whitespace replace them by underscores
+ if ( (uint)label.contains( ' ' ) == label.length() )
+ label.replace( QRegExp( " ", true, true ), "_" );
+ label = toHtmlString( label, None );
+
+ // attachment comment
+ QString comment = att->contentDescription()->asUnicodeString();
+ comment = toHtmlString( comment, ParseURL | FancyFormatting );
+
+ QString href;
+ QString fileName = writeAttachmentToTempFile( att, partNum );
+ if ( fileName.isEmpty() ) {
+ href = "part://" + QString::number( partNum );
+ } else {
+ href = "file:" + KURL::encode_string( fileName );
+ mAttachementMap[fileName] = partNum;
+ }
+
+ if ( mAttachmentStyle == "inline" && inlinePossible( att ) ) {
+ if ( ct->isImage() ) {
+ html += "<div><a href=\"" + href + "\">"
+ "<img src=\"" + fileName + "\" border=\"0\"></a>"
+ "</div><div><a href=\"" + href + "\">" + label + "</a>"
+ "</div><div>" + comment + "</div><br>";
+ } else { //text
+ // frame
+ html += "<table cellspacing=\"1\" class=\"textAtm\">"
+ "<tr class=\"textAtmH\"><td>"
+ "<a href=\"" + href + "\">" + label + "</a>";
+ if ( !comment.isEmpty() )
+ html += "<br>" + comment;
+ html += "</td></tr><tr class=\"textAtmB\"><td>";
+ // content
+ QString tmp;
+ att->decodedText( tmp );
+ /*if( ct->isHTMLText() )
+ // ### to dangerous, we should use the same stuff as for the main text here
+ html += tmp;
+ else*/
+ html += toHtmlString( tmp, ParseURL );
+ // finish frame
+ html += "</td></tr></table>";
+ }
+ } else { // icon
+ QCString mimetype = ct->mimeType();
+ KPIM::kAsciiToLower( mimetype.data() );
+ QString iconName = KMimeType::mimeType( mimetype )->icon( QString::null, false );
+ QString iconFile = KGlobal::instance()->iconLoader()->iconPath( iconName, KIcon::Desktop );
+ html += "<div><a href=\"" + href + "\"><img src=\"" +
+ iconFile + "\" border=\"0\">" + label +
+ "</a></div><div>" + comment + "</div><br>";
+ }
+ mViewer->write( html );
+}
+
+
+QString ArticleWidget::toHtmlString( const QString &line, int flags )
+{
+ int llflags = LinkLocator::PreserveSpaces;
+ if ( !(flags & ArticleWidget::ParseURL) )
+ llflags |= LinkLocator::IgnoreUrls;
+ if ( mFancyToggle->isChecked() && (flags & ArticleWidget::FancyFormatting) )
+ llflags |= LinkLocator::ReplaceSmileys | LinkLocator::HighlightText;
+ QString text = line;
+ if ( flags & ArticleWidget::AllowROT13 ) {
+ if ( mRot13 )
+ text = KNHelper::rot13( line );
+ }
+ return LinkLocator::convertToHtml( text, llflags );
+}
+
+
+// from KMail headerstyle.cpp
+QString ArticleWidget::imgToDataUrl( const QImage &image, const char* fmt )
+{
+ QByteArray ba;
+ QBuffer buffer( ba );
+ buffer.open( IO_WriteOnly );
+ image.save( &buffer, fmt );
+ return QString::fromLatin1("data:image/%1;base64,%2")
+ .arg( fmt, KCodecs::base64Encode( ba ) );
+}
+
+
+
+int ArticleWidget::quotingDepth( const QString &line, const QString &quoteChars )
+{
+ int level = -1;
+ for ( uint i = 0; i < line.length(); ++i ) {
+ // skip spaces
+ if ( line[i].isSpace() )
+ continue;
+ if ( quoteChars.find( line[i] ) != -1 )
+ ++level;
+ else
+ break;
+ }
+ return level;
+}
+
+
+bool ArticleWidget::inlinePossible( KMime::Content *c )
+{
+ KMime::Headers::ContentType *ct = c->contentType();
+ return ( ct->isText() || ct->isImage() );
+}
+
+
+bool ArticleWidget::canDecodeText( const QCString &charset ) const
+{
+ if ( charset.isEmpty() )
+ return false;
+ bool ok = true;
+ KGlobal::charsets()->codecForName( charset,ok );
+ return ok;
+}
+
+
+
+void ArticleWidget::updateContents()
+{
+ // save current scrollbar position
+ float savedPosition = (float)mViewer->view()->contentsY() / (float)mViewer->view()->contentsHeight();
+ if ( mArticle && mArticle->hasContent() )
+ displayArticle();
+ else
+ clear();
+ // restore scrollbar position
+ mViewer->view()->setContentsPos( 0, qRound( mViewer->view()->contentsHeight() * savedPosition ) );
+}
+
+
+
+QString ArticleWidget::writeAttachmentToTempFile( KMime::Content *att, int partNum )
+{
+ // more or less KMail code
+ KTempFile *tempFile = new KTempFile( QString::null, "." + QString::number( partNum ) );
+ tempFile->setAutoDelete( true );
+ QString fname = tempFile->name();
+ delete tempFile;
+
+ if( ::access( QFile::encodeName( fname ), W_OK ) != 0 )
+ // Not there or not writable
+ if( ::mkdir( QFile::encodeName( fname ), 0 ) != 0
+ || ::chmod( QFile::encodeName( fname ), S_IRWXU ) != 0 )
+ return QString::null; //failed create
+
+ Q_ASSERT( !fname.isNull() );
+
+ mTempDirs.append( fname );
+ // strip off a leading path
+ KMime::Headers::ContentType* ct = att->contentType();
+ QString attName = ct->name();
+ int slashPos = attName.findRev( '/' );
+ if( -1 != slashPos )
+ attName = attName.mid( slashPos + 1 );
+ if( attName.isEmpty() )
+ attName = "unnamed";
+ fname += "/" + attName;
+
+ QByteArray data = att->decodedContent();
+ size_t size = data.size();
+ // ### KMail does crlf2lf conversion here before writing the file
+ if( !KPIM::kBytesToFile( data.data(), size, fname, false, false, false ) )
+ return QString::null;
+
+ mTempFiles.append( fname );
+ // make file read-only so that nobody gets the impression that he might
+ // edit attached files
+ ::chmod( QFile::encodeName( fname ), S_IRUSR );
+
+ return fname;
+}
+
+
+void ArticleWidget::removeTempFiles( )
+{
+ for ( QStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it )
+ QFile::remove(*it);
+ mTempFiles.clear();
+ for ( QStringList::Iterator it = mTempDirs.begin(); it != mTempDirs.end(); ++it )
+ QDir(*it).rmdir(*it);
+ mTempDirs.clear();
+}
+
+
+
+void ArticleWidget::processJob( KNJobData * job )
+{
+ if ( job->type() == KNJobData::JTfetchSource ) {
+ KNRemoteArticle *a = static_cast<KNRemoteArticle*>( job->data() );
+ if ( !job->canceled() ) {
+ if ( !job->success() )
+ KMessageBox::error( this, i18n("An error occurred while downloading the article source:\n")
+ .arg( job->errorString() ) );
+ else
+ new KNSourceViewWindow( a->head() + "\n" + a->body() );
+ }
+ delete job;
+ delete a;
+ }
+ else
+ delete job;
+}
+
+
+
+typedef QValueList<ArticleWidget*>::ConstIterator InstanceIterator;
+
+void ArticleWidget::configChanged()
+{
+ for( InstanceIterator it = mInstances.begin(); it != mInstances.end(); ++it ) {
+ (*it)->readConfig();
+ (*it)->updateContents();
+ }
+}
+
+
+bool ArticleWidget::articleVisible( KNArticle *article )
+{
+ for ( InstanceIterator it = mInstances.begin(); it != mInstances.end(); ++it )
+ if ( (*it)->article() == article )
+ return true;
+ return false;
+}
+
+
+void ArticleWidget::articleRemoved( KNArticle *article )
+{
+ for ( InstanceIterator it = mInstances.begin(); it != mInstances.end(); ++it )
+ if ( (*it)->article() == article )
+ (*it)->setArticle( 0 );
+}
+
+
+void ArticleWidget::articleChanged( KNArticle *article )
+{
+ for ( InstanceIterator it = mInstances.begin(); it != mInstances.end(); ++it )
+ if ( (*it)->article() == article )
+ (*it)->displayArticle();
+}
+
+
+void ArticleWidget::articleLoadError( KNArticle *article, const QString &error )
+{
+ for ( InstanceIterator it = mInstances.begin(); it != mInstances.end(); ++it )
+ if ( (*it)->article() == article )
+ (*it)->displayErrorMessage( error );
+}
+
+
+void ArticleWidget::collectionRemoved( KNArticleCollection *coll )
+{
+ for ( InstanceIterator it = mInstances.begin(); it != mInstances.end(); ++it )
+ if ( (*it)->article() && (*it)->article()->collection() == coll )
+ (*it)->setArticle( 0 );
+}
+
+
+void ArticleWidget::cleanup()
+{
+ for ( InstanceIterator it = mInstances.begin(); it != mInstances.end(); ++it )
+ (*it)->setArticle( 0 ); //delete orphant articles => avoid crash in destructor
+}
+
+
+
+bool ArticleWidget::atBottom() const
+{
+ const KHTMLView *view = mViewer->view();
+ return view->contentsY() + view->visibleHeight() >= view->contentsHeight();
+}
+
+void ArticleWidget::scrollUp()
+{
+ mViewer->view()->scrollBy( 0, -10 );
+}
+
+void ArticleWidget::scrollDown()
+{
+ mViewer->view()->scrollBy( 0, 10 );
+}
+
+void ArticleWidget::scrollPrior()
+{
+ mViewer->view()->scrollBy( 0, -(int)(mViewer->view()->height() * 0.8) );
+}
+
+void ArticleWidget::scrollNext()
+{
+ mViewer->view()->scrollBy( 0, (int)(mViewer->view()->height() * 0.8) );
+}
+
+
+
+void ArticleWidget::slotURLClicked( const KURL &url, bool forceOpen)
+{
+ // internal URLs
+ if ( url.protocol() == "knode" ) {
+ if ( url.path() == "showHTML" ) {
+ mShowHtml = true;
+ updateContents();
+ }
+ return;
+ }
+ // handle mailto
+ if ( url.protocol() == "mailto" ) {
+ KMime::Headers::AddressField addr( mArticle );
+ addr.fromUnicodeString( url.path(), "" );
+ knGlobals.artFactory->createMail( &addr );
+ return;
+ }
+ // handle news URL's
+ if ( url.protocol() == "news" ) {
+ kdDebug( 5003 ) << k_funcinfo << url << endl;
+ knGlobals.top->openURL( url );
+ return;
+ }
+ // handle attachments
+ int partNum = 0;
+ if ( url.protocol() == "file" || url.protocol() == "part" ) {
+ if ( url.protocol() == "file" ) {
+ if ( !mAttachementMap.contains( url.path() ) )
+ return;
+ partNum = mAttachementMap[url.path()];
+ }
+ if ( url.protocol() == "part" )
+ partNum = url.path().toInt();
+ KMime::Content *c = mAttachments.at( partNum );
+ if ( !c )
+ return;
+ // TODO: replace with message box as done in KMail
+ if ( forceOpen || knGlobals.configManager()->readNewsViewer()->openAttachmentsOnClick() )
+ knGlobals.articleManager()->openContent( c );
+ else
+ knGlobals.articleManager()->saveContentToFile( c, this );
+ return;
+ }
+ // let KDE take care of the remaing protocols (http, ftp, etc.)
+ new KRun( url );
+}
+
+
+void ArticleWidget::slotURLPopup( const QString &url, const QPoint &point )
+{
+ mCurrentURL = KURL( url );
+ QString popupName;
+ if ( url.isEmpty() ) // plain text
+ popupName = "body_popup";
+ else if ( mCurrentURL.protocol() == "mailto" )
+ popupName = "mailto_popup";
+ else if ( mCurrentURL.protocol() == "file" || mCurrentURL.protocol() == "part" )
+ popupName = "attachment_popup";
+ // ### news URLS?
+ else if ( mCurrentURL.protocol() == "knode" )
+ return; // skip
+ else
+ popupName = "url_popup"; // all other URLs
+ QPopupMenu *popup = static_cast<QPopupMenu*>( mGuiClient->factory()->container( popupName, mGuiClient ) );
+ if ( popup )
+ popup->popup( point );
+}
+
+
+
+void ArticleWidget::slotTimeout()
+{
+ if ( mArticle && mArticle->type() == KMime::Base::ATremote && !mArticle->isOrphant() ) {
+ KNRemoteArticle::List l;
+ l.append( static_cast<KNRemoteArticle*>( mArticle ) );
+ knGlobals.articleManager()->setRead( l, true );
+ }
+}
+
+
+
+void ArticleWidget::slotSave()
+{
+ if ( mArticle )
+ knGlobals.articleManager()->saveArticleToFile( mArticle, this );
+}
+
+void ArticleWidget::slotPrint( )
+{
+ if ( mArticle )
+ mViewer->view()->print();
+}
+
+
+void ArticleWidget::slotCopySelection( )
+{
+ kapp->clipboard()->setText( mViewer->selectedText() );
+}
+
+
+void ArticleWidget::slotSelectAll()
+{
+ mViewer->selectAll();
+}
+
+
+void ArticleWidget::slotFind()
+{
+ mViewer->findText();
+}
+
+
+void ArticleWidget::slotViewSource()
+{
+ // local article can be shown directly
+ if ( mArticle && mArticle->type() == KMime::Base::ATlocal && mArticle->hasContent() ) {
+ new KNSourceViewWindow( mArticle->encodedContent( false ) );
+ } else {
+ // download remote article
+ if ( mArticle && mArticle->type() == KMime::Base::ATremote ) {
+ KNGroup *g = static_cast<KNGroup*>( mArticle->collection() );
+ KNRemoteArticle *a = new KNRemoteArticle( g ); //we need "g" to access the nntp-account
+ a->messageID( true )->from7BitString( mArticle->messageID()->as7BitString( false ) );
+ a->lines( true )->from7BitString( mArticle->lines( true )->as7BitString( false ) );
+ a->setArticleNumber( static_cast<KNRemoteArticle*>( mArticle)->articleNumber() );
+ emitJob( new KNJobData( KNJobData::JTfetchSource, this, g->account(), a) );
+ }
+ }
+}
+
+
+void ArticleWidget::slotReply()
+{
+ if ( mArticle && mArticle->type() == KMime::Base::ATremote )
+ knGlobals.artFactory->createReply( static_cast<KNRemoteArticle*>( mArticle ),
+ mViewer->selectedText(), true, false );
+}
+
+
+void ArticleWidget::slotRemail()
+{
+ if ( mArticle && mArticle->type()==KMime::Base::ATremote )
+ knGlobals.artFactory->createReply( static_cast<KNRemoteArticle*>( mArticle ),
+ mViewer->selectedText(), false, true );
+}
+
+
+void ArticleWidget::slotForward()
+{
+ knGlobals.artFactory->createForward( mArticle );
+}
+
+
+void ArticleWidget::slotCancel()
+{
+ knGlobals.artFactory->createCancel( mArticle );
+}
+
+
+void ArticleWidget::slotSupersede()
+{
+ knGlobals.artFactory->createSupersede( mArticle );
+}
+
+
+void ArticleWidget::slotToggleFixedFont()
+{
+ writeConfig();
+ updateContents();
+}
+
+
+void ArticleWidget::slotToggleFancyFormating( )
+{
+ writeConfig();
+ updateContents();
+}
+
+
+void ArticleWidget::slotFancyHeaders()
+{
+ mHeaderStyle = "fancy";
+ writeConfig();
+ updateContents();
+}
+
+void ArticleWidget::slotStandardHeaders()
+{
+ mHeaderStyle = "standard";
+ writeConfig();
+ updateContents();
+}
+
+void ArticleWidget::slotAllHeaders()
+{
+ mHeaderStyle = "all";
+ writeConfig();
+ updateContents();
+}
+
+
+void ArticleWidget::slotIconAttachments()
+{
+ mAttachmentStyle = "icon";
+ writeConfig();
+ updateContents();
+}
+
+void ArticleWidget::slotInlineAttachments()
+{
+ mAttachmentStyle = "inline";
+ writeConfig();
+ updateContents();
+}
+
+void ArticleWidget::slotHideAttachments()
+{
+ mAttachmentStyle = "hide";
+ writeConfig();
+ updateContents();
+}
+
+
+void ArticleWidget::slotToggleRot13()
+{
+ mRot13 = !mRot13;
+ updateContents();
+}
+
+
+
+void ArticleWidget::slotSetCharset( const QString &charset )
+{
+ if ( charset.isEmpty() )
+ return;
+
+ if ( charset == i18n("Automatic") ) {
+ mForceCharset = false;
+ mOverrideCharset = KMime::Headers::Latin1;
+ } else {
+ mForceCharset = true;
+ mOverrideCharset = KGlobal::charsets()->encodingForName( charset ).latin1();
+ }
+
+ if ( mArticle && mArticle->hasContent() ) {
+ mArticle->setDefaultCharset( mOverrideCharset ); // the article will choose the correct default,
+ mArticle->setForceDefaultCS( mForceCharset ); // when we disable the overdrive
+ updateContents();
+ }
+}
+
+
+void ArticleWidget::slotSetCharsetKeyboard( )
+{
+ int charset = KNHelper::selectDialog( this, i18n("Select Charset"),
+ mCharsetSelect->items(), mCharsetSelect->currentItem() );
+ if ( charset != -1 ) {
+ mCharsetSelect->setCurrentItem( charset );
+ slotSetCharset( *(mCharsetSelect->items().at( charset )) );
+ }
+}
+
+
+
+void ArticleWidget::slotOpenURL()
+{
+ slotURLClicked( mCurrentURL );
+}
+
+void ArticleWidget::slotCopyURL()
+{
+ QString address;
+ if ( mCurrentURL.protocol() == "mailto" )
+ address = mCurrentURL.path();
+ else
+ address = mCurrentURL.url();
+ QApplication::clipboard()->setText( address, QClipboard::Clipboard );
+ QApplication::clipboard()->setText( address, QClipboard::Selection );
+}
+
+void ArticleWidget::slotAddBookmark()
+{
+ if ( mCurrentURL.isEmpty() )
+ return;
+ QString filename = locateLocal( "data", QString::fromLatin1("konqueror/bookmarks.xml") );
+ KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename, false );
+ KBookmarkGroup group = bookManager->root();
+ group.addBookmark( bookManager, mCurrentURL.url(), mCurrentURL );
+ bookManager->save();
+}
+
+void ArticleWidget::slotAddToAddressBook()
+{
+ KAddrBookExternal::addEmail( mCurrentURL.path(), this );
+}
+
+void ArticleWidget::slotOpenInAddressBook()
+{
+ KAddrBookExternal::openEmail( mCurrentURL.path(), this );
+}
+
+void ArticleWidget::slotOpenAttachment()
+{
+ slotURLClicked( mCurrentURL, true );
+}
+
+void ArticleWidget::slotSaveAttachment()
+{
+ if ( mCurrentURL.protocol() != "file" && mCurrentURL.protocol() != "part" )
+ return;
+ int partNum = 0;
+ if ( mCurrentURL.protocol() == "file" ) {
+ if ( !mAttachementMap.contains( mCurrentURL.path() ) )
+ return;
+ partNum = mAttachementMap[mCurrentURL.path()];
+ }
+ if ( mCurrentURL.protocol() == "part" )
+ partNum = mCurrentURL.path().toInt();
+ KMime::Content *c = mAttachments.at( partNum );
+ if ( !c )
+ return;
+ knGlobals.articleManager()->saveContentToFile( c, this );
+}
+
+
+
+void ArticleWidget::focusInEvent( QFocusEvent *e )
+{
+ emit focusChanged(e);
+ QWidget::focusInEvent(e);
+}
+
+void ArticleWidget::focusOutEvent( QFocusEvent *e )
+{
+ emit focusChanged(e);
+ QWidget::focusOutEvent(e);
+}
+
+bool ArticleWidget::eventFilter( QObject *o, QEvent *e )
+{
+ if ( e->type() == QEvent::KeyPress && (static_cast<QKeyEvent*>(e)->key() == Key_Tab) ) {
+ emit focusChangeRequest( this );
+ if ( !hasFocus() ) // focusChangeRequest was successful
+ return true;
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+#include "articlewidget.moc"
diff --git a/knode/articlewidget.h b/knode/articlewidget.h
new file mode 100644
index 000000000..d22926196
--- /dev/null
+++ b/knode/articlewidget.h
@@ -0,0 +1,269 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 2005 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNODE_ARTICLEWIDGET_H
+#define KNODE_ARTICLEWIDGET_H
+
+#include <qmap.h>
+#include <qvaluelist.h>
+#include <qwidget.h>
+
+#include <kurl.h>
+
+#include <kmime_content.h>
+
+#include "knjobdata.h"
+
+class QStringList;
+class QTimer;
+
+class KAction;
+class KActionCollection;
+class KActionMenu;
+class KHTMLPart;
+class KURL;
+class KSelectAction;
+class KToggleAction;
+class KXMLGUIClient;
+
+namespace Kpgp {
+ class Block;
+}
+
+class KNArticle;
+class KNArticleCollection;
+
+namespace KNode {
+
+class CSSHelper;
+
+/**
+ Widget to display a news article
+*/
+class ArticleWidget : public QWidget, public KNJobConsumer {
+
+ Q_OBJECT
+
+ public:
+ /// Construct a new article widget
+ ArticleWidget( QWidget *parent,
+ KXMLGUIClient *guiClient,
+ KActionCollection *actionCollection,
+ const char *name = 0 );
+ ~ArticleWidget();
+
+ /// read config settings
+ void readConfig();
+ /// write config settings (call only for the main viewer)
+ void writeConfig();
+
+ /// display the given article
+ void setArticle( KNArticle *article );
+ /// returns the currently shown article
+ KNArticle *article() const { return mArticle; }
+
+ KAction* setCharsetKeyboardAction() const { return mCharsetSelectKeyb; }
+
+ /// notify all instances about a config change
+ static void configChanged();
+ /// check wether the given article is displayed in any instance
+ static bool articleVisible( KNArticle *article );
+ /// notify all instances that the given article has been removed
+ static void articleRemoved( KNArticle *article );
+ /// notify all instances that the given article has changed
+ static void articleChanged( KNArticle *article );
+ /// notify all instances about an error during loading the given article
+ static void articleLoadError( KNArticle *article, const QString &error );
+ /// notify all instances that the given collection has been removed
+ static void collectionRemoved( KNArticleCollection *coll );
+ /// cleanup all instances
+ static void cleanup();
+
+ /// checks wether the readers is scrolled down to the bottom
+ bool atBottom() const;
+
+ public slots:
+ void scrollUp();
+ void scrollDown();
+ void scrollPrior();
+ void scrollNext();
+
+ signals:
+ void focusChanged( QFocusEvent* );
+ void focusChangeRequest( QWidget* );
+
+ protected:
+ /// process download jobs for view source action
+ void processJob( KNJobData *j );
+
+ virtual void focusInEvent( QFocusEvent *e );
+ virtual void focusOutEvent( QFocusEvent *e );
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ private:
+ void initActions();
+
+ /// enable article dependent actions
+ void enableActions();
+ /// disable article dependent actions
+ void disableActions();
+
+ /// clears the article viewer
+ void clear();
+ /// displays the current article or clears the view if no article is set
+ void displayArticle();
+ /// displays the given error message in the viewer
+ void displayErrorMessage( const QString &msg );
+
+ /// display the message header (should be replaced by KMail's HeaderStyle class)
+ void displayHeader();
+ /** displays the given text block, including quote and signature handling
+ * @param lines A list of lines to display.
+ */
+ void displayBodyBlock( const QStringList &lines );
+ /// displays a signature block header
+ QString displaySigHeader( Kpgp::Block* block );
+ /// displays a signature footer
+ void displaySigFooter( const QString &signClass );
+ /// displays the given attachment
+ void displayAttachment( KMime::Content *att, int partNum );
+
+ /// HTML conversion flags for toHtmlString()
+ enum ConversionFlags {
+ None = 0,
+ ParseURL = 1,
+ FancyFormatting = 2,
+ AllowROT13 = 4
+ };
+ /// convert the given string into an HTML string
+ QString toHtmlString( const QString &line, int flags = ParseURL );
+ /// convert the given image into a data:/ URL
+ static QString imgToDataUrl( const QImage &image, const char* fmt );
+
+ /** calculates the quoting depth of the given line
+ * @returns -1 if no quoting was found, the quoting level otherwise
+ */
+ static int quotingDepth( const QString &line, const QString &quoteChars );
+ /// checks wether the given attachment can be shown inline
+ bool inlinePossible( KMime::Content *c );
+ /// checks if the given charset is supported
+ bool canDecodeText( const QCString &charset ) const;
+
+ /// regenerated viewer content without changing scrollbar position
+ void updateContents();
+
+ /** stores the given attachment into a temporary file
+ * @returns the filename the attachment has been stored to
+ */
+ QString writeAttachmentToTempFile( KMime::Content *att, int partNum );
+ /// removes all temporary files
+ void removeTempFiles();
+
+ private slots:
+ /// called if the user clicked on an URL
+ void slotURLClicked( const KURL &url, bool forceOpen = false );
+ /// called if the user RMB clicked on an URL
+ void slotURLPopup( const QString &url, const QPoint &point );
+
+ /// mark as read timeout
+ void slotTimeout();
+
+ void slotSave();
+ void slotPrint();
+ void slotCopySelection();
+ void slotSelectAll();
+ void slotFind();
+ void slotViewSource();
+ void slotReply();
+ void slotRemail();
+ void slotForward();
+ void slotCancel();
+ void slotSupersede();
+ void slotToggleFixedFont();
+ void slotToggleFancyFormating();
+ void slotToggleRot13();
+
+ void slotFancyHeaders();
+ void slotStandardHeaders();
+ void slotAllHeaders();
+
+ void slotIconAttachments();
+ void slotInlineAttachments();
+ void slotHideAttachments();
+
+ void slotSetCharset( const QString &charset );
+ void slotSetCharsetKeyboard();
+
+ void slotOpenURL();
+ void slotCopyURL();
+ void slotAddBookmark();
+ void slotAddToAddressBook();
+ void slotOpenInAddressBook();
+ void slotOpenAttachment();
+ void slotSaveAttachment();
+
+ private:
+ /// the currently shown article
+ KNArticle *mArticle;
+ /// attachments of the current article
+ KMime::Content::List mAttachments;
+ /// mapping of temporary file names to part numbers
+ QMap<QString, int> mAttachementMap;
+
+ KHTMLPart *mViewer;
+ CSSHelper *mCSSHelper;
+
+ QStringList mTempDirs, mTempFiles;
+
+ QString mHeaderStyle;
+ QString mAttachmentStyle;
+ bool mShowHtml;
+ bool mRot13;
+ bool mForceCharset;
+ QCString mOverrideCharset;
+
+ /// mark as read timer
+ QTimer *mTimer;
+
+ /// the last RMB clicked URL
+ KURL mCurrentURL;
+
+ /// list of all instances of this class
+ static QValueList<ArticleWidget*> mInstances;
+
+ KXMLGUIClient *mGuiClient;
+ KActionCollection *mActionCollection;
+
+ KAction *mSaveAction;
+ KAction *mPrintAction;
+ KAction *mCopySelectionAction;
+ KAction *mSelectAllAction;
+ KAction *mFindAction;
+ KAction *mViewSourceAction;
+ KAction *mCharsetSelectKeyb;
+ KAction *mReplyAction;
+ KAction *mRemailAction;
+ KAction *mForwardAction;
+ KAction *mCancelAction;
+ KAction *mSupersedeAction;
+ KActionMenu *mHeaderStyleMenu;
+ KActionMenu *mAttachmentStyleMenu;
+ KToggleAction *mFixedFontToggle;
+ KToggleAction *mFancyToggle;
+ KToggleAction *mRot13Toggle;
+ KSelectAction *mCharsetSelect;
+};
+
+}
+
+#endif
diff --git a/knode/csshelper.cpp b/knode/csshelper.cpp
new file mode 100644
index 000000000..144d52bc8
--- /dev/null
+++ b/knode/csshelper.cpp
@@ -0,0 +1,42 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 2005 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+
+#include "csshelper.h"
+#include "knconfig.h"
+#include "knconfigmanager.h"
+#include "knglobals.h"
+
+
+KNode::CSSHelper::CSSHelper( const QPaintDeviceMetrics &pdm ) :
+ KPIM::CSSHelper( pdm )
+{
+ KNConfig::Appearance *app = knGlobals.configManager()->appearance();
+
+ mForegroundColor = app->textColor();
+ mLinkColor = app->linkColor();
+ mVisitedLinkColor = app->linkColor();
+ mBackgroundColor = app->backgroundColor();
+ for ( int i = 0; i < 3; ++i )
+ mQuoteColor[i] = app->quoteColor( i );
+
+ cHtmlWarning = app->htmlWarningColor();
+ cPgpOk1H = app->signOkKeyOkColor();
+ cPgpOk0H = app->signOkKeyBadColor();
+ cPgpWarnH = app->signWarnColor();
+ cPgpErrH = app->signErrColor();
+
+ mBodyFont = mPrintFont = app->articleFont();
+ mFixedFont = mFixedPrintFont = app->articleFixedFont();
+
+ recalculatePGPColors();
+}
diff --git a/knode/csshelper.h b/knode/csshelper.h
new file mode 100644
index 000000000..23b2216a0
--- /dev/null
+++ b/knode/csshelper.h
@@ -0,0 +1,30 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 2005 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNODE_CSSHELPER_H
+#define KNODE_CSSHELPER_H
+
+#include <libkdepim/csshelper.h>
+
+namespace KNode {
+
+class CSSHelper : public KPIM::CSSHelper
+{
+ public:
+ CSSHelper( const QPaintDeviceMetrics &pdm );
+
+};
+
+}
+
+#endif
diff --git a/knode/filters/1.fltr b/knode/filters/1.fltr
new file mode 100644
index 000000000..f66021cd3
--- /dev/null
+++ b/knode/filters/1.fltr
@@ -0,0 +1,42 @@
+[AGE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[FROM]
+Data=
+contains=true
+enabled=false
+regX=false
+[GENERAL]
+Translate_Name=true
+applyOn=0
+enabled=true
+name=all
+[LINES]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[SCORE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[STATUS]
+DAT_N=false
+DAT_NS=false
+DAT_R=false
+DAT_US=false
+EN_N=false
+EN_NS=false
+EN_R=false
+EN_US=false
+[SUBJECT]
+Data=
+contains=true
+enabled=false
+regX=false
diff --git a/knode/filters/2.fltr b/knode/filters/2.fltr
new file mode 100644
index 000000000..7b11f48e2
--- /dev/null
+++ b/knode/filters/2.fltr
@@ -0,0 +1,42 @@
+[AGE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[FROM]
+Data=
+contains=true
+enabled=false
+regX=false
+[GENERAL]
+Translate_Name=true
+applyOn=0
+enabled=true
+name=unread
+[LINES]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[SCORE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[STATUS]
+DAT_N=false
+DAT_NS=false
+DAT_R=false
+DAT_US=false
+EN_N=false
+EN_NS=false
+EN_R=true
+EN_US=false
+[SUBJECT]
+Data=
+contains=true
+enabled=false
+regX=false
diff --git a/knode/filters/3.fltr b/knode/filters/3.fltr
new file mode 100644
index 000000000..e7c3e0f6e
--- /dev/null
+++ b/knode/filters/3.fltr
@@ -0,0 +1,42 @@
+[AGE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[FROM]
+Data=
+contains=true
+enabled=false
+regX=false
+[GENERAL]
+Translate_Name=true
+applyOn=0
+enabled=true
+name=new
+[LINES]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[SCORE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[STATUS]
+DAT_N=true
+DAT_NS=false
+DAT_R=false
+DAT_US=false
+EN_N=true
+EN_NS=false
+EN_R=false
+EN_US=false
+[SUBJECT]
+Data=
+contains=true
+enabled=false
+regX=false
diff --git a/knode/filters/4.fltr b/knode/filters/4.fltr
new file mode 100644
index 000000000..4972266fd
--- /dev/null
+++ b/knode/filters/4.fltr
@@ -0,0 +1,42 @@
+[AGE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[FROM]
+Data=
+contains=true
+enabled=false
+regX=false
+[GENERAL]
+Translate_Name=true
+applyOn=0
+enabled=true
+name=watched
+[LINES]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[SCORE]
+enabled=true
+op1=0
+op2=5
+val1=0
+val2=0
+[STATUS]
+DAT_N=false
+DAT_NS=false
+DAT_R=false
+DAT_US=false
+EN_N=false
+EN_NS=false
+EN_R=false
+EN_US=false
+[SUBJECT]
+Data=
+contains=true
+enabled=false
+regX=false
diff --git a/knode/filters/5.fltr b/knode/filters/5.fltr
new file mode 100644
index 000000000..24ca696b0
--- /dev/null
+++ b/knode/filters/5.fltr
@@ -0,0 +1,42 @@
+[AGE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[FROM]
+Data=
+contains=true
+enabled=false
+regX=false
+[GENERAL]
+Translate_Name=true
+applyOn=1
+enabled=true
+name=threads with unread
+[LINES]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[SCORE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[STATUS]
+DAT_N=false
+DAT_NS=false
+DAT_R=false
+DAT_US=false
+EN_N=false
+EN_NS=false
+EN_R=true
+EN_US=false
+[SUBJECT]
+Data=
+contains=true
+enabled=false
+regX=false
diff --git a/knode/filters/6.fltr b/knode/filters/6.fltr
new file mode 100644
index 000000000..7ad3d6124
--- /dev/null
+++ b/knode/filters/6.fltr
@@ -0,0 +1,42 @@
+[AGE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[FROM]
+Data=
+contains=true
+enabled=false
+regX=false
+[GENERAL]
+Translate_Name=true
+applyOn=1
+enabled=true
+name=threads with new
+[LINES]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[SCORE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[STATUS]
+DAT_N=true
+DAT_NS=false
+DAT_R=false
+DAT_US=false
+EN_N=true
+EN_NS=false
+EN_R=false
+EN_US=false
+[SUBJECT]
+Data=
+contains=true
+enabled=false
+regX=false
diff --git a/knode/filters/7.fltr b/knode/filters/7.fltr
new file mode 100644
index 000000000..81fec0cde
--- /dev/null
+++ b/knode/filters/7.fltr
@@ -0,0 +1,42 @@
+[AGE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[FROM]
+Data=%MYNAME
+contains=true
+enabled=true
+regX=false
+[GENERAL]
+Translate_Name=true
+applyOn=0
+enabled=true
+name=own articles
+[LINES]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[SCORE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[STATUS]
+DAT_N=false
+DAT_NS=false
+DAT_R=false
+DAT_US=false
+EN_N=false
+EN_NS=false
+EN_R=false
+EN_US=false
+[SUBJECT]
+Data=
+contains=true
+enabled=false
+regX=false
diff --git a/knode/filters/8.fltr b/knode/filters/8.fltr
new file mode 100644
index 000000000..5978c616f
--- /dev/null
+++ b/knode/filters/8.fltr
@@ -0,0 +1,42 @@
+[AGE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[FROM]
+Data=%MYNAME
+contains=true
+enabled=true
+regX=false
+[GENERAL]
+Translate_Name=true
+applyOn=1
+enabled=true
+name=threads with own articles
+[LINES]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[SCORE]
+enabled=false
+op1=2
+op2=0
+val1=0
+val2=0
+[STATUS]
+DAT_N=false
+DAT_NS=false
+DAT_R=false
+DAT_US=false
+EN_N=false
+EN_NS=false
+EN_R=false
+EN_US=false
+[SUBJECT]
+Data=
+contains=true
+enabled=false
+regX=false
diff --git a/knode/filters/Makefile.am b/knode/filters/Makefile.am
new file mode 100644
index 000000000..1ddad6fb9
--- /dev/null
+++ b/knode/filters/Makefile.am
@@ -0,0 +1,4 @@
+
+appsdir = $(kde_datadir)/knode/filters
+apps_DATA = 1.fltr 2.fltr 3.fltr 4.fltr 5.fltr 6.fltr 7.fltr 8.fltr filters.rc
+
diff --git a/knode/filters/filters.rc b/knode/filters/filters.rc
new file mode 100644
index 000000000..b2ec22121
--- /dev/null
+++ b/knode/filters/filters.rc
@@ -0,0 +1,2 @@
+Active=1,2,3,4,5,6,7,8
+Menu=1,-1,2,3,4,7,-1,8,5,6
diff --git a/knode/headers.rc b/knode/headers.rc
new file mode 100644
index 000000000..ccf409a2e
--- /dev/null
+++ b/knode/headers.rc
@@ -0,0 +1,35 @@
+[000]
+Flags=0,0,0,0,1,1,0,0
+Header=Subject
+Name=
+Translate_Name=true
+[001]
+Flags=0,1,0,0,0,0,0,0
+Header=From
+Name=From
+Translate_Name=true
+[002]
+Flags=0,1,0,0,0,0,0,0
+Header=Reply-To
+Name=Reply-To
+Translate_Name=true
+[003]
+Flags=0,1,0,0,0,0,0,0
+Header=Date
+Name=Date
+Translate_Name=true
+[004]
+Flags=0,1,0,0,0,0,0,0
+Header=To
+Name=To
+Translate_Name=true
+[005]
+Flags=0,1,0,0,0,0,0,0
+Header=Newsgroups
+Name=Groups
+Translate_Name=true
+[006]
+Flags=0,1,0,0,0,0,0,0
+Header=Followup-To
+Name=Followup-To
+Translate_Name=true
diff --git a/knode/headerview.cpp b/knode/headerview.cpp
new file mode 100644
index 000000000..aef02f13a
--- /dev/null
+++ b/knode/headerview.cpp
@@ -0,0 +1,617 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qcursor.h>
+#include <qheader.h>
+#include <qstylesheet.h>
+#include <qtimer.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kpopupmenu.h>
+
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "headerview.h"
+#include "knhdrviewitem.h"
+#include "kngroupmanager.h"
+#include "knarticle.h"
+#include "knarticlemanager.h"
+#include "knmainwidget.h"
+
+
+KNHeaderView::KNHeaderView(QWidget *parent, const char *name) :
+ KListView(parent,name),
+ mSortCol( -1 ),
+ mSortAsc( true ),
+ mSortByThreadChangeDate( false ),
+ mDelayedCenter( -1 ),
+ mActiveItem( 0 ),
+ mShowingFolder( false ),
+ mInitDone( false )
+{
+ mPaintInfo.subCol = addColumn( i18n("Subject"), 310 );
+ mPaintInfo.senderCol = addColumn( i18n("From"), 115 );
+ mPaintInfo.scoreCol = addColumn( i18n("Score"), 42 );
+ mPaintInfo.sizeCol = addColumn( i18n("Lines"), 42 );
+ mPaintInfo.dateCol = addColumn( i18n("Date"), 102 );
+
+ setDropVisualizer( false );
+ setDropHighlighter( false );
+ setItemsRenameable( false );
+ setItemsMovable( false );
+ setAcceptDrops( false );
+ setDragEnabled( true );
+ setAllColumnsShowFocus( true );
+ setSelectionMode( QListView::Extended );
+ setShowSortIndicator( true );
+ setShadeSortColumn ( true );
+ setRootIsDecorated( true );
+ setSorting( mPaintInfo.dateCol );
+ header()->setMovingEnabled( true );
+ setColumnAlignment( mPaintInfo.sizeCol, Qt::AlignRight );
+ setColumnAlignment( mPaintInfo.scoreCol, Qt::AlignRight );
+
+ // due to our own column text squeezing we need to repaint on column resizing
+ disconnect( header(), SIGNAL(sizeChange(int, int, int)) );
+ connect( header(), SIGNAL(sizeChange(int, int, int)),
+ SLOT(slotSizeChanged(int, int, int)) );
+
+ // column selection RMB menu
+ mPopup = new KPopupMenu( this );
+ mPopup->insertTitle( i18n("View Columns") );
+ mPopup->setCheckable( true );
+ mPopup->insertItem( i18n("Line Count"), KPaintInfo::COL_SIZE );
+ mPopup->insertItem( i18n("Score"), KPaintInfo::COL_SCORE );
+
+ connect( mPopup, SIGNAL(activated(int)), this, SLOT(toggleColumn(int)) );
+
+ // connect to the article manager
+ connect( knGlobals.articleManager(), SIGNAL(aboutToShowGroup()), SLOT(prepareForGroup()) );
+ connect( knGlobals.articleManager(), SIGNAL(aboutToShowFolder()), SLOT(prepareForFolder()) );
+
+ new KNHeaderViewToolTip( this );
+
+ installEventFilter( this );
+}
+
+
+KNHeaderView::~KNHeaderView()
+{
+ // ### crash because KNConfigManager is already deleted here
+ // writeConfig();
+}
+
+
+void KNHeaderView::readConfig()
+{
+ if ( !mInitDone ) {
+ KConfig *conf = knGlobals.config();
+ conf->setGroup( "HeaderView" );
+ mSortByThreadChangeDate = conf->readBoolEntry( "sortByThreadChangeDate", false );
+ restoreLayout( conf, "HeaderView" );
+ mInitDone = true;
+ }
+
+ KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral();
+ toggleColumn( KPaintInfo::COL_SIZE, rngConf->showLines() );
+ if ( !mShowingFolder ) // score column is always hidden when showing a folder
+ toggleColumn( KPaintInfo::COL_SCORE, rngConf->showScore() );
+
+ mDateFormatter.setCustomFormat( rngConf->dateCustomFormat() );
+ mDateFormatter.setFormat( rngConf->dateFormat() );
+
+ KNConfig::Appearance *app = knGlobals.configManager()->appearance();
+ QPalette p = palette();
+ p.setColor( QColorGroup::Base, app->backgroundColor() );
+ p.setColor( QColorGroup::Text, app->textColor() );
+ setPalette( p );
+ setAlternateBackground( app->alternateBackgroundColor() );
+ setFont( app->articleListFont() );
+}
+
+
+void KNHeaderView::writeConfig()
+{
+ KConfig *conf = knGlobals.config();
+ conf->setGroup( "HeaderView" );
+ conf->writeEntry( "sortByThreadChangeDate", mSortByThreadChangeDate );
+ saveLayout( conf, "HeaderView" );
+
+ KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral();
+ rngConf->setShowLines( mPaintInfo.showSize );
+ if ( !mShowingFolder ) // score column is always hidden when showing a folder
+ rngConf->setShowScore( mPaintInfo.showScore );
+}
+
+
+void KNHeaderView::setActive( QListViewItem *i )
+{
+ KNHdrViewItem *item = static_cast<KNHdrViewItem*>( i );
+
+ if ( !item || item->isActive() )
+ return;
+
+ if ( mActiveItem ) {
+ mActiveItem->setActive( false );
+ repaintItem( mActiveItem );
+ mActiveItem = 0;
+ }
+
+ item->setActive( true );
+ setSelected( item, true );
+ setCurrentItem( i );
+ ensureItemVisibleWithMargin( i );
+ mActiveItem = item;
+ emit( itemSelected(item) );
+}
+
+
+void KNHeaderView::clear()
+{
+ mActiveItem = 0;
+ QListView::clear();
+}
+
+
+void KNHeaderView::ensureItemVisibleWithMargin( const QListViewItem *i )
+{
+ if ( !i )
+ return;
+
+ QListViewItem *parent = i->parent();
+ while ( parent ) {
+ if ( !parent->isOpen() )
+ parent->setOpen( true );
+ parent = parent->parent();
+ }
+
+ mDelayedCenter = -1;
+ int y = itemPos( i );
+ int h = i->height();
+
+ if ( knGlobals.configManager()->readNewsGeneral()->smartScrolling() &&
+ ((y + h + 5) >= (contentsY() + visibleHeight()) ||
+ (y - 5 < contentsY())) )
+ {
+ ensureVisible( contentsX(), y + h/2, 0, h/2 );
+ mDelayedCenter = y + h/2;
+ QTimer::singleShot( 300, this, SLOT(slotCenterDelayed()) );
+ } else {
+ ensureVisible( contentsX(), y + h/2, 0, h/2 );
+ }
+}
+
+
+void KNHeaderView::slotCenterDelayed()
+{
+ if ( mDelayedCenter != -1 )
+ ensureVisible( contentsX(), mDelayedCenter, 0, visibleHeight() / 2 );
+}
+
+
+void KNHeaderView::setSorting( int column, bool ascending )
+{
+ if ( column == mSortCol ) {
+ mSortAsc = ascending;
+ if ( mInitDone && column == mPaintInfo.dateCol && ascending )
+ mSortByThreadChangeDate = !mSortByThreadChangeDate;
+ } else {
+ mSortCol = column;
+ emit sortingChanged( column );
+ }
+
+ KListView::setSorting( column, ascending );
+
+ if ( currentItem() )
+ ensureItemVisible( currentItem() );
+
+ if ( mSortByThreadChangeDate )
+ setColumnText( mPaintInfo.dateCol , i18n("Date (thread changed)") );
+ else
+ setColumnText( mPaintInfo.dateCol, i18n("Date") );
+}
+
+
+void KNHeaderView::nextArticle()
+{
+ KNHdrViewItem *it = static_cast<KNHdrViewItem*>( currentItem() );
+
+ if (it) {
+ if (it->isActive()) { // take current article, if not selected
+ if (it->isExpandable())
+ it->setOpen(true);
+ it = static_cast<KNHdrViewItem*>(it->itemBelow());
+ }
+ } else
+ it = static_cast<KNHdrViewItem*>( firstChild() );
+
+ if(it) {
+ clearSelection();
+ setActive( it );
+ setSelectionAnchor( currentItem() );
+ }
+}
+
+
+void KNHeaderView::prevArticle()
+{
+ KNHdrViewItem *it = static_cast<KNHdrViewItem*>( currentItem() );
+
+ if (it && it->isActive()) { // take current article, if not selected
+ if (it)
+ it = static_cast<KNHdrViewItem*>(it->itemAbove());
+ else
+ it = static_cast<KNHdrViewItem*>( firstChild() );
+ }
+
+ if (it) {
+ clearSelection();
+ setActive( it );
+ setSelectionAnchor( currentItem() );
+ }
+}
+
+
+void KNHeaderView::incCurrentArticle()
+{
+ QListViewItem *lvi = currentItem();
+ if ( lvi && lvi->isExpandable() )
+ lvi->setOpen( true );
+ if ( lvi && lvi->itemBelow() ) {
+ setCurrentItem( lvi->itemBelow() );
+ ensureItemVisible( currentItem() );
+ setFocus();
+ }
+}
+
+void KNHeaderView::decCurrentArticle()
+{
+ QListViewItem *lvi = currentItem();
+ if ( lvi && lvi->itemAbove() ) {
+ if ( lvi->itemAbove()->isExpandable() )
+ lvi->itemAbove()->setOpen( true );
+ setCurrentItem( lvi->itemAbove() );
+ ensureItemVisible( currentItem() );
+ setFocus();
+ }
+}
+
+
+void KNHeaderView::selectCurrentArticle()
+{
+ clearSelection();
+ setActive( currentItem() );
+}
+
+
+bool KNHeaderView::nextUnreadArticle()
+{
+ if ( !knGlobals.groupManager()->currentGroup() )
+ return false;
+
+ KNHdrViewItem *next, *current;
+ KNRemoteArticle *art;
+
+ current = static_cast<KNHdrViewItem*>( currentItem() );
+ if ( !current )
+ current = static_cast<KNHdrViewItem*>( firstChild() );
+
+ if(!current)
+ return false;
+
+ art = static_cast<KNRemoteArticle*>( current->art );
+
+ if ( !current->isActive() && !art->isRead() ) // take current article, if unread & not selected
+ next = current;
+ else {
+ if ( current->isExpandable() && art->hasUnreadFollowUps() && !current->isOpen() )
+ setOpen( current, true );
+ next = static_cast<KNHdrViewItem*>( current->itemBelow() );
+ }
+
+ while ( next ) {
+ art = static_cast<KNRemoteArticle*>( next->art );
+ if ( !art->isRead() )
+ break;
+ else {
+ if ( next->isExpandable() && art->hasUnreadFollowUps() && !next->isOpen() )
+ setOpen( next, true );
+ next = static_cast<KNHdrViewItem*>( next->itemBelow() );
+ }
+ }
+
+ if ( next ) {
+ clearSelection();
+ setActive( next );
+ setSelectionAnchor( currentItem() );
+ return true;
+ }
+ return false;
+}
+
+
+bool KNHeaderView::nextUnreadThread()
+{
+ KNHdrViewItem *next, *current;
+ KNRemoteArticle *art;
+
+ if ( !knGlobals.groupManager()->currentGroup() )
+ return false;
+
+ current = static_cast<KNHdrViewItem*>( currentItem() );
+ if ( !current )
+ current = static_cast<KNHdrViewItem*>( firstChild() );
+
+ if ( !current )
+ return false;
+
+ art = static_cast<KNRemoteArticle*>( current->art );
+
+ if ( current->depth() == 0 && !current->isActive() && (!art->isRead() || art->hasUnreadFollowUps()) )
+ next = current; // take current article, if unread & not selected
+ else
+ next = static_cast<KNHdrViewItem*>( current->itemBelow() );
+
+ while ( next ) {
+ art = static_cast<KNRemoteArticle*>( next->art );
+
+ if ( next->depth() == 0 ) {
+ if ( !art->isRead() || art->hasUnreadFollowUps() )
+ break;
+ }
+ next = static_cast<KNHdrViewItem*>( next->itemBelow() );
+ }
+
+ if ( next ) {
+ setCurrentItem( next );
+ if ( art->isRead() )
+ nextUnreadArticle();
+ else {
+ clearSelection();
+ setActive( next );
+ setSelectionAnchor( currentItem() );
+ }
+ return true;
+ }
+ return false;
+}
+
+
+void KNHeaderView::toggleColumn( int column, int mode )
+{
+ bool *show = 0;
+ int *col = 0;
+ int width = 0;
+
+ switch ( static_cast<KPaintInfo::ColumnIds>( column ) )
+ {
+ case KPaintInfo::COL_SIZE:
+ show = &mPaintInfo.showSize;
+ col = &mPaintInfo.sizeCol;
+ width = 42;
+ break;
+ case KPaintInfo::COL_SCORE:
+ show = &mPaintInfo.showScore;
+ col = &mPaintInfo.scoreCol;
+ width = 42;
+ break;
+ default:
+ return;
+ }
+
+ if ( mode == -1 )
+ *show = !*show;
+ else
+ *show = mode;
+
+ mPopup->setItemChecked( column, *show );
+
+ if (*show) {
+ header()->setResizeEnabled( true, *col );
+ setColumnWidth( *col, width );
+ }
+ else {
+ header()->setResizeEnabled( false, *col );
+ header()->setStretchEnabled( false, *col );
+ hideColumn( *col );
+ }
+
+ if ( mode == -1 ) // save config when toggled
+ writeConfig();
+}
+
+
+void KNHeaderView::prepareForGroup()
+{
+ mShowingFolder = false;
+ header()->setLabel( mPaintInfo.senderCol, i18n("From") );
+ KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral();
+ toggleColumn( KPaintInfo::COL_SCORE, rngConf->showScore() );
+}
+
+
+void KNHeaderView::prepareForFolder()
+{
+ mShowingFolder = true;
+ header()->setLabel( mPaintInfo.senderCol, i18n("Newsgroups / To") );
+ toggleColumn( KPaintInfo::COL_SCORE, false ); // local folders have no score
+}
+
+
+bool KNHeaderView::event( QEvent *e )
+{
+ // we don't want to have the alternate list background restored
+ // to the system defaults!
+ if (e->type() == QEvent::ApplicationPaletteChange)
+ return QListView::event(e);
+ else
+ return KListView::event(e);
+}
+
+void KNHeaderView::contentsMousePressEvent( QMouseEvent *e )
+{
+ if (!e) return;
+
+ bool selectMode=(( e->state() & ShiftButton ) || ( e->state() & ControlButton ));
+
+ QPoint vp = contentsToViewport(e->pos());
+ QListViewItem *i = itemAt(vp);
+
+ KListView::contentsMousePressEvent( e );
+
+ if ( i ) {
+ int decoLeft = header()->sectionPos( 0 ) +
+ treeStepSize() * ( (i->depth() - 1) + ( rootIsDecorated() ? 1 : 0) );
+ int decoRight = kMin( decoLeft + treeStepSize() + itemMargin(),
+ header()->sectionPos( 0 ) + header()->sectionSize( 0 ) );
+ bool rootDecoClicked = vp.x() > decoLeft && vp.x() < decoRight;
+
+ if( !selectMode && i->isSelected() && !rootDecoClicked )
+ setActive( i );
+ }
+}
+
+
+void KNHeaderView::contentsMouseDoubleClickEvent( QMouseEvent *e )
+{
+ if (!e) return;
+
+ QListViewItem *i = itemAt( contentsToViewport(e->pos()) );
+ if (i) {
+ emit doubleClick( i );
+ return;
+ }
+
+ KListView::contentsMouseDoubleClickEvent( e );
+}
+
+
+void KNHeaderView::keyPressEvent(QKeyEvent *e)
+{
+ if (!e) return;
+
+ QListViewItem *i = currentItem();
+
+ switch(e->key()) {
+ case Key_Space:
+ case Key_Backspace:
+ case Key_Delete:
+ e->ignore(); // don't eat them
+ break;
+ case Key_Enter:
+ case Key_Return:
+ setActive( i );
+ break;
+
+ default:
+ KListView::keyPressEvent (e);
+ }
+}
+
+
+QDragObject* KNHeaderView::dragObject()
+{
+ KNHdrViewItem *item = static_cast<KNHdrViewItem*>( itemAt(viewport()->mapFromGlobal(QCursor::pos())) );
+ if (item)
+ return item->dragObject();
+ else
+ return 0;
+}
+
+
+void KNHeaderView::slotSizeChanged( int section, int, int newSize )
+{
+ viewport()->repaint( header()->sectionPos(section), 0, newSize, visibleHeight(), false);
+}
+
+
+bool KNHeaderView::eventFilter(QObject *o, QEvent *e)
+{
+ if ((e->type() == QEvent::KeyPress) && (static_cast<QKeyEvent*>(e)->key() == Key_Tab)) {
+ emit(focusChangeRequest(this));
+ if (!hasFocus()) // focusChangeRequest was successful
+ return true;
+ }
+
+ // right click on header
+ if ( e->type() == QEvent::MouseButtonPress &&
+ static_cast<QMouseEvent*>(e)->button() == RightButton &&
+ o->isA("QHeader") )
+ {
+ mPopup->popup( static_cast<QMouseEvent*>(e)->globalPos() );
+ return true;
+ }
+
+ return KListView::eventFilter(o, e);
+}
+
+
+void KNHeaderView::focusInEvent(QFocusEvent *e)
+{
+ QListView::focusInEvent(e);
+ emit focusChanged(e);
+}
+
+
+void KNHeaderView::focusOutEvent(QFocusEvent *e)
+{
+ QListView::focusOutEvent(e);
+ emit focusChanged(e);
+}
+
+
+void KNHeaderView::resetCurrentTime()
+{
+ mDateFormatter.reset();
+ QTimer::singleShot( 1000, this, SLOT(resetCurrentTime()) );
+}
+
+
+//BEGIN: KNHeaderViewToolTip ==================================================
+
+KNHeaderViewToolTip::KNHeaderViewToolTip( KNHeaderView *parent ) :
+ QToolTip( parent->viewport() ),
+ listView( parent )
+{
+}
+
+
+void KNHeaderViewToolTip::maybeTip( const QPoint &p )
+{
+ const KNHdrViewItem *item = static_cast<KNHdrViewItem*>( listView->itemAt( p ) );
+ if ( !item )
+ return;
+ const int column = listView->header()->sectionAt( p.x() );
+ if ( column == -1 )
+ return;
+
+ if ( !item->showToolTip( column ) )
+ return;
+
+ const QRect itemRect = listView->itemRect( item );
+ if ( !itemRect.isValid() )
+ return;
+ const QRect headerRect = listView->header()->sectionRect( column );
+ if ( !headerRect.isValid() )
+ return;
+
+ tip( QRect( headerRect.left(), itemRect.top(), headerRect.width(), itemRect.height() ),
+ QStyleSheet::escape( item->text( column ) ) );
+}
+
+//END: KNHeaderViewToolTip ====================================================
+
+#include "headerview.moc"
diff --git a/knode/headerview.h b/knode/headerview.h
new file mode 100644
index 000000000..411258d2f
--- /dev/null
+++ b/knode/headerview.h
@@ -0,0 +1,120 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNHEADERVIEW_H
+#define KNHEADERVIEW_H
+
+#include <qtooltip.h>
+
+#include <klistview.h>
+#include <kfoldertree.h>
+#include <kmime_util.h>
+
+class KPopupMenu;
+class KNHdrViewItem;
+
+class KNHeaderView : public KListView {
+
+ Q_OBJECT
+
+ friend class KNHdrViewItem;
+
+ public:
+ KNHeaderView( QWidget *parent, const char *name = 0 );
+ ~KNHeaderView();
+
+ void setActive( QListViewItem *item );
+ void clear();
+
+ void ensureItemVisibleWithMargin( const QListViewItem *i );
+
+ virtual void setSorting( int column, bool ascending = true );
+ bool sortByThreadChangeDate() const { return mSortByThreadChangeDate; }
+ void setSortByThreadChangeDate( bool b ) { mSortByThreadChangeDate = b; }
+
+ bool nextUnreadArticle();
+ bool nextUnreadThread();
+
+ void readConfig();
+ void writeConfig();
+
+ const KPaintInfo* paintInfo() const { return &mPaintInfo; }
+
+ signals:
+ void itemSelected( QListViewItem* );
+ void doubleClick( QListViewItem* );
+ void sortingChanged( int );
+ void focusChanged( QFocusEvent* );
+ void focusChangeRequest( QWidget* );
+
+ public slots:
+ void nextArticle();
+ void prevArticle();
+ void incCurrentArticle();
+ void decCurrentArticle();
+ void selectCurrentArticle();
+
+ void toggleColumn( int column, int mode = -1 );
+ void prepareForGroup();
+ void prepareForFolder();
+
+ protected:
+ void activeRemoved() { mActiveItem = 0; }
+ /**
+ * Reimplemented to avoid that KListview reloads the alternate
+ * background on palette changes.
+ */
+ virtual bool event( QEvent *e );
+ void contentsMousePressEvent( QMouseEvent *e );
+ void contentsMouseDoubleClickEvent( QMouseEvent *e );
+ void keyPressEvent( QKeyEvent *e );
+ bool eventFilter( QObject *, QEvent * );
+ void focusInEvent( QFocusEvent *e );
+ void focusOutEvent( QFocusEvent *e );
+ virtual QDragObject* dragObject();
+
+ private:
+ int mSortCol;
+ bool mSortAsc;
+ bool mSortByThreadChangeDate;
+ int mDelayedCenter;
+ KNHdrViewItem *mActiveItem;
+ KPaintInfo mPaintInfo;
+ KMime::DateFormatter mDateFormatter;
+ KPopupMenu *mPopup;
+ bool mShowingFolder;
+ bool mInitDone;
+
+ private slots:
+ void slotCenterDelayed();
+ void slotSizeChanged( int, int, int );
+ void resetCurrentTime();
+
+};
+
+
+class KNHeaderViewToolTip : public QToolTip {
+
+ public:
+ KNHeaderViewToolTip( KNHeaderView *parent );
+
+ protected:
+ void maybeTip( const QPoint &p );
+
+ private:
+ KNHeaderView *listView;
+
+};
+
+#endif
diff --git a/knode/hi128-app-knode.png b/knode/hi128-app-knode.png
new file mode 100644
index 000000000..2a9aea36e
--- /dev/null
+++ b/knode/hi128-app-knode.png
Binary files differ
diff --git a/knode/hi128-app-knode2.png b/knode/hi128-app-knode2.png
new file mode 100644
index 000000000..883a49617
--- /dev/null
+++ b/knode/hi128-app-knode2.png
Binary files differ
diff --git a/knode/hi16-app-knode.png b/knode/hi16-app-knode.png
new file mode 100644
index 000000000..d99813134
--- /dev/null
+++ b/knode/hi16-app-knode.png
Binary files differ
diff --git a/knode/hi16-app-knode2.png b/knode/hi16-app-knode2.png
new file mode 100644
index 000000000..75c186327
--- /dev/null
+++ b/knode/hi16-app-knode2.png
Binary files differ
diff --git a/knode/hi32-app-knode.png b/knode/hi32-app-knode.png
new file mode 100644
index 000000000..1c3e88e9f
--- /dev/null
+++ b/knode/hi32-app-knode.png
Binary files differ
diff --git a/knode/hi32-app-knode2.png b/knode/hi32-app-knode2.png
new file mode 100644
index 000000000..74a8af16e
--- /dev/null
+++ b/knode/hi32-app-knode2.png
Binary files differ
diff --git a/knode/hi48-app-knode.png b/knode/hi48-app-knode.png
new file mode 100644
index 000000000..521e1ee33
--- /dev/null
+++ b/knode/hi48-app-knode.png
Binary files differ
diff --git a/knode/hi48-app-knode2.png b/knode/hi48-app-knode2.png
new file mode 100644
index 000000000..7b2c5d59f
--- /dev/null
+++ b/knode/hi48-app-knode2.png
Binary files differ
diff --git a/knode/hi64-app-knode.png b/knode/hi64-app-knode.png
new file mode 100644
index 000000000..d910f150e
--- /dev/null
+++ b/knode/hi64-app-knode.png
Binary files differ
diff --git a/knode/hi64-app-knode2.png b/knode/hi64-app-knode2.png
new file mode 100644
index 000000000..9ff8b9ec6
--- /dev/null
+++ b/knode/hi64-app-knode2.png
Binary files differ
diff --git a/knode/knaccountmanager.cpp b/knode/knaccountmanager.cpp
new file mode 100644
index 000000000..be43e56b0
--- /dev/null
+++ b/knode/knaccountmanager.cpp
@@ -0,0 +1,305 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <stdlib.h>
+
+#include <qdir.h>
+
+#include <kdebug.h>
+#include <ksimpleconfig.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kwallet.h>
+
+#include "kngroupmanager.h"
+#include "knnntpaccount.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "utilities.h"
+#include "knaccountmanager.h"
+#include "knfoldermanager.h"
+
+KWallet::Wallet* KNAccountManager::mWallet = 0;
+bool KNAccountManager::mWalletOpenFailed = false;
+
+KNAccountManager::KNAccountManager(KNGroupManager *gm, QObject * parent, const char * name)
+ : QObject(parent, name), gManager(gm), c_urrentAccount(0),
+ mAsyncOpening( false )
+{
+ s_mtp = new KNServerInfo();
+ s_mtp->setType(KNServerInfo::STsmtp);
+ s_mtp->setId(0);
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("MAILSERVER");
+ s_mtp->readConf(conf);
+
+ loadAccounts();
+}
+
+
+KNAccountManager::~KNAccountManager()
+{
+ QValueList<KNNntpAccount*>::Iterator it;
+ for ( it = mAccounts.begin(); it != mAccounts.end(); ++it )
+ delete (*it);
+ mAccounts.clear();
+ delete s_mtp;
+ delete mWallet;
+ mWallet = 0;
+}
+
+
+void KNAccountManager::prepareShutdown()
+{
+ QValueList<KNNntpAccount*>::Iterator it;
+ for ( it = mAccounts.begin(); it != mAccounts.end(); ++it )
+ (*it)->saveInfo();
+}
+
+
+void KNAccountManager::loadAccounts()
+{
+ QString dir(locateLocal("data","knode/"));
+ if (dir.isNull()) {
+ KNHelper::displayInternalFileError();
+ return;
+ }
+ QDir d(dir);
+ KNNntpAccount *a;
+ QStringList entries(d.entryList("nntp.*", QDir::Dirs));
+
+ QStringList::Iterator it;
+ for(it = entries.begin(); it != entries.end(); ++it) {
+ a = new KNNntpAccount();
+ if (a->readInfo(dir+(*it) + "/info")) {
+ mAccounts.append(a);
+ gManager->loadGroups(a);
+ emit accountAdded(a);
+ } else {
+ delete a;
+ kdError(5003) << "Unable to load account " << (*it) << "!" << endl;
+ }
+ }
+}
+
+
+KNNntpAccount* KNAccountManager::account( int id )
+{
+ if ( id <= 0 )
+ return 0;
+ QValueList<KNNntpAccount*>::ConstIterator it;
+ for ( it = mAccounts.begin(); it != mAccounts.end(); ++it )
+ if ( (*it)->id() == id )
+ return *it;
+ return 0;
+}
+
+
+void KNAccountManager::setCurrentAccount(KNNntpAccount *a)
+{
+ c_urrentAccount = a;
+}
+
+
+// a is new account allocated and configured by the caller
+bool KNAccountManager::newAccount(KNNntpAccount *a)
+{
+ // find a unused id for the new account...
+ QString dir(locateLocal("data","knode/"));
+ if (dir.isNull()) {
+ delete a;
+ KNHelper::displayInternalFileError();
+ return false;
+ }
+ QDir d(dir);
+ QStringList entries(d.entryList("nntp.*", QDir::Dirs));
+
+ int id = 1;
+ while (entries.findIndex(QString("nntp.%1").arg(id))!=-1)
+ ++id;
+
+ a->setId(id);
+
+ dir = locateLocal("data",QString("knode/nntp.%1/").arg(a->id()));
+ if (!dir.isNull()) {
+ mAccounts.append(a);
+ emit(accountAdded(a));
+ return true;
+ } else {
+ delete a;
+ KMessageBox::error(knGlobals.topWidget, i18n("Cannot create a folder for this account."));
+ return false;
+ }
+}
+
+
+// a==0: remove current account
+bool KNAccountManager::removeAccount(KNNntpAccount *a)
+{
+ if(!a) a=c_urrentAccount;
+ if(!a) return false;
+
+ QValueList<KNGroup*> lst;
+ if(knGlobals.folderManager()->unsentForAccount(a->id()) > 0) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("This account cannot be deleted since there are some unsent messages for it."));
+ }
+ else if(KMessageBox::warningContinueCancel(knGlobals.topWidget, i18n("Do you really want to delete this account?"),"",KGuiItem(i18n("&Delete"),"editdelete"))==KMessageBox::Continue) {
+ lst = gManager->groupsOfAccount( a );
+ for ( QValueList<KNGroup*>::Iterator it = lst.begin(); it != lst.end(); ++it ) {
+ if ( (*it)->isLocked() ) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("At least one group of this account is currently in use.\nThe account cannot be deleted at the moment."));
+ return false;
+ }
+ }
+ for ( QValueList<KNGroup*>::Iterator it = lst.begin(); it != lst.end(); ++it )
+ gManager->unsubscribeGroup( (*it) );
+
+ QDir dir(a->path());
+ if (dir.exists()) {
+ const QFileInfoList *list = dir.entryInfoList(); // get list of matching files and delete all
+ if (list) {
+ QFileInfoListIterator it( *list );
+ while (it.current()) {
+ dir.remove(it.current()->fileName());
+ ++it;
+ }
+ }
+ dir.cdUp(); // directory should now be empty, deleting it
+ dir.rmdir(QString("nntp.%1/").arg(a->id()));
+ }
+
+ if(c_urrentAccount==a) setCurrentAccount(0);
+
+ emit(accountRemoved(a));
+ mAccounts.remove( a ); // finally delete a
+ return true;
+ }
+
+ return false;
+}
+
+
+void KNAccountManager::editProperties(KNNntpAccount *a)
+{
+ if(!a) a=c_urrentAccount;
+ if(!a) return;
+
+ a->editProperties(knGlobals.topWidget);
+ emit(accountModified(a));
+}
+
+
+void KNAccountManager::accountRenamed(KNNntpAccount *a)
+{
+ if(!a) a=c_urrentAccount;
+ if(!a) return;
+
+ emit(accountModified(a));
+}
+
+
+KNNntpAccount* KNAccountManager::first() const
+{
+ if ( mAccounts.isEmpty() )
+ return 0;
+ return mAccounts.first();
+}
+
+
+void KNAccountManager::loadPasswordsAsync()
+{
+ if ( !mWallet && !mWalletOpenFailed ) {
+ if ( knGlobals.top )
+ mWallet = Wallet::openWallet( Wallet::NetworkWallet(),
+ knGlobals.topWidget->topLevelWidget()->winId(),
+ Wallet::Asynchronous );
+ else
+ mWallet = Wallet::openWallet( Wallet::NetworkWallet(), 0, Wallet::Asynchronous );
+ if ( mWallet ) {
+ connect( mWallet, SIGNAL(walletOpened(bool)), SLOT(slotWalletOpened(bool)) );
+ mAsyncOpening = true;
+ }
+ else {
+ mWalletOpenFailed = true;
+ loadPasswords();
+ }
+ return;
+ }
+ if ( mWallet && !mAsyncOpening )
+ loadPasswords();
+}
+
+
+void KNAccountManager::loadPasswords()
+{
+ s_mtp->readPassword();
+ QValueList<KNNntpAccount*>::Iterator it;
+ for ( it = mAccounts.begin(); it != mAccounts.end(); ++it )
+ (*it)->readPassword();
+ emit passwordsChanged();
+}
+
+
+KWallet::Wallet* KNAccountManager::wallet()
+{
+ if ( mWallet && mWallet->isOpen() )
+ return mWallet;
+
+ if ( !Wallet::isEnabled() || mWalletOpenFailed )
+ return 0;
+
+ delete mWallet;
+ if ( knGlobals.top )
+ mWallet = Wallet::openWallet( Wallet::NetworkWallet(),
+ knGlobals.topWidget->topLevelWidget()->winId() );
+ else
+ mWallet = Wallet::openWallet( Wallet::NetworkWallet() );
+
+ if ( !mWallet ) {
+ mWalletOpenFailed = true;
+ return 0;
+ }
+
+ prepareWallet();
+ return mWallet;
+}
+
+
+void KNAccountManager::prepareWallet()
+{
+ if ( !mWallet )
+ return;
+ if ( !mWallet->hasFolder("knode") )
+ mWallet->createFolder( "knode" );
+ mWallet->setFolder( "knode" );
+}
+
+
+void KNAccountManager::slotWalletOpened( bool success )
+{
+ mAsyncOpening = false;
+ if ( !success ) {
+ mWalletOpenFailed = true;
+ delete mWallet;
+ mWallet = 0;
+ } else {
+ prepareWallet();
+ }
+ loadPasswords();
+}
+
+//--------------------------------
+
+#include "knaccountmanager.moc"
diff --git a/knode/knaccountmanager.h b/knode/knaccountmanager.h
new file mode 100644
index 000000000..5b6cd4ef9
--- /dev/null
+++ b/knode/knaccountmanager.h
@@ -0,0 +1,93 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNACCOUNTMANAGER_H
+#define KNACCOUNTMANAGER_H
+
+#include <qglobal.h>
+#include <qvaluelist.h>
+
+namespace KWallet {
+ class Wallet;
+}
+
+class KNGroupManager;
+class KNNntpAccount;
+class KNServerInfo;
+
+
+class KNAccountManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ KNAccountManager(KNGroupManager *gm, QObject * parent=0, const char * name=0);
+ ~KNAccountManager();
+
+ void prepareShutdown();
+
+ void setCurrentAccount(KNNntpAccount *a);
+
+ bool newAccount(KNNntpAccount *a); // a is new account allocated and configured by the caller
+ bool removeAccount(KNNntpAccount *a=0); // a==0: remove current account
+ void editProperties(KNNntpAccount *a=0);
+ void accountRenamed(KNNntpAccount *a=0);
+
+ bool hasCurrentAccount() const { return (c_urrentAccount!=0); }
+ KNNntpAccount* currentAccount() const { return c_urrentAccount; }
+ KNServerInfo* smtp() const { return s_mtp; }
+ /** Returns the account with the given id. */
+ KNNntpAccount* account( int id );
+ QValueList<KNNntpAccount*>::Iterator begin() { return mAccounts.begin(); }
+ QValueList<KNNntpAccount*>::Iterator end() { return mAccounts.end(); }
+ /** Returns the first account (used as fallback sometimes). */
+ KNNntpAccount* first() const;
+
+ /** Loads the passwords of all accounts, allows on-demand wallet opening */
+ void loadPasswords();
+ /** Loads passwords of all accounts asynchronous */
+ void loadPasswordsAsync();
+
+ /** Returns a pointer to an open wallet if available, 0 otherwise */
+ static KWallet::Wallet* wallet();
+
+ protected:
+ void loadAccounts();
+ KNGroupManager *gManager;
+ KNNntpAccount *c_urrentAccount;
+ KNServerInfo *s_mtp;
+
+ signals:
+ void accountAdded(KNNntpAccount *a);
+ void accountRemoved(KNNntpAccount *a); // don't do anything with a, it will be deleted soon
+ void accountModified(KNNntpAccount *a);
+ /** Emitted if passwords have been loaded from the wallet */
+ void passwordsChanged();
+
+ private slots:
+ void slotWalletOpened( bool success );
+
+ private:
+ /** set/create wallet-folder */
+ static void prepareWallet();
+
+ private:
+ QValueList<KNNntpAccount*> mAccounts;
+ static KWallet::Wallet *mWallet;
+ static bool mWalletOpenFailed;
+ bool mAsyncOpening;
+
+};
+
+#endif
diff --git a/knode/knapplication.cpp b/knode/knapplication.cpp
new file mode 100644
index 000000000..2bbcf0494
--- /dev/null
+++ b/knode/knapplication.cpp
@@ -0,0 +1,88 @@
+/*
+ knapplication.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <kwin.h>
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+
+#include "knode.h"
+#include "knapplication.h"
+#include "knconvert.h"
+#include "knglobals.h"
+#include "knmainwidget.h"
+#include "knapplication.moc"
+
+
+int KNApplication::newInstance()
+{
+ kdDebug(5003) << "KNApplication::newInstance()" << endl;
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("GENERAL");
+ QString ver=conf->readEntry("Version");
+
+ if(!ver.isEmpty() && ver!=KNODE_VERSION) { //new version installed
+ if(KNConvert::needToConvert(ver)) { //we need to convert
+ kdDebug(5003) << "KNApplication::newInstance() : conversion needed" << endl;
+ KNConvert *convDlg=new KNConvert(ver);
+ if(!convDlg->exec()) { //reject()
+ if(convDlg->conversionDone()) //conversion has already happened but the user has canceled afterwards
+ conf->writeEntry("Version", KNODE_VERSION);
+ exit(0);
+ return(0);
+ } else //conversion done
+ conf->writeEntry("Version", KNODE_VERSION);
+ delete convDlg;
+ }
+ else //new version but no need to convert anything => just save the new version
+ conf->writeEntry("Version", KNODE_VERSION);
+ }
+
+ if (!mainWidget()) {
+ if (isRestored()) {
+ int n = 1;
+ while (KNMainWindow::canBeRestored(n)){
+ if (KNMainWindow::classNameOfToplevel(n)=="KNMainWindow") {
+ KNMainWindow* mainWin = new KNMainWindow;
+ mainWin->restore(n);
+ if ( n == 1 )
+ setMainWidget( mainWin );
+ break;
+ }
+ n++;
+ }
+ }
+
+ if (!mainWidget()) {
+ KNMainWindow* mainWin = new KNMainWindow;
+ setMainWidget(mainWin); // this makes the external viewer windows close on shutdown...
+ mainWin->show();
+ }
+ }
+
+ // Handle window activation and startup notification
+ KUniqueApplication::newInstance();
+
+ // process URLs...
+ KNMainWidget *w = static_cast<KNMainWindow*>(mainWidget())->mainWidget();
+ w->handleCommandLine();
+
+ kdDebug(5003) << "KNApplication::newInstance() done" << endl;
+ return 0;
+}
+
+
diff --git a/knode/knapplication.h b/knode/knapplication.h
new file mode 100644
index 000000000..68c051e62
--- /dev/null
+++ b/knode/knapplication.h
@@ -0,0 +1,34 @@
+/*
+ knapplication.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNAPPLICATION_H
+#define KNAPPLICATION_H
+
+#include <kuniqueapplication.h>
+
+class KNApplication : public KUniqueApplication
+{
+ Q_OBJECT
+ public:
+ KNApplication(): KUniqueApplication() { };
+
+
+ /** Create new instance of KNode. Make the existing
+ main window active if KNode is already running */
+ int newInstance();
+
+};
+#endif
diff --git a/knode/knarticle.cpp b/knode/knarticle.cpp
new file mode 100644
index 000000000..d43e501ea
--- /dev/null
+++ b/knode/knarticle.cpp
@@ -0,0 +1,576 @@
+/*
+ knmime.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+
+#include <klocale.h>
+#include <kmdcodec.h>
+#include <kmimemagic.h>
+
+#include "knhdrviewitem.h"
+#include "kngroup.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "utilities.h"
+
+using namespace KMime;
+
+
+KNArticle::KNArticle(KNArticleCollection *c) : i_d(-1), c_ol(c), i_tem(0)
+{
+}
+
+
+KNArticle::~KNArticle()
+{
+ delete i_tem;
+}
+
+
+
+void KNArticle::setListItem(KNHdrViewItem *it)
+{
+ i_tem=it;
+ if(i_tem) i_tem->art=this;
+}
+
+
+void KNArticle::setLocked(bool b)
+{
+ f_lags.set(0, b);
+ if(c_ol) { // local articles may have c_ol==0 !
+ if(b)
+ c_ol->articleLocked();
+ else
+ c_ol->articleUnlocked();
+ }
+}
+
+
+//=========================================================================================
+
+
+KNRemoteArticle::KNRemoteArticle(KNGroup *g)
+ : KNArticle(g), a_rticleNumber(-1), i_dRef(-1), d_ref(0), t_hrLevel(0), s_core(0),
+ c_olor(knGlobals.configManager()->appearance()->unreadThreadColor()),
+ u_nreadFups(0), n_ewFups(0), s_ubThreadChangeDate(0)
+{
+ m_essageID.setParent(this);
+ f_rom.setParent(this);
+ r_eferences.setParent(this);
+
+ if (g && g->useCharset())
+ setDefaultCharset( g->defaultCharset() );
+ else
+ setDefaultCharset( knGlobals.configManager()->postNewsTechnical()->charset() );
+}
+
+
+KNRemoteArticle::~KNRemoteArticle()
+{}
+
+
+void KNRemoteArticle::parse()
+{
+ KNArticle::parse();
+ QCString raw;
+ if( !(raw=rawHeader(m_essageID.type())).isEmpty() )
+ m_essageID.from7BitString(raw);
+
+ if( !(raw=rawHeader(f_rom.type())).isEmpty() )
+ f_rom.from7BitString(raw);
+
+ if( !(raw=rawHeader(r_eferences.type())).isEmpty() )
+ r_eferences.from7BitString(raw);
+}
+
+
+void KNRemoteArticle::clear()
+{
+ m_essageID.clear();
+ f_rom.clear();
+ r_eferences.clear();
+ KNArticle::clear();
+}
+
+
+Headers::Base* KNRemoteArticle::getHeaderByType(const char *type)
+{
+ if(strcasecmp("Message-ID", type)==0) {
+ if(m_essageID.isEmpty()) return 0;
+ else return &m_essageID;
+ }
+ else if(strcasecmp("From", type)==0) {
+ if(f_rom.isEmpty()) return 0;
+ else return &f_rom;
+ }
+ else if(strcasecmp("References", type)==0) {
+ if(r_eferences.isEmpty()) return 0;
+ else return &r_eferences;
+ }
+ else
+ return KNArticle::getHeaderByType(type);
+}
+
+
+void KNRemoteArticle::setHeader(Headers::Base *h)
+{
+ bool del=true;
+ if(h->is("Message-ID"))
+ m_essageID.from7BitString(h->as7BitString(false));
+ else if(h->is("From")) {
+ f_rom.setEmail( (static_cast<Headers::From*>(h))->email() );
+ f_rom.setName( (static_cast<Headers::From*>(h))->name() );
+ }
+ else if(h->is("References")) {
+ r_eferences.from7BitString(h->as7BitString(false));
+ }
+ else {
+ del=false;
+ KNArticle::setHeader(h);
+ }
+
+ if(del) delete h;
+}
+
+
+bool KNRemoteArticle::removeHeader(const char *type)
+{
+ if(strcasecmp("Message-ID", type)==0)
+ m_essageID.clear();
+ else if(strcasecmp("From", type)==0)
+ f_rom.clear();
+ else if(strcasecmp("References", type)==0)
+ r_eferences.clear();
+ else
+ return KNArticle::removeHeader(type);
+
+ return true;
+}
+
+
+void KNRemoteArticle::initListItem()
+{
+ if(!i_tem) return;
+
+ if(f_rom.hasName())
+ i_tem->setText(1, f_rom.name());
+ else
+ i_tem->setText(1, QString(f_rom.email()));
+
+ updateListItem();
+}
+
+
+void KNRemoteArticle::updateListItem()
+{
+ if(!i_tem) return;
+
+ KNConfig::Appearance *app=knGlobals.configManager()->appearance();
+
+ if(isRead()) {
+ if(hasContent())
+ i_tem->setPixmap(0, app->icon(KNConfig::Appearance::greyBallChkd));
+ else
+ i_tem->setPixmap(0, app->icon(KNConfig::Appearance::greyBall));
+ }
+ else {
+ if(hasContent())
+ i_tem->setPixmap(0,app->icon(KNConfig::Appearance::redBallChkd));
+ else
+ i_tem->setPixmap(0, app->icon(KNConfig::Appearance::redBall));
+ }
+
+ if(hasNewFollowUps())
+ i_tem->setPixmap(1, app->icon(KNConfig::Appearance::newFups));
+ else
+ i_tem->setPixmap(1, app->icon(KNConfig::Appearance::null));
+
+ if(isWatched())
+ i_tem->setPixmap(2, app->icon(KNConfig::Appearance::eyes));
+ else {
+ if(isIgnored())
+ i_tem->setPixmap(2, app->icon(KNConfig::Appearance::ignore));
+ else
+ i_tem->setPixmap(2, app->icon(KNConfig::Appearance::null));
+ }
+
+ i_tem->setExpandable( (threadMode() && hasVisibleFollowUps()) );
+
+ i_tem->repaint(); //force repaint
+}
+
+
+void KNRemoteArticle::thread(KNRemoteArticle::List &l)
+{
+ KNRemoteArticle *tmp=0, *ref=this;
+ KNGroup *g=static_cast<KNGroup*>(c_ol);
+ int idRef=i_dRef, topID=-1;
+
+ while(idRef!=0) {
+ ref=g->byId(idRef);
+ if(!ref)
+ return; // sh#t !!
+ idRef=ref->idRef();
+ }
+
+ topID=ref->id();
+ l.append(ref);
+
+ for(int i=0; i<g->length(); i++) {
+ tmp=g->at(i);
+ if(tmp->idRef()!=0) {
+ idRef=tmp->idRef();
+ while(idRef!=0) {
+ ref=g->byId(idRef);
+ idRef=ref->idRef();
+ }
+ if(ref->id()==topID)
+ l.append(tmp);
+ }
+ }
+}
+
+
+void KNRemoteArticle::setForceDefaultCS(bool b)
+{
+ if (!b) { // restore default
+ KNGroup *g=static_cast<KNGroup*>(c_ol);
+ if (g && g->useCharset())
+ setDefaultCharset( g->defaultCharset() );
+ else
+ setDefaultCharset( knGlobals.configManager()->postNewsTechnical()->charset() );
+ }
+ KNArticle::setForceDefaultCS(b);
+ initListItem();
+}
+
+
+void KNRemoteArticle::propagateThreadChangedDate()
+{
+ KNRemoteArticle *ref=this;
+ KNGroup *g=static_cast<KNGroup*>(c_ol);
+ int idRef=i_dRef;
+
+ while (idRef!=0) {
+ ref=g->byId(idRef);
+ if(!ref)
+ return; // sh#t !!
+ idRef=ref->idRef();
+ }
+
+ if (date()->unixTime() > ref->date()->unixTime()) {
+ ref->setSubThreadChangeDate(date()->unixTime());
+ }
+}
+
+
+//=========================================================================================
+
+
+KNLocalArticle::KNLocalArticle(KNArticleCollection *c)
+ : KNArticle(c), s_Offset(0), e_Offset(0), s_erverId(-1)
+{
+ n_ewsgroups.setParent(this);
+ t_o.setParent(this);
+ setDefaultCharset( knGlobals.configManager()->postNewsTechnical()->charset() );
+}
+
+
+KNLocalArticle::~KNLocalArticle()
+{}
+
+
+void KNLocalArticle::parse()
+{
+ KNArticle::parse();
+ QCString raw;
+
+ if( !(raw=rawHeader(n_ewsgroups.type())).isEmpty() )
+ n_ewsgroups.from7BitString(raw);
+
+ if( !(raw=rawHeader(t_o.type())).isEmpty() )
+ t_o.from7BitString(raw);
+}
+
+
+void KNLocalArticle::clear()
+{
+ KNArticle::clear();
+ n_ewsgroups.clear();
+ t_o.clear();
+}
+
+
+Headers::Base* KNLocalArticle::getHeaderByType(const char *type)
+{
+ if(strcasecmp("Newsgroups", type)==0)
+ return newsgroups(false);
+ else if(strcasecmp("To", type)==0)
+ return to(false);
+ else
+ return KNArticle::getHeaderByType(type);
+}
+
+
+void KNLocalArticle::setHeader(Headers::Base *h)
+{
+ bool del=true;
+ if(h->is("To"))
+ t_o.from7BitString(h->as7BitString(false));
+ else if(h->is("Newsgroups"))
+ n_ewsgroups.from7BitString(h->as7BitString(false));
+ else {
+ del=false;
+ KNArticle::setHeader(h);
+ }
+
+ if(del) delete h;
+}
+
+
+bool KNLocalArticle::removeHeader(const char *type)
+{
+ if(strcasecmp("To", type)==0)
+ t_o.clear();
+ else if(strcasecmp("Newsgroups", type)==0)
+ n_ewsgroups.clear();
+ else
+ return KNArticle::removeHeader(type);
+
+ return true;
+}
+
+
+void KNLocalArticle::updateListItem()
+{
+ if(!i_tem)
+ return;
+
+ QString tmp;
+ int idx=0;
+ KNConfig::Appearance *app=knGlobals.configManager()->appearance();
+
+ if(isSavedRemoteArticle()) {
+ i_tem->setPixmap(0, app->icon(KNConfig::Appearance::savedRemote));
+ if (!n_ewsgroups.isEmpty())
+ tmp=n_ewsgroups.asUnicodeString();
+ else
+ tmp=t_o.asUnicodeString();
+ }
+ else {
+
+ if(doPost()) {
+ tmp+=n_ewsgroups.asUnicodeString();
+ if(canceled())
+ i_tem->setPixmap(idx++, app->icon(KNConfig::Appearance::canceledPosting));
+ else
+ i_tem->setPixmap(idx++, app->icon(KNConfig::Appearance::posting));
+ }
+
+ if(doMail()) {
+ i_tem->setPixmap(idx++, app->icon(KNConfig::Appearance::mail));
+ if(doPost())
+ tmp+=" / ";
+ tmp+=t_o.asUnicodeString();
+ }
+
+ }
+
+ i_tem->setText(1, tmp);
+}
+
+
+void KNLocalArticle::setForceDefaultCS(bool b)
+{
+ if (!b) // restore default
+ setDefaultCharset( knGlobals.configManager()->postNewsTechnical()->charset() );
+ KNArticle::setForceDefaultCS(b);
+ updateListItem();
+}
+
+
+//=========================================================================================
+
+
+KNAttachment::KNAttachment(Content *c)
+ : c_ontent(c), l_oadHelper(0), f_ile(0), i_sAttached(true)
+{
+ Headers::ContentType *t=c->contentType();
+ Headers::CTEncoding *e=c->contentTransferEncoding();
+ Headers::CDescription *d=c->contentDescription(false);
+
+ n_ame=t->name();
+
+ if(d)
+ d_escription=d->asUnicodeString();
+
+
+ setMimeType(t->mimeType());
+
+ if(e->cte()==Headers::CEuuenc) {
+ setCte( Headers::CEbase64 );
+ updateContentInfo();
+ }
+ else
+ e_ncoding.setCte( e->cte() );
+
+
+ h_asChanged=false; // has been set to "true" in setMimeType()
+}
+
+
+KNAttachment::KNAttachment(KNLoadHelper *helper)
+ : c_ontent(0), l_oadHelper(helper), f_ile(helper->getFile()), i_sAttached(false), h_asChanged(true)
+{
+ setMimeType((KMimeMagic::self()->findFileType(f_ile->name()))->mimeType());
+ n_ame=helper->getURL().fileName();
+}
+
+
+KNAttachment::~KNAttachment()
+{
+ if(!i_sAttached && c_ontent)
+ delete c_ontent;
+ delete l_oadHelper;
+}
+
+
+void KNAttachment::setMimeType(const QString &s)
+{
+ m_imeType=s.latin1();
+ h_asChanged=true;
+
+ if(m_imeType.find("text/", 0, false)==-1) {
+ f_b64=true;
+ e_ncoding.setCte(Headers::CEbase64);
+ }
+ else {
+ f_b64=false;
+ if (knGlobals.configManager()->postNewsTechnical()->allow8BitBody())
+ setCte(Headers::CE8Bit);
+ else
+ setCte(Headers::CEquPr);
+ }
+}
+
+
+QString KNAttachment::contentSize() const
+{
+ QString ret;
+ int s=0;
+
+ if(c_ontent && c_ontent->hasContent())
+ s=c_ontent->size();
+ else {
+ if (f_ile)
+ s=f_ile->size();
+ }
+
+ if(s > 1023) {
+ s=s/1024;
+ ret.setNum(s);
+ ret+=" kB";
+ }
+ else {
+ ret.setNum(s);
+ ret+=" Bytes";
+ }
+
+ return ret;
+}
+
+
+void KNAttachment::updateContentInfo()
+{
+ if(!h_asChanged || !c_ontent)
+ return;
+
+ //Content-Type
+ Headers::ContentType *t=c_ontent->contentType();
+ t->setMimeType(m_imeType);
+ t->setName(n_ame, "UTF-8");
+ t->setCategory(Headers::CCmixedPart);
+
+ //Content-Description
+ if(d_escription.isEmpty())
+ c_ontent->removeHeader("Content-Description");
+ else
+ c_ontent->contentDescription()->fromUnicodeString(d_escription, "UTF-8");
+
+ //Content-Disposition
+ Headers::CDisposition *d=c_ontent->contentDisposition();
+ d->setDisposition(Headers::CDattachment);
+ d->setFilename(n_ame);
+
+ //Content-Transfer-Encoding
+ if(i_sAttached)
+ c_ontent->changeEncoding(e_ncoding.cte());
+ else
+ c_ontent->contentTransferEncoding()->setCte(e_ncoding.cte());
+
+ c_ontent->assemble();
+
+ h_asChanged=false;
+}
+
+
+
+void KNAttachment::attach(Content *c)
+{
+ if(i_sAttached || !f_ile)
+ return;
+
+ c_ontent=new Content();
+ updateContentInfo();
+ Headers::ContentType *type=c_ontent->contentType();
+ Headers::CTEncoding *e=c_ontent->contentTransferEncoding();
+ QByteArray data(f_ile->size());
+
+ int readBytes=f_ile->readBlock(data.data(), f_ile->size());
+
+ if (readBytes<(int)f_ile->size() && f_ile->status()!=IO_Ok) {
+ KNHelper::displayExternalFileError();
+ delete c_ontent;
+ c_ontent=0;
+ } else {
+ if (e_ncoding.cte()==Headers::CEbase64 || !type->isText()) { //encode base64
+ c_ontent->setBody( KCodecs::base64Encode(data, true) + '\n' );
+ // c_ontent->b_ody += '\n';
+ e->setCte(Headers::CEbase64);
+ e->setDecoded(false);
+ } else {
+ c_ontent->setBody( QCString(data.data(), data.size()+1) + '\n' );
+ // c_ontent->b_ody += '\n';
+ e->setDecoded(true);
+ }
+ }
+
+ if(c_ontent) {
+ c->addContent(c_ontent);
+ i_sAttached=true;
+ }
+}
+
+
+void KNAttachment::detach(Content *c)
+{
+ if(i_sAttached) {
+ c->removeContent(c_ontent, false);
+ i_sAttached=false;
+ }
+}
+
+
diff --git a/knode/knarticle.h b/knode/knarticle.h
new file mode 100644
index 000000000..9c2fc8979
--- /dev/null
+++ b/knode/knarticle.h
@@ -0,0 +1,337 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNARTICLE_H
+#define KNARTICLE_H
+
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qfile.h>
+#include <qfont.h>
+#include <qcolor.h>
+#include <qasciidict.h>
+#include <qvaluelist.h>
+
+#include <kmime_headers.h>
+#include <kmime_newsarticle.h>
+#include <boolflags.h>
+
+#include "knjobdata.h"
+
+//forward declarations
+class KNLoadHelper;
+class KNHdrViewItem;
+class KNArticleCollection;
+
+/** This class encapsulates a generic article. It provides all the
+ usual headers of a RFC822-message. Further more it contains an
+ unique id and can store a pointer to a @ref QListViewItem. It is
+ used as a base class for all visible articles. */
+
+class KNArticle : public KMime::NewsArticle, public KNJobItem {
+
+ public:
+ typedef QValueList<KNArticle*> List;
+
+ KNArticle(KNArticleCollection *c);
+ ~KNArticle();
+
+ //id
+ int id() const { return i_d; }
+ void setId(int i) { i_d=i; }
+
+ //list item handling
+ KNHdrViewItem* listItem() const { return i_tem; }
+ void setListItem(KNHdrViewItem *i);
+ virtual void updateListItem() {}
+
+ //network lock (reimplemented from KNJobItem)
+ bool isLocked() { return f_lags.get(0); }
+ void setLocked(bool b=true);
+
+ //prevent that the article is unloaded automatically
+ bool isNotUnloadable() { return f_lags.get(1); }
+ void setNotUnloadable(bool b=true) { f_lags.set(1, b); }
+
+ //article-collection
+ KNArticleCollection* collection() const { return c_ol; }
+ void setCollection(KNArticleCollection *c) { c_ol=c; }
+ bool isOrphant() const { return (i_d==-1); }
+
+ protected:
+ int i_d; //unique in the given collection
+ KNArticleCollection *c_ol;
+ KNHdrViewItem *i_tem;
+
+}; // KNArticle
+
+
+class KNGroup;
+
+/** KNRemoteArticle represents an article, whos body has to be
+ retrieved from a remote host or from the local cache.
+ All articles in a newsgroup are stored in instances
+ of this class. */
+
+class KNRemoteArticle : public KNArticle {
+
+ public:
+ typedef QValueList<KNRemoteArticle*> List;
+
+ KNRemoteArticle(KNGroup *g);
+ ~KNRemoteArticle();
+
+ // type
+ articleType type() { return ATremote; }
+
+ // content handling
+ virtual void parse();
+ virtual void assemble() {} //assembling is disabled for remote articles
+ virtual void clear();
+
+ // header access
+ KMime::Headers::Base* getHeaderByType(const char *type);
+ void setHeader(KMime::Headers::Base *h);
+ bool removeHeader(const char *type);
+ KMime::Headers::MessageID* messageID(bool create=true) { if(!create && m_essageID.isEmpty()) return 0; return &m_essageID; }
+ KMime::Headers::From* from(bool create=true) { if(!create && f_rom.isEmpty()) return 0; return &f_rom; }
+ KMime::Headers::References* references(bool create=true) { if(!create && r_eferences.isEmpty()) return 0; return &r_eferences; }
+
+ // article number
+ int articleNumber() const { return a_rticleNumber; }
+ void setArticleNumber(int number) { a_rticleNumber = number; }
+
+ // status
+ bool isNew() { return f_lags.get(2); }
+ void setNew(bool b=true) { f_lags.set(2, b); }
+ bool getReadFlag() { return f_lags.get(3); }
+ bool isRead() { return f_lags.get(7) || f_lags.get(3); } // ignored articles == read
+ void setRead(bool b=true) { f_lags.set(3, b); }
+ bool isExpired() { return f_lags.get(4); }
+ void setExpired(bool b=true) { f_lags.set(4, b); }
+ bool isKept() { return f_lags.get(5); }
+ void setKept(bool b=true) { f_lags.set(5, b); }
+ bool hasChanged() { return f_lags.get(6); }
+ void setChanged(bool b=true) { f_lags.set(6, b); }
+ bool isIgnored() { return f_lags.get(7); }
+ void setIgnored(bool b=true) { f_lags.set(7, b); }
+ bool isWatched() { return f_lags.get(8); }
+ void setWatched(bool b=true) { f_lags.set(8, b); }
+
+ // thread info
+ int idRef() { return i_dRef; }
+ void setIdRef(int i) { if (i != id())
+ i_dRef=i;
+ else
+ i_dRef=0; }
+ KNRemoteArticle* displayedReference() { return d_ref; }
+ void setDisplayedReference(KNRemoteArticle *dr) { d_ref=dr; }
+ bool threadMode() { return f_lags.get(9); }
+ void setThreadMode(bool b=true) { f_lags.set(9, b); }
+ unsigned char threadingLevel() { return t_hrLevel; }
+ void setThreadingLevel(unsigned char l) { t_hrLevel=l; }
+ short score() { return s_core; }
+ void setScore(short s) { s_core=s; }
+ unsigned short newFollowUps() { return n_ewFups; }
+ bool hasNewFollowUps() { return (n_ewFups>0); }
+ void setNewFollowUps(unsigned short s) { n_ewFups=s; }
+ void incNewFollowUps(unsigned short s=1) { n_ewFups+=s; }
+ void decNewFollowUps(unsigned short s=1) { n_ewFups-=s; }
+ unsigned short unreadFollowUps() { return u_nreadFups; }
+ bool hasUnreadFollowUps() { return (u_nreadFups>0); }
+ void setUnreadFollowUps(unsigned short s) { u_nreadFups=s; }
+ void incUnreadFollowUps(unsigned short s=1) { u_nreadFups+=s; }
+ void decUnreadFollowUps(unsigned short s=1) { u_nreadFups-=s; }
+ void thread(List &f);
+
+ //filtering
+ bool filterResult() { return f_lags.get(10); }
+ void setFilterResult(bool b=true) { f_lags.set(10, b); }
+ bool isFiltered() { return f_lags.get(11); }
+ void setFiltered(bool b=true) { f_lags.set(11, b); }
+ bool hasVisibleFollowUps() { return f_lags.get(12); }
+ void setVisibleFollowUps(bool b=true) { f_lags.set(12, b); }
+
+ // list item handling
+ void initListItem();
+ void updateListItem();
+
+ void setForceDefaultCS(bool b);
+
+ QColor color() const { return c_olor; }
+ void setColor(const QColor& c) { c_olor = c; }
+
+ time_t subThreadChangeDate() { return s_ubThreadChangeDate; }
+ void setSubThreadChangeDate(time_t date) { s_ubThreadChangeDate = date; }
+ // propagate the change date to the root article
+ void propagateThreadChangedDate();
+
+ protected:
+ // hardcoded headers
+ KMime::Headers::MessageID m_essageID;
+ KMime::Headers::From f_rom;
+ KMime::Headers::References r_eferences;
+
+ int a_rticleNumber;
+ int i_dRef; // id of a reference-article (0 == none)
+ KNRemoteArticle *d_ref; // displayed reference-article (may differ from i_dRef)
+ unsigned char t_hrLevel; // quality of threading
+ short s_core; // guess what ;-)
+ QColor c_olor; // color for the header list
+ unsigned short u_nreadFups, // number of the article's unread follow-ups
+ n_ewFups; // number of the article's new follow-ups
+ time_t s_ubThreadChangeDate; // the last time the sub-thread of this article changed
+ // i.e. when the last article arrived...
+
+}; // KNRemoteArticle
+
+
+
+/* This class encapsulates an article, that is
+ stored locally in an MBOX-file. All own and
+ saved articles are represented by instances
+ of this class. */
+
+
+class KNLocalArticle : public KNArticle {
+
+ public:
+ typedef QValueList<KNLocalArticle*> List;
+
+ KNLocalArticle(KNArticleCollection *c=0);
+ ~KNLocalArticle();
+
+ //type
+ articleType type() { return ATlocal; }
+
+ //content handling
+ void parse();
+ void clear();
+
+ // header access
+ KMime::Headers::Base* getHeaderByType(const char *type);
+ void setHeader(KMime::Headers::Base *h);
+ bool removeHeader(const char *type);
+ KMime::Headers::Newsgroups* newsgroups(bool create=true) { if ( (!create && n_ewsgroups.isEmpty()) ||
+ (!create && !isSavedRemoteArticle() && !doPost()) )
+ return 0;
+ return &n_ewsgroups; }
+ KMime::Headers::To* to(bool create=true) { if ( (!create && t_o.isEmpty()) ||
+ (!create && !isSavedRemoteArticle() && !doMail()) )
+ return 0;
+ return &t_o; }
+
+ //send article as mail
+ bool doMail() { return f_lags.get(2); }
+ void setDoMail(bool b=true) { f_lags.set(2, b); }
+ bool mailed() { return f_lags.get(3); }
+ void setMailed(bool b=true) { f_lags.set(3, b); }
+
+ //post article to a newsgroup
+ bool doPost() { return f_lags.get(4); }
+ void setDoPost(bool b=true) { f_lags.set(4, b); }
+ bool posted() { return f_lags.get(5); }
+ void setPosted(bool b=true) { f_lags.set(5, b); }
+ bool canceled() { return f_lags.get(6); }
+ void setCanceled(bool b=true) { f_lags.set(6, b); }
+
+ // status
+ bool pending() { return ( (doPost() && !posted()) || (doMail() && !mailed()) ); }
+ bool isSavedRemoteArticle() { return ( !doPost() && !doMail() && editDisabled() ); }
+
+ //edit
+ bool editDisabled() { return f_lags.get(7); }
+ void setEditDisabled(bool b=true) { f_lags.set(7, b); }
+
+ //search
+ bool filterResult() { return f_lags.get(8); }
+ void setFilterResult(bool b=true) { f_lags.set(8, b); }
+
+ //MBOX infos
+ int startOffset() const { return s_Offset; }
+ void setStartOffset(int so) { s_Offset=so; }
+ int endOffset() const { return e_Offset; }
+ void setEndOffset(int eo) { e_Offset=eo; }
+
+ //nntp-server id
+ int serverId() { if(!doPost()) return -1; else return s_erverId; }
+ void setServerId(int i) { s_erverId=i; }
+
+ //list item handling
+ void updateListItem();
+
+ void setForceDefaultCS(bool b);
+
+ protected:
+ //hardcoded headers
+ KMime::Headers::Newsgroups n_ewsgroups;
+ KMime::Headers::To t_o;
+ int s_Offset, //position in mbox-file : start
+ e_Offset, //position in mbox-file : end
+ s_erverId; //id of the nntp-server this article is posted to
+};
+
+
+/* KNAttachment represents a file that is
+ or will be attached to an article. */
+
+class KNAttachment {
+
+ public:
+ KNAttachment(KMime::Content *c);
+ KNAttachment(KNLoadHelper *helper);
+ ~KNAttachment();
+
+ //name (used as a Content-Type parameter and as filename)
+ const QString& name() { return n_ame; }
+ void setName(const QString &s) { n_ame=s; h_asChanged=true; }
+
+ //mime type
+ const QCString& mimeType() { return m_imeType; }
+ void setMimeType(const QString &s);
+
+ //Content-Description
+ const QString& description() { return d_escription; }
+ void setDescription(const QString &s) { d_escription=s; h_asChanged=true; }
+
+ //Encoding
+ int cte() { return e_ncoding.cte(); }
+ void setCte(int e) { e_ncoding.setCte( (KMime::Headers::contentEncoding)(e) );
+ h_asChanged=true; }
+ bool isFixedBase64()const { return f_b64; }
+ QString encoding() { return e_ncoding.asUnicodeString(); }
+
+ //content handling
+ KMime::Content* content()const { return c_ontent; }
+ QString contentSize() const;
+ bool isAttached() const { return i_sAttached; }
+ bool hasChanged() const { return h_asChanged; }
+ void updateContentInfo();
+ void attach(KMime::Content *c);
+ void detach(KMime::Content *c);
+
+ protected:
+ KMime::Content *c_ontent;
+ KNLoadHelper *l_oadHelper;
+ QFile *f_ile;
+ QCString m_imeType;
+ QString n_ame,
+ d_escription;
+ KMime::Headers::CTEncoding e_ncoding;
+ bool i_sAttached,
+ h_asChanged,
+ f_b64;
+};
+
+#endif //KNARTICLE_H
diff --git a/knode/knarticlecollection.cpp b/knode/knarticlecollection.cpp
new file mode 100644
index 000000000..bd8def75b
--- /dev/null
+++ b/knode/knarticlecollection.cpp
@@ -0,0 +1,403 @@
+/*
+ knarticlecollection.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <stdlib.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+
+#include "knglobals.h"
+#include "knarticlecollection.h"
+#include "knarticle.h"
+
+
+static const int sizeIncr=50;
+
+KNArticleVector::KNArticleVector(KNArticleVector *master, SortingType sorting)
+ : m_aster(master), l_en(0), s_ize(0), l_ist(0), s_ortType(sorting)
+{
+}
+
+
+KNArticleVector::~KNArticleVector()
+{
+ clear();
+}
+
+
+bool KNArticleVector::resize(int s)
+{
+ KNArticle **bak=l_ist;
+ int nSize;
+
+ if(s==0)
+ nSize=s_ize+sizeIncr;
+ else
+ nSize=((s/sizeIncr)+1)*sizeIncr;
+
+ l_ist=(KNArticle**) realloc(l_ist, sizeof(KNArticle*)*nSize);
+
+ if(!l_ist) {
+ KMessageBox::error(knGlobals.topWidget, i18n("Memory allocation failed.\nYou should close this application now\nto avoid data loss."));
+ l_ist=bak;
+ return false;
+ }
+ else {
+ s_ize=nSize;
+ //kdDebug(5003) << "size : " << siz << "\n" << endl;
+ return true;
+ }
+
+}
+
+
+bool KNArticleVector::append(KNArticle *a, bool autoSort)
+{
+ if( (l_en+1 > s_ize) && !resize()) // array too small => try to realloc
+ return false; // allocation failed !!
+
+ l_ist[l_en++]=a;
+
+ if(autoSort) sort();
+ return true;
+}
+
+
+void KNArticleVector::remove(int pos, bool autoDel, bool autoCompact)
+{
+
+ if(pos < 0 || pos > l_en-1)
+ return;
+
+ if(autoDel)
+ delete l_ist[pos];
+
+ l_ist[pos]=0;
+
+ if(autoCompact)
+ compact();
+}
+
+
+void KNArticleVector::clear()
+{
+ if(l_ist){
+ if(m_aster==0)
+ for(int i=0; i<l_en; i++) delete l_ist[i];
+ free(l_ist);
+ }
+
+ l_ist=0; l_en=0; s_ize=0;
+}
+
+
+void KNArticleVector::compact()
+{
+ int newLen, nullStart=0, nullCnt=0, ptrStart=0, ptrCnt=0;
+
+ for(int idx=0; idx<l_en; idx++) {
+ if(l_ist[idx]==0) {
+ ptrStart=-1;
+ ptrCnt=-1;
+ nullStart=idx;
+ nullCnt=1;
+ for(int idx2=idx+1; idx2<l_en; idx2++) {
+ if(l_ist[idx2]==0) nullCnt++;
+ else {
+ ptrStart=idx2;
+ ptrCnt=1;
+ break;
+ }
+ }
+ if(ptrStart!=-1) {
+ for(int idx2=ptrStart+1; idx2<l_en; idx2++) {
+ if(l_ist[idx2]!=0) ptrCnt++;
+ else break;
+ }
+ memmove(&(l_ist[nullStart]), &(l_ist[ptrStart]), ptrCnt*sizeof(KNArticle*));
+ for(int idx2=nullStart+ptrCnt; idx2<nullStart+ptrCnt+nullCnt; idx2++)
+ l_ist[idx2]=0;
+ idx=nullStart+ptrCnt-1;
+ }
+ else break;
+ }
+ }
+ newLen=0;
+ while(l_ist[newLen]!=0) newLen++;
+ l_en=newLen;
+}
+
+
+void KNArticleVector::syncWithMaster()
+{
+ if(!m_aster) return;
+
+ if(resize(m_aster->l_en)) {
+ memcpy(l_ist, m_aster->l_ist, (m_aster->l_en) * sizeof(KNArticle*));
+ l_en=m_aster->l_en;
+ sort();
+ }
+}
+
+
+void KNArticleVector::sort()
+{
+ int (*cmp)(const void*,const void*) = 0;
+
+ switch(s_ortType) {
+ case STid:
+ cmp=compareById;
+ break;
+ case STmsgId:
+ cmp=compareByMsgId;
+ break;
+ default:
+ cmp=0;
+ break;
+ }
+
+ if(cmp) {
+ //compact(); // remove null-pointers
+ qsort(l_ist, l_en, sizeof(KNArticle*), cmp);
+ }
+}
+
+
+int KNArticleVector::compareById(const void *p1, const void *p2)
+{
+ KNArticle *a1, *a2;
+ int rc=0, id1, id2;
+
+ a1=*((KNArticle**)(p1));
+ a2=*((KNArticle**)(p2));
+
+ id1=a1->id(),
+ id2=a2->id();
+
+ if( id1 < id2 ) rc=-1;
+ else if( id1 > id2 ) rc=1;
+
+ return rc;
+}
+
+
+int KNArticleVector::compareByMsgId(const void *p1, const void *p2)
+{
+ KNArticle *a1, *a2;
+ QCString mid1, mid2;
+
+ a1=*(KNArticle**)(p1);
+ a2=*(KNArticle**)(p2);
+
+ mid1=a1->messageID(true)->as7BitString(false);
+ mid2=a2->messageID(true)->as7BitString(false);
+
+ if(mid1.isNull()) mid1="";
+ if(mid2.isNull()) mid2="";
+
+ return strcmp( mid1.data(), mid2.data() );
+}
+
+
+KNArticle* KNArticleVector::bsearch(int id)
+{
+ int idx=indexForId(id);
+
+ return ( idx>-1 ? l_ist[idx] : 0 );
+}
+
+
+KNArticle* KNArticleVector::bsearch(const QCString &id)
+{
+ int idx=indexForMsgId(id);
+
+ return ( idx>-1 ? l_ist[idx] : 0 );
+}
+
+
+int KNArticleVector::indexForId(int id)
+{
+ if(s_ortType!=STid) return -1;
+
+ int start=0, end=l_en, mid=0, currentId=0;
+ bool found=false;
+ KNArticle *current=0;
+
+ while(start!=end && !found) {
+ mid=(start+end)/2;
+ current=l_ist[mid];
+ currentId=current->id();
+
+ if(currentId==id)
+ found=true;
+ else if(currentId < id)
+ start=mid+1;
+ else
+ end=mid;
+ }
+
+ if(found)
+ return mid;
+ else {
+ #ifndef NDEBUG
+ qDebug("knode: KNArticleVector::indexForId() : id=%d not found", id);
+ #endif
+ return -1;
+ }
+}
+
+
+int KNArticleVector::indexForMsgId(const QCString &id)
+{
+ if(s_ortType!=STmsgId) return -1;
+
+ int start=0, end=l_en, mid=0;
+ QCString currentMid=0;
+ bool found=false;
+ KNArticle *current=0;
+ int cnt=0;
+
+ while(start!=end && !found) {
+ mid=(start+end)/2;
+ current=l_ist[mid];
+ currentMid=current->messageID(true)->as7BitString(false);
+
+ if(currentMid==id)
+ found=true;
+ else if( strcmp(currentMid.data(), id.data()) < 0 )
+ start=mid+1;
+ else
+ end=mid;
+
+ cnt++;
+ }
+
+ if(found) {
+ /*#ifndef NDEBUG
+ qDebug("KNArticleVector::indexForMsgID() : msgID=%s found after %d compares", id.data(), cnt);
+ #endif*/
+ return mid;
+ }
+ else {
+ /*#ifndef NDEBUG
+ qDebug("knode: KNArticleVector::indexForMsgID() : msgID=%s not found", id.data());
+ #endif*/
+ return -1;
+ }
+}
+
+
+
+
+// -------------------------------------------------------------------------------------------
+
+
+
+KNArticleCollection::KNArticleCollection(KNCollection *p)
+ : KNCollection(p), l_astID(0), l_ockedArticles(0), n_otUnloadable(false)
+{
+ a_rticles.setSortMode(KNArticleVector::STid);
+ m_idIndex.setSortMode(KNArticleVector::STmsgId);
+ m_idIndex.setMaster(&a_rticles);
+}
+
+
+
+KNArticleCollection::~KNArticleCollection()
+{
+ clear();
+}
+
+
+
+bool KNArticleCollection::resize(int s)
+{
+ return a_rticles.resize(s);
+}
+
+
+
+bool KNArticleCollection::append(KNArticle *a, bool autoSync)
+{
+ if(a_rticles.append(a, false)) {
+ if(a->id()==-1)
+ a->setId(++l_astID);
+ if(autoSync)
+ syncSearchIndex();
+ return true;
+ }
+ return false;
+
+}
+
+
+
+void KNArticleCollection::clear()
+{
+ a_rticles.clear();
+ m_idIndex.clear();
+ l_astID=0;
+}
+
+
+
+void KNArticleCollection::compact()
+{
+ a_rticles.compact();
+ m_idIndex.clear();
+}
+
+
+KNArticle* KNArticleCollection::byId(int id)
+{
+ return a_rticles.bsearch(id);
+}
+
+
+KNArticle* KNArticleCollection::byMessageId(const QCString &mid)
+{
+ if(m_idIndex.isEmpty()) {
+ m_idIndex.syncWithMaster();
+ kdDebug(5003) << "KNArticleCollection::byMessageId() : created index" << endl;
+ }
+ return m_idIndex.bsearch(mid);
+}
+
+
+void KNArticleCollection::setLastID()
+{
+ if(a_rticles.length()>0)
+ l_astID=a_rticles.at(a_rticles.length()-1)->id();
+ else
+ l_astID=0;
+}
+
+
+void KNArticleCollection::syncSearchIndex()
+{
+ m_idIndex.syncWithMaster();
+
+ /*for(int i=0; i<m_idIndex.length(); i++) {
+ kdDebug(5003) << m_idIndex.at(i)->id() << " , " << m_idIndex.at(i)->messageID()->as7BitString(false) << endl;
+ } */
+}
+
+
+void KNArticleCollection::clearSearchIndex()
+{
+ m_idIndex.clear();
+}
diff --git a/knode/knarticlecollection.h b/knode/knarticlecollection.h
new file mode 100644
index 000000000..3a92f2182
--- /dev/null
+++ b/knode/knarticlecollection.h
@@ -0,0 +1,120 @@
+/*
+ knarticlecollection.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNARTICLECOLLECTION_H
+#define KNARTICLECOLLECTION_H
+
+#include "kncollection.h"
+
+class KNArticle;
+
+
+class KNArticleVector {
+
+ public:
+ enum SortingType { STid, STmsgId, STunsorted };
+
+ KNArticleVector(KNArticleVector *master=0, SortingType sorting=STunsorted);
+ virtual ~KNArticleVector();
+
+ // list-info
+ KNArticleVector* master() { return m_aster; }
+ void setMaster(KNArticleVector *m) { m_aster=m; }
+ bool isMaster() { return (m_aster==0); }
+
+ bool isEmpty() { return ( (l_ist==0) || (l_en==0) ); }
+ int length() { return l_en; }
+ int size() { return s_ize; }
+
+ // list-handling
+ bool resize(int s=0);
+ bool append(KNArticle *a, bool autoSort=false);
+ void remove(int pos, bool autoDel=false, bool autoCompact=false);
+ void clear();
+ void compact();
+ void syncWithMaster();
+
+ // sorting
+ SortingType sortMode() { return s_ortType; }
+ void setSortMode(SortingType s) { s_ortType=s; }
+ void sort();
+ static int compareById(const void *a1, const void *a2);
+ static int compareByMsgId(const void *a1, const void *a2);
+
+ // article access
+ KNArticle* at(int i) { return ( (i>=0 && i<l_en) ? l_ist[i] : 0 ); }
+ KNArticle* bsearch(int id);
+ KNArticle* bsearch(const QCString &id);
+
+ int indexForId(int id);
+ int indexForMsgId(const QCString &id);
+
+ protected:
+ KNArticleVector *m_aster;
+ int l_en,
+ s_ize;
+ KNArticle **l_ist;
+ SortingType s_ortType;
+};
+
+
+class KNArticleCollection : public KNCollection {
+
+ public:
+ KNArticleCollection(KNCollection *p=0);
+ ~KNArticleCollection();
+
+ // info
+ bool isEmpty() { return a_rticles.isEmpty(); }
+ bool isLoaded() { return (c_ount==0 || a_rticles.length()>0); }
+ int size() { return a_rticles.size(); }
+ int length() { return a_rticles.length(); }
+
+ // cache behavior
+ bool isNotUnloadable() { return n_otUnloadable; }
+ void setNotUnloadable(bool b=true) { n_otUnloadable = b; }
+
+ // locking
+ unsigned int lockedArticles() { return l_ockedArticles; }
+ void articleLocked() { l_ockedArticles++; }
+ void articleUnlocked() { l_ockedArticles--; }
+
+ // list-handling
+ bool resize(int s=0);
+ bool append(KNArticle *a, bool autoSync=false);
+ void clear();
+ void compact();
+ void setLastID();
+
+ // article access
+ KNArticle* at(int i) { return a_rticles.at(i); }
+ KNArticle* byId(int id);
+ KNArticle* byMessageId(const QCString &mid);
+
+ // search index
+ void syncSearchIndex();
+ void clearSearchIndex();
+
+ protected:
+ int l_astID;
+ unsigned int l_ockedArticles;
+ bool n_otUnloadable;
+ KNArticleVector a_rticles;
+ KNArticleVector m_idIndex;
+};
+
+
+#endif
diff --git a/knode/knarticlefactory.cpp b/knode/knarticlefactory.cpp
new file mode 100644
index 000000000..3c072887b
--- /dev/null
+++ b/knode/knarticlefactory.cpp
@@ -0,0 +1,1111 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qvbox.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kwin.h>
+#include <kapplication.h>
+
+#include "knarticlefactory.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "kngroupmanager.h"
+#include "knaccountmanager.h"
+#include "knfoldermanager.h"
+#include "knarticlemanager.h"
+#include "knfolder.h"
+#include "kncomposer.h"
+#include "knnntpaccount.h"
+#include "utilities.h"
+#include "resource.h"
+
+
+KNArticleFactory::KNArticleFactory(QObject *p, const char *n)
+ : QObject(p, n), s_endErrDlg(0)
+{
+}
+
+
+KNArticleFactory::~KNArticleFactory()
+{
+ for ( QValueList<KNComposer*>::Iterator it = mCompList.begin(); it != mCompList.end(); ++it )
+ delete (*it);
+ delete s_endErrDlg;
+}
+
+
+void KNArticleFactory::createPosting(KNNntpAccount *a)
+{
+ if(!a)
+ return;
+
+ QString sig;
+ KNLocalArticle *art=newArticle(a, sig, knGlobals.configManager()->postNewsTechnical()->charset());
+ if(!art)
+ return;
+
+ art->setServerId(a->id());
+ art->setDoPost(true);
+ art->setDoMail(false);
+
+ KNComposer *c = new KNComposer( art, QString::null, sig, QString::null, true );
+ mCompList.append( c );
+ connect(c, SIGNAL(composerDone(KNComposer*)), this, SLOT(slotComposerDone(KNComposer*)));
+ c->show();
+}
+
+
+void KNArticleFactory::createPosting(KNGroup *g)
+{
+ if(!g)
+ return;
+
+ QCString chset;
+ if (g->useCharset())
+ chset = g->defaultCharset();
+ else
+ chset = knGlobals.configManager()->postNewsTechnical()->charset();
+
+ QString sig;
+ KNLocalArticle *art=newArticle(g, sig, chset);
+
+ if(!art)
+ return;
+
+ art->setServerId(g->account()->id());
+ art->setDoPost(true);
+ art->setDoMail(false);
+ art->newsgroups()->fromUnicodeString(g->groupname(), art->defaultCharset());
+
+ KNComposer *c=new KNComposer(art, QString::null, sig, QString::null, true);
+ mCompList.append( c );
+ connect(c, SIGNAL(composerDone(KNComposer*)), this, SLOT(slotComposerDone(KNComposer*)));
+ c->show();
+}
+
+
+void KNArticleFactory::createReply(KNRemoteArticle *a, QString selectedText, bool post, bool mail)
+{
+ if(!a)
+ return;
+
+ KNGroup *g=static_cast<KNGroup*>(a->collection());
+
+ QCString chset;
+ if (knGlobals.configManager()->postNewsTechnical()->useOwnCharset()) {
+ if (g->useCharset())
+ chset = g->defaultCharset();
+ else
+ chset = knGlobals.configManager()->postNewsTechnical()->charset();
+ } else
+ chset = knGlobals.configManager()->postNewsTechnical()->findComposerCharset(a->contentType()->charset());
+
+ //create new article
+ QString sig;
+ KNLocalArticle *art=newArticle(g, sig, chset, true, a);
+ if(!art)
+ return;
+
+ art->setServerId(g->account()->id());
+ art->setDoPost(post);
+ art->setDoMail(mail);
+
+ //------------------------- <Headers> ----------------------------
+
+ //subject
+ QString subject=a->subject()->asUnicodeString();
+ if(subject.left(3).upper()!="RE:")
+ subject.prepend("Re: ");
+ art->subject()->fromUnicodeString(subject, a->subject()->rfc2047Charset());
+
+ //newsgroups
+ KMime::Headers::FollowUpTo *fup2=a->followUpTo(false);
+ if(fup2 && !fup2->isEmpty()) {
+ if( ( fup2->as7BitString(false).upper()=="POSTER" ) ) { //Followup-To: poster
+ if( post && // user wanted to reply by public posting?
+ // ask the user if she wants to ignore this F'up-To: poster
+ ( KMessageBox::Yes != KMessageBox::questionYesNo(knGlobals.topWidget,
+ i18n("The author has requested a reply by email instead\nof a followup to the newsgroup. (Followup-To: poster)\nDo you want to reply in public anyway?"), QString::null, i18n("Reply Public"), i18n("Reply by Email")) ))
+ {
+ art->setDoPost(false);
+ art->setDoMail(true);
+ }
+ art->newsgroups()->from7BitString(a->newsgroups()->as7BitString(false));
+ }
+ else
+ art->newsgroups()->from7BitString(fup2->as7BitString(false));
+ }
+ else
+ art->newsgroups()->from7BitString(a->newsgroups()->as7BitString(false));
+
+ //To
+ KMime::Headers::ReplyTo *replyTo=a->replyTo(false);
+ KMime::Headers::AddressField address;
+ if(replyTo && !replyTo->isEmpty()) {
+ if(replyTo->hasName())
+ address.setName(replyTo->name());
+ if(replyTo->hasEmail())
+ address.setEmail(replyTo->email().copy());
+ }
+ else {
+ KMime::Headers::From *from=a->from();
+ if(from->hasName())
+ address.setName(from->name());
+ if(from->hasEmail())
+ address.setEmail(from->email().copy());
+ }
+ art->to()->addAddress(address);
+
+ //References
+ KMime::Headers::References *references=a->references(false);
+ QCString refs;
+ if (references)
+ refs=references->as7BitString(false);
+ else
+ refs = "";
+
+ art->references()->from7BitString(refs);
+ art->references()->append(a->messageID()->as7BitString(false));
+
+ //Mail-Copies-To
+ bool authorDislikesMailCopies=false;
+ bool authorWantsMailCopies=false;
+ KMime::Headers::MailCopiesTo *mailCopiesTo=a->mailCopiesTo(false);
+
+ if(mailCopiesTo && !mailCopiesTo->isEmpty() && mailCopiesTo->isValid()) {
+ authorDislikesMailCopies = mailCopiesTo->neverCopy();
+ authorWantsMailCopies = mailCopiesTo->alwaysCopy();
+ if (authorWantsMailCopies) // warn the user
+ KMessageBox::information(knGlobals.topWidget,i18n("The author requested a mail copy of your reply. (Mail-Copies-To header)"),
+ QString::null,"mailCopiesToWarning");
+ if (authorWantsMailCopies && mailCopiesTo->hasEmail()) {
+ address.setName(mailCopiesTo->name());
+ address.setEmail(mailCopiesTo->email());
+ art->to()->clear();
+ art->to()->addAddress(address);
+ }
+ }
+
+ //------------------------- </Headers> ---------------------------
+
+ //--------------------------- <Body> -----------------------------
+
+ // attribution line
+ QString attribution=knGlobals.configManager()->postNewsComposer()->intro();
+ QString name(a->from()->name());
+ if (name.isEmpty())
+ name = QString::fromLatin1(a->from()->email());
+ attribution.replace(QRegExp("%NAME"),name);
+ attribution.replace(QRegExp("%EMAIL"),QString::fromLatin1(a->from()->email()));
+ attribution.replace(QRegExp("%DATE"),KGlobal::locale()->formatDateTime(a->date()->qdt(),false));
+ attribution.replace(QRegExp("%MSID"),a->messageID()->asUnicodeString());
+ attribution.replace(QRegExp("%GROUP"),g->groupname());
+ attribution.replace(QRegExp("%L"),"\n");
+ attribution+="\n\n";
+
+ QString quoted=attribution;
+ QString notRewraped=QString::null;
+ QStringList text;
+ QStringList::Iterator line;
+ bool incSig=knGlobals.configManager()->postNewsComposer()->includeSignature();
+
+ if (selectedText.isEmpty()) {
+ KMime::Content *tc = a->textContent();
+ if(tc)
+ tc->decodedText(text, true, knGlobals.configManager()->readNewsViewer()->removeTrailingNewlines());
+ }
+ else
+ text = QStringList::split('\n',selectedText,true);
+
+ for(line=text.begin(); line!=text.end(); ++line) {
+ if(!incSig && (*line)=="-- ")
+ break;
+
+ if ((*line)[0]=='>')
+ quoted+=">"+(*line)+"\n"; // second quote level without space
+ else
+ quoted+="> "+(*line)+"\n";
+ }
+
+ if(knGlobals.configManager()->postNewsComposer()->rewrap()) { //rewrap original article
+
+ notRewraped=quoted; // store the original text in here, the user can request it in the composer
+ quoted=attribution;
+
+ quoted += KNHelper::rewrapStringList(text, knGlobals.configManager()->postNewsComposer()->maxLineLength(), '>', !incSig, false);
+ }
+
+ //-------------------------- </Body> -----------------------------
+
+ if (art->doMail() && knGlobals.configManager()->postNewsTechnical()->useExternalMailer()) {
+ sendMailExternal(address.asUnicodeString(), subject, quoted);
+ art->setDoMail(false);
+ if (!art->doPost()) {
+ delete art;
+ return;
+ }
+ }
+
+ //open composer
+ KNComposer *c=new KNComposer(art, quoted, sig, notRewraped, true, authorDislikesMailCopies, authorWantsMailCopies);
+ mCompList.append( c );
+ connect(c, SIGNAL(composerDone(KNComposer*)), this, SLOT(slotComposerDone(KNComposer*)));
+ c->show();
+}
+
+
+void KNArticleFactory::createForward(KNArticle *a)
+{
+ if(!a)
+ return;
+
+ KMime::Headers::ContentType *ct=a->contentType();
+ QCString chset;
+ bool incAtt = ( !knGlobals.configManager()->postNewsTechnical()->useExternalMailer() &&
+ ct->isMultipart() && ct->isSubtype("mixed") &&
+ KMessageBox::Yes == KMessageBox::questionYesNo(knGlobals.topWidget,
+ i18n("This article contains attachments. Do you want them to be forwarded as well?"), QString::null, i18n("Forward"), i18n("Do Not Forward"))
+ );
+
+ if (knGlobals.configManager()->postNewsTechnical()->useOwnCharset())
+ chset = knGlobals.configManager()->postNewsTechnical()->charset();
+ else
+ chset = knGlobals.configManager()->postNewsTechnical()->findComposerCharset(a->contentType()->charset());
+
+ //create new article
+ QString sig;
+ KNLocalArticle *art=newArticle(knGlobals.groupManager()->currentGroup(), sig, chset);
+ if(!art)
+ return;
+
+ art->setDoPost(false);
+ art->setDoMail(true);
+
+ //------------------------- <Headers> ----------------------------
+
+ //subject
+ QString subject=("Fwd: "+a->subject()->asUnicodeString());
+ art->subject()->fromUnicodeString(subject, a->subject()->rfc2047Charset());
+
+ //------------------------- </Headers> ---------------------------
+
+ //--------------------------- <Body> -----------------------------
+
+ QString fwd = QString("\n--------------- %1\n\n").arg(i18n("Forwarded message (begin)"));
+
+ fwd+=( i18n("Subject") + ": " + a->subject()->asUnicodeString() + "\n" );
+ fwd+=( i18n("From") + ": " + a->from()->asUnicodeString() + "\n" );
+ fwd+=( i18n("Date") + ": " + a->date()->asUnicodeString() + "\n" );
+ fwd+=( i18n("Newsgroup") + ": " + a->newsgroups()->asUnicodeString() + "\n\n" );
+
+ KMime::Content *text=a->textContent();
+ if(text) {
+ QStringList decodedLines;
+ text->decodedText( decodedLines, false, false );
+ for(QStringList::Iterator it=decodedLines.begin(); it!=decodedLines.end(); ++it)
+ fwd += (*it) + "\n";
+ }
+
+ fwd += QString("\n--------------- %1\n").arg(i18n("Forwarded message (end)"));
+
+ //--------------------------- </Body> ----------------------------
+
+
+ //------------------------ <Attachments> -------------------------
+
+ if(incAtt) {
+ KMime::Content::List al;
+
+ a->attachments(&al, false);
+ for(KMime::Content *c=al.first(); c; c=al.next()) {
+ art->addContent( new KMime::Content(c->head(), c->body()) );
+ }
+ }
+
+ //------------------------ </Attachments> ------------------------
+
+
+ if (knGlobals.configManager()->postNewsTechnical()->useExternalMailer()) {
+ sendMailExternal(QString::null, subject, fwd);
+ delete art;
+ return;
+ }
+
+ //open composer
+ KNComposer *c=new KNComposer(art, fwd, sig, QString::null, true);
+ mCompList.append( c );
+ connect(c, SIGNAL(composerDone(KNComposer*)), this, SLOT(slotComposerDone(KNComposer*)));
+ c->show();
+}
+
+
+void KNArticleFactory::createCancel(KNArticle *a)
+{
+ if(!cancelAllowed(a))
+ return;
+
+ if(KMessageBox::No==KMessageBox::questionYesNo(knGlobals.topWidget,
+ i18n("Do you really want to cancel this article?"), QString::null, i18n("Cancel Article"), KStdGuiItem::cancel()))
+ return;
+
+ bool sendNow;
+ switch (KMessageBox::warningYesNoCancel(knGlobals.topWidget, i18n("Do you want to send the cancel\nmessage now or later?"), i18n("Question"),i18n("&Now"),i18n("&Later"))) {
+ case KMessageBox::Yes : sendNow = true; break;
+ case KMessageBox::No : sendNow = false; break;
+ default : return;
+ }
+
+ KNGroup *grp;
+ KNNntpAccount *nntp=0;
+
+ if(a->type()==KMime::Base::ATremote)
+ nntp=(static_cast<KNGroup*>(a->collection()))->account();
+ else {
+ if(!nntp)
+ nntp=knGlobals.accountManager()->first();
+ if(!nntp) {
+ KMessageBox::error(knGlobals.topWidget, i18n("You have no valid news accounts configured."));
+ return;
+ }
+ KNLocalArticle *la=static_cast<KNLocalArticle*>(a);
+ la->setCanceled(true);
+ la->updateListItem();
+ nntp=knGlobals.accountManager()->account(la->serverId());
+ }
+
+ grp=knGlobals.groupManager()->group(a->newsgroups()->firstGroup(), nntp);
+
+ QString sig;
+ KNLocalArticle *art=newArticle(grp, sig, "us-ascii", false);
+ if(!art)
+ return;
+
+ //init
+ art->setDoPost(true);
+ art->setDoMail(false);
+
+ //server
+ art->setServerId(nntp->id());
+
+ //subject
+ KMime::Headers::MessageID *msgId=a->messageID();
+ QCString tmp;
+ tmp="cancel of "+msgId->as7BitString(false);
+ art->subject()->from7BitString(tmp);
+
+ //newsgroups
+ art->newsgroups()->from7BitString(a->newsgroups()->as7BitString(false));
+
+ //control
+ tmp="cancel "+msgId->as7BitString(false);
+ art->control()->from7BitString(tmp);
+
+ //Lines
+ art->lines()->setNumberOfLines(1);
+
+ //body
+ art->fromUnicodeString(QString::fromLatin1("cancel by original author\n"));
+
+ //assemble
+ art->assemble();
+
+ //send
+ KNLocalArticle::List lst;
+ lst.append(art);
+ sendArticles( lst, sendNow );
+}
+
+
+void KNArticleFactory::createSupersede(KNArticle *a)
+{
+ if (!a)
+ return;
+
+ if(!cancelAllowed(a))
+ return;
+
+ if(KMessageBox::No==KMessageBox::questionYesNo(knGlobals.topWidget,
+ i18n("Do you really want to supersede this article?"), QString::null, i18n("Supersede"), KStdGuiItem::cancel()))
+ return;
+
+ KNGroup *grp;
+ KNNntpAccount *nntp;
+
+ if(a->type()==KMime::Base::ATremote)
+ nntp=(static_cast<KNGroup*>(a->collection()))->account();
+ else {
+ KNLocalArticle *la=static_cast<KNLocalArticle*>(a);
+ la->setCanceled(true);
+ la->updateListItem();
+ nntp=knGlobals.accountManager()->account(la->serverId());
+ if(!nntp)
+ nntp=knGlobals.accountManager()->first();
+ if(!nntp) {
+ KMessageBox::error(knGlobals.topWidget, i18n("You have no valid news accounts configured."));
+ return;
+ }
+ }
+
+ grp=knGlobals.groupManager()->group(a->newsgroups()->firstGroup(), nntp);
+
+ //new article
+ QString sig;
+ KNLocalArticle *art=newArticle(grp, sig, knGlobals.configManager()->postNewsTechnical()->findComposerCharset(a->contentType()->charset()));
+ if(!art)
+ return;
+
+ art->setDoPost(true);
+ art->setDoMail(false);
+
+ //server
+ art->setServerId(nntp->id());
+
+ //subject
+ art->subject()->fromUnicodeString(a->subject()->asUnicodeString(), a->subject()->rfc2047Charset());
+
+ //newsgroups
+ art->newsgroups()->from7BitString(a->newsgroups()->as7BitString(false));
+
+ //followup-to
+ art->followUpTo()->from7BitString(a->followUpTo()->as7BitString(false));
+
+ //References
+ if ( !a->references()->isEmpty() )
+ art->references()->from7BitString( a->references()->as7BitString(false) );
+
+ //Supersedes
+ art->supersedes()->from7BitString(a->messageID()->as7BitString(false));
+
+ //Body
+ QString text;
+ KMime::Content *textContent=a->textContent();
+ if(textContent)
+ textContent->decodedText(text);
+
+ //open composer
+ KNComposer *c=new KNComposer(art, text, sig);
+ mCompList.append( c );
+ connect(c, SIGNAL(composerDone(KNComposer*)), this, SLOT(slotComposerDone(KNComposer*)));
+ c->show();
+}
+
+
+void KNArticleFactory::createMail(KMime::Headers::AddressField *address)
+{
+ if (knGlobals.configManager()->postNewsTechnical()->useExternalMailer()) {
+ sendMailExternal(address->asUnicodeString());
+ return;
+ }
+
+ //create new article
+ QString sig;
+ KNLocalArticle *art=newArticle(knGlobals.groupManager()->currentGroup(), sig, knGlobals.configManager()->postNewsTechnical()->charset());
+ if(!art)
+ return;
+
+ art->setDoMail(true);
+ art->setDoPost(false);
+ art->to()->addAddress((*address));
+
+ //open composer
+ KNComposer *c=new KNComposer(art, QString::null, sig, QString::null, true);
+ mCompList.append( c );
+ connect(c, SIGNAL(composerDone(KNComposer*)), this, SLOT(slotComposerDone(KNComposer*)));
+ c->show();
+}
+
+
+void KNArticleFactory::sendMailExternal(const QString &address, const QString &subject, const QString &body)
+{
+ KURL mailtoURL;
+ QStringList queries;
+ QString query=QString::null;
+ mailtoURL.setProtocol("mailto");
+
+ if (!address.isEmpty())
+ mailtoURL.setPath(address);
+ if (!subject.isEmpty())
+ queries.append("subject="+KURL::encode_string(subject));
+ if (!body.isEmpty())
+ queries.append("body="+KURL::encode_string(body));
+
+ if (queries.count() > 0) {
+ query = "?";
+ for ( QStringList::Iterator it = queries.begin(); it != queries.end(); ++it ) {
+ if (it != queries.begin())
+ query.append("&");
+ query.append((*it));
+ }
+ }
+
+ if (!query.isEmpty())
+ mailtoURL.setQuery(query);
+
+ kapp->invokeMailer(mailtoURL);
+}
+
+
+void KNArticleFactory::edit(KNLocalArticle *a)
+{
+ if(!a)
+ return;
+
+ KNComposer *com=findComposer(a);
+ if(com) {
+ KWin::activateWindow(com->winId());
+ return;
+ }
+
+ if(a->editDisabled()) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("This article cannot be edited."));
+ return;
+ }
+
+ //find signature
+ KNConfig::Identity *id=knGlobals.configManager()->identity();
+
+ if(a->doPost()) {
+ KNNntpAccount *acc=knGlobals.accountManager()->account(a->serverId());
+ if(acc) {
+ KMime::Headers::Newsgroups *grps=a->newsgroups();
+ KNGroup *grp=knGlobals.groupManager()->group(grps->firstGroup(), acc);
+ if (grp && grp->identity())
+ id=grp->identity();
+ else if (acc->identity())
+ id=acc->identity();
+ }
+ }
+
+ //load article body
+ if(!a->hasContent())
+ knGlobals.articleManager()->loadArticle(a);
+
+ //open composer
+ com=new KNComposer(a, QString::null, id->getSignature());
+ if(id->useSigGenerator() && !id->getSigGeneratorStdErr().isEmpty())
+ KMessageBox::information(knGlobals.topWidget,
+ i18n("<qt>The signature generator program produced the "
+ "following output:<br><br>%1</qt>")
+ .arg(id->getSigGeneratorStdErr()));
+
+ mCompList.append( com );
+ connect(com, SIGNAL(composerDone(KNComposer*)), this, SLOT(slotComposerDone(KNComposer*)));
+ com->show();
+}
+
+
+void KNArticleFactory::sendArticles( KNLocalArticle::List &l, bool now )
+{
+ KNJobData *job=0;
+ KNServerInfo *ser=0;
+
+ KNLocalArticle::List unsent, sent;
+ for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
+ if ( (*it)->pending() )
+ unsent.append( (*it) );
+ else
+ sent.append( (*it) );
+ }
+
+ if(!sent.isEmpty()) {
+ showSendErrorDialog();
+ for ( KNLocalArticle::List::Iterator it = sent.begin(); it != sent.end(); ++it )
+ s_endErrDlg->append( (*it)->subject()->asUnicodeString(), i18n("Article has already been sent.") );
+ }
+
+ if(!now) {
+ knGlobals.articleManager()->moveIntoFolder(unsent, knGlobals.folderManager()->outbox());
+ return;
+ }
+
+
+ for ( KNLocalArticle::List::Iterator it = unsent.begin(); it != unsent.end(); ++it ) {
+
+ if ( (*it)->isLocked() )
+ continue;
+
+ if ( !(*it)->hasContent() ) {
+ if ( !knGlobals.articleManager()->loadArticle( (*it) ) ) {
+ showSendErrorDialog();
+ s_endErrDlg->append( (*it)->subject()->asUnicodeString(), i18n("Unable to load article.") );
+ continue;
+ }
+ }
+
+ if ( (*it)->doPost() && !(*it)->posted() ) {
+ ser = knGlobals.accountManager()->account( (*it)->serverId() );
+ job = new KNJobData( KNJobData::JTpostArticle, this, ser, (*it) );
+ emitJob(job);
+ }
+ else if( (*it)->doMail() && !(*it)->mailed() ) {
+ ser = knGlobals.accountManager()->smtp();
+ job = new KNJobData( KNJobData::JTmail, this, ser, (*it) );
+ emitJob(job);
+ }
+ }
+}
+
+
+void KNArticleFactory::sendOutbox()
+{
+ KNLocalArticle::List lst;
+ KNFolder *ob=0;
+
+ if(!knGlobals.folderManager()->loadOutbox()) {
+ KMessageBox::error(knGlobals.topWidget, i18n("Unable to load the outbox-folder."));
+ return;
+ }
+
+ ob=knGlobals.folderManager()->outbox();
+ for(int i=0; i< ob->length(); i++)
+ lst.append(ob->at(i));
+
+ sendArticles( lst, true );
+}
+
+
+bool KNArticleFactory::closeComposeWindows()
+{
+ while ( !mCompList.isEmpty() ) {
+ QValueList<KNComposer*>::Iterator it = mCompList.begin();
+ if ( !(*it)->close() )
+ return false;
+ }
+
+ return true;
+}
+
+
+void KNArticleFactory::deleteComposerForArticle(KNLocalArticle *a)
+{
+ KNComposer *com = findComposer( a );
+ if ( com ) {
+ mCompList.remove( com );
+ delete com;
+ }
+}
+
+
+KNComposer* KNArticleFactory::findComposer(KNLocalArticle *a)
+{
+ for ( QValueList<KNComposer*>::Iterator it = mCompList.begin(); it != mCompList.end(); ++it )
+ if ( (*it)->article() == a )
+ return (*it);
+ return 0;
+}
+
+
+void KNArticleFactory::configChanged()
+{
+ for ( QValueList<KNComposer*>::Iterator it = mCompList.begin(); it != mCompList.end(); ++it )
+ (*it)->setConfig( false );
+}
+
+
+void KNArticleFactory::processJob(KNJobData *j)
+{
+ KNLocalArticle *art=static_cast<KNLocalArticle*>(j->data());
+ KNLocalArticle::List lst;
+ lst.append(art);
+
+ if(j->canceled()) {
+ delete j;
+
+ //sending of this article was canceled => move it to the "Outbox-Folder"
+ if(art->collection()!=knGlobals.folderManager()->outbox())
+ knGlobals.articleManager()->moveIntoFolder(lst, knGlobals.folderManager()->outbox());
+
+ KMessageBox::information(knGlobals.topWidget, i18n("You have aborted the posting of articles. The unsent articles are stored in the \"Outbox\" folder."));
+
+ return;
+ }
+
+ if(!j->success()) {
+ showSendErrorDialog();
+ s_endErrDlg->append(art->subject()->asUnicodeString(), j->errorString());
+ delete j; //unlock article
+
+ //sending of this article failed => move it to the "Outbox-Folder"
+ if(art->collection()!=knGlobals.folderManager()->outbox())
+ knGlobals.articleManager()->moveIntoFolder(lst, knGlobals.folderManager()->outbox());
+ }
+ else {
+
+ //disable edit
+ art->setEditDisabled(true);
+
+ switch(j->type()) {
+
+ case KNJobData::JTpostArticle:
+ delete j; //unlock article
+ art->setPosted(true);
+ if(art->doMail() && !art->mailed()) { //article has been posted, now mail it
+ sendArticles( lst, true );
+ return;
+ }
+ break;
+
+ case KNJobData::JTmail:
+ delete j; //unlock article
+ art->setMailed(true);
+ break;
+
+ default: break;
+ };
+
+ //article has been sent successfully => move it to the "Sent-folder"
+ knGlobals.articleManager()->moveIntoFolder(lst, knGlobals.folderManager()->sent());
+ }
+}
+
+
+KNLocalArticle* KNArticleFactory::newArticle(KNCollection *col, QString &sig, QCString defChset, bool withXHeaders, KNArticle *origPost)
+{
+ KNConfig::PostNewsTechnical *pnt=knGlobals.configManager()->postNewsTechnical();
+
+ if(pnt->generateMessageID() && pnt->hostname().isEmpty()) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("Please set a hostname for the generation\nof the message-id or disable it."));
+ return 0;
+ }
+
+ KNLocalArticle *art=new KNLocalArticle(0);
+ KNConfig::Identity *tmpId=0, *id=0;
+
+ if (col) {
+ if (col->type() == KNCollection::CTgroup) {
+ id = (static_cast<KNGroup *>(col))->identity();
+ tmpId = (static_cast<KNGroup *>(col))->account()->identity();
+ } else
+ if (col->type() == KNCollection::CTnntpAccount) {
+ id = (static_cast<KNNntpAccount *>(col))->identity();
+ }
+ }
+
+ // determine active innermost non-empty identity
+ if (!id) {
+ if (tmpId)
+ id = tmpId;
+ else
+ id = knGlobals.configManager()->identity();
+ }
+
+ //Message-id
+ if(pnt->generateMessageID())
+ art->messageID()->generate(pnt->hostname());
+
+ //From
+ KMime::Headers::From *from=art->from();
+ from->setRFC2047Charset(pnt->charset());
+
+ //name
+ if(id->hasName())
+ from->setName(id->name());
+
+ //email
+ if(id->hasEmail()&&id->emailIsValid())
+ from->setEmail(id->email().latin1());
+ else {
+ if ( id->hasEmail() )
+ KMessageBox::sorry(knGlobals.topWidget,
+ i18n("Please enter a valid email address at the identity tab of the account configuration dialog."));
+ else
+ KMessageBox::sorry(knGlobals.topWidget,
+ i18n("Please enter a valid email address at the identity section of the configuration dialog."));
+ delete art;
+ return 0;
+ }
+
+ //Reply-To
+ if(id->hasReplyTo()) {
+ art->replyTo()->fromUnicodeString(id->replyTo(), pnt->charset());
+ if (!art->replyTo()->hasEmail()) // the header is invalid => drop it
+ art->removeHeader("Reply-To");
+ }
+
+ //Mail-Copies-To
+ if(id->hasMailCopiesTo()) {
+ art->mailCopiesTo()->fromUnicodeString(id->mailCopiesTo(), pnt->charset());
+ if (!art->mailCopiesTo()->isValid()) // the header is invalid => drop it
+ art->removeHeader("Mail-Copies-To");
+ }
+
+ //Organization
+ if(id->hasOrga())
+ art->organization()->fromUnicodeString(id->orga(), pnt->charset());
+
+ //Date
+ art->date()->setUnixTime(); //set current date+time
+
+ //User-Agent
+ if( !pnt->noUserAgent() ) {
+ art->userAgent()->from7BitString("KNode/" KNODE_VERSION);
+ }
+
+ //Mime
+ KMime::Headers::ContentType *type=art->contentType();
+ type->setMimeType("text/plain");
+
+ type->setCharset(defChset);
+
+ if (defChset.lower()=="us-ascii")
+ art->contentTransferEncoding()->setCte(KMime::Headers::CE7Bit);
+ else
+ art->contentTransferEncoding()->setCte(pnt->allow8BitBody()? KMime::Headers::CE8Bit : KMime::Headers::CEquPr);
+
+ //X-Headers
+ if(withXHeaders) {
+ KNConfig::XHeaders::Iterator it;
+ for(it=pnt->xHeaders().begin(); it!=pnt->xHeaders().end(); ++it) {
+ QString value = (*it).value();
+ if(origPost) {
+ QString name(origPost->from()->name());
+ if (name.isEmpty())
+ name = QString::fromLatin1(origPost->from()->email());
+ value.replace(QRegExp("%NAME"), name);
+ value.replace(QRegExp("%EMAIL"), QString::fromLatin1(origPost->from()->email()));
+ }
+ else
+ if(value.find("%NAME") != -1 || value.find("%EMAIL") != -1)
+ continue;
+
+ art->setHeader( new KMime::Headers::Generic( (QCString("X-")+(*it).name()), art, value, pnt->charset() ) );
+ }
+ }
+
+ //Signature
+ if(id->hasSignature())
+ {
+ sig=id->getSignature();
+ if(id->useSigGenerator() && !id->getSigGeneratorStdErr().isEmpty())
+ KMessageBox::information(knGlobals.topWidget,
+ i18n("<qt>The signature generator program produced the "
+ "following output:<br><br>%1</qt>")
+ .arg(id->getSigGeneratorStdErr()));
+ }
+ else
+ sig=QString::null;
+
+ return art;
+}
+
+
+bool KNArticleFactory::cancelAllowed(KNArticle *a)
+{
+ if(!a)
+ return false;
+
+ if(a->type()==KMime::Base::ATlocal) {
+ KNLocalArticle *localArt=static_cast<KNLocalArticle*>(a);
+
+ if(localArt->doMail() && !localArt->doPost()) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("Emails cannot be canceled or superseded."));
+ return false;
+ }
+
+ KMime::Headers::Control *ctrl=localArt->control(false);
+ if(ctrl && ctrl->isCancel()) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("Cancel messages cannot be canceled or superseded."));
+ return false;
+ }
+
+ if(!localArt->posted()) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("Only sent articles can be canceled or superseded."));
+ return false;
+ }
+
+ if(localArt->canceled()) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("This article has already been canceled or superseded."));
+ return false;
+ }
+
+ KMime::Headers::MessageID *mid=localArt->messageID(false);
+ if(!mid || mid->isEmpty()) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n(
+"This article cannot be canceled or superseded,\n\
+because its message-id has not been created by KNode.\n\
+But you can look for your article in the newsgroup\n\
+and cancel (or supersede) it there."));
+ return false;
+ }
+
+ return true;
+ }
+ else if(a->type()==KMime::Base::ATremote) {
+
+ KNRemoteArticle *remArt=static_cast<KNRemoteArticle*>(a);
+ KNGroup *g=static_cast<KNGroup*>(a->collection());
+ KNConfig::Identity *defId=knGlobals.configManager()->identity(),
+ *gid=g->identity(),
+ *accId=g->account()->identity();
+ bool ownArticle = false;
+
+ if (gid && gid->hasName())
+ ownArticle |= ( gid->name() == remArt->from()->name() );
+ if (accId && accId->hasName())
+ ownArticle |= ( accId->name() == remArt->from()->name() );
+ ownArticle |= ( defId->name() == remArt->from()->name() );
+
+ if(ownArticle) {
+ ownArticle = false;
+ if(gid && gid->hasEmail())
+ ownArticle |= ( gid->email().latin1() == remArt->from()->email() );
+ if (accId && accId->hasEmail())
+ ownArticle |= ( accId->email().latin1() == remArt->from()->email() );
+ ownArticle |= ( defId->email().latin1() == remArt->from()->email() );
+ }
+
+ if(!ownArticle) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("This article does not appear to be from you.\nYou can only cancel or supersede your own articles."));
+ return false;
+ }
+
+ if(!remArt->hasContent()) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("You have to download the article body\nbefore you can cancel or supersede the article."));
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+void KNArticleFactory::showSendErrorDialog()
+{
+ if(!s_endErrDlg) {
+ s_endErrDlg=new KNSendErrorDialog();
+ connect(s_endErrDlg, SIGNAL(closeClicked()), this, SLOT(slotSendErrorDialogDone()));
+ }
+ s_endErrDlg->show();
+}
+
+
+void KNArticleFactory::slotComposerDone(KNComposer *com)
+{
+ bool delCom=true;
+ KNLocalArticle::List lst;
+ lst.append(com->article());
+
+ switch(com->result()) {
+
+ case KNComposer::CRsendNow:
+ delCom=com->hasValidData();
+ if(delCom) {
+ if ( com->applyChanges() )
+ sendArticles( lst, true );
+ else
+ delCom = false;
+ }
+ break;
+
+ case KNComposer::CRsendLater:
+ delCom=com->hasValidData();
+ if(delCom) {
+ if ( com->applyChanges() )
+ sendArticles( lst, false );
+ else
+ delCom = false;
+ }
+ break;
+
+ case KNComposer::CRsave :
+ if ( com->applyChanges() )
+ knGlobals.articleManager()->moveIntoFolder(lst, knGlobals.folderManager()->drafts());
+ break;
+
+ case KNComposer::CRdelAsk:
+ delCom=knGlobals.articleManager()->deleteArticles(lst, true);
+ break;
+
+ case KNComposer::CRdel:
+ delCom=knGlobals.articleManager()->deleteArticles(lst, false);
+ break;
+
+ case KNComposer::CRcancel:
+ // just close...
+ break;
+
+ default: break;
+
+ };
+
+ if ( delCom ) {
+ mCompList.remove( com );
+ delete com;
+ } else
+ KWin::activateWindow(com->winId());
+}
+
+
+void KNArticleFactory::slotSendErrorDialogDone()
+{
+ s_endErrDlg->delayedDestruct();
+ s_endErrDlg=0;
+}
+
+
+//======================================================================================================
+
+
+KNSendErrorDialog::KNSendErrorDialog()
+ : KDialogBase(knGlobals.topWidget, 0, true, i18n("Errors While Sending"), Close, Close, true)
+{
+ p_ixmap=knGlobals.configManager()->appearance()->icon(KNConfig::Appearance::sendErr);
+
+ QVBox *page = makeVBoxMainWidget();
+
+ new QLabel(QString("<b>%1</b><br>%2").arg(i18n("Errors occurred while sending these articles:"))
+ .arg(i18n("The unsent articles are stored in the \"Outbox\" folder.")), page);
+ j_obs=new KNDialogListBox(true, page);
+ e_rror=new QLabel(QString::null, page);
+
+ connect(j_obs, SIGNAL(highlighted(int)), this, SLOT(slotHighlighted(int)));
+
+ KNHelper::restoreWindowSize("sendDlg", this, QSize(320,250));
+}
+
+
+KNSendErrorDialog::~KNSendErrorDialog()
+{
+ KNHelper::saveWindowSize("sendDlg", size());
+}
+
+
+void KNSendErrorDialog::append(const QString &subject, const QString &error)
+{
+
+ LBoxItem *it=new LBoxItem(error, subject, &p_ixmap);
+ j_obs->insertItem(it);
+ j_obs->setCurrentItem(it);
+}
+
+
+void KNSendErrorDialog::slotHighlighted(int idx)
+{
+ LBoxItem *it=static_cast<LBoxItem*>(j_obs->item(idx));
+ if(it) {
+ QString tmp=i18n("<b>Error message:</b><br>")+it->error;
+ e_rror->setText(tmp);
+ }
+}
+
+//-------------------------------
+#include "knarticlefactory.moc"
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/knarticlefactory.h b/knode/knarticlefactory.h
new file mode 100644
index 000000000..6b18b6e5b
--- /dev/null
+++ b/knode/knarticlefactory.h
@@ -0,0 +1,124 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNARTICLEFACTORY_H
+#define KNARTICLEFACTORY_H
+
+#include <qvaluelist.h>
+#include <kdialogbase.h>
+
+#include "knjobdata.h"
+#include "knarticle.h"
+#include "knwidgets.h"
+
+class QLabel;
+
+class KNGroup;
+class KNFolder;
+class KNCollection;
+class KNComposer;
+class KNSendErrorDialog;
+class KNNntpAccount;
+
+namespace KNConfig {
+ class Identity;
+}
+
+
+class KNArticleFactory : public QObject , public KNJobConsumer {
+
+ Q_OBJECT
+
+ public:
+ enum replyType { RTgroup, RTmail, RTboth };
+
+ KNArticleFactory(QObject *p=0, const char *n=0);
+ ~KNArticleFactory();
+
+ //factory methods
+ void createPosting(KNNntpAccount *a);
+ void createPosting(KNGroup *g);
+ void createReply(KNRemoteArticle *a, QString selectedText=QString::null, bool post=true, bool mail=false);
+ void createForward(KNArticle *a);
+ void createCancel(KNArticle *a);
+ void createSupersede(KNArticle *a);
+ void createMail(KMime::Headers::AddressField *address);
+
+ // send a mail via an external program...
+ void sendMailExternal(const QString &address=QString::null, const QString &subject=QString::null, const QString &body=QString::null);
+
+ //article handling
+ void edit(KNLocalArticle *a);
+ void sendArticles( KNLocalArticle::List &l, bool now = true );
+ void sendOutbox();
+
+ //composer handling
+ bool closeComposeWindows(); // try to close all composers, return false if user objects
+ void deleteComposerForArticle(KNLocalArticle *a);
+ KNComposer* findComposer(KNLocalArticle *a);
+ void configChanged();
+
+ protected:
+ //job handling
+ void processJob(KNJobData *j); //reimplemented from KNJobConsumer
+
+ //article generation
+ // col: group or account
+ KNLocalArticle* newArticle(KNCollection *col, QString &sig, QCString defChset, bool withXHeaders=true, KNArticle *origPost=0);
+
+ //cancel & supersede
+ bool cancelAllowed(KNArticle *a);
+
+ //send-errors
+ void showSendErrorDialog();
+
+ QValueList<KNComposer*> mCompList;
+ KNSendErrorDialog *s_endErrDlg;
+
+ protected slots:
+ void slotComposerDone(KNComposer *com);
+ void slotSendErrorDialogDone();
+
+};
+
+
+class KNSendErrorDialog : public KDialogBase {
+
+ Q_OBJECT
+
+ public:
+ KNSendErrorDialog();
+ ~KNSendErrorDialog();
+
+ void append(const QString &subject, const QString &error);
+
+ protected:
+ class LBoxItem : public KNListBoxItem {
+ public:
+ LBoxItem(const QString &e, const QString &t, QPixmap *p=0)
+ : KNListBoxItem(t, p) , error(e) {}
+ ~LBoxItem() {}
+
+ QString error;
+ };
+
+ KNDialogListBox *j_obs;
+ QLabel *e_rror;
+ QPixmap p_ixmap;
+
+ protected slots:
+ void slotHighlighted(int idx);
+};
+
+#endif //KNARTICLEFACTORY_H
diff --git a/knode/knarticlefilter.cpp b/knode/knarticlefilter.cpp
new file mode 100644
index 000000000..b5a49d55b
--- /dev/null
+++ b/knode/knarticlefilter.cpp
@@ -0,0 +1,383 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <kstandarddirs.h>
+#include <ksimpleconfig.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "kngroup.h"
+#include "knfolder.h"
+#include "utilities.h"
+#include "knarticlefilter.h"
+
+//=============================================================================================================
+
+
+// the names of our default filters
+static const char *defFil[] = { "all","unread","new","watched","threads with unread",
+ "threads with new","own articles","threads with own articles", 0 };
+void dummyFilter()
+{
+ i18n("default filter name","all");
+ i18n("default filter name","unread");
+ i18n("default filter name","new");
+ i18n("default filter name","watched");
+ i18n("default filter name","threads with unread");
+ i18n("default filter name","threads with new");
+ i18n("default filter name","own articles");
+ i18n("default filter name","threads with own articles");
+}
+
+
+//=============================================================================================================
+
+
+KNArticleFilter::KNArticleFilter(int id)
+: i_d(id), c_ount(0), l_oaded(false), e_nabled(true), translateName(true), s_earchFilter(false), apon(articles)
+{}
+
+
+
+// constructs a copy of org
+KNArticleFilter::KNArticleFilter(const KNArticleFilter& org)
+: i_d(-1), c_ount(0), l_oaded(false), e_nabled(org.e_nabled), translateName(true), s_earchFilter(org.s_earchFilter), apon(org.apon)
+{
+ status = org.status;
+ score = org.score;
+ age = org.age;
+ lines = org.lines;
+ subject = org.subject;
+ from = org.from;
+ messageId = org.messageId;
+ references = org.messageId;
+}
+
+
+
+KNArticleFilter::~KNArticleFilter()
+{}
+
+
+
+bool KNArticleFilter::loadInfo()
+{
+ if (i_d!=-1) {
+ QString fname(locate("data",QString( "knode/filters/%1.fltr" ).arg(i_d) ) );
+
+ if (fname.isNull())
+ return false;
+ KSimpleConfig conf(fname,true);
+
+ conf.setGroup("GENERAL");
+ n_ame=conf.readEntry("name");
+ translateName = conf.readBoolEntry("Translate_Name",true);
+ e_nabled=conf.readBoolEntry("enabled", true);
+ apon=(ApOn) conf.readNumEntry("applyOn", 0);
+ return true;
+ }
+ return false;
+}
+
+
+
+void KNArticleFilter::load()
+{
+ QString fname(locate("data",QString( "knode/filters/%1.fltr").arg(i_d) ) );
+
+ if (fname.isNull())
+ return;
+ KSimpleConfig conf(fname,true);
+
+ conf.setGroup("STATUS");
+ status.load(&conf);
+
+ conf.setGroup("SCORE");
+ score.load(&conf);
+
+ conf.setGroup("AGE");
+ age.load(&conf);
+
+ conf.setGroup("LINES");
+ lines.load(&conf);
+
+ conf.setGroup("SUBJECT");
+ subject.load(&conf);
+
+ conf.setGroup("FROM");
+ from.load(&conf);
+
+ conf.setGroup("MESSAGEID");
+ messageId.load(&conf);
+
+ conf.setGroup("REFERENCES");
+ references.load(&conf);
+
+ l_oaded=true;
+
+ kdDebug(5003) << "KNMessageFilter: filter loaded \"" << n_ame << "\" " << endl;
+
+}
+
+
+
+void KNArticleFilter::save()
+{
+ if (i_d==-1)
+ return;
+ QString dir(locateLocal("data","knode/")+"filters/");
+ if (dir.isNull()) {
+ KNHelper::displayInternalFileError();
+ return;
+ }
+ KSimpleConfig conf(dir+QString("%1.fltr").arg(i_d));
+
+ conf.setGroup("GENERAL");
+ conf.writeEntry("name", QString(n_ame));
+ conf.writeEntry("Translate_Name",translateName);
+ conf.writeEntry("enabled", e_nabled);
+ conf.writeEntry("applyOn", (int) apon);
+
+ conf.setGroup("STATUS");
+ status.save(&conf);
+
+ conf.setGroup("SCORE");
+ score.save(&conf);
+
+ conf.setGroup("AGE");
+ age.save(&conf);
+
+ conf.setGroup("LINES");
+ lines.save(&conf);
+
+ conf.setGroup("SUBJECT");
+ subject.save(&conf);
+
+ conf.setGroup("FROM");
+ from.save(&conf);
+
+ conf.setGroup("MESSAGEID");
+ messageId.save(&conf);
+
+ conf.setGroup("REFERENCES");
+ references.save(&conf);
+
+ kdDebug(5003) << "KNMessageFilter: filter saved \"" << n_ame << "\" " << endl;
+}
+
+
+
+void KNArticleFilter::doFilter(KNGroup *g)
+{
+ c_ount=0;
+ KNRemoteArticle *art=0, *ref=0;
+ KNRemoteArticle::List orphant_threads;
+ int idRef;
+ int mergeCnt=0;
+ bool inThread=false;
+
+ if(!l_oaded) load();
+
+ subject.expand(g); // replace placeholders
+ from.expand(g);
+ messageId.expand(g);
+ references.expand(g);
+
+ for(int idx=0; idx<g->length(); idx++) {
+ art=g->at(idx);
+ art->setFiltered(false);
+ art->setVisibleFollowUps(false);
+ art->setDisplayedReference(0);
+ }
+
+ for(int idx=0; idx<g->length(); idx++) {
+
+ art=g->at(idx);
+
+ if(!art->isFiltered() && applyFilter(art) && apon==threads) {
+ idRef=art->idRef();
+ while(idRef!=0) {
+ ref=g->byId(idRef);
+ ref->setFilterResult(true);
+ ref->setFiltered(true);
+ if ( idRef==ref->idRef() ) break;
+ idRef=ref->idRef();
+ }
+ }
+
+ }
+
+ for(int idx=0; idx<g->length(); idx++) {
+
+ art=g->at(idx);
+
+ if( apon==threads && !art->filterResult() ) {
+ inThread=false;
+ idRef=art->idRef();
+ while(idRef!=0 && !inThread) {
+ ref=g->byId(idRef);
+ inThread=ref->filterResult();
+ idRef=ref->idRef();
+ }
+ art->setFilterResult(inThread);
+ }
+
+ if(art->filterResult()) {
+ c_ount++;
+
+ ref = (art->idRef()>0) ? g->byId(art->idRef()) : 0;
+ while(ref && !ref->filterResult())
+ ref = (ref->idRef()>0) ? g->byId(ref->idRef()) : 0;
+
+ art->setDisplayedReference(ref);
+ if(ref)
+ ref->setVisibleFollowUps(true);
+ else if(art->idRef()>0) {
+ orphant_threads.append(art);
+ }
+ }
+
+ }
+
+ if( orphant_threads.count() > 0 ) {
+ // try to merge orphant threads by subject
+ KNRemoteArticle::List same_subjects;
+ QString s;
+ for ( KNRemoteArticle::List::Iterator it = orphant_threads.begin(); it != orphant_threads.end(); ++it ) {
+ if ( (*it)->displayedReference() ) // already processed
+ continue;
+
+ s = (*it)->subject()->asUnicodeString();
+ same_subjects.clear();
+ for ( KNRemoteArticle::List::Iterator it2 = orphant_threads.begin(); it2 != orphant_threads.end(); ++it2 ) {
+ if ( (*it2) != (*it) && (*it2)->subject()->asUnicodeString() == s )
+ same_subjects.append( (*it2) );
+ }
+
+ (*it)->setVisibleFollowUps( (*it)->hasVisibleFollowUps() || same_subjects.count() > 0 );
+ for ( KNRemoteArticle::List::Iterator it2 = same_subjects.begin(); it2 != same_subjects.end(); ++it2 ) {
+ (*it2)->setDisplayedReference( (*it) );
+ mergeCnt++;
+ }
+ }
+ }
+
+ kdDebug(5003) << "KNArticleFilter::doFilter() : matched " << c_ount
+ << " articles , merged " << mergeCnt
+ << " threads by subject" << endl;
+
+}
+
+
+void KNArticleFilter::doFilter(KNFolder *f)
+{
+ c_ount=0;
+ KNLocalArticle *art=0;
+
+ if(!l_oaded) load();
+
+ subject.expand(0); // replace placeholders
+ from.expand(0);
+ messageId.expand(0);
+ references.expand(0);
+
+ for(int idx=0; idx<f->length(); idx++) {
+ art=f->at(idx);
+ if (applyFilter(art))
+ c_ount++;
+ }
+}
+
+
+// *tries* to translate the name
+QString KNArticleFilter::translatedName()
+{
+ if (translateName) {
+ // major hack alert !!!
+ if (!n_ame.isEmpty()) {
+ if (i18n("default filter name",n_ame.local8Bit())!=n_ame.local8Bit().data()) // try to guess if this english or not
+ return i18n("default filter name",n_ame.local8Bit());
+ else
+ return n_ame;
+ } else
+ return QString::null;
+ } else
+ return n_ame;
+}
+
+
+
+// *tries* to retranslate the name to english
+void KNArticleFilter::setTranslatedName(const QString &s)
+{
+ bool retranslated = false;
+ for (const char **c=defFil;(*c)!=0;c++) // ok, try if it matches any of the standard filter names
+ if (s==i18n("default filter name",*c)) {
+ n_ame = QString::fromLatin1(*c);
+ retranslated = true;
+ break;
+ }
+
+ if (!retranslated) { // ok, we give up and store the maybe non-english string
+ n_ame = s;
+ translateName = false; // and don't try to translate it, so a german user *can* use the original english name
+ } else
+ translateName = true;
+}
+
+
+
+bool KNArticleFilter::applyFilter(KNRemoteArticle *a)
+{
+ bool result=true;
+
+ if(result) result=status.doFilter(a);
+ if(result) result=score.doFilter(a->score());
+ if(result) result=lines.doFilter(a->lines()->numberOfLines());
+ if(result) result=age.doFilter(a->date()->ageInDays());
+ if(result) result=subject.doFilter(a->subject()->asUnicodeString());
+ if(result) {
+ QString tmp = (a->from()->name()+"##") + QString(a->from()->email().data());
+ result=from.doFilter(tmp);
+ }
+ if(result) result=messageId.doFilter(a->messageID()->asUnicodeString());
+ if(result) result=references.doFilter(a->references()->asUnicodeString());
+
+ a->setFilterResult(result);
+ a->setFiltered(true);
+
+ return result;
+}
+
+
+bool KNArticleFilter::applyFilter(KNLocalArticle *a)
+{
+ bool result=true;
+
+ if (isSearchFilter()) {
+ if(result) result=lines.doFilter(a->lines()->numberOfLines());
+ if(result) result=age.doFilter(a->date()->ageInDays());
+ if(result) result=subject.doFilter(a->subject()->asUnicodeString());
+ if(result) {
+ QString tmp = (a->from()->name()+"##") + QString(a->from()->email().data());
+ result=from.doFilter(tmp);
+ }
+ if(result) result=messageId.doFilter(a->messageID()->asUnicodeString());
+ if(result) result=references.doFilter(a->references()->asUnicodeString());
+ }
+
+ a->setFilterResult(result);
+
+ return result;
+}
diff --git a/knode/knarticlefilter.h b/knode/knarticlefilter.h
new file mode 100644
index 000000000..6d544342d
--- /dev/null
+++ b/knode/knarticlefilter.h
@@ -0,0 +1,80 @@
+/*
+ knarticlefilter.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNARTICLEFILTER_H
+#define KNARTICLEFILTER_H
+
+#include "knstatusfilter.h"
+#include "knrangefilter.h"
+#include "knstringfilter.h"
+
+class KNRemoteArticle;
+class KNLocalArticle;
+class KNGroup;
+class KNFolder;
+
+
+class KNArticleFilter {
+
+ friend class KNFilterManager;
+ friend class KNFilterDialog;
+ friend class KNSearchDialog;
+
+ public:
+ KNArticleFilter(int id=-1);
+ KNArticleFilter(const KNArticleFilter& org); // constructs a copy of org
+ ~KNArticleFilter();
+
+ bool loadInfo();
+ void load();
+ void save();
+
+ void doFilter(KNGroup *g);
+ void doFilter(KNFolder *f);
+ int count()const { return c_ount; }
+ int id()const { return i_d; }
+ int applyOn() { return static_cast<int>(apon); }
+ const QString& name() { return n_ame; }
+ QString translatedName(); // *tries* to translate the name
+ bool isEnabled()const { return e_nabled; }
+ bool loaded()const { return l_oaded; }
+ bool isSearchFilter()const { return s_earchFilter; }
+
+ void setId(int i) { i_d=i; }
+ void setApplyOn(int i) { apon=(ApOn)i; }
+ void setLoaded(bool l) { l_oaded=l; }
+ void setName(const QString &s) { n_ame=s; }
+ void setTranslatedName(const QString &s); // *tries* to retranslate the name to english
+ void setEnabled(bool l) { e_nabled=l; }
+ void setSearchFilter(bool b) { s_earchFilter = b; }
+
+ protected:
+
+ enum ApOn { articles=0 , threads=1 };
+ bool applyFilter(KNRemoteArticle *a);
+ bool applyFilter(KNLocalArticle *a);
+
+ QString n_ame;
+ int i_d, c_ount;
+ bool l_oaded, e_nabled, translateName, s_earchFilter;
+ ApOn apon;
+
+ KNStatusFilter status;
+ KNRangeFilter score, age, lines;
+ KNStringFilter subject, from, messageId, references;
+};
+
+#endif
diff --git a/knode/knarticlemanager.cpp b/knode/knarticlemanager.cpp
new file mode 100644
index 000000000..4f1786e8c
--- /dev/null
+++ b/knode/knarticlemanager.cpp
@@ -0,0 +1,1090 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <kmessagebox.h>
+#include <kuserprofile.h>
+#include <kopenwith.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kwin.h>
+#include <ktempfile.h>
+
+#include "articlewidget.h"
+#include "knmainwidget.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "utilities.h"
+#include "knarticlemanager.h"
+#include "kngroupmanager.h"
+#include "knsearchdialog.h"
+#include "knfiltermanager.h"
+#include "knfolder.h"
+#include "knarticlefilter.h"
+#include "knhdrviewitem.h"
+#include "knnetaccess.h"
+#include "knnntpaccount.h"
+#include "knscoring.h"
+#include "knmemorymanager.h"
+#include "knarticlefactory.h"
+#include "knarticlewindow.h"
+#include "knfoldermanager.h"
+#include "headerview.h"
+
+using namespace KNode;
+
+
+KNArticleManager::KNArticleManager() : QObject(0,0)
+{
+ g_roup=0;
+ f_older=0;
+ f_ilterMgr = knGlobals.filterManager();
+ f_ilter = f_ilterMgr->currentFilter();
+ s_earchDlg=0;
+ d_isableExpander=false;
+
+ connect(f_ilterMgr, SIGNAL(filterChanged(KNArticleFilter*)), this,
+ SLOT(slotFilterChanged(KNArticleFilter*)));
+}
+
+
+KNArticleManager::~KNArticleManager()
+{
+ delete s_earchDlg;
+}
+
+
+void KNArticleManager::deleteTempFiles()
+{
+ for ( QValueList<KTempFile*>::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it ) {
+ (*it)->unlink();
+ delete (*it);
+ }
+ mTempFiles.clear();
+}
+
+
+void KNArticleManager::saveContentToFile(KMime::Content *c, QWidget *parent)
+{
+ KNSaveHelper helper(c->contentType()->name(),parent);
+
+ QFile *file = helper.getFile(i18n("Save Attachment"));
+
+ if (file) {
+ QByteArray data=c->decodedContent();
+ if (file->writeBlock(data.data(), data.size()) == -1 )
+ KNHelper::displayExternalFileError( parent );
+ }
+}
+
+
+void KNArticleManager::saveArticleToFile(KNArticle *a, QWidget *parent)
+{
+ QString fName = a->subject()->asUnicodeString();
+ QString s = "";
+
+ for (unsigned int i=0; i<fName.length(); i++)
+ if (fName[i].isLetterOrNumber())
+ s.append(fName[i]);
+ else
+ s.append(' ');
+ fName = s.simplifyWhiteSpace();
+ fName.replace(QRegExp("[\\s]"),"_");
+
+ KNSaveHelper helper(fName,parent);
+ QFile *file = helper.getFile(i18n("Save Article"));
+
+ if (file) {
+ QCString tmp=a->encodedContent(false);
+ if ( file->writeBlock(tmp.data(), tmp.size()) == -1 )
+ KNHelper::displayExternalFileError( parent );
+ }
+}
+
+
+QString KNArticleManager::saveContentToTemp(KMime::Content *c)
+{
+ QString path;
+ KTempFile* tmpFile;
+ KMime::Headers::Base *pathHdr=c->getHeaderByType("X-KNode-Tempfile"); // check for existing temp file
+
+ if(pathHdr) {
+ path = pathHdr->asUnicodeString();
+ bool found=false;
+
+ // lets see if the tempfile-path is still valid...
+ for ( QValueList<KTempFile*>::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it ) {
+ if ( (*it)->name() == path ) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ return path;
+ else
+ c->removeHeader("X-KNode-Tempfile");
+ }
+
+ tmpFile=new KTempFile();
+ if (tmpFile->status()!=0) {
+ KNHelper::displayTempFileError();
+ delete tmpFile;
+ return QString::null;
+ }
+
+ mTempFiles.append(tmpFile);
+ QFile *f=tmpFile->file();
+ QByteArray data=c->decodedContent();
+ f->writeBlock(data.data(), data.size());
+ tmpFile->close();
+ path=tmpFile->name();
+ pathHdr=new KMime::Headers::Generic("X-KNode-Tempfile", c, path, "UTF-8");
+ c->setHeader(pathHdr);
+
+ return path;
+}
+
+
+void KNArticleManager::openContent(KMime::Content *c)
+{
+ QString path=saveContentToTemp(c);
+ if(path.isNull()) return;
+
+ KService::Ptr offer = KServiceTypeProfile::preferredService(c->contentType()->mimeType(), "Application");
+ KURL::List lst;
+ KURL url;
+ url.setPath(path);
+ lst.append(url);
+
+ if (offer)
+ KRun::run(*offer, lst);
+ else
+ KRun::displayOpenWithDialog(lst);
+}
+
+
+void KNArticleManager::showHdrs(bool clear)
+{
+ if(!g_roup && !f_older) return;
+
+ bool setFirstChild=true;
+ bool showThreads=knGlobals.configManager()->readNewsGeneral()->showThreads();
+ bool expandThreads=knGlobals.configManager()->readNewsGeneral()->defaultToExpandedThreads();
+
+ if(clear)
+ v_iew->clear();
+
+ knGlobals.top->setCursorBusy(true);
+ knGlobals.setStatusMsg(i18n(" Creating list..."));
+ knGlobals.top->secureProcessEvents();
+
+ if(g_roup) {
+ KNRemoteArticle *art, *ref, *current;
+
+ current = static_cast<KNRemoteArticle*>( knGlobals.top->articleViewer()->article() );
+
+ if(current && (current->collection() != g_roup)) {
+ current=0;
+ knGlobals.top->articleViewer()->setArticle( 0 );
+ }
+
+ if(g_roup->isLocked())
+ knGlobals.netAccess()->nntpMutex().lock();
+
+ if(f_ilter)
+ f_ilter->doFilter(g_roup);
+ else
+ for(int i=0; i<g_roup->length(); i++) {
+ art=g_roup->at(i);
+ art->setFilterResult(true);
+ art->setFiltered(true);
+ ref=(art->idRef()!=0) ? g_roup->byId(art->idRef()) : 0;
+ art->setDisplayedReference(ref);
+ if(ref)
+ ref->setVisibleFollowUps(true);
+ }
+
+ d_isableExpander=true;
+
+ for(int i=0; i<g_roup->length(); i++) {
+
+ art=g_roup->at(i);
+ art->setThreadMode(showThreads);
+
+ if(showThreads) {
+ art->propagateThreadChangedDate();
+
+ if( !art->listItem() && art->filterResult() ) {
+
+ // ### disable delayed header view item creation for now, it breaks
+ // the quick search
+ // since it doesn't seem to improve performance at all, it probably
+ // could be removed entirely (see also slotItemExpanded(), etc.)
+ /*if (!expandThreads) {
+
+ if( (ref=art->displayedReference()) ) {
+
+ if( ref->listItem() && ( ref->listItem()->isOpen() || ref->listItem()->childCount()>0 ) ) {
+ art->setListItem(new KNHdrViewItem(ref->listItem()));
+ art->initListItem();
+ }
+
+ }
+ else {
+ art->setListItem(new KNHdrViewItem(v_iew));
+ art->initListItem();
+ }
+
+ } else { // expandThreads == true */
+ createThread(art);
+ if ( expandThreads )
+ art->listItem()->setOpen(true);
+// }
+
+ }
+ else if(art->listItem()) {
+ art->updateListItem();
+ if (expandThreads)
+ art->listItem()->setOpen(true);
+ }
+
+ }
+ else {
+
+ if(!art->listItem() && art->filterResult()) {
+ art->setListItem(new KNHdrViewItem(v_iew));
+ art->initListItem();
+ } else if(art->listItem())
+ art->updateListItem();
+
+ }
+
+ }
+
+ if (current && !current->filterResult()) { // try to find a parent that is visible
+ int idRef;
+ while (current && !current->filterResult()) {
+ idRef=current->idRef();
+ if (idRef == -1)
+ break;
+ current = g_roup->byId(idRef);
+ }
+ }
+
+ if(current && current->filterResult()) {
+ if(!current->listItem())
+ createCompleteThread(current);
+ v_iew->setActive( current->listItem() );
+ setFirstChild=false;
+ }
+
+ d_isableExpander=false;
+
+ if (g_roup->isLocked())
+ knGlobals.netAccess()->nntpMutex().unlock();
+ }
+
+ else { //folder
+
+ KNLocalArticle *art;
+ if(f_ilter) {
+ f_ilter->doFilter(f_older);
+ } else {
+ for(int i=0; i<f_older->length(); i++) {
+ art=f_older->at(i);
+ art->setFilterResult(true);
+ }
+ }
+
+ for(int idx=0; idx<f_older->length(); idx++) {
+ art=f_older->at(idx);
+
+ if(!art->listItem() && art->filterResult()) {
+ art->setListItem( new KNHdrViewItem(v_iew, art) );
+ art->updateListItem();
+ } else if(art->listItem())
+ art->updateListItem();
+ }
+
+ }
+
+ if(setFirstChild && v_iew->firstChild()) {
+ v_iew->setCurrentItem(v_iew->firstChild());
+ knGlobals.top->articleViewer()->setArticle( 0 );
+ }
+
+ knGlobals.setStatusMsg(QString::null);
+ updateStatusString();
+ knGlobals.top->setCursorBusy(false);
+}
+
+
+void KNArticleManager::updateViewForCollection(KNArticleCollection *c)
+{
+ if(g_roup==c || f_older==c)
+ showHdrs(false);
+}
+
+
+void KNArticleManager::updateListViewItems()
+{
+ if(!g_roup && !f_older) return;
+
+ if(g_roup) {
+ KNRemoteArticle *art;
+
+ for(int i=0; i<g_roup->length(); i++) {
+ art=g_roup->at(i);
+ if(art->listItem())
+ art->updateListItem();
+ }
+ } else { //folder
+ KNLocalArticle *art;
+
+ for(int idx=0; idx<f_older->length(); idx++) {
+ art=f_older->at(idx);
+ if(art->listItem())
+ art->updateListItem();
+ }
+ }
+}
+
+
+void KNArticleManager::setAllThreadsOpen(bool b)
+{
+ KNRemoteArticle *art;
+ if(g_roup) {
+ knGlobals.top->setCursorBusy(true);
+ d_isableExpander = true;
+ for(int idx=0; idx<g_roup->length(); idx++) {
+ art = g_roup->at(idx);
+ if (art->listItem())
+ art->listItem()->setOpen(b);
+ else
+ if (b && art->filterResult()) {
+ createThread(art);
+ art->listItem()->setOpen(true);
+ }
+ }
+ d_isableExpander = false;
+ knGlobals.top->setCursorBusy(false);
+ }
+}
+
+
+void KNArticleManager::search()
+{
+ if(s_earchDlg) {
+ s_earchDlg->show();
+ KWin::activateWindow(s_earchDlg->winId());
+ } else {
+ s_earchDlg=new KNSearchDialog(KNSearchDialog::STgroupSearch, 0);
+ connect(s_earchDlg, SIGNAL(doSearch(KNArticleFilter*)), this,
+ SLOT(slotFilterChanged(KNArticleFilter*)));
+ connect(s_earchDlg, SIGNAL(dialogDone()), this,
+ SLOT(slotSearchDialogDone()));
+ s_earchDlg->show();
+ }
+}
+
+
+void KNArticleManager::setGroup(KNGroup *g)
+{
+ g_roup = g;
+ if ( g )
+ emit aboutToShowGroup();
+}
+
+
+void KNArticleManager::setFolder(KNFolder *f)
+{
+ f_older = f;
+ if ( f )
+ emit aboutToShowFolder();
+}
+
+
+KNArticleCollection* KNArticleManager::collection()
+{
+ if(g_roup)
+ return g_roup;
+ if(f_older)
+ return f_older;
+
+ return 0;
+}
+
+
+bool KNArticleManager::loadArticle(KNArticle *a)
+{
+ if (!a)
+ return false;
+
+ if (a->hasContent())
+ return true;
+
+ if (a->isLocked()) {
+ if (a->type()==KMime::Base::ATremote)
+ return true; // locked == we are already loading this article...
+ else
+ return false;
+ }
+
+ if(a->type()==KMime::Base::ATremote) {
+ KNGroup *g=static_cast<KNGroup*>(a->collection());
+ if(g)
+ emitJob( new KNJobData(KNJobData::JTfetchArticle, this, g->account(), a) );
+ else
+ return false;
+ }
+ else { // local article
+ KNFolder *f=static_cast<KNFolder*>(a->collection());
+ if( f && f->loadArticle( static_cast<KNLocalArticle*>(a) ) )
+ knGlobals.memoryManager()->updateCacheEntry(a);
+ else
+ return false;
+ }
+ return true;
+}
+
+
+bool KNArticleManager::unloadArticle(KNArticle *a, bool force)
+{
+ if(!a || a->isLocked() )
+ return false;
+ if(!a->hasContent())
+ return true;
+
+ if (!force && a->isNotUnloadable())
+ return false;
+
+ if ( !force && ( ArticleWidget::articleVisible( a ) ) )
+ return false;
+
+ if (!force && (a->type()==KMime::Base::ATlocal) &&
+ (knGlobals.artFactory->findComposer(static_cast<KNLocalArticle*>(a))!=0))
+ return false;
+
+ if (!KNArticleWindow::closeAllWindowsForArticle(a, force))
+ if (!force)
+ return false;
+
+ ArticleWidget::articleRemoved( a );
+ if ( a->type() != KMime::Base::ATlocal )
+ knGlobals.artFactory->deleteComposerForArticle(static_cast<KNLocalArticle*>(a));
+ a->KMime::Content::clear();
+ a->updateListItem();
+ knGlobals.memoryManager()->removeCacheEntry(a);
+
+ return true;
+}
+
+
+void KNArticleManager::copyIntoFolder(KNArticle::List &l, KNFolder *f)
+{
+ if(!f) return;
+
+ KNLocalArticle *loc;
+ KNLocalArticle::List l2;
+
+ for ( KNArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
+ if ( !(*it)->hasContent() )
+ continue;
+ loc=new KNLocalArticle(0);
+ loc->setEditDisabled(true);
+ loc->setContent( (*it)->encodedContent() );
+ loc->parse();
+ l2.append(loc);
+ }
+
+ if ( !l2.isEmpty() ) {
+
+ f->setNotUnloadable(true);
+
+ if ( !f->isLoaded() && !knGlobals.folderManager()->loadHeaders( f ) ) {
+ for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it )
+ delete (*it);
+ l2.clear();
+ f->setNotUnloadable(false);
+ return;
+ }
+
+ if( !f->saveArticles( l2 ) ) {
+ for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it ) {
+ if ( (*it)->isOrphant() )
+ delete (*it); // ok, this is ugly; we simply delete orphant articles
+ else
+ (*it)->KMime::Content::clear(); // no need to keep them in memory
+ }
+ KNHelper::displayInternalFileError();
+ } else {
+ for ( KNLocalArticle::List::Iterator it = l2.begin(); it != l2.end(); ++it )
+ (*it)->KMime::Content::clear(); // no need to keep them in memory
+ knGlobals.memoryManager()->updateCacheEntry(f);
+ }
+
+ f->setNotUnloadable(false);
+ }
+}
+
+
+void KNArticleManager::moveIntoFolder(KNLocalArticle::List &l, KNFolder *f)
+{
+ if(!f) return;
+ kdDebug(5003) << k_funcinfo << " Target folder: " << f->name() << endl;
+
+ f->setNotUnloadable(true);
+
+ if (!f->isLoaded() && !knGlobals.folderManager()->loadHeaders(f)) {
+ f->setNotUnloadable(false);
+ return;
+ }
+
+ if ( f->saveArticles( l ) ) {
+ for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
+ knGlobals.memoryManager()->updateCacheEntry( (*it) );
+ knGlobals.memoryManager()->updateCacheEntry(f);
+ } else {
+ for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
+ if ( (*it)->isOrphant() )
+ delete (*it); // ok, this is ugly; we simply delete orphant articles
+ KNHelper::displayInternalFileError();
+ }
+
+ f->setNotUnloadable(false);
+}
+
+
+bool KNArticleManager::deleteArticles(KNLocalArticle::List &l, bool ask)
+{
+ if(ask) {
+ QStringList lst;
+ for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
+ if ( (*it)->isLocked() )
+ continue;
+ if ( (*it)->subject()->isEmpty() )
+ lst << i18n("no subject");
+ else
+ lst << (*it)->subject()->asUnicodeString();
+ }
+ if( KMessageBox::Cancel == KMessageBox::warningContinueCancelList(
+ knGlobals.topWidget, i18n("Do you really want to delete these articles?"), lst,
+ i18n("Delete Articles"), KGuiItem(i18n("&Delete"),"editdelete")) )
+ return false;
+ }
+
+ for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
+ knGlobals.memoryManager()->removeCacheEntry( (*it) );
+
+ KNFolder *f=static_cast<KNFolder*>(l.first()->collection());
+ if ( f ) {
+ f->removeArticles( l, true );
+ knGlobals.memoryManager()->updateCacheEntry( f );
+ }
+ else {
+ for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it )
+ delete (*it);
+ }
+
+ return true;
+}
+
+
+void KNArticleManager::setAllRead( bool read, int lastcount )
+{
+ if ( !g_roup )
+ return;
+
+ int groupLength = g_roup->length();
+ int newCount = g_roup->newCount();
+ int readCount = g_roup->readCount();
+ int offset = lastcount;
+
+ if ( lastcount > groupLength || lastcount < 0 )
+ offset = groupLength;
+
+ KNRemoteArticle *a;
+ for ( int i = groupLength - offset; i < groupLength; i++ ) {
+ a = g_roup->at( i );
+ if ( a->getReadFlag() != read && !a->isIgnored() ) {
+ a->setRead( read );
+ a->setChanged( true );
+ if ( !read ) {
+ readCount--;
+ if ( a->isNew() )
+ newCount++;
+ } else {
+ readCount++;
+ if ( a->isNew() )
+ newCount--;
+ }
+ }
+ }
+
+ g_roup->updateThreadInfo();
+ if ( lastcount < 0 && read ) {
+ // HACK: try to hide the effects of the ignore/filter new/unread count bug
+ g_roup->setReadCount( groupLength );
+ g_roup->setNewCount( 0 );
+ } else {
+ g_roup->setReadCount( readCount );
+ g_roup->setNewCount( newCount );
+ }
+
+ g_roup->updateListItem();
+ showHdrs( true );
+}
+
+
+void KNArticleManager::setRead(KNRemoteArticle::List &l, bool r, bool handleXPosts)
+{
+ if ( l.isEmpty() )
+ return;
+
+ KNRemoteArticle *ref = 0;
+ KNGroup *g=static_cast<KNGroup*>( l.first()->collection() );
+ int changeCnt=0, idRef=0;
+
+ for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
+ if( r && knGlobals.configManager()->readNewsGeneral()->markCrossposts() &&
+ handleXPosts && (*it)->newsgroups()->isCrossposted() ) {
+
+ QStringList groups = (*it)->newsgroups()->getGroups();
+ KNGroup *targetGroup=0;
+ KNRemoteArticle *xp=0;
+ KNRemoteArticle::List al;
+ QCString mid = (*it)->messageID()->as7BitString( false );
+
+ for ( QStringList::Iterator it2 = groups.begin(); it2 != groups.end(); ++it2 ) {
+ targetGroup = knGlobals.groupManager()->group(*it2, g->account());
+ if (targetGroup) {
+ if (targetGroup->isLoaded() && (xp=targetGroup->byMessageId(mid)) ) {
+ al.clear();
+ al.append(xp);
+ setRead(al, r, false);
+ } else {
+ targetGroup->appendXPostID(mid);
+ }
+ }
+ }
+ }
+
+ else if ( (*it)->getReadFlag() != r ) {
+ (*it)->setRead( r );
+ (*it)->setChanged( true );
+ (*it)->updateListItem();
+
+ if ( !(*it)->isIgnored() ) {
+ changeCnt++;
+ idRef = (*it)->idRef();
+
+ while ( idRef != 0 ) {
+ ref=g->byId(idRef);
+ if(r) {
+ ref->decUnreadFollowUps();
+ if ( (*it)->isNew() )
+ ref->decNewFollowUps();
+ }
+ else {
+ ref->incUnreadFollowUps();
+ if ( (*it)->isNew() )
+ ref->incNewFollowUps();
+ }
+
+ if(ref->listItem() &&
+ ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
+ (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
+ ref->updateListItem();
+
+ idRef=ref->idRef();
+ }
+
+ if(r) {
+ g->incReadCount();
+ if ( (*it)->isNew() )
+ g->decNewCount();
+ }
+ else {
+ g->decReadCount();
+ if ( (*it)->isNew() )
+ g->incNewCount();
+ }
+ }
+ }
+ }
+
+ if(changeCnt>0) {
+ g->updateListItem();
+ if(g==g_roup)
+ updateStatusString();
+ }
+}
+
+
+void KNArticleManager::setAllNotNew()
+{
+ if ( !g_roup )
+ return;
+ KNRemoteArticle *a;
+ for ( int i = 0; i < g_roup->length(); ++i) {
+ a = g_roup->at(i);
+ if ( a->isNew() ) {
+ a->setNew( false );
+ a->setChanged( true );
+ }
+ }
+ g_roup->setFirstNewIndex( -1 );
+ g_roup->setNewCount( 0 );
+ g_roup->updateThreadInfo();
+}
+
+
+bool KNArticleManager::toggleWatched(KNRemoteArticle::List &l)
+{
+ if(l.isEmpty())
+ return true;
+
+ KNRemoteArticle *a=l.first(), *ref=0;
+ bool watch = (!a->isWatched());
+ KNGroup *g=static_cast<KNGroup*>(a->collection() );
+ int changeCnt=0, idRef=0;
+
+ for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
+ if ( (*it)->isIgnored() ) {
+ (*it)->setIgnored(false);
+
+ if ( !(*it)->getReadFlag() ) {
+ changeCnt++;
+ idRef = (*it)->idRef();
+
+ while ( idRef != 0 ) {
+ ref=g->byId(idRef);
+
+ ref->incUnreadFollowUps();
+ if ( (*it)->isNew() )
+ ref->incNewFollowUps();
+
+ if(ref->listItem() &&
+ ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
+ (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
+ ref->updateListItem();
+
+ idRef=ref->idRef();
+ }
+ g->decReadCount();
+ if ( (*it)->isNew() )
+ g->incNewCount();
+ }
+ }
+
+ (*it)->setWatched( watch );
+ (*it)->updateListItem();
+ (*it)->setChanged( true );
+ }
+
+ if(changeCnt>0) {
+ g->updateListItem();
+ if(g==g_roup)
+ updateStatusString();
+ }
+
+ return watch;
+}
+
+
+bool KNArticleManager::toggleIgnored(KNRemoteArticle::List &l)
+{
+ if(l.isEmpty())
+ return true;
+
+ KNRemoteArticle *ref = 0;
+ bool ignore = !l.first()->isIgnored();
+ KNGroup *g = static_cast<KNGroup*>( l.first()->collection() );
+ int changeCnt = 0, idRef = 0;
+
+ for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
+ (*it)->setWatched(false);
+ if ( (*it)->isIgnored() != ignore ) {
+ (*it)->setIgnored( ignore );
+
+ if ( !(*it)->getReadFlag() ) {
+ changeCnt++;
+ idRef = (*it)->idRef();
+
+ while ( idRef != 0 ) {
+ ref = g->byId( idRef );
+
+ if ( ignore ) {
+ ref->decUnreadFollowUps();
+ if ( (*it)->isNew() )
+ ref->decNewFollowUps();
+ } else {
+ ref->incUnreadFollowUps();
+ if ( (*it)->isNew() )
+ ref->incNewFollowUps();
+ }
+
+ if(ref->listItem() &&
+ ((ref->unreadFollowUps()==0 || ref->unreadFollowUps()==1) ||
+ (ref->newFollowUps()==0 || ref->newFollowUps()==1)))
+ ref->updateListItem();
+
+ idRef=ref->idRef();
+ }
+
+ if ( ignore ) {
+ g->incReadCount();
+ if ( (*it)->isNew() )
+ g->decNewCount();
+ } else {
+ g->decReadCount();
+ if ( (*it)->isNew() )
+ g->incNewCount();
+ }
+
+ }
+ }
+ (*it)->updateListItem();
+ (*it)->setChanged(true);
+ }
+
+ if(changeCnt>0) {
+ g->updateListItem();
+ if(g==g_roup)
+ updateStatusString();
+ }
+
+ return ignore;
+}
+
+
+void KNArticleManager::rescoreArticles(KNRemoteArticle::List &l)
+{
+ if ( l.isEmpty() )
+ return;
+
+ KNGroup *g = static_cast<KNGroup*>( l.first()->collection() );
+ KScoringManager *sm = knGlobals.scoringManager();
+ sm->initCache(g->groupname());
+
+ for ( KNRemoteArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
+ int defScore = 0;
+ if ( (*it)->isIgnored())
+ defScore = knGlobals.configManager()->scoring()->ignoredThreshold();
+ else if ( (*it)->isWatched() )
+ defScore = knGlobals.configManager()->scoring()->watchedThreshold();
+ (*it)->setScore(defScore);
+
+ bool read = (*it)->isRead();
+
+ KNScorableArticle sa( (*it) );
+ sm->applyRules(sa);
+ (*it)->updateListItem();
+ (*it)->setChanged( true );
+
+ if ( !read && (*it)->isRead() != read )
+ g_roup->incReadCount();
+ }
+}
+
+
+void KNArticleManager::processJob(KNJobData *j)
+{
+ if(j->type()==KNJobData::JTfetchArticle && !j->canceled()) {
+ if(j->success()) {
+ KNRemoteArticle *a=static_cast<KNRemoteArticle*>(j->data());
+ ArticleWidget::articleChanged( a );
+ if(!a->isOrphant()) //orphant articles are deleted by the displaying widget
+ knGlobals.memoryManager()->updateCacheEntry(a);
+ if(a->listItem())
+ a->updateListItem();
+ }
+ else
+ ArticleWidget::articleLoadError(static_cast<KNRemoteArticle*>(j->data()), j->errorString());
+ }
+
+ delete j;
+}
+
+
+void KNArticleManager::createThread(KNRemoteArticle *a)
+{
+ KNRemoteArticle *ref=a->displayedReference();
+
+ if(ref) {
+ if(!ref->listItem())
+ createThread(ref);
+ a->setListItem(new KNHdrViewItem(ref->listItem()));
+ }
+ else
+ a->setListItem(new KNHdrViewItem(v_iew));
+
+ a->setThreadMode(knGlobals.configManager()->readNewsGeneral()->showThreads());
+ a->initListItem();
+}
+
+
+void KNArticleManager::createCompleteThread(KNRemoteArticle *a)
+{
+ KNRemoteArticle *ref=a->displayedReference(), *art, *top;
+ bool inThread=false;
+ bool showThreads=knGlobals.configManager()->readNewsGeneral()->showThreads();
+ KNConfig::ReadNewsGeneral *rng=knGlobals.configManager()->readNewsGeneral();
+
+ while (ref->displayedReference() != 0)
+ ref=ref->displayedReference();
+
+ top = ref;
+
+ if (!top->listItem()) // shouldn't happen
+ return;
+
+ for(int i=0; i<g_roup->count(); i++) {
+ art=g_roup->at(i);
+ if(art->filterResult() && !art->listItem()) {
+
+ if(art->displayedReference()==top) {
+ art->setListItem(new KNHdrViewItem(top->listItem()));
+ art->setThreadMode(showThreads);
+ art->initListItem();
+ }
+ else {
+ ref=art->displayedReference();
+ inThread=false;
+ while(ref && !inThread) {
+ inThread=(ref==top);
+ ref=ref->displayedReference();
+ }
+ if(inThread)
+ createThread(art);
+ }
+ }
+ }
+
+ if(rng->totalExpandThreads())
+ top->listItem()->expandChildren();
+}
+
+
+void KNArticleManager::updateStatusString()
+{
+ int displCnt=0;
+
+ if(g_roup) {
+ if(f_ilter)
+ displCnt=f_ilter->count();
+ else
+ displCnt=g_roup->count();
+
+ QString name = g_roup->name();
+ if (g_roup->status()==KNGroup::moderated)
+ name += i18n(" (moderated)");
+
+ knGlobals.setStatusMsg(i18n(" %1: %2 new , %3 displayed")
+ .arg(name).arg(g_roup->newCount()).arg(displCnt),SB_GROUP);
+
+ if(f_ilter)
+ knGlobals.setStatusMsg(i18n(" Filter: %1").arg(f_ilter->translatedName()), SB_FILTER);
+ else
+ knGlobals.setStatusMsg(QString::null, SB_FILTER);
+ }
+ else if(f_older) {
+ if(f_ilter)
+ displCnt=f_ilter->count();
+ else
+ displCnt=f_older->count();
+ knGlobals.setStatusMsg(i18n(" %1: %2 displayed")
+ .arg(f_older->name()).arg(displCnt), SB_GROUP);
+ knGlobals.setStatusMsg(QString::null, SB_FILTER);
+ } else {
+ knGlobals.setStatusMsg(QString::null, SB_GROUP);
+ knGlobals.setStatusMsg(QString::null, SB_FILTER);
+ }
+}
+
+
+void KNArticleManager::slotFilterChanged(KNArticleFilter *f)
+{
+ f_ilter=f;
+ showHdrs();
+}
+
+
+void KNArticleManager::slotSearchDialogDone()
+{
+ s_earchDlg->hide();
+ slotFilterChanged(f_ilterMgr->currentFilter());
+}
+
+
+void KNArticleManager::slotItemExpanded(QListViewItem *p)
+{
+ if (d_isableExpander) // we don't want to call this method recursively
+ return;
+ d_isableExpander = true;
+
+ KNRemoteArticle *top, *art, *ref;
+ KNHdrViewItem *hdrItem;
+ bool inThread=false;
+ bool showThreads=knGlobals.configManager()->readNewsGeneral()->showThreads();
+ KNConfig::ReadNewsGeneral *rng=knGlobals.configManager()->readNewsGeneral();
+ hdrItem=static_cast<KNHdrViewItem*>(p);
+ top=static_cast<KNRemoteArticle*>(hdrItem->art);
+
+ if (p->childCount() == 0) {
+
+ knGlobals.top->setCursorBusy(true);
+
+ for(int i=0; i<g_roup->count(); i++) {
+ art=g_roup->at(i);
+ if(art->filterResult() && !art->listItem()) {
+
+ if(art->displayedReference()==top) {
+ art->setListItem(new KNHdrViewItem(hdrItem));
+ art->setThreadMode(showThreads);
+ art->initListItem();
+ }
+ else if(rng->totalExpandThreads()) { //totalExpand
+ ref=art->displayedReference();
+ inThread=false;
+ while(ref && !inThread) {
+ inThread=(ref==top);
+ ref=ref->displayedReference();
+ }
+ if(inThread)
+ createThread(art);
+ }
+ }
+ }
+
+ knGlobals.top->setCursorBusy(false);
+ }
+
+ if(rng->totalExpandThreads())
+ hdrItem->expandChildren();
+
+ d_isableExpander = false;
+}
+
+
+void KNArticleManager::setView(KNHeaderView* v) {
+ v_iew = v;
+ if(v) {
+ connect(v, SIGNAL(expanded(QListViewItem*)), this,
+ SLOT(slotItemExpanded(QListViewItem*)));
+ }
+}
+
+//-----------------------------
+#include "knarticlemanager.moc"
diff --git a/knode/knarticlemanager.h b/knode/knarticlemanager.h
new file mode 100644
index 000000000..07335e1c1
--- /dev/null
+++ b/knode/knarticlemanager.h
@@ -0,0 +1,122 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNARTICLEMANAGER_H
+#define KNARTICLEMANAGER_H
+
+#include <qvaluelist.h>
+
+#include "knjobdata.h"
+#include "knarticle.h"
+
+class QListViewItem;
+
+class KTempFile;
+
+class KNArticle;
+class KNHeaderView;
+class KNThread;
+class KNArticleCollection;
+class KNGroup;
+class KNFolder;
+class KNArticleFilter;
+class KNFilterManager;
+class KNSearchDialog;
+class KNJobData;
+
+
+class KNArticleManager : public QObject, public KNJobConsumer {
+
+ Q_OBJECT
+
+ public:
+ KNArticleManager();
+ virtual ~KNArticleManager();
+
+ //content handling
+ void deleteTempFiles();
+ void saveContentToFile(KMime::Content *c, QWidget *parent);
+ void saveArticleToFile(KNArticle *a, QWidget *parent);
+ QString saveContentToTemp(KMime::Content *c);
+ void openContent(KMime::Content *c);
+
+ //listview handling
+ void showHdrs(bool clear=true);
+ void updateViewForCollection(KNArticleCollection *c);
+ void updateListViewItems();
+ void setAllThreadsOpen(bool b=true);
+
+ void updateStatusString();
+
+ //filter
+ KNArticleFilter* filter() const { return f_ilter; }
+ void search();
+
+ //collection handling
+ void setGroup(KNGroup *g);
+ void setFolder(KNFolder *f);
+ KNArticleCollection* collection();
+
+ //article loading
+ bool loadArticle(KNArticle *a);
+ bool unloadArticle(KNArticle *a, bool force=true);
+
+ //article storage
+ void copyIntoFolder(KNArticle::List &l, KNFolder *f);
+ void moveIntoFolder(KNLocalArticle::List &l, KNFolder *f);
+ bool deleteArticles(KNLocalArticle::List &l, bool ask=true);
+
+ //article handling
+ void setAllRead( bool read = true, int lastcount = -1 );
+ void setRead(KNRemoteArticle::List &l, bool r=true, bool handleXPosts=true);
+ /// mark all articles in the current group as not new
+ void setAllNotNew();
+
+ // returns false if the changes were reverted (i.e. ignored articles->neutral articles)
+ bool toggleWatched(KNRemoteArticle::List &l);
+ bool toggleIgnored(KNRemoteArticle::List &l);
+
+ void rescoreArticles(KNRemoteArticle::List &l);
+
+ // Allow to delay the setup of UI elements, since the knode part may not
+ // be available when the config dialog is called
+ void setView(KNHeaderView* v);
+
+ signals:
+ // signals for the header view to adapt to the upcoming content
+ void aboutToShowGroup();
+ void aboutToShowFolder();
+
+ protected:
+ void processJob(KNJobData *j);
+ void createThread(KNRemoteArticle *a);
+ void createCompleteThread(KNRemoteArticle *a);
+
+ KNHeaderView *v_iew;
+ KNGroup *g_roup;
+ KNFolder *f_older;
+ KNArticleFilter *f_ilter;
+ KNFilterManager *f_ilterMgr;
+ KNSearchDialog *s_earchDlg;
+ QValueList<KTempFile*> mTempFiles;
+ bool d_isableExpander;
+
+ public slots:
+ void slotFilterChanged(KNArticleFilter *f);
+ void slotSearchDialogDone();
+ void slotItemExpanded(QListViewItem *p);
+
+};
+
+#endif
diff --git a/knode/knarticlewindow.cpp b/knode/knarticlewindow.cpp
new file mode 100644
index 000000000..3e6898182
--- /dev/null
+++ b/knode/knarticlewindow.cpp
@@ -0,0 +1,131 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <kwin.h>
+
+#include <kstdaction.h>
+#include <kconfig.h>
+#include <kaccel.h>
+#include <kaction.h>
+
+#include "knarticle.h"
+#include "articlewidget.h"
+#include "utilities.h"
+#include "knglobals.h"
+#include "knmainwidget.h"
+#include "knarticlewindow.h"
+
+using namespace KNode;
+
+QValueList<KNArticleWindow*> KNArticleWindow::mInstances;
+
+
+bool KNArticleWindow::closeAllWindowsForCollection( KNArticleCollection *col, bool force )
+{
+ QValueList<KNArticleWindow*> list = mInstances;
+ for ( QValueList<KNArticleWindow*>::Iterator it = list.begin(); it != list.end(); ++it )
+ if ( (*it)->artW->article() && (*it)->artW->article()->collection() == col ) {
+ if ( force )
+ (*it)->close();
+ else
+ return false;
+ }
+ return true;
+}
+
+
+bool KNArticleWindow::closeAllWindowsForArticle( KNArticle *art, bool force )
+{
+ QValueList<KNArticleWindow*> list = mInstances;
+ for ( QValueList<KNArticleWindow*>::Iterator it = list.begin(); it != list.end(); ++it )
+ if ( (*it)->artW->article() && (*it)->artW->article() == art ) {
+ if ( force )
+ (*it)->close();
+ else
+ return false;
+ }
+ return true;
+}
+
+
+bool KNArticleWindow::raiseWindowForArticle( KNArticle *art )
+{
+ for ( QValueList<KNArticleWindow*>::Iterator it = mInstances.begin(); it != mInstances.end(); ++it )
+ if ( (*it)->artW->article() && (*it)->artW->article() == art ) {
+ KWin::activateWindow( (*it)->winId() );
+ return true;
+ }
+ return false;
+}
+
+
+bool KNArticleWindow::raiseWindowForArticle(const QCString &mid)
+{
+ for ( QValueList<KNArticleWindow*>::Iterator it = mInstances.begin(); it != mInstances.end(); ++it )
+ if ( (*it)->artW->article() && (*it)->artW->article()->messageID()->as7BitString( false ) == mid ) {
+ KWin::activateWindow( (*it)->winId() );
+ return true;
+ }
+
+ return false;
+}
+
+
+//==================================================================================
+
+KNArticleWindow::KNArticleWindow(KNArticle *art)
+ : KMainWindow(0, "articleWindow")
+{
+ if(knGlobals.instance)
+ setInstance(knGlobals.instance);
+
+ if(art)
+ setCaption(art->subject()->asUnicodeString());
+
+ artW = new ArticleWidget( this, this, actionCollection() );
+ artW->setArticle(art);
+ setCentralWidget(artW);
+
+ mInstances.append( this );
+
+ // file menu
+ KStdAction::close( this, SLOT(close()), actionCollection() );
+
+ // settings menu
+ KStdAction::preferences(knGlobals.top, SLOT(slotSettings()), actionCollection());
+
+ KAccel *accel = new KAccel( this );
+ artW->setCharsetKeyboardAction()->plugAccel( accel );
+
+ setupGUI( ToolBar|Keys|Create, "knreaderui.rc");
+
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("articleWindow_options");
+ resize(500,400); // default optimized for 800x600
+ applyMainWindowSettings(conf);
+}
+
+
+KNArticleWindow::~KNArticleWindow()
+{
+ mInstances.remove( this );
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("articleWindow_options");
+ saveMainWindowSettings(conf);
+}
+
+//--------------------------------
+
+#include "knarticlewindow.moc"
diff --git a/knode/knarticlewindow.h b/knode/knarticlewindow.h
new file mode 100644
index 000000000..224af1dfb
--- /dev/null
+++ b/knode/knarticlewindow.h
@@ -0,0 +1,47 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNARTICLEWINDOW_H
+#define KNARTICLEWINDOW_H
+
+#include <kmainwindow.h>
+
+class KNArticle;
+class KNArticleCollection;
+
+namespace KNode {
+ class ArticleWidget;
+}
+
+class KNArticleWindow : public KMainWindow {
+
+ Q_OBJECT
+
+ public:
+ KNArticleWindow(KNArticle *art);
+ ~KNArticleWindow();
+ KNode::ArticleWidget* artWidget()const { return artW; }
+
+ static bool closeAllWindowsForCollection(KNArticleCollection *col, bool force=true);
+ static bool closeAllWindowsForArticle(KNArticle *art, bool force=true);
+ static bool raiseWindowForArticle(KNArticle *art); // false: no window found
+ static bool raiseWindowForArticle(const QCString &mid);
+
+ protected:
+ KNode::ArticleWidget *artW;
+ static QValueList<KNArticleWindow*> mInstances;
+
+};
+
+#endif
diff --git a/knode/kncleanup.cpp b/knode/kncleanup.cpp
new file mode 100644
index 000000000..4998bd4c2
--- /dev/null
+++ b/knode/kncleanup.cpp
@@ -0,0 +1,319 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <stdlib.h>
+
+#include <qdir.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qprogressbar.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kseparator.h>
+#include <kapplication.h>
+#include <kdebug.h>
+
+#include "knfolder.h"
+#include "knglobals.h"
+#include "kncleanup.h"
+#include "knconfig.h"
+#include "knfoldermanager.h"
+#include "kngroupmanager.h"
+#include "knarticlemanager.h"
+#include "knnntpaccount.h"
+
+
+KNCleanUp::KNCleanUp() : d_lg(0)
+{
+}
+
+
+KNCleanUp::~KNCleanUp()
+{
+ delete d_lg;
+}
+
+
+void KNCleanUp::start()
+{
+ if ( mColList.isEmpty() )
+ return;
+
+ d_lg = new ProgressDialog( mColList.count() );
+ d_lg->show();
+
+ for ( QValueList<KNArticleCollection*>::Iterator it = mColList.begin(); it != mColList.end(); ++it ) {
+ if ( (*it)->type() == KNCollection::CTgroup ) {
+ d_lg->showMessage( i18n("Deleting expired articles in <b>%1</b>").arg( (*it)->name() ) );
+ kapp->processEvents();
+ expireGroup( static_cast<KNGroup*>( (*it) ) );
+ d_lg->doProgress();
+ }
+ else if ( (*it)->type() == KNCollection::CTfolder ) {
+ d_lg->showMessage( i18n("Compacting folder <b>%1</b>").arg( (*it)->name() ) );
+ kapp->processEvents();
+ compactFolder( static_cast<KNFolder*>( (*it) ) );
+ d_lg->doProgress();
+ }
+ }
+
+ delete d_lg;
+ d_lg=0;
+}
+
+
+void KNCleanUp::reset()
+{
+ mColList.clear();
+ if(d_lg) {
+ delete d_lg;
+ d_lg=0;
+ }
+}
+
+
+void KNCleanUp::expireGroup( KNGroup *g, bool showResult )
+{
+ int expDays=0, idRef=0, foundId=-1, delCnt=0, leftCnt=0, newCnt=0, firstArtNr=g->firstNr(), firstNew=-1;
+ bool unavailable=false;
+ KNRemoteArticle *art, *ref;
+
+ if (!g)
+ return;
+
+ KNConfig::Cleanup *conf = g->activeCleanupConfig();
+
+ g->setNotUnloadable(true);
+
+ if (!g->isLoaded() && !knGlobals.groupManager()->loadHeaders(g)) {
+ g->setNotUnloadable(false);
+ return;
+ }
+
+ //find all expired
+ for(int i=0; i<g->length(); i++) {
+ art=g->at(i);
+ if(art->isRead())
+ expDays = conf->maxAgeForRead();
+ else
+ expDays = conf->maxAgeForUnread();
+
+ unavailable = false;
+ if ((art->articleNumber() != -1) && conf->removeUnavailable())
+ unavailable = (art->articleNumber() < firstArtNr);
+
+ art->setExpired( (art->date()->ageInDays() >= expDays) || unavailable );
+ }
+
+ //save threads
+ if (conf->preserveThreads()) {
+ for(int i=0; i<g->length(); i++) {
+ art=g->at(i);
+ if(!art->isExpired()) {
+ idRef=art->idRef();
+ while(idRef!=0) {
+ ref=g->byId(idRef);
+ ref->setExpired(false);
+ idRef=ref->idRef();
+ }
+ }
+ }
+ }
+
+ //restore threading
+ for(int i=0; i<g->length(); i++) {
+ art=g->at(i);
+ if(!art->isExpired()) {
+ idRef=art->idRef();
+ foundId=0;
+ while(foundId==0 && idRef!=0) {
+ ref=g->byId(idRef);
+ if(!ref->isExpired()) foundId=ref->id();
+ idRef=ref->idRef();
+ }
+ art->setIdRef(foundId);
+ }
+ }
+
+ //delete expired
+ for(int i=0; i<g->length(); i++) {
+ art=g->at(i);
+ if(art->isExpired()) {
+ if(art->isRead())
+ g->decReadCount();
+ delCnt++;
+ if (art->hasContent())
+ knGlobals.articleManager()->unloadArticle(art, true);
+ }
+ else if(art->isNew() && !art->isRead()) {
+ if(firstNew==-1)
+ firstNew=i;
+ newCnt++;
+ }
+ }
+
+ g->setNotUnloadable(false);
+
+ if(delCnt>0) {
+ g->saveStaticData(g->length(), true);
+ g->saveDynamicData(g->length(), true);
+ g->decCount(delCnt);
+ g->setNewCount(newCnt);
+ g->setFirstNewIndex(firstNew);
+ g->saveInfo();
+ knGlobals.groupManager()->unloadHeaders(g, true);
+ }
+ else
+ g->syncDynamicData();
+
+ conf->setLastExpireDate();
+ g->saveInfo();
+ leftCnt=g->count();
+
+ kdDebug(5003) << "KNCleanUp::expireGroup() : " << g->groupname() << ": "
+ << delCnt << " deleted , " << leftCnt << " left" << endl;
+
+ if(showResult)
+ KMessageBox::information(knGlobals.topWidget,
+ i18n("<b>%1</b><br>expired: %2<br>left: %3").arg(g->groupname()).arg(delCnt).arg(leftCnt));
+}
+
+
+void KNCleanUp::compactFolder(KNFolder *f)
+{
+ KNLocalArticle *art;
+
+ if (!f)
+ return;
+
+ QDir dir(f->path());
+
+ if(!dir.exists())
+ return;
+
+ f->setNotUnloadable(true);
+
+ if (!f->isLoaded() && !knGlobals.folderManager()->loadHeaders(f)) {
+ f->setNotUnloadable(false);
+ return;
+ }
+
+ f->closeFiles();
+ QFileInfo info(f->m_boxFile);
+ QString oldName=info.fileName();
+ QString newName=oldName+".new";
+ KNFile newMBoxFile(info.dirPath(true)+"/"+newName);
+
+ if( (f->m_boxFile.open(IO_ReadOnly)) && (newMBoxFile.open(IO_WriteOnly)) ) {
+ QTextStream ts(&newMBoxFile);
+ ts.setEncoding(QTextStream::Latin1);
+ for(int idx=0; idx<f->length(); idx++) {
+ art=f->at(idx);
+ if(f->m_boxFile.at(art->startOffset())) {
+ ts << "From aaa@aaa Mon Jan 01 00:00:00 1997\n";
+ art->setStartOffset(newMBoxFile.at());
+ while(f->m_boxFile.at() < (uint)art->endOffset())
+ ts << f->m_boxFile.readLineWnewLine();
+ art->setEndOffset(newMBoxFile.at());
+ newMBoxFile.putch('\n');
+ }
+ }
+
+ f->syncIndex(true);
+ newMBoxFile.close();
+ f->closeFiles();
+
+ dir.remove(oldName);
+ dir.rename(newName, oldName);
+ }
+
+ f->setNotUnloadable(false);
+}
+
+
+//===============================================================================================
+
+
+KNCleanUp::ProgressDialog::ProgressDialog(int steps)
+ : QDialog(knGlobals.topWidget, 0, true)
+{
+ const int w=400,
+ h=160;
+
+ p_rogress=0;
+ s_teps=steps;
+
+ setCaption(kapp->makeStdCaption(i18n("Cleaning Up")));
+
+ setFixedSize(w,h);
+ QFrame *top=new QFrame(this);
+ top->setGeometry(0,0, w,h);
+
+ QVBoxLayout *topL=new QVBoxLayout(top, 10);
+
+ QLabel *l=new QLabel(i18n("Cleaning up. Please wait..."), top);
+ topL->addWidget(l);
+
+ KSeparator *sep=new KSeparator(top);
+ topL->addWidget(sep);
+
+ m_sg=new QLabel(top);
+ topL->addWidget(m_sg);
+
+ p_bar=new QProgressBar(top);
+ topL->addWidget(p_bar);
+ p_bar->setTotalSteps(100*s_teps);
+ p_bar->setProgress(1);
+
+
+ if(knGlobals.topWidget->isVisible()) {
+ int x, y;
+ x=(knGlobals.topWidget->width()-w)/2;
+ y=(knGlobals.topWidget->height()-h)/2;
+ if(x<0 || y<0) {
+ x=0;
+ y=0;
+ }
+ x+=knGlobals.topWidget->x();
+ y+=knGlobals.topWidget->y();
+ move(x,y);
+ }
+}
+
+
+KNCleanUp::ProgressDialog::~ProgressDialog()
+{
+}
+
+
+void KNCleanUp::ProgressDialog::showMessage(const QString &s)
+{
+ m_sg->setText(s);
+}
+
+
+void KNCleanUp::ProgressDialog::doProgress()
+{
+ p_rogress++;
+ p_bar->setProgress(p_rogress*100);
+}
+
+
+void KNCleanUp::ProgressDialog::closeEvent(QCloseEvent *)
+{
+ // do nothing => prevent that the user closes the window
+}
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/kncleanup.h b/knode/kncleanup.h
new file mode 100644
index 000000000..81e453dff
--- /dev/null
+++ b/knode/kncleanup.h
@@ -0,0 +1,72 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCLEANUP_H
+#define KNCLEANUP_H
+
+#include <qsemimodal.h>
+
+class QProgressBar;
+class QLabel;
+
+class KNArticleCollection;
+class KNGroup;
+class KNFolder;
+
+namespace KNConfig {
+class Cleanup;
+}
+
+
+class KNCleanUp {
+
+ public:
+ KNCleanUp();
+ ~KNCleanUp();
+
+ void appendCollection(KNArticleCollection *c) { mColList.append( c ); }
+ void start();
+ void reset();
+
+ void expireGroup( KNGroup *g, bool showResult = false );
+ void compactFolder(KNFolder *f);
+
+ protected:
+
+ class ProgressDialog : public QDialog {
+
+ public:
+ ProgressDialog(int steps);
+ ~ProgressDialog();
+
+ void showMessage(const QString &s);
+ void doProgress();
+
+ protected:
+ void closeEvent(QCloseEvent *e);
+
+ QLabel *m_sg;
+ QProgressBar *p_bar;
+
+ int s_teps, p_rogress;
+ };
+
+ ProgressDialog *d_lg;
+ QValueList<KNArticleCollection*> mColList;
+
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/kncollection.cpp b/knode/kncollection.cpp
new file mode 100644
index 000000000..28651fad9
--- /dev/null
+++ b/knode/kncollection.cpp
@@ -0,0 +1,50 @@
+/*
+ kncollection.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include "kncollection.h"
+#include "kncollectionviewitem.h"
+
+KNCollection::KNCollection(KNCollection *p)
+{
+ p_arent=p;
+ l_istItem=0;
+ c_ount=0;
+}
+
+
+
+KNCollection::~KNCollection()
+{
+ delete l_istItem;
+}
+
+
+
+void KNCollection::setListItem(KNCollectionViewItem *i)
+{
+ l_istItem=i;
+ if(i) {
+ i->coll=this;
+ i->setText(0, name());
+ }
+}
+
+
+
+void KNCollection::updateListItem()
+{
+ if(l_istItem) l_istItem->setText(0, name());
+}
diff --git a/knode/kncollection.h b/knode/kncollection.h
new file mode 100644
index 000000000..80907ca55
--- /dev/null
+++ b/knode/kncollection.h
@@ -0,0 +1,70 @@
+/*
+ kncollection.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCOLLECTION_H
+#define KNCOLLECTION_H
+
+#include <qstring.h>
+
+class KNCollectionViewItem;
+
+
+class KNCollection {
+
+ public:
+ enum collectionType { CTnntpAccount, CTgroup,
+ CTfolder, CTcategory,
+ CTvirtualGroup };
+
+ KNCollection(KNCollection *p);
+ virtual ~KNCollection();
+
+ // type
+ virtual collectionType type()=0;
+
+ // list item handling
+ KNCollectionViewItem* listItem()const { return l_istItem; }
+ void setListItem(KNCollectionViewItem *i);
+ virtual void updateListItem();
+
+ // info
+ virtual QString path()=0;
+ virtual bool readInfo(const QString &confPath)=0;
+ virtual void saveInfo()=0;
+
+ // parent
+ KNCollection* parent()const { return p_arent; }
+ virtual void setParent(KNCollection *p) { p_arent=p; }
+
+ // name
+ virtual const QString& name() { return n_ame; }
+ void setName(const QString &s) { n_ame=s; }
+
+ // count
+ int count()const { return c_ount; }
+ void setCount(int i) { c_ount=i; }
+ void incCount(int i) { c_ount+=i; }
+ void decCount(int i) { c_ount-=i; }
+
+ protected:
+ KNCollection *p_arent;
+ KNCollectionViewItem *l_istItem;
+ QString n_ame;
+ int c_ount;
+
+};
+
+#endif
diff --git a/knode/kncollectionview.cpp b/knode/kncollectionview.cpp
new file mode 100644
index 000000000..2d48a9e5d
--- /dev/null
+++ b/knode/kncollectionview.cpp
@@ -0,0 +1,457 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 2004-2005 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+
+#include <qcursor.h>
+#include <qheader.h>
+
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+
+#include "knglobals.h"
+#include "knconfig.h"
+#include "knconfigmanager.h"
+#include "knnntpaccount.h"
+#include "knaccountmanager.h"
+#include "kngroup.h"
+#include "kngroupmanager.h"
+#include "knfolder.h"
+#include "knfoldermanager.h"
+#include "headerview.h"
+#include "kncollectionview.h"
+#include "kncollectionviewitem.h"
+
+KNCollectionView::KNCollectionView(QWidget *parent, const char* name) :
+ KFolderTree(parent, name),
+ mActiveItem( 0 ),
+ mPopup( 0 )
+{
+ setDragEnabled(true);
+ addAcceptableDropMimetype("x-knode-drag/article", false);
+ addAcceptableDropMimetype("x-knode-drag/folder", true);
+ addColumn(i18n("Name"),162);
+ setDropHighlighter(true);
+
+ // popup menu to enable/disable unread and total columns
+ header()->setClickEnabled( true );
+ header()->installEventFilter( this );
+ mPopup = new KPopupMenu( this );
+ mPopup->insertTitle( i18n("View Columns") );
+ mPopup->setCheckable( true );
+ mUnreadPop = mPopup->insertItem( i18n("Unread Column"), this, SLOT(toggleUnreadColumn()) );
+ mTotalPop = mPopup->insertItem( i18n("Total Column"), this, SLOT(toggleTotalColumn()) );
+
+ // add unread and total columns if necessary
+ readConfig();
+
+ // load accounts and folders
+ reloadAccounts();
+ reloadFolders();
+
+ // connect to the account manager
+ KNAccountManager* am = knGlobals.accountManager();
+ connect(am, SIGNAL(accountAdded(KNNntpAccount*)), SLOT(addAccount(KNNntpAccount*)));
+ connect(am, SIGNAL(accountRemoved(KNNntpAccount*)), SLOT(removeAccount(KNNntpAccount*)));
+ connect(am, SIGNAL(accountModified(KNNntpAccount*)), SLOT(updateAccount(KNNntpAccount*)));
+
+ // connect to the group manager
+ KNGroupManager* gm = knGlobals.groupManager();
+ connect(gm, SIGNAL(groupAdded(KNGroup*)), SLOT(addGroup(KNGroup*)));
+ connect(gm, SIGNAL(groupRemoved(KNGroup*)), SLOT(removeGroup(KNGroup*)));
+ connect(gm, SIGNAL(groupUpdated(KNGroup*)), SLOT(updateGroup(KNGroup*)));
+
+ // connect to the folder manager
+ KNFolderManager* fm = knGlobals.folderManager();
+ connect(fm, SIGNAL(folderAdded(KNFolder*)), SLOT(addPendingFolders()));
+ connect(fm, SIGNAL(folderRemoved(KNFolder*)), SLOT(removeFolder(KNFolder*)));
+ connect(fm, SIGNAL(folderActivated(KNFolder*)), SLOT(activateFolder(KNFolder*)));
+
+ installEventFilter(this);
+}
+
+
+KNCollectionView::~KNCollectionView()
+{
+ writeConfig();
+}
+
+
+
+void KNCollectionView::readConfig()
+{
+ KConfig *conf = knGlobals.config();
+ conf->setGroup( "GroupView" );
+
+ // execute the listview layout stuff only once
+ static bool initDone = false;
+ if (!initDone) {
+ initDone = true;
+ const int unreadColumn = conf->readNumEntry("UnreadColumn", 1);
+ const int totalColumn = conf->readNumEntry("TotalColumn", 2);
+
+ // we need to _activate_ them in the correct order
+ // this is ugly because we can't use header()->moveSection
+ // but otherwise the restoreLayout doesn't know that to do
+ if (unreadColumn != -1 && unreadColumn < totalColumn)
+ addUnreadColumn( i18n("Unread"), 48 );
+ if (totalColumn != -1)
+ addTotalColumn( i18n("Total"), 36 );
+ if (unreadColumn != -1 && unreadColumn > totalColumn)
+ addUnreadColumn( i18n("Unread"), 48 );
+ updatePopup();
+
+ restoreLayout( knGlobals.config(), "GroupView" );
+ }
+
+ // font & color settings
+ KNConfig::Appearance *app = knGlobals.configManager()->appearance();
+ setFont( app->groupListFont() );
+
+ QPalette p = palette();
+ p.setColor( QColorGroup::Base, app->backgroundColor() );
+ p.setColor( QColorGroup::Text, app->textColor() );
+ setPalette( p );
+ setAlternateBackground( app->backgroundColor() );
+ // FIXME: make this configurable
+ mPaintInfo.colUnread = QColor( "blue" );
+ mPaintInfo.colFore = app->textColor();
+ mPaintInfo.colBack = app->backgroundColor();
+}
+
+
+void KNCollectionView::writeConfig()
+{
+ KConfig *conf = knGlobals.config();
+ conf->setGroup( "GroupView" );
+ saveLayout( knGlobals.config(), "GroupView" );
+ conf->writeEntry( "UnreadColumn", unreadIndex() );
+ conf->writeEntry( "TotalColumn", totalIndex() );
+}
+
+
+
+void KNCollectionView::addAccount(KNNntpAccount *a)
+{
+ // add account item
+ KNCollectionViewItem* item = new KNCollectionViewItem( this, KFolderTreeItem::News );
+ a->setListItem( item );
+ item->setOpen( a->wasOpen() );
+
+ // add groups for this account
+ QValueList<KNGroup*> groups = knGlobals.groupManager()->groupsOfAccount( a );
+ for ( QValueList<KNGroup*>::Iterator it = groups.begin(); it != groups.end(); ++it ) {
+ KNCollectionViewItem *gitem = new KNCollectionViewItem( item, KFolderTreeItem::News );
+ (*it)->setListItem( gitem );
+ (*it)->updateListItem();
+ }
+}
+
+
+void KNCollectionView::removeAccount(KNNntpAccount *a)
+{
+ if(!a->listItem())
+ return;
+ KNCollectionViewItem *child = 0, *aitem = a->listItem();
+ while((child = static_cast<KNCollectionViewItem*>(aitem->firstChild())))
+ removeGroup(static_cast<KNGroup*>(child->coll));
+ delete aitem;
+ a->setListItem(0);
+}
+
+
+void KNCollectionView::updateAccount(KNNntpAccount *a)
+{
+ a->updateListItem();
+}
+
+
+void KNCollectionView::reloadAccounts()
+{
+ KNAccountManager* am = knGlobals.accountManager();
+ QValueList<KNNntpAccount*>::Iterator it;
+ for ( it = am->begin(); it != am->end(); ++it ) {
+ removeAccount( *it );
+ addAccount( *it );
+ }
+}
+
+
+
+void KNCollectionView::addGroup(KNGroup *g)
+{
+ if (!g->account()->listItem())
+ return;
+
+ KNCollectionViewItem *gitem =
+ new KNCollectionViewItem( g->account()->listItem(), KFolderTreeItem::News );
+ g->setListItem(gitem);
+ updateGroup(g);
+}
+
+
+void KNCollectionView::removeGroup(KNGroup *g)
+{
+ if (!g->listItem())
+ return;
+
+ delete g->listItem();
+ g->setListItem(0);
+}
+
+
+void KNCollectionView::updateGroup(KNGroup *g)
+{
+ g->updateListItem();
+}
+
+
+
+void KNCollectionView::addFolder(KNFolder *f)
+{
+ KNCollectionViewItem *it;
+
+ if (!f->parent()) {
+ // root folder
+ it = new KNCollectionViewItem(this, KFolderTreeItem::Local);
+ } else {
+ // make sure the parent folder has already been added
+ if (!f->parent()->listItem())
+ addFolder( static_cast<KNFolder*>(f->parent()) );
+ // handle special folders
+ KFolderTreeItem::Type type = KFolderTreeItem::Other;
+ switch ( f->id() ) {
+ case 1:
+ type = KFolderTreeItem::Drafts; break;
+ case 2:
+ type = KFolderTreeItem::Outbox; break;
+ case 3:
+ type = KFolderTreeItem::SentMail; break;
+ }
+ it = new KNCollectionViewItem( f->parent()->listItem(), KFolderTreeItem::Local, type );
+ }
+ f->setListItem( it );
+ updateFolder( f );
+}
+
+
+void KNCollectionView::removeFolder(KNFolder* f)
+{
+ if(!f->listItem())
+ return;
+ KNCollectionViewItem *child = 0, *it = f->listItem();
+ while((child = static_cast<KNCollectionViewItem*>(it->firstChild())))
+ removeFolder(static_cast<KNFolder*>(child->coll));
+ delete f->listItem();
+ f->setListItem(0);
+}
+
+
+void KNCollectionView::reloadFolders()
+{
+ // remove existing folder items
+ removeFolder(knGlobals.folderManager()->root());
+
+ // add folder items
+ addPendingFolders();
+}
+
+
+void KNCollectionView::addPendingFolders()
+{
+ QValueList<KNFolder*> folders = knGlobals.folderManager()->folders();
+ for ( QValueList<KNFolder*>::Iterator it = folders.begin(); it != folders.end(); ++it )
+ if ( !(*it)->listItem() )
+ addFolder( (*it) );
+ // now open the folders if they were open in the last session
+ for ( QValueList<KNFolder*>::Iterator it = folders.begin(); it != folders.end(); ++it )
+ if ( (*it)->listItem())
+ (*it)->listItem()->setOpen( (*it)->wasOpen() );
+}
+
+
+void KNCollectionView::activateFolder(KNFolder* f)
+{
+ if(f->listItem())
+ setActive( f->listItem() );
+}
+
+
+void KNCollectionView::updateFolder(KNFolder* f)
+{
+ f->updateListItem();
+}
+
+
+void KNCollectionView::reload()
+{
+ reloadAccounts();
+ reloadFolders();
+}
+
+void KNCollectionView::setActive( QListViewItem *i )
+{
+ if (!i || mActiveItem == i)
+ return;
+
+ clearSelection();
+ setSelected( i, true );
+ setCurrentItem( i );
+ mActiveItem = i;
+ emit( selectionChanged( i ) );
+}
+
+
+void KNCollectionView::nextGroup()
+{
+ incCurrentFolder();
+ setActive( currentItem() );
+}
+
+
+void KNCollectionView::prevGroup()
+{
+ decCurrentFolder();
+ setActive( currentItem() );
+}
+
+
+void KNCollectionView::decCurrentFolder()
+{
+ QListViewItemIterator it( currentItem() );
+ --it;
+ KFolderTreeItem* fti = static_cast<KFolderTreeItem*>(it.current());
+ if (fti) {
+ ensureItemVisible( fti );
+ setFocus();
+ setCurrentItem( fti );
+ }
+}
+
+
+void KNCollectionView::incCurrentFolder()
+{
+ QListViewItemIterator it( currentItem() );
+ ++it;
+ KFolderTreeItem* fti = static_cast<KFolderTreeItem*>(it.current());
+ if (fti) {
+ ensureItemVisible( fti );
+ setFocus();
+ setCurrentItem( fti );
+ }
+}
+
+
+void KNCollectionView::selectCurrentFolder()
+{
+ KFolderTreeItem* fti = static_cast<KFolderTreeItem*>( currentItem() );
+ if (fti) {
+ ensureItemVisible( fti );
+ setActive( fti );
+ }
+}
+
+
+QDragObject* KNCollectionView::dragObject()
+{
+ KFolderTreeItem *item = static_cast<KFolderTreeItem*>
+ (itemAt(viewport()->mapFromGlobal(QCursor::pos())));
+ if ( item && item->protocol() == KFolderTreeItem::Local && item->type() == KFolderTreeItem::Other ) {
+ QDragObject *d = new QStoredDrag( "x-knode-drag/folder", viewport() );
+ d->setPixmap( SmallIcon("folder") );
+ return d;
+ }
+ return 0;
+}
+
+
+void KNCollectionView::contentsDropEvent( QDropEvent *e )
+{
+ cleanItemHighlighter(); // necessary since we overwrite KListView::contentsDropEvent()
+ QListViewItem *item = itemAt( contentsToViewport(e->pos()) );
+ KNCollectionViewItem *fti = static_cast<KNCollectionViewItem*>(item);
+ if (fti && (fti->coll) && acceptDrag(e)) {
+ emit folderDrop( e, fti );
+ e->accept( true );
+ }
+ else
+ e->accept( false );
+}
+
+
+
+void KNCollectionView::toggleUnreadColumn()
+{
+ if ( isUnreadActive() )
+ removeUnreadColumn();
+ else
+ addUnreadColumn( i18n("Unread"), 48 );
+ mPopup->setItemChecked( mUnreadPop, isUnreadActive() );
+ reload();
+}
+
+
+void KNCollectionView::toggleTotalColumn()
+{
+ if ( isTotalActive() )
+ removeTotalColumn();
+ else
+ addTotalColumn( i18n("Total"), 36 );
+ mPopup->setItemChecked( mTotalPop, isTotalActive() );
+ reload();
+}
+
+void KNCollectionView::updatePopup() const
+{
+ mPopup->setItemChecked( mUnreadPop, isUnreadActive() );
+ mPopup->setItemChecked( mTotalPop, isTotalActive() );
+}
+
+
+
+bool KNCollectionView::eventFilter(QObject *o, QEvent *e)
+{
+ if ((e->type() == QEvent::KeyPress) && (static_cast<QKeyEvent*>(e)->key() == Key_Tab)) {
+ emit(focusChangeRequest(this));
+ if (!hasFocus()) // focusChangeRequest was successful
+ return true;
+ }
+
+ // header popup menu
+ if ( e->type() == QEvent::MouseButtonPress &&
+ static_cast<QMouseEvent*>(e)->button() == RightButton &&
+ o->isA("QHeader") )
+ {
+ mPopup->popup( static_cast<QMouseEvent*>(e)->globalPos() );
+ return true;
+ }
+
+ return KFolderTree::eventFilter(o, e);
+}
+
+
+void KNCollectionView::focusInEvent(QFocusEvent *e)
+{
+ QListView::focusInEvent(e);
+ emit focusChanged(e);
+}
+
+
+void KNCollectionView::focusOutEvent(QFocusEvent *e)
+{
+ QListView::focusOutEvent(e);
+ emit focusChanged(e);
+}
+
+
+#include "kncollectionview.moc"
diff --git a/knode/kncollectionview.h b/knode/kncollectionview.h
new file mode 100644
index 000000000..3c7e0f61c
--- /dev/null
+++ b/knode/kncollectionview.h
@@ -0,0 +1,93 @@
+/*
+ kncollectionview.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 2004 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCOLLECTIONTREE_H
+#define KNCOLLECTIONTREE_H
+
+#include <kfoldertree.h>
+
+class KPopupMenu;
+class KNNntpAccount;
+class KNGroup;
+class KNFolder;
+class KNCollectionViewItem;
+
+class KNCollectionView : public KFolderTree {
+
+ Q_OBJECT
+
+ public:
+ KNCollectionView(QWidget *parent, const char *name = 0);
+ ~KNCollectionView();
+
+ void setActive(QListViewItem *item);
+
+ void readConfig();
+ void writeConfig();
+
+ public slots:
+ void addAccount(KNNntpAccount* a);
+ void removeAccount(KNNntpAccount* a);
+ void updateAccount(KNNntpAccount* a);
+ void reloadAccounts();
+
+ void addGroup(KNGroup* g);
+ void removeGroup(KNGroup* g);
+ void updateGroup(KNGroup* g);
+
+ void addFolder(KNFolder* f);
+ void removeFolder(KNFolder* f);
+ void activateFolder(KNFolder* f);
+ void updateFolder(KNFolder* f);
+ void addPendingFolders();
+ void reloadFolders();
+
+ void reload();
+
+ void nextGroup();
+ void prevGroup();
+
+ // KMail like keyboard navigation
+ void decCurrentFolder();
+ void incCurrentFolder();
+ void selectCurrentFolder();
+
+ void toggleUnreadColumn();
+ void toggleTotalColumn();
+ void updatePopup() const;
+
+ signals:
+ void folderDrop( QDropEvent *e, KNCollectionViewItem *item );
+
+ void focusChanged( QFocusEvent* );
+ void focusChangeRequest( QWidget* );
+
+ protected:
+ // dnd
+ virtual QDragObject* dragObject();
+ virtual void contentsDropEvent( QDropEvent *e );
+
+ bool eventFilter( QObject *, QEvent * );
+ void focusInEvent( QFocusEvent *e );
+ void focusOutEvent( QFocusEvent *e );
+
+ private:
+ QListViewItem *mActiveItem;
+ KPopupMenu *mPopup;
+ int mUnreadPop, mTotalPop;
+
+};
+
+#endif
diff --git a/knode/kncollectionviewitem.cpp b/knode/kncollectionviewitem.cpp
new file mode 100644
index 000000000..ace78a63a
--- /dev/null
+++ b/knode/kncollectionviewitem.cpp
@@ -0,0 +1,172 @@
+/*
+ kncollectionviewitem.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qdragobject.h>
+#include <qpainter.h>
+
+#include <kiconloader.h>
+#include <kstringhandler.h>
+
+#include "kncollectionviewitem.h"
+#include "kncollectionview.h"
+#include "kngroup.h"
+#include "knfolder.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+
+
+KNCollectionViewItem::KNCollectionViewItem( KFolderTree *parent, Protocol protocol, Type type) :
+ KFolderTreeItem(parent, QString::null, protocol, type), coll(0)
+{
+ setIcon();
+}
+
+
+KNCollectionViewItem::KNCollectionViewItem( KFolderTreeItem *it, Protocol protocol, Type type, int unread, int total ) :
+ KFolderTreeItem(it, QString::null, protocol, type, unread, total), coll(0)
+{
+ setIcon();
+}
+
+
+KNCollectionViewItem::~KNCollectionViewItem()
+{
+ if(coll) coll->setListItem(0);
+}
+
+
+void KNCollectionViewItem::setIcon() {
+ if ( protocol() == KFolderTreeItem::News ) {
+ // news servers/groups
+ switch ( type() ) {
+ case KFolderTreeItem::Root:
+ setPixmap( 0, SmallIcon("server") );
+ break;
+ default:
+ setPixmap( 0, UserIcon("group") );
+ }
+ } else {
+ // local folders
+ switch ( type() ) {
+ case KFolderTreeItem::Outbox:
+ setPixmap( 0, SmallIcon("folder_outbox") );
+ break;
+ case KFolderTreeItem::Drafts:
+ setPixmap( 0, SmallIcon("edit") );
+ break;
+ case KFolderTreeItem::SentMail:
+ setPixmap( 0, SmallIcon("folder_sent_mail") );
+ break;
+ default:
+ setPixmap( 0, SmallIcon("folder") );
+ }
+ }
+}
+
+
+int KNCollectionViewItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ KFolderTreeItem *other = static_cast<KFolderTreeItem*>(i);
+
+ // folders should be always on the bottom
+ if (protocol() == KFolderTreeItem::Local) {
+ if (other && other->protocol() == KFolderTreeItem::News)
+ return ascending ? 1 : -1;
+ }
+
+ // news servers should be always on top
+ if (protocol() == KFolderTreeItem::News) {
+ if (other && other->protocol() == KFolderTreeItem::Local)
+ return ascending ? -1 : 1;
+ }
+
+ return KFolderTreeItem::compare(i, col, ascending);
+}
+
+
+bool KNCollectionViewItem::acceptDrag(QDropEvent* event) const
+{
+ if (event && coll && coll->type()==KNCollection::CTfolder) {
+ if (event->provides("x-knode-drag/article"))
+ return !(static_cast<KNFolder*>(coll)->isRootFolder()); // don't drop articles on the root folder
+ else if (event->provides("x-knode-drag/folder"))
+ return !isSelected(); // don't drop on itself
+ }
+ return false;
+}
+
+
+void KNCollectionViewItem::paintCell( QPainter * p, const QColorGroup & cg,int column, int width, int align )
+{
+ KFolderTree *ft = static_cast<KFolderTree*>( listView() );
+
+ // we only need to deal with the case where we paint the folder/group name
+ // and the unread count is displayed in a separate column
+ if ( !ft->isUnreadActive() || column != 0 ) {
+ KFolderTreeItem::paintCell( p, cg, column, width, align );
+ return;
+ }
+
+ // find out if we will use bold font, necessary for the text squeezing
+ if ( (column == 0 || column == ft->unreadIndex()) && ( mUnread > 0 ) ) {
+ QFont f = p->font();
+ f.setWeight(QFont::Bold);
+ p->setFont(f);
+ }
+
+ // consider pixmap size for squeezing
+ int pxWidth = 8;
+ const QPixmap *px = pixmap(column);
+ if (px)
+ pxWidth += px->width();
+
+ // temporary set the squeezed text and use the parent class to paint it
+ QString curText = text( column );
+ if ( p->fontMetrics().width( curText ) > width - pxWidth ) {
+ setText( column, squeezeFolderName( curText, p->fontMetrics(), width - pxWidth ) );
+ KFolderTreeItem::paintCell( p, cg, column, width, align );
+ setText( column, curText );
+ } else
+ KFolderTreeItem::paintCell( p, cg, column, width, align );
+}
+
+
+QString KNCollectionViewItem::squeezeFolderName( const QString &text,
+ const QFontMetrics &fm,
+ uint width ) const
+{
+ if (protocol() == KFolderTreeItem::News && type() == KFolderTreeItem::Other) {
+ QString t(text);
+ int curPos = 0, nextPos = 0;
+ QString temp;
+ while ( (uint)fm.width(t) > width && nextPos != -1 ) {
+ nextPos = t.find('.', curPos);
+ if ( nextPos != -1 ) {
+ temp = t[curPos];
+ t.replace( curPos, nextPos - curPos, temp );
+ curPos += 2;
+ }
+ }
+ if ( (uint)fm.width( t ) > width )
+ t = KStringHandler::rPixelSqueeze( t, fm, width );
+ return t;
+ } else
+ return KFolderTreeItem::squeezeFolderName( text, fm, width );
+}
diff --git a/knode/kncollectionviewitem.h b/knode/kncollectionviewitem.h
new file mode 100644
index 000000000..0d13678f2
--- /dev/null
+++ b/knode/kncollectionviewitem.h
@@ -0,0 +1,56 @@
+/*
+ kncollectionviewitem.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCOLLECTIONVIEWITEM_H
+#define KNCOLLECTIONVIEWITEM_H
+
+#include <kfoldertree.h>
+
+class QPainter;
+class QColorGroup;
+
+class KNCollection;
+
+
+class KNCollectionViewItem : public KFolderTreeItem {
+
+ public:
+ KNCollectionViewItem( KFolderTree *parent, Protocol protocol = NONE, Type type = Root);
+ KNCollectionViewItem( KFolderTreeItem *parent, Protocol protocol = NONE,
+ Type type = Other, int unread = 0, int total = 0 );
+ ~KNCollectionViewItem();
+
+ void paintCell( QPainter * p, const QColorGroup & cg,
+ int column, int width, int align );
+
+ int compare(QListViewItem *i, int col, bool ascending) const;
+
+ // DND
+ virtual bool acceptDrag(QDropEvent* event) const;
+
+ KNCollection *coll;
+
+ protected:
+ virtual QString squeezeFolderName( const QString &text,
+ const QFontMetrics &fm,
+ uint width ) const;
+
+ private:
+ void setIcon();
+
+};
+
+#endif
diff --git a/knode/kncomposer.cpp b/knode/kncomposer.cpp
new file mode 100644
index 000000000..b6fdd4249
--- /dev/null
+++ b/knode/kncomposer.cpp
@@ -0,0 +1,2661 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qheader.h>
+#include <qtextcodec.h>
+#include <qclipboard.h>
+#include <qapplication.h>
+#include <kspelldlg.h>
+#include <kdeversion.h>
+#include "addressesdialog.h"
+using KPIM::AddressesDialog;
+#include "recentaddresses.h"
+using KRecentAddress::RecentAddresses;
+#include <kaccel.h>
+#include <kcharsets.h>
+#include <kmessagebox.h>
+#include <kabc/addresseedialog.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kkeydialog.h>
+#include <kedittoolbar.h>
+#include <kpopupmenu.h>
+#include <kfiledialog.h>
+#include <kdebug.h>
+#include <klineedit.h>
+#include <kcombobox.h>
+#include <kspell.h>
+#include <ktempfile.h>
+#include <kpgp.h>
+#include <kpgpblock.h>
+#include <kprocess.h>
+#include <kqcstringsplitter.h>
+#include <ksyntaxhighlighter.h>
+#include <qcursor.h>
+#include <kurldrag.h>
+#include <kcompletionbox.h>
+
+#include <kapplication.h>
+#include "kngroupselectdialog.h"
+#include "utilities.h"
+#include "knglobals.h"
+#include "kncomposer.h"
+#include "knmainwidget.h"
+#include "knconfigmanager.h"
+#include "knaccountmanager.h"
+#include "knnntpaccount.h"
+#include "knarticlefactory.h"
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <qpopupmenu.h>
+#include <spellingfilter.h>
+#include <kstdguiitem.h>
+
+KNLineEdit::KNLineEdit(KNComposer::ComposerView *_composerView, bool useCompletion,
+ QWidget *parent, const char *name)
+ : KNLineEditInherited(parent,useCompletion,name) , composerView(_composerView)
+
+{
+}
+
+
+QPopupMenu *KNLineEdit::createPopupMenu()
+{
+ QPopupMenu *menu = KLineEdit::createPopupMenu();
+ if ( !menu )
+ return 0;
+
+ menu->insertSeparator();
+ menu->insertItem( i18n( "Edit Recent Addresses..." ),
+ this, SLOT( editRecentAddresses() ) );
+
+ return menu;
+}
+
+void KNLineEdit::editRecentAddresses()
+{
+ KRecentAddress::RecentAddressDialog dlg( this );
+ dlg.setAddresses( RecentAddresses::self( knGlobals.config() )->addresses() );
+ if ( dlg.exec() ) {
+ RecentAddresses::self( knGlobals.config() )->clear();
+ QStringList addrList = dlg.addresses();
+ QStringList::Iterator it;
+ for ( it = addrList.begin(); it != addrList.end(); ++it )
+ RecentAddresses::self( knGlobals.config() )->add( *it );
+
+ loadAddresses();
+ }
+}
+
+void KNLineEdit::loadAddresses()
+{
+ KNLineEditInherited::loadAddresses();
+
+ QStringList recent = RecentAddresses::self(knGlobals.config())->addresses();
+ QStringList::Iterator it = recent.begin();
+ for ( ; it != recent.end(); ++it )
+ addAddress( *it );
+}
+
+void KNLineEdit::keyPressEvent(QKeyEvent *e)
+{
+ // ---sven's Return is same Tab and arrow key navigation start ---
+ if ((e->key() == Key_Enter || e->key() == Key_Return) &&
+ !completionBox()->isVisible())
+ {
+ composerView->focusNextPrevEdit( this, true );
+ return;
+ }
+ if (e->key() == Key_Up)
+ {
+ composerView->focusNextPrevEdit( this, false ); // Go up
+ return;
+ }
+ if (e->key() == Key_Down)
+ {
+ composerView->focusNextPrevEdit( this, true ); // Go down
+ return;
+ }
+ // ---sven's Return is same Tab and arrow key navigation end ---
+ KNLineEditInherited::keyPressEvent(e);
+}
+
+KNLineEditSpell::KNLineEditSpell( KNComposer::ComposerView *_composerView, bool useCompletion,QWidget * parent, const char * name)
+ :KNLineEdit( _composerView, useCompletion, parent,name )
+{
+}
+
+void KNLineEditSpell::highLightWord( unsigned int length, unsigned int pos )
+{
+ setSelection ( pos, length );
+}
+
+void KNLineEditSpell::spellCheckDone( const QString &s )
+{
+ if( s != text() )
+ setText( s );
+}
+
+void KNLineEditSpell::spellCheckerMisspelling( const QString &_text, const QStringList &, unsigned int pos)
+{
+ highLightWord( _text.length(),pos );
+}
+
+void KNLineEditSpell::spellCheckerCorrected( const QString &old, const QString &corr, unsigned int pos)
+{
+ if( old!= corr )
+ {
+ setSelection ( pos, old.length() );
+ insert( corr );
+ setSelection ( pos, corr.length() );
+ }
+}
+
+
+KNComposer::KNComposer(KNLocalArticle *a, const QString &text, const QString &sig, const QString &unwraped, bool firstEdit, bool dislikesCopies, bool createCopy)
+ : KMainWindow(0,"composerWindow"), r_esult(CRsave), a_rticle(a), s_ignature(sig), u_nwraped(unwraped),
+ n_eeds8Bit(true), v_alidated(false), a_uthorDislikesMailCopies(dislikesCopies), e_xternalEdited(false), e_xternalEditor(0),
+ e_ditorTempfile(0), s_pellChecker(0), a_ttChanged(false),
+ mFirstEdit( firstEdit )
+{
+ mSpellingFilter = 0;
+ spellLineEdit = false;
+ m_listAction.setAutoDelete( true );
+
+ if(knGlobals.instance)
+ setInstance(knGlobals.instance);
+
+ // activate dnd of attachments...
+ setAcceptDrops(true);
+
+ //init v_iew
+ v_iew=new ComposerView(this);
+ setCentralWidget(v_iew);
+
+ connect(v_iew->c_ancelEditorBtn, SIGNAL(clicked()), SLOT(slotCancelEditor()));
+ connect(v_iew->e_dit, SIGNAL(sigDragEnterEvent(QDragEnterEvent *)), SLOT(slotDragEnterEvent(QDragEnterEvent *)));
+ connect(v_iew->e_dit, SIGNAL(sigDropEvent(QDropEvent *)), SLOT(slotDropEvent(QDropEvent *)));
+
+ //statusbar
+ KStatusBar *sb=statusBar();
+ sb->insertItem(QString::null, 1,1); // type
+ sb->setItemAlignment (1,AlignLeft | AlignVCenter);
+ sb->insertItem(QString::null, 2,1); // charset
+ sb->setItemAlignment (2,AlignLeft | AlignVCenter);
+ sb->insertItem(QString::null, 3,0); // column
+ sb->setItemAlignment (3,AlignCenter | AlignVCenter);
+ sb->insertItem(QString::null, 4,0); // column
+ sb->setItemAlignment (4,AlignCenter | AlignVCenter);
+ sb->insertItem(QString::null, 5,0); // line
+ sb->setItemAlignment (5,AlignCenter | AlignVCenter);
+ connect(v_iew->e_dit, SIGNAL(CursorPositionChanged()), SLOT(slotUpdateCursorPos()));
+ connect(v_iew->e_dit, SIGNAL(toggle_overwrite_signal()), SLOT(slotUpdateStatusBar()));
+
+ //------------------------------- <Actions> --------------------------------------
+
+ //file menu
+ new KAction(i18n("&Send Now"),"mail_send", CTRL + Key_Return , this,
+ SLOT(slotSendNow()), actionCollection(), "send_now");
+
+ new KAction(i18n("Send &Later"), "queue", 0, this,
+ SLOT(slotSendLater()), actionCollection(), "send_later");
+
+ new KAction(i18n("Save as &Draft"),"filesave", 0 , this,
+ SLOT(slotSaveAsDraft()), actionCollection(), "save_as_draft");
+
+ new KAction(i18n("D&elete"),"editdelete", 0 , this,
+ SLOT(slotArtDelete()), actionCollection(), "art_delete");
+
+ KStdAction::close(this, SLOT(close()),actionCollection());
+
+ //edit menu
+ KStdAction::undo(this, SLOT(slotUndo()), actionCollection());
+ KStdAction::redo(this, SLOT(slotRedo()), actionCollection());
+
+ KStdAction::cut(this, SLOT(slotCut()), actionCollection());
+
+
+ KStdAction::copy(this, SLOT(slotCopy()), actionCollection());
+
+ KStdAction::pasteText(this, SLOT(slotPaste()), actionCollection());
+
+ new KAction(i18n("Paste as &Quotation"), 0, v_iew->e_dit,
+ SLOT(slotPasteAsQuotation()), actionCollection(), "paste_quoted");
+
+ KStdAction::selectAll(this, SLOT(slotSelectAll()), actionCollection());
+
+ KStdAction::find(v_iew->e_dit, SLOT(slotFind()), actionCollection());
+ KStdAction::findNext(v_iew->e_dit, SLOT(slotSearchAgain()), actionCollection());
+
+ KStdAction::replace(v_iew->e_dit, SLOT(slotReplace()), actionCollection());
+
+ //attach menu
+ new KAction(i18n("Append &Signature"), 0 , this, SLOT(slotAppendSig()),
+ actionCollection(), "append_signature");
+
+ new KAction(i18n("&Insert File..."), 0, this, SLOT(slotInsertFile()),
+ actionCollection(), "insert_file");
+
+ new KAction(i18n("Insert File (in a &box)..."), 0, this, SLOT(slotInsertFileBoxed()),
+ actionCollection(), "insert_file_boxed");
+
+ new KAction(i18n("Attach &File..."), "attach", 0, this, SLOT(slotAttachFile()),
+ actionCollection(), "attach_file");
+
+ a_ctPGPsign = new KToggleAction(i18n("Sign Article with &PGP"),
+ "signature", 0,
+ actionCollection(), "sign_article");
+
+ a_ctRemoveAttachment = new KAction(i18n("&Remove"), 0, this,
+ SLOT(slotRemoveAttachment()), actionCollection(), "remove_attachment");
+
+ a_ctAttachmentProperties = new KAction(i18n("&Properties"), 0, this,
+ SLOT(slotAttachmentProperties()), actionCollection(), "attachment_properties");
+
+ //options menu
+
+ a_ctDoPost = new KToggleAction(i18n("Send &News Article"), "filenew", 0 , this,
+ SLOT(slotToggleDoPost()), actionCollection(), "send_news");
+
+ a_ctDoMail = new KToggleAction(i18n("Send E&mail"), "mail_generic" , 0 , this,
+ SLOT(slotToggleDoMail()), actionCollection(), "send_mail");
+
+ a_ctSetCharset = new KSelectAction(i18n("Set &Charset"), 0, actionCollection(), "set_charset");
+ a_ctSetCharset->setItems(knGlobals.configManager()->postNewsTechnical()->composerCharsets());
+ a_ctSetCharset->setShortcutConfigurable(false);
+ connect(a_ctSetCharset, SIGNAL(activated(const QString&)),
+ this, SLOT(slotSetCharset(const QString&)));
+
+ a_ctSetCharsetKeyb = new KAction(i18n("Set Charset"), 0, this,
+ SLOT(slotSetCharsetKeyboard()), actionCollection(), "set_charset_keyboard");
+
+
+ a_ctWordWrap = new KToggleAction(i18n("&Word Wrap"), 0 , this,
+ SLOT(slotToggleWordWrap()), actionCollection(), "toggle_wordwrap");
+
+ //tools menu
+
+ new KAction(i18n("Add &Quote Characters"), 0, v_iew->e_dit,
+ SLOT(slotAddQuotes()), actionCollection(), "tools_quote");
+
+ new KAction(i18n("&Remove Quote Characters"), 0, v_iew->e_dit,
+ SLOT(slotRemoveQuotes()), actionCollection(), "tools_unquote");
+
+ new KAction(i18n("Add &Box"), 0, v_iew->e_dit,
+ SLOT(slotAddBox()), actionCollection(), "tools_box");
+
+ new KAction(i18n("Re&move Box"), 0, v_iew->e_dit,
+ SLOT(slotRemoveBox()), actionCollection(), "tools_unbox");
+
+ KAction *undoRewrap = new KAction(i18n("Get &Original Text (not re-wrapped)"), 0, this,
+ SLOT(slotUndoRewrap()), actionCollection(), "tools_undoRewrap");
+ undoRewrap->setEnabled(!u_nwraped.isNull());
+
+ KAction *rot13 = new KAction(i18n("S&cramble (Rot 13)"), "encrypted", 0, v_iew->e_dit,
+ SLOT(slotRot13()), actionCollection(), "tools_rot13");
+ rot13->setEnabled(false);
+ connect(v_iew->e_dit, SIGNAL(copyAvailable(bool)), rot13, SLOT(setEnabled(bool)));
+
+ a_ctExternalEditor = new KAction(i18n("Start &External Editor"), "run", 0, this,
+ SLOT(slotExternalEditor()), actionCollection(), "external_editor");
+
+ a_ctSpellCheck = KStdAction::spelling (this, SLOT(slotSpellcheck()), actionCollection());
+
+ //settings menu
+ createStandardStatusBarAction();
+ setStandardToolBarMenuEnabled(true);
+
+ KStdAction::keyBindings(this, SLOT(slotConfKeys()), actionCollection());
+
+ KStdAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection());
+
+ KStdAction::preferences(knGlobals.top, SLOT(slotSettings()), actionCollection());
+
+ a_ccel=new KAccel(this);
+ a_ctSetCharsetKeyb->plugAccel(a_ccel);
+
+ createGUI("kncomposerui.rc", false);
+
+ //---------------------------------- </Actions> ----------------------------------------
+
+
+ //attachment popup
+ a_ttPopup=static_cast<QPopupMenu*> (factory()->container("attachment_popup", this));
+ if(!a_ttPopup) a_ttPopup = new QPopupMenu();
+ slotAttachmentSelected(0);
+
+ //init
+ initData(text);
+
+ //apply configuration
+ setConfig(false);
+
+ if (firstEdit) { // now we place the cursor at the end of the quoted text / below the attribution line
+ if (knGlobals.configManager()->postNewsComposer()->cursorOnTop()) {
+ int numLines = knGlobals.configManager()->postNewsComposer()->intro().contains("%L");
+ v_iew->e_dit->setCursorPosition(numLines+1,0);
+ }
+ else
+ v_iew->e_dit->setCursorPosition(v_iew->e_dit->numLines()-1,0);
+ } else
+ v_iew->e_dit->setCursorPosition(0,0);
+
+ v_iew->e_dit->setFocus();
+
+ if (v_iew->s_ubject->text().length() == 0) {
+ v_iew->s_ubject->setFocus();
+ }
+
+ if (v_iew->g_roups->text().length() == 0 && m_ode == news) {
+ v_iew->g_roups->setFocus();
+ }
+
+ if (v_iew->t_o->text().length() == 0 && m_ode == mail) {
+ v_iew->t_o->setFocus();
+ }
+
+ if(firstEdit && knGlobals.configManager()->postNewsComposer()->appendOwnSignature())
+ slotAppendSig();
+
+ if (createCopy && (m_ode==news)) {
+ a_ctDoMail->setChecked(true);
+ slotToggleDoMail();
+ }
+
+ v_iew->e_dit->setModified(false);
+
+ // restore window & toolbar configuration
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("composerWindow_options");
+ resize(535,450); // default optimized for 800x600
+ applyMainWindowSettings(conf);
+
+ // starting the external editor
+ if(knGlobals.configManager()->postNewsComposer()->useExternalEditor())
+ slotExternalEditor();
+}
+
+
+KNComposer::~KNComposer()
+{
+ delete s_pellChecker;
+ delete mSpellingFilter;
+ delete e_xternalEditor; // this also kills the editor process if it's still running
+
+ if(e_ditorTempfile) {
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ }
+
+ for ( QValueList<KNAttachment*>::Iterator it = mDeletedAttachments.begin(); it != mDeletedAttachments.end(); ++it )
+ delete (*it);
+
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("composerWindow_options");
+ saveMainWindowSettings(conf);
+}
+
+int KNComposer::listOfResultOfCheckWord( const QStringList & lst , const QString & selectWord)
+{
+ createGUI("kncomposerui.rc", false);
+ unplugActionList("spell_result" );
+ m_listAction.clear();
+ if ( !lst.contains( selectWord ) )
+ {
+ QStringList::ConstIterator it = lst.begin();
+ for ( ; it != lst.end() ; ++it )
+ {
+ if ( !(*it).isEmpty() ) // in case of removed subtypes or placeholders
+ {
+ KAction * act = new KAction( *it );
+
+ connect( act, SIGNAL(activated()), v_iew->e_dit, SLOT(slotCorrectWord()) );
+ m_listAction.append( act );
+ }
+ }
+ }
+ if ( m_listAction.count()>0 )
+ plugActionList("spell_result", m_listAction );
+ return m_listAction.count();
+}
+
+void KNComposer::slotUndo()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->undo();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->undo();
+}
+
+void KNComposer::slotRedo()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->redo();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->redo();
+}
+
+void KNComposer::slotCut()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->cut();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->cut();
+ else kdDebug(5003) << "wrong focus widget" << endl;
+}
+
+void KNComposer::slotCopy()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->copy();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->copy();
+ else kdDebug(5003) << "wrong focus widget" << endl;
+
+}
+
+
+void KNComposer::slotPaste()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->paste();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->paste();
+ else kdDebug(5003) << "wrong focus widget" << endl;
+}
+
+void KNComposer::slotSelectAll()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->selectAll();
+ else if (fw->inherits("QMultiLineEdit"))
+ ((QMultiLineEdit*)fw)->selectAll();
+}
+
+
+void KNComposer::setConfig(bool onlyFonts)
+{
+ if (!onlyFonts) {
+ v_iew->e_dit->setWordWrap(knGlobals.configManager()->postNewsComposer()->wordWrap()?
+ QMultiLineEdit::FixedColumnWidth : QMultiLineEdit::NoWrap);
+ v_iew->e_dit->setWrapColumnOrWidth(knGlobals.configManager()->postNewsComposer()->maxLineLength());
+ a_ctWordWrap->setChecked(knGlobals.configManager()->postNewsComposer()->wordWrap());
+
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+ a_ctPGPsign->setEnabled(pgp->usePGP());
+ }
+
+ QFont fnt=knGlobals.configManager()->appearance()->composerFont();
+ v_iew->s_ubject->setFont(fnt);
+ v_iew->t_o->setFont(fnt);
+ v_iew->g_roups->setFont(fnt);
+ v_iew->f_up2->setFont(fnt);
+ v_iew->e_dit->setFont(fnt);
+
+ slotUpdateStatusBar();
+}
+
+
+void KNComposer::setMessageMode(MessageMode mode)
+{
+ m_ode = mode;
+ a_ctDoPost->setChecked(m_ode!=mail);
+ a_ctDoMail->setChecked(m_ode!=news);
+ v_iew->setMessageMode(m_ode);
+
+ if (m_ode == news_mail) {
+ QString s = v_iew->e_dit->textLine(0);
+ if (!s.contains(i18n("<posted & mailed>")))
+ v_iew->e_dit->insertAt(i18n("<posted & mailed>\n\n"),0,0);
+ } else {
+ if (v_iew->e_dit->textLine(0)==i18n("<posted & mailed>")) {
+ v_iew->e_dit->removeLine(0);
+ if (v_iew->e_dit->textLine(0).isEmpty())
+ v_iew->e_dit->removeLine(0);
+ }
+ }
+
+ slotUpdateStatusBar();
+}
+
+
+bool KNComposer::hasValidData()
+{
+ v_alidated=false;
+ n_eeds8Bit=false;
+
+ // header checks
+
+ if (v_iew->s_ubject->text().isEmpty()) {
+ KMessageBox::sorry(this, i18n("Please enter a subject."));
+ return false;
+ }
+ if (!n_eeds8Bit && !KMime::isUsAscii(v_iew->s_ubject->text()))
+ n_eeds8Bit=true;
+
+ if (m_ode != mail) {
+ if (v_iew->g_roups->text().isEmpty()) {
+ KMessageBox::sorry(this, i18n("Please enter a newsgroup."));
+ return false;
+ }
+
+ int groupCount = QStringList::split(',',v_iew->g_roups->text()).count();
+ int fupCount = QStringList::split(',',v_iew->f_up2->currentText()).count();
+ bool followUp = !v_iew->f_up2->currentText().isEmpty();
+
+ if (groupCount>12) {
+ KMessageBox::sorry(this, i18n("You are crossposting to more than 12 newsgroups.\nPlease remove all newsgroups in which your article is off-topic."));
+ return false;
+ }
+
+ if (groupCount>5)
+ if (!(KMessageBox::warningYesNo( this, i18n("You are crossposting to more than five newsgroups.\nPlease reconsider whether this is really useful\nand remove groups in which your article is off-topic.\nDo you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+
+ if ( !followUp && groupCount > 2 ) {
+ if ( KMessageBox::warningYesNo( this,
+ i18n("You are crossposting to more than two newsgroups.\n"
+ "Please use the \"Followup-To\" header to direct the replies "
+ "to your article into one group.\n"
+ "Do you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"), i18n("edit article","&Edit"), "missingFollowUpTo" )
+ != KMessageBox::Yes )
+ return false;
+ }
+
+ if (fupCount>12) {
+ KMessageBox::sorry(this, i18n("You are directing replies to more than 12 newsgroups.\nPlease remove some newsgroups from the \"Followup-To\" header."));
+ return false;
+ }
+
+ if (fupCount>5)
+ if (!(KMessageBox::warningYesNo( this, i18n("You are directing replies to more than five newsgroups.\nPlease reconsider whether this is really useful.\nDo you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+ }
+
+ if (m_ode != news) {
+ if (v_iew->t_o->text().isEmpty() ) {
+ KMessageBox::sorry(this, i18n("Please enter the email address."));
+ return false;
+ }
+ if (!n_eeds8Bit && !KMime::isUsAscii(v_iew->t_o->text()))
+ n_eeds8Bit=true;
+ }
+
+ //GNKSA body checks
+ bool firstLine = true;
+ bool empty = true;
+ bool longLine = false;
+ bool hasAttributionLine = false;
+ int sigLength = 0;
+ int notQuoted = 0;
+ int textLines = 0;
+ QStringList text = v_iew->e_dit->processedText();
+
+ for (QStringList::Iterator it = text.begin(); it != text.end(); ++it) {
+
+ if (!n_eeds8Bit && !KMime::isUsAscii(*it))
+ n_eeds8Bit=true;
+
+ if (*it == "-- ") { // signature text
+ for (++it; it != text.end(); ++it) {
+
+ if (!n_eeds8Bit && !KMime::isUsAscii(*it))
+ n_eeds8Bit=true;
+
+ sigLength++;
+ if((*it).length()>80) {
+ longLine = true;
+ }
+ }
+ break;
+ }
+
+ if(!(*it).isEmpty()) {
+ empty = false;
+ textLines++;
+ if ((*it)[0]!='>') {
+ notQuoted++;
+ if (firstLine) hasAttributionLine = true;
+ }
+ }
+ if((*it).length()>80) {
+ longLine = true;
+ }
+
+ firstLine = false;
+ }
+
+ if (n_eeds8Bit && (c_harset.lower()=="us-ascii")) {
+ KMessageBox::sorry(this, i18n("Your message contains characters which are not included\nin the \"us-ascii\" character set; please choose\na suitable character set from the \"Options\" menu."));
+ return false;
+ }
+
+ if (empty) {
+ KMessageBox::sorry(this, i18n("You cannot post an empty message."));
+ return false;
+ }
+
+ if ((textLines>1)&&(notQuoted==1)) {
+ if (hasAttributionLine)
+ if (!(KMessageBox::warningYesNo( this, i18n("Your article seems to consist entirely of quoted text;\ndo you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+ } else {
+ if (notQuoted==0) {
+ KMessageBox::sorry(this, i18n("You cannot post an article consisting\n"
+ "entirely of quoted text."));
+ return false;
+ }
+ }
+
+ if (longLine)
+ if (!(KMessageBox::warningYesNo( this,
+ i18n("Your article contains lines longer than 80 characters.\n"
+ "Do you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),
+ i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+
+ if (sigLength>8) {
+ if (!(KMessageBox::warningYesNo( this, i18n("Your signature is more than 8 lines long.\nYou should shorten it to match the widely accepted limit of 4 lines.\nDo you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+ } else
+ if (sigLength>4)
+ KMessageBox::information(this, i18n("Your signature exceeds the widely-accepted limit of 4 lines:\nplease consider shortening your signature;\notherwise, you will probably annoy your readers."),
+ QString::null,"longSignatureWarning");
+
+ // check if article can be signed
+ if ( a_ctPGPsign->isChecked() ) {
+ // try to get the signing key
+ QCString signingKey = knGlobals.configManager()->identity()->signingKey();
+ KNNntpAccount *acc = knGlobals.accountManager()->account( a_rticle->serverId() );
+ if ( acc ) {
+ KMime::Headers::Newsgroups *grps = a_rticle->newsgroups();
+ KNGroup *grp = knGlobals.groupManager()->group( grps->firstGroup(), acc );
+ if (grp && grp->identity())
+ signingKey = grp->identity()->signingKey();
+ else if (acc->identity())
+ signingKey = acc->identity()->signingKey();
+ }
+
+ // the article can only be signed if we have a key
+ if (signingKey.isEmpty()) {
+ if ( KMessageBox::warningContinueCancel( this,
+ i18n("You have not configured your preferred "
+ "signing key yet;\n"
+ "please specify it in the global "
+ "identity configuration,\n"
+ "in the account properties or in the "
+ "group properties.\n"
+ "The article will be sent unsigned." ),
+ QString::null, i18n( "Send Unsigned" ),
+ "sendUnsignedDialog" )
+ == KMessageBox::Cancel )
+ return false;
+ }
+ }
+
+ v_alidated=true;
+ return true;
+}
+
+
+bool KNComposer::applyChanges()
+{
+ KMime::Content *text=0;
+ KNAttachment *a=0;
+
+ //Date
+ a_rticle->date()->setUnixTime(); //set current date+time
+
+ //Subject
+ a_rticle->subject()->fromUnicodeString(v_iew->s_ubject->text(), c_harset);
+
+ //Newsgroups
+ if (m_ode != mail) {
+ a_rticle->newsgroups()->fromUnicodeString(v_iew->g_roups->text().remove(QRegExp("\\s")), KMime::Headers::Latin1);
+ a_rticle->setDoPost(true);
+ } else
+ a_rticle->setDoPost(false);
+
+ //To
+ if (m_ode != news) {
+ a_rticle->to()->fromUnicodeString(v_iew->t_o->text(), c_harset);
+ a_rticle->setDoMail(true);
+ } else
+ a_rticle->setDoMail(false);
+
+ //Followup-To
+ if( a_rticle->doPost() && !v_iew->f_up2->currentText().isEmpty())
+ a_rticle->followUpTo()->fromUnicodeString(v_iew->f_up2->currentText(), KMime::Headers::Latin1);
+ else
+ a_rticle->removeHeader("Followup-To");
+
+ if(a_ttChanged && (v_iew->a_ttView)) {
+
+ QListViewItemIterator it(v_iew->a_ttView);
+ while(it.current()) {
+ a=(static_cast<AttachmentViewItem*> (it.current()))->attachment;
+ if(a->hasChanged()) {
+ if(a->isAttached())
+ a->updateContentInfo();
+ else
+ a->attach(a_rticle);
+ }
+ ++it;
+ }
+ }
+
+ for ( QValueList<KNAttachment*>::Iterator it = mDeletedAttachments.begin(); it != mDeletedAttachments.end(); ++it )
+ if ( (*it)->isAttached() )
+ (*it)->detach( a_rticle );
+
+ text=a_rticle->textContent();
+
+ if(!text) {
+ text=new KMime::Content();
+ KMime::Headers::ContentType *type=text->contentType();
+ KMime::Headers::CTEncoding *enc=text->contentTransferEncoding();
+ type->setMimeType("text/plain");
+ enc->setDecoded(true);
+ text->assemble();
+ a_rticle->addContent(text, true);
+ }
+
+ //set text
+ KNConfig::PostNewsTechnical *pnt=knGlobals.configManager()->postNewsTechnical();
+ if (v_alidated) {
+ if (n_eeds8Bit) {
+ text->contentType()->setCharset(c_harset);
+ if (pnt->allow8BitBody())
+ text->contentTransferEncoding()->setCte(KMime::Headers::CE8Bit);
+ else
+ text->contentTransferEncoding()->setCte(KMime::Headers::CEquPr);
+ } else {
+ text->contentType()->setCharset("us-ascii"); // fall back to us-ascii
+ text->contentTransferEncoding()->setCte(KMime::Headers::CE7Bit);
+ }
+ } else { // save as draft
+ text->contentType()->setCharset(c_harset);
+ if (c_harset.lower()=="us-ascii")
+ text->contentTransferEncoding()->setCte(KMime::Headers::CE7Bit);
+ else
+ text->contentTransferEncoding()->setCte(pnt->allow8BitBody()? KMime::Headers::CE8Bit : KMime::Headers::CEquPr);
+ }
+
+ //assemble the text line by line
+ QString tmp;
+ QStringList textLines = v_iew->e_dit->processedText();
+ for (QStringList::Iterator it = textLines.begin(); it != textLines.end(); ++it)
+ tmp += *it + "\n";
+
+ // Sign article if needed
+ if ( a_ctPGPsign->isChecked() ) {
+ // first get the signing key
+ QCString signingKey = knGlobals.configManager()->identity()->signingKey();
+ KNNntpAccount *acc = knGlobals.accountManager()->account( a_rticle->serverId() );
+ if ( acc ) {
+ KMime::Headers::Newsgroups *grps = a_rticle->newsgroups();
+ KNGroup *grp = knGlobals.groupManager()->group( grps->firstGroup(), acc );
+ if (grp && grp->identity())
+ signingKey = grp->identity()->signingKey();
+ else if (acc->identity())
+ signingKey = acc->identity()->signingKey();
+ }
+ // now try to sign the article
+ if (!signingKey.isEmpty()) {
+ QString tmpText = tmp;
+ Kpgp::Block block;
+ bool ok=true;
+ QTextCodec *codec=KGlobal::charsets()->codecForName(c_harset, ok);
+ if(!ok) // no suitable codec found => try local settings and hope the best ;-)
+ codec=KGlobal::locale()->codecForEncoding();
+
+ block.setText( codec->fromUnicode(tmpText) );
+ kdDebug(5003) << "signing article from " << article()->from()->email() << endl;
+ if( block.clearsign( signingKey, codec->name() ) == Kpgp::Ok ) {
+ QCString result = block.text();
+ tmp = codec->toUnicode(result.data(), result.length() );
+ } else {
+ return false;
+ }
+ }
+ }
+
+ text->fromUnicodeString(tmp);
+
+ //text is set and all attached contents have been assembled => now set lines
+ a_rticle->lines()->setNumberOfLines(a_rticle->lineCount());
+
+ a_rticle->assemble();
+ a_rticle->updateListItem();
+ return true;
+}
+
+
+void KNComposer::closeEvent(QCloseEvent *e)
+{
+ if(!v_iew->e_dit->isModified() && !a_ttChanged) { // nothing to save, don't show nag screen
+ if(a_rticle->id()==-1)
+ r_esult=CRdel;
+ else
+ r_esult=CRcancel;
+ }
+ else {
+ switch ( KMessageBox::warningYesNoCancel( this, i18n("Do you want to save this article in the draft folder?"),
+ QString::null, KStdGuiItem::save(), KStdGuiItem::discard())) {
+ case KMessageBox::Yes :
+ r_esult=CRsave;
+ break;
+ case KMessageBox::No :
+ if (a_rticle->id()==-1) r_esult=CRdel;
+ else r_esult=CRcancel;
+ break;
+ default: // cancel
+ e->ignore();
+ return;
+ }
+ }
+
+ e->accept();
+ emit composerDone(this);
+ // we're dead at this point, don't access members!
+}
+
+
+void KNComposer::initData(const QString &text)
+{
+ //Subject
+ if(a_rticle->subject()->isEmpty())
+ slotSubjectChanged(QString::null);
+ else
+ v_iew->s_ubject->setText(a_rticle->subject()->asUnicodeString());
+
+ //Newsgroups
+ v_iew->g_roups->setText(a_rticle->newsgroups()->asUnicodeString());
+
+ //To
+ v_iew->t_o->setText(a_rticle->to()->asUnicodeString());
+
+ //Followup-To
+ KMime::Headers::FollowUpTo *fup2=a_rticle->followUpTo(false);
+ if(fup2 && !fup2->isEmpty())
+ v_iew->f_up2->lineEdit()->setText(fup2->asUnicodeString());
+
+ KMime::Content *textContent=a_rticle->textContent();
+ QString s;
+
+ if(text.isEmpty()) {
+ if(textContent)
+ textContent->decodedText(s);
+ } else
+ s = text;
+
+ v_iew->e_dit->setText(s);
+
+ // initialize the charset select action
+ if(textContent)
+ c_harset=textContent->contentType()->charset();
+ else
+ c_harset=knGlobals.configManager()->postNewsTechnical()->charset();
+
+ a_ctSetCharset->setCurrentItem(knGlobals.configManager()->postNewsTechnical()->indexForCharset(c_harset));
+
+ // initialize the message type select action
+ if (a_rticle->doPost() && a_rticle->doMail())
+ m_ode = news_mail;
+ else
+ if (a_rticle->doPost())
+ m_ode = news;
+ else
+ m_ode = mail;
+ setMessageMode(m_ode);
+
+ if(a_rticle->contentType()->isMultipart()) {
+ v_iew->showAttachmentView();
+ KMime::Content::List attList;
+ AttachmentViewItem *item=0;
+ attList.setAutoDelete(false);
+ a_rticle->attachments(&attList);
+ for(KMime::Content *c=attList.first(); c; c=attList.next()) {
+ item=new AttachmentViewItem(v_iew->a_ttView, new KNAttachment(c));
+ }
+ }
+}
+
+
+// inserts at cursor position if clear is false, replaces content otherwise
+// puts the file content into a box if box==true
+// "file" is already open for reading
+void KNComposer::insertFile(QFile *file, bool clear, bool box, QString boxTitle)
+{
+ QString temp;
+ bool ok=true;
+ QTextCodec *codec=KGlobal::charsets()->codecForName(c_harset, ok);
+ QTextStream ts(file);
+ ts.setCodec(codec);
+
+ if (box)
+ temp = QString::fromLatin1(",----[ %1 ]\n").arg(boxTitle);
+
+ if (box && (v_iew->e_dit->wordWrap()!=QMultiLineEdit::NoWrap)) {
+ int wrapAt = v_iew->e_dit->wrapColumnOrWidth();
+ QStringList lst;
+ QString line;
+ while(!file->atEnd()) {
+ line=ts.readLine();
+ if (!file->atEnd())
+ line+="\n";
+ lst.append(line);
+ }
+ temp+=KNHelper::rewrapStringList(lst, wrapAt, '|', false, true);
+ } else {
+ while(!file->atEnd()) {
+ if (box)
+ temp+="| ";
+ temp+=ts.readLine();
+ if (!file->atEnd())
+ temp += "\n";
+ }
+ }
+
+ if (box)
+ temp += QString::fromLatin1("`----");
+
+ if(clear)
+ v_iew->e_dit->setText(temp);
+ else
+ v_iew->e_dit->insert(temp);
+}
+
+
+// ask for a filename, handle network urls
+void KNComposer::insertFile(bool clear, bool box)
+{
+ KNLoadHelper helper(this);
+ QFile *file = helper.getFile(i18n("Insert File"));
+ KURL url;
+ QString boxName;
+
+ if (file) {
+ url = helper.getURL();
+
+ if (url.isLocalFile())
+ boxName = url.path();
+ else
+ boxName = url.prettyURL();
+
+ insertFile(file,clear,box,boxName);
+ }
+}
+
+
+//-------------------------------- <Actions> ------------------------------------
+
+
+void KNComposer::addRecentAddress()
+{
+ if( !v_iew->t_o->isHidden() )
+ RecentAddresses::self(knGlobals.config())->add( v_iew->t_o->text() );
+}
+
+void KNComposer::slotSendNow()
+{
+ r_esult=CRsendNow;
+ addRecentAddress();
+ emit composerDone(this);
+}
+
+
+void KNComposer::slotSendLater()
+{
+ r_esult=CRsendLater;
+ addRecentAddress();
+ emit composerDone(this);
+}
+
+
+void KNComposer::slotSaveAsDraft()
+{
+ r_esult=CRsave;
+ addRecentAddress();
+ emit composerDone(this);
+}
+
+
+void KNComposer::slotArtDelete()
+{
+ r_esult=CRdelAsk;
+ emit composerDone(this);
+}
+
+
+void KNComposer::slotAppendSig()
+{
+ if(!s_ignature.isEmpty()) {
+ v_iew->e_dit->append("\n"+s_ignature);
+ v_iew->e_dit->setModified(true);
+ }
+}
+
+
+void KNComposer::slotInsertFile()
+{
+ insertFile(false,false);
+}
+
+
+void KNComposer::slotInsertFileBoxed()
+{
+ insertFile(false,true);
+}
+
+
+void KNComposer::slotAttachFile()
+{
+ KNLoadHelper *helper = new KNLoadHelper(this);
+
+ if (helper->getFile(i18n("Attach File"))) {
+ if (!v_iew->v_iewOpen) {
+ KNHelper::saveWindowSize("composer", size());
+ v_iew->showAttachmentView();
+ }
+ (void) new AttachmentViewItem(v_iew->a_ttView, new KNAttachment(helper));
+ a_ttChanged=true;
+ } else {
+ delete helper;
+ }
+}
+
+
+void KNComposer::slotRemoveAttachment()
+{
+ if(!v_iew->v_iewOpen) return;
+
+ if(v_iew->a_ttView->currentItem()) {
+ AttachmentViewItem *it=static_cast<AttachmentViewItem*>(v_iew->a_ttView->currentItem());
+ if(it->attachment->isAttached()) {
+ mDeletedAttachments.append( it->attachment );
+ it->attachment=0;
+ }
+ delete it;
+
+ if(v_iew->a_ttView->childCount()==0) {
+ KNHelper::saveWindowSize("composerAtt", size());
+ v_iew->hideAttachmentView();
+ }
+
+ a_ttChanged=true;
+ }
+}
+
+void KNComposer::slotAttachmentProperties()
+{
+ if(!v_iew->v_iewOpen) return;
+
+ if(v_iew->a_ttView->currentItem()) {
+ AttachmentViewItem *it=static_cast<AttachmentViewItem*>(v_iew->a_ttView->currentItem());
+ AttachmentPropertiesDlg *d=new AttachmentPropertiesDlg(it->attachment, this);
+ if(d->exec()) {
+ d->apply();
+ it->setText(1, it->attachment->mimeType());
+ it->setText(3, it->attachment->description());
+ it->setText(4, it->attachment->encoding());
+ }
+ delete d;
+ a_ttChanged=true;
+ }
+}
+
+
+void KNComposer::slotToggleDoPost()
+{
+ if (a_ctDoPost->isChecked()) {
+ if (a_ctDoMail->isChecked())
+ m_ode=news_mail;
+ else
+ m_ode=news;
+ } else {
+ if (a_ctDoMail->isChecked())
+ m_ode=mail;
+ else { // invalid
+ a_ctDoPost->setChecked(true); //revert
+ return;
+ }
+ }
+ setMessageMode(m_ode);
+}
+
+
+void KNComposer::slotToggleDoMail()
+{
+ if (a_ctDoMail->isChecked()) {
+ if (a_uthorDislikesMailCopies) {
+ if (!(KMessageBox::warningContinueCancel(this, i18n("The poster does not want a mail copy of your reply (Mail-Copies-To: nobody);\nplease respect their request."),
+ QString::null, i18n("&Send Copy")) == KMessageBox::Continue)) {
+ a_ctDoMail->setChecked(false); //revert
+ return;
+ }
+ }
+
+ if (knGlobals.configManager()->postNewsTechnical()->useExternalMailer()) {
+ QString s = v_iew->e_dit->textLine(0);
+ if (!s.contains(i18n("<posted & mailed>")))
+ v_iew->e_dit->insertAt(i18n("<posted & mailed>\n\n"),0,0);
+ QString tmp;
+ QStringList textLines = v_iew->e_dit->processedText();
+ for (QStringList::Iterator it = textLines.begin(); it != textLines.end(); ++it) {
+ if (*it == "-- ") // try to be smart, don't include the signature,
+ break; // kmail will append one, too.
+ tmp+=*it+"\n";
+ }
+ knGlobals.artFactory->sendMailExternal(v_iew->t_o->text(), v_iew->s_ubject->text(), tmp);
+ a_ctDoMail->setChecked(false); //revert
+ return;
+ } else {
+ if (a_ctDoPost->isChecked())
+ m_ode=news_mail;
+ else
+ m_ode=mail;
+ }
+ } else {
+ if (a_ctDoPost->isChecked())
+ m_ode=news;
+ else { // invalid
+ a_ctDoMail->setChecked(true); //revert
+ return;
+ }
+ }
+ setMessageMode(m_ode);
+}
+
+
+void KNComposer::slotSetCharset(const QString &s)
+{
+ if(s.isEmpty())
+ return;
+
+ c_harset=s.latin1();
+ setConfig(true); //adjust fonts
+}
+
+
+void KNComposer::slotSetCharsetKeyboard()
+{
+ int newCS = KNHelper::selectDialog(this, i18n("Select Charset"), a_ctSetCharset->items(), a_ctSetCharset->currentItem());
+ if (newCS != -1) {
+ a_ctSetCharset->setCurrentItem(newCS);
+ slotSetCharset(*(a_ctSetCharset->items().at(newCS)));
+ }
+}
+
+
+void KNComposer::slotToggleWordWrap()
+{
+ v_iew->e_dit->setWordWrap(a_ctWordWrap->isChecked()? QMultiLineEdit::FixedColumnWidth : QMultiLineEdit::NoWrap);
+}
+
+
+void KNComposer::slotUndoRewrap()
+{
+ if (KMessageBox::warningContinueCancel( this, i18n("This will replace all text you have written.")) == KMessageBox::Continue) {
+ v_iew->e_dit->setText(u_nwraped);
+ slotAppendSig();
+ }
+}
+
+void KNComposer::slotExternalEditor()
+{
+ if(e_xternalEditor) // in progress...
+ return;
+
+ QString editorCommand=knGlobals.configManager()->postNewsComposer()->externalEditor();
+
+ if(editorCommand.isEmpty())
+ KMessageBox::sorry(this, i18n("No editor configured.\nPlease do this in the settings dialog."));
+
+ if(e_ditorTempfile) { // shouldn't happen...
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+ }
+
+ e_ditorTempfile=new KTempFile();
+
+ if(e_ditorTempfile->status()!=0) {
+ KNHelper::displayTempFileError(this);
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+ return;
+ }
+
+ bool ok=true;
+ QTextCodec *codec=KGlobal::charsets()->codecForName(c_harset, ok);
+
+ QString tmp;
+ QStringList textLines = v_iew->e_dit->processedText();
+ for (QStringList::Iterator it = textLines.begin(); it != textLines.end();) {
+ tmp += *it;
+ ++it;
+ if (it != textLines.end())
+ tmp+="\n";
+ }
+
+ QCString local = codec->fromUnicode(tmp);
+ e_ditorTempfile->file()->writeBlock(local.data(),local.length());
+ e_ditorTempfile->file()->flush();
+
+ if(e_ditorTempfile->status()!=0) {
+ KNHelper::displayTempFileError(this);
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+ return;
+ }
+
+ e_xternalEditor=new KProcess();
+
+ // construct command line...
+ QStringList command = QStringList::split(' ',editorCommand);
+ bool filenameAdded=false;
+ for ( QStringList::Iterator it = command.begin(); it != command.end(); ++it ) {
+ if ((*it).contains("%f")) {
+ (*it).replace(QRegExp("%f"),e_ditorTempfile->name());
+ filenameAdded=true;
+ }
+ (*e_xternalEditor) << (*it);
+ }
+ if(!filenameAdded) // no %f in the editor command
+ (*e_xternalEditor) << e_ditorTempfile->name();
+
+ connect(e_xternalEditor, SIGNAL(processExited(KProcess *)),this, SLOT(slotEditorFinished(KProcess *)));
+ if(!e_xternalEditor->start()) {
+ KMessageBox::error(this, i18n("Unable to start external editor.\nPlease check your configuration in the settings dialog."));
+ delete e_xternalEditor;
+ e_xternalEditor=0;
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+ return;
+ }
+
+ a_ctExternalEditor->setEnabled(false); // block other edit action while the editor is running...
+ a_ctSpellCheck->setEnabled(false);
+ v_iew->showExternalNotification();
+}
+
+
+void KNComposer::slotSpellcheck()
+{
+ if(s_pellChecker) // in progress...
+ return;
+ spellLineEdit = !spellLineEdit;
+ a_ctExternalEditor->setEnabled(false);
+ a_ctSpellCheck->setEnabled(false);
+
+ s_pellChecker = new KSpell(this, i18n("Spellcheck"), this, SLOT(slotSpellStarted(KSpell *)));
+ QStringList l = KSpellingHighlighter::personalWords();
+ for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
+ s_pellChecker->addPersonal( *it );
+ }
+ connect(s_pellChecker, SIGNAL(death()), this, SLOT(slotSpellFinished()));
+ connect(s_pellChecker, SIGNAL(done(const QString&)), this, SLOT(slotSpellDone(const QString&)));
+ connect(s_pellChecker, SIGNAL(misspelling (const QString &, const QStringList &, unsigned int)),
+ this, SLOT(slotMisspelling (const QString &, const QStringList &, unsigned int)));
+ connect(s_pellChecker, SIGNAL(corrected (const QString &, const QString &, unsigned int)),
+ this, SLOT(slotCorrected (const QString &, const QString &, unsigned int)));
+}
+
+
+void KNComposer::slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos)
+{
+ if( spellLineEdit )
+ v_iew->s_ubject->spellCheckerMisspelling( text, lst, pos);
+ else
+ v_iew->e_dit->misspelling(text, lst, pos);
+
+}
+
+void KNComposer::slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos)
+{
+ if( spellLineEdit )
+ v_iew->s_ubject->spellCheckerCorrected( oldWord, newWord, pos);
+ else
+ v_iew->e_dit->corrected(oldWord, newWord, pos);
+}
+
+void KNComposer::slotUpdateStatusBar()
+{
+ QString typeDesc;
+ switch (m_ode) {
+ case news: typeDesc = i18n("News Article");
+ break;
+ case mail: typeDesc = i18n("Email");
+ break;
+ default : typeDesc = i18n("News Article & Email");
+ }
+ QString overwriteDesc;
+ if (v_iew->e_dit->isOverwriteMode())
+ overwriteDesc = i18n(" OVR ");
+ else
+ overwriteDesc = i18n(" INS ");
+
+ statusBar()->changeItem(i18n(" Type: %1 ").arg(typeDesc), 1);
+ statusBar()->changeItem(i18n(" Charset: %1 ").arg(c_harset), 2);
+ statusBar()->changeItem(overwriteDesc, 3);
+ statusBar()->changeItem(i18n(" Column: %1 ").arg(v_iew->e_dit->currentColumn() + 1), 4);
+ statusBar()->changeItem(i18n(" Line: %1 ").arg(v_iew->e_dit->currentLine() + 1), 5);
+}
+
+
+void KNComposer::slotUpdateCursorPos()
+{
+ statusBar()->changeItem(i18n(" Column: %1 ").arg(v_iew->e_dit->currentColumn() + 1), 4);
+ statusBar()->changeItem(i18n(" Line: %1 ").arg(v_iew->e_dit->currentLine() + 1), 5);
+}
+
+
+void KNComposer::slotConfKeys()
+{
+ KKeyDialog::configure(actionCollection(), this, true);
+}
+
+
+void KNComposer::slotConfToolbar()
+{
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("composerWindow_options");
+ saveMainWindowSettings(conf);
+ KEditToolbar dlg(guiFactory(),this);
+ connect(&dlg,SIGNAL( newToolbarConfig() ), this, SLOT( slotNewToolbarConfig() ));
+ dlg.exec();
+}
+
+void KNComposer::slotNewToolbarConfig()
+{
+ createGUI("kncomposerui.rc");
+
+ a_ttPopup=static_cast<QPopupMenu*> (factory()->container("attachment_popup", this));
+ if(!a_ttPopup) a_ttPopup = new QPopupMenu();
+
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("composerWindow_options");
+ applyMainWindowSettings(conf);
+}
+
+//-------------------------------- </Actions> -----------------------------------
+
+
+void KNComposer::slotSubjectChanged(const QString &t)
+{
+ // replace newlines
+ QString subject = t;
+ subject.replace( '\n', ' ' );
+ subject.replace( '\r', ' ' );
+ if ( subject != t ) // setText() sets the cursor to the end
+ v_iew->s_ubject->setText( subject );
+ // update caption
+ if( !subject.isEmpty() )
+ setCaption( subject );
+ else
+ setCaption( i18n("No Subject") );
+}
+
+
+void KNComposer::slotGroupsChanged(const QString &t)
+{
+ KQCStringSplitter split;
+ bool splitOk;
+ QString currText=v_iew->f_up2->currentText();
+
+ v_iew->f_up2->clear();
+
+ split.init(t.latin1(), ",");
+ splitOk=split.first();
+ while(splitOk) {
+ v_iew->f_up2->insertItem(QString::fromLatin1(split.string()));
+ splitOk=split.next();
+ }
+ v_iew->f_up2->insertItem("");
+
+ if ( !currText.isEmpty() || !mFirstEdit ) // user might have cleared fup2 intentionally during last edit
+ v_iew->f_up2->lineEdit()->setText(currText);
+}
+
+
+void KNComposer::slotToBtnClicked()
+{
+ AddressesDialog dlg( this );
+ QString txt;
+ QString to = v_iew->t_o->text();
+ dlg.setShowBCC(false);
+ dlg.setShowCC(false);
+#if 0
+ QStringList lst;
+
+
+ txt = mEdtTo->text().stripWhiteSpace();
+ if ( !txt.isEmpty() ) {
+ lst = KMMessage::splitEmailAddrList( txt );
+ dlg.setSelectedTo( lst );
+ }
+#endif
+ dlg.setRecentAddresses( RecentAddresses::self(knGlobals.config())->kabcAddresses() );
+ if (dlg.exec()==QDialog::Rejected) return;
+
+ if(!to.isEmpty())
+ to+=", ";
+ to+=dlg.to().join(", ");
+
+ v_iew->t_o->setText(to);
+
+}
+
+
+void KNComposer::slotGroupsBtnClicked()
+{
+ int id=a_rticle->serverId();
+ KNNntpAccount *nntp=0;
+
+ if(id!=-1)
+ nntp=knGlobals.accountManager()->account(id);
+
+ if(!nntp)
+ nntp=knGlobals.accountManager()->first();
+
+ if(!nntp) {
+ KMessageBox::error(this, i18n("You have no valid news accounts configured."));
+ v_iew->g_roups->clear();
+ return;
+ }
+
+ if(id==-1)
+ a_rticle->setServerId(nntp->id());
+
+ KNGroupSelectDialog *dlg=new KNGroupSelectDialog(this, nntp, v_iew->g_roups->text().remove(QRegExp("\\s")));
+
+ connect(dlg, SIGNAL(loadList(KNNntpAccount*)),
+ knGlobals.groupManager(), SLOT(slotLoadGroupList(KNNntpAccount*)));
+ connect(knGlobals.groupManager(), SIGNAL(newListReady(KNGroupListData*)),
+ dlg, SLOT(slotReceiveList(KNGroupListData*)));
+
+ if(dlg->exec())
+ v_iew->g_roups->setText(dlg->selectedGroups());
+
+ delete dlg;
+}
+
+
+void KNComposer::slotEditorFinished(KProcess *)
+{
+ if(e_xternalEditor->normalExit()) {
+ e_ditorTempfile->file()->close();
+ e_ditorTempfile->file()->open(IO_ReadOnly);
+ insertFile(e_ditorTempfile->file(), true);
+ e_xternalEdited=true;
+ }
+
+ slotCancelEditor(); // cleanup...
+}
+
+
+void KNComposer::slotCancelEditor()
+{
+ delete e_xternalEditor; // this also kills the editor process if it's still running
+ e_xternalEditor=0;
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+
+ a_ctExternalEditor->setEnabled(true);
+ a_ctSpellCheck->setEnabled(true);
+ v_iew->hideExternalNotification();
+}
+
+
+void KNComposer::slotAttachmentPopup(KListView*, QListViewItem *it, const QPoint &p)
+{
+ if(it)
+ a_ttPopup->popup(p);
+}
+
+
+void KNComposer::slotAttachmentSelected(QListViewItem *it)
+{
+ if(v_iew->a_ttWidget) {
+ v_iew->a_ttRemoveBtn->setEnabled((it!=0));
+ v_iew->a_ttEditBtn->setEnabled((it!=0));
+ }
+}
+
+
+void KNComposer::slotAttachmentEdit(QListViewItem *)
+{
+ slotAttachmentProperties();
+}
+
+
+void KNComposer::slotAttachmentRemove(QListViewItem *)
+{
+ slotRemoveAttachment();
+}
+
+
+//==============================================================================
+// spellchecking code copied form kedit (Bernd Johannes Wuebben)
+//==============================================================================
+
+
+void KNComposer::slotSpellStarted( KSpell *)
+{
+ if( !spellLineEdit )
+ {
+ v_iew->e_dit->spellcheck_start();
+ s_pellChecker->setProgressResolution(2);
+
+ // read the quote indicator from the preferences
+ KConfig *config=knGlobals.config();
+ KConfigGroupSaver saver(config, "READNEWS");
+ QString quotePrefix;
+ quotePrefix = config->readEntry("quoteCharacters",">");
+//todo fixme
+//quotePrefix = mComposer->msg()->formatString(quotePrefix);
+
+ kdDebug(5003) << "spelling: new SpellingFilter with prefix=\"" << quotePrefix << "\"" << endl;
+ mSpellingFilter = new SpellingFilter(v_iew->e_dit->text(), quotePrefix, SpellingFilter::FilterUrls,
+ SpellingFilter::FilterEmailAddresses);
+
+ s_pellChecker->check(mSpellingFilter->filteredText());
+ }
+ else
+ s_pellChecker->check( v_iew->s_ubject->text());
+}
+
+void KNComposer::slotSpellDone(const QString &newtext)
+{
+ a_ctExternalEditor->setEnabled(true);
+ a_ctSpellCheck->setEnabled(true);
+ if ( !spellLineEdit )
+ v_iew->e_dit->spellcheck_stop();
+
+ int dlgResult = s_pellChecker->dlgResult();
+ if ( dlgResult == KS_CANCEL )
+ {
+ if( spellLineEdit)
+ {
+ //stop spell check
+ spellLineEdit = false;
+ QString tmpText( newtext);
+ tmpText = tmpText.remove('\n');
+
+ if( tmpText != v_iew->s_ubject->text() )
+ v_iew->s_ubject->setText( tmpText );
+ }
+ else
+ {
+ kdDebug(5003) << "spelling: canceled - restoring text from SpellingFilter" << endl;
+ kdDebug(5003)<<" mSpellingFilter->originalText() :"<<mSpellingFilter->originalText()<<endl;
+ v_iew->e_dit->setText(mSpellingFilter->originalText());
+
+ //v_iew->e_dit->setModified(mWasModifiedBeforeSpellCheck);
+ }
+ }
+ s_pellChecker->cleanUp();
+ KDictSpellingHighlighter::dictionaryChanged();
+}
+
+
+void KNComposer::slotSpellFinished()
+{
+ a_ctExternalEditor->setEnabled(true);
+ a_ctSpellCheck->setEnabled(true);
+ KSpell::spellStatus status=s_pellChecker->status();
+ delete s_pellChecker;
+ s_pellChecker=0;
+
+ kdDebug(5003) << "spelling: delete SpellingFilter" << endl;
+ delete mSpellingFilter;
+ mSpellingFilter = 0;
+
+ if(status==KSpell::Error) {
+ KMessageBox::error(this, i18n("ISpell could not be started.\n"
+ "Please make sure you have ISpell properly configured and in your PATH."));
+ }
+ else if(status==KSpell::Crashed) {
+ v_iew->e_dit->spellcheck_stop();
+ KMessageBox::error(this, i18n("ISpell seems to have crashed."));
+ }
+ else
+ {
+ if( spellLineEdit )
+ slotSpellcheck();
+ else if( status == KSpell::FinishedNoMisspellingsEncountered )
+ KMessageBox::information( this, i18n("No misspellings encountered."));
+ }
+}
+
+
+void KNComposer::slotDragEnterEvent(QDragEnterEvent *ev)
+{
+ QStringList files;
+ ev->accept(KURLDrag::canDecode(ev));
+}
+
+
+void KNComposer::slotDropEvent(QDropEvent *ev)
+{
+ KURL::List urls;
+
+ if (!KURLDrag::decode(ev, urls))
+ return;
+
+ for (KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it) {
+ const KURL &url = *it;
+ KNLoadHelper *helper = new KNLoadHelper(this);
+
+ if (helper->setURL(url)) {
+ if (!v_iew->v_iewOpen) {
+ KNHelper::saveWindowSize("composer", size());
+ v_iew->showAttachmentView();
+ }
+ (void) new AttachmentViewItem(v_iew->a_ttView, new KNAttachment(helper));
+ a_ttChanged=true;
+ } else {
+ delete helper;
+ }
+ }
+}
+
+
+void KNComposer::dragEnterEvent(QDragEnterEvent *ev)
+{
+ slotDragEnterEvent(ev);
+}
+
+
+void KNComposer::dropEvent(QDropEvent *ev)
+{
+ slotDropEvent(ev);
+}
+
+QPopupMenu * KNComposer::popupMenu( const QString& name )
+{
+ Q_ASSERT(factory());
+ if ( factory() )
+ return ((QPopupMenu*)factory()->container( name, this ));
+ return 0L;
+}
+
+
+//=====================================================================================
+
+
+KNComposer::ComposerView::ComposerView(KNComposer *composer, const char *n)
+ : QSplitter(QSplitter::Vertical, composer, n), a_ttWidget(0), a_ttView(0), v_iewOpen(false)
+{
+ QWidget *main=new QWidget(this);
+
+ //headers
+ QFrame *hdrFrame=new QFrame(main);
+ hdrFrame->setFrameStyle(QFrame::Box | QFrame::Sunken);
+ QGridLayout *hdrL=new QGridLayout(hdrFrame, 4,3, 7,5);
+ hdrL->setColStretch(1,1);
+
+ //To
+ t_o=new KNLineEdit(this, true, hdrFrame);
+ mEdtList.append(t_o);
+
+ l_to=new QLabel(t_o, i18n("T&o:"), hdrFrame);
+ t_oBtn=new QPushButton(i18n("&Browse..."), hdrFrame);
+ hdrL->addWidget(l_to, 0,0);
+ hdrL->addWidget(t_o, 0,1);
+ hdrL->addWidget(t_oBtn, 0,2);
+ connect(t_oBtn, SIGNAL(clicked()), parent(), SLOT(slotToBtnClicked()));
+
+ //Newsgroups
+ g_roups=new KNLineEdit(this, false, hdrFrame);
+ mEdtList.append(g_roups);
+
+ l_groups=new QLabel(g_roups, i18n("&Groups:"), hdrFrame);
+ g_roupsBtn=new QPushButton(i18n("B&rowse..."), hdrFrame);
+ hdrL->addWidget(l_groups, 1,0);
+ hdrL->addWidget(g_roups, 1,1);
+ hdrL->addWidget(g_roupsBtn, 1,2);
+ connect(g_roups, SIGNAL(textChanged(const QString&)),
+ parent(), SLOT(slotGroupsChanged(const QString&)));
+ connect(g_roupsBtn, SIGNAL(clicked()), parent(), SLOT(slotGroupsBtnClicked()));
+
+ //Followup-To
+ f_up2=new KComboBox(true, hdrFrame);
+ l_fup2=new QLabel(f_up2, i18n("Follo&wup-To:"), hdrFrame);
+ hdrL->addWidget(l_fup2, 2,0);
+ hdrL->addMultiCellWidget(f_up2, 2,2, 1,2);
+
+ //subject
+ s_ubject=new KNLineEditSpell(this, false, hdrFrame);
+ mEdtList.append(s_ubject);
+
+ QLabel *l=new QLabel(s_ubject, i18n("S&ubject:"), hdrFrame);
+ hdrL->addWidget(l, 3,0);
+ hdrL->addMultiCellWidget(s_ubject, 3,3, 1,2);
+ connect(s_ubject, SIGNAL(textChanged(const QString&)),
+ parent(), SLOT(slotSubjectChanged(const QString&)));
+
+ //Editor
+ e_dit=new Editor(this, composer, main);
+ e_dit->setMinimumHeight(50);
+
+ KConfig *config = knGlobals.config();
+ KConfigGroupSaver saver(config, "VISUAL_APPEARANCE");
+ QColor defaultColor1( kapp->palette().active().text()); // defaults from kmreaderwin.cpp
+ QColor defaultColor2( kapp->palette().active().text() );
+ QColor defaultColor3( kapp->palette().active().text() );
+ QColor defaultForeground( kapp->palette().active().text() );
+ QColor col1 = config->readColorEntry( "ForegroundColor", &defaultForeground );
+ QColor col2 = config->readColorEntry( "quote3Color", &defaultColor3 );
+ QColor col3 = config->readColorEntry( "quote2Color", &defaultColor2 );
+ QColor col4 = config->readColorEntry( "quote1Color", &defaultColor1 );
+ QColor c = QColor("red");
+ mSpellChecker = new KDictSpellingHighlighter(e_dit, /*active*/ true, /*autoEnabled*/ true,
+ /*spellColor*/ config->readColorEntry("NewMessage", &c),
+ /*colorQuoting*/ true, col1, col2, col3, col4);
+ connect( mSpellChecker, SIGNAL(newSuggestions(const QString&, const QStringList&, unsigned int)), e_dit,
+ SLOT(slotAddSuggestion(const QString&, const QStringList&, unsigned int)) );
+
+ QVBoxLayout *notL=new QVBoxLayout(e_dit);
+ notL->addStretch(1);
+ n_otification=new QGroupBox(2, Qt::Horizontal, e_dit);
+ l=new QLabel(i18n("You are currently editing the article body\nin an external editor. To continue, you have\nto close the external editor."), n_otification);
+ c_ancelEditorBtn=new QPushButton(i18n("&Kill External Editor"), n_otification);
+ n_otification->setFrameStyle(QFrame::Panel | QFrame::Raised);
+ n_otification->setLineWidth(2);
+ n_otification->hide();
+ notL->addWidget(n_otification, 0, Qt::AlignHCenter);
+ notL->addStretch(1);
+
+ //finish GUI
+ QVBoxLayout *topL=new QVBoxLayout(main, 4,4);
+ topL->addWidget(hdrFrame);
+ topL->addWidget(e_dit, 1);
+}
+
+
+KNComposer::ComposerView::~ComposerView()
+{
+ if(v_iewOpen) {
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("POSTNEWS");
+
+ conf->writeEntry("Att_Splitter",sizes()); // save splitter pos
+
+ QValueList<int> lst; // save header sizes
+ QHeader *h=a_ttView->header();
+ for (int i=0; i<5; i++)
+ lst << h->sectionSize(i);
+ conf->writeEntry("Att_Headers",lst);
+ }
+ delete mSpellChecker;
+}
+
+
+void KNComposer::ComposerView::focusNextPrevEdit(const QWidget* aCur, bool aNext)
+{
+ QValueList<QWidget*>::Iterator it;
+
+ if ( !aCur ) {
+ it = --( mEdtList.end() );
+ } else {
+ for ( QValueList<QWidget*>::Iterator it2 = mEdtList.begin(); it2 != mEdtList.end(); ++it2 ) {
+ if ( (*it2) == aCur ) {
+ it = it2;
+ break;
+ }
+ }
+ if ( it == mEdtList.end() )
+ return;
+ if ( aNext )
+ ++it;
+ else {
+ if ( it != mEdtList.begin() )
+ --it;
+ else
+ return;
+ }
+ }
+ if ( it != mEdtList.end() ) {
+ if ( (*it)->isVisible() )
+ (*it)->setFocus();
+ } else if ( aNext )
+ e_dit->setFocus();
+}
+
+
+void KNComposer::ComposerView::setMessageMode(KNComposer::MessageMode mode)
+{
+ if (mode != KNComposer::news) {
+ l_to->show();
+ t_o->show();
+ t_oBtn->show();
+ } else {
+ l_to->hide();
+ t_o->hide();
+ t_oBtn->hide();
+ }
+ if (mode != KNComposer::mail) {
+ l_groups->show();
+ l_fup2->show();
+ g_roups->show();
+ f_up2->show();
+ g_roupsBtn->show();
+
+ } else {
+ l_groups->hide();
+ l_fup2->hide();
+ g_roups->hide();
+ f_up2->hide();
+ g_roupsBtn->hide();
+ }
+}
+
+void KNComposer::ComposerView::restartBackgroundSpellCheck()
+{
+ mSpellChecker->restartBackgroundSpellCheck();
+}
+
+void KNComposer::ComposerView::showAttachmentView()
+{
+ if(!a_ttWidget) {
+ a_ttWidget=new QWidget(this);
+ QGridLayout *topL=new QGridLayout(a_ttWidget, 3, 2, 4, 4);
+
+ a_ttView=new AttachmentView(a_ttWidget);
+ topL->addMultiCellWidget(a_ttView, 0,2, 0,0);
+
+ //connections
+ connect(a_ttView, SIGNAL(currentChanged(QListViewItem*)),
+ parent(), SLOT(slotAttachmentSelected(QListViewItem*)));
+ connect(a_ttView, SIGNAL(clicked ( QListViewItem * )),
+ parent(), SLOT(slotAttachmentSelected(QListViewItem*)));
+
+ connect(a_ttView, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
+ parent(), SLOT(slotAttachmentPopup(KListView*, QListViewItem*, const QPoint&)));
+ connect(a_ttView, SIGNAL(delPressed(QListViewItem*)),
+ parent(), SLOT(slotAttachmentRemove(QListViewItem*)));
+ connect(a_ttView, SIGNAL(doubleClicked(QListViewItem*)),
+ parent(), SLOT(slotAttachmentEdit(QListViewItem*)));
+ connect(a_ttView, SIGNAL(returnPressed(QListViewItem*)),
+ parent(), SLOT(slotAttachmentEdit(QListViewItem*)));
+
+ //buttons
+ a_ttAddBtn=new QPushButton(i18n("A&dd..."),a_ttWidget);
+ connect(a_ttAddBtn, SIGNAL(clicked()), parent(), SLOT(slotAttachFile()));
+ topL->addWidget(a_ttAddBtn, 0,1);
+
+ a_ttRemoveBtn=new QPushButton(i18n("&Remove"), a_ttWidget);
+ a_ttRemoveBtn->setEnabled(false);
+ connect(a_ttRemoveBtn, SIGNAL(clicked()), parent(), SLOT(slotRemoveAttachment()));
+ topL->addWidget(a_ttRemoveBtn, 1,1);
+
+ a_ttEditBtn=new QPushButton(i18n("&Properties"), a_ttWidget);
+ a_ttEditBtn->setEnabled(false);
+ connect(a_ttEditBtn, SIGNAL(clicked()), parent(), SLOT(slotAttachmentProperties()));
+ topL->addWidget(a_ttEditBtn, 2,1, Qt::AlignTop);
+
+ topL->setRowStretch(2,1);
+ topL->setColStretch(0,1);
+ }
+
+ if(!v_iewOpen) {
+ v_iewOpen=true;
+ a_ttWidget->show();
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("POSTNEWS");
+
+ QValueList<int> lst=conf->readIntListEntry("Att_Splitter");
+ if(lst.count()!=2)
+ lst << 267 << 112;
+ setSizes(lst);
+
+ lst=conf->readIntListEntry("Att_Headers");
+ if(lst.count()==5) {
+ QValueList<int>::Iterator it=lst.begin();
+
+ QHeader *h=a_ttView->header();
+ for(int i=0; i<5; i++) {
+ h->resizeSection(i,(*it));
+ ++it;
+ }
+ }
+ }
+}
+
+
+void KNComposer::ComposerView::hideAttachmentView()
+{
+ if(v_iewOpen) {
+ a_ttWidget->hide();
+ v_iewOpen=false;
+ }
+}
+
+
+void KNComposer::ComposerView::showExternalNotification()
+{
+ e_dit->setReadOnly(true);
+ n_otification->show();
+}
+
+
+void KNComposer::ComposerView::hideExternalNotification()
+{
+ e_dit->setReadOnly(false);
+ n_otification->hide();
+}
+
+
+//=====================================================================================
+
+#include <kcursor.h>
+KNComposer::Editor::Editor(KNComposer::ComposerView *_composerView, KNComposer *_composer, QWidget *parent, char *name)
+ : KEdit(parent, name), m_composer( _composer ), m_composerView(_composerView)
+{
+ setOverwriteEnabled(true);
+ spell = 0L;
+ installEventFilter(this);
+ KCursor::setAutoHideCursor( this, true, true );
+ m_bound = QRegExp( QString::fromLatin1("[\\s\\W]") );
+}
+
+
+KNComposer::Editor::~Editor()
+{
+ removeEventFilter(this);
+ delete spell;
+}
+
+//-----------------------------------------------------------------------------
+bool KNComposer::Editor::eventFilter(QObject*o, QEvent* e)
+{
+ if (o == this)
+ KCursor::autoHideEventFilter(o, e);
+
+ if (e->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *k = (QKeyEvent*)e;
+ // ---sven's Arrow key navigation start ---
+ // Key Up in first line takes you to Subject line.
+ if (k->key() == Key_Up && k->state() != ShiftButton && currentLine() == 0
+ && lineOfChar(0, currentColumn()) == 0)
+ {
+ deselect();
+ m_composerView->focusNextPrevEdit(0, false); //take me up
+ return true;
+ }
+ // ---sven's Arrow key navigation end ---
+
+ if (k->key() == Key_Backtab && k->state() == ShiftButton)
+ {
+ deselect();
+ m_composerView->focusNextPrevEdit(0, false);
+ return true;
+ }
+ } else if ( e->type() == QEvent::ContextMenu ) {
+ QContextMenuEvent *event = (QContextMenuEvent*) e;
+
+ int para = 1, charPos, firstSpace, lastSpace;
+
+ //Get the character at the position of the click
+ charPos = charAt( viewportToContents(event->pos() ), &para );
+ QString paraText = text( para );
+
+ if( !paraText.at(charPos).isSpace() )
+ {
+ //Get word right clicked on
+ firstSpace = paraText.findRev( m_bound, charPos ) + 1;
+ lastSpace = paraText.find( m_bound, charPos );
+ if( lastSpace == -1 )
+ lastSpace = paraText.length();
+ QString word = paraText.mid( firstSpace, lastSpace - firstSpace );
+ //Continue if this word was misspelled
+ if( !word.isEmpty() && m_replacements.contains( word ) )
+ {
+ KPopupMenu p;
+ p.insertTitle( i18n("Suggestions") );
+
+ //Add the suggestions to the popup menu
+ QStringList reps = m_replacements[word];
+ if( reps.count() > 0 )
+ {
+ int listPos = 0;
+ for ( QStringList::Iterator it = reps.begin(); it != reps.end(); ++it ) {
+ p.insertItem( *it, listPos );
+ listPos++;
+ }
+ }
+ else
+ {
+ p.insertItem( QString::fromLatin1("No Suggestions"), -2 );
+ }
+
+ //Execute the popup inline
+ int id = p.exec( mapToGlobal( event->pos() ) );
+
+ if( id > -1 )
+ {
+ //Save the cursor position
+ int parIdx = 1, txtIdx = 1;
+ getCursorPosition(&parIdx, &txtIdx);
+ setSelection(para, firstSpace, para, lastSpace);
+ insert(m_replacements[word][id]);
+ // Restore the cursor position; if the cursor was behind the
+ // misspelled word then adjust the cursor position
+ if ( para == parIdx && txtIdx >= lastSpace )
+ txtIdx += m_replacements[word][id].length() - word.length();
+ setCursorPosition(parIdx, txtIdx);
+ }
+ //Cancel original event
+ return true;
+ }
+ }
+ }
+
+ return KEdit::eventFilter(o, e);
+}
+
+void KNComposer::Editor::slotAddSuggestion( const QString &text, const QStringList &lst, unsigned int )
+{
+ m_replacements[text] = lst;
+}
+
+// expand tabs to avoid the "tab-damage",
+// auto-wraped paragraphs have to split (code taken from KEdit::saveText)
+QStringList KNComposer::Editor::processedText()
+{
+ QStringList ret;
+ int lines = numLines()-1;
+ if (lines < 0)
+ return ret;
+
+ if (wordWrap() == NoWrap) {
+ for (int i = 0; i <= lines; i++)
+ ret.append(textLine(i));
+ } else {
+ for (int i = 0; i <= lines; i++) {
+ int lines_in_parag = linesOfParagraph(i);
+
+ if (lines_in_parag == 1) {
+ ret.append(textLine(i));
+ } else {
+ QString parag_text = textLine(i);
+ int pos = 0;
+ int last_pos = 0;
+ int current_line = 0;
+ while (current_line+1 < lines_in_parag) {
+ while (lineOfChar(i, pos) == current_line) pos++;
+ ret.append(parag_text.mid(last_pos, pos - last_pos - 1));
+ current_line++;
+ last_pos = pos;
+ }
+ // add last line
+ ret.append(parag_text.mid(pos));
+ }
+ }
+ }
+
+ QString replacement;
+ int tabPos;
+ for (QStringList::Iterator it = ret.begin(); it != ret.end(); ++it ) {
+ while ((tabPos=(*it).find('\t'))!=-1) {
+ replacement.fill(QChar(' '), 8-(tabPos%8));
+ (*it).replace(tabPos, 1, replacement);
+ }
+ }
+
+ return ret;
+}
+
+
+void KNComposer::Editor::slotPasteAsQuotation()
+{
+ QString s = QApplication::clipboard()->text();
+ if (!s.isEmpty()) {
+ for (int i=0; (uint)i<s.length(); i++) {
+ if ( s[i] < ' ' && s[i] != '\n' && s[i] != '\t' )
+ s[i] = ' ';
+ }
+ s.prepend("> ");
+ s.replace(QRegExp("\n"),"\n> ");
+ insert(s);
+ }
+}
+
+
+void KNComposer::Editor::slotFind()
+{
+ search();
+}
+
+void KNComposer::Editor::slotSearchAgain()
+{
+ repeatSearch();
+}
+
+void KNComposer::Editor::slotReplace()
+{
+ replace();
+}
+
+
+void KNComposer::Editor::slotAddQuotes()
+{
+ if (hasMarkedText()) {
+ QString s = markedText();
+ s.prepend("> ");
+ s.replace(QRegExp("\n"),"\n> ");
+ insert(s);
+ } else {
+ int l = currentLine();
+ int c = currentColumn();
+ QString s = textLine(l);
+ s.prepend("> ");
+ insertLine(s,l);
+ removeLine(l+1);
+ setCursorPosition(l,c+2);
+ }
+}
+
+
+void KNComposer::Editor::slotRemoveQuotes()
+{
+ if (hasMarkedText()) {
+ QString s = markedText();
+ if (s.left(2) == "> ")
+ s.remove(0,2);
+ s.replace(QRegExp("\n> "),"\n");
+ insert(s);
+ } else {
+ int l = currentLine();
+ int c = currentColumn();
+ QString s = textLine(l);
+ if (s.left(2) == "> ") {
+ s.remove(0,2);
+ insertLine(s,l);
+ removeLine(l+1);
+ setCursorPosition(l,c-2);
+ }
+ }
+}
+
+
+void KNComposer::Editor::slotAddBox()
+{
+ if (hasMarkedText()) {
+ QString s = markedText();
+ s.prepend(",----[ ]\n");
+ s.replace(QRegExp("\n"),"\n| ");
+ s.append("\n`----");
+ insert(s);
+ } else {
+ int l = currentLine();
+ int c = currentColumn();
+ QString s = QString::fromLatin1(",----[ ]\n| %1\n`----").arg(textLine(l));
+ insertLine(s,l);
+ removeLine(l+3);
+ setCursorPosition(l+1,c+2);
+ }
+}
+
+
+void KNComposer::Editor::slotRemoveBox()
+{
+ if (hasMarkedText()) {
+ QString s = QString::fromLatin1("\n") + markedText() + QString::fromLatin1("\n");
+ s.replace(QRegExp("\n,----[^\n]*\n"),"\n");
+ s.replace(QRegExp("\n| "),"\n");
+ s.replace(QRegExp("\n`----[^\n]*\n"),"\n");
+ s.remove(0,1);
+ s.truncate(s.length()-1);
+ insert(s);
+ } else {
+ int l = currentLine();
+ int c = currentColumn();
+
+ QString s = textLine(l); // test if we are in a box
+ if (!((s.left(2) == "| ")||(s.left(5)==",----")||(s.left(5)=="`----")))
+ return;
+
+ setAutoUpdate(false);
+
+ // find & remove box begin
+ int x = l;
+ while ((x>=0)&&(textLine(x).left(5)!=",----"))
+ x--;
+ if ((x>=0)&&(textLine(x).left(5)==",----")) {
+ removeLine(x);
+ l--;
+ for (int i=x;i<=l;i++) { // remove quotation
+ s = textLine(i);
+ if (s.left(2) == "| ") {
+ s.remove(0,2);
+ insertLine(s,i);
+ removeLine(i+1);
+ }
+ }
+ }
+
+ // find & remove box end
+ x = l;
+ while ((x<numLines())&&(textLine(x).left(5)!="`----"))
+ x++;
+ if ((x<numLines())&&(textLine(x).left(5)=="`----")) {
+ removeLine(x);
+ for (int i=l+1;i<x;i++) { // remove quotation
+ s = textLine(i);
+ if (s.left(2) == "| ") {
+ s.remove(0,2);
+ insertLine(s,i);
+ removeLine(i+1);
+ }
+ }
+ }
+
+ setCursorPosition(l,c-2);
+
+ setAutoUpdate(true);
+ repaint(false);
+ }
+}
+
+
+void KNComposer::Editor::slotRot13()
+{
+ if (hasMarkedText())
+ insert(KNHelper::rot13(markedText()));
+}
+
+
+void KNComposer::Editor::contentsDragEnterEvent(QDragEnterEvent *ev)
+{
+ if (KURLDrag::canDecode(ev))
+ emit(sigDragEnterEvent(ev));
+ else
+ KEdit::dragEnterEvent(ev);
+}
+
+
+void KNComposer::Editor::contentsDropEvent(QDropEvent *ev)
+{
+ if (KURLDrag::canDecode(ev))
+ emit(sigDropEvent(ev));
+ else
+ KEdit::dropEvent(ev);
+}
+
+void KNComposer::Editor::keyPressEvent ( QKeyEvent *e)
+{
+ if( e->key() == Key_Return ) {
+ int line, col;
+ getCursorPosition( &line, &col );
+ QString lineText = text( line );
+ // returns line with additional trailing space (bug in Qt?), cut it off
+ lineText.truncate( lineText.length() - 1 );
+ // special treatment of quoted lines only if the cursor is neither at
+ // the begin nor at the end of the line
+ if( ( col > 0 ) && ( col < int( lineText.length() ) ) ) {
+ bool isQuotedLine = false;
+ uint bot = 0; // bot = begin of text after quote indicators
+ while( bot < lineText.length() ) {
+ if( ( lineText[bot] == '>' ) || ( lineText[bot] == '|' ) ) {
+ isQuotedLine = true;
+ ++bot;
+ }
+ else if( lineText[bot].isSpace() ) {
+ ++bot;
+ }
+ else {
+ break;
+ }
+ }
+
+ KEdit::keyPressEvent( e );
+
+ // duplicate quote indicators of the previous line before the new
+ // line if the line actually contained text (apart from the quote
+ // indicators) and the cursor is behind the quote indicators
+ if( isQuotedLine
+ && ( bot != lineText.length() )
+ && ( col >= int( bot ) ) ) {
+ QString newLine = text( line + 1 );
+ // remove leading white space from the new line and instead
+ // add the quote indicators of the previous line
+ unsigned int leadingWhiteSpaceCount = 0;
+ while( ( leadingWhiteSpaceCount < newLine.length() )
+ && newLine[leadingWhiteSpaceCount].isSpace() ) {
+ ++leadingWhiteSpaceCount;
+ }
+ newLine = newLine.replace( 0, leadingWhiteSpaceCount,
+ lineText.left( bot ) );
+ removeParagraph( line + 1 );
+ insertParagraph( newLine, line + 1 );
+ // place the cursor at the begin of the new line since
+ // we assume that the user split the quoted line in order
+ // to add a comment to the first part of the quoted line
+ setCursorPosition( line + 1 , 0 );
+ }
+ }
+ else
+ KEdit::keyPressEvent( e );
+ }
+ else
+ KEdit::keyPressEvent( e );
+}
+
+
+void KNComposer::Editor::contentsContextMenuEvent( QContextMenuEvent */*e*/ )
+{
+ QString selectWord = selectWordUnderCursor();
+ QPopupMenu* popup = 0L;
+ if ( selectWord.isEmpty())
+ {
+ popup = m_composer ? m_composer->popupMenu( "edit" ): 0;
+ if ( popup )
+ popup->popup(QCursor::pos());
+ }
+ else
+ {
+ spell = new KSpell(this, i18n("Spellcheck"), this, SLOT(slotSpellStarted(KSpell *)));
+ QStringList l = KSpellingHighlighter::personalWords();
+ for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
+ spell->addPersonal( *it );
+ }
+ connect(spell, SIGNAL(death()), this, SLOT(slotSpellFinished()));
+ connect(spell, SIGNAL(done(const QString&)), this, SLOT(slotSpellDone(const QString&)));
+ connect(spell, SIGNAL(misspelling (const QString &, const QStringList &, unsigned int)),
+ this, SLOT(slotMisspelling (const QString &, const QStringList &, unsigned int)));
+ }
+}
+
+void KNComposer::Editor::slotSpellStarted( KSpell *)
+{
+ spell->check( selectWordUnderCursor(),false );
+}
+
+
+void KNComposer::Editor::slotSpellDone(const QString &/*newtext*/)
+{
+ spell->cleanUp();
+}
+
+void KNComposer::Editor::slotSpellFinished()
+{
+ KSpell::spellStatus status=spell->status();
+ delete spell;
+ spell=0;
+
+ if(status==KSpell::Error) {
+ KMessageBox::error(this, i18n("ISpell could not be started.\n"
+ "Please make sure you have ISpell properly configured and in your PATH."));
+ }
+ else if(status==KSpell::Crashed) {
+
+ KMessageBox::error(this, i18n("ISpell seems to have crashed."));
+ }
+}
+
+void KNComposer::Editor::cut()
+{
+ KEdit::cut();
+ m_composer->v_iew->restartBackgroundSpellCheck();
+}
+
+void KNComposer::Editor::clear()
+{
+ KEdit::clear();
+ m_composer->v_iew->restartBackgroundSpellCheck();
+}
+
+void KNComposer::Editor::del()
+{
+ KEdit::del();
+ m_composer->v_iew->restartBackgroundSpellCheck();
+}
+
+
+void KNComposer::Editor::slotMisspelling (const QString &, const QStringList &lst, unsigned int)
+{
+ int countAction = m_composer->listOfResultOfCheckWord( lst , selectWordUnderCursor());
+ if ( countAction>0 )
+ {
+ QPopupMenu* popup = m_composer ? m_composer->popupMenu( "edit_with_spell" ): 0;
+ if ( popup )
+ popup->popup(QCursor::pos());
+ }
+ else
+ {
+ QPopupMenu* popup = m_composer ? m_composer->popupMenu( "edit" ): 0;
+ if ( popup )
+ popup->popup(QCursor::pos());
+ }
+}
+
+void KNComposer::Editor::slotCorrectWord()
+{
+ removeSelectedText();
+ KAction * act = (KAction *)(sender());
+ int line, col;
+ getCursorPosition(&line,&col);
+
+
+
+ insertAt( act->text(), line, col );
+
+ //insert( act->text() );
+}
+
+//=====================================================================================
+
+
+KNComposer::AttachmentView::AttachmentView(QWidget *parent, char *name)
+ : KListView(parent, name)
+{
+ setFrameStyle(QFrame::WinPanel | QFrame::Sunken); // match the QMultiLineEdit style
+ addColumn(i18n("File"), 115);
+ addColumn(i18n("Type"), 91);
+ addColumn(i18n("Size"), 55);
+ addColumn(i18n("Description"), 110);
+ addColumn(i18n("Encoding"), 60);
+ header()->setClickEnabled(false);
+ setAllColumnsShowFocus(true);
+}
+
+
+KNComposer::AttachmentView::~AttachmentView()
+{
+}
+
+
+void KNComposer::AttachmentView::keyPressEvent(QKeyEvent *e)
+{
+ if(!e)
+ return; // subclass bug
+
+ if( (e->key()==Key_Delete) && (currentItem()) )
+ emit(delPressed(currentItem()));
+ else
+ KListView::keyPressEvent(e);
+}
+
+
+//=====================================================================================
+
+
+KNComposer::AttachmentViewItem::AttachmentViewItem(KListView *v, KNAttachment *a) :
+ KListViewItem(v), attachment(a)
+{
+ setText(0, a->name());
+ setText(1, a->mimeType());
+ setText(2, a->contentSize());
+ setText(3, a->description());
+ setText(4, a->encoding());
+}
+
+
+
+KNComposer::AttachmentViewItem::~AttachmentViewItem()
+{
+ delete attachment;
+}
+
+
+//=====================================================================================
+
+
+KNComposer::AttachmentPropertiesDlg::AttachmentPropertiesDlg(KNAttachment *a, QWidget *p, const char *n) :
+ KDialogBase(p, n, true, i18n("Attachment Properties"), Help|Ok|Cancel, Ok), a_ttachment(a),
+ n_onTextAsText(false)
+{
+ //init GUI
+ QWidget *page=new QWidget(this);
+ setMainWidget(page);
+ QVBoxLayout *topL=new QVBoxLayout(page);
+
+ //file info
+ QGroupBox *fileGB=new QGroupBox(i18n("File"), page);
+ QGridLayout *fileL=new QGridLayout(fileGB, 3,2, 15,5);
+
+ fileL->addRowSpacing(0, fontMetrics().lineSpacing()-9);
+ fileL->addWidget(new QLabel(i18n("Name:"), fileGB) ,1,0);
+ fileL->addWidget(new QLabel(QString("<b>%1</b>").arg(a->name()), fileGB), 1,1, Qt::AlignLeft);
+ fileL->addWidget(new QLabel(i18n("Size:"), fileGB), 2,0);
+ fileL->addWidget(new QLabel(a->contentSize(), fileGB), 2,1, Qt::AlignLeft);
+
+ fileL->setColStretch(1,1);
+ topL->addWidget(fileGB);
+
+ //mime info
+ QGroupBox *mimeGB=new QGroupBox(i18n("Mime"), page);
+ QGridLayout *mimeL=new QGridLayout(mimeGB, 4,2, 15,5);
+
+ mimeL->addRowSpacing(0, fontMetrics().lineSpacing()-9);
+ m_imeType=new KLineEdit(mimeGB);
+ m_imeType->setText(a->mimeType());
+ mimeL->addWidget(m_imeType, 1,1);
+ mimeL->addWidget(new QLabel(m_imeType, i18n("&Mime-Type:"), mimeGB), 1,0);
+
+ d_escription=new KLineEdit(mimeGB);
+ d_escription->setText(a->description());
+ mimeL->addWidget(d_escription, 2,1);
+ mimeL->addWidget(new QLabel(d_escription, i18n("&Description:"), mimeGB), 2,0);
+
+ e_ncoding=new QComboBox(false, mimeGB);
+ e_ncoding->insertItem("7Bit");
+ e_ncoding->insertItem("8Bit");
+ e_ncoding->insertItem("quoted-printable");
+ e_ncoding->insertItem("base64");
+ if(a->isFixedBase64()) {
+ e_ncoding->setCurrentItem(3);
+ e_ncoding->setEnabled(false);
+ }
+ else
+ e_ncoding->setCurrentItem(a->cte());
+ mimeL->addWidget(e_ncoding, 3,1);
+ mimeL->addWidget(new QLabel(e_ncoding, i18n("&Encoding:"), mimeGB), 3,0);
+
+ mimeL->setColStretch(1,1);
+ topL->addWidget(mimeGB);
+
+ //connections
+ connect(m_imeType, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotMimeTypeTextChanged(const QString&)));
+
+ //finish GUI
+ setFixedHeight(sizeHint().height());
+ KNHelper::restoreWindowSize("attProperties", this, QSize(300,250));
+ setHelp("anc-knode-editor-advanced");
+}
+
+
+KNComposer::AttachmentPropertiesDlg::~AttachmentPropertiesDlg()
+{
+ KNHelper::saveWindowSize("attProperties", this->size());
+}
+
+
+void KNComposer::AttachmentPropertiesDlg::apply()
+{
+ a_ttachment->setDescription(d_escription->text());
+ a_ttachment->setMimeType(m_imeType->text());
+ a_ttachment->setCte(e_ncoding->currentItem());
+}
+
+
+void KNComposer::AttachmentPropertiesDlg::accept()
+{
+ if(m_imeType->text().find('/')==-1) {
+ KMessageBox::sorry(this, i18n("You have set an invalid mime-type.\nPlease change it."));
+ return;
+ }
+ else if(n_onTextAsText && m_imeType->text().find("text/", 0, false)!=-1 &&
+ KMessageBox::warningContinueCancel(this,
+ i18n("You have changed the mime-type of this non-textual attachment\nto text. This might cause an error while loading or encoding the file.\nProceed?")
+ ) == KMessageBox::Cancel) return;
+
+ KDialogBase::accept();
+}
+
+
+void KNComposer::AttachmentPropertiesDlg::slotMimeTypeTextChanged(const QString &text)
+{
+ enableButtonOK( !text.isEmpty() );
+ if(text.left(5)!="text/") {
+ n_onTextAsText=a_ttachment->isFixedBase64();
+ e_ncoding->setCurrentItem(3);
+ e_ncoding->setEnabled(false);
+ }
+ else {
+ e_ncoding->setCurrentItem(a_ttachment->cte());
+ e_ncoding->setEnabled(true);
+ }
+}
+
+
+//--------------------------------
+
+#include "kncomposer.moc"
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/kncomposer.h b/knode/kncomposer.h
new file mode 100644
index 000000000..12c2150f8
--- /dev/null
+++ b/knode/kncomposer.h
@@ -0,0 +1,384 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCOMPOSER_H
+#define KNCOMPOSER_H
+
+#include <klistview.h>
+
+#include <kmainwindow.h>
+#include <kdialogbase.h>
+#include <keditcl.h>
+#include <qlineedit.h>
+#include <qregexp.h>
+
+#include <kdeversion.h>
+#include <keditcl.h>
+
+#include <kabc/addresslineedit.h>
+#include <knodecomposeriface.h>
+
+class QGroupBox;
+
+class KProcess;
+class KSpell;
+class KDictSpellingHighlighter;
+class KSelectAction;
+class KToggleAction;
+
+class KNLocalArticle;
+class KNAttachment;
+class SpellingFilter;
+
+class KNComposer : public KMainWindow , virtual public KNodeComposerIface {
+
+ Q_OBJECT
+
+ public:
+ enum composerResult { CRsendNow, CRsendLater, CRdelAsk,
+ CRdel, CRsave, CRcancel };
+ enum MessageMode { news=0, mail=1, news_mail=2 };
+
+ // unwraped == original, not rewraped text
+ // firstEdit==true: place the cursor at the end of the article
+ KNComposer(KNLocalArticle *a, const QString &text=QString::null, const QString &sig=QString::null, const QString &unwraped=QString::null, bool firstEdit=false, bool dislikesCopies=false, bool createCopy=false);
+ ~KNComposer();
+ void setConfig(bool onlyFonts);
+ void setMessageMode(MessageMode mode);
+
+ //get result
+ bool hasValidData();
+ composerResult result() const { return r_esult; }
+ KNLocalArticle* article()const { return a_rticle; }
+ bool applyChanges();
+
+ void closeEvent(QCloseEvent *e);
+
+ //set data from the given article
+ void initData(const QString &text);
+
+ // inserts at cursor position if clear is false, replaces content otherwise
+ // puts the file content into a box if box==true
+ // "file" is already open for reading
+ void insertFile(QFile *file, bool clear=false, bool box=false, QString boxTitle=QString::null);
+
+ // ask for a filename, handle network urls
+ void insertFile(bool clear=false, bool box=false);
+
+ QPopupMenu * popupMenu( const QString& name );
+ int listOfResultOfCheckWord( const QStringList & lst , const QString & selectWord);
+
+//internal classes
+ class ComposerView;
+ class Editor;
+ class AttachmentView;
+ class AttachmentViewItem;
+ class AttachmentPropertiesDlg;
+
+ //GUI
+ ComposerView *v_iew;
+ QPopupMenu *a_ttPopup;
+
+ //Data
+ composerResult r_esult;
+ KNLocalArticle *a_rticle;
+ QString s_ignature, u_nwraped;
+ QCString c_harset;
+ MessageMode m_ode;
+ bool n_eeds8Bit, // false: fall back to us-ascii
+ v_alidated, // hasValidData was run and found no problems, n_eeds8Bit is valid
+ a_uthorDislikesMailCopies;
+
+ //edit
+ bool e_xternalEdited;
+ KProcess *e_xternalEditor;
+ KTempFile *e_ditorTempfile;
+ KSpell *s_pellChecker;
+ SpellingFilter* mSpellingFilter;
+
+ //Attachments
+ QValueList<KNAttachment*> mDeletedAttachments;
+ QPtrList<KAction> m_listAction;
+ bool a_ttChanged;
+
+ //------------------------------ <Actions> -----------------------------
+
+ KAccel *a_ccel;
+ KAction *a_ctExternalEditor,
+ *a_ctSpellCheck,
+ *a_ctRemoveAttachment,
+ *a_ctAttachmentProperties,
+ *a_ctSetCharsetKeyb;
+ KToggleAction *a_ctPGPsign,
+ *a_ctDoPost, *a_ctDoMail, *a_ctWordWrap;
+ KSelectAction *a_ctSetCharset;
+ bool spellLineEdit;
+ protected slots:
+ void slotSendNow();
+ void slotSendLater();
+ void slotSaveAsDraft();
+ void slotArtDelete();
+ void slotAppendSig();
+ void slotInsertFile();
+ void slotInsertFileBoxed();
+ void slotAttachFile();
+ void slotRemoveAttachment();
+ void slotAttachmentProperties();
+ void slotToggleDoPost();
+ void slotToggleDoMail();
+ void slotSetCharset(const QString &s);
+ void slotSetCharsetKeyboard();
+ void slotToggleWordWrap();
+ void slotUndoRewrap();
+ void slotExternalEditor();
+ void slotSpellcheck();
+
+ void slotUpdateStatusBar();
+ void slotUpdateCursorPos();
+ void slotConfKeys();
+ void slotConfToolbar();
+ void slotNewToolbarConfig();
+
+ //------------------------------ </Actions> ----------------------------
+
+ // GUI
+ void slotSubjectChanged(const QString &t);
+ void slotGroupsChanged(const QString &t);
+ void slotToBtnClicked();
+ void slotGroupsBtnClicked();
+
+ // external editor
+ void slotEditorFinished(KProcess *);
+ void slotCancelEditor();
+
+ // attachment list
+ void slotAttachmentPopup(KListView*, QListViewItem *it, const QPoint &p);
+ void slotAttachmentSelected(QListViewItem *it);
+ void slotAttachmentEdit(QListViewItem *it);
+ void slotAttachmentRemove(QListViewItem *it);
+
+ // spellcheck operation
+ void slotSpellStarted(KSpell *);
+ void slotSpellDone(const QString&);
+ void slotSpellFinished();
+
+ // DND handling
+ virtual void slotDragEnterEvent(QDragEnterEvent *);
+ virtual void slotDropEvent(QDropEvent *);
+
+ void slotUndo();
+ void slotRedo();
+ void slotCut();
+ void slotCopy();
+ void slotPaste();
+ void slotSelectAll();
+ void slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos);
+ void slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos);
+ void addRecentAddress();
+
+ protected:
+
+ // DND handling
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ virtual void dropEvent(QDropEvent *);
+
+ signals:
+ void composerDone(KNComposer*);
+
+ private:
+ bool mFirstEdit;
+
+};
+
+
+
+class KNLineEditSpell;
+class KNLineEdit;
+
+class KNComposer::ComposerView : public QSplitter {
+
+ public:
+ ComposerView(KNComposer *_composer, const char *n=0);
+ ~ComposerView();
+ void focusNextPrevEdit(const QWidget* aCur, bool aNext);
+ void setMessageMode(KNComposer::MessageMode mode);
+ void showAttachmentView();
+ void hideAttachmentView();
+ void showExternalNotification();
+ void hideExternalNotification();
+ void restartBackgroundSpellCheck();
+ QValueList<QWidget*> mEdtList;
+
+ QLabel *l_to,
+ *l_groups,
+ *l_fup2;
+ KNLineEditSpell *s_ubject;
+
+ KNLineEdit *g_roups;
+ KNLineEdit *t_o;
+
+ KComboBox *f_up2;
+ QPushButton *g_roupsBtn,
+ *t_oBtn;
+
+ Editor *e_dit;
+ QGroupBox *n_otification;
+ QPushButton *c_ancelEditorBtn;
+
+ QWidget *a_ttWidget;
+ AttachmentView *a_ttView;
+ QPushButton *a_ttAddBtn,
+ *a_ttRemoveBtn,
+ *a_ttEditBtn;
+ KDictSpellingHighlighter *mSpellChecker;
+
+ bool v_iewOpen;
+};
+
+
+//internal class : handle Tabs... (expanding them in textLine(), etc.)
+class KNComposer::Editor : public KEdit {
+
+ Q_OBJECT
+
+ public:
+ Editor(KNComposer::ComposerView *_composerView, KNComposer *_composer, QWidget *parent=0, char *name=0);
+ ~Editor();
+ QStringList processedText();
+
+ public slots:
+ void slotPasteAsQuotation();
+ void slotFind();
+ void slotSearchAgain();
+ void slotReplace();
+ void slotAddQuotes();
+ void slotRemoveQuotes();
+ void slotAddBox();
+ void slotRemoveBox();
+ void slotRot13();
+ void slotCorrectWord();
+
+protected slots:
+ void slotSpellStarted( KSpell *);
+ void slotSpellDone(const QString &);
+ void slotSpellFinished();
+ void slotMisspelling (const QString &, const QStringList &lst, unsigned int);
+ virtual void cut();
+ virtual void clear();
+ virtual void del();
+ void slotAddSuggestion( const QString &, const QStringList &lst, unsigned int );
+ signals:
+ void sigDragEnterEvent(QDragEnterEvent *);
+ void sigDropEvent(QDropEvent *);
+
+ protected:
+
+ // DND handling
+ virtual void contentsDragEnterEvent(QDragEnterEvent *);
+ virtual void contentsDropEvent(QDropEvent *);
+ virtual void contentsContextMenuEvent( QContextMenuEvent *e );
+ virtual void keyPressEvent ( QKeyEvent *e);
+
+ virtual bool eventFilter(QObject*, QEvent*);
+private:
+ KNComposer *m_composer;
+ KNComposer::ComposerView *m_composerView;
+ KSpell *spell;
+ QMap<QString,QStringList> m_replacements;
+ QRegExp m_bound;
+};
+
+
+class KNComposer::AttachmentView : public KListView {
+
+ Q_OBJECT
+
+ public:
+ AttachmentView(QWidget *parent, char *name=0);
+ ~AttachmentView();
+
+ protected:
+ void keyPressEvent( QKeyEvent *e );
+
+ signals:
+ void delPressed ( QListViewItem * ); // the user used Key_Delete on a list view item
+};
+
+
+class KNComposer::AttachmentViewItem : public KListViewItem {
+
+ public:
+ AttachmentViewItem(KListView *v, KNAttachment *a);
+ ~AttachmentViewItem();
+
+ KNAttachment *attachment;
+
+};
+
+
+class KNComposer::AttachmentPropertiesDlg : public KDialogBase {
+
+ Q_OBJECT
+
+ public:
+ AttachmentPropertiesDlg( KNAttachment *a, QWidget *p=0, const char *n=0);
+ ~AttachmentPropertiesDlg();
+
+ void apply();
+
+ protected:
+ KLineEdit *m_imeType,
+ *d_escription;
+ QComboBox *e_ncoding;
+
+ KNAttachment *a_ttachment;
+ bool n_onTextAsText;
+
+ protected slots:
+ void accept();
+ void slotMimeTypeTextChanged(const QString &text);
+};
+
+//-----------------------------------------------------------------------------
+class KNLineEdit : public KABC::AddressLineEdit
+{
+ Q_OBJECT
+ typedef KABC::AddressLineEdit KNLineEditInherited;
+public:
+
+ KNLineEdit(KNComposer::ComposerView *_composerView, bool useCompletion, QWidget *parent = 0,
+ const char *name = 0);
+protected:
+ // Inherited. Always called by the parent when this widget is created.
+ virtual void loadAddresses();
+ void keyPressEvent(QKeyEvent *e);
+ virtual QPopupMenu *createPopupMenu();
+private slots:
+ void editRecentAddresses();
+private:
+ KNComposer::ComposerView *composerView;
+};
+
+class KNLineEditSpell : public KNLineEdit
+{
+ Q_OBJECT
+public:
+ KNLineEditSpell(KNComposer::ComposerView *_composerView, bool useCompletion,QWidget * parent, const char * name = 0);
+ void highLightWord( unsigned int length, unsigned int pos );
+ void spellCheckDone( const QString &s );
+ void spellCheckerMisspelling( const QString &text, const QStringList &, unsigned int pos);
+ void spellCheckerCorrected( const QString &old, const QString &corr, unsigned int pos);
+};
+
+#endif
diff --git a/knode/kncomposerui.rc b/knode/kncomposerui.rc
new file mode 100644
index 000000000..0a203d266
--- /dev/null
+++ b/knode/kncomposerui.rc
@@ -0,0 +1,102 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="KNComposer" version="20">
+
+<MenuBar>
+ <Menu noMerge="1" name="file"><text>&amp;File</text>
+ <Action name="send_now"/>
+ <Action name="send_later"/>
+ <Action name="save_as_draft"/>
+ <Action name="art_delete"/>
+ <Separator/>
+ <Action name="file_close"/>
+ </Menu>
+ <Menu noMerge="1" name="edit"><text>&amp;Edit</text>
+ <Action name="edit_undo"/>
+ <Action name="edit_redo"/>
+ <Separator/>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <Action name="paste_quoted"/>
+ <Action name="edit_select_all"/>
+ <Separator/>
+ <Action name="edit_find"/>
+ <Action name="edit_find_next"/>
+ <Action name="edit_replace"/>
+ </Menu>
+ <Menu name="attach"><text>&amp;Attach</text>
+ <Action name="append_signature"/>
+ <Action name="insert_file"/>
+ <Action name="insert_file_boxed"/>
+ <Action name="attach_file"/>
+ </Menu>
+ <Menu name="options"><text>Optio&amp;ns</text>
+ <Action name="send_news"/>
+ <Action name="send_mail"/>
+ <Separator/>
+ <Action name="set_charset"/>
+ <Action name="toggle_wordwrap"/>
+ </Menu>
+ <Menu noMerge="1" name="tools"><text>&amp;Tools</text>
+ <Action name="tools_quote"/>
+ <Action name="tools_unquote"/>
+ <Action name="tools_box"/>
+ <Action name="tools_unbox"/>
+ <Separator/>
+ <Action name="sign_article"/>
+ <Action name="tools_undoRewrap"/>
+ <Action name="tools_rot13"/>
+ <Separator/>
+ <Action name="external_editor"/>
+ <Action name="tools_spelling"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar noMerge="1" name="mainToolBar"><text>Main Toolbar</text>
+ <Action name="send_now"/>
+ <Action name="send_later"/>
+ <Action name="save_as_draft"/>
+ <Action name="art_delete"/>
+ <Separator/>
+ <Action name="edit_undo"/>
+ <Action name="edit_redo"/>
+ <Separator/>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <Separator/>
+ <Action name="send_news"/>
+ <Action name="send_mail"/>
+ <Separator/>
+ <Action name="attach_file"/>
+ <Action name="sign_article"/>
+ <Action name="tools_spelling"/>
+</ToolBar>
+
+<StatusBar/>
+
+<Menu name="attachment_popup">
+ <Action name="attachment_properties"/>
+ <Action name="remove_attachment"/>
+</Menu>
+
+
+<Menu name="edit_with_spell">
+ <Action name="edit_undo"/>
+ <Action name="edit_redo"/>
+ <Separator/>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <Action name="paste_quoted"/>
+ <Action name="edit_select_all"/>
+ <Separator/>
+ <Action name="edit_find"/>
+ <Action name="edit_replace"/>
+ <Separator/>
+ <Menu name="spell"><text>Spell Result</text>
+ <ActionList name="spell_result"/>
+ </Menu>
+</Menu>
+
+</kpartgui>
diff --git a/knode/knconfig.cpp b/knode/knconfig.cpp
new file mode 100644
index 000000000..cadf402b8
--- /dev/null
+++ b/knode/knconfig.cpp
@@ -0,0 +1,1252 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include "knconfig.h"
+
+#include <stdlib.h>
+
+#include <qtextcodec.h>
+
+#include <ksimpleconfig.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kglobalsettings.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kiconeffect.h>
+#include <kprocess.h>
+
+#include <email.h>
+
+#include "kndisplayedheader.h"
+#include "knglobals.h"
+#include "kngroupmanager.h"
+#include "utilities.h"
+
+
+
+KNConfig::Identity::Identity(bool g)
+ : u_seSigFile(false), u_seSigGenerator(false), g_lobal(g)
+{
+ if(g_lobal) {
+ KConfig *c=knGlobals.config();
+ c->setGroup("IDENTITY");
+ loadConfig(c);
+ }
+}
+
+
+KNConfig::Identity::~Identity()
+{}
+
+
+void KNConfig::Identity::loadConfig(KConfigBase *c)
+{
+ n_ame=c->readEntry("Name");
+ e_mail=c->readEntry("Email");
+ r_eplyTo=c->readEntry("Reply-To");
+ m_ailCopiesTo=c->readEntry("Mail-Copies-To");
+ o_rga=c->readEntry("Org");
+ s_igningKey = c->readEntry("SigningKey").local8Bit();
+ u_seSigFile=c->readBoolEntry("UseSigFile",false);
+ u_seSigGenerator=c->readBoolEntry("UseSigGenerator",false);
+ s_igPath=c->readPathEntry("sigFile");
+ s_igText=c->readEntry("sigText");
+}
+
+
+void KNConfig::Identity::saveConfig(KConfigBase *c)
+{
+ c->writeEntry("Name", n_ame);
+ c->writeEntry("Email", e_mail);
+ c->writeEntry("Reply-To", r_eplyTo);
+ c->writeEntry("Mail-Copies-To", m_ailCopiesTo);
+ c->writeEntry("Org", o_rga);
+ c->writeEntry("SigningKey", QString(s_igningKey));
+ c->writeEntry("UseSigFile", u_seSigFile);
+ c->writeEntry("UseSigGenerator",u_seSigGenerator);
+ c->writePathEntry("sigFile", s_igPath);
+ c->writeEntry("sigText", s_igText);
+ c->sync();
+}
+
+
+void KNConfig::Identity::save()
+{
+ kdDebug(5003) << "KNConfig::Identity::save()" << endl;
+ if(g_lobal) {
+ KConfig *c=knGlobals.config();
+ c->setGroup("IDENTITY");
+ saveConfig(c);
+ }
+}
+
+
+bool KNConfig::Identity::isEmpty()
+{
+ return ( n_ame.isEmpty() && e_mail.isEmpty() &&
+ r_eplyTo.isEmpty() && m_ailCopiesTo.isEmpty() &&
+ o_rga.isEmpty() && s_igPath.isEmpty() && s_igText.isEmpty() &&
+ s_igningKey.isEmpty() );
+}
+
+
+bool KNConfig::Identity::emailIsValid()
+{
+ return KPIM::isValidSimpleEmailAddress( e_mail );
+}
+
+
+QString KNConfig::Identity::getSignature()
+{
+ s_igContents = QString::null; // don't cache file contents
+ s_igStdErr = QString::null;
+
+ if (u_seSigFile) {
+ if(!s_igPath.isEmpty()) {
+ if (!u_seSigGenerator) {
+ QFile f(s_igPath);
+ if(f.open(IO_ReadOnly)) {
+ QTextStream ts(&f);
+ while(!ts.atEnd()) {
+ s_igContents += ts.readLine();
+ if (!ts.atEnd())
+ s_igContents += "\n";
+ }
+ f.close();
+ }
+ else
+ KMessageBox::error(knGlobals.topWidget, i18n("Cannot open the signature file."));
+ } else {
+ KProcess process;
+
+ // construct command line...
+ QStringList command = QStringList::split(' ',s_igPath);
+ for ( QStringList::Iterator it = command.begin(); it != command.end(); ++it )
+ process << (*it);
+
+ connect(&process, SIGNAL(receivedStdout(KProcess *, char *, int)), SLOT(slotReceiveStdout(KProcess *, char *, int)));
+ connect(&process, SIGNAL(receivedStderr(KProcess *, char *, int)), SLOT(slotReceiveStderr(KProcess *, char *, int)));
+
+ if (!process.start(KProcess::Block,KProcess::AllOutput))
+ KMessageBox::error(knGlobals.topWidget, i18n("Cannot run the signature generator."));
+ }
+ }
+ }
+ else
+ s_igContents = s_igText;
+
+ if (!s_igContents.isEmpty() && !s_igContents.contains("\n-- \n") && !(s_igContents.left(4) == "-- \n"))
+ s_igContents.prepend("-- \n");
+
+ return s_igContents;
+}
+
+
+void KNConfig::Identity::slotReceiveStdout(KProcess *, char *buffer, int buflen)
+{
+ s_igContents.append(QString::fromLocal8Bit(buffer,buflen));
+}
+
+
+void KNConfig::Identity::slotReceiveStderr(KProcess *, char *buffer, int buflen)
+{
+ s_igStdErr.append(QString::fromLocal8Bit(buffer,buflen));
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::Appearance::Appearance()
+{
+ KConfig *c=knGlobals.config();
+ c->setGroup("VISUAL_APPEARANCE");
+
+ //colors
+ u_seColors=c->readBoolEntry("customColors", false);
+
+ QColor defCol = defaultColor( background );
+ c_olors[background] = c->readColorEntry( "backgroundColor", &defCol );
+ c_olorNames[background] = i18n("Background");
+
+ defCol = defaultColor( alternateBackground );
+ c_olors[alternateBackground] = c->readColorEntry("alternateBackgroundColor", &defCol );
+ c_olorNames[alternateBackground] = i18n("Alternate Background");
+
+ defCol = defaultColor( normalText );
+ c_olors[normalText] = c->readColorEntry( "textColor", &defCol );
+ c_olorNames[normalText] = i18n("Normal Text");
+
+ defCol = defaultColor( quoted1 );
+ c_olors[quoted1] = c->readColorEntry( "quote1Color", &defCol );
+ c_olorNames[quoted1] = i18n("Quoted Text - First level");
+
+ defCol = defaultColor( quoted2 );
+ c_olors[quoted2] = c->readColorEntry( "quote2Color", &defCol );
+ c_olorNames[quoted2] = i18n("Quoted Text - Second level");
+
+ defCol = defaultColor( quoted3 );
+ c_olors[quoted3] = c->readColorEntry( "quote3Color", &defCol );
+ c_olorNames[quoted3] = i18n("Quoted Text - Third level");
+
+ defCol = defaultColor( url );
+ c_olors[url] = c->readColorEntry( "URLColor", &defCol );
+ c_olorNames[url] = i18n("Link");
+
+ defCol = defaultColor( readThread );
+ c_olors[readThread] = c->readColorEntry( "readThreadColor", &defCol );
+ c_olorNames[readThread] = i18n("Read Thread");
+
+ defCol = defaultColor( unreadThread );
+ c_olors[unreadThread] = c->readColorEntry("unreadThreadColor", &defCol );
+ c_olorNames[unreadThread] = i18n("Unread Thread");
+
+ defCol = defaultColor( readThread );
+ c_olors[readArticle] = c->readColorEntry("readArtColor", &defCol );
+ c_olorNames[readArticle] = i18n("Read Article");
+
+ defCol = defaultColor( unreadArticle );
+ c_olors[unreadArticle] = c->readColorEntry("unreadArtColor", &defCol );
+ c_olorNames[unreadArticle] = i18n("Unread Article");
+
+ defCol = defaultColor( signOkKeyOk );
+ c_olors[signOkKeyOk] = c->readColorEntry("signOkKeyOk", &defCol );
+ defCol = defaultColor( signOkKeyBad );
+ c_olors[signOkKeyBad] = c->readColorEntry("signOkKeyBad", &defCol );
+ defCol = defaultColor( signWarn );
+ c_olors[signWarn] = c->readColorEntry("signWarn", &defCol );
+ defCol = defaultColor( signErr );
+ c_olors[signErr] = c->readColorEntry("signErr", &defCol );
+ defCol = defaultColor( htmlWarning );
+ c_olors[htmlWarning] = c->readColorEntry("htmlWarning", &defCol );
+
+ c_olorNames[signOkKeyOk] = i18n("Valid Signature with Trusted Key");
+ c_olorNames[signOkKeyBad] = i18n("Valid Signature with Untrusted Key");
+ c_olorNames[signWarn] = i18n("Unchecked Signature");
+ c_olorNames[signErr] = i18n("Bad Signature");
+ c_olorNames[htmlWarning] = i18n("HTML Message Warning");
+
+ //fonts
+ u_seFonts = c->readBoolEntry("customFonts", false);
+ QFont defFont=KGlobalSettings::generalFont();
+ f_onts[article]=c->readFontEntry("articleFont",&defFont);
+ f_ontNames[article]=i18n("Article Body");
+
+ defFont=KGlobalSettings::fixedFont();
+ f_onts[articleFixed]=c->readFontEntry("articleFixedFont",&defFont);
+ f_ontNames[articleFixed]=i18n("Article Body (Fixed)");
+
+ f_onts[composer]=c->readFontEntry("composerFont",&defFont);
+ f_ontNames[composer]=i18n("Composer");
+
+ defFont=KGlobalSettings::generalFont();
+ f_onts[groupList]=c->readFontEntry("groupListFont",&defFont);
+ f_ontNames[groupList]=i18n("Group List");
+
+ f_onts[articleList]=c->readFontEntry("articleListFont",&defFont);
+ f_ontNames[articleList]=i18n("Article List");
+
+ //icons
+ KGlobal::iconLoader()->addAppDir("knode");
+ recreateLVIcons();
+ i_cons[newFups] = UserIcon("newsubs");
+ i_cons[eyes] = UserIcon("eyes");
+ i_cons[ignore] = UserIcon("ignore");
+ i_cons[mail] = SmallIcon("mail_generic");
+ i_cons[posting] = UserIcon("article");
+ i_cons[canceledPosting] = SmallIcon("editdelete");
+ i_cons[savedRemote] = SmallIcon("editcopy");
+ i_cons[group] = UserIcon("group");
+ i_cons[sendErr] = UserIcon("snderr");
+}
+
+
+KNConfig::Appearance::~Appearance()
+{
+}
+
+
+void KNConfig::Appearance::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::Appearance::save()" << endl;
+
+ KConfig *c=knGlobals.config();
+ c->setGroup("VISUAL_APPEARANCE");
+
+ c->writeEntry("customColors", u_seColors);
+ c->writeEntry("backgroundColor", c_olors[background]);
+ c->writeEntry("alternateBackgroundColor", c_olors[alternateBackground]);
+ c->writeEntry("textColor", c_olors[normalText]);
+ c->writeEntry("quote1Color", c_olors[quoted1]);
+ c->writeEntry("quote2Color", c_olors[quoted2]);
+ c->writeEntry("quote3Color", c_olors[quoted3]);
+ c->writeEntry("URLColor", c_olors[url]);
+ c->writeEntry("readThreadColor", c_olors[readThread]);
+ c->writeEntry("unreadThreadColor", c_olors[unreadThread]);
+ c->writeEntry("readArtColor", c_olors[readArticle]);
+ c->writeEntry("unreadArtColor", c_olors[unreadArticle]);
+ c->writeEntry( "signOkKeyOk", c_olors[signOkKeyOk] );
+ c->writeEntry( "signOkKeyBad", c_olors[signOkKeyBad] );
+ c->writeEntry( "signWarn", c_olors[signWarn] );
+ c->writeEntry( "signErr", c_olors[signErr] );
+ c->writeEntry( "htmlWarning", c_olors[htmlWarning] );
+
+ c->writeEntry("customFonts", u_seFonts);
+ c->writeEntry("articleFont", f_onts[article]);
+ c->writeEntry("articleFixedFont", f_onts[articleFixed]);
+ c->writeEntry("composerFont", f_onts[composer]);
+ c->writeEntry("groupListFont", f_onts[groupList]);
+ c->writeEntry("articleListFont", f_onts[articleList]);
+ c->sync();
+ d_irty = false;
+}
+
+
+QColor KNConfig::Appearance::backgroundColor() const
+{
+ if(u_seColors)
+ return c_olors[background];
+ else
+ return defaultColor( background );
+}
+
+
+QColor KNConfig::Appearance::alternateBackgroundColor() const
+{
+ if(u_seColors)
+ return c_olors[alternateBackground];
+ else
+ return defaultColor( alternateBackground );
+}
+
+
+QColor KNConfig::Appearance::textColor() const
+{
+ if(u_seColors)
+ return c_olors[normalText];
+ else
+ return defaultColor( normalText );
+}
+
+
+QColor KNConfig::Appearance::quoteColor( int depth ) const
+{
+ if ( u_seColors )
+ return c_olors[quoted1 + depth];
+ else
+ return defaultColor( quoted1 + depth );
+}
+
+
+QColor KNConfig::Appearance::linkColor() const
+{
+ if(u_seColors)
+ return c_olors[url];
+ else
+ return defaultColor( url );
+
+}
+
+
+QColor KNConfig::Appearance::unreadThreadColor() const
+{
+ if(u_seColors)
+ return c_olors[unreadThread];
+ else
+ return defaultColor( unreadThread );
+}
+
+
+QColor KNConfig::Appearance::readThreadColor() const
+{
+ if(u_seColors)
+ return c_olors[readThread];
+ else
+ return defaultColor( readThread );
+}
+
+
+QColor KNConfig::Appearance::unreadArticleColor() const
+{
+ if(u_seColors)
+ return c_olors[unreadArticle];
+ else
+ return defaultColor( unreadArticle );
+}
+
+
+QColor KNConfig::Appearance::readArticleColor() const
+{
+ if(u_seColors)
+ return c_olors[readArticle];
+ else
+ return defaultColor( readArticle );
+}
+
+
+QFont KNConfig::Appearance::articleFont() const
+{
+ if(u_seFonts)
+ return f_onts[article];
+ else
+ return defaultFont( article );
+}
+
+
+QFont KNConfig::Appearance::articleFixedFont() const
+{
+ if(u_seFonts)
+ return f_onts[articleFixed];
+ else
+ return defaultFont( articleFixed );
+}
+
+
+QFont KNConfig::Appearance::composerFont() const
+{
+ if(u_seFonts)
+ return f_onts[composer];
+ else
+ return defaultFont( composer );
+}
+
+
+QFont KNConfig::Appearance::groupListFont() const
+{
+ if(u_seFonts)
+ return f_onts[groupList];
+ else
+ return defaultFont( groupList );
+}
+
+
+QFont KNConfig::Appearance::articleListFont() const
+{
+ if(u_seFonts)
+ return f_onts[articleList];
+ else
+ return defaultFont( articleList );
+}
+
+
+QColor KNConfig::Appearance::defaultColor(int i) const
+{
+ // defaults should match libkdepim/csshelper.cpp
+ switch(i) {
+
+ case background:
+ return kapp->palette().active().base();
+
+ case alternateBackground:
+ return KGlobalSettings::alternateBackgroundColor();
+
+ case quoted1:
+ return QColor( 0x00, 0x80, 0x00 );
+ case quoted2:
+ return QColor( 0x00, 0x70, 0x00 );
+ case quoted3:
+ return QColor( 0x00, 0x60, 0x00 );
+
+ case normalText:
+ case unreadThread:
+ return kapp->palette().active().text();
+
+ case url:
+ return KGlobalSettings::linkColor();
+
+ case readThread:
+ return kapp->palette().disabled().text();
+
+ case unreadArticle:
+ return QColor( 183, 154, 11 );
+
+ case readArticle:
+ return QColor( 136, 136, 136 );
+
+ case signOkKeyOk:
+ return QColor( 0x40, 0xFF, 0x00 );
+ case signOkKeyBad:
+ case signWarn:
+ return QColor( 0xFF, 0xFF, 0x40 );
+ case signErr:
+ return Qt::red;
+
+ case htmlWarning:
+ return QColor( 0xFF, 0x40, 0x40 );
+ }
+
+ return kapp->palette().disabled().text();
+}
+
+
+QFont KNConfig::Appearance::defaultFont(int i) const
+{
+ if ( i == articleFixed || i == composer )
+ return KGlobalSettings::fixedFont();
+ else
+ return KGlobalSettings::generalFont();
+}
+
+
+void KNConfig::Appearance::recreateLVIcons()
+{
+ QPixmap tempPix = UserIcon("greyball");
+
+ QImage tempImg=tempPix.convertToImage();
+ KIconEffect::colorize(tempImg, readArticleColor(), 1.0);
+ i_cons[greyBall].convertFromImage(tempImg);
+
+ tempImg=tempPix.convertToImage();
+ KIconEffect::colorize(tempImg, unreadArticleColor(), 1.0);
+ i_cons[redBall].convertFromImage(tempImg);
+
+ tempPix = UserIcon("greyballchk");
+
+ tempImg=tempPix.convertToImage();
+ KIconEffect::colorize(tempImg, readArticleColor(), 1.0);
+ i_cons[greyBallChkd].convertFromImage(tempImg);
+
+ tempImg=tempPix.convertToImage();
+ KIconEffect::colorize(tempImg, unreadArticleColor(), 1.0);
+ i_cons[redBallChkd].convertFromImage(tempImg);
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::ReadNewsGeneral::ReadNewsGeneral()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("READNEWS");
+
+ a_utoCheck=conf->readBoolEntry("autoCheck", true);
+ m_axFetch=conf->readNumEntry("maxFetch", 1000);
+ if (m_axFetch<0) m_axFetch = 0;
+ a_utoMark=conf->readBoolEntry("autoMark", true);
+ m_arkSecs=conf->readNumEntry("markSecs", 0);
+ if (m_arkSecs<0) m_arkSecs = 0;
+ m_arkCrossposts=conf->readBoolEntry("markCrossposts", true);
+ s_martScrolling=conf->readBoolEntry("smartScrolling", true);
+ t_otalExpand=conf->readBoolEntry("totalExpand", true);
+ d_efaultExpand=conf->readBoolEntry("defaultExpand", false);
+ s_howLines=conf->readBoolEntry("showLines3", true);
+ s_howScore=conf->readBoolEntry("showScore3", true);
+ s_howUnread=conf->readBoolEntry("showUnread", true);
+ s_howThreads = conf->readBoolEntry("showThreads", true);
+ mDateFormat = (KMime::DateFormatter::FormatType) conf->readNumEntry( "dateFormat", KMime::DateFormatter::Localized );
+ mDateCustomFormat = conf->readEntry( "customDateFormat" );
+
+ conf->setGroup("CACHE");
+ c_ollCacheSize=conf->readNumEntry("collMemSize", 2048);
+ a_rtCacheSize=conf->readNumEntry("artMemSize", 1024);
+}
+
+
+KNConfig::ReadNewsGeneral::~ReadNewsGeneral()
+{
+}
+
+
+void KNConfig::ReadNewsGeneral::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::ReadNewsGeneral::save()" << endl;
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("READNEWS");
+
+ conf->writeEntry("autoCheck", a_utoCheck);
+ conf->writeEntry("maxFetch", m_axFetch);
+ conf->writeEntry("autoMark", a_utoMark);
+ conf->writeEntry("markSecs", m_arkSecs);
+ conf->writeEntry("markCrossposts", m_arkCrossposts);
+ conf->writeEntry("smartScrolling", s_martScrolling);
+ conf->writeEntry("totalExpand", t_otalExpand);
+ conf->writeEntry("defaultExpand", d_efaultExpand);
+ conf->writeEntry("showLines3", s_howLines);
+ conf->writeEntry("showScore3", s_howScore);
+ conf->writeEntry("showUnread", s_howUnread);
+ conf->writeEntry("showThreads", s_howThreads);
+ conf->writeEntry( "dateFormat", mDateFormat );
+ conf->writeEntry( "customDateFormat", mDateCustomFormat );
+
+ conf->setGroup("CACHE");
+ conf->writeEntry("collMemSize", c_ollCacheSize);
+ conf->writeEntry("artMemSize", a_rtCacheSize);
+ conf->sync();
+ d_irty = false;
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::ReadNewsNavigation::ReadNewsNavigation()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("READNEWS_NAVIGATION");
+
+ m_arkAllReadGoNext=conf->readBoolEntry("markAllReadGoNext", false);
+ m_arkThreadReadGoNext=conf->readBoolEntry("markThreadReadGoNext", false);
+ m_arkThreadReadCloseThread=conf->readBoolEntry("markThreadReadCloseThread", false);
+ i_gnoreThreadGoNext=conf->readBoolEntry("ignoreThreadGoNext", false);
+ i_gnoreThreadCloseThread=conf->readBoolEntry("ignoreThreadCloseThread", false);
+ mLeaveGroupMarkAsRead = conf->readBoolEntry( "leaveGroupMarkAsRead", false );
+}
+
+
+KNConfig::ReadNewsNavigation::~ReadNewsNavigation()
+{
+}
+
+
+void KNConfig::ReadNewsNavigation::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::ReadNewsNavigation::save()" << endl;
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("READNEWS_NAVIGATION");
+
+ conf->writeEntry("markAllReadGoNext", m_arkAllReadGoNext);
+ conf->writeEntry("markThreadReadGoNext", m_arkThreadReadGoNext);
+ conf->writeEntry("markThreadReadCloseThread", m_arkThreadReadCloseThread);
+ conf->writeEntry("ignoreThreadGoNext", i_gnoreThreadGoNext);
+ conf->writeEntry("ignoreThreadCloseThread", i_gnoreThreadCloseThread);
+ conf->writeEntry("leaveGroupMarkAsRead=true", mLeaveGroupMarkAsRead );
+ conf->sync();
+ d_irty = false;
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::ReadNewsViewer::ReadNewsViewer()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("READNEWS");
+
+ r_ewrapBody=conf->readBoolEntry("rewrapBody", true);
+ r_emoveTrailingNewlines=conf->readBoolEntry("removeTrailingNewlines", true);
+ s_howSig=conf->readBoolEntry("showSig", true);
+ i_nterpretFormatTags=conf->readBoolEntry("interpretFormatTags", true);
+ q_uoteCharacters=conf->readEntry("quoteCharacters",">:");
+ o_penAtt=conf->readBoolEntry("openAtt", false) ;
+ s_howAlts=conf->readBoolEntry("showAlts", false);
+ u_seFixedFont=conf->readBoolEntry("articleBodyFixedFont", false);
+ mShowRefBar = conf->readBoolEntry( "showRefBar", true );
+ mAlwaysShowHTML = conf->readBoolEntry( "alwaysShowHTML", false );
+}
+
+
+KNConfig::ReadNewsViewer::~ReadNewsViewer()
+{
+}
+
+
+void KNConfig::ReadNewsViewer::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::ReadNewsViewer::save()" << endl;
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("READNEWS");
+
+ conf->writeEntry("rewrapBody", r_ewrapBody);
+ conf->writeEntry("removeTrailingNewlines", r_emoveTrailingNewlines);
+ conf->writeEntry("showSig", s_howSig);
+ conf->writeEntry("interpretFormatTags", i_nterpretFormatTags);
+ conf->writeEntry("quoteCharacters",q_uoteCharacters);
+ conf->writeEntry("openAtt", o_penAtt);
+ conf->writeEntry("showAlts", s_howAlts);
+ conf->writeEntry("articleBodyFixedFont", u_seFixedFont);
+ conf->writeEntry("showRefBar", mShowRefBar );
+ conf->writeEntry( "alwaysShowHTML", mAlwaysShowHTML );
+ conf->sync();
+ d_irty = false;
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::DisplayedHeaders::DisplayedHeaders()
+{
+ QString fname( locate("data","knode/headers.rc") );
+
+ if (!fname.isNull()) {
+ KSimpleConfig headerConf(fname,true);
+ QStringList headers = headerConf.groupList();
+ headers.remove("<default>");
+ headers.sort();
+
+ KNDisplayedHeader *h;
+ QValueList<int> flags;
+
+ QStringList::Iterator it;
+ for( it = headers.begin(); it != headers.end(); ++it ) {
+ h=createNewHeader();
+ headerConf.setGroup((*it));
+ h->setName(headerConf.readEntry("Name"));
+ h->setTranslateName(headerConf.readBoolEntry("Translate_Name",true));
+ h->setHeader(headerConf.readEntry("Header"));
+ flags=headerConf.readIntListEntry("Flags");
+ if(h->name().isNull() || h->header().isNull() || (flags.count()!=8)) {
+ kdDebug(5003) << "KNConfig::DisplayedHeaders::DisplayedHeaders() : ignoring invalid/incomplete Header" << endl;
+ remove(h);
+ }
+ else {
+ for (int i=0; i<8; i++)
+ h->setFlag(i, (flags[i]>0));
+ h->createTags();
+ }
+ }
+ }
+}
+
+
+KNConfig::DisplayedHeaders::~DisplayedHeaders()
+{
+ for ( QValueList<KNDisplayedHeader*>::Iterator it = mHeaderList.begin(); it != mHeaderList.end(); ++it )
+ delete (*it);
+}
+
+
+void KNConfig::DisplayedHeaders::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::DisplayedHeaders::save()" << endl;
+
+ QString dir(locateLocal("data","knode/"));
+ if (dir.isNull()) {
+ KNHelper::displayInternalFileError();
+ return;
+ }
+ KSimpleConfig headerConf(dir+"headers.rc");
+ QStringList oldHeaders = headerConf.groupList();
+
+ QStringList::Iterator oldIt=oldHeaders.begin();
+ for( ;oldIt != oldHeaders.end(); ++oldIt ) // remove all old groups
+ headerConf.deleteGroup((*oldIt)); // find a better way to do it?
+
+ QValueList<int> flags;
+ int idx=0;
+ QString group;
+
+ for ( QValueList<KNDisplayedHeader*>::Iterator it = mHeaderList.begin(); it != mHeaderList.end(); ++it ) {
+ group.setNum(idx++);
+ while (group.length()<3)
+ group.prepend("0");
+ headerConf.setGroup(group);
+ headerConf.writeEntry("Name",(*it)->name());
+ headerConf.writeEntry("Translate_Name",(*it)->translateName());
+ headerConf.writeEntry("Header",(*it)->header());
+ flags.clear();
+ for (int i=0; i<8; i++) {
+ if ((*it)->flag(i))
+ flags << 1;
+ else
+ flags << 0;
+ }
+ headerConf.writeEntry("Flags",flags);
+ }
+ headerConf.sync();
+ d_irty = false;
+}
+
+
+KNDisplayedHeader* KNConfig::DisplayedHeaders::createNewHeader()
+{
+ KNDisplayedHeader *h=new KNDisplayedHeader();
+ mHeaderList.append( h );
+
+ return h;
+}
+
+
+void KNConfig::DisplayedHeaders::remove(KNDisplayedHeader *h)
+{
+ if ( !mHeaderList.remove( h ) )
+ kdDebug(5003) << "KNConfig::DisplayedHeaders::remove() : cannot find pointer in list!" << endl;
+
+}
+
+
+void KNConfig::DisplayedHeaders::up(KNDisplayedHeader *h)
+{
+ int idx = mHeaderList.findIndex( h );
+ if ( idx != -1 ) {
+ mHeaderList.remove( mHeaderList.at( idx ) );
+ mHeaderList.insert( mHeaderList.at( idx - 1 ), h );
+ }
+ else kdDebug(5003) << "KNConfig::DisplayedHeaders::up() : item not found in list" << endl;
+}
+
+
+void KNConfig::DisplayedHeaders::down(KNDisplayedHeader *h)
+{
+ int idx = mHeaderList.findIndex( h );
+ if ( idx != -1 ) {
+ mHeaderList.remove( mHeaderList.at( idx ) );
+ mHeaderList.insert( mHeaderList.at( idx + 1 ), h );
+ }
+ else kdDebug(5003) << "KNConfig::DisplayedHeaders::down() : item not found in list" << endl;
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::Scoring::Scoring()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("SCORING");
+
+ i_gnoredThreshold=conf->readNumEntry("ignoredThreshold", -100);
+ w_atchedThreshold=conf->readNumEntry("watchedThreshold", 100);
+}
+
+
+KNConfig::Scoring::~Scoring()
+{
+}
+
+
+void KNConfig::Scoring::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::Scoring::save()" << endl;
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("SCORING");
+
+ conf->writeEntry("ignoredThreshold", i_gnoredThreshold);
+ conf->writeEntry("watchedThreshold", w_atchedThreshold);
+ conf->sync();
+ d_irty = false;
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::XHeader::XHeader(const QString &s)
+{
+ if(s.left(2)=="X-") {
+ int pos=s.find(": ");
+ if(pos!=-1) {
+ n_ame=s.mid(2, pos-2).latin1();
+ pos+=2;
+ v_alue=s.mid(pos, s.length()-pos);
+ }
+ }
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::PostNewsTechnical::PostNewsTechnical()
+ : findComposerCSCache(113)
+{
+ findComposerCSCache.setAutoDelete(true);
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("POSTNEWS");
+
+ c_omposerCharsets=conf->readListEntry("ComposerCharsets");
+ if (c_omposerCharsets.isEmpty())
+ c_omposerCharsets=QStringList::split(',',"us-ascii,utf-8,iso-8859-1,iso-8859-2,"
+ "iso-8859-3,iso-8859-4,iso-8859-5,iso-8859-6,iso-8859-7,iso-8859-8,"
+ "iso-8859-9,iso-8859-10,iso-8859-13,iso-8859-14,iso-8859-15,koi8-r,koi8-u,"
+ "iso-2022-jp,iso-2022-jp-2,iso-2022-kr,euc-jp,euc-kr,Big5,gb2312");
+
+ c_harset=conf->readEntry("Charset").latin1();
+ if (c_harset.isEmpty()) {
+ QCString localeCharset(QTextCodec::codecForLocale()->mimeName());
+
+ // special logic for japanese users:
+ // "euc-jp" is default encoding for them, but in the news
+ // "iso-2022-jp" is used
+ if (localeCharset.lower() == "euc-jp")
+ localeCharset = "iso-2022-jp";
+
+ c_harset=findComposerCharset(localeCharset);
+ if (c_harset.isEmpty())
+ c_harset="iso-8859-1"; // shit
+ }
+
+ h_ostname=conf->readEntry("MIdhost").latin1();
+ a_llow8BitBody=conf->readBoolEntry("8BitEncoding",true);
+ u_seOwnCharset=conf->readBoolEntry("UseOwnCharset",true);
+ g_enerateMID=conf->readBoolEntry("generateMId", false);
+ d_ontIncludeUA=conf->readBoolEntry("dontIncludeUA", false);
+ u_seExternalMailer=conf->readBoolEntry("useExternalMailer", false);
+
+ QString dir(locateLocal("data","knode/"));
+ if (!dir.isNull()) {
+ QFile f(dir+"xheaders");
+ if(f.open(IO_ReadOnly)) {
+ QTextStream ts(&f);
+ while(!ts.eof())
+ x_headers.append( XHeader(ts.readLine()) );
+
+ f.close();
+ }
+ }
+}
+
+
+KNConfig::PostNewsTechnical::~PostNewsTechnical()
+{
+}
+
+
+void KNConfig::PostNewsTechnical::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::PostNewsTechnical::save()" << endl;
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("POSTNEWS");
+
+ conf->writeEntry("ComposerCharsets", c_omposerCharsets);
+ conf->writeEntry("Charset", QString::fromLatin1(c_harset));
+ conf->writeEntry("8BitEncoding", a_llow8BitBody);
+ conf->writeEntry("UseOwnCharset", u_seOwnCharset);
+ conf->writeEntry("generateMId", g_enerateMID);
+ conf->writeEntry("MIdhost", QString::fromLatin1(h_ostname));
+ conf->writeEntry("dontIncludeUA", d_ontIncludeUA);
+ conf->writeEntry("useExternalMailer", u_seExternalMailer);
+
+ QString dir(locateLocal("data","knode/"));
+ if (dir.isNull())
+ KNHelper::displayInternalFileError();
+ else {
+ QFile f(dir+"xheaders");
+ if(f.open(IO_WriteOnly)) {
+ QTextStream ts(&f);
+ XHeaders::Iterator it;
+ for(it=x_headers.begin(); it!=x_headers.end(); ++it)
+ ts << (*it).header() << "\n";
+ f.close();
+ }
+ else
+ KNHelper::displayInternalFileError();
+ }
+ conf->sync();
+ d_irty = false;
+}
+
+
+int KNConfig::PostNewsTechnical::indexForCharset(const QCString &str)
+{
+ int i=0;
+ bool found=false;
+ for ( QStringList::Iterator it = c_omposerCharsets.begin(); it != c_omposerCharsets.end(); ++it ) {
+ if ((*it).lower() == str.lower().data()) {
+ found = true;
+ break;
+ }
+ i++;
+ }
+ if (!found) {
+ i=0;
+ for ( QStringList::Iterator it = c_omposerCharsets.begin(); it != c_omposerCharsets.end(); ++it ) {
+ if ((*it).lower() == c_harset.lower().data()) {
+ found = true;
+ break;
+ }
+ i++;
+ }
+ if (!found)
+ i=0;
+ }
+ return i;
+}
+
+
+QCString KNConfig::PostNewsTechnical::findComposerCharset(QCString cs)
+{
+ QCString *ret=findComposerCSCache.find(cs);
+ if (ret)
+ return *ret;
+
+ QCString s;
+
+ QStringList::Iterator it;
+ for( it = c_omposerCharsets.begin(); it != c_omposerCharsets.end(); ++it ) {
+ // match by name
+ if ((*it).lower()==cs.lower().data()) {
+ s = (*it).latin1();
+ break;
+ }
+ }
+
+ if (s.isEmpty()) {
+ for( it = c_omposerCharsets.begin(); it != c_omposerCharsets.end(); ++it ) {
+ // match by charset, avoid to return "us-ascii" for iso-8859-1
+ if ((*it).lower()!="us-ascii") {
+ QTextCodec *composerCodec = QTextCodec::codecForName((*it).latin1());
+ QTextCodec *csCodec = QTextCodec::codecForName(cs);
+ if ((composerCodec != 0) &&
+ (csCodec != 0) &&
+ (0 == strcmp(composerCodec->name(), csCodec->name()))) {
+ s = (*it).latin1();
+ break;
+ }
+ }
+ }
+ }
+
+ if (s.isEmpty())
+ s = "us-ascii";
+
+ findComposerCSCache.insert(cs, new QCString(s));
+
+ return s;
+}
+
+
+//==============================================================================================================
+
+
+KNConfig::PostNewsComposer::PostNewsComposer()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("POSTNEWS");
+
+ w_ordWrap=conf->readBoolEntry("wordWrap",true);
+ m_axLen=conf->readNumEntry("maxLength", 76);
+ a_ppSig=conf->readBoolEntry("appSig",true);
+ r_ewrap=conf->readBoolEntry("rewrap",true);
+ i_ncSig=conf->readBoolEntry("incSig",false);
+ c_ursorOnTop=conf->readBoolEntry("cursorOnTop",false);
+ u_seExtEditor=conf->readBoolEntry("useExternalEditor",false);
+ i_ntro=conf->readEntry("Intro","%NAME wrote:");
+ e_xternalEditor=conf->readEntry("externalEditor","kwrite %f");
+}
+
+
+KNConfig::PostNewsComposer::~PostNewsComposer()
+{
+}
+
+
+void KNConfig::PostNewsComposer::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::PostNewsComposer::save()" << endl;
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("POSTNEWS");
+
+ conf->writeEntry("wordWrap", w_ordWrap);
+ conf->writeEntry("maxLength", m_axLen);
+ conf->writeEntry("appSig", a_ppSig);
+ conf->writeEntry("rewrap",r_ewrap);
+ conf->writeEntry("incSig", i_ncSig);
+ conf->writeEntry("cursorOnTop", c_ursorOnTop);
+ conf->writeEntry("useExternalEditor", u_seExtEditor);
+ conf->writeEntry("Intro", i_ntro);
+ conf->writeEntry("externalEditor", e_xternalEditor);
+ conf->sync();
+
+ d_irty = false;
+}
+
+//==============================================================================================================
+
+
+
+
+// BEGIN: Cleanup configuration ===============================================
+
+
+KNConfig::Cleanup::Cleanup( bool global ) :
+ // default values for new accounts / groups
+ d_oExpire( true ), r_emoveUnavailable( true ), p_reserveThr( true ),
+ e_xpireInterval( 5 ), r_eadMaxAge( 10 ), u_nreadMaxAge( 15 ),
+ mGlobal(global), mDefault(!global), mLastExpDate( QDate::currentDate() )
+{
+ if (mGlobal) {
+ KConfig *conf = knGlobals.config();
+ conf->setGroup( "EXPIRE" );
+ loadConfig( conf );
+ }
+}
+
+
+void KNConfig::Cleanup::loadConfig(KConfigBase *conf)
+{
+ // group expire settings
+ d_oExpire = conf->readBoolEntry( "doExpire", true );
+ r_emoveUnavailable = conf->readBoolEntry( "removeUnavailable", true );
+ p_reserveThr = conf->readBoolEntry( "saveThreads", true );
+ e_xpireInterval = conf->readNumEntry( "expInterval", 5 );
+ r_eadMaxAge = conf->readNumEntry( "readDays", 10 );
+ u_nreadMaxAge = conf->readNumEntry( "unreadDays", 15 );
+ mLastExpDate = conf->readDateTimeEntry( "lastExpire" ).date();
+
+ // folder compaction settings (only available globally)
+ if (mGlobal) {
+ d_oCompact = conf->readBoolEntry( "doCompact", true );
+ c_ompactInterval = conf->readNumEntry( "comInterval", 5 );
+ mLastCompDate = conf->readDateTimeEntry( "lastCompact" ).date();
+ }
+
+ if (!mGlobal)
+ mDefault = conf->readBoolEntry( "UseDefaultExpConf", true );
+}
+
+
+void KNConfig::Cleanup::saveConfig(KConfigBase *conf)
+{
+ // group expire settings
+ conf->writeEntry( "doExpire", d_oExpire );
+ conf->writeEntry( "removeUnavailable", r_emoveUnavailable );
+ conf->writeEntry( "saveThreads", p_reserveThr );
+ conf->writeEntry( "expInterval", e_xpireInterval );
+ conf->writeEntry( "readDays", r_eadMaxAge );
+ conf->writeEntry( "unreadDays", u_nreadMaxAge );
+ conf->writeEntry( "lastExpire", mLastExpDate );
+
+ // folder compaction settings (only available globally)
+ if (mGlobal) {
+ conf->writeEntry( "doCompact", d_oCompact );
+ conf->writeEntry( "comInterval", c_ompactInterval );
+ conf->writeEntry( "lastCompact", mLastCompDate );
+ }
+
+ if (!mGlobal)
+ conf->writeEntry( "UseDefaultExpConf", mDefault );
+
+ conf->sync();
+}
+
+
+void KNConfig::Cleanup::save()
+{
+ kdDebug(5003) << "KNConfig::Cleanup::save()" << endl;
+ if (mGlobal) {
+ KConfig *conf = knGlobals.config();
+ conf->setGroup( "EXPIRE" );
+ saveConfig( conf );
+ }
+}
+
+
+bool KNConfig::Cleanup::expireToday()
+{
+ if (!d_oExpire)
+ return false;
+
+ QDate today = QDate::currentDate();
+ if (mLastExpDate == today)
+ return false;
+
+ return (mLastExpDate.daysTo( today ) >= e_xpireInterval);
+}
+
+
+void KNConfig::Cleanup::setLastExpireDate()
+{
+ mLastExpDate = QDateTime::currentDateTime();
+}
+
+
+bool KNConfig::Cleanup::compactToday()
+{
+ if (!d_oCompact)
+ return false;
+
+ QDate today = QDate::currentDate();
+ if (mLastCompDate == today)
+ return false;
+
+ return (mLastCompDate.daysTo( today ) >= c_ompactInterval);
+}
+
+
+void KNConfig::Cleanup::setLastCompactDate()
+{
+ mLastCompDate = QDateTime::currentDateTime();
+}
+
+
+// END: Cleanup configuration =================================================
+
+
+
+/*KNConfig::Cache::Cache()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("CACHE");
+
+ m_emMaxArt=conf->readNumEntry("memMaxArt", 1000);
+ m_emMaxKB=conf->readNumEntry("memMaxKB", 1024);
+
+ d_iskMaxArt=conf->readNumEntry("diskMaxArt", 1000);
+ d_iskMaxKB=conf->readNumEntry("diskMaxKB", 1024);
+}
+
+
+KNConfig::Cache::~Cache()
+{
+}
+
+
+void KNConfig::Cache::save()
+{
+ if(!d_irty)
+ return;
+
+ kdDebug(5003) << "KNConfig::Cache::save()" << endl;
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("CACHE");
+
+ conf->writeEntry("memMaxArt", m_emMaxArt);
+ conf->writeEntry("memMaxKB", m_emMaxKB);
+
+ conf->writeEntry("diskMaxArt", d_iskMaxArt);
+ conf->writeEntry("diskMaxKB", d_iskMaxKB);
+
+ d_irty = false;
+}
+*/
+
+#include "knconfig.moc"
diff --git a/knode/knconfig.h b/knode/knconfig.h
new file mode 100644
index 000000000..2248a361c
--- /dev/null
+++ b/knode/knconfig.h
@@ -0,0 +1,566 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCONFIG_H
+#define KNCONFIG_H
+
+#include <qasciidict.h>
+#include <qcolor.h>
+#include <qdatetime.h>
+#include <qfont.h>
+#include <qobject.h>
+#include <qpixmap.h>
+
+#include <kconfig.h>
+
+#include <kmime_util.h>
+
+class KScoringRule;
+class KProcess;
+class KSpellConfig;
+namespace Kpgp {
+ class Config;
+}
+
+class KNNntpAccount;
+class KNAccountManager;
+class KNArticleFilter;
+class KNFilterManager;
+class KNDisplayedHeader;
+class KNServerInfo;
+class KNScoringManager;
+namespace KNConfig {
+ class IdentityWidget;
+}
+
+namespace KNConfig {
+
+class Base {
+
+ public:
+ Base() : d_irty(false) {}
+ virtual ~Base() {}
+
+ virtual void save() {}
+
+ bool dirty()const { return d_irty; }
+ void setDirty(bool b) { d_irty=b; }
+
+ protected:
+ bool d_irty;
+
+};
+
+
+class KDE_EXPORT Identity : public QObject, public Base {
+
+Q_OBJECT
+
+ friend class IdentityWidget;
+
+ public:
+ Identity(bool g=true);
+ ~Identity();
+
+ void loadConfig(KConfigBase *c);
+ void saveConfig(KConfigBase *c);
+ void save();
+ bool isEmpty();
+ bool isGlobal()const { return g_lobal; }
+
+ //personal information
+ bool hasName() { return (!n_ame.isEmpty()); }
+ QString name() const { return n_ame; }
+ void setName(const QString &s) { n_ame=s; }
+ bool emailIsValid();
+ bool hasEmail() { return (!e_mail.isEmpty()); }
+ QString email() { return e_mail; }
+ void setEmail(const QCString &s) { e_mail=s; }
+ bool hasReplyTo() { return (!r_eplyTo.isEmpty()); }
+ QString replyTo() { return r_eplyTo; }
+ void setReplyTo(const QString &s) { r_eplyTo=s; }
+ bool hasMailCopiesTo() { return (!m_ailCopiesTo.isEmpty()); }
+ QString mailCopiesTo() { return m_ailCopiesTo; }
+ void setMailCopiesTo(const QString &s) { m_ailCopiesTo=s; }
+ bool hasOrga() { return (!o_rga.isEmpty()); }
+ QString orga() const { return o_rga; }
+ void setOrga(const QString &s) { o_rga=s; }
+
+ // OpenPGP signing key
+ bool hasSigningKey() { return (!s_igningKey.isEmpty()); }
+ QCString signingKey() { return s_igningKey; }
+ void setSigningKey(const QCString &s) { s_igningKey=s;}
+
+ //signature
+ bool hasSignature() { return ( (u_seSigFile && !s_igPath.isEmpty()) || !s_igText.isEmpty() ); }
+ bool useSigFile() const { return u_seSigFile; }
+ bool useSigGenerator()const { return u_seSigGenerator; }
+ QString sigPath()const { return s_igPath; }
+ QString sigText()const { return s_igText; }
+ QString getSignature();
+ QString getSigGeneratorStdErr() { return s_igStdErr; }
+
+
+ protected slots:
+ void slotReceiveStdout(KProcess *proc, char *buffer, int buflen);
+ void slotReceiveStderr(KProcess *proc, char *buffer, int buflen);
+
+ protected:
+ QString n_ame,
+ e_mail,
+ o_rga,
+ r_eplyTo,
+ m_ailCopiesTo,
+ s_igText,
+ s_igContents,
+ s_igStdErr,
+ s_igPath;
+ QCString s_igningKey;
+ bool u_seSigFile,
+ u_seSigGenerator,
+ g_lobal;
+};
+
+
+class KDE_EXPORT Appearance : public Base {
+
+#define COL_CNT 16
+#define FNT_CNT 5
+#define HEX_CNT 4
+#define ICON_CNT 14
+
+ friend class AppearanceWidget;
+
+ public:
+ enum ColorIndex { background=0, alternateBackground=1, normalText=2, quoted1=3,
+ quoted2=4, quoted3=5, url=6, unreadThread=7, readThread=8,
+ unreadArticle=9, readArticle=10, signOkKeyOk=11, signOkKeyBad=12,
+ signWarn=13, signErr=14, htmlWarning=15 };
+
+ enum FontIndex { article=0, articleFixed=1, composer=2, groupList=3, articleList=4 };
+
+ enum IconIndex { greyBall=0, redBall=1, greyBallChkd=2,
+ redBallChkd=3, newFups=4, eyes=5,
+ ignore=6, mail=7, posting=8,
+ canceledPosting=9, savedRemote=10, group=11,
+ sendErr=12, null=13 };
+ Appearance();
+ ~Appearance();
+
+ void save();
+
+ QColor backgroundColor() const;
+ QColor alternateBackgroundColor() const;
+ QColor textColor() const;
+ QColor quoteColor( int depth ) const;
+ QColor linkColor() const;
+ QColor unreadThreadColor() const;
+ QColor readThreadColor() const;
+ QColor unreadArticleColor() const;
+ QColor readArticleColor() const;
+ QColor signOkKeyOkColor() const { return u_seColors ? c_olors[signOkKeyOk] : defaultColor( signOkKeyOk ); }
+ QColor signOkKeyBadColor() const { return u_seColors ? c_olors[signOkKeyBad] : defaultColor( signOkKeyBad ); }
+ QColor signWarnColor() const { return u_seColors ? c_olors[signWarn] : defaultColor( signWarn ); }
+ QColor signErrColor() const { return u_seColors ? c_olors[signErr] : defaultColor( signErr ); }
+ QColor htmlWarningColor() const { return u_seColors ? c_olors[htmlWarning] : defaultColor( htmlWarning ); }
+
+ QFont articleFont() const;
+ QFont articleFixedFont() const;
+ QFont composerFont() const;
+ QFont groupListFont() const;
+ QFont articleListFont() const;
+
+ const QPixmap& icon(IconIndex i) { return i_cons[i]; }
+
+ protected:
+ const QColor& color( int i ) const { return c_olors[i]; }
+ const QString& colorName( int i ) const { return c_olorNames[i]; }
+ int colorCount() const { return COL_CNT; }
+ QColor defaultColor(int i) const;
+
+ const QFont& font(int i) const { return f_onts[i]; }
+ const QString& fontName(int i) const { return f_ontNames[i]; }
+ int fontCount() const { return FNT_CNT; }
+ QFont defaultFont(int) const;
+
+ void recreateLVIcons();
+
+ bool u_seColors,
+ u_seFonts;
+ QColor c_olors[COL_CNT];
+ QString c_olorNames[COL_CNT];
+ QFont f_onts[FNT_CNT];
+ QString f_ontNames[FNT_CNT];
+ QPixmap i_cons[ICON_CNT];
+
+};
+
+
+class KDE_EXPORT ReadNewsGeneral : public Base {
+
+ friend class ReadNewsGeneralWidget;
+
+ public:
+ ReadNewsGeneral();
+ ~ReadNewsGeneral();
+
+ void save();
+
+ bool autoCheckGroups()const { return a_utoCheck; }
+ int maxToFetch()const { return m_axFetch; }
+ bool autoMark()const { return a_utoMark; }
+ int autoMarkSeconds()const { return m_arkSecs; }
+ bool markCrossposts()const { return m_arkCrossposts; }
+
+ bool smartScrolling()const { return s_martScrolling; }
+ bool totalExpandThreads()const { return t_otalExpand; }
+ bool defaultToExpandedThreads()const { return d_efaultExpand; }
+ bool showLines()const { return s_howLines; }
+ bool showScore()const { return s_howScore; }
+ bool showUnread()const { return s_howUnread; }
+
+ int collCacheSize()const { return c_ollCacheSize; }
+ int artCacheSize()const { return a_rtCacheSize; }
+
+ bool showThreads()const { return s_howThreads; }
+ void setShowThreads(bool b) { d_irty=true; s_howThreads=b;}
+
+ KMime::DateFormatter::FormatType dateFormat() const { return mDateFormat; }
+ QString dateCustomFormat() const { return mDateCustomFormat; }
+
+ void setShowLines( bool show ) { d_irty = true; s_howLines = show; }
+ void setShowScore( bool show ) { d_irty = true; s_howScore = show; }
+
+ protected:
+ bool a_utoCheck,
+ a_utoMark,
+ m_arkCrossposts,
+ s_martScrolling,
+ t_otalExpand,
+ d_efaultExpand,
+ s_howLines,
+ s_howScore,
+ s_howUnread,
+ s_howThreads;
+
+ int m_axFetch,
+ m_arkSecs,
+ c_ollCacheSize,
+ a_rtCacheSize;
+
+ KMime::DateFormatter::FormatType mDateFormat;
+ QString mDateCustomFormat;
+
+};
+
+
+class KDE_EXPORT ReadNewsNavigation : public Base {
+
+ friend class ReadNewsNavigationWidget;
+
+ public:
+ ReadNewsNavigation();
+ ~ReadNewsNavigation();
+
+ void save();
+
+ bool markAllReadGoNext()const { return m_arkAllReadGoNext; }
+ bool markThreadReadGoNext() const { return m_arkThreadReadGoNext; }
+ bool markThreadReadCloseThread()const { return m_arkThreadReadCloseThread; }
+ bool ignoreThreadGoNext()const { return i_gnoreThreadGoNext; }
+ bool ignoreThreadCloseThread()const { return i_gnoreThreadCloseThread; }
+ bool leaveGroupMarkAsRead() const { return mLeaveGroupMarkAsRead; }
+
+ protected:
+ bool m_arkAllReadGoNext,
+ m_arkThreadReadGoNext,
+ m_arkThreadReadCloseThread,
+ i_gnoreThreadGoNext,
+ i_gnoreThreadCloseThread,
+ mLeaveGroupMarkAsRead;
+
+};
+
+
+class KDE_EXPORT ReadNewsViewer : public Base {
+
+ friend class ReadNewsViewerWidget;
+
+ public:
+ ReadNewsViewer();
+ ~ReadNewsViewer();
+
+ void save();
+
+ bool rewrapBody()const { return r_ewrapBody; }
+ bool removeTrailingNewlines()const { return r_emoveTrailingNewlines; }
+ bool showSignature()const { return s_howSig; }
+ bool interpretFormatTags()const { return i_nterpretFormatTags; }
+ void setInterpretFormatTags( bool f ) { d_irty = true; i_nterpretFormatTags = f; }
+ QString quoteCharacters()const { return q_uoteCharacters; }
+
+ bool openAttachmentsOnClick()const { return o_penAtt; }
+ bool showAlternativeContents()const { return s_howAlts; }
+
+ bool useFixedFont() const { return u_seFixedFont; }
+ void setUseFixedFont(bool b) { d_irty = true; u_seFixedFont=b; }
+
+ bool showRefBar() const { return mShowRefBar; }
+ void setShowRefBar( bool b ) { d_irty = true; mShowRefBar = b; }
+
+ bool alwaysShowHTML() const { return mAlwaysShowHTML; }
+ void setAlwaysShowHTML( bool b ) { d_irty = true; mAlwaysShowHTML = b; }
+
+ protected:
+ bool r_ewrapBody,
+ r_emoveTrailingNewlines,
+ s_howSig,
+ i_nterpretFormatTags,
+ o_penAtt,
+ s_howAlts,
+ u_seFixedFont,
+ mShowRefBar,
+ mAlwaysShowHTML;
+ QString q_uoteCharacters;
+};
+
+
+class KDE_EXPORT DisplayedHeaders : public Base
+{
+ public:
+ DisplayedHeaders();
+ ~DisplayedHeaders();
+
+ void save();
+
+ KNDisplayedHeader* createNewHeader();
+ void remove(KNDisplayedHeader *h);
+ void up(KNDisplayedHeader *h);
+ void down(KNDisplayedHeader *h);
+
+ QValueList<KNDisplayedHeader*> headers() const { return mHeaderList; }
+
+
+ protected:
+ QValueList<KNDisplayedHeader*> mHeaderList;
+
+};
+
+
+class KDE_EXPORT Scoring : public Base {
+
+ friend class ScoringWidget;
+
+ public:
+ Scoring();
+ ~Scoring();
+
+ void save();
+
+ int ignoredThreshold() { return i_gnoredThreshold; }
+ int watchedThreshold() { return w_atchedThreshold; }
+
+ protected:
+ int i_gnoredThreshold,
+ w_atchedThreshold;
+
+};
+
+
+class KDE_EXPORT XHeader {
+
+ public:
+ XHeader() {}
+ XHeader(const QString &s);
+ XHeader(const XHeader &s) { n_ame=s.n_ame; v_alue=s.v_alue; }
+ ~XHeader() {}
+
+ XHeader& operator=(const XHeader &s) { n_ame=s.n_ame; v_alue=s.v_alue; return (*this); }
+
+ QCString name() { return n_ame; }
+ QString value() { return v_alue; }
+ QString header() { return (QString::fromLatin1(("X-"+n_ame+": "))+v_alue); }
+
+ protected:
+ QCString n_ame;
+ QString v_alue;
+
+};
+typedef QValueList<XHeader> XHeaders;
+
+
+class KDE_EXPORT PostNewsTechnical : public Base {
+
+ friend class PostNewsTechnicalWidget;
+ friend class SmtpAccountWidget;
+
+ public:
+ PostNewsTechnical();
+ ~PostNewsTechnical();
+
+ void save();
+
+ QCString charset() const { return c_harset; }
+ QStringList composerCharsets() { return c_omposerCharsets; }
+ int indexForCharset(const QCString &str);
+ QCString findComposerCharset(QCString cs);
+
+ bool allow8BitBody() const { return a_llow8BitBody; }
+ bool useOwnCharset() const { return u_seOwnCharset; }
+ bool generateMessageID()const { return g_enerateMID; }
+ QCString hostname()const { return h_ostname; }
+ XHeaders& xHeaders() { return x_headers; }
+ bool noUserAgent()const { return d_ontIncludeUA; }
+ bool useExternalMailer()const { return u_seExternalMailer; }
+
+ protected:
+ QCString c_harset,
+ h_ostname;
+ QStringList c_omposerCharsets;
+
+ bool a_llow8BitBody,
+ u_seOwnCharset,
+ g_enerateMID,
+ d_ontIncludeUA,
+ u_seExternalMailer;
+
+ XHeaders x_headers;
+
+ QAsciiDict<QCString> findComposerCSCache;
+};
+
+
+class PostNewsComposer : public Base {
+
+ friend class PostNewsComposerWidget;
+
+ public:
+ PostNewsComposer();
+ ~PostNewsComposer();
+
+ void save();
+
+ bool wordWrap()const { return w_ordWrap; }
+ int maxLineLength()const { return m_axLen; }
+ bool appendOwnSignature()const { return a_ppSig; }
+
+ QString intro()const { return i_ntro; }
+ bool rewrap()const { return r_ewrap; }
+ bool includeSignature()const { return i_ncSig; }
+ bool cursorOnTop()const { return c_ursorOnTop; }
+
+ QString externalEditor()const { return e_xternalEditor; }
+ bool useExternalEditor()const { return u_seExtEditor; }
+
+
+ protected:
+ int m_axLen;
+ bool w_ordWrap,
+ a_ppSig,
+ r_ewrap,
+ i_ncSig,
+ c_ursorOnTop,
+ u_seExtEditor;
+ QString i_ntro,
+ e_xternalEditor;
+
+};
+
+
+//BEGIN: Cleanup configuration -----------------------------------------------
+
+class KDE_EXPORT Cleanup : public Base {
+
+ friend class CleanupWidget;
+ friend class GroupCleanupWidget;
+
+ public:
+ Cleanup( bool global = true );
+ ~Cleanup() {}
+
+ void loadConfig( KConfigBase *conf );
+ void saveConfig( KConfigBase *conf );
+ void save();
+
+ //expire
+ int maxAgeForRead() const { return r_eadMaxAge; }
+ int maxAgeForUnread() const { return u_nreadMaxAge; }
+ bool removeUnavailable() const { return r_emoveUnavailable; }
+ bool preserveThreads() const { return p_reserveThr; }
+ bool isGlobal() const { return mGlobal; }
+ bool useDefault() const { return mDefault; }
+ bool expireToday();
+ void setLastExpireDate();
+
+ void setUseDefault( bool def ) { mDefault = def; }
+
+ //compact
+ bool compactToday();
+ void setLastCompactDate();
+
+
+ protected:
+ bool d_oExpire,
+ r_emoveUnavailable,
+ p_reserveThr,
+ d_oCompact;
+ int e_xpireInterval,
+ r_eadMaxAge,
+ u_nreadMaxAge,
+ c_ompactInterval;
+
+ private:
+ /** global vs. per account or per group configuration */
+ bool mGlobal;
+ /** use default cleanup configuration */
+ bool mDefault;
+ /** last expiration and last comapction date */
+ QDateTime mLastExpDate, mLastCompDate;
+
+};
+
+//END: Cleanup configuration -------------------------------------------------
+
+
+/*class Cache : public Base {
+
+ friend class CacheWidget;
+
+ public:
+ Cache();
+ ~Cache();
+
+ void save();
+
+ // memory-cache
+ int memoryMaxArticles() { return m_emMaxArt; }
+ int memoryMaxKBytes() { return m_emMaxKB; }
+
+ // disk-cache
+ int diskMaxArticles() { return d_iskMaxArt; }
+ int diskMaxKBytes() { return d_iskMaxKB; }
+
+
+ protected:
+ int m_emMaxArt,
+ m_emMaxKB,
+ d_iskMaxArt,
+ d_iskMaxKB;
+
+};*/
+
+
+} //KNConfig
+
+#endif //KNCONFIG_H
diff --git a/knode/knconfigmanager.cpp b/knode/knconfigmanager.cpp
new file mode 100644
index 000000000..b117f3c49
--- /dev/null
+++ b/knode/knconfigmanager.cpp
@@ -0,0 +1,133 @@
+/*
+ knconfigmanager.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include "knconfigmanager.h"
+
+#include <kcmultidialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kwin.h>
+
+#include <qhbox.h>
+
+#include "utilities.h"
+#include "knglobals.h"
+#include "articlewidget.h"
+#include "knarticlefactory.h"
+#include "knmainwidget.h"
+
+
+KNConfigManager::KNConfigManager(QObject *p, const char *n)
+ : QObject(p, n), d_ialog(0)
+{
+ i_dentity = new KNConfig::Identity();
+ a_ppearance = new KNConfig::Appearance();
+ r_eadNewsGeneral = new KNConfig::ReadNewsGeneral();
+ r_eadNewsNavigation = new KNConfig::ReadNewsNavigation();
+ r_eadNewsViewer = new KNConfig::ReadNewsViewer();
+ d_isplayedHeaders = new KNConfig::DisplayedHeaders();
+ s_coring = new KNConfig::Scoring();
+ p_ostNewsTechnical = new KNConfig::PostNewsTechnical();
+ p_ostNewsCompose = new KNConfig::PostNewsComposer();
+ c_leanup = new KNConfig::Cleanup();
+ //c_ache = new KNConfig::Cache();
+}
+
+
+KNConfigManager::~KNConfigManager()
+{
+ delete i_dentity;
+ delete a_ppearance;
+ delete r_eadNewsGeneral;
+ delete r_eadNewsNavigation;
+ delete r_eadNewsViewer;
+ delete d_isplayedHeaders;
+ delete s_coring;
+ delete p_ostNewsTechnical;
+ delete p_ostNewsCompose;
+ delete c_leanup;
+ //delete c_ache;
+}
+
+
+void KNConfigManager::configure()
+{
+ if(!d_ialog) {
+ d_ialog=new KNConfigDialog(knGlobals.topWidget, "Preferences_Dlg");
+ connect(d_ialog, SIGNAL(finished()), this, SLOT(slotDialogDone()));
+ d_ialog->show();
+ }
+ else
+ KWin::activateWindow(d_ialog->winId());
+}
+
+
+void KNConfigManager::syncConfig()
+{
+ a_ppearance->save();
+ r_eadNewsGeneral->save();
+ r_eadNewsNavigation->save();
+ r_eadNewsViewer->save();
+ d_isplayedHeaders->save();
+ s_coring->save();
+ p_ostNewsTechnical->save();
+ p_ostNewsCompose->save();
+ c_leanup->save();
+ //c_ache->save();
+}
+
+
+void KNConfigManager::slotDialogDone()
+{
+ d_ialog->delayedDestruct();
+ d_ialog=0;
+}
+
+
+//===================================================================================================
+
+
+KNConfigDialog::KNConfigDialog(QWidget *p, const char *n)
+ : KCMultiDialog(p, n)
+{
+ addModule ( "knode_config_identity", false );
+ addModule ( "knode_config_accounts", false );
+ addModule ( "knode_config_appearance", false );
+ addModule ( "knode_config_read_news", false );
+ addModule ( "knode_config_post_news", false );
+ addModule ( "knode_config_privacy", false );
+ addModule ( "knode_config_cleanup", false );
+
+ setHelp("anc-setting-your-identity");
+
+ connect(this, SIGNAL(configCommitted()), this, SLOT(slotConfigCommitted()));
+}
+
+
+void KNConfigDialog::slotConfigCommitted()
+{
+ knGlobals.configManager()->syncConfig();
+
+ KNode::ArticleWidget::configChanged();
+ if(knGlobals.top)
+ knGlobals.top->configChanged();
+ if(knGlobals.artFactory)
+ knGlobals.artFactory->configChanged();
+}
+
+
+//-----------------------------
+#include "knconfigmanager.moc"
diff --git a/knode/knconfigmanager.h b/knode/knconfigmanager.h
new file mode 100644
index 000000000..6f0db527d
--- /dev/null
+++ b/knode/knconfigmanager.h
@@ -0,0 +1,83 @@
+/*
+ knconfigmanager.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCONFIGMANAGER_H
+#define KNCONFIGMANAGER_H
+
+#include <kcmultidialog.h>
+
+#include "knconfig.h"
+
+class KNConfigDialog;
+
+
+class KNConfigManager : QObject {
+
+ Q_OBJECT
+
+ public:
+ KNConfigManager(QObject *p=0, const char *n=0);
+ ~KNConfigManager();
+
+ KNConfig::Identity* identity() const { return i_dentity; }
+ KNConfig::Appearance* appearance()const { return a_ppearance; }
+ KNConfig::ReadNewsGeneral* readNewsGeneral()const { return r_eadNewsGeneral; }
+ KNConfig::ReadNewsNavigation* readNewsNavigation()const { return r_eadNewsNavigation; }
+ KNConfig::ReadNewsViewer* readNewsViewer()const { return r_eadNewsViewer; }
+ KNConfig::DisplayedHeaders* displayedHeaders()const { return d_isplayedHeaders; }
+ KNConfig::Scoring* scoring()const { return s_coring; }
+ KNConfig::PostNewsTechnical* postNewsTechnical()const { return p_ostNewsTechnical; }
+ KNConfig::PostNewsComposer* postNewsComposer() const { return p_ostNewsCompose; }
+ KNConfig::Cleanup* cleanup()const { return c_leanup; }
+ //KNConfig::Cache* cache()const { return c_ache; }
+
+ void configure();
+ void syncConfig();
+
+ protected:
+ KNConfig::Identity *i_dentity;
+ KNConfig::Appearance *a_ppearance;
+ KNConfig::ReadNewsGeneral *r_eadNewsGeneral;
+ KNConfig::ReadNewsNavigation *r_eadNewsNavigation;
+ KNConfig::ReadNewsViewer *r_eadNewsViewer;
+ KNConfig::DisplayedHeaders *d_isplayedHeaders;
+ KNConfig::Scoring *s_coring;
+ KNConfig::PostNewsTechnical *p_ostNewsTechnical;
+ KNConfig::PostNewsComposer *p_ostNewsCompose;
+ KNConfig::Cleanup *c_leanup;
+ //KNConfig::Cache *c_ache;
+
+ KNConfigDialog *d_ialog;
+
+ protected slots:
+ void slotDialogDone();
+
+};
+
+
+class KNConfigDialog : public KCMultiDialog {
+
+ Q_OBJECT
+
+ public:
+ KNConfigDialog(QWidget *p=0, const char *n=0);
+
+ protected slots:
+ void slotConfigCommitted();
+
+};
+
+#endif //KNCONFIGMANAGER_H
diff --git a/knode/knconfigpages.cpp b/knode/knconfigpages.cpp
new file mode 100644
index 000000000..c7d0c0758
--- /dev/null
+++ b/knode/knconfigpages.cpp
@@ -0,0 +1,198 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 2004-2005 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+
+#include <kcmodule.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "knglobals.h"
+#include "knconfig.h"
+#include "knconfigmanager.h"
+#include "knconfigpages.h"
+#include "knconfigwidgets.h"
+
+#include <kdepimmacros.h>
+
+//
+// common config page with tabs (code mostly taken from kmail)
+//
+KNConfig::BasePageWithTabs::BasePageWithTabs( QWidget * parent, const char * name )
+ : KCModule( parent, name )
+{
+ QVBoxLayout *vlay = new QVBoxLayout( this, 0, KDialog::spacingHint() );
+ mTabWidget = new QTabWidget( this );
+ vlay->addWidget( mTabWidget );
+}
+
+void KNConfig::BasePageWithTabs::addTab( KCModule* tab, const QString & title ) {
+ mTabWidget->addTab( tab, title );
+ connect( tab, SIGNAL(changed( bool )), this, SIGNAL(changed( bool )) );
+}
+
+void KNConfig::BasePageWithTabs::load() {
+ for ( int i = 0 ; i < mTabWidget->count() ; ++i ) {
+ KCModule *tab = (KCModule*) mTabWidget->page(i);
+ if ( tab )
+ tab->load();
+ }
+}
+
+void KNConfig::BasePageWithTabs::save() {
+ for ( int i = 0 ; i < mTabWidget->count() ; ++i ) {
+ KCModule *tab = (KCModule*) mTabWidget->page(i);
+ if ( tab )
+ tab->save();
+ }
+}
+
+void KNConfig::BasePageWithTabs::defaults() {
+ for ( int i = 0 ; i < mTabWidget->count() ; ++i ) {
+ KCModule *tab = (KCModule*) mTabWidget->page(i);
+ if ( tab )
+ tab->defaults();
+ }
+}
+
+
+
+//
+// identity page
+//
+extern "C"
+{
+ KDE_EXPORT KCModule *create_knode_config_identity( QWidget *parent, const char * )
+ {
+ KNConfig::IdentityWidget *page = new KNConfig::IdentityWidget(
+ knGlobals.configManager()->identity(),
+ parent,
+ "kcmknode_config_identity" );
+ return page;
+ }
+}
+
+
+
+//
+// accounts page
+//
+extern "C"
+{
+ KCModule *create_knode_config_accounts( QWidget *parent, const char * )
+ {
+ KNConfig::AccountsPage *page = new KNConfig::AccountsPage( parent, "kcmknode_config_accounts" );
+ return page;
+ }
+}
+
+KNConfig::AccountsPage::AccountsPage(QWidget *parent, const char *name)
+ : BasePageWithTabs(parent, name) {
+
+ addTab(new KNConfig::NntpAccountListWidget(this), i18n("Newsgroup Servers"));
+ addTab(new KNConfig::SmtpAccountWidget(this), i18n("Mail Server (SMTP)"));
+}
+
+
+
+//
+// appearance page
+//
+extern "C"
+{
+ KCModule *create_knode_config_appearance( QWidget *parent, const char * )
+ {
+ KNConfig::AppearanceWidget *page = new KNConfig::AppearanceWidget( parent, "kcmknode_config_appearance" );
+ return page;
+ }
+}
+
+
+
+//
+// read news page
+//
+extern "C"
+{
+ KCModule *create_knode_config_read_news( QWidget *parent, const char * )
+ {
+ KNConfig::ReadNewsPage *page = new KNConfig::ReadNewsPage( parent, "kcmknode_config_read_news" );
+ return page;
+ }
+}
+
+KNConfig::ReadNewsPage::ReadNewsPage(QWidget *parent, const char *name)
+ : BasePageWithTabs(parent, name) {
+
+ KNConfigManager *cfgMgr = knGlobals.configManager();
+ addTab(new KNConfig::ReadNewsGeneralWidget(cfgMgr->readNewsGeneral(), this), i18n("General"));
+ addTab(new KNConfig::ReadNewsNavigationWidget(cfgMgr->readNewsNavigation(), this), i18n("Navigation"));
+ addTab(new KNConfig::ScoringWidget(cfgMgr->scoring(), this), i18n("Scoring"));
+ addTab(new KNConfig::FilterListWidget(this), i18n("Filters"));
+ addTab(new KNConfig::DisplayedHeadersWidget(cfgMgr->displayedHeaders(), this), i18n("Headers"));
+ addTab(new KNConfig::ReadNewsViewerWidget(cfgMgr->readNewsViewer(), this), i18n("Viewer"));
+}
+
+
+
+//
+// post news page
+//
+extern "C"
+{
+ KCModule *create_knode_config_post_news( QWidget *parent, const char * )
+ {
+ KNConfig::PostNewsPage *page = new KNConfig::PostNewsPage( parent, "kcmknode_config_post_news" );
+ return page;
+ }
+}
+
+KNConfig::PostNewsPage::PostNewsPage(QWidget *parent, const char *name)
+ : BasePageWithTabs(parent, name) {
+
+ KNConfigManager *cfgMgr = knGlobals.configManager();
+ addTab(new KNConfig::PostNewsTechnicalWidget(cfgMgr->postNewsTechnical(), this), i18n("Technical"));
+ addTab(new KNConfig::PostNewsComposerWidget(cfgMgr->postNewsComposer(), this), i18n("Composer"));
+ addTab(new KNConfig::PostNewsSpellingWidget(this), i18n("Spelling"));
+}
+
+
+
+//
+// privacy page
+//
+extern "C"
+{
+ KCModule *create_knode_config_privacy( QWidget *parent, const char * )
+ {
+ KNConfig::PrivacyWidget *page = new KNConfig::PrivacyWidget( parent, "kcmknode_config_privacy" );
+ return page;
+ }
+}
+
+
+
+//
+// cleanup page
+//
+extern "C"
+{
+ KCModule *create_knode_config_cleanup( QWidget *parent, const char * )
+ {
+ KNConfig::CleanupWidget *page = new KNConfig::CleanupWidget( parent, "kcmknode_config_cleanup" );
+ return page;
+ }
+}
+
+
+#include "knconfigpages.moc"
diff --git a/knode/knconfigpages.h b/knode/knconfigpages.h
new file mode 100644
index 000000000..c76432b78
--- /dev/null
+++ b/knode/knconfigpages.h
@@ -0,0 +1,79 @@
+/*
+ knconfigpages.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 2004 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCONFIGPAGES_H
+#define KNCONFIGPAGES_H
+
+
+#include <kcmodule.h>
+
+class BaseWidget;
+class IdentityWidget;
+
+namespace KNConfig {
+
+/*
+ * BasePageWithTabs represents a kcm with several tabs.
+ * It simply forwards load and save operations to all tabs.
+ * Code mostly taken from kmail.
+ */
+class KDE_EXPORT BasePageWithTabs : public KCModule {
+ Q_OBJECT
+ public:
+ BasePageWithTabs( QWidget * parent=0, const char * name=0 );
+ ~BasePageWithTabs() {};
+
+ virtual void load();
+ virtual void save();
+ virtual void defaults();
+
+ protected:
+ void addTab( KCModule* tab, const QString & title );
+
+ private:
+ QTabWidget *mTabWidget;
+
+};
+
+
+// accounts page
+class AccountsPage : public BasePageWithTabs {
+ Q_OBJECT
+
+ public:
+ AccountsPage(QWidget *parent = 0, const char *name = 0);
+};
+
+
+// read news page
+class KDE_EXPORT ReadNewsPage : public BasePageWithTabs {
+ Q_OBJECT
+
+ public:
+ ReadNewsPage(QWidget *parent = 0, const char *name = 0);
+};
+
+// post news page
+class KDE_EXPORT PostNewsPage : public BasePageWithTabs {
+ Q_OBJECT
+
+ public:
+ PostNewsPage(QWidget *parent = 0, const char *name = 0);
+};
+
+
+} //KNConfig
+
+#endif //KNCONFIGPAGES_H
diff --git a/knode/knconfigwidgets.cpp b/knode/knconfigwidgets.cpp
new file mode 100644
index 000000000..72d8f366c
--- /dev/null
+++ b/knode/knconfigwidgets.cpp
@@ -0,0 +1,2627 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+
+#include <qvbox.h>
+#include <qpainter.h>
+#include <qwhatsthis.h>
+#include <qlabel.h>
+
+#include <klocale.h>
+#include <knumvalidator.h>
+#include <kmessagebox.h>
+#include <kcolordialog.h>
+#include <kfontdialog.h>
+#include <kfiledialog.h>
+#include <kuserprofile.h>
+#include <kopenwith.h>
+#include <kscoringeditor.h>
+#include <kspell.h>
+#include <kcombobox.h>
+#include <kpgpui.h>
+#include <kurlcompletion.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include "knaccountmanager.h"
+#include "knconfig.h"
+#include "knconfigwidgets.h"
+#include "knconfigmanager.h"
+#include "kndisplayedheader.h"
+#include "kngroupmanager.h"
+#include "knglobals.h"
+#include "knnntpaccount.h"
+#include "utilities.h"
+#include "knfiltermanager.h"
+#include "knarticlefilter.h"
+#include "knscoring.h"
+#include <kpgp.h>
+
+
+KNConfig::IdentityWidget::IdentityWidget( Identity *d, QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( d )
+{
+ QString msg;
+
+ QGridLayout *topL=new QGridLayout(this, 11, 3, 5,5);
+
+ n_ame=new KLineEdit(this);
+ QLabel *l=new QLabel(n_ame, i18n("&Name:"), this);
+ topL->addWidget(l, 0,0);
+ topL->addMultiCellWidget(n_ame, 0,0, 1,2);
+ msg = i18n("<qt><p>Your name as it will appear to others reading your articles.</p>"
+ "<p>Ex: <b>John Stuart Masterson III</b>.</p></qt>");
+ QWhatsThis::add( n_ame, msg );
+ QWhatsThis::add( l, msg );
+ connect( n_ame, SIGNAL(textChanged(const QString&)), SLOT(changed()) );
+
+ o_rga=new KLineEdit(this);
+ l=new QLabel(o_rga, i18n("Organi&zation:"), this);
+ topL->addWidget(l, 1,0);
+ topL->addMultiCellWidget(o_rga, 1,1, 1,2);
+ msg = i18n( "<qt><p>The name of the organization you work for.</p>"
+ "<p>Ex: <b>KNode, Inc</b>.</p></qt>" );
+ QWhatsThis::add( o_rga, msg );
+ QWhatsThis::add( l, msg );
+ connect( o_rga, SIGNAL(textChanged(const QString&)), SLOT(changed()) );
+
+ e_mail=new KLineEdit(this);
+ l=new QLabel(e_mail, i18n("Email a&ddress:"), this);
+ topL->addWidget(l, 2,0);
+ topL->addMultiCellWidget(e_mail, 2,2, 1,2);
+ msg = i18n( "<qt><p>Your email address as it will appear to others "
+ "reading your articles</p><p>Ex: <b>nospam@please.com</b>.</qt>" );
+ QWhatsThis::add( l, msg );
+ QWhatsThis::add( e_mail, msg );
+ connect( e_mail, SIGNAL(textChanged(const QString&)), SLOT(changed()) );
+
+ r_eplyTo=new KLineEdit(this);
+ l=new QLabel(r_eplyTo, i18n("&Reply-to address:"), this);
+ topL->addWidget(l, 3,0);
+ topL->addMultiCellWidget(r_eplyTo, 3,3, 1,2);
+ msg = i18n( "<qt><p>When someone reply to your article by email, this is the address the message "
+ "will be sent. If you fill in this field, please do it with a real "
+ "email address.</p><p>Ex: <b>john@example.com</b>.</p></qt>" );
+ QWhatsThis::add( l, msg );
+ QWhatsThis::add( r_eplyTo, msg );
+ connect( r_eplyTo, SIGNAL(textChanged(const QString&)), SLOT(changed()) );
+
+ m_ailCopiesTo=new KLineEdit(this);
+ l=new QLabel(m_ailCopiesTo, i18n("&Mail-copies-to:"), this);
+ topL->addWidget(l, 4,0);
+ topL->addMultiCellWidget(m_ailCopiesTo, 4,4, 1,2);
+ connect( m_ailCopiesTo, SIGNAL(textChanged(const QString&)), SLOT(changed()) );
+
+ s_igningKey = new Kpgp::SecretKeyRequester(this);
+ s_igningKey->dialogButton()->setText(i18n("Chan&ge..."));
+ s_igningKey->setDialogCaption(i18n("Your OpenPGP Key"));
+ s_igningKey->setDialogMessage(i18n("Select the OpenPGP key which should be "
+ "used for signing articles."));
+ l=new QLabel(s_igningKey, i18n("Signing ke&y:"), this);
+ topL->addWidget(l, 5,0);
+ topL->addMultiCellWidget(s_igningKey, 5,5, 1,2);
+ msg = i18n("<qt><p>The OpenPGP key you choose here will be "
+ "used to sign your articles.</p></qt>");
+ QWhatsThis::add( l, msg );
+ QWhatsThis::add( s_igningKey, msg );
+ connect( s_igningKey, SIGNAL(changed()), SLOT(changed()) );
+
+ b_uttonGroup = new QButtonGroup(this);
+ connect( b_uttonGroup, SIGNAL(clicked(int)),
+ this, SLOT(slotSignatureType(int)) );
+ b_uttonGroup->setExclusive(true);
+ b_uttonGroup->hide();
+
+ s_igFile = new QRadioButton( i18n("&Use a signature from file"), this );
+ b_uttonGroup->insert(s_igFile, 0);
+ topL->addMultiCellWidget(s_igFile, 6, 6, 0, 2);
+ QWhatsThis::add( s_igFile,
+ i18n( "<qt><p>Mark this to let KNode read the signature from a file.</p></qt>" ) );
+ s_ig = new KLineEdit(this);
+
+ f_ileName = new QLabel(s_ig, i18n("Signature &file:"), this);
+ topL->addWidget(f_ileName, 7, 0 );
+ topL->addWidget(s_ig, 7, 1 );
+ c_ompletion = new KURLCompletion();
+ s_ig->setCompletionObject(c_ompletion);
+ msg = i18n( "<qt><p>The file from which the signature will be read.</p>"
+ "<p>Ex: <b>/home/robt/.sig</b>.</p></qt>" );
+ QWhatsThis::add( f_ileName, msg );
+ QWhatsThis::add( s_ig, msg );
+
+ c_hooseBtn = new QPushButton( i18n("Choo&se..."), this);
+ connect(c_hooseBtn, SIGNAL(clicked()),
+ this, SLOT(slotSignatureChoose()));
+ topL->addWidget(c_hooseBtn, 7, 2 );
+ e_ditBtn = new QPushButton( i18n("&Edit File"), this);
+ connect(e_ditBtn, SIGNAL(clicked()),
+ this, SLOT(slotSignatureEdit()));
+ topL->addWidget(e_ditBtn, 8, 2);
+
+ s_igGenerator = new QCheckBox(i18n("&The file is a program"), this);
+ topL->addMultiCellWidget(s_igGenerator, 8, 8, 0, 1);
+ msg = i18n( "<qt><p>Mark this option if the signature will be generated by a program</p>"
+ "<p>Ex: <b>/home/robt/gensig.sh</b>.</p></qt>" );
+ QWhatsThis::add( s_igGenerator, msg );
+ connect( s_igGenerator, SIGNAL(toggled(bool)), SLOT(changed()) );
+
+ s_igEdit = new QRadioButton( i18n("Specify signature &below"), this);
+ b_uttonGroup->insert(s_igEdit, 1);
+ topL->addMultiCellWidget(s_igEdit, 9, 9, 0, 2);
+
+ s_igEditor = new QTextEdit(this);
+ s_igEditor->setTextFormat(Qt::PlainText);
+ topL->addMultiCellWidget(s_igEditor, 10, 10, 0, 2);
+ connect( s_igEditor, SIGNAL(textChanged()), SLOT(changed()) );
+
+ topL->setColStretch(1,1);
+ topL->setRowStretch(7,1);
+ topL->setResizeMode(QLayout::Minimum);
+ connect(s_ig,SIGNAL(textChanged ( const QString & )),
+ this,SLOT(textFileNameChanged(const QString &)));
+
+ load();
+}
+
+
+KNConfig::IdentityWidget::~IdentityWidget()
+{
+ delete c_ompletion;
+}
+
+void KNConfig::IdentityWidget::textFileNameChanged(const QString &text)
+{
+ e_ditBtn->setEnabled(!text.isEmpty());
+ emit changed( true );
+}
+
+void KNConfig::IdentityWidget::load()
+{
+ kdDebug() << "void KNConfig::IdentityWidget::load()" << endl;
+ n_ame->setText(d_ata->n_ame);
+ o_rga->setText(d_ata->o_rga);
+ e_mail->setText(d_ata->e_mail);
+ r_eplyTo->setText(d_ata->r_eplyTo);
+ m_ailCopiesTo->setText(d_ata->m_ailCopiesTo);
+ s_igningKey->setKeyIDs(Kpgp::KeyIDList() << d_ata->s_igningKey);
+ s_ig->setText(d_ata->s_igPath);
+ s_igGenerator->setChecked(d_ata->useSigGenerator());
+ s_igEditor->setText(d_ata->s_igText);
+ slotSignatureType(d_ata->useSigFile()? 0:1);
+}
+
+void KNConfig::IdentityWidget::save()
+{
+ d_ata->n_ame=n_ame->text();
+ d_ata->o_rga=o_rga->text();
+ d_ata->e_mail=e_mail->text();
+ d_ata->r_eplyTo=r_eplyTo->text();
+ d_ata->m_ailCopiesTo=m_ailCopiesTo->text();
+ d_ata->s_igningKey = s_igningKey->keyIDs().first();
+ d_ata->u_seSigFile=s_igFile->isChecked();
+ d_ata->u_seSigGenerator=s_igGenerator->isChecked();
+ d_ata->s_igPath=c_ompletion->replacedPath(s_ig->text());
+ d_ata->s_igText=s_igEditor->text();
+
+ if(d_ata->isGlobal())
+ d_ata->save();
+}
+
+void KNConfig::IdentityWidget::slotSignatureType(int type)
+{
+ bool sigFromFile = (type==0);
+
+ b_uttonGroup->setButton(type);
+ f_ileName->setEnabled(sigFromFile);
+ s_ig->setEnabled(sigFromFile);
+ c_hooseBtn->setEnabled(sigFromFile);
+ e_ditBtn->setEnabled(sigFromFile && !s_ig->text().isEmpty());
+ s_igGenerator->setEnabled(sigFromFile);
+ s_igEditor->setEnabled(!sigFromFile);
+
+ if (sigFromFile)
+ f_ileName->setFocus();
+ else
+ s_igEditor->setFocus();
+ emit changed( true );
+}
+
+
+void KNConfig::IdentityWidget::slotSignatureChoose()
+{
+ QString tmp=KFileDialog::getOpenFileName(c_ompletion->replacedPath(s_ig->text()),QString::null,this,i18n("Choose Signature"));
+ if(!tmp.isEmpty()) s_ig->setText(tmp);
+ emit changed( true );
+}
+
+
+void KNConfig::IdentityWidget::slotSignatureEdit()
+{
+ QString fileName = c_ompletion->replacedPath(s_ig->text()).stripWhiteSpace();
+
+ if (fileName.isEmpty()) {
+ KMessageBox::sorry(this, i18n("You must specify a filename."));
+ return;
+ }
+
+ QFileInfo fileInfo( fileName );
+ if (fileInfo.isDir()) {
+ KMessageBox::sorry(this, i18n("You have specified a folder."));
+ return;
+ }
+
+ KService::Ptr offer = KServiceTypeProfile::preferredService("text/plain", "Application");
+ KURL u(fileName);
+
+ if (offer)
+ KRun::run(*offer, u);
+ else
+ KRun::displayOpenWithDialog(u);
+ emit changed( true );
+}
+
+
+
+//==========================================================================================
+
+//BEGIN: NNTP account configuration widgets ----------------------------------
+
+KNConfig::NntpAccountListWidget::NntpAccountListWidget(QWidget *p, const char *n) :
+ KCModule( p, n ),
+ a_ccManager( knGlobals.accountManager() )
+{
+ p_ixmap = SmallIcon("server");
+
+ QGridLayout *topL=new QGridLayout(this, 6,2, 5,5);
+
+ // account listbox
+ l_box=new KNDialogListBox(false, this);
+ connect(l_box, SIGNAL(selected(int)), this, SLOT(slotItemSelected(int)));
+ connect(l_box, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
+ topL->addMultiCellWidget(l_box, 0,4, 0,0);
+
+ // info box
+ QGroupBox *gb = new QGroupBox(2,Qt::Vertical,QString::null,this);
+ topL->addWidget(gb,5,0);
+
+ s_erverInfo = new QLabel(gb);
+ p_ortInfo = new QLabel(gb);
+
+ // buttons
+ a_ddBtn=new QPushButton(i18n("&Add..."), this);
+ connect(a_ddBtn, SIGNAL(clicked()), this, SLOT(slotAddBtnClicked()));
+ topL->addWidget(a_ddBtn, 0,1);
+
+ e_ditBtn=new QPushButton(i18n("modify something","&Edit..."), this);
+ connect(e_ditBtn, SIGNAL(clicked()), this, SLOT(slotEditBtnClicked()));
+ topL->addWidget(e_ditBtn, 1,1);
+
+ d_elBtn=new QPushButton(i18n("&Delete"), this);
+ connect(d_elBtn, SIGNAL(clicked()), this, SLOT(slotDelBtnClicked()));
+ topL->addWidget(d_elBtn, 2,1);
+
+ s_ubBtn=new QPushButton(i18n("&Subscribe..."), this);
+ connect(s_ubBtn, SIGNAL(clicked()), this, SLOT(slotSubBtnClicked()));
+ topL->addWidget(s_ubBtn, 3,1);
+
+ topL->setRowStretch(4,1); // stretch the server listbox
+
+ load();
+
+ // the settings dialog is non-modal, so we have to react to changes
+ // made outside of the dialog
+ connect(a_ccManager, SIGNAL(accountAdded(KNNntpAccount*)), this, SLOT(slotAddItem(KNNntpAccount*)));
+ connect(a_ccManager, SIGNAL(accountRemoved(KNNntpAccount*)), this, SLOT(slotRemoveItem(KNNntpAccount*)));
+ connect(a_ccManager, SIGNAL(accountModified(KNNntpAccount*)), this, SLOT(slotUpdateItem(KNNntpAccount*)));
+
+ slotSelectionChanged(); // disable Delete & Edit initially
+}
+
+
+KNConfig::NntpAccountListWidget::~NntpAccountListWidget()
+{
+}
+
+
+void KNConfig::NntpAccountListWidget::load()
+{
+ l_box->clear();
+ QValueList<KNNntpAccount*>::Iterator it;
+ for ( it = a_ccManager->begin(); it != a_ccManager->end(); ++it )
+ slotAddItem( *it );
+}
+
+
+void KNConfig::NntpAccountListWidget::slotAddItem(KNNntpAccount *a)
+{
+ LBoxItem *it;
+ it=new LBoxItem(a, a->name(), &p_ixmap);
+ l_box->insertItem(it);
+ emit changed(true);
+}
+
+
+void KNConfig::NntpAccountListWidget::slotRemoveItem(KNNntpAccount *a)
+{
+ LBoxItem *it;
+ for(uint i=0; i<l_box->count(); i++) {
+ it=static_cast<LBoxItem*>(l_box->item(i));
+ if(it && it->account==a) {
+ l_box->removeItem(i);
+ break;
+ }
+ }
+ slotSelectionChanged();
+ emit changed(true);
+}
+
+
+void KNConfig::NntpAccountListWidget::slotUpdateItem(KNNntpAccount *a)
+{
+ LBoxItem *it;
+ for(uint i=0; i<l_box->count(); i++) {
+ it=static_cast<LBoxItem*>(l_box->item(i));
+ if(it && it->account==a) {
+ it=new LBoxItem(a, a->name(), &p_ixmap);
+ l_box->changeItem(it, i);
+ break;
+ }
+ }
+ slotSelectionChanged();
+ emit changed(true);
+}
+
+
+
+void KNConfig::NntpAccountListWidget::slotSelectionChanged()
+{
+ int curr=l_box->currentItem();
+ d_elBtn->setEnabled(curr!=-1);
+ e_ditBtn->setEnabled(curr!=-1);
+ s_ubBtn->setEnabled(curr!=-1);
+
+ LBoxItem *it = static_cast<LBoxItem*>(l_box->item(curr));
+ if(it) {
+ s_erverInfo->setText(i18n("Server: %1").arg(it->account->server()));
+ p_ortInfo->setText(i18n("Port: %1").arg(it->account->port()));
+ }
+ else {
+ s_erverInfo->setText(i18n("Server: "));
+ p_ortInfo->setText(i18n("Port: "));
+ }
+}
+
+
+
+void KNConfig::NntpAccountListWidget::slotItemSelected(int)
+{
+ slotEditBtnClicked();
+}
+
+
+
+void KNConfig::NntpAccountListWidget::slotAddBtnClicked()
+{
+ KNNntpAccount *acc = new KNNntpAccount();
+
+ if(acc->editProperties(this)) {
+ if (a_ccManager->newAccount(acc))
+ acc->saveInfo();
+ }
+ else
+ delete acc;
+}
+
+
+
+void KNConfig::NntpAccountListWidget::slotDelBtnClicked()
+{
+ LBoxItem *it = static_cast<LBoxItem*>(l_box->item(l_box->currentItem()));
+
+ if(it)
+ a_ccManager->removeAccount(it->account);
+}
+
+
+
+void KNConfig::NntpAccountListWidget::slotEditBtnClicked()
+{
+ LBoxItem *it = static_cast<LBoxItem*>(l_box->item(l_box->currentItem()));
+
+ if(it) {
+ it->account->editProperties(this);
+ slotUpdateItem(it->account);
+ }
+}
+
+
+void KNConfig::NntpAccountListWidget::slotSubBtnClicked()
+{
+ LBoxItem *it = static_cast<LBoxItem*>(l_box->item(l_box->currentItem()));
+
+ if(it)
+ knGlobals.groupManager()->showGroupDialog(it->account, this);
+}
+
+
+//=======================================================================================
+
+
+KNConfig::NntpAccountConfDialog::NntpAccountConfDialog(KNNntpAccount *a, QWidget *p, const char *n)
+ : KDialogBase(Tabbed, (a->id()!=-1)? i18n("Properties of %1").arg(a->name()):i18n("New Account"),
+ Ok|Cancel|Help, Ok, p, n),
+ a_ccount(a)
+{
+ QFrame* page=addPage(i18n("Ser&ver"));
+ QGridLayout *topL=new QGridLayout(page, 11, 3, 5);
+
+ n_ame=new KLineEdit(page);
+ QLabel *l=new QLabel(n_ame,i18n("&Name:"),page);
+ topL->addWidget(l, 0,0);
+ n_ame->setText(a->name());
+ topL->addMultiCellWidget(n_ame, 0, 0, 1, 2);
+
+ s_erver=new KLineEdit(page);
+ l=new QLabel(s_erver,i18n("&Server:"), page);
+ s_erver->setText(a->server());
+ topL->addWidget(l, 1,0);
+ topL->addMultiCellWidget(s_erver, 1, 1, 1, 2);
+
+ p_ort=new KLineEdit(page);
+ l=new QLabel(p_ort, i18n("&Port:"), page);
+ p_ort->setValidator(new KIntValidator(0,65536,this));
+ p_ort->setText(QString::number(a->port()));
+ topL->addWidget(l, 2,0);
+ topL->addWidget(p_ort, 2,1);
+
+ h_old = new KIntSpinBox(5,1800,5,5,10,page);
+ l = new QLabel(h_old,i18n("Hol&d connection for:"), page);
+ h_old->setSuffix(i18n(" sec"));
+ h_old->setValue(a->hold());
+ topL->addWidget(l,3,0);
+ topL->addWidget(h_old,3,1);
+
+ t_imeout = new KIntSpinBox(15,600,5,15,10,page);
+ l = new QLabel(t_imeout, i18n("&Timeout:"), page);
+ t_imeout->setValue(a->timeout());
+ t_imeout->setSuffix(i18n(" sec"));
+ topL->addWidget(l,4,0);
+ topL->addWidget(t_imeout,4,1);
+
+ f_etchDes=new QCheckBox(i18n("&Fetch group descriptions"), page);
+ f_etchDes->setChecked(a->fetchDescriptions());
+ topL->addMultiCellWidget(f_etchDes, 5,5, 0,3);
+
+ /*u_seDiskCache=new QCheckBox(i18n("&Cache articles on disk"), page);
+ u_seDiskCache->setChecked(a->useDiskCache());
+ topL->addMultiCellWidget(u_seDiskCache, 6,6, 0,3);*/
+
+ a_uth=new QCheckBox(i18n("Server requires &authentication"), page);
+ connect(a_uth, SIGNAL(toggled(bool)), this, SLOT(slotAuthChecked(bool)));
+ topL->addMultiCellWidget(a_uth, 6,6, 0,3);
+
+ u_ser=new KLineEdit(page);
+ u_serLabel=new QLabel(u_ser,i18n("&User:"), page);
+ u_ser->setText(a->user());
+ topL->addWidget(u_serLabel, 7,0);
+ topL->addMultiCellWidget(u_ser, 7,7, 1,2);
+
+ p_ass=new KLineEdit(page);
+ p_assLabel=new QLabel(p_ass, i18n("Pass&word:"), page);
+ p_ass->setEchoMode(KLineEdit::Password);
+ if ( a->readyForLogin() )
+ p_ass->setText(a->pass());
+ else
+ if ( a->needsLogon() )
+ knGlobals.accountManager()->loadPasswordsAsync();
+ topL->addWidget(p_assLabel, 8,0);
+ topL->addMultiCellWidget(p_ass, 8,8, 1,2);
+
+ i_nterval=new QCheckBox(i18n("Enable &interval news checking"), page);
+ connect(i_nterval, SIGNAL(toggled(bool)), this, SLOT(slotIntervalChecked(bool)));
+ topL->addMultiCellWidget(i_nterval, 9,9, 0,3);
+
+ c_heckInterval=new KIntSpinBox(1,10000,1,1,10,page);
+ c_heckIntervalLabel=new QLabel(c_heckInterval, i18n("Check inter&val:"), page);
+ c_heckInterval->setSuffix(i18n(" min") );
+ c_heckInterval->setValue(a->checkInterval());
+ c_heckIntervalLabel->setBuddy(c_heckInterval);
+ topL->addWidget(c_heckIntervalLabel, 10,0);
+ topL->addMultiCellWidget(c_heckInterval, 10,10, 1,2);
+
+ slotAuthChecked(a->needsLogon());
+ slotIntervalChecked(a->intervalChecking());
+
+ topL->setColStretch(1, 1);
+ topL->setColStretch(2, 1);
+
+ // Specfic Identity tab =========================================
+ i_dWidget=new KNConfig::IdentityWidget(a->identity(), addVBoxPage(i18n("&Identity")));
+
+ // per server cleanup configuration
+ QFrame* cleanupPage = addPage( i18n("&Cleanup") );
+ QVBoxLayout *cleanupLayout = new QVBoxLayout( cleanupPage, KDialog::spacingHint() );
+ mCleanupWidget = new GroupCleanupWidget( a->cleanupConfig(), cleanupPage );
+ mCleanupWidget->load();
+ cleanupLayout->addWidget( mCleanupWidget );
+ cleanupLayout->addStretch( 1 );
+
+ connect( knGlobals.accountManager(), SIGNAL(passwordsChanged()), SLOT(slotPasswordChanged()) );
+
+ KNHelper::restoreWindowSize("accNewsPropDLG", this, sizeHint());
+
+ setHelp("anc-setting-the-news-account");
+}
+
+
+
+KNConfig::NntpAccountConfDialog::~NntpAccountConfDialog()
+{
+ KNHelper::saveWindowSize("accNewsPropDLG", size());
+}
+
+
+void KNConfig::NntpAccountConfDialog::slotOk()
+{
+ if (n_ame->text().isEmpty() || s_erver->text().stripWhiteSpace().isEmpty()) {
+ KMessageBox::sorry(this, i18n("Please enter an arbitrary name for the account and the\nhostname of the news server."));
+ return;
+ }
+
+ a_ccount->setName(n_ame->text());
+ a_ccount->setServer(s_erver->text().stripWhiteSpace());
+ a_ccount->setPort(p_ort->text().toInt());
+ a_ccount->setHold(h_old->value());
+ a_ccount->setTimeout(t_imeout->value());
+ a_ccount->setFetchDescriptions(f_etchDes->isChecked());
+ //a_ccount->setUseDiskCache(u_seDiskCache->isChecked());
+ a_ccount->setNeedsLogon(a_uth->isChecked());
+ a_ccount->setUser(u_ser->text());
+ a_ccount->setPass(p_ass->text());
+ a_ccount->setIntervalChecking(i_nterval->isChecked());
+ a_ccount->setCheckInterval(c_heckInterval->value());
+ if (a_ccount->id() != -1) // only save if account has a valid id
+ a_ccount->saveInfo();
+
+ i_dWidget->save();
+ mCleanupWidget->save();
+
+ accept();
+}
+
+
+void KNConfig::NntpAccountConfDialog::slotAuthChecked(bool b)
+{
+ a_uth->setChecked(b);
+ u_ser->setEnabled(b);
+ u_serLabel->setEnabled(b);
+ p_ass->setEnabled(b);
+ p_assLabel->setEnabled(b);
+}
+
+void KNConfig::NntpAccountConfDialog::slotIntervalChecked(bool b)
+{
+ i_nterval->setChecked(b);
+ c_heckInterval->setEnabled(b);
+ c_heckIntervalLabel->setEnabled(b);
+}
+
+void KNConfig::NntpAccountConfDialog::slotPasswordChanged()
+{
+ if ( p_ass->text().isEmpty() )
+ p_ass->setText( a_ccount->pass() );
+}
+
+//END: NNTP account configuration widgets ------------------------------------
+
+//=============================================================================================
+
+KNConfig::SmtpAccountWidget::SmtpAccountWidget( QWidget *p, const char *n ) :
+ SmtpAccountWidgetBase( p, n )
+{
+ mAccount = knGlobals.accountManager()->smtp();
+ connect( knGlobals.accountManager(), SIGNAL(passwordsChanged()), SLOT(slotPasswordChanged()) );
+ load();
+}
+
+
+void KNConfig::SmtpAccountWidget::load()
+{
+ mUseExternalMailer->setChecked( knGlobals.configManager()->postNewsTechnical()->useExternalMailer() );
+ useExternalMailerToggled( knGlobals.configManager()->postNewsTechnical()->useExternalMailer() );
+ mServer->setText( mAccount->server() );
+ mPort->setValue( mAccount->port() );
+ mLogin->setChecked( mAccount->needsLogon() );
+ loginToggled( mAccount->needsLogon() );
+ mUser->setText( mAccount->user() );
+ if ( mAccount->readyForLogin() )
+ mPassword->setText( mAccount->pass() );
+ else
+ if ( mAccount->needsLogon() )
+ knGlobals.accountManager()->loadPasswordsAsync();
+ switch ( mAccount->encryption() ) {
+ case KNServerInfo::None:
+ mEncNone->setChecked( true );
+ break;
+ case KNServerInfo::SSL:
+ mEncSSL->setChecked( true );
+ break;
+ case KNServerInfo::TLS:
+ mEncTLS->setChecked( true );
+ break;
+ }
+}
+
+
+void KNConfig::SmtpAccountWidget::save()
+{
+ knGlobals.configManager()->postNewsTechnical()->u_seExternalMailer = mUseExternalMailer->isChecked();
+ knGlobals.configManager()->postNewsTechnical()->setDirty(true);
+
+ mAccount->setServer( mServer->text() );
+ mAccount->setPort( mPort->value() );
+ mAccount->setNeedsLogon( mLogin->isChecked() );
+ if ( mAccount->needsLogon() ) {
+ mAccount->setUser( mUser->text() );
+ mAccount->setPass( mPassword->text() );
+ }
+ if ( mEncNone->isChecked() )
+ mAccount->setEncryption( KNServerInfo::None );
+ if ( mEncSSL->isChecked() )
+ mAccount->setEncryption( KNServerInfo::SSL );
+ if ( mEncTLS->isChecked() )
+ mAccount->setEncryption( KNServerInfo::TLS );
+
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("MAILSERVER");
+ mAccount->saveConf( conf );
+}
+
+
+void KNConfig::SmtpAccountWidget::useExternalMailerToggled( bool b )
+{
+ mServer->setEnabled( !b );
+ mPort->setEnabled( !b );
+ mServerLabel->setEnabled( !b );
+ mPortLabel->setEnabled( !b );
+ mLogin->setEnabled( !b );
+ if ( !b )
+ loginToggled( mLogin->isChecked() );
+ else
+ loginToggled( false );
+ mEncGroup->setEnabled( !b );
+ emit changed( true );
+}
+
+
+void KNConfig::SmtpAccountWidget::loginToggled( bool b )
+{
+ bool canEnable = ( b && !mUseExternalMailer->isChecked() );
+ mUser->setEnabled( canEnable );
+ mUserLabel->setEnabled( canEnable );
+ mPassword->setEnabled( canEnable );
+ mPasswordLabel->setEnabled( canEnable );
+ emit changed( true );
+}
+
+
+void KNConfig::SmtpAccountWidget::slotPasswordChanged()
+{
+ if ( mPassword->text().isEmpty() )
+ mPassword->setText( mAccount->pass() );
+}
+
+
+//=============================================================================================
+
+
+//===================================================================================
+// code taken from KMail, Copyright (C) 2000 Espen Sand, espen@kde.org
+
+KNConfig::AppearanceWidget::ColorListItem::ColorListItem( const QString &text, const QColor &color )
+ : QListBoxText(text), mColor( color )
+{
+}
+
+
+KNConfig::AppearanceWidget::ColorListItem::~ColorListItem()
+{
+}
+
+
+void KNConfig::AppearanceWidget::ColorListItem::paint( QPainter *p )
+{
+ QFontMetrics fm = p->fontMetrics();
+ int h = fm.height();
+
+ p->drawText( 30+3*2, fm.ascent() + fm.leading()/2, text() );
+
+ p->setPen( Qt::black );
+ p->drawRect( 3, 1, 30, h-1 );
+ p->fillRect( 4, 2, 28, h-3, mColor );
+}
+
+
+int KNConfig::AppearanceWidget::ColorListItem::height(const QListBox *lb ) const
+{
+ return( lb->fontMetrics().lineSpacing()+1 );
+}
+
+
+int KNConfig::AppearanceWidget::ColorListItem::width(const QListBox *lb ) const
+{
+ return( 30 + lb->fontMetrics().width( text() ) + 6 );
+}
+
+
+//===================================================================================
+
+
+KNConfig::AppearanceWidget::FontListItem::FontListItem( const QString &name, const QFont &font )
+ : QListBoxText(name), f_ont(font)
+{
+ fontInfo = QString("[%1 %2]").arg(f_ont.family()).arg(f_ont.pointSize());
+}
+
+
+KNConfig::AppearanceWidget::FontListItem::~FontListItem()
+{
+}
+
+
+void KNConfig::AppearanceWidget::FontListItem::setFont(const QFont &font)
+{
+ f_ont = font;
+ fontInfo = QString("[%1 %2]").arg(f_ont.family()).arg(f_ont.pointSize());
+}
+
+
+void KNConfig::AppearanceWidget::FontListItem::paint( QPainter *p )
+{
+ QFont fnt = p->font();
+ fnt.setWeight(QFont::Bold);
+ p->setFont(fnt);
+ int fontInfoWidth = p->fontMetrics().width(fontInfo);
+ int h = p->fontMetrics().ascent() + p->fontMetrics().leading()/2;
+ p->drawText(2, h, fontInfo );
+ fnt.setWeight(QFont::Normal);
+ p->setFont(fnt);
+ p->drawText(5 + fontInfoWidth, h, text() );
+}
+
+
+int KNConfig::AppearanceWidget::FontListItem::width(const QListBox *lb ) const
+{
+ return( lb->fontMetrics().width(fontInfo) + lb->fontMetrics().width(text()) + 20 );
+}
+
+
+//===================================================================================
+
+
+KNConfig::AppearanceWidget::AppearanceWidget( QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( knGlobals.configManager()->appearance() )
+{
+ QGridLayout *topL=new QGridLayout(this, 8,2, 5,5);
+
+ //color-list
+ c_List = new KNDialogListBox(false, this);
+ topL->addMultiCellWidget(c_List,1,3,0,0);
+ connect(c_List, SIGNAL(selected(QListBoxItem*)),SLOT(slotColItemSelected(QListBoxItem*)));
+ connect(c_List, SIGNAL(selectionChanged()), SLOT(slotColSelectionChanged()));
+
+ c_olorCB = new QCheckBox(i18n("&Use custom colors"),this);
+ topL->addWidget(c_olorCB,0,0);
+ connect(c_olorCB, SIGNAL(toggled(bool)), this, SLOT(slotColCheckBoxToggled(bool)));
+
+ c_olChngBtn=new QPushButton(i18n("Cha&nge..."), this);
+ connect(c_olChngBtn, SIGNAL(clicked()), this, SLOT(slotColChangeBtnClicked()));
+ topL->addWidget(c_olChngBtn,1,1);
+
+ //font-list
+ f_List = new KNDialogListBox(false, this);
+ topL->addMultiCellWidget(f_List,5,7,0,0);
+ connect(f_List, SIGNAL(selected(QListBoxItem*)),SLOT(slotFontItemSelected(QListBoxItem*)));
+ connect(f_List, SIGNAL(selectionChanged()),SLOT(slotFontSelectionChanged()));
+
+ f_ontCB = new QCheckBox(i18n("Use custom &fonts"),this);
+ topL->addWidget(f_ontCB,4,0);
+ connect(f_ontCB, SIGNAL(toggled(bool)), this, SLOT(slotFontCheckBoxToggled(bool)));
+
+ f_ntChngBtn=new QPushButton(i18n("Chang&e..."), this);
+ connect(f_ntChngBtn, SIGNAL(clicked()), this, SLOT(slotFontChangeBtnClicked()));
+ topL->addWidget(f_ntChngBtn,5,1);
+
+ load();
+}
+
+
+KNConfig::AppearanceWidget::~AppearanceWidget()
+{
+}
+
+
+void KNConfig::AppearanceWidget::load()
+{
+ c_olorCB->setChecked(d_ata->u_seColors);
+ slotColCheckBoxToggled(d_ata->u_seColors);
+ c_List->clear();
+ for(int i=0; i < d_ata->colorCount(); i++)
+ c_List->insertItem(new ColorListItem(d_ata->colorName(i), d_ata->color(i)));
+
+ f_ontCB->setChecked(d_ata->u_seFonts);
+ slotFontCheckBoxToggled(d_ata->u_seFonts);
+ f_List->clear();
+ for(int i=0; i < d_ata->fontCount(); i++)
+ f_List->insertItem(new FontListItem(d_ata->fontName(i), d_ata->font(i)));
+}
+
+
+void KNConfig::AppearanceWidget::save()
+{
+ d_ata->u_seColors=c_olorCB->isChecked();
+ for(int i=0; i<d_ata->colorCount(); i++)
+ d_ata->c_olors[i] = (static_cast<ColorListItem*>(c_List->item(i)))->color();
+
+ d_ata->u_seFonts=f_ontCB->isChecked();
+ for(int i=0; i<d_ata->fontCount(); i++)
+ d_ata->f_onts[i] = (static_cast<FontListItem*>(f_List->item(i)))->font();
+
+ d_ata->setDirty(true);
+
+ d_ata->recreateLVIcons();
+}
+
+
+void KNConfig::AppearanceWidget::defaults()
+{
+ // default colors
+ ColorListItem *colorItem;
+ for(int i=0; i < d_ata->colorCount(); i++) {
+ colorItem=static_cast<ColorListItem*>(c_List->item(i));
+ colorItem->setColor(d_ata->defaultColor(i));
+ }
+ c_List->triggerUpdate(true);
+ c_List->repaint(true);
+
+ // default fonts
+ FontListItem *fontItem;
+ for(int i=0; i < d_ata->fontCount(); i++) {
+ fontItem=static_cast<FontListItem*>(f_List->item(i));
+ fontItem->setFont(d_ata->defaultFont(i));
+ }
+ f_List->triggerUpdate(false);
+
+ emit changed(true);
+}
+
+
+void KNConfig::AppearanceWidget::slotColCheckBoxToggled(bool b)
+{
+ c_List->setEnabled(b);
+ c_olChngBtn->setEnabled(b && (c_List->currentItem()!=-1));
+ if (b) c_List->setFocus();
+ emit changed(true);
+}
+
+
+// show color dialog for the entry
+void KNConfig::AppearanceWidget::slotColItemSelected(QListBoxItem *it)
+{
+ if (it) {
+ ColorListItem *colorItem = static_cast<ColorListItem*>(it);
+ QColor col = colorItem->color();
+ int result = KColorDialog::getColor(col,this);
+
+ if (result == KColorDialog::Accepted) {
+ colorItem->setColor(col);
+ c_List->triggerUpdate(false);
+ }
+ }
+ emit changed(true);
+}
+
+
+void KNConfig::AppearanceWidget::slotColChangeBtnClicked()
+{
+ if(c_List->currentItem()!=-1)
+ slotColItemSelected(c_List->item(c_List->currentItem()));
+}
+
+
+void KNConfig::AppearanceWidget::slotColSelectionChanged()
+{
+ c_olChngBtn->setEnabled(c_List->currentItem()!=-1);
+}
+
+
+void KNConfig::AppearanceWidget::slotFontCheckBoxToggled(bool b)
+{
+ f_List->setEnabled(b);
+ f_ntChngBtn->setEnabled(b && (f_List->currentItem()!=-1));
+ if (b) f_List->setFocus();
+ emit changed(true);
+}
+
+
+// show font dialog for the entry
+void KNConfig::AppearanceWidget::slotFontItemSelected(QListBoxItem *it)
+{
+ if (it) {
+ FontListItem *fontItem = static_cast<FontListItem*>(it);
+ QFont font = fontItem->font();
+ int result = KFontDialog::getFont(font,false,this);
+
+ if (result == KFontDialog::Accepted) {
+ fontItem->setFont(font);
+ f_List->triggerUpdate(false);
+ }
+ }
+ emit changed(true);
+}
+
+
+void KNConfig::AppearanceWidget::slotFontChangeBtnClicked()
+{
+ if(f_List->currentItem()!=-1)
+ slotFontItemSelected(f_List->item(f_List->currentItem()));
+}
+
+
+void KNConfig::AppearanceWidget::slotFontSelectionChanged()
+{
+ f_ntChngBtn->setEnabled(f_List->currentItem()!=-1);
+}
+
+
+//=============================================================================================
+
+
+KNConfig::ReadNewsGeneralWidget::ReadNewsGeneralWidget( ReadNewsGeneral *d, QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( d )
+{
+ QGroupBox *hgb=new QGroupBox(i18n("Article Handling"), this);
+ QGroupBox *lgb=new QGroupBox(i18n("Article List"), this);
+ QGroupBox *cgb=new QGroupBox(i18n("Memory Consumption"), this);
+ QLabel *l1, *l2, *l3;
+
+ a_utoCB=new QCheckBox(i18n("Check for new articles a&utomatically"), hgb);
+ m_axFetch=new KIntSpinBox(0, 100000, 1, 0, 10, hgb);
+ l1=new QLabel(m_axFetch, i18n("&Maximum number of articles to fetch:"), hgb);
+ m_arkCB=new QCheckBox(i18n("Mar&k article as read after:"), hgb);
+ m_arkSecs=new KIntSpinBox(0, 9999, 1, 0, 10, hgb);
+ connect(m_arkCB, SIGNAL(toggled(bool)), m_arkSecs, SLOT(setEnabled(bool)));
+ m_arkSecs->setSuffix(i18n(" sec"));
+ m_arkCrossCB=new QCheckBox(i18n("Mark c&rossposted articles as read"), hgb);
+
+ s_martScrollingCB=new QCheckBox(i18n("Smart scrolli&ng"), lgb);
+ e_xpThrCB=new QCheckBox(i18n("Show &whole thread on expanding"), lgb);
+ d_efaultExpandCB=new QCheckBox(i18n("Default to e&xpanded threads"), lgb);
+ s_coreCB=new QCheckBox(i18n("Show article &score"), lgb);
+ l_inesCB=new QCheckBox(i18n("Show &line count"), lgb);
+ u_nreadCB=new QCheckBox(i18n("Show unread count in &thread"), lgb);
+
+ c_ollCacheSize=new KIntSpinBox(0, 99999, 1, 1, 10, cgb);
+ c_ollCacheSize->setSuffix(" KB");
+ l2=new QLabel(c_ollCacheSize, i18n("Cach&e size for headers:"), cgb);
+ a_rtCacheSize=new KIntSpinBox(0, 99999, 1, 1, 10, cgb);
+ a_rtCacheSize->setSuffix(" KB");
+ l3=new QLabel(a_rtCacheSize, i18n("Cache si&ze for articles:"), cgb);
+
+ QVBoxLayout *topL=new QVBoxLayout(this, 5);
+ QGridLayout *hgbL=new QGridLayout(hgb, 5,2, 8,5);
+ QVBoxLayout *lgbL=new QVBoxLayout(lgb, 8, 5);
+ QGridLayout *cgbL=new QGridLayout(cgb, 3,2, 8,5);
+
+ topL->addWidget(hgb);
+ topL->addWidget(lgb);
+ topL->addWidget(cgb);
+ topL->addStretch(1);
+
+ hgbL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+ hgbL->addMultiCellWidget(a_utoCB, 1,1, 0,1);
+ hgbL->addWidget(l1, 2, 0);
+ hgbL->addWidget(m_axFetch, 2,1);
+ hgbL->addWidget(m_arkCB, 3,0);
+ hgbL->addWidget(m_arkSecs, 3,1);
+ hgbL->addMultiCellWidget(m_arkCrossCB, 4,4, 0,1);
+ hgbL->setColStretch(0,1);
+
+ lgbL->addSpacing(fontMetrics().lineSpacing()-4);
+ lgbL->addWidget(s_martScrollingCB);
+ lgbL->addWidget(e_xpThrCB);
+ lgbL->addWidget(d_efaultExpandCB);
+ lgbL->addWidget(s_coreCB);
+ lgbL->addWidget(l_inesCB);
+ lgbL->addWidget(u_nreadCB);
+
+ cgbL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+ cgbL->addWidget(l2, 1,0);
+ cgbL->addWidget(c_ollCacheSize, 1,1);
+ cgbL->addWidget(l3, 2,0);
+ cgbL->addWidget(a_rtCacheSize, 2,1);
+ cgbL->setColStretch(0,1);
+
+ topL->setResizeMode(QLayout::Minimum);
+
+ connect(a_utoCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(m_axFetch, SIGNAL(valueChanged(int)), SLOT(changed()));
+ connect(m_arkCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(m_arkSecs, SIGNAL(valueChanged(int)), SLOT(changed()));
+ connect(m_arkCrossCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(s_martScrollingCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(e_xpThrCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(d_efaultExpandCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(l_inesCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(s_coreCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(u_nreadCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(c_ollCacheSize, SIGNAL(valueChanged(int)), SLOT(changed()));
+ connect(a_rtCacheSize, SIGNAL(valueChanged(int)), SLOT(changed()));
+
+ load();
+}
+
+
+KNConfig::ReadNewsGeneralWidget::~ReadNewsGeneralWidget()
+{
+}
+
+
+void KNConfig::ReadNewsGeneralWidget::load()
+{
+ a_utoCB->setChecked(d_ata->a_utoCheck);
+ m_axFetch->setValue(d_ata->m_axFetch);
+ m_arkCB->setChecked(d_ata->a_utoMark);
+ m_arkSecs->setValue(d_ata->m_arkSecs);
+ m_arkSecs->setEnabled(d_ata->a_utoMark);
+ m_arkCrossCB->setChecked(d_ata->m_arkCrossposts);
+ s_martScrollingCB->setChecked(d_ata->s_martScrolling);
+ e_xpThrCB->setChecked(d_ata->t_otalExpand);
+ d_efaultExpandCB->setChecked(d_ata->d_efaultExpand);
+ l_inesCB->setChecked(d_ata->s_howLines);
+ s_coreCB->setChecked(d_ata->s_howScore);
+ u_nreadCB->setChecked(d_ata->s_howUnread);
+ c_ollCacheSize->setValue(d_ata->c_ollCacheSize);
+ a_rtCacheSize->setValue(d_ata->a_rtCacheSize);
+}
+
+void KNConfig::ReadNewsGeneralWidget::save()
+{
+ d_ata->a_utoCheck=a_utoCB->isChecked();
+ d_ata->m_axFetch=m_axFetch->value();
+ d_ata->a_utoMark=m_arkCB->isChecked();
+ d_ata->m_arkSecs=m_arkSecs->value();
+ d_ata->m_arkCrossposts=m_arkCrossCB->isChecked();
+ d_ata->s_martScrolling=s_martScrollingCB->isChecked();
+ d_ata->t_otalExpand=e_xpThrCB->isChecked();
+ d_ata->d_efaultExpand=d_efaultExpandCB->isChecked();
+ d_ata->s_howLines=l_inesCB->isChecked();
+ d_ata->s_howScore=s_coreCB->isChecked();
+ d_ata->s_howUnread=u_nreadCB->isChecked();
+ d_ata->c_ollCacheSize=c_ollCacheSize->value();
+ d_ata->a_rtCacheSize=a_rtCacheSize->value();
+
+ d_ata->setDirty(true);
+}
+
+//=============================================================================================
+
+
+KNConfig::ReadNewsNavigationWidget::ReadNewsNavigationWidget( ReadNewsNavigation *d, QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( d )
+{
+ QVBoxLayout *topL=new QVBoxLayout(this, 5);
+
+ // ==== Mark All as Read ====================================================
+
+ QGroupBox *gb=new QGroupBox(i18n("\"Mark All as Read\" Triggers Following Actions"), this);
+ QVBoxLayout *gbL=new QVBoxLayout(gb, 8, 5);
+ topL->addWidget(gb);
+
+ gbL->addSpacing(fontMetrics().lineSpacing()-4);
+ m_arkAllReadGoNextCB=new QCheckBox(i18n("&Switch to the next group"), gb);
+ gbL->addWidget(m_arkAllReadGoNextCB);
+
+ connect(m_arkAllReadGoNextCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ // ==== Mark Thread as Read =================================================
+
+ gb=new QGroupBox(i18n("\"Mark Thread as Read\" Triggers Following Actions"), this);
+ gbL=new QVBoxLayout(gb, 8, 5);
+ topL->addWidget(gb);
+
+ gbL->addSpacing(fontMetrics().lineSpacing()-4);
+ m_arkThreadReadCloseThreadCB=new QCheckBox(i18n("Clos&e the current thread"), gb);
+ gbL->addWidget(m_arkThreadReadCloseThreadCB);
+ m_arkThreadReadGoNextCB=new QCheckBox(i18n("Go &to the next unread thread"), gb);
+ gbL->addWidget(m_arkThreadReadGoNextCB);
+
+ connect(m_arkThreadReadCloseThreadCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(m_arkThreadReadGoNextCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ // ==== Ignore Thread =======================================================
+
+ gb=new QGroupBox(i18n("\"Ignore Thread\" Triggers Following Actions"), this);
+ gbL=new QVBoxLayout(gb, 8, 5);
+ topL->addWidget(gb);
+
+ gbL->addSpacing(fontMetrics().lineSpacing()-4);
+ i_gnoreThreadCloseThreadCB=new QCheckBox(i18n("Close the cu&rrent thread"), gb);
+ gbL->addWidget(i_gnoreThreadCloseThreadCB);
+ i_gnoreThreadGoNextCB=new QCheckBox(i18n("Go to the next &unread thread"), gb);
+ gbL->addWidget(i_gnoreThreadGoNextCB);
+
+ connect(i_gnoreThreadCloseThreadCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(i_gnoreThreadGoNextCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ topL->addStretch(1);
+ topL->setResizeMode(QLayout::Minimum);
+
+ load();
+}
+
+
+KNConfig::ReadNewsNavigationWidget::~ReadNewsNavigationWidget()
+{
+}
+
+
+void KNConfig::ReadNewsNavigationWidget::load()
+{
+ m_arkAllReadGoNextCB->setChecked(d_ata->m_arkAllReadGoNext);
+ m_arkThreadReadGoNextCB->setChecked(d_ata->m_arkThreadReadGoNext);
+ m_arkThreadReadCloseThreadCB->setChecked(d_ata->m_arkThreadReadCloseThread);
+ i_gnoreThreadGoNextCB->setChecked(d_ata->i_gnoreThreadGoNext);
+ i_gnoreThreadCloseThreadCB->setChecked(d_ata->i_gnoreThreadCloseThread);
+}
+
+void KNConfig::ReadNewsNavigationWidget::save()
+{
+ d_ata->m_arkAllReadGoNext = m_arkAllReadGoNextCB->isChecked();
+ d_ata->m_arkThreadReadGoNext = m_arkThreadReadGoNextCB->isChecked();
+ d_ata->m_arkThreadReadCloseThread = m_arkThreadReadCloseThreadCB->isChecked();
+ d_ata->i_gnoreThreadGoNext = i_gnoreThreadGoNextCB->isChecked();
+ d_ata->i_gnoreThreadCloseThread = i_gnoreThreadCloseThreadCB->isChecked();
+
+ d_ata->setDirty(true);
+}
+
+
+//=============================================================================================
+
+
+KNConfig::ReadNewsViewerWidget::ReadNewsViewerWidget( ReadNewsViewer *d, QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( d )
+{
+ QGroupBox *appgb=new QGroupBox(i18n("Appearance"), this);
+ QGroupBox *agb=new QGroupBox(i18n("Attachments"), this);
+ QGroupBox *secbox = new QGroupBox( i18n("Security"), this );
+
+ r_ewrapCB=new QCheckBox(i18n("Re&wrap text when necessary"), appgb);
+ r_emoveTrailingCB=new QCheckBox(i18n("Re&move trailing empty lines"), appgb);
+ s_igCB=new QCheckBox(i18n("Show sig&nature"), appgb);
+ mShowRefBar = new QCheckBox( i18n("Show reference bar"), appgb );
+ q_uoteCharacters=new KLineEdit(appgb);
+ QLabel *quoteCharL = new QLabel(q_uoteCharacters, i18n("Recognized q&uote characters:"), appgb);
+
+ o_penAttCB=new QCheckBox(i18n("Open a&ttachments on click"), agb);
+ a_ltAttCB=new QCheckBox(i18n("Show alternati&ve contents as attachments"), agb);
+
+ mAlwaysShowHTML = new QCheckBox( i18n("Prefer HTML to plain text"), secbox );
+
+ QVBoxLayout *topL=new QVBoxLayout(this, 5);
+ QGridLayout *appgbL=new QGridLayout(appgb, 5,2, 8,5);
+ QVBoxLayout *agbL=new QVBoxLayout(agb, 8, 5);
+ QVBoxLayout *secLayout = new QVBoxLayout( secbox, 8, 5 );
+
+ topL->addWidget(appgb);
+ topL->addWidget(agb);
+ topL->addWidget( secbox );
+ topL->addStretch(1);
+
+ appgbL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+ appgbL->addMultiCellWidget(r_ewrapCB, 2,2, 0,1);
+ appgbL->addMultiCellWidget(r_emoveTrailingCB, 3,3, 0,1);
+ appgbL->addMultiCellWidget(s_igCB, 4,4, 0,1);
+ appgbL->addMultiCellWidget( mShowRefBar, 5,5, 0,1 );
+ appgbL->addWidget(quoteCharL, 6,0);
+ appgbL->addWidget(q_uoteCharacters, 6,1);
+
+ agbL->addSpacing(fontMetrics().lineSpacing()-4);
+ agbL->addWidget(o_penAttCB);
+ agbL->addWidget(a_ltAttCB);
+
+ secLayout->addSpacing( fontMetrics().lineSpacing() - 4 );
+ secLayout->addWidget( mAlwaysShowHTML );
+
+ topL->setResizeMode(QLayout::Minimum);
+
+ connect(r_ewrapCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(r_emoveTrailingCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(s_igCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(q_uoteCharacters, SIGNAL(textChanged(const QString&)), SLOT(changed()));
+ connect(o_penAttCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(a_ltAttCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect( mShowRefBar, SIGNAL(toggled(bool)), SLOT(changed()) );
+ connect( mAlwaysShowHTML, SIGNAL(toggled(bool)), SLOT(changed()) );
+
+ load();
+}
+
+
+KNConfig::ReadNewsViewerWidget::~ReadNewsViewerWidget()
+{
+}
+
+
+void KNConfig::ReadNewsViewerWidget::load()
+{
+ r_ewrapCB->setChecked(d_ata->r_ewrapBody);
+ r_emoveTrailingCB->setChecked(d_ata->r_emoveTrailingNewlines);
+ s_igCB->setChecked(d_ata->s_howSig);
+ q_uoteCharacters->setText(d_ata->q_uoteCharacters);
+ o_penAttCB->setChecked(d_ata->o_penAtt);
+ a_ltAttCB->setChecked(d_ata->s_howAlts);
+ mShowRefBar->setChecked( d_ata->showRefBar() );
+ mAlwaysShowHTML->setChecked( d_ata->alwaysShowHTML() );
+}
+
+
+void KNConfig::ReadNewsViewerWidget::save()
+{
+ d_ata->r_ewrapBody=r_ewrapCB->isChecked();
+ d_ata->r_emoveTrailingNewlines=r_emoveTrailingCB->isChecked();
+ d_ata->s_howSig=s_igCB->isChecked();
+ d_ata->q_uoteCharacters=q_uoteCharacters->text();
+ d_ata->o_penAtt=o_penAttCB->isChecked();
+ d_ata->s_howAlts=a_ltAttCB->isChecked();
+ d_ata->setShowRefBar( mShowRefBar->isChecked() );
+ d_ata->setAlwaysShowHTML( mAlwaysShowHTML->isChecked() );
+
+ d_ata->setDirty(true);
+}
+
+
+//=============================================================================================
+
+
+KNConfig::DisplayedHeadersWidget::DisplayedHeadersWidget( DisplayedHeaders *d, QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ s_ave( false ),
+ d_ata( d )
+{
+ QGridLayout *topL=new QGridLayout(this, 7,2, 5,5);
+
+ //listbox
+ l_box=new KNDialogListBox(false, this);
+ connect(l_box, SIGNAL(selected(int)), this, SLOT(slotItemSelected(int)));
+ connect(l_box, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
+ topL->addMultiCellWidget(l_box, 0,6, 0,0);
+
+ // buttons
+ a_ddBtn=new QPushButton(i18n("&Add..."), this);
+ connect(a_ddBtn, SIGNAL(clicked()), this, SLOT(slotAddBtnClicked()));
+ topL->addWidget(a_ddBtn, 0,1);
+
+ d_elBtn=new QPushButton(i18n("&Delete"), this);
+ connect(d_elBtn, SIGNAL(clicked()), this, SLOT(slotDelBtnClicked()));
+ topL->addWidget(d_elBtn, 1,1);
+
+ e_ditBtn=new QPushButton(i18n("modify something","&Edit..."), this);
+ connect(e_ditBtn, SIGNAL(clicked()), this, SLOT(slotEditBtnClicked()));
+ topL->addWidget(e_ditBtn, 2,1);
+
+ u_pBtn=new QPushButton(i18n("&Up"), this);
+ connect(u_pBtn, SIGNAL(clicked()), this, SLOT(slotUpBtnClicked()));
+ topL->addWidget(u_pBtn, 4,1);
+
+ d_ownBtn=new QPushButton(i18n("Do&wn"), this);
+ connect(d_ownBtn, SIGNAL(clicked()), this, SLOT(slotDownBtnClicked()));
+ topL->addWidget(d_ownBtn, 5,1);
+
+ topL->addRowSpacing(3,20); // separate up/down buttons
+ topL->setRowStretch(6,1); // stretch the listbox
+
+ slotSelectionChanged(); // disable buttons initially
+
+ load();
+}
+
+
+
+KNConfig::DisplayedHeadersWidget::~DisplayedHeadersWidget()
+{
+}
+
+
+void KNConfig::DisplayedHeadersWidget::load()
+{
+ l_box->clear();
+ QValueList<KNDisplayedHeader*> list = d_ata->headers();
+ for ( QValueList<KNDisplayedHeader*>::Iterator it = list.begin(); it != list.end(); ++it )
+ l_box->insertItem( generateItem( (*it) ) );
+}
+
+void KNConfig::DisplayedHeadersWidget::save()
+{
+ if(s_ave) {
+ d_ata->setDirty(true);
+ d_ata->save();
+ }
+ s_ave = false;
+}
+
+
+
+KNConfig::DisplayedHeadersWidget::HdrItem* KNConfig::DisplayedHeadersWidget::generateItem(KNDisplayedHeader *h)
+{
+ QString text;
+ if(h->hasName()) {
+ text=h->translatedName();
+ text+=": <";
+ } else
+ text="<";
+ text+=h->header();
+ text+=">";
+ return new HdrItem(text,h);
+}
+
+
+
+void KNConfig::DisplayedHeadersWidget::slotItemSelected(int)
+{
+ slotEditBtnClicked();
+}
+
+
+
+void KNConfig::DisplayedHeadersWidget::slotSelectionChanged()
+{
+ int curr = l_box->currentItem();
+ d_elBtn->setEnabled(curr!=-1);
+ e_ditBtn->setEnabled(curr!=-1);
+ u_pBtn->setEnabled(curr>0);
+ d_ownBtn->setEnabled((curr!=-1)&&(curr+1!=(int)(l_box->count())));
+}
+
+
+
+void KNConfig::DisplayedHeadersWidget::slotAddBtnClicked()
+{
+ KNDisplayedHeader *h=d_ata->createNewHeader();
+
+ DisplayedHeaderConfDialog* dlg=new DisplayedHeaderConfDialog(h, this);
+ if(dlg->exec()) {
+ l_box->insertItem(generateItem(h));
+ h->createTags();
+ s_ave=true;
+ } else
+ d_ata->remove(h);
+ emit changed(true);
+}
+
+
+
+void KNConfig::DisplayedHeadersWidget::slotDelBtnClicked()
+{
+ if(l_box->currentItem()==-1)
+ return;
+
+ if(KMessageBox::warningContinueCancel(this, i18n("Really delete this header?"),"",KGuiItem(i18n("&Delete"),"editdelete"))==KMessageBox::Continue) {
+ KNDisplayedHeader *h = (static_cast<HdrItem*>(l_box->item(l_box->currentItem())))->hdr;
+ d_ata->remove(h);
+ l_box->removeItem(l_box->currentItem());
+ s_ave=true;
+ }
+ emit changed(true);
+}
+
+
+
+void KNConfig::DisplayedHeadersWidget::slotEditBtnClicked()
+{
+ if (l_box->currentItem()==-1) return;
+ KNDisplayedHeader *h = (static_cast<HdrItem*>(l_box->item(l_box->currentItem())))->hdr;
+
+ DisplayedHeaderConfDialog* dlg=new DisplayedHeaderConfDialog(h, this);
+ if(dlg->exec()) {
+ l_box->changeItem(generateItem(h), l_box->currentItem());
+ h->createTags();
+ s_ave=true;
+ }
+ emit changed(true);
+}
+
+
+
+void KNConfig::DisplayedHeadersWidget::slotUpBtnClicked()
+{
+ int c=l_box->currentItem();
+ if(c==0 || c==-1) return;
+
+ KNDisplayedHeader *h = (static_cast<HdrItem*>(l_box->item(c)))->hdr;
+
+ d_ata->up(h);
+ l_box->insertItem(generateItem(h), c-1);
+ l_box->removeItem(c+1);
+ l_box->setCurrentItem(c-1);
+ s_ave=true;
+ emit changed(true);
+}
+
+
+
+void KNConfig::DisplayedHeadersWidget::slotDownBtnClicked()
+{
+ int c=l_box->currentItem();
+ if(c==-1 || c==(int) l_box->count()-1) return;
+
+ KNDisplayedHeader *h = (static_cast<HdrItem*>(l_box->item(c)))->hdr;
+
+ d_ata->down(h);
+ l_box->insertItem(generateItem(h), c+2);
+ l_box->removeItem(c);
+ l_box->setCurrentItem(c+1);
+ s_ave=true;
+ emit changed(true);
+}
+
+
+//=============================================================================================
+
+
+KNConfig::DisplayedHeaderConfDialog::DisplayedHeaderConfDialog(KNDisplayedHeader *h, QWidget *p, char *n)
+ : KDialogBase(Plain, i18n("Header Properties"),Ok|Cancel|Help, Ok, p, n),
+ h_dr(h)
+{
+ QFrame* page=plainPage();
+ QGridLayout *topL=new QGridLayout(page, 2, 2, 0, 5);
+
+ QWidget *nameW = new QWidget(page);
+ QGridLayout *nameL=new QGridLayout(nameW, 2, 2, 5);
+
+ h_drC=new KComboBox(true, nameW);
+ h_drC->lineEdit()->setMaxLength(64);
+ connect(h_drC, SIGNAL(activated(int)), this, SLOT(slotActivated(int)));
+ nameL->addWidget(new QLabel(h_drC, i18n("H&eader:"),nameW),0,0);
+ nameL->addWidget(h_drC,0,1);
+
+ n_ameE=new KLineEdit(nameW);
+
+ n_ameE->setMaxLength(64);
+ nameL->addWidget(new QLabel(n_ameE, i18n("Displayed na&me:"),nameW),1,0);
+ nameL->addWidget(n_ameE,1,1);
+ nameL->setColStretch(1,1);
+
+ topL->addMultiCellWidget(nameW,0,0,0,1);
+
+ QGroupBox *ngb=new QGroupBox(i18n("Name"), page);
+ // ### hide style settings for now, the new viewer doesn't support this yet
+ ngb->hide();
+ QVBoxLayout *ngbL = new QVBoxLayout(ngb, 8, 5);
+ ngbL->setAutoAdd(true);
+ ngbL->addSpacing(fontMetrics().lineSpacing()-4);
+ n_ameCB[0]=new QCheckBox(i18n("&Large"), ngb);
+ n_ameCB[1]=new QCheckBox(i18n("&Bold"), ngb);
+ n_ameCB[2]=new QCheckBox(i18n("&Italic"), ngb);
+ n_ameCB[3]=new QCheckBox(i18n("&Underlined"), ngb);
+ topL->addWidget(ngb,1,0);
+
+ QGroupBox *vgb=new QGroupBox(i18n("Value"), page);
+ // ### hide style settings for now, the new viewer doen't support this yet
+ vgb->hide();
+ QVBoxLayout *vgbL = new QVBoxLayout(vgb, 8, 5);
+ vgbL->setAutoAdd(true);
+ vgbL->addSpacing(fontMetrics().lineSpacing()-4);
+ v_alueCB[0]=new QCheckBox(i18n("L&arge"), vgb);
+ v_alueCB[1]=new QCheckBox(i18n("Bol&d"), vgb);
+ v_alueCB[2]=new QCheckBox(i18n("I&talic"), vgb);
+ v_alueCB[3]=new QCheckBox(i18n("U&nderlined"), vgb);
+ topL->addWidget(vgb,1,1);
+
+ topL->setColStretch(0,1);
+ topL->setColStretch(1,1);
+
+ // preset values...
+ h_drC->insertStrList(KNDisplayedHeader::predefs());
+ h_drC->lineEdit()->setText(h->header());
+ n_ameE->setText(h->translatedName());
+ for(int i=0; i<4; i++) {
+ n_ameCB[i]->setChecked(h->flag(i));
+ v_alueCB[i]->setChecked(h->flag(i+4));
+ }
+
+ setFixedHeight(sizeHint().height());
+ KNHelper::restoreWindowSize("accReadHdrPropDLG", this, sizeHint());
+
+ connect(n_ameE, SIGNAL(textChanged(const QString&)), SLOT(slotNameChanged(const QString&)));
+
+ setHelp("anc-knode-headers");
+ slotNameChanged( n_ameE->text() );
+}
+
+
+KNConfig::DisplayedHeaderConfDialog::~DisplayedHeaderConfDialog()
+{
+ KNHelper::saveWindowSize("accReadHdrPropDLG", size());
+}
+
+
+void KNConfig::DisplayedHeaderConfDialog::slotOk()
+{
+ h_dr->setHeader(h_drC->currentText());
+ h_dr->setTranslatedName(n_ameE->text());
+ for(int i=0; i<4; i++) {
+ if(h_dr->hasName())
+ h_dr->setFlag(i, n_ameCB[i]->isChecked());
+ else
+ h_dr->setFlag(i,false);
+ h_dr->setFlag(i+4, v_alueCB[i]->isChecked());
+ }
+ accept();
+}
+
+
+// the user selected one of the presets, insert the *translated* string as display name:
+void KNConfig::DisplayedHeaderConfDialog::slotActivated(int pos)
+{
+ n_ameE->setText(i18n(h_drC->text(pos).local8Bit())); // I think it's save here, the combobox has only english defaults
+}
+
+
+// disable the name format options when the name is empty
+void KNConfig::DisplayedHeaderConfDialog::slotNameChanged(const QString& str)
+{
+ for(int i=0; i<4; i++)
+ n_ameCB[i]->setEnabled(!str.isEmpty());
+}
+
+//=============================================================================================
+
+
+KNConfig::ScoringWidget::ScoringWidget( Scoring *d, QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( d )
+{
+ QGridLayout *topL = new QGridLayout(this,4,2, 5,5);
+ ksc = new KScoringEditorWidget(knGlobals.scoringManager(), this);
+ topL->addMultiCellWidget(ksc, 0,0, 0,1);
+
+ topL->addRowSpacing(1, 10);
+
+ i_gnored=new KIntSpinBox(-100000, 100000, 1, 0, 10, this);
+ QLabel *l=new QLabel(i_gnored, i18n("Default score for &ignored threads:"), this);
+ topL->addWidget(l, 2, 0);
+ topL->addWidget(i_gnored, 2, 1);
+ connect(i_gnored, SIGNAL(valueChanged(int)), SLOT(changed()));
+
+ w_atched=new KIntSpinBox(-100000, 100000, 1, 0, 10, this);
+ l=new QLabel(w_atched, i18n("Default score for &watched threads:"), this);
+ topL->addWidget(l, 3, 0);
+ topL->addWidget(w_atched, 3, 1);
+ connect(w_atched, SIGNAL(valueChanged(int)), SLOT(changed()));
+
+ topL->setColStretch(0, 1);
+
+ load();
+}
+
+
+KNConfig::ScoringWidget::~ScoringWidget()
+{
+}
+
+
+void KNConfig::ScoringWidget::load()
+{
+ i_gnored->setValue(d_ata->i_gnoredThreshold);
+ w_atched->setValue(d_ata->w_atchedThreshold);
+}
+
+void KNConfig::ScoringWidget::save()
+{
+ d_ata->i_gnoredThreshold = i_gnored->value();
+ d_ata->w_atchedThreshold = w_atched->value();
+
+ d_ata->setDirty(true);
+}
+
+
+//=============================================================================================
+
+
+KNConfig::FilterListWidget::FilterListWidget( QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ f_ilManager( knGlobals.filterManager() )
+{
+ QGridLayout *topL=new QGridLayout(this, 6,2, 5,5);
+
+ // == Filters =================================================
+
+ f_lb=new KNDialogListBox(false, this);
+ topL->addWidget(new QLabel(f_lb, i18n("&Filters:"),this),0,0);
+
+ connect(f_lb, SIGNAL(selectionChanged()), SLOT(slotSelectionChangedFilter()));
+ connect(f_lb, SIGNAL(selected(int)), SLOT(slotItemSelectedFilter(int)));
+ topL->addMultiCellWidget(f_lb,1,5,0,0);
+
+ a_ddBtn=new QPushButton(i18n("&Add..."), this);
+ connect(a_ddBtn, SIGNAL(clicked()), this, SLOT(slotAddBtnClicked()));
+ topL->addWidget(a_ddBtn,1,1);
+
+ e_ditBtn=new QPushButton(i18n("modify something","&Edit..."), this);
+ connect(e_ditBtn, SIGNAL(clicked()), this, SLOT(slotEditBtnClicked()));
+ topL->addWidget(e_ditBtn,2,1);
+
+ c_opyBtn=new QPushButton(i18n("Co&py..."), this);
+ connect(c_opyBtn, SIGNAL(clicked()), this, SLOT(slotCopyBtnClicked()));
+ topL->addWidget(c_opyBtn,3,1);
+
+ d_elBtn=new QPushButton(i18n("&Delete"), this);
+ connect(d_elBtn, SIGNAL(clicked()), this, SLOT(slotDelBtnClicked()));
+ topL->addWidget(d_elBtn,4,1);
+
+ // == Menu ====================================================
+
+ m_lb=new KNDialogListBox(false, this);
+ topL->addWidget(new QLabel(m_lb, i18n("&Menu:"),this),6,0);
+
+ connect(m_lb, SIGNAL(selectionChanged()), SLOT(slotSelectionChangedMenu()));
+ topL->addMultiCellWidget(m_lb,7,11,0,0);
+
+ u_pBtn=new QPushButton(i18n("&Up"), this);
+ connect(u_pBtn, SIGNAL(clicked()), this, SLOT(slotUpBtnClicked()));
+ topL->addWidget(u_pBtn,7,1);
+
+ d_ownBtn=new QPushButton(i18n("Do&wn"), this);
+ connect(d_ownBtn, SIGNAL(clicked()), this, SLOT(slotDownBtnClicked()));
+ topL->addWidget(d_ownBtn,8,1);
+
+ s_epAddBtn=new QPushButton(i18n("Add\n&Separator"), this);
+ connect(s_epAddBtn, SIGNAL(clicked()), this, SLOT(slotSepAddBtnClicked()));
+ topL->addWidget(s_epAddBtn,9,1);
+
+ s_epRemBtn=new QPushButton(i18n("&Remove\nSeparator"), this);
+ connect(s_epRemBtn, SIGNAL(clicked()), this, SLOT(slotSepRemBtnClicked()));
+ topL->addWidget(s_epRemBtn,10,1);
+
+ topL->setRowStretch(5,1);
+ topL->setRowStretch(11,1);
+
+ a_ctive = SmallIcon("filter",16);
+ d_isabled = SmallIcon("filter",16,KIcon::DisabledState);
+
+ load();
+
+ slotSelectionChangedFilter();
+ slotSelectionChangedMenu();
+}
+
+
+KNConfig::FilterListWidget::~FilterListWidget()
+{
+ f_ilManager->endConfig();
+}
+
+
+void KNConfig::FilterListWidget::load()
+{
+ f_lb->clear();
+ m_lb->clear();
+ f_ilManager->startConfig(this);
+}
+
+void KNConfig::FilterListWidget::save()
+{
+ f_ilManager->commitChanges();
+}
+
+
+void KNConfig::FilterListWidget::addItem(KNArticleFilter *f)
+{
+ if(f->isEnabled())
+ f_lb->insertItem(new LBoxItem(f, f->translatedName(), &a_ctive));
+ else
+ f_lb->insertItem(new LBoxItem(f, f->translatedName(), &d_isabled));
+ slotSelectionChangedFilter();
+ emit changed(true);
+}
+
+
+void KNConfig::FilterListWidget::removeItem(KNArticleFilter *f)
+{
+ int i=findItem(f_lb, f);
+ if (i!=-1) f_lb->removeItem(i);
+ slotSelectionChangedFilter();
+ emit changed(true);
+}
+
+
+void KNConfig::FilterListWidget::updateItem(KNArticleFilter *f)
+{
+ int i=findItem(f_lb, f);
+
+ if(i!=-1) {
+ if(f->isEnabled()) {
+ f_lb->changeItem(new LBoxItem(f, f->translatedName(), &a_ctive), i);
+ m_lb->changeItem(new LBoxItem(f, f->translatedName()), findItem(m_lb, f));
+ } else
+ f_lb->changeItem(new LBoxItem(f, f->translatedName(), &d_isabled), i);
+ }
+ slotSelectionChangedFilter();
+ emit changed(true);
+}
+
+
+void KNConfig::FilterListWidget::addMenuItem(KNArticleFilter *f)
+{
+ if (f) {
+ if (findItem(m_lb, f)==-1)
+ m_lb->insertItem(new LBoxItem(f, f->translatedName()));
+ } else // separator
+ m_lb->insertItem(new LBoxItem(0, "==="));
+ slotSelectionChangedMenu();
+ emit changed(true);
+}
+
+
+void KNConfig::FilterListWidget::removeMenuItem(KNArticleFilter *f)
+{
+ int i=findItem(m_lb, f);
+ if(i!=-1) m_lb->removeItem(i);
+ slotSelectionChangedMenu();
+ emit changed(true);
+}
+
+
+QValueList<int> KNConfig::FilterListWidget::menuOrder()
+{
+ KNArticleFilter *f;
+ QValueList<int> lst;
+
+ for(uint i=0; i<m_lb->count(); i++) {
+ f= (static_cast<LBoxItem*>(m_lb->item(i)))->filter;
+ if(f)
+ lst << f->id();
+ else
+ lst << -1;
+ }
+ return lst;
+}
+
+
+int KNConfig::FilterListWidget::findItem(QListBox *l, KNArticleFilter *f)
+{
+ int idx=0;
+ bool found=false;
+ while(!found && idx < (int) l->count()) {
+ found=( (static_cast<LBoxItem*>(l->item(idx)))->filter==f );
+ if(!found) idx++;
+ }
+ if(found) return idx;
+ else return -1;
+}
+
+
+void KNConfig::FilterListWidget::slotAddBtnClicked()
+{
+ f_ilManager->newFilter();
+}
+
+
+void KNConfig::FilterListWidget::slotDelBtnClicked()
+{
+ if (f_lb->currentItem()!=-1)
+ f_ilManager->deleteFilter( (static_cast<LBoxItem*>(f_lb->item(f_lb->currentItem())))->filter );
+}
+
+
+void KNConfig::FilterListWidget::slotEditBtnClicked()
+{
+ if (f_lb->currentItem()!=-1)
+ f_ilManager->editFilter( (static_cast<LBoxItem*>(f_lb->item(f_lb->currentItem())))->filter );
+}
+
+
+void KNConfig::FilterListWidget::slotCopyBtnClicked()
+{
+ if (f_lb->currentItem()!=-1)
+ f_ilManager->copyFilter( (static_cast<LBoxItem*>(f_lb->item(f_lb->currentItem())))->filter );
+}
+
+
+void KNConfig::FilterListWidget::slotUpBtnClicked()
+{
+ int c=m_lb->currentItem();
+ KNArticleFilter *f=0;
+
+ if(c==0 || c==-1) return;
+ f=(static_cast<LBoxItem*>(m_lb->item(c)))->filter;
+ if(f)
+ m_lb->insertItem(new LBoxItem(f, f->translatedName()), c-1);
+ else
+ m_lb->insertItem(new LBoxItem(0, "==="), c-1);
+ m_lb->removeItem(c+1);
+ m_lb->setCurrentItem(c-1);
+ emit changed(true);
+}
+
+
+void KNConfig::FilterListWidget::slotDownBtnClicked()
+{
+ int c=m_lb->currentItem();
+ KNArticleFilter *f=0;
+
+ if(c==-1 || c+1==(int)m_lb->count()) return;
+ f=(static_cast<LBoxItem*>(m_lb->item(c)))->filter;
+ if(f)
+ m_lb->insertItem(new LBoxItem(f, f->translatedName()), c+2);
+ else
+ m_lb->insertItem(new LBoxItem(0, "==="), c+2);
+ m_lb->removeItem(c);
+ m_lb->setCurrentItem(c+1);
+ emit changed(true);
+}
+
+
+void KNConfig::FilterListWidget::slotSepAddBtnClicked()
+{
+ m_lb->insertItem(new LBoxItem(0, "==="), m_lb->currentItem());
+ slotSelectionChangedMenu();
+ emit changed(true);
+}
+
+
+void KNConfig::FilterListWidget::slotSepRemBtnClicked()
+{
+ int c=m_lb->currentItem();
+
+ if( (c!=-1) && ( (static_cast<LBoxItem*>(m_lb->item(c)))->filter==0 ) )
+ m_lb->removeItem(c);
+ slotSelectionChangedMenu();
+ emit changed(true);
+}
+
+
+void KNConfig::FilterListWidget::slotItemSelectedFilter(int)
+{
+ slotEditBtnClicked();
+}
+
+
+void KNConfig::FilterListWidget::slotSelectionChangedFilter()
+{
+ int curr = f_lb->currentItem();
+
+ d_elBtn->setEnabled(curr!=-1);
+ e_ditBtn->setEnabled(curr!=-1);
+ c_opyBtn->setEnabled(curr!=-1);
+}
+
+
+void KNConfig::FilterListWidget::slotSelectionChangedMenu()
+{
+ int curr = m_lb->currentItem();
+
+ u_pBtn->setEnabled(curr>0);
+ d_ownBtn->setEnabled((curr!=-1)&&(curr+1!=(int)m_lb->count()));
+ s_epRemBtn->setEnabled((curr!=-1) && ( (static_cast<LBoxItem*>(m_lb->item(curr)))->filter==0 ) );
+}
+
+
+//=============================================================================================
+
+
+KNConfig::PostNewsTechnicalWidget::PostNewsTechnicalWidget( PostNewsTechnical *d, QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( d )
+{
+ QVBoxLayout *topL=new QVBoxLayout(this, 5);
+
+ // ==== General =============================================================
+
+ QGroupBox *ggb=new QGroupBox(i18n("General"), this);
+ QGridLayout *ggbL=new QGridLayout(ggb, 6,2, 8,5);
+ topL->addWidget(ggb);
+
+ ggbL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+ c_harset=new QComboBox(ggb);
+ c_harset->insertStringList(d->composerCharsets());
+ ggbL->addWidget(new QLabel(c_harset, i18n("Cha&rset:"), ggb), 1,0);
+ ggbL->addWidget(c_harset, 1,1);
+ connect(c_harset, SIGNAL(activated(int)), SLOT(changed()));
+
+ e_ncoding=new QComboBox(ggb);
+ e_ncoding->insertItem(i18n("Allow 8-bit"));
+ e_ncoding->insertItem(i18n("7-bit (Quoted-Printable)"));
+ ggbL->addWidget(new QLabel(e_ncoding, i18n("Enco&ding:"), ggb), 2,0);
+ ggbL->addWidget(e_ncoding, 2,1);
+ connect(e_ncoding, SIGNAL(activated(int)), SLOT(changed()));
+
+ u_seOwnCSCB=new QCheckBox(i18n("Use o&wn default charset when replying"), ggb);
+ ggbL->addMultiCellWidget(u_seOwnCSCB, 3,3, 0,1);
+ connect(u_seOwnCSCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ g_enMIdCB=new QCheckBox(i18n("&Generate message-id"), ggb);
+ connect(g_enMIdCB, SIGNAL(toggled(bool)), this, SLOT(slotGenMIdCBToggled(bool)));
+ ggbL->addMultiCellWidget(g_enMIdCB, 4,4, 0,1);
+ h_ost=new KLineEdit(ggb);
+ h_ost->setEnabled(false);
+ h_ostL=new QLabel(h_ost, i18n("Ho&st name:"), ggb);
+ h_ostL->setEnabled(false);
+ ggbL->addWidget(h_ostL, 5,0);
+ ggbL->addWidget(h_ost, 5,1);
+ ggbL->setColStretch(1,1);
+ connect(h_ost, SIGNAL(textChanged(const QString&)), SLOT(changed()));
+
+ // ==== X-Headers =============================================================
+
+ QGroupBox *xgb=new QGroupBox(i18n("X-Headers"), this);
+ topL->addWidget(xgb, 1);
+ QGridLayout *xgbL=new QGridLayout(xgb, 7,2, 8,5);
+
+ xgbL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+
+ l_box=new KNDialogListBox(false, xgb);
+ connect(l_box, SIGNAL(selected(int)), SLOT(slotItemSelected(int)));
+ connect(l_box, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
+ xgbL->addMultiCellWidget(l_box, 1,4, 0,0);
+
+ a_ddBtn=new QPushButton(i18n("&Add..."), xgb);
+ connect(a_ddBtn, SIGNAL(clicked()), SLOT(slotAddBtnClicked()));
+ xgbL->addWidget(a_ddBtn, 1,1);
+
+ d_elBtn=new QPushButton(i18n("Dele&te"), xgb);
+ connect(d_elBtn, SIGNAL(clicked()), SLOT(slotDelBtnClicked()));
+ xgbL->addWidget(d_elBtn, 2,1);
+
+ e_ditBtn=new QPushButton(i18n("modify something","&Edit..."), xgb);
+ connect(e_ditBtn, SIGNAL(clicked()), SLOT(slotEditBtnClicked()));
+ xgbL->addWidget(e_ditBtn, 3,1);
+
+ QLabel *placeHolders = new QLabel(i18n("<qt>Placeholders for replies: <b>%NAME</b>=sender's name, <b>%EMAIL</b>=sender's address</qt>"), xgb);
+ xgbL->addMultiCellWidget(placeHolders, 5, 5, 0, 1);
+
+ i_ncUaCB=new QCheckBox(i18n("Do not add the \"&User-Agent\" identification header"), xgb);
+ xgbL->addMultiCellWidget(i_ncUaCB, 6,6, 0,1);
+ connect(i_ncUaCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ xgbL->setRowStretch(4,1);
+ xgbL->setColStretch(0,1);
+
+ load();
+
+ slotSelectionChanged();
+}
+
+
+KNConfig::PostNewsTechnicalWidget::~PostNewsTechnicalWidget()
+{
+}
+
+
+void KNConfig::PostNewsTechnicalWidget::load()
+{
+ c_harset->setCurrentItem(d_ata->indexForCharset(d_ata->charset()));
+ e_ncoding->setCurrentItem(d_ata->a_llow8BitBody? 0:1);
+ u_seOwnCSCB->setChecked(d_ata->u_seOwnCharset);
+ g_enMIdCB->setChecked(d_ata->g_enerateMID);
+ h_ost->setText(d_ata->h_ostname);
+ i_ncUaCB->setChecked(d_ata->d_ontIncludeUA);
+
+ l_box->clear();
+ for(XHeaders::Iterator it=d_ata->x_headers.begin(); it!=d_ata->x_headers.end(); ++it)
+ l_box->insertItem((*it).header());
+}
+
+void KNConfig::PostNewsTechnicalWidget::save()
+{
+ d_ata->c_harset=c_harset->currentText().latin1();
+ d_ata->a_llow8BitBody=(e_ncoding->currentItem()==0);
+ d_ata->u_seOwnCharset=u_seOwnCSCB->isChecked();
+ d_ata->g_enerateMID=g_enMIdCB->isChecked();
+ d_ata->h_ostname=h_ost->text().latin1();
+ d_ata->d_ontIncludeUA=i_ncUaCB->isChecked();
+ d_ata->x_headers.clear();
+ for(unsigned int idx=0; idx<l_box->count(); idx++)
+ d_ata->x_headers.append( XHeader(l_box->text(idx)) );
+
+ d_ata->setDirty(true);
+}
+
+
+
+void KNConfig::PostNewsTechnicalWidget::slotGenMIdCBToggled(bool b)
+{
+ h_ost->setEnabled(b);
+ h_ostL->setEnabled(b);
+ emit changed(true);
+}
+
+
+
+void KNConfig::PostNewsTechnicalWidget::slotSelectionChanged()
+{
+ d_elBtn->setEnabled(l_box->currentItem()!=-1);
+ e_ditBtn->setEnabled(l_box->currentItem()!=-1);
+}
+
+
+
+void KNConfig::PostNewsTechnicalWidget::slotItemSelected(int)
+{
+ slotEditBtnClicked();
+}
+
+
+
+void KNConfig::PostNewsTechnicalWidget::slotAddBtnClicked()
+{
+ XHeaderConfDialog *dlg=new XHeaderConfDialog(QString::null, this);
+ if (dlg->exec())
+ l_box->insertItem(dlg->result());
+
+ delete dlg;
+
+ slotSelectionChanged();
+ emit changed(true);
+}
+
+
+
+void KNConfig::PostNewsTechnicalWidget::slotDelBtnClicked()
+{
+ int c=l_box->currentItem();
+ if (c == -1)
+ return;
+
+ l_box->removeItem(c);
+ slotSelectionChanged();
+ emit changed(true);
+}
+
+
+
+void KNConfig::PostNewsTechnicalWidget::slotEditBtnClicked()
+{
+ int c=l_box->currentItem();
+ if (c == -1)
+ return;
+
+ XHeaderConfDialog *dlg=new XHeaderConfDialog(l_box->text(c), this);
+ if (dlg->exec())
+ l_box->changeItem(dlg->result(),c);
+
+ delete dlg;
+
+ slotSelectionChanged();
+ emit changed(true);
+}
+
+
+//===================================================================================================
+
+
+KNConfig::XHeaderConfDialog::XHeaderConfDialog(const QString &h, QWidget *p, const char *n)
+ : KDialogBase(Plain, i18n("X-Headers"),Ok|Cancel, Ok, p, n)
+{
+ QFrame* page=plainPage();
+ QHBoxLayout *topL=new QHBoxLayout(page, 5,8);
+ topL->setAutoAdd(true);
+
+ new QLabel("X-", page);
+ n_ame=new KLineEdit(page);
+ new QLabel(":", page);
+ v_alue=new KLineEdit(page);
+
+ int pos=h.find(": ", 2);
+ if(pos!=-1) {
+ n_ame->setText(h.mid(2, pos-2));
+ pos+=2;
+ v_alue->setText(h.mid(pos, h.length()-pos));
+ }
+
+ setFixedHeight(sizeHint().height());
+ KNHelper::restoreWindowSize("XHeaderDlg", this, sizeHint());
+
+ n_ame->setFocus();
+}
+
+
+
+KNConfig::XHeaderConfDialog::~XHeaderConfDialog()
+{
+ KNHelper::saveWindowSize("XHeaderDlg", size());
+}
+
+
+
+QString KNConfig::XHeaderConfDialog::result()
+{
+ QString value = v_alue->text();
+ // just in case someone pastes a newline
+ value.replace( '\n', ' ' );
+ return QString( "X-%1: %2" ).arg( n_ame->text() ).arg( value );
+}
+
+
+//===================================================================================================
+
+
+KNConfig::PostNewsComposerWidget::PostNewsComposerWidget( PostNewsComposer *d, QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( d )
+{
+ QVBoxLayout *topL=new QVBoxLayout(this, 5);
+
+ // === general ===========================================================
+
+ QGroupBox *generalB=new QGroupBox(i18n("General"), this);
+ topL->addWidget(generalB);
+ QGridLayout *generalL=new QGridLayout(generalB, 3,3, 8,5);
+
+ generalL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+
+ w_ordWrapCB=new QCheckBox(i18n("Word &wrap at column:"), generalB);
+ generalL->addWidget(w_ordWrapCB,1,0);
+ m_axLen=new KIntSpinBox(20, 200, 1, 20, 10, generalB);
+ generalL->addWidget(m_axLen,1,2);
+ connect(w_ordWrapCB, SIGNAL(toggled(bool)), m_axLen, SLOT(setEnabled(bool)));
+ connect(w_ordWrapCB, SIGNAL(toggled(bool)), SLOT(changed()));
+ connect(m_axLen, SIGNAL(valueChanged(int)), SLOT(changed()));
+
+ o_wnSigCB=new QCheckBox(i18n("Appe&nd signature automatically"), generalB);
+ generalL->addMultiCellWidget(o_wnSigCB,2,2,0,1);
+ connect(o_wnSigCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ generalL->setColStretch(1,1);
+
+ // === reply =============================================================
+
+ QGroupBox *replyB=new QGroupBox(i18n("Reply"), this);
+ topL->addWidget(replyB);
+ QGridLayout *replyL=new QGridLayout(replyB, 7,2, 8,5);
+
+ replyL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+
+ i_ntro=new KLineEdit(replyB);
+ replyL->addMultiCellWidget(new QLabel(i_ntro,i18n("&Introduction phrase:"), replyB),1,1,0,1);
+ replyL->addMultiCellWidget(i_ntro, 2,2,0,1);
+ replyL->addMultiCellWidget(new QLabel(i18n("<qt>Placeholders: <b>%NAME</b>=sender's name, <b>%EMAIL</b>=sender's address,<br><b>%DATE</b>=date, <b>%MSID</b>=message-id, <b>%GROUP</b>=group name, <b>%L</b>=line break</qt>"), replyB),3,3,0,1);
+ connect(i_ntro, SIGNAL(textChanged(const QString&)), SLOT(changed()));
+
+ r_ewrapCB=new QCheckBox(i18n("Rewrap quoted te&xt automatically"), replyB);
+ replyL->addMultiCellWidget(r_ewrapCB, 4,4,0,1);
+ connect(r_ewrapCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ a_uthSigCB=new QCheckBox(i18n("Include the a&uthor's signature"), replyB);
+ replyL->addMultiCellWidget(a_uthSigCB, 5,5,0,1);
+ connect(a_uthSigCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ c_ursorOnTopCB=new QCheckBox(i18n("Put the cursor &below the introduction phrase"), replyB);
+ replyL->addMultiCellWidget(c_ursorOnTopCB, 6,6,0,1);
+ connect(c_ursorOnTopCB, SIGNAL(toggled(bool)), SLOT(changed()));
+
+ replyL->setColStretch(1,1);
+
+ // === external editor ========================================================
+
+ QGroupBox *editorB=new QGroupBox(i18n("External Editor"), this);
+ topL->addWidget(editorB);
+ QGridLayout *editorL=new QGridLayout(editorB, 6,3, 8,5);
+
+ editorL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+
+ e_ditor=new KLineEdit(editorB);
+ editorL->addWidget(new QLabel(e_ditor, i18n("Specify edi&tor:"), editorB),1,0);
+ editorL->addWidget(e_ditor,1,1);
+ QPushButton *btn = new QPushButton(i18n("Choo&se..."),editorB);
+ connect(btn, SIGNAL(clicked()), SLOT(slotChooseEditor()));
+ connect(e_ditor, SIGNAL(textChanged(const QString&)), SLOT(changed()));
+ editorL->addWidget(btn,1,2);
+
+ editorL->addMultiCellWidget(new QLabel(i18n("%f will be replaced with the filename to edit."), editorB),2,2,0,2);
+
+ e_xternCB=new QCheckBox(i18n("Start exte&rnal editor automatically"), editorB);
+ editorL->addMultiCellWidget(e_xternCB, 3,3,0,2);
+ connect(e_xternCB, SIGNAL(clicked()), SLOT(changed()));
+
+ editorL->setColStretch(1,1);
+
+ topL->addStretch(1);
+
+ load();
+}
+
+
+KNConfig::PostNewsComposerWidget::~PostNewsComposerWidget()
+{
+}
+
+
+void KNConfig::PostNewsComposerWidget::load()
+{
+ w_ordWrapCB->setChecked(d_ata->w_ordWrap);
+ m_axLen->setEnabled(d_ata->w_ordWrap);
+ a_uthSigCB->setChecked(d_ata->i_ncSig);
+ c_ursorOnTopCB->setChecked(d_ata->c_ursorOnTop);
+ e_xternCB->setChecked(d_ata->u_seExtEditor);
+ o_wnSigCB->setChecked(d_ata->a_ppSig);
+ r_ewrapCB->setChecked(d_ata->r_ewrap);
+ m_axLen->setValue(d_ata->m_axLen);
+ i_ntro->setText(d_ata->i_ntro);
+ e_ditor->setText(d_ata->e_xternalEditor);
+}
+
+
+void KNConfig::PostNewsComposerWidget::save()
+{
+ d_ata->w_ordWrap=w_ordWrapCB->isChecked();
+ d_ata->m_axLen=m_axLen->value();
+ d_ata->r_ewrap=r_ewrapCB->isChecked();
+ d_ata->a_ppSig=o_wnSigCB->isChecked();
+ d_ata->i_ntro=i_ntro->text();
+ d_ata->i_ncSig=a_uthSigCB->isChecked();
+ d_ata->c_ursorOnTop=c_ursorOnTopCB->isChecked();
+ d_ata->e_xternalEditor=e_ditor->text();
+ d_ata->u_seExtEditor=e_xternCB->isChecked();
+
+ d_ata->setDirty(true);
+}
+
+
+void KNConfig::PostNewsComposerWidget::slotChooseEditor()
+{
+ QString path=e_ditor->text().simplifyWhiteSpace();
+ if (path.right(3) == " %f")
+ path.truncate(path.length()-3);
+
+ path=KFileDialog::getOpenFileName(path, QString::null, this, i18n("Choose Editor"));
+
+ if (!path.isEmpty())
+ e_ditor->setText(path+" %f");
+}
+
+
+//===================================================================================================
+
+
+KNConfig::PostNewsSpellingWidget::PostNewsSpellingWidget( QWidget *p, const char *n ) :
+ KCModule( p, n )
+{
+ QVBoxLayout *topL=new QVBoxLayout(this, 5);
+
+ c_onf = new KSpellConfig( this, "spell", 0, false );
+ topL->addWidget(c_onf);
+ connect(c_onf, SIGNAL(configChanged()), SLOT(changed()));
+
+ topL->addStretch(1);
+}
+
+
+KNConfig::PostNewsSpellingWidget::~PostNewsSpellingWidget()
+{
+}
+
+
+void KNConfig::PostNewsSpellingWidget::save()
+{
+ c_onf->writeGlobalSettings();
+}
+
+
+//==============================================================================================================
+
+KNConfig::PrivacyWidget::PrivacyWidget(QWidget *p, const char *n) :
+ KCModule( p, n )
+{
+ QBoxLayout *topLayout = new QVBoxLayout(this, 5);
+ c_onf = new Kpgp::Config(this,"knode pgp config",false);
+ topLayout->addWidget(c_onf);
+ connect(c_onf, SIGNAL(changed()), SLOT(changed()));
+
+ topLayout->addStretch(1);
+
+ load();
+}
+
+
+KNConfig::PrivacyWidget::~PrivacyWidget()
+{
+}
+
+
+void KNConfig::PrivacyWidget::save()
+{
+ c_onf->applySettings();
+}
+
+
+//==============================================================================================================
+
+
+//BEGIN: Cleanup configuration widgets ---------------------------------------
+
+
+KNConfig::GroupCleanupWidget::GroupCleanupWidget( Cleanup *data, QWidget *parent, const char *name )
+ : QWidget( parent, name ), mData( data )
+{
+ QVBoxLayout *top = new QVBoxLayout( this );
+
+ if (!mData->isGlobal()) {
+ mDefault = new QCheckBox( i18n("&Use global cleanup configuration"), this );
+ connect( mDefault, SIGNAL(toggled(bool)), SLOT(slotDefaultToggled(bool)) );
+ top->addWidget( mDefault );
+ }
+
+ mExpGroup = new QGroupBox( i18n("Newsgroup Cleanup Settings"), this );
+ mExpGroup->setColumnLayout(0, Qt::Vertical );
+ mExpGroup->layout()->setSpacing( KDialog::spacingHint() );
+ mExpGroup->layout()->setMargin( KDialog::marginHint() );
+ top->addWidget( mExpGroup );
+ QGridLayout *grid = new QGridLayout( mExpGroup->layout(), 7, 2 );
+
+ grid->setRowSpacing( 0, KDialog::spacingHint() );
+
+ mExpEnabled = new QCheckBox( i18n("&Expire old articles automatically"), mExpGroup );
+ grid->addMultiCellWidget( mExpEnabled, 1, 1, 0, 1 );
+ connect( mExpEnabled, SIGNAL(toggled(bool)), SIGNAL(changed()) );
+
+ mExpDays = new KIntSpinBox( 0, 99999, 1, 0, 10, mExpGroup );
+ QLabel *label = new QLabel( mExpDays, i18n("&Purge groups every:"), mExpGroup );
+ grid->addWidget( label, 2, 0 );
+ grid->addWidget( mExpDays, 2, 1, Qt::AlignRight );
+ connect( mExpDays, SIGNAL(valueChanged(int)), SIGNAL(changed()) );
+ connect( mExpDays, SIGNAL(valueChanged(int)), SLOT(expDaysChanged(int)) );
+ connect( mExpEnabled, SIGNAL(toggled(bool)), label, SLOT(setEnabled(bool)) );
+ connect( mExpEnabled, SIGNAL(toggled(bool)), mExpDays, SLOT(setEnabled(bool)) );
+
+ mExpReadDays = new KIntSpinBox( 0, 99999, 1, 0, 10, mExpGroup );
+ label = new QLabel( mExpReadDays, i18n("&Keep read articles:"), mExpGroup );
+ grid->addWidget( label, 3, 0 );
+ grid->addWidget( mExpReadDays, 3, 1, Qt::AlignRight );
+ connect( mExpReadDays, SIGNAL(valueChanged(int)), SIGNAL(changed()) );
+ connect( mExpReadDays, SIGNAL(valueChanged(int)), SLOT(expReadDaysChanged(int)) );
+
+ mExpUnreadDays = new KIntSpinBox( 0, 99999, 1, 0, 10, mExpGroup );
+ label = new QLabel( mExpUnreadDays, i18n("Keep u&nread articles:"), mExpGroup );
+ grid->addWidget( label, 4, 0 );
+ grid->addWidget( mExpUnreadDays, 4, 1, Qt::AlignRight );
+ connect( mExpUnreadDays, SIGNAL(valueChanged(int)), SIGNAL(changed()) );
+ connect( mExpUnreadDays, SIGNAL(valueChanged(int)), SLOT(expUnreadDaysChanged(int)) );
+
+ mExpUnavailable = new QCheckBox( i18n("&Remove articles that are not available on the server"), mExpGroup );
+ grid->addMultiCellWidget( mExpUnavailable, 5, 5, 0, 1 );
+ connect( mExpUnavailable, SIGNAL(toggled(bool)), SIGNAL(changed()) );
+
+ mPreserveThreads = new QCheckBox( i18n("Preser&ve threads"), mExpGroup );
+ grid->addMultiCellWidget( mPreserveThreads, 6, 6, 0, 1 );
+ connect( mPreserveThreads, SIGNAL(toggled(bool)), SIGNAL(changed()) );
+
+ grid->setColStretch(1,1);
+}
+
+void KNConfig::GroupCleanupWidget::expDaysChanged(int value)
+{
+ mExpDays->setSuffix( i18n(" day", " days", value) );
+}
+
+void KNConfig::GroupCleanupWidget::expReadDaysChanged(int value)
+{
+ mExpReadDays->setSuffix( i18n(" day", " days", value) );
+}
+
+void KNConfig::GroupCleanupWidget::expUnreadDaysChanged(int value)
+{
+ mExpUnreadDays->setSuffix( i18n(" day", " days", value) );
+}
+
+void KNConfig::GroupCleanupWidget::load()
+{
+ if (!mData->isGlobal()) {
+ mDefault->setChecked( mData->useDefault() );
+ slotDefaultToggled( mData->useDefault() );
+ }
+ mExpEnabled->setChecked( !mData->d_oExpire ); // make sure the toggled(bool) signal is emitted at least once
+ mExpEnabled->setChecked( mData->d_oExpire );
+ mExpDays->setValue( mData->e_xpireInterval );
+ mExpReadDays->setValue( mData->maxAgeForRead() );
+ mExpUnreadDays->setValue( mData->maxAgeForUnread() );
+ mExpUnavailable->setChecked( mData->removeUnavailable() );
+ mPreserveThreads->setChecked( mData->preserveThreads() );
+}
+
+
+void KNConfig::GroupCleanupWidget::save()
+{
+ if (!mData->isGlobal())
+ mData->setUseDefault( mDefault->isChecked() );
+ mData->d_oExpire = mExpEnabled->isChecked();
+ mData->e_xpireInterval = mExpDays->value();
+ mData->r_eadMaxAge = mExpReadDays->value();
+ mData->u_nreadMaxAge = mExpUnreadDays->value();
+ mData->r_emoveUnavailable = mExpUnavailable->isChecked();
+ mData->p_reserveThr = mPreserveThreads->isChecked();
+}
+
+
+void KNConfig::GroupCleanupWidget::slotDefaultToggled( bool state )
+{
+ mExpGroup->setEnabled( !state );
+}
+
+
+KNConfig::CleanupWidget::CleanupWidget( QWidget *p, const char *n ) :
+ KCModule( p, n ),
+ d_ata( knGlobals.configManager()->cleanup() )
+{
+ QVBoxLayout *topL=new QVBoxLayout(this, 5);
+
+ mGroupCleanup = new GroupCleanupWidget( d_ata, this );
+ topL->addWidget( mGroupCleanup );
+ connect( mGroupCleanup, SIGNAL(changed()), SLOT(changed()) );
+
+ // === folders =========================================================
+
+ QGroupBox *foldersB=new QGroupBox(i18n("Folders"), this);
+ foldersB->setColumnLayout(0, Qt::Vertical );
+ foldersB->layout()->setSpacing( KDialog::spacingHint() );
+ foldersB->layout()->setMargin( KDialog::marginHint() );
+
+ topL->addWidget(foldersB);
+ QGridLayout *foldersL=new QGridLayout(foldersB->layout(), 3,2);
+
+ foldersL->setRowSpacing( 0, KDialog::spacingHint() );
+
+ f_olderCB=new QCheckBox(i18n("Co&mpact folders automatically"), foldersB);
+ connect(f_olderCB, SIGNAL(toggled(bool)), this, SLOT(slotFolderCBtoggled(bool)));
+ foldersL->addMultiCellWidget(f_olderCB,1,1,0,1);
+
+ f_olderDays=new KIntSpinBox(0, 99999, 1, 0, 10, foldersB);
+ f_olderDaysL=new QLabel(f_olderDays,i18n("P&urge folders every:"), foldersB);
+ foldersL->addWidget(f_olderDaysL,2,0);
+ foldersL->addWidget(f_olderDays,2,1,Qt::AlignRight);
+ connect(f_olderDays, SIGNAL(valueChanged(int)), SLOT(changed()));
+ connect(f_olderDays, SIGNAL(valueChanged(int)), SLOT(slotFolderDaysChanged(int)));
+
+ foldersL->setColStretch(1,1);
+
+ topL->addStretch(1);
+
+ load();
+}
+
+
+KNConfig::CleanupWidget::~CleanupWidget()
+{
+}
+
+
+void KNConfig::CleanupWidget::load()
+{
+ f_olderCB->setChecked(d_ata->d_oCompact);
+ slotFolderCBtoggled(d_ata->d_oCompact);
+ f_olderDays->setValue(d_ata->c_ompactInterval);
+ mGroupCleanup->load();
+}
+
+
+void KNConfig::CleanupWidget::save()
+{
+ d_ata->d_oCompact=f_olderCB->isChecked();
+ d_ata->c_ompactInterval=f_olderDays->value();
+
+ mGroupCleanup->save();
+
+ d_ata->setDirty(true);
+}
+
+
+void KNConfig::CleanupWidget::slotFolderCBtoggled(bool b)
+{
+ f_olderDaysL->setEnabled(b);
+ f_olderDays->setEnabled(b);
+ emit changed(true);
+}
+
+void KNConfig::CleanupWidget::slotFolderDaysChanged(int value)
+{
+ f_olderDays->setSuffix(i18n(" day", " days", value));
+}
+
+//END: Cleanup configuration widgets -----------------------------------------
+
+//==============================================================================================================
+
+/*
+KNConfig::CacheWidget::CacheWidget(Cache *d, QWidget *p, const char *n)
+ : KCModule p, n), d_ata(d)
+{
+ QVBoxLayout *topL=new QVBoxLayout(this, 5);
+
+ // memory
+ QGroupBox *memGB=new QGroupBox(i18n("Memory Cache"), this);
+ topL->addWidget(memGB);
+ QGridLayout *memL=new QGridLayout(memGB, 3,2, 8,5);
+ memL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+
+ memL->addWidget(new QLabel(i18n("Max articles to keep:"), memGB), 1,0);
+ m_emMaxArt=new KIntSpinBox(0, 99999, 1, 1, 10, memGB);
+ memL->addWidget(m_emMaxArt, 1,1);
+
+ memL->addWidget(new QLabel(i18n("Max memory usage:"), memGB), 2,0);
+ m_emMaxKB=new KIntSpinBox(0, 99999, 1, 1, 10, memGB);
+ m_emMaxKB->setSuffix(" KB");
+ memL->addWidget(m_emMaxKB, 2,1);
+
+ memL->setColStretch(0,1);
+
+
+ // disk
+ QGroupBox *diskGB=new QGroupBox(i18n("Disk Cache"), this);
+ topL->addWidget(diskGB);
+ QGridLayout *diskL=new QGridLayout(diskGB, 3,2, 8,5);
+ diskL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+
+ d_iskMaxArtL=new QLabel(i18n("Max articles to keep:"), diskGB);
+ diskL->addWidget(d_iskMaxArtL, 2,0);
+ d_iskMaxArt=new KIntSpinBox(0, 99999, 1, 1, 10, diskGB);
+ diskL->addWidget(d_iskMaxArt, 2,1);
+
+ d_iskMaxKBL=new QLabel(i18n("Max disk usage:"), diskGB);
+ diskL->addWidget(d_iskMaxKBL, 3,0);
+ d_iskMaxKB=new KIntSpinBox(0, 99999, 1, 1, 10, diskGB);
+ d_iskMaxKB->setSuffix(" KB");
+ diskL->addWidget(d_iskMaxKB, 3,1);
+
+ diskL->setColStretch(0,1);
+ 7
+
+ topL->addStretch(1);
+
+
+ // init
+ m_emMaxArt->setValue(d->memoryMaxArticles());
+ m_emMaxKB->setValue(d->memoryMaxKBytes());
+ d_iskMaxArt->setValue(d->diskMaxArticles());
+ d_iskMaxKB->setValue(d->diskMaxKBytes());
+}
+
+
+KNConfig::CacheWidget::~CacheWidget()
+{
+}
+
+
+void KNConfig::CacheWidget::apply()
+{
+ d_ata->m_emMaxArt=m_emMaxArt->value();
+ d_ata->m_emMaxKB=m_emMaxKB->value();
+
+ d_ata->d_iskMaxArt=d_iskMaxArt->value();
+ d_ata->d_iskMaxKB=d_iskMaxKB->value();
+
+ d_ata->setDirty(true);
+}
+*/
+
+
+//------------------------
+#include "knconfigwidgets.moc"
diff --git a/knode/knconfigwidgets.h b/knode/knconfigwidgets.h
new file mode 100644
index 000000000..109ea7dc7
--- /dev/null
+++ b/knode/knconfigwidgets.h
@@ -0,0 +1,730 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCONFIGWIDGETS_H
+#define KNCONFIGWIDGETS_H
+
+#include <kdialogbase.h>
+#include <kcmodule.h>
+
+#include "knwidgets.h"
+#include "smtpaccountwidget_base.h"
+
+class QButtonGroup;
+class QCheckBox;
+class QGroupBox;
+class QRadioButton;
+class QTextEdit;
+
+class KScoringEditorWidget;
+class KConfigBase;
+class KLineEdit;
+class KComboBox;
+class KIntSpinBox;
+class KSpellConfig;
+class KURLCompletion;
+
+namespace Kpgp {
+ class Config;
+ class SecretKeyRequester;
+}
+
+class KNAccountManager;
+class KNArticleFilter;
+class KNDisplayedHeader;
+class KNFilterManager;
+class KNNntpAccount;
+class KNServerInfo;
+
+namespace KNConfig {
+ class Appearance;
+ class Cleanup;
+ class Identity;
+ class DisplayedHeaders;
+ class GroupCleanupWidget;
+ class PostNewsTechnical;
+ class ReadNewsGeneral;
+ class ReadNewsNavigation;
+ class PostNewsComposer;
+ class ReadNewsViewer;
+ class Scoring;
+}
+
+namespace KNConfig {
+
+class KDE_EXPORT IdentityWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ IdentityWidget(Identity *d, QWidget *p=0, const char *n=0);
+ ~IdentityWidget();
+
+ void load();
+ void save();
+
+ protected:
+ QLabel *f_ileName;
+ KLineEdit *n_ame,
+ *o_rga,
+ *e_mail,
+ *r_eplyTo,
+ *m_ailCopiesTo,
+ *s_ig;
+ QRadioButton *s_igFile,
+ *s_igEdit;
+ QCheckBox *s_igGenerator;
+ QPushButton *c_hooseBtn,
+ *e_ditBtn;
+ QTextEdit *s_igEditor;
+ QButtonGroup *b_uttonGroup;
+ Kpgp::SecretKeyRequester
+ *s_igningKey;
+ KURLCompletion *c_ompletion;
+
+ Identity *d_ata;
+
+ protected slots:
+ void slotSignatureType(int type);
+ void slotSignatureChoose();
+ void slotSignatureEdit();
+ void textFileNameChanged(const QString &);
+
+};
+
+
+class KDE_EXPORT NntpAccountListWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ NntpAccountListWidget(QWidget *p=0, const char *n=0);
+ ~NntpAccountListWidget();
+
+ void load();
+
+ protected:
+ class LBoxItem : public KNListBoxItem {
+ public:
+ LBoxItem(KNNntpAccount *a, const QString &t, QPixmap *p=0)
+ : KNListBoxItem(t, p) , account(a) {}
+ ~LBoxItem() {}
+ KNNntpAccount *account;
+ };
+
+ KNDialogListBox *l_box;
+ QPushButton *a_ddBtn,
+ *d_elBtn,
+ *e_ditBtn,
+ *s_ubBtn;
+ QPixmap p_ixmap;
+ QLabel *s_erverInfo,
+ *p_ortInfo;
+
+ KNAccountManager *a_ccManager;
+
+
+ public slots:
+ void slotAddItem(KNNntpAccount *a);
+ void slotRemoveItem(KNNntpAccount *a);
+ void slotUpdateItem(KNNntpAccount *a);
+
+ protected slots:
+ void slotSelectionChanged();
+ void slotItemSelected(int id);
+ void slotAddBtnClicked();
+ void slotDelBtnClicked();
+ void slotEditBtnClicked();
+ void slotSubBtnClicked();
+
+};
+
+
+class KDE_EXPORT NntpAccountConfDialog : public KDialogBase {
+
+ Q_OBJECT
+
+ public:
+ NntpAccountConfDialog(KNNntpAccount* acc, QWidget *p=0, const char *n=0);
+ ~NntpAccountConfDialog();
+
+ protected:
+ KLineEdit *n_ame,
+ *s_erver,
+ *u_ser,
+ *p_ass,
+ *p_ort;
+ QLabel *u_serLabel,
+ *p_assLabel,
+ *c_heckIntervalLabel;
+ KIntSpinBox *h_old,
+ *t_imeout,
+ *c_heckInterval;
+ QCheckBox *f_etchDes,
+ *a_uth,
+ *u_seDiskCache,
+ *i_nterval;
+ KNConfig::IdentityWidget* i_dWidget;
+
+ KNNntpAccount *a_ccount;
+
+ protected slots:
+ void slotOk();
+ void slotAuthChecked(bool b);
+ void slotIntervalChecked(bool b);
+
+ private slots:
+ void slotPasswordChanged();
+
+ private:
+ GroupCleanupWidget *mCleanupWidget;
+};
+
+
+class KDE_EXPORT SmtpAccountWidget : public SmtpAccountWidgetBase {
+
+Q_OBJECT
+
+ public:
+ SmtpAccountWidget(QWidget *p=0, const char *n=0);
+ ~SmtpAccountWidget() {}
+
+ virtual void load();
+ virtual void save();
+
+ protected slots:
+ virtual void useExternalMailerToggled( bool b );
+ virtual void loginToggled( bool b );
+ void slotPasswordChanged();
+
+ protected:
+ KNServerInfo *mAccount;
+};
+
+
+class KDE_EXPORT AppearanceWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ AppearanceWidget(QWidget *p=0, const char *n=0);
+ ~AppearanceWidget();
+
+ void load();
+ void save();
+ void defaults();
+
+ //===================================================================================
+ // code taken from KMail, Copyright (C) 2000 Espen Sand, espen@kde.org
+
+ class KDE_EXPORT ColorListItem : public QListBoxText {
+
+ public:
+ ColorListItem( const QString &text, const QColor &color=Qt::black );
+ ~ColorListItem();
+ const QColor& color() { return mColor; }
+ void setColor( const QColor &color ) { mColor = color; }
+
+ protected:
+ virtual void paint( QPainter * );
+ virtual int height( const QListBox * ) const;
+ virtual int width( const QListBox * ) const;
+
+ private:
+ QColor mColor;
+ };
+
+ //===================================================================================
+
+ class KDE_EXPORT FontListItem : public QListBoxText {
+
+ public:
+ FontListItem( const QString &name, const QFont & );
+ ~FontListItem();
+ const QFont& font() { return f_ont; }
+ void setFont( const QFont &);
+
+ protected:
+ virtual void paint( QPainter * );
+ virtual int width( const QListBox * ) const;
+
+ private:
+ QFont f_ont;
+ QString fontInfo;
+ };
+
+ //===================================================================================
+
+ KNDialogListBox *c_List,
+ *f_List;
+ QCheckBox *c_olorCB,
+ *f_ontCB;
+ QPushButton *c_olChngBtn,
+ *f_ntChngBtn;
+
+ Appearance *d_ata;
+
+ protected slots:
+ //colors
+ void slotColCheckBoxToggled(bool b);
+ void slotColItemSelected(QListBoxItem *); // show color dialog for the entry
+ void slotColChangeBtnClicked();
+ void slotColSelectionChanged();
+
+ //fonts
+ void slotFontCheckBoxToggled(bool b);
+ void slotFontItemSelected(QListBoxItem *); // show font dialog for the entry
+ void slotFontChangeBtnClicked();
+ void slotFontSelectionChanged();
+
+};
+
+
+class KDE_EXPORT ReadNewsGeneralWidget : public KCModule {
+
+ public:
+ ReadNewsGeneralWidget(ReadNewsGeneral *d, QWidget *p=0, const char *n=0);
+ ~ReadNewsGeneralWidget();
+
+ void load();
+ void save();
+
+ protected:
+ QCheckBox *a_utoCB,
+ *m_arkCB,
+ *m_arkCrossCB,
+ *s_martScrollingCB,
+ *e_xpThrCB,
+ *d_efaultExpandCB,
+ *l_inesCB,
+ *u_nreadCB,
+ *s_coreCB;
+ KIntSpinBox *m_arkSecs,
+ *m_axFetch,
+ *c_ollCacheSize,
+ *a_rtCacheSize;
+
+ ReadNewsGeneral *d_ata;
+
+};
+
+
+class KDE_EXPORT ReadNewsNavigationWidget : public KCModule {
+
+ public:
+ ReadNewsNavigationWidget(ReadNewsNavigation *d, QWidget *p=0, const char *n=0);
+ ~ReadNewsNavigationWidget();
+
+ void load();
+ void save();
+
+ protected:
+ QCheckBox *m_arkAllReadGoNextCB,
+ *m_arkThreadReadGoNextCB,
+ *m_arkThreadReadCloseThreadCB,
+ *i_gnoreThreadGoNextCB,
+ *i_gnoreThreadCloseThreadCB;
+
+ ReadNewsNavigation *d_ata;
+
+};
+
+
+class KDE_EXPORT ReadNewsViewerWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ ReadNewsViewerWidget(ReadNewsViewer *d, QWidget *p=0, const char *n=0);
+ ~ReadNewsViewerWidget();
+
+ void load();
+ void save();
+
+ protected:
+ QCheckBox *r_ewrapCB,
+ *r_emoveTrailingCB,
+ *s_igCB,
+ *o_penAttCB,
+ *a_ltAttCB,
+ *mShowRefBar,
+ *mAlwaysShowHTML;
+ KLineEdit *q_uoteCharacters;
+
+ ReadNewsViewer *d_ata;
+
+};
+
+
+class KDE_EXPORT DisplayedHeadersWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ DisplayedHeadersWidget(DisplayedHeaders *d, QWidget *p=0, const char *n=0);
+ ~DisplayedHeadersWidget();
+
+ void load();
+ void save();
+
+ protected:
+
+ class HdrItem : public QListBoxText {
+
+ public:
+ HdrItem( const QString &t, KNDisplayedHeader *h ) : QListBoxText(t), hdr(h) {}
+ ~HdrItem() {}
+
+ KNDisplayedHeader *hdr;
+ };
+
+ HdrItem* generateItem(KNDisplayedHeader *);
+
+ KNDialogListBox *l_box;
+ QPushButton *a_ddBtn,
+ *d_elBtn,
+ *e_ditBtn,
+ *u_pBtn,
+ *d_ownBtn;
+ bool s_ave;
+
+ DisplayedHeaders *d_ata;
+
+ protected slots:
+ void slotItemSelected(int);
+ void slotSelectionChanged();
+ void slotAddBtnClicked();
+ void slotDelBtnClicked();
+ void slotEditBtnClicked();
+ void slotUpBtnClicked();
+ void slotDownBtnClicked();
+
+};
+
+
+class KDE_EXPORT DisplayedHeaderConfDialog : public KDialogBase {
+
+ Q_OBJECT
+
+ public:
+ DisplayedHeaderConfDialog(KNDisplayedHeader *h, QWidget *p=0, char *n=0);
+ ~DisplayedHeaderConfDialog();
+
+
+ protected:
+ KNDisplayedHeader *h_dr;
+ KComboBox *h_drC;
+ KLineEdit *n_ameE;
+ QCheckBox *n_ameCB[4],
+ *v_alueCB[4];
+
+
+ protected slots:
+ void slotOk();
+ void slotActivated(int);
+ void slotNameChanged(const QString&);
+};
+
+
+class KDE_EXPORT ScoringWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ ScoringWidget(Scoring *d, QWidget *p=0, const char *n=0);
+ ~ScoringWidget();
+
+ void load();
+ void save();
+
+ private:
+ KScoringEditorWidget *ksc;
+ KIntSpinBox *i_gnored,
+ *w_atched;
+
+ Scoring *d_ata;
+};
+
+
+class KDE_EXPORT FilterListWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ FilterListWidget(QWidget *p=0, const char *n=0);
+ ~FilterListWidget();
+
+ void load();
+ void save();
+
+ void addItem(KNArticleFilter *f);
+ void removeItem(KNArticleFilter *f);
+ void updateItem(KNArticleFilter *f);
+ void addMenuItem(KNArticleFilter *f);
+ void removeMenuItem(KNArticleFilter *f);
+ QValueList<int> menuOrder();
+
+
+ protected:
+ class LBoxItem : public KNListBoxItem {
+ public:
+ LBoxItem(KNArticleFilter *f, const QString &t, QPixmap *p=0)
+ : KNListBoxItem(t, p) , filter(f) {}
+ ~LBoxItem() {}
+
+ KNArticleFilter *filter;
+ };
+
+ int findItem(QListBox *l, KNArticleFilter *f);
+
+ KNDialogListBox *f_lb,
+ *m_lb;
+
+ QPushButton *a_ddBtn,
+ *d_elBtn,
+ *e_ditBtn,
+ *c_opyBtn,
+ *u_pBtn,
+ *d_ownBtn,
+ *s_epAddBtn,
+ *s_epRemBtn;
+
+ QPixmap a_ctive,
+ d_isabled;
+
+ KNFilterManager *f_ilManager;
+
+
+ protected slots:
+ void slotAddBtnClicked();
+ void slotDelBtnClicked();
+ void slotEditBtnClicked();
+ void slotCopyBtnClicked();
+ void slotUpBtnClicked();
+ void slotDownBtnClicked();
+ void slotSepAddBtnClicked();
+ void slotSepRemBtnClicked();
+ void slotItemSelectedFilter(int);
+ void slotSelectionChangedFilter();
+ void slotSelectionChangedMenu();
+
+};
+
+
+class KDE_EXPORT PostNewsTechnicalWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ PostNewsTechnicalWidget(PostNewsTechnical *d, QWidget *p=0, const char *n=0);
+ ~PostNewsTechnicalWidget();
+
+ void load();
+ void save();
+
+ protected:
+ QComboBox *c_harset,
+ *e_ncoding;
+ QCheckBox *u_seOwnCSCB,
+ *g_enMIdCB,
+ *i_ncUaCB;
+ KNDialogListBox *l_box;
+ QPushButton *a_ddBtn,
+ *d_elBtn,
+ *e_ditBtn;
+ KLineEdit *h_ost;
+ QLabel *h_ostL;
+
+ PostNewsTechnical *d_ata;
+
+ protected slots:
+ void slotGenMIdCBToggled(bool b);
+ void slotSelectionChanged();
+ void slotItemSelected(int id);
+ void slotAddBtnClicked();
+ void slotDelBtnClicked();
+ void slotEditBtnClicked();
+
+};
+
+
+class KDE_EXPORT XHeaderConfDialog : public KDialogBase {
+
+ public:
+ XHeaderConfDialog(const QString &h=QString::null, QWidget *p=0, const char *n=0);
+ ~XHeaderConfDialog();
+
+ QString result();
+
+
+ protected:
+ KLineEdit *n_ame,
+ *v_alue;
+
+};
+
+
+class KDE_EXPORT PostNewsComposerWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ PostNewsComposerWidget(PostNewsComposer *d, QWidget *p=0, const char *n=0);
+ ~PostNewsComposerWidget();
+
+ void load();
+ void save();
+
+ protected:
+ KIntSpinBox *m_axLen;
+ QCheckBox *w_ordWrapCB,
+ *o_wnSigCB,
+ *r_ewrapCB,
+ *a_uthSigCB,
+ *c_ursorOnTopCB,
+ *e_xternCB;
+ KLineEdit *i_ntro,
+ *e_ditor;
+
+ PostNewsComposer *d_ata;
+
+ protected slots:
+ void slotChooseEditor();
+
+};
+
+
+class KDE_EXPORT PostNewsSpellingWidget : public KCModule {
+
+ public:
+ PostNewsSpellingWidget(QWidget *p=0, const char *n=0);
+ ~PostNewsSpellingWidget();
+
+ void save();
+
+ protected:
+ KSpellConfig *c_onf;
+
+};
+
+
+
+class KDE_EXPORT PrivacyWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ PrivacyWidget(QWidget *p=0, const char *n=0);
+ ~PrivacyWidget();
+
+ void save();
+
+ protected:
+ Kpgp::Config *c_onf;
+};
+
+
+
+//BEGIN: Cleanup configuration -----------------------------------------------
+
+/** Configuration widget for group expireration */
+class KDE_EXPORT GroupCleanupWidget : public QWidget {
+
+ Q_OBJECT
+
+ public:
+ GroupCleanupWidget( Cleanup *data, QWidget *parent = 0, const char *name = 0 );
+
+ void load();
+ void save();
+
+ signals:
+ void changed();
+
+ private:
+ QCheckBox *mDefault, *mExpEnabled, *mExpUnavailable, *mPreserveThreads;
+ KIntSpinBox *mExpDays, *mExpReadDays, *mExpUnreadDays;
+ QGroupBox *mExpGroup;
+ Cleanup *mData;
+
+ private slots:
+ void slotDefaultToggled( bool state );
+ void expDaysChanged( int value );
+ void expReadDaysChanged( int value );
+ void expUnreadDaysChanged( int value );
+};
+
+
+/** Global cleanup configuration widget */
+class KDE_EXPORT CleanupWidget : public KCModule {
+
+ Q_OBJECT
+
+ public:
+ CleanupWidget(QWidget *p=0, const char *n=0);
+ ~CleanupWidget();
+
+ void load();
+ void save();
+
+ protected:
+ QCheckBox *f_olderCB;
+ KIntSpinBox *f_olderDays;
+ QLabel *f_olderDaysL;
+
+ Cleanup *d_ata;
+
+
+ protected slots:
+ void slotFolderCBtoggled(bool b);
+ void slotFolderDaysChanged(int value);
+
+ private:
+ GroupCleanupWidget *mGroupCleanup;
+
+};
+
+//END: Cleanup configuration -------------------------------------------------
+
+
+/*class CacheWidget : public KCModule {
+
+
+ Q_OBJECT
+
+ public:
+ CacheWidget(Cache *d, QWidget *p=0, const char *n=0);
+ ~CacheWidget();
+
+ void apply();
+
+
+ protected:
+ KIntSpinBox *m_emMaxArt,
+ *m_emMaxKB,
+ *d_iskMaxArt,
+ *d_iskMaxKB;
+
+ QLabel *d_iskMaxArtL,
+ *d_iskMaxKBL;
+
+ Cache *d_ata;
+
+
+}; */
+
+
+} //KNConfig
+
+#endif //KNCONFIGWIDGETS_H
diff --git a/knode/knconvert.cpp b/knode/knconvert.cpp
new file mode 100644
index 000000000..40461139b
--- /dev/null
+++ b/knode/knconvert.cpp
@@ -0,0 +1,447 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+#include <qwidgetstack.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+
+#include <klocale.h>
+#include <kfiledialog.h>
+#include <kseparator.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <klineedit.h>
+#include <kprocess.h>
+#include <kapplication.h>
+#include <kpushbutton.h>
+
+#include <kmime_util.h>
+
+#include "knconvert.h"
+#include "resource.h"
+
+
+bool KNConvert::needToConvert(const QString &oldVersion)
+{
+ bool ret=(
+ (oldVersion.left(3)=="0.3") ||
+ (oldVersion.left(3)=="0.4")
+ );
+
+ return ret;
+}
+
+
+KNConvert::KNConvert(const QString &version)
+ : QDialog(0,0,true), l_ogList(0), c_onversionDone(false), v_ersion(version)
+{
+ setCaption(kapp->makeStdCaption(i18n("Conversion")));
+ QVBoxLayout *topL=new QVBoxLayout(this, 5,5);
+ s_tack=new QWidgetStack(this);
+ topL->addWidget(s_tack, 1);
+ topL->addWidget(new KSeparator(this));
+
+ QHBoxLayout *btnL=new QHBoxLayout(topL, 5);
+ s_tartBtn=new QPushButton(i18n("Start Conversion..."), this);
+ s_tartBtn->setDefault(true);
+ btnL->addStretch(1);
+ btnL->addWidget(s_tartBtn);
+ c_ancelBtn=new KPushButton(KStdGuiItem::cancel(), this);
+ btnL->addWidget(c_ancelBtn);
+
+ connect(s_tartBtn, SIGNAL(clicked()), this, SLOT(slotStart()));
+ connect(c_ancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
+
+ w_1=new QWidget(s_tack);
+ s_tack->addWidget(w_1, 1);
+ QGridLayout *w1L=new QGridLayout(w_1, 5,3, 5,5);
+
+ QLabel *l1=new QLabel(i18n(
+"<b>Congratulations, you have upgraded to KNode version %1.</b><br>\
+Unfortunately this version uses a different format for some data-files, so \
+in order to keep your existing data it is necessary to convert it first. This is \
+now done automatically by KNode. If you want to, a backup of your existing data \
+will be created before the conversion starts.").arg(KNODE_VERSION), w_1);
+ w1L->addMultiCellWidget(l1, 0,0, 0,2);
+
+ c_reateBkup=new QCheckBox(i18n("Create backup of old data"), w_1);
+ w1L->addMultiCellWidget(c_reateBkup, 2,2, 0,2);
+ connect(c_reateBkup, SIGNAL(toggled(bool)), this, SLOT(slotCreateBkupToggled(bool)));
+
+ b_ackupPathLabel=new QLabel(i18n("Save backup in:"), w_1);
+ w1L->addWidget(b_ackupPathLabel, 3,0);
+
+ b_ackupPath=new KLineEdit(QDir::homeDirPath()+QString("/knodedata-")+v_ersion+".tar.gz", w_1);
+ w1L->addWidget(b_ackupPath, 3,1);
+
+ b_rowseBtn= new QPushButton(i18n("Browse..."), w_1);
+ connect(b_rowseBtn, SIGNAL(clicked()), this, SLOT(slotBrowse()));
+ w1L->addWidget(b_rowseBtn, 3,2);
+ w1L->setColStretch(1,1);
+ w1L->addRowSpacing(1,15);
+ w1L->setRowStretch(4,1);
+ w1L->addRowSpacing(4,15);
+
+ w_2=new QLabel(s_tack);
+ w_2->setText(i18n("<b>Converting, please wait...</b>"));
+ w_2->setAlignment(AlignCenter);
+ s_tack->addWidget(w_2, 2);
+
+ w_3=new QWidget(s_tack);
+ s_tack->addWidget(w_3, 3);
+ QVBoxLayout *w3L=new QVBoxLayout(w_3, 5,5);
+
+ r_esultLabel=new QLabel(w_3);
+ w3L->addWidget(r_esultLabel);
+ QLabel *l2=new QLabel(i18n("Processed tasks:"), w_3);
+ l_ogList=new QListBox(w_3);
+ w3L->addSpacing(15);
+ w3L->addWidget(l2);
+ w3L->addWidget(l_ogList, 1);
+
+ s_tack->raiseWidget(w_1);
+ slotCreateBkupToggled(false);
+}
+
+
+KNConvert::~KNConvert()
+{
+ for ( QValueList<Converter*>::Iterator it = mConverters.begin(); it != mConverters.end(); ++it )
+ delete (*it);
+}
+
+
+void KNConvert::convert()
+{
+ int errors=0;
+ for ( QValueList<Converter*>::Iterator it = mConverters.begin(); it != mConverters.end(); ++it )
+ if( !(*it)->doConvert() )
+ errors++;
+
+ if(errors>0)
+ r_esultLabel->setText(i18n(
+"<b>Some errors occurred during the conversion.</b>\
+<br>You should now examine the log to find out what went wrong."));
+ else
+ r_esultLabel->setText(i18n(
+"<b>The conversion was successful.</b>\
+<br>Have a lot of fun with this new version of KNode. ;-)"));
+
+ s_tartBtn->setText(i18n("Start KNode"));
+ s_tartBtn->setEnabled(true);
+ c_ancelBtn->setEnabled(true);
+ l_ogList->insertStringList(l_og);
+ s_tack->raiseWidget(w_3);
+
+ c_onversionDone=true;
+}
+
+
+void KNConvert::slotStart()
+{
+ if(c_onversionDone) {
+ accept();
+ return;
+ }
+
+ s_tartBtn->setEnabled(false);
+ c_ancelBtn->setEnabled(false);
+ s_tack->raiseWidget(w_2);
+
+ if(v_ersion.left(3)=="0.3" || v_ersion.left(7)=="0.4beta") {
+ //Version 0.4
+ mConverters.append( new Converter04( &l_og ) );
+ }
+
+ //create backup of old data using "tar"
+ if(c_reateBkup->isChecked()) {
+ if(b_ackupPath->text().isEmpty()) {
+ KMessageBox::error(this, i18n("Please select a valid backup path."));
+ return;
+ }
+
+ QString dataDir=locateLocal("data","knode/");
+ t_ar=new KProcess;
+ *t_ar << "tar";
+ *t_ar << "-cz" << dataDir
+ << "-f" << b_ackupPath->text();
+ connect(t_ar, SIGNAL(processExited(KProcess*)), this, SLOT(slotTarExited(KProcess*)));
+ if(!t_ar->start()) {
+ delete t_ar;
+ t_ar = 0;
+ slotTarExited(0);
+ }
+ }
+ else
+ convert(); //convert files without backup
+}
+
+
+void KNConvert::slotCreateBkupToggled(bool b)
+{
+ b_ackupPathLabel->setEnabled(b);
+ b_ackupPath->setEnabled(b);
+ b_rowseBtn->setEnabled(b);
+}
+
+
+void KNConvert::slotBrowse()
+{
+ QString newPath=KFileDialog::getSaveFileName(b_ackupPath->text());
+
+ if(!newPath.isEmpty())
+ b_ackupPath->setText(newPath);
+}
+
+
+void KNConvert::slotTarExited(KProcess *proc)
+{
+ bool success=true;
+
+ if(!proc || !proc->normalExit() || proc->exitStatus()!=0) {
+ success=false;
+ if(KMessageBox::Cancel==KMessageBox::warningContinueCancel(this, i18n("<b>The backup failed</b>; do you want to continue anyway?"))) {
+
+ delete t_ar;
+ t_ar = 0;
+ reject();
+ return;
+ }
+ }
+
+ delete t_ar;
+ t_ar = 0;
+ if(success)
+ l_og.append(i18n("created backup of the old data-files in %1").arg(b_ackupPath->text()));
+ else
+ l_og.append(i18n("backup failed."));
+
+ // now we actually convert the files
+ convert();
+}
+
+
+
+//============================================================================================
+
+
+
+bool KNConvert::Converter04::doConvert()
+{
+ QString dir=locateLocal("data","knode/")+"folders/";
+ int num;
+ bool error=false;
+
+ //Drafts
+ if(QFile::exists(dir+"folder1.idx")) {
+ num=convertFolder(dir+"folder1", dir+"drafts_1");
+ if(num==-1) {
+ error=true;
+ l_og->append(i18n("conversion of folder \"Drafts\" to version 0.4 failed."));
+ }
+ else {
+ l_og->append(i18n("converted folder \"Drafts\" to version 0.4"));
+ }
+ }
+ else
+ l_og->append(i18n("nothing to be done for folder \"Drafts\""));
+
+ //Outbox
+ if(QFile::exists(dir+"folder2.idx")) {
+ num=convertFolder(dir+"folder2", dir+"outbox_2");
+ if(num==-1) {
+ error=true;
+ l_og->append(i18n("conversion of folder \"Outbox\" to version 0.4 failed."));
+ }
+ else {
+ l_og->append(i18n("converted folder \"Outbox\" to version 0.4"));
+ }
+ }
+ else
+ l_og->append(i18n("nothing to be done for folder \"Outbox\""));
+
+ //Sent
+ if(QFile::exists(dir+"folder3.idx")) {
+ num=convertFolder(dir+"folder3", dir+"sent_3");
+ if(num==-1) {
+ error=true;
+ l_og->append(i18n("conversion of folder \"Sent\" to version 0.4 failed."));
+ }
+ else {
+ l_og->append(i18n("converted folder \"Sent\" to version 0.4"));
+ }
+ }
+ else
+ l_og->append(i18n("nothing to be done for folder \"Sent\""));
+
+ //remove old info-files
+ QFile::remove(dir+"standard.info");
+ QFile::remove(dir+".standard.info");
+ return (!error);
+}
+
+
+int KNConvert::Converter04::convertFolder(QString srcPrefix, QString dstPrefix)
+{
+ QFile srcMBox(srcPrefix+".mbox"),
+ srcIdx(srcPrefix+".idx"),
+ dstMBox(dstPrefix+".mbox"),
+ dstIdx(dstPrefix+".idx");
+ QTextStream ts(&dstMBox);
+ ts.setEncoding(QTextStream::Latin1);
+
+ OldFolderIndex oldIdx;
+ NewFolderIndex newIdx;
+ int lastId=0;
+ bool filesOpen;
+
+ //open files
+ filesOpen=srcMBox.open(IO_ReadOnly);
+ filesOpen=filesOpen && srcIdx.open(IO_ReadOnly);
+
+ if(dstIdx.exists() && dstIdx.size()>0) { //we are converting from 0.4beta*
+ if( (filesOpen=filesOpen && dstIdx.open(IO_ReadOnly)) ) {
+ dstIdx.at( dstIdx.size()-sizeof(NewFolderIndex) ); //set filepointer to last entry
+ dstIdx.readBlock( (char*)(&newIdx), sizeof(NewFolderIndex) );
+ lastId=newIdx.id;
+ dstIdx.close();
+ }
+ }
+
+ filesOpen=filesOpen && dstMBox.open(IO_WriteOnly | IO_Append);
+ filesOpen=filesOpen && dstIdx.open(IO_WriteOnly | IO_Append);
+
+ if(!filesOpen) {
+ srcMBox.close();
+ srcIdx.close();
+ dstMBox.close();
+ dstIdx.close();
+ return -1;
+ }
+
+ //conversion starts here
+ while(!srcIdx.atEnd()) {
+
+ //read index data
+ srcIdx.readBlock( (char*)(&oldIdx), sizeof(OldFolderIndex));
+ newIdx.id=++lastId;
+ newIdx.sId=oldIdx.sId;
+ newIdx.ti=oldIdx.ti;
+
+ switch(oldIdx.status) {
+ case 0: //AStoPost
+ newIdx.flags[0]=false; //doMail()
+ newIdx.flags[1]=false; //mailed()
+ newIdx.flags[2]=true; //doPost()
+ newIdx.flags[3]=false; //posted()
+ newIdx.flags[4]=false; //canceled()
+ newIdx.flags[5]=false; //editDisabled()
+ break;
+
+ case 1: //AStoMail
+ newIdx.flags[0]=true; //doMail()
+ newIdx.flags[1]=false; //mailed()
+ newIdx.flags[2]=false; //doPost()
+ newIdx.flags[3]=false; //posted()
+ newIdx.flags[4]=false; //canceled()
+ newIdx.flags[5]=false; //editDisabled()
+ break;
+
+ case 2: //ASposted
+ newIdx.flags[0]=false; //doMail()
+ newIdx.flags[1]=false; //mailed()
+ newIdx.flags[2]=true; //doPost()
+ newIdx.flags[3]=true; //posted()
+ newIdx.flags[4]=false; //canceled()
+ newIdx.flags[5]=true; //editDisabled()
+ break;
+
+ case 3: //ASmailed
+ newIdx.flags[0]=true; //doMail()
+ newIdx.flags[1]=true; //mailed()
+ newIdx.flags[2]=false; //doPost()
+ newIdx.flags[3]=false; //posted()
+ newIdx.flags[4]=false; //canceled()
+ newIdx.flags[5]=true; //editDisabled()
+ break;
+
+ case 6: //AScanceled
+ newIdx.flags[0]=false; //doMail()
+ newIdx.flags[1]=false; //mailed()
+ newIdx.flags[2]=true; //doPost()
+ newIdx.flags[3]=true; //posted()
+ newIdx.flags[4]=true; //canceled()
+ newIdx.flags[5]=true; //editDisabled()
+ break;
+
+ default: //what the ..
+ newIdx.flags[0]=false; //doMail()
+ newIdx.flags[1]=false; //mailed()
+ newIdx.flags[2]=false; //doPost()
+ newIdx.flags[3]=false; //posted()
+ newIdx.flags[4]=false; //canceled()
+ newIdx.flags[5]=false; //editDisabled()
+ break;
+ }
+
+
+ //read mbox-data
+ unsigned int size=oldIdx.eo-oldIdx.so;
+ QCString buff(size+10);
+ srcMBox.at(oldIdx.so);
+ int readBytes=srcMBox.readBlock(buff.data(), size);
+ buff.at(readBytes)='\0'; //terminate string;
+
+ //remove "X-KNode-Overview"
+ int pos=buff.find('\n');
+ if(pos>-1)
+ buff.remove(0, pos+1);
+
+ //write mbox-data
+ ts << "From aaa@aaa Mon Jan 01 00:00:00 1997\n";
+ newIdx.so=dstMBox.at(); //save start-offset
+ ts << "X-KNode-Overview: ";
+ ts << KMime::extractHeader(buff, "Subject") << '\t';
+ ts << KMime::extractHeader(buff, "Newsgroups") << '\t';
+ ts << KMime::extractHeader(buff, "To") << '\t';
+ ts << KMime::extractHeader(buff, "Lines") << '\n';
+ ts << buff;
+ newIdx.eo=dstMBox.at(); //save end-offset
+ ts << '\n';
+
+ //write index-data
+ dstIdx.writeBlock((char*)(&newIdx), sizeof(NewFolderIndex));
+ }
+
+ //close/remove files and return number of articles in the new folder
+ srcMBox.remove();
+ srcIdx.remove();
+ dstMBox.close();
+ dstIdx.close();
+ return ( dstIdx.size()/sizeof(NewFolderIndex) );
+}
+
+
+
+
+//-----------------------------
+#include "knconvert.moc"
+
+
+
+
+
+
+
diff --git a/knode/knconvert.h b/knode/knconvert.h
new file mode 100644
index 000000000..c75e0da00
--- /dev/null
+++ b/knode/knconvert.h
@@ -0,0 +1,126 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNCONVERT_H
+#define KNCONVERT_H
+
+#include <time.h>
+
+#include <qdialog.h>
+
+#include <qglobal.h>
+#include <qvaluelist.h>
+#include <kdepimmacros.h>
+
+class QListBox;
+class QLabel;
+class QWidgetStack;
+class QCheckBox;
+
+class KLineEdit;
+class KProcess;
+
+
+class KDE_EXPORT KNConvert : public QDialog {
+
+ Q_OBJECT
+
+ public:
+ static bool needToConvert(const QString &oldVersion);
+
+ KNConvert(const QString &version);
+ ~KNConvert();
+ bool conversionDone()const { return c_onversionDone; }
+
+
+ protected:
+
+ //------------ <Converter-classes> ---------------
+
+ //Base class for all converters
+ class Converter {
+
+ public:
+ Converter(QStringList *log) { l_og=log; }
+ virtual ~Converter() {}
+ virtual bool doConvert()=0;
+
+ protected:
+ QStringList *l_og;
+ };
+
+
+ //Converter for version 0.4
+ class Converter04 : public Converter {
+
+ public:
+ Converter04(QStringList *log) : Converter(log) {}
+ ~Converter04() {}
+ bool doConvert();
+
+ protected:
+ int convertFolder(QString srcPrefix, QString dstPrefix);
+
+ struct OldFolderIndex {
+ int id,
+ status,
+ so,
+ eo,
+ sId;
+ time_t ti;
+ };
+
+ struct NewFolderIndex {
+ int id,
+ so,
+ eo,
+ sId;
+ time_t ti;
+ bool flags[6];
+ };
+ };
+
+ //------------ </Converter-classes> --------------
+
+ QWidgetStack *s_tack;
+ QWidget *w_1,
+ *w_3;
+ QCheckBox *c_reateBkup;
+ QLabel *b_ackupPathLabel,
+ *w_2,
+ *r_esultLabel;
+ KLineEdit *b_ackupPath;
+ QPushButton *b_rowseBtn,
+ *s_tartBtn,
+ *c_ancelBtn;
+ QListBox *l_ogList;
+
+ QValueList<Converter*> mConverters;
+ QStringList l_og;
+ bool c_onversionDone;
+ QString v_ersion;
+ KProcess *t_ar;
+
+ void convert();
+
+ protected slots:
+ void slotStart();
+ void slotCreateBkupToggled(bool b);
+ void slotBrowse();
+ void slotTarExited(KProcess *proc);
+
+};
+
+
+#endif // KNCONVERT_H
diff --git a/knode/kndisplayedheader.cpp b/knode/kndisplayedheader.cpp
new file mode 100644
index 000000000..589bd0027
--- /dev/null
+++ b/knode/kndisplayedheader.cpp
@@ -0,0 +1,175 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <klocale.h>
+
+#include "kndisplayedheader.h"
+
+
+// some standard headers
+static const char *predef[] = { "Approved","Content-Transfer-Encoding","Content-Type","Control","Date","Distribution",
+ "Expires","Followup-To","From","Lines","Mail-Copies-To","Message-ID","Mime-Version","NNTP-Posting-Host",
+ "Newsgroups","Organization","Path","References","Reply-To", "Sender","Subject",
+ "Supersedes","To", "User-Agent","X-Mailer","X-Newsreader","X-No-Archive","XRef",0 };
+
+// default display names KNode uses
+static const char *disp[] = { "Groups", 0 };
+
+void dummyHeader()
+{
+ i18n("collection of article headers","Approved");
+ i18n("collection of article headers","Content-Transfer-Encoding");
+ i18n("collection of article headers","Content-Type");
+ i18n("collection of article headers","Control");
+ i18n("collection of article headers","Date");
+ i18n("collection of article headers","Distribution");
+ i18n("collection of article headers","Expires");
+ i18n("collection of article headers","Followup-To");
+ i18n("collection of article headers","From");
+ i18n("collection of article headers","Lines");
+ i18n("collection of article headers","Mail-Copies-To");
+ i18n("collection of article headers","Message-ID");
+ i18n("collection of article headers","Mime-Version");
+ i18n("collection of article headers","NNTP-Posting-Host");
+ i18n("collection of article headers","Newsgroups");
+ i18n("collection of article headers","Organization");
+ i18n("collection of article headers","Path");
+ i18n("collection of article headers","References");
+ i18n("collection of article headers","Reply-To");
+ i18n("collection of article headers","Sender");
+ i18n("collection of article headers","Subject");
+ i18n("collection of article headers","Supersedes");
+ i18n("collection of article headers","To");
+ i18n("collection of article headers","User-Agent");
+ i18n("collection of article headers","X-Mailer");
+ i18n("collection of article headers","X-Newsreader");
+ i18n("collection of article headers","X-No-Archive");
+ i18n("collection of article headers","XRef");
+
+ i18n("collection of article headers","Groups");
+}
+
+
+//=============================================================================================================
+
+
+KNDisplayedHeader::KNDisplayedHeader()
+ : t_ranslateName(true)
+{
+ f_lags.fill(false, 8);
+ f_lags[1] = true; // header name bold by default
+}
+
+
+KNDisplayedHeader::~KNDisplayedHeader()
+{
+}
+
+
+// some common headers
+const char** KNDisplayedHeader::predefs()
+{
+ return predef;
+}
+
+
+// *tries* to translate the name
+QString KNDisplayedHeader::translatedName()
+{
+ if (t_ranslateName) {
+ // major hack alert !!!
+ if (!n_ame.isEmpty()) {
+ if (i18n("collection of article headers",n_ame.local8Bit())!=n_ame.local8Bit().data()) // try to guess if this english or not
+ return i18n("collection of article headers",n_ame.local8Bit());
+ else
+ return n_ame;
+ } else
+ return QString::null;
+ } else
+ return n_ame;
+}
+
+
+// *tries* to retranslate the name to english
+void KNDisplayedHeader::setTranslatedName(const QString &s)
+{
+ bool retranslated = false;
+ for (const char **c=predef;(*c)!=0;c++) { // ok, first the standard header names
+ if (s==i18n("collection of article headers",*c)) {
+ n_ame = QString::fromLatin1(*c);
+ retranslated = true;
+ break;
+ }
+ }
+
+ if (!retranslated) {
+ for (const char **c=disp;(*c)!=0;c++) // now our standard display names
+ if (s==i18n("collection of article headers",*c)) {
+ n_ame = QString::fromLatin1(*c);
+ retranslated = true;
+ break;
+ }
+ }
+
+ if (!retranslated) { // ok, we give up and store the maybe non-english string
+ n_ame = s;
+ t_ranslateName = false; // and don't try to translate it, so a german user *can* use the original english name
+ } else
+ t_ranslateName = true;
+}
+
+
+void KNDisplayedHeader::createTags()
+{
+ const char *tokens[] = { "<big>","</big>","<b>","</b>",
+ "<i>","</i>","<u>","</u>" };
+
+ for(int i=0; i<4; i++) t_ags[i]=QString::null;
+
+ if(f_lags.at(0)) { // <big>
+ t_ags[0]=tokens[0];
+ t_ags[1]=tokens[1];
+ }
+ if(f_lags.at(4)) {
+ t_ags[2]=tokens[0];
+ t_ags[3]=tokens[1];
+ }
+
+ if(f_lags.at(1)) { // <b>
+ t_ags[0]+=(tokens[2]);
+ t_ags[1].prepend(tokens[3]);
+ }
+ if(f_lags.at(5)) {
+ t_ags[2]+=tokens[2];
+ t_ags[3].prepend(tokens[3]);
+ }
+
+ if(f_lags.at(2)) { // <i>
+ t_ags[0]+=tokens[4];
+ t_ags[1].prepend(tokens[5]);
+ }
+ if(f_lags.at(6)) {
+ t_ags[2]+=tokens[4];
+ t_ags[3].prepend(tokens[5]);
+ }
+
+ if(f_lags.at(3)) { // <u>
+ t_ags[0]+=tokens[6];
+ t_ags[1].prepend(tokens[7]);
+ }
+ if(f_lags.at(7)) {
+ t_ags[2]+=tokens[6];
+ t_ags[3].prepend(tokens[7]);
+ }
+}
diff --git a/knode/kndisplayedheader.h b/knode/kndisplayedheader.h
new file mode 100644
index 000000000..84f260e58
--- /dev/null
+++ b/knode/kndisplayedheader.h
@@ -0,0 +1,63 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNDISPLAYEDHEADER_H
+#define KNDISPLAYEDHEADER_H
+
+#include <qbitarray.h>
+
+
+class KNDisplayedHeader {
+
+ public:
+ KNDisplayedHeader();
+ ~KNDisplayedHeader();
+
+ //some common headers
+ static const char** predefs();
+
+ //name
+ const QString& name() { return n_ame; }
+ void setName(const QString &s) { n_ame = s; }
+ bool hasName() const { return !n_ame.isEmpty(); }
+
+ //translated name
+ QString translatedName(); // *tries* to translate the name
+ void setTranslatedName(const QString &s); // *tries* to retranslate the name to english
+ void setTranslateName(bool b) { t_ranslateName=b; }
+ bool translateName() const { return t_ranslateName; }
+
+ //header
+ const QString& header() { return h_eader; }
+ void setHeader(const QString &s) { h_eader = s; }
+
+ //flags
+ bool flag(int i) { return f_lags.at(i); }
+ void setFlag(int i, bool b) { f_lags.setBit(i, b); }
+
+ //HTML-tags
+ void createTags();
+ const QString& nameOpenTag() { return t_ags[0]; }
+ const QString& nameCloseTag() { return t_ags[1]; }
+ const QString& headerOpenTag() { return t_ags[2]; }
+ const QString& headerCloseTag() { return t_ags[3]; }
+
+ protected:
+ bool t_ranslateName;
+ QString n_ame, h_eader, t_ags[4];
+ QBitArray f_lags;
+
+};
+
+#endif
diff --git a/knode/knewsservice.protocol b/knode/knewsservice.protocol
new file mode 100644
index 000000000..6adc5e958
--- /dev/null
+++ b/knode/knewsservice.protocol
@@ -0,0 +1,13 @@
+[Protocol]
+exec=knode %u
+protocol=news
+input=none
+output=none
+helper=true
+listing=
+reading=false
+writing=false
+makedir=false
+deleting=false
+DocPath=kioslave/news.html
+Icon=knode
diff --git a/knode/knfilterconfigwidget.cpp b/knode/knfilterconfigwidget.cpp
new file mode 100644
index 000000000..2a7a8cc47
--- /dev/null
+++ b/knode/knfilterconfigwidget.cpp
@@ -0,0 +1,93 @@
+/*
+ knfilterconfigwidget.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+#include <qlabel.h>
+
+#include <klocale.h>
+
+#include "knstringfilter.h"
+#include "knstatusfilter.h"
+#include "knrangefilter.h"
+#include "knfilterconfigwidget.h"
+
+
+KNFilterConfigWidget::KNFilterConfigWidget(QWidget *parent, const char *name ) :
+ QTabWidget(parent,name)
+{
+ QWidget *sf, *idW, *add;
+ sf=new QWidget(this);
+ QVBoxLayout *sfL=new QVBoxLayout(sf, 8,5);
+ subject=new KNStringFilterWidget(i18n("Subject"), sf);
+ sfL->addWidget(subject);
+ from=new KNStringFilterWidget(i18n("From"), sf);
+ sfL->addWidget(from);
+ QLabel *l = new QLabel(i18n("The following placeholders are supported:\n%MYNAME=own name, %MYEMAIL=own email address"),sf);
+ sfL->addWidget(l);
+ sfL->addStretch(1);
+ addTab(sf, i18n("Subject && &From"));
+
+ idW=new QWidget(this);
+ QVBoxLayout *idL=new QVBoxLayout(idW, 8,5);
+ messageId=new KNStringFilterWidget(i18n("Message-ID"), idW);
+ idL->addWidget(messageId);
+ references=new KNStringFilterWidget(i18n("References"), idW);
+ idL->addWidget(references);
+ idL->addStretch(1);
+ addTab(idW, i18n("M&essage-IDs"));
+
+ status=new KNStatusFilterWidget(this);
+ addTab(status, i18n("&Status"));
+
+ add=new QWidget(this);
+ QVBoxLayout *addL=new QVBoxLayout(add, 8,5);
+ score=new KNRangeFilterWidget(i18n("Score"), -99999, 99999, add);
+ addL->addWidget(score);
+ age=new KNRangeFilterWidget(i18n("Age"), 0, 999, add, i18n(" days"));
+ addL->addWidget(age);
+ lines=new KNRangeFilterWidget(i18n("Lines"), 0, 99999, add);
+ addL->addWidget(lines);
+ addL->addStretch(1);
+ addTab(add, i18n("&Additional"));
+}
+
+
+KNFilterConfigWidget::~KNFilterConfigWidget()
+{
+}
+
+
+void KNFilterConfigWidget::reset()
+{
+ from->clear();
+ subject->clear();
+ messageId->clear();
+ references->clear();
+ age->clear();
+ lines->clear();
+ score->clear();
+ status->clear();
+}
+
+
+void KNFilterConfigWidget::setStartFocus()
+{
+ subject->setStartFocus();
+}
+
+// -----------------------------------------------------------------------------
+
+#include "knfilterconfigwidget.moc"
diff --git a/knode/knfilterconfigwidget.h b/knode/knfilterconfigwidget.h
new file mode 100644
index 000000000..7cafa18db
--- /dev/null
+++ b/knode/knfilterconfigwidget.h
@@ -0,0 +1,53 @@
+/*
+ knfilterconfigwidget.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNFILTERCONFIGWIDGET_H
+#define KNFILTERCONFIGWIDGET_H
+
+#include <qtabwidget.h>
+
+class KNStatusFilterWidget;
+class KNStringFilterWidget;
+class KNRangeFilterWidget;
+
+
+class KNFilterConfigWidget : public QTabWidget {
+
+ Q_OBJECT
+
+ friend class KNFilterDialog;
+ friend class KNSearchDialog;
+
+ public:
+ KNFilterConfigWidget(QWidget *parent=0, const char *name=0);
+ ~KNFilterConfigWidget();
+
+ void reset();
+
+ void setStartFocus(); // useablity hack for the search dialog
+
+ protected:
+ KNStatusFilterWidget *status;
+ KNStringFilterWidget *subject;
+ KNStringFilterWidget *from;
+ KNStringFilterWidget *messageId;
+ KNStringFilterWidget *references;
+ KNRangeFilterWidget *age;
+ KNRangeFilterWidget *lines;
+ KNRangeFilterWidget *score;
+};
+
+#endif
diff --git a/knode/knfilterdialog.cpp b/knode/knfilterdialog.cpp
new file mode 100644
index 000000000..e363e6ad0
--- /dev/null
+++ b/knode/knfilterdialog.cpp
@@ -0,0 +1,125 @@
+/*
+ knfilterdialog.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <klineedit.h>
+
+#include "knglobals.h"
+#include "knfiltermanager.h"
+#include "knfilterconfigwidget.h"
+#include "knarticlefilter.h"
+#include "utilities.h"
+#include "knfilterdialog.h"
+
+
+KNFilterDialog::KNFilterDialog(KNArticleFilter *f, QWidget *parent, const char *name)
+ : KDialogBase(Plain, (f->id()==-1)? i18n("New Filter"):i18n("Properties of %1").arg(f->name()),
+ Ok|Cancel|Help, Ok, parent, name),
+ fltr(f)
+{
+ QFrame* page=plainPage();
+
+ QGroupBox *gb=new QGroupBox(page);
+ fname=new KLineEdit(gb);
+ QLabel *l1=new QLabel(fname, i18n("Na&me:"), gb);
+ apon=new QComboBox(gb);
+ apon->insertItem(i18n("Single Articles"));
+ apon->insertItem(i18n("Whole Threads"));
+ QLabel *l2=new QLabel(apon, i18n("Apply o&n:"), gb);
+ enabled=new QCheckBox(i18n("Sho&w in menu"), gb);
+
+ fw=new KNFilterConfigWidget(page);
+
+ QGridLayout *gbL=new QGridLayout(gb, 2,4,8,5);
+ gbL->addWidget(l1, 0,0);
+ gbL->addMultiCellWidget(fname, 0,0,1,3);
+ gbL->addWidget(enabled, 1,0);
+ gbL->addWidget(l2, 1,2);
+ gbL->addWidget(apon, 1,3);
+ gbL->setColStretch(1,1);
+
+ QVBoxLayout *topL=new QVBoxLayout(page,0,5);
+
+ topL->addWidget(gb);
+ topL->addWidget(fw,1);
+
+ enabled->setChecked(f->isEnabled());
+ apon->setCurrentItem((int) f->applyOn());
+ fname->setText(f->translatedName());
+
+ fw->status->setFilter(f->status);
+ fw->lines->setFilter(f->lines);
+ fw->age->setFilter(f->age);
+ fw->score->setFilter(f->score);
+ fw->subject->setFilter(f->subject);
+ fw->from->setFilter(f->from);
+ fw->messageId->setFilter(f->messageId);
+ fw->references->setFilter(f->references);
+
+ setFixedHeight(sizeHint().height());
+ KNHelper::restoreWindowSize("filterDLG", this, sizeHint());
+
+ setHelp("anc-using-filters");
+ connect( fname, SIGNAL( textChanged ( const QString & )), this, SLOT( slotTextChanged( const QString & )));
+ slotTextChanged( fname->text() );
+}
+
+
+
+KNFilterDialog::~KNFilterDialog()
+{
+ KNHelper::saveWindowSize("filterDLG", size());
+}
+
+void KNFilterDialog::slotTextChanged( const QString &_text )
+{
+ enableButtonOK( !_text.isEmpty() );
+}
+
+void KNFilterDialog::slotOk()
+{
+ if (fname->text().isEmpty())
+ KMessageBox::sorry(this, i18n("Please provide a name for this filter."));
+ else
+ if (!knGlobals.filterManager()->newNameIsOK(fltr,fname->text()))
+ KMessageBox::sorry(this, i18n("A filter with this name exists already.\nPlease choose a different name."));
+ else {
+ fltr->setTranslatedName(fname->text());
+ fltr->setEnabled(enabled->isChecked());
+ fltr->status=fw->status->filter();
+ fltr->score=fw->score->filter();
+ fltr->age=fw->age->filter();
+ fltr->lines=fw->lines->filter();
+ fltr->subject=fw->subject->filter();
+ fltr->from=fw->from->filter();
+ fltr->messageId=fw->messageId->filter();
+ fltr->references=fw->references->filter();
+ fltr->setApplyOn(apon->currentItem());
+
+ accept();
+ }
+}
+
+
+
+//--------------------------------
+
+#include "knfilterdialog.moc"
diff --git a/knode/knfilterdialog.h b/knode/knfilterdialog.h
new file mode 100644
index 000000000..a7dcbfebf
--- /dev/null
+++ b/knode/knfilterdialog.h
@@ -0,0 +1,54 @@
+/*
+ knfilterdialog.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNFILTERDIALOG_H
+#define KNFILTERDIALOG_H
+
+#include <kdialogbase.h>
+
+class KNFilterConfigWidget;
+class KNArticleFilter;
+class KLineEdit;
+class QComboBox;
+class QCheckBox;
+
+
+class KNFilterDialog : public KDialogBase {
+
+ Q_OBJECT
+
+ friend class KNFilterManager;
+
+ public:
+ KNFilterDialog(KNArticleFilter *f=0, QWidget *parent=0, const char *name=0);
+ ~KNFilterDialog();
+
+ KNArticleFilter* filter() { return fltr; }
+
+ protected:
+ KNFilterConfigWidget *fw;
+ KLineEdit *fname;
+ QComboBox *apon;
+ QCheckBox *enabled;
+
+ KNArticleFilter *fltr;
+
+ protected slots:
+ void slotOk();
+ void slotTextChanged( const QString & );
+};
+
+#endif
diff --git a/knode/knfiltermanager.cpp b/knode/knfiltermanager.cpp
new file mode 100644
index 000000000..ce8469512
--- /dev/null
+++ b/knode/knfiltermanager.cpp
@@ -0,0 +1,396 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <stdlib.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kstandarddirs.h>
+#include <ksimpleconfig.h>
+
+#include "utilities.h"
+#include "knglobals.h"
+#include "knarticlefilter.h"
+#include "knfilterdialog.h"
+#include "knfiltermanager.h"
+#include "knconfig.h"
+#include "knconfigwidgets.h"
+
+
+KNFilterSelectAction::KNFilterSelectAction( const QString& text, const QString& pix,
+ QObject* parent, const char *name )
+ : KActionMenu(text,pix,parent,name), currentItem(-42)
+{
+ popupMenu()->setCheckable(true);
+ connect(popupMenu(),SIGNAL(activated(int)),this,SLOT(slotMenuActivated(int)));
+ setDelayed(false);
+}
+
+
+
+KNFilterSelectAction::~KNFilterSelectAction()
+{
+}
+
+void KNFilterSelectAction::setCurrentItem(int id)
+{
+ popupMenu()->setItemChecked(currentItem, false);
+ popupMenu()->setItemChecked(id, true);
+ currentItem = id;
+}
+
+
+void KNFilterSelectAction::slotMenuActivated(int id)
+{
+ setCurrentItem(id);
+ emit(activated(id));
+}
+
+
+//==============================================================================
+
+KNFilterManager::KNFilterManager(QObject * parent, const char * name)
+ : QObject(parent,name), fset(0), currFilter(0), a_ctFilter(0)
+{
+ loadFilters();
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("READNEWS");
+ setFilter(conf->readNumEntry("lastFilterID", 1));
+}
+
+
+
+KNFilterManager::~KNFilterManager()
+{
+ for ( QValueList<KNArticleFilter*>::Iterator it = mFilterList.begin(); it != mFilterList.end(); ++it )
+ delete (*it);
+}
+
+
+
+void KNFilterManager::readOptions()
+{
+}
+
+
+
+void KNFilterManager::saveOptions()
+{
+}
+
+
+void KNFilterManager::prepareShutdown()
+{
+ if (currFilter) {
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("READNEWS");
+ conf->writeEntry("lastFilterID", currFilter->id());
+ }
+}
+
+
+void KNFilterManager::loadFilters()
+{
+ QString fname(locate("data","knode/filters/filters.rc") );
+
+ if (!fname.isNull()) {
+ KSimpleConfig conf(fname,true);
+
+ QValueList<int> activeFilters = conf.readIntListEntry("Active");
+ menuOrder = conf.readIntListEntry("Menu");
+
+ QValueList<int>::Iterator it = activeFilters.begin();
+ while (it != activeFilters.end()) {
+ KNArticleFilter *f=new KNArticleFilter((*it));
+ if (f->loadInfo())
+ addFilter(f);
+ else
+ delete f;
+ it++;
+ }
+ }
+ updateMenu();
+}
+
+
+
+void KNFilterManager::saveFilterLists()
+{
+ QString dir(locateLocal("data","knode/")+"filters/");
+
+ if (dir.isNull()) {
+ KNHelper::displayInternalFileError();
+ return;
+ }
+ KSimpleConfig conf(dir+"filters.rc");
+ QValueList<int> activeFilters;
+ for ( QValueList<KNArticleFilter*>::Iterator it = mFilterList.begin(); it != mFilterList.end(); ++it )
+ activeFilters << (*it)->id();
+
+ conf.writeEntry("Active",activeFilters);
+ conf.writeEntry("Menu",menuOrder);
+}
+
+
+
+void KNFilterManager::startConfig(KNConfig::FilterListWidget *fs)
+{
+ fset=fs;
+ commitNeeded = false;
+
+ for ( QValueList<KNArticleFilter*>::Iterator it = mFilterList.begin(); it != mFilterList.end(); ++it )
+ fset->addItem( (*it) );
+
+ QValueList<int>::Iterator it = menuOrder.begin();
+ while (it != menuOrder.end()) {
+ if ((*it)!=-1)
+ fset->addMenuItem(byID((*it)));
+ else
+ fset->addMenuItem(0);
+ ++it;
+ }
+}
+
+
+
+void KNFilterManager::endConfig()
+{
+ fset=0;
+}
+
+
+
+void KNFilterManager::commitChanges()
+{
+ menuOrder = fset->menuOrder();
+ saveFilterLists();
+
+ if(currFilter)
+ if(!currFilter->isEnabled()) currFilter=0;
+
+ updateMenu();
+
+ if (commitNeeded)
+ emit filterChanged(currFilter);
+}
+
+
+
+void KNFilterManager::newFilter()
+{
+ KNArticleFilter *f=new KNArticleFilter();
+ editFilter(f);
+}
+
+
+
+void KNFilterManager::addFilter(KNArticleFilter *f)
+{
+ if ( f->id() == -1 ) { // new filter, find suitable ID
+ QValueList<int> activeFilters;
+ // ok, this is a ugly hack: we want to reuse old id's, so we try to find the first unused id
+ for ( QValueList<KNArticleFilter*>::Iterator it = mFilterList.begin(); it != mFilterList.end(); ++it )
+ activeFilters << (*it)->id();
+ int newId = 1;
+ while ( activeFilters.contains( newId ) > 0 )
+ newId++;
+ f->setId( newId );
+ }
+ mFilterList.append( f );
+}
+
+
+
+void KNFilterManager::editFilter(KNArticleFilter *f)
+{
+ if (!f->loaded() && f->id()!=-1)
+ f->load();
+
+ KNFilterDialog *fdlg=new KNFilterDialog(f,(fset)? fset:knGlobals.topWidget);
+
+ if (fdlg->exec()) {
+ commitNeeded = true;
+ if(f->id()==-1) { // new filter
+ addFilter(f);
+ f->setLoaded(true);
+ if(fset) { // updating settings tab
+ fset->addItem(f);
+ if(f->isEnabled())
+ fset->addMenuItem(f);
+ }
+ } else {
+ if(fset) { // updating settings tab
+ if(f->isEnabled())
+ fset->addMenuItem(f);
+ else
+ fset->removeMenuItem(f);
+ fset->updateItem(f);
+ }
+ }
+ f->save();
+ } else {
+ if(f->id()==-1) // new filter
+ delete f;
+ }
+
+ delete fdlg;
+}
+
+
+void KNFilterManager::copyFilter(KNArticleFilter *f)
+{
+ if (!f->loaded())
+ f->load();
+ KNArticleFilter *newf=new KNArticleFilter(*f);
+ editFilter(newf);
+}
+
+
+void KNFilterManager::deleteFilter(KNArticleFilter *f)
+{
+ if ( KMessageBox::warningContinueCancel( fset ? fset : knGlobals.topWidget,
+ i18n("Do you really want to delete this filter?"), QString::null, KGuiItem( i18n("&Delete"), "editdelete" ) )
+ == KMessageBox::Continue ) {
+ if ( mFilterList.remove( f ) ) { // does not delete surplus config files
+ if ( fset ) { // we reuse ids to reduce the number of dead files
+ fset->removeItem( f );
+ fset->removeMenuItem( f );
+ }
+ if ( currFilter == f ) {
+ currFilter = 0;
+ emit filterChanged( currFilter );
+ }
+ }
+ }
+}
+
+
+bool KNFilterManager::newNameIsOK(KNArticleFilter *f, const QString &newName)
+{
+ for ( QValueList<KNArticleFilter*>::Iterator it = mFilterList.begin(); it != mFilterList.end(); ++it )
+ if ( (*it) != f && newName == (*it)->translatedName() )
+ return false;
+
+ return true;
+}
+
+
+
+KNArticleFilter* KNFilterManager::setFilter(const int id)
+{
+ KNArticleFilter *bak=currFilter;
+
+ currFilter=byID(id);
+
+ if(currFilter) {
+ if(a_ctFilter)
+ a_ctFilter->setCurrentItem(currFilter->id());
+ emit(filterChanged(currFilter));
+ } else
+ currFilter=bak;
+
+ return currFilter;
+}
+
+
+
+KNArticleFilter* KNFilterManager::byID(int id)
+{
+ for ( QValueList<KNArticleFilter*>::Iterator it = mFilterList.begin(); it != mFilterList.end(); ++it )
+ if ( (*it)->id() == id )
+ return (*it);
+
+ return 0;
+}
+
+
+
+void KNFilterManager::updateMenu()
+{
+ if(!a_ctFilter)
+ return;
+
+ a_ctFilter->popupMenu()->clear();
+ KNArticleFilter *f=0;
+
+ QValueList<int>::Iterator it = menuOrder.begin();
+ while (it != menuOrder.end()) {
+ if ((*it)!=-1) {
+ if ((f=byID((*it))))
+ a_ctFilter->popupMenu()->insertItem(f->translatedName(), f->id());
+ }
+ else a_ctFilter->popupMenu()->insertSeparator();
+ ++it;
+ }
+
+ if(currFilter)
+ a_ctFilter->setCurrentItem(currFilter->id());
+}
+
+
+
+void KNFilterManager::slotMenuActivated(int id)
+{
+ KNArticleFilter *f=setFilter(id);
+
+ if (!f)
+ KMessageBox::error(knGlobals.topWidget, i18n("ERROR: no such filter."));
+}
+
+
+void KNFilterManager::slotShowFilterChooser()
+{
+ KNArticleFilter *f=0;
+ QStringList items;
+ QValueList<int> ids;
+
+ QValueList<int>::Iterator it = menuOrder.begin();
+ while (it != menuOrder.end()) {
+ if ((*it)!=-1)
+ if ((f=byID((*it)))) {
+ items.append(f->translatedName());
+ ids.append(*it);
+ }
+ ++it;
+ }
+
+ int currentItem=0;
+ if (currFilter)
+ currentItem=ids.findIndex(currFilter->id());
+ if (currentItem==-1)
+ currentItem=0;
+
+ int newFilter = KNHelper::selectDialog(knGlobals.topWidget, i18n("Select Filter"), items, currentItem);
+ if (newFilter != -1)
+ setFilter(ids[newFilter]);
+}
+
+
+void KNFilterManager::setMenuAction(KNFilterSelectAction *a, KAction *keybA)
+{
+ if(a) {
+ a_ctFilter = a;
+ connect(a_ctFilter, SIGNAL(activated(int)), this, SLOT(slotMenuActivated(int)));
+ }
+ if(keybA)
+ connect(keybA, SIGNAL(activated()), this, SLOT(slotShowFilterChooser()));
+
+ updateMenu();
+}
+
+//--------------------------------
+
+#include "knfiltermanager.moc"
diff --git a/knode/knfiltermanager.h b/knode/knfiltermanager.h
new file mode 100644
index 000000000..ca76bcb75
--- /dev/null
+++ b/knode/knfiltermanager.h
@@ -0,0 +1,106 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNFILTERMANAGER_H
+#define KNFILTERMANAGER_H
+
+#include <qglobal.h>
+#include <qvaluelist.h>
+
+#include <kaction.h>
+
+namespace KNConfig {
+class FilterListWidget;
+}
+
+class KNArticleFilter;
+class KNFilterDialog;
+
+
+class KNFilterSelectAction : public KActionMenu
+{
+ Q_OBJECT
+
+ public:
+ KNFilterSelectAction( const QString& text, const QString& pix,
+ QObject* parent, const char *name );
+ ~KNFilterSelectAction();
+
+ void setCurrentItem(int id);
+
+ protected slots:
+ void slotMenuActivated(int id);
+
+ signals:
+ void activated(int id);
+
+ private:
+ int currentItem;
+};
+
+
+class KNFilterManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ KNFilterManager(QObject * parent = 0, const char * name = 0);
+ ~KNFilterManager();
+
+ void readOptions();
+ void saveOptions();
+
+ void prepareShutdown();
+
+ KNArticleFilter* currentFilter() { return currFilter; }
+
+ void startConfig(KNConfig::FilterListWidget *fs);
+ void endConfig();
+ void commitChanges();
+ void newFilter();
+ void editFilter(KNArticleFilter *f);
+ void copyFilter(KNArticleFilter *f);
+ void addFilter(KNArticleFilter *f);
+ void deleteFilter(KNArticleFilter *f);
+ bool newNameIsOK(KNArticleFilter *f, const QString &newName);
+
+ // Allow to delay the setup of UI elements, since the knode part may not
+ // be available when the config dialog is called
+ void setMenuAction(KNFilterSelectAction *a, KAction *keybA);
+
+ protected:
+ void loadFilters();
+ void saveFilterLists();
+ KNArticleFilter* setFilter(const int id);
+ KNArticleFilter* byID(int id);
+ void updateMenu();
+
+ QValueList<KNArticleFilter*> mFilterList;
+ KNConfig::FilterListWidget *fset;
+ KNArticleFilter *currFilter;
+ KNFilterSelectAction *a_ctFilter;
+ QValueList<int> menuOrder;
+ bool commitNeeded;
+
+ protected slots:
+ void slotMenuActivated(int id);
+ void slotShowFilterChooser();
+
+ signals:
+ void filterChanged(KNArticleFilter *f);
+
+};
+
+#endif
+
diff --git a/knode/knfolder.cpp b/knode/knfolder.cpp
new file mode 100644
index 000000000..9cdb2f054
--- /dev/null
+++ b/knode/knfolder.cpp
@@ -0,0 +1,601 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qfileinfo.h>
+
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kqcstringsplitter.h>
+
+#include "articlewidget.h"
+#include "knarticlemanager.h"
+#include "kncollectionviewitem.h"
+#include "knhdrviewitem.h"
+#include "utilities.h"
+#include "knglobals.h"
+#include "knarticlefactory.h"
+#include "knfolder.h"
+#include "knarticlewindow.h"
+#include "knmainwidget.h"
+
+using namespace KNode;
+
+
+KNFolder::KNFolder()
+ : KNArticleCollection(0), i_d(-1), p_arentId(-1), i_ndexDirty(false), w_asOpen(true)
+{
+}
+
+
+KNFolder::KNFolder(int id, const QString &name, KNFolder *parent)
+ : KNArticleCollection(parent), i_d(id), i_ndexDirty(false), w_asOpen(true)
+{
+ QString fname=path()+QString("custom_%1").arg(i_d);
+
+ n_ame = name;
+ m_boxFile.setName(fname+".mbox");
+ i_ndexFile.setName(fname+".idx");
+ i_nfoPath=fname+".info";
+
+ p_arentId=parent?parent->id():-1;
+
+ if(i_ndexFile.exists())
+ c_ount=i_ndexFile.size()/sizeof(DynData);
+ else
+ c_ount=0;
+}
+
+
+KNFolder::KNFolder(int id, const QString &name, const QString &prefix, KNFolder *parent)
+ : KNArticleCollection(parent), i_d(id), i_ndexDirty(false), w_asOpen(true)
+{
+ QString fname=path()+QString("%1_%2").arg(prefix).arg(i_d);
+
+ n_ame = name;
+ m_boxFile.setName(fname+".mbox");
+ i_ndexFile.setName(fname+".idx");
+ i_nfoPath=fname+".info";
+
+ p_arentId=parent?parent->id():-1;
+
+ if(i_ndexFile.exists())
+ c_ount=i_ndexFile.size()/sizeof(DynData);
+ else
+ c_ount=0;
+}
+
+
+KNFolder::~KNFolder()
+{
+ closeFiles();
+}
+
+
+void KNFolder::updateListItem()
+{
+ if(l_istItem) {
+ l_istItem->setText(0, n_ame);
+ if (!isRootFolder())
+ l_istItem->setTotalCount( c_ount );
+ }
+}
+
+
+QString KNFolder::path()
+{
+ QString dir(locateLocal("data","knode/")+"folders/");
+ /*if (dir.isNull())
+ KNHelper::displayInternalFileError();*/
+ return dir;
+}
+
+
+bool KNFolder::readInfo(const QString &infoPath)
+{
+ if(infoPath.isEmpty())
+ return false;
+
+ i_nfoPath=infoPath;
+
+ KSimpleConfig info(i_nfoPath);
+ if (!isRootFolder() && !isStandardFolder()) {
+ n_ame=info.readEntry("name");
+ i_d=info.readNumEntry("id", -1);
+ p_arentId=info.readNumEntry("parentId", -1);
+ }
+ w_asOpen=info.readBoolEntry("wasOpen", true);
+
+ if(i_d>-1) {
+ QFileInfo fi(infoPath);
+ QString fname=fi.dirPath(true)+"/"+fi.baseName();
+ closeFiles();
+ clear();
+
+ m_boxFile.setName(fname+".mbox");
+ i_ndexFile.setName(fname+".idx");
+ c_ount=i_ndexFile.exists() ? (i_ndexFile.size()/sizeof(DynData)) : 0;
+ }
+
+ return (i_d!=-1);
+}
+
+
+bool KNFolder::readInfo()
+{
+ return readInfo(i_nfoPath);
+}
+
+
+void KNFolder::saveInfo()
+{
+ if(!i_nfoPath.isEmpty()) {
+ KSimpleConfig info(i_nfoPath);
+ if (!isRootFolder() && !isStandardFolder()) {
+ info.writeEntry("name", n_ame);
+ info.writeEntry("id", i_d);
+ info.writeEntry("parentId", p_arentId);
+ }
+ if(l_istItem)
+ info.writeEntry("wasOpen", l_istItem->isOpen());
+ }
+}
+
+
+void KNFolder::setParent(KNCollection *p)
+{
+ p_arent = p;
+ p_arentId = p ? (static_cast<KNFolder*>(p))->id() : -1;
+}
+
+
+bool KNFolder::loadHdrs()
+{
+ if(isLoaded()) {
+ kdDebug(5003) << "KNFolder::loadHdrs() : already loaded" << endl;
+ return true;
+ }
+
+ if(!i_ndexFile.open(IO_ReadOnly)) {
+ kdError(5003) << "KNFolder::loadHdrs() : cannot open index-file!" << endl;
+ closeFiles();
+ return false;
+ }
+
+ if(!m_boxFile.open(IO_ReadOnly)) {
+ kdError(5003) << "KNFolder::loadHdrs() : cannot open mbox-file!" << endl;
+ closeFiles();
+ return false;
+ }
+
+ if(!resize(c_ount)) {
+ closeFiles();
+ return false;
+ }
+
+ QCString tmp;
+ KQCStringSplitter split;
+ KNLocalArticle *art;
+ DynData dynamic;
+ int pos1=0, pos2=0, cnt=0, byteCount;
+
+ knGlobals.top->setCursorBusy(true);
+ knGlobals.setStatusMsg(i18n(" Loading folder..."));
+ knGlobals.top->secureProcessEvents();
+
+ while(!i_ndexFile.atEnd()) {
+
+ //read index-data
+ byteCount=i_ndexFile.readBlock((char*)(&dynamic), sizeof(DynData));
+ if(byteCount!=sizeof(DynData))
+ if(i_ndexFile.status() == IO_Ok) {
+ kdWarning(5003) << "KNFolder::loadHeaders() : found broken entry in index-file: Ignored!" << endl;
+ continue;
+ }
+ else {
+ kdError(5003) << "KNFolder::loadHeaders() : corrupted index-file, IO-error!" << endl;
+ closeFiles();
+ clear();
+ knGlobals.top->setCursorBusy( false );
+ return false;
+ }
+
+ art=new KNLocalArticle(this);
+
+ //set index-data
+ dynamic.getData(art);
+
+ //read overview
+ if(!m_boxFile.at(art->startOffset())) {
+ kdError(5003) << "KNFolder::loadHdrs() : cannot set mbox file-pointer!" << endl;
+ closeFiles();
+ clear();
+ knGlobals.top->setCursorBusy( false );
+ return false;
+ }
+ tmp=m_boxFile.readLine(); //KNFile::readLine()
+ if(tmp.isEmpty()) {
+ if(m_boxFile.status() == IO_Ok) {
+ kdWarning(5003) << "found broken entry in mbox-file: Ignored!" << endl;
+ delete art;
+ continue;
+ }
+ else {
+ kdError(5003) << "KNFolder::loadHdrs() : corrupted mbox-file, IO-error!"<< endl;
+ closeFiles();
+ clear();
+ knGlobals.top->setCursorBusy( false );
+ return false;
+ }
+ }
+
+ //set overview
+ bool end=false;
+ pos1=tmp.find(' ')+1;
+ pos2=tmp.find('\t', pos1);
+ if (pos2 == -1) {
+ pos2=tmp.length();
+ end=true;
+ }
+ art->subject()->from7BitString(tmp.mid(pos1, pos2-pos1));
+
+ if (!end) {
+ pos1=pos2+1;
+ pos2=tmp.find('\t', pos1);
+ if (pos2 == -1) {
+ pos2=tmp.length();
+ end=true;
+ }
+ art->newsgroups()->from7BitString(tmp.mid(pos1, pos2-pos1));
+ }
+
+ if (!end) {
+ pos1=pos2+1;
+ pos2=tmp.find('\t', pos1);
+ if (pos2 == -1) {
+ pos2=tmp.length();
+ end=true;
+ }
+ art->to()->from7BitString(tmp.mid(pos1,pos2-pos1));
+ }
+
+ if (!end) {
+ pos1=pos2+1;
+ pos2=tmp.length();
+ art->lines()->from7BitString(tmp.mid(pos1,pos2-pos1));
+ }
+
+ if(!append(art)) {
+ kdError(5003) << "KNFolder::loadHdrs() : cannot append article!"<< endl;
+ delete art;
+ clear();
+ closeFiles();
+
+ knGlobals.setStatusMsg(QString::null);
+ knGlobals.top->setCursorBusy(false);
+ return false;
+ }
+
+ cnt++;
+ }
+
+ closeFiles();
+ setLastID();
+ c_ount=cnt;
+ updateListItem();
+
+ knGlobals.setStatusMsg(QString::null);
+ knGlobals.top->setCursorBusy(false);
+
+ return true;
+}
+
+
+bool KNFolder::unloadHdrs(bool force)
+{
+ if(l_ockedArticles>0)
+ return false;
+
+ if (!force && isNotUnloadable())
+ return false;
+
+ KNLocalArticle *a;
+ for(int idx=0; idx<length(); idx++) {
+ a=at(idx);
+ if (a->hasContent() && !knGlobals.articleManager()->unloadArticle(a, force))
+ return false;
+ }
+ syncIndex();
+ clear();
+
+ return true;
+}
+
+bool KNFolder::loadArticle(KNLocalArticle *a)
+{
+ if(a->hasContent())
+ return true;
+
+ closeFiles();
+ if(!m_boxFile.open(IO_ReadOnly)) {
+ kdError(5003) << "KNFolder::loadArticle(KNLocalArticle *a) : cannot open mbox file: "
+ << m_boxFile.name() << endl;
+ return false;
+ }
+
+ //set file-pointer
+ if(!m_boxFile.at(a->startOffset())) {
+ kdError(5003) << "KNFolder::loadArticle(KNLocalArticle *a) : cannot set mbox file-pointer!" << endl;
+ closeFiles();
+ return false;
+ }
+
+ //read content
+ m_boxFile.readLine(); //skip X-KNode-Overview
+
+ unsigned int size=a->endOffset()-m_boxFile.at()-1;
+ QCString buff(size+10);
+ int readBytes=m_boxFile.readBlock(buff.data(), size);
+ closeFiles();
+ if(readBytes < (int)(size) && m_boxFile.status() != IO_Ok) { //cannot read file
+ kdError(5003) << "KNFolder::loadArticle(KNLocalArticle *a) : corrupted mbox file, IO-error!" << endl;
+ return false;
+ }
+
+ //set content
+ buff.at(readBytes)='\0'; //terminate string
+ a->setContent(buff);
+ a->parse();
+
+ return true;
+}
+
+
+bool KNFolder::saveArticles( KNLocalArticle::List &l )
+{
+ if(!isLoaded()) // loading should not be done here - keep the StorageManager in sync !!
+ return false;
+
+ if(!m_boxFile.open(IO_WriteOnly | IO_Append)) {
+ kdError(5003) << "KNFolder::saveArticles() : cannot open mbox-file!" << endl;
+ closeFiles();
+ return false;
+ }
+
+ int addCnt=0;
+ bool ret=true;
+ bool clear=false;
+ QTextStream ts(&m_boxFile);
+ ts.setEncoding(QTextStream::Latin1);
+
+ for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it ) {
+
+ clear=false;
+ if ( (*it)->id() == -1 || (*it)->collection() != this ) {
+ if ( (*it)->id() != -1 ) {
+ KNFolder *oldFolder = static_cast<KNFolder*>( (*it)->collection() );
+ if ( !(*it)->hasContent() )
+ if( !( clear = oldFolder->loadArticle( (*it) ) ) ) {
+ ret = false;
+ continue;
+ }
+
+ KNLocalArticle::List l;
+ l.append( (*it) );
+ oldFolder->removeArticles( l, false );
+ }
+ if ( !append( (*it) ) ) {
+ kdError(5003) << "KNFolder::saveArticle(KNLocalArticle::List *l) : cannot append article!" << endl;
+ ret = false;
+ continue;
+ (*it)->setCollection(0);
+ }
+ else {
+ (*it)->setCollection(this);
+ addCnt++;
+ }
+ }
+
+ if ( byId( (*it)->id() ) == (*it) ) {
+
+ //MBox
+ ts << "From aaa@aaa Mon Jan 01 00:00:00 1997\n";
+ (*it)->setStartOffset(m_boxFile.at()); //save offset
+
+ //write overview information
+ ts << "X-KNode-Overview: ";
+ ts << (*it)->subject()->as7BitString(false) << '\t';
+
+ KMime::Headers::Base* h;
+ if( ( h = (*it)->newsgroups( false ) ) !=0 )
+ ts << h->as7BitString(false);
+ ts << '\t';
+
+ if( (h = (*it)->to( false ) ) != 0 )
+ ts << h->as7BitString(false);
+ ts << '\t';
+
+ ts << (*it)->lines()->as7BitString(false) << '\n';
+
+ //write article
+ (*it)->toStream( ts );
+ ts << "\n";
+
+ (*it)->setEndOffset( m_boxFile.at() ); //save offset
+
+ //update
+ ArticleWidget::articleChanged( (*it) );
+ i_ndexDirty=true;
+
+ }
+ else {
+ kdError(5003) << "KNFolder::saveArticle() : article not in folder!" << endl;
+ ret=false;
+ }
+
+ if ( clear )
+ (*it)->KMime::Content::clear();
+ }
+
+ closeFiles();
+ syncIndex();
+
+ if(addCnt>0) {
+ c_ount=length();
+ updateListItem();
+ knGlobals.articleManager()->updateViewForCollection(this);
+ }
+
+ return ret;
+}
+
+
+void KNFolder::removeArticles( KNLocalArticle::List &l, bool del )
+{
+ if( !isLoaded() || l.isEmpty() )
+ return;
+
+ int idx = 0, delCnt = 0, *positions;
+ positions = new int[l.count()];
+ KNLocalArticle *a = 0;
+
+ for ( KNLocalArticle::List::Iterator it = l.begin(); it != l.end(); ++it, ++idx ) {
+ if ( (*it)->isLocked() )
+ positions[idx] = -1;
+ else
+ positions[idx] = a_rticles.indexForId( (*it)->id() );
+ }
+
+ for ( idx = 0; idx < (int)(l.count()); ++idx ) {
+ if(positions[idx]==-1)
+ continue;
+
+ a=at(positions[idx]);
+
+ //update
+ knGlobals.artFactory->deleteComposerForArticle(a);
+ KNArticleWindow::closeAllWindowsForArticle(a);
+ ArticleWidget::articleRemoved( a );
+ delete a->listItem();
+
+ //delete article
+ a_rticles.remove(positions[idx], del, false);
+ delCnt++;
+ if(!del)
+ a->setId(-1);
+ }
+
+ if(delCnt>0) {
+ compact();
+ c_ount-=delCnt;
+ updateListItem();
+ i_ndexDirty=true;
+ }
+ delete[] positions;
+}
+
+
+void KNFolder::deleteAll()
+{
+ if(l_ockedArticles>0)
+ return;
+
+ if (!unloadHdrs(true))
+ return;
+
+ clear();
+ c_ount=0;
+ syncIndex(true);
+ updateListItem();
+}
+
+
+void KNFolder::deleteFiles()
+{
+ m_boxFile.remove();
+ i_ndexFile.remove();
+ QFile::remove(i_nfoPath);
+}
+
+
+void KNFolder::syncIndex(bool force)
+{
+ if(!i_ndexDirty && !force)
+ return;
+
+ if(!i_ndexFile.open(IO_WriteOnly)) {
+ kdError(5003) << "KNFolder::syncIndex(bool force) : cannot open index-file!" << endl;
+ closeFiles();
+ return;
+ }
+
+ KNLocalArticle *a;
+ DynData d;
+ for(int idx=0; idx<length(); idx++) {
+ a=at(idx);
+ d.setData(a);
+ i_ndexFile.writeBlock((char*)(&d), sizeof(DynData));
+ }
+ closeFiles();
+
+ i_ndexDirty=false;
+}
+
+
+void KNFolder::closeFiles()
+{
+ if(m_boxFile.isOpen())
+ m_boxFile.close();
+ if(i_ndexFile.isOpen())
+ i_ndexFile.close();
+}
+
+
+//==============================================================================
+
+
+void KNFolder::DynData::setData(KNLocalArticle *a)
+{
+ id=a->id();
+ so=a->startOffset();
+ eo=a->endOffset();
+ sId=a->serverId();
+ ti=a->date()->unixTime();
+
+ flags[0]=a->doMail();
+ flags[1]=a->mailed();
+ flags[2]=a->doPost();
+ flags[3]=a->posted();
+ flags[4]=a->canceled();
+ flags[5]=a->editDisabled();
+}
+
+
+void KNFolder::DynData::getData(KNLocalArticle *a)
+{
+ a->setId(id);
+ a->date()->setUnixTime(ti);
+ a->setStartOffset(so);
+ a->setEndOffset(eo);
+ a->setServerId(sId);
+ a->setDoMail(flags[0]);
+ a->setMailed(flags[1]);
+ a->setDoPost(flags[2]);
+ a->setPosted(flags[3]);
+ a->setCanceled(flags[4]);
+ a->setEditDisabled(flags[5]);
+}
+
diff --git a/knode/knfolder.h b/knode/knfolder.h
new file mode 100644
index 000000000..0e2bfafb9
--- /dev/null
+++ b/knode/knfolder.h
@@ -0,0 +1,103 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNFOLDER_H
+#define KNFOLDER_H
+
+#include <time.h>
+
+#include "utilities.h"
+#include "knarticle.h"
+#include "knarticlecollection.h"
+
+
+class KNFolder : public KNArticleCollection {
+
+ friend class KNCleanUp;
+
+ public:
+ KNFolder();
+ KNFolder(int id, const QString &name, KNFolder *parent=0);
+ KNFolder(int id, const QString &name, const QString &prefix, KNFolder *parent=0);
+ ~KNFolder();
+
+ //type
+ collectionType type() { return CTfolder; }
+
+ //id
+ int id() const { return i_d; }
+ void setId(int i) { i_d=i; }
+ int parentId() const { return p_arentId; }
+ bool isStandardFolder() { return (i_d > 0) && (i_d <=3); }
+ bool isRootFolder() { return i_d==0; }
+
+ //list item handling
+ void updateListItem();
+ bool wasOpen()const { return w_asOpen; }
+
+ //info
+ QString path();
+ bool readInfo(const QString &confPath);
+ bool readInfo();
+ void saveInfo();
+
+ //article access
+ KNLocalArticle* at(int i) { return static_cast<KNLocalArticle*>(KNArticleCollection::at(i)); }
+ KNLocalArticle* byId(int id) { return static_cast<KNLocalArticle*>(KNArticleCollection::byId(id)); }
+ KNLocalArticle* byMessageId(const QCString &mid)
+ { return static_cast<KNLocalArticle*>(KNArticleCollection::byMessageId(mid)); }
+
+ //parent
+ void setParent(KNCollection *p);
+
+ //load, save and delete
+ bool loadHdrs();
+ bool unloadHdrs(bool force=true);
+ bool loadArticle(KNLocalArticle *a);
+ bool saveArticles( KNLocalArticle::List &l );
+ void removeArticles( KNLocalArticle::List &l, bool del = true );
+ void deleteAll();
+ void deleteFiles();
+
+ //index synchronization
+ void syncIndex(bool force=false);
+
+ protected:
+ void closeFiles();
+ int i_d; // unique id: 0: root folder 1-3: standard folders
+ int p_arentId; // -1 for the root folder
+ bool i_ndexDirty; // do we need to sync?
+ bool w_asOpen; // was this folder open in the listview on the last shutdown?
+ KNFile m_boxFile;
+ QFile i_ndexFile;
+ QString i_nfoPath;
+
+ /* helper-class: stores index-data of an article */
+ class DynData {
+ public:
+ DynData() {}
+ ~DynData() {}
+ void setData(KNLocalArticle *a);
+ void getData(KNLocalArticle *a);
+
+ int id,
+ so,
+ eo,
+ sId;
+ time_t ti;
+ bool flags[6];
+ };
+};
+
+#endif
diff --git a/knode/knfoldermanager.cpp b/knode/knfoldermanager.cpp
new file mode 100644
index 000000000..5acb41866
--- /dev/null
+++ b/knode/knfoldermanager.cpp
@@ -0,0 +1,482 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qdir.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "knfolder.h"
+#include "utilities.h"
+#include "knfoldermanager.h"
+#include "knarticlemanager.h"
+#include "kncleanup.h"
+#include "knmemorymanager.h"
+#include "knmainwidget.h"
+
+
+KNFolderManager::KNFolderManager(KNArticleManager *a) : a_rtManager(a)
+{
+ //standard folders
+ QString dir(locateLocal("data","knode/")+"folders/");
+ if (dir.isNull()) {
+ KNHelper::displayInternalFileError();
+ return;
+ }
+
+ KNFolder *f;
+
+ f=new KNFolder(0, i18n("Local Folders"), "root");
+ mFolderList.append( f );
+ f->readInfo();
+
+ f=new KNFolder(1, i18n("Drafts"), "drafts", root());
+ mFolderList.append( f );
+ f->readInfo();
+
+ f=new KNFolder(2, i18n("Outbox"), "outbox", root());
+ mFolderList.append( f );
+ f->readInfo();
+
+ f=new KNFolder(3, i18n("Sent"), "sent", root());
+ mFolderList.append( f );
+ f->readInfo();
+
+ l_astId=3;
+
+ //custom folders
+ loadCustomFolders();
+
+ setCurrentFolder(0);
+}
+
+
+KNFolderManager::~KNFolderManager()
+{
+ for ( QValueList<KNFolder*>::Iterator it = mFolderList.begin(); it != mFolderList.end(); ++it )
+ delete (*it);
+}
+
+
+void KNFolderManager::setCurrentFolder(KNFolder *f)
+{
+ c_urrentFolder=f;
+ a_rtManager->setFolder(f);
+
+ kdDebug(5003) << "KNFolderManager::setCurrentFolder() : folder changed" << endl;
+
+ if(f && !f->isRootFolder()) {
+ if( loadHeaders(f) )
+ a_rtManager->showHdrs();
+ else
+ KMessageBox::error(knGlobals.topWidget, i18n("Cannot load index-file."));
+ }
+}
+
+
+bool KNFolderManager::loadHeaders(KNFolder *f)
+{
+ if( !f || f->isRootFolder() )
+ return false;
+
+ if (f->isLoaded())
+ return true;
+
+ // we want to delete old stuff first => reduce vm fragmentation
+ knGlobals.memoryManager()->prepareLoad(f);
+
+ if (f->loadHdrs()) {
+ knGlobals.memoryManager()->updateCacheEntry( f );
+ return true;
+ }
+
+ return false;
+}
+
+
+bool KNFolderManager::unloadHeaders(KNFolder *f, bool force)
+{
+ if(!f || !f->isLoaded())
+ return false;
+
+ if (!force && (c_urrentFolder == f))
+ return false;
+
+ if (f->unloadHdrs(force))
+ knGlobals.memoryManager()->removeCacheEntry(f);
+ else
+ return false;
+
+ return true;
+}
+
+
+KNFolder* KNFolderManager::folder(int i)
+{
+ for ( QValueList<KNFolder*>::Iterator it = mFolderList.begin(); it != mFolderList.end(); ++it )
+ if ( (*it)->id() == i )
+ return (*it);
+ return 0;
+}
+
+
+KNFolder* KNFolderManager::newFolder(KNFolder *p)
+{
+ if (!p)
+ p = root();
+ KNFolder *f=new KNFolder(++l_astId, i18n("New folder"), p);
+ mFolderList.append( f );
+ emit folderAdded(f);
+ return f;
+}
+
+
+bool KNFolderManager::deleteFolder(KNFolder *f)
+{
+ if(!f || f->isRootFolder() || f->isStandardFolder() || f->lockedArticles()>0)
+ return false;
+
+ QValueList<KNFolder*> del;
+ KNCollection *p;
+
+ // find all subfolders of the folder we want to delete
+ for ( QValueList<KNFolder*>::Iterator it = mFolderList.begin(); it != mFolderList.end(); ++it ) {
+ p = (*it)->parent();
+ while ( p ) {
+ if ( p == f ) {
+ if ( (*it)->lockedArticles() > 0 )
+ return false;
+ del.append( (*it) );
+ break;
+ }
+ p = p->parent();
+ }
+ }
+
+ emit folderRemoved(f);
+
+ del.append(f);
+ for ( QValueList<KNFolder*>::Iterator it = del.begin(); it != del.end(); ++it ) {
+ if ( c_urrentFolder == (*it) )
+ c_urrentFolder = 0;
+
+ if ( unloadHeaders( (*it), true ) ) {
+ (*it)->deleteFiles();
+ mFolderList.remove( (*it) );
+ delete (*it);
+ } else
+ return false;
+ }
+
+ return true;
+}
+
+
+void KNFolderManager::emptyFolder(KNFolder *f)
+{
+ if(!f || f->isRootFolder())
+ return;
+ knGlobals.memoryManager()->removeCacheEntry(f);
+ f->deleteAll();
+}
+
+
+bool KNFolderManager::moveFolder(KNFolder *f, KNFolder *p)
+{
+ if(!f || p==f->parent()) // nothing to be done
+ return true;
+
+ // is "p" a child of "f" ?
+ KNCollection *p2=p?p->parent():0;
+ while(p2) {
+ if(p2==f)
+ break;
+ p2=p2->parent();
+ }
+
+ if( (p2 && p2==f) || f==p || f->isStandardFolder() || f->isRootFolder()) // no way ;-)
+ return false;
+
+ emit folderRemoved(f);
+
+ // reparent
+ f->setParent(p);
+ f->saveInfo();
+
+ emit folderAdded(f);
+
+ if(c_urrentFolder==f)
+ emit folderActivated(f);
+
+ return true;
+}
+
+
+int KNFolderManager::unsentForAccount(int accId)
+{
+ int cnt=0;
+
+ for ( QValueList<KNFolder*>::Iterator it = mFolderList.begin(); it != mFolderList.end(); ++it ) {
+ for ( int idx = 0; idx < (*it)->length(); ++idx ) {
+ KNLocalArticle *a = (*it)->at( idx );
+ if ( a->serverId() == accId && a->doPost() && !a->posted() )
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+
+void KNFolderManager::compactFolder(KNFolder *f)
+{
+ if(!f || f->isRootFolder())
+ return;
+
+ KNCleanUp cup;
+ cup.compactFolder(f);
+}
+
+
+void KNFolderManager::compactAll(KNCleanUp *cup)
+{
+ for ( QValueList<KNFolder*>::Iterator it = mFolderList.begin(); it != mFolderList.end(); ++it )
+ if ( !(*it)->isRootFolder() && (*it)->lockedArticles() == 0 )
+ cup->appendCollection( (*it) );
+}
+
+
+void KNFolderManager::compactAll()
+{
+ KNCleanUp *cup = new KNCleanUp();
+ compactAll( cup );
+ cup->start();
+
+ knGlobals.configManager()->cleanup()->setLastCompactDate();
+ delete cup;
+}
+
+
+void KNFolderManager::importFromMBox(KNFolder *f)
+{
+ if(!f || f->isRootFolder())
+ return;
+
+ f->setNotUnloadable(true);
+
+ if (!f->isLoaded() && !loadHeaders(f)) {
+ f->setNotUnloadable(false);
+ return;
+ }
+
+ KNLoadHelper helper(knGlobals.topWidget);
+ KNFile *file = helper.getFile(i18n("Import MBox Folder"));
+ KNLocalArticle::List list;
+ KNLocalArticle *art;
+ QString s;
+ int artStart=0, artEnd=0;
+ bool done=true;
+
+ if (file) {
+ knGlobals.top->setCursorBusy(true);
+ knGlobals.setStatusMsg(i18n(" Importing articles..."));
+ knGlobals.top->secureProcessEvents();
+
+ if (!file->atEnd()) { // search for the first article...
+ s = file->readLine();
+ if (s.left(5) == "From ") {
+ artStart = file->at();
+ done = false;
+ } else {
+ artStart = file->findString("\n\nFrom ");
+ if (artStart != -1) {
+ file->at(artStart+1);
+ s = file->readLine();
+ artStart = file->at();
+ done = false;
+ }
+ }
+ }
+
+ knGlobals.top->secureProcessEvents();
+
+ if (!done) {
+ while (!file->atEnd()) {
+ artEnd = file->findString("\n\nFrom ");
+
+ if (artEnd != -1) {
+ file->at(artStart); // seek the first character of the article
+ int size=artEnd-artStart;
+ QCString buff(size+10);
+ int readBytes=file->readBlock(buff.data(), size);
+
+ if (readBytes != -1) {
+ buff.at(readBytes)='\0'; //terminate string
+ art = new KNLocalArticle(0);
+ art->setEditDisabled(true);
+ art->setContent(buff);
+ art->parse();
+ list.append(art);
+ }
+
+ file->at(artEnd+1);
+ s = file->readLine();
+ artStart = file->at();
+ } else {
+ if ((int)file->size() > artStart) {
+ file->at(artStart); // seek the first character of the article
+ int size=file->size()-artStart;
+ QCString buff(size+10);
+ int readBytes=file->readBlock(buff.data(), size);
+
+ if (readBytes != -1) {
+ buff.at(readBytes)='\0'; //terminate string
+ art = new KNLocalArticle(0);
+ art->setEditDisabled(true);
+ art->setContent(buff);
+ art->parse();
+ list.append(art);
+ }
+ }
+ }
+
+ if (list.count()%75 == 0)
+ knGlobals.top->secureProcessEvents();
+ }
+ }
+
+ knGlobals.setStatusMsg(i18n(" Storing articles..."));
+ knGlobals.top->secureProcessEvents();
+
+ if (!list.isEmpty())
+ knGlobals.articleManager()->moveIntoFolder(list, f);
+
+ knGlobals.setStatusMsg(QString::null);
+ knGlobals.top->setCursorBusy(false);
+ }
+
+ f->setNotUnloadable(false);
+}
+
+
+void KNFolderManager::exportToMBox(KNFolder *f)
+{
+ if(!f || f->isRootFolder())
+ return;
+
+ f->setNotUnloadable(true);
+
+ if (!f->isLoaded() && !loadHeaders(f)) {
+ f->setNotUnloadable(false);
+ return;
+ }
+
+ KNSaveHelper helper(f->name()+".mbox", knGlobals.topWidget);
+ QFile *file = helper.getFile(i18n("Export Folder"));
+
+ if (file) {
+ knGlobals.top->setCursorBusy(true);
+ knGlobals.setStatusMsg(i18n(" Exporting articles..."));
+ knGlobals.top->secureProcessEvents();
+
+ QTextStream ts(file);
+ ts.setEncoding(QTextStream::Latin1);
+ KNLocalArticle *a;
+
+ for(int idx=0; idx<f->length(); idx++) {
+ a=f->at(idx);
+
+ a->setNotUnloadable(true);
+
+ if (a->hasContent() || knGlobals.articleManager()->loadArticle(a)) {
+ ts << "From aaa@aaa Mon Jan 01 00:00:00 1997\n";
+ a->toStream(ts, true);
+ ts << "\n";
+ }
+
+ a->setNotUnloadable(false);
+
+ if (idx%75 == 0)
+ knGlobals.top->secureProcessEvents();
+ }
+
+ knGlobals.setStatusMsg(QString::null);
+ knGlobals.top->setCursorBusy(false);
+ }
+}
+
+
+void KNFolderManager::syncFolders()
+{
+ QString dir(locateLocal("data","knode/")+"folders/");
+ if (dir.isNull()) {
+ KNHelper::displayInternalFileError();
+ return;
+ }
+
+ //sync
+ for ( QValueList<KNFolder*>::Iterator it = mFolderList.begin(); it != mFolderList.end(); ++it ) {
+ if ( !(*it)->isRootFolder() )
+ (*it)->syncIndex();
+ (*it)->saveInfo();
+ }
+}
+
+
+int KNFolderManager::loadCustomFolders()
+{
+ int cnt=0;
+ QString dir(locateLocal("data","knode/")+"folders/");
+ KNFolder *f;
+
+ if (dir.isNull()) {
+ KNHelper::displayInternalFileError();
+ return 0;
+ }
+
+ QDir d(dir);
+ QStringList entries(d.entryList("custom_*.info")); // ignore info files of standard folders
+ for(QStringList::Iterator it=entries.begin(); it != entries.end(); ++it) {
+ f=new KNFolder();
+ if(f->readInfo(d.absFilePath(*it))) {
+ if(f->id()>l_astId)
+ l_astId=f->id();
+ mFolderList.append( f );
+ cnt++;
+ }
+ else
+ delete f;
+ }
+
+ // set parents
+ if(cnt>0) {
+ for ( QValueList<KNFolder*>::Iterator it = mFolderList.begin(); it != mFolderList.end(); ++it ) {
+ if ( !(*it)->isRootFolder() ) { // the root folder has no parent
+ KNFolder *par = folder( (*it)->parentId() );
+ if ( !par )
+ par = root();
+ (*it)->setParent( par );
+ }
+ }
+ }
+
+ return cnt;
+}
+
+
+#include "knfoldermanager.moc"
diff --git a/knode/knfoldermanager.h b/knode/knfoldermanager.h
new file mode 100644
index 000000000..9bc8254e8
--- /dev/null
+++ b/knode/knfoldermanager.h
@@ -0,0 +1,91 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNFOLDERMANAGER_H
+#define KNFOLDERMANAGER_H
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+class KNFolder;
+class KNArticleManager;
+class KNCleanUp;
+
+
+class KNFolderManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ KNFolderManager(KNArticleManager *a);
+ ~KNFolderManager();
+
+ //folder access
+ void setCurrentFolder(KNFolder *f);
+ KNFolder* currentFolder() const { return c_urrentFolder; }
+ bool hasCurrentFolder() const { return (c_urrentFolder!=0); }
+ KNFolder* folder(int id);
+ QValueList<KNFolder*> folders() const { return mFolderList; }
+
+ //standard folders
+ KNFolder* root() const { return mFolderList[0]; }
+ KNFolder* drafts() const { return mFolderList[1]; }
+ KNFolder* outbox() const { return mFolderList[2]; }
+ KNFolder* sent() const { return mFolderList[3]; }
+
+ //header loading
+ bool loadHeaders(KNFolder *f);
+ bool unloadHeaders(KNFolder *f, bool force=true);
+ bool loadDrafts() { return loadHeaders(drafts()); }
+ bool loadOutbox() { return loadHeaders(outbox()); }
+ bool loadSent() { return loadHeaders(sent()); }
+
+ // returns the new folder
+ KNFolder* newFolder(KNFolder *p);
+ bool deleteFolder(KNFolder *f);
+ void emptyFolder(KNFolder *f);
+ bool moveFolder(KNFolder *f, KNFolder *p);
+
+ //unsent articles
+ int unsentForAccount(int accId);
+
+ //compacting
+ void compactFolder(KNFolder *f);
+ void compactAll(KNCleanUp *cup);
+ void compactAll();
+
+ // import + export
+ void importFromMBox(KNFolder *f);
+ void exportToMBox(KNFolder *f);
+
+ //synchronization
+ void syncFolders();
+
+ signals:
+ // signals for the collection tree to update the UI
+ void folderAdded(KNFolder *f);
+ void folderRemoved(KNFolder *f);
+ void folderActivated(KNFolder *f);
+
+ protected:
+ int loadCustomFolders();
+
+ KNFolder *c_urrentFolder;
+ QValueList<KNFolder*> mFolderList;
+ int l_astId;
+ KNArticleManager *a_rtManager;
+
+};
+
+#endif
diff --git a/knode/knglobals.cpp b/knode/knglobals.cpp
new file mode 100644
index 000000000..5238bebec
--- /dev/null
+++ b/knode/knglobals.cpp
@@ -0,0 +1,114 @@
+/*
+ knglobals.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include "knglobals.h"
+
+
+#include <kconfig.h>
+#include <kstaticdeleter.h>
+
+#include "knconfigmanager.h"
+#include "knnetaccess.h"
+#include "knaccountmanager.h"
+#include "kngroupmanager.h"
+#include "knarticlemanager.h"
+#include "knfiltermanager.h"
+#include "knfoldermanager.h"
+#include "knscoring.h"
+#include "knmemorymanager.h"
+#include "knmainwidget.h"
+#include "knwidgets.h"
+
+KConfig* KNGlobals::config()
+{
+ if (!c_onfig) {
+ c_onfig = KSharedConfig::openConfig( "knoderc" );
+ }
+ return c_onfig;
+}
+
+KNConfigManager* KNGlobals::configManager()
+{
+ if (!mCfgManager)
+ mCfgManager = new KNConfigManager();
+ return mCfgManager;
+}
+
+KNNetAccess* KNGlobals::netAccess()
+{
+ if(!mNetAccess)
+ mNetAccess = new KNNetAccess();
+ return mNetAccess;
+}
+
+KNAccountManager* KNGlobals::accountManager()
+{
+ if(!mAccManager)
+ mAccManager = new KNAccountManager(groupManager());
+ return mAccManager;
+}
+
+KNGroupManager* KNGlobals::groupManager()
+{
+ if(!mGrpManager)
+ mGrpManager = new KNGroupManager();
+ return mGrpManager;
+}
+
+KNArticleManager* KNGlobals::articleManager()
+{
+ if(!mArtManager)
+ mArtManager = new KNArticleManager();
+ return mArtManager;
+}
+
+KNFilterManager* KNGlobals::filterManager()
+{
+ if (!mFilManager)
+ mFilManager = new KNFilterManager();
+ return mFilManager;
+}
+
+KNFolderManager* KNGlobals::folderManager()
+{
+ if(!mFolManager)
+ mFolManager = new KNFolderManager(articleManager());
+ return mFolManager;
+}
+
+KNScoringManager* KNGlobals::mScoreManager = 0;
+
+KNScoringManager* KNGlobals::scoringManager()
+{
+ static KStaticDeleter<KNScoringManager> sd;
+ if (!mScoreManager)
+ sd.setObject(mScoreManager, new KNScoringManager());
+ return mScoreManager;
+}
+
+KNMemoryManager* KNGlobals::memoryManager()
+{
+ if(!mMemManager)
+ mMemManager = new KNMemoryManager();
+ return mMemManager;
+}
+
+
+void KNGlobals::setStatusMsg(const QString &text, int id)
+{
+ if(top)
+ top->setStatusMsg(text, id);
+}
diff --git a/knode/knglobals.h b/knode/knglobals.h
new file mode 100644
index 000000000..12b934dae
--- /dev/null
+++ b/knode/knglobals.h
@@ -0,0 +1,95 @@
+/*
+ knglobals.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNGLOBALS_H
+#define KNGLOBALS_H
+
+#include <kconfig.h>
+#include "resource.h"
+
+#include <kdepimmacros.h>
+
+class KInstance;
+class KNConfigManager;
+class KNNetAccess;
+class KNProgress;
+class KNAccountManager;
+class KNGroupManager;
+class KNArticleManager;
+class KNArticleFactory;
+class KNFolderManager;
+class QWidget;
+class KNFilterManager;
+class KNMainWidget;
+class KNScoringManager;
+class KNMemoryManager;
+class KXMLGUIClient;
+namespace Kpgp {
+ class Module;
+}
+namespace KNode {
+ class ArticleWidget;
+}
+
+
+/** idea: Previously the manager classes were available
+ via KNodeApp. Now they can be accessed directly,
+ this removes many header dependencies.
+ (knode.h isn't include everywhere) */
+class KDE_EXPORT KNGlobals {
+ public:
+ /** topWidget == top, used for message boxes, */
+ QWidget *topWidget;
+ /** no need to include knode.h everywhere */
+ KNMainWidget *top;
+ KXMLGUIClient *guiClient;
+ KNode::ArticleWidget *artWidget;
+ KNArticleFactory *artFactory;
+ Kpgp::Module *pgp;
+ KConfig *config();
+ KInstance *instance;
+
+ KNConfigManager *configManager();
+ KNNetAccess *netAccess();
+ KNAccountManager *accountManager();
+ KNGroupManager *groupManager();
+ KNArticleManager *articleManager();
+ KNFilterManager *filterManager();
+ KNFolderManager *folderManager();
+ KNScoringManager *scoringManager();
+ KNMemoryManager *memoryManager();
+
+ /** forwarded to top->setStatusMsg() if available */
+ void setStatusMsg(const QString& text = QString::null, int id = SB_MAIN);
+
+private:
+ KSharedConfig::Ptr c_onfig;
+
+ KNNetAccess *mNetAccess;
+ KNConfigManager *mCfgManager;
+ KNAccountManager *mAccManager;
+ KNGroupManager *mGrpManager;
+ KNArticleManager *mArtManager;
+ KNFilterManager *mFilManager;
+ KNFolderManager *mFolManager;
+ static KNScoringManager *mScoreManager;
+ KNMemoryManager *mMemManager;
+};
+
+
+extern KNGlobals knGlobals KDE_EXPORT;
+
+#endif
diff --git a/knode/kngroup.cpp b/knode/kngroup.cpp
new file mode 100644
index 000000000..1c8bdfbf1
--- /dev/null
+++ b/knode/kngroup.cpp
@@ -0,0 +1,1112 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+
+#include <ksimpleconfig.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <kqcstringsplitter.h>
+
+#include "knprotocolclient.h"
+#include "knglobals.h"
+#include "kncollectionviewitem.h"
+#include "kngrouppropdlg.h"
+#include "utilities.h"
+#include "knconfigmanager.h"
+#include "knmainwidget.h"
+#include "knscoring.h"
+#include "knarticlemanager.h"
+#include "kngroupmanager.h"
+#include "knnntpaccount.h"
+#include "headerview.h"
+
+
+#define SORT_DEPTH 5
+
+KNGroup::KNGroup(KNCollection *p)
+ : KNArticleCollection(p), n_ewCount(0), l_astFetchCount(0), r_eadCount(0), i_gnoreCount(0),
+ l_astNr(0), m_axFetch(0), d_ynDataFormat(1), f_irstNew(-1), l_ocked(false),
+ u_seCharset(false), s_tatus(unknown), i_dentity(0)
+{
+ mCleanupConf = new KNConfig::Cleanup( false );
+}
+
+
+KNGroup::~KNGroup()
+{
+ delete i_dentity;
+ delete mCleanupConf;
+}
+
+
+QString KNGroup::path()
+{
+ return p_arent->path();
+}
+
+
+const QString& KNGroup::name()
+{
+ static QString ret;
+ if(n_ame.isEmpty()) ret=g_roupname;
+ else ret=n_ame;
+ return ret;
+}
+
+
+void KNGroup::updateListItem()
+{
+ if(!l_istItem) return;
+ l_istItem->setTotalCount( c_ount );
+ l_istItem->setUnreadCount( c_ount - r_eadCount - i_gnoreCount );
+ l_istItem->repaint();
+}
+
+
+bool KNGroup::readInfo(const QString &confPath)
+{
+ KSimpleConfig info(confPath);
+
+ g_roupname = info.readEntry("groupname");
+ d_escription = info.readEntry("description");
+ n_ame = info.readEntry("name");
+ c_ount = info.readNumEntry("count",0);
+ r_eadCount = info.readNumEntry("read",0);
+ if (r_eadCount > c_ount) r_eadCount = c_ount;
+ f_irstNr = info.readNumEntry("firstMsg",0);
+ l_astNr = info.readNumEntry("lastMsg",0);
+ d_ynDataFormat = info.readNumEntry("dynDataFormat",0);
+ u_seCharset = info.readBoolEntry("useCharset", false);
+ d_efaultChSet = info.readEntry("defaultChSet").latin1();
+ QString s = info.readEntry("status","unknown");
+ if (s=="readOnly")
+ s_tatus = readOnly;
+ else if (s=="postingAllowed")
+ s_tatus = postingAllowed;
+ else if (s=="moderated")
+ s_tatus = moderated;
+ else
+ s_tatus = unknown;
+ c_rosspostIDBuffer = info.readListEntry("crosspostIDBuffer");
+
+ i_dentity=new KNConfig::Identity(false);
+ i_dentity->loadConfig(&info);
+ if(!i_dentity->isEmpty()) {
+ kdDebug(5003) << "KNGroup::readInfo(const QString &confPath) : using alternative user for " << g_roupname << endl;
+ }
+ else {
+ delete i_dentity;
+ i_dentity=0;
+ }
+
+ mCleanupConf->loadConfig( &info );
+
+ return (!g_roupname.isEmpty());
+}
+
+
+void KNGroup::saveInfo()
+{
+ QString dir(path());
+
+ if (!dir.isNull()) {
+ KSimpleConfig info(dir+g_roupname+".grpinfo");
+
+ info.writeEntry("groupname", g_roupname);
+ info.writeEntry("description", d_escription);
+ info.writeEntry("firstMsg", f_irstNr);
+ info.writeEntry("lastMsg", l_astNr);
+ info.writeEntry("count", c_ount);
+ info.writeEntry("read", r_eadCount);
+ info.writeEntry("dynDataFormat", d_ynDataFormat);
+ info.writeEntry("name", n_ame);
+ info.writeEntry("useCharset", u_seCharset);
+ info.writeEntry("defaultChSet", QString::fromLatin1(d_efaultChSet));
+ switch (s_tatus) {
+ case unknown: info.writeEntry("status","unknown");
+ break;
+ case readOnly: info.writeEntry("status","readOnly");
+ break;
+ case postingAllowed: info.writeEntry("status","postingAllowed");
+ break;
+ case moderated: info.writeEntry("status","moderated");
+ break;
+ }
+ info.writeEntry("crosspostIDBuffer", c_rosspostIDBuffer);
+
+ if(i_dentity)
+ i_dentity->saveConfig(&info);
+ else if(info.hasKey("Email")) {
+ info.deleteEntry("Name", false);
+ info.deleteEntry("Email", false);
+ info.deleteEntry("Reply-To", false);
+ info.deleteEntry("Mail-Copies-To", false);
+ info.deleteEntry("Org", false);
+ info.deleteEntry("UseSigFile", false);
+ info.deleteEntry("UseSigGenerator", false);
+ info.deleteEntry("sigFile", false);
+ info.deleteEntry("sigText", false);
+ }
+
+ mCleanupConf->saveConfig( &info );
+ }
+}
+
+
+KNNntpAccount* KNGroup::account()
+{
+ KNCollection *p=parent();
+ while(p->type()!=KNCollection::CTnntpAccount) p=p->parent();
+
+ return (KNNntpAccount*)p_arent;
+}
+
+
+bool KNGroup::loadHdrs()
+{
+ if(isLoaded()) {
+ kdDebug(5003) << "KNGroup::loadHdrs() : nothing to load" << endl;
+ return true;
+ }
+
+ kdDebug(5003) << "KNGroup::loadHdrs() : loading headers" << endl;
+ QCString buff, hdrValue;
+ KNFile f;
+ KQCStringSplitter split;
+ int cnt=0, id, lines, fileFormatVersion, artNumber;
+ unsigned int timeT;
+ KNRemoteArticle *art;
+
+ QString dir(path());
+ if (dir.isNull())
+ return false;
+
+ f.setName(dir+g_roupname+".static");
+
+ if(f.open(IO_ReadOnly)) {
+
+ if(!resize(c_ount)) {
+ f.close();
+ return false;
+ }
+
+ while(!f.atEnd()) {
+ buff=f.readLine();
+ if(buff.isEmpty()){
+ if (f.status() == IO_Ok) {
+ kdWarning(5003) << "Found broken line in static-file: Ignored!" << endl;
+ continue;
+ } else {
+ kdError(5003) << "Corrupted static file, IO-error!" << endl;
+ clear();
+ return false;
+ }
+ }
+
+ split.init(buff, "\t");
+
+ art=new KNRemoteArticle(this);
+
+ split.first();
+ art->messageID()->from7BitString(split.string());
+
+ split.next();
+ art->subject()->from7BitString(split.string());
+
+ split.next();
+ art->from()->setEmail(split.string());
+ split.next();
+ if(split.string()!="0")
+ art->from()->setNameFrom7Bit(split.string());
+
+ buff=f.readLine();
+ if(buff!="0") art->references()->from7BitString(buff.copy());
+
+ buff=f.readLine();
+ if (sscanf(buff,"%d %d %u %d", &id, &lines, &timeT, &fileFormatVersion) < 4)
+ fileFormatVersion = 0; // KNode <= 0.4 had no version number
+ art->setId(id);
+ art->lines()->setNumberOfLines(lines);
+ art->date()->setUnixTime(timeT);
+
+ if (fileFormatVersion > 0) {
+ buff=f.readLine();
+ sscanf(buff,"%d", &artNumber);
+ art->setArticleNumber(artNumber);
+ }
+
+ // optional headers
+ if (fileFormatVersion > 1) {
+ // first line is the number of addiotion headers
+ buff = f.readLine();
+ // following lines contain one header per line
+ for (uint i = buff.toUInt(); i > 0; --i) {
+ buff = f.readLine();
+ int pos = buff.find(':');
+ QCString hdrName = buff.left( pos );
+ // skip headers we already set above and which we actually never should
+ // find here, but however it still happens... (eg. #101355)
+ if ( hdrName == "Subject" || hdrName == "From" || hdrName == "Date"
+ || hdrName == "Message-ID" || hdrName == "References"
+ || hdrName == "Bytes" || hdrName == "Lines" )
+ continue;
+ hdrValue = buff.right( buff.length() - (pos + 2) );
+ if (hdrValue.length() > 0)
+ art->setHeader( new KMime::Headers::Generic( hdrName, art, hdrValue ) );
+ }
+ }
+
+ if(append(art)) cnt++;
+ else {
+ f.close();
+ clear();
+ return false;
+ }
+ }
+
+ setLastID();
+ f.close();
+ }
+ else {
+ clear();
+ return false;
+ }
+
+
+ f.setName(dir+g_roupname+".dynamic");
+
+ if (f.open(IO_ReadOnly)) {
+
+ dynDataVer0 data0;
+ dynDataVer1 data1;
+ int readCnt=0,byteCount,dataSize;
+ if (d_ynDataFormat==0)
+ dataSize = sizeof(data0);
+ else
+ dataSize = sizeof(data1);
+
+ while(!f.atEnd()) {
+
+ if (d_ynDataFormat==0)
+ byteCount = f.readBlock((char*)(&data0), dataSize);
+ else
+ byteCount = f.readBlock((char*)(&data1), dataSize);
+ if ((byteCount == -1)||(byteCount!=dataSize))
+ if (f.status() == IO_Ok) {
+ kdWarning(5003) << "Found broken entry in dynamic-file: Ignored!" << endl;
+ continue;
+ } else {
+ kdError(5003) << "Corrupted dynamic file, IO-error!" << endl;
+ clear();
+ return false;
+ }
+
+ if (d_ynDataFormat==0)
+ art=byId(data0.id);
+ else
+ art=byId(data1.id);
+
+ if(art) {
+ if (d_ynDataFormat==0)
+ data0.getData(art);
+ else
+ data1.getData(art);
+
+ if (art->isRead()) readCnt++;
+ }
+
+ }
+
+ f.close();
+
+ r_eadCount=readCnt;
+
+ }
+
+ else {
+ clear();
+ return false;
+ }
+
+ kdDebug(5003) << cnt << " articles read from file" << endl;
+ c_ount=length();
+
+ // convert old data files into current format:
+ if (d_ynDataFormat!=1) {
+ saveDynamicData(length(), true);
+ d_ynDataFormat=1;
+ }
+
+ // restore "New" - flags
+ if( f_irstNew > -1 ) {
+ for( int i = f_irstNew; i < length(); i++ ) {
+ at(i)->setNew(true);
+ }
+ }
+
+ updateThreadInfo();
+ processXPostBuffer(false);
+ return true;
+}
+
+
+bool KNGroup::unloadHdrs(bool force)
+{
+ if(l_ockedArticles>0)
+ return false;
+
+ if (!force && isNotUnloadable())
+ return false;
+
+ KNRemoteArticle *a;
+ for(int idx=0; idx<length(); idx++) {
+ a=at(idx);
+ if (a->hasContent() && !knGlobals.articleManager()->unloadArticle(a, force))
+ return false;
+ }
+ syncDynamicData();
+ clear();
+
+ return true;
+}
+
+
+// Attention: this method is called from the network thread!
+void KNGroup::insortNewHeaders(QStrList *hdrs, QStrList *hdrfmt, KNProtocolClient *client)
+{
+ KNRemoteArticle *art=0, *art2=0;
+ QCString data, hdr, hdrName;
+ KQCStringSplitter split;
+ split.setIncludeSep(false);
+ int new_cnt=0, added_cnt=0, todo=hdrs->count();
+ QTime timer;
+
+ l_astFetchCount=0;
+
+ if(!hdrs || hdrs->count()==0)
+ return;
+
+ timer.start();
+
+ //resize the list
+ if(!resize(size()+hdrs->count())) return;
+
+ // recreate msg-ID index
+ syncSearchIndex();
+
+ // remember index of first new
+ if(f_irstNew == -1)
+ f_irstNew = length(); // index of last + 1
+
+ for(char *line=hdrs->first(); line; line=hdrs->next()) {
+ split.init(line, "\t");
+
+ //new Header-Object
+ art=new KNRemoteArticle(this);
+ art->setNew(true);
+
+ //Article Number
+ split.first();
+ art->setArticleNumber(split.string().toInt());
+
+ //Subject
+ split.next();
+ art->subject()->from7BitString(split.string());
+ if(art->subject()->isEmpty())
+ art->subject()->fromUnicodeString(i18n("no subject"), art->defaultCharset());
+
+ //From and Email
+ split.next();
+ art->from()->from7BitString(split.string());
+
+ //Date
+ split.next();
+ art->date()->from7BitString(split.string());
+
+ //Message-ID
+ split.next();
+ art->messageID()->from7BitString(split.string().simplifyWhiteSpace());
+
+ //References
+ split.next();
+ if(!split.string().isEmpty())
+ art->references()->from7BitString(split.string()); //use QCString::copy() ?
+
+ // Bytes
+ split.next();
+
+ //Lines
+ split.next();
+ art->lines()->setNumberOfLines(split.string().toInt());
+
+ // optinal additional headers
+ mOptionalHeaders = *hdrfmt;
+ for (hdr = hdrfmt->first(); hdr; hdr = hdrfmt->next()) {
+ if (!split.next())
+ break;
+ data = split.string();
+ int pos = hdr.find(':');
+ hdrName = hdr.left( pos );
+ // if the header format is 'full' we have to strip the header name
+ if (hdr.findRev("full") == (int)(hdr.length() - 4))
+ data = data.right( data.length() - (hdrName.length() + 2) );
+
+ // add header
+ art->setHeader( new KMime::Headers::Generic( hdrName, art, data ) );
+ }
+
+ // check if we have this article already in this group,
+ // if so mark it as new (useful with leafnodes delay-body function)
+ art2=byMessageId(art->messageID()->as7BitString(false));
+ if(art2) { // ok, we already have this article
+ art2->setNew(true);
+ art2->setArticleNumber(art->articleNumber());
+ delete art;
+ new_cnt++;
+ }
+ else if (append(art)) {
+ added_cnt++;
+ new_cnt++;
+ }
+ else {
+ delete art;
+ return;
+ }
+
+ if (timer.elapsed() > 200) { // don't flicker
+ timer.restart();
+ if (client) client->updatePercentage((new_cnt*30)/todo);
+ }
+ }
+
+ // now we build the threads
+ syncSearchIndex(); // recreate the msgId-index so it contains the appended headers
+ buildThreads(added_cnt, client);
+ updateThreadInfo();
+
+ // save the new headers
+ saveStaticData(added_cnt);
+ saveDynamicData(added_cnt);
+
+ // update group-info
+ c_ount=length();
+ n_ewCount+=new_cnt;
+ l_astFetchCount=new_cnt;
+ updateListItem();
+ saveInfo();
+}
+
+
+int KNGroup::saveStaticData(int cnt,bool ovr)
+{
+ int idx, savedCnt=0, mode;
+ KNRemoteArticle *art;
+
+ QString dir(path());
+ if (dir.isNull())
+ return 0;
+
+ QFile f(dir+g_roupname+".static");
+
+ if(ovr) mode=IO_WriteOnly;
+ else mode=IO_WriteOnly | IO_Append;
+
+ if(f.open(mode)) {
+
+ QTextStream ts(&f);
+ ts.setEncoding(QTextStream::Latin1);
+
+ for(idx=length()-cnt; idx<length(); idx++) {
+
+ art=at(idx);
+
+ if(art->isExpired()) continue;
+
+ ts << art->messageID()->as7BitString(false) << '\t';
+ ts << art->subject()->as7BitString(false) << '\t';
+ ts << art->from()->email() << '\t';
+
+ if(art->from()->hasName())
+ ts << art->from()->nameAs7Bit() << '\n';
+ else
+ ts << "0\n";
+
+ if(!art->references()->isEmpty())
+ ts << art->references()->as7BitString(false) << "\n";
+ else
+ ts << "0\n";
+
+ ts << art->id() << ' ';
+ ts << art->lines()->numberOfLines() << ' ';
+ ts << art->date()->unixTime() << ' ';
+ ts << "2\n"; // version number to achieve backward compatibility easily
+
+ ts << art->articleNumber() << '\n';
+
+ // optional headers
+ ts << mOptionalHeaders.count() << '\n';
+ for (QCString hdrName = mOptionalHeaders.first(); hdrName; hdrName = mOptionalHeaders.next()) {
+ hdrName = hdrName.left( hdrName.find(':') );
+ KMime::Headers::Base *hdr = art->getHeaderByType( hdrName );
+ if ( hdr )
+ ts << hdrName << ": " << hdr->asUnicodeString() << '\n';
+ else
+ ts << hdrName << ": \n";
+ }
+
+ savedCnt++;
+ }
+
+ f.close();
+ }
+
+ return savedCnt;
+}
+
+
+void KNGroup::saveDynamicData(int cnt,bool ovr)
+{
+ dynDataVer1 data;
+ int mode;
+ KNRemoteArticle *art;
+
+ if(length()>0) {
+ QString dir(path());
+ if (dir.isNull())
+ return;
+
+ QFile f(dir+g_roupname+".dynamic");
+
+ if(ovr) mode=IO_WriteOnly;
+ else mode=IO_WriteOnly | IO_Append;
+
+ if(f.open(mode)) {
+
+ for(int idx=length()-cnt; idx<length(); idx++) {
+ art=at(idx);
+ if(art->isExpired()) continue;
+ data.setData(art);
+ f.writeBlock((char*)(&data), sizeof(data));
+ art->setChanged(false);
+ }
+ f.close();
+ }
+ else KNHelper::displayInternalFileError();
+ }
+}
+
+
+void KNGroup::syncDynamicData()
+{
+ dynDataVer1 data;
+ int cnt=0, readCnt=0, sOfData;
+ KNRemoteArticle *art;
+
+ if(length()>0) {
+
+ QString dir(path());
+ if (dir.isNull())
+ return;
+
+ QFile f(dir+g_roupname+".dynamic");
+
+ if(f.open(IO_ReadWrite)) {
+
+ sOfData=sizeof(data);
+
+ for(int i=0; i<length(); i++) {
+ art=at(i);
+
+ if(art->hasChanged() && !art->isExpired()) {
+
+ data.setData(art);
+ f.at(i*sOfData);
+ f.writeBlock((char*) &data, sOfData);
+ cnt++;
+ art->setChanged(false);
+ }
+
+ if(art->isRead() && !art->isExpired()) readCnt++;
+ }
+
+ f.close();
+
+ kdDebug(5003) << g_roupname << " => updated " << cnt << " entries of dynamic data" << endl;
+
+ r_eadCount=readCnt;
+ }
+ else KNHelper::displayInternalFileError();
+ }
+}
+
+
+void KNGroup::appendXPostID(const QString &id)
+{
+ c_rosspostIDBuffer.append(id);
+}
+
+
+void KNGroup::processXPostBuffer(bool deleteAfterwards)
+{
+ QStringList remainder;
+ KNRemoteArticle *xp;
+ KNRemoteArticle::List al;
+
+ for (QStringList::Iterator it = c_rosspostIDBuffer.begin(); it != c_rosspostIDBuffer.end(); ++it) {
+ if ((xp=byMessageId((*it).local8Bit())))
+ al.append(xp);
+ else
+ remainder.append(*it);
+ }
+ knGlobals.articleManager()->setRead(al, true, false);
+
+ if (!deleteAfterwards)
+ c_rosspostIDBuffer = remainder;
+ else
+ c_rosspostIDBuffer.clear();
+}
+
+
+void KNGroup::buildThreads(int cnt, KNProtocolClient *client)
+{
+ int end=length(),
+ start=end-cnt,
+ foundCnt=0, bySubCnt=0, refCnt=0,
+ resortCnt=0, idx, oldRef; // idRef;
+ KNRemoteArticle *art, *ref;
+ QTime timer;
+
+ timer.start();
+
+ // this method is called from the nntp-thread!!!
+#ifndef NDEBUG
+ qDebug("knode: KNGroup::buildThreads() : start = %d end = %d",start,end);
+#endif
+
+ //resort old hdrs
+ if(start>0)
+ for(idx=0; idx<start; idx++) {
+ art=at(idx);
+ if(art->threadingLevel()>1) {
+ oldRef=art->idRef();
+ ref=findReference(art);
+ if(ref) {
+ // this method is called from the nntp-thread!!!
+ #ifndef NDEBUG
+ qDebug("knode: %d: old %d new %d",art->id(), oldRef, art->idRef());
+ #endif
+ resortCnt++;
+ art->setChanged(true);
+ }
+ }
+ }
+
+
+ for(idx=start; idx<end; idx++) {
+
+ art=at(idx);
+
+ if(art->idRef()==-1 && !art->references()->isEmpty() ){ //hdr has references
+ refCnt++;
+ if(findReference(art))
+ foundCnt++;
+ }
+ else {
+ if(art->subject()->isReply()) {
+ art->setIdRef(0); //hdr has no references
+ art->setThreadingLevel(0);
+ }
+ else if(art->idRef()==-1)
+ refCnt++;
+ }
+
+ if (timer.elapsed() > 200) { // don't flicker
+ timer.restart();
+ if(client)
+ client->updatePercentage(30+((foundCnt)*70)/cnt);
+ }
+ }
+
+
+ if(foundCnt<refCnt) { // some references could not been found
+
+ //try to sort by subject
+ KNRemoteArticle *oldest;
+ KNRemoteArticle::List list;
+
+ for(idx=start; idx<end; idx++) {
+
+ art=at(idx);
+
+ if(art->idRef()==-1) { //for all not sorted headers
+
+ list.clear();
+ list.append(art);
+
+ //find all headers with same subject
+ for(int idx2=0; idx2<length(); idx2++)
+ if(at(idx2)==art) continue;
+ else if(at(idx2)->subject()==art->subject())
+ list.append(at(idx2));
+
+ if(list.count()==1) {
+ art->setIdRef(0);
+ art->setThreadingLevel(6);
+ bySubCnt++;
+ }
+ else {
+
+ //find oldest
+ oldest=list.first();
+ for ( KNRemoteArticle::List::Iterator it = list.begin(); it != list.end(); ++it )
+ if ( (*it)->date()->unixTime() < oldest->date()->unixTime() )
+ oldest = (*it);
+
+ //oldest gets idRef 0
+ if(oldest->idRef()==-1) bySubCnt++;
+ oldest->setIdRef(0);
+ oldest->setThreadingLevel(6);
+
+ for ( KNRemoteArticle::List::Iterator it = list.begin(); it != list.end(); ++it ) {
+ if ( (*it) == oldest )
+ continue;
+ if ( (*it)->idRef() == -1 || ( (*it)->idRef() != -1 && (*it)->threadingLevel() == 6 ) ) {
+ (*it)->setIdRef(oldest->id());
+ (*it)->setThreadingLevel(6);
+ if ( (*it)->id() >= at(start)->id() )
+ bySubCnt++;
+ }
+ }
+ }
+ }
+
+ if (timer.elapsed() > 200) { // don't flicker
+ timer.restart();
+ if (client) client->updatePercentage(30+((bySubCnt+foundCnt)*70)/cnt);
+ }
+ }
+ }
+
+ //all not found items get refID 0
+ for (int idx=start; idx<end; idx++){
+ art=at(idx);
+ if(art->idRef()==-1) {
+ art->setIdRef(0);
+ art->setThreadingLevel(6); //was 0 !!!
+ }
+ }
+
+ //check for loops in threads
+ int startId;
+ bool isLoop;
+ int iterationCount;
+ for (int idx=start; idx<end; idx++){
+ art=at(idx);
+ startId=art->id();
+ isLoop=false;
+ iterationCount=0;
+ while(art->idRef()!=0 && !isLoop && (iterationCount < end)) {
+ art=byId(art->idRef());
+ isLoop=(art->id()==startId);
+ iterationCount++;
+ }
+
+ if(isLoop) {
+ // this method is called from the nntp-thread!!!
+ #ifndef NDEBUG
+ qDebug("knode: Sorting : loop in %d",startId);
+ #endif
+ art=at(idx);
+ art->setIdRef(0);
+ art->setThreadingLevel(0);
+ }
+ }
+
+ // propagate ignored/watched flags to new headers
+ for(int idx=start; idx<end; idx++) {
+ art=at(idx);
+ int idRef=art->idRef();
+ int tmpIdRef;
+
+ if(idRef!=0) {
+ while(idRef!=0) {
+ art=byId(idRef);
+ tmpIdRef=art->idRef();
+ idRef = (idRef!=tmpIdRef)? tmpIdRef : 0;
+
+ }
+ if (art) {
+ if (art->isIgnored()) {
+ at(idx)->setIgnored(true);
+ ++i_gnoreCount;
+ }
+ at(idx)->setWatched(art->isWatched());
+ }
+ }
+ }
+
+ // this method is called from the nntp-thread!!!
+#ifndef NDEBUG
+ qDebug("knode: Sorting : %d headers resorted", resortCnt);
+ qDebug("knode: Sorting : %d references of %d found", foundCnt, refCnt);
+ qDebug("knode: Sorting : %d references of %d sorted by subject", bySubCnt, refCnt);
+#endif
+}
+
+
+KNRemoteArticle* KNGroup::findReference(KNRemoteArticle *a)
+{
+ int found=false;
+ QCString ref_mid;
+ int ref_nr=0;
+ KNRemoteArticle *ref_art=0;
+
+ ref_mid=a->references()->first();
+
+ while(!found && !ref_mid.isNull() && ref_nr < SORT_DEPTH) {
+ ref_art=byMessageId(ref_mid);
+ if(ref_art) {
+ found=true;
+ a->setThreadingLevel(ref_nr+1);
+ a->setIdRef(ref_art->id());
+ }
+ ref_nr++;
+ ref_mid=a->references()->next();
+ }
+
+ return ref_art;
+}
+
+
+void KNGroup::scoreArticles(bool onlynew)
+{
+ kdDebug(5003) << "KNGroup::scoreArticles()" << endl;
+ int len=length(),
+ todo=(onlynew)? lastFetchCount():length();
+
+ if (todo) {
+ // reset the notify collection
+ delete KNScorableArticle::notifyC;
+ KNScorableArticle::notifyC = 0;
+
+ kdDebug(5003) << "scoring " << newCount() << " articles" << endl;
+ kdDebug(5003) << "(total " << length() << " article in group)" << endl;
+ knGlobals.top->setCursorBusy(true);
+ knGlobals.setStatusMsg(i18n(" Scoring..."));
+
+ int defScore;
+ KScoringManager *sm = knGlobals.scoringManager();
+ sm->initCache(groupname());
+ for(int idx=0; idx<todo; idx++) {
+ KNRemoteArticle *a = at(len-idx-1);
+ if ( !a ) {
+ kdWarning( 5003 ) << "found no article at " << len-idx-1 << endl;
+ continue;
+ }
+
+ defScore = 0;
+ if (a->isIgnored())
+ defScore = knGlobals.configManager()->scoring()->ignoredThreshold();
+ else if (a->isWatched())
+ defScore = knGlobals.configManager()->scoring()->watchedThreshold();
+
+ if (a->score() != defScore) {
+ a->setScore(defScore);
+ a->setChanged(true);
+ }
+
+ bool read = a->isRead();
+
+ KNScorableArticle sa(a);
+ sm->applyRules(sa);
+
+ if ( a->isRead() != read && !read )
+ incReadCount();
+ }
+
+ knGlobals.setStatusMsg(QString::null);
+ knGlobals.top->setCursorBusy(false);
+
+ //kdDebug(5003) << KNScorableArticle::notifyC->collection() << endl;
+ if (KNScorableArticle::notifyC)
+ KNScorableArticle::notifyC->displayCollection(knGlobals.topWidget);
+ }
+}
+
+
+void KNGroup::reorganize()
+{
+ kdDebug(5003) << "KNGroup::reorganize()" << endl;
+
+ knGlobals.top->setCursorBusy(true);
+ knGlobals.setStatusMsg(i18n(" Reorganizing headers..."));
+
+ for(int idx=0; idx<length(); idx++) {
+ KNRemoteArticle *a = at(idx);
+ Q_ASSERT( a );
+ a->setId(idx+1); //new ids
+ a->setIdRef(-1);
+ a->setThreadingLevel(0);
+ }
+
+ buildThreads(length());
+ saveStaticData(length(), true);
+ saveDynamicData(length(), true);
+ knGlobals.top->headerView()->repaint();
+ knGlobals.setStatusMsg(QString::null);
+ knGlobals.top->setCursorBusy(false);
+}
+
+
+void KNGroup::updateThreadInfo()
+{
+ KNRemoteArticle *ref;
+ bool brokenThread=false;
+
+ for(int idx=0; idx<length(); idx++) {
+ at(idx)->setUnreadFollowUps(0);
+ at(idx)->setNewFollowUps(0);
+ }
+
+ for(int idx=0; idx<length(); idx++) {
+ int idRef=at(idx)->idRef();
+ int tmpIdRef;
+ int iterCount=1; // control iteration count to avoid infinite loops
+ while((idRef!=0) && (iterCount <= length())) {
+ ref=byId(idRef);
+ if(!ref) {
+ brokenThread=true;
+ break;
+ }
+
+ if(!at(idx)->isRead()) {
+ ref->incUnreadFollowUps();
+ if(at(idx)->isNew()) ref->incNewFollowUps();
+ }
+ tmpIdRef=ref->idRef();
+ idRef= (idRef!=tmpIdRef) ? ref->idRef() : 0;
+ iterCount++;
+ }
+ if(iterCount > length())
+ brokenThread=true;
+ if(brokenThread) break;
+ }
+
+ if(brokenThread) {
+ kdWarning(5003) << "KNGroup::updateThreadInfo() : Found broken threading infos! Restoring ..." << endl;
+ reorganize();
+ updateThreadInfo();
+ }
+}
+
+
+void KNGroup::showProperties()
+{
+ if(!i_dentity) i_dentity=new KNConfig::Identity(false);
+ KNGroupPropDlg *d=new KNGroupPropDlg(this, knGlobals.topWidget);
+
+ if(d->exec())
+ if(d->nickHasChanged())
+ l_istItem->setText(0, name());
+
+ if(i_dentity->isEmpty()) {
+ delete i_dentity;
+ i_dentity=0;
+ }
+
+ delete d;
+}
+
+
+int KNGroup::statThrWithNew()
+{
+ int cnt=0;
+ for(int i=0; i<length(); i++)
+ if( (at(i)->idRef()==0) && (at(i)->hasNewFollowUps()) ) cnt++;
+ return cnt;
+}
+
+
+int KNGroup::statThrWithUnread()
+{
+ int cnt=0;
+ for(int i=0; i<length(); i++)
+ if( (at(i)->idRef()==0) && (at(i)->hasUnreadFollowUps()) ) cnt++;
+ return cnt;
+}
+
+QString KNGroup::prepareForExecution()
+{
+ if (knGlobals.groupManager()->loadHeaders(this))
+ return QString::null;
+ else
+ return i18n("Cannot load saved headers: %1").arg(groupname());
+}
+
+//***************************************************************************
+
+void KNGroup::dynDataVer0::setData(KNRemoteArticle *a)
+{
+ id=a->id();
+ idRef=a->idRef();
+ thrLevel=a->threadingLevel();
+ read=a->getReadFlag();
+ score=a->score();
+}
+
+
+void KNGroup::dynDataVer0::getData(KNRemoteArticle *a)
+{
+ a->setId(id);
+ a->setIdRef(idRef);
+ a->setRead(read);
+ a->setThreadingLevel(thrLevel);
+ a->setScore(score);
+}
+
+
+void KNGroup::dynDataVer1::setData(KNRemoteArticle *a)
+{
+ id=a->id();
+ idRef=a->idRef();
+ thrLevel=a->threadingLevel();
+ read=a->getReadFlag();
+ score=a->score();
+ ignoredWatched = 0;
+ if (a->isWatched())
+ ignoredWatched = 1;
+ else if (a->isIgnored())
+ ignoredWatched = 2;
+}
+
+
+void KNGroup::dynDataVer1::getData(KNRemoteArticle *a)
+{
+ a->setId(id);
+ a->setIdRef(idRef);
+ a->setRead(read);
+ a->setThreadingLevel(thrLevel);
+ a->setScore(score);
+ a->setWatched(ignoredWatched==1);
+ a->setIgnored(ignoredWatched==2);
+}
+
+
+KNConfig::Cleanup * KNGroup::activeCleanupConfig()
+{
+ if (!cleanupConfig()->useDefault())
+ return cleanupConfig();
+ return account()->activeCleanupConfig();
+}
diff --git a/knode/kngroup.h b/knode/kngroup.h
new file mode 100644
index 000000000..8158e192b
--- /dev/null
+++ b/knode/kngroup.h
@@ -0,0 +1,200 @@
+/*
+ kngroup.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNGROUP_H
+#define KNGROUP_H
+
+#include "knarticlecollection.h"
+#include "knjobdata.h"
+#include "knarticle.h"
+
+class QStrList;
+
+class KNProtocolClient;
+class KNNntpAccount;
+
+namespace KNConfig {
+ class Identity;
+ class Cleanup;
+}
+
+
+class KNGroup : public KNArticleCollection , public KNJobItem {
+
+ public:
+ enum Status { unknown=0, readOnly=1, postingAllowed=2, moderated=3 };
+
+ KNGroup(KNCollection *p=0);
+ ~KNGroup();
+
+ /** type */
+ collectionType type() { return CTgroup; }
+
+ /** list-item handling */
+ void updateListItem();
+
+ /** info */
+ QString path();
+ bool readInfo(const QString &confPath);
+ void saveInfo();
+
+ /** name */
+ bool hasName() const { return (!n_ame.isEmpty()); }
+ const QString& name();
+ const QString& groupname() { return g_roupname; }
+ void setGroupname(const QString &s) { g_roupname=s; }
+ const QString& description() { return d_escription; }
+ void setDescription(const QString &s) { d_escription=s; }
+
+ /** count + numbers */
+ int newCount() const { return n_ewCount; }
+ void setNewCount(int i) { n_ewCount=i; }
+ void incNewCount(int i=1) { n_ewCount+=i; }
+ void decNewCount(int i=1) { n_ewCount-=i; }
+ int firstNewIndex() const { return f_irstNew; }
+ void setFirstNewIndex(int i) { f_irstNew=i; }
+
+ int lastFetchCount() const { return l_astFetchCount; }
+ void setLastFetchCount(int i) { l_astFetchCount=i; }
+
+ int readCount()const { return r_eadCount; }
+ void setReadCount(int i) { r_eadCount=i; }
+ void incReadCount(int i=1) { r_eadCount+=i; }
+ void decReadCount(int i=1) { r_eadCount-=i; }
+
+ int firstNr() const { return f_irstNr; }
+ void setFirstNr(int i) { f_irstNr=i; }
+ int lastNr() const { return l_astNr; }
+ void setLastNr(int i) { l_astNr=i; }
+ int maxFetch() const { return m_axFetch; }
+ void setMaxFetch(int i) { m_axFetch=i; }
+
+ int statThrWithNew();
+ int statThrWithUnread();
+
+ /** article access */
+ KNRemoteArticle* at(int i) { return static_cast<KNRemoteArticle*> (KNArticleCollection::at(i)); }
+ KNRemoteArticle* byId(int id) { return static_cast<KNRemoteArticle*> (KNArticleCollection::byId(id)); }
+ KNRemoteArticle* byMessageId(const QCString &mId)
+ { return static_cast<KNRemoteArticle*> (KNArticleCollection::byMessageId(mId)); }
+ /** load + save */
+ bool loadHdrs();
+ bool unloadHdrs(bool force=true);
+ void insortNewHeaders(QStrList *hdrs, QStrList *hdrfmt, KNProtocolClient *client=0);
+ int saveStaticData(int cnt,bool ovr=false);
+ void saveDynamicData(int cnt,bool ovr=false);
+ void syncDynamicData();
+
+ /** mark articles with this id as read when we later load the headers / fetch new articles */
+ void appendXPostID(const QString &id);
+ void processXPostBuffer(bool deleteAfterwards);
+
+ /** article handling */
+ void updateThreadInfo();
+ void reorganize();
+ void scoreArticles(bool onlynew=true);
+
+ /** locking */
+ bool isLocked() { return l_ocked; }
+ void setLocked(bool l) { l_ocked=l; }
+
+ QString prepareForExecution();
+
+ /** charset-handling */
+ const QCString defaultCharset() { return d_efaultChSet; }
+ void setDefaultCharset(const QCString &s) { d_efaultChSet=s; }
+ bool useCharset() { return ( u_seCharset && !d_efaultChSet.isEmpty() ); }
+ void setUseCharset(bool b) { u_seCharset=b; }
+
+ // misc
+ KNNntpAccount* account();
+ KNConfig::Identity* identity()const { return i_dentity; }
+ void setIdentity(KNConfig::Identity *i) { i_dentity=i; }
+ Status status()const { return s_tatus; }
+ void setStatus(Status s) { s_tatus=s; }
+ void showProperties();
+
+ // cleanup configuration
+ KNConfig::Cleanup *cleanupConfig() const { return mCleanupConf; }
+ KNConfig::Cleanup *activeCleanupConfig();
+
+
+ protected:
+ void buildThreads(int cnt, KNProtocolClient *client=0);
+ KNRemoteArticle* findReference(KNRemoteArticle *a);
+
+ int n_ewCount,
+ l_astFetchCount,
+ r_eadCount,
+ i_gnoreCount,
+ f_irstNr,
+ l_astNr,
+ m_axFetch,
+ d_ynDataFormat,
+ f_irstNew;
+
+ QCString d_efaultChSet;
+ QString g_roupname,
+ d_escription;
+
+ bool l_ocked,
+ u_seCharset;
+
+ Status s_tatus;
+
+ QStringList c_rosspostIDBuffer;
+
+ /** Optional headers provided by the XOVER command
+ * These headers will be saved within the static data
+ */
+ QStrList mOptionalHeaders;
+
+ KNConfig::Identity *i_dentity;
+ KNConfig::Cleanup *mCleanupConf;
+
+ class dynDataVer0 {
+
+ public:
+ dynDataVer0() { id=-1; idRef=-1; read=0; thrLevel=0; score=50; }
+ ~dynDataVer0() {}
+ void setData(KNRemoteArticle *a);
+ void getData(KNRemoteArticle *a);
+
+ int id;
+ int idRef;
+ bool read;
+ short thrLevel, score;
+ };
+
+ class dynDataVer1 {
+
+ public:
+ dynDataVer1() { id=-1; idRef=-1; read=0; thrLevel=0; score=0, ignoredWatched=0; }
+ void setData(KNRemoteArticle *a);
+ void getData(KNRemoteArticle *a);
+
+ int id;
+ int idRef;
+ bool read;
+ short thrLevel, score;
+ char ignoredWatched;
+ };
+
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/kngroupbrowser.cpp b/knode/kngroupbrowser.cpp
new file mode 100644
index 000000000..17342a4be
--- /dev/null
+++ b/knode/kngroupbrowser.cpp
@@ -0,0 +1,481 @@
+/*
+ kngroupbrowser.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qtimer.h>
+#include <qapplication.h>
+
+#include <kseparator.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <klineedit.h>
+
+#include "knnetaccess.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "knnntpaccount.h"
+#include "kngroupbrowser.h"
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+
+KNGroupBrowser::KNGroupBrowser(QWidget *parent, const QString &caption, KNNntpAccount *a,
+ int buttons, bool newCBact, const QString &user1, const QString &user2) :
+ KDialogBase( parent, 0L, true, caption, buttons | Help | Ok | Cancel, Ok, true, user1, user2 ),
+ incrementalFilter(false), a_ccount(a)
+{
+ refilterTimer = new QTimer();
+
+ allList=new QSortedList<KNGroupInfo>;
+ allList->setAutoDelete(true);
+ matchList=new QSortedList<KNGroupInfo>;
+ matchList->setAutoDelete(false);
+
+ //create Widgets
+ page=new QWidget(this);
+ setMainWidget(page);
+
+ filterEdit=new KLineEdit(page);
+ QLabel *l=new QLabel(filterEdit,i18n("S&earch:"), page);
+ noTreeCB=new QCheckBox(i18n("Disable &tree view"), page);
+ noTreeCB->setChecked(false);
+ subCB=new QCheckBox(i18n("&Subscribed only"), page);
+ subCB->setChecked(false);
+ newCB=new QCheckBox(i18n("&New only"), page);
+ if (!newCBact)
+ newCB->hide();
+ newCB->setChecked(false);
+ KSeparator *sep=new KSeparator(KSeparator::HLine, page);
+
+ QFont fnt=font();
+ fnt.setBold(true);
+ leftLabel=new QLabel(i18n("Loading groups..."), page);
+ rightLabel=new QLabel(page);
+ leftLabel->setFont(fnt);
+ rightLabel->setFont(fnt);
+
+ pmGroup=knGlobals.configManager()->appearance()->icon(KNConfig::Appearance::group);
+ pmNew=knGlobals.configManager()->appearance()->icon(KNConfig::Appearance::redBall);
+ pmRight=BarIconSet( QApplication::reverseLayout()? "back": "forward");
+ pmLeft=BarIconSet( QApplication::reverseLayout() ? "forward" : "back");
+
+ arrowBtn1=new QPushButton(page);
+ arrowBtn1->setEnabled(false);
+ arrowBtn2=new QPushButton(page);
+ arrowBtn2->setEnabled(false);
+ arrowBtn1->setIconSet(pmRight);
+ arrowBtn2->setIconSet(pmLeft);
+ arrowBtn1->setFixedSize(35,30);
+ arrowBtn2->setFixedSize(35,30);
+
+ groupView=new QListView(page);
+ groupView->setRootIsDecorated(true);
+ groupView->addColumn(i18n("Name"));
+ groupView->addColumn(i18n("Description"));
+ groupView->setTreeStepSize(15);
+
+ connect(groupView, SIGNAL(doubleClicked(QListViewItem*)),
+ this, SLOT(slotItemDoubleClicked(QListViewItem*)));
+
+ //layout
+ QGridLayout *topL=new QGridLayout(page,3,1,0,5);
+ QHBoxLayout *filterL=new QHBoxLayout(10);
+ QVBoxLayout *arrL=new QVBoxLayout(10);
+ listL=new QGridLayout(2, 3, 5);
+
+ topL->addLayout(filterL, 0,0);
+ topL->addWidget(sep,1,0);
+ topL->addLayout(listL, 2,0);
+
+ filterL->addWidget(l);
+ filterL->addWidget(filterEdit, 1);
+ filterL->addWidget(noTreeCB);
+ filterL->addWidget(subCB);
+ if (newCBact)
+ filterL->addWidget(newCB);
+
+ listL->addWidget(leftLabel, 0,0);
+ listL->addWidget(rightLabel, 0,2);
+ listL->addWidget(groupView, 1,0);
+ listL->addLayout(arrL, 1,1);
+ listL->setRowStretch(1,1);
+ listL->setColStretch(0,5);
+ listL->setColStretch(2,2);
+
+ arrL->addWidget(arrowBtn1, AlignCenter);
+ arrL->addWidget(arrowBtn2, AlignCenter);
+
+ //connect
+ connect(filterEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotFilterTextChanged(const QString&)));
+ connect(groupView, SIGNAL(expanded(QListViewItem*)),
+ SLOT(slotItemExpand(QListViewItem*)));
+
+ connect(refilterTimer, SIGNAL(timeout()), SLOT(slotRefilter()));
+ connect(noTreeCB, SIGNAL(clicked()), SLOT(slotTreeCBToggled()));
+ connect(subCB, SIGNAL(clicked()), SLOT(slotSubCBToggled()));
+ connect(newCB, SIGNAL(clicked()), SLOT(slotNewCBToggled()));
+
+ enableButton(User1,false);
+ enableButton(User2,false);
+
+ filterEdit->setFocus();
+
+ QTimer::singleShot(2, this, SLOT(slotLoadList()));
+}
+
+
+KNGroupBrowser::~KNGroupBrowser()
+{
+
+ knGlobals.netAccess()->stopJobsNntp(KNJobData::JTLoadGroups);
+ knGlobals.netAccess()->stopJobsNntp(KNJobData::JTFetchGroups);
+ knGlobals.netAccess()->stopJobsNntp(KNJobData::JTCheckNewGroups);
+
+ delete matchList;
+ delete allList;
+ delete refilterTimer;
+}
+
+
+void KNGroupBrowser::slotReceiveList(KNGroupListData* d)
+{
+ enableButton(User1,true);
+ enableButton(User2,true);
+
+ if (d) { // d==0 if something has gone wrong...
+ delete allList;
+ allList = d->extractList();
+ incrementalFilter=false;
+ slotRefilter();
+ }
+}
+
+
+void KNGroupBrowser::changeItemState(const KNGroupInfo &gi, bool s)
+{
+ QListViewItemIterator it(groupView);
+
+ for( ; it.current(); ++it)
+ if (it.current()->isSelectable() && (static_cast<CheckItem*>(it.current())->info==gi))
+ static_cast<CheckItem*>(it.current())->setChecked(s);
+}
+
+
+bool KNGroupBrowser::itemInListView(QListView *view, const KNGroupInfo &gi)
+{
+ if(!view) return false;
+ QListViewItemIterator it(view);
+
+ for( ; it.current(); ++it)
+ if(static_cast<GroupItem*>(it.current())->info==gi)
+ return true;
+
+ return false;
+}
+
+
+void KNGroupBrowser::createListItems(QListViewItem *parent)
+{
+ QString prefix, tlgn, compare;
+ QListViewItem *it;
+ CheckItem *cit;
+ int colon;
+ bool expandit=false;
+
+ if(parent) {
+ QListViewItem *p=parent;
+ while(p) {
+ prefix.prepend(p->text(0));
+ p=p->parent();
+ }
+ }
+
+ for(KNGroupInfo *gn=matchList->first(); gn; gn=matchList->next()) {
+
+ if(!prefix.isEmpty() && !gn->name.startsWith(prefix))
+ if(!compare.isNull())
+ break;
+ else
+ continue;
+
+ compare=gn->name.mid(prefix.length());
+
+ if(!expandit || !compare.startsWith(tlgn)) {
+ if((colon=compare.find('.'))!=-1) {
+ colon++;
+ expandit=true;
+ } else {
+ colon=compare.length();
+ expandit=false;
+ }
+
+ tlgn = compare.left(colon);
+
+ if(expandit) {
+ if(parent)
+ it=new QListViewItem(parent, tlgn);
+ else
+ it=new QListViewItem(groupView, tlgn);
+
+ it->setSelectable(false);
+ it->setExpandable(true);
+ }
+ else {
+ if(parent)
+ cit=new CheckItem(parent, *gn, this);
+ else
+ cit=new CheckItem(groupView, *gn, this);
+ updateItemState(cit);
+ }
+ }
+ }
+}
+
+
+void KNGroupBrowser::removeListItem(QListView *view, const KNGroupInfo &gi)
+{
+ if(!view) return;
+ QListViewItemIterator it(view);
+
+ for( ; it.current(); ++it)
+ if(static_cast<GroupItem*>(it.current())->info==gi) {
+ delete it.current();
+ break;
+ }
+}
+
+
+void KNGroupBrowser::slotLoadList()
+{
+ emit(loadList(a_ccount));
+}
+
+
+void KNGroupBrowser::slotItemExpand(QListViewItem *it)
+{
+ if(!it) return;
+
+ if(it->childCount()) {
+ kdDebug(5003) << "KNGroupBrowser::slotItemExpand() : has already been expanded, returning" << endl;
+ return;
+ }
+
+ createListItems(it);
+
+ // center the item - smart scrolling
+ delayedCenter = -1;
+ int y = groupView->itemPos(it);
+ int h = it->height();
+
+ if ( (y+h*4+5) >= (groupView->contentsY()+groupView->visibleHeight()) )
+ {
+ groupView->ensureVisible(groupView->contentsX(), y+h/2, 0, h/2);
+ delayedCenter = y+h/2;
+ QTimer::singleShot(300, this, SLOT(slotCenterDelayed()));
+ }
+}
+
+
+void KNGroupBrowser::slotCenterDelayed()
+{
+ if (delayedCenter != -1)
+ groupView->ensureVisible(groupView->contentsX(), delayedCenter, 0, groupView->visibleHeight()/2);
+}
+
+
+void KNGroupBrowser::slotItemDoubleClicked(QListViewItem *it)
+{
+ if (it && (it->childCount()==0)) static_cast<CheckItem*>(it)->setOn(!static_cast<CheckItem*>(it)->isOn());
+}
+
+
+#define MIN_FOR_TREE 200
+void KNGroupBrowser::slotFilter(const QString &txt)
+{
+ QString filtertxt = txt.lower();
+ QRegExp reg(filtertxt, false, false);
+ CheckItem *cit=0;
+
+ bool notCheckSub = !subCB->isChecked();
+ bool notCheckNew = !newCB->isChecked();
+ bool notCheckStr = (filtertxt.isEmpty());
+
+ bool isRegexp = filtertxt.contains(QRegExp("[^a-z0-9\\-\\+.]"));
+
+ bool doIncrementalUpdate = (!isRegexp && incrementalFilter && (filtertxt.left(lastFilter.length())==lastFilter));
+
+ if (doIncrementalUpdate) {
+ QSortedList<KNGroupInfo> *tempList = new QSortedList<KNGroupInfo>();
+ tempList->setAutoDelete(false);
+
+ for(KNGroupInfo *g=matchList->first(); g; g=matchList->next()) {
+ if ((notCheckSub||g->subscribed)&&
+ (notCheckNew||g->newGroup)&&
+ (notCheckStr||(g->name.find(filtertxt)!=-1)))
+ tempList->append(g);
+ }
+
+ delete matchList;
+ matchList=tempList;
+ } else {
+ matchList->clear();
+
+ for(KNGroupInfo *g=allList->first(); g; g=allList->next()) {
+ if ((notCheckSub||g->subscribed)&&
+ (notCheckNew||g->newGroup)&&
+ (notCheckStr||(isRegexp? (reg.search(g->name,0) != -1):(g->name.find(filtertxt)!=-1))))
+ matchList->append(g);
+ }
+ }
+
+ groupView->clear();
+
+ if((matchList->count() < MIN_FOR_TREE) || noTreeCB->isChecked()) {
+ for(KNGroupInfo *g=matchList->first(); g; g=matchList->next()) {
+ cit=new CheckItem(groupView, *g, this);
+ updateItemState(cit);
+ }
+ } else {
+ createListItems();
+ }
+
+ lastFilter = filtertxt;
+ incrementalFilter = !isRegexp;
+
+ leftLabel->setText(i18n("Groups on %1: (%2 displayed)").arg(a_ccount->name()).arg(matchList->count()));
+
+ arrowBtn1->setEnabled(false);
+ arrowBtn2->setEnabled(false);
+}
+
+
+void KNGroupBrowser::slotTreeCBToggled()
+{
+ incrementalFilter=false;
+ slotRefilter();
+}
+
+
+void KNGroupBrowser::slotSubCBToggled()
+{
+ incrementalFilter=subCB->isChecked();
+ slotRefilter();
+}
+
+
+void KNGroupBrowser::slotNewCBToggled()
+{
+ incrementalFilter=newCB->isChecked();
+ slotRefilter();
+}
+
+
+void KNGroupBrowser::slotFilterTextChanged(const QString &)
+{
+ if (subCB->isChecked() || newCB->isChecked())
+ slotRefilter();
+ else
+ refilterTimer->start(200,true);
+}
+
+
+void KNGroupBrowser::slotRefilter()
+{
+ refilterTimer->stop();
+ slotFilter(filterEdit->text());
+}
+
+
+//=======================================================================================
+
+
+KNGroupBrowser::CheckItem::CheckItem(QListView *v, const KNGroupInfo &gi, KNGroupBrowser *b) :
+ QCheckListItem(v, gi.name, QCheckListItem::CheckBox), info(gi), browser(b)
+{
+ QString des(gi.description);
+ if (gi.status == KNGroup::moderated) {
+ setText(0,gi.name+" (m)");
+ if (!des.upper().contains(i18n("moderated").upper()))
+ des+=i18n(" (moderated)");
+ }
+ setText(1,des);
+}
+
+
+KNGroupBrowser::CheckItem::CheckItem(QListViewItem *i, const KNGroupInfo &gi, KNGroupBrowser *b) :
+ QCheckListItem(i, gi.name, QCheckListItem::CheckBox), info(gi), browser(b)
+{
+ QString des(gi.description);
+ if (gi.status == KNGroup::moderated) {
+ setText(0,gi.name+" (m)");
+ if (!des.upper().contains(i18n("moderated").upper()))
+ des+=i18n(" (moderated)");
+ }
+ setText(1,des);
+}
+
+
+KNGroupBrowser::CheckItem::~CheckItem()
+{
+}
+
+
+void KNGroupBrowser::CheckItem::setChecked(bool c)
+{
+ KNGroupBrowser *b=browser;
+ browser=0;
+ QCheckListItem::setOn(c);
+ browser=b;
+}
+
+
+void KNGroupBrowser::CheckItem::stateChange(bool s)
+{
+ if(browser) {
+ kdDebug(5003) << "KNGroupBrowser::CheckItem::stateChange()" << endl;
+ browser->itemChangedState(this, s);
+ }
+}
+
+
+//=======================================================================================
+
+
+KNGroupBrowser::GroupItem::GroupItem(QListView *v, const KNGroupInfo &gi)
+ : QListViewItem(v, gi.name), info(gi)
+{
+ if (gi.status == KNGroup::moderated)
+ setText(0,gi.name+" (m)");
+}
+
+
+KNGroupBrowser::GroupItem::GroupItem(QListViewItem *i, const KNGroupInfo &gi)
+ : QListViewItem(i, gi.name), info(gi)
+{
+}
+
+
+KNGroupBrowser::GroupItem::~GroupItem()
+{
+}
+
+
+//-----------------------------------------
+
+#include "kngroupbrowser.moc"
diff --git a/knode/kngroupbrowser.h b/knode/kngroupbrowser.h
new file mode 100644
index 000000000..eb633c8b9
--- /dev/null
+++ b/knode/kngroupbrowser.h
@@ -0,0 +1,117 @@
+/*
+ kngroupbrowser.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNGROUPBROWSER_H
+#define KNGROUPBROWSER_H
+
+#include <qlistview.h>
+
+#include <kdialogbase.h>
+
+#include "kngroupmanager.h"
+
+class KLineEdit;
+class QCheckBox;
+class QLayout;
+class QLabel;
+class QGridLayout;
+
+class KNNntpAccount;
+
+
+class KNGroupBrowser : public KDialogBase {
+
+ Q_OBJECT
+
+ public:
+ class CheckItem : public QCheckListItem {
+
+ public:
+ CheckItem(QListView *v, const KNGroupInfo &gi, KNGroupBrowser *b);
+ CheckItem(QListViewItem *i, const KNGroupInfo &gi, KNGroupBrowser *b);
+ ~CheckItem();
+ void setChecked(bool c);
+
+ KNGroupInfo info;
+
+ protected:
+ void stateChange(bool s);
+ KNGroupBrowser *browser;
+ };
+
+ class GroupItem : public QListViewItem {
+
+ public:
+ GroupItem(QListView *v, const KNGroupInfo &gi);
+ GroupItem(QListViewItem *i, const KNGroupInfo &gi);
+ ~GroupItem();
+
+ KNGroupInfo info;
+ };
+
+ KNGroupBrowser(QWidget *parent, const QString &caption, KNNntpAccount *a, int buttons=0,
+ bool newCBact=false, const QString &user1=QString::null, const QString &user2=QString::null);
+ ~KNGroupBrowser();
+
+ KNNntpAccount* account()const { return a_ccount; }
+ virtual void itemChangedState(CheckItem *it, bool s)=0;
+
+ public slots:
+ void slotReceiveList(KNGroupListData* d);
+
+ signals:
+ void loadList(KNNntpAccount *a);
+
+ protected:
+ virtual void updateItemState(CheckItem *it)=0;
+ void changeItemState(const KNGroupInfo &gi, bool s);
+ bool itemInListView(QListView *view, const KNGroupInfo &gi);
+ void removeListItem(QListView *view, const KNGroupInfo &gi);
+ void createListItems(QListViewItem *parent=0);
+
+ QWidget *page;
+ QListView *groupView;
+ int delayedCenter;
+ KLineEdit *filterEdit;
+ QCheckBox *noTreeCB, *subCB, *newCB;
+ QPushButton *arrowBtn1, *arrowBtn2;
+ QPixmap pmGroup, pmNew;
+ QIconSet pmRight, pmLeft;
+ QGridLayout *listL;
+ QLabel *leftLabel, *rightLabel;
+ QTimer *refilterTimer;
+ QString lastFilter;
+ bool incrementalFilter;
+
+ KNNntpAccount *a_ccount;
+ QSortedList<KNGroupInfo> *allList, *matchList;
+
+ protected slots:
+ void slotLoadList();
+ void slotItemExpand(QListViewItem *it);
+ void slotCenterDelayed();
+ /** double click checks/unchecks (opens/closes) item */
+ void slotItemDoubleClicked(QListViewItem *it);
+ void slotFilter(const QString &txt);
+ void slotTreeCBToggled();
+ void slotSubCBToggled();
+ void slotNewCBToggled();
+ void slotFilterTextChanged(const QString &txt);
+ void slotRefilter();
+
+};
+
+#endif
diff --git a/knode/kngroupdialog.cpp b/knode/kngroupdialog.cpp
new file mode 100644
index 000000000..b983f724a
--- /dev/null
+++ b/knode/kngroupdialog.cpp
@@ -0,0 +1,335 @@
+/*
+ kngroupdialog.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kdatepicker.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <klineedit.h>
+
+#include "utilities.h"
+#include "knnntpaccount.h"
+#include "kngroupdialog.h"
+#include "knglobals.h"
+#include <qpushbutton.h>
+
+
+KNGroupDialog::KNGroupDialog(QWidget *parent, KNNntpAccount *a) :
+ KNGroupBrowser(parent, i18n("Subscribe to Newsgroups"),a, User1 | User2, true, i18n("New &List"), i18n("New &Groups...") )
+{
+ rightLabel->setText(i18n("Current changes:"));
+ subView=new QListView(page);
+ subView->addColumn(i18n("Subscribe To"));
+ unsubView=new QListView(page);
+ unsubView->addColumn(i18n("Unsubscribe From"));
+
+ QVBoxLayout *protL=new QVBoxLayout(3);
+ listL->addLayout(protL, 1,2);
+ protL->addWidget(subView);
+ protL->addWidget(unsubView);
+
+ dir1=right;
+ dir2=left;
+
+ connect(groupView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotItemSelected(QListViewItem*)));
+ connect(groupView, SIGNAL(selectionChanged()),
+ this, SLOT(slotSelectionChanged()));
+ connect(subView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotItemSelected(QListViewItem*)));
+ connect(unsubView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotItemSelected(QListViewItem*)));
+
+ connect(arrowBtn1, SIGNAL(clicked()), this, SLOT(slotArrowBtn1()));
+ connect(arrowBtn2, SIGNAL(clicked()), this, SLOT(slotArrowBtn2()));
+
+ KNHelper::restoreWindowSize("groupDlg", this, QSize(662,393)); // optimized for 800x600
+
+ setHelp("anc-fetch-group-list");
+}
+
+
+
+KNGroupDialog::~KNGroupDialog()
+{
+ KNHelper::saveWindowSize("groupDlg", this->size());
+}
+
+
+
+void KNGroupDialog::itemChangedState(CheckItem *it, bool s)
+{
+ kdDebug(5003) << "KNGroupDialog::itemChangedState()" << endl;
+ if(s){
+ if(itemInListView(unsubView, it->info)) {
+ removeListItem(unsubView, it->info);
+ setButtonDirection(btn2, right);
+ arrowBtn1->setEnabled(false);
+ arrowBtn2->setEnabled(true);
+ }
+ else {
+ new GroupItem(subView, it->info);
+ arrowBtn1->setEnabled(false);
+ arrowBtn2->setEnabled(false);
+ }
+ }
+ else {
+ if(itemInListView(subView, it->info)) {
+ removeListItem(subView, it->info);
+ setButtonDirection(btn1, right);
+ arrowBtn1->setEnabled(true);
+ arrowBtn2->setEnabled(false);
+ }
+ else {
+ new GroupItem(unsubView, it->info);
+ arrowBtn1->setEnabled(false);
+ arrowBtn2->setEnabled(false);
+ }
+ }
+}
+
+
+
+void KNGroupDialog::updateItemState(CheckItem *it)
+{
+ it->setChecked( (it->info.subscribed && !itemInListView(unsubView, it->info)) ||
+ (!it->info.subscribed && itemInListView(subView, it->info)) );
+
+ if((it->info.subscribed || it->info.newGroup) && it->pixmap(0)==0)
+ it->setPixmap(0, (it->info.newGroup)? pmNew:pmGroup);
+}
+
+
+
+void KNGroupDialog::toSubscribe(QSortedList<KNGroupInfo> *l)
+{
+ KNGroupInfo *info;
+ l->clear();
+ l->setAutoDelete(true);
+
+ bool moderated=false;
+ QListViewItemIterator it(subView);
+ for(; it.current(); ++it) {
+ info = new KNGroupInfo();
+ *info = ((static_cast<GroupItem*>(it.current()))->info);
+ l->append(info);
+ if (info->status==KNGroup::moderated)
+ moderated=true;
+ }
+ if (moderated) // warn the user
+ KMessageBox::information(knGlobals.topWidget,i18n("You have subscribed to a moderated newsgroup.\nYour articles will not appear in the group immediately.\nThey have to go through a moderation process."),
+ QString::null,"subscribeModeratedWarning");
+}
+
+
+
+void KNGroupDialog::toUnsubscribe(QStringList *l)
+{
+ l->clear();
+ QListViewItemIterator it(unsubView);
+ for(; it.current(); ++it)
+ l->append(((static_cast<GroupItem*>(it.current()))->info).name);
+}
+
+
+
+void KNGroupDialog::setButtonDirection(arrowButton b, arrowDirection d)
+{
+ QPushButton *btn=0;
+ if(b==btn1 && dir1!=d) {
+ btn=arrowBtn1;
+ dir1=d;
+ }
+ else if(b==btn2 && dir2!=d) {
+ btn=arrowBtn2;
+ dir2=d;
+ }
+
+ if(btn) {
+ if(d==right)
+ btn->setIconSet(pmRight);
+ else
+ btn->setIconSet(pmLeft);
+ }
+}
+
+
+
+void KNGroupDialog::slotItemSelected(QListViewItem *it)
+{
+ const QObject *s=sender();
+
+
+ if(s==subView) {
+ unsubView->clearSelection();
+ groupView->clearSelection();
+ arrowBtn2->setEnabled(false);
+ arrowBtn1->setEnabled(true);
+ setButtonDirection(btn1, left);
+ }
+ else if(s==unsubView) {
+ subView->clearSelection();
+ groupView->clearSelection();
+ arrowBtn1->setEnabled(false);
+ arrowBtn2->setEnabled(true);
+ setButtonDirection(btn2, left);
+ }
+ else {
+ CheckItem *cit;
+ subView->clearSelection();
+ unsubView->clearSelection();
+ cit=static_cast<CheckItem*>(it);
+ if(!cit->isOn() && !itemInListView(subView, cit->info) && !itemInListView(unsubView, cit->info)) {
+ arrowBtn1->setEnabled(true);
+ arrowBtn2->setEnabled(false);
+ setButtonDirection(btn1, right);
+ }
+ else if(cit->isOn() && !itemInListView(unsubView, cit->info) && !itemInListView(subView, cit->info)) {
+ arrowBtn2->setEnabled(true);
+ arrowBtn1->setEnabled(false);
+ setButtonDirection(btn2, right);
+ }
+ else {
+ arrowBtn1->setEnabled(false);
+ arrowBtn2->setEnabled(false);
+ }
+ }
+}
+
+
+
+void KNGroupDialog::slotSelectionChanged()
+{
+ if (!groupView->selectedItem())
+ arrowBtn1->setEnabled(false);
+}
+
+
+
+void KNGroupDialog::slotArrowBtn1()
+{
+ if(dir1==right) {
+ CheckItem *it=static_cast<CheckItem*>(groupView->selectedItem());
+ if (it) {
+ new GroupItem(subView, it->info);
+ it->setChecked(true);
+ }
+ }
+ else {
+ GroupItem *it=static_cast<GroupItem*>(subView->selectedItem());
+ if (it) {
+ changeItemState(it->info, false);
+ delete it;
+ }
+ }
+
+ arrowBtn1->setEnabled(false);
+}
+
+
+void KNGroupDialog::slotArrowBtn2()
+{
+ if(dir2==right) {
+ CheckItem *it=static_cast<CheckItem*>(groupView->selectedItem());
+ if (it) {
+ new GroupItem(unsubView, it->info);
+ it->setChecked(false);
+ }
+ }
+ else {
+ GroupItem *it=static_cast<GroupItem*>(unsubView->selectedItem());
+ if (it) {
+ changeItemState(it->info, true);
+ delete it;
+ }
+ }
+
+ arrowBtn2->setEnabled(false);
+}
+
+
+// new list
+void KNGroupDialog::slotUser1()
+{
+ leftLabel->setText(i18n("Downloading groups..."));
+ enableButton(User1,false);
+ enableButton(User2,false);
+ emit(fetchList(a_ccount));
+}
+
+
+// new groups
+void KNGroupDialog::slotUser2()
+{
+ QDate lastDate = a_ccount->lastNewFetch();
+ KDialogBase *dlg = new KDialogBase( this, 0L, true, i18n("New Groups"), Ok | Cancel, Ok);
+
+ QButtonGroup *btnGrp = new QButtonGroup(i18n("Check for New Groups"),dlg);
+ dlg->setMainWidget(btnGrp);
+ QGridLayout *topL = new QGridLayout(btnGrp,4,2,25,10);
+
+ QRadioButton *takeLast = new QRadioButton( i18n("Created since last check:"), btnGrp );
+ topL->addMultiCellWidget(takeLast, 0, 0, 0, 1);
+
+ QLabel *l = new QLabel(KGlobal::locale()->formatDate(lastDate, false),btnGrp);
+ topL->addWidget(l, 1, 1, Qt::AlignLeft);
+
+ connect(takeLast, SIGNAL(toggled(bool)), l, SLOT(setEnabled(bool)));
+
+ QRadioButton *takeCustom = new QRadioButton( i18n("Created since this date:"), btnGrp );
+ topL->addMultiCellWidget(takeCustom, 2, 2, 0, 1);
+
+ KDatePicker *dateSel = new KDatePicker(btnGrp, lastDate);
+ dateSel->setMinimumSize(dateSel->sizeHint());
+ topL->addWidget(dateSel, 3, 1, Qt::AlignLeft);
+
+ connect(takeCustom, SIGNAL(toggled(bool)), dateSel, SLOT(setEnabled(bool)));
+
+ takeLast->setChecked(true);
+ dateSel->setEnabled(false);
+
+ topL->addColSpacing(0,30);
+ dlg->disableResize();
+
+ if (dlg->exec()) {
+ if (takeCustom->isChecked())
+ lastDate = dateSel->date();
+ a_ccount->setLastNewFetch(QDate::currentDate());
+ leftLabel->setText(i18n("Checking for new groups..."));
+ enableButton(User1,false);
+ enableButton(User2,false);
+ filterEdit->clear();
+ subCB->setChecked(false);
+ newCB->setChecked(true);
+ emit(checkNew(a_ccount,lastDate));
+ incrementalFilter=false;
+ slotRefilter();
+ }
+
+ delete dlg;
+}
+
+
+//--------------------------------
+
+#include "kngroupdialog.moc"
diff --git a/knode/kngroupdialog.h b/knode/kngroupdialog.h
new file mode 100644
index 000000000..306a36810
--- /dev/null
+++ b/knode/kngroupdialog.h
@@ -0,0 +1,60 @@
+/*
+ kngroupdialog.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNGROUPDIALOG_H
+#define KNGROUPDIALOG_H
+
+#include "kngroupbrowser.h"
+
+
+class KNGroupDialog : public KNGroupBrowser {
+
+ Q_OBJECT
+
+ public:
+ KNGroupDialog(QWidget *parent, KNNntpAccount *a);
+ ~KNGroupDialog();
+
+ void toSubscribe(QSortedList<KNGroupInfo> *l);
+ void toUnsubscribe(QStringList *l);
+
+ protected:
+ enum arrowDirection { right, left };
+ enum arrowButton { btn1, btn2 };
+ void updateItemState(CheckItem *it);
+ void itemChangedState(CheckItem *it, bool s);
+ void setButtonDirection(arrowButton b, arrowDirection d);
+ QPushButton *newListBtn;
+ QListView *subView, *unsubView;
+ arrowDirection dir1, dir2;
+
+ protected slots:
+ void slotItemSelected(QListViewItem *it);
+ /** deactivates the button when a root item is selected */
+ void slotSelectionChanged();
+ void slotArrowBtn1();
+ void slotArrowBtn2();
+ /** new list */
+ void slotUser1();
+ /** new groups */
+ void slotUser2();
+
+ signals:
+ void fetchList(KNNntpAccount *a);
+ void checkNew(KNNntpAccount *a,QDate date);
+};
+
+#endif
diff --git a/knode/kngroupmanager.cpp b/knode/kngroupmanager.cpp
new file mode 100644
index 000000000..584896091
--- /dev/null
+++ b/knode/kngroupmanager.cpp
@@ -0,0 +1,704 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <qdir.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kcharsets.h>
+
+#include "articlewidget.h"
+#include "knmainwidget.h"
+#include "knarticlemanager.h"
+#include "kngroupdialog.h"
+#include "knnntpaccount.h"
+#include "knprotocolclient.h"
+#include "kncleanup.h"
+#include "knnetaccess.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "resource.h"
+#include "utilities.h"
+#include "knarticlewindow.h"
+#include "knmemorymanager.h"
+
+using namespace KNode;
+
+
+//=================================================================================
+
+// helper classes for the group selection dialog (getting the server's grouplist,
+// getting recently created groups)
+
+
+KNGroupInfo::KNGroupInfo()
+{
+}
+
+
+KNGroupInfo::KNGroupInfo(const QString &n_ame, const QString &d_escription, bool n_ewGroup, bool s_ubscribed, KNGroup::Status s_tatus)
+ : name(n_ame), description(d_escription), newGroup(n_ewGroup), subscribed(s_ubscribed),
+ status(s_tatus)
+{
+}
+
+
+KNGroupInfo::~KNGroupInfo()
+{
+}
+
+
+bool KNGroupInfo::operator== (const KNGroupInfo &gi2)
+{
+ return (name == gi2.name);
+}
+
+
+bool KNGroupInfo::operator< (const KNGroupInfo &gi2)
+{
+ return (name < gi2.name);
+}
+
+
+//===============================================================================
+
+
+KNGroupListData::KNGroupListData()
+ : codecForDescriptions(0)
+{
+ groups = new QSortedList<KNGroupInfo>;
+ groups->setAutoDelete(true);
+}
+
+
+
+KNGroupListData::~KNGroupListData()
+{
+ delete groups;
+}
+
+
+
+bool KNGroupListData::readIn(KNProtocolClient *client)
+{
+ KNFile f(path+"groups");
+ QCString line;
+ int sepPos1,sepPos2;
+ QString name,description;
+ bool sub;
+ KNGroup::Status status=KNGroup::unknown;
+ QTime timer;
+ uint size=f.size()+2;
+
+ timer.start();
+ if (client) client->updatePercentage(0);
+
+ if(f.open(IO_ReadOnly)) {
+ while(!f.atEnd()) {
+ line = f.readLine();
+ sepPos1 = line.find(' ');
+
+ if (sepPos1==-1) { // no description
+ name = QString::fromUtf8(line);
+ description = QString::null;
+ status = KNGroup::unknown;
+ } else {
+ name = QString::fromUtf8(line.left(sepPos1));
+
+ sepPos2 = line.find(' ',sepPos1+1);
+ if (sepPos2==-1) { // no status
+ description = QString::fromUtf8(line.right(line.length()-sepPos1-1));
+ status = KNGroup::unknown;
+ } else {
+ description = QString::fromUtf8(line.right(line.length()-sepPos2-1));
+ switch (line[sepPos1+1]) {
+ case 'u': status = KNGroup::unknown;
+ break;
+ case 'n': status = KNGroup::readOnly;
+ break;
+ case 'y': status = KNGroup::postingAllowed;
+ break;
+ case 'm': status = KNGroup::moderated;
+ break;
+ }
+ }
+ }
+
+ if (subscribed.contains(name)) {
+ subscribed.remove(name); // group names are unique, we wont find it again anyway...
+ sub = true;
+ } else
+ sub = false;
+
+ groups->append(new KNGroupInfo(name,description,false,sub,status));
+
+ if (timer.elapsed() > 200) { // don't flicker
+ timer.restart();
+ if (client) client->updatePercentage((f.at()*100)/size);
+ }
+ }
+
+ f.close();
+ return true;
+ } else {
+ kdWarning(5003) << "unable to open " << f.name() << " reason " << f.status() << endl;
+ return false;
+ }
+}
+
+
+
+bool KNGroupListData::writeOut()
+{
+ QFile f(path+"groups");
+ QCString temp;
+
+ if(f.open(IO_WriteOnly)) {
+ for (KNGroupInfo *i=groups->first(); i; i=groups->next()) {
+ temp = i->name.utf8();
+ switch (i->status) {
+ case KNGroup::unknown: temp += " u ";
+ break;
+ case KNGroup::readOnly: temp += " n ";
+ break;
+ case KNGroup::postingAllowed: temp += " y ";
+ break;
+ case KNGroup::moderated: temp += " m ";
+ break;
+ }
+ temp += i->description.utf8() + "\n";
+ f.writeBlock(temp.data(),temp.length());
+ }
+ f.close();
+ return true;
+ } else {
+ kdWarning(5003) << "unable to open " << f.name() << " reason " << f.status() << endl;
+ return false;
+ }
+}
+
+
+
+// merge in new groups, we want to preserve the "subscribed"-flag
+// of the loaded groups and the "new"-flag of the new groups.
+void KNGroupListData::merge(QSortedList<KNGroupInfo>* newGroups)
+{
+ bool subscribed;
+
+ for (KNGroupInfo *i=newGroups->first(); i; i=newGroups->next()) {
+ if (groups->find(i)>=0) {
+ subscribed = groups->current()->subscribed;
+ groups->remove(); // avoid duplicates
+ } else
+ subscribed = false;
+ groups->append(new KNGroupInfo(i->name,i->description,true,subscribed,i->status));
+ }
+
+ groups->sort();
+}
+
+
+QSortedList<KNGroupInfo>* KNGroupListData::extractList()
+{
+ QSortedList<KNGroupInfo>* temp = groups;
+ groups = 0;
+ return temp;
+}
+
+
+//===============================================================================
+
+
+KNGroupManager::KNGroupManager(QObject * parent, const char * name)
+ : QObject(parent,name)
+{
+ c_urrentGroup=0;
+ a_rticleMgr = knGlobals.articleManager();
+}
+
+
+KNGroupManager::~KNGroupManager()
+{
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
+ delete (*it);
+}
+
+
+void KNGroupManager::syncGroups()
+{
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
+ (*it)->syncDynamicData();
+ (*it)->saveInfo();
+ }
+}
+
+
+void KNGroupManager::loadGroups(KNNntpAccount *a)
+{
+ KNGroup *group;
+
+ QString dir(a->path());
+ if (dir.isNull())
+ return;
+ QDir d(dir);
+
+ QStringList entries(d.entryList("*.grpinfo"));
+ for(QStringList::Iterator it=entries.begin(); it != entries.end(); ++it) {
+ group=new KNGroup(a);
+ if (group->readInfo(dir+(*it))) {
+ mGroupList.append( group );
+ emit groupAdded(group);
+ } else {
+ delete group;
+ kdError(5003) << "Unable to load " << (*it) << "!" << endl;
+ }
+ }
+}
+
+
+void KNGroupManager::getSubscribed(KNNntpAccount *a, QStringList &l)
+{
+ l.clear();
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
+ if ( (*it)->account() == a )
+ l.append( (*it)->groupname() );
+}
+
+
+QValueList<KNGroup*> KNGroupManager::groupsOfAccount( KNNntpAccount *a )
+{
+ QValueList<KNGroup*> ret;
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
+ if ( (*it)->account() == a )
+ ret.append( (*it) );
+ return ret;
+}
+
+
+bool KNGroupManager::loadHeaders(KNGroup *g)
+{
+ if (!g)
+ return false;
+
+ if (g->isLoaded())
+ return true;
+
+ // we want to delete old stuff first => reduce vm fragmentation
+ knGlobals.memoryManager()->prepareLoad(g);
+
+ if (g->loadHdrs()) {
+ knGlobals.memoryManager()->updateCacheEntry( g );
+ return true;
+ }
+
+ return false;
+}
+
+
+bool KNGroupManager::unloadHeaders(KNGroup *g, bool force)
+{
+ if(!g || g->isLocked())
+ return false;
+
+ if(!g->isLoaded())
+ return true;
+
+ if (!force && (c_urrentGroup == g))
+ return false;
+
+ if (g->unloadHdrs(force))
+ knGlobals.memoryManager()->removeCacheEntry(g);
+ else
+ return false;
+
+ return true;
+}
+
+
+KNGroup* KNGroupManager::group(const QString &gName, const KNServerInfo *s)
+{
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
+ if ( (*it)->account() == s && (*it)->groupname() == gName )
+ return (*it);
+
+ return 0;
+}
+
+
+KNGroup* KNGroupManager::firstGroupOfAccount(const KNServerInfo *s)
+{
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
+ if ( (*it)->account() == s )
+ return (*it);
+
+ return 0;
+}
+
+
+void KNGroupManager::expireAll(KNCleanUp *cup)
+{
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
+ if( (*it)->isLocked() || (*it)->lockedArticles() > 0 )
+ continue;
+ if ( !(*it)->activeCleanupConfig()->expireToday() )
+ continue;
+ cup->appendCollection( *(it) );
+ }
+}
+
+
+void KNGroupManager::expireAll(KNNntpAccount *a)
+{
+ KNCleanUp *cup = new KNCleanUp();
+
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
+ if( (*it)->account() != a || (*it)->isLocked() || (*it)->lockedArticles() > 0 )
+ continue;
+
+ KNArticleWindow::closeAllWindowsForCollection( (*it) );
+ cup->appendCollection( (*it) );
+ }
+
+ cup->start();
+
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
+ if( (*it)->account() != a || (*it)->isLocked() || (*it)->lockedArticles() > 0 )
+ continue;
+
+ emit groupUpdated( (*it) );
+ if ( (*it) == c_urrentGroup ) {
+ if ( loadHeaders( (*it) ) )
+ a_rticleMgr->showHdrs();
+ else
+ a_rticleMgr->setGroup(0);
+ }
+ }
+
+ delete cup;
+}
+
+
+void KNGroupManager::showGroupDialog(KNNntpAccount *a, QWidget *parent)
+{
+ KNGroupDialog* gDialog=new KNGroupDialog((parent!=0)? parent:knGlobals.topWidget, a);
+
+ connect(gDialog, SIGNAL(loadList(KNNntpAccount*)), this, SLOT(slotLoadGroupList(KNNntpAccount*)));
+ connect(gDialog, SIGNAL(fetchList(KNNntpAccount*)), this, SLOT(slotFetchGroupList(KNNntpAccount*)));
+ connect(gDialog, SIGNAL(checkNew(KNNntpAccount*,QDate)), this, SLOT(slotCheckForNewGroups(KNNntpAccount*,QDate)));
+ connect(this, SIGNAL(newListReady(KNGroupListData*)), gDialog, SLOT(slotReceiveList(KNGroupListData*)));
+
+ if(gDialog->exec()) {
+ KNGroup *g=0;
+
+ QStringList lst;
+ gDialog->toUnsubscribe(&lst);
+ if (lst.count()>0) {
+ if (KMessageBox::Yes == KMessageBox::questionYesNoList((parent!=0)? parent:knGlobals.topWidget,i18n("Do you really want to unsubscribe\nfrom these groups?"),
+ lst, QString::null, i18n("Unsubscribe"), KStdGuiItem::cancel())) {
+ for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
+ if((g=group(*it, a)))
+ unsubscribeGroup(g);
+ }
+ }
+ }
+
+ QSortedList<KNGroupInfo> lst2;
+ gDialog->toSubscribe(&lst2);
+ for(KNGroupInfo *var=lst2.first(); var; var=lst2.next()) {
+ subscribeGroup(var, a);
+ }
+ }
+
+ delete gDialog;
+}
+
+
+void KNGroupManager::subscribeGroup(const KNGroupInfo *gi, KNNntpAccount *a)
+{
+ KNGroup *grp;
+
+ grp=new KNGroup(a);
+ grp->setGroupname(gi->name);
+ grp->setDescription(gi->description);
+ grp->setStatus(gi->status);
+ grp->saveInfo();
+ mGroupList.append( grp );
+ emit groupAdded(grp);
+}
+
+
+bool KNGroupManager::unsubscribeGroup(KNGroup *g)
+{
+ KNNntpAccount *acc;
+ if(!g) g=c_urrentGroup;
+ if(!g) return false;
+
+ if((g->isLocked()) || (g->lockedArticles()>0)) {
+ KMessageBox::sorry(knGlobals.topWidget, i18n("The group \"%1\" is being updated currently.\nIt is not possible to unsubscribe from it at the moment.").arg(g->groupname()));
+ return false;
+ }
+
+ KNArticleWindow::closeAllWindowsForCollection(g);
+ ArticleWidget::collectionRemoved( g );
+
+ acc=g->account();
+
+ QDir dir(acc->path(),g->groupname()+"*");
+ if (dir.exists()) {
+ if (unloadHeaders(g, true)) {
+ if(c_urrentGroup==g) {
+ setCurrentGroup(0);
+ a_rticleMgr->updateStatusString();
+ }
+
+ const QFileInfoList *list = dir.entryInfoList(); // get list of matching files and delete all
+ if (list) {
+ QFileInfoListIterator it( *list );
+ while (it.current()) {
+ if (it.current()->fileName() == g->groupname()+".dynamic" ||
+ it.current()->fileName() == g->groupname()+".static" ||
+ it.current()->fileName() == g->groupname()+".grpinfo")
+ dir.remove(it.current()->fileName());
+ ++it;
+ }
+ }
+ kdDebug(5003) << "Files deleted!" << endl;
+
+ emit groupRemoved(g);
+ mGroupList.remove( g );
+ delete g;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void KNGroupManager::showGroupProperties(KNGroup *g)
+{
+ if(!g) g=c_urrentGroup;
+ if(!g) return;
+ g->showProperties();
+}
+
+
+void KNGroupManager::checkGroupForNewHeaders(KNGroup *g)
+{
+ if(!g) g=c_urrentGroup;
+ if(!g) return;
+ if(g->isLocked()) {
+ kdDebug(5003) << "KNGroupManager::checkGroupForNewHeaders() : group locked - returning" << endl;
+ return;
+ }
+
+ g->setMaxFetch(knGlobals.configManager()->readNewsGeneral()->maxToFetch());
+ emitJob( new KNJobData(KNJobData::JTfetchNewHeaders, this, g->account(), g) );
+}
+
+
+void KNGroupManager::expireGroupNow(KNGroup *g)
+{
+ if(!g) return;
+
+ if((g->isLocked()) || (g->lockedArticles()>0)) {
+ KMessageBox::sorry(knGlobals.topWidget,
+ i18n("This group cannot be expired because it is currently being updated.\n Please try again later."));
+ return;
+ }
+
+ KNArticleWindow::closeAllWindowsForCollection(g);
+
+ KNCleanUp cup;
+ cup.expireGroup(g, true);
+
+ emit groupUpdated(g);
+ if(g==c_urrentGroup) {
+ if( loadHeaders(g) )
+ a_rticleMgr->showHdrs();
+ else
+ a_rticleMgr->setGroup(0);
+ }
+}
+
+
+void KNGroupManager::reorganizeGroup(KNGroup *g)
+{
+ if(!g) g=c_urrentGroup;
+ if(!g) return;
+ g->reorganize();
+ if(g==c_urrentGroup)
+ a_rticleMgr->showHdrs();
+}
+
+
+void KNGroupManager::setCurrentGroup(KNGroup *g)
+{
+ c_urrentGroup=g;
+ a_rticleMgr->setGroup(g);
+ kdDebug(5003) << "KNGroupManager::setCurrentGroup() : group changed" << endl;
+
+ if(g) {
+ if( !loadHeaders(g) ) {
+ //KMessageBox::error(knGlobals.topWidget, i18n("Cannot load saved headers"));
+ return;
+ }
+ a_rticleMgr->showHdrs();
+ if(knGlobals.configManager()->readNewsGeneral()->autoCheckGroups())
+ checkGroupForNewHeaders(g);
+ }
+}
+
+
+void KNGroupManager::checkAll(KNNntpAccount *a, bool silent)
+{
+ if(!a) return;
+
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
+ if ( (*it)->account() == a ) {
+ (*it)->setMaxFetch( knGlobals.configManager()->readNewsGeneral()->maxToFetch() );
+ if ( silent )
+ emitJob( new KNJobData(KNJobData::JTsilentFetchNewHeaders, this, (*it)->account(), (*it) ) );
+ else
+ emitJob( new KNJobData(KNJobData::JTfetchNewHeaders, this, (*it)->account(), (*it) ) );
+ }
+ }
+}
+
+
+void KNGroupManager::processJob(KNJobData *j)
+{
+ if((j->type()==KNJobData::JTLoadGroups)||(j->type()==KNJobData::JTFetchGroups)||(j->type()==KNJobData::JTCheckNewGroups)) {
+ KNGroupListData *d=static_cast<KNGroupListData*>(j->data());
+
+ if (!j->canceled()) {
+ if (j->success()) {
+ if ((j->type()==KNJobData::JTFetchGroups)||(j->type()==KNJobData::JTCheckNewGroups)) {
+ // update the descriptions of the subscribed groups
+ for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
+ if ( (*it)->account() == j->account() ) {
+ for ( KNGroupInfo* inf = d->groups->first(); inf; inf = d->groups->next() )
+ if ( inf->name == (*it)->groupname() ) {
+ (*it)->setDescription( inf->description );
+ (*it)->setStatus( inf->status );
+ break;
+ }
+ }
+ }
+ }
+ emit(newListReady(d));
+ } else {
+ KMessageBox::error(knGlobals.topWidget, j->errorString());
+ emit(newListReady(0));
+ }
+ } else
+ emit(newListReady(0));
+
+ delete j;
+ delete d;
+
+
+ } else { //KNJobData::JTfetchNewHeaders or KNJobData::JTsilentFetchNewHeaders
+ KNGroup *group=static_cast<KNGroup*>(j->data());
+
+ if (!j->canceled()) {
+ if (j->success()) {
+ if(group->lastFetchCount()>0) {
+ group->scoreArticles();
+ group->processXPostBuffer(true);
+ emit groupUpdated(group);
+ group->saveInfo();
+ knGlobals.memoryManager()->updateCacheEntry(group);
+ }
+ } else {
+ // ok, hack (?):
+ // stop all other active fetch jobs, this prevents that
+ // we show multiple error dialogs if a server is unavailable
+ knGlobals.netAccess()->stopJobsNntp(KNJobData::JTfetchNewHeaders);
+ knGlobals.netAccess()->stopJobsNntp(KNJobData::JTsilentFetchNewHeaders);
+ if(!(j->type()==KNJobData::JTsilentFetchNewHeaders)) {
+ KMessageBox::error(knGlobals.topWidget, j->errorString());
+ }
+ }
+ }
+ if(group==c_urrentGroup)
+ a_rticleMgr->showHdrs(false);
+
+ delete j;
+ }
+}
+
+
+// load group list from disk (if this fails: ask user if we should fetch the list)
+void KNGroupManager::slotLoadGroupList(KNNntpAccount *a)
+{
+ KNGroupListData *d = new KNGroupListData();
+ d->path = a->path();
+
+ if(!QFileInfo(d->path+"groups").exists()) {
+ if (KMessageBox::Yes==KMessageBox::questionYesNo(knGlobals.topWidget,i18n("You do not have any groups for this account;\ndo you want to fetch a current list?"), QString::null, i18n("Fetch List"), i18n("Do Not Fetch"))) {
+ delete d;
+ slotFetchGroupList(a);
+ return;
+ } else {
+ emit(newListReady(d));
+ delete d;
+ return;
+ }
+ }
+
+ getSubscribed(a,d->subscribed);
+ d->getDescriptions = a->fetchDescriptions();
+
+ emitJob( new KNJobData(KNJobData::JTLoadGroups, this, a, d) );
+}
+
+
+// fetch group list from server
+void KNGroupManager::slotFetchGroupList(KNNntpAccount *a)
+{
+ KNGroupListData *d = new KNGroupListData();
+ d->path = a->path();
+ getSubscribed(a,d->subscribed);
+ d->getDescriptions = a->fetchDescriptions();
+ d->codecForDescriptions=KGlobal::charsets()->codecForName(knGlobals.configManager()->postNewsTechnical()->charset());
+
+ emitJob( new KNJobData(KNJobData::JTFetchGroups, this, a, d) );
+}
+
+
+// check for new groups (created after the given date)
+void KNGroupManager::slotCheckForNewGroups(KNNntpAccount *a, QDate date)
+{
+ KNGroupListData *d = new KNGroupListData();
+ d->path = a->path();
+ getSubscribed(a,d->subscribed);
+ d->getDescriptions = a->fetchDescriptions();
+ d->fetchSince = date;
+ d->codecForDescriptions=KGlobal::charsets()->codecForName(knGlobals.configManager()->postNewsTechnical()->charset());
+
+ emitJob( new KNJobData(KNJobData::JTCheckNewGroups, this, a, d) );
+}
+
+
+//--------------------------------
+
+#include "kngroupmanager.moc"
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/kngroupmanager.h b/knode/kngroupmanager.h
new file mode 100644
index 000000000..f3805dc81
--- /dev/null
+++ b/knode/kngroupmanager.h
@@ -0,0 +1,142 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNGROUPMANAGER_H
+#define KNGROUPMANAGER_H
+
+#include <qobject.h>
+#include <qsortedlist.h>
+
+#include "knjobdata.h"
+#include "kngroup.h"
+
+class KNNntpAccount;
+class KNProtocolClient;
+class KNServerInfo;
+class KNArticleManager;
+class KNCleanUp;
+
+
+//=================================================================================
+
+/** helper classes for the group selection dialog
+ contains info about a newsgroup (name, description) */
+
+class KNGroupInfo {
+
+ public:
+
+ KNGroupInfo();
+ KNGroupInfo(const QString &n_ame, const QString &d_escription, bool n_ewGroup=false, bool s_ubscribed=false, KNGroup::Status s_tatus=KNGroup::unknown );
+ ~KNGroupInfo();
+
+ /** group names will be utf-8 encoded in the future... */
+ QString name,description;
+ bool newGroup, subscribed;
+ KNGroup::Status status;
+
+ bool operator== (const KNGroupInfo &gi2);
+ bool operator< (const KNGroupInfo &gi2);
+};
+
+
+class KNGroupListData : public KNJobItem {
+
+ public:
+ KNGroupListData();
+ ~KNGroupListData();
+
+ bool readIn(KNProtocolClient *client=0);
+ bool writeOut();
+ void merge(QSortedList<KNGroupInfo>* newGroups);
+
+ QSortedList<KNGroupInfo>* extractList();
+
+ QStringList subscribed;
+ QString path;
+ QSortedList<KNGroupInfo> *groups;
+ QDate fetchSince;
+ bool getDescriptions;
+ QTextCodec *codecForDescriptions;
+
+};
+
+//===============================================================================
+
+
+class KNGroupManager : public QObject , public KNJobConsumer {
+
+ Q_OBJECT
+
+ public:
+
+ KNGroupManager(QObject * parent=0, const char * name=0);
+ ~KNGroupManager();
+
+ // group access
+ void loadGroups(KNNntpAccount *a);
+ void getSubscribed(KNNntpAccount *a, QStringList &l);
+ QValueList<KNGroup*> groupsOfAccount( KNNntpAccount *a );
+
+ bool loadHeaders(KNGroup *g);
+ bool unloadHeaders(KNGroup *g, bool force=true);
+
+ KNGroup* group(const QString &gName, const KNServerInfo *s);
+ KNGroup* firstGroupOfAccount(const KNServerInfo *s);
+ KNGroup* currentGroup() const { return c_urrentGroup; }
+ bool hasCurrentGroup() const { return (c_urrentGroup!=0); }
+ void setCurrentGroup(KNGroup *g);
+
+ // group handling
+ void showGroupDialog(KNNntpAccount *a, QWidget *parent=0);
+ void subscribeGroup(const KNGroupInfo *gi, KNNntpAccount *a);
+ bool unsubscribeGroup(KNGroup *g=0);
+ void showGroupProperties(KNGroup *g=0);
+ void expireGroupNow(KNGroup *g=0);
+ void reorganizeGroup(KNGroup *g=0);
+
+ void checkGroupForNewHeaders(KNGroup *g=0);
+ void checkAll(KNNntpAccount *a, bool silent=false);
+
+ void expireAll(KNCleanUp *cup);
+ void expireAll(KNNntpAccount *a);
+ void syncGroups();
+
+ public slots:
+ /** load group list from disk (if this fails: ask user if we should fetch the list) */
+ void slotLoadGroupList(KNNntpAccount *a);
+ /** fetch group list from server */
+ void slotFetchGroupList(KNNntpAccount *a);
+ /** check for new groups (created after the given date) */
+ void slotCheckForNewGroups(KNNntpAccount *a, QDate date);
+
+ protected:
+ /** reimplemented from @ref KNJobConsumer */
+ void processJob(KNJobData *j);
+ QValueList<KNGroup*> mGroupList;
+ KNGroup *c_urrentGroup;
+ KNArticleManager *a_rticleMgr;
+
+ signals:
+ void newListReady(KNGroupListData* d);
+
+ void groupAdded(KNGroup* g);
+ void groupRemoved(KNGroup* g);
+ void groupUpdated(KNGroup* g);
+
+};
+
+
+
+#endif
diff --git a/knode/kngrouppropdlg.cpp b/knode/kngrouppropdlg.cpp
new file mode 100644
index 000000000..b93669374
--- /dev/null
+++ b/knode/kngrouppropdlg.cpp
@@ -0,0 +1,181 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qgroupbox.h>
+#include <qlayout.h>
+#include <qvbox.h>
+#include <qcheckbox.h>
+
+#include <klocale.h>
+#include <klineedit.h>
+
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "knconfigwidgets.h"
+#include "utilities.h"
+#include "kngroup.h"
+#include "kngrouppropdlg.h"
+#include <qlabel.h>
+
+
+KNGroupPropDlg::KNGroupPropDlg(KNGroup *group, QWidget *parent, const char *name )
+ : KDialogBase(Tabbed, i18n("Properties of %1").arg(group->groupname()),
+ Ok|Cancel|Help, Ok, parent, name),
+ g_rp(group), n_ickChanged(false)
+{
+
+ // General tab ===============================================
+
+ QWidget *page = addPage(i18n("&General"));
+ QVBoxLayout *pageL = new QVBoxLayout(page, 3);
+
+ // settings
+ QGroupBox *gb=new QGroupBox(i18n("Settings"), page);
+ pageL->addWidget(gb);
+ QGridLayout *grpL=new QGridLayout(gb, 3, 3, 15, 5);
+
+ grpL->addRowSpacing(0, fontMetrics().lineSpacing()-9);
+
+ n_ick=new KLineEdit(gb);
+ if (g_rp->hasName())
+ n_ick->setText(g_rp->name());
+ QLabel *l=new QLabel(n_ick, i18n("&Nickname:"), gb);
+ grpL->addWidget(l,1,0);
+ grpL->addMultiCellWidget(n_ick,1,1,1,2);
+
+ u_seCharset=new QCheckBox(i18n("&Use different default charset:"), gb);
+ u_seCharset->setChecked(g_rp->useCharset());
+ grpL->addMultiCellWidget(u_seCharset,2,2,0,1);
+
+ c_harset=new QComboBox(false, gb);
+ c_harset->insertStringList(knGlobals.configManager()->postNewsTechnical()->composerCharsets());
+ c_harset->setCurrentItem(knGlobals.configManager()->postNewsTechnical()->indexForCharset(g_rp->defaultCharset()));
+ c_harset->setEnabled(g_rp->useCharset());
+ connect(u_seCharset, SIGNAL(toggled(bool)), c_harset, SLOT(setEnabled(bool)));
+ grpL->addWidget(c_harset, 2,2);
+
+ grpL->setColStretch(1,1);
+ grpL->setColStretch(2,2);
+
+ // group name & description
+ gb=new QGroupBox(i18n("Description"), page);
+ pageL->addWidget(gb);
+ grpL=new QGridLayout(gb, 4, 3, 15, 5);
+
+ grpL->addRowSpacing(0, fontMetrics().lineSpacing()-9);
+
+ l=new QLabel(i18n("Name:"), gb);
+ grpL->addWidget(l,1,0);
+ l=new QLabel(group->groupname(),gb);
+ grpL->addWidget(l,1,2);
+
+ l=new QLabel(i18n("Description:"), gb);
+ grpL->addWidget(l,2,0);
+ l=new QLabel(g_rp->description(),gb);
+ grpL->addWidget(l,2,2);
+
+ l=new QLabel(i18n("Status:"), gb);
+ grpL->addWidget(l,3,0);
+ QString status;
+ switch (g_rp->status()) {
+ case KNGroup::unknown: status=i18n("unknown");
+ break;
+ case KNGroup::readOnly: status=i18n("posting forbidden");
+ break;
+ case KNGroup::postingAllowed: status=i18n("posting allowed");
+ break;
+ case KNGroup::moderated: status=i18n("moderated");
+ break;
+ }
+ l=new QLabel(status,gb);
+ grpL->addWidget(l,3,2);
+
+ grpL->addColSpacing(1,20);
+ grpL->setColStretch(2,1);
+
+ // statistics
+ gb=new QGroupBox(i18n("Statistics"), page);
+ pageL->addWidget(gb);
+ grpL=new QGridLayout(gb, 6, 3, 15, 5);
+
+ grpL->addRowSpacing(0, fontMetrics().lineSpacing()-9);
+
+ l=new QLabel(i18n("Articles:"), gb);
+ grpL->addWidget(l,1,0);
+ l=new QLabel(QString::number(g_rp->count()),gb);
+ grpL->addWidget(l,1,2);
+
+ l=new QLabel(i18n("Unread articles:"), gb);
+ grpL->addWidget(l,2,0);
+ l=new QLabel(QString::number(g_rp->count()-g_rp->readCount()),gb);
+ grpL->addWidget(l,2,2);
+
+ l=new QLabel(i18n("New articles:"), gb);
+ grpL->addWidget(l,3,0);
+ l=new QLabel(QString::number(g_rp->newCount()),gb);
+ grpL->addWidget(l,3,2);
+
+ l=new QLabel(i18n("Threads with unread articles:"), gb);
+ grpL->addWidget(l,4,0);
+ l=new QLabel(QString::number(g_rp->statThrWithUnread()),gb);
+ grpL->addWidget(l,4,2);
+
+ l=new QLabel(i18n("Threads with new articles:"), gb);
+ grpL->addWidget(l,5,0);
+ l=new QLabel(QString::number(g_rp->statThrWithNew()),gb);
+ grpL->addWidget(l,5,2);
+
+ grpL->addColSpacing(1,20);
+ grpL->setColStretch(2,1);
+
+ pageL->addStretch(1);
+
+ // Specfic Identity tab =========================================
+ i_dWidget=new KNConfig::IdentityWidget(g_rp->identity(), addVBoxPage(i18n("&Identity")));
+
+ // per server cleanup configuration
+ QFrame* cleanupPage = addPage( i18n("&Cleanup") );
+ QVBoxLayout *cleanupLayout = new QVBoxLayout( cleanupPage, KDialog::spacingHint() );
+ mCleanupWidget = new KNConfig::GroupCleanupWidget( g_rp->cleanupConfig(), cleanupPage );
+ mCleanupWidget->load();
+ cleanupLayout->addWidget( mCleanupWidget );
+ cleanupLayout->addStretch( 1 );
+
+ KNHelper::restoreWindowSize("groupPropDLG", this, sizeHint());
+}
+
+
+
+KNGroupPropDlg::~KNGroupPropDlg()
+{
+ KNHelper::saveWindowSize("groupPropDLG", size());
+}
+
+
+
+void KNGroupPropDlg::slotOk()
+{
+ if( !(g_rp->name()==n_ick->text()) ) {
+ g_rp->setName(n_ick->text());
+ n_ickChanged=true;
+ }
+
+ i_dWidget->save();
+ mCleanupWidget->save();
+
+ g_rp->setUseCharset(u_seCharset->isChecked());
+ g_rp->setDefaultCharset(c_harset->currentText().latin1());
+
+ accept();
+}
diff --git a/knode/kngrouppropdlg.h b/knode/kngrouppropdlg.h
new file mode 100644
index 000000000..3b67df9dc
--- /dev/null
+++ b/knode/kngrouppropdlg.h
@@ -0,0 +1,59 @@
+/*
+ kngrouppropdlg.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNGROUPPROPDLG_H
+#define KNGROUPPROPDLG_H
+
+#include <kdialogbase.h>
+
+class QCheckBox;
+class QComboBox;
+
+class KLineEdit;
+
+class KNGroup;
+
+namespace KNConfig {
+ class IdentityWidget;
+ class GroupCleanupWidget;
+}
+
+
+class KNGroupPropDlg : public KDialogBase {
+
+ public:
+ KNGroupPropDlg(KNGroup *group, QWidget *parent=0, const char *name=0);
+ ~KNGroupPropDlg();
+
+ bool nickHasChanged()const { return n_ickChanged; }
+
+ protected:
+ KNGroup *g_rp;
+ bool n_ickChanged;
+ KNConfig::IdentityWidget* i_dWidget;
+ KNConfig::GroupCleanupWidget *mCleanupWidget;
+ KLineEdit *n_ick;
+ QCheckBox *u_seCharset;
+ QComboBox *c_harset;
+
+ protected slots:
+ void slotOk();
+
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/kngroupselectdialog.cpp b/knode/kngroupselectdialog.cpp
new file mode 100644
index 000000000..e9ac3a2dd
--- /dev/null
+++ b/knode/kngroupselectdialog.cpp
@@ -0,0 +1,172 @@
+/*
+ kngroupselectdialog.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qheader.h>
+#include <qcheckbox.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "utilities.h"
+#include "kngroupselectdialog.h"
+#include <qpushbutton.h>
+
+
+KNGroupSelectDialog::KNGroupSelectDialog(QWidget *parent, KNNntpAccount *a, const QString &act) :
+ KNGroupBrowser(parent, i18n("Select Destinations"), a)
+{
+ selView=new QListView(page);
+ selView->addColumn(QString::null);
+ selView->header()->hide();
+ listL->addWidget(selView, 1,2);
+ rightLabel->setText(i18n("Groups for this article:"));
+ subCB->setChecked(true);
+
+ KNGroupInfo info;
+ QStringList actGroups = QStringList::split(',',act);
+ for ( QStringList::Iterator it = actGroups.begin(); it != actGroups.end(); ++it ) {
+ info.name = *it;
+ new GroupItem(selView, info);
+ }
+
+ connect(selView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotItemSelected(QListViewItem*)));
+ connect(groupView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotItemSelected(QListViewItem*)));
+ connect(groupView, SIGNAL(selectionChanged()),
+ this, SLOT(slotSelectionChanged()));
+ connect(arrowBtn1, SIGNAL(clicked()), this, SLOT(slotArrowBtn1()));
+ connect(arrowBtn2, SIGNAL(clicked()), this, SLOT(slotArrowBtn2()));
+
+ KNHelper::restoreWindowSize("groupSelDlg", this, QSize(659,364)); // optimized for 800x600
+}
+
+
+
+KNGroupSelectDialog::~KNGroupSelectDialog()
+{
+ KNHelper::saveWindowSize("groupSelDlg", this->size());
+}
+
+
+
+void KNGroupSelectDialog::itemChangedState(CheckItem *it, bool s)
+{
+ if(s)
+ new GroupItem(selView, it->info);
+ else
+ removeListItem(selView, it->info);
+ arrowBtn1->setEnabled(!s);
+}
+
+
+
+void KNGroupSelectDialog::updateItemState(CheckItem *it)
+{
+ it->setChecked(itemInListView(selView, it->info));
+ if(it->info.subscribed && it->pixmap(0)==0)
+ it->setPixmap(0, pmGroup);
+}
+
+
+
+QString KNGroupSelectDialog::selectedGroups()const
+{
+ QString ret;
+ QListViewItemIterator it(selView);
+ bool moderated=false;
+ int count=0;
+ bool isFirst=true;
+
+ for(; it.current(); ++it) {
+ if(!isFirst)
+ ret+=",";
+ ret+=(static_cast<GroupItem*>(it.current()))->info.name;
+ isFirst=false;
+ count++;
+ if ((static_cast<GroupItem*>(it.current()))->info.status == KNGroup::moderated)
+ moderated=true;
+ }
+
+ if (moderated && (count>=2)) // warn the user
+ KMessageBox::information(parentWidget(),i18n("You are crossposting to a moderated newsgroup.\nPlease be aware that your article will not appear in any group\nuntil it has been approved by the moderators of the moderated group."),
+ QString::null,"crosspostModeratedWarning");
+
+ return ret;
+}
+
+
+
+void KNGroupSelectDialog::slotItemSelected(QListViewItem *it)
+{
+ const QObject *s=sender();
+
+ if(s==groupView) {
+ selView->clearSelection();
+ arrowBtn2->setEnabled(false);
+ if(it)
+ arrowBtn1->setEnabled(!(static_cast<CheckItem*>(it))->isOn());
+ else
+ arrowBtn1->setEnabled(false);
+ }
+ else {
+ groupView->clearSelection();
+ arrowBtn1->setEnabled(false);
+ arrowBtn2->setEnabled((it!=0));
+ }
+}
+
+
+
+void KNGroupSelectDialog::slotSelectionChanged()
+{
+ if (!groupView->selectedItem())
+ arrowBtn1->setEnabled(false);
+}
+
+
+
+void KNGroupSelectDialog::slotArrowBtn1()
+{
+ CheckItem *i=static_cast<CheckItem*>(groupView->selectedItem());
+
+ if(i) {
+ new GroupItem(selView, i->info);
+ arrowBtn1->setEnabled(false);
+ i->setChecked(true);
+ }
+}
+
+
+
+void KNGroupSelectDialog::slotArrowBtn2()
+{
+ GroupItem *i=static_cast<GroupItem*>(selView->selectedItem());
+
+ if(i) {
+ changeItemState(i->info, false);
+ delete i;
+ arrowBtn2->setEnabled(false);
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+
+#include "kngroupselectdialog.moc"
+
diff --git a/knode/kngroupselectdialog.h b/knode/kngroupselectdialog.h
new file mode 100644
index 000000000..d46826cc3
--- /dev/null
+++ b/knode/kngroupselectdialog.h
@@ -0,0 +1,63 @@
+/*
+ kngroupselectdialog.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNGROUPSELECTDIALOG_H
+#define KNGROUPSELECTDIALOG_H
+
+#include "kngroupbrowser.h"
+
+
+class KNGroupSelectDialog : public KNGroupBrowser {
+
+ Q_OBJECT
+
+ public:
+ KNGroupSelectDialog(QWidget *parent, KNNntpAccount *a, const QString &act);
+ ~KNGroupSelectDialog();
+
+ QString selectedGroups()const;
+ void itemChangedState(CheckItem *it, bool s);
+
+ protected:
+ void updateItemState(CheckItem *it);
+ QListView *selView;
+
+ protected slots:
+ void slotItemSelected(QListViewItem *it);
+ /** deactivates the button when a root item is selected */
+ void slotSelectionChanged();
+ void slotArrowBtn1();
+ void slotArrowBtn2();
+
+};
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/knode/knhdrviewitem.cpp b/knode/knhdrviewitem.cpp
new file mode 100644
index 000000000..93b54fc0a
--- /dev/null
+++ b/knode/knhdrviewitem.cpp
@@ -0,0 +1,294 @@
+/*
+ knhdrviewitem.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qdragobject.h>
+#include <qpainter.h>
+
+#include <kdeversion.h>
+#include <kdebug.h>
+#include <kstringhandler.h>
+
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "knhdrviewitem.h"
+#include "knarticle.h"
+#include "headerview.h"
+
+
+KNHdrViewItem::KNHdrViewItem( KNHeaderView *ref, KNArticle *a ) :
+ KListViewItem( ref )
+{
+ init( a );
+}
+
+
+KNHdrViewItem::KNHdrViewItem( KNHdrViewItem *ref, KNArticle *a ) :
+ KListViewItem( ref )
+{
+ init( a );
+}
+
+
+void KNHdrViewItem::init( KNArticle *a )
+{
+ art = a;
+ mActive = false;
+ for ( int i = 0; i < 5; ++i) // FIXME hardcoded column count
+ mShowToolTip[i] = false;
+}
+
+
+KNHdrViewItem::~KNHdrViewItem()
+{
+ if (mActive) {
+ QListView *lv = listView();
+ if (lv)
+ static_cast<KNHeaderView*>( lv )->activeRemoved();
+ }
+
+ if (art) art->setListItem( 0 );
+}
+
+
+void KNHdrViewItem::expandChildren()
+{
+ QListViewItemIterator it( firstChild() );
+ for ( ; it.current(); ++it) {
+ if (it.current()->depth() <= depth())
+ break;
+ it.current()->setOpen( true );
+ }
+}
+
+
+int KNHdrViewItem::compare( QListViewItem *i, int col, bool ) const
+{
+ KNArticle *otherArticle = static_cast<KNHdrViewItem*>( i )->art;
+ int diff = 0;
+ time_t date1 = 0, date2 = 0;
+
+ switch (col) {
+ case 0:
+ case 1:
+ return text( col ).localeAwareCompare( i->text(col) );
+
+ case 2:
+ if (art->type() == KMime::Base::ATremote) {
+ diff = static_cast<KNRemoteArticle*>( art )->score() - static_cast<KNRemoteArticle*>( otherArticle )->score();
+ return (diff < 0 ? -1 : diff > 0 ? 1 : 0);
+ } else
+ return 0;
+
+ case 3:
+ diff = art->lines()->numberOfLines() - otherArticle->lines()->numberOfLines();
+ return (diff < 0 ? -1 : diff > 0 ? 1 : 0);
+
+ case 4:
+ date1 = art->date()->unixTime();
+ date2 = otherArticle->date()->unixTime();
+ if (art->type() == KMime::Base::ATremote && static_cast<KNHeaderView*>( listView() )->sortByThreadChangeDate()) {
+ if (static_cast<KNRemoteArticle*>( art )->subThreadChangeDate() > date1)
+ date1 = static_cast<KNRemoteArticle*>( art )->subThreadChangeDate();
+ if (static_cast<KNRemoteArticle*>( otherArticle )->subThreadChangeDate() > date2)
+ date2 = static_cast<KNRemoteArticle*>( otherArticle )->subThreadChangeDate();
+ }
+ diff = date1 - date2;
+ return (diff < 0 ? -1 : diff > 0 ? 1 : 0);
+
+ default:
+ return 0;
+ }
+}
+
+
+void KNHdrViewItem::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int alignment )
+{
+ int xText = 0, xPM = 3, yPM = 0;
+ QColor base;
+ const KPaintInfo *paintInfo = static_cast<KNHeaderView*>( listView() )->paintInfo();
+
+ QPen pen = p->pen();
+ if (isSelected() || mActive) {
+ pen.setColor( cg.highlightedText() );
+ base = cg.highlight();
+ } else {
+ if (greyOut())
+ pen.setColor( greyColor() );
+ else
+ pen.setColor( normalColor() );
+ base = backgroundColor( column );
+ }
+
+ p->setPen( pen );
+
+ p->fillRect( 0, 0, width, height(), QBrush(base) );
+
+ if ( column == paintInfo->subCol ) {
+ QFont font = p->font();
+ font.setBold( firstColBold() );
+ p->setFont( font );
+ const QPixmap *pm;
+
+ for (int i = 0; i < 4; i++) {
+ pm = pixmap( i );
+ if (pm && !pm->isNull()) {
+ yPM = (height() - pm->height()) / 2;
+ p->drawPixmap( xPM, yPM, *pm );
+ xPM += pm->width() + 3;
+ }
+ }
+
+ xText = xPM;
+ }
+
+ if (width - xText - 5 > 0) {
+ int cntWidth = 0;
+ QString t2;
+ QFont f2;
+ if (countUnreadInThread() > 0 && column == paintInfo->subCol && !isOpen()) {
+ t2 = QString( " (%1)" ).arg( countUnreadInThread() );
+ f2 = p->font();
+ f2.setBold( true );
+ cntWidth = QFontMetrics( f2 ).width( t2, -1 );
+ }
+ QString t = KStringHandler::rPixelSqueeze( text( column ), p->fontMetrics(), width - xText - cntWidth - 5 );
+
+ // show tooltip if we have to squeeze the text
+ if ( t != text( column ) )
+ mShowToolTip[column] = true;
+ else
+ mShowToolTip[column] = false;
+
+ p->drawText( xText, 0, width - xText - 5, height(), alignment | AlignVCenter, t );
+ if (cntWidth) {
+ QFont orig = p->font();
+ p->setFont( f2 );
+ QPen pen = p->pen();
+ if (isSelected() || mActive) {
+ pen.setColor( cg.highlightedText() );
+ } else {
+ pen.setColor( cg.link() );
+ }
+ p->setPen( pen );
+ p->drawText( xText + QFontMetrics( orig ).width( t, -1 ), 0, width - xText - 5, height(), alignment | AlignVCenter, t2 );
+ }
+ }
+}
+
+
+int KNHdrViewItem::width( const QFontMetrics &fm, const QListView *, int column )
+{
+ int ret = fm.boundingRect( text(column) ).width();
+ const KPaintInfo *paintInfo = static_cast<KNHeaderView*>( listView() )->paintInfo();
+
+ // all pixmaps are drawn in the first column
+ if ( column == paintInfo->subCol ) {
+ const QPixmap *pm;
+ for (int i = 0; i < 4; ++i) {
+ pm = pixmap( i );
+ if (pm && !pm->isNull())
+ ret += pm->width() + 3;
+ }
+ }
+
+ return ret;
+}
+
+
+QString KNHdrViewItem::text( int col ) const
+{
+ if ( !art )
+ return QString::null;
+ KNHeaderView *hv = static_cast<KNHeaderView*>( listView() );
+
+ if ( col == hv->paintInfo()->subCol ) {
+ return art->subject()->asUnicodeString();
+ }
+
+ if ( col == hv->paintInfo()->sizeCol ) {
+ if ( art->lines()->numberOfLines() != -1 )
+ return QString::number( art->lines()->numberOfLines() );
+ else
+ return QString::null;
+ }
+
+ if ( col == hv->paintInfo()->scoreCol ) {
+ if ( art->type() == KMime::Base::ATremote )
+ return QString::number( static_cast<KNRemoteArticle*>( art )->score() );
+ else
+ return QString::null;
+ }
+
+ if ( col == hv->paintInfo()->dateCol ) {
+ return hv->mDateFormatter.dateString( art->date()->qdt() );
+ } else
+ return KListViewItem::text( col );
+}
+
+
+QDragObject* KNHdrViewItem::dragObject()
+{
+ QDragObject *d = new QStoredDrag( "x-knode-drag/article" , listView()->viewport() );
+ d->setPixmap( knGlobals.configManager()->appearance()->icon( KNConfig::Appearance::posting ) );
+ return d;
+}
+
+
+int KNHdrViewItem::countUnreadInThread()
+{
+ int count = 0;
+ if (knGlobals.configManager()->readNewsGeneral()->showUnread()) {
+ if (art->type() == KMime::Base::ATremote) {
+ count = static_cast<KNRemoteArticle*>( art )->unreadFollowUps();
+ }
+ }
+ return count;
+}
+
+
+bool KNHdrViewItem::greyOut()
+{
+ if (art->type() == KMime::Base::ATremote) {
+ return !static_cast<KNRemoteArticle*>( art )->hasUnreadFollowUps()
+ && static_cast<KNRemoteArticle*>( art )->isRead();
+ } else
+ return false;
+}
+
+
+bool KNHdrViewItem::firstColBold()
+{
+ if(art->type() == KMime::Base::ATremote)
+ return static_cast<KNRemoteArticle*>( art )->isNew();
+ else
+ return false;
+}
+
+
+QColor KNHdrViewItem::normalColor()
+{
+ if (art->type()==KMime::Base::ATremote)
+ return static_cast<KNRemoteArticle*>( art )->color();
+ else
+ return knGlobals.configManager()->appearance()->unreadThreadColor();
+}
+
+
+QColor KNHdrViewItem::greyColor()
+{
+ return knGlobals.configManager()->appearance()->readThreadColor();
+}
+
diff --git a/knode/knhdrviewitem.h b/knode/knhdrviewitem.h
new file mode 100644
index 000000000..a080c8381
--- /dev/null
+++ b/knode/knhdrviewitem.h
@@ -0,0 +1,67 @@
+/*
+ knhdrviewitem.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNHDRVIEWITEM_H
+#define KNHDRVIEWITEM_H
+
+#include <klistview.h>
+#include "headerview.h"
+
+class KNArticle;
+class KNHeaderView;
+
+
+class KNHdrViewItem : public KListViewItem {
+
+ public:
+ KNHdrViewItem( KNHeaderView *ref, KNArticle *a = 0 );
+ KNHdrViewItem( KNHdrViewItem *ref, KNArticle *a = 0 );
+ ~KNHdrViewItem();
+
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+ void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment);
+ int width(const QFontMetrics &fm, const QListView *lv, int column);
+
+ virtual QString text( int col ) const;
+
+ void expandChildren();
+
+ void setActive( bool b ) { mActive = b; }
+ bool isActive() const { return mActive; }
+
+ // DND
+ QDragObject* dragObject();
+
+ KNArticle *art;
+ int countUnreadInThread();
+
+ bool showToolTip( int column ) const { return mShowToolTip[column]; }
+
+ private:
+ void init( KNArticle *a );
+
+ bool greyOut();
+ bool firstColBold();
+ QColor normalColor();
+ QColor greyColor();
+
+ bool mActive;
+ bool mShowToolTip[5]; // ### hardcoded column count :-(
+
+};
+
+#endif
diff --git a/knode/knjobdata.cpp b/knode/knjobdata.cpp
new file mode 100644
index 000000000..7d8cb6f8f
--- /dev/null
+++ b/knode/knjobdata.cpp
@@ -0,0 +1,147 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kio/job.h>
+
+#include <libkdepim/progressmanager.h>
+
+#include "knarticle.h"
+#include "knglobals.h"
+#include "knnetaccess.h"
+#include "knnntpaccount.h"
+
+#include <qstylesheet.h>
+
+KNJobConsumer::KNJobConsumer()
+{
+}
+
+
+KNJobConsumer::~KNJobConsumer()
+{
+ QValueList<KNJobData*>::Iterator it;
+ for ( it = mJobs.begin(); it != mJobs.end(); ++it )
+ (*it)->c_onsumer = 0;
+}
+
+
+void KNJobConsumer::emitJob( KNJobData *j )
+{
+ if ( j ) {
+ mJobs.append( j );
+ knGlobals.netAccess()->addJob( j );
+ }
+}
+
+
+void KNJobConsumer::jobDone( KNJobData *j )
+{
+ if ( j && mJobs.remove( j ) )
+ processJob( j );
+}
+
+
+void KNJobConsumer::processJob( KNJobData *j )
+{
+ delete j;
+}
+
+
+// the assingment of a_ccount may cause race conditions, check again.... (CG)
+KNJobData::KNJobData(jobType t, KNJobConsumer *c, KNServerInfo *a, KNJobItem *i)
+ : t_ype(t), d_ata(i), a_ccount(a), c_anceled(false), a_uthError(false), c_onsumer(c),
+ mJob( 0 ),
+ mProgressItem( 0 )
+{
+ d_ata->setLocked(true);
+}
+
+
+
+KNJobData::~KNJobData()
+{
+ d_ata->setLocked(false);
+}
+
+
+void KNJobData::notifyConsumer()
+{
+
+ if(c_onsumer)
+ c_onsumer->jobDone(this);
+ else
+ delete this;
+}
+
+void KNJobData::cancel()
+{
+ c_anceled = true;
+ if ( mJob ) {
+ mJob->kill();
+ mJob = 0;
+ }
+ if ( mProgressItem ) {
+ mProgressItem->setStatus( "Canceled" );
+ mProgressItem->setComplete();
+ mProgressItem = 0;
+ }
+}
+
+void KNJobData::setJob( KIO::Job *job )
+{
+ mJob = job;
+ if ( job ) {
+ connect( job, SIGNAL( percent(KIO::Job*, unsigned long) ),
+ SLOT( slotJobPercent(KIO::Job*, unsigned long) ) );
+ connect( job, SIGNAL( infoMessage(KIO::Job*, const QString&) ),
+ SLOT( slotJobInfoMessage(KIO::Job*, const QString&) ) );
+ }
+}
+
+void KNJobData::createProgressItem()
+{
+ if ( mProgressItem )
+ return;
+ KNNntpAccount *acc = static_cast<KNNntpAccount*>( account() );
+ QString msg = i18n( "KNode" );
+ if ( type() == JTmail )
+ msg = i18n( "Sending message" );
+ else {
+ if ( acc )
+ msg = QStyleSheet::escape( acc->name() );
+ }
+ bool encr = false;
+ if ( acc && acc->encryption() != KNServerInfo::None )
+ encr = true;
+ mProgressItem = KPIM::ProgressManager::createProgressItem( 0,
+ KPIM::ProgressManager::getUniqueID(), msg, i18n( "Waiting..." ), true, encr );
+}
+
+void KNJobData::slotJobPercent( KIO::Job*, unsigned long percent )
+{
+ kdDebug(5003) << k_funcinfo << "Progress: " << percent << endl;
+ setProgress( percent );
+}
+
+void KNJobData::slotJobInfoMessage( KIO::Job*, const QString &msg )
+{
+ kdDebug(5003) << k_funcinfo << "Status: " << msg << endl;
+ setStatus( msg );
+}
+
+
+#include "knjobdata.moc"
diff --git a/knode/knjobdata.h b/knode/knjobdata.h
new file mode 100644
index 000000000..5733a9883
--- /dev/null
+++ b/knode/knjobdata.h
@@ -0,0 +1,141 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNJOBDATA_H
+#define KNJOBDATA_H
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+#include <libkdepim/progressmanager.h>
+
+namespace KIO {
+ class Job;
+}
+
+class KNJobData;
+class KNServerInfo;
+
+
+class KNJobConsumer {
+
+ public:
+ KNJobConsumer();
+ virtual ~KNJobConsumer();
+
+ /** Send the job to KNNetAccess and append it to the
+ joblist */
+ void emitJob(KNJobData *j);
+
+ /** Remove the job from the joblist and process it by
+ calling @ref processJob */
+ void jobDone(KNJobData *j);
+
+ /** Returns true if we are waiting for at least one job
+ to be completed */
+ bool jobsPending() const { return !mJobs.isEmpty(); }
+
+ protected:
+ /** The actual work is done here */
+ virtual void processJob(KNJobData *j);
+ QValueList<KNJobData*> mJobs;
+
+};
+
+
+class KNJobItem {
+
+ public:
+ KNJobItem() {}
+ virtual ~KNJobItem() {}
+
+ virtual bool isLocked() { return false; }
+ virtual void setLocked(bool) { }
+
+ virtual QString prepareForExecution() { return QString::null; }
+
+};
+
+
+class KNJobData : public QObject
+{
+ Q_OBJECT
+
+ public:
+
+ friend class KNJobConsumer;
+
+ enum jobType { JTLoadGroups=1,
+ JTFetchGroups,
+ JTCheckNewGroups,
+ JTfetchNewHeaders,
+ JTsilentFetchNewHeaders,
+ JTfetchArticle,
+ JTpostArticle,
+ JTmail,
+ JTfetchSource };
+
+ KNJobData(jobType t, KNJobConsumer *c, KNServerInfo *a, KNJobItem *i);
+ ~KNJobData();
+
+ jobType type() const { return t_ype; }
+
+ bool net() const { return (t_ype!=JTLoadGroups); }
+ KNServerInfo* account() const { return a_ccount; }
+ KNJobItem* data() const { return d_ata; }
+
+ const QString& errorString() const { return e_rrorString; }
+ bool success() const { return e_rrorString.isEmpty(); }
+ bool canceled() const { return c_anceled; }
+ bool authError() const { return a_uthError; }
+
+ void setErrorString(const QString& s) { e_rrorString=s; }
+ void cancel();
+ void setAuthError(bool b) { a_uthError=b; }
+
+ void prepareForExecution() { e_rrorString = d_ata->prepareForExecution(); }
+ void notifyConsumer();
+
+ KIO::Job* job() const { return mJob; }
+ void setJob( KIO::Job *job );
+
+ KPIM::ProgressItem* progressItem() const { return mProgressItem; }
+ void createProgressItem();
+
+ // safe forwards to the progress item
+ void setStatus( const QString &msg ) { if ( mProgressItem ) mProgressItem->setStatus( msg ); }
+ void setProgress( unsigned int progress ) { if ( mProgressItem ) mProgressItem->setProgress( progress ); }
+ void setComplete() { if ( mProgressItem ) { mProgressItem->setComplete(); mProgressItem = 0; } }
+
+ protected:
+ jobType t_ype;
+ KNJobItem *d_ata;
+ KNServerInfo *a_ccount;
+ QString e_rrorString;
+ bool c_anceled;
+ bool a_uthError;
+ KNJobConsumer *c_onsumer;
+
+ private slots:
+ void slotJobPercent( KIO::Job *job, unsigned long percent );
+ void slotJobInfoMessage( KIO::Job *job, const QString &msg );
+
+ private:
+ KIO::Job *mJob;
+ KPIM::ProgressItem *mProgressItem;
+
+};
+
+
+#endif
diff --git a/knode/knmainwidget.cpp b/knode/knmainwidget.cpp
new file mode 100644
index 000000000..e4d91e812
--- /dev/null
+++ b/knode/knmainwidget.cpp
@@ -0,0 +1,2215 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2004-2005 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+#include "knmainwidget.h"
+
+#include <qhbox.h>
+#include <qlayout.h>
+#include <ktoolbar.h>
+
+#include <kinputdialog.h>
+#include <kaccel.h>
+#include <kxmlguiclient.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kedittoolbar.h>
+#include <kstdaction.h>
+#include <kdebug.h>
+#include <kmenubar.h>
+#include <kiconloader.h>
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <kapplication.h>
+
+#include "broadcaststatus.h"
+#include "krsqueezedtextlabel.h"
+#include "recentaddresses.h"
+using KPIM::BroadcastStatus;
+using KRecentAddress::RecentAddresses;
+
+//GUI
+#include "knmainwidget.h"
+#include "knarticlewindow.h"
+#include "kncollectionview.h"
+#include "kncollectionviewitem.h"
+#include "knhdrviewitem.h"
+
+//Core
+#include "articlewidget.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "knarticlemanager.h"
+#include "knarticlefactory.h"
+#include "kngroupmanager.h"
+#include "knnntpaccount.h"
+#include "knaccountmanager.h"
+#include "knnetaccess.h"
+#include "knfiltermanager.h"
+#include "knfoldermanager.h"
+#include "knfolder.h"
+#include "kncleanup.h"
+#include "utilities.h"
+#include "knscoring.h"
+#include <kpgp.h>
+#include "knmemorymanager.h"
+#include <kcmdlineargs.h>
+
+#include <klistviewsearchline.h>
+
+using namespace KNode;
+
+KNGlobals knGlobals;
+
+KNMainWidget::KNMainWidget( KXMLGUIClient* client, bool detachable, QWidget* parent,
+ const char* name )
+ : DCOPObject("KNodeIface"), KDockArea( parent, name ),
+ b_lockui( false ), m_GUIClient( client )
+{
+ knGlobals.top=this;
+ knGlobals.guiClient=client;
+ knGlobals.topWidget=this;
+
+ //------------------------------- <CONFIG> ----------------------------------
+ c_fgManager = knGlobals.configManager();
+ //------------------------------- </CONFIG> ----------------------------------
+
+ //-------------------------------- <GUI> ------------------------------------
+ QAccel *accel = new QAccel( this );
+ initStatusBar();
+
+ //setup splitter behavior
+ manager()->setSplitterHighResolution(true);
+ manager()->setSplitterOpaqueResize(true);
+
+ //article view
+ a_rtDock = createDockWidget("article_viewer", SmallIcon("contents"), 0,
+ kapp->makeStdCaption(i18n("Article Viewer")), i18n("Article Viewer"));
+ if (!detachable) {
+ a_rtDock->setEnableDocking(KDockWidget::DockFullSite);
+ }
+ KDockWidgetHeader *header = new KDockWidgetHeader(a_rtDock, "artDockHeader");
+ a_rtDock->setHeader(header);
+ mArticleViewer = new ArticleWidget( a_rtDock, knGlobals.guiClient, actionCollection(), "articleViewer");
+ header->setDragPanel( new KNDockWidgetHeaderDrag( mArticleViewer, header, a_rtDock ) );
+ knGlobals.artWidget = mArticleViewer;
+ a_rtDock->setWidget( mArticleViewer );
+ //setView(a_rtDock);
+ setMainDockWidget(a_rtDock);
+
+ connect(a_rtDock, SIGNAL(iMBeingClosed()), SLOT(slotArticleDockHidden()));
+ connect(a_rtDock, SIGNAL(hasUndocked()), SLOT(slotArticleDockHidden()));
+ connect( mArticleViewer, SIGNAL(focusChangeRequest(QWidget*)), SLOT(slotDockWidgetFocusChangeRequest(QWidget*)) );
+
+ //collection view
+ c_olDock = createDockWidget("group_view", UserIcon("group"), 0,
+ kapp->makeStdCaption(i18n("Group View")), i18n("Group View"));
+ if (!detachable) {
+ c_olDock->setEnableDocking(KDockWidget::DockFullSite);
+ }
+ header = new KDockWidgetHeader(c_olDock, "colDockHeader");
+ c_olDock->setHeader(header);
+ c_olView = new KNCollectionView(this, "collectionView");
+ header->setDragPanel(new KNDockWidgetHeaderDrag(c_olView, header, c_olDock));
+ c_olDock->setWidget(c_olView);
+ c_olDock->manualDock(a_rtDock, KDockWidget::DockLeft, 3000);
+
+ connect(c_olDock, SIGNAL(iMBeingClosed()), SLOT(slotGroupDockHidden()));
+ connect(c_olDock, SIGNAL(hasUndocked()), SLOT(slotGroupDockHidden()));
+ connect(c_olView, SIGNAL(focusChangeRequest(QWidget *)), SLOT(slotDockWidgetFocusChangeRequest(QWidget *)));
+ connect(c_olView, SIGNAL(selectionChanged(QListViewItem*)),
+ SLOT(slotCollectionSelected(QListViewItem*)));
+ connect(c_olView, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
+ SLOT(slotCollectionRMB(KListView*, QListViewItem*, const QPoint&)));
+ connect(c_olView, SIGNAL(folderDrop(QDropEvent*, KNCollectionViewItem*)),
+ SLOT(slotCollectionViewDrop(QDropEvent*, KNCollectionViewItem*)));
+ connect(c_olView, SIGNAL(itemRenamed(QListViewItem*)),
+ SLOT(slotCollectionRenamed(QListViewItem*)));
+
+ accel->connectItem( accel->insertItem(Key_Up), mArticleViewer, SLOT(scrollUp()) );
+ accel->connectItem( accel->insertItem(Key_Down), mArticleViewer, SLOT(scrollDown()) );
+ accel->connectItem( accel->insertItem(Key_Prior), mArticleViewer, SLOT(scrollPrior()) );
+ accel->connectItem( accel->insertItem(Key_Next), mArticleViewer, SLOT(scrollNext()) );
+
+ //header view
+ h_drDock = createDockWidget("header_view", SmallIcon("text_block"), 0,
+ kapp->makeStdCaption(i18n("Header View")), i18n("Header View"));
+ if (!detachable) {
+ h_drDock->setEnableDocking(KDockWidget::DockFullSite);
+ }
+ header = new KDockWidgetHeader(h_drDock, "headerDockHeader");
+ h_drDock->setHeader(header);
+ QWidget *dummy = new QWidget(h_drDock);
+ QVBoxLayout *vlay = new QVBoxLayout(dummy);
+ h_drView = new KNHeaderView( dummy, "hdrView" );
+ header->setDragPanel(new KNDockWidgetHeaderDrag(h_drView, header, h_drDock));
+ h_drDock->setWidget(dummy);
+ h_drDock->manualDock(a_rtDock, KDockWidget::DockTop, 5000);
+
+ q_uicksearch = new KToolBar(dummy, "search toolbar");
+ KAction *resetQuickSearch = new KAction( i18n( "Reset Quick Search" ),
+ QApplication::reverseLayout()
+ ? "clear_left"
+ : "locationbar_erase",
+ 0, actionCollection(),
+ "reset_quicksearch" );
+ resetQuickSearch->plug( q_uicksearch );
+ resetQuickSearch->setWhatsThis( i18n( "<b>Reset Quick Search</b><br>"
+ "Resets the quick search so that "
+ "all messages are shown again." ) );
+
+ QLabel *lbl = new QLabel(i18n("&Search:"), q_uicksearch, "kde toolbar widget");
+ s_earchLineEdit = new KListViewSearchLine(q_uicksearch, h_drView, "KListViewSearchLine");
+ q_uicksearch->setStretchableWidget(s_earchLineEdit);
+ lbl->setBuddy(s_earchLineEdit);
+ connect( resetQuickSearch, SIGNAL( activated() ), s_earchLineEdit, SLOT( clear() ));
+
+ vlay->addWidget(q_uicksearch);
+ vlay->addWidget(h_drView);
+
+ connect(h_drDock, SIGNAL(iMBeingClosed()), SLOT(slotHeaderDockHidden()));
+ connect(h_drDock, SIGNAL(hasUndocked()), SLOT(slotHeaderDockHidden()));
+ connect(h_drView, SIGNAL(focusChangeRequest(QWidget *)),
+ SLOT(slotDockWidgetFocusChangeRequest(QWidget *)));
+ connect(h_drView, SIGNAL(itemSelected(QListViewItem*)),
+ SLOT(slotArticleSelected(QListViewItem*)));
+ connect(h_drView, SIGNAL(selectionChanged()),
+ SLOT(slotArticleSelectionChanged()));
+ connect(h_drView, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
+ SLOT(slotArticleRMB(KListView*, QListViewItem*, const QPoint&)));
+ connect(h_drView, SIGNAL(doubleClick(QListViewItem *)),
+ SLOT(slotOpenArticle(QListViewItem *)));
+ connect(h_drView, SIGNAL(sortingChanged(int)),
+ SLOT(slotHdrViewSortingChanged(int)));
+
+ //actions
+ initActions();
+
+ //-------------------------------- </GUI> ------------------------------------
+
+ //-------------------------------- <CORE> ------------------------------------
+
+ //Network
+ n_etAccess = knGlobals.netAccess();
+ connect(n_etAccess, SIGNAL(netActive(bool)), this, SLOT(slotNetworkActive(bool)));
+
+ //Filter Manager
+ f_ilManager = knGlobals.filterManager();
+ f_ilManager->setMenuAction(a_ctArtFilter, a_ctArtFilterKeyb);
+
+ //Article Manager
+ a_rtManager = knGlobals.articleManager();
+ a_rtManager->setView(h_drView);
+
+ //Group Manager
+ g_rpManager = knGlobals.groupManager();
+
+ //Folder Manager
+ f_olManager = knGlobals.folderManager();
+
+ //Account Manager
+ a_ccManager = knGlobals.accountManager();
+
+ //Article Factory
+ a_rtFactory=new KNArticleFactory();
+ knGlobals.artFactory=a_rtFactory;
+
+ // Score Manager
+ s_coreManager = knGlobals.scoringManager();
+ //connect(s_coreManager, SIGNAL(changedRules()), SLOT(slotReScore()));
+ connect(s_coreManager, SIGNAL(finishedEditing()), SLOT(slotReScore()));
+
+ // Memory Manager
+ m_emManager = knGlobals.memoryManager();
+
+ // create a global pgp instance
+ p_gp = new Kpgp::Module();
+ knGlobals.pgp = p_gp;
+
+ //-------------------------------- </CORE> -----------------------------------
+
+ //apply saved options
+ readOptions();
+
+ //apply configuration
+ configChanged();
+
+ // set the keyboard focus indicator on the first item in the Collection View
+ if( c_olView->firstChild() ) {
+ QListViewItem *i = c_olView->firstChild();
+ bool open = i->isOpen();
+ c_olView->setActive( i );
+ i->setOpen( open );
+ }
+
+ c_olView->setFocus();
+
+ setStatusMsg();
+
+ if( firstStart() ) { // open the config dialog on the first start
+ show(); // the settings dialog must appear in front of the main window!
+ slotSettings();
+ }
+}
+
+KNMainWidget::~KNMainWidget()
+{
+ delete a_ccel;
+
+ h_drView->clear(); //avoid some random crashes in KNHdrViewItem::~KNHdrViewItem()
+
+ delete n_etAccess;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Net deleted" << endl;
+
+ delete a_rtManager;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Article Manager deleted" << endl;
+
+ delete a_rtFactory;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Article Factory deleted" << endl;
+
+ delete g_rpManager;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Group Manager deleted" << endl;
+
+ delete f_olManager;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Folder Manager deleted" << endl;
+
+ delete f_ilManager;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Filter Manager deleted" << endl;
+
+ delete a_ccManager;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Account Manager deleted" << endl;
+
+ delete c_fgManager;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Config deleted" << endl;
+
+ delete m_emManager;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : Memory Manager deleted" << endl;
+
+ delete p_gp;
+ kdDebug(5003) << "KNMainWidget::~KNMainWidget() : PGP deleted" << endl;
+
+ delete c_olDock;
+ delete h_drDock;
+ delete a_rtDock;
+}
+
+void KNMainWidget::initStatusBar()
+{
+ //statusbar
+ KMainWindow *mainWin = dynamic_cast<KMainWindow*>(topLevelWidget());
+ KStatusBar *sb = mainWin ? mainWin->statusBar() : 0;
+ s_tatusFilter = new KRSqueezedTextLabel( QString::null, sb );
+ s_tatusFilter->setAlignment( AlignLeft | AlignVCenter );
+ s_tatusGroup = new KRSqueezedTextLabel( QString::null, sb );
+ s_tatusGroup->setAlignment( AlignLeft | AlignVCenter );
+}
+
+//================================== GUI =================================
+
+void KNMainWidget::setStatusMsg(const QString& text, int id)
+{
+ KMainWindow *mainWin = dynamic_cast<KMainWindow*>(topLevelWidget());
+ KStatusBar *bar = mainWin ? mainWin->statusBar() : 0;
+ if ( !bar )
+ return;
+ bar->clear();
+ if (text.isEmpty() && (id==SB_MAIN)) {
+ if (knGlobals.netAccess()->currentMsg().isEmpty())
+ BroadcastStatus::instance()->setStatusMsg(i18n(" Ready"));
+ else
+ BroadcastStatus::instance()->setStatusMsg(knGlobals.netAccess()->currentMsg());
+ } else {
+ switch(id) {
+ case SB_MAIN:
+ BroadcastStatus::instance()->setStatusMsg(text); break;
+ case SB_GROUP:
+ s_tatusGroup->setText(text); break;
+ case SB_FILTER:
+ s_tatusFilter->setText(text); break;
+ }
+ }
+}
+
+
+void KNMainWidget::setStatusHelpMsg(const QString& text)
+{
+ KMainWindow *mainWin = dynamic_cast<KMainWindow*>(topLevelWidget());
+ KStatusBar *bar = mainWin ? mainWin->statusBar() : 0;
+ if ( bar )
+ bar->message(text, 2000);
+}
+
+
+void KNMainWidget::updateCaption()
+{
+ QString newCaption=i18n("KDE News Reader");
+ if (g_rpManager->currentGroup()) {
+ newCaption = g_rpManager->currentGroup()->name();
+ if (g_rpManager->currentGroup()->status()==KNGroup::moderated)
+ newCaption += i18n(" (moderated)");
+ } else if (a_ccManager->currentAccount()) {
+ newCaption = a_ccManager->currentAccount()->name();
+ } else if (f_olManager->currentFolder()) {
+ newCaption = f_olManager->currentFolder()->name();
+ }
+ emit signalCaptionChangeRequest(newCaption);
+}
+
+
+void KNMainWidget::setCursorBusy(bool b)
+{
+ if(b) KApplication::setOverrideCursor(waitCursor);
+ else KApplication::restoreOverrideCursor();
+}
+
+
+void KNMainWidget::blockUI(bool b)
+{
+ b_lockui = b;
+ KMainWindow *mainWin = dynamic_cast<KMainWindow*>(topLevelWidget());
+ KMenuBar *mbar = mainWin ? mainWin->menuBar() : 0;
+ if ( mbar )
+ mbar->setEnabled(!b);
+ a_ccel->setEnabled(!b);
+ KAccel *naccel = mainWin ? mainWin->accel() : 0;
+ if ( naccel )
+ naccel->setEnabled(!b);
+ if (b)
+ installEventFilter(this);
+ else
+ removeEventFilter(this);
+ setCursorBusy(b);
+}
+
+
+void KNMainWidget::disableAccels(bool b)
+{
+ a_ccel->setEnabled(!b);
+ KMainWindow *mainWin = dynamic_cast<KMainWindow*>(topLevelWidget());
+ KAccel *naccel = mainWin ? mainWin->accel() : 0;
+ if ( naccel )
+ naccel->setEnabled(!b);
+ if (b)
+ installEventFilter(this);
+ else
+ removeEventFilter(this);
+}
+
+
+// processEvents with some blocking
+void KNMainWidget::secureProcessEvents()
+{
+ b_lockui = true;
+ KMainWindow *mainWin = dynamic_cast<KMainWindow*>(topLevelWidget());
+ KMenuBar *mbar = mainWin ? mainWin->menuBar() : 0;
+ if ( mbar )
+ mbar->setEnabled(false);
+ a_ccel->setEnabled(false);
+ KAccel *naccel = mainWin ? mainWin->accel() : 0;
+ if ( naccel )
+ naccel->setEnabled(false);
+ installEventFilter(this);
+
+ kapp->processEvents();
+
+ b_lockui = false;
+ if ( mbar )
+ mbar->setEnabled(true);
+ a_ccel->setEnabled(true);
+ if ( naccel )
+ naccel->setEnabled(true);
+ removeEventFilter(this);
+}
+
+
+QSize KNMainWidget::sizeHint() const
+{
+ return QSize(759,478); // default optimized for 800x600
+}
+
+
+void KNMainWidget::openURL(const KURL &url)
+{
+ kdDebug(5003) << k_funcinfo << url << endl;
+ QString host = url.host();
+ unsigned short int port = url.port();
+ KNNntpAccount *acc=0;
+
+ if (url.url().left(7) == "news://") {
+
+ // lets see if we already have an account for this host...
+ QValueList<KNNntpAccount*>::Iterator it;
+ for ( it = a_ccManager->begin(); it != a_ccManager->end(); ++it ) {
+ if ( (*it)->server().lower() == host.lower() && ( port==0 || (*it)->port() == port ) ) {
+ acc = *it;
+ break;
+ }
+ }
+
+ if(!acc) {
+ acc=new KNNntpAccount();
+ acc->setName(host);
+ acc->setServer(host);
+
+ if(port!=0)
+ acc->setPort(port);
+
+ if(url.hasUser() && url.hasPass()) {
+ acc->setNeedsLogon(true);
+ acc->setUser(url.user());
+ acc->setPass(url.pass());
+ }
+
+ if(!a_ccManager->newAccount(acc))
+ return;
+ }
+ } else {
+ if (url.url().left(5) == "news:") {
+ // TODO: make the default server configurable
+ acc = a_ccManager->currentAccount();
+ if ( acc == 0 )
+ acc = a_ccManager->first();
+ } else {
+ kdDebug(5003) << "KNMainWidget::openURL() URL is not a valid news URL" << endl;
+ }
+ }
+
+ if (acc) {
+ bool isMID=(url.url().contains('@')==1);
+
+ if (!isMID) {
+ QString groupname=url.path(-1);
+ while(groupname.startsWith("/"))
+ groupname.remove(0,1);
+ QListViewItem *item=0;
+ if(groupname.isEmpty())
+ item=acc->listItem();
+ else {
+ KNGroup *grp= g_rpManager->group(groupname, acc);
+
+ if(!grp) {
+ KNGroupInfo inf(groupname, "");
+ g_rpManager->subscribeGroup(&inf, acc);
+ grp=g_rpManager->group(groupname, acc);
+ if(grp)
+ item=grp->listItem();
+ }
+ else
+ item=grp->listItem();
+ }
+
+ if (item) {
+ c_olView->ensureItemVisible(item);
+ c_olView->setActive( item );
+ }
+ } else {
+ QString groupname = url.url().mid( url.protocol().length()+1 );
+ KNGroup *g = g_rpManager->currentGroup();
+ if (g == 0)
+ g = g_rpManager->firstGroupOfAccount(acc);
+
+ if (g) {
+ if(!KNArticleWindow::raiseWindowForArticle(groupname.latin1())) { //article not yet opened
+ KNRemoteArticle *a=new KNRemoteArticle(g);
+ QString messageID = "<"+groupname+">";
+ a->messageID()->from7BitString(messageID.latin1());
+ KNArticleWindow *awin=new KNArticleWindow(a);
+ awin->show();
+ }
+ } else {
+ // TODO: fetch without group
+ kdDebug(5003) << "KNMainWidget::openURL() account has no groups" << endl;
+ }
+ }
+ }
+}
+
+
+// update fonts and colors
+void KNMainWidget::configChanged()
+{
+ h_drView->readConfig();
+ c_olView->readConfig();
+ a_rtManager->updateListViewItems();
+}
+
+
+void KNMainWidget::initActions()
+{
+ a_ccel=new KAccel(this);
+ mArticleViewer->setCharsetKeyboardAction()->plugAccel(a_ccel);
+
+ //navigation
+ a_ctNavNextArt = new KAction( KGuiItem(i18n("&Next Article"), "next",
+ i18n("Go to next article")), "N;Right", h_drView,
+ SLOT(nextArticle()), actionCollection(), "go_nextArticle" );
+ a_ctNavPrevArt = new KAction( KGuiItem(i18n("&Previous Article"), "previous",
+ i18n("Go to previous article")), "P;Left" , h_drView,
+ SLOT(prevArticle()), actionCollection(), "go_prevArticle" );
+ a_ctNavNextUnreadArt = new KAction(i18n("Next Unread &Article"), "1rightarrow", ALT+SHIFT+Key_Space , this,
+ SLOT(slotNavNextUnreadArt()), actionCollection(), "go_nextUnreadArticle");
+ a_ctNavNextUnreadThread = new KAction(i18n("Next Unread &Thread"),"2rightarrow", SHIFT+Key_Space , this,
+ SLOT(slotNavNextUnreadThread()), actionCollection(), "go_nextUnreadThread");
+ a_ctNavNextGroup = new KAction(i18n("Ne&xt Group"), "down", Key_Plus , c_olView,
+ SLOT(nextGroup()), actionCollection(), "go_nextGroup");
+ a_ctNavPrevGroup = new KAction(i18n("Pre&vious Group"), "up", Key_Minus , c_olView,
+ SLOT(prevGroup()), actionCollection(), "go_prevGroup");
+ a_ctNavReadThrough = new KAction(i18n("Read &Through Articles"), Key_Space , this,
+ SLOT(slotNavReadThrough()), actionCollection(), "go_readThrough");
+ a_ctNavReadThrough->plugAccel(a_ccel);
+
+ QAccel *accel = new QAccel( this );
+ new KAction( i18n("Focus on Next Folder"), CTRL+Key_Right, c_olView,
+ SLOT(incCurrentFolder()), actionCollection(), "inc_current_folder" );
+ accel->connectItem(accel->insertItem(CTRL+Key_Right),
+ c_olView, SLOT(incCurrentFolder()));
+ new KAction( i18n("Focus on Previous Folder"), CTRL+Key_Left, c_olView,
+ SLOT(decCurrentFolder()), actionCollection(), "dec_current_folder" );
+ accel->connectItem(accel->insertItem(CTRL+Key_Left),
+ c_olView, SLOT(decCurrentFolder()));
+ new KAction( i18n("Select Folder with Focus"), CTRL+Key_Space, c_olView,
+ SLOT(selectCurrentFolder()), actionCollection(), "select_current_folder" );
+ accel->connectItem(accel->insertItem(CTRL+Key_Space),
+ c_olView, SLOT(selectCurrentFolder()));
+
+ new KAction( i18n("Focus on Next Article"), ALT+Key_Right, h_drView,
+ SLOT(incCurrentArticle()), actionCollection(), "inc_current_article" );
+ accel->connectItem( accel->insertItem(ALT+Key_Right),
+ h_drView, SLOT(incCurrentArticle()) );
+ new KAction( i18n("Focus on Previous Article"), ALT+Key_Left, h_drView,
+ SLOT(decCurrentArticle()), actionCollection(), "dec_current_article" );
+ accel->connectItem( accel->insertItem(ALT+Key_Left),
+ h_drView, SLOT(decCurrentArticle()) );
+ new KAction( i18n("Select Article with Focus"), ALT+Key_Space, h_drView,
+ SLOT(selectCurrentArticle()), actionCollection(), "select_current_article" );
+ accel->connectItem( accel->insertItem(ALT+Key_Space),
+ h_drView, SLOT(selectCurrentArticle()) );
+
+ //collection-view - accounts
+ a_ctAccProperties = new KAction(i18n("Account &Properties"), "configure", 0, this,
+ SLOT(slotAccProperties()), actionCollection(), "account_properties");
+ a_ctAccRename = new KAction(i18n("&Rename Account"), "text", 0, this,
+ SLOT(slotAccRename()), actionCollection(), "account_rename");
+ a_ctAccSubscribe = new KAction(i18n("&Subscribe to Newsgroups..."), "news_subscribe", 0, this,
+ SLOT(slotAccSubscribe()), actionCollection(), "account_subscribe");
+ a_ctAccExpireAll = new KAction(i18n("&Expire All Groups"), 0, this,
+ SLOT(slotAccExpireAll()), actionCollection(), "account_expire_all");
+ a_ctAccGetNewHdrs = new KAction(i18n("&Get New Articles in All Groups"), "mail_get", 0, this,
+ SLOT(slotAccGetNewHdrs()), actionCollection(), "account_dnlHeaders");
+ a_ctAccGetNewHdrsAll = new KAction(i18n("&Get New Articles in All Accounts"), "mail_get_all", 0, this,
+ SLOT(slotAccGetNewHdrsAll()), actionCollection(), "account_dnlAllHeaders");
+ a_ctAccDelete = new KAction(i18n("&Delete Account"), "editdelete", 0, this,
+ SLOT(slotAccDelete()), actionCollection(), "account_delete");
+ a_ctAccPostNewArticle = new KAction(i18n("&Post to Newsgroup..."), "mail_new", CTRL+Key_N, this,
+ SLOT(slotAccPostNewArticle()), actionCollection(), "article_postNew");
+
+ //collection-view - groups
+ a_ctGrpProperties = new KAction(i18n("Group &Properties"), "configure", 0, this,
+ SLOT(slotGrpProperties()), actionCollection(), "group_properties");
+ a_ctGrpRename = new KAction(i18n("Rename &Group"), "text", 0, this,
+ SLOT(slotGrpRename()), actionCollection(), "group_rename");
+ a_ctGrpGetNewHdrs = new KAction(i18n("&Get New Articles"), "mail_get" , 0, this,
+ SLOT(slotGrpGetNewHdrs()), actionCollection(), "group_dnlHeaders");
+ a_ctGrpExpire = new KAction(i18n("E&xpire Group"), "wizard", 0, this,
+ SLOT(slotGrpExpire()), actionCollection(), "group_expire");
+ a_ctGrpReorganize = new KAction(i18n("Re&organize Group"), 0, this,
+ SLOT(slotGrpReorganize()), actionCollection(), "group_reorg");
+ a_ctGrpUnsubscribe = new KAction(i18n("&Unsubscribe From Group"), "news_unsubscribe", 0, this,
+ SLOT(slotGrpUnsubscribe()), actionCollection(), "group_unsubscribe");
+ a_ctGrpSetAllRead = new KAction(i18n("Mark All as &Read"), "goto", 0, this,
+ SLOT(slotGrpSetAllRead()), actionCollection(), "group_allRead");
+ a_ctGrpSetAllUnread = new KAction(i18n("Mark All as U&nread"), 0, this,
+ SLOT(slotGrpSetAllUnread()), actionCollection(), "group_allUnread");
+ a_ctGrpSetUnread = new KAction(i18n("Mark Last as Unr&ead..."), 0, this,
+ SLOT(slotGrpSetUnread()), actionCollection(), "group_unread");
+
+
+
+ (void) new KAction( i18n("&Configure KNode..."),
+ "configure", 0, this,
+ SLOT(slotSettings()), actionCollection(),
+ "knode_configure_knode" );
+
+ //collection-view - folder
+ a_ctFolNew = new KAction(i18n("&New Folder"), "folder_new", 0, this,
+ SLOT(slotFolNew()), actionCollection(), "folder_new");
+ a_ctFolNewChild = new KAction(i18n("New &Subfolder"), "folder_new", 0, this,
+ SLOT(slotFolNewChild()), actionCollection(), "folder_newChild");
+ a_ctFolDelete = new KAction(i18n("&Delete Folder"), "editdelete", 0, this,
+ SLOT(slotFolDelete()), actionCollection(), "folder_delete");
+ a_ctFolRename = new KAction(i18n("&Rename Folder"), "text", 0, this,
+ SLOT(slotFolRename()), actionCollection(), "folder_rename");
+ a_ctFolCompact = new KAction(i18n("C&ompact Folder"), "wizard", 0, this,
+ SLOT(slotFolCompact()), actionCollection(), "folder_compact");
+ a_ctFolCompactAll = new KAction(i18n("Co&mpact All Folders"), 0, this,
+ SLOT(slotFolCompactAll()), actionCollection(), "folder_compact_all");
+ a_ctFolEmpty = new KAction(i18n("&Empty Folder"), 0, this,
+ SLOT(slotFolEmpty()), actionCollection(), "folder_empty");
+ a_ctFolMboxImport = new KAction(i18n("&Import MBox Folder..."), 0, this,
+ SLOT(slotFolMBoxImport()), actionCollection(), "folder_MboxImport");
+ a_ctFolMboxExport = new KAction(i18n("E&xport as MBox Folder..."), 0, this,
+ SLOT(slotFolMBoxExport()), actionCollection(), "folder_MboxExport");
+
+ //header-view - list-handling
+ a_ctArtSortHeaders = new KSelectAction(i18n("S&ort"), 0, actionCollection(), "view_Sort");
+ QStringList items;
+ items += i18n("By &Subject");
+ items += i18n("By S&ender");
+ items += i18n("By S&core");
+ items += i18n("By &Lines");
+ items += i18n("By &Date");
+ a_ctArtSortHeaders->setItems(items);
+ a_ctArtSortHeaders->setShortcutConfigurable(false);
+ connect(a_ctArtSortHeaders, SIGNAL(activated(int)), this, SLOT(slotArtSortHeaders(int)));
+ a_ctArtSortHeadersKeyb = new KAction(i18n("Sort"), QString::null, Key_F7 , this,
+ SLOT(slotArtSortHeadersKeyb()), actionCollection(), "view_Sort_Keyb");
+ a_ctArtSortHeadersKeyb->plugAccel(a_ccel);
+ a_ctArtFilter = new KNFilterSelectAction(i18n("&Filter"), "filter",
+ actionCollection(), "view_Filter");
+ a_ctArtFilter->setShortcutConfigurable(false);
+ a_ctArtFilterKeyb = new KAction(i18n("Filter"), Key_F6, actionCollection(), "view_Filter_Keyb");
+ a_ctArtFilterKeyb->plugAccel(a_ccel);
+ a_ctArtSearch = new KAction(i18n("&Search Articles..."),"mail_find" , Key_F4 , this,
+ SLOT(slotArtSearch()), actionCollection(), "article_search");
+ a_ctArtRefreshList = new KAction(i18n("&Refresh List"),"reload", KStdAccel::shortcut(KStdAccel::Reload), this,
+ SLOT(slotArtRefreshList()), actionCollection(), "view_Refresh");
+ a_ctArtCollapseAll = new KAction(i18n("&Collapse All Threads"), 0 , this,
+ SLOT(slotArtCollapseAll()), actionCollection(), "view_CollapseAll");
+ a_ctArtExpandAll = new KAction(i18n("E&xpand All Threads"), 0 , this,
+ SLOT(slotArtExpandAll()), actionCollection(), "view_ExpandAll");
+ a_ctArtToggleThread = new KAction(i18n("&Toggle Subthread"), Key_T, this,
+ SLOT(slotArtToggleThread()), actionCollection(), "thread_toggle");
+ a_ctArtToggleShowThreads = new KToggleAction(i18n("Show T&hreads"), 0 , this,
+ SLOT(slotArtToggleShowThreads()), actionCollection(), "view_showThreads");
+ a_ctArtToggleShowThreads->setCheckedState(i18n("Hide T&hreads"));
+
+ a_ctArtToggleShowThreads->setChecked(c_fgManager->readNewsGeneral()->showThreads());
+
+ //header-view - remote articles
+ a_ctArtSetArtRead = new KAction(i18n("Mark as &Read"), Key_D , this,
+ SLOT(slotArtSetArtRead()), actionCollection(), "article_read");
+ a_ctArtSetArtUnread = new KAction(i18n("Mar&k as Unread"), Key_U , this,
+ SLOT(slotArtSetArtUnread()), actionCollection(), "article_unread");
+ a_ctArtSetThreadRead = new KAction(i18n("Mark &Thread as Read"), CTRL+Key_D , this,
+ SLOT(slotArtSetThreadRead()), actionCollection(), "thread_read");
+ a_ctArtSetThreadUnread = new KAction(i18n("Mark T&hread as Unread"), CTRL+Key_U , this,
+ SLOT(slotArtSetThreadUnread()), actionCollection(), "thread_unread");
+ a_ctArtOpenNewWindow = new KAction(i18n("Open in Own &Window"), "window_new", Key_O , this,
+ SLOT(slotArtOpenNewWindow()), actionCollection(), "article_ownWindow");
+
+ // scoring
+ a_ctScoresEdit = new KAction(i18n("&Edit Scoring Rules..."), "edit", CTRL+Key_E, this,
+ SLOT(slotScoreEdit()), actionCollection(), "scoreedit");
+ a_ctReScore = new KAction(i18n("Recalculate &Scores"), 0, this,
+ SLOT(slotReScore()),actionCollection(),"rescore");
+ a_ctScoreLower = new KAction(i18n("&Lower Score for Author..."), CTRL+Key_L, this,
+ SLOT(slotScoreLower()), actionCollection(), "scorelower");
+ a_ctScoreRaise = new KAction(i18n("&Raise Score for Author..."), CTRL+Key_I, this,
+ SLOT(slotScoreRaise()),actionCollection(),"scoreraise");
+ a_ctArtToggleIgnored = new KAction(i18n("&Ignore Thread"), "bottom", Key_I , this,
+ SLOT(slotArtToggleIgnored()), actionCollection(), "thread_ignore");
+ a_ctArtToggleWatched = new KAction(i18n("&Watch Thread"), "top", Key_W , this,
+ SLOT(slotArtToggleWatched()), actionCollection(), "thread_watch");
+
+ //header-view local articles
+ a_ctArtSendOutbox = new KAction(i18n("Sen&d Pending Messages"), "mail_send", 0, this,
+ SLOT(slotArtSendOutbox()), actionCollection(), "net_sendPending");
+ a_ctArtDelete = new KAction(i18n("&Delete Article"), "editdelete", Key_Delete, this,
+ SLOT(slotArtDelete()), actionCollection(), "article_delete");
+ a_ctArtSendNow = new KAction(i18n("Send &Now"),"mail_send", 0 , this,
+ SLOT(slotArtSendNow()), actionCollection(), "article_sendNow");
+ a_ctArtEdit = new KAction(i18n("edit article","&Edit Article..."), "edit", Key_E , this,
+ SLOT(slotArtEdit()), actionCollection(), "article_edit");
+
+ //network
+ a_ctNetCancel = new KAction(i18n("Stop &Network"),"stop",0, this,
+ SLOT(slotNetCancel()), actionCollection(), "net_stop");
+ a_ctNetCancel->setEnabled(false);
+
+ a_ctFetchArticleWithID = new KAction(i18n("&Fetch Article with ID..."), 0, this,
+ SLOT(slotFetchArticleWithID()), actionCollection(), "fetch_article_with_id");
+ a_ctFetchArticleWithID->setEnabled(false);
+
+ a_ctToggleGroupView = new KToggleAction(i18n("Show &Group View"), CTRL+Key_G, this,
+ SLOT(slotToggleGroupView()), actionCollection(), "settings_show_groupView");
+ a_ctToggleGroupView->setCheckedState(i18n("Hide &Group View"));
+ a_ctToggleHeaderView = new KToggleAction(i18n("Show &Header View"), CTRL+Key_H, this,
+ SLOT(slotToggleHeaderView()), actionCollection(), "settings_show_headerView");
+ a_ctToggleHeaderView->setCheckedState(i18n("Hide &Header View"));
+ a_ctToggleArticleViewer = new KToggleAction(i18n("Show &Article Viewer"), CTRL+Key_J, this,
+ SLOT(slotToggleArticleViewer()), actionCollection(), "settings_show_articleViewer");
+ a_ctToggleArticleViewer->setCheckedState(i18n("Hide &Article Viewer"));
+ a_ctToggleQuickSearch = new KToggleAction(i18n("Show Quick Search"), QString::null, this,
+ SLOT(slotToggleQuickSearch()), actionCollection(), "settings_show_quickSearch");
+ a_ctToggleQuickSearch->setCheckedState(i18n("Hide Quick Search"));
+ a_ctSwitchToGroupView = new KAction(i18n("Switch to Group View"), Key_G , this,
+ SLOT(slotSwitchToGroupView()), actionCollection(), "switch_to_group_view");
+ a_ctSwitchToGroupView->plugAccel(a_ccel);
+ a_ctSwitchToHeaderView = new KAction(i18n("Switch to Header View"), Key_H , this,
+ SLOT(slotSwitchToHeaderView()), actionCollection(), "switch_to_header_view");
+ a_ctSwitchToHeaderView->plugAccel(a_ccel);
+ a_ctSwitchToArticleViewer = new KAction(i18n("Switch to Article Viewer"), Key_J , this,
+ SLOT(slotSwitchToArticleViewer()), actionCollection(), "switch_to_article_viewer");
+ a_ctSwitchToArticleViewer->plugAccel(a_ccel);
+}
+
+bool KNMainWidget::firstStart()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("GENERAL");
+ QString ver = conf->readEntry("Version");
+ if(!ver.isEmpty())
+ return false;
+
+ KConfig emailConf("emaildefaults");
+
+ emailConf.setGroup("Defaults");
+ QString group = emailConf.readEntry("Profile","Default");
+
+ emailConf.setGroup(QString("PROFILE_%1").arg(group));
+ KNConfig::Identity *id=knGlobals.configManager()->identity();
+ id->setName(emailConf.readEntry("FullName"));
+ id->setEmail(emailConf.readEntry("EmailAddress").latin1());
+ id->setOrga(emailConf.readEntry("Organization"));
+ id->setReplyTo(emailConf.readEntry("ReplyAddr"));
+ id->save();
+
+ KNServerInfo *smtp=knGlobals.accountManager()->smtp();
+ smtp->setServer(emailConf.readEntry("OutgoingServer").latin1());
+ smtp->setPort(25);
+ conf->setGroup("MAILSERVER");
+ smtp->saveConf(conf);
+
+ conf->setGroup("GENERAL");
+ conf->writeEntry("Version", KNODE_VERSION);
+
+ return true;
+}
+
+
+void KNMainWidget::readOptions()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("APPEARANCE");
+
+ if (conf->readBoolEntry("quicksearch", true))
+ a_ctToggleQuickSearch->setChecked(true);
+ else
+ q_uicksearch->hide();
+ c_olView->readConfig();
+ h_drView->readConfig();
+ a_ctArtSortHeaders->setCurrentItem( h_drView->sortColumn() );
+
+ resize(787,478); // default optimized for 800x600
+ //applyMainWindowSettings(KGlobal::config(),"mainWindow_options");
+
+ // restore dock configuration
+ manager()->readConfig(knGlobals.config(),"dock_configuration");
+}
+
+
+void KNMainWidget::saveOptions()
+{
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("APPEARANCE");
+
+ conf->writeEntry("quicksearch", q_uicksearch->isShown());
+ //saveMainWindowSettings(KGlobal::config(),"mainWindow_options");
+
+ c_olView->writeConfig();
+ h_drView->writeConfig();
+ mArticleViewer->writeConfig();
+
+ // store dock configuration
+ manager()->writeConfig(knGlobals.config(),"dock_configuration");
+}
+
+
+bool KNMainWidget::requestShutdown()
+{
+ kdDebug(5003) << "KNMainWidget::requestShutdown()" << endl;
+
+ if( a_rtFactory->jobsPending() &&
+ KMessageBox::No==KMessageBox::warningYesNo(this, i18n(
+"KNode is currently sending articles. If you quit now you might lose these \
+articles.\nDo you want to quit anyway?"), QString::null, KStdGuiItem::quit(), KStdGuiItem::cancel())
+ )
+ return false;
+
+ if(!a_rtFactory->closeComposeWindows())
+ return false;
+
+ return true;
+}
+
+
+void KNMainWidget::prepareShutdown()
+{
+ kdDebug(5003) << "KNMainWidget::prepareShutdown()" << endl;
+
+ //cleanup article-views
+ ArticleWidget::cleanup();
+
+ // expire groups (if necessary)
+ KNCleanUp *cup = new KNCleanUp();
+ g_rpManager->expireAll(cup);
+ cup->start();
+
+ // compact folders
+ KNConfig::Cleanup *conf=c_fgManager->cleanup();
+ if (conf->compactToday()) {
+ cup->reset();
+ f_olManager->compactAll(cup);
+ cup->start();
+ conf->setLastCompactDate();
+ }
+
+ delete cup;
+
+ saveOptions();
+ RecentAddresses::self(knGlobals.config())->save( knGlobals.config() );
+ c_fgManager->syncConfig();
+ a_rtManager->deleteTempFiles();
+ g_rpManager->syncGroups();
+ f_olManager->syncFolders();
+ f_ilManager->prepareShutdown();
+ a_ccManager->prepareShutdown();
+ s_coreManager->save();
+}
+
+
+bool KNMainWidget::queryClose()
+{
+ if(b_lockui)
+ return false;
+
+ if(!requestShutdown())
+ return false;
+
+ prepareShutdown();
+
+ return true;
+}
+
+
+void KNMainWidget::showEvent(QShowEvent *)
+{
+ slotCheckDockWidgetStatus();
+}
+
+
+void KNMainWidget::fontChange( const QFont & )
+{
+ a_rtFactory->configChanged();
+ ArticleWidget::configChanged();
+ configChanged();
+}
+
+
+void KNMainWidget::paletteChange( const QPalette & )
+{
+ ArticleWidget::configChanged();
+ configChanged();
+}
+
+
+bool KNMainWidget::eventFilter(QObject *o, QEvent *e)
+{
+ if (((e->type() == QEvent::KeyPress) ||
+ (e->type() == QEvent::KeyRelease) ||
+ (e->type() == QEvent::Accel) ||
+ (e->type() == QEvent::AccelOverride)) &&
+ b_lockui)
+ return true;
+ return KDockArea::eventFilter(o, e);
+}
+
+
+void KNMainWidget::getSelectedArticles(KNArticle::List &l)
+{
+ if(!g_rpManager->currentGroup() && !f_olManager->currentFolder())
+ return;
+
+ for(QListViewItem *i=h_drView->firstChild(); i; i=i->itemBelow())
+ if(i->isSelected() || (static_cast<KNHdrViewItem*>(i)->isActive()))
+ l.append( static_cast<KNArticle*> ((static_cast<KNHdrViewItem*>(i))->art) );
+}
+
+
+void KNMainWidget::getSelectedArticles(KNRemoteArticle::List &l)
+{
+ if(!g_rpManager->currentGroup()) return;
+
+ for(QListViewItem *i=h_drView->firstChild(); i; i=i->itemBelow())
+ if(i->isSelected() || (static_cast<KNHdrViewItem*>(i)->isActive()))
+ l.append( static_cast<KNRemoteArticle*> ((static_cast<KNHdrViewItem*>(i))->art) );
+}
+
+
+void KNMainWidget::getSelectedThreads(KNRemoteArticle::List &l)
+{
+ KNRemoteArticle *art;
+ for(QListViewItem *i=h_drView->firstChild(); i; i=i->itemBelow())
+ if(i->isSelected() || (static_cast<KNHdrViewItem*>(i)->isActive())) {
+ art=static_cast<KNRemoteArticle*> ((static_cast<KNHdrViewItem*>(i))->art);
+ // ignore the article if it is already in the list
+ // (multiple aritcles are selected in one thread)
+ if ( l.find(art) == l.end() )
+ art->thread(l);
+ }
+}
+
+
+void KNMainWidget::getSelectedArticles( KNLocalArticle::List &l )
+{
+ if(!f_olManager->currentFolder()) return;
+
+ for(QListViewItem *i=h_drView->firstChild(); i; i=i->itemBelow())
+ if(i->isSelected() || (static_cast<KNHdrViewItem*>(i)->isActive()))
+ l.append( static_cast<KNLocalArticle*> ((static_cast<KNHdrViewItem*>(i))->art) );
+}
+
+
+void KNMainWidget::closeCurrentThread()
+{
+ QListViewItem *item = h_drView->currentItem();
+ if (item) {
+ while (item->parent())
+ item = item->parent();
+ h_drView->setCurrentItem(item);
+ item->setOpen(false);
+ h_drView->ensureItemVisible(item);
+ }
+}
+
+void KNMainWidget::slotArticleSelected(QListViewItem *i)
+{
+ kdDebug(5003) << "KNMainWidget::slotArticleSelected(QListViewItem *i)" << endl;
+ if(b_lockui)
+ return;
+ KNArticle *selectedArticle=0;
+
+ if(i)
+ selectedArticle=(static_cast<KNHdrViewItem*>(i))->art;
+
+ mArticleViewer->setArticle( selectedArticle );
+
+ //actions
+ bool enabled;
+
+ enabled=( selectedArticle && selectedArticle->type()==KMime::Base::ATremote );
+ if(a_ctArtSetArtRead->isEnabled() != enabled) {
+ a_ctArtSetArtRead->setEnabled(enabled);
+ a_ctArtSetArtUnread->setEnabled(enabled);
+ a_ctArtSetThreadRead->setEnabled(enabled);
+ a_ctArtSetThreadUnread->setEnabled(enabled);
+ a_ctArtToggleIgnored->setEnabled(enabled);
+ a_ctArtToggleWatched->setEnabled(enabled);
+ a_ctScoreLower->setEnabled(enabled);
+ a_ctScoreRaise->setEnabled(enabled);
+ }
+
+ a_ctArtOpenNewWindow->setEnabled( selectedArticle && (f_olManager->currentFolder()!=f_olManager->outbox())
+ && (f_olManager->currentFolder()!=f_olManager->drafts()));
+
+ enabled=( selectedArticle && selectedArticle->type()==KMime::Base::ATlocal );
+ a_ctArtDelete->setEnabled(enabled);
+ a_ctArtSendNow->setEnabled(enabled && (f_olManager->currentFolder()==f_olManager->outbox()));
+ a_ctArtEdit->setEnabled(enabled && ((f_olManager->currentFolder()==f_olManager->outbox())||
+ (f_olManager->currentFolder()==f_olManager->drafts())));
+}
+
+
+void KNMainWidget::slotArticleSelectionChanged()
+{
+ // enable all actions that work with multiple selection
+
+ //actions
+ bool enabled = (g_rpManager->currentGroup()!=0);
+
+ if(a_ctArtSetArtRead->isEnabled() != enabled) {
+ a_ctArtSetArtRead->setEnabled(enabled);
+ a_ctArtSetArtUnread->setEnabled(enabled);
+ a_ctArtSetThreadRead->setEnabled(enabled);
+ a_ctArtSetThreadUnread->setEnabled(enabled);
+ a_ctArtToggleIgnored->setEnabled(enabled);
+ a_ctArtToggleWatched->setEnabled(enabled);
+ a_ctScoreLower->setEnabled(enabled);
+ a_ctScoreRaise->setEnabled(enabled);
+ }
+
+ enabled = (f_olManager->currentFolder()!=0);
+ a_ctArtDelete->setEnabled(enabled);
+ a_ctArtSendNow->setEnabled(enabled && (f_olManager->currentFolder()==f_olManager->outbox()));
+}
+
+
+void KNMainWidget::slotCollectionSelected(QListViewItem *i)
+{
+ kdDebug(5003) << "KNMainWidget::slotCollectionSelected(QListViewItem *i)" << endl;
+ if(b_lockui)
+ return;
+ KNCollection *c=0;
+ KNNntpAccount *selectedAccount=0;
+ KNGroup *selectedGroup=0;
+ KNFolder *selectedFolder=0;
+
+ s_earchLineEdit->clear();
+ h_drView->clear();
+ slotArticleSelected(0);
+
+ // mark all articles in current group as not new/read
+ if ( knGlobals.configManager()->readNewsNavigation()->leaveGroupMarkAsRead() )
+ a_rtManager->setAllRead( true );
+ a_rtManager->setAllNotNew();
+
+ if(i) {
+ c=(static_cast<KNCollectionViewItem*>(i))->coll;
+ switch(c->type()) {
+ case KNCollection::CTnntpAccount :
+ selectedAccount=static_cast<KNNntpAccount*>(c);
+ if(!i->isOpen())
+ i->setOpen(true);
+ break;
+ case KNCollection::CTgroup :
+ if ( !h_drView->hasFocus() && !mArticleViewer->hasFocus() )
+ h_drView->setFocus();
+ selectedGroup=static_cast<KNGroup*>(c);
+ selectedAccount=selectedGroup->account();
+ break;
+
+ case KNCollection::CTfolder :
+ if ( !h_drView->hasFocus() && !mArticleViewer->hasFocus() )
+ h_drView->setFocus();
+ selectedFolder=static_cast<KNFolder*>(c);
+ break;
+
+ default: break;
+ }
+ }
+
+ a_ccManager->setCurrentAccount(selectedAccount);
+ g_rpManager->setCurrentGroup(selectedGroup);
+ f_olManager->setCurrentFolder(selectedFolder);
+ if (!selectedGroup && !selectedFolder) // called from showHeaders() otherwise
+ a_rtManager->updateStatusString();
+
+ updateCaption();
+
+ //actions
+ bool enabled;
+
+ enabled=(selectedGroup) || (selectedFolder && !selectedFolder->isRootFolder());
+ if(a_ctNavNextArt->isEnabled() != enabled) {
+ a_ctNavNextArt->setEnabled(enabled);
+ a_ctNavPrevArt->setEnabled(enabled);
+ }
+
+ enabled=( selectedGroup!=0 );
+ if(a_ctNavNextUnreadArt->isEnabled() != enabled) {
+ a_ctNavNextUnreadArt->setEnabled(enabled);
+ a_ctNavNextUnreadThread->setEnabled(enabled);
+ a_ctNavReadThrough->setEnabled(enabled);
+ a_ctFetchArticleWithID->setEnabled(enabled);
+ }
+
+ enabled=( selectedAccount!=0 );
+ if(a_ctAccProperties->isEnabled() != enabled) {
+ a_ctAccProperties->setEnabled(enabled);
+ a_ctAccRename->setEnabled(enabled);
+ a_ctAccSubscribe->setEnabled(enabled);
+ a_ctAccExpireAll->setEnabled(enabled);
+ a_ctAccGetNewHdrs->setEnabled(enabled);
+ a_ctAccGetNewHdrsAll->setEnabled(enabled);
+ a_ctAccDelete->setEnabled(enabled);
+ a_ctAccPostNewArticle->setEnabled(enabled);
+ }
+
+ enabled=( selectedGroup!=0 );
+ if(a_ctGrpProperties->isEnabled() != enabled) {
+ a_ctGrpProperties->setEnabled(enabled);
+ a_ctGrpRename->setEnabled(enabled);
+ a_ctGrpGetNewHdrs->setEnabled(enabled);
+ a_ctGrpExpire->setEnabled(enabled);
+ a_ctGrpReorganize->setEnabled(enabled);
+ a_ctGrpUnsubscribe->setEnabled(enabled);
+ a_ctGrpSetAllRead->setEnabled(enabled);
+ a_ctGrpSetAllUnread->setEnabled(enabled);
+ a_ctGrpSetUnread->setEnabled(enabled);
+ a_ctArtFilter->setEnabled(enabled);
+ a_ctArtFilterKeyb->setEnabled(enabled);
+ a_ctArtRefreshList->setEnabled(enabled);
+ a_ctArtCollapseAll->setEnabled(enabled);
+ a_ctArtExpandAll->setEnabled(enabled);
+ a_ctArtToggleShowThreads->setEnabled(enabled);
+ a_ctReScore->setEnabled(enabled);
+ }
+
+ a_ctFolNewChild->setEnabled(selectedFolder!=0);
+
+ enabled=( selectedFolder!=0 && !selectedFolder->isRootFolder() && !selectedFolder->isStandardFolder() );
+ if(a_ctFolDelete->isEnabled() != enabled) {
+ a_ctFolDelete->setEnabled(enabled);
+ a_ctFolRename->setEnabled(enabled);
+ }
+
+ enabled=( selectedFolder!=0 && !selectedFolder->isRootFolder() );
+ if(a_ctFolCompact->isEnabled() != enabled) {
+ a_ctFolCompact->setEnabled(enabled);
+ a_ctFolEmpty->setEnabled(enabled);
+ a_ctFolMboxImport->setEnabled(enabled);
+ a_ctFolMboxExport->setEnabled(enabled);
+ }
+}
+
+
+void KNMainWidget::slotCollectionRenamed(QListViewItem *i)
+{
+ kdDebug(5003) << "KNMainWidget::slotCollectionRenamed(QListViewItem *i)" << endl;
+
+ if (i) {
+ (static_cast<KNCollectionViewItem*>(i))->coll->setName(i->text(0));
+ updateCaption();
+ a_rtManager->updateStatusString();
+ if ((static_cast<KNCollectionViewItem*>(i))->coll->type()==KNCollection::CTnntpAccount)
+ a_ccManager->accountRenamed(static_cast<KNNntpAccount*>((static_cast<KNCollectionViewItem*>(i))->coll));
+ disableAccels(false);
+ }
+}
+
+
+void KNMainWidget::slotCollectionViewDrop(QDropEvent* e, KNCollectionViewItem* after)
+{
+ kdDebug(5003) << "KNMainWidget::slotCollectionViewDrop() : type = " << e->format(0) << endl;
+
+ KNCollectionViewItem *cvi=static_cast<KNCollectionViewItem*>(after);
+ if (cvi && cvi->coll->type() != KNCollection::CTfolder) // safety measure...
+ return;
+ KNFolder *dest=cvi ? static_cast<KNFolder*>(cvi->coll) : 0;
+
+ if (e->provides("x-knode-drag/folder") && f_olManager->currentFolder()) {
+ f_olManager->moveFolder(f_olManager->currentFolder(), dest);
+ }
+ else if(dest && e->provides("x-knode-drag/article")) {
+ if(f_olManager->currentFolder()) {
+ if (e->action() == QDropEvent::Move) {
+ KNLocalArticle::List l;
+ getSelectedArticles(l);
+ a_rtManager->moveIntoFolder(l, dest);
+ } else {
+ KNArticle::List l;
+ getSelectedArticles(l);
+ a_rtManager->copyIntoFolder(l, dest);
+ }
+ }
+ else if(g_rpManager->currentGroup()) {
+ KNArticle::List l;
+ getSelectedArticles(l);
+ a_rtManager->copyIntoFolder(l, dest);
+ }
+ }
+}
+
+
+void KNMainWidget::slotArticleRMB(KListView*, QListViewItem *i, const QPoint &p)
+{
+ if(b_lockui)
+ return;
+
+ if(i) {
+ QPopupMenu *popup;
+ if( (static_cast<KNHdrViewItem*>(i))->art->type()==KMime::Base::ATremote) {
+ popup = static_cast<QPopupMenu *>(factory()->container("remote_popup", m_GUIClient));
+ } else {
+ popup = static_cast<QPopupMenu *>(factory()->container("local_popup", m_GUIClient));
+ }
+
+ if ( popup )
+ popup->popup(p);
+ }
+}
+
+
+void KNMainWidget::slotCollectionRMB(KListView*, QListViewItem *i, const QPoint &p)
+{
+ if(b_lockui)
+ return;
+
+ if(i) {
+ if( (static_cast<KNCollectionViewItem*>(i))->coll->type()==KNCollection::CTgroup) {
+ QPopupMenu *popup = static_cast<QPopupMenu *>(factory()->container("group_popup", m_GUIClient));
+ if ( popup )
+ popup->popup(p);
+ } else if ((static_cast<KNCollectionViewItem*>(i))->coll->type()==KNCollection::CTfolder) {
+ if (static_cast<KNFolder*>(static_cast<KNCollectionViewItem*>(i)->coll)->isRootFolder()) {
+ QPopupMenu *popup = static_cast<QPopupMenu *>(factory()->container("root_folder_popup", m_GUIClient));
+ if ( popup )
+ popup->popup(p);
+ } else {
+ QPopupMenu *popup = static_cast<QPopupMenu *>(factory()->container("folder_popup", m_GUIClient));
+ if ( popup )
+ popup->popup(p);
+ }
+ } else {
+ QPopupMenu *popup = static_cast<QPopupMenu *>(factory()->container("account_popup", m_GUIClient));
+ if ( popup )
+ popup->popup( p );
+ }
+ }
+}
+
+
+void KNMainWidget::slotOpenArticle(QListViewItem *item)
+{
+ if(b_lockui)
+ return;
+
+ if (item) {
+ KNArticle *art=(static_cast<KNHdrViewItem*>(item))->art;
+
+ if ((art->type()==KMime::Base::ATlocal) && ((f_olManager->currentFolder()==f_olManager->outbox())||
+ (f_olManager->currentFolder()==f_olManager->drafts()))) {
+ a_rtFactory->edit( static_cast<KNLocalArticle*>(art) );
+ } else {
+ if (!KNArticleWindow::raiseWindowForArticle(art)) {
+ KNArticleWindow *w=new KNArticleWindow(art);
+ w->show();
+ }
+ }
+ }
+}
+
+
+void KNMainWidget::slotHdrViewSortingChanged(int i)
+{
+ a_ctArtSortHeaders->setCurrentItem(i);
+}
+
+
+void KNMainWidget::slotNetworkActive(bool b)
+{
+ a_ctNetCancel->setEnabled(b);
+}
+
+
+void KNMainWidget::slotCheckDockWidgetStatus()
+{
+ a_ctToggleGroupView->setChecked(c_olDock->isVisible());
+ a_ctToggleArticleViewer->setChecked(a_rtDock->isVisible());
+ a_ctToggleHeaderView->setChecked(h_drDock->isVisible());
+}
+
+
+void KNMainWidget::slotGroupDockHidden()
+{
+ a_ctToggleGroupView->setChecked(false);
+}
+
+
+void KNMainWidget::slotHeaderDockHidden()
+{
+ a_ctToggleHeaderView->setChecked(false);
+}
+
+
+void KNMainWidget::slotArticleDockHidden()
+{
+ a_ctToggleArticleViewer->setChecked(false);
+}
+
+
+void KNMainWidget::slotDockWidgetFocusChangeRequest(QWidget *w)
+{
+ if ( w == mArticleViewer ) {
+ if (c_olView->isVisible()) {
+ c_olView->setFocus();
+ if (!w->hasFocus()) // fails if the view is visible but floating
+ return;
+ }
+ if (h_drView->isVisible()) {
+ h_drView->setFocus();
+ return;
+ }
+ }
+ if (w == c_olView) {
+ if (h_drView->isVisible()) {
+ h_drView->setFocus();
+ if (!w->hasFocus()) // fails if the view is visible but floating
+ return;
+ }
+ if ( mArticleViewer->isVisible() ) {
+ mArticleViewer->setFocus();
+ return;
+ }
+ }
+ if (w == h_drView) {
+ if ( mArticleViewer->isVisible() ) {
+ mArticleViewer->setFocus();
+ if (!w->hasFocus()) // fails if the view is visible but floating
+ return;
+ }
+ if (c_olView->isVisible()) {
+ c_olView->setFocus();
+ return;
+ }
+ }
+}
+
+
+//------------------------------ <Actions> --------------------------------
+
+
+void KNMainWidget::slotNavNextUnreadArt()
+{
+ if ( !h_drView->nextUnreadArticle() )
+ c_olView->nextGroup();
+}
+
+
+void KNMainWidget::slotNavNextUnreadThread()
+{
+ if ( !h_drView->nextUnreadThread() )
+ c_olView->nextGroup();
+}
+
+
+void KNMainWidget::slotNavReadThrough()
+{
+ kdDebug(5003) << "KNMainWidget::slotNavReadThrough()" << endl;
+ if ( !mArticleViewer->atBottom() )
+ mArticleViewer->scrollNext();
+ else if(g_rpManager->currentGroup() != 0)
+ slotNavNextUnreadArt();
+}
+
+
+void KNMainWidget::slotAccProperties()
+{
+ kdDebug(5003) << "KNMainWidget::slotAccProperties()" << endl;
+ if(a_ccManager->currentAccount())
+ a_ccManager->editProperties(a_ccManager->currentAccount());
+ updateCaption();
+ a_rtManager->updateStatusString();
+}
+
+
+void KNMainWidget::slotAccRename()
+{
+ kdDebug(5003) << "KNMainWidget::slotAccRename()" << endl;
+ if(a_ccManager->currentAccount()) {
+ disableAccels(true); // hack: global accels break the inplace renaming
+ c_olView->rename(a_ccManager->currentAccount()->listItem(), 0);
+ }
+}
+
+
+void KNMainWidget::slotAccSubscribe()
+{
+ kdDebug(5003) << "KNMainWidget::slotAccSubscribe()" << endl;
+ if(a_ccManager->currentAccount())
+ g_rpManager->showGroupDialog(a_ccManager->currentAccount());
+}
+
+
+void KNMainWidget::slotAccExpireAll()
+{
+ kdDebug(5003) << "KNMainWidget::slotAccExpireAll()" << endl;
+ if(a_ccManager->currentAccount())
+ g_rpManager->expireAll(a_ccManager->currentAccount());
+}
+
+
+void KNMainWidget::slotAccGetNewHdrs()
+{
+ kdDebug(5003) << "KNMainWidget::slotAccGetNewHdrs()" << endl;
+ if(a_ccManager->currentAccount())
+ g_rpManager->checkAll(a_ccManager->currentAccount());
+}
+
+
+
+void KNMainWidget::slotAccDelete()
+{
+ kdDebug(5003) << "KNMainWidget::slotAccDelete()" << endl;
+ if(a_ccManager->currentAccount()) {
+ if (a_ccManager->removeAccount(a_ccManager->currentAccount()))
+ slotCollectionSelected(0);
+ }
+}
+
+void KNMainWidget::slotAccGetNewHdrsAll()
+{
+ QValueList<KNNntpAccount*>::Iterator it;
+ for ( it = a_ccManager->begin(); it != a_ccManager->end(); ++it )
+ g_rpManager->checkAll( *it );
+}
+
+void KNMainWidget::slotAccPostNewArticle()
+{
+ kdDebug(5003) << "KNMainWidget::slotAccPostNewArticle()" << endl;
+ if(g_rpManager->currentGroup())
+ a_rtFactory->createPosting(g_rpManager->currentGroup());
+ else if(a_ccManager->currentAccount())
+ a_rtFactory->createPosting(a_ccManager->currentAccount());
+}
+
+
+void KNMainWidget::slotGrpProperties()
+{
+ kdDebug(5003) << "slotGrpProperties()" << endl;
+ if(g_rpManager->currentGroup())
+ g_rpManager->showGroupProperties(g_rpManager->currentGroup());
+ updateCaption();
+ a_rtManager->updateStatusString();
+}
+
+
+void KNMainWidget::slotGrpRename()
+{
+ kdDebug(5003) << "slotGrpRename()" << endl;
+ if(g_rpManager->currentGroup()) {
+ disableAccels(true); // hack: global accels break the inplace renaming
+ c_olView->rename(g_rpManager->currentGroup()->listItem(), 0);
+ }
+}
+
+
+void KNMainWidget::slotGrpGetNewHdrs()
+{
+ kdDebug(5003) << "KNMainWidget::slotGrpGetNewHdrs()" << endl;
+ if(g_rpManager->currentGroup())
+ g_rpManager->checkGroupForNewHeaders(g_rpManager->currentGroup());
+}
+
+
+void KNMainWidget::slotGrpExpire()
+{
+ kdDebug(5003) << "KNMainWidget::slotGrpExpire()" << endl;
+ if(g_rpManager->currentGroup())
+ g_rpManager->expireGroupNow(g_rpManager->currentGroup());
+}
+
+
+void KNMainWidget::slotGrpReorganize()
+{
+ kdDebug(5003) << "KNMainWidget::slotGrpReorganize()" << endl;
+ g_rpManager->reorganizeGroup(g_rpManager->currentGroup());
+}
+
+
+void KNMainWidget::slotGrpUnsubscribe()
+{
+ kdDebug(5003) << "KNMainWidget::slotGrpUnsubscribe()" << endl;
+ if(g_rpManager->currentGroup()) {
+ if(KMessageBox::Yes==KMessageBox::questionYesNo(knGlobals.topWidget,
+ i18n("Do you really want to unsubscribe from %1?").arg(g_rpManager->currentGroup()->groupname()), QString::null, i18n("Unsubscribe"), KStdGuiItem::cancel()))
+ if (g_rpManager->unsubscribeGroup(g_rpManager->currentGroup()))
+ slotCollectionSelected(0);
+ }
+}
+
+
+void KNMainWidget::slotGrpSetAllRead()
+{
+ kdDebug(5003) << "KNMainWidget::slotGrpSetAllRead()" << endl;
+
+ a_rtManager->setAllRead(true);
+ if (c_fgManager->readNewsNavigation()->markAllReadGoNext())
+ c_olView->nextGroup();
+}
+
+
+void KNMainWidget::slotGrpSetAllUnread()
+{
+ kdDebug(5003) << "KNMainWidget::slotGrpSetAllUnread()" << endl;
+ a_rtManager->setAllRead(false);
+}
+
+void KNMainWidget::slotGrpSetUnread()
+{
+ kdDebug(5003) << "KNMainWidget::slotGrpSetUnread()" << endl;
+ int groupLength = g_rpManager->currentGroup()->length();
+
+ bool ok = false;
+ int res = KInputDialog::getInteger(
+ i18n( "Mark Last as Unread" ),
+ i18n( "Enter how many articles should be marked unread:" ), groupLength, 1, groupLength, 1, &ok, this );
+ if ( ok )
+ a_rtManager->setAllRead( false, res );
+}
+
+void KNMainWidget::slotFolNew()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolNew()" << endl;
+ KNFolder *f = f_olManager->newFolder(0);
+
+ if (f) {
+ f_olManager->setCurrentFolder(f);
+ c_olView->ensureItemVisible(f->listItem());
+ c_olView->setActive( f->listItem() );
+ slotFolRename();
+ }
+}
+
+
+void KNMainWidget::slotFolNewChild()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolNew()" << endl;
+ if(f_olManager->currentFolder()) {
+ KNFolder *f = f_olManager->newFolder(f_olManager->currentFolder());
+
+ if (f) {
+ f_olManager->setCurrentFolder(f);
+ c_olView->ensureItemVisible(f->listItem());
+ c_olView->setActive( f->listItem() );
+ slotFolRename();
+ }
+ }
+}
+
+
+void KNMainWidget::slotFolDelete()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolDelete()" << endl;
+
+ if(!f_olManager->currentFolder() || f_olManager->currentFolder()->isRootFolder())
+ return;
+
+ if(f_olManager->currentFolder()->isStandardFolder())
+ KMessageBox::sorry(knGlobals.topWidget, i18n("You cannot delete a standard folder."));
+
+ else if( KMessageBox::Continue==KMessageBox::warningContinueCancel(knGlobals.topWidget,
+ i18n("Do you really want to delete this folder and all its children?"),"",KGuiItem(i18n("&Delete"),"editdelete")) ) {
+
+ if(!f_olManager->deleteFolder(f_olManager->currentFolder()))
+ KMessageBox::sorry(knGlobals.topWidget,
+ i18n("This folder cannot be deleted because some of\n its articles are currently in use.") );
+ else
+ slotCollectionSelected(0);
+ }
+}
+
+
+void KNMainWidget::slotFolRename()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolRename()" << endl;
+
+ if(f_olManager->currentFolder() && !f_olManager->currentFolder()->isRootFolder()) {
+ if(f_olManager->currentFolder()->isStandardFolder())
+ KMessageBox::sorry(knGlobals.topWidget, i18n("You cannot rename a standard folder."));
+ else {
+ disableAccels(true); // hack: global accels break the inplace renaming
+ c_olView->rename(f_olManager->currentFolder()->listItem(), 0);
+ }
+ }
+}
+
+
+void KNMainWidget::slotFolCompact()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolCompact()" << endl;
+ if(f_olManager->currentFolder() && !f_olManager->currentFolder()->isRootFolder())
+ f_olManager->compactFolder(f_olManager->currentFolder());
+}
+
+
+void KNMainWidget::slotFolCompactAll()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolCompactAll()" << endl;
+ f_olManager->compactAll();
+}
+
+
+void KNMainWidget::slotFolEmpty()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolEmpty()" << endl;
+ if(f_olManager->currentFolder() && !f_olManager->currentFolder()->isRootFolder()) {
+ if(f_olManager->currentFolder()->lockedArticles()>0) {
+ KMessageBox::sorry(this,
+ i18n("This folder cannot be emptied at the moment\nbecause some of its articles are currently in use.") );
+ return;
+ }
+ if( KMessageBox::Continue == KMessageBox::warningContinueCancel(
+ this, i18n("Do you really want to delete all articles in %1?").arg(f_olManager->currentFolder()->name()),"",KGuiItem(i18n("&Delete"),"editdelete")) )
+ f_olManager->emptyFolder(f_olManager->currentFolder());
+ }
+}
+
+
+void KNMainWidget::slotFolMBoxImport()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolMBoxImport()" << endl;
+ if(f_olManager->currentFolder() && !f_olManager->currentFolder()->isRootFolder()) {
+ f_olManager->importFromMBox(f_olManager->currentFolder());
+ }
+}
+
+
+void KNMainWidget::slotFolMBoxExport()
+{
+ kdDebug(5003) << "KNMainWidget::slotFolMBoxExport()" << endl;
+ if(f_olManager->currentFolder() && !f_olManager->currentFolder()->isRootFolder()) {
+ f_olManager->exportToMBox(f_olManager->currentFolder());
+ }
+}
+
+
+void KNMainWidget::slotArtSortHeaders(int i)
+{
+ kdDebug(5003) << "KNMainWidget::slotArtSortHeaders(int i)" << endl;
+ h_drView->setSorting( i );
+}
+
+
+void KNMainWidget::slotArtSortHeadersKeyb()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtSortHeadersKeyb()" << endl;
+
+ int newCol = KNHelper::selectDialog(this, i18n("Select Sort Column"), a_ctArtSortHeaders->items(), a_ctArtSortHeaders->currentItem());
+ if (newCol != -1)
+ h_drView->setSorting( newCol );
+}
+
+
+void KNMainWidget::slotArtSearch()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtSearch()" << endl;
+ a_rtManager->search();
+}
+
+
+void KNMainWidget::slotArtRefreshList()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtRefreshList()" << endl;
+ a_rtManager->showHdrs(true);
+}
+
+
+void KNMainWidget::slotArtCollapseAll()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtCollapseAll()" << endl;
+
+ closeCurrentThread();
+ a_rtManager->setAllThreadsOpen(false);
+ if (h_drView->currentItem())
+ h_drView->ensureItemVisible(h_drView->currentItem());
+}
+
+
+void KNMainWidget::slotArtExpandAll()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtExpandAll()" << endl;
+
+ a_rtManager->setAllThreadsOpen(true);
+ if (h_drView->currentItem())
+ h_drView->ensureItemVisible(h_drView->currentItem());
+}
+
+
+void KNMainWidget::slotArtToggleThread()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtToggleThread()" << endl;
+ if( mArticleViewer->article() && mArticleViewer->article()->listItem()->isExpandable() ) {
+ bool o = !(mArticleViewer->article()->listItem()->isOpen());
+ mArticleViewer->article()->listItem()->setOpen( o );
+ }
+}
+
+
+void KNMainWidget::slotArtToggleShowThreads()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtToggleShowThreads()" << endl;
+ if(g_rpManager->currentGroup()) {
+ c_fgManager->readNewsGeneral()->setShowThreads(!c_fgManager->readNewsGeneral()->showThreads());
+ a_rtManager->showHdrs(true);
+ }
+}
+
+
+void KNMainWidget::slotArtSetArtRead()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtSetArtRead()" << endl;
+ if(!g_rpManager->currentGroup())
+ return;
+
+ KNRemoteArticle::List l;
+ getSelectedArticles(l);
+ a_rtManager->setRead(l, true);
+}
+
+
+void KNMainWidget::slotArtSetArtUnread()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtSetArtUnread()" << endl;
+ if(!g_rpManager->currentGroup())
+ return;
+
+ KNRemoteArticle::List l;
+ getSelectedArticles(l);
+ a_rtManager->setRead(l, false);
+}
+
+
+void KNMainWidget::slotArtSetThreadRead()
+{
+ kdDebug(5003) << "slotArtSetThreadRead()" << endl;
+ if( !g_rpManager->currentGroup() )
+ return;
+
+ KNRemoteArticle::List l;
+ getSelectedThreads(l);
+ a_rtManager->setRead(l, true);
+
+ if (h_drView->currentItem()) {
+ if (c_fgManager->readNewsNavigation()->markThreadReadCloseThread())
+ closeCurrentThread();
+ if (c_fgManager->readNewsNavigation()->markThreadReadGoNext())
+ slotNavNextUnreadThread();
+ }
+}
+
+
+void KNMainWidget::slotArtSetThreadUnread()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtSetThreadUnread()" << endl;
+ if( !g_rpManager->currentGroup() )
+ return;
+
+ KNRemoteArticle::List l;
+ getSelectedThreads(l);
+ a_rtManager->setRead(l, false);
+}
+
+
+void KNMainWidget::slotScoreEdit()
+{
+ kdDebug(5003) << "KNMainWidget::slotScoreEdit()" << endl;
+ s_coreManager->configure();
+}
+
+
+void KNMainWidget::slotReScore()
+{
+ kdDebug(5003) << "KNMainWidget::slotReScore()" << endl;
+ if( !g_rpManager->currentGroup() )
+ return;
+
+ g_rpManager->currentGroup()->scoreArticles(false);
+ a_rtManager->showHdrs(true);
+}
+
+
+void KNMainWidget::slotScoreLower()
+{
+ kdDebug(5003) << "KNMainWidget::slotScoreLower() start" << endl;
+ if( !g_rpManager->currentGroup() )
+ return;
+
+ if ( mArticleViewer->article() && mArticleViewer->article()->type() == KMime::Base::ATremote ) {
+ KNRemoteArticle *ra = static_cast<KNRemoteArticle*>( mArticleViewer->article() );
+ s_coreManager->addRule(KNScorableArticle(ra), g_rpManager->currentGroup()->groupname(), -10);
+ }
+}
+
+
+void KNMainWidget::slotScoreRaise()
+{
+ kdDebug(5003) << "KNMainWidget::slotScoreRaise() start" << endl;
+ if( !g_rpManager->currentGroup() )
+ return;
+
+ if ( mArticleViewer->article() && mArticleViewer->article()->type() == KMime::Base::ATremote ) {
+ KNRemoteArticle *ra = static_cast<KNRemoteArticle*>( mArticleViewer->article() );
+ s_coreManager->addRule(KNScorableArticle(ra), g_rpManager->currentGroup()->groupname(), +10);
+ }
+}
+
+
+void KNMainWidget::slotArtToggleIgnored()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtToggleIgnored()" << endl;
+ if( !g_rpManager->currentGroup() )
+ return;
+
+ KNRemoteArticle::List l;
+ getSelectedThreads(l);
+ bool revert = !a_rtManager->toggleIgnored(l);
+ a_rtManager->rescoreArticles(l);
+
+ if (h_drView->currentItem() && !revert) {
+ if (c_fgManager->readNewsNavigation()->ignoreThreadCloseThread())
+ closeCurrentThread();
+ if (c_fgManager->readNewsNavigation()->ignoreThreadGoNext())
+ slotNavNextUnreadThread();
+ }
+}
+
+
+void KNMainWidget::slotArtToggleWatched()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtToggleWatched()" << endl;
+ if( !g_rpManager->currentGroup() )
+ return;
+
+ KNRemoteArticle::List l;
+ getSelectedThreads(l);
+ a_rtManager->toggleWatched(l);
+ a_rtManager->rescoreArticles(l);
+}
+
+
+void KNMainWidget::slotArtOpenNewWindow()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtOpenNewWindow()" << endl;
+
+ if( mArticleViewer->article() ) {
+ if ( !KNArticleWindow::raiseWindowForArticle( mArticleViewer->article() )) {
+ KNArticleWindow *win=new KNArticleWindow( mArticleViewer->article() );
+ win->show();
+ }
+ }
+}
+
+
+void KNMainWidget::slotArtSendOutbox()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtSendOutbox()" << endl;
+ a_rtFactory->sendOutbox();
+}
+
+
+void KNMainWidget::slotArtDelete()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtDelete()" << endl;
+ if (!f_olManager->currentFolder())
+ return;
+
+ KNLocalArticle::List lst;
+ getSelectedArticles(lst);
+
+ if(!lst.isEmpty())
+ a_rtManager->deleteArticles(lst);
+
+ if(h_drView->currentItem())
+ h_drView->setActive( h_drView->currentItem() );
+}
+
+
+void KNMainWidget::slotArtSendNow()
+{
+ kdDebug(5003) << "KNMainWidget::slotArtSendNow()" << endl;
+ if (!f_olManager->currentFolder())
+ return;
+
+ KNLocalArticle::List lst;
+ getSelectedArticles(lst);
+
+ if(!lst.isEmpty())
+ a_rtFactory->sendArticles( lst, true );
+}
+
+
+void KNMainWidget::slotArtEdit()
+{
+ kdDebug(5003) << "KNodeVew::slotArtEdit()" << endl;
+ if (!f_olManager->currentFolder())
+ return;
+
+ if ( mArticleViewer->article() && mArticleViewer->article()->type() == KMime::Base::ATlocal )
+ a_rtFactory->edit( static_cast<KNLocalArticle*>( mArticleViewer->article() ) );
+}
+
+
+void KNMainWidget::slotNetCancel()
+{
+ kdDebug(5003) << "KNMainWidget::slotNetCancel()" << endl;
+ n_etAccess->cancelAllJobs();
+}
+
+
+void KNMainWidget::slotFetchArticleWithID()
+{
+ kdDebug(5003) << "KNMainWidget::slotFetchArticleWithID()" << endl;
+ if( !g_rpManager->currentGroup() )
+ return;
+
+ FetchArticleIdDlg *dlg = new FetchArticleIdDlg(this, "messageid" );
+
+ if (dlg->exec()) {
+ QString id = dlg->messageId().simplifyWhiteSpace();
+ if (id.find(QRegExp("*@*",false,true))!=-1) {
+ if (id.find(QRegExp("<*>",false,true))==-1) // add "<>" when necessary
+ id = QString("<%1>").arg(id);
+
+ if(!KNArticleWindow::raiseWindowForArticle(id.latin1())) { //article not yet opened
+ KNRemoteArticle *a=new KNRemoteArticle(g_rpManager->currentGroup());
+ a->messageID()->from7BitString(id.latin1());
+ KNArticleWindow *awin=new KNArticleWindow(a);
+ awin->show();
+ }
+ }
+ }
+
+ KNHelper::saveWindowSize("fetchArticleWithID",dlg->size());
+ delete dlg;
+}
+
+void KNMainWidget::slotToggleGroupView()
+{
+ c_olDock->changeHideShowState();
+ slotCheckDockWidgetStatus();
+}
+
+
+void KNMainWidget::slotToggleHeaderView()
+{
+
+ if ( !h_drDock->isVisible() )
+ if ( !h_drDock->isDockBackPossible() ) {
+ h_drDock->manualDock( a_rtDock, KDockWidget::DockTop );
+ h_drDock->makeDockVisible();
+ slotCheckDockWidgetStatus();
+ return;
+ }
+
+ h_drDock->changeHideShowState();
+ slotCheckDockWidgetStatus();
+}
+
+
+void KNMainWidget::slotToggleArticleViewer()
+{
+ a_rtDock->changeHideShowState();
+ slotCheckDockWidgetStatus();
+}
+
+void KNMainWidget::slotToggleQuickSearch()
+{
+ if (q_uicksearch->isHidden())
+ q_uicksearch->show();
+ else
+ q_uicksearch->hide();
+}
+
+void KNMainWidget::slotSwitchToGroupView()
+{
+ if (!c_olView->isVisible())
+ slotToggleGroupView();
+ c_olView->setFocus();
+}
+
+
+void KNMainWidget::slotSwitchToHeaderView()
+{
+ if (!h_drView->isVisible())
+ slotToggleHeaderView();
+ h_drView->setFocus();
+}
+
+void KNMainWidget::slotSwitchToArticleViewer()
+{
+ if ( !mArticleViewer->isVisible() )
+ slotToggleArticleViewer();
+ mArticleViewer->setFocus();
+}
+
+
+void KNMainWidget::slotSettings()
+{
+ c_fgManager->configure();
+}
+
+KActionCollection* KNMainWidget::actionCollection() const
+{
+ return m_GUIClient->actionCollection();
+}
+
+KXMLGUIFactory* KNMainWidget::factory() const
+{
+ kdDebug(5003)<<"m_guiclient is "<< m_GUIClient
+ <<", the factory is " << m_GUIClient->factory() <<endl;
+ return m_GUIClient->factory();
+}
+
+//--------------------------------
+
+
+FetchArticleIdDlg::FetchArticleIdDlg(QWidget *parent, const char */*name*/ )
+ :KDialogBase(parent, 0, true, i18n("Fetch Article with ID"), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok)
+{
+ QHBox *page = makeHBoxMainWidget();
+
+ QLabel *label = new QLabel(i18n("&Message-ID:"),page);
+ edit = new KLineEdit(page);
+ label->setBuddy(edit);
+ edit->setFocus();
+ enableButtonOK( false );
+ setButtonOK( i18n("&Fetch") );
+ connect( edit, SIGNAL(textChanged( const QString & )), this, SLOT(slotTextChanged(const QString & )));
+ KNHelper::restoreWindowSize("fetchArticleWithID", this, QSize(325,66));
+}
+
+QString FetchArticleIdDlg::messageId() const
+{
+ return edit->text();
+}
+
+void FetchArticleIdDlg::slotTextChanged(const QString &_text )
+{
+ enableButtonOK( !_text.isEmpty() );
+}
+
+
+////////////////////////////////////////////////////////////////////////
+//////////////////////// DCOP implementation
+// Move to the next article
+void KNMainWidget::nextArticle()
+{
+ h_drView->nextArticle();
+}
+
+// Move to the previous article
+void KNMainWidget::previousArticle()
+{
+ h_drView->prevArticle();
+}
+
+// Move to the next unread article
+void KNMainWidget::nextUnreadArticle()
+{
+ slotNavNextUnreadArt();
+}
+
+// Move to the next unread thread
+void KNMainWidget::nextUnreadThread()
+{
+ slotNavNextUnreadThread();
+}
+
+// Move to the next group
+void KNMainWidget::nextGroup()
+{
+ c_olView->nextGroup();
+}
+
+// Move to the previous group
+void KNMainWidget::previousGroup()
+{
+ c_olView->prevGroup();
+}
+
+void KNMainWidget::fetchHeaders()
+{
+ // Simply call the slot
+ slotAccGetNewHdrs();
+}
+
+void KNMainWidget::expireArticles()
+{
+ slotAccExpireAll();
+}
+
+// Open the editor to post a new article in the selected group
+void KNMainWidget::postArticle()
+{
+ slotAccPostNewArticle();
+}
+
+// Fetch the new headers in the selected groups
+void KNMainWidget::fetchHeadersInCurrentGroup()
+{
+ slotGrpGetNewHdrs();
+}
+
+// Expire the articles in the current group
+void KNMainWidget::expireArticlesInCurrentGroup()
+{
+ slotGrpExpire();
+}
+
+// Mark all the articles in the current group as read
+void KNMainWidget::markAllAsRead()
+{
+ slotGrpSetAllRead();
+}
+
+// Mark all the articles in the current group as unread
+void KNMainWidget::markAllAsUnread()
+{
+ slotGrpSetAllUnread();
+}
+
+// Mark the current article as read
+void KNMainWidget::markAsRead()
+{
+ slotArtSetArtRead();
+}
+
+// Mark the current article as unread
+void KNMainWidget::markAsUnread()
+{
+ slotArtSetArtUnread();
+}
+
+// Mark the current thread as read
+void KNMainWidget::markThreadAsRead()
+{
+ slotArtSetThreadRead();
+}
+
+// Mark the current thread as unread
+void KNMainWidget::markThreadAsUnread()
+{
+ slotArtSetThreadUnread();
+}
+
+// Send the pending articles
+void KNMainWidget::sendPendingMessages()
+{
+ slotArtSendOutbox();
+}
+
+// Delete the current article
+void KNMainWidget::deleteArticle()
+{
+ slotArtDelete();
+}
+
+// Send the current article
+void KNMainWidget::sendNow()
+{
+ slotArtSendNow();
+}
+
+// Edit the current article
+void KNMainWidget::editArticle()
+{
+ slotArtEdit();
+}
+
+bool KNMainWidget::handleCommandLine()
+{
+ bool doneSomething = false;
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if (args->count()>0) {
+ KURL url=args->url(0); // we take only one URL
+ openURL(url);
+ doneSomething = true;
+ }
+ args->clear();
+ return doneSomething;
+}
+
+//////////////////////// end DCOP implementation
+////////////////////////////////////////////////////////////////////////
+
+#include "knmainwidget.moc"
diff --git a/knode/knmainwidget.h b/knode/knmainwidget.h
new file mode 100644
index 000000000..2b3660bd7
--- /dev/null
+++ b/knode/knmainwidget.h
@@ -0,0 +1,426 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2004-2005 Volker Krause <volker.krause@rwth-aachen.de>
+
+ 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.
+ 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, US
+*/
+#ifndef KNMAINWIDGET_H
+#define KNMAINWIDGET_H
+
+#include "knodeiface.h"
+
+#include <kdockwidget.h>
+#include <kdialogbase.h>
+#include "resource.h"
+
+#include <qglobal.h>
+#include <kdepimmacros.h>
+
+class QListViewItem;
+
+class KURL;
+class KAccel;
+class KAction;
+class KToggleAction;
+class KSelectAction;
+class KRSqueezedTextLabel;
+class KLineEdit;
+class KXMLGUIClient;
+
+class KNHeaderView;
+class KNCollectionView;
+class KNCollectionViewItem;
+class KNProgress;
+class KNConfigManager;
+class KNAccountManager;
+class KNGroupManager;
+class KNFolderManager;
+class KNArticleManager;
+class KNArticleFactory;
+class KNFilterManager;
+class KNScoringManager;
+class KNMemoryManager;
+class KNFilterSelectAction;
+class KNNetAccess;
+namespace Kpgp {
+ class Module;
+}
+namespace KNode {
+ class ArticleWidget;
+}
+class KNArticle;
+class KNLocalArticle;
+class KNRemoteArticle;
+class KActionCollection;
+
+class KDE_EXPORT KNMainWidget : public KDockArea, virtual public KNodeIface
+{
+ Q_OBJECT
+public:
+ KNMainWidget( KXMLGUIClient *client, bool detachable, QWidget* parent, const char* name );
+ ~KNMainWidget();
+
+ /** exit */
+ bool queryClose();
+ void prepareShutdown();
+
+ //GUI
+ void setStatusMsg(const QString& = QString::null, int id=SB_MAIN);
+ void setStatusHelpMsg(const QString& text);
+ void updateCaption();
+ void setCursorBusy(bool b=true);
+ void blockUI(bool b=true);
+ void disableAccels(bool b=true);
+ /** processEvents with some blocking */
+ void secureProcessEvents();
+
+ /** useful default value */
+ virtual QSize sizeHint() const;
+
+ /** handle URL given as command-line argument */
+ void openURL(const KURL &url);
+
+ /** update fonts and colors */
+ void configChanged();
+
+ /** access to GUI-elements */
+ KNCollectionView* collectionView()const { return c_olView; }
+ KNHeaderView* headerView()const { return h_drView; }
+ KNode::ArticleWidget* articleViewer() const { return mArticleViewer; }
+ KRSqueezedTextLabel* statusBarLabelGroup() const { return s_tatusGroup; }
+ KRSqueezedTextLabel* statusBarLabelFilter() const { return s_tatusFilter; }
+ public: //The dcop interface
+ // Implementation of KNodeIface
+ /* Navigation */
+ // Move to the next article
+ virtual void nextArticle();
+ // Move to the previous article
+ virtual void previousArticle();
+ // Move to the next unread article
+ virtual void nextUnreadArticle();
+ // Move to the next unread thread
+ virtual void nextUnreadThread();
+ // Move to the next group
+ virtual void nextGroup();
+ // Move to the previous group
+ virtual void previousGroup();
+
+ /* Group options */
+ // Open the editor to post a new article in the selected group
+ virtual void postArticle();
+ // Fetch the new headers in the selected groups
+ virtual void fetchHeadersInCurrentGroup();
+ // Expire the articles in the current group
+ virtual void expireArticlesInCurrentGroup();
+ // Mark all the articles in the current group as read
+ virtual void markAllAsRead();
+ // Mark all the articles in the current group as unread
+ virtual void markAllAsUnread();
+
+ /* Header view */
+ // Mark the current article as read
+ virtual void markAsRead();
+ // Mark the current article as unread
+ virtual void markAsUnread();
+ // Mark the current thread as read
+ virtual void markThreadAsRead();
+ // Mark the current thread as unread
+ virtual void markThreadAsUnread();
+
+ /* Articles */
+
+ // Send the pending articles
+ virtual void sendPendingMessages();
+ // Delete the current article
+ virtual void deleteArticle();
+ // Send the current article
+ virtual void sendNow();
+ // Edit the current article
+ virtual void editArticle();
+ /// Fetch all the new article headers
+ virtual void fetchHeaders();
+ /// Expire articles in all groups
+ virtual void expireArticles();
+
+ /* Kontact integration */
+ /// Process command-line options
+ virtual bool handleCommandLine();
+
+ //end dcop interface
+signals:
+ void signalCaptionChangeRequest( const QString& );
+
+protected:
+
+ KActionCollection* actionCollection() const;
+ KXMLGUIFactory *factory() const;
+
+ void initActions();
+ void initStatusBar();
+
+ /** checks if run for the first time, sets some global defaults (email configuration) */
+ bool firstStart();
+
+ void readOptions();
+ void saveOptions();
+
+ bool requestShutdown();
+
+ virtual void showEvent(QShowEvent *);
+
+ /** update appearance */
+ virtual void fontChange( const QFont & );
+ virtual void paletteChange ( const QPalette & );
+
+ bool eventFilter(QObject *, QEvent *);
+
+ // convenience methods...
+ void getSelectedArticles( QValueList<KNArticle*> &l );
+ void getSelectedArticles( QValueList<KNRemoteArticle*> &l );
+ void getSelectedThreads( QValueList<KNRemoteArticle*> &l );
+ void getSelectedArticles( QValueList<KNLocalArticle*> &l );
+ void closeCurrentThread();
+
+ //GUI
+ KAccel *a_ccel;
+ KNProgress *p_rogBar;
+ KNode::ArticleWidget *mArticleViewer;
+ KNCollectionView *c_olView;
+ KNHeaderView *h_drView;
+ KDockWidget *c_olDock, *h_drDock, *a_rtDock;
+ bool b_lockui;
+ KToolBar *q_uicksearch;
+ QLineEdit *s_earchLineEdit;
+
+ //Core
+ KNConfigManager *c_fgManager;
+ KNNetAccess *n_etAccess;
+ KNAccountManager *a_ccManager;
+ KNGroupManager *g_rpManager;
+ KNArticleManager *a_rtManager;
+ KNArticleFactory *a_rtFactory;
+ KNFolderManager *f_olManager;
+ KNFilterManager *f_ilManager;
+ KNScoringManager *s_coreManager;
+ KNMemoryManager *m_emManager;
+ Kpgp::Module *p_gp;
+
+protected slots:
+ //listview slots
+ void slotArticleSelected(QListViewItem*);
+ void slotArticleSelectionChanged();
+ void slotCollectionSelected(QListViewItem*);
+ void slotCollectionRenamed(QListViewItem*);
+ void slotCollectionViewDrop(QDropEvent* e, KNCollectionViewItem* after);
+ void slotArticleRMB(KListView*, QListViewItem *i, const QPoint &p);
+ void slotCollectionRMB(KListView*, QListViewItem *i, const QPoint &p);
+ /** Open selected article in own composer/reader window */
+ void slotOpenArticle(QListViewItem *item);
+ void slotHdrViewSortingChanged(int i);
+
+ //network slots
+ void slotNetworkActive(bool b);
+
+ //dock widget slots
+ void slotCheckDockWidgetStatus();
+ void slotGroupDockHidden();
+ void slotHeaderDockHidden();
+ void slotArticleDockHidden();
+ void slotDockWidgetFocusChangeRequest(QWidget *w);
+
+ //---------------------------------- <Actions> ----------------------------------
+
+protected:
+
+ //navigation
+ KAction *a_ctNavNextArt,
+ *a_ctNavPrevArt,
+ *a_ctNavNextUnreadArt,
+ *a_ctNavNextUnreadThread,
+ *a_ctNavNextGroup,
+ *a_ctNavPrevGroup,
+ *a_ctNavReadThrough;
+
+ //collection-view - accounts
+ KAction *a_ctAccProperties,
+ *a_ctAccRename,
+ *a_ctAccSubscribe,
+ *a_ctAccExpireAll,
+ *a_ctAccGetNewHdrs,
+ *a_ctAccGetNewHdrsAll,
+ *a_ctAccDelete,
+ *a_ctAccPostNewArticle;
+
+ //collection-view - groups
+ KAction *a_ctGrpProperties,
+ *a_ctGrpRename,
+ *a_ctGrpGetNewHdrs,
+ *a_ctGrpExpire,
+ *a_ctGrpReorganize,
+ *a_ctGrpUnsubscribe,
+ *a_ctGrpSetAllRead,
+ *a_ctGrpSetAllUnread,
+ *a_ctGrpSetUnread;
+
+ //collection-view - folder
+ KAction *a_ctFolNew,
+ *a_ctFolNewChild,
+ *a_ctFolDelete,
+ *a_ctFolRename,
+ *a_ctFolCompact,
+ *a_ctFolCompactAll,
+ *a_ctFolEmpty,
+ *a_ctFolMboxImport,
+ *a_ctFolMboxExport;
+
+ //header-view - list-handling
+ KSelectAction *a_ctArtSortHeaders;
+ KNFilterSelectAction *a_ctArtFilter;
+ KAction *a_ctArtSortHeadersKeyb,
+ *a_ctArtFilterKeyb,
+ *a_ctArtSearch,
+ *a_ctArtRefreshList,
+ *a_ctArtCollapseAll,
+ *a_ctArtExpandAll,
+ *a_ctArtToggleThread;
+ KToggleAction *a_ctArtToggleShowThreads;
+
+ //header-view - remote articles
+ KAction *a_ctArtSetArtRead,
+ *a_ctArtSetArtUnread,
+ *a_ctArtSetThreadRead,
+ *a_ctArtSetThreadUnread,
+ *a_ctArtOpenNewWindow;
+
+ // scoring
+ KAction *a_ctScoresEdit,
+ *a_ctReScore,
+ *a_ctScoreLower,
+ *a_ctScoreRaise,
+ *a_ctArtToggleIgnored,
+ *a_ctArtToggleWatched;
+
+ //header-view local articles
+ KAction *a_ctArtSendOutbox,
+ *a_ctArtDelete,
+ *a_ctArtSendNow,
+ *a_ctArtEdit;
+
+ //network
+ KAction *a_ctNetCancel;
+
+ KAction *a_ctFetchArticleWithID;
+
+ // settings menu
+ KToggleAction *a_ctToggleGroupView,
+ *a_ctToggleHeaderView,
+ *a_ctToggleArticleViewer,
+ *a_ctToggleQuickSearch;
+ KAction *a_ctSwitchToGroupView,
+ *a_ctSwitchToHeaderView,
+ *a_ctSwitchToArticleViewer;
+
+protected slots:
+ void slotNavNextUnreadArt();
+ void slotNavNextUnreadThread();
+ void slotNavReadThrough();
+
+ void slotAccProperties();
+ void slotAccRename();
+ void slotAccSubscribe();
+ void slotAccExpireAll();
+ void slotAccGetNewHdrs();
+ void slotAccGetNewHdrsAll();
+ void slotAccDelete();
+ void slotAccPostNewArticle();
+
+ void slotGrpProperties();
+ void slotGrpRename();
+ void slotGrpGetNewHdrs();
+ void slotGrpExpire();
+ void slotGrpReorganize();
+ void slotGrpUnsubscribe();
+ void slotGrpSetAllRead();
+ void slotGrpSetAllUnread();
+ void slotGrpSetUnread();
+
+ void slotFolNew();
+ void slotFolNewChild();
+ void slotFolDelete();
+ void slotFolRename();
+ void slotFolCompact();
+ void slotFolCompactAll();
+ void slotFolEmpty();
+ void slotFolMBoxImport();
+ void slotFolMBoxExport();
+
+ void slotArtSortHeaders(int i);
+ void slotArtSortHeadersKeyb();
+ void slotArtSearch();
+ void slotArtRefreshList();
+ void slotArtCollapseAll();
+ void slotArtExpandAll();
+ void slotArtToggleThread();
+ void slotArtToggleShowThreads();
+
+ void slotArtSetArtRead();
+ void slotArtSetArtUnread();
+ void slotArtSetThreadRead();
+ void slotArtSetThreadUnread();
+
+ void slotScoreEdit();
+ void slotReScore();
+ void slotScoreLower();
+ void slotScoreRaise();
+ void slotArtToggleIgnored();
+ void slotArtToggleWatched();
+
+ void slotArtOpenNewWindow();
+ void slotArtSendOutbox();
+ void slotArtDelete();
+ void slotArtSendNow();
+ void slotArtEdit();
+
+ void slotNetCancel();
+
+ void slotFetchArticleWithID();
+
+ void slotToggleGroupView();
+ void slotToggleHeaderView();
+ void slotToggleArticleViewer();
+ void slotToggleQuickSearch();
+ void slotSwitchToGroupView();
+ void slotSwitchToHeaderView();
+ void slotSwitchToArticleViewer();
+ void slotSettings();
+
+ //--------------------------- </Actions> -----------------------------
+
+private:
+ KRSqueezedTextLabel *s_tatusGroup; // widget used in the statusBar() for the group status
+ KRSqueezedTextLabel *s_tatusFilter;
+ KXMLGUIClient *m_GUIClient;
+};
+
+
+class FetchArticleIdDlg : public KDialogBase
+{
+ Q_OBJECT
+public:
+ FetchArticleIdDlg(QWidget *parent, const char */*name*/ );
+ QString messageId() const;
+
+protected slots:
+ void slotTextChanged(const QString & );
+protected:
+ KLineEdit *edit;
+};
+
+#endif
diff --git a/knode/knmemorymanager.cpp b/knode/knmemorymanager.cpp
new file mode 100644
index 000000000..283bcf546
--- /dev/null
+++ b/knode/knmemorymanager.cpp
@@ -0,0 +1,217 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <kdebug.h>
+
+#include "knmemorymanager.h"
+#include "knfolder.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "knarticlemanager.h"
+#include "kngroupmanager.h"
+#include "knfoldermanager.h"
+
+
+KNMemoryManager::KNMemoryManager()
+ : c_ollCacheSize(0), a_rtCacheSize(0)
+{
+}
+
+
+KNMemoryManager::~KNMemoryManager()
+{
+ for ( QValueList<CollectionItem*>::Iterator it = mColList.begin(); it != mColList.end(); ++it )
+ delete (*it);
+ for ( QValueList<ArticleItem*>::Iterator it = mArtList.begin(); it != mArtList.end(); ++it )
+ delete (*it);
+}
+
+
+void KNMemoryManager::updateCacheEntry(KNArticleCollection *c)
+{
+ CollectionItem *ci;
+ int oldSize=0;
+
+ if( (ci=findCacheEntry(c, true)) ) { // item is taken from the list
+ oldSize=ci->storageSize;
+ ci->sync();
+ kdDebug(5003) << "KNMemoryManager::updateCacheEntry() : collection (" << c->name() << ") updated" << endl;
+ }
+ else {
+ ci=new CollectionItem(c);
+ kdDebug(5003) << "KNMemoryManager::updateCacheEntry() : collection (" << c->name() << ") added" << endl;
+ }
+
+ mColList.append(ci);
+ c_ollCacheSize += (ci->storageSize - oldSize);
+ checkMemoryUsageCollections();
+}
+
+
+void KNMemoryManager::removeCacheEntry(KNArticleCollection *c)
+{
+ CollectionItem *ci;
+ ci=findCacheEntry(c, true);
+
+ if(ci) {
+ c_ollCacheSize -= ci->storageSize;
+ delete ci;
+
+ kdDebug(5003) << "KNMemoryManager::removeCacheEntry() : collection removed (" << c->name() << "), "
+ << mColList.count() << " collections left in cache" << endl;
+ }
+}
+
+
+void KNMemoryManager::prepareLoad(KNArticleCollection *c)
+{
+ CollectionItem ci(c);
+
+ c_ollCacheSize += ci.storageSize;
+ checkMemoryUsageCollections();
+ c_ollCacheSize -= ci.storageSize;
+}
+
+
+void KNMemoryManager::updateCacheEntry(KNArticle *a)
+{
+ ArticleItem *ai;
+ int oldSize=0;
+
+ if( (ai=findCacheEntry(a, true)) ) {
+ oldSize=ai->storageSize;
+ ai->sync();
+ kdDebug(5003) << "KNMemoryManager::updateCacheEntry() : article updated" << endl;
+ }
+ else {
+ ai=new ArticleItem(a);
+ kdDebug(5003) << "KNMemoryManager::updateCacheEntry() : article added" << endl;
+ }
+
+ mArtList.append(ai);
+ a_rtCacheSize += (ai->storageSize - oldSize);
+ checkMemoryUsageArticles();
+}
+
+
+void KNMemoryManager::removeCacheEntry(KNArticle *a)
+{
+ ArticleItem *ai;
+
+ if( (ai=findCacheEntry(a, true)) ) {
+ a_rtCacheSize -= ai->storageSize;
+ delete ai;
+
+ kdDebug(5003) << "KNMemoryManager::removeCacheEntry() : article removed, "
+ << mArtList.count() << " articles left in cache" << endl;
+
+ }
+}
+
+
+KNMemoryManager::CollectionItem* KNMemoryManager::findCacheEntry(KNArticleCollection *c, bool take)
+{
+ for ( QValueList<CollectionItem*>::Iterator it = mColList.begin(); it != mColList.end(); ++it ) {
+ if ( (*it)->col == c ) {
+ CollectionItem *ret = (*it);
+ if ( take )
+ mColList.remove( it );
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+
+KNMemoryManager::ArticleItem* KNMemoryManager::findCacheEntry(KNArticle *a, bool take)
+{
+ for ( QValueList<ArticleItem*>::Iterator it = mArtList.begin(); it != mArtList.end(); ++it ) {
+ if ( (*it)->art == a ) {
+ ArticleItem *ret = (*it);
+ if ( take )
+ mArtList.remove( it );
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+
+void KNMemoryManager::checkMemoryUsageCollections()
+{
+ int maxSize = knGlobals.configManager()->readNewsGeneral()->collCacheSize() * 1024;
+ KNArticleCollection *c=0;
+
+ if (c_ollCacheSize > maxSize) {
+ QValueList<CollectionItem*> tempList( mColList ); // work on a copy, KNGroup-/Foldermanager will
+ // modify the original list
+
+ for ( QValueList<CollectionItem*>::Iterator it = tempList.begin(); it != tempList.end(); ) {
+ if ( c_ollCacheSize <= maxSize )
+ break;
+ // unloadHeaders() will remove the cache entry and thus invalidate the iterator!
+ c = (*it)->col;
+ ++it;
+
+ if (c->type() == KNCollection::CTgroup)
+ knGlobals.groupManager()->unloadHeaders(static_cast<KNGroup*>(c), false); // *try* to unload
+ else
+ if (c->type() == KNCollection::CTfolder)
+ knGlobals.folderManager()->unloadHeaders(static_cast<KNFolder*>(c), false); // *try* to unload
+ }
+ }
+
+ kdDebug(5003) << "KNMemoryManager::checkMemoryUsageCollections() : "
+ << mColList.count() << " collections in cache => Usage : "
+ << ( c_ollCacheSize*100.0 / maxSize ) << "%" << endl;
+}
+
+
+void KNMemoryManager::checkMemoryUsageArticles()
+{
+ int maxSize = knGlobals.configManager()->readNewsGeneral()->artCacheSize() * 1024;
+
+ if (a_rtCacheSize > maxSize) {
+ QValueList<ArticleItem*> tempList( mArtList ); // work on a copy, KNArticlemanager will
+ // modify the original list
+
+ for ( QValueList<ArticleItem*>::Iterator it = mArtList.begin(); it != mArtList.end(); ) {
+ if ( a_rtCacheSize <= maxSize )
+ break;
+ // unloadArticle() will remove the cache entry and thus invalidate the iterator!
+ KNArticle *art = (*it)->art;
+ ++it;
+ knGlobals.articleManager()->unloadArticle( art, false ); // *try* to unload
+ }
+ }
+
+ kdDebug(5003) << "KNMemoryManager::checkMemoryUsageArticles() : "
+ << mArtList.count() << " articles in cache => Usage : "
+ << ( a_rtCacheSize*100.0 / maxSize ) << "%" << endl;
+}
+
+
+void KNMemoryManager::ArticleItem::sync()
+{
+ storageSize=art->storageSize();
+}
+
+
+void KNMemoryManager::CollectionItem::sync()
+{
+ storageSize=col->length()*1024; // rule of thumb : ~1k per header
+}
+
diff --git a/knode/knmemorymanager.h b/knode/knmemorymanager.h
new file mode 100644
index 000000000..a9a79d9cf
--- /dev/null
+++ b/knode/knmemorymanager.h
@@ -0,0 +1,74 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNMEMORYMANAGER_H
+#define KNMEMORYMANAGER_H
+
+#include <qglobal.h>
+#include <qvaluelist.h>
+
+class KNArticle;
+class KNArticleCollection;
+
+
+class KNMemoryManager {
+
+ public:
+ KNMemoryManager();
+ ~KNMemoryManager();
+
+ /** Collection-Handling */
+ void updateCacheEntry(KNArticleCollection *c);
+ void removeCacheEntry(KNArticleCollection *c);
+ /** try to free enough memory for this collection */
+ void prepareLoad(KNArticleCollection *c);
+
+ /** Article-Handling */
+ void updateCacheEntry(KNArticle *a);
+ void removeCacheEntry(KNArticle *a);
+
+ protected:
+
+ class ArticleItem {
+ public:
+ ArticleItem(KNArticle *a) { art=a; sync(); }
+ ~ArticleItem() {}
+ void sync();
+
+ KNArticle *art;
+ int storageSize;
+ };
+
+ class CollectionItem {
+ public:
+ CollectionItem(KNArticleCollection *c) { col=c; sync(); }
+ ~CollectionItem() { }
+ void sync();
+
+ KNArticleCollection *col;
+ int storageSize;
+ };
+
+ CollectionItem* findCacheEntry(KNArticleCollection *c, bool take=false);
+ ArticleItem* findCacheEntry(KNArticle *a, bool take=false);
+ void checkMemoryUsageCollections();
+ void checkMemoryUsageArticles();
+
+ QValueList<CollectionItem*> mColList;
+ QValueList<ArticleItem*> mArtList;
+ int c_ollCacheSize, a_rtCacheSize;
+};
+
+
+#endif
diff --git a/knode/knnetaccess.cpp b/knode/knnetaccess.cpp
new file mode 100644
index 000000000..65b43e7a5
--- /dev/null
+++ b/knode/knnetaccess.cpp
@@ -0,0 +1,545 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <qsocketnotifier.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kio/job.h>
+#include <kio/passdlg.h>
+#include <ksocks.h>
+#include <kapplication.h>
+
+#include "knaccountmanager.h"
+#include "knarticle.h"
+#include "knmainwidget.h"
+#include "knjobdata.h"
+#include "knnntpclient.h"
+#include "knglobals.h"
+#include "knnetaccess.h"
+#include "knwidgets.h"
+
+using KPIM::ProgressManager;
+
+
+KNNetAccess::KNNetAccess(QObject *parent, const char *name )
+ : QObject(parent,name), currentNntpJob(0), currentSmtpJob(0)
+{
+ if ( pipe(nntpInPipe) == -1 || pipe(nntpOutPipe) == -1 ) {
+ KMessageBox::error(knGlobals.topWidget, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+ if ( fcntl( nntpInPipe[0], F_SETFL, O_NONBLOCK ) == -1 ||
+ fcntl( nntpOutPipe[0], F_SETFL, O_NONBLOCK ) == -1 ) {
+ KMessageBox::error(knGlobals.topWidget, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+
+ nntpNotifier=new QSocketNotifier(nntpInPipe[0], QSocketNotifier::Read);
+ connect(nntpNotifier, SIGNAL(activated(int)), this, SLOT(slotThreadSignal(int)));
+
+ // initialize the KSocks stuff in the main thread, otherwise we get
+ // strange effects on FreeBSD
+ (void) KSocks::self();
+
+ nntpClient=new KNNntpClient(nntpOutPipe[0],nntpInPipe[1],nntp_Mutex);
+ nntpClient->start();
+
+ connect( knGlobals.accountManager(), SIGNAL(passwordsChanged()), SLOT(slotPasswordsChanged()) );
+}
+
+
+
+KNNetAccess::~KNNetAccess()
+{
+ disconnect(nntpNotifier, SIGNAL(activated(int)), this, SLOT(slotThreadSignal(int)));
+
+ nntpClient->terminateClient();
+ triggerAsyncThread(nntpOutPipe[1]);
+ nntpClient->wait();
+
+ delete nntpClient;
+ delete nntpNotifier;
+
+ if ( ::close(nntpInPipe[0]) == -1 ||
+ ::close(nntpInPipe[1]) == -1 ||
+ ::close(nntpOutPipe[0]) == -1 ||
+ ::close(nntpOutPipe[1]) == -1 )
+ kdDebug(5003) << "Can't close pipes" << endl;
+}
+
+
+
+void KNNetAccess::addJob(KNJobData *job)
+{
+ // kdDebug(5003) << "KNNetAccess::addJob() : job queued" << endl;
+ if(job->account()==0) {
+ job->setErrorString(i18n("Internal Error: No account set for this job."));
+ job->notifyConsumer();
+ return;
+ }
+
+ job->createProgressItem();
+ connect( job->progressItem(), SIGNAL(progressItemCanceled(KPIM::ProgressItem*)), SLOT(slotCancelJob(KPIM::ProgressItem*)) );
+ emit netActive( true );
+
+ // put jobs which are waiting for the wallet into an extra queue
+ if ( !job->account()->readyForLogin() ) {
+ mWalletQueue.append( job );
+ knGlobals.accountManager()->loadPasswordsAsync();
+ job->setStatus( i18n( "Waiting for KWallet..." ) );
+ return;
+ }
+
+ if (job->type()==KNJobData::JTmail) {
+ smtpJobQueue.append(job);
+ if (!currentSmtpJob) // no active job, start the new one
+ startJobSmtp();
+ } else {
+
+ /*
+ TODO: the following code doesn't really belong here, it should
+ be moved to KNGroupManager, or elsewere...
+ */
+
+ // avoid duplicate fetchNewHeader jobs...
+ bool duplicate = false;
+ if ( job->type() == KNJobData::JTfetchNewHeaders || job->type() == KNJobData::JTsilentFetchNewHeaders ) {
+ QValueList<KNJobData*>::ConstIterator it;
+ for ( it = nntpJobQueue.begin(); it != nntpJobQueue.end(); ++it ) {
+ if ( ( (*it)->type() == KNJobData::JTfetchNewHeaders || (*it)->type() == KNJobData::JTsilentFetchNewHeaders )
+ && (*it)->data() == job->data() ) // job works on the same group...
+ duplicate = true;
+ }
+ }
+
+ if (!duplicate) {
+ // give a lower priority to fetchNewHeaders and postArticle jobs
+ if ( job->type() == KNJobData::JTfetchNewHeaders
+ || job->type() == KNJobData::JTsilentFetchNewHeaders
+ || job->type() == KNJobData::JTpostArticle ) {
+ nntpJobQueue.append( job );
+ } else {
+ nntpJobQueue.prepend( job );
+ }
+
+ if (!currentNntpJob) // no active job, start the new one
+ startJobNntp();
+ }
+ }
+ updateStatus();
+}
+
+
+void KNNetAccess::cancelCurrentNntpJob( int type )
+{
+ if ((currentNntpJob && !currentNntpJob->canceled()) && ((type==0)||(currentNntpJob->type()==type))) { // stop active job
+ currentNntpJob->cancel();
+ triggerAsyncThread(nntpOutPipe[1]);
+ }
+}
+
+
+void KNNetAccess::stopJobsNntp( int type )
+{
+ cancelCurrentNntpJob( type );
+ KNJobData *tmp = 0;
+ QValueList<KNJobData*>::Iterator it;
+ for ( it = nntpJobQueue.begin(); it != nntpJobQueue.end();) {
+ tmp = *it;
+ if ( type == 0 || tmp->type() == type ) {
+ it = nntpJobQueue.remove( it );
+ tmp->cancel();
+ tmp->notifyConsumer();
+ } else
+ ++it;
+ }
+ for ( it = mWalletQueue.begin(); it != mWalletQueue.end();) {
+ tmp = *it;
+ if ( type == 0 || tmp->type() == type ) {
+ it = mWalletQueue.remove( it );
+ tmp->cancel();
+ tmp->notifyConsumer();
+ } else
+ ++it;
+ }
+ updateStatus();
+}
+
+
+
+// type==0 => all jobs
+void KNNetAccess::cancelCurrentSmtpJob( int type )
+{
+ if ((currentSmtpJob && !currentSmtpJob->canceled()) && ((type==0)||(currentSmtpJob->type()==type))) { // stop active job
+ currentSmtpJob->cancel();
+ threadDoneSmtp();
+ }
+}
+
+
+void KNNetAccess::stopJobsSmtp( int type )
+{
+ cancelCurrentSmtpJob( type );
+ KNJobData *tmp = 0;
+ QValueList<KNJobData*>::Iterator it;
+ for ( it = smtpJobQueue.begin(); it != smtpJobQueue.end();) {
+ tmp = *it;
+ if ( type == 0 || tmp->type() == type ) {
+ it = smtpJobQueue.remove( it );
+ tmp->cancel();
+ tmp->notifyConsumer();
+ } else
+ ++it;
+ }
+ updateStatus();
+}
+
+
+
+// passes a signal through the ipc-pipe to the net-thread
+void KNNetAccess::triggerAsyncThread(int pipeFd)
+{
+ int signal=0;
+
+ // kdDebug(5003) << "KNNetAccess::triggerAsyncThread() : sending signal to net thread" << endl;
+ write(pipeFd, &signal, sizeof(int));
+}
+
+
+
+void KNNetAccess::startJobNntp()
+{
+ if ( nntpJobQueue.isEmpty() )
+ return;
+
+ currentNntpJob = nntpJobQueue.first();
+ nntpJobQueue.remove( nntpJobQueue.begin() );
+ currentNntpJob->prepareForExecution();
+ if (currentNntpJob->success()) {
+ nntpClient->insertJob(currentNntpJob);
+ triggerAsyncThread(nntpOutPipe[1]);
+ kdDebug(5003) << "KNNetAccess::startJobNntp(): job started" << endl;
+ } else {
+ threadDoneNntp();
+ }
+}
+
+
+
+void KNNetAccess::startJobSmtp()
+{
+ if ( smtpJobQueue.isEmpty() )
+ return;
+
+ currentSmtpJob = smtpJobQueue.first();
+ smtpJobQueue.remove( smtpJobQueue.begin() );
+ currentSmtpJob->prepareForExecution();
+ if (currentSmtpJob->success()) {
+ KNLocalArticle *art = static_cast<KNLocalArticle*>( currentSmtpJob->data() );
+ // create url query part
+ QString query("headers=0&from=");
+ query += KURL::encode_string( art->from()->email() );
+ QStrList emails;
+ art->to()->emails( &emails );
+ for ( char *e = emails.first(); e; e = emails.next() ) {
+ query += "&to=" + KURL::encode_string( e );
+ }
+ // create url
+ KURL destination;
+ KNServerInfo *account = currentSmtpJob->account();
+ if ( account->encryption() == KNServerInfo::SSL )
+ destination.setProtocol( "smtps" );
+ else
+ destination.setProtocol( "smtp" );
+ destination.setHost( account->server() );
+ destination.setPort( account->port() );
+ destination.setQuery( query );
+ if ( account->needsLogon() ) {
+ destination.setUser( account->user() );
+ destination.setPass( account->pass() );
+ }
+ KIO::Job* job = KIO::storedPut( art->encodedContent(true), destination, -1, false, false, false );
+ connect( job, SIGNAL( result(KIO::Job*) ),
+ SLOT( slotJobResult(KIO::Job*) ) );
+ if ( account->encryption() == KNServerInfo::TLS )
+ job->addMetaData( "tls", "on" );
+ else
+ job->addMetaData( "tls", "off" );
+ currentSmtpJob->setJob( job );
+
+ kdDebug(5003) << "KNNetAccess::startJobSmtp(): job started" << endl;
+ } else {
+ threadDoneSmtp();
+ }
+}
+
+
+
+void KNNetAccess::threadDoneNntp()
+{
+ KNJobData *tmp;
+ if (!currentNntpJob) {
+ kdWarning(5003) << "KNNetAccess::threadDoneNntp(): no current job?? aborting" << endl;
+ return;
+ }
+
+ kdDebug(5003) << "KNNetAccess::threadDoneNntp(): job done" << endl;
+
+ tmp = currentNntpJob;
+
+ if (!tmp->success() && tmp->authError()) {
+ kdDebug(5003) << "KNNetAccess::threadDoneNntp(): authentication error" << endl;
+ KNServerInfo *info = tmp->account();
+ if (info) {
+ QString user = info->user();
+ QString pass = info->pass();
+ bool keep=false;
+ if (KDialog::Accepted == KIO::PasswordDialog::getNameAndPassword(user, pass, &keep,
+ i18n("You need to supply a username and a\npassword to access this server"), false,
+ kapp->makeStdCaption(i18n("Authentication Failed")),info->server(),i18n("Server:"))) {
+ info->setNeedsLogon(true);
+ info->setUser(user);
+ info->setPass(pass);
+ tmp->setAuthError(false);
+ tmp->setErrorString(QString::null);
+
+ kdDebug(5003) << "KNNetAccess::threadDoneNntp(): trying again with authentication data" << endl;
+
+ // restart job...
+ triggerAsyncThread(nntpOutPipe[1]);
+ return;
+ }
+ }
+ }
+
+ nntpClient->removeJob();
+ currentNntpJob = 0L;
+
+ currMsg = QString::null;
+ knGlobals.setStatusMsg();
+ tmp->setComplete();
+
+ tmp->notifyConsumer();
+
+ if (!nntpJobQueue.isEmpty())
+ startJobNntp();
+
+ updateStatus();
+}
+
+
+
+void KNNetAccess::threadDoneSmtp()
+{
+ KNJobData *tmp;
+ if (!currentSmtpJob) {
+ kdWarning(5003) << "KNNetAccess::threadDoneSmtp(): no current job?? aborting" << endl;
+ return;
+ }
+
+ kdDebug(5003) << "KNNetAccess::threadDoneSmtp(): job done" << endl;
+
+ tmp = currentSmtpJob;
+ currentSmtpJob = 0L;
+ if (!currentNntpJob) {
+ currMsg = QString::null;
+ knGlobals.setStatusMsg();
+ }
+ tmp->setComplete();
+
+ tmp->notifyConsumer();
+
+ if (!smtpJobQueue.isEmpty())
+ startJobSmtp();
+
+ updateStatus();
+}
+
+
+void KNNetAccess::cancelAllJobs()
+{
+ stopJobsNntp(0);
+ stopJobsSmtp(0);
+}
+
+
+
+void KNNetAccess::slotThreadSignal(int i)
+{
+ int signal;
+ QString tmp;
+
+ //kdDebug(5003) << "KNNetAccess::slotThreadSignal() : signal received from net thread" << endl;
+ if(read(i, &signal, sizeof(int))==-1) {
+ kdDebug(5003) << "KNNetAccess::slotThreadSignal() : cannot read from pipe" << endl;
+ return;
+ }
+
+ if (i == nntpInPipe[0]) { // signal from nntp thread
+ switch(signal) {
+ case KNProtocolClient::TSworkDone:
+ threadDoneNntp();
+ break;
+ case KNProtocolClient::TSconnect:
+ currMsg = i18n(" Connecting to server...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSloadGrouplist:
+ currMsg = i18n(" Loading group list from disk...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSwriteGrouplist:
+ currMsg = i18n(" Writing group list to disk...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSdownloadGrouplist:
+ currMsg = i18n(" Downloading group list...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSdownloadNewGroups:
+ currMsg = i18n(" Looking for new groups...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSdownloadDesc:
+ currMsg = i18n(" Downloading group descriptions...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSdownloadNew:
+ currMsg = i18n(" Downloading new headers...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSsortNew:
+ currMsg = i18n(" Sorting...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSdownloadArticle:
+ currMsg = i18n(" Downloading article...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSsendArticle:
+ currMsg = i18n(" Sending article...");
+ knGlobals.setStatusMsg(currMsg);
+ currentNntpJob->setStatus(currMsg);
+ break;
+ case KNProtocolClient::TSjobStarted:
+ currentNntpJob->setProgress(0);
+ break;
+ case KNProtocolClient::TSprogressUpdate:
+ currentNntpJob->setProgress(nntpClient->getProgressValue()/10);
+ break;
+ };
+ }
+}
+
+
+void KNNetAccess::slotJobResult( KIO::Job *job )
+{
+ if ( job == currentSmtpJob->job() ) {
+ if ( job->error() )
+ currentSmtpJob->setErrorString( job->errorString() );
+ threadDoneSmtp();
+ return;
+ }
+ if ( job == currentNntpJob->job() ) {
+ // TODO
+ return;
+ }
+ kdError(5003) << k_funcinfo << "unknown job" << endl;
+}
+
+
+void KNNetAccess::slotPasswordsChanged()
+{
+ QValueList<KNJobData*>::ConstIterator it;
+ for ( it = mWalletQueue.begin(); it != mWalletQueue.end(); ++it ) {
+ (*it)->setStatus( i18n("Waiting...") );
+ if ( (*it)->type() == KNJobData::JTmail )
+ smtpJobQueue.append( (*it) );
+ else
+ nntpJobQueue.append( (*it) );
+ }
+ mWalletQueue.clear();
+ if ( !currentNntpJob )
+ startJobNntp();
+ if ( !currentSmtpJob )
+ startJobSmtp();
+}
+
+
+void KNNetAccess::slotCancelJob( KPIM::ProgressItem *item )
+{
+ KNJobData *tmp = 0;
+ QValueList<KNJobData*>::Iterator it;
+ for ( it = nntpJobQueue.begin(); it != nntpJobQueue.end();) {
+ tmp = *it;
+ if ( tmp->progressItem() == item ) {
+ it = nntpJobQueue.remove( it );
+ tmp->cancel();
+ tmp->notifyConsumer();
+ } else
+ ++it;
+ }
+ for ( it = smtpJobQueue.begin(); it != smtpJobQueue.end();) {
+ tmp = *it;
+ if ( tmp->progressItem() == item ) {
+ it = smtpJobQueue.remove( it );
+ tmp->cancel();
+ tmp->notifyConsumer();
+ } else
+ ++it;
+ }
+ for ( it = mWalletQueue.begin(); it != mWalletQueue.end();) {
+ tmp = *it;
+ if ( tmp->progressItem() == item ) {
+ it = mWalletQueue.remove( it );
+ tmp->cancel();
+ tmp->notifyConsumer();
+ } else
+ ++it;
+ }
+
+ if ( currentNntpJob && currentNntpJob->progressItem() == item )
+ cancelCurrentNntpJob();
+ if ( currentSmtpJob && currentSmtpJob->progressItem() == item )
+ cancelCurrentSmtpJob();
+
+ updateStatus();
+}
+
+void KNNetAccess::updateStatus( )
+{
+ if ( nntpJobQueue.isEmpty() && smtpJobQueue.isEmpty() && !currentNntpJob
+ && !currentSmtpJob && mWalletQueue.isEmpty() )
+ emit netActive( false );
+ else
+ emit netActive( true );
+}
+
+//--------------------------------
+
+#include "knnetaccess.moc"
diff --git a/knode/knnetaccess.h b/knode/knnetaccess.h
new file mode 100644
index 000000000..02450b9c7
--- /dev/null
+++ b/knode/knnetaccess.h
@@ -0,0 +1,102 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNNETACCESS_H
+#define KNNETACCESS_H
+
+#include <qobject.h>
+#include <qmutex.h>
+#include <qvaluelist.h>
+
+class QSocketNotifier;
+
+namespace KIO {
+ class Job;
+}
+
+namespace KPIM {
+ class ProgressItem;
+}
+using KPIM::ProgressItem;
+
+class KNJobData;
+class KNNntpClient;
+
+
+class KNNetAccess : public QObject {
+
+ Q_OBJECT
+
+ public:
+
+ KNNetAccess(QObject *parent=0, const char *name=0);
+ ~KNNetAccess();
+
+ void addJob(KNJobData *job);
+ /** type==0 => all jobs */
+ void stopJobsNntp(int type);
+ /** type==0 => all jobs */
+ void stopJobsSmtp(int type);
+ void cancelAllJobs();
+
+ /** current statusbar message */
+ QString currentMsg() const { return currMsg; }
+
+ QMutex& nntpMutex() { return nntp_Mutex; }
+
+ protected:
+ /** passes a signal through the ipc-pipe to the net-thread */
+ void triggerAsyncThread(int pipeFd);
+ void startJobNntp();
+ void startJobSmtp();
+ void threadDoneNntp();
+ void threadDoneSmtp();
+
+ /** stores the current status message,
+ so that it can be restored by the mainwindow */
+ QString currMsg;
+
+ KNNntpClient *nntpClient;
+ QValueList<KNJobData*> nntpJobQueue, smtpJobQueue;
+ KNJobData *currentNntpJob, *currentSmtpJob;
+ QMutex nntp_Mutex;
+ int nntpInPipe[2], nntpOutPipe[2];
+ QSocketNotifier *nntpNotifier;
+
+ protected slots:
+ void slotThreadSignal(int i);
+
+ signals:
+ void netActive(bool);
+
+ private:
+ void cancelCurrentNntpJob( int type = 0 );
+ void cancelCurrentSmtpJob( int type = 0 );
+ /** Update activitiy status, i.e. emit netActive signal. */
+ void updateStatus();
+
+ private slots:
+ void slotJobResult( KIO::Job *job );
+
+ void slotCancelJob( KPIM::ProgressItem *item );
+
+ void slotPasswordsChanged();
+
+ private:
+ /// jobs waiting for async wallet loading
+ QValueList<KNJobData*> mWalletQueue;
+
+};
+
+#endif
diff --git a/knode/knnntpaccount.cpp b/knode/knnntpaccount.cpp
new file mode 100644
index 000000000..22ff904b2
--- /dev/null
+++ b/knode/knnntpaccount.cpp
@@ -0,0 +1,229 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+#include "utilities.h"
+#include "kncollectionviewitem.h"
+#include "knnntpaccount.h"
+#include "knconfig.h"
+#include "knconfigmanager.h"
+#include "knconfigwidgets.h"
+#include "kngroupmanager.h"
+#include "knglobals.h"
+
+
+KNNntpAccountIntervalChecking::KNNntpAccountIntervalChecking(KNNntpAccount* account) : t_imer(0) {
+ a_ccount = account;
+}
+
+
+
+KNNntpAccountIntervalChecking::~KNNntpAccountIntervalChecking()
+{
+ if (t_imer) deinstallTimer();
+ a_ccount = 0;
+}
+
+
+
+void KNNntpAccountIntervalChecking::installTimer()
+{
+ if (a_ccount->checkInterval() <= 0) return;
+ if(!t_imer)
+ {
+ t_imer = new QTimer();
+ connect(t_imer,SIGNAL(timeout()),this,SLOT(slotCheckNews()));
+ }
+ else
+ {
+ t_imer->stop();
+ }
+ t_imer->start(a_ccount->checkInterval()*60000);
+}
+
+
+
+void KNNntpAccountIntervalChecking::deinstallTimer()
+{
+ delete t_imer;
+ t_imer = 0;
+}
+
+
+
+void KNNntpAccountIntervalChecking::slotCheckNews()
+{
+ knGlobals.groupManager()->checkAll(a_ccount, true);
+}
+
+
+
+KNNntpAccount::KNNntpAccount()
+ : KNCollection(0), KNServerInfo(), i_dentity(0), f_etchDescriptions(true), w_asOpen(false), i_ntervalChecking(false), c_heckInterval(10)
+{
+ l_astNewFetch = QDate::currentDate();
+ a_ccountIntervalChecking = new KNNntpAccountIntervalChecking(this);
+ mCleanupConf = new KNConfig::Cleanup( false );
+}
+
+
+KNNntpAccount::~KNNntpAccount()
+{
+ delete a_ccountIntervalChecking;
+ delete i_dentity;
+ delete mCleanupConf;
+}
+
+
+// tries to read information, returns false if it fails to do so
+bool KNNntpAccount::readInfo(const QString &confPath)
+{
+ KSimpleConfig conf(confPath);
+
+ n_ame = conf.readEntry("name");
+ //u_nsentCount = conf.readNumEntry("unsentCnt", 0);
+ f_etchDescriptions = conf.readBoolEntry("fetchDescriptions", true);
+ l_astNewFetch = conf.readDateTimeEntry("lastNewFetch").date();
+ w_asOpen = conf.readBoolEntry("listItemOpen", false);
+ u_seDiskCache = conf.readBoolEntry("useDiskCache", false);
+ i_ntervalChecking=conf.readBoolEntry("intervalChecking", false);
+ c_heckInterval=conf.readNumEntry("checkInterval", 10);
+ KNServerInfo::readConf(&conf);
+
+ startTimer();
+
+ i_dentity=new KNConfig::Identity(false);
+ i_dentity->loadConfig(&conf);
+ if(!i_dentity->isEmpty()) {
+ kdDebug(5003) << "KNGroup::readInfo(const QString &confPath) : using alternative user for " << n_ame << endl;
+ } else {
+ delete i_dentity;
+ i_dentity=0;
+ }
+
+ mCleanupConf->loadConfig( &conf );
+
+ if (n_ame.isEmpty() || s_erver.isEmpty() || i_d == -1)
+ return false;
+ else
+ return true;
+}
+
+
+void KNNntpAccount::saveInfo()
+{
+ QString dir(path());
+ if (dir.isNull())
+ return;
+
+ KSimpleConfig conf(dir+"info");
+
+ conf.writeEntry("name", n_ame);
+ //conf.writeEntry("unsentCnt", u_nsentCount);
+ conf.writeEntry("fetchDescriptions", f_etchDescriptions);
+ conf.writeEntry("lastNewFetch", QDateTime(l_astNewFetch));
+ if(l_istItem)
+ conf.writeEntry("listItemOpen", l_istItem->isOpen());
+ conf.writeEntry("useDiskCache", u_seDiskCache);
+ conf.writeEntry("intervalChecking", i_ntervalChecking);
+ conf.writeEntry("checkInterval", c_heckInterval);
+
+ KNServerInfo::saveConf(&conf); // save not KNNntpAccount specific settings
+
+ if(i_dentity)
+ i_dentity->saveConfig(&conf);
+ else if(conf.hasKey("Email")) {
+ conf.deleteEntry("Name", false);
+ conf.deleteEntry("Email", false);
+ conf.deleteEntry("Reply-To", false);
+ conf.deleteEntry("Mail-Copies-To", false);
+ conf.deleteEntry("Org", false);
+ conf.deleteEntry("UseSigFile", false);
+ conf.deleteEntry("UseSigGenerator", false);
+ conf.deleteEntry("sigFile", false);
+ conf.deleteEntry("sigText", false);
+ }
+
+ mCleanupConf->saveConfig( &conf );
+}
+
+
+/*void KNNntpAccount::syncInfo()
+{
+ QString dir(path());
+ if (dir.isNull())
+ return;
+ KSimpleConfig conf(dir+"info");
+ conf.writeEntry("unsentCnt", u_nsentCount);
+}*/
+
+
+QString KNNntpAccount::path()
+{
+ QString dir(locateLocal("data","knode/")+QString("nntp.%1/").arg(i_d));
+ if (dir.isNull())
+ KNHelper::displayInternalFileError();
+ return (dir);
+}
+
+
+bool KNNntpAccount::editProperties(QWidget *parent)
+{
+ if(!i_dentity) i_dentity=new KNConfig::Identity(false);
+ KNConfig::NntpAccountConfDialog *d = new KNConfig::NntpAccountConfDialog(this, parent);
+
+ bool ret=false;
+ if (d->exec()) {
+ updateListItem();
+ ret=true;
+ }
+
+ if(i_dentity->isEmpty()) {
+ delete i_dentity;
+ i_dentity=0;
+ }
+
+ delete d;
+ return ret;
+}
+
+void KNNntpAccount::startTimer()
+{
+ if ( (i_ntervalChecking == true) && (c_heckInterval > 0) )
+ {
+ a_ccountIntervalChecking->installTimer();
+ }
+ else
+ {
+ a_ccountIntervalChecking->deinstallTimer();
+ }
+}
+
+void KNNntpAccount::setCheckInterval(int c)
+{
+ c_heckInterval = c;
+ startTimer();
+}
+
+KNConfig::Cleanup *KNNntpAccount::activeCleanupConfig() const
+{
+ if (cleanupConfig()->useDefault())
+ return knGlobals.configManager()->cleanup();
+ return cleanupConfig();
+}
+
+#include "knnntpaccount.moc"
diff --git a/knode/knnntpaccount.h b/knode/knnntpaccount.h
new file mode 100644
index 000000000..4c7694ff6
--- /dev/null
+++ b/knode/knnntpaccount.h
@@ -0,0 +1,117 @@
+/*
+ knnntpaccount.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNNNTPACCOUNT_H
+#define KNNNTPACCOUNT_H
+
+#include <qdatetime.h>
+
+#include "kncollection.h"
+#include "knserverinfo.h"
+#include <qobject.h>
+#include <qtimer.h>
+
+class KNNntpAccount;
+
+namespace KNConfig {
+ class Identity;
+ class Cleanup;
+}
+
+
+class KNNntpAccountIntervalChecking : public QObject {
+
+ Q_OBJECT
+
+ public:
+ KNNntpAccountIntervalChecking(KNNntpAccount *account);
+ ~KNNntpAccountIntervalChecking();
+ void installTimer();
+ void deinstallTimer();
+
+ protected:
+ QTimer *t_imer;
+ KNNntpAccount *a_ccount;
+
+ protected slots:
+ void slotCheckNews();
+
+};
+
+class KNNntpAccount : public KNCollection , public KNServerInfo {
+
+ public:
+ KNNntpAccount();
+ ~KNNntpAccount();
+
+ collectionType type() { return CTnntpAccount; }
+
+ /** tries to read information, returns false if it fails to do so */
+ bool readInfo(const QString &confPath);
+ void saveInfo();
+ //void syncInfo();
+ QString path();
+ /** returns true when the user accepted */
+ bool editProperties(QWidget *parent);
+
+ // news interval checking
+ void startTimer();
+
+ //get
+ bool fetchDescriptions() const { return f_etchDescriptions; }
+ QDate lastNewFetch() const { return l_astNewFetch; }
+ bool wasOpen() const { return w_asOpen; }
+ bool useDiskCache() const { return u_seDiskCache; }
+ KNConfig::Identity* identity() const { return i_dentity; }
+ bool intervalChecking() const { return i_ntervalChecking; }
+ int checkInterval() const { return c_heckInterval; }
+ KNConfig::Cleanup *cleanupConfig() const { return mCleanupConf; }
+
+ /** Returns the cleanup configuration that should be used for this account */
+ KNConfig::Cleanup *activeCleanupConfig() const;
+
+ //set
+ void setFetchDescriptions(bool b) { f_etchDescriptions = b; }
+ void setLastNewFetch(QDate date) { l_astNewFetch = date; }
+ void setUseDiskCache(bool b) { u_seDiskCache=b; }
+ void setCheckInterval(int c);
+ void setIntervalChecking(bool b) { i_ntervalChecking=b; }
+
+ protected:
+ /** server specific identity */
+ KNConfig::Identity *i_dentity;
+ /** account specific cleanup configuration */
+ KNConfig::Cleanup *mCleanupConf;
+ /** use an additional "list newsgroups" command to fetch the newsgroup descriptions */
+ bool f_etchDescriptions;
+ /** last use of "newgroups" */
+ QDate l_astNewFetch;
+ /** was the server open in the listview on the last shutdown? */
+ bool w_asOpen;
+ /** cache fetched articles on disk */
+ bool u_seDiskCache;
+ /** is interval checking enabled */
+ bool i_ntervalChecking;
+ int c_heckInterval;
+
+ /** helper class for news interval checking, manages the QTimer */
+ KNNntpAccountIntervalChecking *a_ccountIntervalChecking;
+
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/knnntpclient.cpp b/knode/knnntpclient.cpp
new file mode 100644
index 000000000..2c594be34
--- /dev/null
+++ b/knode/knnntpclient.cpp
@@ -0,0 +1,748 @@
+/*
+ knnntpclient.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <stdlib.h>
+#include <klocale.h>
+#include <qtextcodec.h>
+#include <qmutex.h>
+
+#include "kngroupmanager.h"
+#include "knnntpclient.h"
+#include "utilities.h"
+
+
+KNNntpClient::KNNntpClient(int NfdPipeIn, int NfdPipeOut, QMutex& nntpMutex)
+: KNProtocolClient(NfdPipeIn,NfdPipeOut), mutex(nntpMutex)
+{}
+
+
+KNNntpClient::~KNNntpClient()
+{}
+
+
+// examines the job and calls the suitable handling method
+void KNNntpClient::processJob()
+{
+ switch (job->type()) {
+ case KNJobData::JTLoadGroups :
+ doLoadGroups();
+ break;
+ case KNJobData::JTFetchGroups :
+ doFetchGroups();
+ break;
+ case KNJobData::JTCheckNewGroups :
+ doCheckNewGroups();
+ break;
+ case KNJobData::JTfetchNewHeaders :
+ case KNJobData::JTsilentFetchNewHeaders :
+ doFetchNewHeaders();
+ break;
+ case KNJobData::JTfetchArticle :
+ doFetchArticle();
+ break;
+ case KNJobData::JTpostArticle :
+ doPostArticle();
+ break;
+ case KNJobData::JTfetchSource :
+ doFetchSource();
+ break;
+ default:
+#ifndef NDEBUG
+ qDebug("knode: KNNntpClient::processJob(): mismatched job");
+#endif
+ break;
+ }
+}
+
+
+void KNNntpClient::doLoadGroups()
+{
+ KNGroupListData *target = static_cast<KNGroupListData *>(job->data());
+ sendSignal(TSloadGrouplist);
+
+ if (!target->readIn(this))
+ job->setErrorString(i18n("Unable to read the group list file"));
+}
+
+
+void KNNntpClient::doFetchGroups()
+{
+ KNGroupListData *target = static_cast<KNGroupListData *>(job->data());
+
+ sendSignal(TSdownloadGrouplist);
+ errorPrefix = i18n("The group list could not be retrieved.\nThe following error occurred:\n");
+
+ progressValue = 100;
+ predictedLines = 30000; // rule of thumb ;-)
+
+ if (!sendCommandWCheck("LIST",215)) // 215 list of newsgroups follows
+ return;
+
+ char *s, *line;
+ QString name;
+ KNGroup::Status status;
+ bool subscribed;
+
+ while (getNextLine()) {
+ line = getCurrentLine();
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double period into one
+ else
+ if (line[1]==0)
+ break; // message complete
+ }
+ s = strchr(line,' ');
+ if(!s) {
+#ifndef NDEBUG
+ qDebug("knode: retrieved broken group-line - ignoring");
+#endif
+ } else {
+ s[0] = 0; // cut string
+
+ name = QString::fromUtf8(line);
+
+ if (target->subscribed.contains(name)) {
+ target->subscribed.remove(name); // group names are unique, we wont find it again anyway...
+ subscribed = true;
+ } else
+ subscribed = false;
+
+ while (s[1]!=0) s++; // the last character determines the moderation status
+ switch (s[0]) {
+ case 'n' : status = KNGroup::readOnly;
+ break;
+ case 'y' : status = KNGroup::postingAllowed;
+ break;
+ case 'm' : status = KNGroup::moderated;
+ break;
+ default : status = KNGroup::unknown;
+ }
+
+ target->groups->append(new KNGroupInfo(name,QString::null,false,subscribed,status));
+ }
+ doneLines++;
+ }
+
+ if (!job->success() || job->canceled())
+ return; // stopped...
+
+ QSortedVector<KNGroupInfo> tempVector;
+ target->groups->toVector(&tempVector);
+ tempVector.sort();
+
+ if (target->getDescriptions) {
+ errorPrefix = i18n("The group descriptions could not be retrieved.\nThe following error occurred:\n");
+ progressValue = 100;
+ doneLines = 0;
+ predictedLines = target->groups->count();
+
+ sendSignal(TSdownloadDesc);
+ sendSignal(TSprogressUpdate);
+
+ int rep;
+ if (!sendCommand("LIST NEWSGROUPS",rep))
+ return;
+
+ if (rep == 215) { // 215 informations follows
+ QString description;
+ KNGroupInfo info;
+ int pos;
+
+ while (getNextLine()) {
+ line = getCurrentLine();
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double period into one
+ else
+ if (line[1]==0)
+ break; // message complete
+ }
+ s = line;
+ while (*s != '\0' && *s != '\t' && *s != ' ') s++;
+ if (*s == '\0') {
+#ifndef NDEBUG
+ qDebug("knode: retrieved broken group-description - ignoring");
+#endif
+ } else {
+ s[0] = 0; // terminate groupname
+ s++;
+ while (*s == ' ' || *s == '\t') s++; // go on to the description
+
+ name = QString::fromUtf8(line);
+ if (target->codecForDescriptions) // some countries use local 8 bit characters in the tag line
+ description = target->codecForDescriptions->toUnicode(s);
+ else
+ description = QString::fromLocal8Bit(s);
+ info.name = name;
+
+ if ((pos=tempVector.bsearch(&info))!=-1)
+ tempVector[pos]->description = description;
+ }
+ doneLines++;
+ }
+ }
+
+ if (!job->success() || job->canceled())
+ return; // stopped...
+ }
+
+ target->groups->setAutoDelete(false);
+ tempVector.toList(target->groups);
+ target->groups->setAutoDelete(true);
+
+ sendSignal(TSwriteGrouplist);
+ if (!target->writeOut())
+ job->setErrorString(i18n("Unable to write the group list file"));
+
+}
+
+
+void KNNntpClient::doCheckNewGroups()
+{
+ KNGroupListData *target = static_cast<KNGroupListData *>(job->data());
+
+ sendSignal(TSdownloadNewGroups);
+ errorPrefix = i18n("New groups could not be retrieved.\nThe following error occurred:\n");
+
+ progressValue = 100;
+ predictedLines = 30; // rule of thumb ;-)
+
+ QCString cmd;
+ cmd.sprintf("NEWGROUPS %.2d%.2d%.2d 000000",target->fetchSince.year()%100,target->fetchSince.month(),target->fetchSince.day());
+ if (!sendCommandWCheck(cmd,231)) // 231 list of new newsgroups follows
+ return;
+
+ char *s, *line;
+ QString name;
+ KNGroup::Status status;
+ QSortedList<KNGroupInfo> tmpList;
+ tmpList.setAutoDelete(true);
+
+ while (getNextLine()) {
+ line = getCurrentLine();
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double period into one
+ else
+ if (line[1]==0)
+ break; // message complete
+ }
+ s = strchr(line,' ');
+ if(!s) {
+#ifndef NDEBUG
+ qDebug("knode: retrieved broken group-line - ignoring");
+#endif
+ } else {
+ s[0] = 0; // cut string
+ name = QString::fromUtf8(line);
+
+ while (s[1]!=0) s++; // the last character determines the moderation status
+ switch (s[0]) {
+ case 'n' : status = KNGroup::readOnly;
+ break;
+ case 'y' : status = KNGroup::postingAllowed;
+ break;
+ case 'm' : status = KNGroup::moderated;
+ break;
+ default : status = KNGroup::unknown;
+ }
+
+ tmpList.append(new KNGroupInfo(name,QString::null,true,false,status));
+ }
+ doneLines++;
+ }
+
+ if (!job->success() || job->canceled())
+ return; // stopped...
+
+ if (target->getDescriptions) {
+ errorPrefix = i18n("The group descriptions could not be retrieved.\nThe following error occurred:\n");
+ progressValue = 100;
+ doneLines = 0;
+ predictedLines = tmpList.count()*3;
+
+ sendSignal(TSdownloadDesc);
+ sendSignal(TSprogressUpdate);
+
+ cmd = "LIST NEWSGROUPS ";
+ QStrList desList;
+ char *s;
+ int rep;
+
+ for (KNGroupInfo *group=tmpList.first(); group; group=tmpList.next()) {
+ if (!sendCommand(cmd+group->name.utf8(),rep))
+ return;
+ if (rep != 215) // 215 informations follows
+ break;
+ desList.clear();
+ if (!getMsg(desList))
+ return;
+
+ if (desList.count()>0) { // group has a description
+ s = desList.first();
+ while (*s !=- '\0' && *s != '\t' && *s != ' ') s++;
+ if (*s == '\0') {
+#ifndef NDEBUG
+ qDebug("knode: retrieved broken group-description - ignoring");
+#endif
+ } else {
+ while (*s == ' ' || *s == '\t') s++; // go on to the description
+ if (target->codecForDescriptions) // some countries use local 8 bit characters in the tag line
+ group->description = target->codecForDescriptions->toUnicode(s);
+ else
+ group->description = QString::fromLocal8Bit(s);
+ }
+ }
+ }
+ }
+
+ sendSignal(TSloadGrouplist);
+
+ if (!target->readIn()) {
+ job->setErrorString(i18n("Unable to read the group list file"));
+ return;
+ }
+ target->merge(&tmpList);
+ sendSignal(TSwriteGrouplist);
+ if (!target->writeOut()) {
+ job->setErrorString(i18n("Unable to write the group list file"));
+ return;
+ }
+}
+
+
+void KNNntpClient::doFetchNewHeaders()
+{
+ KNGroup* target=static_cast<KNGroup*>(job->data());
+ char* s;
+ int first=0, last=0, oldlast=0, toFetch=0, rep=0;
+ QCString cmd;
+
+ target->setLastFetchCount(0);
+
+ sendSignal(TSdownloadNew);
+ errorPrefix=i18n("No new articles could be retrieved for\n%1/%2.\nThe following error occurred:\n")
+ .arg(account.server()).arg(target->groupname());
+
+ cmd="GROUP ";
+ cmd+=target->groupname().utf8();
+ if (!sendCommandWCheck(cmd,211)) { // 211 n f l s group selected
+ return;
+ }
+
+ currentGroup = target->groupname();
+
+ progressValue = 90;
+
+ s = strchr(getCurrentLine(),' ');
+ if (s) {
+ s++;
+ s = strchr(s,' ');
+ }
+ if (s) {
+ s++;
+ first=atoi(s);
+ target->setFirstNr(first);
+ s = strchr(s,' ');
+ }
+ if (s) {
+ last=atoi(s);
+ } else {
+ QString tmp=i18n("No new articles could be retrieved.\nThe server sent a malformatted response:\n");
+ tmp+=getCurrentLine();
+ job->setErrorString(tmp);
+ closeConnection();
+ return;
+ }
+
+ if(target->lastNr()==0) { //first fetch
+ if(first>0)
+ oldlast=first-1;
+ else
+ oldlast=first;
+ } else
+ oldlast=target->lastNr();
+
+ toFetch=last-oldlast;
+ //qDebug("knode: last %d oldlast %d toFetch %d\n",last,oldlast,toFetch);
+
+ if(toFetch<=0) {
+ //qDebug("knode: No new Articles in group\n");
+ target->setLastNr(last); // don't get stuck when the article numbers wrap
+ return;
+ }
+
+ if(toFetch>target->maxFetch()) {
+ toFetch=target->maxFetch();
+ //qDebug("knode: Fetching only %d articles\n",toFetch);
+ }
+
+ progressValue = 100;
+ predictedLines = toFetch;
+
+ // get list of additional headers provided by the XOVER command
+ // see RFC 2980 section 2.1.7
+ QStrList headerformat;
+ cmd = "LIST OVERVIEW.FMT";
+ if ( sendCommand( cmd, rep ) && rep == 215 ) {
+ QStrList tmp;
+ if (getMsg(tmp)) {
+ for(QCString s = tmp.first(); s; s = tmp.next()) {
+ s = s.stripWhiteSpace();
+ // remove the mandatory xover header
+ if (s == "Subject:" || s == "From:" || s == "Date:" || s == "Message-ID:"
+ || s == "References:" || s == "Bytes:" || s == "Lines:")
+ continue;
+ else
+ headerformat.append(s);
+ }
+ }
+ }
+
+ //qDebug("knode: KNNntpClient::doFetchNewHeaders() : xover %d-%d", last-toFetch+1, last);
+ cmd.sprintf("xover %d-%d",last-toFetch+1,last);
+ if (!sendCommand(cmd,rep))
+ return;
+
+ // no articles in selected range...
+ if (rep==420) { // 420 No article(s) selected
+ target->setLastNr(last);
+ return;
+ } else if (rep!=224) { // 224 success
+ handleErrors();
+ return;
+ }
+
+ QStrList headers;
+ if (!getMsg(headers)) {
+ return;
+ }
+
+ progressValue = 1000;
+ sendSignal(TSprogressUpdate);
+
+ sendSignal(TSsortNew);
+
+ mutex.lock();
+ target->insortNewHeaders(&headers, &headerformat, this);
+ target->setLastNr(last);
+ mutex.unlock();
+}
+
+
+void KNNntpClient::doFetchArticle()
+{
+ KNRemoteArticle *target = static_cast<KNRemoteArticle*>(job->data());
+ QCString cmd;
+
+ sendSignal(TSdownloadArticle);
+ errorPrefix = i18n("Article could not be retrieved.\nThe following error occurred:\n");
+
+ progressValue = 100;
+ predictedLines = target->lines()->numberOfLines()+10;
+
+ if (target->collection()) {
+ QString groupName = static_cast<KNGroup*>(target->collection())->groupname();
+ if (currentGroup != groupName) {
+ cmd="GROUP ";
+ cmd+=groupName.utf8();
+ if (!sendCommandWCheck(cmd,211)) // 211 n f l s group selected
+ return;
+ currentGroup = groupName;
+ }
+ }
+
+ if (target->articleNumber() != -1) {
+ cmd.setNum(target->articleNumber());
+ cmd.prepend("ARTICLE ");
+ } else {
+ cmd = "ARTICLE " + target->messageID()->as7BitString(false);
+ }
+
+ if (!sendCommandWCheck(cmd,220)) { // 220 n <a> article retrieved - head and body follow
+ int code = atoi(getCurrentLine());
+ if ( code == 430 || code == 423 ) { // 430 no such article found || 423 no such article number in this group
+ QString msgId = target->messageID()->as7BitString( false );
+ // strip of '<' and '>'
+ msgId = msgId.mid( 1, msgId.length() - 2 );
+ job->setErrorString( errorPrefix + getCurrentLine() +
+ i18n("<br><br>The article you requested is not available on your news server."
+ "<br>You could try to get it from <a href=\"http://groups.google.com/groups?selm=%1\">groups.google.com</a>.")
+ .arg( msgId ) );
+ }
+ return;
+ }
+
+ QStrList msg;
+ if (!getMsg(msg))
+ return;
+
+ progressValue = 1000;
+ sendSignal(TSprogressUpdate);
+
+ target->setContent(&msg);
+ target->parse();
+}
+
+
+void KNNntpClient::doPostArticle()
+{
+ KNLocalArticle *art=static_cast<KNLocalArticle*>(job->data());
+
+ sendSignal(TSsendArticle);
+
+ if (art->messageID(false)!=0) {
+ int rep;
+ if (!sendCommand(QCString("STAT ")+art->messageID(false)->as7BitString(false),rep))
+ return;
+
+ if (rep==223) { // 223 n <a> article retrieved - request text separately
+ #ifndef NDEBUG
+ qDebug("knode: STAT successful, we have probably already sent this article.");
+ #endif
+ return; // the article is already on the server, lets put it silently into the send folder
+ }
+ }
+
+ if(!sendCommandWCheck("POST", 340)) // 340 send article to be posted. End with <CR-LF>.<CR-LF>
+ return;
+
+ if (art->messageID(false)==0) { // article has no message ID => search for a ID in the response
+ QCString s = getCurrentLine();
+ int start = s.findRev(QRegExp("<[^\\s]*@[^\\s]*>"));
+ if (start != -1) { // post response includes a recommended id
+ int end = s.find('>',start);
+ art->messageID()->from7BitString(s.mid(start,end-start+1));
+ art->assemble();
+ #ifndef NDEBUG
+ qDebug("knode: using the message-id recommended by the server: %s",s.mid(start,end-start+1).data());
+ #endif
+ }
+ }
+
+ if (!sendMsg(art->encodedContent(true)))
+ return;
+
+ if (!checkNextResponse(240)) // 240 article posted ok
+ return;
+}
+
+
+void KNNntpClient::doFetchSource()
+{
+ KNRemoteArticle *target = static_cast<KNRemoteArticle*>(job->data());
+
+ sendSignal(TSdownloadArticle);
+ errorPrefix = i18n("Article could not be retrieved.\nThe following error occurred:\n");
+
+ progressValue = 100;
+ predictedLines = target->lines()->numberOfLines()+10;
+
+ QCString cmd = "ARTICLE " + target->messageID()->as7BitString(false);
+ if (!sendCommandWCheck(cmd,220)) // 220 n <a> article retrieved - head and body follow
+ return;
+
+ QStrList msg;
+ if (!getMsg(msg))
+ return;
+
+ progressValue = 1000;
+ sendSignal(TSprogressUpdate);
+
+ target->setContent(&msg);
+}
+
+
+bool KNNntpClient::openConnection()
+{
+ currentGroup = QString::null;
+
+ QString oldPrefix = errorPrefix;
+ errorPrefix=i18n("Unable to connect.\nThe following error occurred:\n");
+
+ if (!KNProtocolClient::openConnection())
+ return false;
+
+ progressValue = 30;
+
+ int rep;
+ if (!getNextResponse(rep))
+ return false;
+
+ if ( ( rep < 200 ) || ( rep > 299 ) ) { // RFC977: 2xx - Command ok
+ handleErrors();
+ return false;
+ }
+
+ progressValue = 50;
+
+ if (!sendCommand("MODE READER",rep))
+ return false;
+
+ if (rep==500) {
+#ifndef NDEBUG
+ qDebug("knode: \"MODE READER\" command not recognized.");
+#endif
+ } else
+ if ( ( rep < 200 ) || ( rep > 299 ) ) { // RFC977: 2xx - Command ok
+ handleErrors();
+ return false;
+ }
+
+ progressValue = 60;
+
+ // logon now, some newsserver send a incomplete group list otherwise
+ if (account.needsLogon() && !account.user().isEmpty()) {
+ //qDebug("knode: user: %s",account.user().latin1());
+
+ QCString command = "AUTHINFO USER ";
+ command += account.user().local8Bit();
+ if (!KNProtocolClient::sendCommand(command,rep))
+ return false;
+
+ if (rep==381) { // 381 PASS required
+ //qDebug("knode: Password required");
+
+ if (!account.pass().length()) {
+ job->setErrorString(i18n("Authentication failed.\nCheck your username and password."));
+ job->setAuthError(true);
+ return false;
+ }
+
+ //qDebug("knode: pass: %s",account.pass().latin1());
+
+ command = "AUTHINFO PASS ";
+ command += account.pass().local8Bit();
+ if (!KNProtocolClient::sendCommand(command,rep))
+ return false;
+
+ if (rep==281) { // 281 authorization success
+ #ifndef NDEBUG
+ qDebug("knode: Authorization successful");
+ #endif
+ } else {
+ #ifndef NDEBUG
+ qDebug("knode: Authorization failed");
+ #endif
+ job->setErrorString(i18n("Authentication failed.\nCheck your username and password.\n\n%1").arg(getCurrentLine()));
+ job->setAuthError(true);
+ closeConnection();
+ return false;
+ }
+ } else {
+ if (rep==281) { // 281 authorization success
+ #ifndef NDEBUG
+ qDebug("knode: Authorization successful");
+ #endif
+ } else {
+ if ((rep==482)||(rep==500)) { //482 Authentication rejected
+ #ifndef NDEBUG
+ qDebug("knode: Authorization failed"); // we don't care, the server can refuse the info
+ #endif
+ } else {
+ handleErrors();
+ return false;
+ }
+ }
+ }
+ }
+
+ progressValue = 70;
+
+ errorPrefix = oldPrefix;
+ return true;
+}
+
+
+// authentication on demand
+bool KNNntpClient::sendCommand(const QCString &cmd, int &rep)
+{
+ if (!KNProtocolClient::sendCommand(cmd,rep))
+ return false;
+
+ if (rep==480) { // 480 requesting authorization
+ //qDebug("knode: Authorization requested");
+
+ if (!account.user().length()) {
+ job->setErrorString(i18n("Authentication failed.\nCheck your username and password."));
+ job->setAuthError(true);
+ closeConnection();
+ return false;
+ }
+
+ //qDebug("knode: user: %s",account.user().data());
+
+ QCString command = "AUTHINFO USER ";
+ command += account.user().local8Bit();
+ if (!KNProtocolClient::sendCommand(command,rep))
+ return false;
+
+ if (rep==381) { // 381 PASS required
+ //qDebug("knode: Password required");
+
+ if (!account.pass().length()) {
+ job->setErrorString(i18n("Authentication failed.\nCheck your username and password.\n\n%1").arg(getCurrentLine()));
+ job->setAuthError(true);
+ closeConnection();
+ return false;
+ }
+
+ //qDebug("knode: pass: %s",account.pass().data());
+
+ command = "AUTHINFO PASS ";
+ command += account.pass().local8Bit();
+ if (!KNProtocolClient::sendCommand(command,rep))
+ return false;
+ }
+
+ if (rep==281) { // 281 authorization success
+ #ifndef NDEBUG
+ qDebug("knode: Authorization successful");
+ #endif
+ if (!KNProtocolClient::sendCommand(cmd,rep)) // retry the original command
+ return false;
+ } else {
+ job->setErrorString(i18n("Authentication failed.\nCheck your username and password.\n\n%1").arg(getCurrentLine()));
+ job->setAuthError(true);
+ closeConnection();
+ return false;
+ }
+ }
+ return true;
+}
+
+
+void KNNntpClient::handleErrors()
+{
+ if (errorPrefix.isEmpty())
+ job->setErrorString(i18n("An error occurred:\n%1").arg(getCurrentLine()));
+ else
+ job->setErrorString(errorPrefix + getCurrentLine());
+
+ int code = atoi(getCurrentLine());
+
+ // close the connection only when necessary:
+ // 430 no such article found
+ // 411 no such news group
+ // 423 no such article number in this group
+ if ((code != 430)&&(code != 411)&&(code != 423))
+ closeConnection();
+}
+
+
+//--------------------------------
+
diff --git a/knode/knnntpclient.h b/knode/knnntpclient.h
new file mode 100644
index 000000000..274c12271
--- /dev/null
+++ b/knode/knnntpclient.h
@@ -0,0 +1,57 @@
+/*
+ knnntpclient.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNNNTPCLIENT_H
+#define KNNNTPCLIENT_H
+
+#include <qmutex.h>
+
+#include <knprotocolclient.h>
+
+
+class KNNntpClient : public KNProtocolClient {
+
+ public:
+
+ KNNntpClient(int NfdPipeIn, int NfdPipeOut, QMutex& nntpMutex);
+ ~KNNntpClient();
+
+ protected:
+
+ /** examines the job and calls the suitable handling method */
+ virtual void processJob();
+
+ void doLoadGroups();
+ void doFetchGroups();
+ void doCheckNewGroups();
+ void doFetchNewHeaders();
+ void doFetchArticle();
+ void doPostArticle();
+ void doFetchSource();
+
+ /** connect, handshake */
+ virtual bool openConnection();
+ /** authentication on demand */
+ virtual bool sendCommand(const QCString &cmd, int &rep);
+ virtual void handleErrors();
+ bool switchToGroup(const QString &newGroup);
+
+ QString currentGroup;
+ QMutex& mutex;
+
+};
+
+#endif
diff --git a/knode/knode.cpp b/knode/knode.cpp
new file mode 100644
index 000000000..0dd4e6a38
--- /dev/null
+++ b/knode/knode.cpp
@@ -0,0 +1,122 @@
+/*
+ knode.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+#include "knode.h"
+#include "knglobals.h"
+#include "knwidgets.h"
+
+#include <kkeydialog.h>
+#include <kedittoolbar.h>
+#include <kstdaction.h>
+#include <kdebug.h>
+#include <kmenubar.h>
+#include <kiconloader.h>
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <kapplication.h>
+
+#include "broadcaststatus.h"
+#include "krsqueezedtextlabel.h"
+#include "progressdialog.h"
+#include "statusbarprogresswidget.h"
+
+//GUI
+#include "knmainwidget.h"
+#include "knarticlewindow.h"
+#include "kncollectionviewitem.h"
+#include "knhdrviewitem.h"
+
+KNMainWindow::KNMainWindow( QWidget* pWidget )
+ : KMainWindow(pWidget,"mainWindow")
+{
+ //setupStatusBar();
+ createStandardStatusBarAction();
+ setStandardToolBarMenuEnabled(true);
+
+ //config stuff
+ KStdAction::quit(kapp, SLOT(closeAllWindows()), actionCollection());
+ KStdAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection());
+ KStdAction::keyBindings(this, SLOT(slotConfKeys()), actionCollection());
+
+ m_mainWidget = new KNMainWidget( this, true, this, 0 );
+ connect( m_mainWidget, SIGNAL(signalCaptionChangeRequest(const QString&)),
+ SLOT( setCaption(const QString&)) );
+ setCentralWidget( m_mainWidget );
+ setupStatusBar();
+ connect( KPIM::BroadcastStatus::instance(), SIGNAL(statusMsg(const QString&)),
+ this, SLOT(slotShowStatusMsg(const QString& )) );
+ createGUI( "knodeui.rc" );
+ knGlobals.instance = 0;
+
+ applyMainWindowSettings(KGlobal::config(),"mainWindow_options");
+}
+
+KNMainWindow::~KNMainWindow()
+{
+ saveMainWindowSettings(knGlobals.config(),"mainWindow_options");
+}
+
+void KNMainWindow::openURL( const KURL& url )
+{
+ m_mainWidget->openURL( url );
+}
+
+void KNMainWindow::slotConfToolbar()
+{
+ saveMainWindowSettings(knGlobals.config(),"mainWindow_options");
+ KEditToolbar dlg(actionCollection(), "knodeui.rc");
+ connect(&dlg,SIGNAL( newToolbarConfig() ), this, SLOT( slotNewToolbarConfig() ));
+ dlg.exec();
+}
+
+void KNMainWindow::slotNewToolbarConfig()
+{
+ createGUI("knodeui.rc");
+ //initPopups();
+ applyMainWindowSettings(knGlobals.config(),"mainWindow_options");
+}
+
+void KNMainWindow::slotConfKeys()
+{
+ KKeyDialog::configure(actionCollection(), true);
+}
+
+bool KNMainWindow::queryClose()
+{
+ return m_mainWidget->queryClose();
+}
+
+void KNMainWindow::setupStatusBar()
+{
+ mProgressDialog = new KPIM::ProgressDialog( statusBar(), this );
+ mProgressDialog->hide();
+
+ mLittleProgress = new StatusbarProgressWidget( mProgressDialog, statusBar() );
+ mLittleProgress->show();
+
+ statusBar()->addWidget( mLittleProgress, 0 , true );
+
+ mStatusMsgLabel = new KRSqueezedTextLabel( QString::null, statusBar() );
+ mStatusMsgLabel->setAlignment( AlignLeft | AlignVCenter );
+ statusBar()->addWidget( mStatusMsgLabel, 2 );
+ statusBar()->addWidget(m_mainWidget->statusBarLabelFilter(), 2);
+ statusBar()->addWidget(m_mainWidget->statusBarLabelGroup(), 3);
+}
+
+void KNMainWindow::slotShowStatusMsg( const QString &msg ) {
+ mStatusMsgLabel->setText( msg );
+}
+
+#include "knode.moc"
diff --git a/knode/knode.h b/knode/knode.h
new file mode 100644
index 000000000..8b719a5d8
--- /dev/null
+++ b/knode/knode.h
@@ -0,0 +1,70 @@
+/*
+ knode.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNODE_H
+#define KNODE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kmainwindow.h>
+#include <kdialogbase.h>
+#include "resource.h"
+
+#include <qglobal.h>
+
+class KURL;
+
+namespace KPIM {
+ class StatusbarProgressWidget;
+ class ProgressDialog;
+}
+using KPIM::StatusbarProgressWidget;
+using KPIM::ProgressDialog;
+class KRSqueezedTextLabel;
+
+class KNMainWidget;
+class KNHeaderView;
+
+
+class KNMainWindow : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ KNMainWindow( QWidget* parentWidget=0 );
+ ~KNMainWindow();
+ void openURL( const KURL& );
+ KNMainWidget *mainWidget() { return m_mainWidget; }
+
+public slots:
+ void slotConfToolbar();
+ void slotNewToolbarConfig();
+ void slotConfKeys();
+protected:
+ bool queryClose();
+private:
+ void setupStatusBar();
+ KNMainWidget *m_mainWidget;
+ StatusbarProgressWidget *mLittleProgress;
+ ProgressDialog *mProgressDialog;
+ KRSqueezedTextLabel *mStatusMsgLabel;
+private slots:
+ void slotShowStatusMsg( const QString& );
+};
+
+#endif // KNODE_H
diff --git a/knode/knode_config_accounts.desktop b/knode/knode_config_accounts.desktop
new file mode 100644
index 000000000..9d0716f64
--- /dev/null
+++ b/knode/knode_config_accounts.desktop
@@ -0,0 +1,118 @@
+[Desktop Entry]
+Icon=network
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=knode
+X-KDE-FactoryName=knode_config_accounts
+X-KDE-HasReadOnlyMode=false
+X-KDE-ParentApp=knode
+X-KDE-ParentComponents=knode,kontact_knodeplugin
+X-KDE-CfgDlgHierarchy=KNode
+
+Name=Accounts
+Name[af]=Rekeninge
+Name[ar]=الحسابات
+Name[be]=Рахункі
+Name[bg]=Сметки
+Name[br]=Kontoù
+Name[bs]=Računi
+Name[ca]=Comptes
+Name[cs]=Účty
+Name[cy]=Cyfrifon
+Name[da]=Konti
+Name[de]=Zugänge
+Name[el]=Λογαριασμοί
+Name[eo]=Kontoj
+Name[es]=Cuentas
+Name[et]=Kontod
+Name[eu]=Kontuak
+Name[fa]=حسابها
+Name[fi]=Tilit
+Name[fr]=Comptes
+Name[fy]=Akkounts
+Name[ga]=Cuntais
+Name[gl]=Contas
+Name[he]=חשבונות
+Name[hu]=Fiókok
+Name[is]=Tengingar
+Name[it]=Account
+Name[ja]=アカウント
+Name[ka]=ანგარიშები
+Name[kk]=Тіркелгілері
+Name[km]=គណនី
+Name[lt]=Paskyros
+Name[mk]=Сметки
+Name[ms]=Akaun
+Name[nb]=Kontoer
+Name[nds]=Kontos
+Name[ne]=खाता
+Name[nn]=Kontoar
+Name[pa]=ਖਾਤੇ
+Name[pl]=Konta
+Name[pt]=Contas
+Name[pt_BR]=Contas
+Name[ru]=Учётные записи
+Name[se]=Kontut
+Name[sk]=Účty
+Name[sl]=Računi
+Name[sr]=Налози
+Name[sr@Latn]=Nalozi
+Name[sv]=Konton
+Name[ta]=கணக்குகள்
+Name[tg]=Қайдҳои баҳисобгирӣ
+Name[tr]=Hesaplar
+Name[uk]=Рахунки
+Name[uz]=Hisoblar
+Name[uz@cyrillic]=Ҳисоблар
+Name[zh_CN]=账户
+Name[zh_TW]=帳號
+Comment=Setup for Newsgroup and Mail Servers
+Comment[af]=Opstel van nuus groep en e-pos bedieners
+Comment[bg]=Настройки на групите и пощенските сървъри
+Comment[bs]=Postavke za newsgrupe i mail servere
+Comment[ca]=Configuració dels grups de notícies i servidors de correu
+Comment[cs]=Nastavení diskuzní skupiny a poštovních serverů
+Comment[da]=Opsætning af nyhedsgruppe og e-mail-servere
+Comment[de]=Einstellungen für Newsgruppen- und Mailserver
+Comment[el]=Ρύθμιση για Ομάδες Συζήτησης και Διακομιστών Αλληλογραφίας
+Comment[es]=Configuración de los servidores de grupos de noticias y de correo
+Comment[et]=Uudiste- ja e-posti serverite seadistus
+Comment[eu]=Berri-talde eta posta zerbitzarien konfigurazioa
+Comment[fa]=برپایی برای کارسازهای نامه و Newsgroup
+Comment[fi]=Uutisryhmien ja sähköpostipalvelinten asetukset
+Comment[fr]=Configuration les serveurs de messagerie et de groupes de discussion
+Comment[fy]=Nijs- en posttsjinners opset
+Comment[gl]=Configuración para Servidores de Correo e Novas
+Comment[hu]=Hír- és levelezési kiszolgálók beállítása
+Comment[is]=Uppsetning fyrir fréttahópa og póstþjóna
+Comment[it]=Impostazioni per newsgroup e server di posta
+Comment[ja]=ニュースグループとメールサーバの設定
+Comment[ka]=სიახლეთა ჯგუფებისა და საფოსტო სერვერის კონფიგურაცია
+Comment[kk]=Жаңалық топтар мен Пошта серверлері
+Comment[km]=រៀបចំម៉ាស៊ីន​បម្រើ​វេទិកា​ព័ត៌មាន និង​សំបុត្រ
+Comment[lt]=Naujienų grupių ir pašto serverių nustatymai
+Comment[mk]=Поставување за сервери за групи вести и за е-пошта
+Comment[ms]=Setup untuk Kumpulan Berita dan Pelayan Mel
+Comment[nb]=Oppsett av njusgruppe- og e-post tjenere
+Comment[nds]=Narichtenkrink- und Nettpostservers instellen
+Comment[ne]=समाचार समूह र पत्र सर्भरका लागि सेटअप गर्नुहोस्
+Comment[nl]=Instellingen voor nieuws- en mailservers
+Comment[nn]=Oppsett av Usenet- og e-post-tenarar
+Comment[pl]=Ustawienia serwerów poczty i grup dyskusyjnych
+Comment[pt]=Configuração de Servidores de Notícias e de E-mail
+Comment[pt_BR]=Configura Grupos de Discussão e Servidores de E-mails
+Comment[ru]=Учётные записи на почтовых серверах и серверах телеконференций
+Comment[se]=Heivet Usenet- ja e-boastabálvváid
+Comment[sk]=Nastavenie serverov pre poštu a diskusné skupiny
+Comment[sl]=Nastavitve za novične in poštne strežnike
+Comment[sr]=Поставке за групе новости и сервере поште
+Comment[sr@Latn]=Postavke za grupe novosti i servere pošte
+Comment[sv]=Inställningar för diskussionsgrupp och postservrar
+Comment[ta]=செய்திக்குழு மற்றும் மின்னஞ்சல் சேவைகளுக்கான அமைப்பு
+Comment[tg]=Қайдҳои баҳисобгирӣ дар серверҳои почтавӣ ва серверҳои телеконференсия
+Comment[tr]=E-posta ve Haber Grubu Sunucuları için Yapılandırma
+Comment[uk]=Налаштування для груп новин і серверів пошти
+Comment[zh_CN]=新闻组和邮件服务器的设置
+Comment[zh_TW]=設定新聞群組及郵件伺服器
diff --git a/knode/knode_config_appearance.desktop b/knode/knode_config_appearance.desktop
new file mode 100644
index 000000000..bbd2ec26d
--- /dev/null
+++ b/knode/knode_config_appearance.desktop
@@ -0,0 +1,122 @@
+[Desktop Entry]
+Icon=looknfeel
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=knode
+X-KDE-FactoryName=knode_config_appearance
+X-KDE-HasReadOnlyMode=false
+X-KDE-ParentApp=knode
+X-KDE-ParentComponents=knode,kontact_knodeplugin
+X-KDE-CfgDlgHierarchy=KNode
+
+Name=Appearance
+Name[ar]=المظهر
+Name[be]=Знешні выгляд
+Name[bg]=Външен вид
+Name[br]=Neuziadur
+Name[bs]=Izgled
+Name[ca]=Aparença
+Name[cs]=Vzhled
+Name[cy]=Golwg
+Name[da]=Udseende
+Name[de]=Erscheinungsbild
+Name[el]=Εμφάνιση
+Name[eo]=Prezentado
+Name[es]=Apariencia
+Name[et]=Välimus
+Name[eu]=Itxura
+Name[fa]=ظاهر
+Name[fi]=Ulkoasu
+Name[fr]=Apparence
+Name[fy]=Uterlik
+Name[ga]=Cuma
+Name[gl]=Apariencia
+Name[he]=מראה
+Name[hr]=Izgled
+Name[hu]=Megjelenés
+Name[is]=Útlit
+Name[it]=Aspetto
+Name[ja]=外観
+Name[ka]=იერსახე
+Name[kk]=Сыртқы көрінісі
+Name[km]=រូបរាង
+Name[ko]=모양
+Name[lt]=Išvaizda
+Name[mk]=Изглед
+Name[ms]=Rupa
+Name[nb]=Utseende
+Name[nds]=Utsehn
+Name[ne]=मोहडा
+Name[nl]=Uiterlijk
+Name[nn]=Utsjånad
+Name[pl]=Wygląd
+Name[pt]=Aparência
+Name[pt_BR]=Aparência
+Name[ru]=Внешний вид
+Name[rw]=Imigaragarire
+Name[se]=Fárda
+Name[sk]=Vzhľad
+Name[sl]=Videz
+Name[sr]=Изглед
+Name[sr@Latn]=Izgled
+Name[sv]=Uppträdande
+Name[ta]=தோற்றம்
+Name[tg]=Намуди зоҳирӣ
+Name[tr]=Görünüm
+Name[uk]=Вигляд
+Name[uz]=Koʻrinishi
+Name[uz@cyrillic]=Кўриниши
+Name[zh_CN]=外观
+Comment=Customize Visual Appearance
+Comment[af]=Pasmaak die visuele voorkoms
+Comment[bg]=Настройки на външния вид
+Comment[bs]=Prilagodi izgled
+Comment[ca]=Personalitza l'aparença visual
+Comment[cs]=Nastavení vzhledu
+Comment[cy]=Addasu Golwg
+Comment[da]=Brugerindstil visuelt udseende
+Comment[de]=Erscheinungsbild anpassen
+Comment[el]=Προσαρμογή εμφάνισης
+Comment[en_GB]=Customise Visual Appearance
+Comment[es]=Apariencia visual personalizada
+Comment[et]=Välimuse seadistused
+Comment[eu]=Pertsonalizatu itxura
+Comment[fa]=سفارشی کردن ظاهر تصویری
+Comment[fi]=Ulkonäköasetukset
+Comment[fr]=Personnalisation de l'apparence
+Comment[fy]=It uterlik oanpasse
+Comment[gl]=Personalizar a apariencia visual
+Comment[he]=הגדרת מראה
+Comment[hu]=A grafikai megjelenés testreszabása
+Comment[is]=Stilla útlit
+Comment[it]=Personalizza l'aspetto
+Comment[ja]=外観をカスタマイズ
+Comment[ka]=ვიზუალური იერსახის დაყენება
+Comment[kk]=Сыртқы көрінісін ыңғайлау
+Comment[km]=ប្ដូរ​រូបរាង​មើល​ឃើញ​តាម​បំណង
+Comment[lt]=Derinti vizualinę išvaizdą
+Comment[mk]=Приспособете ја визуелната појава
+Comment[ms]=Suaikan Rupa Visual
+Comment[nb]=Tilpass visuelt utseende
+Comment[nds]=Utsehn topassen
+Comment[ne]=दृश्यात्मक मोहडा अनुकूल गर्नुहोस्
+Comment[nl]=Pas het uiterlijk aan
+Comment[nn]=Tilpass utsjånad
+Comment[pl]=Zmiana wyglądu
+Comment[pt]=Personalizar a Aparência Visual
+Comment[pt_BR]=Personalizar Aparência Visual
+Comment[ru]=Настройка внешнего вида
+Comment[se]=Heivet fárdda
+Comment[sk]=Prispôsobenie vzhľadu
+Comment[sl]=Prilagodi videz
+Comment[sr]=Прилагодите визуелни приказ
+Comment[sr@Latn]=Prilagodite vizuelni prikaz
+Comment[sv]=Anpassa visuellt uppträdande
+Comment[ta]=பார்க்கும் தோற்றத்தை தனிபயனாக்கு
+Comment[tg]=Танзимоти намуди зоҳирӣ
+Comment[tr]=Görsel Görünümü Özelleştir
+Comment[uk]=Налаштування зовнішнього вигляду
+Comment[zh_CN]=自定义视觉外观
+Comment[zh_TW]=調整視覺顯示
diff --git a/knode/knode_config_cleanup.desktop b/knode/knode_config_cleanup.desktop
new file mode 100644
index 000000000..5f08bbe29
--- /dev/null
+++ b/knode/knode_config_cleanup.desktop
@@ -0,0 +1,113 @@
+[Desktop Entry]
+Icon=wizard
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=knode
+X-KDE-FactoryName=knode_config_cleanup
+X-KDE-HasReadOnlyMode=false
+X-KDE-ParentApp=knode
+X-KDE-ParentComponents=knode,kontact_knodeplugin
+X-KDE-CfgDlgHierarchy=KNode
+
+Name=Cleanup
+Name[ar]=التنظيف
+Name[bg]=Изчистване
+Name[bs]=Čišćenje
+Name[ca]=Neteja
+Name[cs]=Vyčištění
+Name[da]=Oprydning
+Name[de]=Aufräumen
+Name[el]=Καθαρισμός
+Name[eo]=Purigo
+Name[es]=Limpiar
+Name[et]=Puhastamine
+Name[eu]=Garbiketa
+Name[fa]=پاک‌سازی
+Name[fi]=Siivoaminen
+Name[fr]=Nettoyage
+Name[fy]=Opskjinning
+Name[ga]=Glan
+Name[gl]=Limpeza
+Name[he]=ניקיון
+Name[hu]=Tisztítás
+Name[is]=Hreinsun
+Name[it]=Pulizia
+Name[ja]=整理
+Name[ka]=გაწმენდა
+Name[kk]=Тазалау
+Name[km]=សម្អាត
+Name[lt]=Išvalymas
+Name[mk]=Расчистување
+Name[ms]=Pembersihan
+Name[nb]=Rydd opp
+Name[nds]=Oprümen
+Name[ne]=हटाउनुहोस्
+Name[nl]=Opschoning
+Name[nn]=Rydd opp
+Name[pl]=Porządkowanie
+Name[pt]=Limpeza
+Name[pt_BR]=Limpeza
+Name[ru]=Очистка диска
+Name[se]=Čorge
+Name[sk]=Vyčistenie
+Name[sl]=Čiščenje
+Name[sr]=Чишћење
+Name[sr@Latn]=Čišćenje
+Name[sv]=Upprensning
+Name[ta]=சுத்தம் செய்
+Name[tg]=Тозакунии диск
+Name[tr]=Temizle
+Name[uk]=Очищення диску
+Name[zh_CN]=清理
+Name[zh_TW]=清除
+Comment=Preserving Disk Space
+Comment[af]=Bespaar Skyf Spasie
+Comment[bg]=Освобождаване на свободно място на диска
+Comment[bs]=Čuvanje prostora na disku
+Comment[ca]=Preservar l'espai a disc
+Comment[cs]=Zachování místa na disku
+Comment[da]=Bevarer diskplads
+Comment[de]=Plattenplatz bewahren
+Comment[el]=Διαφύλαξη χώρου στο Δίσκο
+Comment[eo]=Ŝpari diskspacon
+Comment[es]=Preservar el espacio de disco
+Comment[et]=Kettaruumi eest hoolitsemine
+Comment[eu]=Diskaren espazioa mantendu
+Comment[fa]=حفظ فضای دیسک
+Comment[fi]=Kiintolevytilan säästäminen
+Comment[fr]=Préservation de l'espace disque
+Comment[fy]=Besparring fan skiifromte
+Comment[gl]=Preservar Espacio en Disco
+Comment[he]=שמירת מקום בכונן
+Comment[hu]=A lemezterület megőrzése
+Comment[is]=Varðveita diskpláss
+Comment[it]=Risparmia lo spazio su disco
+Comment[ja]=ディスクスペースを維持
+Comment[ka]=სივრცის შენახვა დისკზე
+Comment[kk]=Дискідегі орынды үнемдеу
+Comment[km]=បង្ការ​ទំហំ​ថាស
+Comment[lt]=Disko erdvės išsaugojimas
+Comment[mk]=Зачувување простор на дискот
+Comment[ms]=Memelihara Ruang Cakera
+Comment[nb]=Bevarer disk plass
+Comment[nds]=Platz op de Fastplaat wohren
+Comment[ne]=चक्का खाली स्थान संचित गर्दैछ
+Comment[nl]=Besparing van schrijfruimte
+Comment[nn]=Frigjer diskplass
+Comment[pl]=Kontrolowanie użytej przestrzeni dyskowej
+Comment[pt]=Preservar o Espaço em Disco
+Comment[pt_BR]=Preservando o Espaço em Disco
+Comment[ru]=Сохранение места на диске
+Comment[sk]=Úspora miesta na disku
+Comment[sl]=Ohranjanje prostora na disku
+Comment[sr]=Чување простора на диску
+Comment[sr@Latn]=Čuvanje prostora na disku
+Comment[sv]=Bevara diskutrymme
+Comment[ta]=தட்டின் இடத்தை சேமித்தல்
+Comment[tg]=Нигоҳ доштани ҷое дар диск
+Comment[tr]=Saklama için Disk Alanı
+Comment[uk]=Збереження місця на диску
+Comment[zh_CN]=腾出磁盘空间
+Comment[zh_TW]=保留磁碟空間
diff --git a/knode/knode_config_identity.desktop b/knode/knode_config_identity.desktop
new file mode 100644
index 000000000..2fda0f69b
--- /dev/null
+++ b/knode/knode_config_identity.desktop
@@ -0,0 +1,124 @@
+[Desktop Entry]
+Icon=identity
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=knode
+X-KDE-FactoryName=knode_config_identity
+X-KDE-HasReadOnlyMode=false
+X-KDE-ParentApp=knode
+X-KDE-ParentComponents=knode,kontact_knodeplugin
+X-KDE-CfgDlgHierarchy=KNode
+
+Name=Identity
+Name[af]=Identiteit
+Name[ar]=الهوية
+Name[be]=Увасабленне
+Name[bg]=Самоличност
+Name[br]=Anvelezh
+Name[bs]=Identitet
+Name[ca]=Identitat
+Name[cy]=Dynodiad
+Name[da]=Identitet
+Name[de]=Identität
+Name[el]=Ταυτότητα
+Name[eo]=Idento
+Name[es]=Identidad
+Name[et]=Identiteet
+Name[eu]=Identitatea
+Name[fa]=هویت
+Name[fi]=Henkilöllisyys
+Name[fr]=Identité
+Name[fy]=Identiteit
+Name[ga]=Aitheantas
+Name[gl]=Identidade
+Name[he]=זהות
+Name[hu]=Azonosító
+Name[is]=Auðkenni
+Name[it]=Identità
+Name[ja]=個人情報
+Name[ka]=პროფილი
+Name[kk]=Іс-әлпеті
+Name[km]=អត្តសញ្ញាណ
+Name[lt]=Tapatybė
+Name[mk]=Идентитет
+Name[ms]=Identiti
+Name[nb]=Identitet
+Name[nds]=Persoon
+Name[ne]=पहिचान
+Name[nl]=Identiteit
+Name[nn]=Identitet
+Name[pl]=Tożsamość
+Name[pt]=Identidade
+Name[pt_BR]=Identidade
+Name[ru]=Профиль
+Name[se]=Identitehta
+Name[sk]=Identita
+Name[sl]=Identiteta
+Name[sr]=Идентитет
+Name[sr@Latn]=Identitet
+Name[sv]=Identitet
+Name[ta]=அடையாளம்
+Name[tg]=Профил
+Name[tr]=Kimlik
+Name[uk]=Профіль
+Name[uz]=Shaxsiyat
+Name[uz@cyrillic]=Шахсият
+Name[zh_CN]=身份
+Name[zh_TW]=身份
+Comment=Personal Information
+Comment[af]=Persoonlike Informasie
+Comment[be]=Пэрсанальная інфармацыя
+Comment[bg]=Управление на личната информация
+Comment[br]=Titouroù diouzhin
+Comment[bs]=Lične informacije
+Comment[ca]=Informació personal
+Comment[cs]=Osobní informace
+Comment[cy]=Gwybodaeth Bersonol
+Comment[da]=Personlig information
+Comment[de]=Persönliche Angaben
+Comment[el]=Προσωπικές πληροφορίες
+Comment[eo]=Personaj Informoj
+Comment[es]=Información personal
+Comment[et]=Personaalne info
+Comment[eu]=Informazio pertsonala
+Comment[fa]=اطلاعات شخصی
+Comment[fi]=Omat tiedot
+Comment[fr]=Informations personnelles
+Comment[fy]=Persoanlike ynformatie
+Comment[ga]=Eolas Pearsanta
+Comment[gl]=Información Persoal
+Comment[he]=מידע אישי
+Comment[hu]=Személyi adatok
+Comment[is]=Persónuupplýsingar
+Comment[it]=Informazioni personali
+Comment[ja]=個人の情報
+Comment[ka]=პირადი ინფორმაცია
+Comment[kk]=Дербес мәліметтер
+Comment[km]=ព័ត៌មាន​ផ្ទាល់​ខ្លួន
+Comment[lt]=Asmeninė informacija
+Comment[mk]=Лични информации
+Comment[ms]=Maklumat Peribadi
+Comment[nb]=Personlig informasjon
+Comment[nds]=Persöönlich Informatschonen
+Comment[ne]=व्यक्तिगत सूचना
+Comment[nl]=Persoonlijke informatie
+Comment[nn]=Personleg informasjon
+Comment[pa]=ਨਿੱਜੀ ਜਾਣਕਾਰੀ
+Comment[pl]=Informacje osobiste
+Comment[pt]=Informação Pessoal
+Comment[pt_BR]=Informações Pessoais
+Comment[ru]=Персональная информация
+Comment[se]=Peršuvnnalaš dieđut
+Comment[sk]=Osobné informácie
+Comment[sl]=Osebne informacije
+Comment[sr]=Лична информација
+Comment[sr@Latn]=Lična informacija
+Comment[sv]=Personlig information
+Comment[ta]=சொந்த தகவல்
+Comment[tg]=Маълумоти шахсӣ
+Comment[tr]=Kişisel Bilgi
+Comment[uk]=Особиста інформація
+Comment[zh_CN]=个人信息
+Comment[zh_TW]=個人資訊
diff --git a/knode/knode_config_post_news.desktop b/knode/knode_config_post_news.desktop
new file mode 100644
index 000000000..784b7c39c
--- /dev/null
+++ b/knode/knode_config_post_news.desktop
@@ -0,0 +1,62 @@
+[Desktop Entry]
+Icon=mail_forward
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=knode
+X-KDE-FactoryName=knode_config_post_news
+X-KDE-HasReadOnlyMode=false
+X-KDE-ParentApp=knode
+X-KDE-ParentComponents=knode,kontact_knodeplugin
+X-KDE-CfgDlgHierarchy=KNode
+
+Name=Posting News
+Name[af]=Pos Nuus
+Name[bg]=Изпращане
+Name[bs]=Slanje na Usenet
+Name[ca]=Enviament de notícies
+Name[cs]=Odesílání novinek
+Name[da]=Indsender nyheder
+Name[de]=Artikelversand
+Name[el]=Αποστολή νέων
+Name[es]=Enviar noticias
+Name[et]=Uudiste postitamine
+Name[eu]=Berrien argitaratzea
+Name[fa]=ارسال سریع اخبار
+Name[fi]=Viestien lähettäminen
+Name[fr]=Postage de nouvelles
+Name[fy]=Nijs poste
+Name[gl]=Publicando Noticias
+Name[he]=שליחת חדשות
+Name[hu]=Hírek írása
+Name[is]=Senda fréttir
+Name[it]=Invio news
+Name[ja]=ニュースを投稿
+Name[ka]=სიახლეების განთავსება
+Name[kk]=Жариялау
+Name[km]=ប្រកាស​ព័ត៌មាន​
+Name[lt]=Naujienų skelbimas
+Name[mk]=Испраќање вести
+Name[ms]=Menampal Berita
+Name[nb]=Legge inn Njus
+Name[nds]=Narichten sennen
+Name[ne]=समाचार पोष्ट गर्दैछ
+Name[nl]=Nieuws posten
+Name[nn]=Send innlegg
+Name[pl]=Publikowanie wiadomości
+Name[pt]=Enviar Notícias
+Name[pt_BR]=Enviando Notícias
+Name[ru]=Размещение статей
+Name[sk]=Posielanie príspevkov
+Name[sl]=Pošiljanje novic
+Name[sr]=Слање новости
+Name[sr@Latn]=Slanje novosti
+Name[sv]=Skicka inlägg
+Name[ta]=செய்திகளை அனுப்புதல்
+Name[tg]=Ҷойгиркунии мақола
+Name[tr]=Gönderilen Haberler
+Name[uk]=Розміщення новин
+Name[zh_CN]=投递新闻
+Name[zh_TW]=發表文章
+
diff --git a/knode/knode_config_privacy.desktop b/knode/knode_config_privacy.desktop
new file mode 100644
index 000000000..21bdddb6f
--- /dev/null
+++ b/knode/knode_config_privacy.desktop
@@ -0,0 +1,107 @@
+[Desktop Entry]
+Icon=password
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=knode
+X-KDE-FactoryName=knode_config_privacy
+X-KDE-HasReadOnlyMode=false
+X-KDE-ParentApp=knode
+X-KDE-ParentComponents=knode,kontact_knodeplugin
+X-KDE-CfgDlgHierarchy=KNode
+
+Name=Signing/Verifying
+Name[af]=Teken/Bevestig
+Name[ar]=التوقيع/التحقق
+Name[bg]=Подпис/проверка
+Name[bs]=Potpisivanje/Provjera
+Name[ca]=Signat/Verificació
+Name[cs]=Podepisování/ověřování
+Name[da]=Underskriver/Verificerer
+Name[de]=Signieren/Verifizieren
+Name[el]=Υπογραφή/Επαλήθευση
+Name[es]=Firmar/Verificar
+Name[et]=Signeerimine/kontroll
+Name[eu]=Sinadura/Egiaztapena
+Name[fa]=امضا/وارسی
+Name[fi]=Allekirjoittaminen/tarkistaminen
+Name[fr]=Signature/Vérification
+Name[fy]=Undertekening/Ferifiearje
+Name[gl]=Asinando/Verificando
+Name[he]=חתימה/ווידוא
+Name[hu]=Aláírás/ellenőrzés
+Name[is]=Undirrita/staðfesta
+Name[it]=Firma/verifica
+Name[ja]=署名/検証
+Name[ka]=ხელმოწერა/დამოწმება
+Name[kk]=Қолтаңбалау/Тексеру
+Name[km]=ចុះហត្ថលេខា/ផ្ទៀងផ្ទាត់
+Name[lt]=Pasirašoma/tikrinama
+Name[mk]=Потпишување/верификација
+Name[ms]=Menandatangan/Menentu sah
+Name[nb]=Signering/Verifisering
+Name[nds]=Ünnerschrieven / Pröven
+Name[ne]=साइनिङ/रुजु गर्दैछ
+Name[nl]=Ondertekening/Verifiëring
+Name[nn]=Signering/stadfesting
+Name[pl]=Podpisywanie/Weryfikacja podpisów
+Name[pt]=Assinar/Verificar
+Name[pt_BR]=Assinando/Verificando
+Name[ru]=Подпись и проверка
+Name[sk]=Podpis/overenie
+Name[sl]=Podpisovanje/Preverjanje
+Name[sr]=Потписивање-проверавање
+Name[sr@Latn]=Potpisivanje-proveravanje
+Name[sv]=Signera och verifiera
+Name[ta]=கையெழுத்து இடுதல்/சரிபார்த்தல்
+Name[tg]=Имзо ва тафтиш
+Name[tr]=İmzalama/Doğruluma
+Name[uk]=Підписування і перевірка
+Name[zh_CN]=签名/校验
+Name[zh_TW]=簽署/檢查
+Comment=Protect your privacy by signing and verifying postings
+Comment[af]=Beskerm you privaatheid deur pos stukke te onderteken en te bevestig
+Comment[bg]=Защита на личната кореспонденция чрез подписване и проверка на съобщенията
+Comment[bs]=Zaštitite vašu privatnost potpisivanjem i provjerom postinga
+Comment[ca]=Protegiu la vostra privadesa signant i verificant els missatges
+Comment[cs]=Chraňte své soukromí pomocí podepisování a ověřování podpisů
+Comment[da]=Beskyt dine private oplysninger ved at underskrive og verificere indsendelser
+Comment[de]=Schutz der Privatsphäre durch Signieren und Verifizieren von Beiträgen.
+Comment[el]=Προστατεύστε το απόρρητό σας υπογράφοντας και επαληθεύοντας τα μηνύματα
+Comment[es]=Proteja su privacidad firmando y verificando sus envíos
+Comment[et]=Kaitseb sinu privaatsust postitusi signeerides ja konrollides
+Comment[eu]=Babestu zure pribakortasuna argitarapenak sinatu eta egiaztatuz
+Comment[fa]=حفظ محرمانگی خود با امضا و وارسی ارسالهای سریع
+Comment[fi]=Suojele yksityisyyttäsi allekirjoittamalla ja varmentamalla posti
+Comment[fr]=Protection de votre vie privée lors de la signature et vérification des envois
+Comment[fy]=Beskermje jo privacy troch jo berjochten te ûndertekenjen en te ferifiearjen
+Comment[gl]=Protexe a súa intimidade asinando e verificando as entradas
+Comment[hu]=Az adatok védelme az üzenetek elektronikus aláírásával, titkosításával
+Comment[is]=Verndaðu einkalífið þitt með því að undirrita og staðfesta sendingar
+Comment[it]=Proteggi la tua privacy firmando e verificando i messaggi
+Comment[ja]=投稿の署名と検証によりあなたのプライバシーを保護します
+Comment[ka]=დაიცავით თქვენი პირადულობა ხელმოწერითა და განთავსებული სტატიების დამოწმებით
+Comment[kk]=Жарияланғанды қолтаңбалап/тексеріп қорғану
+Comment[km]=ការពារ​ភាព​ឯកជន​របស់​អ្នក​ដោយ​ចុះហត្ថលេខា និង​​ផ្ទៀងផ្ទាត់​ការ​ប្រកាស
+Comment[lt]=Saugokite savo privatumą pasirašydami ir patikrindami skelbimus
+Comment[ms]=Melindungi ruang privasi anda dengan menandatangan dan menentusah pengeposan
+Comment[nb]=Beskytt privatlivet ditt ved å signere og verifisere oppslag
+Comment[nds]=Dör dat Ünnerschrieven un Överpröven vun Narichten warrt Dien Privaatrebeet schuult.
+Comment[ne]=तपाईँको नीजिपना साइन गरेर र पोष्टिङ रुजु गरेर सुरक्षा गर्नुहोस्
+Comment[nl]=Bescherm uw privacy door uw berichten te ondertekenen en te verifiëren
+Comment[nn]=Vern om privatlivet med signerte og stadfesta innlegg
+Comment[pl]=Ochrona prywatności poprzez podpisywanie i weryfikację podpisów wiadomości
+Comment[pt]=Proteja a sua privacidade assinando e verificando as mensagens
+Comment[pt_BR]=Protege sua privacidade através da assinatura e verificação das mensagens
+Comment[ru]=Защита информации посредством подписей и проверки сообщений
+Comment[sk]=Chráňte svoje súkromie digitálnymi podpismi a overovaním príspevkov
+Comment[sl]=Zaščitite vašo zasebnost s podpisovanjem ali preverjanjem člankov
+Comment[sr]=Заштитите вашу приватност потписивајући и проверавајући поруке
+Comment[sr@Latn]=Zaštitite vašu privatnost potpisivajući i proveravajući poruke
+Comment[sv]=Skyddar din integritet genom att signera och verifiera meddelanden
+Comment[tg]=Муҳофизати иттилоот ба воситаи имзо ва тафтиши ахборот
+Comment[tr]=Gönderilen iletilerinizi imzalayarak ve doğrulayarak güvenirliliğinizi sağlayın
+Comment[uk]=Захист конфіденційності за допомогою підписування і перевірки повідомлень
+Comment[zh_CN]=通过对发表的文件进行签名和校验来保护您的隐私
+Comment[zh_TW]=以簽署或檢查文章來保護您的隱私
diff --git a/knode/knode_config_read_news.desktop b/knode/knode_config_read_news.desktop
new file mode 100644
index 000000000..a4ad37183
--- /dev/null
+++ b/knode/knode_config_read_news.desktop
@@ -0,0 +1,66 @@
+[Desktop Entry]
+Icon=mail_get
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=knode
+X-KDE-FactoryName=knode_config_read_news
+X-KDE-HasReadOnlyMode=false
+X-KDE-ParentApp=knode
+X-KDE-ParentComponents=knode,kontact_knodeplugin
+X-KDE-CfgDlgHierarchy=KNode
+
+Name=Reading News
+Name[af]=Lees Nuus
+Name[ar]=قراءة الأخبار
+Name[be]=Чытанне навін
+Name[bg]=Четене на новини
+Name[bs]=Čitanje Useneta
+Name[ca]=Lectura de notícies
+Name[cs]=Čtení novinek
+Name[da]=Læser nyheder
+Name[de]=Artikel lesen
+Name[el]=Ανάγνωση νέων
+Name[es]=Leer noticias
+Name[et]=Uudiste lugemine
+Name[eu]=Berrien irakurketa
+Name[fa]=خواندن اخبار
+Name[fi]=Uutisryhmien lukeminen
+Name[fr]=Lecture de nouvelles
+Name[fy]=Nijs lêze
+Name[gl]=Ler Noticias
+Name[he]=קריאת חדשות
+Name[hu]=Hírek olvasása
+Name[is]=Lestur frétta
+Name[it]=Lettura News
+Name[ja]=ニュースを読む
+Name[ka]=სიახლეების კითხვა
+Name[kk]=Жаңалықтарды оқу
+Name[km]=អាន​ព័ត៌មាន
+Name[lt]=Naujienų skaitymas
+Name[mk]=Читање вести
+Name[ms]=Membaca Berita
+Name[nb]=Lese Njus
+Name[nds]=Narichten lesen
+Name[ne]=समाचार वाचन गर्दैछ
+Name[nl]=Nieuws lezen
+Name[nn]=Les innlegg
+Name[pl]=Odczytywanie wiadomości
+Name[pt]=Leitura de Notícias
+Name[pt_BR]=Lendo Notícias
+Name[ru]=Чтение новостей
+Name[sk]=Čítanie diskusných skupín
+Name[sl]=Branje novic
+Name[sr]=Читање новости
+Name[sr@Latn]=Čitanje novosti
+Name[sv]=Läsa inlägg
+Name[ta]=செய்திகளைப் படித்தல்
+Name[tg]=Хондани ахборот
+Name[tr]=Haber Okuma
+Name[uk]=Читання новин
+Name[uz]=Yangiliklarni oʻqish
+Name[uz@cyrillic]=Янгиликларни ўқиш
+Name[zh_CN]=阅读新闻
+Name[zh_TW]=閱讀新聞
+
diff --git a/knode/knode_options.h b/knode/knode_options.h
new file mode 100644
index 000000000..03c51d4d0
--- /dev/null
+++ b/knode/knode_options.h
@@ -0,0 +1,30 @@
+/*
+ main.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNODE_OPTIONS_H
+#define KNODE_OPTIONS_H
+
+#include <kcmdlineargs.h>
+#include <klocale.h>
+
+static KCmdLineOptions knode_options[] =
+{
+ { "+[url]", I18N_NOOP("A 'news://server/group' URL"), 0 },
+ KCmdLineLastOption
+};
+
+#endif /* KNODE_OPTIONS_H */
+
diff --git a/knode/knode_part.cpp b/knode/knode_part.cpp
new file mode 100644
index 000000000..b1280ced5
--- /dev/null
+++ b/knode/knode_part.cpp
@@ -0,0 +1,125 @@
+/*
+ This file is part of KNode.
+ Copyright (c) 2003 Laurent Montel <montel@kde.org>,
+ Based on the work of 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 "knode_part.h"
+#include "knglobals.h"
+#include "knmainwidget.h"
+#include "aboutdata.h"
+#include "kncollectionview.h"
+#include "knwidgets.h"
+
+#include "sidebarextension.h"
+
+#include <kapplication.h>
+#include <kparts/genericfactory.h>
+#include <kparts/statusbarextension.h>
+#include <knotifyclient.h>
+#include <dcopclient.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kstatusbar.h>
+#include <krsqueezedtextlabel.h>
+
+#include <qlayout.h>
+
+
+typedef KParts::GenericFactory< KNodePart > KNodeFactory;
+K_EXPORT_COMPONENT_FACTORY( libknodepart, KNodeFactory )
+
+KNodePart::KNodePart(QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name, const QStringList &)
+ : KParts::ReadOnlyPart(parent, name),
+ mParentWidget( parentWidget )
+{
+ kdDebug(5003) << "KNodePart()" << endl;
+ kdDebug(5003) << " InstanceName: " << kapp->instanceName() << endl;
+
+ setInstance( KNodeFactory::instance() );
+
+ kdDebug(5003) << "KNodePart()..." << endl;
+ kdDebug(5003) << " InstanceName: " << kapp->instanceName() << endl;
+
+ KGlobal::locale()->insertCatalogue("libkdepim");
+ KGlobal::locale()->insertCatalogue("libkpgp");
+ kapp->dcopClient()->suspend(); // Don't handle DCOP requests yet
+ KGlobal::iconLoader()->addAppDir("knode");
+ knGlobals.instance = KNodeFactory::instance();
+
+ // create a canvas to insert our widget
+ QWidget *canvas = new QWidget(parentWidget, widgetName);
+ canvas->setFocusPolicy(QWidget::ClickFocus);
+ setWidget(canvas);
+
+ mainWidget = new KNMainWidget( this, false, canvas, "knode_widget" );
+ QVBoxLayout *topLayout = new QVBoxLayout(canvas);
+ topLayout->addWidget(mainWidget);
+ mainWidget->setFocusPolicy(QWidget::ClickFocus);
+
+ kapp->dcopClient()->resume(); // Ok. We are ready for DCOP requests.
+
+ new KParts::SideBarExtension( mainWidget->collectionView(),
+ this,
+ "KNodeSidebar" );
+
+ KParts::StatusBarExtension* statusBar = new KParts::StatusBarExtension(this);
+ statusBar->addStatusBarItem(mainWidget->statusBarLabelFilter(), 10, false);
+ statusBar->addStatusBarItem(mainWidget->statusBarLabelGroup(), 15, false);
+
+ setXMLFile( "knodeui.rc" );
+}
+
+KNodePart::~KNodePart()
+{
+ mainWidget->prepareShutdown();
+}
+
+KAboutData *KNodePart::createAboutData()
+{
+ return new KNode::AboutData();
+}
+
+bool KNodePart::openFile()
+{
+ kdDebug(5003) << "KNodePart:openFile()" << endl;
+
+ mainWidget->show();
+ return true;
+}
+
+void KNodePart::guiActivateEvent(KParts::GUIActivateEvent *e)
+{
+ kdDebug(5003) << "KNodePart::guiActivateEvent" << endl;
+ KParts::ReadOnlyPart::guiActivateEvent(e);
+}
+
+
+QWidget* KNodePart::parentWidget() const
+{
+ return mParentWidget;
+}
+
+
+
+#include "knode_part.moc"
+
diff --git a/knode/knode_part.h b/knode/knode_part.h
new file mode 100644
index 000000000..951befc5f
--- /dev/null
+++ b/knode/knode_part.h
@@ -0,0 +1,64 @@
+/*
+ This file is part of KMail.
+ Copyright (c) 2003 Laurent Montel <montel@kde.org>,
+ Based on the work of 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 KNODE_PART_H
+#define KNODE_PART_H
+
+
+#include <kdeversion.h>
+#include <kparts/browserextension.h>
+#include <kparts/factory.h>
+#include <kparts/event.h>
+#include <kparts/part.h>
+
+#include <qwidget.h>
+
+class KInstance;
+class KAboutData;
+
+class ActionManager;
+class KNMainWidget;
+
+class KNodePart: public KParts::ReadOnlyPart
+{
+ Q_OBJECT
+ public:
+ KNodePart(QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name, const QStringList &);
+ virtual ~KNodePart();
+
+ QWidget* parentWidget() const;
+
+ static KAboutData *createAboutData();
+
+ protected:
+ virtual bool openFile();
+ virtual void guiActivateEvent(KParts::GUIActivateEvent *e);
+
+ private:
+ ActionManager *mActionManager;
+ QWidget *mParentWidget;
+ KNMainWidget *mainWidget;
+};
+
+#endif
diff --git a/knode/knodecomposeriface.h b/knode/knodecomposeriface.h
new file mode 100644
index 000000000..d311541be
--- /dev/null
+++ b/knode/knodecomposeriface.h
@@ -0,0 +1,14 @@
+#ifndef KNODECOMPOSERIFACE_H
+#define KNODECOMPOSERIFACE_H
+
+#include <dcopobject.h>
+#include <kurl.h>
+
+class KNodeComposerIface : virtual public DCOPObject
+{
+ K_DCOP
+ k_dcop:
+ virtual void initData(const QString &text) = 0;
+};
+
+#endif
diff --git a/knode/knodeiface.h b/knode/knodeiface.h
new file mode 100644
index 000000000..f92967bbb
--- /dev/null
+++ b/knode/knodeiface.h
@@ -0,0 +1,152 @@
+/*
+ knodeiface.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2003 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+#ifndef KNODEIFACE_H
+#define KNODEIFACE_H
+
+
+// no forward declarations - dcopidl2cpp won't work
+#include <dcopobject.h>
+#include <dcopref.h>
+#include <kurl.h>
+#include <qstringlist.h>
+
+
+class KNodeIface : virtual public DCOPObject
+{
+ K_DCOP
+
+k_dcop:
+ /**
+ * Move to the next article
+ */
+ virtual void nextArticle() = 0;
+
+ /**
+ * Move to the previous article
+ */
+ virtual void previousArticle() = 0;
+
+ /**
+ * Move to the next unread article
+ */
+ virtual void nextUnreadArticle() = 0;
+
+ /**
+ * Move to the next unread thread
+ */
+ virtual void nextUnreadThread() = 0;
+
+ /**
+ * Move to the next group
+ */
+ virtual void nextGroup() = 0;
+
+ /**
+ * Move to the previous group
+ */
+ virtual void previousGroup() = 0;
+
+ /* Group options */
+
+ /**
+ * Open the editor to post a new article in the selected group
+ */
+ virtual void postArticle() = 0;
+
+ /**
+ * Fetch the new headers in the selected groups
+ */
+ virtual void fetchHeadersInCurrentGroup() = 0;
+
+ /**
+ * Expire the articles in the current group
+ */
+ virtual void expireArticlesInCurrentGroup() = 0;
+
+ /**
+ * Mark all the articles in the current group as read
+ */
+ virtual void markAllAsRead() = 0;
+
+ /**
+ * Mark all the articles in the current group as unread
+ */
+ virtual void markAllAsUnread() = 0;
+
+ /* Header view */
+
+ /**
+ * Mark the current article as read
+ */
+ virtual void markAsRead() = 0;
+
+ /**
+ * Mark the current article as unread
+ */
+ virtual void markAsUnread() = 0;
+
+ /**
+ * Mark the current thread as read
+ */
+ virtual void markThreadAsRead() = 0;
+
+ /**
+ * Mark the current thread as unread
+ */
+ virtual void markThreadAsUnread() = 0;
+
+ /* Articles */
+
+ /**
+ * Send the pending articles
+ */
+ virtual void sendPendingMessages() = 0;
+
+ /**
+ * Delete the current article
+ */
+ virtual void deleteArticle() = 0;
+
+ /**
+ * Send the current article
+ */
+ virtual void sendNow() = 0;
+
+ /**
+ * Edit the current article
+ */
+ virtual void editArticle() = 0;
+
+ /**
+ * Fetch all the new article headers
+ */
+ virtual void fetchHeaders() = 0;
+
+ /**
+ * Expire articles in all groups
+ */
+ virtual void expireArticles() = 0;
+
+ /* Kontact integration */
+ /**
+ * Process command-line options
+ * Return true if args were handled, false if there were no args.
+ */
+ virtual bool handleCommandLine() = 0;
+
+};
+
+#endif
diff --git a/knode/knodeui.rc b/knode/knodeui.rc
new file mode 100644
index 000000000..4c020d454
--- /dev/null
+++ b/knode/knodeui.rc
@@ -0,0 +1,256 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="KNode" version="42">
+
+<MenuBar>
+ <Menu noMerge="1" name="file"><text>&amp;File</text>
+ <Action name="file_save"/>
+ <Action name="file_print"/>
+ <Separator/>
+ <Action name="net_sendPending"/>
+ <Action name="net_stop"/>
+ <Separator/>
+ <Action name="file_quit"/>
+
+ </Menu>
+ <Menu noMerge="1" name="edit"><text>&amp;Edit</text>
+ <Action name="edit_copy"/>
+ <Action name="edit_select_all"/>
+ <Separator/>
+ <Action name="article_search"/>
+ <Action name="find_in_article"/>
+ <Separator/>
+ <Action name="fetch_article_with_id"/>
+ </Menu>
+ <Menu noMerge="1" name="view"><text>&amp;View</text>
+ <Action name="view_headers"/>
+ <Action name="view_attachments"/>
+ <Separator/>
+ <Action name="view_showThreads"/>
+ <Action name="view_ExpandAll"/>
+ <Action name="view_CollapseAll"/>
+ <Action name="thread_toggle"/>
+ <Separator/>
+ <Action name="view_Filter"/>
+ <Action name="view_Sort"/>
+ <Action name="view_Refresh"/>
+ <Separator/>
+ <Action name="view_rot13"/>
+ <Separator/>
+ <Action name="article_viewSource"/>
+ <Separator/>
+ <Action name="view_useFixedFont"/>
+ <Action name="view_fancyFormating"/>
+ <Action name="set_charset"/>
+ </Menu>
+ <Menu noMerge="1" name="go_document"><text>&amp;Go</text>
+ <Action name="go_prevArticle"/>
+ <Action name="go_nextArticle"/>
+ <Separator/>
+ <Action name="go_nextUnreadArticle"/>
+ <Action name="go_nextUnreadThread"/>
+ <Separator/>
+ <Action name="go_prevGroup"/>
+ <Action name="go_nextGroup"/>
+ </Menu>
+ <Menu name="account"><text>A&amp;ccount</text>
+ <Action name="account_dnlHeaders"/>
+ <Action name="account_dnlAllHeaders"/>
+ <Action name="account_subscribe"/>
+ <Action name="account_expire_all"/>
+ <Separator/>
+ <Action name="account_properties"/>
+ <Action name="account_delete"/>
+ </Menu>
+ <Menu name="group"><text>G&amp;roup</text>
+ <Action name="group_dnlHeaders"/>
+ <Action name="group_expire"/>
+ <Separator/>
+ <Action name="group_reorg"/>
+ <Separator/>
+ <Action name="group_allRead"/>
+ <Action name="group_allUnread"/>
+ <Action name="group_unread"/>
+ <Separator/>
+ <Action name="group_properties"/>
+ <Action name="group_unsubscribe"/>
+ </Menu>
+ <Menu name="folder"><text>Fol&amp;der</text>
+ <Action name="folder_new"/>
+ <Action name="folder_newChild"/>
+ <Action name="folder_rename"/>
+ <Separator/>
+ <Action name="folder_MboxImport"/>
+ <Action name="folder_MboxExport"/>
+ <Separator/>
+ <Action name="folder_compact"/>
+ <Action name="folder_compact_all"/>
+ <Action name="folder_empty"/>
+ <Separator/>
+ <Action name="folder_delete"/>
+ </Menu>
+ <Menu name="article"><text>&amp;Article</text>
+ <Action name="article_postNew"/>
+ <Action name="article_postReply"/>
+ <Action name="article_mailReply"/>
+ <Action name="article_forward"/>
+ <Separator/>
+ <Action name="article_read"/>
+ <Action name="article_unread"/>
+ <Action name="thread_read"/>
+ <Action name="thread_unread"/>
+ <Separator/>
+ <Action name="article_cancel"/>
+ <Action name="article_supersede"/>
+ <Separator/>
+ <Action name="article_ownWindow"/>
+ <Separator/>
+ <Action name="article_edit"/>
+ <Action name="article_delete"/>
+ <Action name="article_sendNow"/>
+ </Menu>
+ <Menu name="scoring"><text>Sc&amp;oring</text>
+ <Action name="scoreedit"/>
+ <Action name="rescore"/>
+ <Separator/>
+ <Action name="scorelower"/>
+ <Action name="scoreraise"/>
+ <Separator/>
+ <Action name="thread_watch"/>
+ <Action name="thread_ignore"/>
+ </Menu>
+ <Menu name="settings"><text>&amp;Settings</text>
+ <Action name="settings_show_groupView" append="show_merge"/>
+ <Action name="settings_show_headerView" append="show_merge"/>
+ <Action name="settings_show_articleViewer" append="show_merge"/>
+ <Action name="settings_show_quickSearch" append="show_merge"/>
+ <Separator/>
+ <Action name="knode_configure_knode" append="configure_merge" group="settings_configure"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar noMerge="1" name="mainToolBar"><text>Main Toolbar</text>
+ <Action name="article_postNew"/>
+ <Action name="file_save"/>
+ <Action name="file_print"/>
+ <Separator/>
+ <Action name="account_dnlHeaders"/>
+ <Action name="net_stop"/>
+ <Separator/>
+ <Action name="article_postReply"/>
+ <Action name="article_mailReply"/>
+ <Action name="article_forward"/>
+ <Separator/>
+ <Action name="go_nextUnreadArticle"/>
+ <Action name="go_nextUnreadThread"/>
+ <Separator/>
+ <Action name="view_Filter"/>
+ <Action name="article_search"/>
+ <Action name="view_Refresh"/>
+ <Separator/>
+ <Action name="account_subscribe"/>
+</ToolBar>
+
+<Menu name="account_popup">
+ <Action name="account_dnlHeaders"/>
+ <Action name="account_dnlAllHeaders"/>
+ <Action name="account_subscribe"/>
+ <Action name="account_expire_all"/>
+ <Separator/>
+ <Action name="account_rename"/>
+ <Action name="account_properties"/>
+ <Action name="account_delete"/>
+</Menu>
+
+<Menu name="group_popup">
+ <Action name="group_dnlHeaders"/>
+ <Action name="group_expire"/>
+ <Action name="group_allRead"/>
+ <Action name="group_allUnread"/>
+ <Action name="group_unread"/>
+ <Separator/>
+ <Action name="group_rename"/>
+ <Action name="group_properties"/>
+ <Action name="group_unsubscribe"/>
+</Menu>
+
+<Menu name="root_folder_popup">
+ <Action name="folder_newChild"/>
+ <Action name="folder_compact_all"/>
+</Menu>
+
+<Menu name="folder_popup">
+ <Action name="folder_newChild"/>
+ <Action name="folder_rename"/>
+ <Separator/>
+ <Action name="folder_MboxImport"/>
+ <Action name="folder_MboxExport"/>
+ <Separator/>
+ <Action name="folder_compact"/>
+ <Action name="folder_empty"/>
+ <Separator/>
+ <Action name="folder_delete"/>
+</Menu>
+
+<Menu name="remote_popup">
+ <Action name="article_postReply"/>
+ <Action name="article_mailReply"/>
+ <Action name="article_forward"/>
+ <Separator/>
+ <Action name="article_cancel"/>
+ <Action name="article_supersede"/>
+ <Separator/>
+ <Action name="article_read"/>
+ <Action name="article_unread"/>
+ <Separator/>
+ <Action name="thread_read"/>
+ <Action name="thread_unread"/>
+ <Separator/>
+ <Action name="thread_watch"/>
+ <Action name="thread_ignore"/>
+ <Separator/>
+ <Action name="article_ownWindow"/>
+</Menu>
+
+<Menu name="local_popup">
+ <Action name="article_edit"/>
+ <Action name="article_delete"/>
+ <Action name="article_sendNow"/>
+ <Separator/>
+ <Action name="article_cancel"/>
+ <Action name="article_supersede"/>
+</Menu>
+
+<Menu name="body_popup">
+ <Action name="file_save_as"/>
+ <Action name="file_print"/>
+ <Separator/>
+ <Action name="edit_copy"/>
+ <Action name="edit_select_all"/>
+ <Separator/>
+ <Action name="article_viewSource"/>
+ <Separator/>
+ <Action name="view_rot13"/>
+ <Separator/>
+ <Action name="view_useFixedFont"/>
+ <Action name="view_fancyFormating"/>
+ <Action name="set_charset"/>
+</Menu>
+
+<Menu name="url_popup">
+ <Action name="open_url"/>
+ <Action name="copy_url"/>
+ <Action name="add_bookmark"/>
+</Menu>
+
+<Menu name="mailto_popup">
+ <Action name="add_addr_book"/>
+ <Action name="openin_addr_book"/>
+ <Action name="copy_url"/>
+</Menu>
+
+<Menu name="attachment_popup">
+ <Action name="open_attachment"/>
+ <Action name="save_attachment"/>
+</Menu>
+
+</kpartgui>
diff --git a/knode/knprotocolclient.cpp b/knode/knprotocolclient.cpp
new file mode 100644
index 000000000..1d992d8a3
--- /dev/null
+++ b/knode/knprotocolclient.cpp
@@ -0,0 +1,634 @@
+/*
+ knprotocolclient.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <klocale.h>
+#include <kextsock.h>
+#include <ksocks.h>
+
+#include "knjobdata.h"
+#include "knprotocolclient.h"
+
+
+KNProtocolClient::KNProtocolClient(int NfdPipeIn, int NfdPipeOut) :
+ job( 0 ),
+ inputSize( 10000 ),
+ fdPipeIn( NfdPipeIn ),
+ fdPipeOut( NfdPipeOut ),
+ tcpSocket( -1 ),
+ mTerminate( false )
+{
+ input = new char[inputSize];
+}
+
+
+KNProtocolClient::~KNProtocolClient()
+{
+ if (isConnected())
+ closeConnection();
+ delete [] input;
+}
+
+
+void KNProtocolClient::run()
+{
+ if (0!=pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL))
+ qWarning("pthread_setcancelstate failed!");
+ if (0!= pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL))
+ qWarning("pthread_setcanceltype failed!");
+
+ signal(SIGPIPE,SIG_IGN); // ignore sigpipe
+ waitForWork();
+}
+
+
+void KNProtocolClient::insertJob(KNJobData *newJob)
+{
+ job = newJob;
+}
+
+
+void KNProtocolClient::removeJob()
+{
+ job = 0L;
+}
+
+
+void KNProtocolClient::updatePercentage(int percent)
+{
+ byteCountMode=false;
+ progressValue = percent*10;
+ sendSignal(TSprogressUpdate);
+}
+
+
+// main loop, maintains connection and waits for next job
+void KNProtocolClient::waitForWork()
+{
+ fd_set fdsR,fdsE;
+ timeval tv;
+ int selectRet;
+
+ while (true) {
+ if (isConnected()) { // we are connected, hold the connection for xx secs
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ tv.tv_sec = account.hold();
+ tv.tv_usec = 0;
+ selectRet = KSocks::self()->select(FD_SETSIZE, &fdsR, NULL, &fdsE, &tv);
+ if ( mTerminate ) {
+ clearPipe();
+ closeConnection();
+ return;
+ }
+ // In addition to the timeout, this will also happen
+ // if select() returns early because of a signal
+ if (selectRet == 0) {
+#ifndef NDEBUG
+ qDebug("knode: KNProtocolClient::waitForWork(): hold time elapsed, closing connection.");
+#endif
+ closeConnection(); // nothing happend...
+ } else {
+ if (((selectRet > 0)&&(!FD_ISSET(fdPipeIn,&fdsR)))||(selectRet == -1)) {
+#ifndef NDEBUG
+ qDebug("knode: KNProtocolClient::waitForWork(): connection broken, closing it");
+#endif
+ closeSocket();
+ }
+ }
+ }
+
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ } while (select(FD_SETSIZE, &fdsR, NULL, NULL, NULL) <= 0); // don't get tricked by signals
+
+ clearPipe(); // remove start signal
+
+ if (mTerminate)
+ return;
+
+ timer.start();
+
+ sendSignal(TSjobStarted);
+ if (job) {
+ // qDebug("knode: KNProtocolClient::waitForWork(): got job");
+
+ if (job->net()&&!(account == *job->account())) { // server changed
+ account = *job->account();
+ if (isConnected())
+ closeConnection();
+ }
+
+ input[0] = 0; //terminate string
+ thisLine = input;
+ nextLine = input;
+ inputEnd = input;
+ progressValue = 10;
+ predictedLines = -1;
+ doneLines = 0;
+ byteCount = 0;
+ byteCountMode = true;
+
+ if (!job->net()) // job needs no net access
+ processJob();
+ else {
+ if (!isConnected())
+ openConnection();
+
+ if (isConnected()) // connection is ready
+ processJob();
+ }
+ errorPrefix = QString::null;
+
+ clearPipe();
+ }
+ sendSignal(TSworkDone); // emit stopped signal
+ }
+}
+
+
+void KNProtocolClient::processJob()
+{}
+
+
+// connect, handshake and authorization
+bool KNProtocolClient::openConnection()
+{
+ sendSignal(TSconnect);
+
+#ifndef NDEBUG
+ qDebug("knode: KNProtocolClient::openConnection(): opening connection");
+#endif
+
+ if (account.server().isEmpty()) {
+ job->setErrorString(i18n("Unable to resolve hostname"));
+ return false;
+ }
+
+ KExtendedSocket ks;
+
+ ks.setAddress(account.server(), account.port());
+ ks.setTimeout(account.timeout());
+ if (ks.connect() < 0) {
+ if (ks.status() == IO_LookupError) {
+ job->setErrorString(i18n("Unable to resolve hostname"));
+ } else if (ks.status() == IO_ConnectError) {
+ job->setErrorString(i18n("Unable to connect:\n%1").arg(KExtendedSocket::strError(ks.status(), errno)));
+ } else if (ks.status() == IO_TimeOutError)
+ job->setErrorString(i18n("A delay occurred which exceeded the\ncurrent timeout limit."));
+ else
+ job->setErrorString(i18n("Unable to connect:\n%1").arg(KExtendedSocket::strError(ks.status(), errno)));
+
+ closeSocket();
+ return false;
+ }
+
+ tcpSocket = ks.fd();
+ ks.release();
+
+ return true;
+}
+
+
+// sends QUIT-command and closes the socket
+void KNProtocolClient::closeConnection()
+{
+ fd_set fdsW;
+ timeval tv;
+
+#ifndef NDEBUG
+ qDebug("knode: KNProtocolClient::closeConnection(): closing connection");
+#endif
+
+ FD_ZERO(&fdsW);
+ FD_SET(tcpSocket, &fdsW);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ int ret = KSocks::self()->select(FD_SETSIZE, NULL, &fdsW, NULL, &tv);
+
+ if (ret > 0) { // we can write...
+ QCString cmd = "QUIT\r\n";
+ int todo = cmd.length();
+ KSocks::self()->write(tcpSocket,&cmd.data()[0],todo);
+ }
+ closeSocket();
+}
+
+
+// sends a command (one line), return code is written to rep
+bool KNProtocolClient::sendCommand(const QCString &cmd, int &rep)
+{
+ if (!sendStr(cmd + "\r\n"))
+ return false;
+ if (!getNextResponse(rep))
+ return false;
+ return true;
+}
+
+
+// checks return code and calls handleErrors() if necessary
+bool KNProtocolClient::sendCommandWCheck(const QCString &cmd, int rep)
+{
+ int code;
+
+ if (!sendCommand(cmd,code))
+ return false;
+ if (code!=rep) {
+ handleErrors();
+ return false;
+ }
+ return true;
+}
+
+
+// sends a message (multiple lines)
+bool KNProtocolClient::sendMsg(const QCString &msg)
+{
+ const char *line = msg.data();
+ const char *end;
+ QCString buffer;
+ size_t length;
+ char inter[10000];
+
+ progressValue = 100;
+ predictedLines = msg.length()/80; // rule of thumb
+
+ while ((end = ::strstr(line,"\r\n"))) {
+ if (line[0]=='.') // expand one period to double period...
+ buffer.append(".");
+ length = end-line+2;
+ if ((buffer.length()>1)&&((buffer.length()+length)>1024)) { // artifical limit, because I don't want to generate too large blocks
+ if (!sendStr(buffer))
+ return false;
+ buffer = "";
+ }
+ if (length > 9500) {
+ job->setErrorString(i18n("Message size exceeded the size of the internal buffer."));
+ closeSocket();
+ return false;
+ }
+ memcpy(inter,line,length);
+ inter[length]=0; // terminate string
+ buffer += inter;
+ line = end+2;
+ doneLines++;
+ }
+ buffer += ".\r\n";
+ if (!sendStr(buffer))
+ return false;
+
+ return true;
+}
+
+
+// reads next complete line of input
+bool KNProtocolClient::getNextLine()
+{
+ thisLine = nextLine;
+ nextLine = strstr(thisLine,"\r\n");
+ if (nextLine) { // there is another full line in the inputbuffer
+ nextLine[0] = 0; // terminate string
+ nextLine[1] = 0;
+ nextLine+=2;
+ return true;
+ }
+ unsigned int div = inputEnd-thisLine+1; // hmmm, I need to fetch more input from the server...
+ memmove(input,thisLine,div); // save last, incomplete line
+ thisLine = input;
+ inputEnd = input+div-1;
+ do {
+ div = inputEnd-thisLine+1;
+ if ((div) > inputSize-100) {
+ inputSize += 10000;
+ char *newInput = new char[inputSize];
+ memmove(newInput,input,div);
+ delete [] input;
+ input = newInput;
+ thisLine = input;
+ inputEnd = input+div-1;
+#ifndef NDEBUG
+ qDebug("knode: KNProtocolClient::getNextLine(): input buffer enlarged");
+#endif
+ }
+ if (!waitForRead())
+ return false;
+
+ int received;
+ do {
+ received = KSocks::self()->read(tcpSocket, inputEnd, inputSize-(inputEnd-input)-1);
+ } while ((received<0)&&(errno==EINTR)); // don't get tricked by signals
+
+ if (received <= 0) {
+ job->setErrorString(i18n("The connection is broken."));
+ closeSocket();
+ return false;
+ }
+
+ // remove null characters that some stupid servers return...
+ for (int i=0; i<received; i++)
+ if (inputEnd[i] == 0) {
+ memmove(inputEnd+i,inputEnd+i+1,received-i-1);
+ received--;
+ i--;
+ }
+
+ inputEnd += received;
+ inputEnd[0] = 0; // terminate *char
+
+ byteCount += received;
+
+ } while (!(nextLine = strstr(thisLine,"\r\n")));
+
+ if (timer.elapsed()>50) { // reduce framerate to 20 f/s
+ timer.start();
+ if (predictedLines > 0)
+ progressValue = 100 + (doneLines*900/predictedLines);
+ sendSignal(TSprogressUpdate);
+ }
+
+ nextLine[0] = 0; // terminate string
+ nextLine[1] = 0;
+ nextLine+=2;
+ return true;
+}
+
+
+// receives a message (multiple lines)
+bool KNProtocolClient::getMsg(QStrList &msg)
+{
+ char *line;
+
+ while (getNextLine()) {
+ line = getCurrentLine();
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double period into one
+ else
+ if (line[1]==0)
+ return true; // message complete
+ }
+ msg.append(line);
+ doneLines++;
+ }
+
+ return false; // getNextLine() failed
+}
+
+
+// reads next line and returns the response code
+bool KNProtocolClient::getNextResponse(int &code)
+{
+ if (!getNextLine())
+ return false;
+ code = -1;
+ code = atoi(thisLine);
+ return true;
+}
+
+
+// checks return code and calls handleErrors() if necessary
+bool KNProtocolClient::checkNextResponse(int code)
+{
+ if (!getNextLine())
+ return false;
+ if (atoi(thisLine)!=code) {
+ handleErrors();
+ return false;
+ }
+ return true;
+}
+
+
+
+// interprets error code, generates error message and closes the connection
+void KNProtocolClient::handleErrors()
+{
+ if (errorPrefix.isEmpty())
+ job->setErrorString(i18n("An error occurred:\n%1").arg(thisLine));
+ else
+ job->setErrorString(errorPrefix + thisLine);
+
+ closeConnection();
+}
+
+
+void KNProtocolClient::sendSignal(threadSignal s)
+{
+ int signal=(int)s;
+ // qDebug("knode: KNProtcolClient::sendSignal() : sending signal to main thread");
+ write(fdPipeOut, &signal, sizeof(int));
+}
+
+
+// waits until socket is readable
+bool KNProtocolClient::waitForRead()
+{
+ fd_set fdsR,fdsE;
+ timeval tv;
+
+ int ret;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ FD_SET(fdPipeIn, &fdsE);
+ tv.tv_sec = account.timeout();
+ tv.tv_usec = 0;
+ ret = KSocks::self()->select(FD_SETSIZE, &fdsR, NULL, &fdsE, &tv);
+ } while ((ret<0)&&(errno==EINTR)); // don't get tricked by signals
+
+ if (ret == -1) { // select failed
+ if (job) {
+ QString str = i18n("Communication error:\n");
+ str += strerror(errno);
+ job->setErrorString(str);
+ }
+ closeSocket();
+ return false;
+ }
+ if (ret == 0) { // Nothing happend, timeout
+ if (job)
+ job->setErrorString(i18n("A delay occurred which exceeded the\ncurrent timeout limit."));
+ closeConnection();
+ return false;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(fdPipeIn,&fdsR)) { // stop signal
+#ifndef NDEBUG
+ qDebug("knode: KNProtocolClient::waitForRead(): got stop signal");
+#endif
+ closeConnection();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsE)||FD_ISSET(fdPipeIn,&fdsE)) { // broken pipe, etc
+ if (job)
+ job->setErrorString(i18n("The connection is broken."));
+ closeSocket();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsR)) // all ok
+ return true;
+ }
+
+ if (job)
+ job->setErrorString(i18n("Communication error"));
+ closeSocket();
+ return false;
+}
+
+
+// used by sendBuffer() & connect()
+bool KNProtocolClient::waitForWrite()
+{
+ fd_set fdsR,fdsW,fdsE;
+ timeval tv;
+
+ int ret;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsW);
+ FD_SET(tcpSocket, &fdsW);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ FD_SET(fdPipeIn, &fdsE);
+ tv.tv_sec = account.timeout();
+ tv.tv_usec = 0;
+ ret = KSocks::self()->select(FD_SETSIZE, &fdsR, &fdsW, &fdsE, &tv);
+ } while ((ret<0)&&(errno==EINTR)); // don't get tricked by signals
+
+
+ if (ret == -1) { // select failed
+ if (job) {
+ QString str = i18n("Communication error:\n");
+ str += strerror(errno);
+ job->setErrorString(str);
+ }
+ closeSocket();
+ return false;
+ }
+ if (ret == 0) { // nothing happend, timeout
+ if (job)
+ job->setErrorString(i18n("A delay occurred which exceeded the\ncurrent timeout limit."));
+ closeConnection();
+ return false;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(fdPipeIn,&fdsR)) { // stop signal
+#ifndef NDEBUG
+ qDebug("knode: KNProtocolClient::waitForWrite(): got stop signal");
+#endif
+ closeConnection();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsR)||FD_ISSET(tcpSocket,&fdsE)||FD_ISSET(fdPipeIn,&fdsE)) { // broken pipe, etc
+ if (job)
+ job->setErrorString(i18n("The connection is broken."));
+ closeSocket();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsW)) // all ok
+ return true;
+ }
+
+ if (job)
+ job->setErrorString(i18n("Communication error"));
+ closeSocket();
+ return false;
+}
+
+
+void KNProtocolClient::closeSocket()
+{
+ if (-1 != tcpSocket) {
+ close(tcpSocket);
+ tcpSocket = -1;
+ }
+}
+
+
+// sends str to the server
+bool KNProtocolClient::sendStr(const QCString &str)
+{
+ int ret;
+ int todo = str.length();
+ int done = 0;
+
+ while (todo > 0) {
+ if (!waitForWrite())
+ return false;
+ ret = KSocks::self()->write(tcpSocket,&str.data()[done],todo);
+ if (ret <= 0) {
+ if (job) {
+ QString str = i18n("Communication error:\n");
+ str += strerror(errno);
+ job->setErrorString(str);
+ }
+ closeSocket();
+ return false;
+ } else {
+ done += ret;
+ todo -= ret;
+ }
+ byteCount += ret;
+ }
+ if (timer.elapsed()>50) { // reduce framerate to 20 f/s
+ timer.start();
+ if (predictedLines > 0)
+ progressValue = 100 + (doneLines/predictedLines)*900;
+ sendSignal(TSprogressUpdate);
+ }
+ return true;
+}
+
+
+// removes start/stop signal
+void KNProtocolClient::clearPipe()
+{
+ fd_set fdsR;
+ timeval tv;
+ int selectRet;
+ char buf;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn,&fdsR);
+ if (1==(selectRet=select(FD_SETSIZE,&fdsR,NULL,NULL,&tv)))
+ if ( read(fdPipeIn, &buf, 1 ) == -1 )
+ ::perror( "clearPipe()" );
+ } while (selectRet == 1);
+}
+
diff --git a/knode/knprotocolclient.h b/knode/knprotocolclient.h
new file mode 100644
index 000000000..e531a278b
--- /dev/null
+++ b/knode/knprotocolclient.h
@@ -0,0 +1,122 @@
+/*
+ knprotocolclient.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNPROTOCOLCLIENT_H
+#define KNPROTOCOLCLIENT_H
+
+#include <qdatetime.h>
+#include <qthread.h>
+#include <qcstring.h>
+
+#include <knserverinfo.h>
+
+class QStrList;
+class KNJobData;
+struct in_addr;
+
+
+class KNProtocolClient : public QThread {
+
+ public:
+ enum threadSignal { TSworkDone=0, TSjobStarted=1, TSconnect=2, TSloadGrouplist=3,
+ TSwriteGrouplist=4, TSdownloadGrouplist=5, TSdownloadNew=6,
+ TSsortNew=7, TSdownloadArticle=8, TSsendArticle=9, TSsendMail=10,
+ TSprogressUpdate=11, TSdownloadDesc=12, TSdownloadNewGroups=13 };
+
+ KNProtocolClient(int NfdPipeIn, int NfdPipeOut);
+ ~KNProtocolClient();
+
+ virtual void run();
+
+ void insertJob(KNJobData *newJob);
+ void removeJob();
+
+ void updatePercentage(int percent);
+
+ int getProgressValue() const { return progressValue; };
+ /** bytes in&out for the current connection */
+ int getByteCount() const { return byteCount; };
+ bool isInByteCountMode() const { return byteCountMode; };
+
+ void terminateClient() { mTerminate = true; }
+ protected:
+
+ /** main loop, maintains connection and waits for next job */
+ void waitForWork();
+ /** examines the job and calls the suitable handling method */
+ virtual void processJob();
+
+ /** connect, handshake and authorization */
+ virtual bool openConnection();
+ bool isConnected() { return (tcpSocket!=-1); };
+ /** sends QUIT-command and closes the socket */
+ virtual void closeConnection();
+
+ /** sends a command (one line), return code is written to rep */
+ virtual bool sendCommand(const QCString &cmd, int &rep);
+ /** checks return code and calls handleErrors() if necessary */
+ bool sendCommandWCheck(const QCString &cmd, int rep);
+ /** sends a message (multiple lines) */
+ bool sendMsg(const QCString &msg);
+
+ /** reads next complete line of input */
+ bool getNextLine();
+ /** returns pointer to current line of input */
+ char* getCurrentLine() { return thisLine; };
+ /** receives a message (multiple lines) */
+ bool getMsg(QStrList &msg);
+ /** reads next line and returns the response code */
+ bool getNextResponse(int &rep);
+ /** checks return code and calls handleErrors() if necessary */
+ bool checkNextResponse(int rep);
+
+ /** interprets error code, generates error message and closes the connection */
+ virtual void handleErrors();
+
+ void sendSignal(threadSignal s);
+
+ KNJobData *job;
+ KNServerInfo account;
+ /** handleErrors() adds this string to the error message */
+ QString errorPrefix;
+ int progressValue, predictedLines, doneLines;
+ bool byteCountMode;
+
+ private:
+ /** waits until socket is readable */
+ bool waitForRead();
+ /** waits until socket is writeable */
+ bool waitForWrite();
+ void closeSocket();
+ /** sends str to the server */
+ bool sendStr(const QCString &str);
+ /** removes start/stop signal */
+ void clearPipe();
+
+ char *input;
+ char *thisLine, *nextLine, *inputEnd;
+ unsigned int inputSize;
+ /** IPC-Pipes to/from async thread */
+ int fdPipeIn,fdPipeOut;
+ int tcpSocket;
+ /** bytes in&out for the current connection */
+ int byteCount;
+ QTime timer;
+ bool mTerminate;
+
+};
+
+#endif
diff --git a/knode/knrangefilter.cpp b/knode/knrangefilter.cpp
new file mode 100644
index 000000000..bfa02110f
--- /dev/null
+++ b/knode/knrangefilter.cpp
@@ -0,0 +1,228 @@
+/*
+ knrangefilter.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+
+#include <ksimpleconfig.h>
+#include <knuminput.h>
+
+#include "knrangefilter.h"
+
+
+bool KNRangeFilter::doFilter(int a)
+{
+ bool ret=true;
+ if(enabled) {
+ switch (op1) {
+ case gt:
+ case gtoeq:
+ if (op2 != dis)
+ ret=( matchesOp(val1,op1,a) && matchesOp(a,op2,val2) );
+ else
+ ret = matchesOp(val1,op1,a);
+ break;
+ case eq:
+ case lt:
+ case ltoeq:
+ ret = matchesOp(val1,op1,a);
+ break;
+ default:
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+
+
+bool KNRangeFilter::matchesOp(int v1, Op o, int v2)
+{
+ bool ret=false;
+
+ switch(o) {
+ case eq: ret=(v1==v2); break;
+ case gt: ret=(v1<v2); break;
+ case gtoeq: ret=(v1<=v2); break;
+ case ltoeq: ret=(v1>=v2); break;
+ case lt: ret=(v1>v2); break;
+ default: ret=false; break;
+ };
+
+ return ret;
+}
+
+
+
+void KNRangeFilter::load(KSimpleConfig *conf)
+{
+ enabled=conf->readBoolEntry("enabled", false);
+ val1=conf->readNumEntry("val1",0);
+ op1=(Op) conf->readNumEntry("op1",0);
+ val2=conf->readNumEntry("val2",0);
+ op2=(Op) conf->readNumEntry("op2",0);
+}
+
+
+
+void KNRangeFilter::save(KSimpleConfig *conf)
+{
+ conf->writeEntry("enabled", enabled);
+ conf->writeEntry("val1", val1);
+ conf->writeEntry("op1", (int)op1);
+ conf->writeEntry("op2", (int)op2);
+ conf->writeEntry("val2", val2);
+}
+
+
+
+
+//=====================================================================================
+//=====================================================================================
+
+KNRangeFilterWidget::KNRangeFilterWidget(const QString& value, int min, int max, QWidget* parent, const QString &unit)
+ : QGroupBox(value, parent)
+{
+ enabled=new QCheckBox(this);
+
+ val1=new KIntSpinBox(min, max, 1, min, 10, this);
+ val1->setSuffix(unit);
+ val2=new KIntSpinBox(min, max, 1, min, 10, this);
+ val2->setSuffix(unit);
+
+ op1=new QComboBox(this);
+ op1->insertItem("<");
+ op1->insertItem("<=");
+ op1->insertItem("=");
+ op1->insertItem(">=");
+ op1->insertItem(">");
+ op2=new QComboBox(this);
+ op2->insertItem("");
+ op2->insertItem("<");
+ op2->insertItem("<=");
+
+ des=new QLabel(value, this);
+ des->setAlignment(AlignCenter);
+
+ QGridLayout *topL=new QGridLayout(this, 2,6, 8,5 );
+
+ topL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+ topL->addWidget(enabled,1,0, Qt::AlignHCenter);
+ topL->addColSpacing(0, 30);
+ topL->addWidget(val1,1,1);
+ topL->addWidget(op1,1,2);
+ topL->addWidget(des,1,3);
+ topL->addColSpacing(3, 45);
+ topL->addWidget(op2,1,4);
+ topL->addWidget(val2,1,5);
+ topL->setColStretch(1,1);
+ topL->setColStretch(5,1);
+
+ connect(op1, SIGNAL(activated(int)), SLOT(slotOp1Changed(int)));
+ connect(op2, SIGNAL(activated(int)), SLOT(slotOp2Changed(int)));
+ connect(enabled, SIGNAL(toggled(bool)), SLOT(slotEnabled(bool)));
+
+ slotEnabled(false);
+}
+
+
+
+KNRangeFilterWidget::~KNRangeFilterWidget()
+{
+}
+
+
+
+KNRangeFilter KNRangeFilterWidget::filter()
+{
+ KNRangeFilter r;
+ r.val1=val1->value();
+ r.val2=val2->value();
+
+ r.op1=(KNRangeFilter::Op) op1->currentItem();
+ if (op2->currentText().isEmpty())
+ r.op2=KNRangeFilter::dis;
+ else if (op2->currentText()=="<")
+ r.op2=KNRangeFilter::gt;
+ else if (op2->currentText()=="<=")
+ r.op2=KNRangeFilter::gtoeq;
+
+ r.enabled=enabled->isChecked();
+
+ return r;
+}
+
+
+
+void KNRangeFilterWidget::setFilter(KNRangeFilter &f)
+{
+ val1->setValue(f.val1);
+ val2->setValue(f.val2);
+
+ op1->setCurrentItem((int)f.op1);
+ if (f.op2 == KNRangeFilter::dis)
+ op2->setCurrentItem(0);
+ else if (f.op2 == KNRangeFilter::gt)
+ op2->setCurrentItem(1);
+ else if (f.op2 == KNRangeFilter::gtoeq)
+ op2->setCurrentItem(2);
+
+ enabled->setChecked(f.enabled);
+}
+
+
+
+void KNRangeFilterWidget::clear()
+{
+ val1->setValue(val1->minValue());
+ val2->setValue(val2->minValue());
+ enabled->setChecked(false);
+}
+
+
+
+void KNRangeFilterWidget::slotOp1Changed(int id)
+{
+ bool state = (op1->isEnabled() && (id<2));
+ op2->setEnabled(state);
+ des->setEnabled(state);
+ slotOp2Changed(op2->currentItem());
+}
+
+
+
+void KNRangeFilterWidget::slotOp2Changed(int id)
+{
+ val2->setEnabled(op1->isEnabled() && (op1->currentItem()<2) && (id>0));
+}
+
+
+
+void KNRangeFilterWidget::slotEnabled(bool e)
+{
+ op1->setEnabled(e);
+ val1->setEnabled(e);
+ des->setEnabled(e);
+ slotOp1Changed(op1->currentItem());
+}
+
+// -----------------------------------------------------------------------------
+
+#include "knrangefilter.moc"
+
diff --git a/knode/knrangefilter.h b/knode/knrangefilter.h
new file mode 100644
index 000000000..702a2ca55
--- /dev/null
+++ b/knode/knrangefilter.h
@@ -0,0 +1,88 @@
+/*
+ knrangefilter.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNRANGEFILTER_H
+#define KNRANGEFILTER_H
+
+#include <qgroupbox.h>
+
+class QLabel;
+class KIntSpinBox;
+class QComboBox;
+class QCheckBox;
+
+class KSimpleConfig;
+
+
+class KNRangeFilter {
+
+ friend class KNRangeFilterWidget;
+
+ public:
+ KNRangeFilter() { op1=eq; op2=dis; val1=0; val2=0; enabled=false; }
+ ~KNRangeFilter() {}
+
+ KNRangeFilter& operator=(const KNRangeFilter &nr)
+ { val1=nr.val1; val2=nr.val2;
+ op1=nr.op1; op2=nr.op2;
+ enabled=nr.enabled;
+ return (*this); }
+
+ void load(KSimpleConfig *conf);
+ void save(KSimpleConfig *conf);
+
+ bool doFilter(int a);
+
+ protected:
+ enum Op { gt=0, gtoeq=1, eq=2, ltoeq=3, lt=4, dis=5 };
+ bool matchesOp(int v1, Op o, int v2);
+
+ int val1, val2;
+ Op op1, op2;
+ bool enabled;
+
+};
+
+
+//==================================================================================
+
+
+class KNRangeFilterWidget : public QGroupBox {
+
+ Q_OBJECT
+
+ public:
+ KNRangeFilterWidget(const QString& value, int min, int max, QWidget* parent, const QString &unit=QString::null);
+ ~KNRangeFilterWidget();
+
+ KNRangeFilter filter();
+ void setFilter(KNRangeFilter &f);
+ void clear();
+
+ protected:
+ QCheckBox *enabled;
+ QLabel *des;
+ KIntSpinBox *val1, *val2;
+ QComboBox *op1, *op2;
+
+ protected slots:
+ void slotEnabled(bool e);
+ void slotOp1Changed(int id);
+ void slotOp2Changed(int id);
+
+};
+
+#endif
diff --git a/knode/knreaderui.rc b/knode/knreaderui.rc
new file mode 100644
index 000000000..4609e5b8d
--- /dev/null
+++ b/knode/knreaderui.rc
@@ -0,0 +1,81 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="KNReader" version="14">
+
+<MenuBar>
+ <Menu noMerge="1" name="file"><text>&amp;File</text>
+ <Action name="file_save"/>
+ <Action name="file_print"/>
+ <Separator/>
+ <Action name="file_close"/>
+ </Menu>
+ <Menu noMerge="1" name="edit"><text>&amp;Edit</text>
+ <Action name="edit_copy"/>
+ <Action name="edit_select_all"/>
+ <Action name="find_in_article"/>
+ </Menu>
+ <Menu noMerge="1" name="view"><text>&amp;View</text>
+ <Action name="view_headers"/>
+ <Action name="view_attachments"/>
+ <Separator/>
+ <Action name="view_rot13"/>
+ <Separator/>
+ <Action name="article_viewSource"/>
+ <Separator/>
+ <Action name="view_useFixedFont"/>
+ <Action name="view_fancyFormating"/>
+ <Action name="set_charset"/>
+ </Menu>
+ <Menu name="article"><text>&amp;Article</text>
+ <Action name="article_postReply"/>
+ <Action name="article_mailReply"/>
+ <Action name="article_forward"/>
+ <Separator/>
+ <Action name="article_cancel"/>
+ <Action name="article_supersede"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar noMerge="1" name="mainToolBar"><text>Main Toolbar</text>
+ <Action name="file_save"/>
+ <Action name="file_print"/>
+ <Action name="edit_copy"/>
+ <Separator/>
+ <Action name="article_postReply"/>
+ <Action name="article_mailReply"/>
+ <Action name="article_forward"/>
+</ToolBar>
+
+<Menu name="body_popup">
+ <Action name="file_save_as"/>
+ <Action name="file_print"/>
+ <Separator/>
+ <Action name="edit_copy"/>
+ <Action name="edit_select_all"/>
+ <Separator/>
+ <Action name="article_viewSource"/>
+ <Separator/>
+ <Action name="view_rot13"/>
+ <Separator/>
+ <Action name="view_useFixedFont"/>
+ <Action name="view_fancyFormating"/>
+ <Action name="set_charset"/>
+</Menu>
+
+<Menu name="url_popup">
+ <Action name="open_url"/>
+ <Action name="copy_url"/>
+ <Action name="add_bookmark"/>
+</Menu>
+
+<Menu name="mailto_popup">
+ <Action name="add_addr_book"/>
+ <Action name="openin_addr_book"/>
+ <Action name="copy_url"/>
+</Menu>
+
+<Menu name="attachment_popup">
+ <Action name="open_attachment"/>
+ <Action name="save_attachment"/>
+</Menu>
+
+</kpartgui>
diff --git a/knode/knscoring.cpp b/knode/knscoring.cpp
new file mode 100644
index 000000000..cad44fc81
--- /dev/null
+++ b/knode/knscoring.cpp
@@ -0,0 +1,142 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qstring.h>
+
+#include <kwin.h>
+#include <kscoringeditor.h>
+
+#include "knscoring.h"
+#include "knaccountmanager.h"
+#include "kngroupmanager.h"
+#include "utilities.h"
+#include "knglobals.h"
+
+//----------------------------------------------------------------------------
+NotifyCollection* KNScorableArticle::notifyC = 0;
+
+KNScorableArticle::KNScorableArticle(KNRemoteArticle* a)
+ : ScorableArticle(), _a(a)
+{
+}
+
+
+KNScorableArticle::~KNScorableArticle()
+{
+}
+
+
+void KNScorableArticle::addScore(short s)
+{
+ _a->setScore(_a->score()+s);
+ _a->setChanged(true);
+}
+
+void KNScorableArticle::changeColor(const QColor& c)
+{
+ _a->setColor(c);
+}
+
+void KNScorableArticle::displayMessage(const QString& s)
+{
+ if (!_a->isNew()) return;
+ if (!notifyC) notifyC = new NotifyCollection();
+ notifyC->addNote(*this,s);
+}
+
+QString KNScorableArticle::from() const
+{
+ return _a->from()->asUnicodeString();
+}
+
+
+QString KNScorableArticle::subject() const
+{
+ return _a->subject()->asUnicodeString();
+}
+
+
+QString KNScorableArticle::getHeaderByType(const QString& s) const
+{
+ KMime::Headers::Base *h = _a->getHeaderByType(s.latin1());
+ if (!h) return "";
+ QString t = _a->getHeaderByType(s.latin1())->asUnicodeString();
+ Q_ASSERT( !t.isEmpty() );
+ return t;
+}
+
+
+void KNScorableArticle::markAsRead()
+{
+ _a->setRead();
+}
+
+//----------------------------------------------------------------------------
+
+KNScorableGroup::KNScorableGroup()
+{
+}
+
+
+KNScorableGroup::~KNScorableGroup()
+{
+}
+
+//----------------------------------------------------------------------------
+
+KNScoringManager::KNScoringManager() : KScoringManager("knode")
+{
+}
+
+
+KNScoringManager::~KNScoringManager()
+{
+}
+
+
+QStringList KNScoringManager::getGroups() const
+{
+ KNAccountManager *am = knGlobals.accountManager();
+ QStringList res;
+ QValueList<KNNntpAccount*>::Iterator it;
+ for ( it = am->begin(); it != am->end(); ++it ) {
+ QStringList groups;
+ knGlobals.groupManager()->getSubscribed( (*it), groups);
+ res += groups;
+ }
+ res.sort();
+ return res;
+}
+
+
+QStringList KNScoringManager::getDefaultHeaders() const
+{
+ QStringList l = KScoringManager::getDefaultHeaders();
+ l << "Lines";
+ l << "References";
+ return l;
+}
+
+
+void KNScoringManager::configure()
+{
+ KScoringEditor *dlg = KScoringEditor::createEditor(this, knGlobals.topWidget);
+
+ if (dlg) {
+ dlg->show();
+ KWin::activateWindow(dlg->winId());
+ }
+}
+
+#include "knscoring.moc"
diff --git a/knode/knscoring.h b/knode/knscoring.h
new file mode 100644
index 000000000..77b011e22
--- /dev/null
+++ b/knode/knscoring.h
@@ -0,0 +1,76 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNSCORING_H
+#define KNSCORING_H
+
+#include <kscoring.h>
+
+class KDialogBase;
+class KNRemoteArticle;
+class KNGroup;
+
+
+class KNScorableArticle : public ScorableArticle
+{
+public:
+ KNScorableArticle(KNRemoteArticle*);
+ virtual ~KNScorableArticle();
+
+ virtual void addScore(short s);
+ virtual void changeColor(const QColor&);
+ virtual void displayMessage(const QString&);
+ virtual QString from() const;
+ virtual QString subject() const;
+ virtual QString getHeaderByType(const QString&) const;
+ virtual void markAsRead();
+
+ static NotifyCollection* notifyC;
+
+private:
+ KNRemoteArticle *_a;
+};
+
+
+class KNScorableGroup : public ScorableGroup
+{
+public:
+ KNScorableGroup();
+ virtual ~KNScorableGroup();
+};
+
+
+// class KNScorableServer : public ScorableServer
+// {
+// public:
+// virtual ~KNScorableServer();
+// };
+
+
+class KNScoringManager : public KScoringManager
+{
+ Q_OBJECT
+
+public:
+ KNScoringManager();
+ virtual ~KNScoringManager();
+ virtual QStringList getGroups() const;
+ virtual QStringList getDefaultHeaders() const;
+
+ void configure();
+ bool canColors()const { return true; }
+ bool canMarkAsRead() const { return true; }
+};
+
+#endif
diff --git a/knode/knsearchdialog.cpp b/knode/knsearchdialog.cpp
new file mode 100644
index 000000000..113bfe733
--- /dev/null
+++ b/knode/knsearchdialog.cpp
@@ -0,0 +1,122 @@
+/*
+ knsearchdialog.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+
+#include <klocale.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include "knfilterconfigwidget.h"
+#include "knarticlefilter.h"
+#include "utilities.h"
+#include "knsearchdialog.h"
+
+
+KNSearchDialog::KNSearchDialog(searchType /*t*/, QWidget *parent)
+ : QDialog(parent)
+{
+ setCaption(kapp->makeStdCaption( i18n("Search for Articles") ));
+ setIcon(SmallIcon("knode"));
+ QGroupBox *bg=new QGroupBox(this);
+
+ startBtn=new QPushButton(SmallIcon("mail_find"),i18n("Sea&rch"), bg);
+ startBtn->setDefault(true);
+ newBtn=new QPushButton(SmallIcon("editclear"),i18n("C&lear"), bg);
+ closeBtn=new KPushButton(KStdGuiItem::close(), bg);
+
+ completeThreads=new QCheckBox(i18n("Sho&w complete threads"),this);
+ fcw=new KNFilterConfigWidget(this);
+ fcw->reset();
+
+ QHBoxLayout *topL=new QHBoxLayout(this, 5);
+ QVBoxLayout *filterL=new QVBoxLayout(this, 0, 5);
+ QVBoxLayout *btnL=new QVBoxLayout(bg, 8, 5);
+
+ filterL->addWidget(completeThreads);
+ filterL->addWidget(fcw,1);
+
+ btnL->addWidget(startBtn);
+ btnL->addWidget(newBtn);
+ btnL->addStretch(1);
+ btnL->addWidget(closeBtn);
+
+ topL->addLayout(filterL, 1);
+ topL->addWidget(bg);
+
+ connect(startBtn, SIGNAL(clicked()), this, SLOT(slotStartClicked()));
+ connect(newBtn, SIGNAL(clicked()), this, SLOT(slotNewClicked()));
+ connect(closeBtn, SIGNAL(clicked()), this, SLOT(slotCloseClicked()));
+
+ f_ilter=new KNArticleFilter();
+ f_ilter->setLoaded(true);
+ f_ilter->setSearchFilter(true);
+
+ setFixedHeight(sizeHint().height());
+ KNHelper::restoreWindowSize("searchDlg", this, sizeHint());
+ fcw->setStartFocus();
+}
+
+
+
+KNSearchDialog::~KNSearchDialog()
+{
+ delete f_ilter;
+ KNHelper::saveWindowSize("searchDlg", size());
+}
+
+
+void KNSearchDialog::slotStartClicked()
+{
+ f_ilter->status=fcw->status->filter();
+ f_ilter->score=fcw->score->filter();
+ f_ilter->age=fcw->age->filter();
+ f_ilter->lines=fcw->lines->filter();
+ f_ilter->subject=fcw->subject->filter();
+ f_ilter->from=fcw->from->filter();
+ f_ilter->messageId=fcw->messageId->filter();
+ f_ilter->references=fcw->references->filter();
+ f_ilter->setApplyOn(completeThreads->isChecked()? 1:0);
+ emit doSearch(f_ilter);
+}
+
+
+
+void KNSearchDialog::slotNewClicked()
+{
+ fcw->reset();
+}
+
+
+
+void KNSearchDialog::slotCloseClicked()
+{
+ emit dialogDone();
+}
+
+
+void KNSearchDialog::closeEvent( QCloseEvent * )
+{
+ emit dialogDone();
+}
+
+//--------------------------------
+
+#include "knsearchdialog.moc"
+
diff --git a/knode/knsearchdialog.h b/knode/knsearchdialog.h
new file mode 100644
index 000000000..7c68a49fd
--- /dev/null
+++ b/knode/knsearchdialog.h
@@ -0,0 +1,58 @@
+/*
+ knsearchdialog.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNSEARCHDIALOG_H
+#define KNSEARCHDIALOG_H
+
+#include <qdialog.h>
+
+class QPushButton;
+
+class KNFilterConfigWidget;
+class KNArticleFilter;
+
+
+class KNSearchDialog : public QDialog {
+
+ Q_OBJECT
+
+ public:
+ enum searchType { STfolderSearch, STgroupSearch };
+ KNSearchDialog(searchType t=STgroupSearch, QWidget *parent=0);
+ ~KNSearchDialog();
+
+ KNArticleFilter* filter() const { return f_ilter; }
+
+ protected:
+ void closeEvent( QCloseEvent* e );
+
+ KNFilterConfigWidget *fcw;
+ QPushButton *startBtn, *newBtn, *closeBtn;
+ QCheckBox *completeThreads;
+ KNArticleFilter *f_ilter;
+
+ protected slots:
+ void slotStartClicked();
+ void slotNewClicked();
+ void slotCloseClicked();
+
+ signals:
+ void doSearch(KNArticleFilter *);
+ void dialogDone();
+
+};
+
+#endif
diff --git a/knode/knserverinfo.cpp b/knode/knserverinfo.cpp
new file mode 100644
index 000000000..b79735a2f
--- /dev/null
+++ b/knode/knserverinfo.cpp
@@ -0,0 +1,188 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kwallet.h>
+using namespace KWallet;
+
+#include "knglobals.h"
+#include "knserverinfo.h"
+#include "knaccountmanager.h"
+#include "utilities.h"
+
+#include <qwidget.h>
+
+KNServerInfo::KNServerInfo() :
+ t_ype(STnntp), i_d(-1), p_ort(119), h_old(300),
+ t_imeout(60), n_eedsLogon(false), p_assDirty(false),
+ mPassLoaded( false ),
+ mEncryption( None )
+{
+}
+
+
+
+KNServerInfo::~KNServerInfo()
+{
+}
+
+
+
+void KNServerInfo::readConf(KConfig *conf)
+{
+ s_erver=conf->readEntry("server", "localhost");
+
+ if(t_ype==STnntp)
+ p_ort=conf->readNumEntry("port", 119);
+ else
+ p_ort=conf->readNumEntry("port", 25);
+
+ h_old=conf->readNumEntry("holdTime", 300);
+
+ if(h_old < 0) h_old=0;
+
+ t_imeout=conf->readNumEntry("timeout", 60);
+
+ if(t_imeout < 15) t_imeout=15;
+
+ if(t_ype==STnntp)
+ i_d=conf->readNumEntry("id", -1);
+
+ n_eedsLogon=conf->readBoolEntry("needsLogon",false);
+ u_ser=conf->readEntry("user");
+ p_ass = KNHelper::decryptStr(conf->readEntry("pass"));
+
+ // migration to KWallet
+ if (Wallet::isEnabled() && !p_ass.isEmpty()) {
+ conf->deleteEntry( "pass" );
+ p_assDirty = true;
+ }
+
+ // if the wallet is open, no need to delay the password loading
+ if (Wallet::isOpen( Wallet::NetworkWallet() ))
+ readPassword();
+
+ QString encStr = conf->readEntry( "encryption", "None" );
+ if ( encStr.contains( "SSL", false ) )
+ mEncryption = SSL;
+ else if ( encStr.contains( "TLS", false ) )
+ mEncryption = TLS;
+ else
+ mEncryption = None;
+}
+
+
+void KNServerInfo::saveConf(KConfig *conf)
+{
+ conf->writeEntry("server", s_erver);
+ if ( p_ort == 0 ) p_ort = 119;
+ conf->writeEntry("port", p_ort);
+ conf->writeEntry("holdTime", h_old);
+ conf->writeEntry("timeout", t_imeout);
+ if (t_ype==STnntp)
+ conf->writeEntry("id", i_d);
+
+ conf->writeEntry("needsLogon", n_eedsLogon);
+ conf->writeEntry("user", u_ser);
+ // open wallet for storing only if the user actually changed the password
+ if (n_eedsLogon && p_assDirty) {
+ Wallet *wallet = KNAccountManager::wallet();
+ if (!wallet || wallet->writePassword(QString::number(i_d), p_ass)) {
+ if ( KMessageBox::warningYesNo( 0,
+ i18n("KWallet is not available. It is strongly recommended to use "
+ "KWallet for managing your passwords.\n"
+ "However, KNode can store the password in its configuration "
+ "file instead. The password is stored in an obfuscated format, "
+ "but should not be considered secure from decryption efforts "
+ "if access to the configuration file is obtained.\n"
+ "Do you want to store the password for server '%1' in the "
+ "configuration file?").arg( server() ),
+ i18n("KWallet Not Available"),
+ KGuiItem( i18n("Store Password") ),
+ KGuiItem( i18n("Do Not Store Password") ) )
+ == KMessageBox::Yes ) {
+ conf->writeEntry( "pass", KNHelper::encryptStr( p_ass ) );
+ }
+ }
+ p_assDirty = false;
+ }
+
+ switch ( mEncryption ) {
+ case SSL:
+ conf->writeEntry( "encryption", "SSL" );
+ break;
+ case TLS:
+ conf->writeEntry( "encryption", "TLS" );
+ break;
+ default:
+ conf->writeEntry( "encryption", "None" );
+ }
+}
+
+
+
+bool KNServerInfo::operator==(const KNServerInfo &s)
+{
+ return ( (t_ype==s.t_ype) &&
+ (s_erver==s.s_erver) &&
+ (p_ort==s.p_ort) &&
+ (h_old==s.h_old) &&
+ (t_imeout==s.t_imeout) &&
+ (n_eedsLogon==s.n_eedsLogon) &&
+ (u_ser==s.u_ser) &&
+ (p_ass==s.p_ass) &&
+ (mEncryption == s.mEncryption)
+ );
+}
+
+
+const QString &KNServerInfo::pass()
+{
+ // if we need to load the password, load all of them
+ if (n_eedsLogon && !mPassLoaded && p_ass.isEmpty() )
+ knGlobals.accountManager()->loadPasswords();
+
+ return p_ass;
+}
+
+void KNServerInfo::setPass(const QString &s)
+{
+ if (p_ass != s) {
+ p_ass = s;
+ p_assDirty = true;
+ }
+}
+
+
+void KNServerInfo::readPassword()
+{
+ // no need to load a password if the account doesn't require auth
+ if (!n_eedsLogon)
+ return;
+ mPassLoaded = true;
+
+ // check wether there is a chance to find our password at all
+ if (Wallet::folderDoesNotExist(Wallet::NetworkWallet(), "knode") ||
+ Wallet::keyDoesNotExist(Wallet::NetworkWallet(), "knode", QString::number(i_d)))
+ return;
+
+ // finally try to open the wallet and read the password
+ KWallet::Wallet *wallet = KNAccountManager::wallet();
+ if ( wallet )
+ wallet->readPassword( QString::number(i_d), p_ass );
+}
diff --git a/knode/knserverinfo.h b/knode/knserverinfo.h
new file mode 100644
index 000000000..6f564d934
--- /dev/null
+++ b/knode/knserverinfo.h
@@ -0,0 +1,91 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNSERVERINFO_H
+#define KNSERVERINFO_H
+
+#include <qstring.h>
+
+class KConfig;
+namespace KWallet {
+ class Wallet;
+}
+using KWallet::Wallet;
+
+class KNServerInfo {
+
+ public:
+ enum serverType { STnntp, STsmtp, STpop3 };
+ enum Encryption { None, SSL, TLS };
+
+ KNServerInfo();
+ ~KNServerInfo();
+
+ void readConf(KConfig *conf);
+ void saveConf(KConfig *conf);
+
+ //get
+ serverType type()const { return t_ype; }
+ int id()const { return i_d; }
+ const QString& server() { return s_erver; }
+ const QString& user() { return u_ser; }
+ const QString& pass();
+ int port() const { return p_ort; }
+ int hold() const { return h_old; }
+ int timeout() const { return t_imeout; }
+ bool needsLogon()const { return n_eedsLogon; }
+ bool isEmpty()const { return s_erver.isEmpty(); }
+ bool readyForLogin() const { return !n_eedsLogon || mPassLoaded; }
+ Encryption encryption() const { return mEncryption; }
+
+ //set
+ void setType(serverType t) { t_ype=t; }
+ void setId(int i) { i_d=i; }
+ void setServer(const QString &s) { s_erver=s; }
+ void setUser(const QString &s) { u_ser=s; }
+ void setPass(const QString &s);
+ void setPort(int p) { p_ort=p; }
+ void setHold(int h) { h_old=h; }
+ void setTimeout(int t) { t_imeout=t; }
+ void setNeedsLogon(bool b) { n_eedsLogon=b; }
+ void setEncryption( Encryption enc ) { mEncryption = enc; }
+
+ bool operator==(const KNServerInfo &s);
+
+ /** Loads the password from KWallet, used for on-demand password loading */
+ void readPassword();
+
+ protected:
+ serverType t_ype;
+
+ QString s_erver,
+ u_ser,
+ p_ass;
+
+ int i_d,
+ p_ort,
+ h_old,
+ t_imeout;
+
+ bool n_eedsLogon,
+ p_assDirty;
+ /** Prevent loading the password multiple times since wallet operations
+ from the I/O thread don't work. */
+ bool mPassLoaded;
+ /** Encyrption method */
+ Encryption mEncryption;
+};
+
+
+#endif
diff --git a/knode/knsourceviewwindow.cpp b/knode/knsourceviewwindow.cpp
new file mode 100644
index 000000000..ad0eb1cf5
--- /dev/null
+++ b/knode/knsourceviewwindow.cpp
@@ -0,0 +1,62 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qaccel.h>
+
+#include <kapplication.h>
+
+#include "knsourceviewwindow.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "utilities.h"
+
+
+KNSourceViewWindow::KNSourceViewWindow( const QString &text )
+ : KTextBrowser(0)
+{
+ setWFlags(WType_TopLevel | WDestructiveClose);
+ QAccel *accel = new QAccel( this, "browser close-accel" );
+ accel->connectItem( accel->insertItem( Qt::Key_Escape ), this , SLOT( close() ));
+ KNConfig::Appearance *app=knGlobals.configManager()->appearance();
+
+ setTextFormat( PlainText );
+
+ setCaption(kapp->makeStdCaption(i18n("Article Source")));
+ setPaper( QBrush(app->backgroundColor()) );
+ setFont( app->articleFixedFont() );
+ setColor( app->textColor() );
+ setWordWrap( KTextBrowser::NoWrap );
+
+ setText( text );
+ KNHelper::restoreWindowSize("sourceWindow", this, QSize(500,300));
+ show();
+}
+
+
+void KNSourceViewWindow::setPalette( const QPalette &pal )
+{
+ QPalette p = pal;
+ p.setColor( QColorGroup::Text, knGlobals.configManager()->appearance()->textColor() );
+ p.setColor( QColorGroup::Background, knGlobals.configManager()->appearance()->backgroundColor() );
+ KTextBrowser::setPalette( p );
+}
+
+
+KNSourceViewWindow::~KNSourceViewWindow()
+{
+ KNHelper::saveWindowSize("sourceWindow",size());
+}
+
+
+#include "knsourceviewwindow.moc"
diff --git a/knode/knsourceviewwindow.h b/knode/knsourceviewwindow.h
new file mode 100644
index 000000000..c3a7fc8ca
--- /dev/null
+++ b/knode/knsourceviewwindow.h
@@ -0,0 +1,33 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNSOURCEVIEWWINDOW_H
+#define KNSOURCEVIEWWINDOW_H
+
+#include <ktextbrowser.h>
+
+
+class KNSourceViewWindow : public KTextBrowser {
+
+ Q_OBJECT
+
+ public:
+ KNSourceViewWindow( const QString &text );
+ ~KNSourceViewWindow();
+ virtual void setPalette( const QPalette &pal );
+
+};
+
+
+#endif
diff --git a/knode/knstatusfilter.cpp b/knode/knstatusfilter.cpp
new file mode 100644
index 000000000..6389794d7
--- /dev/null
+++ b/knode/knstatusfilter.cpp
@@ -0,0 +1,232 @@
+/*
+ knstatusfilter.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+
+#include "knarticle.h"
+#include "knstatusfilter.h"
+
+
+KNStatusFilter::KNStatusFilter()
+{
+ data.fill(false,8);
+}
+
+
+
+KNStatusFilter::~KNStatusFilter()
+{
+}
+
+
+
+void KNStatusFilter::load(KSimpleConfig *conf)
+{
+ data.setBit(EN_R, conf->readBoolEntry("EN_R", false));
+ data.setBit(DAT_R, conf->readBoolEntry("DAT_R", false));
+
+ data.setBit(EN_N, conf->readBoolEntry("EN_N", false));
+ data.setBit(DAT_N, conf->readBoolEntry("DAT_N", false));
+
+ data.setBit(EN_US, conf->readBoolEntry("EN_US", false));
+ data.setBit(DAT_US, conf->readBoolEntry("DAT_US", false));
+
+ data.setBit(EN_NS, conf->readBoolEntry("EN_NS", false));
+ data.setBit(DAT_NS, conf->readBoolEntry("DAT_NS", false));
+
+}
+
+
+
+void KNStatusFilter::save(KSimpleConfig *conf)
+{
+ conf->writeEntry("EN_R", data.at(EN_R));
+ conf->writeEntry("DAT_R", data.at(DAT_R));
+
+ conf->writeEntry("EN_N", data.at(EN_N));
+ conf->writeEntry("DAT_N", data.at(DAT_N));
+
+ conf->writeEntry("EN_US", data.at(EN_US));
+ conf->writeEntry("DAT_US", data.at(DAT_US));
+
+ conf->writeEntry("EN_NS", data.at(EN_NS));
+ conf->writeEntry("DAT_NS", data.at(DAT_NS));
+}
+
+
+
+bool KNStatusFilter::doFilter(KNRemoteArticle *a)
+{
+ bool ret=true;
+
+ if(data.at(EN_R) && ret)
+ ret=(a->isRead() == data.at(DAT_R));
+
+ if(data.at(EN_N) && ret)
+ ret=(a->isNew() == data.at(DAT_N));
+
+ if(data.at(EN_US) && ret)
+ ret=(a->hasUnreadFollowUps() == data.at(DAT_US));
+
+ if(data.at(EN_NS) && ret)
+ ret=(a->hasNewFollowUps() == data.at(DAT_NS));
+
+ return ret;
+}
+
+
+
+//==============================================================================
+
+KNStatusFilterWidget::KNStatusFilterWidget(QWidget *parent) :
+ QButtonGroup(0, parent)
+{
+ setFrameStyle(NoFrame);
+ enR=new QCheckBox(i18n("Is read:"), this);
+ enN=new QCheckBox(i18n("Is new:"), this);
+ enUS=new QCheckBox(i18n("Has unread followups:"), this);
+ enNS=new QCheckBox(i18n("Has new followups:"), this);
+
+ rCom=new TFCombo(this);
+ nCom=new TFCombo(this);
+ usCom=new TFCombo(this);
+ nsCom=new TFCombo(this);
+
+ QGridLayout *topL=new QGridLayout(this, 5, 3, 15,5);
+ topL->addWidget(enR,0,0); topL->addWidget(rCom,0,1);
+ topL->addWidget(enN,1,0); topL->addWidget(nCom,1,1);
+ topL->addWidget(enUS,2,0); topL->addWidget(usCom,2,1);
+ topL->addWidget(enNS,3,0); topL->addWidget(nsCom,3,1);
+ topL->setColStretch(2,1);
+ topL->setRowStretch(4,1);
+
+ connect(this, SIGNAL(clicked(int)), this, SLOT(slotEnabled(int)));
+}
+
+
+
+KNStatusFilterWidget::~KNStatusFilterWidget()
+{
+}
+
+
+
+KNStatusFilter KNStatusFilterWidget::filter()
+{
+ KNStatusFilter f;
+
+ f.data.setBit(EN_R, enR->isChecked());
+ f.data.setBit(DAT_R, rCom->value());
+
+ f.data.setBit(EN_N, enN->isChecked());
+ f.data.setBit(DAT_N, nCom->value());
+
+ f.data.setBit(EN_US, enUS->isChecked());
+ f.data.setBit(DAT_US, usCom->value());
+
+ f.data.setBit(EN_NS, enNS->isChecked());
+ f.data.setBit(DAT_NS, nsCom->value());
+
+ return f;
+}
+
+
+
+void KNStatusFilterWidget::setFilter(KNStatusFilter &f)
+{
+ enR->setChecked(f.data.at(EN_R));
+ rCom->setValue(f.data.at(DAT_R));
+
+ enN->setChecked(f.data.at(EN_N));
+ nCom->setValue(f.data.at(DAT_N));
+
+ enUS->setChecked(f.data.at(EN_US));
+ usCom->setValue(f.data.at(DAT_US));
+
+ enNS->setChecked(f.data.at(EN_NS));
+ nsCom->setValue(f.data.at(DAT_NS));
+
+ for(int i=0; i<4; i++) slotEnabled(i);
+}
+
+
+void KNStatusFilterWidget::clear()
+{
+ enR->setChecked(false);
+ enN->setChecked(false);
+ enUS->setChecked(false);
+ enNS->setChecked(false);
+ rCom->setValue(true);
+ nCom->setValue(true);
+ nsCom->setValue(true);
+ usCom->setValue(true);
+
+ for(int i=0; i<4; i++) slotEnabled(i);
+}
+
+
+
+void KNStatusFilterWidget::slotEnabled(int c)
+{
+ switch(c) {
+
+ case 0: rCom->setEnabled(enR->isChecked()); break;
+ case 1: nCom->setEnabled(enN->isChecked()); break;
+ case 2: usCom->setEnabled(enUS->isChecked()); break;
+ case 3: nsCom->setEnabled(enNS->isChecked()); break;
+ };
+}
+
+
+//==============================================================================
+
+
+KNStatusFilterWidget::TFCombo::TFCombo(QWidget *parent) : QComboBox(parent)
+{
+ insertItem(i18n("True"));
+ insertItem(i18n("False"));
+}
+
+
+
+KNStatusFilterWidget::TFCombo::~TFCombo()
+{
+}
+
+
+
+//--------------------------------
+
+#include "knstatusfilter.moc"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/knode/knstatusfilter.h b/knode/knstatusfilter.h
new file mode 100644
index 000000000..43030de4e
--- /dev/null
+++ b/knode/knstatusfilter.h
@@ -0,0 +1,99 @@
+/*
+ knstatusfilter.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNSTATUSFILTER_H
+#define KNSTATUSFILTER_H
+
+#include <qbuttongroup.h>
+#include <qcombobox.h>
+#include <qbitarray.h>
+
+class QCheckBox;
+class KSimpleConfig;
+class KNRemoteArticle;
+
+
+class KNStatusFilter {
+
+ friend class KNStatusFilterWidget;
+
+ public:
+ KNStatusFilter();
+ ~KNStatusFilter();
+
+ KNStatusFilter& operator=(const KNStatusFilter &sf)
+ { for(int i=0; i<8; i++) data.setBit(i, sf.data.at(i)); return (*this); }
+
+ void load(KSimpleConfig *conf);
+ void save(KSimpleConfig *conf);
+
+ bool doFilter(KNRemoteArticle *a);
+
+ protected:
+ QBitArray data;
+
+};
+
+
+//=================================================================================
+
+
+class KNStatusFilterWidget : public QButtonGroup {
+
+ Q_OBJECT
+
+ public:
+ KNStatusFilterWidget(QWidget *parent);
+ ~KNStatusFilterWidget();
+
+ KNStatusFilter filter();
+ void setFilter(KNStatusFilter &f);
+ void clear();
+
+
+ protected:
+
+ class TFCombo : public QComboBox {
+
+ public:
+ TFCombo(QWidget *parent);
+ ~TFCombo();
+ void setValue(bool b) { if(b) setCurrentItem(0); else setCurrentItem(1); }
+ bool value() const { return (currentItem()==0); }
+ };
+
+
+ QCheckBox *enR, *enN, *enUS, *enNS;
+ TFCombo *rCom, *nCom, *usCom, *nsCom;
+
+ protected slots:
+ void slotEnabled(int c);
+
+};
+
+
+#define EN_R 0
+#define EN_N 1
+#define EN_US 2
+#define EN_NS 3
+
+#define DAT_R 4
+#define DAT_N 5
+#define DAT_US 6
+#define DAT_NS 7
+
+
+#endif
diff --git a/knode/knstringfilter.cpp b/knode/knstringfilter.cpp
new file mode 100644
index 000000000..c41b723fe
--- /dev/null
+++ b/knode/knstringfilter.cpp
@@ -0,0 +1,165 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qcombobox.h>
+#include <qlayout.h>
+#include <qcheckbox.h>
+
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <klineedit.h>
+
+#include "kngroup.h"
+#include "knnntpaccount.h"
+#include "knglobals.h"
+#include "knconfigmanager.h"
+#include "knstringfilter.h"
+
+
+KNStringFilter& KNStringFilter::operator=(const KNStringFilter &sf)
+{
+ con=sf.con;
+ data=sf.data.copy();
+ regExp=sf.regExp;
+
+ return (*this);
+}
+
+
+
+bool KNStringFilter::doFilter(const QString &s)
+{
+ bool ret=true;
+
+ if(!expanded.isEmpty()) {
+ if(regExp) {
+ QRegExp matcher(expanded);
+ ret = ( matcher.search(s) >= 0 );
+ } else
+ ret=(s.find(expanded,0,false)!=-1);
+
+ if(!con) ret=!ret;
+ }
+
+ return ret;
+}
+
+
+
+// replace placeholders
+void KNStringFilter::expand(KNGroup *g)
+{
+ KNConfig::Identity *id = (g) ? g->identity() : 0;
+
+ if (!id) {
+ id = (g) ? g->account()->identity() : 0;
+ if (!id)
+ id = knGlobals.configManager()->identity();
+ }
+
+ expanded = data;
+ expanded.replace(QRegExp("%MYNAME"), id->name());
+ expanded.replace(QRegExp("%MYEMAIL"), id->email());
+}
+
+
+
+void KNStringFilter::load(KSimpleConfig *conf)
+{
+ con=conf->readBoolEntry("contains", true);
+ data=conf->readEntry("Data");
+ regExp=conf->readBoolEntry("regX", false);
+}
+
+
+
+void KNStringFilter::save(KSimpleConfig *conf)
+{
+ conf->writeEntry("contains", con);
+ conf->writeEntry("Data", data);
+ conf->writeEntry("regX", regExp);
+}
+
+
+//===============================================================================
+
+KNStringFilterWidget::KNStringFilterWidget(const QString& title, QWidget *parent)
+ : QGroupBox(title, parent)
+{
+ fType=new QComboBox(this);
+ fType->insertItem(i18n("Does Contain"));
+ fType->insertItem(i18n("Does NOT Contain"));
+
+ fString=new KLineEdit(this);
+
+ regExp=new QCheckBox(i18n("Regular expression"), this);
+
+ QGridLayout *topL=new QGridLayout(this, 3,3, 8,5 );
+ topL->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+ topL->addWidget(fType, 1,0);
+ topL->addColSpacing(1, 10);
+ topL->addWidget(regExp, 1,1);
+ topL->addMultiCellWidget(fString, 2,2, 0,2);
+ topL->setColStretch(2,1);
+}
+
+
+
+KNStringFilterWidget::~KNStringFilterWidget()
+{
+}
+
+
+
+KNStringFilter KNStringFilterWidget::filter()
+{
+ KNStringFilter ret;
+ ret.con=(fType->currentItem()==0);
+ ret.data=fString->text();
+ ret.regExp=regExp->isChecked();
+
+ return ret;
+}
+
+
+
+void KNStringFilterWidget::setFilter(KNStringFilter &f)
+{
+ if(f.con) fType->setCurrentItem(0);
+ else fType->setCurrentItem(1);
+ fString->setText(f.data);
+ regExp->setChecked(f.regExp);
+}
+
+
+
+void KNStringFilterWidget::clear()
+{
+ fString->clear();
+ fType->setCurrentItem(0);
+ regExp->setChecked(false);
+}
+
+
+void KNStringFilterWidget::setStartFocus()
+{
+ fString->setFocus();
+}
+
+
+// -----------------------------------------------------------------------------+
+
+#include "knstringfilter.moc"
+
+// kate: space-indent on; indent-width 2;
diff --git a/knode/knstringfilter.h b/knode/knstringfilter.h
new file mode 100644
index 000000000..17199cbe7
--- /dev/null
+++ b/knode/knstringfilter.h
@@ -0,0 +1,82 @@
+/*
+ knstringfilter.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNSTRINGFILTER_H
+#define KNSTRINGFILTER_H
+
+#include <qgroupbox.h>
+
+class QCheckBox;
+class QComboBox;
+
+class KLineEdit;
+class KSimpleConfig;
+
+class KNGroup;
+
+
+class KNStringFilter {
+
+ friend class KNStringFilterWidget;
+
+ public:
+ KNStringFilter() { con=true; regExp=false;}
+ ~KNStringFilter() {}
+
+ KNStringFilter& operator=(const KNStringFilter &sf);
+ /** replace placeholders */
+ void expand(KNGroup *g);
+
+ void load(KSimpleConfig *conf);
+ void save(KSimpleConfig *conf);
+
+ bool doFilter(const QString &s);
+
+ protected:
+ QString data, expanded;
+ bool con, regExp;
+
+};
+
+
+//===============================================================================
+
+
+class KNStringFilterWidget : public QGroupBox {
+
+ Q_OBJECT
+
+ public:
+ KNStringFilterWidget(const QString& title, QWidget *parent);
+ ~KNStringFilterWidget();
+
+ KNStringFilter filter();
+ void setFilter(KNStringFilter &f);
+ void clear();
+
+ /** usablity hack for the search dialog */
+ void setStartFocus();
+
+ protected:
+ QCheckBox *regExp;
+ QComboBox *fType;
+ KLineEdit *fString;
+
+};
+
+
+#endif
+
diff --git a/knode/knwidgets.cpp b/knode/knwidgets.cpp
new file mode 100644
index 000000000..d71624eb6
--- /dev/null
+++ b/knode/knwidgets.cpp
@@ -0,0 +1,159 @@
+/*
+ knwidgets.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include "knwidgets.h"
+
+
+//====================================================================================
+
+KNListBoxItem::KNListBoxItem(const QString& text, QPixmap *pm)
+{
+ p_m=pm;
+ setText(text);
+}
+
+
+KNListBoxItem::~KNListBoxItem()
+{
+}
+
+
+void KNListBoxItem::paint(QPainter *p)
+{
+
+ QFontMetrics fm = p->fontMetrics();
+
+ int tYPos=0, tXPos=3, pYPos=0;
+
+ tYPos = fm.ascent() + fm.leading()/2; // vertical text position
+
+ if(p_m) {
+
+ tXPos=p_m->width() + 6;
+
+ if ( p_m->height() < fm.height() ) {
+ //tYPos = fm.ascent() + fm.leading()/2;
+ pYPos = (fm.height() - p_m->height())/2;}
+ else {
+ tYPos = p_m->height()/2 - fm.height()/2 + fm.ascent();
+ pYPos = 0;
+ }
+ p->drawPixmap( 3, pYPos , *p_m );
+ }
+
+
+ p->drawText( tXPos, tYPos, text() );
+}
+
+
+int KNListBoxItem::height(const QListBox *lb) const
+{
+ if(p_m)
+ return QMAX( p_m->height(), lb->fontMetrics().lineSpacing() + 1 );
+ else
+ return (lb->fontMetrics().lineSpacing() + 1);
+}
+
+
+int KNListBoxItem::width(const QListBox *lb) const
+{
+ if(p_m)
+ return (p_m->width() + lb->fontMetrics().width( text() ) + 6);
+ else
+ return (lb->fontMetrics().width( text() ) + 6);
+}
+
+
+//====================================================================================
+
+// **** listbox for dialogs **************************************************
+
+KNDialogListBox::KNDialogListBox(bool alwaysIgnore, QWidget * parent, const char * name)
+ : QListBox(parent, name), a_lwaysIgnore(alwaysIgnore)
+{
+}
+
+
+KNDialogListBox::~KNDialogListBox()
+{
+}
+
+
+void KNDialogListBox::keyPressEvent(QKeyEvent *e)
+{
+ if ((a_lwaysIgnore || !(hasFocus()&&isVisible()))&&((e->key()==Key_Enter)||(e->key()==Key_Return)))
+ e->ignore();
+ else
+ QListBox::keyPressEvent(e);
+}
+
+
+//====================================================================================
+
+
+KNDockWidgetHeaderDrag::KNDockWidgetHeaderDrag(QWidget *focusWidget, KDockWidgetAbstractHeader* parent, KDockWidget* dock, const char* name )
+ : KDockWidgetHeaderDrag(parent, dock, name), f_ocus(false)
+{
+ connect(focusWidget, SIGNAL(focusChanged(QFocusEvent*)), SLOT(slotFocusChanged(QFocusEvent*)));
+}
+
+
+KNDockWidgetHeaderDrag::~KNDockWidgetHeaderDrag()
+{
+}
+
+
+void KNDockWidgetHeaderDrag::slotFocusChanged(QFocusEvent *e)
+{
+ if(e->gotFocus()) {
+ f_ocus = true;
+ } else if(e->lostFocus()) {
+ f_ocus = false;
+ }
+ update();
+}
+
+
+void KNDockWidgetHeaderDrag::paintEvent(QPaintEvent* ev)
+{
+ if (!f_ocus) {
+ KDockWidgetHeaderDrag::paintEvent(ev);
+ return;
+ }
+
+ QPixmap drawBuffer(width(), height());
+ QPainter paint;
+
+ paint.begin(&drawBuffer);
+ paint.fillRect(drawBuffer.rect(), QBrush(colorGroup().brush(QColorGroup::Background)));
+
+ paint.setPen(palette().active().highlight());
+ paint.drawLine(1, 2, width(), 2);
+ paint.drawLine(1, 3, width(), 3);
+ paint.drawLine(1, 5, width(), 5);
+ paint.drawLine(1, 6, width(), 6);
+
+ bitBlt( this,0,0,&drawBuffer,0,0,width(),height());
+ paint.end();
+}
+
+
+//====================================================================================
+
+#include "knwidgets.moc"
diff --git a/knode/knwidgets.h b/knode/knwidgets.h
new file mode 100644
index 000000000..af71bd5f0
--- /dev/null
+++ b/knode/knwidgets.h
@@ -0,0 +1,88 @@
+/*
+ knwidgets.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2004 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef KNWIDGETS_H
+#define KNWIDGETS_H
+
+#include <qlistbox.h>
+#include <qbitarray.h>
+
+#include <kdockwidget.h>
+#include <kprogress.h>
+
+class QPainter;
+class QPixmap;
+
+//====================================================================================
+
+
+class KNListBoxItem : public QListBoxItem {
+
+ public:
+ KNListBoxItem(const QString& text, QPixmap *pm=0);
+ ~KNListBoxItem();
+
+
+ protected:
+ virtual void paint(QPainter *);
+ virtual int height(const QListBox *) const;
+ virtual int width(const QListBox *) const;
+
+ QPixmap *p_m;
+};
+
+
+//====================================================================================
+
+
+/** a list box which ignores Enter, useful for dialogs */
+class KNDialogListBox : public QListBox
+{
+ public:
+ // alwaysIgnore==false: enter is ignored when the widget isn't visible/out of focus
+ KNDialogListBox(bool alwaysIgnore=false, QWidget * parent=0, const char * name=0);
+ ~KNDialogListBox();
+
+ protected:
+ void keyPressEvent( QKeyEvent *e );
+
+ bool a_lwaysIgnore;
+};
+
+
+//====================================================================================
+
+
+class KNDockWidgetHeaderDrag : public KDockWidgetHeaderDrag
+{
+ Q_OBJECT
+
+ public:
+ KNDockWidgetHeaderDrag(QWidget *focusWidget, KDockWidgetAbstractHeader* parent, KDockWidget* dock,
+ const char* name = 0);
+ virtual ~KNDockWidgetHeaderDrag();
+
+ protected slots:
+ void slotFocusChanged(QFocusEvent *e);
+
+ protected:
+ virtual void paintEvent( QPaintEvent* );
+
+ bool f_ocus;
+};
+
+
+#endif
diff --git a/knode/main.cpp b/knode/main.cpp
new file mode 100644
index 000000000..dad1963fa
--- /dev/null
+++ b/knode/main.cpp
@@ -0,0 +1,44 @@
+/*
+ main.cpp
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2001 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+
+#include "knapplication.h"
+#include "resource.h"
+#include "knode.h"
+#include "aboutdata.h"
+#include "knode_options.h"
+using KNode::AboutData;
+
+int main(int argc, char* argv[])
+{
+ AboutData aboutData;
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( knode_options );
+ KUniqueApplication::addCmdLineOptions();
+
+ if (!KNApplication::start())
+ return 0;
+
+ KNApplication app;
+ KGlobal::locale()->insertCatalogue("libkdepim");
+ KGlobal::locale()->insertCatalogue("libkpgp");
+ return app.exec();
+}
+
diff --git a/knode/pics/Makefile.am b/knode/pics/Makefile.am
new file mode 100644
index 000000000..f32c60694
--- /dev/null
+++ b/knode/pics/Makefile.am
@@ -0,0 +1,12 @@
+pics_DATA = stat_edit.png mail.png stat_saved.png \
+ ctlart.png greyball.png ignore.png \
+ newsubs.png stat_sent.png \
+ eyes.png greyballchk.png snderr.png \
+ group.png posting.png stat_cncl.png \
+ pgp-keys.png group_big.png article.png
+
+picsdir = $(kde_datadir)/knode/pics
+
+EXTRA_DIST = $(pics_DATA)
+knodeicondir = $(kde_datadir)/knode/icons
+knodeicon_ICON = message_reply mail_get_all
diff --git a/knode/pics/article.png b/knode/pics/article.png
new file mode 100644
index 000000000..9ebd69759
--- /dev/null
+++ b/knode/pics/article.png
Binary files differ
diff --git a/knode/pics/cr16-action-mail_get_all.png b/knode/pics/cr16-action-mail_get_all.png
new file mode 100644
index 000000000..425a23a9d
--- /dev/null
+++ b/knode/pics/cr16-action-mail_get_all.png
Binary files differ
diff --git a/knode/pics/cr16-action-message_reply.png b/knode/pics/cr16-action-message_reply.png
new file mode 100644
index 000000000..af0b3b3e1
--- /dev/null
+++ b/knode/pics/cr16-action-message_reply.png
Binary files differ
diff --git a/knode/pics/cr22-action-mail_get_all.png b/knode/pics/cr22-action-mail_get_all.png
new file mode 100644
index 000000000..327045c39
--- /dev/null
+++ b/knode/pics/cr22-action-mail_get_all.png
Binary files differ
diff --git a/knode/pics/cr22-action-message_reply.png b/knode/pics/cr22-action-message_reply.png
new file mode 100644
index 000000000..27c401ad6
--- /dev/null
+++ b/knode/pics/cr22-action-message_reply.png
Binary files differ
diff --git a/knode/pics/cr32-action-mail_get_all.png b/knode/pics/cr32-action-mail_get_all.png
new file mode 100644
index 000000000..34610de6e
--- /dev/null
+++ b/knode/pics/cr32-action-mail_get_all.png
Binary files differ
diff --git a/knode/pics/cr32-action-message_reply.png b/knode/pics/cr32-action-message_reply.png
new file mode 100644
index 000000000..170f2be45
--- /dev/null
+++ b/knode/pics/cr32-action-message_reply.png
Binary files differ
diff --git a/knode/pics/crsc-action-mail_get_all.svg b/knode/pics/crsc-action-mail_get_all.svg
new file mode 100644
index 000000000..6096ce7cc
--- /dev/null
+++ b/knode/pics/crsc-action-mail_get_all.svg
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ x="0"
+ y="0"
+ width="40"
+ height="40"
+ id="svg602"
+ xml:space="preserve"><defs
+ id="defs604"><linearGradient
+ id="linearGradient660"><stop
+ style="stop-color:#0c000d;stop-opacity:1;"
+ offset="0"
+ id="stop662" /><stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop663" /></linearGradient><linearGradient
+ id="linearGradient714"><stop
+ style="stop-color:#0c610d;stop-opacity:1;"
+ offset="0"
+ id="stop715" /><stop
+ style="stop-color:#307a30;stop-opacity:1;"
+ offset="1"
+ id="stop716" /></linearGradient><linearGradient
+ id="linearGradient710"><stop
+ style="stop-color:#6eff3a;stop-opacity:1;"
+ offset="0"
+ id="stop711" /><stop
+ style="stop-color:#35bd3d;stop-opacity:1;"
+ offset="1"
+ id="stop712" /></linearGradient><linearGradient
+ id="linearGradient706"><stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop707" /><stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop708" /></linearGradient><linearGradient
+ id="linearGradient675"><stop
+ style="stop-color:#a29ac5;stop-opacity:1;"
+ offset="0"
+ id="stop676" /><stop
+ style="stop-color:#5e5e99;stop-opacity:1;"
+ offset="1"
+ id="stop677" /></linearGradient><linearGradient
+ id="linearGradient659"><stop
+ style="stop-color:#edeaff;stop-opacity:1;"
+ offset="0"
+ id="stop660" /><stop
+ style="stop-color:#9797bd;stop-opacity:1;"
+ offset="1"
+ id="stop661" /></linearGradient><linearGradient
+ id="linearGradient615"><stop
+ style="stop-color:#aba8bd;stop-opacity:1;"
+ offset="0"
+ id="stop616" /><stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop617" /></linearGradient><radialGradient
+ cx="0.77600008"
+ cy="0.82812488"
+ r="0.57651013"
+ fx="0.77600008"
+ fy="0.82812488"
+ id="radialGradient618"
+ xlink:href="#linearGradient615" /><linearGradient
+ x1="0.36507928"
+ y1="0.68987346"
+ x2="0.84656072"
+ y2="0.94303805"
+ id="linearGradient658"
+ xlink:href="#linearGradient659" /><linearGradient
+ x1="0.45098045"
+ y1="0.386076"
+ x2="0.82352948"
+ y2="0.64556968"
+ id="linearGradient674"
+ xlink:href="#linearGradient675" /><radialGradient
+ cx="0.47715884"
+ cy="1.56241897e-2"
+ r="0.93336767"
+ fx="0.47715884"
+ fy="1.56241897e-2"
+ id="radialGradient705"
+ xlink:href="#linearGradient706" /><radialGradient
+ cx="0.51937997"
+ cy="0.875"
+ r="0.49147549"
+ fx="0.51937997"
+ fy="0.875"
+ id="radialGradient709"
+ xlink:href="#linearGradient710" /><linearGradient
+ x1="0.37735853"
+ y1="0.74050617"
+ x2="0.81761009"
+ y2="-7.59494528e-2"
+ id="linearGradient713"
+ xlink:href="#linearGradient714" /><radialGradient
+ cx="0.51937979"
+ cy="0.546875"
+ r="0.47348177"
+ fx="0.51937979"
+ fy="0.546875"
+ id="radialGradient659"
+ xlink:href="#linearGradient660" /></defs><g
+ transform="matrix(0.7543,0,0,0.7543,0.654275,0.583227)"
+ style="font-size:12;"
+ id="g650"><path
+ d="M 0.818516 13.53465 L 27.37481 0.620286 L 38.10647 23.72063 L 11.73206 39.27243 L 0.818516 13.53465 z "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:url(#radialGradient618);fill-rule:evenodd;"
+ id="path614" /><path
+ d="M 11.55017 38.99959 L 19.37155 21.17413 "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:none;fill-rule:evenodd;stroke:#a299c0;stroke-width:0.75;"
+ id="path609" /><path
+ d="M 37.74268 23.90252 L 23.73696 19.99183 "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:none;fill-rule:evenodd;stroke:#a299c0;stroke-width:0.75;"
+ id="path608" /><path
+ d="M 27.19292 0.711238 L 23.19129 22.90211 L 0.818516 13.35276 "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient658);stroke-width:1.25;"
+ id="path607" /><path
+ d="M 0.818516 13.53465 L 27.46576 0.620288 L 38.01552 23.90252 L 11.55017 39.18148 L 0.818516 13.53465 z "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient674);stroke-width:1.8762;stroke-linecap:round;stroke-linejoin:round;"
+ id="path606" /></g><path
+ d="M 7.275697 18.3548 L 15.27896 37.08972 L 37.10606 27.08564 L 28.19333 7.259362 L 7.275697 18.3548 z "
+ style="font-size:12;fill:url(#radialGradient659);fill-rule:evenodd;stroke-width:1;"
+ id="path658" /><g
+ transform="matrix(0.631045,0,0,0.631045,14.80571,13.06199)"
+ style="font-size:12;"
+ id="g652"><path
+ d="M 0.818516 13.53465 L 27.37481 0.620286 L 38.10647 23.72063 L 11.73206 39.27243 L 0.818516 13.53465 z "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:url(#radialGradient618);fill-rule:evenodd;"
+ id="path653" /><path
+ d="M 11.55017 38.99959 L 19.37155 21.17413 "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:none;fill-rule:evenodd;stroke:#a299c0;stroke-width:0.75;"
+ id="path654" /><path
+ d="M 37.74268 23.90252 L 23.73696 19.99183 "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:none;fill-rule:evenodd;stroke:#a299c0;stroke-width:0.75;"
+ id="path655" /><path
+ d="M 27.19292 0.711238 L 23.19129 22.90211 L 0.818516 13.35276 "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient658);stroke-width:1.25;"
+ id="path656" /><path
+ d="M 0.818516 13.53465 L 27.46576 0.620288 L 38.01552 23.90252 L 11.55017 39.18148 L 0.818516 13.53465 z "
+ transform="matrix(0.992683,0,0,0.992683,0.369773,0.277376)"
+ style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient674);stroke-width:1.8762;stroke-linecap:round;stroke-linejoin:round;"
+ id="path657" /></g><g
+ transform="matrix(0.913514,0,0,0.913514,0.978449,2.68033)"
+ style="font-size:12;"
+ id="g665"><ellipse
+ cx="9.95861053"
+ cy="31.3146362"
+ rx="8.86725616"
+ ry="8.86725616"
+ transform="matrix(0.948718,0,0,0.948718,-0.23772,1.282547)"
+ style="fill:url(#radialGradient709);fill-rule:evenodd;stroke:url(#linearGradient713);"
+ id="path610" /><path
+ d="M 9.33377 26.68416 L 9.33377 34.77838 "
+ transform="translate(-7.812232e-2,7.8125e-2)"
+ style="fill:none;fill-opacity:0.4549;fill-rule:evenodd;stroke:#ffffff;stroke-width:2.225;stroke-linecap:round;stroke-linejoin:round;"
+ id="path703" /><path
+ d="M 5.24119 31.68622 L 9.33377 35.41501 L 13.5173 31.68622 L 13.5173 31.68622 "
+ transform="translate(-7.812232e-2,7.8125e-2)"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2.225;stroke-linecap:round;stroke-linejoin:round;"
+ id="path702" /><ellipse
+ cx="-14.7332878"
+ cy="27.6313133"
+ rx="5.45677233"
+ ry="3.54690266"
+ transform="matrix(1.065382,0,0,1.162882,24.94917,-4.76965)"
+ style="fill:url(#radialGradient705);fill-rule:evenodd;"
+ id="path704" /></g></svg>
diff --git a/knode/pics/ctlart.png b/knode/pics/ctlart.png
new file mode 100644
index 000000000..76836287d
--- /dev/null
+++ b/knode/pics/ctlart.png
Binary files differ
diff --git a/knode/pics/eyes.png b/knode/pics/eyes.png
new file mode 100644
index 000000000..6c696eb82
--- /dev/null
+++ b/knode/pics/eyes.png
Binary files differ
diff --git a/knode/pics/greyball.png b/knode/pics/greyball.png
new file mode 100644
index 000000000..745200696
--- /dev/null
+++ b/knode/pics/greyball.png
Binary files differ
diff --git a/knode/pics/greyballchk.png b/knode/pics/greyballchk.png
new file mode 100644
index 000000000..f16a11aff
--- /dev/null
+++ b/knode/pics/greyballchk.png
Binary files differ
diff --git a/knode/pics/group.png b/knode/pics/group.png
new file mode 100644
index 000000000..9ebd69759
--- /dev/null
+++ b/knode/pics/group.png
Binary files differ
diff --git a/knode/pics/group_big.png b/knode/pics/group_big.png
new file mode 100644
index 000000000..b0a5a2237
--- /dev/null
+++ b/knode/pics/group_big.png
Binary files differ
diff --git a/knode/pics/ignore.png b/knode/pics/ignore.png
new file mode 100644
index 000000000..0e06e3be2
--- /dev/null
+++ b/knode/pics/ignore.png
Binary files differ
diff --git a/knode/pics/mail.png b/knode/pics/mail.png
new file mode 100644
index 000000000..cf7160ab4
--- /dev/null
+++ b/knode/pics/mail.png
Binary files differ
diff --git a/knode/pics/newsubs.png b/knode/pics/newsubs.png
new file mode 100644
index 000000000..9b9819c61
--- /dev/null
+++ b/knode/pics/newsubs.png
Binary files differ
diff --git a/knode/pics/pgp-keys.png b/knode/pics/pgp-keys.png
new file mode 100644
index 000000000..4daed596c
--- /dev/null
+++ b/knode/pics/pgp-keys.png
Binary files differ
diff --git a/knode/pics/posting.png b/knode/pics/posting.png
new file mode 100644
index 000000000..83f375218
--- /dev/null
+++ b/knode/pics/posting.png
Binary files differ
diff --git a/knode/pics/snderr.png b/knode/pics/snderr.png
new file mode 100644
index 000000000..81ca0060e
--- /dev/null
+++ b/knode/pics/snderr.png
Binary files differ
diff --git a/knode/pics/stat_cncl.png b/knode/pics/stat_cncl.png
new file mode 100644
index 000000000..18ad41a64
--- /dev/null
+++ b/knode/pics/stat_cncl.png
Binary files differ
diff --git a/knode/pics/stat_edit.png b/knode/pics/stat_edit.png
new file mode 100644
index 000000000..edb4f2b4a
--- /dev/null
+++ b/knode/pics/stat_edit.png
Binary files differ
diff --git a/knode/pics/stat_saved.png b/knode/pics/stat_saved.png
new file mode 100644
index 000000000..85ecf0e33
--- /dev/null
+++ b/knode/pics/stat_saved.png
Binary files differ
diff --git a/knode/pics/stat_sent.png b/knode/pics/stat_sent.png
new file mode 100644
index 000000000..1e4fd842c
--- /dev/null
+++ b/knode/pics/stat_sent.png
Binary files differ
diff --git a/knode/resource.h b/knode/resource.h
new file mode 100644
index 000000000..3519d4437
--- /dev/null
+++ b/knode/resource.h
@@ -0,0 +1,43 @@
+/*
+ resource.h
+
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef RESSOURCE_H
+#define RESSOURCE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+//========= KNode Version Information ============
+
+#define KNODE_VERSION "0.10.9"
+
+//================= StatusBar ====================
+
+#define SB_MAIN 4000005
+#define SB_GROUP 4000010
+#define SB_FILTER 4000030
+
+
+//================== Folders =====================
+
+#define FOLD_DRAFTS 200010
+#define FOLD_SENT 200020
+#define FOLD_OUTB 200030
+
+
+#endif // RESOURCE_H
diff --git a/knode/smtpaccountwidget_base.ui b/knode/smtpaccountwidget_base.ui
new file mode 100644
index 000000000..9acad519c
--- /dev/null
+++ b/knode/smtpaccountwidget_base.ui
@@ -0,0 +1,232 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KNConfig::SmtpAccountWidgetBase</class>
+<widget class="KCModule">
+ <property name="name">
+ <cstring>SmtpAccountWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>306</width>
+ <height>320</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>mUseExternalMailer</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use external mailer</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>mServerLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Server:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mServer</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>mPortLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Port:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mPort</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>mUserLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;User:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mUser</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>mPasswordLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Pass&amp;word:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mPassword</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>mLogin</cstring>
+ </property>
+ <property name="text">
+ <string>Server requires &amp;authentication</string>
+ </property>
+ </widget>
+ <spacer row="7" column="2">
+ <property name="name">
+ <cstring>mSpacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KLineEdit" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mPassword</cstring>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="4" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mUser</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput" row="2" column="1">
+ <property name="name">
+ <cstring>mPort</cstring>
+ </property>
+ <property name="value">
+ <number>25</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="maxValue">
+ <number>65536</number>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mServer</cstring>
+ </property>
+ </widget>
+ <widget class="QButtonGroup" row="6" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>mEncGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Encryption</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>mEncNone</cstring>
+ </property>
+ <property name="text">
+ <string>None</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>mEncSSL</cstring>
+ </property>
+ <property name="text">
+ <string>SSL</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>mEncTLS</cstring>
+ </property>
+ <property name="text">
+ <string>TLS</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>mServer</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>SmtpAccountWidgetBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>mPort</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>SmtpAccountWidgetBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>mUseExternalMailer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SmtpAccountWidgetBase</receiver>
+ <slot>useExternalMailerToggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUser</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>SmtpAccountWidgetBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>mPassword</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>SmtpAccountWidgetBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>mLogin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SmtpAccountWidgetBase</receiver>
+ <slot>loginToggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mEncGroup</sender>
+ <signal>clicked(int)</signal>
+ <receiver>SmtpAccountWidgetBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot access="protected">useExternalMailerToggled(bool)</slot>
+ <slot access="protected">loginToggled(bool)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcmodule.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/knode/utilities.cpp b/knode/utilities.cpp
new file mode 100644
index 000000000..5f9c2e98c
--- /dev/null
+++ b/knode/utilities.cpp
@@ -0,0 +1,478 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#include <qlayout.h>
+#include <qregexp.h>
+#include <qapplication.h>
+#include <qcursor.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kglobalsettings.h>
+#include <kdebug.h>
+#include <kio/netaccess.h>
+#include <ktempfile.h>
+#include <kfiledialog.h>
+
+#include "knwidgets.h"
+#include "knglobals.h"
+#include "utilities.h"
+
+
+
+//================================================================================
+
+KNFile::KNFile(const QString& fname)
+ : QFile(fname), filePos(0), readBytes(0)
+{
+ buffer.resize(512);
+ dataPtr=buffer.data();
+ dataPtr[0]='\0';
+}
+
+
+KNFile::~KNFile()
+{
+}
+
+
+const QCString& KNFile::readLine()
+{
+ filePos=at();
+ readBytes=QFile::readLine(dataPtr, buffer.size()-1);
+ if(readBytes!=-1) {
+ while ((dataPtr[readBytes-1]!='\n')&&(static_cast<uint>(readBytes+2)==buffer.size())) { // don't get tricked by files without newline
+ at(filePos);
+ if (!increaseBuffer() ||
+ (readBytes=QFile::readLine(dataPtr, buffer.size()-1))==-1) {
+ readBytes=1;
+ break;
+ }
+ }
+ } else
+ readBytes=1;
+
+ dataPtr[readBytes-1] = '\0';
+ return buffer;
+}
+
+
+const QCString& KNFile::readLineWnewLine()
+{
+ filePos=at();
+ readBytes=QFile::readLine(dataPtr, buffer.size()-1);
+ if(readBytes!=-1) {
+ while ((dataPtr[readBytes-1]!='\n')&&(static_cast<uint>(readBytes+2)==buffer.size())) { // don't get tricked by files without newline
+ at(filePos);
+ if (!increaseBuffer() ||
+ (readBytes=QFile::readLine(dataPtr, buffer.size()-1))==-1) {
+ dataPtr[0] = '\0';
+ break;
+ }
+ }
+ }
+ else dataPtr[0] = '\0';
+
+ return buffer;
+}
+
+
+int KNFile::findString(const char *s)
+{
+ QCString searchBuffer;
+ searchBuffer.resize(2048);
+ char *buffPtr = searchBuffer.data(), *pos;
+ int readBytes, currentFilePos;
+
+ while (!atEnd()) {
+ currentFilePos = at();
+ readBytes = readBlock(buffPtr, 2047);
+ if (readBytes == -1)
+ return -1;
+ else
+ buffPtr[readBytes] = 0; // terminate string
+
+ pos = strstr(buffPtr,s);
+ if (pos == 0) {
+ if (!atEnd())
+ at(at()-strlen(s));
+ else
+ return -1;
+ } else {
+ return currentFilePos + (pos-buffPtr);
+ }
+ }
+
+ return -1;
+}
+
+
+bool KNFile::increaseBuffer()
+{
+ if(buffer.resize(2*buffer.size())) {;
+ dataPtr=buffer.data();
+ dataPtr[0]='\0';
+ kdDebug(5003) << "KNFile::increaseBuffer() : buffer doubled" << endl;
+ return true;
+ }
+ else return false;
+}
+
+
+//===============================================================================
+
+QString KNSaveHelper::lastPath;
+
+KNSaveHelper::KNSaveHelper(QString saveName, QWidget *parent)
+ : p_arent(parent), s_aveName(saveName), file(0), tmpFile(0)
+{
+}
+
+
+KNSaveHelper::~KNSaveHelper()
+{
+ if (file) { // local filesystem, just close the file
+ delete file;
+ } else
+ if (tmpFile) { // network location, initiate transaction
+ tmpFile->close();
+ if (KIO::NetAccess::upload(tmpFile->name(),url, 0) == false)
+ KNHelper::displayRemoteFileError();
+ tmpFile->unlink(); // delete temp file
+ delete tmpFile;
+ }
+}
+
+
+QFile* KNSaveHelper::getFile(const QString &dialogTitle)
+{
+ url = KFileDialog::getSaveURL(lastPath + s_aveName, QString::null, p_arent, dialogTitle);
+
+ if (url.isEmpty())
+ return 0;
+
+ lastPath = url.upURL().url();
+
+ if (url.isLocalFile()) {
+ if (QFileInfo(url.path()).exists() &&
+ (KMessageBox::warningContinueCancel(knGlobals.topWidget,
+ i18n("<qt>A file named <b>%1</b> already exists.<br>Do you want to replace it?</qt>").arg(url.path()),
+ dialogTitle, i18n("&Replace")) != KMessageBox::Continue)) {
+ return 0;
+ }
+
+ file = new QFile(url.path());
+ if(!file->open(IO_WriteOnly)) {
+ KNHelper::displayExternalFileError();
+ delete file;
+ file = 0;
+ }
+ return file;
+ } else {
+ tmpFile = new KTempFile();
+ if (tmpFile->status()!=0) {
+ KNHelper::displayTempFileError();
+ delete tmpFile;
+ tmpFile = 0;
+ return 0;
+ }
+ return tmpFile->file();
+ }
+}
+
+
+//===============================================================================
+
+QString KNLoadHelper::l_astPath;
+
+KNLoadHelper::KNLoadHelper(QWidget *parent)
+ : p_arent(parent), f_ile(0)
+{
+}
+
+
+KNLoadHelper::~KNLoadHelper()
+{
+ delete f_ile;
+ if (!t_empName.isEmpty())
+ KIO::NetAccess::removeTempFile(t_empName);
+}
+
+
+KNFile* KNLoadHelper::getFile( const QString &dialogTitle )
+{
+ if (f_ile)
+ return f_ile;
+
+ KURL url = KFileDialog::getOpenURL(l_astPath,QString::null,p_arent,dialogTitle);
+
+ if (url.isEmpty())
+ return 0;
+
+ l_astPath = url.url(-1);
+ l_astPath.truncate(l_astPath.length()-url.fileName().length());
+
+ return setURL(url);
+}
+
+
+KNFile* KNLoadHelper::setURL(KURL url)
+{
+ if (f_ile)
+ return f_ile;
+
+ u_rl = url;
+
+ if (u_rl.isEmpty())
+ return 0;
+
+ QString fileName;
+ if (!u_rl.isLocalFile()) {
+ if (KIO::NetAccess::download(u_rl, t_empName, 0))
+ fileName = t_empName;
+ } else
+ fileName = u_rl.path();
+
+ if (fileName.isEmpty())
+ return 0;
+
+ f_ile = new KNFile(fileName);
+ if(!f_ile->open(IO_ReadOnly)) {
+ KNHelper::displayExternalFileError();
+ delete f_ile;
+ f_ile = 0;
+ }
+ return f_ile;
+}
+
+
+//===============================================================================
+
+
+// **** keyboard selection dialog *********************************************
+int KNHelper::selectDialog(QWidget *parent, const QString &caption, const QStringList &options, int initialValue)
+{
+ KDialogBase *dlg=new KDialogBase(KDialogBase::Plain, caption, KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok, parent);
+ QFrame *page = dlg->plainPage();
+ QHBoxLayout *pageL = new QHBoxLayout(page,8,5);
+
+ KNDialogListBox *list = new KNDialogListBox(true, page);
+ pageL->addWidget(list);
+
+ QString s;
+ for ( QStringList::ConstIterator it = options.begin(); it != options.end(); ++it ) {
+ s = (*it);
+ s.replace(QRegExp("&"),""); // remove accelerators
+ list->insertItem(s);
+ }
+
+ list->setCurrentItem(initialValue);
+ list->setFocus();
+ restoreWindowSize("selectBox", dlg, QSize(247,174));
+
+ int ret;
+ if (dlg->exec())
+ ret = list->currentItem();
+ else
+ ret = -1;
+
+ saveWindowSize("selectBox", dlg->size());
+ delete dlg;
+ return ret;
+}
+
+// **** window geometry managing *********************************************
+
+void KNHelper::saveWindowSize(const QString &name, const QSize &s)
+{
+ KConfig *c=knGlobals.config();
+ c->setGroup("WINDOW_SIZES");
+ c->writeEntry(name, s);
+}
+
+
+void KNHelper::restoreWindowSize(const QString &name, QWidget *d, const QSize &defaultSize)
+{
+ KConfig *c=knGlobals.config();
+ c->setGroup("WINDOW_SIZES");
+
+ QSize s=c->readSizeEntry(name,&defaultSize);
+
+ if(s.isValid()) {
+ QRect max = KGlobalSettings::desktopGeometry(QCursor::pos());
+ if ( s.width() > max.width() ) s.setWidth( max.width()-5 );
+ if ( s.height() > max.height() ) s.setHeight( max.height()-5 );
+ d->resize(s);
+ }
+}
+
+// **** scramble password strings **********************************************
+
+const QString KNHelper::encryptStr(const QString& aStr)
+{
+ uint i,val,len = aStr.length();
+ QCString result;
+
+ for (i=0; i<len; i++)
+ {
+ val = aStr[i] - ' ';
+ val = (255-' ') - val;
+ result += (char)(val + ' ');
+ }
+
+ return result;
+}
+
+
+const QString KNHelper::decryptStr(const QString& aStr)
+{
+ return encryptStr(aStr);
+}
+
+// **** rot13 *******************************************************************
+
+QString KNHelper::rot13(const QString &s)
+{
+ QString r(s);
+
+ for (int i=0; (uint)i<r.length(); i++) {
+ if ( r[i] >= QChar('A') && r[i] <= QChar('M') ||
+ r[i] >= QChar('a') && r[i] <= QChar('m') )
+ r[i] = (char)((int)QChar(r[i]) + 13);
+ else
+ if ( r[i] >= QChar('N') && r[i] <= QChar('Z') ||
+ r[i] >= QChar('n') && r[i] <= QChar('z') )
+ r[i] = (char)((int)QChar(r[i]) - 13);
+ }
+
+ return r;
+}
+
+// **** text rewraping *********************************************************
+
+int findBreakPos(const QString &text, int start)
+{
+ int i;
+ for(i=start;i>=0;i--)
+ if(text[i].isSpace())
+ break;
+ if(i>0)
+ return i;
+ for(i=start;i<(int)text.length();i++) // ok, the line is to long
+ if(text[i].isSpace())
+ break;
+ return i;
+}
+
+
+void appendTextWPrefix(QString &result, const QString &text, int wrapAt, const QString &prefix)
+{
+ QString txt=text;
+ int breakPos;
+
+ while(!txt.isEmpty()) {
+
+ if((int)(prefix.length()+txt.length()) > wrapAt) {
+ breakPos=findBreakPos(txt,wrapAt-prefix.length());
+ result+=(prefix+txt.left(breakPos)+"\n");
+ txt.remove(0,breakPos+1);
+ } else {
+ result+=(prefix+txt+"\n");
+ txt=QString::null;
+ }
+ }
+}
+
+
+QString KNHelper::rewrapStringList(QStringList text, int wrapAt, QChar quoteChar, bool stopAtSig, bool alwaysSpace)
+{
+ QString quoted, lastPrefix, thisPrefix, leftover, thisLine;
+ int breakPos;
+
+ for(QStringList::Iterator line=text.begin(); line!=text.end(); ++line) {
+
+ if(stopAtSig && (*line)=="-- ")
+ break;
+
+ thisLine=(*line);
+ if (!alwaysSpace && (thisLine[0]==quoteChar))
+ thisLine.prepend(quoteChar); // second quote level without space
+ else
+ thisLine.prepend(quoteChar+' ');
+
+ thisPrefix=QString::null;
+ QChar c;
+ for(int idx=0; idx<(int)(thisLine.length()); idx++) {
+ c=thisLine.at(idx);
+ if( (c==' ') ||
+ (c==quoteChar) || (c=='>') ||(c=='|') || (c==':') || (c=='#') || (c=='[') || (c=='{'))
+ thisPrefix.append(c);
+ else
+ break;
+ }
+
+ thisLine.remove(0,thisPrefix.length());
+ thisLine = thisLine.stripWhiteSpace();
+
+ if(!leftover.isEmpty()) { // don't break paragraphs, tables and quote levels
+ if(thisLine.isEmpty() || (thisPrefix!=lastPrefix) || thisLine.contains(" ") || thisLine.contains('\t'))
+ appendTextWPrefix(quoted, leftover, wrapAt, lastPrefix);
+ else
+ thisLine.prepend(leftover+" ");
+ leftover=QString::null;
+ }
+
+ if((int)(thisPrefix.length()+thisLine.length()) > wrapAt) {
+ breakPos=findBreakPos(thisLine,wrapAt-thisPrefix.length());
+ if(breakPos < (int)(thisLine.length())) {
+ leftover=thisLine.right(thisLine.length()-breakPos-1);
+ thisLine.truncate(breakPos);
+ }
+ }
+
+ quoted+=thisPrefix+thisLine+"\n";
+ lastPrefix=thisPrefix;
+ }
+
+ if (!leftover.isEmpty())
+ appendTextWPrefix(quoted, leftover, wrapAt, lastPrefix);
+
+ return quoted;
+}
+
+// **** misc. message-boxes **********************************************************
+
+void KNHelper::displayInternalFileError(QWidget *w)
+{
+ KMessageBox::error((w!=0)? w : knGlobals.topWidget, i18n("Unable to load/save configuration.\nWrong permissions on home folder?\nYou should close KNode now to avoid data loss."));
+}
+
+
+void KNHelper::displayExternalFileError(QWidget *w)
+{
+ KMessageBox::error((w!=0)? w : knGlobals.topWidget, i18n("Unable to load/save file."));
+}
+
+
+void KNHelper::displayRemoteFileError(QWidget *w)
+{
+ KMessageBox::error((w!=0)? w : knGlobals.topWidget, i18n("Unable to save remote file."));
+}
+
+
+void KNHelper::displayTempFileError(QWidget *w)
+{
+ KMessageBox::error((w!=0)? w : knGlobals.topWidget, i18n("Unable to create temporary file."));
+}
diff --git a/knode/utilities.h b/knode/utilities.h
new file mode 100644
index 000000000..31f83d989
--- /dev/null
+++ b/knode/utilities.h
@@ -0,0 +1,166 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ 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.
+ 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, US
+*/
+
+#ifndef UTILITIES_H
+#define UTILITIES_H
+
+#include <kurl.h>
+
+#include <qfile.h>
+
+#include <qglobal.h>
+#include <qptrvector.h>
+#include <qptrlist.h>
+
+class QWidget;
+class QString;
+class QChar;
+class QStringList;
+class QSize;
+
+class KTempFile;
+
+
+//*****************************************************************************
+// utility classes
+//*****************************************************************************
+
+/** clone of QSortedList... */
+template<class type> class Q_EXPORT QSortedVector : public QPtrVector<type>
+{
+public:
+ QSortedVector() {}
+ QSortedVector ( uint size ) : QPtrVector<type>(size) {}
+ QSortedVector( const QSortedVector<type> &l ) : QPtrVector<type>(l) {}
+ ~QSortedVector() { QPtrVector<type>::clear(); }
+ QSortedVector<type> &operator=(const QSortedVector<type> &l)
+ { return (QSortedVector<type>&)QPtrList<type>::operator=(l); }
+
+ virtual int compareItems( QPtrCollection::Item s1, QPtrCollection::Item s2 )
+ { if ( *((type*)s1) == *((type*)s2) ) return 0; return ( *((type*)s1) < *((type*)s2) ? -1 : 1 ); }
+};
+
+
+//==============================================================================
+
+
+class KNFile : public QFile {
+
+ public:
+ KNFile(const QString& fname=QString::null);
+ ~KNFile();
+ const QCString& readLine();
+ const QCString& readLineWnewLine();
+ /** searches for the string from the current file position
+ returns -1 when the string wasn't found. */
+ int findString(const char *s);
+
+ protected:
+ bool increaseBuffer();
+
+ QCString buffer;
+ char *dataPtr;
+ int filePos, readBytes;
+};
+
+
+//========================================================================================
+
+
+class KNSaveHelper {
+
+public:
+
+ KNSaveHelper(QString saveName, QWidget *parent);
+ ~KNSaveHelper();
+
+ /** returns a file open for writing */
+ QFile* getFile(const QString &dialogTitle);
+
+private:
+
+ QWidget *p_arent;
+ QString s_aveName;
+ KURL url;
+ QFile* file;
+ KTempFile* tmpFile;
+ static QString lastPath;
+
+};
+
+
+//========================================================================================
+
+
+class KNLoadHelper {
+
+public:
+
+ KNLoadHelper(QWidget *parent);
+ ~KNLoadHelper();
+
+ /** opens a file dialog and returns a file open for reading */
+ KNFile* getFile( const QString &dialogTitle );
+ /** tries to access the file specified by the url and returns
+ a file open for reading */
+ KNFile* setURL(KURL url);
+ /** returns the file after getFile(QString) of setURL(url) was called */
+ KNFile* getFile()const { return f_ile; };
+ KURL getURL() const { return u_rl; };
+
+private:
+
+ QWidget *p_arent;
+ KURL u_rl;
+ KNFile *f_ile;
+ QString t_empName;
+ static QString l_astPath;
+
+};
+
+
+//========================================================================================
+
+
+class KNHelper {
+
+public:
+
+ /** list selection dialog, used instead of a popup menu
+ when a select action is called via the keyboard.
+ returns -1 when the user canceled the dialog. */
+ static int selectDialog(QWidget *parent, const QString &caption, const QStringList &options, int initialValue);
+
+ static void saveWindowSize(const QString &name, const QSize &s);
+ static void restoreWindowSize(const QString &name, QWidget *d, const QSize &defaultSize);
+
+ static const QString encryptStr(const QString& aStr);
+ static const QString decryptStr(const QString& aStr);
+ static QString rot13(const QString &s);
+
+ /** used for rewarping a text when replying to a message or inserting a file into a box */
+ static QString rewrapStringList(QStringList text, int wrapAt, QChar quoteChar, bool stopAtSig, bool alwaysSpace);
+
+ /** use this for all internal files */
+ static void displayInternalFileError(QWidget *w=0);
+ /** use this for all external files */
+ static void displayExternalFileError(QWidget *w=0);
+ /** use this for remote files */
+ static void displayRemoteFileError(QWidget *w=0);
+ /** use this for error on temporary files */
+ static void displayTempFileError(QWidget *w=0);
+
+};
+
+#endif