See ChangeLog

Monotone-Parent: ede90c4ec21ca642e49b4287679877bd02717ed6
Monotone-Revision: ae2c5342363a3fa87101fa6840e1c1e1f7a819c0

Monotone-Author: flachapelle@inverse.ca
Monotone-Date: 2011-06-01T21:10:25
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Francis Lachapelle 2011-06-01 21:10:25 +00:00
parent 624172fe92
commit 53a01edee4
25 changed files with 521 additions and 102 deletions

View File

@ -1,9 +1,45 @@
2011-06-01 Francis Lachapelle <flachapelle@inverse.ca> 2011-06-01 Francis Lachapelle <flachapelle@inverse.ca>
* SoObjects/SOGo/SOGoUserDefaults.m (-setMailSortByThreads)
(-mailSortByThreads): new accessors for the "SOGoMailSortByThreads"
user defaults.
* UI/PreferencesUI/UIxPreferences.m (-setSortByThreads)
(sortByThreads): idem.
* SoObjects/Mailer/SOGoMailFolder.m
(-fetchUIDsMatchingQualifier:sortOrdering:threaded:): new method
to fetch a threaded-view of the folder.
* SoObjects/SOGo/NSArray+Utilities.m (-flattenedArray): added
recurrence to flatten interleaved arrays.
* SoObjects/Mailer/SOGoMailAccount.m (-updateFilters): write * SoObjects/Mailer/SOGoMailAccount.m (-updateFilters): write
multiple 'redirect' directives when forwarding to multiple email multiple 'redirect' directives when forwarding to multiple email
addresses. addresses.
* UI/MailerUI/UIxMailListActions.m (-threadedUIDs): new method
that returns a flatten representation of messages threads.
* UI/MailerUI/UIxMailMainFrame.m (-columnsMetaData): added CSS
classnames for the thread column.
(-columnsDisplayOrder): add or remove the thread column depending
on the user's defaults.
* UI/MailerUI/UIxMailListActions.m (-getUIDsAndHeadersInFolder)
(-getSortedUIDsAction): added support for threads.
* UI/WebServerResources/ContactsUI.js (onContactContextMenu):
select row at pointer position when not already selected.
* UI/WebServerResources/MailerUI.js: added support for the
threaded view.
(onMessageContextMenu): select row at pointer position when not
already selected.
* UI/WebServerResources/SOGoMailDataSource.js: added support for
the threaded view.
2011-05-31 Francis Lachapelle <flachapelle@inverse.ca> 2011-05-31 Francis Lachapelle <flachapelle@inverse.ca>
* UI/WebServerResources/SchedulerUI.js (initCalendarSelector): use * UI/WebServerResources/SchedulerUI.js (initCalendarSelector): use

10
NEWS
View File

@ -1,3 +1,13 @@
1.3-2011MMDD (1.3.8)
---------------------
New Features
- initial support for threaded-view in the webmail interface
Enhancements
- improved list selection and contextual menu behavior in all web modules
Bug Fixes
1.3-20110503 (1.3.7) 1.3-20110503 (1.3.7)
--------------------- ---------------------
New Features New Features

View File

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2009-2010 Inverse inc. Copyright (C) 2009-2011 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org. This file is part of OpenGroupware.org.
@ -60,7 +60,8 @@
inContext: (id) context; inContext: (id) context;
- (WOResponse *) archiveAllMessagesInContext: (id) localContext; - (WOResponse *) archiveAllMessagesInContext: (id) localContext;
- (NSArray *) fetchUIDsMatchingQualifier: (id)_q sortOrdering: (id) _so; - (NSArray *) fetchUIDsMatchingQualifier: (id) _q sortOrdering: (id) _so;
- (NSArray *) fetchUIDsMatchingQualifier: (id) _q sortOrdering: (id) _so threaded: (BOOL) _threaded;
- (NSArray *) fetchUIDs: (NSArray *) _uids parts: (NSArray *) _parts; - (NSArray *) fetchUIDs: (NSArray *) _uids parts: (NSArray *) _parts;
- (WOResponse *) copyUIDs: (NSArray *) uids - (WOResponse *) copyUIDs: (NSArray *) uids

View File

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2009-2010 Inverse inc. Copyright (C) 2009-2011 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org. This file is part of OpenGroupware.org.
@ -315,8 +315,8 @@ static NSString *defaultUserID = @"anyone";
} }
} }
else else
error = [NSException exceptionWithHTTPStatus: 500 error = [NSException exceptionWithHTTPStatus: 500
reason: @"Did not find Trash folder!"]; reason: @"Did not find Trash folder!"];
} }
if (b) if (b)
@ -544,9 +544,27 @@ static NSString *defaultUserID = @"anyone";
- (NSArray *) fetchUIDsMatchingQualifier: (id) _q - (NSArray *) fetchUIDsMatchingQualifier: (id) _q
sortOrdering: (id) _so sortOrdering: (id) _so
{ {
/* seems to return an NSArray of NSNumber's */ return [self fetchUIDsMatchingQualifier: _q
return [[self imap4Connection] fetchUIDsInURL: [self imap4URL] sortOrdering: _so
qualifier: _q sortOrdering: _so]; threaded: NO];
}
- (NSArray *) fetchUIDsMatchingQualifier: (id) _q
sortOrdering: (id) _so
threaded: (BOOL) _threaded
{
if (_threaded)
{
return [[self imap4Connection] fetchThreadedUIDsInURL: [self imap4URL]
qualifier: _q
sortOrdering: _so];
}
else
{
return [[self imap4Connection] fetchUIDsInURL: [self imap4URL]
qualifier: _q
sortOrdering: _so];
}
} }
- (NSArray *) fetchUIDs: (NSArray *) _uids - (NSArray *) fetchUIDs: (NSArray *) _uids

View File

@ -1,6 +1,6 @@
/* NSArray+Utilities.m - this file is part of SOGo /* NSArray+Utilities.m - this file is part of SOGo
* *
* Copyright (C) 2006-2009 Inverse inc. * Copyright (C) 2006-2011 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -129,7 +129,10 @@
flattenedArray = [NSMutableArray array]; flattenedArray = [NSMutableArray array];
objects = [self objectEnumerator]; objects = [self objectEnumerator];
while ((currentObject = [objects nextObject])) while ((currentObject = [objects nextObject]))
[flattenedArray addObjectsFromArray: currentObject]; if ([currentObject isKindOfClass: [NSArray class]])
[flattenedArray addObjectsFromArray: [(NSArray *)currentObject flattenedArray]];
else
[flattenedArray addObject: currentObject];
return flattenedArray; return flattenedArray;
} }

View File

@ -53,7 +53,7 @@
SOGoMailSignaturePlacement = "below"; SOGoMailSignaturePlacement = "below";
SOGoMailPollingIntervals = ( 1, 2, 5, 10, 20, 30, 60 ); SOGoMailPollingIntervals = ( 1, 2, 5, 10, 20, 30, 60 );
SOGoMailComposeMessageType = "text"; SOGoMailComposeMessageType = "text";
SOGoMailListViewColumnsOrder = ( "Flagged", "Attachment", "Subject", SOGoMailListViewColumnsOrder = ( "Thread", "Flagged", "Attachment", "Subject",
"From", "Unread", "Date", "Priority", "From", "Unread", "Date", "Priority",
"Size" ); "Size" );

View File

@ -1,6 +1,6 @@
/* SOGoUserDefaults.h - this file is part of SOGo /* SOGoUserDefaults.h - this file is part of SOGo
* *
* Copyright (C) 2009 Inverse inc. * Copyright (C) 2011 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -85,6 +85,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setMailShowSubscribedFoldersOnly: (BOOL) newValue; - (void) setMailShowSubscribedFoldersOnly: (BOOL) newValue;
- (BOOL) mailShowSubscribedFoldersOnly; - (BOOL) mailShowSubscribedFoldersOnly;
- (void) setMailSortByThreads: (BOOL) newValue;
- (BOOL) mailSortByThreads;
- (void) setDraftsFolderName: (NSString *) newValue; - (void) setDraftsFolderName: (NSString *) newValue;
- (NSString *) draftsFolderName; - (NSString *) draftsFolderName;

View File

@ -372,6 +372,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self boolForKey: @"SOGoMailShowSubscribedFoldersOnly"]; return [self boolForKey: @"SOGoMailShowSubscribedFoldersOnly"];
} }
- (void) setMailSortByThreads: (BOOL) newValue
{
[self setBool: newValue forKey: @"SOGoMailSortByThreads"];
}
- (BOOL) mailSortByThreads
{
return [self boolForKey: @"SOGoMailSortByThreads"];
}
- (void) setDraftsFolderName: (NSString *) newValue - (void) setDraftsFolderName: (NSString *) newValue
{ {
[self setObject: newValue forKey: @"SOGoDraftsFolderName"]; [self setObject: newValue forKey: @"SOGoDraftsFolderName"];

View File

@ -35,6 +35,7 @@
id message; id message;
SOGoDateFormatter *dateFormatter; SOGoDateFormatter *dateFormatter;
NSTimeZone *userTimeZone; NSTimeZone *userTimeZone;
BOOL sortByThread;
int folderType; int folderType;
int specificMessageNumber; int specificMessageNumber;
} }

View File

@ -78,6 +78,7 @@
user = [[self context] activeUser]; user = [[self context] activeUser];
ASSIGN (dateFormatter, [user dateFormatterInContext: context]); ASSIGN (dateFormatter, [user dateFormatterInContext: context]);
ASSIGN (userTimeZone, [[user userDefaults] timeZone]); ASSIGN (userTimeZone, [[user userDefaults] timeZone]);
sortByThread = [[user userDefaults] mailSortByThreads];
folderType = 0; folderType = 0;
specificMessageNumber = 0; specificMessageNumber = 0;
} }
@ -458,13 +459,115 @@
sortedUIDs sortedUIDs
= [mailFolder fetchUIDsMatchingQualifier: fetchQualifier = [mailFolder fetchUIDsMatchingQualifier: fetchQualifier
sortOrdering: [self imap4SortOrdering]]; sortOrdering: [self imap4SortOrdering]
threaded: sortByThread];
[sortedUIDs retain]; [sortedUIDs retain];
} }
return sortedUIDs; return sortedUIDs;
} }
/**
* Returns a flatten representation of the messages threads as triples of
* metadata, including the message UID, thread level and root position.
* @param _sortedUIDs the interleaved arrays representation of the messages UIDs
* @return an flatten array representation of the messages UIDs
*/
- (NSArray *) threadedUIDs: (NSArray *) _sortedUIDs
{
NSMutableArray *threads;
NSMutableArray *currentThreads;
NSEnumerator *rootThreads;
id thread;
int count;
int i;
BOOL first;
BOOL expected;
int previousLevel;
count = 0;
i = 0;
previousLevel = 0;
expected = YES;
threads = [NSMutableArray arrayWithObject: [NSArray arrayWithObjects: @"uid", @"level", @"first", nil]];
rootThreads = [_sortedUIDs objectEnumerator];
thread = [rootThreads nextObject];
// Make sure rootThreads starts with an NSArray
if (![thread respondsToSelector: @selector(objectEnumerator)])
return nil;
first = [thread count] > 1;
thread = [thread objectEnumerator];
currentThreads = [NSMutableArray array];
while (thread)
{
unsigned int ecount = 0;
id t;
if ([thread isKindOfClass: [NSEnumerator class]])
{
t = [thread nextObject];
}
else
t = thread; // never happen?
while (t && ![t isKindOfClass: [NSArray class]])
{
BOOL currentFirst;
int currentLevel;
NSArray *currentThread;
currentFirst = (first && ecount == 0) || (i == 0 && count > 0) || (count > 0 && previousLevel < 0);
currentLevel = (first && ecount == 0)? 0 : (count > 0? count : -1);
currentThread = [NSArray arrayWithObjects: t,
[NSNumber numberWithInt: currentLevel],
[NSNumber numberWithInt: currentFirst], nil];
[threads addObject: currentThread];
i++;
count++;
ecount++;
expected = NO;
previousLevel = currentLevel;
t = [thread nextObject];
}
if (t)
{
// If t is defined, it has to be an NSArray
if (expected)
{
count++;
expected = NO;
}
thread = [thread allObjects];
if ([thread count] > 0)
[currentThreads addObject: [thread objectEnumerator]];
thread = [t objectEnumerator];
}
else if ([currentThreads count] > 0)
{
thread = [currentThreads objectAtIndex: 0];
[currentThreads removeObjectAtIndex: 0];
count -= ecount;
}
else
{
thread = [[rootThreads nextObject] objectEnumerator]; // assume all objects of rootThreads are NSArrays
count = 0;
expected = YES;
}
// Prepare next iteration
thread = [thread allObjects];
first = !first && (thread != nil) && [thread count] > 1;
thread = [thread objectEnumerator];
}
return threads;
}
- (int) indexOfMessageUID: (int) messageNbr - (int) indexOfMessageUID: (int) messageNbr
{ {
NSArray *messageNbrs; NSArray *messageNbrs;
@ -521,11 +624,9 @@
} }
*/ */
/* actions */
- (NSDictionary *) getUIDsAndHeadersInFolder: (SOGoMailFolder *) mailFolder - (NSDictionary *) getUIDsAndHeadersInFolder: (SOGoMailFolder *) mailFolder
{ {
NSArray *uids, *headers; NSArray *uids, *threadedUids, *headers;
NSDictionary *data; NSDictionary *data;
NSRange r; NSRange r;
int count; int count;
@ -536,18 +637,31 @@
count = [uids count]; count = [uids count];
if (count > headersPrefetchMaxSize) count = headersPrefetchMaxSize; if (count > headersPrefetchMaxSize) count = headersPrefetchMaxSize;
r = NSMakeRange(0, count); r = NSMakeRange(0, count);
headers = [self getHeadersForUIDs: [uids subarrayWithRange: r] headers = [self getHeadersForUIDs: [[uids flattenedArray] subarrayWithRange: r]
inFolder: mailFolder]; inFolder: mailFolder];
if (sortByThread)
{
threadedUids = [self threadedUIDs: uids];
if (threadedUids != nil)
uids = threadedUids;
else
sortByThread = NO;
}
data = [NSDictionary dictionaryWithObjectsAndKeys: uids, @"uids", data = [NSDictionary dictionaryWithObjectsAndKeys: uids, @"uids",
headers, @"headers", nil]; headers, @"headers",
[NSNumber numberWithBool: sortByThread], @"threaded", nil];
return data; return data;
} }
/* Module actions */
- (id <WOActionResults>) getSortedUIDsAction - (id <WOActionResults>) getSortedUIDsAction
{ {
id data; NSDictionary *data;
NSArray *uids, *threadedUids;
NSString *noHeaders; NSString *noHeaders;
SOGoMailFolder *folder; SOGoMailFolder *folder;
WORequest *request; WORequest *request;
@ -563,10 +677,22 @@
[folder expungeLastMarkedFolder]; [folder expungeLastMarkedFolder];
noHeaders = [request formValueForKey: @"no_headers"]; noHeaders = [request formValueForKey: @"no_headers"];
if ([noHeaders length]) if ([noHeaders length])
data = [self getSortedUIDsInFolder: folder]; {
uids = [self getSortedUIDsInFolder: folder];
if (sortByThread)
{
threadedUids = [self threadedUIDs: uids];
if (threadedUids != nil)
uids = threadedUids;
else
sortByThread = NO;
}
data = [NSDictionary dictionaryWithObjectsAndKeys: uids, @"uids",
[NSNumber numberWithBool: sortByThread], @"threaded", nil];
}
else else
data = [self getUIDsAndHeadersInFolder: folder]; data = [self getUIDsAndHeadersInFolder: folder];
[response appendContentString: [data jsonRepresentation]]; [response appendContentString: [data jsonRepresentation]];
return response; return response;

View File

@ -471,13 +471,20 @@
columnsMetaData = [NSMutableDictionary dictionaryWithCapacity: 8]; columnsMetaData = [NSMutableDictionary dictionaryWithCapacity: 8];
tmpKeys = [NSArray arrayWithObjects: @"headerClass", @"headerId", @"value", tmpKeys = [NSArray arrayWithObjects: @"headerClass", @"headerId", @"value",
nil]; nil];
tmpColumns tmpColumns
= [NSArray arrayWithObjects: @"messageSubjectColumn tbtv_headercell sortableTableHeader resizable", = [NSArray arrayWithObjects: @"messageSubjectColumn tbtv_headercell sortableTableHeader resizable",
@"subjectHeader", @"Subject", nil]; @"subjectHeader", @"Subject", nil];
[columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns
forKeys: tmpKeys] forKeys: tmpKeys]
forKey: @"Subject"]; forKey: @"Subject"];
tmpColumns
= [NSArray arrayWithObjects: @"messageThreadColumn tbtv_headercell",
@"invisibleHeader", @"Thread", nil];
[columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns
forKeys: tmpKeys]
forKey: @"Thread"];
tmpColumns tmpColumns
= [NSArray arrayWithObjects: @"messageFlagColumn tbtv_headercell", = [NSArray arrayWithObjects: @"messageFlagColumn tbtv_headercell",
@ -566,6 +573,16 @@
finalOrder = [columnsOrder mutableCopy]; finalOrder = [columnsOrder mutableCopy];
[finalOrder autorelease]; [finalOrder autorelease];
if (![ud mailSortByThreads])
[finalOrder removeObject: @"Thread"];
else
{
i = [finalOrder indexOfObject: @"Thread"];
if (i == NSNotFound)
[finalOrder insertObject: @"Thread" atIndex: 0];
}
if ([self showToAddress]) if ([self showToAddress])
{ {
i = [finalOrder indexOfObject: @"From"]; i = [finalOrder indexOfObject: @"From"];

View File

@ -547,6 +547,16 @@
return [userDefaults mailShowSubscribedFoldersOnly]; return [userDefaults mailShowSubscribedFoldersOnly];
} }
- (void) setSortByThreads: (BOOL) sortByThreads
{
[userDefaults setMailSortByThreads: sortByThreads];
}
- (BOOL) sortByThreads
{
return [userDefaults mailSortByThreads];
}
- (NSArray *) messageCheckList - (NSArray *) messageCheckList
{ {
NSArray *intervalsList; NSArray *intervalsList;

View File

@ -7,7 +7,7 @@
xmlns:label="OGo:label" xmlns:label="OGo:label"
className="UIxPageFrame" className="UIxPageFrame"
title="title" title="title"
const:userDefaultsKeys="SOGoMailMessageCheck,SOGoMailListViewColumnsOrder" const:userDefaultsKeys="SOGoMailMessageCheck,SOGoMailSortByThreads,SOGoMailListViewColumnsOrder"
const:userSettingsKeys="Mail" const:userSettingsKeys="Mail"
const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js,SOGoResizableTable.js,SOGoMailDataSource.js,SOGoDataTable.js"> const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js,SOGoResizableTable.js,SOGoMailDataSource.js,SOGoDataTable.js">
<script type="text/javascript"> <script type="text/javascript">
@ -222,6 +222,9 @@
<tr class="tableview" <tr class="tableview"
><var:foreach list="columnsDisplayOrder" item="currentColumn"> ><var:foreach list="columnsDisplayOrder" item="currentColumn">
<th var:class="currentColumn.headerClass" var:id="currentColumn.headerId"> <th var:class="currentColumn.headerClass" var:id="currentColumn.headerId">
<var:if condition="currentColumn.value" const:value="Thread">
<entity name="nbsp"/>
</var:if>
<var:if condition="currentColumn.value" const:value="Flagged"> <var:if condition="currentColumn.value" const:value="Flagged">
<entity name="nbsp"/> <entity name="nbsp"/>
</var:if> </var:if>
@ -232,10 +235,12 @@
<var:if condition="currentColumn.value" const:value="Unread"> <var:if condition="currentColumn.value" const:value="Unread">
<img rsrc:src="title_read_14x14.png" label:title="$currentColumn.value" /> <img rsrc:src="title_read_14x14.png" label:title="$currentColumn.value" />
</var:if> </var:if>
<var:if condition="currentColumn.value" const:value="Flagged" const:negate="YES"> <var:if condition="currentColumn.value" const:value="Thread" const:negate="YES">
<var:if condition="currentColumn.value" const:value="Attachment" const:negate="YES"> <var:if condition="currentColumn.value" const:value="Flagged" const:negate="YES">
<var:if condition="currentColumn.value" const:value="Unread" const:negate="YES"> <var:if condition="currentColumn.value" const:value="Attachment" const:negate="YES">
<var:string var:value="columnTitle" /> <var:if condition="currentColumn.value" const:value="Unread" const:negate="YES">
<var:string var:value="columnTitle" />
</var:if>
</var:if> </var:if>
</var:if> </var:if>
</var:if> </var:if>
@ -255,6 +260,10 @@
<tbody> <tbody>
<tr const:style="display: none;" <tr const:style="display: none;"
><var:foreach list="columnsDisplayOrder" item="currentColumn" ><var:foreach list="columnsDisplayOrder" item="currentColumn"
><var:if condition="currentColumn.value" const:value="Thread"
><td class="messageThreadColumn"
><!-- thread --></td
></var:if
><var:if condition="currentColumn.value" const:value="Flagged" ><var:if condition="currentColumn.value" const:value="Flagged"
><td class="messageFlagColumn" ><td class="messageFlagColumn"
><!-- flagged --></td ><!-- flagged --></td

View File

@ -176,6 +176,11 @@
const:id="subscribedFoldersOnly" const:id="subscribedFoldersOnly"
var:checked="showSubscribedFoldersOnly" /> var:checked="showSubscribedFoldersOnly" />
<var:string label:value="Show subscribed mailboxes only"/></label><br/> <var:string label:value="Show subscribed mailboxes only"/></label><br/>
<label><input type="checkbox"
const:name="sortByThreads"
const:id="sortByThreads"
var:checked="sortByThreads" />
<var:string label:value="Sort messages by threads"/></label><br/>
<label><var:string label:value="Check for new mail:"/> <label><var:string label:value="Check for new mail:"/>
<var:popup list="messageCheckList" item="item" <var:popup list="messageCheckList" item="item"
const:id="messageCheck" const:id="messageCheck"

View File

@ -40,7 +40,9 @@ DIV#contactsListContent
height: 15.5em; height: 15.5em;
border-left: 1px solid #9B9B9B; border-left: 1px solid #9B9B9B;
overflow: auto; overflow: auto;
overflow-x: hidden; } overflow-x: hidden;
-moz-user-select: none;
-khtml-user-select: none; }
.aptview_text .aptview_text
{ {

View File

@ -215,11 +215,20 @@ function contactsListCallback(http) {
} }
function onContactContextMenu(event) { function onContactContextMenu(event) {
var target = Event.element(event);
var contact = target.up('TR');
var contactsList = $("contactsList"); var contactsList = $("contactsList");
var contacts = contactsList.getSelectedRows();
if (contacts.indexOf(contact) < 0) {
onRowClick(event, target);
contacts = contactsList.getSelectedRows();
}
var menu = $("contactMenu"); var menu = $("contactMenu");
menu.observe("hideMenu", onContactContextMenuHide); menu.observe("hideMenu", onContactContextMenuHide);
if (contactsList) if (contactsList)
popupMenu(event, "contactMenu", contactsList.getSelectedRows()); popupMenu(event, "contactMenu", contacts);
} }
function onContactContextMenuHide(event) { function onContactContextMenuHide(event) {
@ -1285,7 +1294,7 @@ function initContacts(event) {
// Initialize event delegation on contacts table // Initialize event delegation on contacts table
table.multiselect = true; table.multiselect = true;
var tbody = $(table.tBodies[0]); var tbody = $(table.tBodies[0]);
tbody.on("mousedown", onContactSelectionChange); tbody.on("click", onContactSelectionChange);
tbody.on("dblclick", onContactRowDblClick); tbody.on("dblclick", onContactRowDblClick);
tbody.on("selectstart", listRowMouseDownHandler); tbody.on("selectstart", listRowMouseDownHandler);
tbody.on("contextmenu", onContactContextMenu); tbody.on("contextmenu", onContactContextMenu);
@ -1470,12 +1479,19 @@ function startDragging (itm, e) {
var handle = $("dragDropVisual"); var handle = $("dragDropVisual");
var contacts = $('contactsList').getSelectedRowsId(); var contacts = $('contactsList').getSelectedRowsId();
var count = contacts.length; var count = contacts.length;
var row = target.up('TR');
handle.show(); if (count == 0 || contacts.indexOf(row.id) < 0) {
handle.update (count); onRowClick(e, target);
if (e.shiftKey || currentFolderIsRemote ()) { contacts = $("contactsList").getSelectedRowsId();
handle.addClassName ("copy"); count = contacts.length;
} }
handle.update (count);
if (e.shiftKey || currentFolderIsRemote()) {
handle.addClassName("copy");
}
handle.show();
} }
function whileDragging (itm, e) { function whileDragging (itm, e) {

View File

@ -256,6 +256,7 @@ TR#messageCountHeader TH
TABLE.messageList TD TABLE.messageList TD
{ border-right: 1px solid transparent; } { border-right: 1px solid transparent; }
TABLE.messageList .messageThreadColumn,
TABLE.messageList .messageFlagColumn, TABLE.messageList .messageFlagColumn,
TABLE.messageList .messageAttachmentColumn, TABLE.messageList .messageAttachmentColumn,
TABLE.messageList .messageUnreadColumn TABLE.messageList .messageUnreadColumn
@ -270,6 +271,45 @@ TABLE.messageList .messageSubjectColumn
TABLE.messageList .messageSubjectColumn SPAN TABLE.messageList .messageSubjectColumn SPAN
{ padding-left: 20px; } { padding-left: 20px; }
TABLE.messageList TR.openedThread TD,
TABLE.messageList TR.closedThread TD,
TABLE.messageList TR.thread .messageThreadColumn
{ background-color: #DDD; }
TABLE.messageList TR.thread TD
{ background-color: #EEE; }
TABLE.messageList TR.thread1 .messageSubjectColumn
{ background-position: 20px 0px !important;
padding-left: 20px; }
TABLE.messageList TR.thread2 .messageSubjectColumn
{ background-position: 40px 0px !important;
padding-left: 40px; }
TABLE.messageList TR.thread3 .messageSubjectColumn
{ background-position: 60px 0px !important;
padding-left: 60px; }
TABLE.messageList TR.thread4 .messageSubjectColumn
{ background-position: 80px 0px !important;
padding-left: 80px; }
TABLE.messageList TR.thread5 .messageSubjectColumn
{ background-position: 100px 0px !important;
padding-left: 100px; }
TABLE.messageList TR.thread6 .messageSubjectColumn
{ background-position: 120px 0px !important;
padding-left: 120px; }
TABLE.messageList TR.thread7 .messageSubjectColumn
{ background-position: 140px 0px !important;
padding-left: 140px; }
TABLE.messageList TR.thread8 .messageSubjectColumn
{ background-position: 160px 0px !important;
padding-left: 160px; }
TABLE.messageList TR.thread9 .messageSubjectColumn
{ background-position: 180px 0px !important;
padding-left: 180px; }
TABLE.messageList TR.thread10 .messageSubjectColumn
{ background-position: 200px 0px !important;
padding-left: 200px; }
TABLE.messageList .messageAddressColumn TABLE.messageList .messageAddressColumn
{ max-width: 18%; { max-width: 18%;
width: 18%; } width: 18%; }

View File

@ -17,7 +17,10 @@ var Mailer = {
quotas: null, quotas: null,
dataTable: null, dataTable: null,
dataSources: new Hash() dataSources: new Hash(),
columnsOrder: null,
sortByThread: false
}; };
var usersRightsWindowHeight = 320; var usersRightsWindowHeight = 320;
@ -169,8 +172,10 @@ function markMailInWindow(win, msguid, markread) {
return (unseenCount != 0); return (unseenCount != 0);
} }
/**
* This is called by UIxMailView with window.opener.
*/
function markMailReadInWindow(win, msguid) { function markMailReadInWindow(win, msguid) {
/* this is called by UIxMailView with window.opener */
return markMailInWindow(win, msguid, true); return markMailInWindow(win, msguid, true);
} }
@ -203,6 +208,31 @@ function openMessageWindowsForSelection(action, firstOnly) {
return false; return false;
} }
/*
function mailListToggleMessageThread(row, cell) {
var show = row.hasClassName('closedThread');
$(cell).down('img').remove();
if (show) {
row.removeClassName('closedThread');
row.addClassName('openedThread');
var img = createElement("img", null, null, { src: ResourcesURL + '/arrow-down.png' });
cell.insertBefore(img, cell.firstChild);
}
else {
row.removeClassName('openedThread');
row.addClassName('closedThread');
var img = createElement("img", null, null, { src: ResourcesURL + '/arrow-right.png' });
cell.insertBefore(img, cell.firstChild);
}
while ((row = row.next()) && row.hasClassName('thread')) {
if (show)
row.show();
else
row.hide();
}
}
*/
/* Triggered when clicking on the read/unread dot of a message row or /* Triggered when clicking on the read/unread dot of a message row or
* through the contextual menu. */ * through the contextual menu. */
function mailListToggleMessagesRead(row) { function mailListToggleMessagesRead(row) {
@ -399,6 +429,7 @@ function deleteSelectedMessages(sender) {
var uids = new Array(); // message IDs var uids = new Array(); // message IDs
var paths = new Array(); // row IDs var paths = new Array(); // row IDs
var unseenCount = 0; var unseenCount = 0;
var refreshFolder = false;
if (rowIds && rowIds.length > 0) { if (rowIds && rowIds.length > 0) {
messageList.deselectAll(); messageList.deselectAll();
@ -432,15 +463,26 @@ function deleteSelectedMessages(sender) {
var row = $("row_" + uid); var row = $("row_" + uid);
var nextRow = false; var nextRow = false;
if (row) { if (row) {
//row.addClassName("deleted"); // when we'll offer "mark as deleted"
nextRow = row.next("tr"); nextRow = row.next("tr");
if (!nextRow.id.startsWith('row_')) if (!nextRow.id.startsWith('row_'))
nextRow = row.previous("tr"); nextRow = row.previous("tr");
// row.addClassName("deleted"); // when we'll offer "mark as deleted" else if (row.hasClassName('openedThread') || row.hasClassName('closedThread')) {
// Thread root deleted -- must refresh folder
refreshFolder = true;
// New row will be the new thread root -- mark it as first mail of the thread
var nextUid = nextRow.id.substr(4);
var nextIndex = Mailer.dataTable.dataSource.indexOf(nextUid);
Mailer.dataTable.dataSource.uids[nextIndex][2] = 1; // mark it as "first"
Mailer.dataTable.invalidate(nextUid, true);
}
if (nextRow.id.startsWith('row_')) { if (nextRow.id.startsWith('row_')) {
Mailer.currentMessages[Mailer.currentMailbox] = nextRow.id.substr(4); Mailer.currentMessages[Mailer.currentMailbox] = nextRow.id.substr(4);
nextRow.selectElement(); nextRow.selectElement();
if (loadMessage(Mailer.currentMessages[Mailer.currentMailbox])) if (loadMessage(Mailer.currentMessages[Mailer.currentMailbox]) && !refreshFolder)
// Seen state has changed
Mailer.dataTable.invalidate(Mailer.currentMessages[Mailer.currentMailbox], true); Mailer.dataTable.invalidate(Mailer.currentMessages[Mailer.currentMailbox], true);
refreshFolder = true;
} }
} }
else { else {
@ -452,7 +494,6 @@ function deleteSelectedMessages(sender) {
lastClickedRow = nextRow.rowIndex; lastClickedRow = nextRow.rowIndex;
lastClickedRowId = nextRow.id; lastClickedRowId = nextRow.id;
} }
Mailer.dataTable.refresh();
deleteCachedMailboxByType("trash"); deleteCachedMailboxByType("trash");
} }
else { else {
@ -468,7 +509,7 @@ function deleteSelectedMessages(sender) {
} }
var url = ApplicationBaseURL + encodeURI(Mailer.currentMailbox) + "/batchDelete"; var url = ApplicationBaseURL + encodeURI(Mailer.currentMailbox) + "/batchDelete";
var parameters = "uid=" + uids.join(","); var parameters = "uid=" + uids.join(",");
var data = { "id": uids, "mailbox": Mailer.currentMailbox, "path": paths, "refreshUnseenCount": (unseenCount > 0) }; var data = { "id": uids, "mailbox": Mailer.currentMailbox, "path": paths, "refreshUnseenCount": (unseenCount > 0), "refreshFolder": refreshFolder };
triggerAjaxRequest(url, deleteSelectedMessagesCallback, data, parameters, triggerAjaxRequest(url, deleteSelectedMessagesCallback, data, parameters,
{ "Content-type": "application/x-www-form-urlencoded" }); { "Content-type": "application/x-www-form-urlencoded" });
} }
@ -485,6 +526,8 @@ function deleteSelectedMessagesCallback(http) {
// TODO : the unseen count should be returned when calling the batchDelete remote action, // TODO : the unseen count should be returned when calling the batchDelete remote action,
// in order to avoid this extra AJAX call. // in order to avoid this extra AJAX call.
getUnseenCountForFolder(data["mailbox"]); getUnseenCountForFolder(data["mailbox"]);
if (data["refreshFolder"])
Mailer.dataTable.refresh();
} }
else if (!http.callbackData["withoutTrash"]) { else if (!http.callbackData["withoutTrash"]) {
showConfirmDialog(_("Warning"), showConfirmDialog(_("Warning"),
@ -514,16 +557,11 @@ function onMenuDeleteMessage(event) {
preventDefault(event); preventDefault(event);
} }
function deleteMessage(url, id, mailbox, messageId) { /**
var data = { "id": new Array(id), "mailbox": mailbox, "path": new Array(messageId) }; * The following two functions are called from UIxMailPopupView
var parameters = "uid=" + id; * with window.opener.
deleteMessageRequestCount++; */
triggerAjaxRequest(url, deleteSelectedMessagesCallback, data, parameters,
{ "Content-type": "application/x-www-form-urlencoded" });
}
function deleteMessageWithDelay(url, id, mailbox, messageId) { function deleteMessageWithDelay(url, id, mailbox, messageId) {
/* this is called by UIxMailPopupView with window.opener */
var row = $("row_" + id); var row = $("row_" + id);
if (row) row.hide(); if (row) row.hide();
setTimeout("deleteMessage('" + setTimeout("deleteMessage('" +
@ -534,6 +572,14 @@ function deleteMessageWithDelay(url, id, mailbox, messageId) {
50); 50);
} }
function deleteMessage(url, id, mailbox, messageId) {
var data = { "id": new Array(id), "mailbox": mailbox, "path": new Array(messageId) };
var parameters = "uid=" + id;
deleteMessageRequestCount++;
triggerAjaxRequest(url, deleteSelectedMessagesCallback, data, parameters,
{ "Content-type": "application/x-www-form-urlencoded" });
}
function onPrintCurrentMessage(event) { function onPrintCurrentMessage(event) {
var messageList = $("messageListBody").down("TBODY"); var messageList = $("messageListBody").down("TBODY");
var rows = messageList.getSelectedNodesId(); var rows = messageList.getSelectedNodesId();
@ -588,9 +634,9 @@ function toggleAddressColumn(search, replace) {
if (header) { if (header) {
header.id = replace + "Header"; header.id = replace + "Header";
header.update(_(replace.capitalize())); header.update(_(replace.capitalize()));
var i = UserDefaults["SOGoMailListViewColumnsOrder"].indexOf(search.capitalize()); var i = Mailer.columnsOrder.indexOf(search.capitalize());
if (i >= 0) if (i >= 0)
UserDefaults["SOGoMailListViewColumnsOrder"][i] = replace.capitalize(); Mailer.columnsOrder[i] = replace.capitalize();
} }
if (sorting["attribute"] == search) if (sorting["attribute"] == search)
sorting["attribute"] = replace; sorting["attribute"] = replace;
@ -767,7 +813,7 @@ function openMailbox(mailbox, reload) {
if (inboxData) { if (inboxData) {
// Use UIDs and headers from the WOX template; this only // Use UIDs and headers from the WOX template; this only
// happens once and only with the inbox // happens once and only with the inbox
dataSource.init(inboxData['uids'], inboxData['headers']); dataSource.init(inboxData['uids'], inboxData['threaded'], inboxData['headers']);
inboxData = null; // invalidate this initial lookup inboxData = null; // invalidate this initial lookup
} }
else else
@ -813,12 +859,20 @@ function messageListCallback(row, data, isNew) {
row.id = data['rowID']; row.id = data['rowID'];
row.writeAttribute('labels', (data['labels']?data['labels']:"")); row.writeAttribute('labels', (data['labels']?data['labels']:""));
row.className = data['rowClasses']; row.className = data['rowClasses'];
row.show(); // make sure the row is visible
// Restore previous selection // Restore previous selection
if (data['uid'] == currentMessage) if (data['uid'] == currentMessage)
row.addClassName('_selected'); row.addClassName('_selected');
var columnsOrder = UserDefaults["SOGoMailListViewColumnsOrder"]; if (data['Thread'])
row.addClassName('openedThread');
else if (data['ThreadLevel'] > 0) {
if (data['ThreadLevel'] > 10) data['ThreadLevel'] = 10;
row.addClassName('thread');
row.addClassName('thread' + data['ThreadLevel']);
}
var cells; var cells;
if (Prototype.Browser.IE) if (Prototype.Browser.IE)
cells = row.childNodes; cells = row.childNodes;
@ -827,7 +881,7 @@ function messageListCallback(row, data, isNew) {
for (var j = 0; j < cells.length; j++) { for (var j = 0; j < cells.length; j++) {
var cell = $(cells[j]); var cell = $(cells[j]);
var cellType = columnsOrder[j]; var cellType = Mailer.columnsOrder[j];
if (data[cellType]) cell.innerHTML = data[cellType]; if (data[cellType]) cell.innerHTML = data[cellType];
else cell.innerHTML = '&nbsp;'; else cell.innerHTML = '&nbsp;';
@ -957,21 +1011,26 @@ function onMessageListRender(event) {
} }
function onMessageContextMenu(event) { function onMessageContextMenu(event) {
var row = getTarget(event); var target = Event.element(event);
var menu = $('messageListMenu'); var menu = $('messageListMenu');
var topNode = $('messageListBody'); var topNode = $('messageListBody');
var selectedNodes = topNode.getSelectedRows(); var selectedNodes = topNode.getSelectedRowsId();
if (row.tagName != 'TR') var row = target.up('TR');
row = row.parentNode;
if (row.tagName != 'TR')
row = row.parentNode;
if (selectedNodes.indexOf(row.id) < 0) {
if (target.tagName != 'TD')
target = target.up('TD');
onRowClick(event, target);
selectedNodes = topNode.getSelectedRowsId();
}
menu.observe("hideMenu", onMessageContextMenuHide); menu.observe("hideMenu", onMessageContextMenuHide);
if (selectedNodes.length > 1) if (selectedNodes.length > 1)
popupMenu(event, "messagesListMenu", selectedNodes); popupMenu(event, "messagesListMenu", selectedNodes);
else else if (selectedNodes.length == 1)
popupMenu(event, "messageListMenu", row); popupMenu(event, "messageListMenu", row);
return false;
} }
function onMessageContextMenuHide(event) { function onMessageContextMenuHide(event) {
@ -1110,7 +1169,10 @@ function onMessageSelectionChange(event) {
if (t.tagName == 'IMG') { if (t.tagName == 'IMG') {
t = t.parentNode; t = t.parentNode;
if (t.tagName == 'TD') { if (t.tagName == 'TD') {
if (t.className == 'messageUnreadColumn') { if (t.className == 'messageThreadColumn') {
//mailListToggleMessageThread(t.parentNode, t); Disable thread collapsing
}
else if (t.className == 'messageUnreadColumn') {
mailListToggleMessagesRead(t.parentNode); mailListToggleMessagesRead(t.parentNode);
return true; return true;
} }
@ -1781,15 +1843,17 @@ function refreshMessage(mailbox, messageUID) {
} }
} }
function configureMessageListEvents(headerTable, dataTable) { function configureMessageListEvents() {
var headerTable = $("messageListHeader");
var dataTable = $("messageListBody");
if (headerTable) if (headerTable)
// Sortable columns // Sortable columns
configureSortableTableHeaders(headerTable); configureSortableTableHeaders(headerTable);
if (dataTable) { if (dataTable) {
dataTable.multiselect = true; dataTable.multiselect = true;
// Each body row can load a message dataTable.observe("click", onMessageSelectionChange);
dataTable.observe("mouseup", onMessageSelectionChange);
dataTable.observe("dblclick", onMessageDoubleClick); dataTable.observe("dblclick", onMessageDoubleClick);
dataTable.observe("selectstart", listRowMouseDownHandler); dataTable.observe("selectstart", listRowMouseDownHandler);
dataTable.observe("contextmenu", onMessageContextMenu); dataTable.observe("contextmenu", onMessageContextMenu);
@ -1844,6 +1908,11 @@ function openInbox(node) {
function initMailer(event) { function initMailer(event) {
if (!$(document.body).hasClassName("popup")) { if (!$(document.body).hasClassName("popup")) {
Mailer.columnsOrder = UserDefaults["SOGoMailListViewColumnsOrder"];
Mailer.sortByThread = UserDefaults["SOGoMailSortByThreads"] != null && parseInt(UserDefaults["SOGoMailSortByThreads"]) > 0;
if (!Mailer.sortByThread && Mailer.columnsOrder[0] == "Thread")
Mailer.columnsOrder.shift(); // drop the thread column
// Restore sorting from user settings // Restore sorting from user settings
if (UserSettings && UserSettings["Mail"] && UserSettings["Mail"]["SortingState"]) { if (UserSettings && UserSettings["Mail"] && UserSettings["Mail"]["SortingState"]) {
sorting["attribute"] = UserSettings["Mail"]["SortingState"][0]; sorting["attribute"] = UserSettings["Mail"]["SortingState"][0];
@ -1866,7 +1935,7 @@ function initMailer(event) {
messageListHeader.restore($H(UserSettings["Mail"]["ColumnsState"])); messageListHeader.restore($H(UserSettings["Mail"]["ColumnsState"]));
} }
configureMessageListEvents($("messageListHeader"), $("messageListBody")); configureMessageListEvents();
initMailboxTree(); initMailboxTree();
initMessageCheckTimer(); initMessageCheckTimer();
@ -2589,15 +2658,16 @@ function onLabelMenuPrepareVisibility() {
function onMarkMenuPrepareVisibility() { function onMarkMenuPrepareVisibility() {
var messageList = $("messageListBody"); var messageList = $("messageListBody");
if (messageList) { if (messageList) {
var nodes = messageList.down("TBODY").getSelectedNodes(); var nodes = messageList.down("TBODY").getSelectedNodesId();
var isRead = false; var isRead = false;
var isFlagged = false; var isFlagged = false;
if (nodes.length > 0) { if (nodes.length > 0) {
var row = nodes[0]; var row = null;
var firstTd = row.childElements().first(); for (var i = 0; row == null && i < nodes.length; i++)
var img = firstTd.childElements().first(); row = $(nodes[i]);
var img = row.down('img');
isFlagged = img.hasClassName ("messageIsFlagged"); isFlagged = img.hasClassName ("messageIsFlagged");
isRead = !row.hasClassName("mailer_unreadmail"); isRead = !row.hasClassName("mailer_unreadmail");
} }
@ -2685,7 +2755,7 @@ function getMenus() {
"-", "moveMailboxMenu", "-", "moveMailboxMenu",
"copyMailboxMenu", "label-menu", "copyMailboxMenu", "label-menu",
"mark-menu", "-", "mark-menu", "-",
saveAs, null, saveAs, null, null,
onMenuDeleteMessage ], onMenuDeleteMessage ],
imageMenu: [ saveImage ], imageMenu: [ saveImage ],
attachmentMenu: [ saveAttachment ], attachmentMenu: [ saveAttachment ],
@ -2857,16 +2927,15 @@ function startDragging (itm, e) {
if (target.up('TBODY') == undefined) if (target.up('TBODY') == undefined)
return; return;
if (target.tagName != 'TD')
target = target.up('TD');
var row = target.up('TR'); var row = target.up('TR');
var handle = $("dragDropVisual"); var handle = $("dragDropVisual");
var selectedIds = $("messageListBody").getSelectedRowsId(); var selectedIds = $("messageListBody").getSelectedRowsId();
var count = selectedIds.length; var count = selectedIds.length;
var rowId = row.id; var rowId = row.id;
if (count == 0 || selectedIds.indexOf(rowId) < 0) { if (count == 0 || selectedIds.indexOf(rowId) < 0) {
if (target.tagName != 'TD')
target = target.up('TD');
onRowClick(e, target); onRowClick(e, target);
selectedIds = $("messageListBody").getSelectedRowsId(); selectedIds = $("messageListBody").getSelectedRowsId();
count = selectedIds.length; count = selectedIds.length;

View File

@ -57,9 +57,9 @@ var SOGoDataTableInterface = {
s = startIndex; s = startIndex;
e = endIndex; e = endIndex;
} }
while (s <= e) { while (s <= e) {
uid = "row_" + div.dataSource.uids[s]; uid = "row_" + div.dataSource.uidAtIndex(s);
if (this.selectedIds.indexOf(uid) < 0) if (this.selectedIds.indexOf(uid) < 0)
this.selectedIds.push(uid); this.selectedIds.push(uid);
s++; s++;
@ -71,7 +71,7 @@ var SOGoDataTableInterface = {
var div = this.up('div'); var div = this.up('div');
this.selectedIds = new Array(); this.selectedIds = new Array();
for (var i = 0; i < div.dataSource.uids.length; i++) for (var i = 0; i < div.dataSource.uids.length; i++)
this.selectedIds.push("row_" + div.dataSource.uids[i]); this.selectedIds.push("row_" + div.dataSource.uidAtIndex(i));
this.refreshSelectionByIds(); this.refreshSelectionByIds();
}, },

View File

@ -8,6 +8,7 @@ SOGoMailDataSource = Class.create({
this.url = url; this.url = url;
this.uids = new Array(); this.uids = new Array();
this.threaded = false;
this.cache = new Hash(); this.cache = new Hash();
this.loaded = false; this.loaded = false;
@ -27,7 +28,7 @@ SOGoMailDataSource = Class.create({
invalidate: function(uid) { invalidate: function(uid) {
this.cache.unset(uid); this.cache.unset(uid);
var index = this.uids.indexOf(parseInt(uid)); var index = this.indexOf(uid);
// log ("MailDataSource.invalidate(" + uid + ") at index " + index); // log ("MailDataSource.invalidate(" + uid + ") at index " + index);
return index; return index;
@ -41,10 +42,16 @@ SOGoMailDataSource = Class.create({
return index; return index;
}, },
init: function(uids, headers) { init: function(uids, threaded, headers) {
this.uids = uids; this.uids = uids;
if (typeof threaded != "undefined") {
this.threaded = threaded;
if (threaded)
this.uids.shift(); // drop key fields
}
// log ("MailDataSource.init() " + this.uids.length + " uids loaded");
if (headers) { if (headers) {
var keys = headers[0]; var keys = headers[0];
for (var i = 1; i < headers.length; i++) { for (var i = 1; i < headers.length; i++) {
@ -53,6 +60,7 @@ SOGoMailDataSource = Class.create({
header[keys[j]] = headers[i][j]; header[keys[j]] = headers[i][j];
this.cache.set(header["uid"], header); this.cache.set(header["uid"], header);
} }
// log ("MailDataSource.init() " + this.cache.keys().length + " headers loaded");
} }
this.loaded = true; this.loaded = true;
@ -81,7 +89,7 @@ SOGoMailDataSource = Class.create({
if (http.responseText.length > 0) { if (http.responseText.length > 0) {
var data = http.responseText.evalJSON(true); var data = http.responseText.evalJSON(true);
if (data.uids) if (data.uids)
this.init(data.uids, data.headers); this.init(data.uids, data.threaded, data.headers);
else else
this.init(data); this.init(data);
if (this.delayedGetData) { if (this.delayedGetData) {
@ -136,8 +144,9 @@ SOGoMailDataSource = Class.create({
// log ("MailDataSource._getData() from " + index + " to " + (index + count) + " boosted from " + start + " to " + end); // log ("MailDataSource._getData() from " + index + " to " + (index + count) + " boosted from " + start + " to " + end);
for (i = 0, j = start; j < end; j++) { for (i = 0, j = start; j < end; j++) {
if (!this.cache.get(this.uids[j])) { var uid = this.threaded? this.uids[j][0] : this.uids[j];
missingUids[i] = this.uids[j]; if (!this.cache.get(uid)) {
missingUids[i] = uid;
i++; i++;
} }
} }
@ -196,12 +205,45 @@ SOGoMailDataSource = Class.create({
var i, j; var i, j;
var data = new Array(); var data = new Array();
for (i = start, j = 0; i < end; i++, j++) { for (i = start, j = 0; i < end; i++, j++) {
data[j] = this.cache.get(this.uids[i]); if (this.threaded) {
data[j] = this.cache.get(this.uids[i][0]);
// Add thread-related data
if (parseInt(this.uids[i][2]) > 0)
data[j]['Thread'] = '&nbsp;'; //'<img class="messageThread" src="' + ResourcesURL + '/arrow-down.png">';
else
delete data[j]['Thread'];
if (parseInt(this.uids[i][1]) > -1)
data[j]['ThreadLevel'] = this.uids[i][1];
else
delete data[j]['ThreadLevel'];
}
else {
data[j] = this.cache.get(this.uids[i]);
}
} }
callbackFunction(id, start, this.uids.length, data); callbackFunction(id, start, this.uids.length, data);
}, },
indexOf: function(uid) { indexOf: function(uid) {
return this.uids.indexOf(parseInt(uid)); var index = -1;
if (this.threaded) {
for (var i = 0; i < this.uids.length; i++)
if (this.uids[i][0] == uid) {
index = i;
break;
}
}
else
index = this.uids.indexOf(parseInt(uid));
return index;
},
uidAtIndex: function(index) {
if (this.threaded)
return this.uids[index][0];
else
return this.uids[index];
} }
}); });

View File

@ -93,7 +93,7 @@ div#headerArea div.addressList
div#attachmentsArea div#attachmentsArea
{ display: none; { display: none;
float: right; float: right;
width: 120px; width: 200px;
padding: 2px 5px 0; padding: 2px 5px 0;
margin: auto; margin: auto;
border-left: 1px solid #888; } border-left: 1px solid #888; }
@ -144,7 +144,9 @@ UL#attachments
list-style-type: none; list-style-type: none;
list-style-image: none; list-style-image: none;
overflow: auto; overflow: auto;
overflow-x: hidden; } overflow-x: hidden;
-moz-user-select: none;
-khtml-user-select: none; }
UL#attachments LI UL#attachments LI
{ white-space: nowrap; { white-space: nowrap;

View File

@ -361,6 +361,7 @@ function initMailEditor() {
var list = $("attachments"); var list = $("attachments");
if (!list) return; if (!list) return;
list.multiselect = true;
list.on("click", onRowClick); list.on("click", onRowClick);
list.attachMenu("attachmentsMenu"); list.attachMenu("attachmentsMenu");
var elements = $(list).childNodesWithTag("li"); var elements = $(list).childNodesWithTag("li");

View File

@ -89,7 +89,7 @@ DIV.listWrapper
DIV#filtersListWrapper DIV#filtersListWrapper
{ bottom: 30px; { bottom: 30px;
right: 2em; right: 2em;
top: 164px; top: 174px;
left: 2em; } left: 2em; }
TABLE#filtersList TABLE#filtersList

View File

@ -605,14 +605,6 @@ function onRowClick(event, target) {
// Single line selection // Single line selection
$(node.parentNode).deselectAll(); $(node.parentNode).deselectAll();
$(node).selectElement(); $(node).selectElement();
if (initialSelection != $(node.parentNode).getSelectedNodesId()) {
// Selection has changed; fire mousedown event
var parentNode = node.parentNode;
if (parentNode.tagName == 'TBODY')
parentNode = parentNode.parentNode;
parentNode.fire("mousedown");
}
} }
if (rowIndex != null) { if (rowIndex != null) {
lastClickedRow = rowIndex; lastClickedRow = rowIndex;
@ -735,8 +727,6 @@ function hideMenu(menuNode) {
menuNode.parentMenu.submenu = null; menuNode.parentMenu.submenu = null;
menuNode.parentMenu = null; menuNode.parentMenu = null;
} }
$(menuNode).fire("mousedown");
} }
function onMenuEntryClick(event) { function onMenuEntryClick(event) {

View File

@ -50,7 +50,15 @@ TABLE.messageList TD
{ white-space: pre; } { white-space: pre; }
TABLE.messageList TR._selected TD TABLE.messageList TR._selected TD
{ border-right: 1px solid #9ABCD8; } { border-right: 1px solid #9ABCD8 !important; }
TABLE.messageList TR.openedThread TD,
TABLE.messageList TR.closedThread TD,
TABLE.messageList TR.thread .messageThreadColumn
{ border-right: 1px solid #DDD; }
TABLE.messageList TR.thread TD
{ border-right: 1px solid #EEE; }
/* ContactsUI */ /* ContactsUI */