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) 2011-2012 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
8 // All rights reserved.
10 // This file is part of OpenTween.
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)
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
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.
30 using System.Collections.Generic;
31 using System.Collections.Specialized;
32 using System.ComponentModel;
37 using System.Linq.Expressions;
39 using System.Text.RegularExpressions;
40 using System.Windows.Forms;
41 using OpenTween.Models;
45 public partial class FilterDialog : OTBaseForm
47 private EDITMODE mode;
48 private bool directAdd;
49 private MultiSelectionState multiSelState = MultiSelectionState.None;
50 private readonly TabInformations sts;
52 private List<TabModel> tabs = new List<TabModel>();
53 private int selectedTabIndex = -1;
55 private readonly List<string> idlist = new List<string>();
64 private enum EnableButtonMode
72 private enum MultiSelectionState
79 private EnableButtonMode RuleEnableButtonMode
81 get => this.ruleEnableButtonMode;
84 this.ruleEnableButtonMode = value;
86 this.buttonRuleToggleEnabled.Text = value == FilterDialog.EnableButtonMode.Enable
87 ? Properties.Resources.EnableButtonCaption
88 : Properties.Resources.DisableButtonCaption;
89 this.buttonRuleToggleEnabled.Enabled = value != EnableButtonMode.NotSelected;
92 private EnableButtonMode ruleEnableButtonMode = FilterDialog.EnableButtonMode.NotSelected;
94 public TabModel? SelectedTab
95 => this.selectedTabIndex != -1 ? this.tabs[this.selectedTabIndex] : null;
99 this.InitializeComponent();
101 this.sts = TabInformations.GetInstance();
102 this.RefreshListTabs();
105 private void RefreshListTabs()
107 this.tabs = this.sts.Tabs.Append(this.sts.MuteTab).ToList();
109 using (ControlTransaction.Update(this.ListTabs))
111 var selectedTab = this.ListTabs.SelectedItem;
112 this.ListTabs.Items.Clear();
113 this.ListTabs.Items.AddRange(this.tabs.ToArray());
114 this.ListTabs.SelectedIndex = this.tabs.FindIndex(x => x == selectedTab);
118 private void SetFilters(TabModel tab)
120 if (this.ListTabs.Items.Count == 0) return;
122 this.ListFilters.Items.Clear();
124 if (tab is FilterTabModel filterTab)
125 this.ListFilters.Items.AddRange(filterTab.GetFilters());
127 if (this.ListFilters.Items.Count > 0)
128 this.ListFilters.SelectedIndex = 0;
132 if (tab.IsDefaultTabType)
134 this.CheckProtected.Checked = true;
135 this.CheckProtected.Enabled = false;
139 this.CheckProtected.Checked = tab.Protected;
140 this.CheckProtected.Enabled = true;
143 this.CheckManageRead.CheckedChanged -= this.CheckManageRead_CheckedChanged;
144 this.CheckManageRead.Checked = tab.UnreadManage;
145 this.CheckManageRead.CheckedChanged += this.CheckManageRead_CheckedChanged;
147 this.CheckNotifyNew.CheckedChanged -= this.CheckNotifyNew_CheckedChanged;
148 this.CheckNotifyNew.Checked = tab.Notify;
149 this.CheckNotifyNew.CheckedChanged += this.CheckNotifyNew_CheckedChanged;
151 var idx = this.ComboSound.Items.IndexOf(tab.SoundFile);
152 if (idx == -1) idx = 0;
153 this.ComboSound.SelectedIndex = idx;
155 if (this.directAdd) return;
157 if (tab.TabType == MyCommon.TabUsageType.Mute)
159 this.ButtonRenameTab.Enabled = false;
160 this.CheckManageRead.Enabled = false;
161 this.CheckNotifyNew.Enabled = false;
162 this.ComboSound.Enabled = false;
164 this.GroupBox1.Visible = false;
165 this.labelMuteTab.Visible = true;
169 this.ButtonRenameTab.Enabled = true;
170 this.CheckManageRead.Enabled = true;
171 this.CheckNotifyNew.Enabled = true;
172 this.ComboSound.Enabled = true;
174 this.GroupBox1.Visible = true;
175 this.labelMuteTab.Visible = false;
178 this.ListTabs.Enabled = true;
179 this.GroupTab.Enabled = true;
180 this.ListFilters.Enabled = true;
181 this.EditFilterGroup.Enabled = false;
183 if (tab.IsDistributableTabType)
185 this.ButtonNew.Enabled = true;
186 if (this.ListFilters.SelectedIndex > -1)
188 this.ButtonEdit.Enabled = true;
189 this.ButtonDelete.Enabled = true;
190 this.ButtonRuleUp.Enabled = true;
191 this.ButtonRuleDown.Enabled = true;
192 this.ButtonRuleCopy.Enabled = true;
193 this.ButtonRuleMove.Enabled = true;
194 this.buttonRuleToggleEnabled.Enabled = true;
198 this.ButtonEdit.Enabled = false;
199 this.ButtonDelete.Enabled = false;
200 this.ButtonRuleUp.Enabled = false;
201 this.ButtonRuleDown.Enabled = false;
202 this.ButtonRuleCopy.Enabled = false;
203 this.ButtonRuleMove.Enabled = false;
204 this.buttonRuleToggleEnabled.Enabled = false;
209 this.ButtonNew.Enabled = false;
210 this.ButtonEdit.Enabled = false;
211 this.ButtonDelete.Enabled = false;
212 this.ButtonRuleUp.Enabled = false;
213 this.ButtonRuleDown.Enabled = false;
214 this.ButtonRuleCopy.Enabled = false;
215 this.ButtonRuleMove.Enabled = false;
216 this.buttonRuleToggleEnabled.Enabled = false;
219 this.LabelTabType.Text = tab.TabType switch
221 MyCommon.TabUsageType.Home => Properties.Resources.TabUsageTypeName_Home,
222 MyCommon.TabUsageType.Mentions => Properties.Resources.TabUsageTypeName_Mentions,
223 MyCommon.TabUsageType.DirectMessage => Properties.Resources.TabUsageTypeName_DirectMessage,
224 MyCommon.TabUsageType.Favorites => Properties.Resources.TabUsageTypeName_Favorites,
225 MyCommon.TabUsageType.UserDefined => Properties.Resources.TabUsageTypeName_UserDefined,
226 MyCommon.TabUsageType.PublicSearch => Properties.Resources.TabUsageTypeName_PublicSearch,
227 MyCommon.TabUsageType.Lists => Properties.Resources.TabUsageTypeName_Lists,
228 MyCommon.TabUsageType.Related => Properties.Resources.TabUsageTypeName_Related,
229 MyCommon.TabUsageType.UserTimeline => Properties.Resources.TabUsageTypeName_UserTimeline,
230 MyCommon.TabUsageType.Mute => "Mute",
231 MyCommon.TabUsageType.SearchResults => "SearchResults",
235 if (tab.IsDefaultTabType || tab.Protected)
237 this.ButtonDeleteTab.Enabled = false;
241 this.ButtonDeleteTab.Enabled = true;
243 this.ButtonClose.Enabled = true;
246 public void SetCurrent(string tabName)
248 var index = this.tabs.FindIndex(x => x.TabName == tabName);
250 throw new ArgumentException($"Unknown tab: {tabName}", nameof(tabName));
252 this.selectedTabIndex = index;
255 public void AddNewFilter(string id, string msg)
258 this.ButtonNew.Enabled = false;
259 this.ButtonEdit.Enabled = false;
260 this.ButtonRuleUp.Enabled = false;
261 this.ButtonRuleDown.Enabled = false;
262 this.ButtonRuleCopy.Enabled = false;
263 this.ButtonRuleMove.Enabled = false;
264 this.buttonRuleToggleEnabled.Enabled = false;
265 this.ButtonDelete.Enabled = false;
266 this.ButtonClose.Enabled = false;
267 this.EditFilterGroup.Enabled = true;
268 this.ListTabs.Enabled = false;
269 this.GroupTab.Enabled = false;
270 this.ListFilters.Enabled = false;
272 this.RadioAND.Checked = true;
273 this.RadioPLUS.Checked = false;
275 this.UID.SelectAll();
276 this.MSG1.Text = msg;
277 this.MSG1.SelectAll();
278 this.MSG2.Text = id + msg;
279 this.MSG2.SelectAll();
280 this.TextSource.Text = "";
281 this.UID.Enabled = true;
282 this.MSG1.Enabled = true;
283 this.MSG2.Enabled = false;
284 this.CheckRegex.Checked = false;
285 this.CheckURL.Checked = false;
286 this.CheckCaseSensitive.Checked = false;
287 this.CheckRetweet.Checked = false;
288 this.CheckLambda.Checked = false;
290 this.RadioExAnd.Checked = true;
291 this.RadioExPLUS.Checked = false;
292 this.ExUID.Text = "";
293 this.ExUID.SelectAll();
294 this.ExMSG1.Text = "";
295 this.ExMSG1.SelectAll();
296 this.ExMSG2.Text = "";
297 this.ExMSG2.SelectAll();
298 this.TextExSource.Text = "";
299 this.ExUID.Enabled = true;
300 this.ExMSG1.Enabled = true;
301 this.ExMSG2.Enabled = false;
302 this.CheckExRegex.Checked = false;
303 this.CheckExURL.Checked = false;
304 this.CheckExCaseSensitive.Checked = false;
305 this.CheckExRetweet.Checked = false;
306 this.CheckExLambDa.Checked = false;
308 this.OptCopy.Checked = true;
309 this.CheckMark.Checked = true;
311 this.mode = EDITMODE.AddNew;
312 this.directAdd = true;
315 private void ButtonNew_Click(object sender, EventArgs e)
317 this.ButtonNew.Enabled = false;
318 this.ButtonEdit.Enabled = false;
319 this.ButtonClose.Enabled = false;
320 this.ButtonRuleUp.Enabled = false;
321 this.ButtonRuleDown.Enabled = false;
322 this.ButtonRuleCopy.Enabled = false;
323 this.ButtonRuleMove.Enabled = false;
324 this.buttonRuleToggleEnabled.Enabled = false;
325 this.ButtonDelete.Enabled = false;
326 this.ButtonClose.Enabled = false;
327 this.EditFilterGroup.Enabled = true;
328 this.ListTabs.Enabled = false;
329 this.GroupTab.Enabled = false;
330 this.ListFilters.Enabled = false;
332 this.RadioAND.Checked = true;
333 this.RadioPLUS.Checked = false;
337 this.TextSource.Text = "";
338 this.UID.Enabled = true;
339 this.MSG1.Enabled = true;
340 this.MSG2.Enabled = false;
341 this.CheckRegex.Checked = false;
342 this.CheckURL.Checked = false;
343 this.CheckCaseSensitive.Checked = false;
344 this.CheckRetweet.Checked = false;
345 this.CheckLambda.Checked = false;
347 this.RadioExAnd.Checked = true;
348 this.RadioExPLUS.Checked = false;
349 this.ExUID.Text = "";
350 this.ExMSG1.Text = "";
351 this.ExMSG2.Text = "";
352 this.TextExSource.Text = "";
353 this.ExUID.Enabled = true;
354 this.ExMSG1.Enabled = true;
355 this.ExMSG2.Enabled = false;
356 this.CheckExRegex.Checked = false;
357 this.CheckExURL.Checked = false;
358 this.CheckExCaseSensitive.Checked = false;
359 this.CheckExRetweet.Checked = false;
360 this.CheckExLambDa.Checked = false;
362 this.OptCopy.Checked = true;
363 this.CheckMark.Checked = true;
365 this.mode = EDITMODE.AddNew;
368 private void ButtonEdit_Click(object sender, EventArgs e)
370 if (this.ListFilters.SelectedIndex == -1) return;
374 var idx = this.ListFilters.SelectedIndex;
375 this.ListFilters.SelectedIndex = -1;
376 this.ListFilters.SelectedIndex = idx;
377 this.ListFilters.Enabled = false;
379 this.ButtonNew.Enabled = false;
380 this.ButtonEdit.Enabled = false;
381 this.ButtonDelete.Enabled = false;
382 this.ButtonClose.Enabled = false;
383 this.ButtonRuleUp.Enabled = false;
384 this.ButtonRuleDown.Enabled = false;
385 this.ButtonRuleCopy.Enabled = false;
386 this.ButtonRuleMove.Enabled = false;
387 this.buttonRuleToggleEnabled.Enabled = false;
388 this.EditFilterGroup.Enabled = true;
389 this.ListTabs.Enabled = false;
390 this.GroupTab.Enabled = false;
392 this.mode = EDITMODE.Edit;
395 private void ButtonDelete_Click(object sender, EventArgs e)
397 var selectedCount = this.ListFilters.SelectedIndices.Count;
398 if (selectedCount == 0) return;
402 if (selectedCount == 1)
404 tmp = string.Format(Properties.Resources.ButtonDelete_ClickText1, Environment.NewLine, this.ListFilters.SelectedItem);
408 tmp = string.Format(Properties.Resources.ButtonDelete_ClickText3, selectedCount);
411 var rslt = MessageBox.Show(tmp, Properties.Resources.ButtonDelete_ClickText2, MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
412 if (rslt == DialogResult.Cancel) return;
414 var indices = this.ListFilters.SelectedIndices.Cast<int>().Reverse().ToArray(); // 後ろの要素から削除
415 var tab = (FilterTabModel)this.SelectedTab!;
417 using (ControlTransaction.Update(this.ListFilters))
419 foreach (var idx in indices)
421 tab.RemoveFilter((PostFilterRule)this.ListFilters.Items[idx]);
422 this.ListFilters.Items.RemoveAt(idx);
427 private void ButtonCancel_Click(object sender, EventArgs e)
429 this.ListTabs.Enabled = true;
430 this.GroupTab.Enabled = true;
431 this.ListFilters.Enabled = true;
432 this.ListFilters.Focus();
433 if (this.ListFilters.SelectedIndex != -1)
437 this.EditFilterGroup.Enabled = false;
438 this.ButtonNew.Enabled = true;
439 if (this.ListFilters.SelectedIndex > -1)
441 this.ButtonEdit.Enabled = true;
442 this.ButtonDelete.Enabled = true;
443 this.ButtonRuleUp.Enabled = true;
444 this.ButtonRuleDown.Enabled = true;
445 this.ButtonRuleCopy.Enabled = true;
446 this.ButtonRuleMove.Enabled = true;
447 this.buttonRuleToggleEnabled.Enabled = true;
451 this.ButtonEdit.Enabled = false;
452 this.ButtonDelete.Enabled = false;
453 this.ButtonRuleUp.Enabled = false;
454 this.ButtonRuleDown.Enabled = false;
455 this.ButtonRuleCopy.Enabled = false;
456 this.ButtonRuleMove.Enabled = false;
457 this.buttonRuleToggleEnabled.Enabled = false;
459 this.ButtonClose.Enabled = true;
466 private void ShowDetail()
468 if (this.directAdd) return;
470 if (this.ListFilters.SelectedIndex > -1)
472 var fc = (PostFilterRule)this.ListFilters.SelectedItem;
475 this.RadioAND.Checked = true;
476 this.RadioPLUS.Checked = false;
477 this.UID.Enabled = true;
478 this.MSG1.Enabled = true;
479 this.MSG2.Enabled = false;
480 this.UID.Text = fc.FilterName;
481 this.UID.SelectAll();
482 this.MSG1.Text = string.Join(" ", fc.FilterBody);
483 this.MSG1.SelectAll();
488 this.RadioPLUS.Checked = true;
489 this.RadioAND.Checked = false;
490 this.UID.Enabled = false;
491 this.MSG1.Enabled = false;
492 this.MSG2.Enabled = true;
495 this.MSG2.Text = string.Join(" ", fc.FilterBody);
496 this.MSG2.SelectAll();
498 this.TextSource.Text = fc.FilterSource;
499 this.CheckRegex.Checked = fc.UseRegex;
500 this.CheckURL.Checked = fc.FilterByUrl;
501 this.CheckCaseSensitive.Checked = fc.CaseSensitive;
502 this.CheckRetweet.Checked = fc.FilterRt;
503 this.CheckLambda.Checked = fc.UseLambda;
505 if (fc.ExUseNameField)
507 this.RadioExAnd.Checked = true;
508 this.RadioExPLUS.Checked = false;
509 this.ExUID.Enabled = true;
510 this.ExMSG1.Enabled = true;
511 this.ExMSG2.Enabled = false;
512 this.ExUID.Text = fc.ExFilterName;
513 this.ExUID.SelectAll();
514 this.ExMSG1.Text = string.Join(" ", fc.ExFilterBody);
515 this.ExMSG1.SelectAll();
516 this.ExMSG2.Text = "";
520 this.RadioExPLUS.Checked = true;
521 this.RadioExAnd.Checked = false;
522 this.ExUID.Enabled = false;
523 this.ExMSG1.Enabled = false;
524 this.ExMSG2.Enabled = true;
525 this.ExUID.Text = "";
526 this.ExMSG1.Text = "";
527 this.ExMSG2.Text = string.Join(" ", fc.ExFilterBody);
528 this.ExMSG2.SelectAll();
530 this.TextExSource.Text = fc.ExFilterSource;
531 this.CheckExRegex.Checked = fc.ExUseRegex;
532 this.CheckExURL.Checked = fc.ExFilterByUrl;
533 this.CheckExCaseSensitive.Checked = fc.ExCaseSensitive;
534 this.CheckExRetweet.Checked = fc.ExFilterRt;
535 this.CheckExLambDa.Checked = fc.ExUseLambda;
539 this.OptMove.Checked = true;
543 this.OptCopy.Checked = true;
545 this.CheckMark.Checked = fc.MarkMatches;
547 this.ButtonEdit.Enabled = true;
548 this.ButtonDelete.Enabled = true;
549 this.ButtonRuleUp.Enabled = true;
550 this.ButtonRuleDown.Enabled = true;
551 this.ButtonRuleCopy.Enabled = true;
552 this.ButtonRuleMove.Enabled = true;
553 this.buttonRuleToggleEnabled.Enabled = true;
557 this.RadioAND.Checked = true;
558 this.RadioPLUS.Checked = false;
559 this.UID.Enabled = true;
560 this.MSG1.Enabled = true;
561 this.MSG2.Enabled = false;
565 this.TextSource.Text = "";
566 this.CheckRegex.Checked = false;
567 this.CheckURL.Checked = false;
568 this.CheckCaseSensitive.Checked = false;
569 this.CheckRetweet.Checked = false;
570 this.CheckLambda.Checked = false;
572 this.RadioExAnd.Checked = true;
573 this.RadioExPLUS.Checked = false;
574 this.ExUID.Enabled = true;
575 this.ExMSG1.Enabled = true;
576 this.ExMSG2.Enabled = false;
577 this.ExUID.Text = "";
578 this.ExMSG1.Text = "";
579 this.ExMSG2.Text = "";
580 this.TextExSource.Text = "";
581 this.CheckExRegex.Checked = false;
582 this.CheckExURL.Checked = false;
583 this.CheckExCaseSensitive.Checked = false;
584 this.CheckExRetweet.Checked = false;
585 this.CheckExLambDa.Checked = false;
587 this.OptCopy.Checked = true;
588 this.CheckMark.Checked = true;
590 this.ButtonEdit.Enabled = false;
591 this.ButtonDelete.Enabled = false;
592 this.ButtonRuleUp.Enabled = false;
593 this.ButtonRuleDown.Enabled = false;
594 this.ButtonRuleCopy.Enabled = false;
595 this.ButtonRuleMove.Enabled = false;
596 this.buttonRuleToggleEnabled.Enabled = false;
600 private void RadioAND_CheckedChanged(object sender, EventArgs e)
602 var flg = this.RadioAND.Checked;
603 this.UID.Enabled = flg;
604 this.MSG1.Enabled = flg;
605 this.MSG2.Enabled = !flg;
608 private void ButtonOK_Click(object sender, EventArgs e)
611 if (!this.CheckMatchRule(out var isBlankMatch) || !this.CheckExcludeRule(out var isBlankExclude))
615 if (isBlankMatch && isBlankExclude)
617 MessageBox.Show(Properties.Resources.ButtonOK_ClickText1, Properties.Resources.ButtonOK_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
621 var tab = (FilterTabModel)this.SelectedTab!;
622 var i = this.ListFilters.SelectedIndex;
625 if (this.mode == EDITMODE.AddNew)
626 ft = new PostFilterRule();
628 ft = (PostFilterRule)this.ListFilters.SelectedItem;
630 if (tab.TabType != MyCommon.TabUsageType.Mute)
632 ft.MoveMatches = this.OptMove.Checked;
633 ft.MarkMatches = this.CheckMark.Checked;
637 ft.MoveMatches = true;
638 ft.MarkMatches = false;
642 if (this.RadioAND.Checked)
644 ft.FilterName = this.UID.Text;
645 var owner = (TweenMain)this.Owner;
646 var cnt = owner.AtIdSupl.ItemCount;
647 owner.AtIdSupl.AddItem("@" + ft.FilterName);
648 if (cnt != owner.AtIdSupl.ItemCount)
650 owner.MarkSettingAtIdModified();
652 ft.UseNameField = true;
653 bdy = this.MSG1.Text;
658 ft.UseNameField = false;
659 bdy = this.MSG2.Text;
661 ft.FilterSource = this.TextSource.Text;
663 if (this.CheckRegex.Checked || this.CheckLambda.Checked)
665 ft.FilterBody = new[] { bdy };
669 ft.FilterBody = bdy.Split(' ', ' ')
670 .Where(x => !MyCommon.IsNullOrEmpty(x))
674 ft.UseRegex = this.CheckRegex.Checked;
675 ft.FilterByUrl = this.CheckURL.Checked;
676 ft.CaseSensitive = this.CheckCaseSensitive.Checked;
677 ft.FilterRt = this.CheckRetweet.Checked;
678 ft.UseLambda = this.CheckLambda.Checked;
681 if (this.RadioExAnd.Checked)
683 ft.ExFilterName = this.ExUID.Text;
684 ft.ExUseNameField = true;
685 bdy = this.ExMSG1.Text;
689 ft.ExFilterName = "";
690 ft.ExUseNameField = false;
691 bdy = this.ExMSG2.Text;
693 ft.ExFilterSource = this.TextExSource.Text;
695 if (this.CheckExRegex.Checked || this.CheckExLambDa.Checked)
697 ft.ExFilterBody = new[] { bdy };
701 ft.ExFilterBody = bdy.Split(' ', ' ')
702 .Where(x => !MyCommon.IsNullOrEmpty(x))
706 ft.ExUseRegex = this.CheckExRegex.Checked;
707 ft.ExFilterByUrl = this.CheckExURL.Checked;
708 ft.ExCaseSensitive = this.CheckExCaseSensitive.Checked;
709 ft.ExFilterRt = this.CheckExRetweet.Checked;
710 ft.ExUseLambda = this.CheckExLambDa.Checked;
712 if (this.mode == EDITMODE.AddNew)
714 if (!tab.AddFilter(ft))
715 MessageBox.Show(Properties.Resources.ButtonOK_ClickText4, Properties.Resources.ButtonOK_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Error);
718 this.SetFilters(tab);
719 this.ListFilters.SelectedIndex = -1;
720 if (this.mode == EDITMODE.AddNew)
722 this.ListFilters.SelectedIndex = this.ListFilters.Items.Count - 1;
726 this.ListFilters.SelectedIndex = i;
728 this.mode = EDITMODE.None;
736 private bool IsValidLambdaExp(string text)
737 => false; // TODO DynamicQuery相当のGPLv3互換なライブラリで置換する
739 private bool IsValidRegexp(string text)
747 MessageBox.Show(Properties.Resources.ButtonOK_ClickText3 + ex.Message, Properties.Resources.ButtonOK_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
753 private bool CheckMatchRule(out bool isBlank)
756 if (this.RadioAND.Checked)
758 if (MyCommon.IsNullOrEmpty(this.UID.Text) && MyCommon.IsNullOrEmpty(this.MSG1.Text) && MyCommon.IsNullOrEmpty(this.TextSource.Text) && this.CheckRetweet.Checked == false)
763 if (this.CheckLambda.Checked)
765 if (!this.IsValidLambdaExp(this.UID.Text))
769 if (!this.IsValidLambdaExp(this.MSG1.Text))
774 else if (this.CheckRegex.Checked)
776 if (!this.IsValidRegexp(this.UID.Text))
780 if (!this.IsValidRegexp(this.MSG1.Text))
788 if (MyCommon.IsNullOrEmpty(this.MSG2.Text) && MyCommon.IsNullOrEmpty(this.TextSource.Text) && this.CheckRetweet.Checked == false)
793 if (this.CheckLambda.Checked && !this.IsValidLambdaExp(this.MSG2.Text))
797 else if (this.CheckRegex.Checked && !this.IsValidRegexp(this.MSG2.Text))
803 if (this.CheckRegex.Checked && !this.IsValidRegexp(this.TextSource.Text))
810 private bool CheckExcludeRule(out bool isBlank)
813 if (this.RadioExAnd.Checked)
815 if (MyCommon.IsNullOrEmpty(this.ExUID.Text) && MyCommon.IsNullOrEmpty(this.ExMSG1.Text) && MyCommon.IsNullOrEmpty(this.TextExSource.Text) && this.CheckExRetweet.Checked == false)
820 if (this.CheckExLambDa.Checked)
822 if (!this.IsValidLambdaExp(this.ExUID.Text))
826 if (!this.IsValidLambdaExp(this.ExMSG1.Text))
831 else if (this.CheckExRegex.Checked)
833 if (!this.IsValidRegexp(this.ExUID.Text))
837 if (!this.IsValidRegexp(this.ExMSG1.Text))
845 if (MyCommon.IsNullOrEmpty(this.ExMSG2.Text) && MyCommon.IsNullOrEmpty(this.TextExSource.Text) && this.CheckExRetweet.Checked == false)
850 if (this.CheckExLambDa.Checked && !this.IsValidLambdaExp(this.ExMSG2.Text))
854 else if (this.CheckExRegex.Checked && !this.IsValidRegexp(this.ExMSG2.Text))
860 if (this.CheckExRegex.Checked && !this.IsValidRegexp(this.TextExSource.Text))
868 private void ListFilters_SelectedIndexChanged(object sender, EventArgs e)
870 if (this.multiSelState != MultiSelectionState.None) // 複数選択処理中は無視する
875 var selectedCount = this.ListFilters.SelectedIndices.Count;
876 if (selectedCount == 0)
878 this.RuleEnableButtonMode = EnableButtonMode.NotSelected;
882 if (selectedCount == 1 ||
883 this.RuleEnableButtonMode == EnableButtonMode.NotSelected)
885 var topItem = (PostFilterRule)this.ListFilters.SelectedItem;
886 this.RuleEnableButtonMode = topItem.Enabled ? EnableButtonMode.Disable : EnableButtonMode.Enable;
891 private void ButtonClose_Click(object sender, EventArgs e)
894 private void FilterDialog_FormClosed(object sender, FormClosedEventArgs e)
895 => this.directAdd = false;
897 private void FilterDialog_KeyDown(object sender, KeyEventArgs e)
899 if (e.KeyCode == Keys.Escape)
901 if (this.EditFilterGroup.Enabled)
902 this.ButtonCancel_Click(this.ButtonCancel, EventArgs.Empty);
904 this.ButtonClose_Click(this.ButtonClose, EventArgs.Empty);
908 private void ListFilters_DoubleClick(object sender, EventArgs e)
910 var idx = this.ListFilters.SelectedIndex;
911 if (idx == -1) return;
913 var midx = this.ListFilters.IndexFromPoint(this.ListFilters.PointToClient(Control.MousePosition));
914 if (midx == ListBox.NoMatches || idx != midx) return;
916 this.ButtonEdit_Click(sender, e);
919 private void FilterDialog_Shown(object sender, EventArgs e)
921 this.ListTabs.DisplayMember = nameof(TabModel.TabName);
923 this.ComboSound.Items.Clear();
924 this.ComboSound.Items.Add("");
925 var oDir = new DirectoryInfo(Application.StartupPath + Path.DirectorySeparatorChar);
926 if (Directory.Exists(Path.Combine(Application.StartupPath, "Sounds")))
928 oDir = oDir.GetDirectories("Sounds")[0];
930 foreach (var oFile in oDir.GetFiles("*.wav"))
932 this.ComboSound.Items.Add(oFile.Name);
936 foreach (var tmp in ((TweenMain)this.Owner).AtIdSupl.GetItemList())
938 this.idlist.Add(tmp.Remove(0, 1)); // @文字削除
940 this.UID.AutoCompleteCustomSource.Clear();
941 this.UID.AutoCompleteCustomSource.AddRange(this.idlist.ToArray());
943 this.ExUID.AutoCompleteCustomSource.Clear();
944 this.ExUID.AutoCompleteCustomSource.AddRange(this.idlist.ToArray());
947 this.ListTabs.SelectedIndex = this.selectedTabIndex;
950 private void ListTabs_SelectedIndexChanged(object sender, EventArgs e)
952 this.selectedTabIndex = this.ListTabs.SelectedIndex;
954 var selectedTab = this.SelectedTab;
955 if (selectedTab != null)
956 this.SetFilters(selectedTab);
958 this.ListFilters.Items.Clear();
961 private async void ButtonAddTab_Click(object sender, EventArgs e)
963 string? tabName = null;
964 MyCommon.TabUsageType tabType;
965 using (var inputName = new InputTabName())
967 inputName.TabName = this.sts.MakeTabName("MyTab");
968 inputName.IsShowUsage = true;
969 inputName.ShowDialog();
970 if (inputName.DialogResult == DialogResult.Cancel) return;
971 tabName = inputName.TabName;
972 tabType = inputName.Usage;
974 if (!MyCommon.IsNullOrEmpty(tabName))
977 ListElement? list = null;
978 if (tabType == MyCommon.TabUsageType.Lists)
982 using var dialog = new WaitingDialog(Properties.Resources.ListsGetting);
983 var cancellationToken = dialog.EnableCancellation();
985 var task = ((TweenMain)this.Owner).TwitterInstance.GetListsApi();
986 await dialog.WaitForAsync(this, task);
988 cancellationToken.ThrowIfCancellationRequested();
990 catch (OperationCanceledException)
994 catch (WebApiException ex)
996 MessageBox.Show("Failed to get lists. (" + ex.Message + ")");
999 using var listAvail = new ListAvailable();
1001 if (listAvail.ShowDialog(this) == DialogResult.Cancel)
1003 if (listAvail.SelectedList == null)
1005 list = listAvail.SelectedList;
1011 case MyCommon.TabUsageType.UserDefined:
1012 tab = new FilterTabModel(tabName);
1014 case MyCommon.TabUsageType.PublicSearch:
1015 tab = new PublicSearchTabModel(tabName);
1017 case MyCommon.TabUsageType.Lists:
1018 tab = new ListTimelineTabModel(tabName, list!);
1024 if (!this.sts.AddTab(tab) || !((TweenMain)this.Owner).AddNewTab(tab, startup: false))
1026 var tmp = string.Format(Properties.Resources.AddTabMenuItem_ClickText1, tabName);
1027 MessageBox.Show(tmp, Properties.Resources.AddTabMenuItem_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
1033 this.RefreshListTabs();
1038 private void ButtonDeleteTab_Click(object sender, EventArgs e)
1040 var selectedTab = this.SelectedTab;
1041 if (selectedTab != null)
1043 var tb = selectedTab.TabName;
1044 var idx = this.ListTabs.SelectedIndex;
1045 if (((TweenMain)this.Owner).RemoveSpecifiedTab(tb, true))
1047 this.RefreshListTabs();
1049 if (idx < 0) idx = 0;
1050 this.ListTabs.SelectedIndex = idx;
1055 private void ButtonRenameTab_Click(object sender, EventArgs e)
1057 var selectedTab = this.SelectedTab;
1058 if (selectedTab != null)
1060 var origTabName = selectedTab.TabName;
1061 if (((TweenMain)this.Owner).TabRename(origTabName, out _))
1062 this.RefreshListTabs();
1066 private void CheckManageRead_CheckedChanged(object sender, EventArgs e)
1068 var selectedTab = this.SelectedTab;
1069 if (selectedTab != null)
1071 ((TweenMain)this.Owner).ChangeTabUnreadManage(
1072 selectedTab.TabName,
1073 this.CheckManageRead.Checked);
1077 private void ButtonUp_Click(object sender, EventArgs e)
1079 var selectedIndex = this.ListTabs.SelectedIndex;
1081 if (selectedIndex == -1 || selectedIndex == 0)
1084 var selectedTab = this.tabs[selectedIndex];
1085 var selectedTabName = selectedTab.TabName;
1087 var targetTab = this.tabs[selectedIndex - 1];
1088 var targetTabName = targetTab.TabName;
1091 if (selectedTab.TabType == MyCommon.TabUsageType.Mute || targetTab.TabType == MyCommon.TabUsageType.Mute)
1094 var tweenMain = (TweenMain)this.Owner;
1095 tweenMain.ReOrderTab(selectedTabName, targetTabName, true);
1097 this.RefreshListTabs();
1100 private void ButtonDown_Click(object sender, EventArgs e)
1102 var selectedIndex = this.ListTabs.SelectedIndex;
1104 if (selectedIndex == -1 || selectedIndex == this.ListTabs.Items.Count - 1)
1107 var selectedTab = this.tabs[selectedIndex];
1108 var selectedTabName = selectedTab.TabName;
1110 var targetTab = this.tabs[selectedIndex + 1];
1111 var targetTabName = targetTab.TabName;
1114 if (selectedTab.TabType == MyCommon.TabUsageType.Mute || targetTab.TabType == MyCommon.TabUsageType.Mute)
1117 var tweenMain = (TweenMain)this.Owner;
1118 tweenMain.ReOrderTab(selectedTabName, targetTabName, false);
1120 this.RefreshListTabs();
1123 private void CheckLocked_CheckedChanged(object sender, EventArgs e)
1125 var selectedTab = this.SelectedTab;
1126 if (selectedTab != null)
1128 selectedTab.Protected = this.CheckProtected.Checked;
1129 this.ButtonDeleteTab.Enabled = !this.CheckProtected.Checked;
1133 private void CheckNotifyNew_CheckedChanged(object sender, EventArgs e)
1135 var selectedTab = this.SelectedTab;
1136 if (selectedTab != null)
1138 selectedTab.Notify = this.CheckNotifyNew.Checked;
1142 private void ComboSound_SelectedIndexChanged(object sender, EventArgs e)
1144 var selectedTab = this.SelectedTab;
1145 if (selectedTab != null)
1148 if (this.ComboSound.SelectedIndex > -1) filename = this.ComboSound.SelectedItem.ToString();
1149 selectedTab.SoundFile = filename;
1153 private void RadioExAnd_CheckedChanged(object sender, EventArgs e)
1155 var flg = this.RadioExAnd.Checked;
1156 this.ExUID.Enabled = flg;
1157 this.ExMSG1.Enabled = flg;
1158 this.ExMSG2.Enabled = !flg;
1161 private void OptMove_CheckedChanged(object sender, EventArgs e)
1162 => this.CheckMark.Enabled = !this.OptMove.Checked;
1164 private void ButtonRuleUp_Click(object sender, EventArgs e)
1165 => this.MoveSelectedRules(up: true);
1167 private void ButtonRuleDown_Click(object sender, EventArgs e)
1168 => this.MoveSelectedRules(up: false);
1170 private void MoveSelectedRules(bool up)
1172 var selectedTab = this.SelectedTab;
1173 if (selectedTab == null || this.ListFilters.SelectedIndices.Count == 0)
1176 var indices = this.ListFilters.SelectedIndices.Cast<int>().ToArray();
1181 if (indices[0] <= 0) return;
1186 if (indices[indices.Length - 1] >= this.ListFilters.Items.Count - 1) return;
1188 Array.Reverse(indices); // 逆順にして、下にある要素から処理する
1191 var lastSelIdx = indices[0] + diff;
1192 var tab = (FilterTabModel)selectedTab;
1196 this.multiSelState |= MultiSelectionState.MoveSelected;
1198 using (ControlTransaction.Update(this.ListFilters))
1200 this.ListFilters.SelectedIndices.Clear();
1202 foreach (var idx in indices)
1204 var tidx = idx + diff;
1205 var target = (PostFilterRule)this.ListFilters.Items[tidx];
1207 // 移動先にある要素と位置を入れ替える
1208 this.ListFilters.Items.RemoveAt(tidx);
1209 this.ListFilters.Items.Insert(idx, target);
1211 // 移動方向の先頭要素以外なら選択する
1212 if (tidx != lastSelIdx)
1213 this.ListFilters.SelectedIndex = tidx;
1216 tab.FilterArray = this.ListFilters.Items.Cast<PostFilterRule>().ToArray();
1218 // 移動方向の先頭要素は最後に選択する
1220 this.ListFilters.SelectedIndex = lastSelIdx;
1225 this.multiSelState &= ~MultiSelectionState.MoveSelected;
1229 private void ButtonRuleToggleEnabled_Click(object sender, EventArgs e)
1231 if (this.RuleEnableButtonMode == EnableButtonMode.NotSelected)
1234 var enabled = this.RuleEnableButtonMode == EnableButtonMode.Enable;
1236 foreach (var idx in this.ListFilters.SelectedIndices.Cast<int>())
1238 var filter = (PostFilterRule)this.ListFilters.Items[idx];
1239 if (filter.Enabled != enabled)
1241 filter.Enabled = enabled;
1243 var itemRect = this.ListFilters.GetItemRectangle(idx);
1244 this.ListFilters.Invalidate(itemRect);
1248 this.RuleEnableButtonMode = enabled ? EnableButtonMode.Disable : EnableButtonMode.Enable;
1251 private void ButtonRuleCopy_Click(object sender, EventArgs e)
1253 var selectedTab = this.SelectedTab;
1254 if (selectedTab != null && this.ListFilters.SelectedItem != null)
1256 TabModel[] destinationTabs;
1257 using (var dialog = new TabsDialog(this.sts))
1259 dialog.MultiSelect = true;
1260 dialog.Text = Properties.Resources.ButtonRuleCopy_ClickText1;
1262 if (dialog.ShowDialog(this) == DialogResult.Cancel) return;
1264 destinationTabs = dialog.SelectedTabs;
1267 var currentTab = (FilterTabModel)selectedTab;
1268 var filters = new List<PostFilterRule>();
1270 foreach (int idx in this.ListFilters.SelectedIndices)
1272 filters.Add(currentTab.FilterArray[idx].Clone());
1274 foreach (var tb in destinationTabs.Cast<FilterTabModel>())
1276 if (tb.TabName == currentTab.TabName) continue;
1278 foreach (var flt in filters)
1280 if (!tb.FilterArray.Contains(flt))
1281 tb.AddFilter(flt.Clone());
1284 this.SetFilters(selectedTab);
1288 private void ButtonRuleMove_Click(object sender, EventArgs e)
1290 var selectedTab = this.SelectedTab;
1291 if (selectedTab != null && this.ListFilters.SelectedItem != null)
1293 TabModel[] destinationTabs;
1294 using (var dialog = new TabsDialog(this.sts))
1296 dialog.MultiSelect = true;
1297 dialog.Text = Properties.Resources.ButtonRuleMove_ClickText1;
1299 if (dialog.ShowDialog(this) == DialogResult.Cancel) return;
1301 destinationTabs = dialog.SelectedTabs;
1303 var currentTab = (FilterTabModel)selectedTab;
1304 var filters = new List<PostFilterRule>();
1306 foreach (int idx in this.ListFilters.SelectedIndices)
1308 filters.Add(currentTab.FilterArray[idx].Clone());
1310 if (destinationTabs.Length == 1 && destinationTabs[0].TabName == currentTab.TabName) return;
1311 foreach (var tb in destinationTabs.Cast<FilterTabModel>())
1313 if (tb.TabName == currentTab.TabName) continue;
1315 foreach (var flt in filters)
1317 if (!tb.FilterArray.Contains(flt))
1318 tb.AddFilter(flt.Clone());
1321 for (var idx = this.ListFilters.Items.Count - 1; idx >= 0; idx--)
1323 if (this.ListFilters.GetSelected(idx))
1325 currentTab.RemoveFilter((PostFilterRule)this.ListFilters.Items[idx]);
1326 this.ListFilters.Items.RemoveAt(idx);
1329 this.SetFilters(selectedTab);
1333 private void FilterTextBox_KeyDown(object sender, KeyEventArgs e)
1335 if (e.KeyCode == Keys.Space && e.Modifiers == (Keys.Shift | Keys.Control))
1337 var main = (TweenMain)this.Owner;
1338 var tbox = (TextBox)sender;
1339 if (tbox.SelectionStart > 0)
1341 var endidx = tbox.SelectionStart - 1;
1342 for (var i = tbox.SelectionStart - 1; i >= 0; i--)
1344 var c = tbox.Text[i];
1345 if (char.IsLetterOrDigit(c) || c == '_')
1352 startstr = tbox.Text.Substring(i + 1, endidx - i);
1353 main.ShowSuplDialog(tbox, main.AtIdSupl, startstr.Length + 1, startstr);
1357 startstr = tbox.Text.Substring(i + 1, endidx - i);
1358 main.ShowSuplDialog(tbox, main.HashSupl, startstr.Length + 1, startstr);
1370 private void FilterTextBox_KeyPress(object sender, KeyPressEventArgs e)
1372 var main = (TweenMain)this.Owner;
1373 var tbox = (TextBox)sender;
1374 if (e.KeyChar == '@')
1377 main.ShowSuplDialog(tbox, main.AtIdSupl);
1380 else if (e.KeyChar == '#')
1382 main.ShowSuplDialog(tbox, main.HashSupl);
1387 private void ListFilters_DrawItem(object sender, DrawItemEventArgs e)
1393 var filter = (PostFilterRule)this.ListFilters.Items[e.Index];
1394 var isSelected = e.State.HasFlag(DrawItemState.Selected);
1398 textBrush = SystemBrushes.HighlightText;
1399 else if (filter.Enabled)
1400 textBrush = SystemBrushes.WindowText;
1402 textBrush = SystemBrushes.GrayText;
1404 e.Graphics.DrawString(filter.ToString(), e.Font, textBrush, e.Bounds);
1407 e.DrawFocusRectangle();
1410 private void ListFilters_KeyDown(object sender, KeyEventArgs e)
1412 if (e.Control && e.KeyCode == Keys.A)
1414 var itemCount = this.ListFilters.Items.Count;
1415 if (itemCount == 0) return;
1417 using (ControlTransaction.Update(this.ListFilters))
1423 this.multiSelState |= MultiSelectionState.SelectAll;
1425 for (var i = 1; i < itemCount; i++)
1427 this.ListFilters.SetSelected(i, true);
1432 this.multiSelState &= ~MultiSelectionState.SelectAll;
1436 this.ListFilters.SetSelected(0, true);
1441 protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
1443 base.ScaleControl(factor, specified);
1444 this.ListFilters.ItemHeight = this.ListFilters.Font.Height;