OSDN Git Service

C# 8.0 のnull許容参照型を有効化
[opentween/open-tween.git] / OpenTween / HashtagManage.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 //           (c) 2008-2011 Moz (@syo68k)
4 //           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 //           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 //           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 //           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
8 // All rights reserved.
9 // 
10 // This file is part of OpenTween.
11 // 
12 // This program is free software; you can redistribute it and/or modify it
13 // under the terms of the GNU General public License as published by the Free
14 // Software Foundation; either version 3 of the License, or (at your option)
15 // any later version.
16 // 
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License
20 // for more details. 
21 // 
22 // You should have received a copy of the GNU General public License along
23 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25 // Boston, MA 02110-1301, USA.
26
27 #nullable enable
28
29 using System;
30 using System.Collections.Generic;
31 using System.ComponentModel;
32 using System.Data;
33 using System.Drawing;
34 using System.Globalization;
35 using System.Linq;
36 using System.Text;
37 using System.Windows.Forms;
38
39 namespace OpenTween
40 {
41     public partial class HashtagManage : OTBaseForm
42     {
43         public string UseHash { get; private set; } = "";
44         public bool IsPermanent { get; private set; } = false;
45         public bool IsHead { get; private set; } = false;
46         public bool IsNotAddToAtReply { get; private set; } = true;
47
48         /// <summary>
49         /// エラー時にダイアログを表示させない (ユニットテストなどで使用)
50         /// </summary>
51         public bool RunSilent { get; set; }
52
53         //入力補助画面
54         private readonly AtIdSupplement _hashSupl;
55
56         //編集モード
57         private bool _isAdd = false;
58
59         private void ChangeMode(bool isEdit)
60         {
61             this.GroupHashtag.Enabled = !isEdit;
62             this.GroupDetail.Enabled = isEdit;
63             this.TableLayoutButtons.Enabled = !isEdit;
64             if (isEdit)
65                 this.UseHashText.Focus();
66             else
67                 this.HistoryHashList.Focus();
68         }
69
70         private void Cancel_Button_Click(object sender, EventArgs e)
71         {
72             this.DialogResult = DialogResult.Cancel;
73             this.Close();
74         }
75
76         private void AddButton_Click(object sender, EventArgs e)
77         {
78             this.UseHashText.Text = "";
79             ChangeMode(true);
80             _isAdd = true;
81         }
82
83         private void EditButton_Click(object sender, EventArgs e)
84         {
85             if (this.HistoryHashList.SelectedIndices.Count == 0) return;
86             this.UseHashText.Text = this.HistoryHashList.SelectedItems[0].ToString();
87             ChangeMode(true);
88             _isAdd = false;
89         }
90
91         private void DeleteButton_Click(object sender, EventArgs e)
92         {
93             if (this.HistoryHashList.SelectedIndices.Count == 0) return;
94             if (!this.RunSilent &&
95                 MessageBox.Show(Properties.Resources.DeleteHashtagsMessage1,
96                                 "Delete Hashtags",
97                                 MessageBoxButtons.OKCancel,
98                                 MessageBoxIcon.Question) == DialogResult.Cancel)
99             {
100                 return;
101             }
102
103             // 削除によってインデックス番号が変わらないように逆順に処理する
104             var selectedIndices = this.HistoryHashList.SelectedIndices.Cast<int>()
105                 .OrderByDescending(x => x).ToArray();
106
107             foreach (var idx in selectedIndices)
108             {
109                 if (UseHashText.Text == HistoryHashList.Items[idx].ToString()) UseHashText.Text = "";
110                 HistoryHashList.Items.RemoveAt(idx);
111             }
112             if (HistoryHashList.Items.Count > 0)
113             {
114                 HistoryHashList.SelectedIndex = 0;
115             }
116         }
117
118         private void UnSelectButton_Click(object sender, EventArgs e)
119         {
120             do
121             {
122                 HistoryHashList.SelectedIndices.Clear();
123             } while (HistoryHashList.SelectedIndices.Count > 0);
124         }
125
126         private int GetIndexOf(ListBox.ObjectCollection list, string value)
127         {
128             if (string.IsNullOrEmpty(value)) return -1;
129
130             var idx = 0;
131
132             foreach (var l in list)
133             {
134                 var src = (string)l;
135                 if (string.IsNullOrEmpty(src))
136                 {
137                     idx += 1;
138                     continue;
139                 }
140                 if (string.Compare(src, value, StringComparison.OrdinalIgnoreCase) == 0)
141                 {
142                     return idx;
143                 }
144                 idx += 1;
145             }
146
147             // Not Found
148             return -1;
149         }
150
151         public void AddHashToHistory(string hash, bool isIgnorePermanent)
152         {
153             hash = hash.Trim();
154             if (!string.IsNullOrEmpty(hash))
155             {
156                 if (isIgnorePermanent || !this.IsPermanent)
157                 {
158                     //無条件に先頭に挿入
159                     var idx = GetIndexOf(HistoryHashList.Items, hash);
160
161                     if (idx != -1) HistoryHashList.Items.RemoveAt(idx);
162                     HistoryHashList.Items.Insert(0, hash);
163                 }
164                 else
165                 {
166                     //固定されていたら2行目に挿入
167                     var idx = GetIndexOf(HistoryHashList.Items, hash);
168                     if (this.IsPermanent)
169                     {
170                         if (idx > 0)
171                         {
172                             //重複アイテムが2行目以降にあれば2行目へ
173                             HistoryHashList.Items.RemoveAt(idx);
174                             HistoryHashList.Items.Insert(1, hash);
175                         }
176                         else if (idx == -1)
177                         {
178                             //重複アイテムなし
179                             if (HistoryHashList.Items.Count == 0)
180                             {
181                                 //リストが空なら追加
182                                 HistoryHashList.Items.Add(hash);
183                             }
184                             else
185                             {
186                                 //リストにアイテムがあれば2行目へ
187                                 HistoryHashList.Items.Insert(1, hash);
188                             }
189                         }
190                     }
191                 }
192             }
193         }
194
195         private void HashtagManage_Shown(object sender, EventArgs e)
196         {
197             //オプション
198             this.CheckPermanent.Checked = this.IsPermanent;
199             this.RadioHead.Checked = this.IsHead;
200             this.RadioLast.Checked = !this.IsHead;
201             //リスト選択
202             if (this.HistoryHashList.Items.Contains(this.UseHash))
203             {
204                 this.HistoryHashList.SelectedItem = this.UseHash;
205             }
206             else
207             {
208                 if (this.HistoryHashList.Items.Count > 0)
209                     this.HistoryHashList.SelectedIndex = 0;
210             }
211             this.ChangeMode(false);
212         }
213
214         public HashtagManage(AtIdSupplement hashSuplForm, string[] history, string permanentHash, bool IsPermanent, bool IsHead, bool IsNotAddToAtReply)
215         {
216             // この呼び出しは、Windows フォーム デザイナで必要です。
217             InitializeComponent();
218
219             // InitializeComponent() 呼び出しの後で初期化を追加します。
220
221             _hashSupl = hashSuplForm;
222             HistoryHashList.Items.AddRange(history);
223             this.UseHash = permanentHash;
224             this.IsPermanent = IsPermanent;
225             this.IsHead = IsHead;
226             this.IsNotAddToAtReply = IsNotAddToAtReply;
227         }
228
229         private void UseHashText_KeyPress(object sender, KeyPressEventArgs e)
230         {
231             if (e.KeyChar == '#')
232             {
233                 _hashSupl.ShowDialog();
234                 if (!string.IsNullOrEmpty(_hashSupl.inputText))
235                 {
236                     var fHalf = "";
237                     var eHalf = "";
238                     var selStart = UseHashText.SelectionStart;
239                     if (selStart > 0)
240                     {
241                         fHalf = UseHashText.Text.Substring(0, selStart);
242                     }
243                     if (selStart < UseHashText.Text.Length)
244                     {
245                         eHalf = UseHashText.Text.Substring(selStart);
246                     }
247                     UseHashText.Text = fHalf + _hashSupl.inputText + eHalf;
248                     UseHashText.SelectionStart = selStart + _hashSupl.inputText.Length;
249                 }
250                 e.Handled = true;
251             }
252         }
253
254         private void HistoryHashList_DoubleClick(object sender, EventArgs e)
255             => this.OK_Button_Click(this.OK_Button, EventArgs.Empty);
256
257         public void ToggleHash()
258         {
259             if (string.IsNullOrEmpty(this.UseHash))
260             {
261                 if (this.HistoryHashList.Items.Count > 0)
262                     this.UseHash = this.HistoryHashList.Items[0].ToString();
263             }
264             else
265             {
266                 this.UseHash = "";
267             }
268         }
269
270         public List<string> HashHistories
271         {
272             get
273             {
274                 var hash = new List<string>();
275                 foreach (string item in HistoryHashList.Items)
276                 {
277                     hash.Add(item);
278                 }
279                 return hash;
280             }
281         }
282
283         public void ClearHashtag()
284             => this.UseHash = "";
285
286         public void SetPermanentHash(string hash)
287         {
288             //固定ハッシュタグの変更
289             this.UseHash = hash.Trim();
290             this.AddHashToHistory(UseHash, false);
291             this.IsPermanent = true;
292         }
293
294         private void PermOK_Button_Click(object sender, EventArgs e)
295         {
296             //ハッシュタグの整形
297             var hashStr = UseHashText.Text;
298             if (!this.AdjustHashtags(ref hashStr, !this.RunSilent)) return;
299
300             UseHashText.Text = hashStr;
301             if (!this._isAdd && this.HistoryHashList.SelectedIndices.Count > 0)
302             {
303                 var idx = this.HistoryHashList.SelectedIndices[0];
304                 this.HistoryHashList.Items.RemoveAt(idx);
305                 do
306                 {
307                     this.HistoryHashList.SelectedIndices.Clear();
308                 } while (this.HistoryHashList.SelectedIndices.Count > 0);
309                 this.HistoryHashList.Items.Insert(idx, hashStr);
310                 this.HistoryHashList.SelectedIndex = idx;
311             }
312             else
313             {
314                 this.AddHashToHistory(hashStr, false);
315                 do
316                 {
317                     this.HistoryHashList.SelectedIndices.Clear();
318                 } while (this.HistoryHashList.SelectedIndices.Count > 0);
319                 this.HistoryHashList.SelectedIndex = this.HistoryHashList.Items.IndexOf(hashStr);
320             }
321
322             ChangeMode(false);
323         }
324
325         private void PermCancel_Button_Click(object sender, EventArgs e)
326         {
327             if (this.HistoryHashList.Items.Count > 0 && this.HistoryHashList.SelectedIndices.Count > 0)
328                 this.UseHashText.Text = this.HistoryHashList.Items[this.HistoryHashList.SelectedIndices[0]].ToString();
329             else
330                 this.UseHashText.Text = "";
331
332             ChangeMode(false);
333         }
334
335         private void HistoryHashList_KeyDown(object sender, KeyEventArgs e)
336         {
337             if (e.KeyCode == Keys.Delete)
338                 this.DeleteButton_Click(this.DeleteButton, EventArgs.Empty);
339             else if (e.KeyCode == Keys.Insert)
340                 this.AddButton_Click(this.AddButton, EventArgs.Empty);
341         }
342
343         private bool AdjustHashtags(ref string hashtag, bool isShowWarn)
344         {
345             //ハッシュタグの整形
346             hashtag = hashtag.Trim();
347             if (string.IsNullOrEmpty(hashtag))
348             {
349                 if (isShowWarn) MessageBox.Show("emply hashtag.", "Hashtag warning", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
350                 return false;
351             }
352             hashtag = hashtag.Replace("#", "#");
353             hashtag = hashtag.Replace(" ", " ");
354             var adjust = "";
355             foreach (var hash in hashtag.Split(' '))
356             {
357                 if (hash.Length > 0)
358                 {
359                     if (!hash.StartsWith("#", StringComparison.Ordinal))
360                     {
361                         if (isShowWarn) MessageBox.Show("Invalid hashtag. -> " + hash, "Hashtag warning", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
362                         return false;
363                     }
364                     if (hash.Length == 1)
365                     {
366                         if (isShowWarn) MessageBox.Show("empty hashtag.", "Hashtag warning", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
367                         return false;
368                     }
369                     //使用不可の文字チェックはしない
370                     adjust += hash + " ";
371                 }
372             }
373             hashtag = adjust.Trim();
374             return true;
375         }
376
377         private void OK_Button_Click(object sender, EventArgs e)
378         {
379             var hash = "";
380             foreach (string hs in this.HistoryHashList.SelectedItems)
381             {
382                 hash += hs + " ";
383             }
384             hash = hash.Trim();
385             if (!string.IsNullOrEmpty(hash))
386             {
387                 this.AddHashToHistory(hash, true);
388                 this.IsPermanent = this.CheckPermanent.Checked;
389             }
390             else
391             {
392                 //使用ハッシュが未選択ならば、固定オプション外す
393                 this.IsPermanent = false;
394             }
395             this.IsHead = this.RadioHead.Checked;
396             this.UseHash = hash;
397
398             this.DialogResult = DialogResult.OK;
399             this.Close();
400         }
401
402         private void HashtagManage_KeyDown(object sender, KeyEventArgs e)
403         {
404             if (e.KeyCode == Keys.Enter)
405             {
406                 e.Handled = true;
407                 if (this.GroupDetail.Enabled)
408                     this.PermOK_Button_Click(this.PermOK_Button, EventArgs.Empty);
409                 else
410                     this.OK_Button_Click(this.OK_Button, EventArgs.Empty);
411             }
412             else if (e.KeyCode == Keys.Escape)
413             {
414                 e.Handled = true;
415                 if (this.GroupDetail.Enabled)
416                     this.PermCancel_Button_Click(this.PermCancel_Button, EventArgs.Empty);
417                 else
418                     this.Cancel_Button_Click(this.Cancel_Button, EventArgs.Empty);
419             }
420         }
421
422         private void CheckNotAddToAtReply_CheckedChanged(object sender, EventArgs e)
423             => this.IsNotAddToAtReply = CheckNotAddToAtReply.Checked;
424     }
425 }