// Copyright (C) 2015, The Duplicati Team // http://www.duplicati.com, info@duplicati.com // // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 2.1 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Collections.Generic; using System.Windows.Forms; using Duplicati.Server.Serialization; using Duplicati.Server.Serialization.Interface; namespace Duplicati.GUI.TrayIcon.Windows { //We use a separate class to start the runner to avoid attempts to load WinForms from Program.cs public class WinFormsRunner : TrayIconBase { private class MenuItemWrapper : IMenuItem { private readonly ToolStripItem m_menu; private readonly Action m_callback; public ToolStripItem MenuItem { get { return m_menu; } } public MenuItemWrapper(string text, MenuIcons icon, Action callback, IList subitems) { if (text == "-") m_menu = new ToolStripSeparator(); else { m_menu = new ToolStripMenuItem(text); if (icon != MenuIcons.None) m_menu.Image = GetIcon(icon); if (callback != null) { m_callback = callback; m_menu.Click += OnMenuClick; } if (subitems != null && subitems.Count > 0) foreach(var itm in subitems) ((ToolStripMenuItem)m_menu).DropDownItems.Add(((MenuItemWrapper)itm).MenuItem); } } private void OnMenuClick (object sender, EventArgs e) { m_callback(); } private System.Drawing.Image GetIcon(MenuIcons icon) { switch(icon) { case MenuIcons.Pause: return ImageLoader.Pause; case MenuIcons.Quit: return ImageLoader.CloseMenuIcon; case MenuIcons.Resume: return ImageLoader.Play; case MenuIcons.Status: return ImageLoader.StatusMenuIcon; case MenuIcons.None: default: return null; } } #region IMenuItem implementation public void SetText(string text) { m_menu.Text = text; } public void SetIcon(MenuIcons icon) { m_menu.Image = GetIcon(icon); } public void SetDefault(bool value) { m_menu.Font = new System.Drawing.Font(m_menu.Font, System.Drawing.FontStyle.Bold); } #endregion } private Form m_handleProvider; private NotifyIcon m_trayIcon; public override void Init (string[] args) { //We need this ugly hack to get a handle that we can call Invoke on, // and sadly the TrayIcon does not expose one, and forcing the context menu // to create one causes weird "lost clicks" m_handleProvider = new Form() { FormBorderStyle = FormBorderStyle.None, Width = 10, Height = 10, Top = 0, Left = 0 }; m_handleProvider.Show(); m_handleProvider.Hide(); m_trayIcon = new NotifyIcon(); m_trayIcon.DoubleClick += new EventHandler(m_trayIcon_DoubleClick); m_trayIcon.Click += new EventHandler(m_trayIcon_Click); m_trayIcon.BalloonTipClicked += m_BalloonTipClicked; m_trayIcon.Text = Duplicati.Library.AutoUpdater.AutoUpdateSettings.AppName; base.Init(args); } protected override void UpdateUIState(Action action) { m_handleProvider.Invoke(action); } private void m_trayIcon_Click(object sender, EventArgs e) { if (m_onSingleClick != null) m_onSingleClick(); else if (m_trayIcon != null && m_trayIcon.ContextMenuStrip != null) { // Show context menu on left-click as we have nothing happening otherwise try { typeof(NotifyIcon).GetMethod("ShowContextMenu", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).Invoke(m_trayIcon, null); } catch { } } } private void m_trayIcon_DoubleClick(object sender, EventArgs e) { if (m_onDoubleClick != null) m_onDoubleClick(); } private void m_BalloonTipClicked(object sender, EventArgs e) { m_onNotificationClick?.Invoke(); } protected override void RegisterStatusUpdateCallback () { Program.Connection.OnStatusUpdated += delegate(IServerStatus status) { m_handleProvider.Invoke(new Action(OnStatusUpdated), status); }; } #region implemented abstract members of Duplicati.GUI.TrayIcon.TrayIconBase protected override void Run (string[] args) { m_trayIcon.Visible = true; Application.Run(); } protected override IMenuItem CreateMenuItem (string text, MenuIcons icon, Action callback, IList subitems) { return new MenuItemWrapper(text, icon, callback, subitems); } protected override void NotifyUser(string title, string message, NotificationType type) { var icon = ToolTipIcon.None; switch (type) { case NotificationType.Information: icon = ToolTipIcon.Info; break; case NotificationType.Warning: icon = ToolTipIcon.Warning; break; case NotificationType.Error: icon = ToolTipIcon.Error; break; } m_trayIcon.ShowBalloonTip((int)TimeSpan.FromSeconds(60).TotalMilliseconds, title, message, icon); } protected override void Exit () { Application.Exit(); } protected override void SetIcon(TrayIcons icon) { switch (icon) { case TrayIcons.IdleError: m_trayIcon.Icon = ImageLoader.LoadIcon(ImageLoader.ErrorIcon, System.Windows.Forms.SystemInformation.SmallIconSize); break; case TrayIcons.IdleWarning: m_trayIcon.Icon = ImageLoader.LoadIcon(ImageLoader.WarningIcon, System.Windows.Forms.SystemInformation.SmallIconSize); break; case TrayIcons.Paused: m_trayIcon.Icon = ImageLoader.LoadIcon(ImageLoader.PauseIcon, System.Windows.Forms.SystemInformation.SmallIconSize); break; case TrayIcons.PausedError: m_trayIcon.Icon = ImageLoader.LoadIcon(ImageLoader.PauseIcon, System.Windows.Forms.SystemInformation.SmallIconSize); break; case TrayIcons.Running: m_trayIcon.Icon = ImageLoader.LoadIcon(ImageLoader.WorkingIcon, System.Windows.Forms.SystemInformation.SmallIconSize); break; case TrayIcons.RunningError: m_trayIcon.Icon = ImageLoader.LoadIcon(ImageLoader.WorkingIcon, System.Windows.Forms.SystemInformation.SmallIconSize); break; case TrayIcons.Idle: default: m_trayIcon.Icon = ImageLoader.LoadIcon(ImageLoader.NormalIcon, System.Windows.Forms.SystemInformation.SmallIconSize); break; } } protected override void SetMenu (System.Collections.Generic.IEnumerable items) { m_trayIcon.ContextMenuStrip = new ContextMenuStrip(); foreach(var itm in items) m_trayIcon.ContextMenuStrip.Items.Add(((MenuItemWrapper)itm).MenuItem); } public override void Dispose () { if (m_handleProvider != null) { m_handleProvider.Dispose(); m_handleProvider = null; } if (m_trayIcon != null) { m_trayIcon.Visible = false; m_trayIcon.Dispose(); m_trayIcon = null; } } #endregion /*public override IBrowserWindow ShowUrlInWindow(string url) { var v = new WindowsBrowser(url); v.Show(); return v; }*/ } }