/// <summary> /// Handle the Send button. At this point we assume there are valid recipients and something /// in the message body. We really ought to do some validation of the recipient names here. /// </summary> /// <param name="sender">The new message form</param> /// <param name="e">Event arguments</param> private void nmSend_Click(object sender, EventArgs e) { if (_currentConversation == null) { _currentConversation = new InboxConversation { Subject = nmSubject.Text, Author = CIX.Username, Date = DateTime.UtcNow.UTCToGMTBST() }; } if (_currentMessage == null) { _currentMessage = new InboxMessage { Author = (_currentConversation.ID > 0) ? CIX.Username : nmRecipients.Text, Body = nmMessage.Text, ConversationID = _currentConversation.ID, Date = DateTime.UtcNow.UTCToGMTBST() }; CIX.ConversationCollection.Add(_currentConversation, _currentMessage); } DialogResult = DialogResult.OK; Close(); }
/// <summary> /// Handle the conversation being deleted from the client. /// </summary> private void OnConversationDeleted(object sender, InboxEventArgs args) { Platform.UIThread(this, delegate { InboxConversation conversation = args.Conversation; int selectedRow = SelectedRow; bool deletingCurrent = (selectedRow != -1) && (conversation == _conversations[selectedRow]); _conversations.Remove(conversation); InitialiseList(); RefreshList(); if (deletingCurrent) { if (selectedRow == _conversations.Count) { --selectedRow; } if (selectedRow < 0) { ShowEmptyMessage(); } else { SelectedRow = selectedRow; } } }); }
/// <summary> /// Draw the full thread for the selected conversation. The users current scroll position is not altered /// unless resetScrollPosition is true in which case we scroll so that the last message is visible. /// /// Drawing the thread also optionally marks the conversation as read and triggers an update on the server. /// </summary> /// <param name="conversation">The conversation to draw</param> private void ShowMessage(InboxConversation conversation) { inboxMessagePane.BeginUpdate(); inboxMessagePane.Items.Clear(); if (conversation != null) { InboxItem lastItem = null; foreach (InboxMessage message in conversation.Messages) { InboxItem item = new InboxItem(inboxMessagePane, lastItem != null) { ID = message.RemoteID, Image = Mugshot.MugshotForUser(message.Author, true).RealImage, ItemColour = SystemColors.Window, FullDateString = _showFullDate, Message = message, Font = _bodyFont }; inboxMessagePane.Items.Add(item); lastItem = item; } inboxMessagePane.SelectedItem = lastItem; } inboxMessagePane.EndUpdate(null); }
/// <summary> /// Draw the row. /// </summary> private void DrawSubItem(object sender, DrawListViewSubItemEventArgs args) { InboxConversation message = args.Item.Tag as InboxConversation; if (message != null) { DrawRectElements elements = ComputeDrawRectElements(args.Graphics, message, args.Bounds); Color textColor = UI.Forums.HeaderFooterColour; if (args.Item.Selected) { args.DrawFocusRectangle(elements.BoundaryRect); textColor = UI.Forums.SelectionTextColour; } // Draw the selection around the control. using (Pen edgePen = new Pen(args.Item.Selected ? UI.Forums.SelectionColour : BackColor)) { Color fillColour = args.Item.Selected ? UI.Forums.SelectionColour : UI.Forums.RootColour; using (Brush backBrush = new SolidBrush(fillColour)) { args.Graphics.FillRoundedRectangle(edgePen, backBrush, elements.BoundaryRect); } } using (Brush textBrush = new SolidBrush(textColor)) { // Draw read image Image readImage = ReadImageForMessage(message); args.Graphics.DrawImage(readImage, elements.ReadRect); // Draw author name args.Graphics.DrawString(message.Author, _font, textBrush, elements.AuthorRect); // Draw separator const string separatorChar = "•"; args.Graphics.DrawString(separatorChar, _font, textBrush, elements.Separator1Rect); // Draw date string dateString = (_showFullDate) ? message.Date.ToString("d MMM yyyy") + " " + message.Date.ToShortTimeString() : message.Date.FriendlyString(true); args.Graphics.DrawString(dateString, _font, textBrush, elements.DateRect); // Draw ID field string idString = (message.IsDraft) ? "Draft" : message.RemoteID.ToString(CultureInfo.InvariantCulture); args.Graphics.DrawString(idString, _font, textBrush, elements.IDRect); // Another separator args.Graphics.DrawString(separatorChar, _font, textBrush, elements.Separator2Rect); // Draw subject line string subjectLine = message.Subject; args.Graphics.DrawString(subjectLine, _font, textBrush, elements.SubjectRect, new StringFormat(StringFormatFlags.NoWrap)); } } }
/// <summary> /// Go to the next priority unread message. /// </summary> private void GoToNextPriorityUnread(InboxConversation conversation) { if (conversation != null && conversation.UnreadCount > 0) { conversation.MarkRead(); } FoldersTree.NextUnread(FolderOptions.NextUnread | FolderOptions.Priority); }
/// <summary> /// Return the appropriate read icon for this message based on its unread and priority state /// </summary> private static Image ReadImageForMessage(InboxConversation conversation) { if (conversation.LastError) { return(Resources.Error1); } return((conversation.UnreadCount > 0) ? Resources.UnreadMessage : Resources.ReadMessage); }
/// <summary> /// This event is fired when a new conversation is added to the list of conversations. /// </summary> private void OnConversationAdded(object sender, InboxEventArgs args) { Platform.UIThread(this, delegate { InboxConversation selectedMessage = SelectedMessage; SortConversations(); RestoreSelection(selectedMessage); }); }
/// <summary> /// Reload the mail folder list /// </summary> private void RefreshList() { if (_conversations.Count > 0) { InboxConversation savedMessage = SelectedMessage; inboxConversations.RedrawItems(0, _conversations.Count - 1, false); RestoreSelection(savedMessage); } }
/// <summary> /// Put the selection back on the specified conversation. /// </summary> /// <param name="conversation">The message to select</param> private void RestoreSelection(InboxConversation conversation) { if (_conversations.Count == 0) { ShowEmptyMessage(); } else if (conversation != null) { SelectedRow = _conversations.IndexOf(conversation); } }
/// <summary> /// Mark a conversation as read if we previously has unread messages. This also triggers /// a server update and redraws the inbox list item to remove the unread count badge. /// </summary> /// <param name="conversation">Conversation to mark read</param> private static void MarkConversationAsRead(InboxConversation conversation) { if (conversation.UnreadCount > 0) { conversation.MarkRead(); } else { conversation.MarkUnread(); } }
/// <summary> /// Locate and select the first unread mail message after the given row. /// </summary> private bool FirstUnreadAfterRow(int row, FolderOptions options) { while (row < _conversations.Count) { InboxConversation conversation = _conversations[row]; if ((options & FolderOptions.NextUnread) == FolderOptions.NextUnread && conversation.UnreadCount > 0) { SelectedRow = row; break; } row++; } return(row != _conversations.Count); }
/// <summary> /// Return the text string for the specified action. /// </summary> public override string TitleForAction(ActionID id) { if (SelectedMessage != null) { InboxConversation conversation = SelectedMessage; switch (id) { case ActionID.Read: return(conversation.UnreadCount > 0 ? Resources.AsRead : Resources.AsUnread); case ActionID.Withdraw: return(Resources.Delete); } } return(null); }
/// <summary> /// Action the specified ID with the given conversation. /// </summary> /// <param name="id">An action ID</param> /// <param name="conversation">A selected conversation, or null</param> private void Action(ActionID id, InboxConversation conversation) { switch (id) { case ActionID.NewMessage: FoldersTree.MainForm.Address = "cixmail:"; break; case ActionID.Print: Print(conversation); break; case ActionID.Profile: case ActionID.AuthorImage: FoldersTree.MainForm.Address = string.Format("cixuser:{0}", conversation.Author); break; case ActionID.Withdraw: conversation.MarkDelete(); break; case ActionID.PageMessage: case ActionID.NextUnread: GoToNextUnread(conversation); break; case ActionID.NextPriorityUnread: GoToNextPriorityUnread(conversation); break; case ActionID.Edit: case ActionID.Reply: { InboxMessageEditor newMessageWnd = new InboxMessageEditor(conversation); newMessageWnd.Show(); break; } case ActionID.Read: MarkConversationAsRead(conversation); break; case ActionID.SelectAll: SelectAll(); break; } }
/// <summary> /// Handle click on read icons on an item to change the read state of /// the conversation. /// </summary> private void OnMouseDown(object sender, MouseEventArgs mouseEventArgs) { ListViewItem item = inboxConversations.GetItemAt(mouseEventArgs.Location.X, mouseEventArgs.Location.Y); if (item != null) { if (item.Index >= 0 && item.Index < _conversations.Count) { InboxConversation message = _conversations[item.Index]; DrawRectElements elements = ComputeDrawRectElements(inboxConversations.CreateGraphics(), message, item.Bounds); if (elements.ReadRect.Contains(mouseEventArgs.Location)) { Action(ActionID.Read, message); } } } }
/// <summary> /// Display a directory of forums filtered by the given search string. If the search string is /// empty then all forums are displayed. /// </summary> /// <param name="searchString">A search string</param> public override void FilterViewByString(string searchString) { _currentFilterString = searchString.Trim().ToLower(); InboxConversation selectedMessage = SelectedMessage; _conversations = new List <InboxConversation>(CIX.ConversationCollection.AllConversations); bool _isFiltering = !string.IsNullOrEmpty(_currentFilterString); if (_isFiltering) { _conversations = _conversations.Where(msg => msg.Author.Contains(_currentFilterString) || msg.Subject.Contains(_currentFilterString)).ToList(); } InitialiseList(); RefreshList(); RestoreSelection(selectedMessage); }
/// <summary> /// Redisplay the selected message. /// </summary> private void DisplaySelectedRow(bool center) { int newSelection = SelectedRow; if (newSelection >= 0) { if (center) { CentreSelection(); } InboxConversation message = _conversations[newSelection]; string messageAddress = String.Format("cixmailbox:inbox:{0}", message.RemoteID); FoldersTree.MainForm.AddBacktrack(messageAddress, message.UnreadCount > 0); ShowMessage(message); FoldersTree.MainForm.RunEvent(EventID.MessageSelected, message); inboxConversations.Focus(); } }
/// <summary> /// Display the directory for the specified CategoryFolder /// </summary> public override bool ViewFromFolder(FolderBase folder, Address address, FolderOptions options) { if (folder != _currentFolder) { _currentFolder = folder; SortConversations(); } // If an address is specified then it refers to a conversation ID that // needs to be selected. If it is not found, the first message is selected // instead. if (address != null && address.Scheme == "cixmailbox") { int selectedID; Int32.TryParse(address.Data, out selectedID); int selectedIndex; for (selectedIndex = 0; selectedIndex < _conversations.Count; ++selectedIndex) { InboxConversation conversation = _conversations[selectedIndex]; if (conversation.RemoteID == selectedID) { break; } } if (selectedIndex == _conversations.Count) { selectedIndex = 0; } SelectedRow = selectedIndex; if (address.Unread) { SelectedMessage.MarkUnread(); } return(true); } // If options are specified then search for the next unread // in the list otherwise set the initial selection to something // useful. if (options == 0 && SelectedRow == -1) { SetInitialSelection(); } else { int row = inboxConversations.SearchRow; if (row < 0 || options.HasFlag(FolderOptions.Reset)) { row = 0; } else if (_conversations.Count > 0) { InboxConversation conversation = _conversations[row]; if (conversation.UnreadCount > 0) { conversation.MarkRead(); } } if (!FirstUnreadAfterRow(row, options)) { inboxConversations.SearchRow = 0; return(false); } } FoldersTree.SetTopicName(_currentFolder.FullName); ActiveControl = inboxConversations; inboxConversations.Focus(); return(true); }
/// <summary> /// Compute the display rectangles for the various field elements of the given message /// </summary> private DrawRectElements ComputeDrawRectElements(Graphics graphics, InboxConversation message, Rectangle bounds) { DrawRectElements drawRectElements = new DrawRectElements(); Rectangle drawRect = bounds; drawRect.Inflate(-2, -2); // The rectangle for the selection and focus drawRectElements.BoundaryRect = drawRect; drawRect.Y = drawRect.Y + (drawRect.Height - _font.Height) / 2; drawRect.X += 4; drawRect.Width -= 4; // Compute Read image rectangle Image readImage = ReadImageForMessage(message); Rectangle imageRect = new Rectangle(drawRect.X, bounds.Y + (bounds.Height - readImage.Height) / 2, readImage.Width, readImage.Height); drawRectElements.ReadRect = imageRect; drawRect.X += readImage.Width + 4; drawRect.Width -= readImage.Width + 4; // Compute author name rectangle SizeF idSize = graphics.MeasureString(message.Author, _font); drawRectElements.AuthorRect = drawRect; drawRect.X += (int)idSize.Width + 4; drawRect.Width -= (int)idSize.Width + 4; // Compute first separator rectangle const string separatorChar = "•"; idSize = graphics.MeasureString(separatorChar, _font); drawRectElements.Separator1Rect = drawRect; drawRect.X += (int)idSize.Width + 4; drawRect.Width -= (int)idSize.Width + 4; // Compute date field rectangle string dateString = (_showFullDate) ? message.Date.ToString("d MMM yyyy") + " " + message.Date.ToShortTimeString() : message.Date.FriendlyString(true); idSize = graphics.MeasureString(dateString, _font); drawRectElements.DateRect = drawRect; drawRect.X += (int)idSize.Width + 4; drawRect.Width -= (int)idSize.Width + 4; // Compute ID field rectangle string idString = (message.IsDraft) ? "Draft" : message.RemoteID.ToString(CultureInfo.InvariantCulture); idSize = graphics.MeasureString(idString, _font); drawRectElements.IDRect = drawRect; drawRect.X += (int)idSize.Width + 4; drawRect.Width -= (int)idSize.Width + 4; // Compute second separator rectangle idSize = graphics.MeasureString(separatorChar, _font); drawRectElements.Separator2Rect = drawRect; // Compute subject line rectangle drawRect.X += (int)idSize.Width + 4; drawRect.Width -= (int)idSize.Width + 8; drawRectElements.SubjectRect = drawRect; return(drawRectElements); }
/// <summary> /// Initialises a new instance of the <see cref="InboxMessageEditor"/> class and /// populates it with data from the specified conversation. /// </summary> public InboxMessageEditor(InboxConversation conversation) { InitializeComponent(); _currentConversation = conversation; }
/// <summary> /// Print the current conversation. /// </summary> private void Print(InboxConversation conversation) { Font printFont = new Font("Arial", 10); PrintDocument printDoc = new PrintDocument { PrinterSettings = FoldersTree.MainForm.PrintDocument.PrinterSettings, DefaultPageSettings = FoldersTree.MainForm.PrintDocument.DefaultPageSettings }; PrintDialog pdi = new PrintDialog { Document = printDoc, UseEXDialog = true }; if (pdi.ShowDialog() == DialogResult.OK) { StringBuilder convText = new StringBuilder(); List <InboxMessage> thread = conversation.Messages.ToList(); convText.AppendFormat(Resources.PrintMailSubject, conversation.Subject); convText.AppendLine(); convText.AppendFormat(Resources.PrintMailDate, conversation.Date); convText.AppendLine(); convText.AppendLine(); foreach (InboxMessage message in thread) { convText.AppendFormat(Resources.PrintMailFrom, message.Author); convText.AppendLine(); convText.AppendLine(); convText.AppendLine(message.Body); } string[] lines = convText.ToString().Split('\n'); int lineIndex = 0; try { printDoc.PrintPage += (sender, ev) => { float leftMargin = ev.MarginBounds.Left; float topMargin = ev.MarginBounds.Top; // Print each line of the file. float yPos = topMargin; while (lineIndex < lines.Length) { string line = lines[lineIndex]; SizeF sf = ev.Graphics.MeasureString(line, printFont, ev.MarginBounds.Width); if (yPos + sf.Height > ev.MarginBounds.Bottom) { break; } using (Brush textBrush = new SolidBrush(SystemColors.ControlText)) { ev.Graphics.DrawString(line, printFont, textBrush, new RectangleF(new PointF(leftMargin, yPos), sf), new StringFormat()); } yPos += (sf.Height > 0) ? sf.Height : printFont.GetHeight(ev.Graphics); ++lineIndex; } // If more lines exist, print another page. ev.HasMorePages = lineIndex < lines.Length; }; printDoc.Print(); } catch (Exception e) { MessageBox.Show(string.Format(Resources.PrintError, e.Message), Resources.Error, MessageBoxButtons.OK); } } }