Add source for media player

This commit is contained in:
daleclack 2024-03-16 21:56:25 +08:00
parent 26cb6dd5f8
commit 205b29dc1a
40 changed files with 1745 additions and 3 deletions

View File

@ -31,7 +31,8 @@ set(SOURCES src/core/main.cpp src/core/MainWin.cpp src/core/MyStack.cpp
src/game24_app/Game24.cpp src/game24_app/Game24App.cpp src/text_app/TextEditor.cpp
src/text_app/MyInfoBar.cpp src/image_app/ImageApp.cpp src/image_app/MyImage.cpp
src/mine_app/MineSweeper.cpp src/mine_app/MineCell.cpp src/mine_app/InputBox.cpp
src/mine_app/ScoresItem.cpp src/mine_app/ScoresWin.cpp)
src/mine_app/ScoresItem.cpp src/mine_app/ScoresWin.cpp src/media_app/LyricsParser.cpp
src/media_app/MyMediaPlayer.cpp src/media_app/MediaItem.cpp)
#Compile resources with GCR_CMake

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8 4 L 3 10 L 13 10 L 8 4 z M 3 11 L 3 12 L 13 12 L 13 11 L 3 11 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 404 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8 4 L 3 10 L 13 10 L 8 4 z M 3 11 L 3 12 L 13 12 L 13 11 L 3 11 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 404 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 3 4 L 8 10 L 13 4 L 3 4 z M 3 11 L 3 12 L 13 12 L 13 11 L 3 11 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 405 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 3 4 L 8 10 L 13 4 L 3 4 z M 3 11 L 3 12 L 13 12 L 13 11 L 3 11 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 405 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 4 0 0 -10 z m 6 0 0 10 4 0 0 -10 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 382 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 4 0 0 -10 z m 6 0 0 10 4 0 0 -10 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 382 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 10 -5 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 355 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 10 -5 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 355 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 10 0 0 -10 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 360 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 10 0 0 -10 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 360 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 3 2 A 1 1 0 0 0 2 3 A 1 1 0 0 0 3 4 A 1 1 0 0 0 4 3 A 1 1 0 0 0 3 2 z M 5 2 L 5 3 L 7 3 L 13 3 L 13 2 L 7 2 L 5 2 z M 5 4 L 5 5 L 7 5 L 11 5 L 11 4 L 7 4 L 5 4 z M 10 7 L 10 10 L 7 10 L 7 11 L 10 11 L 10 14 L 11 14 L 11 11 L 14 11 L 14 10 L 11 10 L 11 7 L 10 7 z M 3 8 A 1 1 0 0 0 2 9 A 1 1 0 0 0 3 10 A 1 1 0 0 0 4 9 A 1 1 0 0 0 3 8 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 674 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 3 2 A 1 1 0 0 0 2 3 A 1 1 0 0 0 3 4 A 1 1 0 0 0 4 3 A 1 1 0 0 0 3 2 z M 5 2 L 5 3 L 7 3 L 13 3 L 13 2 L 7 2 L 5 2 z M 5 4 L 5 5 L 7 5 L 11 5 L 11 4 L 7 4 L 5 4 z M 10 7 L 10 10 L 7 10 L 7 11 L 10 11 L 10 14 L 11 14 L 11 11 L 14 11 L 14 10 L 11 10 L 11 7 L 10 7 z M 3 8 A 1 1 0 0 0 2 9 A 1 1 0 0 0 3 10 A 1 1 0 0 0 4 9 A 1 1 0 0 0 3 8 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 674 B

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 16 5 L 16 7 L 3 7 L 3 8 L 16 8 L 16 10 L 19 7.5 L 16 5 z M 16 12 L 16 14 L 3 14 L 3 15 L 16 15 L 16 17 L 19 14.5 L 16 12 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 465 B

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 16 5 L 16 7 L 3 7 L 3 8 L 16 8 L 16 10 L 19 7.5 L 16 5 z M 16 12 L 16 14 L 3 14 L 3 15 L 16 15 L 16 17 L 19 14.5 L 16 12 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 465 B

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 11 3 C 6.568002 3 3 6.568002 3 11 C 3 15.431998 6.568002 19 11 19 C 11.692084 19 12.360349 18.903948 13 18.740234 L 13 17.707031 C 12.366247 17.894565 11.696167 18 11 18 C 7.122001 18 4 14.877999 4 11 C 4 7.122001 7.122001 4 11 4 C 14.877999 4 18 7.122001 18 11 C 18 11.696167 17.894565 12.366247 17.707031 13 L 18.740234 13 C 18.903948 12.360349 19 11.692084 19 11 C 19 6.568002 15.431998 3 11 3 z M 10 5 L 10 12 L 11 12 L 16 12 L 16 11 L 11 11 L 11 5 L 10 5 z M 14 13 L 14 19 L 20 16 L 14 13 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 838 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8 2 C 4.6862915 2 2 4.6862915 2 8 C 2 11.313708 4.6862915 14 8 14 L 8 13 C 5.2385763 13 3 10.761424 3 8 C 3 5.2385763 5.2385763 3 8 3 C 10.419003 3 12.43717 4.7176961 12.900391 7 L 13.910156 7 C 13.433066 4.1634955 10.972138 2 8 2 z M 7 4 L 7 8 L 7 9 L 8 9 L 9 9 L 9 8 L 8 8 L 8 7 L 8 4 L 7 4 z M 10 8 L 10 14 L 15 11 L 10 8 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 665 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 11 3.5 L 11 5 L 5 5 C 3.338 5 2 6.338 2 8 L 3 8 C 3 6.892 3.892 6 5 6 L 11 6 L 11 7.5 L 14 5.5 L 11 3.5 z M 13 8 C 13 9.108 12.108 10 11 10 L 5 10 L 5 8.5 L 2 10.5 L 5 12.5 L 5 11 L 11 11 C 12.662 11 14 9.662 14 8 L 13 8 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@ -0,0 +1,7 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g fill="#dedede">
<path d="m3.994 2c-1.258 0.015-2.179-0.03-2.931 0.385a1.88 1.88 0 0 0-0.838 0.998c-0.165 0.437-0.225 0.96-0.225 1.617v6c0 0.658 0.06 1.179 0.225 1.617 0.164 0.439 0.461 0.79 0.838 0.998 0.752 0.416 1.673 0.37 2.931 0.385h8.012c1.258-0.015 2.179 0.03 2.932-0.385a1.88 1.88 0 0 0 0.838-0.998c0.164-0.438 0.224-0.96 0.224-1.617v-6c0-0.658-0.06-1.179-0.225-1.617a1.88 1.88 0 0 0-0.838-0.998c-0.752-0.416-1.673-0.37-2.931-0.385h-1.006v1h1c1.259 0.015 2.087 0.06 2.453 0.262 0.184 0.1 0.29 0.212 0.387 0.472s0.16 0.674 0.16 1.266v6c0 0.592-0.063 1.006-0.16 1.266-0.098 0.26-0.203 0.371-0.387 0.472-0.366 0.202-1.194 0.247-2.453 0.262h-8c-1.259-0.015-2.09-0.06-2.455-0.262-0.183-0.1-0.287-0.212-0.385-0.472-0.097-0.26-0.16-0.674-0.16-1.266v-6c0-0.592 0.063-1.006 0.16-1.266 0.098-0.26 0.202-0.371 0.385-0.472 0.365-0.202 1.195-0.247 2.455-0.262h2.5v-1h-2.502z" color="#000000" font-family="sans-serif" font-weight="400" overflow="visible" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none"/>
<path d="m5 0 1e-3 5c0.76-0.348 1.535-0.737 2.323-1.166a44.09 44.09 0 0 0 2.259-1.334 51.555 51.555 0 0 0-2.26-1.32 43.088 43.088 0 0 0-2.323-1.18z" color="#000000" overflow="visible"/>
<path d="m6.117 6.247c0.167-0.07 0.34-0.147 0.52-0.234 0.184-0.092 0.363-0.19 0.536-0.294a8.2 8.2 0 0 0 0.494-0.338c0.161-0.121 0.306-0.248 0.433-0.381h0.9v6h-1v-4.652c-0.448 0.347-1.012 0.566-1.494 0.762z" font-family="Ubuntu" font-weight="700" letter-spacing="0" text-anchor="middle" word-spacing="0"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,7 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g fill="#363636">
<path d="m3.994 2c-1.258 0.015-2.179-0.03-2.931 0.385a1.88 1.88 0 0 0-0.838 0.998c-0.165 0.437-0.225 0.96-0.225 1.617v6c0 0.658 0.06 1.179 0.225 1.617 0.164 0.439 0.461 0.79 0.838 0.998 0.752 0.416 1.673 0.37 2.931 0.385h8.012c1.258-0.015 2.179 0.03 2.932-0.385a1.88 1.88 0 0 0 0.838-0.998c0.164-0.438 0.224-0.96 0.224-1.617v-6c0-0.658-0.06-1.179-0.225-1.617a1.88 1.88 0 0 0-0.838-0.998c-0.752-0.416-1.673-0.37-2.931-0.385h-1.006v1h1c1.259 0.015 2.087 0.06 2.453 0.262 0.184 0.1 0.29 0.212 0.387 0.472s0.16 0.674 0.16 1.266v6c0 0.592-0.063 1.006-0.16 1.266-0.098 0.26-0.203 0.371-0.387 0.472-0.366 0.202-1.194 0.247-2.453 0.262h-8c-1.259-0.015-2.09-0.06-2.455-0.262-0.183-0.1-0.287-0.212-0.385-0.472-0.097-0.26-0.16-0.674-0.16-1.266v-6c0-0.592 0.063-1.006 0.16-1.266 0.098-0.26 0.202-0.371 0.385-0.472 0.365-0.202 1.195-0.247 2.455-0.262h2.5v-1h-2.502z" color="#000000" font-family="sans-serif" font-weight="400" overflow="visible" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none"/>
<path d="m5 0 1e-3 5c0.76-0.348 1.535-0.737 2.323-1.166a44.09 44.09 0 0 0 2.259-1.334 51.555 51.555 0 0 0-2.26-1.32 43.088 43.088 0 0 0-2.323-1.18z" color="#000000" overflow="visible"/>
<path d="m6.117 6.247c0.167-0.07 0.34-0.147 0.52-0.234 0.184-0.092 0.363-0.19 0.536-0.294a8.2 8.2 0 0 0 0.494-0.338c0.161-0.121 0.306-0.248 0.433-0.381h0.9v6h-1v-4.652c-0.448 0.347-1.012 0.566-1.494 0.762z" font-family="Ubuntu" font-weight="700" letter-spacing="0" text-anchor="middle" word-spacing="0"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 11 3.5 L 11 5 L 5 5 C 3.338 5 2 6.338 2 8 L 3 8 C 3 6.892 3.892 6 5 6 L 11 6 L 11 7.5 L 14 5.5 L 11 3.5 z M 13 8 C 13 9.108 12.108 10 11 10 L 5 10 L 5 8.5 L 2 10.5 L 5 12.5 L 5 11 L 11 11 C 12.662 11 14 9.662 14 8 L 13 8 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m11 3.5v1.5h-2c-1.662 0-3 1.338-3 3 0 1.108-.892 2-2 2h-2v1h2c1.662 0 3-1.338 3-3 0-1.108.892-2 2-2h2v1.5l3-2zm-9 1.5v1h2c.401 0 .772.119 1.084.32.077-.333.21-.642.387-.922-.436-.246-.933-.398-1.471-.398zm9 3.5v1.5h-2c-.401 0-.772-.119-1.084-.32-.077.333-.21.642-.387.922.436.246.933.398 1.471.398h2v1.5l3-2z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 644 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m11 3.5v1.5h-2c-1.662 0-3 1.338-3 3 0 1.108-.892 2-2 2h-2v1h2c1.662 0 3-1.338 3-3 0-1.108.892-2 2-2h2v1.5l3-2zm-9 1.5v1h2c.401 0 .772.119 1.084.32.077-.333.21-.642.387-.922-.436-.246-.933-.398-1.471-.398zm9 3.5v1.5h-2c-.401 0-.772-.119-1.084-.32-.077.333-.21.642-.387.922.436.246.933.398 1.471.398h2v1.5l3-2z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 644 B

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 11 4 4 11 11 18 11,11 Z M 11 11 l 7 7 0 -14 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 383 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8 3 3 8 8 13 8 8 Z M 8 8 l 5 5 0 -10 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 377 B

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 4 4 0 14 L 11 11 Z M 11 11 11 18 18 11 11 4 Z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 383 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 L 8 8 Z M 8 8 l 0 5 L 13 8 8 3 Z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 380 B

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 4 4 0 14 5 0 0 -7 0 -7 z M 9 11 l 9 7 0 -14 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 383 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 3 0 0 -5 0 -5 z M 6 8 l 7 5 0 -10 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 383 B

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#dedede;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 4 4 0 14 L 13 11 Z M 13 11 l 0 7 5 0 0 -14 -5 0 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 387 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#363636;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="m 3 3 0 10 L 10 8 Z M 10 8 l 0 5 3 0 0 -10 -3 0 z"
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 386 B

View File

@ -0,0 +1,37 @@
#pragma once
#include <gtk/gtk.h>
enum PlayMode{
List_Once, // Play the media with a playlist once
List_Repeat, // Play the media with a playlist repeatly
List_Shuffle, // Random play a media in the playlist
One_Repeat // Repeat a media file
};
G_DECLARE_FINAL_TYPE(MyMediaPlayer, my_media_player, MYMEDIA, PLAYER, GtkApplicationWindow)
gboolean my_media_player_get_music_loaded(MyMediaPlayer *self);
void my_media_player_set_music_loaded(MyMediaPlayer *self, gboolean music_loaded);
GtkWidget *my_media_player_get_video_widget(MyMediaPlayer *self);
GtkLabel *my_media_player_get_lyrics_widget(MyMediaPlayer *self);
PlayMode my_media_player_get_play_mode(MyMediaPlayer *self);
char *my_media_player_get_filename(MyMediaPlayer *self);
guint my_media_player_get_current_index(MyMediaPlayer *self);
guint my_media_player_get_n_audios(MyMediaPlayer *self);
void btnnext_clicked(GtkButton *self, MyMediaPlayer *player);
void my_media_player_load_random_audio(MyMediaPlayer *player);
void my_media_player_reload_audio(MyMediaPlayer *player);
MyMediaPlayer *my_media_player_new(GtkApplication *app);

View File

@ -0,0 +1,9 @@
#pragma once
#ifdef _WIN32
// Definition fix for microsoft windows
// On linux, the limits.h is automatically included
#define NAME_MAX 256
#endif

View File

@ -1,6 +1,6 @@
#include "DrawApp.h"
#include "MyFinder.h"
#define NAME_MAX 256
#include "MyLimit.h"
typedef enum
{

View File

@ -0,0 +1,483 @@
#include "MediaItem.h"
#include "LyricsParser.h"
#include "MyMediaPlayer.h"
#define lyrics_max_length 1024
static char *lyrics_content = NULL;
static gboolean lyrics_loaded = FALSE;
static gboolean line_read = FALSE, lyrics_updated = FALSE;
static char current_lyrics[lyrics_max_length];
static gint64 lyric_time;
// Replace the symbol
static void UTF8_Replace_and_Symbol(gint64 pos_and_char, char *utf8_string)
{
size_t string_length = strlen(utf8_string);
// Copy string after '&'
for (int i = pos_and_char + 1; i < string_length; i++)
{
utf8_string[i + 4] = utf8_string[i];
}
// Replace "&" with "&amp;"
utf8_string[pos_and_char + 1] = 'a';
utf8_string[pos_and_char + 2] = 'm';
utf8_string[pos_and_char + 3] = 'p';
utf8_string[pos_and_char + 4] = ';';
// Fix end symbol
utf8_string[string_length + 3] = '\0';
}
// Fix UTF-8 '&' String
static void UTF8_String_Fix(char *utf8_string)
{
// Check for '&' String
gint64 pos_and_char = -1;
size_t string_length = strlen(utf8_string);
for (int i = 0; i < string_length; i++)
{
if (utf8_string[i] == '&')
{
UTF8_Replace_and_Symbol(i, utf8_string);
}
}
}
// Get lyrics string
static void get_lrc_substr(char *src, char *dest,
size_t start, size_t end)
{
// Copy string to the end
if (strlen(src) == 0 || strlen(src) == start + 1)
{
return;
}
for (int i = 0; i < end; i++)
{
dest[i] = src[i + start + 1];
}
dest[end] = '\0';
}
// Get the position of timestamp end to separate lyrics and timestamp
static size_t get_lrc_timestamp_end_pos(const char *lrc_line)
{
size_t timestamp_end;
// Find the end of timestamp
for (int i = 0; i < strlen(lrc_line); i++)
{
if (lrc_line[i] == ']')
{
timestamp_end = i;
}
}
return timestamp_end;
}
// Get timestamp of a lyrics line
static gint64 get_lrc_line_timestamp(const char *lyrics_line, size_t timestamp_length)
{
static gint64 lyric_time_tmp = 0;
// Minutes
gint64 lyric_min = (lyrics_line[1] - '0') * 10 +
(lyrics_line[2] - '0');
// Seconds
gint64 lyric_sec = (lyrics_line[4] - '0') * 10 +
(lyrics_line[5] - '0');
// Millseconds
gint64 lyric_micro = 0;
switch (timestamp_length)
{
case 9:
lyric_micro = (lyrics_line[7] - '0') * 100 +
(lyrics_line[8] - '0') * 10;
break;
case 10:
lyric_micro = (lyrics_line[7] - '0') * 100 +
(lyrics_line[8] - '0') * 10 +
(lyrics_line[9] - '0');
break;
}
lyric_time_tmp = lyric_micro + lyric_sec * 1000 +
lyric_min * 60 * 1000;
return lyric_time_tmp;
}
// Get a line of lyrics
static void get_lyrics_line(const char *lyrics, char *lyrics_line1, gboolean reset)
{
static gint64 start_pos = 0, end_pos = 0;
// Reset position to load frm start,
// the lyrics and lyrics_line1 should be NULL to avoid problems
if (reset)
{
start_pos = 0;
end_pos = 0;
return;
}
if (lyrics_loaded)
{
// Find the '\n'
while (lyrics[end_pos] != '\n' && lyrics[end_pos] != '\0')
{
end_pos++;
}
// Copy contents
for (int i = 0; i < end_pos - start_pos; i++)
{
lyrics_line1[i] = lyrics[i + start_pos];
}
lyrics_line1[end_pos - start_pos] = '\0';
// Lyrics read finished
if (lyrics[end_pos] == '\0')
{
end_pos = 0;
// lyrics_loaded = FALSE;
}
end_pos++;
start_pos = end_pos;
}
}
// Update lyrics
void update_lyrics(MyMediaPlayer *player)
{
// Get position between filename and extension
int point_pos;
gsize length;
char *current_filename = my_media_player_get_filename(player);
// g_print("%s\n", current_filename);
for (int i = strlen(current_filename) - 1; i > 0; i--)
{
if (current_filename[i] == '.')
{
point_pos = i;
break;
}
}
// Get Lyrics file name
char lyric_filename[path_max_length];
strncpy(lyric_filename, current_filename, point_pos);
lyric_filename[point_pos] = '\0';
strncat(lyric_filename, ".lrc", 4);
// g_print("%s\n", lyric_filename);
// Load lyrics with gio to avoid coding problem
GFile *lyrics_file = g_file_new_for_path(lyric_filename);
// Refresh before load
if (lyrics_content != NULL)
{
g_free(lyrics_content);
lyrics_content = NULL;
}
// Load contents
if (g_file_load_contents(lyrics_file, NULL, &lyrics_content, &length, NULL, NULL))
{
lyrics_loaded = TRUE;
}
else
{
lyrics_loaded = FALSE;
return;
}
g_object_unref(lyrics_file);
// Reset load status
lyrics_updated = TRUE;
line_read = FALSE;
// Reset line read starts
get_lyrics_line(NULL, NULL, TRUE);
}
// Lyrics Line process
static void lyric_line_process(char *lyrics_line,
size_t &timestamp_length)
{
// Some lrc files has empty lines
if (strlen(lyrics_line) == 0)
{
line_read = TRUE;
return;
}
// Not a number
while (lyrics_line[1] < '0' || lyrics_line[1] > '9')
{
line_read = FALSE;
return;
}
// Get timestamp length
timestamp_length = get_lrc_timestamp_end_pos(lyrics_line);
// g_print("%s\n", lyrics_line);
lyric_time = get_lrc_line_timestamp(lyrics_line, timestamp_length);
// Remove time stamp
get_lrc_substr(lyrics_line, current_lyrics, timestamp_length,
strlen(lyrics_line) - timestamp_length);
// Fix some symbols
UTF8_String_Fix(current_lyrics);
// A lyric line is read
line_read = TRUE;
}
// Update label that show lyrics
static void lyrics_label_update(gint64 &curr_time, MyMediaPlayer *player)
{
char label_string[lyrics_max_length];
// if time is on a lyrics, update the label
if (curr_time >= lyric_time - 100 && curr_time <= lyric_time + 100 &&
line_read ||
lyric_time == 0)
{
// Since a new line is read and time match, load lyrics
snprintf(label_string, lyrics_max_length,
"<span color=\"green\" size='12pt'>%s</span>", current_lyrics);
gtk_label_set_markup(my_media_player_get_lyrics_widget(player),
label_string);
line_read = FALSE;
}
}
// Get lyrics for a specfied time
static void get_lyrics(gint64 curr_time, gboolean playing, MyMediaPlayer *player)
{
char lyrics_line[lyrics_max_length];
static size_t timestamp_length;
// Get lyrics data
if (lyrics_loaded)
{
// g_print("Lrc file load successful\n");
if (playing && !line_read)
{
// Get lyrics line
get_lyrics_line(lyrics_content, lyrics_line, FALSE);
g_print("%s\n", lyrics_line);
// Process lyrics line
lyric_line_process(lyrics_line, timestamp_length);
}
// Try to update label
lyrics_label_update(curr_time, player);
}
// else
// {
// // g_print("Lyric file open failed!\n");
// gtk_label_set_markup(my_media_player_get_lyrics_widget(player),
// "<span color=\"green\" size='12pt'>No Lyric File Found!</span>");
// }
}
// Reset lyrics for drag of timeline
static void reset_lyrics(gint64 timestamp, GtkMediaStream *stream,
MyMediaPlayer *player)
{
static size_t timestamp_length;
lyric_time = -1;
char lyrics_line[lyrics_max_length];
char priv_lyrics_line[lyrics_max_length];
// Reset loading status
get_lyrics_line(NULL, NULL, TRUE);
// Fine match lyrics line
do
{
// Get Privous lyrics line
strncpy(priv_lyrics_line, current_lyrics, lyrics_max_length);
// Get lyrics line
get_lyrics_line(lyrics_content, lyrics_line, FALSE);
// Process lyrics line
lyric_line_process(lyrics_line, timestamp_length);
} while (lyric_time < timestamp);
line_read = TRUE;
// Show next lyrics
char *label_string = g_strdup_printf("<span color=\"green\" size='12pt'>%s</span>",
priv_lyrics_line);
gtk_label_set_markup(my_media_player_get_lyrics_widget(player),
label_string);
g_free(label_string);
// Try to update label
lyrics_label_update(timestamp, player);
// Pause audio before seek to avoid isolation
if (timestamp != 0)
{
gtk_media_stream_pause(stream);
gtk_media_stream_seek(stream, timestamp * 1000);
gtk_media_stream_play(stream);
}
}
// Check whether the media is playing
static gboolean get_media_playing(gint64 curr_time,
GtkMediaStream *stream, MyMediaPlayer *player)
{
static gint64 tmp_time = -1;
if (curr_time == tmp_time)
{
return FALSE;
}
else
{
gint64 delta_time = abs(curr_time - tmp_time);
if (delta_time > 500)
{
// Reset lyrics load status
reset_lyrics(curr_time, stream, player);
}
tmp_time = curr_time;
return TRUE;
}
}
// Check whether lyrics should be update
static gboolean get_media_stream_status(MyMediaPlayer *player,
GtkMediaStream *stream, gint64 timestamp)
{
if (timestamp == 0 && !lyrics_updated && !lyrics_loaded)
{
// Load lyrics when a new media loaded
update_lyrics(player);
}
if (get_media_playing(timestamp, stream, player))
{
// Reset status when the media playing
lyrics_updated = FALSE;
lyrics_loaded = TRUE;
return TRUE;
}
else
{
return FALSE;
}
return FALSE;
}
// Operations when a media play end
static void media_play_ended_handler(MyMediaPlayer *player)
{
GtkMediaStream *stream;
// Get Current Play mode
PlayMode play_mode = my_media_player_get_play_mode(player);
switch (play_mode)
{
// Play a list of music once
// g_print("%d", play_mode);
case PlayMode::List_Once:
// Only play music when current audio is not the end of the audio list
// g_print("%d %d\n", my_media_player_get_current_index(player),
// my_media_player_get_n_audios(player));
if (my_media_player_get_current_index(player) <
my_media_player_get_n_audios(player) - 1)
{
// use the function for play next button to load next audio
btnnext_clicked(NULL, player);
// Get media stream to control
stream = gtk_video_get_media_stream(GTK_VIDEO(
my_media_player_get_video_widget(player)));
// Play media stream associated with media file
gtk_media_stream_play(stream);
}
break;
case PlayMode::List_Repeat:
// In List Repeat Mode, use the function for play next button
btnnext_clicked(NULL, player);
// Get Media stream to control
stream = gtk_video_get_media_stream(GTK_VIDEO(
my_media_player_get_video_widget(player)));
// Play media stream associated with media file
gtk_media_stream_play(stream);
break;
case PlayMode::List_Shuffle:
// Play music with random index
my_media_player_load_random_audio(player);
// Get Media stream to control
stream = gtk_video_get_media_stream(GTK_VIDEO(
my_media_player_get_video_widget(player)));
// Play media stream associated with media file
gtk_media_stream_play(stream);
break;
case PlayMode::One_Repeat:
// Reload audio
my_media_player_reload_audio(player);
// Get media stream to control
stream = gtk_video_get_media_stream(GTK_VIDEO(
my_media_player_get_video_widget(player)));
// Play media stream associated with media file
gtk_media_stream_play(stream);
break;
}
}
// Time monitor
gboolean lyric_time_func(gpointer data)
{
MyMediaPlayer *player = MYMEDIA_PLAYER(data);
// if music is loaded, try to get timestamp
if (my_media_player_get_music_loaded(player))
{
// Get media stream
GtkMediaStream *stream;
stream = gtk_video_get_media_stream(GTK_VIDEO(
my_media_player_get_video_widget(player)));
// only get timestamp when media stream vaild
if (GTK_IS_MEDIA_STREAM(stream))
{
// get timestamp
gint64 timestamp = gtk_media_stream_get_timestamp(stream);
gint64 timestamp_ms = timestamp / 1000;
// g_print("%ld\n", timestamp_ms);
// Update lyrics and Check whether media stopped
get_lyrics(timestamp_ms, get_media_stream_status(player, stream, timestamp_ms),
player);
// The Media ended, reset the status
if (gtk_media_stream_get_ended(stream) &&
my_media_player_get_music_loaded(player))
{
my_media_player_set_music_loaded(player, FALSE);
media_play_ended_handler(player);
}
}
}
return TRUE;
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "MyMediaPlayer.h"
// Timeout function for music played time
gboolean lyric_time_func(gpointer data);
// Update lyrics file when a music will play
void update_lyrics(MyMediaPlayer *player);

View File

@ -0,0 +1,40 @@
#include "MediaItem.h"
#include <cstring>
struct _MediaItem
{
GObject parent_instance;
char disp_name[name_max_length];
char file_name[path_max_length];
};
G_DEFINE_TYPE(MediaItem, media_item, G_TYPE_OBJECT)
const char *media_item_get_filename(MediaItem *item)
{
// Get true file name
return item->file_name;
}
const char *media_item_get_dispname(MediaItem *item)
{
// Get Base name
return item->disp_name;
}
static void media_item_init(MediaItem *self)
{
}
static void media_item_class_init(MediaItemClass *klass)
{
}
MediaItem *media_item_new(const char *dispname, const char *filename)
{
// Create a new item
MediaItem *item = Media_Item(g_object_new(media_item_get_type(), NULL));
strncpy(item->disp_name, dispname, name_max_length);
strncpy(item->file_name, filename, path_max_length);
return item;
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <gtk/gtk.h>
// File name and path limits
#define name_max_length 256
#define path_max_length 4096
#define MEDIA_ITEM_TYPE (media_item_get_type())
G_DECLARE_FINAL_TYPE(MediaItem, media_item, Media, Item, GObject)
const char *media_item_get_filename(MediaItem *item);
const char *media_item_get_dispname(MediaItem *item);
MediaItem *media_item_new(const char *dispname, const char *filename);

View File

@ -0,0 +1,762 @@
#include "MyMediaPlayer.h"
#include "LyricsParser.h"
#include "MediaItem.h"
#include "../json_nlohmann/json.hpp"
#include <vector>
#include <fstream>
#include <cstring>
using json = nlohmann::json;
typedef std::vector<std::string> string_vector;
struct _MyMediaPlayer
{
GtkApplicationWindow parent_instance;
GtkWidget *video, *label_lyrics;
GtkWidget *ctrl_box;
GtkWidget *btn_priv, *btn_play, *btn_next,
*btn_stop, *btn_playmode;
PlayMode current_play_mode;
GtkWidget *main_box, *btn_box;
GtkWidget *btn_add, *btn_remove;
GtkWidget *btn_load, *btn_save;
GtkWidget *list_expander, *list_box;
GtkWidget *column_view;
GtkWidget *scrolled_window, *scrolled_lyrics;
GListStore *music_store;
char current_filename[path_max_length];
guint n_items, current_audio_index;
gboolean music_loaded;
gboolean dark_mode;
GtkSingleSelection *music_selection;
GtkListItemFactory *filename_factory;
GtkColumnViewColumn *filename_column;
};
G_DEFINE_TYPE(MyMediaPlayer, my_media_player, GTK_TYPE_APPLICATION_WINDOW)
// Add media file item to the list when the file open dialog accepted
void file_dialog_response(GObject *dialog, GAsyncResult *res, gpointer data)
{
GFile *file;
MyMediaPlayer *player = MYMEDIA_PLAYER(data);
// Get file
file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(dialog), res, NULL);
if (file != NULL)
{
// Get file name
char *path = g_file_get_path(file);
char *name = g_file_get_basename(file);
g_list_store_append(player->music_store,
media_item_new(name, path));
g_object_unref(file);
g_free(path);
g_free(name);
// Update items count of media files
player->n_items = g_list_model_get_n_items(
G_LIST_MODEL(player->music_store));
// Update index information
player->current_audio_index = gtk_single_selection_get_selected(
player->music_selection);
}
}
// Add a media file item
static void btnadd_clicked(GtkWidget *widget, MyMediaPlayer *player)
{
// Create a file dialog window
GtkFileDialog *dialog = gtk_file_dialog_new();
gtk_file_dialog_set_title(dialog, "Open media file");
// Open the file dialog
gtk_file_dialog_open(dialog, GTK_WINDOW(player), NULL, file_dialog_response, player);
}
// Remove the selected item
static void btnremove_clicked(GtkWidget *widget, MyMediaPlayer *player)
{
// Get selected position
guint pos = gtk_single_selection_get_selected(player->music_selection);
// Remove the selected item
g_list_store_remove(player->music_store, pos);
// Update index information
player->n_items = g_list_model_get_n_items(
G_LIST_MODEL(player->music_store));
player->current_audio_index = gtk_single_selection_get_selected(
player->music_selection);
}
// Load playlist
static void load_playlist(std::string filename, MyMediaPlayer *player)
{
// Load a new json data to the list
std::fstream infile;
infile.open(filename, std::ios_base::in);
if (infile.is_open())
{
// Get json data
json data = json::parse(infile);
// Check whether json data is empty
if (data.empty())
{
return;
}
try
{
string_vector sound_names = data["name"];
string_vector sound_paths = data["path"];
std::string disp_name, file_path;
for (int i = 0; i < sound_names.size(); i++)
{
// Append items
disp_name = sound_names[i];
file_path = sound_paths[i];
g_list_store_append(G_LIST_STORE(player->music_store),
media_item_new(disp_name.c_str(), file_path.c_str()));
}
}
catch (const nlohmann::detail::exception &ex)
{
g_print("%s\n", ex.what());
}
// Update count of media files
player->n_items = g_list_model_get_n_items(
G_LIST_MODEL(player->music_store));
// Update index information
player->current_audio_index = gtk_single_selection_get_selected(
player->music_selection);
}
}
// Response for the dialog of load playlist
static void load_dialog_response(GObject *dialog, GAsyncResult *res, gpointer data)
{
GFile *file;
MyMediaPlayer *player = MYMEDIA_PLAYER(data);
// Get file
file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(dialog), res, NULL);
if (file != NULL)
{
// Get file name
char *path = g_file_get_path(file);
load_playlist(path, player);
g_object_unref(file);
}
}
// Handler for load playlist button
static void btnload_clicked(GtkWidget *widget, MyMediaPlayer *player)
{
// Create a file dialog window
GtkFileDialog *dialog = gtk_file_dialog_new();
gtk_file_dialog_set_title(dialog, "Open Playlist file");
// Create a filter
GtkFileFilter *filter_json = gtk_file_filter_new();
gtk_file_filter_add_pattern(filter_json, "*.json");
gtk_file_filter_set_name(filter_json, "json file");
// Create store for filters
GListStore *filters_store = g_list_store_new(GTK_TYPE_FILE_FILTER);
g_list_store_append(filters_store, filter_json);
gtk_file_dialog_set_filters(dialog, G_LIST_MODEL(filters_store));
// Set json filter for default
gtk_file_dialog_set_default_filter(dialog, filter_json);
// Open the file dialog
gtk_file_dialog_open(dialog, GTK_WINDOW(player), NULL, load_dialog_response, player);
}
// Save the play list to a path
static void save_playlist(std::string filename, MyMediaPlayer *player)
{
// Save playlist to json data
string_vector sound_names, sound_paths;
std::string disp_name, file_path;
// Get n items of the list
guint list_items = g_list_model_get_n_items(G_LIST_MODEL(player->music_store));
// Insert all items to the vectors
for (int i = 0; i < list_items; i++)
{
// Get sound name and the path
MediaItem *item = Media_Item(g_list_model_get_item(G_LIST_MODEL(player->music_store), i));
disp_name = std::string(media_item_get_dispname(item));
file_path = std::string(media_item_get_filename(item));
// Push data to the vectors
sound_names.push_back(disp_name);
sound_paths.push_back(file_path);
}
// Save data to json file
std::fstream outfile;
outfile.open(filename, std::ios::out);
if (outfile.is_open())
{
// Load json data
json data = json::parse(R"(
{
"name":[""],
"path":[""]
}
)");
data["name"] = sound_names;
data["path"] = sound_paths;
// Save to file
outfile << data;
}
else
{
g_print("Failed to save file!\n");
}
outfile.close();
}
// Handler for save list dialog
static void btnsave_clicked(GtkWidget *widget, MyMediaPlayer *player)
{
// Currently just save the playlist to a default name
save_playlist("playlist.json", player);
}
// Load a audio with specificed index
static void load_audio(MediaItem *item, MyMediaPlayer *player)
{
// Load the audio file
GFile *music_file;
const char *file_name, *disp_name;
file_name = media_item_get_filename(item);
music_file = g_file_new_for_path(file_name);
disp_name = media_item_get_dispname(item);
if (music_file != NULL)
{
// Add file to video widget for play
gtk_video_set_file(GTK_VIDEO(player->video), music_file);
// Mark the player is ready and update current file name
player->music_loaded = TRUE;
char *filename1 = player->current_filename;
strncpy(filename1, file_name, strlen(file_name));
filename1[strlen(file_name)] = '\0';
g_object_unref(music_file);
// Set the label for initial status
char *label_str = g_strdup_printf("<span color=\"green\" size='12pt'>%s</span>", disp_name);
gtk_label_set_markup(GTK_LABEL(player->label_lyrics), label_str);
g_free(label_str);
// Force update lyrics file
update_lyrics(player);
// Enable control button
gtk_widget_set_sensitive(player->btn_play, TRUE);
}
}
static void column_view_activated(GtkColumnView *self, gint position, MyMediaPlayer *player)
{
// Clear stream for player
GtkMediaStream *stream = gtk_video_get_media_stream(GTK_VIDEO(player->video));
if (stream != NULL)
{
gtk_media_file_clear(GTK_MEDIA_FILE(stream));
// g_object_unref(stream);
}
gtk_video_set_file(GTK_VIDEO(player->video), NULL);
// Play the selected media
MediaItem *item;
// Get selection and open the music file
item = Media_Item(gtk_single_selection_get_selected_item(player->music_selection));
load_audio(item, player);
// Update Column index
player->current_audio_index = gtk_single_selection_get_selected(player->music_selection);
}
static void filename_factory_setup(GtkListItemFactory *factory,
GtkListItem *item)
{
// Create a label
GtkWidget *label;
label = gtk_label_new(" ");
// Add the label to the item
gtk_widget_set_halign(label, GTK_ALIGN_START);
gtk_list_item_set_child(item, label);
}
static void filename_factory_bind(GtkListItemFactory *factory,
GtkListItem *item)
{
// Get child
GtkWidget *label;
label = gtk_list_item_get_child(item);
// Get item
MediaItem *file_item = Media_Item(gtk_list_item_get_item(item));
gtk_label_set_label(GTK_LABEL(label),
media_item_get_dispname(file_item));
}
gboolean my_media_player_get_music_loaded(MyMediaPlayer *self)
{
// Get whether music is loaded
return self->music_loaded;
}
void my_media_player_set_music_loaded(MyMediaPlayer *self, gboolean music_loaded)
{
// Set status of music loaded
self->music_loaded = music_loaded;
}
GtkWidget *my_media_player_get_video_widget(MyMediaPlayer *self)
{
// Get video widget
return self->video;
}
GtkLabel *my_media_player_get_lyrics_widget(MyMediaPlayer *self)
{
// Get Label for lyrics
return GTK_LABEL(self->label_lyrics);
}
char *my_media_player_get_filename(MyMediaPlayer *self)
{
// Get file name
return self->current_filename;
}
PlayMode my_media_player_get_play_mode(MyMediaPlayer *self)
{
// Get Current play mode
return self->current_play_mode;
}
guint my_media_player_get_current_index(MyMediaPlayer *self)
{
// Get the index of current playing audio
return self->current_audio_index;
}
guint my_media_player_get_n_audios(MyMediaPlayer *self)
{
// Get the number of audios in the list
return self->n_items;
}
static void my_media_player_expander_activate(GtkExpander *self, MyMediaPlayer *player)
{
if (!gtk_expander_get_expanded(self))
{
g_print("Try to recover size!\n");
gtk_widget_set_size_request(player->main_box, 300, 270);
gtk_widget_set_size_request(GTK_WIDGET(player), 300, 270);
gtk_widget_queue_resize(player->list_box);
gtk_widget_queue_resize(GTK_WIDGET(player));
}
}
// Create a button with dark icon support
static GtkWidget *player_button_new(const char *icon_name, gboolean dark_mode)
{
char *icon_name1;
GtkWidget *button;
// Change icon name string for dark mode
if (dark_mode)
{
icon_name1 = g_strdup_printf("%s-dark", icon_name);
}
else
{
icon_name1 = g_strdup_printf("%s", icon_name);
}
// Set icon name and free memory for icon name
button = gtk_button_new_from_icon_name(icon_name1);
g_free(icon_name1);
return button;
}
// Set button icon name with dark icon theme support
static void player_button_set_icon_name(GtkButton *button, const char *icon_name,
gboolean dark_mode)
{
char *icon_name1;
// Change icon name string for dark mode
if (dark_mode)
{
icon_name1 = g_strdup_printf("%s-dark", icon_name);
}
else
{
icon_name1 = g_strdup_printf("%s", icon_name);
}
// Set icon name and free memory for icon name
gtk_button_set_icon_name(button, icon_name1);
g_free(icon_name1);
}
// Play button
static void btnplay_clicked(GtkButton *self, MyMediaPlayer *player)
{
// Get Media stream and play
GtkMediaStream *stream = gtk_video_get_media_stream(GTK_VIDEO(player->video));
if (GTK_IS_MEDIA_STREAM(stream))
{
if (gtk_media_stream_get_playing(stream))
{
// Media is playing, pause it
gtk_media_stream_pause(stream);
player_button_set_icon_name(self, "media-playback-start", player->dark_mode);
}
else
{
// Media is not playing
gtk_media_stream_play(stream);
player_button_set_icon_name(self, "media-playback-pause", player->dark_mode);
}
}
}
// Play previous music
static void btnpriv_clicked(GtkButton *self, MyMediaPlayer *player)
{
// Clear stream for player
GtkMediaStream *stream = gtk_video_get_media_stream(GTK_VIDEO(player->video));
if (stream != NULL)
{
gtk_media_file_clear(GTK_MEDIA_FILE(stream));
// g_object_unref(stream);
}
// Clear video widget
gtk_video_set_file(GTK_VIDEO(player->video), NULL);
// Current index
if (player->current_audio_index == 0)
{
player->current_audio_index = player->n_items - 1;
}
else
{
player->current_audio_index -= 1;
}
// Load music at index
// Get item
MediaItem *item = Media_Item(g_list_model_get_item(G_LIST_MODEL(player->music_store),
player->current_audio_index));
load_audio(item, player);
// Update selected item
gtk_single_selection_set_selected(player->music_selection,
player->current_audio_index);
}
// Play next music
void btnnext_clicked(GtkButton *self, MyMediaPlayer *player)
{
// Clear stream for player
GtkMediaStream *stream = gtk_video_get_media_stream(GTK_VIDEO(player->video));
if (stream != NULL)
{
gtk_media_file_clear(GTK_MEDIA_FILE(stream));
// g_object_unref(stream);
}
// Clear video widget
gtk_video_set_file(GTK_VIDEO(player->video), NULL);
// Current index
if (player->current_audio_index == (player->n_items - 1))
{
player->current_audio_index = 0;
}
else
{
player->current_audio_index += 1;
}
// Load music at index
// Get item
MediaItem *item = Media_Item(g_list_model_get_item(G_LIST_MODEL(player->music_store),
player->current_audio_index));
load_audio(item, player);
// Update selected item
gtk_single_selection_set_selected(player->music_selection,
player->current_audio_index);
}
// Load music with random index
void my_media_player_load_random_audio(MyMediaPlayer *player)
{
// Get music index
player->current_audio_index = rand() % (player->n_items);
// Clear stream for player
GtkMediaStream *stream = gtk_video_get_media_stream(GTK_VIDEO(player->video));
if (stream != NULL)
{
gtk_media_file_clear(GTK_MEDIA_FILE(stream));
// g_object_unref(stream);
}
// Clear video widget
gtk_video_set_file(GTK_VIDEO(player->video), NULL);
// Load music at index
// Get item
MediaItem *item = Media_Item(g_list_model_get_item(G_LIST_MODEL(player->music_store),
player->current_audio_index));
load_audio(item, player);
// Update selected item
gtk_single_selection_set_selected(player->music_selection,
player->current_audio_index);
}
// Reload audio for repeat mode
void my_media_player_reload_audio(MyMediaPlayer *player)
{
// Clear stream for player
GtkMediaStream *stream = gtk_video_get_media_stream(GTK_VIDEO(player->video));
if (stream != NULL)
{
gtk_media_file_clear(GTK_MEDIA_FILE(stream));
// g_object_unref(stream);
}
// Clear video widget
gtk_video_set_file(GTK_VIDEO(player->video), NULL);
// Load music at index
// Get item
MediaItem *item = Media_Item(g_list_model_get_item(G_LIST_MODEL(player->music_store),
player->current_audio_index));
load_audio(item, player);
// Update selected item
gtk_single_selection_set_selected(player->music_selection,
player->current_audio_index);
}
// Stop current music
static void btnstop_clicked(GtkButton *self, MyMediaPlayer *player)
{
// Get Media stream and stop
GtkMediaStream *stream = gtk_video_get_media_stream(GTK_VIDEO(player->video));
gtk_media_file_clear(GTK_MEDIA_FILE(stream));
gtk_video_set_file(GTK_VIDEO(player->video), NULL);
player_button_set_icon_name(GTK_BUTTON(player->btn_play), "media-playback-start",
player->dark_mode);
gtk_widget_set_sensitive(player->btn_play, FALSE);
}
// Switch play mode
static void btn_playmode_clicked(GtkButton *self, MyMediaPlayer *player)
{
// Change play mode
switch (player->current_play_mode)
{
case PlayMode::List_Once:
player->current_play_mode = PlayMode::List_Repeat;
player_button_set_icon_name(self, "media-playlist-repeat", player->dark_mode);
break;
case PlayMode::List_Repeat:
player->current_play_mode = PlayMode::List_Shuffle;
player_button_set_icon_name(self, "media-playlist-shuffle", player->dark_mode);
break;
case PlayMode::List_Shuffle:
player->current_play_mode = PlayMode::One_Repeat;
player_button_set_icon_name(self, "media-playlist-repeat-one", player->dark_mode);
break;
case PlayMode::One_Repeat:
player->current_play_mode = PlayMode::List_Once;
player_button_set_icon_name(self, "media-playlist-normal", player->dark_mode);
break;
}
}
static gboolean my_media_player_close_request(GtkWindow *window)
{
// Save current list to a playlist file
save_playlist("playlist.json", MYMEDIA_PLAYER(window));
gtk_window_destroy(window);
return TRUE;
}
// Get whether use dark icon theme, to match icons with stack icons
static gboolean my_media_player_check_dark_theme(MyMediaPlayer *player)
{
gboolean dark_mode = FALSE;
int theme_name_index = 0, theme_name_length;
char temp_string[5] = {0};
// Get current theme
GtkIconTheme *theme = gtk_icon_theme_get_for_display(
gtk_widget_get_display(GTK_WIDGET(player)));
char *theme_name = gtk_icon_theme_get_theme_name(theme);
theme_name_length = strlen(theme_name);
// Translate string to lower
for (int i = 0; i < theme_name_length; i++)
{
theme_name[i] = tolower(theme_name[i]);
}
// Check "dark" string
for (int i = 0; i < 4; i++)
{
temp_string[i] = theme_name[theme_name_length - 4 + i];
}
if (strncmp(temp_string, "dark", 4) == 0)
{
dark_mode = TRUE;
}
free(theme_name);
return dark_mode;
}
static void my_media_player_init(MyMediaPlayer *self)
{
// Initalize window
gtk_window_set_icon_name(GTK_WINDOW(self), "org.gtk.daleclack");
gtk_window_set_title(GTK_WINDOW(self), "Gtk4 Media Player 3");
gtk_window_set_default_size(GTK_WINDOW(self), 300, 270);
gtk_window_set_resizable(GTK_WINDOW(self), TRUE);
// Check whether use dark icon name
self->dark_mode = my_media_player_check_dark_theme(self);
// Create widgets
self->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
self->video = gtk_video_new();
self->label_lyrics = gtk_label_new(" ");
self->ctrl_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
self->btn_play = player_button_new("media-playback-start", self->dark_mode);
self->btn_priv = player_button_new("media-skip-backward", self->dark_mode);
self->btn_next = player_button_new("media-skip-forward", self->dark_mode);
self->btn_stop = player_button_new("media-playback-stop", self->dark_mode);
self->btn_playmode = player_button_new("media-playlist-repeat", self->dark_mode);
self->btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
self->btn_add = gtk_button_new_from_icon_name("list-add");
self->scrolled_window = gtk_scrolled_window_new();
self->scrolled_lyrics = gtk_scrolled_window_new();
self->list_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
self->list_expander = gtk_expander_new("Play List");
self->btn_remove = gtk_button_new_from_icon_name("list-remove");
self->btn_load = gtk_button_new_from_icon_name("go-up");
self->btn_save = gtk_button_new_from_icon_name("document-save");
// Initalize widgets
gtk_widget_set_size_request(self->video, 300, 150);
gtk_widget_set_halign(self->ctrl_box, GTK_ALIGN_CENTER);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(self->scrolled_window),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(self->scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
gtk_label_set_markup(GTK_LABEL(self->label_lyrics),
"<span color=\"red\" size='12pt'>No media file playing!</span>");
gtk_video_set_autoplay(GTK_VIDEO(self->video), FALSE);
gtk_widget_set_hexpand(self->main_box, TRUE);
gtk_widget_set_vexpand(self->main_box, TRUE);
// Link signals for buttons
g_signal_connect(self->btn_add, "clicked", G_CALLBACK(btnadd_clicked), self);
g_signal_connect(self->btn_remove, "clicked", G_CALLBACK(btnremove_clicked), self);
g_signal_connect(self->btn_load, "clicked", G_CALLBACK(btnload_clicked), self);
g_signal_connect(self->btn_save, "clicked", G_CALLBACK(btnsave_clicked), self);
g_signal_connect(self->list_expander, "activate",
G_CALLBACK(my_media_player_expander_activate), self);
g_signal_connect(self->btn_play, "clicked", G_CALLBACK(btnplay_clicked), self);
g_signal_connect(self->btn_priv, "clicked", G_CALLBACK(btnpriv_clicked), self);
g_signal_connect(self->btn_next, "clicked", G_CALLBACK(btnnext_clicked), self);
g_signal_connect(self->btn_stop, "clicked", G_CALLBACK(btnstop_clicked), self);
g_signal_connect(self->btn_playmode, "clicked",
G_CALLBACK(btn_playmode_clicked), self);
// Create store and list view
self->music_store = g_list_store_new(MEDIA_ITEM_TYPE);
self->music_selection = gtk_single_selection_new(G_LIST_MODEL(self->music_store));
self->column_view = gtk_column_view_new(GTK_SELECTION_MODEL(self->music_selection));
gtk_widget_set_vexpand(self->column_view, TRUE);
// Create factory for renderer
self->filename_factory = gtk_signal_list_item_factory_new();
self->filename_column = gtk_column_view_column_new("File Name",
self->filename_factory);
g_signal_connect(self->filename_factory, "setup",
G_CALLBACK(filename_factory_setup), NULL);
g_signal_connect(self->filename_factory, "bind",
G_CALLBACK(filename_factory_bind), NULL);
g_signal_connect(self->column_view, "activate", G_CALLBACK(column_view_activated), self);
gtk_column_view_append_column(GTK_COLUMN_VIEW(self->column_view),
self->filename_column);
gtk_widget_set_size_request(self->column_view, 300, 250);
// Add a timer for music playing
self->music_loaded = FALSE;
g_timeout_add(1, lyric_time_func, self);
// Load a default playlist
load_playlist("playlist.json", self);
// Default Play mode is List_Repeat mode
self->current_play_mode = PlayMode::List_Repeat;
// Add widgets
gtk_box_append(GTK_BOX(self->main_box), self->video);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_lyrics),
self->label_lyrics);
gtk_box_append(GTK_BOX(self->main_box), self->scrolled_lyrics);
gtk_box_append(GTK_BOX(self->ctrl_box), self->btn_priv);
gtk_box_append(GTK_BOX(self->ctrl_box), self->btn_play);
gtk_box_append(GTK_BOX(self->ctrl_box), self->btn_next);
gtk_box_append(GTK_BOX(self->ctrl_box), self->btn_stop);
gtk_box_append(GTK_BOX(self->ctrl_box), self->btn_playmode);
gtk_box_append(GTK_BOX(self->main_box), self->ctrl_box);
gtk_box_append(GTK_BOX(self->btn_box), self->btn_add);
gtk_box_append(GTK_BOX(self->btn_box), self->btn_remove);
gtk_box_append(GTK_BOX(self->btn_box), self->btn_load);
gtk_box_append(GTK_BOX(self->btn_box), self->btn_save);
gtk_box_append(GTK_BOX(self->list_box), self->btn_box);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window),
self->column_view);
gtk_box_append(GTK_BOX(self->list_box), self->scrolled_window);
gtk_expander_set_child(GTK_EXPANDER(self->list_expander), self->list_box);
gtk_box_append(GTK_BOX(self->main_box), self->list_expander);
gtk_window_set_child(GTK_WINDOW(self), self->main_box);
}
static void my_media_player_class_init(MyMediaPlayerClass *klass)
{
GTK_WINDOW_CLASS(klass)->close_request = my_media_player_close_request;
}
MyMediaPlayer *my_media_player_new(GtkApplication *app)
{
// Create a window for media player
return MYMEDIA_PLAYER(g_object_new(my_media_player_get_type(),
"application", app, NULL));
}

View File

@ -1,5 +1,5 @@
#include "ScoresItem.h"
#define NAME_MAX 256
#include "MyLimit.h"
struct _ScoresItem
{