// OpenTween - Client of Twitter // Copyright (c) 2013 kim_upsilon (@kim_upsilon) // All rights reserved. // // This file is part of OpenTween. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3 of the License, or (at your option) // any later version. // // 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 General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program. If not, see , or write to // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, // Boston, MA 02110-1301, USA. #nullable enable using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Windows.Forms.Design; using OpenTween.Api; namespace OpenTween { /// /// API 実行回数制限に到達するまでの目安を表示する ToolStripItem /// [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.All)] public class ToolStripAPIGauge : ToolStripStatusLabel { public ToolStripAPIGauge() { this.UpdateText(); this.DisplayStyle = ToolStripItemDisplayStyle.Text; } /// /// ゲージに表示される横棒グラフの幅 /// [DefaultValue(8)] [RefreshProperties(RefreshProperties.Repaint)] public int GaugeHeight { get => this.gaugeHeight; set { this.gaugeHeight = value; this.UpdateGaugeBounds(); this.Invalidate(); } } private int gaugeHeight = 8; /// /// API 実行回数制限の値 /// [Browsable(false)] public ApiLimit? ApiLimit { get => this.apiLimit; private set { this.apiLimit = value; this.UpdateRemainMinutes(); this.UpdateText(); this.UpdateGaugeBounds(); this.Invalidate(); } } private ApiLimit? apiLimit = null; /// /// API エンドポイント名 /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string? ApiEndpoint { get => this.apiEndpoint; set { if (MyCommon.IsNullOrEmpty(value)) { // リセット this.apiEndpoint = null; this.ApiLimit = null; return; } var apiLimit = MyCommon.TwitterApiInfo.AccessLimit[value]; if (this.apiEndpoint != value) { // ApiEndpointが変更されているので更新する this.apiEndpoint = value; this.ApiLimit = apiLimit; } else { // ApiLimitが変更されていれば更新する if (this.apiLimit == null || !this.apiLimit.Equals(apiLimit)) { this.ApiLimit = apiLimit; } } } } private string? apiEndpoint = null; [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public new string Text { get => base.Text; set => base.Text = value; } [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public new string ToolTipText { get => base.ToolTipText; set => base.ToolTipText = value; } [DefaultValue(ToolStripItemDisplayStyle.Text)] [RefreshProperties(RefreshProperties.Repaint)] public new ToolStripItemDisplayStyle DisplayStyle { get => base.DisplayStyle; set => base.DisplayStyle = value; } protected double remainMinutes = -1; protected virtual void UpdateRemainMinutes() { if (this.apiLimit != null) this.remainMinutes = (this.apiLimit.AccessLimitResetDate - DateTimeUtc.Now).TotalMinutes; else this.remainMinutes = -1; } protected override void OnPaint(PaintEventArgs e) { var g = e.Graphics; if (this.ApiGaugeBounds != Rectangle.Empty) g.FillRectangle(Brushes.LightBlue, this.ApiGaugeBounds); if (this.TimeGaugeBounds != Rectangle.Empty) g.FillRectangle(Brushes.LightPink, this.TimeGaugeBounds); base.OnPaint(e); } #region from Tween v1.1.0.0 // The code in this region block is based on code written by the following authors: // (C) 2010 anis774 (@anis774) // (C) 2010 Moz (@syo68k) internal Rectangle ApiGaugeBounds = Rectangle.Empty; internal Rectangle TimeGaugeBounds = Rectangle.Empty; protected virtual void UpdateGaugeBounds() { if (this.apiLimit == null || this.gaugeHeight < 1) { this.ApiGaugeBounds = Rectangle.Empty; this.TimeGaugeBounds = Rectangle.Empty; return; } var apiGaugeValue = (double)this.apiLimit.AccessLimitRemain / this.apiLimit.AccessLimitCount; this.ApiGaugeBounds = new Rectangle( 0, (this.Height - this.gaugeHeight * 2) / 2, (int)(this.Width * apiGaugeValue), this.gaugeHeight ); var timeGaugeValue = this.remainMinutes >= 15 ? 1.00 : this.remainMinutes / 15; this.TimeGaugeBounds = new Rectangle( 0, this.ApiGaugeBounds.Top + this.gaugeHeight, (int)(this.Width * timeGaugeValue), this.gaugeHeight ); } protected virtual void UpdateText() { string remainCountText; string maxCountText; string minuteText; if (this.apiLimit == null || this.remainMinutes < 0) { remainCountText = "???"; maxCountText = "???"; minuteText = "???"; } else { remainCountText = this.apiLimit.AccessLimitRemain.ToString(); maxCountText = this.apiLimit.AccessLimitCount.ToString(); minuteText = Math.Ceiling(this.remainMinutes).ToString(); } var endpointText = MyCommon.IsNullOrEmpty(this.apiEndpoint) ? "unknown" : this.apiEndpoint; var textFormat = "API {0}/{1}"; this.Text = string.Format(textFormat, remainCountText, maxCountText); var toolTipTextFormat = "API rest {0} {1}/{2}" + Environment.NewLine + "(reset after {3} minutes)"; this.ToolTipText = string.Format(toolTipTextFormat, endpointText, remainCountText, maxCountText, minuteText); } #endregion } }