diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..ad475e3ad --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,182 @@ +name: Build + +on: + push: + branches: ['develop', 'release'] + pull_request: + +env: + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages + +jobs: + build: + runs-on: windows-2022 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.1 + + - name: Set configuration env + shell: pwsh + run: | + if ($env:GITHUB_REF -eq 'refs/heads/release') { + echo 'CONFIGURATION=Release' >> $env:GITHUB_ENV + } else { + echo 'CONFIGURATION=Debug' >> $env:GITHUB_ENV + } + + - uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/.nuget/packages + key: nuget-${{ hashFiles('*/*.csproj') }} + restore-keys: | + nuget- + + - name: Build + shell: pwsh + run: | + msbuild /target:restore,build "/p:Configuration=$($env:CONFIGURATION)" /verbosity:minimal + + - name: Upload build result + uses: actions/upload-artifact@v3 + with: + name: build + path: | + ./OpenTween/bin/ + ./OpenTween/obj/ + ./OpenTween.Tests/bin/ + retention-days: 1 + + test: + runs-on: windows-2022 + needs: [build] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.1 + + - name: Set configuration env + shell: pwsh + run: | + if ($env:GITHUB_REF -eq 'refs/heads/release') { + echo 'CONFIGURATION=Release' >> $env:GITHUB_ENV + } else { + echo 'CONFIGURATION=Debug' >> $env:GITHUB_ENV + } + + - uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/.nuget/packages + key: nuget-${{ hashFiles('*/*.csproj') }} + restore-keys: | + nuget- + + - name: Restore build result + uses: actions/download-artifact@v3 + with: + name: build + + - name: Run tests + shell: pwsh + run: | + $altCoverVersion = '8.2.837' + $xunitVersion = '2.4.1' + $targetFramework = 'net472' + $altCoverPath = "$($env:NUGET_PACKAGES)\altcover\$($altCoverVersion)\tools\$($targetFramework)\AltCover.exe" + $xunitPath = "$($env:NUGET_PACKAGES)\xunit.runner.console\$($xunitVersion)\tools\$($targetFramework)\xunit.console.exe" + + $p = Start-Process ` + -FilePath $altCoverPath ` + -ArgumentList ( + '--inputDirectory', + ".\OpenTween.Tests\bin\$($env:CONFIGURATION)\$($targetFramework)", + '--outputDirectory', + '.\__Instrumented\', + '--assemblyFilter', + '?^OpenTween(?!\.Tests)', + '--typeFilter', + '?^OpenTween\.', + '--fileFilter', + '\.Designer\.cs', + '--visibleBranches' + ) ` + -NoNewWindow ` + -PassThru ` + -Wait + + if ($p.ExitCode -ne 0) { + exit $p.ExitCode + } + + $p = Start-Process ` + -FilePath $altCoverPath ` + -ArgumentList ( + 'runner', + '--recorderDirectory', + '.\__Instrumented\', + '--executable', + $xunitPath, + '--', + '.\__Instrumented\OpenTween.Tests.dll' + ) ` + -NoNewWindow ` + -PassThru ` + -Wait + + if ($p.ExitCode -ne 0) { + exit $p.ExitCode + } + + - name: Upload test results to codecov + shell: pwsh + run: | + Invoke-WebRequest -Uri https://uploader.codecov.io/latest/windows/codecov.exe -Outfile codecov.exe + .\codecov.exe -f coverage.xml + + package: + runs-on: windows-2022 + needs: [build] + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: '${{ github.event.pull_request.head.sha }}' + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.1 + + - name: Set configuration env + shell: pwsh + run: | + if ($env:GITHUB_REF -eq 'refs/heads/release') { + echo 'CONFIGURATION=Release' >> $env:GITHUB_ENV + } else { + echo 'CONFIGURATION=Debug' >> $env:GITHUB_ENV + } + + - name: Restore build result + uses: actions/download-artifact@v3 + with: + name: build + + - name: Build package + shell: powershell # runtime-versionを取得するため従来のPowershellを使用する + run: | + $env:PATH = $env:PATH + ';C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\Roslyn\' + $binDir = '.\OpenTween\bin\' + $env:CONFIGURATION + '\net472\' + $destPath = 'OpenTween.zip' + $headCommit = '${{ github.event.pull_request.head.sha }}' + .\tools\build-zip-archive.ps1 -BinDir $binDir -DestPath $destPath -HeadCommit $headCommit + Copy-Item ($binDir + 'OpenTween.pdb') -Destination '.\' + + - name: Upload build result + uses: actions/upload-artifact@v3 + with: + name: package + path: | + ./OpenTween.zip + ./OpenTween.pdb diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 149d75c3f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: csharp -solution: OpenTween.sln - -sudo: false - -cache: - directories: - - ./packages/ - -before_script: - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start - -script: - - nuget restore -PackagesDirectory packages - - msbuild /p:Configuration=Debug /verbosity:quiet - - mono ./packages/xunit.runner.console/2.3.1/tools/net452/xunit.console.exe ./OpenTween.Tests/bin/Debug/net472/OpenTween.Tests.dll -appdomains denied -parallel none diff --git a/.tx/config b/.tx/config deleted file mode 100644 index c0c8599df..000000000 --- a/.tx/config +++ /dev/null @@ -1,99 +0,0 @@ -[main] -host = https://www.transifex.com -type = RESX - -[opentween.global] -file_filter = OpenTween/Properties/Resources..resx -source_file = OpenTween/Properties/Resources.resx -source_lang = ja - -[opentween.AppendSettingDialog] -file_filter = OpenTween/AppendSettingDialog..resx -source_file = OpenTween/AppendSettingDialog.resx -source_lang = ja - -[opentween.AtIdSupplement] -file_filter = OpenTween/AtIdSupplement..resx -source_file = OpenTween/AtIdSupplement.resx -source_lang = ja - -[opentween.AuthDialog] -file_filter = OpenTween/AuthDialog..resx -source_file = OpenTween/AuthDialog.resx -source_lang = ja - -[opentween.EventViewerDialog] -file_filter = OpenTween/EventViewerDialog..resx -source_file = OpenTween/EventViewerDialog.resx -source_lang = ja - -[opentween.FilterDialog] -file_filter = OpenTween/FilterDialog..resx -source_file = OpenTween/FilterDialog.resx -source_lang = ja - -[opentween.FormInfo] -file_filter = OpenTween/FormInfo..resx -source_file = OpenTween/FormInfo.resx -source_lang = ja - -[opentween.HashtagManage] -file_filter = OpenTween/HashtagManage..resx -source_file = OpenTween/HashtagManage.resx -source_lang = ja - -[opentween.InputTabName] -file_filter = OpenTween/InputTabName..resx -source_file = OpenTween/InputTabName.resx -source_lang = ja - -[opentween.ListAvailable] -file_filter = OpenTween/ListAvailable..resx -source_file = OpenTween/ListAvailable.resx -source_lang = ja - -[opentween.ListManage] -file_filter = OpenTween/ListManage..resx -source_file = OpenTween/ListManage.resx -source_lang = ja - -[opentween.MyLists] -file_filter = OpenTween/MyLists..resx -source_file = OpenTween/MyLists.resx -source_lang = ja - -[opentween.OpenURL] -file_filter = OpenTween/OpenURL..resx -source_file = OpenTween/OpenURL.resx -source_lang = ja - -[opentween.SearchWord] -file_filter = OpenTween/SearchWord..resx -source_file = OpenTween/SearchWord.resx -source_lang = ja - -[opentween.ShowUserInfo] -file_filter = OpenTween/ShowUserInfo..resx -source_file = OpenTween/ShowUserInfo.resx -source_lang = ja - -[opentween.TabsDialog] -file_filter = OpenTween/TabsDialog..resx -source_file = OpenTween/TabsDialog.resx -source_lang = ja - -[opentween.Tween] -file_filter = OpenTween/Tween..resx -source_file = OpenTween/Tween.resx -source_lang = ja - -[opentween.TweenAboutBox] -file_filter = OpenTween/TweenAboutBox..resx -source_file = OpenTween/TweenAboutBox.resx -source_lang = ja - -[opentween.UpdateDialog] -file_filter = OpenTween/UpdateDialog..resx -source_file = OpenTween/UpdateDialog.resx -source_lang = ja - diff --git a/OpenTween.Tests/TweetExtractorTest.cs b/OpenTween.Tests/TweetExtractorTest.cs index 942440d46..29aa41ec0 100644 --- a/OpenTween.Tests/TweetExtractorTest.cs +++ b/OpenTween.Tests/TweetExtractorTest.cs @@ -191,7 +191,7 @@ public void ExtractEmojiEntities_Test() Assert.Equal(new[] { 5, 6 }, entity.Indices); Assert.Equal("✨", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/2728.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/2728.png", entity.Url); } [Fact] @@ -202,7 +202,7 @@ public void ExtractEmojiEntities_SurrogatePairTest() // 「𠮷」「🍚」は UTF-16 でそれぞれ 2byte になるがインデックスはコードポイント単位で数えなければならない Assert.Equal(new[] { 4, 5 }, entity.Indices); Assert.Equal("🍚", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f35a.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f35a.png", entity.Url); } [Fact] @@ -224,7 +224,7 @@ public void ExtractEmojiEntities_VariationSelector_EmojiStyleTest() Assert.Equal(new[] { 0, 2 }, entity.Indices); Assert.Equal("©", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/a9.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/a9.png", entity.Url); } [Fact] @@ -238,7 +238,7 @@ public void ExtractEmojiEntities_VariationSelector_UnnecessaryEmojiStyleTest() Assert.Equal(new[] { 0, 1 }, entities[0].Indices); Assert.Equal("🍣", entities[0].Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f363.png", entities[0].Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f363.png", entities[0].Url); Assert.Equal(new[] { 1, 2 }, entities[1].Indices); Assert.Equal("", entities[1].Text); @@ -253,7 +253,7 @@ public void ExtractEmojiEntities_CombiningCharacterTest() Assert.Equal(new[] { 0, 2 }, entity.Indices); Assert.Equal("#⃣", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/23-20e3.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/23-20e3.png", entity.Url); } [Fact] @@ -265,7 +265,7 @@ public void ExtractEmojiEntities_Unicode10Test() Assert.Equal(new[] { 0, 1 }, entity.Indices); Assert.Equal("🦒", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f992.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f992.png", entity.Url); } [Fact] @@ -277,7 +277,7 @@ public void ExtractEmojiEntities_Unicode11Test() Assert.Equal(new[] { 0, 1 }, entity.Indices); Assert.Equal("🦸", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f9b8.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f9b8.png", entity.Url); } [Fact] @@ -289,7 +289,7 @@ public void ExtractEmojiEntities_Unicode12Test() Assert.Equal(new[] { 0, 1 }, entity.Indices); Assert.Equal("🧅", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f9c5.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f9c5.png", entity.Url); } [Fact] @@ -301,7 +301,7 @@ public void ExtractEmojiEntities_Unicode13Test() Assert.Equal(new[] { 0, 1 }, entity.Indices); Assert.Equal("🥷", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f977.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f977.png", entity.Url); } [Fact] @@ -313,7 +313,7 @@ public void ExtractEmojiEntities_Unicode14Test() Assert.Equal(new[] { 0, 1 }, entity.Indices); Assert.Equal("🫠", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1fae0.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1fae0.png", entity.Url); } [Fact] @@ -325,7 +325,7 @@ public void ExtractEmojiEntities_EmojiModifiers_CombiningTest() Assert.Equal(new[] { 0, 2 }, entity.Indices); Assert.Equal("👦\U0001F3FF", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f466-1f3ff.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f466-1f3ff.png", entity.Url); } [Fact] @@ -337,7 +337,7 @@ public void ExtractEmojiEntities_EmojiModifiers_SingleTest() Assert.Equal(new[] { 0, 1 }, entity.Indices); Assert.Equal("\U0001F3FF", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f3ff.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f3ff.png", entity.Url); } [Fact] @@ -349,7 +349,7 @@ public void ExtractEmojiEntities_EmojiZWJSequenceTest() Assert.Equal(new[] { 0, 3 }, entity.Indices); Assert.Equal("👨\u200D🎨", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3a8.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f468-200d-1f3a8.png", entity.Url); } [Fact] @@ -362,7 +362,7 @@ public void ExtractEmojiEntities_EmojiZWJSequenceWithVariationSelectorTest() Assert.Equal(new[] { 0, 4 }, entity.Indices); Assert.Equal("🏃\u200D♀\uFE0F", entity.Text); - Assert.Equal("https://twemoji.maxcdn.com/2/72x72/1f3c3-200d-2640-fe0f.png", entity.Url); + Assert.Equal("https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f3c3-200d-2640-fe0f.png", entity.Url); } } } diff --git a/OpenTween.Tests/TweetFormatterTest.cs b/OpenTween.Tests/TweetFormatterTest.cs index c8c8ebaa9..8902c96a5 100644 --- a/OpenTween.Tests/TweetFormatterTest.cs +++ b/OpenTween.Tests/TweetFormatterTest.cs @@ -175,11 +175,11 @@ public void FormatEmojiEntity_Test() { Indices = new[] { 0, 1 }, Text = "🍣", - Url = "https://twemoji.maxcdn.com/2/72x72/1f363.png", + Url = "https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/1f363.png", }, }; - var expected = "\"🍣\""; + var expected = "\"🍣\""; Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities)); } diff --git a/OpenTween/HashtagManage.resx b/OpenTween/HashtagManage.resx index a5275e067..2869bcdab 100644 --- a/OpenTween/HashtagManage.resx +++ b/OpenTween/HashtagManage.resx @@ -19,25 +19,17 @@ System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 7 Cancel_Button - Cancel_Button - TableLayoutButtons TableLayoutButtons System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 0 0 CheckNotAddToAtReply $this System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 2 CheckPermanent - CheckPermanent GroupHashtag - GroupHashtag - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 8 - 8 DeleteButton GroupHashtag System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 @@ -59,25 +51,17 @@ System.Windows.Forms.ListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 0 Label1 - Label1 - GroupDetail GroupDetail System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 2 2 Label3 GroupHashtag System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 1 OK_Button - OK_Button TableLayoutButtons - TableLayoutButtons - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 1 - 1 PermCancel_Button TableLayoutPanel2 System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 @@ -107,12 +91,8 @@ System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 2 UseHashText - UseHashText - GroupDetail GroupDetail System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 1 1 NoControl 240, 18 diff --git a/OpenTween/ImageCache.cs b/OpenTween/ImageCache.cs index 76bfb639e..335c49c12 100644 --- a/OpenTween/ImageCache.cs +++ b/OpenTween/ImageCache.cs @@ -46,11 +46,6 @@ public class ImageCache : IDisposable /// private CancellationTokenSource cancelTokenSource; - /// - /// innerDictionary の排他制御のためのロックオブジェクト - /// - private readonly object lockObject = new(); - /// /// オブジェクトが破棄された否か /// @@ -89,25 +84,17 @@ public Task DownloadImageAsync(string address, bool force = false) { var cancelToken = this.cancelTokenSource.Token; - return Task.Run(() => - { - Task? cachedImageTask; - lock (this.lockObject) - this.InnerDictionary.TryGetValue(address, out cachedImageTask); - - if (cachedImageTask != null && !force) - return cachedImageTask; + this.InnerDictionary.TryGetValue(address, out var cachedImageTask); - cancelToken.ThrowIfCancellationRequested(); + if (cachedImageTask != null && !force) + return cachedImageTask; - var imageTask = this.FetchImageAsync(address, cancelToken); + cancelToken.ThrowIfCancellationRequested(); - lock (this.lockObject) - this.InnerDictionary[address] = imageTask; + var imageTask = Task.Run(() => this.FetchImageAsync(address, cancelToken)); + this.InnerDictionary[address] = imageTask; - return imageTask; - }, - cancelToken); + return imageTask; } private async Task FetchImageAsync(string uri, CancellationToken cancelToken) @@ -126,14 +113,11 @@ private async Task FetchImageAsync(string uri, CancellationToken ca public MemoryImage? TryGetFromCache(string address) { - lock (this.lockObject) - { - if (!this.InnerDictionary.TryGetValue(address, out var imageTask) || - imageTask.Status != TaskStatus.RanToCompletion) - return null; + if (!this.InnerDictionary.TryGetValue(address, out var imageTask) || + imageTask.Status != TaskStatus.RanToCompletion) + return null; - return imageTask.Result; - } + return imageTask.Result; } public MemoryImage? TryGetLargerOrSameSizeFromCache(string normalUrl, string size) @@ -154,14 +138,11 @@ private async Task FetchImageAsync(string uri, CancellationToken ca public void CancelAsync() { - lock (this.lockObject) - { - var oldTokenSource = this.cancelTokenSource; - this.cancelTokenSource = new CancellationTokenSource(); + var oldTokenSource = this.cancelTokenSource; + this.cancelTokenSource = new CancellationTokenSource(); - oldTokenSource.Cancel(); - oldTokenSource.Dispose(); - } + oldTokenSource.Cancel(); + oldTokenSource.Dispose(); } protected virtual void Dispose(bool disposing) @@ -172,17 +153,14 @@ protected virtual void Dispose(bool disposing) { this.CancelAsync(); - lock (this.lockObject) + foreach (var (_, task) in this.InnerDictionary) { - foreach (var (_, task) in this.InnerDictionary) - { - if (task.Status == TaskStatus.RanToCompletion) - task.Result?.Dispose(); - } - - this.InnerDictionary.Clear(); - this.cancelTokenSource.Dispose(); + if (task.Status == TaskStatus.RanToCompletion) + task.Result?.Dispose(); } + + this.InnerDictionary.Clear(); + this.cancelTokenSource.Dispose(); } this.disposed = true; diff --git a/OpenTween/ListAvailable.resx b/OpenTween/ListAvailable.resx index 69d9544b4..108f36a46 100644 --- a/OpenTween/ListAvailable.resx +++ b/OpenTween/ListAvailable.resx @@ -15,12 +15,8 @@ ListAvailable OpenTween.OTBaseForm, OpenTween, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null Cancel_Button - Cancel_Button - TableLayoutPanel1 TableLayoutPanel1 System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 1 1 DescriptionText $this @@ -63,12 +59,8 @@ System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 10 OK_Button - OK_Button - TableLayoutPanel1 TableLayoutPanel1 System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 0 0 RefreshButton $this diff --git a/OpenTween/ListManage.resx b/OpenTween/ListManage.resx index 17cb49b93..9f8d3267e 100644 --- a/OpenTween/ListManage.resx +++ b/OpenTween/ListManage.resx @@ -19,12 +19,8 @@ System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 7 CancelEditButton - CancelEditButton - ListGroup ListGroup System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 1 1 CloseButton $this @@ -39,52 +35,32 @@ System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 4 DescriptionText - DescriptionText - ListGroup ListGroup System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 11 11 EditCheckBox $this System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 1 GetMoreUsersButton - GetMoreUsersButton - MemberGroup MemberGroup System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 1 1 GroupBox2 ListGroup System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 2 Label1 - Label1 ListGroup - ListGroup - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 0 - 0 - Label10 Label10 ListGroup - ListGroup - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 10 - 10 Label12 - Label12 - ListGroup ListGroup System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 12 12 Label13 UserGroup @@ -107,29 +83,17 @@ System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 3 Label4 - Label4 - ListGroup ListGroup System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 8 8 Label5 - Label5 - UserGroup UserGroup System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 16 16 Label6 - Label6 ListGroup - ListGroup - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 9 - 9 Label8 UserGroup System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 @@ -155,32 +119,20 @@ System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 6 NameTextBox - NameTextBox - ListGroup ListGroup System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 7 - 7 - OKEditButton OKEditButton ListGroup - ListGroup - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 3 - 3 PrivateRadioButton GroupBox2 System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 0 PublicRadioButton - PublicRadioButton - GroupBox2 GroupBox2 System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 1 1 RefreshListsButton $this @@ -211,13 +163,9 @@ OpenTween.OTPictureBox, OpenTween, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null 1 UserList - UserList MemberGroup - MemberGroup - System.Windows.Forms.ListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Windows.Forms.ListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 0 - 0 UserLocation UserGroup System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/OpenTween/OpenTween.csproj b/OpenTween/OpenTween.csproj index cb4691fdf..8582dfe3a 100644 --- a/OpenTween/OpenTween.csproj +++ b/OpenTween/OpenTween.csproj @@ -1,41 +1,25 @@ - - - + + - Debug - AnyCPU - {3D8995C7-BDF3-4273-9F9D-DDD902F6A101} WinExe - OpenTween - OpenTween - v4.7.2 + true + net472 10.0 - 512 - true + bin\$(Configuration)\ + true + false + true $(MSBuildProjectDirectory)=. - true - full - false - bin\Debug\ - TRACE;DEBUG - prompt - 4 - Auto - false + portable + Off OpenTween.ruleset - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - On + portable + Off false - false OpenTween.ApplicationEvents @@ -62,666 +46,526 @@ - + Form - + ApiInfoDialog.cs - - - - - - - - - - - - - + Code - - - - - - - - - - - - - - - - - - - - - - + Code - - - - - - - - + Form - + AtIdSupplement.cs - + Form - + AppendSettingDialog.cs - + Form - + AuthDialog.cs - - - - - - - - - - - - - - - - - - - + Form - + EncryptApiKeyDialog.cs - - + Form - + FilterDialog.cs - - + Form - + LoginDialog.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - + Form - + SendErrorReportForm.cs - - - - - - - - - + UserControl - + TweetDetailsView.cs - - + Form - + WaitingDialog.cs - + Form - + InputDialog.cs - + UserControl - + MediaSelector.cs - + Code - + Form - + Component - - + Form - + HashtagManage.cs - - + Form - + ListAvailable.cs - - Form - - + + ListManage.cs - - + Component - - - + Component - - + UserControl - + ActionPanel.cs - + UserControl - + BasedPanel.cs - + UserControl - + ConnectionPanel.cs - + UserControl - + CooperatePanel.cs - + UserControl - + FontPanel.cs - + UserControl - + FontPanel2.cs - + UserControl - + GetCountPanel.cs - + UserControl - + GetPeriodPanel.cs - + UserControl - + NotifyPanel.cs - + UserControl - + PreviewPanel.cs - + UserControl - + ProxyPanel.cs - + UserControl - + SettingPanelBase.cs - + UserControl - + ShortUrlPanel.cs - + UserControl - + StartupPanel.cs - + UserControl - + TweetActPanel.cs - + UserControl - + TweetPrvPanel.cs - - + Form - + TabsDialog.cs - - - - - - - - - - - - - - - - - + UserControl - + TweetThumbnail.cs - + Form - + UpdateDialog.cs - - - + Form - + InputTabName.cs - - - - - - + Form - + MyLists.cs - - + Form - + OpenURL.cs - - + True True Resources.resx - + Form - + SearchWordDialog.cs - - - - - - - + Form - + UserInfoDialog.cs - + Component - + Form - + Tween.cs - + Form - + TweenAboutBox.cs - + Component - - - - - - + ApiInfoDialog.cs - + AuthDialog.cs - + AuthDialog.cs - + EncryptApiKeyDialog.cs - + FilterDialog.cs - + FilterDialog.cs - + HashtagManage.cs - + HashtagManage.cs - + InputDialog.cs - + InputDialog.cs - + ListAvailable.cs - + ListAvailable.cs - + ListManage.cs - + ListManage.cs - + AtIdSupplement.cs - + AtIdSupplement.cs - + AppendSettingDialog.cs Designer - + AppendSettingDialog.cs Designer - + LoginDialog.cs - + LoginDialog.cs - + MediaSelector.cs - + MediaSelector.cs - + SendErrorReportForm.cs - + SendErrorReportForm.cs - + TweetDetailsView.cs Designer - + TweetDetailsView.cs - + WaitingDialog.cs - + ActionPanel.cs - + ActionPanel.cs - + BasedPanel.cs - + BasedPanel.cs - + ConnectionPanel.cs - + ConnectionPanel.cs - + CooperatePanel.cs - + CooperatePanel.cs - + FontPanel.cs - + FontPanel.cs - + FontPanel2.cs - + FontPanel2.cs - + GetCountPanel.cs - + GetCountPanel.cs - + GetPeriodPanel.cs - + GetPeriodPanel.cs - + NotifyPanel.cs - + NotifyPanel.cs - + PreviewPanel.cs - + PreviewPanel.cs - + ProxyPanel.cs - + ProxyPanel.cs - + ShortUrlPanel.cs - + ShortUrlPanel.cs - + StartupPanel.cs - + StartupPanel.cs - + TweetActPanel.cs - + TweetActPanel.cs - + TweetPrvPanel.cs - + TweetPrvPanel.cs - + TabsDialog.cs - + TabsDialog.cs - + TweetThumbnail.cs - + TweetThumbnail.cs - + UpdateDialog.cs - + UpdateDialog.cs - + InputTabName.cs - + InputTabName.cs - + MyLists.cs - + MyLists.cs - + OpenURL.cs - + OpenURL.cs - + ResXFileCodeGenerator Resources.Designer.cs Designer - + Designer - + SearchWordDialog.cs - + SearchWordDialog.cs - + UserInfoDialog.cs - + UserInfoDialog.cs - + Tween.cs Designer - + Tween.cs Designer - + TweenAboutBox.cs - + TweenAboutBox.cs @@ -799,24 +643,12 @@ all - - + + + + + - - - - - - - - - \ No newline at end of file diff --git a/OpenTween/OpenURL.resx b/OpenTween/OpenURL.resx index 73765a39c..8a3e89a83 100644 --- a/OpenTween/OpenURL.resx +++ b/OpenTween/OpenURL.resx @@ -17,12 +17,8 @@ OpenURL OpenTween.OTBaseForm, OpenTween, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null Cancel_Button - Cancel_Button - TableLayoutPanel1 TableLayoutPanel1 System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 1 1 OK_Button TableLayoutPanel1 diff --git a/OpenTween/Properties/AssemblyInfo.cs b/OpenTween/Properties/AssemblyInfo.cs index 41ecf4222..7cfef6709 100644 --- a/OpenTween/Properties/AssemblyInfo.cs +++ b/OpenTween/Properties/AssemblyInfo.cs @@ -22,7 +22,7 @@ // 次の GUID は、このプロジェクトが COM に公開される場合の、typelib の ID です [assembly: Guid("2d0ae0ba-adac-49a2-9b10-26fd69e695bf")] -[assembly: AssemblyVersion("2.7.1.0")] +[assembly: AssemblyVersion("2.8.0.0")] [assembly: InternalsVisibleTo("OpenTween.Tests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // for Moq diff --git a/OpenTween/Properties/Resources.Designer.cs b/OpenTween/Properties/Resources.Designer.cs index baa0084e9..e7b61b1cb 100644 --- a/OpenTween/Properties/Resources.Designer.cs +++ b/OpenTween/Properties/Resources.Designer.cs @@ -571,6 +571,11 @@ internal static string ChangeIconToolStripMenuItem_Confirm { /// /// 更新履歴 /// + ///==== Ver 2.8.0(2023/01/11) + /// * NEW: サムネイル画像のコンテキストメニューに「画像をコピー」を追加 (thx @kzlogos!) + /// * NEW: Segoe UI Emoji によるカラー絵文字の描画に対応(発言詳細欄のみ) + /// * CHG: Twemojiの画像の取得元をMaxCDNからCloudflareに移行 (thx @sou_niyari!) + /// ///==== Ver 2.7.1(2022/09/03) /// * FIX: 発言一覧の選択位置を移動した際にデッドロックが発生する場合がある不具合を修正 (thx @Kazuki_Ashiya!) /// * FIX: 発言本文の翻訳時に発生したエラーが適切に処理されない不具合を修正 @@ -578,11 +583,7 @@ internal static string ChangeIconToolStripMenuItem_Confirm { ///==== Ver 2.7.0(2022/07/30) /// * NEW: 発言詳細部の日時ラベルをクリックするとWebブラウザを起動してツイートを表示する機能を追加 /// * NEW: 設定画面に「Twitter API v2 の使用を有効にする」のチェックボックスを追加 - /// - デフォルトで API v2 が有効となり、通常は変更する必要はありません - /// - Twitter の API キーを独自に書き換えている場合で、Project への移行を完了できていない等の理由で API v2 を使用できない時はチェックを外してください - /// * CHG: 発言詳細部の名前ラベルを投稿者とRTしたユーザーで分けずに表示するように変更 - /// * FIX: タブの移動後に発言一覧が空の表示になる不具合を修正 - /// * FIX: [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 + /// - デフォルトで API v2 が有効となり、 [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// internal static string ChangeLog { get { diff --git a/OpenTween/Resources/ChangeLog.txt b/OpenTween/Resources/ChangeLog.txt index 533eb13a4..fe0207093 100644 --- a/OpenTween/Resources/ChangeLog.txt +++ b/OpenTween/Resources/ChangeLog.txt @@ -1,5 +1,10 @@ 更新履歴 +==== Ver 2.8.0(2023/01/11) + * NEW: サムネイル画像のコンテキストメニューに「画像をコピー」を追加 (thx @kzlogos!) + * NEW: Segoe UI Emoji によるカラー絵文字の描画に対応(発言詳細欄のみ) + * CHG: Twemojiの画像の取得元をMaxCDNからCloudflareに移行 (thx @sou_niyari!) + ==== Ver 2.7.1(2022/09/03) * FIX: 発言一覧の選択位置を移動した際にデッドロックが発生する場合がある不具合を修正 (thx @Kazuki_Ashiya!) * FIX: 発言本文の翻訳時に発生したエラーが適切に処理されない不具合を修正 diff --git a/OpenTween/Setting/SettingAtIdList.cs b/OpenTween/Setting/SettingAtIdList.cs index 08ab3c4f9..2fdecf0c2 100644 --- a/OpenTween/Setting/SettingAtIdList.cs +++ b/OpenTween/Setting/SettingAtIdList.cs @@ -30,9 +30,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Xml.Serialization; namespace OpenTween { + [XmlSerializerAssembly(null, null)] // OpenTween アセンブリ内の XmlSerializerContract を使用させる public class SettingAtIdList : SettingBase { #region Settingクラス基本 diff --git a/OpenTween/Setting/SettingCommon.cs b/OpenTween/Setting/SettingCommon.cs index f89f4af8b..a3131e527 100644 --- a/OpenTween/Setting/SettingCommon.cs +++ b/OpenTween/Setting/SettingCommon.cs @@ -34,6 +34,7 @@ namespace OpenTween { + [XmlSerializerAssembly(null, null)] // OpenTween アセンブリ内の XmlSerializerContract を使用させる public class SettingCommon : SettingBase { #region "Settingクラス基本" diff --git a/OpenTween/Setting/SettingLocal.cs b/OpenTween/Setting/SettingLocal.cs index ec3ee5cf1..8c085b0f1 100644 --- a/OpenTween/Setting/SettingLocal.cs +++ b/OpenTween/Setting/SettingLocal.cs @@ -36,6 +36,7 @@ namespace OpenTween { + [XmlSerializerAssembly(null, null)] // OpenTween アセンブリ内の XmlSerializerContract を使用させる public class SettingLocal : SettingBase { #region Settingクラス基本 diff --git a/OpenTween/Setting/SettingTabs.cs b/OpenTween/Setting/SettingTabs.cs index c40d091eb..f8974b99a 100644 --- a/OpenTween/Setting/SettingTabs.cs +++ b/OpenTween/Setting/SettingTabs.cs @@ -35,6 +35,7 @@ namespace OpenTween { + [XmlSerializerAssembly(null, null)] // OpenTween アセンブリ内の XmlSerializerContract を使用させる public class SettingTabs : SettingBase { #region Settingクラス基本 diff --git a/OpenTween/TimelineListViewDrawer.cs b/OpenTween/TimelineListViewDrawer.cs index 1e2ae355f..02b82be57 100644 --- a/OpenTween/TimelineListViewDrawer.cs +++ b/OpenTween/TimelineListViewDrawer.cs @@ -147,65 +147,54 @@ private void DrawListViewItemIcon(DrawListViewItemEventArgs e) iconRect.Offset(0, Math.Max(0, (itemRect.Height - scaledIconSize.Height) / 2)); var post = this.tab[item.Index]; - var img = this.LoadListViewIconLazy(post, scaledIconSize.Width); - if (img != null) - { - e.Graphics.FillRectangle(Brushes.White, iconRect); - e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; - try - { - e.Graphics.DrawImage(img.Image, iconRect); - } - catch (ArgumentException) - { - } - } + this.DrawListViewItemProfileImage(e.Graphics, post, scaledIconSize, iconRect); - if (post.StateIndex > -1) - { - var stateRect = Rectangle.Intersect(new Rectangle(new Point(iconRect.X + scaledIconSize.Width + 2, iconRect.Y), scaledStateSize), itemRect); - if (stateRect.Width > 0) - e.Graphics.DrawIcon(this.GetPostStateIcon(post.StateIndex), stateRect); - } + var stateRect = Rectangle.Intersect(new Rectangle(new Point(iconRect.X + scaledIconSize.Width + 2, iconRect.Y), scaledStateSize), itemRect); + this.DrawListViewItemStateIcon(e.Graphics, post, stateRect); + } + + private void DrawListViewItemStateIcon(Graphics g, PostClass post, Rectangle stateRect) + { + if (post.StateIndex == -1) + return; + + if (stateRect.Width <= 0) + return; + + g.DrawIcon(this.GetPostStateIcon(post.StateIndex), stateRect); } - private MemoryImage? LoadListViewIconLazy(PostClass post, int scaledIconSize) + private void DrawListViewItemProfileImage(Graphics g, PostClass post, Size scaledIconSize, Rectangle iconRect) { - if (scaledIconSize <= 0) - return null; + if (scaledIconSize.Width <= 0) + return; var normalImageUrl = post.ImageUrl; if (MyCommon.IsNullOrEmpty(normalImageUrl)) - return null; + return; - var sizeName = Twitter.DecideProfileImageSize(scaledIconSize); + var sizeName = Twitter.DecideProfileImageSize(scaledIconSize.Width); var cachedImage = this.iconCache.TryGetLargerOrSameSizeFromCache(normalImageUrl, sizeName); - if (cachedImage != null) - return cachedImage; - // キャッシュにない画像の場合は読み込みが完了してから再描画する - _ = Task.Run(async () => + if (cachedImage != null) { + g.FillRectangle(Brushes.White, iconRect); + g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; try { - var imageUrl = Twitter.CreateProfileImageUrl(normalImageUrl, sizeName); - await this.iconCache.DownloadImageAsync(imageUrl); - } - catch (InvalidImageException) - { - return; + g.DrawImage(cachedImage.Image, iconRect); } - catch (HttpRequestException) + catch (ArgumentException) { - return; } - catch (OperationCanceledException) + } + else + { + // キャッシュにない画像の場合は読み込みが完了してから再描画する + async Task RefreshProfileImageLazy() { - return; - } + await this.LoadProfileImage(normalImageUrl, sizeName); - await this.parentForm.InvokeAsync(() => - { if (this.listView.IsDisposed) return; @@ -216,10 +205,31 @@ await this.parentForm.InvokeAsync(() => var newIndex = this.tab.IndexOf(post.StatusId); if (newIndex != -1) this.listView.RedrawItems(newIndex, newIndex, true); - }); - }); + } + + _ = RefreshProfileImageLazy(); + } + } - return null; + private async Task LoadProfileImage(string normalImageUrl, string sizeName) + { + try + { + var imageUrl = Twitter.CreateProfileImageUrl(normalImageUrl, sizeName); + await this.iconCache.DownloadImageAsync(imageUrl); + } + catch (InvalidImageException) + { + return; + } + catch (HttpRequestException) + { + return; + } + catch (OperationCanceledException) + { + return; + } } private Icon GetPostStateIcon(int stateIndex) diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index 5cc1ace7e..5e65deaf5 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -101,7 +101,7 @@ public partial class TweenMain : OTBaseForm "" + "