diff --git a/LAST_ENTERPRISE_SYNC b/LAST_ENTERPRISE_SYNC new file mode 100644 index 00000000..2e52917a --- /dev/null +++ b/LAST_ENTERPRISE_SYNC @@ -0,0 +1 @@ +Revision 1170137 diff --git a/Makefile.am.in b/Makefile.am.in index 39ffd008..9cea71b0 100644 --- a/Makefile.am.in +++ b/Makefile.am.in @@ -23,7 +23,7 @@ COMPILE_BEFORE_certmanager = libkdenetwork libkpgp COMPILE_BEFORE_korganizer = libkdepim libkpimidentities libkpimexchange kgantt COMPILE_BEFORE_kaddressbook = libkdepim certmanager akregator COMPILE_BEFORE_kandy = libkdepim -COMPILE_BEFORE_kmail= libkdepim libkpimidentities certmanager libkpgp libkmime indexlib +COMPILE_BEFORE_kmail= libkdepim libkpimidentities certmanager libkpgp libkmime COMPILE_BEFORE_knode= libkdepim libkpgp libkmime COMPILE_BEFORE_karm = libkdepim kresources COMPILE_BEFORE_plugins = kmail libkdepim libkcal diff --git a/NewsLog.txt b/NewsLog.txt new file mode 100644 index 00000000..10034235 --- /dev/null +++ b/NewsLog.txt @@ -0,0 +1,2147 @@ +Friday, August 27th 2010 + +Problems addressed +------------------ + +* Improve the error message when regenerating a corrupt index: + Show one error message for all folders on startup, instead of multiple error messages +* Possibly fix a crash when selecting or deselecting resources when the side-by-side view is active + + +Friday, August 20th 2010 + +Problems addressed +------------------ + +* kolab/issue4332: event update mail which really is an invitation (rt#6125) +* kolab/issue4484: Send S/MIME encrypted mail to address without certificate, search for external certificate button (rt#5659) +* kolab/issue4485: Try to create a contact without writable folder; edit dialog is closed +* kolab/issue4479: Change used kontact plugins crash (Kontact::MainWindow::addPlugin at mainwindow.cpp:719) (rt#6193) +* kolab/issue4333: Renaming of a shared calendar folder failed (rt#6121) +* Add more debug output to help finding the cause of kolab/issue4498 +* Regenerate the mail index earlier when finding inconsistencies in it + + +Friday, August 13th 2010 + +Problems addressed +------------------ + +* kolab/issue4484(Improvement): Send S/MIME encrypted mail to address without certificate, search for external certificate button (rt#5659) +* kolab/issue4404: Reminder: the event entries of the events which triggered an alarm should be selected + + +Friday, August 5th 2010 + +Problems addressed +------------------ + +* kolab/issue4203(mainly): A reminder can contain non-existing events (rt#6051) +* kolab/issue4335: D'n'D a mail attachment with the icon is not working (rt#6126) +* kolab/issue3932: Kontact should show a warning, if a user tries to accept an invitation of a past event (rt#5833) +* kolab/issue4005(improved): Attachments should open in read-only folders (rt#5915) +* Speed up changing Kolab incidences in KOrganizer by only sending one change instead of two + to the server (see kolab/issue2109) + +kk4: +* kolab/issue4469: (Kontact loses shared calendar folders (type changed)(rt#6174) + +kk6: +* Add more debug output to help finding the cause of kolab/issue4498 + + +Friday, July 30th 2010 + +Problems addressed +------------------ + +* kolab/issue4261(Mainly): Wish: Print whole day events at top of a day print (rt#6084) +* kolab/issue3974: Clicking on an ics/vcs-attachment doesn't import or display the events of the attachment (rt#5812) +* kolab/issue4429: Key selection dialog is too small. 'Abbrechen' button is cut + + +Friday, July 23th 2010 + +Problems addressed +------------------ + +* kolab/issue4335 (partly): D'n'D a mail attachment with the icon is not working (rt#6126) +* kolab/issue3908 (partly): Mail view and print layout problem if description of attachment is to long (#5872) +* kolab/issue4455: calendar activate/deactivate handling broken +* kolab/issue4282: Adding a new event from an invitation update mail shows unnecssary message + dialog 'store/throw away' (rt#6094) +* Fixed a few crashes in KOrganizer and a crash in KMail +* Improvements in renaming calendar folders + + +Friday, July 16th 2010 + +Problems addressed +------------------ + +* kolab/issue4447: Deleting contact from distribution list with DELETE key and Remove button + + +Friday, July 9th 2010 + +Problems addressed +------------------ + +* kolab/issue4445: Importing a events into an existing resource and no resource activated, 1000x error messages +* kolab/issue4437: Wrong update mail (other attendees are removed) after double accepting of an event +* kolab/issue4406: Change write to read rights of a shared calendar folder, can lead to a focus problem (rt#6167) +* kolab/issue4343 (improved): Wish: global audio file for all reminder (rt#6133) +* kolab/issue4406: Change write to read rights of a shared calendar folder, can lead to a focus problem (rt#6167) + +Friday, July 1st 2010 + +Problems addressed +------------------ + +* Fix crash when composing a message, when compiled with GCC >= 4.4 +* When adding many incidences, don't ask which calendar to put it in, for each incidence + +kk2: +* kolab/issue4442: Some broken mails double themself in a sync.(rt#6183) + + +Friday, June 25th 2010 + +Problems addressed +------------------ + +* kolab/issue4417: Resizing a one day event changes the event wrongly (rt#6064) +* kolab/issue4401(mainly): The contacts free-busy-url is not saved in the contact mail but in kde/share/apps/korganizer/freebusyurls +* kolab/issue2652: Kontact generate invalid charset declaration for attached text files +* kolab/issue4085: Wish: Mail attachments only in the header of a mail (rt#5987) + + +Friday, June 18th 2010 + +Problems addressed +------------------ + +* kolab/issue4031: sending mail to OpenPGP-only and S/MIME-only recipient sends only OpenPGP (rt#5938) +* kolab/issue4369: KMail: Pressing Cancel in untrusted certificates warning dialog doesn't cancel, but continue +* kolab/issue4411: todos alarm: configured alarm changed after opening the todo twice +* kolab/issue3726(mainly): A changed Navigator toolbars is displayed without changes after restart (rt#5805) +* kolab/issue4032: Todos summary shows no todos in offline (rt#5944) +* kolab/issue4418: details view of a large multipart/mixed mail shows empty Message-ID (rt#5842) +* kolab/issue4245: Wish: searchbar should also search for mail addresses (rt#6071) +* kolab/issue4240: Wish: Maillist icons for invitations and update mails (rt#6065) +* kolab/issue4419: Accept an invitation with set "Exchange compatible", update mail contains an untranslated + "Accepted" in the subject. (rt#5903) +kk3: +* kolab/issue4407: event with attachment: png/zip attachment size changes + +kk4: +* kolab/issue4410: event attachment cannot be opend after restart (rt#6172) + +Friday, June 11th 2010 + +Problems addressed +------------------ + +* Add a context menu for business card attachments in mails +* Remove groupwise and groupdav resources +* kolab/issue3411: kmail drag and drop (DnD) of email addresses with umlauts from reader to composer exposes encoded-word encoding +* kolab/issue4352: Wish: Reminder: horizontal slider between event list and event details (rt#6142) +* kolab/issue4414: print todos: in the result the priority and summary header overlap +* kolab/issue4413: Todos: Print, but print events is selected +* kolab/issue4408: event with attachment: textfield selection creates invitation without attachment + + +Friday, June 4th 2010 + +Problems addressed +------------------ + +* kolab/issue4400: Composer: create distribution list adds the dist list just into the default resource +* kolab/issue4094: Scrollbar position changes, if a new mail is synced into the folder (rt#5997) +* kolab/issue3911: mail enterprise header: cannot mail to the second to address +* kolab/issue4350: Sometimes the date headers in the agenda view are grainy and doubled (rt#6140) +* kolab/issue3411(partly): kmail drag and drop (DnD) of email addresses with umlauts from reader to composer exposes + encoded-word encoding + + +Friday, May 28th 2010 + +Problems addressed +------------------ + +* kolab/issue4365: Wish: Edit Todo: Complete button. (rt#6156) +* kolab/issue4372: Rename context menu entry "Copy Link Address" to "Copy Email Address(es) +* kolab/issue4281: Composer: create distlist, for every entry a resource chooser appears (rt#6098) +* KDE bug 238945: Crash while displaying an invitation mail + + +Friday, May 21th 2010 + +Problems addressed +------------------ + +* kolab/issue4256: Event attendees tab, Select Addressee.. Enter Filter, empty topics are shown. (rt#6081) +* kolab/issue4340: event edit dialog: Selected attachment should be deleted with shortcut "Remove"/German "Entf"(rt#6124) +* kolab/issue4381: Imap cache corruption giving crash (SIGFPE) on each sync for email without length +* kolab/issue3963: Alarm of an recurring task doesn't pop up (rt#5891) +* kolab/issue4379: Event dialog: Added attendees of a dist list are removed, handling wrong (rt#6159) +* kolab/issue4307: D'n'D a contact to calendar/todo sidebar icon added an empty attachment +* kolab/issue4359 (mainly): Invitation, canceled at the resource dialog, but organizer gets accept mail (rt#6147) +* kolab/issue4316: Pressing Cancel in Recurrence dialog does not discard changes +* kolab/issue4093: In "enterprise" mail header CC and BCC addresses are displayed under the TO field (rt#5996) +* kolab/issue4366: Use own fonts for printing +* kolab/issue4322: Add signature to current line and linebreak (entered at wrong place (rt#6113) +* kolab/issue4264: Advanced alarm dialog of an event/a todo: Change German "Entfernen ..." into "Entfernen" (rt#6087) +* kolab/issue4286: Agenda view shows mix of long and short date format (rt#6105) +* Improve error message if renaming a shared folder failed (see kolab/issue4333) + + +Friday, May 7th 2010 + +Problems addressed +------------------ + +* kolab/issue4315: Add attachment for event/task: Store inline should be set by default +* kolab/issue4314: Can't open attachment of an invitation mail (rt#6109) +* kolab/issue4317: Open invitation window closes, if an attachment is opened (rt#6111) +* kolab/issue3882: Composer: Pressing TAB in the address completion popup list doesn't change to the next addressbook +* kolab/issue4338: Toggle alarm "on" should create a 15 mins for start alarm. (rt#6131) +* kolab/issue4119: Enter a distribution list as attendee into an event, should expand the members of the dist list into + the attendee field (rt#6027) +* kolab/issue4349: Wish: remove standard event icon in the agenda view (rt#6139) +* kolab/issue4363: An event with one attendee doesn't display the organizer in preview pane +* kolab/issue4339: Small translation improvement: German "Kalender/Termin(iCS.." should be "Termin/Kalender (ICS.." (rt#6122) +* kolab/issue4331: email addresses with comma or special characters should be displayed with quotes in the mail headers (rt#6128) +* kolab/issue3999: Kontact sometimes changes from mail part to the calendar part +* kolab/issue4093(partly): In "enterprise" mail header CC and BCC addresses are displayed under the TO field (rt#5996) +* kolab/issue4351: Reminder: Focus after pressing remind me later, should be on the next event (rt#6141) +* kolab/issue4350: Sometimes the date headers in the agenda view are grainy and doubled (rt#6140) +* Add support for alias email addresses in the identity configuration + + +Friday, April 9th 2010 + +Problems addressed +------------------ + +* kolab/issue4267: information about the alarm are missing in the event views (rt#6090) +* kolab/issue2470: Calendar: "Next Month" button doesn't change the displayed month (rt#5961) +* kolab/issue3882(partly): Composer: Pressing TAB in the address completion popup list doesn't change to the next + addressbook +* kolab/issue4280: Due time "00:00" is set for no time associated todos if todo resource disabled+enabled (rt#6096) +* kolab/issue4301: calendar display for single events isn't correct +* kolab/issue2762(partly): Sometimes whole day events overlap in the view (rt#5936) +* kolab/issue4219(partly): Tasks: Copy, Select nothing, Paste of a subtask copies it as subtask + + +Friday, April 1st 2010 + +Problems addressed +------------------ + +* kolab/issue4270: alarm of an event with two lines shows strange half line title.(rt#6093) +* kolab/issue4271: In korganizer's resource view, creating a subresource containing a / originates a crash +* kolab/issue4268: Calendar: Black triangle shows that events are somewhere up, but the user scrolled already to 0:00 (rt#6091) +* kolab/issue4267(partly): information about the alarm are missing in the event views (rt#6090) +* kolab/issue4266: birthday resource should be named "Geburtstage" if German language is used (rt#6089) +* kolab/issue3099: Replying to stored mail crashes Kontact +* kolab/issue4284: Journal: Link Add Journal entry in the week view is not working (rt#6103) +* kolab/issue4285: The default size of the reminder should be larger (rt#6104) +* kolab/issue3469: KNotes: Deleted notes reappear +* kolab/issue4272: Sometimes it is not possible to delete a folder +* kolab/issue4248: Configure Filter dialog: Some filter conditions are not translated to German (rt#6074) + + +Friday, March 26th 2010 + +Problems addressed +------------------ + +* kolab/issue4229: Kontact hangs when switching to calendar with incorrect interval tag in event (rt#6063) +* kolab/issue4230: Todo: alarm 0 mins vs 1 min in advanced alarm dialog +* kolab/issue4220: Calander tabular view , side-by-side tab Move an event asks for resource +* kolab/issue4064: Task with associated due time "00:00" isn't displayed in all calendar views (rt#5974) +* kolab/issue4224: event viewer dialog to small (rt#6060) +* kolab/issue4149: Search Directory Services and comma-formated names (rt#5954) +* kolab/issue3599: KNotes: Crash in KNotesResourceManager::deleteNote at resourcemanager.cpp:109 +* kolab/issue4247: Composer: After removing an attachment is the focus not on the next attachment (rt#6073) +* kolab/issue4246: Not possible to add umlauts to the move message dialog (rt#6072) +* kolab/issue3902: Delete a folder while syncing leads to crash +* kolab/issue4250: Edit "Out of office" replies: Resend notification only after: days is missing (rt#6076) +* kolab/issue4249: Crash while printing a mail without preview pane (rt#6075) +* kolab/issue4142: Composer: view all fields layout and small problems (rt#6032) +* kolab/issue4012: Calender view update problem, if the same appointment is moved on the server in other folders (rt#5930) + + +Friday, March 19th 2010 + +Problems addressed +------------------ + +* kolab/issue4207: alarm time textfield in advanced alarm dialog should be 5-digits (rt#6055) +* kolab/issue4200: Reminder date sorting doesn't recognize different month (rt#6047) +* kolab/issue4215: Events ending at 00h appear in the wrong day in day-view +* kolab/issue4054(needs to patch kdelibs, see issue): Umlauts and sending contacts problem (rt#5956) +* kolab/issue4060: Wish: Auto-spellchecking: action to add new suggestion in context menu (rt#5969) +* kolab/issue4224: event viewer dialog to small (rt#6060) +* kolab/issue4218: Tasks: Copy to of a recurring event forgets the recurrence +* kolab/issue4225: small month view: selected weekend days and holidays should be colored red in blue (rt#6061) +* kolab/issue4194(partly): Task without starttime should have default alarm type "before the end" (rt#6045) + + +Friday, March 12th 2010 + +Problems addressed +------------------ + +* kolab/issue4195: Copy+Paste of a todo with recurrence should not change the enddate (rt#6046) +* kolab/issue4192: Wish: Rename German "Start" into "Beginn" in a event advanced alarm dialog (rt#6043) +* kolab/issue3706: copy+ paste of an appointment in the monthview creates the appointment at a wrong time (rt#6042) +* kolab/issue4194: Task without starttime should have default alarm type "before the end" (rt#6045) +* kolab/issue4109: Task with recurrence: Starttime not saved (rt#6019) +* kolab/issue3963: Alarm of an recurring task doesn't pop up (rt#5891) +* kolab/issue4066: Quota warning color coming later than server warning dialog (rt#5955) +* kolab/issue4098: Cannot import "archived" mails into local folders +* kolab/issue3596: Display a special text instead of a Toltec binary invitation email (rt#5766) +* kolab/issue4203: A reminder can contain non-existing events.(rt#6051) +* kolab/issue4201: Spellchecking in composer:RMB->suggestion deletes wrong word (rt#6050) +* kolab/issue4187: Crash while switching between signed and unsigned mail (rt#5980) + + +Friday, March 5th 2010 + +Problems addressed +------------------ + +* kolab/issue4102: Wish: type of attendee of an event should be displayed in the small event pane (rt#6004) +* kolab/issue4105: Three warning dialogs appears, if I try to create a todo and have deactivated the Task resources +* kolab/issue4107: In workweek mode clicking on the month pane week number selects whole week (rt#6013) +* kolab/issue1765: In parallel calender view there is no need to ask in which resource folder a new event should get. (rt#5816) +* kolab/issue4110: Task with recurrence: due of a selected task in calendar doesn't match the right date (rt#6020) +* kolab/issue4118(partly): Wish: next/previous week buttons in the small month pane (rt#6024) +* kolab/issue3874: Calendar Timeline View: Resize and deactive and activate of a calendar resource changes the duration of + the event to 15 mins +* kolab/issue4066(partly): Quota warning color coming later than server warning dialog (rt#5955) +* kolab/issue2277: Usability problem with "enterprise headers" airmail icon +* kolab/issue4177: A hidden reminder dialog should reappear, if a new alarm is triggered +* kolab/issue4181: Wish: an additional column "folder" in the todos list in the todo component +* kolab/issue4142(partly): Add some tooltips to KMail's composer +* kolab/issue4096: User with write access to a calendar folder should be able to change the events + (even if he isnot the organizer) (rt#5999) +* kolab/issue4148: New event, attendee dialog: entry with comma format should stay in comma format in the attendee pane (rt#6035) +* kolab/issue4128: side-by-side view: scrollbar slider too small + + +Friday, February 26th 2010 + +Problems addressed +------------------ + +* kolab/issue4055: Workweek after restart is today and next 4 days (rt#5958) +* kolab/issue4053: reminder of an event with recurrence shows wrong date (rt#5953) +* kolab/issue4112: Event/Task conflict dialog is too difficult (rt#6009) +* kolab/issue4060: Wish: Auto-spellchecking: action to add new suggestion in context menu (rt#5969) +* When recovering a written message after a crash, don't delete the autosave file immediatley, to avoid data + loss in case KMail crashes again before the next autosave is triggered + + +Friday, February 19th 2010 + +Problems addressed +------------------ + +* kolab/issue4105: (partly) Three warning dialogs appears, if I try to create an event and have deactivated + the Task resources. +* kolab/issue1765: (partly) In parallel calender view there is no need to ask in which resource folder a new event + should get (rt#5816) +* kolab/issue4117: Reminder: Preselected "remind me later" time should always be 5 mins (rt#6017) +* kolab/issue3597: Event reminder of a deleted event appears (rt#6010) +* kolab/issue4108: Reminder option "remind me later" should work even if the user closed the session (rt#6016) +* kolab/issue4125: (mainly) Event with recurrence: Changing attendee of one event, changes it for every event (rt#6029) +* kolab/issue2079: Search for mails: Cannot create a new search if an old search is selected +* kolab/issue4056: calendar month view: It is not visible in the big calendar, which day is selected in the + small month calendar (rt#5960) +* kolab/issue4053: (partly) reminder of an event with recurrence shows wrong date (rt#5953) +* kolab/issue4145: Calendar: month view/agenda view inconsistence with events with recurrence +* kolab/issue4147: Event/Task conflict dialog "Take Both" should be default (rt#6009) + + +Friday, February 12th 2010 + +Problems addressed +------------------ + +* kolab/issue4059: After deactivating of a calendar the small monthview is not updated (rt#5966) +* kolab/issue2656: Wrong scale is shown, if the calendar component is selected (rt#5916) +* kolab/issue3835: Edit in the Reminder dialog doesn't work after restart of KDE +* kolab/issue4090: Wish:Mail: Double click on mail in search dialog should open the mail in a window (rt#5992) +* kolab/issue4066(partly): Change the default value for the close to quota threshold to 80% +* kolab/issue4039: Size of an embedded event attachment is too small +* kolab/issue4057: Attach file: if filedialog default directory doesn't exit, it should use the home directory + of the user as default (rt#5962) +* kolab/issue4097: Wish for an option: reply should always quote the whole mail (rt#5995) +* kolab/issue4089: Composer: double click on a word should select the word and not additional spaces, + brackets etc (rt#5991) +* kolab/issue2524: KMail: message window shows backslash for quoted-pairs +* Don't crash when using menu item Action->Show after deselecting an incidence + + +Friday, February 5th 2010 + +Problems addressed +------------------ + +* kolab/issue1765: In parallel calender view there is no need to ask in which resource folder a new + event should get. (rt#5816) +* kolab/issue4072: too small window: kleopatra: German export secret key (rt#5848) +* kolab/issue4054: Umlauts and sending contacts problem (rt#5956) +* kolab/issue4076: Mail: search for whole message cannot handle umlauts (rt#5979) +* kolab/issue3817: Wish: New choices for the option KMail->Misc->Folder->When entering a folder (rt#5857) +* kolab/issue4081: Use of many kmail subwindows can lead to crash (KMFolder::open) (rt#5854) +* kolab/issue4052: copy+paste of an appointment in side-by-side view creates the appointment at a + wrong time. (rt#5951) +* kolab/issue4084: Overwriting of an address in the To/CC/BCC field of a composer doesnt work with "Paste" + of a copied address (rt#5988) +* In the archiving dialog, disable the Ok button if the wrong folder or no file is selected +* Add a warning when trying to use the current folder as expiry target +* Don't allow deleting 'no content' IMAP folders +* Don't write out the unused revision field to the XML of events +* Remove unmaintained Scalix and featureplan support + + +Friday, January 29th 2010 + +Problems addressed +------------------ + +* kolab/issue4044: Wish: Mail window should close after pressing on reply/forward (rt#5945) +* kolab/issue4043: extended alarm: Own reminder dialog text is ignored +* kolab/issue4053: reminder of an event with recurrence shows wrong date (rt#5953) +* kolab/issue3989: Calendar: Attendee names with comma are handled wrongly (rt#5899) +* kolab/issue4058: German translation of "No Suggestions" of the auto spellchecking is missing (rt#5964) + + +Friday, January 22th 2010 + +Problems addressed +------------------ + +* kolab/issue4004: calendar prints should contain a print date (rt#5927) +* kolab/issue4033: calendar print: week-schedule big time numbers didn't fit the border +* kolab/issue3901: Result of printing a day seems to be broken +* kolab/issue4036: calendar print: tasks are wrongly printed +* kolab/issue4038: After deletion of an event the small monthview is not updated +* kolab/issue3805: Sometimes at the Mail save dialog no filename is created from the subject (rt#5851) +* kolab/issue3989: Calendar: Attendee names with comma are handled wrongly (rt#5899) +* kolab/issue1783: Invitation gets lost, if no Kalender resource is activated +* kolab/issue4042: minutes field in the extended alarm dialog has a problem + + +Friday, January 15th 2010 + +Problems addressed +------------------ + +* kolab/issue3790: Crash in KMReaderWin::parseMsg at kmreaderwin.cpp:1609 +* kolab/issue4013: Make KMail-Archive format input the default (rt#5817) +* kolab/issue3903: Kontact hangs when switching to calendar side-by-side view (rt#5870) +* kolab/issue3986: event/todo recurrence option should be consistent (rt#5900) +* kolab/issue4027: Kontact crahes if store event attachment inline (rt#5941) +* kolab/issue3813: reccuring events show wrong dates (rt#5855) +* kolab/issue4015: Archiving file selector does not keep the suggested file name when changing the directory (rt#5817) +* kolab/issue4026: mail will be printed with wrong header, if it is printed from a mail window (rt#5935) +* kolab/issue4014(partial): Better Archiving zip -> tar.bz2 default (rt#5817) +* kolab/issue4029: Save a attachment of a mail from the composer: no filename (rt#5940) +* kolab/issue4030: The filename of an UTF-16 text attachment is displayed wrongly (rt#5942) +* Fix regression in KMail: Subfolders were never created in the mbox format, even when requested +* In KMail, disable some actions that are not applicable to read-only folders +* Allow to select read-only folders when archiving +* Don't crash when attempting to archive a folder without selecting a file name + + +Friday, January 1st 2010 + +Problems addressed +------------------ + +* kolab/issue3988: Calendar: update mail of a changed attendee asked to reply (rt#5899) +* kolab/issue3929: Contacts: The user can add a distribution list to itself +* kolab/issue3747: Archiving function has a memory leak +* kolab/issue4000: A user rejects a delegation, the delegator cannot react on this +* kolab/issue3948: Show a 'record' button if the organizer is *not* expecting an rsvp (rt#5879) +* kolab/issue3740: Selection jumps after deleting an attachment from an email +* kolab/issue3959: In side-by-side view without Marcus Bains line is the seperator displaced (rt#5912) +* Fix email address spoofing by not allowing BIDI control characters in the display name + + +Friday, December 18th 2009 + +Problems addressed +------------------ + +* kolab/issue3964(partial): An attendee of an event is not informed about his role or status in the event (rt#5893) +* kolab/issue3994: The calendar/event view configuration dialog is too wide +* kolab/issue3934: Reply of mail with signature removes the signature (rt#5887) +* kolab/issue3973: Improvement of the German calendar import menu texts (rt#5812) +* kolab/issue3961: Mail attachment of a task should be persistent (rt#5915) +* kolab/issue4003: Crash after copying a mail to a quota full OnlineIMAP inbox +* Fix priorities of todos stored on the Kolab server not being remembered properly after a restart + + +Friday, December 11th 2009 + +Problems addressed +------------------ + +* kolab/issue3985: Typos in German dialog "Ungültige E-Mail-Adresse" (rt#5898) +* kolab/issue3972: Event print order will be wrong, if recurring and single events are mixed. (rt#5914) +* kolab/issue3964(partial): An attendee of an event is not informed about his role or status in the event (rt#5893) +* kolab/issue3961: Mail attachment of a task should be persistent (rt#5915) +* Fix translation problem in the archiving function + + +Friday, December 4th 2009 + +Problems addressed +------------------ + +* kolab/issue3805: Sometimes at the Mail save dialog no filename is created from the subject (rt#5851) +* kolab/issue3928: Insert signature at cursor position: Two additional empty lines added (rt#5880) +* kolab/issue3971: Users dislike the "Cannot edit attachment" error message (rt#5908) +* kolab/issue3948: Show a 'record' button if the organizer is *not* expecting an rsvp (rt#5879) +* kolab/issue3984: New filter "*.*" for the save mail as dialog (rt#5896) +* kolab/issue3813: reccuring events show wrong dates (rt#5855) + + +Friday, November 27th 2009 + +Problems addressed +------------------ + +* kolab/issue3747: Better archiving function for a branch (subtree) of folders (rt#5817) +* kolab/issue3098: OnlineIMAP, sometimes Mail body no longer shown -> Crash +* kolab/issue3969(partial): two offline warning windows - that might overlap and lock (rt#5917) +* kolab/issue3978: Forwarding some emails with an embedded email as text loses some of the text (rt#5919) +* kolab/issue3955: Kontact automaticly sends a mail to all attendees of an event without confirm +* kolab/issue3958(partial): Copy action on the address of an open mail doesn't work (rt#5911) +* kolab/issue3957: Improvement of German text in conditionally accepted events (rt#5905) + + +Friday, November 13th 2009 + +Problems addressed +------------------ + +* Work on the archiving function (kolab/issue3747). + + +Friday, November 6th 2009 + +Problems addressed +------------------ + +* Work on the archiving function (kolab/issue3747). Disabled from the GUI at the moment, + until it is completed. + + +Friday, October 30th 2009 + +Problems addressed +------------------ + +* kolab/issue3923(partial): Save as of an attachment of an invitation crashes kontact +* kolab/issue3930: time on the Marcus Bains line doesn't change +* kolab/issue3908(partial): Mail view and print layout problem if description of attachment is to long (rt#5872) +* kolab/issue3926: Start khelpcenter instead of webbrowser help: from the introduction/welcome screen (rt#5881) +* kolab/issue3481: Keep current email in front after saving an attachment (rt#5746) +* kolab/issue3098: OnlineIMAP, sometimes Mail body no longer shown -> Crash + + +Monday, October 26th 2009 + +Problems addressed +------------------ + +* kolab/issue3065: Crash in KOAgendaItem::paintEvent +* kolab/issue3902: Delete a folder while syncing leads to crash +* kolab/issue3903: Kontact hangs when switching to calendar view (rt#5870) +* kolab/issue3908 (partial): Mail view and print layout problem if description of attachment is to long (rt#5872) +* kolab/issue3807: Reminder: None of the buttons should be the default (rt#5845) +* kolab/issue3809: Reminder: sorting in look&feel of kontact (rt#5846) +* kolab/issue3813: reccuring events show wrong dates (rt#5855) +* kolab/issue3922: Decline an updated event throws error message +* kolab/issue3864: An invitation of a long event shows a wrong duration. +* kolab/issue3926 (partial): Start khelpcenter instead of webbrowser help: from the introduction/welcome screen (rt#5881) +* kolab/issue3880: Calendar: In an update mail the end time of an event is wrongly translated with the "Beginn" +* Don't check for forgotten attachments in invitation mails +* Better context-aware messages when asking to send mail for adding/changing/removing invitations + + +Friday, October 9th 2009 + +Problems addressed +------------------ + +* kolab/issue3899: Selecting of an event and try to print the day of the event leads to crash +* kolab/issue1499: using the result of an ldap query from the address selection dialog for setting folder + rights might fail (rt#5298) +* kolab/issue3900: confirm dialog will be empty, if a contact without email address is added from the lookup +* kolab/issue3667: attachment icon is missing in use with OL/toltec (rt#5786) + +Friday, October 2th 2009 + +Problems addressed +------------------ + +* kolab/issue3783: SMIME encrypted and signed mail: Audit log dialog contains ???? at the bottom +* kolab/issue3823: mimetreeviewer doesn't show structure of forward-as-attachment message #n where #n > 1 +* kolab/issue3889: Kontact crahes if opening/changing calendar view (rt#5868) +* kolab/issue3867: Save S/MIME encrypted mails unencrypted after reading them +* Also use new icons in the special dates summary, for birthday, anniversary and holiday + + +Friday, September 25th 2009 + +Problems addressed +------------------ + +* kolab/issue3806: The sorting of mails in the mailview changes after opening a configuration dialog (rt#5852) +* kolab/issue3182: Replies to invitations always sent with default outgoing transport +* kolab/issue3876: regression: New Message To: doesn't fill in To: field in composer + + +Friday, September 18th 2009 + +Problems addressed +------------------ + +* kolab/issue2974: Anniversary event has broken title +* kolab/issue3865: Crash while syncing, automatic event archival enabled, in EventArchiver::archiveIncidences +* kolab/issue3853: Crash after renaming a folder +* kolab/issue2069: Kontact rewrites custom folder types +* kolab/issue3830: Mail: Setting option properties->share unread state with all users on an inbox doesn't have an effect +* Add new KOrganizer icons for anniversary, birthday, holiday and special occassion + + +Friday, September 11th 2009 + +Problems addressed +------------------ + +* kolab/issue3853: Crash after renaming a folder +* kolab/issue3836: Saved auditlog contains some strange xml text +* kolab/issue3833: Mails will vanish, if a folder is renamed +* kolab/issue3837: Reproducible crash when (not) decrypting s/mime message +* kolab/issue3577: [regression] S/MIME opaque signed and encrypted email without smime-type parameter suboptimal display +* kolab/issue3855: [regression] S/MIME opaque signed and encrypted email suboptimal display +* kolab/issue3831: Forward inline: the header of the forwarded msg doesn't contain the CC + field and the time after the date (rt#5862) +* Fix copying of an URL from KMail, which was a recent regression + + +Monday, September 7th 2009 + +Problems addressed +------------------ + +* kolab/issue3816: event view: some topics miss the colon after the topic name (rt#5855) +* kolab/issue3788: Calendar: A default for the reminder option should be configurable (rt#5841) +* kolab/issue3827: Address completion ldap, missing results from some sources (rt#5853) +* kolab/issue3799: Freebusy authentication dialog appears in the background +* kolab/issue3803: Reply-to field in composer has unused "..." button +* Fix crash when completing LDAP addresses +* Don't print "CC:" and "BCC:" in the enterprise header. + + +Friday, August 28th 2009 + +Problems addressed +------------------ + +* kolab/issue3359: LDAP Search result not coming in later, if user typed too fast in email completion (rt#5853) +* kolab/issue3815: event view: dates are displayed without weekday (rt#5855) +* kolab/issue3808: Reminder: small problems (rt#5845) +* kolab/issue3816: event view: some topics miss the colon after the topic name (rt#5855) +* kolab/issue3804: Configuration of Address completion order: Missing last-used addresses (rt#5856) +* kolab/issue3821: [regression] kmail reader: cannot drag attachment icon anymore +* kolab/issue3792: Local Folder for Groupware: Type setting partly lost by restart. +* kolab/issue3822: Configuration of Address completion order: last-used-addresses by default shown one but last, though sorted last (rt#5856) +* kolab/issue3671: Calendar: To Today button doesn't display a workweek right (rt#5769) +* kolab/issue3254: printed mail cut at the left side +* kolab/issue3812: Recent addresses of addresses with comma are displayed with backslash and quote +* Fix regressions that images were not shown in the reader in KMail +* Fix regression that shift-clicking an attachment in KMail wouldn't open it anymore + + +Friday, August 21th 2009 + +Problems addressed +------------------ + +* kolab/issue3811: Rejecting an invitation, with the event existing and writable by someone else, + Kontact will delete the event without question (rt#5803) +* kolab/issue3722: Dragging emails to an existing appointment or tasks does not work (rt#5806) +* kolab/issue3788: Calendar: A default for the reminder option should be configurable (rt#5841) +* kolab/issue3608: Lastname of an attendee is missing (rt#5802) +* kolab/issue3814: Completion order configuration for emails broken (rt#5856) +* kolab/issue3819: Calendar Birthday resource: Anniversary day handling has small problems +* kolab/issue3717: The attachment of an invitation isn't displayed + + +Friday, August 14th 2009 + +Problems addressed +------------------ + +* kolab/issue1536: Default to the sender as organizer when it is not set +* kolab/issue3717: Add support for attachments in invitations +* kolab/issue3802: When updating incidences, preserve the UIDs so that the update is correctly registered +* kolab/issue3808: Rename 'Dismiss' into 'Dismiss Reminder' in the reminder dialog +* kolab/issue3375: Make the attachment overview less confusing by making a difference between embedded and + normal attachments +* kolab/issue3776: Show the 'Record into my calendar' button on updated invitations when sharing + a calendar with others who have access rights + + +Friday, August 7th 2009 + +Problems addressed +------------------ + +* kolab/issue3685: Make it possible again to enter the update mail of a moved event into the calendar +* kolab/issue3789: Show again the buttons for the 'Delegate', 'Forward' and 'Look into Calendar' actions +* kolab/issue3234: Put optional participants of events into the CC field of the invitation mails, not into + the To field, to make Outlook happy +* kolab/issue3779: Always allow an attendee to remove invitation events from his calendar +* kolab/issue3788: Make the default time and time unit for new reminders configurable +* kolab/issue3742: Allow to specify the groupware type of local folders again +* kolab/issue3375: When using 'Scroll To' on attachments, mark the scrolled-to attachment with a yellow border +* kolab/issue3776: Don't ask for the storage location on invitation updates +* kolab/issue3740: When displaying encapsulated messages in a seperate window, make deleting attachments work + correctly +* kolab/issue3722: When dragging email messages as attachments, try to use the mail subject + as the attachment name in the KOrganizer attachment editor. +* Fix a problem that attachments in encapsulated messages could not be deleted +* Don't erroneously display the text part of encapsulated messages as attachment in the quick attachment list +* Add nicer icon for the organizer, replacing the old 'tux' icon + + +Friday, July 31th 2009 + +Problems addressed +------------------ + +* kolab/issue3722: Make dragging mails to existing appointments work +* kolab/issue3759: Don't enable the Apply button in the to-do editor when opened from KMail +* kolab/issue3719: Don't remove linebreaks from the comments on invitations +* kolab/issue3780: Allow declining an invitation without getting an error message +* kolab/issue3777: Fix crash when creating a new folder in KMail +* kolab/issue3724: Possible fix a problem that when multiple persons have write access to a folder, + things were confused for other users +* kolab/issue3725: When forwarding an encrypted mail, don't ask for the passphrase twice +* kolab/issue3781: Allow to delete an accepted appointment without notifing the organizer + + +Friday, July 24th 2009 + +Problems addressed +------------------ + +* kolab/issue3718: Don't show the warning about too many recipients when sending invitations +* kolab/issue3769: Don't crash when reading a free/busy list that was sent as mail +* kolab/issue3771: In the attendee edit dialog, allow to edit the organizer field if it is empty +* kolab/issue3744: Also recognize invitation attachments marked as text/x-vcalendar +* kolab/issue3734: Allow to change the free/busy and active alarm setting of the default groupware + folders as well +* kolab/issue3683: Fix the webpage export in KOrganizer not showing the right events +* kolab/issue3735: Fix append-only IMAP folders leading to sync and conflict loops +* kolab/issue3725: When forwarding encrypted mails, correctly include the attachments in the mail +* kolab/issue3724: Fix various issues with invitations if you have access to a calendar of another + person that is also invited +* kolab/issue3742: Don't show the combo box that changes the folder type for online IMAP folders, since + that is not supported there + + +Friday, July 17th 2009 + +Problems addressed +------------------ + +* kolab/issue3687: When printing from KOrganizer, make sure to include the Location +* kolab/issue3756: Fix problems with reminders not showing up with freshly configured folders + + +Sunday, July 12th 2009 + +Problems addressed +------------------ + +* kolab/issue3722 (partly): Don't indicate that mails can be dropped to the attachment editor, that is not possible +* kolab/issue3746: Fix summary not showing in an update mail and remove empty rectangle +* kolab/issue3721: Fix incorrect translation for ical import tool + + +Friday, June 26th 2009 + +Problems addressed +------------------ + +* Fix problems with Free/Busy passwords that have a '!' in them. + This fix is in kdelibs, not in the enterprise branch. +* Fix incompatibility with GPGME 1.2 + + +Friday, June 19th 2009 + +Problems addressed +------------------ + +* kolab/issue3375: Fix attachment overview in the header being confusing for short mails +* kolab/issue3578: Minimize the amount of key lookups in KMail, especially when not encrypting at all +* kolab/issue3682: Fix a crash when displaying certain S/MIME messages + + +Friday, June 12th 2009 + +Problems addressed +------------------ + +* kolab/issue3685: Fix invitation handling regression, now the update mails of moved events can be + entered into the calendar again +* kolab/issue3678: Speed up displaying of inline PGP encrypted messages +* kolab/issue3694: Fix crash when displaying an invitation mail +* kolab/issue3692: Fix crash when quickly closing the window displaying a signed message +* Add info about the invitation options in KMail's handbook + + +Friday, June 5th 2009 + +Problems addressed +------------------ + +* kolab/issue3673: Switch to calendar component when choosing "Look into the calendar" in invitations +* kolab/issue3636: Prevent creating incorrect maildir folders when using an IMAP account +* kolab/issue3648: Syncing mails was slow and used a lot of memory +* Don't block the UI when displaying signed or encrypted mail +* Add select all and unselect all buttons to the filter importer/export in KMail + + +Friday, May 22th 2009 + +Problems addressed +------------------ + +* kolab/issue3615 (partly): Confusing warnings when evaluating key trust in KMail + + +Friday, May 15th 2009 + +Problems addressed +------------------ + +* kolab/issue3608: Lastname of an attendee is missing +* kolab/issue3598: Holidays are not colored correctly +* kolab/issue2972: Bogus error message when refreshing the IMAP cache +* kolab/issue3617: Kleopatra's setting dialog cannot deal with gpgconf TYPE=0 +* Some spelling and whitespace fixes in the english version of the KOrganizer manual + + +Friday, May 8th 2009 + +Problems addressed +------------------ + +* kolab/issue3605: System tray icon displayed even when there is no unread mail +* kolab/issue3606: Some common folder properties are missing +* kolab/issue2459: Don't crash when refreshing the IMAP cache +* kolab/issue3598: Public holiday description of events are missing +* kolab/issue2276: Export contacts or calendar overwrites files without a warning +* kolab/issue2150: Reply action missing in the message popup menu + + +Thursday, April 30th 2009 + +Problems addressed +------------------ + +* kolab/issue3228: Forwarding filter action does not directly send the email +* kolab/issue3251: Fix the phone numbers of a contact in the XML, so that syncing with Toltec etc works +* kolab/issue2931: Fix an incorrect translation +* kolab/issue2963: Crash while deleting mails, in KMMoveCommand::execute +* kolab/issue3268: Changes to the reply prefix would need a restart of KMail to work +* kolab/issue2152: System tray doesn't react on the change of a folder configuration +* Some fixes for the color settings in KOrganizer +* Fix a small memory leak + + +Friday, April 24th 2009 + +Problems addressed +------------------ + +* kolab/issue2472: (partly) Default event color is not used +* kolab/issue3576: Crash in KOrganizer when switching to the month view +* kolab/issue2501: Set the default month view color strategy to "calendar inside, category outside" +* kolab/issue2508: Don't send an invitation when it the event can't be written +* kolab/issue1819: The help link in the template configuration didn't work +* Fix crashes in KAddressbook when undo/redo is used on a deleted resource +* Implement a new (currently hidden) configuration option for making the frames of month or agenda items prettier +* Fix various memory leaks +* Correctly disable various buttons if clicking them would lead to no action or crashes + + +Friday, April 17th 2009 + +Problems addressed +------------------ + +* kolab/issue1894: Default width of folder name column was too small on first start +* kolab/issue2130: Displayed header type in the mail display is wrong +* kolab/issue1838: Remove the What's this icon from Kontact +* kolab/issue3303: Departement and title were not saved and restored +* Reenable dragging of folders in KMail after an error +* Fix various memory leaks +* Correctly disable various buttons if clicking them would lead to no action or crashes +* In KAddressbook, cancel saving when we didn't select a resource + + +Friday, April 10th 2009 + +Problems addressed +------------------ + +* kolab/issue3492: Default outgoing transport changes randomly when removing any outgoing account +* kolab/issue1996: Composer text-completion: Cursor always jumps to the end of the entered text +* kolab/issue3244: Crash at KMFolder::addMsg after a second import of testmails +* In KMail, don't allow executing something when clicking on a URL in the reader +* When formatting incidences, don't add "[Accept]" to it if it can't be accepted +* In the attendee editor, disable "Response request" when the attendee is oneself +* When address completion is disabled, don't search on the LDAP server when typing +* When exporting addresses to vcard, ask before overwriting files +* Fix small memory leaks in KMail + + +Monday, April 6th 2009 + +Problems addressed +------------------ + +* kolab/issue3051: Pressing Shift+R at a shown mail opens a composer with a reference to the mail +* kolab/issue2238: D'n'D a contact on the contact sidebar icon should not be possible. +* kolab/issue1940: Activating "unseen mails behind folder" sets the option "unread mails in own column" +* kolab/issue3512: Kontact does not detect shared seen folder capabilty anymore +* kolab/issue2687: After applying a new identity, the columns of the mail view reset +* Other small improvements in the sieve script editor +* Fix crash in the composer if the transport is empty +* Fix enable/disable action when we are in template folder + + +Friday, March 27th 2009 + +Problems addressed +------------------ + +* kolab/issue3482: Kontact creates invitations with reminders for one week (1W) that Outlook 2003 does not understand +* kolab/issue3263: Support custom templates when forwarding with filters, and also support fixed recipients for templates +* kolab/issue3471: Copy menu entry doubled in context menu +* kolab/issue3312: Crashes if a contact is changed with the contact editor +* In KMail, fix crash when changing the configuration +* When deleting templates in KMail, make sure they are removed from the configuration file +* Remove one unnecessary sync in KNotes + +Merges +------ + +* Merge the forwarding fixes from enterprise4, to prevent corrupt mails after using filters for forwarding + + +Friday, March 20th 2009 + +Problems addressed +------------------ + +* kolab/issue3467: Frequent crash after editing notes +* Eliminate some unnecessary groupware syncs in KNotes +* Don't crash when using "Save As" in KNotes +* Fix problems with notes being randomly recreated or randomly placed +* Don't allow resizes when a note is locked +* Disable "Find Text" and "Show/Hide All Notes" actions when there are no notes + + +Friday, March 13th 2009 + +Problems addressed +------------------ + +* kolab/issue2685: Signature not recognised with combined OpenPGP encrypted/signed email +* kolab/issue2444: Kontact sends base64 encoded password when sieve server does not advertise STARTTLS +* kolab/issue1950: gpgme config dialog broken when /etc/gnupg/gpgconf.conf not empty +* kolab/issue2628: For encrypted emails where no encryption was tried, it says "decryption impossible" +* kolab/issue3477: Kontact creates bad invitations if attendee name with umlauts is entered with double quotes +* In KNotes, disable actions when the note is locked +* In KMail, consider marginally trusted keys as trusted, too + + +Friday, March 6th 2009 + +Problems addressed +------------------ + +* kolab/issue3367: Changing organizer's status doesn't work +* kolab/issue3119: Black notes are displayed after system start +* Don't allow selection of S/MIME certificates when we only want OpenPGP ones (and vice versa) +* Avoid some unnecessary resyncs in KNotes +* Don't enable undo/redo for locked notes +* Don't allow to rename locked notes +* Fix memory leak in the Kolab resource +* Don't save the spellcheck coloring to the notes + +Merges +------ + +* Speed up folder syncing for disconnected IMAP by only uploading flags that really changed + + +Friday, February 27th 2009 + +Problems addressed +------------------ + +* kolab/issue3436: Fix crash in extractAuditLog in KMail. +* kolab/issue3377: Issue warning when emailing to more than X recipients +* Fix enabling/disabling of the Add/Remove Quote Characters actions. + + +Friday, February 20th 2009 + +Problems addressed +------------------ + +* Fix memory leak in KMail when reading signed or encrypted messages + + +Friday, February 13th 2009 + +Problems addressed +------------------ + +* kolab/issue3385 Reminder of an event doesn't work +* kolab/issue2523 Export Calendar as Web Page: Date range empty by default, no warning +* kolab/issue3065: Crash in KOAgendaItem::paintEvent + +Merges +------ + +* Add a cancel button to the reoccurence editor +* Fix memory leak in KNotes +* Disable various buttons if they have no effect, in KOrganizer, Knotes, KAddressbook and KMail + + +Friday, February 6th 2009 + +Problems addressed +------------------ + +* kolab/issue3374 Window behaviour of Kontact is undesirable sometimes if a message box comes up. +* kolab/issue3376 Make gui for option ForwardingInlineByDefault +* kolab/issue3386 Kontact requires that free/busy urls match email addresses + + +Friday, January 30th 2009 + +Problems addressed +------------------- + +* kolab/issue3275 translation of "calendar" to "Kalendar" is wrong, should be "Kalender" + +Tuesday, January 20th 2009 + +Problems addressed +------------------ + +* kolab/issue3304 Pasting a contact opens an edit dialog. +* kolab/issue3345 Calendar: The default status of an organizer should be accepted. +* kolab/issue2677 Misleading german translation "Die Terminordner gehören zur Identität" +* kolab/issue2908 Wrong default name when renaming IMAP ressources +* kolab/issue3219 user templates do not work, if the email has an attachment + + +Sunday, December 21st 2008 + +Problems addressed +------------------ + +* kolab/issue3306 "No attendees" behind the title of a new event or task not translated into German. +* kolab/issue3297 If kontact is started via knotes, knotes sometimes crashed while renaming a note +* kolab/issue2962 Composer text snippets: Add/Edit group, snippet text "GROUP" is not translated into German. +* kolab/issue3324 can delete attachments in read-only folders +* kolab/issue3316 Doubled folder lists in move-to filteraction selection dialog +* kolab/issue3314 Contact folder selection state not saved between KDE sessions +* kolab/issue3084 An organizor of an event cannot change his status +* kolab/issue1783 Invitation get lost, if no Kalender resource is activated + +Friday, December 12th 2008 + +Problems addressed +------------------- + +* kolab/issue3276 cannot encrypt to marginally trusted OpenPGP key, with email address not in key and key in contact entry ( part fix ) +* kolab/issue3305 Double click on a distribution list in the distribution list editor should open an edit dialog +* kolab/issue2048 Distribution list: couldn't delete a contact from a dist list with pressing on "delete" + +Wednesday, December 3rd 2008 + +Problems addressed +------------------ + +* kolab/issue3228 forwarding filter action does not directly send the email +* kolab/issue1950 Kontact: gpgme config dialog broken when /etc/gnupg/gpgconf.conf not empty +* kolab/issue3195 Sending a mail encrypted and signed with an expired Openpgp key shows confusing dialog +* kolab/issue3265 OpenPGP key expiry warning not emitted for signing keys) Editing +* kolab/issue2472 ("Default event color" is not used +* kolab/issue3143 (s/mime invalid keys are accepted by KMail and shown green in kleo (certmanager/lib/ui/keyselectiondialog.cpp) +* kolab/issue3249 (Strange crash while replying to an SMIME mail. +* kolab/issue3281 (Kontact does not quote CNs in iCalendar mails, which Outlook 2003 (OL 2K3) then fails to recognise + +Friday, November 21st 2008 + +Problems addressed +------------------ + +* kolab/issue2627 Status line for audit log shown, even when it is not implemented. +* kolab/issue2617 Kleopatra: After importing a p7c or p12 file the user is informed, that he has imported 0 certifictes. +* kolab/issue2620 Kleopatra throws an error message, if no .gnupg exists and no agent is running. + + +Tuesday, November 19th 2008 + +Problems addressed +------------------- + +* kolab/issue3196 large number of appointments can lead to corrupt index and loss of events. + +Sunday, November 9th 2008 + +Problems addressed +------------------ + +* kolab/issue2627 Status line for audit log shown, even when it is not implemented. +*Support the /vendor/cmu/cyrus-imapd/sharedseen annotation available in Cyrus IMAP server 2.3.9 or higher which allows to share the seen flags between all users that have access to a folder. +* Detect if the server supports shared seen flags. Also preserve the IMAP server capabilities in kmailrc so that detection also works without a prior sync. + + + +Monday, October 27th 2008 + +Problems addressed +------------------- + +* kolab/issue2230 Summary: Can select summaries of parts in the summary configuration dialog, which are deactivated in the components dialog. +* kolab/issue2947 Kontact crashes by opening an added attachment file. + + +Friday, October 17th 2008 + +Problems addressed +------------------- +* kolab/issue2702 (calendar: After deleting an attendee no remaining attendee is selected. +* kolab/issue3127 (Cannot open a mail d'n'ded to the desktop and then cannot restart kontact +* kolab/issue2875 (Better guide the user to find S/MIME certificate manager when trying to encrypt email + + + +Friday, October 3rd 2008 + +Problems addressed +------------------- +* kolab/issue2936 Document non-gui OutOfOffice settings for enterprise35. +* kolab/issue1570 Make standard identity the default in the "Select Address" dialog +* kolab/issue2501 Calendar: events in the month view are displayed in wrong colors +* kolab/issue2472 "Default event color" is not used +* kolab/issue2314 documentation of snippets missing, especially for the variable substitution +* kolab/issue2962 Composer text snippets: Add/Edit group, snippet text "GROUP" is not translated into German. +* kolab/issue2389 contact: After creating a new contact, while I have to select a contact resource the cursor is a clock. +* kolab/issue2989 normal reminder changes to advanced reminder unexpectedly +* kolab/issue1589 "Keep replies in this folder" not honored when replying with identity not matching original recipients +* kolab/issue2614 The context menu of the favorite folder view and the folder view should be more similar + + +Friday, September 26th 2008 + +Problems addressed +------------------- + +*kolab/issue2989 normal reminder changes to advanced reminder unexpectedly + +Friday, September 19th 2008 + +Problems addressed +------------------- +* kolab/issue2830 Default identity used for invitation even if other identity is set as organizer. +* kolab/issue2577 An invitation to a recurring event should give information about the recurrance. +* kolab/issue1828 Printing event doesn't print list of attachment. + + +Friday, September 12th 2008 + +Problems addressed +------------------- +* kolab/issue2792 kontact sometimes doesn't display a shared folder + +Monday, September 8th 2008 + +Problems addressed +------------------ +* kolab/issue3024 Crash in KCal::Incidence::revision. + +Friday, August 29th 2008 + +Problems addressed +------------------ + +* kolab/issue2680 Counter proposal: A user should be notified about an accepted counter proposal. +* kolab/issue2625 Sync with the new sync button for emails stays offline without message. +* kolab/issue2514 Moving a event and pressing "Cancel" in the send mail dialog, doesn't cancel the operation. +* kolab/issue2489 Synchronize Calendar button always syncs all servers. +* kolab/issue2111 Help->Feature Request looks for a non-existing page. +* kolab/issue2655 Fast pressing "Down Arrow" Key in the address lookup of the composer selects an address in the "to" field. + + +Saturday, August 23rd 2008 + +Problems addressed +------------------ +kolab/issue2927 Opened mails/composer are not reopened after a restart. +kolab/issue2675 Calendar side-by-side view: scale doesn't fit with the hours view. + + + +Friday, August 22nd 2008 + +Problems addressed +------------------ +* kolab/issue2954 Possible to delete a mail if just max append rights. +* kolab/issue2036 Press "cancel" at the calender folder selection deletes an updated event. + + + +Friday, August 13th 2008 + +Problems addressed +------------------ + +* kolab/issue2933 Creating folder with a old folder name can trigger a crash. +* kolab/issue2850 Crash while copying a mail to a read-only folder. +* kolab/issue2904 Calender component reloads all events, e.g. after switching to todos and back. + +Friday, August 1st 2008 + +Problems addressed +------------------ + +* kolab/issue2951 The name of the event which was created by d'n'd of a todo is "BEGIN:VCALENDAR". +* kolab/issue2904 calender component reloads all events, e.g. after switching to todos and back. + + +Friday, July 25th 2008 + +Problems addressed +------------------- + +* kolab/issue2866 In the todo summary a todo is shown which was deleted. +* kolab/issue2867 Todos: Often deleted todos reappear. + +Monday, July 22nd 2008 + +Problems addressed +------------------- + +kolab/issue2801 Kontact doubles mails if a filter is used and every mail is filtered. +kolab/issue2868 The name of the todo which was created by d'n'd of an event is "BEGIN:VCALENDAR" + +Friday, July 11th 2008 + +Problems addressed +------------------- + +* kolab/issue2522 Pressing ESC while moving a contact, deletes the contact. +* kolab/issue2607 sync loop after deactivation of autosync. +* kolab/issue2666 Deactivated calendar resources are reactivated after restart. + +Merges +------ +* Enterprise branch is in sync with kdepim 3.5 branch +* Bugfixes from enterprise ported to 3.5 branch + +Friday, July 4th 2008 + +Problems addressed +-------------------- +* kolab/issue2512 Kmail: "Subscription" should be renamed in "Serverside subscription" +* kolab/issue2741 Button to do an ldap lookup is missing from the address selection dialog. +* kolab/issue2511 Pressing "New" in the new event attendee view should not continue to create new attendees, if the first example attendee is not changed. + +Merges +------ + +* Enterprise branch is in sync with kdepim 3.5 branch + +Friday, June 27th 2008 + +Problems addressed +-------------------- +* kolab/issue2785 favorite folder: Two creation ways show different names. +* kolab/issue2790 Strange date/time field in the new todo dialog. +* kolab/issue2568 Button to open certificate manager is missing from encryption key dialog. +* kolab/issue1998 An invitation via a server distribution list, cannot be accepted by attendees as they cannot choose one of their own identity. +* kolab/issue2741 Button to do an ldap lookup is missing from the address selection dialog. +* kolab/issue2618 Kleopatra: the dump of smime certificates doesn't contain the email addresses. +* kolab/issue2773 Usability of "Order of Arrival" string? +* kolab/issue2740 kleopatra shows an error message, if started and the search is on "In External Certificates" + + +Friday, June 16th 2008 + +Problems addressed +-------------------- + +* kolab/issue2506 Typo in the display of a smime signed mail. +* kolab/issue2512 Kmail: "Subscription" should be renamed in "Serverside subscription" +* kolab/issue2511 Pressing "New" in the new event attendee view should not continue to create new attendees, if the first example attendee is not changed. +* kolab/issue2659 Copying and moving contacts with only one resource configured. +* kolab/issue2741 Button to do an ldap lookup is missing from the address selection dialog +* kolab/issue2619 Kleopatra: Trying to create a smime key pair with a "+" in the CN, fails without a warning. +* kolab/issue2568 Button to open certificate manager is missing from encryption key dialog + +Merges +-------- + +* Enterprise branch is in sync with kdepim 3.5 branch + +Friday, May 30th 2008 + +Problems addressed +--------------------- +* kolab/issue2717 - Composer option "Insert signatures above quoted text" doesn't work. Implemented for replytoall for consistency. + + +Merges +------- +* Enterprise in sync with kdepim 3.5 branch. + + + +Tuesday, May 20th 2008 + +Problems addressed +-------------------- +* kolab/issue2667 - Kontact sometimes tries to re-create existing folders and fails. (807761) +* kolab/issue2717 - Composer option "Insert signatures above quoted text" doesn't work. (810237) + +Merges +------- + +* Enterprise in sync with kdepim 3.5 branch. + + +Friday, May 9th 2008 + +Problems addressed +------------------- + +* kolab/issue2560 Unsuccessful Sieve connection causes strange Progress Dialog.(804616) +* kolab/issue2675 Calendar side-by-side view: scale doesn't fit with the hours view.(804642) +* kolab/issue2669 (Profile does not save background color of emails.(804994) +* kolab/issue2148 (counterproposal event should be inserted into the calender +* Kolab issues 2148, 2678, 2681, 2683, 2680 - Counter proposal issues. (805191,805702) +* kolab/issue2247 (Command line switch to choose Kontact profile (W1-32) (805348) + +Merges +-------- + +* Enterprise in sync with kdepim 3.5 branch. + + +Saturday, May 3rd 2008 + +Merges +------- + +* Enterprise in sync with kdepim 3.5 branch. + +Monday, April 28th 2008 + +Problems addressed +----------------------- + +* kolab/issue2585 contacts of a LDAP resource folder should be seen in the mail address text completion of a composer +* kolab/issue2443 kontact aborts sieve when imapd sends capabilities after starttls +* kolab/issue2608 Calendar: counter proposal is not sent to the organizer +* KDE bug 126025: Replying to address with umlaut and comma creates two addressees + + +Friday, April 18th 2008 + +Problems addressed +----------------------- + +* kolab/issue2630 calendar: Wrong scrolling in the side-by-side view. +* kolab/issue2629 calendar: At the headers of a week in the side-by-side view are not above the right columns + +Merges +------- +* Merges in from 3.5 branch. + + + +Friday, April 11th 2008 + +Problems addressed +----------------------- + +* kolab/issue2272 - Event Attendee Editor: After "Zoom To Fit" and zoom it is possible to scroll the fb part without the attendee part. (794734) +* kolab/issue1869 (side-by-side calender view size depends on folder path length.(795745) +* kolab/issue1883 (wasting space with many time scales in side-by-side-view.(795745) +* kolab/issue1947 (calendar side-by-side view: superfluous scrollbars in the dayappointments.(795745) +* kolab/issue1948 (calendar side-by-side view: wasting space by several scrollbars? Fix one grid option.(795745) +* kolab/issue2561 (Side-By-Side view: the view of some folders are of different size.(795745) + + +Friday, April 4th 2008 + +Problems addressed +-------------------- + +* kolab/issue2577 An invitation to a recurring event should give information about the recurrance. (792202) +* kolab/issue2581 The reject counter proposal mail is send to a wrong account.(792230) +* kolab/issue2580 A user cancels a counterpropsal and the invitation mail is gone. (792231) +* kolab/issue2582 After pressing "decline" in the counter proposal dialog and sending the counter proposal the event can be found in the calendar. (792950) +* kolab/issue2578 After deletion of an event the organizer gets a mail, that some attendee accepted the event. (792957) + + + +Saturday, March 29st 2008 + +Problems addressed +------------------ +* kolab/issue2231 - Edit event: After adding and removing an mail address, an update mail is send to the address. +* kolab/issue2217 - "open" button/funcationality in full search window missing (Z37) +* kolab/issue2566 - Translation Error: German key +* kolab/issue2565 - appointment dialog text quirks +* kolab/issue2148 - counterproposal event should be inserted into the calender +* kolab/issue2147 - There should exist two kind of counter proposals +* kolab/issue2149 - A counterproposal should be displayed in a way, that the organizer understands. + + +Friday, March 21st 2008 + +Problems addressed +------------------ +* kolab/issue1826 - Default (New Mail) template needs 3 spaces between the top and the signature. +* kolab/issue2290 - Should warn and offer to change if there are two *.default groupware folders in one account. +* kolab/issue2272 - Event Attendee Editor: After "Zoom To Fit" and zoom it is possible to scroll the fb part without the attendee part. + + +Friday, March 14th 2008 + +Problems addressed +------------------- +* kolab/issue1770 Delete attachment of a signed mail doesn't work. (783303) +* kolab/issue2533 Reproducible crash in KMail::MessageProperty::filtering when refreshing IMAP cache. (784547) +* kolab/issue2239 Addressbook extension doesn't recognise the deletion of a contact folder.(785317) +* KDE Bug 146679 Can't set negative absolute hours - fixed. (785054) +* kolab/issue2168 Email attachment overlay view, header style "enterprise" : Should be made foldable. +* kolab/issue2529 Email attachment overlay view, header style "enterprise": fold button should vanish, if the mail has no attachments. + + + +Friday, March 7th 2008 + +Problems addressed +------------------- + +* kolab/issue2297 New event: Organisor is not attendee. (781595) +* kolab/issue1988 Deleting a event should remove the deleter from the attendee list.(782029) +* kolab/issue2443 kontact aborts sieve when imapd sends capabilities after starttls. (782672) + +Merges +------ + +* Merge35 Filter source folder whitelist (from proko2 ). (783244) + +Friday, February 29th 2008 + +Problems addressed +------------------- + +* Prevent crash after changing the layout and replying then. (778728) +* kolab/issue2442 kolab/issue2442 (kioslave crashs after closing kontact, if the vacation reminder is activated. (779694) +* "Enterprise headers" makes impossible to select text in first paragraph of body. [ kde-bug#151150 ] (779994) +* kolab/issue1700 kleopatra crashes after ldap search. (780211) +* kolab/issue2168 Email attachment overlay view, header style "enterprise" : Should be made foldable (780250) +* kolab/issue2413 Kmail: Cannot select "Mark Thread" in the popup menu if I select two or more threads. (780251) +* kolab/issue2312 Rejecting an invitation does not always work (780286) + +Merges +--------- +* Merges in from 3.5 branch + +Friday, February 22nd 2008 + +Problems addressed +------------------ +* Prevent the message in the reader window from jumping back to the start of the message when the font or the font size is changed in the separate reader window. + +Merges +------ + +* Merges in from 3.5 branch. + +Friday, February 15th 2008 + +Problems addressed +------------------- +* kolab/issue2160 contact view: move distribution list and addressbook view menu item to "views" (775026) +* kolab/issue2477 Contacts: The Dist lists view is at the right side of the contacts list view. (775016) +* Fixed cached imap local subscription so that it works for top-level folders.(774162) +* Fixed the problem that filtering with online IMAP stopped after the first message had been processed.(773733) + +Merges +------ + +* Merges in from 3.5 branch. + +Friday, February 8th 2008 + +Problems addressed +------------------- + +* kolab/issue1981 Context menu entry "Paste" in kalender view +* kolab/issue2260 New resource calendar folder default colors +* Fix message status change randomly not working for non-toplevel mails +* Really apply the identity when we edit a received message +* kolab/issue2394 IMAP sync keeps calendar items which are deleted on the server +* kolab/issue2450 Kmail: D'n'D and "Move To" from a read only folder is possible +* kolab/issue2092 A latin-9 attachment is treated like a utf-8 file +* Fix reply actions being disabled when a collapsed thread is selected +* kolab/issue2390 contact: Two resources on the same server could looks like the same source in the contact view + + +Friday, February 1st 2008 + +Problems addressed +------------------- + +* kolab/issue2036 Press "cancel" at the calender folder selection deletes an updated event. (768070) +* kolab/issue2214 Creating a calender in local folder where the user have no rights shouldn't be possible.(768402) +* kolab/issue2433 Debug log of kio_sieve shows password in base64. (768539) +* kolab/issue2387 Crash when creating a subfolder of the calendar folder.(768738) +* kolab/issue1841 Changing composer wrap margin does not affect currently open composer.(769033) +* kolab/issue1843 Resized composer window snaps back if settings are changed.(769035) +* kolab/issue2166 Rename "Create Task ..." to something that resembled "resubmission" or "reminder".(769042) +* kolab/issue2159 Configure option: distribution list view and addressbook view under or left of the main list of contacts.(769058) +* kolab/issue2167 New tasks should have reminder and until time set by default.(769058) + +Monday, January 28th 2008 + +Warning +-------- +The account password is shown in the debug output (See kolab/issue2344). +Solution is to use "kdebugdialog" and switch "kio_sieve" off. + +Problems addressed +------------------- + +* kolab/issue2387 Crash when creating a subfolder of the calendar folder. (767033) + +* kolab/issue2269 After locally unsubscribed a shared folder, the grey "user" and the grey "ltest1" folder didn't vanish. (765237) + +* kolab/issue2216 Crash in KMFolderSearch::addSerNum (764894) + +* kolab/issue2403 Forwarded or Delegated event not displayed correctly.(764828) + +* kolab/issue2405 Printing a encrypted mail should be possible (764404) + +Merges +------- + +Merging complete with respect to 3.5 branch. + +Friday, January 18th 2008 + +Problems addressed +------------------- + +* kolab/issue2275 upgrade removed groupware resources.( 745813 ) + +* kolab/issue2306 Crash below KMReaderWin::parseMsg after fast navigation through mails (746845) + +* kolab/issue2313 Crashed when subscribing new folders (e.g. in KMFolderMgr::removeFolderAux()) ( 746873 ) + +* kolab/issue2173 "Copy contact to" and "Move contact to" have the same icons. ( 746925 ) + +* kolab/issue2146 Kontact evaluates HTML tags in Contacts ( 747182 ) + +* kolab/issue2182 Kontact shows misleading error messages for a mail encrypted with unsupported cyphers. ( 747318 ) + +* kolab/issue2262 Crash below SnippetWidget::slotAdd()) (747340) + +* kolab/issue2286 Crash below KMail::CachedImapJob::slotPutMessageInfoData after working with tasks. ( 747584 ) + +* kolab/issue2299 Sending a mail with softbreaks inserts newlines into this mail. (748016) + +* kolab/issue2220 "Action Item" and "Important" flag of mails behave different, if a folder is shared with read rights. (748416) + +* kolab/issue2344 Kontact crash on Deleting messages which are attachments shown in message viewer. (748425) + +* kolab/issue2191 filters lose conditions. (748479) + +* kolab/issue2004 Kontact, contact component: minsize of some components too wide (748501) + +* kolab/issue2061 three buttons lead to too large minimum width (in contact, calender and journal view). (748501) + +* kolab/issue2161 contacts view: distribution list view: Should have the usual buttons (748529) + +* kolab/issue2348 Crash on "checking" ToDo in calendar view (750031) + +* kolab/issue2304 Adding new attendees should be easier by clicking. (750093) + +* kolab/issue2301 Contacts: Undo asks to select a folder, after cutting some contacts. (750101) + +* kolab/issue2178 After inserting a text component to a mail the cursor should be at the end of this text component not at the beginning. (750499) + +* kolab/issue2169 Email header style "enterprise" does not jump to all email attachments (750562) + +* kolab/issue2008 Export of kontact profiles doesn't work. (750648) + +* kolab/issue1870 Display of whole day events in side-by-side view shows rubbish. (750879) + +* kolab/issue1952 favorite folder view: clicking on groupware folder should jump to corresponding view. (750904) + +* kolab/issue2379 Favorite todo folder: If the folder is already selected, clicking on it doesn't change the view. ( 758955 ) + +* kolab/issue 2399 "Out of Office" reply reminder is activated althrough "Out of Office "reply is not activated. ( 762310 ) + +* kolab/issue 2401 D'n'D attachment of a mail to the desktop: copy lots of different files to the desktop.( 762653 ) + +* kolab/issue2195 German translation in kontact's Calendar box (long, Deppenapostroph). ( 762985 ) + + + +Merges +------- +* More merges from all of kdepim. + +Monday, December 4th 2007 + +Problems addressed +-------------------- + +* kolab/issue2260 Use better category and resource colors, make the list of default colors configurable, change the default working time color. (744677) + +New functionality +------------------- + +* prokde35-z Item 60: Merge of the attachments and recurrence tabs into the main one, according to the usability suggestions and additional comments from the usability team. D'n'd and c'n'p support for attachments. (revision 742613) + +* prokde35-z Item 65: Merge of the attendee and free-busy tabs into the main one, according to the usability suggestions and additional comments from the usability team (revision 744005) + +* prokde35-z Item 61: Implementation of the xfb concept (revision 744092) + +* prokde35-z Item 203: implementation of the concept for better error messages for crypto operations (audit log) (revision 743026) + +* prokde35-z Item 32: Separate the recipients editor with a splitter, so the number of visible recipients can be freely configured (revision 744676) + + +Friday, November 23rd 2007 + +Problems addressed +-------------------- +* kolab/issue2224 (Crash: KMFolder::countUnread, after hide groupware folders and sync) (739114) +* kolab/issue1864 (Edit Categories not working when creating a new event) (739670) + +New functionality +------------------- +* prokde35-z Item 36: Clearer descriptions for search/filter criteria (738629) +* prokde35-z Item 25: "Edit" for received mails. (739063) +* prokde35-z Item 37: Make the splitters a bit more sensible, by preventing total collapsing, where it makes sense. (739069) +* prokde35-z Item 26: per-account identities (739135) +* prokde35-z Item 18: more and better configurable message actions on the main reader window (739157) +* prokde35-z Item 53: Improved coloring of agenda view items (739603) +* prokde35-z Item 50: Default colors for calendar resources (739689) +* prokde35-z Item 79 (part 2): Statusbar indicator for active vacation scripts (740023) +* prokde35-z Item 60 Allow files to be dropped onto the attachment view and ask whether they should be attached by link or by value. Implement dragging out of the view. + +Friday, November 16th 2007 + +Problems addressed +-------------------- +* kolab/issue2200 (After editing the attendee list the old attendes gets update mails without the possibility to enter them into their calender.) (737072) + +New functionality +------------------- +* prokde35-z Item 75: Aggregated reminders (737035) +* prokde35-z Item 3: Hide no-content folders only containing groupware folders (737469) + +Merges +--------- +* Merges from 3.5 branch for KMail, KAlarm, aKregator, KArm + + +Friday, November 9th 2007 + +Problems addressed +-------------------- +* kolab/issue2123 Make sure the re-generated index is actually written out, to avoid crashes on switching out of the folder, and related unpleasantness. (732422) +* kolab/issue2142 ( part 1 ) Make sure the file selector is shown on top of the filter dialog. (732794) +* crash when there is no smime backend, and the protocol can't be determined.(733049 ) +* kolab/issue2151 Fix creation of new folders when restarting KMail before the next sync. ( 733149 ) +* kolab/issue2142 ( part 2 ) Provide a proper parent for the "ask to overwrite" dialog.(733495) +* kolab/issue 2176 Move transaction code into the filtermanager, from the caller and make sure filter counts are written to and read from the right section. ( 733511 ) +* kolab/issue2175 Show an error message if the to-be-imported filters file can't be read. (733519 ) +* kolab/issue2181 Fix crash when opening a message without a parent, cleanup initialization order.(734247) + +New functionality +------------------- +* Prokde35-z Item 69 Allow comments to be given when reacting to invitations (733159) +* Prokde35-z Item 68 (part 1) Make it configurable whether invitation emails are move to the trash once the reply to them has been sent and add gui for that to the groupware section of the main configuration dialog. (733883) +* Prokde35-z Item 73: Show changes made by the organizer when showing invitation updates. (734580) + +Merges +--------- +* Merges from 3.5 branch for KMail and KAlarm. + + + +Friday, November 2nd 2007 + +Problems addressed +------------------ +* kolab/issue2151 Creating a subfolder doesn't work correctly + +New functionality +------------------ +* Prokde35-z Item 49: "Store Contact In" -> "Copy Contact To" and "Move Contact To" (729611) +* Prokde35-z Item 201: Allow dragging of attachments from composer windows (729661) +* Prokde35-z Item 203 (part 1): Port KMail from cryptplug to kleo (731701) +* Add the ability to associate keyboard shortcuts with snippets. (731716) +* Add a GUI option for syncing groupware changes immediately (731653) + +Friday, October 26th 2007 + +Problems addressed +------------------ +* ensure that tooltips also work on the main folder tree + and show quota info properly (729284) +* kolab/issue2108 (Kontact sends broken invitation email.) Explicitely disable + word wrapping, instead of toggeling, where appropriate. + +New functionality +------------------ +* Prokde35-z Item 16: Filter import/export (728541) +* Prokde35-z Item 37: Open search results in standalone reader, + when preview pane is hidden (729306) + + +Friday, October 12th 2007 + +Problems addressed +------------------ +* kolab/issue1941 (slow LDAP server lookup clears the To: field) + Don't overwrite the user selection if LDAP search results come in delayed. +* kolab/issue2035 (Manage Sieve Script: Couldn't activate a script.) Allow + activation of sieve scripts when none are currently active +* kolab/issue2042 (Not possible to create a new contact) Fix creation of + new contacts in kolab resources +* kolab/issue2045 (Sending unencrypted, unsigned mail to myself throws warning) + Fix problems with ecnryption to untrusted keys +* kolab/issue2046 (Crash: Try to copy an attachment to the konqueror) + Fix crash on drag +* kolab/issue2067 (Kontact doubles mails when filter is used.) Make sure + to persist the list of deleted uids + + +New functionality +------------------ + +* Prokde35-Z Item 55d: Immediately sync groupware changes (revision 723653) +* Prokde35-z Item 27: Softbreaking in the kmail editor when no hardbreak limit is set (724153) +* Prokde35-z Item 19: Raise hardbreak limit from 78 to 255 (724155) +* Change the default for what to select on entering a folder to "last selected" from "jump to new". + +Merges +------ + +* kolab/issue1904 don't crash on shutdown if the kernel is already deleted +* kolab/issue1942 Allow not fully trusted keys to be used for encryption, but ask the user to confirm their use. + + +Wednesday, September 26st 2007 + +Bugfixes +-------- + +* kolab/issue1983 Fix encoding issue in SMIME encryption message. +* kolab/issue1924 Retain state of the crypto state indicators across drafts. +* KDE bug 132938 Avoid asking the user to go online spuriously/ +* kolab/issue1743 Fix upload of out-of-office scripts (716765) +* kolab/issue2023 Missing accelarators in text snippet context menu (716933) +* kolab/issue2009 Fix new/unread flag handling during DIMAP sync (717162) +* kolab/issue2029 Allow to hide signature details again (717191) + +Features +-------- + +* prokde35-z item 70: Check calendar link in invitations (714358) +* prokde35-z item 30: Text snippet support in the kmail composer window (715206) +* prokde35-z item 47: Support for read only contacts folders (715588) +* prokde35-z item 67: Support for invitation counter proposals (716376) +* prokde35-z item 21: D'n'D out of the folder view, removal of the "save encoded" menu entry (716393) +* prokde35-z item 7: Close application despite presence of a system tray icon, configurably (716415) +* prokde35-z item 40: Details on demand for pgp/smime signature status (716476) +* prokde35-z item 79: Warn about active out-of-office scripts on startup (716765) +* prokde35-z item 17: Better error reporting for quota errors (716771) +* prokde35-z item 24: New attachment view in mail viewer (717292) + + +Friday, September 21st 2007 + +Bugfixes +-------- + +* kolab/issue1983 Fix encoding issue in SMIME encryption message. + +* kolab/issue1924 Retain state of the crypto state indicators across drafts. + +* KDE bug 132938 Avoid asking the user to go online spuriously/ + +Features +-------- + +* prokde35-z item 70: Check calendar link in invitations (714358) + +* prokde35-z item 30: Text snippet support in the kmail composer window (715206) + + +Friday, September 14th 2007 + +Features +-------- + +* Make the maximum attachment size configurable by a non-gui (kioskable) option + and change the wording of the warning dialog to refer folks to their admin, + rather than blaming KMail for the limitation. + +Bugfixes +-------- + +* prokde35 W1-32 fixes for global profiling handling + +* kolab/issue896 Change the summary view title to "To-Do" from "To-do List" for consistency with the side bar. + +* Bump the kontact plugin version to reflect the profile loading changes and + update the version in those plugins we ship. Fixed crashes from basket on + startup in kubuntu, for example. + +* Make favourite folder area work with multiple main windows + +* kolab/issue1955 Fix crash when closing KMail. + +* kolab/issue1963, 1964 Make the create task action available in separate reader windows and the toolbar. + +* kolab/issue1984 fill distribution list listview delayed to avoid crash + +* kolab/issue1983 Make on-demand decrytion also work with SMIME. + +Merges +------ + +* Merge kalarm from 3.5 r708803-709812 + + +Friday, September 7th 2007 + +Features +-------- +* prokde35-z item 58: Rename "time associated" to "add-day event" and invert logic (706869) +* Limit attachment size to a sensible value (upstreaming old SUSE patches, this one originally by Waldo Bastian). (706920) +* prokde35-z feature 46: Rework distribution list integration in KAddressBook, add support for multiple active extensions (708034) +* prokde35-z item 41: Decrypt messages only if requested (some prettification still pending) (708741) +* prokde35-z item 9: third part: allow to disable local flags in read-only folders (708749) +* prokde35-z item 2: Better and shorter resource folder names, showing the folder's owner (709038) +* prokde35-z item 11: Message flags can be toggled by clicking into the corresponding header view columns (709066) +* prokde35-z item 48: Allow adding LDAP entries to distribution lists from the LDAP search dialog. Add the ability to import LDAP contacts into the addressbook. (709092) +* prokde35-z itme 42: Show colored indicators of signature and encryption state above the editor area (709142) + +Bugfixes +-------- +* kolab/issue1376: fix \Seen flag handling on initial folder download (708706) +* kolab/issue1924: encryption type is not stored if mail is saved to draft (709084) + +Merges +------ +* Merge of kpilot fixed in 3.5 trunk (707923) +* Merge of changes that help with folder open/close reference counting debugging (mostly from the SuSE team) (707659) + + +Friday, August 31st 2007 + +Features +-------- +* Scalix support for Kontact. + +Bugfixes +--------- +* kolab/issue1868: Crash after reply on a mail using search function (705173, merge issue 38) +* BUG: 93436 - Always encrypt to self function was always on. +* kolab/issue1923 Crash after pressing copy and paste in the composer. (706484) +* kolab/issue1713 @ (at) sign in realname confuses kontact when creating appointments (704180) +* kolab/issue/1894 Default width of the folder name column at first contact start is too small +* kolab/issue1918 favorite folder view migration default size could be better (706063) + +Merges +------ +* Merged kpilot, karm and kalarm changes from 3.5 branch into enterprise. + +Friday, 24th August 2007 + + +Features +---------- + - prokde35-z item 44: keep state of signing and encryption actions across drafts (fully implemented, ported to 3.5, trunk and proko2) (702320) + - prokde35-z item 35: consistent behavior of pasting URLs via keyboard shortcut, d'n'd and menu (fully implemented, ported to trunk) (702342) + - prokde35-z item 1: favourite folders view in KMail (fully implemented, port to trunk pending, merge 36) (702804) + - prokde35-z item 9: first part of this item, concerning sync of SEEN flags in read only folders (issue1376) (703473) + prokde35-z item 9: second part, ability to store custom flags on the server (704199) merges +- prokde35 w1-20 (665722) +- prokde35 w1-11: Let the navigator toolbar be always the last one, if it's in the top dockwindow ( 666073) + - prokde35 W1-19 / kolab/issue1749 Since mail folders are not groupware folders, do a full sync, when + the mail part is already active. (668988) + - prokde35 It's now possible to resize the magenta rectangle that represents the event in the freebusy + view, to change its start time, end time and duration (not only move it around, which preserved + duration). + - prokde35 w1-6 Implement a size column in the shared folder tree class and in kmail. Add the ability + to use a configurable color for the folder name and size when it is close to a configurable quota + threshold (provided the folder has quota info in the first place). Implement size retrieval for mbox + and maildir storage. + - prokde35 w1-23 Add a font family and size selector to the standalone readerwindow, which +overrides the usual font for display and printing for that particular mail. + - prokde35 w1-30 Add an option to show two agenda views (669438) + - prokde35 W1-30 Start of a gantt-based view for korganizer (669557) + - prokde35 W22: prepare printing of notes from Kontact, move existing KNotes printing code to a library + shared by KNotes and the Notes plugin ( 670739) + - prokde35 W1-32: add profile support for kontact, with global/user-specific profiles, and profile + import/export (671800) + - prokde35 w1-15 Show the delegator and delegate in the even preview. + ( related to kolab/issue1773 kolab/issue1774 kolab/issue1776) (670673). + - prokde35 w1-10 Show plugins which failed to load their part as disabled, +and make them not selectable.(670822) + - prokde35 W1-19 (sync buttons): Add submenu with "All" and the single accounts, similar as in +the check mail action + - Drag and drop notes - (662503) + - Implemented the ability to drag the current meeting time in the freebusy view, in order to visually + move the meeting to a spot where the attendees are free.(668059) + - Merge (mostly) hidden config options and their uses from proko2 + +Bugfixes +========= + - kolab/issue896 naming inconsistencies in Kontact, enterprise only so far (703752) + - kolab/issue1318 potential fix for crashes during filtering (703867) + - kolab/issue1743 retain the vacation file name through account editing (703997) + - kolab/issue1894 better initial name column width in the kmail folder tree (704165) + - kolab/issue1713 @ (at) sign in realname confuses kontact when creating appointments (704180) + - kolab/issue1882 Crash after reply to a revoked mail(700851) + - kolab/issue1815 convert KMHeaders::applyFiltersOnMsg to using serial numbers instead of message pointers [in the case not using action scheduler]. Speculative fix to Intevation issue #1815 (Filtering 1450 mails crashes kontact). ( 703867 ) + - More comprehensible documentation of KMFolder and friends ( 703507 ) + - kolab/issue1640 - Minimum sync-interval for kontact ( 703384 ) + - bugs.kde.org - 77223, 85630, 111419 ( 700059 ) + - bugs.kde.org - 144303 - When sending an encrypted message, with an empty body, and the + encryption key selection or encryption prefs selection dialogs were cancelled, mNewBodyPart was + deleted without ever having been initialised. Also init some other vars ASAP. (700035) + - kolab/issue1712 Update revision when adding new attendees.(663228) + - kolab/issue1672 Make sure to open the folder when saving a single message as well.(664661) + - Fix lack of double-quotes around folder name, making GEQUOTAROOT fail on folders with a space in the + name.(665276) + - Port the ability to add and remove subfolders from korganizer's resource view from proko2. (665274) + - kolab/issue1721 Don't exit when the popup menu is canceled.(665679) + - Allow to edit attachments of already existing messages.(668156) + - kolab/issue1642 kontact crashed after moving some events (670485) + - kolab/issue1738 (Kontact escapes part of content type of text/calendar messages, makes Outlook fail + to directly recognise invitations) + - kolab/issue864 prokde35 freebusy display takes internal or does not honor "nobody" annotation( + 669428) + - kolab/issue1759 (Dragging emails onto tasks does not enable reminders by default.) + - kolab/issue1766 prokde35 Kontact crashes after moving event in a free/busy list (TQListViewItem) + - kolab/issue1784 prokde35 Kontact sometimes crashes by creating a new event. + - kolab/issue1780 i18n of "Agenda View Calender Display" missing + - kolab/issue1773 (Delegate an event back to the organizator shows strange effects.) + - kolab/issue1769 (editing attachment of a new mail stops with couldn't write error) + - prokde35 issue1768 - Percentage symbol missing + - kolab/issue1529, start date is set correctly when recurrence date is set + - kolab/issue1792 (email duplication occurs when using client-side filters and disconnected imap) + - kolab/issue1786 (moving folder shows error message) + - kolab/issue1794 (kontact mail composer is missing a combobox to choose the encryption type) + - kolab/issue1760 (Cannot make reminders for emails if left icon bar is disabled.) + - kolab/issue1569 (Kontact sometimes doesn't allow dragging of encrypted messages) + - kolab/issue1763 (Kontact still believes imap quota is set after quota is removed) + - kolab/issue1803 crash after unsharing a folder. + - kolab/issue1844 ("Copy To" on a mail doesn't work for online IMAP to same server) + - kolab/issue1839 (Kontact crashes after opening two composer dialogs and switching between summary and notes) + - kolab/issue1715 (Other disconnected Imap folders good as sources for client filters.) + - kolab/issue1303 (Inform user when a folder gets unshared and move new items (prevent data loss)) + - kolab/issue1846 (Kontact crashes by moving many folders) + - Make sure the crypto state is not restored from headers if there are no headers. (703234) + - b.k.o 118060 - Ensure that there is a KMMainWidget before showing the config dialog, as parts of the config + (identitydialog) depend on its existence. (668075) + - b.k.o 121863 - Prevent crash if folder can't be found.(668092) + - Fixes from 3.5 merges - b.k.o 131176, 102001, 143511, 143866, 144166, 48461, 93199, 128435,50462, + 133395,144347,91252,143237,131029,144779,79685,85539,125920,146585,143869,140549,147602,143230,140881 + + +Current release is "Enterprise - Snapshot-06" + +Features: +========= + + +korganizer +----------- + - proko35 W-15 forwarding and delegation of iCal invitations + - proko35 W1-29 "attaching mails directly to events as either the full message or just the message body" + +Aegypten +------------- + - aegypten/issue 370 - Add scrollbar so that the dialog doesn't become too big when gpgconf gives us many options to configure. + - aegypten/issue 735 - Support for the NO_CHANGE flag from gpgconf 2.0.3 (readonly widgets) + +Bugfixes +======== + - kolab/issue 1698 -Ignore denied invitations from not invited attendees. + - kolab/issue1706 (addressbook: real adress type is broken) - introducing bugs in the GUI due to outlook compatibility, + + +Miscellaneous +============== + - prokde W1-16: when exporting a secret key to a .p12 file, allow the user to select the encoding (charset) of the passphrase in the p12 file. + - proko35 W1-8 Fix for usability issues with changes to the currently selected item when LDAP results come in delayed. + - kolab/35- W1-10 Add the possibility to have disabled plugins shown grayed out in the sidebar. +Configurable by non-gui option. + + +"Enterprise - Snapshot-05" +Features: +========= + +kmail +---------- +- proko35 W1-7 (cyclic section changes on tab and backtab keys in the addressee selection completion popup) +- Better unit test and automated packaging infrastructure. + +Bugfixes +========= +- Honor default forwarding settings for the toolbar button, the search window RMB and the keyboard shortcuts (issue 1530) +- Fix documentation related to issue 1530 +- Also change the font of cut messages in case the inactive color is to similar to the normal color.(issue 1180) + + + +"Enterprise - Snapshot-04" + +Features: +========= + +kmail +---------- +- proko35 W1-14 (automatic moving of not yet synced mails from folders with insufficient access rights to a lost+found folder) +- Unit tests fixed. +- Kolab/issue1530 - make preferred form of forwarding configurable + +Bugfixes +========= +- Fixes related to opening of *mbox files. +- Aegypten/issue734 - Moving email in dimap breaks signature, because lines removed + +Korganizer +----------- +- proko35 W1-9 icon alignment was improved. + +Additional Fixes +================ +- Rebranched libkcal and kitchensync. + + +"Enterprise - Snapshot-03" +Features: +========= + +kmail +---------- +- Kolab/Merge14/issue1095 -Add local subscription +- Kolab/issue1206 - kolabziward quirks +- Kolab/issue1207 - groupware folders only as local subscription still shows mails in inbox +- proko35 W1-5: selectable scope when reindexing a dimap cache + + +Bugfixes: +========= +- Kolab/issue1583 - Fixing OL compatibility with attachments that have umlauts in the filename. +- Kolab/issue 1658 - Adding a warning for empty To : field +- proko35 W1-1 - kontact discards signature settings when signature is deactivated + + + +"Enterprise - Snapshot-02" + +Features: +========= + +kmail +---------- +- Kolab/issue1385 - add/insert signature +- kolab/issue1216 - porting semicolon as separator to enterprise. +- kolab/issue1367 : Made ".mbox" as a default extension/filter while saving messages. + +Bugfixes: +========= + + +kmail +---------- +- kolab/issue 1632 - kontact sometimes crashs by moving mails from the message find dialog to a folder +- kolab/issue1384 - Don't allow base64 or qp encoding for message attachements, such +as when forwarding. + + +"Enterprise - Snapshot-01" + +Features: +========= + +kmail +---------- + +- proko35 Task - W1-24, Kolab/issue 1180 - Copy/move folders ( related kolab/issues 1294 and 1317 ) + +- proko35 Task - W1-2 , Ktnef + +- proko35 Task - W1-28, Search result DnD + +- Kolab/issue 1222 - Reduce the memory consumption. Improve speed. + +- IMAP Quota feature - Forward ports of r503566, r504908, r504943, r544380, r550654, r573761, r573797, r573829, and r503568 from proko2 + + + +Bugfixes: +========= + +general +---------- + +- Kolab/issue 1362 - PO Box in kolab format. + + +kmail +---------- + +- Apply fix for KMAcctCachedImap::processNewMail's crash (r631934) diff --git a/akregator/src/akregator.desktop b/akregator/src/akregator.desktop index 394cf186..eb8952d3 100644 --- a/akregator/src/akregator.desktop +++ b/akregator/src/akregator.desktop @@ -28,7 +28,6 @@ GenericName[hu]=RSS hírolvasó GenericName[is]=RSS fréttaforrit GenericName[it]=Lettore Fonti RSS GenericName[ja]=RSS ニュースリーダー -GenericName[ka]=RSS კვების წამკითხველი GenericName[kk]=RSS жаңалықтарын оқу GenericName[km]=កម្មវិធី​អាន​មតិព័ត៌មាន RSS GenericName[ko]=RSS 피드 리더 @@ -53,8 +52,7 @@ GenericName[sv]=Läsare av RSS-kanaler GenericName[ta]=RSS பீஃட் வாசிப்பான் GenericName[tr]=RSS Haber Kaynağı Okuyucu GenericName[uk]=Програма для читання подач RSS -GenericName[uz]=RSS yangiliklarni oʻquvchi -GenericName[uz@cyrillic]=RSS янгиликларни ўқувчи +GenericName[uz]=RSS янгиликларни ўқувчи GenericName[zh_CN]=RSS 种子阅读器 GenericName[zh_TW]=RSS Feed 閱讀器 Comment=An RSS Aggregator for KDE @@ -79,7 +77,6 @@ Comment[hu]=KDE-s hírolvasó RSS hírcsatornákhoz Comment[is]=RSS fréttaforrit fyrir KDE Comment[it]=Un concentratore KDE per RSS Comment[ja]=KDE 用 RSS アグリゲータ -Comment[ka]= RSS აგრეგატი KDE-სთვის Comment[kk]=KDE-нің RSS жаңалық агрегаторы Comment[km]=កម្មវិធី​អាន RSS សម្រាប់ KDE Comment[ko]=KDE용 RSS 리더 @@ -102,8 +99,7 @@ Comment[sv]=En RSS-samlare för KDE Comment[ta]=கேடியிக்கான ஒரு RSS சேர்ப்பான் Comment[tr]=Bir KDE RSS Okuyucusu Comment[uk]=Агрегатор RSS для KDE -Comment[uz]=KDE uchun RSS yangiliklarni oʻquvchi -Comment[uz@cyrillic]=KDE учун RSS янгиликларни ўқувчи +Comment[uz]=KDE учун RSS янгиликларни ўқувчи Comment[zh_CN]=KDE RSS 新闻收集器 Comment[zh_TW]=KDE 的 RSS 收集器 Terminal=false diff --git a/akregator/src/akregator_plugin.desktop b/akregator/src/akregator_plugin.desktop index 986515c0..9f8608bc 100644 --- a/akregator/src/akregator_plugin.desktop +++ b/akregator/src/akregator_plugin.desktop @@ -25,7 +25,6 @@ Comment[hu]=Akregator bővítőmodul Comment[is]=Íforrit fyrir Akregator Comment[it]=Plugin per Akregator Comment[ja]=Akregator 用プラグイン -Comment[ka]=Akregator-ის მოდული Comment[kk]=Akregator-дың плагин модулі Comment[km]=កម្មវិធី​ជំនួយ Akregator Comment[ko]=Akregator 플러그인 @@ -49,8 +48,7 @@ Comment[sr@Latn]=Priključak za Akregator Comment[sv]=Insticksprogram för Akregator Comment[tr]=Akregator Eklentisi Comment[uk]=Втулок для Akregator -Comment[uz]=Akregator uchun plagin -Comment[uz@cyrillic]=Akregator учун плагин +Comment[uz]=Akregator учун плагин Comment[zh_CN]=Akregator 插件 Comment[zh_TW]=Akregator 外掛程式 diff --git a/akregator/src/akregator_view.cpp b/akregator/src/akregator_view.cpp index d238307d..8776ec12 100644 --- a/akregator/src/akregator_view.cpp +++ b/akregator/src/akregator_view.cpp @@ -225,7 +225,7 @@ View::View( Part *part, TQWidget *parent, ActionManagerImpl* actionManager, cons setFocusPolicy(TQWidget::StrongFocus); TQVBoxLayout *lt = new TQVBoxLayout( this ); - + m_horizontalSplitter = new TQSplitter(TQSplitter::Horizontal, this); m_horizontalSplitter->setOpaqueResize(true); @@ -260,7 +260,7 @@ View::View( Part *part, TQWidget *parent, ActionManagerImpl* actionManager, cons connect(m_tagNodeListView, TQT_SIGNAL(signalContextMenu(KListView*, TreeNode*, const TQPoint&)), this, TQT_SLOT(slotFeedTreeContextMenu(KListView*, TreeNode*, const TQPoint&))); - + ProgressManager::self()->setFeedList(m_feedList); m_tabs = new TabWidget(m_horizontalSplitter); @@ -321,8 +321,12 @@ View::View( Part *part, TQWidget *parent, ActionManagerImpl* actionManager, cons connectFrame(m_mainFrame); m_tabs->addFrame(m_mainFrame); - m_horizontalSplitter->setSizes( Settings::splitter1Sizes() ); - m_articleSplitter->setSizes( Settings::splitter2Sizes() ); + const TQValueList sp1sizes = Settings::splitter1Sizes(); + if ( sp1sizes.count() >= m_horizontalSplitter->sizes().count() ) + m_horizontalSplitter->setSizes( sp1sizes ); + const TQValueList sp2sizes = Settings::splitter2Sizes(); + if ( sp2sizes.count() >= m_articleSplitter->sizes().count() ) + m_articleSplitter->setSizes( sp2sizes ); KConfig *conf = Settings::self()->config(); conf->setGroup("General"); @@ -374,7 +378,7 @@ void View::slotSettingsChanged() { // if tagging is hidden, show only feed list m_listTabWidget->setViewMode(Settings::showTaggingGUI() ? ListTabWidget::verticalTabs : ListTabWidget::single); - + } void View::slotOnShutdown() @@ -406,8 +410,12 @@ void View::slotOnShutdown() void View::saveSettings() { - Settings::setSplitter1Sizes( m_horizontalSplitter->sizes() ); - Settings::setSplitter2Sizes( m_articleSplitter->sizes() ); + const TQValueList spl1 = m_horizontalSplitter->sizes(); + if ( spl1.contains( 0 ) == 0 ) + Settings::setSplitter1Sizes( spl1 ); + const TQValueList spl2 = m_articleSplitter->sizes(); + if ( spl2.contains( 0 ) == 0 ) + Settings::setSplitter2Sizes( spl2 ); Settings::setViewMode( m_viewMode ); Settings::writeConfig(); } @@ -415,7 +423,7 @@ void View::saveSettings() void View::slotOpenNewTab(const KURL& url, bool background) { PageViewer* page = new PageViewer(this, "page"); - + connect( m_part, TQT_SIGNAL(signalSettingsChanged()), page, TQT_SLOT(slotPaletteOrFontChanged())); connect( page, TQT_SIGNAL(setTabIcon(const TQPixmap&)), @@ -822,7 +830,7 @@ void View::slotOpenURL(const KURL& url, Viewer* currentViewer, BrowserRun::Openi else { KParts::URLArgs args = currentViewer ? currentViewer->browserExtension()->urlArgs() : KParts::URLArgs(); - + BrowserRun* r = new BrowserRun(this, currentViewer, url, args, mode); connect(r, TQT_SIGNAL(signalOpenInViewer(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)), this, TQT_SLOT(slotOpenURLReply(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode))); @@ -832,7 +840,7 @@ void View::slotOpenURL(const KURL& url, Viewer* currentViewer, BrowserRun::Openi //TODO: KDE4 remove this ugly ugly hack void View::slotUrlClickedInViewer(const KURL& url, Viewer* viewer, bool newTab, bool background) { - + if (!newTab) { slotOpenURL(url, viewer, BrowserRun::CURRENT_TAB); @@ -983,7 +991,7 @@ void View::slotNextUnreadArticle() { if (m_viewMode == CombinedView) m_listTabWidget->activeView()->slotNextUnreadFeed(); - + TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); if (sel && sel->unread() > 0) m_articleList->slotNextUnreadArticle(); @@ -995,7 +1003,7 @@ void View::slotPrevUnreadArticle() { if (m_viewMode == CombinedView) m_listTabWidget->activeView()->slotPrevUnreadFeed(); - + TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); if (sel && sel->unread() > 0) m_articleList->slotPreviousUnreadArticle(); @@ -1211,8 +1219,8 @@ void View::slotOpenCurrentArticle() link = article.link(); else if (article.guidIsPermaLink()) link = KURL(article.guid()); - - if (link.isValid()) + + if (link.isValid()) { slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND); } @@ -1236,8 +1244,8 @@ void View::slotOpenCurrentArticleBackgroundTab() link = article.link(); else if (article.guidIsPermaLink()) link = KURL(article.guid()); - - if (link.isValid()) + + if (link.isValid()) { slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND); } @@ -1307,7 +1315,7 @@ void View::slotArticleDelete() msg = i18n("Are you sure you want to delete article %1?").arg(TQStyleSheet::escape(articles.first().title())); break; default: - msg = i18n("Are you sure you want to delete the selected article?", + msg = i18n("Are you sure you want to delete the selected article?", "Are you sure you want to delete the %n selected articles?", articles.count()); } @@ -1386,7 +1394,7 @@ void View::slotTextToSpeechRequest() else { TQString selectedText = static_cast(m_currentFrame->part())->selectedText(); - + if (!selectedText.isEmpty()) SpeechClient::self()->slotSpeak(selectedText, "en"); } @@ -1439,7 +1447,7 @@ void View::slotMouseOverInfo(const KFileItem *kifi) void View::readProperties(KConfig* config) { - + if (!Settings::resetQuickFilterOnNodeChange()) { m_searchBar->slotSetText(config->readEntry("searchLine")); @@ -1447,7 +1455,7 @@ void View::readProperties(KConfig* config) if (statusfilter != -1) m_searchBar->slotSetStatus(statusfilter); } - + int selectedID = config->readNumEntry("selectedNodeID", -1); if (selectedID != -1) { @@ -1471,7 +1479,7 @@ void View::saveProperties(KConfig* config) // save filter settings config->writeEntry("searchLine", m_searchBar->text()); config->writeEntry("searchCombo", m_searchBar->status()); - + TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); if (sel) diff --git a/akregator/src/eventsrc b/akregator/src/eventsrc index 3be7516a..6615ce17 100644 --- a/akregator/src/eventsrc +++ b/akregator/src/eventsrc @@ -29,7 +29,6 @@ Name[hu]=Hírforrás felvéve Name[is]=Straum bætt við Name[it]=Aggiunta fonte Name[ja]=フィード追加 -Name[ka]=დამატებულია კვება Name[kk]=Қор қосылды Name[km]=បាន​បន្ថែម​មតិព័ត៌មាន Name[ko]=피드 추가됨 @@ -53,8 +52,7 @@ Name[sv]=Kanal tillagd Name[ta]=பீஃட் சேர்க்கப்பட்டது Name[tr]=Haber kaynağı eklendi Name[uk]=Подачу додано -Name[uz]=Yangiliklar tasmasi qoʻshildi -Name[uz@cyrillic]=Янгиликлар тасмаси қўшилди +Name[uz]=Янгиликлар тасмаси қўшилди Name[zh_CN]=添加了新闻源 Name[zh_TW]=已加入 Feed Comment=A new feed was remotely added to Akregator @@ -78,7 +76,6 @@ Comment[hu]=Egy hírforrást távolról felvettek az Akregatorba Comment[is]=Nýjum straum var bætt við Akregator Comment[it]=Una nuova fonte è stata aggiunta ad Akregator da remoto Comment[ja]=新規フィードがリモートで Akregator に追加されました -Comment[ka]=Akregator-ის სიას დისტანციურად ახალი კვება დაემატა Comment[kk]=Akregator-ға қашықтан жаңа қор қосылды Comment[km]=បាន​បន្ថែម​មតិព័ត៌មាន​ថ្មី​មួយ​ពី​ចម្ងាយ​ទៅ Akregator Comment[ko]=Akregator에 피드가 원격으로 추가됨 @@ -102,8 +99,7 @@ Comment[sv]=En ny kanal har lagts till i aKregator utifrån Comment[ta]=Akregatorக்கு ஒரு புதிய உள்ளீடு சேர்க்கப்பட்டது Comment[tr]=Akregator'a yeni bir haber kaynağı eklendi Comment[uk]=Нову подачу було віддалено додано до Akregator -Comment[uz]=Akregator dasturiga yangi yangiliklar tasmasi qoʻshildi -Comment[uz@cyrillic]=Akregator дастурига янги янгиликлар тасмаси қўшилди +Comment[uz]=Akregator дастурига янги янгиликлар тасмаси қўшилди Comment[zh_CN]=新闻源远程添加进了 Akregator Comment[zh_TW]=已從遠端加入新的 feed 到 Akregator default_presentation=4 @@ -135,7 +131,6 @@ Name[hu]=Hírekk Name[is]=Nýjar greinar Name[it]=Nuovi articoli Name[ja]=新規記事 -Name[ka]=ახალი სტატიები Name[kk]=Жаңа мақалалар Name[km]=អត្ថបទ​ថ្មី Name[ko]=새 글 @@ -160,8 +155,7 @@ Name[sv]=Nya artiklar Name[ta]= புதிய செய்திகள் Name[tr]=Yeni Haberler Name[uk]=Нові статті -Name[uz]=Yangi maqolalar -Name[uz@cyrillic]=Янги мақолалар +Name[uz]=Янги мақолалар Name[zh_CN]=新闻文章 Name[zh_TW]=新文章 Comment=New articles were fetched @@ -188,7 +182,6 @@ Comment[hu]=Új hírek lettek letöltve Comment[is]=Nýjar greinar voru sóttar Comment[it]=I nuovi articoli sono stati recuperati Comment[ja]=新規記事を取得しました -Comment[ka]=მიღებულია ახალი სტატიები Comment[kk]=Жаңа мақалалар қабылданды Comment[km]=បាន​ប្រមូល​អត្ថបទ​ថ្មី Comment[ko]=새 글을 가져옴 diff --git a/akregator/src/mk4storage/akregator_mk4storage_plugin.desktop b/akregator/src/mk4storage/akregator_mk4storage_plugin.desktop index c7d899a7..1e63cfc0 100644 --- a/akregator/src/mk4storage/akregator_mk4storage_plugin.desktop +++ b/akregator/src/mk4storage/akregator_mk4storage_plugin.desktop @@ -21,7 +21,6 @@ Name[hu]=Metakit tároló Name[is]=Metakit geymslu bakendi Name[it]=Backend archiviazione metakit Name[ja]=メタキットストレージバックエンド -Name[ka]=Metakit მეხსიერების ბუფერი Name[kk]=Metakit архивтеу бағдарламасы Name[km]=កម្មវិធី​ខាង​ក្រោយ​សម្រាប់​រក្សាទុក (Metakit) Name[ko]=Metakit 저장소 백엔드 @@ -69,7 +68,6 @@ Comment[hu]=Akregator bővítőmodul Comment[is]=Íforrit fyrir Akregator Comment[it]=Plugin per Akregator Comment[ja]=Akregator 用プラグイン -Comment[ka]=Akregator-ის მოდული Comment[kk]=Akregator-дың плагин модулі Comment[km]=កម្មវិធី​ជំនួយ Akregator Comment[ko]=Akregator 플러그인 @@ -93,8 +91,7 @@ Comment[sr@Latn]=Priključak za Akregator Comment[sv]=Insticksprogram för Akregator Comment[tr]=Akregator Eklentisi Comment[uk]=Втулок для Akregator -Comment[uz]=Akregator uchun plagin -Comment[uz@cyrillic]=Akregator учун плагин +Comment[uz]=Akregator учун плагин Comment[zh_CN]=Akregator 插件 Comment[zh_TW]=Akregator 外掛程式 ServiceTypes=Akregator/Plugin diff --git a/certmanager/certmanager.cpp b/certmanager/certmanager.cpp index f12411bf..c078315b 100644 --- a/certmanager/certmanager.cpp +++ b/certmanager/certmanager.cpp @@ -889,7 +889,8 @@ void CertManager::slotDirmngrExited() { This slot will import CRLs from a file. */ void CertManager::importCRLFromFile() { - TQString filter = TQString("*.crl *.arl *-crl.der *-arl.der|") + i18n("Certificate Revocation List (*.crl *.arl *-crl.der *-arl.der)"); + // loadcrl can only work with DER encoded files (verified with dirmngr 1.0.3) + TQString filter = TQString("*.crl *.arl *-crl.der *-arl.der|") + i18n("Certificate Revocation List, DER encoded (*.crl *.arl *-crl.der *-arl.der)"); KURL url = KFileDialog::getOpenURL( TQString::null, filter, this, @@ -1277,9 +1278,11 @@ void CertManager::slotCertificateExportResult( const GpgME::Error & err, const T void CertManager::slotExportSecretKey() { Kleo::KeySelectionDialog dlg( i18n("Secret Key Export"), - i18n("Select the secret key to export " + "" + + i18n("Select the secret key to export " "(Warning: The PKCS#12 format is insecure; " - "exporting secret keys is discouraged):"), + "exporting secret keys is discouraged):") + + "", std::vector(), Kleo::KeySelectionDialog::SecretKeys|Kleo::KeySelectionDialog::SMIMEKeys, false /* no multiple selection */, diff --git a/certmanager/conf/kleopatra_config_appear.desktop b/certmanager/conf/kleopatra_config_appear.desktop index 8cbeaecf..fa6fa6ba 100644 --- a/certmanager/conf/kleopatra_config_appear.desktop +++ b/certmanager/conf/kleopatra_config_appear.desktop @@ -40,7 +40,6 @@ Name[hu]=Megjelenés Name[is]=Útlit Name[it]=Aspetto Name[ja]=外観 -Name[ka]=იერსახე Name[kk]=Сыртқы көрінісі Name[km]=រូបរាង Name[ko]=모양 @@ -67,8 +66,7 @@ Name[ta]=தோற்றம் Name[tg]=Намуди зоҳирӣ Name[tr]=Görünüm Name[uk]=Вигляд -Name[uz]=Koʻrinishi -Name[uz@cyrillic]=Кўриниши +Name[uz]=Кўриниши Name[zh_CN]=外观 Comment=Colors & Fonts Configuration Comment[af]=Kleur en skrif tipe opstelling @@ -97,7 +95,6 @@ Comment[hu]=A színek és betűtípusok beállítása Comment[is]=Stilling lita & leturgerða Comment[it]=Configurazione tipi di carattere e colori Comment[ja]=色とフォントの設定 -Comment[ka]=ფერთა და შრიფტთა კონფიგურაცია Comment[kk]=Түс & Қаріп параметрлері Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ​ពណ៌ & ពុម្ពអក្សរ Comment[ko]=색과 글꼴 설정 @@ -153,7 +150,6 @@ Keywords[hu]=szín,betűtípus,beállítások Keywords[is]=litir,letur, stillingar Keywords[it]=colori, font, caratteri, configurazione Keywords[ja]=色,フォント,設定 -Keywords[ka]=ფერები,შრიფტები, კონფიგურაცია Keywords[km]=ពណ៌,ពុម្ពអក្សរ,ការ​កំណត់​រចនាសម្ព័ន្ធ Keywords[ko]=색,글꼴,설정 Keywords[lt]=color,font, configuration, spalvų, šriftų konfigūravimas diff --git a/certmanager/conf/kleopatra_config_dirserv.desktop b/certmanager/conf/kleopatra_config_dirserv.desktop index 7a50a7c4..9153b003 100644 --- a/certmanager/conf/kleopatra_config_dirserv.desktop +++ b/certmanager/conf/kleopatra_config_dirserv.desktop @@ -27,7 +27,7 @@ Name[et]=Kataloogiteenused Name[eu]=Direktorio zerbitzuak Name[fa]=خدمات فهرست راهنما Name[fi]=Hakemistopalvelut -Name[fr]=Services d'annuaire +Name[fr]=Services de répertoires Name[fy]=Maptsjinsten Name[ga]=Seirbhísí Eolaire Name[gl]=Servicios de Directorio @@ -36,7 +36,6 @@ Name[hu]=Címtárszolgáltatások Name[is]=Nafnaþjónustur Name[it]=Servizi Directory Name[ja]=ディレクトリサービス -Name[ka]=საცნობარო მომსახურება Name[kk]=Каталог қызметтері Name[km]=សេវា​ថត Name[ko]=디렉터리 서비스 @@ -80,7 +79,7 @@ Comment[et]=LDAP kataloogiteenuste seadistused Comment[eu]=LDAP direktorio zerbitzuen konfigurazioa Comment[fa]=پیکربندی خدمات فهرست راهنمای LDAP Comment[fi]=Hakemistopalveluiden asetukset -Comment[fr]=Configuration des services d'annuaire LDAP +Comment[fr]=Configuration des répertoires LDAP Comment[fy]=Konfiguraasje foar LDAP-tsjinsten Comment[gl]=Configuración dos servicios de directorio LDAP Comment[he]=תצורה של שירותי ספרייה עבור LDAP @@ -88,7 +87,6 @@ Comment[hu]=Az LDAP címtárszolgáltatások beállításai Comment[is]=Stillingar LDAP uppflettingaþjónustunnar Comment[it]=Configurazione servizi LDAP Comment[ja]=LDAP ディレクトリサービスの設定 -Comment[ka]=LDAP საცნობარო მომსახურების კონფიგურაცია Comment[kk]=LDAP каталог қызметтер параметрлері Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ​របស់​សេវា​ថត LDAP Comment[ko]=LDAP 디렉터리 서비스 설정 @@ -133,7 +131,7 @@ Keywords[et]=ldap,kataloog,teenused Keywords[eu]=Idap, direktorio, zerbitzuak Keywords[fa]=ldap، فهرست راهنما، خدمات Keywords[fi]=ldap,hakemisto,palvelut -Keywords[fr]=ldap,annuaire,annuaires,service,services +Keywords[fr]=ldap,dossier,dossiers,service,services Keywords[fy]=ldap,directory,services,triemtafel,tsjinsten Keywords[ga]=ldap,eolaire,seirbhísí Keywords[gl]=ldap,directorio,servicios @@ -142,7 +140,6 @@ Keywords[hu]=ldap,címtár,szolgáltatások Keywords[is]=ldap,directory,services,nafnaþjónusta Keywords[it]=ldap, servizi, directory Keywords[ja]=ldap,ディレクトリ,サービス -Keywords[ka]=ldap,ცნობარი,მომსახურება Keywords[km]=ldap,ថត,សេវា Keywords[ko]=ldap,디렉터리,서비스 Keywords[lt]=ldap,directory,services,aplankai,tarnybos @@ -152,7 +149,7 @@ Keywords[nb]=ldap,mappe,tjenester Keywords[nds]=LDAP,Verteken,Deensten Keywords[ne]=ldap,डाइरेक्टरी,कार्य Keywords[nn]=ldap,katalog,tenester -Keywords[pl]=katalog,usługi katalogowe,usługi,LDAP +Keywords[pl]=ldap,katalog,usługi katalogowe,usługi,LDAP Keywords[pt]=ldap,directório,serviços Keywords[pt_BR]=ldap,diretório,serviços Keywords[ru]=LDAP,службы каталогов diff --git a/certmanager/conf/kleopatra_config_dnorder.desktop b/certmanager/conf/kleopatra_config_dnorder.desktop index dc46c331..a14ffb3c 100644 --- a/certmanager/conf/kleopatra_config_dnorder.desktop +++ b/certmanager/conf/kleopatra_config_dnorder.desktop @@ -34,7 +34,6 @@ Name[hu]=DN-attribútumsorrend Name[is]=DN eiginleikaröðun Name[it]=Attributi DN, ordine Name[ja]=DN 属性順位 -Name[ka]=DN ატრიბუტთა რიგი Name[kk]=DN-атрибуттер реті Name[km]=លំដាប់​គុណលក្ខណៈ DN Name[ko]=DN 분배 순서 @@ -85,7 +84,6 @@ Comment[hu]=A DN-attribútumok megjelenítési sorrendjének beállítása Comment[is]=Stilla röð DN eiginleikanna Comment[it]=Configura l'ordine in cui sono mostrati gli attributi DN Comment[ja]=DN 属性を表示する順位を設定 -Comment[ka]=DN ატრიბუტთა ჩვენების რიგის კონფიგურაცია Comment[kk]=DN атрибуттерді көрсету ретін баптау Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​លំដាប់ ដែល​ត្រូវ​បង្ហាញ​គុណលក្ខណៈ DN Comment[lt]=Konfigūruoti DN savybių rodymo tvarką @@ -137,7 +135,6 @@ Keywords[hu]=DN,sorrend,RDN,attribútum Keywords[is]=DN,röð,order,RDN,attribute Keywords[it]=DN,ordine,RDN,attributi Keywords[ja]=DN,順位,RDN,属性 -Keywords[ka]=DN,რიგი,RDN,ატრიბუტი Keywords[km]=DN,លំដាប់,RDN,គុណលក្ខណៈ Keywords[lt]=DN,order,RDN,attribute,savybė,tvarka Keywords[mk]=DN,order,RDN,attribute,редослед,атрибут diff --git a/certmanager/lib/backends/qgpgme/qgpgmecryptoconfig.cpp b/certmanager/lib/backends/qgpgme/qgpgmecryptoconfig.cpp index c6780050..ae45d683 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmecryptoconfig.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmecryptoconfig.cpp @@ -447,6 +447,13 @@ TQVariant QGpgMECryptoConfigEntry::stringToValue( const TQString& str, bool unes bool isString = isStringType(); if ( isList() ) { + if ( argType() == ArgType_None ) { + bool ok = true; + const TQVariant v = str.isEmpty() ? 0U : str.toUInt( &ok ) ; + if ( !ok ) + kdWarning(5150) << "list-of-none should have an unsigned int as value:" << str << endl; + return v; + } TQValueList lst; TQStringList items = TQStringList::split( ',', str ); for( TQStringList::const_iterator valit = items.begin(); valit != items.end(); ++valit ) { @@ -657,7 +664,10 @@ void QGpgMECryptoConfigEntry::resetToDefault() if ( mFlags & GPGCONF_FLAG_DEFAULT ) mValue = mDefaultValue; else if ( mArgType == ArgType_None ) - mValue = false; + if ( isList() ) + mValue = 0U; + else + mValue = false; } void QGpgMECryptoConfigEntry::setBoolValue( bool b ) @@ -715,7 +725,9 @@ void QGpgMECryptoConfigEntry::setNumberOfTimesSet( unsigned int i ) { Q_ASSERT( mArgType == ArgType_None ); Q_ASSERT( isList() ); - setUIntValue( i ); + mValue = i; + mSet = i > 0; + mDirty = true; } void QGpgMECryptoConfigEntry::setStringValueList( const TQStringList& lst ) diff --git a/certmanager/lib/backends/qgpgme/qgpgmedecryptjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmedecryptjob.cpp index 692efa32..15d3246f 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmedecryptjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmedecryptjob.cpp @@ -85,7 +85,10 @@ GpgME::DecryptionResult Kleo::QGpgMEDecryptJob::exec( const TQByteArray & cipher } void Kleo::QGpgMEDecryptJob::doOperationDoneEvent( const GpgME::Error & ) { - emit result( mCtx->decryptionResult(), mOutDataDataProvider->data() ); + const GpgME::DecryptionResult res = mCtx->decrypt( *mInData, *mOutData ); + const TQByteArray plainText = mOutDataDataProvider->data(); + getAuditLog(); + emit result( res, plainText ); } #include "qgpgmedecryptjob.moc" diff --git a/certmanager/lib/backends/qgpgme/qgpgmedecryptverifyjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmedecryptverifyjob.cpp index 68f67d0f..a14e7e18 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmedecryptverifyjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmedecryptverifyjob.cpp @@ -87,9 +87,11 @@ Kleo::QGpgMEDecryptVerifyJob::exec( const TQByteArray & cipherText, TQByteArray } void Kleo::QGpgMEDecryptVerifyJob::doOperationDoneEvent( const GpgME::Error & ) { - emit result( mCtx->decryptionResult(), - mCtx->verificationResult(), - mOutDataDataProvider->data() ); + const GpgME::DecryptionResult dr = mCtx->decryptionResult(); + const GpgME::VerificationResult vr = mCtx->verificationResult(); + const TQByteArray plainText = mOutDataDataProvider->data(); + getAuditLog(); + emit result( dr, vr, plainText ); } #include "qgpgmedecryptverifyjob.moc" diff --git a/certmanager/lib/backends/qgpgme/qgpgmedeletejob.cpp b/certmanager/lib/backends/qgpgme/qgpgmedeletejob.cpp index c06907ee..3665ee66 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmedeletejob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmedeletejob.cpp @@ -64,6 +64,7 @@ GpgME::Error Kleo::QGpgMEDeleteJob::start( const GpgME::Key & key, bool allowSec } void Kleo::QGpgMEDeleteJob::doOperationDoneEvent( const GpgME::Error & error ) { + getAuditLog(); emit result( error ); } diff --git a/certmanager/lib/backends/qgpgme/qgpgmedownloadjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmedownloadjob.cpp index 364e8f9f..979a8549 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmedownloadjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmedownloadjob.cpp @@ -72,7 +72,9 @@ GpgME::Error Kleo::QGpgMEDownloadJob::start( const TQStringList & pats ) { } void Kleo::QGpgMEDownloadJob::doOperationDoneEvent( const GpgME::Error & error ) { - emit result( error, mOutDataDataProvider->data() ); + const TQByteArray data = mOutDataDataProvider->data(); + getAuditLog(); + emit result( error, data ); } #include "qgpgmedownloadjob.moc" diff --git a/certmanager/lib/backends/qgpgme/qgpgmeencryptjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmeencryptjob.cpp index ddd51717..a6a380b0 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmeencryptjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmeencryptjob.cpp @@ -97,7 +97,10 @@ GpgME::EncryptionResult Kleo::QGpgMEEncryptJob::exec( const std::vectorencryptionResult(), mOutDataDataProvider->data() ); + mResult = mCtx->encryptionResult(); + const TQByteArray ciphertext = mOutDataDataProvider->data(); + getAuditLog(); + emit result( mResult, ciphertext ); } void Kleo::QGpgMEEncryptJob::showErrorDialog( TQWidget * parent, const TQString & caption ) const { diff --git a/certmanager/lib/backends/qgpgme/qgpgmeexportjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmeexportjob.cpp index c31074af..16e5fb77 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmeexportjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmeexportjob.cpp @@ -72,7 +72,9 @@ GpgME::Error Kleo::QGpgMEExportJob::start( const TQStringList & pats ) { } void Kleo::QGpgMEExportJob::doOperationDoneEvent( const GpgME::Error & error ) { - emit result( error, mOutDataDataProvider->data() ); + const TQByteArray data = mOutDataDataProvider->data(); + getAuditLog(); + emit result( error, data ); } #include "qgpgmeexportjob.moc" diff --git a/certmanager/lib/backends/qgpgme/qgpgmeimportjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmeimportjob.cpp index 7c698e7e..80792161 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmeimportjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmeimportjob.cpp @@ -75,11 +75,15 @@ GpgME::Error Kleo::QGpgMEImportJob::start( const TQByteArray & keyData ) { GpgME::ImportResult Kleo::QGpgMEImportJob::exec( const TQByteArray & keyData ) { setup( keyData ); - return mCtx->importKeys( *mInData ); + const GpgME::ImportResult res = mCtx->importKeys( *mInData ); + getAuditLog(); + return res; } void Kleo::QGpgMEImportJob::doOperationDoneEvent( const GpgME::Error & ) { - emit result( mCtx->importResult() ); + const GpgME::ImportResult res = mCtx->importResult(); + getAuditLog(); + emit result( res ); } diff --git a/certmanager/lib/backends/qgpgme/qgpgmejob.cpp b/certmanager/lib/backends/qgpgme/qgpgmejob.cpp index 72f9f77f..6d7ae5b4 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmejob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmejob.cpp @@ -236,21 +236,19 @@ void Kleo::QGpgMEJob::createOutData() { static const unsigned int GetAuditLogFlags = GpgME::Context::AuditLogWithHelp|GpgME::Context::HtmlAuditLog; -static TQString audit_log_as_html( GpgME::Context * ctx ) { - if ( !ctx ) - return TQString(); +static TQString audit_log_as_html( GpgME::Context * ctx, GpgME::Error & err ) { + assert( ctx ); QGpgME::QByteArrayDataProvider dp; GpgME::Data data( &dp ); assert( !data.isNull() ); - if ( const GpgME::Error err = ctx->getAuditLog( data, GetAuditLogFlags ) ) + if ( ( err = ctx->getAuditLog( data, GetAuditLogFlags ) ) ) return TQString(); - else - return TQString::fromUtf8( dp.data().data() ); + const TQByteArray ba = dp.data(); + return TQString::fromUtf8( ba.data(), ba.size() ); } void Kleo::QGpgMEJob::doSlotOperationDoneEvent( GpgME::Context * context, const GpgME::Error & e ) { if ( context == mCtx ) { - getAuditLog(); doEmitDoneSignal(); doOperationDoneEvent( e ); mThis->deleteLater(); @@ -258,7 +256,9 @@ void Kleo::QGpgMEJob::doSlotOperationDoneEvent( GpgME::Context * context, const } void Kleo::QGpgMEJob::getAuditLog() { - mAuditLogAsHtml = audit_log_as_html( mCtx ); + if ( !mCtx ) + return; + mAuditLogAsHtml = audit_log_as_html( mCtx, mAuditLogError ); } void Kleo::QGpgMEJob::doSlotCancel() { diff --git a/certmanager/lib/backends/qgpgme/qgpgmejob.h b/certmanager/lib/backends/qgpgme/qgpgmejob.h index 984bed44..0681c165 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmejob.h +++ b/certmanager/lib/backends/qgpgme/qgpgmejob.h @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -45,8 +46,6 @@ #include namespace GpgME { - class Error; - class Context; class Data; } @@ -120,6 +119,7 @@ namespace Kleo { virtual void doEmitDoneSignal() = 0; void doSlotCancel(); TQString auditLogAsHtml() const { return mAuditLogAsHtml; } + GpgME::Error auditLogError() const { return mAuditLogError; } private: /*! \reimp from GpgME::ProgressProvider */ @@ -146,6 +146,7 @@ namespace Kleo { unsigned int mNumPatterns; unsigned int mChunkSize; unsigned int mPatternStartIndex, mPatternEndIndex; + GpgME::Error mAuditLogError; TQString mAuditLogAsHtml; }; @@ -154,7 +155,7 @@ namespace Kleo { #define make_slot_cancel private: void slotCancel() { QGpgMEJob::doSlotCancel(); } #define make_progress_emitter private: void doEmitProgressSignal( const TQString & what, int cur, int tot ) { emit progress( what, cur, tot ); } #define make_done_emitter private: void doEmitDoneSignal() { emit done(); } -#define make_auditLogAsHtml private: TQString auditLogAsHtml() const { return QGpgMEJob::auditLogAsHtml(); } +#define make_auditLogAsHtml private: TQString auditLogAsHtml() const { return QGpgMEJob::auditLogAsHtml(); } GpgME::Error auditLogError() const { return QGpgMEJob::auditLogError(); } #define QGPGME_JOB make_slot_cancel make_progress_emitter make_done_emitter make_auditLogAsHtml #endif // __KLEO_QGPGMEJOB_H__ diff --git a/certmanager/lib/backends/qgpgme/qgpgmekeygenerationjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmekeygenerationjob.cpp index b226d649..16e13134 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmekeygenerationjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmekeygenerationjob.cpp @@ -80,7 +80,10 @@ GpgME::Error Kleo::QGpgMEKeyGenerationJob::start( const TQString & parameters ) } void Kleo::QGpgMEKeyGenerationJob::doOperationDoneEvent( const GpgME::Error & ) { - emit result( mCtx->keyGenerationResult(), mPubKeyDataProvider ? mPubKeyDataProvider->data() : TQByteArray() ); + const GpgME::KeyGenerationResult res = mCtx->keyGenerationResult(); + const TQByteArray data = mPubKeyDataProvider ? mPubKeyDataProvider->data() : TQByteArray() ; + getAuditLog(); + emit result( res, data ); } #include "qgpgmekeygenerationjob.moc" diff --git a/certmanager/lib/backends/qgpgme/qgpgmekeylistjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmekeylistjob.cpp index 9e663e92..b07135f0 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmekeylistjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmekeylistjob.cpp @@ -48,6 +48,7 @@ #include #include +#include #include @@ -97,6 +98,10 @@ GpgME::Error Kleo::QGpgMEKeyListJob::start( const TQStringList & pats, bool secr kdDebug(5150) << "QGpgMEKeyListJob::start(): retrying keylisting with chunksize " << chunkSize() << endl; continue; } + } else if ( err.code() == GPG_ERR_EOF ) { + kdDebug(5150) << "QGpgMEKeyListJob::start(): early end of keylisting, trying to fake an empty result" << endl; + TQTimer::singleShot( 10, this, TQT_SLOT(slotFakeOperationDoneEvent()) ); + return GpgME::Error(); } deleteLater(); mResult = GpgME::KeyListResult( 0, err ); @@ -158,6 +163,17 @@ void Kleo::QGpgMEKeyListJob::slotNextKeyEvent( GpgME::Context * context, const G emit nextKey( key ); } +void Kleo::QGpgMEKeyListJob::slotFakeOperationDoneEvent() { + const GpgME::KeyListResult res = mCtx->keyListResult(); + if ( !res.error().code() == GPG_ERR_EOF ) + kdDebug(5150) << "QGpgMEKeyListJob::slotFakeOperationDoneEvent: expected EOF, got " + << res.error().asString() << endl; + mResult = GpgME::KeyListResult(); + emit done(); + emit result( mResult ); + deleteLater(); +} + void Kleo::QGpgMEKeyListJob::slotOperationDoneEvent( GpgME::Context * context, const GpgME::Error & ) { if ( context != mCtx ) return; diff --git a/certmanager/lib/backends/qgpgme/qgpgmekeylistjob.h b/certmanager/lib/backends/qgpgme/qgpgmekeylistjob.h index 0bc84c08..38864107 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmekeylistjob.h +++ b/certmanager/lib/backends/qgpgme/qgpgmekeylistjob.h @@ -65,6 +65,7 @@ namespace Kleo { private slots: void slotNextKeyEvent( GpgME::Context * context, const GpgME::Key & key ); void slotOperationDoneEvent( GpgME::Context * context, const GpgME::Error & e ); + void slotFakeOperationDoneEvent(); private: void doOperationDoneEvent( const GpgME::Error &) {} // unused, we implement slotOperationDoneEvent ourselves. diff --git a/certmanager/lib/backends/qgpgme/qgpgmesignencryptjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmesignencryptjob.cpp index bc05eb86..9a469001 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmesignencryptjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmesignencryptjob.cpp @@ -109,7 +109,9 @@ Kleo::QGpgMESignEncryptJob::exec( const std::vector & signers, void Kleo::QGpgMESignEncryptJob::doOperationDoneEvent( const GpgME::Error & ) { mResult.first = mCtx->signingResult(); mResult.second = mCtx->encryptionResult(); - emit result( mResult.first, mResult.second, mOutDataDataProvider->data() ); + const TQByteArray cipherText = mOutDataDataProvider->data(); + getAuditLog(); + emit result( mResult.first, mResult.second, cipherText ); } void Kleo::QGpgMESignEncryptJob::showErrorDialog( TQWidget * parent, const TQString & caption ) const { diff --git a/certmanager/lib/backends/qgpgme/qgpgmesignjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmesignjob.cpp index 0042d6a3..01ae8a91 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmesignjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmesignjob.cpp @@ -102,7 +102,10 @@ GpgME::SigningResult Kleo::QGpgMESignJob::exec( const std::vector & } void Kleo::QGpgMESignJob::doOperationDoneEvent( const GpgME::Error & ) { - emit result( mResult = mCtx->signingResult(), mOutDataDataProvider->data() ); + mResult = mCtx->signingResult(); + const TQByteArray signature = mOutDataDataProvider->data(); + getAuditLog(); + emit result( mResult, signature ); } void Kleo::QGpgMESignJob::showErrorDialog( TQWidget * parent, const TQString & caption ) const { diff --git a/certmanager/lib/backends/qgpgme/qgpgmeverifydetachedjob.cpp b/certmanager/lib/backends/qgpgme/qgpgmeverifydetachedjob.cpp index b1022f61..41fa4849 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmeverifydetachedjob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmeverifydetachedjob.cpp @@ -89,7 +89,9 @@ GpgME::VerificationResult Kleo::QGpgMEVerifyDetachedJob::exec( const TQByteArray } void Kleo::QGpgMEVerifyDetachedJob::doOperationDoneEvent( const GpgME::Error & ) { - emit result( mCtx->verificationResult() ); + const GpgME::VerificationResult res = mCtx->verificationResult(); + getAuditLog(); + emit result( res ); } diff --git a/certmanager/lib/backends/qgpgme/qgpgmeverifyopaquejob.cpp b/certmanager/lib/backends/qgpgme/qgpgmeverifyopaquejob.cpp index 92e1917b..e4340aa3 100644 --- a/certmanager/lib/backends/qgpgme/qgpgmeverifyopaquejob.cpp +++ b/certmanager/lib/backends/qgpgme/qgpgmeverifyopaquejob.cpp @@ -84,7 +84,10 @@ GpgME::VerificationResult Kleo::QGpgMEVerifyOpaqueJob::exec( const TQByteArray & } void Kleo::QGpgMEVerifyOpaqueJob::doOperationDoneEvent( const GpgME::Error & ) { - emit result( mCtx->verificationResult(), mOutDataDataProvider->data() ); + const GpgME::VerificationResult res = mCtx->verificationResult(); + const TQByteArray plainText = mOutDataDataProvider->data(); + getAuditLog(); + emit result( res, plainText ); } diff --git a/certmanager/lib/kleo/job.cpp b/certmanager/lib/kleo/job.cpp index c5371b64..dedbebfc 100644 --- a/certmanager/lib/kleo/job.cpp +++ b/certmanager/lib/kleo/job.cpp @@ -52,9 +52,13 @@ #include "refreshkeysjob.h" #include "specialjob.h" +#include + #include #include +#include + Kleo::Job::Job( TQObject * parent, const char * name ) : TQObject( parent, name ) { @@ -75,6 +79,16 @@ TQString Kleo::Job::auditLogAsHtml() const { return TQString(); } +GpgME::Error Kleo::Job::auditLogError() const { + kdDebug() << "Kleo::Job::auditLogError() should be reimplemented in Kleo::Job subclasses!" << endl; + return GpgME::Error( gpg_error( GPG_ERR_NOT_IMPLEMENTED ) ); +} + +bool Kleo::Job::isAuditLogSupported() const { + const GpgME::Error err = auditLogError(); + return err.code() != GPG_ERR_NOT_IMPLEMENTED ; +} + #define make_job_subclass(x) \ Kleo::x::x( TQObject * parent, const char * name ) : Job( parent, name ) {} \ Kleo::x::~x() {} diff --git a/certmanager/lib/kleo/job.h b/certmanager/lib/kleo/job.h index 1c091bab..78ff58ce 100644 --- a/certmanager/lib/kleo/job.h +++ b/certmanager/lib/kleo/job.h @@ -38,6 +38,10 @@ class TQWidget; +namespace GpgME { + class Error; +} + namespace Kleo { /** @@ -67,6 +71,8 @@ namespace Kleo { virtual void showErrorDialog( TQWidget * parent=0, const TQString & caption=TQString::null ) const; virtual TQString auditLogAsHtml() const; + virtual GpgME::Error auditLogError() const; + bool isAuditLogSupported() const; public slots: virtual void slotCancel() = 0; diff --git a/certmanager/lib/libkleopatrarc.desktop b/certmanager/lib/libkleopatrarc.desktop index 3af8feaf..07af3adc 100644 --- a/certmanager/lib/libkleopatrarc.desktop +++ b/certmanager/lib/libkleopatrarc.desktop @@ -22,7 +22,6 @@ Name[hu]=Nem ellenőrzött kulcs Name[is]=Ekki staðfestur lykill Name[it]=Chiave non convalidata Name[ja]=認証されていない鍵 -Name[ka]=დაუმოწმებელი გასაღები Name[kk]=Тексерілмеген кілт Name[km]=កូនសោ​គ្មាន​សុពលភាព Name[ko]=검증되지 않은 키 @@ -77,7 +76,6 @@ Name[hu]=Lejárt kulcs Name[is]=Útrunninn lykill Name[it]=Chiave scaduta Name[ja]=期限切れの鍵 -Name[ka]=ვადაგასული გასაღები Name[kk]=Ескірген кілт Name[km]=កូនសោ​បាន​ផុតកំណត់ Name[ko]=만료된 키 @@ -130,7 +128,6 @@ Name[hu]=Visszavont kulcs Name[is]=Afturkallaður lykill Name[it]=Chiave revocata Name[ja]=破棄された鍵 -Name[ka]=ანულირებული გასაღები Name[kk]=Күші жойылған кілт Name[km]=កូនសោ​ត្រូវ​បាន​ដកហូត Name[ko]=거부된 키 @@ -183,7 +180,6 @@ Name[hu]=Megbízható gyökértanúsítvány Name[is]=Treyst rótarskilríki Name[it]=Certificato radice affidabile Name[ja]=信頼されたルート証明書 -Name[ka]= რუტის სანდო სერთიფიკატი Name[kk]=Сенім артылған түбір куәлігі Name[km]=វិញ្ញាបនបត្រ Root ដែល​អាច​ជឿទុកចិត្ត Name[lt]=Patikimas root sertifikatas @@ -236,7 +232,6 @@ Name[hu]=Nem megbízható gyökértanúsítvány Name[is]=Ekki traust rótarskilríki Name[it]=Certificato radice non affidabile Name[ja]=信頼されてないルート証明書 -Name[ka]=რუტის არასანდო სერტიფიკატი Name[kk]=Сенім артылмаған түбір куәлігі Name[km]=វិញ្ញាបនបត្រ Root ដែល​មិន​អាច​ជឿទុកចិត្ត Name[lt]=Nepatikimas root sertifikatas @@ -290,7 +285,6 @@ Name[hu]=Egyéb kulcsok Name[is]=Aðrir lyklar Name[it]=Altre chiavi Name[ja]=他の鍵 -Name[ka]=სხვა გასაღებები Name[kk]=Басқа кілттер Name[km]=កូនសោ​ផ្សេង​ទៀត Name[ko]=기타 키 @@ -316,8 +310,7 @@ Name[sv]=Andra nycklar Name[ta]=மற்ற விசைகள் Name[tr]=Diğer Anahtarlar Name[uk]=Інші ключі -Name[uz]=Boshqa kalitlar -Name[uz@cyrillic]=Бошқа калитлар +Name[uz]=Бошқа калитлар Name[zh_CN]=其它密钥 Name[zh_TW]=其他金鑰 diff --git a/certmanager/lib/tests/test_verify.cpp b/certmanager/lib/tests/test_verify.cpp index 54a88b4c..cb5db396 100644 --- a/certmanager/lib/tests/test_verify.cpp +++ b/certmanager/lib/tests/test_verify.cpp @@ -29,6 +29,7 @@ */ #include +#include #include #include diff --git a/certmanager/lib/ui/cryptoconfigdialog.cpp b/certmanager/lib/ui/cryptoconfigdialog.cpp index 47242994..0d383083 100644 --- a/certmanager/lib/ui/cryptoconfigdialog.cpp +++ b/certmanager/lib/ui/cryptoconfigdialog.cpp @@ -46,6 +46,12 @@ Kleo::CryptoConfigDialog::CryptoConfigDialog( Kleo::CryptoConfig* config, TQWidg setMainWidget( mMainWidget ); connect( mMainWidget, TQT_SIGNAL( changed() ), TQT_SLOT( slotChanged() ) ); enableButton( Apply, false ); + if ( mMainWidget->hasError() ) { + showButton( Default, false ); + showButton( User1, false ); + showButton( Apply, false ); + showButton( Ok, false ); + } // Automatically assign accelerators KAcceleratorManager::manage( this ); diff --git a/certmanager/lib/ui/cryptoconfigmodule.cpp b/certmanager/lib/ui/cryptoconfigmodule.cpp index 33e7129c..5b14d7c4 100644 --- a/certmanager/lib/ui/cryptoconfigmodule.cpp +++ b/certmanager/lib/ui/cryptoconfigmodule.cpp @@ -62,8 +62,20 @@ static inline TQPixmap loadIcon( TQString s ) { ->loadIcon( s.replace( TQRegExp( "[^a-zA-Z0-9_]" ), "_" ), KIcon::NoGroup, KIcon::SizeMedium ); } +static unsigned int num_components_with_options( const Kleo::CryptoConfig * config ) { + if ( !config ) + return 0; + const TQStringList components = config->componentList(); + unsigned int result = 0; + for ( TQStringList::const_iterator it = components.begin() ; it != components.end() ; ++it ) + if ( const Kleo::CryptoConfigComponent * const comp = config->component( *it ) ) + if ( !comp->groupList().empty() ) + ++result; + return result; +} + static const KJanusWidget::Face determineJanusFace( const Kleo::CryptoConfig * config ) { - return config && config->componentList().size() < 2 + return num_components_with_options( config ) < 2 ? KJanusWidget::Plain : KJanusWidget::IconList ; } @@ -115,6 +127,22 @@ Kleo::CryptoConfigModule::CryptoConfigModule( Kleo::CryptoConfig* config, TQWidg + scrollView->style().pixelMetric(TQStyle::PM_ScrollBarExtent), QMIN( compGUI->sizeHint().height(), dialogHeight ) ); } + if ( mComponentGUIs.empty() ) { + Q_ASSERT( face() == Plain ); + const TQString msg = i18n("The gpgconf tool used to provide the information " + "for this dialog does not seem to be installed " + "properly. It did not return any components. " + "Try running \"%1\" on the command line for more " + "information.") + .arg( components.empty() ? "gpgconf --list-components" : "gpgconf --list-options gpg" ); + TQLabel * label = new TQLabel( msg, vbox ); + label->setAlignment( TQt::WordBreak ); + label->setMinimumHeight( fontMetrics().lineSpacing() * 5 ); + } +} + +bool Kleo::CryptoConfigModule::hasError() const { + return mComponentGUIs.empty(); } void Kleo::CryptoConfigModule::save() diff --git a/certmanager/lib/ui/cryptoconfigmodule.h b/certmanager/lib/ui/cryptoconfigmodule.h index 70c4a269..167c3ac8 100644 --- a/certmanager/lib/ui/cryptoconfigmodule.h +++ b/certmanager/lib/ui/cryptoconfigmodule.h @@ -50,6 +50,8 @@ namespace Kleo { public: CryptoConfigModule( Kleo::CryptoConfig* config, TQWidget * parent=0, const char * name=0 ); + bool hasError() const; + void save(); void reset(); // i.e. reload current settings, discarding user input void defaults(); diff --git a/certmanager/lib/ui/keyselectiondialog.cpp b/certmanager/lib/ui/keyselectiondialog.cpp index fcb25751..fc590d49 100644 --- a/certmanager/lib/ui/keyselectiondialog.cpp +++ b/certmanager/lib/ui/keyselectiondialog.cpp @@ -61,6 +61,8 @@ #include #include #include +#include +#include // Qt #include @@ -85,7 +87,12 @@ static bool checkKeyUsage( const GpgME::Key & key, unsigned int keyUsage ) { if ( keyUsage & Kleo::KeySelectionDialog::ValidKeys ) { if ( key.isInvalid() ) - kdDebug() << "key is invalid - ignoring" << endl; + if ( key.keyListMode() & GpgME::Context::Validate ) { + kdDebug() << "key is invalid" << endl; + return false; + } else { + kdDebug() << "key is invalid - ignoring" << endl; + } if ( key.isExpired() ) { kdDebug() << "key is expired" << endl; return false; @@ -307,6 +314,28 @@ Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title, init( rememberChoice, extendedSelection, text, TQString::null ); } +Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title, + const TQString & text, + const TQString & initialQuery, + const std::vector & selectedKeys, + unsigned int keyUsage, + bool extendedSelection, + bool rememberChoice, + TQWidget * parent, const char * name, + bool modal ) + : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ), + mOpenPGPBackend( 0 ), + mSMIMEBackend( 0 ), + mRememberCB( 0 ), + mSelectedKeys( selectedKeys ), + mKeyUsage( keyUsage ), + mSearchText( initialQuery ), + mInitialQuery( initialQuery ), + mCurrentContextMenuItem( 0 ) +{ + init( rememberChoice, extendedSelection, text, initialQuery ); +} + Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title, const TQString & text, const TQString & initialQuery, @@ -321,6 +350,7 @@ Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title, mRememberCB( 0 ), mKeyUsage( keyUsage ), mSearchText( initialQuery ), + mInitialQuery( initialQuery ), mCurrentContextMenuItem( 0 ) { init( rememberChoice, extendedSelection, text, initialQuery ); @@ -340,11 +370,26 @@ void Kleo::KeySelectionDialog::init( bool rememberChoice, bool extendedSelection mTopLayout = new TQVBoxLayout( page, 0, spacingHint() ); if ( !text.isEmpty() ) { - TQLabel* textLabel = new TQLabel( text, page ); - textLabel->setAlignment( textLabel->alignment() | Qt::WordBreak ); - mTopLayout->addWidget( textLabel ); + if ( text.startsWith( "" ) ) { + KActiveLabel *textLabel = new KActiveLabel( text, page ); + disconnect( textLabel, TQT_SIGNAL(linkClicked(const TQString&)), textLabel, TQT_SLOT(openLink(const TQString&)) ); + connect( textLabel, TQT_SIGNAL(linkClicked(const TQString&)), TQT_SLOT(slotStartCertificateManager(const TQString&)) ); + textLabel->setAlignment( textLabel->alignment() | TQt::WordBreak ); + mTopLayout->addWidget( textLabel ); + } else { + KActiveLabel *textLabel = new KActiveLabel( text, page ); + textLabel->setAlignment( textLabel->alignment() | TQt::WordBreak ); + mTopLayout->addWidget( textLabel ); + } } + TQPushButton * const searchExternalPB + = new TQPushButton( i18n("Search for &External Certificates"), page ); + mTopLayout->addWidget( searchExternalPB, 0, TQt::AlignLeft ); + connect( searchExternalPB, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotStartSearchForExternalCertificates()) ); + if ( initialQuery.isEmpty() ) + searchExternalPB->hide(); + TQHBoxLayout * hlay = new TQHBoxLayout( mTopLayout ); // inherits spacing TQLineEdit * le = new TQLineEdit( page ); le->setText( initialQuery ); @@ -500,10 +545,12 @@ void Kleo::KeySelectionDialog::slotHelp() emit helpClicked(); } -void Kleo::KeySelectionDialog::slotStartCertificateManager() +void Kleo::KeySelectionDialog::slotStartCertificateManager( const TQString &query ) { KProcess certManagerProc; certManagerProc << "kleopatra"; + if ( !query.isEmpty() ) + certManagerProc << "--external" << "--query" << KURL::decode_string( query ); if( !certManagerProc.start( KProcess::DontCare ) ) KMessageBox::error( this, i18n( "Could not start certificate manager; " diff --git a/certmanager/lib/ui/keyselectiondialog.h b/certmanager/lib/ui/keyselectiondialog.h index fd8f6739..ab8dc756 100644 --- a/certmanager/lib/ui/keyselectiondialog.h +++ b/certmanager/lib/ui/keyselectiondialog.h @@ -91,6 +91,15 @@ namespace Kleo { bool rememberChoice=false, TQWidget * parent=0, const char * name=0, bool modal=true ); + KeySelectionDialog( const TQString & title, + const TQString & text, + const TQString & initialPattern, + const std::vector & selectedKeys, + unsigned int keyUsage=AllKeys, + bool extendedSelection=false, + bool rememberChoice=false, + TQWidget * parent=0, const char * name=0, + bool modal=true ); KeySelectionDialog( const TQString & title, const TQString & text, const TQString & initialPattern, @@ -129,7 +138,10 @@ namespace Kleo { private slots: void slotRereadKeys(); - void slotStartCertificateManager(); + void slotStartCertificateManager( const TQString &query = TQString() ); + void slotStartSearchForExternalCertificates() { + slotStartCertificateManager( mInitialQuery ); + } void slotKeyListResult( const GpgME::KeyListResult & ); void slotSelectionChanged(); void slotCheckSelection() { slotCheckSelection( 0 ); } @@ -170,6 +182,7 @@ namespace Kleo { TQTimer * mStartSearchTimer; // cross-eventloop temporaries: TQString mSearchText; + const TQString mInitialQuery; Kleo::KeyListViewItem * mCurrentContextMenuItem; int mTruncated, mListJobCount, mSavedOffsetY; }; diff --git a/certmanager/lib/ui/messagebox.cpp b/certmanager/lib/ui/messagebox.cpp index ddce8f04..e05a21b9 100644 --- a/certmanager/lib/ui/messagebox.cpp +++ b/certmanager/lib/ui/messagebox.cpp @@ -46,10 +46,15 @@ #include #include #include +#include #include #include #include +#include +#include + +#include using namespace Kleo; using namespace GpgME; @@ -74,6 +79,7 @@ public: explicit AuditLogViewer( const TQString & log, TQWidget * parent=0, const char * name=0, WFlags f=0 ) : KDialogBase( parent, name, false, i18n("View GnuPG Audit Log"), Close|User1|User2, Close, false, KGuiItem_save(), KGuiItem_copy() ), + m_log( /* sic */ ), m_textEdit( new TQTextEdit( this, "m_textEdit" ) ) { setWFlags( f ); @@ -85,7 +91,18 @@ public: ~AuditLogViewer() {} void setAuditLog( const TQString & log ) { - m_textEdit->setText( log ); + if ( log == m_log ) + return; + m_log = log; + m_textEdit->setText( "" + log + "" ); + const TQRect rect = m_textEdit->paragraphRect( 0 ); + kdDebug() << "setAuditLog: rect = " << rect << endl; + if ( !rect.isValid() ) + return; + TQSize maxSize = qApp->desktop()->screenGeometry( this ).size() * 2 / 3 ; + if ( !maxSize.isValid() ) + maxSize = TQSize( 640, 480 ); + m_textEdit->setMinimumSize( rect.size().boundedTo( maxSize ) ); } private: @@ -98,7 +115,12 @@ private: KSaveFile file( fileName ); if ( TQTextStream * const s = file.textStream() ) { - *s << m_textEdit->text() << endl; + *s << ""; + if ( !caption().isEmpty() ) + *s << "\n" << /*TQt*/TQStyleSheet::escape( caption() ) << "\n"; + *s << "\n" + << m_log + << "\n" << endl; file.close(); } @@ -114,6 +136,7 @@ private: } private: + TQString m_log; TQTextEdit * m_textEdit; }; @@ -125,13 +148,23 @@ void MessageBox::auditLog( TQWidget * parent, const Job * job, const TQString & if ( !job ) return; - if ( !GpgME::hasFeature( AuditLogFeature ) ) { + if ( !GpgME::hasFeature( AuditLogFeature ) || !job->isAuditLogSupported() ) { KMessageBox::information( parent, i18n("Your system does not have support for GnuPG Audit Logs"), i18n("System Error") ); return; } + const GpgME::Error err = job->auditLogError(); + + if ( err.code() != GPG_ERR_NO_DATA ) { + KMessageBox::information( parent, i18n("An error occurred while trying to retrieve the GnuPG Audit Log:\n%1") + .arg( TQString::fromLocal8Bit( err.asString() ) ), + i18n("GnuPG Audit Log Error") ); + return; + } + const TQString log = job->auditLogAsHtml(); + if ( log.isEmpty() ) { KMessageBox::information( parent, i18n("No GnuPG Audit Log available for this operation."), i18n("No GnuPG Audit Log") ); @@ -143,7 +176,7 @@ void MessageBox::auditLog( TQWidget * parent, const Job * job, const TQString & // static void MessageBox::auditLog( TQWidget * parent, const TQString & log, const TQString & caption ) { - AuditLogViewer * const alv = new AuditLogViewer( "" + log + "", parent, "alv", Qt::WDestructiveClose ); + AuditLogViewer * const alv = new AuditLogViewer( log, parent, "alv", Qt::WDestructiveClose ); alv->setCaption( caption ); alv->show(); } @@ -246,9 +279,35 @@ void MessageBox::error( TQWidget * parent, const SigningResult & sresult, const make( parent, TQMessageBox::Critical, to_error_string( sresult, eresult ), job, caption, options ); } +// static +bool MessageBox::showAuditLogButton( const Kleo::Job * job ) { + if ( !job ) { + kdDebug() << "not showing audit log button (no job instance)" << endl; + return false; + } + if ( !GpgME::hasFeature( GpgME::AuditLogFeature ) ) { + kdDebug() << "not showing audit log button (gpgme too old)" << endl; + return false; + } + if ( !job->isAuditLogSupported() ) { + kdDebug() << "not showing audit log button (not supported)" << endl; + return false; + } + if ( job->auditLogError().code() == GPG_ERR_NO_DATA ) { + kdDebug() << "not showing audit log button (GPG_ERR_NO_DATA)" << endl; + return false; + } + if ( !job->auditLogError() && job->auditLogAsHtml().isEmpty() ) { + kdDebug() << "not showing audit log button (success, but result empty)" << endl; + return false; + } + return true; +} + + // static void MessageBox::make( TQWidget * parent, TQMessageBox::Icon icon, const TQString & text, const Job * job, const TQString & caption, int options ) { - KDialogBase * dialog = GpgME::hasFeature( GpgME::AuditLogFeature ) + KDialogBase * dialog = showAuditLogButton( job ) ? new KDialogBase( caption, KDialogBase::Yes | KDialogBase::No, KDialogBase::Yes, KDialogBase::Yes, parent, "error", true, true, diff --git a/certmanager/lib/ui/messagebox.h b/certmanager/lib/ui/messagebox.h index 5ed2edaf..ee72e43c 100644 --- a/certmanager/lib/ui/messagebox.h +++ b/certmanager/lib/ui/messagebox.h @@ -40,6 +40,7 @@ namespace GpgME { class EncryptionResult; class DecryptionResult; class VerificationResult; + class Error; } namespace Kleo { @@ -73,6 +74,8 @@ namespace Kleo { static void auditLog( TQWidget * parent, const TQString & log, const TQString & caption ); static void auditLog( TQWidget * parent, const TQString & log ); + static bool showAuditLogButton( const Kleo::Job * job ); + private: static void make( TQWidget * parent, TQMessageBox::Icon icon, const TQString & text, const Kleo::Job * job, const TQString & caption, int options ); }; diff --git a/cvs.sh.diff b/cvs.sh.diff new file mode 100644 index 00000000..313956b0 --- /dev/null +++ b/cvs.sh.diff @@ -0,0 +1,13 @@ +Index: admin/cvs.sh +=================================================================== +--- admin/cvs.sh (revision 637526) ++++ admin/cvs.sh (working copy) +@@ -592,7 +592,7 @@ for i in `ls -1 po.backup/*.pot 2>/dev/n + test -f po/$i || echo "disappeared: $i" + done + for i in `ls -1 po/*.pot 2>/dev/null | sed -e "s#po/##"`; do +- sed -e 's,^"Content-Type: text/plain; charset=CHARSET\\n"$,"Content-Type: text/plain; charset=UTF-8\\n",' po/$i > po/$i.new && mv po/$i.new po/$i ++ sed -e 's,^"Content-Type: text/plain; charset=CHARSET\\n"$,"Content-Type: text/plain; charset=UTF-8\\n",; s,"Content-Transfer-Encoding: ENCODING\\n","Content-Transfer-Encoding: 8bit\\n", ; s,"Language-Team: LANGUAGE \\n","Language-Team: LANGUAGE \\n",' po/$i > po/$i.new && mv po/$i.new po/$i + #msgmerge -q -o po/$i po/$i po/$i + egrep -v '^#[^,]' po/$i | egrep '^.*[^ ]+.*$' | grep -v "\"POT-Creation" > temp.pot + if test -f po.backup/$i && ! cmp -s temp.pot po.backup/$i; then diff --git a/doc/kleopatra/index.docbook b/doc/kleopatra/index.docbook index ba467215..09f9531b 100644 --- a/doc/kleopatra/index.docbook +++ b/doc/kleopatra/index.docbook @@ -146,8 +146,8 @@ menu, as well as from the command line. The &kleopatra; executable is named kleopatra. This program is named after Cleopatra, a famous female -Egyptian pharaoh that lived at the time of Julius Caesar, whom she is -said to have had an intimate relationship with. +Egyptian pharaoh that lived at the time of Julius Caesar, with whom +she had a child, Caesarion, unacknowledged as his heir. The name was chosen since this program originates from the Ägypten @@ -494,7 +494,7 @@ command line interface). -Esc +&Esc; ViewStop Operation diff --git a/doc/kmail/configure.docbook b/doc/kmail/configure.docbook index a66665ad..7643b356 100644 --- a/doc/kmail/configure.docbook +++ b/doc/kmail/configure.docbook @@ -648,7 +648,7 @@ so you can choose a monospaced font for the date field for better readability.Quoted text colors only work in the message reader, not in the composer. If you want folders which are close to their quota -(space alotment, usually used on IMAP servers) to be displayed in a different color, +(space allotment, usually used on IMAP servers) to be displayed in a different color, you can specify a percentage value as a threshold for this. The color to be used can be configured along with the other custom colors. @@ -657,6 +657,11 @@ can be configured along with the other custom colors. Layout +If the Close message window after replying or forwarding option is +activated, KMail will close the message window after replying to or forwarding the message that +is displayed in the window. This only applies to the separate message window, not to the embedded +message preview pane. + Show HTML status bar activates a bar at the left side of the reader pane that tells you if a message is &html; or not. This is important because &html; messages might imitate the look of a signed and encrypted message, so @@ -777,6 +782,32 @@ folder will be selected in &kmail;'s main window. + + + Only quote selected text when repyling + + + + If checked, &kmail; will quote only the selected text in the message window, + instead of the complete message, when replying. + With this, it is possible to quickly generate replies that quote only the relevant + paragraph to which you are actually replying, and omits the other paragraphs that are + not relevant to the context. + + + + + + Remove the signature when replying + + + + If checked, &kmail; will remove the signature from the quoted text + when replying to a message. This is useful since it reduces the amount + of quoted text, which makes it easier to read the reply. + + + Automatically request message disposition notifications @@ -806,6 +837,19 @@ folder will be selected in &kmail;'s main window. + + + Warn if the number of recipients is larger than + + + + If the number of recipients is larger than this value, KMail will + warn and ask for a confirmation before sending the mail. + The default is to warn if there are more than 5 recipients. + This warning can be turned off. + + + Autosave interval @@ -819,6 +863,18 @@ folder will be selected in &kmail;'s main window. + + + Default Forwarding Type + + + + Lets you decide whether you want to forward messages inline or as an attachment by default. + This changes which type of forwarding mechanism is used when you click the toolbar icon or + when you activate the shortcut for forwarding. + + + External Editor @@ -1102,7 +1158,7 @@ of key words can be modified. Attaches the complete message to the disposition notification. Usually, this is overkill. It does - not add any valueable information that cannot be + not add any valuable information that cannot be deduced from the message headers alone, but people sometimes insist on this, since it is much easier for humans to correlate the content of the message @@ -1276,7 +1332,7 @@ of key words can be modified. - If checked, everytime you encrypt a message, a dialog + If checked, every time you encrypt a message, a dialog will appear that presents you with the encryption keys that will be used for each recipient. You can then review the choice of keys, change them, and approve or @@ -1335,7 +1391,7 @@ of key words can be modified. - On this tab you can switch security-relavant warnings on and + On this tab you can switch security-relevant warnings on and off. @@ -1386,7 +1442,7 @@ of key words can be modified. If checked, &kmail; will emit a warning if an &smime; - certifciate or &openpgp; key will be used for a recipient + certificate or &openpgp; key will be used for a recipient whose email address is not listed in the email addresses stored in the certificate. @@ -1698,7 +1754,7 @@ of key words can be modified. backends. Below each backend entry, you can see what protocols (&openpgp; and/or &smime;) the backend supports. If a protocol is not listed, the backend does not support it. If it is - listed, but greyed out, the backend supports the protocol, but + listed, but grayed out, the backend supports the protocol, but some required programs were not found, or other errors occurred during initialization. If you press Rescan, a dialog box will appear that @@ -1793,12 +1849,39 @@ Correspondingly, if you ask &kmail; to go to the previous unread message. -Jump to first unread message when entering a folder + +When entering a folder + +This option controls what happens if you open a folder. + + +If Jump to First New Message is selected then &kmail; will select the +first message it finds that is marked as new. + + + +If Jump to First Unread or New Message is selected then &kmail; will +select the first message it finds that is marked either as new or as unread. + + + +If Jump to Last Selected Message is selected then &kmail; will select +the message that was selected when the folder was open the last time. + + + +If Jump to Newest Message is selected then &kmail; will select the newest +message by date. + + -If this option is enabled &kmail; will go to the first unread message -when you enter a folder; if it is not enabled, &kmail; will go to first -new message or, if there is no new message, to the message -that was selected when you last left the folder. +If Jump to Oldest Message is selected then &kmail; will select the oldest +message by date. + + + + + @@ -1908,6 +1991,21 @@ option is selected. Invitations use to be send as attachments to a mail. By enabling this option, you let the invitation mails to be sent in the text of the mail, which is necessary to send invitations and replies to Microsoft Outlook(tm). + +Exchange compatible invitations naming + +Microsoft Outlook, when used in combination with a Microsoft Exchange +server, has a problem understanding standards-compliant groupware e-mail. +Enable this option to send groupware invitations in a way that Microsoft +Exchange understands. The invitation will be sent as an attachment with name ical.ics. + + + +Outlook compatible invitation reply comments + +When the user provides comments when responding to invitations, send the comment in way that Microsoft Outlook(tm) understands. If this option is not enabled, the response comments will not be seen in Outlook. + + @@ -1934,7 +2032,7 @@ SendMDNsWithEmptySender=true to the [MDN] section of the kmail configuration file. If there is no such section, simply add "[MDN]" on a line by itself just above the option. -Note that the default setting of "false" strictly speaking violates internet standards, but is +Note that the default setting of "false" strictly speaking violates Internet standards, but is set that way for practical reasons, to avoid servers rejecting MDNs that KMail generates because they think they are SPAM. @@ -1955,20 +2053,6 @@ The option is enabled by default. To disable the feature, add a line reading (un - -ForwardingInlineByDefault - - -This option allows you to make inline forwarding the default forwarding method instead -of forwarding as attachement. - -To enable the feature, add a line reading (under [Composer] section): - -ForwardingInlineByDefault=true - - - - MaximumAttachmentSize @@ -2011,6 +2095,73 @@ To disable the feature, add a line reading (under [OutOfOffice] section): + +Allow out-of-office settings to be adjusted by the user. + + +In case you don't want users to be able to upload their own out-of-office scripts to +the server you can add: + +AllowOutOfOfficeSettings=false + +to the [OutOfOffice] section of the kmail configuration file. If there is no such section, simply add "[OutOfOffice]" on +a line by itself just above the option. + + + + + +Allow out-of-office scripts to be uploaded by the user, but no settings changed. + + +If you want to allow your users to upload their own out-of-office scripts but you do not want them to be +able to change the domain to react to and the react-to-spam setting, you can add: + +AllowOutOfOfficeUploadButNoSettings=true + +to the [OutOfOffice] section of the kmail configuration file. If there is no such section, simply add "[OutOfOffice]" on +a line by itself just above the option. + + + + + + +Default domain to limit out-of-office replies to. + + +When editing vacation (out-of-office) scripts, the user can specify a domain to +limit the sending of such replies to. To pre-load this setting with a default +value, add + +OutOfOfficeDomain=myMailDomain.test + +to the [OutOfOffice] section of the kmail configuration file. If there is no such section, simply add "[OutOfOffice]" on +a line by itself just above the option. + +Applying defaults to the out-of-office configuration dialog via the corresponding +button will restore this domain, in case the user has changed it. + + + + + +Enable sending of out-of-office replies to messages marked as SPAM. + + +By default, out-of-office replies are not sent to messages marked as SPAM. To override this +behavior, add + +OutOfOfficeReactToSpam=true + +to the [OutOfOffice] section of the kmail configuration file. If there is no such section, simply add "[OutOfOffice]" on +a line by itself just above the option. + +Applying defaults to the out-of-office configuration dialog via the corresponding +button will restore this setting, in case the user has changed it. + + + disregardUmask @@ -2030,7 +2181,7 @@ To enable the feature, add a line reading (under [General] section): Activate this option to automate the handling of not yet uploaded messages in disconnected IMAP folders that can not be uploaded. This can happen if the folder was removed from the server or your access rights have been restricted. Such messages will automatically moved to a newly created lost+found folder if -this option is enabled, you will be ask how to proceed everytime otherwise. +this option is enabled, you will be ask how to proceed every time otherwise. To enable the feature, add a line reading: diff --git a/doc/kmail/index.docbook b/doc/kmail/index.docbook index fe38a510..06faeb06 100644 --- a/doc/kmail/index.docbook +++ b/doc/kmail/index.docbook @@ -9,6 +9,7 @@ + @@ -126,6 +127,7 @@ &kmail-menus; &kmail-faq; &kmail-importing; +&kmail-chiasmus; &kmail-credits-and-licenses; diff --git a/doc/kmail/using-chiasmus.docbook b/doc/kmail/using-chiasmus.docbook new file mode 100644 index 00000000..cd075d86 --- /dev/null +++ b/doc/kmail/using-chiasmus.docbook @@ -0,0 +1,158 @@ + + + + + +Jan-Oliver +Wagner +
+jan@intevation.de +
+
+ +Till +Adam +
+till@kdab.net +
+
+ +
+ +2005-06-30 +1.8 +
+ +Using the Chiasmus encryption tool + + +Preparations + + +To encrypt and decrypt with Chiasmus, you need the BSI's Chiasmus +command line tool for GNU/Linux. It is intended solely for use in +government agencies and availabe from the BSI on request. + + + +When executed without parameters, the chiasmus +program should output something like: + + + + +Chiasmus (R) fuer Windows K 1.8.0.0 +Bundesamt fuer Sicherheit in der Informationstechnik +Kontakt: Chiasmus@bsi.bund.de +(C) 2001 - 2005 +[ ...] + + + + +Version 1.8.0.0 was tested succesfully. Other versions may or may not +work correctly. + + + +As a last preparation step a directory which will hold the key files +ending in .xis has to be created, if none already exists. Usually +~/.chiasmus/ is used. + + + + + + Configuration + +Select SettingsConfigure KMail + and choose the Crypto-Backends +tab on the Security page. Select the entry for Chiasmus and click +Configure.... to open the configuration dialog +for the Chiasmus backend. + + + +Specify the path to the Chiasmus binary and the directory where key files are +to be stored, using the file selection dialogs which open when you click the +folder icons next to the path entries. + + + +Close the backend configuration dialog by clicking Ok +and check the Chiasmus Checkbox to activate it. Then also close the main +configuration dialog. + + + + + + + Encryption + +To encrypt a message with Chiasmus, select +OptionsEncrypt message with chiasmus +. A dialog comes up in which you can select +the key file to use, as well as additional command line arguments to +be passed to chiasmus. + + + + +If you want to use Chiasmus encryption regularly, you should add +an icon for that option to your toolbar, using +SettingsConfigure toolbars +. Move the entry for Encrypt with Chiasmus + from the left side of the dialog to the right, using the +buttons between the two panes. You can then quickly enable or disable +Chiasmus encryption using that toolbar button and have a visual +indication of the current state. + + + + + +If automatic saving of backup copies of currently edited messages is +enabled, you will be prompted for your Chiasmus password, if Chiamsus +encryption is active. + + + + +On message sending, you will be prompted for the password. If the password +is correct, the text of the message and all its attachments will be +encrypted. + + + + +The other signing and encryption options are completely independent of +the Chiasmus encryption. You can use those in addition to Chiasmus, +⪚ to sign the message. Additional encryption, on the other hand, +does not make much sense and complicates decryption for the +recipients. + + + + + + + Decryption + +&kmail; and &kontact; detect messages that have been encrypted +using Chiasmus and will automatically ask for the key file to use for +decryption, as well as the corresponding password. If the password is correct, the +message will be decrypted and displayed. + + + +To decrypt attachments that were encrypted using Chiasmus (&ie; those which have +a file name ending in .xia), right-click on the +attachment you want to decrypt and select Decrypt using Chiasmus +. After you have chosen a key file to be used for decryption and +entered the correct password for it, a file selection dialog will allow you +to specify where the decrypted attachment should be saved. + + + +
diff --git a/doc/kmail/using-kmail.docbook b/doc/kmail/using-kmail.docbook index 1d3b3e38..6bc1046f 100644 --- a/doc/kmail/using-kmail.docbook +++ b/doc/kmail/using-kmail.docbook @@ -375,6 +375,25 @@ checker. Note that you may first need to configure the spellchecker using + +Setting Up the Text Snippets Tool + +When editing in the composer window you can store often used parts of text as snippets. To configure the capabilities of the mail snippets part select Settings Snippets from the menubar. A new panel will appear on the left side of the composer. + +To add a new snippet to Snippet Panel, right click on panel, click on Add Snippet in the context menu. A snippet editor dialog will appear, in which new text can be added and the snippet can be given a name. Also a Shortcut can be associated with the snippet. +Snippets can be grouped together as well by creating groups and adding snippets to particular group. If you want to view the stored text in a tooltip window whenever you keep the mouse cursor over the title of that snippet. + +The Snippets tool allows for a variable text in predefined places any time you insert a snippet into a file. To accomplish this Snippets provides its own variables' mechanism. You can set up its behaviour in the snippet text itself by using separators ( $ ) that enclose the variable names. For example : $variablename$, $invoicenumber$, $weekno$. + + +The variable separator can be changed to some other character by changing "snippetDelimiter" in [SnippetPart] section. The Text Snippet configuration file can be found here $KDEHOME/share/config/kmailsnippetrc . + +snippetDelimiter=$ + + + + + @@ -503,7 +522,7 @@ events by using the Generate free/busy and activate alarms for In case you don't want to receive reminders for folders shared by someone else, -you can block them locally by activating the Block free/busy and alarms locally +you can block them locally by activating the Block alarms locally checkbox. @@ -1321,7 +1340,7 @@ which you do not like.
Forward To -This will forward the message inline (&ie; as if you selected MessageForwardInline...) to another email address. +This will forward the message inline (&ie; as if you selected MessageForwardInline...) to another email address. You can select the template to be used when forwarding with this filter with the drop down list. diff --git a/doc/kontact/index.docbook b/doc/kontact/index.docbook index 15a7c2c0..25b0a31f 100644 --- a/doc/kontact/index.docbook +++ b/doc/kontact/index.docbook @@ -948,10 +948,89 @@ documentation. &kontact; Profiles -&kontact;'s profile support makes it possible to load and save user settings in profiles. -The settings stored in the profile include typical Look&Feel related options such as app-specific color schemes, icon sets, toolbar layout and application defaults. Personal information, e.g. accounts and identities are not stored in profiles. +A profile is an arbitrary set of configuration values for any of the kontact components (mail, calendar, etc.) accompanied by a configuration file for the profile itself, which specifies its name, description etc. An example profile configuration file looks like this: + + + + + +[Kontact Profile] +Description=Default KDE Kontact settings +Identifier=KontactDefaults +Name=Kontact Style + + + +The format is the standard .ini file format used for configuration throughout KDE, including in kiosk. + + + + +&kontact;'s profile support makes it possible to load and save user settings in profiles. +The settings stored in the profile include typical Look&Feel related options such as app-specific color schemes, icon sets, toolbar layout and application defaults. Personal information, e.g. accounts and identities are not stored in profiles. + + + Two default profiles are provided by Kontact: "&kontact; Style", which contains the default &kontact; settings, and "Outlook Style", adapting &kontact; to Microsoft Outlook Look&Feel. -The user can adapt existing profiles, create new profiles from his current settings, and import and export profiles. To manage profiles, choose "Configure Profiles" from the "Settings" menu. +The user can adapt existing profiles, create new profiles from his current settings, and import and export profiles. + + + +The dialog opened through Settings -> "Configure Profiles" allows profiles to be imported, exported, created, deleted and saved. They can also be loaded (applied) from there. Saving applies the currently active settings throughout Kontact to the selected profile, while import and export allow existing profiles to be written to or read from directories. To change a setting, one can edit the kontactrc file using a text editor and save it. + + + +Apart from the configuration file, a profile directory can contain configuration skeleton files for Kontact as a whole (kontactrc) or for any of the components (korganizerrc, kmailrc, etc.). These files can in turn contain any configuration values that the profile should set (overriding the user's current configuration) when the profile is loaded. + + + + The korganizer configuration for the "Kontact Defaults" profile for example sets the calendar view to be merged, by default : + + + [Views] + Agenda View Calendar Display=CalendarsMerged + + + + + + + The Outlook-like profile, on the other hand, sets it to be side by side, like in Outlook: + + + [Views] + Agenda View Calendar Display=CalendarsSideBySide + + + + + + + If a profile wants to reset or remove a configuration key, it can specify that key like so: + + + [General] + activeBackground=KONTACT_PROFILE_DELETE_KEY + + + + + + + This example resets the background color to whatever the KDE style suggests. The Outlook compatability profile sets this key to the Windows/Outlook color style: + + + [General] + activeBackground=47,103,255 + + + + + + + + +Currently Kontact ships with two (incomplete) profiles, one which (re-)configures things to look and feel the way Kontact natively does, while the other one attempts to approach the look&feel of Outlook on Windows. To this end they change a variety of things, concretely the color palette used, the icon theme used, how the calendar view is layed out, whether the navigation toolbar is visible how the splitter sizes are initialized, in the main kontact interface. diff --git a/doc/korganizer/index.docbook b/doc/korganizer/index.docbook index a0398ed2..83b207e1 100644 --- a/doc/korganizer/index.docbook +++ b/doc/korganizer/index.docbook @@ -4,8 +4,6 @@ - - @@ -123,7 +121,7 @@ help you keep your schedule. alarm appointment event -jornal +journal to-do @@ -153,7 +151,7 @@ information and the freedom to choose the best solutions. &kontact;, offering you an integrated solution for your communication and information management needs: email, notes, contacts management, news reader, synchronization with portable devices, and news feeds -reader. Even if you don't use it inside &kontact;, &korganizer; is +reader. Even if you do not use it inside &kontact;, &korganizer; is integrated with the other &kde; PIM applications. For instance, you can configure it to show birthdays from &kaddressbook; in your agenda, use &kmail; to send and receive invitations, &etc; @@ -350,7 +348,7 @@ appear instantly, allowing you to set the new due date. This five-minute course covered only the basic operation of &korganizer;. Now you should go on and read the rest of this manual to unleash the full -power of &korganizer;'s capabilities. But if you don't feel like it, you +power of &korganizer;'s capabilities. But if you do not feel like it, you should at least skip through the &FAQ;. @@ -405,7 +403,7 @@ want to use another resource, especially if you use a supported groupware server. Please ask the server administrator for the information required to configure the groupware resource, including free/busy information publishing and retrieving. Access to free/busy information allows an event organizer to -take the attendee's calendar in consideration when adding him to the the event's +take the attendee's calendar in consideration when adding him to the event's attendee list. Besides calendar storage, groupware servers typically offer contacts, @@ -514,7 +512,7 @@ storing your calendar information under If you have access to a server that supports the GroupDav protocol, add this -resource in order to be able to to save (and load) events and to-dos +resource in order to be able to save (and load) events and to-dos to the server. To add the resource, you will need to know the server &URL;, your user name and your password. The GroupDav protocol supports the storage of contacts, so you may want to add and configure the &kaddressbook; @@ -532,7 +530,7 @@ date list can be obtained at the If you have access to a Novell GroupWise Server -(version 6.5 or later), add this resource in order to be able to to save (and load) +(version 6.5 or later), add this resource in order to be able to save (and load) events, free/busy information and to-dos to the server. To add the resource, you will need to know the server &URL;, your user name and your password. There is support for storage of contacts, so you may want to configure &kaddressbook; @@ -552,7 +550,7 @@ GroupWise resources, but &kmail;, &kaddressbook; too. Calendar on IMAP Server via &kmail; If you have access to a server that shares calendar data via IMAP, add this -resource in order to be able to to save (and load) events, to-dos, free/busy +resource in order to be able to save (and load) events, to-dos, free/busy information and journal entries to the IMAP server. To enable IMAP access, you will need to configure &kmail; first, then add the &korganizer; resource. Also, since you are using &kmail; to contact the server, &korganizer; will @@ -617,7 +615,7 @@ corruption, you will lose only one calendar item, not the whole calendar. If you have access to a Exchange 2000 Server, -add this resource in order to be able to to save (and load) +add this resource in order to be able to save (and load) events FIXME:,journals, free/busy information and to-dos? to the server. To add the resource, you will need to know the server &URL;, FIXME:port? your user name and your password. There is support for (read only) contacts, so you may want to configure &kaddressbook; @@ -690,7 +688,7 @@ the next start, if you set the Automatic Save to to a regular interval). If you plan to use a calendar resource in writable mode, make sure that your connection is stable, configure the resource to save the file on each change (or at frequent intervals), -and don't reload the file at regular intervals. +and do not reload the file at regular intervals. A related, but opposite problem, is that two users cannot safely edit the same remote file at the same time, because the remote file resource does not @@ -706,13 +704,13 @@ the file, his changes will be lost. SUSE &Linux; OpenExchange Server If you have access to a -SUSE &Linux; OpenExchange -Server, version 4.1, add this resource in order to be able to to save +SUSE &Linux; OpenExchange +Server, version 4.1, add this resource in order to be able to save (and load) events, free/busy information and to-dos to the server. To add the resource, you will need to know the server &URL;, your user name and your password. There is support for storage of contacts, so you may want to configure &kaddressbook; resource. -The most practical way to configure the access to a GroupWise server is +The most practical way to configure the access to an OpenExchange server is to use the sloxwizard wizard. You can start it from the command line prompt: @@ -730,7 +728,7 @@ OpenExchange resources, but &kmail;, &kaddressbook; too. If you have access to a eGroupware -Server, version 1.0, add this resource in order to be able to to save +Server, version 1.0, add this resource in order to be able to save (and load) events, free/busy information and to-dos to the server. To add the resource, you will need to know the server &URL;, your user name and your password. There is support for storage of contacts, so you may want to configure @@ -827,8 +825,8 @@ calendar information with your colleagues and friends. -Disable all the resources you don't want to export, and filter out -the items you don't want to export. +Disable all the resources you do not want to export, and filter out +the items you do not want to export. Choose the File ExportExport Web Page... menu item. You will get a window with three tabs: @@ -1062,7 +1060,7 @@ a chasing-arrows icon is displayed if it repeats multiple times. Hovering the mouse over an calendar item will bring up a tooltip with the item detailed information. To make &korganizer; show a red line marking the current-time on the current day (the Marcus Bains line), -check the Show current-time (Marcus Bains Line) box in the +check the Show current-time (Marcus Bains) line box in the &korganizer; view configuration. @@ -1110,7 +1108,7 @@ will be able to quickly identify the type of event by its color. -The aganda view can display events from all your calendars merged into one view or +The agenda view can display events from all your calendars merged into one view or show a view per calendar. Having both views available via tabs is also possible and can be customized in the preferences dialog. @@ -1337,7 +1335,7 @@ the agenda view or in the month view (depending on the Filters To help you view, find and export your data, you can create and -use filters for your calendars. For instance, if you don't want to view +use filters for your calendars. For instance, if you do not want to view completed to-dos, you can filter them out, instead of purging them. If you assign categories, you can use them to create filters. @@ -1354,11 +1352,11 @@ and edit filters that will affect which items will be displayed by The View Filter submenu and the filter toolbar drop down offer access to all available filters created using -the Edit +the Edit Calendar Filters dialog. To toggle the display of the filters toolbar on and off, choose the Settings ToolbarsFilter toolbar. -If you don't want to use any filter, choose No filter. +If you do not want to use any filter, choose No filter. Filters are useful when exporting your calendar. If you use a filter, only the filtered (visible) items will @@ -1381,8 +1379,8 @@ entries) according to title, description, and/or categories. The find action will only search the specified fields. You can use wildcards if you do not remember the exact summary. If -you don't know single character of the summary, put ? -instead of the missing character. If you don't know more characters, use +you do not know single character of the summary, put ? +instead of the missing character. If you do not know more characters, use *. For instance if you know that the event or to-do has meeting in the beginning and product after, you would write meeting*product in the Find dialog. @@ -1449,7 +1447,7 @@ Enter your data as described below. This is a thorough description of the individual event window fields and widgets. Not all fields have to be filled in; some can be left empty. Read the -Required fields section +Required fields section for detailed information. And if you prefer to learn by example, there's one in the Entering event section. @@ -1511,7 +1509,7 @@ program; if you do not, the program will not run.--> reminded, or click the Advanced button to open the Edit Reminders dialog. Using this dialog, you can set repeating intervals for your reminders, and create special reminders that play -sounds, run programs, or send emails. +sounds or run programs. Show Time As: The duration of the event may be shown as Busy or Free in your schedule. Choose it from the Show Time As -menu. +drop down box. @@ -1540,7 +1538,7 @@ you. Select Categories You can assign several categories to a calendar item. Click the -Select Categories button to open the +Select button to open the Select Categories dialog. Check the category boxes to assign suitable categories to the event. You can also add a new category, modify a category or delete a category by pressing the button @@ -1556,8 +1554,8 @@ either Personal or Special Occasion - it is your choice. Access: Choose Private or Confidential -to keep the event private or confidential. Currently, this choice correctly -sets the CLASS attribute of the events to +to keep the event private or confidential. Currently, this choice correctly +sets the CLASS attribute of the events to PUBLIC, PRIVATE or CONFIDENTIAL. However, if these settings are really used to restrict the access of the information depends on the client and / or groupware server implementation. @@ -1572,7 +1570,7 @@ only you can access. -When you want to confirm, call off or revert the entered data, choose +When you want to confirm, apply or call off the entered data, choose among the Action Buttons, OK, Apply and Cancel. @@ -1649,7 +1647,7 @@ on. This is similar to Monthly. You need to choose whether or not you are indicating a calendar day of the year (⪚, the 250th day of the year) - Recur on day # of the -year, in in a specific week on a specific day of the week of a month +year, in a specific week on a specific day of the week of a month (⪚, the 2nd Tuesday of March), or the day of the month in a particular month of the year - Recur on day # of the month. By default, &korganizer; @@ -1775,7 +1773,7 @@ is displayed in the details list window gives you quick overview of what should be done to make the event successful. It can be used for sorting (see above). If you use groupware schedule and request responses from your attendees, as you receive the responses the status of the attendee will be -updated. If you don't request responses, you will have to update the status +updated. If you do not request responses, you will have to update the status manually. @@ -1819,7 +1817,7 @@ sending the invitations. represents one of the attendees, listed on the left. The marked areas on the chart represent the time already taken by other events, unmarked areas represent time free from other events. You can move the event to a different point in time -by dragging it with the mouse, or resize it, by moving the edges of the highlighted +by dragging it with the mouse, or resize it by moving the edges of the highlighted area with the mouse. The free/busy information is only available if the @@ -1827,7 +1825,7 @@ attendee publishes his free/busy schedule, and if &korganizer; is correctly configured to retrieve it. For more information about configuring &korganizer; to publish and retrieve free/busy information, please check the . Double-clicking on an attendee entry in the list -will allow you to enter the location of their free/busy information. +will allow you to enter the location of their free/busy information. @@ -1837,7 +1835,7 @@ will allow you to enter the location of their free/busy information. Sets the zoom level on the schedule chart. Hour shows a range of several hours, Day shows a range of a few days, Week shows a range of a few months, and -Month shows a range of a few years, while +Month shows a range of a few months, while Automatic selects the range most appropriate for the current event or to-do. @@ -1989,7 +1987,7 @@ where the to-do will take place. Date & Time The Due and Start boxes are not initially checked, as to-dos usually do not have a start and due -date. Check one of both boxes and +date. Check one or both boxes and change these dates as desired. Enter the dates directly or use the Calendar Widget. Choose time from quarter hour intervals, or enter the desired time directly into @@ -2013,7 +2011,7 @@ in steps of 10%. Priority Assign a priority to your to-do. This drop down menu offers -priorities from one to five, one being the highest. Initially to-dos are set +priorities from one to nine, one being the highest. Initially to-dos are set to priority five (medium). @@ -2029,7 +2027,7 @@ program; if you do not, the program will not run.--> reminded, or click the Advanced button to open the Edit Reminders dialog. Using this dialog, you can set repeating intervals for your reminders, and create special reminders that play -sounds, run programs, or send emails. +sounds or run programs. ; other browsers do. - if (*p=='>') - { - p++; - } - else - { - while (p!=pEnd) - { - if (*p=='-') - { - // This is the real end of comment, "-->". - if (p[1]=='-' && p[2]=='>') - { - p += 3; - break; - } - // This is the incorrect end of comment that other browsers allow, "--!>". - if (p[1] == '-' && p[2] == '!' && p[3] == '>') - { - p += 4; - break; - } - } - p++; - } - } - ptr=p; -} - -// Returns the position of the encoding string. -static int findXMLEncoding(const TQCString &str, int &encodingLength) -{ - int len = str.length(); - int pos = str.find("encoding"); - if (pos == -1) - return -1; - pos += 8; - - // Skip spaces and stray control characters. - while (pos=len || str[pos] != '=') - return -1; - ++pos; - - // Skip spaces and stray control characters. - while (pos= len) - return -1; - - // Skip quotation mark. - char quoteMark = str[pos]; - if (quoteMark != '"' && quoteMark != '\'') - return -1; - ++pos; - - // Find the trailing quotation mark. - int end=pos; - while (end=len) - return -1; - - encodingLength = end-pos; - return pos; -} - - bool EncodingDetector::errorsIfUtf8 (const char* data, int length) { if (d->m_codec->mibEnum()!=MibUtf8) @@ -905,6 +824,7 @@ EncodingDetector::EncodingChoiceSource EncodingDetector::encodingChoiceSource() const char* EncodingDetector::encoding() const { d->m_storeDecoderName = d->m_codec->name(); + d->m_storeDecoderName = d->m_storeDecoderName.lower().replace( "iso ", "iso-" ); return d->m_storeDecoderName.data(); } diff --git a/kmail/eventsrc b/kmail/eventsrc index 76720efe..6f4c38de 100644 --- a/kmail/eventsrc +++ b/kmail/eventsrc @@ -34,7 +34,6 @@ Name[hu]=Új levél érkezett Name[is]=Nýr póstur Name[it]=Nuova posta arrivata Name[ja]=新規メール着信 -Name[ka]=მიღებულია ახალი ფოსტა Name[kk]=Жаңа пошта келді Name[km]=មាន​អ៊ីមែល​ថ្មី​មក​ដល់ Name[lt]=Atėjo naujas paštas @@ -60,8 +59,7 @@ Name[ta]=புதிய அஞ்சல் வந்துள்ளது Name[tg]=Почтаи нав қабул шуд Name[tr]=Yeni E-posta Geldi Name[uk]=Отримана нова пошта -Name[uz]=Yangi xat keldi -Name[uz@cyrillic]=Янги хат келди +Name[uz]=Янги хат келди Name[zh_CN]=新邮件到达 Name[zh_TW]=您有新郵件 Comment=New mail arrived @@ -90,7 +88,6 @@ Comment[hu]=Új levél érkezett Comment[is]=Nýr póstur Comment[it]=Nuova posta Comment[ja]=新規メール着信 -Comment[ka]=მიღებულია ახალი ფოსტა Comment[kk]=Жаңа пошта келді Comment[km]=មាន​អ៊ីមែល​ថ្មី​មក​ដល់ Comment[lt]=Atėjo naujas paštas @@ -116,8 +113,7 @@ Comment[ta]=புதிய அஞ்சல் வந்துள்ளது Comment[tg]=Почтаи нав қабул шуд Comment[tr]=Yeni e-posta geldi Comment[uk]=Надійшла нова пошта -Comment[uz]=Yangi xat keldi -Comment[uz@cyrillic]=Янги хат келди +Comment[uz]=Янги хат келди Comment[zh_CN]=新邮件到达 Comment[zh_TW]=您有新郵件 default_sound= diff --git a/kmail/expirypropertiesdialog.cpp b/kmail/expirypropertiesdialog.cpp index a313cc88..c524f8b3 100644 --- a/kmail/expirypropertiesdialog.cpp +++ b/kmail/expirypropertiesdialog.cpp @@ -28,8 +28,8 @@ using namespace KMail; * */ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* folder ) - : KDialogBase( tree, "expiry_properties", false, i18n( "Mail Expiry Properties" ), - KDialogBase::Ok|KDialogBase::Cancel, + : KDialogBase( tree, "expiry_properties", false, i18n( "Mail Expiry Properties" ), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ), mFolder( folder ) { @@ -37,10 +37,10 @@ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* fo TQWidget* privateLayoutWidget = new TQWidget( this, "globalVBox" ); setMainWidget( privateLayoutWidget ); privateLayoutWidget->setGeometry( TQRect( 10, 20, 270, 138 ) ); - globalVBox = new TQVBoxLayout( privateLayoutWidget, 11, 6, "globalVBox"); + globalVBox = new TQVBoxLayout( privateLayoutWidget, 11, 6, "globalVBox"); globalVBox->setSpacing( 20 ); - readHBox = new TQHBoxLayout( 0, 0, 6, "readHBox"); + readHBox = new TQHBoxLayout( 0, 0, 6, "readHBox"); expireReadMailCB = new TQCheckBox( privateLayoutWidget, "expireReadMailCB" ); expireReadMailCB->setText( i18n( "Expire read mails after" ) ); @@ -58,7 +58,7 @@ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* fo readHBox->addWidget( labelDays ); globalVBox->addLayout( readHBox ); - unreadHBox = new TQHBoxLayout( 0, 0, 6, "unreadHBox"); + unreadHBox = new TQHBoxLayout( 0, 0, 6, "unreadHBox"); expireUnreadMailCB = new TQCheckBox( privateLayoutWidget, "expireUnreadMailCB" ); expireUnreadMailCB->setText( i18n( "Expire unread mails after" ) ); @@ -77,18 +77,18 @@ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* fo unreadHBox->addWidget( labelDays2 ); globalVBox->addLayout( unreadHBox ); - expiryActionHBox = new TQHBoxLayout( 0, 0, 6, "expiryActionHBox"); + expiryActionHBox = new TQHBoxLayout( 0, 0, 6, "expiryActionHBox"); expiryActionLabel = new TQLabel( privateLayoutWidget, "expiryActionLabel" ); expiryActionLabel->setText( i18n( "Expiry action:" ) ); expiryActionLabel->setAlignment( int( TQLabel::AlignVCenter ) ); expiryActionHBox->addWidget( expiryActionLabel ); - actionsHBox = new TQVBoxLayout( 0, 0, 6, "actionsHBox"); + actionsHBox = new TQVBoxLayout( 0, 0, 6, "actionsHBox"); actionsGroup = new TQButtonGroup( this ); actionsGroup->hide(); // for mutual exclusion of the radio buttons - moveToHBox = new TQHBoxLayout( 0, 0, 6, "moveToHBox"); + moveToHBox = new TQHBoxLayout( 0, 0, 6, "moveToHBox"); moveToRB = new TQRadioButton( privateLayoutWidget, "moveToRB" ); actionsGroup->insert( moveToRB ); @@ -119,14 +119,14 @@ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* fo int daysToExpireRead, daysToExpireUnread; mFolder->daysToExpire( daysToExpireUnread, daysToExpireRead); - if ( expiryGloballyOn - && mFolder->getReadExpireUnits() != expireNever + if ( expiryGloballyOn + && mFolder->getReadExpireUnits() != expireNever && daysToExpireRead >= 0 ) { expireReadMailCB->setChecked( true ); expireReadMailSB->setValue( daysToExpireRead ); } if ( expiryGloballyOn - && mFolder->getUnreadExpireUnits() != expireNever + && mFolder->getUnreadExpireUnits() != expireNever && daysToExpireUnread >= 0 ) { expireUnreadMailCB->setChecked( true ); expireUnreadMailSB->setValue( daysToExpireUnread ); @@ -159,11 +159,29 @@ ExpiryPropertiesDialog::~ExpiryPropertiesDialog() void ExpiryPropertiesDialog::slotOk() { bool enableGlobally = expireReadMailCB->isChecked() || expireUnreadMailCB->isChecked(); - if ( enableGlobally && moveToRB->isChecked() && !folderSelector->folder() ) { - KMessageBox::error( this, i18n("Please select a folder to expire messages into."), - i18n( "No Folder Selected" ) ); + + KMFolder *expireToFolder = folderSelector->folder(); + if ( enableGlobally && moveToRB->isChecked() && !expireToFolder ) { + KMessageBox::error( + this, + i18n( "Please select a folder to expire messages into." ), + i18n( "No Folder Selected" ) ); return; - } + } + + if ( expireToFolder ) { + if ( expireToFolder->idString() == mFolder->idString() ) { + KMessageBox::error( + this, + i18n( "Please select a different folder than the current folder " + "to expire message into." ), + i18n( "Wrong Folder Selected" ) ); + return; + } else { + mFolder->setExpireToFolderId( expireToFolder->idString() ); + } + } + mFolder->setAutoExpire( enableGlobally ); // we always write out days now mFolder->setReadExpireAge( expireReadMailSB->value() ); @@ -175,9 +193,6 @@ void ExpiryPropertiesDialog::slotOk() mFolder->setExpireAction( KMFolder::ExpireDelete ); else mFolder->setExpireAction( KMFolder::ExpireMove ); - KMFolder* expireToFolder = folderSelector->folder(); - if ( expireToFolder ) - mFolder->setExpireToFolderId( expireToFolder->idString() ); // trigger immediate expiry if there is something to do if ( enableGlobally ) diff --git a/kmail/favoritefolderview.cpp b/kmail/favoritefolderview.cpp index 35b2e3ee..5d8e3d13 100644 --- a/kmail/favoritefolderview.cpp +++ b/kmail/favoritefolderview.cpp @@ -309,7 +309,8 @@ void FavoriteFolderView::dropped(TQDropEvent * e, TQListViewItem * after) KMFolderTreeItem *fti = static_cast( it.current() ); if ( !fti->folder() ) continue; - afterItem = addFolder( fti->folder(), prettyName( fti ), afterItem ); + if( !mFolderToItem.contains( fti->folder() ) ) + afterItem = addFolder( fti->folder(), prettyName( fti ), afterItem ); } e->accept(); } @@ -323,20 +324,25 @@ void FavoriteFolderView::contextMenu(TQListViewItem * item, const TQPoint & poin mContextMenuItem = fti; KPopupMenu contextMenu; if ( fti && fti->folder() ) { - contextMenu.insertItem( SmallIconSet("editdelete"), i18n("Remove From Favorites"), - this, TQT_SLOT(removeFolder()) ); - contextMenu.insertItem( SmallIconSet("edit"), i18n("Rename Favorite"), this, TQT_SLOT(renameFolder()) ); - contextMenu.insertSeparator(); - mainWidget()->action("mark_all_as_read")->plug( &contextMenu ); if ( fti->folder()->folderType() == KMFolderTypeImap || fti->folder()->folderType() == KMFolderTypeCachedImap ) mainWidget()->action("refresh_folder")->plug( &contextMenu ); if ( fti->folder()->isMailingListEnabled() ) mainWidget()->action("post_message")->plug( &contextMenu ); + mainWidget()->action("search_messages")->plug( &contextMenu ); + if ( fti->folder()->canDeleteMessages() && ( fti->folder()->count() > 0 ) ) + mainWidget()->action("empty")->plug( &contextMenu ); + contextMenu.insertSeparator(); contextMenu.insertItem( SmallIconSet("configure_shortcuts"), i18n("&Assign Shortcut..."), fti, TQT_SLOT(assignShortcut()) ); contextMenu.insertItem( i18n("Expire..."), fti, TQT_SLOT(slotShowExpiryProperties()) ); mainWidget()->action("modify")->plug( &contextMenu ); + contextMenu.insertSeparator(); + + contextMenu.insertItem( SmallIconSet("editdelete"), i18n("Remove From Favorites"), + this, TQT_SLOT(removeFolder()) ); + contextMenu.insertItem( SmallIconSet("edit"), i18n("Rename Favorite"), this, TQT_SLOT(renameFolder()) ); + } else { contextMenu.insertItem( SmallIconSet("bookmark_add"), i18n("Add Favorite Folder..."), this, TQT_SLOT(addFolder()) ); @@ -346,8 +352,13 @@ void FavoriteFolderView::contextMenu(TQListViewItem * item, const TQPoint & poin void FavoriteFolderView::removeFolder() { + KMFolderTreeItem *fti = mContextMenuItem; + KMFolder *folder = 0; + if( fti ) + folder = fti->folder(); delete mContextMenuItem; mContextMenuItem = 0; + removeFromFolderToItemMap(folder); notifyInstancesOnChange(); } @@ -446,6 +457,9 @@ void FavoriteFolderView::addFolder() KMFolder *folder = dlg.folder(); if ( !folder ) return; + if ( mFolderToItem.contains( folder ) ) + return; + KMFolderTreeItem *fti = findFolderTreeItem( folder ); addFolder( folder, fti ? prettyName( fti ) : folder->label() ); } @@ -454,7 +468,8 @@ void KMail::FavoriteFolderView::addFolder(KMFolderTreeItem * fti) { if ( !fti || !fti->folder() ) return; - addFolder( fti->folder(), prettyName( fti ) ); + if ( !mFolderToItem.contains( fti->folder() ) ) + addFolder( fti->folder(), prettyName( fti ) ); } KMFolderTreeItem * FavoriteFolderView::findFolderTreeItem(KMFolder * folder) const @@ -485,7 +500,7 @@ void FavoriteFolderView::checkMail() imap->getAndCheckFolder(); } else if ( fti->folder()->folderType() == KMFolderTypeCachedImap ) { KMFolderCachedImap* f = static_cast( fti->folder()->storage() ); - f->account()->processNewMailSingleFolder( fti->folder() ); + f->account()->processNewMailInFolder( fti->folder() ); } } } diff --git a/kmail/filterimporterexporter.cpp b/kmail/filterimporterexporter.cpp index bfe40170..c5780431 100644 --- a/kmail/filterimporterexporter.cpp +++ b/kmail/filterimporterexporter.cpp @@ -38,74 +38,105 @@ #include #include #include +#include #include +#include using namespace KMail; -class FilterSelectionDialog : public KDialogBase +FilterSelectionDialog::FilterSelectionDialog( TQWidget * parent ) + :KDialogBase( parent, "filterselection", true, i18n("Select Filters"), Ok|Cancel, Ok, true ), + wasCancelled( false ) { -public: - FilterSelectionDialog( TQWidget * parent = 0 ) - :KDialogBase( parent, "filterselection", true, i18n("Select Filters"), Ok|Cancel, Ok, true ), - wasCancelled( false ) - { - filtersListView = new KListView( this ); - setMainWidget(filtersListView); - filtersListView->setSorting( -1 ); - filtersListView->setSelectionMode( TQListView::NoSelection ); - filtersListView->addColumn( i18n("Filters"), 300 ); - filtersListView->setFullWidth( true ); - resize( 300, 350 ); - } + TQWidget *w = new TQWidget( this ); + TQVBoxLayout *top = new TQVBoxLayout( w ); + + filtersListView = new KListView( w ); + top->addWidget( filtersListView ); + setMainWidget(w); + filtersListView->setSorting( -1 ); + filtersListView->setSelectionMode( TQListView::NoSelection ); + filtersListView->addColumn( i18n("Filters"), 300 ); + filtersListView->setFullWidth( true ); + TQHBoxLayout *buttonLayout = new TQHBoxLayout( this ); + top->addLayout( buttonLayout ); + selectAllButton = new KPushButton( i18n( "Select All" ), w ); + buttonLayout->addWidget( selectAllButton ); + unselectAllButton = new KPushButton( i18n( "Unselect All" ), w ); + buttonLayout->addWidget( unselectAllButton ); + connect( selectAllButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotSelectAllButton() ) ); + connect( unselectAllButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotUnselectAllButton() ) ); + resize( 300, 350 ); +} - virtual ~FilterSelectionDialog() - { - } - - virtual void slotCancel() - { - wasCancelled = true; - KDialogBase::slotCancel(); - } - - bool cancelled() - { - return wasCancelled; - } +FilterSelectionDialog::~FilterSelectionDialog() +{ +} - void setFilters( const TQValueList& filters ) - { - originalFilters = filters; - filtersListView->clear(); - TQValueListConstIterator it = filters.constEnd(); - while ( it != filters.constBegin() ) { - --it; - KMFilter* filter = *it; - TQCheckListItem* item = new TQCheckListItem( filtersListView, filter->name(), TQCheckListItem::CheckBox ); - item->setOn( true ); - } - } - - TQValueList selectedFilters() const - { - TQValueList filters; - TQListViewItemIterator it( filtersListView ); - int i = 0; - while( it.current() ) { - TQCheckListItem* item = static_cast( it.current() ); - if ( item->isOn() ) - filters << originalFilters[i]; - ++i; ++it; - } - return filters; - } -private: - KListView *filtersListView; - TQValueList originalFilters; - bool wasCancelled; -}; +void FilterSelectionDialog::slotCancel() +{ + wasCancelled = true; + KDialogBase::slotCancel(); +} + +bool FilterSelectionDialog::cancelled() +{ + return wasCancelled; +} + +void FilterSelectionDialog::setFilters( const TQValueList& filters ) +{ + if ( filters.isEmpty() ) + { + enableButtonOK( false ); + return; + } + originalFilters = filters; + filtersListView->clear(); + TQValueListConstIterator it = filters.constEnd(); + while ( it != filters.constBegin() ) { + --it; + KMFilter* filter = *it; + TQCheckListItem* item = new TQCheckListItem( filtersListView, filter->name(), TQCheckListItem::CheckBox ); + item->setOn( true ); + } +} + +TQValueList FilterSelectionDialog::selectedFilters() const +{ + TQValueList filters; + TQListViewItemIterator it( filtersListView ); + int i = 0; + while( it.current() ) { + TQCheckListItem* item = static_cast( it.current() ); + if ( item->isOn() ) + filters << originalFilters[i]; + ++i; ++it; + } + return filters; +} + +void FilterSelectionDialog::slotUnselectAllButton() +{ + TQListViewItemIterator it( filtersListView ); + while( it.current() ) { + TQCheckListItem* item = static_cast( it.current() ); + item->setOn( false ); + ++it; + } +} + +void FilterSelectionDialog::slotSelectAllButton() +{ + TQListViewItemIterator it( filtersListView ); + while( it.current() ) { + TQCheckListItem* item = static_cast( it.current() ); + item->setOn( true ); + ++it; + } +} /* static */ TQValueList FilterImporterExporter::readFiltersFromConfig( KConfig* config, bool bPopFilter ) @@ -116,7 +147,7 @@ TQValueList FilterImporterExporter::readFiltersFromConfig( KConfig* c numFilters = config->readNumEntry("popfilters",0); else numFilters = config->readNumEntry("filters",0); - + TQValueList filters; for ( int i=0 ; i < numFilters ; ++i ) { TQString grpName; @@ -136,7 +167,7 @@ TQValueList FilterImporterExporter::readFiltersFromConfig( KConfig* c return filters; } -/* static */ +/* static */ void FilterImporterExporter::writeFiltersToConfig( const TQValueList& filters, KConfig* config, bool bPopFilter ) { // first, delete all groups: @@ -145,7 +176,7 @@ void FilterImporterExporter::writeFiltersToConfig( const TQValueList& for ( TQStringList::Iterator it = filterGroups.begin() ; it != filterGroups.end() ; ++it ) config->deleteGroup( *it ); - + int i = 0; for ( TQValueListConstIterator it = filters.constBegin() ; it != filters.constEnd() ; ++it ) { @@ -180,9 +211,9 @@ FilterImporterExporter::~FilterImporterExporter() TQValueList FilterImporterExporter::importFilters() { TQString fileName = KFileDialog::getOpenFileName( TQDir::homeDirPath(), TQString::null, mParent, i18n("Import Filters") ); - if ( fileName.isEmpty() ) + if ( fileName.isEmpty() ) return TQValueList(); // cancel - + { // scoping TQFile f( fileName ); if ( !f.open( IO_ReadOnly ) ) { @@ -190,7 +221,7 @@ TQValueList FilterImporterExporter::importFilters() return TQValueList(); } } - + KConfig config( fileName ); TQValueList imported = readFiltersFromConfig( &config, mPopFilter ); FilterSelectionDialog dlg( mParent ); @@ -202,10 +233,10 @@ TQValueList FilterImporterExporter::importFilters() void FilterImporterExporter::exportFilters(const TQValueList & filters ) { KURL saveUrl = KFileDialog::getSaveURL( TQDir::homeDirPath(), TQString::null, mParent, i18n("Export Filters") ); - + if ( saveUrl.isEmpty() || !Util::checkOverwrite( saveUrl, mParent ) ) return; - + KConfig config( saveUrl.path() ); FilterSelectionDialog dlg( mParent ); dlg.setFilters( filters ); @@ -214,3 +245,4 @@ void FilterImporterExporter::exportFilters(const TQValueList & filter writeFiltersToConfig( dlg.selectedFilters(), &config, mPopFilter ); } +#include "filterimporterexporter.moc" diff --git a/kmail/filterimporterexporter.h b/kmail/filterimporterexporter.h index 75f43d14..8b9a63bb 100644 --- a/kmail/filterimporterexporter.h +++ b/kmail/filterimporterexporter.h @@ -31,10 +31,11 @@ #define __FILTERIMPORTEREXPORTER_H__ #include - +#include class KMFilter; class KConfig; class TQWidget; +class KPushButton; namespace KMail { @@ -48,22 +49,44 @@ class FilterImporterExporter public: FilterImporterExporter( TQWidget* parent, bool popFilter = false ); virtual ~FilterImporterExporter(); - - /** Export the given filter rules to a file which + + /** Export the given filter rules to a file which * is asked from the user. The list to export is also * presented for confirmation/selection. */ void exportFilters( const TQValueList & ); - + /** Import filters. Ask the user where to import them from * and which filters to import. */ TQValueList importFilters(); - + static void writeFiltersToConfig( const TQValueList& filters, KConfig* config, bool bPopFilter ); static TQValueList readFiltersFromConfig( KConfig* config, bool bPopFilter ); private: TQWidget* mParent; bool mPopFilter; }; +class FilterSelectionDialog : public KDialogBase +{ + Q_OBJECT +public: + FilterSelectionDialog( TQWidget * parent = 0 ); + + virtual ~FilterSelectionDialog(); + virtual void slotCancel(); + bool cancelled(); + void setFilters( const TQValueList& filters ); + + TQValueList selectedFilters() const; +public slots: + void slotUnselectAllButton(); + void slotSelectAllButton(); +private: + KListView *filtersListView; + TQValueList originalFilters; + bool wasCancelled; + KPushButton *selectAllButton; + KPushButton *unselectAllButton; +}; } diff --git a/kmail/folderdiaacltab.cpp b/kmail/folderdiaacltab.cpp index d1f02a57..bebd9c31 100644 --- a/kmail/folderdiaacltab.cpp +++ b/kmail/folderdiaacltab.cpp @@ -30,7 +30,7 @@ * your version. */ -#include +#include // FOR KDEPIM_NEW_DISTRLISTS #include "folderdiaacltab.h" #include "acljobs.h" @@ -90,7 +90,7 @@ KMail::ACLEntryDialog::ACLEntryDialog( IMAPUserIdFormat userIdFormat, const TQSt { TQWidget *page = new TQWidget( this ); setMainWidget(page); - TQGridLayout *topLayout = new TQGridLayout( page, 3 /*rows*/, 3 /*cols*/, 0, spacingHint() ); + TQGridLayout *topLayout = new TQGridLayout( page, 4 /*rows*/, 3 /*cols*/, 0, spacingHint() ); TQLabel *label = new TQLabel( i18n( "&User identifier:" ), page ); topLayout->addWidget( label, 0, 0 ); @@ -100,7 +100,7 @@ KMail::ACLEntryDialog::ACLEntryDialog( IMAPUserIdFormat userIdFormat, const TQSt label->setBuddy( mUserIdLineEdit ); TQWhatsThis::add( mUserIdLineEdit, i18n( "The User Identifier is the login of the user on the IMAP server. This can be a simple user name or the full email address of the user; the login for your own account on the server will tell you which one it is." ) ); - TQPushButton* kabBtn = new TQPushButton( "...", page ); + TQPushButton* kabBtn = new TQPushButton( i18n( "Se&lect..." ), page ); topLayout->addWidget( kabBtn, 0, 2 ); mButtonGroup = new TQVButtonGroup( i18n( "Permissions" ), page ); @@ -115,6 +115,9 @@ KMail::ACLEntryDialog::ACLEntryDialog( IMAPUserIdFormat userIdFormat, const TQSt } topLayout->setRowStretch(2, 10); + TQLabel *noteLabel = new TQLabel( i18n( "Note: Renaming requires write permissions on the parent folder." ), page ); + topLayout->addMultiCellWidget( noteLabel, 2, 2, 0, 2 ); + connect( mUserIdLineEdit, TQT_SIGNAL( textChanged( const TQString& ) ), TQT_SLOT( slotChanged() ) ); connect( kabBtn, TQT_SIGNAL( clicked() ), TQT_SLOT( slotSelectAddresses() ) ); connect( mButtonGroup, TQT_SIGNAL( clicked( int ) ), TQT_SLOT( slotChanged() ) ); @@ -178,12 +181,7 @@ TQString KMail::ACLEntryDialog::userId() const TQStringList KMail::ACLEntryDialog::userIds() const { - TQStringList lst = TQStringList::split( ",", mUserIdLineEdit->text() ); - for( TQStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { - // Strip white space (in particular, due to ", ") - *it = (*it).stripWhiteSpace(); - } - return lst; + return KPIM::splitEmailAddrList( mUserIdLineEdit->text() ); } unsigned int KMail::ACLEntryDialog::permissions() const @@ -319,6 +317,7 @@ KMail::FolderDiaACLTab::FolderDiaACLTab( KMFolderDialog* dlg, TQWidget* parent, : FolderDiaTab( parent, name ), mImapAccount( 0 ), mUserRights( 0 ), + mUserRightsState( KMail::ACLJobs::NotFetchedYet ), mDlg( dlg ), mChanged( false ), mAccepting( false ), mSaving( false ) { @@ -344,7 +343,7 @@ KMail::FolderDiaACLTab::FolderDiaACLTab( KMFolderDialog* dlg, TQWidget* parent, TQT_SLOT(slotEditACL(TQListViewItem*)) ); connect( mListView, TQT_SIGNAL(returnPressed(TQListViewItem*)), TQT_SLOT(slotEditACL(TQListViewItem*)) ); - connect( mListView, TQT_SIGNAL(selectionChanged(TQListViewItem*)), + connect( mListView, TQT_SIGNAL(currentChanged(TQListViewItem*)), TQT_SLOT(slotSelectionChanged(TQListViewItem*)) ); TQVBox* buttonBox = new TQVBox( mACLWidget ); @@ -381,12 +380,14 @@ void KMail::FolderDiaACLTab::initializeWithValuesFromFolder( KMFolder* folder ) mImapPath = folderImap->imapPath(); mImapAccount = folderImap->account(); mUserRights = folderImap->userRights(); + mUserRightsState = folderImap->userRightsState(); } else if ( mFolderType == KMFolderTypeCachedImap ) { KMFolderCachedImap* folderImap = static_cast( folder->storage() ); mImapPath = folderImap->imapPath(); mImapAccount = folderImap->account(); mUserRights = folderImap->userRights(); + mUserRightsState = folderImap->userRightsState(); } else assert( 0 ); // see KMFolderDialog constructor @@ -422,13 +423,16 @@ void KMail::FolderDiaACLTab::load() if ( mFolderType == KMFolderTypeCachedImap ) { KMFolder* folder = mDlg->folder() ? mDlg->folder() : mDlg->parentFolder(); KMFolderCachedImap* folderImap = static_cast( folder->storage() ); - if ( mUserRights == -1 ) { // error - mLabel->setText( i18n( "Error retrieving user permissions." ) ); - } else if ( mUserRights == 0 /* can't happen anymore*/ || folderImap->aclList().isEmpty() ) { - /* We either synced, or we read user rights from the config, so we can - assume the server supports acls and an empty list means we haven't - synced yet. */ - mLabel->setText( i18n( "Information not retrieved from server yet, please use \"Check Mail\"." ) ); + if ( mUserRightsState == KMail::ACLJobs::FetchFailed || + folderImap->aclListState() == KMail::ACLJobs::FetchFailed ) { + TQString text = i18n( "Error retrieving user permissions." ); + if ( mUserRightsState == KMail::ACLJobs::Ok ) { + text += "\n" + i18n( "You might not have enough permissions to see the permissions of this folder." ); + } + mLabel->setText( text ); + } else if ( mUserRightsState == KMail::ACLJobs::NotFetchedYet || + folderImap->aclListState() == KMail::ACLJobs::NotFetchedYet ) { + mLabel->setText( i18n( "Information not retrieved from server, you need to use \"Check Mail\" and have administrative privileges on the folder.")); } else { loadFinished( folderImap->aclList() ); } @@ -474,7 +478,7 @@ void KMail::FolderDiaACLTab::slotConnectionResult( int errorCode, const TQString return; } - if ( mUserRights == 0 ) { + if ( mUserRightsState != KMail::ACLJobs::Ok ) { connect( mImapAccount, TQT_SIGNAL( receivedUserRights( KMFolder* ) ), this, TQT_SLOT( slotReceivedUserRights( KMFolder* ) ) ); KMFolder* folder = mDlg->folder() ? mDlg->folder() : mDlg->parentFolder(); @@ -494,6 +498,7 @@ void KMail::FolderDiaACLTab::slotReceivedUserRights( KMFolder* folder ) if ( folder == mDlg->folder() ? mDlg->folder() : mDlg->parentFolder() ) { KMFolderImap* folderImap = static_cast( folder->storage() ); mUserRights = folderImap->userRights(); + mUserRightsState = folderImap->userRightsState(); startListing(); } } diff --git a/kmail/folderdiaacltab.h b/kmail/folderdiaacltab.h index 87b296ee..6576d13e 100644 --- a/kmail/folderdiaacltab.h +++ b/kmail/folderdiaacltab.h @@ -33,6 +33,7 @@ #define FOLDERDIAACL_H #include "kmfolderdia.h" +#include "acljobs.h" #include "kmfoldertype.h" class KMFolderImap; @@ -137,6 +138,7 @@ private: TQString mImapPath; ImapAccountBase* mImapAccount; int mUserRights; + KMail::ACLJobs::ACLFetchState mUserRightsState; KMFolderType mFolderType; ACLList mInitialACLList; ACLList mACLList; // to be set diff --git a/kmail/foldersetselector.cpp b/kmail/foldersetselector.cpp new file mode 100644 index 00000000..c35fd0c1 --- /dev/null +++ b/kmail/foldersetselector.cpp @@ -0,0 +1,88 @@ +/* + Copyright (c) 2007 Volker Krause + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "foldersetselector.h" + +#include "globalsettings.h" +#include "kmfoldertree.h" +#include "simplefoldertree.h" +#include "kmfoldercachedimap.h" + +#include + +using namespace KMail; + +FolderSetSelector::FolderSetSelector( KMFolderTree *ft, TQWidget * parent ) + : KDialogBase( parent, "FolderSetSelector", true, TQString(), Ok|Cancel, Ok, true ) +{ + assert( ft ); + + mTreeView = new KMail::SimpleFolderTreeBase( makeVBoxMainWidget(), ft, + GlobalSettings::self()->lastSelectedFolder(), false ); + mTreeView->setFocus(); + + TQListViewItemIterator it( mTreeView ); + while ( it.current() ) { + SimpleFolderTreeItem *item = dynamic_cast*>( it.current() ); + ++it; + if ( !item ) + continue; + if ( !item->folder() ) { + item->setEnabled( false ); + continue; + } + if ( item->folder()->folderType() == KMFolderTypeCachedImap + && static_cast( item->folder()->storage() )->imapPath() == "/INBOX/" ) { + item->setOn( true ); + } + if ( item->folder()->folderType() != KMFolderTypeCachedImap ) { + item->setEnabled( false ); + } + } + +} + +TQValueList< int > FolderSetSelector::selectedFolders() +{ + TQValueList rv; + TQListViewItemIterator it( mTreeView ); + while ( it.current() ) { + SimpleFolderTreeItem *item = dynamic_cast*>( it.current() ); + if ( item && item->isOn() && item->folder() ) + rv.append( item->folder()->id() ); + ++it; + } + return rv; +} + +void FolderSetSelector::setSelectedFolders(const TQValueList< int > & folderIds) +{ + TQListViewItemIterator it( mTreeView ); + while ( it.current() ) { + SimpleFolderTreeItem *item = dynamic_cast*>( it.current() ); + if ( item && item->folder() ) { + if ( folderIds.contains( item->folder()->id() ) ) + item->setOn( true ); + else + item->setOn( false ); + } + ++it; + } +} + +#include "foldersetselector.moc" diff --git a/kmail/foldersetselector.h b/kmail/foldersetselector.h new file mode 100644 index 00000000..5d51db31 --- /dev/null +++ b/kmail/foldersetselector.h @@ -0,0 +1,45 @@ +/* + Copyright (c) 2007 Volker Krause + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef KMAIL_FOLDERSETSELECTOR_H +#define KMAIL_FOLDERSETSELECTOR_H + +#include + +class KListView; +class KMFolderTree; + +namespace KMail { + +class FolderSetSelector : public KDialogBase +{ + Q_OBJECT + + public: + FolderSetSelector( KMFolderTree *ft, TQWidget *parent = 0 ); + + TQValueList selectedFolders(); + void setSelectedFolders( const TQValueList &folderIds ); + + private: + KListView *mTreeView; +}; + +} + +#endif diff --git a/kmail/folderstorage.cpp b/kmail/folderstorage.cpp index c1d447a6..78724d50 100644 --- a/kmail/folderstorage.cpp +++ b/kmail/folderstorage.cpp @@ -85,8 +85,8 @@ FolderStorage::FolderStorage( KMFolder* folder, const char* aName ) mHasChildren = HasNoChildren; mContentsType = KMail::ContentsTypeMail; - - connect(this, TQT_SIGNAL(closed(KMFolder*)), mFolder, TQT_SIGNAL(closed())); + + connect(this, TQT_SIGNAL(closed(KMFolder*)), mFolder, TQT_SIGNAL(closed())); } //----------------------------------------------------------------------------- @@ -477,11 +477,11 @@ void FolderStorage::take(TQPtrList msgList) KMMessage* FolderStorage::getMsg(int idx) { if ( mOpenCount <= 0 ) { - kdWarning(5006) << "FolderStorage::getMsg was called on a closed folder: " << folder()->prettyURL() << endl; + kdWarning(5006) << "FolderStorage::getMsg was called on a closed folder: " << folder()->prettyURL() << endl; return 0; } - if ( idx < 0 || idx >= count() ) { - kdWarning(5006) << "FolderStorage::getMsg was asked for an invalid index. idx =" << idx << " count()=" << count() << endl; + if ( idx < 0 || idx >= count() ) { + kdWarning(5006) << "FolderStorage::getMsg was asked for an invalid index. idx =" << idx << " count()=" << count() << endl; return 0; } @@ -502,6 +502,10 @@ KMMessage* FolderStorage::getMsg(int idx) if (mCompactable && (!msg || (msg->subject().isEmpty() != mbSubject.isEmpty()))) { kdDebug(5006) << "Error: " << location() << " Index file is inconsistent with folder file. This should never happen." << endl; + + // We can't recreate the index at this point, since that would invalidate the current + // message list and delete KMMsgBase or KMMessage objects that are in use. + // Do it later in KMFolderIndex::readIndexHeader() instead. mCompactable = false; // Don't compact writeConfig(); } @@ -522,11 +526,16 @@ KMMessage* FolderStorage::getMsg(int idx) //----------------------------------------------------------------------------- KMMessage* FolderStorage::readTemporaryMsg(int idx) { - if(!(idx >= 0 && idx <= count())) + if(!(idx >= 0 && idx <= count())) { + kdDebug(5006) << k_funcinfo << "Invalid index " << idx << "!" << endl; return 0; + } KMMsgBase* mb = getMsgBase(idx); - if (!mb) return 0; + if (!mb) { + kdDebug(5006) << k_funcinfo << "getMsgBase() for " << idx << " failed!" << endl; + return 0; + } unsigned long sernum = mb->getMsgSerNum(); @@ -542,7 +551,11 @@ KMMessage* FolderStorage::readTemporaryMsg(int idx) msg = new KMMessage(*(KMMsgInfo*)mb); msg->setMsgSerNum(sernum); // before fromDwString so that readyToShow uses the right sernum msg->setComplete( true ); - msg->fromDwString(getDwString(idx)); + const DwString msgString = getDwString( idx ); + if ( msgString.size() <= 0 ) { + kdDebug(5006) << k_funcinfo << " Calling getDwString() failed!" << endl; + } + msg->fromDwString( msgString ); } msg->setEnableUndo(undo); return msg; @@ -914,7 +927,7 @@ void FolderStorage::readConfig() mCompactable = config->readBoolEntry("Compactable", true); if ( mSize == -1 ) mSize = config->readNum64Entry("FolderSize", -1); - + int type = config->readNumEntry( "ContentsType", 0 ); if ( type < 0 || type > KMail::ContentsTypeLast ) type = 0; setContentsType( static_cast( type ) ); @@ -1173,4 +1186,22 @@ KMAccount* FolderStorage::account() const return 0; } +bool FolderStorage::mailCheckInProgress() const +{ + return false; +} + +bool FolderStorage::canDeleteMessages() const +{ + return !isReadOnly(); +} + +void FolderStorage::setNoContent(bool aNoContent) +{ + const bool changed = aNoContent != mNoContent; + mNoContent = aNoContent; + if ( changed ) + emit noContentChanged(); +} + #include "folderstorage.moc" diff --git a/kmail/folderstorage.h b/kmail/folderstorage.h index e132c7b0..7d716543 100644 --- a/kmail/folderstorage.h +++ b/kmail/folderstorage.h @@ -106,8 +106,7 @@ public: virtual bool noContent() const { return mNoContent; } /** Specify, that the folder can't contain mails. */ - virtual void setNoContent(bool aNoContent) - { mNoContent = aNoContent; } + virtual void setNoContent(bool aNoContent); /** Returns, if the folder can't have children */ virtual bool noChildren() const { return mNoChildren; } @@ -342,6 +341,9 @@ public: /** Is the folder read-only? */ virtual bool isReadOnly() const = 0; + /** Can messages in this folder be deleted? */ + virtual bool canDeleteMessages() const; + /** Returns the label of the folder for visualization. */ TQString label() const; @@ -417,6 +419,8 @@ public: virtual KMAccount* account() const; + virtual bool mailCheckInProgress() const; + signals: /** Emitted when the status, name, or associated accounts of this folder changed. */ @@ -432,7 +436,7 @@ signals: /** Emitted when the folder was closed and ticket owners have to reopen */ void closed( KMFolder* ); - + /** Emitted when the serial numbers of this folder were invalidated. */ void invalidated( KMFolder * ); @@ -451,6 +455,9 @@ signals: /** Emitted when the readonly status of the folder changes. */ void readOnlyChanged(KMFolder*); + /** Emitted when the no content state of the folder changes. */ + void noContentChanged(); + /** Emitted before a message is removed from the folder. */ void msgRemoved(KMFolder*, Q_UINT32 sernum); @@ -494,6 +501,11 @@ signals: /** Emitted when the folder's size changes. */ void folderSizeChanged(); + /** + * Emiitted when the sync state, i.e. mailCheckInProgress(), changes. + * Currently only supported for disconnected IMAP. + */ + void syncStateChanged(); public slots: /** Incrementally update the index if possible else call writeIndex */ diff --git a/kmail/foldertreebase.cpp b/kmail/foldertreebase.cpp index 3ca61845..c35c8807 100644 --- a/kmail/foldertreebase.cpp +++ b/kmail/foldertreebase.cpp @@ -81,12 +81,12 @@ int FolderTreeBase::dndMode(bool alwaysAsk) action = DRAG_MOVE; } else { if ( GlobalSettings::self()->showPopupAfterDnD() || alwaysAsk ) { - KPopupMenu *menu = new KPopupMenu( this ); - menu->insertItem( i18n("&Move Here"), DRAG_MOVE, 0 ); - menu->insertItem( SmallIcon("editcopy"), i18n("&Copy Here"), DRAG_COPY, 1 ); - menu->insertSeparator(); - menu->insertItem( SmallIcon("cancel"), i18n("C&ancel"), DRAG_CANCEL, 3 ); - action = menu->exec( TQCursor::pos(), 0 ); + KPopupMenu menu; + menu.insertItem( i18n("&Move Here"), DRAG_MOVE, 0 ); + menu.insertItem( SmallIcon("editcopy"), i18n("&Copy Here"), DRAG_COPY, 1 ); + menu.insertSeparator(); + menu.insertItem( SmallIcon("cancel"), i18n("C&ancel"), DRAG_CANCEL, 3 ); + action = menu.exec( TQCursor::pos(), 0 ); } else action = DRAG_MOVE; diff --git a/kmail/folderutil.cpp b/kmail/folderutil.cpp new file mode 100644 index 00000000..8e65fff2 --- /dev/null +++ b/kmail/folderutil.cpp @@ -0,0 +1,113 @@ +/* Copyright 2009 Klarälvdalens Datakonsult AB + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#include "folderutil.h" + +#include "kmfolder.h" +#include "kmfolderimap.h" +#include "kmfoldercachedimap.h" +#include "kmfoldermgr.h" + +#include + +using namespace KMail; +using namespace FolderUtil; + +KMFolder *KMail::FolderUtil::createSubFolder( KMFolder *parentFolder, KMFolderDir *parentDir, + const TQString &folderName, const TQString &namespaceName, + KMFolderType localFolderType ) +{ + KMFolder *newFolder = 0; + + if ( parentFolder && parentFolder->folderType() == KMFolderTypeImap ) { + KMFolderImap* selectedStorage = static_cast( parentFolder->storage() ); + KMAcctImap *anAccount = selectedStorage->account(); + // check if a connection is available BEFORE creating the folder + if (anAccount->makeConnection() == ImapAccountBase::Connected) { + newFolder = kmkernel->imapFolderMgr()->createFolder( folderName, false, KMFolderTypeImap, parentDir ); + if ( newFolder ) { + TQString imapPath, parent; + if ( !namespaceName.isEmpty() ) { + // create folder with namespace + parent = anAccount->addPathToNamespace( namespaceName ); + imapPath = anAccount->createImapPath( parent, folderName ); + } else { + imapPath = anAccount->createImapPath( selectedStorage->imapPath(), folderName ); + } + KMFolderImap* newStorage = static_cast( newFolder->storage() ); + selectedStorage->createFolder(folderName, parent); // create it on the server + newStorage->initializeFrom( selectedStorage, imapPath, TQString::null ); + static_cast(parentFolder->storage())->setAccount( selectedStorage->account() ); + return newFolder; + } + } + } else if ( parentFolder && parentFolder->folderType() == KMFolderTypeCachedImap ) { + newFolder = kmkernel->dimapFolderMgr()->createFolder( folderName, false, KMFolderTypeCachedImap, + parentDir ); + if ( newFolder ) { + KMFolderCachedImap* selectedStorage = static_cast( parentFolder->storage() ); + KMFolderCachedImap* newStorage = static_cast( newFolder->storage() ); + newStorage->initializeFrom( selectedStorage ); + if ( !namespaceName.isEmpty() ) { + // create folder with namespace + TQString path = selectedStorage->account()->createImapPath( + namespaceName, folderName ); + newStorage->setImapPathForCreation( path ); + } + return newFolder; + } + } else { + // local folder + Q_ASSERT( localFolderType == KMFolderTypeMaildir || localFolderType == KMFolderTypeMbox ); + newFolder = kmkernel->folderMgr()->createFolder( folderName, false, localFolderType, + parentDir ); + return newFolder; + } + + return newFolder; +} + +void KMail::FolderUtil::deleteFolder( KMFolder *folderToDelete, TQWidget *parent ) +{ + if ( folderToDelete->hasAccounts() ) { + // this folder has an account, so we need to change that to the inbox + for ( AccountList::Iterator it (folderToDelete->acctList()->begin() ), + end( folderToDelete->acctList()->end() ); it != end; ++it ) { + (*it)->setFolder( kmkernel->inboxFolder() ); + KMessageBox::information(parent, + i18n("The folder you deleted was associated with the account " + "%1 which delivered mail into it. The folder the account " + "delivers new mail into was reset to the main Inbox folder.").arg( (*it)->name())); + } + } + if (folderToDelete->folderType() == KMFolderTypeImap) + kmkernel->imapFolderMgr()->remove(folderToDelete); + else if (folderToDelete->folderType() == KMFolderTypeCachedImap) { + // Deleted by user -> tell the account (see KMFolderCachedImap::listDirectory2) + KMFolderCachedImap* storage = static_cast( folderToDelete->storage() ); + KMAcctCachedImap* acct = storage->account(); + if ( acct ) + acct->addDeletedFolder( folderToDelete ); + + kmkernel->dimapFolderMgr()->remove(folderToDelete); + } + else if (folderToDelete->folderType() == KMFolderTypeSearch) + kmkernel->searchFolderMgr()->remove(folderToDelete); + else + kmkernel->folderMgr()->remove(folderToDelete); +} diff --git a/kmail/folderutil.h b/kmail/folderutil.h new file mode 100644 index 00000000..fbb4d069 --- /dev/null +++ b/kmail/folderutil.h @@ -0,0 +1,65 @@ +/* Copyright 2009 Klarälvdalens Datakonsult AB + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#ifndef FOLDERUTIL_H +#define FOLDERUTIL_H + +#include "kmfoldertype.h" + +class KMFolder; +class KMFolderDir; +class TQString; +class TQWidget; + +namespace KMail +{ + +namespace FolderUtil +{ + +/** + * Low-level function to create a subfolder for a folder of any kind. + * + * @param parentFolder parent folder of the folder that should be created. Can be 0 in case of + * local folders + * @param parentDir parent folder directory, which should be the folder directory of parentFolder + * @param folderName the name the newly created folder should have + * @param namespaceName for (d)IMAP folders, the namespace the new folder should be in. Can be empty. + * @param localFolderType for local folders, this determines if the folder should be MBOX or maildir + * + * @return the newly created folder or 0 in case an error occured + */ +KMFolder *createSubFolder( KMFolder *parentFolder, KMFolderDir *parentDir, + const TQString &folderName, const TQString &namespaceName, + KMFolderType localFolderType ); + +/** + * Deletes a folder and all its subfolders. + * Handles all types of folders correctly, as well as folders with accounts + * + * @param folderToDelete the folder which is going to be deleted + * @param parent the parent widget, which is used when displaying a messagebox, + * which happens when removing a folder with an associated account + */ +void deleteFolder( KMFolder *folderToDelete, TQWidget *parent ); + +} + +} + +#endif diff --git a/kmail/globalsettings_base.kcfgc b/kmail/globalsettings_base.kcfgc index 7fbfbaf8..3ca47605 100644 --- a/kmail/globalsettings_base.kcfgc +++ b/kmail/globalsettings_base.kcfgc @@ -4,4 +4,4 @@ Mutators=true Singleton=true ItemAccessors=true SetUserTexts=true -IncludeFiles=templatesconfiguration.h,kmglobal.h,templatesconfiguration_base.h +IncludeFiles=templatesconfiguration.h,kmglobal.h,templatesconfiguration_base.h,objecttreeparser.h diff --git a/kmail/headeritem.cpp b/kmail/headeritem.cpp index f950d35c..188e0532 100644 --- a/kmail/headeritem.cpp +++ b/kmail/headeritem.cpp @@ -115,6 +115,28 @@ int HeaderItem::msgId() const return mMsgId; } +TQString HeaderItem::to() const +{ + KMHeaders * const headers = static_cast( listView() ); + KMMsgBase * const msgBase = headers->folder()->getMsgBase( mMsgId ); + if ( msgBase ) { + return msgBase->to(); + } else { + return TQString(); + } +} + +TQString HeaderItem::from() const +{ + KMHeaders * const headers = static_cast( listView() ); + KMMsgBase * const msgBase = headers->folder()->getMsgBase( mMsgId ); + if ( msgBase ) { + return msgBase->from(); + } else { + return TQString(); + } +} + // Return the serial number Q_UINT32 HeaderItem::msgSerNum() const { @@ -295,9 +317,14 @@ const TQPixmap *HeaderItem::pixmap(int col) const // Only merge the attachment icon in if that is configured. if ( headers->paintInfo()->showAttachmentIcon && !headers->paintInfo()->showAttachment && - msgBase->attachmentState() == KMMsgHasAttachment ) + msgBase->attachmentState() == KMMsgHasAttachment ) pixmaps << *KMHeaders::pixAttachment; + // Only merge the invitation icon in if that is configured. + if ( headers->paintInfo()->showInvitationIcon && + msgBase->invitationState() == KMMsgHasInvitation ) + pixmaps << *KMHeaders::pixInvitation; + // Only merge the crypto icons in if that is configured. if ( headers->paintInfo()->showCryptoIcons ) { const TQPixmap *pix; @@ -326,6 +353,10 @@ const TQPixmap *HeaderItem::pixmap(int col) const if ( msgBase->attachmentState() == KMMsgHasAttachment ) return KMHeaders::pixAttachment; } + else if ( col == headers->paintInfo()->invitationCol ) { + if ( msgBase->invitationState() == KMMsgHasInvitation ) + return KMHeaders::pixInvitation; + } else if ( col == headers->paintInfo()->importantCol ) { if ( msgBase->isImportant() ) return KMHeaders::pixFlag; @@ -485,6 +516,10 @@ TQString HeaderItem::generate_key( KMHeaders *headers, TQString s(msg->attachmentState() == KMMsgHasAttachment ? "1" : "0"); return ret + s + sortArrival; } + else if (column == paintInfo->invitationCol) { + TQString s(msg->invitationState() == KMMsgHasInvitation ? "1" : "0"); + return ret + s + sortArrival; + } else if (column == paintInfo->importantCol) { TQString s(msg->isImportant() ? "1" : "0"); return ret + s + sortArrival; @@ -558,6 +593,7 @@ int HeaderItem::compare( TQListViewItem *i, int col, bool ascending ) const if ( ( col == headers->paintInfo()->statusCol ) || ( col == headers->paintInfo()->sizeCol ) || ( col == headers->paintInfo()->attachmentCol ) || + ( col == headers->paintInfo()->invitationCol ) || ( col == headers->paintInfo()->importantCol ) || ( col == headers->paintInfo()->todoCol ) || ( col == headers->paintInfo()->spamHamCol ) || diff --git a/kmail/headeritem.h b/kmail/headeritem.h index 961c99b7..ac46cb95 100644 --- a/kmail/headeritem.h +++ b/kmail/headeritem.h @@ -179,6 +179,9 @@ public: /** Return the msgId of the message associated with this item. */ int msgId() const; + TQString to() const; + TQString from() const; + // Return the serial number of the message associated with this item; Q_UINT32 msgSerNum() const; diff --git a/kmail/headerlistquicksearch.cpp b/kmail/headerlistquicksearch.cpp index 9f20b7e8..69ac5b1f 100644 --- a/kmail/headerlistquicksearch.cpp +++ b/kmail/headerlistquicksearch.cpp @@ -69,6 +69,7 @@ HeaderListQuickSearch::HeaderListQuickSearch( TQWidget *parent, TQLabel *label = new TQLabel( i18n("Stat&us:"), parent, "kde toolbar widget" ); mStatusCombo = new TQComboBox( parent, "quick search status combo box" ); + mStatusCombo->setSizeLimit( 12 ); mStatusCombo->insertItem( SmallIcon( "run" ), i18n("Any Status") ); insertStatus( StatusUnread ); @@ -78,6 +79,7 @@ HeaderListQuickSearch::HeaderListQuickSearch( TQWidget *parent, insertStatus( StatusForwarded ); insertStatus( StatusToDo ); insertStatus( StatusHasAttachment ); + insertStatus( StatusInvitation ); insertStatus( StatusWatched ); insertStatus( StatusIgnored ); mStatusCombo->setCurrentItem( 0 ); @@ -91,7 +93,7 @@ HeaderListQuickSearch::HeaderListQuickSearch( TQWidget *parent, 0, i18n( "Open Full Search" ) ); connect( btn, TQT_SIGNAL( clicked() ), TQT_SIGNAL( requestFullSearch() ) ); - /* Disable the signal connected by KListViewSearchLine since it will call + /* Disable the signal connected by KListViewSearchLine since it will call * itemAdded during KMHeaders::readSortOrder() which will in turn result * in getMsgBaseForItem( item ) wanting to access items which are no longer * there. Rather rely on KMHeaders::msgAdded and its signal. */ @@ -149,6 +151,17 @@ bool HeaderListQuickSearch::itemMatches(const TQListViewItem *item, const TQStri if ( !msg || ! ( msg->status() & mStatus ) ) return false; } + + // The full email address is not visible, but we still want it to be searchable. + // KListViewSearchLine::itemMatches() only searches in visible columns. + const HeaderItem *headerItem = static_cast( item ); + if ( headerItem->from().lower().contains( s.lower() ) ) { + return true; + } + if ( headerItem->to().lower().contains( s.lower() ) ) { + return true; + } + return KListViewSearchLine::itemMatches(item, s); } diff --git a/kmail/headerstyle.cpp b/kmail/headerstyle.cpp index 9e6e6315..29046112 100644 --- a/kmail/headerstyle.cpp +++ b/kmail/headerstyle.cpp @@ -118,6 +118,7 @@ namespace KMail { TQString BriefHeaderStyle::format( const KMMessage * message, const HeaderStrategy * strategy, const TQString & vCardName, bool printing, bool topLevel ) const { + Q_UNUSED( topLevel ); if ( !message ) return TQString::null; if ( !strategy ) strategy = HeaderStrategy::brief(); @@ -216,6 +217,7 @@ namespace KMail { TQString PlainHeaderStyle::format( const KMMessage * message, const HeaderStrategy * strategy, const TQString & vCardName, bool printing, bool topLevel ) const { + Q_UNUSED( topLevel ); if ( !message ) return TQString::null; if ( !strategy ) strategy = HeaderStrategy::rich(); @@ -416,6 +418,7 @@ namespace KMail { TQString FancyHeaderStyle::format( const KMMessage * message, const HeaderStrategy * strategy, const TQString & vCardName, bool printing, bool topLevel ) const { + Q_UNUSED( topLevel ); if ( !message ) return TQString::null; if ( !strategy ) strategy = HeaderStrategy::rich(); @@ -785,23 +788,10 @@ namespace KMail { // reverse colors for encapsulated if( !topLevel ){ activeColorDark = activeColor.dark(50); - fontColor = qApp->palette().active().text(); - linkColor = ""; + fontColor = TQColor(TQt::black); + linkColor = "class =\"black\""; } - TQStringList headerParts; - if( strategy->showHeader( "to" ) ) - headerParts << KMMessage::emailAddrAsAnchor( message->to(), false, linkColor ); - - if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() ) - headerParts << i18n("CC: ") + KMMessage::emailAddrAsAnchor( message->cc(), true, linkColor ); - - if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() ) - headerParts << i18n("BCC: ") + KMMessage::emailAddrAsAnchor( message->bcc(), true, linkColor ); - - // remove all empty (modulo whitespace) entries and joins them via ", \n" - TQString headerPart = " " + headerParts.grep( TQRegExp( "\\S" ) ).join( ", " ); - // Prepare the date string (when printing always use the localized date) TQString dateString; if( printing ) { @@ -822,12 +812,12 @@ namespace KMail { if(topLevel) headerStr += "
 
" + "width: 10px; min-height: 100%;\"> " "
 
"; + "width: 10px; min-height: 100%;\"> "; headerStr += "" - "
"+dateString+"
" + "
"+dateString+"
" // #0057ae " \n" " \n" @@ -836,8 +826,15 @@ namespace KMail { " \n" " \n" " \n" - " "; - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - if ( event->doesRecur() ) { - TQDateTime dt = - event->recurrence()->getNextDateTime( TQDateTime::currentDateTime() ); + // TODO: print comments? + + int reminderCount = event->alarms().count(); + if ( reminderCount > 0 && event->isAlarmEnabled() ) { tmpStr += ""; - tmpStr += ""; - if ( !event->doesFloat() ) { - tmpStr += ""; - } else { - tmpStr += ""; - } + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - int attendeeCount = event->attendees().count(); - if ( attendeeCount > 0 ) { - tmpStr += ""; + tmpStr += displayViewFormatAttendees( event ); + + int categoryCount = event->categories().count(); + if ( categoryCount > 0 ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; } int attachmentCount = event->attachments().count(); if ( attachmentCount > 0 ) { tmpStr += ""; - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - tmpStr += "
\n" - " \n"; + " "; - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - if ( !event->location().isEmpty() ) { + if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) { tmpStr += ""; - tmpStr += ""; - tmpStr += ""; + if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) { + tmpStr += ""; + } else { + tmpStr += ""; + } + tmpStr += ""; tmpStr += ""; + tmpStr += "
\n"; + + headerStr += + "
\n" + " \n" + "
\n"; + + headerStr += + " \n"; // subject //strToHtml( message->subject() ) @@ -845,7 +842,7 @@ namespace KMail { headerStr += " \n" " \n" - " \n" + " \n" " \n"; } @@ -858,7 +855,7 @@ namespace KMail { TQString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true, linkColor ); if ( !vCardName.isEmpty() ) fromPart += "  " + i18n("[vCard]") + ""; - //TDDO strategy date + //TODO strategy date //if ( strategy->showHeader( "date" ) ) headerStr += " \n" @@ -867,14 +864,35 @@ namespace KMail { " "; } - // to, cc, bcc - headerStr += + // to line + if( strategy->showHeader( "to" ) ) + headerStr += + " " + " " + " " + " \n"; + + // cc line, if any + if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() ) + headerStr += " " - " " - " " + " " - " "; + " \n"; + + // bcc line, if any + if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() ) + headerStr += + " " + " " + " " + " \n"; // header-bottom headerStr += @@ -893,25 +911,27 @@ namespace KMail { // kmail icon if(topLevel) { - headerStr += - "
\n" - "\n" - "
\n"; // attachments headerStr += - "
" + "
" "
" "
\n"; } - headerStr += "
"; + if ( printing ) { + //provide a bit more left padding when printing + //kolab/issue3254 (printed mail cut at the left side) + headerStr += "
"; + } else { + headerStr += "
"; + } - // TODO - // spam status - // ### iterate over the rest of strategy->headerToDisplay() (or - // ### all headers if DefaultPolicy == Display) (elsewhere, too) - return headerStr; + // TODO + // spam status + // ### iterate over the rest of strategy->headerToDisplay() (or + // ### all headers if DefaultPolicy == Display) (elsewhere, too) + return headerStr; } // ##################### diff --git a/kmail/identitydialog.cpp b/kmail/identitydialog.cpp index c0a1952d..22b5a4c8 100644 --- a/kmail/identitydialog.cpp +++ b/kmail/identitydialog.cpp @@ -48,6 +48,7 @@ using KMail::FolderRequester; #include "kmfolder.h" #include "templatesconfiguration.h" #include "templatesconfiguration_kfg.h" +#include "simplestringlisteditor.h" // other kdepim headers: // libkdepim @@ -76,6 +77,8 @@ using KMail::FolderRequester; #include #include #include +#include +#include // other headers: #include @@ -107,7 +110,7 @@ namespace KMail { tab = new TQWidget( tabWidget ); tabWidget->addTab( tab, i18n("&General") ); - glay = new TQGridLayout( tab, 4, 2, marginHint(), spacingHint() ); + glay = new TQGridLayout( tab, 5, 2, marginHint(), spacingHint() ); glay->setRowStretch( 3, 1 ); glay->setColStretch( 1, 1 ); @@ -140,19 +143,40 @@ namespace KMail { TQWhatsThis::add( mOrganizationEdit, msg ); // "Email Address" line edit and label: - // (row 3: spacer) ++row; mEmailEdit = new KLineEdit( tab ); glay->addWidget( mEmailEdit, row, 1 ); label = new TQLabel( mEmailEdit, i18n("&Email address:"), tab ); glay->addWidget( label, row, 0 ); msg = i18n("

Email address

" - "

This field should have your full email address.

" + "

This field should have your full email address

" + "

This address is the primary one, used for all outgoing mail. " + "If you have more than one address, either create a new identity, " + "or add additional alias addresses in the field below.

" "

If you leave this blank, or get it wrong, people " "will have trouble replying to you.

"); TQWhatsThis::add( label, msg ); TQWhatsThis::add( mEmailEdit, msg ); + // "Email Aliases" string list edit and label: + ++row; + mAliasEdit = new SimpleStringListEditor( tab ); + glay->addMultiCellWidget( mAliasEdit, row, row+1, 1, 1 ); + label = new TQLabel( mAliasEdit, i18n("Email a&liases:"), tab ); + glay->addWidget( label, row, 0, TQt::AlignTop ); + msg = i18n("

Email aliases

" + "

This field contains alias addresses that should also " + "be considered as belonging to this identity (as opposed " + "to representing a different identity).

" + "

Example:

" + "
"+message->subject()+""+message->subject()+"
" + i18n("To: ") + "" + + KMMessage::emailAddrAsAnchor( message->to(), false, linkColor ) + + "
"+i18n("To: ")+"" - +headerPart+ + " " + i18n("CC: ") + "" + + KMMessage::emailAddrAsAnchor( message->cc(), false, linkColor ) + "
" + i18n("BCC: ") + "" + + KMMessage::emailAddrAsAnchor( message->bcc(), false, linkColor ) + + "
" + "" + "" + "
Primary address:first.last@example.org
Aliases:first@example.org
last@example.org
" + "

Type one alias address per line.

"); + TQWhatsThis::add( label, msg ); + TQWhatsThis::add( mAliasEdit, msg ); + // // Tab Widget: Cryptography // @@ -499,6 +523,15 @@ void IdentityDialog::slotOk() { return; } + const TQStringList aliases = mAliasEdit->stringList(); + for ( TQStringList::const_iterator it = aliases.begin(), end = aliases.end() ; it != end ; ++it ) { + if ( !isValidSimpleEmailAddress( *it ) ) { + TQString errorMsg( simpleEmailAddressErrorMsg()); + KMessageBox::sorry( this, errorMsg, i18n("Invalid Email Alias \"%1\"").arg( *it ) ); + return; + } + } + if ( !validateAddresses( mReplyToEdit->text().stripWhiteSpace() ) ) { return; } @@ -584,7 +617,8 @@ void IdentityDialog::slotOk() { // "General" tab: mNameEdit->setText( ident.fullName() ); mOrganizationEdit->setText( ident.organization() ); - mEmailEdit->setText( ident.emailAddr() ); + mEmailEdit->setText( ident.primaryEmailAddress() ); + mAliasEdit->setStringList( ident.emailAliases() ); // "Cryptography" tab: mPGPSigningKeyRequester->setFingerprint( ident.pgpSigningKey() ); @@ -652,7 +686,9 @@ void IdentityDialog::slotOk() { ident.setFullName( mNameEdit->text() ); ident.setOrganization( mOrganizationEdit->text() ); TQString email = mEmailEdit->text(); - ident.setEmailAddr( email ); + ident.setPrimaryEmailAddress( email ); + const TQStringList aliases = mAliasEdit->stringList(); + ident.setEmailAliases( aliases ); // "Cryptography" tab: ident.setPGPSigningKey( mPGPSigningKeyRequester->fingerprint().latin1() ); ident.setPGPEncryptionKey( mPGPEncryptionKeyRequester->fingerprint().latin1() ); diff --git a/kmail/identitydialog.h b/kmail/identitydialog.h index 20bb55a7..f8e71073 100644 --- a/kmail/identitydialog.h +++ b/kmail/identitydialog.h @@ -40,6 +40,7 @@ class TQCheckBox; class TQComboBox; class TQString; class TQStringList; +class SimpleStringListEditor; class TemplatesConfiguration; class KPushButton; namespace Kleo { @@ -87,6 +88,7 @@ namespace KMail { TQLineEdit *mNameEdit; TQLineEdit *mOrganizationEdit; TQLineEdit *mEmailEdit; + SimpleStringListEditor *mAliasEdit; // "cryptography" tab: TQWidget *mCryptographyTab; Kleo::SigningKeyRequester *mPGPSigningKeyRequester; diff --git a/kmail/imapaccountbase.cpp b/kmail/imapaccountbase.cpp index 77840398..b0f78c5a 100644 --- a/kmail/imapaccountbase.cpp +++ b/kmail/imapaccountbase.cpp @@ -198,6 +198,7 @@ namespace KMail { setOnlyLocallySubscribedFolders( config.readBoolEntry( "locally-subscribed-folders", false ) ); setLoadOnDemand( config.readBoolEntry( "loadondemand", false ) ); setListOnlyOpenFolders( config.readBoolEntry( "listOnlyOpenFolders", false ) ); + mCapabilities = config.readListEntry( "capabilities", TQStringList() ); // read namespaces nsMap map; TQStringList list = config.readListEntry( TQString::number( PersonalNS ) ); @@ -237,6 +238,7 @@ namespace KMail { config.writeEntry( "locally-subscribed-folders", onlyLocallySubscribedFolders() ); config.writeEntry( "loadondemand", loadOnDemand() ); config.writeEntry( "listOnlyOpenFolders", listOnlyOpenFolders() ); + config.writeEntry( "capabilities", mCapabilities ); TQString data; for ( nsMap::Iterator it = mNamespaces.begin(); it != mNamespaces.end(); ++it ) { if ( !it.data().isEmpty() ) { @@ -357,7 +359,7 @@ namespace KMail { } //----------------------------------------------------------------------------- - void ImapAccountBase::changeSubscription( bool subscribe, const TQString& imapPath ) + void ImapAccountBase::changeSubscription( bool subscribe, const TQString& imapPath, bool quiet ) { // change the subscription of the folder KURL url = getUrl(); @@ -380,6 +382,7 @@ namespace KMail { // a bit of a hack to save one slot if (subscribe) jd.onlySubscribed = true; else jd.onlySubscribed = false; + jd.quiet = quiet; insertJob(job, jd); connect(job, TQT_SIGNAL(result(KIO::Job *)), @@ -396,7 +399,9 @@ namespace KMail { TQString path = static_cast(job)->url().path(); if (job->error()) { - handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' ); + if ( !(*it).quiet ) + handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' ); + emit subscriptionChangeFailed( job->errorString() ); // ## emit subscriptionChanged here in case anyone needs it to support continue/cancel } else @@ -416,9 +421,9 @@ namespace KMail { // don't even allow removing one's own admin permission, so this code won't hurt either). if ( imapPath == "/INBOX/" ) { if ( parent->folderType() == KMFolderTypeImap ) - static_cast( parent->storage() )->setUserRights( ACLJobs::All ); + static_cast( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok ); else if ( parent->folderType() == KMFolderTypeCachedImap ) - static_cast( parent->storage() )->setUserRights( ACLJobs::All ); + static_cast( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok ); emit receivedUserRights( parent ); // warning, you need to connect first to get that one return; } @@ -452,12 +457,15 @@ namespace KMail { #ifndef NDEBUG //kdDebug(5006) << "User Rights: " << ACLJobs::permissionsToString( job->permissions() ) << endl; #endif - // Store the permissions - if ( folder->folderType() == KMFolderTypeImap ) - static_cast( folder->storage() )->setUserRights( job->permissions() ); - else if ( folder->folderType() == KMFolderTypeCachedImap ) - static_cast( folder->storage() )->setUserRights( job->permissions() ); } + // Store the permissions + if ( folder->folderType() == KMFolderTypeImap ) + static_cast( folder->storage() )->setUserRights( job->permissions(), + job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok ); + else if ( folder->folderType() == KMFolderTypeCachedImap ) + static_cast( folder->storage() )->setUserRights( job->permissions(), + job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok ); + if (mSlave) removeJob(job); emit receivedUserRights( folder ); } @@ -802,7 +810,7 @@ namespace KMail { //----------------------------------------------------------------------------- TQString ImapAccountBase::delimiterForNamespace( const TQString& prefix ) { - kdDebug(5006) << "delimiterForNamespace " << prefix << endl; + //kdDebug(5006) << "delimiterForNamespace " << prefix << endl; // try to match exactly if ( mNamespaceToDelimiter.contains(prefix) ) { return mNamespaceToDelimiter[prefix]; @@ -826,7 +834,7 @@ namespace KMail { return mNamespaceToDelimiter[""]; } // well, we tried - kdDebug(5006) << "delimiterForNamespace - not found" << endl; + //kdDebug(5006) << "delimiterForNamespace - not found" << endl; return TQString::null; } @@ -893,17 +901,17 @@ namespace KMail { bool readOnly = false; if (it != mapJobData.end()) { const KMFolder * const folder = (*it).parent; - assert(folder); + if( !folder ) return _error; const KMFolderCachedImap * const imap = dynamic_cast( folder->storage() ); if ( imap ) { - quotaAsString = imap->quotaInfo().toString(); + quotaAsString = imap->quotaInfo().toString(); } readOnly = folder->isReadOnly(); } error = i18n("The folder is too close to its quota limit. (%1)").arg( quotaAsString ); if ( readOnly ) { error += i18n("\nSince you do not have write privileges on this folder, " - "please ask the owner of the folder to free up some space in it."); + "please ask the owner of the folder to free up some space in it."); } return error; } @@ -1015,12 +1023,12 @@ namespace KMail { } //----------------------------------------------------------------------------- - void ImapAccountBase::processNewMailSingleFolder(KMFolder* folder) + void ImapAccountBase::processNewMailInFolder( KMFolder* folder, FolderListType type /*= Single*/ ) { if ( mFoldersQueuedForChecking.contains( folder ) ) return; - mFoldersQueuedForChecking.append(folder); - mCheckingSingleFolder = true; + mFoldersQueuedForChecking.append( folder ); + mCheckingSingleFolder = ( type == Single ); if ( checkingMail() ) { disconnect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ), diff --git a/kmail/imapaccountbase.h b/kmail/imapaccountbase.h index 26d7a4b0..39fb33b6 100644 --- a/kmail/imapaccountbase.h +++ b/kmail/imapaccountbase.h @@ -187,8 +187,10 @@ namespace KMail { * Subscribe (@p subscribe = TRUE) / Unsubscribe the folder * identified by @p imapPath. * Emits subscriptionChanged signal on success. + * Emits subscriptionChangeFailed signal when it fails. + * @param quiet if false, an error message will be displayed if the job fails. */ - void changeSubscription(bool subscribe, const TQString& imapPath); + void changeSubscription(bool subscribe, const TQString& imapPath, bool quiet = false ); /** * Returns whether the account is locally subscribed to the @@ -252,9 +254,10 @@ namespace KMail { virtual void cancelMailCheck(); /** - * Init a new-mail-check for a single folder + * Init a new-mail-check for a single folder, and optionally its subfolders. */ - void processNewMailSingleFolder(KMFolder* folder); + enum FolderListType { Single, Recursive }; + void processNewMailInFolder( KMFolder* folder, FolderListType type = Single ); /** * Return true if we are processing a mailcheck for a single folder @@ -323,7 +326,7 @@ namespace KMail { /** * Returns the root folder of this account */ - virtual FolderStorage* const rootFolder() const = 0; + virtual FolderStorage* rootFolder() const = 0; /** * Progress item for listDir @@ -585,6 +588,12 @@ namespace KMail { */ void subscriptionChanged(const TQString& imapPath, bool subscribed); + /** + * Emitted when changeSubscription() failed. + * @param errorMessage the error message that contains the reason for the failure + */ + void subscriptionChangeFailed( const TQString &errorMessage ); + /** * Emitted upon completion of the job for setting the status for a group of UIDs, * as a result of a setImapStatus call. @@ -595,7 +604,8 @@ namespace KMail { /** * Emitted when the get-user-rights job is done, * as a result of a getUserRights call. - * Use userRights() to retrieve them, they will still be on 0 if the job failed. + * Use userRights() to retrieve them after using userRightsState() to see if the results are + * valid. */ void receivedUserRights( KMFolder* folder ); diff --git a/kmail/imapjob.cpp b/kmail/imapjob.cpp index 50db423c..56034368 100644 --- a/kmail/imapjob.cpp +++ b/kmail/imapjob.cpp @@ -420,6 +420,8 @@ void ImapJob::slotGetMessageResult( KIO::Job * job ) // do not know if the message has no attachment or we simply did not load the header if (msg->attachmentState() != KMMsgHasAttachment) msg->updateAttachmentState(); + if (msg->invitationState() != KMMsgHasInvitation) + msg->updateInvitationState(); } } else { kdDebug(5006) << "ImapJob::slotGetMessageResult - got no data for " << mPartSpecifier << endl; diff --git a/kmail/importarchivedialog.cpp b/kmail/importarchivedialog.cpp new file mode 100644 index 00000000..bdae8054 --- /dev/null +++ b/kmail/importarchivedialog.cpp @@ -0,0 +1,107 @@ +/* Copyright 2009 Klarälvdalens Datakonsult AB + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#include "importarchivedialog.h" + +#include "kmfolder.h" +#include "folderrequester.h" +#include "kmmainwidget.h" +#include "importjob.h" + +#include +#include +#include + +#include +#include + +using namespace KMail; + +ImportArchiveDialog::ImportArchiveDialog( TQWidget *parent, TQt::WidgetFlags flags ) + : KDialogBase( parent, "import_archive_dialog", false, i18n( "Import Archive" ), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, true ), + mParentWidget( parent ) +{ + setWFlags( flags ); + TQWidget *mainWidget = new TQWidget( this ); + TQGridLayout *mainLayout = new TQGridLayout( mainWidget ); + mainLayout->setSpacing( KDialog::spacingHint() ); + mainLayout->setMargin( KDialog::marginHint() ); + setMainWidget( mainWidget ); + + int row = 0; + + // TODO: Explaination label + // TODO: Use QFormLayout in KDE4 + // TODO: better label for "Ok" button + + TQLabel *folderLabel = new TQLabel( i18n( "&Folder:" ), mainWidget ); + mainLayout->addWidget( folderLabel, row, 0 ); + mFolderRequester = new FolderRequester( mainWidget, kmkernel->getKMMainWidget()->folderTree() ); + folderLabel->setBuddy( mFolderRequester ); + mainLayout->addWidget( mFolderRequester, row, 1 ); + row++; + + TQLabel *fileNameLabel = new TQLabel( i18n( "&Archive File:" ), mainWidget ); + mainLayout->addWidget( fileNameLabel, row, 0 ); + mUrlRequester = new KURLRequester( mainWidget ); + mUrlRequester->setMode( KFile::LocalOnly ); + mUrlRequester->setFilter( "*.tar *.zip *.tar.gz *.tar.bz2" ); + fileNameLabel->setBuddy( mUrlRequester ); + mainLayout->addWidget( mUrlRequester, row, 1 ); + row++; + + // TODO: what's this, tooltips + + mainLayout->setColStretch( 1, 1 ); + mainLayout->addItem( new TQSpacerItem( 1, 1, TQSizePolicy::Expanding, TQSizePolicy::Expanding ), row, 0 ); + + // Make it a bit bigger, else the folder requester cuts off the text too early + resize( 500, minimumSize().height() ); +} + +void ImportArchiveDialog::setFolder( KMFolder *defaultFolder ) +{ + mFolderRequester->setFolder( defaultFolder ); +} + +void ImportArchiveDialog::slotOk() +{ + if ( !TQFile::exists( mUrlRequester->url() ) ) { + KMessageBox::information( this, i18n( "Please select an archive file that should be imported." ), + i18n( "No archive file selected" ) ); + return; + } + + if ( !mFolderRequester->folder() ) { + KMessageBox::information( this, i18n( "Please select the folder where the archive should be imported to." ), + i18n( "No target folder selected" ) ); + return; + } + + // TODO: check if url is empty. or better yet, disable ok button until file is chosen + + ImportJob *importJob = new KMail::ImportJob( mParentWidget ); + importJob->setFile( mUrlRequester->url() ); + importJob->setRootFolder( mFolderRequester->folder() ); + importJob->start(); + accept(); +} + +#include "importarchivedialog.moc" diff --git a/kmail/importarchivedialog.h b/kmail/importarchivedialog.h new file mode 100644 index 00000000..0b04d9b8 --- /dev/null +++ b/kmail/importarchivedialog.h @@ -0,0 +1,55 @@ +/* Copyright 2009 Klarälvdalens Datakonsult AB + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#ifndef IMPORTARCHIVEDIALOG_H +#define IMPORTARCHIVEDIALOG_H + +#include + +class KMFolder; +class KURLRequester; + +namespace KMail +{ +class FolderRequester; + +// TODO: Common base class for ArchiveFolderDialog and ImportArchiveDialog? +class ImportArchiveDialog : public KDialogBase +{ + Q_OBJECT + + public: + + ImportArchiveDialog( TQWidget *parent, TQt::WidgetFlags flags ); + void setFolder( KMFolder *defaultFolder ); + + protected slots: + + /** reimp */ + virtual void slotOk(); + + private: + + TQWidget *mParentWidget; + FolderRequester *mFolderRequester; + KURLRequester *mUrlRequester; +}; + +} + +#endif diff --git a/kmail/importjob.cpp b/kmail/importjob.cpp new file mode 100644 index 00000000..3a7de198 --- /dev/null +++ b/kmail/importjob.cpp @@ -0,0 +1,396 @@ +/* Copyright 2009 Klarälvdalens Datakonsult AB + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#include "importjob.h" + +#include "kmfolder.h" +#include "folderutil.h" +#include "kmfolderdir.h" +#include "kmfolderimap.h" +#include "imapjob.h" + +#include "progressmanager.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace KMail; + +KMail::ImportJob::ImportJob( TQWidget *parentWidget ) + : TQObject( parentWidget ), + mArchive( 0 ), + mRootFolder( 0 ), + mParentWidget( parentWidget ), + mNumberOfImportedMessages( 0 ), + mCurrentFolder( 0 ), + mCurrentMessage( 0 ), + mCurrentMessageFile( 0 ), + mProgressItem( 0 ), + mAborted( false ) +{ +} + +KMail::ImportJob::~ImportJob() +{ + if ( mArchive && mArchive->isOpened() ) { + mArchive->close(); + } + delete mArchive; + mArchive = 0; +} + +void KMail::ImportJob::setFile( const KURL &archiveFile ) +{ + mArchiveFile = archiveFile; +} + +void KMail::ImportJob::setRootFolder( KMFolder *rootFolder ) +{ + mRootFolder = rootFolder; +} + +void KMail::ImportJob::finish() +{ + kdDebug(5006) << "Finished import job." << endl; + mProgressItem->setComplete(); + mProgressItem = 0; + TQString text = i18n( "Importing the archive file '%1' into the folder '%2' succeeded." ) + .arg( mArchiveFile.path() ).arg( mRootFolder->name() ); + text += "\n" + i18n( "1 message was imported.", "%n messages were imported.", mNumberOfImportedMessages ); + KMessageBox::information( mParentWidget, text, i18n( "Import finished." ) ); + deleteLater(); +} + +void KMail::ImportJob::cancelJob() +{ + abort( i18n( "The operation was canceled by the user." ) ); +} + +void KMail::ImportJob::abort( const TQString &errorMessage ) +{ + if ( mAborted ) + return; + + mAborted = true; + TQString text = i18n( "Failed to import the archive into folder '%1'." ).arg( mRootFolder->name() ); + text += "\n" + errorMessage; + if ( mProgressItem ) { + mProgressItem->setComplete(); + mProgressItem = 0; + // The progressmanager will delete it + } + KMessageBox::sorry( mParentWidget, text, i18n( "Importing archive failed." ) ); + deleteLater(); +} + +KMFolder * KMail::ImportJob::createSubFolder( KMFolder *parent, const TQString &folderName, mode_t permissions ) +{ + KMFolder *newFolder = FolderUtil::createSubFolder( parent, parent->child(), folderName, TQString(), + KMFolderTypeMaildir ); + if ( !newFolder ) { + abort( i18n( "Unable to create subfolder for folder '%1'." ).arg( parent->name() ) ); + return 0; + } + else { + newFolder->createChildFolder(); // TODO: Just creating a child folder here is wasteful, only do + // that if really needed. We do it here so we can set the + // permissions + chmod( newFolder->location().latin1(), permissions | S_IXUSR ); + chmod( newFolder->subdirLocation().latin1(), permissions | S_IXUSR ); + // TODO: chown? + // TODO: what about subdirectories like "cur"? + return newFolder; + } +} + +void KMail::ImportJob::enqueueMessages( const KArchiveDirectory *dir, KMFolder *folder ) +{ + const KArchiveDirectory *messageDir = dynamic_cast( dir->entry( "cur" ) ); + if ( messageDir ) { + Messages messagesToQueue; + messagesToQueue.parent = folder; + const TQStringList entries = messageDir->entries(); + for ( uint i = 0; i < entries.size(); i++ ) { + const KArchiveEntry *entry = messageDir->entry( entries[i] ); + Q_ASSERT( entry ); + if ( entry->isDirectory() ) { + kdWarning(5006) << "Unexpected subdirectory in archive folder " << dir->name() << endl; + } + else { + kdDebug(5006) << "Queueing message " << entry->name() << endl; + const KArchiveFile *file = static_cast( entry ); + messagesToQueue.files.append( file ); + } + } + mQueuedMessages.append( messagesToQueue ); + } + else { + kdWarning(5006) << "No 'cur' subdirectory for archive directory " << dir->name() << endl; + } +} + +void KMail::ImportJob::messageAdded() +{ + mNumberOfImportedMessages++; + if ( mCurrentFolder->folderType() == KMFolderTypeMaildir || + mCurrentFolder->folderType() == KMFolderTypeCachedImap ) { + const TQString messageFile = mCurrentFolder->location() + "/cur/" + mCurrentMessage->fileName(); + // TODO: what if the file is not in the "cur" subdirectory? + if ( TQFile::exists( messageFile ) ) { + chmod( messageFile.latin1(), mCurrentMessageFile->permissions() ); + // TODO: changing user/group he requires a bit more work, requires converting the strings + // to uid_t and gid_t + //getpwnam() + //chown( messageFile, + } + else { + kdWarning(5006) << "Unable to change permissions for newly created file: " << messageFile << endl; + } + } + // TODO: Else? + + mCurrentMessage = 0; + mCurrentMessageFile = 0; + TQTimer::singleShot( 0, this, TQT_SLOT( importNextMessage() ) ); +} + +void KMail::ImportJob::importNextMessage() +{ + if ( mAborted ) + return; + + if ( mQueuedMessages.isEmpty() ) { + kdDebug(5006) << "importNextMessage(): Processed all messages in the queue." << endl; + if ( mCurrentFolder ) { + mCurrentFolder->close( "ImportJob" ); + } + mCurrentFolder = 0; + importNextDirectory(); + return; + } + + Messages &messages = mQueuedMessages.front(); + if ( messages.files.isEmpty() ) { + mQueuedMessages.pop_front(); + importNextMessage(); + return; + } + + KMFolder *folder = messages.parent; + if ( folder != mCurrentFolder ) { + kdDebug(5006) << "importNextMessage(): Processed all messages in the current folder of the queue." << endl; + if ( mCurrentFolder ) { + mCurrentFolder->close( "ImportJob" ); + } + mCurrentFolder = folder; + if ( mCurrentFolder->open( "ImportJob" ) != 0 ) { + abort( i18n( "Unable to open folder '%1'." ).arg( mCurrentFolder->name() ) ); + return; + } + kdDebug(5006) << "importNextMessage(): Current folder of queue is now: " << mCurrentFolder->name() << endl; + mProgressItem->setStatus( i18n( "Importing folder %1" ).arg( mCurrentFolder->name() ) ); + } + + mProgressItem->setProgress( ( mProgressItem->progress() + 5 ) ); + + mCurrentMessageFile = messages.files.first(); + Q_ASSERT( mCurrentMessageFile ); + messages.files.removeFirst(); + + mCurrentMessage = new KMMessage(); + mCurrentMessage->fromByteArray( mCurrentMessageFile->data(), true /* setStatus */ ); + int retIndex; + + // If this is not an IMAP folder, we can add the message directly. Otherwise, the whole thing is + // async, for online IMAP. While addMsg() fakes a sync call, we rather do it the async way here + // ourselves, as otherwise the whole thing gets pretty much messed up with regards to folder + // refcounting. Furthermore, the completion dialog would be shown before the messages are actually + // uploaded. + if ( mCurrentFolder->folderType() != KMFolderTypeImap ) { + if ( mCurrentFolder->addMsg( mCurrentMessage, &retIndex ) != 0 ) { + abort( i18n( "Failed to add a message to the folder '%1'." ).arg( mCurrentFolder->name() ) ); + return; + } + messageAdded(); + } + else { + ImapJob *imapJob = new ImapJob( mCurrentMessage, ImapJob::tPutMessage, + dynamic_cast( mCurrentFolder->storage() ) ); + connect( imapJob, TQT_SIGNAL(result(KMail::FolderJob*)), + TQT_SLOT(messagePutResult(KMail::FolderJob*)) ); + imapJob->start(); + } +} + +void KMail::ImportJob::messagePutResult( KMail::FolderJob *job ) +{ + if ( mAborted ) + return; + + if ( job->error() ) { + abort( i18n( "Failed to upload a message to the IMAP server." ) ); + return; + } else { + + KMFolderImap *imap = dynamic_cast( mCurrentFolder->storage() ); + Q_ASSERT( imap ); + + // Ok, we uploaded the message, but we still need to add it to the folder. Use addMsgQuiet(), + // otherwise it will be uploaded again. + imap->addMsgQuiet( mCurrentMessage ); + messageAdded(); + } +} + +// Input: .inbox.directory +// Output: inbox +// Can also return an empty string if this is no valid dir name +static TQString folderNameForDirectoryName( const TQString &dirName ) +{ + Q_ASSERT( dirName.startsWith( "." ) ); + const TQString end = ".directory"; + const int expectedIndex = dirName.length() - end.length(); + if ( dirName.lower().find( end ) != expectedIndex ) + return TQString(); + TQString returnName = dirName.left( dirName.length() - end.length() ); + returnName = returnName.right( returnName.length() - 1 ); + return returnName; +} + +KMFolder* KMail::ImportJob::getOrCreateSubFolder( KMFolder *parentFolder, const TQString &subFolderName, + mode_t subFolderPermissions ) +{ + if ( !parentFolder->createChildFolder() ) { + abort( i18n( "Unable to create subfolder for folder '%1'." ).arg( parentFolder->name() ) ); + return 0; + } + + KMFolder *subFolder = 0; + subFolder = dynamic_cast( parentFolder->child()->hasNamedFolder( subFolderName ) ); + + if ( !subFolder ) { + subFolder = createSubFolder( parentFolder, subFolderName, subFolderPermissions ); + } + return subFolder; +} + +void KMail::ImportJob::importNextDirectory() +{ + if ( mAborted ) + return; + + if ( mQueuedDirectories.isEmpty() ) { + finish(); + return; + } + + Folder folder = mQueuedDirectories.first(); + KMFolder *currentFolder = folder.parent; + mQueuedDirectories.pop_front(); + kdDebug(5006) << "importNextDirectory(): Working on directory " << folder.archiveDir->name() << endl; + + TQStringList entries = folder.archiveDir->entries(); + for ( uint i = 0; i < entries.size(); i++ ) { + const KArchiveEntry *entry = folder.archiveDir->entry( entries[i] ); + Q_ASSERT( entry ); + kdDebug(5006) << "Queueing entry " << entry->name() << endl; + if ( entry->isDirectory() ) { + const KArchiveDirectory *dir = static_cast( entry ); + if ( !dir->name().startsWith( "." ) ) { + + kdDebug(5006) << "Queueing messages in folder " << entry->name() << endl; + KMFolder *subFolder = getOrCreateSubFolder( currentFolder, entry->name(), entry->permissions() ); + if ( !subFolder ) + return; + + enqueueMessages( dir, subFolder ); + } + + // Entry starts with a dot, so we assume it is a subdirectory + else { + + const TQString folderName = folderNameForDirectoryName( entry->name() ); + if ( folderName.isEmpty() ) { + abort( i18n( "Unexpected subdirectory named '%1'." ).arg( entry->name() ) ); + return; + } + KMFolder *subFolder = getOrCreateSubFolder( currentFolder, folderName, entry->permissions() ); + if ( !subFolder ) + return; + + Folder newFolder; + newFolder.archiveDir = dir; + newFolder.parent = subFolder; + kdDebug(5006) << "Enqueueing directory " << entry->name() << endl; + mQueuedDirectories.push_back( newFolder ); + } + } + } + + importNextMessage(); +} + +// TODO: +// BUGS: +// Online IMAP can fail spectacular, for example when cancelling upload +// Online IMAP: Inform that messages are still being uploaded on finish()! +void KMail::ImportJob::start() +{ + Q_ASSERT( mRootFolder ); + Q_ASSERT( mArchiveFile.isValid() ); + + KMimeType::Ptr mimeType = KMimeType::findByURL( mArchiveFile, 0, true /* local file */ ); + if ( !mimeType->patterns().grep( "tar", false /* no case-sensitive */ ).isEmpty() ) + mArchive = new KTar( mArchiveFile.path() ); + else if ( !mimeType->patterns().grep( "zip", false ).isEmpty() ) + mArchive = new KZip( mArchiveFile.path() ); + else { + abort( i18n( "The file '%1' does not appear to be a valid archive." ).arg( mArchiveFile.path() ) ); + return; + } + + if ( !mArchive->open( IO_ReadOnly ) ) { + abort( i18n( "Unable to open archive file '%1'" ).arg( mArchiveFile.path() ) ); + return; + } + + mProgressItem = KPIM::ProgressManager::createProgressItem( + "ImportJob", + i18n( "Importing Archive" ), + TQString(), + true ); + mProgressItem->setUsesBusyIndicator( true ); + connect( mProgressItem, TQT_SIGNAL(progressItemCanceled(KPIM::ProgressItem*)), + this, TQT_SLOT(cancelJob()) ); + + Folder nextFolder; + nextFolder.archiveDir = mArchive->directory(); + nextFolder.parent = mRootFolder; + mQueuedDirectories.push_back( nextFolder ); + importNextDirectory(); +} + +#include "importjob.moc" diff --git a/kmail/importjob.h b/kmail/importjob.h new file mode 100644 index 00000000..ee7a0ac8 --- /dev/null +++ b/kmail/importjob.h @@ -0,0 +1,126 @@ +/* Copyright 2009 Klarälvdalens Datakonsult AB + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#ifndef IMPORTJOB_H +#define IMPORTJOB_H + +#include + +#include +#include +#include + +#include + +class TQWidget; +class KArchive; +class KArchiveDirectory; +class KArchiveFile; +class KMFolder; +class KMMessage; + +namespace KPIM +{ + class ProgressItem; +} + +namespace KMail +{ + class FolderJob; + +/** + * Imports an archive that was previously backed up with an BackupJob. + * This job will re-create the folder structure, under the root folder given in setRootFolder(). + * + * The job deletes itself after it finished. + */ +class ImportJob : public TQObject +{ + Q_OBJECT + + public: + + explicit ImportJob( TQWidget *parentWidget = 0 ); + ~ImportJob(); + void start(); + void setFile( const KURL &archiveFile ); + void setRootFolder( KMFolder *rootFolder ); + + private slots: + + void importNextMessage(); + void cancelJob(); + void messagePutResult( KMail::FolderJob *job ); + + private: + + struct Folder + { + KMFolder *parent; + const KArchiveDirectory *archiveDir; + }; + + struct Messages + { + KMFolder *parent; + TQPtrList files; + }; + + void finish(); + void abort( const TQString &errorMessage ); + void queueFolders(); + void importNextDirectory(); + KMFolder* createSubFolder( KMFolder *parent, const TQString &folderName, mode_t permissions ); + KMFolder* getOrCreateSubFolder( KMFolder *parentFolder, const TQString &subFolderName, + mode_t subFolderPermissions ); + void enqueueMessages( const KArchiveDirectory *dir, KMFolder *folder ); + void messageAdded(); + + KArchive *mArchive; + + // The root folder which the user has selected as the folder to which everything should be + // imported + KMFolder *mRootFolder; + + TQWidget *mParentWidget; + KURL mArchiveFile; + int mNumberOfImportedMessages; + + // List of archive folders with their corresponding KMail parent folder that are awaiting + // processing + TQValueList mQueuedDirectories; + + // List of list of messages and their parent folders which are awaiting processing + TQValueList mQueuedMessages; + + // The folder to which we are currently importing messages + KMFolder *mCurrentFolder; + + // The message which is currently being added + KMMessage *mCurrentMessage; + + // The archive file of the current message that is being added + KArchiveFile *mCurrentMessageFile; + + KPIM::ProgressItem *mProgressItem; + bool mAborted; +}; + +} + +#endif diff --git a/kmail/interfaces/bodypartformatter.h b/kmail/interfaces/bodypartformatter.h index b31cb26b..9bc947f5 100644 --- a/kmail/interfaces/bodypartformatter.h +++ b/kmail/interfaces/bodypartformatter.h @@ -35,7 +35,7 @@ #define __KMAIL_INTERFACE_BODYPARTFORMATTER_H__ namespace KMail { - + class Callback; class HtmlWriter; namespace Interface { @@ -47,7 +47,7 @@ namespace KMail { public: virtual ~BodyPartFormatter() {} - /** + /** @li Ok returned when format() generated some HTML @li NeedContent returned when format() needs the body of the part @li AsIcon returned when the part should be shown iconified @@ -61,7 +61,7 @@ namespace KMail { @return the result code (see above) */ - virtual Result format( BodyPart * part, KMail::HtmlWriter * writer ) const = 0; + virtual Result format( BodyPart * part, KMail::HtmlWriter * writer, Callback &c ) const = 0; }; /** diff --git a/kmail/interfaces/urlhandler.h b/kmail/interfaces/urlhandler.h index fba673d6..38ada083 100644 --- a/kmail/interfaces/urlhandler.h +++ b/kmail/interfaces/urlhandler.h @@ -57,6 +57,41 @@ namespace KMail { false otherwise. */ virtual bool handleClick( const KURL & url, KMReaderWin * w ) const = 0; + + /** + * Called when shift-clicking the link in the reader. + * @return true if the click was handled by this URLHandler, false otherwise + */ + virtual bool handleShiftClick( const KURL &url, KMReaderWin *window ) const { + Q_UNUSED( url ); + Q_UNUSED( window ); + return false; + } + + /** + * @return should return true if this URLHandler will handle the drag + */ + virtual bool willHandleDrag( const KURL &url, const TQString &imagePath, + KMReaderWin *window ) const { + Q_UNUSED( url ); + Q_UNUSED( window ); + Q_UNUSED( imagePath ); + return false; + } + + /** + * Called when starting a drag with the given URL. + * If the drag is handled, you should create a drag object. + * @return true if the click was handled by this URLHandler, false otherwise + */ + virtual bool handleDrag( const KURL &url, const TQString &imagePath, + KMReaderWin *window ) const { + Q_UNUSED( url ); + Q_UNUSED( window ); + Q_UNUSED( imagePath ); + return false; + } + /** Called when RMB-clicking on a link in the reader. Should show a context menu at the specified point with the specified widget as parent. diff --git a/kmail/isubject.cpp b/kmail/isubject.cpp index 7219dc0e..d231d365 100644 --- a/kmail/isubject.cpp +++ b/kmail/isubject.cpp @@ -31,8 +31,15 @@ namespace KMail { void ISubject::notify() { kdDebug(5006) << "ISubject::notify " << mObserverList.size() << endl; - for ( TQValueVector::iterator it = mObserverList.begin() ; it != mObserverList.end() ; ++it ) - (*it)->update( this ); + + // iterate over a copy (to prevent crashes when + // {attach(),detach()} is called from an Observer::update() + const TQValueVector copy = mObserverList; + for ( TQValueVector::const_iterator it = copy.begin() ; it != copy.end() ; ++it ) { + if ( (*it) ) { + (*it)->update( this ); + } + } } } diff --git a/kmail/keyresolver.cpp b/kmail/keyresolver.cpp index e35a7f5f..3c1a952c 100644 --- a/kmail/keyresolver.cpp +++ b/kmail/keyresolver.cpp @@ -42,6 +42,7 @@ #include "kcursorsaver.h" #include "kleo_util.h" +#include "stl_util.h" #include #include @@ -103,7 +104,7 @@ static inline bool WithRespectToKeyID( const GpgME::Key & left, const GpgME::Key return qstrcmp( left.keyID(), right.keyID() ) == 0 ; } -static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) { +static bool ValidOpenPGPEncryptionKey( const GpgME::Key & key ) { if ( key.protocol() != GpgME::Context::OpenPGP ) { return false; } @@ -119,9 +120,15 @@ static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) { #endif if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() ) return false; + return true; +} + +static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) { + if ( !ValidOpenPGPEncryptionKey( key ) ) + return false; const std::vector uids = key.userIDs(); for ( std::vector::const_iterator it = uids.begin() ; it != uids.end() ; ++it ) { - if ( !it->isRevoked() && it->validity() != GpgME::UserID::Marginal ) + if ( !it->isRevoked() && it->validity() >= GpgME::UserID::Marginal ) return true; #if 0 else @@ -134,7 +141,7 @@ static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) { return false; } -static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) { +static bool ValidSMIMEEncryptionKey( const GpgME::Key & key ) { if ( key.protocol() != GpgME::Context::CMS ) return false; if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() ) @@ -142,6 +149,12 @@ static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) { return true; } +static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) { + if ( !ValidSMIMEEncryptionKey( key ) ) + return false; + return true; +} + static inline bool ValidTrustedEncryptionKey( const GpgME::Key & key ) { switch ( key.protocol() ) { case GpgME::Context::OpenPGP: @@ -153,6 +166,17 @@ static inline bool ValidTrustedEncryptionKey( const GpgME::Key & key ) { } } +static inline bool ValidEncryptionKey( const GpgME::Key & key ) { + switch ( key.protocol() ) { + case GpgME::Context::OpenPGP: + return ValidOpenPGPEncryptionKey( key ); + case GpgME::Context::CMS: + return ValidSMIMEEncryptionKey( key ); + default: + return false; + } +} + static inline bool ValidSigningKey( const GpgME::Key & key ) { if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canSign() ) return false; @@ -171,14 +195,26 @@ static inline bool NotValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) return !ValidTrustedOpenPGPEncryptionKey( key ); } +static inline bool NotValidOpenPGPEncryptionKey( const GpgME::Key & key ) { + return !ValidOpenPGPEncryptionKey( key ); +} + static inline bool NotValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) { return !ValidTrustedSMIMEEncryptionKey( key ); } +static inline bool NotValidSMIMEEncryptionKey( const GpgME::Key & key ) { + return !ValidSMIMEEncryptionKey( key ); +} + static inline bool NotValidTrustedEncryptionKey( const GpgME::Key & key ) { return !ValidTrustedEncryptionKey( key ); } +static inline bool NotValidEncryptionKey( const GpgME::Key & key ) { + return !ValidEncryptionKey( key ); +} + static inline bool NotValidSigningKey( const GpgME::Key & key ) { return !ValidSigningKey( key ); } @@ -191,6 +227,40 @@ static inline bool NotValidSMIMESigningKey( const GpgME::Key & key ) { return !ValidSMIMESigningKey( key ); } +namespace { + struct ByTrustScore { + static int score( const GpgME::UserID & uid ) { + return uid.isRevoked() || uid.isInvalid() ? -1 : uid.validity() ; + } + bool operator()( const GpgME::UserID & lhs, const GpgME::UserID & rhs ) const { + return score( lhs ) < score( rhs ) ; + } + }; +} + +static std::vector matchingUIDs( const std::vector & uids, const TQString & address ) { + if ( address.isEmpty() ) + return std::vector(); + + std::vector result; + result.reserve( uids.size() ); + for ( std::vector::const_iterator it = uids.begin(), end = uids.end() ; it != end ; ++it ) + // PENDING(marc) check DN for an EMAIL, too, in case of X.509 certs... :/ + if ( const char * email = it->email() ) + if ( *email && TQString::fromUtf8( email ).stripWhiteSpace().lower() == address ) + result.push_back( *it ); + return result; +} + +static GpgME::UserID findBestMatchUID( const GpgME::Key & key, const TQString & address ) { + const std::vector all = key.userIDs(); + if ( all.empty() ) + return GpgME::UserID(); + const std::vector matching = matchingUIDs( all, address.lower() ); + const std::vector & v = matching.empty() ? all : matching ; + return *std::max_element( v.begin(), v.end(), ByTrustScore() ); +} + static TQStringList keysAsStrings( const std::vector& keys ) { TQStringList strings; for ( std::vector::const_iterator it = keys.begin() ; it != keys.end() ; ++it ) { @@ -205,35 +275,40 @@ static TQStringList keysAsStrings( const std::vector& keys ) { return strings; } -static inline std::vector TrustedOrConfirmed( const std::vector & keys ) { +static std::vector trustedOrConfirmed( const std::vector & keys, const TQString & address, bool & canceled ) { + // PENDING(marc) work on UserIDs here? std::vector fishies; std::vector ickies; + std::vector rewookies; std::vector::const_iterator it = keys.begin(); const std::vector::const_iterator end = keys.end(); for ( ; it != end ; it++ ) { - const GpgME::Key key = *it; - assert( ValidTrustedEncryptionKey( key ) ); - const std::vector uids = key.userIDs(); - for ( std::vector::const_iterator it = uids.begin() ; it != uids.end() ; ++it ) { - if ( !it->isRevoked() && it->validity() == GpgME::UserID::Marginal ) { + const GpgME::Key & key = *it; + assert( ValidEncryptionKey( key ) ); + const GpgME::UserID uid = findBestMatchUID( key, address ); + if ( uid.isRevoked() ) { + rewookies.push_back( key ); + } + if ( !uid.isRevoked() && uid.validity() == GpgME::UserID::Marginal ) { fishies.push_back( key ); - break; - } - if ( !it->isRevoked() && it->validity() < GpgME::UserID::Never ) { + } + if ( !uid.isRevoked() && uid.validity() < GpgME::UserID::Never ) { ickies.push_back( key ); - break; - } } } - if ( fishies.empty() && ickies.empty() ) + if ( fishies.empty() && ickies.empty() && rewookies.empty() ) return keys; // if some keys are not fully trusted, let the user confirm their use - TQString msg = i18n("One or more of your configured OpenPGP encryption " - "keys or S/MIME certificates is not fully trusted " - "for encryption."); + TQString msg = address.isEmpty() + ? i18n("One or more of your configured OpenPGP encryption " + "keys or S/MIME certificates is not fully trusted " + "for encryption.") + : i18n("One or more of the OpenPGP encryption keys or S/MIME " + "certificates for recipient \"%1\" is not fully trusted " + "for encryption.").arg(address) ; if ( !fishies.empty() ) { // certificates can't have marginal trust @@ -244,6 +319,10 @@ static inline std::vector TrustedOrConfirmed( const std::vectorrevoked: \n"); + msg += keysAsStrings( rewookies ).join(","); + } if( KMessageBox::warningContinueCancel( 0, msg, i18n("Not Fully Trusted Encryption Keys"), KStdGuiItem::cont(), @@ -251,7 +330,8 @@ static inline std::vector TrustedOrConfirmed( const std::vector(); + canceled = true; + return std::vector(); } namespace { @@ -266,6 +346,20 @@ namespace { const Kleo::CryptoMessageFormat format; }; + + struct IsForFormat : std::unary_function { + explicit IsForFormat( Kleo::CryptoMessageFormat f ) + : protocol( isOpenPGP( f ) ? GpgME::Context::OpenPGP : + isSMIME( f ) ? GpgME::Context::CMS : + /* else */ GpgME::Context::Unknown ) {} + + bool operator()( const GpgME::Key & key ) const { + return key.protocol() == protocol ; + } + + const GpgME::Context::Protocol protocol; + }; + } @@ -334,6 +428,11 @@ public: } void operator()( Item & item ); + template + void process( Container & c ) { + *this = std::for_each( c.begin(), c.end(), *this ); + } + #define make_int_accessor(x) unsigned int num##x() const { return m##x; } make_int_accessor(NoKey) make_int_accessor(NeverEncrypt) @@ -346,6 +445,7 @@ public: #undef make_int_accessor private: EncryptionPreference mDefaultPreference; + bool mNoOps; unsigned int mTotal; unsigned int mNoKey; unsigned int mNeverEncrypt, mUnknownPreference, mAlwaysEncrypt, @@ -353,12 +453,14 @@ private: }; void Kleo::KeyResolver::EncryptionPreferenceCounter::operator()( Item & item ) { + if ( _this ) { if ( item.needKeys ) item.keys = _this->getEncryptionKeys( item.address, true ); if ( item.keys.empty() ) { ++mNoKey; return; } + } switch ( !item.pref ? mDefaultPreference : item.pref ) { #define CASE(x) case Kleo::x: ++m##x; break CASE(NeverEncrypt); @@ -427,13 +529,13 @@ namespace { void EncryptionFormatPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) { if ( item.format & (Kleo::InlineOpenPGPFormat|Kleo::OpenPGPMIMEFormat) && std::find_if( item.keys.begin(), item.keys.end(), - ValidTrustedOpenPGPEncryptionKey ) != item.keys.end() ) { + ValidTrustedOpenPGPEncryptionKey ) != item.keys.end() ) { // -= trusted? CASE(OpenPGPMIME); CASE(InlineOpenPGP); } if ( item.format & (Kleo::SMIMEFormat|Kleo::SMIMEOpaqueFormat) && std::find_if( item.keys.begin(), item.keys.end(), - ValidTrustedSMIMEEncryptionKey ) != item.keys.end() ) { + ValidTrustedSMIMEEncryptionKey ) != item.keys.end() ) { // -= trusted? CASE(SMIME); CASE(SMIMEOpaque); } @@ -523,13 +625,108 @@ Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, cons const GpgME::Subkey subkey = key.subkey(0); if ( d->alreadyWarnedFingerprints.count( subkey.fingerprint() ) ) return Kpgp::Ok; // already warned about this one (and so about it's issuers) - d->alreadyWarnedFingerprints.insert( subkey.fingerprint() ); if ( subkey.neverExpires() ) return Kpgp::Ok; static const double secsPerDay = 24 * 60 * 60; - const int daysTillExpiry = - 1 + int( ::difftime( subkey.expirationTime(), time(0) ) / secsPerDay ); + const double secsTillExpiry = ::difftime( subkey.expirationTime(), time(0) ); + if ( secsTillExpiry <= 0 ) { + const int daysSinceExpiry = 1 + int( -secsTillExpiry / secsPerDay ); + kdDebug() << "Key 0x" << key.shortKeyID() << " expired less than " + << daysSinceExpiry << " days ago" << endl; + const TQString msg = + key.protocol() == GpgME::Context::OpenPGP + ? ( mine ? sign + ? i18n("

Your OpenPGP signing key

%1 (KeyID 0x%2)

" + "

expired less than a day ago.

", + "

Your OpenPGP signing key

%1 (KeyID 0x%2)

" + "

expired %n days ago.

", + daysSinceExpiry ) + : i18n("

Your OpenPGP encryption key

%1 (KeyID 0x%2)

" + "

expired less than a day ago.

", + "

Your OpenPGP encryption key

%1 (KeyID 0x%2)

" + "

expired %n days ago.

", + daysSinceExpiry ) + : i18n("

The OpenPGP key for

%1 (KeyID 0x%2)

" + "

expired less than a day ago.

", + "

The OpenPGP key for

%1 (KeyID 0x%2)

" + "

expired %n days ago.

", + daysSinceExpiry ) ).arg( TQString::fromUtf8( key.userID(0).id() ), + key.shortKeyID() ) + : ( ca + ? ( key.isRoot() + ? ( mine ? sign + ? i18n("

The root certificate

%3

" + "

for your S/MIME signing certificate

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

The root certificate

%3

" + "

for your S/MIME signing certificate

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) + : i18n("

The root certificate

%3

" + "

for your S/MIME encryption certificate

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

The root certificate

%3

" + "

for your S/MIME encryption certificate

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) + : i18n("

The root certificate

%3

" + "

for S/MIME certificate

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

The root certificate

%3

" + "

for S/MIME certificate

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) ) + : ( mine ? sign + ? i18n("

The intermediate CA certificate

%3

" + "

for your S/MIME signing certificate

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

The intermediate CA certificate

%3

" + "

for your S/MIME signing certificate

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) + : i18n("

The intermediate CA certificate

%3

" + "

for your S/MIME encryption certificate

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

The intermediate CA certificate

%3

" + "

for your S/MIME encryption certificate

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) + : i18n("

The intermediate CA certificate

%3

" + "

for S/MIME certificate

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

The intermediate CA certificate

%3

" + "

for S/MIME certificate

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) ) ).arg( Kleo::DN( orig.userID(0).id() ).prettyDN(), + orig.issuerSerial(), + Kleo::DN( key.userID(0).id() ).prettyDN() ) + : ( mine ? sign + ? i18n("

Your S/MIME signing certificate

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

Your S/MIME signing certificate

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) + : i18n("

Your S/MIME encryption certificate

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

Your S/MIME encryption certificate

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) + : i18n("

The S/MIME certificate for

%1 (serial number %2)

" + "

expired less than a day ago.

", + "

The S/MIME certificate for

%1 (serial number %2)

" + "

expired %n days ago.

", + daysSinceExpiry ) ).arg( Kleo::DN( key.userID(0).id() ).prettyDN(), + key.issuerSerial() ) ); + d->alreadyWarnedFingerprints.insert( subkey.fingerprint() ); + if ( KMessageBox::warningContinueCancel( 0, msg, + key.protocol() == GpgME::Context::OpenPGP + ? i18n("OpenPGP Key Expired" ) + : i18n("S/MIME Certificate Expired" ), + KStdGuiItem::cont(), dontAskAgainName ) == KMessageBox::Cancel ) + return Kpgp::Canceled; + } else { + const int daysTillExpiry = 1 + int( secsTillExpiry / secsPerDay ); kdDebug() << "Key 0x" << key.shortKeyID() << " expires in less than " << daysTillExpiry << " days" << endl; const int threshold = @@ -629,6 +826,7 @@ Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, cons "

expires in less than %n days.

", daysTillExpiry ) ).arg( Kleo::DN( key.userID(0).id() ).prettyDN(), key.issuerSerial() ) ); + d->alreadyWarnedFingerprints.insert( subkey.fingerprint() ); if ( KMessageBox::warningContinueCancel( 0, msg, key.protocol() == GpgME::Context::OpenPGP ? i18n("OpenPGP Key Expires Soon" ) @@ -637,6 +835,7 @@ Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, cons == KMessageBox::Cancel ) return Kpgp::Canceled; } + } if ( key.isRoot() ) return Kpgp::Ok; else if ( const char * chain_id = key.chainID() ) { @@ -657,10 +856,10 @@ Kpgp::Result Kleo::KeyResolver::setEncryptToSelfKeys( const TQStringList & finge std::vector keys = lookup( fingerprints ); std::remove_copy_if( keys.begin(), keys.end(), std::back_inserter( d->mOpenPGPEncryptToSelfKeys ), - NotValidTrustedOpenPGPEncryptionKey ); + NotValidTrustedOpenPGPEncryptionKey ); // -= trusted? std::remove_copy_if( keys.begin(), keys.end(), std::back_inserter( d->mSMIMEEncryptToSelfKeys ), - NotValidTrustedSMIMEEncryptionKey ); + NotValidTrustedSMIMEEncryptionKey ); // -= trusted? if ( d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size() < keys.size() ) { @@ -814,6 +1013,20 @@ Kleo::Action Kleo::KeyResolver::checkEncryptionPreferences( bool encryptionReque d->mOpenPGPEncryptToSelfKeys.empty() && d->mSMIMEEncryptToSelfKeys.empty() ) return Impossible; + if ( !encryptionRequested && !mOpportunisticEncyption ) { + // try to minimize crypto ops (including key lookups) by only + // looking up keys when at least one the the encryption + // preferences needs it: + EncryptionPreferenceCounter count( 0, UnknownPreference ); + count.process( d->mPrimaryEncryptionKeys ); + count.process( d->mSecondaryEncryptionKeys ); + if ( !count.numAlwaysEncrypt() && + !count.numAlwaysAskForEncryption() && // this guy might not need a lookup, when declined, but it's too complex to implement that here + !count.numAlwaysEncryptIfPossible() && + !count.numAskWheneverPossible() ) + return DontDoIt; + } + EncryptionPreferenceCounter count( this, mOpportunisticEncyption ? AskWheneverPossible : UnknownPreference ); count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count ); @@ -859,9 +1072,10 @@ Kpgp::Result Kleo::KeyResolver::resolveAllKeys( bool& signingRequested, bool& en result = resolveEncryptionKeys( signingRequested ); if ( result != Kpgp::Ok ) return result; - if ( signingRequested ) - if ( encryptionRequested ) + if ( signingRequested ) { + if ( encryptionRequested ) { result = resolveSigningKeysForEncryption(); + } else { result = resolveSigningKeysForSigningOnly(); if ( result == Kpgp::Failure ) { @@ -869,6 +1083,7 @@ Kpgp::Result Kleo::KeyResolver::resolveAllKeys( bool& signingRequested, bool& en return Kpgp::Ok; } } + } return result; } @@ -1334,10 +1549,10 @@ Kpgp::Result Kleo::KeyResolver::showKeyApprovalDialog() { std::remove_copy_if( senderKeys.begin(), senderKeys.end(), std::back_inserter( d->mOpenPGPEncryptToSelfKeys ), - NotValidTrustedOpenPGPEncryptionKey ); + NotValidTrustedOpenPGPEncryptionKey ); // -= trusted (see above, too)? std::remove_copy_if( senderKeys.begin(), senderKeys.end(), std::back_inserter( d->mSMIMEEncryptToSelfKeys ), - NotValidTrustedSMIMEEncryptionKey ); + NotValidTrustedSMIMEEncryptionKey ); // -= trusted (see above, too)? return Kpgp::Ok; } @@ -1364,16 +1579,21 @@ std::vector Kleo::KeyResolver::signingKeys( CryptoMessageFormat f ) std::vector Kleo::KeyResolver::selectKeys( const TQString & person, const TQString & msg, const std::vector & selectedKeys ) const { + const bool opgp = containsOpenPGP( mCryptoMessageFormats ); + const bool x509 = containsSMIME( mCryptoMessageFormats ); + Kleo::KeySelectionDialog dlg( i18n("Encryption Key Selection"), - msg, selectedKeys, - Kleo::KeySelectionDialog::ValidEncryptionKeys, + msg, KPIM::getEmailAddress(person), selectedKeys, + Kleo::KeySelectionDialog::ValidEncryptionKeys + & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys) + & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys), true, true ); // multi-selection and "remember choice" box if ( dlg.exec() != TQDialog::Accepted ) return std::vector(); std::vector keys = dlg.selectedKeys(); keys.erase( std::remove_if( keys.begin(), keys.end(), - NotValidTrustedEncryptionKey ), + NotValidTrustedEncryptionKey ), // -= trusted? keys.end() ); if ( !keys.empty() && dlg.rememberSelection() ) setKeysForAddress( person, dlg.pgpKeyFingerprints(), dlg.smimeFingerprints() ); @@ -1396,70 +1616,80 @@ std::vector Kleo::KeyResolver::getEncryptionKeys( const TQString & p if ( !keys.empty() ) { // Check if all of the keys are trusted and valid encryption keys if ( std::find_if( keys.begin(), keys.end(), - NotValidTrustedEncryptionKey ) != keys.end() ) { + NotValidTrustedEncryptionKey ) != keys.end() ) { // -= trusted? // not ok, let the user select: this is not conditional on !quiet, // since it's a bug in the configuration and the user should be // notified about it as early as possible: keys = selectKeys( person, i18n("if in your language something like " - "'key(s)' isn't possible please " + "'certificate(s)' isn't possible please " "use the plural in the translation", "There is a problem with the " - "encryption key(s) for \"%1\".\n\n" - "Please re-select the key(s) which should " + "encryption certificate(s) for \"%1\".\n\n" + "Please re-select the certificate(s) which should " "be used for this recipient.").arg(person), keys ); } - keys = TrustedOrConfirmed( keys ); + bool canceled = false; + keys = trustedOrConfirmed( keys, address, canceled ); + if ( canceled ) + return std::vector(); if ( !keys.empty() ) return keys; - // hmmm, should we not return the keys in any case here? + // keys.empty() is considered cancel by callers, so go on } } // Now search all public keys for matching keys std::vector matchingKeys = lookup( person ); matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(), - NotValidTrustedEncryptionKey ), + NotValidEncryptionKey ), matchingKeys.end() ); // if no keys match the complete address look for keys which match // the canonical mail address if ( matchingKeys.empty() ) { matchingKeys = lookup( address ); matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(), - NotValidTrustedEncryptionKey ), + NotValidEncryptionKey ), matchingKeys.end() ); } // if called with quite == true (from EncryptionPreferenceCounter), we only want to // check if there are keys for this recipients, not (yet) their validity, so // don't show the untrusted encryption key warning in that case + bool canceled = false; if ( !quiet ) - matchingKeys = TrustedOrConfirmed( matchingKeys ); + matchingKeys = trustedOrConfirmed( matchingKeys, address, canceled ); + if ( canceled ) + return std::vector(); if ( quiet || matchingKeys.size() == 1 ) return matchingKeys; // no match until now, or more than one key matches; let the user // choose the key(s) // FIXME: let user get the key from keyserver - return TrustedOrConfirmed( selectKeys( person, + return trustedOrConfirmed( selectKeys( person, matchingKeys.empty() ? i18n("if in your language something like " - "'key(s)' isn't possible please " + "'certificate(s)' isn't possible please " "use the plural in the translation", - "No valid and trusted encryption key was " - "found for \"%1\".\n\n" - "Select the key(s) which should " - "be used for this recipient.").arg(person) + "No valid and trusted encryption certificate was " + "found for \"%1\".

" + "Select the certificate(s) which should " + "be used for this recipient. If there is no suitable certificate in the list " + "you can also search for external certificates by clicking the button: search for external certificates.
") + .arg( TQStyleSheet::escape(person) ) : i18n("if in your language something like " - "'key(s)' isn't possible please " + "'certificate(s)' isn't possible please " "use the plural in the translation", - "More than one key matches \"%1\".\n\n" - "Select the key(s) which should " - "be used for this recipient.").arg(person), - matchingKeys ) ); + "More than one certificate matches \"%1\".\n\n" + "Select the certificate(s) which should " + "be used for this recipient.").arg( TQStyleSheet::escape(person) ), + matchingKeys ), address, canceled ); + // we can ignore 'canceled' here, since trustedOrConfirmed() returns + // an empty vector when canceled == true, and we'd just do the same } @@ -1513,8 +1743,11 @@ void Kleo::KeyResolver::addKeys( const std::vector & items ) { SplitInfo si( it->address ); CryptoMessageFormat f = AutoFormat; for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) { - if ( concreteCryptoMessageFormats[i] & it->format ) { - f = concreteCryptoMessageFormats[i]; + const CryptoMessageFormat fmt = concreteCryptoMessageFormats[i]; + if ( ( fmt & it->format ) && + kdtools::any( it->keys.begin(), it->keys.end(), IsForFormat( fmt ) ) ) + { + f = fmt; break; } } diff --git a/kmail/khtmlparthtmlwriter.cpp b/kmail/khtmlparthtmlwriter.cpp index dfc9fd35..2d29d3c9 100644 --- a/kmail/khtmlparthtmlwriter.cpp +++ b/kmail/khtmlparthtmlwriter.cpp @@ -49,7 +49,7 @@ namespace KMail { KHtmlPartHtmlWriter::KHtmlPartHtmlWriter( KHTMLPart * part, TQObject * parent, const char * name ) : TQObject( parent, name ), HtmlWriter(), - mHtmlPart( part ), mState( Ended ), mHtmlTimer( 0, "mHtmlTimer" ) + mHtmlPart( part ), mHtmlTimer( 0, "mHtmlTimer" ), mState( Ended ) { assert( part ); connect( &mHtmlTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotWriteNextHtmlChunk()) ); diff --git a/kmail/kleo_util.h b/kmail/kleo_util.h index b2378e9f..dc0eb6d2 100644 --- a/kmail/kleo_util.h +++ b/kmail/kleo_util.h @@ -77,5 +77,12 @@ static inline bool isOpenPGP( Kleo::CryptoMessageFormat f ) { return f == Kleo::InlineOpenPGPFormat || f == Kleo::OpenPGPMIMEFormat ; } +static inline bool containsSMIME( unsigned int f ) { + return f & (Kleo::SMIMEFormat|Kleo::SMIMEOpaqueFormat) ; +} + +static inline bool containsOpenPGP( unsigned int f ) { + return f & (Kleo::OpenPGPMIMEFormat|Kleo::InlineOpenPGPFormat) ; +} #endif // __KDEPIM_KMAIL_KLEO_UTIL_H__ diff --git a/kmail/kmacctcachedimap.cpp b/kmail/kmacctcachedimap.cpp index 2305b080..b673b5e1 100644 --- a/kmail/kmacctcachedimap.cpp +++ b/kmail/kmacctcachedimap.cpp @@ -179,21 +179,6 @@ void KMAcctCachedImap::cancelMailCheck() } } -//----------------------------------------------------------------------------- -void KMAcctCachedImap::killJobsForItem(KMFolderTreeItem * fti) -{ - TQMap::Iterator it = mapJobData.begin(); - while (it != mapJobData.end()) - { - if (it.data().parent == fti->folder()) - { - killAllJobs(); - break; - } - else ++it; - } -} - // Reimplemented from ImapAccountBase because we only check one folder at a time void KMAcctCachedImap::slotCheckQueuedFolders() { @@ -217,7 +202,12 @@ void KMAcctCachedImap::processNewMail( bool /*interactive*/ ) else { KMFolder* f = mMailCheckFolders.front(); mMailCheckFolders.pop_front(); - processNewMail( static_cast( f->storage() ), false ); + + // Only check mail if the folder really exists, it might have been removed by the sync in + // the meantime. + if ( f ) { + processNewMail( static_cast( f->storage() ), !checkingSingleFolder() ); + } } } @@ -234,7 +224,7 @@ void KMAcctCachedImap::processNewMail( KMFolderCachedImap* folder, mNoopTimer.stop(); // reset namespace todo - if ( folder == mFolder ) { + if ( folder == mFolder && !namespaces().isEmpty() ) { TQStringList nsToList = namespaces()[PersonalNS]; TQStringList otherNSToCheck = namespaces()[OtherUsersNS]; otherNSToCheck += namespaces()[SharedNS]; @@ -361,8 +351,8 @@ void KMAcctCachedImap::invalidateIMAPFolders( KMFolderCachedImap* folder ) TQStringList strList; TQValueList > folderList; kmkernel->dimapFolderMgr()->createFolderList( &strList, &folderList, - folder->folder()->child(), TQString::null, - false ); + folder->folder()->child(), TQString::null, + false ); TQValueList >::Iterator it; mCountLastUnread = 0; mUnreadBeforeCheck.clear(); @@ -374,13 +364,12 @@ void KMAcctCachedImap::invalidateIMAPFolders( KMFolderCachedImap* folder ) // This invalidates the folder completely cfolder->setUidValidity("INVALID"); cfolder->writeUidCache(); - processNewMailSingleFolder( f ); } } folder->setUidValidity("INVALID"); folder->writeUidCache(); - processNewMailSingleFolder( folder->folder() ); + processNewMailInFolder( folder->folder(), Recursive ); } //----------------------------------------------------------------------------- @@ -462,7 +451,7 @@ void KMAcctCachedImap::slotProgressItemCanceled( ProgressItem* ) } } -FolderStorage* const KMAcctCachedImap::rootFolder() const +FolderStorage* KMAcctCachedImap::rootFolder() const { return mFolder; } diff --git a/kmail/kmacctcachedimap.h b/kmail/kmacctcachedimap.h index e8e42820..72ed5c7d 100644 --- a/kmail/kmacctcachedimap.h +++ b/kmail/kmacctcachedimap.h @@ -75,11 +75,6 @@ public: virtual TQString type() const; virtual void processNewMail( bool interactive ); - /** - * Kill all jobs related the the specified folder - */ - void killJobsForItem(KMFolderTreeItem * fti); - /** * Kill the slave if any jobs are active */ @@ -179,7 +174,7 @@ public: /** * Returns the root folder of this account */ - virtual FolderStorage* const rootFolder() const; + virtual FolderStorage* rootFolder() const; /** return if the account passed the annotation test */ bool annotationCheckPassed(){ return mAnnotationCheckPassed;}; diff --git a/kmail/kmacctimap.cpp b/kmail/kmacctimap.cpp index bf96c79a..4ead745c 100644 --- a/kmail/kmacctimap.cpp +++ b/kmail/kmacctimap.cpp @@ -547,7 +547,7 @@ void KMAcctImap::slotMailCheckCanceled() } //----------------------------------------------------------------------------- -FolderStorage* const KMAcctImap::rootFolder() const +FolderStorage* KMAcctImap::rootFolder() const { return mFolder; } diff --git a/kmail/kmacctimap.h b/kmail/kmacctimap.h index c69bca89..fac17eeb 100644 --- a/kmail/kmacctimap.h +++ b/kmail/kmacctimap.h @@ -87,7 +87,7 @@ public: /** * Returns the root folder of this account */ - virtual FolderStorage* const rootFolder() const; + virtual FolderStorage* rootFolder() const; /** * Queues a message for automatic filtering diff --git a/kmail/kmacctlocal.cpp b/kmail/kmacctlocal.cpp index b486b537..d1e15f83 100644 --- a/kmail/kmacctlocal.cpp +++ b/kmail/kmacctlocal.cpp @@ -220,6 +220,7 @@ bool KMAcctLocal::fetchMsg() msg->setSignatureStateChar( msg->headerField( "X-KMail-SignatureState" ).at(0)); msg->setComplete(true); msg->updateAttachmentState(); + msg->updateInvitationState(); mAddedOk = processNewMsg(msg); diff --git a/kmail/kmaddrbook.cpp b/kmail/kmaddrbook.cpp index b115f54e..055b29fc 100644 --- a/kmail/kmaddrbook.cpp +++ b/kmail/kmaddrbook.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/kmail/kmail.kcfg b/kmail/kmail.kcfg index afb228cf..4192d234 100644 --- a/kmail/kmail.kcfg +++ b/kmail/kmail.kcfg @@ -24,6 +24,8 @@ + + SelectLastSelected @@ -153,7 +155,7 @@ - 85 + 80 @@ -184,6 +186,12 @@ false + + + When replying to invitations, send the reply comment in way that Microsoft Outlook understands. + false + + When this is checked, you will not see the mail composer window. Instead, all invitation mails are sent automatically. If you want to see the mail before sending it, you can uncheck this option. However, be aware that the text in the composer window is in iCalendar syntax, and you should not try modifying it by hand. @@ -200,15 +208,25 @@ AskForAllButAcceptance - + When this is checked, received invitation emails that have been replied to will be moved to the Trash folder, once the reply has been successfully sent. true - - + + + true + + + + + KMail::ObjectTreeParser::defaultToltecReplacementText() + + + + @@ -253,14 +271,12 @@ 0 - - true - - false + + true @@ -293,10 +309,6 @@ This option enables or disables the search line edit above the message list which can be used to quickly search the information shown in the message list. true - - - false - true @@ -333,6 +345,11 @@ Remember this mail transport, so that it will be used in future composer windows as well. false + + Remember this dictionary, so that it will be used in future composer windows as well. + + false + true @@ -347,8 +364,21 @@ 30 255 + + + true + If the number of recipients is larger than this value, KMail will warn and ask for a confirmation before sending the mail. The warning can be turned off. + + + + 5 + 1 + 100 + If the number of recipients is larger than this value, KMail will warn and ask for a confirmation before sending the mail. The warning can be turned off. + + @@ -436,6 +466,14 @@ true + + + true + + + + true + @@ -515,6 +553,10 @@ + + + false + true diff --git a/kmail/kmailIface.h b/kmail/kmailIface.h index fd47f048..2338a4c4 100644 --- a/kmail/kmailIface.h +++ b/kmail/kmailIface.h @@ -60,6 +60,19 @@ k_dcop: const TQString &attachParamValue, const TQCString &attachContDisp, const TQCString &attachCharset) = 0; + virtual int openComposer (const TQString &to, const TQString &cc, + const TQString &bcc, const TQString &subject, + const TQString &body, int hidden, + const TQString &attachName, + const TQCString &attachCte, + const TQCString &attachData, + const TQCString &attachType, + const TQCString &attachSubType, + const TQCString &attachParamAttr, + const TQString &attachParamValue, + const TQCString &attachContDisp, + const TQCString &attachCharset, + uint identity) = 0; /** Open composer and return reference to DCOP interface of composer window. If hidden is true, the window will not be shown. If you use that option, it's your responsibility to call the send() function of the composer in @@ -102,6 +115,7 @@ k_dcop: virtual int dcopAddMessage(const TQString & foldername, const KURL & messagefile, const TQString & MsgStatusFlags = TQString()) = 0; + virtual void showImportArchiveDialog() = 0; virtual TQStringList folderList() const =0; virtual DCOPRef getFolder( const TQString& vpath ) =0; @@ -211,7 +225,7 @@ k_dcop_hidden: /** Clears the list of added message ids which is used to filter out duplicates. */ virtual void dcopResetAddMessage() = 0; - + virtual void loadProfile( const TQString& path ) = 0; virtual void saveToProfile( const TQString& path ) const = 0; }; diff --git a/kmail/kmail_config_accounts.desktop b/kmail/kmail_config_accounts.desktop index 4c2c9f92..c2e58a03 100644 --- a/kmail/kmail_config_accounts.desktop +++ b/kmail/kmail_config_accounts.desktop @@ -39,7 +39,6 @@ Name[hu]=Fiókok Name[is]=Tengingar Name[it]=Account Name[ja]=アカウント -Name[ka]=ანგარიშები Name[kk]=Тіркелгілері Name[km]=គណនី Name[lt]=Paskyros @@ -64,8 +63,7 @@ Name[ta]=கணக்குகள் Name[tg]=Қайдҳои баҳисобгирӣ Name[tr]=Hesaplar Name[uk]=Рахунки -Name[uz]=Hisoblar -Name[uz@cyrillic]=Ҳисоблар +Name[uz]=Ҳисоблар Name[zh_CN]=账户 Name[zh_TW]=帳號 Comment=Setup for Sending and Receiving Messages @@ -91,7 +89,6 @@ Comment[hu]=Küldési és fogadási beállítások Comment[is]=Uppsetning fyrir sendingu og móttöku af tölvupósti Comment[it]=Impostazioni per spedire e ricevere messaggi Comment[ja]=メッセージを送受信するための設定 -Comment[ka]=შეტყობინებების გაგზავნისა და მიღების კონფიგურაცია Comment[kk]=Хаттарды жіберу мен қабылдауды баптау Comment[km]=រៀបចំ​ដើម្បី​ផ្ញើ និង​ទទួល​សារ Comment[lt]=Laiškų siuntimo ir gavimo sąranka @@ -139,7 +136,6 @@ Keywords[he]=kmail,accounts,דוא"ל, חשבון, חשבונות Keywords[hu]=kmail,azonosítók Keywords[is]=kmail,accounts,tengingar Keywords[it]=kmail,account -Keywords[ka]=kmail,ანგარიშები Keywords[km]=kmail,គណនី Keywords[lt]=kmail,accounts,paskyros Keywords[mk]=kmail,accounts,кпошта,сметка,сметки diff --git a/kmail/kmail_config_appearance.desktop b/kmail/kmail_config_appearance.desktop index 09df7ea4..3008ea54 100644 --- a/kmail/kmail_config_appearance.desktop +++ b/kmail/kmail_config_appearance.desktop @@ -39,7 +39,6 @@ Name[hu]=Megjelenés Name[is]=Útlit Name[it]=Aspetto Name[ja]=外観 -Name[ka]=იერსახე Name[kk]=Сыртқы көрінісі Name[km]=រូបរាង Name[ko]=모양 @@ -66,8 +65,7 @@ Name[ta]=தோற்றம் Name[tg]=Намуди зоҳирӣ Name[tr]=Görünüm Name[uk]=Вигляд -Name[uz]=Koʻrinishi -Name[uz@cyrillic]=Кўриниши +Name[uz]=Кўриниши Name[zh_CN]=外观 Comment=Customize Visual Appearance Comment[af]=Pasmaak die visuele voorkoms @@ -93,7 +91,6 @@ 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ą @@ -143,7 +140,6 @@ Keywords[hu]=kmail,megjelenés Keywords[is]=kmail,útlit Keywords[it]=kmail,aspetto Keywords[ja]=kmail,外観 -Keywords[ka]=kmail,იერსახე Keywords[km]=kmail,រូបរាង Keywords[lt]=kmail,appearance,išvaizda Keywords[mk]=kmail,appearance,кпошта,појава,изглед,визуелно diff --git a/kmail/kmail_config_composer.desktop b/kmail/kmail_config_composer.desktop index 5c75a1c4..4cc868bb 100644 --- a/kmail/kmail_config_composer.desktop +++ b/kmail/kmail_config_composer.desktop @@ -35,7 +35,6 @@ Name[he]=עורך Name[hu]=Szerkesztő Name[it]=Compositore Name[ja]=メール作成 -Name[ka]=წერილების რედაქტორი Name[kk]=Құрастарғыш Name[km]=កម្មវិធី​តែង Name[lt]=Redaktorius @@ -80,7 +79,6 @@ Comment[fr]=Modèles et comportement général Comment[fy]=Sjabloanen en algemien gedrach Comment[gl]=Planteis e Comportamento Xeral Comment[hu]=Sablonok, általános működési jellemzők -Comment[is]=Forsnið & almenn hegðun Comment[it]=Modelli e comportamento generale Comment[ja]=テンプレートと全般的な動作 Comment[kk]=Үлгілер мен Жалпы тәртібі @@ -124,7 +122,6 @@ Keywords[he]=kmail,composer,כתבן Keywords[hu]=kmail,szerkesztő Keywords[is]=kmail,ritill Keywords[it]=kmail,compositore -Keywords[ka]=kmail,წერილების რედაქტორი Keywords[km]=kmail កម្មវិធី​តែង Keywords[lt]=kmail,composer,redaktorius Keywords[mk]=kmail,composer,кпошта,составувач diff --git a/kmail/kmail_config_identity.desktop b/kmail/kmail_config_identity.desktop index 8cf28eb0..fe985db6 100644 --- a/kmail/kmail_config_identity.desktop +++ b/kmail/kmail_config_identity.desktop @@ -37,7 +37,6 @@ Name[hu]=Azonosítók Name[is]=Auðkenni Name[it]=Identità Name[ja]=個人情報 -Name[ka]=პროფილები Name[kk]=Іс-әлпеттері Name[km]=អត្តសញ្ញាណ Name[lt]=Tapatybės @@ -62,8 +61,7 @@ Name[ta]=அடையாளங்கள் Name[tg]=Профилҳо Name[tr]=Kimlikler Name[uk]=Профілі -Name[uz]=Shaxsiyatlar -Name[uz@cyrillic]=Шахсиятлар +Name[uz]=Шахсиятлар Name[zh_CN]=身份 Name[zh_TW]=身份 Comment=Manage Identities @@ -92,7 +90,6 @@ Comment[hu]=Az azonosítók kezelése Comment[is]=Stjórna auðkennum Comment[it]=Gestisce le identità Comment[ja]=個人情報の管理 -Comment[ka]=პროფილების მართვა Comment[kk]=Іс-әлпеттерді басқару Comment[km]=គ្រប់គ្រង​អត្តសញ្ញាណ Comment[lt]=Tvarkyti tapatybes @@ -143,7 +140,6 @@ Keywords[hu]=kmail,azonosító Keywords[is]=kmail,auðkenni Keywords[it]=kmail,identità Keywords[ja]=kmail,個人情報 -Keywords[ka]=kmail,პროფილი Keywords[km]=kmail,អត្តសញ្ញាណ Keywords[lt]=kmail,identity,tapatybė Keywords[mk]=kmail,identity,кпошта,идентитет,идентитети @@ -165,6 +161,5 @@ Keywords[ta]=கேஅஞ்சல்,அடையாளம் Keywords[tg]=kmail,identity,профил Keywords[tr]=kmail,kimlikler Keywords[uk]=kmail,профіль -Keywords[uz]=kmail,shaxsiyat -Keywords[uz@cyrillic]=kmail,шахсият +Keywords[uz]=kmail,шахсият Keywords[zh_CN]=kmail,identity, 身份 diff --git a/kmail/kmail_config_misc.desktop b/kmail/kmail_config_misc.desktop index 6a33c643..7c1d946f 100644 --- a/kmail/kmail_config_misc.desktop +++ b/kmail/kmail_config_misc.desktop @@ -36,7 +36,6 @@ Name[hu]=Egyéb Name[is]=Ýmislegt Name[it]=Varie Name[ja]=その他 -Name[ka]=სხვადასხვა Name[kk]=Басқалары Name[km]=ផ្សេងៗ Name[lt]=Įvairūs @@ -62,8 +61,7 @@ Name[ta]=இதர Name[tg]=Ғайра Name[tr]=Çeşitli Name[uk]=Різне -Name[uz]=Har xil -Name[uz@cyrillic]=Ҳар хил +Name[uz]=Ҳар хил Name[zh_CN]=杂项 Name[zh_TW]=其他 Comment=Settings that don't fit elsewhere @@ -89,7 +87,6 @@ Comment[hu]=A máshová nem besorolható beállítások Comment[is]=Stillingar sem passa ekki annars staðar Comment[it]=Impostazioni che non rientrano in altre categorie Comment[ja]=その他の設定 -Comment[ka]=პარამეტრები,რომლებიც სხვას არ ერგება Comment[kk]=Басқа параметрлері Comment[km]=ការ​កំណត់​ដែល​មិន​ត្រូវ​នឹង​កន្លែង​ផ្សេង Comment[lt]=Kiti nustatymai @@ -138,7 +135,6 @@ Keywords[hu]=kmail,egyéb Keywords[is]=kmail,ýmislegt Keywords[it]=kmail,varie Keywords[ja]=kmail,その他 -Keywords[ka]=kmail,სხვადასხვა Keywords[km]=kmail,ផ្សេងៗ Keywords[lt]=kmail,misc,įvairūs Keywords[mk]=kmail,misc,кпошта,разно @@ -161,6 +157,5 @@ Keywords[ta]=கேஅஞ்சல், இதர Keywords[tg]=kmail,misc,ғайра,дигар Keywords[tr]=kmail,çeşitli Keywords[uk]=kmail,різне -Keywords[uz]=kmail,har xil -Keywords[uz@cyrillic]=kmail,ҳар хил +Keywords[uz]=kmail,ҳар хил Keywords[zh_CN]=kmail,misc,杂项 diff --git a/kmail/kmail_config_security.desktop b/kmail/kmail_config_security.desktop index 1b61544e..4d1a2a40 100644 --- a/kmail/kmail_config_security.desktop +++ b/kmail/kmail_config_security.desktop @@ -38,7 +38,6 @@ Name[hu]=Biztonság Name[is]=Öryggi Name[it]=Sicurezza Name[ja]=セキュリティ -Name[ka]=უსაფრთხოება Name[kk]=Қауіпсіздік Name[km]=សុវត្ថិភាព Name[lt]=Saugumas @@ -64,8 +63,7 @@ Name[ta]=பாதுகாப்பு Name[tg]=Амният Name[tr]=Güvenlik Name[uk]=Безпека -Name[uz]=Xavfsizlik -Name[uz@cyrillic]=Хавфсизлик +Name[uz]=Хавфсизлик Name[zh_CN]=安全 Name[zh_TW]=安全性 Comment=Security & Privacy Settings @@ -93,7 +91,6 @@ Comment[hu]=Biztonsági és adatvédelmi beállítások Comment[is]=Öryggis & einkalífsstillingar Comment[it]=Impostazioni sicurezza e privacy Comment[ja]=セキュリティ & プライバシーの設定 -Comment[ka]=უსაფრთხოებისა და პირადულობის პარამეტრები Comment[kk]=Қауіпсіздігі пен Дербестік параметрлері Comment[km]=ការ​កំណត់​សុវត្ថិភាព & ភាព​ឯកជន Comment[lt]=Saugumo ir privatumo nustatymai @@ -144,7 +141,6 @@ Keywords[hu]=kmail,biztonság Keywords[is]=kmail,öryggi Keywords[it]=kmail,sicurezza Keywords[ja]=kmail,セキュリティ -Keywords[ka]=kmail,უსაფრთხოება Keywords[km]=kmail,សុវត្ថិភាព Keywords[lt]=kmail,security,saugumas Keywords[mk]=kmail,security,кпошта,безбедност @@ -167,6 +163,5 @@ Keywords[ta]=கேஅஞ்சல்,பாதுகாப்பு Keywords[tg]=kmail,security,амният Keywords[tr]=kmail,güvenlik Keywords[uk]=kmail,безпека -Keywords[uz]=kmail,xavfsizlik -Keywords[uz@cyrillic]=kmail,хавфсизлик +Keywords[uz]=kmail,хавфсизлик Keywords[zh_CN]=kmail,security,安全 diff --git a/kmail/kmail_part.rc b/kmail/kmail_part.rc index 72cf6ec3..fb4794df 100644 --- a/kmail/kmail_part.rc +++ b/kmail/kmail_part.rc @@ -2,7 +2,7 @@ the same menu entries at the same place in KMail and Kontact --> - + &File @@ -100,6 +100,7 @@ + diff --git a/kmail/kmailicalIface.h b/kmail/kmailicalIface.h index d5761103..fc1b7a81 100644 --- a/kmail/kmailicalIface.h +++ b/kmail/kmailicalIface.h @@ -127,10 +127,16 @@ k_dcop: */ virtual bool removeSubresource( const TQString& resource ) = 0; + /** + * Returns the number of dimap folders in the account manager. + */ + virtual int dimapAccounts() = 0; + /** * Causes all resource folders of the given type to be synced with the server. */ virtual bool triggerSync( const TQString & ) = 0; + virtual void changeResourceUIName( const TQString &folderPath, const TQString &newName ) = 0; k_dcop_signals: void incidenceAdded( const TQString& type, const TQString& folder, diff --git a/kmail/kmailicalifaceimpl.cpp b/kmail/kmailicalifaceimpl.cpp index c1f626f8..05524e02 100644 --- a/kmail/kmailicalifaceimpl.cpp +++ b/kmail/kmailicalifaceimpl.cpp @@ -67,6 +67,7 @@ using KMail::AccountManager; #include #include +#include #include #include #include @@ -75,6 +76,8 @@ using KMail::AccountManager; using namespace KMail; +TQMap *KMailICalIfaceImpl::mSubResourceUINamesMap = new TQMap; + // Local helper methods static void vPartMicroParser( const TQString& str, TQString& s ); static void reloadFolderTree(); @@ -548,10 +551,12 @@ TQMap KMailICalIfaceImpl::incidencesKolab( const TQString& m f->open( "incidences" ); + kdDebug(5006) << k_funcinfo << "Getting incidences (" << mimetype << ") for folder " << f->label() + << ", starting with index " << startIndex << ", " << nbMessages << " messages." << endl; + kdDebug(5006) << "The folder has " << f->count() << " messages." << endl; + int stopIndex = nbMessages == -1 ? f->count() : QMIN( f->count(), startIndex + nbMessages ); - kdDebug(5006) << "KMailICalIfaceImpl::incidencesKolab( " << mimetype << ", " - << resource << " ) from " << startIndex << " to " << stopIndex << endl; for(int i = startIndex; i < stopIndex; ++i) { #if 0 @@ -590,6 +595,8 @@ TQMap KMailICalIfaceImpl::incidencesKolab( const TQString& m #else delete msg; #endif + } else { + kdDebug(5006) << k_funcinfo << " Unable to retrieve message " << i << " for incidence!" << endl; } } f->close( "incidences" ); @@ -652,10 +659,20 @@ static int dimapAccountCount() return count; } +int KMailICalIfaceImpl::dimapAccounts() +{ + return dimapAccountCount(); +} + static TQString subresourceLabelForPresentation( const KMFolder * folder ) { + if( KMailICalIfaceImpl::getResourceMap()->contains( folder->location() ) ) { + return folder->label(); + } + TQString label = folder->prettyURL(); TQStringList parts = TQStringList::split( TQString::fromLatin1("/"), label ); + // In the common special case of some other user's folder shared with us // the url looks like "Server Name/user/$USERNAME/Folder/Name". Make // those a bit nicer. @@ -678,9 +695,15 @@ static TQString subresourceLabelForPresentation( const KMFolder * folder ) remainder.pop_front(); remainder.pop_front(); if ( dimapAccountCount() > 1 ) { + // Fix kolab issue 2531 folder->storage() )->account() can be null + if( folder->storage() && static_cast( folder->storage() )->account() ) { label = i18n( "My %1 (%2)") .arg( remainder.join( TQString::fromLatin1("/") ), static_cast( folder->storage() )->account()->name() ); + } else { + label = i18n("My %1") + .arg( remainder.join( TQString::fromLatin1("/") ) ); + } } else { label = i18n("My %1") .arg( remainder.join( TQString::fromLatin1("/") ) ); @@ -700,9 +723,9 @@ TQValueList KMailICalIfaceImpl::subresourcesKol KMFolder* f = folderFromType( contentsType, TQString::null ); if ( f ) { subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ), - !f->isReadOnly(), folderIsAlarmRelevant( f ) ) ); + f->isWritable(), folderIsAlarmRelevant( f ) ) ); kdDebug(5006) << "Adding(1) folder " << f->location() << " " << - ( f->isReadOnly() ? "readonly" : "" ) << endl; + ( !f->isWritable() ? "readonly" : "" ) << endl; } // get the extra ones @@ -712,9 +735,9 @@ TQValueList KMailICalIfaceImpl::subresourcesKol f = it.current()->folder; if ( f && f->storage()->contentsType() == t ) { subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ), - !f->isReadOnly(), folderIsAlarmRelevant( f ) ) ); + f->isWritable(), folderIsAlarmRelevant( f ) ) ); kdDebug(5006) << "Adding(2) folder " << f->location() << " " << - ( f->isReadOnly() ? "readonly" : "" ) << endl; + ( !f->isWritable() ? "readonly" : "" ) << endl; } } @@ -743,7 +766,9 @@ bool KMailICalIfaceImpl::triggerSync( const TQString& contentsType ) imap->getAndCheckFolder(); } else if ( f->folderType() == KMFolderTypeCachedImap ) { KMFolderCachedImap* cached = static_cast( f->storage() ); - cached->account()->processNewMailSingleFolder( f ); + if ( cached->account() ) { + cached->account()->processNewMailInFolder( f ); + } } } return true; @@ -758,7 +783,7 @@ bool KMailICalIfaceImpl::isWritableFolder( const TQString& type, // Definitely not writable return false; - return !f->isReadOnly(); + return f->isWritable(); } /* Used by the resource to query the storage format of the folder. */ @@ -1432,7 +1457,7 @@ void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder* folder, } // Tell about the new resource subresourceAdded( folderContentsType( contentsType ), location, subresourceLabelForPresentation(folder), - !folder->isReadOnly(), folderIsAlarmRelevant( folder ) ); + folder->isWritable(), folderIsAlarmRelevant( folder ) ); } KMFolder* KMailICalIfaceImpl::extraFolder( const TQString& type, @@ -1577,8 +1602,7 @@ void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder* folder ) subresourceDeleted( contentsTypeStr, location ); subresourceAdded( contentsTypeStr, location, subresourceLabelForPresentation( folder ), - !folder->isReadOnly(), folderIsAlarmRelevant( folder ) ); - + folder->isWritable(), folderIsAlarmRelevant( folder ) ); } } @@ -1629,6 +1653,33 @@ KMFolder* KMailICalIfaceImpl::findResourceFolder( const TQString& resource ) return 0; } +void KMailICalIfaceImpl::changeResourceUIName( const TQString &folderPath, const TQString &newName ) +{ + kdDebug() << "Folder path " << folderPath << endl; + KMFolder *f = findResourceFolder( folderPath ); + if ( f ) { + KMailICalIfaceImpl::getResourceMap()->insert( folderPath, newName ); + kmkernel->folderMgr()->renameFolder( f, newName ); + KConfigGroup configGroup( kmkernel->config(), "Resource UINames" ); + configGroup.writeEntry( folderPath, newName ); + } +} + +// Builds a folder list from the dimap and the local folder list. +static void createFolderList( TQStringList &folderNames, TQValueList > &folderList ) +{ + TQStringList dimapFolderNames; + TQStringList localFolderNames; + TQValueList > dimapFolderList; + TQValueList > localFolderList; + kmkernel->dimapFolderMgr()->createFolderList( &dimapFolderNames, &dimapFolderList ); + kmkernel->folderMgr()->createFolderList( &localFolderNames, &localFolderList ); + folderNames += dimapFolderNames; + folderNames += localFolderNames; + folderList += dimapFolderList; + folderList += localFolderList; +} + /**************************** * The config stuff */ @@ -1790,22 +1841,22 @@ void KMailICalIfaceImpl::readConfig() if ( mNotes->folderType() == KMFolderTypeCachedImap ) static_cast( mNotes->storage() )->updateAnnotationFolderType(); - // BEGIN TILL TODO The below only uses the dimap folder manager, which - // will fail for all other folder types. Adjust. - - kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl; - kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl; - kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl; + //kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl; + //kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl; + //kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl; // Find all extra folders TQStringList folderNames; TQValueList > folderList; - kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList); - for(TQValueList >::iterator it = folderList.begin(); - it != folderList.end(); ++it) + createFolderList( folderNames, folderList ); + for( TQValueList >::iterator it = folderList.begin(); + it != folderList.end(); ++it ) { - FolderStorage* storage = (*it)->storage(); - if ( storage->contentsType() != 0 ) { + FolderStorage *storage = (*it)->storage(); + KMFolderCachedImap* dimapStorage = dynamic_cast( storage ); + if ( storage && storage->contentsType() != 0 ) { + if ( dimapStorage ) + dimapStorage->updateAnnotationFolderType(); folderContentsTypeChanged( *it, storage->contentsType() ); } } @@ -1818,8 +1869,6 @@ void KMailICalIfaceImpl::readConfig() mExtraFolders.remove( mContacts->location() ); mExtraFolders.remove( mNotes->location() ); - // END TILL TODO - subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true ); subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true ); subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location(), mJournals->label(), true, false ); @@ -1901,6 +1950,10 @@ void KMailICalIfaceImpl::readConfig() subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false ); } + KConfig *config = kmkernel->config(); + config->setGroup("Resource UINames"); + *KMailICalIfaceImpl::mSubResourceUINamesMap = config->entryMap( "Resource UINames" ); + reloadFolderTree(); } @@ -1930,6 +1983,19 @@ KMFolder* KMailICalIfaceImpl::initFolder( KMail::FolderContentsType contentsType // Find the folder StandardFolderSearchResult result = findStandardResourceFolder( mFolderParentDir, contentsType ); + + // deal with multiple default groupware folders + if ( result.folders.count() > 1 && result.found == StandardFolderSearchResult::FoundAndStandard ) { + TQStringList labels; + for ( TQValueList::ConstIterator it = result.folders.begin(); it != result.folders.end(); ++it ) + labels << (*it)->prettyURL(); + const TQString selected = KInputDialog::getItem( i18n("Default folder"), + i18n("There are multiple %1 default folders, please choose one:") + .arg( localizedDefaultFolderName( contentsType ) ), labels ); + if ( !selected.isEmpty() ) + result.folder = result.folders[ labels.findIndex( selected ) ]; + } + KMFolder* folder = result.folder; if ( !folder ) { @@ -2125,21 +2191,22 @@ static void vPartMicroParser( const TQString& str, TQString& s ) } // Returns the first child folder having the given annotation -static KMFolder* findFolderByAnnotation( KMFolderDir* folderParentDir, const TQString& annotation ) -{ - TQPtrListIterator it( *folderParentDir ); - for ( ; it.current(); ++it ) { - if ( !it.current()->isDir() ) { - KMFolder* folder = static_cast( it.current() ); - if ( folder->folderType() == KMFolderTypeCachedImap ) { - TQString folderAnnotation = static_cast( folder->storage() )->annotationFolderType(); - //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl; - if ( folderAnnotation == annotation ) - return folder; - } +static TQValueList findFolderByAnnotation( KMFolderDir* folderParentDir, const TQString& annotation ) +{ + TQValueList rv; + TQPtrListIterator it( *folderParentDir ); + for ( ; it.current(); ++it ) { + if ( !it.current()->isDir() ) { + KMFolder* folder = static_cast( it.current() ); + if ( folder->folderType() == KMFolderTypeCachedImap ) { + TQString folderAnnotation = static_cast( folder->storage() )->annotationFolderType(); + //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl; + if ( folderAnnotation == annotation ) + rv.append( folder ); } } - return 0; + } + return rv; } KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir* folderParentDir, KMail::FolderContentsType contentsType ) @@ -2147,14 +2214,14 @@ KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardR if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ) { // Look for a folder with an annotation like "event.default" - KMFolder* folder = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) + ".default" ); - if ( folder ) - return StandardFolderSearchResult( folder, StandardFolderSearchResult::FoundAndStandard ); + TQValueList folders = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) + ".default" ); + if ( !folders.isEmpty() ) + return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundAndStandard ); // Fallback: look for a folder with an annotation like "event" - folder = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) ); - if ( folder ) - return StandardFolderSearchResult( folder, StandardFolderSearchResult::FoundByType ); + folders = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) ); + if ( !folders.isEmpty() ) + return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundByType ); // Fallback: look for the folder by name (we'll need to change its type) KMFolderNode* node = folderParentDir->hasNamedFolder( localizedDefaultFolderName( contentsType ) ); @@ -2188,12 +2255,14 @@ bool KMailICalIfaceImpl::folderIsAlarmRelevant( const KMFolder *folder ) if ( folder->folderType() == KMFolderTypeImap ) { const KMFolderImap *imapFolder = static_cast( folder->storage() ); administerRights = - imapFolder->userRights() <= 0 || imapFolder->userRights() & KMail::ACLJobs::Administer; + imapFolder->userRightsState() != KMail::ACLJobs::Ok || + imapFolder->userRights() & KMail::ACLJobs::Administer; } if ( folder->folderType() == KMFolderTypeCachedImap ) { const KMFolderCachedImap *dimapFolder = static_cast( folder->storage() ); administerRights = - dimapFolder->userRights() <= 0 || dimapFolder->userRights() & KMail::ACLJobs::Administer; + dimapFolder->userRightsState() != KMail::ACLJobs::Ok || + dimapFolder->userRights() & KMail::ACLJobs::Administer; relevantForOwner = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor () == KMFolderCachedImap::IncForAdmins ); relevantForEveryone = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor() == KMFolderCachedImap::IncForReaders ); } @@ -2227,6 +2296,12 @@ bool KMailICalIfaceImpl::addSubresource( const TQString& resource, KMFolderDir *parentFolderDir = !parent.isEmpty() && folder ? folder->createChildFolder(): mFolderParentDir; if ( !parentFolderDir || parentFolderDir->hasNamedFolder( resource ) ) return false; + TQString msg; + if ( parentFolderDir->owner() && !parentFolderDir->owner()->isValidName( resource, msg ) ) { + KMessageBox::error( 0, msg ); + return false; + } + KMFolderType type = mFolderType; if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir; @@ -2291,7 +2366,7 @@ void KMailICalIfaceImpl::syncFolder(KMFolder * folder) const else return; } - dimapFolder->account()->processNewMailSingleFolder( folder ); + dimapFolder->account()->processNewMailInFolder( folder ); } #include "kmailicalifaceimpl.moc" diff --git a/kmail/kmailicalifaceimpl.h b/kmail/kmailicalifaceimpl.h index 5a92ffe9..c0308f92 100644 --- a/kmail/kmailicalifaceimpl.h +++ b/kmail/kmailicalifaceimpl.h @@ -113,6 +113,7 @@ public: const TQString& resource, int startIndex, int nbMessages ); + int dimapAccounts(); TQValueList subresourcesKolab( const TQString& contentsType ); @@ -223,6 +224,8 @@ public: bool isResourceQuiet() const; void setResourceQuiet(bool q); + static TQMap* getResourceMap() { return mSubResourceUINamesMap; } + public slots: /* (Re-)Read configuration file */ void readConfig(); @@ -235,6 +238,7 @@ public slots: // Called when a folder is made readonly or readwrite, or renamed, // or any other similar change that affects the resources void slotFolderPropertiesChanged( KMFolder* folder ); + void changeResourceUIName( const TQString &folderPath, const TQString &newName ); private slots: void slotRefreshFolder( KMFolder* ); @@ -259,7 +263,10 @@ private: enum FoundEnum { FoundAndStandard, NotFound, FoundByType, FoundByName }; StandardFolderSearchResult() : folder( 0 ) {} StandardFolderSearchResult( KMFolder* f, FoundEnum e ) : folder( f ), found( e ) {} + StandardFolderSearchResult( const TQValueList &f, FoundEnum e ) : + folder( f.first() ), folders( f ), found( e ) {} KMFolder* folder; // NotFound implies folder==0 of course. + TQValueList folders; // in case we found multiple default folders (which should not happen) FoundEnum found; }; @@ -341,6 +348,7 @@ private: TQMap mTheUnGetMes; TQMap mPendingUpdates; TQMap mInTransit; + static TQMap *mSubResourceUINamesMap; }; diff --git a/kmail/kmcommands.cpp b/kmail/kmcommands.cpp index e0b911df..7bf978b0 100644 --- a/kmail/kmcommands.cpp +++ b/kmail/kmcommands.cpp @@ -101,6 +101,7 @@ using KMail::ActionScheduler; #include "kcursorsaver.h" #include "partNode.h" #include "objecttreeparser.h" +#include "csshelper.h" using KMail::ObjectTreeParser; using KMail::FolderJob; #include "chiasmuskeyselector.h" @@ -243,13 +244,16 @@ void KMCommand::slotStart() return; } - for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next()) - if (!mb->parent()) { - emit messagesTransfered( Failed ); - return; - } else { - keepFolderOpen( mb->parent() ); + for ( KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next() ) { + if ( mb ) { + if ( !mb->parent() ) { + emit messagesTransfered( Failed ); + return; + } else { + keepFolderOpen( mb->parent() ); + } } + } // transfer the selected messages first transferSelectedMsgs(); @@ -446,7 +450,7 @@ void KMCommand::slotTransferCancelled() void KMCommand::keepFolderOpen( KMFolder *folder ) { - folder->open("kmcommand"); + folder->open( "kmcommand" ); mFolders.append( folder ); } @@ -757,10 +761,25 @@ KMCommand::Result KMShowMsgSrcCommand::execute() return OK; } -static KURL subjectToUrl( const TQString & subject ) { - return KFileDialog::getSaveURL( subject.stripWhiteSpace() - .replace( TQDir::separator(), '_' ), - "*.mbox" ); +static KURL subjectToUrl( const TQString & subject ) +{ + // We need to replace colons with underscores since those cause problems with KFileDialog (bug + // in KFileDialog though) and also on Windows filesystems. + // We also look at the special case of ": ", since converting that to "_ " would look strange, + // simply "_" looks better. + // We also don't allow filenames starting with a dot, since then the file is hidden and the poor + // user can't find it anymore. + // Don't allow filenames starting with a tilde either, since that will cause the file dialog to + // discard the filename entirely. + // https://issues.kolab.org/issue3805 + const TQString filter = i18n( "*.mbox|email messages (*.mbox)\n*|all files (*)" ); + TQString cleanSubject = subject.stripWhiteSpace() + .replace( TQDir::separator(), '_' ) + .replace( ": ", "_" ) + .replace( ':', '_' ) + .replace( '.', '_' ) + .replace( '~', '_' ); + return KFileDialog::getSaveURL( cleanSubject, filter ); } KMSaveMsgCommand::KMSaveMsgCommand( TQWidget *parent, KMMessage * msg ) @@ -857,16 +876,23 @@ void KMSaveMsgCommand::slotSaveDataReq() assert( p ); assert( idx >= 0 ); //kdDebug() << "SERNUM: " << mMsgList[mMsgListIndex] << " idx: " << idx << " folder: " << p->prettyURL() << endl; + + const bool alreadyGot = p->isMessage( idx ); + msg = p->getMsg(idx); if ( msg ) { + // Only unGet the message if it isn't already got. + if ( !alreadyGot ) { + mUngetMsgs.append( msg ); + } if ( msg->transferInProgress() ) { TQByteArray data = TQByteArray(); mJob->sendAsyncData( data ); } msg->setTransferInProgress( true ); - if (msg->isComplete() ) { - slotMessageRetrievedForSaving( msg ); + if ( msg->isComplete() ) { + slotMessageRetrievedForSaving( msg ); } else { // retrieve Message first if ( msg->parent() && !msg->isComplete() ) { @@ -918,7 +944,8 @@ void KMSaveMsgCommand::slotMessageRetrievedForSaving(KMMessage *msg) } ++mMsgListIndex; // Get rid of the message. - if ( msg && msg->parent() && msg->getMsgSerNum() ) { + if ( msg && msg->parent() && msg->getMsgSerNum() && + mUngetMsgs.contains( msg ) ) { int idx = -1; KMFolder * p = 0; KMMsgDict::instance()->getLocation( msg, &p, &idx ); @@ -1229,9 +1256,9 @@ KMCommand::Result KMForwardInlineCommand::execute() // fwdMsg->setBody( msgText ); for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) { - TemplateParser parser( fwdMsg, TemplateParser::Forward, - msg->body(), false, false, false, false); - parser.process( msg, 0, true ); + TemplateParser parser( fwdMsg, TemplateParser::Forward ); + parser.setSelection( msg->body() ); // FIXME: Why is this needed? + parser.process( msg, 0, true ); fwdMsg->link( msg, KMMsgStatusForwarded ); } @@ -1256,7 +1283,6 @@ KMCommand::Result KMForwardInlineCommand::execute() { KMail::Composer * win = KMail::makeComposer( fwdMsg, id ); win->setCharset( fwdMsg->codec()->mimeName(), true ); - win->setBody( fwdMsg->bodyToUnicode() ); win->show(); } } @@ -1459,7 +1485,7 @@ KMCommand::Result KMCustomReplyToCommand::execute() return Failed; } KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection, - false, true, false, mTemplate ); + false, true, mTemplate ); KMail::Composer * win = KMail::makeComposer( reply ); win->setCharset( msg->codec()->mimeName(), true ); win->setReplyFocus(); @@ -1484,7 +1510,7 @@ KMCommand::Result KMCustomReplyAllToCommand::execute() return Failed; } KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection, - false, true, false, mTemplate ); + false, true, mTemplate ); KMail::Composer * win = KMail::makeComposer( reply ); win->setCharset( msg->codec()->mimeName(), true ); win->setReplyFocus(); @@ -1533,9 +1559,9 @@ KMCommand::Result KMCustomForwardCommand::execute() // fwdMsg->setBody( msgText ); for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) { - TemplateParser parser( fwdMsg, TemplateParser::Forward, - msg->body(), false, false, false, false); - parser.process( msg, 0, true ); + TemplateParser parser( fwdMsg, TemplateParser::Forward ); + parser.setSelection( msg->body() ); // FIXME: Why is this needed? + parser.process( msg, 0, true ); fwdMsg->link( msg, KMMsgStatusForwarded ); } @@ -1567,14 +1593,24 @@ KMCommand::Result KMCustomForwardCommand::execute() } -KMPrintCommand::KMPrintCommand( TQWidget *parent, - KMMessage *msg, bool htmlOverride, bool htmlLoadExtOverride, - bool useFixedFont, const TQString & encoding ) - : KMCommand( parent, msg ), mHtmlOverride( htmlOverride ), +KMPrintCommand::KMPrintCommand( TQWidget *parent, KMMessage *msg, + const KMail::HeaderStyle *headerStyle, + const KMail::HeaderStrategy *headerStrategy, + bool htmlOverride, bool htmlLoadExtOverride, + bool useFixedFont, const TQString & encoding ) + : KMCommand( parent, msg ), + mHeaderStyle( headerStyle ), mHeaderStrategy( headerStrategy ), + mHtmlOverride( htmlOverride ), mHtmlLoadExtOverride( htmlLoadExtOverride ), mUseFixedFont( useFixedFont ), mEncoding( encoding ) { - mOverrideFont = KGlobalSettings::generalFont(); + if ( GlobalSettings::useDefaultFonts() ) + mOverrideFont = KGlobalSettings::generalFont(); + else { + KConfigGroup fonts( KMKernel::config(), "Fonts" ); + TQString tmp = fonts.readEntry( "print-font", KGlobalSettings::generalFont().toString() ); + mOverrideFont.fromString( tmp ); + } } @@ -1588,11 +1624,13 @@ KMCommand::Result KMPrintCommand::execute() KMReaderWin printWin( 0, 0, 0 ); printWin.setPrinting( true ); printWin.readConfig(); + if ( mHeaderStyle != 0 && mHeaderStrategy != 0 ) + printWin.setHeaderStyleAndStrategy( mHeaderStyle, mHeaderStrategy ); printWin.setHtmlOverride( mHtmlOverride ); printWin.setHtmlLoadExtOverride( mHtmlLoadExtOverride ); printWin.setUseFixedFont( mUseFixedFont ); printWin.setOverrideEncoding( mEncoding ); - printWin.setPrintFont( mOverrideFont ); + printWin.cssHelper()->setPrintFont( mOverrideFont ); printWin.setDecryptMessageOverwrite( true ); printWin.setMsg( retrievedMessage(), true ); printWin.printMsg(); @@ -2149,14 +2187,21 @@ KMCommand::Result KMMoveCommand::execute() mProgressItem->setTotalItems( mSerNumList.count() ); for ( TQValueList::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) { - KMFolder *srcFolder; + if ( *it == 0 ) { + kdDebug(5006) << k_funcinfo << "serial number == 0!" << endl; + continue; // invalid message + } + KMFolder *srcFolder = 0; int idx = -1; KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx ); if (srcFolder == mDestFolder) continue; + assert(srcFolder); assert(idx != -1); - srcFolder->open( "kmmovecommand" ); - mOpenedFolders.append( srcFolder ); + if ( !srcFolder->isOpened() ) { + srcFolder->open( "kmmovecommand" ); + mOpenedFolders.append( srcFolder ); + } msg = srcFolder->getMsg(idx); if ( !msg ) { kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl; @@ -2328,6 +2373,11 @@ KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg ) KMDeleteMsgCommand::KMDeleteMsgCommand( Q_UINT32 sernum ) :KMMoveCommand( sernum ) { + if ( !sernum ) { + setDestFolder( 0 ); + return; + } + KMFolder *srcFolder = 0; int idx; KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx ); @@ -3146,7 +3196,7 @@ void KMHandleAttachmentCommand::atmSave() parts.append( mNode ); // save, do not leave encoded KMSaveAttachmentsCommand *command = - new KMSaveAttachmentsCommand( 0, parts, mMsg, false ); + new KMSaveAttachmentsCommand( parentWidget(), parts, mMsg, false ); command->start(); } @@ -3306,6 +3356,13 @@ AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * ms { } +AttachmentModifyCommand::AttachmentModifyCommand( int nodeId, KMMessage *msg, TQWidget *parent ) + : KMCommand( parent, msg ), + mPartIndex( nodeId ), + mSernum( 0 ) +{ +} + AttachmentModifyCommand::~ AttachmentModifyCommand() { } @@ -3370,31 +3427,14 @@ void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd) deleteLater(); } -DwBodyPart * AttachmentModifyCommand::findPart(KMMessage* msg, int index) -{ - int accu = 0; - return findPartInternal( msg->getTopLevelPart(), index, accu ); -} - -DwBodyPart * AttachmentModifyCommand::findPartInternal(DwEntity * root, int index, int & accu) +KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) : + AttachmentModifyCommand( node, msg, parent ) { - accu++; - if ( index < accu ) // should not happen - return 0; - DwBodyPart *current = dynamic_cast( root ); - if ( index == accu ) - return current; - DwBodyPart *rv = 0; - if ( root->Body().FirstBodyPart() ) - rv = findPartInternal( root->Body().FirstBodyPart(), index, accu ); - if ( !rv && current && current->Next() ) - rv = findPartInternal( current->Next(), index, accu ); - return rv; + kdDebug(5006) << k_funcinfo << endl; } - -KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) : - AttachmentModifyCommand( node, msg, parent ) +KMDeleteAttachmentCommand::KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent ) + : AttachmentModifyCommand( nodeId, msg, parent ) { kdDebug(5006) << k_funcinfo << endl; } @@ -3407,37 +3447,8 @@ KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand() KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify() { KMMessage *msg = retrievedMessage(); - KMMessagePart part; - DwBodyPart *dwpart = findPart( msg, mPartIndex ); - if ( !dwpart ) + if ( !msg || !msg->deleteBodyPart( mPartIndex ) ) return Failed; - KMMessage::bodyPart( dwpart, &part, true ); - if ( !part.isComplete() ) - return Failed; - - DwBody *parentNode = dynamic_cast( dwpart->Parent() ); - if ( !parentNode ) - return Failed; - parentNode->RemoveBodyPart( dwpart ); - - // add dummy part to show that a attachment has been deleted - KMMessagePart dummyPart; - dummyPart.duplicate( part ); - TQString comment = i18n("This attachment has been deleted."); - if ( !part.fileName().isEmpty() ) - comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() ); - dummyPart.setContentDescription( comment ); - dummyPart.setBodyEncodedBinary( TQByteArray() ); - TQCString cd = dummyPart.contentDisposition(); - if ( cd.find( "inline", 0, false ) == 0 ) { - cd.replace( 0, 10, "attachment" ); - dummyPart.setContentDisposition( cd ); - } else if ( cd.isEmpty() ) { - dummyPart.setContentDisposition( "attachment" ); - } - DwBodyPart* newDwPart = msg->createDWBodyPart( &dummyPart ); - parentNode->AddBodyPart( newDwPart ); - msg->getTopLevelPart()->Assemble(); KMMessage *newMsg = new KMMessage(); newMsg->fromDwString( msg->asDwString() ); @@ -3455,6 +3466,13 @@ KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * ms mTempFile.setAutoDelete( true ); } +KMEditAttachmentCommand::KMEditAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent ) + : AttachmentModifyCommand( nodeId, msg, parent ) +{ + kdDebug(5006) << k_funcinfo << endl; + mTempFile.setAutoDelete( true ); +} + KMEditAttachmentCommand::~ KMEditAttachmentCommand() { } @@ -3462,8 +3480,11 @@ KMEditAttachmentCommand::~ KMEditAttachmentCommand() KMCommand::Result KMEditAttachmentCommand::doAttachmentModify() { KMMessage *msg = retrievedMessage(); + if ( !msg ) + return Failed; + KMMessagePart part; - DwBodyPart *dwpart = findPart( msg, mPartIndex ); + DwBodyPart *dwpart = msg->findPart( mPartIndex ); if ( !dwpart ) return Failed; KMMessage::bodyPart( dwpart, &part, true ); @@ -3476,7 +3497,10 @@ KMCommand::Result KMEditAttachmentCommand::doAttachmentModify() mTempFile.file()->writeBlock( part.bodyDecodedBinary() ); mTempFile.file()->flush(); - KMail::EditorWatcher *watcher = new KMail::EditorWatcher( KURL(mTempFile.file()->name()), part.typeStr() + "/" + part.subtypeStr(), false, this ); + KMail::EditorWatcher *watcher = + new KMail::EditorWatcher( KURL( mTempFile.file()->name() ), + part.typeStr() + "/" + part.subtypeStr(), + false, this, parentWidget() ); connect( watcher, TQT_SIGNAL(editDone(KMail::EditorWatcher*)), TQT_SLOT(editDone(KMail::EditorWatcher*)) ); if ( !watcher->start() ) return Failed; @@ -3502,7 +3526,7 @@ void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher) // build the new message KMMessage *msg = retrievedMessage(); KMMessagePart part; - DwBodyPart *dwpart = findPart( msg, mPartIndex ); + DwBodyPart *dwpart = msg->findPart( mPartIndex ); KMMessage::bodyPart( dwpart, &part, true ); DwBody *parentNode = dynamic_cast( dwpart->Parent() ); @@ -3549,8 +3573,8 @@ KMCommand::Result CreateTodoCommand::execute() tf.close(); KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" ); - iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, - uri, tf.name(), TQStringList(), "message/rfc822" ); + iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, uri, + tf.name(), TQStringList(), "message/rfc822", true ); delete iface; return OK; diff --git a/kmail/kmcommands.h b/kmail/kmcommands.h index 7d1385a6..781a8087 100644 --- a/kmail/kmcommands.h +++ b/kmail/kmcommands.h @@ -39,6 +39,8 @@ namespace KMail { class Composer; class FolderJob; class EditorWatcher; + class HeaderStyle; + class HeaderStrategy; } namespace GpgME { class Error; } namespace Kleo { class SpecialJob; } @@ -83,9 +85,11 @@ public slots: void slotProgress( unsigned long done, unsigned long total ); signals: + + /// @param result The status of the command. void messagesTransfered( KMCommand::Result result ); - /** Emitted when the command has completed. - * @param result The status of the command. */ + + /// Emitted when the command has completed. void completed( KMCommand *command ); protected: @@ -342,6 +346,7 @@ private: static const int MAX_CHUNK_SIZE = 64*1024; KURL mUrl; TQValueList mMsgList; + TQValueList mUngetMsgs; unsigned int mMsgListIndex; KMMessage *mStandAloneMessage; TQByteArray mData; @@ -601,8 +606,10 @@ class KDE_EXPORT KMPrintCommand : public KMCommand public: KMPrintCommand( TQWidget *parent, KMMessage *msg, - bool htmlOverride=false, - bool htmlLoadExtOverride=false, + const KMail::HeaderStyle *headerStyle = 0, + const KMail::HeaderStrategy *headerStrategy = 0, + bool htmlOverride = false, + bool htmlLoadExtOverride = false, bool useFixedFont = false, const TQString & encoding = TQString() ); @@ -611,6 +618,8 @@ public: private: virtual Result execute(); + const KMail::HeaderStyle *mHeaderStyle; + const KMail::HeaderStrategy *mHeaderStrategy; bool mHtmlOverride; bool mHtmlLoadExtOverride; bool mUseFixedFont; @@ -1036,11 +1045,11 @@ class KDE_EXPORT AttachmentModifyCommand : public KMCommand Q_OBJECT public: AttachmentModifyCommand( partNode *node, KMMessage *msg, TQWidget *parent ); + AttachmentModifyCommand( int nodeId, KMMessage *msg, TQWidget *parent ); ~AttachmentModifyCommand(); protected: void storeChangedMessage( KMMessage* msg ); - DwBodyPart* findPart( KMMessage* msg, int index ); virtual Result doAttachmentModify() = 0; protected: @@ -1049,7 +1058,6 @@ class KDE_EXPORT AttachmentModifyCommand : public KMCommand private: Result execute(); - DwBodyPart* findPartInternal( DwEntity* root, int index, int &accu ); private slots: void messageStoreResult( KMFolderImap* folder, bool success ); @@ -1064,6 +1072,7 @@ class KDE_EXPORT KMDeleteAttachmentCommand : public AttachmentModifyCommand Q_OBJECT public: KMDeleteAttachmentCommand( partNode *node, KMMessage *msg, TQWidget *parent ); + KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent ); ~KMDeleteAttachmentCommand(); protected: @@ -1076,6 +1085,7 @@ class KDE_EXPORT KMEditAttachmentCommand : public AttachmentModifyCommand Q_OBJECT public: KMEditAttachmentCommand( partNode *node, KMMessage *msg, TQWidget *parent ); + KMEditAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent ); ~KMEditAttachmentCommand(); protected: diff --git a/kmail/kmcomposewin.cpp b/kmail/kmcomposewin.cpp index 592248a0..a1ab32d8 100644 --- a/kmail/kmcomposewin.cpp +++ b/kmail/kmcomposewin.cpp @@ -69,8 +69,6 @@ using KRecentAddress::RecentAddresses; #include #include -#include -#include #include #include "klistboxdialog.h" @@ -159,6 +157,7 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id ) mSpellCheckInProgress( false ), mDone( false ), mAtmModified( false ), + mAtmSelectNew( 0 ), mMsg( 0 ), mAttachMenu( 0 ), mSigningAndEncryptionExplicitlyDisabled( false ), @@ -183,7 +182,11 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id ) mLabelWidth( 0 ), mAutoSaveTimer( 0 ), mLastAutoSaveErrno( 0 ), mSignatureStateIndicator( 0 ), mEncryptionStateIndicator( 0 ), - mPreserveUserCursorPosition( false ) + mPreserveUserCursorPosition( false ), + mPreventFccOverwrite( false ), + mCheckForRecipients( true ), + mCheckForForgottenAttachments( true ), + mIgnoreStickyFields( false ) { mClassicalRecipients = GlobalSettings::self()->recipientsEditorType() == GlobalSettings::EnumRecipientsEditorType::Classic; @@ -200,17 +203,29 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id ) TQVBoxLayout *v = new TQVBoxLayout( mMainWidget ); v->addWidget( mHeadersToEditorSplitter ); mIdentity = new KPIM::IdentityCombo(kmkernel->identityManager(), mHeadersArea); + TQToolTip::add( mIdentity, + i18n( "Select an identity for this message" ) ); + mDictionaryCombo = new DictionaryComboBox( mHeadersArea ); + TQToolTip::add( mDictionaryCombo, + i18n( "Select the dictionary to use when spell-checking this message" ) ); + mFcc = new KMFolderComboBox(mHeadersArea); mFcc->showOutboxFolder( false ); + TQToolTip::add( mFcc, + i18n( "Select the sent-mail folder where a copy of this message will be saved" ) ); + mTransport = new TQComboBox(true, mHeadersArea); + TQToolTip::add( mTransport, + i18n( "Select the outgoing account to use for sending this message" ) ); + mEdtFrom = new KMLineEdit(false,mHeadersArea, "fromLine"); + TQToolTip::add( mEdtFrom, + i18n( "Set the \"From:\" email address for this message" ) ); mEdtReplyTo = new KMLineEdit(true,mHeadersArea, "replyToLine"); - mLblReplyTo = new TQLabel(mHeadersArea); - mBtnReplyTo = new TQPushButton("...",mHeadersArea); - mBtnReplyTo->setFocusPolicy(TQWidget::NoFocus); - connect(mBtnReplyTo,TQT_SIGNAL(clicked()),TQT_SLOT(slotAddrBookReplyTo())); + TQToolTip::add( mEdtReplyTo, + i18n( "Set the \"Reply-To:\" email address for this message" ) ); connect(mEdtReplyTo,TQT_SIGNAL(completionModeChanged(KGlobalSettings::Completion)), TQT_SLOT(slotCompletionModeChanged(KGlobalSettings::Completion))); @@ -234,7 +249,6 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id ) TQToolTip::add( mBtnTo, tip ); TQToolTip::add( mBtnCc, tip ); TQToolTip::add( mBtnBcc, tip ); - TQToolTip::add( mBtnReplyTo, tip ); mBtnTo->setFocusPolicy(TQWidget::NoFocus); mBtnCc->setFocusPolicy(TQWidget::NoFocus); @@ -277,16 +291,30 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id ) mRecipientsEditor->setFocus(); } mEdtSubject = new KMLineEditSpell(false,mHeadersArea, "subjectLine"); - mLblIdentity = new TQLabel(mHeadersArea); - mDictionaryLabel = new TQLabel( mHeadersArea ); - mLblFcc = new TQLabel(mHeadersArea); - mLblTransport = new TQLabel(mHeadersArea); - mLblFrom = new TQLabel(mHeadersArea); - mLblSubject = new TQLabel(mHeadersArea); + TQToolTip::add( mEdtSubject, + i18n( "Set a subject for this message" ) ); + + mLblIdentity = new TQLabel( i18n("&Identity:"), mHeadersArea ); + mDictionaryLabel = new TQLabel( i18n("&Dictionary:"), mHeadersArea ); + mLblFcc = new TQLabel( i18n("&Sent-Mail folder:"), mHeadersArea ); + mLblTransport = new TQLabel( i18n("&Mail transport:"), mHeadersArea ); + mLblFrom = new TQLabel( i18n("sender address field", "&From:"), mHeadersArea ); + mLblReplyTo = new TQLabel( i18n("&Reply to:"), mHeadersArea ); + mLblSubject = new TQLabel( i18n("S&ubject:"), mHeadersArea ); + TQString sticky = i18n("Sticky"); mBtnIdentity = new TQCheckBox(sticky,mHeadersArea); + TQToolTip::add( mBtnIdentity, + i18n( "Use the selected value as your identity for future messages" ) ); mBtnFcc = new TQCheckBox(sticky,mHeadersArea); + TQToolTip::add( mBtnFcc, + i18n( "Use the selected value as your sent-mail folder for future messages" ) ); mBtnTransport = new TQCheckBox(sticky,mHeadersArea); + TQToolTip::add( mBtnTransport, + i18n( "Use the selected value as your outgoing account for future messages" ) ); + mBtnDictionary = new TQCheckBox( sticky, mHeadersArea ); + TQToolTip::add( mBtnDictionary, + i18n( "Use the selected value as your dictionary for future messages" ) ); //setWFlags( WType_TopLevel | WStyle_Dialog ); mHtmlMarkup = GlobalSettings::self()->useHtmlMarkup(); @@ -350,6 +378,8 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id ) GlobalSettings::self()->stickyFccItem()->whatsThis() ); TQWhatsThis::add( mBtnTransport, GlobalSettings::self()->stickyTransportItem()->whatsThis() ); + TQWhatsThis::add( mBtnTransport, + GlobalSettings::self()->stickyDictionaryItem()->whatsThis() ); mSpellCheckInProgress=false; @@ -359,6 +389,7 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id ) mBtnIdentity->setFocusPolicy(TQWidget::NoFocus); mBtnFcc->setFocusPolicy(TQWidget::NoFocus); mBtnTransport->setFocusPolicy(TQWidget::NoFocus); + mBtnDictionary->setFocusPolicy( TQWidget::NoFocus ); mAtmListView = new AttachmentListView( this, mSplitter, "attachment list view" ); @@ -657,6 +688,7 @@ void KMComposeWin::readConfig( bool reload /* = false */ ) } mBtnFcc->setChecked( GlobalSettings::self()->stickyFcc() ); mBtnTransport->setChecked( GlobalSettings::self()->stickyTransport() ); + mBtnDictionary->setChecked( GlobalSettings::self()->stickyDictionary() ); TQStringList transportHistory = GlobalSettings::self()->transportHistory(); TQString currentTransport = GlobalSettings::self()->currentTransport(); @@ -711,8 +743,6 @@ void KMComposeWin::readConfig( bool reload /* = false */ ) const KPIM::Identity & ident = kmkernel->identityManager()->identityForUoid( mIdentity->currentIdentity() ); - mDictionaryCombo->setCurrentByDictionary( ident.dictionary() ); - mTransport->clear(); mTransport->insertStringList( KMTransportInfo::availableTransports() ); while ( transportHistory.count() > (uint)GlobalSettings::self()->maxTransportEntries() ) @@ -723,6 +753,12 @@ void KMComposeWin::readConfig( bool reload /* = false */ ) setTransport( currentTransport ); } + if ( mBtnDictionary->isChecked() ) { + mDictionaryCombo->setCurrentByDictionaryName( GlobalSettings::self()->previousDictionary() ); + } else { + mDictionaryCombo->setCurrentByDictionary( ident.dictionary() ); + } + TQString fccName = ""; if ( mBtnFcc->isChecked() ) { fccName = GlobalSettings::self()->previousFcc(); @@ -737,12 +773,16 @@ void KMComposeWin::readConfig( bool reload /* = false */ ) void KMComposeWin::writeConfig(void) { GlobalSettings::self()->setHeaders( mShowHeaders ); - GlobalSettings::self()->setStickyTransport( mBtnTransport->isChecked() ); - GlobalSettings::self()->setStickyIdentity( mBtnIdentity->isChecked() ); GlobalSettings::self()->setStickyFcc( mBtnFcc->isChecked() ); - GlobalSettings::self()->setPreviousIdentity( mIdentity->currentIdentity() ); - GlobalSettings::self()->setCurrentTransport( mTransport->currentText() ); + if ( !mIgnoreStickyFields ) { + GlobalSettings::self()->setCurrentTransport( mTransport->currentText() ); + GlobalSettings::self()->setStickyTransport( mBtnTransport->isChecked() ); + GlobalSettings::self()->setStickyDictionary( mBtnDictionary->isChecked() ); + GlobalSettings::self()->setStickyIdentity( mBtnIdentity->isChecked() ); + GlobalSettings::self()->setPreviousIdentity( mIdentity->currentIdentity() ); + } GlobalSettings::self()->setPreviousFcc( mFcc->getFolder()->idString() ); + GlobalSettings::self()->setPreviousDictionary( mDictionaryCombo->currentDictionaryName() ); GlobalSettings::self()->setAutoSpellChecking( mAutoSpellCheckingAction->isChecked() ); TQStringList transportHistory = GlobalSettings::self()->transportHistory(); @@ -950,7 +990,7 @@ void KMComposeWin::rethinkFields(bool fromSlot) mGrid->setColStretch(0, 1); mGrid->setColStretch(1, 100); mGrid->setColStretch(2, 1); - mGrid->setRowStretch(mNumHeaders, 100); + mGrid->setRowStretch( mNumHeaders + 1, 100 ); row = 0; kdDebug(5006) << "KMComposeWin::rethinkFields" << endl; @@ -967,37 +1007,37 @@ void KMComposeWin::rethinkFields(bool fromSlot) if (!fromSlot) mAllFieldsAction->setChecked(showHeaders==HDR_ALL); if (!fromSlot) mIdentityAction->setChecked(abs(mShowHeaders)&HDR_IDENTITY); - rethinkHeaderLine(showHeaders,HDR_IDENTITY, row, i18n("&Identity:"), + rethinkHeaderLine(showHeaders,HDR_IDENTITY, row, mLblIdentity, mIdentity, mBtnIdentity); if (!fromSlot) mDictionaryAction->setChecked(abs(mShowHeaders)&HDR_DICTIONARY); - rethinkHeaderLine(showHeaders,HDR_DICTIONARY, row, i18n("&Dictionary:"), - mDictionaryLabel, mDictionaryCombo, 0 ); + rethinkHeaderLine(showHeaders,HDR_DICTIONARY, row, + mDictionaryLabel, mDictionaryCombo, mBtnDictionary ); if (!fromSlot) mFccAction->setChecked(abs(mShowHeaders)&HDR_FCC); - rethinkHeaderLine(showHeaders,HDR_FCC, row, i18n("&Sent-Mail folder:"), + rethinkHeaderLine(showHeaders,HDR_FCC, row, mLblFcc, mFcc, mBtnFcc); if (!fromSlot) mTransportAction->setChecked(abs(mShowHeaders)&HDR_TRANSPORT); - rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row, i18n("&Mail transport:"), + rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row, mLblTransport, mTransport, mBtnTransport); if (!fromSlot) mFromAction->setChecked(abs(mShowHeaders)&HDR_FROM); - rethinkHeaderLine(showHeaders,HDR_FROM, row, i18n("sender address field", "&From:"), + rethinkHeaderLine(showHeaders,HDR_FROM, row, mLblFrom, mEdtFrom /*, mBtnFrom */ ); TQWidget *prevFocus = mEdtFrom; if (!fromSlot) mReplyToAction->setChecked(abs(mShowHeaders)&HDR_REPLY_TO); - rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row,i18n("&Reply to:"), - mLblReplyTo, mEdtReplyTo, mBtnReplyTo); + rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row, + mLblReplyTo, mEdtReplyTo, 0); if ( showHeaders & HDR_REPLY_TO ) { prevFocus = connectFocusMoving( prevFocus, mEdtReplyTo ); } if ( mClassicalRecipients ) { if (!fromSlot) mToAction->setChecked(abs(mShowHeaders)&HDR_TO); - rethinkHeaderLine(showHeaders, HDR_TO, row, i18n("recipient address field", "&To:"), + rethinkHeaderLine(showHeaders, HDR_TO, row, mLblTo, mEdtTo, mBtnTo, i18n("Primary Recipients"), i18n("The email addresses you put " @@ -1007,7 +1047,7 @@ void KMComposeWin::rethinkFields(bool fromSlot) } if (!fromSlot) mCcAction->setChecked(abs(mShowHeaders)&HDR_CC); - rethinkHeaderLine(showHeaders, HDR_CC, row, i18n("&Copy to (CC):"), + rethinkHeaderLine(showHeaders, HDR_CC, row, mLblCc, mEdtCc, mBtnCc, i18n("Additional Recipients"), i18n("The email addresses you put " @@ -1022,7 +1062,7 @@ void KMComposeWin::rethinkFields(bool fromSlot) } if (!fromSlot) mBccAction->setChecked(abs(mShowHeaders)&HDR_BCC); - rethinkHeaderLine(showHeaders,HDR_BCC, row, i18n("&Blind copy to (BCC):"), + rethinkHeaderLine(showHeaders,HDR_BCC, row, mLblBcc, mEdtBcc, mBtnBcc, i18n("Hidden Recipients"), i18n("Essentially the same thing " @@ -1057,7 +1097,7 @@ void KMComposeWin::rethinkFields(bool fromSlot) prevFocus = mRecipientsEditor; } if (!fromSlot) mSubjectAction->setChecked(abs(mShowHeaders)&HDR_SUBJECT); - rethinkHeaderLine(showHeaders,HDR_SUBJECT, row, i18n("S&ubject:"), + rethinkHeaderLine(showHeaders,HDR_SUBJECT, row, mLblSubject, mEdtSubject); connectFocusMoving( mEdtSubject, mEditor ); @@ -1100,13 +1140,12 @@ TQWidget *KMComposeWin::connectFocusMoving( TQWidget *prev, TQWidget *next ) //----------------------------------------------------------------------------- void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow, - const TQString &aLabelStr, TQLabel* aLbl, + TQLabel* aLbl, TQLineEdit* aEdt, TQPushButton* aBtn, const TQString &toolTip, const TQString &whatsThis ) { if (aValue & aMask) { - aLbl->setText(aLabelStr); if ( !toolTip.isEmpty() ) TQToolTip::add( aLbl, toolTip ); if ( !whatsThis.isEmpty() ) @@ -1137,12 +1176,11 @@ void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow, //----------------------------------------------------------------------------- void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow, - const TQString &aLabelStr, TQLabel* aLbl, + TQLabel* aLbl, TQComboBox* aCbx, TQCheckBox* aChk) { if (aValue & aMask) { - aLbl->setText(aLabelStr); aLbl->adjustSize(); aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6); aLbl->setMinimumSize(aLbl->size()); @@ -1310,11 +1348,15 @@ void KMComposeWin::setupActions(void) (void) new KAction (i18n("Paste as Attac&hment"),0,this,TQT_SLOT( slotPasteClipboardAsAttachment()), actionCollection(), "paste_att"); - mAddQuoteChars = new KAction(i18n("Add &Quote Characters"), 0, this, + KAction * addq = new KAction(i18n("Add &Quote Characters"), 0, this, TQT_SLOT(slotAddQuotes()), actionCollection(), "tools_quote"); + connect( mEditor, TQT_SIGNAL(selectionAvailable(bool)), + addq, TQT_SLOT(setEnabled(bool)) ); - mRemQuoteChars = new KAction(i18n("Re&move Quote Characters"), 0, this, + KAction * remq = new KAction(i18n("Re&move Quote Characters"), 0, this, TQT_SLOT(slotRemoveQuotes()), actionCollection(), "tools_unquote"); + connect( mEditor, TQT_SIGNAL(selectionAvailable(bool)), + remq, TQT_SLOT(setEnabled(bool)) ); (void) new KAction (i18n("Cl&ean Spaces"), 0, this, TQT_SLOT(slotCleanSpace()), @@ -1509,6 +1551,7 @@ void KMComposeWin::setupActions(void) actionCollection(), "options_select_crypto" ); mCryptoModuleAction->setItems( l ); mCryptoModuleAction->setCurrentItem( format2cb( ident.preferredCryptoMessageFormat() ) ); + mCryptoModuleAction->setToolTip( i18n( "Select a cryptographic format for this message" ) ); slotSelectCryptoModule( true /* initialize */ ); TQStringList styleItems; @@ -1523,14 +1566,17 @@ void KMComposeWin::setupActions(void) listAction = new KSelectAction( i18n( "Select Style" ), 0, actionCollection(), "text_list" ); listAction->setItems( styleItems ); + listAction->setToolTip( i18n( "Select a list style" ) ); connect( listAction, TQT_SIGNAL( activated( const TQString& ) ), TQT_SLOT( slotListAction( const TQString& ) ) ); fontAction = new KFontAction( "Select Font", 0, actionCollection(), "text_font" ); + fontAction->setToolTip( i18n( "Select a font" ) ); connect( fontAction, TQT_SIGNAL( activated( const TQString& ) ), TQT_SLOT( slotFontAction( const TQString& ) ) ); fontSizeAction = new KFontSizeAction( "Select Size", 0, actionCollection(), "text_size" ); + fontSizeAction->setToolTip( i18n( "Select a font size" ) ); connect( fontSizeAction, TQT_SIGNAL( fontSizeChanged( int ) ), TQT_SLOT( slotSizeAction( int ) ) ); @@ -1821,7 +1867,7 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign, } mEdtSubject->setText(mMsg->subject()); - const bool stickyIdentity = mBtnIdentity->isChecked(); + const bool stickyIdentity = mBtnIdentity->isChecked() && !mIgnoreStickyFields; const bool messageHasIdentity = !newMsg->headerField("X-KMail-Identity").isEmpty(); if (!stickyIdentity && messageHasIdentity) mId = newMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt(); @@ -1933,7 +1979,8 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign, !ident.pgpEncryptionKey().isEmpty() ); TQString transport = newMsg->headerField("X-KMail-Transport"); - if (!mBtnTransport->isChecked() && !transport.isEmpty()) + const bool stickyTransport = mBtnTransport->isChecked() && !mIgnoreStickyFields; + if (!stickyTransport && !transport.isEmpty()) setTransport( transport ); if (!mBtnFcc->isChecked()) @@ -1944,7 +1991,10 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign, setFcc(ident.fcc()); } - mDictionaryCombo->setCurrentByDictionary( ident.dictionary() ); + const bool stickyDictionary = mBtnDictionary->isChecked() && !mIgnoreStickyFields; + if ( !stickyDictionary ) { + mDictionaryCombo->setCurrentByDictionary( ident.dictionary() ); + } partNode * root = partNode::fromMessage( mMsg ); @@ -1952,10 +2002,6 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign, otp.parseObjectTree( root ); KMail::AttachmentCollector ac; - ac.setDiveIntoEncryptions( true ); - ac.setDiveIntoSignatures( true ); - ac.setDiveIntoMessages( false ); - ac.collectAttachmentsFrom( root ); for ( std::vector::const_iterator it = ac.attachments().begin() ; it != ac.attachments().end() ; ++it ) @@ -2122,6 +2168,9 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign, // do this even for new messages mEditor->setCursorPositionFromStart( (unsigned int) mMsg->getCursorPos() ); + + // honor "keep reply in this folder" setting even when the identity is changed later on + mPreventFccOverwrite = ( !newMsg->fcc().isEmpty() && ident.fcc() != newMsg->fcc() ); } @@ -2217,7 +2266,8 @@ bool KMComposeWin::queryClose () //----------------------------------------------------------------------------- bool KMComposeWin::userForgotAttachment() { - bool checkForForgottenAttachments = GlobalSettings::self()->showForgottenAttachmentWarning(); + bool checkForForgottenAttachments = + mCheckForForgottenAttachments && GlobalSettings::self()->showForgottenAttachmentWarning(); if ( !checkForForgottenAttachments || ( mAtmList.count() > 0 ) ) return false; @@ -2482,6 +2532,13 @@ void KMComposeWin::removeAttach(const TQString &aUrl) void KMComposeWin::removeAttach(int idx) { mAtmModified = true; + + KMAtmListViewItem *item = static_cast( mAtmItemList.at( idx ) ); + if ( item->itemBelow() ) + mAtmSelectNew = item->itemBelow(); + else if ( item->itemAbove() ) + mAtmSelectNew = item->itemAbove(); + mAtmList.remove(idx); delete mAtmItemList.take(idx); @@ -2692,11 +2749,19 @@ void KMComposeWin::slotAttachFile() // We will not care about any permissions, existence or whatsoever in // this function. - KFileDialog fdlg(TQString::null, TQString::null, this, 0, true); + // Handle the case where the last savedir is gone. kolab/issue4057 + TQString recent; + KURL recentURL = KFileDialog::getStartURL( TQString::null, recent ); + if ( !recentURL.url().isEmpty() && + !KIO::NetAccess::exists( recentURL, true, this ) ) { + recentURL = KURL( TQDir::homeDirPath() ); + } + + KFileDialog fdlg( recentURL.url(), TQString::null, this, 0, true ); fdlg.setOperationMode( KFileDialog::Other ); - fdlg.setCaption(i18n("Attach File")); - fdlg.okButton()->setGuiItem(KGuiItem(i18n("&Attach"),"fileopen")); - fdlg.setMode(KFile::Files); + fdlg.setCaption( i18n( "Attach File" ) ); + fdlg.okButton()->setGuiItem( KGuiItem( i18n( "&Attach" ),"fileopen" ) ); + fdlg.setMode( KFile::Files ); fdlg.exec(); KURL::List files = fdlg.selectedURLs(); @@ -2831,7 +2896,9 @@ void KMComposeWin::slotAttachFileResult(KIO::Job *job) mMapAtmLoadData.remove(it); - msgPart->setCharset(partCharset); + if ( msgPart->typeStr().lower() == "text" ) { + msgPart->setCharset(partCharset); + } // show message part dialog, if not configured away (default): KConfigGroup composer(KMKernel::config(), "Composer"); @@ -2859,7 +2926,6 @@ void KMComposeWin::slotAttachFileResult(KIO::Job *job) } } mAtmModified = true; - if (msgPart->typeStr().lower() != "text") msgPart->setCharset(TQCString()); // add the new attachment to the list addAttach(msgPart); @@ -3180,7 +3246,6 @@ void KMComposeWin::slotAttachProperties() if (idx < 0) return; KMMessagePart* msgPart = mAtmList.at(idx); - msgPart->setCharset(mCharset); KMMsgPartDialogCompat dlg(mMainWidget); dlg.setMsgPart(msgPart); @@ -3495,7 +3560,9 @@ void KMComposeWin::editAttach(int index, bool openWith) atmTempFile->file()->flush(); - KMail::EditorWatcher *watcher = new KMail::EditorWatcher( KURL( atmTempFile->name() ), contentTypeStr, openWith, this ); + KMail::EditorWatcher *watcher = + new KMail::EditorWatcher( KURL( atmTempFile->name() ), contentTypeStr, openWith, + this, this ); connect( watcher, TQT_SIGNAL(editDone(KMail::EditorWatcher*)), TQT_SLOT(slotEditDone(KMail::EditorWatcher*)) ); if ( watcher->start() ) { mEditorMap.insert( watcher, msgPart ); @@ -3516,7 +3583,7 @@ void KMComposeWin::slotAttachSave() pname = msgPart->name(); if (pname.isEmpty()) pname="unnamed"; - KURL url = KFileDialog::getSaveURL(TQString::null, TQString::null, 0, i18n("Save Attachment As")); + KURL url = KFileDialog::getSaveURL(pname, TQString::null, 0, i18n("Save Attachment As")); if( url.isEmpty() ) return; @@ -3528,6 +3595,7 @@ void KMComposeWin::slotAttachSave() //----------------------------------------------------------------------------- void KMComposeWin::slotAttachRemove() { + mAtmSelectNew = 0; bool attachmentRemoved = false; int i = 0; for ( TQPtrListIterator it(mAtmItemList); *it; ) { @@ -3544,6 +3612,10 @@ void KMComposeWin::slotAttachRemove() if ( attachmentRemoved ) { setModified( true ); slotUpdateAttachActions(); + if ( mAtmSelectNew ) { + mAtmListView->setSelected( mAtmSelectNew, true ); + mAtmListView->setCurrentItem( mAtmSelectNew ); + } } } @@ -3861,6 +3933,7 @@ void KMComposeWin::slotEncryptToggled(bool on) //----------------------------------------------------------------------------- void KMComposeWin::setEncryption( bool encrypt, bool setByUser ) { + bool wasModified = isModified(); if ( setByUser ) setModified( true ); if ( !mEncryptAction->isEnabled() ) @@ -3868,7 +3941,7 @@ void KMComposeWin::setEncryption( bool encrypt, bool setByUser ) // check if the user wants to encrypt messages to himself and if he defined // an encryption key for the current identity else if ( encrypt && encryptToSelf() && !mLastIdentityHasEncryptionKey ) { - if ( setByUser ) + if ( setByUser ) { KMessageBox::sorry( this, i18n("

You have requested that messages be " "encrypted to yourself, but the currently selected " @@ -3878,6 +3951,8 @@ void KMComposeWin::setEncryption( bool encrypt, bool setByUser ) "in the identity configuration.

" "
"), i18n("Undefined Encryption Key") ); + setModified( wasModified ); + } encrypt = false; } @@ -3912,6 +3987,7 @@ void KMComposeWin::slotSignToggled(bool on) //----------------------------------------------------------------------------- void KMComposeWin::setSigning( bool sign, bool setByUser ) { + bool wasModified = isModified(); if ( setByUser ) setModified( true ); if ( !mSignAction->isEnabled() ) @@ -3919,7 +3995,7 @@ void KMComposeWin::setSigning( bool sign, bool setByUser ) // check if the user defined a signing key for the current identity if ( sign && !mLastIdentityHasSigningKey ) { - if ( setByUser ) + if ( setByUser ) { KMessageBox::sorry( this, i18n("

In order to be able to sign " "this message you first have to " @@ -3929,6 +4005,8 @@ void KMComposeWin::setSigning( bool sign, bool setByUser ) "in the identity configuration.

" "
"), i18n("Undefined Signing Key") ); + setModified( wasModified ); + } sign = false; } @@ -3966,6 +4044,26 @@ void KMComposeWin::disableWordWrap() mEditor->setWordWrap( TQTextEdit::NoWrap ); } +void KMComposeWin::disableRecipientNumberCheck() +{ + mCheckForRecipients = false; +} + +void KMComposeWin::disableForgottenAttachmentsCheck() +{ + mCheckForForgottenAttachments = false; +} + +void KMComposeWin::ignoreStickyFields() +{ + mIgnoreStickyFields = true; + mBtnTransport->setChecked( false ); + mBtnDictionary->setChecked( false ); + mBtnIdentity->setChecked( false ); + mBtnTransport->setEnabled( false ); + mBtnDictionary->setEnabled( false ); + mBtnIdentity->setEnabled( false ); +} //----------------------------------------------------------------------------- void KMComposeWin::slotPrint() @@ -4275,11 +4373,24 @@ void KMComposeWin::slotContinueDoSend( bool sentOk ) return; } +bool KMComposeWin::checkTransport() const +{ + if ( KMail::TransportManager::transportNames().isEmpty() ) { + KMessageBox::information( mMainWidget, + i18n("Please create an account for sending and try again.") ); + return false; + } + return true; +} //---------------------------------------------------------------------------- void KMComposeWin::slotSendLater() { + if ( !checkTransport() ) + return; + if ( !checkRecipientNumber() ) + return; if ( mEditor->checkExternalEditorFinished() ) doSend( KMail::MessageSender::SendLater ); } @@ -4322,6 +4433,10 @@ void KMComposeWin::slotSendLaterVia( int item ) void KMComposeWin::slotSendNow() { if ( !mEditor->checkExternalEditorFinished() ) return; + if ( !checkTransport() ) + return; + if ( !checkRecipientNumber() ) + return; if ( GlobalSettings::self()->confirmBeforeSend() ) { int rc = KMessageBox::warningYesNoCancel( mMainWidget, @@ -4339,6 +4454,26 @@ void KMComposeWin::slotSendNow() { doSend( KMail::MessageSender::SendImmediate ); } + +//---------------------------------------------------------------------------- +bool KMComposeWin::checkRecipientNumber() const +{ + uint thresHold = GlobalSettings::self()->recipientThreshold(); + if ( mCheckForRecipients && + GlobalSettings::self()->tooManyRecipients() && + mRecipientsEditor->recipients().count() > thresHold ) { + if ( KMessageBox::questionYesNo( mMainWidget, + i18n("You are trying to send the mail to more than %1 recipients. Send message anyway?").arg(thresHold), + i18n("Too many receipients"), + i18n("&Send as Is"), + i18n("&Edit Recipients")) == KMessageBox::No ) { + return false; + } + } + return true; +} + + //---------------------------------------------------------------------------- void KMComposeWin::slotAppendSignature() { @@ -4348,17 +4483,17 @@ void KMComposeWin::slotAppendSignature() //---------------------------------------------------------------------------- void KMComposeWin::slotPrependSignature() { - insertSignature( false ); + insertSignature( Prepend ); } //---------------------------------------------------------------------------- void KMComposeWin::slotInsertSignatureAtCursor() { - insertSignature( false, mEditor->currentLine() ); + insertSignature( AtCursor ); } //---------------------------------------------------------------------------- -void KMComposeWin::insertSignature( bool append, int pos ) +void KMComposeWin::insertSignature( SignaturePlacement placement ) { bool mod = mEditor->isModified(); @@ -4370,12 +4505,36 @@ void KMComposeWin::insertSignature( bool append, int pos ) if( !mOldSigText.isEmpty() ) { - mEditor->sync(); - if ( append ) { + mEditor->sync(); + int paragraph, index; + mEditor->getCursorPosition( ¶graph, &index ); + index = mEditor->indexOfCurrentLineStart( paragraph, index ); + + switch( placement ) { + case Append: mEditor->setText( mEditor->text() + mOldSigText ); - } else { - mOldSigText = "\n\n"+mOldSigText+"\n"; - mEditor->insertAt(mOldSigText, pos, 0); + break; + case Prepend: + mOldSigText = "\n\n" + mOldSigText + "\n"; + mEditor->insertAt( mOldSigText, paragraph, index ); + break; + case AtCursor: + + // If there is text in the same line, add a newline so that the stuff in + // the current line moves after the signature. Also remove a leading newline, it is not + // needed here. + if ( mEditor->paragraphLength( paragraph ) > 0 ) + mOldSigText = mOldSigText + "\n"; + if ( mOldSigText.startsWith( "\n" ) ) + mOldSigText = mOldSigText.remove( 0, 1 ); + + // If we are inserting into a wordwrapped line, add a newline at the start to make + // the text edit hard-wrap the line here + if ( index != 0 ) + mOldSigText = "\n" + mOldSigText; + + mEditor->insertAt( mOldSigText, paragraph, index ); + break; } mEditor->update(); mEditor->setModified(mod); @@ -4390,8 +4549,13 @@ void KMComposeWin::insertSignature( bool append, int pos ) } else { // for append and prepend, move the cursor to 0,0, for insertAt, // keep it in the same row, but move to first column - mEditor->setCursorPosition( pos, 0 ); - if ( !append && pos == 0 ) + if ( index == 0 ) { + mEditor->setCursorPosition( paragraph, 0 ); + } else { + // For word-wrapped lines, we have created a new paragraph, so change to that one + mEditor->setCursorPosition( paragraph + 1, 0 ); + } + if ( placement == Prepend || placement == Append ) mEditor->setContentsPos( 0, 0 ); } mEditor->sync(); @@ -4715,7 +4879,7 @@ void KMComposeWin::slotIdentityChanged( uint uoid ) } } - if ( !mBtnTransport->isChecked() ) { + if ( !mBtnTransport->isChecked() && !mIgnoreStickyFields ) { TQString transp = ident.transport(); if ( transp.isEmpty() ) { @@ -4727,10 +4891,12 @@ void KMComposeWin::slotIdentityChanged( uint uoid ) setTransport( transp ); } - mDictionaryCombo->setCurrentByDictionary( ident.dictionary() ); + if ( !mBtnDictionary->isChecked() && !mIgnoreStickyFields ) { + mDictionaryCombo->setCurrentByDictionary( ident.dictionary() ); + } - if ( !mBtnFcc->isChecked() ) { - setFcc( ident.fcc() ); + if ( !mBtnFcc->isChecked() && !mPreventFccOverwrite ) { + setFcc( ident.fcc() ); } TQString edtText = mEditor->text(); @@ -4741,27 +4907,40 @@ void KMComposeWin::slotIdentityChanged( uint uoid ) identityManager()-> identityForUoidOrDefault( mMsg->headerField( "X-KMail-Identity" ). stripWhiteSpace().toUInt() ); - mOldSigText = id.signatureText(); + mOldSigText = GlobalSettings::self()->prependSignature() ? id.signature().rawText() : id.signatureText(); } - // try to truncate the old sig - // First remove any trailing whitespace - while ( !edtText.isEmpty() && edtText[edtText.length()-1].isSpace() ) - edtText.truncate( edtText.length() - 1 ); - // From the sig too, just in case - while ( !mOldSigText.isEmpty() && mOldSigText[mOldSigText.length()-1].isSpace() ) - mOldSigText.truncate( mOldSigText.length() - 1 ); - if( edtText.endsWith( mOldSigText ) ) - edtText.truncate( edtText.length() - mOldSigText.length() ); + if ( !GlobalSettings::prependSignature() ) { + // try to truncate the old sig + // First remove any trailing whitespace + while ( !edtText.isEmpty() && edtText[edtText.length()-1].isSpace() ) + edtText.truncate( edtText.length() - 1 ); + // From the sig too, just in case + while ( !mOldSigText.isEmpty() && mOldSigText[mOldSigText.length()-1].isSpace() ) + mOldSigText.truncate( mOldSigText.length() - 1 ); + + if ( edtText.endsWith( mOldSigText ) ) + edtText.truncate( edtText.length() - mOldSigText.length() ); - // now append the new sig - mOldSigText = ident.signatureText(); - if( ( !mOldSigText.isEmpty() ) && - ( GlobalSettings::self()->autoTextSignature() == "auto" ) ) { - edtText.append( mOldSigText ); + // now append the new sig + mOldSigText = ident.signatureText(); + if( ( !mOldSigText.isEmpty() ) && + ( GlobalSettings::self()->autoTextSignature() == "auto" ) ) { + edtText.append( mOldSigText ); + } + mEditor->setText( edtText ); + } else { + const int pos = edtText.find( mOldSigText ); + if ( pos >= 0 && !mOldSigText.isEmpty() ) { + const int oldLength = mOldSigText.length(); + mOldSigText = "\n\n"+ ident.signature().rawText() + "\n"; // see insertSignature() + edtText = edtText.replace( pos, oldLength, mOldSigText ); + mEditor->setText( edtText ); + } else { + insertSignature( Append ); + } } - mEditor->setText( edtText ); // disable certain actions if there is no PGP user identity set // for this profile @@ -4897,9 +5076,6 @@ void KMComposeWin::updateAutoSave() void KMComposeWin::setAutoSaveFilename( const TQString & filename ) { - if ( !mAutoSaveFilename.isEmpty() ) - KMFolderMaildir::removeFile( KMKernel::localDataPath() + "autosave", - mAutoSaveFilename ); mAutoSaveFilename = filename; } @@ -4957,8 +5133,6 @@ void KMComposeWin::slotFolderRemoved(KMFolder* folder) void KMComposeWin::editorFocusChanged(bool gained) { mPasteQuotation->setEnabled(gained); - mAddQuoteChars->setEnabled(gained); - mRemQuoteChars->setEnabled(gained); } void KMComposeWin::slotSetAlwaysSend( bool bAlways ) diff --git a/kmail/kmcomposewin.h b/kmail/kmcomposewin.h index 7f003d44..f38a8f68 100644 --- a/kmail/kmcomposewin.h +++ b/kmail/kmcomposewin.h @@ -96,7 +96,7 @@ namespace GpgME { } //----------------------------------------------------------------------------- -class KMComposeWin : public KMail::Composer, virtual public MailComposerIface +class KMComposeWin : public KMail::Composer, public MailComposerIface { Q_OBJECT friend class ::KMEdit; @@ -161,6 +161,32 @@ public: // kmkernel, kmcommands, callback void disableWordWrap(); + /** Don't check if there are too many recipients for a mail, + * eg. when sending out invitations. + */ + void disableRecipientNumberCheck(); + + /** Don't check for forgotten attachments for a mail, + * eg. when sending out invitations. + */ + void disableForgottenAttachmentsCheck(); + + /** + * Ignore the "sticky" setting of the transport combo box and prefer the X-KMail-Transport + * header field of the message instead. + * Do the same for the identity combo box, don't obey the "sticky" setting but use the + * X-KMail-Identity header field instead. + * + * This is useful when sending out invitations, since you don't see the GUI and want the + * identity and transport to be set to the values stored in the messages. + */ + void ignoreStickyFields(); + + /** + * Returns @c true while the message composing is in progress. + */ + bool isComposing() const { return mComposer != 0; } + private: // kmedit /** * Returns message of the composer. To apply the user changes to the @@ -524,14 +550,22 @@ private: */ void rethinkHeaderLine( int aValue, int aMask, int& aRow, - const TQString &aLabelStr, TQLabel* aLbl, + TQLabel* aLbl, TQLineEdit* aEdt, TQPushButton* aBtn = 0, const TQString &toolTip = TQString::null, const TQString &whatsThis = TQString::null ); void rethinkHeaderLine( int value, int mask, int& row, - const TQString& labelStr, TQLabel* lbl, - TQComboBox* cbx, TQCheckBox *chk ); + TQLabel* lbl, TQComboBox* cbx, TQCheckBox *chk ); + + /** + * Checks how many recipients are and warns if there are too many. + * @return true, if the user accepted the warning and the message should be sent + */ + bool checkRecipientNumber() const; + + + bool checkTransport() const; /** * Initialization methods @@ -691,11 +725,13 @@ private: */ void setTransport( const TQString & transport ); + enum SignaturePlacement { Append, Prepend, AtCursor }; + /** * Helper to insert the signature of the current identy at the * beginning or end of the editor. */ - void insertSignature( bool append = true, int pos = 0 ); + void insertSignature( SignaturePlacement placement = Append ); private slots: /** * Compress an attachemnt with the given index @@ -717,11 +753,12 @@ private: TQLabel *mLblIdentity, *mLblTransport, *mLblFcc; TQLabel *mLblFrom, *mLblReplyTo, *mLblTo, *mLblCc, *mLblBcc, *mLblSubject; TQLabel *mDictionaryLabel; - TQCheckBox *mBtnIdentity, *mBtnTransport, *mBtnFcc; + TQCheckBox *mBtnIdentity, *mBtnDictionary, *mBtnTransport, *mBtnFcc; TQPushButton *mBtnTo, *mBtnCc, *mBtnBcc, /* *mBtnFrom, */ *mBtnReplyTo; bool mSpellCheckInProgress; bool mDone; bool mAtmModified; + TQListViewItem *mAtmSelectNew; KMEdit* mEditor; TQGridLayout* mGrid; @@ -906,6 +943,11 @@ private: * accidentally moving the cursor. */ bool mPreserveUserCursorPosition; + + bool mPreventFccOverwrite; + bool mCheckForRecipients; + bool mCheckForForgottenAttachments; + bool mIgnoreStickyFields; }; #endif diff --git a/kmail/kmedit.cpp b/kmail/kmedit.cpp index bb1bb88f..6fdc3352 100644 --- a/kmail/kmedit.cpp +++ b/kmail/kmedit.cpp @@ -200,10 +200,10 @@ void KMEdit::contentsDropEvent(TQDropEvent *e) } else kdDebug(5006) << "KMEdit::contentsDropEvent, unable to add dropped object" << endl; - } + } else if( e->provides("text/x-textsnippet") ) { emit insertSnippet(); - } + } else { KEdit::contentsDropEvent(e); } @@ -214,7 +214,8 @@ KMEdit::KMEdit(TQWidget *parent, KMComposeWin* composer, const char *name) : KEdit( parent, name ), mComposer( composer ), - mKSpell( 0 ), + mKSpellForDialog( 0 ), + mSpeller( 0 ), mSpellConfig( autoSpellConfig ), mSpellingFilter( 0 ), mExtEditorTempFile( 0 ), @@ -222,19 +223,30 @@ KMEdit::KMEdit(TQWidget *parent, KMComposeWin* composer, mExtEditorProcess( 0 ), mUseExtEditor( false ), mWasModifiedBeforeSpellCheck( false ), - mSpellChecker( 0 ), + mHighlighter( 0 ), mSpellLineEdit( false ), mPasteMode( QClipboard::Clipboard ) { + connect( this, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectionChanged()) ); installEventFilter(this); KCursor::setAutoHideCursor( this, true, true ); setOverwriteEnabled( true ); + createSpellers(); + connect( mSpellConfig, TQT_SIGNAL( configChanged() ), + this, TQT_SLOT( createSpellers() ) ); + connect( mSpeller, TQT_SIGNAL( death() ), + this, TQT_SLOT( spellerDied() ) ); } +void KMEdit::createSpellers() +{ + delete mSpeller; + mSpeller = new KMSpell( this, TQT_SLOT( spellerReady( KSpell * ) ), mSpellConfig ); +} void KMEdit::initializeAutoSpellChecking() { - if ( mSpellChecker ) + if ( mHighlighter ) return; // already initialized TQColor defaultColor1( 0x00, 0x80, 0x00 ); // defaults from kmreaderwin.cpp TQColor defaultColor2( 0x00, 0x70, 0x00 ); @@ -252,14 +264,14 @@ void KMEdit::initializeAutoSpellChecking() TQColor col3 = readerConfig.readColorEntry( "QuotedText2", &defaultColor2 ); TQColor col4 = readerConfig.readColorEntry( "QuotedText1", &defaultColor1 ); TQColor misspelled = readerConfig.readColorEntry( "MisspelledColor", &c ); - mSpellChecker = new KDictSpellingHighlighter( this, /*active*/ true, - /*autoEnabled*/ false, - /*spellColor*/ misspelled, - /*colorQuoting*/ true, - col1, col2, col3, col4, - mSpellConfig ); - - connect( mSpellChecker, TQT_SIGNAL(newSuggestions(const TQString&, const TQStringList&, unsigned int)), + mHighlighter = new KMSyntaxHighter( this, /*active*/ true, + /*autoEnabled*/ false, + /*spellColor*/ misspelled, + /*colorQuoting*/ true, + col1, col2, col3, col4, + mSpellConfig ); + + connect( mHighlighter, TQT_SIGNAL(newSuggestions(const TQString&, const TQStringList&, unsigned int)), this, TQT_SLOT(addSuggestion(const TQString&, const TQStringList&, unsigned int)) ); } @@ -279,8 +291,8 @@ TQPopupMenu *KMEdit::createPopupMenu( const TQPoint& pos ) void KMEdit::deleteAutoSpellChecking() { // because the highlighter doesn't support RichText, delete its instance. - delete mSpellChecker; - mSpellChecker =0; + delete mHighlighter; + mHighlighter =0; } void KMEdit::addSuggestion(const TQString& text, const TQStringList& lst, unsigned int ) @@ -290,8 +302,8 @@ void KMEdit::addSuggestion(const TQString& text, const TQStringList& lst, unsign void KMEdit::setSpellCheckingActive(bool spellCheckingActive) { - if ( mSpellChecker ) { - mSpellChecker->setActive(spellCheckingActive); + if ( mHighlighter ) { + mHighlighter->setActive(spellCheckingActive); } } @@ -300,10 +312,16 @@ KMEdit::~KMEdit() { removeEventFilter(this); - delete mKSpell; - delete mSpellChecker; - mSpellChecker = 0; + if ( mSpeller ) { + // The speller needs some time to clean up, so trigger cleanup and let it delete itself + mSpeller->setAutoDelete( true ); + mSpeller->cleanUp(); + mSpeller = 0; + } + delete mKSpellForDialog; + delete mHighlighter; + mHighlighter = 0; } @@ -343,6 +361,55 @@ unsigned int KMEdit::lineBreakColumn() const return lineBreakColumn; } +KMSpell::KMSpell( TQObject *receiver, const char *slot, KSpellConfig *spellConfig ) + : KSpell( 0, TQString(), receiver, slot, spellConfig ) +{ +} + +KMSyntaxHighter::KMSyntaxHighter( TQTextEdit *textEdit, + bool spellCheckingActive, + bool autoEnable, + const TQColor& spellColor, + bool colorQuoting, + const TQColor& QuoteColor0, + const TQColor& QuoteColor1, + const TQColor& QuoteColor2, + const TQColor& QuoteColor3, + KSpellConfig *spellConfig ) + : KDictSpellingHighlighter( textEdit, spellCheckingActive, autoEnable, spellColor, colorQuoting, + QuoteColor0, QuoteColor1, QuoteColor2, QuoteColor3, spellConfig ) +{ +} + +bool KMSyntaxHighter::isMisspelled( const TQString &word ) +{ + if ( mIgnoredWords.contains( word ) ) { + return false; + } + else { + return KDictSpellingHighlighter::isMisspelled( word ); + } +} + +void KMSyntaxHighter::ignoreWord( const TQString &word ) +{ + mIgnoredWords << word; +} + +TQStringList KMSyntaxHighter::ignoredWords() const +{ + return mIgnoredWords; +} + +void KMEdit::spellerDied() +{ + mSpeller = 0; +} + +void KMEdit::spellerReady( KSpell *spell ) +{ + Q_ASSERT( mSpeller == spell ); +} bool KMEdit::eventFilter(TQObject*o, TQEvent* e) { @@ -439,7 +506,6 @@ bool KMEdit::eventFilter(TQObject*o, TQEvent* e) if( !word.isEmpty() && mReplacements.contains( word ) ) { KPopupMenu p; - p.insertTitle( i18n("Suggestions") ); //Add the suggestions to the popup menu TQStringList reps = mReplacements[word]; @@ -453,13 +519,34 @@ bool KMEdit::eventFilter(TQObject*o, TQEvent* e) } else { - p.insertItem( TQString::fromLatin1("No Suggestions"), -2 ); + p.setItemEnabled( p.insertItem( i18n( "No Suggestions" ), -2 ), false ); + } + + int addToDictionaryId = -42; + int ignoreId = -43; + if ( mSpeller && mSpeller->status() == KSpell::Running ) { + p.insertSeparator(); + addToDictionaryId = p.insertItem( i18n( "Add to Dictionary" ) ); + ignoreId = p.insertItem( i18n( "Ignore All" ) ); } //Execute the popup inline - int id = p.exec( mapToGlobal( event->pos() ) ); + const int id = p.exec( mapToGlobal( event->pos() ) ); - if( id > -1 ) + if ( id == ignoreId ) { + mHighlighter->ignoreWord( word ); + mHighlighter->rehighlight(); + } + if ( id == addToDictionaryId ) { + mSpeller->addPersonal( word ); + mSpeller->writePersonalDictionary(); + if ( mHighlighter ) { + // Wait a bit until reloading the highlighter, the mSpeller first needs to finish saving + // the personal word list. + TQTimer::singleShot( 200, mHighlighter, TQT_SLOT( slotLocalSpellConfigChanged() ) ); + } + } + else if( id > -1 ) { //Save the cursor position int parIdx = 1, txtIdx = 1; @@ -472,6 +559,12 @@ bool KMEdit::eventFilter(TQObject*o, TQEvent* e) txtIdx += mReplacements[word][id].length() - word.length(); setCursorPosition(parIdx, txtIdx); } + + if ( id == addToDictionaryId || id == ignoreId ) { + // No longer misspelled: Either added to dictionary or ignored + mReplacements.remove( word ); + } + //Cancel original event return true; } @@ -494,10 +587,10 @@ int KMEdit::autoSpellChecking( bool on ) KMessageBox::sorry(this, i18n("Automatic spellchecking is not possible on text with markup.")); return -1; } - if ( mSpellChecker ) { + if ( mHighlighter ) { // don't autoEnable spell checking if the user turned spell checking off - mSpellChecker->setAutomatic( on ); - mSpellChecker->setActive( on ); + mHighlighter->setAutomatic( on ); + mHighlighter->setActive( on ); } return 1; } @@ -551,54 +644,57 @@ bool KMEdit::checkExternalEditorFinished() { void KMEdit::spellcheck() { - if ( mKSpell ) + if ( mKSpellForDialog ) return; mWasModifiedBeforeSpellCheck = isModified(); mSpellLineEdit = !mSpellLineEdit; // maybe for later, for now plaintext is given to KSpell // if (textFormat() == Qt::RichText ) { // kdDebug(5006) << "KMEdit::spellcheck, spellchecking for RichText" << endl; -// mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this, +// mKSpellForDialog = new KSpell(this, i18n("Spellcheck - KMail"), this, // TQT_SLOT(slotSpellcheck2(KSpell*)),0,true,false,KSpell::HTML); // } // else { - mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this, - TQT_SLOT(slotSpellcheck2(KSpell*))); + + // Don't use mSpellConfig here. Reason is that the spell dialog, KSpellDlg, uses its own + // spell config, and therefore the two wouldn't be in sync. + mKSpellForDialog = new KSpell( this, i18n("Spellcheck - KMail"), this, + TQT_SLOT(slotSpellcheck2(KSpell*))/*, mSpellConfig*/ ); // } TQStringList l = KSpellingHighlighter::personalWords(); for ( TQStringList::Iterator it = l.begin(); it != l.end(); ++it ) { - mKSpell->addPersonal( *it ); + mKSpellForDialog->addPersonal( *it ); } - connect (mKSpell, TQT_SIGNAL( death()), + connect (mKSpellForDialog, TQT_SIGNAL( death()), this, TQT_SLOT (slotSpellDone())); - connect (mKSpell, TQT_SIGNAL (misspelling (const TQString &, const TQStringList &, unsigned int)), + connect (mKSpellForDialog, TQT_SIGNAL (misspelling (const TQString &, const TQStringList &, unsigned int)), this, TQT_SLOT (slotMisspelling (const TQString &, const TQStringList &, unsigned int))); - connect (mKSpell, TQT_SIGNAL (corrected (const TQString &, const TQString &, unsigned int)), + connect (mKSpellForDialog, TQT_SIGNAL (corrected (const TQString &, const TQString &, unsigned int)), this, TQT_SLOT (slotCorrected (const TQString &, const TQString &, unsigned int))); - connect (mKSpell, TQT_SIGNAL (done(const TQString &)), + connect (mKSpellForDialog, TQT_SIGNAL (done(const TQString &)), this, TQT_SLOT (slotSpellResult (const TQString&))); } void KMEdit::cut() { KEdit::cut(); - if ( textFormat() != Qt::RichText && mSpellChecker ) - mSpellChecker->restartBackgroundSpellCheck(); + if ( textFormat() != Qt::RichText && mHighlighter ) + mHighlighter->restartBackgroundSpellCheck(); } void KMEdit::clear() { KEdit::clear(); - if ( textFormat() != Qt::RichText && mSpellChecker ) - mSpellChecker->restartBackgroundSpellCheck(); + if ( textFormat() != Qt::RichText && mHighlighter ) + mHighlighter->restartBackgroundSpellCheck(); } void KMEdit::del() { KEdit::del(); - if ( textFormat() != Qt::RichText && mSpellChecker ) - mSpellChecker->restartBackgroundSpellCheck(); + if ( textFormat() != Qt::RichText && mHighlighter ) + mHighlighter->restartBackgroundSpellCheck(); } void KMEdit::paste() @@ -620,6 +716,54 @@ void KMEdit::contentsMouseReleaseEvent( TQMouseEvent * e ) mPasteMode = QClipboard::Clipboard; } +void KMEdit::contentsMouseDoubleClickEvent( TQMouseEvent *e ) +{ + bool handled = false; + if ( e->button() == TQt::LeftButton ) { + + // Get the cursor position for the place where the user clicked to + int paragraphPos; + int charPos = charAt ( e->pos(), ¶graphPos ); + TQString paraText = text( paragraphPos ); + + // Now select the word under the cursor + if ( charPos >= 0 && static_cast( charPos ) <= paraText.length() ) { + + // Start the selection where the user clicked + int start = charPos; + unsigned int end = charPos; + + // Extend the selection to the left, until we reach a non-letter and non-digit char + for (;;) { + if ( ( start - 1 ) < 0 ) + break; + TQChar charToTheLeft = paraText.at( start - 1 ); + if ( charToTheLeft.isLetter() || charToTheLeft.isDigit() ) + start--; + else + break; + } + + // Extend the selection to the left, until we reach a non-letter and non-digit char + for (;;) { + if ( ( end + 1 ) >= paraText.length() ) + break; + TQChar charToTheRight = paraText.at( end + 1 ); + if ( charToTheRight.isLetter() || charToTheRight.isDigit() ) + end++; + else + break; + } + + setSelection( paragraphPos, start, paragraphPos, end + 1 ); + handled = true; + } + } + + if ( !handled ) + return KEdit::contentsMouseDoubleClickEvent( e ); +} + void KMEdit::slotMisspelling(const TQString &text, const TQStringList &lst, unsigned int pos) { kdDebug(5006)<<"void KMEdit::slotMisspelling(const TQString &text, const TQStringList &lst, unsigned int pos) : "<ignoredWords().size(); i++ ) + mKSpellForDialog->ignore( mHighlighter->ignoredWords()[i] ); + } + if( !mSpellLineEdit) { spellcheck_start(); @@ -682,10 +832,10 @@ void KMEdit::slotSpellcheck2(KSpell*) mSpellingFilter = new SpellingFilter(plaintext.text(), quotePrefix, SpellingFilter::FilterUrls, SpellingFilter::FilterEmailAddresses); - mKSpell->check(mSpellingFilter->filteredText()); + mKSpellForDialog->check(mSpellingFilter->filteredText()); } else if( mComposer ) - mKSpell->check( mComposer->sujectLineWidget()->text()); + mKSpellForDialog->check( mComposer->sujectLineWidget()->text()); } void KMEdit::slotSpellResult(const TQString &s) @@ -693,7 +843,7 @@ void KMEdit::slotSpellResult(const TQString &s) if( !mSpellLineEdit) spellcheck_stop(); - int dlgResult = mKSpell->dlgResult(); + int dlgResult = mKSpellForDialog->dlgResult(); if ( dlgResult == KS_CANCEL ) { if( mSpellLineEdit) @@ -711,7 +861,7 @@ void KMEdit::slotSpellResult(const TQString &s) setModified(true); } } - mKSpell->cleanUp(); + mKSpellForDialog->cleanUp(); KDictSpellingHighlighter::dictionaryChanged(); emit spellcheck_done( dlgResult ); @@ -720,9 +870,9 @@ void KMEdit::slotSpellResult(const TQString &s) void KMEdit::slotSpellDone() { kdDebug(5006)<<" void KMEdit::slotSpellDone()\n"; - KSpell::spellStatus status = mKSpell->status(); - delete mKSpell; - mKSpell = 0; + KSpell::spellStatus status = mKSpellForDialog->status(); + delete mKSpellForDialog; + mKSpellForDialog = 0; kdDebug(5006) << "spelling: delete SpellingFilter" << endl; delete mSpellingFilter; @@ -763,4 +913,20 @@ void KMEdit::setCursorPositionFromStart( unsigned int pos ) { ensureCursorVisible(); } +int KMEdit::indexOfCurrentLineStart( int paragraph, int index ) +{ + Q_ASSERT( paragraph >= 0 && paragraph < paragraphs() ); + Q_ASSERT( index >= 0 && ( index == 0 || index < paragraphLength( paragraph ) ) ); + + const int startLine = lineOfChar( paragraph, index ); + Q_ASSERT( startLine >= 0 && startLine < linesOfParagraph( paragraph ) ); + for ( int curIndex = index; curIndex >= 0; curIndex-- ) { + const int line = lineOfChar( paragraph, curIndex ); + if ( line != startLine ) { + return curIndex + 1; + } + } + return 0; +} + #include "kmedit.moc" diff --git a/kmail/kmedit.h b/kmail/kmedit.h index b8419cda..855c29ea 100644 --- a/kmail/kmedit.h +++ b/kmail/kmedit.h @@ -7,20 +7,61 @@ #include #include +#include +#include #include #include #include class KMComposeWin; class KSpellConfig; -class KSpell; class SpellingFilter; class KTempFile; -class KDictSpellingHighlighter; class KDirWatch; class KProcess; class TQPopupMenu; +/** + * Reimplemented to make writePersonalDictionary() public, which we call everytime after + * adding a word to the dictionary (for safety's sake and because the highlighter needs to reload + * the personal word list, and for that, it needs to be written to disc) + */ +class KMSpell : public KSpell +{ + public: + + KMSpell( TQObject *receiver, const char *slot, KSpellConfig *spellConfig ); + using KSpell::writePersonalDictionary; +}; + +/** + * Reimplemented to add support for ignored words + */ +class KMSyntaxHighter : public KDictSpellingHighlighter +{ + public: + + KMSyntaxHighter( TQTextEdit *textEdit, + bool spellCheckingActive = true, + bool autoEnable = true, + const TQColor& spellColor = red, + bool colorQuoting = false, + const TQColor& QuoteColor0 = black, + const TQColor& QuoteColor1 = TQColor( 0x00, 0x80, 0x00 ), + const TQColor& QuoteColor2 = TQColor( 0x00, 0x70, 0x00 ), + const TQColor& QuoteColor3 = TQColor( 0x00, 0x60, 0x00 ), + KSpellConfig *spellConfig = 0 ); + + /** Reimplemented */ + virtual bool isMisspelled( const TQString &word ); + + void ignoreWord( const TQString &word ); + + TQStringList ignoredWords() const; + + private: + TQStringList mIgnoredWords; +}; class KMEdit : public KEdit { Q_OBJECT @@ -74,12 +115,15 @@ public: /** set cursor to absolute position pos */ void setCursorPositionFromStart(unsigned int pos); + int indexOfCurrentLineStart( int paragraph, int index ); + signals: void spellcheck_done(int result); void attachPNGImageData(const TQByteArray &image); void pasteImage(); void focusUp(); void focusChanged( bool ); + void selectionAvailable( bool ); void insertSnippet(); public slots: void initializeAutoSpellChecking(); @@ -100,11 +144,29 @@ protected: */ bool eventFilter(TQObject*, TQEvent*); void keyPressEvent( TQKeyEvent* ); - + void contentsMouseReleaseEvent( TQMouseEvent * e ); + /// Reimplemented to select words under the cursor on double-clicks in our way, + /// not the broken TQt way (https://issues.kolab.org/issue4089) + virtual void contentsMouseDoubleClickEvent( TQMouseEvent *e ); + private slots: void slotExternalEditorTempFileChanged( const TQString & fileName ); + void slotSelectionChanged() { + // use !text.isEmpty() here, as null-selections exist, but make no sense + emit selectionAvailable( !selectedText().isEmpty() ); + } + + /// Called when mSpeller is ready to rumble. Does nothing, but KSpell requires a slot as otherwise + /// it will show a dialog itself, which we want to avoid. + void spellerReady( KSpell *speller ); + + /// Called when mSpeller died for some reason. + void spellerDied(); + + /// Re-creates the spellers, called when the dictionary is changed + void createSpellers(); private: void killExternalEditor(); @@ -112,7 +174,14 @@ private: private: KMComposeWin* mComposer; - KSpell *mKSpell; + // This is the speller used for the spellcheck dialog. It is only active as long as the spellcheck + // dialog is shown + KSpell *mKSpellForDialog; + + // This is the speller used when right-clicking a word and choosing "add to dictionary". It lives + // as long as the composer lives. + KMSpell *mSpeller; + KSpellConfig *mSpellConfig; TQMap mReplacements; SpellingFilter* mSpellingFilter; @@ -122,7 +191,7 @@ private: bool mUseExtEditor; TQString mExtEditor; bool mWasModifiedBeforeSpellCheck; - KDictSpellingHighlighter *mSpellChecker; + KMSyntaxHighter *mHighlighter; bool mSpellLineEdit; QClipboard::Mode mPasteMode; }; diff --git a/kmail/kmfawidgets.cpp b/kmail/kmfawidgets.cpp index f5426f25..370d796c 100644 --- a/kmail/kmfawidgets.cpp +++ b/kmail/kmfawidgets.cpp @@ -17,6 +17,7 @@ #include #include +#include //============================================================================= // @@ -30,14 +31,18 @@ KMFilterActionWithAddressWidget::KMFilterActionWithAddressWidget( TQWidget* pare TQHBoxLayout *hbl = new TQHBoxLayout(this); hbl->setSpacing(4); mLineEdit = new KLineEdit(this); + mLineEdit->setName( "addressEdit" ); hbl->addWidget( mLineEdit, 1 /*stretch*/ ); mBtn = new TQPushButton( TQString::null ,this ); mBtn->setPixmap( BarIcon( "contents", KIcon::SizeSmall ) ); mBtn->setFixedHeight( mLineEdit->sizeHint().height() ); + TQToolTip::add( mBtn, i18n( "Open Address Book" ) ); hbl->addWidget( mBtn ); connect( mBtn, TQT_SIGNAL(clicked()), - this, TQT_SLOT(slotAddrBook()) ); + this, TQT_SLOT(slotAddrBook()) ); + connect( mLineEdit, TQT_SIGNAL( textChanged(const TQString&) ), + this, TQT_SIGNAL( textChanged(const TQString&) ) ); } void KMFilterActionWithAddressWidget::slotAddrBook() diff --git a/kmail/kmfawidgets.h b/kmail/kmfawidgets.h index 716cd2aa..5a6990a8 100644 --- a/kmail/kmfawidgets.h +++ b/kmail/kmfawidgets.h @@ -25,6 +25,10 @@ public: TQString text() const { return mLineEdit->text(); } void setText( const TQString & aString ) { mLineEdit->setText( aString ); } +signals: + // Forwarded from the internal text edit + void textChanged(const TQString&); + protected slots: void slotAddrBook(); diff --git a/kmail/kmfilteraction.cpp b/kmail/kmfilteraction.cpp index 9fce0565..9b277d8e 100644 --- a/kmail/kmfilteraction.cpp +++ b/kmail/kmfilteraction.cpp @@ -9,6 +9,8 @@ #include "kmfilteraction.h" +#include "customtemplates.h" +#include "customtemplates_kfg.h" #include "kmcommands.h" #include "kmmsgpart.h" #include "kmfiltermgr.h" @@ -27,7 +29,6 @@ using KPIM::CollectingProcess; #include "folderrequester.h" using KMail::FolderRequester; #include "kmmsgbase.h" -#include "templateparser.h" #include "messageproperty.h" #include "actionscheduler.h" using KMail::MessageProperty; @@ -48,6 +49,8 @@ using KMail::RegExpLineEdit; #include #include #include +#include +#include #include @@ -1433,14 +1436,26 @@ bool KMFilterActionCopy::requiresBody(KMMsgBase*) const //============================================================================= // KMFilterActionForward - forward to -// Forward message to another user +// Forward message to another user, with a defined template //============================================================================= class KMFilterActionForward: public KMFilterActionWithAddress { public: KMFilterActionForward(); - virtual ReturnCode process(KMMessage* msg) const; + virtual ReturnCode process( KMMessage* msg ) const; + virtual TQWidget* createParamWidget( TQWidget* parent ) const; + virtual void applyParamWidgetValue( TQWidget* paramWidget ); + virtual void setParamWidgetValue( TQWidget* paramWidget ) const; + virtual void clearParamWidget( TQWidget* paramWidget ) const; + virtual void argsFromString( const TQString argsStr ); + virtual const TQString argsAsString() const; + virtual const TQString displayString() const; + static KMFilterAction* newAction(void); + +private: + + mutable TQString mTemplate; }; KMFilterAction* KMFilterActionForward::newAction(void) @@ -1460,89 +1475,152 @@ KMFilterAction::ReturnCode KMFilterActionForward::process(KMMessage* aMsg) const // avoid endless loops when this action is used in a filter // which applies to sent messages - if ( KMMessage::addressIsInAddressList( mParameter, aMsg->to() ) ) + if ( KMMessage::addressIsInAddressList( mParameter, aMsg->to() ) ) { + kdWarning(5006) << "Attempt to forward to receipient of original message, ignoring." << endl; return ErrorButGoOn; + } + + KMMessage *fwdMsg = aMsg->createForward( mTemplate ); + fwdMsg->setTo( fwdMsg->to() + ',' + mParameter ); + + if ( !kmkernel->msgSender()->send( fwdMsg, KMail::MessageSender::SendDefault ) ) { + kdWarning(5006) << "KMFilterAction: could not forward message (sending failed)" << endl; + return ErrorButGoOn; // error: couldn't send + } + else + sendMDN( aMsg, KMime::MDN::Dispatched ); - // Create the forwarded message by hand to make forwarding of messages with - // attachments work. - // Note: This duplicates a lot of code from KMMessage::createForward() and - // KMComposeWin::applyChanges(). - // ### FIXME: Remove the code duplication again. + // (the msgSender takes ownership of the message, so don't delete it here) - KMMessage* msg = new KMMessage; + return GoOn; +} - msg->initFromMessage( aMsg ); +TQWidget* KMFilterActionForward::createParamWidget( TQWidget* parent ) const +{ + TQWidget *addressAndTemplate = new TQWidget( parent ); + TQHBoxLayout *hBox = new TQHBoxLayout( addressAndTemplate ); + TQWidget *addressEdit = KMFilterActionWithAddress::createParamWidget( addressAndTemplate ); + addressEdit->setName( "addressEdit" ); + hBox->addWidget( addressEdit ); - // TQString st = TQString::fromUtf8( aMsg->createForwardBody() ); + KLineEdit *lineEdit = dynamic_cast( addressEdit->child( "addressEdit" ) ); + Q_ASSERT( lineEdit ); + TQToolTip::add( lineEdit, i18n( "The addressee the message will be forwarded to" ) ); + TQWhatsThis::add( lineEdit, i18n( "The filter will forward the message to the addressee entered here." ) ); - TemplateParser parser( msg, TemplateParser::Forward, - aMsg->body(), false, false, false, false); - parser.process( aMsg ); + TQComboBox *templateCombo = new TQComboBox( addressAndTemplate ); + templateCombo->setName( "templateCombo" ); + hBox->addWidget( templateCombo ); - QCString - encoding = KMMsgBase::autoDetectCharset( aMsg->charset(), - KMMessage::preferredCharsets(), - msg->body() ); - if( encoding.isEmpty() ) - encoding = "utf-8"; - TQCString str = KMMsgBase::codecForName( encoding )->fromUnicode( msg->body() ); + templateCombo->insertItem( i18n( "Default Template" ) ); + TQStringList templateNames = GlobalSettingsBase::self()->customTemplates(); + for ( TQStringList::const_iterator it = templateNames.begin(); it != templateNames.end(); + it++ ) { + CTemplates templat( *it ); + if ( templat.type() == CustomTemplates::TForward || + templat.type() == CustomTemplates::TUniversal ) + templateCombo->insertItem( *it ); + } + templateCombo->setEnabled( templateCombo->count() > 1 ); + TQToolTip::add( templateCombo, i18n( "The template used when forwarding" ) ); + TQWhatsThis::add( templateCombo, i18n( "Set the forwarding template that will be used with this filter." ) ); - msg->setCharset( encoding ); - msg->setTo( mParameter ); - msg->setSubject( "Fwd: " + aMsg->subject() ); + return addressAndTemplate; +} - bool isQP = kmkernel->msgSender()->sendQuotedPrintable(); +void KMFilterActionForward::applyParamWidgetValue( TQWidget* paramWidget ) +{ + // Use findChildren when porting to KDE 4 + TQWidget *addressEdit = dynamic_cast( paramWidget->child( "addressEdit" ) ); + Q_ASSERT( addressEdit ); + KMFilterActionWithAddress::applyParamWidgetValue( addressEdit ); - if( aMsg->numBodyParts() == 0 ) - { - msg->setAutomaticFields( true ); - msg->setHeaderField( "Content-Type", "text/plain" ); - // msg->setCteStr( isQP ? "quoted-printable": "8bit" ); - TQValueList dummy; - msg->setBodyAndGuessCte(str, dummy, !isQP); - msg->setCharset( encoding ); - if( isQP ) - msg->setBodyEncoded( str ); - else - msg->setBody( str ); + TQComboBox *templateCombo = dynamic_cast( paramWidget->child( "templateCombo" ) ); + Q_ASSERT( templateCombo ); + + if ( templateCombo->currentItem() == 0 ) { + // Default template, so don't use a custom one + mTemplate = TQString::null; } - else - { - KMMessagePart bodyPart, msgPart; - - msg->removeHeaderField( "Content-Type" ); - msg->removeHeaderField( "Content-Transfer-Encoding" ); - msg->setAutomaticFields( true ); - msg->setBody( "This message is in MIME format.\n\n" ); - - bodyPart.setTypeStr( "text" ); - bodyPart.setSubtypeStr( "plain" ); - // bodyPart.setCteStr( isQP ? "quoted-printable": "8bit" ); - TQValueList dummy; - bodyPart.setBodyAndGuessCte(str, dummy, !isQP); - bodyPart.setCharset( encoding ); - bodyPart.setBodyEncoded( str ); - msg->addBodyPart( &bodyPart ); - - for( int i = 0; i < aMsg->numBodyParts(); i++ ) - { - aMsg->bodyPart( i, &msgPart ); - if( i > 0 || qstricmp( msgPart.typeStr(), "text" ) != 0 ) - msg->addBodyPart( &msgPart ); + else { + mTemplate = templateCombo->currentText(); + } +} + +void KMFilterActionForward::setParamWidgetValue( TQWidget* paramWidget ) const +{ + TQWidget *addressEdit = dynamic_cast( paramWidget->child( "addressEdit" ) ); + Q_ASSERT( addressEdit ); + KMFilterActionWithAddress::setParamWidgetValue( addressEdit ); + + TQComboBox *templateCombo = dynamic_cast( paramWidget->child( "templateCombo" ) ); + Q_ASSERT( templateCombo ); + + if ( mTemplate.isEmpty() ) { + templateCombo->setCurrentItem( 0 ); + } + else { + // WTF: TQt3's combobox has no indexOf? Search it manually, then. + int templateIndex = -1; + for ( int i = 1; i < templateCombo->count(); i++ ) { + if ( templateCombo->text( i ) == mTemplate ) { + templateIndex = i; + break; + } + } + + if ( templateIndex != -1 ) { + templateCombo->setCurrentItem( templateIndex ); + } + else { + mTemplate = TQString::null; } } - msg->cleanupHeader(); - msg->link( aMsg, KMMsgStatusForwarded ); +} - sendMDN( aMsg, KMime::MDN::Dispatched ); +void KMFilterActionForward::clearParamWidget( TQWidget* paramWidget ) const +{ + TQWidget *addressEdit = dynamic_cast( paramWidget->child( "addressEdit" ) ); + Q_ASSERT( addressEdit ); + KMFilterActionWithAddress::clearParamWidget( addressEdit ); - if ( !kmkernel->msgSender()->send( msg, KMail::MessageSender::SendLater ) ) { - kdDebug(5006) << "KMFilterAction: could not forward message (sending failed)" << endl; - return ErrorButGoOn; // error: couldn't send + TQComboBox *templateCombo = dynamic_cast( paramWidget->child( "templateCombo" ) ); + Q_ASSERT( templateCombo ); + + templateCombo->setCurrentItem( 0 ); +} + +// We simply place a "@$$@" between the two parameters. The template is the last +// parameter in the string, for compatibility reasons. +static const TQString forwardFilterArgsSeperator = "@$$@"; + +void KMFilterActionForward::argsFromString( const TQString argsStr ) +{ + int seperatorPos = argsStr.find( forwardFilterArgsSeperator ); + + if ( seperatorPos == - 1 ) { + // Old config, assume that the whole string is the addressee + KMFilterActionWithAddress::argsFromString( argsStr ); } - return GoOn; + else { + TQString addressee = argsStr.left( seperatorPos ); + mTemplate = argsStr.mid( seperatorPos + forwardFilterArgsSeperator.length() ); + KMFilterActionWithAddress::argsFromString( addressee ); + } +} + +const TQString KMFilterActionForward::argsAsString() const +{ + return KMFilterActionWithAddress::argsAsString() + forwardFilterArgsSeperator + mTemplate; } +const TQString KMFilterActionForward::displayString() const +{ + if ( mTemplate.isEmpty() ) + return i18n( "Forward to %1 with default template " ).arg( mParameter ); + else + return i18n( "Forward to %1 with template %2" ).arg( mParameter, mTemplate ); +} //============================================================================= // KMFilterActionRedirect - redirect to diff --git a/kmail/kmfilterdlg.cpp b/kmail/kmfilterdlg.cpp index c5589c94..558031f0 100644 --- a/kmail/kmfilterdlg.cpp +++ b/kmail/kmfilterdlg.cpp @@ -15,6 +15,8 @@ using KMail::AccountManager; #include "filterimporterexporter.h" using KMail::FilterImporterExporter; +#include "foldersetselector.h" +#include "globalsettings.h" // other KDE headers: #include @@ -45,6 +47,8 @@ using KMail::FilterImporterExporter; // other headers: #include +using namespace KMail; + // What's this help texts const char * _wt_filterlist = @@ -634,6 +638,14 @@ KMFilterListBox::KMFilterListBox( const TQString & title, TQWidget *parent, cons TQWhatsThis::add( mBtnDelete, i18n(_wt_filterlist_delete) ); TQWhatsThis::add( mBtnRename, i18n(_wt_filterlist_rename) ); + // third row + if ( !popFilter ) { + hb = new TQHBox( this ); + hb->setSpacing( 4 ); + TQPushButton *btn = new TQPushButton( i18n("Select Source Folders"), hb ); + connect( btn, TQT_SIGNAL(clicked()), TQT_SLOT(slotSelectSourceFolders()) ); + } + //----------- now connect everything connect( mListBox, TQT_SIGNAL(highlighted(int)), @@ -699,7 +711,7 @@ void KMFilterListBox::slotUpdateFilterName() if ( mFilterList.at(mIdxSelItem)->isAutoNaming() ) { // auto-naming of patterns - if ( p->first() && !p->first()->field().stripWhiteSpace().isEmpty() ) + if ( !p->isEmpty() && p->first() && !p->first()->field().stripWhiteSpace().isEmpty() ) shouldBeName = TQString( "<%1>: %2" ).arg( p->first()->field() ).arg( p->first()->contents() ); else shouldBeName = "<" + i18n("unnamed") + ">"; @@ -955,6 +967,17 @@ void KMFilterListBox::slotRename() slotUpdateFilterName(); } +void KMFilterListBox::slotSelectSourceFolders() +{ + FolderSetSelector dlg( kmkernel->getKMMainWidget()->folderTree(), this ); + dlg.setCaption( i18n( "Select Folders to Filter" ) ); + if ( !GlobalSettings::filterSourceFolders().isEmpty() ) + dlg.setSelectedFolders( GlobalSettings::filterSourceFolders() ); + if ( dlg.exec() == TQDialog::Accepted ) { + GlobalSettings::setFilterSourceFolders( dlg.selectedFolders() ); + } +} + void KMFilterListBox::enableControls() { bool theFirst = ( mIdxSelItem == 0 ); diff --git a/kmail/kmfilterdlg.h b/kmail/kmfilterdlg.h index 1ab185ca..18da9b56 100644 --- a/kmail/kmfilterdlg.h +++ b/kmail/kmfilterdlg.h @@ -149,6 +149,8 @@ protected slots: dialog prompting to enter the new name. */ void slotRename(); + void slotSelectSourceFolders(); + protected: /** The deep copy of the filter list. */ TQPtrList mFilterList; diff --git a/kmail/kmfiltermgr.cpp b/kmail/kmfiltermgr.cpp index 13e5e0f1..beb3e60a 100644 --- a/kmail/kmfiltermgr.cpp +++ b/kmail/kmfiltermgr.cpp @@ -42,8 +42,6 @@ KMFilterMgr::KMFilterMgr( bool popFilter ) mBufferedFolderTarget( true ), mRefCount( 0 ) { - if (bPopFilter) - kdDebug(5006) << "pPopFilter set" << endl; connect( kmkernel, TQT_SIGNAL( folderRemoved( KMFolder* ) ), this, TQT_SLOT( slotFolderRemoved( KMFolder* ) ) ); } diff --git a/kmail/kmfolder.cpp b/kmail/kmfolder.cpp index cae87071..68fb52f0 100644 --- a/kmail/kmfolder.cpp +++ b/kmail/kmfolder.cpp @@ -124,6 +124,10 @@ KMFolder::KMFolder( KMFolderDir* aParent, const TQString& aFolderName, TQT_SIGNAL( numUnreadMsgsChanged( KMFolder* ) ) ); connect( mStorage, TQT_SIGNAL( removed( KMFolder*, bool ) ), TQT_SIGNAL( removed( KMFolder*, bool ) ) ); + connect( mStorage, TQT_SIGNAL(noContentChanged()), + TQT_SIGNAL(noContentChanged()) ); + connect( mStorage, TQT_SIGNAL(syncStateChanged()), + TQT_SIGNAL(syncStateChanged()) ); connect( mStorage, TQT_SIGNAL( contentsTypeChanged( KMail::FolderContentsType ) ), this, TQT_SLOT( slotContentsTypeChanged( KMail::FolderContentsType ) ) ); @@ -559,6 +563,21 @@ bool KMFolder::isReadOnly() const return mStorage->isReadOnly(); } +bool KMFolder::mailCheckInProgress() const +{ + return mStorage->mailCheckInProgress(); +} + +bool KMFolder::isWritable() const +{ + return !mStorage->isReadOnly() && mStorage->canDeleteMessages(); +} + +bool KMFolder::canDeleteMessages() const +{ + return mStorage->canDeleteMessages(); +} + TQString KMFolder::label() const { if ( !mSystemLabel.isEmpty() ) @@ -877,5 +896,44 @@ void KMFolder::slotFolderSizeChanged() } } +bool KMFolder::isValidName( const TQString &folderName, TQString &message ) +{ + KMFolderType fldType = folderType(); + + // names of local folders must not contain a '/' + if ( folderName.find( '/' ) != -1 && + fldType != KMFolderTypeImap && + fldType != KMFolderTypeCachedImap ) { + message = i18n( "Folder names cannot contain the / (slash) character; please choose another folder name." ); + return false; + } + + // folder names must not start with a '.' + if ( folderName.startsWith( "." ) ) { + message = i18n( "Folder names cannot start with a . (dot) character; please choose another folder name." ); + return false; + } + + // names of IMAP folders must not contain the folder delimiter + if ( fldType == KMFolderTypeImap || fldType == KMFolderTypeCachedImap ) { + TQString delimiter; + if ( fldType == KMFolderTypeImap ) { + KMAcctImap *ai = static_cast( mStorage )->account(); + if ( ai ) { + delimiter = ai->delimiterForFolder( mStorage ); + } + } else { + KMAcctCachedImap *ai = static_cast( mStorage )->account(); + if ( ai ) { + delimiter = ai->delimiterForFolder( mStorage ); + } + } + if ( !delimiter.isEmpty() && folderName.find( delimiter ) != -1 ) { + message = i18n( "Your IMAP server does not allow the character '%1'; please choose another folder name." ).arg( delimiter ); + return false; + } + } + return true; +} #include "kmfolder.moc" diff --git a/kmail/kmfolder.h b/kmail/kmfolder.h index 32518cd7..3ff1d67d 100644 --- a/kmail/kmfolder.h +++ b/kmail/kmfolder.h @@ -353,6 +353,13 @@ public: /** Is the folder read-only? */ bool isReadOnly() const; + /** Can we write into and delete from this folder (on IMAP that's not necessarily !isReadOnly()) */ + bool isWritable() const; + + bool mailCheckInProgress() const; + + /** Can messages in this folder be deleted? */ + bool canDeleteMessages() const; /** Returns true if the folder is a kmail system folder. These are the folders 'inbox', 'outbox', 'sent', 'trash', 'drafts', 'templates'. @@ -532,6 +539,13 @@ public: /** Sets the move-in-progress flag. */ void setMoveInProgress( bool b ) { mMoveInProgress = b; } + /** + * Returns true if the name is valid for a child of this folder. + * If the name contains invalid characters then false is returned and message will contain + * an explanation that can be presented to the user. + */ + bool isValidName( const TQString &folderName, TQString &message ); + signals: /** Emitted when the status, name, or associated accounts of this folder changed. */ @@ -590,6 +604,15 @@ signals: /** Emitted when the folder's size changes. */ void folderSizeChanged( KMFolder * ); + /** Emitted when the no content state changed. */ + void noContentChanged(); + + /** + * Emiitted when the sync state, i.e. mailCheckInProgress(), changes. + * Currently only supported for disconnected IMAP. + */ + void syncStateChanged(); + public slots: /** Incrementally update the index if possible else call writeIndex */ int updateIndex(); diff --git a/kmail/kmfoldercachedimap.cpp b/kmail/kmfoldercachedimap.cpp index 05f01b77..a1c71726 100644 --- a/kmail/kmfoldercachedimap.cpp +++ b/kmail/kmfoldercachedimap.cpp @@ -129,11 +129,11 @@ DImapTroubleShootDialog::DImapTroubleShootDialog( TQWidget* parent, "and all its subfolders.

" ); topLayout->addWidget( new TQLabel( txt, page ) ); - TQButtonGroup *group = new TQButtonGroup( 0 ); + mButtonGroup = new TQButtonGroup( 0 ); mIndexButton = new TQRadioButton( page ); mIndexButton->setText( i18n( "Rebuild &Index" ) ); - group->insert( mIndexButton ); + mButtonGroup->insert( mIndexButton ); topLayout->addWidget( mIndexButton ); TQHBox *hbox = new TQHBox( page ); @@ -148,15 +148,16 @@ DImapTroubleShootDialog::DImapTroubleShootDialog( TQWidget* parent, mCacheButton = new TQRadioButton( page ); mCacheButton->setText( i18n( "Refresh &Cache" ) ); - group->insert( mCacheButton ); + mButtonGroup->insert( mCacheButton ); topLayout->addWidget( mCacheButton ); enableButtonSeparator( true ); connect ( mIndexButton, TQT_SIGNAL(toggled(bool)), mIndexScope, TQT_SLOT(setEnabled(bool)) ); connect ( mIndexButton, TQT_SIGNAL(toggled(bool)), scopeLabel, TQT_SLOT(setEnabled(bool)) ); - + connect( mButtonGroup, TQT_SIGNAL( clicked( int ) ), TQT_SLOT( slotChanged() ) ); connect( this, TQT_SIGNAL( okClicked () ), this, TQT_SLOT( slotDone() ) ); + enableButtonOK( false ); } int DImapTroubleShootDialog::run() @@ -166,6 +167,11 @@ int DImapTroubleShootDialog::run() return d.rc; } +void DImapTroubleShootDialog::slotChanged() +{ + enableButtonOK( mButtonGroup->selected() != 0 ); +} + void DImapTroubleShootDialog::slotDone() { rc = None; @@ -181,17 +187,24 @@ KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName ) mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ), mSubfolderState( imapNoInformation ), mIncidencesFor( IncForAdmins ), + mSharedSeenFlags( false ), mIsSelected( false ), mCheckFlags( true ), mReadOnly( false ), mAccount( NULL ), uidMapDirty( true ), uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ), mFoundAnIMAPDigest( false ), - mUserRights( 0 ), mOldUserRights( 0 ), mSilentUpload( false ), + mUserRights( 0 ), mOldUserRights( 0 ), mUserRightsState( KMail::ACLJobs::NotFetchedYet ), + mACLListState( KMail::ACLJobs::NotFetchedYet ), + mSilentUpload( false ), /*mHoldSyncs( false ),*/ mFolderRemoved( false ), mRecurse( true ), - mStatusChangedLocally( false ), mAnnotationFolderTypeChanged( false ), - mIncidencesForChanged( false ), mPersonalNamespacesCheckDone( true ), - mQuotaInfo(), mAlarmsBlocked( false ), + mQuotaOnly( false ), + mAnnotationFolderTypeChanged( false ), + mIncidencesForChanged( false ), + mSharedSeenFlagsChanged( false ), + mStatusChangedLocally( false ), + mPersonalNamespacesCheckDone( true ), + mQuotaInfo(), mSomeSubFolderCloseToQuotaChanged( false ), mAlarmsBlocked( false ), mRescueCommandCount( 0 ), mPermanentFlags( 31 ) // assume standard flags by default (see imap4/imapinfo.h for bit fields values) { @@ -215,6 +228,7 @@ KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName ) KMFolderCachedImap::~KMFolderCachedImap() { if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() ); + writeConfig(); } void KMFolderCachedImap::reallyDoClose( const char* owner ) @@ -231,7 +245,7 @@ void KMFolderCachedImap::initializeFrom( KMFolderCachedImap* parent ) // Now that we have an account, tell it that this folder was created: // if this folder was just removed, then we don't really want to remove it from the server. mAccount->removeDeletedFolder( imapPath() ); - setUserRights( parent->userRights() ); + setUserRights( parent->userRights(), parent->userRightsState() ); } void KMFolderCachedImap::readConfig() @@ -262,8 +276,11 @@ void KMFolderCachedImap::readConfig() mAlarmsBlocked = config->readBoolEntry( "AlarmsBlocked", false ); // kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath ) // << " readConfig: mIncidencesFor=" << mIncidencesFor << endl; + mSharedSeenFlags = config->readBoolEntry( "SharedSeenFlags", false ); - mUserRights = config->readNumEntry( "UserRights", 0 ); // default is we don't know + mUserRights = config->readNumEntry( "UserRights", 0 ); + mUserRightsState = static_cast( + config->readNumEntry( "UserRightsState", KMail::ACLJobs::NotFetchedYet ) ); mOldUserRights = mUserRights; int storageQuotaUsage = config->readNumEntry( "StorageQuotaUsage", -1 ); @@ -283,19 +300,25 @@ void KMFolderCachedImap::readConfig() mStatusChangedLocally = config->readBoolEntry( "StatusChangedLocally", false ); + TQStringList uidsChanged = config->readListEntry( "UIDStatusChangedLocally" ); + for ( TQStringList::iterator it = uidsChanged.begin(); it != uidsChanged.end(); it++ ) { + mUIDsOfLocallyChangedStatuses.insert( ( *it ).toUInt() ); + } mAnnotationFolderTypeChanged = config->readBoolEntry( "AnnotationFolderTypeChanged", false ); mIncidencesForChanged = config->readBoolEntry( "IncidencesForChanged", false ); + mSharedSeenFlagsChanged = config->readBoolEntry( "SharedSeenFlagsChanged", false ); + if ( mImapPath.isEmpty() ) { mImapPathCreation = config->readEntry("ImapPathCreation"); } - TQStringList uids = config->readListEntry( "UIDSDeletedSinceLastSync" ); + TQStringList delUids = config->readListEntry( "UIDSDeletedSinceLastSync" ); #if MAIL_LOSS_DEBUGGING kdDebug( 5006 ) << "READING IN UIDSDeletedSinceLastSync: " << folder()->prettyURL() << endl << uids << endl; #endif - for ( TQStringList::iterator it = uids.begin(); it != uids.end(); it++ ) { - mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0); + for ( TQStringList::iterator it = delUids.begin(); it != delUids.end(); it++ ) { + mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0); } } @@ -311,7 +334,15 @@ void KMFolderCachedImap::writeConfig() configGroup.writeEntry( "NoContent", mNoContent ); configGroup.writeEntry( "ReadOnly", mReadOnly ); configGroup.writeEntry( "FolderAttributes", mFolderAttributes ); - configGroup.writeEntry( "StatusChangedLocally", mStatusChangedLocally ); + + // StatusChangedLocally is always false, as we use UIDStatusChangedLocally now + configGroup.writeEntry( "StatusChangedLocally", false ); + TQStringList uidsToWrite; + for( std::set::iterator it = mUIDsOfLocallyChangedStatuses.begin(); + it != mUIDsOfLocallyChangedStatuses.end(); it++ ) { + uidsToWrite.append( TQString::number( (*it) ) ); + } + configGroup.writeEntry( "UIDStatusChangedLocally", uidsToWrite ); if ( !mImapPathCreation.isEmpty() ) { if ( mImapPath.isEmpty() ) { configGroup.writeEntry( "ImapPathCreation", mImapPathCreation ); @@ -346,7 +377,12 @@ void KMFolderCachedImap::writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig configGroup.writeEntry( "IncidencesForChanged", mIncidencesForChanged ); configGroup.writeEntry( "IncidencesFor", incidencesForToString( mIncidencesFor ) ); configGroup.writeEntry( "AlarmsBlocked", mAlarmsBlocked ); - configGroup.writeEntry( "UserRights", mUserRights ); + configGroup.writeEntry( "SharedSeenFlags", mSharedSeenFlags ); + configGroup.writeEntry( "SharedSeenFlagsChanged", mSharedSeenFlagsChanged ); + if ( mUserRightsState != KMail::ACLJobs::FetchFailed ) { // No point in overwriting valid results with invalid ones + configGroup.writeEntry( "UserRights", mUserRights ); + configGroup.writeEntry( "UserRightsState", mUserRightsState ); + } configGroup.deleteEntry( "StorageQuotaUsage"); configGroup.deleteEntry( "StorageQuotaRoot"); @@ -503,11 +539,22 @@ int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail, // Add the message rc = KMFolderMaildir::addMsg(msg, index_return); - if( newMail && ( imapPath() == "/INBOX/" || ( !GlobalSettings::self()->filterOnlyDIMAPInbox() - && (userRights() <= 0 || userRights() & ACLJobs::Administer ) + if( newMail && ( imapPath() == "/INBOX/" || + ( ( mUserRights != ACLJobs::Ok || userRights() & ACLJobs::Administer) && (contentsType() == ContentsTypeMail || GlobalSettings::self()->filterGroupwareFolders()) ) ) ) - // This is a new message. Filter it - mAccount->processNewMsg( msg ); + { + // This is a new message. Filter it - maybe + bool filter = false; + if ( GlobalSettings::filterSourceFolders().isEmpty() ) { + if ( imapPath() == "/INBOX/" ) + filter = true; + } else { + if ( GlobalSettings::filterSourceFolders().contains( folder()->id() ) ) + filter = true; + } + if ( filter ) + mAccount->processNewMsg( msg ); + } return rc; } @@ -534,6 +581,9 @@ void KMFolderCachedImap::rememberDeletion( int idx ) /* Reimplemented from KMFolderMaildir */ void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet) { + if ( contentsType() != ContentsTypeMail ) { + kdDebug(5006) << k_funcinfo << "Deleting message with idx " << idx << " in folder " << label() << endl; + } uidMapDirty = true; rememberDeletion( idx ); // Remove it from disk @@ -556,18 +606,20 @@ bool KMFolderCachedImap::canRemoveFolder() const { int KMFolderCachedImap::rename( const TQString& aName, KMFolderDir* /*aParent*/ ) { + if ( account() == 0 || imapPath().isEmpty() ) { + // This can happen when creating a folder and then renaming it without syncing before, + // see https://issues.kolab.org/issue3658 + TQString err = i18n("You must synchronize with the server before renaming IMAP folders."); + KMessageBox::error( 0, err ); + return -1; + } + TQString oldName = mAccount->renamedFolder( imapPath() ); if ( oldName.isEmpty() ) oldName = name(); if ( aName == oldName ) // Stupid user trying to rename it to it's old name :) return 0; - if( account() == 0 || imapPath().isEmpty() ) { // I don't think any of this can happen anymore - TQString err = i18n("You must synchronize with the server before renaming IMAP folders."); - KMessageBox::error( 0, err ); - return -1; - } - // Make the change appear to the user with setLabel, but we'll do the change // on the server during the next sync. The name() is the name at the time of // the last sync. Only rename if the new one is different. If it's the same, @@ -719,7 +771,7 @@ void KMFolderCachedImap::slotTroubleshoot() } } -void KMFolderCachedImap::serverSync( bool recurse ) +void KMFolderCachedImap::serverSync( bool recurse, bool quotaOnly ) { if( mSyncState != SYNC_STATE_INITIAL ) { if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset it to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ), TQString::null, i18n("Reset && Sync"), KStdGuiItem::cancel() ) == KMessageBox::Yes ) { @@ -728,6 +780,7 @@ void KMFolderCachedImap::serverSync( bool recurse ) } mRecurse = recurse; + mQuotaOnly = quotaOnly; assert( account() ); ProgressItem *progressItem = mAccount->mailCheckProgressItem(); @@ -756,31 +809,33 @@ void KMFolderCachedImap::serverSync( bool recurse ) TQString KMFolderCachedImap::state2String( int state ) const { switch( state ) { - case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL"; - case SYNC_STATE_GET_USERRIGHTS: return "SYNC_STATE_GET_USERRIGHTS"; - case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES"; - case SYNC_STATE_UPLOAD_FLAGS: return "SYNC_STATE_UPLOAD_FLAGS"; - case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS"; - case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS"; - case SYNC_STATE_LIST_NAMESPACES: return "SYNC_STATE_LIST_NAMESPACES"; - case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2"; - case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS"; - case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES"; - case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES"; - case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES"; - case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES"; - case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX"; - case SYNC_STATE_TEST_ANNOTATIONS: return "SYNC_STATE_TEST_ANNOTATIONS"; - case SYNC_STATE_GET_ANNOTATIONS: return "SYNC_STATE_GET_ANNOTATIONS"; - case SYNC_STATE_SET_ANNOTATIONS: return "SYNC_STATE_SET_ANNOTATIONS"; - case SYNC_STATE_GET_ACLS: return "SYNC_STATE_GET_ACLS"; - case SYNC_STATE_SET_ACLS: return "SYNC_STATE_SET_ACLS"; - case SYNC_STATE_GET_QUOTA: return "SYNC_STATE_GET_QUOTA"; - case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS"; - case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS"; - case SYNC_STATE_RENAME_FOLDER: return "SYNC_STATE_RENAME_FOLDER"; - case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY"; - default: return "Unknown state"; + case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL"; + case SYNC_STATE_GET_USERRIGHTS: return "SYNC_STATE_GET_USERRIGHTS"; + case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES"; + case SYNC_STATE_UPLOAD_FLAGS: return "SYNC_STATE_UPLOAD_FLAGS"; + case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS"; + case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS"; + case SYNC_STATE_LIST_NAMESPACES: return "SYNC_STATE_LIST_NAMESPACES"; + case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2"; + case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS"; + case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES"; + case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES"; + case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES"; + case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES"; + case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX"; + case SYNC_STATE_TEST_ANNOTATIONS: return "SYNC_STATE_TEST_ANNOTATIONS"; + case SYNC_STATE_GET_ANNOTATIONS: return "SYNC_STATE_GET_ANNOTATIONS"; + case SYNC_STATE_SET_ANNOTATIONS: return "SYNC_STATE_SET_ANNOTATIONS"; + case SYNC_STATE_GET_ACLS: return "SYNC_STATE_GET_ACLS"; + case SYNC_STATE_SET_ACLS: return "SYNC_STATE_SET_ACLS"; + case SYNC_STATE_GET_QUOTA: return "SYNC_STATE_GET_QUOTA"; + case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS"; + case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS"; + case SYNC_STATE_RENAME_FOLDER: return "SYNC_STATE_RENAME_FOLDER"; + case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY"; + case SYNC_STATE_CLOSE: return "SYNC_STATE_CLOSE"; + case SYNC_STATE_GET_SUBFOLDER_QUOTA: return "SYNC_STATE_GET_SUBFOLDER_QUOTA"; + default: return "Unknown state"; } } @@ -866,11 +921,14 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_GET_USERRIGHTS: + + // Now we have started the sync, emit changed() so that the folder tree can update the status + emit syncStateChanged(); //kdDebug(5006) << "===== Syncing " << ( mImapPath.isEmpty() ? label() : mImapPath ) << endl; mSyncState = SYNC_STATE_RENAME_FOLDER; - if( !noContent() && mAccount->hasACLSupport() ) { + if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) { // Check the user's own rights. We do this every time in case they changed. mOldUserRights = mUserRights; newState( mProgress, i18n("Checking permissions")); @@ -880,6 +938,14 @@ void KMFolderCachedImap::serverSyncInternal() break; } + else if ( !mQuotaOnly && noContent() && mAccount->hasACLSupport() ) { + // This is a no content folder. The server would simply say that mailbox does not exist when + // querying the rights for it. So pretend we have no rights. + mUserRights = 0; + mUserRightsState = KMail::ACLJobs::Ok; + writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); + } + case SYNC_STATE_RENAME_FOLDER: { mSyncState = SYNC_STATE_CHECK_UIDVALIDITY; @@ -890,7 +956,7 @@ void KMFolderCachedImap::serverSyncInternal() newState( mProgress, i18n("Renaming folder") ); CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this ); connect( job, TQT_SIGNAL( result(KMail::FolderJob *) ), this, TQT_SLOT( slotIncreaseProgress() ) ); - connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( serverSyncInternal() ) ); + connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( slotRenameFolderFinished() ) ); job->start(); break; } @@ -898,7 +964,7 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_CHECK_UIDVALIDITY: mSyncState = SYNC_STATE_CREATE_SUBFOLDERS; - if( !noContent() ) { + if( !mQuotaOnly && !noContent() ) { checkUidValidity(); break; } @@ -906,33 +972,36 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_CREATE_SUBFOLDERS: mSyncState = SYNC_STATE_PUT_MESSAGES; - createNewFolders(); - break; + if ( !mQuotaOnly ) { + createNewFolders(); + break; + } case SYNC_STATE_PUT_MESSAGES: mSyncState = SYNC_STATE_UPLOAD_FLAGS; - if( !noContent() ) { + if( !mQuotaOnly && !noContent() ) { uploadNewMessages(); break; } // Else carry on case SYNC_STATE_UPLOAD_FLAGS: mSyncState = SYNC_STATE_LIST_NAMESPACES; - if( !noContent() ) { + if( !mQuotaOnly && !noContent() ) { // We haven't downloaded messages yet, so we need to build the map. if( uidMapDirty ) reloadUidMap(); // Upload flags, unless we know from the ACL that we're not allowed // to do that or they did not change locally - if ( mUserRights <= 0 || ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) { - if ( mStatusChangedLocally ) { + if ( mUserRightsState != KMail::ACLJobs::Ok || + ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) { + if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) { uploadFlags(); break; } else { //kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl; } } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) { - if ( mStatusChangedLocally ) { + if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) { uploadSeenFlags(); break; } @@ -941,7 +1010,7 @@ void KMFolderCachedImap::serverSyncInternal() // Else carry on case SYNC_STATE_LIST_NAMESPACES: - if ( this == mAccount->rootFolder() ) { + if ( !mQuotaOnly && this == mAccount->rootFolder() ) { listNamespaces(); break; } @@ -951,22 +1020,26 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_LIST_SUBFOLDERS: newState( mProgress, i18n("Retrieving folderlist")); mSyncState = SYNC_STATE_LIST_SUBFOLDERS2; - if( !listDirectory() ) { - mSyncState = SYNC_STATE_INITIAL; - KMessageBox::error(0, i18n("Error while retrieving the folderlist")); + if ( !mQuotaOnly ) { + if( !listDirectory() ) { + mSyncState = SYNC_STATE_INITIAL; + KMessageBox::error(0, i18n("Error while retrieving the folderlist")); + } + break; } - break; case SYNC_STATE_LIST_SUBFOLDERS2: mSyncState = SYNC_STATE_DELETE_SUBFOLDERS; mProgress += 10; - newState( mProgress, i18n("Retrieving subfolders")); - listDirectory2(); - break; + if ( !mQuotaOnly ) { + newState( mProgress, i18n("Retrieving subfolders")); + listDirectory2(); + break; + } case SYNC_STATE_DELETE_SUBFOLDERS: mSyncState = SYNC_STATE_LIST_MESSAGES; - if( !foldersForDeletionOnServer.isEmpty() ) { + if( !mQuotaOnly && !foldersForDeletionOnServer.isEmpty() ) { newState( mProgress, i18n("Deleting folders from server")); CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer, CachedImapJob::tDeleteFolders, this ); @@ -981,7 +1054,7 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_LIST_MESSAGES: mSyncState = SYNC_STATE_DELETE_MESSAGES; - if( !noContent() ) { + if( !mQuotaOnly && !noContent() ) { newState( mProgress, i18n("Retrieving message list")); listMessages(); break; @@ -990,7 +1063,7 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_DELETE_MESSAGES: mSyncState = SYNC_STATE_EXPUNGE_MESSAGES; - if( !noContent() ) { + if( !mQuotaOnly && !noContent() ) { if( deleteMessages() ) { // Fine, we will continue with the next state } else { @@ -1005,7 +1078,7 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_EXPUNGE_MESSAGES: mSyncState = SYNC_STATE_GET_MESSAGES; - if( !noContent() ) { + if( !mQuotaOnly && !noContent() ) { newState( mProgress, i18n("Expunging deleted messages")); CachedImapJob *job = new CachedImapJob( TQString::null, CachedImapJob::tExpungeFolder, this ); @@ -1018,9 +1091,9 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_GET_MESSAGES: mSyncState = SYNC_STATE_HANDLE_INBOX; - if( !noContent() ) { + if( !mQuotaOnly && !noContent() ) { if( !mMsgsForDownload.isEmpty() ) { - newState( mProgress, i18n("Retrieving new messages")); + newState( mProgress, i18n("Retrieving one new message","Retrieving %n new messages",mMsgsForDownload.size())); CachedImapJob *job = new CachedImapJob( mMsgsForDownload, CachedImapJob::tGetMessage, this ); @@ -1061,8 +1134,8 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_TEST_ANNOTATIONS: mSyncState = SYNC_STATE_GET_ANNOTATIONS; // The first folder with user rights to write annotations - if( !mAccount->annotationCheckPassed() && - ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) + if( !mQuotaOnly && !mAccount->annotationCheckPassed() && + ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) && !imapPath().isEmpty() && imapPath() != "/" ) { kdDebug(5006) << "Setting test attribute on folder: "<< folder()->prettyURL() << endl; newState( mProgress, i18n("Checking annotation support")); @@ -1088,11 +1161,12 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_GET_ANNOTATIONS: { #define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type" #define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for" +#define KOLAB_SHAREDSEEN "/vendor/cmu/cyrus-imapd/sharedseen" //#define KOLAB_FOLDERTYPE "/comment" //for testing, while cyrus-imap doesn't support /vendor/* mSyncState = SYNC_STATE_SET_ANNOTATIONS; bool needToGetInitialAnnotations = false; - if ( !noContent() ) { + if ( !mQuotaOnly && !noContent() ) { // for a folder we didn't create ourselves: get annotation from server if ( mAnnotationFolderType == "FROMSERVER" ) { needToGetInitialAnnotations = true; @@ -1104,13 +1178,15 @@ void KMFolderCachedImap::serverSyncInternal() // First retrieve the annotation, so that we know we have to set it if it's not set. // On the other hand, if the user changed the contentstype, there's no need to get first. - if ( !noContent() && mAccount->hasAnnotationSupport() && + if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() && ( kmkernel->iCalIface().isEnabled() || needToGetInitialAnnotations ) ) { TQStringList annotations; // list of annotations to be fetched if ( !mAnnotationFolderTypeChanged || mAnnotationFolderType.isEmpty() ) annotations << KOLAB_FOLDERTYPE; if ( !mIncidencesForChanged ) annotations << KOLAB_INCIDENCESFOR; + if ( !mSharedSeenFlagsChanged ) + annotations << KOLAB_SHAREDSEEN; if ( !annotations.isEmpty() ) { newState( mProgress, i18n("Retrieving annotations")); KURL url = mAccount->getUrl(); @@ -1132,8 +1208,8 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_SET_ANNOTATIONS: mSyncState = SYNC_STATE_SET_ACLS; - if ( !noContent() && mAccount->hasAnnotationSupport() && - ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) { + if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() && + ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) { newState( mProgress, i18n("Setting annotations")); KURL url = mAccount->getUrl(); url.setPath( imapPath() ); @@ -1149,6 +1225,12 @@ void KMFolderCachedImap::serverSyncInternal() annotations.append( attr ); kdDebug(5006) << "Setting incidences-for annotation for " << label() << " to " << val << endl; } + if ( mSharedSeenFlagsChanged ) { + const TQString val = mSharedSeenFlags ? "true" : "false"; + KMail::AnnotationAttribute attr( KOLAB_SHAREDSEEN, "value.shared", val ); + annotations.append( attr ); + kdDebug(5006) << k_funcinfo << "Setting sharedseen annotation for " << label() << " to " << val << endl; + } if ( !annotations.isEmpty() ) { KIO::Job* job = AnnotationJobs::multiSetAnnotation( mAccount->slave(), url, annotations ); @@ -1167,8 +1249,8 @@ void KMFolderCachedImap::serverSyncInternal() case SYNC_STATE_SET_ACLS: mSyncState = SYNC_STATE_GET_ACLS; - if( !noContent() && mAccount->hasACLSupport() && - ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) { + if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() && + ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) { bool hasChangedACLs = false; ACLList::ConstIterator it = mACLList.begin(); for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) { @@ -1191,19 +1273,36 @@ void KMFolderCachedImap::serverSyncInternal() } case SYNC_STATE_GET_ACLS: - mSyncState = SYNC_STATE_GET_QUOTA; + mSyncState = SYNC_STATE_FIND_SUBFOLDERS; - if( !noContent() && mAccount->hasACLSupport() ) { + if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) { newState( mProgress, i18n( "Retrieving permissions" ) ); mAccount->getACL( folder(), mImapPath ); connect( mAccount, TQT_SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )), this, TQT_SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) ); break; } + case SYNC_STATE_FIND_SUBFOLDERS: + { + mSyncState = SYNC_STATE_SYNC_SUBFOLDERS; + mSomeSubFolderCloseToQuotaChanged = false; + buildSubFolderList(); + } + + // Carry on + case SYNC_STATE_SYNC_SUBFOLDERS: + syncNextSubFolder( false ); + break; + case SYNC_STATE_GET_SUBFOLDER_QUOTA: + + // Sync the subfolders again, so that the quota information is updated for all. This state is + // only entered if the close to quota property of one subfolder changed in the previous state. + syncNextSubFolder( true ); + break; case SYNC_STATE_GET_QUOTA: - // Continue with the subfolders - mSyncState = SYNC_STATE_FIND_SUBFOLDERS; + mSyncState = SYNC_STATE_CLOSE; if( !noContent() && mAccount->hasQuotaSupport() ) { + mProgress = 98; newState( mProgress, i18n("Getting quota information")); KURL url = mAccount->getUrl(); url.setPath( imapPath() ); @@ -1216,79 +1315,114 @@ void KMFolderCachedImap::serverSyncInternal() TQT_SLOT(slotQuotaResult(KIO::Job *)) ); break; } - case SYNC_STATE_FIND_SUBFOLDERS: + case SYNC_STATE_CLOSE: { - mProgress = 98; - newState( mProgress, i18n("Updating cache file")); - - mSyncState = SYNC_STATE_SYNC_SUBFOLDERS; - mSubfoldersForSync.clear(); - mCurrentSubfolder = 0; - if( folder() && folder()->child() ) { - KMFolderNode *node = folder()->child()->first(); - while( node ) { - if( !node->isDir() ) { - KMFolderCachedImap* storage = static_cast(static_cast(node)->storage()); - // Only sync folders that have been accepted by the server - if ( !storage->imapPath().isEmpty() - // and that were not just deleted from it - && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) { - mSubfoldersForSync << storage; - } else { - kdDebug(5006) << "Do not add " << storage->label() - << " to synclist" << endl; - } - } - node = folder()->child()->next(); - } - } - - // All done for this folder. - mProgress = 100; // all done - newState( mProgress, i18n("Synchronization done")); + mProgress = 100; // all done + newState( mProgress, i18n("Synchronization done")); KURL url = mAccount->getUrl(); url.setPath( imapPath() ); kmkernel->iCalIface().folderSynced( folder(), url ); - } - - if ( !mRecurse ) // "check mail for this folder" only - mSubfoldersForSync.clear(); - // Carry on - case SYNC_STATE_SYNC_SUBFOLDERS: - { - if( mCurrentSubfolder ) { - disconnect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), - this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); - mCurrentSubfolder = 0; - } - - if( mSubfoldersForSync.isEmpty() ) { - mSyncState = SYNC_STATE_INITIAL; - mAccount->addUnreadMsgCount( this, countUnread() ); // before closing - close("cachedimap"); - emit folderComplete( this, true ); - } else { - mCurrentSubfolder = mSubfoldersForSync.front(); - mSubfoldersForSync.pop_front(); - connect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), - this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); - - //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl; - assert( !mCurrentSubfolder->imapPath().isEmpty() ); - mCurrentSubfolder->setAccount( account() ); - bool recurse = mCurrentSubfolder->noChildren() ? false : true; - mCurrentSubfolder->serverSync( recurse ); - } + mSyncState = SYNC_STATE_INITIAL; + mAccount->addUnreadMsgCount( this, countUnread() ); // before closing + close( "cachedimap" ); + emit syncStateChanged(); + emit folderComplete( this, true ); } break; default: kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state " - << mSyncState << endl; + << mSyncState << endl; } } +void KMFolderCachedImap::syncNextSubFolder( bool secondSync ) +{ + if( mCurrentSubfolder ) { + disconnectSubFolderSignals(); + } + + if( mSubfoldersForSync.isEmpty() ) { + + // Sync finished, and a close to quota property of an subfolder changed, therefore go into + // the SYNC_STATE_GET_SUBFOLDER_QUOTA state and sync again + if ( mSomeSubFolderCloseToQuotaChanged && mRecurse && !secondSync ) { + buildSubFolderList(); + mSyncState = SYNC_STATE_GET_SUBFOLDER_QUOTA; + serverSyncInternal(); + } + + else { + + // Quota checking has to come after syncing subfolder, otherwise the quota information would + // be outdated, since the subfolders can change in size during the syncing. + // https://issues.kolab.org/issue4066 + mSyncState = SYNC_STATE_GET_QUOTA; + serverSyncInternal(); + } + } else { + mCurrentSubfolder = mSubfoldersForSync.front(); + mSubfoldersForSync.pop_front(); + if ( mCurrentSubfolder ) { + connect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), + this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); + connect( mCurrentSubfolder, TQT_SIGNAL( closeToQuotaChanged() ), + this, TQT_SLOT( slotSubFolderCloseToQuotaChanged() ) ); + + //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl; + assert( !mCurrentSubfolder->imapPath().isEmpty() ); + mCurrentSubfolder->setAccount( account() ); + const bool recurse = mCurrentSubfolder->noChildren() ? false : true; + const bool quotaOnly = secondSync || mQuotaOnly; + mCurrentSubfolder->serverSync( recurse, quotaOnly ); + } + else { + // mCurrentSubfolder is empty, probably because it was deleted while syncing. Go on with the + // next subfolder instead. + syncNextSubFolder( secondSync ); + } + } +} + +void KMFolderCachedImap::buildSubFolderList() +{ + mSubfoldersForSync.clear(); + mCurrentSubfolder = 0; + if( folder() && folder()->child() ) { + KMFolderNode *node = folder()->child()->first(); + while( node ) { + if( !node->isDir() ) { + KMFolderCachedImap* storage = static_cast(static_cast(node)->storage()); + const bool folderIsNew = mNewlyCreatedSubfolders.contains( TQGuardedPtr( storage ) ); + // Only sync folders that have been accepted by the server + if ( !storage->imapPath().isEmpty() + // and that were not just deleted from it + && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) { + if ( mRecurse || folderIsNew ) { + mSubfoldersForSync << storage; + } + } else { + kdDebug(5006) << "Do not add " << storage->label() + << " to synclist" << endl; + } + } + node = folder()->child()->next(); + } + } + + mNewlyCreatedSubfolders.clear(); +} + +void KMFolderCachedImap::disconnectSubFolderSignals() +{ + disconnect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), + this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); + disconnect( mCurrentSubfolder, TQT_SIGNAL( closeToQuotaChanged() ), + this, TQT_SLOT( slotSubFolderCloseToQuotaChanged() ) ); + mCurrentSubfolder = 0; +} + /* Connected to the imap account's connectionResult signal. Emitted when the slave connected or failed to connect. */ @@ -1326,7 +1460,7 @@ void KMFolderCachedImap::uploadNewMessages() { TQValueList newMsgs = findNewMessages(); if( !newMsgs.isEmpty() ) { - if ( mUserRights <= 0 || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) { + if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) { newState( mProgress, i18n("Uploading messages to server")); CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this ); connect( job, TQT_SIGNAL( progress( unsigned long, unsigned long) ), @@ -1377,6 +1511,11 @@ void KMFolderCachedImap::uploadFlags() if( !msg || msg->UID() == 0 ) // Either not a valid message or not one that is on the server yet continue; + if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end() + && !mStatusChangedLocally ) { + // This message has not had its status changed locally + continue; + } TQString flags = KMFolderImap::statusToFlags(msg->status(), mPermanentFlags); // Collect uids for each typem of flags. @@ -1420,6 +1559,12 @@ void KMFolderCachedImap::uploadSeenFlags() // Either not a valid message or not one that is on the server yet continue; + if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end() + && !mStatusChangedLocally ) { + // This message has not had its status changed locally + continue; + } + if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead ) seenUids.append( msg->UID() ); else @@ -1476,13 +1621,21 @@ void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const TQString& void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle) { KMFolderMaildir::setStatus( idx, status, toggle ); - mStatusChangedLocally = true; + const KMMsgBase *msg = getMsgBase( idx ); + Q_ASSERT( msg ); + if ( msg ) + mUIDsOfLocallyChangedStatuses.insert( msg->UID() ); } void KMFolderCachedImap::setStatus(TQValueList& ids, KMMsgStatus status, bool toggle) { KMFolderMaildir::setStatus(ids, status, toggle); - mStatusChangedLocally = true; + for (TQValueList::iterator it = ids.begin(); it != ids.end(); it++ ) { + const KMMsgBase *msg = getMsgBase( *it ); + Q_ASSERT( msg ); + if ( msg ) + mUIDsOfLocallyChangedStatuses.insert( msg->UID() ); + } } /* Upload new folders to server */ @@ -1528,7 +1681,7 @@ TQValueList KMFolderCachedImap::findNewFolders() bool KMFolderCachedImap::deleteMessages() { /* Delete messages from cache that are gone from the server */ - TQPtrList msgsForDeletion; + TQPtrList msgsForDeletion; // It is not possible to just go over all indices and remove // them one by one because the index list can get resized under @@ -1540,11 +1693,15 @@ bool KMFolderCachedImap::deleteMessages() ulong uid ( it.key() ); if( uid!=0 && !uidsOnServer.find( uid ) ) { uids << TQString::number( uid ); - msgsForDeletion.append( getMsg( *it ) ); + msgsForDeletion.append( getMsgBase( *it ) ); } } if( !msgsForDeletion.isEmpty() ) { + if ( contentsType() != ContentsTypeMail ) { + kdDebug(5006) << k_funcinfo << label() << " Going to locally delete " << msgsForDeletion.count() + << " messages, with the uids " << uids.join( "," ) << endl; + } #if MAIL_LOSS_DEBUGGING if ( KMessageBox::warningYesNo( 0, i18n( "

Mails on the server in folder %1 were deleted. " @@ -1554,7 +1711,7 @@ bool KMFolderCachedImap::deleteMessages() removeMsg( msgsForDeletion ); } - if ( mUserRights > 0 && !( mUserRights & KMail::ACLJobs::Delete ) ) + if ( mUserRightsState == KMail::ACLJobs::Ok && !( mUserRights & KMail::ACLJobs::Delete ) ) return false; /* Delete messages from the server that we dont have anymore */ @@ -1688,7 +1845,7 @@ void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const TQByteArray & // updated when selecting the folder again, which might not happen if using // RMB / Check Mail in this folder. We don't need two (potentially conflicting) // sources for the readonly setting, in any case. - if (a != -1 && mUserRights == -1 ) { + if (a != -1 && mUserRightsState != KMail::ACLJobs::Ok ) { int b = (*it).cdata.find("\r\n", a + 12); const TQString access = (*it).cdata.mid(a + 12, b - a - 12); setReadOnly( access == "Read only" ); @@ -1751,7 +1908,7 @@ void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const TQByteArray & #endif // double check we deleted it since the last sync if ( mDeletedUIDsSinceLastSync.contains(uid) ) { - if ( mUserRights <= 0 || ( mUserRights & KMail::ACLJobs::Delete ) ) { + if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & KMail::ACLJobs::Delete ) ) { #if MAIL_LOSS_DEBUGGING kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl; #endif @@ -1821,7 +1978,8 @@ void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet } else { if( lastSet ) { // always true here (this comes from online-imap...) mContentState = imapFinished; - mStatusChangedLocally = false; // we are up to date again + mUIDsOfLocallyChangedStatuses.clear(); // we are up to date again + mStatusChangedLocally = false; } } serverSyncInternal(); @@ -1830,10 +1988,13 @@ void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total) { int progressSpan = 100 - 5 - mProgress; - //kdDebug(5006) << "KMFolderCachedImap::slotProgress done=" << done << " total=" << total << "=> mProgress=" << mProgress + ( progressSpan * done ) / total << endl; + int additionalProgress = ( total == 0 ) ? + progressSpan : + ( progressSpan * done ) / total; + // Progress info while retrieving new emails // (going from mProgress to mProgress+progressSpan) - newState( mProgress + (progressSpan * done) / total, TQString::null ); + newState( mProgress + additionalProgress, TQString::null ); } void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount) @@ -2010,7 +2171,7 @@ void KMFolderCachedImap::slotListResult( const TQStringList& folderNames, mSubfolderMimeTypes = folderMimeTypes; mSubfolderState = imapFinished; mSubfolderAttributes = folderAttributes; - kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl; + //kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl; folder()->createChildFolder(); KMFolderNode *node = folder()->child()->first(); @@ -2209,6 +2370,7 @@ void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const TQValue f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest"); f->setImapPath(mSubfolderPaths[idx]); f->mFolderAttributes = mSubfolderAttributes[idx]; + mNewlyCreatedSubfolders.append( TQGuardedPtr( f ) ); kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <dimapFolderMgr()->contentsChanged(); @@ -2267,18 +2429,26 @@ void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool suc // success == false means the sync was aborted. if ( mCurrentSubfolder ) { Q_ASSERT( sub == mCurrentSubfolder ); - disconnect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ), - this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) ); - mCurrentSubfolder = 0; + disconnectSubFolderSignals(); } + // Next step would be to check quota limits and then to close the folder, but don't bother with + // both and close the folder right here, since we aborted. mSubfoldersForSync.clear(); mSyncState = SYNC_STATE_INITIAL; close("cachedimap"); + emit syncStateChanged(); emit folderComplete( this, false ); } } +void KMFolderCachedImap::slotSubFolderCloseToQuotaChanged() +{ + if ( !mQuotaOnly ) { + mSomeSubFolderCloseToQuotaChanged = true; + } +} + void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const TQByteArray & data) { KMAcctCachedImap::JobIterator it = mAccount->findJob(job); @@ -2312,9 +2482,10 @@ KMFolderCachedImap::doCreateJob( TQPtrList& msgList, const TQString& } void -KMFolderCachedImap::setUserRights( unsigned int userRights ) +KMFolderCachedImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState state ) { mUserRights = userRights; + mUserRightsState = state; writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); } @@ -2324,10 +2495,9 @@ KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder ) if ( folder->storage() == this ) { disconnect( mAccount, TQT_SIGNAL( receivedUserRights( KMFolder* ) ), this, TQT_SLOT( slotReceivedUserRights( KMFolder* ) ) ); - if ( mUserRights == 0 ) // didn't work - mUserRights = -1; // error code (used in folderdia) - else + if ( mUserRightsState == KMail::ACLJobs::Ok ) { setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 ); + } mProgress += 5; serverSyncInternal(); } @@ -2343,11 +2513,12 @@ KMFolderCachedImap::setReadOnly( bool readOnly ) } void -KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job*, const KMail::ACLList& aclList ) +KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job* job, const KMail::ACLList& aclList ) { if ( folder->storage() == this ) { disconnect( mAccount, TQT_SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )), this, TQT_SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) ); + mACLListState = job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok; mACLList = aclList; serverSyncInternal(); } @@ -2362,8 +2533,12 @@ KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info ) void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info ) { if ( info != mQuotaInfo ) { + const bool wasCloseToQuota = isCloseToQuota(); mQuotaInfo = info; writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); + if ( wasCloseToQuota != isCloseToQuota() ) { + emit closeToQuotaChanged(); + } emit folderSizeChanged(); } } @@ -2372,6 +2547,7 @@ void KMFolderCachedImap::setACLList( const ACLList& arr ) { mACLList = arr; + mACLListState = KMail::ACLJobs::Ok; } void @@ -2413,6 +2589,7 @@ void KMFolderCachedImap::resetSyncState() { if ( mSyncState == SYNC_STATE_INITIAL ) return; mSubfoldersForSync.clear(); + mNewlyCreatedSubfolders.clear(); mSyncState = SYNC_STATE_INITIAL; close("cachedimap"); // Don't use newState here, it would revert to mProgress (which is < current value when listing messages) @@ -2421,6 +2598,7 @@ void KMFolderCachedImap::resetSyncState() if (progressItem) progressItem->setStatus( str ); emit statusMsg( str ); + emit syncStateChanged(); } void KMFolderCachedImap::slotIncreaseProgress() @@ -2472,6 +2650,16 @@ void KMFolderCachedImap::setImapPath(const TQString &path) mImapPath = path; } +static bool isFolderTypeKnownToUs( const TQString &type ) +{ + for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) { + FolderContentsType contentsType = static_cast( i ); + if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) + return true; + } + return false; +} + // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc) // It is updated from the folder contents type and whether it's a standard resource folder. // This happens during the syncing phase and during initFolder for a new folder. @@ -2493,12 +2681,18 @@ void KMFolderCachedImap::updateAnnotationFolderType() newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType ); if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) ) newSubType = "default"; - else - newSubType = oldSubType; // preserve unknown subtypes, like drafts etc. And preserve ".default" too. + else if ( oldSubType != "default" ) + newSubType = oldSubType; // preserve unknown subtypes, like drafts etc. } + // We do not want to overwrite custom folder types (which we treat as mail folders). + // So only overwrite custom folder types if the user changed the folder type himself to something + // other than mail. + const bool changingTypeAllowed = isFolderTypeKnownToUs( oldType ) || + ( mContentsType != ContentsTypeMail ); + //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl; - if ( newType != oldType || newSubType != oldSubType ) { + if ( ( newType != oldType || newSubType != oldSubType ) && changingTypeAllowed ) { mAnnotationFolderType = newType + ( newSubType.isEmpty() ? TQString::null : "."+newSubType ); mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl; @@ -2515,6 +2709,14 @@ void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor ) } } +void KMFolderCachedImap::setSharedSeenFlags(bool b) +{ + if ( mSharedSeenFlags != b ) { + mSharedSeenFlags = b; + mSharedSeenFlagsChanged = true; + } +} + void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQString& value, bool found) { if ( entry == KOLAB_FOLDERTYPE ) { @@ -2559,16 +2761,21 @@ void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQStr if ( contentsType != ContentsTypeMail ) markUnreadAsRead(); - // Ensure that further readConfig()s don't lose mAnnotationFolderType - writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); break; } } - if ( !foundKnownType && !mReadOnly ) { - //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, will need to set it" << endl; - // Case 4: server has strange content-type, set it to what we need - mAnnotationFolderTypeChanged = true; + if ( !foundKnownType ) { + //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, leaving it untouched" << endl; + + // Case 4: Server has strange content-type. We must not overwrite it, see https://issues.kolab.org/issue2069. + // Treat the content-type as mail until we change it ourselves. + mAnnotationFolderTypeChanged = false; + mAnnotationFolderType = value; + setContentsType( ContentsTypeMail ); } + + // Ensure that further readConfig()s don't lose mAnnotationFolderType + writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig(); // TODO handle subtype (inbox, drafts, sentitems, junkemail) } else if ( !mReadOnly ) { @@ -2581,6 +2788,10 @@ void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQStr mIncidencesFor = incidencesForFromString( value ); Q_ASSERT( mIncidencesForChanged == false ); } + } else if ( entry == KOLAB_SHAREDSEEN ) { + if ( found ) { + mSharedSeenFlags = value == "true"; + } } } @@ -2700,6 +2911,8 @@ KMFolderCachedImap::slotAnnotationChanged( const TQString& entry, const TQString // The incidences-for changed, we must trigger the freebusy creation. // HACK: in theory we would need a new enum value for this. kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL ); + } else if ( entry == KOLAB_SHAREDSEEN ) { + mSharedSeenFlagsChanged = false; } } @@ -2989,4 +3202,27 @@ void KMFolderCachedImap::slotRescueDone(KMCommand * command) serverSyncInternal(); } +void KMFolderCachedImap::slotRenameFolderFinished() +{ + // The syncing code assumes the folder was opened by us, and later closes it. So better + // make sure the reference count is correct, since the folder was force-closed by the rename. + // Otherwise bad things can happen, see https://issues.kolab.org/issue3853. + open( "cachedimap" ); + serverSyncInternal(); +} + +bool KMFolderCachedImap::canDeleteMessages() const +{ + if ( isReadOnly() ) + return false; + if ( mUserRightsState == KMail::ACLJobs::Ok && !(userRights() & ACLJobs::Delete) ) + return false; + return true; +} + +bool KMFolderCachedImap::mailCheckInProgress() const +{ + return mSyncState != SYNC_STATE_INITIAL; +} + #include "kmfoldercachedimap.moc" diff --git a/kmail/kmfoldercachedimap.h b/kmail/kmfoldercachedimap.h index 5ead77b2..21abeabf 100644 --- a/kmail/kmfoldercachedimap.h +++ b/kmail/kmfoldercachedimap.h @@ -47,6 +47,8 @@ #include "cachedimapjob.h" #include "quotajobs.h" +#include + using KMail::FolderJob; using KMail::QuotaInfo; class KMCommand; @@ -79,10 +81,11 @@ public: private slots: void slotDone(); - + void slotChanged(); private: TQRadioButton *mIndexButton, *mCacheButton; TQComboBox *mIndexScope; + TQButtonGroup *mButtonGroup; int rc; }; @@ -122,7 +125,7 @@ public: virtual void remove(); /** Synchronize this folder and it's subfolders with the server */ - virtual void serverSync( bool recurse ); + virtual void serverSync( bool recurse, bool quotaOnly = false ); /** Force the sync state to be done. */ void resetSyncState( ); @@ -138,7 +141,7 @@ public: enum imapState { imapNoInformation=0, imapInProgress=1, imapFinished=2 }; - virtual imapState getContentState() { return mContentState; } + virtual imapState getContentState() const { return mContentState; } virtual void setContentState(imapState state) { mContentState = state; } virtual imapState getSubfolderState() { return mSubfolderState; } @@ -206,11 +209,12 @@ public: /* Reimplemented from KMFolderMaildir */ virtual void removeMsg(int i, bool imapQuiet = false); - virtual void removeMsg(TQPtrList msgList, bool imapQuiet = false) + virtual void removeMsg( const TQPtrList & msgList, bool imapQuiet = false) { FolderStorage::removeMsg(msgList, imapQuiet); } /// Is the folder readonly? bool isReadOnly() const { return KMFolderMaildir::isReadOnly() || mReadOnly; } + bool canDeleteMessages() const; /** @@ -249,12 +253,14 @@ public: /** * The user's rights on this folder - see bitfield in ACLJobs namespace. - * @return 0 when not known yet, -1 if there was an error fetching them + * Note that the returned value is only valid if userRightsState() returns Ok, so + * that should be checked first. */ int userRights() const { return mUserRights; } + KMail::ACLJobs::ACLFetchState userRightsState() const { return mUserRightsState; } /// Set the user's rights on this folder - called by getUserRights - void setUserRights( unsigned int userRights ); + void setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState state ); /** * The quota information for this folder. @@ -271,6 +277,7 @@ public: /// Return the list of ACL for this folder typedef TQValueVector ACLList; const ACLList& aclList() const { return mACLList; } + KMail::ACLJobs::ACLFetchState aclListState() const { return mACLListState; }; /// Set the list of ACL for this folder (for FolderDiaACLTab) void setACLList( const ACLList& arr ); @@ -298,6 +305,11 @@ public: /// For the folder properties dialog void setIncidencesFor( IncidencesFor incfor ); + /** Returns wether the seen flag is shared among all users or every users has her own seen flags (default). */ + bool sharedSeenFlags() const { return mSharedSeenFlags; } + /** Enable shared seen flags (requires server support). */ + void setSharedSeenFlags( bool b ); + /** Returns true if this folder can be moved */ virtual bool isMoveable() const; @@ -324,6 +336,8 @@ public: TQString folderAttributes() const { return mFolderAttributes; } + virtual bool mailCheckInProgress() const; + protected slots: void slotGetMessagesData(KIO::Job * job, const TQByteArray & data); void getMessagesResult(KMail::FolderJob *, bool lastSet); @@ -333,6 +347,7 @@ protected slots: //virtual void slotCheckValidityResult(KIO::Job * job); void slotSubFolderComplete(KMFolderCachedImap*, bool); + void slotSubFolderCloseToQuotaChanged(); // Connected to the imap account void slotConnectionResult( int errorCode, const TQString& errorMsg ); @@ -426,15 +441,16 @@ private slots: void slotUpdateLastUid(); void slotFolderDeletionOnServerFinished(); void slotRescueDone( KMCommand* command ); + void slotRenameFolderFinished(); signals: void folderComplete(KMFolderCachedImap *folder, bool success); void listComplete( KMFolderCachedImap* ); - /** emitted when we enter the state "state" and - have to process "number" items (for example messages - */ - void syncState( int state, int number ); + /** + * Emitted when isCloseToQuota() changes during syncing + */ + void closeToQuotaChanged(); private: void setReadOnly( bool readOnly ); @@ -448,6 +464,24 @@ private: /** Recursive helper function calling the above method. */ void rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root = true ); + /** + * Small helper function that disconnects the signals from the current subfolder, which where + * connected when starting the sync of that subfolder + */ + void disconnectSubFolderSignals(); + + /** + * Sync the next subfolder in the list of subfolders (mSubfoldersForSync). + * When finished, this will switch either to the state SYNC_STATE_GET_SUBFOLDER_QUOTA or + * to SYNC_STATE_GET_QUOTA. + */ + void syncNextSubFolder( bool secondSync ); + + /** + * Creates the mSubfoldersForSync list + */ + void buildSubFolderList(); + /** State variable for the synchronization mechanism */ enum { SYNC_STATE_INITIAL, @@ -473,7 +507,9 @@ private: SYNC_STATE_FIND_SUBFOLDERS, SYNC_STATE_SYNC_SUBFOLDERS, SYNC_STATE_CHECK_UIDVALIDITY, - SYNC_STATE_RENAME_FOLDER + SYNC_STATE_RENAME_FOLDER, + SYNC_STATE_CLOSE, + SYNC_STATE_GET_SUBFOLDER_QUOTA } mSyncState; int mProgress; @@ -487,6 +523,7 @@ private: TQString mFolderAttributes; TQString mAnnotationFolderType; IncidencesFor mIncidencesFor; + bool mSharedSeenFlags; bool mHasInbox; bool mIsSelected; @@ -500,7 +537,7 @@ private: TQValueList mUidsForDownload; TQStringList foldersForDeletionOnServer; - TQValueList mSubfoldersForSync; + TQValueList< TQGuardedPtr > mSubfoldersForSync; KMFolderCachedImap* mCurrentSubfolder; /** Mapping uid -> index @@ -533,21 +570,36 @@ private: bool mFoundAnIMAPDigest; int mUserRights, mOldUserRights; + KMail::ACLJobs::ACLFetchState mUserRightsState; ACLList mACLList; + KMail::ACLJobs::ACLFetchState mACLListState; bool mSilentUpload; bool mFolderRemoved; //bool mHoldSyncs; bool mRecurse; - /** Set to true by setStatus. Indicates that the client has changed - the status of at least one mail. The mail flags will therefore be - uploaded to the server, overwriting the server's notion of the status - of the mails in this folder. */ - bool mStatusChangedLocally; + bool mQuotaOnly; + /// Set to true when the foldertype annotation needs to be set on the next sync bool mAnnotationFolderTypeChanged; /// Set to true when the "incidences-for" annotation needs to be set on the next sync bool mIncidencesForChanged; + /// Set to true when the "sharedseen" annotation needs to be set on the next sync + bool mSharedSeenFlagsChanged; + + /** + * UIDs added by setStatus. Indicates that the client has changed + * the status of those mails. The mail flags for changed mails will be + * uploaded to the server, overwriting the server's notion of the status + * of the mails in this folder. + */ + std::set mUIDsOfLocallyChangedStatuses; + + /** + * Same as above, but uploads the flags of all mails, even if not all changed. + * Only still here for config compatibility. + */ + bool mStatusChangedLocally; TQStringList mNamespacesToList; int mNamespacesToCheck; @@ -555,12 +607,18 @@ private: TQString mImapPathCreation; QuotaInfo mQuotaInfo; + + /// This is set during syncing of the current subfolder. If true, it means the closeToQuota info + /// for the current subfolder has changed during syncing + bool mSomeSubFolderCloseToQuotaChanged; + TQMap mDeletedUIDsSinceLastSync; bool mAlarmsBlocked; TQValueList mToBeDeletedAfterRescue; int mRescueCommandCount; + TQValueList< TQGuardedPtr > mNewlyCreatedSubfolders; int mPermanentFlags; }; diff --git a/kmail/kmfolderdia.cpp b/kmail/kmfolderdia.cpp index cf02fcdd..533173e5 100644 --- a/kmail/kmfolderdia.cpp +++ b/kmail/kmfolderdia.cpp @@ -32,6 +32,7 @@ #include +#include "acljobs.h" #include "kmfolderdia.h" #include "kmacctfolder.h" #include "kmfoldermgr.h" @@ -247,20 +248,20 @@ static void addLine( TQWidget *parent, TQVBoxLayout* layout ) KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg, const TQString& aName, TQWidget* parent, const char* name ) - : FolderDiaTab( parent, name ), mDlg( dlg ) + : FolderDiaTab( parent, name ), + mSharedSeenFlagsCheckBox( 0 ), + mDlg( dlg ) { - - mIsLocalSystemFolder = mDlg->folder()->isSystemFolder() && - mDlg->folder()->folderType() != KMFolderTypeImap && - mDlg->folder()->folderType() != KMFolderTypeCachedImap; + mIsLocalSystemFolder = mDlg->folder()->isSystemFolder(); + mIsResourceFolder = kmkernel->iCalIface().isStandardResourceFolder( mDlg->folder() ); TQLabel *label; TQVBoxLayout *topLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() ); - // Musn't be able to edit details for a system folder. - if ( !mIsLocalSystemFolder ) { + // Musn't be able to edit details for a non-resource, system folder. + if ( !mIsLocalSystemFolder || mIsResourceFolder ) { TQHBoxLayout *hl = new TQHBoxLayout( topLayout ); hl->setSpacing( KDialog::spacingHint() ); @@ -268,9 +269,65 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg, label = new TQLabel( i18n("&Name:"), this ); hl->addWidget( label ); + // Determine if we are allowed to rename this folder. Only possible if the folder supports + // ACLs. + bool nameChangeAllowed = true; + if ( mDlg->folder() && mDlg->parentFolder() && + mDlg->folder()->storage() && mDlg->parentFolder()->storage() && + ( mDlg->folder()->folderType() == KMFolderTypeCachedImap || + mDlg->folder()->folderType() == KMFolderTypeImap ) ) { + ImapAccountBase *account = 0; + KMFolderCachedImap *dimap = 0; + KMFolderImap *imap = 0; + if ( mDlg->folder()->folderType() == KMFolderTypeCachedImap ) { + dimap = static_cast( mDlg->folder()->storage() ); + account = dynamic_cast( dimap->account() ); + } + if ( mDlg->folder()->folderType() == KMFolderTypeImap ) { + imap = static_cast( mDlg->folder()->storage() ); + account = dynamic_cast( imap->account() ); + } + + if ( account && account->hasACLSupport() ) { + int parentRights = -1; + int folderRights = -1; + bool parentRightsOk = false; + bool folderRightsOk = false; + if ( imap ) { + KMFolderImap * const parent = dynamic_cast( mDlg->parentFolder()->storage() ); + folderRights = imap->userRights(); + folderRightsOk = imap->userRightsState() == KMail::ACLJobs::Ok; + if ( parent ) { + parentRights = parent->userRights(); + parentRightsOk = parent->userRightsState() == KMail::ACLJobs::Ok; + } + } else if ( dimap ) { + KMFolderCachedImap * const parent = dynamic_cast( mDlg->parentFolder()->storage() ); + folderRights = dimap->userRights(); + folderRightsOk = dimap->userRightsState() == KMail::ACLJobs::Ok; + if ( parent ) { + parentRights = parent->userRights(); + parentRightsOk = parent->userRightsState() == KMail::ACLJobs::Ok; + } + } + + // For renaming, we need support for deleting the mailbox and then re-creating it. + if ( parentRightsOk && folderRightsOk && + ( !( parentRights & KMail::ACLJobs::Create ) || !( folderRights & KMail::ACLJobs::Delete ) ) ) { + nameChangeAllowed = false; + } + } + } + mNameEdit = new KLineEdit( this ); - if( !mDlg->folder() ) - mNameEdit->setFocus(); + if( !mDlg->folder() && nameChangeAllowed ) + mNameEdit->setFocus(); + mNameEdit->setEnabled( nameChangeAllowed ); + if ( !nameChangeAllowed ) { + TQToolTip::add( mNameEdit, i18n( "Not enough permissions to rename this folder.\n" + "The parent folder doesn't have write support.\n" + "A sync is needed after changing the permissions." ) ); + } mNameEdit->setText( mDlg->folder() ? mDlg->folder()->label() : i18n("unnamed") ); if (!aName.isEmpty()) mNameEdit->setText(aName); @@ -433,9 +490,10 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg, "automatically. Identities can be set up in the main configuration " "dialog. (Settings -> Configure KMail)") ); - // folder contents - if ( !mIsLocalSystemFolder && kmkernel->iCalIface().isEnabled() ) { + if ( ( !mIsLocalSystemFolder || mIsResourceFolder ) && + kmkernel->iCalIface().isEnabled() && + mDlg->folder() && mDlg->folder()->folderType() != KMFolderTypeImap ) { // Only do make this settable, if the IMAP resource is enabled // and it's not the personal folders (those must not be changed) ++row; @@ -455,12 +513,12 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg, mContentsComboBox->setCurrentItem( mDlg->folder()->storage()->contentsType() ); connect ( mContentsComboBox, TQT_SIGNAL ( activated( int ) ), this, TQT_SLOT( slotFolderContentsSelectionChanged( int ) ) ); - if ( mDlg->folder()->isReadOnly() ) + if ( mDlg->folder()->isReadOnly() || mIsResourceFolder ) mContentsComboBox->setEnabled( false ); } else { mContentsComboBox = 0; } - + mIncidencesForComboBox = 0; mAlarmsBlockedCheckBox = 0; @@ -478,7 +536,7 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg, label->setBuddy( mIncidencesForComboBox ); gl->addWidget( mIncidencesForComboBox, row, 1 ); - const TQString whatsThisForMyOwnFolders = + const TQString whatsThisForMyOwnFolders = i18n( "This setting defines which users sharing " "this folder should get \"busy\" periods in their freebusy lists " "and should see the alarms for the events or tasks in this folder. " @@ -499,13 +557,10 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg, mIncidencesForComboBox->insertItem( i18n( "All Readers of This Folder" ) ); ++row; const TQString whatsThisForReadOnlyFolders = - i18n( "This setting allows you to disable alarms for folders shared by " - "others. "); + i18n( "This setting allows you to disable alarms for folders shared by others. "); mAlarmsBlockedCheckBox = new TQCheckBox( this ); - gl->addWidget( mAlarmsBlockedCheckBox, row, 0 ); - label = new TQLabel( i18n( "Block free/&busy and alarms locally" ), this ); - gl->addWidget( label, row, 1 ); - label->setBuddy( mAlarmsBlockedCheckBox ); + mAlarmsBlockedCheckBox->setText( i18n( "Block alarms locally" ) ); + gl->addMultiCellWidget( mAlarmsBlockedCheckBox, row, row, 0, 1); TQWhatsThis::add( mAlarmsBlockedCheckBox, whatsThisForReadOnlyFolders ); if ( mDlg->folder()->storage()->contentsType() != KMail::ContentsTypeCalendar @@ -514,6 +569,17 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg, mAlarmsBlockedCheckBox->setEnabled( false ); } } + + if ( mDlg->folder()->folderType() == KMFolderTypeCachedImap ) { + kdDebug() << k_funcinfo << mDlg->folder()->folderType() << endl; + mSharedSeenFlagsCheckBox = new TQCheckBox( this ); + mSharedSeenFlagsCheckBox->setText( i18n( "Share unread state with all users" ) ); + ++row; + gl->addMultiCellWidget( mSharedSeenFlagsCheckBox, row, row, 0, 1 ); + TQWhatsThis::add( mSharedSeenFlagsCheckBox, i18n( "If enabled, the unread state of messages in this folder will be the same " + "for all users having access to this folders. If disabled (the default), every user with access to this folder has her " + "own unread state." ) ); + } topLayout->addStretch( 100 ); // eat all superfluous space initializeWithValuesFromFolder( mDlg->folder() ); @@ -568,6 +634,16 @@ void FolderDiaGeneralTab::initializeWithValuesFromFolder( KMFolder* folder ) { KMFolderCachedImap* dimap = static_cast( folder->storage() ); mAlarmsBlockedCheckBox->setChecked( dimap->alarmsBlocked() ); } + if ( mSharedSeenFlagsCheckBox ) { + KMFolderCachedImap *dimap = static_cast( folder->storage() ); + ImapAccountBase *account = dynamic_cast( dimap->account() ); + mSharedSeenFlagsCheckBox->setChecked( dimap->sharedSeenFlags() ); + mSharedSeenFlagsCheckBox->setDisabled( folder->isReadOnly() ); + if ( account && account->hasCapability( "x-kmail-sharedseen" ) ) + mSharedSeenFlagsCheckBox->show(); + else + mSharedSeenFlagsCheckBox->hide(); + } } //----------------------------------------------------------------------------- @@ -614,11 +690,13 @@ bool FolderDiaGeneralTab::save() folder->setPutRepliesInSameFolder( mKeepRepliesInSameFolderCheckBox->isChecked() ); TQString fldName, oldFldName; - if ( !mIsLocalSystemFolder ) + KMFolderCachedImap* dimap = 0; + if ( folder->folderType() == KMFolderTypeCachedImap ) + dimap = static_cast( mDlg->folder()->storage() ); + + if ( !mIsLocalSystemFolder || mIsResourceFolder ) { - TQString acctName; oldFldName = mDlg->folder()->name(); - if (!mNameEdit->text().isEmpty()) fldName = mNameEdit->text(); else @@ -641,11 +719,11 @@ bool FolderDiaGeneralTab::save() folder->setIconPaths( "", "" ); } } - if ( folder->useCustomIcons() && + if ( folder->useCustomIcons() && ( (( mNormalIconButton->icon() != folder->normalIconPath() ) && ( !mNormalIconButton->icon().isEmpty())) || (( mUnreadIconButton->icon() != folder->unreadIconPath() ) && - ( !mUnreadIconButton->icon().isEmpty())) ) { + ( !mUnreadIconButton->icon().isEmpty())) ) ) { folder->setIconPaths( mNormalIconButton->icon(), mUnreadIconButton->icon() ); } @@ -656,8 +734,7 @@ bool FolderDiaGeneralTab::save() folder->storage()->setContentsType( type ); } - if ( folder->folderType() == KMFolderTypeCachedImap ) { - KMFolderCachedImap* dimap = static_cast( mDlg->folder()->storage() ); + if ( dimap ) { if ( mIncidencesForComboBox ) { KMFolderCachedImap::IncidencesFor incfor = KMFolderCachedImap::IncForAdmins; incfor = static_cast( mIncidencesForComboBox->currentItem() ); @@ -678,9 +755,23 @@ bool FolderDiaGeneralTab::save() imapFolder->setIncludeInMailCheck( mNewMailCheckBox->isChecked() ); } - // make sure everything is on disk, connected slots will call readConfig() - // when creating a new folder. - folder->storage()->writeConfig(); + } + + if ( dimap && mSharedSeenFlagsCheckBox && + mSharedSeenFlagsCheckBox->isChecked() != dimap->sharedSeenFlags() ) { + dimap->setSharedSeenFlags( mSharedSeenFlagsCheckBox->isChecked() ); + dimap->writeConfig(); + } + + // make sure everything is on disk, connected slots will call readConfig() + // when creating a new folder. + folder->storage()->writeConfig(); + + TQString msg; + if ( !folder->isValidName( fldName, msg ) ) { + KMessageBox::sorry( this, msg ); + return false; + } else { // Renamed an existing folder? We don't check for oldName == newName on // purpose here. The folder might be pending renaming on the next dimap // sync already, in which case the old name would still be around and @@ -694,6 +785,7 @@ bool FolderDiaGeneralTab::save() kmkernel->folderMgr()->contentsChanged(); } } + return true; } @@ -708,9 +800,8 @@ KMail::FolderDiaTemplatesTab::FolderDiaTemplatesTab( KMFolderDialog* dlg, : FolderDiaTab( parent, 0 ), mDlg( dlg ) { - mIsLocalSystemFolder = mDlg->folder()->isSystemFolder() && - mDlg->folder()->folderType() != KMFolderTypeImap && - mDlg->folder()->folderType() != KMFolderTypeCachedImap; + mIsLocalSystemFolder = mDlg->folder()->isSystemFolder(); + TQVBoxLayout *topLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() ); diff --git a/kmail/kmfolderdia.h b/kmail/kmfolderdia.h index 913685d8..4db30ab1 100644 --- a/kmail/kmfolderdia.h +++ b/kmail/kmfolderdia.h @@ -137,6 +137,7 @@ private: TQComboBox *mContentsComboBox; TQComboBox *mIncidencesForComboBox; TQCheckBox *mAlarmsBlockedCheckBox; + TQCheckBox *mSharedSeenFlagsCheckBox; TQLabel *mNormalIconLabel; KIconButton *mNormalIconButton; TQLabel *mUnreadIconLabel; @@ -151,6 +152,7 @@ private: KMFolderDialog* mDlg; bool mIsLocalSystemFolder; + bool mIsResourceFolder; }; /** diff --git a/kmail/kmfolderdir.cpp b/kmail/kmfolderdir.cpp index 46aba345..1ecab637 100644 --- a/kmail/kmfolderdir.cpp +++ b/kmail/kmfolderdir.cpp @@ -163,6 +163,31 @@ TQString KMFolderDir::prettyURL() const return label(); } +//----------------------------------------------------------------------------- +void KMFolderDir::addDirToParent( const TQString &dirName, KMFolder *parentFolder ) +{ + KMFolderDir* folderDir = new KMFolderDir( parentFolder, this, dirName, mDirType); + folderDir->reload(); + append( folderDir ); + parentFolder->setChild( folderDir ); +} + +// Get the default folder type of the given dir type. This function should only be used when +// needing to find out what the folder type of a missing folder is. +KMFolderType dirTypeToFolderType( KMFolderDirType dirType ) +{ + switch( dirType ) { + + // Use maildir for normal folder dirs, as this function is only called when finding a dir + // without a parent folder, which can only happen with maildir-like folders + case KMStandardDir: return KMFolderTypeMaildir; + + case KMImapDir: return KMFolderTypeImap; + case KMDImapDir: return KMFolderTypeCachedImap; + case KMSearchDir: return KMFolderTypeSearch; + default: Q_ASSERT( false ); return KMFolderTypeMaildir; + } +} //----------------------------------------------------------------------------- bool KMFolderDir::reload(void) @@ -272,6 +297,7 @@ bool KMFolderDir::reload(void) } } + TQStringList dirsWithoutFolder = diList; for (folder=folderList.first(); folder; folder=folderList.next()) { for(TQStringList::Iterator it = diList.begin(); @@ -279,13 +305,36 @@ bool KMFolderDir::reload(void) ++it) if (*it == "." + folder->fileName() + ".directory") { - KMFolderDir* folderDir = new KMFolderDir( folder, this, *it, mDirType); - folderDir->reload(); - append(folderDir); - folder->setChild(folderDir); + dirsWithoutFolder.remove( *it ); + addDirToParent( *it, folder ); break; } } + + // Check if the are any dirs without an associated folder. This can happen if the user messes + // with the on-disk folder structure, see kolab issue 2972. In that case, we don't want to loose + // the subfolders as well, so we recreate the folder so the folder/dir hierachy is OK again. + if ( type() == KMDImapDir ) { + for ( TQStringList::Iterator it = dirsWithoutFolder.begin(); + it != dirsWithoutFolder.end(); ++it ) { + + // .foo.directory => foo + TQString folderName = *it; + int right = folderName.find( ".directory" ); + int left = folderName.find( "." ); + Q_ASSERT( left != -1 && right != -1 ); + folderName = folderName.mid( left + 1, right - 1 ); + + kdDebug(5006) << "Found dir without associated folder: " << ( *it ) << ", recreating the folder " << folderName << "." << endl; + + // Recreate the missing folder + KMFolder *folder = new KMFolder( this, folderName, KMFolderTypeCachedImap ); + append( folder ); + folderList.append( folder ); + + addDirToParent( *it, folder ); + } + } return TRUE; } diff --git a/kmail/kmfolderdir.h b/kmail/kmfolderdir.h index 72f4c432..c93317e5 100644 --- a/kmail/kmfolderdir.h +++ b/kmail/kmfolderdir.h @@ -19,11 +19,16 @@ class KMFolderDir: public KMFolderNode, public KMFolderNodeList public: KMFolderDir( KMFolder * owner, KMFolderDir * parent = 0, const TQString& path = TQString::null, - KMFolderDirType = KMStandardDir ); + KMFolderDirType = KMStandardDir ); virtual ~KMFolderDir(); virtual bool isDir() const { return true; } + /** + * Adds the given subdirectory of this directory to the associated folder. + */ + void addDirToParent( const TQString &dirName, KMFolder *parentFolder ); + /** Read contents of directory. */ virtual bool reload(); @@ -39,9 +44,9 @@ public: /** Create a mail folder in this directory with given name. If sysFldr==TRUE the folder is marked as a (KMail) system folder. Returns Folder on success. */ - virtual KMFolder* createFolder(const TQString& folderName, - bool sysFldr=false, - KMFolderType folderType=KMFolderTypeMbox); + virtual KMFolder* createFolder( const TQString& folderName, + bool sysFldr=false, + KMFolderType folderType=KMFolderTypeMbox ); /** Returns folder with given name or zero if it does not exist */ virtual KMFolderNode* hasNamedFolder(const TQString& name); @@ -67,9 +72,9 @@ class KMFolderRootDir: public KMFolderDir Q_OBJECT public: - KMFolderRootDir(KMFolderMgr* manager, - const TQString& path=TQString::null, - KMFolderDirType dirType = KMStandardDir); + KMFolderRootDir( KMFolderMgr* manager, + const TQString& path=TQString::null, + KMFolderDirType dirType = KMStandardDir ); virtual ~KMFolderRootDir(); virtual TQString path() const; diff --git a/kmail/kmfolderimap.cpp b/kmail/kmfolderimap.cpp index f32162f8..e5c7bd82 100644 --- a/kmail/kmfolderimap.cpp +++ b/kmail/kmfolderimap.cpp @@ -49,6 +49,7 @@ using KMail::ListJob; using KMail::SearchJob; #include "renamejob.h" using KMail::RenameJob; +#include "acljobs.h" #include #include @@ -73,6 +74,7 @@ KMFolderImap::KMFolderImap(KMFolder* folder, const char* aName) mCheckMail = true; mCheckingValidity = false; mUserRights = 0; + mUserRightsState = KMail::ACLJobs::NotFetchedYet; mAlreadyRemoved = false; mHasChildren = ChildrenUnknown; mMailCheckProgressItem = 0; @@ -108,12 +110,6 @@ KMFolderImap::~KMFolderImap() //----------------------------------------------------------------------------- void KMFolderImap::reallyDoClose(const char* owner) { - if (isSelected()) { - kdWarning(5006) << "Trying to close the selected folder " << label() << - " - ignoring!" << endl; - return; - } - // FIXME is this still needed? if (account()) account()->ignoreJobsForFolder( folder() ); @@ -334,8 +330,6 @@ void KMFolderImap::addMsgQuiet(KMMessage* aMsg) int idx = aFolder->find( aMsg ); assert( idx != -1 ); aFolder->take( idx ); - } else { - kdDebug(5006) << k_funcinfo << "no parent" << endl; } if ( !account()->hasCapability("uidplus") ) { // Remember the status with the MD5 as key @@ -999,6 +993,13 @@ void KMFolderImap::initializeFrom( KMFolderImap* parent, TQString folderPath, setNoChildren( mimeType == "message/digest" ); } +//----------------------------------------------------------------------------- +bool KMFolderImap::mailCheckInProgress() const +{ + return getContentState() != imapNoInformation && + getContentState() != imapFinished; +} + //----------------------------------------------------------------------------- void KMFolderImap::setChildrenState( TQString attributes ) { @@ -1188,7 +1189,7 @@ void KMFolderImap::getAndCheckFolder(bool force) return getFolder(force); if ( account() ) - account()->processNewMailSingleFolder( folder() ); + account()->processNewMailInFolder( folder() ); if (force) { // force an update mCheckFlags = true; @@ -2261,10 +2262,10 @@ int KMFolderImap::expungeContents() //----------------------------------------------------------------------------- void -KMFolderImap::setUserRights( unsigned int userRights ) +KMFolderImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState userRightsState ) { mUserRights = userRights; - kdDebug(5006) << imapPath() << " setUserRights: " << userRights << endl; + mUserRightsState = userRightsState; } //----------------------------------------------------------------------------- @@ -2392,7 +2393,7 @@ bool KMFolderImap::isMoveable() const } //----------------------------------------------------------------------------- -const ulong KMFolderImap::serNumForUID( ulong uid ) +ulong KMFolderImap::serNumForUID( ulong uid ) { if ( mUidMetaDataMap.find( uid ) ) { KMMsgMetaData *md = mUidMetaDataMap[uid]; @@ -2431,4 +2432,13 @@ void KMFolderImap::finishMailCheck( const char *dbg, imapState state ) close(dbg); } +bool KMFolderImap::canDeleteMessages() const +{ + if ( isReadOnly() ) + return false; + if ( mUserRightsState == KMail::ACLJobs::Ok && !(mUserRights & KMail::ACLJobs::Delete) ) + return false; + return true; +} + #include "kmfolderimap.moc" diff --git a/kmail/kmfolderimap.h b/kmail/kmfolderimap.h index 7b25520f..76a3db98 100644 --- a/kmail/kmfolderimap.h +++ b/kmail/kmfolderimap.h @@ -24,6 +24,7 @@ #ifndef kmfolderimap_h #define kmfolderimap_h +#include "acljobs.h" #include "kmacctimap.h" #include "kmfoldermbox.h" #include "kmmsgbase.h" @@ -65,8 +66,8 @@ public: KMMsgMetaData(KMMsgStatus aStatus, Q_UINT32 aSerNum) :mStatus(aStatus), mSerNum(aSerNum) {} ~KMMsgMetaData() {}; - const KMMsgStatus status() const { return mStatus; } - const Q_UINT32 serNum() const { return mSerNum; } + KMMsgStatus status() const { return mStatus; } + Q_UINT32 serNum() const { return mSerNum; } private: KMMsgStatus mStatus; Q_UINT32 mSerNum; @@ -91,7 +92,7 @@ public: imapFinished = 3 }; - virtual imapState getContentState() { return mContentState; } + virtual imapState getContentState() const { return mContentState; } virtual void setContentState(imapState state) { mContentState = state; } virtual imapState getSubfolderState() { return mSubfolderState; } @@ -250,7 +251,7 @@ public: /** * Get the serial number for the given UID (if available) */ - const ulong serNumForUID( ulong uid ); + ulong serNumForUID( ulong uid ); /** * Save the metadata for the UID @@ -294,15 +295,18 @@ public: /// Is the folder readonly? bool isReadOnly() const { return KMFolderMbox::isReadOnly() || mReadOnly; } + bool canDeleteMessages() const; /** * The user's rights on this folder - see bitfield in ACLJobs namespace. - * @return 0 when not known yet + * Note that the returned value is only valid if userRightsState() returns Ok, so + * that should be checked first. */ unsigned int userRights() const { return mUserRights; } + KMail::ACLJobs::ACLFetchState userRightsState() const { return mUserRightsState; } /** Set the user's rights on this folder - called by getUserRights */ - void setUserRights( unsigned int userRights ); + void setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState userRightsState ); /** * Search for messages @@ -321,6 +325,8 @@ public: /** Returns the IMAP flags that can be stored on the server. */ int permanentFlags() const { return mPermanentFlags; } + virtual bool mailCheckInProgress() const; + signals: void folderComplete(KMFolderImap *folder, bool success); @@ -515,6 +521,7 @@ protected: // the current uidvalidity TQString mUidValidity; unsigned int mUserRights; + KMail::ACLJobs::ACLFetchState mUserRightsState; private: // if we're checking validity currently diff --git a/kmail/kmfolderindex.cpp b/kmail/kmfolderindex.cpp index 6a378157..95746ed1 100644 --- a/kmail/kmfolderindex.cpp +++ b/kmail/kmfolderindex.cpp @@ -18,6 +18,8 @@ #include "kmfolderindex.h" #include "kmfolder.h" +#include "kmfoldertype.h" +#include "kcursorsaver.h" #include #include #include @@ -31,7 +33,7 @@ #endif // Current version of the table of contents (index) files -#define INDEX_VERSION 1506 +#define INDEX_VERSION 1507 #ifndef MAX_LINE #define MAX_LINE 4096 @@ -110,9 +112,13 @@ int KMFolderIndex::updateIndex() return 0; bool dirty = mDirty; mDirtyTimer->stop(); - for (unsigned int i=0; !dirty && isyncIndexString(); + for ( unsigned int i = 0; !dirty && i < mMsgList.high(); i++ ) { + if ( mMsgList.at(i) ) { + if ( !mMsgList.at(i)->syncIndexString() ) { + dirty = true; + } + } + } if (!dirty) { // Update successful touchFolderIdsFile(); return 0; @@ -209,9 +215,11 @@ int KMFolderIndex::writeIndex( bool createEmptyIndex ) return 0; } - bool KMFolderIndex::readIndex() { + if ( contentsType() != KMail::ContentsTypeMail ) { + kdDebug(5006) << k_funcinfo << "Reading index for " << label() << endl; + } Q_INT32 len; KMMsgInfo* mi; @@ -224,6 +232,7 @@ bool KMFolderIndex::readIndex() setDirty( false ); if (!readIndexHeader(&version)) return false; + //kdDebug(5006) << "Index version for " << label() << " is " << version << endl; mUnreadMsgs = 0; mTotalMsgs = 0; @@ -234,15 +243,20 @@ bool KMFolderIndex::readIndex() { mi = 0; if(version >= 1505) { - if(!fread(&len, sizeof(len), 1, mIndexStream)) + if(!fread(&len, sizeof(len), 1, mIndexStream)) { + // Seems to be normal? + // kdDebug(5006) << k_funcinfo << " Unable to read length field!" << endl; break; + } if (mIndexSwapByteOrder) len = kmail_swap_32(len); off_t offs = ftell(mIndexStream); - if(fseek(mIndexStream, len, SEEK_CUR)) + if(fseek(mIndexStream, len, SEEK_CUR)) { + kdDebug(5006) << k_funcinfo << " Unable to seek to the end of the message!" << endl; break; + } mi = new KMMsgInfo(folder(), offs, len); } else @@ -251,16 +265,18 @@ bool KMFolderIndex::readIndex() fgets(line.data(), MAX_LINE, mIndexStream); if (feof(mIndexStream)) break; if (*line.data() == '\0') { - fclose(mIndexStream); - mIndexStream = 0; - clearIndex(); - return false; + fclose(mIndexStream); + mIndexStream = 0; + clearIndex(); + return false; } mi = new KMMsgInfo(folder()); mi->compat_fromOldIndexString(line, mConvertToUtf8); } - if(!mi) + if(!mi) { + kdDebug(5006) << k_funcinfo << " Unable to create message info object!" << endl; break; + } if (mi->isDeleted()) { @@ -290,7 +306,17 @@ bool KMFolderIndex::readIndex() setDirty( true ); writeIndex(); } + + if ( version < 1507 ) { + updateInvitationAndAddressFieldsFromContents(); + setDirty( true ); + writeIndex(); + } + mTotalMsgs = mMsgList.count(); + if ( contentsType() != KMail::ContentsTypeMail ) { + kdDebug(5006) << k_funcinfo << "Done reading the index for " << label() << ", we have " << mTotalMsgs << " messages." << endl; + } return true; } @@ -316,6 +342,15 @@ bool KMFolderIndex::readIndexHeader(int *gv) return false; // index file has invalid header if(gv) *gv = indexVersion; + + // Check if the index is corrupted ("not compactable") and recreate it if necessary. See + // FolderStorage::getMsg() for the detection code. + if ( !mCompactable ) { + kdWarning(5006) << "Index file " << indexLocation() << " is corrupted!!. Re-creating it." << endl; + recreateIndex( false /* don't call readIndex() afterwards */ ); + return false; + } + if (indexVersion < 1505 ) { if(indexVersion == 1503) { kdDebug(5006) << "Converting old index file " << indexLocation() << " to utf-8" << endl; @@ -323,7 +358,7 @@ bool KMFolderIndex::readIndexHeader(int *gv) } return true; } else if (indexVersion == 1505) { - } else if (indexVersion < INDEX_VERSION) { + } else if (indexVersion < INDEX_VERSION && indexVersion != 1506) { kdDebug(5006) << "Index file " << indexLocation() << " is out of date. Re-creating it." << endl; createIndexFromContents(); return false; @@ -434,6 +469,9 @@ bool KMFolderIndex::updateIndexStreamPtr(bool) KMFolderIndex::IndexStatus KMFolderIndex::indexStatus() { + if ( !mCompactable ) + return IndexCorrupt; + TQFileInfo contInfo(location()); TQFileInfo indInfo(indexLocation()); @@ -484,16 +522,56 @@ KMMsgInfo* KMFolderIndex::setIndexEntry( int idx, KMMessage *msg ) return msgInfo; } -void KMFolderIndex::recreateIndex() +void KMFolderIndex::recreateIndex( bool readIndexAfterwards ) { kapp->setOverrideCursor(KCursor::arrowCursor()); - KMessageBox::error(0, + KMessageBox::information(0, i18n("The mail index for '%1' is corrupted and will be regenerated now, " - "but some information, including status flags, will be lost.").arg(name())); + "but some information, like status flags, might get lost.").arg(name())); kapp->restoreOverrideCursor(); createIndexFromContents(); - readIndex(); + if ( readIndexAfterwards ) { + readIndex(); + } + + // Clear the corrupted flag + mCompactable = true; + writeConfig(); +} + +void KMFolderIndex::silentlyRecreateIndex() +{ + Q_ASSERT( !isOpened() ); + open( "silentlyRecreateIndex" ); + KCursorSaver busy( KBusyPtr::busy() ); + createIndexFromContents(); + mCompactable = true; + writeConfig(); + close( "silentlyRecreateIndex" ); } +void KMFolderIndex::updateInvitationAndAddressFieldsFromContents() +{ + kdDebug(5006) << "Updating index for " << label() << ", this might take a while." << endl; + for ( uint i = 0; i < mMsgList.size(); i++ ) { + KMMsgInfo * const msgInfo = dynamic_cast( mMsgList[i] ); + if ( msgInfo ) { + DwString msgString( getDwString( i ) ); + if ( msgString.size() > 0 ) { + KMMessage msg; + msg.fromDwString( msgString, false ); + msg.updateInvitationState(); + if ( msg.status() & KMMsgStatusHasInvitation ) { + msgInfo->setStatus( msgInfo->status() | KMMsgStatusHasInvitation ); + } + if ( msg.status() & KMMsgStatusHasNoInvitation ) { + msgInfo->setStatus( msgInfo->status() | KMMsgStatusHasNoInvitation ); + } + msgInfo->setFrom( msg.from() ); + msgInfo->setTo( msg.to() ); + } + } + } +} #include "kmfolderindex.moc" diff --git a/kmail/kmfolderindex.h b/kmail/kmfolderindex.h index 05453bb6..29039765 100644 --- a/kmail/kmfolderindex.h +++ b/kmail/kmfolderindex.h @@ -48,6 +48,7 @@ public: */ enum IndexStatus { IndexOk, IndexMissing, + IndexCorrupt, IndexTooOld }; @@ -80,7 +81,16 @@ public: virtual TQString indexLocation() const; virtual int writeIndex( bool createEmptyIndex = false ); - void recreateIndex(); + void recreateIndex( bool readIndexAfterwards = true ); + void silentlyRecreateIndex(); + + /** Tests whether the contents of this folder is newer than the index. + Should return IndexTooOld if the index is older than the contents. + Should return IndexMissing if there is contents but no index. + Should return IndexOk if the folder doesn't exist anymore "physically" + or if the index is not older than the contents. + */ + virtual IndexStatus indexStatus() = 0; public slots: /** Incrementally update the index if possible else call writeIndex */ @@ -99,14 +109,6 @@ protected: bool updateIndexStreamPtr(bool just_close=FALSE); - /** Tests whether the contents of this folder is newer than the index. - Should return IndexTooOld if the index is older than the contents. - Should return IndexMissing if there is contents but no index. - Should return IndexOk if the folder doesn't exist anymore "physically" - or if the index is not older than the contents. - */ - virtual IndexStatus indexStatus() = 0; - /** Inserts messages into the message dictionary by iterating over the * message list. The messages will get new serial numbers. This is only * used on newly appeared folders, where there is no .ids file yet, or @@ -125,6 +127,10 @@ protected: int mIndexStreamPtrLength, mIndexId; bool mIndexSwapByteOrder; // Index file was written with swapped byte order int mIndexSizeOfLong; // Index file was written with longs of this size + +private: + void updateInvitationAndAddressFieldsFromContents(); + }; #endif /*kmfolderindex_h*/ diff --git a/kmail/kmfoldermaildir.cpp b/kmail/kmfoldermaildir.cpp index bfb606f7..09800a94 100644 --- a/kmail/kmfoldermaildir.cpp +++ b/kmail/kmfoldermaildir.cpp @@ -224,6 +224,7 @@ int KMFolderMaildir::create() //----------------------------------------------------------------------------- void KMFolderMaildir::reallyDoClose(const char* owner) { + Q_UNUSED( owner ); if (mAutoCreateIndex) { updateIndex(); @@ -465,9 +466,12 @@ if( fileD0.open( IO_WriteOnly ) ) { ++mTotalMsgs; mSize = -1; - if ( aMsg->attachmentState() == KMMsgAttachmentUnknown && - aMsg->readyToShow() ) + if ( aMsg->attachmentState() == KMMsgAttachmentUnknown && aMsg->readyToShow() ) { aMsg->updateAttachmentState(); + } + if ( aMsg->invitationState() == KMMsgInvitationUnknown && aMsg->readyToShow() ) { + aMsg->updateInvitationState(); + } // store information about the position in the folder file in the message aMsg->setParent(folder()); @@ -743,7 +747,7 @@ void KMFolderMaildir::readFileHeaderIntern(const TQString& dir, const TQString& } // Is this a long header line? - if (inHeader && line[0] == '\t' || line[0] == ' ') + if (inHeader && ( line[0] == '\t' || line[0] == ' ' ) ) { int i = 0; while (line[i] == '\t' || line[i] == ' ') @@ -901,6 +905,9 @@ int KMFolderMaildir::createIndexFromContents() KMFolderIndex::IndexStatus KMFolderMaildir::indexStatus() { + if ( !mCompactable ) + return KMFolderIndex::IndexCorrupt; + TQFileInfo new_info(location() + "/new"); TQFileInfo cur_info(location() + "/cur"); TQFileInfo index_info(indexLocation()); diff --git a/kmail/kmfoldermbox.cpp b/kmail/kmfoldermbox.cpp index dc35328d..c2e60a09 100644 --- a/kmail/kmfoldermbox.cpp +++ b/kmail/kmfoldermbox.cpp @@ -94,6 +94,7 @@ KMFolderMbox::~KMFolderMbox() //----------------------------------------------------------------------------- int KMFolderMbox::open(const char *owner) { + Q_UNUSED( owner ); int rc = 0; mOpenCount++; @@ -258,6 +259,7 @@ int KMFolderMbox::create() //----------------------------------------------------------------------------- void KMFolderMbox::reallyDoClose(const char* owner) { + Q_UNUSED( owner ); if (mAutoCreateIndex) { if (KMFolderIndex::IndexOk != indexStatus()) { @@ -521,6 +523,9 @@ int KMFolderMbox::unlock() //----------------------------------------------------------------------------- KMFolderIndex::IndexStatus KMFolderMbox::indexStatus() { + if ( !mCompactable ) + return KMFolderIndex::IndexCorrupt; + TQFileInfo contInfo(location()); TQFileInfo indInfo(indexLocation()); @@ -1065,9 +1070,12 @@ if( fileD1.open( IO_WriteOnly ) ) { ++mTotalMsgs; mSize = -1; - if ( aMsg->attachmentState() == KMMsgAttachmentUnknown && - aMsg->readyToShow() ) + if ( aMsg->attachmentState() == KMMsgAttachmentUnknown && aMsg->readyToShow() ) { aMsg->updateAttachmentState(); + } + if ( aMsg->invitationState() == KMMsgInvitationUnknown && aMsg->readyToShow() ) { + aMsg->updateInvitationState(); + } // store information about the position in the folder file in the message aMsg->setParent(folder()); @@ -1095,13 +1103,13 @@ if( fileD1.open( IO_WriteOnly ) ) { revert = ftell(mIndexStream); KMMsgBase * mb = &aMsg->toMsgBase(); - int len; - const uchar *buffer = mb->asIndexString(len); - fwrite(&len,sizeof(len), 1, mIndexStream); - mb->setIndexOffset( ftell(mIndexStream) ); - mb->setIndexLength( len ); - if(fwrite(buffer, len, 1, mIndexStream) != 1) - kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl; + int len; + const uchar *buffer = mb->asIndexString(len); + fwrite(&len,sizeof(len), 1, mIndexStream); + mb->setIndexOffset( ftell(mIndexStream) ); + mb->setIndexLength( len ); + if(fwrite(buffer, len, 1, mIndexStream) != 1) + kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl; fflush(mIndexStream); error = ferror(mIndexStream); diff --git a/kmail/kmfoldersearch.cpp b/kmail/kmfoldersearch.cpp index 85ba2040..522785d1 100644 --- a/kmail/kmfoldersearch.cpp +++ b/kmail/kmfoldersearch.cpp @@ -547,6 +547,7 @@ void KMFolderSearch::sync() void KMFolderSearch::reallyDoClose(const char* owner) { + Q_UNUSED( owner ); if (mAutoCreateIndex) { if (mSearch) mSearch->write(location()); diff --git a/kmail/kmfolderseldlg.cpp b/kmail/kmfolderseldlg.cpp index 607b4243..00849b6e 100644 --- a/kmail/kmfolderseldlg.cpp +++ b/kmail/kmfolderseldlg.cpp @@ -2,7 +2,6 @@ #include #include "kmfolderseldlg.h" -#include "kmfoldertree.h" #include "kmfolder.h" #include "kmmainwidget.h" #include "globalsettings.h" @@ -14,429 +13,10 @@ #include #include - -namespace KMail { - -class FolderItem : public KFolderTreeItem -{ - public: - FolderItem( KFolderTree * listView ); - FolderItem( KFolderTreeItem * listViewItem ); - - void setFolder( KMFolder * folder ) { mFolder = folder; }; - const KMFolder * folder() { return mFolder; }; - - // Redefine isAlternate() for proper row coloring behavior. - // KListViewItem::isAlternate() is not virtual! Therefore, - // it is necessary to overload paintCell() below. If it were - // made virtual, paintCell() would no longer be necessary. - bool isAlternate () { - return mAlternate; - } - - // Set the flag which determines if this is an alternate row - void setAlternate ( bool alternate ) { - mAlternate = alternate; - } - - // Must overload paintCell because neither KListViewItem::isAlternate() - // or KListViewItem::backgroundColor() are virtual! - virtual void paintCell( TQPainter *p, const TQColorGroup &cg, - int column, int width, int alignment ) - { - KListView* view = static_cast< KListView* >( listView() ); - - // Set alternate background to invalid - TQColor nocolor; - TQColor alt = view->alternateBackground(); - view->setAlternateBackground( nocolor ); - - // Set the base and text to the appropriate colors - TQColorGroup *cgroup = (TQColorGroup *)&view->viewport()->colorGroup(); - TQColor base = cgroup->base(); - TQColor text = cgroup->text(); - cgroup->setColor( TQColorGroup::Base, isAlternate() ? alt : base ); - cgroup->setColor( TQColorGroup::Text, isEnabled() ? text : Qt::lightGray ); - - // Call the parent paint routine - KListViewItem::paintCell( p, cg, column, width, alignment ); - - // Restore the base and alternate background - cgroup->setColor( TQColorGroup::Base, base ); - cgroup->setColor( TQColorGroup::Text, text ); - view->setAlternateBackground( alt ); - } - - private: - KMFolder * mFolder; - bool mAlternate; -}; - -//----------------------------------------------------------------------------- -FolderItem::FolderItem( KFolderTree * listView ) - : KFolderTreeItem( listView ), - mFolder( 0 ) -{} - -//----------------------------------------------------------------------------- -FolderItem::FolderItem( KFolderTreeItem * listViewItem ) - : KFolderTreeItem( listViewItem ), - mFolder( 0 ) -{} - -//----------------------------------------------------------------------------- -SimpleFolderTree::SimpleFolderTree( TQWidget * parent, - KMFolderTree * folderTree, - const TQString & preSelection, - bool mustBeReadWrite ) - : KFolderTree( parent ), mFolderTree( folderTree ) -{ - setSelectionModeExt( Single ); - mFolderColumn = addColumn( i18n( "Folder" ), 0 ); - mPathColumn = addColumn( i18n( "Path" ), 0 ); - setAllColumnsShowFocus( true ); - setAlternateBackground( TQColor( 0xf0, 0xf0, 0xf0 ) ); - - reload( mustBeReadWrite, true, true, preSelection ); - readColorConfig(); - - applyFilter( "" ); - - connect(this, TQT_SIGNAL(collapsed(TQListViewItem*)), TQT_SLOT(recolorRows())); - connect(this, TQT_SIGNAL(expanded(TQListViewItem*)), TQT_SLOT(recolorRows())); - - connect( this, TQT_SIGNAL( contextMenuRequested( TQListViewItem*, const TQPoint &, int ) ), - this, TQT_SLOT( slotContextMenuRequested( TQListViewItem*, const TQPoint & ) ) ); -} - -//----------------------------------------------------------------------------- -void SimpleFolderTree::reload( bool mustBeReadWrite, bool showOutbox, - bool showImapFolders, const TQString& preSelection ) -{ - mLastMustBeReadWrite = mustBeReadWrite; - mLastShowOutbox = showOutbox; - mLastShowImapFolders = showImapFolders; - - clear(); - FolderItem * lastItem = 0; - FolderItem * lastTopItem = 0; - FolderItem * selectedItem = 0; - int lastDepth = 0; - - TQString selected = preSelection; - if ( selected.isEmpty() && folder() ) - selected = folder()->idString(); - - mFilter = ""; - TQString path; - - for ( TQListViewItemIterator it( mFolderTree ) ; it.current() ; ++it ) - { - KMFolderTreeItem * fti = static_cast( it.current() ); - - // search folders are never shown - if ( !fti || fti->protocol() == KFolderTreeItem::Search ) - continue; - - // imap folders? - if ( fti->protocol() == KFolderTreeItem::Imap && !showImapFolders ) - continue; - - // the outbox? - if ( fti->type() == KFolderTreeItem::Outbox && !showOutbox ) - continue; - - int depth = fti->depth();// - 1; - FolderItem * item = 0; - if ( depth <= 0 ) { - // top level - first top level item or after last existing top level item - item = new FolderItem( this ); - if ( lastTopItem ) - item->moveItem( lastTopItem ); - lastTopItem = item; - depth = 0; - path = ""; - } - else { - if ( depth > lastDepth ) { - // next lower level - parent node will get opened - item = new FolderItem( lastItem ); - lastItem->setOpen(true); - } - else { - path = path.section( '/', 0, -2 - (lastDepth-depth) ); - - if ( depth == lastDepth ) { - // same level - behind previous item - item = new FolderItem( static_cast(lastItem->parent()) ); - item->moveItem( lastItem ); - } else if ( depth < lastDepth ) { - // above previous level - might be more than one level difference - // but highest possibility is top level - while ( ( depth <= --lastDepth ) && lastItem->parent() ) { - lastItem = static_cast( lastItem->parent() ); - } - if ( lastItem->parent() ) { - item = new FolderItem( static_cast(lastItem->parent()) ); - item->moveItem( lastItem ); - } else { - // chain somehow broken - what does cause this ??? - kdDebug( 5006 ) << "You shouldn't get here: depth=" << depth - << "folder name=" << fti->text( 0 ) << endl; - item = new FolderItem( this ); - lastTopItem = item; - } - } - } - } - - if ( depth > 0 ) - path += "/"; - path += fti->text( 0 ); - - item->setText( mFolderColumn, fti->text( 0 ) ); - item->setText( mPathColumn, path ); - - item->setProtocol( fti->protocol() ); - item->setType( fti->type() ); - - // Make items without folders and readonly items unselectable - // if we're told so - if ( mustBeReadWrite && ( !fti->folder() || fti->folder()->isReadOnly() ) ) { - item->setSelectable( false ); - } else { - if ( fti->folder() ) { - item->setFolder( fti->folder() ); - if ( selected == item->folder()->idString() ) - selectedItem = item; - } - } - lastItem = item; - lastDepth = depth; - } - - if ( selectedItem ) { - setSelected( selectedItem, true ); - ensureItemVisible( selectedItem ); - } -} - -//----------------------------------------------------------------------------- -const KMFolder * SimpleFolderTree::folder() const -{ - TQListViewItem * item = currentItem(); - if ( item ) { - const KMFolder * folder = static_cast( item )->folder(); - if( folder ) return folder; - } - return 0; -} - -//----------------------------------------------------------------------------- -void SimpleFolderTree::setFolder( KMFolder *folder ) -{ - for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) - { - const KMFolder *fld = static_cast( it.current() )->folder(); - if ( fld == folder ) - { - setSelected( it.current(), true ); - ensureItemVisible( it.current() ); - } - } -} - -//----------------------------------------------------------------------------- -void SimpleFolderTree::setFolder( const TQString& idString ) -{ - setFolder( kmkernel->findFolderById( idString ) ); -} - -//----------------------------------------------------------------------------- -void SimpleFolderTree::addChildFolder() -{ - const KMFolder *fld = folder(); - if ( fld ) { - mFolderTree->addChildFolder( (KMFolder *) fld, parentWidget() ); - reload( mLastMustBeReadWrite, mLastShowOutbox, mLastShowImapFolders ); - setFolder( (KMFolder *) fld ); - } -} - -//----------------------------------------------------------------------------- -void SimpleFolderTree::slotContextMenuRequested( TQListViewItem *lvi, - const TQPoint &p ) -{ - if (!lvi) - return; - setCurrentItem( lvi ); - setSelected( lvi, TRUE ); - - const KMFolder * folder = static_cast( lvi )->folder(); - if ( !folder || folder->noContent() || folder->noChildren() ) - return; - - KPopupMenu *folderMenu = new KPopupMenu; - folderMenu->insertTitle( folder->label() ); - folderMenu->insertSeparator(); - folderMenu->insertItem(SmallIconSet("folder_new"), - i18n("&New Subfolder..."), this, - TQT_SLOT(addChildFolder())); - kmkernel->setContextMenuShown( true ); - folderMenu->exec (p, 0); - kmkernel->setContextMenuShown( false ); - delete folderMenu; - folderMenu = 0; -} - -//----------------------------------------------------------------------------- -void SimpleFolderTree::readColorConfig (void) -{ - TQColor c1=TQColor(kapp->palette().active().text()); - TQColor c2=TQColor(kapp->palette().active().base()); - - mPaintInfo.colFore = c1; - mPaintInfo.colBack = c2; - - TQPalette newPal = kapp->palette(); - newPal.setColor( TQColorGroup::Base, mPaintInfo.colBack ); - newPal.setColor( TQColorGroup::Text, mPaintInfo.colFore ); - setPalette( newPal ); -} - - -//----------------------------------------------------------------------------- -static int recurseFilter( TQListViewItem * item, const TQString& filter, int column ) -{ - if ( item == 0 ) - return 0; - - TQListViewItem * child; - child = item->firstChild(); - - int enabled = 0; - while ( child ) { - enabled += recurseFilter( child, filter, column ); - child = child->nextSibling(); - } - - if ( filter.length() == 0 || - item->text( column ).find( filter, 0, false ) >= 0 ) { - item->setVisible( true ); - ++enabled; - } - else { - item->setVisible( !!enabled ); - item->setEnabled( false ); - } - - return enabled; -} - -void SimpleFolderTree::recolorRows() -{ - // Iterate through the list to set the alternate row flags. - int alt = 0; - TQListViewItemIterator it ( this ); - while ( it.current() ) { - FolderItem * item = static_cast< FolderItem* >( it.current() ); - - if ( item->isVisible() ) { - bool visible = true; - TQListViewItem * parent = item->parent(); - while ( parent ) { - if (!parent->isOpen()) { - visible = false; - break; - } - parent = parent->parent(); - } - - if ( visible ) { - item->setAlternate( alt ); - alt = !alt; - } - } - - ++it; - } -} - -void SimpleFolderTree::applyFilter( const TQString& filter ) -{ - // Reset all items to visible, enabled, and open - TQListViewItemIterator clean( this ); - while ( clean.current() ) { - TQListViewItem * item = clean.current(); - item->setEnabled( true ); - item->setVisible( true ); - item->setOpen( true ); - ++clean; - } - - mFilter = filter; - - if ( filter.isEmpty() ) { - setColumnText( mPathColumn, i18n("Path") ); - return; - } - - // Set the visibility and enabled status of each list item. - // The recursive algorithm is necessary because visiblity - // changes are automatically applied to child nodes by Qt. - TQListViewItemIterator it( this ); - while ( it.current() ) { - TQListViewItem * item = it.current(); - if ( item->depth() <= 0 ) - recurseFilter( item, filter, mPathColumn ); - ++it; - } - - // Recolor the rows appropriately - recolorRows(); - - // Iterate through the list to find the first selectable item - TQListViewItemIterator first ( this ); - while ( first.current() ) { - FolderItem * item = static_cast< FolderItem* >( first.current() ); - - if ( item->isVisible() && item->isSelectable() ) { - setSelected( item, true ); - ensureItemVisible( item ); - break; - } - - ++first; - } - - // Display and save the current filter - if ( filter.length() > 0 ) - setColumnText( mPathColumn, i18n("Path") + " ( " + filter + " )" ); - else - setColumnText( mPathColumn, i18n("Path") ); - - mFilter = filter; -} - -//----------------------------------------------------------------------------- -void SimpleFolderTree::keyPressEvent( TQKeyEvent *e ) { - int ch = e->ascii(); - - if ( ch >= 32 && ch <= 126 ) - applyFilter( mFilter + ch ); - - else if ( ch == 8 || ch == 127 ) { - if ( mFilter.length() > 0 ) { - mFilter.truncate( mFilter.length()-1 ); - applyFilter( mFilter ); - } - } - - else - KListView::keyPressEvent( e ); -} +#include +using namespace KMail; //----------------------------------------------------------------------------- KMFolderSelDlg::KMFolderSelDlg( KMMainWidget * parent, const TQString& caption, bool mustBeReadWrite, bool useGlobalSettings ) @@ -452,7 +32,9 @@ KMFolderSelDlg::KMFolderSelDlg( KMMainWidget * parent, const TQString& caption, TQString preSelection = mUseGlobalSettings ? GlobalSettings::self()->lastSelectedFolder() : TQString::null; - mTreeView = new KMail::SimpleFolderTree( makeVBoxMainWidget(), ft, + TQWidget * container = makeVBoxMainWidget(); + new TQLabel( i18n("You can start typing to filter the list of folders"), container ); + mTreeView = new KMail::SimpleFolderTree( container, ft, preSelection, mustBeReadWrite ); init(); } @@ -469,7 +51,9 @@ KMFolderSelDlg::KMFolderSelDlg( TQWidget * parent, KMFolderTree * tree, { TQString preSelection = mUseGlobalSettings ? GlobalSettings::self()->lastSelectedFolder() : TQString::null; - mTreeView = new KMail::SimpleFolderTree( makeVBoxMainWidget(), tree, + TQWidget * container = makeVBoxMainWidget(); + new TQLabel( i18n("You can start typing to filter the list of folders"), container ); + mTreeView = new KMail::SimpleFolderTree( container, tree, preSelection, mustBeReadWrite ); init(); } @@ -547,13 +131,13 @@ void KMFolderSelDlg::readConfig() TQValueList widths = config->readIntListEntry( "ColumnWidths" ); if ( !widths.isEmpty() ) { - mTreeView->setColumnWidth(mTreeView->mFolderColumn, widths[0]); - mTreeView->setColumnWidth(mTreeView->mPathColumn, widths[1]); + mTreeView->setColumnWidth(mTreeView->folderColumn(), widths[0]); + mTreeView->setColumnWidth(mTreeView->pathColumn(), widths[1]); } else { int colWidth = width() / 2; - mTreeView->setColumnWidth(mTreeView->mFolderColumn, colWidth); - mTreeView->setColumnWidth(mTreeView->mPathColumn, colWidth); + mTreeView->setColumnWidth(mTreeView->folderColumn(), colWidth); + mTreeView->setColumnWidth(mTreeView->pathColumn(), colWidth); } } @@ -564,11 +148,10 @@ void KMFolderSelDlg::writeConfig() config->writeEntry( "Size", size() ); TQValueList widths; - widths.push_back(mTreeView->columnWidth(mTreeView->mFolderColumn)); - widths.push_back(mTreeView->columnWidth(mTreeView->mPathColumn)); + widths.push_back(mTreeView->columnWidth(mTreeView->folderColumn())); + widths.push_back(mTreeView->columnWidth(mTreeView->pathColumn())); config->writeEntry( "ColumnWidths", widths ); } -} // namespace KMail #include "kmfolderseldlg.moc" diff --git a/kmail/kmfolderseldlg.h b/kmail/kmfolderseldlg.h index 119ceff7..f1637a2f 100644 --- a/kmail/kmfolderseldlg.h +++ b/kmail/kmfolderseldlg.h @@ -7,61 +7,16 @@ #define kmfolderseldlg_h #include -#include +#include +#include +#include class KMFolder; class KMFolderTree; class KMMainWidget; +class SimpleFolderTree; namespace KMail { - - class SimpleFolderTree : public KFolderTree - { - Q_OBJECT - - public: - SimpleFolderTree( TQWidget * parent, KMFolderTree * folderTree, - const TQString & preSelection, bool mustBeReadWrite ); - - /** Reload the tree and select what folders to show and what not */ - void reload( bool mustBeReadWrite, bool showOutbox, bool showImapFolders, - const TQString& preSelection = TQString::null ); - - /** Return the current folder */ - const KMFolder * folder() const; - - /** Set the current folder */ - void setFolder( KMFolder* ); - void setFolder( const TQString& idString ); - - /** Apply the given filter. */ - void applyFilter( const TQString& filter ); - - public slots: - void addChildFolder(); - - protected slots: - void slotContextMenuRequested( TQListViewItem *, const TQPoint & ); - virtual void recolorRows(); - - protected: - /** Read color options and set palette. */ - virtual void readColorConfig(void); - virtual void keyPressEvent( TQKeyEvent *e ); - - /** Folder and path column IDs. */ - friend class KMFolderSelDlg; - int mFolderColumn; - int mPathColumn; - - private: - KMFolderTree* mFolderTree; - TQString mFilter; - bool mLastMustBeReadWrite; - bool mLastShowOutbox; - bool mLastShowImapFolders; -}; - //----------------------------------------------------------------------------- class KMFolderSelDlg: public KDialogBase { diff --git a/kmail/kmfoldertree.cpp b/kmail/kmfoldertree.cpp index e99e7058..d2e098b8 100644 --- a/kmail/kmfoldertree.cpp +++ b/kmail/kmfoldertree.cpp @@ -126,7 +126,13 @@ TQPixmap KMFolderTreeItem::normalIcon(int size) const case Trash: icon = "trashcan_empty"; break; case Drafts: icon = "edit"; break; case Templates: icon = "filenew"; break; - default: icon = kmkernel->iCalIface().folderPixmap( type() ); break; + default: + { + //If not a resource folder don't try to use icalIface folder pixmap + if(kmkernel->iCalIface().isResourceFolder( mFolder )) + icon = kmkernel->iCalIface().folderPixmap( type() ); + break; + } } // non-root search folders if ( protocol() == KMFolderTreeItem::Search ) { @@ -177,7 +183,8 @@ TQPixmap KMFolderTreeItem::unreadIcon(int size) const pm = il->loadIcon( "folder_grey_open", KIcon::Small, size, KIcon::DefaultState, 0, true ); } else { - pm = il->loadIcon( kmkernel->iCalIface().folderPixmap( type() ), + if( kmkernel->iCalIface().isResourceFolder( mFolder ) ) + pm = il->loadIcon( kmkernel->iCalIface().folderPixmap( type() ), KIcon::Small, size, KIcon::DefaultState, 0, true ); if ( pm.isNull() ) pm = il->loadIcon( "folder_open", KIcon::Small, size, @@ -245,8 +252,15 @@ void KMFolderTreeItem::slotIconsChanged() { kdDebug(5006) << k_funcinfo << endl; // this is prone to change, so better check + KFolderTreeItem::Type newType = type(); if( kmkernel->iCalIface().isResourceFolder( mFolder ) ) - setType( kmkernel->iCalIface().folderType(mFolder) ); + newType = kmkernel->iCalIface().folderType(mFolder); + + // reload the folder tree if the type changed, needed because of the + // various type-dependent folder hiding options + if ( type() != newType ) + static_cast( listView() )->delayedReload(); + setType( newType ); if ( unreadCount() > 0 ) setPixmap( 0, unreadIcon( iconSize() ) ); @@ -263,6 +277,12 @@ void KMFolderTreeItem::slotNameChanged() repaint(); } +void KMFolderTreeItem::slotNoContentChanged() +{ + // reload the folder tree if the no content state changed, needed because + // we hide no-content folders if their child nodes are hidden + TQTimer::singleShot( 0, static_cast( listView() ), TQT_SLOT(reload()) ); +} //----------------------------------------------------------------------------- bool KMFolderTreeItem::acceptDrag(TQDropEvent* e) const @@ -333,7 +353,7 @@ void KMFolderTreeItem::assignShortcut() kmkernel->getKMMainWidget(), listView() ); shorty->exec(); - return; + delete shorty; } //----------------------------------------------------------------------------- @@ -362,6 +382,7 @@ KMFolderTree::KMFolderTree( KMMainWidget *mainWidget, TQWidget *parent, oldSelected = 0; oldCurrent = 0; mLastItem = 0; + dropItem = 0; mMainWidget = mainWidget; mReloading = false; mCutFolder = false; @@ -375,7 +396,7 @@ KMFolderTree::KMFolderTree( KMMainWidget *mainWidget, TQWidget *parent, int namecol = addColumn( i18n("Folder"), 250 ); header()->setStretchEnabled( true, namecol ); - + setResizeMode( TQListView::NoColumn ); // connect connectSignals(); @@ -595,6 +616,16 @@ void KMFolderTree::reload(bool openFolders) connect(fti->folder(),TQT_SIGNAL(nameChanged()), fti,TQT_SLOT(slotNameChanged())); + disconnect( fti->folder(), TQT_SIGNAL(noContentChanged()), + fti, TQT_SLOT(slotNoContentChanged()) ); + connect( fti->folder(), TQT_SIGNAL(noContentChanged()), + fti, TQT_SLOT(slotNoContentChanged()) ); + + disconnect( fti->folder(), TQT_SIGNAL(syncStateChanged()), + this, TQT_SLOT(slotSyncStateChanged()) ); + connect( fti->folder(), TQT_SIGNAL(syncStateChanged()), + this, TQT_SLOT(slotSyncStateChanged()) ); + // we want to be noticed of changes to update the unread/total columns disconnect(fti->folder(), TQT_SIGNAL(msgAdded(KMFolder*,Q_UINT32)), this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*))); @@ -733,6 +764,8 @@ void KMFolderTree::addDirectory( KMFolderDir *fdir, KMFolderTreeItem* parent ) // It is removeFromFolderToItemMap( folder ); delete fti; + // still, it might change in the future, so we better check the change signals + connect ( folder, TQT_SIGNAL(noContentChanged()), TQT_SLOT(delayedReload()) ); continue; } @@ -986,7 +1019,6 @@ void KMFolderTree::doFolderSelected( TQListViewItem* qlvi, bool keepSelection ) KMFolder* folder = 0; if (fti) folder = fti->folder(); - if (mLastItem && mLastItem != fti && mLastItem->folder() && (mLastItem->folder()->folderType() == KMFolderTypeImap)) { @@ -1059,7 +1091,7 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi, TQString createChild = i18n("&New Subfolder..."); if (!fti->folder()) createChild = i18n("&New Folder..."); - if (fti->folder() || (fti->text(0) != i18n("Searches")) && !multiFolder) + if ( ( fti->folder() || (fti->text(0) != i18n("Searches")) ) && !multiFolder) folderMenu->insertItem(SmallIconSet("folder_new"), createChild, this, TQT_SLOT(addChildFolder())); @@ -1086,7 +1118,7 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi, folderToPopupMenu( CopyFolder, this, &mMenuToFolder, copyMenu ); folderMenu->insertItem( i18n("&Copy Folder To"), copyMenu ); - if ( fti->folder()->isMoveable() ) + if ( fti->folder()->isMoveable() && fti->folder()->canDeleteMessages() ) { TQPopupMenu *moveMenu = new TQPopupMenu( folderMenu ); folderToPopupMenu( MoveFolder, this, &mMenuToFolder, moveMenu ); @@ -1101,6 +1133,8 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi, if ( !multiFolder ) mMainWidget->action("search_messages")->plug(folderMenu); + mMainWidget->action( "archive_folder" )->plug( folderMenu ); + mMainWidget->action("compact")->plug(folderMenu); if ( GlobalSettings::self()->enableFavoriteFolderView() ) { @@ -1123,7 +1157,7 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi, fti->folder()->folderType() == KMFolderTypeCachedImap )) { folderMenu->insertItem(SmallIconSet("bookmark_folder"), - i18n("Subscription..."), mMainWidget, + i18n("Serverside Subscription..."), mMainWidget, TQT_SLOT(slotSubscriptionDialog())); folderMenu->insertItem(SmallIcon("bookmark_folder"), i18n("Local Subscription..."), mMainWidget, @@ -1157,7 +1191,7 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi, fti, TQT_SLOT(assignShortcut())); - if ( !fti->folder()->noContent() ) { + if ( !fti->folder()->noContent() && fti->folder()->canDeleteMessages() ) { folderMenu->insertItem( i18n("Expire..."), fti, TQT_SLOT( slotShowExpiryProperties() ) ); } @@ -1215,12 +1249,14 @@ static bool folderHasCreateRights( const KMFolder *folder ) bool createRights = true; // we don't have acls for local folders yet if ( folder && folder->folderType() == KMFolderTypeImap ) { const KMFolderImap *imapFolder = static_cast( folder->storage() ); - createRights = imapFolder->userRights() == 0 || // hack, we should get the acls - ( imapFolder->userRights() > 0 && ( imapFolder->userRights() & KMail::ACLJobs::Create ) ); + createRights = imapFolder->userRightsState() != KMail::ACLJobs::Ok || // hack, we should get the acls + ( imapFolder->userRightsState() == KMail::ACLJobs::Ok && + ( imapFolder->userRights() & KMail::ACLJobs::Create ) ); } else if ( folder && folder->folderType() == KMFolderTypeCachedImap ) { const KMFolderCachedImap *dimapFolder = static_cast( folder->storage() ); - createRights = dimapFolder->userRights() == 0 || - ( dimapFolder->userRights() > 0 && ( dimapFolder->userRights() & KMail::ACLJobs::Create ) ); + createRights = dimapFolder->userRightsState() != KMail::ACLJobs::Ok || + ( dimapFolder->userRightsState() == KMail::ACLJobs::Ok && + ( dimapFolder->userRights() & KMail::ACLJobs::Create ) ); } return createRights; } @@ -1241,8 +1277,7 @@ void KMFolderTree::addChildFolder( KMFolder *folder, TQWidget * parent ) if (!aFolder->createChildFolder()) return; if ( !folderHasCreateRights( aFolder ) ) { - // FIXME: change this message to "Cannot create folder under ..." or similar - const TQString message = i18n( "Cannot create folder %1 because of insufficient " + const TQString message = i18n( "Cannot create folder under %1 because of insufficient " "permissions on the server. If you think you should be able to create " "subfolders here, ask your administrator to grant you rights to do so." " " ).arg(aFolder->label()); @@ -1953,6 +1988,7 @@ void KMFolderTree::moveOrCopyFolder( TQValueList > source if ( parent->hasNamedFolder( sourceFolderName ) || sourceFolderNames.contains( sourceFolderName ) ) { KMessageBox::error( this, i18n("Cannot move or copy folder %1 here because a folder with the same name already exists.") .arg( sourceFolderName ) ); + setDragEnabled( true ); return; } sourceFolderNames.append( sourceFolderName ); @@ -1963,6 +1999,7 @@ void KMFolderTree::moveOrCopyFolder( TQValueList > source if ( f->moveInProgress() ) { KMessageBox::error( this, i18n("Cannot move or copy folder %1 because it is not completely copied itself.") .arg( sourceFolderName ) ); + setDragEnabled( true ); return; } if ( f->parent() ) @@ -1982,6 +2019,7 @@ void KMFolderTree::moveOrCopyFolder( TQValueList > source if ( folderDir->findRef( source ) != -1 ) { KMessageBox::error( this, message ); + setDragEnabled( true ); return; } folderDir = folderDir->parent(); @@ -1991,12 +2029,14 @@ void KMFolderTree::moveOrCopyFolder( TQValueList > source if( source && source->child() && parent && ( parent->path().find( source->child()->path() + "/" ) == 0 ) ) { KMessageBox::error( this, message ); + setDragEnabled( true ); return; } if( source && source->child() && ( parent == source->child() ) ) { KMessageBox::error( this, message ); + setDragEnabled( true ); return; } } @@ -2013,6 +2053,7 @@ void KMFolderTree::moveOrCopyFolder( TQValueList > source do { if ( parentDir == childDir || parentDir->findRef( childDir->owner() ) != -1 ) { KMessageBox::error( this, i18n("Moving the selected folders is not possible") ); + setDragEnabled( true ); return; } childDir = childDir->parent(); @@ -2106,6 +2147,23 @@ void KMFolderTree::updateCopyActions() paste->setEnabled( true ); } +void KMFolderTree::slotSyncStateChanged() +{ + // Only emit the signal when a selected folder changes, otherwise the folder menu is updated + // too often + TQValueList< TQGuardedPtr > folders = selectedFolders(); + TQValueList< TQGuardedPtr >::const_iterator it = folders.constBegin(); + TQValueList< TQGuardedPtr >::const_iterator end = folders.constEnd(); + while ( it != end ) { + TQGuardedPtr folder = *it; + if ( folder == sender() ) { + emit syncStateChanged(); + break; + } + ++it; + } +} + void KMFolderTree::slotAddToFavorites() { KMail::FavoriteFolderView *favView = mMainWidget->favoriteFolderView(); @@ -2123,4 +2181,9 @@ void KMFolderTree::slotUnhideLocalInbox() reload(); } +void KMFolderTree::delayedReload() +{ + TQTimer::singleShot( 0, this, TQT_SLOT(reload()) ); +} + #include "kmfoldertree.moc" diff --git a/kmail/kmfoldertree.h b/kmail/kmfoldertree.h index df23116a..b7244c6c 100644 --- a/kmail/kmfoldertree.h +++ b/kmail/kmfoldertree.h @@ -88,6 +88,7 @@ public slots: void slotShowExpiryProperties(); void slotIconsChanged(); void slotNameChanged(); + void slotNoContentChanged(); void updateCount(); protected: @@ -115,9 +116,6 @@ public: /** Save config options */ void writeConfig(); - /** Get/refresh the folder tree */ - virtual void reload(bool openFolders = false); - /** Recusively add folders in a folder directory to a listview item. */ virtual void addDirectory( KMFolderDir *fdir, KMFolderTreeItem* parent ); @@ -178,6 +176,9 @@ signals: /** The selected folder has changed to go to an unread message */ void folderSelectedUnread( KMFolder * ); + /** The sync state of the _selected_ folder has changed */ + void syncStateChanged(); + /** unread/total/size column has changed */ void columnsChanged(); @@ -188,6 +189,9 @@ signals: void nameChanged( KMFolderTreeItem * ); public slots: + /** Get/refresh the folder tree */ + virtual void reload(bool openFolders = false); + /** Select the next folder with unread messages */ void nextUnreadFolder(); @@ -231,6 +235,9 @@ public slots: /** Pastes a previously copied/cutted folder below the currently selected folder. */ void pasteFolder(); + /** Reload the folder tree (using a single shot timer) */ + void delayedReload(); + protected slots: // void slotRMB(int, int); /** called by the folder-manager when the list of folders changed */ @@ -282,6 +289,8 @@ protected slots: /** Updates copy/cut/paste actions */ void updateCopyActions(); + void slotSyncStateChanged(); + protected: virtual void contentsMousePressEvent( TQMouseEvent *e ); virtual void contentsMouseReleaseEvent(TQMouseEvent* me); diff --git a/kmail/kmgroupware.cpp b/kmail/kmgroupware.cpp index 08afc331..47bcf33b 100644 --- a/kmail/kmgroupware.cpp +++ b/kmail/kmgroupware.cpp @@ -55,8 +55,8 @@ bool vPartFoundAndDecoded( KMMessage* msg, TQString& s ) s = TQString::fromUtf8( msg->bodyDecoded() ); return true; } else if( DwMime::kTypeMultipart == msg->type() && - (DwMime::kSubtypeMixed == msg->subtype() ) || - (DwMime::kSubtypeAlternative == msg->subtype() )) + ( (DwMime::kSubtypeMixed == msg->subtype() ) || + (DwMime::kSubtypeAlternative == msg->subtype() ) )) { // kdDebug(5006) << "KMGroupware looking for TNEF data" << endl; DwBodyPart* dwPart = msg->findDwBodyPart( DwMime::kTypeApplication, diff --git a/kmail/kmheaders.cpp b/kmail/kmheaders.cpp index c0e5c422..bcfc06f3 100644 --- a/kmail/kmheaders.cpp +++ b/kmail/kmheaders.cpp @@ -85,6 +85,7 @@ TQPixmap* KMHeaders::pixUndefinedEncrypted = 0; TQPixmap* KMHeaders::pixEncryptionProblematic = 0; TQPixmap* KMHeaders::pixSignatureProblematic = 0; TQPixmap* KMHeaders::pixAttachment = 0; +TQPixmap* KMHeaders::pixInvitation = 0; TQPixmap* KMHeaders::pixReadFwd = 0; TQPixmap* KMHeaders::pixReadReplied = 0; TQPixmap* KMHeaders::pixReadFwdReplied = 0; @@ -93,7 +94,8 @@ TQPixmap* KMHeaders::pixReadFwdReplied = 0; //----------------------------------------------------------------------------- KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent, const char *name) : - KListView(parent, name) + KListView( parent, name ), + mIgnoreSortOrderChanges( false ) { static bool pixmapsLoaded = false; //qInitImageIO(); @@ -131,6 +133,7 @@ KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent, mPopup->insertItem(i18n("Important"), KPaintInfo::COL_IMPORTANT); mPopup->insertItem(i18n("Action Item"), KPaintInfo::COL_TODO); mPopup->insertItem(i18n("Attachment"), KPaintInfo::COL_ATTACHMENT); + mPopup->insertItem(i18n("Invitation"), KPaintInfo::COL_INVITATION); mPopup->insertItem(i18n("Spam/Ham"), KPaintInfo::COL_SPAM_HAM); mPopup->insertItem(i18n("Watched/Ignored"), KPaintInfo::COL_WATCHED_IGNORED); mPopup->insertItem(i18n("Signature"), KPaintInfo::COL_SIGNED); @@ -169,6 +172,7 @@ KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent, pixEncryptionProblematic = new TQPixmap( UserIcon( "kmmsgencryptionproblematic" ) ); pixSignatureProblematic = new TQPixmap( UserIcon( "kmmsgsignatureproblematic" ) ); pixAttachment = new TQPixmap( UserIcon( "kmmsgattachment" ) ); + pixInvitation = new TQPixmap( UserIcon( "kmmsginvitation" ) ); pixReadFwd = new TQPixmap( UserIcon( "kmmsgread_fwd" ) ); pixReadReplied = new TQPixmap( UserIcon( "kmmsgread_replied" ) ); pixReadFwdReplied = new TQPixmap( UserIcon( "kmmsgread_fwd_replied" ) ); @@ -187,6 +191,7 @@ KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent, mPaintInfo.importantCol = addColumn( *pixFlag , "", 0 ); mPaintInfo.todoCol = addColumn( *pixTodo , "", 0 ); mPaintInfo.attachmentCol = addColumn( *pixAttachment , "", 0 ); + mPaintInfo.invitationCol = addColumn( *pixInvitation , "", 0 ); mPaintInfo.spamHamCol = addColumn( *pixSpam , "", 0 ); mPaintInfo.watchedIgnoredCol = addColumn( *pixWatched , "", 0 ); mPaintInfo.signedCol = addColumn( *pixFullySigned , "", 0 ); @@ -277,6 +282,15 @@ void KMHeaders::slotToggleColumn(int id, int mode) moveToCol = 0; break; } + case KPaintInfo::COL_INVITATION: + { + show = &mPaintInfo.showInvitation; + col = &mPaintInfo.invitationCol; + width = pixAttachment->width() + 8; + if ( *col == header()->mapToIndex( *col ) ) + moveToCol = 0; + break; + } case KPaintInfo::COL_IMPORTANT: { show = &mPaintInfo.showImportant; @@ -475,6 +489,9 @@ void KMHeaders::readConfig (void) show = config->readBoolEntry("showAttachmentColumn"); slotToggleColumn(KPaintInfo::COL_ATTACHMENT, show); + show = config->readBoolEntry("showInvitationColumn"); + slotToggleColumn(KPaintInfo::COL_INVITATION, show); + show = config->readBoolEntry("showImportantColumn"); slotToggleColumn(KPaintInfo::COL_IMPORTANT, show); @@ -501,6 +518,7 @@ void KMHeaders::readConfig (void) mPaintInfo.showCryptoIcons = config->readBoolEntry( "showCryptoIcons", false ); mPaintInfo.showAttachmentIcon = config->readBoolEntry( "showAttachmentIcon", true ); + mPaintInfo.showInvitationIcon = config->readBoolEntry( "showInvitationIcon", false ); KMime::DateFormatter::FormatType t = (KMime::DateFormatter::FormatType) config->readNumEntry("dateFormat", KMime::DateFormatter::Fancy ) ; @@ -538,6 +556,16 @@ void KMHeaders::readConfig (void) } } +//----------------------------------------------------------------------------- +void KMHeaders::restoreColumnLayout( KConfig *config, const TQString &group ) +{ + // KListView::restoreLayout() will call setSorting(), which is reimplemented by us. + // We don't want to change the sort order, so we set a flag here that is checked in + // setSorting(). + mIgnoreSortOrderChanges = true; + restoreLayout( config, group ); + mIgnoreSortOrderChanges = false; +} //----------------------------------------------------------------------------- void KMHeaders::reset() @@ -593,7 +621,7 @@ void KMHeaders::readFolderConfig (void) mCurrentItem = config->readNumEntry("Current", 0); mCurrentItemSerNum = config->readNumEntry("CurrentSerialNum", 0); - mPaintInfo.orderOfArrival = config->readBoolEntry( "OrderOfArrival", true ); + mPaintInfo.orderOfArrival = config->readBoolEntry( "OrderOfArrival", false ); mPaintInfo.status = config->readBoolEntry( "Status", false ); { //area for config group "Geometry" @@ -636,6 +664,7 @@ void KMHeaders::writeConfig (void) KConfigGroupSaver saver(config, "General"); config->writeEntry("showMessageSize" , mPaintInfo.showSize); config->writeEntry("showAttachmentColumn" , mPaintInfo.showAttachment); + config->writeEntry("showInvitationColumn" , mPaintInfo.showInvitation); config->writeEntry("showImportantColumn" , mPaintInfo.showImportant); config->writeEntry("showTodoColumn" , mPaintInfo.showTodo); config->writeEntry("showSpamHamColumn" , mPaintInfo.showSpamHam); @@ -792,9 +821,19 @@ void KMHeaders::msgChanged() clear(); return; } - int i = topItemIndex(); - int cur = currentItemIndex(); if (!isUpdatesEnabled()) return; + + // Remember selected messages, current message and some scrollbar data, as we have to restore it + const TQValueList oldSelectedItems = selectedItems(); + const int oldCurrentItemIndex = currentItemIndex(); + const bool scrollbarAtTop = verticalScrollBar() && + verticalScrollBar()->value() == verticalScrollBar()->minValue(); + const bool scrollbarAtBottom = verticalScrollBar() && + verticalScrollBar()->value() == verticalScrollBar()->maxValue(); + const HeaderItem * const oldFirstVisibleItem = dynamic_cast( itemAt( TQPoint( 0, 0 ) ) ); + const int oldOffsetOfFirstVisibleItem = itemRect( oldFirstVisibleItem ).y(); + const uint oldSerNumOfFirstVisibleItem = oldFirstVisibleItem ? oldFirstVisibleItem->msgSerNum() : 0; + TQString msgIdMD5; TQListViewItem *item = currentItem(); HeaderItem *hi = dynamic_cast(item); @@ -808,27 +847,26 @@ void KMHeaders::msgChanged() // prevent IMAP messages from scrolling to top disconnect(this,TQT_SIGNAL(currentChanged(TQListViewItem*)), this,TQT_SLOT(highlightMessage(TQListViewItem*))); - // remember all selected messages - TQValueList curItems = selectedItems(); + updateMessageList(); // do not change the selection - // restore the old state, but move up when there are unread message just out of view - HeaderItem *topOfList = mItems[i]; - item = firstChild(); - TQListViewItem *unreadItem = 0; - while(item && item != topOfList) { - KMMsgBase *msg = mFolder->getMsgBase( static_cast(item)->msgId() ); - if ( msg->isUnread() || msg->isNew() ) { - if ( !unreadItem ) - unreadItem = item; - } else - unreadItem = 0; - item = item->itemBelow(); - } - if(unreadItem == 0) - unreadItem = topOfList; - setContentsPos( 0, itemPos( unreadItem )); - setCurrentMsg( cur ); - setSelectedByIndex( curItems, true ); + + // Restore scrollbar state and selected and current messages + setCurrentMsg( oldCurrentItemIndex ); + setSelectedByIndex( oldSelectedItems, true ); + if ( scrollbarAtTop ) { + setContentsPos( 0, 0 ); + } else if ( scrollbarAtBottom ) { + setContentsPos( 0, contentsHeight() ); + } else if ( oldSerNumOfFirstVisibleItem > 0 ) { + for ( uint i = 0; i < mItems.size(); ++i ) { + const KMMsgBase * const mMsgBase = mFolder->getMsgBase( i ); + if ( mMsgBase->getMsgSerNum() == oldSerNumOfFirstVisibleItem ) { + setContentsPos( 0, itemPos( mItems[i] ) - oldOffsetOfFirstVisibleItem ); + break; + } + } + } + connect(this,TQT_SIGNAL(currentChanged(TQListViewItem*)), this,TQT_SLOT(highlightMessage(TQListViewItem*))); @@ -1909,8 +1947,8 @@ void KMHeaders::findUnreadAux( HeaderItem*& item, if (!msgBase) continue; if (msgBase->isUnread() || msgBase->isNew()) foundUnreadMessage = true; - if (!onlyNew && (msgBase->isUnread() || msgBase->isNew()) - || onlyNew && msgBase->isNew()) + if ( ( !onlyNew && (msgBase->isUnread() || msgBase->isNew()) ) + || ( onlyNew && msgBase->isNew() ) ) lastUnread = newItem; if (newItem == item) break; newItem = static_cast(newItem->itemBelow()); @@ -1941,11 +1979,14 @@ int KMHeaders::findUnread(bool aDirNext, int aStartAt, bool onlyNew, bool accept if (!item) return -1; - if ( !acceptCurrent ) - if (aDirNext) + if ( !acceptCurrent ) { + if (aDirNext) { item = static_cast(item->itemBelow()); - else + } + else { item = static_cast(item->itemAbove()); + } + } } pitem = item; @@ -2413,9 +2454,7 @@ void KMHeaders::slotRMB() mOwner->useAction()->plug( menu ); } else { // show most used actions - if( !mFolder->isSent() ) { - mOwner->messageActions()->replyMenu()->plug( menu ); - } + mOwner->messageActions()->replyMenu()->plug( menu ); mOwner->forwardMenu()->plug( menu ); if( mOwner->sendAgainAction()->isEnabled() ) { mOwner->sendAgainAction()->plug( menu ); @@ -2430,7 +2469,7 @@ void KMHeaders::slotRMB() &mMenuToFolder, msgCopyMenu ); menu->insertItem(i18n("&Copy To"), msgCopyMenu); - if ( mFolder->isReadOnly() ) { + if ( !mFolder->canDeleteMessages() ) { int id = menu->insertItem( i18n("&Move To") ); menu->setItemEnabled( id, false ); } else { @@ -2577,6 +2616,9 @@ const KMMsgBase* KMHeaders::getMsgBaseForItem( const TQListViewItem *item ) cons //----------------------------------------------------------------------------- void KMHeaders::setSorting( int column, bool ascending ) { + if ( mIgnoreSortOrderChanges ) + return; + if (column != -1) { // carsten: really needed? // if (column != mSortCol) @@ -2659,7 +2701,9 @@ void KMHeaders::folderCleared() void KMHeaders::folderClosed() { - mFolder->open( "kmheaders" ); + if ( mFolder->open( "kmheaders" ) == 0 ) + updateMessageList(); + else folderCleared(); } @@ -3027,6 +3071,8 @@ bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread ) bool jumpToUnread = (GlobalSettings::self()->actionEnterFolder() == GlobalSettings::EnumActionEnterFolder::SelectFirstUnreadNew) || forceJumpToUnread; + HeaderItem *oldestItem = 0; + HeaderItem *newestItem = 0; TQMemArray sortCache(mFolder->count()); bool error = false; @@ -3330,6 +3376,16 @@ bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread ) { unread_exists = true; } + + if ( !oldestItem || mFolder->getMsgBase( oldestItem->msgId() )->date() > + mFolder->getMsgBase( new_kci->id() )->date() ) { + oldestItem = khi; + } + + if ( !newestItem || mFolder->getMsgBase( newestItem->msgId() )->date() < + mFolder->getMsgBase( new_kci->id() )->date() ) { + newestItem = khi; + } } // If we are sorting by date and ascending the top level items are sorted // ascending and the threads themselves are sorted descending. One wants @@ -3379,10 +3435,12 @@ bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread ) } } - //show a message + // Select a message, depending on the "When entering a folder:" setting CREATE_TIMER(selection); START_TIMER(selection); if(set_selection) { + + // Search for the id of the first unread/new item, should there be any int first_unread = -1; if (unread_exists) { HeaderItem *item = static_cast(firstChild()); @@ -3401,19 +3459,33 @@ bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread ) } } + // No unread message to select, so either select the newest, oldest or lastest selected if(first_unread == -1 ) { - setTopItemByIndex(mTopItem); - if ( mCurrentItem >= 0 ) + setTopItemByIndex( mTopItem ); + + if ( GlobalSettings::self()->actionEnterFolder() == + GlobalSettings::EnumActionEnterFolder::SelectNewest && newestItem != 0 ) { + setCurrentItemByIndex( newestItem->msgId() ); + } + else if ( GlobalSettings::self()->actionEnterFolder() == + GlobalSettings::EnumActionEnterFolder::SelectOldest && oldestItem != 0 ) { + setCurrentItemByIndex( oldestItem->msgId() ); + } + else if ( mCurrentItem >= 0 ) setCurrentItemByIndex( mCurrentItem ); else if ( mCurrentItemSerNum > 0 ) setCurrentItemBySerialNum( mCurrentItemSerNum ); else setCurrentItemByIndex( 0 ); + + // There is an unread item to select, so select it } else { setCurrentItemByIndex(first_unread); makeHeaderVisible(); center( contentsX(), itemPos(mItems[first_unread]), 0, 9.0 ); } + + // we are told to not change the selection } else { // only reset the selection if we have no current item if (mCurrentItem <= 0) { @@ -3503,7 +3575,7 @@ void KMHeaders::updateActions() cut->setEnabled( false ); } else { copy->setEnabled( true ); - if ( folder() && folder()->isReadOnly() ) + if ( folder() && !folder()->canDeleteMessages() ) cut->setEnabled( false ); else cut->setEnabled( true ); @@ -3534,7 +3606,9 @@ TQValueList< Q_UINT32 > KMHeaders::selectedSernums() if ( it.current()->isSelected() && it.current()->isVisible() ) { HeaderItem* item = static_cast( it.current() ); KMMsgBase *msgBase = mFolder->getMsgBase( item->msgId() ); - list.append( msgBase->getMsgSerNum() ); + if ( msgBase ) { + list.append( msgBase->getMsgSerNum() ); + } } } return list; @@ -3558,7 +3632,9 @@ TQValueList< Q_UINT32 > KMHeaders::selectedVisibleSernums() } HeaderItem *item = static_cast(it.current()); KMMsgBase *msgBase = mFolder->getMsgBase( item->msgId() ); - list.append( msgBase->getMsgSerNum() ); + if ( msgBase ) { + list.append( msgBase->getMsgSerNum() ); + } } ++it; } diff --git a/kmail/kmheaders.h b/kmail/kmheaders.h index f0f973d6..4055e3c3 100644 --- a/kmail/kmheaders.h +++ b/kmail/kmheaders.h @@ -133,6 +133,15 @@ public: /** Read color options and set palette. */ virtual void readColorConfig(void); + /** + * Same as KListView::restoreLayout(), only that this does _not_ restore the sort order. + * This is useful since restoreLayout() doesn't restore the sort order correctly, as + * KListView doesn't know about our extended sort order like date of arrival. + * + * Note that if you want to restore the sort order correctly, call readConfig(). + */ + void restoreColumnLayout( KConfig *config, const TQString &group ); + /** Return the current message */ virtual KMMessage* currentMsg(); /** Return the current list view item */ @@ -197,6 +206,9 @@ public: */ bool isMessageCut( Q_UINT32 serNum ) const; + /** Write global config options. */ + virtual void writeConfig(void); + signals: /** emitted when the list view item corresponding to this message has been selected */ @@ -294,8 +306,8 @@ protected: *pixFullySigned, *pixPartiallySigned, *pixUndefinedSigned, *pixFullyEncrypted, *pixPartiallyEncrypted, *pixUndefinedEncrypted, *pixFiller, *pixEncryptionProblematic, - *pixSignatureProblematic, *pixAttachment, - *pixReadFwd, *pixReadReplied, *pixReadFwdReplied,*pixTodo; + *pixSignatureProblematic, *pixAttachment, *pixInvitation, + *pixReadFwd, *pixReadReplied, *pixReadFwdReplied, *pixTodo; /** Look for color changes */ virtual bool event(TQEvent *e); @@ -321,9 +333,6 @@ protected: /** Write per-folder config options. */ virtual void writeFolderConfig(void); - /** Write global config options. */ - virtual void writeConfig(void); - /** Handle shift and control selection */ virtual void contentsMousePressEvent(TQMouseEvent*); virtual void contentsMouseReleaseEvent(TQMouseEvent* e); @@ -389,6 +398,7 @@ private: NestingPolicy nestingPolicy; int mSortCol; bool mSortDescending; + bool mIgnoreSortOrderChanges; struct { uint ascending : 1; diff --git a/kmail/kmkernel.cpp b/kmail/kmkernel.cpp index 9bff9a72..d0e706f9 100644 --- a/kmail/kmkernel.cpp +++ b/kmail/kmkernel.cpp @@ -23,6 +23,7 @@ using KPIM::BroadcastStatus; #include "kmacctcachedimap.h" #include "kmfiltermgr.h" #include "kmfilteraction.h" +#include "kmheaders.h" #define REALLY_WANT_KMSENDER #include "kmsender.h" #undef REALLY_WANT_KMSENDER @@ -43,6 +44,7 @@ using KRecentAddress::RecentAddresses; #include "kmcommands.h" #include "kmsystemtray.h" #include "transportmanager.h" +#include "importarchivedialog.h" #include #include "kmailicalifaceimpl.h" @@ -91,6 +93,7 @@ using KWallet::Wallet; #include KMKernel *KMKernel::mySelf = 0; +static bool s_askingToGoOnline = false; /********************************************************************/ /* Constructor and destructor */ @@ -316,12 +319,16 @@ bool KMKernel::handleCommandLine( bool noArgsOpensReader ) /********************************************************************/ void KMKernel::checkMail () //might create a new reader but won't show!! { + if ( !kmkernel->askToGoOnline() ) + return; kmkernel->acctMgr()->checkMail(false); } TQStringList KMKernel::accounts() { - return kmkernel->acctMgr()->getAccounts(); + if( kmkernel->acctMgr() ) + return kmkernel->acctMgr()->getAccounts(); + return TQStringList(); } void KMKernel::checkAccount (const TQString &account) //might create a new reader but won't show!! @@ -399,8 +406,7 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc, if( !str.isEmpty() ) { msg->setBody( TQString::fromLocal8Bit( str ).utf8() ); } else { - TemplateParser parser( msg, TemplateParser::NewMessage, - "", false, false, false, false ); + TemplateParser parser( msg, TemplateParser::NewMessage ); parser.process( NULL, NULL ); } } @@ -410,8 +416,7 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc, } else { - TemplateParser parser( msg, TemplateParser::NewMessage, - "", false, false, false, false ); + TemplateParser parser( msg, TemplateParser::NewMessage ); parser.process( NULL, NULL ); } @@ -480,6 +485,27 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc, const TQString &attachParamValue, const TQCString &attachContDisp, const TQCString &attachCharset ) +{ + kdDebug(5006) << "KMKernel::openComposer called (deprecated version)" << endl; + return openComposer ( to, cc, bcc, subject, body, hidden, + attachName, attachCte, attachData, + attachType, attachSubType, attachParamAttr, + attachParamValue, attachContDisp, attachCharset, 0 ); +} + +int KMKernel::openComposer (const TQString &to, const TQString &cc, + const TQString &bcc, const TQString &subject, + const TQString &body, int hidden, + const TQString &attachName, + const TQCString &attachCte, + const TQCString &attachData, + const TQCString &attachType, + const TQCString &attachSubType, + const TQCString &attachParamAttr, + const TQString &attachParamValue, + const TQCString &attachContDisp, + const TQCString &attachCharset, + unsigned int identity ) { kdDebug(5006) << "KMKernel::openComposer()" << endl; @@ -491,11 +517,11 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc, if ( !bcc.isEmpty() ) msg->setBcc(bcc); if ( !subject.isEmpty() ) msg->setSubject(subject); if ( !to.isEmpty() ) msg->setTo(to); + if ( identity > 0 ) msg->setHeaderField( "X-KMail-Identity", TQString::number( identity ) ); if ( !body.isEmpty() ) { msg->setBody(body.utf8()); } else { - TemplateParser parser( msg, TemplateParser::NewMessage, - "", false, false, false, false ); + TemplateParser parser( msg, TemplateParser::NewMessage ); parser.process( NULL, NULL ); } @@ -557,6 +583,11 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc, if ( msgPart ) cWin->addAttach(msgPart); + if ( isICalInvitation ) { + cWin->disableRecipientNumberCheck(); + cWin->disableForgottenAttachmentsCheck(); + } + if ( hidden == 0 && !iCalAutoSend ) { cWin->show(); // Activate window - doing this instead of KWin::activateWindow(cWin->winId()); @@ -597,8 +628,7 @@ DCOPRef KMKernel::openComposer(const TQString &to, const TQString &cc, if (!body.isEmpty()) { msg->setBody(body.utf8()); } else { - TemplateParser parser( msg, TemplateParser::NewMessage, - "", false, false, false, false ); + TemplateParser parser( msg, TemplateParser::NewMessage ); parser.process( NULL, NULL ); } @@ -644,13 +674,11 @@ DCOPRef KMKernel::newMessage(const TQString &to, if (!bcc.isEmpty()) msg->setBcc(bcc); if ( useFolderId ) { - TemplateParser parser( msg, TemplateParser::NewMessage, - "", false, false, false, false ); + TemplateParser parser( msg, TemplateParser::NewMessage ); parser.process( NULL, folder ); win = makeComposer( msg, id ); } else { - TemplateParser parser( msg, TemplateParser::NewMessage, - "", false, false, false, false ); + TemplateParser parser( msg, TemplateParser::NewMessage ); parser.process( NULL, folder ); win = makeComposer( msg ); } @@ -1053,6 +1081,14 @@ int KMKernel::dcopAddMessage_fastImport( const TQString & foldername, return retval; } +void KMKernel::showImportArchiveDialog() +{ + KMMainWidget *mainWidget = getKMMainWidget(); + KMail::ImportArchiveDialog *importDialog = new KMail::ImportArchiveDialog( mainWidget, WDestructiveClose ); + importDialog->setFolder( mainWidget->folderTree()->currentFolder() ); + importDialog->show(); +} + TQStringList KMKernel::folderList() const { TQStringList folders; @@ -1235,7 +1271,13 @@ bool KMKernel::isOffline() bool KMKernel::askToGoOnline() { + // already asking means we are offline and need to wait anyhow + if ( s_askingToGoOnline ) { + return false; + } + if ( kmkernel->isOffline() ) { + s_askingToGoOnline = true; int rc = KMessageBox::questionYesNo( KMKernel::self()->mainWin(), i18n("KMail is currently in offline mode. " @@ -1244,6 +1286,7 @@ bool KMKernel::askToGoOnline() i18n("Work Online"), i18n("Work Offline")); + s_askingToGoOnline = false; if( rc == KMessageBox::No ) { return false; } else { @@ -1336,27 +1379,34 @@ void KMKernel::testDir(const char *_name) // Open a composer for each message found in the dead.letter folder void KMKernel::recoverDeadLetters() { - const TQString pathName = localDataPath(); - TQDir dir( pathName ); - if ( !dir.exists( "autosave" ) ) - return; - - KMFolder folder( 0, pathName + "autosave", KMFolderTypeMaildir, false /* no index */ ); - KMFolderOpener openFolder( &folder, "recover" ); - if ( !folder.isOpened() ) { - perror( "cannot open autosave folder" ); + TQDir dir( localDataPath() + "autosave/cur" ); + if ( !dir.exists() ) { + kdWarning(5006) << "Autosave directory " << dir.path() << " not found!" << endl; return; } - const int num = folder.count(); - for ( int i = 0; i < num; i++ ) { - KMMessage *msg = folder.take( 0 ); - if ( msg ) { - KMail::Composer * win = KMail::makeComposer(); - win->setMsg( msg, false, false, true ); - win->setAutoSaveFilename( msg->fileName() ); - win->show(); + const TQStringList entryList = dir.entryList( TQDir::Files | TQDir::NoSymLinks, TQDir::Unsorted ); + for ( unsigned int i = 0; i < entryList.size(); i++ ) { + const TQString fileName = entryList[i]; + TQFile file( dir.path() + '/' + fileName ); + if ( !file.open( IO_ReadOnly ) ) { + kdWarning(5006) << "Unable to open autosave file " << fileName << endl; + continue; } + const TQByteArray msgData = file.readAll(); + file.close(); + + if ( msgData.isEmpty() ) { + kdWarning(5006) << "autosave file " << fileName << " is empty!" << endl; + continue; + } + + KMMessage *msg = new KMMessage(); // Composer will take ownership + msg->fromByteArray( msgData ); + KMail::Composer * win = KMail::makeComposer(); + win->setMsg( msg, false, false, true ); + win->setAutoSaveFilename( fileName ); + win->show(); } } @@ -1475,6 +1525,7 @@ void KMKernel::init() the_folderMgr = new KMFolderMgr(foldersPath); the_imapFolderMgr = new KMFolderMgr( KMFolderImap::cacheLocation(), KMImapDir); the_dimapFolderMgr = new KMFolderMgr( KMFolderCachedImap::cacheLocation(), KMDImapDir); + recreateCorruptIndexFiles(); the_searchFolderMgr = new KMFolderMgr(locateLocal("data","kmail/search"), KMSearchDir); KMFolder *lsf = the_searchFolderMgr->find( i18n("Last Search") ); @@ -1533,6 +1584,20 @@ void KMKernel::init() #else mBackgroundTasksTimer->start( 5 * 60000, true ); // 5 minutes, singleshot #endif + + TQTextCodec *codec; + for ( int i = 0; ( codec = TQTextCodec::codecForIndex ( i ) ); i++ ) { + const TQString asciiString( "azAZ19,.-#+!?=()&" ); + const TQCString encodedString = codec->fromUnicode( asciiString ); + if ( TQString::fromAscii( encodedString ) != asciiString ) { + mNonAsciiCompatibleCodecs.append( codec ); + } + } +} + +bool KMKernel::isCodecAsciiCompatible( const TQTextCodec *codec ) +{ + return !mNonAsciiCompatibleCodecs.contains( codec ); } void KMKernel::readConfig() @@ -1624,6 +1689,43 @@ void KMKernel::cleanupImapFolders() the_dimapFolderMgr->quiet( false ); } +void KMKernel::recreateCorruptIndexFiles() +{ + TQValueList > folders; + TQValueList foldersWithBrokenIndex; + TQStringList strList; + the_folderMgr->createFolderList( &strList, &folders ); + the_imapFolderMgr->createFolderList( &strList, &folders ); + the_dimapFolderMgr->createFolderList( &strList, &folders ); + for ( int i = 0; folders.at(i) != folders.end(); i++ ) { + KMFolder * const folder = *folders.at(i); + if ( !folder || folder->isDir() || folder->isOpened() ) + continue; + KMFolderIndex * const index = dynamic_cast( folder->storage() ); + if ( index && index->indexStatus() != KMFolderIndex::IndexOk ) { + foldersWithBrokenIndex.append( index ); + } + } + + if ( !foldersWithBrokenIndex.isEmpty() ) { + TQStringList folderNames; + for ( uint i = 0; i < foldersWithBrokenIndex.size(); i++ ) { + folderNames << foldersWithBrokenIndex[i]->label(); + } + + KMessageBox::informationList( 0, i18n( "There is a problem with the mail index of the following " + "folders, the indices will now be regenerated.\n" + "This can happen because the index files are out of date, missing or corrupted.\n" + "Contact your administrator if this happens frequently.\n" + "Some information, like status flags, might get lost." ), + folderNames, i18n( "Problem with mail indices" ) ); + + for ( uint i = 0; i < foldersWithBrokenIndex.size(); i++ ) { + foldersWithBrokenIndex[i]->silentlyRecreateIndex(); + } + } +} + bool KMKernel::doSessionManagement() { @@ -1865,9 +1967,17 @@ void KMKernel::dumpDeadLetters() if ( !KMainWindow::memberList ) return; - for ( TQPtrListIterator it(*KMainWindow::memberList) ; it.current() != 0; ++it ) - if ( KMail::Composer * win = ::qt_cast( it.current() ) ) + for ( TQPtrListIterator it(*KMainWindow::memberList) ; it.current() != 0; ++it ) { + if ( KMail::Composer * win = ::qt_cast( it.current() ) ) { win->autoSaveMessage(); + // saving the message has to be finished right here, we are called from a dtor, + // therefore we have no chance to finish this later + // yes, this is ugly and potentially dangerous, but the alternative is losing + // currently composed messages... + while ( win->isComposing() ) + qApp->processEvents(); + } + } } @@ -1955,7 +2065,7 @@ void KMKernel::slotShowConfigurationDialog() { if( !mConfigureDialog ) { mConfigureDialog = new ConfigureDialog( 0, "configure", false ); - connect( mConfigureDialog, TQT_SIGNAL( configCommitted() ), + connect( mConfigureDialog, TQT_SIGNAL( configChanged() ), this, TQT_SLOT( slotConfigChanged() ) ); } @@ -1967,9 +2077,11 @@ void KMKernel::slotShowConfigurationDialog() KMMainWin * win = new KMMainWin; win->show(); } - if( mConfigureDialog->isHidden() ) + { + getKMMainWidget()->headers()->writeConfig(); mConfigureDialog->show(); + } else mConfigureDialog->raise(); } @@ -2326,7 +2438,7 @@ bool KMKernel::canQueryClose() if ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowAlways ) { systray->hideKMail(); return false; - } else if ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) { + } else if ( ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) && ( systray->hasUnreadMail() )) { systray->show(); systray->hideKMail(); return false; diff --git a/kmail/kmkernel.h b/kmail/kmkernel.h index 02f3e437..455e4233 100644 --- a/kmail/kmkernel.h +++ b/kmail/kmkernel.h @@ -135,6 +135,9 @@ public: const TQString &attachParamValue, const TQCString &attachContDisp); + /** For backward compatibility + * @deprecated + */ int openComposer (const TQString &to, const TQString &cc, const TQString &bcc, const TQString &subject, const TQString &body, int hidden, @@ -148,6 +151,20 @@ public: const TQCString &attachContDisp, const TQCString &attachCharset); + int openComposer (const TQString &to, const TQString &cc, + const TQString &bcc, const TQString &subject, + const TQString &body, int hidden, + const TQString &attachName, + const TQCString &attachCte, + const TQCString &attachData, + const TQCString &attachType, + const TQCString &attachSubType, + const TQCString &attachParamAttr, + const TQString &attachParamValue, + const TQCString &attachContDisp, + const TQCString &attachCharset, + unsigned int identity); + DCOPRef openComposer(const TQString &to, const TQString &cc, const TQString &bcc, const TQString &subject, const TQString &body,bool hidden); @@ -179,6 +196,7 @@ public: const TQString & MsgStatusFlags = TQString()); int dcopAddMessage_fastImport(const TQString & foldername, const KURL & messagefile, const TQString & MsgStatusFlags = TQString()); + void showImportArchiveDialog(); TQStringList folderList() const; DCOPRef getFolder( const TQString& vpath ); @@ -235,6 +253,7 @@ public: void init(); void readConfig(); void cleanupImapFolders(); + void recreateCorruptIndexFiles(); void testDir(const char *_name); void recoverDeadLetters(); void initFolders(KConfig* cfg); @@ -389,6 +408,8 @@ public: void loadProfile( const TQString& path ); void saveToProfile( const TQString& path ) const; + + bool isCodecAsciiCompatible( const TQTextCodec *codec ); public slots: /// Save contents of all open composer widnows to ~/dead.letter @@ -488,6 +509,7 @@ private: bool mContextMenuShown; TQValueList systemTrayApplets; + TQValueList mNonAsciiCompatibleCodecs; /* Weaver */ KPIM::ThreadWeaver::Weaver *the_weaver; diff --git a/kmail/kmlineeditspell.cpp b/kmail/kmlineeditspell.cpp index 83542187..dde827be 100644 --- a/kmail/kmlineeditspell.cpp +++ b/kmail/kmlineeditspell.cpp @@ -8,6 +8,7 @@ #include "recentaddresses.h" #include "kmkernel.h" #include "globalsettings.h" +#include "stringutil.h" #include #include @@ -78,52 +79,74 @@ void KMLineEdit::insertEmails( const TQStringList & emails ) for ( TQStringList::const_iterator it = emails.begin(), end = emails.end() ; it != end; ++it ) menu.insertItem( *it ); const int result = menu.exec( TQCursor::pos() ); - if ( result < 0 ) + if ( result == -1 ) return; setText( contents + menu.text( result ) ); } -void KMLineEdit::dropEvent(TQDropEvent *event) +void KMLineEdit::dropEvent( TQDropEvent *event ) { - TQString vcards; - KVCardDrag::decode( event, vcards ); - if ( !vcards.isEmpty() ) { - KABC::VCardConverter converter; - KABC::Addressee::List list = converter.parseVCards( vcards ); + KURL::List urls; + + // Case one: The user dropped a text/directory (i.e. vcard), so decode its + // contents + if ( KVCardDrag::canDecode( event ) ) { + KABC::Addressee::List list; + KVCardDrag::decode( event, list ); + KABC::Addressee::List::Iterator ait; for ( ait = list.begin(); ait != list.end(); ++ait ){ insertEmails( (*ait).emails() ); } - } else { - KURL::List urls; - if ( KURLDrag::decode( event, urls) ) { - //kdDebug(5006) << "urlList" << endl; - KURL::List::Iterator it = urls.begin(); - KABC::VCardConverter converter; - KABC::Addressee::List list; - TQString fileName; - TQString caption( i18n( "vCard Import Failed" ) ); - for ( it = urls.begin(); it != urls.end(); ++it ) { - if ( KIO::NetAccess::download( *it, fileName, parentWidget() ) ) { + } + + // Case two: The user dropped a list or Urls. + // Iterate over that list. For mailto: Urls, just add the addressee to the list, + // and for other Urls, download the Url and assume it points to a vCard + else if ( KURLDrag::decode( event, urls ) ) { + KURL::List::Iterator it = urls.begin(); + KABC::Addressee::List list; + for ( it = urls.begin(); it != urls.end(); ++it ) { + + // First, let's deal with mailto Urls. The path() part contains the + // email-address. + if ( (*it).protocol() == "mailto" ) { + KABC::Addressee addressee; + addressee.insertEmail( KMail::StringUtil::decodeMailtoUrl( (*it).path() ), true /* preferred */ ); + list += addressee; + } + // Otherwise, download the vCard to which the Url points + else { + KABC::VCardConverter converter; + TQString fileName; + if ( KIO::NetAccess::download( (*it), fileName, parentWidget() ) ) { TQFile file( fileName ); file.open( IO_ReadOnly ); - TQByteArray rawData = file.readAll(); + const TQByteArray data = file.readAll(); file.close(); - TQString data = TQString::fromUtf8( rawData.data(), rawData.size() + 1 ); +#if defined(KABC_VCARD_ENCODING_FIX) + list += converter.parseVCardsRaw( data.data() ); +#else list += converter.parseVCards( data ); +#endif KIO::NetAccess::removeTempFile( fileName ); } else { - TQString text = i18n( "Unable to access %1." ); - KMessageBox::error( parentWidget(), text.arg( (*it).url() ), caption ); + TQString caption( i18n( "vCard Import Failed" ) ); + TQString text = i18n( "Unable to access %1." ).arg( (*it).url() ); + KMessageBox::error( parentWidget(), text, caption ); } - KABC::Addressee::List::Iterator ait; - for ( ait = list.begin(); ait != list.end(); ++ait ) - insertEmails((*ait).emails()); } - } else { - KPIM::AddresseeLineEdit::dropEvent( event ); + // Now, let the user choose which addressee to add. + KABC::Addressee::List::Iterator ait; + for ( ait = list.begin(); ait != list.end(); ++ait ) + insertEmails( (*ait).emails() ); } } + + // Case three: Let AddresseeLineEdit deal with the rest + else { + KPIM::AddresseeLineEdit::dropEvent( event ); + } } TQPopupMenu *KMLineEdit::createPopupMenu() @@ -156,7 +179,6 @@ void KMLineEdit::editRecentAddresses() //----------------------------------------------------------------------------- void KMLineEdit::loadContacts() { - // was: KABC::AddressLineEdit::loadAddresses() AddresseeLineEdit::loadContacts(); if ( GlobalSettings::self()->showRecentAddressesInComposer() ){ @@ -165,13 +187,22 @@ void KMLineEdit::loadContacts() KRecentAddress::RecentAddresses::self( KMKernel::config() )->addresses(); TQStringList::Iterator it = recent.begin(); TQString name, email; - int idx = addCompletionSource( i18n( "Recent Addresses" ) ); + + KConfig config( "kpimcompletionorder" ); + config.setGroup( "CompletionWeights" ); + int weight = config.readEntry( "Recent Addresses", "10" ).toInt(); + int idx = addCompletionSource( i18n( "Recent Addresses" ), weight ); for ( ; it != recent.end(); ++it ) { KABC::Addressee addr; KPIM::getNameAndMail(*it, name, email); - addr.setNameFromString( KPIM::quoteNameIfNecessary( name )); + name = KPIM::quoteNameIfNecessary( name ); + if ( ( name[0] == '"' ) && ( name[name.length() - 1] == '"' ) ) { + name.remove( 0, 1 ); + name.truncate( name.length() - 1 ); + } + addr.setNameFromString( name ); addr.insertEmail( email, true ); - addContact( addr, 120, idx ); // more weight than kabc entries and more than ldap results + addContact( addr, weight, idx ); } } } diff --git a/kmail/kmmainwidget.cpp b/kmail/kmmainwidget.cpp index 0b847ba6..27b04df0 100644 --- a/kmail/kmmainwidget.cpp +++ b/kmail/kmmainwidget.cpp @@ -18,15 +18,13 @@ #include #include #include -#include #include +#include +#include +#include #include - #include -#include -#include - #include #include #include @@ -45,8 +43,6 @@ #include #include -#include - #include "globalsettings.h" #include "kcursorsaver.h" #include "broadcaststatus.h" @@ -80,9 +76,6 @@ using KMail::ImapAccountBase; #include "vacation.h" using KMail::Vacation; #include "favoritefolderview.h" - -#include - #include "subscriptiondialog.h" using KMail::SubscriptionDialog; #include "localsubscriptiondialog.h" @@ -106,6 +99,9 @@ using KMail::HeaderListQuickSearch; #include "kmheaders.h" #include "mailinglistpropertiesdialog.h" #include "templateparser.h" +#include "archivefolderdialog.h" +#include "folderutil.h" +#include "csshelper.h" #if !defined(NDEBUG) #include "sievedebugdialog.h" @@ -129,7 +125,6 @@ using KMime::Types::AddrSpecList; using KPIM::ProgressManager; #include "managesievescriptsdialog.h" -#include #include "customtemplates.h" #include "customtemplates_kfg.h" @@ -150,6 +145,7 @@ KMMainWidget::KMMainWidget(TQWidget *parent, const char *name, mFolderViewParent( 0 ), mFolderViewSplitter( 0 ), mQuickSearchLine( 0 ), + mArchiveFolderAction( 0 ), mShowBusySplashTimer( 0 ), mShowingOfflineScreen( false ), mMsgActions( 0 ), @@ -226,6 +222,8 @@ KMMainWidget::KMMainWidget(TQWidget *parent, const char *name, this, TQT_SLOT(slotChangeCaption(TQListViewItem*))); connect(mFolderTree, TQT_SIGNAL(selectionChanged()), TQT_SLOT(updateFolderMenu()) ); + connect( mFolderTree, TQT_SIGNAL(syncStateChanged()), + TQT_SLOT(updateFolderMenu()) ); connect(kmkernel->folderMgr(), TQT_SIGNAL(folderRemoved(KMFolder*)), this, TQT_SLOT(slotFolderRemoved(KMFolder*))); @@ -300,8 +298,6 @@ void KMMainWidget::readPreConfig(void) mHtmlPref = reader.readBoolEntry( "htmlMail", false ); mHtmlLoadExtPref = reader.readBoolEntry( "htmlLoadExternal", false ); mEnableFavoriteFolderView = GlobalSettings::self()->enableFavoriteFolderView(); - mEnableFolderQuickSearch = GlobalSettings::self()->enableFolderQuickSearch(); - mEnableQuickSearch = GlobalSettings::self()->quickSearchActive(); } @@ -344,8 +340,6 @@ void KMMainWidget::readConfig(void) bool oldReaderWindowActive = mReaderWindowActive; bool oldReaderWindowBelow = mReaderWindowBelow; bool oldFavoriteFolderView = mEnableFavoriteFolderView; - bool oldFolderQuickSearch = mEnableFolderQuickSearch; - bool oldQuickSearch = mEnableQuickSearch; TQString str; TQSize siz; @@ -360,9 +354,7 @@ void KMMainWidget::readConfig(void) bool layoutChanged = ( oldLongFolderList != mLongFolderList ) || ( oldReaderWindowActive != mReaderWindowActive ) || ( oldReaderWindowBelow != mReaderWindowBelow ) - || ( oldFavoriteFolderView != mEnableFavoriteFolderView ) - || ( oldFolderQuickSearch != mEnableFolderQuickSearch ) - || ( oldQuickSearch != mEnableQuickSearch ); + || ( oldFavoriteFolderView != mEnableFavoriteFolderView ); if( layoutChanged ) { @@ -450,7 +442,7 @@ void KMMainWidget::readConfig(void) mMsgView->readConfig(); mHeaders->readConfig(); - mHeaders->restoreLayout(KMKernel::config(), "Header-Geometry"); + mHeaders->restoreColumnLayout( KMKernel::config(), "Header-Geometry" ); if ( mFolderViewSplitter && !GlobalSettings::self()->folderViewSplitterPosition().isEmpty() ) { mFolderViewSplitter->setSizes( GlobalSettings::self()->folderViewSplitterPosition() ); @@ -493,9 +485,7 @@ void KMMainWidget::readConfig(void) bool layoutChanged = ( oldLongFolderList != mLongFolderList ) || ( oldReaderWindowActive != mReaderWindowActive ) || ( oldReaderWindowBelow != mReaderWindowBelow ) - || ( oldFavoriteFolderView != mEnableFavoriteFolderView ) - || ( oldFolderQuickSearch != mEnableFolderQuickSearch ) - || ( oldQuickSearch != mEnableQuickSearch ); + || ( oldFavoriteFolderView != mEnableFavoriteFolderView ); if ( layoutChanged ) { activatePanners(); } @@ -671,10 +661,10 @@ void KMMainWidget::createWidgets(void) KAction *action; - action = new KAction( i18n("Move Message to Folder"), Key_M, this, + mMoveMsgToFolderAction = new KAction( i18n("Move Message to Folder"), Key_M, this, TQT_SLOT(slotMoveMsg()), actionCollection(), "move_message_to_folder" ); - action->plugAccel( actionCollection()->kaccel() ); + mMoveMsgToFolderAction->plugAccel( actionCollection()->kaccel() ); action = new KAction( i18n("Copy Message to Folder"), Key_C, this, TQT_SLOT(slotCopyMsg()), actionCollection(), @@ -697,25 +687,9 @@ void KMMainWidget::createWidgets(void) folderTreeParent = mFolderViewSplitter; mFolderView = mFolderViewSplitter; } - - // the "folder tree" consists of a quicksearch input field and the tree itself - mSearchAndTree = new TQVBox(folderTreeParent); - mFolderQuickSearch = new TQHBox(mSearchAndTree); - TQPushButton *clear = new TQPushButton(TQApplication::reverseLayout() - ? SmallIcon("clear_left") - : SmallIcon("locationbar_erase"), "", mFolderQuickSearch); - clear->setFlat(true); - KListViewSearchLine *search = new KListViewSearchLine(mFolderQuickSearch); - mFolderTree = new KMFolderTree(this, mSearchAndTree, "folderTree"); - search->setListView(mFolderTree); - connect(clear, TQT_SIGNAL(clicked()), search, TQT_SLOT(clear())); - - if ( !GlobalSettings::enableFolderQuickSearch() ) { - mFolderQuickSearch->hide(); - } - + mFolderTree = new KMFolderTree(this, folderTreeParent, "folderTree"); if ( !GlobalSettings::enableFavoriteFolderView() ) { - mFolderView = mSearchAndTree; + mFolderView = mFolderTree; } connect( mFolderTree, TQT_SIGNAL(folderSelected(KMFolder*)), mFavoriteFolderView, TQT_SLOT(folderTreeSelectionChanged(KMFolder*)) ); @@ -1020,14 +994,12 @@ void KMMainWidget::slotCompose() if ( mFolder ) { msg->initHeader( mFolder->identity() ); - TemplateParser parser( msg, TemplateParser::NewMessage, - "", false, false, false, false ); + TemplateParser parser( msg, TemplateParser::NewMessage ); parser.process( NULL, mFolder ); win = KMail::makeComposer( msg, mFolder->identity() ); } else { msg->initHeader(); - TemplateParser parser( msg, TemplateParser::NewMessage, - "", false, false, false, false ); + TemplateParser parser( msg, TemplateParser::NewMessage ); parser.process( NULL, NULL ); win = KMail::makeComposer( msg ); } @@ -1128,6 +1100,9 @@ void KMMainWidget::modifyFolder( KMFolderTreeItem* folderItem ) i18n("Properties of Folder %1").arg( folder->label() ) ); props.exec(); updateFolderMenu(); + //Kolab issue 2152 + if ( mSystemTray ) + mSystemTray->foldersChanged(); } //----------------------------------------------------------------------------- @@ -1204,6 +1179,13 @@ void KMMainWidget::slotEmptyFolder() mEmptyFolderAction->setEnabled( false ); } +//----------------------------------------------------------------------------- +void KMMainWidget::slotArchiveFolder() +{ + KMail::ArchiveFolderDialog archiveDialog; + archiveDialog.setFolder( mFolder ); + archiveDialog.exec(); +} //----------------------------------------------------------------------------- void KMMainWidget::slotRemoveFolder() @@ -1214,6 +1196,13 @@ void KMMainWidget::slotRemoveFolder() if ( !mFolder ) return; if ( mFolder->isSystemFolder() ) return; if ( mFolder->isReadOnly() ) return; + if ( mFolder->mailCheckInProgress() ) { + KMessageBox::sorry( this, i18n( "It is not possible to delete this folder right now because it " + "is being syncronized. Please wait until the syncronization of " + "this folder is complete and then try again." ), + i18n( "Unable to delete folder" ) ); + return; + } TQString title; if ( mFolder->folderType() == KMFolderTypeSearch ) { @@ -1259,32 +1248,7 @@ void KMMainWidget::slotRemoveFolder() KGuiItem( i18n("&Delete"), "editdelete")) == KMessageBox::Continue) { - if ( mFolder->hasAccounts() ) { - // this folder has an account, so we need to change that to the inbox - for ( AccountList::Iterator it (mFolder->acctList()->begin() ), - end( mFolder->acctList()->end() ); it != end; ++it ) { - (*it)->setFolder( kmkernel->inboxFolder() ); - KMessageBox::information(this, - i18n("The folder you deleted was associated with the account " - "%1 which delivered mail into it. The folder the account " - "delivers new mail into was reset to the main Inbox folder.").arg( (*it)->name())); - } - } - if (mFolder->folderType() == KMFolderTypeImap) - kmkernel->imapFolderMgr()->remove(mFolder); - else if (mFolder->folderType() == KMFolderTypeCachedImap) { - // Deleted by user -> tell the account (see KMFolderCachedImap::listDirectory2) - KMFolderCachedImap* storage = static_cast( mFolder->storage() ); - KMAcctCachedImap* acct = storage->account(); - if ( acct ) - acct->addDeletedFolder( mFolder ); - - kmkernel->dimapFolderMgr()->remove(mFolder); - } - else if (mFolder->folderType() == KMFolderTypeSearch) - kmkernel->searchFolderMgr()->remove(mFolder); - else - kmkernel->folderMgr()->remove(mFolder); + KMail::FolderUtil::deleteFolder( mFolder, this ); } } @@ -1328,7 +1292,7 @@ void KMMainWidget::slotRefreshFolder() imap->getAndCheckFolder(); } else if ( mFolder->folderType() == KMFolderTypeCachedImap ) { KMFolderCachedImap* f = static_cast( mFolder->storage() ); - f->account()->processNewMailSingleFolder( mFolder ); + f->account()->processNewMailInFolder( mFolder ); } } } @@ -1443,6 +1407,18 @@ void KMMainWidget::slotToggleSubjectThreading() mHeaders->setSubjectThreading(mFolderThreadSubjPref); } +//----------------------------------------------------------------------------- +void KMMainWidget::slotToggleShowQuickSearch() +{ + GlobalSettings::self()->setQuickSearchActive( !GlobalSettings::self()->quickSearchActive() ); + if ( GlobalSettings::self()->quickSearchActive() ) + mSearchToolBar->show(); + else { + mQuickSearchLine->reset(); + mSearchToolBar->hide(); + } +} + //----------------------------------------------------------------------------- void KMMainWidget::slotMessageQueuedOrDrafted() { @@ -1670,6 +1646,7 @@ void KMMainWidget::slotUndo() { mHeaders->undo(); updateMessageActions(); + updateFolderMenu(); } //----------------------------------------------------------------------------- @@ -1827,22 +1804,69 @@ void KMMainWidget::slotCopyMsg() //----------------------------------------------------------------------------- void KMMainWidget::slotPrintMsg() { + KMMessage *msg = mHeaders->currentMsg(); + if ( !msg ) { + return; + } + bool htmlOverride = mMsgView ? mMsgView->htmlOverride() : false; bool htmlLoadExtOverride = mMsgView ? mMsgView->htmlLoadExtOverride() : false; KConfigGroup reader( KMKernel::config(), "Reader" ); bool useFixedFont = mMsgView ? mMsgView->isFixedFont() : reader.readBoolEntry( "useFixedFont", false ); - KMCommand *command = - new KMPrintCommand( this, mHeaders->currentMsg(), + + const HeaderStyle *style; + const HeaderStrategy *strategy; + if ( mMsgView ) { + style = mMsgView->headerStyle(); + strategy = mMsgView->headerStrategy(); + } else { + style = HeaderStyle::create( reader.readEntry( "header-style", "fancy" ) ); + strategy = HeaderStrategy::create( reader.readEntry( "header-set-displayed", "rich" ) ); + } + + KMPrintCommand *command = + new KMPrintCommand( this, msg, + style, strategy, htmlOverride, htmlLoadExtOverride, useFixedFont, overrideEncoding() ); + if ( mMsgView ) + command->setOverrideFont( mMsgView->cssHelper()->bodyFont( mMsgView->isFixedFont(), true /*printing*/ ) ); + command->start(); } +//----------------------------------------------------------------------------- +void KMMainWidget::setupForwardActions() +{ + disconnect( mForwardActionMenu, TQT_SIGNAL( activated() ), 0, 0 ); + mForwardActionMenu->remove( mForwardInlineAction ); + mForwardActionMenu->remove( mForwardAttachedAction ); + + if ( GlobalSettings::self()->forwardingInlineByDefault() ) { + mForwardActionMenu->insert( mForwardInlineAction, 0 ); + mForwardActionMenu->insert( mForwardAttachedAction, 1 ); + mForwardInlineAction->setShortcut( Key_F ); + mForwardAttachedAction->setShortcut( SHIFT+Key_F ); + connect( mForwardActionMenu, TQT_SIGNAL(activated()), this, + TQT_SLOT(slotForwardInlineMsg()) ); + + } else { + mForwardActionMenu->insert( mForwardAttachedAction, 0 ); + mForwardActionMenu->insert( mForwardInlineAction, 1 ); + mForwardInlineAction->setShortcut( SHIFT+Key_F ); + mForwardAttachedAction->setShortcut( Key_F ); + connect( mForwardActionMenu, TQT_SIGNAL(activated()), this, + TQT_SLOT(slotForwardAttachedMsg()) ); + } +} + //----------------------------------------------------------------------------- void KMMainWidget::slotConfigChanged() { readConfig(); + setupForwardActions(); + setupForwardingActionsList(); } //----------------------------------------------------------------------------- @@ -1888,6 +1912,7 @@ void KMMainWidget::slotOnlineStatus() kmkernel->stopNetworkJobs(); } else { kmkernel->resumeNetworkJobs(); + slotCheckVacation(); } } @@ -2396,9 +2421,7 @@ void KMMainWidget::slotMsgPopup(KMMessage&, const KURL &aUrl, const TQPoint& aPo if ( mFolder->isTemplates() ) { mUseAction->plug( menu ); } else { - - if ( !mFolder->isSent() ) - mMsgActions->replyMenu()->plug( menu ); + mMsgActions->replyMenu()->plug( menu ); mForwardActionMenu->plug( menu ); } editAction()->plug(menu); @@ -2790,6 +2813,10 @@ void KMMainWidget::setupActions() mRemoveFolderAction = new KAction( "foo" /*set in updateFolderMenu*/, "editdelete", 0, this, TQT_SLOT(slotRemoveFolder()), actionCollection(), "delete_folder" ); + mArchiveFolderAction = new KAction( i18n( "&Archive Folder..." ), "filesave", 0, this, + TQT_SLOT( slotArchiveFolder() ), actionCollection(), + "archive_folder" ); + mPreferHtmlAction = new KToggleAction( i18n("Prefer &HTML to Plain Text"), 0, this, TQT_SLOT(slotOverrideHtml()), actionCollection(), "prefer_html" ); @@ -2862,22 +2889,7 @@ void KMMainWidget::setupActions() "message_forward_redirect" ); - if ( GlobalSettings::self()->forwardingInlineByDefault() ) { - mForwardActionMenu->insert( mForwardInlineAction ); - mForwardActionMenu->insert( mForwardAttachedAction ); - mForwardInlineAction->setShortcut( Key_F ); - mForwardAttachedAction->setShortcut( SHIFT+Key_F ); - connect( mForwardActionMenu, TQT_SIGNAL(activated()), this, - TQT_SLOT(slotForwardInlineMsg()) ); - - } else { - mForwardActionMenu->insert( mForwardAttachedAction ); - mForwardActionMenu->insert( mForwardInlineAction ); - mForwardInlineAction->setShortcut( SHIFT+Key_F ); - mForwardAttachedAction->setShortcut( Key_F ); - connect( mForwardActionMenu, TQT_SIGNAL(activated()), this, - TQT_SLOT(slotForwardAttachedMsg()) ); - } + setupForwardActions(); mForwardActionMenu->insert( mForwardDigestAction ); mForwardActionMenu->insert( mRedirectAction ); @@ -3114,6 +3126,13 @@ void KMMainWidget::setupActions() actionCollection(), "go_next_unread_text" ); //----- Settings Menu + mToggleShowQuickSearchAction = new KToggleAction(i18n("Show Quick Search"), TQString::null, + 0, this, TQT_SLOT(slotToggleShowQuickSearch()), + actionCollection(), "show_quick_search"); + mToggleShowQuickSearchAction->setChecked( GlobalSettings::self()->quickSearchActive() ); + mToggleShowQuickSearchAction->setWhatsThis( + i18n( GlobalSettings::self()->quickSearchActiveItem()->whatsThis().utf8() ) ); + (void) new KAction( i18n("Configure &Filters..."), 0, this, TQT_SLOT(slotFilter()), actionCollection(), "filter" ); (void) new KAction( i18n("Configure &POP Filters..."), 0, this, @@ -3337,8 +3356,8 @@ void KMMainWidget::updateMessageActions() mMarkThreadAsUnreadAction->setEnabled( thread_actions ); mToggleThreadTodoAction->setEnabled( thread_actions && flags_available ); mToggleThreadFlagAction->setEnabled( thread_actions && flags_available ); - mTrashThreadAction->setEnabled( thread_actions && !mFolder->isReadOnly() ); - mDeleteThreadAction->setEnabled( thread_actions && !mFolder->isReadOnly() ); + mTrashThreadAction->setEnabled( thread_actions && mFolder->canDeleteMessages() ); + mDeleteThreadAction->setEnabled( thread_actions && mFolder->canDeleteMessages() ); if (mFolder && mHeaders && mHeaders->currentMsg()) { if (thread_actions) { @@ -3349,29 +3368,30 @@ void KMMainWidget::updateMessageActions() } } - mMoveActionMenu->setEnabled( mass_actions && !mFolder->isReadOnly() ); + mMoveActionMenu->setEnabled( mass_actions && mFolder->canDeleteMessages() ); + mMoveMsgToFolderAction->setEnabled( mass_actions && mFolder->canDeleteMessages() ); mCopyActionMenu->setEnabled( mass_actions ); - mTrashAction->setEnabled( mass_actions && !mFolder->isReadOnly() ); - mDeleteAction->setEnabled( mass_actions && !mFolder->isReadOnly() ); - mFindInMessageAction->setEnabled( mass_actions ); - mForwardInlineAction->setEnabled( mass_actions ); - mForwardAttachedAction->setEnabled( mass_actions ); - mForwardDigestAction->setEnabled( count > 1 || parent_thread ); + mTrashAction->setEnabled( mass_actions && mFolder->canDeleteMessages() ); + mDeleteAction->setEnabled( mass_actions && mFolder->canDeleteMessages() ); + mFindInMessageAction->setEnabled( mass_actions && !kmkernel->folderIsTemplates( mFolder ) ); + mForwardInlineAction->setEnabled( mass_actions && !kmkernel->folderIsTemplates( mFolder )); + mForwardAttachedAction->setEnabled( mass_actions && !kmkernel->folderIsTemplates( mFolder ) ); + mForwardDigestAction->setEnabled( ( count > 1 || parent_thread ) && !kmkernel->folderIsTemplates( mFolder ) ); - forwardMenu()->setEnabled( mass_actions ); + forwardMenu()->setEnabled( mass_actions && !kmkernel->folderIsTemplates( mFolder )); bool single_actions = count == 1; mUseAction->setEnabled( single_actions && kmkernel->folderIsTemplates( mFolder ) ); filterMenu()->setEnabled( single_actions ); - redirectAction()->setEnabled( single_actions ); + redirectAction()->setEnabled( single_actions && !kmkernel->folderIsTemplates( mFolder ) ); printAction()->setEnabled( single_actions ); viewSourceAction()->setEnabled( single_actions ); mSendAgainAction->setEnabled( single_actions - && ( mHeaders->currentMsg() && mHeaders->currentMsg()->isSent() ) - || ( mFolder && mHeaders->currentMsg() && - kmkernel->folderIsSentMailFolder( mFolder ) ) ); + && ( ( mHeaders->currentMsg() && mHeaders->currentMsg()->isSent() ) + || ( mFolder && mHeaders->currentMsg() && + kmkernel->folderIsSentMailFolder( mFolder ) ) ) ); mSaveAsAction->setEnabled( mass_actions ); bool mails = mFolder && mFolder->count(); bool enable_goto_unread = mails @@ -3426,13 +3446,27 @@ void KMMainWidget::updateFolderMenu() || ( cachedImap && knownImapPath ) ) && !multiFolder ); if ( mTroubleshootFolderAction ) mTroubleshootFolderAction->setEnabled( folderWithContent && ( cachedImap && knownImapPath ) && !multiFolder ); - mEmptyFolderAction->setEnabled( folderWithContent && ( mFolder->count() > 0 ) && !mFolder->isReadOnly() && !multiFolder ); - mEmptyFolderAction->setText( (mFolder && kmkernel->folderIsTrash(mFolder)) - ? i18n("E&mpty Trash") : i18n("&Move All Messages to Trash") ); - mRemoveFolderAction->setEnabled( mFolder && !mFolder->isSystemFolder() && !mFolder->isReadOnly() && !multiFolder); - mRemoveFolderAction->setText( mFolder && mFolder->folderType() == KMFolderTypeSearch - ? i18n("&Delete Search") : i18n("&Delete Folder") ); - mExpireFolderAction->setEnabled( mFolder && mFolder->isAutoExpire() && !multiFolder ); + + mEmptyFolderAction->setEnabled( folderWithContent && + ( mFolder->count() > 0 ) && mFolder->canDeleteMessages() && + !multiFolder ); + mEmptyFolderAction->setText( ( mFolder && kmkernel->folderIsTrash( mFolder ) ) ? + i18n( "E&mpty Trash" ) : + i18n( "&Move All Messages to Trash" ) ); + + mRemoveFolderAction->setEnabled( mFolder && + !mFolder->isSystemFolder() && + mFolder->canDeleteMessages() && + !multiFolder && !mFolder->noContent() && + !mFolder->mailCheckInProgress() ); + mRemoveFolderAction->setText( mFolder && + mFolder->folderType() == KMFolderTypeSearch ? + i18n( "&Delete Search" ) : + i18n( "&Delete Folder" ) ); + + if ( mArchiveFolderAction ) + mArchiveFolderAction->setEnabled( mFolder && !multiFolder ); + mExpireFolderAction->setEnabled( mFolder && mFolder->isAutoExpire() && !multiFolder && mFolder->canDeleteMessages() ); updateMarkAsReadAction(); // the visual ones only make sense if we are showing a message list mPreferHtmlAction->setEnabled( mHeaders->folder() ? true : false ); @@ -3447,8 +3481,8 @@ void KMMainWidget::updateFolderMenu() mHeaders->folder() ? ( mThreadMessagesAction->isChecked()) : false ); mThreadBySubjectAction->setChecked( mFolderThreadSubjPref ); - mNewFolderAction->setEnabled( !multiFolder ); - mRemoveDuplicatesAction->setEnabled( !multiFolder ); + mNewFolderAction->setEnabled( !multiFolder && ( mFolder && mFolder->folderType() != KMFolderTypeSearch )); + mRemoveDuplicatesAction->setEnabled( !multiFolder && mFolder && mFolder->canDeleteMessages() ); mFolderShortCutCommandAction->setEnabled( !multiFolder ); } @@ -3849,6 +3883,7 @@ void KMMainWidget::slotFolderTreeColumnsChanged() mTotalColumnToggle->setChecked( mFolderTree->isTotalActive() ); mUnreadColumnToggle->setChecked( mFolderTree->isUnreadActive() ); mSizeColumnToggle->setChecked( mFolderTree->isSizeActive() ); + mUnreadTextToggle->setChecked( !mFolderTree->isUnreadActive() ); } void KMMainWidget::toggleSystemTray() @@ -3896,6 +3931,7 @@ void KMMainWidget::updateFileMenu() actionCollection()->action("check_mail")->setEnabled( actList.size() > 0 ); actionCollection()->action("check_mail_in")->setEnabled( actList.size() > 0 ); + actionCollection()->action("favorite_check_mail")->setEnabled( actList.size() > 0 ); } @@ -3934,17 +3970,17 @@ void KMMainWidget::setupFolderView() { if ( GlobalSettings::self()->enableFavoriteFolderView() ) { mFolderView = mFolderViewSplitter; - mSearchAndTree->reparent( mFolderViewSplitter, 0, TQPoint( 0, 0 ) ); + mFolderTree->reparent( mFolderViewSplitter, 0, TQPoint( 0, 0 ) ); mFolderViewSplitter->show(); mFavoriteFolderView->show(); } else { - mFolderView = mSearchAndTree; + mFolderView = mFolderTree; mFolderViewSplitter->hide(); mFavoriteFolderView->hide(); } mFolderView->reparent( mFolderViewParent, 0, TQPoint( 0, 0 ) ); mFolderViewParent->moveToFirst( mFolderView ); - mSearchAndTree->show(); + mFolderTree->show(); } diff --git a/kmail/kmmainwidget.h b/kmail/kmmainwidget.h index 74baaf2a..20e19a3c 100644 --- a/kmail/kmmainwidget.h +++ b/kmail/kmmainwidget.h @@ -201,7 +201,12 @@ public slots: /** Select the folder and jump to the next unread msg */ void folderSelectedUnread( KMFolder* ); - void slotMsgSelected(KMMessage*); + void slotMsgSelected( KMMessage * ); + /** + Open a separate viewer window containing the specified message. + */ + void slotMsgActivated( KMMessage * ); + void slotMsgChanged(); /** Change the current folder, select a message in the current folder */ @@ -283,6 +288,7 @@ protected slots: void slotExpireAll(); void slotInvalidateIMAPFolders(); void slotMarkAllAsRead(); + void slotArchiveFolder(); void slotRemoveFolder(); void slotEmptyFolder(); void slotCompactFolder(); @@ -360,7 +366,6 @@ protected slots: /** etc. */ void slotDisplayCurrentMessage(); - void slotMsgActivated( KMMessage* ); void slotShowNewFromTemplate(); void slotNewFromTemplate( int ); @@ -378,6 +383,9 @@ protected slots: * often the the other updates and is therefor in its own method. */ void updateMarkAsReadAction(); + /** Settings menu */ + void slotToggleShowQuickSearch(); + /** XML-GUI stuff */ void slotEditNotifications(); void slotEditKeys(); @@ -427,6 +435,13 @@ private: */ TQString findCurrentImapPath(); + /** + * This function adds or updates the actions of the forward action menu, taking the + * preference whether to forward inline or as attachment by default into account. + * This has to be called when that preference config has been changed. + */ + void setupForwardActions(); + void setupFolderView(); private slots: @@ -439,7 +454,8 @@ private: *mDeleteThreadAction, *mSaveAsAction, *mUseAction, *mSendAgainAction, *mApplyAllFiltersAction, *mFindInMessageAction, *mSaveAttachmentsAction, *mOpenAction, *mViewSourceAction, - *mFavoritesCheckMailAction; + *mFavoritesCheckMailAction, + *mMoveMsgToFolderAction; // Composition actions KAction *mPrintAction, *mForwardInlineAction, *mForwardAttachedAction, *mForwardDigestAction, @@ -476,8 +492,8 @@ private: KToggleAction* mTotalColumnToggle; KToggleAction* mSizeColumnToggle; - TQVBox *mSearchAndTree; - TQHBox *mFolderQuickSearch; + KToggleAction *mToggleShowQuickSearchAction; + KMFolderTree *mFolderTree; KMail::FavoriteFolderView *mFavoriteFolderView; TQWidget *mFolderView; @@ -513,8 +529,6 @@ private: mFolderHtmlPref, mFolderHtmlLoadExtPref, mFolderThreadPref, mFolderThreadSubjPref, mReaderWindowActive, mReaderWindowBelow; bool mEnableFavoriteFolderView; - bool mEnableFolderQuickSearch; - bool mEnableQuickSearch; // TQPopupMenu *mMessageMenu; KMail::SearchWindow *mSearchWin; @@ -523,7 +537,7 @@ private: *mCompactFolderAction, *mRefreshFolderAction, *mEmptyFolderAction, *mMarkAllAsReadAction, *mFolderMailingListPropertiesAction, *mFolderShortCutCommandAction, *mTroubleshootFolderAction, - *mRemoveDuplicatesAction; + *mRemoveDuplicatesAction, *mArchiveFolderAction; KToggleAction *mPreferHtmlAction, *mPreferHtmlLoadExtAction, *mThreadMessagesAction; KToggleAction *mThreadBySubjectAction; KToggleAction *mFolderAction, *mHeaderAction, *mMimeAction; diff --git a/kmail/kmmainwin.cpp b/kmail/kmmainwin.cpp index 7d69e2ee..994219b0 100644 --- a/kmail/kmmainwin.cpp +++ b/kmail/kmmainwin.cpp @@ -40,7 +40,7 @@ KMMainWin::KMMainWin(TQWidget *) actionCollection(), "new_mail_client" ); mKMMainWidget = new KMMainWidget( this, "KMMainWidget", this, actionCollection() ); - mKMMainWidget->resize( 725, 700 ); + mKMMainWidget->resize( 450, 600 ); setCentralWidget(mKMMainWidget); setupStatusBar(); if (kmkernel->xmlGuiInstance()) diff --git a/kmail/kmmainwin.rc b/kmail/kmmainwin.rc index 07b9f6c5..955dabad 100644 --- a/kmail/kmmainwin.rc +++ b/kmail/kmmainwin.rc @@ -2,7 +2,7 @@ the same menu entries at the same place in KMail and Kontact --> - +

&File @@ -103,6 +103,7 @@ + diff --git a/kmail/kmmessage.cpp b/kmail/kmmessage.cpp index 5a70afdb..6819d68d 100644 --- a/kmail/kmmessage.cpp +++ b/kmail/kmmessage.cpp @@ -415,7 +415,9 @@ void KMMessage::fromDwString(const DwString& str, bool aSetStatus) setSignatureStateChar( headerField("X-KMail-SignatureState").at(0) ); setMDNSentState( static_cast( headerField("X-KMail-MDN-Sent").at(0).latin1() ) ); } - if (attachmentState() == KMMsgAttachmentUnknown && readyToShow()) + if ( invitationState() == KMMsgInvitationUnknown && readyToShow() ) + updateInvitationState(); + if ( attachmentState() == KMMsgAttachmentUnknown && readyToShow() ) updateAttachmentState(); mNeedsAssembly = false; @@ -720,11 +722,6 @@ void KMMessage::parseTextStringFromDwPart( partNode * root, if ( !root ) return; isHTML = false; - // initialy parse the complete message to decrypt any encrypted parts - { - ObjectTreeParser otp( 0, 0, true, false, true ); - otp.parseObjectTree( root ); - } partNode * curNode = root->findType( DwMime::kTypeText, DwMime::kSubtypeUnknown, true, @@ -743,15 +740,18 @@ void KMMessage::parseTextStringFromDwPart( partNode * root, //----------------------------------------------------------------------------- -TQString KMMessage::asPlainText( bool aStripSignature, bool allowDecryption ) const { +TQString KMMessage::asPlainTextFromObjectTree( partNode *root, bool aStripSignature, + bool allowDecryption ) const +{ + Q_ASSERT( root ); + Q_ASSERT( root->processed() ); + TQCString parsedString; bool isHTML = false; const TQTextCodec * codec = 0; - partNode * root = partNode::fromMessage( this ); if ( !root ) return TQString::null; parseTextStringFromDwPart( root, parsedString, codec, isHTML ); - delete root; if ( mOverrideCodec || !codec ) codec = this->codec(); @@ -767,27 +767,27 @@ TQString KMMessage::asPlainText( bool aStripSignature, bool allowDecryption ) co TQPtrList pgpBlocks; TQStrList nonPgpBlocks; if ( Kpgp::Module::prepareMessageForDecryption( parsedString, - pgpBlocks, - nonPgpBlocks ) ) { + pgpBlocks, + nonPgpBlocks ) ) { // Only decrypt/strip off the signature if there is only one OpenPGP // block in the message if ( pgpBlocks.count() == 1 ) { - Kpgp::Block * block = pgpBlocks.first(); - if ( block->type() == Kpgp::PgpMessageBlock || - block->type() == Kpgp::ClearsignedBlock ) { - if ( block->type() == Kpgp::PgpMessageBlock ) { - // try to decrypt this OpenPGP block - block->decrypt(); - } else { - // strip off the signature - block->verify(); - clearSigned = true; - } + Kpgp::Block * block = pgpBlocks.first(); + if ( block->type() == Kpgp::PgpMessageBlock || + block->type() == Kpgp::ClearsignedBlock ) { + if ( block->type() == Kpgp::PgpMessageBlock ) { + // try to decrypt this OpenPGP block + block->decrypt(); + } else { + // strip off the signature + block->verify(); + clearSigned = true; + } - result = codec->toUnicode( nonPgpBlocks.first() ) - + codec->toUnicode( block->text() ) - + codec->toUnicode( nonPgpBlocks.last() ); - } + result = codec->toUnicode( nonPgpBlocks.first() ) + + codec->toUnicode( block->text() ) + + codec->toUnicode( nonPgpBlocks.last() ); + } } } } @@ -820,6 +820,21 @@ TQString KMMessage::asPlainText( bool aStripSignature, bool allowDecryption ) co return result; } +//----------------------------------------------------------------------------- + +TQString KMMessage::asPlainText( bool aStripSignature, bool allowDecryption ) const +{ + partNode *root = partNode::fromMessage( this ); + if ( !root ) + return TQString::null; + + ObjectTreeParser otp; + otp.parseObjectTree( root ); + TQString result = asPlainTextFromObjectTree( root, aStripSignature, allowDecryption ); + delete root; + return result; +} + TQString KMMessage::asQuotedString( const TQString& aHeaderStr, const TQString& aIndentStr, const TQString& selection /* = TQString::null */, @@ -852,11 +867,10 @@ KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy, TQString selection /* = TQString::null */, bool noQuote /* = false */, bool allowDecryption /* = true */, - bool selectionIsBody /* = false */, const TQString &tmpl /* = TQString::null */ ) { KMMessage* msg = new KMMessage; - TQString str, replyStr, mailingListStr, replyToStr, toStr; + TQString mailingListStr, replyToStr, toStr; TQStringList mailingListAddresses; TQCString refStr, headerName; bool replyAll = true; @@ -881,8 +895,6 @@ KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy, } // use the "On ... Joe User wrote:" header by default - replyStr = sReplyAllStr; - switch( replyStrategy ) { case KMail::ReplySmart : { if ( !headerField( "Mail-Followup-To" ).isEmpty() ) { @@ -898,7 +910,7 @@ KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy, else { // doesn't seem to be a mailing list, reply to From: address toStr = from(); - replyStr = sReplyStr; // reply to author, so use "On ... you wrote:" + //replyStr = sReplyStr; // reply to author, so use "On ... you wrote:" replyAll = false; } // strip all my addresses from the list of recipients @@ -1028,7 +1040,6 @@ KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy, else if ( !from().isEmpty() ) { toStr = from(); } - replyStr = sReplyStr; // reply to author, so use "On ... you wrote:" replyAll = false; break; } @@ -1056,15 +1067,20 @@ KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy, // } msg->setSubject( replySubject() ); - - TemplateParser parser( msg, (replyAll ? TemplateParser::ReplyAll : TemplateParser::Reply), - selection, sSmartQuote, noQuote, allowDecryption, selectionIsBody ); - if ( !tmpl.isEmpty() ) { - parser.process( tmpl, this ); - } else { - parser.process( this ); + msg->setHeaderField( "X-KMail-QuotePrefix", + formatString( GlobalSettings::self()->quoteString() ) ); + if( !noQuote ) { + TemplateParser parser( msg, ( replyAll ? TemplateParser::ReplyAll : TemplateParser::Reply ) ); + parser.setAllowDecryption( allowDecryption ); + if ( GlobalSettings::quoteSelectionOnly() ) { + parser.setSelection( selection ); + } + if ( !tmpl.isEmpty() ) { + parser.process( tmpl, this ); + } else { + parser.process( this ); + } } - // setStatus(KMMsgStatusReplied); msg->link(this, KMMsgStatusReplied); @@ -1127,12 +1143,12 @@ KMMessage* KMMessage::createRedirect( const TQString &toStr ) TQString strByWayOf = TQString("%1 (by way of %2 <%3>)") .arg( from() ) .arg( ident.fullName() ) - .arg( ident.emailAddr() ); + .arg( ident.primaryEmailAddress() ); // Resent-From: content TQString strFrom = TQString("%1 <%2>") .arg( ident.fullName() ) - .arg( ident.emailAddr() ); + .arg( ident.primaryEmailAddress() ); // format the current date to be used in Resent-Date: TQString origDate = msg->headerField( "Date" ); @@ -1211,7 +1227,6 @@ void KMMessage::sanitizeHeaders( const TQStringList& whiteList ) KMMessage* KMMessage::createForward( const TQString &tmpl /* = TQString::null */ ) { KMMessage* msg = new KMMessage(); - TQString id; // If this is a multipart mail or if the main part is only the text part, // Make an identical copy of the mail, minus headers, so attachments are @@ -1222,8 +1237,7 @@ KMMessage* KMMessage::createForward( const TQString &tmpl /* = TQString::null */ msg->fromDwString( this->asDwString() ); // remember the type and subtype, initFromMessage sets the contents type to // text/plain, via initHeader, for unclear reasons - const int type = msg->type(); - const int subtype = msg->subtype(); + DwMediaType oldContentType = msg->mMsg->Headers().ContentType(); msg->sanitizeHeaders(); @@ -1240,12 +1254,14 @@ KMMessage* KMMessage::createForward( const TQString &tmpl /* = TQString::null */ } } msg->mMsg->Assemble(); - msg->initFromMessage( this ); + //restore type - msg->setType( type ); - msg->setSubtype( subtype ); - } else if( type() == DwMime::kTypeText && subtype() == DwMime::kSubtypeHtml ) { + msg->mMsg->Headers().ContentType().FromString( oldContentType.AsString() ); + msg->mMsg->Headers().ContentType().Parse(); + msg->mMsg->Assemble(); + } + else if( type() == DwMime::kTypeText && subtype() == DwMime::kSubtypeHtml ) { // This is non-multipart html mail. Let`s make it text/plain and allow // template parser do the hard job. msg->initFromMessage( this ); @@ -1253,7 +1269,8 @@ KMMessage* KMMessage::createForward( const TQString &tmpl /* = TQString::null */ msg->setSubtype( DwMime::kSubtypeHtml ); msg->mNeedsAssembly = true; msg->cleanupHeader(); - } else { + } + else { // This is a non-multipart, non-text mail (e.g. text/calendar). Construct // a multipart/mixed mail and add the original body as an attachment. msg->initFromMessage( this ); @@ -1287,9 +1304,7 @@ KMMessage* KMMessage::createForward( const TQString &tmpl /* = TQString::null */ msg->setSubject( forwardSubject() ); - TemplateParser parser( msg, TemplateParser::Forward, - asPlainText( false, false ), - false, false, false, false); + TemplateParser parser( msg, TemplateParser::Forward ); if ( !tmpl.isEmpty() ) { parser.process( tmpl, this ); } else { @@ -1351,25 +1366,27 @@ static const int numMdnMessageBoxes static int requestAdviceOnMDN( const char * what ) { - for ( int i = 0 ; i < numMdnMessageBoxes ; ++i ) - if ( !qstrcmp( what, mdnMessageBoxes[i].dontAskAgainID ) ) + for ( int i = 0 ; i < numMdnMessageBoxes ; ++i ) { + if ( !qstrcmp( what, mdnMessageBoxes[i].dontAskAgainID ) ) { if ( mdnMessageBoxes[i].canDeny ) { - const KCursorSaver saver( TQCursor::ArrowCursor ); - int answer = TQMessageBox::information( 0, - i18n("Message Disposition Notification Request"), - i18n( mdnMessageBoxes[i].text ), - i18n("&Ignore"), i18n("Send \"&denied\""), i18n("&Send") ); - return answer ? answer + 1 : 0 ; // map to "mode" in createMDN + const KCursorSaver saver( TQCursor::ArrowCursor ); + int answer = TQMessageBox::information( 0, + i18n("Message Disposition Notification Request"), + i18n( mdnMessageBoxes[i].text ), + i18n("&Ignore"), i18n("Send \"&denied\""), i18n("&Send") ); + return answer ? answer + 1 : 0 ; // map to "mode" in createMDN } else { - const KCursorSaver saver( TQCursor::ArrowCursor ); - int answer = TQMessageBox::information( 0, - i18n("Message Disposition Notification Request"), - i18n( mdnMessageBoxes[i].text ), - i18n("&Ignore"), i18n("&Send") ); - return answer ? answer + 2 : 0 ; // map to "mode" in createMDN + const KCursorSaver saver( TQCursor::ArrowCursor ); + int answer = TQMessageBox::information( 0, + i18n("Message Disposition Notification Request"), + i18n( mdnMessageBoxes[i].text ), + i18n("&Ignore"), i18n("&Send") ); + return answer ? answer + 2 : 0 ; // map to "mode" in createMDN } + } + } kdWarning(5006) << "didn't find data for message box \"" - << what << "\"" << endl; + << what << "\"" << endl; return 0; } @@ -2487,28 +2504,38 @@ TQCString KMMessage::contentTransferEncodingStr() const //----------------------------------------------------------------------------- -int KMMessage::contentTransferEncoding() const +int KMMessage::contentTransferEncoding( DwEntity *entity ) const { - DwHeaders& header = mMsg->Headers(); - if (header.HasContentTransferEncoding()) + if ( !entity ) + entity = mMsg; + + DwHeaders& header = entity->Headers(); + if ( header.HasContentTransferEncoding() ) return header.ContentTransferEncoding().AsEnum(); else return DwMime::kCteNull; } //----------------------------------------------------------------------------- -void KMMessage::setContentTransferEncodingStr(const TQCString& aStr) +void KMMessage::setContentTransferEncodingStr( const TQCString& cteString, + DwEntity *entity ) { - mMsg->Headers().ContentTransferEncoding().FromString(aStr); - mMsg->Headers().ContentTransferEncoding().Parse(); + if ( !entity ) + entity = mMsg; + + entity->Headers().ContentTransferEncoding().FromString( cteString ); + entity->Headers().ContentTransferEncoding().Parse(); mNeedsAssembly = true; } //----------------------------------------------------------------------------- -void KMMessage::setContentTransferEncoding(int aCte) +void KMMessage::setContentTransferEncoding( int cte, DwEntity *entity ) { - mMsg->Headers().ContentTransferEncoding().FromEnum(aCte); + if ( !entity ) + entity = mMsg; + + entity->Headers().ContentTransferEncoding().FromEnum( cte ); mNeedsAssembly = true; } @@ -2526,6 +2553,16 @@ void KMMessage::setNeedsAssembly() mNeedsAssembly = true; } +//----------------------------------------------------------------------------- +void KMMessage::assembleIfNeeded() +{ + Q_ASSERT( mMsg ); + + if ( mNeedsAssembly ) { + mMsg->Assemble(); + mNeedsAssembly = false; + } +} //----------------------------------------------------------------------------- TQCString KMMessage::body() const @@ -2646,22 +2683,16 @@ TQValueList KMMessage::determineAllowedCtes( const CharFreq& cf, void KMMessage::setBodyAndGuessCte( const TQByteArray& aBuf, TQValueList & allowedCte, bool allow8Bit, - bool willBeSigned ) + bool willBeSigned, + DwEntity *entity ) { - CharFreq cf( aBuf ); // it's safe to pass null arrays + if ( !entity ) + entity = mMsg; + CharFreq cf( aBuf ); // it's safe to pass null arrays allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned ); - -#ifndef NDEBUG - DwString dwCte; - DwCteEnumToStr(allowedCte[0], dwCte); - kdDebug(5006) << "CharFreq returned " << cf.type() << "/" - << cf.printableRatio() << " and I chose " - << dwCte.c_str() << endl; -#endif - - setCte( allowedCte[0] ); // choose best fitting - setBodyEncodedBinary( aBuf ); + setCte( allowedCte[0], entity ); // choose best fitting + setBodyEncodedBinary( aBuf, entity ); } @@ -2669,32 +2700,29 @@ void KMMessage::setBodyAndGuessCte( const TQByteArray& aBuf, void KMMessage::setBodyAndGuessCte( const TQCString& aBuf, TQValueList & allowedCte, bool allow8Bit, - bool willBeSigned ) + bool willBeSigned, + DwEntity *entity ) { - CharFreq cf( aBuf.data(), aBuf.size()-1 ); // it's safe to pass null strings + if ( !entity ) + entity = mMsg; + CharFreq cf( aBuf.data(), aBuf.size()-1 ); // it's safe to pass null strings allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned ); - -#ifndef NDEBUG - DwString dwCte; - DwCteEnumToStr(allowedCte[0], dwCte); - kdDebug(5006) << "CharFreq returned " << cf.type() << "/" - << cf.printableRatio() << " and I chose " - << dwCte.c_str() << endl; -#endif - - setCte( allowedCte[0] ); // choose best fitting - setBodyEncoded( aBuf ); + setCte( allowedCte[0], entity ); // choose best fitting + setBodyEncoded( aBuf, entity ); } //----------------------------------------------------------------------------- -void KMMessage::setBodyEncoded(const TQCString& aStr) +void KMMessage::setBodyEncoded(const TQCString& aStr, DwEntity *entity ) { + if ( !entity ) + entity = mMsg; + DwString dwSrc(aStr.data(), aStr.size()-1 /* not the trailing NUL */); DwString dwResult; - switch (cte()) + switch (cte( entity )) { case DwMime::kCteBase64: DwEncodeBase64(dwSrc, dwResult); @@ -2707,30 +2735,35 @@ void KMMessage::setBodyEncoded(const TQCString& aStr) break; } - mMsg->Body().FromString(dwResult); + entity->Body().FromString(dwResult); mNeedsAssembly = true; } //----------------------------------------------------------------------------- -void KMMessage::setBodyEncodedBinary(const TQByteArray& aStr) +void KMMessage::setBodyEncodedBinary( const TQByteArray& aStr, DwEntity *entity ) { + if ( !entity ) + entity = mMsg; + DwString dwSrc(aStr.data(), aStr.size()); DwString dwResult; - switch (cte()) + switch ( cte( entity ) ) { case DwMime::kCteBase64: - DwEncodeBase64(dwSrc, dwResult); + DwEncodeBase64( dwSrc, dwResult ); break; case DwMime::kCteQuotedPrintable: - DwEncodeQuotedPrintable(dwSrc, dwResult); + DwEncodeQuotedPrintable( dwSrc, dwResult ); break; default: dwResult = dwSrc; break; } - mMsg->Body().FromString(dwResult); + entity->Body().FromString( dwResult ); + entity->Body().Parse(); + mNeedsAssembly = true; } @@ -2752,6 +2785,7 @@ void KMMessage::setBody(const char* aStr) mNeedsAssembly = true; } +//----------------------------------------------------------------------------- void KMMessage::setMultiPartBody( const TQCString & aStr ) { setBody( aStr ); mMsg->Body().Parse(); @@ -3053,7 +3087,8 @@ void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart ) // Content-description if (headers.HasContentDescription()) - aPart->setContentDescription(headers.ContentDescription().AsString().c_str()); + aPart->setContentDescription( KMMsgBase::decodeRFC2047String( + headers.ContentDescription().AsString().c_str() ) ); else aPart->setContentDescription(""); @@ -3136,6 +3171,44 @@ void KMMessage::deleteBodyParts() mMsg->Body().DeleteBodyParts(); } +//----------------------------------------------------------------------------- + +bool KMMessage::deleteBodyPart( int partIndex ) +{ + KMMessagePart part; + DwBodyPart *dwpart = findPart( partIndex ); + if ( !dwpart ) + return false; + KMMessage::bodyPart( dwpart, &part, true ); + if ( !part.isComplete() ) + return false; + + DwBody *parentNode = dynamic_cast( dwpart->Parent() ); + if ( !parentNode ) + return false; + parentNode->RemoveBodyPart( dwpart ); + + // add dummy part to show that a attachment has been deleted + KMMessagePart dummyPart; + dummyPart.duplicate( part ); + TQString comment = i18n("This attachment has been deleted."); + if ( !part.fileName().isEmpty() ) + comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() ); + dummyPart.setContentDescription( comment ); + dummyPart.setBodyEncodedBinary( TQByteArray() ); + TQCString cd = dummyPart.contentDisposition(); + if ( cd.find( "inline", 0, false ) == 0 ) { + cd.replace( 0, 10, "attachment" ); + dummyPart.setContentDisposition( cd ); + } else if ( cd.isEmpty() ) { + dummyPart.setContentDisposition( "attachment" ); + } + DwBodyPart* newDwPart = createDWBodyPart( &dummyPart ); + parentNode->AddBodyPart( newDwPart ); + getTopLevelPart()->Assemble(); + return true; +} + //----------------------------------------------------------------------------- DwBodyPart* KMMessage::createDWBodyPart(const KMMessagePart* aPart) { @@ -3150,9 +3223,7 @@ DwBodyPart* KMMessage::createDWBodyPart(const KMMessagePart* aPart) TQCString cte = aPart->cteStr(); TQCString contDesc = aPart->contentDescriptionEncoded(); TQCString contDisp = aPart->contentDisposition(); - TQCString encoding = autoDetectCharset(charset, sPrefCharsets, aPart->name()); - if (encoding.isEmpty()) encoding = "utf-8"; - TQCString name = KMMsgBase::encodeRFC2231String(aPart->name(), encoding); + TQCString name = KMMsgBase::encodeRFC2231StringAutoDetectCharset( aPart->name(), charset ); bool RFC2231encoded = aPart->name() != TQString(name); TQCString paramAttr = aPart->parameterAttribute(); @@ -3229,12 +3300,8 @@ DwBodyPart* KMMessage::createDWBodyPart(const KMMessagePart* aPart) if (!paramAttr.isEmpty()) { - TQCString encoding = autoDetectCharset(charset, sPrefCharsets, - aPart->parameterValue()); - if (encoding.isEmpty()) encoding = "utf-8"; TQCString paramValue; - paramValue = KMMsgBase::encodeRFC2231String(aPart->parameterValue(), - encoding); + paramValue = KMMsgBase::encodeRFC2231StringAutoDetectCharset( aPart->parameterValue(), charset ); DwParameter *param = new DwParameter; if (aPart->parameterValue() != TQString(paramValue)) { @@ -3771,24 +3838,41 @@ TQString KMMessage::emailAddrAsAnchor(const TQString& aEmail, bool stripped, con return aEmail; TQStringList addressList = KPIM::splitEmailAddrList( aEmail ); - TQString result; for( TQStringList::ConstIterator it = addressList.begin(); ( it != addressList.end() ); ++it ) { if( !(*it).isEmpty() ) { - TQString address = *it; + + // Extract the name, mail and some pretty versions out of the mail address + TQString name, mail; + KPIM::getNameAndMail( *it, name, mail ); + TQString pretty; + TQString prettyStripped; + if ( name.stripWhiteSpace().isEmpty() ) { + pretty = mail; + prettyStripped = mail; + } else { + pretty = KPIM::quoteNameIfNecessary( name ) + " <" + mail + ">"; + prettyStripped = name; + } + if(aLink) { - result += ""; + result += ""; + } + + if ( stripped ) { + result += KMMessage::quoteHtmlChars( prettyStripped, true ); + } + else { + result += KMMessage::quoteHtmlChars( pretty, true ); } - if( stripped ) - address = KMMessage::stripEmailAddr( address ); - result += KMMessage::quoteHtmlChars( address, true ); + if(aLink) - result += ", "; + result += ", "; } } // cut of the trailing ", " @@ -3800,7 +3884,6 @@ TQString KMMessage::emailAddrAsAnchor(const TQString& aEmail, bool stripped, con return result; } - //----------------------------------------------------------------------------- //static TQStringList KMMessage::stripAddressFromAddressList( const TQString& address, @@ -4023,7 +4106,7 @@ TQCString KMMessage::charset() const } //----------------------------------------------------------------------------- -void KMMessage::setCharset(const TQCString& bStr) +void KMMessage::setCharset( const TQCString &charset, DwEntity *entity ) { kdWarning( type() != DwMime::kTypeText ) << "KMMessage::setCharset(): trying to set a charset for a non-textual mimetype." << endl @@ -4031,23 +4114,32 @@ void KMMessage::setCharset(const TQCString& bStr) << "====================================================================" << endl << kdBacktrace( 5 ) << endl << "====================================================================" << endl; - TQCString aStr = bStr; - KPIM::kAsciiToLower( aStr.data() ); - DwMediaType &mType = dwContentType(); + + if ( !entity ) + entity = mMsg; + + DwMediaType &mType = entity->Headers().ContentType(); mType.Parse(); - DwParameter *param=mType.FirstParameter(); - while(param) + DwParameter *param = mType.FirstParameter(); + while( param ) { + // FIXME use the mimelib functions here for comparison. - if (!kasciistricmp(param->Attribute().c_str(), "charset")) break; - else param=param->Next(); - if (!param){ - param=new DwParameter; - param->SetAttribute("charset"); - mType.AddParameter(param); + if ( !kasciistricmp( param->Attribute().c_str(), "charset" ) ) + break; + + param = param->Next(); + } + if ( !param ) { + param = new DwParameter; + param->SetAttribute( "charset" ); + mType.AddParameter( param ); } else mType.SetModified(); - param->SetValue(DwString(aStr)); + + TQCString lowerCharset = charset; + KPIM::kAsciiToLower( lowerCharset.data() ); + param->SetValue( DwString( lowerCharset ) ); mType.Assemble(); } @@ -4078,7 +4170,8 @@ void KMMessage::setSignatureState(KMMsgSignatureState s, int idx) KMMsgBase::setSignatureState(s, idx); } -void KMMessage::setMDNSentState( KMMsgMDNSentState status, int idx ) { +void KMMessage::setMDNSentState( KMMsgMDNSentState status, int idx ) +{ if ( mMDNSentState == status ) return; if ( status == 0 ) @@ -4247,6 +4340,21 @@ void KMMessage::updateBodyPart(const TQString partSpecifier, const TQByteArray & } } +void KMMessage::updateInvitationState() +{ + if ( mMsg && mMsg->hasHeaders() && mMsg->Headers().HasContentType() ) { + TQString cntType = mMsg->Headers().ContentType().TypeStr().c_str(); + cntType += '/'; + cntType += mMsg->Headers().ContentType().SubtypeStr().c_str(); + if ( cntType.lower() == "text/calendar" ) { + setStatus( KMMsgStatusHasInvitation ); + return; + } + } + setStatus( KMMsgStatusHasNoInvitation ); + return; +} + //----------------------------------------------------------------------------- void KMMessage::updateAttachmentState( DwBodyPart* part ) { @@ -4270,6 +4378,18 @@ void KMMessage::updateAttachmentState( DwBodyPart* part ) filenameEmpty = KMMsgBase::decodeRFC2231String( KMMsgBase::extractRFC2231HeaderField( cd.AsString().c_str(), "filename" ) ).isEmpty(); } } + + // Filename still empty? Check if the content-type has a "name" parameter and try to use that as + // the attachment name + if ( filenameEmpty && part->Headers().HasContentType() ) { + DwMediaType contentType = part->Headers().ContentType(); + filenameEmpty = contentType.Name().empty(); + if ( filenameEmpty ) { + // let's try if it is rfc 2231 encoded which mimelib can't handle + filenameEmpty = KMMsgBase::decodeRFC2231String( KMMsgBase::extractRFC2231HeaderField( + contentType.AsString().c_str(), "name" ) ).isEmpty(); + } + } } if ( part->hasHeaders() && @@ -4312,15 +4432,17 @@ void KMMessage::updateAttachmentState( DwBodyPart* part ) setStatus( KMMsgStatusHasNoAttach ); } -void KMMessage::setBodyFromUnicode( const TQString & str ) { +void KMMessage::setBodyFromUnicode( const TQString &str, DwEntity *entity ) +{ TQCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str ); if ( encoding.isEmpty() ) encoding = "utf-8"; const TQTextCodec * codec = KMMsgBase::codecForName( encoding ); assert( codec ); TQValueList dummy; - setCharset( encoding ); - setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false /* no 8bit */ ); + setCharset( encoding, entity ); + setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false /* no 8bit */, + false, entity ); } const TQTextCodec * KMMessage::codec() const { @@ -4371,3 +4493,28 @@ void KMMessage::deleteWhenUnused() { sPendingDeletes << this; } + +DwBodyPart* KMMessage::findPart( int index ) +{ + int accu = 0; + return findPartInternal( getTopLevelPart(), index, accu ); +} + +DwBodyPart* KMMessage::findPartInternal(DwEntity * root, int index, int & accu) +{ + accu++; + if ( index < accu ) // should not happen + return 0; + DwBodyPart *current = dynamic_cast( root ); + if ( index == accu ) + return current; + DwBodyPart *rv = 0; + if ( root->Body().FirstBodyPart() ) + rv = findPartInternal( root->Body().FirstBodyPart(), index, accu ); + if ( !rv && current && current->Next() ) + rv = findPartInternal( current->Next(), index, accu ); + if ( !rv && root->Body().Message() ) + rv = findPartInternal( root->Body().Message(), index, accu ); + return rv; +} + diff --git a/kmail/kmmessage.h b/kmail/kmmessage.h index 53c1e408..958d017e 100644 --- a/kmail/kmmessage.h +++ b/kmail/kmmessage.h @@ -49,6 +49,7 @@ namespace KMail { class HeaderStrategy; } +class DwEntity; class DwBodyPart; class DwMediaType; class DwHeaders; @@ -161,8 +162,8 @@ public: required header fields with the proper values. The returned message is not stored in any folder. Marks this message as replied. */ KMMessage* createReply( KMail::ReplyStrategy replyStrategy = KMail::ReplySmart, - TQString selection=TQString::null, bool noQuote=false, - bool allowDecryption=true, bool selectionIsBody=false, + TQString selection=TQString::null, bool noQuote = false, + bool allowDecryption = true, const TQString &tmpl = TQString::null ); /** Create a new message that is a redirect to this message, filling all @@ -503,25 +504,46 @@ public: the header via headers() function) */ void setNeedsAssembly(); - /** Get or set the 'Content-Transfer-Encoding' header field - The member functions that involve enumerated types (ints) - will work only for well-known encodings. */ + /** + * Assemble the internal message. This is done automatically in most + * cases, but sometimes still necessary to call this manually. + */ + void assembleIfNeeded(); + + /** + * Get or set the 'Content-Transfer-Encoding' header field + * The member functions that involve enumerated types (ints) + * will work only for well-known encodings. + * Some functions take a DwEntity as second parameter, which + * specifies the body part or message of which the CTE will be changed or + * returned. If this is zero, the toplevel message will be taken. + */ TQCString contentTransferEncodingStr() const; - int contentTransferEncoding() const; - void setContentTransferEncodingStr(const TQCString& aStr); - void setContentTransferEncoding(int aCte); + int contentTransferEncoding( DwEntity *entity = 0 ) const; + void setContentTransferEncodingStr( const TQCString& cteString, DwEntity *entity = 0 ); + void setContentTransferEncoding( int cte, DwEntity *entity = 0 ); - /** Cte is short for ContentTransferEncoding. - These functions are an alternative to the ones with longer names. */ + /** + * Cte is short for ContentTransferEncoding. + * These functions are an alternative to the ones with longer names. + */ TQCString cteStr() const { return contentTransferEncodingStr(); } - int cte() const { return contentTransferEncoding(); } - void setCteStr(const TQCString& aStr) { setContentTransferEncodingStr(aStr); } - void setCte(int aCte) { setContentTransferEncoding(aCte); } + int cte( DwEntity *entity = 0 ) const { return contentTransferEncoding( entity ); } + void setCteStr( const TQCString& aStr, DwEntity *entity = 0 ) { + setContentTransferEncodingStr( aStr, entity ); + } + void setCte( int aCte, DwEntity *entity = 0 ) { + setContentTransferEncoding( aCte, entity ); + } - /** Sets this body part's content to @p str. @p str is subject to - automatic charset and CTE detection. - **/ - void setBodyFromUnicode( const TQString & str ); + /** + * Sets this body's content to @p str. @p str is subject to + * automatic charset and CTE detection. + * + * @param entity The body of this entity will be changed. If entity is 0, + * the body of the whole message will be changed. + */ + void setBodyFromUnicode( const TQString & str, DwEntity *entity = 0 ); /** Returns the body part decoded to unicode. **/ @@ -538,11 +560,17 @@ public: /** Hack to enable structured body parts to be set as flat text... */ void setMultiPartBody( const TQCString & aStr ); - /** Set the message body, encoding it according to the current content - transfer encoding. The first method for null terminated strings, - the second for binary data */ - void setBodyEncoded(const TQCString& aStr); - void setBodyEncodedBinary(const TQByteArray& aStr); + /** + * Set the message body, encoding it according to the current content + * transfer encoding. The first method for null terminated strings, + * the second for binary data. + * + * @param entity Specifies the body part or message of which the body will be + * set. If this is 0, the body of the toplevel message will be + * set. + */ + void setBodyEncoded( const TQCString& aStr, DwEntity *entity = 0 ); + void setBodyEncodedBinary( const TQByteArray& aStr, DwEntity *entity = 0 ); /** Returns a list of content-transfer-encodings that can be used with the given result of the character frequency analysis of a message or @@ -551,23 +579,29 @@ public: bool allow8Bit, bool willBeSigned ); - /** Sets body, encoded in the best fitting - content-transfer-encoding, which is determined by character - frequency count. + /** + * Sets body, encoded in the best fitting + * content-transfer-encoding, which is determined by character + * frequency count. + * + * @param aBuf input buffer + * @param allowedCte return: list of allowed cte's + * @param allow8Bit whether "8bit" is allowed as cte. + * @param willBeSigned whether "7bit"/"8bit" is allowed as cte according to RFC 3156 + * @param entity The body of this message or body part will get changed. + * If this is 0, the body of the toplevel message will be + * set. + */ + void setBodyAndGuessCte( const TQByteArray& aBuf, TQValueList& allowedCte, + bool allow8Bit = false, + bool willBeSigned = false, + DwEntity *entity = 0 ); - @param aBuf input buffer - @param allowedCte return: list of allowed cte's - @param allow8Bit whether "8bit" is allowed as cte. - @param willBeSigned whether "7bit"/"8bit" is allowed as cte according to RFC 3156 - */ - void setBodyAndGuessCte( const TQByteArray& aBuf, - TQValueList& allowedCte, - bool allow8Bit = false, - bool willBeSigned = false ); void setBodyAndGuessCte( const TQCString& aBuf, - TQValueList& allowedCte, - bool allow8Bit = false, - bool willBeSigned = false ); + TQValueList& allowedCte, + bool allow8Bit = false, + bool willBeSigned = false, + DwEntity *entity = 0 ); /** Returns a decoded version of the body from the current content transfer encoding. The first method returns a null terminated string, the second @@ -627,6 +661,12 @@ public: /** Delete all body parts. */ void deleteBodyParts(); + /** + * Delete a body part with the specified part index. + * A dummy body part with the text "the attachment foo was deleted" will replace the old part. + */ + bool deleteBodyPart( int partIndex ); + /** Set "Status" and "X-Status" fields of the message from the * internal message status. */ void setStatusFields(); @@ -725,8 +765,15 @@ public: /** Get the message charset.*/ TQCString charset() const; - /** Set the message charset. */ - void setCharset(const TQCString& aStr); + /** + * Sets the charset of the message or a subpart of the message. + * Only call this when the message or the subpart has a textual mimetype. + * + * @param aStr the MIME-compliant charset name, like 'ISO-88519-15'. + * @param entity the body part or message of which the charset should be changed. + * If this is 0, the charset of the toplevel message will be changed. + */ + void setCharset( const TQCString& charset, DwEntity *entity = 0 ); /** Get a TQTextCodec suitable for this message part */ const TQTextCodec * codec() const; @@ -822,7 +869,8 @@ public: /** Set if the message is ready to be shown */ void setReadyToShow( bool v ) { mReadyToShow = v; } - void updateAttachmentState(DwBodyPart * part = 0); + void updateAttachmentState( DwBodyPart *part = 0 ); + void updateInvitationState(); /** Return, if the message should not be deleted */ bool transferInProgress() const; @@ -860,6 +908,15 @@ public: converting HTML to plain text if necessary. */ TQString asPlainText( bool stripSignature, bool allowDecryption ) const; + /** + * Same as asPlainText(), only that this method expects an already parsed object tree as + * paramter. + * By passing an already parsed objecttree, this allows to share the objecttree and therefore + * reduce the amount of parsing (which can include decrypting, which can include a passphrase dialog) + */ + TQString asPlainTextFromObjectTree( partNode *root, bool stripSignature, + bool allowDecryption ) const; + /** Get stored cursor position */ int getCursorPos() { return mCursorPos; }; /** Set cursor position as offset from message start */ @@ -879,6 +936,8 @@ public: /** Delete this message as soon as it no longer in use. */ void deleteWhenUnused(); + DwBodyPart* findPart( int index ); + private: /** Initialization shared by the ctors. */ @@ -886,10 +945,12 @@ private: /** Assign the values of @param other to this message. Used in the copy c'tor. */ void assign( const KMMessage& other ); + DwBodyPart* findPartInternal( DwEntity* root, int index, int &accu ); + TQString mDrafts; TQString mTemplates; mutable DwMessage* mMsg; - mutable bool mNeedsAssembly :1; + mutable bool mNeedsAssembly :1; bool mDecodeHTML :1; bool mReadyToShow :1; bool mComplete :1; diff --git a/kmail/kmmimeparttree.cpp b/kmail/kmmimeparttree.cpp index cafef94e..d7fb9541 100644 --- a/kmail/kmmimeparttree.cpp +++ b/kmail/kmmimeparttree.cpp @@ -129,8 +129,6 @@ void KMMimePartTree::itemRightClicked( TQListViewItem* item, kdDebug(5006) << "Item was not a KMMimePartTreeItem!" << endl; } else { - kdDebug(5006) << "\n**\n** KMMimePartTree::itemRightClicked() **\n**" << endl; - TQPopupMenu* popup = new TQPopupMenu; if ( mCurrentContextMenuItem->node()->nodeId() > 2 && mCurrentContextMenuItem->node()->typeString() != "Multipart" ) { diff --git a/kmail/kmmsgbase.cpp b/kmail/kmmsgbase.cpp index df53e6e5..e3d3fec1 100644 --- a/kmail/kmmsgbase.cpp +++ b/kmail/kmmsgbase.cpp @@ -243,6 +243,14 @@ void KMMsgBase::setStatus(const KMMsgStatus aStatus, int idx) mStatus &= ~KMMsgStatusHasAttach; mStatus |= KMMsgStatusHasNoAttach; break; + case KMMsgStatusHasInvitation: + mStatus &= ~KMMsgStatusHasNoInvitation; + mStatus |= KMMsgStatusHasInvitation; + break; + case KMMsgStatusHasNoInvitation: + mStatus &= ~KMMsgStatusHasInvitation; + mStatus |= KMMsgStatusHasNoInvitation; + break; default: mStatus = aStatus; break; @@ -658,7 +666,8 @@ TQString KMMsgBase::decodeRFC2047String(const TQCString& aStr, TQCString prefCha return TQString::null; if ( str.find( "=?" ) < 0 ) { - if ( !prefCharset.isEmpty() ) { + if ( !prefCharset.isEmpty() && + kmkernel->isCodecAsciiCompatible( KMMsgBase::codecForName( prefCharset ) ) ) { if ( prefCharset == "us-ascii" ) { // isn`t this foolproof? return KMMsgBase::codecForName( "utf-8" )->toUnicode( str ); @@ -666,9 +675,15 @@ TQString KMMsgBase::decodeRFC2047String(const TQCString& aStr, TQCString prefCha return KMMsgBase::codecForName( prefCharset )->toUnicode( str ); } } else { - return KMMsgBase::codecForName( GlobalSettings::self()-> + if ( kmkernel->isCodecAsciiCompatible( KMMsgBase::codecForName( + GlobalSettings::self()->fallbackCharacterEncoding().latin1() ) ) ) { + return KMMsgBase::codecForName( GlobalSettings::self()-> fallbackCharacterEncoding().latin1() )->toUnicode( str ); + } } + + // Not RFC2047 encoded, and codec not ascii-compatible -> interpret as ascii + return TQString::fromAscii( str ); } TQString result; @@ -917,6 +932,16 @@ TQCString KMMsgBase::encodeRFC2231String( const TQString& _str, return result; } +//----------------------------------------------------------------------------- +TQCString KMMsgBase::encodeRFC2231StringAutoDetectCharset( const TQString &str, + const TQCString &defaultCharset ) +{ + TQCString encoding = KMMsgBase::autoDetectCharset( defaultCharset, + KMMessage::preferredCharsets(), str ); + if ( encoding.isEmpty() ) + encoding = "utf-8"; + return KMMsgBase::encodeRFC2231String( str, encoding ); +} //----------------------------------------------------------------------------- TQString KMMsgBase::decodeRFC2231String(const TQCString& _str) @@ -1077,6 +1102,18 @@ KMMsgAttachmentState KMMsgBase::attachmentState() const return KMMsgAttachmentUnknown; } + +KMMsgInvitationState KMMsgBase::invitationState() const +{ + KMMsgStatus st = status(); + if (st & KMMsgStatusHasInvitation) + return KMMsgHasInvitation; + else if (st & KMMsgStatusHasNoInvitation) + return KMMsgHasNoInvitation; + else + return KMMsgInvitationUnknown; +} + //----------------------------------------------------------------------------- static void swapEndian(TQString &str) { @@ -1340,11 +1377,11 @@ const uchar *KMMsgBase::asIndexString(int &length) const //these are completely arbitrary order tmp_str = fromStrip().stripWhiteSpace(); - STORE_DATA_LEN(MsgFromPart, tmp_str.unicode(), tmp_str.length() * 2, true); + STORE_DATA_LEN(MsgFromStripPart, tmp_str.unicode(), tmp_str.length() * 2, true); tmp_str = subject().stripWhiteSpace(); STORE_DATA_LEN(MsgSubjectPart, tmp_str.unicode(), tmp_str.length() * 2, true); tmp_str = toStrip().stripWhiteSpace(); - STORE_DATA_LEN(MsgToPart, tmp_str.unicode(), tmp_str.length() * 2, true); + STORE_DATA_LEN(MsgToStripPart, tmp_str.unicode(), tmp_str.length() * 2, true); tmp_str = replyToIdMD5().stripWhiteSpace(); STORE_DATA_LEN(MsgReplyToIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true); tmp_str = xmark().stripWhiteSpace(); @@ -1376,6 +1413,12 @@ const uchar *KMMsgBase::asIndexString(int &length) const tmp = UID(); STORE_DATA(MsgUIDPart, tmp); + tmp_str = from(); + STORE_DATA_LEN( MsgFromPart, tmp_str.unicode(), tmp_str.length() * 2, true ); + + tmp_str = to(); + STORE_DATA_LEN( MsgToPart, tmp_str.unicode(), tmp_str.length() * 2, true ); + return ret; } #undef STORE_DATA_LEN diff --git a/kmail/kmmsgbase.h b/kmail/kmmsgbase.h index b2d86783..1137bb6e 100644 --- a/kmail/kmmsgbase.h +++ b/kmail/kmmsgbase.h @@ -57,7 +57,9 @@ enum MsgStatus KMMsgStatusSpam = 0x00002000, KMMsgStatusHam = 0x00004000, KMMsgStatusHasAttach = 0x00008000, - KMMsgStatusHasNoAttach = 0x00010000 + KMMsgStatusHasNoAttach = 0x00010000, + KMMsgStatusHasInvitation = 0x00020000, + KMMsgStatusHasNoInvitation = 0x00040000 }; typedef uint KMMsgStatus; @@ -132,6 +134,13 @@ typedef enum KMMsgAttachmentUnknown } KMMsgAttachmentState; +/** Flags for invitation state */ +typedef enum +{ + KMMsgHasInvitation, + KMMsgHasNoInvitation, + KMMsgInvitationUnknown +} KMMsgInvitationState; class KMMsgBase { @@ -250,7 +259,9 @@ public: /** Important header fields of the message that are also kept in the index. */ virtual TQString subject(void) const = 0; virtual TQString fromStrip(void) const = 0; + virtual TQString from() const = 0; virtual TQString toStrip(void) const = 0; + virtual TQString to() const = 0; virtual TQString replyToIdMD5(void) const = 0; virtual TQString msgIdMD5(void) const = 0; virtual TQString replyToAuxIdMD5() const = 0; @@ -353,11 +364,18 @@ public: static TQCString encodeRFC2231String(const TQString& aStr, const TQCString& charset); + /** + * Just like encodeRFC2231String, only that the encoding is auto-detected. + * @param defaultCharset If given, this will be the prefered charset + */ + static TQCString encodeRFC2231StringAutoDetectCharset( const TQString &str, + const TQCString &defaultCharset = "" ); + /** Decode given string as described in RFC2231 */ static TQString decodeRFC2231String(const TQCString& aStr); /** Extract a given param from the RFC2231-encoded header field, in particular concatenate possibly multiple entries, which are given as paramname*0=..; - paramname*1=..; ... or paramname*0*=..; paramname*1*=..; ... and return + paramname*1=..; ... or paramname*0*=..; paramname*1*=..; ... and return their value as one string. That string will still be encoded */ static TQCString extractRFC2231HeaderField( const TQCString &aStr, const TQCString &field ); @@ -385,6 +403,9 @@ public: /** Return if the message has at least one attachment */ virtual KMMsgAttachmentState attachmentState() const; + /** Return if the message contains an invitation */ + virtual KMMsgInvitationState invitationState() const; + /** Check for prefixes @p prefixRegExps in @p str. If none is found, @p newPrefix + ' ' is prepended to @p str and the resulting string is returned. If @p replace is true, any @@ -439,9 +460,9 @@ public: { MsgNoPart = 0, //unicode strings - MsgFromPart = 1, + MsgFromStripPart = 1, MsgSubjectPart = 2, - MsgToPart = 3, + MsgToStripPart = 3, MsgReplyToIdMD5Part = 4, MsgIdMD5Part = 5, MsgXMarkPart = 6, @@ -459,7 +480,9 @@ public: // and another unsigned long MsgStatusPart = 16, MsgSizeServerPart = 17, - MsgUIDPart = 18 + MsgUIDPart = 18, + MsgToPart = 19, + MsgFromPart = 20 }; /** access to long msgparts */ off_t getLongPart(MsgPartType) const; diff --git a/kmail/kmmsgdict.cpp b/kmail/kmmsgdict.cpp index bedc3bb4..8f9a9551 100644 --- a/kmail/kmmsgdict.cpp +++ b/kmail/kmmsgdict.cpp @@ -458,8 +458,15 @@ int KMMsgDict::readFolderIds( FolderStorage& storage ) return -1; } - //if (!msn) - //kdDebug(5006) << "Dict found zero serial number in folder " << folder->label() << endl; + // We found a serial number that is zero. This is not allowed, and would + // later cause problems like in bug 149715. + // Therefore, use a fresh serial number instead + if ( msn == 0 ) { + kdWarning(5006) << "readFolderIds(): Found serial number zero at index " << index + << " in folder " << filename << endl; + msn = getNextMsgSerNum(); + Q_ASSERT( msn != 0 ); + } // Insert into the dict. Don't use dict->replace() as we _know_ // there is no entry with the same msn, we just made sure. @@ -556,6 +563,10 @@ int KMMsgDict::writeFolderIds( const FolderStorage &storage ) Q_UINT32 msn = rentry->getMsn(index); if (!fwrite(&msn, sizeof(msn), 1, fp)) return -1; + if ( msn == 0 ) { + kdWarning(5006) << "writeFolderIds(): Serial number of message at index " + << index << " is zero in folder " << storage.label() << endl; + } } rentry->sync(); diff --git a/kmail/kmmsginfo.cpp b/kmail/kmmsginfo.cpp index 3b1112dc..ece847a3 100644 --- a/kmail/kmmsginfo.cpp +++ b/kmail/kmmsginfo.cpp @@ -18,16 +18,17 @@ class KMMsgInfo::KMMsgInfoPrivate { public: enum { - SUBJECT_SET = 0x01, TO_SET = 0x02, REPLYTO_SET = 0x04, MSGID_SET=0x08, + SUBJECT_SET = 0x01, TOSTRIP_SET = 0x02, REPLYTO_SET = 0x04, MSGID_SET=0x08, DATE_SET = 0x10, OFFSET_SET = 0x20, SIZE_SET = 0x40, SIZESERVER_SET = 0x80, - XMARK_SET=0x100, FROM_SET=0x200, FILE_SET=0x400, ENCRYPTION_SET=0x800, + XMARK_SET=0x100, FROMSTRIP_SET=0x200, FILE_SET=0x400, ENCRYPTION_SET=0x800, SIGNATURE_SET=0x1000, MDN_SET=0x2000, REPLYTOAUX_SET = 0x4000, STRIPPEDSUBJECT_SET = 0x8000, UID_SET = 0x10000, + TO_SET = 0x20000, FROM_SET = 0x40000, ALL_SET = 0xFFFFFF, NONE_SET = 0x000000 }; uint modifiers; - TQString subject, from, to, replyToIdMD5, replyToAuxIdMD5, + TQString subject, fromStrip, toStrip, replyToIdMD5, replyToAuxIdMD5, strippedSubjectMD5, msgIdMD5, xmark, file; off_t folderOffset; size_t msgSize, msgSizeServer; @@ -36,6 +37,7 @@ public: KMMsgSignatureState signatureState; KMMsgMDNSentState mdnSentState; ulong UID; + TQString to, from; KMMsgInfoPrivate() : modifiers(NONE_SET) { } KMMsgInfoPrivate& operator=(const KMMsgInfoPrivate& other) { @@ -48,17 +50,17 @@ public: modifiers |= STRIPPEDSUBJECT_SET; strippedSubjectMD5 = other.strippedSubjectMD5; } - if (other.modifiers & FROM_SET) { - modifiers |= FROM_SET; - from = other.from; + if (other.modifiers & FROMSTRIP_SET) { + modifiers |= FROMSTRIP_SET; + fromStrip = other.fromStrip; } if (other.modifiers & FILE_SET) { modifiers |= FILE_SET; - file = other.from; + file = other.file; } - if (other.modifiers & TO_SET) { - modifiers |= TO_SET; - to = other.to; + if (other.modifiers & TOSTRIP_SET) { + modifiers |= TOSTRIP_SET; + toStrip = other.toStrip; } if (other.modifiers & REPLYTO_SET) { modifiers |= REPLYTO_SET; @@ -109,6 +111,14 @@ public: modifiers |= UID_SET; UID = other.UID; } + if (other.modifiers & TO_SET) { + modifiers |= TO_SET; + to = other.to; + } + if (other.modifiers & FROM_SET) { + modifiers |= FROM_SET; + from = other.from; + } return *this; } }; @@ -157,8 +167,8 @@ KMMsgInfo& KMMsgInfo::operator=(const KMMessage& msg) kd = new KMMsgInfoPrivate; kd->modifiers = KMMsgInfoPrivate::ALL_SET; kd->subject = msg.subject(); - kd->from = msg.fromStrip(); - kd->to = msg.toStrip(); + kd->fromStrip = msg.fromStrip(); + kd->toStrip = msg.toStrip(); kd->replyToIdMD5 = msg.replyToIdMD5(); kd->replyToAuxIdMD5 = msg.replyToAuxIdMD5(); kd->strippedSubjectMD5 = msg.strippedSubjectMD5(); @@ -174,6 +184,8 @@ KMMsgInfo& KMMsgInfo::operator=(const KMMessage& msg) kd->mdnSentState = msg.mdnSentState(); kd->msgSizeServer = msg.msgSizeServer(); kd->UID = msg.UID(); + kd->to = msg.to(); + kd->from = msg.from(); return *this; } @@ -187,8 +199,8 @@ void KMMsgInfo::init(const TQCString& aSubject, const TQCString& aFrom, KMMsgSignatureState signatureState, KMMsgMDNSentState mdnSentState, const TQCString& prefCharset, - off_t aFolderOffset, size_t aMsgSize, - size_t aMsgSizeServer, ulong aUID) + off_t aFolderOffset, size_t aMsgSize, + size_t aMsgSizeServer, ulong aUID) { mIndexOffset = 0; mIndexLength = 0; @@ -196,8 +208,8 @@ void KMMsgInfo::init(const TQCString& aSubject, const TQCString& aFrom, kd = new KMMsgInfoPrivate; kd->modifiers = KMMsgInfoPrivate::ALL_SET; kd->subject = decodeRFC2047String(aSubject, prefCharset); - kd->from = decodeRFC2047String( KMMessage::stripEmailAddr( aFrom ), prefCharset ); - kd->to = decodeRFC2047String( KMMessage::stripEmailAddr( aTo ), prefCharset ); + kd->fromStrip = decodeRFC2047String( KMMessage::stripEmailAddr( aFrom ), prefCharset ); + kd->toStrip = decodeRFC2047String( KMMessage::stripEmailAddr( aTo ), prefCharset ); kd->replyToIdMD5 = base64EncodedMD5( replyToId ); kd->replyToAuxIdMD5 = base64EncodedMD5( replyToAuxId ); kd->strippedSubjectMD5 = base64EncodedMD5( KMMessage::stripOffPrefixes( kd->subject ), true /*utf8*/ ); @@ -213,7 +225,9 @@ void KMMsgInfo::init(const TQCString& aSubject, const TQCString& aFrom, kd->mdnSentState = mdnSentState; kd->msgSizeServer = aMsgSizeServer; kd->UID = aUID; - mDirty = false; + kd->to = aTo; + kd->from = aFrom; + mDirty = false; } void KMMsgInfo::init(const TQCString& aSubject, const TQCString& aFrom, @@ -245,15 +259,23 @@ TQString KMMsgInfo::subject(void) const return getStringPart(MsgSubjectPart); } - //----------------------------------------------------------------------------- TQString KMMsgInfo::fromStrip(void) const +{ + if (kd && kd->modifiers & KMMsgInfoPrivate::FROMSTRIP_SET) + return kd->fromStrip; + return getStringPart(MsgFromStripPart); +} + +//----------------------------------------------------------------------------- +TQString KMMsgInfo::from() const { if (kd && kd->modifiers & KMMsgInfoPrivate::FROM_SET) return kd->from; - return getStringPart(MsgFromPart); + return getStringPart( MsgFromPart ); } + //----------------------------------------------------------------------------- TQString KMMsgInfo::fileName(void) const { @@ -265,10 +287,18 @@ TQString KMMsgInfo::fileName(void) const //----------------------------------------------------------------------------- TQString KMMsgInfo::toStrip(void) const +{ + if (kd && kd->modifiers & KMMsgInfoPrivate::TOSTRIP_SET) + return kd->toStrip; + return getStringPart(MsgToStripPart); +} + +//----------------------------------------------------------------------------- +TQString KMMsgInfo::to() const { if (kd && kd->modifiers & KMMsgInfoPrivate::TO_SET) return kd->to; - return getStringPart(MsgToPart); + return getStringPart( MsgToPart ); } //----------------------------------------------------------------------------- @@ -656,6 +686,24 @@ void KMMsgInfo::setDate(time_t aUnixTime) mDirty = true; } +void KMMsgInfo::setFrom( const TQString &from ) +{ + if ( !kd ) + kd = new KMMsgInfoPrivate; + kd->modifiers |= KMMsgInfoPrivate::FROM_SET; + kd->from = from; + mDirty = true; +} + +void KMMsgInfo::setTo( const TQString &to ) +{ + if ( !kd ) + kd = new KMMsgInfoPrivate; + kd->modifiers |= KMMsgInfoPrivate::TO_SET; + kd->to = to; + mDirty = true; +} + //--- For compatability with old index files void KMMsgInfo::compat_fromOldIndexString(const TQCString& str, bool toUtf8) { @@ -671,8 +719,8 @@ void KMMsgInfo::compat_fromOldIndexString(const TQCString& str, bool toUtf8) mStatus = (KMMsgStatus)str.at(0); if (toUtf8) { kd->subject = str.mid(37, 100).stripWhiteSpace(); - kd->from = str.mid(138, 50).stripWhiteSpace(); - kd->to = str.mid(189, 50).stripWhiteSpace(); + kd->fromStrip = str.mid(138, 50).stripWhiteSpace(); + kd->toStrip = str.mid(189, 50).stripWhiteSpace(); } else { start = offset = str.data() + 37; while (*start == ' ' && start - offset < 100) start++; @@ -680,11 +728,11 @@ void KMMsgInfo::compat_fromOldIndexString(const TQCString& str, bool toUtf8) 100 - (start - offset)), 100 - (start - offset)); start = offset = str.data() + 138; while (*start == ' ' && start - offset < 50) start++; - kd->from = TQString::fromUtf8(str.mid(start - str.data(), + kd->fromStrip = TQString::fromUtf8(str.mid(start - str.data(), 50 - (start - offset)), 50 - (start - offset)); start = offset = str.data() + 189; while (*start == ' ' && start - offset < 50) start++; - kd->to = TQString::fromUtf8(str.mid(start - str.data(), + kd->toStrip = TQString::fromUtf8(str.mid(start - str.data(), 50 - (start - offset)), 50 - (start - offset)); } kd->replyToIdMD5 = str.mid(240, 22).stripWhiteSpace(); diff --git a/kmail/kmmsginfo.h b/kmail/kmmsginfo.h index 04383020..8f557f25 100644 --- a/kmail/kmmsginfo.h +++ b/kmail/kmmsginfo.h @@ -68,7 +68,9 @@ public: /** Inherited methods (see KMMsgBase for description): */ virtual TQString subject(void) const; virtual TQString fromStrip(void) const; + virtual TQString from() const; virtual TQString toStrip(void) const; + virtual TQString to() const; virtual TQString xmark(void) const; virtual TQString replyToIdMD5(void) const; virtual TQString replyToAuxIdMD5() const; @@ -101,6 +103,8 @@ public: virtual void setSignatureState( const KMMsgSignatureState, int idx = -1 ); virtual void setMDNSentState( const KMMsgMDNSentState, int idx = -1 ); virtual void setUID(ulong); + virtual void setFrom( const TQString &from ); + virtual void setTo( const TQString &to ); /** Grr.. c++! */ virtual void setStatus(const char* s1, const char* s2=0) { KMMsgBase::setStatus(s1, s2); } diff --git a/kmail/kmmsgpart.cpp b/kmail/kmmsgpart.cpp index 085e36c0..112236c0 100644 --- a/kmail/kmmsgpart.cpp +++ b/kmail/kmmsgpart.cpp @@ -284,6 +284,9 @@ void KMMessagePart::setBodyEncodedBinary(const TQByteArray& aStr) assert( codec ); // Nice: We can use the convenience function :-) mBody = codec->encode( aStr ); + // QP encoding does CRLF -> LF conversion, which can change the size after decoding again + // and a size mismatch triggers an assert in various other methods + mBodyDecodedSize = -1; break; } default: @@ -585,7 +588,7 @@ TQString KMMessagePart::fileName(void) const const TQCString str = mContentDisposition.mid(startOfFilename, endOfFilename-startOfFilename+1) .stripWhiteSpace(); - return KMMsgBase::decodeRFC2047String(str, charset()); + return KMMsgBase::decodeRFC2047String(str); } return TQString::null; diff --git a/kmail/kmmsgpartdlg.cpp b/kmail/kmmsgpartdlg.cpp index 921c9d55..4a897291 100644 --- a/kmail/kmmsgpartdlg.cpp +++ b/kmail/kmmsgpartdlg.cpp @@ -390,10 +390,7 @@ void KMMsgPartDialogCompat::applyChanges() TQString name = fileName(); if ( !name.isEmpty() || !mMsgPart->name().isEmpty()) { mMsgPart->setName( name ); - TQCString encoding = KMMsgBase::autoDetectCharset( mMsgPart->charset(), - KMMessage::preferredCharsets(), name ); - if ( encoding.isEmpty() ) encoding = "utf-8"; - TQCString encName = KMMsgBase::encodeRFC2231String( name, encoding ); + TQCString encName = KMMsgBase::encodeRFC2231StringAutoDetectCharset( name, mMsgPart->charset() ); cDisp += "\n\tfilename"; if ( name != TQString( encName ) ) diff --git a/kmail/kmpopheaders.cpp b/kmail/kmpopheaders.cpp index 85445abd..2828a49e 100644 --- a/kmail/kmpopheaders.cpp +++ b/kmail/kmpopheaders.cpp @@ -22,8 +22,7 @@ KMPopHeaders::KMPopHeaders() } KMPopHeaders::~KMPopHeaders(){ - if (mHeader) - delete mHeader; + delete mHeader; } /** No descriptions */ diff --git a/kmail/kmreadermainwin.cpp b/kmail/kmreadermainwin.cpp index 8e77ad9d..2f5227cd 100644 --- a/kmail/kmreadermainwin.cpp +++ b/kmail/kmreadermainwin.cpp @@ -140,10 +140,15 @@ void KMReaderMainWin::setUseFixedFont( bool useFixedFont ) } //----------------------------------------------------------------------------- -void KMReaderMainWin::showMsg( const TQString & encoding, KMMessage *msg ) +void KMReaderMainWin::showMsg( const TQString & encoding, KMMessage *msg, + unsigned long serNumOfOriginalMessage, int nodeIdOffset ) { mReaderWin->setOverrideEncoding( encoding ); mReaderWin->setMsg( msg, true ); + if ( serNumOfOriginalMessage != 0 ) { + Q_ASSERT( nodeIdOffset != -1 ); + mReaderWin->setOriginalMsg( serNumOfOriginalMessage, nodeIdOffset ); + } mReaderWin->slotTouchMessage(); setCaption( msg->subject() ); mMsg = msg; @@ -163,6 +168,13 @@ void KMReaderMainWin::slotFolderRemoved( TQObject* folderPtr ) mMsg->setParent( 0 ); } +void KMReaderMainWin::slotReplyOrForwardFinished() +{ + if ( GlobalSettings::self()->closeAfterReplyOrForward() ) { + close(); + } +} + //----------------------------------------------------------------------------- void KMReaderMainWin::slotTrashMsg() { @@ -212,6 +224,7 @@ void KMReaderMainWin::slotMarkAll() void KMReaderMainWin::slotPrintMsg() { KMPrintCommand *command = new KMPrintCommand( this, mReaderWin->message(), + mReaderWin->headerStyle(), mReaderWin->headerStrategy(), mReaderWin->htmlOverride(), mReaderWin->htmlLoadExtOverride(), mReaderWin->isFixedFont(), mReaderWin->overrideEncoding() ); command->setOverrideFont( mReaderWin->cssHelper()->bodyFont( mReaderWin->isFixedFont(), true /*printing*/ ) ); @@ -228,6 +241,8 @@ void KMReaderMainWin::slotForwardInlineMsg() } else { command = new KMForwardInlineCommand( this, mReaderWin->message() ); } + connect( command, TQT_SIGNAL( completed( KMCommand * ) ), + this, TQT_SLOT( slotReplyOrForwardFinished() ) ); command->start(); } @@ -241,6 +256,8 @@ void KMReaderMainWin::slotForwardAttachedMsg() } else { command = new KMForwardAttachedCommand( this, mReaderWin->message() ); } + connect( command, TQT_SIGNAL( completed( KMCommand * ) ), + this, TQT_SLOT( slotReplyOrForwardFinished() ) ); command->start(); } @@ -254,6 +271,8 @@ void KMReaderMainWin::slotForwardDigestMsg() } else { command = new KMForwardDigestCommand( this, mReaderWin->message() ); } + connect( command, TQT_SIGNAL( completed( KMCommand * ) ), + this, TQT_SLOT( slotReplyOrForwardFinished() ) ); command->start(); } @@ -261,6 +280,8 @@ void KMReaderMainWin::slotForwardDigestMsg() void KMReaderMainWin::slotRedirectMsg() { KMCommand *command = new KMRedirectCommand( this, mReaderWin->message() ); + connect( command, TQT_SIGNAL( completed( KMCommand * ) ), + this, TQT_SLOT( slotReplyOrForwardFinished() ) ); command->start(); } @@ -275,10 +296,37 @@ void KMReaderMainWin::slotShowMsgSrc() command->start(); } +//----------------------------------------------------------------------------- +void KMReaderMainWin::setupForwardActions() +{ + disconnect( mForwardActionMenu, TQT_SIGNAL( activated() ), 0, 0 ); + mForwardActionMenu->remove( mForwardInlineAction ); + mForwardActionMenu->remove( mForwardAttachedAction ); + + if ( GlobalSettings::self()->forwardingInlineByDefault() ) { + mForwardActionMenu->insert( mForwardInlineAction, 0 ); + mForwardActionMenu->insert( mForwardAttachedAction, 1 ); + mForwardInlineAction->setShortcut( Key_F ); + mForwardAttachedAction->setShortcut( SHIFT+Key_F ); + connect( mForwardActionMenu, TQT_SIGNAL(activated()), this, + TQT_SLOT(slotForwardInlineMsg()) ); + + } else { + mForwardActionMenu->insert( mForwardAttachedAction, 0 ); + mForwardActionMenu->insert( mForwardInlineAction, 1 ); + mForwardInlineAction->setShortcut( SHIFT+Key_F ); + mForwardAttachedAction->setShortcut( Key_F ); + connect( mForwardActionMenu, TQT_SIGNAL(activated()), this, + TQT_SLOT(slotForwardAttachedMsg()) ); + } +} + //----------------------------------------------------------------------------- void KMReaderMainWin::slotConfigChanged() { //readConfig(); + setupForwardActions(); + setupForwardingActionsList(); } void KMReaderMainWin::setupAccel() @@ -288,6 +336,9 @@ void KMReaderMainWin::setupAccel() mMsgActions = new KMail::MessageActions( actionCollection(), this ); mMsgActions->setMessageView( mReaderWin ); + connect( mMsgActions, TQT_SIGNAL( replyActionFinished() ), + this, TQT_SLOT( slotReplyOrForwardFinished() ) ); + //----- File Menu //mOpenAction = KStdAction::open( this, TQT_SLOT( slotOpenMsg() ), // actionCollection() ); @@ -351,21 +402,7 @@ void KMReaderMainWin::setupAccel() actionCollection(), "message_forward_redirect" ); - if ( GlobalSettings::self()->forwardingInlineByDefault() ) { - mForwardActionMenu->insert( mForwardInlineAction ); - mForwardActionMenu->insert( mForwardAttachedAction ); - mForwardInlineAction->setShortcut( Key_F ); - mForwardAttachedAction->setShortcut( SHIFT+Key_F ); - connect( mForwardActionMenu, TQT_SIGNAL(activated()), this, - TQT_SLOT(slotForwardInlineMsg()) ); - } else { - mForwardActionMenu->insert( mForwardAttachedAction ); - mForwardActionMenu->insert( mForwardInlineAction ); - mForwardInlineAction->setShortcut( SHIFT+Key_F ); - mForwardAttachedAction->setShortcut( Key_F ); - connect( mForwardActionMenu, TQT_SIGNAL(activated()), this, - TQT_SLOT(slotForwardAttachedMsg()) ); - } + setupForwardActions(); mForwardActionMenu->insert( mForwardDigestAction ); mForwardActionMenu->insert( mRedirectAction ); @@ -408,7 +445,7 @@ void KMReaderMainWin::slotMsgPopup(KMMessage &aMsg, const KURL &aUrl, const TQPo mUrl = aUrl; mMsg = &aMsg; bool urlMenuAdded=false; - + bool copyAdded = false; if (!aUrl.isEmpty()) { if (aUrl.protocol() == "mailto") { @@ -421,7 +458,8 @@ void KMReaderMainWin::slotMsgPopup(KMMessage &aMsg, const KURL &aUrl, const TQPo } mReaderWin->addAddrBookAction()->plug( menu ); mReaderWin->openAddrBookAction()->plug( menu ); - mReaderWin->copyAction()->plug( menu ); + mReaderWin->copyURLAction()->plug( menu ); + copyAdded = true; } else { // popup on a not-mailto URL mReaderWin->urlOpenAction()->plug( menu ); @@ -436,8 +474,8 @@ void KMReaderMainWin::slotMsgPopup(KMMessage &aMsg, const KURL &aUrl, const TQPo menu->insertSeparator(); mMsgActions->replyMenu()->plug( menu ); menu->insertSeparator(); - - mReaderWin->copyAction()->plug( menu ); + if( !copyAdded ) + mReaderWin->copyAction()->plug( menu ); mReaderWin->selectAllAction()->plug( menu ); } else if ( !urlMenuAdded ) { diff --git a/kmail/kmreadermainwin.h b/kmail/kmreadermainwin.h index ba458eb0..a4d8e972 100644 --- a/kmail/kmreadermainwin.h +++ b/kmail/kmreadermainwin.h @@ -36,8 +36,16 @@ public: void setUseFixedFont( bool useFixedFont ); - // take ownership of and show @param msg - void showMsg( const TQString & encoding, KMMessage *msg ); + /** + * take ownership of and show @param msg + * + * The last two paramters, serNumOfOriginalMessage and nodeIdOffset, are needed when @p msg + * is derived from another message, e.g. the user views an encapsulated message in this window. + * Then, the reader needs to know about that original message, so those to paramters are passed + * onto setOriginalMsg() of KMReaderWin. + */ + void showMsg( const TQString & encoding, KMMessage *msg, + unsigned long serNumOfOriginalMessage = 0, int nodeIdOffset = -1 ); /** * Sets up action list for forward menu. @@ -70,10 +78,19 @@ private slots: void slotFolderRemoved( TQObject* folderPtr ); + /// This closes the window if the setting to close the window after replying or + /// forwarding is set. + void slotReplyOrForwardFinished(); + private: void initKMReaderMainWin(); void setupAccel(); + /** + * @see the KMMainWidget function with the same name. + */ + void setupForwardActions(); + KMReaderWin *mReaderWin; KMMessage *mMsg; KURL mUrl; diff --git a/kmail/kmreaderwin.cpp b/kmail/kmreaderwin.cpp index b5ee4105..21be5a44 100644 --- a/kmail/kmreaderwin.cpp +++ b/kmail/kmreaderwin.cpp @@ -52,6 +52,7 @@ using KMail::ISubject; using KMail::URLHandlerManager; #include "interfaces/observable.h" #include "util.h" +#include "kmheaders.h" #include "broadcaststatus.h" @@ -86,7 +87,7 @@ using KMail::TeeHtmlWriter; #include #include #include - +#include #include // for the click on attachment stuff (dnaber): @@ -208,81 +209,33 @@ void KMReaderWin::objectTreeToDecryptedMsg( partNode* node, kdDebug(5006) << TQString("-------------------------------------------------" ) << endl; kdDebug(5006) << TQString("KMReaderWin::objectTreeToDecryptedMsg( %1 ) START").arg( recCount ) << endl; if( node ) { + + kdDebug(5006) << node->typeString() << '/' << node->subTypeString() << endl; + partNode* curNode = node; partNode* dataNode = curNode; partNode * child = node->firstChild(); - bool bIsMultipart = false; + const bool bIsMultipart = node->type() == DwMime::kTypeMultipart ; + bool bKeepPartAsIs = false; switch( curNode->type() ){ - case DwMime::kTypeText: { -kdDebug(5006) << "* text *" << endl; - switch( curNode->subType() ){ - case DwMime::kSubtypeHtml: -kdDebug(5006) << "html" << endl; - break; - case DwMime::kSubtypeXVCard: -kdDebug(5006) << "v-card" << endl; - break; - case DwMime::kSubtypeRichtext: -kdDebug(5006) << "rich text" << endl; - break; - case DwMime::kSubtypeEnriched: -kdDebug(5006) << "enriched " << endl; - break; - case DwMime::kSubtypePlain: -kdDebug(5006) << "plain " << endl; - break; - default: -kdDebug(5006) << "default " << endl; - break; - } - } - break; case DwMime::kTypeMultipart: { -kdDebug(5006) << "* multipart *" << endl; - bIsMultipart = true; switch( curNode->subType() ){ - case DwMime::kSubtypeMixed: -kdDebug(5006) << "mixed" << endl; - break; - case DwMime::kSubtypeAlternative: -kdDebug(5006) << "alternative" << endl; - break; - case DwMime::kSubtypeDigest: -kdDebug(5006) << "digest" << endl; - break; - case DwMime::kSubtypeParallel: -kdDebug(5006) << "parallel" << endl; - break; - case DwMime::kSubtypeSigned: -kdDebug(5006) << "signed" << endl; + case DwMime::kSubtypeSigned: { + bKeepPartAsIs = true; + } break; case DwMime::kSubtypeEncrypted: { -kdDebug(5006) << "encrypted" << endl; - if ( child ) { - /* - ATTENTION: This code is to be replaced by the new 'auto-detect' feature. -------------------------------------- - */ - partNode* data = - child->findType( DwMime::kTypeApplication, DwMime::kSubtypeOctetStream, false, true ); - if ( !data ) - data = child->findType( DwMime::kTypeApplication, DwMime::kSubtypePkcs7Mime, false, true ); - if ( data && data->firstChild() ) - dataNode = data; - } + if ( child ) + dataNode = child; } break; - default : -kdDebug(5006) << "( unknown subtype )" << endl; - break; } } break; case DwMime::kTypeMessage: { -kdDebug(5006) << "* message *" << endl; switch( curNode->subType() ){ case DwMime::kSubtypeRfc822: { -kdDebug(5006) << "RfC 822" << endl; if ( child ) dataNode = child; } @@ -291,25 +244,19 @@ kdDebug(5006) << "RfC 822" << endl; } break; case DwMime::kTypeApplication: { -kdDebug(5006) << "* application *" << endl; switch( curNode->subType() ){ - case DwMime::kSubtypePostscript: -kdDebug(5006) << "postscript" << endl; - break; case DwMime::kSubtypeOctetStream: { -kdDebug(5006) << "octet stream" << endl; if ( child ) dataNode = child; } break; - case DwMime::kSubtypePgpEncrypted: -kdDebug(5006) << "pgp encrypted" << endl; - break; - case DwMime::kSubtypePgpSignature: -kdDebug(5006) << "pgp signed" << endl; + case DwMime::kSubtypePkcs7Signature: { + // note: subtype Pkcs7Signature specifies a signature part + // which we do NOT want to remove! + bKeepPartAsIs = true; + } break; case DwMime::kSubtypePkcs7Mime: { -kdDebug(5006) << "pkcs7 mime" << endl; // note: subtype Pkcs7Mime can also be signed // and we do NOT want to remove the signature! if ( child && curNode->encryptionState() != KMMsgNotEncrypted ) @@ -319,39 +266,6 @@ kdDebug(5006) << "pkcs7 mime" << endl; } } break; - case DwMime::kTypeImage: { -kdDebug(5006) << "* image *" << endl; - switch( curNode->subType() ){ - case DwMime::kSubtypeJpeg: -kdDebug(5006) << "JPEG" << endl; - break; - case DwMime::kSubtypeGif: -kdDebug(5006) << "GIF" << endl; - break; - } - } - break; - case DwMime::kTypeAudio: { -kdDebug(5006) << "* audio *" << endl; - switch( curNode->subType() ){ - case DwMime::kSubtypeBasic: -kdDebug(5006) << "basic" << endl; - break; - } - } - break; - case DwMime::kTypeVideo: { -kdDebug(5006) << "* video *" << endl; - switch( curNode->subType() ){ - case DwMime::kSubtypeMpeg: -kdDebug(5006) << "mpeg" << endl; - break; - } - } - break; - case DwMime::kTypeModel: -kdDebug(5006) << "* model *" << endl; - break; } @@ -389,6 +303,10 @@ kdDebug(5006) << " new Content-Type = " << headers->ContentType( } } + if ( bKeepPartAsIs ) { + resultingData += dataNode->encodedBody(); + } else { + // B) Store the body of this part. if( headers && bIsMultipart && dataNode->firstChild() ) { kdDebug(5006) << "is valid Multipart, processing children:" << endl; @@ -424,6 +342,7 @@ kdDebug(5006) << "Multipart processing children - DONE" << endl; kdDebug(5006) << "is Simple part or invalid Multipart, storing body data .. DONE" << endl; resultingData += part->Body().AsString().c_str(); } + } } else { kdDebug(5006) << "dataNode != curNode: Replace curNode by dataNode." << endl; bool rootNodeReplaceFlag = weAreReplacingTheRootNode || !curNode->parentNode(); @@ -488,6 +407,8 @@ KMReaderWin::KMReaderWin(TQWidget *aParent, const char *aName, int aFlags ) : TQWidget(aParent, aName, aFlags | Qt::WDestructiveClose), + mSerNumOfOriginalMessage( 0 ), + mNodeIdOffset( -1 ), mAttachmentStrategy( 0 ), mHeaderStrategy( 0 ), mHeaderStyle( 0 ), @@ -511,14 +432,18 @@ KMReaderWin::KMReaderWin(TQWidget *aParent, mAddBookmarksAction( 0 ), mStartIMChatAction( 0 ), mSelectAllAction( 0 ), + mHeaderOnlyAttachmentsAction( 0 ), mSelectEncodingAction( 0 ), mToggleFixFontAction( 0 ), + mCanStartDrag( false ), mHtmlWriter( 0 ), mSavedRelativePosition( 0 ), mDecrytMessageOverwrite( false ), mShowSignatureDetails( false ), - mShowAttachmentQuicklist( true ) + mShowAttachmentQuicklist( true ), + mShowRawToltecMail( false ) { + mExternalWindow = (aParent == mainWindow ); mSplitterSizes << 180 << 100; mMimeTreeMode = 1; mMimeTreeAtBottom = true; @@ -526,7 +451,6 @@ KMReaderWin::KMReaderWin(TQWidget *aParent, mLastSerNum = 0; mWaitingForSerNum = 0; mMessage = 0; - mLastStatus = KMMsgStatusUnknown; mMsgDisplay = true; mPrinting = false; mShowColorbar = false; @@ -642,6 +566,13 @@ void KMReaderWin::createActions( KActionCollection * ac ) { raction->setExclusiveGroup( "view_attachments_group" ); attachmentMenu->insert( raction ); + mHeaderOnlyAttachmentsAction = new KRadioAction( i18n( "View->attachments->", "In Header &Only" ), 0, + this, TQT_SLOT( slotHeaderOnlyAttachments() ), + ac, "view_attachments_headeronly" ); + mHeaderOnlyAttachmentsAction->setToolTip( i18n( "Show Attachments only in the header of the mail" ) ); + mHeaderOnlyAttachmentsAction->setExclusiveGroup( "view_attachments_group" ); + attachmentMenu->insert( mHeaderOnlyAttachmentsAction ); + // Set Encoding submenu mSelectEncodingAction = new KSelectAction( i18n( "&Set Encoding" ), "charset", 0, this, TQT_SLOT( slotSetEncoding() ), @@ -725,6 +656,8 @@ KRadioAction *KMReaderWin::actionForAttachmentStrategy( const AttachmentStrategy actionName = "view_attachments_inline"; else if ( as == AttachmentStrategy::hidden() ) actionName = "view_attachments_hide"; + else if ( as == AttachmentStrategy::headerOnly() ) + actionName = "view_attachments_headeronly"; if ( actionName ) return static_cast(mActionCollection->action(actionName)); @@ -735,37 +668,46 @@ KRadioAction *KMReaderWin::actionForAttachmentStrategy( const AttachmentStrategy void KMReaderWin::slotEnterpriseHeaders() { setHeaderStyleAndStrategy( HeaderStyle::enterprise(), HeaderStrategy::rich() ); + if( !mExternalWindow ) + writeConfig(); } void KMReaderWin::slotFancyHeaders() { setHeaderStyleAndStrategy( HeaderStyle::fancy(), HeaderStrategy::rich() ); + if( !mExternalWindow ) + writeConfig(); } void KMReaderWin::slotBriefHeaders() { setHeaderStyleAndStrategy( HeaderStyle::brief(), HeaderStrategy::brief() ); + if( !mExternalWindow ) + writeConfig(); } void KMReaderWin::slotStandardHeaders() { setHeaderStyleAndStrategy( HeaderStyle::plain(), HeaderStrategy::standard()); + writeConfig(); } void KMReaderWin::slotLongHeaders() { setHeaderStyleAndStrategy( HeaderStyle::plain(), HeaderStrategy::rich() ); + if( !mExternalWindow ) + writeConfig(); } void KMReaderWin::slotAllHeaders() { setHeaderStyleAndStrategy( HeaderStyle::plain(), HeaderStrategy::all() ); + if( !mExternalWindow ) + writeConfig(); } void KMReaderWin::slotLevelQuote( int l ) { - kdDebug( 5006 ) << "Old Level: " << mLevelQuote << " New Level: " << l << endl; - mLevelQuote = l; saveRelativePosition(); update(true); @@ -820,6 +762,10 @@ void KMReaderWin::slotHideAttachments() { setAttachmentStrategy( AttachmentStrategy::hidden() ); } +void KMReaderWin::slotHeaderOnlyAttachments() { + setAttachmentStrategy( AttachmentStrategy::headerOnly() ); +} + void KMReaderWin::slotCycleAttachmentStrategy() { setAttachmentStrategy( attachmentStrategy()->next() ); KRadioAction * action = actionForAttachmentStrategy( attachmentStrategy() ); @@ -831,6 +777,7 @@ void KMReaderWin::slotCycleAttachmentStrategy() { //----------------------------------------------------------------------------- KMReaderWin::~KMReaderWin() { + clearBodyPartMementos(); delete mHtmlWriter; mHtmlWriter = 0; delete mCSSHelper; if (mAutoDelete) delete message(); @@ -846,7 +793,7 @@ void KMReaderWin::slotMessageArrived( KMMessage *msg ) if ( msg->getMsgSerNum() == mWaitingForSerNum ) { setMsg( msg, true ); } else { - kdDebug( 5006 ) << "KMReaderWin::slotMessageArrived - ignoring update" << endl; + //kdDebug( 5006 ) << "KMReaderWin::slotMessageArrived - ignoring update" << endl; } } } @@ -856,7 +803,7 @@ void KMReaderWin::update( KMail::Interface::Observable * observable ) { if ( !mAtmUpdate ) { // reparse the msg - kdDebug(5006) << "KMReaderWin::update - message" << endl; + //kdDebug(5006) << "KMReaderWin::update - message" << endl; updateReaderWin(); return; } @@ -1067,8 +1014,6 @@ void KMReaderWin::initHtmlWidget(void) connect(mViewer->browserExtension(), TQT_SIGNAL(createNewWindow(const KURL &, const KParts::URLArgs &)),this, TQT_SLOT(slotUrlOpen(const KURL &))); - connect(mViewer,TQT_SIGNAL(onURL(const TQString &)),this, - TQT_SLOT(slotUrlOn(const TQString &))); connect(mViewer,TQT_SIGNAL(popupMenu(const TQString &, const TQPoint &)), TQT_SLOT(slotUrlPopup(const TQString &, const TQPoint &))); connect( kmkernel->imProxy(), TQT_SIGNAL( sigContactPresenceChanged( const TQString & ) ), @@ -1105,6 +1050,16 @@ void KMReaderWin::setHeaderStyleAndStrategy( const HeaderStyle * style, const HeaderStrategy * strategy ) { mHeaderStyle = style ? style : HeaderStyle::fancy(); mHeaderStrategy = strategy ? strategy : HeaderStrategy::rich(); + if ( mHeaderOnlyAttachmentsAction ) { + const bool styleHasAttachmentQuickList = mHeaderStyle == HeaderStyle::fancy() || + mHeaderStyle == HeaderStyle::enterprise(); + mHeaderOnlyAttachmentsAction->setEnabled( styleHasAttachmentQuickList ); + if ( !styleHasAttachmentQuickList && mAttachmentStrategy == AttachmentStrategy::headerOnly() ) { + // Style changed to something without an attachment quick list, need to change attachment + // strategy + setAttachmentStrategy( AttachmentStrategy::smart() ); + } + } update( true ); } @@ -1150,7 +1105,6 @@ void KMReaderWin::setPrintFont( const TQFont& font ) //----------------------------------------------------------------------------- const TQTextCodec * KMReaderWin::overrideCodec() const { - kdDebug(5006) << k_funcinfo << " mOverrideEncoding == '" << mOverrideEncoding << "'" << endl; if ( mOverrideEncoding.isEmpty() || mOverrideEncoding == "Auto" ) // Auto return 0; else @@ -1179,15 +1133,25 @@ void KMReaderWin::readGlobalOverrideCodec() } //----------------------------------------------------------------------------- -void KMReaderWin::setMsg(KMMessage* aMsg, bool force) +void KMReaderWin::setOriginalMsg( unsigned long serNumOfOriginalMessage, int nodeIdOffset ) { - if (aMsg) - kdDebug(5006) << "(" << aMsg->getMsgSerNum() << ", last " << mLastSerNum << ") " << aMsg->subject() << " " - << aMsg->fromStrip() << ", readyToShow " << (aMsg->readyToShow()) << endl; + mSerNumOfOriginalMessage = serNumOfOriginalMessage; + mNodeIdOffset = nodeIdOffset; +} - //Reset the level quote if the msg has changed. - if (aMsg && aMsg->getMsgSerNum() != mLastSerNum ){ +//----------------------------------------------------------------------------- +void KMReaderWin::setMsg( KMMessage* aMsg, bool force, bool updateOnly ) +{ + if ( aMsg ) { + kdDebug(5006) << "(" << aMsg->getMsgSerNum() << ", last " << mLastSerNum << ") " << aMsg->subject() << " " + << aMsg->fromStrip() << ", readyToShow " << (aMsg->readyToShow()) << endl; + } + + // Reset message-transient state + if ( aMsg && aMsg->getMsgSerNum() != mLastSerNum && !updateOnly ){ mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin()-1; + mShowRawToltecMail = !GlobalSettings::self()->showToltecReplacementText(); + clearBodyPartMementos(); } if ( mPrinting ) mLevelQuote = -1; @@ -1234,14 +1198,11 @@ void KMReaderWin::setMsg(KMMessage* aMsg, bool force) if (aMsg) { aMsg->setOverrideCodec( overrideCodec() ); aMsg->setDecodeHTML( htmlMail() ); - mLastStatus = aMsg->status(); // FIXME: workaround to disable DND for IMAP load-on-demand if ( !aMsg->isComplete() ) mViewer->setDNDEnabled( false ); else mViewer->setDNDEnabled( true ); - } else { - mLastStatus = KMMsgStatusUnknown; } // only display the msg if it is complete @@ -1522,17 +1483,16 @@ void KMReaderWin::displayMessage() { TQTimer::singleShot( 1, this, TQT_SLOT(injectAttachments()) ); } +static bool message_was_saved_decrypted_before( const KMMessage * msg ) { + if ( !msg ) + return false; + //kdDebug(5006) << "msgId = " << msg->msgId() << endl; + return msg->msgId().stripWhiteSpace().startsWith( "typeString() + '/' + mRootNode->subTypeString(); TQString cntDesc = aMsg->subject(); @@ -1573,24 +1533,38 @@ void KMReaderWin::parseMsg(KMMessage* aMsg) if( vCardNode ) { // ### FIXME: We should only do this if the vCard belongs to the sender, // ### i.e. if the sender's email address is contained in the vCard. - const TQString vcard = vCardNode->msgPart().bodyToUnicode( overrideCodec() ); KABC::VCardConverter t; +#if defined(KABC_VCARD_ENCODING_FIX) + const TQByteArray vcard = vCardNode->msgPart().bodyDecodedBinary(); + if ( !t.parseVCardsRaw( vcard.data() ).empty() ) { +#else + const TQString vcard = vCardNode->msgPart().bodyToUnicode( overrideCodec() ); if ( !t.parseVCards( vcard ).empty() ) { +#endif hasVCard = true; - kdDebug(5006) << "FOUND A VALID VCARD" << endl; writeMessagePartToTempFile( &vCardNode->msgPart(), vCardNode->nodeId() ); } } - htmlWriter()->queue( writeMsgHeader(aMsg, hasVCard, true ) ); + + if ( !mRootNode || !mRootNode->isToltecMessage() || mShowRawToltecMail ) { + htmlWriter()->queue( writeMsgHeader(aMsg, hasVCard ? vCardNode : 0, true ) ); + } // show message content ObjectTreeParser otp( this ); + otp.setAllowAsync( true ); + otp.setShowRawToltecMail( mShowRawToltecMail ); otp.parseObjectTree( mRootNode ); // store encrypted/signed status information in the KMMessage // - this can only be done *after* calling parseObjectTree() KMMsgEncryptionState encryptionState = mRootNode->overallEncryptionState(); KMMsgSignatureState signatureState = mRootNode->overallSignatureState(); + // Don't crash when switching message while GPG passphrase entry dialog is shown #53185 + if (aMsg != message()) { + displayMessage(); + return; + } aMsg->setEncryptionState( encryptionState ); // Don't reset the signature state to "not signed" (e.g. if one canceled the // decryption of a signed messages which has already been decrypted before). @@ -1618,22 +1592,23 @@ void KMReaderWin::parseMsg(KMMessage* aMsg) kdDebug(5006) << "\n\n\nKMReaderWin::parseMsg() - special post-encryption handling:\n1." << endl; kdDebug(5006) << "(aMsg == msg) = " << (aMsg == message()) << endl; -kdDebug(5006) << " (KMMsgStatusUnknown == mLastStatus) = " << (KMMsgStatusUnknown == mLastStatus) << endl; -kdDebug(5006) << "|| (KMMsgStatusNew == mLastStatus) = " << (KMMsgStatusNew == mLastStatus) << endl; -kdDebug(5006) << "|| (KMMsgStatusUnread == mLastStatus) = " << (KMMsgStatusUnread == mLastStatus) << endl; -kdDebug(5006) << "(mIdOfLastViewedMessage != aMsg->msgId()) = " << (mIdOfLastViewedMessage != aMsg->msgId()) << endl; +kdDebug(5006) << "aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder() = " << (aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder()) << endl; +kdDebug(5006) << "message_was_saved_decrypted_before( aMsg ) = " << message_was_saved_decrypted_before( aMsg ) << endl; +kdDebug(5006) << "this->decryptMessage() = " << decryptMessage() << endl; +kdDebug(5006) << "otp.hasPendingAsyncJobs() = " << otp.hasPendingAsyncJobs() << endl; kdDebug(5006) << " (KMMsgFullyEncrypted == encryptionState) = " << (KMMsgFullyEncrypted == encryptionState) << endl; kdDebug(5006) << "|| (KMMsgPartiallyEncrypted == encryptionState) = " << (KMMsgPartiallyEncrypted == encryptionState) << endl; // only proceed if we were called the normal way - not by // double click on the message (==not running in a separate window) if( (aMsg == message()) + // don't remove encryption in the outbox folder :) + && ( aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder() ) // only proceed if this message was not saved encryptedly before - // to make sure only *new* messages are saved in decrypted form - && ( (KMMsgStatusUnknown == mLastStatus) - || (KMMsgStatusNew == mLastStatus) - || (KMMsgStatusUnread == mLastStatus) ) - // avoid endless recursions - && (mIdOfLastViewedMessage != aMsg->msgId()) + && !message_was_saved_decrypted_before( aMsg ) + // only proceed if the message has actually been decrypted + && decryptMessage() + // only proceed if no pending async jobs are running: + && !otp.hasPendingAsyncJobs() // only proceed if this message is (at least partially) encrypted && ( (KMMsgFullyEncrypted == encryptionState) || (KMMsgPartiallyEncrypted == encryptionState) ) ) { @@ -1690,15 +1665,15 @@ kdDebug(5006) << "KMReaderWin - composing unencrypted message" << endl; //----------------------------------------------------------------------------- -TQString KMReaderWin::writeMsgHeader(KMMessage* aMsg, bool hasVCard, bool topLevel) +TQString KMReaderWin::writeMsgHeader( KMMessage* aMsg, partNode *vCardNode, bool topLevel ) { kdFatal( !headerStyle(), 5006 ) << "trying to writeMsgHeader() without a header style set!" << endl; kdFatal( !headerStrategy(), 5006 ) << "trying to writeMsgHeader() without a header strategy set!" << endl; TQString href; - if (hasVCard) - href = TQString("file:") + KURL::encode_string( mTempFiles.last() ); + if ( vCardNode ) + href = vCardNode->asHREF( "body" ); return headerStyle()->format( aMsg, headerStrategy(), href, mPrinting, topLevel ); } @@ -1763,10 +1738,14 @@ TQString KMReaderWin::createTempDir( const TQString ¶m ) } //----------------------------------------------------------------------------- -void KMReaderWin::showVCard( KMMessagePart * msgPart ) { +void KMReaderWin::showVCard( KMMessagePart *msgPart ) +{ +#if defined(KABC_VCARD_ENCODING_FIX) + const TQByteArray vCard = msgPart->bodyDecodedBinary(); +#else const TQString vCard = msgPart->bodyToUnicode( overrideCodec() ); - - VCardViewer *vcv = new VCardViewer(this, vCard, "vCardDialog"); +#endif + VCardViewer *vcv = new VCardViewer( this, vCard, "vCardDialog" ); vcv->show(); } @@ -1782,19 +1761,13 @@ void KMReaderWin::printMsg() int KMReaderWin::msgPartFromUrl(const KURL &aUrl) { if (aUrl.isEmpty()) return -1; - - bool ok; - if ( aUrl.url().startsWith( "#att" ) ) { - int res = aUrl.url().mid( 4 ).toInt( &ok ); - if ( ok ) return res; - } - if (!aUrl.isLocalFile()) return -1; TQString path = aUrl.path(); uint right = path.findRev('/'); uint left = path.findRev('.', right); + bool ok; int res = path.mid(left + 1, right - left - 1).toInt(&ok); return (ok) ? res : -1; } @@ -1910,7 +1883,8 @@ bool foundSMIMEData( const TQString aUrl, void KMReaderWin::slotUrlOn(const TQString &aUrl) { const KURL url(aUrl); - if ( url.protocol() == "kmail" || url.protocol() == "x-kmail" + + if ( url.protocol() == "kmail" || url.protocol() == "x-kmail" || url.protocol() == "attachment" || (url.protocol().isEmpty() && url.path().isEmpty()) ) { mViewer->setDNDEnabled( false ); } else { @@ -1919,10 +1893,12 @@ void KMReaderWin::slotUrlOn(const TQString &aUrl) if ( aUrl.stripWhiteSpace().isEmpty() ) { KPIM::BroadcastStatus::instance()->reset(); + mHoveredUrl = KURL(); + mLastClickImagePath = TQString(); return; } - mUrlClicked = url; + mHoveredUrl = url; const TQString msg = URLHandlerManager::instance()->statusBarMessage( url, this ); @@ -1934,7 +1910,7 @@ void KMReaderWin::slotUrlOn(const TQString &aUrl) //----------------------------------------------------------------------------- void KMReaderWin::slotUrlOpen(const KURL &aUrl, const KParts::URLArgs &) { - mUrlClicked = aUrl; + mClickedUrl = aUrl; if ( URLHandlerManager::instance()->handleClick( aUrl, this ) ) return; @@ -1947,17 +1923,43 @@ void KMReaderWin::slotUrlOpen(const KURL &aUrl, const KParts::URLArgs &) void KMReaderWin::slotUrlPopup(const TQString &aUrl, const TQPoint& aPos) { const KURL url( aUrl ); - mUrlClicked = url; + mClickedUrl = url; + + if ( url.protocol() == "mailto" ) { + mCopyURLAction->setText( i18n( "Copy Email Address" ) ); + } else { + mCopyURLAction->setText( i18n( "Copy Link Address" ) ); + } if ( URLHandlerManager::instance()->handleContextMenuRequest( url, aPos, this ) ) return; if ( message() ) { kdWarning( 5006 ) << "KMReaderWin::slotUrlPopup(): Unhandled URL right-click!" << endl; - emit popupMenu( *message(), url, aPos ); + emitPopupMenu( url, aPos ); } } +// Checks if the given node has a parent node that is a DIV which has an ID attribute +// with the value specified here +static bool hasParentDivWithId( const DOM::Node &start, const TQString &id ) +{ + if ( start.isNull() ) + return false; + + if ( start.nodeName().string() == "div" ) { + for ( unsigned int i = 0; i < start.attributes().length(); i++ ) { + if ( start.attributes().item( i ).nodeName().string() == "id" && + start.attributes().item( i ).nodeValue().string() == id ) + return true; + } + } + + if ( !start.parentNode().isNull() ) + return hasParentDivWithId( start.parentNode(), id ); + else return false; +} + //----------------------------------------------------------------------------- void KMReaderWin::showAttachmentPopup( int id, const TQString & name, const TQPoint & p ) { @@ -1969,14 +1971,22 @@ void KMReaderWin::showAttachmentPopup( int id, const TQString & name, const TQPo menu->insertItem(i18n("to view something", "View"), 3); menu->insertItem(SmallIcon("filesaveas"),i18n("Save As..."), 4); menu->insertItem(SmallIcon("editcopy"), i18n("Copy"), 9 ); - if ( GlobalSettings::self()->allowAttachmentEditing() ) + const bool canChange = message()->parent() ? !message()->parent()->isReadOnly() : false; + if ( GlobalSettings::self()->allowAttachmentEditing() && canChange ) menu->insertItem(SmallIcon("edit"), i18n("Edit Attachment"), 8 ); - if ( GlobalSettings::self()->allowAttachmentDeletion() ) + if ( GlobalSettings::self()->allowAttachmentDeletion() && canChange ) menu->insertItem(SmallIcon("editdelete"), i18n("Delete Attachment"), 7 ); if ( name.endsWith( ".xia", false ) && Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" ) ) menu->insertItem( i18n( "Decrypt With Chiasmus..." ), 6 ); menu->insertItem(i18n("Properties"), 5); + + const bool attachmentInHeader = hasParentDivWithId( mViewer->nodeUnderMouse(), "attachmentInjectionPoint" ); + const bool hasScrollbar = mViewer->view()->verticalScrollBar()->isVisible(); + if ( attachmentInHeader && hasScrollbar ) { + menu->insertItem( i18n("Scroll To"), 10 ); + } + connect(menu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotHandleAttachment(int))); menu->exec( p ,0 ); delete menu; @@ -2032,6 +2042,8 @@ void KMReaderWin::slotHandleAttachment( int choice ) urls.append( url ); KURLDrag* drag = new KURLDrag( urls, this ); TQApplication::clipboard()->setData( drag, QClipboard::Clipboard ); + } else if ( choice == 10 ) { // Scroll To + scrollToAttachment( node ); } } @@ -2064,7 +2076,7 @@ void KMReaderWin::slotCopySelectedText() //----------------------------------------------------------------------------- -void KMReaderWin::atmViewMsg(KMMessagePart* aMsgPart) +void KMReaderWin::atmViewMsg( KMMessagePart* aMsgPart, int nodeId ) { assert(aMsgPart!=0); KMMessage* msg = new KMMessage; @@ -2076,7 +2088,7 @@ void KMReaderWin::atmViewMsg(KMMessagePart* aMsgPart) msg->setUID(message()->UID()); msg->setReadyToShow(true); KMReaderMainWin *win = new KMReaderMainWin(); - win->showMsg( overrideEncoding(), msg ); + win->showMsg( overrideEncoding(), msg, message()->getMsgSerNum(), nodeId ); win->show(); } @@ -2168,6 +2180,7 @@ void KMReaderWin::setMsgPart( KMMessagePart* aMsgPart, bool aHTML, htmlWriter()->end(); setCaption( i18n("View Attachment: %1").arg( pname ) ); show(); + delete iio; } else { htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) ); @@ -2208,7 +2221,7 @@ void KMReaderWin::slotAtmView( int id, const TQString& name ) if (pname.isEmpty()) pname="unnamed"; // image Attachment is saved already if (kasciistricmp(msgPart.typeStr(), "message")==0) { - atmViewMsg(&msgPart); + atmViewMsg( &msgPart,id ); } else if ((kasciistricmp(msgPart.typeStr(), "text")==0) && (kasciistricmp(msgPart.subtypeStr(), "x-vcard")==0)) { setMsgPart( &msgPart, htmlMail(), name, pname ); @@ -2239,7 +2252,7 @@ void KMReaderWin::openAttachment( int id, const TQString & name ) KMMessagePart& msgPart = node->msgPart(); if (kasciistricmp(msgPart.typeStr(), "message")==0) { - atmViewMsg(&msgPart); + atmViewMsg( &msgPart, id ); return; } @@ -2431,7 +2444,7 @@ void KMReaderWin::update( bool force ) { KMMessage* msg = message(); if ( msg ) - setMsg( msg, force ); + setMsg( msg, force, true /* updateOnly */ ); } @@ -2467,7 +2480,7 @@ void KMReaderWin::slotUrlClicked() identity = message()->parent()->identity(); } - KMCommand *command = new KMUrlClickedCommand( mUrlClicked, identity, this, + KMCommand *command = new KMUrlClickedCommand( mClickedUrl, identity, this, false, mainWidget ); command->start(); } @@ -2475,14 +2488,14 @@ void KMReaderWin::slotUrlClicked() //----------------------------------------------------------------------------- void KMReaderWin::slotMailtoCompose() { - KMCommand *command = new KMMailtoComposeCommand( mUrlClicked, message() ); + KMCommand *command = new KMMailtoComposeCommand( mClickedUrl, message() ); command->start(); } //----------------------------------------------------------------------------- void KMReaderWin::slotMailtoForward() { - KMCommand *command = new KMMailtoForwardCommand( mMainWindow, mUrlClicked, + KMCommand *command = new KMMailtoForwardCommand( mMainWindow, mClickedUrl, message() ); command->start(); } @@ -2490,7 +2503,7 @@ void KMReaderWin::slotMailtoForward() //----------------------------------------------------------------------------- void KMReaderWin::slotMailtoAddAddrBook() { - KMCommand *command = new KMMailtoAddAddrBookCommand( mUrlClicked, + KMCommand *command = new KMMailtoAddAddrBookCommand( mClickedUrl, mMainWindow); command->start(); } @@ -2498,7 +2511,7 @@ void KMReaderWin::slotMailtoAddAddrBook() //----------------------------------------------------------------------------- void KMReaderWin::slotMailtoOpenAddrBook() { - KMCommand *command = new KMMailtoOpenAddrBookCommand( mUrlClicked, + KMCommand *command = new KMMailtoOpenAddrBookCommand( mClickedUrl, mMainWindow ); command->start(); } @@ -2509,7 +2522,7 @@ void KMReaderWin::slotUrlCopy() // we don't necessarily need a mainWidget for KMUrlCopyCommand so // it doesn't matter if the dynamic_cast fails. KMCommand *command = - new KMUrlCopyCommand( mUrlClicked, + new KMUrlCopyCommand( mClickedUrl, dynamic_cast( mMainWindow ) ); command->start(); } @@ -2518,30 +2531,30 @@ void KMReaderWin::slotUrlCopy() void KMReaderWin::slotUrlOpen( const KURL &url ) { if ( !url.isEmpty() ) - mUrlClicked = url; - KMCommand *command = new KMUrlOpenCommand( mUrlClicked, this ); + mClickedUrl = url; + KMCommand *command = new KMUrlOpenCommand( mClickedUrl, this ); command->start(); } //----------------------------------------------------------------------------- void KMReaderWin::slotAddBookmarks() { - KMCommand *command = new KMAddBookmarksCommand( mUrlClicked, this ); + KMCommand *command = new KMAddBookmarksCommand( mClickedUrl, this ); command->start(); } //----------------------------------------------------------------------------- void KMReaderWin::slotUrlSave() { - KMCommand *command = new KMUrlSaveCommand( mUrlClicked, mMainWindow ); + KMCommand *command = new KMUrlSaveCommand( mClickedUrl, mMainWindow ); command->start(); } //----------------------------------------------------------------------------- void KMReaderWin::slotMailtoReply() { - KMCommand *command = new KMMailtoReplyCommand( mMainWindow, mUrlClicked, - message(), copyText() ); + KMCommand *command = new KMMailtoReplyCommand( mMainWindow, mClickedUrl, + message(), copyText() ); command->start(); } @@ -2584,6 +2597,14 @@ void KMReaderWin::slotSaveAttachments() saveCommand->start(); } +//----------------------------------------------------------------------------- +void KMReaderWin::saveAttachment( const KURL &tempFileName ) +{ + mAtmCurrent = msgPartFromUrl( tempFileName ); + mAtmCurrentName = mClickedUrl.path(); + slotHandleAttachment( KMHandleAttachmentCommand::Save ); // save +} + //----------------------------------------------------------------------------- void KMReaderWin::slotSaveMsg() { @@ -2597,10 +2618,35 @@ void KMReaderWin::slotSaveMsg() //----------------------------------------------------------------------------- void KMReaderWin::slotIMChat() { - KMCommand *command = new KMIMChatCommand( mUrlClicked, message() ); + KMCommand *command = new KMIMChatCommand( mClickedUrl, message() ); command->start(); } +//----------------------------------------------------------------------------- +static TQString linkForNode( const DOM::Node &node ) +{ + try { + if ( node.isNull() ) + return TQString(); + + const DOM::NamedNodeMap attributes = node.attributes(); + if ( !attributes.isNull() ) { + const DOM::Node href = attributes.getNamedItem( DOM::DOMString( "href" ) ); + if ( !href.isNull() ) { + return href.nodeValue().string(); + } + } + if ( !node.parentNode().isNull() ) { + return linkForNode( node.parentNode() ); + } else { + return TQString(); + } + } catch ( DOM::DOMException &e ) { + kdWarning(5006) << "Got an exception when trying to determine link under cursor!" << endl; + return TQString(); + } +} + //----------------------------------------------------------------------------- bool KMReaderWin::eventFilter( TQObject *, TQEvent *e ) { @@ -2608,17 +2654,87 @@ bool KMReaderWin::eventFilter( TQObject *, TQEvent *e ) TQMouseEvent* me = static_cast(e); if ( me->button() == LeftButton && ( me->state() & ShiftButton ) ) { // special processing for shift+click - mAtmCurrent = msgPartFromUrl( mUrlClicked ); - if ( mAtmCurrent < 0 ) return false; // not an attachment - mAtmCurrentName = mUrlClicked.path(); - slotHandleAttachment( KMHandleAttachmentCommand::Save ); // save - return true; // eat event + URLHandlerManager::instance()->handleShiftClick( mHoveredUrl, this ); + return true; + } + + if ( me->button() == LeftButton ) { + + TQString imagePath; + const DOM::Node nodeUnderMouse = mViewer->nodeUnderMouse(); + if ( !nodeUnderMouse.isNull() ) { + const DOM::NamedNodeMap attributes = nodeUnderMouse.attributes(); + if ( !attributes.isNull() ) { + const DOM::Node src = attributes.getNamedItem( DOM::DOMString( "src" ) ); + if ( !src.isNull() ) { + imagePath = src.nodeValue().string(); + } + } + } + + mCanStartDrag = URLHandlerManager::instance()->willHandleDrag( mHoveredUrl, imagePath, this ); + mLastClickPosition = me->pos(); + mLastClickImagePath = imagePath; } } + + if ( e->type() == TQEvent::MouseButtonRelease ) { + mCanStartDrag = false; + } + + if ( e->type() == TQEvent::MouseMove ) { + TQMouseEvent* me = static_cast( e ); + + // Handle this ourselves instead of connecting to mViewer::onURL(), since KHTML misses some + // notifications in case we started a drag ourselves + slotUrlOn( linkForNode( mViewer->nodeUnderMouse() ) ); + + if ( ( mLastClickPosition - me->pos() ).manhattanLength() > KGlobalSettings::dndEventDelay() ) { + if ( mCanStartDrag && ( !( mHoveredUrl.isEmpty() && mLastClickImagePath.isEmpty() ) ) ) { + if ( URLHandlerManager::instance()->handleDrag( mHoveredUrl, mLastClickImagePath, this ) ) { + mCanStartDrag = false; + slotUrlOn( TQString() ); + + // HACK: Send a mouse release event to the KHTMLView, as otherwise that will be missed in + // case we started a drag. If the event is missed, the HTML view gets into a wrong + // state, in which funny things like unsolicited drags start to happen. + TQMouseEvent mouseEvent( TQEvent::MouseButtonRelease, me->pos(), TQt::NoButton, TQt::NoButton ); + static_cast( mViewer->view() )->eventFilter( mViewer->view()->viewport(), + &mouseEvent ); + return true; + } + } + } + } + // standard event processing return false; } +void KMReaderWin::fillCommandInfo( partNode *node, KMMessage **msg, int *nodeId ) +{ + Q_ASSERT( msg && nodeId ); + + if ( mSerNumOfOriginalMessage != 0 ) { + KMFolder *folder = 0; + int index = -1; + KMMsgDict::instance()->getLocation( mSerNumOfOriginalMessage, &folder, &index ); + if ( folder && index != -1 ) + *msg = folder->getMsg( index ); + + if ( !( *msg ) ) { + kdWarning( 5006 ) << "Unable to find the original message, aborting attachment deletion!" << endl; + return; + } + + *nodeId = node->nodeId() + mNodeIdOffset; + } + else { + *nodeId = node->nodeId(); + *msg = message(); + } +} + void KMReaderWin::slotDeleteAttachment(partNode * node) { if ( KMessageBox::warningContinueCancel( this, @@ -2627,8 +2743,52 @@ void KMReaderWin::slotDeleteAttachment(partNode * node) != KMessageBox::Continue ) { return; } - KMDeleteAttachmentCommand* command = new KMDeleteAttachmentCommand( node, message(), this ); - command->start(); + + int nodeId = -1; + KMMessage *msg = 0; + fillCommandInfo( node, &msg, &nodeId ); + if ( msg && nodeId != -1 ) { + KMDeleteAttachmentCommand* command = new KMDeleteAttachmentCommand( nodeId, msg, this ); + command->start(); + connect( command, TQT_SIGNAL( completed( KMCommand * ) ), + this, TQT_SLOT( updateReaderWin() ) ); + connect( command, TQT_SIGNAL( completed( KMCommand * ) ), + this, TQT_SLOT( disconnectMsgAdded() ) ); + + // ### HACK: Since the command will do delete + add, a new message will arrive. However, we don't + // want the selection to change. Therefore, as soon as a new message arrives, select it, and then + // disconnect. + // Of course the are races, another message can arrive before ours, but we take the risk. + // And it won't work properly with multiple main windows + const KMHeaders * const headers = KMKernel::self()->getKMMainWidget()->headers(); + connect( headers, TQT_SIGNAL( msgAddedToListView( TQListViewItem* ) ), + this, TQT_SLOT( msgAdded( TQListViewItem* ) ) ); + } + + // If we are operating on a copy of parts of the message, make sure to update the copy as well. + if ( mSerNumOfOriginalMessage != 0 && message() ) { + message()->deleteBodyPart( node->nodeId() ); + update( true ); + } +} + +void KMReaderWin::msgAdded( TQListViewItem *item ) +{ + // A new message was added to the message list view. Select it. + // This is only connected right after we started a attachment delete command, so we expect a new + // message. Disconnect right afterwards, we only want this particular message to be selected. + disconnectMsgAdded(); + KMHeaders * const headers = KMKernel::self()->getKMMainWidget()->headers(); + headers->setCurrentItem( item ); + headers->clearSelection(); + headers->setSelected( item, true ); +} + +void KMReaderWin::disconnectMsgAdded() +{ + const KMHeaders *const headers = KMKernel::self()->getKMMainWidget()->headers(); + disconnect( headers, TQT_SIGNAL( msgAddedToListView( TQListViewItem* ) ), + this, TQT_SLOT( msgAdded( TQListViewItem* ) ) ); } void KMReaderWin::slotEditAttachment(partNode * node) @@ -2639,8 +2799,16 @@ void KMReaderWin::slotEditAttachment(partNode * node) != KMessageBox::Continue ) { return; } - KMEditAttachmentCommand* command = new KMEditAttachmentCommand( node, message(), this ); - command->start(); + + int nodeId = -1; + KMMessage *msg = 0; + fillCommandInfo( node, &msg, &nodeId ); + if ( msg && nodeId != -1 ) { + KMEditAttachmentCommand* command = new KMEditAttachmentCommand( nodeId, msg, this ); + command->start(); + } + + // FIXME: If we are operating on a copy of parts of the message, make sure to update the copy as well. } KMail::CSSHelper* KMReaderWin::cssHelper() @@ -2655,6 +2823,42 @@ bool KMReaderWin::decryptMessage() const return true; } +void KMReaderWin::scrollToAttachment( const partNode *node ) +{ + DOM::Document doc = mViewer->htmlDocument(); + + // The anchors for this are created in ObjectTreeParser::parseObjectTree() + mViewer->gotoAnchor( TQString::fromLatin1( "att%1" ).arg( node->nodeId() ) ); + + // Remove any old color markings which might be there + const partNode *root = node->topLevelParent(); + for ( int i = 0; i <= root->totalChildCount() + 1; i++ ) { + DOM::Element attachmentDiv = doc.getElementById( TQString( "attachmentDiv%1" ).arg( i + 1 ) ); + if ( !attachmentDiv.isNull() ) + attachmentDiv.removeAttribute( "style" ); + } + + // Don't mark hidden nodes, that would just produce a strange yellow line + if ( node->isDisplayedHidden() ) + return; + + // Now, color the div of the attachment in yellow, so that the user sees what happened. + // We created a special marked div for this in writeAttachmentMarkHeader() in ObjectTreeParser, + // find and modify that now. + DOM::Element attachmentDiv = doc.getElementById( TQString( "attachmentDiv%1" ).arg( node->nodeId() ) ); + if ( attachmentDiv.isNull() ) { + kdWarning( 5006 ) << "Could not find attachment div for attachment " << node->nodeId() << endl; + return; + } + + attachmentDiv.setAttribute( "style", TQString( "border:2px solid %1" ) + .arg( cssHelper()->pgpWarnColor().name() ) ); + + // Update rendering, otherwise the rendering is not updated when the user clicks on an attachment + // that causes scrolling and the open attachment dialog + doc.updateRendering(); +} + void KMReaderWin::injectAttachments() { // inject attachments in header view @@ -2668,30 +2872,33 @@ void KMReaderWin::injectAttachments() TQString visibility; TQString urlHandle; TQString imgSrc; - if( !showAttachmentQuicklist() ) - { - urlHandle.append( "kmail:showAttachmentQuicklist" ); - imgSrc.append( "attachmentQuicklistClosed.png" ); - } else { - urlHandle.append( "kmail:hideAttachmentQuicklist" ); - imgSrc.append( "attachmentQuicklistOpened.png" ); - } + if( !showAttachmentQuicklist() ) { + urlHandle.append( "kmail:showAttachmentQuicklist" ); + imgSrc.append( "attachmentQuicklistClosed.png" ); + } else { + urlHandle.append( "kmail:hideAttachmentQuicklist" ); + imgSrc.append( "attachmentQuicklistOpened.png" ); + } TQString html = renderAttachments( mRootNode, TQApplication::palette().active().background() ); if ( html.isEmpty() ) return; - if ( headerStyle() == HeaderStyle::fancy() ) - html.prepend( TQString::fromLatin1("
%1 
" ).arg(i18n("Attachments:")) ); - - if ( headerStyle() == HeaderStyle::enterprise() ) { - TQString link(""); - link += "
"; - html.prepend( link ); - } + TQString link(""); + if ( headerStyle() == HeaderStyle::fancy() ) { + link += "
"; + html.prepend( link ); + html.prepend( TQString::fromLatin1( "
%1 
" ). + arg( i18n( "Attachments:" ) ) ); + } else { + link += "
"; + html.prepend( link ); + } - assert( injectionPoint.tagName() == "div" ); - static_cast( injectionPoint ).setInnerHTML( html ); + assert( injectionPoint.tagName() == "div" ); + static_cast( injectionPoint ).setInnerHTML( html ); } static TQColor nextColor( const TQColor & c ) @@ -2712,48 +2919,45 @@ TQString KMReaderWin::renderAttachments(partNode * node, const TQColor &bgColor if ( !subHtml.isEmpty() ) { TQString visibility; - if( !showAttachmentQuicklist() ) - { - visibility.append( "display:none;" ); - } + if ( !showAttachmentQuicklist() ) { + visibility.append( "display:none;" ); + } TQString margin; if ( node != mRootNode || headerStyle() != HeaderStyle::enterprise() ) margin = "padding:2px; margin:2px; "; - if ( node->msgPart().typeStr() == "message" || node == mRootNode ) + TQString align = "left"; + if ( headerStyle() == HeaderStyle::enterprise() ) + align = "right"; + if ( node->msgPart().typeStr().lower() == "message" || node == mRootNode ) html += TQString::fromLatin1("
").arg( bgColor.name() ).arg( margin ).arg( visibility ); + "vertical-align:middle; float:%3; %4\">").arg( bgColor.name() ).arg( margin ) + .arg( align ).arg( visibility ); html += subHtml; - if ( node->msgPart().typeStr() == "message" || node == mRootNode ) + if ( node->msgPart().typeStr().lower() == "message" || node == mRootNode ) html += "
"; } } else { - TQString label, icon; - icon = node->msgPart().iconName( KIcon::Small ); - label = node->msgPart().contentDescription(); - if( label.isEmpty() ) - label = node->msgPart().name().stripWhiteSpace(); - if( label.isEmpty() ) - label = node->msgPart().fileName(); - bool typeBlacklisted = node->msgPart().typeStr() == "multipart"; - if ( !typeBlacklisted && node->msgPart().typeStr() == "application" ) { - typeBlacklisted = node->msgPart().subtypeStr() == "pgp-encrypted" - || node->msgPart().subtypeStr() == "pgp-signature" - || node->msgPart().subtypeStr() == "pkcs7-mime" - || node->msgPart().subtypeStr() == "pkcs7-signature"; - } - typeBlacklisted = typeBlacklisted || node == mRootNode; - if ( !label.isEmpty() && !icon.isEmpty() && !typeBlacklisted ) { + partNode::AttachmentDisplayInfo info = node->attachmentDisplayInfo(); + if ( info.displayInHeader ) { html += "
"; html += TQString::fromLatin1( "" ).arg( bgColor.name() ); - html += TQString::fromLatin1( "" ).arg( node->nodeId() ); - html += " "; + TQString fileName = writeMessagePartToTempFile( &node->msgPart(), node->nodeId() ); + TQString href = node->asHREF( "header" ); + html += TQString::fromLatin1( "" ); + html += " "; if ( headerStyle() == HeaderStyle::enterprise() ) { TQFont bodyFont = mCSSHelper->bodyFont( isFixedFont() ); TQFontMetrics fm( bodyFont ); - html += KStringHandler::rPixelSqueeze( label, fm, 140 ); - } else - html += label; + html += KStringHandler::rPixelSqueeze( info.label, fm, 140 ); + } else if ( headerStyle() == HeaderStyle::fancy() ) { + TQFont bodyFont = mCSSHelper->bodyFont( isFixedFont() ); + TQFontMetrics fm( bodyFont ); + html += KStringHandler::rPixelSqueeze( info.label, fm, 640 ); + } else { + html += info.label; + } html += "
"; } } @@ -2762,6 +2966,67 @@ TQString KMReaderWin::renderAttachments(partNode * node, const TQColor &bgColor return html; } +using namespace KMail::Interface; + +void KMReaderWin::setBodyPartMemento( const partNode * node, const TQCString & which, BodyPartMemento * memento ) +{ + const TQCString index = node->path() + ':' + which.lower(); + + const std::map::iterator it = mBodyPartMementoMap.lower_bound( index ); + if ( it != mBodyPartMementoMap.end() && it->first == index ) { + + if ( memento && memento == it->second ) + return; + + delete it->second; + + if ( memento ) { + it->second = memento; + } + else { + mBodyPartMementoMap.erase( it ); + } + + } else { + if ( memento ) { + mBodyPartMementoMap.insert( it, std::make_pair( index, memento ) ); + } + } + + if ( Observable * o = memento ? memento->asObservable() : 0 ) + o->attach( this ); +} + +BodyPartMemento * KMReaderWin::bodyPartMemento( const partNode * node, const TQCString & which ) const +{ + const TQCString index = node->path() + ':' + which.lower(); + const std::map::const_iterator it = mBodyPartMementoMap.find( index ); + if ( it == mBodyPartMementoMap.end() ) { + return 0; + } + else { + return it->second; + } +} + +static void detach_and_delete( BodyPartMemento * memento, KMReaderWin * obs ) { + if ( Observable * const o = memento ? memento->asObservable() : 0 ) + o->detach( obs ); + delete memento; +} + +void KMReaderWin::clearBodyPartMementos() +{ + for ( std::map::const_iterator it = mBodyPartMementoMap.begin(), end = mBodyPartMementoMap.end() ; it != end ; ++it ) + // Detach the memento from the reader. When cancelling it, it might trigger an update of the + // reader, which we are not interested in, and which is dangerous, since half the mementos are + // already deleted. + // https://issues.kolab.org/issue4187 + detach_and_delete( it->second, this ); + + mBodyPartMementoMap.clear(); +} + #include "kmreaderwin.moc" diff --git a/kmail/kmreaderwin.h b/kmail/kmreaderwin.h index da702936..32c7abdf 100644 --- a/kmail/kmreaderwin.h +++ b/kmail/kmreaderwin.h @@ -14,6 +14,8 @@ #include "kmmimeparttree.h" // Needed for friend declaration. #include "interfaces/observer.h" +#include + class TQFrame; class TQSplitter; class TQHBox; @@ -42,6 +44,7 @@ class KMMessagePart; namespace KMail { namespace Interface { class Observable; + class BodyPartMemento; } class PartMetaData; class ObjectTreeParser; @@ -139,7 +142,20 @@ public: /** Set the message that shall be shown. If msg is 0, an empty page is displayed. */ - virtual void setMsg(KMMessage* msg, bool force = false); + virtual void setMsg( KMMessage* msg, bool force = false, bool updateOnly = false ); + + /** + * This should be called when setting a message that was constructed from another message, which + * is the case when viewing encapsulated messages in the seperate reader window. + * We need to know the serial number of the original message, and at which part index the encapsulated + * message was at that original message, so that deleting and editing attachments can work on the + * original message. + * + * This is a HACK. There really shouldn't be a copy of the original mail. + * + * @see slotDeleteAttachment, slotEditAttachment, fillCommandInfo + */ + void setOriginalMsg( unsigned long serNumOfOriginalMessage, int nodeIdOffset ); /** Instead of settings a message to be shown sets a message part to be shown */ @@ -210,8 +226,12 @@ public: /** Enable the displaying of messages again after an URL was displayed */ void enableMsgDisplay(); - /** View message part of type message/RFC822 in extra viewer window. */ - void atmViewMsg(KMMessagePart* msgPart); + /** + * View message part of type message/RFC822 in extra viewer window. + * @param msgPart the part to display + * @param nodeId the part index of the message part that is displayed + */ + void atmViewMsg( KMMessagePart* msgPart, int nodeId ); bool atBottom() const; @@ -266,6 +286,7 @@ public: KMMessage* message(KMFolder** folder=0) const; void openAttachment( int id, const TQString & name ); + void saveAttachment( const KURL &tempFileName ); void emitUrlClicked( const KURL & url, int button ) { emit urlClicked( url, button ); @@ -302,6 +323,27 @@ public: /* show or hide the list that points to the attachments */ void setShowAttachmentQuicklist( bool showAttachmentQuicklist = true ) { mShowAttachmentQuicklist = showAttachmentQuicklist; } + // This controls whether a Toltec invitation is shown in its raw form or as a replacement text. + // This can be toggled with the "kmail:showRawToltecMail" link. + bool showRawToltecMail() const { return mShowRawToltecMail; } + void setShowRawToltecMail( bool showRawToltecMail ) { mShowRawToltecMail = showRawToltecMail; } + + /* retrieve BodyPartMemento of id \a which for partNode \a node */ + KMail::Interface::BodyPartMemento * bodyPartMemento( const partNode * node, const TQCString & which ) const; + + /* set/replace BodyPartMemento \a memento of id \a which for + partNode \a node. If there was a BodyPartMemento registered + already, replaces (deletes) that one. */ + void setBodyPartMemento( const partNode * node, const TQCString & which, KMail::Interface::BodyPartMemento * memento ); + + /// Scrolls to the given attachment and marks it with a yellow border + void scrollToAttachment( const partNode *node ); + +private: + /* deletes all BodyPartMementos. Use this when skipping to another + message (as opposed to re-loading the same one again). */ + void clearBodyPartMementos(); + signals: /** Emitted after parsing of a message to have it stored in unencrypted state in it's folder. */ @@ -383,6 +425,15 @@ public slots: void slotLevelQuote( int l ); void slotTouchMessage(); + /** + * Find the node ID and the message of the attachment that should be edited or deleted. + * This is used when setOriginalMsg() was called before, in that case we want to operate + * on the original message instead of our copy. + * + * @see setOriginalMsg + */ + void fillCommandInfo( partNode *node, KMMessage **msg, int *nodeId ); + void slotDeleteAttachment( partNode* node ); void slotEditAttachment( partNode* node ); @@ -402,12 +453,19 @@ protected slots: void slotSmartAttachments(); void slotInlineAttachments(); void slotHideAttachments(); + void slotHeaderOnlyAttachments(); /** Some attachment operations. */ void slotAtmView( int id, const TQString& name ); void slotDelayedResize(); void slotHandleAttachment( int ); + /** Helper functions used to change message selection in the message list after deleting + * an attachment, see slotDeleteAttachment() + */ + void disconnectMsgAdded(); + void msgAdded( TQListViewItem *item ); + protected: /** reimplemented in order to update the frame width in case of a changed GUI style */ @@ -432,7 +490,7 @@ protected: /** Creates a nice mail header depending on the current selected header style. */ - TQString writeMsgHeader(KMMessage* aMsg, bool hasVCard=false, bool topLevel=false); + TQString writeMsgHeader(KMMessage* aMsg, partNode *vCardNode = 0, bool topLevel=false ); /** Writes the given message part to a temporary file and returns the name of this file or TQString::null if writing failed. @@ -485,6 +543,11 @@ private: int mAtmCurrent; TQString mAtmCurrentName; KMMessage *mMessage; + + // See setOriginalMsg() for an explaination for those two. + unsigned long mSerNumOfOriginalMessage; + int mNodeIdOffset; + // widgets: TQSplitter * mSplitter; TQHBox *mBox; @@ -507,7 +570,6 @@ private: bool mMsgDisplay; bool mNoMDNsWhenEncrypted; unsigned long mLastSerNum; - KMMsgStatus mLastStatus; KMail::CSSHelper * mCSSHelper; bool mUseFixedFont; @@ -526,19 +588,29 @@ private: KAction *mMailToComposeAction, *mMailToReplyAction, *mMailToForwardAction, *mAddAddrBookAction, *mOpenAddrBookAction, *mCopyAction, *mCopyURLAction, *mUrlOpenAction, *mUrlSaveAsAction, *mAddBookmarksAction, *mStartIMChatAction, *mSelectAllAction; + KToggleAction *mHeaderOnlyAttachmentsAction; KSelectAction *mSelectEncodingAction; KToggleAction *mToggleFixFontAction; - KURL mUrlClicked; + + KURL mHoveredUrl; + KURL mClickedUrl; + TQPoint mLastClickPosition; + TQString mLastClickImagePath; + bool mCanStartDrag; + KMail::HtmlWriter * mHtmlWriter; + std::map mBodyPartMementoMap; // an attachment should be updated bool mAtmUpdate; int mChoice; unsigned long mWaitingForSerNum; float mSavedRelativePosition; - int mLevelQuote; + int mLevelQuote; bool mDecrytMessageOverwrite; bool mShowSignatureDetails; bool mShowAttachmentQuicklist; + bool mShowRawToltecMail; + bool mExternalWindow; }; diff --git a/kmail/kmsearchpattern.cpp b/kmail/kmsearchpattern.cpp index bec476ef..3d97a2b3 100644 --- a/kmail/kmsearchpattern.cpp +++ b/kmail/kmsearchpattern.cpp @@ -27,6 +27,8 @@ using KMail::FilterLog; #include #include +#include +#include #include @@ -58,7 +60,8 @@ static struct _statusNames statusNames[] = { { "To Do", KMMsgStatusTodo }, { "Spam", KMMsgStatusSpam }, { "Ham", KMMsgStatusHam }, - { "Has Attachment", KMMsgStatusHasAttach } + { "Has Attachment", KMMsgStatusHasAttach }, + { "Invitation", KMMsgStatusHasInvitation } }; static const int numStatusNames = sizeof statusNames / sizeof ( struct _statusNames ); @@ -280,7 +283,7 @@ bool KMSearchRuleString::matches( const DwString & aStr, KMMessage & msg, start += headerLen; size_t stop = aStr.find( '\n', start ); char ch = '\0'; - while ( stop != DwString::npos && ( ch = aStr.at( stop + 1 ) ) == ' ' || ch == '\t' ) + while ( stop != DwString::npos && ( ( ch = aStr.at( stop + 1 ) ) == ' ' || ch == '\t' ) ) stop = aStr.find( '\n', stop + 1 ); const int len = stop == DwString::npos ? aStr.length() - start : stop - start ; const TQCString codedValue( aStr.data() + start, len + 1 ); @@ -333,7 +336,19 @@ bool KMSearchRuleString::matches( const KMMessage * msg ) const bool logContents = true; if( field() == "" ) { - msgContents = msg->asString(); + + // When searching in the complete message, we can't simply use msg->asString() here, + // as that wouldn't decode the body. Therefore we use the decoded body and all decoded + // header fields and add all to the one big search string. + msgContents += msg->bodyToUnicode(); + const DwHeaders& headers = msg->headers(); + const DwField * dwField = headers.FirstField(); + while( dwField != 0 ) { + const char * const fieldName = dwField->FieldNameStr().c_str(); + const TQString fieldValue = msg->headerFields( fieldName ).join( " " ); + msgContents += " " + fieldValue; + dwField = dwField->Next(); + } logContents = false; } else if ( field() == "" ) { msgContents = msg->bodyToUnicode(); diff --git a/kmail/kmsearchpattern.h b/kmail/kmsearchpattern.h index 00a26c7b..45ffe680 100644 --- a/kmail/kmsearchpattern.h +++ b/kmail/kmsearchpattern.h @@ -230,7 +230,8 @@ namespace KMail { { I18N_NOOP( "Spam" ), "kmmsgspam" }, { I18N_NOOP( "Ham" ), "kmmsgham" }, { I18N_NOOP( "To Do" ), "kmmsgtodo" }, - { I18N_NOOP( "Has Attachment"), "kmmsgattachment" } + { I18N_NOOP( "Invitation" ), "kmmsginvitation" }, + { I18N_NOOP( "Has Attachment"), "kmmsgattachment" } //must be last }; // If you change the ordering here; also do it in the array above enum StatusValueTypes { @@ -249,7 +250,8 @@ namespace KMail { StatusSpam = 12, StatusHam = 13, StatusToDo = 14, - StatusHasAttachment = 15 + StatusInvitation = 15, + StatusHasAttachment = 16 //must be last }; static const int StatusValueCount = @@ -291,7 +293,7 @@ public: KConfig group and there is a constructor, mainly used by KMFilter to initialize from a preset KConfig-Group. - From a class hierarchy point of view, it is a TQPtrList of + From a class hierarchy point of view, it is a TQPtrList of KMSearchRule's that adds the boolean operators (see Operator) 'and' and 'or' that connect the rules logically, and has a name under which it could be stored in the config file. diff --git a/kmail/kmsearchpatternedit.cpp b/kmail/kmsearchpatternedit.cpp index a928e57f..399bc0e1 100644 --- a/kmail/kmsearchpatternedit.cpp +++ b/kmail/kmsearchpatternedit.cpp @@ -37,7 +37,13 @@ static const struct { { "", I18N_NOOP( "All Recipients" ) }, { "", I18N_NOOP( "Size in Bytes" ) }, { "", I18N_NOOP( "Age in Days" ) }, - { "", I18N_NOOP( "Message Status" ) } + { "", I18N_NOOP( "Message Status" ) }, + { "Subject", I18N_NOOP( "Subject" ) }, + { "From", I18N_NOOP( "From" ) }, + { "To", I18N_NOOP( "To" ) }, + { "CC", I18N_NOOP( "CC" ) }, + { "Reply-To", I18N_NOOP( "Reply To" ) }, + { "Organization", I18N_NOOP( "Organization" ) } }; static const int SpecialRuleFieldsCount = sizeof( SpecialRuleFields ) / sizeof( *SpecialRuleFields ); @@ -247,16 +253,16 @@ void KMSearchRuleWidget::initFieldList( bool headersOnly, bool absoluteDates ) mFilterFieldList.append( i18n( SpecialRuleFields[Size].displayName ) ); if ( !absoluteDates ) mFilterFieldList.append( i18n( SpecialRuleFields[AgeInDays].displayName ) ); - mFilterFieldList.append( i18n( SpecialRuleFields[Status].displayName ) ); + mFilterFieldList.append( i18n( SpecialRuleFields[Subject].displayName ) ); + mFilterFieldList.append( i18n( SpecialRuleFields[From].displayName ) ); + mFilterFieldList.append( i18n( SpecialRuleFields[To].displayName ) ); + mFilterFieldList.append( i18n( SpecialRuleFields[CC].displayName ) ); + mFilterFieldList.append( i18n( SpecialRuleFields[ReplyTo].displayName ) ); + mFilterFieldList.append( i18n( SpecialRuleFields[Organization].displayName ) ); + // these others only represent message headers and you can add to // them as you like - mFilterFieldList.append("Subject"); - mFilterFieldList.append("From"); - mFilterFieldList.append("To"); - mFilterFieldList.append("CC"); - mFilterFieldList.append("Reply-To"); mFilterFieldList.append("List-Id"); - mFilterFieldList.append("Organization"); mFilterFieldList.append("Resent-From"); mFilterFieldList.append("X-Loop"); mFilterFieldList.append("X-Mailing-List"); diff --git a/kmail/kmsearchpatternedit.h b/kmail/kmsearchpatternedit.h index a6ffd641..3fee81e1 100644 --- a/kmail/kmsearchpatternedit.h +++ b/kmail/kmsearchpatternedit.h @@ -45,7 +45,8 @@ public: be used to initialize the widget. */ KMSearchRuleWidget( TQWidget* parent=0, KMSearchRule* aRule=0, const char* name=0, bool headersOnly = false, bool absoluteDates = false ); - enum { Message, Body, AnyHeader, Recipients, Size, AgeInDays, Status }; + enum { Message, Body, AnyHeader, Recipients, Size, AgeInDays, Status, + Subject, From, To, CC, ReplyTo, Organization }; /** Set whether only header fields can be searched. If @p is true only header fields can be searched otherwise \ and \ searches diff --git a/kmail/kmsender.cpp b/kmail/kmsender.cpp index a79da268..7f7730da 100644 --- a/kmail/kmsender.cpp +++ b/kmail/kmsender.cpp @@ -317,6 +317,7 @@ void KMSender::doSendMsg() mCurrentMsg->setStatus(KMMsgStatusSent); mCurrentMsg->setStatus(KMMsgStatusRead); // otherwise it defaults to new on imap mCurrentMsg->updateAttachmentState(); + mCurrentMsg->updateInvitationState(); const KPIM::Identity & id = kmkernel->identityManager() ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() ); @@ -421,10 +422,10 @@ void KMSender::doSendMsg() // empty const KPIM::Identity & id = kmkernel->identityManager() ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() ); - if ( !id.emailAddr().isEmpty() ) { + if ( !id.primaryEmailAddress().isEmpty() ) { mCurrentMsg->setFrom( id.fullEmailAddr() ); } - else if ( !kmkernel->identityManager()->defaultIdentity().emailAddr().isEmpty() ) { + else if ( !kmkernel->identityManager()->defaultIdentity().primaryEmailAddress().isEmpty() ) { mCurrentMsg->setFrom( kmkernel->identityManager()->defaultIdentity().fullEmailAddr() ); } else { diff --git a/kmail/kmsystemtray.cpp b/kmail/kmsystemtray.cpp index e4d3a987..8dc4eee9 100644 --- a/kmail/kmsystemtray.cpp +++ b/kmail/kmsystemtray.cpp @@ -110,7 +110,6 @@ void KMSystemTray::buildPopupMenu() { // Delete any previously created popup menu delete mPopupMenu; - mPopupMenu = 0; mPopupMenu = new KPopupMenu(); KMMainWidget * mainWidget = kmkernel->getKMMainWidget(); @@ -293,6 +292,9 @@ void KMSystemTray::foldersChanged() /** Check all new folders to see if we started with any new messages */ updateNewMessageNotification(currentFolder); } + else { + disconnect(currentFolder, TQT_SIGNAL(numUnreadMsgsChanged(KMFolder *)), this, TQT_SLOT(updateNewMessageNotification(KMFolder *)) ); + } } } @@ -588,4 +590,9 @@ void KMSystemTray::selectedAccount(int id) ft->selectCurrentFolder(); } +bool KMSystemTray::hasUnreadMail() const +{ + return ( mCount != 0 ); +} + #include "kmsystemtray.moc" diff --git a/kmail/kmsystemtray.h b/kmail/kmsystemtray.h index ab80f362..62707464 100644 --- a/kmail/kmsystemtray.h +++ b/kmail/kmsystemtray.h @@ -51,10 +51,13 @@ public: int mode() const; void hideKMail(); + bool hasUnreadMail() const; + +public slots: + void foldersChanged(); private slots: void updateNewMessageNotification(KMFolder * folder); - void foldersChanged(); void selectedAccount(int); void updateNewMessages(); void tray_quit(); diff --git a/kmail/kmversion.h b/kmail/kmversion.h index 8a7e6858..98706ae1 100644 --- a/kmail/kmversion.h +++ b/kmail/kmversion.h @@ -3,6 +3,6 @@ #ifndef kmversion_h #define kmversion_h -#define KMAIL_VERSION "1.9.10" +#define KMAIL_VERSION "1.9.10 (enterprise35 0.20100827.1168748)" #endif /*kmversion_h*/ diff --git a/kmail/managesievescriptsdialog.cpp b/kmail/managesievescriptsdialog.cpp index a089c94a..3bec7132 100644 --- a/kmail/managesievescriptsdialog.cpp +++ b/kmail/managesievescriptsdialog.cpp @@ -83,10 +83,15 @@ static KURL findUrlForAccount( const KMail::ImapAccountBase * a ) { u.setPass( a->passwd() ); u.setPort( sieve.port() ); // Translate IMAP LOGIN to PLAIN: - u.setQuery( "x-mech=" + ( a->auth() == "*" ? "PLAIN" : a->auth() ) ); + u.addQueryItem( "x-mech", a->auth() == "*" ? "PLAIN" : a->auth() ); + if ( !a->useSSL() && !a->useTLS() ) + u.addQueryItem( "x-allow-unencrypted", "true" ); return u; } else { - return sieve.alternateURL(); + KURL u = sieve.alternateURL(); + if ( u.protocol().lower() == "sieve" && !a->useSSL() && !a->useTLS() && u.queryItem("x-allow-unencrypted").isEmpty() ) + u.addQueryItem( "x-allow-unencrypted", "true" ); + return u; } } @@ -159,6 +164,7 @@ void KMail::ManageSieveScriptsDialog::slotContextMenuRequested( TQListViewItem * // script items: menu.insertItem( i18n( "Delete Script" ), this, TQT_SLOT(slotDeleteScript()) ); menu.insertItem( i18n( "Edit Script..." ), this, TQT_SLOT(slotEditScript()) ); + menu.insertItem( i18n( "Deactivate Script" ), this, TQT_SLOT(slotDeactivateScript()) ); } else { // top-levels: menu.insertItem( i18n( "New Script..." ), this, TQT_SLOT(slotNewScript()) ); @@ -167,6 +173,20 @@ void KMail::ManageSieveScriptsDialog::slotContextMenuRequested( TQListViewItem * mContextMenuItem = 0; } + +void KMail::ManageSieveScriptsDialog::slotDeactivateScript() { + if ( !mContextMenuItem ) + return; + + TQCheckListItem * parent = qcli_cast( mContextMenuItem->parent() ); + if ( !parent ) + return; + if ( mContextMenuItem->isOn()) { + mSelectedItems[parent] = mContextMenuItem; + changeActiveScript( parent,false ); + } +} + void KMail::ManageSieveScriptsDialog::slotSelectionChanged( TQListViewItem * i ) { TQCheckListItem * item = qcli_cast( i ); if ( !item ) @@ -176,11 +196,11 @@ void KMail::ManageSieveScriptsDialog::slotSelectionChanged( TQListViewItem * i ) return; if ( item->isOn() && mSelectedItems[parent] != item ) { mSelectedItems[parent] = item; - changeActiveScript( parent ); + changeActiveScript( parent,true ); } } -void KMail::ManageSieveScriptsDialog::changeActiveScript( TQCheckListItem * item ) { +void KMail::ManageSieveScriptsDialog::changeActiveScript( TQCheckListItem * item , bool activate) { if ( !item ) return; if ( !mUrls.count( item ) ) @@ -194,8 +214,11 @@ void KMail::ManageSieveScriptsDialog::changeActiveScript( TQCheckListItem * item if ( !selected ) return; u.setFileName( selected->text( 0 ) ); - - SieveJob * job = SieveJob::activate( u ); + SieveJob * job; + if ( activate ) + job = SieveJob::activate( u ); + else + job = SieveJob::desactivate( u ); connect( job, TQT_SIGNAL(result(KMail::SieveJob*,bool,const TQString&,bool)), this, TQT_SLOT(slotRefresh()) ); } @@ -235,7 +258,6 @@ void KMail::ManageSieveScriptsDialog::slotDeleteScript() { KStdGuiItem::del() ) != KMessageBox::Continue ) return; - SieveJob * job = SieveJob::del( u ); connect( job, TQT_SIGNAL(result(KMail::SieveJob*,bool,const TQString&,bool)), this, TQT_SLOT(slotRefresh()) ); @@ -295,15 +317,22 @@ KMail::SieveEditor::SieveEditor( TQWidget * parent, const char * name ) TQVBoxLayout * vlay = new TQVBoxLayout( plainPage(), 0, spacingHint() ); mTextEdit = new TQTextEdit( plainPage() ); vlay->addWidget( mTextEdit ); + mTextEdit->setFocus(); mTextEdit->setTextFormat( TQTextEdit::PlainText ); mTextEdit->setWordWrap( TQTextEdit::NoWrap ); mTextEdit->setFont( KGlobalSettings::fixedFont() ); - + connect( mTextEdit, TQT_SIGNAL( textChanged () ), TQT_SLOT( slotTextChanged() ) ); resize( 3 * sizeHint() ); } KMail::SieveEditor::~SieveEditor() {} + +void KMail::SieveEditor::slotTextChanged() +{ + enableButtonOK( !script().isEmpty() ); +} + void KMail::ManageSieveScriptsDialog::slotGetResult( KMail::SieveJob *, bool success, const TQString & script, bool isActive ) { if ( !success ) return; @@ -330,6 +359,7 @@ void KMail::ManageSieveScriptsDialog::slotSieveEditorOkClicked() { void KMail::ManageSieveScriptsDialog::slotSieveEditorCancelClicked() { mSieveEditor->deleteLater(); mSieveEditor = 0; mCurrentURL = KURL(); + slotRefresh(); } void KMail::ManageSieveScriptsDialog::slotPutResult( KMail::SieveJob *, bool success ) { diff --git a/kmail/managesievescriptsdialog.h b/kmail/managesievescriptsdialog.h index 2195e911..70c4f697 100644 --- a/kmail/managesievescriptsdialog.h +++ b/kmail/managesievescriptsdialog.h @@ -28,6 +28,7 @@ private slots: void slotSelectionChanged( TQListViewItem * ); void slotNewScript(); void slotEditScript(); + void slotDeactivateScript(); void slotDeleteScript(); void slotGetResult( KMail::SieveJob *, bool, const TQString &, bool ); void slotPutResult( KMail::SieveJob *, bool ); @@ -36,7 +37,7 @@ private slots: private: void killAllJobs(); - void changeActiveScript( TQCheckListItem * ); + void changeActiveScript( TQCheckListItem *, bool activate = true ); private: TQListView * mListView; diff --git a/kmail/managesievescriptsdialog_p.h b/kmail/managesievescriptsdialog_p.h index 83fde06b..2ae12ff5 100644 --- a/kmail/managesievescriptsdialog_p.h +++ b/kmail/managesievescriptsdialog_p.h @@ -16,7 +16,8 @@ public: TQString script() const { return mTextEdit->text(); } void setScript( const TQString & script ) { mTextEdit->setText( script ); } - +private slots: + void slotTextChanged(); private: TQTextEdit * mTextEdit; }; diff --git a/kmail/messageactions.cpp b/kmail/messageactions.cpp index b900b7b5..cc543195 100644 --- a/kmail/messageactions.cpp +++ b/kmail/messageactions.cpp @@ -138,7 +138,11 @@ void MessageActions::setSelectedVisibleSernums(const TQValueList< Q_UINT32 > & s void MessageActions::updateActions() { - const bool singleMsg = (mCurrentMessage != 0); + bool singleMsg = (mCurrentMessage != 0); + if ( mCurrentMessage && mCurrentMessage->parent() ) { + if ( mCurrentMessage->parent()->isTemplates() ) + singleMsg = false; + } const bool multiVisible = mVisibleSernums.count() > 0 || mCurrentMessage; const bool flagsAvailable = GlobalSettings::self()->allowLocalFlags() || !((mCurrentMessage && mCurrentMessage->parent()) ? mCurrentMessage->parent()->isReadOnly() : true); @@ -164,6 +168,18 @@ void MessageActions::updateActions() mEditAction->setEnabled( singleMsg ); } +template void MessageActions::replyCommand() +{ + if ( !mCurrentMessage ) + return; + const TQString text = mMessageView ? mMessageView->copyText() : ""; + KMCommand *command = new T( mParent, mCurrentMessage, text ); + connect( command, TQT_SIGNAL( completed( KMCommand * ) ), + this, TQT_SIGNAL( replyActionFinished() ) ); + command->start(); +} + + void MessageActions::slotCreateTodo() { if ( !mCurrentMessage ) diff --git a/kmail/messageactions.h b/kmail/messageactions.h index 39e9f937..41279c55 100644 --- a/kmail/messageactions.h +++ b/kmail/messageactions.h @@ -55,19 +55,20 @@ class MessageActions : public QObject KAction* editAction() const { return mEditAction; } + signals: + + // This signal is emitted when a reply is triggered and the + // action has finished. + // This is useful for the stand-alone reader, it might want to close the window in + // that case. + void replyActionFinished(); + public slots: void editCurrentMessage(); private: void updateActions(); - template void replyCommand() - { - if ( !mCurrentMessage ) - return; - const TQString text = mMessageView ? mMessageView->copyText() : ""; - KMCommand *command = new T( mParent, mCurrentMessage, text ); - command->start(); - } + template void replyCommand(); void setMessageStatus( KMMsgStatus status, bool toggle = false ); private slots: diff --git a/kmail/messagecomposer.cpp b/kmail/messagecomposer.cpp index 8d995073..8b3f1c5c 100644 --- a/kmail/messagecomposer.cpp +++ b/kmail/messagecomposer.cpp @@ -86,6 +86,7 @@ #include #include +#include #include // ## keep default values in sync with configuredialog.cpp, Security::CryptoTab::setup() @@ -612,11 +613,8 @@ void MessageComposer::chiasmusEncryptAllAttachments() { part->setTypeStr( "application" ); part->setSubtypeStr( "vnd.de.bund.bsi.chiasmus" ); part->setName( filename + ".xia" ); - // this is taken from kmmsgpartdlg.cpp: - TQCString encoding = KMMsgBase::autoDetectCharset( part->charset(), KMMessage::preferredCharsets(), filename ); - if ( encoding.isEmpty() ) - encoding = "utf-8"; - const TQCString enc_name = KMMsgBase::encodeRFC2231String( filename + ".xia", encoding ); + const TQCString enc_name = KMMsgBase::encodeRFC2231StringAutoDetectCharset( + filename + ".xia", part->charset() ); const TQCString cDisp = "attachment;\n\tfilename" + ( TQString( enc_name ) != filename + ".xia" ? "*=" + enc_name @@ -2146,8 +2144,14 @@ void MessageComposer::pgpSignedMsg( const TQByteArray& cText, Kleo::CryptoMessag mSignature = TQByteArray(); const std::vector signingKeys = mKeyResolver->signingKeys( format ); - - assert( !signingKeys.empty() ); + if ( signingKeys.empty() ) { + KMessageBox::sorry( mComposeWin, + i18n("This message could not be signed, " + "since no valid signing keys have been found; " + "this should actually never happen, " + "please report this bug.") ); + return; + } // TODO: ASync call? Likely, yes :-) const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance(); @@ -2171,6 +2175,11 @@ void MessageComposer::pgpSignedMsg( const TQByteArray& cText, Kleo::CryptoMessag TQByteArray signature; const GpgME::SigningResult res = job->exec( signingKeys, cText, signingMode( format ), signature ); + { + std::stringstream ss; + ss << res; + kdDebug(5006) << ss.str().c_str() << endl; + } if ( res.error().isCanceled() ) { kdDebug() << "signing was canceled by user" << endl; return; @@ -2182,6 +2191,7 @@ void MessageComposer::pgpSignedMsg( const TQByteArray& cText, Kleo::CryptoMessag } if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() ) + if ( Kleo::MessageBox::showAuditLogButton( job.get() ) ) Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Signing Operation") ); mSignature = signature; @@ -2219,6 +2229,11 @@ Kpgp::Result MessageComposer::pgpEncryptedMsg( TQByteArray & encryptedBody, const GpgME::EncryptionResult res = job->exec( encryptionKeys, cText, true /* we do ownertrust ourselves */, encryptedBody ); + { + std::stringstream ss; + ss << res; + kdDebug(5006) << ss.str().c_str() << endl; + } if ( res.error().isCanceled() ) { kdDebug() << "encryption was canceled by user" << endl; return Kpgp::Canceled; @@ -2230,6 +2245,7 @@ Kpgp::Result MessageComposer::pgpEncryptedMsg( TQByteArray & encryptedBody, } if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() ) + if ( Kleo::MessageBox::showAuditLogButton( job.get() ) ) Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Encryption Operation") ); return Kpgp::Ok; @@ -2261,6 +2277,11 @@ Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( TQByteArray & encryptedB const std::pair res = job->exec( signingKeys, encryptionKeys, cText, false, encryptedBody ); + { + std::stringstream ss; + ss << res.first << '\n' << res.second; + kdDebug(5006) << ss.str().c_str() << endl; + } if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) { kdDebug() << "encrypt/sign was canceled by user" << endl; return Kpgp::Canceled; @@ -2275,6 +2296,7 @@ Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( TQByteArray & encryptedB } if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() ) + if ( Kleo::MessageBox::showAuditLogButton( job.get() ) ) Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Encryption Operation") ); return Kpgp::Ok; diff --git a/kmail/messageproperty.cpp b/kmail/messageproperty.cpp index 7de13eb3..b1e02161 100644 --- a/kmail/messageproperty.cpp +++ b/kmail/messageproperty.cpp @@ -1,5 +1,5 @@ /* Message Property - + This file is part of KMail, the KDE mail client. Copyright (c) Don Sanders @@ -66,14 +66,13 @@ void MessageProperty::setFiltering( const KMMsgBase *msgBase, bool filter ) KMFolder* MessageProperty::filterFolder( Q_UINT32 serNum ) { - if (sFolders.contains(serNum)) - return sFolders[serNum].operator->(); - return 0; + TQMap >::const_iterator it = sFolders.find( serNum ); + return it == sFolders.constEnd() ? 0 : (*it).operator->(); } void MessageProperty::setFilterFolder( Q_UINT32 serNum, KMFolder* folder ) { - sFolders.replace(serNum, TQGuardedPtr(folder) ); + sFolders.insert(serNum, TQGuardedPtr(folder) ); } KMFolder* MessageProperty::filterFolder( const KMMsgBase *msgBase ) @@ -88,15 +87,14 @@ void MessageProperty::setFilterFolder( const KMMsgBase *msgBase, KMFolder* folde ActionScheduler* MessageProperty::filterHandler( Q_UINT32 serNum ) { - if ( sHandlers.contains( serNum )) - return sHandlers[serNum].operator->(); - return 0; + TQMap >::const_iterator it = sHandlers.find( serNum ); + return it == sHandlers.constEnd() ? 0 : (*it).operator->(); } void MessageProperty::setFilterHandler( Q_UINT32 serNum, ActionScheduler* handler ) { if (handler) - sHandlers.replace( serNum, TQGuardedPtr(handler) ); + sHandlers.insert( serNum, TQGuardedPtr(handler) ); else sHandlers.remove( serNum ); } @@ -113,16 +111,16 @@ void MessageProperty::setFilterHandler( const KMMsgBase *msgBase, ActionSchedule bool MessageProperty::transferInProgress( Q_UINT32 serNum ) { - if (sTransfers.contains(serNum)) - return sTransfers[serNum]; - return false; + TQMap::const_iterator it = sTransfers.find( serNum ); + return it == sTransfers.constEnd() ? false : *it; } void MessageProperty::setTransferInProgress( Q_UINT32 serNum, bool transfer, bool force ) { int transferInProgress = 0; - if (sTransfers.contains(serNum)) - transferInProgress = sTransfers[serNum]; + TQMap::const_iterator it = sTransfers.find( serNum ); + if (it != sTransfers.constEnd()) + transferInProgress = *it; if ( force && !transfer ) transferInProgress = 0; else @@ -130,7 +128,7 @@ void MessageProperty::setTransferInProgress( Q_UINT32 serNum, bool transfer, boo if ( transferInProgress < 0 ) transferInProgress = 0; if (transferInProgress) - sTransfers.replace( serNum, transferInProgress ); + sTransfers.insert( serNum, transferInProgress ); else sTransfers.remove( serNum ); } @@ -147,15 +145,14 @@ void MessageProperty::setTransferInProgress( const KMMsgBase *msgBase, bool tran Q_UINT32 MessageProperty::serialCache( const KMMsgBase *msgBase ) { - if (sSerialCache.contains( msgBase )) - return sSerialCache[msgBase]; - return 0; + TQMap::const_iterator it = sSerialCache.find( msgBase ); + return it == sSerialCache.constEnd() ? 0 : *it; } void MessageProperty::setSerialCache( const KMMsgBase *msgBase, Q_UINT32 serNum ) { if (serNum) - sSerialCache.replace( msgBase, serNum ); + sSerialCache.insert( msgBase, serNum ); else sSerialCache.remove( msgBase ); } diff --git a/kmail/messageproperty.h b/kmail/messageproperty.h index 4809cd7a..91168e6f 100644 --- a/kmail/messageproperty.h +++ b/kmail/messageproperty.h @@ -91,6 +91,7 @@ public: static bool transferInProgress( const KMMsgBase* ); static void setTransferInProgress( Q_UINT32, bool, bool = false ); static bool transferInProgress( Q_UINT32 ); + /** Some properties, namely complete, transferInProgress, and serialCache must be forgotten when a message class instance is destructed or assigned a new value */ @@ -99,10 +100,13 @@ public: private: // The folder a message is to be moved into once filtering is finished if any static TQMap > sFolders; + // The action scheduler currently processing a message if any static TQMap > sHandlers; + // The transferInProgres state of a message if any. static TQMap sTransfers; + // The cached serial number of a message if any. static TQMap sSerialCache; }; diff --git a/kmail/newfolderdialog.cpp b/kmail/newfolderdialog.cpp index 78294e5f..99051222 100644 --- a/kmail/newfolderdialog.cpp +++ b/kmail/newfolderdialog.cpp @@ -40,6 +40,7 @@ #include #include +#include "folderutil.h" #include "newfolderdialog.h" #include "kmfolder.h" #include "folderstorage.h" @@ -58,6 +59,9 @@ NewFolderDialog::NewFolderDialog( TQWidget* parent, KMFolder *folder ) : KDialogBase( parent, "new_folder_dialog", false, i18n( "New Folder" ), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ), + mFormatComboBox( 0 ), + mContentsComboBox( 0 ), + mNamespacesComboBox( 0 ), mFolder( folder ) { setWFlags( getWFlags() | WDestructiveClose ); @@ -112,7 +116,8 @@ NewFolderDialog::NewFolderDialog( TQWidget* parent, KMFolder *folder ) } // --- contents ----- - if ( kmkernel->iCalIface().isEnabled() ) { + if ( kmkernel->iCalIface().isEnabled() && + mFolder && mFolder->folderType() != KMFolderTypeImap ) { mContentsHBox = new TQHBoxLayout( 0, 0, 6, "mContentsHBox"); mContentsLabel = new TQLabel( privateLayoutWidget, "mContentsLabel" ); @@ -190,41 +195,12 @@ void NewFolderDialog::slotOk() return; } - // names of local folders must not contain a '/' - if ( fldName.find( '/' ) != -1 && - ( !mFolder || - ( mFolder->folderType() != KMFolderTypeImap && - mFolder->folderType() != KMFolderTypeCachedImap ) ) ) { - KMessageBox::error( this, i18n( "Folder names cannot contain the / (slash) character; please choose another folder name." ) ); + TQString msg; + if ( mFolder && !mFolder->isValidName( fldName, msg ) ) { + KMessageBox::error( this, msg ); return; } - // folder names must not start with a '.' - if ( fldName.startsWith( "." ) ) { - KMessageBox::error( this, i18n( "Folder names cannot start with a . (dot) character; please choose another folder name." ) ); - return; - } - - // names of IMAP folders must not contain the folder delimiter - if ( mFolder && - ( mFolder->folderType() == KMFolderTypeImap || - mFolder->folderType() == KMFolderTypeCachedImap ) ) { - TQString delimiter; - if ( mFolder->folderType() == KMFolderTypeImap ) { - KMAcctImap* ai = static_cast( mFolder->storage() )->account(); - if ( ai ) - delimiter = ai->delimiterForFolder( mFolder->storage() ); - } else { - KMAcctCachedImap* ai = static_cast( mFolder->storage() )->account(); - if ( ai ) - delimiter = ai->delimiterForFolder( mFolder->storage() ); - } - if ( !delimiter.isEmpty() && fldName.find( delimiter ) != -1 ) { - KMessageBox::error( this, i18n( "Your IMAP server does not allow the character '%1'; please choose another folder name." ).arg( delimiter ) ); - return; - } - } - // default parent is Top Level local folders KMFolderDir * selectedFolderDir = &(kmkernel->folderMgr()->dir()); // we got a parent, let's use that @@ -245,55 +221,21 @@ void NewFolderDialog::slotOk() /* Ok, obvious errors caught, let's try creating it for real. */ const TQString message = i18n( "Failed to create folder %1." " " ).arg(fldName); - bool success = false; - KMFolder *newFolder = 0; - if ( mFolder && mFolder->folderType() == KMFolderTypeImap ) { - KMFolderImap* selectedStorage = static_cast( mFolder->storage() ); - KMAcctImap *anAccount = selectedStorage->account(); - // check if a connection is available BEFORE creating the folder - if (anAccount->makeConnection() == ImapAccountBase::Connected) { - newFolder = kmkernel->imapFolderMgr()->createFolder( fldName, false, KMFolderTypeImap, selectedFolderDir ); - if ( newFolder ) { - TQString imapPath, parent; - if ( mNamespacesComboBox ) { - // create folder with namespace - parent = anAccount->addPathToNamespace( mNamespacesComboBox->currentText() ); - imapPath = anAccount->createImapPath( parent, fldName ); - } else { - imapPath = anAccount->createImapPath( selectedStorage->imapPath(), fldName ); - } - KMFolderImap* newStorage = static_cast( newFolder->storage() ); - selectedStorage->createFolder(fldName, parent); // create it on the server - newStorage->initializeFrom( selectedStorage, imapPath, TQString::null ); - static_cast(mFolder->storage())->setAccount( selectedStorage->account() ); - success = true; - } - } - } else if ( mFolder && mFolder->folderType() == KMFolderTypeCachedImap ) { - newFolder = kmkernel->dimapFolderMgr()->createFolder( fldName, false, KMFolderTypeCachedImap, selectedFolderDir ); - if ( newFolder ) { - KMFolderCachedImap* selectedStorage = static_cast( mFolder->storage() ); - KMFolderCachedImap* newStorage = static_cast( newFolder->storage() ); - newStorage->initializeFrom( selectedStorage ); - if ( mNamespacesComboBox ) { - // create folder with namespace - TQString path = selectedStorage->account()->createImapPath( - mNamespacesComboBox->currentText(), fldName ); - newStorage->setImapPathForCreation( path ); - } - success = true; - } - } else { - // local folder - if (mFormatComboBox->currentItem() == 1) - newFolder = kmkernel->folderMgr()->createFolder(fldName, false, KMFolderTypeMaildir, selectedFolderDir ); - else - newFolder = kmkernel->folderMgr()->createFolder(fldName, false, KMFolderTypeMbox, selectedFolderDir ); - if ( newFolder ) - success = true; + TQString namespaceName; + if ( mNamespacesComboBox ) { + namespaceName = mNamespacesComboBox->currentText(); } - if ( !success ) { + + KMFolderType folderType = KMFolderTypeUnknown; + if ( mFormatComboBox && mFormatComboBox->currentItem() == 1 ) + folderType = KMFolderTypeMaildir; + else if ( mFormatComboBox ) + folderType = KMFolderTypeMbox; + + KMFolder *newFolder = KMail::FolderUtil::createSubFolder( mFolder, selectedFolderDir, fldName, + namespaceName, folderType ); + if ( !newFolder ) { KMessageBox::error( this, message ); return; } diff --git a/kmail/newfolderdialog.h b/kmail/newfolderdialog.h index 96f4421e..4744db3e 100644 --- a/kmail/newfolderdialog.h +++ b/kmail/newfolderdialog.h @@ -69,7 +69,7 @@ class NewFolderDialog : public KDialogBase TQHBoxLayout* mNamespacesHBox; protected slots: void slotOk(); - void slotFolderNameChanged( const TQString & _text); + void slotFolderNameChanged( const TQString & _text); private: KMFolder* mFolder; diff --git a/kmail/objecttreeparser.cpp b/kmail/objecttreeparser.cpp index 4c56a7bd..d8b98f24 100644 --- a/kmail/objecttreeparser.cpp +++ b/kmail/objecttreeparser.cpp @@ -34,6 +34,7 @@ // my header file #include "objecttreeparser.h" +#include "objecttreeparser_p.h" // other KMail headers #include "kmkernel.h" @@ -52,6 +53,7 @@ #include "interfaces/bodypartformatter.h" #include "globalsettings.h" #include "util.h" +#include "callback.h" // other module headers #include @@ -108,9 +110,11 @@ // other headers #include +#include #include #include #include +#include #include "chiasmuskeyselector.h" namespace KMail { @@ -145,6 +149,9 @@ namespace KMail { mShowOnlyOneMimePart( showOnlyOneMimePart ), mKeepEncryptions( keepEncryptions ), mIncludeSignatures( includeSignatures ), + mHasPendingAsyncJobs( false ), + mAllowAsync( false ), + mShowRawToltecMail( false ), mAttachmentStrategy( strategy ), mHtmlWriter( htmlWriter ), mCSSHelper( cssHelper ) @@ -164,6 +171,8 @@ namespace KMail { mShowOnlyOneMimePart( other.showOnlyOneMimePart() ), mKeepEncryptions( other.keepEncryptions() ), mIncludeSignatures( other.includeSignatures() ), + mHasPendingAsyncJobs( other.hasPendingAsyncJobs() ), + mAllowAsync( other.allowAsync() ), mAttachmentStrategy( other.attachmentStrategy() ), mHtmlWriter( other.htmlWriter() ), mCSSHelper( other.cssHelper() ) @@ -176,7 +185,7 @@ namespace KMail { void ObjectTreeParser::insertAndParseNewChildNode( partNode& startNode, const char* content, const char* cntDesc, - bool append ) + bool append, bool addToTextualContent ) { DwBodyPart* myBody = new DwBodyPart( DwString( content ), 0 ); myBody->Parse(); @@ -201,6 +210,11 @@ namespace KMail { partNode* parentNode = &startNode; partNode* newNode = new partNode(false, myBody); + + // Build the object tree of the new node before setting the parent, as otherwise + // buildObjectTree() would erronously modify the parents as well + newNode->buildObjectTree( false ); + if ( append && parentNode->firstChild() ) { parentNode = parentNode->firstChild(); while( parentNode->nextSibling() ) @@ -209,40 +223,37 @@ namespace KMail { } else parentNode->setFirstChild( newNode ); - newNode->buildObjectTree( false ); - if ( startNode.mimePartTreeItem() ) { - kdDebug(5006) << "\n -----> Inserting items into MimePartTree\n" << endl; newNode->fillMimePartTree( startNode.mimePartTreeItem(), 0, TQString::null, TQString::null, TQString::null, 0, append ); - kdDebug(5006) << "\n <----- Finished inserting items into MimePartTree\n" << endl; } else { - kdDebug(5006) << "\n ------ Sorry, node.mimePartTreeItem() returns ZERO so" - << "\n we cannot insert new lines into MimePartTree. :-(\n" << endl; } - kdDebug(5006) << "\n -----> Now parsing the MimePartTree\n" << endl; ObjectTreeParser otp( mReader, cryptoProtocol() ); otp.parseObjectTree( newNode ); - mRawReplyString += otp.rawReplyString(); - mTextualContent += otp.textualContent(); - if ( !otp.textualContentCharset().isEmpty() ) - mTextualContentCharset = otp.textualContentCharset(); - kdDebug(5006) << "\n <----- Finished parsing the MimePartTree in insertAndParseNewChildNode()\n" << endl; + if ( addToTextualContent ) { + mRawReplyString += otp.rawReplyString(); + mTextualContent += otp.textualContent(); + if ( !otp.textualContentCharset().isEmpty() ) + mTextualContentCharset = otp.textualContentCharset(); + } } //----------------------------------------------------------------------------- void ObjectTreeParser::parseObjectTree( partNode * node ) { - kdDebug(5006) << "ObjectTreeParser::parseObjectTree( " - << (node ? "node OK, " : "no node, ") - << "showOnlyOneMimePart: " << (showOnlyOneMimePart() ? "TRUE" : "FALSE") - << " )" << endl; + //kdDebug(5006) << "ObjectTreeParser::parseObjectTree( " + // << (node ? "node OK, " : "no node, ") + // << "showOnlyOneMimePart: " << (showOnlyOneMimePart() ? "TRUE" : "FALSE") + // << " )" << endl; if ( !node ) return; + // reset pending async jobs state (we'll rediscover pending jobs as we go) + mHasPendingAsyncJobs = false; + // reset "processed" flags for... if ( showOnlyOneMimePart() ) { // ... this node and all descendants @@ -260,29 +271,38 @@ namespace KMail { ProcessResult processResult; - if ( mReader ) + if ( mReader ) { htmlWriter()->queue( TQString::fromLatin1("").arg( node->nodeId() ) ); + } + if ( const Interface::BodyPartFormatter * formatter = BodyPartFormatterFactory::instance()->createFor( node->typeString(), node->subTypeString() ) ) { - PartNodeBodyPart part( *node, codecFor( node ) ); - // Set the default display strategy for this body part relying on the - // identity of KMail::Interface::BodyPart::Display and AttachmentStrategy::Display - part.setDefaultDisplay( (KMail::Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay( node ) ); - const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter() ); - if ( mReader && node->bodyPartMemento() ) - if ( Interface::Observable * obs = node->bodyPartMemento()->asObservable() ) - obs->attach( mReader ); - switch ( result ) { - case Interface::BodyPartFormatter::AsIcon: - processResult.setNeverDisplayInline( true ); - // fall through: - case Interface::BodyPartFormatter::Failed: - defaultHandling( node, processResult ); - break; - case Interface::BodyPartFormatter::Ok: - case Interface::BodyPartFormatter::NeedContent: - // FIXME: incomplete content handling - ; + + // Only use the external plugin if we have a reader. Otherwise, just do nothing for this + // node. + if ( mReader ) { + PartNodeBodyPart part( *node, codecFor( node ) ); + // Set the default display strategy for this body part relying on the + // identity of KMail::Interface::BodyPart::Display and AttachmentStrategy::Display + part.setDefaultDisplay( (KMail::Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay( node ) ); + + writeAttachmentMarkHeader( node ); + node->setDisplayedEmbedded( true ); + Callback callback( mReader->message(), mReader ); + const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter(), callback ); + writeAttachmentMarkFooter(); + switch ( result ) { + case Interface::BodyPartFormatter::AsIcon: + processResult.setNeverDisplayInline( true ); + // fall through: + case Interface::BodyPartFormatter::Failed: + defaultHandling( node, processResult ); + break; + case Interface::BodyPartFormatter::Ok: + case Interface::BodyPartFormatter::NeedContent: + // FIXME: incomplete content handling + ; + } } } else { const BodyPartFormatter * bpf @@ -291,9 +311,13 @@ namespace KMail { << node->typeString() << '/' << node->subTypeString() << ')' << endl; - if ( bpf && !bpf->process( this, node, processResult ) ) + writeAttachmentMarkHeader( node ); + if ( bpf && !bpf->process( this, node, processResult ) ) { defaultHandling( node, processResult ); + } + writeAttachmentMarkFooter(); } + node->setProcessed( true, false ); // adjust signed/encrypted flags if inline PGP was found @@ -309,10 +333,15 @@ namespace KMail { // ### bodypartformatters. if ( !mReader ) return; - if ( attachmentStrategy() == AttachmentStrategy::hidden() && + + + const AttachmentStrategy * as = attachmentStrategy(); + if ( as && as->defaultDisplay( node ) == AttachmentStrategy::None && !showOnlyOneMimePart() && - node->parentNode() /* message is not an attachment */ ) + node->parentNode() /* message is not an attachment */ ) { + node->setDisplayedHidden( true ); return; + } bool asIcon = true; if ( showOnlyOneMimePart() ) @@ -321,25 +350,32 @@ namespace KMail { // window! asIcon = !node->hasContentDispositionInline(); else if ( !result.neverDisplayInline() ) - if ( const AttachmentStrategy * as = attachmentStrategy() ) + if ( as ) asIcon = as->defaultDisplay( node ) == AttachmentStrategy::AsIcon; // neither image nor text -> show as icon - if ( !result.isImage() - && node->type() != DwMime::kTypeText ) + if ( !result.isImage() && node->type() != DwMime::kTypeText ) asIcon = true; // if the image is not complete do not try to show it inline if ( result.isImage() && !node->msgPart().isComplete() ) asIcon = true; if ( asIcon ) { - if ( attachmentStrategy() != AttachmentStrategy::hidden() - || showOnlyOneMimePart() ) + if ( !( as && as->defaultDisplay( node ) == AttachmentStrategy::None ) || + showOnlyOneMimePart() ) { writePartIcon( &node->msgPart(), node->nodeId() ); - } else if ( result.isImage() ) + } + else { + node->setDisplayedHidden( true ); + } + } else if ( result.isImage() ) { + node->setDisplayedEmbedded( true ); writePartIcon( &node->msgPart(), node->nodeId(), true ); - else + } + else { + node->setDisplayedEmbedded( true ); writeBodyString( node->msgPart().bodyDecoded(), node->trueFromAddress(), codecFor( node ), result, false ); + } // end of ### } @@ -380,7 +416,7 @@ namespace KMail { const TQString& fromAddress, bool doCheck, TQCString* cleartextData, - std::vector paramSignatures, + const std::vector & paramSignatures, bool hideErrors ) { bool bIsOpaqueSigned = false; @@ -397,18 +433,22 @@ namespace KMail { } #ifndef NDEBUG - if ( !doCheck ) - kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: showing OpenPGP (Encrypted+Signed) data" << endl; - else - if ( data ) - kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Multipart Signed data" << endl; - else - kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Opaque Signed data" << endl; + if ( !doCheck ) { + //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: showing OpenPGP (Encrypted+Signed) data" << endl; + } + else { + if ( data ) { + //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Multipart Signed data" << endl; + } + else { + //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Opaque Signed data" << endl; + } + } #endif if ( doCheck && cryptProto ) { - kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: going to call CRYPTPLUG " - << cryptPlugLibName << endl; + //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: going to call CRYPTPLUG " + // << cryptPlugLibName << endl; } TQCString cleartext; @@ -423,9 +463,9 @@ namespace KMail { // replace simple LFs by CRLSs // according to RfC 2633, 3.1.1 Canonicalization - kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl; + //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl; cleartext = Util::lf2crlf( cleartext ); - kdDebug(5006) << " done." << endl; + //kdDebug(5006) << " done." << endl; } dumpToFile( "dat_02_reader_signedtext_after_canonicalization", @@ -437,7 +477,7 @@ namespace KMail { } std::vector signatures; - if ( doCheck ) + if ( !doCheck ) signatures = paramSignatures; PartMetaData messagePart; @@ -450,36 +490,100 @@ namespace KMail { messagePart.status = i18n("Wrong Crypto Plug-In."); messagePart.status_code = GPGME_SIG_STAT_NONE; + GpgME::Key key; + if ( doCheck && cryptProto ) { GpgME::VerificationResult result; if ( data ) { // detached - if ( Kleo::VerifyDetachedJob * const job = cryptProto->verifyDetachedJob() ) { - TQByteArray plainData = cleartext; - plainData.resize( cleartext.size() - 1 ); - result = job->exec( signaturetext, plainData ); - messagePart.auditLog = job->auditLogAsHtml(); - } else { - cryptPlugError = CANT_VERIFY_SIGNATURES; + const VerifyDetachedBodyPartMemento * m + = dynamic_cast( sign.bodyPartMemento( "verifydetached" ) ); + if ( !m ) { + Kleo::VerifyDetachedJob * job = cryptProto->verifyDetachedJob(); + if ( !job ) { + cryptPlugError = CANT_VERIFY_SIGNATURES; + // PENDING(marc) cryptProto = 0 here? + } else { + TQByteArray plainData = cleartext; + plainData.resize( cleartext.size() - 1 ); + VerifyDetachedBodyPartMemento * newM + = new VerifyDetachedBodyPartMemento( job, cryptProto->keyListJob(), signaturetext, plainData ); + if ( allowAsync() ) { + if ( newM->start() ) { + messagePart.inProgress = true; + mHasPendingAsyncJobs = true; + } else { + m = newM; + } + } else { + newM->exec(); + m = newM; + } + sign.setBodyPartMemento( "verifydetached", newM ); + } + } else if ( m->isRunning() ) { + messagePart.inProgress = true; + mHasPendingAsyncJobs = true; + m = 0; + } + + if ( m ) { + result = m->verifyResult(); + messagePart.auditLogError = m->auditLogError(); + messagePart.auditLog = m->auditLogAsHtml(); + key = m->signingKey(); } } else { // opaque - if ( Kleo::VerifyOpaqueJob * const job = cryptProto->verifyOpaqueJob() ) { - TQByteArray plainData; - result = job->exec( signaturetext, plainData ); + const VerifyOpaqueBodyPartMemento * m + = dynamic_cast( sign.bodyPartMemento( "verifyopaque" ) ); + if ( !m ) { + Kleo::VerifyOpaqueJob * job = cryptProto->verifyOpaqueJob(); + if ( !job ) { + cryptPlugError = CANT_VERIFY_SIGNATURES; + // PENDING(marc) cryptProto = 0 here? + } else { + VerifyOpaqueBodyPartMemento * newM + = new VerifyOpaqueBodyPartMemento( job, cryptProto->keyListJob(), signaturetext ); + if ( allowAsync() ) { + if ( newM->start() ) { + messagePart.inProgress = true; + mHasPendingAsyncJobs = true; + } else { + m = newM; + } + } else { + newM->exec(); + m = newM; + } + sign.setBodyPartMemento( "verifyopaque", newM ); + } + } else if ( m->isRunning() ) { + messagePart.inProgress = true; + mHasPendingAsyncJobs = true; + m = 0; + } + + if ( m ) { + result = m->verifyResult(); + const TQByteArray & plainData = m->plainText(); cleartext = TQCString( plainData.data(), plainData.size() + 1 ); - messagePart.auditLog = job->auditLogAsHtml(); - } else { - cryptPlugError = CANT_VERIFY_SIGNATURES; + messagePart.auditLogError = m->auditLogError(); + messagePart.auditLog = m->auditLogAsHtml(); + key = m->signingKey(); } } + std::stringstream ss; + ss << result; + //kdDebug(5006) << ss.str().c_str() << endl; signatures = result.signatures(); } - if ( doCheck ) - kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: returned from CRYPTPLUG" << endl; + if ( doCheck ) { + //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: returned from CRYPTPLUG" << endl; + } // ### only one signature supported if ( signatures.size() > 0 ) { - kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: found signature" << endl; + //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: found signature" << endl; GpgME::Signature signature = signatures[0]; messagePart.status_code = signatureToStatus( signature ); @@ -493,16 +597,6 @@ namespace KMail { if ( messagePart.status_code & GPGME_SIG_STAT_GOOD ) messagePart.isGoodSignature = true; - // get key for this signature - Kleo::KeyListJob *job = cryptProto->keyListJob(); - std::vector keys; - GpgME::KeyListResult keyListRes = job->exec( TQString::fromLatin1( signature.fingerprint() ), false, keys ); - GpgME::Key key; - if ( keys.size() == 1 ) - key = keys[0]; - else if ( keys.size() > 1 ) - assert( false ); // ### wtf, what should we do in this case?? - // save extended signature status flags messagePart.sigSummary = signature.summary(); @@ -543,9 +637,9 @@ namespace KMail { } } - kdDebug(5006) << "\n key id: " << messagePart.keyId - << "\n key trust: " << messagePart.keyTrust - << "\n signer: " << messagePart.signer << endl; + //kdDebug(5006) << "\n key id: " << messagePart.keyId + // << "\n key trust: " << messagePart.keyTrust + // << "\n signer: " << messagePart.signer << endl; } else { messagePart.creationTime = TQDateTime(); @@ -631,11 +725,51 @@ namespace KMail { htmlWriter()->queue( writeSigstatFooter( messagePart ) ); } - kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: done, returning " - << ( bIsOpaqueSigned ? "TRUE" : "FALSE" ) << endl; + //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: done, returning " + // << ( bIsOpaqueSigned ? "TRUE" : "FALSE" ) << endl; return bIsOpaqueSigned; } +void ObjectTreeParser::writeDecryptionInProgressBlock() { + assert( mReader ); + // PENDING(marc) find an animated icon here: + //const TQString iconName = KGlobal::instance()->iconLoader()->iconPath( "decrypted", KIcon::Small ); + const TQString decryptedData = i18n("Encrypted data not shown"); + PartMetaData messagePart; + messagePart.isDecryptable = true; + messagePart.isEncrypted = true; + messagePart.isSigned = false; + messagePart.inProgress = true; + htmlWriter()->queue( writeSigstatHeader( messagePart, + cryptoProtocol(), + TQString() ) ); + //htmlWriter()->queue( decryptedData ); + htmlWriter()->queue( writeSigstatFooter( messagePart ) ); +} + +void ObjectTreeParser::writeDeferredDecryptionBlock() { + assert( mReader ); + const TQString iconName = KGlobal::instance()->iconLoader()->iconPath( "decrypted", KIcon::Small ); + const TQString decryptedData = + "
" + + i18n("This message is encrypted.") + + "
" + "
"; + PartMetaData messagePart; + messagePart.isDecryptable = true; + messagePart.isEncrypted = true; + messagePart.isSigned = false; + mRawReplyString += decryptedData.utf8(); + htmlWriter()->queue( writeSigstatHeader( messagePart, + cryptoProtocol(), + TQString() ) ); + htmlWriter()->queue( decryptedData ); + htmlWriter()->queue( writeSigstatFooter( messagePart ) ); +} bool ObjectTreeParser::okDecryptMIME( partNode& data, TQCString& decryptedData, @@ -644,11 +778,15 @@ bool ObjectTreeParser::okDecryptMIME( partNode& data, bool showWarning, bool& passphraseError, bool& actuallyEncrypted, + bool& decryptionStarted, TQString& aErrorText, + GpgME::Error & auditLogError, TQString& auditLog ) { passphraseError = false; + decryptionStarted = false; aErrorText = TQString::null; + auditLogError = GpgME::Error(); auditLog = TQString::null; bool bDecryptionOk = false; enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT } @@ -660,19 +798,7 @@ bool ObjectTreeParser::okDecryptMIME( partNode& data, if ( cryptProto ) cryptPlugLibName = cryptProto->name(); - if ( mReader && !mReader->decryptMessage() ) { - TQString iconName = KGlobal::instance()->iconLoader()->iconPath( "decrypted", KIcon::Small ); - decryptedData = "
" - + i18n("This message is encrypted.").utf8() - + "
" - ""; - return false; - } + assert( !mReader || mReader->decryptMessage() ); if ( cryptProto && !kmkernel->contextMenuShown() ) { TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() ); @@ -698,20 +824,48 @@ bool ObjectTreeParser::okDecryptMIME( partNode& data, #endif - kdDebug(5006) << "ObjectTreeParser::decryptMIME: going to call CRYPTPLUG " - << cryptPlugLibName << endl; + //kdDebug(5006) << "ObjectTreeParser::decryptMIME: going to call CRYPTPLUG " + // << cryptPlugLibName << endl; if ( mReader ) emit mReader->noDrag(); // in case pineentry pops up, don't let kmheaders start a drag afterwards - Kleo::DecryptVerifyJob* job = cryptProto->decryptVerifyJob(); - if ( !job ) { - cryptPlugError = CANT_DECRYPT; - cryptProto = 0; - } else { - TQByteArray plainText; - const std::pair res = job->exec( ciphertext, plainText ); - const GpgME::DecryptionResult & decryptResult = res.first; - const GpgME::VerificationResult & verifyResult = res.second; + // Check whether the memento contains a result from last time: + const DecryptVerifyBodyPartMemento * m + = dynamic_cast( data.bodyPartMemento( "decryptverify" ) ); + if ( !m ) { + Kleo::DecryptVerifyJob * job = cryptProto->decryptVerifyJob(); + if ( !job ) { + cryptPlugError = CANT_DECRYPT; + cryptProto = 0; + } else { + DecryptVerifyBodyPartMemento * newM + = new DecryptVerifyBodyPartMemento( job, ciphertext ); + if ( allowAsync() ) { + if ( newM->start() ) { + decryptionStarted = true; + mHasPendingAsyncJobs = true; + } else { + m = newM; + } + } else { + newM->exec(); + m = newM; + } + data.setBodyPartMemento( "decryptverify", newM ); + } + } else if ( m->isRunning() ) { + decryptionStarted = true; + mHasPendingAsyncJobs = true; + m = 0; + } + + if ( m ) { + const TQByteArray & plainText = m->plainText(); + const GpgME::DecryptionResult & decryptResult = m->decryptResult(); + const GpgME::VerificationResult & verifyResult = m->verifyResult(); + std::stringstream ss; + ss << decryptResult << '\n' << verifyResult; + //kdDebug(5006) << ss.str().c_str() << endl; signatureFound = verifyResult.signatures().size() > 0; signatures = verifyResult.signatures(); bDecryptionOk = !decryptResult.error(); @@ -719,10 +873,11 @@ bool ObjectTreeParser::okDecryptMIME( partNode& data, || decryptResult.error().code() == GPG_ERR_NO_SECKEY; actuallyEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA; aErrorText = TQString::fromLocal8Bit( decryptResult.error().asString() ); - auditLog = job->auditLogAsHtml(); + auditLogError = m->auditLogError(); + auditLog = m->auditLogAsHtml(); - kdDebug(5006) << "ObjectTreeParser::decryptMIME: returned from CRYPTPLUG" - << endl; + //kdDebug(5006) << "ObjectTreeParser::decryptMIME: returned from CRYPTPLUG" + // << endl; if ( bDecryptionOk ) decryptedData = TQCString( plainText.data(), plainText.size() + 1 ); else if ( mReader && showWarning ) { @@ -819,6 +974,7 @@ bool ObjectTreeParser::okDecryptMIME( partNode& data, showOnlyOneMimePart() ) { if ( mReader->htmlMail() ) { + curNode->setDisplayedEmbedded( true ); // ---Sven's strip and from end of attachment start- // We must fo this, or else we will see only 1st inlined html // attachment. It is IMHO enough to search only for and @@ -909,7 +1065,7 @@ namespace KMail { if ( nextDelim < 0) return false; - kdDebug(5006) << " processing old style Mailman digest" << endl; + //kdDebug(5006) << " processing old style Mailman digest" << endl; //if ( curNode->mRoot ) // curNode = curNode->mRoot; @@ -952,7 +1108,7 @@ namespace KMail { if ( -1 < thisEoL ) subject.truncate( thisEoL ); } - kdDebug(5006) << " embedded message found: \"" << subject << "\"" << endl; + //kdDebug(5006) << " embedded message found: \"" << subject << "\"" << endl; insertAndParseNewChildNode( *curNode, &*partStr, subject, true ); @@ -1029,8 +1185,7 @@ namespace KMail { TQString htmlStr = "" "
"; if ( !fileName.isEmpty() ) - htmlStr += "" + htmlStr += "asHREF( "body" ) + "\">" + label + ""; else htmlStr += label; @@ -1043,9 +1198,11 @@ namespace KMail { // process old style not-multipart Mailman messages to // enable verification of the embedded messages' signatures if ( !isMailmanMessage( curNode ) || - !processMailmanMessage( curNode ) ) + !processMailmanMessage( curNode ) ) { writeBodyString( mRawReplyString, curNode->trueFromAddress(), codecFor( curNode ), result, !bDrawFrame ); + curNode->setDisplayedEmbedded( true ); + } if ( bDrawFrame ) htmlWriter()->queue( "
" ); @@ -1065,7 +1222,30 @@ namespace KMail { mTextualContentCharset = otp.textualContentCharset(); } + TQString ObjectTreeParser::defaultToltecReplacementText() + { + return i18n( "This message is a Toltec Groupware object, it can only be viewed with " + "Microsoft Outlook in combination with the Toltec connector." ); + } + + bool ObjectTreeParser::processToltecMail( partNode *node ) + { + if ( !node || !mHtmlWriter || !GlobalSettings::self()->showToltecReplacementText() || + !node->isToltecMessage() || mShowRawToltecMail ) + return false; + + htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() ); + htmlWriter()->queue( "

" + + i18n( "Show Raw Message" ) + "" ); + return true; + } + bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) { + + if ( processToltecMail( node ) ) { + return true; + } + partNode * child = node->firstChild(); if ( !child ) return false; @@ -1203,20 +1383,28 @@ namespace KMail { CryptoProtocolSaver cpws( this, useThisCryptProto ); if ( partNode * dataChild = data->firstChild() ) { - kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; + //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; stdChildHandling( dataChild ); - kdDebug(5006) << "\n-----> Returning from parseObjectTree( curNode->mChild )\n" << endl; + //kdDebug(5006) << "\n-----> Returning from parseObjectTree( curNode->mChild )\n" << endl; return true; } - kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl; - PartMetaData messagePart; node->setEncryptionState( KMMsgFullyEncrypted ); + + if ( mReader && !mReader->decryptMessage() ) { + writeDeferredDecryptionBlock(); + data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed + return true; + } + + //kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl; + PartMetaData messagePart; TQCString decryptedData; bool signatureFound; std::vector signatures; bool passphraseError; bool actuallyEncrypted = true; + bool decryptionStarted; bool bOkDecrypt = okDecryptMIME( *data, decryptedData, @@ -1225,9 +1413,16 @@ namespace KMail { true, passphraseError, actuallyEncrypted, + decryptionStarted, messagePart.errorText, + messagePart.auditLogError, messagePart.auditLog ); + if ( decryptionStarted ) { + writeDecryptionInProgressBlock(); + return true; + } + // paint the frame if ( mReader ) { messagePart.isDecryptable = bOkDecrypt; @@ -1287,17 +1482,17 @@ namespace KMail { return false; if ( partNode * child = node->firstChild() ) { - kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; + //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; ObjectTreeParser otp( mReader, cryptoProtocol() ); otp.parseObjectTree( child ); mRawReplyString += otp.rawReplyString(); mTextualContent += otp.textualContent(); if ( !otp.textualContentCharset().isEmpty() ) mTextualContentCharset = otp.textualContentCharset(); - kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl; + //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl; return true; } - kdDebug(5006) << "\n-----> Initially processing data of embedded RfC 822 message\n" << endl; + //kdDebug(5006) << "\n-----> Initially processing data of embedded RfC 822 message\n" << endl; // paint the frame PartMetaData messagePart; if ( mReader ) { @@ -1310,7 +1505,7 @@ namespace KMail { htmlWriter()->queue( writeSigstatHeader( messagePart, cryptoProtocol(), node->trueFromAddress(), - filename ) ); + node ) ); } TQCString rfc822messageStr( node->msgPart().bodyDecoded() ); // display the headers of the encapsulated message @@ -1319,14 +1514,16 @@ namespace KMail { rfc822DwMessage->Parse(); KMMessage rfc822message( rfc822DwMessage ); node->setFromAddress( rfc822message.from() ); - kdDebug(5006) << "\n-----> Store RfC 822 message header \"From: " << rfc822message.from() << "\"\n" << endl; + //kdDebug(5006) << "\n-----> Store RfC 822 message header \"From: " << rfc822message.from() << "\"\n" << endl; if ( mReader ) htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) ); //mReader->parseMsgHeader( &rfc822message ); // display the body of the encapsulated message insertAndParseNewChildNode( *node, &*rfc822messageStr, - "encapsulated message" ); + "encapsulated message", false /*append*/, + false /*add to textual content*/ ); + node->setDisplayedEmbedded( true ); if ( mReader ) htmlWriter()->queue( writeSigstatFooter( messagePart ) ); return true; @@ -1335,14 +1532,14 @@ namespace KMail { bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) { if ( partNode * child = node->firstChild() ) { - kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; + //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; ObjectTreeParser otp( mReader, cryptoProtocol() ); otp.parseObjectTree( child ); mRawReplyString += otp.rawReplyString(); mTextualContent += otp.textualContent(); if ( !otp.textualContentCharset().isEmpty() ) mTextualContentCharset = otp.textualContentCharset(); - kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl; + //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl; return true; } @@ -1350,7 +1547,7 @@ namespace KMail { if ( node->parentNode() && DwMime::kTypeMultipart == node->parentNode()->type() && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) { - kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl; + //kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl; node->setEncryptionState( KMMsgFullyEncrypted ); if ( keepEncryptions() ) { const TQCString cstr = node->msgPart().bodyDecoded(); @@ -1358,6 +1555,8 @@ namespace KMail { writeBodyString( cstr, node->trueFromAddress(), codecFor( node ), result, false ); mRawReplyString += cstr; + } else if ( mReader && !mReader->decryptMessage() ) { + writeDeferredDecryptionBlock(); } else { /* ATTENTION: This code is to be replaced by the planned 'auto-detect' feature. @@ -1369,6 +1568,7 @@ namespace KMail { std::vector signatures; bool passphraseError; bool actuallyEncrypted = true; + bool decryptionStarted; bool bOkDecrypt = okDecryptMIME( *node, decryptedData, @@ -1377,9 +1577,16 @@ namespace KMail { true, passphraseError, actuallyEncrypted, + decryptionStarted, messagePart.errorText, + messagePart.auditLogError, messagePart.auditLog ); + if ( decryptionStarted ) { + writeDecryptionInProgressBlock(); + return true; + } + // paint the frame if ( mReader ) { messagePart.isDecryptable = bOkDecrypt; @@ -1415,18 +1622,18 @@ namespace KMail { bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) { if ( partNode * child = node->firstChild() ) { - kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; + //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; ObjectTreeParser otp( mReader, cryptoProtocol() ); otp.parseObjectTree( child ); mRawReplyString += otp.rawReplyString(); mTextualContent += otp.textualContent(); if ( !otp.textualContentCharset().isEmpty() ) mTextualContentCharset = otp.textualContentCharset(); - kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl; + //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl; return true; } - kdDebug(5006) << "\n-----> Initially processing signed and/or encrypted data\n" << endl; + //kdDebug(5006) << "\n-----> Initially processing signed and/or encrypted data\n" << endl; if ( !node->dwPart() || !node->dwPart()->hasHeaders() ) return false; @@ -1445,7 +1652,7 @@ namespace KMail { const TQByteArray certData = node->msgPart().bodyDecodedBinary(); - Kleo::ImportJob *import = smimeCrypto->importJob(); + const STD_NAMESPACE_PREFIX auto_ptr import( smimeCrypto->importJob() ); const GpgME::ImportResult res = import->exec( certData ); if ( res.error() ) { htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.
" @@ -1491,11 +1698,14 @@ namespace KMail { htmlWriter()->queue( i18n( "Failed: %1 (%2)" ) .arg( (*it).fingerprint(), TQString::fromLocal8Bit( (*it).error().asString() ) ) ); - else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) - if ( (*it).status() & GpgME::Import::ContainedSecretKey ) + else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) { + if ( (*it).status() & GpgME::Import::ContainedSecretKey ) { htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) ); - else + } + else { htmlWriter()->queue( i18n( "New or changed: %1" ).arg( (*it).fingerprint() ) ); + } + } htmlWriter()->queue( "
" ); } @@ -1520,10 +1730,12 @@ namespace KMail { // if we either *know* that it is an encrypted message part // or there is neither signed nor encrypted parameter. if ( !isSigned ) { - if ( isEncrypted ) - kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: enveloped (encrypted) data" << endl; - else - kdDebug(5006) << "pkcs7 mime - type unknown - enveloped (encrypted) data ?" << endl; + if ( isEncrypted ) { + //kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: enveloped (encrypted) data" << endl; + } + else { + //kdDebug(5006) << "pkcs7 mime - type unknown - enveloped (encrypted) data ?" << endl; + } TQCString decryptedData; PartMetaData messagePart; messagePart.isEncrypted = true; @@ -1532,57 +1744,69 @@ namespace KMail { std::vector signatures; bool passphraseError; bool actuallyEncrypted = true; + bool decryptionStarted; - if ( okDecryptMIME( *node, + if ( mReader && !mReader->decryptMessage() ) { + writeDeferredDecryptionBlock(); + isEncrypted = true; + signTestNode = 0; // PENDING(marc) to be abs. sure, we'd need to have to look at the content + } else { + const bool bOkDecrypt = okDecryptMIME( *node, decryptedData, signatureFound, signatures, false, passphraseError, actuallyEncrypted, + decryptionStarted, messagePart.errorText, - messagePart.auditLog ) ) { - kdDebug(5006) << "pkcs7 mime - encryption found - enveloped (encrypted) data !" << endl; - isEncrypted = true; - node->setEncryptionState( KMMsgFullyEncrypted ); - signTestNode = 0; - // paint the frame - messagePart.isDecryptable = true; - if ( mReader ) - htmlWriter()->queue( writeSigstatHeader( messagePart, - cryptoProtocol(), - node->trueFromAddress() ) ); - insertAndParseNewChildNode( *node, - &*decryptedData, - "encrypted data" ); - if ( mReader ) - htmlWriter()->queue( writeSigstatFooter( messagePart ) ); - } else { - // decryption failed, which could be because the part was encrypted but - // decryption failed, or because we didn't know if it was encrypted, tried, - // and failed. If the message was not actually encrypted, we continue - // assuming it's signed - if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) { + messagePart.auditLogError, + messagePart.auditLog ); + if ( decryptionStarted ) { + writeDecryptionInProgressBlock(); + return true; + } + if ( bOkDecrypt ) { + //kdDebug(5006) << "pkcs7 mime - encryption found - enveloped (encrypted) data !" << endl; isEncrypted = true; + node->setEncryptionState( KMMsgFullyEncrypted ); signTestNode = 0; - } - - if ( isEncrypted ) { - kdDebug(5006) << "pkcs7 mime - ERROR: COULD NOT DECRYPT enveloped data !" << endl; // paint the frame - messagePart.isDecryptable = false; - if ( mReader ) { + messagePart.isDecryptable = true; + if ( mReader ) htmlWriter()->queue( writeSigstatHeader( messagePart, cryptoProtocol(), node->trueFromAddress() ) ); - if ( mReader->decryptMessage() ) - writePartIcon( &node->msgPart(), node->nodeId() ); - else - htmlWriter()->queue( TQString::fromUtf8( decryptedData ) ); + insertAndParseNewChildNode( *node, + &*decryptedData, + "encrypted data" ); + if ( mReader ) htmlWriter()->queue( writeSigstatFooter( messagePart ) ); - } } else { - kdDebug(5006) << "pkcs7 mime - NO encryption found" << endl; + // decryption failed, which could be because the part was encrypted but + // decryption failed, or because we didn't know if it was encrypted, tried, + // and failed. If the message was not actually encrypted, we continue + // assuming it's signed + if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) { + isEncrypted = true; + signTestNode = 0; + } + + if ( isEncrypted ) { + //kdDebug(5006) << "pkcs7 mime - ERROR: COULD NOT DECRYPT enveloped data !" << endl; + // paint the frame + messagePart.isDecryptable = false; + if ( mReader ) { + htmlWriter()->queue( writeSigstatHeader( messagePart, + cryptoProtocol(), + node->trueFromAddress() ) ); + assert( mReader->decryptMessage() ); // handled above + writePartIcon( &node->msgPart(), node->nodeId() ); + htmlWriter()->queue( writeSigstatFooter( messagePart ) ); + } + } else { + //kdDebug(5006) << "pkcs7 mime - NO encryption found" << endl; + } } } if ( isEncrypted ) @@ -1591,10 +1815,12 @@ namespace KMail { // We now try signature verification if necessarry. if ( signTestNode ) { - if ( isSigned ) - kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: opaque signed data" << endl; - else - kdDebug(5006) << "pkcs7 mime - type unknown - opaque signed data ?" << endl; + if ( isSigned ) { + //kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: opaque signed data" << endl; + } + else { + //kdDebug(5006) << "pkcs7 mime - type unknown - opaque signed data ?" << endl; + } bool sigFound = writeOpaqueOrMultipartSignedData( 0, *signTestNode, @@ -1605,14 +1831,14 @@ namespace KMail { isEncrypted ); if ( sigFound ) { if ( !isSigned ) { - kdDebug(5006) << "pkcs7 mime - signature found - opaque signed data !" << endl; + //kdDebug(5006) << "pkcs7 mime - signature found - opaque signed data !" << endl; isSigned = true; } signTestNode->setSignatureState( KMMsgFullySigned ); if ( signTestNode != node ) node->setSignatureState( KMMsgFullySigned ); } else { - kdDebug(5006) << "pkcs7 mime - NO signature found :-(" << endl; + //kdDebug(5006) << "pkcs7 mime - NO signature found :-(" << endl; } } @@ -1666,8 +1892,8 @@ bool ObjectTreeParser::decryptChiasmus( const TQByteArray& data, TQByteArray& bo GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() ); assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() ); - Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", TQMap() ); - if ( !job ) { + const STD_NAMESPACE_PREFIX auto_ptr job( chiasmus->specialJob( "x-decrypt", TQMap() ) ); + if ( !job.get() ) { errorText = i18n( "Chiasmus backend does not offer the " "\"x-decrypt\" function. Please report this bug." ); return false; @@ -1761,8 +1987,7 @@ bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessR TQString htmlStr = "" ""; + + tmpStr += "
"; if ( !fileName.isEmpty() ) - htmlStr += "" + htmlStr += "asHREF( "body" ) + "\">" + label + ""; else htmlStr += label; @@ -1816,8 +2041,6 @@ bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessR if ( !mReader || !msgPart ) return; - kdDebug(5006) << "writePartIcon: PartNum: " << partNum << endl; - TQString label = msgPart->fileName(); if( label.isEmpty() ) label = msgPart->name(); @@ -1831,9 +2054,7 @@ bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessR TQString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum ); - TQString href = fileName.isEmpty() ? - "part://" + TQString::number( partNum + 1 ) : - "file:" + KURL::encode_string( fileName ) ; + TQString href = TQString( "attachment:%1?place=body" ).arg( partNum ); TQString iconName; if( inlineImage ) @@ -1854,13 +2075,13 @@ bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessR if( inlineImage ) // show the filename of the image below the embedded image htmlWriter()->queue( "
" - "" + "" "
" "" "
" + comment + "

" ); else - // show the filename next to the image + // show the filename next to the icon htmlWriter()->queue( "" @@ -2077,16 +2298,29 @@ static TQString beginVerboseSigstatHeader() return ""; html += "
"; } -static TQString makeShowAuditLogLink( const TQString & auditLog ) { - if ( auditLog.isEmpty() ) - return i18n("No Audit Log available"); +static TQString makeShowAuditLogLink( const GpgME::Error & err, const TQString & auditLog ) { + if ( const unsigned int code = err.code() ) { + if ( code == GPG_ERR_NOT_IMPLEMENTED ) { + //kdDebug(5006) << "makeShowAuditLogLink: not showing link (not implemented)" << endl; + return TQString(); + } else if ( code == GPG_ERR_NO_DATA ) { + //kdDebug(5006) << "makeShowAuditLogLink: not showing link (not available)" << endl; + return i18n("No Audit Log available"); + } else { + return i18n("Error Retrieving Audit Log: %1").arg( TQString::fromLocal8Bit( err.asString() ) ); + } + } - KURL url; - url.setProtocol( "kmail" ); - url.setPath( "showAuditLog" ); - url.addQueryItem( "log", auditLog ); + if ( !auditLog.isEmpty() ) { + KURL url; + url.setProtocol( "kmail" ); + url.setPath( "showAuditLog" ); + url.addQueryItem( "log", auditLog ); - return "" + i18n("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") + ""; + return "" + i18n("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") + ""; + } + + return TQString::null; } static TQString endVerboseSigstatHeader( const PartMetaData & pmd ) @@ -2097,7 +2331,7 @@ static TQString endVerboseSigstatHeader( const PartMetaData & pmd ) html += i18n( "Hide Details" ); html += "
"; - html += makeShowAuditLogLink( pmd.auditLog ); + html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog ); html += "
"; return html; } @@ -2105,7 +2339,7 @@ static TQString endVerboseSigstatHeader( const PartMetaData & pmd ) TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block, const Kleo::CryptoBackend::Protocol * cryptProto, const TQString & fromAddress, - const TQString & filename ) + partNode *node ) { const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ); TQString signer = block.signer; @@ -2118,12 +2352,11 @@ TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block, { htmlStr += "" "\n"; @@ -275,12 +279,16 @@ void HtmlExport::createEvent (TQTextStream *ts, Event *event, if (event->isMultiDay() && (event->dtStart().date() != date)) { *ts << " \n"; } else { - *ts << " \n"; + *ts << " \n"; } if (event->isMultiDay() && (event->dtEnd().date() != date)) { *ts << " \n"; } else { - *ts << " \n"; + *ts << " \n"; } } else { *ts << " \n"; @@ -459,7 +467,7 @@ void HtmlExport::createTodo (TQTextStream *ts,Todo *todo) if (completed) *ts << " class=\"done\""; *ts << ">\n"; if (todo->hasDueDate()) { - *ts << " " << todo->dtDueDateStr() << "\n"; + *ts << " " << IncidenceFormatter::dateToString( todo->dtDue( true ) ) << "\n"; } else { *ts << "  \n"; } diff --git a/libkcal/icalformatimpl.cpp b/libkcal/icalformatimpl.cpp index a1655409..613d492e 100644 --- a/libkcal/icalformatimpl.cpp +++ b/libkcal/icalformatimpl.cpp @@ -55,6 +55,7 @@ static TQDateTime ICalDate2TQDate(const icaltimetype& t) return TQDateTime(TQDate(year,t.month,t.day), TQTime(t.hour,t.minute,t.second)); } +/* static void _dumpIcaltime( const icaltimetype& t) { kdDebug(5800) << "--- Y: " << t.year << " M: " << t.month << " D: " << t.day @@ -64,6 +65,16 @@ static void _dumpIcaltime( const icaltimetype& t) kdDebug(5800) << "--- isUtc: " << icaltime_is_utc( t )<< endl; kdDebug(5800) << "--- zoneId: " << icaltimezone_get_tzid( const_cast( t.zone ) )<< endl; } +*/ + +static TQString quoteForParam( const TQString &text ) +{ + TQString tmp = text; + tmp.remove( '"' ); + if ( tmp.contains( ';' ) || tmp.contains( ':' ) || tmp.contains( ',' ) ) + return tmp; // libical quotes in this case already, see icalparameter_as_ical_string() + return TQString::fromLatin1( "\"" ) + tmp + TQString::fromLatin1( "\"" ); +} const int gSecondsPerMinute = 60; const int gSecondsPerHour = gSecondsPerMinute * 60; @@ -565,7 +576,7 @@ icalproperty *ICalFormatImpl::writeOrganizer( const Person &organizer ) icalproperty *p = icalproperty_new_organizer("MAILTO:" + organizer.email().utf8()); if (!organizer.name().isEmpty()) { - icalproperty_add_parameter( p, icalparameter_new_cn(organizer.name().utf8()) ); + icalproperty_add_parameter( p, icalparameter_new_cn(quoteForParam(organizer.name()).utf8()) ); } // TODO: Write dir, sent-by and language @@ -578,7 +589,7 @@ icalproperty *ICalFormatImpl::writeAttendee(Attendee *attendee) icalproperty *p = icalproperty_new_attendee("mailto:" + attendee->email().utf8()); if (!attendee->name().isEmpty()) { - icalproperty_add_parameter(p,icalparameter_new_cn(attendee->name().utf8())); + icalproperty_add_parameter(p,icalparameter_new_cn(quoteForParam(attendee->name()).utf8())); } @@ -649,14 +660,15 @@ icalproperty *ICalFormatImpl::writeAttendee(Attendee *attendee) return p; } -icalproperty *ICalFormatImpl::writeAttachment(Attachment *att) +icalproperty *ICalFormatImpl::writeAttachment( Attachment *att ) { icalattach *attach; - if (att->isUri()) - attach = icalattach_new_from_url( att->uri().utf8().data()); - else - attach = icalattach_new_from_data ( (unsigned char *)att->data(), 0, 0); - icalproperty *p = icalproperty_new_attach(attach); + if ( att->isUri() ) { + attach = icalattach_new_from_url( att->uri().utf8().data() ); + } else { + attach = icalattach_new_from_data ( (unsigned char *)att->data(), 0, 0 ); + } + icalproperty *p = icalproperty_new_attach( attach ); if ( !att->mimeType().isEmpty() ) { icalproperty_add_parameter( p, @@ -853,7 +865,7 @@ icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm) for (TQValueList::Iterator ad = addresses.begin(); ad != addresses.end(); ++ad) { icalproperty *p = icalproperty_new_attendee("MAILTO:" + (*ad).email().utf8()); if (!(*ad).name().isEmpty()) { - icalproperty_add_parameter(p,icalparameter_new_cn((*ad).name().utf8())); + icalproperty_add_parameter(p,icalparameter_new_cn(quoteForParam((*ad).name()).utf8())); } icalcomponent_add_property(a,p); } @@ -892,7 +904,7 @@ icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm) offset = alarm->startOffset(); else offset = alarm->endOffset(); - trigger.duration = icaldurationtype_from_int( offset.asSeconds() ); + trigger.duration = writeICalDuration( offset.asSeconds() ); } icalproperty *p = icalproperty_new_trigger(trigger); if ( alarm->hasEndOffset() ) @@ -903,7 +915,7 @@ icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm) if (alarm->repeatCount()) { icalcomponent_add_property(a,icalproperty_new_repeat(alarm->repeatCount())); icalcomponent_add_property(a,icalproperty_new_duration( - icaldurationtype_from_int(alarm->snoozeTime()*60))); + writeICalDuration(alarm->snoozeTime().value()))); } // Custom properties @@ -1000,9 +1012,9 @@ Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezon readIncidence( vevent, tz, event); - icalproperty *p = icalcomponent_get_first_property(vevent,ICAL_ANY_PROPERTY); + icalproperty *p = icalcomponent_get_first_property( vevent, ICAL_ANY_PROPERTY ); -// int intvalue; + // int intvalue; icaltimetype icaltime; TQStringList categories; @@ -1010,16 +1022,19 @@ Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezon bool dtEndProcessed = false; - while (p) { - icalproperty_kind kind = icalproperty_isa(p); - switch (kind) { + while ( p ) { + icalproperty_kind kind = icalproperty_isa( p ); + switch ( kind ) { case ICAL_DTEND_PROPERTY: // start date and time - icaltime = icalproperty_get_dtend(p); - if (icaltime.is_date) { + icaltime = icalproperty_get_dtend( p ); + if ( icaltime.is_date ) { // End date is non-inclusive TQDate endDate = readICalDate( icaltime ).addDays( -1 ); - if ( mCompat ) mCompat->fixFloatingEnd( endDate ); + if ( mCompat ) { + mCompat->fixFloatingEnd( endDate ); + } + if ( endDate < event->dtStart().date() ) { endDate = event->dtStart().date(); } @@ -1032,26 +1047,26 @@ Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezon break; case ICAL_RELATEDTO_PROPERTY: // related event (parent) - event->setRelatedToUid(TQString::fromUtf8(icalproperty_get_relatedto(p))); - mEventsRelate.append(event); + event->setRelatedToUid( TQString::fromUtf8( icalproperty_get_relatedto( p ) ) ); + mEventsRelate.append( event ); break; - case ICAL_TRANSP_PROPERTY: // Transparency - transparency = icalproperty_get_transp(p); - if( transparency == ICAL_TRANSP_TRANSPARENT ) + transparency = icalproperty_get_transp( p ); + if ( transparency == ICAL_TRANSP_TRANSPARENT ) { event->setTransparency( Event::Transparent ); - else + } else { event->setTransparency( Event::Opaque ); + } break; default: -// kdDebug(5800) << "ICALFormat::readEvent(): Unknown property: " << kind -// << endl; + // kdDebug(5800) << "ICALFormat::readEvent(): Unknown property: " << kind + // << endl; break; } - p = icalcomponent_get_next_property(vevent,ICAL_ANY_PROPERTY); + p = icalcomponent_get_next_property( vevent, ICAL_ANY_PROPERTY ); } // according to rfc2445 the dtend shouldn't be written when it equals @@ -1060,13 +1075,15 @@ Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezon event->setDtEnd( event->dtStart() ); } - TQString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); - if (!msade.isEmpty()) { - bool floats = (msade == TQString::fromLatin1("TRUE")); + const TQString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); + if ( !msade.isEmpty() ) { + const bool floats = ( msade == TQString::fromLatin1("TRUE") ); event->setFloats(floats); } - if ( mCompat ) mCompat->fixEmptySummary( event ); + if ( mCompat ) { + mCompat->fixEmptySummary( event ); + } return event; } @@ -1096,7 +1113,8 @@ FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy) freebusy->setDtEnd(readICalDateTime(p, icaltime)); break; - case ICAL_FREEBUSY_PROPERTY: { //Any FreeBusy Times + case ICAL_FREEBUSY_PROPERTY: //Any FreeBusy Times + { icalperiodtype icalperiod = icalproperty_get_freebusy(p); TQDateTime period_start = readICalDateTime(p, icalperiod.start); Period period; @@ -1107,12 +1125,21 @@ FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy) Duration duration = readICalDuration( icalperiod.duration ); period = Period(period_start, duration); } - TQCString param = icalproperty_get_parameter_as_string( p, "X-SUMMARY" ); - period.setSummary( TQString::fromUtf8( KCodecs::base64Decode( param ) ) ); - param = icalproperty_get_parameter_as_string( p, "X-LOCATION" ); - period.setLocation( TQString::fromUtf8( KCodecs::base64Decode( param ) ) ); + icalparameter *param = icalproperty_get_first_parameter( p, ICAL_X_PARAMETER ); + while ( param ) { + if ( strncmp( icalparameter_get_xname( param ), "X-SUMMARY", 9 ) == 0 ) { + period.setSummary( TQString::fromUtf8( + KCodecs::base64Decode( icalparameter_get_xvalue( param ) ) ) ); + } + if ( strncmp( icalparameter_get_xname( param ), "X-LOCATION", 10 ) == 0 ) { + period.setLocation( TQString::fromUtf8( + KCodecs::base64Decode( icalparameter_get_xvalue( param ) ) ) ); + } + param = icalproperty_get_next_parameter( p, ICAL_X_PARAMETER ); + } periods.append( period ); - break;} + break; + } default: // kdDebug(5800) << "ICalFormatImpl::readFreeBusy(): Unknown property: " @@ -1256,31 +1283,70 @@ Attachment *ICalFormatImpl::readAttachment(icalproperty *attach) { Attachment *attachment = 0; - icalvalue_kind value_kind = icalvalue_isa(icalproperty_get_value(attach)); + const char *p; + icalvalue *value = icalproperty_get_value( attach ); - if ( value_kind == ICAL_ATTACH_VALUE || value_kind == ICAL_BINARY_VALUE ) { - icalattach *a = icalproperty_get_attach(attach); - - int isurl = icalattach_get_is_url (a); - if (isurl == 0) - attachment = new Attachment((const char*)icalattach_get_data(a)); - else { - attachment = new Attachment(TQString::fromUtf8(icalattach_get_url(a))); + switch( icalvalue_isa( value ) ) { + case ICAL_ATTACH_VALUE: + { + icalattach *a = icalproperty_get_attach( attach ); + if ( !icalattach_get_is_url( a ) ) { + p = (const char *)icalattach_get_data( a ); + if ( p ) { + attachment = new Attachment( p ); + } + } else { + p = icalattach_get_url( a ); + if ( p ) { + attachment = new Attachment( TQString::fromUtf8( p ) ); + } + } + break; + } + case ICAL_BINARY_VALUE: + { + icalattach *a = icalproperty_get_attach( attach ); + p = (const char *)icalattach_get_data( a ); + if ( p ) { + attachment = new Attachment( p ); } + break; } - else if ( value_kind == ICAL_URI_VALUE ) { - attachment = new Attachment(TQString::fromUtf8(icalvalue_get_uri(icalproperty_get_value(attach)))); + case ICAL_URI_VALUE: + p = icalvalue_get_uri( value ); + attachment = new Attachment( TQString::fromUtf8( p ) ); + break; + default: + break; } - icalparameter *p = icalproperty_get_first_parameter(attach, ICAL_FMTTYPE_PARAMETER); - if (p && attachment) - attachment->setMimeType(TQString(icalparameter_get_fmttype(p))); + if ( attachment ) { + icalparameter *p = + icalproperty_get_first_parameter( attach, ICAL_FMTTYPE_PARAMETER ); + if ( p ) { + attachment->setMimeType( TQString( icalparameter_get_fmttype( p ) ) ); + } - p = icalproperty_get_first_parameter(attach,ICAL_X_PARAMETER); - while (p) { - if ( strncmp (icalparameter_get_xname(p), "X-LABEL", 7) == 0 ) - attachment->setLabel( icalparameter_get_xvalue(p) ); - p = icalproperty_get_next_parameter(attach, ICAL_X_PARAMETER); + p = icalproperty_get_first_parameter( attach, ICAL_X_PARAMETER ); + while ( p ) { + TQString xname = TQString( icalparameter_get_xname( p ) ).upper(); + TQString xvalue = TQString::fromUtf8( icalparameter_get_xvalue( p ) ); + if ( xname == "X-CONTENT-DISPOSITION" ) { + attachment->setShowInline( xvalue.lower() == "inline" ); + } + if ( xname == "X-LABEL" ) { + attachment->setLabel( xvalue ); + } + p = icalproperty_get_next_parameter( attach, ICAL_X_PARAMETER ); + } + + p = icalproperty_get_first_parameter( attach, ICAL_X_PARAMETER ); + while ( p ) { + if ( strncmp( icalparameter_get_xname( p ), "X-LABEL", 7 ) == 0 ) { + attachment->setLabel( TQString::fromUtf8( icalparameter_get_xvalue( p ) ) ); + } + p = icalproperty_get_next_parameter( attach, ICAL_X_PARAMETER ); + } } return attachment; @@ -1482,32 +1548,48 @@ void ICalFormatImpl::readIncidenceBase(icalcomponent *parent,IncidenceBase *inci { icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY); - while (p) { - icalproperty_kind kind = icalproperty_isa(p); + bool uidProcessed = false; + + while ( p ) { + icalproperty_kind kind = icalproperty_isa( p ); switch (kind) { case ICAL_UID_PROPERTY: // unique id - incidenceBase->setUid(TQString::fromUtf8(icalproperty_get_uid(p))); + uidProcessed = true; + incidenceBase->setUid( TQString::fromUtf8(icalproperty_get_uid( p ) ) ); break; case ICAL_ORGANIZER_PROPERTY: // organizer - incidenceBase->setOrganizer( readOrganizer(p)); + incidenceBase->setOrganizer( readOrganizer( p ) ); break; case ICAL_ATTENDEE_PROPERTY: // attendee - incidenceBase->addAttendee(readAttendee(p)); + incidenceBase->addAttendee( readAttendee( p ) ); break; case ICAL_COMMENT_PROPERTY: incidenceBase->addComment( - TQString::fromUtf8(icalproperty_get_comment(p))); + TQString::fromUtf8( icalproperty_get_comment( p ) ) ); break; default: break; } - p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY); + p = icalcomponent_get_next_property( parent, ICAL_ANY_PROPERTY ); + } + + if ( !uidProcessed ) { + kdWarning() << "The incidence didn't have any UID! Report a bug " + << "to the application that generated this file." + << endl; + + // Our in-memory incidence has a random uid generated in Event's ctor. + // Make it empty so it matches what's in the file: + incidenceBase->setUid( TQString() ); + + // Otherwise, next time we read the file, this function will return + // an event with another random uid and we will have two events in the calendar. } // kpilot stuff @@ -1736,7 +1818,7 @@ void ICalFormatImpl::readAlarm(icalcomponent *alarm,Incidence *incidence) } case ICAL_DURATION_PROPERTY: { icaldurationtype duration = icalproperty_get_duration(p); - ialarm->setSnoozeTime(icaldurationtype_as_int(duration)/60); + ialarm->setSnoozeTime( readICalDuration( duration ) ); break; } case ICAL_REPEAT_PROPERTY: @@ -1937,13 +2019,16 @@ TQDate ICalFormatImpl::readICalDate(icaltimetype t) icaldurationtype ICalFormatImpl::writeICalDuration(int seconds) { + // should be able to use icaldurationtype_from_int(), except we know + // that some older tools do not properly support weeks. So we never + // set a week duration, only days + icaldurationtype d; d.is_neg = (seconds<0)?1:0; if (seconds<0) seconds = -seconds; - d.weeks = seconds / gSecondsPerWeek; - seconds %= gSecondsPerWeek; + d.weeks = 0; d.days = seconds / gSecondsPerDay; seconds %= gSecondsPerDay; d.hours = seconds / gSecondsPerHour; @@ -2001,7 +2086,7 @@ icalcomponent *ICalFormatImpl::createCalendarComponent(Calendar *cal) // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc. // and break it down from its tree-like format into the dictionary format // that is used internally in the ICalFormatImpl. -bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar) +bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar ) { // this function will populate the caldict dictionary and other event // lists. It turns vevents into Events and then inserts them. @@ -2031,6 +2116,14 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar) return false; } else { const char *version = icalproperty_get_version(p); + if ( !version ) { + kdDebug(5800) << "No VERSION property found" << endl; + mParent->setException( new ErrorFormat( + ErrorFormat::CalVersionUnknown, + i18n( "No VERSION property found" ) ) ); + return false; + } + // kdDebug(5800) << "VCALENDAR version: '" << version << "'" << endl; if (strcmp(version,"1.0") == 0) { @@ -2071,7 +2164,11 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar) TQString originalUid = todo->uid(); todo->setUid(originalUid + QString("-recur-%1").arg(todo->recurrenceID().toTime_t())); if (!cal->todo(todo->uid())) { - cal->addTodo(todo); + if ( !cal->addTodo( todo ) ) { + cal->endBatchAdding(); + // If the user pressed cancel, return true, it's not an error. + return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel; + } if (!cal->event(originalUid)) { printf("FIXME! [WARNING] Parent for child event does not yet exist!\n\r"); } @@ -2085,7 +2182,11 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar) } else { if (!cal->todo(todo->uid())) { - cal->addTodo(todo); + if ( !cal->addTodo( todo ) ) { + cal->endBatchAdding(); + // If the user pressed cancel, return true, it's not an error. + return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel; + } } else { delete todo; mTodosRelate.remove( todo ); @@ -2119,7 +2220,11 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar) } else { if (!cal->event(event->uid())) { - cal->addEvent(event); + if ( !cal->addEvent( event ) ) { + cal->endBatchAdding(); + // If the user pressed cancel, return true, it's not an error. + return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel; + } } else { delete event; mEventsRelate.remove( event ); @@ -2153,7 +2258,11 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar) } else { if (!cal->journal(journal->uid())) { - cal->addJournal(journal); + if ( !cal->addJournal(journal) ) { + cal->endBatchAdding(); + // If the user pressed cancel, return true, it's not an error. + return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel; + } } else { delete journal; } @@ -2162,6 +2271,8 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar) c = icalcomponent_get_next_component(calendar,ICAL_VJOURNAL_COMPONENT); } + cal->endBatchAdding(); + // Post-Process list of events with relations, put Event objects in relation Event::List::ConstIterator eIt; for ( eIt = mEventsRelate.begin(); eIt != mEventsRelate.end(); ++eIt ) { diff --git a/libkcal/incidence.h b/libkcal/incidence.h index 477b758c..89d45b08 100644 --- a/libkcal/incidence.h +++ b/libkcal/incidence.h @@ -65,6 +65,36 @@ class LIBKCAL_EXPORT Incidence : public IncidenceBase, public Recurrence::Observ T *mResource; }; + /** + This class implements a visitor for adding an Incidence to a resource + plus subresource supporting addEvent(), addTodo() and addJournal() calls. + */ + template + class AddSubResourceVisitor : public IncidenceBase::Visitor + { + public: + AddSubResourceVisitor( T *r, const TQString &subResource ) + : mResource( r ), mSubResource( subResource ) {} + + protected: + bool visit( Event *e ) + { + return mResource->addEvent( e, mSubResource ); + } + bool visit( Todo *t ) + { + return mResource->addTodo( t, mSubResource ); + } + bool visit( Journal *j ) + { + return mResource->addJournal( j, mSubResource ); + } + + private: + T *mResource; + TQString mSubResource; + }; + /** This class implements a visitor for deleting an Incidence from a resource supporting deleteEvent(), deleteTodo() and deleteJournal() calls. diff --git a/libkcal/incidencebase.cpp b/libkcal/incidencebase.cpp index 2aeba830..a8a6bdb0 100644 --- a/libkcal/incidencebase.cpp +++ b/libkcal/incidencebase.cpp @@ -59,6 +59,10 @@ IncidenceBase::IncidenceBase(const IncidenceBase &i) : mSyncStatus = i.mSyncStatus; mComments = i.mComments; + // The copied object is a new one, so it isn't observed by the observer + // of the original object. + mObservers.clear(); + mAttendees.setAutoDelete( true ); } @@ -404,7 +408,9 @@ void IncidenceBase::updated() while( it.current() ) { Observer *o = it.current(); ++it; - o->incidenceUpdated( this ); + if ( o ) { + o->incidenceUpdated( this ); + } } } diff --git a/libkcal/incidencebase.h b/libkcal/incidencebase.h index 72455b43..fa3a1a61 100644 --- a/libkcal/incidencebase.h +++ b/libkcal/incidencebase.h @@ -131,16 +131,28 @@ class LIBKCAL_EXPORT IncidenceBase : public CustomProperties /** for setting the event's starting date/time with a TQDateTime. */ virtual void setDtStart( const TQDateTime &dtStart ); /** returns an event's starting date/time as a TQDateTime. */ + virtual TQDateTime dtStart() const; - /** returns an event's starting time as a string formatted according to the - users locale settings */ - virtual TQString dtStartTimeStr() const; - /** returns an event's starting date as a string formatted according to the - users locale settings */ - virtual TQString dtStartDateStr( bool shortfmt = true ) const; - /** returns an event's starting date and time as a string formatted according - to the users locale settings */ - virtual TQString dtStartStr() const; + + /** + returns an event's starting time as a string formatted according to the + users locale settings. + @deprecated use IncidenceFormatter::timeToString() + */ + virtual KDE_DEPRECATED TQString dtStartTimeStr() const; + + /** + returns an event's starting date as a string formatted according to the + users locale settings + @deprecated use IncidenceFormatter::dateToString() + */ + virtual KDE_DEPRECATED TQString dtStartDateStr( bool shortfmt = true ) const; + /** + returns an event's starting date and time as a string formatted according + to the users locale settings + @deprecated use IncidenceFormatter::dateTimeToString() + */ + virtual KDE_DEPRECATED TQString dtStartStr() const; virtual void setDuration( int seconds ); int duration() const; diff --git a/libkcal/incidenceformatter.cpp b/libkcal/incidenceformatter.cpp index 61861171..cebdd0b1 100644 --- a/libkcal/incidenceformatter.cpp +++ b/libkcal/incidenceformatter.cpp @@ -3,6 +3,7 @@ Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2004 Reinhold Kainhofer + Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -42,27 +43,28 @@ #include #include -// #include +#include #include #include #include +#include +#include #include #include #include +#include #include - using namespace KCal; +/******************* + * General helpers + *******************/ -/******************************************************************* - * Helper functions for the extensive display (event viewer) - *******************************************************************/ - -static TQString eventViewerAddLink( const TQString &ref, const TQString &text, +static TQString htmlAddLink( const TQString &ref, const TQString &text, bool newline = true ) { TQString tmpStr( "" + text + "" ); @@ -70,7 +72,7 @@ static TQString eventViewerAddLink( const TQString &ref, const TQString &text, return tmpStr; } -static TQString eventViewerAddTag( const TQString & tag, const TQString & text ) +static TQString htmlAddTag( const TQString & tag, const TQString & text ) { int numLineBreaks = text.contains( "\n" ); TQString str = "<" + tag + ">"; @@ -94,134 +96,299 @@ static TQString eventViewerAddTag( const TQString & tag, const TQString & text ) return tmpStr; } -static TQString linkPerson( const TQString& email, TQString name, TQString uid ) +static bool iamAttendee( Attendee *attendee ) +{ + // Check if I'm this attendee + + bool iam = false; + KEMailSettings settings; + TQStringList profiles = settings.profiles(); + for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { + settings.setProfile( *it ); + if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) { + iam = true; + break; + } + } + return iam; +} + +static bool iamOrganizer( Incidence *incidence ) +{ + // Check if I'm the organizer for this incidence + + if ( !incidence ) { + return false; + } + + bool iam = false; + KEMailSettings settings; + TQStringList profiles = settings.profiles(); + for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { + settings.setProfile( *it ); + if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) { + iam = true; + break; + } + } + return iam; +} + +static bool senderIsOrganizer( Incidence *incidence, const TQString &sender ) +{ + // Check if the specified sender is the organizer + + if ( !incidence || sender.isEmpty() ) { + return true; + } + bool isorg = true; + TQString senderName, senderEmail; + if ( KPIM::getNameAndMail( sender, senderName, senderEmail ) ) { + // for this heuristic, we say the sender is the organizer if either the name or the email match. + if ( incidence->organizer().email() != senderEmail && + incidence->organizer().name() != senderName ) { + isorg = false; + } + } + return isorg; +} + +static TQString firstAttendeeName( Incidence *incidence, const TQString &defName ) +{ + TQString name; + if ( !incidence ) { + return name; + } + + Attendee::List attendees = incidence->attendees(); + if( attendees.count() > 0 ) { + Attendee *attendee = *attendees.begin(); + name = attendee->name(); + if ( name.isEmpty() ) { + name = attendee->email(); + } + if ( name.isEmpty() ) { + name = defName; + } + } + return name; +} + +/******************************************************************* + * Helper functions for the extensive display (display viewer) + *******************************************************************/ + +static TQString displayViewLinkPerson( const TQString& email, TQString name, TQString uid ) { // Make the search, if there is an email address to search on, // and either name or uid is missing if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) { KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); KABC::Addressee::List addressList = add_book->findByEmail( email ); - KABC::Addressee o = addressList.first(); - if ( !o.isEmpty() && addressList.size() < 2 ) { - if ( name.isEmpty() ) - // No name set, so use the one from the addressbook - name = o.formattedName(); - uid = o.uid(); - } else - // Email not found in the addressbook. Don't make a link - uid = TQString::null; + if ( !addressList.isEmpty() ) { + KABC::Addressee o = addressList.first(); + if ( !o.isEmpty() && addressList.size() < 2 ) { + if ( name.isEmpty() ) { + // No name set, so use the one from the addressbook + name = o.formattedName(); + } + uid = o.uid(); + } else { + // Email not found in the addressbook. Don't make a link + uid = TQString::null; + } + } } - kdDebug(5850) << "formatAttendees: uid = " << uid << endl; // Show the attendee - TQString tmpString = "
  • "; + TQString tmpString; if ( !uid.isEmpty() ) { // There is a UID, so make a link to the addressbook - if ( name.isEmpty() ) + if ( name.isEmpty() ) { // Use the email address for text - tmpString += eventViewerAddLink( "uid:" + uid, email ); - else - tmpString += eventViewerAddLink( "uid:" + uid, name ); + tmpString += htmlAddLink( "uid:" + uid, email ); + } else { + tmpString += htmlAddLink( "uid:" + uid, name ); + } } else { // No UID, just show some text tmpString += ( name.isEmpty() ? email : name ); } - tmpString += '\n'; // Make the mailto link if ( !email.isEmpty() ) { - KCal::Person person( name, email ); KURL mailto; mailto.setProtocol( "mailto" ); - mailto.setPath( person.fullName() ); - tmpString += eventViewerAddLink( mailto.url(), TQString::null ); + mailto.setPath( email ); + const TQString iconPath = + KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small ); + tmpString += " " + + htmlAddLink( mailto.url(), + "" ); } - tmpString += "
  • \n"; return tmpString; } -static TQString eventViewerFormatAttendees( Incidence *event ) +static TQString displayViewFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role ) { TQString tmpStr; - Attendee::List attendees = event->attendees(); - if ( attendees.count() ) { - - // Add organizer link - tmpStr += eventViewerAddTag( "i", i18n("Organizer") ); - tmpStr += "
      "; - tmpStr += linkPerson( event->organizer().email(), - event->organizer().name(), TQString::null ); - tmpStr += "
    "; - - // Add attendees links - tmpStr += eventViewerAddTag( "i", i18n("Attendees") ); - tmpStr += "
      "; - Attendee::List::ConstIterator it; - for( it = attendees.begin(); it != attendees.end(); ++it ) { - Attendee *a = *it; - tmpStr += linkPerson( a->email(), a->name(), a->uid() ); - if ( !a->delegator().isEmpty() ) { - tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); - } - if ( !a->delegate().isEmpty() ) { - tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); - } + Attendee::List::ConstIterator it; + Attendee::List attendees = incidence->attendees(); + + for ( it = attendees.begin(); it != attendees.end(); ++it ) { + Attendee *a = *it; + if ( a->role() != role ) { + // skip this role + continue; + } + if ( a->email() == incidence->organizer().email() ) { + // skip attendee that is also the organizer + continue; } - tmpStr += "
    "; + tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid() ); + if ( !a->delegator().isEmpty() ) { + tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); + } + if ( !a->delegate().isEmpty() ) { + tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); + } + tmpStr += "
    "; + } + if ( tmpStr.endsWith( "
    " ) ) { + tmpStr.truncate( tmpStr.length() - 4 ); + } + return tmpStr; +} + +static TQString displayViewFormatAttendees( Incidence *incidence ) +{ + TQString tmpStr, str; + + // Add organizer link + int attendeeCount = incidence->attendees().count(); + if ( attendeeCount > 1 || + ( attendeeCount == 1 && + incidence->organizer().email() != incidence->attendees().first()->email() ) ) { + tmpStr += "
    "; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + // Add "chair" + str = displayViewFormatAttendeeRoleList( incidence, Attendee::Chair ); + if ( !str.isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + // Add required participants + str = displayViewFormatAttendeeRoleList( incidence, Attendee::ReqParticipant ); + if ( !str.isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + // Add optional participants + str = displayViewFormatAttendeeRoleList( incidence, Attendee::OptParticipant ); + if ( !str.isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + // Add observers + str = displayViewFormatAttendeeRoleList( incidence, Attendee::NonParticipant ); + if ( !str.isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; } + return tmpStr; } -static TQString eventViewerFormatAttachments( Incidence *i ) +static TQString displayViewFormatAttachments( Incidence *incidence ) { TQString tmpStr; - Attachment::List as = i->attachments(); - if ( as.count() > 0 ) { - Attachment::List::ConstIterator it; - for( it = as.begin(); it != as.end(); ++it ) { - if ( (*it)->isUri() ) { - TQString name; - if ( (*it)->uri().startsWith( "kmail:" ) ) - name = i18n( "Show mail" ); - else + Attachment::List as = incidence->attachments(); + Attachment::List::ConstIterator it; + uint count = 0; + for( it = as.begin(); it != as.end(); ++it ) { + count++; + if ( (*it)->isUri() ) { + TQString name; + if ( (*it)->uri().startsWith( "kmail:" ) ) { + name = i18n( "Show mail" ); + } else { + if ( (*it)->label().isEmpty() ) { name = (*it)->uri(); - tmpStr += eventViewerAddLink( (*it)->uri(), name ); - tmpStr += "
    "; + } else { + name = (*it)->label(); + } } + tmpStr += htmlAddLink( (*it)->uri(), name ); + } else { + tmpStr += htmlAddLink( "ATTACH:" + incidence->uid() + ':' + (*it)->label(), + (*it)->label(), false ); + } + if ( count < as.count() ) { + tmpStr += "
    "; } } return tmpStr; } -/* - FIXME:This function depends of kaddressbook. Is necessary a new - type of event? -*/ -static TQString eventViewerFormatBirthday( Event *event ) +static TQString displayViewFormatCategories( Incidence *incidence ) +{ + // We do not use Incidence::categoriesStr() since it does not have whitespace + return incidence->categories().join( ", " ); +} + +static TQString displayViewFormatCreationDate( Incidence *incidence ) +{ + return i18n( "Creation date: %1" ). + arg( IncidenceFormatter::dateTimeToString( incidence->created(), false, true ) ); +} + +static TQString displayViewFormatBirthday( Event *event ) { - if ( !event) return TQString::null; - if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) return TQString::null; + if ( !event ) { + return TQString::null; + } + if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) { + return TQString::null; + } TQString uid = event->customProperty("KABC","UID-1"); TQString name = event->customProperty("KABC","NAME-1"); TQString email= event->customProperty("KABC","EMAIL-1"); - TQString tmpString = "
      "; - tmpString += linkPerson( email, name, uid ); + TQString tmpStr = displayViewLinkPerson( email, name, uid ); if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) { uid = event->customProperty("KABC","UID-2"); name = event->customProperty("KABC","NAME-2"); email= event->customProperty("KABC","EMAIL-2"); - tmpString += linkPerson( email, name, uid ); + tmpStr += "
      "; + tmpStr += displayViewLinkPerson( email, name, uid ); } - tmpString += "
    "; - return tmpString; + return tmpStr; } -static TQString eventViewerFormatHeader( Incidence *incidence ) +static TQString displayViewFormatHeader( Incidence *incidence ) { TQString tmpStr = "
    "; - if( !filename.isEmpty() ) - htmlStr += "" + if ( node ) + htmlStr += "asHREF( "body" ) + "\">" + i18n("Encapsulated message") + ""; else - htmlStr += i18n("Encapsulated message"); + htmlStr += i18n("Encapsulated message"); htmlStr += "
    "; } @@ -2131,7 +2364,9 @@ TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block, { htmlStr += "" "
    "; - if( block.isDecryptable ) + if ( block.inProgress ) + htmlStr += i18n("Please wait while the message is being decrypted..."); + else if ( block.isDecryptable ) htmlStr += i18n("Encrypted message"); else { htmlStr += i18n("Encrypted message (decryption not possible)"); @@ -2140,9 +2375,18 @@ TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block, } htmlStr += "
    "; } + + if ( block.isSigned && block.inProgress ) + { + block.signClass = "signInProgress"; + htmlStr += "" + "
    "; + htmlStr += i18n("Please wait while the signature is being verified..."); + htmlStr += "
    "; + } simpleHtmlStr = htmlStr; - if( block.isSigned ) { + if ( block.isSigned && !block.inProgress ) { TQStringList& blockAddrs( block.signerMailAddresses ); // note: At the moment frameColor and showKeyInfos are // used for CMS only but not for PGP signatures @@ -2232,7 +2476,7 @@ TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block, if( block.keyId.isEmpty() ) certificate = i18n("certificate"); else - certificate = startKeyHREF + i18n("certificate") + ""; + certificate = startKeyHREF + i18n("certificate") + ""; if( !blockAddrs.empty() ){ if( blockAddrs.grep( msgFrom, @@ -2512,6 +2756,26 @@ TQString ObjectTreeParser::writeSigstatFooter( PartMetaData& block ) return htmlStr; } +//----------------------------------------------------------------------------- + +void ObjectTreeParser::writeAttachmentMarkHeader( partNode *node ) +{ + if ( !mReader ) + return; + + htmlWriter()->queue( TQString( "
    \n" ).arg( node->nodeId() ) ); +} + +//----------------------------------------------------------------------------- + +void ObjectTreeParser::writeAttachmentMarkFooter() +{ + if ( !mReader ) + return; + + htmlWriter()->queue( TQString( "
    " ) ); +} + //----------------------------------------------------------------------------- void ObjectTreeParser::writeBodyStr( const TQCString& aStr, const TQTextCodec *aCodec, const TQString& fromAddress ) @@ -2562,8 +2826,8 @@ void ObjectTreeParser::writeBodyStr( const TQCString& aStr, const TQTextCodec *a TQCString str( *npbit ); if( !str.isEmpty() ) { htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate ); - kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str - << "'" << endl; + //kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str + // << "'" << endl; // treat messages with empty lines before the first clearsigned // block as fully signed/encrypted if( firstNonPgpBlock ) { @@ -2706,7 +2970,7 @@ TQString ObjectTreeParser::quotedHTML( const TQString& s, bool decorate ) const unsigned int length = s.length(); // skip leading empty lines - for ( pos = 0; pos < length && s[pos] <= ' '; pos++ ); + for ( pos = 0; pos < length && s[pos] <= ' '; pos++ ) { ; } while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--; beg = pos; @@ -2836,10 +3100,6 @@ TQString ObjectTreeParser::quotedHTML( const TQString& s, bool decorate ) else htmlStr.append( quoteEnd ); - //kdDebug(5006) << "KMReaderWin::quotedHTML:\n" - // << "========================================\n" - // << htmlStr - // << "\n======================================\n"; return htmlStr; } diff --git a/kmail/objecttreeparser.h b/kmail/objecttreeparser.h index 2edc406f..ad980eda 100644 --- a/kmail/objecttreeparser.h +++ b/kmail/objecttreeparser.h @@ -40,12 +40,18 @@ #include #include +#include + class KMReaderWin; class KMMessagePart; class TQString; class TQWidget; class partNode; +namespace GpgME { + class Error; +} + namespace KMail { class AttachmentStrategy; @@ -110,6 +116,11 @@ namespace KMail { KMail::CSSHelper * cssHelper=0 ); virtual ~ObjectTreeParser(); + void setAllowAsync( bool allow ) { assert( !mHasPendingAsyncJobs ); mAllowAsync = allow; } + bool allowAsync() const { return mAllowAsync; } + + bool hasPendingAsyncJobs() const { return mHasPendingAsyncJobs; } + TQCString rawReplyString() const { return mRawReplyString; } /*! @return the text of the message, ie. what would appear in the @@ -140,6 +151,15 @@ namespace KMail { mIncludeSignatures = include; } + // Controls whether Toltec invitations are displayed in their raw form or as a replacement text, + // which is used in processToltecMail(). + void setShowRawToltecMail( bool showRawToltecMail ) { mShowRawToltecMail = showRawToltecMail; } + bool showRawToltecMail() const { return mShowRawToltecMail; } + + /// default text for processToltecMail(), which is used in kmail.kcfg, therefore it + /// needs to be static here. + static TQString defaultToltecReplacementText(); + const KMail::AttachmentStrategy * attachmentStrategy() const { return mAttachmentStrategy; } @@ -161,16 +181,24 @@ namespace KMail { void defaultHandling( partNode * node, ProcessResult & result ); - /** 1. Create a new partNode using 'content' data and Content-Description - found in 'cntDesc'. - 2. Make this node the child of 'node'. - 3. Insert the respective entries in the Mime Tree Viewer. - 3. Parse the 'node' to display the content. */ + /** + * 1. Create a new partNode using 'content' data and Content-Description + * found in 'cntDesc'. + * 2. Make this node the child of 'node'. + * 3. Insert the respective entries in the Mime Tree Viewer. + * 3. Parse the 'node' to display the content. + * + * @param addToTextualContent If true, this will add the textual content of the parsed node + * to the textual content of the current object tree parser. + * Setting this to false is useful for encapsulated messages, as we + * do not want the text in those to appear in the editor + */ // Function will be replaced once KMime is alive. void insertAndParseNewChildNode( partNode & node, const char * content, const char * cntDesc, - bool append=false ); + bool append=false, + bool addToTextualContent = true ); /** if data is 0: Feeds the HTML widget with the contents of the opaque signed data found in partNode 'sign'. @@ -186,9 +214,17 @@ namespace KMail { const TQString & fromAddress, bool doCheck=true, TQCString * cleartextData=0, - std::vector paramSignatures = std::vector(), + const std::vector & paramSignatures = std::vector(), bool hideErrors=false ); + /** Writes out the block that we use when the node is encrypted, + but we're deferring decryption for later. */ + void writeDeferredDecryptionBlock(); + + /** Writes out the block that we use when the node is encrypted, + but we've just kicked off async decryption. */ + void writeDecryptionInProgressBlock(); + /** Returns the contents of the given multipart/encrypted object. Data is decypted. May contain body parts. */ bool okDecryptMIME( partNode& data, @@ -198,11 +234,24 @@ namespace KMail { bool showWarning, bool& passphraseError, bool& actuallyEncrypted, + bool& decryptionStarted, TQString& aErrorText, + GpgME::Error & auditLogError, TQString& auditLog ); bool processMailmanMessage( partNode * node ); + /** + * This is called for all multipart/mixed nodes. It checks if that belongs to a Toltec mail, + * by checking various criteria. + * If it is a toltec mail, a special text, instead of the confusing toltec text, will be + * displayed. + * + * @return true if the mail was indeed a toltec mail, in which case the node should not be + * processed further + */ + bool processToltecMail( partNode * node ); + /** Checks whether @p str contains external references. To be precise, we only check whether @p str contains 'xxx="http[s]:' where xxx is not href. Obfuscated external references are ignored on purpose. @@ -245,9 +294,15 @@ namespace KMail { TQString writeSigstatHeader( KMail::PartMetaData & part, const Kleo::CryptoBackend::Protocol * cryptProto, const TQString & fromAddress, - const TQString & filename = TQString::null ); + partNode *node = 0 ); TQString writeSigstatFooter( KMail::PartMetaData & part ); + // The attachment mark is a div that is placed around the attchment. It is used for drawing + // a yellow border around the attachment when scrolling to it. When scrolling to it, the border + // color of the div is changed, see KMReaderWin::scrollToAttachment(). + void writeAttachmentMarkHeader( partNode *node ); + void writeAttachmentMarkFooter(); + void writeBodyStr( const TQCString & bodyString, const TQTextCodec * aCodec, const TQString & fromAddress, @@ -281,6 +336,9 @@ namespace KMail { bool mShowOnlyOneMimePart; bool mKeepEncryptions; bool mIncludeSignatures; + bool mHasPendingAsyncJobs; + bool mAllowAsync; + bool mShowRawToltecMail; const KMail::AttachmentStrategy * mAttachmentStrategy; KMail::HtmlWriter * mHtmlWriter; KMail::CSSHelper * mCSSHelper; diff --git a/kmail/objecttreeparser_p.cpp b/kmail/objecttreeparser_p.cpp new file mode 100644 index 00000000..a645b398 --- /dev/null +++ b/kmail/objecttreeparser_p.cpp @@ -0,0 +1,350 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + objecttreeparser_p.cpp + + This file is part of KMail, the KDE mail client. + Copyright (c) 2009 Klarälvdalens Datakonsult AB + Authors: Marc Mutz + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail 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 + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include + +#include "objecttreeparser_p.h" + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +using namespace KMail; +using namespace Kleo; +using namespace GpgME; + +CryptoBodyPartMemento::CryptoBodyPartMemento() + : TQObject( 0 ), + Interface::BodyPartMemento(), + ISubject(), + m_running( false ) +{ + +} + +CryptoBodyPartMemento::~CryptoBodyPartMemento() {} + +void CryptoBodyPartMemento::setAuditLog( const GpgME::Error & err, const TQString & log ) { + m_auditLogError = err; + m_auditLog = log; +} + +void CryptoBodyPartMemento::setRunning( bool running ) { + m_running = running; +} + +DecryptVerifyBodyPartMemento::DecryptVerifyBodyPartMemento( DecryptVerifyJob * job, const TQByteArray & cipherText ) + : CryptoBodyPartMemento(), + m_cipherText( cipherText ), + m_job( job ) +{ + assert( m_job ); +} + +DecryptVerifyBodyPartMemento::~DecryptVerifyBodyPartMemento() { + if ( m_job ) + m_job->slotCancel(); +} + +bool DecryptVerifyBodyPartMemento::start() { + assert( m_job ); + if ( const GpgME::Error err = m_job->start( m_cipherText ) ) { + m_dr = DecryptionResult( err ); + return false; + } + connect( m_job, TQT_SIGNAL(result(const GpgME::DecryptionResult&,const GpgME::VerificationResult&,const TQByteArray&)), + this, TQT_SLOT(slotResult(const GpgME::DecryptionResult&,const GpgME::VerificationResult&,const TQByteArray&)) ); + setRunning( true ); + return true; +} + +void DecryptVerifyBodyPartMemento::exec() { + assert( m_job ); + TQByteArray plainText; + setRunning( true ); + const std::pair p = m_job->exec( m_cipherText, plainText ); + saveResult( p.first, p.second, plainText ); + m_job->deleteLater(); // exec'ed jobs don't delete themselves + m_job = 0; +} + +void DecryptVerifyBodyPartMemento::saveResult( const DecryptionResult & dr, + const VerificationResult & vr, + const TQByteArray & plainText ) +{ + assert( m_job ); + setRunning( false ); + m_dr = dr; + m_vr = vr; + m_plainText = plainText; + setAuditLog( m_job->auditLogError(), m_job->auditLogAsHtml() ); +} + +void DecryptVerifyBodyPartMemento::slotResult( const DecryptionResult & dr, + const VerificationResult & vr, + const TQByteArray & plainText ) +{ + saveResult( dr, vr, plainText ); + m_job = 0; + notify(); +} + + + + +VerifyDetachedBodyPartMemento::VerifyDetachedBodyPartMemento( VerifyDetachedJob * job, + KeyListJob * klj, + const TQByteArray & signature, + const TQByteArray & plainText ) + : CryptoBodyPartMemento(), + m_signature( signature ), + m_plainText( plainText ), + m_job( job ), + m_keylistjob( klj ) +{ + assert( m_job ); +} + +VerifyDetachedBodyPartMemento::~VerifyDetachedBodyPartMemento() { + if ( m_job ) + m_job->slotCancel(); + if ( m_keylistjob ) + m_keylistjob->slotCancel(); +} + +bool VerifyDetachedBodyPartMemento::start() { + assert( m_job ); + if ( const GpgME::Error err = m_job->start( m_signature, m_plainText ) ) { + m_vr = VerificationResult( err ); + return false; + } + connect( m_job, TQT_SIGNAL(result(const GpgME::VerificationResult&)), + this, TQT_SLOT(slotResult(const GpgME::VerificationResult&)) ); + setRunning( true ); + return true; +} + +void VerifyDetachedBodyPartMemento::exec() { + assert( m_job ); + setRunning( true ); + saveResult( m_job->exec( m_signature, m_plainText ) ); + m_job->deleteLater(); // exec'ed jobs don't delete themselves + m_job = 0; + if ( canStartKeyListJob() ) { + std::vector keys; + m_keylistjob->exec( keyListPattern(), /*secretOnly=*/false, keys ); + if ( !keys.empty() ) + m_key = keys.back(); + } + if ( m_keylistjob ) + m_keylistjob->deleteLater(); // exec'ed jobs don't delete themselves + m_keylistjob = 0; + setRunning( false ); +} + +bool VerifyDetachedBodyPartMemento::canStartKeyListJob() const +{ + if ( !m_keylistjob ) + return false; + const char * const fpr = m_vr.signature( 0 ).fingerprint(); + return fpr && *fpr; +} + +TQStringList VerifyDetachedBodyPartMemento::keyListPattern() const +{ + assert( canStartKeyListJob() ); + return TQStringList( TQString::fromLatin1( m_vr.signature( 0 ).fingerprint() ) ); +} + +void VerifyDetachedBodyPartMemento::saveResult( const VerificationResult & vr ) +{ + assert( m_job ); + m_vr = vr; + setAuditLog( m_job->auditLogError(), m_job->auditLogAsHtml() ); +} + +void VerifyDetachedBodyPartMemento::slotResult( const VerificationResult & vr ) +{ + saveResult( vr ); + m_job = 0; + if ( canStartKeyListJob() && startKeyListJob() ) + return; + if ( m_keylistjob ) + m_keylistjob->deleteLater(); + m_keylistjob = 0; + setRunning( false ); + notify(); +} + +bool VerifyDetachedBodyPartMemento::startKeyListJob() +{ + assert( canStartKeyListJob() ); + if ( const GpgME::Error err = m_keylistjob->start( keyListPattern() ) ) + return false; + connect( m_keylistjob, TQT_SIGNAL(done()), this, TQT_SLOT(slotKeyListJobDone()) ); + connect( m_keylistjob, TQT_SIGNAL(nextKey(const GpgME::Key&)), + this, TQT_SLOT(slotNextKey(const GpgME::Key&)) ); + return true; +} + +void VerifyDetachedBodyPartMemento::slotNextKey( const GpgME::Key & key ) +{ + m_key = key; +} + +void VerifyDetachedBodyPartMemento::slotKeyListJobDone() +{ + m_keylistjob = 0; + setRunning( false ); + notify(); +} + + +VerifyOpaqueBodyPartMemento::VerifyOpaqueBodyPartMemento( VerifyOpaqueJob * job, + KeyListJob * klj, + const TQByteArray & signature ) + : CryptoBodyPartMemento(), + m_signature( signature ), + m_job( job ), + m_keylistjob( klj ) +{ + assert( m_job ); +} + +VerifyOpaqueBodyPartMemento::~VerifyOpaqueBodyPartMemento() { + if ( m_job ) + m_job->slotCancel(); + if ( m_keylistjob ) + m_keylistjob->slotCancel(); +} + +bool VerifyOpaqueBodyPartMemento::start() { + assert( m_job ); + if ( const GpgME::Error err = m_job->start( m_signature ) ) { + m_vr = VerificationResult( err ); + return false; + } + connect( m_job, TQT_SIGNAL(result(const GpgME::VerificationResult&,const TQByteArray&)), + this, TQT_SLOT(slotResult(const GpgME::VerificationResult&,const TQByteArray&)) ); + setRunning( true ); + return true; +} + +void VerifyOpaqueBodyPartMemento::exec() { + assert( m_job ); + setRunning( true ); + TQByteArray plainText; + saveResult( m_job->exec( m_signature, plainText ), plainText ); + m_job->deleteLater(); // exec'ed jobs don't delete themselves + m_job = 0; + if ( canStartKeyListJob() ) { + std::vector keys; + m_keylistjob->exec( keyListPattern(), /*secretOnly=*/false, keys ); + if ( !keys.empty() ) + m_key = keys.back(); + } + if ( m_keylistjob ) + m_keylistjob->deleteLater(); // exec'ed jobs don't delete themselves + m_keylistjob = 0; + setRunning( false ); +} + +bool VerifyOpaqueBodyPartMemento::canStartKeyListJob() const +{ + if ( !m_keylistjob ) + return false; + const char * const fpr = m_vr.signature( 0 ).fingerprint(); + return fpr && *fpr; +} + +TQStringList VerifyOpaqueBodyPartMemento::keyListPattern() const +{ + assert( canStartKeyListJob() ); + return TQStringList( TQString::fromLatin1( m_vr.signature( 0 ).fingerprint() ) ); +} + +void VerifyOpaqueBodyPartMemento::saveResult( const VerificationResult & vr, + const TQByteArray & plainText ) +{ + assert( m_job ); + m_vr = vr; + m_plainText = plainText; + setAuditLog( m_job->auditLogError(), m_job->auditLogAsHtml() ); +} + +void VerifyOpaqueBodyPartMemento::slotResult( const VerificationResult & vr, + const TQByteArray & plainText ) +{ + saveResult( vr, plainText ); + m_job = 0; + if ( canStartKeyListJob() && startKeyListJob() ) + return; + if ( m_keylistjob ) + m_keylistjob->deleteLater(); + m_keylistjob = 0; + setRunning( false ); + notify(); +} + +bool VerifyOpaqueBodyPartMemento::startKeyListJob() +{ + assert( canStartKeyListJob() ); + if ( const GpgME::Error err = m_keylistjob->start( keyListPattern() ) ) + return false; + connect( m_keylistjob, TQT_SIGNAL(done()), this, TQT_SLOT(slotKeyListJobDone()) ); + connect( m_keylistjob, TQT_SIGNAL(nextKey(const GpgME::Key&)), + this, TQT_SLOT(slotNextKey(const GpgME::Key&)) ); + return true; +} + +void VerifyOpaqueBodyPartMemento::slotNextKey( const GpgME::Key & key ) +{ + m_key = key; +} + +void VerifyOpaqueBodyPartMemento::slotKeyListJobDone() +{ + m_keylistjob = 0; + setRunning( false ); + notify(); +} + + +#include "objecttreeparser_p.moc" diff --git a/kmail/objecttreeparser_p.h b/kmail/objecttreeparser_p.h new file mode 100644 index 00000000..d70cd8c7 --- /dev/null +++ b/kmail/objecttreeparser_p.h @@ -0,0 +1,203 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + objecttreeparser_p.h + + This file is part of KMail, the KDE mail client. + Copyright (c) 2009 Klarälvdalens Datakonsult AB + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail 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 + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef _KMAIL_OBJECTTREEPARSER_P_H_ +#define _KMAIL_OBJECTTREEPARSER_P_H_ + +#include +#include +#include + +#include +#include +#include +#include + +#include "isubject.h" +#include "interfaces/bodypart.h" + +namespace Kleo { + class DecryptVerifyJob; + class VerifyDetachedJob; + class VerifyOpaqueJob; + class KeyListJob; +} + +class TQStringList; + +namespace KMail { + + class CryptoBodyPartMemento + : public TQObject, + public KMail::Interface::BodyPartMemento, + public KMail::ISubject + { + Q_OBJECT + public: + CryptoBodyPartMemento(); + ~CryptoBodyPartMemento(); + + /* reimp */ Interface::Observer * asObserver() { return 0; } + /* reimp */ Interface::Observable * asObservable() { return this; } + + bool isRunning() const { return m_running; } + + const TQString & auditLogAsHtml() const { return m_auditLog; } + GpgME::Error auditLogError() const { return m_auditLogError; } + + protected: + void setAuditLog( const GpgME::Error & err, const TQString & log ); + void setRunning( bool running ); + + private: + bool m_running; + TQString m_auditLog; + GpgME::Error m_auditLogError; + }; + + class DecryptVerifyBodyPartMemento + : public CryptoBodyPartMemento + { + Q_OBJECT + public: + DecryptVerifyBodyPartMemento( Kleo::DecryptVerifyJob * job, const TQByteArray & cipherText ); + ~DecryptVerifyBodyPartMemento(); + + bool start(); + void exec(); + + const TQByteArray & plainText() const { return m_plainText; } + const GpgME::DecryptionResult & decryptResult() const { return m_dr; } + const GpgME::VerificationResult & verifyResult() const { return m_vr; } + + private slots: + void slotResult( const GpgME::DecryptionResult & dr, + const GpgME::VerificationResult & vr, + const TQByteArray & plainText ); + + private: + void saveResult( const GpgME::DecryptionResult &, + const GpgME::VerificationResult &, + const TQByteArray & ); + private: + // input: + const TQByteArray m_cipherText; + TQGuardedPtr m_job; + // output: + GpgME::DecryptionResult m_dr; + GpgME::VerificationResult m_vr; + TQByteArray m_plainText; + }; + + + class VerifyDetachedBodyPartMemento + : public CryptoBodyPartMemento + { + Q_OBJECT + public: + VerifyDetachedBodyPartMemento( Kleo::VerifyDetachedJob * job, + Kleo::KeyListJob * klj, + const TQByteArray & signature, + const TQByteArray & plainText ); + ~VerifyDetachedBodyPartMemento(); + + bool start(); + void exec(); + + const GpgME::VerificationResult & verifyResult() const { return m_vr; } + const GpgME::Key & signingKey() const { return m_key; } + + private slots: + void slotResult( const GpgME::VerificationResult & vr ); + void slotKeyListJobDone(); + void slotNextKey( const GpgME::Key & ); + + private: + void saveResult( const GpgME::VerificationResult & ); + bool canStartKeyListJob() const; + TQStringList keyListPattern() const; + bool startKeyListJob(); + private: + // input: + const TQByteArray m_signature; + const TQByteArray m_plainText; + TQGuardedPtr m_job; + TQGuardedPtr m_keylistjob; + // output: + GpgME::VerificationResult m_vr; + GpgME::Key m_key; + }; + + + class VerifyOpaqueBodyPartMemento + : public CryptoBodyPartMemento + { + Q_OBJECT + public: + VerifyOpaqueBodyPartMemento( Kleo::VerifyOpaqueJob * job, + Kleo::KeyListJob * klj, + const TQByteArray & signature ); + ~VerifyOpaqueBodyPartMemento(); + + bool start(); + void exec(); + + const TQByteArray & plainText() const { return m_plainText; } + const GpgME::VerificationResult & verifyResult() const { return m_vr; } + const GpgME::Key & signingKey() const { return m_key; } + + private slots: + void slotResult( const GpgME::VerificationResult & vr, + const TQByteArray & plainText ); + void slotKeyListJobDone(); + void slotNextKey( const GpgME::Key & ); + + private: + void saveResult( const GpgME::VerificationResult &, + const TQByteArray & ); + bool canStartKeyListJob() const; + TQStringList keyListPattern() const; + bool startKeyListJob(); + private: + // input: + const TQByteArray m_signature; + TQGuardedPtr m_job; + TQGuardedPtr m_keylistjob; + // output: + GpgME::VerificationResult m_vr; + TQByteArray m_plainText; + GpgME::Key m_key; + }; + + +} // namespace KMail + +#endif // _KMAIL_OBJECTTREEPARSER_H_ diff --git a/kmail/partNode.cpp b/kmail/partNode.cpp index 80e0545b..dbd5442c 100644 --- a/kmail/partNode.cpp +++ b/kmail/partNode.cpp @@ -30,7 +30,10 @@ */ #include + #include "partNode.h" +#include "kmreaderwin.h" + #include #include #include "kmmimeparttree.h" @@ -64,12 +67,14 @@ partNode::partNode() mEncodedOk( false ), mDeleteDwBodyPart( false ), mMimePartTreeItem( 0 ), - mBodyPartMemento( 0 ) + mBodyPartMementoMap(), + mReader( 0 ), + mDisplayedEmbedded( false ) { adjustDefaultType( this ); } -partNode::partNode( DwBodyPart* dwPart, int explicitType, int explicitSubType, +partNode::partNode( KMReaderWin * win, DwBodyPart* dwPart, int explicitType, int explicitSubType, bool deleteDwBodyPart ) : mRoot( 0 ), mNext( 0 ), mChild( 0 ), mWasProcessed( false ), @@ -80,13 +85,15 @@ partNode::partNode( DwBodyPart* dwPart, int explicitType, int explicitSubType, mEncodedOk( false ), mDeleteDwBodyPart( deleteDwBodyPart ), mMimePartTreeItem( 0 ), - mBodyPartMemento( 0 ) + mBodyPartMementoMap(), + mReader( win ), + mDisplayedEmbedded( false ), + mDisplayedHidden( false ) { if ( explicitType != DwMime::kTypeUnknown ) { mType = explicitType; // this happens e.g. for the Root Node mSubType = explicitSubType; // representing the _whole_ message } else { -// kdDebug(5006) << "\n partNode::partNode() explicitType == DwMime::kTypeUnknown\n" << endl; if(dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType()) { mType = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type(); mSubType = dwPart->Headers().ContentType().Subtype(); @@ -95,17 +102,9 @@ partNode::partNode( DwBodyPart* dwPart, int explicitType, int explicitSubType, mSubType = DwMime::kSubtypeUnknown; } } -#ifdef DEBUG - { - DwString type, subType; - DwTypeEnumToStr( mType, type ); - DwSubtypeEnumToStr( mSubType, subType ); - kdDebug(5006) << "\npartNode::partNode() " << type.c_str() << "/" << subType.c_str() << "\n" << endl; - } -#endif } -partNode * partNode::fromMessage( const KMMessage * msg ) { +partNode * partNode::fromMessage( const KMMessage * msg, KMReaderWin * win ) { if ( !msg ) return 0; @@ -124,11 +123,11 @@ partNode * partNode::fromMessage( const KMMessage * msg ) { // as just another DwBodyPart... DwBodyPart * mainBody = new DwBodyPart( *msg->getTopLevelPart() ); - partNode * root = new partNode( mainBody, mainType, mainSubType, true ); + partNode * root = new partNode( win, mainBody, mainType, mainSubType, true ); root->buildObjectTree(); root->setFromAddress( msg->from() ); - root->dump(); + //root->dump(); return root; } @@ -142,7 +141,9 @@ partNode::partNode( bool deleteDwBodyPart, DwBodyPart* dwPart ) mEncodedOk( false ), mDeleteDwBodyPart( deleteDwBodyPart ), mMimePartTreeItem( 0 ), - mBodyPartMemento( 0 ) + mBodyPartMementoMap(), + mReader( 0 ), + mDisplayedEmbedded( false ) { if ( dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType() ) { mType = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type(); @@ -159,13 +160,16 @@ partNode::~partNode() { mDwPart = 0; delete mChild; mChild = 0; delete mNext; mNext = 0; - delete mBodyPartMemento; mBodyPartMemento = 0; + for ( std::map::const_iterator it = mBodyPartMementoMap.begin(), end = mBodyPartMementoMap.end() ; it != end ; ++it ) + delete it->second; + mBodyPartMementoMap.clear(); } #ifndef NDEBUG void partNode::dump( int chars ) const { - kdDebug(5006) << TQString().fill( ' ', chars ) << "+ " - << typeString() << '/' << subTypeString() << endl; + kdDebug(5006) << nodeId() << " " << TQString().fill( ' ', chars ) << "+ " + << typeString() << '/' << subTypeString() << " embedded:" << mDisplayedEmbedded + << " address:" << this << endl; if ( mChild ) mChild->dump( chars + 1 ); if ( mNext ) @@ -194,7 +198,7 @@ void partNode::buildObjectTree( bool processSiblings ) while( curNode && curNode->dwPart() ) { //dive into multipart messages while( DwMime::kTypeMultipart == curNode->type() ) { - partNode * newNode = new partNode( curNode->dwPart()->Body().FirstBodyPart() ); + partNode * newNode = new partNode( mReader, curNode->dwPart()->Body().FirstBodyPart() ); curNode->setFirstChild( newNode ); curNode = newNode; } @@ -210,7 +214,7 @@ void partNode::buildObjectTree( bool processSiblings ) return; // store next node if( curNode && curNode->dwPart() && curNode->dwPart()->Next() ) { - partNode* nextNode = new partNode( curNode->dwPart()->Next() ); + partNode* nextNode = new partNode( mReader, curNode->dwPart()->Next() ); curNode->setNext( nextNode ); curNode = nextNode; } else @@ -230,6 +234,13 @@ TQCString partNode::subTypeString() const { return s.c_str(); } +const partNode* partNode::topLevelParent() const { + const partNode *ret = this; + while ( ret->parentNode() ) + ret = ret->parentNode(); + return ret; +} + int partNode::childCount() const { int count = 0; for ( partNode * child = firstChild() ; child ; child = child->nextSibling() ) @@ -237,6 +248,15 @@ int partNode::childCount() const { return count; } +int partNode::totalChildCount() const { + int count = 0; + for ( partNode * child = firstChild() ; child ; child = child->nextSibling() ) { + ++count; + count += child->totalChildCount(); + } + return count; +} + TQString partNode::contentTypeParameter( const char * name ) const { if ( !mDwPart || !mDwPart->hasHeaders() ) return TQString::null; @@ -292,8 +312,6 @@ KMMsgEncryptionState partNode::overallEncryptionState() const } } -//kdDebug(5006) << "\n\n KMMsgEncryptionState: " << myState << endl; - return myState; } @@ -335,11 +353,24 @@ KMMsgSignatureState partNode::overallSignatureState() const } } -//kdDebug(5006) << "\n\n KMMsgSignatureState: " << myState << endl; - return myState; } +TQCString partNode::path() const +{ + if ( !parentNode() ) + return ':'; + const partNode * p = parentNode(); + + // count number of siblings with the same type as us: + int nth = 0; + for ( const partNode * c = p->firstChild() ; c != this ; c = c->nextSibling() ) + if ( c->type() == type() && c->subType() == subType() ) + ++nth; + + return p->path() + TQCString().sprintf( ":%X/%X[%X]", type(), subType(), nth ); +} + int partNode::nodeId() const { @@ -392,13 +423,6 @@ int partNode::calcNodeIdOrFindNode( int &curId, const partNode* findNode, int fi partNode* partNode::findType( int type, int subType, bool deep, bool wide ) { -#ifndef NDEBUG - DwString typeStr, subTypeStr; - DwTypeEnumToStr( mType, typeStr ); - DwSubtypeEnumToStr( mSubType, subTypeStr ); - kdDebug(5006) << "partNode::findType() is looking at " << typeStr.c_str() - << "/" << subTypeStr.c_str() << endl; -#endif if( (mType != DwMime::kTypeUnknown) && ( (type == DwMime::kTypeUnknown) || (type == mType) ) @@ -470,12 +494,12 @@ void partNode::fillMimePartTree( KMMimePartTreeItem* parentItem, } else cntType = "text/plain"; - if( cntDesc.isEmpty() ) - cntDesc = msgPart().contentDescription(); if( cntDesc.isEmpty() ) cntDesc = msgPart().name().stripWhiteSpace(); if( cntDesc.isEmpty() ) cntDesc = msgPart().fileName(); + if( cntDesc.isEmpty() ) + cntDesc = msgPart().contentDescription(); if( cntDesc.isEmpty() ) { if( mRoot && mRoot->mRoot ) cntDesc = i18n("internal part"); @@ -494,8 +518,6 @@ void partNode::fillMimePartTree( KMMimePartTreeItem* parentItem, // remove linebreak+whitespace from folded Content-Description cntDesc.replace( TQRegExp("\\n\\s*"), " " ); -kdDebug(5006) << " Inserting one item into MimePartTree" << endl; -kdDebug(5006) << " Content-Type: " << cntType << endl; if( parentItem ) mMimePartTreeItem = new KMMimePartTreeItem( parentItem, this, @@ -547,6 +569,13 @@ bool partNode::isAttachment() const if ( !dwPart()->hasHeaders() ) return false; DwHeaders& headers = dwPart()->Headers(); + if ( headers.HasContentType() && + headers.ContentType().Type() == DwMime::kTypeMessage && + headers.ContentType().Subtype() == DwMime::kSubtypeRfc822 ) { + // Messages are always attachments. Normally message attachments created from KMail have a content + // disposition, but some mail clients omit that. + return true; + } if( !headers.HasContentDisposition() ) return false; return ( headers.ContentDisposition().DispositionType() @@ -591,6 +620,47 @@ bool partNode::isFirstTextPart() const { return false; // make comiler happy } +bool partNode::isToltecMessage() const +{ + if ( type() != DwMime::kTypeMultipart || subType() != DwMime::kSubtypeMixed ) + return false; + + if ( childCount() != 3 ) + return false; + + const DwField* library = dwPart()->Headers().FindField( "X-Library" ); + if ( !library ) + return false; + + if ( !library->FieldBody() || + TQString( library->FieldBody()->AsString().c_str() ) != TQString( "Toltec" ) ) + return false; + + const DwField* kolabType = dwPart()->Headers().FindField( "X-Kolab-Type" ); + if ( !kolabType ) + return false; + + if ( !kolabType->FieldBody() || + !TQString( kolabType->FieldBody()->AsString().c_str() ).startsWith( "application/x-vnd.kolab" ) ) + return false; + + return true; +} + +bool partNode::isInEncapsulatedMessage() const +{ + const partNode * const topLevel = topLevelParent(); + const partNode *cur = this; + while ( cur && cur != topLevel ) { + const bool parentIsMessage = cur->parentNode() && + cur->parentNode()->msgPart().typeStr().lower() == "message"; + if ( parentIsMessage && cur->parentNode() != topLevel ) + return true; + cur = cur->parentNode(); + } + return false; +} + bool partNode::hasContentDispositionInline() const { if( !dwPart() ) @@ -610,3 +680,97 @@ const TQString& partNode::trueFromAddress() const node = node->mRoot; return node->mFromAddress; } + +KMail::Interface::BodyPartMemento * partNode::bodyPartMemento( const TQCString & which ) const +{ + if ( const KMReaderWin * r = reader() ) + return r->bodyPartMemento( this, which ); + else + return internalBodyPartMemento( which ); +} + +KMail::Interface::BodyPartMemento * partNode::internalBodyPartMemento( const TQCString & which ) const +{ + assert( !reader() ); + + const std::map::const_iterator it = mBodyPartMementoMap.find( which.lower() ); + return it != mBodyPartMementoMap.end() ? it->second : 0 ; +} + +void partNode::setBodyPartMemento( const TQCString & which, KMail::Interface::BodyPartMemento * memento ) +{ + if ( KMReaderWin * r = reader() ) + r->setBodyPartMemento( this, which, memento ); + else + internalSetBodyPartMemento( which, memento ); +} + +void partNode::internalSetBodyPartMemento( const TQCString & which, KMail::Interface::BodyPartMemento * memento ) +{ + assert( !reader() ); + + const std::map::iterator it = mBodyPartMementoMap.lower_bound( which.lower() ); + if ( it != mBodyPartMementoMap.end() && it->first == which.lower() ) { + delete it->second; + if ( memento ) { + it->second = memento; + } + else { + mBodyPartMementoMap.erase( it ); + } + } else { + mBodyPartMementoMap.insert( it, std::make_pair( which.lower(), memento ) ); + } +} + +bool partNode::isDisplayedEmbedded() const +{ + return mDisplayedEmbedded; +} + +void partNode::setDisplayedEmbedded( bool displayedEmbedded ) +{ + mDisplayedEmbedded = displayedEmbedded; +} + +bool partNode::isDisplayedHidden() const +{ + return mDisplayedHidden; +} + +void partNode::setDisplayedHidden( bool displayedHidden ) +{ + mDisplayedHidden = displayedHidden; +} + + +TQString partNode::asHREF( const TQString &place ) const +{ + return TQString( "attachment:%1?place=%2" ).arg( nodeId() ).arg( place ); +} + +partNode::AttachmentDisplayInfo partNode::attachmentDisplayInfo() const +{ + AttachmentDisplayInfo info; + info.icon = msgPart().iconName( KIcon::Small ); + info.label = msgPart().name().stripWhiteSpace(); + if ( info.label.isEmpty() ) + info.label = msgPart().fileName(); + if ( info.label.isEmpty() ) + info.label = msgPart().contentDescription(); + bool typeBlacklisted = msgPart().typeStr().lower() == "multipart"; + if ( !typeBlacklisted && msgPart().typeStr().lower() == "application" ) { + typeBlacklisted = msgPart().subtypeStr() == "pgp-encrypted" + || msgPart().subtypeStr().lower() == "pgp-signature" + || msgPart().subtypeStr().lower() == "pkcs7-mime" + || msgPart().subtypeStr().lower() == "pkcs7-signature"; + } + typeBlacklisted = typeBlacklisted || this == topLevelParent(); + bool firstTextChildOfEncapsulatedMsg = msgPart().typeStr().lower() == "text" && + msgPart().subtypeStr().lower() == "plain" && + parentNode() && + parentNode()->msgPart().typeStr().lower() == "message"; + typeBlacklisted = typeBlacklisted || firstTextChildOfEncapsulatedMsg; + info.displayInHeader = !info.label.isEmpty() && !info.icon.isEmpty() && !typeBlacklisted; + return info; +} diff --git a/kmail/partNode.h b/kmail/partNode.h index d5397703..ddc76aa6 100644 --- a/kmail/partNode.h +++ b/kmail/partNode.h @@ -45,9 +45,13 @@ #include #include +#include + class KMMimePartTreeItem; class KMMimePartTree; +class KMReaderWin; + /* =========================================================================== @@ -67,14 +71,22 @@ class partNode int calcNodeIdOrFindNode( int& curId, const partNode* calcNode, int findId, partNode** findNode ); -public: - static partNode * fromMessage( const KMMessage * msg ); - - partNode( DwBodyPart* dwPart, + partNode( KMReaderWin * win, DwBodyPart* dwPart, int explicitType = DwMime::kTypeUnknown, int explicitSubType = DwMime::kSubtypeUnknown, bool deleteDwBodyPart = false ); +public: + + struct AttachmentDisplayInfo + { + TQString label; + TQString icon; + bool displayInHeader; + }; + + static partNode * fromMessage( const KMMessage * msg, KMReaderWin * win=0 ); + partNode( bool deleteDwBodyPart, DwBodyPart* dwPart ); @@ -151,6 +163,11 @@ public: return mSignatureState; } + // path is a hierarchical path to this partNode. It is designed to + // be stable under decryption, where new child nodes are + // added. Treat it as an opaque string. + TQCString path() const; + int nodeId() const; // node ids start at 1 (this is the top level root node) partNode* findId( int id ); // returns the node which has the given id (or 0, resp.) @@ -202,7 +219,7 @@ public: mMimePartTreeItem = item; } - KMMimePartTreeItem* mimePartTreeItem() { + KMMimePartTreeItem* mimePartTreeItem() const { return mMimePartTreeItem; } @@ -217,23 +234,52 @@ public: */ bool isFirstTextPart() const; + bool isToltecMessage() const; + + /** + * @return true if this node is a child or an encapsulated message + */ + bool isInEncapsulatedMessage() const; + bool hasContentDispositionInline() const; TQString contentTypeParameter( const char * name ) const; const TQString& trueFromAddress() const; + const partNode * topLevelParent() const; partNode * parentNode() const { return mRoot; } partNode * nextSibling() const { return mNext; } partNode * firstChild() const { return mChild; } partNode * next( bool allowChildren=true ) const; int childCount() const; + int totalChildCount() const; bool processed() const { return mWasProcessed; } - KMail::Interface::BodyPartMemento * bodyPartMemento() const { return mBodyPartMemento; }; - void setBodyPartMemento( KMail::Interface::BodyPartMemento * memento ) { - mBodyPartMemento = memento; - }; + KMail::Interface::BodyPartMemento * bodyPartMemento( const TQCString & which ) const; + void setBodyPartMemento( const TQCString & which, KMail::Interface::BodyPartMemento * memento ); + + // A flag to remember if the node was embedded. This is useful for attachment nodes, the reader + // needs to know if they were displayed inline or not. + bool isDisplayedEmbedded() const; + void setDisplayedEmbedded( bool displayedEmbedded ); + + // Same as above, but this time determines if the node was hidden or not + bool isDisplayedHidden() const; + void setDisplayedHidden( bool displayedHidden ); + + // Get a href in the form attachment:?place=, used by ObjectTreeParser and + // UrlHandlerManager. + TQString asHREF( const TQString &place ) const; + + AttachmentDisplayInfo attachmentDisplayInfo() const; + +private: + KMReaderWin * reader() const { + return mReader ? mReader : mRoot ? mRoot->reader() : 0 ; + } + KMail::Interface::BodyPartMemento * internalBodyPartMemento( const TQCString & ) const; + void internalSetBodyPartMemento( const TQCString & which, KMail::Interface::BodyPartMemento * memento ); private: partNode* mRoot; @@ -253,7 +299,10 @@ private: bool mEncodedOk; bool mDeleteDwBodyPart; KMMimePartTreeItem* mMimePartTreeItem; - KMail::Interface::BodyPartMemento * mBodyPartMemento; + std::map mBodyPartMementoMap; + KMReaderWin * mReader; + bool mDisplayedEmbedded; + bool mDisplayedHidden; }; #endif diff --git a/kmail/partmetadata.h b/kmail/partmetadata.h index 1bc93d81..ee6f51d3 100644 --- a/kmail/partmetadata.h +++ b/kmail/partmetadata.h @@ -18,6 +18,7 @@ #define _KMAIL_PARTMETADATA_H_ #include +#include #include #include @@ -34,6 +35,7 @@ namespace KMail { isGoodSignature( false ), isEncrypted( false ), isDecryptable( false ), + inProgress( false ), technicalProblem( false ), isEncapsulatedRfc822Message( false ) { @@ -50,10 +52,12 @@ namespace KMail { TQDateTime creationTime; TQString decryptionError; TQString auditLog; + GpgME::Error auditLogError; bool isSigned : 1; bool isGoodSignature : 1; bool isEncrypted : 1; bool isDecryptable : 1; + bool inProgress : 1; bool technicalProblem : 1; bool isEncapsulatedRfc822Message : 1; }; diff --git a/kmail/partnodebodypart.cpp b/kmail/partnodebodypart.cpp index 37e37353..f9a56316 100644 --- a/kmail/partnodebodypart.cpp +++ b/kmail/partnodebodypart.cpp @@ -82,11 +82,11 @@ bool KMail::PartNodeBodyPart::hasCompleteBody() const { } KMail::Interface::BodyPartMemento * KMail::PartNodeBodyPart::memento() const { - return mPartNode.bodyPartMemento(); + return mPartNode.bodyPartMemento( "__plugin__" ); } void KMail::PartNodeBodyPart::setBodyPartMemento( Interface::BodyPartMemento * memento ) { - mPartNode.setBodyPartMemento( memento ); + mPartNode.setBodyPartMemento( "__plugin__", memento ); } KMail::Interface::BodyPart::Display KMail::PartNodeBodyPart::defaultDisplay() const { diff --git a/kmail/pics/Makefile.am b/kmail/pics/Makefile.am index b1e1c368..8c185ddb 100644 --- a/kmail/pics/Makefile.am +++ b/kmail/pics/Makefile.am @@ -9,7 +9,7 @@ pics_DATA = kmmsgdel.png kmmsgnew.png kmmsgunseen.png kmmsgread.png \ kmmsgpartiallysigned.png kmmsgfullyencrypted.png \ kmmsgfullysigned.png kmmsgundefinedencrypted.png \ kmmsgundefinedsigned.png \ - kmmsgspam.png kmmsgham.png kmmsgattachment.png \ + kmmsgspam.png kmmsgham.png kmmsgattachment.png kmmsginvitation.png \ kmwizard.png \ quotecollapse.png quoteexpand.png \ enterprise_bottom_left.png \ diff --git a/kmail/pics/kmmsginvitation.png b/kmail/pics/kmmsginvitation.png new file mode 100644 index 00000000..82c9a67b Binary files /dev/null and b/kmail/pics/kmmsginvitation.png differ diff --git a/kmail/profiles/profile-default-rc.desktop b/kmail/profiles/profile-default-rc.desktop index 7a75c8bb..b8ba7e96 100644 --- a/kmail/profiles/profile-default-rc.desktop +++ b/kmail/profiles/profile-default-rc.desktop @@ -31,7 +31,6 @@ Name[id]=Standar Name[is]=Sjálfgefið Name[it]=Predefinito Name[ja]=標準 -Name[ka]=ნაგულისხმევი Name[kk]=Әдетті Name[km]=លំនាំដើម Name[lt]=Numatytasis @@ -62,8 +61,7 @@ Name[tg]=Пешфарзӣ Name[th]=ค่าปริยาย Name[tr]=Öntanımlı Name[uk]=Типовий -Name[uz]=Andoza -Name[uz@cyrillic]=Андоза +Name[uz]=Андоза Name[ven]=Zwi si zwavhudi Name[vi]=Mặc định Name[xh]=Engagqibekanga @@ -99,7 +97,6 @@ Comment[hu]=Standard profil Comment[is]=Venjulegt snið Comment[it]=Profilo standard Comment[ja]=標準プロファイル -Comment[ka]=სტანდარტული პროფილი Comment[kk]=Стандартты профилі Comment[km]=ទម្រង់​ខ្នាត​គំរូ Comment[lt]=Standartinis profilis @@ -127,8 +124,7 @@ Comment[tg]=Профили оддӣ Comment[th]=โปรไฟล์มาตรฐาน Comment[tr]=Standart profil Comment[uk]=Типовий профіль -Comment[uz]=Andoza profili -Comment[uz@cyrillic]=Андоза профили +Comment[uz]=Андоза профили Comment[ven]=Zwithu zwo doweleaho Comment[vi]=Hồ sơ chuẩn Comment[xh]=Imboniselo yabucala esezantsi diff --git a/kmail/profiles/profile-high-contrast-rc.desktop b/kmail/profiles/profile-high-contrast-rc.desktop index 8c5b7cee..a744ff1f 100644 --- a/kmail/profiles/profile-high-contrast-rc.desktop +++ b/kmail/profiles/profile-high-contrast-rc.desktop @@ -27,7 +27,6 @@ Name[hu]=Kontrasztos Name[is]=Mikil birtuskil Name[it]=Alto contrasto Name[ja]=ハイコントラスト -Name[ka]=მაღალი კონტრასტი Name[kk]=Контрастығы жоғары Name[km]=កម្រិត​ពណ៌​ខ្ពស់ Name[lt]=Didelis kontrastas @@ -82,7 +81,6 @@ Comment[hu]=Nagyobb betűméretek látáscsökkent felhasználóknak Comment[is]=Stærra letur fyrir notendur með slæma sjón Comment[it]=Dimensioni più grandi dei caratteri per chi ha problemi di vista Comment[ja]=視力障害者のために大きなフォントを使用します -Comment[ka]=შრიფტის გაზრდილი ზომა მხედველობაშეზღუდული მომხმარებლებისთვის Comment[kk]=Көру қабілеті нашарларға арналған ірі қаріпті көрініс Comment[km]=បង្កើន​ទំហំពុម្ពអក្សរ ដើម្បី​បង្ក​លក្ខណៈ​ងាយស្រួល​ដល់​ជន​ពិការ​ភ្នែក Comment[lt]=Padidinti šriftų dydžiai blogai matantiems naudotojams diff --git a/kmail/profiles/profile-html-rc.desktop b/kmail/profiles/profile-html-rc.desktop index e609c50d..f9655c91 100644 --- a/kmail/profiles/profile-html-rc.desktop +++ b/kmail/profiles/profile-html-rc.desktop @@ -21,7 +21,7 @@ Comment[et]=Standardprofiil HTML-i eelvaatlusega - pole nii turvaline! Comment[eu]=HTML aurrebista gaituta duen profil estandarra - sekuritate gutxiago du Comment[fa]=profile استاندارد با پیش‌نمایش زنگام فعال‌شده - با ایمنی کمتر! Comment[fi]=Normaali profiili HTML-esikatselua käyttäville - vähemmän turvallinen. -Comment[fr]=Profil standard avec l'aperçu HTML activé - Moins sécurisé ! +Comment[fr]=Profil standard avec l'aperçu HTML activé - Moins sécurisé ! Comment[fy]=Standertprofyl mei HTML-foarbyld aktivearre - minder feilich! Comment[gl]=Perfil estándar con previsualización HTML activada - menos seguro! Comment[hi]=एचटीएमएल पूर्वावलोकन के साथ मानक प्रोफ़ाइल सक्षम है - कम सुरक्षित! @@ -30,7 +30,6 @@ Comment[hu]=Standard profil, HTML-előnézettel (kevésbé biztonságos) Comment[is]=Staðlað snið með HTML forsýn - minna öryggi! Comment[it]=Profilo standard con l'anteprima HTML abilitata - meno sicuro! Comment[ja]=HTML プレビューを有効にした標準プロファイル - 安全度は下がります! -Comment[ka]=სტანდარტული პროფილი HTML ესკიზით - ნაკლებად უსაფრთხო! Comment[kk]=Стандартты, HTML көрінісі бар профилі - қауіпсізігі төмен! Comment[km]=ទម្រង់​ខ្នាត​គំរូ​ដែល​អាច​មើល HTML ជាមុន - មិន​សូវ​មាន​សុវត្ថិភាព​ឡើយ ! Comment[lt]=Standartinis profilis su HTML peržiūra – mažiau saugus! diff --git a/kmail/profiles/profile-purist-rc.desktop b/kmail/profiles/profile-purist-rc.desktop index c77f2808..fed41395 100644 --- a/kmail/profiles/profile-purist-rc.desktop +++ b/kmail/profiles/profile-purist-rc.desktop @@ -18,7 +18,6 @@ Name[hr]=Čistunski Name[hu]=Egyszerűsített Name[it]=Purista Name[ja]=純正 -Name[ka]=პურისტი Name[kk]=Пурист Name[km]=បរិសុទ្ធ Name[lt]=Itin paprastas @@ -72,7 +71,6 @@ Comment[hu]=A legtöbb extra kikapcsolva, a KDE alapértelmezéseinek felhaszná Comment[is]=Slökkt á flestum aukahlutum, notast við víðværar stillingar KDE Comment[it]=La maggior parte delle funzioni sono disabilitate, vengono usate le impostazioni globali di KDE Comment[ja]=ほとんどの機能を無効にし、KDE の全体設定を使用します -Comment[ka]=უმეტესობა შესაძლებლობებისა ამორთულია, გამოიყენება KDE-ს ზოგადი პარამეტრები Comment[kk]=Мүмкіндіктердің көбі, KDE жалпылары ғана қалдырып, өшірілген Comment[km]=លក្ខណៈ​ពិសេស​ភាគ​ច្រើន​ត្រូវ​បាន​បិទ ដោយ​ប្រើ​តែ​ការ​កំណត់​សកល​របស់ KDE ប៉ុណ្ណោះ Comment[lt]=Dauguma savybių išjungta, naudojami globalūs KDE nustatymai @@ -83,7 +81,7 @@ Comment[nb]=De fleste funksjoner slått av, KDEs globale innstillinger er i bruk Comment[nds]=Mehrste Funkschonen utmaakt, globaal KDE-Vörinstellen warrt bruukt Comment[ne]=धेरै विशेषता बन्द गरिएका छन, केडीई विश्वव्यापी सेटिङ प्रयोग गरिएका छन् Comment[nl]=Meeste mogelijkheden uitgezet, de globale KDE-instellingen worden gebruikt -Comment[nn]=Dei fleste funksjonar er slått av, globale KDE-innstillingar vert bruka +Comment[nn]=Dei fleste funksjonar er slått av, globale KDE-innstillingar vert brukt Comment[pl]=Większość opcji wyłączona, używane są domyślne ustawienia KDE Comment[pt]=A maioria das funcionalidades desligada, sendo usadas as opções globais do KDE Comment[pt_BR]=Maioria dos recursos desligados, são usadas configurações globais do KDE diff --git a/kmail/profiles/profile-secure-rc.desktop b/kmail/profiles/profile-secure-rc.desktop index fbc14b91..3cfc3cf2 100644 --- a/kmail/profiles/profile-secure-rc.desktop +++ b/kmail/profiles/profile-secure-rc.desktop @@ -26,7 +26,6 @@ Name[hu]=Maximális biztonság Name[is]=Öruggast Name[it]=Massima sicurezza Name[ja]=最も安全 -Name[ka]=ყველაზე უსაფრთხო Name[kk]=Ең қауіпсіз Name[km]=សុវត្ថិភាព​បំផុត Name[lt]=Saugiausias @@ -52,8 +51,7 @@ Name[ta]=மிகவும் பாதுகாப்பான Name[tg]=Аз ҳама бехавфнокаш Name[tr]=En Güvenli Name[uk]=Найбільш безпечний -Name[uz]=Juda xavfsiz -Name[uz@cyrillic]=Жуда хавфсиз +Name[uz]=Жуда хавфсиз Name[zh_CN]=最安全 Name[zh_TW]=最安全 Comment=Sets all necessary options to achieve maximum security @@ -81,7 +79,6 @@ Comment[hu]=Az összes beállítás a legbiztonságosabb értékre állítva Comment[is]=Setur allar stillingar þannig að öryggið sé mest Comment[it]=Imposta tutte le opzioni necessario per ottenere la massima sicurezza Comment[ja]=最大のセキュリティ確保のために必要なすべてのオプションを設定します -Comment[ka]=აყენებს ყველა საჭირო ოპციას მაქსიმალური უსაფრთხოების მისაღწევად Comment[kk]=Қауіпсіздігі мейілінше арттырып бапталғаны Comment[km]=កំណត់​ជម្រើស​ចាំបាច់​ទាំងអស់ ដើម្បី​ទទួល​បាន​សុវត្ថិភាព​ខ្ពស់​បំផុត Comment[lt]=Nustato visas būtinas maksimaliam saugumui parinktis diff --git a/kmail/recipientseditor.cpp b/kmail/recipientseditor.cpp index 5ca2d735..fe7b85ca 100644 --- a/kmail/recipientseditor.cpp +++ b/kmail/recipientseditor.cpp @@ -159,6 +159,8 @@ RecipientLine::RecipientLine( TQWidget *parent ) TQToolTip::add( mCombo, i18n("Select type of recipient") ); mEdit = new RecipientLineEdit( this ); + TQToolTip::add( mEdit, + i18n( "Set the list of email addresses to receive this message" ) ); topLayout->addWidget( mEdit ); connect( mEdit, TQT_SIGNAL( returnPressed() ), TQT_SLOT( slotReturnPressed() ) ); connect( mEdit, TQT_SIGNAL( deleteMe() ), TQT_SLOT( slotPropagateDeletion() ) ); @@ -891,7 +893,8 @@ void RecipientsEditor::saveDistributionList() { DistributionListDialog *dlg = new DistributionListDialog( this ); dlg->setRecipients( mRecipientsView->recipients() ); - dlg->show(); + dlg->exec(); + delete dlg; } Recipient::List RecipientsEditor::recipients() const diff --git a/kmail/recipientspicker.cpp b/kmail/recipientspicker.cpp index 839898cb..c1d2b3ae 100644 --- a/kmail/recipientspicker.cpp +++ b/kmail/recipientspicker.cpp @@ -772,7 +772,7 @@ void RecipientsPicker::pick( Recipient::Type type ) kdDebug() << "RecipientsPicker::pick " << int( type ) << endl; int count = 0; - TQListViewItemIterator it( mRecipientList , + TQListViewItemIterator it( mRecipientList , TQListViewItemIterator::Visible | TQListViewItemIterator::Selected ); for ( ; it.current(); ++it ) ++count; @@ -787,7 +787,7 @@ void RecipientsPicker::pick( Recipient::Type type ) return; } - it = TQListViewItemIterator( mRecipientList , + it = TQListViewItemIterator( mRecipientList , TQListViewItemIterator::Visible | TQListViewItemIterator::Selected ); for ( ; it.current(); ++it ) { RecipientViewItem *item = static_cast( it.current() ); @@ -855,7 +855,7 @@ void RecipientsPicker::slotSearchLDAP() void RecipientsPicker::ldapSearchResult() { - TQStringList emails = TQStringList::split(',', mLdapSearchDialog->selectedEMails() ); + TQStringList emails = KPIM::splitEmailAddrList( mLdapSearchDialog->selectedEMails() ); TQStringList::iterator it( emails.begin() ); TQStringList::iterator end( emails.end() ); for ( ; it != end; ++it ){ diff --git a/kmail/redirectdialog.cpp b/kmail/redirectdialog.cpp index 150e6a5d..bf8fe51c 100644 --- a/kmail/redirectdialog.cpp +++ b/kmail/redirectdialog.cpp @@ -76,14 +76,23 @@ RedirectDialog::RedirectDialog( TQWidget *parent, const char *name, connect( mBtnTo, TQT_SIGNAL(clicked()), TQT_SLOT(slotAddrBook()) ); + connect( mEditTo, TQT_SIGNAL( textChanged ( const TQString & ) ), TQT_SLOT( slotEmailChanged( const TQString & ) ) ); mLabelTo->setBuddy( mBtnTo ); mEditTo->setFocus(); setButtonGuiItem( User1, KGuiItem( i18n("&Send Now"), "mail_send" ) ); setButtonGuiItem( User2, KGuiItem( i18n("Send &Later"), "queue" ) ); + enableButton( User1, false ); + enableButton( User2, false ); } +void RedirectDialog::slotEmailChanged( const TQString & text ) +{ + enableButton( User1, !text.isEmpty() ); + enableButton( User2, !text.isEmpty() ); +} + //----------------------------------------------------------------------------- void RedirectDialog::slotUser1() { diff --git a/kmail/redirectdialog.h b/kmail/redirectdialog.h index a89714a0..9be9f272 100644 --- a/kmail/redirectdialog.h +++ b/kmail/redirectdialog.h @@ -78,7 +78,7 @@ namespace KMail { void slotUser1(); void slotUser2(); - + void slotEmailChanged( const TQString & ); private: TQLabel *mLabelTo; KMLineEdit *mEditTo; diff --git a/kmail/searchwindow.cpp b/kmail/searchwindow.cpp index 81d64167..b32dd96e 100644 --- a/kmail/searchwindow.cpp +++ b/kmail/searchwindow.cpp @@ -181,7 +181,6 @@ SearchWindow::SearchWindow(KMMainWidget* w, const char* name, } else { mChkbxAllFolders->setChecked(true); } - mFolder = searchFolder; } mPatternEdit->setSearchPattern( mSearchPattern ); TQObjectList *list = mPatternEdit->queryList( 0, "mRuleField" ); @@ -237,13 +236,15 @@ SearchWindow::SearchWindow(KMMainWidget* w, const char* name, mLbxMatches->setDragEnabled( true ); - connect(mLbxMatches, TQT_SIGNAL(doubleClicked(TQListViewItem *)), - this, TQT_SLOT(slotShowMsg(TQListViewItem *))); - connect(mLbxMatches, TQT_SIGNAL(currentChanged(TQListViewItem *)), - this, TQT_SLOT(slotCurrentChanged(TQListViewItem *))); - connect( mLbxMatches, TQT_SIGNAL( contextMenuRequested( TQListViewItem*, const TQPoint &, int )), - this, TQT_SLOT( slotContextMenuRequested( TQListViewItem*, const TQPoint &, int ))); - vbl->addWidget(mLbxMatches); + connect( mLbxMatches, TQT_SIGNAL(clicked(TQListViewItem *)), + this, TQT_SLOT(slotShowMsg(TQListViewItem *)) ); + connect( mLbxMatches, TQT_SIGNAL(doubleClicked(TQListViewItem *)), + this, TQT_SLOT(slotViewMsg(TQListViewItem *)) ); + connect( mLbxMatches, TQT_SIGNAL(currentChanged(TQListViewItem *)), + this, TQT_SLOT(slotCurrentChanged(TQListViewItem *)) ); + connect( mLbxMatches, TQT_SIGNAL(contextMenuRequested(TQListViewItem *,const TQPoint &,int)), + this, TQT_SLOT(slotContextMenuRequested(TQListViewItem *,const TQPoint &,int)) ); + vbl->addWidget( mLbxMatches ); TQHBoxLayout *hbl2 = new TQHBoxLayout( vbl, spacingHint(), "kmfs_hbl2" ); mSearchFolderLbl = new TQLabel(i18n("Search folder &name:"), searchWidget); @@ -269,7 +270,7 @@ SearchWindow::SearchWindow(KMMainWidget* w, const char* name, mSearchResultOpenBtn->setEnabled(false); hbl2->addWidget(mSearchResultOpenBtn); connect( mSearchResultOpenBtn, TQT_SIGNAL( clicked() ), - this, TQT_SLOT( slotShowSelectedMsg() )); + this, TQT_SLOT( slotViewSelectedMsg() )); mStatusBar = new KStatusBar(searchWidget); mStatusBar->insertFixedItem(i18n("AMiddleLengthText..."), 0, true); mStatusBar->changeItem(i18n("Ready."), 0); @@ -472,6 +473,9 @@ void SearchWindow::slotSearch() mFetchingInProgress = 0; mSearchFolderOpenBtn->setEnabled(true); + if ( mSearchFolderEdt->text().isEmpty() ) { + mSearchFolderEdt->setText( i18n("Last Search") ); + } mBtnSearch->setEnabled(false); mBtnStop->setEnabled(true); @@ -486,8 +490,6 @@ void SearchWindow::slotSearch() // create one. if (!mFolder) { KMFolderMgr *mgr = kmkernel->searchFolderMgr(); - if (mSearchFolderEdt->text().isEmpty()) - mSearchFolderEdt->setText(i18n("Last Search")); TQString baseName = mSearchFolderEdt->text(); TQString fullName = baseName; int count = 0; @@ -630,12 +632,12 @@ void SearchWindow::closeEvent(TQCloseEvent *e) //----------------------------------------------------------------------------- void SearchWindow::scheduleRename( const TQString &s) { - if (!s.isEmpty() && s != i18n("Last Search")) { + if (!s.isEmpty() ) { mRenameTimer.start(250, true); mSearchFolderOpenBtn->setEnabled(false); } else { mRenameTimer.stop(); - mSearchFolderOpenBtn->setEnabled(true); + mSearchFolderOpenBtn->setEnabled(!s.isEmpty()); } } @@ -656,11 +658,13 @@ void SearchWindow::renameSearchFolder() ++i; } } - mSearchFolderOpenBtn->setEnabled(true); + if ( mFolder ) + mSearchFolderOpenBtn->setEnabled(true); } void SearchWindow::openSearchFolder() { + Q_ASSERT( mFolder ); renameSearchFolder(); mKMMainWidget->slotSelectFolder( mFolder->folder() ); slotClose(); @@ -680,32 +684,51 @@ void SearchWindow::folderInvalidated(KMFolder *folder) } //----------------------------------------------------------------------------- -bool SearchWindow::slotShowMsg(TQListViewItem *item) +KMMessage *SearchWindow::indexToMessage( TQListViewItem *item ) { - if(!item) - return false; + if( !item ) { + return 0; + } - KMFolder* folder; - int msgIndex; - KMMsgDict::instance()->getLocation(item->text(MSGID_COLUMN).toUInt(), - &folder, &msgIndex); + KMFolder *folder; + int msgIndex; + KMMsgDict::instance()->getLocation( item->text( MSGID_COLUMN ).toUInt(), + &folder, &msgIndex ); - if (!folder || msgIndex < 0) - return false; + if ( !folder || msgIndex < 0 ) { + return 0; + } - mKMMainWidget->slotSelectFolder(folder); - KMMessage* message = folder->getMsg(msgIndex); - if (!message) - return false; + mKMMainWidget->slotSelectFolder( folder ); + return folder->getMsg( msgIndex ); +} - mKMMainWidget->slotSelectMessage(message); +//----------------------------------------------------------------------------- +bool SearchWindow::slotShowMsg( TQListViewItem *item ) +{ + KMMessage *message = indexToMessage( item ); + if ( message ) { + mKMMainWidget->slotSelectMessage( message ); return true; + } + return false; } //----------------------------------------------------------------------------- -void SearchWindow::slotShowSelectedMsg() +void SearchWindow::slotViewSelectedMsg() { - slotShowMsg(mLbxMatches->currentItem()); + slotViewMsg( mLbxMatches->currentItem() ); +} + +//----------------------------------------------------------------------------- +bool SearchWindow::slotViewMsg( TQListViewItem *item ) +{ + KMMessage *message = indexToMessage( item ); + if ( message ) { + mKMMainWidget->slotMsgActivated( message ); + return true; + } + return false; } //----------------------------------------------------------------------------- @@ -720,8 +743,8 @@ void SearchWindow::enableGUI() KMSearch const *search = (mFolder) ? (mFolder->search()) : 0; bool searching = (search) ? (search->running()) : false; actionButton(KDialogBase::Close)->setEnabled(!searching); - mCbxFolders->setEnabled(!searching); - mChkSubFolders->setEnabled(!searching); + mCbxFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked()); + mChkSubFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked()); mChkbxAllFolders->setEnabled(!searching); mChkbxSpecificFolders->setEnabled(!searching); mPatternEdit->setEnabled(!searching); diff --git a/kmail/searchwindow.h b/kmail/searchwindow.h index c94b263e..836553ae 100644 --- a/kmail/searchwindow.h +++ b/kmail/searchwindow.h @@ -113,9 +113,10 @@ protected slots: void renameSearchFolder(); void openSearchFolder(); void folderInvalidated(KMFolder *); - virtual bool slotShowMsg(TQListViewItem *); - void slotShowSelectedMsg(); - void slotCurrentChanged(TQListViewItem *); + virtual bool slotShowMsg( TQListViewItem * ); + void slotViewSelectedMsg(); + virtual bool slotViewMsg( TQListViewItem * ); + void slotCurrentChanged( TQListViewItem * ); virtual void updateContextMenuActions(); virtual void slotContextMenuRequested( TQListViewItem*, const TQPoint &, int ); virtual void copySelectedToFolder( int menuId ); @@ -189,6 +190,9 @@ protected: KMSearchPattern *mSearchPattern; static const int MSGID_COLUMN; + +private: + KMMessage *indexToMessage( TQListViewItem *item ); }; } // namespace KMail diff --git a/kmail/sievedebugdialog.cpp b/kmail/sievedebugdialog.cpp index 5ab305e8..bc5d2cca 100644 --- a/kmail/sievedebugdialog.cpp +++ b/kmail/sievedebugdialog.cpp @@ -219,6 +219,32 @@ SieveDebugDialog::~SieveDebugDialog() kdDebug( 5006 ) << k_funcinfo << endl; } +static KURL urlFromAccount( const KMail::ImapAccountBase * a ) { + const SieveConfig sieve = a->sieveConfig(); + if ( !sieve.managesieveSupported() ) + return KURL(); + + KURL u; + if ( sieve.reuseConfig() ) { + // assemble Sieve url from the settings of the account: + u.setProtocol( "sieve" ); + u.setHost( a->host() ); + u.setUser( a->login() ); + u.setPass( a->passwd() ); + u.setPort( sieve.port() ); + + // Translate IMAP LOGIN to PLAIN: + u.addQueryItem( "x-mech", a->auth() == "*" ? "PLAIN" : a->auth() ); + if ( !a->useSSL() && !a->useTLS() ) + u.addQueryItem( "x-allow-unencrypted", "true" ); + } else { + u = sieve.alternateURL(); + if ( u.protocol().lower() == "sieve" && !a->useSSL() && !a->useTLS() && u.queryItem("x-allow-unencrypted").isEmpty() ) + u.addQueryItem( "x-allow-unencrypted", "true" ); + } + return u; +} + void SieveDebugDialog::slotDiagNextAccount() { if ( mAccountList.isEmpty() ) @@ -233,26 +259,12 @@ void SieveDebugDialog::slotDiagNextAccount() if ( mAccountBase ) { // Detect URL for this IMAP account - SieveConfig sieve = mAccountBase->sieveConfig(); - if ( !sieve.managesieveSupported() ) + const KURL url = urlFromAccount( mAccountBase ); + if ( !url.isValid() ) { mEdit->append( i18n( "(Account does not support Sieve)\n\n" ) ); } else { - if ( sieve.reuseConfig() ) - { - // assemble Sieve url from the settings of the account: - mUrl.setProtocol( "sieve" ); - mUrl.setHost( mAccountBase->host() ); - mUrl.setUser( mAccountBase->login() ); - mUrl.setPass( mAccountBase->passwd() ); - mUrl.setPort( sieve.port() ); - - // Translate IMAP LOGIN to PLAIN: - mUrl.setQuery( "x-mech=" + ( mAccountBase->auth() == "*" ? "PLAIN" : mAccountBase->auth() ) ); - } else { - sieve.alternateURL(); - mUrl.setFileName( sieve.vacationFileName() ); - } + mUrl = url; mSieveJob = SieveJob::list( mUrl ); @@ -284,22 +296,9 @@ void SieveDebugDialog::slotDiagNextScript() mScriptList.pop_front(); mEdit->append( i18n( "Contents of script '%1':\n" ).arg( scriptFile ) ); - SieveConfig sieve = mAccountBase->sieveConfig(); - if ( sieve.reuseConfig() ) - { - // assemble Sieve url from the settings of the account: - mUrl.setProtocol( "sieve" ); - mUrl.setHost( mAccountBase->host() ); - mUrl.setUser( mAccountBase->login() ); - mUrl.setPass( mAccountBase->passwd() ); - mUrl.setPort( sieve.port() ); - // Translate IMAP LOGIN to PLAIN - mUrl.setQuery( "x-mech=" + ( mAccountBase->auth() == "*" ? "PLAIN" : mAccountBase->auth() ) ); - mUrl.setFileName( scriptFile ); - } else { - sieve.alternateURL(); - mUrl.setFileName( scriptFile ); - } + + mUrl = urlFromAccount( mAccountBase ); + mUrl.setFileName( scriptFile ); mSieveJob = SieveJob::get( mUrl ); diff --git a/kmail/sievejob.cpp b/kmail/sievejob.cpp index ade08b1d..0ce073d9 100644 --- a/kmail/sievejob.cpp +++ b/kmail/sievejob.cpp @@ -275,6 +275,12 @@ namespace KMail { return new SieveJob( url, TQString::null, commands ); } + SieveJob * SieveJob::desactivate( const KURL & url ) { + TQValueStack commands; + commands.push( Deactivate ); + return new SieveJob( url, TQString(), commands ); + } + SieveJob * SieveJob::activate( const KURL & url ) { TQValueStack commands; commands.push( Activate ); diff --git a/kmail/sievejob.h b/kmail/sievejob.h index ef9f4a50..ed8fb5d8 100644 --- a/kmail/sievejob.h +++ b/kmail/sievejob.h @@ -68,6 +68,8 @@ namespace KMail { static SieveJob * activate( const KURL & url ); + static SieveJob * desactivate( const KURL & url ); + void kill( bool quiet=true ); const TQStringList & sieveCapabilities() const { diff --git a/kmail/simplefoldertree.h b/kmail/simplefoldertree.h new file mode 100644 index 00000000..6bfc3eb3 --- /dev/null +++ b/kmail/simplefoldertree.h @@ -0,0 +1,249 @@ +/* + Copyright (c) 2007 Volker Krause + Copyright (c) 2003 Andreas Gungl + Copyright (c) Stefan Taferner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef KMAIL_SIMPLEFOLDERTREE_H +#define KMAIL_SIMPLEFOLDERTREE_H + +#include "kmfolder.h" +#include "kmfoldertree.h" +#include "treebase.h" + +#include +#include +#include +#include + +class KMFolder; +class KMFolderTree; + +namespace KMail { + +static int recurseFilter( TQListViewItem * item, const TQString& filter, int column ) +{ + if ( item == 0 ) + return 0; + + TQListViewItem * child; + child = item->firstChild(); + + int enabled = 0; + while ( child ) { + enabled += recurseFilter( child, filter, column ); + child = child->nextSibling(); + } + + if ( filter.length() == 0 || + item->text( column ).find( filter, 0, false ) >= 0 ) { + item->setVisible( true ); + ++enabled; + } + else { + item->setVisible( !!enabled ); + item->setEnabled( false ); + } + + return enabled; +} + +class TreeItemBase +{ + public : + TreeItemBase() + : mFolder( 0 ) + { + kdDebug(5006) << k_funcinfo << endl; + } + virtual ~TreeItemBase() { } + + void setFolder( KMFolder * folder ) { mFolder = folder; }; + const KMFolder * folder() { return mFolder; }; + + // Set the flag which determines if this is an alternate row + void setAlternate ( bool alternate ) { + mAlternate = alternate; + } + + private: + KMFolder * mFolder; + bool mAlternate; + +}; + +template class SimpleFolderTreeItem : public T, public TreeItemBase +{ + public: + SimpleFolderTreeItem( TQListView * listView ) : + T( listView ), TreeItemBase() + { + kdDebug(5006) << k_funcinfo << endl; + } + SimpleFolderTreeItem( TQListView * listView, TQListViewItem * afterListViewItem ) : + T( listView, afterListViewItem ) , TreeItemBase() + { + kdDebug(5006) << k_funcinfo << endl; + } + SimpleFolderTreeItem( TQListViewItem * listViewItem ) : T( listViewItem ) , TreeItemBase() + { + kdDebug(5006) << k_funcinfo << endl; + } + + SimpleFolderTreeItem( TQListViewItem * listViewItem, TQListViewItem * afterListViewItem ) : + T( listViewItem, afterListViewItem ) , TreeItemBase() + { + kdDebug(5006) << k_funcinfo << endl; + } + +}; + +template <> class SimpleFolderTreeItem : public TQCheckListItem, public TreeItemBase +{ + public: + SimpleFolderTreeItem( TQListView * listView ) : + TQCheckListItem( listView, TQString(), CheckBox ), TreeItemBase() {} + SimpleFolderTreeItem( TQListView * listView, TQListViewItem * afterListViewItem ) : + TQCheckListItem( listView, afterListViewItem, TQString(), CheckBox ), TreeItemBase() {} + SimpleFolderTreeItem( TQListViewItem * listViewItem ) : + TQCheckListItem( listViewItem, TQString(), CheckBox ) {} + SimpleFolderTreeItem( TQListViewItem * listViewItem, TQListViewItem * afterListViewItem ) : + TQCheckListItem( listViewItem, afterListViewItem, TQString(), CheckBox ) {} + +}; + + +template class SimpleFolderTreeBase : public TreeBase +{ + + public: + + + inline SimpleFolderTreeBase( TQWidget * parent, KMFolderTree *folderTree, + const TQString &preSelection, bool mustBeReadWrite ) + : TreeBase( parent, folderTree, preSelection, mustBeReadWrite ) + { + assert( folderTree ); + setFolderColumn( addColumn( i18n( "Folder" ) ) ); + setPathColumn( addColumn( i18n( "Path" ) ) ); + + setRootIsDecorated( true ); + setSorting( -1 ); + + reload( mustBeReadWrite, true, true, preSelection ); + + } + + virtual SimpleFolderTreeItem* createItem( TQListView * parent ) + { + return new SimpleFolderTreeItem( parent ); + } + + virtual SimpleFolderTreeItem* createItem( TQListView * parent, TQListViewItem* afterListViewItem ) + { + return new SimpleFolderTreeItem( parent, afterListViewItem ); + } + + virtual SimpleFolderTreeItem* createItem( TQListViewItem * parent, TQListViewItem* afterListViewItem ) + { + return new SimpleFolderTreeItem( parent, afterListViewItem ); + } + + virtual SimpleFolderTreeItem* createItem( TQListViewItem * parent ) + { + return new SimpleFolderTreeItem( parent ); + } + + inline void keyPressEvent( TQKeyEvent *e ) + { + const char ascii = e->ascii(); + if ( ascii == 8 || ascii == 127 ) { + if ( mFilter.length() > 0 ) { + mFilter.truncate( mFilter.length()-1 ); + applyFilter( mFilter ); + } + } else if ( !e->text().isEmpty() && e->text().length() == 1 && e->text().at( 0 ).isPrint() ) { + applyFilter( mFilter + e->text() ); + } else { + KListView::keyPressEvent( e ); + } + } + + void applyFilter( const TQString& filter ) + { + kdDebug(5006) << k_funcinfo << filter << endl ; + // Reset all items to visible, enabled, and open + TQListViewItemIterator clean( this ); + while ( clean.current() ) { + TQListViewItem * item = clean.current(); + item->setEnabled( true ); + item->setVisible( true ); + item->setOpen( true ); + ++clean; + } + + mFilter = filter; + + if ( filter.isEmpty() ) { + setColumnText( pathColumn(), i18n("Path") ); + return; + } + + // Set the visibility and enabled status of each list item. + // The recursive algorithm is necessary because visiblity + // changes are automatically applied to child nodes by TQt. + TQListViewItemIterator it( this ); + while ( it.current() ) { + TQListViewItem * item = it.current(); + if ( item->depth() <= 0 ) + recurseFilter( item, filter, pathColumn() ); + ++it; + } + + // Recolor the rows appropriately + recolorRows(); + + // Iterate through the list to find the first selectable item + TQListViewItemIterator first ( this ); + while ( first.current() ) { + SimpleFolderTreeItem * item = static_cast< SimpleFolderTreeItem * >( first.current() ); + + if ( item->isVisible() && item->isSelectable() ) { + setSelected( item, true ); + ensureItemVisible( item ); + break; + } + + ++first; + } + + // Display and save the current filter + if ( filter.length() > 0 ) + setColumnText( pathColumn(), i18n("Path") + " ( " + filter + " )" ); + else + setColumnText( pathColumn(), i18n("Path") ); + + mFilter = filter; + } + +}; + +typedef SimpleFolderTreeBase SimpleFolderTree; + +} + +#endif diff --git a/kmail/simplestringlisteditor.cpp b/kmail/simplestringlisteditor.cpp index 8d71e221..4be67e6e 100644 --- a/kmail/simplestringlisteditor.cpp +++ b/kmail/simplestringlisteditor.cpp @@ -166,6 +166,15 @@ TQStringList SimpleStringListEditor::stringList() const { return result; } +bool SimpleStringListEditor::containsString( const TQString & str ) { + for ( TQListBoxItem * item = mListBox->firstItem() ; + item ; item = item->next() ) { + if ( item->text() == str ) + return true; + } + return false; +} + void SimpleStringListEditor::setButtonText( ButtonCode button, const TQString & text ) { switch ( button ) { @@ -207,9 +216,10 @@ void SimpleStringListEditor::slotAdd() { &ok, this ); // let the user verify the string before adding emit aboutToAdd( newEntry ); - if ( ok && !newEntry.isEmpty() ) - mListBox->insertItem( newEntry ); - emit changed(); + if ( ok && !newEntry.isEmpty() && !containsString( newEntry )) { + mListBox->insertItem( newEntry ); + emit changed(); + } } void SimpleStringListEditor::slotRemove() { diff --git a/kmail/simplestringlisteditor.h b/kmail/simplestringlisteditor.h index 47c6f6c9..115a9a05 100644 --- a/kmail/simplestringlisteditor.h +++ b/kmail/simplestringlisteditor.h @@ -91,6 +91,7 @@ protected slots: void slotSelectionChanged(); protected: + bool containsString( const TQString & str ); TQListBox *mListBox; TQPushButton *mAddButton; TQPushButton *mRemoveButton; diff --git a/kmail/snippetdlg.cpp b/kmail/snippetdlg.cpp index 61c75db8..93478d13 100644 --- a/kmail/snippetdlg.cpp +++ b/kmail/snippetdlg.cpp @@ -1,6 +1,6 @@ /*************************************************************************** * snippet feature from kdevelop/plugins/snippet/ * - * * + * * * Copyright (C) 2007 by Robert Gruber * * rgruber@users.sourceforge.net * * * @@ -14,6 +14,7 @@ #include "snippetdlg.h" #include +#include #include #include @@ -42,6 +43,12 @@ SnippetDlg::SnippetDlg( KActionCollection* ac, TQWidget* parent, const char* nam connect( keyButton, TQT_SIGNAL( capturedShortcut( const KShortcut& ) ), this, TQT_SLOT( slotCapturedShortcut( const KShortcut& ) ) ); + btnAdd->setEnabled( false ); + connect( snippetName, TQT_SIGNAL(textChanged(const TQString &)), + this, TQT_SLOT(slotTextChanged(const TQString &)) ); + connect( snippetName, TQT_SIGNAL(returnPressed()), + this, TQT_SLOT(slotReturnPressed()) ); + layout3->addWidget( textLabel3, 7, 0 ); layout3->addWidget( keyButton, 7, 1 ); @@ -68,7 +75,7 @@ SnippetDlg::~SnippetDlg() */ void SnippetDlg::languageChange() { - textLabel3->setText( tr2i18n( "Sh&ortcut:" ) ); + textLabel3->setText( i18n( "Sh&ortcut:" ) ); } static bool shortcutIsValid( const KActionCollection* actionCollection, const KShortcut &sc ) @@ -105,4 +112,16 @@ void SnippetDlg::setShowShortcut( bool show ) keyButton->setShown( show ); } +void SnippetDlg::slotTextChanged( const TQString &text ) +{ + btnAdd->setEnabled( !text.isEmpty() ); +} + +void SnippetDlg::slotReturnPressed() +{ + if ( !snippetName->text().isEmpty() ) { + accept(); + } +} + #include "snippetdlg.moc" diff --git a/kmail/snippetdlg.h b/kmail/snippetdlg.h index fd990539..efd7bc53 100644 --- a/kmail/snippetdlg.h +++ b/kmail/snippetdlg.h @@ -20,7 +20,7 @@ class SnippetDlg : public SnippetDlgBase { Q_OBJECT -public: + public: SnippetDlg( KActionCollection* ac, TQWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 ); ~SnippetDlg(); @@ -31,12 +31,13 @@ public: KKeyButton* keyButton; KActionCollection* actionCollection; -private slots: - void slotCapturedShortcut( const KShortcut& ); - -protected slots: + protected slots: + void slotTextChanged( const TQString& ); + void slotReturnPressed(); virtual void languageChange(); + private slots: + void slotCapturedShortcut( const KShortcut& ); }; #endif // SNIPPETDLG_H diff --git a/kmail/snippetdlgbase.ui b/kmail/snippetdlgbase.ui index 5d87b564..a5a28a72 100644 --- a/kmail/snippetdlgbase.ui +++ b/kmail/snippetdlgbase.ui @@ -124,6 +124,9 @@ Group: + + cbGroup + diff --git a/kmail/snippetitem.cpp b/kmail/snippetitem.cpp index 206114ac..f9f3aa87 100644 --- a/kmail/snippetitem.cpp +++ b/kmail/snippetitem.cpp @@ -1,6 +1,6 @@ /*************************************************************************** * snippet feature from kdevelop/plugins/snippet/ * - * * + * * * Copyright (C) 2007 by Robert Gruber * * rgruber@users.sourceforge.net * * * @@ -23,6 +23,7 @@ SnippetItem::SnippetItem(TQListView * parent, TQString name, TQString text ) strName = name; strText = text; iParent = -1; + setOpen( true ); } SnippetItem::SnippetItem(TQListViewItem * parent, TQString name, TQString text) @@ -31,6 +32,7 @@ SnippetItem::SnippetItem(TQListViewItem * parent, TQString name, TQString text) strName = name; strText = text; iParent = ((SnippetGroup *)parent)->getId(); + setOpen( true ); } SnippetItem::~SnippetItem() @@ -86,7 +88,7 @@ void SnippetItem::resetParent() KAction* SnippetItem::getAction() -{ +{ return action; } @@ -128,7 +130,7 @@ Deklaration for class SnippetGroup int SnippetGroup::iMaxId = 1; SnippetGroup::SnippetGroup(TQListView * parent, TQString name, int id) - : SnippetItem(parent, name, "GROUP") + : SnippetItem(parent, name, i18n("GROUP")) { if (id > 0) { iId = id; @@ -146,7 +148,7 @@ SnippetGroup::~SnippetGroup() void SnippetGroup::setId(int id) { - iId = id; + iId = id; if (iId >= iMaxId) iMaxId = iId+1; } diff --git a/kmail/snippetwidget.cpp b/kmail/snippetwidget.cpp index 61702174..800a98e5 100644 --- a/kmail/snippetwidget.cpp +++ b/kmail/snippetwidget.cpp @@ -1,6 +1,6 @@ /*************************************************************************** * snippet feature from kdevelop/plugins/snippet/ * - * * + * * * Copyright (C) 2007 by Robert Gruber * * rgruber@users.sourceforge.net * * * @@ -168,7 +168,7 @@ void SnippetWidget::slotAddGroup() SnippetDlg dlg( mActionCollection, this, "SnippetDlg"); dlg.setShowShortcut( false ); dlg.snippetText->setEnabled(false); - dlg.snippetText->setText("GROUP"); + dlg.snippetText->setText(i18n("GROUP")); dlg.setCaption(i18n("Add Group")); dlg.cbGroup->insertItem(i18n("All")); dlg.cbGroup->setCurrentText(i18n("All")); diff --git a/kmail/stl_util.h b/kmail/stl_util.h index 69963df7..5e3e5ce6 100644 --- a/kmail/stl_util.h +++ b/kmail/stl_util.h @@ -32,6 +32,8 @@ #ifndef __KDEPIM__KMAIL__STL_UTIL_H__ #define __KDEPIM__KMAIL__STL_UTIL_H__ +#include + template struct DeleteAndSetToZero { void operator()( const T * & t ) { delete t; t = 0; } @@ -44,4 +46,19 @@ static inline void deleteAll( T & c ) { } } +namespace kdtools { + + template + bool any( Iterator first, Iterator last, UnaryPredicate p ) + { + while ( first != last ) + if ( p( *first ) ) + return true; + else + ++first; + return false; + } + +} // namespace kdtools + #endif // __KDEPIM__KMAIL__STL_UTIL_H__ diff --git a/kmail/stringutil.cpp b/kmail/stringutil.cpp new file mode 100644 index 00000000..907c5e1f --- /dev/null +++ b/kmail/stringutil.cpp @@ -0,0 +1,49 @@ +/* Copyright 2009 Thomas McGuire + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#include "stringutil.h" +#include "kmmsgbase.h" +#include + +namespace KMail +{ + +namespace StringUtil +{ +#ifndef KMAIL_UNITTESTS +TQString encodeMailtoUrl( const TQString& str ) +{ + TQString result; + result = TQString::fromLatin1( KMMsgBase::encodeRFC2047String( str, + "utf-8" ) ); + result = KURL::encode_string( result ); + return result; +} + +TQString decodeMailtoUrl( const TQString& url ) +{ + TQString result; + result = KURL::decode_string( url.latin1() ); + result = KMMsgBase::decodeRFC2047String( result.latin1() ); + return result; +} +#endif + +} + +} diff --git a/kmail/stringutil.h b/kmail/stringutil.h new file mode 100644 index 00000000..2528bdce --- /dev/null +++ b/kmail/stringutil.h @@ -0,0 +1,43 @@ +/* Copyright 2009 Thomas McGuire + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#ifndef KMAIL_STRINGUTIL_H +#define KMAIL_STRINGUTIL_H + +#include + +namespace KMail +{ +/** + * This namespace contain helper functions for string manipulation + */ +namespace StringUtil +{ + /** Encodes an email address as mailto URL + */ + TQString encodeMailtoUrl( const TQString& str ); + + /** Decodes a mailto URL + */ + TQString decodeMailtoUrl( const TQString& url ); +} + +} +#endif + + diff --git a/kmail/subscriptiondialog.cpp b/kmail/subscriptiondialog.cpp index 72531743..58d00b24 100644 --- a/kmail/subscriptiondialog.cpp +++ b/kmail/subscriptiondialog.cpp @@ -373,7 +373,7 @@ void SubscriptionDialog::doSave() KMail::ImapAccountBase *a = static_cast(mAcct); if( !a->onlySubscribedFolders() ) { int result = KMessageBox::questionYesNoCancel( this, - i18n("Currently subscriptions are not used for server %1\ndo you want to enable subscriptions?") + i18n("Currently subscriptions are not used for server. %1\nDo you want to enable subscriptions?") .arg( a->name() ), i18n("Enable Subscriptions?"), i18n("Enable"), i18n("Do Not Enable")); switch(result) { diff --git a/kmail/templateparser.cpp b/kmail/templateparser.cpp index 4112b58e..79df8509 100644 --- a/kmail/templateparser.cpp +++ b/kmail/templateparser.cpp @@ -43,21 +43,46 @@ #include "kmkernel.h" #include #include +#include "partNode.h" +#include "attachmentcollector.h" +#include "objecttreeparser.h" +#include "util.h" #include "templateparser.h" +#include -TemplateParser::TemplateParser( KMMessage *amsg, const Mode amode, - const TQString aselection, - bool asmartQuote, bool anoQuote, - bool aallowDecryption, bool aselectionIsBody ) : - mMode( amode ), mFolder( 0 ), mIdentity( 0 ), mSelection( aselection ), - mSmartQuote( asmartQuote ), mNoQuote( anoQuote ), - mAllowDecryption( aallowDecryption ), mSelectionIsBody( aselectionIsBody ), - mDebug( false ), mQuoteString( "> " ), mAppend( false ) +using namespace KMail; + +TemplateParser::TemplateParser( KMMessage *amsg, const Mode amode ) : + mMode( amode ), mFolder( 0 ), mIdentity( 0 ), + mAllowDecryption( false ), + mDebug( false ), mQuoteString( "> " ), mAppend( false ), mOrigRoot( 0 ) { mMsg = amsg; } +void TemplateParser::setSelection( const TQString &selection ) +{ + mSelection = selection; +} + +void TemplateParser::setAllowDecryption( const bool allowDecryption ) +{ + mAllowDecryption = allowDecryption; +} + +bool TemplateParser::shouldStripSignature() const +{ + // Only strip the signature when replying, it should be preserved when forwarding + return ( mMode == Reply || mMode == ReplyAll) && GlobalSettings::stripSignature(); +} + +TemplateParser::~TemplateParser() +{ + delete mOrigRoot; + mOrigRoot = 0; +} + int TemplateParser::parseQuotes( const TQString &prefix, const TQString &str, TQString "e ) const { @@ -279,36 +304,36 @@ void TemplateParser::processWithTemplate( const TQString &tmpl ) int len = parseQuotes( "QUOTEPIPE=", cmd, q ); i += len; TQString pipe_cmd = q; - if ( mOrigMsg && !mNoQuote ) { - TQString str = pipe( pipe_cmd, mSelection ); + if ( mOrigMsg ) { + TQString str = pipe( pipe_cmd, messageText( false ) ); TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, str, - mSmartQuote, mAllowDecryption ); + shouldStripSignature(), mAllowDecryption ); body.append( quote ); } } else if ( cmd.startsWith( "QUOTE" ) ) { kdDebug() << "Command: QUOTE" << endl; i += strlen( "QUOTE" ); - if ( mOrigMsg && !mNoQuote ) { - TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, mSelection, - mSmartQuote, mAllowDecryption ); + if ( mOrigMsg ) { + TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, messageText( true ), + shouldStripSignature(), mAllowDecryption ); body.append( quote ); } } else if ( cmd.startsWith( "QHEADERS" ) ) { kdDebug() << "Command: QHEADERS" << endl; i += strlen( "QHEADERS" ); - if ( mOrigMsg && !mNoQuote ) { + if ( mOrigMsg ) { TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, mOrigMsg->headerAsSendableString(), - mSmartQuote, false ); + false, false ); body.append( quote ); } } else if ( cmd.startsWith( "HEADERS" ) ) { kdDebug() << "Command: HEADERS" << endl; i += strlen( "HEADERS" ); - if ( mOrigMsg && !mNoQuote ) { + if ( mOrigMsg ) { TQString str = mOrigMsg->headerAsSendableString(); body.append( str ); } @@ -321,7 +346,7 @@ void TemplateParser::processWithTemplate( const TQString &tmpl ) i += len; TQString pipe_cmd = q; if ( mOrigMsg ) { - TQString str = pipe(pipe_cmd, mSelection ); + TQString str = pipe(pipe_cmd, messageText( false ) ); body.append( str ); } @@ -363,7 +388,7 @@ void TemplateParser::processWithTemplate( const TQString &tmpl ) kdDebug() << "Command: TEXT" << endl; i += strlen( "TEXT" ); if ( mOrigMsg ) { - TQString quote = mOrigMsg->asPlainText( false, mAllowDecryption ); + TQString quote = messageText( false ); body.append( quote ); } @@ -379,10 +404,22 @@ void TemplateParser::processWithTemplate( const TQString &tmpl ) kdDebug() << "Command: OTEXT" << endl; i += strlen( "OTEXT" ); if ( mOrigMsg ) { - TQString quote = mOrigMsg->asPlainText( false, mAllowDecryption ); + TQString quote = messageText( false ); body.append( quote ); } + } else if ( cmd.startsWith( "OADDRESSEESADDR" ) ) { + kdDebug() << "Command: OADDRESSEESADDR" << endl; + i += strlen( "OADDRESSEESADDR" ); + const TQString to = mOrigMsg->to(); + const TQString cc = mOrigMsg->cc(); + if ( !to.isEmpty() ) + body.append( i18n( "To:" ) + ' ' + to ); + if ( !to.isEmpty() && !cc.isEmpty() ) + body.append( '\n' ); + if ( !cc.isEmpty() ) + body.append( i18n( "CC:" ) + ' ' + cc ); + } else if ( cmd.startsWith( "CCADDR" ) ) { kdDebug() << "Command: CCADDR" << endl; i += strlen( "CCADDR" ); @@ -829,20 +866,124 @@ void TemplateParser::processWithTemplate( const TQString &tmpl ) } } - // kdDebug() << "Message body: " << body << endl; + addProcessedBodyToMessage( body ); +} + +TQString TemplateParser::messageText( bool allowSelectionOnly ) +{ + if ( !mSelection.isEmpty() && allowSelectionOnly ) + return mSelection; + + // No selection text, therefore we need to parse the object tree ourselves to get + partNode *root = parsedObjectTree(); + return mOrigMsg->asPlainTextFromObjectTree( root, shouldStripSignature(), mAllowDecryption ); +} +partNode* TemplateParser::parsedObjectTree() +{ + if ( mOrigRoot ) + return mOrigRoot; + + mOrigRoot = partNode::fromMessage( mOrigMsg ); + ObjectTreeParser otp; // all defaults are ok + otp.parseObjectTree( mOrigRoot ); + return mOrigRoot; +} + +void TemplateParser::addProcessedBodyToMessage( const TQString &body ) +{ if ( mAppend ) { + + // ### What happens here if the body is multipart or in some way encoded? TQCString msg_body = mMsg->body(); msg_body.append( body.utf8() ); mMsg->setBody( msg_body ); - } else { - mMsg->setBodyFromUnicode( body ); + } + else { + + // Get the attachments of the original mail + partNode *root = parsedObjectTree(); + AttachmentCollector ac; + ac.collectAttachmentsFrom( root ); + + // Now, delete the old content and set the new content, which + // is either only the new text or the new text with some attachments. + mMsg->deleteBodyParts(); + + // Set To and CC from the template + if ( mMode == Forward ) { + if ( !mTo.isEmpty() ) { + mMsg->setTo( mMsg->to() + ',' + mTo ); + } + if ( !mCC.isEmpty() ) + mMsg->setCc( mMsg->cc() + ',' + mCC ); + } + + // If we have no attachment, simply create a text/plain part and + // set the processed template text as the body + if ( ac.attachments().empty() || mMode != Forward ) { + mMsg->headers().ContentType().FromString( DwString() ); // to get rid of old boundary + mMsg->headers().ContentType().Parse(); + mMsg->headers().ContentType().SetType( DwMime::kTypeText ); + mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypePlain ); + mMsg->headers().Assemble(); + mMsg->setBodyFromUnicode( body ); + mMsg->assembleIfNeeded(); + } + + // If we have some attachments, create a multipart/mixed mail and + // add the normal body as well as the attachments + else + { + mMsg->headers().ContentType().SetType( DwMime::kTypeMultipart ); + mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed ); + mMsg->headers().ContentType().CreateBoundary( 0 ); + + KMMessagePart textPart; + textPart.setBodyFromUnicode( body ); + mMsg->addDwBodyPart( mMsg->createDWBodyPart( &textPart ) ); + mMsg->assembleIfNeeded(); + + int attachmentNumber = 1; + for ( std::vector::const_iterator it = ac.attachments().begin(); + it != ac.attachments().end(); ++it, attachmentNumber++ ) { + + // When adding this body part, make sure to _not_ add the next bodypart + // as well, which mimelib would do, therefore creating a mail with many + // duplicate attachments (so many that KMail runs out of memory, in fact). + // Body::AddBodyPart is very misleading here... + ( *it )->dwPart()->SetNext( 0 ); + + DwBodyPart *cloned = static_cast( ( *it )->dwPart()->Clone() ); + + // If the content type has no name or filename parameter, add one, since otherwise the name + // would be empty in the attachment view of the composer, which looks confusing + if ( cloned->Headers().HasContentType() ) { + DwMediaType &ct = cloned->Headers().ContentType(); + + // Converting to a string here, since DwMediaType does not have a HasParameter() function + TQString ctStr = ct.AsString().c_str(); + if ( !ctStr.lower().contains( "name=" ) && !ctStr.lower().contains( "filename=" ) ) { + DwParameter *nameParameter = new DwParameter; + nameParameter->SetAttribute( "name" ); + nameParameter->SetValue( Util::dwString( KMMsgBase::encodeRFC2231StringAutoDetectCharset( + i18n( "Attachment %1" ).arg( attachmentNumber ) ) ) ); + ct.AddParameter( nameParameter ); + } + } + + mMsg->addDwBodyPart( cloned ); + mMsg->assembleIfNeeded(); + } + } } } TQString TemplateParser::findCustomTemplate( const TQString &tmplName ) { CTemplates t( tmplName ); + mTo = t.to(); + mCC = t.cC(); TQString content = t.content(); if ( !content.isEmpty() ) { return content; diff --git a/kmail/templateparser.h b/kmail/templateparser.h index 7e432ac3..3cb53ac1 100644 --- a/kmail/templateparser.h +++ b/kmail/templateparser.h @@ -29,7 +29,29 @@ class KMFolder; class TQObject; class KProcess; -class TemplateParser : public QObject +/** + * The TemplateParser transforms a message with a given template. + * + * A template contains text and commands, such as %QUOTE or %ODATE, which will be + * replaced with the real values in process(). + * + * The message given in the constructor is the message that is being transformed. + * The message text will be replaced by the processed text of the template, but other + * properties, such as the attachments or the subject, are preserved. + * + * There are two different kind of commands: Those that work on the message that is + * to be transformed and those that work on an 'original message'. + * Those that work on the message that is to be transformed have no special prefix, e.g. + * '%DATE'. Those that work on the original message have an 'O' prefix, for example + * '%ODATE'. + * This means that the %DATE command will take the date of the message passed in the + * constructor, the message which is to be transformed, whereas the %ODATE command will + * take the date of the message that is being passed in process(), the original message. + * + * TODO: What is the usecase of the commands that work on the message to be transformed? + * In general you only use the commands that work on the original message... + */ +class TemplateParser : public TQObject { Q_OBJECT @@ -44,16 +66,43 @@ class TemplateParser : public QObject static const int PipeTimeout = 15; public: - TemplateParser( KMMessage *amsg, const Mode amode, const TQString aselection, - bool aSmartQuote, bool anoQuote, bool aallowDecryption, - bool aselectionIsBody ); + TemplateParser( KMMessage *amsg, const Mode amode ); + ~TemplateParser(); + + /** + * Sets the selection. If this is set, only the selection will be added to commands such + * as %QUOTE. Otherwise, the whole message is quoted. + * If this is not called at all, the whole message is quoted as well. + * Call this before calling process(). + */ + void setSelection( const TQString &selection ); + + /** + * Sets whether the template parser is allowed to decrypt the original message when needing + * its message text, for example for the %QUOTE command. + * If true, it will tell the ObjectTreeParser it uses internally to decrypt the message, + * and that will possibly show a password request dialog to the user. + * + * The default is false. + */ + void setAllowDecryption( const bool allowDecryption ); - virtual void process( KMMessage *aorig_msg, KMFolder *afolder = NULL, bool append = false ); + virtual void process( KMMessage *aorig_msg, KMFolder *afolder = 0, bool append = false ); virtual void process( const TQString &tmplName, KMMessage *aorig_msg, - KMFolder *afolder = NULL, bool append = false ); + KMFolder *afolder = 0, bool append = false ); virtual void processWithTemplate( const TQString &tmpl ); + + /// This finds the template to use. Either the one from the folder, identity or + /// finally the global template. + /// This also reads the To and CC address of the template + /// @return the contents of the template virtual TQString findTemplate(); + + /// Finds the template with the given name. + /// This also reads the To and CC address of the template + /// @return the contents of the template virtual TQString findCustomTemplate( const TQString &tmpl ); + virtual TQString pipe( const TQString &cmd, const TQString &buf ); virtual TQString getFName( const TQString &str ); @@ -66,16 +115,48 @@ class TemplateParser : public QObject KMMessage *mMsg; KMMessage *mOrigMsg; TQString mSelection; - bool mSmartQuote; - bool mNoQuote; bool mAllowDecryption; - bool mSelectionIsBody; int mPipeRc; TQString mPipeOut; TQString mPipeErr; bool mDebug; TQString mQuoteString; bool mAppend; + TQString mTo, mCC; + partNode *mOrigRoot; + + /** + * If there was a text selection set in the constructor, that will be returned. + * Otherwise, returns the plain text of the original message, as in KMMessage::asPlainText(). + * The only difference is that this uses the cached object tree from parsedObjectTree() + * + * @param allowSelectionOnly if false, it will always return the complete mail text + */ + TQString messageText( bool allowSelectionOnly ); + + /** + * Returns the parsed object tree of the original message. + * The result is cached in mOrigRoot, therefore calling this multiple times will only parse + * the tree once. + */ + partNode* parsedObjectTree(); + + /** + * Called by processWithTemplate(). This adds the completely processed body to + * the message. + * + * In append mode, this will simply append the text to the body. + * + * Otherwise, the content of the old message is deleted and replaced with @p body. + * Attachments of the original message are also added back to the new message. + */ + void addProcessedBodyToMessage( const TQString &body ); + + /** + * Determines whether the signature should be stripped when getting the text of the original + * message, e.g. for commands such as %QUOTE + */ + bool shouldStripSignature() const; int parseQuotes( const TQString &prefix, const TQString &str, TQString "e ) const; diff --git a/kmail/templatesconfiguration.cpp b/kmail/templatesconfiguration.cpp index 1e2d74cb..8b4f37e8 100644 --- a/kmail/templatesconfiguration.cpp +++ b/kmail/templatesconfiguration.cpp @@ -394,9 +394,9 @@ void TemplatesConfiguration::importFromPhrases() "---------- %1 ----------\n" "\n" "Subject: %OFULLSUBJECT\n" - "Date: %ODATE\n" + "Date: %ODATE, %OTIMELONG\n" "From: %OFROMADDR\n" - "To: %OTOADDR\n" + "%OADDRESSEESADDR\n" "\n" "%TEXT\n" "-------------------------------------------------------\n" @@ -509,38 +509,43 @@ void TemplatesConfiguration::slotInsertCommand( TQString cmd, int adjustCursor ) TQString TemplatesConfiguration::defaultNewMessage() { return i18n( "%REM=\"Default new message template\"%-\n" - "%BLANK" + "%BLANK\n" + "%BLANK\n" + "%BLANK\n" ); } TQString TemplatesConfiguration::defaultReply() { return i18n( + "%CURSOR\n" + "%BLANK\n" "%REM=\"Default reply template\"%-\n" "On %ODATEEN %OTIMELONGEN you wrote:\n" "%QUOTE\n" - "%CURSOR\n" ); } TQString TemplatesConfiguration::defaultReplyAll() { return i18n( + "%CURSOR\n" + "%BLANK\n" "%REM=\"Default reply all template\"%-\n" "On %ODATEEN %OTIMELONGEN %OFROMNAME wrote:\n" "%QUOTE\n" - "%CURSOR\n" ); } -TQString TemplatesConfiguration::defaultForward() { +TQString TemplatesConfiguration::defaultForward() +{ return i18n( "%REM=\"Default forward template\"%-\n" "\n" "---------- Forwarded Message ----------\n" "\n" "Subject: %OFULLSUBJECT\n" - "Date: %ODATE\n" + "Date: %ODATE, %OTIMELONG\n" "From: %OFROMADDR\n" - "To: %OTOADDR\n" + "%OADDRESSEESADDR\n" "\n" "%TEXT\n" "-------------------------------------------------------\n" diff --git a/kmail/templatesinsertcommand.cpp b/kmail/templatesinsertcommand.cpp index ee07c375..45bd81fc 100644 --- a/kmail/templatesinsertcommand.cpp +++ b/kmail/templatesinsertcommand.cpp @@ -139,6 +139,9 @@ TemplatesInsertCommand::TemplatesInsertCommand( TQWidget *parent, 0, mapper, TQT_SLOT( map() ), menu ); mapper->setMapping( action, COFromLName ); menu->insert( action ); + action = new KAction( i18n( "Addresses of all original recipients" ), + 0, mapper, TQT_SLOT( map() ), menu ); + mapper->setMapping( action, COAddresseesAddr ); action = new KAction( i18n( "Subject" ), 0, mapper, TQT_SLOT( map() ), menu ); mapper->setMapping( action, COFullSubject ); @@ -365,6 +368,7 @@ void TemplatesInsertCommand::slotMapped( int cmd ) case TemplatesInsertCommand::CTime: emit insertCommand("%TIME"); break; case TemplatesInsertCommand::CTimeLong: emit insertCommand("%TIMELONG"); break; case TemplatesInsertCommand::CTimeLongEn: emit insertCommand("%TIMELONGEN"); break; + case TemplatesInsertCommand::COAddresseesAddr: emit insertCommand("%OADDRESSEESADDR"); break; case TemplatesInsertCommand::CToAddr: emit insertCommand("%TOADDR"); break; case TemplatesInsertCommand::CToName: emit insertCommand("%TONAME"); break; case TemplatesInsertCommand::CToFName: emit insertCommand("%TOFNAME"); break; diff --git a/kmail/templatesinsertcommand.h b/kmail/templatesinsertcommand.h index 4a94a979..04878136 100644 --- a/kmail/templatesinsertcommand.h +++ b/kmail/templatesinsertcommand.h @@ -44,7 +44,8 @@ class TemplatesInsertCommand : public QPushButton CODateEn, CODateShort, CODate, CODow, COTimeLongEn, COTimeLong, COTime, CBlank, CNop, CClear, CDebug, CDebugOff, CToFName, CToLName, CFromFName, CFromLName, COToFName, COToLName, COFromFName, COFromLName, CCursor, - CCCAddr, CCCName, CCCFName, CCCLName, COCCAddr, COCCName, COCCFName, COCCLName }; + CCCAddr, CCCName, CCCFName, CCCLName, COCCAddr, COCCName, COCCFName, COCCLName, + COAddresseesAddr }; signals: void insertCommand( TemplatesInsertCommand::Command cmd ); diff --git a/kmail/treebase.cpp b/kmail/treebase.cpp new file mode 100644 index 00000000..19aff315 --- /dev/null +++ b/kmail/treebase.cpp @@ -0,0 +1,235 @@ +/* + Copyright (c) 2008 Pradeepto K. Bhattacharya + ( adapted from kdepim/kmail/kmfolderseldlg.cpp and simplefoldertree.h ) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "treebase.h" +#include "kmfolder.h" +#include "kmfoldertree.h" +#include "simplefoldertree.h" + +#include +#include + +using namespace KMail; + +TreeBase::TreeBase( TQWidget *parent, KMFolderTree *folderTree, + const TQString &preSelection, bool mustBeReadWrite ) + : KListView( parent ), mFolderTree( folderTree ) +{ + Q_UNUSED( preSelection ); + Q_UNUSED( mustBeReadWrite ); + kdDebug(5006) << k_funcinfo << endl; + + connect(this, TQT_SIGNAL(collapsed(TQListViewItem*)), TQT_SLOT(recolorRows())); + connect(this, TQT_SIGNAL(expanded(TQListViewItem*)), TQT_SLOT(recolorRows())); + connect( this, TQT_SIGNAL( contextMenuRequested( TQListViewItem*, const TQPoint &, int ) ), + this, TQT_SLOT( slotContextMenuRequested( TQListViewItem*, const TQPoint & ) ) ); + +} + +const KMFolder * TreeBase::folder() const +{ + TQListViewItem * item = currentItem(); + if( item ) { + TreeItemBase *base = dynamic_cast( item ); + assert(base); + const KMFolder * folder = base->folder(); + return folder; + } + return 0; +} + +void TreeBase::setFolder( KMFolder *folder ) + { + for ( TQListViewItemIterator it( this ) ; it.current() ; ++it ) + { + const KMFolder *fld = dynamic_cast( it.current() )->folder(); + if ( fld == folder ) + { + setSelected( it.current(), true ); + ensureItemVisible( it.current() ); + } + } +} + +void TreeBase::addChildFolder() +{ + kdDebug(5006) << k_funcinfo << endl; + + const KMFolder *fld = folder(); + if ( fld ) { + mFolderTree->addChildFolder( (KMFolder *) fld, parentWidget() ); + reload( mLastMustBeReadWrite, mLastShowOutbox, mLastShowImapFolders ); + setFolder( (KMFolder *) fld ); + } +} + +void TreeBase::slotContextMenuRequested( TQListViewItem *lvi, const TQPoint &p ) +{ + kdDebug(5006) << k_funcinfo << endl; + + if (!lvi) + return; + setCurrentItem( lvi ); + setSelected( lvi, TRUE ); + + const KMFolder * folder = dynamic_cast( lvi )->folder(); + if ( !folder || folder->noContent() || folder->noChildren() ) + return; + + KPopupMenu *folderMenu = new KPopupMenu; + folderMenu->insertTitle( folder->label() ); + folderMenu->insertSeparator(); + folderMenu->insertItem(SmallIconSet("folder_new"), + i18n("&New Subfolder..."), this, + TQT_SLOT(addChildFolder())); + kmkernel->setContextMenuShown( true ); + folderMenu->exec (p, 0); + kmkernel->setContextMenuShown( false ); + delete folderMenu; + +} + +void TreeBase::recolorRows() +{ + kdDebug(5006) << k_funcinfo << endl; + + // Iterate through the list to set the alternate row flags. + int alt = 0; + TQListViewItemIterator it ( this ); + while ( it.current() ) { + TQListViewItem * item = it.current() ; + if ( item->isVisible() ) { + bool visible = true; + TQListViewItem * parent = item->parent(); + while ( parent ) { + if (!parent->isOpen()) { + visible = false; + break; + } + parent = parent->parent(); + } + + if ( visible ) { + TreeItemBase * treeItemBase = dynamic_cast( item ); + treeItemBase->setAlternate( alt ); + alt = !alt; + } + } + ++it; + } +} + +void TreeBase::reload( bool mustBeReadWrite, bool showOutbox, bool showImapFolders, + const TQString& preSelection ) +{ + clear(); + + mLastMustBeReadWrite = mustBeReadWrite; + mLastShowOutbox = showOutbox; + mLastShowImapFolders = showImapFolders; + + TQListViewItem * lastItem = 0; + TQListViewItem * lastTopItem = 0; + TQListViewItem * selectedItem = 0; + int lastDepth = 0; + + mFilter = ""; + TQString path; + + for ( TQListViewItemIterator it( mFolderTree ) ; it.current() ; ++it ) { + KMFolderTreeItem * fti = dynamic_cast( it.current() ); + + if ( !fti || fti->protocol() == KFolderTreeItem::Search ) + continue; + + int depth = fti->depth();// - 1; + //kdDebug( 5006 ) << "LastDepth=" << lastDepth << "\tdepth=" << depth + // << "\tname=" << fti->text( 0 ) << endl; + TQListViewItem * item = 0; + if ( depth <= 0 ) { + // top level - first top level item or after last existing top level item + if ( lastTopItem ) + item = createItem( this, lastTopItem ); + else + item = createItem( this ); + lastTopItem = item; + depth = 0; + path = ""; + } + else { + if ( depth > lastDepth ) { + // next lower level - parent node will get opened + item = createItem( lastItem ); + lastItem->setOpen( true ); + } + else { + + path = path.section( '/', 0, -2 - (lastDepth-depth) ); + if ( depth == lastDepth ) + // same level - behind previous item + item = createItem( lastItem->parent(), lastItem ); + else if ( depth < lastDepth ) { + // above previous level - might be more than one level difference + // but highest possibility is top level + while ( ( depth <= --lastDepth ) && lastItem->parent() ) { + lastItem = static_cast( lastItem->parent() ); + } + if ( lastItem->parent() ) + item = createItem( lastItem->parent(), lastItem ); + else { + // chain somehow broken - what does cause this ??? + kdDebug( 5006 ) << "You shouldn't get here: depth=" << depth + << "folder name=" << fti->text( 0 ) << endl; + item = createItem( this ); + lastTopItem = item; + } + } + } + } + + if ( depth > 0 ) + path += "/"; + path += fti->text( 0 ); + + + item->setText( mFolderColumn, fti->text( 0 ) ); + item->setText( mPathColumn, path ); + // Make items without folders and top level items unselectable + // (i.e. root item Local Folders and IMAP accounts) + if ( !fti->folder() || depth == 0 || ( mustBeReadWrite && fti->folder()->isReadOnly() ) ) { + item->setSelectable( false ); + } else { + TreeItemBase * treeItemBase = dynamic_cast( item ); + assert(treeItemBase); + treeItemBase->setFolder( fti->folder() ); + if ( preSelection == treeItemBase->folder()->idString() ) + selectedItem = item; + } + lastItem = item; + lastDepth = depth; + } + + if ( selectedItem ) { + setSelected( selectedItem, true ); + ensureItemVisible( selectedItem ); + } + +} + +#include "treebase.moc" diff --git a/kmail/treebase.h b/kmail/treebase.h new file mode 100644 index 00000000..3822add2 --- /dev/null +++ b/kmail/treebase.h @@ -0,0 +1,83 @@ +/* + Copyright (c) 2008 Pradeepto K. Bhattacharya + ( adapted from kdepim/kmail/kmfolderseldlg.cpp and simplefoldertree.h ) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef KMAIL_TREEBASE_H +#define KMAIL_TREEBASE_H + +#include "kmfolder.h" +#include "kmfoldertree.h" + +#include +#include + +namespace KMail { + +class TreeItemBase; + +class TreeBase : public KListView +{ + Q_OBJECT + public: + TreeBase( TQWidget * parent, KMFolderTree *folderTree, + const TQString &preSelection, bool mustBeReadWrite ); + + virtual ~TreeBase() {} + + const KMFolder * folder() const; + /** Set the current folder */ + void setFolder( KMFolder *folder ); + + inline void setFolder( const TQString& idString ) + { + setFolder( kmkernel->findFolderById( idString ) ); + } + + void reload( bool mustBeReadWrite, bool showOutbox, bool showImapFolders, + const TQString& preSelection = TQString::null ); + + int folderColumn() const { return mFolderColumn; } + void setFolderColumn( const int folderCol ) { mFolderColumn = folderCol; } + int pathColumn() const { return mPathColumn; } + void setPathColumn( const int pathCol ) { mPathColumn = pathCol; } + + public slots: + void addChildFolder(); + protected slots: + void slotContextMenuRequested( TQListViewItem *lvi, + const TQPoint &p ); + void recolorRows(); +protected: + virtual TQListViewItem* createItem( TQListView* ) = 0; + virtual TQListViewItem* createItem( TQListView*, TQListViewItem* ) = 0; + virtual TQListViewItem* createItem( TQListViewItem* ) = 0; + virtual TQListViewItem* createItem( TQListViewItem*, TQListViewItem* ) = 0; + + protected: + KMFolderTree* mFolderTree; + TQString mFilter; + bool mLastMustBeReadWrite; + bool mLastShowOutbox; + bool mLastShowImapFolders; + /** Folder and path column IDs. */ + int mFolderColumn; + int mPathColumn; + +}; +} +#endif diff --git a/kmail/urlhandlermanager.cpp b/kmail/urlhandlermanager.cpp index 100009fa..8ab2a25f 100644 --- a/kmail/urlhandlermanager.cpp +++ b/kmail/urlhandlermanager.cpp @@ -2,7 +2,7 @@ urlhandlermanager.cpp This file is part of KMail, the KDE mail client. - Copyright (c) 2002-2003 Klar�vdalens Datakonsult AB + Copyright (c) 2002-2003 Klar�lvdalens Datakonsult AB Copyright (c) 2003 Marc Mutz KMail is free software; you can redistribute it and/or modify it @@ -43,9 +43,11 @@ #include "kmreaderwin.h" #include "kmkernel.h" #include "callback.h" +#include "stl_util.h" +#include +#include #include -#include "stl_util.h" #include #include @@ -123,8 +125,14 @@ namespace { ~AttachmentURLHandler() {} bool handleClick( const KURL &, KMReaderWin * ) const; + bool handleShiftClick( const KURL &url, KMReaderWin *window ) const; + bool handleDrag( const KURL &url, const TQString& imagePath, KMReaderWin *window ) const; + bool willHandleDrag( const KURL &url, const TQString& imagePath, KMReaderWin *window ) const; bool handleContextMenuRequest( const KURL &, const TQPoint &, KMReaderWin * ) const; TQString statusBarMessage( const KURL &, KMReaderWin * ) const; + private: + partNode* partNodeForUrl( const KURL &url, KMReaderWin *w ) const; + bool attachmentIsInHeader( const KURL &url ) const; }; class ShowAuditLogURLHandler : public KMail::URLHandler { @@ -137,6 +145,24 @@ namespace { TQString statusBarMessage( const KURL &, KMReaderWin * ) const; }; + // Handler that prevents dragging of internal images added by KMail, such as the envelope image + // in the enterprise header + class InternalImageURLHandler : public KMail::URLHandler { + public: + InternalImageURLHandler() : KMail::URLHandler() + {} + ~InternalImageURLHandler() + {} + bool handleDrag( const KURL &url, const TQString& imagePath, KMReaderWin *window ) const; + bool willHandleDrag( const KURL &url, const TQString& imagePath, KMReaderWin *window ) const; + bool handleClick( const KURL &, KMReaderWin * ) const + { return false; } + bool handleContextMenuRequest( const KURL &, const TQPoint &, KMReaderWin * ) const + { return false; } + TQString statusBarMessage( const KURL &, KMReaderWin * ) const + { return TQString(); } + }; + class FallBackURLHandler : public KMail::URLHandler { public: FallBackURLHandler() : KMail::URLHandler() {} @@ -212,7 +238,7 @@ static partNode * partNodeFromXKMailUrl( const KURL & url, KMReaderWin * w, TQSt const int part_id = urlParts[1].toInt( &ok ); if ( !ok ) return 0; - *path = KURL::decode_string( urlParts[2], 106 ); + *path = KURL::decode_string( urlParts[2] ); return w->partNodeForId( part_id ); } @@ -274,6 +300,7 @@ KMail::URLHandlerManager::URLHandlerManager() { registerHandler( new AttachmentURLHandler() ); registerHandler( mBodyPartURLHandlerManager = new BodyPartURLHandlerManager() ); registerHandler( new ShowAuditLogURLHandler() ); + registerHandler( new InternalImageURLHandler ); registerHandler( new FallBackURLHandler() ); } @@ -311,6 +338,32 @@ bool KMail::URLHandlerManager::handleClick( const KURL & url, KMReaderWin * w ) return false; } +bool KMail::URLHandlerManager::handleShiftClick( const KURL &url, KMReaderWin *window ) const +{ + for ( HandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it ) + if ( (*it)->handleShiftClick( url, window ) ) + return true; + return false; +} + +bool KMail::URLHandlerManager::willHandleDrag( const KURL &url, const TQString& imagePath, + KMReaderWin *window ) const +{ + for ( HandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it ) + if ( (*it)->willHandleDrag( url, imagePath, window ) ) + return true; + return false; +} + +bool KMail::URLHandlerManager::handleDrag( const KURL &url, const TQString& imagePath, + KMReaderWin *window ) const +{ + for ( HandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it ) + if ( (*it)->handleDrag( url, imagePath, window ) ) + return true; + return false; +} + bool KMail::URLHandlerManager::handleContextMenuRequest( const KURL & url, const TQPoint & p, KMReaderWin * w ) const { for ( HandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it ) if ( (*it)->handleContextMenuRequest( url, p, w ) ) @@ -405,6 +458,13 @@ namespace { return true; } + if ( url.path() == "showRawToltecMail" ) { + w->saveRelativePosition(); + w->setShowRawToltecMail( true ); + w->update( true ); + return true; + } + // if ( url.path() == "startIMApp" ) // { // kmkernel->imProxy()->startPreferredApp(); @@ -430,6 +490,10 @@ namespace { return i18n("Show signature details."); if ( url.path() == "hideSignatureDetails" ) return i18n("Hide signature details."); + if ( url.path() == "hideAttachmentQuicklist" ) + return i18n( "Hide attachment list" ); + if ( url.path() == "showAttachmentQuicklist" ) + return i18n( "Show attachment list" ); } return TQString::null ; } @@ -459,11 +523,14 @@ namespace { if ( url.protocol() == "kmail" && url.path() == "levelquote" ) { TQString query= url.query(); - if ( query.length()>=2 ) - if ( query[ 1 ] =='-' ) + if ( query.length()>=2 ) { + if ( query[ 1 ] =='-' ) { return i18n("Expand all quoted text."); - else + } + else { return i18n("Collapse quoted text."); + } + } } return TQString::null ; } @@ -517,32 +584,106 @@ namespace { } namespace { - bool AttachmentURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const { + + partNode* AttachmentURLHandler::partNodeForUrl( const KURL &url, KMReaderWin *w ) const + { if ( !w || !w->message() ) + return 0; + if ( url.protocol() != "attachment" ) + return 0; + + bool ok; + int nodeId = url.path().toInt( &ok ); + if ( !ok ) + return 0; + + partNode * node = w->partNodeForId( nodeId ); + return node; + } + + bool AttachmentURLHandler::attachmentIsInHeader( const KURL &url ) const + { + bool inHeader = false; + const TQString place = url.queryItem( "place" ).lower(); + if ( place != TQString::null ) { + inHeader = ( place == "header" ); + } + return inHeader; + } + + bool AttachmentURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const + { + partNode * node = partNodeForUrl( url, w ); + if ( !node ) + return false; + + const bool inHeader = attachmentIsInHeader( url ); + const bool shouldShowDialog = !node->isDisplayedEmbedded() || !inHeader; + if ( inHeader ) + w->scrollToAttachment( node ); + if ( shouldShowDialog ) + w->openAttachment( node->nodeId(), w->tempFileUrlFromPartNode( node ).path() ); + return true; + } + + bool AttachmentURLHandler::handleShiftClick( const KURL &url, KMReaderWin *window ) const + { + partNode * node = partNodeForUrl( url, window ); + if ( !node ) return false; - const int id = KMReaderWin::msgPartFromUrl( url ); - if ( id <= 0 ) + if ( !window ) return false; - w->openAttachment( id, url.path() ); + window->saveAttachment( window->tempFileUrlFromPartNode( node ) ); return true; } - bool AttachmentURLHandler::handleContextMenuRequest( const KURL & url, const TQPoint & p, KMReaderWin * w ) const { - if ( !w || !w->message() ) + bool AttachmentURLHandler::willHandleDrag( const KURL &url, const TQString& imagePath, + KMReaderWin *window ) const + { + Q_UNUSED( imagePath ); + return partNodeForUrl( url, window ) != 0; + } + + bool AttachmentURLHandler::handleDrag( const KURL &url, const TQString& imagePath, + KMReaderWin *window ) const + { + Q_UNUSED( imagePath ); + const partNode * node = partNodeForUrl( url, window ); + if ( !node ) return false; - const int id = KMReaderWin::msgPartFromUrl( url ); - if ( id <= 0 ) + + KURL file = window->tempFileUrlFromPartNode( node ).path(); + if ( !file.isEmpty() ) { + TQString icon = node->msgPart().iconName( KIcon::Small ); + KURLDrag* urlDrag = new KURLDrag( file, window ); + if ( !icon.isEmpty() ) { + TQPixmap iconMap( icon ); + urlDrag->setPixmap( iconMap ); + } + urlDrag->drag(); + return true; + } + else { + return false; + } + } + + bool AttachmentURLHandler::handleContextMenuRequest( const KURL & url, const TQPoint & p, KMReaderWin * w ) const + { + partNode * node = partNodeForUrl( url, w ); + if ( !node ) return false; - w->showAttachmentPopup( id, url.path(), p ); + + w->showAttachmentPopup( node->nodeId(), w->tempFileUrlFromPartNode( node ).path(), p ); return true; } - TQString AttachmentURLHandler::statusBarMessage( const KURL & url, KMReaderWin * w ) const { - if ( !w || !w->message() ) - return TQString::null; - const partNode * node = w->partNodeFromUrl( url ); + TQString AttachmentURLHandler::statusBarMessage( const KURL & url, KMReaderWin * w ) const + { + partNode * node = partNodeForUrl( url, w ); if ( !node ) return TQString::null; + const KMMessagePart & msgPart = node->msgPart(); TQString name = msgPart.fileName(); if ( name.isEmpty() ) @@ -569,7 +710,9 @@ namespace { return true; } - bool ShowAuditLogURLHandler::handleContextMenuRequest( const KURL & url, const TQPoint &, KMReaderWin * w ) const { + bool ShowAuditLogURLHandler::handleContextMenuRequest( const KURL & url, const TQPoint &, KMReaderWin * w ) const + { + Q_UNUSED( w ); // disable RMB for my own links: return !extractAuditLog( url ).isEmpty(); } @@ -582,6 +725,30 @@ namespace { } } +namespace { + bool InternalImageURLHandler::handleDrag( const KURL &url, const TQString& imagePath, + KMReaderWin *window ) const + { + Q_UNUSED( window ); + Q_UNUSED( url ); + const TQString kmailImagePath = locate( "data", "kmail/pics/" ); + if ( imagePath.contains( kmailImagePath ) ) { + // Do nothing, don't start a drag + return true; + } + return false; + } + + bool InternalImageURLHandler::willHandleDrag( const KURL &url, const TQString& imagePath, + KMReaderWin *window ) const + { + Q_UNUSED( window ); + Q_UNUSED( url ); + const TQString kmailImagePath = locate( "data", "kmail/pics/" ); + return imagePath.contains( kmailImagePath ); + } +} + namespace { bool FallBackURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const { if ( w ) diff --git a/kmail/urlhandlermanager.h b/kmail/urlhandlermanager.h index acd9f284..81595259 100644 --- a/kmail/urlhandlermanager.h +++ b/kmail/urlhandlermanager.h @@ -72,7 +72,10 @@ namespace KMail { void unregisterHandler( const Interface::BodyPartURLHandler * handler ); bool handleClick( const KURL & url, KMReaderWin * w=0 ) const; + bool handleShiftClick( const KURL &url, KMReaderWin *window = 0 ) const; bool handleContextMenuRequest( const KURL & url, const TQPoint & p, KMReaderWin * w=0 ) const; + bool willHandleDrag( const KURL &url, const TQString& imagePath, KMReaderWin *window = 0 ) const; + bool handleDrag( const KURL &url, const TQString& imagePath, KMReaderWin *window = 0 ) const; TQString statusBarMessage( const KURL & url, KMReaderWin * w=0 ) const; private: diff --git a/kmail/vacation.cpp b/kmail/vacation.cpp index d0cdedf8..0b9222f4 100644 --- a/kmail/vacation.cpp +++ b/kmail/vacation.cpp @@ -500,11 +500,15 @@ namespace KMail { u.setUser( a->login() ); u.setPass( a->passwd() ); u.setPort( sieve.port() ); - u.setQuery( "x-mech=" + (a->auth() == "*" ? "PLAIN" : a->auth()) ); //translate IMAP LOGIN to PLAIN + u.addQueryItem( "x-mech", a->auth() == "*" ? "PLAIN" : a->auth() ); //translate IMAP LOGIN to PLAIN + if ( !a->useSSL() && !a->useTLS() ) + u.addQueryItem( "x-allow-unencrypted", "true" ); u.setFileName( sieve.vacationFileName() ); return u; } else { KURL u = sieve.alternateURL(); + if ( u.protocol().lower() == "sieve" && !a->useSSL() && !a->useTLS() && u.queryItem("x-allow-unencrypted").isEmpty() ) + u.addQueryItem( "x-allow-unencrypted", "true" ); u.setFileName( sieve.vacationFileName() ); return u; } @@ -579,9 +583,11 @@ namespace KMail { TQStringList Vacation::defaultMailAliases() { TQStringList sl; for ( KPIM::IdentityManager::ConstIterator it = kmkernel->identityManager()->begin() ; - it != kmkernel->identityManager()->end() ; ++it ) - if ( !(*it).emailAddr().isEmpty() ) - sl.push_back( (*it).emailAddr() ); + it != kmkernel->identityManager()->end() ; ++it ) { + if ( !(*it).primaryEmailAddress().isEmpty() ) + sl.push_back( (*it).primaryEmailAddress() ); + sl += (*it).emailAliases(); + } return sl; } @@ -666,6 +672,7 @@ namespace KMail { mDialog->setMailAliases( defaultMailAliases().join(", ") ); mDialog->setSendForSpam( defaultSendForSpam() ); mDialog->setDomainName( defaultDomainName() ); + mDialog->setDomainCheck( false ); } void Vacation::slotDialogOk() { diff --git a/kmail/vacationdialog.cpp b/kmail/vacationdialog.cpp index a2dabe9b..50dea24e 100644 --- a/kmail/vacationdialog.cpp +++ b/kmail/vacationdialog.cpp @@ -71,7 +71,9 @@ namespace KMail { // "Resent only after" spinbox and label: ++row; - mIntervalSpin = new KIntSpinBox( 1, 356, 1, 7, 10, plainPage(), "mIntervalSpin" ); + int defDayInterval = 7; //default day interval + mIntervalSpin = new KIntSpinBox( 1, 356, 1, defDayInterval, 10, plainPage(), "mIntervalSpin" ); + mIntervalSpin->setSuffix( i18n(" day", " days", defDayInterval) ); connect(mIntervalSpin, TQT_SIGNAL( valueChanged( int )), TQT_SLOT( slotIntervalSpinChanged( int ) ) ); glay->addWidget( new TQLabel( mIntervalSpin, i18n("&Resend notification only after:"), plainPage() ), row, 0 ); glay->addWidget( mIntervalSpin, row, 1 ); @@ -168,28 +170,40 @@ namespace KMail { } void VacationDialog::setDomainName( const TQString & domain ) { - mDomainEdit->setText( domain ); - if ( !domain.isEmpty() ) + if ( !domain.isEmpty() ) { + mDomainEdit->setText( domain ); mDomainCheck->setChecked( true ); + } + } + + bool VacationDialog::domainCheck() const + { + return mDomainCheck->isChecked(); + } + + void VacationDialog::setDomainCheck( bool check ) + { + mDomainCheck->setChecked( check ); } - bool VacationDialog::sendForSpam() const { + bool VacationDialog::sendForSpam() const + { return !mSpamCheck->isChecked(); } - void VacationDialog::setSendForSpam( bool enable ) { + void VacationDialog::setSendForSpam( bool enable ) + { mSpamCheck->setChecked( !enable ); } - /* virtual*/ - void KMail::VacationDialog::enableDomainAndSendForSpam( bool enable ) { - mDomainCheck->setEnabled( enable ); - mDomainEdit->setEnabled( enable ); - mSpamCheck->setEnabled( enable ); + void KMail::VacationDialog::enableDomainAndSendForSpam( bool enable ) + { + mDomainCheck->setEnabled( enable ); + mDomainEdit->setEnabled( enable && mDomainCheck->isChecked() ); + mSpamCheck->setEnabled( enable ); } - } // namespace KMail #include "vacationdialog.moc" diff --git a/kmail/vacationdialog.h b/kmail/vacationdialog.h index 2ce076ff..a971b118 100644 --- a/kmail/vacationdialog.h +++ b/kmail/vacationdialog.h @@ -46,6 +46,9 @@ namespace KMail { bool activateVacation() const; virtual void setActivateVacation( bool activate ); + bool domainCheck() const; + virtual void setDomainCheck( bool check ); + TQString messageText() const; virtual void setMessageText( const TQString & text ); @@ -55,14 +58,14 @@ namespace KMail { KMime::Types::AddrSpecList mailAliases() const; virtual void setMailAliases( const KMime::Types::AddrSpecList & aliases ); virtual void setMailAliases( const TQString & aliases ); - + TQString domainName() const; virtual void setDomainName( const TQString & domain ); bool sendForSpam() const; virtual void setSendForSpam( bool enable ); - + private slots: void slotIntervalSpinChanged( int value ); diff --git a/kmail/vcardviewer.cpp b/kmail/vcardviewer.cpp index ff4a3b86..0e34f72c 100644 --- a/kmail/vcardviewer.cpp +++ b/kmail/vcardviewer.cpp @@ -37,7 +37,11 @@ using KABC::Addressee; #include -KMail::VCardViewer::VCardViewer(TQWidget *parent, const TQString& vCard, const char* name) +#if defined(KABC_VCARD_ENCODING_FIX) +KMail::VCardViewer::VCardViewer( TQWidget *parent, const TQByteArray &vCard, const char *name ) +#else +KMail::VCardViewer::VCardViewer( TQWidget *parent, const TQString &vCard, const char *name ) +#endif : KDialogBase( parent, name, false, i18n("VCard Viewer"), User1|User2|User3|Close, Close, true, i18n("&Import"), i18n("&Next Card"), i18n("&Previous Card") ) { @@ -47,7 +51,11 @@ KMail::VCardViewer::VCardViewer(TQWidget *parent, const TQString& vCard, const c setMainWidget(mAddresseeView); VCardConverter vcc; +#if defined(KABC_VCARD_ENCODING_FIX) + mAddresseeList = vcc.parseVCardsRaw( vCard.data() ); +#else mAddresseeList = vcc.parseVCards( vCard ); +#endif if ( !mAddresseeList.empty() ) { itAddresseeList = mAddresseeList.begin(); mAddresseeView->setAddressee( *itAddresseeList ); diff --git a/kmail/vcardviewer.h b/kmail/vcardviewer.h index 2fdcd090..48da60ba 100644 --- a/kmail/vcardviewer.h +++ b/kmail/vcardviewer.h @@ -22,6 +22,7 @@ #include #include +#include // for KABC_VCARD_ENCODING_FIX define #include @@ -33,24 +34,28 @@ namespace KPIM { namespace KMail { - class VCardViewer : public KDialogBase - { - Q_OBJECT - public: - VCardViewer(TQWidget *parent, const TQString& vCard, const char* name); - virtual ~VCardViewer(); - - protected: - virtual void slotUser1(); - virtual void slotUser2(); - virtual void slotUser3(); - - private: - KPIM::AddresseeView * mAddresseeView; - KABC::Addressee::List mAddresseeList; - - TQValueListIterator itAddresseeList; - }; +class VCardViewer : public KDialogBase +{ + Q_OBJECT + public: +#if defined(KABC_VCARD_ENCODING_FIX) + VCardViewer( TQWidget *parent, const TQByteArray &vCard, const char *name ); +#else + VCardViewer( TQWidget *parent, const TQString &vCard, const char *name ); +#endif + virtual ~VCardViewer(); + + protected: + virtual void slotUser1(); + virtual void slotUser2(); + virtual void slotUser3(); + + private: + KPIM::AddresseeView *mAddresseeView; + KABC::Addressee::List mAddresseeList; + + TQValueListIterator itAddresseeList; +}; } diff --git a/kmailcvt/Makefile.am b/kmailcvt/Makefile.am index 2ae9b916..b5e48e83 100644 --- a/kmailcvt/Makefile.am +++ b/kmailcvt/Makefile.am @@ -6,7 +6,7 @@ kmailcvt_SOURCES = kimportpagedlg.ui kimportpage.cpp kselfilterpagedlg.ui \ filter_mbox.cxx filter_evolution.cxx filter_mailapp.cxx \ filter_evolution_v2.cxx filter_opera.cxx filter_thunderbird.cxx \ filter_kmail_maildir.cxx filter_sylpheed.cxx filter_thebat.cxx \ - filter_lnotes.cxx + filter_lnotes.cxx filter_kmail_archive.cxx kmailcvt_LDADD = $(LIB_KFILE) @@ -25,7 +25,8 @@ EXTRA_DIST = main.cpp kmailcvt.cpp kimportpage.cpp kimportpage.h \ filter_thunderbird.hxx filter_thunderbird.cxx \ filter_kmail_maildir.hxx filter_kmail_maildir.cxx \ filter_sylpheed.hxx filter_sylpheed.cxx \ - filter_thebat.hxx filter_thebat.cxx filter_lnotes.hxx filter_lnotes.cxx + filter_thebat.hxx filter_thebat.cxx filter_lnotes.hxx filter_lnotes.cxx \ + filter_kmail_archive.cxx fitler_kmail_archive.hxx install-data-local: uninstall.desktop $(mkinstalldirs) $(DESTDIR)$(kde_appsdir)/Utilities diff --git a/kmailcvt/filter_evolution.cxx b/kmailcvt/filter_evolution.cxx index abc602a2..a07a6f08 100644 --- a/kmailcvt/filter_evolution.cxx +++ b/kmailcvt/filter_evolution.cxx @@ -39,7 +39,6 @@ FilterEvolution::FilterEvolution(void) : /** Destructor. */ FilterEvolution::~FilterEvolution(void) { - endImport(); } /** Recursive import of Evolution's mboxes. */ diff --git a/kmailcvt/filter_evolution_v2.cxx b/kmailcvt/filter_evolution_v2.cxx index 16c3093a..dd69ee7b 100644 --- a/kmailcvt/filter_evolution_v2.cxx +++ b/kmailcvt/filter_evolution_v2.cxx @@ -39,7 +39,6 @@ FilterEvolution_v2::FilterEvolution_v2(void) : /** Destructor. */ FilterEvolution_v2::~FilterEvolution_v2(void) { - endImport(); } /** Recursive import of Evolution's mboxes. */ @@ -61,6 +60,7 @@ void FilterEvolution_v2::import(FilterInfo *info) kfd->setMode(KFile::Directory | KFile::LocalOnly); kfd->exec(); mailDir = kfd->selectedFile(); + delete kfd; if (mailDir.isEmpty()) { info->alert(i18n("No directory selected.")); diff --git a/kmailcvt/filter_kmail_archive.cxx b/kmailcvt/filter_kmail_archive.cxx new file mode 100644 index 00000000..2fca99bd --- /dev/null +++ b/kmailcvt/filter_kmail_archive.cxx @@ -0,0 +1,35 @@ +/* Copyright 2009 Klarälvdalens Datakonsult AB + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#include "filter_kmail_archive.hxx" + +#include + +FilterKMailArchive::FilterKMailArchive() + : Filter( i18n( "Import KMail Archive File" ), + "Klar\xE4lvdalens Datakonsult AB", + i18n( "

    KMail Archive File Import Filter

    " + "

    This filter will import archives files previously exported by KMail.

    " + "

    Archive files contain a complete folder subtree compressed into a single file.

    " ) ) +{ +} + +void FilterKMailArchive::import( FilterInfo *info ) +{ + showKMailImportArchiveDialog( info ); +} diff --git a/kmailcvt/filter_kmail_archive.hxx b/kmailcvt/filter_kmail_archive.hxx new file mode 100644 index 00000000..755d3cc2 --- /dev/null +++ b/kmailcvt/filter_kmail_archive.hxx @@ -0,0 +1,32 @@ +/* Copyright 2009 Klarälvdalens Datakonsult AB + + 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) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + 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, see . +*/ +#ifndef FILTER_KMAIL_ARCHIVE_HXX +#define FILTER_KMAIL_ARCHIVE_HXX + +#include "filters.hxx" + +class FilterKMailArchive : public Filter +{ +public: + FilterKMailArchive(); + void import( FilterInfo *info ); + virtual bool needsSecondPage() { return false; } +}; + +#endif diff --git a/kmailcvt/filter_kmail_maildir.cxx b/kmailcvt/filter_kmail_maildir.cxx index 5ea7d4fc..2a196156 100644 --- a/kmailcvt/filter_kmail_maildir.cxx +++ b/kmailcvt/filter_kmail_maildir.cxx @@ -38,7 +38,6 @@ FilterKMail_maildir::FilterKMail_maildir( void ) : /** Destructor. */ FilterKMail_maildir::~FilterKMail_maildir( void ) { - endImport(); } /** Recursive import of KMail maildir. */ @@ -52,6 +51,7 @@ void FilterKMail_maildir::import( FilterInfo *info ) kfd->setMode( KFile::Directory | KFile::LocalOnly ); kfd->exec(); mailDir = kfd->selectedFile(); + delete kfd; if ( mailDir.isEmpty() ) { info->alert( i18n( "No directory selected." ) ); diff --git a/kmailcvt/filter_lnotes.cxx b/kmailcvt/filter_lnotes.cxx index 69a48167..4bd3c423 100644 --- a/kmailcvt/filter_lnotes.cxx +++ b/kmailcvt/filter_lnotes.cxx @@ -40,7 +40,6 @@ FilterLNotes::FilterLNotes() : /** Destructor. */ FilterLNotes::~FilterLNotes() { - endImport(); } /** diff --git a/kmailcvt/filter_mailapp.cxx b/kmailcvt/filter_mailapp.cxx index 2121badb..901fa698 100644 --- a/kmailcvt/filter_mailapp.cxx +++ b/kmailcvt/filter_mailapp.cxx @@ -37,7 +37,6 @@ FilterMailApp::FilterMailApp() : FilterMailApp::~FilterMailApp() { - endImport(); } void FilterMailApp::import(FilterInfo *info) diff --git a/kmailcvt/filter_mbox.cxx b/kmailcvt/filter_mbox.cxx index 3777cd7e..76b37668 100644 --- a/kmailcvt/filter_mbox.cxx +++ b/kmailcvt/filter_mbox.cxx @@ -37,7 +37,6 @@ FilterMBox::FilterMBox() : FilterMBox::~FilterMBox() { - endImport(); } void FilterMBox::import(FilterInfo *info) diff --git a/kmailcvt/filter_oe.cxx b/kmailcvt/filter_oe.cxx index e1465d14..bde399b6 100644 --- a/kmailcvt/filter_oe.cxx +++ b/kmailcvt/filter_oe.cxx @@ -50,7 +50,6 @@ FilterOE::FilterOE() : FilterOE::~FilterOE() { - endImport(); } void FilterOE::import(FilterInfo *info) diff --git a/kmailcvt/filter_opera.cxx b/kmailcvt/filter_opera.cxx index 3b2fff17..65e2eae4 100644 --- a/kmailcvt/filter_opera.cxx +++ b/kmailcvt/filter_opera.cxx @@ -37,7 +37,6 @@ FilterOpera::FilterOpera() : FilterOpera::~FilterOpera() { - endImport(); } void FilterOpera::import(FilterInfo *info) @@ -55,6 +54,7 @@ void FilterOpera::import(FilterInfo *info) kfd->setMode(KFile::Directory | KFile::LocalOnly); kfd->exec(); TQString operaDir = kfd->selectedFile(); + delete kfd; if (operaDir.isEmpty()) { info->alert(i18n("No directory selected.")); diff --git a/kmailcvt/filter_outlook.cxx b/kmailcvt/filter_outlook.cxx index 0dad8fde..a5a9f407 100644 --- a/kmailcvt/filter_outlook.cxx +++ b/kmailcvt/filter_outlook.cxx @@ -37,7 +37,6 @@ FilterOutlook::FilterOutlook() : FilterOutlook::~FilterOutlook() { - endImport(); } void FilterOutlook::import(FilterInfo *info) diff --git a/kmailcvt/filter_plain.cxx b/kmailcvt/filter_plain.cxx index 0246c8c6..04abea3d 100644 --- a/kmailcvt/filter_plain.cxx +++ b/kmailcvt/filter_plain.cxx @@ -34,7 +34,6 @@ FilterPlain::FilterPlain() : FilterPlain::~FilterPlain() { - endImport(); } void FilterPlain::import(FilterInfo *info) diff --git a/kmailcvt/filter_pmail.cxx b/kmailcvt/filter_pmail.cxx index 5fcc1256..23617f91 100644 --- a/kmailcvt/filter_pmail.cxx +++ b/kmailcvt/filter_pmail.cxx @@ -38,7 +38,6 @@ FilterPMail::FilterPMail() : FilterPMail::~FilterPMail() { - endImport(); } void FilterPMail::import(FilterInfo *info) @@ -51,6 +50,7 @@ void FilterPMail::import(FilterInfo *info) kfd->setMode(KFile::Directory | KFile::LocalOnly); kfd->exec(); chosenDir = kfd->selectedFile(); + delete kfd; if (chosenDir.isEmpty()) { info->alert(i18n("No directory selected.")); diff --git a/kmailcvt/filter_sylpheed.cxx b/kmailcvt/filter_sylpheed.cxx index fcf53f74..489083f0 100644 --- a/kmailcvt/filter_sylpheed.cxx +++ b/kmailcvt/filter_sylpheed.cxx @@ -37,7 +37,6 @@ FilterSylpheed::FilterSylpheed( void ) : /** Destructor. */ FilterSylpheed::~FilterSylpheed( void ) { - endImport(); } /** Recursive import of Sylpheed maildir. */ @@ -51,6 +50,7 @@ void FilterSylpheed::import( FilterInfo *info ) kfd->setMode( KFile::Directory | KFile::LocalOnly ); kfd->exec(); mailDir = kfd->selectedFile(); + delete kfd; if ( mailDir.isEmpty() ) { info->alert( i18n( "No directory selected." ) ); diff --git a/kmailcvt/filter_thebat.cxx b/kmailcvt/filter_thebat.cxx index fa00b915..de6e5c6b 100644 --- a/kmailcvt/filter_thebat.cxx +++ b/kmailcvt/filter_thebat.cxx @@ -44,7 +44,6 @@ FilterTheBat::FilterTheBat( void ) : /** Destructor. */ FilterTheBat::~FilterTheBat( void ) { - endImport(); } /** Recursive import of The Bat! maildir. */ @@ -57,6 +56,7 @@ void FilterTheBat::import( FilterInfo *info ) kfd->setMode( KFile::Directory | KFile::LocalOnly ); kfd->exec(); mailDir = kfd->selectedFile(); + delete kfd; if ( mailDir.isEmpty() ) { info->alert( i18n( "No directory selected." ) ); diff --git a/kmailcvt/filter_thunderbird.cxx b/kmailcvt/filter_thunderbird.cxx index a9e091d9..6ada6809 100644 --- a/kmailcvt/filter_thunderbird.cxx +++ b/kmailcvt/filter_thunderbird.cxx @@ -39,7 +39,6 @@ FilterThunderbird::FilterThunderbird(void) : /** Destructor. */ FilterThunderbird::~FilterThunderbird(void) { - endImport(); } /** Recursive import of Evolution's mboxes. */ @@ -60,6 +59,7 @@ void FilterThunderbird::import(FilterInfo *info) kfd->setMode(KFile::Directory | KFile::LocalOnly); kfd->exec(); mailDir = kfd->selectedFile(); + delete kfd; if (mailDir.isEmpty()) { info->alert(i18n("No directory selected.")); diff --git a/kmailcvt/filters.cxx b/kmailcvt/filters.cxx index 5e163e19..56e69d37 100644 --- a/kmailcvt/filters.cxx +++ b/kmailcvt/filters.cxx @@ -134,12 +134,12 @@ bool Filter::addMessage( FilterInfo* info, const TQString& folderName, { KURL msgURL; msgURL.setPath( msgPath ); - + if ( !kapp->dcopClient()->isApplicationRegistered( "kmail" ) ) KApplication::startServiceByDesktopName( "kmail", TQString::null ); // Will wait until kmail is started DCOPReply reply = DCOPRef( "kmail", "KMailIface" ).call( "dcopAddMessage", folderName, msgURL, msgStatusFlags ); - + if ( !reply.isValid() ) { info->alert( i18n( "Fatal: Unable to start KMail for DCOP communication. " @@ -170,7 +170,7 @@ bool Filter::addMessage_fastImport( FilterInfo* info, const TQString& folderName { KURL msgURL; msgURL.setPath( msgPath ); - + if ( !kapp->dcopClient()->isApplicationRegistered( "kmail" ) ) KApplication::startServiceByDesktopName( "kmail", TQString::null ); // Will wait until kmail is started @@ -197,17 +197,22 @@ bool Filter::addMessage_fastImport( FilterInfo* info, const TQString& folderName return true; } -bool Filter::endImport() +void Filter::showKMailImportArchiveDialog( FilterInfo* info ) { - if ( !kapp->dcopClient()->isApplicationRegistered( "kmail" ) ) + if ( !kapp->dcopClient()->isApplicationRegistered( "kmail" ) ) KApplication::startServiceByDesktopName( "kmail", TQString::null ); // Will wait until kmail is started - DCOPReply reply = DCOPRef( "kmail", "KMailIface" ).call( "dcopAddMessage", TQString::null, TQString::null); - if ( !reply.isValid() ) return false; - - reply = DCOPRef( "kmail", "KMailIface" ).call( "dcopResetAddMessage" ); - if ( !reply.isValid() ) return false; + DCOPReply reply = DCOPRef( "kmail", "KMailIface" ).call( "showImportArchiveDialog" ); + if ( !reply.isValid() ) + { + info->alert( i18n( "Fatal: Unable to start KMail for DCOP communication. " + "Make sure kmail is installed." ) ); + } +} - return true; +bool Filter::needsSecondPage() +{ + return true; } + // vim: ts=2 sw=2 et diff --git a/kmailcvt/filters.hxx b/kmailcvt/filters.hxx index 203f3d7b..dc51f8c9 100644 --- a/kmailcvt/filters.hxx +++ b/kmailcvt/filters.hxx @@ -67,10 +67,13 @@ class Filter TQString author() const { return m_author; } TQString name() const { return m_name; } TQString info() const { return m_info; } - + + virtual bool needsSecondPage(); + int count_duplicates; //to count all duplicate messages - + protected: + void showKMailImportArchiveDialog( FilterInfo* info ); bool addMessage( FilterInfo* info, const TQString& folder, const TQString& msgFile, @@ -79,7 +82,6 @@ class Filter const TQString& folder, const TQString& msgFile, const TQString& msgStatusFlags = TQString()); - bool endImport(); private: TQString m_name; TQString m_author; @@ -112,7 +114,7 @@ public: for(unsigned int i=0; i #include - +#include +#include +#include #include "filters.hxx" KMailCVT::KMailCVT(TQWidget *parent, const char *name) @@ -34,6 +36,19 @@ KMailCVT::KMailCVT(TQWidget *parent, const char *name) } KMailCVT::~KMailCVT() { + endImport(); +} + +void KMailCVT::endImport() { + if ( !kapp->dcopClient()->isApplicationRegistered( "kmail" ) ) + KApplication::startServiceByDesktopName( "kmail", TQString::null ); // Will wait until kmail is started + + DCOPReply reply = DCOPRef( "kmail", "KMailIface" ).call( "dcopAddMessage", TQString::null, TQString::null, TQString::null); + if ( !reply.isValid() ) + return; + reply = DCOPRef( "kmail", "KMailIface" ).call( "dcopResetAddMessage" ); + if ( !reply.isValid() ) + return; } void KMailCVT::next() { @@ -41,24 +56,33 @@ void KMailCVT::next() { // Save selected filter Filter *selectedFilter = selfilterpage->getSelectedFilter(); // without filter don't go next - if (!selectedFilter) + if ( !selectedFilter ) return; - // Goto next page - KWizard::next(); - // Disable back & finish - setBackEnabled( currentPage(), false ); - setFinishEnabled( currentPage(), false ); - // Start import - FilterInfo *info = new FilterInfo(importpage, this, selfilterpage->removeDupMsg_checked()); - info->setStatusMsg(i18n("Import in progress")); - info->clear(); // Clear info from last time - selectedFilter->import(info); - info->setStatusMsg(i18n("Import finished")); - // Cleanup - delete info; - // Enable finish & back buttons - setFinishEnabled( currentPage(), true ); - setBackEnabled( currentPage(), true ); + + if ( !selectedFilter->needsSecondPage() ) { + FilterInfo *info = new FilterInfo( importpage, this, selfilterpage->removeDupMsg_checked() ); + selectedFilter->import( info ); + accept(); + delete info; + } + else { + // Goto next page + KWizard::next(); + // Disable back & finish + setBackEnabled( currentPage(), false ); + setFinishEnabled( currentPage(), false ); + // Start import + FilterInfo *info = new FilterInfo(importpage, this, selfilterpage->removeDupMsg_checked()); + info->setStatusMsg(i18n("Import in progress")); + info->clear(); // Clear info from last time + selectedFilter->import(info); + info->setStatusMsg(i18n("Import finished")); + // Cleanup + delete info; + // Enable finish & back buttons + setFinishEnabled( currentPage(), true ); + setBackEnabled( currentPage(), true ); + } } else KWizard::next(); } diff --git a/kmailcvt/kmailcvt.h b/kmailcvt/kmailcvt.h index b4ff16ac..34907bcc 100644 --- a/kmailcvt/kmailcvt.h +++ b/kmailcvt/kmailcvt.h @@ -23,7 +23,7 @@ #include "kimportpage.h" #include "kselfilterpage.h" - + /** KMailCVT is the base class of the project */ class KMailCVT : public KWizard { Q_OBJECT @@ -36,6 +36,7 @@ public: public slots: void help(); private: + void endImport(); KSelFilterPage* selfilterpage; KImportPage* importpage; }; diff --git a/kmailcvt/kselfilterpage.cpp b/kmailcvt/kselfilterpage.cpp index a3f1a9b9..b8af0d0b 100644 --- a/kmailcvt/kselfilterpage.cpp +++ b/kmailcvt/kselfilterpage.cpp @@ -32,6 +32,7 @@ #include "filter_opera.hxx" #include "filter_thunderbird.hxx" #include "filter_kmail_maildir.hxx" +#include "filter_kmail_archive.hxx" #include "filter_sylpheed.hxx" #include "filter_thebat.hxx" #include "filter_lnotes.hxx" @@ -49,6 +50,8 @@ KSelFilterPage::KSelFilterPage(TQWidget *parent, const char *name ) : KSelFilter // For now, we have to live without the warm and fuzzy feeling a refactoring might give. // Patches appreciated. (danimo) + addFilter(new FilterKMailArchive); + addFilter(new FilterMBox); addFilter(new FilterEvolution); addFilter(new FilterEvolution_v2); addFilter(new FilterKMail_maildir); @@ -60,7 +63,6 @@ KSelFilterPage::KSelFilterPage(TQWidget *parent, const char *name ) : KSelFilter addFilter(new FilterOE); // addFilter(new FilterOutlook); addFilter(new FilterPMail); - addFilter(new FilterMBox); addFilter(new FilterLNotes); addFilter(new FilterPlain); } diff --git a/kmailcvt/main.cpp b/kmailcvt/main.cpp index aea1edde..87ab0c81 100644 --- a/kmailcvt/main.cpp +++ b/kmailcvt/main.cpp @@ -48,8 +48,10 @@ int main(int argc, char *argv[]) DCOPClient *client=a.dcopClient(); if (!client->attach()) { + delete kmailcvt; return 1; } - - return a.exec(); + int ret = a.exec(); + delete kmailcvt; + return ret; } diff --git a/kmobile/devices/digicam/libkmobile_digicam.desktop b/kmobile/devices/digicam/libkmobile_digicam.desktop index b00b8d70..adb3c9ab 100644 --- a/kmobile/devices/digicam/libkmobile_digicam.desktop +++ b/kmobile/devices/digicam/libkmobile_digicam.desktop @@ -27,7 +27,6 @@ Name[hu]=Fényképezőgép Name[is]=Stafræn myndavél Name[it]=Macchina fotografica digitale Name[ja]=デジタルカメラ -Name[ka]=ციფრული კამერა Name[kk]=Цифрлық камера Name[km]=ម៉ាស៊ីន​ថត​រូប​ឌីជីថល Name[lt]=Skaitmeninė kamera @@ -54,8 +53,7 @@ Name[ta]=டிஜிடல் காமரா Name[tg]=Камераи digital Name[tr]=Dijital Kamera Name[uk]=Цифровий фотоапарат -Name[uz]=Fotoaparat -Name[uz@cyrillic]=Фотоапарат +Name[uz]=Фотоапарат Name[zh_CN]=数码相机 Name[zh_TW]=數位相機 Comment=This driver supports many digital cameras @@ -82,7 +80,6 @@ Comment[hu]=Ez a meghajtó különféle digitális fényképezőgépek kezelés Comment[is]=Þessi rekill styður margar stafrænar myndavélar Comment[it]=Questo driver supporta molte fotocamere digitali Comment[ja]=このドライバは多種のデジタルカメラをサポートします -Comment[ka]=ეს დრაივერი უჭერს მხარს მრავალ ციფრულ კამერას Comment[kk]=Бұл бірспыра цифрлық камераларды қамтитын драйвер Comment[km]=កម្មវិធី​បញ្ជា​នេះ​គាំទ្រ​ម៉ាស៊ីន​ថត​រូប​ឌីជីថល​ជាច្រើន Comment[lt]=Ši tvarkyklė palaiko daugelį skaitmeninių kamerų diff --git a/kmobile/devices/gammu/libkmobile_gammu.desktop b/kmobile/devices/gammu/libkmobile_gammu.desktop index 4dd6e506..925e6fac 100644 --- a/kmobile/devices/gammu/libkmobile_gammu.desktop +++ b/kmobile/devices/gammu/libkmobile_gammu.desktop @@ -27,7 +27,6 @@ Name[hu]=Mobiltelefon/határidőnapló-kezelő (Gammu) Name[is]=Farsími eða lófatölva (gammu) Name[it]=Organizer o telefono cellulare (gammu) Name[ja]=携帯電話またはスケジュール管理 (gammu) -Name[ka]=მობილური ტელეფონი ან ორგანიზატორი(gammu) Name[kk]=Қалта телефон не Ұйымдастырғыш (gammu) Name[km]=កម្មវិធី​រៀបចំ​ទូរស័ព្ទ​ចល័ត (gammu) Name[lt]=Mobilus telefonas ar asmeninės info tvarkyklė (gammu) @@ -50,8 +49,7 @@ Name[ta]=செல்பேசி அல்லது ஒருங்கிணை Name[tg]=Телефони мобилӣ ё органайзер (gammu) Name[tr]=Cep Telefonu ya da Organizer (gammu) Name[uk]=Мобільний телефон та тижневик (gammu) -Name[uz]=Uyali telefon yoki organayzer (gammu) -Name[uz@cyrillic]=Уяли телефон ёки органайзер (gammu) +Name[uz]=Уяли телефон ёки органайзер (gammu) Name[zh_CN]=移动电话或 PDA(gammu) Name[zh_TW]=行動電話或數位助理(gammu) Comment=This driver supports many NOKIA and other mobile phones via the gammu library @@ -78,7 +76,6 @@ Comment[hu]=Ez a meghajtó mobiltelefonok (elsősorban Nokia gyártmányúak) ke Comment[is]=Þessi rekill styður marga NOKIA og aðra farsíma gegnum gammu aðgerðasafnið Comment[it]=Questo driver supporta molti NOKIA e altri telefoni cellulari tramite la libreria gammu. Comment[ja]=このドライバは gammu ライブラリを介して NOKIA および他社製の携帯電話を幅広くサポートします -Comment[ka]=ეს დრაივერი მხარს უჭერს Nokia-ს მრავალ და სხვა ტელეფონებს gammu ბიბლიოთეკის საშუალებით Comment[kk]=Бұл көп NOKIA мен басқа қалта құрылғыларды, gammu жиыны арқылы, қамтитын драйвер Comment[km]=កម្មវិធី​បញ្ជា​នេះ​គាំទ្រ​ប្រភេទ​ទូរស័ព្ទ​ណូគៀ និងទូរស័ព្ទ​ផ្សេងៗ​ទៀត​ជាច្រើន​តាម​រយៈ​បណ្ណាល័យ gammu Comment[lt]=Ši tvarkyklė palaiko daugelį NOKIA ir kitų telefonų pasinaudodama gammu biblioteka @@ -88,7 +85,7 @@ Comment[nds]=Disse Driever ünnerstütt vele Nokia- un anner Mobiltelefonen öve Comment[ne]=यो ड्राइभरले गामु लाइब्रेरीबाट धेरै नोकिया र अन्य मोबाइल फोन समर्थन गर्छ Comment[nl]=Dit stuurprogramma biedt ondersteuning voor veel Nokia- en andere mobiele telefoons via de gammu-bibliotheek Comment[nn]=Denne drivaren støttar mange NOKIA og andre mobiltelefonar via gammu-biblioteket -Comment[pl]=Ten sterownik obsługuje wiele telefonów komórkowych Nokia i innych za pomocą biblioteki gammu +Comment[pl]=Ten sterownik obsługuje wiele telefonów komórkowychNokia i innych za pomocą biblioteki gammu Comment[pt]=Este controlador suportar muitos NOKIAs, bem como outros telemóveis, com a biblioteca 'gammu' Comment[pt_BR]=Este driver suporta muitos telefones móveis, NOKIA e outros, via biblioteca gammu Comment[ru]=Этот драйвер поддерживает множество моделей мобильных телефонов Nokia и других производителей через библиотеку gammu diff --git a/kmobile/devices/gnokii/libkmobile_gnokii.desktop b/kmobile/devices/gnokii/libkmobile_gnokii.desktop index 6479e480..f6ec4b96 100644 --- a/kmobile/devices/gnokii/libkmobile_gnokii.desktop +++ b/kmobile/devices/gnokii/libkmobile_gnokii.desktop @@ -27,7 +27,6 @@ Name[hu]=Mobiltelefon/határidőnapló-kezelő (Gnokii) Name[is]=Farsími eða lófatölva (gnokii) Name[it]=Telefono cellulare o organizer (gnokii) Name[ja]=携帯電話またはスケジュール管理 (gnokii) -Name[ka]=მობილური ტელეფონი ან ორგანიზატორი(gnokii) Name[kk]=Қалта телефон не Ұйымдастырғыш (gnokii) Name[km]=កម្មវិធី​រៀបចំ​ទូរស័ព្ទ​ចល័ត (gnokii) Name[lt]=Mobilus telefonas ar asmeninės info tvarkyklė (gnokii) @@ -50,8 +49,7 @@ Name[ta]=செல்பேசி அல்லது ஒருங்கிணை Name[tg]=Телефони мобилӣ ё органайзери (gnokii) Name[tr]=Cep Telefonu ya da Organizer (gnokii) Name[uk]=Мобільний телефон та тижневик (gnokii) -Name[uz]=Uyali telefon yoki organayzer (gnokii) -Name[uz@cyrillic]=Уяли телефон ёки органайзер (gnokii) +Name[uz]=Уяли телефон ёки органайзер (gnokii) Name[zh_CN]=移动电话或 PDA(gnokii) Name[zh_TW]=行動電話或數位助理(gnokii) Comment=This driver supports many NOKIA and other mobile phones via the gnokii library @@ -78,7 +76,6 @@ Comment[hu]=Ez a meghajtó mobiltelefonok (elsősorban Nokia gyártmányúak) ke Comment[is]=Þessi rekill styður marga farsíma frá NOKIA og fleiri gegnum gnokii aðgerðasafnið Comment[it]=Questo driver supporta molti NOKIA e altri telefoni cellulari tramite la libreria gnokii Comment[ja]=このドライバは gnokii ライブラリを介して NOKIA および他社製の携帯電話を幅広くサポートします -Comment[ka]=ეს დრაივერი მხარს უჭერს Nokia-ს მრავალ და სხვა ტელეფონებს gnokii ბიბლიოთეკის საშუალებით Comment[kk]=Бұл көп NOKIA мен басқа қалта құрылғыларды, gnokii жиыны арқылы, қамтитын драйвер Comment[km]=កម្មវិធី​បញ្ជា​នេះ​គាំទ្រ​ប្រភេទ​ទូរស័ព្ទ​ណូគៀ និង​ទូរស័ព្ទ​ផ្សេងៗ​ទៀត​ជាច្រើន​តាម​រយៈ​បណ្ណាល័យ gnokii Comment[lt]=Ši tvarkyklė palaiko daugelį NOKIA ir kitų telefonų pasinaudodama gnokii biblioteka @@ -88,7 +85,7 @@ Comment[nds]=Disse Driever ünnerstütt vele Nokia- un anner Mobiltelefonen öve Comment[ne]=यो ड्राइभरले जिनोकी लाइब्रेरीबाट धेरै नोकिया र अन्य मोबाइल फोन समर्थन गर्छ Comment[nl]=Dit stuurprogramma biedt ondersteuning voor veel Nokia- en andere mobiele telefoons via de gnokii-bibliotheek Comment[nn]=Denne drivaren støttar mange NOKIA og andre mobiltelefonar via gnokii-biblioteket -Comment[pl]=Ten sterownik obsługuje wiele telefonów komórkowych Nokia i innych za pomocą biblioteki gnokii +Comment[pl]=Ten sterownik obsługuje wiele telefonów komórkowychNokia i innych za pomocą biblioteki gnokii Comment[pt]=Este controlador suportar muitos NOKIAs, bem como outros telemóveis, com a biblioteca 'gnokii' Comment[pt_BR]=Este driver suporta muitos telefones móveis, NOKIA e outros, via biblioteca gnokii Comment[ru]=Этот драйвер поддерживает множество моделей мобильных телефонов Nokia и других производителей через библиотеку gnokii diff --git a/kmobile/devices/skeleton/libkmobile_skeleton.desktop b/kmobile/devices/skeleton/libkmobile_skeleton.desktop index 06f59c10..72943d15 100644 --- a/kmobile/devices/skeleton/libkmobile_skeleton.desktop +++ b/kmobile/devices/skeleton/libkmobile_skeleton.desktop @@ -26,7 +26,6 @@ Name[hu]=Skeleton eszköz Name[is]=Grunntæki Name[it]=Dispositivo skeleton Name[ja]=スケルトンデバイス -Name[ka]=მოწყობილობის ჩონჩხი Name[kk]=Қаңқалы құрылғы Name[km]=ឧបករណ៍​គ្រោង Name[lt]=Įrenginio skeletas @@ -78,7 +77,6 @@ Comment[hu]=Hordozható skeleton eszköz Comment[is]=Grunn farsími eða lófatölva Comment[it]=Dispositivo portatile skeleton Comment[ja]=モバイルスケルトンデバイス -Comment[ka]=მობილურის ჩონჩხის მოწყობილობა Comment[kk]=Қалта қаңқалы құрылғы Comment[km]=ឧបករណ៍​គ្រោង​ចល័ត Comment[lt]=Mobilaus įrenginio skeletas diff --git a/kmobile/kioslave/mimetypes/mobile_addressbook.desktop b/kmobile/kioslave/mimetypes/mobile_addressbook.desktop index 53a041c1..44c03daf 100644 --- a/kmobile/kioslave/mimetypes/mobile_addressbook.desktop +++ b/kmobile/kioslave/mimetypes/mobile_addressbook.desktop @@ -26,7 +26,6 @@ Comment[hu]=Címbejegyzések a mobiltelefonon Comment[is]=Tengiliðir í farsíma eða lófatölvu Comment[it]=Contatti nel dispositivo portatile Comment[ja]=モバイル機器内の連絡先 -Comment[ka]=კონტაქტები მობილურ მოწყობილობაზ Comment[kk]=Қалта құрылғыдағы контакттар Comment[km]=ទំនាក់​ទំនង​ក្នុង​ឧបករណ៍​ចល័ត Comment[lt]=Kontaktai mobiliajame įrenginyje diff --git a/kmobile/kioslave/mimetypes/mobile_calendar.desktop b/kmobile/kioslave/mimetypes/mobile_calendar.desktop index 35fa7b9b..935e0d88 100644 --- a/kmobile/kioslave/mimetypes/mobile_calendar.desktop +++ b/kmobile/kioslave/mimetypes/mobile_calendar.desktop @@ -26,7 +26,6 @@ Comment[hu]=Naptár a mobiltelefonon Comment[is]=Dagatal í farsíma eða lófatölvu Comment[it]=Calendari nel dispositivo portatile Comment[ja]=モバイル機器内のカレンダー -Comment[ka]=კალენდარი მობილურ მოწყობილობაზე Comment[kk]=Қалта құрылғыдағы күнтізбе Comment[km]=ប្រតិទិន​ក្នុង​ឧបករណ៍​ចល័ត Comment[lt]=Kalendorius mobiliajame įrenginyje diff --git a/kmobile/kioslave/mimetypes/mobile_device.desktop b/kmobile/kioslave/mimetypes/mobile_device.desktop index 025d5d5d..4462b124 100644 --- a/kmobile/kioslave/mimetypes/mobile_device.desktop +++ b/kmobile/kioslave/mimetypes/mobile_device.desktop @@ -28,7 +28,6 @@ Comment[hu]=Mobil eszköz Comment[is]=Farsími eða lófatölva Comment[it]=Dispositivo portatile Comment[ja]=モバイル機器 -Comment[ka]=მობილური მოწყობილობა Comment[kk]=Қалта құрылғысы Comment[km]=ឧបករណ៍​ចល័ត Comment[lt]=Mobilusis įrenginys diff --git a/kmobile/kioslave/mimetypes/mobile_notes.desktop b/kmobile/kioslave/mimetypes/mobile_notes.desktop index 12dc25c0..fa68bad6 100644 --- a/kmobile/kioslave/mimetypes/mobile_notes.desktop +++ b/kmobile/kioslave/mimetypes/mobile_notes.desktop @@ -25,7 +25,6 @@ Comment[hu]=Feljegyzések a mobiltelefonon Comment[is]=Áminningar í farsíma eða lófatölvu Comment[it]=Note nel dispositivo portatile Comment[ja]=モバイル機器内のメモ -Comment[ka]=ჩანიშვნები მობილურ ტელეფონზე Comment[kk]=Қалта құрылғыдағы жазбалар Comment[km]=ចំណាំ​ក្នុង​ឧបករណ៍​ចល័ត Comment[lt]=Užrašai mobiliajame įrenginyje diff --git a/kmobile/kmobile.desktop b/kmobile/kmobile.desktop index f8f0d5a1..aac1904a 100644 --- a/kmobile/kmobile.desktop +++ b/kmobile/kmobile.desktop @@ -43,7 +43,6 @@ GenericName[hu]=Mobilkezelő GenericName[is]=Sýsla með lófatölvur og farsíma GenericName[it]=Gestisce dispositivi portatili GenericName[ja]=モバイル機器の管理 -GenericName[ka]=მობილური მოწყობილობების მართვა GenericName[kk]=Қалта құрылғыларды басқару GenericName[km]=គ្រប់គ្រង​ឧបករណ៍​ចល័ត GenericName[lt]=Mobiliųjų įrenginių tvarkymas @@ -99,7 +98,6 @@ Comment[hu]=KDE-alapú mobiltelefon-kezelő Comment[is]=KDE stjórnforrit fyrir lófatölvur og farsíma Comment[it]=Un gestore di dispositivi portatili di KDE Comment[ja]=KDE モバイル機器マネージャ -Comment[ka]=KDE-ს მობილურ მოწყობილობათა მმართველი Comment[kk]=KDE-нің қалта құрылғылар менеджері Comment[km]=កម្មវិធី​គ្រប់គ្រង​ឧបករណ៍​ចល័ត KDE Comment[lt]=KDE mobiliųjų įrenginių tvarkyklė diff --git a/kmobile/libkmobile.desktop b/kmobile/libkmobile.desktop index 6f195ed9..b6444d2a 100644 --- a/kmobile/libkmobile.desktop +++ b/kmobile/libkmobile.desktop @@ -24,7 +24,6 @@ Name[hu]=Alacsony szintű KDE-s eszközmeghajtó mobiltelefonok kezeléséhez Name[is]=Hrár KDE vélbúnaðarrekill fyrir farsíma og lófatölvur Name[it]=KDE driver hardware di basso livello per dispositivi portatili Name[ja]=KDE モバイル機器低レベルハードウェアドライバ -Name[ka]=KDE მობილური მოწყობილობის დაბალდონიანი ხისტი დრაივერი Name[kk]=KDE-нің қалта құрылғы жабдықтың төмен деңгейлі драйвері Name[km]=កម្មវិធី​បញ្ជា​ផ្នែករឹង​កម្រិត​ទាប​របស់​ឧបករណ៍​ចល័ត KDE Name[lt]=KDE mobiliųjų įrenginių žemo lygmens aparatinės įrangos tvarkyklė diff --git a/knode/KNode.desktop b/knode/KNode.desktop index dda0bb6b..99e5d867 100644 --- a/knode/KNode.desktop +++ b/knode/KNode.desktop @@ -44,7 +44,6 @@ GenericName[id]=Pembaca Berita GenericName[is]=Fréttaforrit GenericName[it]=Lettore newsgroup GenericName[ja]=ニュースリーダー -GenericName[ka]=სიახლეების წამკითხველი GenericName[kk]=Жаңалықтарды оқу құралы GenericName[km]=កម្មវិធី​អាន​ព័ត៌មាន GenericName[lt]=Naujienų skaityklė diff --git a/knode/articlewidget.cpp b/knode/articlewidget.cpp index 1bb7527f..357a9c00 100644 --- a/knode/articlewidget.cpp +++ b/knode/articlewidget.cpp @@ -278,10 +278,12 @@ void ArticleWidget::disableActions() void ArticleWidget::readConfig() { - mFixedFontToggle->setChecked( knGlobals.configManager()->readNewsViewer()->useFixedFont() ); - mFancyToggle->setChecked( knGlobals.configManager()->readNewsViewer()->interpretFormatTags() ); + KNConfigManager *cfgMgr = knGlobals.configManager(); - mShowHtml = knGlobals.configManager()->readNewsViewer()->alwaysShowHTML(); + mFixedFontToggle->setChecked( cfgMgr->readNewsViewer()->useFixedFont() ); + mFancyToggle->setChecked( cfgMgr->readNewsViewer()->interpretFormatTags() ); + + mShowHtml = cfgMgr->readNewsViewer()->alwaysShowHTML(); KConfig *conf = knGlobals.config(); conf->setGroup( "READNEWS" ); @@ -296,7 +298,7 @@ void ArticleWidget::readConfig() delete mCSSHelper; mCSSHelper = new CSSHelper( TQPaintDeviceMetrics( mViewer->view() ) ); - if ( !knGlobals.configManager()->readNewsGeneral()->autoMark() ) + if ( !cfgMgr->readNewsGeneral()->autoMark() ) mTimer->stop(); } @@ -312,8 +314,9 @@ void ArticleWidget::writeConfig() conf->writeEntry( "attachmentStyle", mAttachmentStyle ); conf->writeEntry( "headerStyle", mHeaderStyle ); - knGlobals.configManager()->readNewsViewer()->setUseFixedFont( mFixedFontToggle->isChecked() ); - knGlobals.configManager()->readNewsViewer()->setInterpretFormatTags( mFancyToggle->isChecked() ); + KNConfigManager *cfgMgr = knGlobals.configManager(); + cfgMgr->readNewsViewer()->setUseFixedFont( mFixedFontToggle->isChecked() ); + cfgMgr->readNewsViewer()->setInterpretFormatTags( mFancyToggle->isChecked() ); } @@ -324,7 +327,8 @@ void ArticleWidget::setArticle( KNArticle *article ) if ( mArticle && mArticle->isOrphant() ) delete mArticle; - mShowHtml = knGlobals.configManager()->readNewsViewer()->alwaysShowHTML(); + KNConfigManager *cfgMgr = knGlobals.configManager(); + mShowHtml = cfgMgr->readNewsViewer()->alwaysShowHTML(); mRot13 = false; mRot13Toggle->setChecked( false ); mTimer->stop(); @@ -380,7 +384,8 @@ void ArticleWidget::displayArticle() mArticle->setForceDefaultCS( mForceCharset ); } - KNConfig::ReadNewsViewer *rnv = knGlobals.configManager()->readNewsViewer(); + KNConfigManager *cfgMgr = knGlobals.configManager(); + KNConfig::ReadNewsViewer *rnv = cfgMgr->readNewsViewer(); removeTempFiles(); mViewer->begin(); @@ -460,12 +465,12 @@ void ArticleWidget::displayArticle() mViewer->write( i18n("
    This article has the MIME type "message/partial", which KNode cannot handle yet.
    Meanwhile you can save the article as a text file and reassemble it by hand.
    ") ); } - // display body text + // display body text if ( text && text->hasContent() && !ct->isPartial() ) { // handle HTML messages if ( text->contentType()->isHTMLText() ) { TQString htmlTxt; - text->decodedText( htmlTxt, true, knGlobals.configManager()->readNewsViewer()->removeTrailingNewlines() ); + text->decodedText( htmlTxt, true, cfgMgr->readNewsViewer()->removeTrailingNewlines() ); if ( mShowHtml ) { // strip & int i = kMin( htmlTxt.findRev( "", -1, false ), htmlTxt.findRev( "", -1, false ) ); @@ -487,7 +492,7 @@ void ArticleWidget::displayArticle() else { if ( !containsPGP ) { TQStringList lines; - text->decodedText( lines, true, knGlobals.configManager()->readNewsViewer()->removeTrailingNewlines() ); + text->decodedText( lines, true, cfgMgr->readNewsViewer()->removeTrailingNewlines() ); displayBodyBlock( lines ); } } @@ -508,8 +513,8 @@ void ArticleWidget::displayArticle() mViewer->end(); enableActions(); - if( mArticle->type() == KMime::Base::ATremote && knGlobals.configManager()->readNewsGeneral()->autoMark() ) - mTimer->start( knGlobals.configManager()->readNewsGeneral()->autoMarkSeconds() * 1000, true ); + if( mArticle->type() == KMime::Base::ATremote && cfgMgr->readNewsGeneral()->autoMark() ) + mTimer->start( cfgMgr->readNewsGeneral()->autoMarkSeconds() * 1000, true ); } @@ -527,7 +532,8 @@ void ArticleWidget::displayErrorMessage( const TQString &msg ) mViewer->end(); // mark article as read if there is a negative reply from the server - if ( knGlobals.configManager()->readNewsGeneral()->autoMark() && + KNConfigManager *cfgMgr = knGlobals.configManager(); + if ( cfgMgr->readNewsGeneral()->autoMark() && mArticle && mArticle->type() == KMime::Base::ATremote && !mArticle->isOrphant() && ( msg.find("430") != -1 || msg.find("423") != -1 ) ) { KNRemoteArticle::List l; @@ -569,7 +575,8 @@ void ArticleWidget::displayHeader() // standard & fancy header style KMime::Headers::Base *hb; - TQValueList dhs = knGlobals.configManager()->displayedHeaders()->headers(); + KNConfigManager *cfgMgr = knGlobals.configManager(); + TQValueList dhs = cfgMgr->displayedHeaders()->headers(); for ( TQValueList::Iterator it = dhs.begin(); it != dhs.end(); ++it ) { KNDisplayedHeader *dh = (*it); hb = mArticle->getHeaderByType(dh->header().latin1()); @@ -648,7 +655,7 @@ void ArticleWidget::displayHeader() // references KMime::Headers::References *refs = mArticle->references( false ); if ( mArticle->type() == KMime::Base::ATremote && refs - && knGlobals.configManager()->readNewsViewer()->showRefBar() ) { + && cfgMgr->readNewsViewer()->showRefBar() ) { html += "
    "; int refCnt = refs->count(), i = 1; TQCString id = refs->first(); @@ -674,7 +681,8 @@ void ArticleWidget::displayBodyBlock( const TQStringList &lines ) int oldLevel = -2, newLevel = -2; bool isSig = false; TQString line, html; - KNConfig::ReadNewsViewer *rnv = knGlobals.configManager()->readNewsViewer(); + KNConfigManager *cfgMgr = knGlobals.configManager(); + KNConfig::ReadNewsViewer *rnv = cfgMgr->readNewsViewer(); TQString quoteChars = rnv->quoteCharacters().simplifyWhiteSpace(); if (quoteChars.isEmpty()) quoteChars = ">"; @@ -1160,7 +1168,8 @@ void ArticleWidget::slotURLClicked( const KURL &url, bool forceOpen) if ( !c ) return; // TODO: replace with message box as done in KMail - if ( forceOpen || knGlobals.configManager()->readNewsViewer()->openAttachmentsOnClick() ) + KNConfigManager *cfgMgr = knGlobals.configManager(); + if ( forceOpen || cfgMgr->readNewsViewer()->openAttachmentsOnClick() ) knGlobals.articleManager()->openContent( c ); else knGlobals.articleManager()->saveContentToFile( c, this ); diff --git a/knode/kncomposer.cpp b/knode/kncomposer.cpp index 111bdf19..53520e29 100644 --- a/knode/kncomposer.cpp +++ b/knode/kncomposer.cpp @@ -2069,7 +2069,7 @@ bool KNComposer::Editor::eventFilter(TQObject*o, TQEvent* e) } else { - p.insertItem( TQString::fromLatin1("No Suggestions"), -2 ); + p.insertItem( i18n( "No Suggestions" ), -2 ); } //Execute the popup inline diff --git a/knode/knglobals.cpp b/knode/knglobals.cpp index be9bb148..eaf026e8 100644 --- a/knode/knglobals.cpp +++ b/knode/knglobals.cpp @@ -32,6 +32,33 @@ #include "knmainwidget.h" #include "knwidgets.h" +KNGlobals::KNGlobals() : + mNetAccess( 0 ), + mCfgManager( 0 ), + mAccManager( 0 ), + mGrpManager( 0 ), + mArtManager( 0 ), + mFilManager( 0 ), + mFolManager( 0 ), + mMemManager( 0 ) +{ +} + +KNGlobals::~KNGlobals( ) +{ +#if 0 +// hmm.. something in here is causing an 'impossible' crash. let's ignore the cleanup then. + delete mNetAccess; + delete mCfgManager; + delete mAccManager; + delete mGrpManager; + delete mArtManager; + delete mFilManager; + delete mFolManager; + delete mMemManager; +#endif +} + KConfig* KNGlobals::config() { if (!c_onfig) { diff --git a/knode/knglobals.h b/knode/knglobals.h index 3a6aa10a..605f956c 100644 --- a/knode/knglobals.h +++ b/knode/knglobals.h @@ -51,6 +51,9 @@ namespace KNode { (knode.h isn't include everywhere) */ class KDE_EXPORT KNGlobals { public: + KNGlobals(); + ~KNGlobals(); + /** topWidget == top, used for message boxes, */ TQWidget *topWidget; /** no need to include knode.h everywhere */ diff --git a/knode/knode_config_accounts.desktop b/knode/knode_config_accounts.desktop index 9d0716f6..f311b260 100644 --- a/knode/knode_config_accounts.desktop +++ b/knode/knode_config_accounts.desktop @@ -39,7 +39,6 @@ Name[hu]=Fiókok Name[is]=Tengingar Name[it]=Account Name[ja]=アカウント -Name[ka]=ანგარიშები Name[kk]=Тіркелгілері Name[km]=គណនី Name[lt]=Paskyros @@ -64,8 +63,7 @@ Name[ta]=கணக்குகள் Name[tg]=Қайдҳои баҳисобгирӣ Name[tr]=Hesaplar Name[uk]=Рахунки -Name[uz]=Hisoblar -Name[uz@cyrillic]=Ҳисоблар +Name[uz]=Ҳисоблар Name[zh_CN]=账户 Name[zh_TW]=帳號 Comment=Setup for Newsgroup and Mail Servers @@ -89,7 +87,6 @@ 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 diff --git a/knode/knode_config_appearance.desktop b/knode/knode_config_appearance.desktop index bbd2ec26..2e734de4 100644 --- a/knode/knode_config_appearance.desktop +++ b/knode/knode_config_appearance.desktop @@ -39,7 +39,6 @@ Name[hu]=Megjelenés Name[is]=Útlit Name[it]=Aspetto Name[ja]=外観 -Name[ka]=იერსახე Name[kk]=Сыртқы көрінісі Name[km]=រូបរាង Name[ko]=모양 @@ -66,8 +65,7 @@ Name[ta]=தோற்றம் Name[tg]=Намуди зоҳирӣ Name[tr]=Görünüm Name[uk]=Вигляд -Name[uz]=Koʻrinishi -Name[uz@cyrillic]=Кўриниши +Name[uz]=Кўриниши Name[zh_CN]=外观 Comment=Customize Visual Appearance Comment[af]=Pasmaak die visuele voorkoms @@ -93,7 +91,6 @@ 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ą diff --git a/knode/knode_config_cleanup.desktop b/knode/knode_config_cleanup.desktop index 5f08bbe2..e6afc8cc 100644 --- a/knode/knode_config_cleanup.desktop +++ b/knode/knode_config_cleanup.desktop @@ -35,7 +35,6 @@ Name[hu]=Tisztítás Name[is]=Hreinsun Name[it]=Pulizia Name[ja]=整理 -Name[ka]=გაწმენდა Name[kk]=Тазалау Name[km]=សម្អាត Name[lt]=Išvalymas @@ -85,7 +84,6 @@ 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 diff --git a/knode/knode_config_identity.desktop b/knode/knode_config_identity.desktop index 2fda0f69..f7c0fae9 100644 --- a/knode/knode_config_identity.desktop +++ b/knode/knode_config_identity.desktop @@ -38,7 +38,6 @@ Name[hu]=Azonosító Name[is]=Auðkenni Name[it]=Identità Name[ja]=個人情報 -Name[ka]=პროფილი Name[kk]=Іс-әлпеті Name[km]=អត្តសញ្ញាណ Name[lt]=Tapatybė @@ -63,8 +62,7 @@ Name[ta]=அடையாளம் Name[tg]=Профил Name[tr]=Kimlik Name[uk]=Профіль -Name[uz]=Shaxsiyat -Name[uz@cyrillic]=Шахсият +Name[uz]=Шахсият Name[zh_CN]=身份 Name[zh_TW]=身份 Comment=Personal Information @@ -94,7 +92,6 @@ 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 diff --git a/knode/knode_config_post_news.desktop b/knode/knode_config_post_news.desktop index 784b7c39..8d1658b1 100644 --- a/knode/knode_config_post_news.desktop +++ b/knode/knode_config_post_news.desktop @@ -33,7 +33,6 @@ 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 diff --git a/knode/knode_config_privacy.desktop b/knode/knode_config_privacy.desktop index 21bdddb6..e2ab8fcf 100644 --- a/knode/knode_config_privacy.desktop +++ b/knode/knode_config_privacy.desktop @@ -34,7 +34,6 @@ 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 @@ -81,7 +80,6 @@ Comment[hu]=Az adatok védelme az üzenetek elektronikus aláírásával, titkos 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 diff --git a/knode/knode_config_read_news.desktop b/knode/knode_config_read_news.desktop index a4ad3718..a8ba3e2d 100644 --- a/knode/knode_config_read_news.desktop +++ b/knode/knode_config_read_news.desktop @@ -35,7 +35,6 @@ 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 @@ -59,8 +58,7 @@ Name[ta]=செய்திகளைப் படித்தல் Name[tg]=Хондани ахборот Name[tr]=Haber Okuma Name[uk]=Читання новин -Name[uz]=Yangiliklarni oʻqish -Name[uz@cyrillic]=Янгиликларни ўқиш +Name[uz]=Янгиликларни ўқиш Name[zh_CN]=阅读新闻 Name[zh_TW]=閱讀新聞 diff --git a/knotes/knote.cpp b/knotes/knote.cpp index 1e55dd14..9e644d7a 100644 --- a/knotes/knote.cpp +++ b/knotes/knote.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -84,7 +85,8 @@ KNote::KNote( TQDomDocument buildDoc, Journal *j, TQWidget *parent, const char * : TQFrame( parent, name, WStyle_Customize | WStyle_NoBorder | WDestructiveClose ), m_label( 0 ), m_pushpin( 0 ), m_fold( 0 ), m_button( 0 ), m_tool( 0 ), m_editor( 0 ), m_config( 0 ), m_journal( j ), m_find( 0 ), - m_kwinConf( KSharedConfig::openConfig( "kwinrc", true ) ) + m_kwinConf( KSharedConfig::openConfig( "kwinrc", true ) ), + m_busy( 0 ), m_deleteWhenIdle( false ), m_blockEmitDataChanged( false ) { setAcceptDrops( true ); actionCollection()->setWidget( this ); @@ -105,7 +107,7 @@ KNote::KNote( TQDomDocument buildDoc, Journal *j, TQWidget *parent, const char * // create the menu items for the note - not the editor... // rename, mail, print, save as, insert date, alarm, close, delete, new note new KAction( i18n("New"), "filenew", 0, - this, TQT_SIGNAL(sigRequestNewNote()), actionCollection(), "new_note" ); + this,TQT_SLOT(slotRequestNewNote()) , actionCollection(), "new_note" ); new KAction( i18n("Rename..."), "text", 0, this, TQT_SLOT(slotRename()), actionCollection(), "rename_note" ); m_readOnly = new KToggleAction( i18n("Lock"), "lock" , 0, @@ -159,6 +161,7 @@ KNote::KNote( TQDomDocument buildDoc, Journal *j, TQWidget *parent, const char * // create the note editor m_editor = new KNoteEdit( actionCollection(), this ); + m_editor->setNote( this ); m_editor->installEventFilter( this ); // receive events (for modified) m_editor->viewport()->installEventFilter( this ); connect( m_editor, TQT_SIGNAL(contentsMoving( int, int )), this, TQT_SLOT(slotUpdateViewport( int, int ))); @@ -369,11 +372,27 @@ KNote::~KNote() delete m_config; } +void KNote::slotRequestNewNote() +{ + //Be sure to save before to request a new note + saveConfig(); + saveData(); + emit sigRequestNewNote(); +} + +void KNote::changeJournal(KCal::Journal *journal) +{ + m_journal = journal; + m_editor->setText( m_journal->description() ); + m_label->setText( m_journal->summary() ); + updateLabelAlignment(); +} // -------------------- public slots -------------------- // void KNote::slotKill( bool force ) { + m_blockEmitDataChanged = true; if ( !force && KMessageBox::warningContinueCancel( this, i18n("Do you really want to delete note %1?").arg( m_label->text() ), @@ -382,9 +401,10 @@ void KNote::slotKill( bool force ) ) != KMessageBox::Continue ) { + m_blockEmitDataChanged = false; return; } - + aboutToEnterEventLoop(); // delete the configuration first, then the corresponding file delete m_config; m_config = 0; @@ -396,21 +416,24 @@ void KNote::slotKill( bool force ) kdError(5500) << "Can't remove the note config: " << configFile << endl; emit sigKillNote( m_journal ); + eventLoopLeft(); + } // -------------------- public member functions -------------------- // -void KNote::saveData() +void KNote::saveData(bool update) { m_journal->setSummary( m_label->text() ); m_journal->setDescription( m_editor->text() ); m_journal->setCustomProperty( "KNotes", "FgColor", m_config->fgColor().name() ); m_journal->setCustomProperty( "KNotes", "BgColor", m_config->bgColor().name() ); m_journal->setCustomProperty( "KNotes", "RichText", m_config->richText() ? "true" : "false" ); - - emit sigDataChanged(); + if(update) { + emit sigDataChanged( noteId() ); m_editor->setModified( false ); + } } void KNote::saveConfig() const @@ -498,7 +521,7 @@ void KNote::setColor( const TQColor& fg, const TQColor& bg ) m_config->setBgColor( bg ); m_journal->updated(); // because setCustomProperty() doesn't call it!! - emit sigDataChanged(); + emit sigDataChanged(noteId()); m_config->writeConfig(); TQPalette newpalette = palette(); @@ -684,11 +707,16 @@ void KNote::setStyle( int style ) void KNote::slotRename() { + m_blockEmitDataChanged = true; // pop up dialog to get the new name bool ok; + aboutToEnterEventLoop(); + TQString oldName = m_label->text(); TQString newName = KInputDialog::getText( TQString::null, i18n("Please enter the new name:"), m_label->text(), &ok, this ); - if ( !ok ) // handle cancel + eventLoopLeft(); + m_blockEmitDataChanged = false; + if ( !ok || ( oldName == newName) ) // handle cancel return; setName( newName ); @@ -711,6 +739,21 @@ void KNote::slotUpdateReadOnly() actionCollection()->action( "edit_cut" )->setEnabled( !readOnly && m_editor->hasSelectedText() ); actionCollection()->action( "edit_paste" )->setEnabled( !readOnly ); actionCollection()->action( "edit_clear" )->setEnabled( !readOnly ); + actionCollection()->action( "rename_note" )->setEnabled( !readOnly ); + + actionCollection()->action( "format_bold" )->setEnabled( !readOnly ); + actionCollection()->action( "format_italic" )->setEnabled( !readOnly ); + actionCollection()->action( "format_underline" )->setEnabled( !readOnly ); + actionCollection()->action( "format_strikeout" )->setEnabled( !readOnly ); + actionCollection()->action( "format_alignleft" )->setEnabled( !readOnly ); + actionCollection()->action( "format_aligncenter" )->setEnabled( !readOnly ); + actionCollection()->action( "format_alignright" )->setEnabled( !readOnly ); + actionCollection()->action( "format_alignblock" )->setEnabled( !readOnly ); + actionCollection()->action( "format_list" )->setEnabled( !readOnly ); + actionCollection()->action( "format_super" )->setEnabled( !readOnly ); + actionCollection()->action( "format_sub" )->setEnabled( !readOnly ); + actionCollection()->action( "format_size" )->setEnabled( !readOnly ); + actionCollection()->action( "format_color" )->setEnabled( !readOnly ); updateFocus(); } @@ -724,7 +767,7 @@ void KNote::slotClose() m_editor->clearFocus(); m_config->setHideNote( true ); m_config->setPosition( pos() ); - + m_config->writeConfig(); // just hide the note so it's still available from the dock window hide(); } @@ -736,11 +779,15 @@ void KNote::slotInsDate() void KNote::slotSetAlarm() { + m_blockEmitDataChanged = true; KNoteAlarmDlg dlg( name(), this ); dlg.setIncidence( m_journal ); + aboutToEnterEventLoop(); if ( dlg.exec() == TQDialog::Accepted ) - emit sigDataChanged(); + emit sigDataChanged(noteId()); + eventLoopLeft(); + m_blockEmitDataChanged = false; } void KNote::slotPreferences() @@ -760,11 +807,12 @@ void KNote::slotSend() { // pop up dialog to get the IP KNoteHostDlg hostDlg( i18n("Send \"%1\"").arg( name() ), this ); + aboutToEnterEventLoop(); bool ok = (hostDlg.exec() == TQDialog::Accepted); - TQString host = hostDlg.host(); - + eventLoopLeft(); if ( !ok ) // handle cancel return; + TQString host = hostDlg.host(); if ( host.isEmpty() ) { @@ -782,11 +830,11 @@ void KNote::slotSend() void KNote::slotMail() { // get the mail action command - TQStringList cmd_list = TQStringList::split( TQChar(' '), KNotesGlobalConfig::mailAction() ); + const TQStringList cmd_list = TQStringList::split( TQChar(' '), KNotesGlobalConfig::mailAction() ); KProcess mail; - for ( TQStringList::Iterator it = cmd_list.begin(); - it != cmd_list.end(); ++it ) + for ( TQStringList::ConstIterator it = cmd_list.constBegin(); + it != cmd_list.constEnd(); ++it ) { if ( *it == "%f" ) mail << plainText().local8Bit(); // convert rich text to plain text @@ -802,8 +850,6 @@ void KNote::slotMail() void KNote::slotPrint() { - saveData(); - TQString content; if ( m_editor->textFormat() == PlainText ) content = TQStyleSheet::convertFromPlainText( m_editor->text() ); @@ -821,6 +867,7 @@ void KNote::slotPrint() void KNote::slotSaveAs() { + m_blockEmitDataChanged = true; TQCheckBox *convert = 0; if ( m_editor->textFormat() == RichText ) @@ -832,12 +879,16 @@ void KNote::slotSaveAs() KFileDialog dlg( TQString::null, TQString::null, this, "filedialog", true, convert ); dlg.setOperationMode( KFileDialog::Saving ); dlg.setCaption( i18n("Save As") ); + aboutToEnterEventLoop(); dlg.exec(); + eventLoopLeft(); TQString fileName = dlg.selectedFile(); if ( fileName.isEmpty() ) + { + m_blockEmitDataChanged = false; return; - + } TQFile file( fileName ); if ( file.exists() && @@ -845,6 +896,7 @@ void KNote::slotSaveAs() "Are you sure you want to overwrite it?").arg( TQFileInfo(file).fileName() ) ) != KMessageBox::Continue ) { + m_blockEmitDataChanged = false; return; } @@ -857,6 +909,7 @@ void KNote::slotSaveAs() else stream << text(); } + m_blockEmitDataChanged = false; } void KNote::slotPopupActionToDesktop( int id ) @@ -993,19 +1046,20 @@ void KNote::updateFocus() { m_label->setBackgroundColor( palette().active().shadow() ); m_button->show(); - m_editor->cornerWidget()->show(); if ( !m_editor->isReadOnly() ) { if ( m_tool && m_tool->isHidden() && m_editor->textFormat() == TQTextEdit::RichText ) { m_tool->show(); + m_editor->cornerWidget()->show(); setGeometry( x(), y(), width(), height() + m_tool->height() ); } } else if ( m_tool && !m_tool->isHidden() ) { m_tool->hide(); + m_editor->cornerWidget()->hide(); setGeometry( x(), y(), width(), height() - m_tool->height() ); updateLayout(); // to update the minimum height } @@ -1210,8 +1264,9 @@ void KNote::resizeEvent( TQResizeEvent *qre ) updateLayout(); } -void KNote::closeEvent( TQCloseEvent * ) +void KNote::closeEvent( TQCloseEvent *event ) { + event->ignore(); //We don't want to close (and delete the widget). Just hide it slotClose(); } @@ -1268,8 +1323,10 @@ bool KNote::eventFilter( TQObject *o, TQEvent *ev ) TQMouseEvent *e = (TQMouseEvent *)ev; if ( ev->type() == TQEvent::MouseButtonDblClick ) - slotRename(); - + { + if( !m_editor->isReadOnly()) + slotRename(); + } if ( ev->type() == TQEvent::MouseButtonPress && (e->button() == LeftButton || e->button() == MidButton)) { @@ -1301,21 +1358,21 @@ bool KNote::eventFilter( TQObject *o, TQEvent *ev ) return false; } - if ( o == m_editor ) - { - if ( ev->type() == TQEvent::FocusOut ) - { + if ( o == m_editor ) { + if ( ev->type() == TQEvent::FocusOut ) { TQFocusEvent *fe = static_cast(ev); if ( fe->reason() != TQFocusEvent::Popup && - fe->reason() != TQFocusEvent::Mouse ) - { + fe->reason() != TQFocusEvent::Mouse ) { updateFocus(); - if ( m_editor->isModified() ) - saveData(); + if ( isModified() ) { + saveConfig(); + if ( !m_blockEmitDataChanged ) + saveData(); + } } - } - else if ( ev->type() == TQEvent::FocusIn ) + } else if ( ev->type() == TQEvent::FocusIn ) { updateFocus(); + } return false; } @@ -1334,6 +1391,31 @@ bool KNote::eventFilter( TQObject *o, TQEvent *ev ) return false; } +void KNote::slotSaveData() +{ + saveData(); +} + +void KNote::deleteWhenIdle() +{ + if ( m_busy <= 0 ) + deleteLater(); + else + m_deleteWhenIdle = true; +} + +void KNote::aboutToEnterEventLoop() +{ + ++m_busy; +} + +void KNote::eventLoopLeft() +{ + --m_busy; + if ( m_busy <= 0 && m_deleteWhenIdle ) + deleteLater(); +} + #include "knote.moc" #include "knotebutton.moc" diff --git a/knotes/knote.h b/knotes/knote.h index a3bfd7b3..6bb2ffe8 100644 --- a/knotes/knote.h +++ b/knotes/knote.h @@ -56,7 +56,8 @@ public: const char *name = 0 ); ~KNote(); - void saveData(); + void changeJournal(KCal::Journal *); + void saveData( bool update = true); void saveConfig() const; TQString noteId() const; @@ -82,6 +83,8 @@ public: static void setStyle( int style ); + void deleteWhenIdle(); + void blockEmitDataChanged( bool _b ) { m_blockEmitDataChanged = _b;} public slots: void slotKill( bool force = false ); @@ -89,7 +92,7 @@ signals: void sigRequestNewNote(); void sigShowNextNote(); void sigNameChanged(); - void sigDataChanged(); + void sigDataChanged(const TQString &); void sigColorChanged(); void sigKillNote( KCal::Journal* ); @@ -108,6 +111,10 @@ protected: virtual bool focusNextPrevChild( bool ); + /// Protect against deletion while we are running a sub-eventloop + void aboutToEnterEventLoop(); + void eventLoopLeft(); + private slots: void slotRename(); void slotUpdateReadOnly(); @@ -133,7 +140,8 @@ private slots: void slotUpdateDesktopActions(); void slotUpdateViewport( int, int ); - + void slotRequestNewNote(); + void slotSaveData(); private: void updateFocus(); void updateMask(); @@ -170,6 +178,10 @@ private: KSharedConfig::Ptr m_kwinConf; static int s_ppOffset; + + int m_busy; + bool m_deleteWhenIdle; + bool m_blockEmitDataChanged; }; #endif diff --git a/knotes/knotealarmdlg.cpp b/knotes/knotealarmdlg.cpp index ef71c0f5..2652af63 100644 --- a/knotes/knotealarmdlg.cpp +++ b/knotes/knotealarmdlg.cpp @@ -70,7 +70,8 @@ KNoteAlarmDlg::KNoteAlarmDlg( const TQString& caption, TQWidget *parent, const c TQLabel *in_min = new TQLabel( i18n("hours/minutes"), in ); label_in->setEnabled( false ); // TODO - + in->hide(); //show it and enable it when feature will implement + connect( m_buttons, TQT_SIGNAL(clicked( int )), TQT_SLOT(slotButtonChanged( int )) ); } diff --git a/knotes/knoteedit.cpp b/knotes/knoteedit.cpp index 7df68149..7b99254b 100644 --- a/knotes/knoteedit.cpp +++ b/knotes/knoteedit.cpp @@ -27,24 +27,26 @@ #include #include #include - +#include +#include #include "knoteedit.h" +#include "knote.h" static const short SEP = 5; static const short ICON_SIZE = 10; KNoteEdit::KNoteEdit( KActionCollection *actions, TQWidget *parent, const char *name ) - : KTextEdit( parent, name ) + : KTextEdit( parent, name ), m_note( 0 ) { setAcceptDrops( true ); setWordWrap( WidgetWidth ); setWrapPolicy( AtWhiteSpace ); setLinkUnderline( true ); - + setCheckSpellingEnabled(false); // create the actions for the RMB menu - KAction* undo = KStdAction::undo( this, TQT_SLOT(undo()), actions ); - KAction* redo = KStdAction::redo( this, TQT_SLOT(redo()), actions ); + undo = KStdAction::undo( this, TQT_SLOT(undo()), actions ); + redo = KStdAction::redo( this, TQT_SLOT(redo()), actions ); undo->setEnabled( isUndoAvailable() ); redo->setEnabled( isRedoAvailable() ); @@ -56,10 +58,10 @@ KNoteEdit::KNoteEdit( KActionCollection *actions, TQWidget *parent, const char * m_copy->setEnabled( false ); m_paste->setEnabled( true ); - connect( this, TQT_SIGNAL(undoAvailable(bool)), undo, TQT_SLOT(setEnabled(bool)) ); - connect( this, TQT_SIGNAL(redoAvailable(bool)), redo, TQT_SLOT(setEnabled(bool)) ); + connect( this, TQT_SIGNAL(undoAvailable(bool)), this, TQT_SLOT(setEnabledUndo(bool)) ); + connect( this, TQT_SIGNAL(redoAvailable(bool)), this, TQT_SLOT(setEnabledRedo(bool)) ); - connect( this, TQT_SIGNAL(copyAvailable(bool)), m_cut, TQT_SLOT(setEnabled(bool)) ); + connect( this, TQT_SIGNAL(copyAvailable(bool)), this, TQT_SLOT( slotCutEnabled( bool ) ) ); connect( this, TQT_SIGNAL(copyAvailable(bool)), m_copy, TQT_SLOT(setEnabled(bool)) ); new KAction( KStdGuiItem::clear(), 0, this, TQT_SLOT(clear()), actions, "edit_clear" ); @@ -156,6 +158,21 @@ KNoteEdit::~KNoteEdit() { } +void KNoteEdit::setEnabledRedo( bool b ) +{ + redo->setEnabled( b && !isReadOnly() ); +} + +void KNoteEdit::setEnabledUndo( bool b ) +{ + undo->setEnabled( b && !isReadOnly() ); +} + +void KNoteEdit::slotCutEnabled( bool b ) +{ + m_cut->setEnabled( b && !isReadOnly() ); +} + void KNoteEdit::setText( const TQString& text ) { // to update the font and font size combo box - TQTextEdit stopped @@ -269,10 +286,14 @@ void KNoteEdit::textStrikeOut( bool s ) void KNoteEdit::textColor() { + if ( m_note ) + m_note->blockEmitDataChanged( true ); TQColor c = color(); int ret = KColorDialog::getColor( c, this ); if ( ret == TQDialog::Accepted ) setTextColor( c ); + if ( m_note ) + m_note->blockEmitDataChanged( false ); } void KNoteEdit::textAlignLeft() @@ -347,13 +368,17 @@ void KNoteEdit::contentsDropEvent( TQDropEvent *e ) KURL::List list; if ( KURLDrag::decode( e, list ) ) - for ( KURL::List::Iterator it = list.begin(); it != list.end(); ++it ) + { + KURL::List::ConstIterator begin = list.constBegin(); + KURL::List::ConstIterator end = list.constEnd(); + for ( KURL::List::ConstIterator it = begin; it != end; ++it ) { - if ( it != list.begin() ) + if ( it != begin ) insert( ", " ); insert( (*it).prettyURL() ); } + } else KTextEdit::contentsDropEvent( e ); } @@ -492,4 +517,41 @@ void KNoteEdit::disableRichTextActions() // m_textDecreaseIndent->setEnabled( false ); } +void KNoteEdit::slotAllowTab() +{ + setTabChangesFocus(!tabChangesFocus()); +} + +TQPopupMenu *KNoteEdit::createPopupMenu( const TQPoint &pos ) +{ + enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll }; + + TQPopupMenu *menu = TQTextEdit::createPopupMenu( pos ); + + if ( isReadOnly() ) + menu->changeItem( menu->idAt(0), SmallIconSet("editcopy"), menu->text( menu->idAt(0) ) ); + else { + int id = menu->idAt(0); + menu->changeItem( id - IdUndo, SmallIconSet("undo"), menu->text( id - IdUndo) ); + menu->changeItem( id - IdRedo, SmallIconSet("redo"), menu->text( id - IdRedo) ); + menu->changeItem( id - IdCut, SmallIconSet("editcut"), menu->text( id - IdCut) ); + menu->changeItem( id - IdCopy, SmallIconSet("editcopy"), menu->text( id - IdCopy) ); + menu->changeItem( id - IdPaste, SmallIconSet("editpaste"), menu->text( id - IdPaste) ); + menu->changeItem( id - IdClear, SmallIconSet("editclear"), menu->text( id - IdClear) ); + + menu->insertSeparator(); + id = menu->insertItem( SmallIconSet( "spellcheck" ), i18n( "Check Spelling..." ), + this, TQT_SLOT( checkSpelling() ) ); + + if( text().isEmpty() ) + menu->setItemEnabled( id, false ); + + menu->insertSeparator(); + id=menu->insertItem(i18n("Allow Tabulations"),this,TQT_SLOT(slotAllowTab())); + menu->setItemChecked(id, !tabChangesFocus()); + } + + return menu; +} + #include "knoteedit.moc" diff --git a/knotes/knoteedit.h b/knotes/knoteedit.h index 862aa38b..0e6a6e4b 100644 --- a/knotes/knoteedit.h +++ b/knotes/knoteedit.h @@ -24,7 +24,7 @@ #include #include - +class KNote; class TQFont; class TQColor; class TQPushButton; @@ -41,7 +41,9 @@ class KNoteEdit : public KTextEdit public: KNoteEdit( KActionCollection *actions, TQWidget *parent=0, const char *name=0 ); ~KNoteEdit(); - + void setNote( KNote *_note ) { + m_note = _note; + } void setText( const TQString& text ); void setTextFont( const TQFont& font ); void setTextColor( const TQColor& color ); @@ -67,10 +69,12 @@ public slots: //void textIncreaseIndent(); //void textDecreaseIndent(); + void slotCutEnabled( bool ); protected: virtual void contentsDragEnterEvent( TQDragEnterEvent *e ); virtual void contentsDropEvent( TQDropEvent *e ); + virtual TQPopupMenu *createPopupMenu( const TQPoint &pos ); private slots: void slotReturnPressed(); @@ -79,6 +83,10 @@ private slots: void colorChanged( const TQColor &c ); void alignmentChanged( int a ); void verticalAlignmentChanged( VerticalAlignment a ); + void slotAllowTab(); + + void setEnabledRedo( bool b ); + void setEnabledUndo( bool b ); private: void autoIndent(); @@ -114,8 +122,10 @@ private: KAction *m_textColor; KFontAction *m_textFont; KFontSizeAction *m_textSize; - + KAction* undo; + KAction* redo; bool m_autoIndentMode; + KNote *m_note; }; #endif diff --git a/knotes/knoteprinter.cpp b/knotes/knoteprinter.cpp index 0bd6ef8a..c5803167 100644 --- a/knotes/knoteprinter.cpp +++ b/knotes/knoteprinter.cpp @@ -4,7 +4,7 @@ #include #include - +#include #include #include #include @@ -79,6 +79,9 @@ void KNotePrinter::doPrint( KPrinter& printer, TQPainter& painter, metrics.width() - marginX * 2, metrics.height() - marginY * 2 ); + kdDebug()<<" content :"<& journals ) con TQPainter painter; painter.begin( &printer ); TQString content; - TQValueListConstIterator it( journals.begin() ); - TQValueListConstIterator end( journals.end() ); + TQValueListConstIterator it( journals.constBegin() ); + TQValueListConstIterator end( journals.constEnd() ); while ( it != end ) { KCal::Journal *j = *it; it++; diff --git a/knotes/knotes.desktop b/knotes/knotes.desktop index a455e07d..6a099e58 100644 --- a/knotes/knotes.desktop +++ b/knotes/knotes.desktop @@ -32,7 +32,6 @@ GenericName[hu]=Jegyzettömb GenericName[is]=Litlir gulir miðar GenericName[it]=Note a comparsa GenericName[ja]=ポップアップメモ -GenericName[ka]=მოტივტივე შენიშვნები GenericName[kk]=Қалқымалы жазбалар GenericName[km]=ចំណាំ​លេចឡើង GenericName[lt]=Pastabos lapeliuose diff --git a/knotes/knotes_manager.desktop b/knotes/knotes_manager.desktop index adce2afb..d193bcbb 100644 --- a/knotes/knotes_manager.desktop +++ b/knotes/knotes_manager.desktop @@ -27,7 +27,6 @@ Name[hu]=Feljegyzések Name[is]=Minnismiðar Name[it]=Note Name[ja]=メモ -Name[ka]=ჩანიშვნები Name[kk]=Жазбалар Name[km]=ចំណាំ Name[lt]=Užrašai @@ -53,8 +52,7 @@ Name[tg]=Ахборот Name[th]=บันทึกช่วยจำ Name[tr]=Notlar Name[uk]=Примітки -Name[uz]=Yozma xotira -Name[uz@cyrillic]=Ёзма хотира +Name[uz]=Ёзма хотира Name[zh_CN]=便笺 Name[zh_TW]=備忘錄 Type=Service diff --git a/knotes/knotesalarm.cpp b/knotes/knotesalarm.cpp index c653d80c..616c83b7 100644 --- a/knotes/knotesalarm.cpp +++ b/knotes/knotesalarm.cpp @@ -61,10 +61,11 @@ void KNotesAlarm::checkAlarms() KNotesGlobalConfig::self()->setAlarmsLastChecked( TQDateTime::currentDateTime() ); TQValueList alarms = m_manager->alarms( from, KNotesGlobalConfig::self()->alarmsLastChecked() ); - + if( alarms.isEmpty()) + return; TQStringList notes; TQValueList::ConstIterator it; - for ( it = alarms.begin(); it != alarms.end(); ++it ) + for ( it = alarms.constBegin(); it != alarms.constEnd(); ++it ) { KCal::Incidence *incidence = (*it)->parent(); notes += incidence->summary(); diff --git a/knotes/knotesapp.cpp b/knotes/knotesapp.cpp index 858cbb79..72b1ecce 100644 --- a/knotes/knotesapp.cpp +++ b/knotes/knotesapp.cpp @@ -121,7 +121,7 @@ KNotesApp::KNotesApp() this, TQT_SLOT(hideAllNotes()), actionCollection(), "hide_all_notes" ); new KHelpMenu( this, kapp->aboutData(), false, actionCollection() ); - KStdAction::find( this, TQT_SLOT(slotOpenFindDialog()), actionCollection() ); + m_findAction = KStdAction::find( this, TQT_SLOT(slotOpenFindDialog()), actionCollection() ); KStdAction::preferences( this, TQT_SLOT(slotPreferences()), actionCollection() ); KStdAction::keyBindings( this, TQT_SLOT(slotConfigureAccels()), actionCollection() ); //FIXME: no shortcut removing!? @@ -188,7 +188,7 @@ KNotesApp::KNotesApp() { KCal::Journal::List notes = calendar.journals(); KCal::Journal::List::ConstIterator it; - for ( it = notes.begin(); it != notes.end(); ++it ) + for ( it = notes.constBegin(); it != notes.constEnd(); ++it ) m_manager->addNewNote( *it ); m_manager->save(); @@ -258,10 +258,9 @@ TQString KNotesApp::newNote( const TQString& name, const TQString& text ) // the body of the note journal->setDescription( text ); - m_manager->addNewNote( journal ); - - showNote( journal->uid() ); - + if ( m_manager->addNewNote( journal ) ) { + showNote( journal->uid() ); + } return journal->uid(); } @@ -284,7 +283,6 @@ void KNotesApp::showAllNotes() const for ( ; *it; ++it ) { (*it)->show(); - (*it)->setFocus(); } } @@ -516,7 +514,7 @@ void KNotesApp::slotOpenFindDialog() findDia.setHasCursor( false ); findDia.setSupportsBackwardsFind( false ); - if ( findDia.exec() != TQDialog::Accepted ) + if ( (findDia.exec() != TQDialog::Accepted) || findDia.pattern().isEmpty() ) return; delete m_findPos; @@ -596,6 +594,7 @@ void KNotesApp::slotConfigureAccels() void KNotesApp::slotNoteKilled( KCal::Journal *journal ) { + m_noteUidModify=""; m_manager->deleteNote( journal ); saveNotes(); } @@ -606,7 +605,7 @@ void KNotesApp::slotQuit() for ( ; *it; ++it ) if ( (*it)->isModified() ) - (*it)->saveData(); + (*it)->saveData(false); saveConfigs(); kapp->quit(); @@ -625,6 +624,15 @@ void KNotesApp::showNote( KNote* note ) const void KNotesApp::createNote( KCal::Journal *journal ) { + if( journal->uid() == m_noteUidModify) + { + KNote *note = m_noteList[m_noteUidModify]; + if ( note ) + note->changeJournal(journal); + + return; + } + m_noteUidModify = journal->uid(); KNote *newNote = new KNote( m_noteGUI, journal, 0, journal->uid().utf8() ); m_noteList.insert( newNote->noteId(), newNote ); @@ -633,7 +641,7 @@ void KNotesApp::createNote( KCal::Journal *journal ) connect( newNote, TQT_SIGNAL(sigKillNote( KCal::Journal* )), TQT_SLOT(slotNoteKilled( KCal::Journal* )) ); connect( newNote, TQT_SIGNAL(sigNameChanged()), TQT_SLOT(updateNoteActions()) ); - connect( newNote, TQT_SIGNAL(sigDataChanged()), TQT_SLOT(saveNotes()) ); + connect( newNote, TQT_SIGNAL(sigDataChanged(const TQString &)), TQT_SLOT(saveNotes(const TQString &)) ); connect( newNote, TQT_SIGNAL(sigColorChanged()), TQT_SLOT(updateNoteActions()) ); connect( newNote, TQT_SIGNAL(sigFindFinished()), TQT_SLOT(slotFindNext()) ); @@ -644,9 +652,17 @@ void KNotesApp::createNote( KCal::Journal *journal ) void KNotesApp::killNote( KCal::Journal *journal ) { + if(m_noteUidModify == journal->uid()) + { + return; + } // this kills the KNote object - m_noteList.remove( journal->uid() ); - updateNoteActions(); + KNote *note = m_noteList.take( journal->uid() ); + if ( note ) + { + note->deleteWhenIdle(); + updateNoteActions(); + } } void KNotesApp::acceptConnection() @@ -661,6 +677,12 @@ void KNotesApp::acceptConnection() } } +void KNotesApp::saveNotes( const TQString & uid ) +{ + m_noteUidModify = uid; + saveNotes(); +} + void KNotesApp::saveNotes() { KNotesGlobalConfig::writeConfig(); @@ -692,14 +714,21 @@ void KNotesApp::updateNoteActions() m_noteActions.append( action ); } - m_noteActions.sort(); - if ( m_noteActions.isEmpty() ) { + actionCollection()->action( "hide_all_notes" )->setEnabled( false ); + actionCollection()->action( "show_all_notes" )->setEnabled( false ); + m_findAction->setEnabled( false ); KAction *action = new KAction( i18n("No Notes") ); m_noteActions.append( action ); } - + else + { + actionCollection()->action( "hide_all_notes" )->setEnabled( true ); + actionCollection()->action( "show_all_notes" )->setEnabled( true ); + m_findAction->setEnabled( true ); + m_noteActions.sort(); + } plugActionList( "notes", m_noteActions ); } diff --git a/knotes/knotesapp.h b/knotes/knotesapp.h index ba156cca..3048958d 100644 --- a/knotes/knotesapp.h +++ b/knotes/knotesapp.h @@ -125,6 +125,7 @@ private: private slots: void acceptConnection(); void saveNotes(); + void saveNotes( const TQString & uid ); void updateNoteActions(); void updateGlobalAccels(); void updateNetworkListener(); @@ -159,6 +160,8 @@ private: KXMLGUIBuilder *m_guiBuilder; TQDomDocument m_noteGUI; + KAction *m_findAction; + TQString m_noteUidModify; }; #endif diff --git a/knotes/knoteslegacy.cpp b/knotes/knoteslegacy.cpp index 8cb57d4c..6dd80368 100644 --- a/knotes/knoteslegacy.cpp +++ b/knotes/knoteslegacy.cpp @@ -72,8 +72,8 @@ bool KNotesLegacy::convert( CalendarLocal *calendar ) bool converted = false; TQDir noteDir( KGlobal::dirs()->saveLocation( "appdata", "notes/" ) ); - TQStringList notes = noteDir.entryList( TQDir::Files, TQDir::Name ); - for ( TQStringList::Iterator note = notes.begin(); note != notes.end(); note++ ) + const TQStringList notes = noteDir.entryList( TQDir::Files, TQDir::Name ); + for ( TQStringList::ConstIterator note = notes.constBegin(); note != notes.constEnd(); ++note ) { TQString file = noteDir.absFilePath( *note ); KSimpleConfig* test = new KSimpleConfig( file ); @@ -82,7 +82,6 @@ bool KNotesLegacy::convert( CalendarLocal *calendar ) if ( version < 3.0 ) { - delete test; // create the new note Journal *journal = new Journal(); @@ -109,8 +108,8 @@ bool KNotesLegacy::convert( CalendarLocal *calendar ) test->writeEntry( "ShowInTaskbar", (state & NET::SkipTaskbar) ? false : true ); test->writeEntry( "KeepAbove", (state & NET::KeepAbove) ? true : false ); test->deleteEntry( "state" ); - delete test; } + delete test; } return converted; diff --git a/knotes/local.desktop b/knotes/local.desktop index 514145a8..c0748995 100644 --- a/knotes/local.desktop +++ b/knotes/local.desktop @@ -26,7 +26,6 @@ Name[hu]=Helyi fájlban tárolt feljegyzések Name[is]=Minnismiðar í staðbundinni skrá Name[it]=Note in file locale Name[ja]=ローカルファイルのメモ -Name[ka]=ჩანიშვნები ლოკალურ ფაილში Name[kk]=Жергілікті файлдағы жазбалар Name[km]=ចំណាំ​ក្នុង​ឯកសារ​មូលដ្ឋាន Name[lt]=Užrašai vietinėje byloje diff --git a/knotes/resourcelocal.cpp b/knotes/resourcelocal.cpp index a6e4b9a3..91e6d2d9 100644 --- a/knotes/resourcelocal.cpp +++ b/knotes/resourcelocal.cpp @@ -75,7 +75,7 @@ bool ResourceLocal::load() KCal::Journal::List notes = mCalendar.journals(); KCal::Journal::List::ConstIterator it; - for ( it = notes.begin(); it != notes.end(); ++it ) + for ( it = notes.constBegin(); it != notes.constEnd(); ++it ) manager()->registerNote( this, *it ); return true; @@ -98,14 +98,12 @@ bool ResourceLocal::save() bool ResourceLocal::addNote( KCal::Journal *journal ) { - mCalendar.addJournal( journal ); - return true; + return mCalendar.addJournal( journal ); } bool ResourceLocal::deleteNote( KCal::Journal *journal ) { - mCalendar.deleteJournal( journal ); - return true; + return mCalendar.deleteJournal( journal ); } KCal::Alarm::List ResourceLocal::alarms( const TQDateTime& from, const TQDateTime& to ) @@ -113,11 +111,11 @@ KCal::Alarm::List ResourceLocal::alarms( const TQDateTime& from, const TQDateTim KCal::Alarm::List alarms; KCal::Journal::List notes = mCalendar.journals(); KCal::Journal::List::ConstIterator note; - for ( note = notes.begin(); note != notes.end(); ++note ) + for ( note = notes.constBegin(); note != notes.constEnd(); ++note ) { TQDateTime preTime = from.addSecs( -1 ); KCal::Alarm::List::ConstIterator it; - for( it = (*note)->alarms().begin(); it != (*note)->alarms().end(); ++it ) + for( it = (*note)->alarms().constBegin(); it != (*note)->alarms().constEnd(); ++it ) { if ( (*it)->enabled() ) { diff --git a/knotes/resourcemanager.cpp b/knotes/resourcemanager.cpp index 112ec03f..226d9000 100644 --- a/knotes/resourcemanager.cpp +++ b/knotes/resourcemanager.cpp @@ -64,6 +64,11 @@ void KNotesResourceManager::load() KRES::Manager::ActiveIterator it; for ( it = m_manager->activeBegin(); it != m_manager->activeEnd(); ++it ) { + if ( (*it)->isOpen() ) { + kdDebug(5500) << (*it)->resourceName() << " is already open" << endl; + continue; + } + kdDebug(5500) << "Opening resource " + (*it)->resourceName() << endl; (*it)->setManager( this ); if ( (*it)->open() ) @@ -80,17 +85,19 @@ void KNotesResourceManager::save() // when adding a new note, make sure a config file exists!! -void KNotesResourceManager::addNewNote( KCal::Journal *journal ) +bool KNotesResourceManager::addNewNote( KCal::Journal *journal ) { // TODO: Make this configurable ResourceNotes *resource = m_manager->standardResource(); - if ( resource ) - { - resource->addNote( journal ); - registerNote( resource, journal ); - } - else + if ( resource ) { + if ( resource->addNote( journal ) ) { + registerNote( resource, journal ); + return true; + } + } else { kdWarning(5500) << k_funcinfo << "no resource!" << endl; + } + return false; } void KNotesResourceManager::registerNote( ResourceNotes *resource, @@ -103,15 +110,21 @@ void KNotesResourceManager::registerNote( ResourceNotes *resource, void KNotesResourceManager::deleteNote( KCal::Journal *journal ) { + if ( !journal ) + return; + TQString uid = journal->uid(); // Remove the journal from the resource it came from - m_resourceMap[ uid ]->deleteNote( journal ); - m_resourceMap.remove( uid ); - - // libkcal does not delete the journal immediately, therefore it is ok to - // emit the journal here - emit sigDeregisteredNote( journal ); + ResourceNotes *res = m_resourceMap[ uid ]; + if ( res ) { + res->deleteNote( journal ); + m_resourceMap.remove( uid ); + + // libkcal does not delete the journal immediately, therefore it is ok to + // emit the journal here + emit sigDeregisteredNote( journal ); + } } KCal::Alarm::List KNotesResourceManager::alarms( const TQDateTime& from, const TQDateTime& to ) @@ -122,8 +135,8 @@ KCal::Alarm::List KNotesResourceManager::alarms( const TQDateTime& from, const T for ( it = m_manager->activeBegin(); it != m_manager->activeEnd(); ++it ) { KCal::Alarm::List list = (*it)->alarms( from, to ); - KCal::Alarm::List::Iterator it; - for ( it = list.begin(); it != list.end(); ++it ) + KCal::Alarm::List::ConstIterator it; + for ( it = list.constBegin(); it != list.constEnd(); ++it ) result.append( *it ); } @@ -137,6 +150,11 @@ void KNotesResourceManager::resourceAdded( ResourceNotes *resource ) if ( !resource->isActive() ) return; + if ( resource->isOpen() ) { + kdDebug(5500) << resource->resourceName() << " is already open" << endl; + return; + } + resource->setManager( this ); if ( resource->open() ) resource->load(); diff --git a/knotes/resourcemanager.h b/knotes/resourcemanager.h index 0ba70b4b..a864fc38 100644 --- a/knotes/resourcemanager.h +++ b/knotes/resourcemanager.h @@ -57,7 +57,7 @@ public: void load(); void save(); - void addNewNote( KCal::Journal *journal ); + bool addNewNote( KCal::Journal *journal ); void registerNote( ResourceNotes *resource, KCal::Journal *journal ); void deleteNote( KCal::Journal *journal ); diff --git a/konsolekalendar/main.cpp b/konsolekalendar/main.cpp index 6343144e..59b9a2f8 100644 --- a/konsolekalendar/main.cpp +++ b/konsolekalendar/main.cpp @@ -799,13 +799,13 @@ int main( int argc, char *argv[] ) if ( !args->isSet( "time" ) && !args->isSet( "epoch-start" ) && !args->isSet( "end-time" ) && !args->isSet( "epoch-end" ) ) { // set default start date/time - startdatetime = TQDateTime::TQDateTime( startdate, starttime ); + startdatetime = TQDateTime( startdate, starttime ); kdDebug() << "main | datetimestamp | " << "setting startdatetime from " << "default startdate (today) and starttime" << endl; // set default end date/time - enddatetime = TQDateTime::TQDateTime( enddate, endtime ); + enddatetime = TQDateTime( enddate, endtime ); kdDebug() << "main | datetimestamp | " << "setting enddatetime from " << "default enddate (today) and endtime" @@ -814,13 +814,13 @@ int main( int argc, char *argv[] ) // Set startdatetime, enddatetime if still necessary if ( startdatetime.isNull() ) { - startdatetime = TQDateTime::TQDateTime( startdate, starttime ); + startdatetime = TQDateTime( startdate, starttime ); kdDebug() << "main | datetimestamp | " << "setting startdatetime from startdate and starttime" << endl; } if ( enddatetime.isNull() ) { - enddatetime = TQDateTime::TQDateTime( enddate, endtime ); + enddatetime = TQDateTime( enddate, endtime ); kdDebug() << "main | datetimestamp | " << "setting enddatetime from enddate and endtime" << endl; diff --git a/kontact/interfaces/kontactplugin.desktop b/kontact/interfaces/kontactplugin.desktop index 28803bc6..11787765 100644 --- a/kontact/interfaces/kontactplugin.desktop +++ b/kontact/interfaces/kontactplugin.desktop @@ -28,7 +28,6 @@ Name[hu]=Kontact-bővítőmodul Name[is]=Kontact íforrit Name[it]=Plugin Kontact Name[ja]=Kontact プラグイン -Name[ka]=Kontact მოდული Name[kk]=Kontact модулі Name[km]=កម្មវិធី​ជំនួយ Kontact Name[lt]=Kontact priedas @@ -52,8 +51,7 @@ Name[ta]=சொருகுப்பொருளை தொடர்புக் Name[tg]=Модули Kontact Name[tr]=Kontact Eklentisi Name[uk]=Втулок Kontact -Name[uz]=Kontact uchun plagin -Name[uz@cyrillic]=Kontact учун плагин +Name[uz]=Kontact учун плагин Name[zh_CN]=Kontact 插件 Name[zh_TW]=Kontack 外掛程式 diff --git a/kontact/interfaces/plugin.h b/kontact/interfaces/plugin.h index e94c0315..13f14c58 100644 --- a/kontact/interfaces/plugin.h +++ b/kontact/interfaces/plugin.h @@ -125,7 +125,7 @@ class KDE_EXPORT Plugin : public TQObject, virtual public KXMLGUIClient virtual bool createDCOPInterface( const TQString& /*serviceType*/ ) { return false; } /** - Reimplement this method and return wether a standalone application is still running + Reimplement this method and return whether a standalone application is still running This is only required if your part is also available as standalone application. */ virtual bool isRunningStandalone() { return false; } @@ -174,7 +174,7 @@ class KDE_EXPORT Plugin : public TQObject, virtual public KXMLGUIClient virtual Summary *createSummaryWidget( TQWidget * /*parent*/ ) { return 0; } /** - Returns wether the plugin provides a part that should be shown in the sidebar. + Returns whether the plugin provides a part that should be shown in the sidebar. */ virtual bool showInSideBar() const; diff --git a/kontact/interfaces/uniqueapphandler.cpp b/kontact/interfaces/uniqueapphandler.cpp index 34de0180..5dd9e72d 100644 --- a/kontact/interfaces/uniqueapphandler.cpp +++ b/kontact/interfaces/uniqueapphandler.cpp @@ -188,6 +188,8 @@ static KCmdLineOptions options[] = { "module ", I18N_NOOP( "Start with a specific Kontact module" ), 0 }, { "iconify", I18N_NOOP( "Start in iconified (minimized) mode" ), 0 }, { "list", I18N_NOOP( "List all possible modules and exit" ), 0 }, + { "listprofiles", I18N_NOOP( "List all possible profiles and exit" ), 0 }, + { "profile ", I18N_NOOP( "Start with a specific Kontact profile" ), 0 }, KCmdLineLastOption }; diff --git a/kontact/plugins/akregator/akregator_plugin.h b/kontact/plugins/akregator/akregator_plugin.h index 42808464..6689a5a3 100644 --- a/kontact/plugins/akregator/akregator_plugin.h +++ b/kontact/plugins/akregator/akregator_plugin.h @@ -57,7 +57,7 @@ class Plugin : public Kontact::Plugin const TQStringList & ); ~Plugin(); - int weight() const { return 700; } + int weight() const { return 475; } AkregatorPartIface_stub *interface(); diff --git a/kontact/plugins/akregator/akregatorplugin.desktop b/kontact/plugins/akregator/akregatorplugin.desktop index 015ae80a..6b44fbc4 100644 --- a/kontact/plugins/akregator/akregatorplugin.desktop +++ b/kontact/plugins/akregator/akregatorplugin.desktop @@ -17,24 +17,17 @@ Comment=Feed Reader Component (Akregator Plugin) Comment[bg]=Приставка за Akregator Comment[ca]=Component lector d'enllaços (endollable de l'Akregator) Comment[da]=Feed-læserkomponent (Akregator-plugin) -Comment[de]=News-Leser (Akregator-Modul) +Comment[de]=Feedreader Komponente Comment[el]=Συστατικό ανάγνωσης ροών (Πρόσθετο του Akregator) -Comment[es]=Componente de lectura de fuentes (complemento de Akregator) Comment[et]=Uudistevoogude plugin (Akregator) -Comment[fr]=Composant du lecteur de flux (Module pour Akregator) -Comment[is]=Fréttastraumalestur (Akregator íforrit) Comment[it]=Componente lettore fonti (plugin Akregator) Comment[ja]=フィードリーダーコンポーネント (Akregator プラグイン) -Comment[km]=មមាសភាគ​កម្មវិធី​អាន​មតិព័ត៌មាន (កម្មវិធី​ជំនួយ Akregator​) Comment[nds]=Stroomleser-Komponent (Akregator-Moduul) Comment[nl]=Component om feeds te lezen (Akregator-plugin) -Comment[pl]=Składnik do czytania kanałów RSS (wtyczka Akregator) -Comment[ru]=Просмотр лент новостей (модуль Akregator) -Comment[sk]=Komponent na čítanie kanálov (Modul pre Akregator) +Comment[pl]=Składnik do czytania niusów (wtyczka Akregator) Comment[sr]=Компонента читања довода (прикључак Akregator-а) Comment[sr@Latn]=Komponenta čitanja dovoda (priključak Akregator-a) Comment[sv]=Komponent för läsning av kanaler (Akregator-insticksprogram) -Comment[tr]=Kaynak Okuyucu Bileşeni (Akregator Eklentisi) Comment[zh_CN]=新闻源阅读器组件(Akregator 插件) Comment[zh_TW]=Feed 閱讀器組件(Akregator 外掛程式) Name=Feeds @@ -43,7 +36,7 @@ Name[bg]=Новини Name[ca]=Enllaços Name[cs]=Kanály Name[da]=Kilder -Name[de]=Nachrichten +Name[de]=Feeds Name[el]=Ροές Name[eo]=Fluoj Name[es]=Orígenes @@ -60,7 +53,6 @@ Name[hu]=Hírforrások Name[is]=Fréttastraumar Name[it]=Fonti Name[ja]=フィード -Name[ka]=კვება Name[kk]=Ақпарлар Name[km]=មតិព័ត៌មាន Name[lt]=Kanalai @@ -73,7 +65,7 @@ Name[pl]=Kanały Name[pt]=Fontes Name[pt_BR]=Fontes de Notícias Name[ru]=Ленты новостей -Name[sk]=Kanály +Name[sk]=Kŕmitka Name[sl]=Viri Name[sr]=Доводи Name[sr@Latn]=Dovodi @@ -81,7 +73,6 @@ Name[sv]=Kanaler Name[ta]=உள்ளீடுகள் Name[tr]=Haberler Name[uk]=Подачі -Name[uz]=Yangiliklar tasmalari -Name[uz@cyrillic]=Янгиликлар тасмалари +Name[uz]=Янгиликлар тасмалари Name[zh_CN]=种子 diff --git a/kontact/plugins/akregator/akregatorplugin3.2.desktop b/kontact/plugins/akregator/akregatorplugin3.2.desktop index 7bd29ffb..81f9f074 100644 --- a/kontact/plugins/akregator/akregatorplugin3.2.desktop +++ b/kontact/plugins/akregator/akregatorplugin3.2.desktop @@ -35,7 +35,6 @@ Comment[hu]=Akregator bővítőmodul Comment[is]=Akregator íforrit Comment[it]=Plugin aKregator Comment[ja]=Akregator プラグイン -Comment[ka]=Akregator-ის მოდული Comment[kk]=Akregator модулі Comment[km]=កម្មវិធី​ជំនួយ Akregator Comment[lt]=Akregator priedas @@ -58,8 +57,7 @@ Comment[sv]=Akregator-insticksprogram Comment[ta]=Akregator சொருகுப்பொருள் Comment[tr]=Akregator Eklentisi Comment[uk]=Втулок Akregator -Comment[uz]=Akregator plagini -Comment[uz@cyrillic]=Akregator плагини +Comment[uz]=Akregator плагини Comment[zh_CN]=Akregator 插件 Comment[zh_TW]=Akregator 外掛程式 Name=Feeds @@ -85,7 +83,6 @@ Name[hu]=Hírforrások Name[is]=Fréttastraumar Name[it]=Fonti Name[ja]=フィード -Name[ka]=კვება Name[kk]=Ақпарлар Name[km]=មតិព័ត៌មាន Name[lt]=Kanalai @@ -98,7 +95,7 @@ Name[pl]=Kanały Name[pt]=Fontes Name[pt_BR]=Fontes de Notícias Name[ru]=Ленты новостей -Name[sk]=Kanály +Name[sk]=Kŕmitka Name[sl]=Viri Name[sr]=Доводи Name[sr@Latn]=Dovodi @@ -106,7 +103,6 @@ Name[sv]=Kanaler Name[ta]=உள்ளீடுகள் Name[tr]=Haberler Name[uk]=Подачі -Name[uz]=Yangiliklar tasmalari -Name[uz@cyrillic]=Янгиликлар тасмалари +Name[uz]=Янгиликлар тасмалари Name[zh_CN]=种子 diff --git a/kontact/plugins/kaddressbook/kaddressbook_plugin.cpp b/kontact/plugins/kaddressbook/kaddressbook_plugin.cpp index e3128cdf..d0493f1d 100644 --- a/kontact/plugins/kaddressbook/kaddressbook_plugin.cpp +++ b/kontact/plugins/kaddressbook/kaddressbook_plugin.cpp @@ -145,8 +145,7 @@ bool KAddressbookPlugin::isRunningStandalone() bool KAddressbookPlugin::canDecodeDrag( TQMimeSource *mimeSource ) { - return TQTextDrag::canDecode( mimeSource ) || - KPIM::MailListDrag::canDecode( mimeSource ); + return KPIM::MailListDrag::canDecode( mimeSource ); } #include diff --git a/kontact/plugins/kaddressbook/kaddressbookplugin.desktop b/kontact/plugins/kaddressbook/kaddressbookplugin.desktop index f5208ea5..29e5cb23 100644 --- a/kontact/plugins/kaddressbook/kaddressbookplugin.desktop +++ b/kontact/plugins/kaddressbook/kaddressbookplugin.desktop @@ -19,23 +19,17 @@ Comment=Contacts Component (KAdressbook Plugin) Comment[bg]=Приставка за адресника Comment[ca]=Component de contactes (endollable del KAdressbook) Comment[da]=Kontaktkomponent (KAddressbook-plugin) -Comment[de]=Kontakte-Komponente (Adressbuch-Modul) +Comment[de]=Adressbuch-Komponente (KAddressbook-Modul) Comment[el]=Συστατικό επαφών (Πρόσθετο του KAdressbook) -Comment[es]=Componente de contactos (complemento de KAddressbook) Comment[et]=Kontaktide plugin (KDE aadressiraamat) -Comment[fr]=Composant des contacts (module externe KAdressBook) -Comment[is]=Vistfangaskráreining (KAddressBook íforrit) Comment[it]=Componente contatti (plugin KAddressbook) Comment[ja]=アドレス帳コンポーネント (KAddressbook プラグイン) -Comment[km]=សមាសភាគ​ទំនាក់ទំន​ង (កម្មវិធី​ជំនួយ KAdressbook​) Comment[nds]=Kontakten-Komponent (KAddressbook-Moduul) Comment[nl]=Adresboekcomponent (KAddressbook-plugin) Comment[pl]=Składnik wizytówek (wtyczka KAddressBook) -Comment[ru]=Контакты (модуль KAddressBook) Comment[sr]=Компонента контаката (прикључак KAddressBook-а) Comment[sr@Latn]=Komponenta kontakata (priključak KAddressBook-a) Comment[sv]=Kontaktkomponent (adressboksinsticksprogram) -Comment[tr]=Kişiler Bileşeni (KAdresDefteri Eklentisi) Comment[zh_CN]=联系人组件(KAddressbook 插件) Comment[zh_TW]=聯絡人組件(KAddressBook 外掛程式) Name=Contacts @@ -66,7 +60,6 @@ Name[hu]=Névjegyek Name[is]=Tengiliðir Name[it]=Contatti Name[ja]=コンタクト -Name[ka]=კონტაქტები Name[kk]=Контакттар Name[km]=ទំនាក់ទំនង Name[lt]=Kontaktai @@ -92,9 +85,8 @@ Name[sv]=Kontakter Name[ta]=தொடர்புகள் Name[tg]=Алоқот Name[th]=ที่อยู่ติดต่อ -Name[tr]=Kişiler +Name[tr]=Bağlantılar Name[uk]=Контакти -Name[uz]=Aloqalar -Name[uz@cyrillic]=Алоқалар +Name[uz]=Алоқалар Name[zh_CN]=联系人 Name[zh_TW]=聯絡人 diff --git a/kontact/plugins/karm/karmplugin.desktop b/kontact/plugins/karm/karmplugin.desktop index 469989fe..c6b39dfc 100644 --- a/kontact/plugins/karm/karmplugin.desktop +++ b/kontact/plugins/karm/karmplugin.desktop @@ -16,23 +16,17 @@ Comment=Time Tracker Component (KArm Plugin) Comment[bg]=Приставка за KArm Comment[ca]=Component de seguiment dels temps (endollable del KArm) Comment[da]=Time Tracker-komponent (KArm-plugin) -Comment[de]=Zeitplaner-Komponente (KArm-Modul) +Comment[de]=Zeiterfassungskomponente (KArm-Modul) Comment[el]=Συστατικό γραμμής χρόνου (Πρόσθετο του KArm) -Comment[es]=Componente de seguimiento de tiempos (complemento de KArm) Comment[et]=Ajaarvestaja plugin (KArm) -Comment[fr]=Composant de suivi temporel (Module pour KArm) -Comment[is]=Tímastjórnunareining (KArm íforrit) Comment[it]=Componente segna-tempo (plugin Karm) Comment[ja]=タイムトラッカーコンポーネント (KArm プラグイン) -Comment[km]=សមាសភាគ​កម្មវិធី​តាមដាន​ពេលវេលា (កម្មវិធី​ជំនួយ KArm​) Comment[nds]=Tietlogbook-Komponent (KArm-Moduul) Comment[nl]=Tijdsregistratiecomponent (KArm-plugin) Comment[pl]=Składnik śledzenia czasu (wtyczka KArm) -Comment[ru]=Отслеживание времени (модуль KArm) Comment[sr]=Компонента праћења времена (прикључак KArm-а) Comment[sr@Latn]=Komponenta praćenja vremena (priključak KArm-a) Comment[sv]=Komponent för tidmätning (Karm-insticksprogram) -Comment[tr]=Zaman İzleyici Bileşeni (KArm Eklentisi) Comment[zh_CN]=时间追踪组件(KArm 插件) Comment[zh_TW]=時間追蹤器組件(KArm 外掛程式) @@ -41,17 +35,11 @@ Name[bg]=Таймер Name[ca]=Cronòmetre Name[de]=Stoppuhr Name[el]=Χρονόμετρο -Name[es]=Temporizador Name[et]=Ajaarvestaja -Name[fr]=Minuteur -Name[is]=Tímamælir Name[ja]=タイマー -Name[km]=កម្មវិធី​កំណត់​ពេលវេលា Name[nds]=Tietgever Name[nl]=Tijdklok Name[pl]=Stoper -Name[ru]=Таймер -Name[sk]=Časovač Name[sr]=Тајмер Name[sr@Latn]=Tajmer Name[sv]=Tidmätning diff --git a/kontact/plugins/kitchensync/kitchensync.desktop b/kontact/plugins/kitchensync/kitchensync.desktop index 0b1ac507..8b8fd76b 100644 --- a/kontact/plugins/kitchensync/kitchensync.desktop +++ b/kontact/plugins/kitchensync/kitchensync.desktop @@ -16,25 +16,17 @@ Comment=Synchronization Component (Kitchensynk Plugin) Comment[bg]=Приставка за синхронизация Comment[ca]=Component de sincronització (endollable del KitchenSync) Comment[da]=Synkronisergingskomponent (Kitchensync-plugin) -Comment[de]=Abgleich-Komponente (KitchenSync-Modul) +Comment[de]=Synchronisationskomponente (KitchenSync-Modul) Comment[el]=Συστατικό συγχρονισμού (Πρόσθετο του Kitchensynk) -Comment[en_GB]=Synchronisation Component (Kitchensynk Plugin) -Comment[es]=Componente de sincronización (complemento de KitchenSync) Comment[et]=Sünkroniseerimise plugin (KitchenSync) -Comment[fr]=Composant de synchronisation (Module KitchenSync) -Comment[is]=Samstillingareining (KitchenSync íforrit) Comment[it]=Componente di sincronizzazione (plugin KitchenSync) Comment[ja]=同期コンポーネント (KitchenSync プラグイン) -Comment[km]=ការ​ធ្វើ​សមកាលកម្ម​សមាសភាគ (កម្មវិធី​ជំនួយ Kitchensynk​) Comment[nds]=Synkroniseer-Komponent (Kitchensynk-Moduul) Comment[nl]=Synchronisatiecomponent (Kitchensynk-plugin) Comment[pl]=Składnik synchronizacji (wtyczka KitchenSync) -Comment[ru]=Синхронизация (модуль KitchenSync) -Comment[sk]=Synchronizačný komponent (Modul pre Kitchensynk) Comment[sr]=Компонента синхронизације (прикључак KitchenSync-а) Comment[sr@Latn]=Komponenta sinhronizacije (priključak KitchenSync-a) Comment[sv]=Synkroniseringskomponent (Kitchensynk-insticksprogram) -Comment[tr]=Eşzamanlama Eklentisi (Kitchensynk Eklentisi) Comment[zh_CN]=同步组件(KitchenSync 插件) Comment[zh_TW]=同步組件(KitchenSynk 外掛程式) Name=Sync @@ -42,11 +34,8 @@ Name[bg]=Синхронизация Name[de]=Abgleich Name[el]=Συγχρονισμός Name[et]=Sünkroniseerimine -Name[fr]=Synchroniser -Name[is]=Samstilling Name[ja]=同期 Name[nds]=Synkroniseren Name[pl]=Synchronizacja -Name[ru]=Синхронизация Name[sv]=Synkronisering Name[zh_TW]=同步 diff --git a/kontact/plugins/kmail/Makefile.am b/kontact/plugins/kmail/Makefile.am index d176693a..f390aff4 100644 --- a/kontact/plugins/kmail/Makefile.am +++ b/kontact/plugins/kmail/Makefile.am @@ -1,5 +1,7 @@ INCLUDES = -I$(top_srcdir)/kontact/interfaces -I$(top_srcdir)/kmail -I$(top_builddir)/kmail \ -I$(top_srcdir)/libkdepim \ + -I$(top_srcdir)/libkdenetwork \ + -I$(top_srcdir)/certmanager/lib \ -I$(top_srcdir) $(all_includes) kde_module_LTLIBRARIES = libkontact_kmailplugin.la kcm_kmailsummary.la diff --git a/kontact/plugins/kmail/kcmkmailsummary.desktop b/kontact/plugins/kmail/kcmkmailsummary.desktop index 2b8c7d99..d616429e 100644 --- a/kontact/plugins/kmail/kcmkmailsummary.desktop +++ b/kontact/plugins/kmail/kcmkmailsummary.desktop @@ -16,22 +16,15 @@ Name[ca]=Resum de correu Name[da]=Oversigt over e-mail Name[de]=E-Mail-Übersicht Name[el]=Επισκόπηση αλληλογραφίας -Name[es]=Resumen de correo electrónico Name[et]=E-posti ülevaade -Name[fr]=Aperçu du courriel -Name[is]=Yfirsýn á tölvupóst Name[it]=Panoramica posta elettronica Name[ja]=メールの要約 -Name[km]=ទិដ្ឋភាព​ទូទៅ​របស់​អ៊ីមែល Name[nds]=Nettpost-Översicht Name[nl]=E-mailoverzicht Name[pl]=Poczta -Name[ru]=Сведения о почте -Name[sk]=Prehľad pošty Name[sr]=Преглед е-поште Name[sr@Latn]=Pregled e-pošte Name[sv]=E-postöversikt -Name[tr]=E-Postalara Genel Bakış Name[zh_CN]=邮件概览 Name[zh_TW]=郵件概要 Comment=E-Mail Summary Setup @@ -40,22 +33,15 @@ Comment[ca]=Configuració del resum de correu Comment[da]=Opsætning af post-opsummering Comment[de]=Einstellungen für E-Mail-Übersicht Comment[el]=Ρύθμιση σύνοψης αλληλογραφίας -Comment[es]=Configuración del resumen de correo electrónico Comment[et]=E-posti kokkuvõtte seadistus -Comment[fr]=Configuration du résumé des courriels -Comment[is]=Uppsetning póstyfirlits Comment[it]=Impostazioni sommario posta elettronica Comment[ja]=メール要約の設定 -Comment[km]=រៀបចំ​សេចក្ដី​សង្ខេប​អ៊ីមែល Comment[nds]=Instellen för Nettpost-Översicht Comment[nl]=Instellingen voor e-mailoverzicht Comment[pl]=Ustawienia podsumowania e-maili -Comment[ru]=Настройка сводки почты -Comment[sk]=Nastavenie súhrnu pošty Comment[sr]=Подешавање сажетка е-поште Comment[sr@Latn]=Podešavanje sažetka e-pošte Comment[sv]=Inställning av e-postöversikt -Comment[tr]=E-Posta Özet Yapılandırması Comment[zh_CN]=邮件摘要设置 Comment[zh_TW]=郵件摘要設定 Keywords=email, summary, configure, settings @@ -72,7 +58,7 @@ Keywords[et]=e-post, meil, seadistamine, seadistused Keywords[eu]=eposta, laburpena, konfiguratu, ezarpenak Keywords[fa]=email، خلاصه، پیکربندی، تنظیمات Keywords[fi]=sähköposti, yhteenveto, asetukset -Keywords[fr]=message,messagerie,courriel,résumé,vue,configurer,paramètres,paramètre +Keywords[fr]=message,messagerie,courrier,résumé,vue,configurer,paramètres,paramètre Keywords[fy]=email,e-mail,e-post,oersicht,gearfetting,ynstellings, konfiguraasje Keywords[ga]=ríomhphost, achoimre, cumraigh, socruithe Keywords[gl]=email, resumo, configurar, opcións @@ -81,7 +67,6 @@ Keywords[hu]=e-mail,áttekintés,konfigurálás,beállítások Keywords[is]=tölvupóstur, yfirlit, stillingar, stilla Keywords[it]=posta elettronica, email, sommario, configura, impostazioni Keywords[ja]=メール,要約,設定,設定 -Keywords[ka]=ელფოსტა,დაიჯესტი,კონფიგურაცია,პარამეტრები Keywords[km]=អ៊ីមែល,សង្ខេប,កំណត់​រចនាសម្ព័ន្ធ,ការ​កំណត់ Keywords[lt]=email, summary, configure, settings, e. paštas, santrauka, konfigūruoti, nustatymai Keywords[mk]=email, summary, configure, settings, е-пошта, преглед, конфигурација, поставувања @@ -91,10 +76,10 @@ Keywords[nds]=Nettpost,Nettbreef,Översicht,instellen Keywords[ne]=इमेल, सारांश, कन्फिगर, सेटिङ Keywords[nl]=email,e-mail,overzicht,samenvatting,instellingen,configuratie Keywords[nn]=e-post,samandrag,oppsett,innstillingar -Keywords[pl]=e-mail,list,podsumowanie,konfiguracja,ustawienia +Keywords[pl]=email,list,podsumowanie,konfiguracja,ustawienia Keywords[pt]=e-mail, sumário, configurar, configuração Keywords[pt_BR]=e-mail, resumo, configurar, configurações -Keywords[ru]=email,summary,configure,settings,настройки,сводка,почта +Keywords[ru]=email,summary,configure,settings,настройки,дайджест,почта Keywords[sk]=email,súhrn,nastavenie Keywords[sl]=e-pošta,pošta,povzetek,nastavi,nastavitve Keywords[sr]=емаил, сажетак, подеси, поставке diff --git a/kontact/plugins/kmail/kmailplugin.desktop b/kontact/plugins/kmail/kmailplugin.desktop index 55ba5e97..7758a8e2 100644 --- a/kontact/plugins/kmail/kmailplugin.desktop +++ b/kontact/plugins/kmail/kmailplugin.desktop @@ -22,46 +22,31 @@ Comment[ca]=Component de correu (endollable del KMail) Comment[da]=Post-komponent (KMail-plugin) Comment[de]=E-Mail-Komponente (KMail-Modul) Comment[el]=Συστατικό αλληλογραφίας (Πρόσθετο του KMail) -Comment[es]=Componente de correo electrónico (complemento de KMail) Comment[et]=E-posti plugin (KMail) -Comment[fr]=Composant de courriel (Module pour KMail) -Comment[is]=Pósteining (KMail íforrit) Comment[it]=Componente posta elettronica (plugin KMail) Comment[ja]=メールコンポーネント (KMail プラグイン) -Comment[km]=សមាសភាគ​អ៊ីមែល (កម្មវិធី​ជំនួយ KMail) Comment[nds]=Nettpost-Komponent (KMail-Moduul) Comment[nl]=E-mailcomponent (KMail-plugin) Comment[pl]=Składnik poczty (wtyczka KMail) -Comment[pt_BR]=Componente de e-mail (plug-in do KMail) -Comment[ru]=Электронная почта (модуль KMail) -Comment[sk]=Poštový komponent (Model pre KMail) Comment[sr]=Компонента е-поште (прикључак KMail-а) Comment[sr@Latn]=Komponenta e-pošte (priključak KMail-a) Comment[sv]=E-postkomponent (Kmail-insticksprogram) -Comment[tr]=E-Posta Bileşeni (KMail Eklentisi) Comment[zh_CN]=邮件组件(KMail 插件) Comment[zh_TW]=電子郵件組件(KMail 外掛程式) Name=E-Mail Name[bg]=Е-поща Name[ca]=Correu Name[da]=E-mail +Name[de]=E-Mail Name[el]=Αλληλογραφία -Name[es]=Correo electrónico Name[et]=E-post -Name[fr]=Courriel -Name[is]=Tölvupóstur Name[it]=Posta elettronica Name[ja]=メール -Name[km]=អ៊ីមែល Name[nds]=Nettpost Name[nl]=E-mail Name[pl]=E-mail -Name[pt_BR]=E-mail -Name[ru]=Электронная почта -Name[sk]=Pošta Name[sr]=Е-пошта Name[sr@Latn]=E-pošta Name[sv]=E-post -Name[tr]=E-Posta Name[zh_CN]=邮件 Name[zh_TW]=電子郵件 diff --git a/kontact/plugins/knode/knodeplugin.desktop b/kontact/plugins/knode/knodeplugin.desktop index d84c16f7..559eaadd 100644 --- a/kontact/plugins/knode/knodeplugin.desktop +++ b/kontact/plugins/knode/knodeplugin.desktop @@ -19,21 +19,15 @@ Comment[ca]=Component de notícies (endollable del KNode) Comment[da]=Nyhedskomponent (KNode-plugin) Comment[de]=News-Komponente (KNode-Modul) Comment[el]=Συστατικό ανάγνωσης νέων (Πρόσθετο του KNode) -Comment[es]=Componente de noticias (complemento de KNode) Comment[et]=Uudistelugeja plugin (KNode) -Comment[fr]=Composant de lecteur de nouvelles (Module pour KNode) -Comment[is]=Fréttaeining (KNode íforrit) Comment[it]=Componente lettore di news (plugin KNode) Comment[ja]=ニュースリーダーコンポーネント (KNode プラグイン) -Comment[km]=សមាសភាគ Newsreader (កម្មវិធី​ជំនួយ KNode) Comment[nds]=Narichtenkieker-Komponent (KNode-Moduul) Comment[nl]=Nieuwscomponent (KNode-plugin) Comment[pl]=Składnik wiadomości (wtyczka KNode) -Comment[ru]=Новости (модуль KNode) Comment[sr]=Компонента вести (прикључак KNode-а) Comment[sr@Latn]=Komponenta vesti (priključak KNode-a) Comment[sv]=Komponent för läsning av diskussionsgrupper (Knode-insticksprogram) -Comment[tr]=Haber Okuyucu Bileşeni (KNode Eklentisi) Comment[zh_CN]=新闻组阅读器组件(KNode 插件) Comment[zh_TW]=新聞閱讀器組件(KNode 外掛程式) Name=News @@ -64,7 +58,6 @@ Name[hi]=समाचार Name[hu]=Hírek Name[is]=Fréttir Name[ja]=ニュース -Name[ka]=სიახლეები Name[kk]=Жаңалықтар Name[km]=ព័ត៌មាន Name[lt]=Naujienos @@ -92,7 +85,6 @@ Name[tg]=Ахборот Name[th]=ข่าว Name[tr]=Haberler Name[uk]=Новини -Name[uz]=Yangiliklar -Name[uz@cyrillic]=Янгиликлар +Name[uz]=Янгиликлар Name[zh_CN]=新闻 Name[zh_TW]=新聞 diff --git a/kontact/plugins/knotes/knotes_part.cpp b/kontact/plugins/knotes/knotes_part.cpp index 4702eb2d..d4a5e854 100644 --- a/kontact/plugins/knotes/knotes_part.cpp +++ b/kontact/plugins/knotes/knotes_part.cpp @@ -386,12 +386,14 @@ void KNotesPart::editNote( TQIconViewItem *item ) void KNotesPart::renameNote() { + mOldName = mNotesView->currentItem()->text(); mNotesView->currentItem()->rename(); } void KNotesPart::renamedNote( TQIconViewItem* ) { - mManager->save(); + if ( mOldName != mNotesView->currentItem()->text() ) + mManager->save(); } void KNotesPart::slotOnCurrentChanged( TQIconViewItem* ) diff --git a/kontact/plugins/knotes/knotes_part.h b/kontact/plugins/knotes/knotes_part.h index de41f9aa..764bb484 100644 --- a/kontact/plugins/knotes/knotes_part.h +++ b/kontact/plugins/knotes/knotes_part.h @@ -96,6 +96,7 @@ class KNotesPart : public KParts::ReadOnlyPart, virtual public KNotesIface KNotesResourceManager *mManager; TQDict mNoteList; + TQString mOldName; }; #endif diff --git a/kontact/plugins/knotes/knotesplugin.desktop b/kontact/plugins/knotes/knotesplugin.desktop index e7fe8fc0..71068d94 100644 --- a/kontact/plugins/knotes/knotesplugin.desktop +++ b/kontact/plugins/knotes/knotesplugin.desktop @@ -19,21 +19,15 @@ Comment[ca]=Component de notes (endollable del KNotes) Comment[da]=Notatkomponent (KNotes-plugin) Comment[de]=Notizen-Komponente (KNotes-Modul) Comment[el]=Συσταικό σημειώσεων (Πρόσθετο του KNotes) -Comment[es]=Componente de notas (complemento de KNotes) Comment[et]=Märkmete plugin (KNotes) -Comment[fr]=Composant de notes (Module KNotes) -Comment[is]=Minnismiðaeining (KNotes íforrit) Comment[it]=Componente note (plugin KNotes) Comment[ja]=メモコンポーネント (KNotes プラグイン) -Comment[km]=សមាសភាគ​ចំណាំ (កម្មវិធី​ជំនួយ KNotes​) Comment[nds]=Notizen-Komponent (KNotes-Moduul) Comment[nl]=Notitiecomponent (KNotes-plugin) Comment[pl]=Składnik notatek (wtyczka KNotes) -Comment[ru]=Заметки (модуль KNotes) Comment[sr]=Компонента белешки (прикључак KNotes-а) Comment[sr@Latn]=Komponenta beleški (priključak KNotes-a) Comment[sv]=Anteckningskomponent (Knotes-insticksprogram) -Comment[tr]=Notlar Bileşeni (KNotes Eklentisi) Comment[zh_CN]=便笺组件(KNotes 插件) Comment[zh_TW]=便條組件(KNotes 外掛程式) Name=Notes @@ -63,7 +57,6 @@ Name[hu]=Feljegyzések Name[is]=Minnismiðar Name[it]=Note Name[ja]=メモ -Name[ka]=ჩანიშვნები Name[kk]=Жазбалар Name[km]=ចំណាំ Name[lt]=Užrašai @@ -89,7 +82,6 @@ Name[tg]=Ахборот Name[th]=บันทึกช่วยจำ Name[tr]=Notlar Name[uk]=Примітки -Name[uz]=Yozma xotira -Name[uz@cyrillic]=Ёзма хотира +Name[uz]=Ёзма хотира Name[zh_CN]=便笺 Name[zh_TW]=備忘錄 diff --git a/kontact/plugins/korganizer/journalplugin.desktop b/kontact/plugins/korganizer/journalplugin.desktop index 8f33753e..f24c1206 100644 --- a/kontact/plugins/korganizer/journalplugin.desktop +++ b/kontact/plugins/korganizer/journalplugin.desktop @@ -21,21 +21,15 @@ Comment[ca]=Component de diari (endollable del KOrganizer) Comment[da]=Journalkomponent (KOrganizer-plugin) Comment[de]=Journal-Komponente (KOrganizer-Modul) Comment[el]=Συστατικό χρονικών (Πρόσθετο του KOrganizer) -Comment[es]=Componente de diario (Complemento de KOrganizer) Comment[et]=Päevikuplugin (KOrganizer) -Comment[fr]= Composant de journal (Module KOrganizer) -Comment[is]=Dagbókareining (Journal KOrganizer íforrit) Comment[it]=Componente diario (plugin KOrganizer) Comment[ja]=ジャーナルコンポーネント (KOrganizer プラグイン) -Comment[km]=សមាភាគទិនានុប្បវត្តិ (កម្មវិធី​ជំនួយ KOrganizer​) Comment[nds]=Daagböker-Komponent (KOrganizer-Moduul) Comment[nl]=Journaalcomponent (KOrganizer-plugin) Comment[pl]=Składnik dziennika (wtyczka Korganizer) -Comment[ru]=Журнал (модуль KOrganizer) Comment[sr]=Компонента дневника (прикључак KOrganizer-а) Comment[sr@Latn]=Komponenta dnevnika (priključak KOrganizer-a) Comment[sv]=Journalkomponent (Korganizer-insticksprogram) -Comment[tr]=Günlük Bileşeni (KOrganizer Eklentisi) Comment[zh_CN]=日记组件(KOrganizer 插件) Comment[zh_TW]=日誌組件(KOrganizer 外掛程式) Name=Journal @@ -61,7 +55,6 @@ Name[hu]=Napló Name[is]=Dagbók Name[it]=Diario Name[ja]=ジャーナル -Name[ka]=ჟურნალი Name[kk]=Күнделік Name[km]=ទិនានុប្បវត្តិ Name[lt]=Dienynas @@ -83,7 +76,6 @@ Name[ta]=பத்திரிகை Name[th]=วารสาร Name[tr]=Günlük Name[uk]=Журнал -Name[uz]=Kundalik -Name[uz@cyrillic]=Кундалик +Name[uz]=Кундалик Name[zh_CN]=日记 Name[zh_TW]=日誌 diff --git a/kontact/plugins/korganizer/journalplugin.h b/kontact/plugins/korganizer/journalplugin.h index 2aeca354..beb4c2e9 100644 --- a/kontact/plugins/korganizer/journalplugin.h +++ b/kontact/plugins/korganizer/journalplugin.h @@ -40,7 +40,7 @@ class JournalPlugin : public Kontact::Plugin virtual bool createDCOPInterface( const TQString& serviceType ); virtual bool isRunningStandalone(); - int weight() const { return 500; } + int weight() const { return 525; } virtual TQStringList invisibleToolbarActions() const; diff --git a/kontact/plugins/korganizer/kcmkorgsummary.desktop b/kontact/plugins/korganizer/kcmkorgsummary.desktop index 2f657852..175ed624 100644 --- a/kontact/plugins/korganizer/kcmkorgsummary.desktop +++ b/kontact/plugins/korganizer/kcmkorgsummary.desktop @@ -16,21 +16,15 @@ Name[ca]=Resum de cites i tasques pendents Name[da]=Oversigt over møder og gøremål Name[de]=Übersicht über Termine und Aufgaben Name[el]=Επισκόπηση ραντεβού και προς υλοποίηση εργασιών -Name[es]=Resumen de citas y tareas pendientes Name[et]=Kohtumised ja ülesannete ülevaade -Name[fr]=Aperçu des rendez-vous et des tâches -Name[is]=Yfirlit um fundi og verkþætti Name[it]=Panoramica appuntamenti e cose da fare Name[ja]=約束と To-Do の要約 -Name[km]=ទិដ្ឋភាព​ការ​ណាត់ និង​ការងារ​ត្រូវ​ធ្វើ Name[nds]=Termin- un Opgaven-Översicht Name[nl]=Overzicht van evenementen en taken Name[pl]=Spotkania i zadania -Name[ru]=Сводка встреч и задач Name[sr]=Преглед састанака и обавеза Name[sr@Latn]=Pregled sastanaka i obaveza Name[sv]=Översikt av möten och uppgifter -Name[tr]=Randevulara ve Yapılacaklara Genel Bakış Name[zh_CN]=约会和待办概览 Name[zh_TW]=約會與待辦事項概觀 Comment=Appointments and To-dos Summary Setup @@ -50,10 +44,9 @@ Comment[fr]=Configuration du résumé des évènements et des tâches Comment[fy]=Oersichtsynstellings foar eveneminten en taken Comment[gl]=Configuración de sumarios de tarefas e notas Comment[hu]=A találkozók és feladatok áttekintőjének beállítása -Comment[is]=Uppsetning á yfirliti yfir fundi og verkefni +Comment[is]=Uppsetning á yfirliti yfir fundi og verkþætti Comment[it]=Impostazioni sommario appuntamenti e cose da fare Comment[ja]=約束と To-Do の要約設定 -Comment[ka]=შეხვედრათა და გასაკეთებელთა რეზიუმეს დაყენება Comment[kk]=Кездесулер мен Жоспарлар тұжырымының баптау Comment[km]=រៀបចំ​សេចក្ដី​សង្ខេប​ការ​ណាត់ និង​ការងារ​ត្រូវ​ធ្វើ Comment[lt]=Susitikimų ir užduočių santraukos nustatymai @@ -66,7 +59,7 @@ Comment[nn]=Oppsett av samandrag av avtalar og oppgåver Comment[pl]=Ustawienia podsumowania spotkań i zadań Comment[pt]=Configuração do Sumário de Compromissos e A-fazeres Comment[pt_BR]=Configuração do Resumo de Compromissos e Tarefas -Comment[ru]=Настройка сводки встреч и задач +Comment[ru]=Настройка показа встреч и задач Comment[sk]=Nastavenie súhrnu pripomienok a úloh Comment[sl]=Nastavitve povzetka sestankov in opravil Comment[sr]=Подешавање сажетка састанака и обавеза @@ -97,10 +90,9 @@ Keywords[ga]=féilire, tascanna, cumraigh, socruithe Keywords[gl]=calendario, pendentes, configurar, opcións Keywords[he]=calendar, todos, configure, settings, יומן, יומנים, משימות, מטלות, הגדרות, תצורה Keywords[hu]=naptár,feladatok,konfigurálás,beállítások -Keywords[is]=dagatal, verkefni, stillingar, stilla +Keywords[is]=dagatal, verkþættir, stillingar, stilla Keywords[it]=calendario, cose da fare, configura, impostazioni Keywords[ja]=カレンダー, To-Do, 設定 -Keywords[ka]=კალენდარი,გასაკეთებლები,კონფიგურაცია,პარამეტრები Keywords[km]=ប្រតិទិន,ការងារ​ត្រូវ​ធ្វើ,កំណត់​រចនាសម្ព័ន្ធ,ការ​កំណត់ Keywords[lt]=calendar, todos, configure, settings, kalendorius, darbai, konfigūruoti, nustatymai Keywords[ms]=kalendar, tugasan, konfigur, seting diff --git a/kontact/plugins/korganizer/korg_uniqueapp.cpp b/kontact/plugins/korganizer/korg_uniqueapp.cpp index b7004235..42a7adca 100644 --- a/kontact/plugins/korganizer/korg_uniqueapp.cpp +++ b/kontact/plugins/korganizer/korg_uniqueapp.cpp @@ -20,19 +20,40 @@ */ #include "korg_uniqueapp.h" -#include #include "../../korganizer/korganizer_options.h" +#include "core.h" +#include +#include +#include +#include + void KOrganizerUniqueAppHandler::loadCommandLineOptions() { - KCmdLineArgs::addCmdLineOptions( korganizer_options ); + KCmdLineArgs::addCmdLineOptions( korganizer_options ); } int KOrganizerUniqueAppHandler::newInstance() { - //kdDebug(5602) << k_funcinfo << endl; - // Ensure part is loaded - (void)plugin()->part(); - // TODO handle command line options - return Kontact::UniqueAppHandler::newInstance(); + // Ensure part is loaded + (void)plugin()->part(); + DCOPRef korganizer( "korganizer", "KOrganizerIface" ); + korganizer.send( "handleCommandLine" ); + + // Bring korganizer's plugin to front + // This bit is duplicated from KUniqueApplication::newInstance() + if ( kapp->mainWidget() ) { + kapp->mainWidget()->show(); + KWin::forceActiveWindow( kapp->mainWidget()->winId() ); + KStartupInfo::appStarted(); + } + + // Then ensure the part appears in kontact. + // ALWAYS use the korganizer plugin; i.e. never show the todo nor journal + // plugins when creating a new instance via the command line, even if + // the command line options are empty; else we'd need to examine the + // options and then figure out which plugin we should show. + // kolab/issue3971 + plugin()->core()->selectPlugin( "kontact_korganizerplugin" ); + return 0; } diff --git a/kontact/plugins/korganizer/korganizerplugin.cpp b/kontact/plugins/korganizer/korganizerplugin.cpp index f0f88c7c..cd590ec9 100644 --- a/kontact/plugins/korganizer/korganizerplugin.cpp +++ b/kontact/plugins/korganizer/korganizerplugin.cpp @@ -42,6 +42,10 @@ #include #include +#include + +#include +#include #include "core.h" #include "summarywidget.h" @@ -78,6 +82,11 @@ KOrganizerPlugin::~KOrganizerPlugin() Kontact::Summary *KOrganizerPlugin::createSummaryWidget( TQWidget *parent ) { + // korg part must be loaded, otherwise when starting kontact on summary view + // it won't display our stuff. + // If the part is already loaded loadPart() is harmless and just returns + loadPart(); + return new SummaryWidget( this, parent ); } @@ -160,27 +169,43 @@ bool KOrganizerPlugin::canDecodeDrag( TQMimeSource *mimeSource ) void KOrganizerPlugin::processDropEvent( TQDropEvent *event ) { - TQString text; - - KABC::VCardConverter converter; - if ( KVCardDrag::canDecode( event ) && KVCardDrag::decode( event, text ) ) { - KABC::Addressee::List contacts = converter.parseVCards( text ); - KABC::Addressee::List::Iterator it; - + KABC::Addressee::List list; + if ( KVCardDrag::decode( event, list ) ) { TQStringList attendees; - for ( it = contacts.begin(); it != contacts.end(); ++it ) { + KABC::Addressee::List::Iterator it; + for ( it = list.begin(); it != list.end(); ++it ) { TQString email = (*it).fullEmail(); - if ( email.isEmpty() ) + if ( email.isEmpty() ) { attendees.append( (*it).realName() + "<>" ); - else + } else { attendees.append( email ); + } } - interface()->openEventEditor( i18n( "Meeting" ), TQString::null, TQString::null, attendees ); return; } + if ( KCal::ICalDrag::canDecode( event) ) { + KCal::CalendarLocal cal( KPimPrefs::timezone() ); + if ( KCal::ICalDrag::decode( event, &cal ) ) { + KCal::Incidence::List incidences = cal.incidences(); + if ( !incidences.isEmpty() ) { + event->accept(); + KCal::Incidence *i = incidences.first(); + TQString summary; + if ( dynamic_cast( i ) ) + summary = i18n( "Note: %1" ).arg( i->summary() ); + else + summary = i->summary(); + interface()->openEventEditor( summary, i->description(), TQString() ); + return; + } + // else fall through to text decoding + } + } + + TQString text; if ( TQTextDrag::decode( event, text ) ) { kdDebug(5602) << "DROP:" << text << endl; interface()->openEventEditor( text ); diff --git a/kontact/plugins/korganizer/korganizerplugin.desktop b/kontact/plugins/korganizer/korganizerplugin.desktop index 7a29913c..896320c1 100644 --- a/kontact/plugins/korganizer/korganizerplugin.desktop +++ b/kontact/plugins/korganizer/korganizerplugin.desktop @@ -21,22 +21,15 @@ Comment[ca]=Component de calendari (endollable del KOrganizer) Comment[da]=Kalenderkomponent (KOrganizer-plugin) Comment[de]=Kalender-Komponente (KOrganizer-Modul) Comment[el]=Συστατικό ημερολογίου (Πρόσθετο του KOrganizer) -Comment[es]=Componente de calendario (complemento de KOrganizer) Comment[et]=Kalendriplugin (KOrganizer) -Comment[fr]= Composant de calendrier (Module KOrganizer) -Comment[is]=Dagatalseining (KOrganizer íforrit) Comment[it]=Componente calendario (plugin KOrganizer) Comment[ja]=カレンダーコンポーネント (KOrganizer プラグイン) -Comment[km]=សមាសភាគ​ប្រតិទិន (កម្មវិធីជំនួយ​ KOrganizer​) Comment[nds]=Kalenner-Komponent (KOrganizer-Moduul) Comment[nl]=Agendacomponent (KOrganizer-plugin) Comment[pl]=Składnik kalendarza (wtyczka KOrganizer) -Comment[ru]=Календарь (модуль KOrganizer) -Comment[sk]=Kalendárový komponent (Modeul pre KOrganizer) Comment[sr]=Компонента календара (прикључак KOrganizer-а) Comment[sr@Latn]=Komponenta kalendara (priključak KOrganizer-a) Comment[sv]=Kalenderkomponent (Korganizer-insticksprogram) -Comment[tr]=Takvim Bileşeni (KOrganizer Eklentisi) Comment[zh_CN]=日历组件(KOrganizer 插件) Comment[zh_TW]=行事曆組件(KOrganizer 外掛程式) Name=Calendar @@ -68,7 +61,6 @@ Name[hu]=Naptár Name[is]=Dagatal Name[it]=Calendario Name[ja]=カレンダー -Name[ka]=კალენდარი Name[kk]=Күнтізбе Name[km]=ប្រតិទិន Name[lt]=Kalendorius @@ -95,7 +87,6 @@ Name[tg]=Тақвим Name[th]=บันทึกประจำวัน Name[tr]=Takvim Name[uk]=Календар -Name[uz]=Kalendar -Name[uz@cyrillic]=Календар +Name[uz]=Календар Name[zh_CN]=日历 Name[zh_TW]=行事曆 diff --git a/kontact/plugins/korganizer/summarywidget.cpp b/kontact/plugins/korganizer/summarywidget.cpp index ff3d5da9..cab7788f 100644 --- a/kontact/plugins/korganizer/summarywidget.cpp +++ b/kontact/plugins/korganizer/summarywidget.cpp @@ -65,7 +65,6 @@ SummaryWidget::SummaryWidget( KOrganizerPlugin *plugin, TQWidget *parent, mLayout->setRowStretch( 6, 1 ); mCalendar = KOrg::StdCalendar::self(); - mCalendar->load(); connect( mCalendar, TQT_SIGNAL( calendarChanged() ), TQT_SLOT( updateView() ) ); connect( mPlugin->core(), TQT_SIGNAL( dayChanged( const TQDate& ) ), @@ -94,6 +93,8 @@ void SummaryWidget::updateView() TQLabel *label = 0; int counter = 0; TQPixmap pm = loader.loadIcon( "appointment", KIcon::Small ); + TQPixmap pmb = loader.loadIcon( "calendarbirthday", KIcon::Small ); + TQPixmap pma = loader.loadIcon( "calendaranniversary", KIcon::Small ); TQDate dt; TQDate currentDate = TQDate::currentDate(); @@ -101,38 +102,22 @@ void SummaryWidget::updateView() dt<=currentDate.addDays( days - 1 ); dt=dt.addDays(1) ) { - KCal::Event *ev; - - KCal::Event::List events_orig = mCalendar->events( dt ); - KCal::Event::List::ConstIterator it = events_orig.begin(); - - KCal::Event::List events; - events.setAutoDelete( true ); - TQDateTime qdt; - - // prevent implicitely sharing while finding recurring events - // replacing the TQDate with the currentDate - for ( ; it != events_orig.end(); ++it ) { - ev = (*it)->clone(); - if ( ev->recursOn( dt ) ) { - qdt = ev->dtStart(); - qdt.setDate( dt ); - ev->setDtStart( qdt ); - } - events.append( ev ); - } + KCal::Event::List events = mCalendar->events( dt ); // sort the events for this date by summary - events = KCal::Calendar::sortEvents( &events, - KCal::EventSortSummary, - KCal::SortDirectionAscending ); + events = KCal::Calendar::sortEventsForDate( &events, + dt, + KCal::EventSortSummary, + KCal::SortDirectionAscending ); // sort the events for this date by start date - events = KCal::Calendar::sortEvents( &events, - KCal::EventSortStartDate, - KCal::SortDirectionAscending ); + events = KCal::Calendar::sortEventsForDate( &events, + dt, + KCal::EventSortStartDate, + KCal::SortDirectionAscending ); + KCal::Event::List::ConstIterator it = events.begin(); for ( it=events.begin(); it!=events.end(); ++it ) { - ev = *it; + KCal::Event *ev = *it; // Count number of days remaining in multiday event int span=1; int dayof=1; @@ -156,7 +141,13 @@ void SummaryWidget::updateView() // Fill Appointment Pixmap Field label = new TQLabel( this ); - label->setPixmap( pm ); + if ( ev->categories().contains( "Birthday" ) ) { + label->setPixmap( pmb ); + } else if ( ev->categories().contains( "Anniversary" ) ) { + label->setPixmap( pma ); + } else { + label->setPixmap( pm ); + } label->setMaximumWidth( label->minimumSizeHint().width() ); label->setAlignment( AlignVCenter ); mLayout->addWidget( label, counter, 0 ); @@ -167,7 +158,7 @@ void SummaryWidget::updateView() TQString datestr; // Modify event date for printing - TQDate sD = TQDate::TQDate( dt.year(), dt.month(), dt.day() ); + TQDate sD = TQDate( dt.year(), dt.month(), dt.day() ); if ( ( sD.month() == currentDate.month() ) && ( sD.day() == currentDate.day() ) ) { datestr = i18n( "Today" ); @@ -216,7 +207,7 @@ void SummaryWidget::updateView() connect( urlLabel, TQT_SIGNAL( rightClickedURL( const TQString& ) ), this, TQT_SLOT( popupMenu( const TQString& ) ) ); - TQString tipText( KCal::IncidenceFormatter::toolTipString( ev, true ) ); + TQString tipText( KCal::IncidenceFormatter::toolTipStr( mCalendar, ev, dt, true ) ); if ( !tipText.isEmpty() ) { TQToolTip::add( urlLabel, tipText ); } @@ -227,10 +218,10 @@ void SummaryWidget::updateView() TQTime sET = ev->dtEnd().time(); if ( ev->isMultiDay() ) { if ( ev->dtStart().date() < dt ) { - sST = TQTime::TQTime( 0, 0 ); + sST = TQTime( 0, 0 ); } if ( ev->dtEnd().date() > dt ) { - sET = TQTime::TQTime( 23, 59 ); + sET = TQTime( 23, 59 ); } } datestr = i18n( "Time from - to", "%1 - %2" ) diff --git a/kontact/plugins/korganizer/todoplugin.cpp b/kontact/plugins/korganizer/todoplugin.cpp index 3b6ab214..bc76205c 100644 --- a/kontact/plugins/korganizer/todoplugin.cpp +++ b/kontact/plugins/korganizer/todoplugin.cpp @@ -161,22 +161,18 @@ bool TodoPlugin::isRunningStandalone() void TodoPlugin::processDropEvent( TQDropEvent *event ) { - TQString text; - - KABC::VCardConverter converter; - if ( KVCardDrag::canDecode( event ) && KVCardDrag::decode( event, text ) ) { - KABC::Addressee::List contacts = converter.parseVCards( text ); - KABC::Addressee::List::Iterator it; - + KABC::Addressee::List list; + if ( KVCardDrag::decode( event, list ) ) { TQStringList attendees; - for ( it = contacts.begin(); it != contacts.end(); ++it ) { + KABC::Addressee::List::Iterator it; + for ( it = list.begin(); it != list.end(); ++it ) { TQString email = (*it).fullEmail(); - if ( email.isEmpty() ) + if ( email.isEmpty() ) { attendees.append( (*it).realName() + "<>" ); - else + } else { attendees.append( email ); + } } - interface()->openTodoEditor( i18n( "Meeting" ), TQString::null, TQString::null, attendees ); return; @@ -185,17 +181,23 @@ void TodoPlugin::processDropEvent( TQDropEvent *event ) if ( KCal::ICalDrag::canDecode( event) ) { KCal::CalendarLocal cal( KPimPrefs::timezone() ); if ( KCal::ICalDrag::decode( event, &cal ) ) { - KCal::Journal::List journals = cal.journals(); - if ( !journals.isEmpty() ) { + KCal::Incidence::List incidences = cal.incidences(); + if ( !incidences.isEmpty() ) { event->accept(); - KCal::Journal *j = journals.first(); - interface()->openTodoEditor( i18n("Note: %1").arg( j->summary() ), j->description(), TQString() ); + KCal::Incidence *i = incidences.first(); + TQString summary; + if ( dynamic_cast( i ) ) + summary = i18n( "Note: %1" ).arg( i->summary() ); + else + summary = i->summary(); + interface()->openTodoEditor( summary, i->description(), TQString() ); return; } // else fall through to text decoding } } + TQString text; if ( TQTextDrag::decode( event, text ) ) { interface()->openTodoEditor( text ); return; @@ -217,8 +219,8 @@ void TodoPlugin::processDropEvent( TQDropEvent *event ) mail.messageId(); tf.file()->writeBlock( event->encodedData( "message/rfc822" ) ); tf.close(); - interface()->openTodoEditor( i18n("Mail: %1").arg( mail.subject() ), txt, - uri, tf.name(), TQStringList(), "message/rfc822" ); + interface()->openTodoEditor( i18n("Mail: %1").arg( mail.subject() ), + txt, uri, tf.name(), TQStringList(), "message/rfc822", false ); } return; } diff --git a/kontact/plugins/korganizer/todoplugin.desktop b/kontact/plugins/korganizer/todoplugin.desktop index a571539d..dde1a0c1 100644 --- a/kontact/plugins/korganizer/todoplugin.desktop +++ b/kontact/plugins/korganizer/todoplugin.desktop @@ -21,22 +21,15 @@ Comment[ca]=Component de llista de pendents (endollable del KOrganizer) Comment[da]=Komponent til gøremålsliste (KOrganizer-plugin) Comment[de]=Aufgabenlisten-Komponente (KOrganizer-Modul) Comment[el]=Συστατικό λίστα προς υλοποίηση εργασιών (Πρόσθετο του KOrganizer) -Comment[es]=Componente de tareas pendientes (complemento de KOrganizer) Comment[et]=Ülesannete nimekirja plugin (KOrganizer) -Comment[fr]=Composant de la liste des tâches (Module KOrganizer) -Comment[is]=Verkefnaeining (KOrganizer íforrit) Comment[it]=Componente elenco delle cose da fare (plugin KOrganizer) Comment[ja]=To-Do リストコンポーネント (KOrganizer プラグイン) -Comment[km]=សមាសភាគ​បញ្ជី​ការងារ​ត្រូវ​ធ្វើ (កម្មវិធី​ជំនួយ KOrganizer​) Comment[nds]=Opgavenlist-Komponent (KOrganizer-Moduul) Comment[nl]=Takenlijstcomponent (KOrganizer-plugin) Comment[pl]=Składnik zadań (wtyczka KOrganizer) -Comment[ru]=Задачи (модуль KOrganizer) -Comment[sk]=Komponent zoznamu úloh (Modul pre KOrganizer) Comment[sr]=Прикључак листе обавеза (прикључак KOrganizer-а) Comment[sr@Latn]=Priključak liste obaveza (priključak KOrganizer-a) Comment[sv]=Uppgiftslistkomponent (Korganizer-insticksprogram) -Comment[tr]=Yapılacak İşler Bileşeni (KOrganizer eklentisi) Comment[zh_CN]=待办清单组件(KOrganizer 插件) Comment[zh_TW]=待辦事項清單組件(KOrganizer 外掛程式) Name=To-do @@ -45,21 +38,14 @@ Name[ca]=Pendents Name[da]=Gøremål Name[de]=Aufgaben Name[el]=Προς υλοποίηση εργασίες -Name[es]=Tareas pendientes Name[et]=Ülesanded -Name[fr]=Tâches -Name[is]=Verkefni Name[it]=Cose da fare Name[ja]=To-Do -Name[km]=​ការងារ​ត្រូវ​ធ្វើ Name[nds]=Opgaav Name[nl]=Takenlijst Name[pl]=Lista zadań -Name[ru]=Задачи -Name[sk]=Zoznam úloh Name[sr]=Обавезе Name[sr@Latn]=Obaveze Name[sv]=Uppgift -Name[tr]=Yapılacak Ögeleri Name[zh_CN]=待办清单 Name[zh_TW]=待辦事項 diff --git a/kontact/plugins/korganizer/todosummarywidget.cpp b/kontact/plugins/korganizer/todosummarywidget.cpp index e58ee6aa..953aa42c 100644 --- a/kontact/plugins/korganizer/todosummarywidget.cpp +++ b/kontact/plugins/korganizer/todosummarywidget.cpp @@ -67,7 +67,6 @@ TodoSummaryWidget::TodoSummaryWidget( TodoPlugin *plugin, mLayout->setRowStretch( 6, 1 ); mCalendar = KOrg::StdCalendar::self(); - mCalendar->load(); connect( mCalendar, TQT_SIGNAL( calendarChanged() ), TQT_SLOT( updateView() ) ); connect( mPlugin->core(), TQT_SIGNAL( dayChanged( const TQDate& ) ), @@ -169,7 +168,7 @@ void TodoSummaryWidget::updateView() connect( urlLabel, TQT_SIGNAL( rightClickedURL( const TQString& ) ), this, TQT_SLOT( popupMenu( const TQString& ) ) ); - TQString tipText( KCal::IncidenceFormatter::toolTipString( todo, true ) ); + TQString tipText( KCal::IncidenceFormatter::toolTipStr( mCalendar, todo, currentDate, true ) ); if ( !tipText.isEmpty() ) { TQToolTip::add( urlLabel, tipText ); } @@ -213,11 +212,11 @@ void TodoSummaryWidget::completeTodo( const TQString &uid ) { KCal::Todo *todo = mCalendar->todo( uid ); IncidenceChanger *changer = new IncidenceChanger( mCalendar, this ); - if ( !todo->isReadOnly() && changer->beginChange( todo ) ) { + if ( !todo->isReadOnly() && changer->beginChange( todo, 0, TQString() ) ) { KCal::Todo *oldTodo = todo->clone(); todo->setCompleted( TQDateTime::currentDateTime() ); - changer->changeIncidence( oldTodo, todo, KOGlobals::COMPLETION_MODIFIED ); - changer->endChange( todo ); + changer->changeIncidence( oldTodo, todo, KOGlobals::COMPLETION_MODIFIED, this ); + changer->endChange( todo, 0, TQString() ); delete oldTodo; updateView(); } diff --git a/kontact/plugins/kpilot/kpilotplugin.desktop b/kontact/plugins/kpilot/kpilotplugin.desktop index 7ad217fd..68f8d9d2 100644 --- a/kontact/plugins/kpilot/kpilotplugin.desktop +++ b/kontact/plugins/kpilot/kpilotplugin.desktop @@ -19,26 +19,21 @@ Comment=Palm Tools Component (KPilot Plugin) Comment[bg]=Приставка за KPilot Comment[ca]=Component d'eines de la Palm (endollable del KPilot) Comment[da]=Komponent til palm-værktøjer (KPilot-plugin) -Comment[de]=Palm-Komponente (KPilot-Modul) +Comment[de]=Palm Werkzeuge (KPilot-Modul) Comment[el]=Συστατικό εργαλείων Palm (Πρόσθετο του KPilot) -Comment[es]=Componente de herramientas de Palm (complemento KPilot) Comment[et]=Palmi tööriistade plugin (KPilot) -Comment[fr]=Composant d'outils pour Palms (Module KPilot) -Comment[is]=Palm verkfæraeining (KPilot íforrit) Comment[it]=Componente strumenti Palm (plugin KPilot) Comment[ja]=Palm ツールコンポーネント (KPilot プラグイン) -Comment[km]=សមាសភាគ​ឧបករណ៍ Palm (កម្មវិធី​ជំនួយ KPilot​) Comment[nds]=Palmreekner-Warktüüchkomponent (KPilot-Moduul) Comment[nl]=Component met hulpmiddelen voor PalmOS(tm)-apparaten (KPilot-plugin) Comment[pl]=Składnik narzędzi Palma (wtyczka KPilot) -Comment[ru]=Синхронизация с Palm (модуль KPilot) Comment[sr]=Компонента алата за Palm (прикључак KPilot-а) Comment[sr@Latn]=Komponenta alata za Palm (priključak KPilot-a) Comment[sv]=Palm Pilot-verktygskomponent (Kpilot-insticksprogram) -Comment[tr]=Palm Araçları Bileşeni (KPilot Eklentisi) Comment[zh_CN]=Palm 工具组件(KPilot 插件) Comment[zh_TW]=Palm 工具組件(KPilot 外掛程式) Name=Palm +Name[de]=KPilot-Einrichtung Name[nds]=Palmreekner Name[nl]=PalmOS(tm)-apparaat Name[sv]=Palm Pilot diff --git a/kontact/plugins/newsticker/kcmkontactknt.desktop b/kontact/plugins/newsticker/kcmkontactknt.desktop index d866e252..3e57621c 100644 --- a/kontact/plugins/newsticker/kcmkontactknt.desktop +++ b/kontact/plugins/newsticker/kcmkontactknt.desktop @@ -36,7 +36,6 @@ Name[id]=Ticker Berita Name[is]=Fréttastrimill Name[it]=Ticker notizie Name[ja]=ニュースティッカー -Name[ka]=სიახლეთა ტიკერი Name[kk]=Жаңалық таспасы Name[km]=កម្មវិធី​ទទួល​ព័ត៌មាន Name[lt]=News pranešėjas @@ -88,7 +87,6 @@ Comment[hu]=A hírmegjelenítő áttekintőjének beállításai Comment[is]=Uppsetning á yfirliti yfir fréttastrimla Comment[it]=Impostazioni sommario ticker notizie Comment[ja]=ニュースティッカーの設定 -Comment[ka]=სიახლეთა ტიკერის დაიჯესტის კონფიგურაცია Comment[kk]=Жаңалық таспасының тұжырымынын баптау Comment[km]=រៀបចំ​សេចក្ដី​សង្ខេប​កម្មវិធី​ទទួល​ព័ត៌មាន Comment[lt]=News Ticker santraukos nustatymai @@ -100,7 +98,7 @@ Comment[nn]=Oppsett av nyhendetelegrafsamandrag Comment[pl]=Ustawienia podsumowania wiadomości Comment[pt]=Configuração do Sumário do Extractor de Notícias Comment[pt_BR]=Configuração de Resumo de Notícias -Comment[ru]=Настройка сводки новостей +Comment[ru]=Настройка дайджеста новостей Comment[sk]=Nastavenie súhrnu správ Comment[sl]=Nastavitve povzetka novic Comment[sr]=Подешавање сажетка приказивања вести @@ -131,7 +129,6 @@ Keywords[hu]=hírmegjelenítő,konfigurálás,beállítások Keywords[is]=fréttastrimill, stillingar, stilla Keywords[it]=ticker notizie, configura, impostazioni Keywords[ja]=ニュースティッカー,設定,設定 -Keywords[ka]=სიახლეთა ტიკერი, კონფიგურაცია, პარამეტრები Keywords[km]=កម្មវិធី​ទទួល​ព័ត៌មាន,កំណត់​រចនាសម្ព័ន្ធ,ការ​កំណត់ Keywords[lt]=news ticker, configure, settings, konfigūravimas, nustatymai, naujienų pranešėjas Keywords[ms]=Pengetik berita, konfigur, seting diff --git a/kontact/plugins/newsticker/newstickerplugin.desktop b/kontact/plugins/newsticker/newstickerplugin.desktop index 18a34cda..079f9007 100644 --- a/kontact/plugins/newsticker/newstickerplugin.desktop +++ b/kontact/plugins/newsticker/newstickerplugin.desktop @@ -20,21 +20,15 @@ Comment[ca]=Component de teletip de notícies Comment[da]=Nyhedstelegraf-komponent Comment[de]=Newsticker-Komponente Comment[el]=Συστατικό προβολέα ειδήσεων -Comment[es]=Componente de teletipo de noticias Comment[et]=Uudistejälgija plugin -Comment[fr]=Composant Newsticker -Comment[is]=Fréttastrimilseining Comment[it]=Componente ticker notizie Comment[ja]=ニュースティッカーコンポーネント -Comment[km]=សមាសភាគ Newsticker Comment[nds]=Narichtentelegraaf-Komponent Comment[nl]=Nieuwstickercomponent Comment[pl]=Składnik paska wiadomości -Comment[ru]=Компонент новостей Comment[sr]=Компонента откуцавача вести Comment[sr@Latn]=Komponenta otkucavača vesti Comment[sv]=Nyhetsövervakningskomponent -Comment[tr]=Haber İzleyici Bileşeni Comment[zh_CN]=新闻点点通组件 Comment[zh_TW]=新聞顯示組件 Name=News @@ -48,7 +42,7 @@ Name[ca]=Notícies Name[cs]=Novinky Name[cy]=Newyddion Name[da]=Nyheder -Name[de]=Usenet +Name[de]=Newsticker Name[el]=Νέα Name[eo]=Novaĵoj Name[es]=Noticias @@ -65,7 +59,6 @@ Name[hi]=समाचार Name[hu]=Hírek Name[is]=Fréttir Name[ja]=ニュース -Name[ka]=სიახლეები Name[kk]=Жаңалықтар Name[km]=ព័ត៌មាន Name[lt]=Naujienos @@ -93,7 +86,6 @@ Name[tg]=Ахборот Name[th]=ข่าว Name[tr]=Haberler Name[uk]=Новини -Name[uz]=Yangiliklar -Name[uz@cyrillic]=Янгиликлар +Name[uz]=Янгиликлар Name[zh_CN]=新闻 Name[zh_TW]=新聞 diff --git a/kontact/plugins/specialdates/kcmsdsummary.desktop b/kontact/plugins/specialdates/kcmsdsummary.desktop index f294c383..6835ce8f 100644 --- a/kontact/plugins/specialdates/kcmsdsummary.desktop +++ b/kontact/plugins/specialdates/kcmsdsummary.desktop @@ -16,22 +16,15 @@ Name[ca]=Resum de dates especials Name[da]=Oversigt over særlige datoer Name[de]=Übersicht über besondere Termine Name[el]=Επισκόπηση σημαντικών ημερομηνιών -Name[es]=Resumen de fechas especiales Name[et]=Tähtpäevade ülevaade -Name[fr]=Aperçu des dates importantes -Name[is]=Yfirlit sérstakra daga Name[it]=Panoramica delle date speciali Name[ja]=特別な日の要約 -Name[km]=ទិដ្ឋភាព​កាលបរិច្ឆេទ​ពិសេស Name[nds]=Översicht besünner Daten Name[nl]=Overzicht van speciale data Name[pl]=Daty specjalne -Name[pt_BR]=Resumo de Datas Especiais -Name[ru]=Сводка особых дат Name[sr]=Преглед посебних датума Name[sr@Latn]=Pregled posebnih datuma Name[sv]=Översikt av speciella datum -Name[tr]=Özel Tarihlere Genel Bakış Name[zh_CN]=特殊日期概览 Name[zh_TW]=特殊日期概觀 Comment=Special Dates Summary Setup @@ -55,7 +48,6 @@ Comment[hu]=A fontos dátumok áttekintőjének beállításai Comment[is]=Yfirlitsuppsetning sérstakra daga Comment[it]=Impostazioni per le date speciali Comment[ja]=特別な日の要約設定 -Comment[ka]=განსაკუთრებულ თარიღთა დაიჯესტის კონფიგურაცია Comment[kk]=Ерекше күндер тұжырымының баптауы Comment[km]=រៀបចំ​សេចក្ដី​សង្ខេប​ថ្ងៃ​ពិសេស Comment[lt]=Ypatingų datų santraukos sąranka @@ -102,7 +94,6 @@ Keywords[hu]=születésnap,évforduló,szabadság,konfigurálás,beállítások Keywords[is]=afmæli, frídagar, stillingar, stilla Keywords[it]=compleanno, anniversario, vacanze, configura, impostazioni Keywords[ja]=誕生日,記念日,休日,設定,設定 -Keywords[ka]=დაბადების დღე,სახელობის დღე,დასვენების დღე,კონფიგურაცია,პარამეტრები Keywords[km]=ថ្ងៃ​ខួប​កំណើត,បុណ្យ​ខួប,វិស្សមកាល,កំណត់​រចនាសម្ព័ន្ធ,ការ​កំណត់ Keywords[lt]=birthday, anniversary, holiday, configure, settings, konfigūruoti, nustatymai, gimtadieniai, išeiginės,sukaktys Keywords[mk]=birthday, anniversary, holiday, configure, settings, роденден, годишнина, конфигурирање, конфигурација, поставувања diff --git a/kontact/plugins/specialdates/sdsummarywidget.cpp b/kontact/plugins/specialdates/sdsummarywidget.cpp index 44c67155..2967e889 100644 --- a/kontact/plugins/specialdates/sdsummarywidget.cpp +++ b/kontact/plugins/specialdates/sdsummarywidget.cpp @@ -127,7 +127,6 @@ SDSummaryWidget::SDSummaryWidget( Kontact::Plugin *plugin, TQWidget *parent, manager->setStandardResource( defaultResource ); } mCalendar = KOrg::StdCalendar::self(); - mCalendar->load(); connect( mCalendar, TQT_SIGNAL( calendarChanged() ), this, TQT_SLOT( updateView() ) ); @@ -220,6 +219,8 @@ void SDSummaryWidget::updateView() mLabels.clear(); mLabels.setAutoDelete( false ); + KIconLoader loader( "kdepim" ); + KABC::StdAddressBook *ab = KABC::StdAddressBook::self( true ); TQValueList dates; TQLabel *label = 0; @@ -391,9 +392,9 @@ void SDSummaryWidget::updateView() TQImage icon_img; TQString icon_name; KABC::Picture pic; - switch( (*addrIt).category ) { // TODO: better icons + switch( (*addrIt).category ) { case CategoryBirthday: - icon_name = "cookie"; + icon_name = "calendarbirthday"; pic = (*addrIt).addressee.photo(); if ( pic.isIntern() && !pic.data().isNull() ) { TQImage img = pic.data(); @@ -405,7 +406,7 @@ void SDSummaryWidget::updateView() } break; case CategoryAnniversary: - icon_name = "kdmconfig"; + icon_name = "calendaranniversary"; pic = (*addrIt).addressee.photo(); if ( pic.isIntern() && !pic.data().isNull() ) { TQImage img = pic.data(); @@ -417,7 +418,7 @@ void SDSummaryWidget::updateView() } break; case CategoryHoliday: - icon_name = "kdmconfig"; break; + icon_name = "calendarholiday"; break; case CategoryOther: icon_name = "cookie"; break; } @@ -438,8 +439,7 @@ void SDSummaryWidget::updateView() //Muck with the year -- change to the year 'daysTo' days away int year = currentDate.addDays( (*addrIt).daysTo ).year(); - TQDate sD = TQDate::TQDate( year, - (*addrIt).date.month(), (*addrIt).date.day() ); + TQDate sD = TQDate( year, (*addrIt).date.month(), (*addrIt).date.day() ); if ( (*addrIt).daysTo == 0 ) { datestr = i18n( "Today" ); diff --git a/kontact/plugins/specialdates/sdsummarywidget.h b/kontact/plugins/specialdates/sdsummarywidget.h index 6ef29eff..5c52c77c 100644 --- a/kontact/plugins/specialdates/sdsummarywidget.h +++ b/kontact/plugins/specialdates/sdsummarywidget.h @@ -78,7 +78,7 @@ class SDSummaryWidget : public Kontact::Summary bool mShowHolidays; bool mShowSpecialsFromCal; - KHolidays::KHolidays *mHolidays; + KHolidays *mHolidays; }; #endif diff --git a/kontact/plugins/specialdates/specialdates_plugin.cpp b/kontact/plugins/specialdates/specialdates_plugin.cpp index 9f78de3f..c8a816c2 100644 --- a/kontact/plugins/specialdates/specialdates_plugin.cpp +++ b/kontact/plugins/specialdates/specialdates_plugin.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -41,6 +42,7 @@ SpecialdatesPlugin::SpecialdatesPlugin( Kontact::Core *core, const char *name, c mAboutData( 0 ) { setInstance( SpecialdatesPluginFactory::instance() ); + instance()->iconLoader()->addAppDir( "kdepim" ); } SpecialdatesPlugin::~SpecialdatesPlugin() diff --git a/kontact/plugins/specialdates/specialdatesplugin.desktop b/kontact/plugins/specialdates/specialdatesplugin.desktop index bceb13ad..cb6a691c 100644 --- a/kontact/plugins/specialdates/specialdatesplugin.desktop +++ b/kontact/plugins/specialdates/specialdatesplugin.desktop @@ -29,7 +29,7 @@ Name[et]=Tähtpäevad Name[eu]=Data bereziak Name[fa]=تاریخهای ویژه Name[fi]=Erikoispäivät -Name[fr]=Dates importantes +Name[fr]=Dates particulières Name[fy]=Spesjale datums Name[ga]=Dátaí Speisialta Name[gl]=Datas Especiais @@ -38,7 +38,6 @@ Name[hu]=Fontos dátumok Name[is]=Sérstakir dagar Name[it]=Date speciali Name[ja]=特別な日 -Name[ka]=განსაკუტრებული თარიღები Name[kk]=Ерекше күндер Name[km]=ថ្ងៃ​ពិសេស Name[lt]=Ypatingos datos @@ -62,8 +61,7 @@ Name[ta]=விசேஷ தேதிகள் Name[th]=วันพิเศษ Name[tr]=Özel Tarihler Name[uk]=Особливі дати -Name[uz]=Maxsus kunlar -Name[uz@cyrillic]=Махсус кунлар +Name[uz]=Махсус кунлар Name[zh_CN]=特殊日期 Name[zh_TW]=特殊日期 Comment=Special Dates Component @@ -72,21 +70,14 @@ Comment[ca]=Component de dates especials Comment[da]=Komponent til særlige datoer Comment[de]=Komponente für Übersicht über besondere Termine Comment[el]=Συστατικό σημαντικών ημερομηνιών -Comment[es]=Componente de fechas especiales Comment[et]=Tähtpäevade plugin -Comment[fr]=Composant des dates importantes -Comment[is]=Eining fyrir sérstaka daga Comment[it]=Componente per le date speciali Comment[ja]=特別な日コンポーネント -Comment[km]=សមាសភាគ​កាលបរិច្ឆេទ​ពិសេស Comment[nds]=Komponent för besünner Daten Comment[nl]=Component voor overzicht van speciale data Comment[pl]=Składnik dat specjalnych -Comment[pt_BR]=Componente de Datas Especiais -Comment[ru]=Особые даты Comment[sr]=Компонента посебних датума Comment[sr@Latn]=Komponenta posebnih datuma Comment[sv]=Speciella datumkomponent -Comment[tr]=Özel Tarihler Bileşeni Comment[zh_CN]=特殊日期组件 Comment[zh_TW]=特殊日期組件 diff --git a/kontact/plugins/summary/kcmkontactsummary.cpp b/kontact/plugins/summary/kcmkontactsummary.cpp index cf815416..f21a2bb3 100644 --- a/kontact/plugins/summary/kcmkontactsummary.cpp +++ b/kontact/plugins/summary/kcmkontactsummary.cpp @@ -142,8 +142,10 @@ void KCMKontactSummary::load() mPluginList = KPluginInfo::fromServices( offers, &config, "Plugins" ); KPluginInfo::List::Iterator it; + KConfig *conf = new KConfig("kontactrc"); + KConfigGroup *cg = new KConfigGroup( conf, "Plugins" ); for ( it = mPluginList.begin(); it != mPluginList.end(); ++it ) { - (*it)->load(); + (*it)->load( cg ); if ( !(*it)->isPluginEnabled() ) continue; diff --git a/kontact/plugins/summary/kcmkontactsummary.desktop b/kontact/plugins/summary/kcmkontactsummary.desktop index 5442d5c0..b4100a59 100644 --- a/kontact/plugins/summary/kcmkontactsummary.desktop +++ b/kontact/plugins/summary/kcmkontactsummary.desktop @@ -13,23 +13,17 @@ X-KDE-CfgDlgHierarchy=KontactSummary Name=Summary View Items Name[bg]=Обобщение Name[ca]=Vista resum d'elements -Name[da]=Elementer i opsummeringsvisning -Name[de]=Elemente der Zusammenfassungsansicht +Name[de]=Übersichtseinträge Name[el]=Αντικείμενα προβολής σύνοψης -Name[es]=Elementos de la vista de sumario Name[et]=Kokkuvõttevaate elemendid Name[it]=Elementi vista sommario Name[ja]=要約ビューの項目 -Name[km]=ធាតុ​ទិដ្ឋភាព​សង្ខេប Name[nds]=Översicht-Indrääg Name[nl]=Overzichtsweergave-items -Name[pl]=Elementy widoku podsumowania Name[sr]=Ставке приказа сажетка Name[sr@Latn]=Stavke prikaza sažetka Name[sv]=Objekt i översiktsvy -Name[tr]=Özet Görünüm Ögeleri Name[zh_CN]=摘要视图项目 -Name[zh_TW]=摘要檢視項目 Comment=General Configuration of Kontact's Summary View Comment[af]=Algemene opstelling van Kontact se opsomming aansig Comment[bg]=Настройка на обобщението @@ -51,7 +45,6 @@ Comment[hu]=A Kontact áttekintő nézetének beállításai Comment[is]=Almennar stillingar fyrir Kontact yfirlitssýn Comment[it]=Configurazione generale della vista sommario di Kontact Comment[ja]=Kontact の要約表示の一般的な設定 -Comment[ka]=Kontact დაიჯესტის ხედის ზოგადი პარამეტრები Comment[kk]=Тұжырымдаманың жалпы параметрлері Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ​ទូទៅ​នៃ​ទិដ្ឋភាព​សង្ខេប​របស់ Kontact Comment[lt]=Kontact Santraukos vaizdo bendrasis konfigūravimas @@ -65,7 +58,7 @@ Comment[nn]=Generelt oppsett av samandragsvisinga i Kontact Comment[pl]=Ogólna konfiguracja widoku podsumowania w Kontact Comment[pt]=Configuração Geral da Vista Sumária do Kontact Comment[pt_BR]=Configuração Geral da Visão de Resumo do Kontact -Comment[ru]=Настройка сводок Kontact +Comment[ru]=Общие настройки дайджеста Kontact Comment[sk]=Všeobecné nastavenie súhrnu Kontact Comment[sl]=Splošne nastavitve za prikaz povzetka v Kontract Comment[sr]=Опште подешавање Kontact-овог приказа сажетка diff --git a/kontact/plugins/summary/summaryplugin.desktop b/kontact/plugins/summary/summaryplugin.desktop index d2aa1c95..79f8119d 100644 --- a/kontact/plugins/summary/summaryplugin.desktop +++ b/kontact/plugins/summary/summaryplugin.desktop @@ -15,23 +15,17 @@ Comment=Summary View Component Comment[bg]=Обобщение Comment[ca]=Component de vista resum Comment[da]=Komponent for opsummeringsvisning -Comment[de]=Zusammenfassungsansicht-Komponente +Comment[de]=Übersichtskomponente Comment[el]=Συστατικό προβολής σύνοψης -Comment[es]=Componente Vista de resumen Comment[et]=Kokkuvõttevaate plugin -Comment[fr]=Composant de la vue résumée -Comment[is]=Eining fyrir yfirlitssýn Comment[it]=Componente vista sommario Comment[ja]=要約ビューコンポーネント -Comment[km]=សមាសភាគ​ទិដ្ឋភាព​សង្ខេប Comment[nds]=Översicht-Komponent Comment[nl]=Overzichtsweergaveitem -Comment[pl]=Składnik widoku podsumowania -Comment[ru]=Просмотр сводок +Comment[pl]=Składnik podsumowania Comment[sr]=Компонента приказа сажетка Comment[sr@Latn]=Komponenta prikaza sažetka Comment[sv]=Översiktsvykomponent -Comment[tr]=Özet Görünüm Bileşeni Comment[zh_CN]=摘要视图组件 Comment[zh_TW]=摘要檢視組件 Name=Summary @@ -62,7 +56,6 @@ Name[hu]=Áttekintő Name[is]=Yfirlit Name[it]=Sommario Name[ja]=要約 -Name[ka]=დაიჯესტი Name[kk]=Тұжырым Name[km]=សង្ខេប Name[lt]=Santrauka @@ -77,7 +70,7 @@ Name[pl]=Podsumowanie Name[pt]=Resumo Name[pt_BR]=Resumo Name[ro]=Sumar -Name[ru]=Сводки +Name[ru]=Дайджест Name[se]=Čoahkkáigeassu Name[sk]=Súhrn Name[sl]=Povzetek @@ -89,8 +82,7 @@ Name[tg]=Дайджест Name[th]=หน้าสรุป Name[tr]=Özet Name[uk]=Зведення -Name[uz]=Hisobot -Name[uz@cyrillic]=Ҳисобот +Name[uz]=Ҳисобот Name[zh_CN]=概览 Name[zh_TW]=摘要 #Always last diff --git a/kontact/plugins/summary/summaryview_plugin.cpp b/kontact/plugins/summary/summaryview_plugin.cpp index 9f907380..555d7954 100644 --- a/kontact/plugins/summary/summaryview_plugin.cpp +++ b/kontact/plugins/summary/summaryview_plugin.cpp @@ -39,8 +39,8 @@ SummaryView::SummaryView( Kontact::Core *core, const char *name, const TQStringL { setInstance( SummaryViewFactory::instance() ); - mSyncAction = new KSelectAction( i18n( "Synchronize All" ), "reload", 0, this, - TQT_SLOT( doSync() ), actionCollection(), + mSyncAction = new KSelectAction( i18n( "Synchronize All" ), "reload", 0, 0, + 0, actionCollection(), "kontact_summary_sync" ); connect( mSyncAction, TQT_SIGNAL( activated( const TQString& ) ), this, TQT_SLOT( syncAccount( const TQString& ) ) ); connect( mSyncAction->popupMenu(), TQT_SIGNAL( aboutToShow() ), this, TQT_SLOT( fillSyncActionSubEntries() ) ); @@ -68,9 +68,12 @@ void SummaryView::fillSyncActionSubEntries() void SummaryView::syncAccount( const TQString& account ) { - const TQString acc = account == i18n("All") ? TQString() : account; - DCOPRef ref( "kmail", "KMailIface" ); - ref.send( "checkAccount", acc ); + if ( account == i18n("All") ) { + doSync(); + } else { + DCOPRef ref( "kmail", "KMailIface" ); + ref.send( "checkAccount", account ); + } fillSyncActionSubEntries(); } diff --git a/kontact/plugins/test/kptestplugin.desktop b/kontact/plugins/test/kptestplugin.desktop index 17a3e52e..75e0988e 100644 --- a/kontact/plugins/test/kptestplugin.desktop +++ b/kontact/plugins/test/kptestplugin.desktop @@ -35,7 +35,6 @@ Comment[hu]=Kontact tesztmodul Comment[is]=Kontact prufu íforrit Comment[it]=Plugin test Kontact Comment[ja]=Kontact テストプラグイン -Comment[ka]=Kontact სატესტო მოდული Comment[kk]=Сынақ модулі Comment[km]=កម្មវិធី​ជំនួយ​ការ​សាកល្បង​ក្នុង Kontact Comment[lt]=Kontact bandymo priedas @@ -60,8 +59,7 @@ Comment[ta]=பரிசோதனை சொருகுப்பொருளை Comment[tg]=Модули матнии Kontact Comment[tr]=Kontact Test Eklentisi Comment[uk]=Тестовий втулок Kontact -Comment[uz]=Kontact uchun sinash plagini -Comment[uz@cyrillic]=Kontact учун синаш плагини +Comment[uz]=Kontact учун синаш плагини Comment[zh_CN]=Kontact Test 插件 Comment[zh_TW]=Kontact 測試外掛程式 Name=TestPlugin @@ -83,7 +81,6 @@ Name[hu]=Tesztmodul Name[is]=Prufu íforrit Name[it]=Plugin Test Name[ja]=テストプラグイン -Name[ka]=სატესტო მოდული Name[kk]=Сынақ модулі Name[km]=កម្មវិធី​ជំនួយ​ការ​សាកល្បង Name[mk]=Приклучок за тест diff --git a/kontact/plugins/weather/weatherplugin.desktop b/kontact/plugins/weather/weatherplugin.desktop index 68c1924e..80d9be8e 100644 --- a/kontact/plugins/weather/weatherplugin.desktop +++ b/kontact/plugins/weather/weatherplugin.desktop @@ -21,21 +21,15 @@ Comment[ca]=Component del temps del Kontact Comment[da]=Vejrkomponent til Kontact Comment[de]=Wetter-Komponente für Kontact Comment[el]=Συστατικό καιρού του Kontact -Comment[es]=Extensión de meteorología para Kontact Comment[et]=Kontacti ilmaplugin -Comment[fr]=Composant météo pour Kontact -Comment[is]=Kontact veðurfréttaeining Comment[it]=Componente meteorologico di Kontact Comment[ja]=Kontact 気象情報コンポーネント -Comment[km]=សមាសភាគ​អាកាសធាតុ Kontact Comment[nds]=Kontact-Wederkomponent Comment[nl]=Kontact Weercomponent Comment[pl]=Składnik Kontaktu wiadomości o pogodzie -Comment[ru]=Компонент информации о погоде для Kontact Comment[sr]=Компонента времена за Kontact Comment[sr@Latn]=Komponenta vremena za Kontact Comment[sv]=Kontacts väderkomponent -Comment[tr]=Kontact Hava Durumu Bileşeni Comment[zh_CN]=Kontact 天气插件 Comment[zh_TW]=Kontact 天氣組件 Name=Weather Service @@ -64,7 +58,6 @@ Name[hu]=Időjárás Name[is]=Veðurþjónusta Name[it]=Servizio meteorologico Name[ja]=気象サービス -Name[ka]=ამინდის მომსახურება Name[kk]=Ауа райы қызметі Name[km]=សេវា​អាកាសធាតុ Name[lt]=Orų tarnyba @@ -86,7 +79,6 @@ Name[sv]=Väderleksprognos Name[th]=รายงานอากาศ Name[tr]=Hava Durumu Servisi Name[uk]=Служба погоди -Name[uz]=Ob-havo xizmati -Name[uz@cyrillic]=Об-ҳаво хизмати +Name[uz]=Об-ҳаво хизмати Name[zh_CN]=天气服务 Name[zh_TW]=天氣服務 diff --git a/kontact/src/Kontact.desktop b/kontact/src/Kontact.desktop index 6cd9d0d3..a4127aac 100644 --- a/kontact/src/Kontact.desktop +++ b/kontact/src/Kontact.desktop @@ -36,7 +36,6 @@ GenericName[hu]=Információkezelő GenericName[is]=Persónulegur upplýsingastjórnandi GenericName[it]=Gestione informazioni personali GenericName[ja]=個人情報マネージャ -GenericName[ka]=პირადი ინფორმაციის მმართველი GenericName[kk]=Дербес Ақпарат Менеджері GenericName[km]=កម្មវិធី​គ្រប់គ្រង​ព័ត៌មាន​ផ្ទាល់​ខ្លួន GenericName[lt]=Asmeninės informacijos tvarkyklė @@ -61,8 +60,7 @@ GenericName[ta]=அந்தரங்க தகவல் மேலாளர் GenericName[tg]=Мудири маълумоти шахсӣ GenericName[tr]=Kişisel Bilgi Yöneticisi GenericName[uk]=Менеджер особистої інформації -GenericName[uz]=Shaxsiy maʼlumot boshqaruvchisi -GenericName[uz@cyrillic]=Шахсий маълумот бошқарувчиси +GenericName[uz]=Шахсий маълумот бошқарувчиси GenericName[zh_CN]=个人信息管理器 GenericName[zh_TW]=個人資訊管理者 Terminal=false diff --git a/kontact/src/about/kontact.css b/kontact/src/about/kontact.css index 18aa0ddc..2366a974 100644 --- a/kontact/src/about/kontact.css +++ b/kontact/src/about/kontact.css @@ -15,13 +15,6 @@ right: 125px; } -#boxCenter { - background-image: url(box-center-kontact.png); - background-repeat: no-repeat; - background-color: #dfe7f3; - background-position: bottom right; -} - #subtext { font-style: italic; } diff --git a/kontact/src/iconsidepane.cpp b/kontact/src/iconsidepane.cpp index d56513ae..4303175f 100644 --- a/kontact/src/iconsidepane.cpp +++ b/kontact/src/iconsidepane.cpp @@ -113,8 +113,8 @@ void EntryItem::reloadPixmap() if ( size != 0 ) mPixmap = KGlobal::iconLoader()->loadIcon( mPlugin->icon(), KIcon::Desktop, size, - mPlugin->disabled() ? - KIcon::DisabledState + mPlugin->disabled() ? + KIcon::DisabledState : KIcon::DefaultState); else mPixmap = TQPixmap(); @@ -250,7 +250,7 @@ void EntryItem::setPaintActive( bool paintActive ) mPaintActive = paintActive; } -Navigator::Navigator( SidePaneBase *parent, const char *name ) +Navigator::Navigator( IconSidePane *parent, const char *name ) : KListBox( parent, name ), mSidePane( parent ), mShowIcons( true ), mShowText( true ) { @@ -328,7 +328,6 @@ void Navigator::updatePlugins( TQValueList plugins_ ) mActions.clear(); mActions.setAutoDelete( false ); - int counter = 0; int minWidth = 0; qBubbleSort( plugins ); TQValueList::ConstIterator end = plugins.end(); @@ -343,14 +342,6 @@ void Navigator::updatePlugins( TQValueList plugins_ ) if ( item->width( this ) > minWidth ) minWidth = item->width( this ); - - TQString name = TQString( "CTRL+%1" ).arg( counter + 1 ); - KAction *action = new KAction( plugin->title(), plugin->icon(), KShortcut( name ), - mMapper, TQT_SLOT( map() ), - mSidePane->actionCollection(), name.latin1() ); - mActions.append( action ); - mMapper->setMapping( action, counter ); - counter++; } parentWidget()->setFixedWidth( minWidth ); diff --git a/kontact/src/iconsidepane.h b/kontact/src/iconsidepane.h index 03f838c9..674980c0 100644 --- a/kontact/src/iconsidepane.h +++ b/kontact/src/iconsidepane.h @@ -37,6 +37,7 @@ namespace Kontact { class Core; +class IconSidePane; class Plugin; class Navigator; @@ -118,7 +119,7 @@ class Navigator : public KListBox { Q_OBJECT public: - Navigator( SidePaneBase *parent = 0, const char *name = 0 ); + Navigator( IconSidePane *parent = 0, const char *name = 0 ); virtual void setSelected( TQListBoxItem *, bool ); @@ -156,7 +157,7 @@ class Navigator : public KListBox void slotStopHighlight(); private: - SidePaneBase *mSidePane; + IconSidePane *mSidePane; IconViewMode mViewMode; TQListBoxItem* mMouseOn; diff --git a/kontact/src/kontact.setdlg b/kontact/src/kontact.setdlg index a90778a2..872457ad 100644 --- a/kontact/src/kontact.setdlg +++ b/kontact/src/kontact.setdlg @@ -5,21 +5,15 @@ Name[ca]=Vista de resum Name[da]=Opsummeringsvisning Name[de]=Übersicht Name[el]=Προβολή σύνοψης -Name[es]=Vista de resumen Name[et]=Kokkuvõttevaade -Name[fr]=Vue résumée -Name[is]=Yfirlitssýn Name[it]=Vista sommario Name[ja]=要約ビュー -Name[km]=ទិដ្ឋភាព​សង្ខេប Name[nds]=Översicht Name[nl]=Overzichtsweergave Name[pl]=Podsumowanie -Name[ru]=Просмотр сводок Name[sr]=Приказ сажетка Name[sr@Latn]=Prikaz sažetka Name[sv]=Översiktsvy -Name[tr]=Özet Görünümü Name[zh_CN]=摘要视图 Name[zh_TW]=摘要檢視 Comment=Configuration of Kontact's Summary View. Some plugins provide Summary View items, choose the ones you would like to list. @@ -28,17 +22,12 @@ Comment[ca]=Configuració de la vista de resum del Kontact. Alguns endoll Comment[da]=Konfiguration af Kontacts Opsummeringsvisning. Nogle plugins giver Opsummeringsvisning-elementer, vælg dem du ønsker på listen. Comment[de]=Einrichtung der Zusammenfassungsansicht von Kontact. Einige Kontact-Module stellen Elemente für die Zusammenfassungsansicht zur Verfügung. Wählen Sie hier, welche Elemente angezeigt werden sollen. Comment[el]=Ρύθμιση της Προβολής σύνοψης του Kontact. Κάποια πρόσθετα παρέχουν αντικείμενα Προβολή σύνοψης· επιλέξτε αυτά που θέλετε να εμφανίζονται. -Comment[es]=Configuración de Kontact Vista de resumen. Algunos complementos proveen elementos para la Vista de resumen, elija los que quiera listar. Comment[et]=Kontacti kokkuvõttevaate seadistamine. Mõned pluginad pakuvad kokkuvõttevaate elemente. Vali nimekirjas need, mida soovid näha. -Comment[fr]=Configuration de Kontact Vue résumée. Certains modules fournissent des éléments de Vue Résumée, choisissez ceux que vous voulez voir listés. -Comment[is]=Stillingar á Yfirlitssýní Kontact. Sum íforrit koma með hluti í Yfirlitssýn, veldu þá sem þú vilt hafa á síðunni. Comment[it]=Configurazione della vista sommario di Kontact. Alcuni plugin forniscono elementi di vista sommario, scegli quelli che desideri vedere nell'elenco. Comment[ja]=Kontact の要約ビューの設定。要約ビューの項目を提供するプラグインがいくつかあります。要約ビューに表示させる項目を選択してください。 -Comment[km]=កា​រកំណត់​រចនាសម្ព័ន្ធ​របស់ Kontact ទិដ្ឋភាព​សង្ខេប ។ កម្មវិធី​ជំនួយ​មួយ​ចំនួនផ្ដល់​នូវ​ធាតុទិដ្ឋភាព​សង្ខេប ជ្រើស​ទិដ្ឋភាព​មួយ​ក្នុង​ចំណោម​ទិដ្ឋភាព​ទាំងនេះ​ដែល​អ្នក​ចង់​រាយ ។ Comment[nds]=Kontact sien Översicht instellen. En Reeg Modulen stellt Indrääg för de Översicht praat. Hier kannst Du de utsöken, de Du hebben wullt. Comment[nl]=Configuratie van Kontacts Overzichtsweergave. Sommige plugins bieden ook items aan voor de Overzichtweergave, kies welke u wilt zien. Comment[pl]=Konfiguracja Podsumowania Kontaktu. Niektóre wtyczki zapewniają elementy Widok podsumowania, wybierz te które chcesz zobaczyć. -Comment[ru]=Настройка показа сводок различных компонентов. Comment[sr]=Подешавање Kontact-овог приказа сажетка. Неки прикључци дају ставке приказа сажетка, изаберите оне које желите. Comment[sr@Latn]=Podešavanje Kontact-ovog prikaza sažetka. Neki priključci daju stavke prikaza sažetka, izaberite one koje želite. Comment[sv]=Inställning av Kontacts översiktsvy. Vissa insticksprogram tillhandahåller objekt för översiktsvyn. Välj de du skulle vilja visa. @@ -52,24 +41,17 @@ Name=E-Mail Name[bg]=Е-поща Name[ca]=Correu Name[da]=E-mail +Name[de]=E-Mail Name[el]=Αλληλογραφία -Name[es]=Correo electrónico Name[et]=E-post -Name[fr]=Courriel -Name[is]=Tölvupóstur Name[it]=Posta elettronica Name[ja]=メール -Name[km]=អ៊ីមែល Name[nds]=Nettpost Name[nl]=E-mail Name[pl]=E-mail -Name[pt_BR]=E-mail -Name[ru]=Электронная почта -Name[sk]=Pošta Name[sr]=Е-пошта Name[sr@Latn]=E-pošta Name[sv]=E-post -Name[tr]=E-Posta Name[zh_CN]=邮件 Name[zh_TW]=電子郵件 Comment=Configuration of Kontact's E-Mail Plugin KMail, includes a Summary View Item and represents a Kontact Component. @@ -78,16 +60,11 @@ Comment[ca]=Configuració de l'endollable de correu KMail del Kontact, in Comment[da]=Konfiguration af Kontacts e-mail-plugin KMail, inkluderer et Opsummeringsvisnings-element og repræsenterer en Kontact-komponent. Comment[de]=Einrichtung des E-Mail-Moduls KMail für Kontact. Die E-Mail-Komponente kann als Kontact-Komponente in die Zusammenfassungsansicht integriert werden. Comment[el]=Η ρύθμιση του προσθέτου αλληλογραφίας KMail του Kontact, περιέχει ένα Αντικείμενο προβολής σύνοψης και αντιπροσωπεύει ένα Συστατικό του Kontact. -Comment[es]=Configuración del complemento de correo-e de Kontact KMail, incluye una Vista de resumen y representa un Componente de Kontact. Comment[et]=Kontacti e-posti plugina KMaili seadistamine, mis sisaldab kokkuvõttevaate elementi. -Comment[fr]=Configuration du Module de Courriel de Kontact KMail, inclut un Élément de Vue Résumée et correspond à un Composant de Kontact. -Comment[is]=Stillingar fyrir tölvupóstíforrit Kontact KMail, inniheldur yfirlitssýnarhlut sem stendur fyrir Kontact einingu. Comment[it]=Configurazione del plugin di posta elettronica KMail di Kontact, include una vista sommario e rappresenta un componente Kontact. -Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ​របស់​កម្មវិធី​ជំនួយ​​អ៊ីមែល​របស់ Kontact​ KMail រួម​មានធាតុ​ទិដ្ឋភាព​សង្ខេប និង​បង្ហាញ សមាសភាគ Kontact​ ។ Comment[nds]=Kontact sien Nettpost-Moduul KMail instellen. Stellt en Översicht-Indrag praat un is en Kontact-Komponent. Comment[nl]=Instellingen voor Kontacts e-mailplugin KMail. Bevat een Overzichtsweergaveplugin en een Kontact-component. Comment[pl]=Konfiguracja wtyczki poczty Kontaktu KMail, zawiera element Podsumowanie i jest Składnikiem Kontaktu. -Comment[ru]=Настройка модуля электронной почты KMail для показа в виде сводки. Comment[sr]=Подешавање Kontact-овог прикључка за е-пошту преко KMail-, укључујући приказ сажетка, и дат као компонента Kontact-а. Comment[sr@Latn]=Podešavanje Kontact-ovog priključka za e-poštu preko KMail-, uključujući prikaz sažetka, i dat kao komponenta Kontact-a. Comment[sv]=Inställning av Kontacts e-postinsticksprogram Kmail, omfattar ett objekt för översiktsvyn och representerar en Kontactkomponent. @@ -125,7 +102,6 @@ Name[hu]=Névjegyek Name[is]=Tengiliðir Name[it]=Contatti Name[ja]=コンタクト -Name[ka]=კონტაქტები Name[kk]=Контакттар Name[km]=ទំនាក់ទំនង Name[lt]=Kontaktai @@ -151,10 +127,9 @@ Name[sv]=Kontakter Name[ta]=தொடர்புகள் Name[tg]=Алоқот Name[th]=ที่อยู่ติดต่อ -Name[tr]=Kişiler +Name[tr]=Bağlantılar Name[uk]=Контакти -Name[uz]=Aloqalar -Name[uz@cyrillic]=Алоқалар +Name[uz]=Алоқалар Name[zh_CN]=联系人 Name[zh_TW]=聯絡人 Comment=Configuration of Kontact's Adress Book Plugin KAdressbook which represents a Kontact Component. @@ -163,16 +138,11 @@ Comment[ca]=Configuració de l'endollable de la llibreta d'adreces KAdressboo Comment[da]=Konfiguration af Kontacts adressebog-plugin KAdressbook som repræsenterer en Kontact-komponent. Comment[de]=Einrichtung des Adressbuch-Moduls für Kontact, welches eine Kontact-Komponente repräsentiert. Comment[el]=Ρύθμιση του πρόσθετου βιβλίου διευθύνσεων KAdressbook του Kontact που αντιπροσωπεύει ένα Συστατικό του Kontact. -Comment[es]=Configuración del complemento de libreta de direcciones de Kontact KAddressbok, el cual representa un Componente de Kontact. Comment[et]=Kontacti aadressiraamatu plugina KAdressbook seaditamine. -Comment[fr]=Configuration du Module de Carnet d'Adresses de Kontact KAdressbook qui correspond à un Composant de Kontact. -Comment[is]=Stillingar fyrir vistfangaskráríforrit Kontact KAdressbook, inniheldur yfirlitssýnarhlut sem stendur fyrir Kontact einingu. Comment[it]=Configurazione del plugin rubrica di KAddressbook che rappresenta un componente Kontact. -Comment[km]=កា​រកំណត់​រចនាសម្ព័ន្ធ​របស់​កម្មវិធី​ជំនួយ​សៀវភៅ​អាសយដ្ឋាន​របស់ Kontact KAdressbook ដែល​បង្ហាញ សមាសភាគ Kontact​ ។ Comment[nds]=Kontact sien Adressbook-Moduul KAddressbook instellende, dat en Kontact-Komponent is. Comment[nl]=Instellingen voor Kontacts adresboekplugin KMail. Bevat een Kontact-component. Comment[pl]=Konfiguracja wtyczki książki adresowej Kontaktu KAddressBook, która jest Składnikiem Kontaktu. -Comment[ru]=Настройка модуля адресной книги KAdressbook для показа в виде сводки. Comment[sr]=Подешавање Kontact-овог прикључка за адресар преко KAdressbook у облику компоненте Kontact-а. Comment[sr@Latn]=Podešavanje Kontact-ovog priključka za adresar preko KAdressbook u obliku komponente Kontact-a. Comment[sv]=Inställning av Kontacts adressboksinsticksprogram Kaddressbook, som representerar en Kontactkomponent. @@ -205,7 +175,6 @@ Name[hu]=A fontos dátumok áttekintője Name[is]=Yfirlit sérstakra daga Name[it]=Sommario date speciali Name[ja]=特別な日の要約 -Name[ka]=განსაკუთრებულ თარიღთა დაიჯესტი Name[kk]=Ерекше күндер тұжырымы Name[km]=សង្ខេប​ថ្ងៃ​ពិសេស Name[lt]=Ypatingų dienų santrauka @@ -219,7 +188,7 @@ Name[nn]=Samandrag for spesielle datoar Name[pl]=Podsumowanie dat specjalnych Name[pt]=Sumário de Datas Especiais Name[pt_BR]=Resumo de Datas Especiais -Name[ru]=Сводка особых дат +Name[ru]=Дайджест особых дат Name[sk]=Súhrn špeciálnych dátumov Name[sl]=Povzetek posebnih datumov Name[sr]=Сажетак посебних датума @@ -253,7 +222,6 @@ Comment[hu]=A fontos dátumok áttekintő komponense Comment[is]=Yfirlitshluti sérstakra daga Comment[it]=Componente sommario per le date speciali Comment[ja]=特別な日の要約コンポーネント -Comment[ka]=განაკუთრებულ თარიღთა დაიჯესტის კომპონენტი Comment[kk]=Ерекше күндер тұжырымының компоненті Comment[km]=សមាសភាគ​សង្ខេប​នៃ​ថ្ងៃ​ពិសេស Comment[lt]=Ypatingų dienų santraukos komponentas @@ -267,7 +235,7 @@ Comment[nn]=Komponent for samandrag for spesielle datoar Comment[pl]=Składnik podsumowania dat specjalnych Comment[pt]=Componente de Sumário de Datas Especiais Comment[pt_BR]=Componente de Datas Especiais -Comment[ru]=Компонент сводки особых дат +Comment[ru]=Компонент дайджеста особых дат Comment[sk]=Komponent súhrnu špeciálnych dátumov Comment[sl]=Komponenta za povzetke posebnih datumov Comment[sr]=Компонента сажетка посебних датума @@ -311,7 +279,6 @@ Name[hu]=Naptár Name[is]=Dagatal Name[it]=Calendario Name[ja]=カレンダー -Name[ka]=კალენდარი Name[kk]=Күнтізбе Name[km]=ប្រតិទិន Name[lt]=Kalendorius @@ -338,8 +305,7 @@ Name[tg]=Тақвим Name[th]=บันทึกประจำวัน Name[tr]=Takvim Name[uk]=Календар -Name[uz]=Kalendar -Name[uz@cyrillic]=Календар +Name[uz]=Календар Name[zh_CN]=日历 Name[zh_TW]=行事曆 Comment=Configuration of Kontact's Calendar Plugin KOrganizer, includes a Summary View Item and represents a Kontact Component. @@ -348,16 +314,11 @@ Comment[ca]=Configuració de l'endollable de calendari KOrganizer del Kon Comment[da]=Konfiguration af Kontacts kalender-plugin KOrganizer, inkluderer et Opsummeringsvisningselement og repræsenterer en Kontact-komponent. Comment[de]=Einrichtung des Kalender-Moduls KOrganizer für Kontact, einschließlich der Zusammenfassungsansicht; repräsentiert eine Kontact-Komponente Comment[el]=Η ρύθμιση του προσθέτου ημερολογίου του KOrganizer του Kontact, περιέχει ένα Αντικείμενο προβολής σύνοψης και αντιπροσωπεύει ένα Συστατικό του Kontact. -Comment[es]=Configuración del complemento de calendario de Kontact, KOrganizer, incluye una Vista de resumen y representa un Componente de Kontact. Comment[et]=Kontacti kalendriplugina KOrganizeri seadistamine, mis sisaldab kokkuvõttevaate elementi. -Comment[fr]=Configuration du Module de Calendrier de Kontact KOrganizer, inclut un Élément de Vue Résumée et correspond à un Composant de Kontact. -Comment[is]=Stillingar fyrir dagatalsíforrit Kontact KOrganizer, inniheldur yfirlitssýnarhlut sem stendur fyrir Kontact einingu. Comment[it]=Configurazione del plugin calendario KOrganizer di Kontact, include una vista sommario e rappresenta un componente Kontact. -Comment[km]=កា​រកំណត់​រចនាសម្ព័ន្ធ​របស់​កម្មវិធី​ជំនួយ​ប្រតិទិន​របស់ Kontact KOrganizerរួម​មាន ធាតុទិដ្ឋភាព​សង្ខេប និង​បង្ហាញ សមាសភាគ​ Kontact​. Comment[nds]=Kontact sien Kalenner-Moduul KOrganizer instellen. Stellt en Översicht-Indrag praat un is en Kontact-Komponent. Comment[nl]=Instellingen voor Kontacts agendaplugin KOrganizer. Bevat een Overzichtsweergaveplugin en een Kontact-component. Comment[pl]=Konfiguracja wtyczki kalendarza Kontaktu KOrganizer, zawiera element Podsumowanie i jest Składnikiem Kontaktu. -Comment[ru]=Настройка модуля календаря KOrganizer для показа в виде сводки. Comment[sr]=Подешавање Kontact-овог прикључка за календар преко KOrganizer-а, укључујући приказ сажетка, и дат као компонента Kontact-а. Comment[sr@Latn]=Podešavanje Kontact-ovog priključka za kalendar preko KOrganizer-a, uključujući prikaz sažetka, i dat kao komponenta Kontact-a. Comment[sv]=Inställning av Kontacts kalenderinsticksprogram Korganizer, omfattar ett objekt för översiktsvyn och representerar en Kontactkomponent. @@ -395,7 +356,6 @@ Name[hi]=समाचार Name[hu]=Hírek Name[is]=Fréttir Name[ja]=ニュース -Name[ka]=სიახლეები Name[kk]=Жаңалықтар Name[km]=ព័ត៌មាន Name[lt]=Naujienos @@ -423,8 +383,7 @@ Name[tg]=Ахборот Name[th]=ข่าว Name[tr]=Haberler Name[uk]=Новини -Name[uz]=Yangiliklar -Name[uz@cyrillic]=Янгиликлар +Name[uz]=Янгиликлар Name[zh_CN]=新闻 Name[zh_TW]=新聞 Comment=Configuration of Kontact's News Plugin KNode which represents a Kontact Component. @@ -433,16 +392,11 @@ Comment[ca]=Configuració de l'endollable de notícies KNode del Kontact, Comment[da]=Konfiguration af Kontacts nyheds-plugin KNode som repræsenterer en Kontact-komponent. Comment[de]=Einrichtung des News-Moduls KNode für Kontact; repräsentiert eine Kontact-Komponente Comment[el]=Ρύθμιση του πρόσθετου νέων KNode του Kontact που αντιπροσωπεύει ένα Συστατικό του Kontact. -Comment[es]=Configuración del complemento de noticias de Kontact KNode , el cual representa un Componente de Kontact. Comment[et]=Kontacti uudisteplugina KNode seadistamine. -Comment[fr]=Configuration du Module de Nouvelles de Kontact KNode qui correspond à un Composant de Kontact. -Comment[is]=Stillingar fyrir fréttastraumsíforrit Kontact KNode, inniheldur yfirlitssýnarhlut sem stendur fyrir Kontact einingu. Comment[it]=Configurazione del plugin notizie KNode di Kontact, che rappresenta un componente Kontact. -Comment[km]=កា​រកំណត់​រចនាសម្ព័ន្ធ​របស់​កម្មវិធី​ជំនួយ​ព័ត៌មាន​របស់ Kontact KNode ដែល​បង្ហាញ សមាភាគ​​របស់ Kontact​​ ។ Comment[nds]=Kontact sien Narichten-Moduul KNode instellen, dat en Kontact-Komponent is. Comment[nl]=Instellingen voor Kontacts nieuwsplugin KNode. Bevat een Kontact-component. -Comment[pl]=Konfiguracja wtyczki wiadomości Kontaktu KNode, która jest składnikiem Kontaktu. -Comment[ru]=Настройка модуля новостей KNode для показа в виде сводки. +Comment[pl]=Konfiguracja wtyczki niusów Kontaktu KNode, która jest Składnikiem Kontaktu. Comment[sr]=Подешавање Kontact-овог прикључка за вести преко KNode-а у облику компненте Kontact-а. Comment[sr@Latn]=Podešavanje Kontact-ovog priključka za vesti preko KNode-a u obliku kompnente Kontact-a. Comment[sv]=Inställning av Kontacts nyhetsinsticksprogram Knode, som representerar en Kontactkomponent. @@ -481,7 +435,6 @@ Name[hu]=Időjárás Name[is]=Veður Name[it]=Tempo meteorologico Name[ja]=気象情報 -Name[ka]=ამინდი Name[kk]=Ауа райы Name[km]=អាកាសធាតុ Name[lt]=Orų pranešėjas @@ -508,8 +461,7 @@ Name[tg]=Пешгӯии ҳаво Name[th]=รายงานอากาศ Name[tr]=Hava Durumu Name[uk]=Погода -Name[uz]=Ob-havo -Name[uz@cyrillic]=Об-ҳаво +Name[uz]=Об-ҳаво Name[zh_CN]=天气 Name[zh_TW]=天氣 Comment=Weather Information Component @@ -518,22 +470,15 @@ Comment[ca]=Component d'informació del temps Comment[da]=Komponent til vejrinformation Comment[de]=Komponente für Wetterinformationen Comment[el]=Συστατικό πληροφοριών καιρού -Comment[es]=Componente de información meteorológica Comment[et]=Ilmateate komponent -Comment[fr]=Composant d'Informations météorologiques -Comment[is]=Eining fyrir veðurupplýsingar Comment[it]=Informazioni meteorologiche Comment[ja]=気象情報コンポーネント -Comment[km]=សមាសភាគ​ព័ត៌មាន​អាកាសធាតុ Comment[nds]=Wederinformatschonen-Komponent Comment[nl]=Weerinformatiecomponent Comment[pl]=Składnik informacji o pogodzie -Comment[ru]=Информация о погоде -Comment[sk]=Informácie o počasí Comment[sr]=Компонента информација о времену Comment[sr@Latn]=Komponenta informacija o vremenu Comment[sv]=Komponent med väderrapport -Comment[tr]=Hava Durumu Bilgisi Bileşeni Comment[zh_CN]=天气信息组件 Comment[zh_TW]=天氣資訊組件 Weight=1000 @@ -566,7 +511,6 @@ Name[id]=Ticker Berita Name[is]=Fréttastrimill Name[it]=Ticker notizie Name[ja]=ニュースティッカー -Name[ka]=სიახლეთა ტიკერი Name[kk]=Жаңалық таспасы Name[km]=កម្មវិធី​ទទួល​ព័ត៌មាន Name[lt]=News pranešėjas @@ -618,7 +562,6 @@ Comment[hu]=Hírmegjelenítő komponens Comment[is]=Fréttastrimilshluti Comment[it]=Ticker notizie Comment[ja]=ニュースティッカーコンポーネント -Comment[ka]=სიახლეთა ტიკერის კომპონენტი Comment[kk]=Жаңалық таспасының компоненті Comment[km]=សមាសភាគ​កម្មវិធី​ទទួល​ព័ត៌មាន Comment[lt]=Naujienų pranešėjo komponentas diff --git a/kontact/src/kontactui.rc b/kontact/src/kontactui.rc index 8c68f060..bd846c78 100644 --- a/kontact/src/kontactui.rc +++ b/kontact/src/kontactui.rc @@ -1,6 +1,6 @@ - + &File @@ -31,15 +31,12 @@ &Help - - Main Toolbar -
    "; - Event::List events = mCalendar->events( start, - EventSortStartDate, - SortDirectionAscending ); - if (events.count()) { - *ts << ""; - Event::List::ConstIterator it; - for( it = events.begin(); it != events.end(); ++it ) { + // Only print events within the from-to range + if ( start >= fromDate() && start <= toDate() ) { + Event::List events = mCalendar->events( start, + EventSortStartDate, + SortDirectionAscending ); + if (events.count()) { + *ts << "
    "; + Event::List::ConstIterator it; + for( it = events.begin(); it != events.end(); ++it ) { if ( checkSecrecy( *it ) ) { createEvent( ts, *it, start, false ); } + } + *ts << "
    "; + } else { + *ts << " "; } - *ts << "
    "; - } else { - *ts << " "; } *ts << "
     " << event->dtStartTimeStr() << "" + << IncidenceFormatter::timeToString( event->dtStart(), true ) + << " " << event->dtEndTimeStr() << "" + << IncidenceFormatter::timeToString( event->dtEnd(), true ) + << "  
    " + i18n( "Organizer:" ) + "" + + displayViewLinkPerson( incidence->organizer().email(), + incidence->organizer().name(), + TQString::null ) + + "
    " + i18n( "Chair:" ) + "" + str + "
    " + i18n( "Required Participants:" ) + "" + str + "
    " + i18n( "Optional Participants:" ) + "" + str + "
    " + i18n( "Observers:" ) + "" + str + "
    "; @@ -230,32 +397,41 @@ static TQString eventViewerFormatHeader( Incidence *incidence ) tmpStr += ""; } - tmpStr += ""; - tmpStr += "
    "; if ( incidence->type() == "Event" ) { - tmpStr += "iconPath( "appointment", KIcon::Small ) + - "\">"; + TQString iconPath; + if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) { + if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) { + iconPath = + KGlobal::iconLoader()->iconPath( "calendaranniversary", KIcon::Small ); + } else { + iconPath = KGlobal::iconLoader()->iconPath( "calendarbirthday", KIcon::Small ); + } + } else { + iconPath = KGlobal::iconLoader()->iconPath( "appointment", KIcon::Small ); + } + tmpStr += ""; } if ( incidence->type() == "Todo" ) { - tmpStr += "iconPath( "todo", KIcon::Small ) + "\">"; } if ( incidence->type() == "Journal" ) { - tmpStr += "iconPath( "journal", KIcon::Small ) + "\">"; } if ( incidence->isAlarmEnabled() ) { - tmpStr += "iconPath( "bell", KIcon::Small ) + "\">"; } if ( incidence->doesRecur() ) { - tmpStr += "iconPath( "recur", KIcon::Small ) + "\">"; } if ( incidence->isReadOnly() ) { - tmpStr += "iconPath( "readonlyevent", KIcon::Small ) + "\">"; } @@ -263,245 +439,409 @@ static TQString eventViewerFormatHeader( Incidence *incidence ) tmpStr += "" - + eventViewerAddTag( "u", - eventViewerAddTag( "b", incidence->summary() ) ) - + "

    "; + tmpStr += "
    "; + tmpStr += "" + incidence->summary() + ""; + tmpStr += "
    "; return tmpStr; } -static TQString eventViewerFormatEvent( Event *event ) +static TQString displayViewFormatEvent( Calendar *calendar, Event *event, + const TQDate &date ) { - if ( !event ) return TQString::null; - TQString tmpStr = eventViewerFormatHeader( event ); + if ( !event ) { + return TQString::null; + } + + TQString tmpStr = displayViewFormatHeader( event ); tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + + if ( calendar ) { + TQString calStr = IncidenceFormatter::resourceString( calendar, event ); + if ( !calStr.isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + } + + if ( !event->location().isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + TQDateTime startDt = event->dtStart(); + TQDateTime endDt = event->dtEnd(); + if ( event->doesRecur() ) { + if ( date.isValid() ) { + TQDateTime dt( date, TQTime( 0, 0, 0 ) ); + int diffDays = startDt.daysTo( dt ); + dt = dt.addSecs( -1 ); + startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() ); + if ( event->hasEndDate() ) { + endDt = endDt.addDays( diffDays ); + if ( startDt > endDt ) { + startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() ); + endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) ); + } + } + } + } tmpStr += ""; if ( event->doesFloat() ) { if ( event->isMultiDay() ) { - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; } else { - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; } } else { if ( event->isMultiDay() ) { - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; } else { - tmpStr += ""; - if ( event->hasEndDate() && event->dtStart() != event->dtEnd()) { - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + + tmpStr += ""; + tmpStr += ""; + if ( event->hasEndDate() && startDt != endDt ) { + tmpStr += ""; } else { - tmpStr += ""; + tmpStr += ""; } - tmpStr += ""; - tmpStr += ""; - tmpStr += ""; } } tmpStr += ""; - if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) { + TQString durStr = IncidenceFormatter::durationString( event ); + if ( !durStr.isEmpty() ) { tmpStr += ""; - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; - tmpStr += "
    " + i18n( "Calendar:" ) + "" + calStr + "
    " + i18n( "Location:" ) + "" + event->location() + "
    " + i18n( "Time" ) + "" + i18n(" - ","%1 - %2") - .arg( event->dtStartDateStr() ) - .arg( event->dtEndDateStr() ) + "" + i18n( "Date:" ) + "" + + i18n(" - ","%1 - %2"). + arg( IncidenceFormatter::dateToString( startDt, false ) ). + arg( IncidenceFormatter::dateToString( endDt, false ) ) + + "" + i18n( "Date" ) + "" + i18n("date as string","%1").arg( event->dtStartDateStr() ) + "" + i18n( "Date:" ) + "" + + i18n("date as string","%1"). + arg( IncidenceFormatter::dateToString( startDt, false ) ) + + "" + i18n( "Time" ) + "" + i18n(" - ","%1 - %2") - .arg( event->dtStartStr() ) - .arg( event->dtEndStr() ) + "" + i18n( "Date:" ) + "" + + i18n(" - ","%1 - %2"). + arg( IncidenceFormatter::dateToString( startDt, false ) ). + arg( IncidenceFormatter::dateToString( endDt, false ) ) + + "" + i18n( "Time" ) + "" + i18n(" - ","%1 - %2") - .arg( event->dtStartTimeStr() ) - .arg( event->dtEndTimeStr() ) + "" + i18n( "Date:" ) + "" + + i18n("date as string","%1"). + arg( IncidenceFormatter::dateToString( startDt, false ) ) + + "
    " + i18n( "Time:" ) + "" + + i18n(" - ","%1 - %2"). + arg( IncidenceFormatter::timeToString( startDt, true ) ). + arg( IncidenceFormatter::timeToString( endDt, true ) ) + + "" + event->dtStartTimeStr() + "" + + IncidenceFormatter::timeToString( startDt, true ) + + "
    " + i18n( "Date" ) + "" + i18n("date as string","%1") - .arg( event->dtStartDateStr() ) + "
    " + i18n( "Birthday" ) + "" + eventViewerFormatBirthday( event ) + "" + i18n( "Duration:" ) + "" + durStr + "
    "; - return tmpStr; } - if ( !event->description().isEmpty() ) { + if ( event->doesRecur() ) { tmpStr += "
    " + i18n( "Description" ) + "" + eventViewerAddTag( "p", event->description() ) + "" + i18n( "Recurrence:" ) + "" + + IncidenceFormatter::recurrenceString( event ) + + "
    " + i18n( "Location" ) + "" + event->location() + "" + i18n( "Anniversary:" ) + "" + i18n( "Birthday:" ) + "" + displayViewFormatBirthday( event ) + "
    "; + return tmpStr; } - if ( event->categories().count() > 0 ) { + if ( !event->description().isEmpty() ) { tmpStr += "
    " + i18n( "1 Category", "%n Categories", event->categories().count() )+ "" + event->categoriesStr() + "" + i18n( "Description:" ) + "" + event->description() + "
    " + i18n( "Next on" ) + "" + - KGlobal::locale()->formatDateTime( dt, true ) + "" + - KGlobal::locale()->formatDate( dt.date(), true ) + "" + + i18n( "Reminder:", "%n Reminders:", reminderCount ) + + "" + IncidenceFormatter::reminderStringList( event ).join( "
    " ) + "
    "; - tmpStr += eventViewerFormatAttendees( event ); - tmpStr += "
    " + + i18n( "Category:", "%n Categories:", categoryCount ) + + "" + displayViewFormatCategories( event ) + "
    " + i18n( "1 attachment", "%n attachments", attachmentCount )+ "" + eventViewerFormatAttachments( event ) + "" + + i18n( "Attachment:", "%n Attachments:", attachmentCount ) + + "" + displayViewFormatAttachments( event ) + "
    "; - tmpStr += "" + i18n( "Creation date: %1.").arg( - KGlobal::locale()->formatDateTime( event->created() , true ) ) + ""; + + tmpStr += "" + displayViewFormatCreationDate( event ) + ""; + return tmpStr; } -static TQString eventViewerFormatTodo( Todo *todo ) +static TQString displayViewFormatTodo( Calendar *calendar, Todo *todo, + const TQDate &date ) { - if ( !todo ) return TQString::null; - TQString tmpStr = eventViewerFormatHeader( todo ); + if ( !todo ) { + return TQString::null; + } + + TQString tmpStr = displayViewFormatHeader( todo ); tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + + if ( calendar ) { + TQString calStr = IncidenceFormatter::resourceString( calendar, todo ); + if ( !calStr.isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + } - if ( todo->hasDueDate() ) { + if ( !todo->location().isEmpty() ) { tmpStr += ""; - tmpStr += ""; - if ( !todo->doesFloat() ) { - tmpStr += ""; - } else { - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + if ( todo->hasStartDate() && todo->dtStart().isValid() ) { + TQDateTime startDt = todo->dtStart(); + if ( todo->doesRecur() ) { + if ( date.isValid() ) { + startDt.setDate( date ); + } } + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - if ( !todo->description().isEmpty() ) { + if ( todo->hasDueDate() && todo->dtDue().isValid() ) { + TQDateTime dueDt = todo->dtDue(); + if ( todo->doesRecur() ) { + if ( date.isValid() ) { + TQDateTime dt( date, TQTime( 0, 0, 0 ) ); + dt = dt.addSecs( -1 ); + dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() ); + } + } tmpStr += ""; - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - if ( !todo->location().isEmpty() ) { + TQString durStr = IncidenceFormatter::durationString( todo ); + if ( !durStr.isEmpty() ) { tmpStr += ""; - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - if ( todo->categories().count() > 0 ) { + if ( todo->doesRecur() ) { tmpStr += ""; - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - tmpStr += ""; - tmpStr += ""; - if ( todo->priority() > 0 ) { - tmpStr += ""; - } else { - tmpStr += ""; + if ( !todo->description().isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; } - tmpStr += ""; - tmpStr += ""; - tmpStr += ""; - tmpStr += ""; - tmpStr += ""; + // TODO: print comments? - if ( todo->doesRecur() ) { - TQDateTime dt = - todo->recurrence()->getNextDateTime( TQDateTime::currentDateTime() ); + int reminderCount = todo->alarms().count(); + if ( reminderCount > 0 && todo->isAlarmEnabled() ) { tmpStr += ""; - tmpStr += ""; - if ( !todo->doesFloat() ) { - tmpStr += ""; - } else { - tmpStr += ""; - } + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + tmpStr += displayViewFormatAttendees( todo ); + + int categoryCount = todo->categories().count(); + if ( categoryCount > 0 ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + if ( todo->priority() > 0 ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } - int attendeeCount = todo->attendees().count(); - if ( attendeeCount > 0 ) { - tmpStr += ""; + tmpStr += ""; + if ( todo->isCompleted() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; int attachmentCount = todo->attachments().count(); if ( attachmentCount > 0 ) { tmpStr += ""; - tmpStr += ""; - tmpStr += ""; + tmpStr += ""; + tmpStr += ""; tmpStr += ""; } tmpStr += "
    " + i18n( "Calendar:" ) + "" + calStr + "
    " + i18n( "Due on" ) + "" + - KGlobal::locale()->formatDateTime( todo->dtDue(), true ) + - "" + - KGlobal::locale()->formatDate( todo->dtDue().date(), true ) + - "" + i18n( "Location:" ) + "" + todo->location() + "
    " + i18n( "Start:" ) + "" + + IncidenceFormatter::dateTimeToString( startDt, + todo->doesFloat(), false ) + + "
    " + i18n( "Description" ) + "" + todo->description() + "" + i18n( "Due:" ) + "" + + IncidenceFormatter::dateTimeToString( dueDt, + todo->doesFloat(), false ) + + "
    " + i18n( "Location" ) + "" + todo->location() + "" + i18n( "Duration:" ) + "" + durStr + "
    " + i18n( "1 Category", "%n Categories", todo->categories().count() )+ "" + todo->categoriesStr() + "" + i18n( "Recurrence:" ) + "" + + IncidenceFormatter::recurrenceString( todo ) + + "
    " + i18n( "Priority" ) + "" + TQString::number( todo->priority() ) + "" + i18n( "Unspecified" ) + "
    " + i18n( "Description:" ) + "" + todo->description() + "
    " + i18n( "Completed" ) + "" + i18n( "%1 %" ).arg( todo->percentComplete() ) + "
    " + i18n( "Next on" ) + "" + - KGlobal::locale()->formatDateTime( dt, true ) + "" + - KGlobal::locale()->formatDate( dt.date(), true ) + "" + + i18n( "Reminder:", "%n Reminders:", reminderCount ) + + "" + IncidenceFormatter::reminderStringList( todo ).join( "
    " ) + "
    " + + i18n( "Category:", "%n Categories:", categoryCount ) + + "" + displayViewFormatCategories( todo ) + "
    " + i18n( "Priority:" ) + ""; + tmpStr += TQString::number( todo->priority() ); + tmpStr += "
    "; - tmpStr += eventViewerFormatAttendees( todo ); - tmpStr += "
    " + i18n( "Completed:" ) + ""; + tmpStr += todo->completedStr(); + } else { + tmpStr += "" + i18n( "Percent Done:" ) + ""; + tmpStr += i18n( "%1%" ).arg( todo->percentComplete() ); } + tmpStr += "
    " + i18n( "1 attachment", "%n attachments", attachmentCount )+ "" + eventViewerFormatAttachments( todo ) + "" + + i18n( "Attachment:", "Attachments:", attachmentCount ) + + "" + displayViewFormatAttachments( todo ) + "
    "; - tmpStr += "" + i18n( "Creation date: %1.").arg( - KGlobal::locale()->formatDateTime( todo->created(), true ) ) + ""; + + tmpStr += "" + displayViewFormatCreationDate( todo ) + ""; + return tmpStr; } -static TQString eventViewerFormatJournal( Journal *journal ) +static TQString displayViewFormatJournal( Calendar *calendar, Journal *journal ) { - if ( !journal ) return TQString::null; + if ( !journal ) { + return TQString::null; + } - TQString tmpStr; - if ( !journal->summary().isEmpty() ) { - tmpStr += eventViewerAddTag( "u", - eventViewerAddTag( "b", journal->summary() ) ); + TQString tmpStr = displayViewFormatHeader( journal ); + + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + + if ( calendar ) { + TQString calStr = IncidenceFormatter::resourceString( calendar, journal ); + if ( !calStr.isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } } - tmpStr += eventViewerAddTag( "b", i18n("Journal for %1").arg( journal->dtStartDateStr( false ) ) ); - if ( !journal->description().isEmpty() ) - tmpStr += eventViewerAddTag( "p", journal->description() ); + + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + + if ( !journal->description().isEmpty() ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + + int categoryCount = journal->categories().count(); + if ( categoryCount > 0 ) { + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + tmpStr += "
    " + i18n( "Calendar:" ) + "" + calStr + "
    " + i18n( "Date:" ) + "" + + IncidenceFormatter::dateToString( journal->dtStart(), false ) + + "
    " + i18n( "Description:" ) + "" + journal->description() + "
    " + + i18n( "Category:", "%n Categories:", categoryCount ) + + "" + displayViewFormatCategories( journal ) + "
    "; + + tmpStr += "" + displayViewFormatCreationDate( journal ) + ""; + return tmpStr; } -static TQString eventViewerFormatFreeBusy( FreeBusy *fb ) +static TQString displayViewFormatFreeBusy( Calendar * /*calendar*/, FreeBusy *fb ) { - if ( !fb ) return TQString::null; + if ( !fb ) { + return TQString::null; + } + + TQString tmpStr = htmlAddTag( "h2", + htmlAddTag( "b", + i18n("Free/Busy information for %1"). + arg( fb->organizer().fullName() ) ) ); - TQString tmpStr = - eventViewerAddTag( "u", - eventViewerAddTag( "b", i18n("Free/Busy information for %1") - .arg( fb->organizer().fullName() ) ) ); - tmpStr += eventViewerAddTag( "i", i18n("Busy times in date range %1 - %2:") - .arg( KGlobal::locale()->formatDate( fb->dtStart().date(), true ) ) - .arg( KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) ) ); + tmpStr += htmlAddTag( "h4", i18n("Busy times in date range %1 - %2:"). + arg( IncidenceFormatter::dateToString( fb->dtStart(), true ) ). + arg( IncidenceFormatter::dateToString( fb->dtEnd(), true ) ) ); TQValueList periods = fb->busyPeriods(); - TQString text = eventViewerAddTag( "em", eventViewerAddTag( "b", i18n("Busy:") ) ); + TQString text = htmlAddTag( "em", htmlAddTag( "b", i18n("Busy:") ) ); TQValueList::iterator it; for ( it = periods.begin(); it != periods.end(); ++it ) { Period per = *it; @@ -519,91 +859,121 @@ static TQString eventViewerFormatFreeBusy( FreeBusy *fb ) if ( dur > 0 ) { cont += i18n("1 second", "%n seconds", dur); } - text += i18n("startDate for duration", "%1 for %2") - .arg( KGlobal::locale()->formatDateTime( per.start(), false ) ) - .arg( cont ); + text += i18n("startDate for duration", "%1 for %2"). + arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ). + arg( cont ); text += "
    "; } else { if ( per.start().date() == per.end().date() ) { - text += i18n("date, fromTime - toTime ", "%1, %2 - %3") - .arg( KGlobal::locale()->formatDate( per.start().date() ) ) - .arg( KGlobal::locale()->formatTime( per.start().time() ) ) - .arg( KGlobal::locale()->formatTime( per.end().time() ) ); + text += i18n("date, fromTime - toTime ", "%1, %2 - %3"). + arg( IncidenceFormatter::dateToString( per.start().date(), true ) ). + arg( IncidenceFormatter::timeToString( per.start(), true ) ). + arg( IncidenceFormatter::timeToString( per.end(), true ) ); } else { - text += i18n("fromDateTime - toDateTime", "%1 - %2") - .arg( KGlobal::locale()->formatDateTime( per.start(), false ) ) - .arg( KGlobal::locale()->formatDateTime( per.end(), false ) ); + text += i18n("fromDateTime - toDateTime", "%1 - %2"). + arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ). + arg( IncidenceFormatter::dateTimeToString( per.end(), false, true ) ); } text += "
    "; } } - tmpStr += eventViewerAddTag( "p", text ); + tmpStr += htmlAddTag( "p", text ); return tmpStr; } class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor { public: - EventViewerVisitor() { mResult = ""; } - bool act( IncidenceBase *incidence ) { return incidence->accept( *this ); } - TQString result() const { return mResult; } - protected: + EventViewerVisitor() + : mCalendar( 0 ), mResult( "" ) {} + + bool act( Calendar *calendar, IncidenceBase *incidence, const TQDate &date ) + { + mCalendar = calendar; + mDate = date; + mResult = ""; + return incidence->accept( *this ); + } + TQString result() const { return mResult; } + + protected: bool visit( Event *event ) { - mResult = eventViewerFormatEvent( event ); + mResult = displayViewFormatEvent( mCalendar, event, mDate ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { - mResult = eventViewerFormatTodo( todo ); + mResult = displayViewFormatTodo( mCalendar, todo, mDate ); return !mResult.isEmpty(); } bool visit( Journal *journal ) { - mResult = eventViewerFormatJournal( journal ); + mResult = displayViewFormatJournal( mCalendar, journal ); return !mResult.isEmpty(); } bool visit( FreeBusy *fb ) { - mResult = eventViewerFormatFreeBusy( fb ); + mResult = displayViewFormatFreeBusy( mCalendar, fb ); return !mResult.isEmpty(); } protected: + Calendar *mCalendar; + TQDate mDate; TQString mResult; }; TQString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence ) { - if ( !incidence ) return TQString::null; + return extensiveDisplayStr( 0, incidence, TQDate() ); +} + +TQString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar, + IncidenceBase *incidence, + const TQDate &date ) +{ + if ( !incidence ) { + return TQString::null; + } + EventViewerVisitor v; - if ( v.act( incidence ) ) { + if ( v.act( calendar, incidence, date ) ) { return v.result(); - } else + } else { return TQString::null; + } } - - - -/******************************************************************* - * Helper functions for the body part formatter of kmail - *******************************************************************/ +/*********************************************************************** + * Helper functions for the body part formatter of kmail (Invitations) + ***********************************************************************/ static TQString string2HTML( const TQString& str ) { return TQStyleSheet::convertFromPlainText(str, TQStyleSheetItem::WhiteSpaceNormal); } +static TQString cleanHtml( const TQString &html ) +{ + TQRegExp rx( "]*>(.*)" ); + rx.setCaseSensitive( false ); + rx.search( html ); + TQString body = rx.cap( 1 ); + + return TQStyleSheet::escape( body.remove( TQRegExp( "<[^>]*>" ) ).stripWhiteSpace() ); +} + static TQString eventStartTimeStr( Event *event ) { TQString tmp; - if ( ! event->doesFloat() ) { - tmp = i18n("%1: Start Date, %2: Start Time", "%1 %2") - .arg( event->dtStartDateStr(), event->dtStartTimeStr() ); + if ( !event->doesFloat() ) { + tmp = i18n( "%1: Start Date, %2: Start Time", "%1 %2" ). + arg( IncidenceFormatter::dateToString( event->dtStart(), true ), + IncidenceFormatter::timeToString( event->dtStart(), true ) ); } else { - tmp = i18n("%1: Start Date", "%1 (time unspecified)") - .arg( event->dtStartDateStr() ); + tmp = i18n( "%1: Start Date", "%1 (all day)" ). + arg( IncidenceFormatter::dateToString( event->dtStart(), true ) ); } return tmp; } @@ -611,16 +981,15 @@ static TQString eventStartTimeStr( Event *event ) static TQString eventEndTimeStr( Event *event ) { TQString tmp; - if ( event->hasEndDate() ) { - if ( ! event->doesFloat() ) { - tmp = i18n("%1: End Date, %2: End Time", "%1 %2") - .arg( event->dtEndDateStr(), event->dtEndTimeStr() ); + if ( event->hasEndDate() && event->dtEnd().isValid() ) { + if ( !event->doesFloat() ) { + tmp = i18n( "%1: End Date, %2: End Time", "%1 %2" ). + arg( IncidenceFormatter::dateToString( event->dtEnd(), true ), + IncidenceFormatter::timeToString( event->dtEnd(), true ) ); } else { - tmp = i18n("%1: End Date", "%1 (time unspecified)") - .arg( event->dtEndDateStr() ); + tmp = i18n( "%1: End Date", "%1 (all day)" ). + arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) ); } - } else { - tmp = i18n( "Unspecified" ); } return tmp; } @@ -630,198 +999,495 @@ static TQString invitationRow( const TQString &cell1, const TQString &cell2 ) return "" + cell1 + "" + cell2 + "\n"; } -static TQString invitationsDetailsIncidence( Incidence *incidence ) +static Attendee *findDelegatedFromMyAttendee( Incidence *incidence ) +{ + // Return the first attendee that was delegated-from me + + Attendee *attendee = 0; + if ( !incidence ) { + return attendee; + } + + KEMailSettings settings; + TQStringList profiles = settings.profiles(); + for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { + settings.setProfile( *it ); + + TQString delegatorName, delegatorEmail; + Attendee::List attendees = incidence->attendees(); + Attendee::List::ConstIterator it2; + for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) { + Attendee *a = *it2; + KPIM::getNameAndMail( a->delegator(), delegatorName, delegatorEmail ); + if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) { + attendee = a; + break; + } + } + } + return attendee; +} + +static Attendee *findMyAttendee( Incidence *incidence ) +{ + // Return the attendee for the incidence that is probably me + + Attendee *attendee = 0; + if ( !incidence ) { + return attendee; + } + + KEMailSettings settings; + TQStringList profiles = settings.profiles(); + for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { + settings.setProfile( *it ); + + Attendee::List attendees = incidence->attendees(); + Attendee::List::ConstIterator it2; + for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) { + Attendee *a = *it2; + if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) { + attendee = a; + break; + } + } + } + return attendee; +} + +static Attendee *findAttendee( Incidence *incidence, const TQString &email ) +{ + // Search for an attendee by email address + + Attendee *attendee = 0; + if ( !incidence ) { + return attendee; + } + + Attendee::List attendees = incidence->attendees(); + Attendee::List::ConstIterator it; + for ( it = attendees.begin(); it != attendees.end(); ++it ) { + Attendee *a = *it; + if ( email == a->email() ) { + attendee = a; + break; + } + } + return attendee; +} + +static bool rsvpRequested( Incidence *incidence ) { + if ( !incidence ) { + return false; + } + + //use a heuristic to determine if a response is requested. + + bool rsvp = true; // better send superfluously than not at all + Attendee::List attendees = incidence->attendees(); + Attendee::List::ConstIterator it; + for ( it = attendees.begin(); it != attendees.end(); ++it ) { + if ( it == attendees.begin() ) { + rsvp = (*it)->RSVP(); // use what the first one has + } else { + if ( (*it)->RSVP() != rsvp ) { + rsvp = true; // they differ, default + break; + } + } + } + return rsvp; +} + +static TQString rsvpRequestedStr( bool rsvpRequested, const TQString &role ) +{ + if ( rsvpRequested ) { + if ( role.isEmpty() ) { + return i18n( "Your response is requested" ); + } else { + return i18n( "Your response as %1 is requested" ).arg( role ); + } + } else { + if ( role.isEmpty() ) { + return i18n( "No response is necessary" ); + } else { + return i18n( "No response as %1 is necessary" ).arg( role ); + } + } +} + +static TQString myStatusStr( Incidence *incidence ) +{ + TQString ret; + Attendee *a = findMyAttendee( incidence ); + if ( a && + a->status() != Attendee::NeedsAction && a->status() != Attendee::Delegated ) { + ret = i18n( "(Note: the Organizer preset your response to %1)" ). + arg( Attendee::statusName( a->status() ) ); + } + return ret; +} + +static TQString invitationPerson( const TQString& email, TQString name, TQString uid ) +{ + // Make the search, if there is an email address to search on, + // and either name or uid is missing + if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) { + KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); + KABC::Addressee::List addressList = add_book->findByEmail( email ); + if ( !addressList.isEmpty() ) { + KABC::Addressee o = addressList.first(); + if ( !o.isEmpty() && addressList.size() < 2 ) { + if ( name.isEmpty() ) { + // No name set, so use the one from the addressbook + name = o.formattedName(); + } + uid = o.uid(); + } else { + // Email not found in the addressbook. Don't make a link + uid = TQString::null; + } + } + } + + // Show the attendee + TQString tmpString; + if ( !uid.isEmpty() ) { + // There is a UID, so make a link to the addressbook + if ( name.isEmpty() ) { + // Use the email address for text + tmpString += htmlAddLink( "uid:" + uid, email ); + } else { + tmpString += htmlAddLink( "uid:" + uid, name ); + } + } else { + // No UID, just show some text + tmpString += ( name.isEmpty() ? email : name ); + } + tmpString += '\n'; + + // Make the mailto link + if ( !email.isEmpty() ) { + KCal::Person person( name, email ); + KURL mailto; + mailto.setProtocol( "mailto" ); + mailto.setPath( person.fullName() ); + const TQString iconPath = + KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small ); + tmpString += " " + + htmlAddLink( mailto.url(), "" ) +; + } + tmpString += "\n"; + + return tmpString; +} + +static TQString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode ) +{ + // if description and comment -> use both + // if description, but no comment -> use the desc as the comment (and no desc) + // if comment, but no description -> use the comment and no description + TQString html; - TQString descr = incidence->description(); + TQString descr; + TQStringList comments; + + if ( incidence->comments().isEmpty() ) { + if ( !incidence->description().isEmpty() ) { + // use description as comments + if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) { + comments << string2HTML( incidence->description() ); + } else { + comments << incidence->description(); + if ( noHtmlMode ) { + comments[0] = cleanHtml( comments[0] ); + } + comments[0] = htmlAddTag( "p", comments[0] ); + } + } + //else desc and comments are empty + } else { + // non-empty comments + TQStringList cl = incidence->comments(); + uint i = 0; + for( TQStringList::Iterator it=cl.begin(); it!=cl.end(); ++it ) { + if ( !TQStyleSheet::mightBeRichText( *it ) ) { + comments.append( string2HTML( *it ) ); + } else { + if ( noHtmlMode ) { + comments.append( cleanHtml( "" + (*it) + "" ) ); + } else { + comments.append( *it ); + } + } + i++; + } + if ( !incidence->description().isEmpty() ) { + // use description too + if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) { + descr = string2HTML( incidence->description() ); + } else { + descr = incidence->description(); + if ( noHtmlMode ) { + descr = cleanHtml( descr ); + } + descr = htmlAddTag( "p", descr ); + } + } + } + if( !descr.isEmpty() ) { - html += "
    " + i18n("Description:") - + "
     "; - html += string2HTML(descr) + "
    "; + html += "

    "; + html += ""; + html += ""; + html += ""; + html += "
    " + + htmlAddTag( "u", i18n( "Description:" ) ) + + "
    " + descr + "
    "; } - TQStringList comments = incidence->comments(); + if ( !comments.isEmpty() ) { - html += "
    " + i18n("Comments:") - + "
     
      "; - for ( uint i = 0; i < comments.count(); ++i ) - html += "
    • " + string2HTML( comments[i] ) + "
    • "; - html += "
    "; + html += "

    "; + html += ""; + html += ""; + html += ""; + html += "
    " + + htmlAddTag( "u", i18n( "Comments:" ) ) + + "
    "; + if ( comments.count() > 1 ) { + html += "
      "; + for ( uint i=0; i < comments.count(); ++i ) { + html += "
    • " + comments[i] + "
    • "; + } + html += "
    "; + } else { + html += comments[0]; + } + html += "
    "; } return html; } -static TQString invitationDetailsEvent( Event* event ) +static TQString invitationDetailsEvent( Event* event, bool noHtmlMode ) { - // Meeting details are formatted into an HTML table - if ( !event ) + // Invitation details are formatted into an HTML table + if ( !event ) { return TQString::null; - - TQString html; - TQString tmp; + } TQString sSummary = i18n( "Summary unspecified" ); - if ( ! event->summary().isEmpty() ) { - sSummary = string2HTML( event->summary() ); + if ( !event->summary().isEmpty() ) { + if ( !TQStyleSheet::mightBeRichText( event->summary() ) ) { + sSummary = TQStyleSheet::escape( event->summary() ); + } else { + sSummary = event->summary(); + if ( noHtmlMode ) { + sSummary = cleanHtml( sSummary ); + } + } } TQString sLocation = i18n( "Location unspecified" ); - if ( ! event->location().isEmpty() ) { - sLocation = string2HTML( event->location() ); + if ( !event->location().isEmpty() ) { + if ( !TQStyleSheet::mightBeRichText( event->location() ) ) { + sLocation = TQStyleSheet::escape( event->location() ); + } else { + sLocation = event->location(); + if ( noHtmlMode ) { + sLocation = cleanHtml( sLocation ); + } + } } TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" ); - html = TQString("

    \n").arg(dir); + TQString html = TQString("
    \n").arg(dir); html += "\n"; - // Meeting summary & location rows + // Invitation summary & location rows html += invitationRow( i18n( "What:" ), sSummary ); html += invitationRow( i18n( "Where:" ), sLocation ); - // Meeting Start Time Row if (event->doesRecur() == true) { html += invitationRow( i18n( "First Start Time:" ), eventStartTimeStr( event ) ); - } - else { - html += invitationRow( i18n( "Start Time:" ), eventStartTimeStr( event ) ); - } - - // Meeting End Time Row - if (event->doesRecur() == true) { html += invitationRow( i18n( "First End Time:" ), eventEndTimeStr( event ) ); } - else { - html += invitationRow( i18n( "End Time:" ), eventEndTimeStr( event ) ); - } - - // Meeting Duration Row - if ( !event->doesFloat() && event->hasEndDate() ) { - tmp = TQString::null; - TQTime sDuration(0,0,0), t; - int secs = event->dtStart().secsTo( event->dtEnd() ); - t = sDuration.addSecs( secs ); - if ( t.hour() > 0 ) { - tmp += i18n( "1 hour ", "%n hours ", t.hour() ); - } - if ( t.minute() > 0 ) { - tmp += i18n( "1 minute ", "%n minutes ", t.minute() ); +// else { + // If a 1 day event + if ( event->dtStart().date() == event->dtEnd().date() ) { + html += invitationRow( i18n( "Date:" ), + IncidenceFormatter::dateToString( event->dtStart(), false ) ); + if ( !event->doesFloat() ) { + html += invitationRow( i18n( "Time:" ), + IncidenceFormatter::timeToString( event->dtStart(), true ) + + " - " + + IncidenceFormatter::timeToString( event->dtEnd(), true ) ); + } + } else { + html += invitationRow( i18n( "Starting date of an event", "From:" ), + IncidenceFormatter::dateToString( event->dtStart(), false ) ); + if ( !event->doesFloat() ) { + html += invitationRow( i18n( "Starting time of an event", "At:" ), + IncidenceFormatter::timeToString( event->dtStart(), true ) ); + } + if ( event->hasEndDate() ) { + html += invitationRow( i18n( "Ending date of an event", "To:" ), + IncidenceFormatter::dateToString( event->dtEnd(), false ) ); + if ( !event->doesFloat() ) { + html += invitationRow( i18n( "Starting time of an event", "At:" ), + IncidenceFormatter::timeToString( event->dtEnd(), true ) ); + } + } else { + html += invitationRow( i18n( "Ending date of an event", "To:" ), + i18n( "no end date specified" ) ); + } } +// } - html += invitationRow( i18n( "Duration:" ), tmp ); - - if ( event->doesRecur() ) { - TQString recurrence[]= {i18n("no recurrence", "None"), - i18n("Minutely"), i18n("Hourly"), i18n("Daily"), - i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"), - i18n("Yearly"), i18n("Yearly"), i18n("Yearly")}; + // Invitation Duration Row + QString durStr = IncidenceFormatter::durationString( event ); + if ( !durStr.isEmpty() ) { + html += invitationRow( i18n( "Duration:" ), durStr ); + } - Recurrence *recur = event->recurrence(); - if (event->doesRecur() == true) { - html += invitationRow( " ", " " ); - html += invitationRow( i18n( "Recurs:" ), recurrence[ recur->recurrenceType() ] ); - html += invitationRow( i18n("Frequency:"), i18n("%1").arg(event->recurrence()->frequency()) ); + // Recurrence Information Rows + if ( event->doesRecur() ) { + Recurrence *recur = event->recurrence(); + html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) ); - if ( recur->duration() > 0 ) { - if ( recur->duration() == 1 ) - html += invitationRow( i18n("Repeats:"), i18n("Once") ); - else - html += invitationRow( i18n("Repeats:"), i18n("%1 times").arg(recur->duration())); - } else { - if ( recur->duration() != -1 ) { - TQString endstr; - if ( event->doesFloat() ) { - endstr = KGlobal::locale()->formatDate( recur->endDate() ); - } else { - endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() ); - } - html += invitationRow( i18n("Repeats until:"), endstr ); - } else { - html += invitationRow( i18n("Repeats:"), i18n("Forever") ); - } + DateList exceptions = recur->exDates(); + if (exceptions.isEmpty() == false) { + bool isFirstExRow; + isFirstExRow = true; + DateList::ConstIterator ex_iter; + for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) { + if (isFirstExRow == true) { + isFirstExRow = false; + html += invitationRow( i18n("Cancelled on:"), KGlobal::locale()->formatDate(* ex_iter ) ); } - - DateList exceptions = recur->exDates(); - if (exceptions.isEmpty() == false) { - bool isFirstExRow; - isFirstExRow = true; - DateList::ConstIterator ex_iter; - for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) { - if (isFirstExRow == true) { - isFirstExRow = false; - html += invitationRow( i18n("Cancelled on:"), KGlobal::locale()->formatDate(* ex_iter ) ); - } - else { - html += invitationRow(" ", KGlobal::locale()->formatDate(* ex_iter ) ); - } - } + else { + html += invitationRow(" ", KGlobal::locale()->formatDate(* ex_iter ) ); } } } } html += "
    \n"; - html += invitationsDetailsIncidence( event ); + html += invitationsDetailsIncidence( event, noHtmlMode ); html += "
    \n"; return html; } -static TQString invitationDetailsTodo( Todo *todo ) +static TQString invitationDetailsTodo( Todo *todo, bool noHtmlMode ) { // Task details are formatted into an HTML table - if ( !todo ) + if ( !todo ) { return TQString::null; + } TQString sSummary = i18n( "Summary unspecified" ); - TQString sDescr = i18n( "Description unspecified" ); - if ( ! todo->summary().isEmpty() ) { - sSummary = todo->summary(); + if ( !todo->summary().isEmpty() ) { + if ( !TQStyleSheet::mightBeRichText( todo->summary() ) ) { + sSummary = TQStyleSheet::escape( todo->summary() ); + } else { + sSummary = todo->summary(); + if ( noHtmlMode ) { + sSummary = cleanHtml( sSummary ); + } + } + } + + TQString sLocation = i18n( "Location unspecified" ); + if ( !todo->location().isEmpty() ) { + if ( !TQStyleSheet::mightBeRichText( todo->location() ) ) { + sLocation = TQStyleSheet::escape( todo->location() ); + } else { + sLocation = todo->location(); + if ( noHtmlMode ) { + sLocation = cleanHtml( sLocation ); + } + } } - if ( ! todo->description().isEmpty() ) { - sDescr = todo->description(); + + TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" ); + TQString html = TQString("
    \n").arg(dir); + html += "\n"; + + // Invitation summary & location rows + html += invitationRow( i18n( "What:" ), sSummary ); + html += invitationRow( i18n( "Where:" ), sLocation ); + + if ( todo->hasStartDate() && todo->dtStart().isValid() ) { + html += invitationRow( i18n( "Start Date:" ), + IncidenceFormatter::dateToString( todo->dtStart(), false ) ); + if ( !todo->doesFloat() ) { + html += invitationRow( i18n( "Start Time:" ), + IncidenceFormatter::timeToString( todo->dtStart(), false ) ); + } + } + if ( todo->hasDueDate() && todo->dtDue().isValid() ) { + html += invitationRow( i18n( "Due Date:" ), + IncidenceFormatter::dateToString( todo->dtDue(), false ) ); + if ( !todo->doesFloat() ) { + html += invitationRow( i18n( "Due Time:" ), + IncidenceFormatter::timeToString( todo->dtDue(), false ) ); + } + + } else { + html += invitationRow( i18n( "Due Date:" ), i18n( "Due Date: None", "None" ) ); } - TQString html( "
    \n" ); - html += invitationRow( i18n( "Summary:" ), sSummary ); - html += invitationRow( i18n( "Description:" ), sDescr ); - html += "
    \n"; - html += invitationsDetailsIncidence( todo ); + + html += "
    \n"; + html += invitationsDetailsIncidence( todo, noHtmlMode ); return html; } -static TQString invitationDetailsJournal( Journal *journal ) +static TQString invitationDetailsJournal( Journal *journal, bool noHtmlMode ) { - if ( !journal ) + if ( !journal ) { return TQString::null; + } TQString sSummary = i18n( "Summary unspecified" ); TQString sDescr = i18n( "Description unspecified" ); if ( ! journal->summary().isEmpty() ) { sSummary = journal->summary(); + if ( noHtmlMode ) { + sSummary = cleanHtml( sSummary ); + } } if ( ! journal->description().isEmpty() ) { sDescr = journal->description(); + if ( noHtmlMode ) { + sDescr = cleanHtml( sDescr ); + } } TQString html( "\n" ); html += invitationRow( i18n( "Summary:" ), sSummary ); - html += invitationRow( i18n( "Date:" ), journal->dtStartDateStr( false ) ); + html += invitationRow( i18n( "Date:" ), + IncidenceFormatter::dateToString( journal->dtStart(), false ) ); html += invitationRow( i18n( "Description:" ), sDescr ); html += "
    \n"; - html += invitationsDetailsIncidence( journal ); + html += invitationsDetailsIncidence( journal, noHtmlMode ); return html; } -static TQString invitationDetailsFreeBusy( FreeBusy *fb ) +static TQString invitationDetailsFreeBusy( FreeBusy *fb, bool /*noHtmlMode*/ ) { if ( !fb ) return TQString::null; TQString html( "\n" ); html += invitationRow( i18n("Person:"), fb->organizer().fullName() ); - html += invitationRow( i18n("Start date:"), fb->dtStartDateStr() ); + html += invitationRow( i18n("Start date:"), + IncidenceFormatter::dateToString( fb->dtStart(), true ) ); html += invitationRow( i18n("End date:"), - KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) ); + KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) ); html += "\n"; html += "\n"; @@ -868,259 +1534,524 @@ static TQString invitationDetailsFreeBusy( FreeBusy *fb ) return html; } -static TQString invitationHeaderEvent( Event *event, ScheduleMessage *msg ) +static bool replyMeansCounter( Incidence */*incidence*/ ) +{ + return false; +/** + see kolab/issue 3665 for an example of when we might use this for something + + bool status = false; + if ( incidence ) { + // put code here that looks at the incidence and determines that + // the reply is meant to be a counter proposal. We think this happens + // with Outlook counter proposals, but we aren't sure how yet. + if ( condition ) { + status = true; + } + } + return status; +*/ +} + +static TQString invitationHeaderEvent( Event *event, Incidence *existingIncidence, + ScheduleMessage *msg, const TQString &sender ) { if ( !msg || !event ) return TQString::null; + switch ( msg->method() ) { - case Scheduler::Publish: - return i18n("This event has been published"); - case Scheduler::Request: - if ( event->revision() > 0 ) - return i18n( "This meeting has been updated" ); - return i18n( "You have been invited to this meeting" ); - case Scheduler::Refresh: - return i18n( "This invitation was refreshed" ); - case Scheduler::Cancel: - return i18n( "This meeting has been canceled" ); - case Scheduler::Add: - return i18n( "Addition to the meeting invitation" ); - case Scheduler::Reply: { - Attendee::List attendees = event->attendees(); - if( attendees.count() == 0 ) { - kdDebug(5850) << "No attendees in the iCal reply!\n"; - return TQString::null; + case Scheduler::Publish: + return i18n( "This invitation has been published" ); + case Scheduler::Request: + if ( existingIncidence && event->revision() > 0 ) { + return i18n( "This invitation has been updated by the organizer %1" ). + arg( event->organizer().fullName() ); + } + if ( iamOrganizer( event ) ) { + return i18n( "I created this invitation" ); + } else { + TQString orgStr; + if ( !event->organizer().fullName().isEmpty() ) { + orgStr = event->organizer().fullName(); + } else if ( !event->organizer().email().isEmpty() ) { + orgStr = event->organizer().email(); + } + if ( senderIsOrganizer( event, sender ) ) { + if ( !orgStr.isEmpty() ) { + return i18n( "You received an invitation from %1" ).arg( orgStr ); + } else { + return i18n( "You received an invitation" ); } - if( attendees.count() != 1 ) - kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " - << "but is " << attendees.count() << endl; - Attendee* attendee = *attendees.begin(); - TQString attendeeName = attendee->name(); - if ( attendeeName.isEmpty() ) - attendeeName = attendee->email(); - if ( attendeeName.isEmpty() ) - attendeeName = i18n( "Sender" ); - - TQString delegatorName, dummy; - KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy ); - if ( delegatorName.isEmpty() ) - delegatorName = attendee->delegator(); - - switch( attendee->status() ) { - case Attendee::NeedsAction: - return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName ); - case Attendee::Accepted: - if ( delegatorName.isEmpty() ) - return i18n( "%1 accepts this meeting invitation" ).arg( attendeeName ); - return i18n( "%1 accepts this meeting invitation on behalf of %2" ) - .arg( attendeeName ).arg( delegatorName ); - case Attendee::Tentative: - if ( delegatorName.isEmpty() ) - return i18n( "%1 tentatively accepts this meeting invitation" ).arg( attendeeName ); - return i18n( "%1 tentatively accepts this meeting invitation on behalf of %2" ) - .arg( attendeeName ).arg( delegatorName ); - case Attendee::Declined: - if ( delegatorName.isEmpty() ) - return i18n( "%1 declines this meeting invitation" ).arg( attendeeName ); - return i18n( "%1 declines this meeting invitation on behalf of %2" ) - .arg( attendeeName ).arg( delegatorName ); - case Attendee::Delegated: { - TQString delegate, dummy; - KPIM::getNameAndMail( attendee->delegate(), delegate, dummy ); - if ( delegate.isEmpty() ) - delegate = attendee->delegate(); - if ( !delegate.isEmpty() ) - return i18n( "%1 has delegated this meeting invitation to %2" ) - .arg( attendeeName ) .arg( delegate ); - return i18n( "%1 has delegated this meeting invitation" ).arg( attendeeName ); - } - case Attendee::Completed: - return i18n( "This meeting invitation is now completed" ); - case Attendee::InProcess: - return i18n( "%1 is still processing the invitation" ).arg( attendeeName ); - default: - return i18n( "Unknown response to this meeting invitation" ); + } else { + if ( !orgStr.isEmpty() ) { + return i18n( "You received an invitation from %1 as a representative of %2" ). + arg( sender, orgStr ); + } else { + return i18n( "You received an invitation from %1 as the organizer's representative" ). + arg( sender ); } - break; } - case Scheduler::Counter: - return i18n( "Sender makes this counter proposal" ); - case Scheduler::Declinecounter: - return i18n( "Sender declines the counter proposal" ); - case Scheduler::NoMethod: - return i18n("Error: iMIP message with unknown method: '%1'") - .arg( msg->method() ); + } + } + case Scheduler::Refresh: + return i18n( "This invitation was refreshed" ); + case Scheduler::Cancel: + return i18n( "This invitation has been canceled" ); + case Scheduler::Add: + return i18n( "Addition to the invitation" ); + case Scheduler::Reply: + { + if ( replyMeansCounter( event ) ) { + return i18n( "%1 makes this counter proposal" ). + arg( firstAttendeeName( event, i18n( "Sender" ) ) ); + } + + Attendee::List attendees = event->attendees(); + if( attendees.count() == 0 ) { + kdDebug(5850) << "No attendees in the iCal reply!" << endl; + return TQString::null; + } + if( attendees.count() != 1 ) { + kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " + << "but is " << attendees.count() << endl; + } + TQString attendeeName = firstAttendeeName( event, i18n( "Sender" ) ); + + TQString delegatorName, dummy; + Attendee* attendee = *attendees.begin(); + KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy ); + if ( delegatorName.isEmpty() ) { + delegatorName = attendee->delegator(); + } + + switch( attendee->status() ) { + case Attendee::NeedsAction: + return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName ); + case Attendee::Accepted: + if ( event->revision() > 0 ) { + if ( !sender.isEmpty() ) { + return i18n( "This invitation has been updated by attendee %1" ).arg( sender ); + } else { + return i18n( "This invitation has been updated by an attendee" ); + } + } else { + if ( delegatorName.isEmpty() ) { + return i18n( "%1 accepts this invitation" ).arg( attendeeName ); + } else { + return i18n( "%1 accepts this invitation on behalf of %2" ). + arg( attendeeName ).arg( delegatorName ); + } + } + case Attendee::Tentative: + if ( delegatorName.isEmpty() ) { + return i18n( "%1 tentatively accepts this invitation" ). + arg( attendeeName ); + } else { + return i18n( "%1 tentatively accepts this invitation on behalf of %2" ). + arg( attendeeName ).arg( delegatorName ); + } + case Attendee::Declined: + if ( delegatorName.isEmpty() ) { + return i18n( "%1 declines this invitation" ).arg( attendeeName ); + } else { + return i18n( "%1 declines this invitation on behalf of %2" ). + arg( attendeeName ).arg( delegatorName ); + } + case Attendee::Delegated: { + TQString delegate, dummy; + KPIM::getNameAndMail( attendee->delegate(), delegate, dummy ); + if ( delegate.isEmpty() ) { + delegate = attendee->delegate(); + } + if ( !delegate.isEmpty() ) { + return i18n( "%1 has delegated this invitation to %2" ). + arg( attendeeName ) .arg( delegate ); + } else { + return i18n( "%1 has delegated this invitation" ).arg( attendeeName ); + } + } + case Attendee::Completed: + return i18n( "This invitation is now completed" ); + case Attendee::InProcess: + return i18n( "%1 is still processing the invitation" ). + arg( attendeeName ); + default: + return i18n( "Unknown response to this invitation" ); + } + break; + } + + case Scheduler::Counter: + return i18n( "%1 makes this counter proposal" ). + arg( firstAttendeeName( event, i18n( "Sender" ) ) ); + + case Scheduler::Declinecounter: + return i18n( "%1 declines the counter proposal" ). + arg( firstAttendeeName( event, i18n( "Sender" ) ) ); + + case Scheduler::NoMethod: + return i18n("Error: iMIP message with unknown method: '%1'"). + arg( msg->method() ); } return TQString::null; } -static TQString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg ) +static TQString invitationHeaderTodo( Todo *todo, Incidence *existingIncidence, + ScheduleMessage *msg, const TQString &sender ) { - if ( !msg || !todo ) + if ( !msg || !todo ) { return TQString::null; + } + switch ( msg->method() ) { - case Scheduler::Publish: - return i18n("This task has been published"); - case Scheduler::Request: - if ( todo->revision() > 0 ) - return i18n( "This task has been updated" ); - return i18n( "You have been assigned this task" ); - case Scheduler::Refresh: - return i18n( "This task was refreshed" ); - case Scheduler::Cancel: - return i18n( "This task was canceled" ); - case Scheduler::Add: - return i18n( "Addition to the task" ); - case Scheduler::Reply: { - Attendee::List attendees = todo->attendees(); - if( attendees.count() == 0 ) { - kdDebug(5850) << "No attendees in the iCal reply!\n"; - return TQString::null; + case Scheduler::Publish: + return i18n("This task has been published"); + case Scheduler::Request: + if ( existingIncidence && todo->revision() > 0 ) { + return i18n( "This task has been updated by the organizer %1" ). + arg( todo->organizer().fullName() ); + } else { + if ( iamOrganizer( todo ) ) { + return i18n( "I created this task" ); + } else { + TQString orgStr; + if ( !todo->organizer().fullName().isEmpty() ) { + orgStr = todo->organizer().fullName(); + } else if ( !todo->organizer().email().isEmpty() ) { + orgStr = todo->organizer().email(); } - if( attendees.count() != 1 ) - kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " - << "but is " << attendees.count() << endl; - Attendee* attendee = *attendees.begin(); - - switch( attendee->status() ) { - case Attendee::NeedsAction: - return i18n( "Sender indicates this task assignment still needs some action" ); - case Attendee::Accepted: - return i18n( "Sender accepts this task" ); - case Attendee::Tentative: - return i18n( "Sender tentatively accepts this task" ); - case Attendee::Declined: - return i18n( "Sender declines this task" ); - case Attendee::Delegated: { - TQString delegate, dummy; - KPIM::getNameAndMail( attendee->delegate(), delegate, dummy ); - if ( delegate.isEmpty() ) - delegate = attendee->delegate(); - if ( !delegate.isEmpty() ) - return i18n( "Sender has delegated this request for the task to %1" ).arg( delegate ); - return i18n( "Sender has delegated this request for the task " ); + if ( senderIsOrganizer( todo, sender ) ) { + if ( !orgStr.isEmpty() ) { + return i18n( "You have been assigned this task by %1" ).arg( orgStr ); + } else { + return i18n( "You have been assigned this task" ); } - case Attendee::Completed: - return i18n( "The request for this task is now completed" ); - case Attendee::InProcess: - return i18n( "Sender is still processing the invitation" ); - default: - return i18n( "Unknown response to this task" ); + } else { + if ( !orgStr.isEmpty() ) { + return i18n( "You have been assigned this task by %1 as a representative of %2" ). + arg( sender, orgStr ); + } else { + return i18n( "You have been assigned this task by %1 as the organizer's representative" ). + arg( sender ); } - break; } - case Scheduler::Counter: - return i18n( "Sender makes this counter proposal" ); - case Scheduler::Declinecounter: - return i18n( "Sender declines the counter proposal" ); - case Scheduler::NoMethod: - return i18n("Error: iMIP message with unknown method: '%1'") - .arg( msg->method() ); + } + } + } + case Scheduler::Refresh: + return i18n( "This task was refreshed" ); + case Scheduler::Cancel: + return i18n( "This task was canceled" ); + case Scheduler::Add: + return i18n( "Addition to the task" ); + case Scheduler::Reply: + { + if ( replyMeansCounter( todo ) ) { + return i18n( "%1 makes this counter proposal" ). + arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); + } + + Attendee::List attendees = todo->attendees(); + if( attendees.count() == 0 ) { + kdDebug(5850) << "No attendees in the iCal reply!" << endl; + return TQString::null; + } + if( attendees.count() != 1 ) { + kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " + << "but is " << attendees.count() << endl; + } + TQString attendeeName = firstAttendeeName( todo, i18n( "Sender" ) ); + + TQString delegatorName, dummy; + Attendee* attendee = *attendees.begin(); + KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy ); + if ( delegatorName.isEmpty() ) { + delegatorName = attendee->delegator(); + } + + switch( attendee->status() ) { + case Attendee::NeedsAction: + return i18n( "%1 indicates this task assignment still needs some action" ).arg( attendeeName ); + case Attendee::Accepted: + if ( todo->revision() > 0 ) { + if ( !sender.isEmpty() ) { + if ( todo->isCompleted() ) { + return i18n( "This task has been completed by assignee %1" ).arg( sender ); + } else { + return i18n( "This task has been updated by assignee %1" ).arg( sender ); + } + } else { + if ( todo->isCompleted() ) { + return i18n( "This task has been completed by an assignee" ); + } else { + return i18n( "This task has been updated by an assignee" ); + } + } + } else { + if ( delegatorName.isEmpty() ) { + return i18n( "%1 accepts this task" ).arg( attendeeName ); + } else { + return i18n( "%1 accepts this task on behalf of %2" ). + arg( attendeeName ).arg( delegatorName ); + } + } + case Attendee::Tentative: + if ( delegatorName.isEmpty() ) { + return i18n( "%1 tentatively accepts this task" ). + arg( attendeeName ); + } else { + return i18n( "%1 tentatively accepts this task on behalf of %2" ). + arg( attendeeName ).arg( delegatorName ); + } + case Attendee::Declined: + if ( delegatorName.isEmpty() ) { + return i18n( "%1 declines this task" ).arg( attendeeName ); + } else { + return i18n( "%1 declines this task on behalf of %2" ). + arg( attendeeName ).arg( delegatorName ); + } + case Attendee::Delegated: { + TQString delegate, dummy; + KPIM::getNameAndMail( attendee->delegate(), delegate, dummy ); + if ( delegate.isEmpty() ) { + delegate = attendee->delegate(); + } + if ( !delegate.isEmpty() ) { + return i18n( "%1 has delegated this request for the task to %2" ). + arg( attendeeName ).arg( delegate ); + } else { + return i18n( "%1 has delegated this request for the task" ). + arg( attendeeName ); + } + } + case Attendee::Completed: + return i18n( "The request for this task is now completed" ); + case Attendee::InProcess: + return i18n( "%1 is still processing the task" ). + arg( attendeeName ); + default: + return i18n( "Unknown response to this task" ); + } + break; + } + + case Scheduler::Counter: + return i18n( "%1 makes this counter proposal" ). + arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); + + case Scheduler::Declinecounter: + return i18n( "%1 declines the counter proposal" ). + arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); + + case Scheduler::NoMethod: + return i18n( "Error: iMIP message with unknown method: '%1'" ). + arg( msg->method() ); } return TQString::null; } static TQString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg ) { - // TODO: Several of the methods are not allowed for journals, so remove them. - if ( !msg || !journal ) + if ( !msg || !journal ) { return TQString::null; + } + switch ( msg->method() ) { - case Scheduler::Publish: - return i18n("This journal has been published"); - case Scheduler::Request: - return i18n( "You have been assigned this journal" ); - case Scheduler::Refresh: - return i18n( "This journal was refreshed" ); - case Scheduler::Cancel: - return i18n( "This journal was canceled" ); - case Scheduler::Add: - return i18n( "Addition to the journal" ); - case Scheduler::Reply: { - Attendee::List attendees = journal->attendees(); - if( attendees.count() == 0 ) { - kdDebug(5850) << "No attendees in the iCal reply!\n"; - return TQString::null; - } - if( attendees.count() != 1 ) - kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " - << "but is " << attendees.count() << endl; - Attendee* attendee = *attendees.begin(); - - switch( attendee->status() ) { - case Attendee::NeedsAction: - return i18n( "Sender indicates this journal assignment still needs some action" ); - case Attendee::Accepted: - return i18n( "Sender accepts this journal" ); - case Attendee::Tentative: - return i18n( "Sender tentatively accepts this journal" ); - case Attendee::Declined: - return i18n( "Sender declines this journal" ); - case Attendee::Delegated: - return i18n( "Sender has delegated this request for the journal" ); - case Attendee::Completed: - return i18n( "The request for this journal is now completed" ); - case Attendee::InProcess: - return i18n( "Sender is still processing the invitation" ); - default: - return i18n( "Unknown response to this journal" ); - } - break; } - case Scheduler::Counter: - return i18n( "Sender makes this counter proposal" ); - case Scheduler::Declinecounter: - return i18n( "Sender declines the counter proposal" ); - case Scheduler::NoMethod: - return i18n("Error: iMIP message with unknown method: '%1'") - .arg( msg->method() ); + case Scheduler::Publish: + return i18n("This journal has been published"); + case Scheduler::Request: + return i18n( "You have been assigned this journal" ); + case Scheduler::Refresh: + return i18n( "This journal was refreshed" ); + case Scheduler::Cancel: + return i18n( "This journal was canceled" ); + case Scheduler::Add: + return i18n( "Addition to the journal" ); + case Scheduler::Reply: + { + if ( replyMeansCounter( journal ) ) { + return i18n( "Sender makes this counter proposal" ); + } + + Attendee::List attendees = journal->attendees(); + if( attendees.count() == 0 ) { + kdDebug(5850) << "No attendees in the iCal reply!" << endl; + return TQString::null; + } + if( attendees.count() != 1 ) { + kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " + << "but is " << attendees.count() << endl; + } + Attendee* attendee = *attendees.begin(); + + switch( attendee->status() ) { + case Attendee::NeedsAction: + return i18n( "Sender indicates this journal assignment still needs some action" ); + case Attendee::Accepted: + return i18n( "Sender accepts this journal" ); + case Attendee::Tentative: + return i18n( "Sender tentatively accepts this journal" ); + case Attendee::Declined: + return i18n( "Sender declines this journal" ); + case Attendee::Delegated: + return i18n( "Sender has delegated this request for the journal" ); + case Attendee::Completed: + return i18n( "The request for this journal is now completed" ); + case Attendee::InProcess: + return i18n( "Sender is still processing the invitation" ); + default: + return i18n( "Unknown response to this journal" ); + } + break; + } + case Scheduler::Counter: + return i18n( "Sender makes this counter proposal" ); + case Scheduler::Declinecounter: + return i18n( "Sender declines the counter proposal" ); + case Scheduler::NoMethod: + return i18n("Error: iMIP message with unknown method: '%1'"). + arg( msg->method() ); } return TQString::null; } static TQString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg ) { - if ( !msg || !fb ) + if ( !msg || !fb ) { return TQString::null; + } + switch ( msg->method() ) { - case Scheduler::Publish: - return i18n("This free/busy list has been published"); - case Scheduler::Request: - return i18n( "The free/busy list has been requested" ); - case Scheduler::Refresh: - return i18n( "This free/busy list was refreshed" ); - case Scheduler::Cancel: - return i18n( "This free/busy list was canceled" ); - case Scheduler::Add: - return i18n( "Addition to the free/busy list" ); - case Scheduler::NoMethod: - default: - return i18n("Error: Free/Busy iMIP message with unknown method: '%1'") - .arg( msg->method() ); + case Scheduler::Publish: + return i18n("This free/busy list has been published"); + case Scheduler::Request: + return i18n( "The free/busy list has been requested" ); + case Scheduler::Refresh: + return i18n( "This free/busy list was refreshed" ); + case Scheduler::Cancel: + return i18n( "This free/busy list was canceled" ); + case Scheduler::Add: + return i18n( "Addition to the free/busy list" ); + case Scheduler::NoMethod: + default: + return i18n("Error: Free/Busy iMIP message with unknown method: '%1'"). + arg( msg->method() ); + } +} + +static TQString invitationAttendees( Incidence *incidence ) +{ + TQString tmpStr; + if ( !incidence ) { + return tmpStr; + } + + if ( incidence->type() == "Todo" ) { + tmpStr += htmlAddTag( "u", i18n( "Assignees" ) ); + } else { + tmpStr += htmlAddTag( "u", i18n( "Attendees" ) ); + } + tmpStr += "
    "; + + int count=0; + Attendee::List attendees = incidence->attendees(); + if ( !attendees.isEmpty() ) { + + Attendee::List::ConstIterator it; + for( it = attendees.begin(); it != attendees.end(); ++it ) { + Attendee *a = *it; + if ( !iamAttendee( a ) ) { + count++; + if ( count == 1 ) { + tmpStr += "

    Busy periods given in this free/busy object:
    "; + } + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + tmpStr += ""; + } + } + } + if ( count ) { + tmpStr += "
    "; + tmpStr += invitationPerson( a->email(), a->name(), TQString::null ); + if ( !a->delegator().isEmpty() ) { + tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); + } + if ( !a->delegate().isEmpty() ) { + tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); + } + tmpStr += "" + a->statusStr() + "
    "; + } else { + tmpStr += "" + i18n( "No attendee", "None" ) + ""; + } + + return tmpStr; +} + +static TQString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence ) +{ + TQString tmpStr; + if ( !incidence ) { + return tmpStr; + } + + Attachment::List attachments = incidence->attachments(); + if ( !attachments.isEmpty() ) { + tmpStr += i18n( "Attached Documents:" ) + "
      "; + + Attachment::List::ConstIterator it; + for( it = attachments.begin(); it != attachments.end(); ++it ) { + Attachment *a = *it; + tmpStr += "
    1. "; + // Attachment icon + KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() ); + const TQString iconStr = mimeType ? mimeType->icon( a->uri(), false ) : TQString( "application-octet-stream" ); + const TQString iconPath = KGlobal::iconLoader()->iconPath( iconStr, KIcon::Small ); + if ( !iconPath.isEmpty() ) { + tmpStr += ""; + } + tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() ); + tmpStr += "
    2. "; + } + tmpStr += "
    "; } + + return tmpStr; } -class IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor +class IncidenceFormatter::ScheduleMessageVisitor + : public IncidenceBase::Visitor { public: - ScheduleMessageVisitor() : mMessage(0) { mResult = ""; } - bool act( IncidenceBase *incidence, ScheduleMessage *msg ) { mMessage = msg; return incidence->accept( *this ); } + ScheduleMessageVisitor() : mExistingIncidence( 0 ), mMessage( 0 ) { mResult = ""; } + bool act( IncidenceBase *incidence, Incidence *existingIncidence, ScheduleMessage *msg, + const TQString &sender ) + { + mExistingIncidence = existingIncidence; + mMessage = msg; + mSender = sender; + return incidence->accept( *this ); + } TQString result() const { return mResult; } protected: TQString mResult; + Incidence *mExistingIncidence; ScheduleMessage *mMessage; + TQString mSender; }; -class IncidenceFormatter::InvitationHeaderVisitor : - public IncidenceFormatter::ScheduleMessageVisitor +class IncidenceFormatter::InvitationHeaderVisitor + : public IncidenceFormatter::ScheduleMessageVisitor { protected: bool visit( Event *event ) { - mResult = invitationHeaderEvent( event, mMessage ); + mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { - mResult = invitationHeaderTodo( todo, mMessage ); + mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender ); return !mResult.isEmpty(); } bool visit( Journal *journal ) @@ -1135,47 +2066,59 @@ class IncidenceFormatter::InvitationHeaderVisitor : } }; -class IncidenceFormatter::InvitationBodyVisitor : - public IncidenceFormatter::ScheduleMessageVisitor +class IncidenceFormatter::InvitationBodyVisitor + : public IncidenceFormatter::ScheduleMessageVisitor { + public: + InvitationBodyVisitor( bool noHtmlMode ) + : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ) {} + protected: bool visit( Event *event ) { - mResult = invitationDetailsEvent( event ); + mResult = invitationDetailsEvent( event, mNoHtmlMode ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { - mResult = invitationDetailsTodo( todo ); + mResult = invitationDetailsTodo( todo, mNoHtmlMode ); return !mResult.isEmpty(); } bool visit( Journal *journal ) { - mResult = invitationDetailsJournal( journal ); + mResult = invitationDetailsJournal( journal, mNoHtmlMode ); return !mResult.isEmpty(); } bool visit( FreeBusy *fb ) { - mResult = invitationDetailsFreeBusy( fb ); + mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode ); return !mResult.isEmpty(); } + + private: + bool mNoHtmlMode; }; -class IncidenceFormatter::IncidenceCompareVisitor : - public IncidenceBase::Visitor +class IncidenceFormatter::IncidenceCompareVisitor + : public IncidenceBase::Visitor { public: IncidenceCompareVisitor() : mExistingIncidence(0) {} - bool act( IncidenceBase *incidence, Incidence* existingIncidence ) + bool act( IncidenceBase *incidence, Incidence *existingIncidence, int method ) { + Incidence *inc = dynamic_cast( incidence ); + if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() ) + return false; mExistingIncidence = existingIncidence; + mMethod = method; return incidence->accept( *this ); } TQString result() const { - if ( mChanges.isEmpty() ) - return TQString(); + if ( mChanges.isEmpty() ) { + return TQString::null; + } TQString html = "
    • "; html += mChanges.join( "
    • " ); html += "
      "; @@ -1186,17 +2129,18 @@ class IncidenceFormatter::IncidenceCompareVisitor : bool visit( Event *event ) { compareEvents( event, dynamic_cast( mExistingIncidence ) ); - compareIncidences( event, mExistingIncidence ); + compareIncidences( event, mExistingIncidence, mMethod ); return !mChanges.isEmpty(); } bool visit( Todo *todo ) { - compareIncidences( todo, mExistingIncidence ); + compareTodos( todo, dynamic_cast( mExistingIncidence ) ); + compareIncidences( todo, mExistingIncidence, mMethod ); return !mChanges.isEmpty(); } bool visit( Journal *journal ) { - compareIncidences( journal, mExistingIncidence ); + compareIncidences( journal, mExistingIncidence, mMethod ); return !mChanges.isEmpty(); } bool visit( FreeBusy *fb ) @@ -1211,60 +2155,60 @@ class IncidenceFormatter::IncidenceCompareVisitor : if ( !oldEvent || !newEvent ) return; if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() ) - mChanges += i18n( "The begin of the meeting has been changed from %1 to %2" ) - .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) ); + mChanges += i18n( "The invitation starting time has been changed from %1 to %2" ) + .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) ); if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() ) - mChanges += i18n( "The end of the meeting has been changed from %1 to %2" ) - .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) ); - if ( newEvent->doesRecur() ) { - TQString recurrence[]= {i18n("no recurrence", "None"), - i18n("Minutely"), i18n("Hourly"), i18n("Daily"), - i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"), - i18n("Yearly"), i18n("Yearly"), i18n("Yearly")}; - - Recurrence *recur = newEvent->recurrence(); - if (oldEvent->doesRecur() == false) { - mChanges += i18n( "The meeting now recurs %1" ).arg( recurrence[ recur->recurrenceType() ] ); - DateList exceptions = recur->exDates(); - if (exceptions.isEmpty() == false) { - mChanges += i18n("This recurring meeting has been cancelled on the following days:
      "); - DateList::ConstIterator ex_iter; - for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) { - mChanges += i18n("  %1
      ").arg( KGlobal::locale()->formatDate(* ex_iter ) ); - } - } - } - else { - Recurrence *oldRecur = oldEvent->recurrence(); - DateList exceptions = recur->exDates(); - DateList oldExceptions = oldRecur->exDates(); - bool existsInOldEvent; - bool atLeastOneModified; - if (exceptions.isEmpty() == false) { - atLeastOneModified = false; - DateList::ConstIterator ex_iter; - DateList::ConstIterator ex_iter_old; - for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) { - existsInOldEvent = false; - for ( ex_iter_old = oldExceptions.begin(); ex_iter_old != oldExceptions.end(); ++ex_iter_old ) { - if ( KGlobal::locale()->formatDate(* ex_iter ) == KGlobal::locale()->formatDate(* ex_iter_old ) ) { - existsInOldEvent = true; - if (atLeastOneModified == false) { - mChanges += i18n("This recurring meeting has been cancelled on the following days:
      "); - } - atLeastOneModified = true; - } - } - if (existsInOldEvent == false ) { - mChanges += i18n("  %1
      ").arg( KGlobal::locale()->formatDate(* ex_iter ) ); - } - } - } - } + mChanges += i18n( "The invitation ending time has been changed from %1 to %2" ) + .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) ); + } + + void compareTodos( Todo *newTodo, Todo *oldTodo ) + { + if ( !oldTodo || !newTodo ) { + return; + } + + if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) { + mChanges += i18n( "The task has been completed" ); + } + if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) { + mChanges += i18n( "The task is no longer completed" ); + } + if ( oldTodo->percentComplete() != newTodo->percentComplete() ) { + const TQString oldPer = i18n( "%1%" ).arg( oldTodo->percentComplete() ); + const TQString newPer = i18n( "%1%" ).arg( newTodo->percentComplete() ); + mChanges += i18n( "The task completed percentage has changed from %1 to %2" ). + arg( oldPer, newPer ); + } + + if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) { + mChanges += i18n( "A task starting time has been added" ); + } + if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) { + mChanges += i18n( "The task starting time has been removed" ); + } + if ( oldTodo->hasStartDate() && newTodo->hasStartDate() && + oldTodo->dtStart() != newTodo->dtStart() ) { + mChanges += i18n( "The task starting time has been changed from %1 to %2" ). + arg( dateTimeToString( oldTodo->dtStart(), oldTodo->doesFloat(), false ), + dateTimeToString( newTodo->dtStart(), newTodo->doesFloat(), false ) ); + } + + if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) { + mChanges += i18n( "A task due time has been added" ); + } + if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) { + mChanges += i18n( "The task due time has been removed" ); + } + if ( oldTodo->hasDueDate() && newTodo->hasDueDate() && + oldTodo->dtDue() != newTodo->dtDue() ) { + mChanges += i18n( "The task due time has been changed from %1 to %2" ). + arg( dateTimeToString( oldTodo->dtDue(), oldTodo->doesFloat(), false ), + dateTimeToString( newTodo->dtDue(), newTodo->doesFloat(), false ) ); } } - void compareIncidences( Incidence *newInc, Incidence *oldInc ) + void compareIncidences( Incidence *newInc, Incidence *oldInc, int method ) { if ( !oldInc || !newInc ) return; @@ -1276,56 +2220,176 @@ class IncidenceFormatter::IncidenceCompareVisitor : mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() ); Attendee::List oldAttendees = oldInc->attendees(); Attendee::List newAttendees = newInc->attendees(); - for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); it != newAttendees.constEnd(); ++it ) { + for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); + it != newAttendees.constEnd(); ++it ) { Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() ); if ( !oldAtt ) { mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() ); } else { if ( oldAtt->status() != (*it)->status() ) - mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).arg( (*it)->fullName() ) - .arg( (*it)->statusStr() ); + mChanges += i18n( "The status of attendee %1 has been changed to: %2" ). + arg( (*it)->fullName() ).arg( (*it)->statusStr() ); } } - for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); it != oldAttendees.constEnd(); ++it ) { - Attendee *newAtt = newInc->attendeeByMail( (*it)->email() ); - if ( !newAtt ) - mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() ); + if ( method == Scheduler::Request ) { + for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); + it != oldAttendees.constEnd(); ++it ) { + if ( (*it)->email() != oldInc->organizer().email() ) { + Attendee *newAtt = newInc->attendeeByMail( (*it)->email() ); + if ( !newAtt ) { + mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() ); + } + } + } } } private: - Incidence* mExistingIncidence; + Incidence *mExistingIncidence; + int mMethod; TQStringList mChanges; }; TQString InvitationFormatterHelper::makeLink( const TQString &id, const TQString &text ) { - TQString res( "%2" ); - return res.arg( generateLinkURL( id ) ).arg( text ); - return res; + if ( !id.startsWith( "ATTACH:" ) ) { + TQString res = TQString( "%2" ). + arg( generateLinkURL( id ), text ); + return res; + } else { + // draw the attachment links in non-bold face + TQString res = TQString( "%2" ). + arg( generateLinkURL( id ), text ); + return res; + } } // Check if the given incidence is likely one that we own instead one from // a shared calendar (Kolab-specific) -static bool incidenceOwnedByMe( Calendar* calendar, Incidence *incidence ) +static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence ) { - CalendarResources* cal = dynamic_cast( calendar ); - if ( !cal || !incidence ) + CalendarResources *cal = dynamic_cast( calendar ); + if ( !cal || !incidence ) { return true; - ResourceCalendar* res = cal->resource( incidence ); - if ( !res ) + } + ResourceCalendar *res = cal->resource( incidence ); + if ( !res ) { return true; + } const TQString subRes = res->subresourceIdentifier( incidence ); - if ( !subRes.contains( "/.INBOX.directory/" ) ) + if ( !subRes.contains( "/.INBOX.directory/" ) ) { return false; + } return true; } -TQString IncidenceFormatter::formatICalInvitation( TQString invitation, Calendar *mCalendar, - InvitationFormatterHelper *helper ) +// The spacer for the invitation buttons +static TQString spacer = "   "; +// The open & close table cell tags for the invitation buttons +static TQString tdOpen = ""; +static TQString tdClose = "" + spacer; + +static TQString responseButtons( Incidence *inc, bool rsvpReq, bool rsvpRec, + InvitationFormatterHelper *helper ) +{ + TQString html; + if ( !helper ) { + return html; + } + + if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) { + // Record only + html += tdOpen; + html += helper->makeLink( "record", i18n( "[Record]" ) ); + html += tdClose; + + // Move to trash + html += tdOpen; + html += helper->makeLink( "delete", i18n( "[Move to Trash]" ) ); + html += tdClose; + + } else { + + // Accept + html += tdOpen; + html += helper->makeLink( "accept", i18n( "[Accept]" ) ); + html += tdClose; + + // Tentative + html += tdOpen; + html += helper->makeLink( "accept_conditionally", + i18n( "Accept conditionally", "[Accept cond.]" ) ); + html += tdClose; + + // Counter proposal + html += tdOpen; + html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) ); + html += tdClose; + + // Decline + html += tdOpen; + html += helper->makeLink( "decline", i18n( "[Decline]" ) ); + html += tdClose; + } + + if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) { + // Delegate + html += tdOpen; + html += helper->makeLink( "delegate", i18n( "[Delegate]" ) ); + html += tdClose; + + // Forward + html += tdOpen; + html += helper->makeLink( "forward", i18n( "[Forward]" ) ); + html += tdClose; + + // Check calendar + if ( inc && inc->type() == "Event" ) { + html += tdOpen; + html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) ); + html += tdClose; + } + } + return html; +} + +static TQString counterButtons( Incidence *incidence, + InvitationFormatterHelper *helper ) +{ + TQString html; + if ( !helper ) { + return html; + } + + // Accept proposal + html += tdOpen; + html += helper->makeLink( "accept_counter", i18n("[Accept]") ); + html += tdClose; + + // Decline proposal + html += tdOpen; + html += helper->makeLink( "decline_counter", i18n("[Decline]") ); + html += tdClose; + + // Check calendar + if ( incidence && incidence->type() == "Event" ) { + html += tdOpen; + html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) ); + html += tdClose; + } + return html; +} + +TQString IncidenceFormatter::formatICalInvitationHelper( TQString invitation, + Calendar *mCalendar, + InvitationFormatterHelper *helper, + bool noHtmlMode, + const TQString &sender ) { - if ( invitation.isEmpty() ) return TQString::null; + if ( invitation.isEmpty() ) { + return TQString::null; + } ICalFormat format; // parseScheduleMessage takes the tz from the calendar, no need to set it manually here for the format! @@ -1340,15 +2404,18 @@ TQString IncidenceFormatter::formatICalInvitation( TQString invitation, Calendar IncidenceBase *incBase = msg->event(); - Incidence* existingIncidence = 0; - if ( helper->calendar() ) { + // Determine if this incidence is in my calendar (and owned by me) + Incidence *existingIncidence = 0; + if ( incBase && helper->calendar() ) { existingIncidence = helper->calendar()->incidence( incBase->uid() ); - if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) + if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) { existingIncidence = 0; + } if ( !existingIncidence ) { const Incidence::List list = helper->calendar()->incidences(); for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) { - if ( (*it)->schedulingID() == incBase->uid() && incidenceOwnedByMe( helper->calendar(), *it ) ) { + if ( (*it)->schedulingID() == incBase->uid() && + incidenceOwnedByMe( helper->calendar(), *it ) ) { existingIncidence = *it; break; } @@ -1369,115 +2436,262 @@ TQString IncidenceFormatter::formatICalInvitation( TQString invitation, Calendar html += tableHead; InvitationHeaderVisitor headerVisitor; // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled - if ( !headerVisitor.act( incBase, msg ) ) + if ( !headerVisitor.act( incBase, existingIncidence, msg, sender ) ) return TQString::null; html += "" + headerVisitor.result() + ""; - InvitationBodyVisitor bodyVisitor; - if ( !bodyVisitor.act( incBase, msg ) ) + InvitationBodyVisitor bodyVisitor( noHtmlMode ); + if ( !bodyVisitor.act( incBase, existingIncidence, msg, sender ) ) return TQString::null; html += bodyVisitor.result(); - if ( msg->method() == Scheduler::Request ) { // ### Scheduler::Publish/Refresh/Add as well? + if ( msg->method() == Scheduler::Request ) { + IncidenceCompareVisitor compareVisitor; + if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) { + html += "

      "; + html += i18n( "The following changes have been made by the organizer:" ); + html += "

      "; + html += compareVisitor.result(); + } + } + if ( msg->method() == Scheduler::Reply ) { IncidenceCompareVisitor compareVisitor; - if ( compareVisitor.act( incBase, existingIncidence ) ) { - html += i18n("

      The following changes have been made by the organizer:

      "); + if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) { + html += "

      "; + if ( !sender.isEmpty() ) { + html += i18n( "The following changes have been made by %1:" ).arg( sender ); + } else { + html += i18n( "The following changes have been made by an attendee:" ); + } + html += "

      "; html += compareVisitor.result(); } } - html += "
      "; - html += ""; + Incidence *inc = dynamic_cast( incBase ); + + // determine if I am the organizer for this invitation + bool myInc = iamOrganizer( inc ); -#if 0 - html += helper->makeLinkURL( "accept", i18n("[Enter this into my calendar]") ); - html += "
       
        "; -#endif + // determine if the invitation response has already been recorded + bool rsvpRec = false; + Attendee *ea = 0; + if ( !myInc ) { + Incidence *rsvpIncidence = existingIncidence; + if ( !rsvpIncidence && inc && inc->revision() > 0 ) { + rsvpIncidence = inc; + } + if ( rsvpIncidence ) { + ea = findMyAttendee( rsvpIncidence ); + } + if ( ea && + ( ea->status() == Attendee::Accepted || + ea->status() == Attendee::Declined || + ea->status() == Attendee::Tentative ) ) { + rsvpRec = true; + } + } + + // determine invitation role + TQString role; + bool isDelegated = false; + Attendee *a = findMyAttendee( inc ); + if ( !a && inc ) { + if ( !inc->attendees().isEmpty() ) { + a = inc->attendees().first(); + } + } + if ( a ) { + isDelegated = ( a->status() == Attendee::Delegated ); + role = Attendee::roleName( a->role() ); + } + + // determine if RSVP needed, not-needed, or response already recorded + bool rsvpReq = rsvpRequested( inc ); + if ( !myInc && a ) { + html += "
      "; + html += ""; + if ( rsvpRec && inc ) { + if ( inc->revision() == 0 ) { + html += i18n( "Your %1 response has already been recorded" ). + arg( ea->statusStr() ); + } else { + html += i18n( "Your status for this invitation is %1" ). + arg( ea->statusStr() ); + } + rsvpReq = false; + } else if ( msg->method() == Scheduler::Cancel ) { + html += i18n( "This invitation was declined" ); + } else if ( msg->method() == Scheduler::Add ) { + html += i18n( "This invitation was accepted" ); + } else { + if ( !isDelegated ) { + html += rsvpRequestedStr( rsvpReq, role ); + } else { + html += i18n( "Awaiting delegation response" ); + } + } + html += ""; + } + + // Print if the organizer gave you a preset status + if ( !myInc ) { + if ( inc && inc->revision() == 0 ) { + TQString statStr = myStatusStr( inc ); + if ( !statStr.isEmpty() ) { + html += "
      "; + html += ""; + html += statStr; + html += ""; + } + } + } // Add groupware links + html += "
      "; + switch ( msg->method() ) { case Scheduler::Publish: case Scheduler::Request: case Scheduler::Refresh: case Scheduler::Add: { - Incidence *inc = dynamic_cast( incBase ); - if ( inc && inc->revision() > 0 && (existingIncidence || !helper->calendar()) ) { - if ( incBase->type() == "Todo" ) { - html += ""; + if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) { + html += ""; + if ( inc->type() == "Todo" ) { + html += ""; + } + + if ( !myInc && a ) { + html += "" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + ""; + } + break; + } + + case Scheduler::Cancel: + // Remove invitation + if ( inc ) { + html += ""; + if ( inc->type() == "Todo" ) { + html += ""; + } + break; + + case Scheduler::Reply: + { + // Record invitation response + Attendee *a = 0; + Attendee *ea = 0; + if ( inc ) { + // First, determine if this reply is really a counter in disguise. + if ( replyMeansCounter( inc ) ) { + html += "" + counterButtons( inc, helper ) + ""; + break; + } + + // Next, maybe this is a declined reply that was delegated from me? + // find first attendee who is delegated-from me + // look a their PARTSTAT response, if the response is declined, + // then we need to start over which means putting all the action + // buttons and NOT putting on the [Record response..] button + a = findDelegatedFromMyAttendee( inc ); + if ( a ) { + if ( a->status() != Attendee::Accepted || + a->status() != Attendee::Tentative ) { + html += "" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + ""; + break; } } - break; - } - - case Scheduler::Cancel: - // Cancel event from my calendar - html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) ); - break; - case Scheduler::Reply: - // Enter this into my calendar - if ( incBase->type() == "Todo" ) { - html += helper->makeLink( "reply", i18n( "[Enter this into my task list]" ) ); - } else { - html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) ); + // Finally, simply allow a Record of the reply + if ( !inc->attendees().isEmpty() ) { + a = inc->attendees().first(); } - break; + if ( a ) { + ea = findAttendee( existingIncidence, a->email() ); + } + } + if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) { + if ( inc && inc->revision() > 0 ) { + html += "
      "; + html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() ); + html += ""; + } + } else { + if ( inc ) { + html += ""; + } + } + break; + } case Scheduler::Counter: - html += helper->makeLink( "accept_counter", i18n("[Accept]") ); - html += " "; - html += helper->makeLink( "decline_counter", i18n("[Decline]") ); - html += " "; - html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) ); - break; + // Counter proposal + html += "" + counterButtons( inc, helper ) + ""; + break; + case Scheduler::Declinecounter: case Scheduler::NoMethod: - break; + break; } + // close the groupware table html += "
       
      "; - html += helper->makeLink( "reply", i18n( "[Enter this into my task list]" ) ); - } else { - html += ""; - html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) ); - } - html += "
      "; + html += helper->makeLink( "reply", i18n( "[Record invitation in my task list]" ) ); + } else { + html += ""; + html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) ); } - html += ""; - - if ( !existingIncidence ) { - // Accept - html += helper->makeLink( "accept", i18n( "[Accept]" ) ); - html += "   "; - html += helper->makeLink( "accept_conditionally", - i18n( "Accept conditionally", "[Accept cond.]" ) ); - html += "   "; - // counter proposal - html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) ); - html += "   "; - // Decline - html += helper->makeLink( "decline", i18n( "[Decline]" ) ); - html += "   "; - - // Delegate - html += helper->makeLink( "delegate", i18n( "[Delegate]" ) ); - html += "   "; - - // Forward - html += helper->makeLink( "forward", i18n( "[Forward]" ) ); - - if ( incBase->type() == "Event" ) { - html += "   "; - html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) ); + html += "
      "; + html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) ); + } else { + html += ""; + html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) ); + } + html += "
      "; + if ( inc->type() == "Todo" ) { + html += helper->makeLink( "reply", i18n( "[Record response in my task list]" ) ); + } else { + html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) ); + } + html += "
      "; + // Add the attendee list if I am the organizer + if ( myInc && helper->calendar() ) { + html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) ); + } + + // close the top-level table html += "

      "; + // Add the attachment list + html += invitationAttachments( helper, inc ); + return html; } +TQString IncidenceFormatter::formatICalInvitation( TQString invitation, + Calendar *mCalendar, + InvitationFormatterHelper *helper ) +{ + return formatICalInvitationHelper( invitation, mCalendar, helper, false, TQString() ); +} + +TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation, + Calendar *mCalendar, + InvitationFormatterHelper *helper ) +{ + return formatICalInvitationHelper( invitation, mCalendar, helper, true, TQString() ); +} +TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation, + Calendar *mCalendar, + InvitationFormatterHelper *helper, + const TQString &sender ) +{ + return formatICalInvitationHelper( invitation, mCalendar, helper, true, sender ); +} /******************************************************************* @@ -1944,10 +3158,14 @@ TQString IncidenceFormatter::formatTNEFInvitation( const TQByteArray& tnef, class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor { public: - ToolTipVisitor() : mRichText( true ), mResult( "" ) {} + ToolTipVisitor() + : mCalendar( 0 ), mRichText( true ), mResult( "" ) {} - bool act( IncidenceBase *incidence, bool richText=true) + bool act( Calendar *calendar, IncidenceBase *incidence, + const TQDate &date=TQDate(), bool richText=true ) { + mCalendar = calendar; + mDate = date; mRichText = richText; mResult = ""; return incidence ? incidence->accept( *this ) : false; @@ -1960,43 +3178,65 @@ class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor bool visit( Journal *journal ); bool visit( FreeBusy *fb ); - TQString dateRangeText( Event*event ); - TQString dateRangeText( Todo *todo ); + TQString dateRangeText( Event *event, const TQDate &date ); + TQString dateRangeText( Todo *todo, const TQDate &date ); TQString dateRangeText( Journal *journal ); TQString dateRangeText( FreeBusy *fb ); TQString generateToolTip( Incidence* incidence, TQString dtRangeText ); protected: + Calendar *mCalendar; + TQDate mDate; bool mRichText; TQString mResult; }; -TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event*event ) +TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const TQDate &date ) { TQString ret; TQString tmp; + + TQDateTime startDt = event->dtStart(); + TQDateTime endDt = event->dtEnd(); + if ( event->doesRecur() ) { + if ( date.isValid() ) { + TQDateTime dt( date, TQTime( 0, 0, 0 ) ); + int diffDays = startDt.daysTo( dt ); + dt = dt.addSecs( -1 ); + startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() ); + if ( event->hasEndDate() ) { + endDt = endDt.addDays( diffDays ); + if ( startDt > endDt ) { + startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() ); + endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) ); + } + } + } + } if ( event->isMultiDay() ) { tmp = "
      " + i18n("Event start", "From: %1"); if (event->doesFloat()) - ret += tmp.arg( event->dtStartDateStr().replace(" ", " ") ); + ret += tmp.arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") ); else - ret += tmp.arg( event->dtStartStr().replace(" ", " ") ); + ret += tmp.arg( IncidenceFormatter::dateToString( startDt ).replace(" ", " ") ); tmp = "
      " + i18n("Event end","To: %1"); if (event->doesFloat()) - ret += tmp.arg( event->dtEndDateStr().replace(" ", " ") ); + ret += tmp.arg( IncidenceFormatter::dateToString( endDt, false ).replace(" ", " ") ); else - ret += tmp.arg( event->dtEndStr().replace(" ", " ") ); + ret += tmp.arg( IncidenceFormatter::dateToString( endDt ).replace(" ", " ") ); } else { ret += "
      "+i18n("Date: %1"). - arg( event->dtStartDateStr().replace(" ", " ") ); + arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") ); if ( !event->doesFloat() ) { - const TQString dtStartTime = event->dtStartTimeStr().replace( " ", " " ); - const TQString dtEndTime = event->dtEndTimeStr().replace( " ", " " ); + const TQString dtStartTime = + IncidenceFormatter::timeToString( startDt, true ).replace( " ", " " ); + const TQString dtEndTime = + IncidenceFormatter::timeToString( endDt, true ).replace( " ", " " ); if ( dtStartTime == dtEndTime ) { // to prevent 'Time: 17:00 - 17:00' tmp = "
      " + i18n("time for event,   to prevent ugly line breaks", "Time: %1"). @@ -2013,27 +3253,55 @@ TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event*event ) return ret; } -TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo*todo ) +TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const TQDate &date ) { TQString ret; bool floats( todo->doesFloat() ); - if (todo->hasStartDate()) - // No need to add here. This is separated issue and each line - // is very visible on its own. On the other hand... Yes, I like it - // italics here :) - ret += "
      " + i18n("Start: %1").arg( - (floats) - ?(todo->dtStartDateStr().replace(" ", " ")) - :(todo->dtStartStr().replace(" ", " ")) ) ; - if (todo->hasDueDate()) - ret += "
      " + i18n("Due: %1").arg( - (floats) - ?(todo->dtDueDateStr().replace(" ", " ")) - :(todo->dtDueStr().replace(" ", " ")) ); - if (todo->isCompleted()) - ret += "
      " + i18n("Completed: %1").arg( todo->completedStr().replace(" ", " ") ); - else - ret += "
      " + i18n("%1 % completed").arg(todo->percentComplete()); + + if ( todo->hasStartDate() && todo->dtStart().isValid() ) { + TQDateTime startDt = todo->dtStart(); + if ( todo->doesRecur() ) { + if ( date.isValid() ) { + startDt.setDate( date ); + } + } + ret += "
      " + + i18n("Start: %1"). + arg( IncidenceFormatter::dateTimeToString( startDt, floats, false ). + replace( " ", " " ) ); + } + + if ( todo->hasDueDate() && todo->dtDue().isValid() ) { + TQDateTime dueDt = todo->dtDue(); + if ( todo->doesRecur() ) { + if ( date.isValid() ) { + TQDateTime dt( date, TQTime( 0, 0, 0 ) ); + dt = dt.addSecs( -1 ); + dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() ); + } + } + ret += "
      " + + i18n("Due: %1"). + arg( IncidenceFormatter::dateTimeToString( dueDt, floats, false ). + replace( " ", " " ) ); + } + + // Print priority and completed info here, for lack of a better place + + if ( todo->priority() > 0 ) { + ret += "
      "; + ret += "" + i18n( "Priority:" ) + "" + " "; + ret += TQString::number( todo->priority() ); + } + + ret += "
      "; + if ( todo->isCompleted() ) { + ret += "" + i18n( "Completed:" ) + "" + " "; + ret += todo->completedStr().replace( " ", " " ); + } else { + ret += "" + i18n( "Percent Done:" ) + "" + " "; + ret += i18n( "%1%" ).arg( todo->percentComplete() ); + } return ret; } @@ -2042,7 +3310,9 @@ TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal ) { TQString ret; if (journal->dtStart().isValid() ) { - ret += "
      " + i18n("Date: %1").arg( journal->dtStartDateStr( false ) ); + ret += "
      " + + i18n("Date: %1"). + arg( IncidenceFormatter::dateToString( journal->dtStart(), false ) ); } return ret; } @@ -2060,13 +3330,13 @@ TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb ) bool IncidenceFormatter::ToolTipVisitor::visit( Event *event ) { - mResult = generateToolTip( event, dateRangeText( event ) ); + mResult = generateToolTip( event, dateRangeText( event, mDate ) ); return !mResult.isEmpty(); } bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo ) { - mResult = generateToolTip( todo, dateRangeText( todo ) ); + mResult = generateToolTip( todo, dateRangeText( todo, mDate ) ); return !mResult.isEmpty(); } @@ -2085,43 +3355,209 @@ bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb ) return !mResult.isEmpty(); } +static TQString tooltipPerson( const TQString& email, TQString name ) +{ + // Make the search, if there is an email address to search on, + // and name is missing + if ( name.isEmpty() && !email.isEmpty() ) { + KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); + KABC::Addressee::List addressList = add_book->findByEmail( email ); + if ( !addressList.isEmpty() ) { + KABC::Addressee o = addressList.first(); + if ( !o.isEmpty() && addressList.size() < 2 ) { + // use the name from the addressbook + name = o.formattedName(); + } + } + } + + // Show the attendee + TQString tmpString = ( name.isEmpty() ? email : name ); + + return tmpString; +} + +static TQString etc = i18n( "elipsis", "..." ); +static TQString tooltipFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role ) +{ + int maxNumAtts = 8; // maximum number of people to print per attendee role + TQString sep = i18n( "separator for lists of people names", ", " ); + int sepLen = sep.length(); + + int i = 0; + TQString tmpStr; + Attendee::List::ConstIterator it; + Attendee::List attendees = incidence->attendees(); + + for( it = attendees.begin(); it != attendees.end(); ++it ) { + Attendee *a = *it; + if ( a->role() != role ) { + // skip not this role + continue; + } + if ( a->email() == incidence->organizer().email() ) { + // skip attendee that is also the organizer + continue; + } + if ( i == maxNumAtts ) { + tmpStr += etc; + break; + } + tmpStr += tooltipPerson( a->email(), a->name() ); + if ( !a->delegator().isEmpty() ) { + tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); + } + if ( !a->delegate().isEmpty() ) { + tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); + } + tmpStr += sep; + i++; + } + if ( tmpStr.endsWith( sep ) ) { + tmpStr.truncate( tmpStr.length() - sepLen ); + } + return tmpStr; +} + +static TQString tooltipFormatAttendees( Incidence *incidence ) +{ + TQString tmpStr, str; + + // Add organizer link + int attendeeCount = incidence->attendees().count(); + if ( attendeeCount > 1 || + ( attendeeCount == 1 && + incidence->organizer().email() != incidence->attendees().first()->email() ) ) { + tmpStr += "" + i18n( "Organizer:" ) + "" + " "; + tmpStr += tooltipPerson( incidence->organizer().email(), + incidence->organizer().name() ); + } + + // Add "chair" + str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair ); + if ( !str.isEmpty() ) { + tmpStr += "
      " + i18n( "Chair:" ) + "" + " "; + tmpStr += str; + } + + // Add required participants + str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant ); + if ( !str.isEmpty() ) { + tmpStr += "
      " + i18n( "Required Participants:" ) + "" + " "; + tmpStr += str; + } + + // Add optional participants + str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant ); + if ( !str.isEmpty() ) { + tmpStr += "
      " + i18n( "Optional Participants:" ) + "" + " "; + tmpStr += str; + } + + // Add observers + str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant ); + if ( !str.isEmpty() ) { + tmpStr += "
      " + i18n( "Observers:" ) + "" + " "; + tmpStr += str; + } + + return tmpStr; +} + TQString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, TQString dtRangeText ) { - if ( !incidence ) + uint maxDescLen = 120; // maximum description chars to print (before elipsis) + + if ( !incidence ) { return TQString::null; + } + + TQString tmp = ""; - TQString tmp = ""+ incidence->summary().replace("\n", "
      ")+"
      "; + // header + tmp += "" + incidence->summary().replace( "\n", "
      " ) + "
      "; + //NOTE: using
      seems to confuse TQt3 tooltips in some cases so use "-----" + tmp += "
      ----------
      "; + + if ( mCalendar ) { + TQString calStr = IncidenceFormatter::resourceString( mCalendar, incidence ); + if ( !calStr.isEmpty() ) { + tmp += "" + i18n( "Calendar:" ) + "" + " "; + tmp += calStr; + } + } tmp += dtRangeText; - if (!incidence->location().isEmpty()) { - // Put Location: in italics - tmp += "
      "+i18n("Location: %1"). - arg( incidence->location().replace("\n", "
      ") ); + if ( !incidence->location().isEmpty() ) { + tmp += "
      "; + tmp += "" + i18n( "Location:" ) + "" + " "; + tmp += incidence->location().replace( "\n", "
      " ); + } + + TQString durStr = IncidenceFormatter::durationString( incidence ); + if ( !durStr.isEmpty() ) { + tmp += "
      "; + tmp += "" + i18n( "Duration:" ) + "" + " "; + tmp += durStr; + } + + if ( incidence->doesRecur() ) { + tmp += "
      "; + tmp += "" + i18n( "Recurrence:" ) + "" + " "; + tmp += IncidenceFormatter::recurrenceString( incidence ); } - if (!incidence->description().isEmpty()) { - TQString desc(incidence->description()); - if (desc.length()>120) { - desc = desc.left(120) + "..."; + + if ( !incidence->description().isEmpty() ) { + TQString desc( incidence->description() ); + if ( desc.length() > maxDescLen ) { + desc = desc.left( maxDescLen ) + etc; } - tmp += "
      ----------
      " + i18n("Description:
      ") + desc.replace("\n", "
      "); + tmp += "
      ----------
      "; + tmp += "" + i18n( "Description:" ) + "" + "
      "; + tmp += desc.replace( "\n", "
      " ); + tmp += "
      ----------"; + } + + int reminderCount = incidence->alarms().count(); + if ( reminderCount > 0 && incidence->isAlarmEnabled() ) { + tmp += "
      "; + tmp += "" + i18n( "Reminder:", "%n Reminders:", reminderCount ) + "" + " "; + tmp += IncidenceFormatter::reminderStringList( incidence ).join( ", " ); + } + + tmp += "
      "; + tmp += tooltipFormatAttendees( incidence ); + + int categoryCount = incidence->categories().count(); + if ( categoryCount > 0 ) { + tmp += "
      "; + tmp += "" + i18n( "Category:", "%n Categories:", categoryCount ) + "" + " "; + tmp += incidence->categories().join( ", " ); } + tmp += "
      "; return tmp; } TQString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText ) +{ + return toolTipStr( 0, incidence, TQDate(), richText ); +} + +TQString IncidenceFormatter::toolTipStr( Calendar *calendar, + IncidenceBase *incidence, + const TQDate &date, + bool richText ) { ToolTipVisitor v; - if ( v.act( incidence, richText ) ) { + if ( v.act( calendar, incidence, date, richText ) ) { return v.result(); - } else + } else { return TQString::null; + } } - - - /******************************************************************* * Helper functions for the Incidence tooltips *******************************************************************/ @@ -2171,15 +3607,19 @@ bool IncidenceFormatter::MailBodyVisitor::visit( Event *event ) i18n("Yearly"), i18n("Yearly"), i18n("Yearly")}; mResult = mailBodyIncidence( event ); - mResult += i18n("Start Date: %1\n").arg( event->dtStartDateStr() ); + mResult += i18n("Start Date: %1\n"). + arg( IncidenceFormatter::dateToString( event->dtStart(), true ) ); if ( !event->doesFloat() ) { - mResult += i18n("Start Time: %1\n").arg( event->dtStartTimeStr() ); + mResult += i18n("Start Time: %1\n"). + arg( IncidenceFormatter::timeToString( event->dtStart(), true ) ); } if ( event->dtStart() != event->dtEnd() ) { - mResult += i18n("End Date: %1\n").arg( event->dtEndDateStr() ); + mResult += i18n("End Date: %1\n"). + arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) ); } if ( !event->doesFloat() ) { - mResult += i18n("End Time: %1\n").arg( event->dtEndTimeStr() ); + mResult += i18n("End Time: %1\n"). + arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) ); } if ( event->doesRecur() ) { Recurrence *recur = event->recurrence(); @@ -2228,15 +3668,19 @@ bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo ) mResult = mailBodyIncidence( todo ); if ( todo->hasStartDate() ) { - mResult += i18n("Start Date: %1\n").arg( todo->dtStartDateStr() ); + mResult += i18n("Start Date: %1\n"). + arg( IncidenceFormatter::dateToString( todo->dtStart( false ), true ) ); if ( !todo->doesFloat() ) { - mResult += i18n("Start Time: %1\n").arg( todo->dtStartTimeStr() ); + mResult += i18n("Start Time: %1\n"). + arg( IncidenceFormatter::timeToString( todo->dtStart( false ),true ) ); } } if ( todo->hasDueDate() ) { - mResult += i18n("Due Date: %1\n").arg( todo->dtDueDateStr() ); + mResult += i18n("Due Date: %1\n"). + arg( IncidenceFormatter::dateToString( todo->dtDue(), true ) ); if ( !todo->doesFloat() ) { - mResult += i18n("Due Time: %1\n").arg( todo->dtDueTimeStr() ); + mResult += i18n("Due Time: %1\n"). + arg( IncidenceFormatter::timeToString( todo->dtDue(), true ) ); } } TQString details = todo->description(); @@ -2249,9 +3693,11 @@ bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo ) bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal ) { mResult = mailBodyIncidence( journal ); - mResult += i18n("Date: %1\n").arg( journal->dtStartDateStr() ); + mResult += i18n("Date: %1\n"). + arg( IncidenceFormatter::dateToString( journal->dtStart(), true ) ); if ( !journal->doesFloat() ) { - mResult += i18n("Time: %1\n").arg( journal->dtStartTimeStr() ); + mResult += i18n("Time: %1\n"). + arg( IncidenceFormatter::timeToString( journal->dtStart(), true ) ); } if ( !journal->description().isEmpty() ) mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() ); @@ -2283,47 +3729,479 @@ static TQString recurEnd( Incidence *incidence ) return endstr; } -TQString IncidenceFormatter::recurrenceString(Incidence * incidence) +/************************************ + * More static formatting functions + ************************************/ +TQString IncidenceFormatter::recurrenceString( Incidence *incidence ) { - if ( !incidence->doesRecur() ) + if ( !incidence->doesRecur() ) { return i18n( "No recurrence" ); - + } + TQStringList dayList; + dayList.append( i18n( "31st Last" ) ); + dayList.append( i18n( "30th Last" ) ); + dayList.append( i18n( "29th Last" ) ); + dayList.append( i18n( "28th Last" ) ); + dayList.append( i18n( "27th Last" ) ); + dayList.append( i18n( "26th Last" ) ); + dayList.append( i18n( "25th Last" ) ); + dayList.append( i18n( "24th Last" ) ); + dayList.append( i18n( "23rd Last" ) ); + dayList.append( i18n( "22nd Last" ) ); + dayList.append( i18n( "21st Last" ) ); + dayList.append( i18n( "20th Last" ) ); + dayList.append( i18n( "19th Last" ) ); + dayList.append( i18n( "18th Last" ) ); + dayList.append( i18n( "17th Last" ) ); + dayList.append( i18n( "16th Last" ) ); + dayList.append( i18n( "15th Last" ) ); + dayList.append( i18n( "14th Last" ) ); + dayList.append( i18n( "13th Last" ) ); + dayList.append( i18n( "12th Last" ) ); + dayList.append( i18n( "11th Last" ) ); + dayList.append( i18n( "10th Last" ) ); + dayList.append( i18n( "9th Last" ) ); + dayList.append( i18n( "8th Last" ) ); + dayList.append( i18n( "7th Last" ) ); + dayList.append( i18n( "6th Last" ) ); + dayList.append( i18n( "5th Last" ) ); + dayList.append( i18n( "4th Last" ) ); + dayList.append( i18n( "3rd Last" ) ); + dayList.append( i18n( "2nd Last" ) ); + dayList.append( i18n( "last day of the month", "Last" ) ); + dayList.append( i18n( "unknown day of the month", "unknown" ) ); //#31 - zero offset from UI + dayList.append( i18n( "1st" ) ); + dayList.append( i18n( "2nd" ) ); + dayList.append( i18n( "3rd" ) ); + dayList.append( i18n( "4th" ) ); + dayList.append( i18n( "5th" ) ); + dayList.append( i18n( "6th" ) ); + dayList.append( i18n( "7th" ) ); + dayList.append( i18n( "8th" ) ); + dayList.append( i18n( "9th" ) ); + dayList.append( i18n( "10th" ) ); + dayList.append( i18n( "11th" ) ); + dayList.append( i18n( "12th" ) ); + dayList.append( i18n( "13th" ) ); + dayList.append( i18n( "14th" ) ); + dayList.append( i18n( "15th" ) ); + dayList.append( i18n( "16th" ) ); + dayList.append( i18n( "17th" ) ); + dayList.append( i18n( "18th" ) ); + dayList.append( i18n( "19th" ) ); + dayList.append( i18n( "20th" ) ); + dayList.append( i18n( "21st" ) ); + dayList.append( i18n( "22nd" ) ); + dayList.append( i18n( "23rd" ) ); + dayList.append( i18n( "24th" ) ); + dayList.append( i18n( "25th" ) ); + dayList.append( i18n( "26th" ) ); + dayList.append( i18n( "27th" ) ); + dayList.append( i18n( "28th" ) ); + dayList.append( i18n( "29th" ) ); + dayList.append( i18n( "30th" ) ); + dayList.append( i18n( "31st" ) ); + int weekStart = KGlobal::locale()->weekStartDay(); + TQString dayNames; + TQString recurStr, txt; + const KCalendarSystem *calSys = KGlobal::locale()->calendar(); Recurrence *recur = incidence->recurrence(); switch ( recur->recurrenceType() ) { - case Recurrence::rNone: - return i18n( "No recurrence" ); - case Recurrence::rMinutely: - if ( recur->duration() != -1 ) - return i18n( "Recurs every minute until %1", "Recurs every %n minutes until %1", recur->frequency() ) - .arg( recurEnd( incidence ) ); - return i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() ); - case Recurrence::rHourly: - if ( recur->duration() != -1 ) - return i18n( "Recurs hourly until %1", "Recurs every %n hours until %1", recur->frequency() ) - .arg( recurEnd( incidence ) ); - return i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() ); - case Recurrence::rDaily: - if ( recur->duration() != -1 ) - return i18n( "Recurs daily until %1", "Recurs every %n days until %1", recur->frequency() ) - .arg( recurEnd( incidence ) ); - return i18n( "Recurs daily", "Recurs every %n days", recur->frequency() ); - case Recurrence::rWeekly: - if ( recur->duration() != -1 ) - return i18n( "Recurs weekly until %1", "Recurs every %n weeks until %1", recur->frequency() ) - .arg( recurEnd( incidence ) ); - return i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() ); - case Recurrence::rMonthlyPos: - case Recurrence::rMonthlyDay: - if ( recur->duration() != -1 ) - return i18n( "Recurs monthly until %1" ).arg( recurEnd( incidence ) ); - return i18n( "Recurs monthly" ); - case Recurrence::rYearlyMonth: - case Recurrence::rYearlyDay: - case Recurrence::rYearlyPos: - if ( recur->duration() != -1 ) - return i18n( "Recurs yearly until %1" ).arg( recurEnd( incidence ) ); - return i18n( "Recurs yearly" ); - default: - return i18n( "Incidence recurs" ); + case Recurrence::rNone: + return i18n( "No recurrence" ); + + case Recurrence::rMinutely: + recurStr = i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() ); + if ( recur->duration() != -1 ) { + txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + return recurStr; + + case Recurrence::rHourly: + recurStr = i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() ); + if ( recur->duration() != -1 ) { + txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + return recurStr; + + case Recurrence::rDaily: + recurStr = i18n( "Recurs daily", "Recurs every %n days", recur->frequency() ); + if ( recur->duration() != -1 ) { + + txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + return recurStr; + + case Recurrence::rWeekly: + { + recurStr = i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() ); + + bool addSpace = false; + for ( int i = 0; i < 7; ++i ) { + if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) { + if ( addSpace ) { + dayNames.append( i18n( "separator for list of days", ", " ) ); + } + dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1, true ) ); + addSpace = true; + } + } + if ( dayNames.isEmpty() ) { + dayNames = i18n( "Recurs weekly on no days", "no days" ); + } + if ( recur->duration() != -1 ) { + txt = i18n( "%1 on %2 until %3" ). + arg( recurStr ).arg( dayNames ).arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + txt = i18n( "%1 on %2" ).arg( recurStr ).arg( dayNames ); + return txt; + } + case Recurrence::rMonthlyPos: + { + recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() ); + + if ( !recur->monthPositions().isEmpty() ) { + KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0]; + if ( recur->duration() != -1 ) { + txt = i18n( "%1 on the %2 %3 until %4" ). + arg( recurStr ). + arg( dayList[rule.pos() + 31] ). + arg( calSys->weekDayName( rule.day(), false ) ). + arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + txt = i18n( "%1 on the %2 %3" ). + arg( recurStr ). + arg( dayList[rule.pos() + 31] ). + arg( calSys->weekDayName( rule.day(), false ) ); + return txt; + } else { + return recurStr; + } + break; + } + case Recurrence::rMonthlyDay: + { + recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() ); + + if ( !recur->monthDays().isEmpty() ) { + int days = recur->monthDays()[0]; + if ( recur->duration() != -1 ) { + txt = i18n( "%1 on the %2 day until %3" ). + arg( recurStr ). + arg( dayList[days + 31] ). + arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + txt = i18n( "%1 on the %2 day" ).arg( recurStr ).arg( dayList[days + 31] ); + return txt; + } else { + return recurStr; + } + break; + } + case Recurrence::rYearlyMonth: + { + recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() ); + + if ( recur->duration() != -1 ) { + if ( !recur->yearDates().isEmpty() ) { + txt = i18n( "%1 on %2 %3 until %4" ). + arg( recurStr ). + arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). + arg( dayList[ recur->yearDates()[0] + 31 ] ). + arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + } + if ( !recur->yearDates().isEmpty() ) { + txt = i18n( "%1 on %2 %3" ). + arg( recurStr ). + arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). + arg( dayList[ recur->yearDates()[0] + 31 ] ); + return txt; + } else { + if ( !recur->yearMonths().isEmpty() ) { + txt = i18n( "Recurs yearly on %1 %2" ). + arg( calSys->monthName( recur->yearMonths()[0], + recur->startDate().year() ) ). + arg( dayList[ recur->startDate().day() + 31 ] ); + } else { + txt = i18n( "Recurs yearly on %1 %2" ). + arg( calSys->monthName( recur->startDate().month(), + recur->startDate().year() ) ). + arg( dayList[ recur->startDate().day() + 31 ] ); + } + return txt; + } + break; + } + case Recurrence::rYearlyDay: + { + recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() ); + if ( !recur->yearDays().isEmpty() ) { + if ( recur->duration() != -1 ) { + txt = i18n( "%1 on day %2 until %3" ). + arg( recurStr ). + arg( recur->yearDays()[0] ). + arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + txt = i18n( "%1 on day %2" ).arg( recurStr ).arg( recur->yearDays()[0] ); + return txt; + } else { + return recurStr; + } + break; + } + case Recurrence::rYearlyPos: + { + recurStr = i18n( "Every year", "Every %n years", recur->frequency() ); + if ( !recur->yearPositions().isEmpty() && !recur->yearMonths().isEmpty() ) { + KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0]; + if ( recur->duration() != -1 ) { + txt = i18n( "%1 on the %2 %3 of %4 until %5" ). + arg( recurStr ). + arg( dayList[rule.pos() + 31] ). + arg( calSys->weekDayName( rule.day(), false ) ). + arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). + arg( recurEnd( incidence ) ); + if ( recur->duration() > 0 ) { + txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); + } + return txt; + } + txt = i18n( "%1 on the %2 %3 of %4" ). + arg( recurStr ). + arg( dayList[rule.pos() + 31] ). + arg( calSys->weekDayName( rule.day(), false ) ). + arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ); + return txt; + } else { + return recurStr; + } + break; + } + } + + return i18n( "Incidence recurs" ); +} + +TQString IncidenceFormatter::timeToString( const TQDateTime &date, bool shortfmt ) +{ + return KGlobal::locale()->formatTime( date.time(), !shortfmt ); +} + +TQString IncidenceFormatter::dateToString( const TQDateTime &date, bool shortfmt ) +{ + return + KGlobal::locale()->formatDate( date.date(), shortfmt ); +} + +TQString IncidenceFormatter::dateTimeToString( const TQDateTime &date, + bool allDay, bool shortfmt ) +{ + if ( allDay ) { + return dateToString( date, shortfmt ); + } + + return KGlobal::locale()->formatDateTime( date, shortfmt ); +} + +TQString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence ) +{ + if ( !calendar || !incidence ) { + return TQString::null; + } + + CalendarResources *calendarResource = dynamic_cast( calendar ); + if ( !calendarResource ) { + return TQString::null; + } + + ResourceCalendar *resourceCalendar = calendarResource->resource( incidence ); + if ( resourceCalendar ) { + if ( !resourceCalendar->subresources().isEmpty() ) { + TQString subRes = resourceCalendar->subresourceIdentifier( incidence ); + if ( subRes.isEmpty() ) { + return resourceCalendar->resourceName(); + } else { + return resourceCalendar->labelForSubresource( subRes ); + } + } + return resourceCalendar->resourceName(); + } + + return TQString::null; +} + +static TQString secs2Duration( int secs ) +{ + TQString tmp; + int days = secs / 86400; + if ( days > 0 ) { + tmp += i18n( "1 day", "%n days", days ); + tmp += ' '; + secs -= ( days * 86400 ); + } + int hours = secs / 3600; + if ( hours > 0 ) { + tmp += i18n( "1 hour", "%n hours", hours ); + tmp += ' '; + secs -= ( hours * 3600 ); + } + int mins = secs / 60; + if ( mins > 0 ) { + tmp += i18n( "1 minute", "%n minutes", mins ); + } + return tmp; +} + +TQString IncidenceFormatter::durationString( Incidence *incidence ) +{ + TQString tmp; + if ( incidence->type() == "Event" ) { + Event *event = static_cast( incidence ); + if ( event->hasEndDate() ) { + if ( !event->doesFloat() ) { + tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) ); + } else { + tmp = i18n( "1 day", "%n days", + event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 ); + } + } else { + tmp = i18n( "forever" ); + } + } else if ( incidence->type() == "Todo" ) { + Todo *todo = static_cast( incidence ); + if ( todo->hasDueDate() ) { + if ( todo->hasStartDate() ) { + if ( !todo->doesFloat() ) { + tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) ); + } else { + tmp = i18n( "1 day", "%n days", + todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 ); + } + } + } + } + return tmp; +} + +TQStringList IncidenceFormatter::reminderStringList( Incidence *incidence, bool shortfmt ) +{ + //TODO: implement shortfmt=false + Q_UNUSED( shortfmt ); + + TQStringList reminderStringList; + + if ( incidence ) { + Alarm::List alarms = incidence->alarms(); + Alarm::List::ConstIterator it; + for ( it = alarms.begin(); it != alarms.end(); ++it ) { + Alarm *alarm = *it; + int offset = 0; + TQString remStr, atStr, offsetStr; + if ( alarm->hasTime() ) { + offset = 0; + if ( alarm->time().isValid() ) { + atStr = KGlobal::locale()->formatDateTime( alarm->time() ); + } + } else if ( alarm->hasStartOffset() ) { + offset = alarm->startOffset().asSeconds(); + if ( offset < 0 ) { + offset = -offset; + offsetStr = i18n( "N days/hours/minutes before the start datetime", + "%1 before the start" ); + } else if ( offset > 0 ) { + offsetStr = i18n( "N days/hours/minutes after the start datetime", + "%1 after the start" ); + } else { //offset is 0 + if ( incidence->dtStart().isValid() ) { + atStr = KGlobal::locale()->formatDateTime( incidence->dtStart() ); + } + } + } else if ( alarm->hasEndOffset() ) { + offset = alarm->endOffset().asSeconds(); + if ( offset < 0 ) { + offset = -offset; + if ( incidence->type() == "Todo" ) { + offsetStr = i18n( "N days/hours/minutes before the due datetime", + "%1 before the to-do is due" ); + } else { + offsetStr = i18n( "N days/hours/minutes before the end datetime", + "%1 before the end" ); + } + } else if ( offset > 0 ) { + if ( incidence->type() == "Todo" ) { + offsetStr = i18n( "N days/hours/minutes after the due datetime", + "%1 after the to-do is due" ); + } else { + offsetStr = i18n( "N days/hours/minutes after the end datetime", + "%1 after the end" ); + } + } else { //offset is 0 + if ( incidence->type() == "Todo" ) { + Todo *t = static_cast( incidence ); + if ( t->dtDue().isValid() ) { + atStr = KGlobal::locale()->formatDateTime( t->dtDue() ); + } + } else { + Event *e = static_cast( incidence ); + if ( e->dtEnd().isValid() ) { + atStr = KGlobal::locale()->formatDateTime( e->dtEnd() ); + } + } + } + } + if ( offset == 0 ) { + if ( !atStr.isEmpty() ) { + remStr = i18n( "reminder occurs at datetime", "at %1" ).arg( atStr ); + } + } else { + remStr = offsetStr.arg( secs2Duration( offset ) ); + } + + if ( alarm->repeatCount() > 0 ) { + TQString countStr = i18n( "repeats once", "repeats %n times", alarm->repeatCount() ); + TQString intervalStr = i18n( "interval is N days/hours/minutes", "interval is %1" ). + arg( secs2Duration( alarm->snoozeTime().asSeconds() ) ); + TQString repeatStr = i18n( "(repeat string, interval string)", "(%1, %2)" ). + arg( countStr, intervalStr ); + remStr = remStr + ' ' + repeatStr; + + } + reminderStringList << remStr; + } } + + return reminderStringList; } diff --git a/libkcal/incidenceformatter.h b/libkcal/incidenceformatter.h index ce37b016..b2dcd432 100644 --- a/libkcal/incidenceformatter.h +++ b/libkcal/incidenceformatter.h @@ -3,6 +3,7 @@ Copyright (c) 2001-2003 Cornelius Schumacher Copyright (c) 2004 Reinhold Kainhofer + Copyright (c) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -22,10 +23,12 @@ #ifndef KCAL_INCIDENCEFORMATTER_H #define KCAL_INCIDENCEFORMATTER_H -#include - #include "libkcal_export.h" +#include +#include +#include + namespace KCal { class Calendar; class Incidence; @@ -36,7 +39,7 @@ class LIBKCAL_EXPORT InvitationFormatterHelper public: virtual TQString generateLinkURL( const TQString &id ) { return id; } virtual TQString makeLink( const TQString &id, const TQString &text ); - virtual Calendar* calendar() const { return 0; } + virtual Calendar *calendar() const { return 0; } }; /** @@ -49,12 +52,27 @@ class LIBKCAL_EXPORT InvitationFormatterHelper class LIBKCAL_EXPORT IncidenceFormatter { public: - static TQString toolTipString( IncidenceBase *incidence, bool richText = true ); + static TQString KDE_DEPRECATED toolTipString( IncidenceBase *incidence, bool richText = true ); + static TQString toolTipStr( Calendar *calendar, + IncidenceBase *incidence, + const TQDate &date=TQDate(), + bool richText = true ); static TQString mailBodyString( IncidenceBase *incidencebase ); - static TQString extensiveDisplayString( IncidenceBase *incidence ); + static TQString KDE_DEPRECATED extensiveDisplayString( IncidenceBase *incidence ); + static TQString extensiveDisplayStr( Calendar *calendar, + IncidenceBase *incidence, + const TQDate &date=TQDate() ); static TQString formatICalInvitation( TQString invitation, Calendar *mCalendar, InvitationFormatterHelper *helper ); + static TQString KDE_DEPRECATED formatICalInvitationNoHtml( TQString invitation, + Calendar *mCalendar, + InvitationFormatterHelper *helper ); + static TQString formatICalInvitationNoHtml( TQString invitation, + Calendar *mCalendar, + InvitationFormatterHelper *helper, + const TQString &sender ); + // Format a TNEF attachment to an HTML mail static TQString formatTNEFInvitation( const TQByteArray& tnef, Calendar *mCalendar, @@ -63,7 +81,44 @@ class LIBKCAL_EXPORT IncidenceFormatter static TQString msTNEFToVPart( const TQByteArray& tnef ); static TQString recurrenceString( Incidence *incidence ); + + /* + Returns a reminder string computed for the specified Incidence. + Each item of the returning TQStringList corresponds to a string + representation of an reminder belonging to this incidence. + @param incidence is a pointer to the Incidence. + @param shortfmt if false, a short version of each reminder is printed; + else a longer version of each reminder is printed. + */ + static TQStringList reminderStringList( Incidence *incidence, bool shortfmt = true ); + + static TQString timeToString( const TQDateTime &date, bool shortfmt = true ); + + static TQString dateToString( const TQDateTime &date, bool shortfmt = true ); + + static TQString dateTimeToString( const TQDateTime &date, + bool dateOnly = false, + bool shortfmt = true ); + /** + Returns a Calendar Resource label name for the specified Incidence. + @param calendar is a pointer to the Calendar. + @param incidence is a pointer to the Incidence. + */ + static TQString resourceString( Calendar *calendar, Incidence *incidence ); + + /** + Returns a duration string computed for the specified Incidence. + Only makes sense for Events and Todos. + @param incidence is a pointer to the Incidence. + */ + static TQString durationString( Incidence *incidence ); + private: + static TQString formatICalInvitationHelper( TQString invitation, + Calendar *mCalendar, + InvitationFormatterHelper *helper, + bool noHtmlMode, + const TQString &sender ); class EventViewerVisitor; class ScheduleMessageVisitor; class InvitationHeaderVisitor; diff --git a/libkcal/kcal_manager.desktop b/libkcal/kcal_manager.desktop index e606a9d6..a0a72e28 100644 --- a/libkcal/kcal_manager.desktop +++ b/libkcal/kcal_manager.desktop @@ -29,7 +29,6 @@ Name[hu]=Naptár Name[is]=Dagatal Name[it]=Calendario Name[ja]=カレンダー -Name[ka]=კალენდარი Name[kk]=Күнтізбе Name[km]=ប្រតិទិន Name[lt]=Kalendorius @@ -56,8 +55,7 @@ Name[tg]=Тақвим Name[th]=บันทึกประจำวัน Name[tr]=Takvim Name[uk]=Календар -Name[uz]=Kalendar -Name[uz@cyrillic]=Календар +Name[uz]=Календар Name[zh_CN]=日历 Name[zh_TW]=行事曆 Type=Service diff --git a/libkcal/libical/configure.in.in b/libkcal/libical/configure.in.in new file mode 100644 index 00000000..79aafbc3 --- /dev/null +++ b/libkcal/libical/configure.in.in @@ -0,0 +1,20 @@ +dnl Checks for programs. +AC_PROG_YACC +AM_PROG_LEX + +AC_CHECK_PROGS(PERL, perl5 perl) + +AC_DEFINE(ICAL_SAFESAVES,1, [safe saves]) +AC_DEFINE(ICAL_UNIX_NEWLINE,1, [unix newline]) + +AC_CHECK_HEADERS(time.h sys/types.h assert.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T +AC_STRUCT_TM +AM_PROG_LEX + +dnl Checks for library functions. +AC_CHECK_FUNCS(strdup) + diff --git a/libkcal/libical/src/libical/icalattach.c b/libkcal/libical/src/libical/icalattach.c new file mode 100644 index 00000000..106096bf --- /dev/null +++ b/libkcal/libical/src/libical/icalattach.c @@ -0,0 +1,151 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalattach.c + CREATOR: acampi 28 May 02 + + $Id: icalattach.c 1024886 2009-09-17 13:28:13Z winterz $ + $Locker: $ + + + (C) COPYRIGHT 2000, Andrea Campi + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icaltypes.c + + ======================================================================*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icaltypes.h" +#include "icalerror.h" +#include "icalmemory.h" +#include "icalattachimpl.h" +#include /* for malloc and abs() */ +#include /* for errno */ +#include /* for icalmemory_strdup */ +#include + +icalattach * +icalattach_new_from_url (const char *url) +{ + icalattach *attach; + char *url_copy; + + icalerror_check_arg_rz ((url != NULL), "url"); + + if ((attach = malloc (sizeof (icalattach))) == NULL) { + errno = ENOMEM; + return NULL; + } + + if ((url_copy = strdup (url)) == NULL) { + free (attach); + errno = ENOMEM; + return NULL; + } + + attach->refcount = 1; + attach->is_url = 1; + attach->u.url.url = url_copy; + + return attach; +} + +icalattach * +icalattach_new_from_data (unsigned char *data, icalattach_free_fn_t free_fn, + void *free_fn_data) +{ + icalattach *attach; + char *data_copy; + + icalerror_check_arg_rz ((data != NULL), "data"); + + if ((attach = malloc (sizeof (icalattach))) == NULL) { + errno = ENOMEM; + return NULL; + } + + if ((data_copy = strdup (data)) == NULL) { + free (attach); + errno = ENOMEM; + return NULL; + } + + attach->refcount = 1; + attach->is_url = 0; + attach->u.data.data = data_copy; + attach->u.data.free_fn = free_fn; + attach->u.data.free_fn_data = free_fn_data; + + return attach; +} + +void +icalattach_ref (icalattach *attach) +{ + icalerror_check_arg_rv ((attach != NULL), "attach"); + icalerror_check_arg_rv ((attach->refcount > 0), "attach->refcount > 0"); + + attach->refcount++; +} + +void +icalattach_unref (icalattach *attach) +{ + icalerror_check_arg_rv ((attach != NULL), "attach"); + icalerror_check_arg_rv ((attach->refcount > 0), "attach->refcount > 0"); + + attach->refcount--; + + if (attach->refcount != 0) + return; + + if (attach->is_url) { + free (attach->u.url.url); + } else { + free (attach->u.data.data); +/* unused for now + if (attach->u.data.free_fn) + (* attach->u.data.free_fn) (attach->u.data.data, attach->u.data.free_fn_data); +*/ + } + + free (attach); +} + +int +icalattach_get_is_url (icalattach *attach) +{ + icalerror_check_arg_rz ((attach != NULL), "attach"); + + return attach->is_url ? 1 : 0; +} + +const char * +icalattach_get_url (icalattach *attach) +{ + icalerror_check_arg_rz ((attach != NULL), "attach"); + icalerror_check_arg_rz ((attach->is_url), "attach->is_url"); + + return attach->u.url.url; +} + +unsigned char * +icalattach_get_data (icalattach *attach) +{ + icalerror_check_arg_rz ((attach != NULL), "attach"); + icalerror_check_arg_rz ((!attach->is_url), "!attach->is_url"); + + return attach->u.data.data; +} diff --git a/libkcal/local.desktop b/libkcal/local.desktop index 66956592..9cf91413 100644 --- a/libkcal/local.desktop +++ b/libkcal/local.desktop @@ -26,7 +26,6 @@ Name[hu]=Helyi fájlban tárolt naptár Name[is]=Dagatal í staðbundinni skrá Name[it]=Calendario in file locale Name[ja]=ローカルファイルのカレンダー -Name[ka]=კალენდარი ლოკალურ ფაილში Name[kk]=Жергілікті файлдағы күнтізбе Name[km]=ប្រតិទិន​នៅ​ក្នុង​ឯកសារ​មូលដ្ឋាន Name[lt]=Kalendorius vietinėje byloje diff --git a/libkcal/localdir.desktop b/libkcal/localdir.desktop index f65fe745..d7e4b413 100644 --- a/libkcal/localdir.desktop +++ b/libkcal/localdir.desktop @@ -26,7 +26,6 @@ Name[hu]=Helyi könyvtárban tárolt naptár Name[is]=Dagatal í staðbundinni möppu Name[it]=Calendario nella directory locale Name[ja]=ローカルディレクトリのカレンダー -Name[ka]=კალენდარი ლოკალურ დირექტორიაში Name[kk]=Жергілікті каталогтағы күнтізбе Name[km]=ប្រតិទិន​នៅ​ក្នុង​ថត​មូលដ្ឋាន Name[lt]=Kalendorius vietiniame aplanke diff --git a/libkcal/period.cpp b/libkcal/period.cpp index be07d7d3..ff944ee9 100644 --- a/libkcal/period.cpp +++ b/libkcal/period.cpp @@ -51,6 +51,14 @@ bool Period::operator<( const Period& other ) return start() < other.start(); } +bool Period::operator==( const Period &other ) const +{ + return + mStart == other.mStart && + mEnd == other.mEnd && + mHasDuration == other.mHasDuration; +} + TQDateTime Period::start() const { return mStart; diff --git a/libkcal/period.h b/libkcal/period.h index 64dd24d9..b0ca32a3 100644 --- a/libkcal/period.h +++ b/libkcal/period.h @@ -42,6 +42,24 @@ class KDE_EXPORT Period /** Returns true if this element is smaller than the @param other one */ bool operator<( const Period& other ); + /** + Returns true if this period is equal to the @p other one. + Even if their start and end times are the same, two periods are + considered not equal if one is defined in terms of a duration and the + other in terms of a start and end time. + + @param other the other period to compare + */ + bool operator==( const Period &other ) const; + + /** + Returns true if this period is not equal to the @p other one. + + @param other the other period to compare + @see operator==() + */ + bool operator!=( const Period &other ) const { return !operator==( other ); } + TQDateTime start() const; TQDateTime end() const; Duration duration(); diff --git a/libkcal/recurrence.cpp b/libkcal/recurrence.cpp index c2d7897a..ae47db32 100644 --- a/libkcal/recurrence.cpp +++ b/libkcal/recurrence.cpp @@ -34,7 +34,6 @@ using namespace KCal; - Recurrence::Recurrence() : mFloating( false ), mRecurReadOnly(false), @@ -282,9 +281,8 @@ bool Recurrence::recursOn(const TQDate &qd) const if ( mRDates.contains( qd ) ) return true; - // Check if it might recur today at all. bool recurs = false; - if ( startDate() == qd ) recurs = true; + for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) { recurs = recurs || (*rr)->recursOn( qd ); } @@ -770,10 +768,74 @@ TimeList Recurrence::recurTimesOn( const TQDate &date ) const return times; } +DateTimeList Recurrence::timesInInterval( const TQDateTime &start, const TQDateTime &end ) const +{ + int i, count; + DateTimeList times; + for ( i = 0, count = mRRules.count(); i < count; ++i ) { + times += mRRules[i]->timesInInterval( start, end ); + } + + // add rdatetimes that fit in the interval + for ( i = 0, count = mRDateTimes.count(); i < count; ++i ) { + if ( mRDateTimes[i] >= start && mRDateTimes[i] <= end ) { + times += mRDateTimes[i]; + } + } + + // add rdates that fit in the interval + TQDateTime qdt( mStartDateTime ); + for ( i = 0, count = mRDates.count(); i < count; ++i ) { + qdt.setDate( mRDates[i] ); + if ( qdt >= start && qdt <= end ) { + times += qdt; + } + } + + // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list + // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include + // mStartDateTime. + // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly + // add mStartDateTime to the list, otherwise we won't see the first occurrence. + if ( ( !mRDates.isEmpty() || !mRDateTimes.isEmpty() ) && + mRRules.isEmpty() && + start <= mStartDateTime && + end >= mStartDateTime ) { + times += mStartDateTime; + } + + qSortUnique( times ); + + // Remove excluded times + int idt = 0; + int enddt = times.count(); + for ( i = 0, count = mExDates.count(); i < count && idt < enddt; ++i ) { + while ( idt < enddt && times[idt].date() < mExDates[i] ) ++idt; + while ( idt < enddt && times[idt].date() == mExDates[i] ) { + times.remove( times.at( idt ) ); + --enddt; + } + } + DateTimeList extimes; + for ( i = 0, count = mExRules.count(); i < count; ++i ) { + extimes += mExRules[i]->timesInInterval( start, end ); + } + extimes += mExDateTimes; + qSortUnique( extimes ); + + int st = 0; + for ( i = 0, count = extimes.count(); i < count; ++i ) { + int j = removeSorted( times, extimes[i], st ); + if ( j >= 0 ) { + st = j; + } + } + + return times; +} TQDateTime Recurrence::getNextDateTime( const TQDateTime &preDateTime ) const { -//kdDebug(5800) << " Recurrence::getNextDateTime after " << preDateTime << endl; TQDateTime nextDT = preDateTime; // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g. // the exrule is identical to the rrule). If an occurrence is found, break @@ -795,40 +857,51 @@ TQDateTime Recurrence::getNextDateTime( const TQDateTime &preDateTime ) const ++loop; // First, get the next recurrence from the RDate lists DateTimeList dates; - if ( nextDT < startDateTime() ) dates << startDateTime(); - DateTimeList::ConstIterator it = mRDateTimes.begin(); + if ( nextDT < startDateTime() ) { + dates << startDateTime(); + } + + int end; // Assume that the rdatetime list is sorted - while ( it != mRDateTimes.end() && (*it) <= nextDT ) ++it; - if ( it != mRDateTimes.end() ) dates << (*it); + int i = findGT( mRDateTimes, nextDT, 0 ); + if ( i >= 0 ) { + dates << mRDateTimes[i]; + } -/*kdDebug(5800) << " nextDT: " << nextDT << ", startDT: " << startDateTime() << endl; -kdDebug(5800) << " getNextDateTime: found " << dates.count() << " RDATES and DTSTART in loop " << loop << endl;*/ - DateList::ConstIterator dit = mRDates.begin(); - while ( dit != mRDates.end() && TQDateTime( (*dit), startDateTime().time() ) <= nextDT ) ++dit; - if ( dit != mRDates.end() ) dates << TQDateTime( (*dit), startDateTime().time() ); + TQDateTime qdt( startDateTime() ); + for ( i = 0, end = mRDates.count(); i < end; ++i ) { + qdt.setDate( mRDates[i] ); + if ( qdt > nextDT ) { + dates << qdt; + break; + } + } // Add the next occurrences from all RRULEs. - for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) { - TQDateTime dt = (*rr)->getNextDate( nextDT ); - if ( dt.isValid() ) dates << dt; + for ( i = 0, end = mRRules.count(); i < end; ++i ) { + TQDateTime dt = mRRules[i]->getNextDate( nextDT ); + if ( dt.isValid() ) { + dates << dt; + } } // Take the first of these (all others can't be used later on) qSortUnique( dates ); -// kdDebug(5800) << " getNextDateTime: found " << dates.count() << " dates in loop " << loop << endl; - - if ( dates.isEmpty() ) return TQDateTime(); + if ( dates.isEmpty() ) { + return TQDateTime(); + } nextDT = dates.first(); // Check if that date/time is excluded explicitly or by an exrule: - if ( !mExDates.contains( nextDT.date() ) && !mExDateTimes.contains( nextDT ) ) { -// kdDebug(5800) << " NextDT" << nextDT << " not excluded by EXDATE " << endl; + if ( !containsSorted( mExDates, nextDT.date() ) && + !containsSorted( mExDateTimes, nextDT ) ) { bool allowed = true; - for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) { - allowed = allowed && !( (*rr)->recursAt( nextDT ) ); + for ( i = 0, end = mExRules.count(); i < end; ++i ) { + allowed = allowed && !( mExRules[i]->recursAt( nextDT ) ); + } + if ( allowed ) { + return nextDT; } -// kdDebug(5800) << " NextDT " << nextDT << ", allowed=" << allowed << endl; - if ( allowed ) return nextDT; } } @@ -856,44 +929,50 @@ TQDateTime Recurrence::getPreviousDateTime( const TQDateTime &afterDateTime ) co ++loop; // First, get the next recurrence from the RDate lists DateTimeList dates; - if ( prevDT > startDateTime() ) dates << startDateTime(); - - DateTimeList::ConstIterator dtit = mRDateTimes.end(); - if ( dtit != mRDateTimes.begin() ) { - do { - --dtit; - } while ( dtit != mRDateTimes.begin() && (*dtit) >= prevDT ); - if ( (*dtit) < prevDT ) dates << (*dtit); + if ( prevDT > startDateTime() ) { + dates << startDateTime(); + } + + int i = findLT( mRDateTimes, prevDT, 0 ); + if ( i >= 0 ) { + dates << mRDateTimes[i]; } - DateList::ConstIterator dit = mRDates.end(); - if ( dit != mRDates.begin() ) { - do { - --dit; - } while ( dit != mRDates.begin() && TQDateTime((*dit), startDateTime().time()) >= prevDT ); - if ( TQDateTime((*dit), startDateTime().time()) < prevDT ) - dates << TQDateTime( (*dit), startDateTime().time() ); + TQDateTime qdt( startDateTime() ); + for ( i = mRDates.count(); --i >= 0; ) { + qdt.setDate( mRDates[i] ); + if ( qdt < prevDT ) { + dates << qdt; + break; + } } // Add the previous occurrences from all RRULEs. - for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) { - TQDateTime dt = (*rr)->getPreviousDate( prevDT ); - if ( dt.isValid() ) dates << dt; + int end; + for ( i = 0, end = mRRules.count(); i < end; ++i ) { + TQDateTime dt = mRRules[i]->getPreviousDate( prevDT ); + if ( dt.isValid() ) { + dates << dt; + } } -//kdDebug(5800) << " getPreviousDateTime: found " << dates.count() << " dates in loop " << loop << endl; // Take the last of these (all others can't be used later on) qSortUnique( dates ); - if ( dates.isEmpty() ) return TQDateTime(); + if ( dates.isEmpty() ) { + return TQDateTime(); + } prevDT = dates.last(); // Check if that date/time is excluded explicitly or by an exrule: - if ( !mExDates.contains( prevDT.date() ) && !mExDateTimes.contains( prevDT ) ) { + if ( !containsSorted( mExDates, prevDT.date() ) && + !containsSorted( mExDateTimes, prevDT ) ) { bool allowed = true; - for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) { - allowed = allowed && !( (*rr)->recursAt( prevDT ) ); + for ( i = 0, end = mExRules.count(); i < end; ++i ) { + allowed = allowed && !( mExRules[i]->recursAt( prevDT ) ); + } + if ( allowed ) { + return prevDT; } - if ( allowed ) return prevDT; } } diff --git a/libkcal/recurrence.h b/libkcal/recurrence.h index ec696597..0a594a6f 100644 --- a/libkcal/recurrence.h +++ b/libkcal/recurrence.h @@ -166,6 +166,21 @@ class LIBKCAL_EXPORT Recurrence : public RecurrenceRule::Observer */ TQValueList recurTimesOn(const TQDate &date) const; + /** Returns a list of all the times at which the recurrence will occur + * between two specified times. + * + * There is a (large) maximum limit to the number of times returned. If due to + * this limit the list is incomplete, this is indicated by the last entry being + * set to an invalid TQDateTime value. If you need further values, call the + * method again with a start time set to just after the last valid time returned. + * + * @param start inclusive start of interval + * @param end inclusive end of interval + * @return list of date/time values + */ + DateTimeList timesInInterval( const TQDateTime &start, const TQDateTime &end ) const; + + /** Returns the date and time of the next recurrence, after the specified date/time. * If the recurrence has no time, the next date after the specified date is returned. * @param preDateTime the date/time after which to find the recurrence. diff --git a/libkcal/recurrencerule.cpp b/libkcal/recurrencerule.cpp index ce64aadc..17292ec5 100644 --- a/libkcal/recurrencerule.cpp +++ b/libkcal/recurrencerule.cpp @@ -32,6 +32,8 @@ using namespace KCal; +// Maximum number of intervals to process +const int LOOP_LIMIT = 10000; // FIXME: If Qt is ever changed so that TQDateTime:::addSecs takes into account // DST shifts, we need to use our own addSecs method, too, since we @@ -774,6 +776,8 @@ void RecurrenceRule::setWeekStart( short weekStart ) void RecurrenceRule::buildConstraints() { + mTimedRepetition = 0; + mNoByRules = mBySetPos.isEmpty(); mConstraints.clear(); Constraint con; if ( mWeekStart > 0 ) con.weekstart = mWeekStart; @@ -785,6 +789,7 @@ void RecurrenceRule::buildConstraints() #define intConstraint( list, element ) \ if ( !list.isEmpty() ) { \ + mNoByRules = false; \ for ( it = mConstraints.constBegin(); it != mConstraints.constEnd(); ++it ) { \ for ( intit = list.constBegin(); intit != list.constEnd(); ++intit ) { \ con = (*it); \ @@ -806,6 +811,7 @@ void RecurrenceRule::buildConstraints() #undef intConstraint if ( !mByDays.isEmpty() ) { + mNoByRules = false; for ( it = mConstraints.constBegin(); it != mConstraints.constEnd(); ++it ) { TQValueList::const_iterator dayit; for ( dayit = mByDays.constBegin(); dayit != mByDays.constEnd(); ++dayit ) { @@ -866,12 +872,28 @@ void RecurrenceRule::buildConstraints() } #undef fixConstraint - Constraint::List::Iterator conit = mConstraints.begin(); - while ( conit != mConstraints.end() ) { - if ( (*conit).isConsistent( mPeriod ) ) { - ++conit; - } else { - conit = mConstraints.remove( conit ); + if ( mNoByRules ) { + switch ( mPeriod ) { + case rHourly: + mTimedRepetition = mFrequency * 3600; + break; + case rMinutely: + mTimedRepetition = mFrequency * 60; + break; + case rSecondly: + mTimedRepetition = mFrequency; + break; + default: + break; + } + } else { + Constraint::List::Iterator conit = mConstraints.begin(); + while ( conit != mConstraints.end() ) { + if ( (*conit).isConsistent( mPeriod ) ) { + ++conit; + } else { + conit = mConstraints.remove( conit ); + } } } } @@ -941,81 +963,179 @@ bool RecurrenceRule::dateMatchesRules( const TQDateTime &qdt ) const bool RecurrenceRule::recursOn( const TQDate &qd ) const { -// kdDebug(5800) << " RecurrenceRule::recursOn: " << qd << endl; - if ( qd < startDt().date() ) return false; + int i, iend; + if ( doesFloat() ) { + // It's a date-only rule, so it has no time specification. + if ( qd < mDateStart.date() ) { + return false; + } + // Start date is only included if it really matches + TQDate endDate; + if ( mDuration >= 0 ) { + endDate = endDt().date(); + if ( qd > endDate ) { + return false; + } + } + + // The date must be in an appropriate interval (getNextValidDateInterval), + // Plus it must match at least one of the constraints + bool match = false; + for ( i = 0, iend = mConstraints.count(); i < iend && !match; ++i ) { + match = mConstraints[i].matches( qd, recurrenceType() ); + } + if ( !match ) { + return false; + } + + TQDateTime start( qd, TQTime( 0, 0, 0 ) ); + Constraint interval( getNextValidDateInterval( start, recurrenceType() ) ); + // Constraint::matches is quite efficient, so first check if it can occur at + // all before we calculate all actual dates. + if ( !interval.matches( qd, recurrenceType() ) ) { + return false; + } + // We really need to obtain the list of dates in this interval, since + // otherwise BYSETPOS will not work (i.e. the date will match the interval, + // but BYSETPOS selects only one of these matching dates! + TQDateTime end = start.addDays(1); + do { + DateTimeList dts = datesForInterval( interval, recurrenceType() ); + for ( i = 0, iend = dts.count(); i < iend; ++i ) { + if ( dts[i].date() >= qd ) { + return dts[i].date() == qd; + } + } + interval.increase( recurrenceType(), frequency() ); + } while ( interval.intervalDateTime( recurrenceType() ) < end ); + return false; + } + + // It's a date-time rule, so we need to take the time specification into account. + TQDateTime start( qd, TQTime( 0, 0, 0 ) ); + TQDateTime end = start.addDays( 1 ); + if ( end < mDateStart ) { + return false; + } + if ( start < mDateStart ) { + start = mDateStart; + } + // Start date is only included if it really matches -// if ( qd == startDt().date() ) return true; - if ( mDuration >= 0 && qd > endDt().date() ) return false; + if ( mDuration >= 0 ) { + TQDateTime endRecur = endDt(); + if ( endRecur.isValid() ) { + if ( start > endRecur ) { + return false; + } + if ( end > endRecur ) { + end = endRecur; // limit end-of-day time to end of recurrence rule + } + } + } + + if ( mTimedRepetition ) { + // It's a simple sub-daily recurrence with no constraints + int n = static_cast( ( mDateStart.secsTo( start ) - 1 ) % mTimedRepetition ); + return start.addSecs( mTimedRepetition - n ) < end; + } + + // Find the start and end dates in the time spec for the rule + TQDate startDay = start.date(); + TQDate endDay = end.addSecs( -1 ).date(); + int dayCount = startDay.daysTo( endDay ) + 1; // The date must be in an appropriate interval (getNextValidDateInterval), // Plus it must match at least one of the constraints bool match = false; - for ( Constraint::List::ConstIterator it = mConstraints.begin(); - it!=mConstraints.end(); ++it ) { - match = match || ( (*it).matches( qd, recurrenceType() ) ); + for ( i = 0, iend = mConstraints.count(); i < iend && !match; ++i ) { + match = mConstraints[i].matches( startDay, recurrenceType() ); + for ( int day = 1; day < dayCount && !match; ++day ) { + match = mConstraints[i].matches( startDay.addDays( day ), recurrenceType() ); + } + } + if ( !match ) { + return false; } - if ( !match ) return false; - TQDateTime tmp( qd, TQTime( 0, 0, 0 ) ); - Constraint interval( getNextValidDateInterval( tmp, recurrenceType() ) ); + + Constraint interval( getNextValidDateInterval( start, recurrenceType() ) ); // Constraint::matches is quite efficient, so first check if it can occur at // all before we calculate all actual dates. - if ( !interval.matches( qd, recurrenceType() ) ) return false; + match = false; + Constraint intervalm = interval; + do { + match = intervalm.matches( startDay, recurrenceType() ); + for ( int day = 1; day < dayCount && !match; ++day ) { + match = intervalm.matches( startDay.addDays( day ), recurrenceType() ); + } + if ( match ) { + break; + } + intervalm.increase( recurrenceType(), frequency() ); + } while ( intervalm.intervalDateTime( recurrenceType() ) < end ); + if ( !match ) { + return false; + } + // We really need to obtain the list of dates in this interval, since // otherwise BYSETPOS will not work (i.e. the date will match the interval, // but BYSETPOS selects only one of these matching dates! - DateTimeList times = datesForInterval( interval, recurrenceType() ); - DateTimeList::ConstIterator it = times.begin(); - while ( ( it != times.end() ) && ( (*it).date() < qd ) ) - ++it; - if ( it != times.end() ) { - // If we are beyond the end... - if ( mDuration >= 0 && (*it) > endDt() ) - return false; - if ( (*it).date() == qd ) - return true; - } + do { + DateTimeList dts = datesForInterval( interval, recurrenceType() ); + int i = findGE( dts, start, 0 ); + if ( i >= 0 ) { + return dts[i] < end; + } + interval.increase( recurrenceType(), frequency() ); + } while ( interval.intervalDateTime( recurrenceType() ) < end ); + return false; } - -bool RecurrenceRule::recursAt( const TQDateTime &qd ) const +bool RecurrenceRule::recursAt( const TQDateTime &dt ) const { -// kdDebug(5800) << " RecurrenceRule::recursAt: " << qd << endl; - if ( doesFloat() ) return recursOn( qd.date() ); - if ( qd < startDt() ) return false; + if ( doesFloat() ) { + return recursOn( dt.date() ); + } + if ( dt < mDateStart ) { + return false; + } // Start date is only included if it really matches -// if ( qd == startDt() ) return true; - if ( mDuration >= 0 && qd > endDt() ) return false; + if ( mDuration >= 0 && dt > endDt() ) { + return false; + } + + if ( mTimedRepetition ) { + // It's a simple sub-daily recurrence with no constraints + return !( mDateStart.secsTo( dt ) % mTimedRepetition ); + } // The date must be in an appropriate interval (getNextValidDateInterval), // Plus it must match at least one of the constraints - bool match = dateMatchesRules( qd ); - if ( !match ) return false; + if ( !dateMatchesRules( dt ) ) { + return false; + } // if it recurs every interval, speed things up... -// if ( mFrequency == 1 && mBySetPos.isEmpty() && mByDays.isEmpty() ) return true; - Constraint interval( getNextValidDateInterval( qd, recurrenceType() ) ); +// if ( d->mFrequency == 1 && d->mBySetPos.isEmpty() && d->mByDays.isEmpty() ) return true; + Constraint interval( getNextValidDateInterval( dt, recurrenceType() ) ); // TODO_Recurrence: Does this work with BySetPos??? - if ( interval.matches( qd, recurrenceType() ) ) return true; - + if ( interval.matches( dt, recurrenceType() ) ) { + return true; + } return false; } - TimeList RecurrenceRule::recurTimesOn( const TQDate &date ) const { -// kdDebug(5800) << " RecurrenceRule::recurTimesOn: " << date << endl; TimeList lst; - if ( !recursOn( date ) ) return lst; - - if ( doesFloat() ) return lst; - - TQDateTime dt( date, TQTime( 0, 0, 0 ) ); - bool valid = dt.isValid() && ( dt.date() == date ); - while ( valid ) { - // TODO: Add a flag so that the date is never increased! - dt = getNextDate( dt ); - valid = dt.isValid() && ( dt.date() == date ); - if ( valid ) lst.append( dt.time() ); + if ( doesFloat() ) { + return lst; + } + TQDateTime start( date, TQTime( 0, 0, 0 ) ); + TQDateTime end = start.addDays( 1 ).addSecs( -1 ); + DateTimeList dts = timesInInterval( start, end ); // returns between start and end inclusive + for ( int i = 0, iend = dts.count(); i < iend; ++i ) { + lst += dts[i].time(); } return lst; } @@ -1150,6 +1270,104 @@ TQDateTime RecurrenceRule::getNextDate( const TQDateTime &preDate ) const return TQDateTime(); } +DateTimeList RecurrenceRule::timesInInterval( const TQDateTime &dtStart, + const TQDateTime &dtEnd ) const +{ + TQDateTime start = dtStart; + TQDateTime end = dtEnd; + DateTimeList result; + if ( end < mDateStart ) { + return result; // before start of recurrence + } + TQDateTime enddt = end; + if ( mDuration >= 0 ) { + TQDateTime endRecur = endDt(); + if ( endRecur.isValid() ) { + if ( start > endRecur ) { + return result; // beyond end of recurrence + } + if ( end > endRecur ) { + enddt = endRecur; // limit end time to end of recurrence rule + } + } + } + + if ( mTimedRepetition ) { + // It's a simple sub-daily recurrence with no constraints + int n = static_cast( ( mDateStart.secsTo( start ) - 1 ) % mTimedRepetition ); + TQDateTime dt = start.addSecs( mTimedRepetition - n ); + if ( dt < enddt ) { + n = static_cast( ( dt.secsTo( enddt ) - 1 ) / mTimedRepetition ) + 1; + // limit n by a sane value else we can "explode". + n = QMIN( n, LOOP_LIMIT ); + for ( int i = 0; i < n; dt = dt.addSecs( mTimedRepetition ), ++i ) { + result += dt; + } + } + return result; + } + + TQDateTime st = start; + bool done = false; + if ( mDuration > 0 ) { + if ( !mCached ) { + buildCache(); + } + if ( mCachedDateEnd.isValid() && start > mCachedDateEnd ) { + return result; // beyond end of recurrence + } + int i = findGE( mCachedDates, start, 0 ); + if ( i >= 0 ) { + int iend = findGT( mCachedDates, enddt, i ); + if ( iend < 0 ) { + iend = mCachedDates.count(); + } else { + done = true; + } + while ( i < iend ) { + result += mCachedDates[i++]; + } + } + if ( mCachedDateEnd.isValid() ) { + done = true; + } else if ( !result.isEmpty() ) { + result += TQDateTime(); // indicate that the returned list is incomplete + done = true; + } + if ( done ) { + return result; + } + // We don't have any result yet, but we reached the end of the incomplete cache + st = mCachedLastDate.addSecs( 1 ); + } + + Constraint interval( getNextValidDateInterval( st, recurrenceType() ) ); + int loop = 0; + do { + DateTimeList dts = datesForInterval( interval, recurrenceType() ); + int i = 0; + int iend = dts.count(); + if ( loop == 0 ) { + i = findGE( dts, st, 0 ); + if ( i < 0 ) { + i = iend; + } + } + int j = findGT( dts, enddt, i ); + if ( j >= 0 ) { + iend = j; + loop = LOOP_LIMIT; + } + while ( i < iend ) { + result += dts[i++]; + } + // Increase the interval. + interval.increase( recurrenceType(), frequency() ); + } while ( ++loop < LOOP_LIMIT && + interval.intervalDateTime( recurrenceType() ) < end ); + return result; +} + RecurrenceRule::Constraint RecurrenceRule::getPreviousValidDateInterval( const TQDateTime &preDate, PeriodType type ) const { // kdDebug(5800) << " (o) getPreviousValidDateInterval after " << preDate << ", type=" << type << endl; diff --git a/libkcal/recurrencerule.h b/libkcal/recurrencerule.h index 049d9c52..86b8ca8e 100644 --- a/libkcal/recurrencerule.h +++ b/libkcal/recurrencerule.h @@ -50,6 +50,109 @@ Q_INLINE_TEMPLATES void qSortUnique( TQValueList &lst ) } } +template +Q_INLINE_TEMPLATES int findGE( const TQValueList &lst, const T &value, int start ) +{ + // Do a binary search to find the first item >= value + int st = start - 1; + int end = lst.count(); + while ( end - st > 1 ) { + int i = ( st + end ) / 2; + if ( value <= lst[i] ) { + end = i; + } else { + st = i; + } + } + ++st; + return ( st == int( lst.count() ) ) ? -1 : st; +} + +template +Q_INLINE_TEMPLATES int findGT( const TQValueList &lst, const T &value, int start ) +{ + // Do a binary search to find the first item > value + int st = start - 1; + int end = lst.count(); + while ( end - st > 1 ) { + int i = ( st + end ) / 2; + if ( value < lst[i] ) { + end = i; + } else { + st = i; + } + } + ++st; + return ( st == int( lst.count() ) ) ? -1 : st; +} + +template +Q_INLINE_TEMPLATES int findLE( const TQValueList &lst, const T &value, int start ) +{ + // Do a binary search to find the last item <= value + int st = start - 1; + int end = lst.count(); + while ( end - st > 1 ) { + int i = ( st + end ) / 2; + if ( value < lst[i] ) { + end = i; + } else { + st = i; + } + } + return ( end > start ) ? st : -1; +} + +template +Q_INLINE_TEMPLATES int findLT( const TQValueList &lst, const T &value, int start ) +{ + // Do a binary search to find the last item < value + int st = start - 1; + int end = lst.count(); + while ( end - st > 1 ) { + int i = ( st + end ) / 2; + if ( value <= lst[i] ) { + end = i; + } else { + st = i; + } + } + return ( end > start ) ? st : -1; +} + +template +Q_INLINE_TEMPLATES int findSorted( const TQValueList &lst, const T &value, int start ) +{ + // Do a binary search to find the item == value + int st = start - 1; + int end = lst.count(); + while ( end - st > 1 ) { + int i = ( st + end ) / 2; + if ( value < lst[i] ) { + end = i; + } else { + st = i; + } + } + return ( end > start && value == lst[st] ) ? st : -1; +} + +template +Q_INLINE_TEMPLATES int removeSorted( TQValueList &lst, const T &value, int start ) +{ + int i = findSorted( lst, value, start ); + if ( i >= 0 ) { + lst.remove( lst.at( i ) ); + } + return i; +} + +template +Q_INLINE_TEMPLATES bool containsSorted( const TQValueList &lst, const T &value ) +{ + return findSorted( lst, value, 0 ) >= 0; +} + namespace KCal { @@ -188,6 +291,18 @@ class LIBKCAL_EXPORT RecurrenceRule */ TimeList recurTimesOn( const TQDate &date ) const; + /** Returns a list of all the times at which the recurrence will occur + * between two specified times. + * + * There is a (large) maximum limit to the number of times returned. If due to + * this limit the list is incomplete, this is indicated by the last entry being + * set to an invalid KDateTime value. If you need further values, call the + * method again with a start time set to just after the last valid time returned. + * @param start inclusive start of interval + * @param end inclusive end of interval + * @return list of date/time values + */ + DateTimeList timesInInterval( const TQDateTime &start, const TQDateTime &end ) const; /** Returns the date and time of the next recurrence, after the specified date/time. * If the recurrence has no time, the next date after the specified date is returned. @@ -331,8 +446,12 @@ class LIBKCAL_EXPORT RecurrenceRule // Cache for duration mutable DateTimeList mCachedDates; - mutable bool mCached; mutable TQDateTime mCachedDateEnd; + mutable TQDateTime mCachedLastDate; // when mCachedDateEnd invalid, last date checked + mutable bool mCached; + + bool mNoByRules; // no BySeconds, ByMinutes, ... rules exist + uint mTimedRepetition; // repeats at a regular number of seconds interval, or 0 class Private; Private *d; diff --git a/libkcal/resourcecached.cpp b/libkcal/resourcecached.cpp index 1a4e8b19..dacab7e9 100644 --- a/libkcal/resourcecached.cpp +++ b/libkcal/resourcecached.cpp @@ -50,7 +50,7 @@ static bool m_editoropen = false; ResourceCached::ResourceCached( const KConfig* config ) : ResourceCalendar( config ), mCalendar( TQString::fromLatin1( "UTC" ) ), - mReloadPolicy( ReloadNever ), mReloadInterval( 10 ), + mReloadPolicy( ReloadNever ), mReloadInterval( 10 ), mReloadTimer( 0, "mReloadTimer" ), mReloaded( false ), mSavePolicy( SaveNever ), mSaveInterval( 10 ), mSaveTimer( 0, "mSaveTimer" ), mIdMapper( "kcal/uidmaps/" ) @@ -161,6 +161,12 @@ bool ResourceCached::addEvent(Event *event) return mCalendar.addEvent( event ); } +bool ResourceCached::addEvent(Event *event, const TQString &subresource ) +{ + Q_UNUSED( subresource ); // CalendarLocal does not support subresources + return mCalendar.addEvent( event ); +} + // probably not really efficient, but...it works for now. bool ResourceCached::deleteEvent( Event *event ) { @@ -205,6 +211,12 @@ bool ResourceCached::addTodo( Todo *todo ) return mCalendar.addTodo( todo ); } +bool ResourceCached::addTodo( Todo *todo, const TQString &subresource ) +{ + Q_UNUSED( subresource ); // CalendarLocal does not support subresources + return mCalendar.addTodo( todo ); +} + bool ResourceCached::deleteTodo( Todo *todo ) { return mCalendar.deleteTodo( todo ); @@ -231,11 +243,14 @@ Todo::List ResourceCached::rawTodosForDate( const TQDate &date ) return mCalendar.rawTodosForDate( date ); } - bool ResourceCached::addJournal( Journal *journal ) { - kdDebug(5800) << "Adding Journal on " << journal->dtStart().toString() << endl; + return mCalendar.addJournal( journal ); +} +bool ResourceCached::addJournal( Journal *journal, const TQString &subresource ) +{ + Q_UNUSED( subresource ); // CalendarLocal does not support subresources return mCalendar.addJournal( journal ); } diff --git a/libkcal/resourcecached.h b/libkcal/resourcecached.h index 60976698..74fcf5d6 100644 --- a/libkcal/resourcecached.h +++ b/libkcal/resourcecached.h @@ -136,7 +136,9 @@ class KDE_EXPORT ResourceCached : public ResourceCalendar, /** Add event to calendar. */ - bool addEvent(Event *anEvent); + KDE_DEPRECATED bool addEvent( Event *event ); + bool addEvent( Event *event, const TQString &subresource ); + /** Deletes an event from this calendar. */ @@ -174,7 +176,9 @@ class KDE_EXPORT ResourceCached : public ResourceCalendar, /** Add a todo to the todolist. */ - bool addTodo( Todo *todo ); + KDE_DEPRECATED bool addTodo( Todo *todo ); + bool addTodo( Todo *todo, const TQString &subresource ); + /** Remove a todo from the todolist. */ @@ -195,15 +199,17 @@ class KDE_EXPORT ResourceCached : public ResourceCalendar, /** Add a Journal entry to calendar */ - virtual bool addJournal( Journal * ); + KDE_DEPRECATED bool addJournal( Journal *journal ); + bool addJournal( Journal *journal, const TQString &subresource ); + /** Remove a Journal from the calendar */ - virtual bool deleteJournal( Journal * ); + bool deleteJournal( Journal * ); /** Return Journal with given unique id. */ - virtual Journal *journal( const TQString &uid ); + Journal *journal( const TQString &uid ); /** Return list of all journals. */ diff --git a/libkcal/resourcecalendar.cpp b/libkcal/resourcecalendar.cpp index c008f030..9f611e75 100644 --- a/libkcal/resourcecalendar.cpp +++ b/libkcal/resourcecalendar.cpp @@ -33,18 +33,38 @@ using namespace KCal; ResourceCalendar::ResourceCalendar( const KConfig *config ) - : KRES::Resource( config ),mResolveConflict( false ) + : KRES::Resource( config ), mResolveConflict( false ) { + mException = 0; } ResourceCalendar::~ResourceCalendar() { + delete mException; +} + +void ResourceCalendar::clearException() +{ + delete mException; + mException = 0; +} + +void ResourceCalendar::setException( ErrorFormat *exception ) +{ + delete mException; + mException = exception; +} + +ErrorFormat *ResourceCalendar::exception() +{ + return mException; } void ResourceCalendar::setResolveConflict( bool b) { mResolveConflict = b; } + TQString ResourceCalendar::infoText() const { TQString txt; @@ -84,6 +104,12 @@ bool ResourceCalendar::addIncidence( Incidence *incidence ) return incidence->accept( v ); } +bool ResourceCalendar::addIncidence( Incidence *incidence, const TQString &subresource ) +{ + Incidence::AddSubResourceVisitor v( this, subresource ); + return incidence->accept( v ); +} + bool ResourceCalendar::deleteIncidence( Incidence *incidence ) { Incidence::DeleteVisitor v( this ); @@ -192,6 +218,8 @@ void ResourceCalendar::saveError( const TQString &err ) bool ResourceCalendar::setValue( const TQString &key, const TQString &value ) { + Q_UNUSED( key ); + Q_UNUSED( value ); return false; } @@ -201,5 +229,21 @@ TQString ResourceCalendar::subresourceType( const TQString &resource ) return TQString(); } +bool ResourceCalendar::subresourceWritable( const TQString &resource ) const +{ + if ( resource.isEmpty() ) { + return !readOnly(); + } else { + return false; + } +} + +void ResourceCalendar::beginAddingIncidences() +{ +} + +void ResourceCalendar::endAddingIncidences() +{ +} #include "resourcecalendar.moc" diff --git a/libkcal/resourcecalendar.h b/libkcal/resourcecalendar.h index 3fea84a1..91f3d1ae 100644 --- a/libkcal/resourcecalendar.h +++ b/libkcal/resourcecalendar.h @@ -36,6 +36,7 @@ #include "event.h" #include "journal.h" #include "calendar.h" +#include "exceptions.h" #include #include @@ -55,11 +56,28 @@ class CalFormat; */ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource { - Q_OBJECT + Q_OBJECT public: ResourceCalendar( const KConfig * ); virtual ~ResourceCalendar(); + /** + Clears the exception status. + */ + void clearException(); + + /** + Set exception for this object. This is used by the functions of this + class to report errors. + */ + void setException( ErrorFormat *error ); + + /** + Returns an exception, if there is any, containing information about the + last error that occurred. + */ + ErrorFormat *exception(); + void setResolveConflict( bool b); virtual void writeConfig( KConfig* config ); @@ -123,8 +141,14 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource /** Add incidence to resource. + @deprecated use addIncidence(Incidence *,const TQString &) instead. + */ + virtual KDE_DEPRECATED bool addIncidence( Incidence * ); + + /** + Add incidence to resource and subresource. */ - virtual bool addIncidence( Incidence * ); + virtual bool addIncidence( Incidence *, const TQString &subresource ); /** Delete incidence from resource. @@ -139,8 +163,10 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource /** Add event to resource. + @deprecated use addEvent(Event *,const TQString&) instead. */ - virtual bool addEvent( Event *event ) = 0; + virtual KDE_DEPRECATED bool addEvent( Event *event ) = 0; + virtual bool addEvent( Event *event, const TQString &subresource ) = 0; /** Delete event from this resource. @@ -241,8 +267,11 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource public: /** Add a todo to the todolist. + @deprecated use addTodo(Todo *,const TQString &) instead. */ - virtual bool addTodo( Todo *todo ) = 0; + virtual KDE_DEPRECATED bool addTodo( Todo *todo ) = 0; + virtual bool addTodo( Todo *todo, const TQString &subresource ) = 0; + /** Remove a todo from the todolist. */ @@ -265,8 +294,10 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource /** Add a Journal entry to the resource. + @deprecated use addJournal(Journal *,const TQString &) instead. */ - virtual bool addJournal( Journal * ) = 0; + virtual KDE_DEPRECATED bool addJournal( Journal * ) = 0; + virtual bool addJournal( Journal *journal, const TQString &subresource ) = 0; /** Remove a Journal entry from calendar. @@ -323,6 +354,11 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource */ virtual bool subresourceActive( const TQString& ) const { return true; } + /** + Is this subresource writable or not? + */ + virtual bool subresourceWritable( const TQString& ) const; + /** What is the label for this subresource? */ @@ -360,6 +396,18 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource */ virtual TQString subresourceType( const TQString &resource ); + /** + * Called when we starting adding a batch of incidences. + * So we don't show the same warnings for each incidence. + */ + virtual void beginAddingIncidences(); + + /** + * Called when we finish adding a batch of incidences. + * @see beginAddingIncidences() + */ + virtual void endAddingIncidences(); + public slots: /** (De-)activate a subresource. @@ -402,6 +450,8 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource bool mReceivedLoadError; bool mReceivedSaveError; + ErrorFormat *mException; + class Private; Private *d; }; diff --git a/libkcal/resourcelocaldir.cpp b/libkcal/resourcelocaldir.cpp index 1d5c9caa..f1e00f32 100644 --- a/libkcal/resourcelocaldir.cpp +++ b/libkcal/resourcelocaldir.cpp @@ -128,7 +128,7 @@ bool ResourceLocalDir::doLoad() TQString dirName = mURL.path(); if ( !( KStandardDirs::exists( dirName ) || KStandardDirs::exists( dirName + "/") ) ) { - kdDebug(5800) << "ResourceLocalDir::load(): Directory '" << dirName + kdDebug(5800) << "ResourceLocalDir::load(): Directory '" << dirName << "' doesn't exist yet. Creating it..." << endl; // Create the directory. Use 0775 to allow group-writable if the umask // allows it (permissions will be 0775 & ~umask). This is desired e.g. for @@ -139,7 +139,7 @@ bool ResourceLocalDir::doLoad() // The directory exists. Now try to open (the files in) it. kdDebug(5800) << "ResourceLocalDir::load(): '" << dirName << "'" << endl; TQFileInfo dirInfo( dirName ); - if ( !( dirInfo.isDir() && dirInfo.isReadable() && + if ( !( dirInfo.isDir() && dirInfo.isReadable() && ( dirInfo.isWritable() || readOnly() ) ) ) return false; @@ -193,6 +193,11 @@ bool ResourceLocalDir::doSave() bool ResourceLocalDir::doSave( Incidence *incidence ) { + if ( mDeletedIncidences.contains( incidence ) ) { + mDeletedIncidences.remove( incidence ); + return true; + } + mDirWatch.stopScan(); // do prohibit the dirty() signal and a following reload() TQString fileName = mURL.path() + "/" + incidence->uid(); @@ -231,28 +236,46 @@ void ResourceLocalDir::reload( const TQString &file ) bool ResourceLocalDir::deleteEvent(Event *event) { kdDebug(5800) << "ResourceLocalDir::deleteEvent" << endl; - if ( deleteIncidenceFile(event) ) - return( mCalendar.deleteEvent( event ) ); - else - return( false ); + if ( deleteIncidenceFile(event) ) { + if ( mCalendar.deleteEvent( event ) ) { + mDeletedIncidences.append( event ); + return true; + } else { + return false; + } + } else { + return false; + } } bool ResourceLocalDir::deleteTodo(Todo *todo) { - if ( deleteIncidenceFile(todo) ) - return( mCalendar.deleteTodo( todo ) ); - else - return( false ); + if ( deleteIncidenceFile(todo) ) { + if ( mCalendar.deleteTodo( todo ) ) { + mDeletedIncidences.append( todo ); + return true; + } else { + return false; + } + } else { + return false; + } } bool ResourceLocalDir::deleteJournal( Journal *journal ) { - if ( deleteIncidenceFile( journal ) ) - return( mCalendar.deleteJournal( journal ) ); - else - return( false ); + if ( deleteIncidenceFile( journal ) ) { + if ( mCalendar.deleteJournal( journal ) ) { + mDeletedIncidences.append( journal ); + return true; + } else { + return false; + } + } else { + return false; + } } diff --git a/libkcal/resourcelocaldir.h b/libkcal/resourcelocaldir.h index e6c5526a..e1316f21 100644 --- a/libkcal/resourcelocaldir.h +++ b/libkcal/resourcelocaldir.h @@ -94,6 +94,7 @@ class LIBKCAL_EXPORT ResourceLocalDir : public ResourceCached KABC::Lock *mLock; + TQPtrListmDeletedIncidences; class Private; Private *d; }; diff --git a/libkcal/resourcelocaldirconfig.cpp b/libkcal/resourcelocaldirconfig.cpp index 2c8d8983..df2412b3 100644 --- a/libkcal/resourcelocaldirconfig.cpp +++ b/libkcal/resourcelocaldirconfig.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include "vcaldrag.h" @@ -41,7 +42,7 @@ using namespace KCal; ResourceLocalDirConfig::ResourceLocalDirConfig( TQWidget* parent, const char* name ) : KRES::ConfigWidget( parent, name ) { - resize( 245, 115 ); + resize( 245, 115 ); TQGridLayout *mainLayout = new TQGridLayout( this, 2, 2 ); TQLabel *label = new TQLabel( i18n( "Location:" ), this ); @@ -65,6 +66,14 @@ void ResourceLocalDirConfig::saveSettings( KRES::Resource *resource ) ResourceLocalDir* res = static_cast( resource ); if (res) { res->mURL = mURL->url(); + if ( mURL->url().isEmpty() && !resource->readOnly() ) { + KMessageBox::information( + this, + i18n( "No location specified. The calendar will be read-only." ), + TQString(), + "ResourceLocalDirUrl" ); + resource->setReadOnly( true ); + } } else kdDebug(5700) << "ERROR: ResourceLocalDirConfig::saveSettings(): no ResourceLocalDir, cast failed" << endl; } diff --git a/libkcal/scheduler.cpp b/libkcal/scheduler.cpp index 802c977f..6e3cf385 100644 --- a/libkcal/scheduler.cpp +++ b/libkcal/scheduler.cpp @@ -25,12 +25,15 @@ #include #include +#include "calhelper.h" #include "event.h" #include "todo.h" #include "freebusy.h" #include "icalformat.h" #include "calendar.h" +#include "calendarresources.h" #include "freebusycache.h" +#include "assignmentvisitor.h" #include "scheduler.h" @@ -94,7 +97,10 @@ FreeBusyCache *Scheduler::freeBusyCache() const return d->mFreeBusyCache; } -bool Scheduler::acceptTransaction(IncidenceBase *incidence,Method method,ScheduleMessage::Status status) +bool Scheduler::acceptTransaction( IncidenceBase *incidence, + Method method, + ScheduleMessage::Status status, + const TQString &attendee ) { kdDebug(5800) << "Scheduler::acceptTransaction, method=" << methodName( method ) << endl; @@ -103,11 +109,11 @@ bool Scheduler::acceptTransaction(IncidenceBase *incidence,Method method,Schedul case Publish: return acceptPublish(incidence, status, method); case Request: - return acceptRequest(incidence, status); + return acceptRequest( incidence, status, attendee ); case Add: return acceptAdd(incidence, status); case Cancel: - return acceptCancel(incidence, status); + return acceptCancel(incidence, status, attendee ); case Declinecounter: return acceptDeclineCounter(incidence, status); case Reply: @@ -185,24 +191,28 @@ bool Scheduler::acceptPublish( IncidenceBase *newIncBase, bool res = false; kdDebug(5800) << "Scheduler::acceptPublish, status=" - << ScheduleMessage::statusName( status ) << endl; + << ScheduleMessage::statusName( status ) << endl; Incidence *newInc = static_cast( newIncBase ); Incidence *calInc = mCalendar->incidence( newIncBase->uid() ); switch ( status ) { case ScheduleMessage::Unknown: case ScheduleMessage::PublishNew: case ScheduleMessage::PublishUpdate: - res = true; - if ( calInc ) { + if ( calInc && newInc ) { if ( (newInc->revision() > calInc->revision()) || (newInc->revision() == calInc->revision() && newInc->lastModified() > calInc->lastModified() ) ) { - mCalendar->deleteIncidence( calInc ); - } else - res = false; + AssignmentVisitor visitor; + const TQString oldUid = calInc->uid(); + if ( !visitor.assign( calInc, newInc ) ) { + kdError(5800) << "assigning different incidence types" << endl; + } else { + calInc->setUid( oldUid ); + calInc->setSchedulingID( newInc->uid() ); + res = true; + } + } } - if ( res ) - mCalendar->addIncidence( newInc ); break; case ScheduleMessage::Obsolete: res = true; @@ -214,36 +224,169 @@ bool Scheduler::acceptPublish( IncidenceBase *newIncBase, return res; } -bool Scheduler::acceptRequest(IncidenceBase *newIncBase, ScheduleMessage::Status /* status */) +bool Scheduler::acceptRequest( IncidenceBase *incidence, + ScheduleMessage::Status status, + const TQString &attendee ) { - if (newIncBase->type()=="FreeBusy") { + Incidence *inc = static_cast(incidence); + if ( !inc ) + return false; + if (inc->type()=="FreeBusy") { // reply to this request is handled in korganizer's incomingdialog return true; } - Incidence *newInc = dynamic_cast( newIncBase ); - if ( newInc ) { - bool res = true; - Incidence *exInc = mCalendar->incidenceFromSchedulingID( newIncBase->uid() ); - if ( exInc ) { - res = false; - if ( (newInc->revision() > exInc->revision()) || - (newInc->revision() == exInc->revision() && - newInc->lastModified()>exInc->lastModified()) ) { - mCalendar->deleteIncidence( exInc ); - res = true; + + const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() ); + kdDebug(5800) << "Scheduler::acceptRequest status=" << ScheduleMessage::statusName( status ) << ": found " << existingIncidences.count() << " incidences with schedulingID " << inc->schedulingID() << endl; + Incidence::List::ConstIterator incit = existingIncidences.begin(); + for ( ; incit != existingIncidences.end() ; ++incit ) { + Incidence* const i = *incit; + kdDebug(5800) << "Considering this found event (" + << ( i->isReadOnly() ? "readonly" : "readwrite" ) + << ") :" << mFormat->toString( i ) << endl; + // If it's readonly, we can't possible update it. + if ( i->isReadOnly() ) + continue; + if ( i->revision() <= inc->revision() ) { + // The new incidence might be an update for the found one + bool isUpdate = true; + // Code for new invitations: + // If you think we could check the value of "status" to be RequestNew: we can't. + // It comes from a similar check inside libical, where the event is compared to + // other events in the calendar. But if we have another version of the event around + // (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated. + kdDebug(5800) << "looking in " << i->uid() << "'s attendees" << endl; + // This is supposed to be a new request, not an update - however we want to update + // the existing one to handle the "clicking more than once on the invitation" case. + // So check the attendee status of the attendee. + const KCal::Attendee::List attendees = i->attendees(); + KCal::Attendee::List::ConstIterator ait; + for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) { + if( (*ait)->email() == attendee && (*ait)->status() == Attendee::NeedsAction ) { + // This incidence wasn't created by me - it's probably in a shared folder + // and meant for someone else, ignore it. + kdDebug(5800) << "ignoring " << i->uid() << " since I'm still NeedsAction there" << endl; + isUpdate = false; + break; + } + } + if ( isUpdate ) { + if ( i->revision() == inc->revision() && + i->lastModified() > inc->lastModified() ) { + // This isn't an update - the found incidence was modified more recently + kdDebug(5800) << "This isn't an update - the found incidence was modified more recently" << endl; + deleteTransaction(incidence); + return false; + } + kdDebug(5800) << "replacing existing incidence " << i->uid() << endl; + bool res = true; + AssignmentVisitor visitor; + const TQString oldUid = i->uid(); + if ( !visitor.assign( i, inc ) ) { + kdError(5800) << "assigning different incidence types" << endl; + res = false; + } else { + i->setUid( oldUid ); + i->setSchedulingID( inc->uid() ); + } + deleteTransaction( incidence ); + return res; } + } else { + // This isn't an update - the found incidence has a bigger revision number + kdDebug(5800) << "This isn't an update - the found incidence has a bigger revision number" << endl; + deleteTransaction(incidence); + return false; + } + } + + // Move the uid to be the schedulingID and make a unique UID + inc->setSchedulingID( inc->uid() ); + inc->setUid( CalFormat::createUniqueId() ); + // notify the user in case this is an update and we didn't find the to-be-updated incidence + if ( existingIncidences.count() == 0 && inc->revision() > 0 ) { + KMessageBox::information( + 0, + i18n( "" + "You accepted an invitation update, but an earlier version of the " + "item could not be found in your calendar.

      " + "This may have occurred because:

        " + "
      • the organizer did not include you in the original invitation
      • " + "
      • you did not accept the original invitation yet
      • " + "
      • you deleted the original invitation from your calendar
      • " + "
      • you no longer have access to the calendar containing the invitation
      • " + "
      " + "This is not a problem, but we thought you should know.
      " ), + i18n( "Cannot find invitation to be updated" ), "AcceptCantFindIncidence" ); + } + kdDebug(5800) << "Storing new incidence with scheduling uid=" << inc->schedulingID() + << " and uid=" << inc->uid() << endl; + + CalendarResources *stdcal = dynamic_cast( mCalendar ); + if( stdcal && !stdcal->hasCalendarResources() ) { + KMessageBox::sorry( + 0, + i18n( "No calendars found, unable to save the invitation." ) ); + return false; + } + + // FIXME: This is a nasty hack, since we need to set a parent for the + // resource selection dialog. However, we don't have any UI methods + // in the calendar, only in the CalendarResources::DestinationPolicy + // So we need to type-cast it and extract it from the CalendarResources + TQWidget *tmpparent = 0; + if ( stdcal ) { + tmpparent = stdcal->dialogParentWidget(); + stdcal->setDialogParentWidget( 0 ); + } + +TryAgain: + bool success = false; + if ( stdcal ) { + success = stdcal->addIncidence( inc ); + } else { + success = mCalendar->addIncidence( inc ); + } + + if ( !success ) { + ErrorFormat *e = stdcal ? stdcal->exception() : 0; + + if ( e && e->errorCode() == KCal::ErrorFormat::UserCancel && + KMessageBox::warningYesNo( + 0, + i18n( "You canceled the save operation. Therefore, the appointment will not be " + "stored in your calendar even though you accepted the invitation. " + "Are you certain you want to discard this invitation? " ), + i18n( "Discard this invitation?" ), + i18n( "Discard" ), i18n( "Go Back to Folder Selection" ) ) == KMessageBox::Yes ) { + KMessageBox::information( + 0, + i18n( "The invitation \"%1\" was not saved to your calendar " + "but you are still listed as an attendee for that appointment.\n" + "If you mistakenly accepted the invitation or do not plan to attend, please notify " + "the organizer %2 and ask them to remove you from the attendee list."). + arg( inc->summary(), inc->organizer().fullName() ) ); + deleteTransaction( incidence ); + return true; + } else { + goto TryAgain; } - if ( res ) { - // Move the uid to be the schedulingID and make a unique UID - newInc->setSchedulingID( newInc->uid() ); - newInc->setUid( CalFormat::createUniqueId() ); - mCalendar->addIncidence(newInc); + // We can have a failure if the user pressed [cancel] in the resource + // selectdialog, so check the exception. + if ( !e || + ( e && ( e->errorCode() != KCal::ErrorFormat::UserCancel && + e->errorCode() != KCal::ErrorFormat::NoWritableFound ) ) ) { + TQString errMessage = i18n( "Unable to save %1 \"%2\"." ). + arg( i18n( inc->type() ) ). + arg( inc->summary() ); + KMessageBox::sorry( 0, errMessage ); } - deleteTransaction( newIncBase ); - return res; + return false; } - return false; + + deleteTransaction( incidence ); + return true; } bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status /* status */) @@ -252,22 +395,131 @@ bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status /* st return false; } -bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status /* status */) +bool Scheduler::acceptCancel( IncidenceBase *incidence, + ScheduleMessage::Status status, + const TQString &attendee ) { + Incidence *inc = static_cast( incidence ); + if ( !inc ) { + return false; + } + + if ( inc->type() == "FreeBusy" ) { + // reply to this request is handled in korganizer's incomingdialog + return true; + } + + const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() ); + kdDebug(5800) << "Scheduler::acceptCancel=" + << ScheduleMessage::statusName( status ) + << ": found " << existingIncidences.count() + << " incidences with schedulingID " << inc->schedulingID() + << endl; + + // Remove existing incidences that aren't stored in my calendar as we + // will never attempt to remove those -- even if we have write-access. + Incidence::List myExistingIncidences; + Incidence::List::ConstIterator incit = existingIncidences.begin(); + for ( ; incit != existingIncidences.end() ; ++incit ) { + Incidence *i = *incit; + if ( CalHelper::isMyCalendarIncidence( mCalendar, i ) ) { + myExistingIncidences.append( i ); + } + } + bool ret = false; + incit = myExistingIncidences.begin(); + for ( ; incit != myExistingIncidences.end() ; ++incit ) { + Incidence *i = *incit; + kdDebug(5800) << "Considering this found event (" + << ( i->isReadOnly() ? "readonly" : "readwrite" ) + << ") :" << mFormat->toString( i ) << endl; + + // If it's readonly, we can't possible remove it. + if ( i->isReadOnly() ) { + continue; + } + + // Code for new invitations: + // We cannot check the value of "status" to be RequestNew because + // "status" comes from a similar check inside libical, where the event + // is compared to other events in the calendar. But if we have another + // version of the event around (e.g. shared folder for a group), the + // status could be RequestNew, Obsolete or Updated. + kdDebug(5800) << "looking in " << i->uid() << "'s attendees" << endl; + + // This is supposed to be a new request, not an update - however we want + // to update the existing one to handle the "clicking more than once + // on the invitation" case. So check the attendee status of the attendee. + bool isMine = true; + const KCal::Attendee::List attendees = i->attendees(); + KCal::Attendee::List::ConstIterator ait; + for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) { + if ( (*ait)->email() == attendee && + (*ait)->status() == Attendee::NeedsAction ) { + // This incidence wasn't created by me - it's probably in a shared + // folder and meant for someone else, ignore it. + kdDebug(5800) << "ignoring " << i->uid() + << " since I'm still NeedsAction there" << endl; + isMine = false; + break; + } + } + + if ( isMine ) { + kdDebug(5800) << "removing existing incidence " << i->uid() << endl; + if ( i->type() == "Event" ) { + Event *event = mCalendar->event( i->uid() ); + ret = ( event && mCalendar->deleteEvent( event ) ); + } else if ( i->type() == "Todo" ) { + Todo *todo = mCalendar->todo( i->uid() ); + ret = ( todo && mCalendar->deleteTodo( todo ) ); + } + deleteTransaction( incidence ); + return ret; + } + } + + // in case we didn't find the to-be-removed incidence + if ( myExistingIncidences.count() > 0 && inc->revision() > 0 ) { + KMessageBox::information( + 0, + i18n( "The event or task could not be removed from your calendar. " + "Maybe it has already been deleted or is not owned by you. " + "Or it might belong to a read-only or disabled calendar." ) ); + } + deleteTransaction( incidence ); + return ret; +} + +bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status /* status */) +{ const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() ); + + bool ret = true; if ( toDelete ) { - Event *even = mCalendar->event(toDelete->uid()); - if (even) { - mCalendar->deleteEvent(even); - ret = true; - } else { - Todo *todo = mCalendar->todo(toDelete->uid()); - if (todo) { - mCalendar->deleteTodo(todo); - ret = true; - } + if ( toDelete->type() == "Event" ) { + Event *event = mCalendar->event( toDelete->uid() ); + ret = ( event && mCalendar->deleteEvent( event ) ); + } else if ( toDelete->type() == "Todo" ) { + Todo *todo = mCalendar->todo( toDelete->uid() ); + ret = ( todo && mCalendar->deleteTodo( todo ) ); } + } else { + // only complain if we failed to determine the toDelete incidence + // on non-initial request. + Incidence *inc = static_cast( incidence ); + if ( inc->revision() > 0 ) { + ret = false; + } + } + + if ( !ret ) { + KMessageBox::information( + 0, + i18n( "The event or task to be canceled could not be removed from your calendar. " + "Maybe it has already been deleted or is not owned by you. " + "Or it might belong to a read-only or disabled calendar." ) ); } deleteTransaction(incidence); return ret; @@ -370,13 +622,25 @@ bool Scheduler::acceptReply(IncidenceBase *incidence,ScheduleMessage::Status /* // send update about new participants if ( attendeeAdded ) { + bool sendMail = false; + if ( ev || to ) { + if ( KMessageBox::questionYesNo( 0, i18n( "An attendee was added to the incidence. " + "Do you want to email the attendees an update message?" ), + i18n( "Attendee Added" ), i18n( "Send Messages" ), + i18n( "Do Not Send" ) ) == KMessageBox::Yes ) { + sendMail = true; + } + } + if ( ev ) { ev->setRevision( ev->revision() + 1 ); - performTransaction( ev, Scheduler::Request ); + if ( sendMail ) + performTransaction( ev, Scheduler::Request ); } if ( to ) { - to->setRevision( ev->revision() + 1 ); - performTransaction( to, Scheduler::Request ); + to->setRevision( to->revision() + 1 ); + if ( sendMail ) + performTransaction( to, Scheduler::Request ); } } diff --git a/libkcal/scheduler.h b/libkcal/scheduler.h index 17e19ec4..bf22c9c8 100644 --- a/libkcal/scheduler.h +++ b/libkcal/scheduler.h @@ -49,14 +49,14 @@ class ScheduleMessage */ enum Status { PublishNew, PublishUpdate, Obsolete, RequestNew, RequestUpdate, Unknown }; - + /** Create a scheduling message with method as defined in Scheduler::Method and a status. */ ScheduleMessage( IncidenceBase *, int method, Status status ); ~ScheduleMessage() {}; - + /** Return event associated with this message. */ @@ -102,13 +102,13 @@ class LIBKCAL_EXPORT Scheduler */ enum Method { Publish,Request,Refresh,Cancel,Add,Reply,Counter, Declinecounter,NoMethod }; - + /** Create scheduler for calendar specified as argument. */ Scheduler( Calendar *calendar ); virtual ~Scheduler(); - + /** iTIP publish action */ @@ -121,8 +121,8 @@ class LIBKCAL_EXPORT Scheduler virtual bool performTransaction( IncidenceBase *incidence, Method method ) = 0; /** - Perform iTIP transaction on incidence to specified recipient(s). The - method is specified as the method argumanet and can be any valid iTIP + Perform iTIP transaction on incidence to specified recipient(s). The + method is specified as the method argumanet and can be any valid iTIP method. */ virtual bool performTransaction( IncidenceBase *incidence, Method method, @@ -136,10 +136,12 @@ class LIBKCAL_EXPORT Scheduler Accept transaction. The incidence argument specifies the iCal compoennt on which the transaction acts. The status is the result of processing a iTIP message with the current calendar and specifies the action to be - taken for this incidence. + taken for this incidence. The attendee is the email address of the person + on who's behalf this transaction is to be performed. */ bool acceptTransaction( IncidenceBase *, Method method, - ScheduleMessage::Status status ); + ScheduleMessage::Status status, + const TQString& attendee = TQString::null ); /** Return a machine-readable name for a iTIP method. @@ -151,7 +153,7 @@ class LIBKCAL_EXPORT Scheduler static TQString translatedMethodName( Method ); virtual bool deleteTransaction( IncidenceBase *incidence ); - + /** Returns the directory where the free-busy information is stored. */ @@ -169,9 +171,12 @@ class LIBKCAL_EXPORT Scheduler protected: bool acceptPublish( IncidenceBase *, ScheduleMessage::Status status, Method method ); - bool acceptRequest( IncidenceBase *, ScheduleMessage::Status status ); + bool acceptRequest( IncidenceBase *, ScheduleMessage::Status status, + const TQString & attendee ); bool acceptAdd( IncidenceBase *, ScheduleMessage::Status status ); - bool acceptCancel( IncidenceBase *, ScheduleMessage::Status status ); + KDE_DEPRECATED bool acceptCancel( IncidenceBase *, ScheduleMessage::Status status ); + bool acceptCancel( IncidenceBase *, ScheduleMessage::Status status, + const TQString & attendee ); bool acceptDeclineCounter( IncidenceBase *, ScheduleMessage::Status status ); bool acceptReply( IncidenceBase *, ScheduleMessage::Status status, diff --git a/libkcal/tests/Makefile.am b/libkcal/tests/Makefile.am index 6e7096a1..1a09143e 100644 --- a/libkcal/tests/Makefile.am +++ b/libkcal/tests/Makefile.am @@ -18,7 +18,8 @@ check_PROGRAMS = testtostring \ testrecurson \ testrecurrencetype \ testvcalexport \ - testfb + testfb \ + testcalselectdialog METASOURCES = AUTO @@ -76,6 +77,10 @@ testfb_SOURCES = testfb.cpp testfb_LDFLAGS = $(all_libraries) $(KDE_RPATH) testfb_LDADD = ../libkcal.la +testcalselectdialog_SOURCES = testcalselectdialog.cpp +testcalselectdialog_LDFLAGS = $(all_libraries) $(KDE_RPATH) +testcalselectdialog_LDADD = ../libkcal.la + TESTFILES = test1.ics test2.ics test3.ics test4.ics test5.ics test_Mozilla.ics check-local: readandwrite testrecurrence testrecurprevious testrecurson testvcalexport diff --git a/libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref index 1698d7d2..783c984a 100644 --- a/libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref @@ -1,4 +1,3 @@ -2005-04-01 2005-05-03 2005-05-05 2005-06-07 diff --git a/libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref index 2562eae7..8b137891 100644 --- a/libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref @@ -1,2 +1 @@ -2005-05-31 diff --git a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref index eb9f8158..a17df247 100644 --- a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref @@ -1,4 +1,3 @@ -2002-04-02 2002-04-04 2002-04-11 2002-04-18 diff --git a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref index 7abf1e5a..13edfa43 100644 --- a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref @@ -1,4 +1,3 @@ -1997-09-02 1997-09-03 1997-09-05 1997-09-15 diff --git a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref index 887df0de..e1d367ad 100644 --- a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref @@ -1,4 +1,3 @@ -1997-09-02 1998-02-13 1998-03-13 1998-11-13 diff --git a/libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref index 7abf1e5a..24ad0551 100644 --- a/libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref @@ -1,4 +1,3 @@ -1997-09-02 1997-09-03 1997-09-05 1997-09-15 @@ -23,4 +22,3 @@ 1997-12-10 1997-12-12 1997-12-22 - diff --git a/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref index 2f3a7e16..e69de29b 100644 --- a/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref @@ -1 +0,0 @@ -1997-12-15 diff --git a/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref index 6fca0d2f..f57f89d0 100644 --- a/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref @@ -1,2 +1 @@ 1997-09-02 - diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref index 05df6b37..c55b5583 100644 --- a/libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref @@ -1,4 +1,3 @@ -2005-05-12 2005-05-31 2005-06-30 2005-07-29 diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref index bbf1358c..4c2c61aa 100644 --- a/libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref @@ -1,4 +1,3 @@ -2005-05-12 2005-05-18 2005-05-24 2005-06-15 diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref index 4082c9c2..70712a5e 100644 --- a/libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref @@ -1,4 +1,3 @@ -2005-05-12 2005-05-14 2005-05-16 2005-05-17 diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref index 71cfa3b6..5bd295fc 100644 --- a/libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref @@ -1,4 +1,3 @@ -2005-05-12 2007-01-07 2007-01-14 2007-01-21 diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref index 59d81cdc..1b1e0191 100644 --- a/libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref +++ b/libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref @@ -1,4 +1,3 @@ -2005-05-12 2005-05-23 2005-05-25 2005-06-06 @@ -293,3 +292,210 @@ 2010-12-15 2010-12-27 2010-12-29 +2011-01-10 +2011-01-12 +2011-01-24 +2011-01-26 +2011-02-07 +2011-02-09 +2011-02-21 +2011-02-23 +2011-03-07 +2011-03-09 +2011-03-21 +2011-03-23 +2011-04-04 +2011-04-06 +2011-04-18 +2011-04-20 +2011-05-02 +2011-05-04 +2011-05-16 +2011-05-18 +2011-05-30 +2011-06-01 +2011-06-13 +2011-06-15 +2011-06-27 +2011-06-29 +2011-07-11 +2011-07-13 +2011-07-25 +2011-07-27 +2011-08-08 +2011-08-10 +2011-08-22 +2011-08-24 +2011-09-05 +2011-09-07 +2011-09-19 +2011-09-21 +2011-10-03 +2011-10-05 +2011-10-17 +2011-10-19 +2011-10-31 +2011-11-02 +2011-11-14 +2011-11-16 +2011-11-28 +2011-11-30 +2011-12-12 +2011-12-14 +2011-12-26 +2011-12-28 +2012-01-09 +2012-01-11 +2012-01-23 +2012-01-25 +2012-02-06 +2012-02-08 +2012-02-20 +2012-02-22 +2012-03-05 +2012-03-07 +2012-03-19 +2012-03-21 +2012-04-02 +2012-04-04 +2012-04-16 +2012-04-18 +2012-04-30 +2012-05-02 +2012-05-14 +2012-05-16 +2012-05-28 +2012-05-30 +2012-06-11 +2012-06-13 +2012-06-25 +2012-06-27 +2012-07-09 +2012-07-11 +2012-07-23 +2012-07-25 +2012-08-06 +2012-08-08 +2012-08-20 +2012-08-22 +2012-09-03 +2012-09-05 +2012-09-17 +2012-09-19 +2012-10-01 +2012-10-03 +2012-10-15 +2012-10-17 +2012-10-29 +2012-10-31 +2012-11-12 +2012-11-14 +2012-11-26 +2012-11-28 +2012-12-10 +2012-12-12 +2012-12-24 +2012-12-26 +2013-01-07 +2013-01-09 +2013-01-21 +2013-01-23 +2013-02-04 +2013-02-06 +2013-02-18 +2013-02-20 +2013-03-04 +2013-03-06 +2013-03-18 +2013-03-20 +2013-04-01 +2013-04-03 +2013-04-15 +2013-04-17 +2013-04-29 +2013-05-01 +2013-05-13 +2013-05-15 +2013-05-27 +2013-05-29 +2013-06-10 +2013-06-12 +2013-06-24 +2013-06-26 +2013-07-08 +2013-07-10 +2013-07-22 +2013-07-24 +2013-08-05 +2013-08-07 +2013-08-19 +2013-08-21 +2013-09-02 +2013-09-04 +2013-09-16 +2013-09-18 +2013-09-30 +2013-10-02 +2013-10-14 +2013-10-16 +2013-10-28 +2013-10-30 +2013-11-11 +2013-11-13 +2013-11-25 +2013-11-27 +2013-12-09 +2013-12-11 +2013-12-23 +2013-12-25 +2014-01-06 +2014-01-08 +2014-01-20 +2014-01-22 +2014-02-03 +2014-02-05 +2014-02-17 +2014-02-19 +2014-03-03 +2014-03-05 +2014-03-17 +2014-03-19 +2014-03-31 +2014-04-02 +2014-04-14 +2014-04-16 +2014-04-28 +2014-04-30 +2014-05-12 +2014-05-14 +2014-05-26 +2014-05-28 +2014-06-09 +2014-06-11 +2014-06-23 +2014-06-25 +2014-07-07 +2014-07-09 +2014-07-21 +2014-07-23 +2014-08-04 +2014-08-06 +2014-08-18 +2014-08-20 +2014-09-01 +2014-09-03 +2014-09-15 +2014-09-17 +2014-09-29 +2014-10-01 +2014-10-13 +2014-10-15 +2014-10-27 +2014-10-29 +2014-11-10 +2014-11-12 +2014-11-24 +2014-11-26 +2014-12-08 +2014-12-10 +2014-12-22 diff --git a/libkcal/tests/runtestcase.pl b/libkcal/tests/runtestcase.pl index 52e6ead1..4cad27cc 100755 --- a/libkcal/tests/runtestcase.pl +++ b/libkcal/tests/runtestcase.pl @@ -78,7 +78,7 @@ sub checkfile() exit 1; } while( ) { - push @ref, $_; + push @ref, $_ if($_ !~ m/^\s*$/); #skip blank lines in the ref } close REF; @@ -92,10 +92,13 @@ sub checkfile() $line = 0; my $errorlines = 0; while( ) { + next if ($_ =~ m/^\s*$/); #skip blank lines in the output $out = $_; $ref = @ref[$i++]; $line++; + $out =~ s/\s*$//; #remove trailing whitespace + $ref =~ s/\s*$//; #remove trailing whitespace # DTSTAMP, LAST-MODIFIED and CREATED might be different to the reference... if ( $out =~ /^DTSTAMP:[0-9ZT]+\r?$/ && $ref =~ /^DTSTAMP:[0-9ZT]+\r?$/ ) { next; @@ -122,7 +125,6 @@ sub checkfile() print " \n"; } } - } close READ; @@ -150,7 +152,7 @@ sub checkfile() } else { print "\n FAILED: $error errors found.\n"; if ( $error > 5 ) { - system( "diff -u $file.$id.ref $outfile" ); + system( "diff -u $file.$id.ref $outfile" ); } system( "touch FAILED" ); exit 1; diff --git a/libkcal/tests/testcalselectdialog.cpp b/libkcal/tests/testcalselectdialog.cpp new file mode 100644 index 00000000..5c513045 --- /dev/null +++ b/libkcal/tests/testcalselectdialog.cpp @@ -0,0 +1,45 @@ +/* + Copyright (c) 2010 Klar�lvdalens Datakonsult AB, a KDAB Group company + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "calselectdialog.h" +using namespace KCal; + +#include +#include +#include +#include + +int main( int argc, char **argv ) +{ + KCmdLineArgs::init( argc, argv, "testcalselectdialog", 0, + "KCalSelectDialogTest", "1.0", + "kcalselectedialog test app" ); + KApplication app; + TQStringList cals; + cals << "standard" << "shared" << "mine" << "yours"; + TQString cal = CalSelectDialog::getItem( i18n( "Calendar Selection" ), + i18n( "Please select a calendar" ), + cals ); + + if ( !cal.isEmpty() ) { + kdDebug() << "Selected calendar " << cal << endl; + } else { + kdDebug() << "nothing selected. user cancel" << endl; + } +} diff --git a/libkcal/todo.cpp b/libkcal/todo.cpp index 9787ff2a..b3fe65d9 100644 --- a/libkcal/todo.cpp +++ b/libkcal/todo.cpp @@ -116,10 +116,13 @@ void Todo::setDtDue(const TQDateTime &dtDue, bool first ) TQDateTime Todo::dtDue( bool first ) const { - if ( doesRecur() && !first && mDtRecurrence.isValid() ) + if ( doesRecur() && !first && mDtRecurrence.isValid() ) { return mDtRecurrence; - - return mDtDue; + } else if ( hasDueDate() ) { + return mDtDue; + } else { + return TQDateTime(); + } } TQString Todo::dtDueTimeStr() const @@ -173,10 +176,17 @@ void Todo::setHasStartDate(bool f) TQDateTime Todo::dtStart( bool first ) const { - if ( doesRecur() && !first ) - return mDtRecurrence.addDays( dtDue( first ).daysTo( IncidenceBase::dtStart() ) ); - else + if ( doesRecur() && !first ) { + TQDateTime dt = mDtRecurrence.addDays( dtDue( true ).daysTo( IncidenceBase::dtStart() ) ); + + // We want the dtStart's time, not dtDue's + dt.setTime( IncidenceBase::dtStart().time() ); + return dt; + } else if ( hasStartDate() ) { return IncidenceBase::dtStart(); + } else { + return TQDateTime(); + } } void Todo::setDtStart( const TQDateTime &dtStart ) @@ -255,10 +265,14 @@ int Todo::percentComplete() const return mPercentComplete; } -void Todo::setPercentComplete(int v) +void Todo::setPercentComplete( int v ) { mPercentComplete = v; - if ( v != 100 ) mHasCompletedDate = false; + if ( v != 100 ) { + mHasCompletedDate = false; + mCompleted = TQDateTime(); + } + updated(); } @@ -292,7 +306,8 @@ bool Todo::recurTodo() while ( !recursAt( nextDate ) || nextDate <= TQDateTime::currentDateTime() ) { - if ( !nextDate.isValid() || nextDate > endDateTime ) { + if ( !nextDate.isValid() || + ( nextDate > endDateTime && r->duration() != -1 ) ) { return false; } diff --git a/libkcal/todo.h b/libkcal/todo.h index 1a7b050d..74e1b39d 100644 --- a/libkcal/todo.h +++ b/libkcal/todo.h @@ -66,21 +66,26 @@ class LIBKCAL_EXPORT Todo : public Incidence /** Returns due time as string formatted according to the users locale settings. + @deprecated use IncidenceFormatter::timeToString() */ - TQString dtDueTimeStr() const; + KDE_DEPRECATED TQString dtDueTimeStr() const; + /** Returns due date as string formatted according to the users locale settings. @param shortfmt If set to true, use short date format, if set to false use long format. + @deprecated use IncidenceFormatter::dateToString() */ - TQString dtDueDateStr( bool shortfmt = true ) const; + KDE_DEPRECATED TQString dtDueDateStr( bool shortfmt = true ) const; + /** Returns due date and time as string formatted according to the users locale settings. + @deprecated use IncidenceFormatter::dateTimeToString() */ - TQString dtDueStr() const; + KDE_DEPRECATED TQString dtDueStr() const; /** Returns true if the todo has a due date, otherwise return false. @@ -218,7 +223,14 @@ class LIBKCAL_EXPORT Todo : public Incidence private: bool accept(Visitor &v) { return v.visit( this ); } - /** Returns true if the todo got a new date, else false will be returned. */ + + /** + * If the todo recurs, mDtRecurrence is set to the next occurrence + * that's after today, mPercentComplete is set to 0 and true is returned. + * + * If the todo doesn't recur or if there aren't anymore occurrences + * it just returns false. + */ bool recurTodo(); TQDateTime mDtDue; // due date of todo diff --git a/libkdenetwork/gpgmepp/context.cpp b/libkdenetwork/gpgmepp/context.cpp index c0286fcb..547adde9 100644 --- a/libkdenetwork/gpgmepp/context.cpp +++ b/libkdenetwork/gpgmepp/context.cpp @@ -43,6 +43,7 @@ //#include //using std::string; +#include #ifndef NDEBUG #include using std::cerr; @@ -77,6 +78,10 @@ namespace GpgME { return code() == GPG_ERR_CANCELED; } + std::ostream & operator<<( std::ostream & os, Error err ) { + return os << "GpgME::Error(" << err.operator int() << " (" << err.asString() << "))"; + } + Context::Context( gpgme_ctx_t ctx ) { d = new Private( ctx ); } @@ -628,6 +633,89 @@ namespace GpgME { return d->lasterr; } + std::ostream & operator<<( std::ostream & os, Context::Protocol proto ) { + os << "GpgME::Context::Protocol("; + switch ( proto ) { + case Context::OpenPGP: + os << "OpenPGP"; + break; + case Context::CMS: + os << "CMS"; + break; + default: + case Context::Unknown: + os << "Unknown"; + break; + } + return os << ')'; + } + + std::ostream & operator<<( std::ostream & os, Context::CertificateInclusion incl ) { + os << "GpgME::Context::CertificateInclusion(" << static_cast( incl ); + switch ( incl ) { + case Context::DefaultCertificates: + os << "(DefaultCertificates)"; + break; + case Context::AllCertificatesExceptRoot: + os << "(AllCertificatesExceptRoot)"; + break; + case Context::AllCertificates: + os << "(AllCertificates)"; + break; + case Context::NoCertificates: + os << "(NoCertificates)"; + break; + case Context::OnlySenderCertificate: + os << "(OnlySenderCertificate)"; + break; + } + return os << ')'; + } + + std::ostream & operator<<( std::ostream & os, Context::KeyListMode mode ) { + os << "GpgME::Context::KeyListMode("; +#define CHECK( x ) if ( !(mode & (Context::x)) ) {} else do { os << #x " "; } while (0) + CHECK( Local ); + CHECK( Extern ); + CHECK( Signatures ); + CHECK( Validate ); +#undef CHECK + return os << ')'; + } + + std::ostream & operator<<( std::ostream & os, Context::SignatureMode mode ) { + os << "GpgME::Context::SignatureMode("; + switch ( mode ) { +#define CHECK( x ) case Context::x: os << #x; break + CHECK( Normal ); + CHECK( Detached ); + CHECK( Clearsigned ); +#undef CHECK + default: + os << "???" "(" << static_cast( mode ) << ')'; + break; + } + return os << ')'; + } + + std::ostream & operator<<( std::ostream & os, Context::EncryptionFlags flags ) { + os << "GpgME::Context::EncryptionFlags("; +#define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) + CHECK( AlwaysTrust ); +#undef CHECK + return os << ')'; + } + + std::ostream & operator<<( std::ostream & os, Context::AuditLogFlags flags ) { + os << "GpgME::Context::AuditLogFlags("; +#define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) + CHECK( HtmlAuditLog ); + CHECK( AuditLogWithHelp ); +#undef CHECK + return os << ')'; + } + + } // namespace GpgME diff --git a/libkdenetwork/gpgmepp/context.h b/libkdenetwork/gpgmepp/context.h index aae69f86..b07e722e 100644 --- a/libkdenetwork/gpgmepp/context.h +++ b/libkdenetwork/gpgmepp/context.h @@ -25,6 +25,8 @@ #include #include +#include + #include namespace GpgME { @@ -64,6 +66,8 @@ namespace GpgME { int mErr; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, Error err ); + class KDE_EXPORT Context { Context( gpgme_ctx_t ); public: @@ -281,6 +285,13 @@ namespace GpgME { const Context & operator=( const Context & ); }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, Context::Protocol proto ); + KDE_EXPORT std::ostream & operator<<( std::ostream & os, Context::CertificateInclusion incl ); + KDE_EXPORT std::ostream & operator<<( std::ostream & os, Context::KeyListMode mode ); + KDE_EXPORT std::ostream & operator<<( std::ostream & os, Context::SignatureMode mode ); + KDE_EXPORT std::ostream & operator<<( std::ostream & os, Context::EncryptionFlags flags ); + KDE_EXPORT std::ostream & operator<<( std::ostream & os, Context::AuditLogFlags flags ); + // // // Globals diff --git a/libkdenetwork/gpgmepp/data.cpp b/libkdenetwork/gpgmepp/data.cpp index 52c96544..52471be9 100644 --- a/libkdenetwork/gpgmepp/data.cpp +++ b/libkdenetwork/gpgmepp/data.cpp @@ -133,11 +133,11 @@ GpgME::Data::Data( DataProvider * dp ) { if ( e ) d->data = 0; #ifndef NDEBUG - std::cerr << "GpgME::Data(): DataProvider supports: " - << ( d->cbs.read ? "read" : "no read" ) << ", " - << ( d->cbs.write ? "write" : "no write" ) << ", " - << ( d->cbs.seek ? "seek" : "no seek" ) << ", " - << ( d->cbs.release ? "release" : "no release" ) << std::endl; +// std::cerr << "GpgME::Data(): DataProvider supports: " +// << ( d->cbs.read ? "read" : "no read" ) << ", " +// << ( d->cbs.write ? "write" : "no write" ) << ", " +// << ( d->cbs.seek ? "seek" : "no seek" ) << ", " +// << ( d->cbs.release ? "release" : "no release" ) << std::endl; #endif } diff --git a/libkdenetwork/gpgmepp/decryptionresult.cpp b/libkdenetwork/gpgmepp/decryptionresult.cpp index cdefefe9..2efcc67d 100644 --- a/libkdenetwork/gpgmepp/decryptionresult.cpp +++ b/libkdenetwork/gpgmepp/decryptionresult.cpp @@ -25,11 +25,13 @@ #include #include "shared.h" #include "result_p.h" +#include "util.h" #include #include #include +#include class GpgME::DecryptionResult::Private : public GpgME::Shared { public: @@ -71,3 +73,13 @@ bool GpgME::DecryptionResult::wrongKeyUsage() const { #endif return false; } + +std::ostream & GpgME::operator<<( std::ostream & os, const DecryptionResult & result ) { + os << "GpgME::DecryptionResult("; + if ( !result.isNull() ) + os << "\n error: " << result.error() + << "\n unsupportedAlgortihm: " << protect( result.unsupportedAlgortihm() ) + << "\n wrongKeyUsage: " << result.wrongKeyUsage() + << '\n'; + return os << ')'; +} diff --git a/libkdenetwork/gpgmepp/decryptionresult.h b/libkdenetwork/gpgmepp/decryptionresult.h index b6307a05..b97f59ae 100644 --- a/libkdenetwork/gpgmepp/decryptionresult.h +++ b/libkdenetwork/gpgmepp/decryptionresult.h @@ -23,6 +23,9 @@ #include #include + +#include + #include namespace GpgME { @@ -49,6 +52,8 @@ namespace GpgME { Private * d; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const DecryptionResult & result ); + } -#endif // __GPGMEPP_KEYGENERATIONRESULT_H__ +#endif // __GPGMEPP_DECRYPTIONRESULT_H__ diff --git a/libkdenetwork/gpgmepp/encryptionresult.cpp b/libkdenetwork/gpgmepp/encryptionresult.cpp index deec8f9d..f827ca83 100644 --- a/libkdenetwork/gpgmepp/encryptionresult.cpp +++ b/libkdenetwork/gpgmepp/encryptionresult.cpp @@ -25,11 +25,15 @@ #include #include "shared.h" #include "result_p.h" +#include "util.h" #include #include #include +#include +#include +#include class GpgME::EncryptionResult::Private : public GpgME::Shared { public: @@ -137,3 +141,25 @@ GpgME::Error GpgME::InvalidRecipient::reason() const { return isNull() ? 0 : d->invalid[idx]->reason ; } + + +std::ostream & GpgME::operator<<( std::ostream & os, const EncryptionResult & result ) { + os << "GpgME::EncryptionResult("; + if ( !result.isNull() ) { + os << "\n error: " << result.error() + << "\n invalid recipients:\n"; + const std::vector ir = result.invalidEncryptionKeys(); + std::copy( ir.begin(), ir.end(), + std::ostream_iterator( os, "\n" ) ); + } + return os << ')'; +} + +std::ostream & GpgME::operator<<( std::ostream & os, const InvalidRecipient & ir ) { + os << "GpgME::InvalidRecipient("; + if ( !ir.isNull() ) + os << "\n fingerprint: " << protect( ir.fingerprint() ) + << "\n reason: " << ir.reason() + << '\n'; + return os << ')'; +} diff --git a/libkdenetwork/gpgmepp/encryptionresult.h b/libkdenetwork/gpgmepp/encryptionresult.h index ba59554e..7267afc0 100644 --- a/libkdenetwork/gpgmepp/encryptionresult.h +++ b/libkdenetwork/gpgmepp/encryptionresult.h @@ -25,6 +25,8 @@ #include #include +#include + #include namespace GpgME { @@ -53,6 +55,8 @@ namespace GpgME { Private * d; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const EncryptionResult & result ); + class KDE_EXPORT InvalidRecipient { friend class EncryptionResult; InvalidRecipient( EncryptionResult::Private * parent, unsigned int index ); @@ -73,6 +77,8 @@ namespace GpgME { unsigned int idx; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const InvalidRecipient & recipient ); + } #endif // __GPGMEPP_ENCRYPTIONRESULT_H__ diff --git a/libkdenetwork/gpgmepp/signingresult.cpp b/libkdenetwork/gpgmepp/signingresult.cpp index bd5e0a3b..6996eef7 100644 --- a/libkdenetwork/gpgmepp/signingresult.cpp +++ b/libkdenetwork/gpgmepp/signingresult.cpp @@ -25,11 +25,15 @@ #include #include "shared.h" #include "result_p.h" +#include "util.h" #include #include #include +#include +#include +#include class GpgME::SigningResult::Private : public GpgME::Shared { public: @@ -239,3 +243,41 @@ unsigned int GpgME::CreatedSignature::signatureClass() const { return isNull() ? 0 : d->created[idx]->sig_class ; } + +std::ostream & GpgME::operator<<( std::ostream & os, const SigningResult & result ) { + os << "GpgME::SigningResult("; + if ( !result.isNull() ) { + os << "\n error: " << result.error() + << "\n createdSignatures:\n"; + const std::vector cs = result.createdSignatures(); + std::copy( cs.begin(), cs.end(), + std::ostream_iterator( os, "\n" ) ); + os << " invalidSigningKeys:\n"; + const std::vector isk = result.invalidSigningKeys(); + std::copy( isk.begin(), isk.end(), + std::ostream_iterator( os, "\n" ) ); + } + return os << ')'; +} + +std::ostream & GpgME::operator<<( std::ostream & os, const CreatedSignature & sig ) { + os << "GpgME::CreatedSignature("; + if ( !sig.isNull() ) + os << "\n fingerprint: " << protect( sig.fingerprint() ) + << "\n creationTime: " << sig.creationTime() + << "\n mode: " << sig.mode() + << "\n publicKeyAlgorithm: " << protect( sig.publicKeyAlgorithmAsString() ) + << "\n hashAlgorithm: " << protect( sig.hashAlgorithmAsString() ) + << "\n signatureClass: " << sig.signatureClass() + << '\n'; + return os << ')'; +} + +std::ostream & GpgME::operator<<( std::ostream & os, const InvalidSigningKey & key ) { + os << "GpgME::InvalidSigningKey("; + if ( !key.isNull() ) + os << "\n fingerprint: " << protect( key.fingerprint() ) + << "\n reason: " << key.reason() + << '\n'; + return os << ')'; +} diff --git a/libkdenetwork/gpgmepp/signingresult.h b/libkdenetwork/gpgmepp/signingresult.h index e9684abb..202d09b4 100644 --- a/libkdenetwork/gpgmepp/signingresult.h +++ b/libkdenetwork/gpgmepp/signingresult.h @@ -28,7 +28,10 @@ #include #include +#include + #include + namespace GpgME { class Error; @@ -57,6 +60,8 @@ namespace GpgME { Private * d; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const SigningResult & result ); + class KDE_EXPORT InvalidSigningKey { friend class SigningResult; InvalidSigningKey( SigningResult::Private * parent, unsigned int index ); @@ -77,6 +82,8 @@ namespace GpgME { unsigned int idx; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const InvalidSigningKey & key ); + class KDE_EXPORT CreatedSignature { friend class SigningResult; CreatedSignature( SigningResult::Private * parent, unsigned int index ); @@ -110,6 +117,8 @@ namespace GpgME { unsigned int idx; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const CreatedSignature & sig ); + } #endif // __GPGMEPP_SIGNINGRESULT_H__ diff --git a/libkdenetwork/gpgmepp/util.h b/libkdenetwork/gpgmepp/util.h index c8786bdc..8246cfc3 100644 --- a/libkdenetwork/gpgmepp/util.h +++ b/libkdenetwork/gpgmepp/util.h @@ -28,6 +28,10 @@ #include #endif +static inline const char * protect( const char * s ) { + return s ? s : "" ; +} + static inline gpgme_keylist_mode_t add_to_gpgme_keylist_mode_t( unsigned int oldmode, unsigned int newmodes ) { if ( newmodes & GpgME::Context::Local ) oldmode |= GPGME_KEYLIST_MODE_LOCAL; if ( newmodes & GpgME::Context::Extern ) oldmode |= GPGME_KEYLIST_MODE_EXTERN; diff --git a/libkdenetwork/gpgmepp/verificationresult.cpp b/libkdenetwork/gpgmepp/verificationresult.cpp index decf5bce..abf30d96 100644 --- a/libkdenetwork/gpgmepp/verificationresult.cpp +++ b/libkdenetwork/gpgmepp/verificationresult.cpp @@ -25,10 +25,13 @@ #include #include "shared.h" #include "result_p.h" +#include "util.h" #include +#include #include +#include #include #include @@ -299,3 +302,56 @@ const char * GpgME::Signature::Notation::value() const { return isNull() ? 0 : d->nota[sidx][nidx].value ; } + +std::ostream & GpgME::operator<<( std::ostream & os, const VerificationResult & result ) { + os << "GpgME::VerificationResult("; + if ( !result.isNull() ) { + os << "\n error: " << result.error() + << "\n signatures:\n"; + const std::vector sigs = result.signatures(); + std::copy( sigs.begin(), sigs.end(), + std::ostream_iterator( os, "\n" ) ); + } + return os << ')'; +} + +std::ostream & GpgME::operator<<( std::ostream & os, Signature::Summary summary ) { +#define OUTPUT( x ) if ( !(summary & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0) + os << "GpgME::Signature::Summary("; + OUTPUT( Valid ); + OUTPUT( Green ); + OUTPUT( Red ); + OUTPUT( KeyRevoked ); + OUTPUT( KeyExpired ); + OUTPUT( SigExpired ); + OUTPUT( KeyMissing ); + OUTPUT( CrlMissing ); + OUTPUT( CrlTooOld ); + OUTPUT( BadPolicy ); + OUTPUT( SysError ); +#undef OUTPUT + return os << ')'; +} + +std::ostream & GpgME::operator<<( std::ostream & os, const Signature & sig ) { + os << "GpgME::Signature("; + if ( !sig.isNull() ) { + os << "\n Summary: " << sig.summary() + << "\n Fingerprint: " << protect( sig.fingerprint() ) + << "\n Status: " << sig.status() + << "\n creationTime: " << sig.creationTime() + << "\n expirationTime: " << sig.expirationTime() + << "\n wrongKeyUsage: " << sig.wrongKeyUsage() + << "\n validity: " << sig.validityAsString() + << "\n nonValidityReason: " << sig.nonValidityReason() + << "\n notations:\n"; + const std::vector nota = sig.notations(); + std::copy( nota.begin(), nota.end(), + std::ostream_iterator( os, "\n" ) ); + } + return os << ')'; +} + +std::ostream & GpgME::operator<<( std::ostream & os, const Signature::Notation & nota ) { + return os << "GpgME::Signature::Notation( \"" << protect( nota.name() ) << "\", \"" << protect( nota.value() ) << "\")"; +} diff --git a/libkdenetwork/gpgmepp/verificationresult.h b/libkdenetwork/gpgmepp/verificationresult.h index 86e3525f..edcd0278 100644 --- a/libkdenetwork/gpgmepp/verificationresult.h +++ b/libkdenetwork/gpgmepp/verificationresult.h @@ -27,6 +27,7 @@ #include #include +#include #include @@ -54,6 +55,8 @@ namespace GpgME { Private * d; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const VerificationResult & result ); + class KDE_EXPORT Signature { friend class VerificationResult; Signature( VerificationResult::Private * parent, unsigned int index ); @@ -110,6 +113,9 @@ namespace GpgME { unsigned int idx; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const Signature & sig ); + KDE_EXPORT std::ostream & operator<<( std::ostream & os, Signature::Summary summary ); + class KDE_EXPORT Signature::Notation { friend class Signature; Notation( VerificationResult::Private * parent, unsigned int sindex, unsigned int nindex ); @@ -131,6 +137,8 @@ namespace GpgME { unsigned int nidx; }; + KDE_EXPORT std::ostream & operator<<( std::ostream & os, const Signature::Notation & nota ); + } #endif // __GPGMEPP_VERIFICATIONRESULT_H__ diff --git a/libkdepim/addresseelineedit.cpp b/libkdepim/addresseelineedit.cpp index 7031a83e..5e3d0cf3 100644 --- a/libkdepim/addresseelineedit.cpp +++ b/libkdepim/addresseelineedit.cpp @@ -70,12 +70,23 @@ KPIM::LdapSearch* AddresseeLineEdit::s_LDAPSearch = 0L; TQString* AddresseeLineEdit::s_LDAPText = 0L; AddresseeLineEdit* AddresseeLineEdit::s_LDAPLineEdit = 0L; +// The weights associated with the completion sources in s_completionSources. +// Both are maintained by addCompletionSource(), don't attempt to modifiy those yourself. +TQMap* s_completionSourceWeights = 0; + +// maps LDAP client indices to completion source indices +// the assumption that they are always the first n indices in s_completion +// does not hold when clients are added later on +TQMap* AddresseeLineEdit::s_ldapClientToCompletionSourceMap = 0; + static KStaticDeleter completionDeleter; static KStaticDeleter completionItemsDeleter; static KStaticDeleter ldapTimerDeleter; static KStaticDeleter ldapSearchDeleter; static KStaticDeleter ldapTextDeleter; static KStaticDeleter completionSourcesDeleter; +static KStaticDeleter > completionSourceWeightsDeleter; +static KStaticDeleter > ldapClientToCompletionSourceMapDeleter; // needs to be unique, but the actual name doesn't matter much static TQCString newLineEditDCOPObjectName() @@ -100,7 +111,8 @@ static bool itemIsHeader( const TQListBoxItem* item ) AddresseeLineEdit::AddresseeLineEdit( TQWidget* parent, bool useCompletion, const char *name ) - : ClickLineEdit( parent, TQString::null, name ), DCOPObject( newLineEditDCOPObjectName() ) + : ClickLineEdit( parent, TQString::null, name ), DCOPObject( newLineEditDCOPObjectName() ), + m_useSemiColonAsSeparator( false ), m_allowDistLists( true ) { m_useCompletion = useCompletion; m_completionInitialized = false; @@ -114,6 +126,18 @@ AddresseeLineEdit::AddresseeLineEdit( TQWidget* parent, bool useCompletion, s_addressesDirty = true; } +void AddresseeLineEdit::updateLDAPWeights() +{ + /* Add completion sources for all ldap server, 0 to n. Added first so + * that they map to the ldapclient::clientNumber() */ + s_LDAPSearch->updateCompletionWeights(); + TQValueList< LdapClient* > clients = s_LDAPSearch->clients(); + int clientIndex = 0; + for ( TQValueList::iterator it = clients.begin(); it != clients.end(); ++it, ++clientIndex ) { + const int sourceIndex = addCompletionSource( "LDAP server: " + (*it)->server().host(), (*it)->completionWeight() ); + s_ldapClientToCompletionSourceMap->insert( clientIndex, sourceIndex ); + } +} void AddresseeLineEdit::init() { @@ -124,8 +148,9 @@ void AddresseeLineEdit::init() completionItemsDeleter.setObject( s_completionItemMap, new KPIM::CompletionItemsMap() ); completionSourcesDeleter.setObject( s_completionSources, new TQStringList() ); + completionSourceWeightsDeleter.setObject( s_completionSourceWeights, new TQMap ); + ldapClientToCompletionSourceMapDeleter.setObject( s_ldapClientToCompletionSourceMap, new TQMap ); } - // connect( s_completion, TQT_SIGNAL( match( const TQString& ) ), // this, TQT_SLOT( slotMatched( const TQString& ) ) ); @@ -134,14 +159,10 @@ void AddresseeLineEdit::init() ldapTimerDeleter.setObject( s_LDAPTimer, new TQTimer( 0, "ldapTimerDeleter" ) ); ldapSearchDeleter.setObject( s_LDAPSearch, new KPIM::LdapSearch ); ldapTextDeleter.setObject( s_LDAPText, new TQString ); - - /* Add completion sources for all ldap server, 0 to n. Added first so - * that they map to the ldapclient::clientNumber() */ - TQValueList< LdapClient* > clients = s_LDAPSearch->clients(); - for ( TQValueList::iterator it = clients.begin(); it != clients.end(); ++it ) { - addCompletionSource( "LDAP server: " + (*it)->server().host() ); - } } + + updateLDAPWeights(); + if ( !m_completionInitialized ) { setCompletionObject( s_completion, false ); connect( this, TQT_SIGNAL( completion( const TQString& ) ), @@ -187,6 +208,11 @@ void AddresseeLineEdit::allowSemiColonAsSeparator( bool useSemiColonAsSeparator m_useSemiColonAsSeparator = useSemiColonAsSeparator; } +void AddresseeLineEdit::allowDistributionLists( bool allowDistLists ) +{ + m_allowDistLists = allowDistLists; +} + void AddresseeLineEdit::keyPressEvent( TQKeyEvent *e ) { bool accept = false; @@ -206,9 +232,15 @@ void AddresseeLineEdit::keyPressEvent( TQKeyEvent *e ) } } + const TQString oldContent = text(); if ( !accept ) KLineEdit::keyPressEvent( e ); + // if the text didn't change (eg. because a cursor navigation key was pressed) + // we don't need to trigger a new search + if ( oldContent == text() ) + return; + if ( e->isAccepted() ) { updateSearchString(); TQString searchString( m_searchString ); @@ -264,24 +296,25 @@ void AddresseeLineEdit::insert( const TQString &t ) TQString contents = text(); int start_sel = 0; - int end_sel = 0; int pos = cursorPosition( ); - if ( getSelection( &start_sel, &end_sel ) ) { + + if ( hasSelectedText() ) { // Cut away the selection. - if ( pos > end_sel ) - pos -= (end_sel - start_sel); - else if ( pos > start_sel ) - pos = start_sel; - contents = contents.left( start_sel ) + contents.right( end_sel + 1 ); + start_sel = selectionStart(); + pos = start_sel; + contents = contents.left( start_sel ) + contents.mid( start_sel + selectedText().length() ); } int eot = contents.length(); - while ((eot > 0) && contents[ eot - 1 ].isSpace() ) eot--; - if ( eot == 0 ) + while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) { + eot--; + } + if ( eot == 0 ) { contents = TQString::null; - else if ( pos >= eot ) { - if ( contents[ eot - 1 ] == ',' ) + } else if ( pos >= eot ) { + if ( contents[ eot - 1 ] == ',' ) { eot--; + } contents.truncate( eot ); contents += ", "; pos = eot + 2; @@ -324,35 +357,45 @@ void AddresseeLineEdit::mouseReleaseEvent( TQMouseEvent *e ) void AddresseeLineEdit::dropEvent( TQDropEvent *e ) { KURL::List uriList; - if ( !isReadOnly() - && KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ) ) { - TQString contents = text(); - // remove trailing white space and comma - int eot = contents.length(); - while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) - eot--; - if ( eot == 0 ) - contents = TQString::null; - else if ( contents[ eot - 1 ] == ',' ) { - eot--; - contents.truncate( eot ); - } - bool mailtoURL = false; - // append the mailto URLs - for ( KURL::List::Iterator it = uriList.begin(); - it != uriList.end(); ++it ) { - if ( !contents.isEmpty() ) - contents.append( ", " ); - KURL u( *it ); - if ( u.protocol() == "mailto" ) { - mailtoURL = true; - contents.append( (*it).path() ); + if ( !isReadOnly() ) { + if ( KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ) ) { + TQString contents = text(); + // remove trailing white space and comma + int eot = contents.length(); + while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) + eot--; + if ( eot == 0 ) + contents = TQString::null; + else if ( contents[ eot - 1 ] == ',' ) { + eot--; + contents.truncate( eot ); + } + bool mailtoURL = false; + // append the mailto URLs + for ( KURL::List::Iterator it = uriList.begin(); + it != uriList.end(); ++it ) { + if ( !contents.isEmpty() ) + contents.append( ", " ); + KURL u( *it ); + if ( u.protocol() == "mailto" ) { + mailtoURL = true; + contents.append( (*it).path() ); + } + } + if ( mailtoURL ) { + setText( contents ); + setEdited( true ); + return; + } + } else { + // Let's see if this drop contains a comma separated list of emails + TQString dropData = TQString::fromUtf8( e->encodedData( "text/plain" ) ); + TQStringList addrs = splitEmailAddrList( dropData ); + if ( addrs.count() > 0 ) { + setText( normalizeAddressesAndDecodeIDNs( dropData ) ); + setEdited( true ); + return; } - } - if ( mailtoURL ) { - setText( contents ); - setEdited( true ); - return; } } @@ -526,21 +569,19 @@ void AddresseeLineEdit::loadContacts() TQString uid = (*it).uid(); TQMap::const_iterator wit = uidToResourceMap.find( uid ); const TQString subresourceLabel = resabc->subresourceLabel( *wit ); - int idx = s_completionSources->findIndex( subresourceLabel ); - if ( idx == -1 ) { - s_completionSources->append( subresourceLabel ); - idx = s_completionSources->size() -1; - } - int weight = ( wit != uidToResourceMap.end() ) ? resabc->subresourceCompletionWeight( *wit ) : 80; + const int weight = ( wit != uidToResourceMap.end() ) ? resabc->subresourceCompletionWeight( *wit ) : 80; + const int idx = addCompletionSource( subresourceLabel, weight ); + //kdDebug(5300) << (*it).fullEmail() << " subres=" << *wit << " weight=" << weight << endl; addContact( *it, weight, idx ); } } else { // KABC non-imap resource int weight = config.readNumEntry( resource->identifier(), 60 ); - s_completionSources->append( resource->resourceName() ); + int sourceIndex = addCompletionSource( resource->resourceName(), weight ); KABC::Resource::Iterator it; - for ( it = resource->begin(); it != resource->end(); ++it ) - addContact( *it, weight, s_completionSources->size()-1 ); + for ( it = resource->begin(); it != resource->end(); ++it ) { + addContact( *it, weight, sourceIndex ); + } } } @@ -577,12 +618,14 @@ void AddresseeLineEdit::addContact( const KABC::Addressee& addr, int weight, int if ( KPIM::DistributionList::isDistributionList( addr ) ) { //kdDebug(5300) << "AddresseeLineEdit::addContact() distribution list \"" << addr.formattedName() << "\" weight=" << weight << endl; - //for CompletionAuto - addCompletionItem( addr.formattedName(), weight, source ); + if ( m_allowDistLists ) { + //for CompletionAuto + addCompletionItem( addr.formattedName(), weight, source ); - //for CompletionShell, CompletionPopup - TQStringList sl( addr.formattedName() ); - addCompletionItem( addr.formattedName(), weight, source, &sl ); + //for CompletionShell, CompletionPopup + TQStringList sl( addr.formattedName() ); + addCompletionItem( addr.formattedName(), weight, source, &sl ); + } return; } @@ -730,6 +773,11 @@ void AddresseeLineEdit::addCompletionItem( const TQString& string, int weight, i void AddresseeLineEdit::slotStartLDAPLookup() { + KGlobalSettings::Completion mode = completionMode(); + + if ( mode == KGlobalSettings::CompletionNone ) + return; + if ( !s_LDAPSearch->isAvailable() ) { return; } @@ -765,7 +813,7 @@ void AddresseeLineEdit::startLoadingLDAPEntries() void AddresseeLineEdit::slotLDAPSearchData( const KPIM::LdapResultList& adrs ) { - if ( s_LDAPLineEdit != this ) + if ( adrs.isEmpty() || s_LDAPLineEdit != this ) return; for ( KPIM::LdapResultList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) { @@ -773,14 +821,20 @@ void AddresseeLineEdit::slotLDAPSearchData( const KPIM::LdapResultList& adrs ) addr.setNameFromString( (*it).name ); addr.setEmails( (*it).email ); - addContact( addr, (*it).completionWeight, (*it ).clientNumber ); + if ( !s_ldapClientToCompletionSourceMap->contains( (*it).clientNumber ) ) + updateLDAPWeights(); // we got results from a new source, so update the completion sources + + addContact( addr, (*it).completionWeight, (*s_ldapClientToCompletionSourceMap)[ (*it ).clientNumber ] ); } if ( (hasFocus() || completionBox()->hasFocus() ) && completionMode() != KGlobalSettings::CompletionNone - && completionMode() != KGlobalSettings::CompletionShell) { + && completionMode() != KGlobalSettings::CompletionShell ) { setText( m_previousAddresses + m_searchString ); - doCompletion( m_lastSearchMode ); + // only complete again if the user didn't change the selection while we were waiting + // otherwise the completion box will be closed + if ( m_searchString.stripWhiteSpace() != completionBox()->currentText().stripWhiteSpace() ) + doCompletion( m_lastSearchMode ); } } @@ -881,6 +935,10 @@ void AddresseeLineEdit::slotEditCompletionOrder() init(); // for s_LDAPSearch CompletionOrderEditor editor( s_LDAPSearch, this ); editor.exec(); + if ( m_useCompletion ) { + updateLDAPWeights(); + s_addressesDirty = true; + } } void KPIM::AddresseeLineEdit::slotIMAPCompletionOrderChanged() @@ -902,15 +960,23 @@ void AddresseeLineEdit::updateSearchString() int n = -1; bool inQuote = false; - for ( uint i = 0; i < m_searchString.length(); ++i ) { - if ( m_searchString[ i ] == '"' ) + uint searchStringLength = m_searchString.length(); + for ( uint i = 0; i < searchStringLength; ++i ) { + if ( m_searchString[ i ] == '"' ) { inQuote = !inQuote; - if ( m_searchString[ i ] == '\\' && (i + 1) < m_searchString.length() && m_searchString[ i + 1 ] == '"' ) + } + if ( m_searchString[ i ] == '\\' && + (i + 1) < searchStringLength && m_searchString[ i + 1 ] == '"' ) { ++i; - if ( inQuote ) + } + if ( inQuote ) { continue; - if ( m_searchString[ i ] == ',' || ( m_useSemiColonAsSeparator && m_searchString[ i ] == ';' ) ) + } + if ( i < searchStringLength && + ( m_searchString[ i ] == ',' || + ( m_useSemiColonAsSeparator && m_searchString[ i ] == ';' ) ) ) { n = i; + } } if ( n >= 0 ) { @@ -924,9 +990,7 @@ void AddresseeLineEdit::updateSearchString() m_previousAddresses = m_searchString.left( n ); m_searchString = m_searchString.mid( n ).stripWhiteSpace(); - } - else - { + } else { m_previousAddresses = TQString::null; } } @@ -954,18 +1018,30 @@ KCompletion::CompOrder KPIM::AddresseeLineEdit::completionOrder() return KCompletion::Sorted; } -int KPIM::AddresseeLineEdit::addCompletionSource( const TQString &source ) +int KPIM::AddresseeLineEdit::addCompletionSource( const TQString &source, int weight ) { - s_completionSources->append( source ); - return s_completionSources->size()-1; + TQMap::iterator it = s_completionSourceWeights->find( source ); + if ( it == s_completionSourceWeights->end() ) + s_completionSourceWeights->insert( source, weight ); + else + (*s_completionSourceWeights)[source] = weight; + + int sourceIndex = s_completionSources->findIndex( source ); + if ( sourceIndex == -1 ) { + s_completionSources->append( source ); + return s_completionSources->size() - 1; + } + else + return sourceIndex; } bool KPIM::AddresseeLineEdit::eventFilter(TQObject *obj, TQEvent *e) { if ( obj == completionBox() ) { - if ( e->type() == TQEvent::MouseButtonPress - || e->type() == TQEvent::MouseMove - || e->type() == TQEvent::MouseButtonRelease ) { + if ( e->type() == TQEvent::MouseButtonPress || + e->type() == TQEvent::MouseMove || + e->type() == TQEvent::MouseButtonRelease || + e->type() == TQEvent::MouseButtonDblClick ) { TQMouseEvent* me = static_cast( e ); // find list box item at the event position TQListBoxItem *item = completionBox()->itemAt( me->pos() ); @@ -1003,26 +1079,35 @@ bool KPIM::AddresseeLineEdit::eventFilter(TQObject *obj, TQEvent *e) } } if ( ( obj == this ) && - ( e->type() == TQEvent::KeyPress ) && - completionBox()->isVisible() ) { + ( e->type() == TQEvent::KeyPress || e->type() == TQEvent::KeyRelease ) && + completionBox()->isVisible() ) { TQKeyEvent *ke = static_cast( e ); - unsigned int currentIndex = completionBox()->currentItem(); + int currentIndex = completionBox()->currentItem(); + if ( currentIndex < 0 ) { + return true; + } + if ( ke->key() == Key_Up ) { //kdDebug() << "EVENTFILTER: Key_Up currentIndex=" << currentIndex << endl; // figure out if the item we would be moving to is one we want // to ignore. If so, go one further - TQListBoxItem *itemAbove = completionBox()->item( currentIndex - 1 ); + TQListBoxItem *itemAbove = completionBox()->item( currentIndex ); if ( itemAbove && itemIsHeader(itemAbove) ) { // there is a header above us, check if there is even further up // and if so go one up, so it'll be selected - if ( currentIndex > 1 && completionBox()->item( currentIndex - 2 ) ) { + if ( currentIndex > 0 && completionBox()->item( currentIndex - 1 ) ) { //kdDebug() << "EVENTFILTER: Key_Up -> skipping " << currentIndex - 1 << endl; completionBox()->setCurrentItem( itemAbove->prev() ); - completionBox()->setSelected( currentIndex - 2, true ); - } else if ( currentIndex == 1 ) { + completionBox()->setSelected( currentIndex - 1, true ); + } else if ( currentIndex == 0 ) { // nothing to skip to, let's stay where we are, but make sure the // first header becomes visible, if we are the first real entry completionBox()->ensureVisible( 0, 0 ); + //Kolab issue 2941: be sure to add email even if it's the only element. + if ( itemIsHeader( completionBox()->item( currentIndex ) ) ) { + currentIndex++; + } + completionBox()->setCurrentItem( itemAbove ); completionBox()->setSelected( currentIndex, true ); } return true; @@ -1030,14 +1115,15 @@ bool KPIM::AddresseeLineEdit::eventFilter(TQObject *obj, TQEvent *e) } else if ( ke->key() == Key_Down ) { // same strategy for downwards //kdDebug() << "EVENTFILTER: Key_Down. currentIndex=" << currentIndex << endl; - TQListBoxItem *itemBelow = completionBox()->item( currentIndex + 1 ); + TQListBoxItem *itemBelow = completionBox()->item( currentIndex ); if ( itemBelow && itemIsHeader( itemBelow ) ) { - if ( completionBox()->item( currentIndex + 2 ) ) { + if ( completionBox()->item( currentIndex + 1 ) ) { //kdDebug() << "EVENTFILTER: Key_Down -> skipping " << currentIndex+1 << endl; completionBox()->setCurrentItem( itemBelow->next() ); - completionBox()->setSelected( currentIndex + 2, true ); + completionBox()->setSelected( currentIndex + 1, true ); } else { // nothing to skip to, let's stay where we are + completionBox()->setCurrentItem( itemBelow ); completionBox()->setSelected( currentIndex, true ); } return true; @@ -1052,11 +1138,14 @@ bool KPIM::AddresseeLineEdit::eventFilter(TQObject *obj, TQEvent *e) TQListBoxItem *item = completionBox()->item( currentIndex ); if ( item && itemIsHeader(item) ) { completionBox()->setSelected( currentIndex, true ); - } - } else if ( ke->key() == Key_Tab || ke->key() == Key_Backtab ) { - /// first, find the header of teh current section + } + } else if ( e->type() == TQEvent::KeyRelease && + ( ke->key() == Key_Tab || ke->key() == Key_Backtab ) ) { + //kdDebug() << "EVENTFILTER: Key_Tab. currentIndex=" << currentIndex << endl; + /// first, find the header of the current section TQListBoxItem *myHeader = 0; - int i = currentIndex; + const int iterationstep = ke->key() == Key_Tab ? 1 : -1; + int i = QMIN( QMAX( currentIndex - iterationstep, 0 ), completionBox()->count() - 1 ); while ( i>=0 ) { if ( itemIsHeader( completionBox()->item(i) ) ) { myHeader = completionBox()->item( i ); @@ -1066,22 +1155,31 @@ bool KPIM::AddresseeLineEdit::eventFilter(TQObject *obj, TQEvent *e) } Q_ASSERT( myHeader ); // we should always be able to find a header - // find the next header (searching backwards, for Key_Backtab + // find the next header (searching backwards, for Key_Backtab) TQListBoxItem *nextHeader = 0; - const int iterationstep = ke->key() == Key_Tab ? 1 : -1; // when iterating forward, start at the currentindex, when backwards, // one up from our header, or at the end - uint j = ke->key() == Key_Tab ? currentIndex : i==0 ? completionBox()->count()-1 : (i-1) % completionBox()->count(); + uint j; + if ( ke->key() == Key_Tab ) { + j = currentIndex; + } else { + i = completionBox()->index( myHeader ); + if ( i == 0 ) { + j = completionBox()->count() - 1; + } else { + j = ( i - 1 ) % completionBox()->count(); + } + } while ( ( nextHeader = completionBox()->item( j ) ) && nextHeader != myHeader ) { if ( itemIsHeader(nextHeader) ) { - break; + break; } j = (j + iterationstep) % completionBox()->count(); } if ( nextHeader && nextHeader != myHeader ) { TQListBoxItem *item = completionBox()->item( j + 1 ); if ( item && !itemIsHeader(item) ) { - completionBox()->setSelected( j+1, true ); + completionBox()->setSelected( item, true ); completionBox()->setCurrentItem( item ); completionBox()->ensureCurrentVisible(); } @@ -1092,20 +1190,49 @@ bool KPIM::AddresseeLineEdit::eventFilter(TQObject *obj, TQEvent *e) return ClickLineEdit::eventFilter( obj, e ); } +class SourceWithWeight { + public: + int weight; // the weight of the source + TQString sourceName; // the name of the source, e.g. "LDAP Server" + int index; // index into s_completionSources + + bool operator< ( const SourceWithWeight &other ) { + if ( weight > other.weight ) + return true; + if ( weight < other.weight ) + return false; + return sourceName < other.sourceName; + } +}; + const TQStringList KPIM::AddresseeLineEdit::getAdjustedCompletionItems( bool fullSearch ) { TQStringList items = fullSearch ? s_completion->allMatches( m_searchString ) : s_completion->substringCompletion( m_searchString ); + // For weighted mode, the algorithm is the following: + // In the first loop, we add each item to its section (there is one section per completion source) + // We also add spaces in front of the items. + // The sections are appended to the items list. + // In the second loop, we then walk through the sections and add all the items in there to the + // sorted item list, which is the final result. + // + // The algo for non-weighted mode is different. + int lastSourceIndex = -1; unsigned int i = 0; + + // Maps indices of the items list, which are section headers/source items, + // to a TQStringList which are the items of that section/source. TQMap sections; TQStringList sortedItems; for ( TQStringList::Iterator it = items.begin(); it != items.end(); ++it, ++i ) { CompletionItemsMap::const_iterator cit = s_completionItemMap->find(*it); - if ( cit == s_completionItemMap->end() )continue; + if ( cit == s_completionItemMap->end() ) + continue; int idx = (*cit).second; + if ( s_completion->order() == KCompletion::Weighted ) { if ( lastSourceIndex == -1 || lastSourceIndex != idx ) { const TQString sourceLabel( (*s_completionSources)[idx] ); @@ -1124,11 +1251,30 @@ const TQStringList KPIM::AddresseeLineEdit::getAdjustedCompletionItems( bool ful sortedItems.append( *it ); } } + if ( s_completion->order() == KCompletion::Weighted ) { - for ( TQMap::Iterator it( sections.begin() ), end( sections.end() ); it != end; ++it ) { - sortedItems.append( (*s_completionSources)[it.key()] ); - for ( TQStringList::Iterator sit( (*it).begin() ), send( (*it).end() ); sit != send; ++sit ) { - sortedItems.append( *sit ); + + // Sort the sections + TQValueList sourcesAndWeights; + for ( uint i = 0; i < s_completionSources->size(); i++ ) { + SourceWithWeight sww; + sww.sourceName = (*s_completionSources)[i]; + sww.weight = (*s_completionSourceWeights)[sww.sourceName]; + sww.index = i; + sourcesAndWeights.append( sww ); + } + qHeapSort( sourcesAndWeights ); + + // Add the sections and their items to the final sortedItems result list + for( uint i = 0; i < sourcesAndWeights.size(); i++ ) { + TQStringList sectionItems = sections[sourcesAndWeights[i].index]; + if ( !sectionItems.isEmpty() ) { + sortedItems.append( sourcesAndWeights[i].sourceName ); + TQStringList sectionItems = sections[sourcesAndWeights[i].index]; + for ( TQStringList::Iterator sit( sectionItems.begin() ), send( sectionItems.end() ); + sit != send; ++sit ) { + sortedItems.append( *sit ); + } } } } else { diff --git a/libkdepim/addresseelineedit.h b/libkdepim/addresseelineedit.h index 5f8d2f49..86d8d8db 100644 --- a/libkdepim/addresseelineedit.h +++ b/libkdepim/addresseelineedit.h @@ -63,6 +63,12 @@ class KDE_EXPORT AddresseeLineEdit : public ClickLineEdit, public DCOPObject virtual void setFont( const TQFont& ); void allowSemiColonAsSeparator( bool ); + /// Sets if distribution lists will be used for completion. + /// This is true by default. + /// Call this right after the constructor, before anything calls loadContacts(), + /// otherwise this has no effect. + void allowDistributionLists( bool allowDistLists ); + public slots: void cursorAtEnd(); void enableCompletion( bool enable ); @@ -96,8 +102,10 @@ class KDE_EXPORT AddresseeLineEdit : public ClickLineEdit, public DCOPObject * Adds the name of a completion source to the internal list of * such sources and returns its index, such that that can be used * for insertion of items associated with that source. + * + * If the source already exists, the weight will be updated. */ - int addCompletionSource( const TQString& ); + int addCompletionSource( const TQString&, int weight ); /** return whether we are using sorted or weighted display */ static KCompletion::CompOrder completionOrder(); @@ -120,6 +128,7 @@ class KDE_EXPORT AddresseeLineEdit : public ClickLineEdit, public DCOPObject void init(); void startLoadingLDAPEntries(); void stopLDAPLookup(); + void updateLDAPWeights(); void setCompletedItems( const TQStringList& items, bool autoSuggest ); void addCompletionItem( const TQString& string, int weight, int source, const TQStringList * keyWords=0 ); @@ -136,6 +145,7 @@ class KDE_EXPORT AddresseeLineEdit : public ClickLineEdit, public DCOPObject bool m_lastSearchMode; bool m_searchExtended; //has \" been added? bool m_useSemiColonAsSeparator; + bool m_allowDistLists; //TQMap m_contactMap; @@ -147,6 +157,7 @@ class KDE_EXPORT AddresseeLineEdit : public ClickLineEdit, public DCOPObject static TQString *s_LDAPText; static AddresseeLineEdit *s_LDAPLineEdit; static TQStringList *s_completionSources; + static TQMap *s_ldapClientToCompletionSourceMap; class AddresseeLineEditPrivate; AddresseeLineEditPrivate *d; diff --git a/libkdepim/addressesdialog.cpp b/libkdepim/addressesdialog.cpp index 8a33d799..14feafb6 100644 --- a/libkdepim/addressesdialog.cpp +++ b/libkdepim/addressesdialog.cpp @@ -67,7 +67,7 @@ struct AddresseeViewItem::AddresseeViewItemPrivate { }; struct AddressesDialog::AddressesDialogPrivate { - AddressesDialogPrivate() : + AddressesDialogPrivate() : ui(0), personal(0), recent(0), toItem(0), ccItem(0), bccItem(0), ldapSearchDialog(0) @@ -77,6 +77,8 @@ struct AddressesDialog::AddressesDialogPrivate { AddresseeViewItem *personal; AddresseeViewItem *recent; + AddresseeViewItem *topdist; + TQPtrList dists; AddresseeViewItem *toItem; AddresseeViewItem *ccItem; @@ -181,11 +183,11 @@ void AddresseeViewItem::setSelected(bool selected) { if (selected == isSelected()) { - return; + return; } - emit addressSelected( this, selected ); - TQListViewItem::setSelected(selected); + emit addressSelected( this, selected ); + TQListViewItem::setSelected(selected); } int @@ -223,6 +225,9 @@ AddressesDialog::AddressesDialog( TQWidget *widget, const char *name ) initConnections(); d->ui->mAvailableView->setFocus(); + + setMainWidget( page ); + page->setMinimumSize( 750, 400 ); } AddressesDialog::~AddressesDialog() @@ -331,6 +336,7 @@ AddressesDialog::updateRecentAddresses() addAddresseeToAvailable( *it, d->recent ); if ( d->recent->childCount() > 0 ) { + d->recent->setOpen( true ); d->recent->setVisible( true ); } } @@ -472,8 +478,10 @@ AddressesDialog::updateAvailableAddressees() d->recent = 0; updateRecentAddresses(); + d->topdist = 0; addDistributionLists(); if ( d->personal->childCount() > 0 ) { + d->personal->setOpen( true ); d->personal->setVisible( true ); } @@ -724,10 +732,9 @@ AddressesDialog::addSelectedTo() addAddresseesToSelected( d->toItem, selectedAvailableAddresses ); selectedAvailableAddresses.clear(); - if ( d->toItem->childCount() > 0 ) + if ( d->toItem->childCount() > 0 ) { d->toItem->setVisible( true ); - else - { + } else { delete d->toItem; d->toItem = 0; } @@ -746,10 +753,9 @@ AddressesDialog::addSelectedCC() addAddresseesToSelected( d->ccItem, selectedAvailableAddresses ); selectedAvailableAddresses.clear(); - if ( d->ccItem->childCount() > 0 ) + if ( d->ccItem->childCount() > 0 ) { d->ccItem->setVisible( true ); - else - { + } else { delete d->ccItem; d->ccItem = 0; } @@ -768,10 +774,9 @@ AddressesDialog::addSelectedBCC() addAddresseesToSelected( d->bccItem, selectedAvailableAddresses ); selectedAvailableAddresses.clear(); - if ( d->bccItem->childCount() > 0 ) + if ( d->bccItem->childCount() > 0 ) { d->bccItem->setVisible( true ); - else - { + } else { delete d->bccItem; d->bccItem = 0; } @@ -950,7 +955,7 @@ AddressesDialog::searchLdap() void AddressesDialog::ldapSearchResult() { - TQStringList emails = TQStringList::split(',', d->ldapSearchDialog->selectedEMails() ); + TQStringList emails = KPIM::splitEmailAddrList( d->ldapSearchDialog->selectedEMails() ); TQStringList::iterator it( emails.begin() ); TQStringList::iterator end( emails.end() ); for ( ; it != end; ++it ){ @@ -979,22 +984,81 @@ AddressesDialog::filterChanged( const TQString& txt ) if ( txt.isEmpty() ) showAll = true; + int personalVisible = 0; + int recentVisible = 0; while ( it.current() ) { AddresseeViewItem* item = static_cast( it.current() ); ++it; + if ( showAll ) { + item->setOpen( true ); item->setVisible( true ); - if ( item->category() == AddresseeViewItem::Group ) - item->setOpen( false );//close to not have too many entries + // allen: I do not like the following behavior. comment out and see if anyone screams + //if ( item->category() == AddresseeViewItem::Group ) + // item->setOpen( false );//close to not have too many entries continue; } + if ( item->category() == AddresseeViewItem::Entry ) { - bool matches = item->matches( txt ) ; + bool matches = item->matches( txt ); item->setVisible( matches ); - if ( matches && static_cast(item)->parent() ) { - static_cast(item)->parent()->setOpen( true );//open the parents with found entries + TQListViewItem *parent = static_cast( item )->parent(); + if ( matches && parent ) { + if ( parent == d->personal ) { + personalVisible++; + } else if ( parent == d->recent ) { + recentVisible++; + } } } + if ( item->category() == AddresseeViewItem::Group ) { + item->setOpen( true ); + item->setVisible( true ); + } + } + + if ( !showAll && personalVisible == 0 ) { + d->personal->setOpen( false ); + d->personal->setVisible( false ); + } + if ( !showAll && recentVisible == 0 ) { + d->recent->setOpen( false ); + d->recent->setVisible( false ); + } + + int distlistgroupVisible = 0; + if ( !showAll ) { + TQPtrListIterator it( d->dists ); + for ( ; it.current(); ++it ) { + TQListViewItem *p = *it; + p->setVisible( true ); + AddresseeViewItem *p2 = static_cast( p->firstChild() ); + int pcount = 0; + while ( p2 ) { + if ( p2->matches( txt ) ) { + p2->setVisible( true ); + pcount++; + } else { + p2->setVisible( false ); + } + p2 = static_cast( p2->nextSibling() ); + } + if ( !pcount && !p->text( 0 ).contains( txt, false ) ) { + p->setVisible( false ); + } + distlistgroupVisible += pcount; + if ( p->text( 0 ).contains( txt, false ) ) { + distlistgroupVisible++; + } + } + } + if ( d->topdist ) { + if ( showAll || distlistgroupVisible > 0 ) { + d->topdist->setOpen( true ); + } else { + d->topdist->setOpen( false ); + d->topdist->setVisible( false ); + } } } @@ -1084,14 +1148,16 @@ AddressesDialog::addDistributionLists() if ( distLists.isEmpty() ) return; - AddresseeViewItem *topItem = new AddresseeViewItem( d->ui->mAvailableView, - i18n( "Distribution Lists" ) ); + if ( !d->topdist ) { + d->topdist = new AddresseeViewItem( d->ui->mAvailableView, i18n( "Distribution Lists" ) ); + } #ifdef KDEPIM_NEW_DISTRLISTS TQValueList::ConstIterator listIt; #else TQStringList::Iterator listIt; #endif + int total = 0; for ( listIt = distLists.begin(); listIt != distLists.end(); ++listIt ) { #ifdef KDEPIM_NEW_DISTRLISTS KPIM::DistributionList dlist = *listIt; @@ -1101,7 +1167,8 @@ AddressesDialog::addDistributionLists() KABC::DistributionList::Entry::List entries = dlist.entries(); #endif - AddresseeViewItem *item = new AddresseeViewItem( topItem, dlist.name() ); + AddresseeViewItem *item = new AddresseeViewItem( d->topdist, dlist.name() ); + d->dists.append( item ); connect( item, TQT_SIGNAL( addressSelected( AddresseeViewItem*, bool ) ), this, TQT_SLOT( availableAddressSelected( AddresseeViewItem*, bool ) ) ); @@ -1110,8 +1177,18 @@ AddressesDialog::addDistributionLists() #else KABC::DistributionList::Entry::List::Iterator itemIt; #endif - for ( itemIt = entries.begin(); itemIt != entries.end(); ++itemIt ) + for ( itemIt = entries.begin(); itemIt != entries.end(); ++itemIt ) { addAddresseeToAvailable( (*itemIt).addressee, item, false ); + } + if ( item->childCount() > 0 ) { + item->setOpen( true ); + item->setVisible( true ); + } + total += item->childCount(); + } + if ( total > 0 ) { + d->topdist->setOpen( true ); + d->topdist->setVisible( true ); } } diff --git a/libkdepim/calendardiffalgo.h b/libkdepim/calendardiffalgo.h index 43d85513..47e5efd4 100644 --- a/libkdepim/calendardiffalgo.h +++ b/libkdepim/calendardiffalgo.h @@ -22,9 +22,10 @@ #ifndef KPIM_CALENDARDIFFALGO_H #define KPIM_CALENDARDIFFALGO_H +#include "diffalgo.h" + #include #include -#include namespace KPIM { diff --git a/libkdepim/completionordereditor.cpp b/libkdepim/completionordereditor.cpp index b507d9b9..9fd4950a 100644 --- a/libkdepim/completionordereditor.cpp +++ b/libkdepim/completionordereditor.cpp @@ -27,6 +27,7 @@ * you do not wish to do so, delete this exception statement from * your version. */ +#include // FOR KDEPIM_NEW_DISTRLISTS #include "completionordereditor.h" #include "ldapclient.h" @@ -54,11 +55,11 @@ Several items are used in addresseelineedit's completion object: LDAP servers, KABC resources (imap and non-imap), Recent addresses (in kmail only). The default completion weights are as follow: + Recent addresses (kmail) : 10 (see kmail/kmlineeditspell.cpp) LDAP: 50, 49, 48 etc. (see ldapclient.cpp) KABC non-imap resources: 60 (see addresseelineedit.cpp and SimpleCompletionItem here) Distribution lists: 60 (see addresseelineedit.cpp and SimpleCompletionItem here) KABC imap resources: 80 (see kresources/imap/kabc/resourceimap.cpp) - Recent addresses (kmail) : 120 (see kmail/kmcomposewin.cpp) This dialog allows to change those weights, by showing one item per: - LDAP server @@ -99,21 +100,21 @@ private: void LDAPCompletionItem::save( CompletionOrderEditor* ) { - KConfig config( "kabldaprc" ); - config.setGroup( "LDAP" ); - config.writeEntry( TQString( "SelectedCompletionWeight%1" ).arg( mLdapClient->clientNumber() ), - mWeight ); - config.sync(); + KConfig * config = LdapSearch::config(); + config->setGroup( "LDAP" ); + config->writeEntry( TQString( "SelectedCompletionWeight%1" ).arg( mLdapClient->clientNumber() ), + mWeight ); + config->sync(); } // A simple item saved into kpimcompletionorder (no subresources, just name/identifier/weight) class SimpleCompletionItem : public CompletionItem { public: - SimpleCompletionItem( CompletionOrderEditor* editor, const TQString& label, const TQString& identifier ) + SimpleCompletionItem( CompletionOrderEditor* editor, const TQString& label, const TQString& identifier, int weight ) : mLabel( label ), mIdentifier( identifier ) { KConfigGroup group( editor->configFile(), "CompletionWeights" ); - mWeight = group.readNumEntry( mIdentifier, 60 ); + mWeight = group.readNumEntry( mIdentifier, weight ); } virtual TQString label() const { return mLabel; } virtual int completionWeight() const { return mWeight; } @@ -195,15 +196,17 @@ CompletionOrderEditor::CompletionOrderEditor( KPIM::LdapSearch* ldapSearch, } } else { // non-IMAP KABC resource mItems.append( new SimpleCompletionItem( this, (*resit)->resourceName(), - (*resit)->identifier() ) ); + (*resit)->identifier(), 60 ) ); } } #ifndef KDEPIM_NEW_DISTRLISTS // new distr lists are normal contact, so no separate item if using them // Add an item for distribution lists - mItems.append( new SimpleCompletionItem( this, i18n( "Distribution Lists" ), "DistributionLists" ) ); + mItems.append( new SimpleCompletionItem( this, i18n( "Distribution Lists" ), "DistributionLists" ), 60 ); #endif + mItems.append( new SimpleCompletionItem( this, i18n( "Recent Addresses" ), "Recent Addresses", 10 ) ); + // Now sort the items, then create the GUI mItems.sort(); diff --git a/libkdepim/csshelper.cpp b/libkdepim/csshelper.cpp index b65713dd..6b37fbf1 100644 --- a/libkdepim/csshelper.cpp +++ b/libkdepim/csshelper.cpp @@ -238,6 +238,7 @@ namespace KPIM { TQString::number( printFont.pointSize() ) ) + TQString( "tr.textAtmH,\n" + "tr.signInProgressH,\n" "tr.rfc822H,\n" "tr.encrH,\n" "tr.signOkKeyOkH,\n" @@ -367,6 +368,10 @@ namespace KPIM { " color: white ! important;\n" "}\n\n" + "a.black {\n" + " color: black ! important;\n" + "}\n\n" + "table.textAtm { background-color: %2 ! important; }\n\n" "tr.textAtmH {\n" @@ -378,10 +383,12 @@ namespace KPIM { " background-color: %3 ! important;\n" "}\n\n" + "table.signInProgress,\n" "table.rfc822 {\n" " background-color: %3 ! important;\n" "}\n\n" + "tr.signInProgressH,\n" "tr.rfc822H {\n" "%4" "}\n\n" ) @@ -538,6 +545,7 @@ namespace KPIM { " font-weight: normal ! important;\n" "}\n\n" + "tr.signInProgressH,\n" "tr.rfc822H,\n" "tr.encrH,\n" "tr.signOkKeyOkH,\n" @@ -565,6 +573,7 @@ namespace KPIM { "table.signErr,\n" "table.signOkKeyBad,\n" "table.signOkKeyOk,\n" + "table.signInProgress,\n" "div.fancy.header table {\n" " width: 100% ! important;\n" " border-width: 0px ! important;\n" @@ -630,4 +639,9 @@ namespace KPIM { mPrintFont = font; } + TQColor CSSHelper::pgpWarnColor() const + { + return cPgpWarnH; + } + } // namespace KPIM diff --git a/libkdepim/csshelper.h b/libkdepim/csshelper.h index 5d668713..3dbc80c0 100644 --- a/libkdepim/csshelper.h +++ b/libkdepim/csshelper.h @@ -67,6 +67,8 @@ class CSSHelper { void setBodyFont( const TQFont& font ); void setPrintFont( const TQFont& font ); + TQColor pgpWarnColor() const; + protected: /** Recalculate PGP frame and body colors (should be called after changing color settings) */ diff --git a/libkdepim/distributionlist.cpp b/libkdepim/distributionlist.cpp index e70b77b5..2ccdeb52 100644 --- a/libkdepim/distributionlist.cpp +++ b/libkdepim/distributionlist.cpp @@ -37,15 +37,13 @@ static ParseList parseCustom( const TQString& str ) if ( (*it).isEmpty() ) continue; // parse "uid,email" - TQStringList helpList = TQStringList::split( ',', (*it) ); + TQStringList helpList = TQStringList::split( ',', (*it), true ); Q_ASSERT( !helpList.isEmpty() ); if ( helpList.isEmpty() ) continue; - const TQString uid = helpList.first(); - TQString email; Q_ASSERT( helpList.count() < 3 ); // 1 or 2 items, but not more - if ( helpList.count() == 2 ) - email = helpList.last(); + const TQString uid = helpList.first(); + const TQString email = helpList.last(); res.append( qMakePair( uid, email ) ); } return res; diff --git a/libkdepim/htmldiffalgodisplay.h b/libkdepim/htmldiffalgodisplay.h index 23da21f4..4495e200 100644 --- a/libkdepim/htmldiffalgodisplay.h +++ b/libkdepim/htmldiffalgodisplay.h @@ -22,8 +22,9 @@ #ifndef KPIM_HTMLDIFFALGODISPLAY_H #define KPIM_HTMLDIFFALGODISPLAY_H +#include "diffalgo.h" + #include -#include #include namespace KPIM { diff --git a/libkdepim/kabcresourcecached.cpp b/libkdepim/kabcresourcecached.cpp index 26648e76..d46156f6 100644 --- a/libkdepim/kabcresourcecached.cpp +++ b/libkdepim/kabcresourcecached.cpp @@ -214,7 +214,11 @@ void ResourceCached::loadCache() KABC::VCardConverter converter; +#if defined(KABC_VCARD_ENCODING_FIX) + KABC::Addressee::List list = converter.parseVCardsRaw( file.readAll().data() ); +#else KABC::Addressee::List list = converter.parseVCards( TQString::fromUtf8( file.readAll() ) ); +#endif KABC::Addressee::List::Iterator it; for ( it = list.begin(); it != list.end(); ++it ) { @@ -239,8 +243,13 @@ void ResourceCached::saveCache() KABC::Addressee::List list = mAddrMap.values(); KABC::VCardConverter converter; +#if defined(KABC_VCARD_ENCODING_FIX) + TQCString vCard = converter.createVCardsRaw( list ); + file.writeBlock( vCard, vCard.length() ); +#else TQString vCard = converter.createVCards( list ); file.writeBlock( vCard.utf8(), vCard.utf8().length() ); +#endif file.close(); } @@ -259,7 +268,11 @@ void ResourceCached::cleanUpCache( const KABC::Addressee::List &addrList ) KABC::VCardConverter converter; +#if defined(KABC_VCARD_ENCODING_FIX) + KABC::Addressee::List list = converter.parseVCardsRaw( file.readAll().data() ); +#else KABC::Addressee::List list = converter.parseVCards( TQString::fromUtf8( file.readAll() ) ); +#endif KABC::Addressee::List::Iterator cacheIt; KABC::Addressee::List::ConstIterator it; @@ -351,9 +364,14 @@ void ResourceCached::saveChangesCache( const TQMap &m } KABC::VCardConverter converter; +#if defined(KABC_VCARD_ENCODING_FIX) + const TQCString vCards = converter.createVCardsRaw( list ); + file.writeBlock( vCards, vCards.length() ); +#else const TQString vCards = converter.createVCards( list ); TQCString content = vCards.utf8(); file.writeBlock( content, content.length() ); +#endif } } @@ -372,7 +390,11 @@ void ResourceCached::loadChangesCache( TQMap &map, co KABC::VCardConverter converter; +#if defined(KABC_VCARD_ENCODING_FIX) + const KABC::Addressee::List list = converter.parseVCardsRaw( file.readAll().data() ); +#else const KABC::Addressee::List list = converter.parseVCards( TQString::fromUtf8( file.readAll() ) ); +#endif KABC::Addressee::List::ConstIterator it; for ( it = list.begin(); it != list.end(); ++it ) map.insert( (*it).uid(), *it ); diff --git a/libkdepim/kaddrbook.cpp b/libkdepim/kaddrbook.cpp index ade6ef70..e5ef47e6 100644 --- a/libkdepim/kaddrbook.cpp +++ b/libkdepim/kaddrbook.cpp @@ -207,34 +207,9 @@ bool KAddrBookExternal::addVCard( const KABC::Addressee& addressee, TQWidget *pa bool KAddrBookExternal::addAddressee( const KABC::Addressee& addr ) { KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); - -#if KDE_IS_VERSION(3,4,89) - // This ugly hack will be removed in 4.0 - while ( !addressBook->loadingHasFinished() ) { - TQApplication::eventLoop()->processEvents( TQEventLoop::ExcludeUserInput ); - - // use sleep here to reduce cpu usage - usleep( 100 ); - } -#endif - - // Select a resource - TQPtrList kabcResources = addressBook->resources(); - - TQPtrList kresResources; - TQPtrListIterator resIt( kabcResources ); - KABC::Resource *kabcResource; - while ( ( kabcResource = resIt.current() ) != 0 ) { - ++resIt; - if ( !kabcResource->readOnly() ) { - KRES::Resource *res = static_cast( kabcResource ); - if ( res ) - kresResources.append( res ); - } - } - - kabcResource = static_cast( KRES::SelectDialog::getResource( kresResources, 0 ) ); - + KABC::Resource *kabcResource = selectResourceForSaving( addressBook ); + if( !kabcResource ) + return false; KABC::Ticket *ticket = addressBook->requestSaveTicket( kabcResource ); bool saved = false; if ( ticket ) { @@ -278,3 +253,33 @@ TQString KAddrBookExternal::expandDistributionList( const TQString& listName ) #endif return TQString::null; } + +KABC::Resource* KAddrBookExternal::selectResourceForSaving( KABC::AddressBook *addressBook ) +{ +#if KDE_IS_VERSION(3,4,89) + // This ugly hack will be removed in 4.0 + while ( !addressBook->loadingHasFinished() ) { + TQApplication::eventLoop()->processEvents( TQEventLoop::ExcludeUserInput ); + + // use sleep here to reduce cpu usage + usleep( 100 ); + } +#endif + + // Select a resource + TQPtrList kabcResources = addressBook->resources(); + + TQPtrList kresResources; + TQPtrListIterator resIt( kabcResources ); + KABC::Resource *kabcResource; + while ( ( kabcResource = resIt.current() ) != 0 ) { + ++resIt; + if ( !kabcResource->readOnly() ) { + KRES::Resource *res = static_cast( kabcResource ); + if ( res ) + kresResources.append( res ); + } + } + + return static_cast( KRES::SelectDialog::getResource( kresResources, 0 ) ); +} diff --git a/libkdepim/kaddrbook.h b/libkdepim/kaddrbook.h index dfa65823..374753e5 100644 --- a/libkdepim/kaddrbook.h +++ b/libkdepim/kaddrbook.h @@ -11,6 +11,10 @@ #include #include +namespace KABC { + class AddressBook; +} + class TQWidget; class KDE_EXPORT KAddrBookExternal { @@ -23,6 +27,17 @@ public: static bool addVCard( const KABC::Addressee& addressee, TQWidget *parent ); static TQString expandDistributionList( const TQString& listName ); + + /** + * Pops up a dialog to ask the user to select a resource for saving something, and + * returns the selected resource or 0 on failure or if the user cancelled. + * + * The addressbook used to get the resource list from. If the addressbook was loaded + * async and loading is not yet finished, this method will run an eventloop until the + * addressbook is loaded. + */ + static KABC::Resource* selectResourceForSaving( KABC::AddressBook *addressBook ); + private: static bool addAddressee( const KABC::Addressee& addressee ); }; diff --git a/libkdepim/kcmdesignerfields.cpp b/libkdepim/kcmdesignerfields.cpp index 79a570ae..330d8b84 100644 --- a/libkdepim/kcmdesignerfields.cpp +++ b/libkdepim/kcmdesignerfields.cpp @@ -163,6 +163,7 @@ void KCMDesignerFields::delayedInit() // Install a dirwatcher that will detect newly created or removed designer files KDirWatch *dw = new KDirWatch( this ); + KStandardDirs::makeDir(localUiDir()); dw->addDir( localUiDir(), true ); connect( dw, TQT_SIGNAL( created(const TQString&) ), TQT_SLOT( rebuildList() ) ); connect( dw, TQT_SIGNAL( deleted(const TQString&) ), TQT_SLOT( rebuildList() ) ); diff --git a/libkdepim/kdepimprotocols.h b/libkdepim/kdepimprotocols.h new file mode 100644 index 00000000..462fafda --- /dev/null +++ b/libkdepim/kdepimprotocols.h @@ -0,0 +1,32 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2005 Rafal Rzepecki + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KDEPIM_KDEPIMPROTOCOLS_H +#define KDEPIM_KDEPIMPROTOCOLS_H + +/* a central place to store protocol strings to avoid knowledge duplication */ + +#define KDEPIMPROTOCOL_CONTACT "uid:" +#define KDEPIMPROTOCOL_EMAIL "kmail:" +#define KDEPIMPROTOCOL_INCIDENCE "urn:x-ical" +#define KDEPIMPROTOCOL_NEWSARTICLE "news:" + +#endif diff --git a/libkdepim/kfoldertree.h b/libkdepim/kfoldertree.h index e09cd783..2d280584 100644 --- a/libkdepim/kfoldertree.h +++ b/libkdepim/kfoldertree.h @@ -39,6 +39,7 @@ struct KPaintInfo { { COL_SIZE, COL_ATTACHMENT, + COL_INVITATION, COL_IMPORTANT, COL_TODO, COL_SPAM_HAM, @@ -55,8 +56,9 @@ struct KPaintInfo { showSize(false), showAttachment(false), + showInvitation(false), showImportant(false), - showTodo( false ), + showTodo(false), showSpamHam(false), showWatchedIgnored(false), showStatus(false), @@ -73,6 +75,7 @@ struct KPaintInfo { dateCol(-1), sizeCol(-1), attachmentCol(-1), + invitationCol(-1), importantCol(-1), todoCol(-1), spamHamCol(-1), @@ -84,7 +87,8 @@ struct KPaintInfo { orderOfArrival(false), status(false), showCryptoIcons(false), - showAttachmentIcon(false) + showAttachmentIcon(false), + showInvitationIcon(false) {} bool pixmapOn; @@ -99,6 +103,7 @@ struct KPaintInfo { bool showSize; bool showAttachment; + bool showInvitation; bool showImportant; bool showTodo; bool showSpamHam; @@ -117,6 +122,7 @@ struct KPaintInfo { int dateCol; int sizeCol; int attachmentCol; + int invitationCol; int importantCol; int todoCol; int spamHamCol; @@ -129,6 +135,7 @@ struct KPaintInfo { bool status; bool showCryptoIcons; bool showAttachmentIcon; + bool showInvitationIcon; }; //========================================================================== diff --git a/libkdepim/kincidencechooser.cpp b/libkdepim/kincidencechooser.cpp index 53f72c88..2cef98f3 100644 --- a/libkdepim/kincidencechooser.cpp +++ b/libkdepim/kincidencechooser.cpp @@ -77,9 +77,9 @@ KIncidenceChooser::KIncidenceChooser(TQWidget *parent, char *name) : topLayout->addWidget( new TQLabel ( i18n("Last modified:"), topFrame) ,iii,0); mMod1lab = new TQLabel ( "Set Last modified", topFrame); topLayout->addWidget(mMod1lab,iii,1); - showDetails1 = new TQPushButton( i18n("Show Details"),topFrame ); - connect ( showDetails1, TQT_SIGNAL( clicked()), this, TQT_SLOT (showIncidence1() ) ); - topLayout->addWidget(showDetails1,iii,2); + mShowDetails1 = new TQPushButton( i18n("Show Details"),topFrame ); + connect ( mShowDetails1, TQT_SIGNAL( clicked()), this, TQT_SLOT (showIncidence1() ) ); + topLayout->addWidget(mShowDetails1,iii,2); ++iii; mInc2lab = new TQLabel ( "Local incidence", topFrame); @@ -90,19 +90,19 @@ KIncidenceChooser::KIncidenceChooser(TQWidget *parent, char *name) : topLayout->addWidget( new TQLabel ( i18n("Last modified:"), topFrame) ,iii,0); mMod2lab = new TQLabel ( "Set Last modified", topFrame); topLayout->addWidget(mMod2lab,iii,1); - showDetails2 = new TQPushButton( i18n("Show Details"), topFrame); - connect ( showDetails2, TQT_SIGNAL( clicked()), this, TQT_SLOT (showIncidence2() ) ); - topLayout->addWidget(showDetails2,iii,2); + mShowDetails2 = new TQPushButton( i18n("Show Details"), topFrame); + connect ( mShowDetails2, TQT_SIGNAL( clicked()), this, TQT_SLOT (showIncidence2() ) ); + topLayout->addWidget(mShowDetails2,iii,2); ++iii; // #if 0 // commented out for now, because the diff code has too many bugs - diffBut = new TQPushButton( i18n("Show Differences"), topFrame ); - connect ( diffBut, TQT_SIGNAL( clicked()), this, TQT_SLOT ( showDiff() ) ); - topLayout->addMultiCellWidget(diffBut, iii,iii,0,2); + mDiffBut = new TQPushButton( i18n("Show Differences"), topFrame ); + connect ( mDiffBut, TQT_SIGNAL( clicked()), this, TQT_SLOT ( showDiff() ) ); + topLayout->addMultiCellWidget(mDiffBut, iii,iii,0,2); ++iii; #else - diffBut = 0; + mDiffBut = 0; #endif mBg = new TQButtonGroup ( 1, Qt::Horizontal, i18n("Sync Preferences"), topFrame); topLayout->addMultiCellWidget(mBg, iii,iii,0,2); @@ -116,7 +116,7 @@ KIncidenceChooser::KIncidenceChooser(TQWidget *parent, char *name) : mTbL = 0; mTbN = 0; mDisplayDiff = 0; - choosedIncidence = 0; + mSelIncidence = 0; button = new TQPushButton( i18n("Apply This to All Conflicts of This Sync"), topFrame ); connect ( button, TQT_SIGNAL( clicked()), this, TQT_SLOT ( setSyncMode() ) ); topLayout->addMultiCellWidget(button, iii,iii,0,2); @@ -142,7 +142,7 @@ void KIncidenceChooser::setIncidence( KCal::Incidence* local ,KCal::Incidence* r KCal::Incidence* KIncidenceChooser::getIncidence( ) { - KCal::Incidence* retval = choosedIncidence; + KCal::Incidence* retval = mSelIncidence; if ( chooseMode == KIncidenceChooser::local ) retval = mInc1; else if ( chooseMode == KIncidenceChooser::remote ) @@ -185,21 +185,21 @@ void KIncidenceChooser::setLabels() if ( inc->type() == "Event" ) { des->setText( i18n( "Local Event") ); sum->setText( inc->summary().left( 30 )); - if ( diffBut ) - diffBut->setEnabled( true ); + if ( mDiffBut ) + mDiffBut->setEnabled( true ); } else if ( inc->type() == "Todo" ) { des->setText( i18n( "Local Todo") ); sum->setText( inc->summary().left( 30 )); - if ( diffBut ) - diffBut->setEnabled( true ); + if ( mDiffBut ) + mDiffBut->setEnabled( true ); } else if ( inc->type() == "Journal" ) { des->setText( i18n( "Local Journal") ); sum->setText( inc->description().left( 30 )); - if ( diffBut ) - diffBut->setEnabled( false ); + if ( mDiffBut ) + mDiffBut->setEnabled( false ); } mMod1lab->setText( KGlobal::locale()->formatDateTime(inc->lastModified() )); inc = mInc2; @@ -226,10 +226,10 @@ void KIncidenceChooser::showIncidence1() { if ( mTbL ) { if ( mTbL->isVisible() ) { - showDetails1->setText( i18n("Show Details")); + mShowDetails1->setText( i18n("Show Details")); mTbL->hide(); } else { - showDetails1->setText( i18n("Hide Details")); + mShowDetails1->setText( i18n("Hide Details")); mTbL->show(); mTbL->raise(); } @@ -242,7 +242,7 @@ void KIncidenceChooser::showIncidence1() mTbL->setMainWidget( textBrowser ); textBrowser->setText( KCal::IncidenceFormatter::extensiveDisplayString( mInc1 ) ); mTbL->setMinimumSize( 400, 400 ); - showDetails1->setText( i18n("Hide Details")); + mShowDetails1->setText( i18n("Hide Details")); mTbL->show(); mTbL->raise(); } @@ -251,9 +251,9 @@ void KIncidenceChooser::detailsDialogClosed() { KDialogBase* dialog = static_cast( const_cast( sender() ) ); if ( dialog == mTbL ) - showDetails1->setText( i18n( "Show details..." ) ); + mShowDetails1->setText( i18n( "Show details..." ) ); else - showDetails2->setText( i18n( "Show details..." ) ); + mShowDetails2->setText( i18n( "Show details..." ) ); } void KIncidenceChooser::showDiff() @@ -282,10 +282,10 @@ void KIncidenceChooser::showIncidence2() { if ( mTbN ) { if ( mTbN->isVisible() ) { - showDetails2->setText( i18n("Show Details")); + mShowDetails2->setText( i18n("Show Details")); mTbN->hide(); } else { - showDetails2->setText( i18n("Hide Details")); + mShowDetails2->setText( i18n("Hide Details")); mTbN->show(); mTbN->raise(); } @@ -298,27 +298,27 @@ void KIncidenceChooser::showIncidence2() mTbN->setMainWidget( textBrowser ); textBrowser->setText( KCal::IncidenceFormatter::extensiveDisplayString( mInc2 ) ); mTbN->setMinimumSize( 400, 400 ); - showDetails2->setText( i18n("Hide Details")); + mShowDetails2->setText( i18n("Hide Details")); mTbN->show(); mTbN->raise(); } void KIncidenceChooser::takeIncidence1() { - choosedIncidence = mInc1; + mSelIncidence = mInc1; TQDialog::accept(); } void KIncidenceChooser::takeIncidence2() { - choosedIncidence = mInc2; + mSelIncidence = mInc2; TQDialog::accept(); } void KIncidenceChooser::takeBoth() { - choosedIncidence = 0; + mSelIncidence = 0; TQDialog::accept(); } diff --git a/libkdepim/kincidencechooser.h b/libkdepim/kincidencechooser.h index 8c569e33..9d9e016a 100644 --- a/libkdepim/kincidencechooser.h +++ b/libkdepim/kincidencechooser.h @@ -24,44 +24,40 @@ #ifndef _KINCIDENCECHOOSER_H #define _KINCIDENCECHOOSER_H +#include "calendardiffalgo.h" +#include "htmldiffalgodisplay.h" #include -#include -#include -#include - -#include -#include "htmldiffalgodisplay.h" -#include "calendardiffalgo.h" +namespace KCal { + class Incidence; +} +using namespace KCal; -class TQRadioButton; class TQButtonGroup; -class TQVBox; -class TQStringList; -class TQTextBrowser; -class KDialogBase; /** Dialog to change the korganizer configuration. */ class KDE_EXPORT KIncidenceChooser : public KDialog { - Q_OBJECT -public: - enum mode { local, remote, newest, ask, both }; + Q_OBJECT + public: + enum mode { + local, remote, newest, ask, both + }; /** Initialize dialog and pages */ - KIncidenceChooser(TQWidget *parent=0,char *name=0); + KIncidenceChooser( TQWidget *parent=0, char *name=0 ); ~KIncidenceChooser(); //void setChooseText( TQString ); - void setIncidence( KCal::Incidence*,KCal::Incidence*); - KCal::Incidence* getIncidence(); + void setIncidence( KCal::Incidence *, KCal::Incidence * ); + KCal::Incidence *getIncidence(); static int chooseMode; -public slots: + public slots: void useGlobalMode(); -protected slots: + protected slots: void showIncidence1(); void showIncidence2(); void showDiff(); @@ -72,16 +68,15 @@ protected slots: void setSyncMode(); void detailsDialogClosed(); -protected: -private: - KPIM::HTMLDiffAlgoDisplay* mDisplayDiff; - KPIM::CalendarDiffAlgo* diff; - KDialogBase* mTbL, *mTbN; - KCal::Incidence* choosedIncidence; - KCal::Incidence* mInc1, *mInc2; + private: + KPIM::HTMLDiffAlgoDisplay *mDisplayDiff; + KPIM::CalendarDiffAlgo *diff; + KDialogBase *mTbL, *mTbN; + KCal::Incidence *mSelIncidence; + KCal::Incidence *mInc1, *mInc2; TQButtonGroup *mBg; - TQPushButton *diffBut,*showDetails1,*showDetails2; - TQLabel* mInc1lab, *mInc2lab,* mInc1Sumlab, *mInc2Sumlab,*mMod1lab,*mMod2lab; + TQPushButton *mDiffBut,*mShowDetails1,*mShowDetails2; + TQLabel *mInc1lab, *mInc2lab,* mInc1Sumlab, *mInc2Sumlab,*mMod1lab,*mMod2lab; }; diff --git a/libkdepim/komposer/core/komposerconfig.desktop b/libkdepim/komposer/core/komposerconfig.desktop index 9076759d..58c8bd76 100644 --- a/libkdepim/komposer/core/komposerconfig.desktop +++ b/libkdepim/komposer/core/komposerconfig.desktop @@ -27,7 +27,6 @@ Comment[de]=KDE-Komposer Comment[fr]=Komposer KDE Comment[ga]=Komposer KDE Comment[hi]=केडीई कम्पोज़र -Comment[ka]=KDE კომპოზიტორი Comment[ms]=Penggubah KDE Comment[nds]=Nettbreef-Editor vun KDE Comment[ne]=केडीई कम्पोजर @@ -46,7 +45,6 @@ Keywords[da]=brevskriver Keywords[de]=Komposer Keywords[fy]=komposer,opstellen, opsteller Keywords[hi]=कम्पोज़र -Keywords[ka]=komposer,ნოტები Keywords[nds]=Komposer Keywords[ne]=कम्पोजर Keywords[nl]=komposer,opstellen diff --git a/libkdepim/komposer/core/komposereditor.desktop b/libkdepim/komposer/core/komposereditor.desktop index 2e84bd17..fd1c1341 100644 --- a/libkdepim/komposer/core/komposereditor.desktop +++ b/libkdepim/komposer/core/komposereditor.desktop @@ -26,7 +26,6 @@ Comment[hu]=Komposer Comment[is]=Komposer ritill Comment[it]=Komposer editor Comment[ja]=Komposer,エディタ -Comment[ka]=Komposer-ის რედაქტორი Comment[kk]=Komposer өңдегіші Comment[km]=កម្មវិធី​និពន្ធ Komposer Comment[ko]=Komposer 편집기 diff --git a/libkdepim/komposer/core/komposerplugin.desktop b/libkdepim/komposer/core/komposerplugin.desktop index 08b450d6..b398e1c6 100644 --- a/libkdepim/komposer/core/komposerplugin.desktop +++ b/libkdepim/komposer/core/komposerplugin.desktop @@ -29,7 +29,6 @@ Name[hu]=Komposer bővítőmodul Name[is]=Komposer íforrit Name[it]=Plugin Komposer Name[ja]=Komposer プラグイン -Name[ka]=Komposer მოდული Name[kk]=Komposer плагин модулі Name[km]=កម្មវិធី​ជំនួយ Komposer Name[ko]=Komposer 플러그인 diff --git a/libkdepim/komposer/plugins/default/defaulteditor.desktop b/libkdepim/komposer/plugins/default/defaulteditor.desktop index b0fd5a65..910f7d89 100644 --- a/libkdepim/komposer/plugins/default/defaulteditor.desktop +++ b/libkdepim/komposer/plugins/default/defaulteditor.desktop @@ -38,7 +38,6 @@ Name[hu]=Komposer szerkesztő Name[is]=Komposer ritill Name[it]=Editor Komposer Name[ja]=Komposer エディタ -Name[ka]=Komposer რედაქტორი Name[kk]=Komposer өңдегіші Name[km]=កម្មវិធី​និពន្ធ Komposer Name[lt]=Komposer redaktorius @@ -84,7 +83,6 @@ Comment[hu]=A Komposer alapértelmezett szerkesztője Comment[is]=Sjálfgefinn ritill Komposer Comment[it]=Editor di default per Komposer Comment[ja]=Komposer 標準エディタ -Comment[ka]=Komposer ნაგულისხმევი რედაქტორი Comment[kk]=Komposer әдетті өңдегіші Comment[km]=កម្មវិធី​និពន្ធ​លំនាំដើម​របស់ Komposer Comment[ko]=Komposer 기본 편집기 diff --git a/libkdepim/kprefsdialog.cpp b/libkdepim/kprefsdialog.cpp index 9aef645e..1af04fd4 100644 --- a/libkdepim/kprefsdialog.cpp +++ b/libkdepim/kprefsdialog.cpp @@ -376,7 +376,7 @@ KPrefsWidDate::KPrefsWidDate( KConfigSkeleton::ItemDateTime *item, void KPrefsWidDate::readConfig() { - mDateEdit->setDate( mItem->value().date() ); + mDateEdit->setDate( mItem->value().date().isValid() ? mItem->value().date() : TQDate::currentDate() ); } void KPrefsWidDate::writeConfig() diff --git a/libkdepim/ktimeedit.cpp b/libkdepim/ktimeedit.cpp index 15d31841..2e82b7df 100644 --- a/libkdepim/ktimeedit.cpp +++ b/libkdepim/ktimeedit.cpp @@ -161,7 +161,7 @@ TQTime KTimeEdit::getTime() const ok = false; } } - kdDebug(5300) << "KTimeEdit::getTime(): " << time.toString() << endl; + // kdDebug(5300) << "KTimeEdit::getTime(): " << time.toString() << endl; return time; } diff --git a/libkdepim/kvcarddrag.cpp b/libkdepim/kvcarddrag.cpp index 2008f7a9..b8057367 100644 --- a/libkdepim/kvcarddrag.cpp +++ b/libkdepim/kvcarddrag.cpp @@ -25,8 +25,11 @@ static const char vcard_mime_string[] = "text/x-vcard"; -KVCardDrag::KVCardDrag( const TQString &content, TQWidget *dragsource, - const char *name ) +#if defined(KABC_VCARD_ENCODING_FIX) +KVCardDrag::KVCardDrag( const TQByteArray &content, TQWidget *dragsource, const char *name ) +#else +KVCardDrag::KVCardDrag( const TQString &content, TQWidget *dragsource, const char *name ) +#endif : TQStoredDrag( vcard_mime_string, dragsource, name ) { setVCard( content ); @@ -35,28 +38,60 @@ KVCardDrag::KVCardDrag( const TQString &content, TQWidget *dragsource, KVCardDrag::KVCardDrag( TQWidget *dragsource, const char *name ) : TQStoredDrag( vcard_mime_string, dragsource, name ) { +#if defined(KABC_VCARD_ENCODING_FIX) + setVCard( TQByteArray() ); +#else setVCard( TQString::null ); +#endif } +#if defined(KABC_VCARD_ENCODING_FIX) +void KVCardDrag::setVCard( const TQByteArray &content ) +{ + setEncodedData( content ); +} +#else void KVCardDrag::setVCard( const TQString &content ) { setEncodedData( content.utf8() ); } +#endif bool KVCardDrag::canDecode( TQMimeSource *e ) { return e->provides( vcard_mime_string ); } +#if defined(KABC_VCARD_ENCODING_FIX) +bool KVCardDrag::decode( TQMimeSource *e, TQByteArray &content ) +{ + if ( !canDecode( e ) ) { + return false; + } + content = e->encodedData( vcard_mime_string ); + return true; +} +#else bool KVCardDrag::decode( TQMimeSource *e, TQString &content ) { + if ( !canDecode( e ) ) { + return false; + } content = TQString::fromUtf8( e->encodedData( vcard_mime_string ) ); return true; } +#endif bool KVCardDrag::decode( TQMimeSource *e, KABC::Addressee::List& addressees ) { + if ( !canDecode( e ) ) { + return false; + } +#if defined(KABC_VCARD_ENCODING_FIX) + addressees = KABC::VCardConverter().parseVCardsRaw( e->encodedData( vcard_mime_string ).data() ); +#else addressees = KABC::VCardConverter().parseVCards( e->encodedData( vcard_mime_string ) ); +#endif return true; } diff --git a/libkdepim/kvcarddrag.h b/libkdepim/kvcarddrag.h index 55b3ef18..30d8dd74 100644 --- a/libkdepim/kvcarddrag.h +++ b/libkdepim/kvcarddrag.h @@ -26,6 +26,7 @@ #include #include +#include // for KABC_VCARD_ENCODING_FIX define #include class KVCardDragPrivate; @@ -49,14 +50,21 @@ class KDE_EXPORT KVCardDrag : public QStoredDrag /** * Constructs a vcard drag with the @p addressee. */ +#if defined(KABC_VCARD_ENCODING_FIX) + KVCardDrag( const TQByteArray &content, TQWidget *dragsource = 0, const char *name = 0 ); +#else KVCardDrag( const TQString &content, TQWidget *dragsource = 0, const char *name = 0 ); +#endif virtual ~KVCardDrag() {} /** * Sets the vcard of the drag to @p content. */ +#if defined(KABC_VCARD_ENCODING_FIX) + void setVCard( const TQByteArray &content ); +#else void setVCard( const TQString &content ); - +#endif /** * Returns true if the MIME source @p e contains a vcard object. */ @@ -65,7 +73,11 @@ class KDE_EXPORT KVCardDrag : public QStoredDrag /** * Decodes the MIME source @p e and puts the resulting vcard into @p content. */ +#if defined(KABC_VCARD_ENCODING_FIX) + static bool decode( TQMimeSource *e, TQByteArray &content ); +#else static bool decode( TQMimeSource *e, TQString &content ); +#endif /** * Decodes the MIME source @p e and puts the resulting vcard into @p addresseess. diff --git a/libkdepim/ldapclient.cpp b/libkdepim/ldapclient.cpp index 359e0356..39d384e8 100644 --- a/libkdepim/ldapclient.cpp +++ b/libkdepim/ldapclient.cpp @@ -349,6 +349,22 @@ LdapSearch::LdapSearch() TQT_SLOT(slotFileChanged(const TQString&))); } +void LdapSearch::readWeighForClient( LdapClient *client, KConfig *config, int clientNumber ) +{ + const int completionWeight = config->readNumEntry( TQString( "SelectedCompletionWeight%1" ).arg( clientNumber ), -1 ); + if ( completionWeight != -1 ) + client->setCompletionWeight( completionWeight ); +} + +void LdapSearch::updateCompletionWeights() +{ + KConfig *config = KPIM::LdapSearch::config(); + config->setGroup( "LDAP" ); + for ( uint i = 0; i < mClients.size(); i++ ) { + readWeighForClient( mClients[i], config, i ); + } +} + void LdapSearch::readConfig() { cancelSearch(); @@ -371,9 +387,7 @@ void LdapSearch::readConfig() if ( !server.host().isEmpty() ) mNoLDAPLookup = false; ldapClient->setServer( server ); - int completionWeight = config->readNumEntry( TQString( "SelectedCompletionWeight%1" ).arg( j ), -1 ); - if ( completionWeight != -1 ) - ldapClient->setCompletionWeight( completionWeight ); + readWeighForClient( ldapClient, config, j ); TQStringList attrs; // note: we need "objectClass" to detect distribution lists @@ -499,7 +513,7 @@ void LdapSearch::makeSearchData( TQStringList& ret, LdapResultList& resList ) bool wasCN = false; bool wasDC = false; - kdDebug(5300) << "\n\nLdapSearch::makeSearchData()\n\n" << endl; + //kdDebug(5300) << "\n\nLdapSearch::makeSearchData()\n\n" << endl; LdapAttrMap::ConstIterator it2; for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) { @@ -508,7 +522,7 @@ void LdapSearch::makeSearchData( TQStringList& ret, LdapResultList& resList ) if( len > 0 && '\0' == val[len-1] ) --len; const TQString tmp = TQString::fromUtf8( val, len ); - kdDebug(5300) << " key: \"" << it2.key() << "\" value: \"" << tmp << "\"" << endl; + //kdDebug(5300) << " key: \"" << it2.key() << "\" value: \"" << tmp << "\"" << endl; if ( it2.key() == "cn" ) { name = tmp; if( mail.isEmpty() ) @@ -551,7 +565,7 @@ void LdapSearch::makeSearchData( TQStringList& ret, LdapResultList& resList ) if( mails.isEmpty()) { if ( !mail.isEmpty() ) mails.append( mail ); if( isDistributionList ) { - kdDebug(5300) << "\n\nLdapSearch::makeSearchData() found a list: " << name << "\n\n" << endl; + //kdDebug(5300) << "\n\nLdapSearch::makeSearchData() found a list: " << name << "\n\n" << endl; ret.append( name ); // following lines commented out for bugfixing kolab issue #177: // @@ -568,14 +582,14 @@ void LdapSearch::makeSearchData( TQStringList& ret, LdapResultList& resList ) //mail.prepend( name ); //mail = name; } else { - kdDebug(5300) << "LdapSearch::makeSearchData() found BAD ENTRY: \"" << name << "\"" << endl; + //kdDebug(5300) << "LdapSearch::makeSearchData() found BAD ENTRY: \"" << name << "\"" << endl; continue; // nothing, bad entry } } else if ( name.isEmpty() ) { - kdDebug(5300) << "LdapSearch::makeSearchData() mail: \"" << mail << "\"" << endl; + //kdDebug(5300) << "LdapSearch::makeSearchData() mail: \"" << mail << "\"" << endl; ret.append( mail ); } else { - kdDebug(5300) << "LdapSearch::makeSearchData() name: \"" << name << "\" mail: \"" << mail << "\"" << endl; + //kdDebug(5300) << "LdapSearch::makeSearchData() name: \"" << name << "\" mail: \"" << mail << "\"" << endl; ret.append( TQString( "%1 <%2>" ).arg( name ).arg( mail ) ); } diff --git a/libkdepim/ldapclient.h b/libkdepim/ldapclient.h index 5167660e..cf3f5582 100644 --- a/libkdepim/ldapclient.h +++ b/libkdepim/ldapclient.h @@ -256,6 +256,7 @@ class KDE_EXPORT LdapSearch : public QObject void startSearch( const TQString& txt ); void cancelSearch(); bool isAvailable() const; + void updateCompletionWeights(); TQValueList< LdapClient* > clients() const { return mClients; } @@ -276,6 +277,7 @@ class KDE_EXPORT LdapSearch : public QObject void slotFileChanged( const TQString& ); private: + void readWeighForClient( LdapClient *client, KConfig *config, int clientNumber ); void readConfig(); void finish(); void makeSearchData( TQStringList& ret, LdapResultList& resList ); diff --git a/libkdepim/ldapsearchdialog.cpp b/libkdepim/ldapsearchdialog.cpp index 72011966..a51ccfb9 100644 --- a/libkdepim/ldapsearchdialog.cpp +++ b/libkdepim/ldapsearchdialog.cpp @@ -1,5 +1,5 @@ /* ldapsearchdialogimpl.cpp - LDAP access - * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2002 Klar�vdalens Datakonsult AB * * Author: Steffen Hansen * @@ -21,6 +21,8 @@ #include "ldapsearchdialog.h" #include "ldapclient.h" +#include + #include #include #include @@ -94,12 +96,20 @@ static TQMap& adrbookattr2ldap() return keys; } -class ContactListItem : public QListViewItem +namespace KPIM { + +class ContactListItem : public TQListViewItem { public: ContactListItem( TQListView* parent, const KPIM::LdapAttrMap& attrs ) : TQListViewItem( parent ), mAttrs( attrs ) - { } + { + const KPIM::LdapAttrValue &mailAttrs = attrs[ "mail" ]; + if ( mailAttrs.isEmpty() ) { + setSelectable( false ); + setEnabled( false ); + } + } KPIM::LdapAttrMap mAttrs; @@ -112,6 +122,8 @@ class ContactListItem : public QListViewItem } }; +} + LDAPSearchDialog::LDAPSearchDialog( TQWidget* parent, const char* name ) : KDialogBase( Plain, i18n( "Search for Addresses in Directory" ), Help | User1 | User2 | User3 | Cancel, Default, parent, name, false, true ) @@ -248,7 +260,7 @@ void LDAPSearchDialog::restoreSettings() KPIM::LdapClient* ldapClient = new KPIM::LdapClient( 0, this, "ldapclient" ); ldapClient->setServer( ldapServer ); - + TQStringList attrs; for ( TQMap::Iterator it = adrbookattr2ldap().begin(); it != adrbookattr2ldap().end(); ++it ) @@ -435,7 +447,7 @@ TQString LDAPSearchDialog::selectedEMails() const if ( name.isEmpty() ) { result << email; } else { - result << name + " <" + email + ">"; + result << KPIM::quoteNameIfNecessary( name ) + " <" + email + ">"; } } } diff --git a/libkdepim/ldapsearchdialog.h b/libkdepim/ldapsearchdialog.h index 1b3a7cc0..9b19e524 100644 --- a/libkdepim/ldapsearchdialog.h +++ b/libkdepim/ldapsearchdialog.h @@ -1,5 +1,5 @@ /* ldapsearchdialogimpl.h - LDAP access - * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2002 Klar�vdalens Datakonsult AB * * Author: Steffen Hansen * diff --git a/libkdepim/progressdialog.cpp b/libkdepim/progressdialog.cpp index f35f2390..227e7376 100644 --- a/libkdepim/progressdialog.cpp +++ b/libkdepim/progressdialog.cpp @@ -224,6 +224,11 @@ void TransactionItem::setCrypto( bool on ) mSSLLabel->setState( mSSLLabel->lastState() ); } +void TransactionItem::setTotalSteps( int totalSteps ) +{ + mProgress->setTotalSteps( totalSteps ); +} + void TransactionItem::slotItemCanceled() { if ( mItem ) @@ -279,6 +284,8 @@ ProgressDialog::ProgressDialog( TQWidget* alignWidget, TQWidget* parent, const c this, TQT_SLOT( slotTransactionLabel( KPIM::ProgressItem*, const TQString& ) ) ); connect ( pm, TQT_SIGNAL( progressItemUsesCrypto(KPIM::ProgressItem*, bool) ), this, TQT_SLOT( slotTransactionUsesCrypto( KPIM::ProgressItem*, bool ) ) ); + connect ( pm, TQT_SIGNAL( progressItemUsesBusyIndicator(KPIM::ProgressItem*, bool) ), + this, TQT_SLOT( slotTransactionUsesBusyIndicator( KPIM::ProgressItem*, bool ) ) ); connect ( pm, TQT_SIGNAL( showProgressDialog() ), this, TQT_SLOT( slotShow() ) ); } @@ -374,6 +381,17 @@ void ProgressDialog::slotTransactionUsesCrypto( ProgressItem *item, } } +void ProgressDialog::slotTransactionUsesBusyIndicator( KPIM::ProgressItem *item, bool value ) +{ + if ( mTransactionsToListviewItems.contains( item ) ) { + TransactionItem *ti = mTransactionsToListviewItems[ item ]; + if ( value ) + ti->setTotalSteps( 0 ); + else + ti->setTotalSteps( 100 ); + } +} + void ProgressDialog::slotShow() { setVisible( true ); diff --git a/libkdepim/progressdialog.h b/libkdepim/progressdialog.h index dc0e6794..cabcb69a 100644 --- a/libkdepim/progressdialog.h +++ b/libkdepim/progressdialog.h @@ -90,6 +90,7 @@ public: void setLabel( const TQString& ); void setStatus( const TQString& ); void setCrypto( bool ); + void setTotalSteps( int totalSteps ); ProgressItem* item() const { return mItem; } @@ -132,6 +133,7 @@ void slotTransactionAdded( KPIM::ProgressItem *item ); void slotTransactionStatus( KPIM::ProgressItem *item, const TQString& ); void slotTransactionLabel( KPIM::ProgressItem *item, const TQString& ); void slotTransactionUsesCrypto( KPIM::ProgressItem *item, bool ); + void slotTransactionUsesBusyIndicator( KPIM::ProgressItem*, bool ); void slotClose(); void slotShow(); diff --git a/libkdepim/progressmanager.cpp b/libkdepim/progressmanager.cpp index 0c73e0c5..2e5f4d6e 100644 --- a/libkdepim/progressmanager.cpp +++ b/libkdepim/progressmanager.cpp @@ -41,7 +41,7 @@ ProgressItem::ProgressItem( :mId( id ), mLabel( label ), mStatus( status ), mParent( parent ), mCanBeCanceled( canBeCanceled ), mProgress( 0 ), mTotal( 0 ), mCompleted( 0 ), mWaitingForKids( false ), mCanceled( false ), - mUsesCrypto( usesCrypto ) + mUsesCrypto( usesCrypto ), mUsesBusyIndicator( false ) {} ProgressItem::~ProgressItem() @@ -123,6 +123,12 @@ void ProgressItem::setUsesCrypto( bool v ) emit progressItemUsesCrypto( this, v ); } +void ProgressItem::setUsesBusyIndicator( bool useBusyIndicator ) +{ + mUsesBusyIndicator = useBusyIndicator; + emit progressItemUsesBusyIndicator( this, useBusyIndicator ); +} + // ====================================== ProgressManager::ProgressManager() :TQObject() { @@ -170,6 +176,8 @@ ProgressItem* ProgressManager::createProgressItemImpl( this, TQT_SIGNAL( progressItemLabel(KPIM::ProgressItem*, const TQString&) ) ); connect ( t, TQT_SIGNAL( progressItemUsesCrypto(KPIM::ProgressItem*, bool) ), this, TQT_SIGNAL( progressItemUsesCrypto(KPIM::ProgressItem*, bool) ) ); + connect ( t, TQT_SIGNAL( progressItemUsesBusyIndicator(KPIM::ProgressItem*, bool) ), + this, TQT_SIGNAL( progressItemUsesBusyIndicator(KPIM::ProgressItem*, bool) ) ); emit progressItemAdded( t ); } else { @@ -212,7 +220,12 @@ ProgressItem* ProgressManager::singleItem() const ProgressItem *item = 0; TQDictIterator< ProgressItem > it( mTransactions ); for ( ; it.current(); ++it ) { - if ( !(*it)->parent() ) { // if it's a top level one, only those count + + // No single item for progress possible, as one of them is a busy indicator one. + if ( (*it)->usesBusyIndicator() ) + return 0; + + if ( !(*it)->parent() ) { // if it's a top level one, only those count if ( item ) return 0; // we found more than one else diff --git a/libkdepim/progressmanager.h b/libkdepim/progressmanager.h index b4233f8d..bf284271 100644 --- a/libkdepim/progressmanager.h +++ b/libkdepim/progressmanager.h @@ -95,6 +95,18 @@ class KDE_EXPORT ProgressItem : public QObject */ void setUsesCrypto( bool v ); + /** + * @return whether this item uses a busy indicator instead of real progress display + */ + bool usesBusyIndicator() const { return mUsesBusyIndicator; } + + /** + * Sets whether this item uses a busy indicator instead of real progress for its progress bar. + * If it uses a busy indicator, you are still responsible for calling setProgress() from time to + * time to update the busy indicator. + */ + void setUsesBusyIndicator( bool useBusyIndicator ); + /** * @return The current progress value of this item in percent. */ @@ -192,6 +204,15 @@ signals: */ void progressItemUsesCrypto( KPIM::ProgressItem*, bool ); + /** + * Emitted when the busy indicator state of an item changes. Should be used + * by progress dialogs so that they can adjust the display of the progress bar + * to the new mode. + * @param item The updated item + * @param value True if the item uses a busy indicator now, false otherwise + */ + void progressItemUsesBusyIndicator( KPIM::ProgressItem *item, bool value ); + protected: /* Only to be used by our good friend the ProgressManager */ @@ -217,6 +238,7 @@ signals: bool mWaitingForKids; bool mCanceled; bool mUsesCrypto; + bool mUsesBusyIndicator; }; /** @@ -335,6 +357,9 @@ class KDE_EXPORT ProgressManager : public QObject /** * @return the only top level progressitem when there's only one. * Returns 0 if there is no item, or more than one top level item. + * Since this is used to calculate the overall progress, it will also return + * 0 if there is an item which uses a busy indicator, since that will invalidate + * the overall progress. */ ProgressItem* singleItem() const; @@ -361,6 +386,8 @@ class KDE_EXPORT ProgressManager : public QObject void progressItemLabel( KPIM::ProgressItem*, const TQString& ); /** @see ProgressItem::progressItemUsesCrypto() */ void progressItemUsesCrypto( KPIM::ProgressItem*, bool ); + /** @see ProgressItem::progressItemUsesBusyIndicator */ + void progressItemUsesBusyIndicator( KPIM::ProgressItem*, bool ); /** * Emitted when an operation requests the listeners to be shown. diff --git a/libkdepim/statusbarprogresswidget.cpp b/libkdepim/statusbarprogresswidget.cpp index c6fa807a..a7b240ea 100644 --- a/libkdepim/statusbarprogresswidget.cpp +++ b/libkdepim/statusbarprogresswidget.cpp @@ -105,6 +105,8 @@ StatusbarProgressWidget::StatusbarProgressWidget( ProgressDialog* progressDialog this, TQT_SLOT( slotProgressItemAdded( KPIM::ProgressItem * ) ) ); connect ( ProgressManager::instance(), TQT_SIGNAL( progressItemCompleted( KPIM::ProgressItem * ) ), this, TQT_SLOT( slotProgressItemCompleted( KPIM::ProgressItem * ) ) ); + connect ( ProgressManager::instance(), TQT_SIGNAL(progressItemUsesBusyIndicator(KPIM::ProgressItem*,bool)), + this, TQT_SLOT( updateBusyMode() ) ); connect ( progressDialog, TQT_SIGNAL( visibilityChanged( bool )), this, TQT_SLOT( slotProgressDialogVisible( bool ) ) ); @@ -119,9 +121,8 @@ StatusbarProgressWidget::StatusbarProgressWidget( ProgressDialog* progressDialog // In slot..Added we can only end up in 1 or N. // In slot..Removed we can end up in 0, 1, or we can stay in N if we were already. -void StatusbarProgressWidget::slotProgressItemAdded( ProgressItem *item ) +void StatusbarProgressWidget::updateBusyMode() { - if ( item->parent() ) return; // we are only interested in top level items connectSingleItem(); // if going to 1 item if ( mCurrentItem ) { // Exactly one item delete mBusyTimer; @@ -138,6 +139,14 @@ void StatusbarProgressWidget::slotProgressItemAdded( ProgressItem *item ) } } +void StatusbarProgressWidget::slotProgressItemAdded( ProgressItem *item ) +{ + if ( item->parent() ) + return; // we are only interested in top level items + + updateBusyMode(); +} + void StatusbarProgressWidget::slotProgressItemCompleted( ProgressItem *item ) { if ( item->parent() ) return; // we are only interested in top level items diff --git a/libkdepim/statusbarprogresswidget.h b/libkdepim/statusbarprogresswidget.h index d32a37b8..a3594301 100644 --- a/libkdepim/statusbarprogresswidget.h +++ b/libkdepim/statusbarprogresswidget.h @@ -72,6 +72,7 @@ protected slots: void slotProgressDialogVisible( bool ); void slotShowItemDelayed(); void slotBusyIndicator(); + void updateBusyMode(); protected: void setMode(); diff --git a/libkdepim/tests/Makefile.am b/libkdepim/tests/Makefile.am index 5adc2e1c..2e0d081b 100644 --- a/libkdepim/tests/Makefile.am +++ b/libkdepim/tests/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -I$(top_builddir)/libkdepim -I$(top_srcdir)/libemailfunctions $(all_includes) +AM_CPPFLAGS = -I$(top_builddir)/libkdepim -I$(top_srcdir)/libkdepim -I$(top_srcdir)/libemailfunctions $(all_includes) AM_LDFLAGS = $(all_libraries) $(KDE_RPATH) LDADD = ../libkdepim.la $(LIB_KDECORE) @@ -9,8 +9,10 @@ check_PROGRAMS = testwizard testaddresseelineedit \ test_kregexp \ testdateedit \ testlinklocator \ - testdistrlist + testkincidencechooser +# disabled because of X dependency +# testdistrlist testwizard_SOURCES = testwizard.cpp myconfig.kcfgc testaddresseelineedit_SOURCES = testaddresseelineedit.cpp @@ -20,9 +22,10 @@ testutf7decoder_SOURCES = testutf7decoder.cpp test_kregexp_SOURCES = test_kregexp.cpp testdateedit_SOURCES = testdateedit.cpp testlinklocator_SOURCES = testlinklocator.cpp -testdistrlist_SOURCES = testdistrlist.cpp +#testdistrlist_SOURCES = testdistrlist.cpp +testkincidencechooser_SOURCES = testkincidencechooser.cpp -TESTS = testdistrlist +#TESTS = testdistrlist METASOURCES = AUTO diff --git a/libkdepim/tests/testkincidencechooser.cpp b/libkdepim/tests/testkincidencechooser.cpp new file mode 100644 index 00000000..1204f8dc --- /dev/null +++ b/libkdepim/tests/testkincidencechooser.cpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2009 Allen Winter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include + +#include +using namespace KCal; + +#include "kincidencechooser.h" +using namespace KPIM; + +int main( int argc, char **argv ) +{ + KCmdLineArgs::init( argc, argv, "testkincidencechooser", 0, + "KIncidenceChooserTest", "1.0", + "kincidencechooser test app" ); + KApplication app; + KIncidenceChooser *chooser = new KIncidenceChooser(); + + Event event; + event.setSummary( i18n( "Meeting" ) ); + event.setDescription( i18n( "Discuss foo" ) ); + chooser->setIncidence( &event, &event ); + chooser->resize( 600, 600 ); + chooser->show(); + return app.exec(); +} diff --git a/libkdepim/tests/testutf7encoder2.cpp b/libkdepim/tests/testutf7encoder2.cpp index 57c19566..6d67bae0 100644 --- a/libkdepim/tests/testutf7encoder2.cpp +++ b/libkdepim/tests/testutf7encoder2.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include int main( int argc, char * argv[] ) { if ( argc == 1 ) { @@ -31,10 +31,10 @@ int main( int argc, char * argv[] ) { len = 1; cout << (enc->fromUnicode(TQString(buffer[i]),len)).data(); } - cout << endl; + std::cout << std::endl; #else int len = buffer.length(); - cout << (enc->fromUnicode(buffer,len)).data() << endl;; + std::cout << (enc->fromUnicode(buffer,len)).data() << std::endl;; #endif // CHAR_WISE delete enc; #endif // else USE_STREAM diff --git a/libkholidays/kholidays.cpp b/libkholidays/kholidays.cpp index 694a32dd..686b438a 100644 --- a/libkholidays/kholidays.cpp +++ b/libkholidays/kholidays.cpp @@ -90,7 +90,7 @@ TQString KHolidays::location() const TQString KHolidays::shortText( const TQDate &date ) { TQValueList lst = getHolidays( date ); - if ( !lst.isEmpty() ) + if ( !lst.isEmpty() ) return lst.first().text; else return TQString::null; } @@ -116,7 +116,7 @@ bool KHolidays::parseFile( const TQDate &date ) TQString KHolidays::getHoliday( const TQDate &date ) { TQValueList lst = getHolidays( date ); - if ( !lst.isEmpty() ) + if ( !lst.isEmpty() ) return lst.first().text; else return TQString::null; } @@ -124,6 +124,10 @@ TQString KHolidays::getHoliday( const TQDate &date ) TQValueList KHolidays::getHolidays( const TQDate &date ) { TQValueList list; + if ( !date.isValid() ) { + return list; + } + if ( !parseFile( date ) ) return list; struct holiday *hd = &holidays[date.dayOfYear()-1]; while ( hd ) { diff --git a/libkpgp/kpgp.cpp b/libkpgp/kpgp.cpp index ec80e688..8a9c6898 100644 --- a/libkpgp/kpgp.cpp +++ b/libkpgp/kpgp.cpp @@ -1007,7 +1007,7 @@ Module::getKpgp() { if (!kpgpObject) { - kdError(5100) << "there is no instance of kpgp available" << endl; + kpgpObject = new Module(); } return kpgpObject; } diff --git a/libkpgp/kpgpbase.cpp b/libkpgp/kpgpbase.cpp index fa444be7..157c73e3 100644 --- a/libkpgp/kpgpbase.cpp +++ b/libkpgp/kpgpbase.cpp @@ -149,7 +149,8 @@ Base::run( const char *cmd, const char *passphrase, bool onlyReadFromPGP ) if (!onlyReadFromPGP) { if (!input.isEmpty()) { // write to pin[1] one line after the other to prevent dead lock - for (unsigned int i=0; i 2) { if (poller[STD_IN].revents & ( POLLERR | POLLHUP ) ) { kdDebug(5100) << "GnuPG seems to have hung up" << endl; @@ -597,17 +599,17 @@ Base::runGpg( const char *cmd, const char *passphrase, bool onlyReadFromGnuPG ) if (!input.isEmpty()) { // search end of next line if ((len2 = input.find('\n', input_pos)) == -1) - len2 = input.length()-input_pos; + len2 = input_length - input_pos; else - len2 = len2-input_pos+1; + len2 = len2 - input_pos + 1; //kdDebug(5100) << "Trying to write " << len2 << " bytes to pin[1] ..." << endl; - len2 = write(pin[1], input.mid(input_pos,len2).data(), len2); + len2 = write(pin[1], input.data() + input_pos, len2 ); //kdDebug(5100) << "Wrote " << len2 << " bytes to pin[1] ..." << endl; input_pos += len2; // We are done. - if (input_pos >= input.length()) { + if (input_pos >= input_length) { //kdDebug(5100) << "All input was written to pin[1]" << endl; close (pin[1]); pin[1] = -1; @@ -635,7 +637,7 @@ Base::runGpg( const char *cmd, const char *passphrase, bool onlyReadFromGnuPG ) } while(waitpidRetVal == 0); if( 0 <= pin[1] ) - close (pin[1]); + close (pin[1]); close(pout[0]); close(perr[0]); @@ -646,7 +648,7 @@ Base::runGpg( const char *cmd, const char *passphrase, bool onlyReadFromGnuPG ) if (WIFEXITED(childExitStatus) != 0) { // Get the return code of the child childExitStatus = WEXITSTATUS(childExitStatus); - kdDebug(5100) << "GnuPG exited with exit status " << childExitStatus + kdDebug(5100) << "GnuPG exited with exit status " << childExitStatus << endl; } else { diff --git a/libkpimexchange/core/exchangedownload.cpp b/libkpimexchange/core/exchangedownload.cpp index c8a0a215..60879d26 100644 --- a/libkpimexchange/core/exchangedownload.cpp +++ b/libkpimexchange/core/exchangedownload.cpp @@ -49,7 +49,7 @@ #include extern "C" { - #include + #include } #include "exchangeclient.h" diff --git a/libkpimexchange/core/exchangeupload.cpp b/libkpimexchange/core/exchangeupload.cpp index bbc30906..29a796dd 100644 --- a/libkpimexchange/core/exchangeupload.cpp +++ b/libkpimexchange/core/exchangeupload.cpp @@ -33,7 +33,7 @@ #include extern "C" { - #include + #include } #include diff --git a/libkpimexchange/core/utils.cpp b/libkpimexchange/core/utils.cpp index 61847dbd..7e1cbadf 100644 --- a/libkpimexchange/core/utils.cpp +++ b/libkpimexchange/core/utils.cpp @@ -21,7 +21,7 @@ #include extern "C" { - #include + #include } #include "utils.h" diff --git a/libkpimidentities/identity.cpp b/libkpimidentities/identity.cpp index 19cd6593..fb2f1ac1 100644 --- a/libkpimidentities/identity.cpp +++ b/libkpimidentities/identity.cpp @@ -236,6 +236,7 @@ const Identity& Identity::null() bool Identity::isNull() const { return mIdentity.isEmpty() && mFullName.isEmpty() && mEmailAddr.isEmpty() && + mEmailAliases.empty() && mOrganization.isEmpty() && mReplyToAddr.isEmpty() && mBcc.isEmpty() && mVCardFile.isEmpty() && mFcc.isEmpty() && mDrafts.isEmpty() && mTemplates.isEmpty() && @@ -251,6 +252,7 @@ bool Identity::operator==( const Identity & other ) const { bool same = mUoid == other.mUoid && mIdentity == other.mIdentity && mFullName == other.mFullName && mEmailAddr == other.mEmailAddr && mOrganization == other.mOrganization && + mEmailAliases == other.mEmailAliases && mReplyToAddr == other.mReplyToAddr && mBcc == other.mBcc && mVCardFile == other.mVCardFile && mFcc == other.mFcc && @@ -271,6 +273,7 @@ bool Identity::operator==( const Identity & other ) const { if ( mIdentity != other.mIdentity ) kdDebug() << "mIdentity differs : " << mIdentity << " != " << other.mIdentity << endl; if ( mFullName != other.mFullName ) kdDebug() << "mFullName differs : " << mFullName << " != " << other.mFullName << endl; if ( mEmailAddr != other.mEmailAddr ) kdDebug() << "mEmailAddr differs : " << mEmailAddr << " != " << other.mEmailAddr << endl; + if ( mEmailAliases != other.mEmailAliases ) kdDebug() << "mEmailAliases differs : " << mEmailAliases.join(";") << " != " << other.mEmailAliases.join(";") << endl; if ( mOrganization != other.mOrganization ) kdDebug() << "mOrganization differs : " << mOrganization << " != " << other.mOrganization << endl; if ( mReplyToAddr != other.mReplyToAddr ) kdDebug() << "mReplyToAddr differs : " << mReplyToAddr << " != " << other.mReplyToAddr << endl; if ( mBcc != other.mBcc ) kdDebug() << "mBcc differs : " << mBcc << " != " << other.mBcc << endl; @@ -320,6 +323,7 @@ void Identity::readConfig( const KConfigBase * config ) mIdentity = config->readEntry("Identity"); mFullName = config->readEntry("Name"); mEmailAddr = config->readEntry("Email Address"); + mEmailAliases = config->readListEntry("Email Aliases"); mVCardFile = config->readPathEntry("VCardFile"); mOrganization = config->readEntry("Organization"); mPGPSigningKey = config->readEntry("PGP Signing Key").latin1(); @@ -362,6 +366,7 @@ void Identity::writeConfig( KConfigBase * config ) const config->writeEntry("SMIME Encryption Key", mSMIMEEncryptionKey.data()); config->writeEntry("Preferred Crypto Message Format", Kleo::cryptoMessageFormatToString( mPreferredCryptoMessageFormat ) ); config->writeEntry("Email Address", mEmailAddr); + config->writeEntry("Email Aliases", mEmailAliases); config->writeEntry("Reply-To Address", mReplyToAddr); config->writeEntry("Bcc", mBcc); config->writePathEntry("VCardFile", mVCardFile); @@ -385,7 +390,8 @@ TQDataStream & KPIM::operator<<( TQDataStream & stream, const KPIM::Identity & i << i.pgpEncryptionKey() << i.smimeSigningKey() << i.smimeEncryptionKey() - << i.emailAddr() + << i.primaryEmailAddress() + << i.emailAliases() << i.replyToAddr() << i.bcc() << i.vCardFile() @@ -411,6 +417,7 @@ TQDataStream & KPIM::operator>>( TQDataStream & stream, KPIM::Identity & i ) { >> i.mSMIMESigningKey >> i.mSMIMEEncryptionKey >> i.mEmailAddr + >> i.mEmailAliases >> i.mReplyToAddr >> i.mBcc >> i.mVCardFile @@ -484,11 +491,26 @@ void Identity::setSMIMEEncryptionKey(const TQCString &str) } //----------------------------------------------------------------------------- -void Identity::setEmailAddr(const TQString &str) +void Identity::setPrimaryEmailAddress( const TQString & str ) { mEmailAddr = str; } +void Identity::setEmailAliases( const TQStringList & list ) +{ + mEmailAliases = list; +} + +bool Identity::matchesEmailAddress( const TQString & addr ) const +{ + const TQString lower = addr.lower(); + if ( lower == mEmailAddr.lower() ) + return true; + for ( TQStringList::const_iterator it = mEmailAliases.begin(), end = mEmailAliases.end() ; it != end ; ++it ) + if ( (*it).lower() == lower ) + return true; + return false; +} //----------------------------------------------------------------------------- void Identity::setVCardFile(const TQString &str) diff --git a/libkpimidentities/identity.h b/libkpimidentities/identity.h index 236a2dc6..9baba4fa 100644 --- a/libkpimidentities/identity.h +++ b/libkpimidentities/identity.h @@ -206,8 +206,19 @@ public: void setPreferredCryptoMessageFormat( Kleo::CryptoMessageFormat format ) { mPreferredCryptoMessageFormat = format; } /** email address (without the user name - only name\@host) */ - TQString emailAddr() const { return mEmailAddr; } - void setEmailAddr(const TQString&); + KDE_DEPRECATED TQString emailAddr() const { return primaryEmailAddress(); } + KDE_DEPRECATED void setEmailAddr( const TQString & email ) { setPrimaryEmailAddress( email ); } + + /** primary email address (without the user name - only name\@host). + The primary email address is used for all outgoing mail. */ + TQString primaryEmailAddress() const { return mEmailAddr; } + void setPrimaryEmailAddress( const TQString & email ); + + /** email address aliases */ + const TQStringList & emailAliases() const { return mEmailAliases; } + void setEmailAliases( const TQStringList & ); + + bool matchesEmailAddress( const TQString & addr ) const; /** vCard to attach to outgoing emails */ TQString vCardFile() const { return mVCardFile; } @@ -295,6 +306,7 @@ protected: // and operator>> accordingly: uint mUoid; TQString mIdentity, mFullName, mEmailAddr, mOrganization; + TQStringList mEmailAliases; TQString mReplyToAddr; TQString mBcc; TQString mVCardFile; diff --git a/libkpimidentities/identitymanager.cpp b/libkpimidentities/identitymanager.cpp index 4c6ed02a..e7380ca6 100644 --- a/libkpimidentities/identitymanager.cpp +++ b/libkpimidentities/identitymanager.cpp @@ -211,7 +211,7 @@ void IdentityManager::writeConfig() const { // Also write the default identity to emailsettings KEMailSettings es; es.setSetting( KEMailSettings::RealName, (*it).fullName() ); - es.setSetting( KEMailSettings::EmailAddress, (*it).emailAddr() ); + es.setSetting( KEMailSettings::EmailAddress, (*it).primaryEmailAddress() ); es.setSetting( KEMailSettings::Organization, (*it).organization() ); es.setSetting( KEMailSettings::ReplyToAddress, (*it).replyToAddr() ); } @@ -304,16 +304,14 @@ const Identity & IdentityManager::identityForUoidOrDefault( uint uoid ) const const Identity & IdentityManager::identityForAddress( const TQString & addresses ) const { - TQStringList addressList = KPIM::splitEmailAddrList( addresses ); - for ( ConstIterator it = begin() ; it != end() ; ++it ) { - for( TQStringList::ConstIterator addrIt = addressList.begin(); - addrIt != addressList.end(); ++addrIt ) { - // I use TQString::utf8() instead of TQString::latin1() because I want - // a TQCString and not a char*. It doesn't matter because emailAddr() - // returns a 7-bit string. - if( (*it).emailAddr().lower() == - KPIM::getEmailAddress( *addrIt ).lower() ) { - return (*it); + const TQStringList addressList = KPIM::splitEmailAddrList( addresses ); + for( TQStringList::ConstIterator addrIt = addressList.begin(); + addrIt != addressList.end(); ++addrIt ) { + const TQString addr = KPIM::getEmailAddress( *addrIt ).lower(); + for ( ConstIterator it = begin() ; it != end() ; ++it ) { + const Identity & id = *it; + if ( id.matchesEmailAddress( addr ) ) { + return id; } } } @@ -499,7 +497,7 @@ TQStringList KPIM::IdentityManager::allEmails() const { TQStringList lst; for ( ConstIterator it = begin() ; it != end() ; ++it ) { - lst << (*it).emailAddr(); + lst << (*it).primaryEmailAddress(); } return lst; } diff --git a/libksieve/tests/lexertest.cpp b/libksieve/tests/lexertest.cpp index 23e0f886..dee04bd1 100644 --- a/libksieve/tests/lexertest.cpp +++ b/libksieve/tests/lexertest.cpp @@ -30,6 +30,7 @@ your version. */ #include +#include #include using KSieve::Lexer; diff --git a/libksieve/tests/parsertest.cpp b/libksieve/tests/parsertest.cpp index 87381a32..fdc08a97 100644 --- a/libksieve/tests/parsertest.cpp +++ b/libksieve/tests/parsertest.cpp @@ -30,6 +30,7 @@ your version. */ #include +#include #include using KSieve::Parser; diff --git a/mimelib/COPYRIGHT b/mimelib/COPYRIGHT new file mode 100644 index 00000000..75ab5778 --- /dev/null +++ b/mimelib/COPYRIGHT @@ -0,0 +1,13 @@ +Copyright (c) 1996, 1997 Douglas W. Sauder +All rights reserved. + +IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF +THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" +BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE, +SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/mimelib/datetime.cpp b/mimelib/datetime.cpp index a704e127..64f2eef2 100644 --- a/mimelib/datetime.cpp +++ b/mimelib/datetime.cpp @@ -125,7 +125,10 @@ void DwDateTime::Init() DwUint32 t_local = my_inv_gmtime(&local); DwUint32 t_utc = my_inv_gmtime(&utc); sDefaultZone = (int) (t_local - t_utc)/60; - sIsDefaultZoneSet = 1; + + // do not flag this as it would never check the zone again, which + // would lead to wrong timediff on the DST/non-DST day change + //sIsDefaultZoneSet = 1; } // Set the time zone from the default time zone mZone = sDefaultZone; diff --git a/mimelib/dw_cte.cpp b/mimelib/dw_cte.cpp index 6ebf07f5..4498597d 100644 --- a/mimelib/dw_cte.cpp +++ b/mimelib/dw_cte.cpp @@ -60,7 +60,7 @@ int DwToCrLfEol(const DwString& aSrcStr, DwString& aDestStr) char* destBuf = (char*) destStr.data(); // Encode source to destination - size_t destLen; + size_t destLen = 0; to_crlf(srcBuf, srcLen, destBuf, destSize, &destLen); aDestStr.assign(destStr, 0, destLen); return 0; @@ -78,7 +78,7 @@ int DwToLfEol(const DwString& aSrcStr, DwString& aDestStr) char* destBuf = (char*) destStr.data(); // Encode source to destination - size_t destLen; + size_t destLen = 0; to_lf(srcBuf, srcLen, destBuf, destSize, &destLen); aDestStr.assign(destStr, 0, destLen); return 0; @@ -96,7 +96,7 @@ int DwToCrEol(const DwString& aSrcStr, DwString& aDestStr) char* destBuf = (char*) destStr.data(); // Encode source to destination - size_t destLen; + size_t destLen = 0; to_cr(srcBuf, srcLen, destBuf, destSize, &destLen); aDestStr.assign(destStr, 0, destLen); return 0; @@ -129,7 +129,7 @@ int DwEncodeBase64(const DwString& aSrcStr, DwString& aDestStr) char* destBuf = (char*) destStr.data(); // Encode source to destination - size_t destLen; + size_t destLen = 0; int result = encode_base64(srcBuf, srcLen, destBuf, destSize, &destLen); aDestStr.assign(destStr, 0, destLen); @@ -149,7 +149,7 @@ int DwDecodeBase64(const DwString& aSrcStr, DwString& aDestStr) char* destBuf = (char*) destStr.data(); // Encode source to destination - size_t destLen; + size_t destLen = 0; int result = decode_base64(srcBuf, srcLen, destBuf, destSize, &destLen); aDestStr.assign(destStr, 0, destLen); @@ -170,7 +170,7 @@ int DwEncodeQuotedPrintable(const DwString& aSrcStr, DwString& aDestStr) char* destBuf = (char*) destStr.data(); // Encode source to destination - size_t destLen; + size_t destLen = 0; int result = encode_qp(srcBuf, srcLen, destBuf, destSize, &destLen); aDestStr.assign(destStr, 0, destLen); @@ -190,7 +190,7 @@ int DwDecodeQuotedPrintable(const DwString& aSrcStr, DwString& aDestStr) char* destBuf = (char*) destStr.data(); // Encode source to destination - size_t destLen; + size_t destLen = 0; int result = decode_qp(srcBuf, srcLen, destBuf, destSize, &destLen); aDestStr.assign(destStr, 0, destLen); diff --git a/mimelib/dw_date.cpp b/mimelib/dw_date.cpp index c8f89766..e4436bbe 100644 --- a/mimelib/dw_date.cpp +++ b/mimelib/dw_date.cpp @@ -246,7 +246,7 @@ int ParseRfc822Date(const char *str, struct tm *tms, int *z) case 'O': case 'o': /* Oct */ - if ((str[pos+1] == 'c' || str[pos+1] == 'c') + if ((str[pos+1] == 'c' || str[pos+1] == 'C') && (str[pos+2] == 't' || str[pos+2] == 'T')) { n = 9; pos += 3; @@ -713,6 +713,7 @@ int main() // "WWW MMM dd HH:MM:SS [Z] YYYY" zone is optional // e.g.: Fri Oct 14 09:21:49 CEST 2005 // or: Tue Mar 23 18:00:02 2004 +// also: Tue, Feb 04, 2003 00:01:20 +0000 #include #include @@ -733,6 +734,7 @@ int ParseDate(const char *str, struct tm *tms, int *z) int day=1, month=0, year=1970, hour=0, minute=0, second=0, zone=0; int i; + // check for week day for (i = 0; i < 7; i++) if ( strncmp(str, wdays[i], 3) == 0 ) break; @@ -740,8 +742,10 @@ int ParseDate(const char *str, struct tm *tms, int *z) if ( i == 7 ) return -1; + // check for month name + int offset = (str[3] == ',') ? 5 : 4; // allow weekday be terminated with "," for (i = 0; i < 12; i++) - if ( strncmp(str+4, months[i], 3) == 0 ) + if ( strncmp(str+offset, months[i], 3) == 0 ) break; if ( i == 12 ) @@ -749,27 +753,39 @@ int ParseDate(const char *str, struct tm *tms, int *z) month = i; - if ( sscanf(str+8, "%d %d:%d:%d", &day, &hour, &minute, &second) != 4 ) - return -1; - - if ( isdigit(str[20]) ) { // year without zone info, as in ctime() - if ( sscanf(str+20, "%d", &year) != 1 ) - return -1; + // try "dd, YYYY HH:MM:SS +ZZZZ" + int h, m; + char sign; + if ( sscanf(str+offset+4, "%d, %d %d:%d:%d %c%2d%2d", &day, &year, &hour, &minute, &second, &sign, &h, &m) == 8 ) { + // ok, worked, calculate zone + zone = h * 60 + m; + if ( sign == '-' ) + zone = -zone; } else { - if ( sscanf(str+20, "%*s %d", &year) != 1 ) + // try "dd HH:MM:SS" + if ( sscanf(str+8, "%d %d:%d:%d", &day, &hour, &minute, &second) != 4 ) return -1; - if ( strncmp(str+20, "EST" , 3) == 0 ) zone = -5 * 60; - else if ( strncmp(str+20, "EDT" , 3) == 0 ) zone = -4 * 60; - else if ( strncmp(str+20, "CST" , 3) == 0 ) zone = -6 * 60; - else if ( strncmp(str+20, "CDT" , 3) == 0 ) zone = -5 * 60; - else if ( strncmp(str+20, "MST" , 3) == 0 ) zone = -7 * 60; - else if ( strncmp(str+20, "MDT" , 3) == 0 ) zone = -6 * 60; - else if ( strncmp(str+20, "PST" , 3) == 0 ) zone = -8 * 60; - else if ( strncmp(str+20, "PDT" , 3) == 0 ) zone = -7 * 60; - else if ( strncmp(str+20, "CET" , 3) == 0 ) zone = 60; - else if ( strncmp(str+20, "CEST", 4) == 0 ) zone = 120; + if ( isdigit(str[20]) ) { // year without zone info, as in ctime() + if ( sscanf(str+20, "%d", &year) != 1 ) + return -1; + } + else { + if ( sscanf(str+20, "%*s %d", &year) != 1 ) + return -1; + + if ( strncmp(str+20, "EST" , 3) == 0 ) zone = -5 * 60; + else if ( strncmp(str+20, "EDT" , 3) == 0 ) zone = -4 * 60; + else if ( strncmp(str+20, "CST" , 3) == 0 ) zone = -6 * 60; + else if ( strncmp(str+20, "CDT" , 3) == 0 ) zone = -5 * 60; + else if ( strncmp(str+20, "MST" , 3) == 0 ) zone = -7 * 60; + else if ( strncmp(str+20, "MDT" , 3) == 0 ) zone = -6 * 60; + else if ( strncmp(str+20, "PST" , 3) == 0 ) zone = -8 * 60; + else if ( strncmp(str+20, "PDT" , 3) == 0 ) zone = -7 * 60; + else if ( strncmp(str+20, "CET" , 3) == 0 ) zone = 60; + else if ( strncmp(str+20, "CEST", 4) == 0 ) zone = 120; + } } if ( (day < 1) || (day > 31) || diff --git a/mimelib/dw_mime.cpp b/mimelib/dw_mime.cpp index 39f75f63..cca2fded 100644 --- a/mimelib/dw_mime.cpp +++ b/mimelib/dw_mime.cpp @@ -215,6 +215,9 @@ int DwSubtypeStrToEnum(const DwString& aStr) if (DwStrcasecmp(aStr, "digest") == 0) { type = DwMime::kSubtypeDigest; } + if (DwStrcasecmp(aStr, "directory") == 0) { + type = DwMime::kSubtypeDirectory; + } else if (DwStrcasecmp(aStr, "disposition-notification") == 0 ) { type = DwMime::kSubtypeDispositionNotification; } @@ -341,6 +344,9 @@ int DwSubtypeStrToEnum(const DwString& aStr) if (DwStrcasecmp(aStr, "x-diff") == 0) { type = DwMime::kSubtypeXDiff; } + if (DwStrcasecmp(aStr, "x-vcalendar") == 0) { + type = DwMime::kSubtypeVCal; + } break; } return type; @@ -375,6 +381,9 @@ void DwSubtypeEnumToStr(int aEnum, DwString& aStr) case DwMime::kSubtypeXVCard: aStr = "X-VCard"; break; + case DwMime::kSubtypeDirectory: + aStr = "Directory"; + break; case DwMime::kSubtypeXDiff: aStr = "X-Diff"; break; diff --git a/mimelib/dwstring.cpp b/mimelib/dwstring.cpp index cd3c7b1a..749f7c8e 100644 --- a/mimelib/dwstring.cpp +++ b/mimelib/dwstring.cpp @@ -399,7 +399,7 @@ DwString::DwString(const char* aCstr) mStart = 0; mLength = 0; if ( aCstr ) { - size_t len = (aCstr) ? strlen(aCstr) : 0; + size_t len = strlen(aCstr); _replace(0, mLength, aCstr, len); } } diff --git a/mimelib/mimelib/address.h b/mimelib/mimelib/address.h index c384b949..7ed56ba8 100644 --- a/mimelib/mimelib/address.h +++ b/mimelib/mimelib/address.h @@ -36,7 +36,6 @@ #endif class DwAddressList; -class DwMailboxList; //============================================================================= //+ Name DwAddress -- Abstract class representing an RFC-822 address @@ -149,7 +148,7 @@ protected: inline DwBool DwAddress::IsValid() const { - return mIsValid; + return mIsValid != 0; } #endif diff --git a/mimelib/mimelib/enum.h b/mimelib/mimelib/enum.h index 9e2d0ee3..93cfe010 100644 --- a/mimelib/mimelib/enum.h +++ b/mimelib/mimelib/enum.h @@ -78,6 +78,7 @@ enum { kSubtypeEnriched, kSubtypeHtml, kSubtypeXVCard, + kSubtypeDirectory, kSubtypeVCal, kSubtypeRtf, kSubtypeXDiff, diff --git a/mimelib/mimelib/headers.h b/mimelib/mimelib/headers.h index 8f802ce5..f545a3db 100644 --- a/mimelib/mimelib/headers.h +++ b/mimelib/mimelib/headers.h @@ -67,7 +67,6 @@ #endif class DwMessage; -class DwBodyPart; class DwField; class DwFieldBody; class DwDateTime; diff --git a/mimelib/mimelib/mailbox.h b/mimelib/mimelib/mailbox.h index d5d376f2..7e38ae4b 100644 --- a/mimelib/mimelib/mailbox.h +++ b/mimelib/mimelib/mailbox.h @@ -35,7 +35,6 @@ #include #endif -class DwGroup; //============================================================================= //+ Name DwMailbox -- Class representing an RFC-822 mailbox diff --git a/mimelib/mimelib/mboxlist.h b/mimelib/mimelib/mboxlist.h index 0487d751..723a8c55 100644 --- a/mimelib/mimelib/mboxlist.h +++ b/mimelib/mimelib/mboxlist.h @@ -35,7 +35,6 @@ #include #endif -class DwGroup; //============================================================================= diff --git a/mimelib/mimelib/msgcmp.h b/mimelib/mimelib/msgcmp.h index f1b28495..c9e6707e 100644 --- a/mimelib/mimelib/msgcmp.h +++ b/mimelib/mimelib/msgcmp.h @@ -256,8 +256,8 @@ public: //. Returns a object id that is unique among all DwMessageComponent //. objects. - const char* partId() const { return mId.c_str(); }; - void SetPartId( DwString id ) { mId = id; }; + const char* partId() const { return mId.c_str(); } + void SetPartId( DwString id ) { mId = id; } // set or get a unique string for that part protected: diff --git a/mimelib/mimelib/protocol.h b/mimelib/mimelib/protocol.h index 151a68c5..32186a82 100644 --- a/mimelib/mimelib/protocol.h +++ b/mimelib/mimelib/protocol.h @@ -38,6 +38,7 @@ class DwObserver { public: + virtual ~DwObserver(){} virtual void Notify()=0; }; diff --git a/mimelib/test_boyermor.cpp b/mimelib/test_boyermor.cpp index 2140c4fd..d2dcad80 100644 --- a/mimelib/test_boyermor.cpp +++ b/mimelib/test_boyermor.cpp @@ -2,6 +2,7 @@ #include #include +#include using std::cerr; using std::cout; using std::endl; diff --git a/networkstatus/networkstatus.desktop b/networkstatus/networkstatus.desktop index 9ebcffea..4d66b8c5 100644 --- a/networkstatus/networkstatus.desktop +++ b/networkstatus/networkstatus.desktop @@ -21,7 +21,6 @@ Name[hu]=Hálózati állapotjelző szolgáltatás Name[is]=Netstöðupúki Name[it]=Demone dello stato della rete Name[ja]=ネットワークステータスデーモン -Name[ka]=ქსელის მდგომარეობის დემონი Name[kk]=Желі күйінің қызметі Name[km]=ដេមិន​ស្ថានភាព​បណ្ដាញ Name[ko]=네트워크 상태 데몬 @@ -67,7 +66,6 @@ Comment[hu]=Figyeli a hálózati csatolók állapotát és értesítési lehető Comment[is]=Fylgist með stöðu netkorta og sendir tilkynningar til forrita sem nota netið. Comment[it]=Controlla lo stato delle interfacce di rete e fornisce notifiche alle applicazioni che usano al rete. Comment[ja]=ネットワークインターフェースの状態を追跡し、ネットワークを用いるアプリケーションに通知します -Comment[ka]=იძიებს ქსელის ინტერფეისის სტატუსებს და უზრუნველჰყოფს ქსელის მომხმარებელი პროგრამებისათვის შეტყობინებების მიწოდებას. Comment[kk]=Желі интерфейстерінің күйін бақылап, желіні қолданатын бағдарламаларын құлақтандыру қызметі. Comment[km]=តាមដាន​ស្ថានភាព​របស់​ចំណុច​ប្រទាក់​បណ្ដាញ ព្រម​ទាំង​ផ្ដល់​នូវ​ការ​ជូនដំណឹង​ទៅ​កម្មវិធី ដែល​ប្រើ​បណ្ដាញ ។ Comment[lt]=Seka tinklo sąsajų būseną ir informuoja apie jas programas, naudojančias tinklą diff --git a/patchlog.txt b/patchlog.txt new file mode 100644 index 00000000..e69de29b diff --git a/plugins/kmail/bodypartformatter/attendeeselector.cpp b/plugins/kmail/bodypartformatter/attendeeselector.cpp index 0ea917be..8fa67770 100644 --- a/plugins/kmail/bodypartformatter/attendeeselector.cpp +++ b/plugins/kmail/bodypartformatter/attendeeselector.cpp @@ -21,6 +21,7 @@ #include "ui_attendeeselector.h" #include +#include #include #include @@ -51,8 +52,15 @@ AttendeeSelector::AttendeeSelector(TQWidget * parent) TQStringList AttendeeSelector::attendees() const { TQStringList rv; - for ( uint i = 0; i < ui->attendeeList->count(); ++i ) - rv << ui->attendeeList->item( i )->text(); + for ( uint i = 0; i < ui->attendeeList->count(); ++i ) { + TQString addr = ui->attendeeList->item( i )->text(); + + // Build a nice address for this attendee including the CN. + TQString tname, temail; + KPIM::getNameAndMail( addr, tname, temail ); // ignore return value + // which is always false + rv << temail; + } return rv; } diff --git a/plugins/kmail/bodypartformatter/text_calendar.cpp b/plugins/kmail/bodypartformatter/text_calendar.cpp index 2ea1cfd5..49b60f5f 100644 --- a/plugins/kmail/bodypartformatter/text_calendar.cpp +++ b/plugins/kmail/bodypartformatter/text_calendar.cpp @@ -38,10 +38,14 @@ #include #include +#include + #include #include +#include #include #include +#include #include #include @@ -54,6 +58,7 @@ #include #include +#include #include #include #include @@ -65,6 +70,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include @@ -86,10 +96,10 @@ class CalendarManager public: CalendarManager(); ~CalendarManager(); - static KCal::Calendar* calendar(); + static Calendar* calendar(); private: - KCal::CalendarResources* mCalendar; + CalendarResources* mCalendar; static CalendarManager* mSelf; }; @@ -130,7 +140,7 @@ CalendarManager::~CalendarManager() mSelf = 0; } -KCal::Calendar* CalendarManager::calendar() +Calendar* CalendarManager::calendar() { if ( !mSelf ) { sCalendarDeleter.setObject( mSelf, new CalendarManager() ); @@ -139,12 +149,12 @@ KCal::Calendar* CalendarManager::calendar() } -class KMInvitationFormatterHelper : public KCal::InvitationFormatterHelper +class KMInvitationFormatterHelper : public InvitationFormatterHelper { public: KMInvitationFormatterHelper( KMail::Interface::BodyPart *bodyPart ) : mBodyPart( bodyPart ) {} virtual TQString generateLinkURL( const TQString &id ) { return mBodyPart->makeLink( id ); } - KCal::Calendar* calendar() const { return CalendarManager::calendar(); } + Calendar* calendar() const { return CalendarManager::calendar(); } private: KMail::Interface::BodyPart *mBodyPart; }; @@ -153,7 +163,7 @@ class Formatter : public KMail::Interface::BodyPartFormatter { public: Result format( KMail::Interface::BodyPart *bodyPart, - KMail::HtmlWriter *writer ) const + KMail::HtmlWriter *writer, KMail::Callback &callback ) const { if ( !writer ) // Guard against crashes in createReply() @@ -163,14 +173,15 @@ class Formatter : public KMail::Interface::BodyPartFormatter TQString source; /* If the bodypart does not have a charset specified, we need to fall back to utf8, not the KMail fallback encoding, so get the contents as binary and decode - explicitely. */ + explicitly. */ if ( bodyPart->contentTypeParameter( "charset").isEmpty() ) { const TQByteArray &ba = bodyPart->asBinary(); source = TQString::fromUtf8(ba); } else { source = bodyPart->asText(); } - TQString html = IncidenceFormatter::formatICalInvitation( source, &cl, &helper ); + TQString html = + IncidenceFormatter::formatICalInvitationNoHtml( source, &cl, &helper, callback.sender() ); if ( html.isEmpty() ) return AsIcon; writer->queue( html ); @@ -201,6 +212,25 @@ static TQString directoryForStatus( Attendee::PartStat status ) return dir; } +static Incidence *icalToString( const TQString &iCal ) +{ + CalendarLocal calendar( KPimPrefs::timezone() ) ; + ICalFormat format; + ScheduleMessage *message = + format.parseScheduleMessage( &calendar, iCal ); + if ( !message ) + //TODO: Error message? + return 0; + return dynamic_cast( message->event() ); +} + +static ScheduleMessage *icalToMessage( const TQString &iCal ) +{ + CalendarLocal calendar( KPimPrefs::timezone() ) ; + ICalFormat format; + return format.parseScheduleMessage( &calendar, iCal ); +} + class UrlHandler : public KMail::Interface::BodyPartURLHandler { public: @@ -209,19 +239,6 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler kdDebug() << "UrlHandler() (iCalendar)" << endl; } - Incidence* icalToString( const TQString& iCal ) const - { - CalendarLocal calendar( KPimPrefs::timezone() ) ; - ICalFormat format; - ScheduleMessage *message = - format.parseScheduleMessage( &calendar, iCal ); - if ( !message ) - //TODO: Error message? - return 0; - return dynamic_cast( message->event() ); - } - - Attendee *findMyself( Incidence* incidence, const TQString& receiver ) const { Attendee::List attendees = incidence->attendees(); @@ -340,6 +357,13 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler break; } + // Set the organizer to the sender, if the ORGANIZER hasn't been set. + if ( incidence->organizer().isEmpty() ) { + TQString tname, temail; + KPIM::getNameAndMail( callback.sender(), tname, temail ); + incidence->setOrganizer( Person( tname, temail ) ); + } + TQString recv = to; if ( recv.isEmpty() ) recv = incidence->organizer().fullName(); @@ -347,7 +371,7 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler return callback.mailICal( recv, msg, subject, statusString, type != Forward ); } - void ensureKorganizerRunning() const + void ensureKorganizerRunning( bool switchTo ) const { TQString error; TQCString dcopService; @@ -359,6 +383,10 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler TQCString dummy; if ( !kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", TQByteArray(), dummy, dummy ) ) { DCOPRef ref( dcopService, dcopService ); // talk to the KUniqueApplication or its kontact wrapper + if ( switchTo ) { + ref.call( "newInstance()" ); // activate korganizer window + } + DCOPReply reply = ref.call( "load()" ); if ( reply.isValid() && (bool)reply ) { kdDebug() << "Loaded " << dcopService << " successfully" << endl; @@ -390,11 +418,126 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler // Now ensure that korganizer is running; otherwise start it, to prevent surprises // (https://intevation.de/roundup/kolab/issue758) - ensureKorganizerRunning(); + ensureKorganizerRunning( false ); return true; } + bool cancelPastInvites( Incidence *incidence, const TQString &path ) const + { + TQString warnStr; + TQDateTime now = TQDateTime::currentDateTime(); + TQDate today = now.date(); + Event * const event = dynamic_cast( incidence ); + Todo * const todo = dynamic_cast( incidence ); + if ( incidence->type() == "Event" ) { + Q_ASSERT( event ); + if ( !event->doesFloat() ) { + if ( event->dtEnd() < now ) { + warnStr = i18n( "\"%1\" occurred already." ).arg( event->summary() ); + } else if ( event->dtStart() <= now && now <= event->dtEnd() ) { + warnStr = i18n( "\"%1\" is currently in-progress." ).arg( event->summary() ); + } + } else { + if ( event->dtEnd().date() < today ) { + warnStr = i18n( "\"%1\" occurred already." ).arg( event->summary() ); + } else if ( event->dtStart().date() <= today && today <= event->dtEnd().date() ) { + warnStr = i18n( "\"%1\", happening all day today, is currently in-progress." ). + arg( event->summary() ); + } + } + } else if ( incidence->type() == "Todo" ) { + Q_ASSERT( todo ); + if ( !todo->doesFloat() ) { + if ( todo->hasDueDate() ) { + if ( todo->dtDue() < now ) { + warnStr = i18n( "\"%1\" is past due." ).arg( todo->summary() ); + } else if ( todo->hasStartDate() && todo->dtStart() <= now && now <= todo->dtDue() ) { + warnStr = i18n( "\"%1\" is currently in-progress." ).arg( todo->summary() ); + } + } else if ( todo->hasStartDate() ) { + if ( todo->dtStart() < now ) { + warnStr = i18n( "\"%1\" has already started." ).arg( todo->summary() ); + } + } + } else { + if ( todo->hasDueDate() ) { + if ( todo->dtDue().date() < today) { + warnStr = i18n( "\"%1\" is past due." ).arg( todo->summary() ); + } else if ( todo->hasStartDate() && + todo->dtStart().date() <= today && today <= todo->dtDue().date() ) { + warnStr = i18n( "\"%1\", happening all-day today, is currently in-progress." ). + arg( todo->summary() ); + } + } else if ( todo->hasStartDate() ) { + if ( todo->dtStart().date() < today ) { + warnStr = i18n( "\"%1\", happening all day, has already started." ). + arg( todo->summary() ); + } + } + } + } + + if ( !warnStr.isEmpty() ) { + TQString queryStr; + Q_ASSERT( event || todo ); + if ( path == "accept" ) { + if ( event ) { + queryStr = i18n( "Do you still want to accept the invitation?" ); + } else if ( todo ) { + queryStr = i18n( "Do you still want to accept the task?" ); + } + } else if ( path == "accept_conditionally" ) { + if ( event ) { + queryStr = i18n( "Do you still want to send conditional acceptance of the invitation?" ); + } else if ( todo ) { + queryStr = i18n( "Do you still want to send conditional acceptance of the task?" ); + } + } else if ( path == "accept_counter" ) { + queryStr = i18n( "Do you still want to accept the counter proposal?" ); + } else if ( path == "counter" ) { + queryStr = i18n( "Do you still want to send a counter proposal?" ); + } else if ( path == "decline" ) { + queryStr = i18n( "Do you still want to send a decline response?" ); + } else if ( path == "decline_counter" ) { + queryStr = i18n( "Do you still want to decline the counter proposal?" ); + } else if ( path == "reply" ) { + queryStr = i18n( "Do you still want to record this reponse in your calendar?" ); + } else if ( path == "delegate" ) { + if ( event ) { + queryStr = i18n( "Do you still want to delegate this invitation?" ); + } else if ( todo ) { + queryStr = i18n( "Do you still want to delegate this task?" ); + } + } else if ( path == "forward" ) { + if ( event ) { + queryStr = i18n( "Do you still want to forward this invitation?" ); + } else if ( todo ) { + queryStr = i18n( "Do you still want to forward this task?" ); + } + } else if ( path == "check_calendar" ) { + queryStr = i18n( "Do you still want to check your calendar?" ); + } else if ( path == "record" ) { + if ( event ) { + queryStr = i18n( "Do you still want to record this invitation in your calendar?" ); + } else if ( todo ) { + queryStr = i18n( "Do you still want to record this task in your calendar?" ); + } + } else if ( path.startsWith( "ATTACH:" ) ) { + return false; + } else { + queryStr = i18n( "%1?" ).arg( path ); + } + + if ( KMessageBox::warningYesNo( + 0, + i18n( "%1\n%2" ).arg( warnStr ).arg( queryStr ) ) == KMessageBox::No ) { + return true; + } + } + return false; + } + bool handleInvitation( const TQString& iCal, Attendee::PartStat status, KMail::Callback &callback ) const { @@ -405,17 +548,22 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler // Must be some error. Still return true though, since we did handle it return true; - // get comment for tentative acceptance - Incidence* incidence = icalToString( iCal ); + Incidence *incidence = icalToString( iCal ); + // get comment for tentative acceptance if ( callback.askForComment( status ) ) { bool ok = false; TQString comment = KInputDialog::getMultiLineText( i18n("Reaction to Invitation"), i18n("Comment:"), TQString(), &ok ); if ( !ok ) return true; - if ( !comment.isEmpty() ) - incidence->addComment( comment ); + if ( !comment.isEmpty() ) { + if ( callback.outlookCompatibleInvitationReplyComments() ) { + incidence->setDescription( comment ); + } else { + incidence->addComment( comment ); + } + } } // First, save it for KOrganizer to handle @@ -521,7 +669,7 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler void showCalendar( const TQDate &date ) const { - ensureKorganizerRunning(); + ensureKorganizerRunning( true ); // raise korganizer part in kontact or the korganizer app kapp->dcopClient()->send( "korganizer", "korganizer", "newInstance()", TQByteArray() ); TQByteArray arg; @@ -550,13 +698,17 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler Incidence* incidence = icalToString( iCal ); if ( callback.askForComment( Attendee::Declined ) ) { bool ok = false; - // ### string freeze - TQString comment = KInputDialog::getMultiLineText( i18n("Reaction to Invitation") /* i18n("Decline Counter Proposal") */, + TQString comment = KInputDialog::getMultiLineText( i18n("Decline Counter Proposal"), i18n("Comment:"), TQString(), &ok ); if ( !ok ) return true; - if ( !comment.isEmpty() ) - incidence->addComment( comment ); + if ( !comment.isEmpty() ) { + if ( callback.outlookCompatibleInvitationReplyComments() ) { + incidence->setDescription( comment ); + } else { + incidence->addComment( comment ); + } + } } return mail( incidence, callback, Attendee::NeedsAction, Scheduler::Declinecounter, callback.sender(), DeclineCounter ); @@ -576,17 +728,39 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler bool handleClick( KMail::Interface::BodyPart *part, const TQString &path, KMail::Callback& c ) const { + if ( !CalHelper::hasMyWritableEventsFolders( "calendar" ) ) { + KMessageBox::error( + 0, + i18n( "You have no writable calendar folders for invitations, " + "so storing or saving a response will not be possible.\n" + "Please create at least 1 writable events calendar and re-sync." ) ); + return false; + } + + + // If the bodypart does not have a charset specified, we need to fall back to utf8, + // not the KMail fallback encoding, so get the contents as binary and decode explicitly. TQString iCal; - /* If the bodypart does not have a charset specified, we need to fall back to - utf8, not the KMail fallback encoding, so get the contents as binary and decode - explicitely. */ if ( part->contentTypeParameter( "charset").isEmpty() ) { const TQByteArray &ba = part->asBinary(); iCal = TQString::fromUtf8(ba); } else { iCal = part->asText(); } + Incidence *incidence = icalToString( iCal ); + if ( !incidence ) { + KMessageBox::sorry( + 0, + i18n( "The calendar invitation stored in this email message is broken in some way. " + "Unable to continue." ) ); + return false; + } + bool result = false; + if ( cancelPastInvites( incidence, path ) ) { + return result; + } + if ( path == "accept" ) result = handleInvitation( iCal, Attendee::Accepted, c ); if ( path == "accept_conditionally" ) @@ -603,7 +777,6 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler if ( path == "delegate" ) result = handleInvitation( iCal, Attendee::Delegated, c ); if ( path == "forward" ) { - Incidence* incidence = icalToString( iCal ); AttendeeSelector dlg; if ( dlg.exec() == TQDialog::Rejected ) return true; @@ -614,7 +787,7 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler Scheduler::Request, fwdTo, Forward ); } if ( path == "check_calendar" ) { - Incidence* incidence = icalToString( iCal ); + incidence = icalToString( iCal ); showCalendar( incidence->dtStart().date() ); } if ( path == "reply" || path == "cancel" || path == "accept_counter" ) { @@ -626,16 +799,98 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler result = true; } } - if ( result ) - c.closeIfSecondaryWindow(); + if ( path == "record" ) { + incidence = icalToString( iCal ); + + int response = + KMessageBox::questionYesNoCancel( + 0, + i18n( "The organizer is not expecting a reply to this invitation " + "but you can send them an email message if you desire.\n\n" + "Would you like to send the organizer a message regarding this invitation?\n" + "Press the [Cancel] button to cancel the recording operation." ), + i18n( "Send Email to Organizer" ), + KGuiItem( i18n( "Do Not Send" ) ), + KGuiItem( i18n( "Send EMail" ) ) ); + + TQString summary; + switch( response ) { + case KMessageBox::Cancel: + break; + case KMessageBox::No: // means "send email" + summary = incidence->summary(); + if ( !summary.isEmpty() ) { + summary = i18n( "Re: %1" ).arg( summary ); + } + + KApplication::kApplication()->invokeMailer( incidence->organizer().email(), summary ); + //fall through + case KMessageBox::Yes: // means "do not send" + if ( saveFile( "Receiver Not Searched", iCal, TQString( "reply" ) ) ) { + if ( c.deleteInvitationAfterReply() ) { + ( new KMDeleteMsgCommand( c.getMsg()->getMsgSerNum() ) )->start(); + result = true; + } + } + showCalendar( incidence->dtStart().date() ); + break; + } + } + + if ( path == "delete" ) { + ( new KMDeleteMsgCommand( c.getMsg()->getMsgSerNum() ) )->start(); + result = true; + } + + if ( path.startsWith( "ATTACH:" ) ) { + TQString name = path; + name.remove( TQRegExp( "^ATTACH:" ) ); + result = AttachmentHandler::view( 0, name, icalToMessage( iCal ) ); + } + + if ( result ) { + // do not close the secondary window if an attachment was opened (kolab/issue4317) + if ( !path.startsWith( "ATTACH:" ) ) { + c.closeIfSecondaryWindow(); + } + } return result; } - bool handleContextMenuRequest( KMail::Interface::BodyPart *, - const TQString &, - const TQPoint & ) const + bool handleContextMenuRequest( KMail::Interface::BodyPart *part, + const TQString &path, + const TQPoint &point ) const { - return false; + TQString name = path; + if ( path.startsWith( "ATTACH:" ) ) { + name.remove( TQRegExp( "^ATTACH:" ) ); + } else { + return false; //because it isn't an attachment inviation + } + + TQString iCal; + if ( part->contentTypeParameter( "charset").isEmpty() ) { + const TQByteArray &ba = part->asBinary(); + iCal = TQString::fromUtf8( ba ); + } else { + iCal = part->asText(); + } + + KPopupMenu *menu = new KPopupMenu(); + menu->insertItem( i18n( "Open Attachment" ), 0 ); + menu->insertItem( i18n( "Save Attachment As..." ), 1 ); + + switch( menu->exec( point, 0 ) ) { + case 0: // open + AttachmentHandler::view( 0, name, icalToMessage( iCal ) ); + break; + case 1: // save as + AttachmentHandler::saveAs( 0, name, icalToMessage( iCal ) ); + break; + default: + break; + } + return true; } TQString statusBarMessage( KMail::Interface::BodyPart *, @@ -643,31 +898,38 @@ class UrlHandler : public KMail::Interface::BodyPartURLHandler { if ( !path.isEmpty() ) { if ( path == "accept" ) - return i18n("Accept incidence"); + return i18n("Accept invitation"); if ( path == "accept_conditionally" ) - return i18n( "Accept incidence conditionally" ); -// ### string freeze -// if ( path == "accept_counter" ) -// return i18n( "Accept counter proposal" ); + return i18n( "Accept invitation conditionally" ); + if ( path == "accept_counter" ) + return i18n( "Accept counter proposal" ); if ( path == "counter" ) return i18n( "Create a counter proposal..." ); if ( path == "ignore" ) return i18n( "Throw mail away" ); if ( path == "decline" ) - return i18n( "Decline incidence" ); -// ### string freeze -// if ( path == "decline_counter" ) -// return i18n( "Decline counter proposal" ); + return i18n( "Decline invitation" ); + if ( path == "decline_counter" ) + return i18n( "Decline counter proposal" ); if ( path == "check_calendar" ) return i18n("Check my calendar..." ); if ( path == "reply" ) - return i18n( "Enter incidence into my calendar" ); + return i18n( "Record response into my calendar" ); + if ( path == "record" ) + return i18n( "Record invitation into my calendar" ); + if ( path == "delete" ) + return i18n( "Move this invitation to my trash folder" ); if ( path == "delegate" ) - return i18n( "Delegate incidence" ); + return i18n( "Delegate invitation" ); if ( path == "forward" ) - return i18n( "Forward incidence" ); + return i18n( "Forward invitation" ); if ( path == "cancel" ) - return i18n( "Remove incidence from my calendar" ); + return i18n( "Remove invitation from my calendar" ); + if ( path.startsWith( "ATTACH:" ) ) { + TQString name = path; + return i18n( "Open attachment \"%1\"" ). + arg( name.remove( TQRegExp( "^ATTACH:" ) ) ); + } } return TQString::null; @@ -679,19 +941,20 @@ class Plugin : public KMail::Interface::BodyPartFormatterPlugin public: const KMail::Interface::BodyPartFormatter *bodyPartFormatter( int idx ) const { - if ( idx == 0 ) return new Formatter(); + if ( idx == 0 || idx == 1 ) return new Formatter(); else return 0; } const char *type( int idx ) const { - if ( idx == 0 ) return "text"; + if ( idx == 0 || idx == 1 ) return "text"; else return 0; } const char *subtype( int idx ) const { if ( idx == 0 ) return "calendar"; + if ( idx == 1 ) return "x-vcalendar"; else return 0; } diff --git a/plugins/kmail/bodypartformatter/text_calendar.desktop b/plugins/kmail/bodypartformatter/text_calendar.desktop index cf2443d3..1321d583 100644 --- a/plugins/kmail/bodypartformatter/text_calendar.desktop +++ b/plugins/kmail/bodypartformatter/text_calendar.desktop @@ -14,7 +14,6 @@ Name[fy]=Applikaasje octetstream Name[gl]=Aplicación Octetstream Name[hu]=Alkalmazás-adatfolyam Name[ja]=アプリケーション オクテット ストリーム -Name[ka]=რვადინებიანი პროგრამა Name[kk]=Қолданбаның бинарлы ағымы Name[km]=Octetstream កម្មវិធី Name[ms]=Aliran Oktet Aplikasi @@ -55,7 +54,6 @@ Comment[hu]=Formázómodul text/calendar adatfolyamok kezeléséhez Comment[is]=Sniðmátstól fyrir text/calendar Comment[it]=Un plugin per formattare il corpo di text/calendar Comment[ja]=text/calendar 用の Bodypart フォーマッタ プラグイン -Comment[ka]=კომპონენტური დამფორმატებელი მოდული text/calendar Comment[kk]=Text/calendar бөлімін пішімдеу модулі Comment[km]=កម្មវិធី​ជំនួយ​កម្មវិធី​ធ្វើ​ទ្រង់ទ្រាយ​ផ្នែក​តួ សម្រាប់​អត្ថបទ/ប្រតិទិន Comment[lt]=text/calendar formatavimo priedas diff --git a/plugins/kmail/bodypartformatter/text_vcard.cpp b/plugins/kmail/bodypartformatter/text_vcard.cpp index 347371d1..39a4ab28 100644 --- a/plugins/kmail/bodypartformatter/text_vcard.cpp +++ b/plugins/kmail/bodypartformatter/text_vcard.cpp @@ -37,9 +37,15 @@ #include #include #include +#include #include +#include +#include +#include -#include + +#include +#include #include "interfaces/bodypartformatter.h" #include "interfaces/bodypart.h" @@ -47,6 +53,7 @@ using KMail::Interface::BodyPart; #include "interfaces/bodyparturlhandler.h" #include "khtmlparthtmlwriter.h" #include +#include #include #include @@ -67,14 +74,18 @@ namespace { //mKIMProxy = ::KIMProxy::instance( kapp->dcopClient() ); } - Result format( BodyPart *bodyPart, KMail::HtmlWriter *writer ) const { + Result format( BodyPart *bodyPart, KMail::HtmlWriter *writer, KMail::Callback & ) const { if ( !writer ) return AsIcon; VCardConverter vcc; const TQString vCard = bodyPart->asText(); if ( vCard.isEmpty() ) return AsIcon; - Addressee::List al = vcc.parseVCards( vCard ); +#if defined(KABC_VCARD_ENCODING_FIX) + Addressee::List al = vcc.parseVCardsRaw( vCard.utf8() ); +#else + Addressee::List al = vcc.parseVCards( vCard ); +#endif if ( al.empty() ) return AsIcon; writer->queue ( @@ -111,28 +122,126 @@ namespace { class UrlHandler : public KMail::Interface::BodyPartURLHandler { public: - bool handleClick( BodyPart * bodyPart, const TQString & path, - KMail::Callback& ) const { + bool handleClick( BodyPart * bodyPart, const TQString & path, + KMail::Callback& ) const { - const TQString vCard = bodyPart->asText(); - if ( vCard.isEmpty() ) return true; - VCardConverter vcc; - Addressee::List al = vcc.parseVCards( vCard ); - int index = path.right( path.length() - path.findRev( ":" ) - 1 ).toInt(); - if ( index == -1 ) return true; - KABC::Addressee a = al[index]; - if ( a.isEmpty() ) return true; - KAddrBookExternal::addVCard( a, 0 ); - return true; - } - - bool handleContextMenuRequest( BodyPart *, const TQString &, const TQPoint & ) const { - return false; - } - - TQString statusBarMessage( BodyPart *, const TQString & ) const { - return i18n("Add this contact to the address book."); - } + const TQString vCard = bodyPart->asText(); + if ( vCard.isEmpty() ) return true; + VCardConverter vcc; +#if defined(KABC_VCARD_ENCODING_FIX) + Addressee::List al = vcc.parseVCardsRaw( vCard.utf8() ); +#else + Addressee::List al = vcc.parseVCards( vCard ); +#endif + int index = path.right( path.length() - path.findRev( ":" ) - 1 ).toInt(); + if ( index == -1 ) return true; + KABC::Addressee a = al[index]; + if ( a.isEmpty() ) return true; + KAddrBookExternal::addVCard( a, 0 ); + return true; + } + + static KABC::Addressee findAddressee( BodyPart *part, const TQString &path ) + { + const TQString vCard = part->asText(); + if ( !vCard.isEmpty() ) { + VCardConverter vcc; +#if defined(KABC_VCARD_ENCODING_FIX) + Addressee::List al = vcc.parseVCardsRaw( vCard.utf8() ); +#else + Addressee::List al = vcc.parseVCards( vCard ); +#endif + int index = path.right( path.length() - path.findRev( ":" ) - 1 ).toInt(); + if ( index >= 0 ) { + return al[index]; + } + } + return KABC::Addressee(); + } + + bool handleContextMenuRequest( KMail::Interface::BodyPart *part, + const TQString &path, + const TQPoint &point ) const + { + const TQString vCard = part->asText(); + if ( vCard.isEmpty() ) { + return true; + } + KABC::Addressee a = findAddressee( part, path ); + if ( a.isEmpty() ) { + return true; + } + + KPopupMenu *menu = new KPopupMenu(); + menu->insertItem( i18n( "View Business Card" ), 0 ); + menu->insertItem( i18n( "Save Business Card As..." ), 1 ); + + switch( menu->exec( point, 0 ) ) { + case 0: // open + openVCard( a, vCard ); + break; + case 1: // save as + saveAsVCard( a, vCard ); + break; + default: + break; + } + return true; + } + + TQString statusBarMessage( BodyPart *part, const TQString &path ) const + { + KABC::Addressee a = findAddressee( part, path ); + if ( a.realName().isEmpty() ) { + return i18n( "Add this contact to the address book." ); + } else { + return i18n( "Add \"%1\" to the address book." ).arg( a.realName() ); + } + } + + bool openVCard( const KABC::Addressee &a, const TQString &vCard ) const + { + Q_UNUSED( vCard ); + AddresseeView *view = new AddresseeView( 0 ); + view->setVScrollBarMode( TQScrollView::Auto ); + if ( a.isEmpty() ) { + view->setText( i18n( "Failed to parse the business card." ) ); + } else { + view->setAddressee( a ); + } + view->setMinimumSize( 300, 400 ); + view->show(); + return true; + } + + bool saveAsVCard( const KABC::Addressee &a, const TQString &vCard ) const + { + TQString fileName = a.givenName() + '_' + a.familyName() + ".vcf"; + + // get the saveas file name + KURL saveAsUrl = + KFileDialog::getSaveURL( fileName, + TQString::null, 0, + i18n( "Save Business Card" ) ); + if ( saveAsUrl.isEmpty() || + ( TQFileInfo( saveAsUrl.path() ).exists() && + ( KMessageBox::warningYesNo( + 0, + i18n( "%1 already exists. Do you want to overwrite it?"). + arg( saveAsUrl.path() ) ) == KMessageBox::No ) ) ) { + return false; + } + + // put the attachment in a temporary file and save it + KTempFile tmpFile; + tmpFile.setAutoDelete( true ); + + TQByteArray data = vCard.utf8(); + tmpFile.file()->writeBlock( data.data(), data.size() ); + tmpFile.close(); + + return KIO::NetAccess::upload( tmpFile.name(), saveAsUrl, 0 ); + } }; class Plugin : public KMail::Interface::BodyPartFormatterPlugin { diff --git a/plugins/kmail/bodypartformatter/text_vcard.desktop b/plugins/kmail/bodypartformatter/text_vcard.desktop index 206d8462..87cbf490 100644 --- a/plugins/kmail/bodypartformatter/text_vcard.desktop +++ b/plugins/kmail/bodypartformatter/text_vcard.desktop @@ -14,7 +14,6 @@ Name[fy]=Applikaasje octetstream Name[gl]=Aplicación Octetstream Name[hu]=Alkalmazás-adatfolyam Name[ja]=アプリケーション オクテット ストリーム -Name[ka]=რვადინებიანი პროგრამა Name[kk]=Қолданбаның бинарлы ағымы Name[km]=Octetstream កម្មវិធី Name[ms]=Aliran Oktet Aplikasi @@ -56,7 +55,6 @@ Comment[hu]=Formázómodul text/vcard adatfolyamok kezeléséhez Comment[is]=Sniðmátstól fyrir text/vcard Comment[it]=Un plugin per formattare il corpo di text/vcard Comment[ja]=text/vcard 用の Bodypart フォーマッタ プラグイン -Comment[ka]=კომპონენტური დამფორმატებელი მოდული text/vcard Comment[kk]=Text/vcard бөлімін пішімдеу модулі Comment[km]=កម្មវិធី​ជំនួយ​កម្មវិធី​ធ្វើ​ទ្រង់ទ្រាយ​ផ្នែក​តួ សម្រាប់​អត្ថបទ/vcard Comment[lt]= Teksto/vcard formatavimo priedas diff --git a/plugins/kmail/bodypartformatter/text_xdiff.cpp b/plugins/kmail/bodypartformatter/text_xdiff.cpp index 87988840..53bb0016 100644 --- a/plugins/kmail/bodypartformatter/text_xdiff.cpp +++ b/plugins/kmail/bodypartformatter/text_xdiff.cpp @@ -59,7 +59,7 @@ namespace { class Formatter : public KMail::Interface::BodyPartFormatter { public: - Result format( KMail::Interface::BodyPart *bodyPart, KMail::HtmlWriter *writer ) const { + Result format( KMail::Interface::BodyPart *bodyPart, KMail::HtmlWriter *writer, KMail::Callback & ) const { if ( !writer ) return Ok; diff --git a/plugins/kmail/bodypartformatter/text_xdiff.desktop b/plugins/kmail/bodypartformatter/text_xdiff.desktop index a2c05f9c..e0422053 100644 --- a/plugins/kmail/bodypartformatter/text_xdiff.desktop +++ b/plugins/kmail/bodypartformatter/text_xdiff.desktop @@ -14,7 +14,6 @@ Name[fy]=Applikaasje octetstream Name[gl]=Aplicación Octetstream Name[hu]=Alkalmazás-adatfolyam Name[ja]=アプリケーション オクテット ストリーム -Name[ka]=რვადინებიანი პროგრამა Name[kk]=Қолданбаның бинарлы ағымы Name[km]=Octetstream កម្មវិធី Name[ms]=Aliran Oktet Aplikasi @@ -54,7 +53,6 @@ Comment[hu]=Formázómodul text/x-diff adatok kezeléséhez Comment[is]=Sniðmátstól fyrir text/x-diff Comment[it]=Un plugin per formattare il corpo di text/x-diff Comment[ja]=text/x-diff 用の bodypart フォーマッタプラグイン -Comment[ka]=კომპონენტური დამფორმატებელი მოდული text/x-diff Comment[kk]=Text/x-diff бөлімін пішімдеу модулі Comment[km]=កម្មវិធី​ជំនួយ​កម្មវិធី​ធ្វើ​ទ្រង់ទ្រាយ​ផ្នែក​តួ សម្រាប់​អត្ថបទ/x-diff Comment[lt]=text/x-diff formatavimo priedas diff --git a/release_howto b/release_howto new file mode 100644 index 00000000..c4bbcb45 --- /dev/null +++ b/release_howto @@ -0,0 +1,90 @@ +====== KDE PIM Enterprise release howto ===== + +David Faure , 02-Aug-2005, last update 12-Mar-2007. + +# First ensure that translations are uptodate, running "translate". +# Packing kdepim doesn't pack the translations themselves, but still, +# at release time we have to ensure they are ok. + +# + +cd kde-common/release +mkdir clean cache dirty sources sources-old + +echo kdepim > modules + +# Now save the patch below to a file, and apply it. + +Index: common +=================================================================== +--- common (revision 615014) ++++ common (working copy) +@@ -23,6 +23,9 @@ case $package in + koffice-l10n) + version=1.3.98 + ;; ++ kdepim) ++ version=3.5.6.enterprise.0.20070227.637543 ++ ;; + *) + version=3.5.5 + ;; + +Index: versions +=================================================================== +--- versions (revision 615014) ++++ versions (working copy) +@@ -20,6 +20,12 @@ + DESTURL=tags/koffice/1.4.0/$1 + subname=$1 + ;; ++ kdepim) ++ HEADURL=branches/kdepim/enterprise/$1 ++ DESTURL=tags/kdepim/enterprise.0.20070227.637543/$1 ++ subname=$1 ++ export UNSERMAKE= ++ ;; + *) + HEADURL=branches/KDE/3.5/$1 + DESTURL=tags/KDE/3.5.5/$1 + + +### --- end of patch --- + + +# Update the version number in "common" and the tagname in "versions" +# The version number is: 0.YYYYMMDD.svnrevision + + +# +# Also update the version number in those files: +# ./kmail/kmversion.h:#define KMAIL_VERSION "1.9.6 (enterprise 0.20070227.637543)" +# ./kontact/src/main.cpp:static const char version[] = "1.2.4 (enterprise 0.20070227.637543)"; +# ./korganizer/version.h:static const char korgVersion[] = "3.5.6 (enterprise 0.20070227.637543)"; + + +./tag_all +# tag_all checks out the enterprise branch of kdepim into a temporary tagging directory +# and then allows to commit - to create the tag. +# The script needs the variables SVNUSER and SVNPROTOCOL to be set. + + +# If you used the "cache" feature with an older release, remove it first +rm -rf cache/kdepim + +# Ready? OK, let's pack it: +./pack kdepim + +# On failure it's always possible to restart from where it stopped, e.g. with +# cd dirty ; ../dist kdepim +# if the "dist" step failed +# (and then ../taritup kdepim for the last step) +# But if all goes well, "pack" will have done it all. + +# You can find the resulting tar.bz2 in sources/, scp it somewhere. + +# To make sure that snapshots display a useful version number, +# change them after release to reflect development status in the branch. +# ./kmail/kmversion.h:#define KMAIL_VERSION "1.9.6 (enterprise branch after 0.20070227.637543)" +# ./kontact/src/main.cpp:static const char version[] = "1.2.4 (enterprise branch after 0.20070227.637543)"; +# ./korganizer/version.h:static const char korgVersion[] = "3.5.6 (enterprise branch after 0.20070227.637543)"; diff --git a/translate b/translate new file mode 100644 index 00000000..0a19b1bf --- /dev/null +++ b/translate @@ -0,0 +1,83 @@ +#!/bin/sh + +# Update this path if necessary +KDEL10N=$PWD/../kde-l10n + +export LC_ALL=C +export CDPATH= + +# Fix up the script +if ! grep -q Language-Team admin/cvs.sh; then + patch admin/cvs.sh < cvs.sh.diff +fi + +test -L po || ( rm -f po ; ln -s ../kde-l10n/templates/messages/kdepim po ) + +# I tried setting podir to get the output directly into kde-l10n but then +# we run old code in cvs.sh; scripty doesn't use that anymore, but the scripts in l10n. +# However those are harder to reduce to kdepim only, so let's keep the old way +# of doing it: with a po subdir, like a 3rd-party app and not like a real kde module. +#export podir="$KDEL10N/templates/messages/kdepim" +if ! test -d "po"; then + echo "po doesn't exist" + exit 1 +fi + +if test -z "$XGETTEXT"; then + if test -f /usr/bin/kde-xgettext; then + xgettext=kde-xgettext + export XGETTEXT=kde-xgettext + elif test -f /opt/kde3/bin/kde-xgettext; then + xgettext=/opt/kde3/bin/kde-xgettext + export XGETTEXT=$xgettext + else + xgettext=xgettext + fi +else + xgettext="$XGETTEXT" +fi +gettext_version=`$xgettext --version | grep 0.10.35` +if test -z "$gettext_version"; then + echo "No xgettext installed or wrong xgettext version: "`$xgettext --version | head -n1` + exit 1 +fi + +kdepim="$PWD" + +# I assume kdepim is uptodate, but kde-l10n is probably not +cd "$KDEL10N" || exit 1 +svn update + +cd "$kdepim" +make -f admin/Makefile.common package-messages || exit 1 +# ? make -f admin/Makefile.common package-merge || exit 1 + +cd "$KDEL10N" || exit 1 + +scripts/merge_all.sh + +svn diff + +for i in `find -name kdepim`; do + cd $i || exit 1 + + for t in `svn status 2>&1 | grep '^M' | gawk '{print $2}'`; do + diff=`diff -u -I'^#' -I'^"POT-Creation-Date:' .svn/text-base/$t.svn-base $t` + if test -n "$diff"; then + echo "$t: changed" + else + #echo "$t: no change" + svn revert $t + fi + done + # New files are not added automatically; do those by hand. + + # To see what changed in a .pot file: svn di kmail.pot | grep '^[-+][^#]' + + cd "$KDEL10N" +done + +scripts/check_po_files + +echo "Now go to $KDEL10N and commit templates/ and de/" + diff --git a/wizards/Makefile.am b/wizards/Makefile.am index 0a211294..61332233 100644 --- a/wizards/Makefile.am +++ b/wizards/Makefile.am @@ -4,22 +4,21 @@ INCLUDES = -I$(top_srcdir)/libkpimidentities -I$(top_srcdir)/libkcal \ -I$(top_srcdir)/kresources/kolab/shared -I$(top_srcdir) \ -I$(top_srcdir)/knotes \ -I$(top_srcdir)/certmanager/lib \ - -I$(top_builddir)/kresources/groupwise \ -I$(top_builddir)/kresources/lib \ -I$(top_srcdir)/kresources/lib \ $(all_includes) bin_PROGRAMS = groupwarewizard egroupwarewizard sloxwizard kolabwizard \ - groupwisewizard exchangewizard scalixwizard + exchangewizard kde_module_LTLIBRARIES = libegroupwarewizard.la libsloxwizard.la \ - libkolabwizard.la libgroupwisewizard.la \ - libexchangewizard.la libscalixwizard.la + libkolabwizard.la \ + libexchangewizard.la groupwarewizard_LDFLAGS = $(all_libraries) $(KDE_RPATH) groupwarewizard_LDADD = libegroupwarewizard.la libsloxwizard.la $(LIB_KDEUI) \ - libkolabwizard.la libgroupwisewizard.la \ + libkolabwizard.la \ libexchangewizard.la groupwarewizard_SOURCES = groupwarewizard.cpp main.cpp overviewpage.cpp @@ -64,22 +63,6 @@ sloxwizard_LDADD = libsloxwizard.la sloxwizard_LDFLAGS = $(all_libraries) $(KDE_RPATH) sloxwizard_SOURCES = sloxmain.cpp -# GroupWise -libgroupwisewizard_la_LDFLAGS = -avoid-version -no-undefined $(all_libraries) -libgroupwisewizard_la_SOURCES = groupwisewizard.cpp groupwiseconfig.kcfgc \ - kmailchanges.cpp -libgroupwisewizard_la_LIBADD = $(top_builddir)/kresources/groupwise/libkcal_groupwise.la \ - $(top_builddir)/kresources/groupwise/libkabc_groupwise.la \ - $(top_builddir)/libkdepim/libkdepim.la \ - $(top_builddir)/libkpimidentities/libkpimidentities.la -libgroupwisewizard_la_COMPILE_FIRST = $(top_builddir)/kresources/groupwise/kabc_groupwiseprefs.h \ - $(top_builddir)/kresources/groupwise/kcal_groupwiseprefsbase.h - - -groupwisewizard_LDADD = libgroupwisewizard.la -groupwisewizard_LDFLAGS = $(all_libraries) $(KDE_RPATH) -groupwisewizard_SOURCES = groupwisemain.cpp - # Exchange libexchangewizard_la_LDFLAGS = -avoid-version -no-undefined $(all_libraries) libexchangewizard_la_SOURCES = exchangewizard.cpp @@ -91,25 +74,10 @@ exchangewizard_LDADD = libexchangewizard.la exchangewizard_LDFLAGS = $(all_libraries) $(KDE_RPATH) exchangewizard_SOURCES = exchangemain.cpp -# Scalix -libscalixwizard_la_LDFLAGS = -avoid-version -no-undefined $(all_libraries) -libscalixwizard_la_LIBADD = $(top_builddir)/kresources/scalix/kcal/libkcalscalix.la \ - $(top_builddir)/kresources/scalix/kabc/libkabcscalix.la \ - $(top_builddir)/libkcal/libkcal.la \ - $(top_builddir)/libkdepim/libkdepim.la \ - $(top_builddir)/libkpimidentities/libkpimidentities.la - -libscalixwizard_la_SOURCES = scalixwizard.cpp kmailchanges.cpp scalixconfig.kcfgc \ - scalixkmailchanges.cpp - -scalixwizard_LDADD = libscalixwizard.la $(LIB_KDEUI) -scalixwizard_LDFLAGS = $(all_libraries) $(KDE_RPATH) -scalixwizard_SOURCES = scalixmain.cpp - noinst_HEADERS = egroupwarewizard.h kmailchanges.h kolabwizard.h sloxwizard.h \ - groupwisewizard.h exchangewizard.h + exchangewizard.h -kde_kcfg_DATA = egroupware.kcfg slox.kcfg kolab.kcfg groupwise.kcfg scalix.kcfg +kde_kcfg_DATA = egroupware.kcfg slox.kcfg kolab.kcfg messages: rc.cpp $(XGETTEXT) *.cpp -o $(podir)/kdepimwizards.pot diff --git a/wizards/egroupwaremain.cpp b/wizards/egroupwaremain.cpp index 86777c61..153c2f83 100644 --- a/wizards/egroupwaremain.cpp +++ b/wizards/egroupwaremain.cpp @@ -50,8 +50,8 @@ int main(int argc,char **argv) bool verbose = false; if ( args->isSet( "verbose" ) ) verbose = true; - + args->clear(); EGroupwareWizard wizard; - wizard.exec(); + return wizard.exec(); } diff --git a/wizards/exchangemain.cpp b/wizards/exchangemain.cpp index 3b68951e..60d58490 100644 --- a/wizards/exchangemain.cpp +++ b/wizards/exchangemain.cpp @@ -50,8 +50,8 @@ int main(int argc,char **argv) bool verbose = false; if ( args->isSet( "verbose" ) ) verbose = true; - + args->clear(); ExchangeWizard wizard; - wizard.exec(); + return wizard.exec(); } diff --git a/wizards/groupwarewizard.desktop b/wizards/groupwarewizard.desktop index ae84eb98..45fd1956 100644 --- a/wizards/groupwarewizard.desktop +++ b/wizards/groupwarewizard.desktop @@ -22,7 +22,6 @@ Name[hu]=KDE csoportmunka-varázsló Name[is]=KDE hópvinnukerfisálfur Name[it]=Assistente configurazione di KDE Groupware Name[ja]=KDE グループウェアウィザード -Name[ka]=სერვერი Groupware Name[kk]=KDE бірікен жұмыс шебері Name[km]=អ្នក​ជំនួយការ​កម្មវិធី​ពហុ​អ្នក​ប្រើ​របស់ KDE Name[ko]=KDE 그룹웨어 마법사 diff --git a/wizards/kmailchanges.cpp b/wizards/kmailchanges.cpp index ca2969a5..79e06f58 100644 --- a/wizards/kmailchanges.cpp +++ b/wizards/kmailchanges.cpp @@ -243,10 +243,11 @@ void CreateDisconnectedImapAccount::apply() c.setGroup( TQString("Folder-%1").arg( uid ) ); c.writeEntry( "isOpen", true ); - if ( mEnableSavePassword ) { - c.writeEntry( "pass", KStringHandler::obscure( mPassword ) ); - c.writeEntry( "store-passwd", true ); - } + c.setGroup( "AccountWizard" ); + c.writeEntry( "ShowOnStartup" , false ); + + c.setGroup( "Composer" ); + c.writeEntry( "default-transport", mAccountName ); c.setGroup( TQString("Transport %1").arg( transportId ) ); c.writeEntry( "name", mAccountName ); @@ -295,7 +296,7 @@ void CreateDisconnectedImapAccount::apply() KPIM::Identity& identity = identityManager.newFromScratch( accountName ); identity.setFullName( mRealName ); - identity.setEmailAddr( mEmail ); + identity.setPrimaryEmailAddress( mEmail ); identityManager.commit(); } @@ -360,6 +361,11 @@ void CreateOnlineImapAccount::apply() c.setGroup( TQString("Folder-%1").arg( uid ) ); c.writeEntry( "isOpen", true ); + + + c.setGroup( "AccountWizard" ); + c.writeEntry( "ShowOnStartup" , false ); + } bool CreateImapAccount::writeToWallet(const TQString & type, int id) diff --git a/wizards/kolabmain.cpp b/wizards/kolabmain.cpp index 865dd9bd..1aa817ad 100644 --- a/wizards/kolabmain.cpp +++ b/wizards/kolabmain.cpp @@ -50,8 +50,8 @@ int main(int argc,char **argv) bool verbose = false; if ( args->isSet( "verbose" ) ) verbose = true; - + args->clear(); KolabWizard wizard; - wizard.exec(); + return wizard.exec(); } diff --git a/wizards/overviewpage.cpp b/wizards/overviewpage.cpp index 657a8a0d..2ee90096 100644 --- a/wizards/overviewpage.cpp +++ b/wizards/overviewpage.cpp @@ -35,7 +35,6 @@ #include "egroupwarewizard.h" #include "kolabwizard.h" #include "sloxwizard.h" -#include "groupwisewizard.h" #include "exchangewizard.h" #include "overviewpage.h" @@ -75,26 +74,22 @@ OverViewPage::OverViewPage( TQWidget *parent, const char *name ) layout->addMultiCellWidget( button, 5, 5, 0, 3 ); connect( button, TQT_SIGNAL( clicked() ), TQT_SLOT( showWizardSlox() ) ); - button = new TQPushButton( i18n("Novell GroupWise"), this ); - layout->addMultiCellWidget( button, 6, 6, 0, 3 ); - connect( button, TQT_SIGNAL( clicked() ), TQT_SLOT( showWizardGroupwise() ) ); - button = new TQPushButton( i18n("Microsoft Exchange"), this ); button->hide(); // not quite ready yet - layout->addMultiCellWidget( button, 7, 7, 0, 3 ); + layout->addMultiCellWidget( button, 6, 6, 0, 3 ); connect( button, TQT_SIGNAL( clicked() ), TQT_SLOT( showWizardExchange() ) ); TQFrame *frame = new TQFrame( this ); frame->setFrameStyle( TQFrame::HLine | TQFrame::Sunken ); - layout->addMultiCellWidget( frame, 8, 8, 0, 3 ); + layout->addMultiCellWidget( frame, 7, 7, 0, 3 ); TQPushButton *cancelButton = new KPushButton( KStdGuiItem::close(), this ); - layout->addWidget( cancelButton, 9, 3 ); + layout->addWidget( cancelButton, 8, 3 ); connect( cancelButton, TQT_SIGNAL( clicked() ), this, TQT_SIGNAL( cancel() ) ); - layout->setRowStretch( 8, 1 ); + layout->setRowStretch( 7, 1 ); KAcceleratorManager::manage( this ); } @@ -121,12 +116,6 @@ void OverViewPage::showWizardSlox() wizard.exec(); } -void OverViewPage::showWizardGroupwise() -{ - GroupwiseWizard wizard; - wizard.exec(); -} - void OverViewPage::showWizardExchange() { ExchangeWizard wizard; diff --git a/wizards/overviewpage.h b/wizards/overviewpage.h index 1d93e42d..487b14ce 100644 --- a/wizards/overviewpage.h +++ b/wizards/overviewpage.h @@ -36,7 +36,6 @@ class OverViewPage : public QWidget void showWizardEGroupware(); void showWizardKolab(); void showWizardSlox(); - void showWizardGroupwise(); void showWizardExchange(); signals: