kopano-ol-extension/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/GABLookupControl.cs

336 lines
12 KiB
C#

/// Copyright 2017 Kopano b.v.
///
/// This program is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License, version 3,
/// as published by the Free Software Foundation.
///
/// 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 Affero General Public License for more details.
///
/// You should have received a copy of the GNU Affero General Public License
/// along with this program.If not, see<http://www.gnu.org/licenses/>.
///
/// Consult LICENSE file for details
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Acacia.ZPush;
using Acacia.Features.GAB;
using Acacia.Stubs;
using System.Collections;
using Acacia.Controls;
namespace Acacia.UI
{
public partial class GABLookupControl : KComboBoxCustomDraw
{
private class NotFoundGABUser : GABUser
{
public NotFoundGABUser(string userName) : base(userName)
{
}
}
private class GABDataSource : KDataSource<GABUser>
{
private readonly GABHandler _gab;
public int Limit { get; set; }
public GABDataSource(GABHandler gab)
{
this._gab = gab;
Limit = 8;
}
protected override void UpdateFilter()
{
}
public override IEnumerable<GABUser> FilteredItems
{
get
{
ISearch<IContactItem> search = null;
try
{
IEnumerable<IItem> items;
if (HasFilter)
{
search = _gab.Contacts.Search<IContactItem>();
search.Sort("FullName", false);
ISearchOperator terms = search.AddOperator(SearchOperator.Or);
terms.AddField("urn:schemas:contacts:cn").SetOperation(SearchOperation.Like, Filter.FilterText + "%");
terms.AddField("urn:schemas:contacts:customerid").SetOperation(SearchOperation.Like, Filter.FilterText + "%");
terms.AddField("urn:schemas:contacts:email1").SetOperation(SearchOperation.Like, Filter.FilterText + "%");
items = search.Search(Limit);
}
else
{
items = _gab.Contacts.Items.Sort("FullName", false);
}
int index = 0;
foreach (IItem item in items)
{
IContactItem contact = item as IContactItem;
// The check for customer id is to avoid groups created as contacts
if (contact != null && contact.CustomerID != null)
{
++index;
if (index > Limit)
break;
yield return new GABUser(contact);
}
}
}
finally
{
if (search != null)
search.Dispose();
}
}
}
protected override string GetItemText(GABUser item)
{
// If there is a filter, try to complete that
if (!string.IsNullOrEmpty(Filter?.FilterText))
{
string s = Filter?.FilterText.ToLower();
if (item.UserName?.ToLower().StartsWith(s) == true)
return item.UserName;
else if (item.FullName?.ToLower().StartsWith(s) == true)
return item.FullName;
else if (item.EmailAddress?.ToLower().StartsWith(s) == true)
return item.EmailAddress;
}
return item.UserName;
}
public override object NotFoundItem
{
get
{
return new NotFoundGABUser(Filter.FilterText);
}
}
}
public GABLookupControl() : this(null)
{
}
public GABLookupControl(GABHandler gab)
{
InitializeComponent();
GAB = gab;
}
#region Properties and events
[Category("Appearance")]
[Localizable(true)]
public string NotFoundText
{
get;
set;
}
#region SelectedUser
public class SelectedUserEventArgs : EventArgs
{
public readonly GABUser SelectedUser;
public readonly bool IsChosen;
public SelectedUserEventArgs(GABUser selectedUser, bool isChosen)
{
this.SelectedUser = selectedUser;
this.IsChosen = isChosen;
}
}
public delegate void SelectedUserEventHandler(object source, SelectedUserEventArgs e);
[Category("Behavior")]
public event SelectedUserEventHandler SelectedUserChanged;
private GABUser _selectedUser;
public GABUser SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = null;
Select(value);
}
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
_selectedUser = string.IsNullOrEmpty(Text) ? null : new GABUser(Text);
SelectedUserChanged?.Invoke(this, new SelectedUserEventArgs(_selectedUser, false));
}
protected override void OnSelectedItemChanged()
{
_selectedUser = (GABUser)SelectedItem?.Item;
// If the tab key was used to select, the user wants to click open
SelectedUserChanged?.Invoke(this, new SelectedUserEventArgs(_selectedUser, GetCommitSource() != CommitSource.KeyTab));
}
#endregion
#region GAB
private GABHandler _gab;
/// <summary>
/// The GAB. This must be set to allow lookups
/// </summary>
public GABHandler GAB
{
get { return _gab; }
set
{
if (_gab != value)
{
_gab = value;
DataSource = _gab == null ? null : new GABDataSource(_gab);
}
}
}
#endregion
#endregion
public GABUser LookupExact(string username)
{
string s = username.ToLower();
if (DataSource != null)
{
using (ISearch<IContactItem> search = _gab.Contacts.Search<IContactItem>())
{
search.Sort("FullName", false);
ISearchOperator terms = search.AddOperator(SearchOperator.Or);
terms.AddField("urn:schemas:contacts:cn").SetOperation(SearchOperation.Like, username);
terms.AddField("urn:schemas:contacts:customerid").SetOperation(SearchOperation.Like, username);
terms.AddField("urn:schemas:contacts:email1").SetOperation(SearchOperation.Like, username);
using (IContactItem contact = search.SearchOne())
{
if (contact != null)
{
return new GABUser(contact);
}
}
}
}
return new GABUser(username);
}
#region Rendering
private static readonly Size NameSpacing = new Size(12, 4);
private static readonly Padding ItemPadding = new Padding(5);
private static readonly Padding BorderPadding = new Padding(2);
private const int BorderThickness = 1;
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
GABUser item = (GABUser)e.Item;
Size nameSize = TextRenderer.MeasureText(e.Graphics, item.FullName, Font);
Size loginSize = TextRenderer.MeasureText(e.Graphics, item.UserName, Font);
Size emailSize = TextRenderer.MeasureText(e.Graphics, GetSecondLine(item), Font);
e.ItemWidth = Math.Max(emailSize.Width, nameSize.Width + loginSize.Width + NameSpacing.Width) +
ItemPadding.Horizontal;
e.ItemHeight = emailSize.Height + Math.Max(nameSize.Height, loginSize.Height) +
ItemPadding.Vertical +
NameSpacing.Height +
BorderThickness + BorderPadding.Vertical;
}
private string GetSecondLine(GABUser item)
{
if (item is NotFoundGABUser)
return NotFoundText;
else
return item.EmailAddress;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
GABUser item = (GABUser)e.Item;
// Draw the background
if (e.State == DrawItemState.Selected)
{
// If the item is selected, we don't want the separating border to get selected too.
// So draw the normal background in the border area
Rectangle rect = e.Bounds;
rect.Y = rect.Bottom - BorderPadding.Vertical - BorderThickness;
rect.Height = BorderPadding.Vertical + BorderThickness;
new System.Windows.Forms.DrawItemEventArgs(e.Graphics, e.Font, rect, e.Index, DrawItemState.None).DrawBackground();
// And the selected background in the item area.
rect.Y = e.Bounds.Y;
rect.Height = e.Bounds.Height - BorderPadding.Vertical - BorderThickness;
new System.Windows.Forms.DrawItemEventArgs(e.Graphics, e.Font, rect, e.Index, DrawItemState.Selected).DrawBackground();
}
else
{
e.DrawBackground();
}
// Get the sizes
Size nameSize = TextRenderer.MeasureText(e.Graphics, item.FullName, Font);
Size loginSize = TextRenderer.MeasureText(e.Graphics, item.UserName, Font);
Size emailSize = TextRenderer.MeasureText(e.Graphics, item.EmailAddress, Font);
// Draw the full name top-left
Point pt = e.Bounds.TopLeft();
pt.Y += ItemPadding.Top;
pt.X += ItemPadding.Left;
TextRenderer.DrawText(e.Graphics, item.FullName, Font, pt, e.ForeColor);
// Draw the username top-right
pt.X = e.Bounds.Right - loginSize.Width - ItemPadding.Right;
TextRenderer.DrawText(e.Graphics, item.UserName, Font, pt, e.ForeColor);
// Draw the email below
pt.Y += Math.Max(nameSize.Height, loginSize.Height) + NameSpacing.Height;
pt.X = e.Bounds.X + ItemPadding.Left;
TextRenderer.DrawText(e.Graphics, GetSecondLine(item), Font, pt, e.ForeColor);
// Draw a separator line
if (e.Index < DisplayItemCount - 1)
{
int lineY = e.Bounds.Bottom - 1 - BorderThickness - BorderPadding.Bottom;
e.Graphics.DrawLine(Pens.LightGray, BorderPadding.Left, lineY, e.Bounds.Width - BorderPadding.Right, lineY);
}
}
#endregion
}
}