/OpenTween/Tween.cs
C# | 11593 lines | 9372 code | 1607 blank | 614 comment | 1889 complexity | 0454e54525a76c9903a6fc35f6efe3e2 MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause, Apache-2.0, GPL-3.0
Large files files are truncated, but you can click here to view the full file
- // OpenTween - Client of Twitter
- // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
- // (c) 2008-2011 Moz (@syo68k)
- // (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
- // (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
- // (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
- // (c) 2011 kim_upsilon (@kim_upsilon) <https://upsilo.net/~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 <http://www.gnu.org/licenses/>, or write to
- // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
- // Boston, MA 02110-1301, USA.
- #nullable enable
- //コンパイル後コマンド
- //"c:\Program Files\Microsoft.NET\SDK\v2.0\Bin\sgen.exe" /f /a:"$(TargetPath)"
- //"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\sgen.exe" /f /a:"$(TargetPath)"
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Drawing;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Media;
- using System.Net;
- using System.Net.Http;
- using System.Reflection;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using OpenTween.Api;
- using OpenTween.Api.DataModel;
- using OpenTween.Connection;
- using OpenTween.Models;
- using OpenTween.OpenTweenCustomControl;
- using OpenTween.Setting;
- using OpenTween.Thumbnail;
- namespace OpenTween
- {
- public partial class TweenMain : OTBaseForm
- {
- //各種設定
- /// <summary>画面サイズ</summary>
- private Size _mySize;
- /// <summary>画面位置</summary>
- private Point _myLoc;
- /// <summary>区切り位置</summary>
- private int _mySpDis;
- /// <summary>発言欄区切り位置</summary>
- private int _mySpDis2;
- /// <summary>プレビュー区切り位置</summary>
- private int _mySpDis3;
- /// <summary>アイコンサイズ</summary>
- /// <remarks>
- /// 現在は16、24、48の3種類。将来直接数字指定可能とする
- /// 注:24x24の場合に26と指定しているのはMSゴシック系フォントのための仕様
- /// </remarks>
- private int _iconSz;
- private bool _iconCol; // 1列表示の時true(48サイズのとき)
- //雑多なフラグ類
- private bool _initial; // true:起動時処理中
- private bool _initialLayout = true;
- private bool _ignoreConfigSave; // true:起動時処理中
- /// <summary>タブドラッグ中フラグ(DoDragDropを実行するかの判定用)</summary>
- private bool _tabDrag;
- private TabPage? _beforeSelectedTab; // タブが削除されたときに前回選択されていたときのタブを選択する為に保持
- private Point _tabMouseDownPoint;
- /// <summary>右クリックしたタブの名前(Tabコントロール機能不足対応)</summary>
- private string? _rclickTabName;
- private readonly object _syncObject = new object(); // ロック用
- private const string detailHtmlFormatHeaderMono =
- "<html><head><meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\">"
- + "<style type=\"text/css\"><!-- "
- + "body, p, pre {margin: 0;} "
- + "pre {font-family: \"%FONT_FAMILY%\", sans-serif; font-size: %FONT_SIZE%pt; background-color:rgb(%BG_COLOR%); word-wrap: break-word; color:rgb(%FONT_COLOR%);} "
- + "a:link, a:visited, a:active, a:hover {color:rgb(%LINK_COLOR%); } "
- + "img.emoji {width: 1em; height: 1em; margin: 0 .05em 0 .1em; vertical-align: -0.1em; border: none;} "
- + ".quote-tweet {border: 1px solid #ccc; margin: 1em; padding: 0.5em;} "
- + ".quote-tweet.reply {border-color: #f33;} "
- + ".quote-tweet-link {color: inherit !important; text-decoration: none;}"
- + "--></style>"
- + "</head><body><pre>";
- private const string detailHtmlFormatFooterMono = "</pre></body></html>";
- private const string detailHtmlFormatHeaderColor =
- "<html><head><meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\">"
- + "<style type=\"text/css\"><!-- "
- + "body, p, pre {margin: 0;} "
- + "body {font-family: \"%FONT_FAMILY%\", sans-serif; font-size: %FONT_SIZE%pt; background-color:rgb(%BG_COLOR%); margin: 0; word-wrap: break-word; color:rgb(%FONT_COLOR%);} "
- + "a:link, a:visited, a:active, a:hover {color:rgb(%LINK_COLOR%); } "
- + "img.emoji {width: 1em; height: 1em; margin: 0 .05em 0 .1em; vertical-align: -0.1em; border: none;} "
- + ".quote-tweet {border: 1px solid #ccc; margin: 1em; padding: 0.5em;} "
- + ".quote-tweet.reply {border-color: rgb(%BG_REPLY_COLOR%);} "
- + ".quote-tweet-link {color: inherit !important; text-decoration: none;}"
- + "--></style>"
- + "</head><body><p>";
- private const string detailHtmlFormatFooterColor = "</p></body></html>";
- private string detailHtmlFormatHeader = null!;
- private string detailHtmlFormatFooter = null!;
- private bool _myStatusError = false;
- private bool _myStatusOnline = false;
- private bool soundfileListup = false;
- private FormWindowState _formWindowState = FormWindowState.Normal; // フォームの状態保存用 通知領域からアイコンをクリックして復帰した際に使用する
- //twitter解析部
- private readonly TwitterApi twitterApi = new TwitterApi();
- private Twitter tw = null!;
- //Growl呼び出し部
- private readonly GrowlHelper gh = new GrowlHelper(ApplicationSettings.ApplicationName);
- //サブ画面インスタンス
- /// <summary>検索画面インスタンス</summary>
- internal SearchWordDialog SearchDialog = new SearchWordDialog();
- private readonly OpenURL UrlDialog = new OpenURL();
- /// <summary>@id補助</summary>
- public AtIdSupplement AtIdSupl = null!;
- /// <summary>Hashtag補助</summary>
- public AtIdSupplement HashSupl = null!;
- public HashtagManage HashMgr = null!;
- private EventViewerDialog evtDialog = null!;
- //表示フォント、色、アイコン
- /// <summary>未読用フォント</summary>
- private Font _fntUnread = null!;
- /// <summary>未読用文字色</summary>
- private Color _clUnread;
- /// <summary>既読用フォント</summary>
- private Font _fntReaded = null!;
- /// <summary>既読用文字色</summary>
- private Color _clReaded;
- /// <summary>Fav用文字色</summary>
- private Color _clFav;
- /// <summary>片思い用文字色</summary>
- private Color _clOWL;
- /// <summary>Retweet用文字色</summary>
- private Color _clRetweet;
- /// <summary>選択中の行用文字色</summary>
- private readonly Color _clHighLight = Color.FromKnownColor(KnownColor.HighlightText);
- /// <summary>発言詳細部用フォント</summary>
- private Font _fntDetail = null!;
- /// <summary>発言詳細部用色</summary>
- private Color _clDetail;
- /// <summary>発言詳細部用リンク文字色</summary>
- private Color _clDetailLink;
- /// <summary>発言詳細部用背景色</summary>
- private Color _clDetailBackcolor;
- /// <summary>自分の発言用背景色</summary>
- private Color _clSelf;
- /// <summary>自分宛返信用背景色</summary>
- private Color _clAtSelf;
- /// <summary>選択発言者の他の発言用背景色</summary>
- private Color _clTarget;
- /// <summary>選択発言中の返信先用背景色</summary>
- private Color _clAtTarget;
- /// <summary>選択発言者への返信発言用背景色</summary>
- private Color _clAtFromTarget;
- /// <summary>選択発言の唯一@先</summary>
- private Color _clAtTo;
- /// <summary>リスト部通常発言背景色</summary>
- private Color _clListBackcolor;
- /// <summary>入力欄背景色</summary>
- private Color _clInputBackcolor;
- /// <summary>入力欄文字色</summary>
- private Color _clInputFont;
- /// <summary>入力欄フォント</summary>
- private Font _fntInputFont = null!;
- /// <summary>アイコン画像リスト</summary>
- private ImageCache IconCache = null!;
- /// <summary>タスクトレイアイコン:通常時 (At.ico)</summary>
- private Icon NIconAt = null!;
- /// <summary>タスクトレイアイコン:通信エラー時 (AtRed.ico)</summary>
- private Icon NIconAtRed = null!;
- /// <summary>タスクトレイアイコン:オフライン時 (AtSmoke.ico)</summary>
- private Icon NIconAtSmoke = null!;
- /// <summary>タスクトレイアイコン:更新中 (Refresh.ico)</summary>
- private Icon[] NIconRefresh = new Icon[4];
- /// <summary>未読のあるタブ用アイコン (Tab.ico)</summary>
- private Icon TabIcon = null!;
- /// <summary>画面左上のアイコン (Main.ico)</summary>
- private Icon MainIcon = null!;
- private Icon ReplyIcon = null!;
- private Icon ReplyIconBlink = null!;
- private readonly ImageList _listViewImageList = new ImageList(); //ListViewItemの高さ変更用
- private PostClass? _anchorPost;
- private bool _anchorFlag; //true:関連発言移動中(関連移動以外のオペレーションをするとfalseへ。trueだとリスト背景色をアンカー発言選択中として描画)
- /// <summary>発言履歴</summary>
- private readonly List<StatusTextHistory> _history = new List<StatusTextHistory>();
- /// <summary>発言履歴カレントインデックス</summary>
- private int _hisIdx;
- //発言投稿時のAPI引数(発言編集時に設定。手書きreplyでは設定されない)
- /// <summary>リプライ先のステータスID・スクリーン名</summary>
- private (long StatusId, string ScreenName)? inReplyTo = null;
- //時速表示用
- private readonly List<DateTimeUtc> _postTimestamps = new List<DateTimeUtc>();
- private readonly List<DateTimeUtc> _favTimestamps = new List<DateTimeUtc>();
- // 以下DrawItem関連
- private readonly SolidBrush _brsHighLight = new SolidBrush(Color.FromKnownColor(KnownColor.Highlight));
- private SolidBrush _brsBackColorMine = null!;
- private SolidBrush _brsBackColorAt = null!;
- private SolidBrush _brsBackColorYou = null!;
- private SolidBrush _brsBackColorAtYou = null!;
- private SolidBrush _brsBackColorAtFromTarget = null!;
- private SolidBrush _brsBackColorAtTo = null!;
- private SolidBrush _brsBackColorNone = null!;
- /// <summary>Listにフォーカスないときの選択行の背景色</summary>
- private readonly SolidBrush _brsDeactiveSelection = new SolidBrush(Color.FromKnownColor(KnownColor.ButtonFace));
- private readonly StringFormat sfTab = new StringFormat();
- //////////////////////////////////////////////////////////////////////////////////////////////////////////
- private TabInformations _statuses = null!;
- /// <summary>
- /// 現在表示している発言一覧の <see cref="ListView"/> に対するキャッシュ
- /// </summary>
- /// <remarks>
- /// キャッシュクリアのために null が代入されることがあるため、
- /// 使用する場合には <see cref="_listItemCache"/> に対して直接メソッド等を呼び出さずに
- /// 一旦ローカル変数に代入してから参照すること。
- /// </remarks>
- private ListViewItemCache? _listItemCache = null;
- internal class ListViewItemCache
- {
- /// <summary>アイテムをキャッシュする対象の <see cref="ListView"/></summary>
- public ListView TargetList { get; set; } = null!;
- /// <summary>キャッシュする範囲の開始インデックス</summary>
- public int StartIndex { get; set; }
- /// <summary>キャッシュする範囲の終了インデックス</summary>
- public int EndIndex { get; set; }
- /// <summary>キャッシュされた範囲に対応する <see cref="ListViewItem"/> と <see cref="PostClass"/> の組</summary>
- public (ListViewItem, PostClass)[] Cache { get; set; } = null!;
- /// <summary>キャッシュされたアイテムの件数</summary>
- public int Count
- => this.EndIndex - this.StartIndex + 1;
- /// <summary>指定されたインデックスがキャッシュの範囲内であるか判定します</summary>
- /// <returns><paramref name="index"/> がキャッシュの範囲内であれば true、それ以外は false</returns>
- public bool Contains(int index)
- => index >= this.StartIndex && index <= this.EndIndex;
- /// <summary>指定されたインデックスの範囲が全てキャッシュの範囲内であるか判定します</summary>
- /// <returns><paramref name="rangeStart"/> から <paramref name="rangeEnd"/> の範囲が全てキャッシュの範囲内であれば true、それ以外は false</returns>
- public bool IsSupersetOf(int rangeStart, int rangeEnd)
- => rangeStart >= this.StartIndex && rangeEnd <= this.EndIndex;
- /// <summary>指定されたインデックスの <see cref="ListViewItem"/> と <see cref="PostClass"/> をキャッシュから取得することを試みます</summary>
- /// <returns>取得に成功すれば true、それ以外は false</returns>
- public bool TryGetValue(int index, [NotNullWhen(true)] out ListViewItem? item, [NotNullWhen(true)] out PostClass? post)
- {
- if (this.Contains(index))
- {
- (item, post) = this.Cache[index - this.StartIndex];
- return true;
- }
- else
- {
- item = null;
- post = null;
- return false;
- }
- }
- }
- private bool _isColumnChanged = false;
- private const int MAX_WORKER_THREADS = 20;
- private readonly SemaphoreSlim workerSemaphore = new SemaphoreSlim(MAX_WORKER_THREADS);
- private readonly CancellationTokenSource workerCts = new CancellationTokenSource();
- private readonly IProgress<string> workerProgress = null!;
- private int UnreadCounter = -1;
- private int UnreadAtCounter = -1;
- private readonly string[] ColumnOrgText = new string[9];
- private readonly string[] ColumnText = new string[9];
- private bool _DoFavRetweetFlags = false;
- //////////////////////////////////////////////////////////////////////////////////////////////////////////
- private readonly TimelineScheduler timelineScheduler = new TimelineScheduler();
- private ThrottlingTimer RefreshThrottlingTimer = null!;
- private ThrottlingTimer selectionDebouncer = null!;
- private ThrottlingTimer saveConfigDebouncer = null!;
- private string recommendedStatusFooter = null!;
- private bool urlMultibyteSplit = false;
- private bool preventSmsCommand = true;
- // URL短縮のUndo用
- private struct urlUndo
- {
- public string Before;
- public string After;
- }
- private List<urlUndo>? urlUndoBuffer = null;
- private readonly struct ReplyChain
- {
- public readonly long OriginalId;
- public readonly long InReplyToId;
- public readonly TabModel OriginalTab;
- public ReplyChain(long originalId, long inReplyToId, TabModel originalTab)
- {
- this.OriginalId = originalId;
- this.InReplyToId = inReplyToId;
- this.OriginalTab = originalTab;
- }
- }
- /// <summary>[, ]でのリプライ移動の履歴</summary>
- private Stack<ReplyChain>? replyChains;
- /// <summary>ポスト選択履歴</summary>
- private readonly Stack<(TabModel, PostClass?)> selectPostChains = new Stack<(TabModel, PostClass?)>();
- public TabModel CurrentTab
- => this._statuses.SelectedTab;
- public string CurrentTabName
- => this._statuses.SelectedTabName;
- public TabPage CurrentTabPage
- => this.ListTab.TabPages[this._statuses.Tabs.IndexOf(this.CurrentTabName)];
- public DetailsListView CurrentListView
- => (DetailsListView)this.CurrentTabPage.Tag;
- public PostClass? CurrentPost
- => this.CurrentTab.SelectedPost;
- /// <summary>検索処理タイプ</summary>
- internal enum SEARCHTYPE
- {
- DialogSearch,
- NextSearch,
- PrevSearch,
- }
- private class StatusTextHistory
- {
- public string status = "";
- public (long StatusId, string ScreenName)? inReplyTo = null;
- /// <summary>画像投稿サービス名</summary>
- public string imageService = "";
- public IMediaItem[]? mediaItems = null;
- public StatusTextHistory()
- {
- }
- public StatusTextHistory(string status, (long StatusId, string ScreenName)? inReplyTo)
- {
- this.status = status;
- this.inReplyTo = inReplyTo;
- }
- }
- private void TweenMain_Activated(object sender, EventArgs e)
- {
- //画面がアクティブになったら、発言欄の背景色戻す
- if (StatusText.Focused)
- {
- this.StatusText_Enter(this.StatusText, System.EventArgs.Empty);
- }
- }
- private bool disposed = false;
- /// <summary>
- /// 使用中のリソースをすべてクリーンアップします。
- /// </summary>
- /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (this.disposed)
- return;
- if (disposing)
- {
- this.components?.Dispose();
- //後始末
- SearchDialog.Dispose();
- UrlDialog.Dispose();
- NIconAt?.Dispose();
- NIconAtRed?.Dispose();
- NIconAtSmoke?.Dispose();
- foreach (var iconRefresh in this.NIconRefresh)
- {
- iconRefresh?.Dispose();
- }
- TabIcon?.Dispose();
- MainIcon?.Dispose();
- ReplyIcon?.Dispose();
- ReplyIconBlink?.Dispose();
- _listViewImageList.Dispose();
- _brsHighLight.Dispose();
- _brsBackColorMine?.Dispose();
- _brsBackColorAt?.Dispose();
- _brsBackColorYou?.Dispose();
- _brsBackColorAtYou?.Dispose();
- _brsBackColorAtFromTarget?.Dispose();
- _brsBackColorAtTo?.Dispose();
- _brsBackColorNone?.Dispose();
- _brsDeactiveSelection?.Dispose();
- //sf.Dispose();
- sfTab.Dispose();
- this.workerCts.Cancel();
- if (IconCache != null)
- {
- this.IconCache.CancelAsync();
- this.IconCache.Dispose();
- }
- this.thumbnailTokenSource?.Dispose();
- this.tw.Dispose();
- this.twitterApi.Dispose();
- this._hookGlobalHotkey.Dispose();
- }
- // 終了時にRemoveHandlerしておかないとメモリリークする
- // http://msdn.microsoft.com/ja-jp/library/microsoft.win32.systemevents.powermodechanged.aspx
- Microsoft.Win32.SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
- Microsoft.Win32.SystemEvents.TimeChanged -= SystemEvents_TimeChanged;
- this.disposed = true;
- }
- private void LoadIcons()
- {
- // Icons フォルダ以下のアイコンを読み込み(着せ替えアイコン対応)
- var iconsDir = Path.Combine(Application.StartupPath, "Icons");
- // ウィンドウ左上のアイコン
- var iconMain = this.LoadIcon(Path.Combine(iconsDir, "MIcon.ico"));
- // タブ見出し未読表示アイコン
- var iconTab = this.LoadIcon(Path.Combine(iconsDir, "Tab.ico"));
- // タスクトレイ: 通常時アイコン
- var iconAt = this.LoadIcon(Path.Combine(iconsDir, "At.ico"));
- // タスクトレイ: エラー時アイコン
- var iconAtRed = this.LoadIcon(Path.Combine(iconsDir, "AtRed.ico"));
- // タスクトレイ: オフライン時アイコン
- var iconAtSmoke = this.LoadIcon(Path.Combine(iconsDir, "AtSmoke.ico"));
- // タスクトレイ: Reply通知アイコン (最大2枚でアニメーション可能)
- var iconReply = this.LoadIcon(Path.Combine(iconsDir, "Reply.ico"));
- var iconReplyBlink = this.LoadIcon(Path.Combine(iconsDir, "ReplyBlink.ico"));
- // タスクトレイ: 更新中アイコン (最大4枚でアニメーション可能)
- var iconRefresh1 = this.LoadIcon(Path.Combine(iconsDir, "Refresh.ico"));
- var iconRefresh2 = this.LoadIcon(Path.Combine(iconsDir, "Refresh2.ico"));
- var iconRefresh3 = this.LoadIcon(Path.Combine(iconsDir, "Refresh3.ico"));
- var iconRefresh4 = this.LoadIcon(Path.Combine(iconsDir, "Refresh4.ico"));
- // 読み込んだアイコンを設定 (不足するアイコンはデフォルトのものを設定)
- this.MainIcon = iconMain ?? Properties.Resources.MIcon;
- this.TabIcon = iconTab ?? Properties.Resources.TabIcon;
- this.NIconAt = iconAt ?? iconMain ?? Properties.Resources.At;
- this.NIconAtRed = iconAtRed ?? Properties.Resources.AtRed;
- this.NIconAtSmoke = iconAtSmoke ?? Properties.Resources.AtSmoke;
- if (iconReply != null && iconReplyBlink != null)
- {
- this.ReplyIcon = iconReply;
- this.ReplyIconBlink = iconReplyBlink;
- }
- else
- {
- this.ReplyIcon = iconReply ?? iconReplyBlink ?? Properties.Resources.Reply;
- this.ReplyIconBlink = this.NIconAt;
- }
- if (iconRefresh1 == null)
- {
- this.NIconRefresh = new[] {
- Properties.Resources.Refresh, Properties.Resources.Refresh2,
- Properties.Resources.Refresh3, Properties.Resources.Refresh4,
- };
- }
- else if (iconRefresh2 == null)
- {
- this.NIconRefresh = new[] { iconRefresh1 };
- }
- else if (iconRefresh3 == null)
- {
- this.NIconRefresh = new[] { iconRefresh1, iconRefresh2 };
- }
- else if (iconRefresh4 == null)
- {
- this.NIconRefresh = new[] { iconRefresh1, iconRefresh2, iconRefresh3 };
- }
- else // iconRefresh1 から iconRefresh4 まで全て揃っている
- {
- this.NIconRefresh = new[] { iconRefresh1, iconRefresh2, iconRefresh3, iconRefresh4 };
- }
- }
- private Icon? LoadIcon(string filePath)
- {
- if (!File.Exists(filePath))
- return null;
- try
- {
- return new Icon(filePath);
- }
- catch (Exception)
- {
- return null;
- }
- }
- private void InitColumns(ListView list, bool startup)
- {
- this.InitColumnText();
- ColumnHeader[]? columns = null;
- try
- {
- if (this._iconCol)
- {
- columns = new[]
- {
- new ColumnHeader(), // アイコン
- new ColumnHeader(), // 本文
- };
- columns[0].Text = this.ColumnText[0];
- columns[1].Text = this.ColumnText[2];
- if (startup)
- {
- var widthScaleFactor = this.CurrentAutoScaleDimensions.Width / SettingManager.Local.ScaleDimension.Width;
- columns[0].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width1);
- columns[1].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width3);
- columns[0].DisplayIndex = 0;
- columns[1].DisplayIndex = 1;
- }
- else
- {
- var idx = 0;
- foreach (var curListColumn in this.CurrentListView.Columns.Cast<ColumnHeader>())
- {
- columns[idx].Width = curListColumn.Width;
- columns[idx].DisplayIndex = curListColumn.DisplayIndex;
- idx++;
- }
- }
- }
- else
- {
- columns = new[]
- {
- new ColumnHeader(), // アイコン
- new ColumnHeader(), // ニックネーム
- new ColumnHeader(), // 本文
- new ColumnHeader(), // 日付
- new ColumnHeader(), // ユーザID
- new ColumnHeader(), // 未読
- new ColumnHeader(), // マーク&プロテクト
- new ColumnHeader(), // ソース
- };
- foreach (var i in Enumerable.Range(0, columns.Length))
- columns[i].Text = this.ColumnText[i];
- if (startup)
- {
- var widthScaleFactor = this.CurrentAutoScaleDimensions.Width / SettingManager.Local.ScaleDimension.Width;
- columns[0].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width1);
- columns[1].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width2);
- columns[2].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width3);
- columns[3].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width4);
- columns[4].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width5);
- columns[5].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width6);
- columns[6].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width7);
- columns[7].Width = ScaleBy(widthScaleFactor, SettingManager.Local.Width8);
- var displayIndex = new[] {
- SettingManager.Local.DisplayIndex1, SettingManager.Local.DisplayIndex2,
- SettingManager.Local.DisplayIndex3, SettingManager.Local.DisplayIndex4,
- SettingManager.Local.DisplayIndex5, SettingManager.Local.DisplayIndex6,
- SettingManager.Local.DisplayIndex7, SettingManager.Local.DisplayIndex8
- };
- foreach (var i in Enumerable.Range(0, displayIndex.Length))
- {
- columns[i].DisplayIndex = displayIndex[i];
- }
- }
- else
- {
- var idx = 0;
- foreach (var curListColumn in this.CurrentListView.Columns.Cast<ColumnHeader>())
- {
- columns[idx].Width = curListColumn.Width;
- columns[idx].DisplayIndex = curListColumn.DisplayIndex;
- idx++;
- }
- }
- }
- list.Columns.AddRange(columns);
- columns = null;
- }
- finally
- {
- if (columns != null)
- {
- foreach (var column in columns)
- column?.Dispose();
- }
- }
- }
- private void InitColumnText()
- {
- ColumnText[0] = "";
- ColumnText[1] = Properties.Resources.AddNewTabText2;
- ColumnText[2] = Properties.Resources.AddNewTabText3;
- ColumnText[3] = Properties.Resources.AddNewTabText4_2;
- ColumnText[4] = Properties.Resources.AddNewTabText5;
- ColumnText[5] = "";
- ColumnText[6] = "";
- ColumnText[7] = "Source";
- ColumnOrgText[0] = "";
- ColumnOrgText[1] = Properties.Resources.AddNewTabText2;
- ColumnOrgText[2] = Properties.Resources.AddNewTabText3;
- ColumnOrgText[3] = Properties.Resources.AddNewTabText4_2;
- ColumnOrgText[4] = Properties.Resources.AddNewTabText5;
- ColumnOrgText[5] = "";
- ColumnOrgText[6] = "";
- ColumnOrgText[7] = "Source";
- var c = this._statuses.SortMode switch
- {
- ComparerMode.Nickname => 1, // ニックネーム
- ComparerMode.Data => 2, // 本文
- ComparerMode.Id => 3, // 時刻=発言Id
- ComparerMode.Name => 4, // 名前
- ComparerMode.Source => 7, // Source
- _ => 0,
- };
- if (_iconCol)
- {
- if (_statuses.SortOrder == SortOrder.Descending)
- {
- // U+25BE BLACK DOWN-POINTING SMALL TRIANGLE
- ColumnText[2] = ColumnOrgText[2] + "▾";
- }
- else
- {
- // U+25B4 BLACK UP-POINTING SMALL TRIANGLE
- ColumnText[2] = ColumnOrgText[2] + "▴";
- }
- }
- else
- {
- if (_statuses.SortOrder == SortOrder.Descending)
- {
- // U+25BE BLACK DOWN-POINTING SMALL TRIANGLE
- ColumnText[c] = ColumnOrgText[c] + "▾";
- }
- else
- {
- // U+25B4 BLACK UP-POINTING SMALL TRIANGLE
- ColumnText[c] = ColumnOrgText[c] + "▴";
- }
- }
- }
- private void InitializeTraceFrag()
- {
- #if DEBUG
- TraceOutToolStripMenuItem.Checked = true;
- MyCommon.TraceFlag = true;
- #endif
- if (!MyCommon.FileVersion.EndsWith("0", StringComparison.Ordinal))
- {
- TraceOutToolStripMenuItem.Checked = true;
- MyCommon.TraceFlag = true;
- }
- }
- private void TweenMain_Load(object sender, EventArgs e)
- {
- _ignoreConfigSave = true;
- this.Visible = false;
- if (MyApplication.StartupOptions.ContainsKey("d"))
- MyCommon.TraceFlag = true;
- InitializeTraceFrag();
- Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
- Regex.CacheSize = 100;
- //発言保持クラス
- _statuses = TabInformations.GetInstance();
- //アイコン設定
- LoadIcons();
- this.Icon = MainIcon; //メインフォーム(TweenMain)
- NotifyIcon1.Icon = NIconAt; //タスクトレイ
- TabImage.Images.Add(TabIcon); //タブ見出し
- //<<<<<<<<<設定関連>>>>>>>>>
- ////設定読み出し
- LoadConfig();
- // 現在の DPI と設定保存時の DPI との比を取得する
- var configScaleFactor = SettingManager.Local.GetConfigScaleFactor(this.CurrentAutoScaleDimensions);
- // UIフォント設定
- var fontUIGlobal = SettingManager.Local.FontUIGlobal;
- if (fontUIGlobal != null)
- {
- OTBaseForm.GlobalFont = fontUIGlobal;
- this.Font = fontUIGlobal;
- }
- //不正値チェック
- if (!MyApplication.StartupOptions.ContainsKey("nolimit"))
- {
- if (SettingManager.Common.TimelinePeriod < 15 && SettingManager.Common.TimelinePeriod > 0)
- SettingManager.Common.TimelinePeriod = 15;
- if (SettingManager.Common.ReplyPeriod < 15 && SettingManager.Common.ReplyPeriod > 0)
- SettingManager.Common.ReplyPeriod = 15;
- if (SettingManager.Common.DMPeriod < 15 && SettingManager.Common.DMPeriod > 0)
- SettingManager.Common.DMPeriod = 15;
- if (SettingManager.Common.PubSearchPeriod < 30 && SettingManager.Common.PubSearchPeriod > 0)
- SettingManager.Common.PubSearchPeriod = 30;
- if (SettingManager.Common.UserTimelinePeriod < 15 && SettingManager.Common.UserTimelinePeriod > 0)
- SettingManager.Common.UserTimelinePeriod = 15;
- if (SettingManager.Common.ListsPeriod < 15 && SettingManager.Common.ListsPeriod > 0)
- SettingManager.Common.ListsPeriod = 15;
- }
- if (!Twitter.VerifyApiResultCount(MyCommon.WORKERTYPE.Timeline, SettingManager.Common.CountApi))
- SettingManager.Common.CountApi = 60;
- if (!Twitter.VerifyApiResultCount(MyCommon.WORKERTYPE.Reply, SettingManager.Common.CountApiReply))
- SettingManager.Common.CountApiReply = 40;
- if (SettingManager.Common.MoreCountApi != 0 && !Twitter.VerifyMoreApiResultCount(SettingManager.Common.MoreCountApi))
- SettingManager.Common.MoreCountApi = 200;
- if (SettingManager.Common.FirstCountApi != 0 && !Twitter.VerifyFirstApiResultCount(SettingManager.Common.FirstCountApi))
- SettingManager.Common.FirstCountApi = 100;
- if (SettingManager.Common.FavoritesCountApi != 0 && !Twitter.VerifyApiResultCount(MyCommon.WORKERTYPE.Favorites, SettingManager.Common.FavoritesCountApi))
- SettingManager.Common.FavoritesCountApi = 40;
- if (SettingManager.Common.ListCountApi != 0 && !Twitter.VerifyApiResultCount(MyCommon.WORKERTYPE.List, SettingManager.Common.ListCountApi))
- SettingManager.Common.ListCountApi = 100;
- if (SettingManager.Common.SearchCountApi != 0 && !Twitter.VerifyApiResultCount(MyCommon.WORKERTYPE.PublicSearch, SettingManager.Common.SearchCountApi))
- SettingManager.Common.SearchCountApi = 100;
- if (SettingManager.Common.UserTimelineCountApi != 0 && !Twitter.VerifyApiResultCount(MyCommon.WORKERTYPE.UserTimeline, SettingManager.Common.UserTimelineCountApi))
- SettingManager.Common.UserTimelineCountApi = 20;
- //廃止サービスが選択されていた場合ux.nuへ読み替え
- if (SettingManager.Common.AutoShortUrlFirst < 0)
- SettingManager.Common.AutoShortUrlFirst = MyCommon.UrlConverter.Uxnu;
- TwitterApiConnection.RestApiHost = SettingManager.Common.TwitterApiHost;
- this.tw = new Twitter(this.twitterApi);
- //認証関連
- if (MyCommon.IsNullOrEmpty(SettingManager.Common.Token)) SettingManager.Common.UserName = "";
- tw.Initialize(SettingManager.Common.Token, SettingManager.Common.TokenSecret, SettingManager.Common.UserName, SettingManager.Common.UserId);
- _initial = true;
- Networking.Initialize();
- var saveRequired = false;
- var firstRun = false;
- //ユーザー名、パスワードが未設定なら設定画面を表示(初回起動時など)
- if (MyCommon.IsNullOrEmpty(tw.Username))
- {
- saveRequired = true;
- firstRun = true;
- //設定せずにキャンセルされたか、設定されたが依然ユーザー名が未設定ならプログラム終了
- if (ShowSettingDialog(showTaskbarIcon: true) != DialogResult.OK ||
- MyCommon.IsNullOrEmpty(tw.Username))
- {
- Application.Exit(); //強制終了
- return;
- }
- }
- //Twitter用通信クラス初期化
- Networking.DefaultTimeout = TimeSpan.FromSeconds(SettingManager.Common.DefaultTimeOut);
- Networking.UploadImageTimeout = TimeSpan.FromSeconds(SettingManager.Common.UploadImageTimeout);
- Networking.SetWebProxy(SettingManager.Local.ProxyType,
- SettingManager.Local.ProxyAddress, SettingManager.Local.ProxyPort,
- SettingManager.Local.ProxyUser, SettingManager.Local.ProxyPassword);
- Networking.ForceIPv4 = SettingManager.Common.ForceIPv4;
- TwitterApiConnection.RestApiHost = SettingManager.Common.TwitterApiHost;
- tw.RestrictFavCheck = SettingManager.Common.RestrictFavCheck;
- tw.ReadOwnPost = SettingManager.Common.ReadOwnPost;
- tw.TrackWord = SettingManager.Common.TrackWord;
- TrackToolStripMenuItem.Checked = !MyCommon.IsNullOrEmpty(tw.TrackWord);
- tw.AllAtReply = SettingManager.Common.AllAtReply;
- AllrepliesToolStripMenuItem.Checked = tw.AllAtReply;
- ShortUrl.Instance.DisableExpanding = !SettingManager.Common.TinyUrlResolve;
- ShortUrl.Instance.BitlyAccessToken = SettingManager.Common.BitlyAccessToken;
- ShortUrl.Instance.BitlyId = SettingManager.Common.BilyUser;
- ShortUrl.Instance.BitlyKey = SettingManager.Common.BitlyPwd;
- // アクセストークンが有効であるか確認する
- // ここが Twitter API への最初のアクセスになるようにすること
- try
- {
- this.tw.VerifyCredentials();
- }
- catch (WebApiException ex)
- {
- MessageBox.Show(this, string.Format(Properties.Resources.StartupAuthError_Text, ex.Message),
- ApplicationSettings.ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
- }
- //サムネイル関連の初期化
- //プロキシ設定等の通信まわりの初期化が済んでから処理する
- ThumbnailGenerator.InitializeGenerator();
- var imgazyobizinet = ThumbnailGenerator.ImgAzyobuziNetInstance;
- imgazyobizinet.Enabled = SettingManager.Common.EnableImgAzyobuziNet;
- imgazyobizinet.DisabledInDM = SettingManager.Common.ImgAzyobuziNetDisabledInDM;
- Thumbnail.Services.TonTwitterCom.GetApiConnection = () => this.twitterApi.Connection;
- //画像投稿サービス
- ImageSelector.Initialize(tw, this.tw.Configuration, SettingManager.Common.UseImageServiceName, SettingManager.Common.UseImageService);
- //ハッシュタグ/@id関連
- AtIdSupl = new AtIdSupplement(SettingManager.AtIdList.AtIdList, "@");
- HashSupl = new AtIdSupplement(SettingManager.Common.HashTags, "#");
- HashMgr = new HashtagManage(HashSupl,
- SettingManager.Common.HashTags.ToArray(),
- SettingManager.Common.HashSelected,
- SettingManager.Common.HashIsPermanent,
- SettingManager.Common.HashIsHead,
- SettingManager.Common.HashIsNotAddToAtReply);
- if (!MyCommon.IsNullOrEmpty(HashMgr.UseHash) && HashMgr.IsPermanent) HashStripSplitButton.Text = HashMgr.UseHash;
- //アイコンリスト作成
- this.IconCache = new ImageCache();
- this.tweetDetailsView.IconCache = this.IconCache;
- //フォント&文字色&背景色保持
- _fntUnread = SettingManager.Local.FontUnread;
- _clUnread = SettingManager.Local.ColorUnread;
- _fntReaded = SettingManager.Local.FontRead;
- _clReaded = SettingManager.Local.ColorRead;
- _clFav = SettingManager.Local.ColorFav;
- _clOWL = SettingManager.Local.ColorOWL;
- _clRetweet = SettingManager.Local.ColorRetweet;
- _fntDetail = SettingManager.Local.FontDetail;
- _clDetail = SettingManager.Local.ColorDetail;
- _clDetailLink = SettingManager.Local.ColorDetailLink;
- _clDetailBackcolor = SettingManager.Local.ColorDetailBackcolor;
- _clSelf = SettingManager.Local.ColorSelf;
- _clAtSelf = SettingManager.Local.ColorAtSelf;
- _clTarget = SettingManager.Local.ColorTarget;
- _clAtTarget = SettingManager.Local.ColorAtTarget;
- _clAtFromTarget = SettingManager.Local.ColorAtFromTarget;
- _clAtTo = SettingManager.Local.ColorAtTo;
- _clListBackcolor = SettingManager.Local.ColorListBackcolor;
- _clInputBackcolor = SettingManager.Local.ColorInputBackcolor;
- _clInputFont = SettingManager.Local.ColorInputFont;
- _fntInputFont = SettingManager.Local.FontInputFont;
- _brsBackColorMine = new SolidBrush(_clSelf);
- _brsBackColorAt = new SolidBrush(_clAtSelf);
- _brsBackColorYou = new SolidBrush(_clTarget);
- _brsBackColorAtYou = new SolidBrush(_clAtTarget);
- _brsBackColorAtFromTarget = new SolidBrush(_clAtFromTarget);
- _brsBackColorAtTo = new SolidBrush(_clAtTo);
- _brsBackColorNone = new SolidBrush(_clListBackcolor);
- // StringFormatオブジェクトへの事前設定
- sfTab.Alignment = StringAlignment.Center;
- sfTab.LineAlignment = StringAlignment.Center;
- InitDetailHtmlFormat();
- this.recommendedStatusFooter = " [TWNv" + Regex.Replace(MyCommon.FileVersion.Replace(".", ""), "^0*", "") + "]";
- _history.Add(new StatusTextHistory());
- _hisIdx = 0;
- this.inReplyTo = null;
- //各種ダイアログ設定
- SearchDialog.Owner = this;
- UrlDialog.Owner = this;
- //新着バルーン通知のチェック状態設定
- NewPostPopMenuItem.Checked = SettingManager.Common.NewAllPop;
- this.NotifyFileMenuItem.Checked = NewPostPopMenuItem.Checked;
- //新着取得時のリストスクロールをするか。trueならスクロールしない
- ListLockMenuItem.Checked = SettingManager.Common.ListLock;
- this.LockListFileMenuItem.Checked = SettingManager.Common.ListLock;
- //サウンド再生(タブ別設定より優先)
- this.PlaySoundMenuItem.Checked = SettingManager.Common.PlaySound;
- this.PlaySoundFileMenuItem.Checked = SettingManager.Common.PlaySound;
- //ウィンドウ設定
- this.ClientSize = ScaleBy(configScaleFactor, SettingManager.Local.FormSize);
- _mySize = this.ClientSize; // サイズ保持(最小化・最大化されたまま終了した場合の対応用)
- _myLoc = SettingManager.Local.FormLocation;
- //タイトルバー領域
- if (this.WindowState != FormWindowState.Minimized)
- {
- var tbarRect = new Rectangle(this._myLoc, new Size(_mySize.Width, SystemInformation.CaptionHeight));
- var outOfScreen = true;
- if (Screen.AllScreens.Length == 1) //ハングするとの報告
- {
- foreach (var scr in Screen.AllScreens)
- {
- if (!Rectangle.Intersect(tbarRect, scr.Bounds).IsEmpty)
- {
- outOfScreen = false;
- break;
- }
- }
- if (outOfScreen)
- this._myLoc = new Point(0, 0);
- }
- this.DesktopLocation = this._myLoc;
- }
- this.TopMost = SettingManager.Common.AlwaysTop;
- _mySpDis = ScaleBy(configScaleFactor.Height, SettingManager.Local.SplitterDistance);
- _mySpDis2 = ScaleBy(configScaleFactor.Height, SettingManager.Local.StatusTextHeight);
- if (SettingManager.Local.PreviewDistance == -1)
- {
- _mySpDis3 = _mySize.Width - ScaleBy(this.CurrentScaleFactor.Width, 150);
- if (_mySpDis3 < 1) _mySpDis3 = ScaleBy(this.CurrentScaleFactor.Width, 50);
- SettingManager.Local.PreviewDistance = _mySpDis3;
- }
- else
- {
- _mySpDis3 = ScaleBy(configScaleFactor.Width, SettingManager.Local.PreviewDistance);
- }
- this.PlaySoundMenuItem.Checked = SettingManager.Common.PlaySound;
- this.PlaySoundFileMenuItem.Checked = SettingManager.Common.PlaySound;
- //入力欄
- StatusText.Font = _fntInputFont;
- StatusText.ForeColor = _clInputFont;
- // SplitContainer2.Panel2MinSize を一行表示の入力欄の高さに合わせる (MS UI Gothic 12pt (96dpi) の場合は 19px)
- this.StatusText.Multiline = false; // SettingManager.Local.StatusMultiline の設定は後で反映される
- this.SplitContainer2.Panel2MinSize = this.StatusText.Height;
- // 必要であれば、発言一覧と発言詳細部・入力欄の上下を入れ替える
- SplitContainer1.IsPanelInverted = !SettingManager.Common.StatusAreaAtBottom;
- //全新着通知のチェック状態により、Reply&DMの新着通知有効無効切り替え(タブ別設定にするため削除予定)
- if (SettingManager.Common.UnreadManage == false)
- {
- ReadedStripMenuItem.Enabled = false;
- UnreadStripMenuItem.Enabled = false;
- }
- //リンク先URL表示部の初期化(画面左下)
- StatusLabelUrl.Text = "";
- //状態表示部の初期化(画面右下)
- StatusLabel.Text = "";
- StatusLabel.AutoToolTip = false;
- StatusLabel.ToolTipText = "";
- //文字カウンタ初期化
- lblLen.Text = this.GetRestStatusCount(this.FormatStatusTextExtended("")).ToString();
- this.JumpReadOpMenuItem.ShortcutKeyDisplayString = "Space";
- CopySTOTMenuItem.ShortcutKeyDisplayString = "Ctrl+C";
- CopyURLMenuItem.ShortcutKeyDisplayString = "Ctrl+Shift+C";
- CopyUserIdStripMenuItem.ShortcutKeyDisplayString = "Shift+Alt+C";
- // SourceLinkLabel のテキストが SplitContainer2.Panel2.AccessibleName にセットされるのを防ぐ
- // (タブオーダー順で SourceLinkLabel の次にある PostBrowser が TabStop = false となっているため、
- // さらに次のコントロールである SplitContainer2.Panel2 の AccessibleName がデフォルトで SourceLinkLabel のテキストになってしまう)
- this.SplitContainer2.Panel2.AccessibleName = "";
- ////////////////////////////////////////////////////////////////////////////////
- var sortOrder = (SortOrder)SettingManager.Common.SortOrder;
- var mode = ComparerMode.Id;
- switch (SettingManager.Common.SortColumn)
- {
- case 0: //0:アイコン,5:未読マーク,6:プロテクト・フィルターマーク
- case 5:
- case 6:
- //ソートしない
- mode = ComparerMode.Id; //Idソートに読み替え
- break;
- case 1: //ニックネーム
- mode = ComparerMode.Nickname;
- break;
- case 2: //本文
- mode = ComparerMode.Data;
- break;
- case 3: //時刻=発言Id
- mode = ComparerMode.Id;
- break;
- …
Large files files are truncated, but you can click here to view the full file