Initial commit

This commit is contained in:
daleclack 2024-05-13 23:42:52 +08:00
commit 8d5dbef861
41 changed files with 1882 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "GCR_CMake"]
path = GCR_CMake
url = https://github.com/daleclack/GCR_CMake.git

1
GCR_CMake Submodule

@ -0,0 +1 @@
Subproject commit 6ea69eca6817aead20a2cb87cdd08ab8e9a173cd

102
Gtk4/CMakeLists.txt Normal file
View File

@ -0,0 +1,102 @@
set(CMAKE_CXX_STANDARD 17)
cmake_minimum_required(VERSION 3.5.0)
project(gtk154_mediaplayer3 VERSION 1.0.0)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../GCR_CMake/macros)
include(GlibCompileResourcesSupport)
include(CTest)
enable_testing()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
include_directories(.)
include_directories(..)
#Find PkgConfig to use gtkmm3
find_package (PkgConfig REQUIRED)
pkg_check_modules (GTK4 REQUIRED gtk4)
include_directories (${GTK4_INCLUDE_DIRS})
link_directories (${GTK4_LIBRARY_DIRS})
#Find Gettext
# find_package (Gettext REQUIRED)
# set(PO_DIR ${CMAKE_BINARY_DIR}/po/zh_CN/LC_MESSAGES)
#Source files
set(SOURCE_FILE src/main.cpp src/MyMediaPlayer.cpp src/MyItem.cpp src/LyricsParser.cpp)
#Compile Resource
set(RESOURCE_LIST
icons/scalable/status/media-eject.svg
icons/scalable/status/media-mount.svg
icons/scalable/status/media-playback-pause.svg
icons/scalable/status/media-playback-start.svg
icons/scalable/status/media-playback-stop.svg
icons/scalable/status/media-playlist-append.svg
icons/scalable/status/media-playlist-normal.svg
icons/scalable/status/media-playlist-play.svg
icons/scalable/status/media-playlist-repeat-one.svg
icons/scalable/status/media-playlist-repeat.svg
icons/scalable/status/media-playlist-shuffle.svg
icons/scalable/status/media-seek-backward.svg
icons/scalable/status/media-seek-forward.svg
icons/scalable/status/media-skip-backward.svg
icons/scalable/status/media-skip-forward.svg
icons/scalable/status/media-eject-dark.svg
icons/scalable/status/media-mount-dark.svg
icons/scalable/status/media-playback-pause-dark.svg
icons/scalable/status/media-playback-start-dark.svg
icons/scalable/status/media-playback-stop-dark.svg
icons/scalable/status/media-playlist-append-dark.svg
icons/scalable/status/media-playlist-normal-dark.svg
icons/scalable/status/media-playlist-play-dark.svg
icons/scalable/status/media-playlist-repeat-one-dark.svg
icons/scalable/status/media-playlist-repeat-dark.svg
icons/scalable/status/media-playlist-shuffle-dark.svg
icons/scalable/status/media-seek-backward-dark.svg
icons/scalable/status/media-seek-forward-dark.svg
icons/scalable/status/media-skip-backward-dark.svg
icons/scalable/status/media-skip-forward-dark.svg
)
compile_gresources(RESOURCE_FILE
XML_OUT
TYPE EMBED_C
RESOURCES ${RESOURCE_LIST}
PREFIX "/org/gtk/daleclack"
SOURCE_DIR ${PROJECT_SOURCE_DIR}/res)
# Add a custom target to the makefile. Now make builds our resource file.
# It depends on the output RESOURCE_FILE.
add_custom_target(resource ALL DEPENDS ${RESOURCE_FILE})
#For win32 platform,use rc resource and .ico icon
if(WIN32)
SET(CMAKE_RC_COMPILER windres)
set(app_WINRC ../icon.rc)
set_property(SOURCE ../icon.rc APPEND PROPERTY
OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/../icon.ico
)
add_executable(${PROJECT_NAME} ${app_WINRC} ${SOURCE_FILE} ${RESOURCE_FILE})
add_custom_command( TARGET ${PROJECT_NAME}
COMMAND echo * > ${CMAKE_BINARY_DIR}/.gitignore
COMMAND echo **/* > ${CMAKE_BINARY_DIR}/.hgignore)
else()
add_executable(${PROJECT_NAME} ${SOURCE_FILE} ${RESOURCE_FILE})
add_custom_command( TARGET ${PROJECT_NAME}
COMMAND echo \"*\" > ${CMAKE_BINARY_DIR}/.gitignore
COMMAND echo \"**/*\" > ${CMAKE_BINARY_DIR}/.hgignore)
endif(WIN32)
#Add command to generate .gitignore and .mo files
# add_custom_command( TARGET ${PROJECT_NAME}
# COMMAND mkdir -p ${PO_DIR}
# COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${CMAKE_SOURCE_DIR}/po/zh_CN.po -o ${PO_DIR}/${PROJECT_NAME}.mo)
SET (CMAKE_EXTRA_CXX_FLAGS ${GTK4_CFLAGS_OTHER})
target_link_libraries (${PROJECT_NAME} ${GTK4_LIBRARIES} -lpthread)

5
Gtk4/README Normal file
View File

@ -0,0 +1,5 @@
# README of My Reminder
This simple application is a reminder for the date,
and the window show the days left for the deadline set by user
The reminded date and color of text can be changed by preferences dialog.

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

486
Gtk4/src/LyricsParser.cpp Normal file
View File

@ -0,0 +1,486 @@
#include "MyItem.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 && lyrics)
{
// 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 load_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];
char *color_str;
// 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
color_str = my_media_player_get_color(player);
snprintf(label_string, lyrics_max_length,
"<span color=\"%s\" size='12pt'>%s</span>", color_str, current_lyrics);
gtk_label_set_markup(my_media_player_get_lyrics_widget(player),
label_string);
line_read = FALSE;
}
// free(color_str);
}
// 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
load_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;
}

9
Gtk4/src/LyricsParser.h Normal file
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 load_lyrics(MyMediaPlayer *player);

40
Gtk4/src/MyItem.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "MyItem.h"
#include <cstring>
struct _MyItem
{
GObject parent_instance;
char disp_name[name_max_length];
char file_name[path_max_length];
};
G_DEFINE_TYPE(MyItem, my_item, G_TYPE_OBJECT)
const char *my_item_get_filename(MyItem *item)
{
// Get true file name
return item->file_name;
}
const char *my_item_get_dispname(MyItem *item)
{
// Get Base name
return item->disp_name;
}
static void my_item_init(MyItem *self)
{
}
static void my_item_class_init(MyItemClass *klass)
{
}
MyItem *my_item_new(const char *dispname, const char *filename)
{
// Create a new item
MyItem *item = MY_ITEM(g_object_new(my_item_get_type(), NULL));
strncpy(item->disp_name, dispname, name_max_length);
strncpy(item->file_name, filename, path_max_length);
return item;
}

16
Gtk4/src/MyItem.h Normal file
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 MY_ITEM_TYPE (my_item_get_type())
G_DECLARE_FINAL_TYPE(MyItem, my_item, MY, ITEM, GObject)
const char *my_item_get_filename(MyItem *item);
const char *my_item_get_dispname(MyItem *item);
MyItem *my_item_new(const char *dispname, const char *filename);

779
Gtk4/src/MyMediaPlayer.cpp Normal file
View File

@ -0,0 +1,779 @@
#include "MyMediaPlayer.h"
#include "LyricsParser.h"
#include "MyItem.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 *btn_color;
GtkColorDialog *dialog_color;
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,
my_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),
my_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
MyItem *item = MY_ITEM(g_list_model_get_item(G_LIST_MODEL(player->music_store), i));
disp_name = std::string(my_item_get_dispname(item));
file_path = std::string(my_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(MyItem *item, MyMediaPlayer *player)
{
// Load the audio file
GFile *music_file;
const char *file_name, *disp_name;
file_name = my_item_get_filename(item);
music_file = g_file_new_for_path(file_name);
disp_name = my_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
load_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
MyItem *item;
// Get selection and open the music file
item = MY_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
MyItem *file_item = MY_ITEM(gtk_list_item_get_item(item));
gtk_label_set_label(GTK_LABEL(label),
my_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
MyItem *item = MY_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
MyItem *item = MY_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
MyItem *item = MY_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
MyItem *item = MY_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;
}
char *my_media_player_get_color(MyMediaPlayer *player)
{
const GdkRGBA *color_rgba;
color_rgba = gtk_color_dialog_button_get_rgba(
GTK_COLOR_DIALOG_BUTTON(player->btn_color));
char *color_str = g_strdup_printf("#%02X%02X%02X",
(int)(color_rgba->red * 256),
(int)(color_rgba->green * 256),
(int)(color_rgba->blue * 256));
return color_str;
}
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->dialog_color = gtk_color_dialog_new();
self->btn_color = gtk_color_dialog_button_new(self->dialog_color);
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(MY_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->ctrl_box), self->btn_color);
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));
}

39
Gtk4/src/MyMediaPlayer.h Normal file
View File

@ -0,0 +1,39 @@
#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);
char *my_media_player_get_color(MyMediaPlayer *player);
MyMediaPlayer *my_media_player_new(GtkApplication *app);

17
Gtk4/src/main.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "MyMediaPlayer.h"
static void gtkmain(GtkApplication *app, gpointer user_data)
{
// Create a media player window and run
MyMediaPlayer *media_player = my_media_player_new(app);
gtk_window_present(GTK_WINDOW(media_player));
}
int main(int argc, char **argv)
{
// Create a application and run
GtkApplication *app;
app = gtk_application_new("org.gtk.daleclack", G_APPLICATION_NON_UNIQUE);
g_signal_connect(app, "activate", G_CALLBACK(gtkmain), NULL);
return g_application_run(G_APPLICATION(app), argc, argv);
}