// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for details. using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Windows.Forms; using OpenLiveWriter.Api; using OpenLiveWriter.ApplicationFramework.Skinning; using OpenLiveWriter.BlogClient; using OpenLiveWriter.ApplicationFramework; using OpenLiveWriter.Controls; using OpenLiveWriter.CoreServices; using OpenLiveWriter.Interop.Windows; using OpenLiveWriter.Extensibility.BlogClient; using OpenLiveWriter.Localization; using OpenLiveWriter.Localization.Bidi; using OpenLiveWriter.PostEditor; namespace OpenLiveWriter.PostEditor.PostPropertyEditing.CategoryControl { internal class CategoryDropDownControlM1 : ComboBox, IBlogPostEditor, IBlogCategorySettings, INewCategoryContext { #region IBlogPostEditor Members void IBlogPostEditor.Initialize(IBlogPostEditingContext editingContext, IBlogClientOptions clientOptions) { // categories from blog CategoryContext.SetBlogCategories(_targetBlog.Categories); // pickup new categories from the blog-post CategoryContext.SetNewCategories(editingContext.BlogPost.NewCategories); // combine all post categories before settting ArrayList selectedCategories = new ArrayList(); selectedCategories.AddRange(editingContext.BlogPost.NewCategories); selectedCategories.AddRange(editingContext.BlogPost.Categories); _lastSelectedCategories = new BlogPostCategory[0]; CategoryContext.SelectedCategories = selectedCategories.ToArray(typeof(BlogPostCategory)) as BlogPostCategory[]; toolTipCategories.SetToolTip(this, CategoryContext.FormattedCategoryList); _isDirty = false; } void IBlogPostEditor.SaveChanges(BlogPost post, BlogPostSaveOptions options) { post.Categories = CategoryContext.SelectedExistingCategories; post.NewCategories = CategoryContext.SelectedNewCategories; _isDirty = false; } private ToolTip2 toolTipCategories; bool IBlogPostEditor.ValidatePublish() { // see if we need to halt publishing to specify a category if (Visible) { if (PostEditorSettings.CategoryReminder && (CategoryContext.Categories.Length > 0) && (CategoryContext.SelectedCategories.Length == 0)) { if (DisplayMessage.Show(MessageId.CategoryReminder, FindForm()) == DialogResult.No) { Focus(); return false; } } } // proceed return true; } void IBlogPostEditor.OnPublishSucceeded(BlogPost blogPost, PostResult postResult) { _isDirty = false; } void INewCategoryContext.NewCategoryAdded(BlogPostCategory category) { _categoryContext.CommitNewCategory(category); _isDirty = false; } bool IBlogPostEditor.IsDirty { get { return _isDirty; } } private bool _isDirty = false; void IBlogPostEditor.OnBlogChanged(Blog newBlog) { // save dirty state bool isDirty = _isDirty; _targetBlog = newBlog; AdaptToBlogOptions(); // a new blog always wipes out "New" categories CategoryContext.SetNewCategories(new BlogPostCategory[0]); _lastSelectedCategories = new BlogPostCategory[0]; CategoryContext.SelectedCategories = new BlogPostCategory[0]; // restore dirty state _isDirty = isDirty; } private Blog _targetBlog; void IBlogPostEditor.OnBlogSettingsChanged(bool templateChanged) { AdaptToBlogOptions(); } private void AdaptToBlogOptions() { if (_targetBlog.ClientOptions.SupportsMultipleCategories) CategoryContext.SelectionMode = CategoryContext.SelectionModes.MultiSelect; else CategoryContext.SelectionMode = CategoryContext.SelectionModes.SingleSelect; CategoryContext.SupportsAddingCategories = _targetBlog.ClientOptions.SupportsNewCategories; CategoryContext.SupportsHierarchicalCategories = _targetBlog.ClientOptions.SupportsHierarchicalCategories; CategoryContext.MaxCategoryNameLength = _targetBlog.ClientOptions.MaxCategoryNameLength; // categories from blog CategoryContext.SetBlogCategories(_targetBlog.Categories); } public void OnClosing(CancelEventArgs e) { } public void OnPostClosing(CancelEventArgs e) { } #endregion public CategoryDropDownControlM1() : base() { if (DesignMode) _categoryContext = new CategoryContext(); // This call is required by the Windows.Forms Form Designer. InitializeComponent(); // prevent editing and showing of drop down DropDownStyle = ComboBoxStyle.DropDownList; // fully cusotm painting IntegralHeight = false; DrawMode = DrawMode.OwnerDrawFixed; Items.Add(String.Empty); } public void Initialize(IWin32Window parentFrame, CategoryContext categoryContext) { _categoryContext = categoryContext ?? new CategoryContext(); _parentFrame = parentFrame; _categoryContext.BlogCategorySettings = this; _categoryContext.Changed += new CategoryContext.CategoryChangedEventHandler(_categoryContext_Changed); } // replace standard drop down behavior with category form protected override void WndProc(ref Message m) { if (IsDropDownMessage(m)) { if (_categoryDisplayForm == null) DisplayCategoryForm(); else Focus(); } else { base.WndProc(ref m); } } // replace standard painting behavior protected override void OnDrawItem(DrawItemEventArgs evt) { BidiGraphics g = new BidiGraphics(evt.Graphics, ClientRectangle); // determine the text color based on whether we have categories bool hasCategories = _categoryContext.SelectedCategories.Length > 0; Color textColor = hasCategories ? SystemColors.ControlText : SystemColors.GrayText; //Color.FromArgb(200, SystemColors.ControlText) ; // margins const int HORIZONTAL_MARGIN = 1; // draw the icon (if we have one) const int ICON_MARGIN = 4; int textMargin = 0; if (_icon != null) { g.DrawImage(true, _icon, new Rectangle(ScaleX(ICON_MARGIN), ScaleY(2), ScaleX(_icon.Width), ScaleY(_icon.Height))); textMargin += ScaleX(ICON_MARGIN + _icon.Width); } // draw the text int leftMargin = textMargin; int rightMargin = ScaleX(HORIZONTAL_MARGIN); Rectangle textRegion = new Rectangle(new Point(leftMargin, -1), new Size(Width - leftMargin - rightMargin, Height)); TextFormatFlags tempFormat = DisplayFormat; Rectangle tempRectangle = textRegion; g.DrawText( _categoryContext.Text, Font, tempRectangle, textColor, tempFormat); // draw focus rectangle (only if necessary) evt.DrawFocusRectangle(); } BlogPostCategory[] IBlogCategorySettings.RefreshCategories(bool ignoreErrors) { try { _targetBlog.RefreshCategories(); } catch (BlogClientOperationCancelledException) { // show no UI for operation cancelled Debug.WriteLine("BlogClient operation cancelled"); } catch (Exception ex) { if (!ignoreErrors) DisplayableExceptionDisplayForm.Show(_parentFrame, ex); } return _targetBlog.Categories; } void IBlogCategorySettings.UpdateCategories(BlogPostCategory[] categories) { using (BlogSettings blogSettings = BlogSettings.ForBlogId(_targetBlog.Id)) blogSettings.Categories = categories; } private CategoryContext CategoryContext { get { return _categoryContext; } } public void DisplayCategoryForm() { Focus(); /* _categoryDisplayForm = new CategoryDisplayFormM1(this, _categoryContext) ; _categoryDisplayForm.MinDropDownWidth = 0; IMiniFormOwner miniFormOwner = FindForm() as IMiniFormOwner; if (miniFormOwner != null) _categoryDisplayForm.FloatAboveOwner(miniFormOwner); _categoryDisplayForm.Closed += new EventHandler(_categoryDisplayForm_Closed); using ( new WaitCursor() ) _categoryDisplayForm.Show(); */ Point anchor = PointToScreen(new Point(RightToLeft == RightToLeft.Yes ? 0 : ClientSize.Width, ClientSize.Height)); _categoryDisplayForm = new CategoryDisplayFormW3M1(_categoryContext, anchor); _categoryDisplayForm.SelfDispose = true; IMiniFormOwner miniFormOwner = FindForm() as IMiniFormOwner; if (miniFormOwner != null) _categoryDisplayForm.FloatAboveOwner(miniFormOwner); _categoryDisplayForm.Closed += _categoryDisplayForm_Closed; using (new WaitCursor()) _categoryDisplayForm.Show(); } private CategoryDisplayFormW3M1 _categoryDisplayForm = null; private void _categoryContext_Changed(object sender, CategoryContext.CategoryChangedEventArgs e) { if (!CategoryListsAreEqual(_lastSelectedCategories, CategoryContext.SelectedCategories)) _isDirty = true; // always record last selected categories _lastSelectedCategories = CategoryContext.SelectedCategories; toolTipCategories.SetToolTip(this, CategoryContext.FormattedCategoryList); Invalidate(); Update(); } private BlogPostCategory[] _lastSelectedCategories = new BlogPostCategory[0]; private bool CategoryListsAreEqual(BlogPostCategory[] categories1, BlogPostCategory[] categories2) { if (categories1.Length == categories2.Length) { for (int i = 0; i < categories1.Length; i++) { if (!categories1[i].Equals(categories2[i])) return false; } // if we got this far they are equal return true; } else { return false; } } private void _categoryDisplayForm_Closed(object sender, EventArgs e) { _categoryDisplayForm = null; Invalidate(); } private TextFormatFlags DisplayFormat { get { return TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix; } } private bool IsDropDownMessage(Message m) { // Left Mouse Button while form is not displaying if (m.Msg == WM.LBUTTONDOWN || m.Msg == WM.LBUTTONDBLCLK) { return true; } // F4 else if (m.Msg == WM.KEYDOWN) { Keys keyCombo = (Keys)(int)m.WParam & Keys.KeyCode; if (keyCombo == Keys.F4) return true; else return false; } // Alt+Arrow else if (m.Msg == WM.SYSKEYDOWN) { int wparam = m.WParam.ToInt32(); int lparam = m.LParam.ToInt32(); if ((wparam == 0x28 && (lparam == 0x21500001 || lparam == 0x20500001)) || //Alt+Down, Alt+NumDown (wparam == 0x26 && (lparam == 0x21480001 || lparam == 0x20480001))) //Alt+Up, Alt+NumUp { return true; } else { return false; } } // Not a drop-down message else { return false; } } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.toolTipCategories = new ToolTip2(this.components); // // CategoryDropDownControlM1 // this.Size = new System.Drawing.Size(138, 48); } #endregion #region High DPI Scaling protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { SaveScaleState(factor.Width, factor.Height); base.ScaleControl(factor, specified); } protected override void ScaleCore(float dx, float dy) { SaveScaleState(dx, dy); base.ScaleCore(dx, dy); } private void SaveScaleState(float dx, float dy) { scale = new PointF(scale.X * dx, scale.Y * dy); } private PointF scale = new PointF(1f, 1f); protected int ScaleX(int x) { return (int)(x * scale.X); } protected int ScaleY(int y) { return (int)(y * scale.Y); } #endregion private IWin32Window _parentFrame; private System.ComponentModel.IContainer components; private CategoryContext _categoryContext; private Bitmap _icon = null; public void OnClosed() { } public void OnPostClosed() { } } }