Get to the point where we are fairly happy with our uncrustify config. Fixes #6.

This commit is contained in:
Joshua Strobl 2021-05-11 20:05:04 +03:00
parent d07d3dfe50
commit 62de9c2032
58 changed files with 4811 additions and 1946 deletions

12
.vscode/tasks.json vendored
View file

@ -13,6 +13,18 @@
], ],
"problemMatcher": [] "problemMatcher": []
}, },
{
"label": "Format",
"type": "shell",
"command": "uncrustify",
"args": [
"-c",
"jsc.cfg",
"--no-backup",
"**/*.c",
"**/*.h"
]
},
{ {
"label": "Meson Configure and Build", "label": "Meson Configure and Build",
"type": "shell", "type": "shell",

44
jsc.cfg
View file

@ -157,27 +157,27 @@ sp_before_ptr_star = ignore # ignore/add/remove/force
# Add or remove space before pointer star '*' that isn't followed by a # Add or remove space before pointer star '*' that isn't followed by a
# variable name. If set to ignore, sp_before_ptr_star is used instead. # variable name. If set to ignore, sp_before_ptr_star is used instead.
sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force sp_before_unnamed_ptr_star = remove # ignore/add/remove/force
# Add or remove space between pointer stars '*'. # Add or remove space between pointer stars '*'.
sp_between_ptr_star = remove # ignore/add/remove/force sp_between_ptr_star = ignore # ignore/add/remove/force
# Add or remove space after pointer star '*', if followed by a word. # Add or remove space after pointer star '*', if followed by a word.
# #
# Overrides sp_type_func. # Overrides sp_type_func.
sp_after_ptr_star = remove # ignore/add/remove/force sp_after_ptr_star = force # ignore/add/remove/force
# Add or remove space after pointer caret '^', if followed by a word. # Add or remove space after pointer caret '^', if followed by a word.
sp_after_ptr_block_caret = ignore # ignore/add/remove/force sp_after_ptr_block_caret = ignore # ignore/add/remove/force
# Add or remove space after pointer star '*', if followed by a qualifier. # Add or remove space after pointer star '*', if followed by a qualifier.
sp_after_ptr_star_qualifier = remove # ignore/add/remove/force sp_after_ptr_star_qualifier = force # ignore/add/remove/force
# Add or remove space after a pointer star '*', if followed by a function # Add or remove space after a pointer star '*', if followed by a function
# prototype or function definition. # prototype or function definition.
# #
# Overrides sp_after_ptr_star and sp_type_func. # Overrides sp_after_ptr_star and sp_type_func.
sp_after_ptr_star_func = remove # ignore/add/remove/force sp_after_ptr_star_func = force # ignore/add/remove/force
# Add or remove space after a pointer star '*', if followed by an open # Add or remove space after a pointer star '*', if followed by an open
# parenthesis, as in 'void* (*)(). # parenthesis, as in 'void* (*)().
@ -1055,7 +1055,7 @@ donot_indent_func_def_close_paren = false # true/false
# Whether to collapse empty blocks between '{' and '}'. # Whether to collapse empty blocks between '{' and '}'.
# If true, overrides nl_inside_empty_func # If true, overrides nl_inside_empty_func
nl_collapse_empty_body = true # true/false nl_collapse_empty_body = false # true/false
# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'. # Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'.
nl_assign_leave_one_liners = false # true/false nl_assign_leave_one_liners = false # true/false
@ -1349,10 +1349,10 @@ nl_func_call_paren = ignore # ignore/add/remove/force
nl_func_call_paren_empty = ignore # ignore/add/remove/force nl_func_call_paren_empty = ignore # ignore/add/remove/force
# Add or remove newline after '(' in a function declaration. # Add or remove newline after '(' in a function declaration.
nl_func_decl_start = remove # ignore/add/remove/force nl_func_decl_start = force # ignore/add/remove/force
# Add or remove newline after '(' in a function definition. # Add or remove newline after '(' in a function definition.
nl_func_def_start = remove # ignore/add/remove/force nl_func_def_start = force # ignore/add/remove/force
# Overrides nl_func_decl_start when there is only one parameter. # Overrides nl_func_decl_start when there is only one parameter.
nl_func_decl_start_single = remove # ignore/add/remove/force nl_func_decl_start_single = remove # ignore/add/remove/force
@ -1369,13 +1369,13 @@ nl_func_decl_start_multi_line = false # true/false
nl_func_def_start_multi_line = true # true/false nl_func_def_start_multi_line = true # true/false
# Add or remove newline after each ',' in a function declaration. # Add or remove newline after each ',' in a function declaration.
nl_func_decl_args = ignore # ignore/add/remove/force nl_func_decl_args = force # ignore/add/remove/force
# Add or remove newline after each ',' in a function definition. # Add or remove newline after each ',' in a function definition.
nl_func_def_args = remove # ignore/add/remove/force nl_func_def_args = force # ignore/add/remove/force
# Add or remove newline after each ',' in a function call. # Add or remove newline after each ',' in a function call.
nl_func_call_args = ignore # ignore/add/remove/force nl_func_call_args = remove # ignore/add/remove/force
# Whether to add a newline after each ',' in a function declaration if '(' # Whether to add a newline after each ',' in a function declaration if '('
# and ')' are in different lines. If false, nl_func_decl_args is used instead. # and ')' are in different lines. If false, nl_func_decl_args is used instead.
@ -1386,10 +1386,10 @@ nl_func_decl_args_multi_line = true # true/false
nl_func_def_args_multi_line = true # true/false nl_func_def_args_multi_line = true # true/false
# Add or remove newline before the ')' in a function declaration. # Add or remove newline before the ')' in a function declaration.
nl_func_decl_end = add # ignore/add/remove/force nl_func_decl_end = force # ignore/add/remove/force
# Add or remove newline before the ')' in a function definition. # Add or remove newline before the ')' in a function definition.
nl_func_def_end = ignore # ignore/add/remove/force nl_func_def_end = force # ignore/add/remove/force
# Overrides nl_func_decl_end when there is only one parameter. # Overrides nl_func_decl_end when there is only one parameter.
nl_func_decl_end_single = remove # ignore/add/remove/force nl_func_decl_end_single = remove # ignore/add/remove/force
@ -1399,7 +1399,7 @@ nl_func_def_end_single = remove # ignore/add/remove/force
# Whether to add a newline before ')' in a function declaration if '(' and ')' # Whether to add a newline before ')' in a function declaration if '(' and ')'
# are in different lines. If false, nl_func_decl_end is used instead. # are in different lines. If false, nl_func_decl_end is used instead.
nl_func_decl_end_multi_line = false # true/false nl_func_decl_end_multi_line = true # true/false
# Whether to add a newline before ')' in a function definition if '(' and ')' # Whether to add a newline before ')' in a function definition if '(' and ')'
# are in different lines. If false, nl_func_def_end is used instead. # are in different lines. If false, nl_func_def_end is used instead.
@ -1456,7 +1456,7 @@ nl_fdef_brace_cond = ignore # ignore/add/remove/force
nl_return_expr = remove # ignore/add/remove/force nl_return_expr = remove # ignore/add/remove/force
# Whether to add a newline after semicolons, except in 'for' statements. # Whether to add a newline after semicolons, except in 'for' statements.
nl_after_semicolon = false # true/false nl_after_semicolon = true # true/false
# Whether to add a newline after the type in an unnamed temporary # Whether to add a newline after the type in an unnamed temporary
# direct-list-initialization. # direct-list-initialization.
@ -1643,7 +1643,7 @@ nl_before_func_class_proto = 0 # unsigned number
nl_before_func_class_def = 0 # unsigned number nl_before_func_class_def = 0 # unsigned number
# The number of newlines after a function prototype. # The number of newlines after a function prototype.
nl_after_func_proto = 0 # unsigned number nl_after_func_proto = 2 # unsigned number
# The number of newlines after a function prototype, if not followed by # The number of newlines after a function prototype, if not followed by
# another function prototype. # another function prototype.
@ -1734,13 +1734,13 @@ nl_after_multiline_comment = false # true/false
nl_after_label_colon = false # true/false nl_after_label_colon = false # true/false
# The number of newlines after '}' or ';' of a struct/enum/union definition. # The number of newlines after '}' or ';' of a struct/enum/union definition.
nl_after_struct = 0 # unsigned number nl_after_struct = 1 # unsigned number
# The number of newlines before a class definition. # The number of newlines before a class definition.
nl_before_class = 0 # unsigned number nl_before_class = 0 # unsigned number
# The number of newlines after '}' or ';' of a class definition. # The number of newlines after '}' or ';' of a class definition.
nl_after_class = 0 # unsigned number nl_after_class = 1 # unsigned number
# The number of newlines before a namespace. # The number of newlines before a namespace.
nl_before_namespace = 0 # unsigned number nl_before_namespace = 0 # unsigned number
@ -2291,9 +2291,6 @@ mod_full_brace_do = ignore # ignore/add/remove/force
# Add or remove braces on a single-line 'for' statement. # Add or remove braces on a single-line 'for' statement.
mod_full_brace_for = ignore # ignore/add/remove/force mod_full_brace_for = ignore # ignore/add/remove/force
# (Pawn) Add or remove braces on a single-line function definition.
mod_full_brace_function = ignore # ignore/add/remove/force
# Add or remove braces on a single-line 'if' statement. Braces will not be # Add or remove braces on a single-line 'if' statement. Braces will not be
# removed if the braced statement contains an 'else'. # removed if the braced statement contains an 'else'.
mod_full_brace_if = ignore # ignore/add/remove/force mod_full_brace_if = ignore # ignore/add/remove/force
@ -2337,10 +2334,7 @@ mod_full_brace_nl = 0 # unsigned number
mod_full_brace_nl_block_rem_mlcond = false # true/false mod_full_brace_nl_block_rem_mlcond = false # true/false
# Add or remove unnecessary parenthesis on 'return' statement. # Add or remove unnecessary parenthesis on 'return' statement.
mod_paren_on_return = ignore # ignore/add/remove/force mod_paren_on_return = ignore # ignore/add/remove/force´
# (Pawn) Whether to change optional semicolons to real semicolons.
mod_pawn_semicolon = false # true/false
# Whether to fully parenthesize Boolean expressions in 'while' and 'if' # Whether to fully parenthesize Boolean expressions in 'while' and 'if'
# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. # statement, as in 'if (a && b > c)' => 'if (a && (b > c))'.

View file

@ -26,52 +26,55 @@
#include "../koto-utils.h" #include "../koto-utils.h"
#include "../koto-window.h" #include "../koto-window.h"
extern KotoAddRemoveTrackPopover *koto_add_remove_track_popup; extern KotoAddRemoveTrackPopover * koto_add_remove_track_popup;
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoPageMusicLocal *music_local_page; extern KotoPageMusicLocal * music_local_page;
extern KotoPlaybackEngine *playback_engine; extern KotoPlaybackEngine * playback_engine;
extern KotoWindow *main_window; extern KotoWindow * main_window;
enum { enum {
SIGNAL_CLOSED, SIGNAL_CLOSED,
N_SIGNALS N_SIGNALS
}; };
static guint actionbar_signals[N_SIGNALS] = { 0 }; static guint actionbar_signals[N_SIGNALS] = {
0
};
struct _KotoActionBar { struct _KotoActionBar {
GObject parent_instance; GObject parent_instance;
GtkActionBar *main; GtkActionBar * main;
GtkWidget *center_box_content; GtkWidget * center_box_content;
GtkWidget *start_box_content; GtkWidget * start_box_content;
GtkWidget *stop_box_content; GtkWidget * stop_box_content;
KotoButton *close_button; KotoButton * close_button;
GtkWidget *go_to_artist; GtkWidget * go_to_artist;
GtkWidget *playlists; GtkWidget * playlists;
GtkWidget *play_track; GtkWidget * play_track;
GtkWidget *remove_from_playlist; GtkWidget * remove_from_playlist;
GList *current_list; GList * current_list;
gchar *current_album_uuid; gchar * current_album_uuid;
gchar *current_playlist_uuid; gchar * current_playlist_uuid;
KotoActionBarRelative relative; KotoActionBarRelative relative;
}; };
struct _KotoActionBarClass { struct _KotoActionBarClass {
GObjectClass parent_class; GObjectClass parent_class;
void (* closed) (KotoActionBar *self); void (* closed) (KotoActionBar * self);
}; };
G_DEFINE_TYPE(KotoActionBar, koto_action_bar, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoActionBar, koto_action_bar, G_TYPE_OBJECT);
KotoActionBar* action_bar; KotoActionBar* action_bar;
static void koto_action_bar_class_init(KotoActionBarClass *c) { static void koto_action_bar_class_init(KotoActionBarClass * c) {
GObjectClass *gobject_class = G_OBJECT_CLASS(c); GObjectClass * gobject_class = G_OBJECT_CLASS(c);
actionbar_signals[SIGNAL_CLOSED] = g_signal_new( actionbar_signals[SIGNAL_CLOSED] = g_signal_new(
"closed", "closed",
@ -86,7 +89,7 @@ static void koto_action_bar_class_init(KotoActionBarClass *c) {
); );
} }
static void koto_action_bar_init(KotoActionBar *self) { static void koto_action_bar_init(KotoActionBar * self) {
self->main = GTK_ACTION_BAR(gtk_action_bar_new()); // Create a new action bar self->main = GTK_ACTION_BAR(gtk_action_bar_new()); // Create a new action bar
self->current_list = NULL; self->current_list = NULL;
@ -135,7 +138,7 @@ static void koto_action_bar_init(KotoActionBar *self) {
koto_action_bar_toggle_reveal(self, FALSE); // Hide by default koto_action_bar_toggle_reveal(self, FALSE); // Hide by default
} }
void koto_action_bar_close(KotoActionBar *self) { void koto_action_bar_close(KotoActionBar * self) {
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
} }
@ -151,7 +154,7 @@ void koto_action_bar_close(KotoActionBar *self) {
); );
} }
GtkActionBar* koto_action_bar_get_main(KotoActionBar *self) { GtkActionBar * koto_action_bar_get_main(KotoActionBar * self) {
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return NULL; return NULL;
} }
@ -159,14 +162,27 @@ GtkActionBar* koto_action_bar_get_main(KotoActionBar *self) {
return self->main; return self->main;
} }
void koto_action_bar_handle_close_button_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_action_bar_handle_close_button_clicked(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
koto_action_bar_close(data); koto_action_bar_close(data);
} }
void koto_action_bar_handle_go_to_artist_button_clicked(GtkButton *button, gpointer data) { void koto_action_bar_handle_go_to_artist_button_clicked(
GtkButton * button,
gpointer data
) {
(void) button; (void) button;
KotoActionBar *self = data; KotoActionBar * self = data;
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
@ -176,13 +192,16 @@ void koto_action_bar_handle_go_to_artist_button_clicked(GtkButton *button, gpoin
return; return;
} }
KotoIndexedTrack *selected_track = g_list_nth_data(self->current_list, 0); // Get the first item KotoIndexedTrack * selected_track = g_list_nth_data(self->current_list, 0); // Get the first item
if (!KOTO_IS_INDEXED_TRACK(selected_track)) { // Not a track if (!KOTO_IS_INDEXED_TRACK(selected_track)) { // Not a track
return; return;
} }
gchar *artist_uuid = NULL; gchar * artist_uuid = NULL;
g_object_get( g_object_get(
selected_track, selected_track,
"artist-uuid", "artist-uuid",
@ -194,9 +213,13 @@ void koto_action_bar_handle_go_to_artist_button_clicked(GtkButton *button, gpoin
koto_window_go_to_page(main_window, "music.local"); // Navigate to the local music stack so we can see the substack page koto_window_go_to_page(main_window, "music.local"); // Navigate to the local music stack so we can see the substack page
koto_action_bar_close(self); // Close the action bar koto_action_bar_close(self); // Close the action bar
} }
void koto_action_bar_handle_playlists_button_clicked(GtkButton *button, gpointer data) { void koto_action_bar_handle_playlists_button_clicked(
GtkButton * button,
gpointer data
) {
(void) button; (void) button;
KotoActionBar *self = data; KotoActionBar * self = data;
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
@ -211,9 +234,13 @@ void koto_action_bar_handle_playlists_button_clicked(GtkButton *button, gpointer
gtk_widget_show(GTK_WIDGET(koto_add_remove_track_popup)); gtk_widget_show(GTK_WIDGET(koto_add_remove_track_popup));
} }
void koto_action_bar_handle_play_track_button_clicked(GtkButton *button, gpointer data) { void koto_action_bar_handle_play_track_button_clicked(
GtkButton * button,
gpointer data
) {
(void) button; (void) button;
KotoActionBar *self = data; KotoActionBar * self = data;
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
@ -223,7 +250,8 @@ void koto_action_bar_handle_play_track_button_clicked(GtkButton *button, gpointe
goto doclose; goto doclose;
} }
KotoIndexedTrack *track = g_list_nth_data(self->current_list, 0); // Get the first track KotoIndexedTrack * track = g_list_nth_data(self->current_list, 0); // Get the first track
if (!KOTO_IS_INDEXED_TRACK(track)) { // Not a track if (!KOTO_IS_INDEXED_TRACK(track)) { // Not a track
goto doclose; goto doclose;
@ -235,9 +263,13 @@ doclose:
koto_action_bar_close(self); koto_action_bar_close(self);
} }
void koto_action_bar_handle_remove_from_playlist_button_clicked(GtkButton *button, gpointer data) { void koto_action_bar_handle_remove_from_playlist_button_clicked(
GtkButton * button,
gpointer data
) {
(void) button; (void) button;
KotoActionBar *self = data; KotoActionBar * self = data;
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
@ -251,15 +283,18 @@ void koto_action_bar_handle_remove_from_playlist_button_clicked(GtkButton *butto
goto doclose; goto doclose;
} }
KotoPlaylist *playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->current_playlist_uuid); KotoPlaylist * playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->current_playlist_uuid);
if (!KOTO_IS_PLAYLIST(playlist)) { // Not a playlist if (!KOTO_IS_PLAYLIST(playlist)) { // Not a playlist
goto doclose; goto doclose;
} }
GList *cur_list; GList * cur_list;
for (cur_list = self->current_list; cur_list != NULL; cur_list = cur_list->next) { // For each KotoIndexedTrack for (cur_list = self->current_list; cur_list != NULL; cur_list = cur_list->next) { // For each KotoIndexedTrack
KotoIndexedTrack *track = cur_list->data; KotoIndexedTrack * track = cur_list->data;
koto_playlist_remove_track_by_uuid(playlist, koto_indexed_track_get_uuid(track)); // Remove this track koto_playlist_remove_track_by_uuid(playlist, koto_indexed_track_get_uuid(track)); // Remove this track
} }
@ -267,7 +302,11 @@ doclose:
koto_action_bar_close(self); koto_action_bar_close(self);
} }
void koto_action_bar_set_tracks_in_album_selection(KotoActionBar *self, gchar *album_uuid, GList *tracks) { void koto_action_bar_set_tracks_in_album_selection(
KotoActionBar * self,
gchar * album_uuid,
GList * tracks
) {
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
} }
@ -297,7 +336,11 @@ void koto_action_bar_set_tracks_in_album_selection(KotoActionBar *self, gchar *a
gtk_widget_hide(GTK_WIDGET(self->remove_from_playlist)); gtk_widget_hide(GTK_WIDGET(self->remove_from_playlist));
} }
void koto_action_bar_set_tracks_in_playlist_selection(KotoActionBar *self, gchar *playlist_uuid, GList *tracks) { void koto_action_bar_set_tracks_in_playlist_selection(
KotoActionBar * self,
gchar * playlist_uuid,
GList * tracks
) {
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
} }
@ -318,13 +361,18 @@ void koto_action_bar_set_tracks_in_playlist_selection(KotoActionBar *self, gchar
self->current_list = g_list_copy(tracks); self->current_list = g_list_copy(tracks);
gboolean single_selected = g_list_length(tracks) == 1; gboolean single_selected = g_list_length(tracks) == 1;
koto_action_bar_toggle_go_to_artist_visibility(self, single_selected); koto_action_bar_toggle_go_to_artist_visibility(self, single_selected);
koto_action_bar_toggle_play_button_visibility(self, single_selected); koto_action_bar_toggle_play_button_visibility(self, single_selected);
gtk_widget_hide(GTK_WIDGET(self->playlists)); gtk_widget_hide(GTK_WIDGET(self->playlists));
gtk_widget_show(GTK_WIDGET(self->remove_from_playlist)); gtk_widget_show(GTK_WIDGET(self->remove_from_playlist));
} }
void koto_action_bar_toggle_go_to_artist_visibility(KotoActionBar *self, gboolean visible) { void koto_action_bar_toggle_go_to_artist_visibility(
KotoActionBar * self,
gboolean visible
) {
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
} }
@ -332,7 +380,10 @@ void koto_action_bar_toggle_go_to_artist_visibility(KotoActionBar *self, gboolea
(visible) ? gtk_widget_show(GTK_WIDGET(self->go_to_artist)) : gtk_widget_hide(GTK_WIDGET(self->go_to_artist)); (visible) ? gtk_widget_show(GTK_WIDGET(self->go_to_artist)) : gtk_widget_hide(GTK_WIDGET(self->go_to_artist));
} }
void koto_action_bar_toggle_play_button_visibility(KotoActionBar *self, gboolean visible) { void koto_action_bar_toggle_play_button_visibility(
KotoActionBar * self,
gboolean visible
) {
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
} }
@ -340,7 +391,10 @@ void koto_action_bar_toggle_play_button_visibility(KotoActionBar *self, gboolean
(visible) ? gtk_widget_show(GTK_WIDGET(self->play_track)) : gtk_widget_hide(GTK_WIDGET(self->play_track)); (visible) ? gtk_widget_show(GTK_WIDGET(self->play_track)) : gtk_widget_hide(GTK_WIDGET(self->play_track));
} }
void koto_action_bar_toggle_reveal(KotoActionBar *self, gboolean state) { void koto_action_bar_toggle_reveal(
KotoActionBar * self,
gboolean state
) {
if (!KOTO_IS_ACTION_BAR(self)) { if (!KOTO_IS_ACTION_BAR(self)) {
return; return;
} }
@ -348,7 +402,7 @@ void koto_action_bar_toggle_reveal(KotoActionBar *self, gboolean state) {
gtk_action_bar_set_revealed(self->main, state); gtk_action_bar_set_revealed(self->main, state);
} }
KotoActionBar* koto_action_bar_new() { KotoActionBar * koto_action_bar_new() {
return g_object_new( return g_object_new(
KOTO_TYPE_ACTION_BAR, KOTO_TYPE_ACTION_BAR,
NULL NULL

View file

@ -39,20 +39,67 @@ GType koto_action_bar_get_type(void) G_GNUC_CONST;
/** /**
* Action Bar Functions * Action Bar Functions
**/ **/
KotoActionBar* koto_action_bar_new(void); KotoActionBar * koto_action_bar_new(void);
void koto_action_bar_close(KotoActionBar *self);
GtkActionBar* koto_action_bar_get_main(KotoActionBar *self); void koto_action_bar_close(KotoActionBar * self);
void koto_action_bar_handle_close_button_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data);
void koto_action_bar_handle_go_to_artist_button_clicked(GtkButton *button, gpointer data); GtkActionBar * koto_action_bar_get_main(KotoActionBar * self);
void koto_action_bar_handle_playlists_button_clicked(GtkButton *button, gpointer data);
void koto_action_bar_handle_play_track_button_clicked(GtkButton *button, gpointer data); void koto_action_bar_handle_close_button_clicked(
void koto_action_bar_handle_remove_from_playlist_button_clicked(GtkButton *button, gpointer data); GtkGestureClick * gesture,
void koto_action_bar_set_tracks_in_album_selection(KotoActionBar *self, gchar *album_uuid, GList *tracks); int n_press,
void koto_action_bar_set_tracks_in_playlist_selection(KotoActionBar *self, gchar *playlist_uuid, GList *tracks); double x,
void koto_action_bar_toggle_go_to_artist_visibility(KotoActionBar *self, gboolean visible); double y,
void koto_action_bar_toggle_play_button_visibility(KotoActionBar *self, gboolean visible); gpointer data
void koto_action_bar_toggle_reveal(KotoActionBar *self, gboolean state); );
void koto_action_bar_handle_go_to_artist_button_clicked(
GtkButton * button,
gpointer data
);
void koto_action_bar_handle_playlists_button_clicked(
GtkButton * button,
gpointer data
);
void koto_action_bar_handle_play_track_button_clicked(
GtkButton * button,
gpointer data
);
void koto_action_bar_handle_remove_from_playlist_button_clicked(
GtkButton * button,
gpointer data
);
void koto_action_bar_set_tracks_in_album_selection(
KotoActionBar * self,
gchar * album_uuid,
GList * tracks
);
void koto_action_bar_set_tracks_in_playlist_selection(
KotoActionBar * self,
gchar * playlist_uuid,
GList * tracks
);
void koto_action_bar_toggle_go_to_artist_visibility(
KotoActionBar * self,
gboolean visible
);
void koto_action_bar_toggle_play_button_visibility(
KotoActionBar * self,
gboolean visible
);
void koto_action_bar_toggle_reveal(
KotoActionBar * self,
gboolean state
);
G_END_DECLS G_END_DECLS

View file

@ -24,10 +24,10 @@
struct _KotoCoverArtButton { struct _KotoCoverArtButton {
GObject parent_instance; GObject parent_instance;
GtkWidget *art; GtkWidget * art;
GtkWidget *main; GtkWidget * main;
GtkWidget *revealer; GtkWidget * revealer;
KotoButton *play_pause_button; KotoButton * play_pause_button;
guint height; guint height;
guint width; guint width;
@ -43,12 +43,27 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
static void koto_cover_art_button_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); NULL,
static void koto_cover_art_button_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); };
static void koto_cover_art_button_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_cover_art_button_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_cover_art_button_class_init(KotoCoverArtButtonClass * c) {
GObjectClass * gobject_class;
static void koto_cover_art_button_class_init(KotoCoverArtButtonClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->get_property = koto_cover_art_button_get_property; gobject_class->get_property = koto_cover_art_button_get_property;
gobject_class->set_property = koto_cover_art_button_set_property; gobject_class->set_property = koto_cover_art_button_set_property;
@ -60,7 +75,7 @@ static void koto_cover_art_button_class_init(KotoCoverArtButtonClass *c) {
0, 0,
G_MAXUINT, G_MAXUINT,
0, 0,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_WRITABLE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_WRITABLE
); );
props[PROP_DESIRED_WIDTH] = g_param_spec_uint( props[PROP_DESIRED_WIDTH] = g_param_spec_uint(
@ -70,7 +85,7 @@ static void koto_cover_art_button_class_init(KotoCoverArtButtonClass *c) {
0, 0,
G_MAXUINT, G_MAXUINT,
0, 0,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_WRITABLE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_WRITABLE
); );
props[PROP_ART_PATH] = g_param_spec_string( props[PROP_ART_PATH] = g_param_spec_string(
@ -78,20 +93,22 @@ static void koto_cover_art_button_class_init(KotoCoverArtButtonClass *c) {
"Path to art", "Path to art",
"Path to art", "Path to art",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_WRITABLE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_WRITABLE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_cover_art_button_init(KotoCoverArtButton *self) { static void koto_cover_art_button_init(KotoCoverArtButton * self) {
self->main = gtk_overlay_new(); // Create our overlay container self->main = gtk_overlay_new(); // Create our overlay container
gtk_widget_add_css_class(self->main, "cover-art-button"); gtk_widget_add_css_class(self->main, "cover-art-button");
self->revealer = gtk_revealer_new(); // Create a new revealer self->revealer = gtk_revealer_new(); // Create a new revealer
gtk_revealer_set_transition_type(GTK_REVEALER(self->revealer), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE); gtk_revealer_set_transition_type(GTK_REVEALER(self->revealer), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE);
gtk_revealer_set_transition_duration(GTK_REVEALER(self->revealer), 400); gtk_revealer_set_transition_duration(GTK_REVEALER(self->revealer), 400);
GtkWidget *controls = gtk_center_box_new(); // Create a center box for the controls GtkWidget * controls = gtk_center_box_new(); // Create a center box for the controls
self->play_pause_button = koto_button_new_with_icon("", "media-playback-start-symbolic", "media-playback-pause-symbolic", KOTO_BUTTON_PIXBUF_SIZE_NORMAL); self->play_pause_button = koto_button_new_with_icon("", "media-playback-start-symbolic", "media-playback-pause-symbolic", KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
gtk_center_box_set_center_widget(GTK_CENTER_BOX(controls), GTK_WIDGET(self->play_pause_button)); gtk_center_box_set_center_widget(GTK_CENTER_BOX(controls), GTK_WIDGET(self->play_pause_button));
@ -99,13 +116,20 @@ static void koto_cover_art_button_init(KotoCoverArtButton *self) {
koto_cover_art_button_hide_overlay_controls(NULL, self); // Hide by default koto_cover_art_button_hide_overlay_controls(NULL, self); // Hide by default
gtk_overlay_add_overlay(GTK_OVERLAY(self->main), self->revealer); // Add our revealer as the overlay gtk_overlay_add_overlay(GTK_OVERLAY(self->main), self->revealer); // Add our revealer as the overlay
GtkEventController *motion_controller = gtk_event_controller_motion_new(); // Create our new motion event controller to track mouse leave and enter GtkEventController * motion_controller = gtk_event_controller_motion_new(); // Create our new motion event controller to track mouse leave and enter
g_signal_connect(motion_controller, "enter", G_CALLBACK(koto_cover_art_button_show_overlay_controls), self); g_signal_connect(motion_controller, "enter", G_CALLBACK(koto_cover_art_button_show_overlay_controls), self);
g_signal_connect(motion_controller, "leave", G_CALLBACK(koto_cover_art_button_hide_overlay_controls), self); g_signal_connect(motion_controller, "leave", G_CALLBACK(koto_cover_art_button_hide_overlay_controls), self);
gtk_widget_add_controller(self->main, motion_controller); gtk_widget_add_controller(self->main, motion_controller);
} }
static void koto_cover_art_button_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_cover_art_button_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
(void) val; (void) val;
switch (prop_id) { switch (prop_id) {
@ -115,8 +139,14 @@ static void koto_cover_art_button_get_property(GObject *obj, guint prop_id, GVal
} }
} }
static void koto_cover_art_button_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_cover_art_button_set_property(
KotoCoverArtButton *self = KOTO_COVER_ART_BUTTON(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoCoverArtButton * self = KOTO_COVER_ART_BUTTON(obj);
switch (prop_id) { switch (prop_id) {
case PROP_ART_PATH: case PROP_ART_PATH:
@ -134,13 +164,18 @@ static void koto_cover_art_button_set_property(GObject *obj, guint prop_id, cons
} }
} }
void koto_cover_art_button_hide_overlay_controls(GtkEventControllerFocus *controller, gpointer data) { void koto_cover_art_button_hide_overlay_controls(
GtkEventControllerFocus * controller,
gpointer data
) {
(void) controller; (void) controller;
KotoCoverArtButton* self = data; KotoCoverArtButton* self = data;
gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), FALSE); gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), FALSE);
} }
KotoButton* koto_cover_art_button_get_button(KotoCoverArtButton *self) { KotoButton * koto_cover_art_button_get_button(KotoCoverArtButton * self) {
if (!KOTO_IS_COVER_ART_BUTTON(self)) { if (!KOTO_IS_COVER_ART_BUTTON(self)) {
return NULL; return NULL;
} }
@ -148,7 +183,7 @@ KotoButton* koto_cover_art_button_get_button(KotoCoverArtButton *self) {
return self->play_pause_button; return self->play_pause_button;
} }
GtkWidget* koto_cover_art_button_get_main(KotoCoverArtButton *self) { GtkWidget * koto_cover_art_button_get_main(KotoCoverArtButton * self) {
if (!KOTO_IS_COVER_ART_BUTTON(self)) { if (!KOTO_IS_COVER_ART_BUTTON(self)) {
return NULL; return NULL;
} }
@ -156,13 +191,17 @@ GtkWidget* koto_cover_art_button_get_main(KotoCoverArtButton *self) {
return self->main; return self->main;
} }
void koto_cover_art_button_set_art_path(KotoCoverArtButton *self, gchar *art_path) { void koto_cover_art_button_set_art_path(
KotoCoverArtButton * self,
gchar * art_path
) {
if (!KOTO_IS_COVER_ART_BUTTON(self)) { if (!KOTO_IS_COVER_ART_BUTTON(self)) {
return; return;
} }
gboolean defined_artwork = koto_utils_is_string_valid(art_path); gboolean defined_artwork = koto_utils_is_string_valid(art_path);
if (GTK_IS_IMAGE(self->art)) { // Already have an image if (GTK_IS_IMAGE(self->art)) { // Already have an image
if (!defined_artwork) { // No art path or empty string if (!defined_artwork) { // No art path or empty string
gtk_image_set_from_icon_name(GTK_IMAGE(self->art), "audio-x-generic-symbolic"); gtk_image_set_from_icon_name(GTK_IMAGE(self->art), "audio-x-generic-symbolic");
@ -175,7 +214,11 @@ void koto_cover_art_button_set_art_path(KotoCoverArtButton *self, gchar *art_pat
} }
} }
void koto_cover_art_button_set_dimensions(KotoCoverArtButton *self, guint height, guint width) { void koto_cover_art_button_set_dimensions(
KotoCoverArtButton * self,
guint height,
guint width
) {
if (!KOTO_IS_COVER_ART_BUTTON(self)) { if (!KOTO_IS_COVER_ART_BUTTON(self)) {
return; return;
} }
@ -197,15 +240,24 @@ void koto_cover_art_button_set_dimensions(KotoCoverArtButton *self, guint height
} }
} }
void koto_cover_art_button_show_overlay_controls(GtkEventControllerFocus *controller, gpointer data) { void koto_cover_art_button_show_overlay_controls(
GtkEventControllerFocus * controller,
gpointer data
) {
(void) controller; (void) controller;
KotoCoverArtButton* self = data; KotoCoverArtButton* self = data;
gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), TRUE); gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), TRUE);
} }
KotoCoverArtButton* koto_cover_art_button_new(guint height, guint width, gchar *art_path) { KotoCoverArtButton * koto_cover_art_button_new(
return g_object_new(KOTO_TYPE_COVER_ART_BUTTON, guint height,
guint width,
gchar * art_path
) {
return g_object_new(
KOTO_TYPE_COVER_ART_BUTTON,
"desired-height", "desired-height",
height, height,
"desired-width", "desired-width",

View file

@ -27,14 +27,37 @@ G_DECLARE_FINAL_TYPE(KotoCoverArtButton, koto_cover_art_button, KOTO, COVER_ART_
/** /**
* Cover Art Functions * Cover Art Functions
**/ **/
KotoCoverArtButton* koto_cover_art_button_new(guint height, guint width, gchar *art_path); KotoCoverArtButton * koto_cover_art_button_new(
KotoButton* koto_cover_art_button_get_button(KotoCoverArtButton *self); guint height,
GtkWidget* koto_cover_art_button_get_main(KotoCoverArtButton *self); guint width,
void koto_cover_art_button_hide_overlay_controls(GtkEventControllerFocus *controller, gpointer data); gchar * art_path
void koto_cover_art_button_set_art_path(KotoCoverArtButton *self, gchar *art_path); );
void koto_cover_art_button_set_dimensions(KotoCoverArtButton *self, guint height, guint width);
void koto_cover_art_button_show_overlay_controls(GtkEventControllerFocus *controller, gpointer data); KotoButton * koto_cover_art_button_get_button(KotoCoverArtButton * self);
GtkWidget * koto_cover_art_button_get_main(KotoCoverArtButton * self);
void koto_cover_art_button_hide_overlay_controls(
GtkEventControllerFocus * controller,
gpointer data
);
void koto_cover_art_button_set_art_path(
KotoCoverArtButton * self,
gchar * art_path
);
void koto_cover_art_button_set_dimensions(
KotoCoverArtButton * self,
guint height,
guint width
);
void koto_cover_art_button_show_overlay_controls(
GtkEventControllerFocus * controller,
gpointer data
);
G_END_DECLS G_END_DECLS

View file

@ -30,36 +30,64 @@ enum {
N_SIGNALS N_SIGNALS
}; };
static guint cartographer_signals[N_SIGNALS] = { 0 }; static guint cartographer_signals[N_SIGNALS] = {
0
};
struct _KotoCartographer { struct _KotoCartographer {
GObject parent_instance; GObject parent_instance;
GHashTable *albums; GHashTable * albums;
GHashTable *artists; GHashTable * artists;
GHashTable *playlists; GHashTable * playlists;
GHashTable *tracks; GHashTable * tracks;
}; };
struct _KotoCartographerClass { struct _KotoCartographerClass {
GObjectClass parent_class; GObjectClass parent_class;
void (* album_added) (KotoCartographer *cartographer, KotoIndexedAlbum *album); void (* album_added) (
void (* album_removed) (KotoCartographer *cartographer, KotoIndexedAlbum *album); KotoCartographer * cartographer,
void (* artist_added) (KotoCartographer *cartographer, KotoIndexedArtist *artist); KotoIndexedAlbum * album
void (* artist_removed) (KotoCartographer *cartographer, KotoIndexedArtist *artist); );
void (* playlist_added) (KotoCartographer *cartographer, KotoPlaylist *playlist); void (* album_removed) (
void (* playlist_removed) (KotoCartographer *cartographer, KotoPlaylist *playlist); KotoCartographer * cartographer,
void (* track_added) (KotoCartographer *cartographer, KotoIndexedTrack *track); KotoIndexedAlbum * album
void (* track_removed) (KotoCartographer *cartographer, KotoIndexedTrack *track); );
void (* artist_added) (
KotoCartographer * cartographer,
KotoIndexedArtist * artist
);
void (* artist_removed) (
KotoCartographer * cartographer,
KotoIndexedArtist * artist
);
void (* playlist_added) (
KotoCartographer * cartographer,
KotoPlaylist * playlist
);
void (* playlist_removed) (
KotoCartographer * cartographer,
KotoPlaylist * playlist
);
void (* track_added) (
KotoCartographer * cartographer,
KotoIndexedTrack * track
);
void (* track_removed) (
KotoCartographer * cartographer,
KotoIndexedTrack * track
);
}; };
G_DEFINE_TYPE(KotoCartographer, koto_cartographer, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoCartographer, koto_cartographer, G_TYPE_OBJECT);
KotoCartographer *koto_maps = NULL; KotoCartographer * koto_maps = NULL;
static void koto_cartographer_class_init(KotoCartographerClass * c) {
GObjectClass * gobject_class;
static void koto_cartographer_class_init(KotoCartographerClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
cartographer_signals[SIGNAL_ALBUM_ADDED] = g_signal_new( cartographer_signals[SIGNAL_ALBUM_ADDED] = g_signal_new(
@ -167,15 +195,20 @@ static void koto_cartographer_class_init(KotoCartographerClass *c) {
); );
} }
static void koto_cartographer_init(KotoCartographer *self) { static void koto_cartographer_init(KotoCartographer * self) {
self->albums = g_hash_table_new(g_str_hash, g_str_equal); self->albums = g_hash_table_new(g_str_hash, g_str_equal);
self->artists = g_hash_table_new(g_str_hash, g_str_equal); self->artists = g_hash_table_new(g_str_hash, g_str_equal);
self->playlists = g_hash_table_new(g_str_hash, g_str_equal); self->playlists = g_hash_table_new(g_str_hash, g_str_equal);
self->tracks = g_hash_table_new(g_str_hash, g_str_equal); self->tracks = g_hash_table_new(g_str_hash, g_str_equal);
} }
void koto_cartographer_add_album(KotoCartographer *self, KotoIndexedAlbum *album) { void koto_cartographer_add_album(
gchar *album_uuid = NULL; KotoCartographer * self,
KotoIndexedAlbum * album
) {
gchar * album_uuid = NULL;
g_object_get(album, "uuid", &album_uuid, NULL); g_object_get(album, "uuid", &album_uuid, NULL);
if ((album_uuid == NULL) || koto_cartographer_has_album_by_uuid(self, album_uuid)) { // Have the album or invalid UUID if ((album_uuid == NULL) || koto_cartographer_has_album_by_uuid(self, album_uuid)) { // Have the album or invalid UUID
@ -192,8 +225,13 @@ void koto_cartographer_add_album(KotoCartographer *self, KotoIndexedAlbum *album
); );
} }
void koto_cartographer_add_artist(KotoCartographer *self, KotoIndexedArtist *artist) { void koto_cartographer_add_artist(
gchar *artist_uuid = NULL; KotoCartographer * self,
KotoIndexedArtist * artist
) {
gchar * artist_uuid = NULL;
g_object_get(artist, "uuid", &artist_uuid, NULL); g_object_get(artist, "uuid", &artist_uuid, NULL);
if ((artist_uuid == NULL) || koto_cartographer_has_artist_by_uuid(self, artist_uuid)) { // Have the artist or invalid UUID if ((artist_uuid == NULL) || koto_cartographer_has_artist_by_uuid(self, artist_uuid)) { // Have the artist or invalid UUID
@ -210,8 +248,13 @@ void koto_cartographer_add_artist(KotoCartographer *self, KotoIndexedArtist *art
); );
} }
void koto_cartographer_add_playlist(KotoCartographer *self, KotoPlaylist *playlist) { void koto_cartographer_add_playlist(
gchar *playlist_uuid = NULL; KotoCartographer * self,
KotoPlaylist * playlist
) {
gchar * playlist_uuid = NULL;
g_object_get(playlist, "uuid", &playlist_uuid, NULL); g_object_get(playlist, "uuid", &playlist_uuid, NULL);
if ((playlist_uuid == NULL) || koto_cartographer_has_playlist_by_uuid(self, playlist_uuid)) { // Have the playlist or invalid UUID if ((playlist_uuid == NULL) || koto_cartographer_has_playlist_by_uuid(self, playlist_uuid)) { // Have the playlist or invalid UUID
@ -227,7 +270,10 @@ void koto_cartographer_add_playlist(KotoCartographer *self, KotoPlaylist *playli
} }
} }
void koto_cartographer_emit_playlist_added(KotoPlaylist *playlist, KotoCartographer *self) { void koto_cartographer_emit_playlist_added(
KotoPlaylist * playlist,
KotoCartographer * self
) {
g_signal_emit( g_signal_emit(
self, self,
cartographer_signals[SIGNAL_PLAYLIST_ADDED], cartographer_signals[SIGNAL_PLAYLIST_ADDED],
@ -236,8 +282,13 @@ void koto_cartographer_emit_playlist_added(KotoPlaylist *playlist, KotoCartograp
); );
} }
void koto_cartographer_add_track(KotoCartographer *self, KotoIndexedTrack *track) { void koto_cartographer_add_track(
gchar *track_uuid = NULL; KotoCartographer * self,
KotoIndexedTrack * track
) {
gchar * track_uuid = NULL;
g_object_get(track, "uuid", &track_uuid, NULL); g_object_get(track, "uuid", &track_uuid, NULL);
if ((track_uuid == NULL) || koto_cartographer_has_track_by_uuid(self, track_uuid)) { // Have the track or invalid UUID if ((track_uuid == NULL) || koto_cartographer_has_track_by_uuid(self, track_uuid)) { // Have the track or invalid UUID
@ -254,33 +305,53 @@ void koto_cartographer_add_track(KotoCartographer *self, KotoIndexedTrack *track
); );
} }
KotoIndexedAlbum* koto_cartographer_get_album_by_uuid(KotoCartographer *self, gchar* album_uuid) { KotoIndexedAlbum * koto_cartographer_get_album_by_uuid(
KotoCartographer * self,
gchar* album_uuid
) {
return g_hash_table_lookup(self->albums, album_uuid); return g_hash_table_lookup(self->albums, album_uuid);
} }
KotoIndexedArtist* koto_cartographer_get_artist_by_uuid(KotoCartographer *self, gchar* artist_uuid) { KotoIndexedArtist * koto_cartographer_get_artist_by_uuid(
KotoCartographer * self,
gchar* artist_uuid
) {
return g_hash_table_lookup(self->artists, artist_uuid); return g_hash_table_lookup(self->artists, artist_uuid);
} }
GHashTable* koto_cartographer_get_playlists(KotoCartographer *self) { GHashTable * koto_cartographer_get_playlists(KotoCartographer * self) {
return self->playlists; return self->playlists;
} }
KotoPlaylist* koto_cartographer_get_playlist_by_uuid(KotoCartographer *self, gchar* playlist_uuid) { KotoPlaylist * koto_cartographer_get_playlist_by_uuid(
KotoCartographer * self,
gchar* playlist_uuid
) {
return g_hash_table_lookup(self->playlists, playlist_uuid); return g_hash_table_lookup(self->playlists, playlist_uuid);
} }
KotoIndexedTrack* koto_cartographer_get_track_by_uuid(KotoCartographer *self, gchar* track_uuid) { KotoIndexedTrack * koto_cartographer_get_track_by_uuid(
KotoCartographer * self,
gchar* track_uuid
) {
return g_hash_table_lookup(self->tracks, track_uuid); return g_hash_table_lookup(self->tracks, track_uuid);
} }
gboolean koto_cartographer_has_album(KotoCartographer *self, KotoIndexedAlbum *album) { gboolean koto_cartographer_has_album(
gchar *album_uuid = NULL; KotoCartographer * self,
KotoIndexedAlbum * album
) {
gchar * album_uuid = NULL;
g_object_get(album, "uuid", &album_uuid, NULL); g_object_get(album, "uuid", &album_uuid, NULL);
return koto_cartographer_has_album_by_uuid(self, album_uuid); return koto_cartographer_has_album_by_uuid(self, album_uuid);
} }
gboolean koto_cartographer_has_album_by_uuid(KotoCartographer *self, gchar* album_uuid) { gboolean koto_cartographer_has_album_by_uuid(
KotoCartographer * self,
gchar* album_uuid
) {
if (album_uuid == NULL) { if (album_uuid == NULL) {
return FALSE; return FALSE;
} }
@ -288,13 +359,21 @@ gboolean koto_cartographer_has_album_by_uuid(KotoCartographer *self, gchar* albu
return g_hash_table_contains(self->albums, album_uuid); return g_hash_table_contains(self->albums, album_uuid);
} }
gboolean koto_cartographer_has_artist(KotoCartographer *self, KotoIndexedArtist *artist) { gboolean koto_cartographer_has_artist(
gchar *artist_uuid = NULL; KotoCartographer * self,
KotoIndexedArtist * artist
) {
gchar * artist_uuid = NULL;
g_object_get(artist, "uuid", &artist_uuid, NULL); g_object_get(artist, "uuid", &artist_uuid, NULL);
return koto_cartographer_has_artist_by_uuid(self, artist_uuid); return koto_cartographer_has_artist_by_uuid(self, artist_uuid);
} }
gboolean koto_cartographer_has_artist_by_uuid(KotoCartographer *self, gchar* artist_uuid) { gboolean koto_cartographer_has_artist_by_uuid(
KotoCartographer * self,
gchar* artist_uuid
) {
if (artist_uuid == NULL) { if (artist_uuid == NULL) {
return FALSE; return FALSE;
} }
@ -302,13 +381,21 @@ gboolean koto_cartographer_has_artist_by_uuid(KotoCartographer *self, gchar* art
return g_hash_table_contains(self->artists, artist_uuid); return g_hash_table_contains(self->artists, artist_uuid);
} }
gboolean koto_cartographer_has_playlist(KotoCartographer *self, KotoPlaylist *playlist) { gboolean koto_cartographer_has_playlist(
gchar *playlist_uuid = NULL; KotoCartographer * self,
KotoPlaylist * playlist
) {
gchar * playlist_uuid = NULL;
g_object_get(playlist, "uuid", &playlist_uuid, NULL); g_object_get(playlist, "uuid", &playlist_uuid, NULL);
return koto_cartographer_has_playlist_by_uuid(self, playlist_uuid); return koto_cartographer_has_playlist_by_uuid(self, playlist_uuid);
} }
gboolean koto_cartographer_has_playlist_by_uuid(KotoCartographer *self, gchar* playlist_uuid) { gboolean koto_cartographer_has_playlist_by_uuid(
KotoCartographer * self,
gchar* playlist_uuid
) {
if (playlist_uuid == NULL) { if (playlist_uuid == NULL) {
return FALSE; return FALSE;
} }
@ -316,13 +403,21 @@ gboolean koto_cartographer_has_playlist_by_uuid(KotoCartographer *self, gchar* p
return g_hash_table_contains(self->playlists, playlist_uuid); return g_hash_table_contains(self->playlists, playlist_uuid);
} }
gboolean koto_cartographer_has_track(KotoCartographer *self, KotoIndexedTrack *track) { gboolean koto_cartographer_has_track(
gchar *track_uuid = NULL; KotoCartographer * self,
KotoIndexedTrack * track
) {
gchar * track_uuid = NULL;
g_object_get(track, "uuid", &track_uuid, NULL); g_object_get(track, "uuid", &track_uuid, NULL);
return koto_cartographer_has_album_by_uuid(self, track_uuid); return koto_cartographer_has_album_by_uuid(self, track_uuid);
} }
gboolean koto_cartographer_has_track_by_uuid(KotoCartographer *self, gchar* track_uuid) { gboolean koto_cartographer_has_track_by_uuid(
KotoCartographer * self,
gchar* track_uuid
) {
if (track_uuid == NULL) { if (track_uuid == NULL) {
return FALSE; return FALSE;
} }
@ -330,13 +425,21 @@ gboolean koto_cartographer_has_track_by_uuid(KotoCartographer *self, gchar* trac
return g_hash_table_contains(self->tracks, track_uuid); return g_hash_table_contains(self->tracks, track_uuid);
} }
void koto_cartographer_remove_album(KotoCartographer *self, KotoIndexedAlbum *album) { void koto_cartographer_remove_album(
gchar *album_uuid = NULL; KotoCartographer * self,
KotoIndexedAlbum * album
) {
gchar * album_uuid = NULL;
g_object_get(album, "uuid", &album_uuid, NULL); g_object_get(album, "uuid", &album_uuid, NULL);
koto_cartographer_remove_album_by_uuid(self, album_uuid); koto_cartographer_remove_album_by_uuid(self, album_uuid);
} }
void koto_cartographer_remove_album_by_uuid(KotoCartographer *self, gchar* album_uuid) { void koto_cartographer_remove_album_by_uuid(
KotoCartographer * self,
gchar* album_uuid
) {
if (album_uuid != NULL) { if (album_uuid != NULL) {
g_hash_table_remove(self->albums, album_uuid); g_hash_table_remove(self->albums, album_uuid);
@ -349,13 +452,21 @@ void koto_cartographer_remove_album_by_uuid(KotoCartographer *self, gchar* album
} }
} }
void koto_cartographer_remove_artist(KotoCartographer *self, KotoIndexedArtist *artist) { void koto_cartographer_remove_artist(
gchar *artist_uuid = NULL; KotoCartographer * self,
KotoIndexedArtist * artist
) {
gchar * artist_uuid = NULL;
g_object_get(artist, "uuid", &artist_uuid, NULL); g_object_get(artist, "uuid", &artist_uuid, NULL);
koto_cartographer_remove_artist_by_uuid(self, artist_uuid); koto_cartographer_remove_artist_by_uuid(self, artist_uuid);
} }
void koto_cartographer_remove_artist_by_uuid(KotoCartographer *self, gchar* artist_uuid) { void koto_cartographer_remove_artist_by_uuid(
KotoCartographer * self,
gchar* artist_uuid
) {
if (artist_uuid == NULL) { if (artist_uuid == NULL) {
g_hash_table_remove(self->artists, artist_uuid); g_hash_table_remove(self->artists, artist_uuid);
@ -368,13 +479,21 @@ void koto_cartographer_remove_artist_by_uuid(KotoCartographer *self, gchar* arti
} }
} }
void koto_cartographer_remove_playlist(KotoCartographer *self, KotoPlaylist *playlist) { void koto_cartographer_remove_playlist(
gchar *playlist_uuid = NULL; KotoCartographer * self,
KotoPlaylist * playlist
) {
gchar * playlist_uuid = NULL;
g_object_get(playlist, "uuid", &playlist_uuid, NULL); g_object_get(playlist, "uuid", &playlist_uuid, NULL);
koto_cartographer_remove_playlist_by_uuid(self, playlist_uuid); koto_cartographer_remove_playlist_by_uuid(self, playlist_uuid);
} }
void koto_cartographer_remove_playlist_by_uuid(KotoCartographer *self, gchar* playlist_uuid) { void koto_cartographer_remove_playlist_by_uuid(
KotoCartographer * self,
gchar* playlist_uuid
) {
if (playlist_uuid != NULL) { if (playlist_uuid != NULL) {
g_hash_table_remove(self->playlists, playlist_uuid); g_hash_table_remove(self->playlists, playlist_uuid);
@ -387,13 +506,21 @@ void koto_cartographer_remove_playlist_by_uuid(KotoCartographer *self, gchar* pl
} }
} }
void koto_cartographer_remove_track(KotoCartographer *self, KotoIndexedTrack *track) { void koto_cartographer_remove_track(
gchar *track_uuid = NULL; KotoCartographer * self,
KotoIndexedTrack * track
) {
gchar * track_uuid = NULL;
g_object_get(track, "uuid", &track_uuid, NULL); g_object_get(track, "uuid", &track_uuid, NULL);
koto_cartographer_remove_track_by_uuid(self, track_uuid); koto_cartographer_remove_track_by_uuid(self, track_uuid);
} }
void koto_cartographer_remove_track_by_uuid(KotoCartographer *self, gchar* track_uuid) { void koto_cartographer_remove_track_by_uuid(
KotoCartographer * self,
gchar* track_uuid
) {
if (track_uuid != NULL) { if (track_uuid != NULL) {
g_hash_table_remove(self->tracks, track_uuid); g_hash_table_remove(self->tracks, track_uuid);
@ -406,6 +533,6 @@ void koto_cartographer_remove_track_by_uuid(KotoCartographer *self, gchar* track
} }
} }
KotoCartographer* koto_cartographer_new() { KotoCartographer * koto_cartographer_new() {
return g_object_new(KOTO_TYPE_CARTOGRAPHER, NULL); return g_object_new(KOTO_TYPE_CARTOGRAPHER, NULL);
} }

View file

@ -24,7 +24,7 @@ G_BEGIN_DECLS
/** /**
* Type Definition * Type Definition
**/ **/
#define KOTO_TYPE_CARTOGRAPHER koto_cartographer_get_type() #define KOTO_TYPE_CARTOGRAPHER koto_cartographer_get_type()
@ -36,39 +36,135 @@ GType koto_cartographer_get_type(void) G_GNUC_CONST;
/** /**
* Cartographer Functions * Cartographer Functions
**/ **/
KotoCartographer* koto_cartographer_new(); KotoCartographer * koto_cartographer_new();
void koto_cartographer_add_album(KotoCartographer *self, KotoIndexedAlbum *album); void koto_cartographer_add_album(
void koto_cartographer_add_artist(KotoCartographer *self, KotoIndexedArtist *artist); KotoCartographer * self,
void koto_cartographer_add_playlist(KotoCartographer *self, KotoPlaylist *playlist); KotoIndexedAlbum * album
void koto_cartographer_add_track(KotoCartographer *self, KotoIndexedTrack *track); );
void koto_cartographer_emit_playlist_added(KotoPlaylist *playlist, KotoCartographer *self); void koto_cartographer_add_artist(
KotoCartographer * self,
KotoIndexedArtist * artist
);
KotoIndexedAlbum* koto_cartographer_get_album_by_uuid(KotoCartographer *self, gchar* album_uuid); void koto_cartographer_add_playlist(
KotoIndexedArtist* koto_cartographer_get_artist_by_uuid(KotoCartographer *self, gchar* artist_uuid); KotoCartographer * self,
KotoPlaylist* koto_cartographer_get_playlist_by_uuid(KotoCartographer *self, gchar* playlist_uuid); KotoPlaylist * playlist
GHashTable* koto_cartographer_get_playlists(KotoCartographer *self); );
KotoIndexedTrack* koto_cartographer_get_track_by_uuid(KotoCartographer *self, gchar* track_uuid);
gboolean koto_cartographer_has_album(KotoCartographer *self, KotoIndexedAlbum *album); void koto_cartographer_add_track(
gboolean koto_cartographer_has_album_by_uuid(KotoCartographer *self, gchar* album_uuid); KotoCartographer * self,
gboolean koto_cartographer_has_artist(KotoCartographer *self, KotoIndexedArtist *artist); KotoIndexedTrack * track
gboolean koto_cartographer_has_artist_by_uuid(KotoCartographer *self, gchar* artist_uuid); );
gboolean koto_cartographer_has_playlist(KotoCartographer *self, KotoPlaylist *playlist);
gboolean koto_cartographer_has_playlist_by_uuid(KotoCartographer *self, gchar* playlist_uuid);
gboolean koto_cartographer_has_track(KotoCartographer *self, KotoIndexedTrack *track);
gboolean koto_cartographer_has_track_by_uuid(KotoCartographer *self, gchar* track_uuid);
void koto_cartographer_remove_album(KotoCartographer *self, KotoIndexedAlbum *album); void koto_cartographer_emit_playlist_added(
void koto_cartographer_remove_album_by_uuid(KotoCartographer *self, gchar* album_uuid); KotoPlaylist * playlist,
void koto_cartographer_remove_artist(KotoCartographer *self, KotoIndexedArtist *artist); KotoCartographer * self
void koto_cartographer_remove_artist_by_uuid(KotoCartographer *self, gchar* artist_uuid); );
void koto_cartographer_remove_playlist(KotoCartographer *self, KotoPlaylist *playlist);
void koto_cartographer_remove_playlist_by_uuid(KotoCartographer *self, gchar* playlist_uuid); KotoIndexedAlbum * koto_cartographer_get_album_by_uuid(
void koto_cartographer_remove_track(KotoCartographer *self, KotoIndexedTrack *track); KotoCartographer * self,
void koto_cartographer_remove_track_by_uuid(KotoCartographer *self, gchar* track_uuid); gchar* album_uuid
);
KotoIndexedArtist * koto_cartographer_get_artist_by_uuid(
KotoCartographer * self,
gchar* artist_uuid
);
KotoPlaylist * koto_cartographer_get_playlist_by_uuid(
KotoCartographer * self,
gchar* playlist_uuid
);
GHashTable * koto_cartographer_get_playlists(KotoCartographer * self);
KotoIndexedTrack * koto_cartographer_get_track_by_uuid(
KotoCartographer * self,
gchar* track_uuid
);
gboolean koto_cartographer_has_album(
KotoCartographer * self,
KotoIndexedAlbum * album
);
gboolean koto_cartographer_has_album_by_uuid(
KotoCartographer * self,
gchar* album_uuid
);
gboolean koto_cartographer_has_artist(
KotoCartographer * self,
KotoIndexedArtist * artist
);
gboolean koto_cartographer_has_artist_by_uuid(
KotoCartographer * self,
gchar* artist_uuid
);
gboolean koto_cartographer_has_playlist(
KotoCartographer * self,
KotoPlaylist * playlist
);
gboolean koto_cartographer_has_playlist_by_uuid(
KotoCartographer * self,
gchar* playlist_uuid
);
gboolean koto_cartographer_has_track(
KotoCartographer * self,
KotoIndexedTrack * track
);
gboolean koto_cartographer_has_track_by_uuid(
KotoCartographer * self,
gchar* track_uuid
);
void koto_cartographer_remove_album(
KotoCartographer * self,
KotoIndexedAlbum * album
);
void koto_cartographer_remove_album_by_uuid(
KotoCartographer * self,
gchar* album_uuid
);
void koto_cartographer_remove_artist(
KotoCartographer * self,
KotoIndexedArtist * artist
);
void koto_cartographer_remove_artist_by_uuid(
KotoCartographer * self,
gchar* artist_uuid
);
void koto_cartographer_remove_playlist(
KotoCartographer * self,
KotoPlaylist * playlist
);
void koto_cartographer_remove_playlist_by_uuid(
KotoCartographer * self,
gchar* playlist_uuid
);
void koto_cartographer_remove_track(
KotoCartographer * self,
KotoIndexedTrack * track
);
void koto_cartographer_remove_track_by_uuid(
KotoCartographer * self,
gchar* track_uuid
);
G_END_DECLS G_END_DECLS

View file

@ -26,8 +26,8 @@ int KOTO_DB_SUCCESS = 0;
int KOTO_DB_NEW = 1; int KOTO_DB_NEW = 1;
int KOTO_DB_FAIL = 2; int KOTO_DB_FAIL = 2;
sqlite3 *koto_db = NULL; sqlite3 * koto_db = NULL;
gchar *db_filepath = NULL; gchar * db_filepath = NULL;
gboolean created_new_db = FALSE; gboolean created_new_db = FALSE;
void close_db() { void close_db() {
@ -35,14 +35,15 @@ void close_db() {
} }
int create_db_tables() { int create_db_tables() {
char *tables_creation_queries = "CREATE TABLE IF NOT EXISTS artists(id string UNIQUE PRIMARY KEY, path string, type int, name string, art_path string);" char * tables_creation_queries = "CREATE TABLE IF NOT EXISTS artists(id string UNIQUE PRIMARY KEY, path string, type int, name string, art_path string);"
"CREATE TABLE IF NOT EXISTS albums(id string UNIQUE PRIMARY KEY, path string, artist_id string, name string, art_path string, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);" "CREATE TABLE IF NOT EXISTS albums(id string UNIQUE PRIMARY KEY, path string, artist_id string, name string, art_path string, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
"CREATE TABLE IF NOT EXISTS tracks(id string UNIQUE PRIMARY KEY, path string, type int, artist_id string, album_id string, file_name string, name string, disc int, position int, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);" "CREATE TABLE IF NOT EXISTS tracks(id string UNIQUE PRIMARY KEY, path string, type int, artist_id string, album_id string, file_name string, name string, disc int, position int, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
"CREATE TABLE IF NOT EXISTS playlist_meta(id string UNIQUE PRIMARY KEY, name string, art_path string, preferred_model int);" "CREATE TABLE IF NOT EXISTS playlist_meta(id string UNIQUE PRIMARY KEY, name string, art_path string, preferred_model int);"
"CREATE TABLE IF NOT EXISTS playlist_tracks(position INTEGER PRIMARY KEY AUTOINCREMENT, playlist_id string, track_id string, current int, FOREIGN KEY(playlist_id) REFERENCES playlist_meta(id), FOREIGN KEY(track_id) REFERENCES tracks(id) ON DELETE CASCADE);"; "CREATE TABLE IF NOT EXISTS playlist_tracks(position INTEGER PRIMARY KEY AUTOINCREMENT, playlist_id string, track_id string, current int, FOREIGN KEY(playlist_id) REFERENCES playlist_meta(id), FOREIGN KEY(track_id) REFERENCES tracks(id) ON DELETE CASCADE);";
gchar *create_tables_errmsg = NULL; gchar * create_tables_errmsg = NULL;
int rc = sqlite3_exec(koto_db, tables_creation_queries, 0,0, &create_tables_errmsg); int rc = sqlite3_exec(koto_db, tables_creation_queries, 0, 0, &create_tables_errmsg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_critical("Failed to create required tables: %s", create_tables_errmsg); g_critical("Failed to create required tables: %s", create_tables_errmsg);
@ -52,9 +53,10 @@ int create_db_tables() {
} }
int enable_foreign_keys() { int enable_foreign_keys() {
gchar *enable_foreign_keys_err = NULL; gchar * enable_foreign_keys_err = NULL;
int rc = sqlite3_exec(koto_db, "PRAGMA foreign_keys = ON;", 0, 0, &enable_foreign_keys_err); int rc = sqlite3_exec(koto_db, "PRAGMA foreign_keys = ON;", 0, 0, &enable_foreign_keys_err);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_critical("Failed to enable foreign key support. Ensure your sqlite3 is compiled with neither SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined: %s", enable_foreign_keys_err); g_critical("Failed to enable foreign key support. Ensure your sqlite3 is compiled with neither SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined: %s", enable_foreign_keys_err);
} }
@ -63,10 +65,10 @@ int enable_foreign_keys() {
return (rc == SQLITE_OK) ? KOTO_DB_SUCCESS : KOTO_DB_FAIL; return (rc == SQLITE_OK) ? KOTO_DB_SUCCESS : KOTO_DB_FAIL;
} }
gchar* get_db_path() { gchar * get_db_path() {
if (db_filepath == NULL) { if (db_filepath == NULL) {
const gchar *data_home = g_get_user_data_dir(); const gchar * data_home = g_get_user_data_dir();
gchar *data_dir = g_build_path(G_DIR_SEPARATOR_S, data_home, "com.github.joshstrobl.koto", NULL); gchar * data_dir = g_build_path(G_DIR_SEPARATOR_S, data_home, "com.github.joshstrobl.koto", NULL);
db_filepath = g_build_filename(g_strdup(data_dir), "db", NULL); // Build out our path using XDG_DATA_HOME (e.g. .local/share/) + our namespace + db as the file name db_filepath = g_build_filename(g_strdup(data_dir), "db", NULL); // Build out our path using XDG_DATA_HOME (e.g. .local/share/) + our namespace + db as the file name
g_free(data_dir); g_free(data_dir);
} }
@ -78,15 +80,17 @@ int have_existing_db() {
struct stat db_stat; struct stat db_stat;
int success = stat(get_db_path(), &db_stat); int success = stat(get_db_path(), &db_stat);
return ((success == 0) && S_ISREG(db_stat.st_mode)) ? 0 : 1; return ((success == 0) && S_ISREG(db_stat.st_mode)) ? 0 : 1;
} }
int open_db() { int open_db() {
int ret = KOTO_DB_SUCCESS; // Default to last return being SUCCESS int ret = KOTO_DB_SUCCESS; // Default to last return being SUCCESS
if (have_existing_db() == 1) { // If we do not have an existing DB if (have_existing_db() == 1) { // If we do not have an existing DB
const gchar *data_home = g_get_user_data_dir(); const gchar * data_home = g_get_user_data_dir();
const gchar *data_dir = g_path_get_dirname(db_filepath); const gchar * data_dir = g_path_get_dirname(db_filepath);
mkdir(data_home, 0755); mkdir(data_home, 0755);
mkdir(data_dir, 0755); mkdir(data_dir, 0755);
chown(data_dir, getuid(), getgid()); chown(data_dir, getuid(), getgid());
@ -102,7 +106,7 @@ int open_db() {
return KOTO_DB_FAIL; return KOTO_DB_FAIL;
} }
if (create_db_tables() != KOTO_DB_SUCCESS) {// Failed to create our database tables if (create_db_tables() != KOTO_DB_SUCCESS) { // Failed to create our database tables
return KOTO_DB_FAIL; return KOTO_DB_FAIL;
} }

View file

@ -24,8 +24,13 @@ extern int KOTO_DB_FAIL;
extern gboolean created_new_db; extern gboolean created_new_db;
void close_db(); void close_db();
int create_db_tables(); int create_db_tables();
gchar* get_db_path();
gchar * get_db_path();
int enable_foreign_keys(); int enable_foreign_keys();
int have_existing_db(); int have_existing_db();
int open_db(); int open_db();

View file

@ -25,19 +25,19 @@
#include "structs.h" #include "structs.h"
#include "koto-utils.h" #include "koto-utils.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoCurrentPlaylist *current_playlist; extern KotoCurrentPlaylist * current_playlist;
extern sqlite3 *koto_db; extern sqlite3 * koto_db;
struct _KotoIndexedAlbum { struct _KotoIndexedAlbum {
GObject parent_instance; GObject parent_instance;
gchar *uuid; gchar * uuid;
gchar *path; gchar * path;
gchar *name; gchar * name;
gchar *art_path; gchar * art_path;
gchar *artist_uuid; gchar * artist_uuid;
GList *tracks; GList * tracks;
gboolean has_album_art; gboolean has_album_art;
gboolean do_initial_index; gboolean do_initial_index;
@ -56,13 +56,28 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
NULL,
};
static void koto_indexed_album_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_indexed_album_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_indexed_album_class_init(KotoIndexedAlbumClass * c) {
GObjectClass * gobject_class;
static void koto_indexed_album_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_indexed_album_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_indexed_album_set_property; gobject_class->set_property = koto_indexed_album_set_property;
gobject_class->get_property = koto_indexed_album_get_property; gobject_class->get_property = koto_indexed_album_get_property;
@ -72,7 +87,7 @@ static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
"UUID to Album in database", "UUID to Album in database",
"UUID to Album in database", "UUID to Album in database",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_DO_INITIAL_INDEX] = g_param_spec_boolean( props[PROP_DO_INITIAL_INDEX] = g_param_spec_boolean(
@ -80,7 +95,7 @@ static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
"Do an initial indexing operating instead of pulling from the database", "Do an initial indexing operating instead of pulling from the database",
"Do an initial indexing operating instead of pulling from the database", "Do an initial indexing operating instead of pulling from the database",
FALSE, FALSE,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_PATH] = g_param_spec_string( props[PROP_PATH] = g_param_spec_string(
@ -88,7 +103,7 @@ static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
"Path", "Path",
"Path to Album", "Path to Album",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_ALBUM_NAME] = g_param_spec_string( props[PROP_ALBUM_NAME] = g_param_spec_string(
@ -96,7 +111,7 @@ static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
"Name", "Name",
"Name of Album", "Name of Album",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_ART_PATH] = g_param_spec_string( props[PROP_ART_PATH] = g_param_spec_string(
@ -104,7 +119,7 @@ static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
"Path to Artwork", "Path to Artwork",
"Path to Artwork", "Path to Artwork",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_ARTIST_UUID] = g_param_spec_string( props[PROP_ARTIST_UUID] = g_param_spec_string(
@ -112,23 +127,28 @@ static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
"UUID of Artist associated with Album", "UUID of Artist associated with Album",
"UUID of Artist associated with Album", "UUID of Artist associated with Album",
NULL, NULL,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_indexed_album_init(KotoIndexedAlbum *self) { static void koto_indexed_album_init(KotoIndexedAlbum * self) {
self->has_album_art = FALSE; self->has_album_art = FALSE;
self->tracks = NULL; self->tracks = NULL;
} }
void koto_indexed_album_add_track(KotoIndexedAlbum *self, KotoIndexedTrack *track) { void koto_indexed_album_add_track(
KotoIndexedAlbum * self,
KotoIndexedTrack * track
) {
if (track == NULL) { // Not a file if (track == NULL) { // Not a file
return; return;
} }
gchar *track_uuid; gchar * track_uuid;
g_object_get(track, "uuid", &track_uuid, NULL); g_object_get(track, "uuid", &track_uuid, NULL);
if (g_list_index(self->tracks, track_uuid) == -1) { if (g_list_index(self->tracks, track_uuid) == -1) {
@ -136,12 +156,12 @@ void koto_indexed_album_add_track(KotoIndexedAlbum *self, KotoIndexedTrack *trac
} }
} }
void koto_indexed_album_commit(KotoIndexedAlbum *self) { void koto_indexed_album_commit(KotoIndexedAlbum * self) {
if (self->art_path == NULL) { // If art_path isn't defined when committing if (self->art_path == NULL) { // If art_path isn't defined when committing
koto_indexed_album_set_album_art(self, ""); // Set to an empty string koto_indexed_album_set_album_art(self, ""); // Set to an empty string
} }
gchar *commit_op = g_strdup_printf( gchar * commit_op = g_strdup_printf(
"INSERT INTO albums(id, path, artist_id, name, art_path)" "INSERT INTO albums(id, path, artist_id, name, art_path)"
"VALUES('%s', quote(\"%s\"), '%s', quote(\"%s\"), quote(\"%s\"))" "VALUES('%s', quote(\"%s\"), '%s', quote(\"%s\"), quote(\"%s\"))"
"ON CONFLICT(id) DO UPDATE SET path=excluded.path, name=excluded.name, art_path=excluded.art_path;", "ON CONFLICT(id) DO UPDATE SET path=excluded.path, name=excluded.name, art_path=excluded.art_path;",
@ -152,9 +172,10 @@ void koto_indexed_album_commit(KotoIndexedAlbum *self) {
self->art_path self->art_path
); );
gchar *commit_op_errmsg = NULL; gchar * commit_op_errmsg = NULL;
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg); int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_warning("Failed to write our album to the database: %s", commit_op_errmsg); g_warning("Failed to write our album to the database: %s", commit_op_errmsg);
} }
@ -163,9 +184,10 @@ void koto_indexed_album_commit(KotoIndexedAlbum *self) {
g_free(commit_op_errmsg); g_free(commit_op_errmsg);
} }
void koto_indexed_album_find_album_art(KotoIndexedAlbum *self) { void koto_indexed_album_find_album_art(KotoIndexedAlbum * self) {
magic_t magic_cookie = magic_open(MAGIC_MIME); magic_t magic_cookie = magic_open(MAGIC_MIME);
if (magic_cookie == NULL) { if (magic_cookie == NULL) {
return; return;
} }
@ -175,13 +197,15 @@ void koto_indexed_album_find_album_art(KotoIndexedAlbum *self) {
return; return;
} }
DIR *dir = opendir(self->path); // Attempt to open our directory DIR * dir = opendir(self->path); // Attempt to open our directory
if (dir == NULL) { if (dir == NULL) {
return; return;
} }
struct dirent *entry; struct dirent * entry;
while ((entry = readdir(dir))) { while ((entry = readdir(dir))) {
if (entry->d_type != DT_REG) { // Not a regular file if (entry->d_type != DT_REG) { // Not a regular file
@ -192,9 +216,9 @@ void koto_indexed_album_find_album_art(KotoIndexedAlbum *self) {
continue; // Skip continue; // Skip
} }
gchar *full_path = g_strdup_printf("%s%s%s", self->path, G_DIR_SEPARATOR_S, entry->d_name); gchar * full_path = g_strdup_printf("%s%s%s", self->path, G_DIR_SEPARATOR_S, entry->d_name);
const char *mime_type = magic_file(magic_cookie, full_path); const char * mime_type = magic_file(magic_cookie, full_path);
if (mime_type == NULL) { // Failed to get the mimetype if (mime_type == NULL) { // Failed to get the mimetype
g_free(full_path); g_free(full_path);
@ -202,8 +226,8 @@ void koto_indexed_album_find_album_art(KotoIndexedAlbum *self) {
} }
if (g_str_has_prefix(mime_type, "image/") && !self->has_album_art) { // Is an image file and doesn't have album art yet if (g_str_has_prefix(mime_type, "image/") && !self->has_album_art) { // Is an image file and doesn't have album art yet
gchar *album_art_no_ext = g_strdup(koto_utils_get_filename_without_extension(entry->d_name)); // Get the name of the file without the extension gchar * album_art_no_ext = g_strdup(koto_utils_get_filename_without_extension(entry->d_name)); // Get the name of the file without the extension
gchar *lower_art = g_strdup(g_utf8_strdown(album_art_no_ext, -1)); // Lowercase gchar * lower_art = g_strdup(g_utf8_strdown(album_art_no_ext, -1)); // Lowercase
if ( if (
(g_strrstr(lower_art, "Small") == NULL) && // Not Small (g_strrstr(lower_art, "Small") == NULL) && // Not Small
@ -226,7 +250,11 @@ void koto_indexed_album_find_album_art(KotoIndexedAlbum *self) {
magic_close(magic_cookie); magic_close(magic_cookie);
} }
void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie, const gchar *path) { void koto_indexed_album_find_tracks(
KotoIndexedAlbum * self,
magic_t magic_cookie,
const gchar * path
) {
if (magic_cookie == NULL) { // No cookie provided if (magic_cookie == NULL) { // No cookie provided
magic_cookie = magic_open(MAGIC_MIME); magic_cookie = magic_open(MAGIC_MIME);
} }
@ -244,20 +272,22 @@ void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie
return; return;
} }
DIR *dir = opendir(path); // Attempt to open our directory DIR * dir = opendir(path); // Attempt to open our directory
if (dir == NULL) { if (dir == NULL) {
return; return;
} }
struct dirent *entry; struct dirent * entry;
while ((entry = readdir(dir))) { while ((entry = readdir(dir))) {
if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item
continue; // Skip continue; // Skip
} }
gchar *full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name); gchar * full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name);
if (entry->d_type == DT_DIR) { // If this is a directory if (entry->d_type == DT_DIR) { // If this is a directory
koto_indexed_album_find_tracks(self, magic_cookie, full_path); // Recursively find tracks koto_indexed_album_find_tracks(self, magic_cookie, full_path); // Recursively find tracks
@ -269,7 +299,7 @@ void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie
continue; // SKIP continue; // SKIP
} }
const char *mime_type = magic_file(magic_cookie, full_path); const char * mime_type = magic_file(magic_cookie, full_path);
if (mime_type == NULL) { // Failed to get the mimetype if (mime_type == NULL) { // Failed to get the mimetype
g_free(full_path); g_free(full_path);
@ -277,19 +307,19 @@ void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie
} }
if (g_str_has_prefix(mime_type, "audio/") || g_str_has_prefix(mime_type, "video/ogg")) { // Is an audio file or ogg because it is special if (g_str_has_prefix(mime_type, "audio/") || g_str_has_prefix(mime_type, "video/ogg")) { // Is an audio file or ogg because it is special
gchar *appended_slash_to_path = g_strdup_printf("%s%s", g_strdup(self->path), G_DIR_SEPARATOR_S); gchar * appended_slash_to_path = g_strdup_printf("%s%s", g_strdup(self->path), G_DIR_SEPARATOR_S);
gchar **possible_cd_split = g_strsplit(full_path, appended_slash_to_path, -1); // Split based on the album path gchar ** possible_cd_split = g_strsplit(full_path, appended_slash_to_path, -1); // Split based on the album path
guint *cd = (guint*) 1; guint * cd = (guint*) 1;
gchar *track_with_cd_sep = g_strdup(possible_cd_split[1]); // Duplicate gchar * track_with_cd_sep = g_strdup(possible_cd_split[1]); // Duplicate
gchar **split_on_cd = g_strsplit(track_with_cd_sep, G_DIR_SEPARATOR_S, -1); // Split based on separator (e.g. / ) gchar ** split_on_cd = g_strsplit(track_with_cd_sep, G_DIR_SEPARATOR_S, -1); // Split based on separator (e.g. / )
if (g_strv_length(split_on_cd) > 1) { if (g_strv_length(split_on_cd) > 1) {
gchar *cdd = g_strdup(split_on_cd[0]); gchar * cdd = g_strdup(split_on_cd[0]);
gchar **cd_sep = g_strsplit(g_utf8_strdown(cdd, -1), "cd", -1); gchar ** cd_sep = g_strsplit(g_utf8_strdown(cdd, -1), "cd", -1);
if (g_strv_length(cd_sep) > 1) { if (g_strv_length(cd_sep) > 1) {
gchar *pos_str = g_strdup(cd_sep[1]); gchar * pos_str = g_strdup(cd_sep[1]);
cd = (guint*) g_ascii_strtoull(pos_str, NULL, 10); // Attempt to convert cd = (guint*) g_ascii_strtoull(pos_str, NULL, 10); // Attempt to convert
g_free(pos_str); g_free(pos_str);
} }
@ -304,7 +334,7 @@ void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie
g_strfreev(possible_cd_split); g_strfreev(possible_cd_split);
g_free(appended_slash_to_path); g_free(appended_slash_to_path);
KotoIndexedTrack *track = koto_indexed_track_new(self, full_path, cd); KotoIndexedTrack * track = koto_indexed_track_new(self, full_path, cd);
if (track != NULL) { // Is a file if (track != NULL) { // Is a file
koto_indexed_album_add_track(self, track); // Add our file koto_indexed_album_add_track(self, track); // Add our file
@ -315,8 +345,14 @@ void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie
} }
} }
static void koto_indexed_album_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_indexed_album_get_property(
KotoIndexedAlbum *self = KOTO_INDEXED_ALBUM(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoIndexedAlbum * self = KOTO_INDEXED_ALBUM(obj);
switch (prop_id) { switch (prop_id) {
case PROP_UUID: case PROP_UUID:
@ -343,8 +379,14 @@ static void koto_indexed_album_get_property(GObject *obj, guint prop_id, GValue
} }
} }
static void koto_indexed_album_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec){ static void koto_indexed_album_set_property(
KotoIndexedAlbum *self = KOTO_INDEXED_ALBUM(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoIndexedAlbum * self = KOTO_INDEXED_ALBUM(obj);
switch (prop_id) { switch (prop_id) {
case PROP_UUID: case PROP_UUID:
@ -372,7 +414,7 @@ static void koto_indexed_album_set_property(GObject *obj, guint prop_id, const G
} }
} }
gchar* koto_indexed_album_get_album_art(KotoIndexedAlbum *self) { gchar * koto_indexed_album_get_album_art(KotoIndexedAlbum * self) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return g_strdup(""); return g_strdup("");
} }
@ -380,7 +422,7 @@ gchar* koto_indexed_album_get_album_art(KotoIndexedAlbum *self) {
return g_strdup((self->has_album_art && koto_utils_is_string_valid(self->art_path)) ? self->art_path : ""); return g_strdup((self->has_album_art && koto_utils_is_string_valid(self->art_path)) ? self->art_path : "");
} }
gchar *koto_indexed_album_get_album_name(KotoIndexedAlbum *self) { gchar * koto_indexed_album_get_album_name(KotoIndexedAlbum * self) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return NULL; return NULL;
} }
@ -392,7 +434,7 @@ gchar *koto_indexed_album_get_album_name(KotoIndexedAlbum *self) {
return g_strdup(self->name); // Return duplicate of the name return g_strdup(self->name); // Return duplicate of the name
} }
gchar* koto_indexed_album_get_album_uuid(KotoIndexedAlbum *self) { gchar * koto_indexed_album_get_album_uuid(KotoIndexedAlbum * self) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return NULL; return NULL;
} }
@ -404,7 +446,7 @@ gchar* koto_indexed_album_get_album_uuid(KotoIndexedAlbum *self) {
return g_strdup(self->uuid); // Return a duplicate of the UUID return g_strdup(self->uuid); // Return a duplicate of the UUID
} }
GList* koto_indexed_album_get_tracks(KotoIndexedAlbum *self) { GList * koto_indexed_album_get_tracks(KotoIndexedAlbum * self) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return NULL; return NULL;
} }
@ -412,7 +454,10 @@ GList* koto_indexed_album_get_tracks(KotoIndexedAlbum *self) {
return self->tracks; // Return return self->tracks; // Return
} }
void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album_art) { void koto_indexed_album_set_album_art(
KotoIndexedAlbum * self,
const gchar * album_art
) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return; return;
} }
@ -430,7 +475,10 @@ void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album
self->has_album_art = TRUE; self->has_album_art = TRUE;
} }
void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedTrack *track) { void koto_indexed_album_remove_file(
KotoIndexedAlbum * self,
KotoIndexedTrack * track
) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return; return;
} }
@ -439,12 +487,17 @@ void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedTrack *tr
return; return;
} }
gchar *track_uuid; gchar * track_uuid;
g_object_get(track, "parsed-name", &track_uuid, NULL); g_object_get(track, "parsed-name", &track_uuid, NULL);
self->tracks = g_list_remove(self->tracks, track_uuid); self->tracks = g_list_remove(self->tracks, track_uuid);
} }
void koto_indexed_album_set_album_name(KotoIndexedAlbum *self, const gchar *album_name) { void koto_indexed_album_set_album_name(
KotoIndexedAlbum * self,
const gchar * album_name
) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return; return;
} }
@ -460,7 +513,10 @@ void koto_indexed_album_set_album_name(KotoIndexedAlbum *self, const gchar *albu
self->name = g_strdup(album_name); self->name = g_strdup(album_name);
} }
void koto_indexed_album_set_artist_uuid(KotoIndexedAlbum *self, const gchar *artist_uuid) { void koto_indexed_album_set_artist_uuid(
KotoIndexedAlbum * self,
const gchar * artist_uuid
) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return; return;
} }
@ -476,7 +532,7 @@ void koto_indexed_album_set_artist_uuid(KotoIndexedAlbum *self, const gchar *art
self->artist_uuid = g_strdup(artist_uuid); self->artist_uuid = g_strdup(artist_uuid);
} }
void koto_indexed_album_set_as_current_playlist(KotoIndexedAlbum *self) { void koto_indexed_album_set_as_current_playlist(KotoIndexedAlbum * self) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return; return;
} }
@ -485,17 +541,23 @@ void koto_indexed_album_set_as_current_playlist(KotoIndexedAlbum *self) {
return; return;
} }
KotoPlaylist *new_album_playlist = koto_playlist_new(); // Create a new playlist KotoPlaylist * new_album_playlist = koto_playlist_new(); // Create a new playlist
g_object_set(new_album_playlist, "ephemeral", TRUE, NULL); // Set as ephemeral / temporary g_object_set(new_album_playlist, "ephemeral", TRUE, NULL); // Set as ephemeral / temporary
// The following section effectively reverses our tracks, so the first is now last. // The following section effectively reverses our tracks, so the first is now last.
// It then adds them in reverse order, since our playlist add function will prepend to our queue // It then adds them in reverse order, since our playlist add function will prepend to our queue
// This enables the preservation and defaulting of "newest" first everywhere else, while in albums preserving the rightful order of the album // This enables the preservation and defaulting of "newest" first everywhere else, while in albums preserving the rightful order of the album
// e.g. first track (0) being added last is actually first in the playlist's tracks // e.g. first track (0) being added last is actually first in the playlist's tracks
GList *reversed_tracks = g_list_copy(self->tracks); // Copy our tracks so we can reverse the order GList * reversed_tracks = g_list_copy(self->tracks); // Copy our tracks so we can reverse the order
reversed_tracks = g_list_reverse(reversed_tracks); // Actually reverse it reversed_tracks = g_list_reverse(reversed_tracks); // Actually reverse it
GList *t; GList * t;
for (t = reversed_tracks; t != NULL; t = t->next) { // For each of the tracks for (t = reversed_tracks; t != NULL; t = t->next) { // For each of the tracks
gchar* track_uuid = t->data; gchar* track_uuid = t->data;
koto_playlist_add_track_by_uuid(new_album_playlist, track_uuid, FALSE, FALSE); // Add the UUID, skip commit to table since it is temporary koto_playlist_add_track_by_uuid(new_album_playlist, track_uuid, FALSE, FALSE); // Add the UUID, skip commit to table since it is temporary
@ -508,10 +570,15 @@ void koto_indexed_album_set_as_current_playlist(KotoIndexedAlbum *self) {
koto_current_playlist_set_playlist(current_playlist, new_album_playlist); // Set our new current playlist koto_current_playlist_set_playlist(current_playlist, new_album_playlist); // Set our new current playlist
} }
gint koto_indexed_album_sort_tracks(gconstpointer track1_uuid, gconstpointer track2_uuid, gpointer user_data) { gint koto_indexed_album_sort_tracks(
gconstpointer track1_uuid,
gconstpointer track2_uuid,
gpointer user_data
) {
(void) user_data; (void) user_data;
KotoIndexedTrack *track1 = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) track1_uuid); KotoIndexedTrack * track1 = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) track1_uuid);
KotoIndexedTrack *track2 = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) track2_uuid); KotoIndexedTrack * track2 = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) track2_uuid);
if ((track1 == NULL) && (track2 == NULL)) { // Neither tracks actually exist if ((track1 == NULL) && (track2 == NULL)) { // Neither tracks actually exist
return 0; return 0;
@ -521,8 +588,9 @@ gint koto_indexed_album_sort_tracks(gconstpointer track1_uuid, gconstpointer tra
return 1; return 1;
} }
guint *track1_disc = (guint*) 1; guint * track1_disc = (guint*) 1;
guint *track2_disc = (guint*) 2; guint * track2_disc = (guint*) 2;
g_object_get(track1, "cd", &track1_disc, NULL); g_object_get(track1, "cd", &track1_disc, NULL);
g_object_get(track2, "cd", &track2_disc, NULL); g_object_get(track2, "cd", &track2_disc, NULL);
@ -533,15 +601,16 @@ gint koto_indexed_album_sort_tracks(gconstpointer track1_uuid, gconstpointer tra
return 1; return 1;
} }
guint16 *track1_pos; guint16 * track1_pos;
guint16 *track2_pos; guint16 * track2_pos;
g_object_get(track1, "position", &track1_pos, NULL); g_object_get(track1, "position", &track1_pos, NULL);
g_object_get(track2, "position", &track2_pos, NULL); g_object_get(track2, "position", &track2_pos, NULL);
if (track1_pos == track2_pos) { // Identical positions (like reported as 0) if (track1_pos == track2_pos) { // Identical positions (like reported as 0)
gchar *track1_name; gchar * track1_name;
gchar *track2_name; gchar * track2_name;
g_object_get(track1, "parsed-name", &track1_name, NULL); g_object_get(track1, "parsed-name", &track1_name, NULL);
g_object_get(track2, "parsed-name", &track2_name, NULL); g_object_get(track2, "parsed-name", &track2_name, NULL);
@ -554,7 +623,10 @@ gint koto_indexed_album_sort_tracks(gconstpointer track1_uuid, gconstpointer tra
} }
} }
void koto_indexed_album_update_path(KotoIndexedAlbum *self, gchar* new_path) { void koto_indexed_album_update_path(
KotoIndexedAlbum * self,
gchar* new_path
) {
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
return; return;
} }
@ -577,32 +649,52 @@ void koto_indexed_album_update_path(KotoIndexedAlbum *self, gchar* new_path) {
koto_indexed_album_find_album_art(self); // Update our path for the album art koto_indexed_album_find_album_art(self); // Update our path for the album art
} }
KotoIndexedAlbum* koto_indexed_album_new(KotoIndexedArtist *artist, const gchar *path) { KotoIndexedAlbum * koto_indexed_album_new(
gchar *artist_uuid = NULL; KotoIndexedArtist * artist,
const gchar * path
) {
gchar * artist_uuid = NULL;
g_object_get(artist, "uuid", &artist_uuid, NULL); g_object_get(artist, "uuid", &artist_uuid, NULL);
KotoIndexedAlbum* album = g_object_new(KOTO_TYPE_INDEXED_ALBUM, KotoIndexedAlbum* album = g_object_new(
"artist-uuid", artist_uuid, KOTO_TYPE_INDEXED_ALBUM,
"uuid", g_strdup(g_uuid_string_random()), "artist-uuid",
"do-initial-index", TRUE, artist_uuid,
"path", path, "uuid",
g_strdup(g_uuid_string_random()),
"do-initial-index",
TRUE,
"path",
path,
NULL NULL
); );
koto_indexed_album_commit(album); koto_indexed_album_commit(album);
koto_indexed_album_find_tracks(album, NULL, NULL); // Scan for tracks now that we committed to the database (hopefully) koto_indexed_album_find_tracks(album, NULL, NULL); // Scan for tracks now that we committed to the database (hopefully)
return album; return album;
} }
KotoIndexedAlbum* koto_indexed_album_new_with_uuid(KotoIndexedArtist *artist, const gchar *uuid) { KotoIndexedAlbum * koto_indexed_album_new_with_uuid(
gchar *artist_uuid = NULL; KotoIndexedArtist * artist,
const gchar * uuid
) {
gchar * artist_uuid = NULL;
g_object_get(artist, "uuid", &artist_uuid, NULL); g_object_get(artist, "uuid", &artist_uuid, NULL);
return g_object_new(KOTO_TYPE_INDEXED_ALBUM, return g_object_new(
"artist-uuid", artist, KOTO_TYPE_INDEXED_ALBUM,
"uuid", g_strdup(uuid), "artist-uuid",
"do-initial-index", FALSE, artist,
"uuid",
g_strdup(uuid),
"do-initial-index",
FALSE,
NULL NULL
); );
} }

View file

@ -21,16 +21,16 @@
#include "../db/db.h" #include "../db/db.h"
#include "../koto-utils.h" #include "../koto-utils.h"
extern sqlite3 *koto_db; extern sqlite3 * koto_db;
struct _KotoIndexedArtist { struct _KotoIndexedArtist {
GObject parent_instance; GObject parent_instance;
gchar *uuid; gchar * uuid;
gchar *path; gchar * path;
gboolean has_artist_art; gboolean has_artist_art;
gchar *artist_name; gchar * artist_name;
GList *albums; GList * albums;
}; };
G_DEFINE_TYPE(KotoIndexedArtist, koto_indexed_artist, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoIndexedArtist, koto_indexed_artist, G_TYPE_OBJECT);
@ -43,13 +43,28 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
NULL,
};
static void koto_indexed_artist_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_indexed_artist_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_indexed_artist_class_init(KotoIndexedArtistClass * c) {
GObjectClass * gobject_class;
static void koto_indexed_artist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_indexed_artist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_indexed_artist_class_init(KotoIndexedArtistClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_indexed_artist_set_property; gobject_class->set_property = koto_indexed_artist_set_property;
gobject_class->get_property = koto_indexed_artist_get_property; gobject_class->get_property = koto_indexed_artist_get_property;
@ -59,7 +74,7 @@ static void koto_indexed_artist_class_init(KotoIndexedArtistClass *c) {
"UUID to Artist in database", "UUID to Artist in database",
"UUID to Artist in database", "UUID to Artist in database",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_PATH] = g_param_spec_string( props[PROP_PATH] = g_param_spec_string(
@ -67,7 +82,7 @@ static void koto_indexed_artist_class_init(KotoIndexedArtistClass *c) {
"Path", "Path",
"Path to Artist", "Path to Artist",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_ARTIST_NAME] = g_param_spec_string( props[PROP_ARTIST_NAME] = g_param_spec_string(
@ -75,19 +90,19 @@ static void koto_indexed_artist_class_init(KotoIndexedArtistClass *c) {
"Name", "Name",
"Name of Artist", "Name of Artist",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
void koto_indexed_artist_commit(KotoIndexedArtist *self) { void koto_indexed_artist_commit(KotoIndexedArtist * self) {
if ((self->uuid == NULL) || strcmp(self->uuid, "")) { // UUID not set if ((self->uuid == NULL) || strcmp(self->uuid, "")) { // UUID not set
self->uuid = g_strdup(g_uuid_string_random()); self->uuid = g_strdup(g_uuid_string_random());
} }
// TODO: Support multiple types instead of just local music artist // TODO: Support multiple types instead of just local music artist
gchar *commit_op = g_strdup_printf( gchar * commit_op = g_strdup_printf(
"INSERT INTO artists(id,path,type,name,art_path)" "INSERT INTO artists(id,path,type,name,art_path)"
"VALUES ('%s', quote(\"%s\"), 0, quote(\"%s\"), NULL)" "VALUES ('%s', quote(\"%s\"), 0, quote(\"%s\"), NULL)"
"ON CONFLICT(id) DO UPDATE SET path=excluded.path, type=excluded.type, name=excluded.name, art_path=excluded.art_path;", "ON CONFLICT(id) DO UPDATE SET path=excluded.path, type=excluded.type, name=excluded.name, art_path=excluded.art_path;",
@ -96,9 +111,10 @@ void koto_indexed_artist_commit(KotoIndexedArtist *self) {
self->artist_name self->artist_name
); );
gchar *commit_opt_errmsg = NULL; gchar * commit_opt_errmsg = NULL;
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_opt_errmsg); int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_opt_errmsg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_warning("Failed to write our artist to the database: %s", commit_opt_errmsg); g_warning("Failed to write our artist to the database: %s", commit_opt_errmsg);
} }
@ -107,13 +123,19 @@ void koto_indexed_artist_commit(KotoIndexedArtist *self) {
g_free(commit_opt_errmsg); g_free(commit_opt_errmsg);
} }
static void koto_indexed_artist_init(KotoIndexedArtist *self) { static void koto_indexed_artist_init(KotoIndexedArtist * self) {
self->has_artist_art = FALSE; self->has_artist_art = FALSE;
self->albums = NULL; // Create a new GList self->albums = NULL; // Create a new GList
} }
static void koto_indexed_artist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_indexed_artist_get_property(
KotoIndexedArtist *self = KOTO_INDEXED_ARTIST(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoIndexedArtist * self = KOTO_INDEXED_ARTIST(obj);
switch (prop_id) { switch (prop_id) {
case PROP_UUID: case PROP_UUID:
@ -131,8 +153,14 @@ static void koto_indexed_artist_get_property(GObject *obj, guint prop_id, GValue
} }
} }
static void koto_indexed_artist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_indexed_artist_set_property(
KotoIndexedArtist *self = KOTO_INDEXED_ARTIST(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoIndexedArtist * self = KOTO_INDEXED_ARTIST(obj);
switch (prop_id) { switch (prop_id) {
case PROP_UUID: case PROP_UUID:
@ -151,7 +179,10 @@ static void koto_indexed_artist_set_property(GObject *obj, guint prop_id, const
} }
} }
void koto_indexed_artist_add_album(KotoIndexedArtist *self, gchar *album_uuid) { void koto_indexed_artist_add_album(
KotoIndexedArtist * self,
gchar * album_uuid
) {
if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist
return; return;
} }
@ -160,14 +191,15 @@ void koto_indexed_artist_add_album(KotoIndexedArtist *self, gchar *album_uuid) {
return; return;
} }
gchar *uuid = g_strdup(album_uuid); // Duplicate our UUID gchar * uuid = g_strdup(album_uuid); // Duplicate our UUID
if (g_list_index(self->albums, uuid) == -1) { if (g_list_index(self->albums, uuid) == -1) {
self->albums = g_list_append(self->albums, uuid); // Push to end of list self->albums = g_list_append(self->albums, uuid); // Push to end of list
} }
} }
GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self) { GList * koto_indexed_artist_get_albums(KotoIndexedArtist * self) {
if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist
return NULL; return NULL;
} }
@ -175,7 +207,7 @@ GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self) {
return g_list_copy(self->albums); return g_list_copy(self->albums);
} }
gchar* koto_indexed_artist_get_name(KotoIndexedArtist *self) { gchar * koto_indexed_artist_get_name(KotoIndexedArtist * self) {
if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist
return g_strdup(""); return g_strdup("");
} }
@ -183,7 +215,10 @@ gchar* koto_indexed_artist_get_name(KotoIndexedArtist *self) {
return g_strdup(koto_utils_is_string_valid(self->artist_name) ? self->artist_name : ""); // Return artist name if set return g_strdup(koto_utils_is_string_valid(self->artist_name) ? self->artist_name : ""); // Return artist name if set
} }
void koto_indexed_artist_remove_album(KotoIndexedArtist *self, KotoIndexedAlbum *album) { void koto_indexed_artist_remove_album(
KotoIndexedArtist * self,
KotoIndexedAlbum * album
) {
if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist
return; return;
} }
@ -192,12 +227,17 @@ void koto_indexed_artist_remove_album(KotoIndexedArtist *self, KotoIndexedAlbum
return; return;
} }
gchar *album_uuid; gchar * album_uuid;
g_object_get(album, "uuid", &album_uuid, NULL); g_object_get(album, "uuid", &album_uuid, NULL);
self->albums = g_list_remove(self->albums, album_uuid); self->albums = g_list_remove(self->albums, album_uuid);
} }
void koto_indexed_artist_update_path(KotoIndexedArtist *self, gchar *new_path) { void koto_indexed_artist_update_path(
KotoIndexedArtist * self,
gchar * new_path
) {
if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist
return; return;
} }
@ -214,7 +254,10 @@ void koto_indexed_artist_update_path(KotoIndexedArtist *self, gchar *new_path) {
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]); g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]);
} }
void koto_indexed_artist_set_artist_name(KotoIndexedArtist *self, gchar *artist_name) { void koto_indexed_artist_set_artist_name(
KotoIndexedArtist * self,
gchar * artist_name
) {
if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist if (!KOTO_IS_INDEXED_ARTIST(self)) { // Not an artist
return; return;
} }
@ -231,21 +274,28 @@ void koto_indexed_artist_set_artist_name(KotoIndexedArtist *self, gchar *artist_
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ARTIST_NAME]); g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ARTIST_NAME]);
} }
KotoIndexedArtist* koto_indexed_artist_new(gchar *path) { KotoIndexedArtist * koto_indexed_artist_new(gchar * path) {
KotoIndexedArtist* artist = g_object_new(KOTO_TYPE_INDEXED_ARTIST, KotoIndexedArtist* artist = g_object_new(
"uuid", g_uuid_string_random(), KOTO_TYPE_INDEXED_ARTIST,
"path", path, "uuid",
"name", g_path_get_basename(path), g_uuid_string_random(),
"path",
path,
"name",
g_path_get_basename(path),
NULL NULL
); );
koto_indexed_artist_commit(artist); // Commit the artist immediately to the database koto_indexed_artist_commit(artist); // Commit the artist immediately to the database
return artist; return artist;
} }
KotoIndexedArtist* koto_indexed_artist_new_with_uuid(const gchar *uuid) { KotoIndexedArtist * koto_indexed_artist_new_with_uuid(const gchar * uuid) {
return g_object_new(KOTO_TYPE_INDEXED_ARTIST, return g_object_new(
"uuid", g_strdup(uuid), KOTO_TYPE_INDEXED_ARTIST,
"uuid",
g_strdup(uuid),
NULL NULL
); );
} }

View file

@ -26,14 +26,14 @@
#include "../koto-utils.h" #include "../koto-utils.h"
#include "structs.h" #include "structs.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern sqlite3 *koto_db; extern sqlite3 * koto_db;
struct _KotoIndexedLibrary { struct _KotoIndexedLibrary {
GObject parent_instance; GObject parent_instance;
gchar *path; gchar * path;
magic_t magic_cookie; magic_t magic_cookie;
GHashTable *music_artists; GHashTable * music_artists;
}; };
G_DEFINE_TYPE(KotoIndexedLibrary, koto_indexed_library, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoIndexedLibrary, koto_indexed_library, G_TYPE_OBJECT);
@ -44,13 +44,28 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
NULL,
};
static void koto_indexed_library_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_indexed_library_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_indexed_library_class_init(KotoIndexedLibraryClass * c) {
GObjectClass * gobject_class;
static void koto_indexed_library_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_indexed_library_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_indexed_library_class_init(KotoIndexedLibraryClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_indexed_library_set_property; gobject_class->set_property = koto_indexed_library_set_property;
gobject_class->get_property = koto_indexed_library_get_property; gobject_class->get_property = koto_indexed_library_get_property;
@ -60,26 +75,31 @@ static void koto_indexed_library_class_init(KotoIndexedLibraryClass *c) {
"Path", "Path",
"Path to Music", "Path to Music",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_UTF8); // Ensure our id3v2 text encoding is UTF-8 taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_UTF8); // Ensure our id3v2 text encoding is UTF-8
} }
static void koto_indexed_library_init(KotoIndexedLibrary *self) { static void koto_indexed_library_init(KotoIndexedLibrary * self) {
self->music_artists = g_hash_table_new(g_str_hash, g_str_equal); self->music_artists = g_hash_table_new(g_str_hash, g_str_equal);
} }
void koto_indexed_library_add_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist) { void koto_indexed_library_add_artist(
KotoIndexedLibrary * self,
KotoIndexedArtist * artist
) {
if (artist == NULL) { // No artist if (artist == NULL) { // No artist
return; return;
} }
koto_indexed_library_get_artists(self); // Call to generate if needed koto_indexed_library_get_artists(self); // Call to generate if needed
gchar *artist_name; gchar * artist_name;
gchar *artist_uuid; gchar * artist_uuid;
g_object_get(artist, "name", &artist_name, "uuid", &artist_uuid, NULL); g_object_get(artist, "name", &artist_name, "uuid", &artist_uuid, NULL);
if (g_hash_table_contains(self->music_artists, artist_name)) { // Already have the artist if (g_hash_table_contains(self->music_artists, artist_name)) { // Already have the artist
@ -90,24 +110,28 @@ void koto_indexed_library_add_artist(KotoIndexedLibrary *self, KotoIndexedArtist
g_hash_table_insert(self->music_artists, artist_name, artist_uuid); // Add the artist by its name (this needs to be done so we can get the artist when doing the depth of 2 indexing for the album) g_hash_table_insert(self->music_artists, artist_name, artist_uuid); // Add the artist by its name (this needs to be done so we can get the artist when doing the depth of 2 indexing for the album)
} }
KotoIndexedArtist* koto_indexed_library_get_artist(KotoIndexedLibrary *self, gchar *artist_name) { KotoIndexedArtist * koto_indexed_library_get_artist(
KotoIndexedLibrary * self,
gchar * artist_name
) {
if (artist_name == NULL) { if (artist_name == NULL) {
return NULL; return NULL;
} }
koto_indexed_library_get_artists(self); // Call to generate if needed koto_indexed_library_get_artists(self); // Call to generate if needed
gchar *artist_uuid = g_hash_table_lookup(self->music_artists, artist_name); // Get the UUID from our music artists gchar * artist_uuid = g_hash_table_lookup(self->music_artists, artist_name); // Get the UUID from our music artists
if (artist_uuid != NULL) { if (artist_uuid != NULL) {
KotoIndexedArtist *artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid); // Return any artist from cartographer KotoIndexedArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid); // Return any artist from cartographer
return artist; return artist;
} else { } else {
return NULL; return NULL;
} }
} }
GHashTable* koto_indexed_library_get_artists(KotoIndexedLibrary *self) { GHashTable * koto_indexed_library_get_artists(KotoIndexedLibrary * self) {
if (self->music_artists == NULL) { // Not a HashTable if (self->music_artists == NULL) { // Not a HashTable
self->music_artists = g_hash_table_new(g_str_hash, g_str_equal); self->music_artists = g_hash_table_new(g_str_hash, g_str_equal);
} }
@ -115,21 +139,32 @@ GHashTable* koto_indexed_library_get_artists(KotoIndexedLibrary *self) {
return self->music_artists; return self->music_artists;
} }
void koto_indexed_library_remove_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist) { void koto_indexed_library_remove_artist(
KotoIndexedLibrary * self,
KotoIndexedArtist * artist
) {
if (artist == NULL) { if (artist == NULL) {
return; return;
} }
koto_indexed_library_get_artists(self); // Call to generate if needed koto_indexed_library_get_artists(self); // Call to generate if needed
gchar *artist_name; gchar * artist_name;
g_object_get(artist, "name", &artist_name, NULL); g_object_get(artist, "name", &artist_name, NULL);
g_hash_table_remove(self->music_artists, artist_name); // Remove the artist g_hash_table_remove(self->music_artists, artist_name); // Remove the artist
} }
static void koto_indexed_library_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_indexed_library_get_property(
KotoIndexedLibrary *self = KOTO_INDEXED_LIBRARY(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoIndexedLibrary * self = KOTO_INDEXED_LIBRARY(obj);
switch (prop_id) { switch (prop_id) {
case PROP_PATH: case PROP_PATH:
@ -141,8 +176,14 @@ static void koto_indexed_library_get_property(GObject *obj, guint prop_id, GValu
} }
} }
static void koto_indexed_library_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec){ static void koto_indexed_library_set_property(
KotoIndexedLibrary *self = KOTO_INDEXED_LIBRARY(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoIndexedLibrary * self = KOTO_INDEXED_LIBRARY(obj);
switch (prop_id) { switch (prop_id) {
case PROP_PATH: case PROP_PATH:
@ -154,7 +195,10 @@ static void koto_indexed_library_set_property(GObject *obj, guint prop_id, const
} }
} }
void koto_indexed_library_set_path(KotoIndexedLibrary *self, gchar *path) { void koto_indexed_library_set_path(
KotoIndexedLibrary * self,
gchar * path
) {
if (path == NULL) { if (path == NULL) {
return; return;
} }
@ -172,25 +216,37 @@ void koto_indexed_library_set_path(KotoIndexedLibrary *self, gchar *path) {
} }
} }
int process_artists(void *data, int num_columns, char **fields, char **column_names) { int process_artists(
(void) num_columns; (void) column_names; // Don't need any of the params void * data,
int num_columns,
char ** fields,
char ** column_names
) {
(void) num_columns;
(void) column_names; // Don't need any of the params
KotoIndexedLibrary *library = (KotoIndexedLibrary*) data; KotoIndexedLibrary * library = (KotoIndexedLibrary*) data;
gchar *artist_uuid = g_strdup(koto_utils_unquote_string(fields[0])); // First column is UUID gchar * artist_uuid = g_strdup(koto_utils_unquote_string(fields[0])); // First column is UUID
gchar *artist_path = g_strdup(koto_utils_unquote_string(fields[1])); // Second column is path gchar * artist_path = g_strdup(koto_utils_unquote_string(fields[1])); // Second column is path
gchar *artist_name = g_strdup(koto_utils_unquote_string(fields[3])); // Fourth column is artist name gchar * artist_name = g_strdup(koto_utils_unquote_string(fields[3])); // Fourth column is artist name
KotoIndexedArtist *artist = koto_indexed_artist_new_with_uuid(artist_uuid); // Create our artist with the UUID KotoIndexedArtist * artist = koto_indexed_artist_new_with_uuid(artist_uuid); // Create our artist with the UUID
g_object_set(artist,
"path", artist_path, // Set path g_object_set(
"name", artist_name, // Set name artist,
"path",
artist_path, // Set path
"name",
artist_name, // Set name
NULL); NULL);
koto_cartographer_add_artist(koto_maps, artist); // Add the artist to our global cartographer koto_cartographer_add_artist(koto_maps, artist); // Add the artist to our global cartographer
koto_indexed_library_add_artist(library, artist); koto_indexed_library_add_artist(library, artist);
int albums_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM albums WHERE artist_id=\"%s\"", artist_uuid), process_albums, artist, NULL); // Process our albums int albums_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM albums WHERE artist_id=\"%s\"", artist_uuid), process_albums, artist, NULL); // Process our albums
if (albums_rc != SQLITE_OK) { // Failed to get our albums if (albums_rc != SQLITE_OK) { // Failed to get our albums
g_critical("Failed to read our albums: %s", sqlite3_errmsg(koto_db)); g_critical("Failed to read our albums: %s", sqlite3_errmsg(koto_db));
return 1; return 1;
@ -203,29 +259,42 @@ int process_artists(void *data, int num_columns, char **fields, char **column_na
return 0; return 0;
} }
int process_albums(void *data, int num_columns, char **fields, char **column_names) { int process_albums(
(void) num_columns; (void) column_names; // Don't need these void * data,
int num_columns,
char ** fields,
char ** column_names
) {
(void) num_columns;
(void) column_names; // Don't need these
KotoIndexedArtist *artist = (KotoIndexedArtist*) data; KotoIndexedArtist * artist = (KotoIndexedArtist*) data;
gchar *album_uuid = g_strdup(koto_utils_unquote_string(fields[0])); gchar * album_uuid = g_strdup(koto_utils_unquote_string(fields[0]));
gchar *path = g_strdup(koto_utils_unquote_string(fields[1])); gchar * path = g_strdup(koto_utils_unquote_string(fields[1]));
gchar *artist_uuid = g_strdup(koto_utils_unquote_string(fields[2])); gchar * artist_uuid = g_strdup(koto_utils_unquote_string(fields[2]));
gchar *album_name = g_strdup(koto_utils_unquote_string(fields[3])); gchar * album_name = g_strdup(koto_utils_unquote_string(fields[3]));
gchar *album_art = (fields[4] != NULL) ? g_strdup(koto_utils_unquote_string(fields[4])) : NULL; gchar * album_art = (fields[4] != NULL) ? g_strdup(koto_utils_unquote_string(fields[4])) : NULL;
KotoIndexedAlbum *album = koto_indexed_album_new_with_uuid(artist, album_uuid); // Create our album KotoIndexedAlbum * album = koto_indexed_album_new_with_uuid(artist, album_uuid); // Create our album
g_object_set(album,
"path", path, // Set the path g_object_set(
"name", album_name, // Set name album,
"art-path", album_art, // Set art path if any "path",
path, // Set the path
"name",
album_name, // Set name
"art-path",
album_art, // Set art path if any
NULL); NULL);
koto_cartographer_add_album(koto_maps, album); // Add the album to our global cartographer koto_cartographer_add_album(koto_maps, album); // Add the album to our global cartographer
koto_indexed_artist_add_album(artist, album_uuid); // Add the album koto_indexed_artist_add_album(artist, album_uuid); // Add the album
int tracks_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM tracks WHERE album_id=\"%s\"", album_uuid), process_tracks, album, NULL); // Process our tracks int tracks_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM tracks WHERE album_id=\"%s\"", album_uuid), process_tracks, album, NULL); // Process our tracks
if (tracks_rc != SQLITE_OK) { // Failed to get our tracks if (tracks_rc != SQLITE_OK) { // Failed to get our tracks
g_critical("Failed to read our tracks: %s", sqlite3_errmsg(koto_db)); g_critical("Failed to read our tracks: %s", sqlite3_errmsg(koto_db));
return 1; return 1;
@ -243,20 +312,31 @@ int process_albums(void *data, int num_columns, char **fields, char **column_nam
return 0; return 0;
} }
int process_playlists(void *data, int num_columns, char **fields, char **column_names) { int process_playlists(
(void) data; (void) num_columns; (void) column_names; // Don't need any of the params void * data,
int num_columns,
char ** fields,
char ** column_names
) {
(void) data;
(void) num_columns;
(void) column_names; // Don't need any of the params
gchar * playlist_uuid = g_strdup(koto_utils_unquote_string(fields[0])); // First column is UUID
gchar * playlist_name = g_strdup(koto_utils_unquote_string(fields[1])); // Second column is playlist name
gchar * playlist_art_path = g_strdup(koto_utils_unquote_string(fields[2])); // Third column is any art path
KotoPlaylist * playlist = koto_playlist_new_with_uuid(playlist_uuid); // Create a playlist using the existing UUID
gchar *playlist_uuid = g_strdup(koto_utils_unquote_string(fields[0])); // First column is UUID
gchar *playlist_name = g_strdup(koto_utils_unquote_string(fields[1])); // Second column is playlist name
gchar *playlist_art_path = g_strdup(koto_utils_unquote_string(fields[2])); // Third column is any art path
KotoPlaylist *playlist = koto_playlist_new_with_uuid(playlist_uuid); // Create a playlist using the existing UUID
koto_playlist_set_name(playlist, playlist_name); // Add the playlist name koto_playlist_set_name(playlist, playlist_name); // Add the playlist name
koto_playlist_set_artwork(playlist, playlist_art_path); // Add the playlist art path koto_playlist_set_artwork(playlist, playlist_art_path); // Add the playlist art path
koto_cartographer_add_playlist(koto_maps, playlist); // Add to cartographer koto_cartographer_add_playlist(koto_maps, playlist); // Add to cartographer
int playlist_tracks_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM playlist_tracks WHERE playlist_id=\"%s\" ORDER BY position ASC", playlist_uuid), process_playlists_tracks, playlist, NULL); // Process our playlist tracks int playlist_tracks_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM playlist_tracks WHERE playlist_id=\"%s\" ORDER BY position ASC", playlist_uuid), process_playlists_tracks, playlist, NULL); // Process our playlist tracks
if (playlist_tracks_rc != SQLITE_OK) { // Failed to get our playlist tracks if (playlist_tracks_rc != SQLITE_OK) { // Failed to get our playlist tracks
g_critical("Failed to read our playlist tracks: %s", sqlite3_errmsg(koto_db)); g_critical("Failed to read our playlist tracks: %s", sqlite3_errmsg(koto_db));
return 1; return 1;
@ -271,15 +351,23 @@ int process_playlists(void *data, int num_columns, char **fields, char **column_
return 0; return 0;
} }
int process_playlists_tracks(void *data, int num_columns, char **fields, char **column_names) { int process_playlists_tracks(
(void) data; (void) num_columns; (void) column_names; // Don't need these void * data,
int num_columns,
char ** fields,
char ** column_names
) {
(void) data;
(void) num_columns;
(void) column_names; // Don't need these
gchar *playlist_uuid = g_strdup(koto_utils_unquote_string(fields[1])); gchar * playlist_uuid = g_strdup(koto_utils_unquote_string(fields[1]));
gchar *track_uuid = g_strdup(koto_utils_unquote_string(fields[2])); gchar * track_uuid = g_strdup(koto_utils_unquote_string(fields[2]));
gboolean current = g_strcmp0(koto_utils_unquote_string(fields[3]), "0"); gboolean current = g_strcmp0(koto_utils_unquote_string(fields[3]), "0");
KotoPlaylist *playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, playlist_uuid); // Get the playlist KotoPlaylist * playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, playlist_uuid); // Get the playlist
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, track_uuid); // Get the track KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, track_uuid); // Get the track
if (!KOTO_IS_PLAYLIST(playlist)) { if (!KOTO_IS_PLAYLIST(playlist)) {
goto freeforret; goto freeforret;
@ -294,29 +382,29 @@ freeforret:
return 0; return 0;
} }
int process_tracks(void *data, int num_columns, char **fields, char **column_names) { int process_tracks(
(void) num_columns; (void) column_names; // Don't need these void * data,
int num_columns,
char ** fields,
char ** column_names
) {
(void) num_columns;
(void) column_names; // Don't need these
KotoIndexedAlbum *album = (KotoIndexedAlbum*) data; KotoIndexedAlbum * album = (KotoIndexedAlbum*) data;
gchar *track_uuid = g_strdup(koto_utils_unquote_string(fields[0])); gchar * track_uuid = g_strdup(koto_utils_unquote_string(fields[0]));
gchar *path = g_strdup(koto_utils_unquote_string(fields[1])); gchar * path = g_strdup(koto_utils_unquote_string(fields[1]));
gchar *artist_uuid = g_strdup(koto_utils_unquote_string(fields[3])); gchar * artist_uuid = g_strdup(koto_utils_unquote_string(fields[3]));
gchar *album_uuid = g_strdup(koto_utils_unquote_string(fields[4])); gchar * album_uuid = g_strdup(koto_utils_unquote_string(fields[4]));
gchar *file_name = g_strdup(koto_utils_unquote_string(fields[5])); gchar * file_name = g_strdup(koto_utils_unquote_string(fields[5]));
gchar *name = g_strdup(koto_utils_unquote_string(fields[6])); gchar * name = g_strdup(koto_utils_unquote_string(fields[6]));
guint *disc_num = (guint*) g_ascii_strtoull(fields[7], NULL, 10); guint * disc_num = (guint*) g_ascii_strtoull(fields[7], NULL, 10);
guint *position = (guint*) g_ascii_strtoull(fields[8], NULL, 10); guint * position = (guint*) g_ascii_strtoull(fields[8], NULL, 10);
KotoIndexedTrack *track = koto_indexed_track_new_with_uuid(track_uuid); // Create our file KotoIndexedTrack * track = koto_indexed_track_new_with_uuid(track_uuid); // Create our file
g_object_set(track,
"artist-uuid", artist_uuid,
"album-uuid", album_uuid, g_object_set(track, "artist-uuid", artist_uuid, "album-uuid", album_uuid, "path", path, "file-name", file_name, "parsed-name", name, "cd", disc_num, "position", position, NULL);
"path", path,
"file-name", file_name,
"parsed-name", name,
"cd", disc_num,
"position", position,
NULL);
koto_cartographer_add_track(koto_maps, track); // Add the track to cartographer koto_cartographer_add_track(koto_maps, track); // Add the track to cartographer
koto_indexed_album_add_track(album, track); // Add the track koto_indexed_album_add_track(album, track); // Add the track
@ -331,8 +419,10 @@ int process_tracks(void *data, int num_columns, char **fields, char **column_nam
return 0; return 0;
} }
void read_from_db(KotoIndexedLibrary *self) { void read_from_db(KotoIndexedLibrary * self) {
int artists_rc = sqlite3_exec(koto_db, "SELECT * FROM artists", process_artists, self, NULL); // Process our artists int artists_rc = sqlite3_exec(koto_db, "SELECT * FROM artists", process_artists, self, NULL); // Process our artists
if (artists_rc != SQLITE_OK) { // Failed to get our artists if (artists_rc != SQLITE_OK) { // Failed to get our artists
g_critical("Failed to read our artists: %s", sqlite3_errmsg(koto_db)); g_critical("Failed to read our artists: %s", sqlite3_errmsg(koto_db));
return; return;
@ -341,16 +431,19 @@ void read_from_db(KotoIndexedLibrary *self) {
g_hash_table_foreach(self->music_artists, output_artists, NULL); g_hash_table_foreach(self->music_artists, output_artists, NULL);
int playlist_rc = sqlite3_exec(koto_db, "SELECT * FROM playlist_meta", process_playlists, self, NULL); // Process our playlists int playlist_rc = sqlite3_exec(koto_db, "SELECT * FROM playlist_meta", process_playlists, self, NULL); // Process our playlists
if (playlist_rc != SQLITE_OK) { // Failed to get our playlists if (playlist_rc != SQLITE_OK) { // Failed to get our playlists
g_critical("Failed to read our playlists: %s", sqlite3_errmsg(koto_db)); g_critical("Failed to read our playlists: %s", sqlite3_errmsg(koto_db));
return; return;
} }
} }
void start_indexing(KotoIndexedLibrary *self) { void start_indexing(KotoIndexedLibrary * self) {
struct stat library_stat; struct stat library_stat;
int success = stat(self->path, &library_stat); int success = stat(self->path, &library_stat);
if (success != 0) { // Failed to read the library path if (success != 0) { // Failed to read the library path
return; return;
} }
@ -376,38 +469,48 @@ void start_indexing(KotoIndexedLibrary *self) {
g_hash_table_foreach(self->music_artists, output_artists, NULL); g_hash_table_foreach(self->music_artists, output_artists, NULL);
} }
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path) { KotoIndexedLibrary * koto_indexed_library_new(const gchar * path) {
return g_object_new(KOTO_TYPE_INDEXED_LIBRARY, return g_object_new(
"path", path, KOTO_TYPE_INDEXED_LIBRARY,
"path",
path,
NULL NULL
); );
} }
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth) { void index_folder(
KotoIndexedLibrary * self,
gchar * path,
guint depth
) {
depth++; depth++;
DIR *dir = opendir(path); // Attempt to open our directory DIR * dir = opendir(path); // Attempt to open our directory
if (dir == NULL) { if (dir == NULL) {
return; return;
} }
struct dirent *entry; struct dirent * entry;
while ((entry = readdir(dir))) { while ((entry = readdir(dir))) {
if (g_str_has_prefix(entry->d_name, ".")) { // A reference to parent dir, self, or a hidden item if (g_str_has_prefix(entry->d_name, ".")) { // A reference to parent dir, self, or a hidden item
continue; continue;
} }
gchar *full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name); gchar * full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name);
if (entry->d_type == DT_DIR) { // Directory if (entry->d_type == DT_DIR) { // Directory
if (depth == 1) { // If we are following FOLDER/ARTIST/ALBUM then this would be artist if (depth == 1) { // If we are following FOLDER/ARTIST/ALBUM then this would be artist
KotoIndexedArtist *artist = koto_indexed_artist_new(full_path); // Attempt to get the artist KotoIndexedArtist * artist = koto_indexed_artist_new(full_path); // Attempt to get the artist
gchar *artist_name; gchar * artist_name;
g_object_get(artist, g_object_get(
"name", &artist_name, artist,
"name",
&artist_name,
NULL NULL
); );
@ -416,17 +519,17 @@ void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth) {
index_folder(self, full_path, depth); // Index this directory index_folder(self, full_path, depth); // Index this directory
g_free(artist_name); g_free(artist_name);
} else if (depth == 2) { // If we are following FOLDER/ARTIST/ALBUM then this would be album } else if (depth == 2) { // If we are following FOLDER/ARTIST/ALBUM then this would be album
gchar *artist_name = g_path_get_basename(path); // Get the last entry from our path which is probably the artist gchar * artist_name = g_path_get_basename(path); // Get the last entry from our path which is probably the artist
KotoIndexedArtist *artist = koto_indexed_library_get_artist(self, artist_name); // Get the artist KotoIndexedArtist * artist = koto_indexed_library_get_artist(self, artist_name); // Get the artist
if (artist == NULL) { if (artist == NULL) {
continue; continue;
} }
KotoIndexedAlbum *album = koto_indexed_album_new(artist, full_path); KotoIndexedAlbum * album = koto_indexed_album_new(artist, full_path);
koto_cartographer_add_album(koto_maps, album); // Add our album to the cartographer koto_cartographer_add_album(koto_maps, album); // Add our album to the cartographer
gchar *album_uuid = NULL; gchar * album_uuid = NULL;
g_object_get(album, "uuid", &album_uuid, NULL); g_object_get(album, "uuid", &album_uuid, NULL);
koto_indexed_artist_add_album(artist, album_uuid); // Add the album koto_indexed_artist_add_album(artist, album_uuid); // Add the album
g_free(artist_name); g_free(artist_name);
@ -439,35 +542,46 @@ void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth) {
closedir(dir); // Close the directory closedir(dir); // Close the directory
} }
void output_artists(gpointer artist_key, gpointer artist_ptr, gpointer data) { void output_artists(
(void) artist_ptr; (void) data; gpointer artist_key,
KotoIndexedArtist *artist = koto_cartographer_get_artist_by_uuid(koto_maps, (gchar*) artist_key); gpointer artist_ptr,
gpointer data
) {
(void) artist_ptr;
(void) data;
KotoIndexedArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, (gchar*) artist_key);
if (artist == NULL) { if (artist == NULL) {
return; return;
} }
gchar *artist_name; gchar * artist_name;
g_object_get(artist, "name", &artist_name, NULL); g_object_get(artist, "name", &artist_name, NULL);
g_debug("Artist: %s", artist_name); g_debug("Artist: %s", artist_name);
GList *albums = koto_indexed_artist_get_albums(artist); // Get the albums for this artist GList * albums = koto_indexed_artist_get_albums(artist); // Get the albums for this artist
if (albums != NULL) { if (albums != NULL) {
g_debug("Length of Albums: %d", g_list_length(albums)); g_debug("Length of Albums: %d", g_list_length(albums));
} }
GList *a; GList * a;
for (a = albums; a != NULL; a = a->next) { for (a = albums; a != NULL; a = a->next) {
gchar *album_uuid = a->data; gchar * album_uuid = a->data;
KotoIndexedAlbum *album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid); KotoIndexedAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid);
if (album == NULL) { if (album == NULL) {
continue; continue;
} }
gchar *artwork = koto_indexed_album_get_album_art(album); gchar * artwork = koto_indexed_album_get_album_art(album);
gchar *album_name; gchar * album_name;
g_object_get(album, "name", &album_name, NULL); g_object_get(album, "name", &album_name, NULL);
g_debug("Album Art: %s", artwork); g_debug("Album Art: %s", artwork);
g_debug("Album Name: %s", album_name); g_debug("Album Name: %s", album_name);
@ -476,24 +590,25 @@ void output_artists(gpointer artist_key, gpointer artist_ptr, gpointer data) {
} }
} }
void output_track(gpointer data, gpointer user_data) { void output_track(
gpointer data,
gpointer user_data
) {
(void) user_data; (void) user_data;
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) data); KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) data);
if (track == NULL) { if (track == NULL) {
return; return;
} }
gchar *filepath; gchar * filepath;
gchar *parsed_name; gchar * parsed_name;
guint *pos; guint * pos;
g_object_get(track,
"path", &filepath, g_object_get(track, "path", &filepath, "parsed-name", &parsed_name, "position", &pos, NULL);
"parsed-name", &parsed_name,
"position", &pos,
NULL);
g_debug("File Path: %s", filepath); g_debug("File Path: %s", filepath);
g_debug("Parsed Name: %s", parsed_name); g_debug("Parsed Name: %s", parsed_name);
g_debug("Position: %d", GPOINTER_TO_INT(pos)); g_debug("Position: %d", GPOINTER_TO_INT(pos));

View file

@ -23,18 +23,18 @@ G_BEGIN_DECLS
/** /**
* Type Definition * Type Definition
**/ **/
#define KOTO_TYPE_INDEXED_LIBRARY koto_indexed_library_get_type() #define KOTO_TYPE_INDEXED_LIBRARY koto_indexed_library_get_type()
G_DECLARE_FINAL_TYPE(KotoIndexedLibrary, koto_indexed_library, KOTO, INDEXED_LIBRARY, GObject); G_DECLARE_FINAL_TYPE(KotoIndexedLibrary, koto_indexed_library, KOTO, INDEXED_LIBRARY, GObject);
#define KOTO_IS_INDEXED_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_INDEXED_LIBRARY)) #define KOTO_IS_INDEXED_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_INDEXED_LIBRARY))
#define KOTO_TYPE_INDEXED_ARTIST koto_indexed_artist_get_type() #define KOTO_TYPE_INDEXED_ARTIST koto_indexed_artist_get_type()
G_DECLARE_FINAL_TYPE (KotoIndexedArtist, koto_indexed_artist, KOTO, INDEXED_ARTIST, GObject); G_DECLARE_FINAL_TYPE(KotoIndexedArtist, koto_indexed_artist, KOTO, INDEXED_ARTIST, GObject);
#define KOTO_IS_INDEXED_ARTIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_INDEXED_ARTIST)) #define KOTO_IS_INDEXED_ARTIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_INDEXED_ARTIST))
#define KOTO_TYPE_INDEXED_ALBUM koto_indexed_album_get_type() #define KOTO_TYPE_INDEXED_ALBUM koto_indexed_album_get_type()
G_DECLARE_FINAL_TYPE (KotoIndexedAlbum, koto_indexed_album, KOTO, INDEXED_ALBUM, GObject); G_DECLARE_FINAL_TYPE(KotoIndexedAlbum, koto_indexed_album, KOTO, INDEXED_ALBUM, GObject);
#define KOTO_IS_INDEXED_ALBUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_INDEXED_ALBUM)) #define KOTO_IS_INDEXED_ALBUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_INDEXED_ALBUM))
#define KOTO_TYPE_INDEXED_TRACK koto_indexed_track_get_type() #define KOTO_TYPE_INDEXED_TRACK koto_indexed_track_get_type()
@ -43,84 +43,258 @@ G_DECLARE_FINAL_TYPE(KotoIndexedTrack, koto_indexed_track, KOTO, INDEXED_TRACK,
/** /**
* Library Functions * Library Functions
**/ **/
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path); KotoIndexedLibrary * koto_indexed_library_new(const gchar * path);
void koto_indexed_library_add_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist); void koto_indexed_library_add_artist(
KotoIndexedArtist* koto_indexed_library_get_artist(KotoIndexedLibrary *self, gchar* artist_name); KotoIndexedLibrary * self,
GHashTable* koto_indexed_library_get_artists(KotoIndexedLibrary *self); KotoIndexedArtist * artist
void koto_indexed_library_remove_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist); );
void koto_indexed_library_set_path(KotoIndexedLibrary *self, gchar *path);
int process_artists(void *data, int num_columns, char **fields, char **column_names); KotoIndexedArtist * koto_indexed_library_get_artist(
int process_albums(void *data, int num_columns, char **fields, char **column_names); KotoIndexedLibrary * self,
int process_playlists(void *data, int num_columns, char **fields, char **column_names); gchar* artist_name
int process_playlists_tracks(void *data, int num_columns, char **fields, char **column_names); );
int process_tracks(void *data, int num_columns, char **fields, char **column_names);
void read_from_db(KotoIndexedLibrary *self); GHashTable * koto_indexed_library_get_artists(KotoIndexedLibrary * self);
void start_indexing(KotoIndexedLibrary *self);
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth); void koto_indexed_library_remove_artist(
void output_track(gpointer data, gpointer user_data); KotoIndexedLibrary * self,
KotoIndexedArtist * artist
);
void koto_indexed_library_set_path(
KotoIndexedLibrary * self,
gchar * path
);
int process_artists(
void * data,
int num_columns,
char ** fields,
char ** column_names
);
int process_albums(
void * data,
int num_columns,
char ** fields,
char ** column_names
);
int process_playlists(
void * data,
int num_columns,
char ** fields,
char ** column_names
);
int process_playlists_tracks(
void * data,
int num_columns,
char ** fields,
char ** column_names
);
int process_tracks(
void * data,
int num_columns,
char ** fields,
char ** column_names
);
void read_from_db(KotoIndexedLibrary * self);
void start_indexing(KotoIndexedLibrary * self);
void index_folder(
KotoIndexedLibrary * self,
gchar * path,
guint depth
);
void output_track(
gpointer data,
gpointer user_data
);
/** /**
* Artist Functions * Artist Functions
**/ **/
KotoIndexedArtist* koto_indexed_artist_new(gchar *path); KotoIndexedArtist * koto_indexed_artist_new(gchar * path);
KotoIndexedArtist* koto_indexed_artist_new_with_uuid(const gchar *uuid);
void koto_indexed_artist_add_album(KotoIndexedArtist *self, gchar *album_uuid); KotoIndexedArtist * koto_indexed_artist_new_with_uuid(const gchar * uuid);
void koto_indexed_artist_commit(KotoIndexedArtist *self);
guint koto_indexed_artist_find_album_with_name(gconstpointer *album_data, gconstpointer *album_name_data); void koto_indexed_artist_add_album(
GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self); KotoIndexedArtist * self,
gchar* koto_indexed_artist_get_name(KotoIndexedArtist *self); gchar * album_uuid
void koto_indexed_artist_remove_album(KotoIndexedArtist *self, KotoIndexedAlbum *album); );
void koto_indexed_artist_remove_album_by_name(KotoIndexedArtist *self, gchar *album_name);
void koto_indexed_artist_set_artist_name(KotoIndexedArtist *self, gchar *artist_name); void koto_indexed_artist_commit(KotoIndexedArtist * self);
void koto_indexed_artist_update_path(KotoIndexedArtist *self, gchar *new_path);
void output_artists(gpointer artist_key, gpointer artist_ptr, gpointer data); guint koto_indexed_artist_find_album_with_name(
gconstpointer * album_data,
gconstpointer * album_name_data
);
GList * koto_indexed_artist_get_albums(KotoIndexedArtist * self);
gchar * koto_indexed_artist_get_name(KotoIndexedArtist * self);
void koto_indexed_artist_remove_album(
KotoIndexedArtist * self,
KotoIndexedAlbum * album
);
void koto_indexed_artist_remove_album_by_name(
KotoIndexedArtist * self,
gchar * album_name
);
void koto_indexed_artist_set_artist_name(
KotoIndexedArtist * self,
gchar * artist_name
);
void koto_indexed_artist_update_path(
KotoIndexedArtist * self,
gchar * new_path
);
void output_artists(
gpointer artist_key,
gpointer artist_ptr,
gpointer data
);
/** /**
* Album Functions * Album Functions
**/ **/
KotoIndexedAlbum* koto_indexed_album_new(KotoIndexedArtist *artist, const gchar *path); KotoIndexedAlbum * koto_indexed_album_new(
KotoIndexedAlbum* koto_indexed_album_new_with_uuid(KotoIndexedArtist *artist, const gchar *uuid); KotoIndexedArtist * artist,
const gchar * path
);
void koto_indexed_album_add_track(KotoIndexedAlbum *self, KotoIndexedTrack *track); KotoIndexedAlbum * koto_indexed_album_new_with_uuid(
void koto_indexed_album_commit(KotoIndexedAlbum *self); KotoIndexedArtist * artist,
void koto_indexed_album_find_album_art(KotoIndexedAlbum *self); const gchar * uuid
void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie, const gchar *path); );
gchar* koto_indexed_album_get_album_art(KotoIndexedAlbum *self);
gchar* koto_indexed_album_get_album_name(KotoIndexedAlbum *self); void koto_indexed_album_add_track(
gchar* koto_indexed_album_get_album_uuid(KotoIndexedAlbum *self); KotoIndexedAlbum * self,
GList* koto_indexed_album_get_tracks(KotoIndexedAlbum *self); KotoIndexedTrack * track
void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedTrack *track); );
void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album_art);
void koto_indexed_album_set_album_name(KotoIndexedAlbum *self, const gchar *album_name); void koto_indexed_album_commit(KotoIndexedAlbum * self);
void koto_indexed_album_set_artist_uuid(KotoIndexedAlbum *self, const gchar *artist_uuid);
void koto_indexed_album_set_as_current_playlist(KotoIndexedAlbum *self); void koto_indexed_album_find_album_art(KotoIndexedAlbum * self);
void koto_indexed_album_update_path(KotoIndexedAlbum *self, gchar *path);
gint koto_indexed_album_sort_tracks(gconstpointer track1_uuid, gconstpointer track2_uuid, gpointer user_data); void koto_indexed_album_find_tracks(
KotoIndexedAlbum * self,
magic_t magic_cookie,
const gchar * path
);
gchar * koto_indexed_album_get_album_art(KotoIndexedAlbum * self);
gchar * koto_indexed_album_get_album_name(KotoIndexedAlbum * self);
gchar * koto_indexed_album_get_album_uuid(KotoIndexedAlbum * self);
GList * koto_indexed_album_get_tracks(KotoIndexedAlbum * self);
void koto_indexed_album_remove_file(
KotoIndexedAlbum * self,
KotoIndexedTrack * track
);
void koto_indexed_album_set_album_art(
KotoIndexedAlbum * self,
const gchar * album_art
);
void koto_indexed_album_set_album_name(
KotoIndexedAlbum * self,
const gchar * album_name
);
void koto_indexed_album_set_artist_uuid(
KotoIndexedAlbum * self,
const gchar * artist_uuid
);
void koto_indexed_album_set_as_current_playlist(KotoIndexedAlbum * self);
void koto_indexed_album_update_path(
KotoIndexedAlbum * self,
gchar * path
);
gint koto_indexed_album_sort_tracks(
gconstpointer track1_uuid,
gconstpointer track2_uuid,
gpointer user_data
);
/** /**
* File / Track Functions * File / Track Functions
**/ **/
KotoIndexedTrack* koto_indexed_track_new(KotoIndexedAlbum *album, const gchar *path, guint *cd); KotoIndexedTrack * koto_indexed_track_new(
KotoIndexedTrack* koto_indexed_track_new_with_uuid(const gchar *uuid); KotoIndexedAlbum * album,
const gchar * path,
guint * cd
);
void koto_indexed_track_commit(KotoIndexedTrack *self); KotoIndexedTrack * koto_indexed_track_new_with_uuid(const gchar * uuid);
GVariant* koto_indexed_track_get_metadata_vardict(KotoIndexedTrack *self);
gchar* koto_indexed_track_get_uuid(KotoIndexedTrack *self); void koto_indexed_track_commit(KotoIndexedTrack * self);
void koto_indexed_track_parse_name(KotoIndexedTrack *self);
void koto_indexed_track_remove_from_playlist(KotoIndexedTrack *self, gchar *playlist_uuid); GVariant * koto_indexed_track_get_metadata_vardict(KotoIndexedTrack * self);
void koto_indexed_track_save_to_playlist(KotoIndexedTrack *self, gchar *playlist_uuid, gint current);
void koto_indexed_track_set_file_name(KotoIndexedTrack *self, gchar *new_file_name); gchar * koto_indexed_track_get_uuid(KotoIndexedTrack * self);
void koto_indexed_track_set_cd(KotoIndexedTrack *self, guint cd);
void koto_indexed_track_set_parsed_name(KotoIndexedTrack *self, gchar *new_parsed_name); void koto_indexed_track_parse_name(KotoIndexedTrack * self);
void koto_indexed_track_set_position(KotoIndexedTrack *self, guint pos);
void koto_indexed_track_update_metadata(KotoIndexedTrack *self); void koto_indexed_track_remove_from_playlist(
void koto_indexed_track_update_path(KotoIndexedTrack *self, const gchar *new_path); KotoIndexedTrack * self,
gchar * playlist_uuid
);
void koto_indexed_track_save_to_playlist(
KotoIndexedTrack * self,
gchar * playlist_uuid,
gint current
);
void koto_indexed_track_set_file_name(
KotoIndexedTrack * self,
gchar * new_file_name
);
void koto_indexed_track_set_cd(
KotoIndexedTrack * self,
guint cd
);
void koto_indexed_track_set_parsed_name(
KotoIndexedTrack * self,
gchar * new_parsed_name
);
void koto_indexed_track_set_position(
KotoIndexedTrack * self,
guint pos
);
void koto_indexed_track_update_metadata(KotoIndexedTrack * self);
void koto_indexed_track_update_path(
KotoIndexedTrack * self,
const gchar * new_path
);
G_END_DECLS G_END_DECLS

View file

@ -22,21 +22,21 @@
#include "structs.h" #include "structs.h"
#include "koto-utils.h" #include "koto-utils.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern sqlite3 *koto_db; extern sqlite3 * koto_db;
struct _KotoIndexedTrack { struct _KotoIndexedTrack {
GObject parent_instance; GObject parent_instance;
gchar *artist_uuid; gchar * artist_uuid;
gchar *album_uuid; gchar * album_uuid;
gchar *uuid; gchar * uuid;
gchar *path; gchar * path;
gchar *file_name; gchar * file_name;
gchar *parsed_name; gchar * parsed_name;
guint *cd; guint * cd;
guint *position; guint * position;
guint *playback_position; guint * playback_position;
gboolean acquired_metadata_from_id3; gboolean acquired_metadata_from_id3;
gboolean do_initial_index; gboolean do_initial_index;
@ -59,13 +59,28 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL }; static GParamSpec * props[N_PROPERTIES] = {
NULL
};
static void koto_indexed_track_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_indexed_track_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_indexed_track_class_init(KotoIndexedTrackClass * c) {
GObjectClass * gobject_class;
static void koto_indexed_track_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_indexed_track_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_indexed_track_set_property; gobject_class->set_property = koto_indexed_track_set_property;
gobject_class->get_property = koto_indexed_track_get_property; gobject_class->get_property = koto_indexed_track_get_property;
@ -75,7 +90,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
"UUID to Artist associated with the File", "UUID to Artist associated with the File",
"UUID to Artist associated with the File", "UUID to Artist associated with the File",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_ALBUM_UUID] = g_param_spec_string( props[PROP_ALBUM_UUID] = g_param_spec_string(
@ -83,7 +98,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
"UUID to Album associated with the File", "UUID to Album associated with the File",
"UUID to Album associated with the File", "UUID to Album associated with the File",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_UUID] = g_param_spec_string( props[PROP_UUID] = g_param_spec_string(
@ -91,7 +106,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
"UUID to File in database", "UUID to File in database",
"UUID to File in database", "UUID to File in database",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_DO_INITIAL_INDEX] = g_param_spec_boolean( props[PROP_DO_INITIAL_INDEX] = g_param_spec_boolean(
@ -99,7 +114,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
"Do an initial indexing operating instead of pulling from the database", "Do an initial indexing operating instead of pulling from the database",
"Do an initial indexing operating instead of pulling from the database", "Do an initial indexing operating instead of pulling from the database",
FALSE, FALSE,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_PATH] = g_param_spec_string( props[PROP_PATH] = g_param_spec_string(
@ -107,7 +122,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
"Path", "Path",
"Path to File", "Path to File",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_FILE_NAME] = g_param_spec_string( props[PROP_FILE_NAME] = g_param_spec_string(
@ -115,7 +130,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
"Name of File", "Name of File",
"Name of File", "Name of File",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_PARSED_NAME] = g_param_spec_string( props[PROP_PARSED_NAME] = g_param_spec_string(
@ -123,7 +138,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
"Parsed Name of File", "Parsed Name of File",
"Parsed Name of File", "Parsed Name of File",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_CD] = g_param_spec_uint( props[PROP_CD] = g_param_spec_uint(
@ -133,7 +148,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
0, 0,
G_MAXUINT16, G_MAXUINT16,
1, 1,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_POSITION] = g_param_spec_uint( props[PROP_POSITION] = g_param_spec_uint(
@ -143,7 +158,7 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
0, 0,
G_MAXUINT16, G_MAXUINT16,
0, 0,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_PLAYBACK_POSITION] = g_param_spec_uint( props[PROP_PLAYBACK_POSITION] = g_param_spec_uint(
@ -153,18 +168,24 @@ static void koto_indexed_track_class_init(KotoIndexedTrackClass *c) {
0, 0,
G_MAXUINT16, G_MAXUINT16,
0, 0,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_indexed_track_init(KotoIndexedTrack *self) { static void koto_indexed_track_init(KotoIndexedTrack * self) {
self->acquired_metadata_from_id3 = FALSE; self->acquired_metadata_from_id3 = FALSE;
} }
static void koto_indexed_track_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_indexed_track_get_property(
KotoIndexedTrack *self = KOTO_INDEXED_TRACK(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoIndexedTrack * self = KOTO_INDEXED_TRACK(obj);
switch (prop_id) { switch (prop_id) {
case PROP_ARTIST_UUID: case PROP_ARTIST_UUID:
@ -200,8 +221,14 @@ static void koto_indexed_track_get_property(GObject *obj, guint prop_id, GValue
} }
} }
static void koto_indexed_track_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_indexed_track_set_property(
KotoIndexedTrack *self = KOTO_INDEXED_TRACK(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoIndexedTrack * self = KOTO_INDEXED_TRACK(obj);
switch (prop_id) { switch (prop_id) {
case PROP_ARTIST_UUID: case PROP_ARTIST_UUID:
@ -244,7 +271,7 @@ static void koto_indexed_track_set_property(GObject *obj, guint prop_id, const G
} }
void koto_indexed_track_commit(KotoIndexedTrack *self) { void koto_indexed_track_commit(KotoIndexedTrack * self) {
if ((self->artist_uuid == NULL) || (strcmp(self->artist_uuid, "") == 0)) { // No valid required artist UUID if ((self->artist_uuid == NULL) || (strcmp(self->artist_uuid, "") == 0)) { // No valid required artist UUID
return; return;
} }
@ -253,7 +280,7 @@ void koto_indexed_track_commit(KotoIndexedTrack *self) {
g_object_set(self, "album-uuid", "", NULL); // Set to an empty string g_object_set(self, "album-uuid", "", NULL); // Set to an empty string
} }
gchar *commit_op = g_strdup_printf( gchar * commit_op = g_strdup_printf(
"INSERT INTO tracks(id, path, type, artist_id, album_id, file_name, name, disc, position)" "INSERT INTO tracks(id, path, type, artist_id, album_id, file_name, name, disc, position)"
"VALUES('%s', quote(\"%s\"), 0, '%s', '%s', quote(\"%s\"), quote(\"%s\"), %d, %d)" "VALUES('%s', quote(\"%s\"), 0, '%s', '%s', quote(\"%s\"), quote(\"%s\"), %d, %d)"
"ON CONFLICT(id) DO UPDATE SET path=excluded.path, type=excluded.type, album_id=excluded.album_id, file_name=excluded.file_name, name=excluded.file_name, disc=excluded.disc, position=excluded.position;", "ON CONFLICT(id) DO UPDATE SET path=excluded.path, type=excluded.type, album_id=excluded.album_id, file_name=excluded.file_name, name=excluded.file_name, disc=excluded.disc, position=excluded.position;",
@ -267,9 +294,10 @@ void koto_indexed_track_commit(KotoIndexedTrack *self) {
GPOINTER_TO_INT((int*) self->position) GPOINTER_TO_INT((int*) self->position)
); );
gchar *commit_op_errmsg = NULL; gchar * commit_op_errmsg = NULL;
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg); int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_warning("Failed to write our file to the database: %s", commit_op_errmsg); g_warning("Failed to write our file to the database: %s", commit_op_errmsg);
} }
@ -278,28 +306,24 @@ void koto_indexed_track_commit(KotoIndexedTrack *self) {
g_free(commit_op_errmsg); g_free(commit_op_errmsg);
} }
GVariant* koto_indexed_track_get_metadata_vardict(KotoIndexedTrack *self) { GVariant * koto_indexed_track_get_metadata_vardict(KotoIndexedTrack * self) {
if (!KOTO_IS_INDEXED_TRACK(self)) { if (!KOTO_IS_INDEXED_TRACK(self)) {
return NULL; return NULL;
} }
GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT); GVariantBuilder * builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
gchar *album_art_path = NULL; gchar * album_art_path = NULL;
gchar *album_name = NULL; gchar * album_name = NULL;
gchar *artist_name = NULL; gchar * artist_name = NULL;
KotoIndexedArtist *artist = koto_cartographer_get_artist_by_uuid(koto_maps, self->artist_uuid); KotoIndexedArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, self->artist_uuid);
KotoIndexedAlbum *album = koto_cartographer_get_album_by_uuid(koto_maps, self->album_uuid); KotoIndexedAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, self->album_uuid);
g_object_get(album,
"art-path", &album_art_path,
"name", &album_name,
NULL);
g_object_get(artist, g_object_get(album, "art-path", &album_art_path, "name", &album_name, NULL);
"name", &artist_name,
NULL); g_object_get(artist, "name", &artist_name, NULL);
g_variant_builder_add(builder, "{sv}", "mpris:trackid", g_variant_new_string(self->uuid)); g_variant_builder_add(builder, "{sv}", "mpris:trackid", g_variant_new_string(self->uuid));
@ -311,8 +335,8 @@ GVariant* koto_indexed_track_get_metadata_vardict(KotoIndexedTrack *self) {
g_variant_builder_add(builder, "{sv}", "xesam:album", g_variant_new_string(album_name)); g_variant_builder_add(builder, "{sv}", "xesam:album", g_variant_new_string(album_name));
if (koto_utils_is_string_valid(artist_name)) { // Valid artist name if (koto_utils_is_string_valid(artist_name)) { // Valid artist name
GVariant *artist_name_variant; GVariant * artist_name_variant;
GVariantBuilder *artist_list_builder = g_variant_builder_new(G_VARIANT_TYPE("as")); GVariantBuilder * artist_list_builder = g_variant_builder_new(G_VARIANT_TYPE("as"));
g_variant_builder_add(artist_list_builder, "s", artist_name); g_variant_builder_add(artist_list_builder, "s", artist_name);
artist_name_variant = g_variant_new("as", artist_list_builder); artist_name_variant = g_variant_new("as", artist_list_builder);
g_variant_builder_unref(artist_list_builder); g_variant_builder_unref(artist_list_builder);
@ -326,12 +350,13 @@ GVariant* koto_indexed_track_get_metadata_vardict(KotoIndexedTrack *self) {
g_variant_builder_add(builder, "{sv}", "xesam:url", g_variant_new_string(self->path)); g_variant_builder_add(builder, "{sv}", "xesam:url", g_variant_new_string(self->path));
g_variant_builder_add(builder, "{sv}", "xesam:trackNumber", g_variant_new_uint64(GPOINTER_TO_UINT(self->position))); g_variant_builder_add(builder, "{sv}", "xesam:trackNumber", g_variant_new_uint64(GPOINTER_TO_UINT(self->position)));
GVariant *metadata_ret = g_variant_builder_end(builder); GVariant * metadata_ret = g_variant_builder_end(builder);
return metadata_ret; return metadata_ret;
} }
gchar* koto_indexed_track_get_uuid(KotoIndexedTrack *self) { gchar * koto_indexed_track_get_uuid(KotoIndexedTrack * self) {
if (!KOTO_IS_INDEXED_TRACK(self)) { if (!KOTO_IS_INDEXED_TRACK(self)) {
return NULL; return NULL;
} }
@ -339,18 +364,20 @@ gchar* koto_indexed_track_get_uuid(KotoIndexedTrack *self) {
return self->uuid; // Do not return a duplicate since otherwise comparison refs fail due to pointer positions being different return self->uuid; // Do not return a duplicate since otherwise comparison refs fail due to pointer positions being different
} }
void koto_indexed_track_parse_name(KotoIndexedTrack *self) { void koto_indexed_track_parse_name(KotoIndexedTrack * self) {
gchar *copied_file_name = g_strdelimit(g_strdup(self->file_name), "_", ' '); // Replace _ with whitespace for starters gchar * copied_file_name = g_strdelimit(g_strdup(self->file_name), "_", ' '); // Replace _ with whitespace for starters
KotoIndexedArtist * artist = NULL;
KotoIndexedArtist *artist = NULL;
artist = koto_cartographer_get_artist_by_uuid(koto_maps, self->artist_uuid); artist = koto_cartographer_get_artist_by_uuid(koto_maps, self->artist_uuid);
if (artist != NULL) { // If we have artist if (artist != NULL) { // If we have artist
gchar *artist_name = NULL; gchar * artist_name = NULL;
g_object_get(artist, "name", &artist_name, NULL); g_object_get(artist, "name", &artist_name, NULL);
if (artist_name != NULL && (strcmp(artist_name, "") != 0)) { if (artist_name != NULL && (strcmp(artist_name, "") != 0)) {
gchar **split = g_strsplit(copied_file_name, artist_name, -1); // Split whenever we encounter the artist gchar ** split = g_strsplit(copied_file_name, artist_name, -1); // Split whenever we encounter the artist
copied_file_name = g_strjoinv("", split); // Remove the artist copied_file_name = g_strjoinv("", split); // Remove the artist
g_strfreev(split); g_strfreev(split);
@ -360,13 +387,16 @@ void koto_indexed_track_parse_name(KotoIndexedTrack *self) {
} }
} }
gchar *file_without_ext = koto_utils_get_filename_without_extension(copied_file_name); gchar * file_without_ext = koto_utils_get_filename_without_extension(copied_file_name);
g_free(copied_file_name); g_free(copied_file_name);
gchar **split = g_regex_split_simple("^([\\d]+)", file_without_ext, G_REGEX_JAVASCRIPT_COMPAT, 0); gchar ** split = g_regex_split_simple("^([\\d]+)", file_without_ext, G_REGEX_JAVASCRIPT_COMPAT, 0);
if (g_strv_length(split) > 1) { // Has positional info at the beginning of the file if (g_strv_length(split) > 1) { // Has positional info at the beginning of the file
gchar *num = split[1]; gchar * num = split[1];
g_free(file_without_ext); // Free the prior name g_free(file_without_ext); // Free the prior name
file_without_ext = g_strdup(split[2]); // Set to our second item which is the rest of the song name without the prefixed numbers file_without_ext = g_strdup(split[2]); // Set to our second item which is the rest of the song name without the prefixed numbers
@ -396,19 +426,24 @@ void koto_indexed_track_parse_name(KotoIndexedTrack *self) {
g_free(file_without_ext); g_free(file_without_ext);
} }
void koto_indexed_track_remove_from_playlist(KotoIndexedTrack *self, gchar *playlist_uuid) { void koto_indexed_track_remove_from_playlist(
KotoIndexedTrack * self,
gchar * playlist_uuid
) {
if (!KOTO_IS_INDEXED_TRACK(self)) { if (!KOTO_IS_INDEXED_TRACK(self)) {
return; return;
} }
gchar *commit_op = g_strdup_printf( gchar * commit_op = g_strdup_printf(
"DELETE FROM playlist_tracks WHERE track_id='%s' AND playlist_id='%s'", "DELETE FROM playlist_tracks WHERE track_id='%s' AND playlist_id='%s'",
self->uuid, self->uuid,
playlist_uuid playlist_uuid
); );
gchar *commit_op_errmsg = NULL; gchar * commit_op_errmsg = NULL;
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg); int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_warning("Failed to remove track from playlist: %s", commit_op_errmsg); g_warning("Failed to remove track from playlist: %s", commit_op_errmsg);
} }
@ -417,12 +452,16 @@ void koto_indexed_track_remove_from_playlist(KotoIndexedTrack *self, gchar *play
g_free(commit_op_errmsg); g_free(commit_op_errmsg);
} }
void koto_indexed_track_save_to_playlist(KotoIndexedTrack *self, gchar *playlist_uuid, gint current) { void koto_indexed_track_save_to_playlist(
KotoIndexedTrack * self,
gchar * playlist_uuid,
gint current
) {
if (!KOTO_IS_INDEXED_TRACK(self)) { if (!KOTO_IS_INDEXED_TRACK(self)) {
return; return;
} }
gchar *commit_op = g_strdup_printf( gchar * commit_op = g_strdup_printf(
"INSERT INTO playlist_tracks(playlist_id, track_id, current)" "INSERT INTO playlist_tracks(playlist_id, track_id, current)"
"VALUES('%s', '%s', quote(\"%d\"))", "VALUES('%s', '%s', quote(\"%d\"))",
playlist_uuid, playlist_uuid,
@ -430,9 +469,10 @@ void koto_indexed_track_save_to_playlist(KotoIndexedTrack *self, gchar *playlist
current current
); );
gchar *commit_op_errmsg = NULL; gchar * commit_op_errmsg = NULL;
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg); int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_warning("Failed to save track to playlist: %s", commit_op_errmsg); g_warning("Failed to save track to playlist: %s", commit_op_errmsg);
} }
@ -441,7 +481,10 @@ void koto_indexed_track_save_to_playlist(KotoIndexedTrack *self, gchar *playlist
g_free(commit_op_errmsg); g_free(commit_op_errmsg);
} }
void koto_indexed_track_set_file_name(KotoIndexedTrack *self, gchar *new_file_name) { void koto_indexed_track_set_file_name(
KotoIndexedTrack * self,
gchar * new_file_name
) {
if (new_file_name == NULL) { if (new_file_name == NULL) {
return; return;
} }
@ -462,7 +505,10 @@ void koto_indexed_track_set_file_name(KotoIndexedTrack *self, gchar *new_file_na
} }
} }
void koto_indexed_track_set_cd(KotoIndexedTrack *self, guint cd) { void koto_indexed_track_set_cd(
KotoIndexedTrack * self,
guint cd
) {
if (cd == 0) { // No change really if (cd == 0) { // No change really
return; return;
} }
@ -471,7 +517,10 @@ void koto_indexed_track_set_cd(KotoIndexedTrack *self, guint cd) {
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CD]); g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CD]);
} }
void koto_indexed_track_set_parsed_name(KotoIndexedTrack *self, gchar *new_parsed_name) { void koto_indexed_track_set_parsed_name(
KotoIndexedTrack * self,
gchar * new_parsed_name
) {
if (new_parsed_name == NULL) { if (new_parsed_name == NULL) {
return; return;
} }
@ -488,7 +537,10 @@ void koto_indexed_track_set_parsed_name(KotoIndexedTrack *self, gchar *new_parse
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PARSED_NAME]); g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PARSED_NAME]);
} }
void koto_indexed_track_set_position(KotoIndexedTrack *self, guint pos) { void koto_indexed_track_set_position(
KotoIndexedTrack * self,
guint pos
) {
if (pos == 0) { // No position change really if (pos == 0) { // No position change really
return; return;
} }
@ -497,12 +549,13 @@ void koto_indexed_track_set_position(KotoIndexedTrack *self, guint pos) {
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_POSITION]); g_object_notify_by_pspec(G_OBJECT(self), props[PROP_POSITION]);
} }
void koto_indexed_track_update_metadata(KotoIndexedTrack *self) { void koto_indexed_track_update_metadata(KotoIndexedTrack * self) {
TagLib_File *t_file = taglib_file_new(self->path); // Get a taglib file for this file TagLib_File * t_file = taglib_file_new(self->path); // Get a taglib file for this file
if ((t_file != NULL) && taglib_file_is_valid(t_file)) { // If we got the taglib file and it is valid if ((t_file != NULL) && taglib_file_is_valid(t_file)) { // If we got the taglib file and it is valid
self->acquired_metadata_from_id3 = TRUE; self->acquired_metadata_from_id3 = TRUE;
TagLib_Tag *tag = taglib_file_tag(t_file); // Get our tag TagLib_Tag * tag = taglib_file_tag(t_file); // Get our tag
koto_indexed_track_set_parsed_name(self, taglib_tag_title(tag)); // Set the title of the file koto_indexed_track_set_parsed_name(self, taglib_tag_title(tag)); // Set the title of the file
koto_indexed_track_set_position(self, (uint) taglib_tag_track(tag)); // Get the track, convert to uint and cast as a pointer koto_indexed_track_set_position(self, (uint) taglib_tag_track(tag)); // Get the track, convert to uint and cast as a pointer
koto_indexed_track_set_file_name(self, g_path_get_basename(self->path)); // Update our file name koto_indexed_track_set_file_name(self, g_path_get_basename(self->path)); // Update our file name
@ -514,7 +567,10 @@ void koto_indexed_track_update_metadata(KotoIndexedTrack *self) {
taglib_file_free(t_file); // Free the file taglib_file_free(t_file); // Free the file
} }
void koto_indexed_track_update_path(KotoIndexedTrack *self, const gchar *new_path) { void koto_indexed_track_update_path(
KotoIndexedTrack * self,
const gchar * new_path
) {
if (new_path == NULL) { if (new_path == NULL) {
return; return;
} }
@ -532,29 +588,44 @@ void koto_indexed_track_update_path(KotoIndexedTrack *self, const gchar *new_pat
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]); g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]);
} }
KotoIndexedTrack* koto_indexed_track_new(KotoIndexedAlbum *album, const gchar *path, guint *cd) { KotoIndexedTrack * koto_indexed_track_new(
gchar *artist_uuid; KotoIndexedAlbum * album,
gchar *album_uuid; const gchar * path,
guint * cd
) {
gchar * artist_uuid;
gchar * album_uuid;
g_object_get(album, "artist-uuid", &artist_uuid, "uuid", &album_uuid, NULL); // Get the artist and album uuids from our Album g_object_get(album, "artist-uuid", &artist_uuid, "uuid", &album_uuid, NULL); // Get the artist and album uuids from our Album
KotoIndexedTrack *track = g_object_new(KOTO_TYPE_INDEXED_TRACK, KotoIndexedTrack * track = g_object_new(
"artist-uuid", artist_uuid, KOTO_TYPE_INDEXED_TRACK,
"album-uuid", album_uuid, "artist-uuid",
"do-initial-index", TRUE, artist_uuid,
"uuid", g_uuid_string_random(), "album-uuid",
"path", path, album_uuid,
"cd", cd, "do-initial-index",
TRUE,
"uuid",
g_uuid_string_random(),
"path",
path,
"cd",
cd,
NULL NULL
); );
koto_indexed_track_commit(track); // Immediately commit to the database koto_indexed_track_commit(track); // Immediately commit to the database
return track; return track;
} }
KotoIndexedTrack* koto_indexed_track_new_with_uuid(const gchar *uuid) { KotoIndexedTrack * koto_indexed_track_new_with_uuid(const gchar * uuid) {
return g_object_new(KOTO_TYPE_INDEXED_TRACK, return g_object_new(
"uuid", g_strdup(uuid), KOTO_TYPE_INDEXED_TRACK,
"uuid",
g_strdup(uuid),
NULL NULL
); );
} }

View file

@ -26,7 +26,7 @@ struct _PixbufSize {
typedef struct _PixbufSize PixbufSize; typedef struct _PixbufSize PixbufSize;
static PixbufSize *pixbuf_sizes = NULL; static PixbufSize * pixbuf_sizes = NULL;
static guint pixbuf_sizes_allocated = 0; static guint pixbuf_sizes_allocated = 0;
static void init_pixbuf_sizes() { static void init_pixbuf_sizes() {
@ -62,24 +62,26 @@ enum {
N_BTN_PROPERTIES N_BTN_PROPERTIES
}; };
static GParamSpec *btn_props[N_BTN_PROPERTIES] = { NULL, }; static GParamSpec * btn_props[N_BTN_PROPERTIES] = {
NULL,
};
struct _KotoButton { struct _KotoButton {
GtkBox parent_instance; GtkBox parent_instance;
guint pix_size; guint pix_size;
GtkWidget *button_pic; GtkWidget * button_pic;
GtkWidget *badge_label; GtkWidget * badge_label;
GtkWidget *button_label; GtkWidget * button_label;
GtkGesture *left_click_gesture; GtkGesture * left_click_gesture;
GtkGesture *right_click_gesture; GtkGesture * right_click_gesture;
gchar *image_file_path; gchar * image_file_path;
gchar *badge_text; gchar * badge_text;
gchar *icon_name; gchar * icon_name;
gchar *alt_icon_name; gchar * alt_icon_name;
gchar *text; gchar * text;
KotoButtonImagePosition image_position; KotoButtonImagePosition image_position;
gboolean use_from_file; gboolean use_from_file;
@ -92,12 +94,26 @@ struct _KotoButtonClass {
G_DEFINE_TYPE(KotoButton, koto_button, GTK_TYPE_BOX); G_DEFINE_TYPE(KotoButton, koto_button, GTK_TYPE_BOX);
static void koto_button_constructed(GObject *obj); static void koto_button_constructed(GObject * obj);
static void koto_button_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_button_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); static void koto_button_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_button_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_button_class_init(KotoButtonClass * c) {
GObjectClass * gobject_class;
static void koto_button_class_init(KotoButtonClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->constructed = koto_button_constructed; gobject_class->constructed = koto_button_constructed;
gobject_class->set_property = koto_button_set_property; gobject_class->set_property = koto_button_set_property;
@ -110,7 +126,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
koto_get_pixbuf_size(KOTO_BUTTON_PIXBUF_SIZE_TINY), koto_get_pixbuf_size(KOTO_BUTTON_PIXBUF_SIZE_TINY),
koto_get_pixbuf_size(KOTO_BUTTON_PIXBUF_SIZE_GODLIKE), koto_get_pixbuf_size(KOTO_BUTTON_PIXBUF_SIZE_GODLIKE),
koto_get_pixbuf_size(KOTO_BUTTON_PIXBUF_SIZE_SMALL), koto_get_pixbuf_size(KOTO_BUTTON_PIXBUF_SIZE_SMALL),
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
btn_props[PROP_TEXT] = g_param_spec_string( btn_props[PROP_TEXT] = g_param_spec_string(
@ -118,7 +134,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
"Button Text", "Button Text",
"Text of Button", "Text of Button",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
btn_props[PROP_BADGE_TEXT] = g_param_spec_string( btn_props[PROP_BADGE_TEXT] = g_param_spec_string(
@ -126,7 +142,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
"Badge Text", "Badge Text",
"Text of Badge", "Text of Badge",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
btn_props[PROP_USE_FROM_FILE] = g_param_spec_boolean( btn_props[PROP_USE_FROM_FILE] = g_param_spec_boolean(
@ -134,7 +150,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
"Use from a file / file name", "Use from a file / file name",
"Use from a file / file name", "Use from a file / file name",
FALSE, FALSE,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
btn_props[PROP_IMAGE_FILE_PATH] = g_param_spec_string( btn_props[PROP_IMAGE_FILE_PATH] = g_param_spec_string(
@ -142,7 +158,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
"File path to image", "File path to image",
"File path to image", "File path to image",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
btn_props[PROP_ICON_NAME] = g_param_spec_string( btn_props[PROP_ICON_NAME] = g_param_spec_string(
@ -150,7 +166,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
"Icon Name", "Icon Name",
"Name of Icon", "Name of Icon",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
btn_props[PROP_ALT_ICON_NAME] = g_param_spec_string( btn_props[PROP_ALT_ICON_NAME] = g_param_spec_string(
@ -158,13 +174,13 @@ static void koto_button_class_init(KotoButtonClass *c) {
"Name of an Alternate Icon", "Name of an Alternate Icon",
"Name of an Alternate Icon", "Name of an Alternate Icon",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_BTN_PROPERTIES, btn_props); g_object_class_install_properties(gobject_class, N_BTN_PROPERTIES, btn_props);
} }
static void koto_button_init(KotoButton *self) { static void koto_button_init(KotoButton * self) {
self->currently_showing_alt = FALSE; self->currently_showing_alt = FALSE;
self->image_position = KOTO_BUTTON_IMAGE_POS_LEFT; self->image_position = KOTO_BUTTON_IMAGE_POS_LEFT;
@ -178,16 +194,24 @@ static void koto_button_init(KotoButton *self) {
gtk_widget_add_controller(GTK_WIDGET(self), GTK_EVENT_CONTROLLER(self->right_click_gesture)); // Add our right click gesture gtk_widget_add_controller(GTK_WIDGET(self), GTK_EVENT_CONTROLLER(self->right_click_gesture)); // Add our right click gesture
} }
static void koto_button_constructed(GObject *obj) { static void koto_button_constructed(GObject * obj) {
KotoButton *self = KOTO_BUTTON(obj); KotoButton * self = KOTO_BUTTON(obj);
GtkStyleContext *style = gtk_widget_get_style_context(GTK_WIDGET(self)); GtkStyleContext * style = gtk_widget_get_style_context(GTK_WIDGET(self));
gtk_style_context_add_class(style, "koto-button"); gtk_style_context_add_class(style, "koto-button");
G_OBJECT_CLASS (koto_button_parent_class)->constructed (obj); G_OBJECT_CLASS(koto_button_parent_class)->constructed(obj);
} }
static void koto_button_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_button_get_property(
KotoButton *self = KOTO_BUTTON(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoButton * self = KOTO_BUTTON(obj);
switch (prop_id) { switch (prop_id) {
case PROP_IMAGE_FILE_PATH: case PROP_IMAGE_FILE_PATH:
@ -217,8 +241,14 @@ static void koto_button_get_property(GObject *obj, guint prop_id, GValue *val, G
} }
} }
static void koto_button_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_button_set_property(
KotoButton *self = KOTO_BUTTON(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoButton * self = KOTO_BUTTON(obj);
switch (prop_id) { switch (prop_id) {
case PROP_PIX_SIZE: case PROP_PIX_SIZE:
@ -257,7 +287,12 @@ static void koto_button_set_property(GObject *obj, guint prop_id, const GValue *
} }
} }
void koto_button_add_click_handler(KotoButton *self, KotoButtonClickType button, GCallback handler, gpointer user_data) { void koto_button_add_click_handler(
KotoButton * self,
KotoButtonClickType button,
GCallback handler,
gpointer user_data
) {
if (!KOTO_IS_BUTTON(self)) { if (!KOTO_IS_BUTTON(self)) {
return; return;
} }
@ -269,7 +304,7 @@ void koto_button_add_click_handler(KotoButton *self, KotoButtonClickType button,
g_signal_connect((button == KOTO_BUTTON_CLICK_TYPE_PRIMARY) ? self->left_click_gesture : self->right_click_gesture, "pressed", handler, user_data); g_signal_connect((button == KOTO_BUTTON_CLICK_TYPE_PRIMARY) ? self->left_click_gesture : self->right_click_gesture, "pressed", handler, user_data);
} }
void koto_button_flip(KotoButton *self) { void koto_button_flip(KotoButton * self) {
if (!KOTO_IS_BUTTON(self)) { if (!KOTO_IS_BUTTON(self)) {
return; return;
} }
@ -277,13 +312,16 @@ void koto_button_flip(KotoButton *self) {
koto_button_show_image(self, !self->currently_showing_alt); koto_button_show_image(self, !self->currently_showing_alt);
} }
void koto_button_hide_image(KotoButton *self) { void koto_button_hide_image(KotoButton * self) {
if (GTK_IS_WIDGET(self->button_pic)) { // Is a widget if (GTK_IS_WIDGET(self->button_pic)) { // Is a widget
gtk_widget_hide(self->button_pic); gtk_widget_hide(self->button_pic);
} }
} }
void koto_button_set_badge_text(KotoButton *self, gchar *text) { void koto_button_set_badge_text(
KotoButton * self,
gchar * text
) {
if ((text == NULL) || (strcmp(text, "") == 0)) { // If the text is empty if ((text == NULL) || (strcmp(text, "") == 0)) { // If the text is empty
self->badge_text = g_strdup(""); self->badge_text = g_strdup("");
} else { } else {
@ -307,7 +345,10 @@ void koto_button_set_badge_text(KotoButton *self, gchar *text) {
g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_BADGE_TEXT]); g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_BADGE_TEXT]);
} }
void koto_button_set_file_path(KotoButton *self, gchar *file_path) { void koto_button_set_file_path(
KotoButton * self,
gchar * file_path
) {
if (!KOTO_IS_BUTTON(self)) { // Not a button if (!KOTO_IS_BUTTON(self)) { // Not a button
return; return;
} }
@ -324,8 +365,13 @@ void koto_button_set_file_path(KotoButton *self, gchar *file_path) {
koto_button_show_image(self, FALSE); koto_button_show_image(self, FALSE);
} }
void koto_button_set_icon_name(KotoButton *self, gchar *icon_name, gboolean for_alt) { void koto_button_set_icon_name(
gchar *copied_icon_name = g_strdup(icon_name); KotoButton * self,
gchar * icon_name,
gboolean for_alt
) {
gchar * copied_icon_name = g_strdup(icon_name);
if (for_alt) { // Is for the alternate icon if (for_alt) { // Is for the alternate icon
if ((self->alt_icon_name != NULL) && strcmp(icon_name, self->alt_icon_name) != 0) { // If the icons are different if ((self->alt_icon_name != NULL) && strcmp(icon_name, self->alt_icon_name) != 0) { // If the icons are different
@ -342,9 +388,11 @@ void koto_button_set_icon_name(KotoButton *self, gchar *icon_name, gboolean for_
} }
gboolean hide_image = FALSE; gboolean hide_image = FALSE;
if (for_alt && self->currently_showing_alt && ((self->alt_icon_name == NULL) || strcmp(self->alt_icon_name, "") == 0)) { // For alt, alt is currently showing, and no longer have alt if (for_alt && self->currently_showing_alt && ((self->alt_icon_name == NULL) || strcmp(self->alt_icon_name, "") == 0)) { // For alt, alt is currently showing, and no longer have alt
hide_image = TRUE; hide_image = TRUE;
} else if (!for_alt && ((self->icon_name == NULL) || (strcmp(self->icon_name,"") == 0))) { // Not for alt, no icon } else if (!for_alt && ((self->icon_name == NULL) || (strcmp(self->icon_name, "") == 0))) { // Not for alt, no icon
hide_image = TRUE; hide_image = TRUE;
} }
@ -359,7 +407,10 @@ void koto_button_set_icon_name(KotoButton *self, gchar *icon_name, gboolean for_
g_object_notify_by_pspec(G_OBJECT(self), for_alt ? btn_props[PROP_ALT_ICON_NAME] : btn_props[PROP_ICON_NAME]); g_object_notify_by_pspec(G_OBJECT(self), for_alt ? btn_props[PROP_ALT_ICON_NAME] : btn_props[PROP_ICON_NAME]);
} }
void koto_button_set_image_position(KotoButton *self, KotoButtonImagePosition pos) { void koto_button_set_image_position(
KotoButton * self,
KotoButtonImagePosition pos
) {
if (self->image_position == pos) { // Is a different position that currently if (self->image_position == pos) { // Is a different position that currently
return; return;
} }
@ -375,7 +426,10 @@ void koto_button_set_image_position(KotoButton *self, KotoButtonImagePosition po
self->image_position = pos; self->image_position = pos;
} }
void koto_button_set_pixbuf_size(KotoButton *self, guint size) { void koto_button_set_pixbuf_size(
KotoButton * self,
guint size
) {
if (size == self->pix_size) { if (size == self->pix_size) {
return; return;
} }
@ -386,7 +440,10 @@ void koto_button_set_pixbuf_size(KotoButton *self, guint size) {
g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_PIX_SIZE]); g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_PIX_SIZE]);
} }
void koto_button_set_text(KotoButton *self, gchar *text) { void koto_button_set_text(
KotoButton * self,
gchar * text
) {
if (text == NULL) { if (text == NULL) {
return; return;
} }
@ -421,7 +478,10 @@ void koto_button_set_text(KotoButton *self, gchar *text) {
g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_TEXT]); g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_TEXT]);
} }
void koto_button_show_image(KotoButton *self, gboolean use_alt) { void koto_button_show_image(
KotoButton * self,
gboolean use_alt
) {
if (!KOTO_IS_BUTTON(self)) { if (!KOTO_IS_BUTTON(self)) {
return; return;
} }
@ -446,7 +506,7 @@ void koto_button_show_image(KotoButton *self, gboolean use_alt) {
} }
self->currently_showing_alt = use_alt; self->currently_showing_alt = use_alt;
gchar *name = use_alt ? self->alt_icon_name : self->icon_name; gchar * name = use_alt ? self->alt_icon_name : self->icon_name;
if (GTK_IS_IMAGE(self->button_pic)) { if (GTK_IS_IMAGE(self->button_pic)) {
gtk_image_set_from_icon_name(GTK_IMAGE(self->button_pic), name); // Just update the existing iamge gtk_image_set_from_icon_name(GTK_IMAGE(self->button_pic), name); // Just update the existing iamge
@ -461,7 +521,7 @@ void koto_button_show_image(KotoButton *self, gboolean use_alt) {
gtk_widget_show(self->button_pic); // Ensure we actually are showing the image gtk_widget_show(self->button_pic); // Ensure we actually are showing the image
} }
void koto_button_unflatten(KotoButton *self) { void koto_button_unflatten(KotoButton * self) {
if (!KOTO_IS_BUTTON(self)) { if (!KOTO_IS_BUTTON(self)) {
return; return;
} }
@ -469,26 +529,44 @@ void koto_button_unflatten(KotoButton *self) {
gtk_widget_remove_css_class(GTK_WIDGET(self), "flat"); gtk_widget_remove_css_class(GTK_WIDGET(self), "flat");
} }
KotoButton* koto_button_new_plain(gchar *label) { KotoButton * koto_button_new_plain(gchar * label) {
return g_object_new(KOTO_TYPE_BUTTON, return g_object_new(
"button-text", label, KOTO_TYPE_BUTTON,
"button-text",
label,
NULL NULL
); );
} }
KotoButton* koto_button_new_with_icon(gchar *label, gchar *icon_name, gchar *alt_icon_name, KotoButtonPixbufSize size) { KotoButton * koto_button_new_with_icon(
return g_object_new(KOTO_TYPE_BUTTON, gchar * label,
"button-text", label, gchar * icon_name,
"icon-name", icon_name, gchar * alt_icon_name,
"alt-icon-name", alt_icon_name, KotoButtonPixbufSize size
"pixbuf-size", koto_get_pixbuf_size(size), ) {
return g_object_new(
KOTO_TYPE_BUTTON,
"button-text",
label,
"icon-name",
icon_name,
"alt-icon-name",
alt_icon_name,
"pixbuf-size",
koto_get_pixbuf_size(size),
NULL NULL
); );
} }
KotoButton *koto_button_new_with_file(gchar *label, gchar *file_path, KotoButtonPixbufSize size) { KotoButton * koto_button_new_with_file(
return g_object_new(KOTO_TYPE_BUTTON, gchar * label,
"button-text", label, gchar * file_path,
KotoButtonPixbufSize size
) {
return g_object_new(
KOTO_TYPE_BUTTON,
"button-text",
label,
"use-from-file", "use-from-file",
TRUE, TRUE,
"image-file-path", "image-file-path",

View file

@ -46,26 +46,78 @@ typedef enum {
#define NUM_BUILTIN_SIZES 7 #define NUM_BUILTIN_SIZES 7
#define KOTO_TYPE_BUTTON (koto_button_get_type()) #define KOTO_TYPE_BUTTON (koto_button_get_type())
G_DECLARE_FINAL_TYPE (KotoButton, koto_button, KOTO, BUTTON, GtkBox) G_DECLARE_FINAL_TYPE(KotoButton, koto_button, KOTO, BUTTON, GtkBox)
#define KOTO_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_BUTTON)) #define KOTO_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_BUTTON))
guint koto_get_pixbuf_size(KotoButtonPixbufSize size); guint koto_get_pixbuf_size(KotoButtonPixbufSize size);
KotoButton* koto_button_new_plain(gchar *label); KotoButton * koto_button_new_plain(gchar * label);
KotoButton* koto_button_new_with_icon(gchar *label, gchar *icon_name, gchar *alt_icon_name, KotoButtonPixbufSize size);
KotoButton *koto_button_new_with_file(gchar *label, gchar *file_path, KotoButtonPixbufSize size);
void koto_button_add_click_handler(KotoButton *self, KotoButtonClickType button, GCallback handler, gpointer user_data); KotoButton * koto_button_new_with_icon(
void koto_button_flip(KotoButton *self); gchar * label,
void koto_button_hide_image(KotoButton *self); gchar * icon_name,
void koto_button_set_badge_text(KotoButton *self, gchar *text); gchar * alt_icon_name,
void koto_button_set_file_path(KotoButton *self, gchar *file_path); KotoButtonPixbufSize size
void koto_button_set_icon_name(KotoButton *self, gchar *icon_name, gboolean for_alt); );
void koto_button_set_image_position(KotoButton *self, KotoButtonImagePosition pos);
void koto_button_set_pixbuf(KotoButton *self, GdkPixbuf *pix); KotoButton * koto_button_new_with_file(
void koto_button_set_pixbuf_size(KotoButton *self, guint size); gchar * label,
void koto_button_set_text(KotoButton *self, gchar *text); gchar * file_path,
void koto_button_show_image(KotoButton *self, gboolean use_alt); KotoButtonPixbufSize size
void koto_button_unflatten(KotoButton *self); );
void koto_button_add_click_handler(
KotoButton * self,
KotoButtonClickType button,
GCallback handler,
gpointer user_data
);
void koto_button_flip(KotoButton * self);
void koto_button_hide_image(KotoButton * self);
void koto_button_set_badge_text(
KotoButton * self,
gchar * text
);
void koto_button_set_file_path(
KotoButton * self,
gchar * file_path
);
void koto_button_set_icon_name(
KotoButton * self,
gchar * icon_name,
gboolean for_alt
);
void koto_button_set_image_position(
KotoButton * self,
KotoButtonImagePosition pos
);
void koto_button_set_pixbuf(
KotoButton * self,
GdkPixbuf * pix
);
void koto_button_set_pixbuf_size(
KotoButton * self,
guint size
);
void koto_button_set_text(
KotoButton * self,
gchar * text
);
void koto_button_show_image(
KotoButton * self,
gboolean use_alt
);
void koto_button_unflatten(KotoButton * self);
G_END_DECLS G_END_DECLS

View file

@ -22,25 +22,20 @@
struct _KotoDialogContainer { struct _KotoDialogContainer {
GtkBox parent_instance; GtkBox parent_instance;
KotoButton *close_button; KotoButton * close_button;
GtkWidget *dialogs; GtkWidget * dialogs;
}; };
G_DEFINE_TYPE(KotoDialogContainer, koto_dialog_container, GTK_TYPE_BOX); G_DEFINE_TYPE(KotoDialogContainer, koto_dialog_container, GTK_TYPE_BOX);
static void koto_dialog_container_class_init(KotoDialogContainerClass *c) { static void koto_dialog_container_class_init(KotoDialogContainerClass * c) {
(void) c; (void) c;
} }
static void koto_dialog_container_init(KotoDialogContainer *self) { static void koto_dialog_container_init(KotoDialogContainer * self) {
gtk_widget_add_css_class(GTK_WIDGET(self), "koto-dialog-container"); gtk_widget_add_css_class(GTK_WIDGET(self), "koto-dialog-container");
g_object_set(GTK_WIDGET(self), g_object_set(GTK_WIDGET(self), "hexpand", TRUE, "vexpand", TRUE, NULL);
"hexpand",
TRUE,
"vexpand",
TRUE,
NULL);
self->close_button = koto_button_new_with_icon(NULL, "window-close-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_LARGE); self->close_button = koto_button_new_with_icon(NULL, "window-close-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_LARGE);
gtk_widget_set_halign(GTK_WIDGET(self->close_button), GTK_ALIGN_END); gtk_widget_set_halign(GTK_WIDGET(self->close_button), GTK_ALIGN_END);
@ -60,7 +55,11 @@ static void koto_dialog_container_init(KotoDialogContainer *self) {
gtk_widget_hide(GTK_WIDGET(self)); // Hide by default gtk_widget_hide(GTK_WIDGET(self)); // Hide by default
} }
void koto_dialog_container_add_dialog(KotoDialogContainer *self, gchar *dialog_name, GtkWidget *dialog) { void koto_dialog_container_add_dialog(
KotoDialogContainer * self,
gchar * dialog_name,
GtkWidget * dialog
) {
if (!KOTO_IS_DIALOG_CONTAINER(self)) { // Not a dialog container if (!KOTO_IS_DIALOG_CONTAINER(self)) { // Not a dialog container
return; return;
} }
@ -68,12 +67,21 @@ void koto_dialog_container_add_dialog(KotoDialogContainer *self, gchar *dialog_n
gtk_stack_add_named(GTK_STACK(self->dialogs), dialog, dialog_name); // Add the dialog to the stack gtk_stack_add_named(GTK_STACK(self->dialogs), dialog, dialog_name); // Add the dialog to the stack
} }
void koto_dialog_container_handle_close_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_dialog_container_handle_close_click(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
koto_dialog_container_hide((KotoDialogContainer*) user_data); koto_dialog_container_hide((KotoDialogContainer*) user_data);
} }
void koto_dialog_container_hide(KotoDialogContainer *self) { void koto_dialog_container_hide(KotoDialogContainer * self) {
if (!KOTO_IS_DIALOG_CONTAINER(self)) { // Not a dialog container if (!KOTO_IS_DIALOG_CONTAINER(self)) { // Not a dialog container
return; return;
} }
@ -81,7 +89,10 @@ void koto_dialog_container_hide(KotoDialogContainer *self) {
gtk_widget_hide(GTK_WIDGET(self)); gtk_widget_hide(GTK_WIDGET(self));
} }
void koto_dialog_container_show_dialog(KotoDialogContainer *self, gchar *dialog_name) { void koto_dialog_container_show_dialog(
KotoDialogContainer * self,
gchar * dialog_name
) {
if (!KOTO_IS_DIALOG_CONTAINER(self)) { // Not a dialog container if (!KOTO_IS_DIALOG_CONTAINER(self)) { // Not a dialog container
return; return;
} }
@ -90,8 +101,9 @@ void koto_dialog_container_show_dialog(KotoDialogContainer *self, gchar *dialog_
gtk_widget_show(GTK_WIDGET(self)); // Ensure we show self gtk_widget_show(GTK_WIDGET(self)); // Ensure we show self
} }
KotoDialogContainer* koto_dialog_container_new() { KotoDialogContainer * koto_dialog_container_new() {
return g_object_new(KOTO_TYPE_DIALOG_CONTAINER, return g_object_new(
KOTO_TYPE_DIALOG_CONTAINER,
"orientation", "orientation",
GTK_ORIENTATION_VERTICAL, GTK_ORIENTATION_VERTICAL,
NULL NULL

View file

@ -23,7 +23,7 @@ G_BEGIN_DECLS
/** /**
* Type Definition * Type Definition
**/ **/
#define KOTO_TYPE_DIALOG_CONTAINER koto_dialog_container_get_type() #define KOTO_TYPE_DIALOG_CONTAINER koto_dialog_container_get_type()
G_DECLARE_FINAL_TYPE(KotoDialogContainer, koto_dialog_container, KOTO, DIALOG_CONTAINER, GtkBox); G_DECLARE_FINAL_TYPE(KotoDialogContainer, koto_dialog_container, KOTO, DIALOG_CONTAINER, GtkBox);
@ -31,12 +31,29 @@ G_DECLARE_FINAL_TYPE(KotoDialogContainer, koto_dialog_container, KOTO, DIALOG_CO
/** /**
* Functions * Functions
**/ **/
KotoDialogContainer* koto_dialog_container_new(); KotoDialogContainer * koto_dialog_container_new();
void koto_dialog_container_add_dialog(KotoDialogContainer *self, gchar *dialog_name, GtkWidget *dialog);
void koto_dialog_container_handle_close_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data); void koto_dialog_container_add_dialog(
void koto_dialog_container_hide(KotoDialogContainer *self); KotoDialogContainer * self,
void koto_dialog_container_show_dialog(KotoDialogContainer *self, gchar *dialog_name); gchar * dialog_name,
GtkWidget * dialog
);
void koto_dialog_container_handle_close_click(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_dialog_container_hide(KotoDialogContainer * self);
void koto_dialog_container_show_dialog(
KotoDialogContainer * self,
gchar * dialog_name
);
G_END_DECLS G_END_DECLS

View file

@ -30,22 +30,24 @@ enum {
N_EXP_PROPERTIES N_EXP_PROPERTIES
}; };
static GParamSpec *expander_props[N_EXP_PROPERTIES] = { NULL, }; static GParamSpec * expander_props[N_EXP_PROPERTIES] = {
NULL,
};
struct _KotoExpander { struct _KotoExpander {
GtkBox parent_instance; GtkBox parent_instance;
gboolean constructed; gboolean constructed;
GtkWidget *header; GtkWidget * header;
KotoButton *header_button; KotoButton * header_button;
gchar *icon_name; gchar * icon_name;
gchar *label; gchar * label;
KotoButton *header_secondary_button; KotoButton * header_secondary_button;
KotoButton *header_expand_button; KotoButton * header_expand_button;
GtkWidget *revealer; GtkWidget * revealer;
GtkWidget *content; GtkWidget * content;
}; };
struct _KotoExpanderClass { struct _KotoExpanderClass {
@ -54,11 +56,24 @@ struct _KotoExpanderClass {
G_DEFINE_TYPE(KotoExpander, koto_expander, GTK_TYPE_BOX); G_DEFINE_TYPE(KotoExpander, koto_expander, GTK_TYPE_BOX);
static void koto_expander_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); static void koto_expander_get_property(
static void koto_expander_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_expander_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_expander_class_init(KotoExpanderClass * c) {
GObjectClass * gobject_class = G_OBJECT_CLASS(c);
static void koto_expander_class_init(KotoExpanderClass *c) {
GObjectClass *gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_expander_set_property; gobject_class->set_property = koto_expander_set_property;
gobject_class->get_property = koto_expander_get_property; gobject_class->get_property = koto_expander_get_property;
@ -67,7 +82,7 @@ static void koto_expander_class_init(KotoExpanderClass *c) {
"Icon Name", "Icon Name",
"Name of the icon to use in the Expander", "Name of the icon to use in the Expander",
"emblem-favorite-symbolic", "emblem-favorite-symbolic",
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
expander_props[PROP_HEADER_LABEL] = g_param_spec_string( expander_props[PROP_HEADER_LABEL] = g_param_spec_string(
@ -75,7 +90,7 @@ static void koto_expander_class_init(KotoExpanderClass *c) {
"Label", "Label",
"Label for the Expander", "Label for the Expander",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
expander_props[PROP_HEADER_SECONDARY_BUTTON] = g_param_spec_object( expander_props[PROP_HEADER_SECONDARY_BUTTON] = g_param_spec_object(
@ -83,7 +98,7 @@ static void koto_expander_class_init(KotoExpanderClass *c) {
"Secondary Button", "Secondary Button",
"Secondary Button to be placed next to Expander button", "Secondary Button to be placed next to Expander button",
KOTO_TYPE_BUTTON, KOTO_TYPE_BUTTON,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
expander_props[PROP_CONTENT] = g_param_spec_object( expander_props[PROP_CONTENT] = g_param_spec_object(
@ -91,14 +106,20 @@ static void koto_expander_class_init(KotoExpanderClass *c) {
"Content", "Content",
"Content inside the Expander", "Content inside the Expander",
GTK_TYPE_WIDGET, GTK_TYPE_WIDGET,
G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_EXP_PROPERTIES, expander_props); g_object_class_install_properties(gobject_class, N_EXP_PROPERTIES, expander_props);
} }
static void koto_expander_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_expander_get_property(
KotoExpander *self = KOTO_EXPANDER(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoExpander * self = KOTO_EXPANDER(obj);
switch (prop_id) { switch (prop_id) {
case PROP_HEADER_ICON_NAME: case PROP_HEADER_ICON_NAME:
@ -108,10 +129,10 @@ static void koto_expander_get_property(GObject *obj, guint prop_id, GValue *val,
g_value_set_string(val, self->label); g_value_set_string(val, self->label);
break; break;
case PROP_HEADER_SECONDARY_BUTTON: case PROP_HEADER_SECONDARY_BUTTON:
g_value_set_object(val, (GObject *) self->header_secondary_button); g_value_set_object(val, (GObject*) self->header_secondary_button);
break; break;
case PROP_CONTENT: case PROP_CONTENT:
g_value_set_object(val, (GObject *) self->content); g_value_set_object(val, (GObject*) self->content);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
@ -119,11 +140,17 @@ static void koto_expander_get_property(GObject *obj, guint prop_id, GValue *val,
} }
} }
static void koto_expander_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_expander_set_property(
KotoExpander *self = KOTO_EXPANDER(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoExpander * self = KOTO_EXPANDER(obj);
if (!GTK_IS_WIDGET(self->header_button)) { // Header Button is not a widget if (!GTK_IS_WIDGET(self->header_button)) { // Header Button is not a widget
KotoButton *new_button = koto_button_new_with_icon(NULL, "emblem-favorite-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); KotoButton * new_button = koto_button_new_with_icon(NULL, "emblem-favorite-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL);
if (GTK_IS_WIDGET(new_button)) { // Created our widget successfully if (GTK_IS_WIDGET(new_button)) { // Created our widget successfully
self->header_button = new_button; self->header_button = new_button;
@ -153,14 +180,18 @@ static void koto_expander_set_property(GObject *obj, guint prop_id, const GValue
} }
} }
static void koto_expander_init(KotoExpander *self) { static void koto_expander_init(KotoExpander * self) {
GtkStyleContext *style = gtk_widget_get_style_context(GTK_WIDGET(self)); GtkStyleContext * style = gtk_widget_get_style_context(GTK_WIDGET(self));
gtk_style_context_add_class(style, "expander"); gtk_style_context_add_class(style, "expander");
gtk_widget_set_hexpand((GTK_WIDGET(self)), TRUE); gtk_widget_set_hexpand((GTK_WIDGET(self)), TRUE);
self->header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); self->header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
GtkStyleContext *header_style = gtk_widget_get_style_context(self->header); GtkStyleContext * header_style = gtk_widget_get_style_context(self->header);
gtk_style_context_add_class(header_style, "expander-header"); gtk_style_context_add_class(header_style, "expander-header");
self->revealer = gtk_revealer_new(); self->revealer = gtk_revealer_new();
@ -178,7 +209,10 @@ static void koto_expander_init(KotoExpander *self) {
koto_button_add_click_handler(self->header_expand_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_expander_toggle_content), self); koto_button_add_click_handler(self->header_expand_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_expander_toggle_content), self);
} }
void koto_expander_set_secondary_button(KotoExpander *self, KotoButton *new_button) { void koto_expander_set_secondary_button(
KotoExpander * self,
KotoButton * new_button
) {
if (!self->constructed) { if (!self->constructed) {
return; return;
} }
@ -197,7 +231,10 @@ void koto_expander_set_secondary_button(KotoExpander *self, KotoButton *new_butt
g_object_notify_by_pspec(G_OBJECT(self), expander_props[PROP_HEADER_SECONDARY_BUTTON]); g_object_notify_by_pspec(G_OBJECT(self), expander_props[PROP_HEADER_SECONDARY_BUTTON]);
} }
void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content) { void koto_expander_set_content(
KotoExpander * self,
GtkWidget * new_content
) {
if (!self->constructed) { if (!self->constructed) {
return; return;
} }
@ -213,34 +250,62 @@ void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content) {
g_object_notify_by_pspec(G_OBJECT(self), expander_props[PROP_CONTENT]); g_object_notify_by_pspec(G_OBJECT(self), expander_props[PROP_CONTENT]);
} }
GtkWidget* koto_expander_get_content(KotoExpander *self) { GtkWidget * koto_expander_get_content(KotoExpander * self) {
return self->content; return self->content;
} }
void koto_expander_toggle_content(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_expander_toggle_content(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoExpander* self = data; KotoExpander* self = data;
koto_button_flip(KOTO_BUTTON(self->header_expand_button)); koto_button_flip(KOTO_BUTTON(self->header_expand_button));
GtkRevealer* rev = GTK_REVEALER(self->revealer); GtkRevealer* rev = GTK_REVEALER(self->revealer);
gtk_revealer_set_reveal_child(rev, !gtk_revealer_get_reveal_child(rev)); // Invert our values gtk_revealer_set_reveal_child(rev, !gtk_revealer_get_reveal_child(rev)); // Invert our values
} }
KotoExpander* koto_expander_new(gchar *primary_icon_name, gchar *primary_label_text) { KotoExpander * koto_expander_new(
return g_object_new(KOTO_TYPE_EXPANDER, gchar * primary_icon_name,
"orientation", GTK_ORIENTATION_VERTICAL, gchar * primary_label_text
"icon-name", primary_icon_name, ) {
"label", primary_label_text, return g_object_new(
KOTO_TYPE_EXPANDER,
"orientation",
GTK_ORIENTATION_VERTICAL,
"icon-name",
primary_icon_name,
"label",
primary_label_text,
NULL NULL
); );
} }
KotoExpander* koto_expander_new_with_button(gchar *primary_icon_name, gchar *primary_label_text, KotoButton *secondary_button) { KotoExpander * koto_expander_new_with_button(
return g_object_new(KOTO_TYPE_EXPANDER, gchar * primary_icon_name,
"orientation", GTK_ORIENTATION_VERTICAL, gchar * primary_label_text,
"icon-name", primary_icon_name, KotoButton * secondary_button
"label", primary_label_text, ) {
"secondary-button", secondary_button, return g_object_new(
KOTO_TYPE_EXPANDER,
"orientation",
GTK_ORIENTATION_VERTICAL,
"icon-name",
primary_icon_name,
"label",
primary_label_text,
"secondary-button",
secondary_button,
NULL NULL
); );
} }

View file

@ -23,16 +23,44 @@
G_BEGIN_DECLS G_BEGIN_DECLS
#define KOTO_TYPE_EXPANDER (koto_expander_get_type()) #define KOTO_TYPE_EXPANDER (koto_expander_get_type())
G_DECLARE_FINAL_TYPE (KotoExpander, koto_expander, KOTO, EXPANDER, GtkBox) G_DECLARE_FINAL_TYPE(KotoExpander, koto_expander, KOTO, EXPANDER, GtkBox)
#define KOTO_IS_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_EXPANDER)) #define KOTO_IS_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_EXPANDER))
KotoExpander* koto_expander_new(gchar *primary_icon_name, gchar *primary_label_text); KotoExpander * koto_expander_new(gchar * primary_icon_name, gchar * primary_label_text);
KotoExpander* koto_expander_new_with_button(gchar *primary_icon_name, gchar *primary_label_text, KotoButton *secondary_button); KotoExpander * koto_expander_new_with_button(
GtkWidget* koto_expander_get_content(KotoExpander *self); gchar * primary_icon_name,
void koto_expander_set_icon_name(KotoExpander *self, const gchar *in); gchar * primary_label_text,
void koto_expander_set_label(KotoExpander *self, const gchar *label); KotoButton * secondary_button
void koto_expander_set_secondary_button(KotoExpander *self, KotoButton *new_button); );
void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content);
void koto_expander_toggle_content(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); GtkWidget * koto_expander_get_content(KotoExpander * self);
void koto_expander_set_icon_name(
KotoExpander * self,
const gchar* in
);
void koto_expander_set_label(
KotoExpander * self,
const gchar * label
);
void koto_expander_set_secondary_button(
KotoExpander * self,
KotoButton * new_button
);
void koto_expander_set_content(
KotoExpander * self,
GtkWidget * new_content
);
void koto_expander_toggle_content(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
G_END_DECLS G_END_DECLS

View file

@ -26,39 +26,39 @@
#include "koto-utils.h" #include "koto-utils.h"
#include "koto-window.h" #include "koto-window.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoWindow *main_window; extern KotoWindow * main_window;
struct _KotoNav { struct _KotoNav {
GObject parent_instance; GObject parent_instance;
GtkWidget *win; GtkWidget * win;
GtkWidget *content; GtkWidget * content;
KotoButton *home_button; KotoButton * home_button;
KotoExpander *audiobook_expander; KotoExpander * audiobook_expander;
KotoExpander *music_expander; KotoExpander * music_expander;
KotoExpander *podcast_expander; KotoExpander * podcast_expander;
KotoExpander *playlists_expander; KotoExpander * playlists_expander;
// Audiobooks // Audiobooks
KotoButton *audiobooks_local; KotoButton * audiobooks_local;
KotoButton *audiobooks_audible; KotoButton * audiobooks_audible;
KotoButton *audiobooks_librivox; KotoButton * audiobooks_librivox;
// Music // Music
KotoButton *music_local; KotoButton * music_local;
KotoButton *music_radio; KotoButton * music_radio;
// Playlists // Playlists
GHashTable *playlist_buttons; GHashTable * playlist_buttons;
// Podcasts // Podcasts
KotoButton *podcasts_local; KotoButton * podcasts_local;
KotoButton *podcasts_discover; KotoButton * podcasts_discover;
}; };
struct _KotoNavClass { struct _KotoNavClass {
@ -67,11 +67,11 @@ struct _KotoNavClass {
G_DEFINE_TYPE(KotoNav, koto_nav, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoNav, koto_nav, G_TYPE_OBJECT);
static void koto_nav_class_init(KotoNavClass *c) { static void koto_nav_class_init(KotoNavClass * c) {
(void) c; (void) c;
} }
static void koto_nav_init(KotoNav *self) { static void koto_nav_init(KotoNav * self) {
self->playlist_buttons = g_hash_table_new(g_str_hash, g_str_equal); self->playlist_buttons = g_hash_table_new(g_str_hash, g_str_equal);
self->win = gtk_scrolled_window_new(); self->win = gtk_scrolled_window_new();
gtk_widget_set_hexpand_set(self->win, TRUE); // using hexpand-set works, hexpand seems to break it by causing it to take up way too much space gtk_widget_set_hexpand_set(self->win, TRUE); // using hexpand-set works, hexpand seems to break it by causing it to take up way too much space
@ -88,7 +88,8 @@ static void koto_nav_init(KotoNav *self) {
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->win), self->content); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->win), self->content);
KotoButton *h_button = koto_button_new_with_icon("Home", "user-home-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); KotoButton * h_button = koto_button_new_with_icon("Home", "user-home-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL);
if (h_button != NULL) { if (h_button != NULL) {
self->home_button = h_button; self->home_button = h_button;
@ -101,13 +102,16 @@ static void koto_nav_init(KotoNav *self) {
koto_nav_create_playlist_section(self); koto_nav_create_playlist_section(self);
} }
void koto_nav_create_audiobooks_section(KotoNav *self) { void koto_nav_create_audiobooks_section(KotoNav * self) {
KotoExpander *a_expander = koto_expander_new("ephy-bookmarks-symbolic", "Audiobooks"); KotoExpander * a_expander = koto_expander_new("ephy-bookmarks-symbolic", "Audiobooks");
self->audiobook_expander = a_expander; self->audiobook_expander = a_expander;
gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->audiobook_expander)); gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->audiobook_expander));
GtkWidget *new_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); GtkWidget * new_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
koto_expander_set_content(a_expander, new_content); koto_expander_set_content(a_expander, new_content);
self->audiobooks_local = koto_button_new_plain("Local Library"); self->audiobooks_local = koto_button_new_plain("Local Library");
@ -119,12 +123,15 @@ void koto_nav_create_audiobooks_section(KotoNav *self) {
gtk_box_append(GTK_BOX(new_content), GTK_WIDGET(self->audiobooks_librivox)); gtk_box_append(GTK_BOX(new_content), GTK_WIDGET(self->audiobooks_librivox));
} }
void koto_nav_create_music_section(KotoNav *self) { void koto_nav_create_music_section(KotoNav * self) {
KotoExpander *m_expander = koto_expander_new("emblem-music-symbolic", "Music"); KotoExpander * m_expander = koto_expander_new("emblem-music-symbolic", "Music");
self->music_expander = m_expander; self->music_expander = m_expander;
gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->music_expander)); gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->music_expander));
GtkWidget *new_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); GtkWidget * new_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
self->music_local = koto_button_new_plain("Local Library"); self->music_local = koto_button_new_plain("Local Library");
self->music_radio = koto_button_new_plain("Radio"); self->music_radio = koto_button_new_plain("Radio");
@ -136,15 +143,17 @@ void koto_nav_create_music_section(KotoNav *self) {
koto_button_add_click_handler(self->music_local, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_nav_handle_local_music_click), NULL); koto_button_add_click_handler(self->music_local, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_nav_handle_local_music_click), NULL);
} }
void koto_nav_create_playlist_section(KotoNav *self) { void koto_nav_create_playlist_section(KotoNav * self) {
KotoButton *playlist_add_button = koto_button_new_with_icon("", "list-add-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); KotoButton * playlist_add_button = koto_button_new_with_icon("", "list-add-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL);
KotoExpander *pl_expander = koto_expander_new_with_button("playlist-symbolic", "Playlists", playlist_add_button); KotoExpander * pl_expander = koto_expander_new_with_button("playlist-symbolic", "Playlists", playlist_add_button);
self->playlists_expander = pl_expander; self->playlists_expander = pl_expander;
gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->playlists_expander)); gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->playlists_expander));
// TODO: Turn into ListBox to sort playlists // TODO: Turn into ListBox to sort playlists
GtkWidget *playlist_list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); GtkWidget * playlist_list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
koto_expander_set_content(self->playlists_expander, playlist_list); koto_expander_set_content(self->playlists_expander, playlist_list);
koto_button_add_click_handler(playlist_add_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_nav_handle_playlist_add_click), NULL); koto_button_add_click_handler(playlist_add_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_nav_handle_playlist_add_click), NULL);
@ -153,12 +162,15 @@ void koto_nav_create_playlist_section(KotoNav *self) {
g_signal_connect(koto_maps, "playlist-removed", G_CALLBACK(koto_nav_handle_playlist_removed), self); g_signal_connect(koto_maps, "playlist-removed", G_CALLBACK(koto_nav_handle_playlist_removed), self);
} }
void koto_nav_create_podcasts_section(KotoNav *self) { void koto_nav_create_podcasts_section(KotoNav * self) {
KotoExpander *p_expander = koto_expander_new("microphone-sensitivity-high-symbolic", "Podcasts"); KotoExpander * p_expander = koto_expander_new("microphone-sensitivity-high-symbolic", "Podcasts");
self->podcast_expander = p_expander; self->podcast_expander = p_expander;
gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->podcast_expander)); gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->podcast_expander));
GtkWidget *new_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); GtkWidget * new_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
self->podcasts_local = koto_button_new_plain("Library"); self->podcasts_local = koto_button_new_plain("Library");
self->podcasts_discover = koto_button_new_plain("Find New Podcasts"); self->podcasts_discover = koto_button_new_plain("Find New Podcasts");
@ -169,43 +181,82 @@ void koto_nav_create_podcasts_section(KotoNav *self) {
koto_expander_set_content(p_expander, new_content); koto_expander_set_content(p_expander, new_content);
} }
void koto_nav_handle_playlist_add_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_nav_handle_playlist_add_click(
(void) gesture; (void) n_press; (void) x; (void) y; (void) user_data; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
(void) user_data;
koto_window_show_dialog(main_window, "create-modify-playlist"); koto_window_show_dialog(main_window, "create-modify-playlist");
} }
void koto_nav_handle_local_music_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_nav_handle_local_music_click(
(void) gesture; (void) n_press; (void) x; (void) y; (void) user_data; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
(void) user_data;
koto_window_go_to_page(main_window, "music.local"); // Go to the playlist page koto_window_go_to_page(main_window, "music.local"); // Go to the playlist page
} }
void koto_nav_handle_playlist_button_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_nav_handle_playlist_button_click(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
gchar *playlist_uuid = user_data; int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
gchar * playlist_uuid = user_data;
koto_window_go_to_page(main_window, playlist_uuid); // Go to the playlist page koto_window_go_to_page(main_window, playlist_uuid); // Go to the playlist page
} }
void koto_nav_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playlist, gpointer user_data) { void koto_nav_handle_playlist_added(
KotoCartographer * carto,
KotoPlaylist * playlist,
gpointer user_data
) {
(void) carto; (void) carto;
if (!KOTO_IS_PLAYLIST(playlist)) { if (!KOTO_IS_PLAYLIST(playlist)) {
return; return;
} }
KotoNav *self = user_data; KotoNav * self = user_data;
if (!KOTO_IS_NAV(self)) { if (!KOTO_IS_NAV(self)) {
return; return;
} }
gchar *playlist_uuid = koto_playlist_get_uuid(playlist); // Get the UUID for a playlist gchar * playlist_uuid = koto_playlist_get_uuid(playlist); // Get the UUID for a playlist
if (g_hash_table_contains(self->playlist_buttons, playlist_uuid)) { // Already added button if (g_hash_table_contains(self->playlist_buttons, playlist_uuid)) { // Already added button
g_free(playlist_uuid); g_free(playlist_uuid);
return; return;
} }
gchar *playlist_name = koto_playlist_get_name(playlist); gchar * playlist_name = koto_playlist_get_name(playlist);
gchar *playlist_art_path = koto_playlist_get_artwork(playlist); // Get any file path for it gchar * playlist_art_path = koto_playlist_get_artwork(playlist); // Get any file path for it
KotoButton *playlist_button = NULL; KotoButton * playlist_button = NULL;
if (koto_utils_is_string_valid(playlist_art_path)) { // Have a file associated if (koto_utils_is_string_valid(playlist_art_path)) { // Have a file associated
playlist_button = koto_button_new_with_file(playlist_name, playlist_art_path, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); playlist_button = koto_button_new_with_file(playlist_name, playlist_art_path, KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
@ -217,7 +268,7 @@ void koto_nav_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playl
g_hash_table_insert(self->playlist_buttons, playlist_uuid, playlist_button); // Add the button g_hash_table_insert(self->playlist_buttons, playlist_uuid, playlist_button); // Add the button
// TODO: Make this a ListBox and sort the playlists alphabetically // TODO: Make this a ListBox and sort the playlists alphabetically
GtkBox *playlist_expander_content = GTK_BOX(koto_expander_get_content(self->playlists_expander)); GtkBox * playlist_expander_content = GTK_BOX(koto_expander_get_content(self->playlists_expander));
if (GTK_IS_BOX(playlist_expander_content)) { if (GTK_IS_BOX(playlist_expander_content)) {
gtk_box_append(playlist_expander_content, GTK_WIDGET(playlist_button)); gtk_box_append(playlist_expander_content, GTK_WIDGET(playlist_button));
@ -229,58 +280,76 @@ void koto_nav_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playl
} }
} }
void koto_nav_handle_playlist_modified(KotoPlaylist *playlist, gpointer user_data) { void koto_nav_handle_playlist_modified(
KotoPlaylist * playlist,
gpointer user_data
) {
if (!KOTO_IS_PLAYLIST(playlist)) { if (!KOTO_IS_PLAYLIST(playlist)) {
return; return;
} }
KotoNav *self = user_data; KotoNav * self = user_data;
if (!KOTO_IS_NAV(self)) { if (!KOTO_IS_NAV(self)) {
return; return;
} }
gchar *playlist_uuid = koto_playlist_get_uuid(playlist); // Get the UUID for a playlist gchar * playlist_uuid = koto_playlist_get_uuid(playlist); // Get the UUID for a playlist
KotoButton * playlist_button = g_hash_table_lookup(self->playlist_buttons, playlist_uuid);
KotoButton *playlist_button = g_hash_table_lookup(self->playlist_buttons, playlist_uuid);
if (!KOTO_IS_BUTTON(playlist_button)) { if (!KOTO_IS_BUTTON(playlist_button)) {
return; return;
} }
gchar *artwork = koto_playlist_get_artwork(playlist); // Get the artwork gchar * artwork = koto_playlist_get_artwork(playlist); // Get the artwork
if (koto_utils_is_string_valid(artwork)) { // Have valid artwork if (koto_utils_is_string_valid(artwork)) { // Have valid artwork
koto_button_set_file_path(playlist_button, artwork); // Update the artwork path koto_button_set_file_path(playlist_button, artwork); // Update the artwork path
} }
gchar *name = koto_playlist_get_name(playlist); // Get the name gchar * name = koto_playlist_get_name(playlist); // Get the name
if (koto_utils_is_string_valid(name)) { // Have valid name if (koto_utils_is_string_valid(name)) { // Have valid name
koto_button_set_text(playlist_button, name); // Update the button text koto_button_set_text(playlist_button, name); // Update the button text
} }
} }
void koto_nav_handle_playlist_removed(KotoCartographer *carto, gchar *playlist_uuid, gpointer user_data) { void koto_nav_handle_playlist_removed(
KotoCartographer * carto,
gchar * playlist_uuid,
gpointer user_data
) {
(void) carto; (void) carto;
KotoNav *self = user_data; KotoNav * self = user_data;
if (!g_hash_table_contains(self->playlist_buttons, playlist_uuid)) { // Does not contain this if (!g_hash_table_contains(self->playlist_buttons, playlist_uuid)) { // Does not contain this
return; return;
} }
KotoButton *playlist_btn = g_hash_table_lookup(self->playlist_buttons, playlist_uuid); // Get the playlist button KotoButton * playlist_btn = g_hash_table_lookup(self->playlist_buttons, playlist_uuid); // Get the playlist button
if (!KOTO_IS_BUTTON(playlist_btn)) { // Not a playlist button if (!KOTO_IS_BUTTON(playlist_btn)) { // Not a playlist button
return; return;
} }
GtkBox *playlist_expander_content = GTK_BOX(koto_expander_get_content(self->playlists_expander)); GtkBox * playlist_expander_content = GTK_BOX(koto_expander_get_content(self->playlists_expander));
gtk_box_remove(playlist_expander_content, GTK_WIDGET(playlist_btn)); // Remove the button gtk_box_remove(playlist_expander_content, GTK_WIDGET(playlist_btn)); // Remove the button
g_hash_table_remove(self->playlist_buttons, playlist_uuid); // Remove from the playlist buttons hash table g_hash_table_remove(self->playlist_buttons, playlist_uuid); // Remove from the playlist buttons hash table
} }
GtkWidget* koto_nav_get_nav(KotoNav *self) { GtkWidget * koto_nav_get_nav(KotoNav * self) {
return self->win; return self->win;
} }
KotoNav* koto_nav_new(void) { KotoNav * koto_nav_new(void) {
return g_object_new(KOTO_TYPE_NAV ,NULL); return g_object_new(KOTO_TYPE_NAV, NULL);
} }

View file

@ -24,19 +24,50 @@ G_BEGIN_DECLS
#define KOTO_TYPE_NAV (koto_nav_get_type()) #define KOTO_TYPE_NAV (koto_nav_get_type())
G_DECLARE_FINAL_TYPE (KotoNav, koto_nav, KOTO, NAV, GObject) G_DECLARE_FINAL_TYPE(KotoNav, koto_nav, KOTO, NAV, GObject)
KotoNav* koto_nav_new (void); KotoNav* koto_nav_new(void);
void koto_nav_create_audiobooks_section(KotoNav *self); void koto_nav_create_audiobooks_section(KotoNav * self);
void koto_nav_create_music_section(KotoNav *self);
void koto_nav_create_playlist_section(KotoNav *self);
void koto_nav_create_podcasts_section(KotoNav *self);
void koto_nav_handle_playlist_add_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
void koto_nav_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playlist, gpointer user_data);
void koto_nav_handle_playlist_modified(KotoPlaylist *playlist, gpointer user_data);
void koto_nav_handle_playlist_removed(KotoCartographer *carto, gchar *playlist_uuid, gpointer user_data);
void koto_nav_handle_local_music_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
GtkWidget* koto_nav_get_nav(KotoNav *self); void koto_nav_create_music_section(KotoNav * self);
void koto_nav_create_playlist_section(KotoNav * self);
void koto_nav_create_podcasts_section(KotoNav * self);
void koto_nav_handle_playlist_add_click(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_nav_handle_playlist_added(
KotoCartographer * carto,
KotoPlaylist * playlist,
gpointer user_data
);
void koto_nav_handle_playlist_modified(
KotoPlaylist * playlist,
gpointer user_data
);
void koto_nav_handle_playlist_removed(
KotoCartographer * carto,
gchar * playlist_uuid,
gpointer user_data
);
void koto_nav_handle_local_music_click(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
GtkWidget * koto_nav_get_nav(KotoNav * self);
G_END_DECLS G_END_DECLS

View file

@ -26,40 +26,40 @@
#include "koto-config.h" #include "koto-config.h"
#include "koto-playerbar.h" #include "koto-playerbar.h"
extern KotoAddRemoveTrackPopover *koto_add_remove_track_popup; extern KotoAddRemoveTrackPopover * koto_add_remove_track_popup;
extern KotoCurrentPlaylist *current_playlist; extern KotoCurrentPlaylist * current_playlist;
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoPlaybackEngine *playback_engine; extern KotoPlaybackEngine * playback_engine;
struct _KotoPlayerBar { struct _KotoPlayerBar {
GObject parent_instance; GObject parent_instance;
GtkWidget *main; GtkWidget * main;
GtkWidget *controls; GtkWidget * controls;
/* Sections */ /* Sections */
GtkWidget *playback_section; GtkWidget * playback_section;
GtkWidget *primary_controls_section; GtkWidget * primary_controls_section;
GtkWidget *secondary_controls_section; GtkWidget * secondary_controls_section;
/* Primary Buttons */ /* Primary Buttons */
KotoButton *back_button; KotoButton * back_button;
KotoButton *play_pause_button; KotoButton * play_pause_button;
KotoButton *forward_button; KotoButton * forward_button;
KotoButton *repeat_button; KotoButton * repeat_button;
KotoButton *shuffle_button; KotoButton * shuffle_button;
KotoButton *playlist_button; KotoButton * playlist_button;
KotoButton *eq_button; KotoButton * eq_button;
GtkWidget *volume_button; GtkWidget * volume_button;
GtkWidget *progress_bar; GtkWidget * progress_bar;
/* Selected Playback Section */ /* Selected Playback Section */
GtkWidget *playback_details_section; GtkWidget * playback_details_section;
GtkWidget *artwork; GtkWidget * artwork;
GtkWidget *playback_title; GtkWidget * playback_title;
GtkWidget *playback_album; GtkWidget * playback_album;
GtkWidget *playback_artist; GtkWidget * playback_artist;
gint64 last_recorded_duration; gint64 last_recorded_duration;
@ -72,17 +72,21 @@ struct _KotoPlayerBarClass {
G_DEFINE_TYPE(KotoPlayerBar, koto_playerbar, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoPlayerBar, koto_playerbar, G_TYPE_OBJECT);
static void koto_playerbar_constructed(GObject *obj); static void koto_playerbar_constructed(GObject * obj);
static void koto_playerbar_class_init(KotoPlayerBarClass * c) {
GObjectClass * gobject_class;
static void koto_playerbar_class_init(KotoPlayerBarClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->constructed = koto_playerbar_constructed; gobject_class->constructed = koto_playerbar_constructed;
} }
static void koto_playerbar_constructed(GObject *obj) { static void koto_playerbar_constructed(GObject * obj) {
KotoPlayerBar *self = KOTO_PLAYERBAR(obj); KotoPlayerBar * self = KOTO_PLAYERBAR(obj);
self->main = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); self->main = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_add_css_class(self->main, "player-bar"); gtk_widget_add_css_class(self->main, "player-bar");
@ -93,7 +97,9 @@ static void koto_playerbar_constructed(GObject *obj) {
gtk_range_set_increments(GTK_RANGE(self->progress_bar), 1, 1); gtk_range_set_increments(GTK_RANGE(self->progress_bar), 1, 1);
gtk_range_set_round_digits(GTK_RANGE(self->progress_bar), 1); gtk_range_set_round_digits(GTK_RANGE(self->progress_bar), 1);
GtkGesture *press_controller = gtk_gesture_click_new(); // Create a new GtkGestureLongPress GtkGesture * press_controller = gtk_gesture_click_new(); // Create a new GtkGestureLongPress
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(press_controller), 1); // Set to left click gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(press_controller), 1); // Set to left click
g_signal_connect(press_controller, "begin", G_CALLBACK(koto_playerbar_handle_progressbar_gesture_begin), self); g_signal_connect(press_controller, "begin", G_CALLBACK(koto_playerbar_handle_progressbar_gesture_begin), self);
@ -140,19 +146,20 @@ static void koto_playerbar_constructed(GObject *obj) {
g_signal_connect(playback_engine, "track-shuffle-changed", G_CALLBACK(koto_playerbar_handle_track_shuffle), self); g_signal_connect(playback_engine, "track-shuffle-changed", G_CALLBACK(koto_playerbar_handle_track_shuffle), self);
} }
static void koto_playerbar_init(KotoPlayerBar *self) { static void koto_playerbar_init(KotoPlayerBar * self) {
self->last_recorded_duration = 0; self->last_recorded_duration = 0;
self->is_progressbar_seeking = FALSE; self->is_progressbar_seeking = FALSE;
} }
KotoPlayerBar* koto_playerbar_new(void) { KotoPlayerBar * koto_playerbar_new(void) {
return g_object_new(KOTO_TYPE_PLAYERBAR, NULL); return g_object_new(KOTO_TYPE_PLAYERBAR, NULL);
} }
void koto_playerbar_create_playback_details(KotoPlayerBar* bar) { void koto_playerbar_create_playback_details(KotoPlayerBar* bar) {
bar->playback_details_section = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); bar->playback_details_section = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
GtkIconTheme *default_icon_theme = gtk_icon_theme_get_for_display(gdk_display_get_default()); // Get the icon theme for this display GtkIconTheme * default_icon_theme = gtk_icon_theme_get_for_display(gdk_display_get_default()); // Get the icon theme for this display
if (default_icon_theme != NULL) { if (default_icon_theme != NULL) {
gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(bar->main)); gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(bar->main));
@ -235,7 +242,7 @@ void koto_playerbar_create_secondary_controls(KotoPlayerBar* bar) {
} }
if (GTK_IS_VOLUME_BUTTON(bar->volume_button)) { if (GTK_IS_VOLUME_BUTTON(bar->volume_button)) {
GtkAdjustment *granular_volume_change = gtk_adjustment_new(0.5, 0, 1.0, 0.02, 0.02, 0.02); GtkAdjustment * granular_volume_change = gtk_adjustment_new(0.5, 0, 1.0, 0.02, 0.02, 0.02);
g_object_set(bar->volume_button, "use-symbolic", TRUE, NULL); g_object_set(bar->volume_button, "use-symbolic", TRUE, NULL);
gtk_scale_button_set_adjustment(GTK_SCALE_BUTTON(bar->volume_button), granular_volume_change); // Set our adjustment gtk_scale_button_set_adjustment(GTK_SCALE_BUTTON(bar->volume_button), granular_volume_change); // Set our adjustment
gtk_box_append(GTK_BOX(bar->secondary_controls_section), bar->volume_button); gtk_box_append(GTK_BOX(bar->secondary_controls_section), bar->volume_button);
@ -245,24 +252,48 @@ void koto_playerbar_create_secondary_controls(KotoPlayerBar* bar) {
} }
} }
void koto_playerbar_go_backwards(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_playerbar_go_backwards(
(void) gesture; (void) n_press; (void) x; (void) y; (void) data; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
(void) data;
koto_playback_engine_backwards(playback_engine); koto_playback_engine_backwards(playback_engine);
} }
void koto_playerbar_go_forwards(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_playerbar_go_forwards(
(void) gesture; (void) n_press; (void) x; (void) y; (void) data; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
(void) data;
koto_playback_engine_forwards(playback_engine); koto_playback_engine_forwards(playback_engine);
} }
void koto_playerbar_handle_is_playing(KotoPlaybackEngine *engine, gpointer user_data) { void koto_playerbar_handle_is_playing(
KotoPlaybackEngine * engine,
gpointer user_data
) {
if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { if (!KOTO_IS_PLAYBACK_ENGINE(engine)) {
return; return;
} }
KotoPlayerBar *bar = user_data; KotoPlayerBar * bar = user_data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -271,12 +302,16 @@ void koto_playerbar_handle_is_playing(KotoPlaybackEngine *engine, gpointer user_
koto_button_show_image(bar->play_pause_button, TRUE); // Set to TRUE to show pause as the next action koto_button_show_image(bar->play_pause_button, TRUE); // Set to TRUE to show pause as the next action
} }
void koto_playerbar_handle_is_paused(KotoPlaybackEngine *engine, gpointer user_data) { void koto_playerbar_handle_is_paused(
KotoPlaybackEngine * engine,
gpointer user_data
) {
if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { if (!KOTO_IS_PLAYBACK_ENGINE(engine)) {
return; return;
} }
KotoPlayerBar *bar = user_data; KotoPlayerBar * bar = user_data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -285,9 +320,19 @@ void koto_playerbar_handle_is_paused(KotoPlaybackEngine *engine, gpointer user_d
koto_button_show_image(bar->play_pause_button, FALSE); // Set to FALSE to show play as the next action koto_button_show_image(bar->play_pause_button, FALSE); // Set to FALSE to show play as the next action
} }
void koto_playerbar_handle_playlist_button_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_playerbar_handle_playlist_button_clicked(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
KotoPlayerBar *self = data; int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoPlayerBar * self = data;
if (!KOTO_IS_PLAYERBAR(self)) { // Not a playerbar if (!KOTO_IS_PLAYERBAR(self)) { // Not a playerbar
return; return;
@ -297,9 +342,15 @@ void koto_playerbar_handle_playlist_button_clicked(GtkGestureClick *gesture, int
gtk_widget_show(GTK_WIDGET(koto_add_remove_track_popup)); gtk_widget_show(GTK_WIDGET(koto_add_remove_track_popup));
} }
void koto_playerbar_handle_progressbar_gesture_begin(GtkGesture *gesture, GdkEventSequence *seq, gpointer data) { void koto_playerbar_handle_progressbar_gesture_begin(
(void) gesture; (void) seq; GtkGesture * gesture,
KotoPlayerBar *bar = data; GdkEventSequence * seq,
gpointer data
) {
(void) gesture;
(void) seq;
KotoPlayerBar * bar = data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -308,9 +359,15 @@ void koto_playerbar_handle_progressbar_gesture_begin(GtkGesture *gesture, GdkEve
bar->is_progressbar_seeking = TRUE; bar->is_progressbar_seeking = TRUE;
} }
void koto_playerbar_handle_progressbar_gesture_end(GtkGesture *gesture, GdkEventSequence *seq, gpointer data) { void koto_playerbar_handle_progressbar_gesture_end(
(void) gesture; (void) seq; GtkGesture * gesture,
KotoPlayerBar *bar = data; GdkEventSequence * seq,
gpointer data
) {
(void) gesture;
(void) seq;
KotoPlayerBar * bar = data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -318,9 +375,19 @@ void koto_playerbar_handle_progressbar_gesture_end(GtkGesture *gesture, GdkEvent
bar->is_progressbar_seeking = FALSE; bar->is_progressbar_seeking = FALSE;
} }
void koto_playerbar_handle_progressbar_pressed(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_playerbar_handle_progressbar_pressed(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
KotoPlayerBar *bar = data; int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoPlayerBar * bar = data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -329,8 +396,12 @@ void koto_playerbar_handle_progressbar_pressed(GtkGestureClick *gesture, int n_p
bar->is_progressbar_seeking = TRUE; bar->is_progressbar_seeking = TRUE;
} }
void koto_playerbar_handle_progressbar_value_changed(GtkRange *progress_bar, gpointer data) { void koto_playerbar_handle_progressbar_value_changed(
KotoPlayerBar *bar = data; GtkRange * progress_bar,
gpointer data
) {
KotoPlayerBar * bar = data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -342,15 +413,20 @@ void koto_playerbar_handle_progressbar_value_changed(GtkRange *progress_bar, gpo
int desired_position = (int) gtk_range_get_value(progress_bar); int desired_position = (int) gtk_range_get_value(progress_bar);
koto_playback_engine_set_position(playback_engine, desired_position); // Update our position koto_playback_engine_set_position(playback_engine, desired_position); // Update our position
} }
void koto_playerbar_handle_tick_duration(KotoPlaybackEngine *engine, gpointer user_data) { void koto_playerbar_handle_tick_duration(
KotoPlaybackEngine * engine,
gpointer user_data
) {
if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { if (!KOTO_IS_PLAYBACK_ENGINE(engine)) {
return; return;
} }
KotoPlayerBar *bar = user_data; KotoPlayerBar * bar = user_data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -360,12 +436,16 @@ void koto_playerbar_handle_tick_duration(KotoPlaybackEngine *engine, gpointer us
} }
void koto_playerbar_handle_tick_track(KotoPlaybackEngine *engine, gpointer user_data) { void koto_playerbar_handle_tick_track(
KotoPlaybackEngine * engine,
gpointer user_data
) {
if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { if (!KOTO_IS_PLAYBACK_ENGINE(engine)) {
return; return;
} }
KotoPlayerBar *bar = user_data; KotoPlayerBar * bar = user_data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -376,12 +456,16 @@ void koto_playerbar_handle_tick_track(KotoPlaybackEngine *engine, gpointer user_
} }
} }
void koto_playerbar_handle_track_repeat(KotoPlaybackEngine *engine, gpointer user_data) { void koto_playerbar_handle_track_repeat(
KotoPlaybackEngine * engine,
gpointer user_data
) {
if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { if (!KOTO_IS_PLAYBACK_ENGINE(engine)) {
return; return;
} }
KotoPlayerBar *bar = user_data; KotoPlayerBar * bar = user_data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -394,12 +478,16 @@ void koto_playerbar_handle_track_repeat(KotoPlaybackEngine *engine, gpointer use
} }
} }
void koto_playerbar_handle_track_shuffle(KotoPlaybackEngine *engine, gpointer user_data) { void koto_playerbar_handle_track_shuffle(
KotoPlaybackEngine * engine,
gpointer user_data
) {
if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { if (!KOTO_IS_PLAYBACK_ENGINE(engine)) {
return; return;
} }
KotoPlayerBar *bar = user_data; KotoPlayerBar * bar = user_data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
@ -412,8 +500,13 @@ void koto_playerbar_handle_track_shuffle(KotoPlaybackEngine *engine, gpointer us
} }
} }
void koto_playerbar_handle_volume_button_change(GtkScaleButton *button, double value, gpointer user_data) { void koto_playerbar_handle_volume_button_change(
(void) button; (void) user_data; GtkScaleButton * button,
double value,
gpointer user_data
) {
(void) button;
(void) user_data;
koto_playback_engine_set_volume(playback_engine, value); koto_playback_engine_set_volume(playback_engine, value);
} }
@ -422,7 +515,10 @@ void koto_playerbar_reset_progressbar(KotoPlayerBar* bar) {
gtk_range_set_value(GTK_RANGE(bar->progress_bar), 0); // Set value to 0 gtk_range_set_value(GTK_RANGE(bar->progress_bar), 0); // Set value to 0
} }
void koto_playerbar_set_progressbar_duration(KotoPlayerBar* bar, gint64 duration) { void koto_playerbar_set_progressbar_duration(
KotoPlayerBar* bar,
gint64 duration
) {
if (duration <= 0) { if (duration <= 0) {
return; return;
} }
@ -433,52 +529,92 @@ void koto_playerbar_set_progressbar_duration(KotoPlayerBar* bar, gint64 duration
} }
} }
void koto_playerbar_set_progressbar_value(KotoPlayerBar* bar, double progress) { void koto_playerbar_set_progressbar_value(
KotoPlayerBar* bar,
double progress
) {
gtk_range_set_value(GTK_RANGE(bar->progress_bar), progress); gtk_range_set_value(GTK_RANGE(bar->progress_bar), progress);
} }
void koto_playerbar_toggle_play_pause(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_playerbar_toggle_play_pause(
(void) gesture; (void) n_press; (void) x; (void) y; (void) data; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
(void) data;
koto_playback_engine_toggle(playback_engine); koto_playback_engine_toggle(playback_engine);
} }
void koto_playerbar_toggle_playlist_shuffle(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_playerbar_toggle_playlist_shuffle(
(void) gesture; (void) n_press; (void) x; (void) y; (void) data; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
(void) data;
koto_playback_engine_toggle_track_shuffle(playback_engine); // Call our playback engine's toggle track shuffle function koto_playback_engine_toggle_track_shuffle(playback_engine); // Call our playback engine's toggle track shuffle function
} }
void koto_playerbar_toggle_track_repeat(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_playerbar_toggle_track_repeat(
(void) gesture; (void) n_press; (void) x; (void) y; (void) data; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
(void) data;
koto_playback_engine_toggle_track_repeat(playback_engine); // Call our playback engine's toggle track repeat function koto_playback_engine_toggle_track_repeat(playback_engine); // Call our playback engine's toggle track repeat function
} }
void koto_playerbar_update_track_info(KotoPlaybackEngine *engine, gpointer user_data) { void koto_playerbar_update_track_info(
KotoPlaybackEngine * engine,
gpointer user_data
) {
if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { if (!KOTO_IS_PLAYBACK_ENGINE(engine)) {
return; return;
} }
KotoPlayerBar *bar = user_data; KotoPlayerBar * bar = user_data;
if (!KOTO_IS_PLAYERBAR(bar)) { if (!KOTO_IS_PLAYERBAR(bar)) {
return; return;
} }
KotoIndexedTrack *current_track = koto_playback_engine_get_current_track(playback_engine); // Get the current track from the playback engine KotoIndexedTrack * current_track = koto_playback_engine_get_current_track(playback_engine); // Get the current track from the playback engine
if (!KOTO_IS_INDEXED_TRACK(current_track)) { if (!KOTO_IS_INDEXED_TRACK(current_track)) {
return; return;
} }
gchar *track_name = NULL; gchar * track_name = NULL;
gchar *artist_uuid = NULL; gchar * artist_uuid = NULL;
gchar *album_uuid = NULL; gchar * album_uuid = NULL;
g_object_get(current_track, "parsed-name", &track_name, "artist-uuid", &artist_uuid, "album-uuid", &album_uuid, NULL); g_object_get(current_track, "parsed-name", &track_name, "artist-uuid", &artist_uuid, "album-uuid", &album_uuid, NULL);
KotoIndexedArtist *artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid); KotoIndexedArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid);
KotoIndexedAlbum *album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid); KotoIndexedAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid);
g_free(artist_uuid); g_free(artist_uuid);
g_free(album_uuid); g_free(album_uuid);
@ -488,7 +624,7 @@ void koto_playerbar_update_track_info(KotoPlaybackEngine *engine, gpointer user_
} }
if (KOTO_IS_INDEXED_ARTIST(artist)) { if (KOTO_IS_INDEXED_ARTIST(artist)) {
gchar *artist_name = NULL; gchar * artist_name = NULL;
g_object_get(artist, "name", &artist_name, NULL); g_object_get(artist, "name", &artist_name, NULL);
if ((artist_name != NULL) && (strcmp(artist_name, "") != 0)) { // Have an artist name if ((artist_name != NULL) && (strcmp(artist_name, "") != 0)) { // Have an artist name
@ -500,8 +636,8 @@ void koto_playerbar_update_track_info(KotoPlaybackEngine *engine, gpointer user_
} }
if (KOTO_IS_INDEXED_ALBUM(album)) { if (KOTO_IS_INDEXED_ALBUM(album)) {
gchar *album_name = NULL; gchar * album_name = NULL;
gchar *art_path = NULL; gchar * art_path = NULL;
g_object_get(album, "name", &album_name, "art-path", &art_path, NULL); // Get album name and art path g_object_get(album, "name", &album_name, "art-path", &art_path, NULL); // Get album name and art path
if ((album_name != NULL) && (strcmp(album_name, "") != 0)) { // Have an album name if ((album_name != NULL) && (strcmp(album_name, "") != 0)) { // Have an album name
@ -519,6 +655,6 @@ void koto_playerbar_update_track_info(KotoPlaybackEngine *engine, gpointer user_
} }
} }
GtkWidget* koto_playerbar_get_main(KotoPlayerBar* bar) { GtkWidget * koto_playerbar_get_main(KotoPlayerBar* bar) {
return bar->main; return bar->main;
} }

View file

@ -23,35 +23,147 @@
G_BEGIN_DECLS G_BEGIN_DECLS
#define KOTO_TYPE_PLAYERBAR (koto_playerbar_get_type()) #define KOTO_TYPE_PLAYERBAR (koto_playerbar_get_type())
G_DECLARE_FINAL_TYPE (KotoPlayerBar, koto_playerbar, KOTO, PLAYERBAR, GObject) G_DECLARE_FINAL_TYPE(KotoPlayerBar, koto_playerbar, KOTO, PLAYERBAR, GObject)
#define KOTO_IS_PLAYERBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYERBAR)) #define KOTO_IS_PLAYERBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYERBAR))
KotoPlayerBar* koto_playerbar_new (void); KotoPlayerBar * koto_playerbar_new(void);
GtkWidget* koto_playerbar_get_main(KotoPlayerBar* bar); GtkWidget * koto_playerbar_get_main(KotoPlayerBar* bar);
void koto_playerbar_create_playback_details(KotoPlayerBar* bar); void koto_playerbar_create_playback_details(KotoPlayerBar* bar);
void koto_playerbar_create_primary_controls(KotoPlayerBar* bar); void koto_playerbar_create_primary_controls(KotoPlayerBar* bar);
void koto_playerbar_create_secondary_controls(KotoPlayerBar* bar); void koto_playerbar_create_secondary_controls(KotoPlayerBar* bar);
void koto_playerbar_go_backwards(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data);
void koto_playerbar_go_forwards(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); void koto_playerbar_go_backwards(
void koto_playerbar_handle_is_playing(KotoPlaybackEngine *engine, gpointer user_data); GtkGestureClick * gesture,
void koto_playerbar_handle_is_paused(KotoPlaybackEngine *engine, gpointer user_data); int n_press,
void koto_playerbar_handle_playlist_button_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); double x,
void koto_playerbar_handle_progressbar_scroll_begin(GtkEventControllerScroll *controller, gpointer data); double y,
void koto_playerbar_handle_progressbar_gesture_begin(GtkGesture *gesture, GdkEventSequence *seq, gpointer data); gpointer data
void koto_playerbar_handle_progressbar_gesture_end(GtkGesture *gesture, GdkEventSequence *seq, gpointer data); );
void koto_playerbar_handle_progressbar_pressed(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data);
void koto_playerbar_handle_progressbar_value_changed(GtkRange *progress_bar, gpointer data); void koto_playerbar_go_forwards(
void koto_playerbar_handle_tick_duration(KotoPlaybackEngine *engine, gpointer user_data); GtkGestureClick * gesture,
void koto_playerbar_handle_tick_track(KotoPlaybackEngine *engine, gpointer user_data); int n_press,
void koto_playerbar_handle_track_repeat(KotoPlaybackEngine *engine, gpointer user_data); double x,
void koto_playerbar_handle_track_shuffle(KotoPlaybackEngine *engine, gpointer user_data); double y,
void koto_playerbar_handle_volume_button_change(GtkScaleButton *button, double value, gpointer user_data); gpointer data
);
void koto_playerbar_handle_is_playing(
KotoPlaybackEngine * engine,
gpointer user_data
);
void koto_playerbar_handle_is_paused(
KotoPlaybackEngine * engine,
gpointer user_data
);
void koto_playerbar_handle_playlist_button_clicked(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
void koto_playerbar_handle_progressbar_scroll_begin(
GtkEventControllerScroll * controller,
gpointer data
);
void koto_playerbar_handle_progressbar_gesture_begin(
GtkGesture * gesture,
GdkEventSequence * seq,
gpointer data
);
void koto_playerbar_handle_progressbar_gesture_end(
GtkGesture * gesture,
GdkEventSequence * seq,
gpointer data
);
void koto_playerbar_handle_progressbar_pressed(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
void koto_playerbar_handle_progressbar_value_changed(
GtkRange * progress_bar,
gpointer data
);
void koto_playerbar_handle_tick_duration(
KotoPlaybackEngine * engine,
gpointer user_data
);
void koto_playerbar_handle_tick_track(
KotoPlaybackEngine * engine,
gpointer user_data
);
void koto_playerbar_handle_track_repeat(
KotoPlaybackEngine * engine,
gpointer user_data
);
void koto_playerbar_handle_track_shuffle(
KotoPlaybackEngine * engine,
gpointer user_data
);
void koto_playerbar_handle_volume_button_change(
GtkScaleButton * button,
double value,
gpointer user_data
);
void koto_playerbar_reset_progressbar(KotoPlayerBar* bar); void koto_playerbar_reset_progressbar(KotoPlayerBar* bar);
void koto_playerbar_set_progressbar_duration(KotoPlayerBar* bar, gint64 duration);
void koto_playerbar_set_progressbar_value(KotoPlayerBar* bar, gdouble progress); void koto_playerbar_set_progressbar_duration(
void koto_playerbar_toggle_play_pause(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); KotoPlayerBar* bar,
void koto_playerbar_toggle_playlist_shuffle(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); gint64 duration
void koto_playerbar_toggle_track_repeat(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); );
void koto_playerbar_update_track_info(KotoPlaybackEngine *engine, gpointer user_data);
void koto_playerbar_set_progressbar_value(
KotoPlayerBar* bar,
gdouble progress
);
void koto_playerbar_toggle_play_pause(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
void koto_playerbar_toggle_playlist_shuffle(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
void koto_playerbar_toggle_track_repeat(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
void koto_playerbar_update_track_info(
KotoPlaybackEngine * engine,
gpointer user_data
);
G_END_DECLS G_END_DECLS

View file

@ -21,13 +21,13 @@
#include "koto-button.h" #include "koto-button.h"
#include "koto-track-item.h" #include "koto-track-item.h"
extern KotoAddRemoveTrackPopover *koto_add_remove_track_popup; extern KotoAddRemoveTrackPopover * koto_add_remove_track_popup;
struct _KotoTrackItem { struct _KotoTrackItem {
GtkBox parent_instance; GtkBox parent_instance;
KotoIndexedTrack *track; KotoIndexedTrack * track;
GtkWidget *track_label; GtkWidget * track_label;
}; };
struct _KotoTrackItemClass { struct _KotoTrackItemClass {
@ -40,15 +40,30 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
NULL,
};
G_DEFINE_TYPE(KotoTrackItem, koto_track_item, GTK_TYPE_BOX); G_DEFINE_TYPE(KotoTrackItem, koto_track_item, GTK_TYPE_BOX);
static void koto_track_item_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); static void koto_track_item_get_property(
static void koto_track_item_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_track_item_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_track_item_class_init(KotoTrackItemClass * c) {
GObjectClass * gobject_class;
static void koto_track_item_class_init(KotoTrackItemClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_track_item_set_property; gobject_class->set_property = koto_track_item_set_property;
gobject_class->get_property = koto_track_item_get_property; gobject_class->get_property = koto_track_item_get_property;
@ -58,14 +73,20 @@ static void koto_track_item_class_init(KotoTrackItemClass *c) {
"Track", "Track",
"Track", "Track",
KOTO_TYPE_INDEXED_TRACK, KOTO_TYPE_INDEXED_TRACK,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_track_item_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_track_item_get_property(
KotoTrackItem *self = KOTO_TRACK_ITEM(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoTrackItem * self = KOTO_TRACK_ITEM(obj);
switch (prop_id) { switch (prop_id) {
case PROP_TRACK: case PROP_TRACK:
@ -77,8 +98,14 @@ static void koto_track_item_get_property(GObject *obj, guint prop_id, GValue *va
} }
} }
static void koto_track_item_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_track_item_set_property(
KotoTrackItem *self = KOTO_TRACK_ITEM(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoTrackItem * self = KOTO_TRACK_ITEM(obj);
switch (prop_id) { switch (prop_id) {
case PROP_TRACK: case PROP_TRACK:
@ -90,7 +117,7 @@ static void koto_track_item_set_property(GObject *obj, guint prop_id, const GVal
} }
} }
static void koto_track_item_init(KotoTrackItem *self) { static void koto_track_item_init(KotoTrackItem * self) {
self->track_label = gtk_label_new(NULL); // Create with no track name self->track_label = gtk_label_new(NULL); // Create with no track name
gtk_label_set_xalign(GTK_LABEL(self->track_label), 0.0); gtk_label_set_xalign(GTK_LABEL(self->track_label), 0.0);
@ -101,23 +128,29 @@ static void koto_track_item_init(KotoTrackItem *self) {
gtk_box_prepend(GTK_BOX(self), self->track_label); gtk_box_prepend(GTK_BOX(self), self->track_label);
} }
KotoIndexedTrack* koto_track_item_get_track(KotoTrackItem *self) { KotoIndexedTrack * koto_track_item_get_track(KotoTrackItem * self) {
return self->track; return self->track;
} }
void koto_track_item_set_track(KotoTrackItem *self, KotoIndexedTrack *track) { void koto_track_item_set_track(
KotoTrackItem * self,
KotoIndexedTrack * track
) {
if (track == NULL) { // Not a track if (track == NULL) { // Not a track
return; return;
} }
self->track = track; self->track = track;
gchar *track_name; gchar * track_name;
g_object_get(self->track, "parsed-name", &track_name, NULL); g_object_get(self->track, "parsed-name", &track_name, NULL);
gtk_label_set_text(GTK_LABEL(self->track_label), track_name); // Update the text gtk_label_set_text(GTK_LABEL(self->track_label), track_name); // Update the text
} }
KotoTrackItem* koto_track_item_new(KotoIndexedTrack *track) { KotoTrackItem * koto_track_item_new(KotoIndexedTrack * track) {
return g_object_new(KOTO_TYPE_TRACK_ITEM, return g_object_new(
KOTO_TYPE_TRACK_ITEM,
"track", "track",
track, track,
NULL NULL

View file

@ -27,9 +27,20 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(KotoTrackItem, koto_track_item, KOTO, TRACK_ITEM, GtkBox) G_DECLARE_FINAL_TYPE(KotoTrackItem, koto_track_item, KOTO, TRACK_ITEM, GtkBox)
KotoTrackItem* koto_track_item_new(KotoIndexedTrack *track); KotoTrackItem* koto_track_item_new(KotoIndexedTrack * track);
void koto_track_item_handle_add_to_playlist_button_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data); void koto_track_item_handle_add_to_playlist_button_click(
KotoIndexedTrack* koto_track_item_get_track(KotoTrackItem *self); GtkGestureClick * gesture,
void koto_track_item_set_track(KotoTrackItem *self, KotoIndexedTrack *track); int n_press,
double x,
double y,
gpointer user_data
);
KotoIndexedTrack * koto_track_item_get_track(KotoTrackItem * self);
void koto_track_item_set_track(
KotoTrackItem * self,
KotoIndexedTrack * track
);
G_END_DECLS G_END_DECLS

View file

@ -18,9 +18,9 @@
#include <glib-2.0/glib.h> #include <glib-2.0/glib.h>
#include <gtk-4.0/gtk/gtk.h> #include <gtk-4.0/gtk/gtk.h>
extern GtkWindow *main_window; extern GtkWindow * main_window;
GtkFileChooserNative* koto_utils_create_image_file_chooser(gchar *file_chooser_label) { GtkFileChooserNative * koto_utils_create_image_file_chooser(gchar * file_chooser_label) {
GtkFileChooserNative* chooser = gtk_file_chooser_native_new( GtkFileChooserNative* chooser = gtk_file_chooser_native_new(
file_chooser_label, file_chooser_label,
main_window, main_window,
@ -29,7 +29,9 @@ GtkFileChooserNative* koto_utils_create_image_file_chooser(gchar *file_chooser_l
"Cancel" "Cancel"
); );
GtkFileFilter *image_filter = gtk_file_filter_new(); // Create our file filter GtkFileFilter * image_filter = gtk_file_filter_new(); // Create our file filter
gtk_file_filter_add_mime_type(image_filter, "image/*"); // Only allow for images gtk_file_filter_add_mime_type(image_filter, "image/*"); // Only allow for images
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(chooser), image_filter); // Only allow picking images gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(chooser), image_filter); // Only allow picking images
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), FALSE); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), FALSE);
@ -37,9 +39,15 @@ GtkFileChooserNative* koto_utils_create_image_file_chooser(gchar *file_chooser_l
return chooser; return chooser;
} }
GtkWidget* koto_utils_create_image_from_filepath(gchar *filepath, gchar *fallback_icon, guint width, guint height) { GtkWidget * koto_utils_create_image_from_filepath(
gchar * filepath,
gchar * fallback_icon,
guint width,
guint height
) {
GtkWidget* image = NULL; GtkWidget* image = NULL;
if ((filepath != NULL) && (strcmp(filepath, "") != 0)) { // If we have a filepath if ((filepath != NULL) && (strcmp(filepath, "") != 0)) { // If we have a filepath
if (g_file_test(filepath, G_FILE_TEST_EXISTS)) { // File exists if (g_file_test(filepath, G_FILE_TEST_EXISTS)) { // File exists
image = gtk_image_new_from_file(filepath); // Load from the filepath image = gtk_image_new_from_file(filepath); // Load from the filepath
@ -57,21 +65,24 @@ GtkWidget* koto_utils_create_image_from_filepath(gchar *filepath, gchar *fallbac
return image; return image;
} }
gchar* koto_utils_get_filename_without_extension(gchar *filename) { gchar * koto_utils_get_filename_without_extension(gchar * filename) {
gchar *trimmed_file_name = g_strdup(filename); gchar * trimmed_file_name = g_strdup(filename);
gchar **split = g_strsplit(filename, ".", -1); // Split every time we see . gchar ** split = g_strsplit(filename, ".", -1); // Split every time we see .
g_free(trimmed_file_name); g_free(trimmed_file_name);
guint len_of_extension_split = g_strv_length(split); guint len_of_extension_split = g_strv_length(split);
if (len_of_extension_split == 2) { // Only have two elements if (len_of_extension_split == 2) { // Only have two elements
trimmed_file_name = g_strdup(split[0]); // Get the first element trimmed_file_name = g_strdup(split[0]); // Get the first element
} else { } else {
gchar *new_parsed_name = ""; gchar * new_parsed_name = "";
for (guint i = 0; i < len_of_extension_split - 1; i++) { // Iterate over everything except the last item for (guint i = 0; i < len_of_extension_split - 1; i++) { // Iterate over everything except the last item
if (g_strcmp0(new_parsed_name, "") == 0) { // Currently empty if (g_strcmp0(new_parsed_name, "") == 0) { // Currently empty
new_parsed_name = g_strdup(split[i]); // Just duplicate this string new_parsed_name = g_strdup(split[i]); // Just duplicate this string
} else { } else {
gchar *tmp_copy = g_strdup(new_parsed_name); gchar * tmp_copy = g_strdup(new_parsed_name);
g_free(new_parsed_name); // Free the old g_free(new_parsed_name); // Free the old
new_parsed_name = g_strjoin(".", tmp_copy, split[i], NULL); // Join the two strings with a . again and duplicate it, setting it to our new_parsed_name new_parsed_name = g_strjoin(".", tmp_copy, split[i], NULL); // Join the two strings with a . again and duplicate it, setting it to our new_parsed_name
g_free(tmp_copy); // Free our temporary copy g_free(tmp_copy); // Free our temporary copy
@ -82,22 +93,32 @@ gchar* koto_utils_get_filename_without_extension(gchar *filename) {
g_free(new_parsed_name); g_free(new_parsed_name);
} }
gchar *stripped_file_name = g_strstrip(g_strdup(trimmed_file_name)); // Strip leading and trailing whitespace gchar * stripped_file_name = g_strstrip(g_strdup(trimmed_file_name)); // Strip leading and trailing whitespace
g_free(trimmed_file_name); g_free(trimmed_file_name);
return stripped_file_name; return stripped_file_name;
} }
gboolean koto_utils_is_string_valid(gchar *str) { gboolean koto_utils_is_string_valid(gchar * str) {
return ((str != NULL) && (g_strcmp0(str, "") != 0)); return ((str != NULL) && (g_strcmp0(str, "") != 0));
} }
void koto_utils_push_queue_element_to_store(gpointer data, gpointer user_data) { void koto_utils_push_queue_element_to_store(
gpointer data,
gpointer user_data
) {
g_list_store_append(G_LIST_STORE(user_data), data); g_list_store_append(G_LIST_STORE(user_data), data);
} }
gchar* koto_utils_replace_string_all(gchar *str, gchar *find, gchar *repl) { gchar * koto_utils_replace_string_all(
gchar *cleaned_string = ""; gchar * str,
gchar **split = g_strsplit(str, find, -1); // Split on find gchar * find,
gchar * repl
) {
gchar * cleaned_string = "";
gchar ** split = g_strsplit(str, find, -1); // Split on find
for (guint i = 0; i < g_strv_length(split); i++) { // For each split for (guint i = 0; i < g_strv_length(split); i++) { // For each split
cleaned_string = g_strjoin(repl, cleaned_string, split[i], NULL); // Join the strings with our replace string cleaned_string = g_strjoin(repl, cleaned_string, split[i], NULL); // Join the strings with our replace string
@ -107,16 +128,19 @@ gchar* koto_utils_replace_string_all(gchar *str, gchar *find, gchar *repl) {
return cleaned_string; return cleaned_string;
} }
gchar* koto_utils_unquote_string(gchar *s) { gchar * koto_utils_unquote_string(gchar * s) {
gchar *new_s = NULL; gchar * new_s = NULL;
if (g_str_has_prefix(s, "'") && g_str_has_suffix(s, "'")) { // Begins and ends with ' if (g_str_has_prefix(s, "'") && g_str_has_suffix(s, "'")) { // Begins and ends with '
new_s = g_utf8_substring(s, 1, g_utf8_strlen(s, -1)-1); // Start at 1 and end at n-1 new_s = g_utf8_substring(s, 1, g_utf8_strlen(s, -1) - 1); // Start at 1 and end at n-1
} else { } else {
new_s = g_strdup(s); new_s = g_strdup(s);
} }
gchar **split_on_double_single = g_strsplit(new_s, "''", -1); // Split on instances of '' gchar ** split_on_double_single = g_strsplit(new_s, "''", -1); // Split on instances of ''
new_s = g_strjoinv("'", split_on_double_single); // Rejoin as ' new_s = g_strjoinv("'", split_on_double_single); // Rejoin as '
g_strfreev(split_on_double_single); // Free our array g_strfreev(split_on_double_single); // Free our array

View file

@ -21,12 +21,30 @@
G_BEGIN_DECLS G_BEGIN_DECLS
GtkFileChooserNative* koto_utils_create_image_file_chooser(gchar *file_chooser_label); GtkFileChooserNative * koto_utils_create_image_file_chooser(gchar * file_chooser_label);
GtkWidget* koto_utils_create_image_from_filepath(gchar *filepath, gchar *fallback_icon, guint width, guint height);
gchar* koto_utils_get_filename_without_extension(gchar *filename); GtkWidget * koto_utils_create_image_from_filepath(
gboolean koto_utils_is_string_valid(gchar *str); gchar * filepath,
void koto_utils_push_queue_element_to_store(gpointer data, gpointer user_data); gchar * fallback_icon,
gchar *koto_utils_replace_string_all(gchar *str, gchar *find, gchar *repl); guint width,
gchar* koto_utils_unquote_string(gchar *s); guint height
);
gchar * koto_utils_get_filename_without_extension(gchar * filename);
gboolean koto_utils_is_string_valid(gchar * str);
void koto_utils_push_queue_element_to_store(
gpointer data,
gpointer user_data
);
gchar * koto_utils_replace_string_all(
gchar * str,
gchar * find,
gchar * repl
);
gchar * koto_utils_unquote_string(gchar * s);
G_END_DECLS G_END_DECLS

View file

@ -31,45 +31,47 @@
#include "koto-playerbar.h" #include "koto-playerbar.h"
#include "koto-window.h" #include "koto-window.h"
extern KotoActionBar *action_bar; extern KotoActionBar * action_bar;
extern KotoAddRemoveTrackPopover *koto_add_remove_track_popup; extern KotoAddRemoveTrackPopover * koto_add_remove_track_popup;
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoCreateModifyPlaylistDialog *playlist_create_modify_dialog; extern KotoCreateModifyPlaylistDialog * playlist_create_modify_dialog;
extern KotoCurrentPlaylist *current_playlist; extern KotoCurrentPlaylist * current_playlist;
extern KotoPageMusicLocal *music_local_page; extern KotoPageMusicLocal * music_local_page;
extern KotoPlaybackEngine *playback_engine; extern KotoPlaybackEngine * playback_engine;
struct _KotoWindow { struct _KotoWindow {
GtkApplicationWindow parent_instance; GtkApplicationWindow parent_instance;
KotoIndexedLibrary *library; KotoIndexedLibrary * library;
KotoCurrentPlaylist *current_playlist; KotoCurrentPlaylist * current_playlist;
KotoDialogContainer *dialogs; KotoDialogContainer * dialogs;
GtkWidget *overlay; GtkWidget * overlay;
GtkWidget *header_bar; GtkWidget * header_bar;
GtkWidget *menu_button; GtkWidget * menu_button;
GtkWidget *search_entry; GtkWidget * search_entry;
GtkWidget *primary_layout; GtkWidget * primary_layout;
GtkWidget *content_layout; GtkWidget * content_layout;
KotoNav *nav; KotoNav * nav;
GtkWidget *pages; GtkWidget * pages;
KotoPlayerBar *player_bar; KotoPlayerBar * player_bar;
}; };
G_DEFINE_TYPE (KotoWindow, koto_window, GTK_TYPE_APPLICATION_WINDOW) G_DEFINE_TYPE(KotoWindow, koto_window, GTK_TYPE_APPLICATION_WINDOW)
static void koto_window_class_init (KotoWindowClass *klass) { static void koto_window_class_init (KotoWindowClass * klass) {
(void)klass; (void) klass;
} }
static void koto_window_init (KotoWindow *self) { static void koto_window_init (KotoWindow * self) {
current_playlist = koto_current_playlist_new(); current_playlist = koto_current_playlist_new();
playback_engine = koto_playback_engine_new(); playback_engine = koto_playback_engine_new();
GtkCssProvider* provider = gtk_css_provider_new(); GtkCssProvider* provider = gtk_css_provider_new();
gtk_css_provider_load_from_resource(provider, "/com/github/joshstrobl/koto/style.css"); gtk_css_provider_load_from_resource(provider, "/com/github/joshstrobl/koto/style.css");
gtk_style_context_add_provider_for_display(gdk_display_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); gtk_style_context_add_provider_for_display(gdk_display_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
@ -115,7 +117,7 @@ static void koto_window_init (KotoWindow *self) {
action_bar = koto_action_bar_new(); // Create our Koto Action Bar action_bar = koto_action_bar_new(); // Create our Koto Action Bar
if (KOTO_IS_ACTION_BAR(action_bar)) { // Is an action bar if (KOTO_IS_ACTION_BAR(action_bar)) { // Is an action bar
GtkActionBar *bar = koto_action_bar_get_main(action_bar); GtkActionBar * bar = koto_action_bar_get_main(action_bar);
if (GTK_IS_ACTION_BAR(bar)) { if (GTK_IS_ACTION_BAR(bar)) {
gtk_box_append(GTK_BOX(self->primary_layout), GTK_WIDGET(bar)); // Add the action gtk_box_append(GTK_BOX(self->primary_layout), GTK_WIDGET(bar)); // Add the action
@ -125,7 +127,7 @@ static void koto_window_init (KotoWindow *self) {
self->player_bar = koto_playerbar_new(); self->player_bar = koto_playerbar_new();
if (KOTO_IS_PLAYERBAR(self->player_bar)) { // Is a playerbar if (KOTO_IS_PLAYERBAR(self->player_bar)) { // Is a playerbar
GtkWidget *playerbar_main = koto_playerbar_get_main(self->player_bar); GtkWidget * playerbar_main = koto_playerbar_get_main(self->player_bar);
gtk_box_append(GTK_BOX(self->primary_layout), playerbar_main); gtk_box_append(GTK_BOX(self->primary_layout), playerbar_main);
} }
@ -141,45 +143,65 @@ static void koto_window_init (KotoWindow *self) {
g_thread_new("load-library", (void*) load_library, self); g_thread_new("load-library", (void*) load_library, self);
} }
void koto_window_add_page(KotoWindow *self, gchar *page_name, GtkWidget *page) { void koto_window_add_page(
KotoWindow * self,
gchar * page_name,
GtkWidget * page
) {
gtk_stack_add_named(GTK_STACK(self->pages), page, page_name); gtk_stack_add_named(GTK_STACK(self->pages), page, page_name);
} }
void koto_window_go_to_page(KotoWindow *self, gchar *page_name) { void koto_window_go_to_page(
KotoWindow * self,
gchar * page_name
) {
gtk_stack_set_visible_child_name(GTK_STACK(self->pages), page_name); gtk_stack_set_visible_child_name(GTK_STACK(self->pages), page_name);
} }
void koto_window_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playlist, gpointer user_data) { void koto_window_handle_playlist_added(
KotoCartographer * carto,
KotoPlaylist * playlist,
gpointer user_data
) {
(void) carto; (void) carto;
if (!KOTO_IS_PLAYLIST(playlist)) { if (!KOTO_IS_PLAYLIST(playlist)) {
return; return;
} }
KotoWindow *self = user_data; KotoWindow * self = user_data;
gchar * playlist_uuid = koto_playlist_get_uuid(playlist);
KotoPlaylistPage * playlist_page = koto_playlist_page_new(playlist_uuid); // Create our new Playlist Page
gchar *playlist_uuid = koto_playlist_get_uuid(playlist);
KotoPlaylistPage *playlist_page = koto_playlist_page_new(playlist_uuid); // Create our new Playlist Page
koto_window_add_page(self, playlist_uuid, koto_playlist_page_get_main(playlist_page)); // Get the GtkScrolledWindow "main" content of the playlist page and add that as a page to our stack by the playlist UUID koto_window_add_page(self, playlist_uuid, koto_playlist_page_get_main(playlist_page)); // Get the GtkScrolledWindow "main" content of the playlist page and add that as a page to our stack by the playlist UUID
} }
void koto_window_hide_dialogs(KotoWindow *self) { void koto_window_hide_dialogs(KotoWindow * self) {
koto_dialog_container_hide(self->dialogs); // Hide the dialog container koto_dialog_container_hide(self->dialogs); // Hide the dialog container
} }
void koto_window_remove_page(KotoWindow *self, gchar *page_name) { void koto_window_remove_page(
GtkWidget *page = gtk_stack_get_child_by_name(GTK_STACK(self->pages), page_name); KotoWindow * self,
gchar * page_name
) {
GtkWidget * page = gtk_stack_get_child_by_name(GTK_STACK(self->pages), page_name);
if (GTK_IS_WIDGET(page)) { if (GTK_IS_WIDGET(page)) {
gtk_stack_remove(GTK_STACK(self->pages), page); gtk_stack_remove(GTK_STACK(self->pages), page);
} }
} }
void koto_window_show_dialog(KotoWindow *self, gchar *dialog_name) { void koto_window_show_dialog(
KotoWindow * self,
gchar * dialog_name
) {
koto_dialog_container_show_dialog(self->dialogs, dialog_name); koto_dialog_container_show_dialog(self->dialogs, dialog_name);
} }
void create_new_headerbar(KotoWindow *self) { void create_new_headerbar(KotoWindow * self) {
self->header_bar = gtk_header_bar_new(); self->header_bar = gtk_header_bar_new();
gtk_widget_add_css_class(self->header_bar, "hdr"); gtk_widget_add_css_class(self->header_bar, "hdr");
g_return_if_fail(GTK_IS_HEADER_BAR(self->header_bar)); g_return_if_fail(GTK_IS_HEADER_BAR(self->header_bar));
@ -199,8 +221,9 @@ void create_new_headerbar(KotoWindow *self) {
gtk_window_set_titlebar(GTK_WINDOW(self), self->header_bar); gtk_window_set_titlebar(GTK_WINDOW(self), self->header_bar);
} }
void load_library(KotoWindow *self) { void load_library(KotoWindow * self) {
KotoIndexedLibrary *lib = koto_indexed_library_new(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC)); KotoIndexedLibrary * lib = koto_indexed_library_new(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC));
if (lib != NULL) { if (lib != NULL) {
self->library = lib; self->library = lib;
@ -216,20 +239,26 @@ void load_library(KotoWindow *self) {
g_thread_exit(0); g_thread_exit(0);
} }
void set_optimal_default_window_size(KotoWindow *self) { void set_optimal_default_window_size(KotoWindow * self) {
GdkDisplay *default_display = gdk_display_get_default(); GdkDisplay * default_display = gdk_display_get_default();
if (!GDK_IS_X11_DISPLAY(default_display)) { // Not an X11 display if (!GDK_IS_X11_DISPLAY(default_display)) { // Not an X11 display
return; return;
} }
GdkMonitor *default_monitor = gdk_x11_display_get_primary_monitor(GDK_X11_DISPLAY(default_display)); // Get primary monitor for the X11 GdkMonitor * default_monitor = gdk_x11_display_get_primary_monitor(GDK_X11_DISPLAY(default_display)); // Get primary monitor for the X11
if (!GDK_IS_X11_MONITOR(default_monitor)) { // Not an X11 Monitor if (!GDK_IS_X11_MONITOR(default_monitor)) { // Not an X11 Monitor
return; return;
} }
GdkRectangle workarea = {0}; GdkRectangle workarea = {
0
};
gdk_monitor_get_geometry(default_monitor, &workarea); gdk_monitor_get_geometry(default_monitor, &workarea);
if (workarea.width <= 1280) { // Honestly how do you even get anything done? if (workarea.width <= 1280) { // Honestly how do you even get anything done?

View file

@ -25,18 +25,43 @@ G_BEGIN_DECLS
#define KOTO_TYPE_WINDOW (koto_window_get_type()) #define KOTO_TYPE_WINDOW (koto_window_get_type())
G_DECLARE_FINAL_TYPE (KotoWindow, koto_window, KOTO, WINDOW, GtkApplicationWindow) G_DECLARE_FINAL_TYPE(KotoWindow, koto_window, KOTO, WINDOW, GtkApplicationWindow)
void koto_window_add_page(KotoWindow *self, gchar *page_name, GtkWidget *page); void koto_window_add_page(
void koto_window_go_to_page(KotoWindow *self, gchar *page_name); KotoWindow * self,
void koto_window_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playlist, gpointer user_data); gchar * page_name,
void koto_window_hide_dialogs(KotoWindow *self); GtkWidget * page
void koto_window_remove_page(KotoWindow *self, gchar *page_name); );
void koto_window_show_dialog(KotoWindow *self, gchar *dialog_name);
void koto_window_go_to_page(
KotoWindow * self,
gchar * page_name
);
void koto_window_handle_playlist_added(
KotoCartographer * carto,
KotoPlaylist * playlist,
gpointer user_data
);
void koto_window_hide_dialogs(KotoWindow * self);
void koto_window_remove_page(
KotoWindow * self,
gchar * page_name
);
void koto_window_show_dialog(
KotoWindow * self,
gchar * dialog_name
);
void create_new_headerbar(KotoWindow * self);
void create_new_headerbar(KotoWindow *self);
void handle_album_added(); void handle_album_added();
void load_library(KotoWindow *self);
void set_optimal_default_window_size(KotoWindow *self); void load_library(KotoWindow * self);
void set_optimal_default_window_size(KotoWindow * self);
G_END_DECLS G_END_DECLS

View file

@ -27,21 +27,21 @@
#include "koto-window.h" #include "koto-window.h"
extern guint mpris_bus_id; extern guint mpris_bus_id;
extern GDBusNodeInfo *introspection_data; extern GDBusNodeInfo * introspection_data;
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern sqlite3 *koto_db; extern sqlite3 * koto_db;
extern GHashTable *supported_mimes_hash; extern GHashTable * supported_mimes_hash;
extern GList *supported_mimes; extern GList * supported_mimes;
GtkApplication *app = NULL; GtkApplication * app = NULL;
GtkWindow *main_window; GtkWindow * main_window;
static void on_activate (GtkApplication *app) { static void on_activate (GtkApplication * app) {
g_assert(GTK_IS_APPLICATION (app)); g_assert(GTK_IS_APPLICATION(app));
main_window = gtk_application_get_active_window (app); main_window = gtk_application_get_active_window(app);
if (main_window == NULL) { if (main_window == NULL) {
main_window = g_object_new(KOTO_TYPE_WINDOW, "application", app, "default-width", 1200, "default-height", 675, NULL); main_window = g_object_new(KOTO_TYPE_WINDOW, "application", app, "default-width", 1200, "default-height", 675, NULL);
setup_mpris_interfaces(); // Set up our MPRIS interfaces setup_mpris_interfaces(); // Set up our MPRIS interfaces
@ -51,20 +51,24 @@ static void on_activate (GtkApplication *app) {
gtk_window_present(main_window); gtk_window_present(main_window);
} }
static void on_shutdown(GtkApplication *app) { static void on_shutdown(GtkApplication * app) {
(void) app; (void) app;
close_db(); // Close the database close_db(); // Close the database
g_bus_unown_name(mpris_bus_id); g_bus_unown_name(mpris_bus_id);
g_dbus_node_info_unref(introspection_data); g_dbus_node_info_unref(introspection_data);
} }
int main (int argc, char *argv[]) { int main (
int argc,
char * argv[]
) {
int ret; int ret;
/* Set up gettext translations */ /* Set up gettext translations */
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE); textdomain(GETTEXT_PACKAGE);
gtk_init(); gtk_init();
gst_init(&argc, &argv); gst_init(&argc, &argv);
@ -76,10 +80,10 @@ int main (int argc, char *argv[]) {
koto_maps = koto_cartographer_new(); // Create our new cartographer and their collection of maps koto_maps = koto_cartographer_new(); // Create our new cartographer and their collection of maps
open_db(); // Open our database open_db(); // Open our database
app = gtk_application_new ("com.github.joshstrobl.koto", G_APPLICATION_FLAGS_NONE); app = gtk_application_new("com.github.joshstrobl.koto", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
g_signal_connect(app, "shutdown", G_CALLBACK(on_shutdown), NULL); g_signal_connect(app, "shutdown", G_CALLBACK(on_shutdown), NULL);
ret = g_application_run (G_APPLICATION (app), argc, argv); ret = g_application_run(G_APPLICATION(app), argc, argv);
return ret; return ret;
} }

View file

@ -25,23 +25,23 @@
#include "koto-config.h" #include "koto-config.h"
#include "koto-utils.h" #include "koto-utils.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
struct _KotoAlbumView { struct _KotoAlbumView {
GObject parent_instance; GObject parent_instance;
KotoIndexedAlbum *album; KotoIndexedAlbum * album;
GtkWidget *main; GtkWidget * main;
GtkWidget *album_tracks_box; GtkWidget * album_tracks_box;
GtkWidget *discs; GtkWidget * discs;
GtkWidget *album_overlay_art; GtkWidget * album_overlay_art;
GtkWidget *album_overlay_container; GtkWidget * album_overlay_container;
GtkWidget *album_overlay_controls; GtkWidget * album_overlay_controls;
GtkWidget *album_overlay_revealer; GtkWidget * album_overlay_revealer;
KotoButton *play_pause_button; KotoButton * play_pause_button;
GtkWidget *album_label; GtkWidget * album_label;
GHashTable *cd_to_track_listbox; GHashTable * cd_to_track_listbox;
}; };
G_DEFINE_TYPE(KotoAlbumView, koto_album_view, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoAlbumView, koto_album_view, G_TYPE_OBJECT);
@ -52,12 +52,27 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
static void koto_album_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); NULL,
static void koto_album_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); };
static void koto_album_view_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_album_view_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_album_view_class_init(KotoAlbumViewClass * c) {
GObjectClass * gobject_class;
static void koto_album_view_class_init(KotoAlbumViewClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_album_view_set_property; gobject_class->set_property = koto_album_view_set_property;
gobject_class->get_property = koto_album_view_get_property; gobject_class->get_property = koto_album_view_get_property;
@ -67,13 +82,13 @@ static void koto_album_view_class_init(KotoAlbumViewClass *c) {
"Album", "Album",
"Album", "Album",
KOTO_TYPE_INDEXED_ALBUM, KOTO_TYPE_INDEXED_ALBUM,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_album_view_init(KotoAlbumView *self) { static void koto_album_view_init(KotoAlbumView * self) {
self->cd_to_track_listbox = g_hash_table_new(g_str_hash, g_str_equal); self->cd_to_track_listbox = g_hash_table_new(g_str_hash, g_str_equal);
self->main = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); self->main = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_add_css_class(self->main, "album-view"); gtk_widget_add_css_class(self->main, "album-view");
@ -113,7 +128,9 @@ static void koto_album_view_init(KotoAlbumView *self) {
gtk_overlay_add_overlay(GTK_OVERLAY(self->album_overlay_container), self->album_overlay_revealer); // Add our revealer as the overlay gtk_overlay_add_overlay(GTK_OVERLAY(self->album_overlay_container), self->album_overlay_revealer); // Add our revealer as the overlay
gtk_box_prepend(GTK_BOX(self->main), self->album_overlay_container); // Add our album overlay container gtk_box_prepend(GTK_BOX(self->main), self->album_overlay_container); // Add our album overlay container
GtkEventController *motion_controller = gtk_event_controller_motion_new(); // Create our new motion event controller to track mouse leave and enter GtkEventController * motion_controller = gtk_event_controller_motion_new(); // Create our new motion event controller to track mouse leave and enter
g_signal_connect(motion_controller, "enter", G_CALLBACK(koto_album_view_show_overlay_controls), self); g_signal_connect(motion_controller, "enter", G_CALLBACK(koto_album_view_show_overlay_controls), self);
g_signal_connect(motion_controller, "leave", G_CALLBACK(koto_album_view_hide_overlay_controls), self); g_signal_connect(motion_controller, "leave", G_CALLBACK(koto_album_view_hide_overlay_controls), self);
gtk_widget_add_controller(self->album_overlay_container, motion_controller); gtk_widget_add_controller(self->album_overlay_container, motion_controller);
@ -121,12 +138,18 @@ static void koto_album_view_init(KotoAlbumView *self) {
koto_button_add_click_handler(self->play_pause_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_album_view_toggle_album_playback), self); koto_button_add_click_handler(self->play_pause_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_album_view_toggle_album_playback), self);
} }
GtkWidget* koto_album_view_get_main(KotoAlbumView *self) { GtkWidget * koto_album_view_get_main(KotoAlbumView * self) {
return self->main; return self->main;
} }
static void koto_album_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_album_view_get_property(
KotoAlbumView *self = KOTO_ALBUM_VIEW(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoAlbumView * self = KOTO_ALBUM_VIEW(obj);
switch (prop_id) { switch (prop_id) {
case PROP_ALBUM: case PROP_ALBUM:
@ -138,8 +161,14 @@ static void koto_album_view_get_property(GObject *obj, guint prop_id, GValue *va
} }
} }
static void koto_album_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_album_view_set_property(
KotoAlbumView *self = KOTO_ALBUM_VIEW(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoAlbumView * self = KOTO_ALBUM_VIEW(obj);
switch (prop_id) { switch (prop_id) {
case PROP_ALBUM: case PROP_ALBUM:
@ -151,58 +180,75 @@ static void koto_album_view_set_property(GObject *obj, guint prop_id, const GVal
} }
} }
void koto_album_view_add_track_to_listbox(KotoIndexedAlbum *self, KotoIndexedTrack *track) { void koto_album_view_add_track_to_listbox(
(void) self; (void) track; KotoIndexedAlbum * self,
KotoIndexedTrack * track
) {
(void) self;
(void) track;
} }
void koto_album_view_hide_overlay_controls(GtkEventControllerFocus *controller, gpointer data) { void koto_album_view_hide_overlay_controls(
GtkEventControllerFocus * controller,
gpointer data
) {
(void) controller; (void) controller;
KotoAlbumView* self = data; KotoAlbumView* self = data;
gtk_revealer_set_reveal_child(GTK_REVEALER(self->album_overlay_revealer), FALSE); gtk_revealer_set_reveal_child(GTK_REVEALER(self->album_overlay_revealer), FALSE);
} }
void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album) { void koto_album_view_set_album(
KotoAlbumView * self,
KotoIndexedAlbum * album
) {
if (album == NULL) { if (album == NULL) {
return; return;
} }
self->album = album; self->album = album;
gchar *album_art = koto_indexed_album_get_album_art(self->album); // Get the art for the album gchar * album_art = koto_indexed_album_get_album_art(self->album); // Get the art for the album
gtk_image_set_from_file(GTK_IMAGE(self->album_overlay_art), album_art); gtk_image_set_from_file(GTK_IMAGE(self->album_overlay_art), album_art);
gchar *album_name; gchar * album_name;
g_object_get(album, "name", &album_name, NULL); // Get the album name g_object_get(album, "name", &album_name, NULL); // Get the album name
self->album_label = gtk_label_new(album_name); self->album_label = gtk_label_new(album_name);
gtk_widget_set_halign(self->album_label, GTK_ALIGN_START); gtk_widget_set_halign(self->album_label, GTK_ALIGN_START);
gtk_box_prepend(GTK_BOX(self->album_tracks_box), self->album_label); // Prepend our new label to the album + tracks box gtk_box_prepend(GTK_BOX(self->album_tracks_box), self->album_label); // Prepend our new label to the album + tracks box
GHashTable *discs = g_hash_table_new(g_str_hash, g_str_equal); GHashTable * discs = g_hash_table_new(g_str_hash, g_str_equal);
GList *tracks = koto_indexed_album_get_tracks(album); // Get the tracks for this album GList * tracks = koto_indexed_album_get_tracks(album); // Get the tracks for this album
for (guint i = 0; i < g_list_length(tracks); i++) { for (guint i = 0; i < g_list_length(tracks); i++) {
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) g_list_nth_data(tracks, i)); // Get the track by its UUID KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) g_list_nth_data(tracks, i)); // Get the track by its UUID
if (track == NULL) { // Track doesn't exist if (track == NULL) { // Track doesn't exist
continue; continue;
} }
guint *disc_number; guint * disc_number;
g_object_get(track, "cd", &disc_number, NULL); g_object_get(track, "cd", &disc_number, NULL);
gchar *disc_num_as_str = g_strdup_printf("%u", GPOINTER_TO_UINT(disc_number)); gchar * disc_num_as_str = g_strdup_printf("%u", GPOINTER_TO_UINT(disc_number));
if (g_hash_table_contains(discs, disc_num_as_str)) { // Already have this added if (g_hash_table_contains(discs, disc_num_as_str)) { // Already have this added
continue; // Skip continue; // Skip
} }
g_hash_table_insert(discs, disc_num_as_str, "0"); // Mark this disc number in the hash table g_hash_table_insert(discs, disc_num_as_str, "0"); // Mark this disc number in the hash table
KotoDiscView *disc_view = koto_disc_view_new(album, disc_number); KotoDiscView * disc_view = koto_disc_view_new(album, disc_number);
gtk_list_box_append(GTK_LIST_BOX(self->discs), GTK_WIDGET(disc_view)); // Add the Disc View to the List Box gtk_list_box_append(GTK_LIST_BOX(self->discs), GTK_WIDGET(disc_view)); // Add the Disc View to the List Box
} }
if (g_list_length(g_hash_table_get_keys(discs)) == 1) { // Only have one album if (g_list_length(g_hash_table_get_keys(discs)) == 1) { // Only have one album
GtkListBoxRow *row = gtk_list_box_get_row_at_index(GTK_LIST_BOX(self->discs), 0); // Get the first item GtkListBoxRow * row = gtk_list_box_get_row_at_index(GTK_LIST_BOX(self->discs), 0); // Get the first item
if (GTK_IS_LIST_BOX_ROW(row)) { // Is a row if (GTK_IS_LIST_BOX_ROW(row)) { // Is a row
koto_disc_view_set_disc_label_visible((KotoDiscView*) gtk_list_box_row_get_child(row), FALSE); // Get koto_disc_view_set_disc_label_visible((KotoDiscView*) gtk_list_box_row_get_child(row), FALSE); // Get
@ -212,21 +258,30 @@ void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album) {
g_hash_table_destroy(discs); g_hash_table_destroy(discs);
} }
void koto_album_view_show_overlay_controls(GtkEventControllerFocus *controller, gpointer data) { void koto_album_view_show_overlay_controls(
GtkEventControllerFocus * controller,
gpointer data
) {
(void) controller; (void) controller;
KotoAlbumView* self = data; KotoAlbumView* self = data;
gtk_revealer_set_reveal_child(GTK_REVEALER(self->album_overlay_revealer), TRUE); gtk_revealer_set_reveal_child(GTK_REVEALER(self->album_overlay_revealer), TRUE);
} }
int koto_album_view_sort_discs(GtkListBoxRow *disc1, GtkListBoxRow *disc2, gpointer user_data) { int koto_album_view_sort_discs(
GtkListBoxRow * disc1,
GtkListBoxRow * disc2,
gpointer user_data
) {
(void) user_data; (void) user_data;
KotoDiscView *disc1_item = KOTO_DISC_VIEW(gtk_list_box_row_get_child(disc1)); KotoDiscView * disc1_item = KOTO_DISC_VIEW(gtk_list_box_row_get_child(disc1));
KotoDiscView *disc2_item = KOTO_DISC_VIEW(gtk_list_box_row_get_child(disc2)); KotoDiscView * disc2_item = KOTO_DISC_VIEW(gtk_list_box_row_get_child(disc2));
guint disc1_num; guint disc1_num;
guint disc2_num; guint disc2_num;
g_object_get(disc1_item, "disc", &disc1_num, NULL); g_object_get(disc1_item, "disc", &disc1_num, NULL);
g_object_get(disc2_item, "disc", &disc2_num, NULL); g_object_get(disc2_item, "disc", &disc2_num, NULL);
@ -239,14 +294,24 @@ int koto_album_view_sort_discs(GtkListBoxRow *disc1, GtkListBoxRow *disc2, gpoin
} }
} }
void koto_album_view_toggle_album_playback(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { void koto_album_view_toggle_album_playback(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoAlbumView* self = data; KotoAlbumView* self = data;
koto_button_show_image(KOTO_BUTTON(self->play_pause_button), TRUE); koto_button_show_image(KOTO_BUTTON(self->play_pause_button), TRUE);
koto_indexed_album_set_as_current_playlist(self->album); // Set as the current playlist koto_indexed_album_set_as_current_playlist(self->album); // Set as the current playlist
} }
KotoAlbumView* koto_album_view_new(KotoIndexedAlbum *album) { KotoAlbumView * koto_album_view_new(KotoIndexedAlbum * album) {
return g_object_new(KOTO_TYPE_ALBUM_VIEW, "album", album, NULL); return g_object_new(KOTO_TYPE_ALBUM_VIEW, "album", album, NULL);
} }

View file

@ -27,13 +27,41 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(KotoAlbumView, koto_album_view, KOTO, ALBUM_VIEW, GObject) G_DECLARE_FINAL_TYPE(KotoAlbumView, koto_album_view, KOTO, ALBUM_VIEW, GObject)
KotoAlbumView* koto_album_view_new(KotoIndexedAlbum *album); KotoAlbumView* koto_album_view_new(KotoIndexedAlbum * album);
GtkWidget* koto_album_view_get_main(KotoAlbumView *self); GtkWidget * koto_album_view_get_main(KotoAlbumView * self);
void koto_album_view_add_track_to_listbox(KotoIndexedAlbum *self, KotoIndexedTrack *track);
void koto_album_view_hide_overlay_controls(GtkEventControllerFocus *controller, gpointer data); void koto_album_view_add_track_to_listbox(
void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album); KotoIndexedAlbum * self,
void koto_album_view_show_overlay_controls(GtkEventControllerFocus *controller, gpointer data); KotoIndexedTrack * track
void koto_album_view_toggle_album_playback(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); );
int koto_album_view_sort_discs(GtkListBoxRow *track1, GtkListBoxRow *track2, gpointer user_data);
void koto_album_view_hide_overlay_controls(
GtkEventControllerFocus * controller,
gpointer data
);
void koto_album_view_set_album(
KotoAlbumView * self,
KotoIndexedAlbum * album
);
void koto_album_view_show_overlay_controls(
GtkEventControllerFocus * controller,
gpointer data
);
void koto_album_view_toggle_album_playback(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
int koto_album_view_sort_discs(
GtkListBoxRow * track1,
GtkListBoxRow * track2,
gpointer user_data
);
G_END_DECLS G_END_DECLS

View file

@ -24,17 +24,17 @@
#include "koto-config.h" #include "koto-config.h"
#include "koto-utils.h" #include "koto-utils.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
struct _KotoArtistView { struct _KotoArtistView {
GObject parent_instance; GObject parent_instance;
KotoIndexedArtist *artist; KotoIndexedArtist * artist;
GtkWidget *scrolled_window; GtkWidget * scrolled_window;
GtkWidget *content; GtkWidget * content;
GtkWidget *favorites_list; GtkWidget * favorites_list;
GtkWidget *album_list; GtkWidget * album_list;
GHashTable *albums_to_component; GHashTable * albums_to_component;
gboolean constructed; gboolean constructed;
}; };
@ -47,13 +47,29 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
static void koto_artist_view_constructed(GObject *obj); NULL,
static void koto_artist_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); };
static void koto_artist_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); static void koto_artist_view_constructed(GObject * obj);
static void koto_artist_view_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_artist_view_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_artist_view_class_init(KotoArtistViewClass * c) {
GObjectClass * gobject_class;
static void koto_artist_view_class_init(KotoArtistViewClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->constructed = koto_artist_view_constructed; gobject_class->constructed = koto_artist_view_constructed;
gobject_class->set_property = koto_artist_view_set_property; gobject_class->set_property = koto_artist_view_set_property;
@ -64,14 +80,20 @@ static void koto_artist_view_class_init(KotoArtistViewClass *c) {
"Artist", "Artist",
"Artist", "Artist",
KOTO_TYPE_INDEXED_ARTIST, KOTO_TYPE_INDEXED_ARTIST,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_artist_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_artist_view_get_property(
KotoArtistView *self = KOTO_ARTIST_VIEW(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoArtistView * self = KOTO_ARTIST_VIEW(obj);
switch (prop_id) { switch (prop_id) {
case PROP_ARTIST: case PROP_ARTIST:
@ -83,8 +105,14 @@ static void koto_artist_view_get_property(GObject *obj, guint prop_id, GValue *v
} }
} }
static void koto_artist_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_artist_view_set_property(
KotoArtistView *self = KOTO_ARTIST_VIEW(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoArtistView * self = KOTO_ARTIST_VIEW(obj);
switch (prop_id) { switch (prop_id) {
case PROP_ARTIST: case PROP_ARTIST:
@ -96,13 +124,15 @@ static void koto_artist_view_set_property(GObject *obj, guint prop_id, const GVa
} }
} }
static void koto_artist_view_init(KotoArtistView *self) { static void koto_artist_view_init(KotoArtistView * self) {
self->artist = NULL; self->artist = NULL;
self->constructed = FALSE; self->constructed = FALSE;
} }
static void koto_artist_view_constructed(GObject *obj) { static void koto_artist_view_constructed(GObject * obj) {
KotoArtistView *self = KOTO_ARTIST_VIEW(obj); KotoArtistView * self = KOTO_ARTIST_VIEW(obj);
self->albums_to_component = g_hash_table_new(g_str_hash, g_str_equal); self->albums_to_component = g_hash_table_new(g_str_hash, g_str_equal);
self->scrolled_window = gtk_scrolled_window_new(); // Create our scrolled window self->scrolled_window = gtk_scrolled_window_new(); // Create our scrolled window
self->content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); // Create our content as a GtkBox self->content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); // Create our content as a GtkBox
@ -132,23 +162,33 @@ static void koto_artist_view_constructed(GObject *obj) {
gtk_widget_set_hexpand(GTK_WIDGET(self->favorites_list), TRUE); gtk_widget_set_hexpand(GTK_WIDGET(self->favorites_list), TRUE);
gtk_widget_set_hexpand(GTK_WIDGET(self->album_list), TRUE); gtk_widget_set_hexpand(GTK_WIDGET(self->album_list), TRUE);
G_OBJECT_CLASS (koto_artist_view_parent_class)->constructed (obj); G_OBJECT_CLASS(koto_artist_view_parent_class)->constructed(obj);
self->constructed = TRUE; self->constructed = TRUE;
} }
void koto_artist_view_add_album(KotoArtistView *self, KotoIndexedAlbum *album) { void koto_artist_view_add_album(
gchar *album_art = koto_indexed_album_get_album_art(album); // Get the art for the album KotoArtistView * self,
KotoIndexedAlbum * album
) {
gchar * album_art = koto_indexed_album_get_album_art(album); // Get the art for the album
GtkWidget * art_image = koto_utils_create_image_from_filepath(album_art, "audio-x-generic-symbolic", 220, 220);
GtkWidget *art_image = koto_utils_create_image_from_filepath(album_art, "audio-x-generic-symbolic", 220, 220);
gtk_widget_set_halign(art_image, GTK_ALIGN_START); // Align to start gtk_widget_set_halign(art_image, GTK_ALIGN_START); // Align to start
gtk_flow_box_insert(GTK_FLOW_BOX(self->favorites_list), art_image, -1); // Append the album art gtk_flow_box_insert(GTK_FLOW_BOX(self->favorites_list), art_image, -1); // Append the album art
KotoAlbumView* album_view = koto_album_view_new(album); // Create our new album view KotoAlbumView* album_view = koto_album_view_new(album); // Create our new album view
GtkWidget* album_view_main = koto_album_view_get_main(album_view); GtkWidget* album_view_main = koto_album_view_get_main(album_view);
gtk_flow_box_insert(GTK_FLOW_BOX(self->album_list), album_view_main, -1); // Append the album view to the album list gtk_flow_box_insert(GTK_FLOW_BOX(self->album_list), album_view_main, -1); // Append the album view to the album list
} }
void koto_artist_view_add_artist(KotoArtistView *self, KotoIndexedArtist *artist) { void koto_artist_view_add_artist(
KotoArtistView * self,
KotoIndexedArtist * artist
) {
if (artist == NULL) { if (artist == NULL) {
return; return;
} }
@ -159,11 +199,13 @@ void koto_artist_view_add_artist(KotoArtistView *self, KotoIndexedArtist *artist
return; return;
} }
GList *albums = koto_indexed_artist_get_albums(self->artist); // Get the albums GList * albums = koto_indexed_artist_get_albums(self->artist); // Get the albums
GList * a;
GList *a;
for (a = albums; a != NULL; a = a->next) { for (a = albums; a != NULL; a = a->next) {
KotoIndexedAlbum *album = koto_cartographer_get_album_by_uuid(koto_maps, (gchar*) a->data); KotoIndexedAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, (gchar*) a->data);
if (album != NULL) { if (album != NULL) {
koto_artist_view_add_album(self, album); // Add the album koto_artist_view_add_album(self, album); // Add the album
@ -171,10 +213,10 @@ void koto_artist_view_add_artist(KotoArtistView *self, KotoIndexedArtist *artist
} }
} }
GtkWidget* koto_artist_view_get_main(KotoArtistView *self) { GtkWidget * koto_artist_view_get_main(KotoArtistView * self) {
return self->scrolled_window; return self->scrolled_window;
} }
KotoArtistView* koto_artist_view_new() { KotoArtistView * koto_artist_view_new() {
return g_object_new(KOTO_TYPE_ARTIST_VIEW, NULL); return g_object_new(KOTO_TYPE_ARTIST_VIEW, NULL);
} }

View file

@ -25,11 +25,19 @@ G_BEGIN_DECLS
#define KOTO_TYPE_ARTIST_VIEW (koto_artist_view_get_type()) #define KOTO_TYPE_ARTIST_VIEW (koto_artist_view_get_type())
G_DECLARE_FINAL_TYPE (KotoArtistView, koto_artist_view, KOTO, ARTIST_VIEW, GObject) G_DECLARE_FINAL_TYPE(KotoArtistView, koto_artist_view, KOTO, ARTIST_VIEW, GObject)
KotoArtistView* koto_artist_view_new(); KotoArtistView* koto_artist_view_new();
void koto_artist_view_add_album(KotoArtistView *self, KotoIndexedAlbum *album); void koto_artist_view_add_album(
void koto_artist_view_add_artist(KotoArtistView *self, KotoIndexedArtist *artist); KotoArtistView * self,
GtkWidget* koto_artist_view_get_main(KotoArtistView *self); KotoIndexedAlbum * album
);
void koto_artist_view_add_artist(
KotoArtistView * self,
KotoIndexedArtist * artist
);
GtkWidget * koto_artist_view_get_main(KotoArtistView * self);
G_END_DECLS G_END_DECLS

View file

@ -23,17 +23,17 @@
#include "../../koto-utils.h" #include "../../koto-utils.h"
#include "disc-view.h" #include "disc-view.h"
extern KotoActionBar *action_bar; extern KotoActionBar * action_bar;
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
struct _KotoDiscView { struct _KotoDiscView {
GtkBox parent_instance; GtkBox parent_instance;
KotoIndexedAlbum *album; KotoIndexedAlbum * album;
GtkWidget *header; GtkWidget * header;
GtkWidget *label; GtkWidget * label;
GtkWidget *list; GtkWidget * list;
guint *disc_number; guint * disc_number;
}; };
G_DEFINE_TYPE(KotoDiscView, koto_disc_view, GTK_TYPE_BOX); G_DEFINE_TYPE(KotoDiscView, koto_disc_view, GTK_TYPE_BOX);
@ -45,12 +45,27 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
static void koto_disc_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); NULL,
static void koto_disc_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); };
static void koto_disc_view_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_disc_view_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_disc_view_class_init(KotoDiscViewClass * c) {
GObjectClass * gobject_class;
static void koto_disc_view_class_init(KotoDiscViewClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_disc_view_set_property; gobject_class->set_property = koto_disc_view_set_property;
gobject_class->get_property = koto_disc_view_get_property; gobject_class->get_property = koto_disc_view_get_property;
@ -62,7 +77,7 @@ static void koto_disc_view_class_init(KotoDiscViewClass *c) {
0, 0,
G_MAXUINT16, G_MAXUINT16,
1, 1,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_ALBUM] = g_param_spec_object( props[PROP_ALBUM] = g_param_spec_object(
@ -70,14 +85,20 @@ static void koto_disc_view_class_init(KotoDiscViewClass *c) {
"Album", "Album",
"Album", "Album",
KOTO_TYPE_INDEXED_ALBUM, KOTO_TYPE_INDEXED_ALBUM,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_disc_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_disc_view_get_property(
KotoDiscView *self = KOTO_DISC_VIEW(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoDiscView * self = KOTO_DISC_VIEW(obj);
switch (prop_id) { switch (prop_id) {
case PROP_DISC: case PROP_DISC:
@ -92,8 +113,14 @@ static void koto_disc_view_get_property(GObject *obj, guint prop_id, GValue *val
} }
} }
static void koto_disc_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_disc_view_set_property(
KotoDiscView *self = KOTO_DISC_VIEW(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoDiscView * self = KOTO_DISC_VIEW(obj);
switch (prop_id) { switch (prop_id) {
case PROP_DISC: case PROP_DISC:
@ -108,14 +135,16 @@ static void koto_disc_view_set_property(GObject *obj, guint prop_id, const GValu
} }
} }
static void koto_disc_view_init(KotoDiscView *self) { static void koto_disc_view_init(KotoDiscView * self) {
gtk_widget_add_css_class(GTK_WIDGET(self), "disc-view"); gtk_widget_add_css_class(GTK_WIDGET(self), "disc-view");
gtk_widget_set_can_focus(GTK_WIDGET(self), FALSE); gtk_widget_set_can_focus(GTK_WIDGET(self), FALSE);
self->header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); self->header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_hexpand(self->header, TRUE); gtk_widget_set_hexpand(self->header, TRUE);
gtk_widget_set_size_request(self->header, 16, -1); gtk_widget_set_size_request(self->header, 16, -1);
GtkWidget *ico = gtk_image_new_from_icon_name("drive-optical-symbolic"); GtkWidget * ico = gtk_image_new_from_icon_name("drive-optical-symbolic");
gtk_box_prepend(GTK_BOX(self->header), ico); gtk_box_prepend(GTK_BOX(self->header), ico);
self->label = gtk_label_new(NULL); // Create an empty label self->label = gtk_label_new(NULL); // Create an empty label
@ -137,41 +166,55 @@ static void koto_disc_view_init(KotoDiscView *self) {
g_signal_connect(self->list, "selected-rows-changed", G_CALLBACK(koto_disc_view_handle_selected_rows_changed), self); g_signal_connect(self->list, "selected-rows-changed", G_CALLBACK(koto_disc_view_handle_selected_rows_changed), self);
} }
void koto_disc_view_list_tracks(gpointer data, gpointer selfptr) { void koto_disc_view_list_tracks(
KotoDiscView *self = (KotoDiscView*) selfptr; gpointer data,
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) data); // Get the track by its UUID gpointer selfptr
) {
KotoDiscView * self = (KotoDiscView*) selfptr;
KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) data); // Get the track by its UUID
guint * disc_number;
guint *disc_number;
g_object_get(track, "cd", &disc_number, NULL); // get the disc number g_object_get(track, "cd", &disc_number, NULL); // get the disc number
if (GPOINTER_TO_UINT(self->disc_number) != GPOINTER_TO_UINT(disc_number)) { // Track does not belong to this CD if (GPOINTER_TO_UINT(self->disc_number) != GPOINTER_TO_UINT(disc_number)) { // Track does not belong to this CD
return; return;
} }
KotoTrackItem *track_item = koto_track_item_new(track); // Create our new track item KotoTrackItem * track_item = koto_track_item_new(track); // Create our new track item
gtk_list_box_append(GTK_LIST_BOX(self->list), GTK_WIDGET(track_item)); // Add to our tracks list box gtk_list_box_append(GTK_LIST_BOX(self->list), GTK_WIDGET(track_item)); // Add to our tracks list box
} }
void koto_disc_view_handle_selected_rows_changed(GtkListBox *box, gpointer user_data) { void koto_disc_view_handle_selected_rows_changed(
KotoDiscView *self = user_data; GtkListBox * box,
gpointer user_data
) {
KotoDiscView * self = user_data;
gchar * album_uuid = koto_indexed_album_get_album_uuid(self->album); // Get the UUID
gchar *album_uuid = koto_indexed_album_get_album_uuid(self->album); // Get the UUID
if (!koto_utils_is_string_valid(album_uuid)) { // Not set if (!koto_utils_is_string_valid(album_uuid)) { // Not set
return; return;
} }
GList *selected_rows = gtk_list_box_get_selected_rows(box); // Get the selected rows GList * selected_rows = gtk_list_box_get_selected_rows(box); // Get the selected rows
if (g_list_length(selected_rows) == 0) { // No rows selected if (g_list_length(selected_rows) == 0) { // No rows selected
koto_action_bar_toggle_reveal(action_bar, FALSE); // Close the action bar koto_action_bar_toggle_reveal(action_bar, FALSE); // Close the action bar
return; return;
} }
GList *selected_tracks = NULL; // Create our list of KotoIndexedTracks GList * selected_tracks = NULL; // Create our list of KotoIndexedTracks
GList *cur_selected_rows; GList * cur_selected_rows;
for (cur_selected_rows = selected_rows; cur_selected_rows != NULL; cur_selected_rows = cur_selected_rows->next) { // Iterate over the rows for (cur_selected_rows = selected_rows; cur_selected_rows != NULL; cur_selected_rows = cur_selected_rows->next) { // Iterate over the rows
KotoTrackItem *track_item = (KotoTrackItem*) gtk_list_box_row_get_child(cur_selected_rows->data); KotoTrackItem * track_item = (KotoTrackItem*) gtk_list_box_row_get_child(cur_selected_rows->data);
selected_tracks = g_list_append(selected_tracks, koto_track_item_get_track(track_item)); // Add the KotoIndexedTrack to our list selected_tracks = g_list_append(selected_tracks, koto_track_item_get_track(track_item)); // Add the KotoIndexedTrack to our list
} }
@ -181,7 +224,10 @@ void koto_disc_view_handle_selected_rows_changed(GtkListBox *box, gpointer user_
koto_action_bar_toggle_reveal(action_bar, TRUE); // Show the action bar koto_action_bar_toggle_reveal(action_bar, TRUE); // Show the action bar
} }
void koto_disc_view_set_album(KotoDiscView *self, KotoIndexedAlbum *album) { void koto_disc_view_set_album(
KotoDiscView * self,
KotoIndexedAlbum * album
) {
if (album == NULL) { if (album == NULL) {
return; return;
} }
@ -195,28 +241,43 @@ void koto_disc_view_set_album(KotoDiscView *self, KotoIndexedAlbum *album) {
g_list_foreach(koto_indexed_album_get_tracks(self->album), koto_disc_view_list_tracks, self); g_list_foreach(koto_indexed_album_get_tracks(self->album), koto_disc_view_list_tracks, self);
} }
void koto_disc_view_set_disc_number(KotoDiscView *self, guint disc_number) { void koto_disc_view_set_disc_number(
KotoDiscView * self,
guint disc_number
) {
if (disc_number == 0) { if (disc_number == 0) {
return; return;
} }
self->disc_number = GUINT_TO_POINTER(disc_number); self->disc_number = GUINT_TO_POINTER(disc_number);
gchar *disc_label = g_strdup_printf("Disc %u", disc_number); gchar * disc_label = g_strdup_printf("Disc %u", disc_number);
gtk_label_set_text(GTK_LABEL(self->label), disc_label); // Set the label gtk_label_set_text(GTK_LABEL(self->label), disc_label); // Set the label
g_free(disc_label); g_free(disc_label);
} }
void koto_disc_view_set_disc_label_visible(KotoDiscView *self, gboolean visible) { void koto_disc_view_set_disc_label_visible(
KotoDiscView * self,
gboolean visible
) {
(visible) ? gtk_widget_show(self->header) : gtk_widget_hide(self->header); (visible) ? gtk_widget_show(self->header) : gtk_widget_hide(self->header);
} }
KotoDiscView* koto_disc_view_new(KotoIndexedAlbum *album, guint *disc_number) { KotoDiscView * koto_disc_view_new(
return g_object_new(KOTO_TYPE_DISC_VIEW, KotoIndexedAlbum * album,
"disc", disc_number, guint * disc_number
"album", album, ) {
"orientation", GTK_ORIENTATION_VERTICAL, return g_object_new(
KOTO_TYPE_DISC_VIEW,
"disc",
disc_number,
"album",
album,
"orientation",
GTK_ORIENTATION_VERTICAL,
NULL NULL
); );
} }

View file

@ -27,11 +27,30 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(KotoDiscView, koto_disc_view, KOTO, DISC_VIEW, GtkBox) G_DECLARE_FINAL_TYPE(KotoDiscView, koto_disc_view, KOTO, DISC_VIEW, GtkBox)
KotoDiscView* koto_disc_view_new(KotoIndexedAlbum *album, guint *disc); KotoDiscView* koto_disc_view_new(KotoIndexedAlbum * album, guint * disc);
void koto_disc_view_handle_selected_rows_changed(GtkListBox *box, gpointer user_data); void koto_disc_view_handle_selected_rows_changed(
void koto_disc_view_list_tracks(gpointer data, gpointer selfptr); GtkListBox * box,
void koto_disc_view_set_album(KotoDiscView *self, KotoIndexedAlbum *album); gpointer user_data
void koto_disc_view_set_disc_label_visible(KotoDiscView *self, gboolean visible); );
void koto_disc_view_set_disc_number(KotoDiscView *self, guint disc_number);
void koto_disc_view_list_tracks(
gpointer data,
gpointer selfptr
);
void koto_disc_view_set_album(
KotoDiscView * self,
KotoIndexedAlbum * album
);
void koto_disc_view_set_disc_label_visible(
KotoDiscView * self,
gboolean visible
);
void koto_disc_view_set_disc_number(
KotoDiscView * self,
guint disc_number
);
G_END_DECLS G_END_DECLS

View file

@ -24,7 +24,7 @@
#include "../../koto-utils.h" #include "../../koto-utils.h"
#include "music-local.h" #include "music-local.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
enum { enum {
PROP_0, PROP_0,
@ -32,15 +32,17 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
NULL,
};
struct _KotoPageMusicLocal { struct _KotoPageMusicLocal {
GtkBox parent_instance; GtkBox parent_instance;
GtkWidget *scrolled_window; GtkWidget * scrolled_window;
GtkWidget *artist_list; GtkWidget * artist_list;
GtkWidget *stack; GtkWidget * stack;
KotoIndexedLibrary *lib; KotoIndexedLibrary * lib;
gboolean constructed; gboolean constructed;
}; };
@ -50,14 +52,28 @@ struct _KotoPageMusicLocalClass {
G_DEFINE_TYPE(KotoPageMusicLocal, koto_page_music_local, GTK_TYPE_BOX); G_DEFINE_TYPE(KotoPageMusicLocal, koto_page_music_local, GTK_TYPE_BOX);
KotoPageMusicLocal *music_local_page; KotoPageMusicLocal * music_local_page;
static void koto_page_music_local_constructed(GObject * obj);
static void koto_page_music_local_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_page_music_local_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_page_music_local_class_init(KotoPageMusicLocalClass * c) {
GObjectClass * gobject_class;
static void koto_page_music_local_constructed(GObject *obj);
static void koto_page_music_local_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_page_music_local_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_page_music_local_class_init(KotoPageMusicLocalClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->constructed = koto_page_music_local_constructed; gobject_class->constructed = koto_page_music_local_constructed;
gobject_class->set_property = koto_page_music_local_set_property; gobject_class->set_property = koto_page_music_local_set_property;
@ -68,14 +84,20 @@ static void koto_page_music_local_class_init(KotoPageMusicLocalClass *c) {
"Library", "Library",
"Library", "Library",
KOTO_TYPE_INDEXED_LIBRARY, KOTO_TYPE_INDEXED_LIBRARY,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_page_music_local_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_page_music_local_get_property(
KotoPageMusicLocal *self = KOTO_PAGE_MUSIC_LOCAL(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoPageMusicLocal * self = KOTO_PAGE_MUSIC_LOCAL(obj);
switch (prop_id) { switch (prop_id) {
case PROP_LIB: case PROP_LIB:
@ -87,8 +109,14 @@ static void koto_page_music_local_get_property(GObject *obj, guint prop_id, GVal
} }
} }
static void koto_page_music_local_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_page_music_local_set_property(
KotoPageMusicLocal *self = KOTO_PAGE_MUSIC_LOCAL(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoPageMusicLocal * self = KOTO_PAGE_MUSIC_LOCAL(obj);
switch (prop_id) { switch (prop_id) {
case PROP_LIB: case PROP_LIB:
@ -100,7 +128,7 @@ static void koto_page_music_local_set_property(GObject *obj, guint prop_id, cons
} }
} }
static void koto_page_music_local_init(KotoPageMusicLocal *self) { static void koto_page_music_local_init(KotoPageMusicLocal * self) {
self->lib = NULL; self->lib = NULL;
self->constructed = FALSE; self->constructed = FALSE;
@ -130,36 +158,55 @@ static void koto_page_music_local_init(KotoPageMusicLocal *self) {
gtk_box_append(GTK_BOX(self), self->stack); gtk_box_append(GTK_BOX(self), self->stack);
} }
static void koto_page_music_local_constructed(GObject *obj) { static void koto_page_music_local_constructed(GObject * obj) {
KotoPageMusicLocal *self = KOTO_PAGE_MUSIC_LOCAL(obj); KotoPageMusicLocal * self = KOTO_PAGE_MUSIC_LOCAL(obj);
G_OBJECT_CLASS (koto_page_music_local_parent_class)->constructed (obj);
G_OBJECT_CLASS(koto_page_music_local_parent_class)->constructed(obj);
self->constructed = TRUE; self->constructed = TRUE;
} }
void koto_page_music_local_add_artist(KotoPageMusicLocal *self, KotoIndexedArtist *artist) { void koto_page_music_local_add_artist(
gchar *artist_name; KotoPageMusicLocal * self,
KotoIndexedArtist * artist
) {
gchar * artist_name;
g_object_get(artist, "name", &artist_name, NULL); g_object_get(artist, "name", &artist_name, NULL);
KotoButton *artist_button = koto_button_new_plain(artist_name); KotoButton * artist_button = koto_button_new_plain(artist_name);
gtk_list_box_prepend(GTK_LIST_BOX(self->artist_list), GTK_WIDGET(artist_button)); gtk_list_box_prepend(GTK_LIST_BOX(self->artist_list), GTK_WIDGET(artist_button));
KotoArtistView *artist_view = koto_artist_view_new(); // Create our new artist view KotoArtistView * artist_view = koto_artist_view_new(); // Create our new artist view
koto_artist_view_add_artist(artist_view, artist); // Add the artist koto_artist_view_add_artist(artist_view, artist); // Add the artist
gtk_stack_add_named(GTK_STACK(self->stack), koto_artist_view_get_main(artist_view), artist_name); gtk_stack_add_named(GTK_STACK(self->stack), koto_artist_view_get_main(artist_view), artist_name);
} }
void koto_page_music_local_go_to_artist_by_name(KotoPageMusicLocal *self, gchar *artist_name) { void koto_page_music_local_go_to_artist_by_name(
KotoPageMusicLocal * self,
gchar * artist_name
) {
gtk_stack_set_visible_child_name(GTK_STACK(self->stack), artist_name); gtk_stack_set_visible_child_name(GTK_STACK(self->stack), artist_name);
} }
void koto_page_music_local_go_to_artist_by_uuid(KotoPageMusicLocal *self, gchar *artist_uuid) { void koto_page_music_local_go_to_artist_by_uuid(
KotoIndexedArtist *artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid); // Get the artist KotoPageMusicLocal * self,
gchar * artist_uuid
) {
KotoIndexedArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid); // Get the artist
if (!KOTO_IS_INDEXED_ARTIST(artist)) { // No artist for this UUID if (!KOTO_IS_INDEXED_ARTIST(artist)) { // No artist for this UUID
return; return;
} }
gchar *artist_name = NULL; gchar * artist_name = NULL;
g_object_get( g_object_get(
artist, artist,
"name", "name",
@ -174,17 +221,26 @@ void koto_page_music_local_go_to_artist_by_uuid(KotoPageMusicLocal *self, gchar
koto_page_music_local_go_to_artist_by_name(self, artist_name); koto_page_music_local_go_to_artist_by_name(self, artist_name);
} }
void koto_page_music_local_handle_artist_click(GtkListBox *box, GtkListBoxRow *row, gpointer data) { void koto_page_music_local_handle_artist_click(
GtkListBox * box,
GtkListBoxRow * row,
gpointer data
) {
(void) box; (void) box;
KotoPageMusicLocal *self = (KotoPageMusicLocal*) data; KotoPageMusicLocal * self = (KotoPageMusicLocal*) data;
KotoButton *btn = KOTO_BUTTON(gtk_list_box_row_get_child(row)); KotoButton * btn = KOTO_BUTTON(gtk_list_box_row_get_child(row));
gchar * artist_name;
gchar *artist_name;
g_object_get(btn, "button-text", &artist_name, NULL); g_object_get(btn, "button-text", &artist_name, NULL);
koto_page_music_local_go_to_artist_by_name(self, artist_name); koto_page_music_local_go_to_artist_by_name(self, artist_name);
} }
void koto_page_music_local_set_library(KotoPageMusicLocal *self, KotoIndexedLibrary *lib) { void koto_page_music_local_set_library(
KotoPageMusicLocal * self,
KotoIndexedLibrary * lib
) {
if (lib == NULL) { if (lib == NULL) {
return; return;
} }
@ -203,11 +259,12 @@ void koto_page_music_local_set_library(KotoPageMusicLocal *self, KotoIndexedLibr
gpointer artist_key; gpointer artist_key;
gpointer artist_data; gpointer artist_data;
GHashTable *artists = koto_indexed_library_get_artists(self->lib); // Get the artists GHashTable * artists = koto_indexed_library_get_artists(self->lib); // Get the artists
g_hash_table_iter_init(&artist_list_iter, artists); g_hash_table_iter_init(&artist_list_iter, artists);
while (g_hash_table_iter_next(&artist_list_iter, &artist_key, &artist_data)) { // For each of the music artists while (g_hash_table_iter_next(&artist_list_iter, &artist_key, &artist_data)) { // For each of the music artists
KotoIndexedArtist *artist = koto_cartographer_get_artist_by_uuid(koto_maps, (gchar*) artist_data); // Cast our data as a KotoIndexedArtist KotoIndexedArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, (gchar*) artist_data); // Cast our data as a KotoIndexedArtist
if (artist != NULL) { if (artist != NULL) {
koto_page_music_local_add_artist(self, artist); koto_page_music_local_add_artist(self, artist);
@ -215,13 +272,18 @@ void koto_page_music_local_set_library(KotoPageMusicLocal *self, KotoIndexedLibr
} }
} }
int koto_page_music_local_sort_artists(GtkListBoxRow *artist1, GtkListBoxRow *artist2, gpointer user_data) { int koto_page_music_local_sort_artists(
GtkListBoxRow * artist1,
GtkListBoxRow * artist2,
gpointer user_data
) {
(void) user_data; (void) user_data;
KotoButton *artist1_btn = KOTO_BUTTON(gtk_list_box_row_get_child(artist1)); KotoButton * artist1_btn = KOTO_BUTTON(gtk_list_box_row_get_child(artist1));
KotoButton *artist2_btn = KOTO_BUTTON(gtk_list_box_row_get_child(artist2)); KotoButton * artist2_btn = KOTO_BUTTON(gtk_list_box_row_get_child(artist2));
gchar * artist1_text;
gchar * artist2_text;
gchar *artist1_text;
gchar *artist2_text;
g_object_get(artist1_btn, "button-text", &artist1_text, NULL); g_object_get(artist1_btn, "button-text", &artist1_text, NULL);
g_object_get(artist2_btn, "button-text", &artist2_text, NULL); g_object_get(artist2_btn, "button-text", &artist2_text, NULL);
@ -229,9 +291,11 @@ int koto_page_music_local_sort_artists(GtkListBoxRow *artist1, GtkListBoxRow *ar
return g_utf8_collate(artist1_text, artist2_text); return g_utf8_collate(artist1_text, artist2_text);
} }
KotoPageMusicLocal* koto_page_music_local_new() { KotoPageMusicLocal * koto_page_music_local_new() {
return g_object_new(KOTO_TYPE_PAGE_MUSIC_LOCAL, return g_object_new(
"orientation", GTK_ORIENTATION_HORIZONTAL, KOTO_TYPE_PAGE_MUSIC_LOCAL,
"orientation",
GTK_ORIENTATION_HORIZONTAL,
NULL NULL
); );
} }

View file

@ -26,14 +26,39 @@ G_BEGIN_DECLS
#define KOTO_TYPE_PAGE_MUSIC_LOCAL (koto_page_music_local_get_type()) #define KOTO_TYPE_PAGE_MUSIC_LOCAL (koto_page_music_local_get_type())
G_DECLARE_FINAL_TYPE (KotoPageMusicLocal, koto_page_music_local, KOTO, PAGE_MUSIC_LOCAL, GtkBox) G_DECLARE_FINAL_TYPE(KotoPageMusicLocal, koto_page_music_local, KOTO, PAGE_MUSIC_LOCAL, GtkBox)
KotoPageMusicLocal* koto_page_music_local_new(); KotoPageMusicLocal* koto_page_music_local_new();
void koto_page_music_local_add_artist(KotoPageMusicLocal *self, KotoIndexedArtist *artist); void koto_page_music_local_add_artist(
void koto_page_music_local_handle_artist_click(GtkListBox *box, GtkListBoxRow *row, gpointer data); KotoPageMusicLocal * self,
void koto_page_music_local_go_to_artist_by_name(KotoPageMusicLocal *self, gchar *artist_name); KotoIndexedArtist * artist
void koto_page_music_local_go_to_artist_by_uuid(KotoPageMusicLocal *self, gchar *artist_uuid); );
void koto_page_music_local_set_library(KotoPageMusicLocal *self, KotoIndexedLibrary *lib);
int koto_page_music_local_sort_artists(GtkListBoxRow *artist1, GtkListBoxRow *artist2, gpointer user_data); void koto_page_music_local_handle_artist_click(
GtkListBox * box,
GtkListBoxRow * row,
gpointer data
);
void koto_page_music_local_go_to_artist_by_name(
KotoPageMusicLocal * self,
gchar * artist_name
);
void koto_page_music_local_go_to_artist_by_uuid(
KotoPageMusicLocal * self,
gchar * artist_uuid
);
void koto_page_music_local_set_library(
KotoPageMusicLocal * self,
KotoIndexedLibrary * lib
);
int koto_page_music_local_sort_artists(
GtkListBoxRow * artist1,
GtkListBoxRow * artist2,
gpointer user_data
);
G_END_DECLS G_END_DECLS

View file

@ -27,11 +27,11 @@
#include "../../playlist/create-modify-dialog.h" #include "../../playlist/create-modify-dialog.h"
#include "list.h" #include "list.h"
extern KotoActionBar *action_bar; extern KotoActionBar * action_bar;
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoCreateModifyPlaylistDialog *playlist_create_modify_dialog; extern KotoCreateModifyPlaylistDialog * playlist_create_modify_dialog;
extern KotoCurrentPlaylist *current_playlist; extern KotoCurrentPlaylist * current_playlist;
extern KotoWindow *main_window; extern KotoWindow * main_window;
enum { enum {
PROP_0, PROP_0,
@ -39,41 +39,43 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
NULL,
};
struct _KotoPlaylistPage { struct _KotoPlaylistPage {
GObject parent_instance; GObject parent_instance;
KotoPlaylist *playlist; KotoPlaylist * playlist;
gchar *uuid; gchar * uuid;
GtkWidget *main; // Our Scrolled Window GtkWidget * main; // Our Scrolled Window
GtkWidget *content; // Content inside scrolled window GtkWidget * content; // Content inside scrolled window
GtkWidget *header; GtkWidget * header;
KotoCoverArtButton *playlist_image; KotoCoverArtButton * playlist_image;
GtkWidget *name_label; GtkWidget * name_label;
GtkWidget *tracks_count_label; GtkWidget * tracks_count_label;
GtkWidget *type_label; GtkWidget * type_label;
KotoButton *favorite_button; KotoButton * favorite_button;
KotoButton *edit_button; KotoButton * edit_button;
GtkListItemFactory *item_factory; GtkListItemFactory * item_factory;
GListModel *model; GListModel * model;
GtkSelectionModel *selection_model; GtkSelectionModel * selection_model;
GtkWidget *track_list_content; GtkWidget * track_list_content;
GtkWidget *track_list_header; GtkWidget * track_list_header;
GtkWidget *track_list_view; GtkWidget * track_list_view;
KotoButton *track_num_button; KotoButton * track_num_button;
KotoButton *track_title_button; KotoButton * track_title_button;
KotoButton *track_album_button; KotoButton * track_album_button;
KotoButton *track_artist_button; KotoButton * track_artist_button;
GtkSizeGroup *track_pos_size_group; GtkSizeGroup * track_pos_size_group;
GtkSizeGroup *track_name_size_group; GtkSizeGroup * track_name_size_group;
GtkSizeGroup *track_album_size_group; GtkSizeGroup * track_album_size_group;
GtkSizeGroup *track_artist_size_group; GtkSizeGroup * track_artist_size_group;
}; };
struct _KotoPlaylistPageClass { struct _KotoPlaylistPageClass {
@ -82,11 +84,24 @@ struct _KotoPlaylistPageClass {
G_DEFINE_TYPE(KotoPlaylistPage, koto_playlist_page, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoPlaylistPage, koto_playlist_page, G_TYPE_OBJECT);
static void koto_playlist_page_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); static void koto_playlist_page_get_property(
static void koto_playlist_page_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_playlist_page_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_playlist_page_class_init(KotoPlaylistPageClass * c) {
GObjectClass * gobject_class;
static void koto_playlist_page_class_init(KotoPlaylistPageClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->get_property = koto_playlist_page_get_property; gobject_class->get_property = koto_playlist_page_get_property;
gobject_class->set_property = koto_playlist_page_set_property; gobject_class->set_property = koto_playlist_page_set_property;
@ -96,13 +111,13 @@ static void koto_playlist_page_class_init(KotoPlaylistPageClass *c) {
"UUID of associated Playlist", "UUID of associated Playlist",
"UUID of associated Playlist", "UUID of associated Playlist",
NULL, NULL,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_playlist_page_init(KotoPlaylistPage *self) { static void koto_playlist_page_init(KotoPlaylistPage * self) {
self->track_name_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); self->track_name_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
self->track_pos_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); self->track_pos_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
self->track_album_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); self->track_album_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
@ -119,10 +134,14 @@ static void koto_playlist_page_init(KotoPlaylistPage *self) {
gtk_box_prepend(GTK_BOX(self->content), self->header); gtk_box_prepend(GTK_BOX(self->content), self->header);
self->playlist_image = koto_cover_art_button_new(220, 220, NULL); // Create our Cover Art Button with no art by default self->playlist_image = koto_cover_art_button_new(220, 220, NULL); // Create our Cover Art Button with no art by default
KotoButton *cover_art_button = koto_cover_art_button_get_button(self->playlist_image); // Get the button for the cover art KotoButton * cover_art_button = koto_cover_art_button_get_button(self->playlist_image); // Get the button for the cover art
koto_button_add_click_handler(cover_art_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playlist_page_handle_cover_art_clicked), self); koto_button_add_click_handler(cover_art_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playlist_page_handle_cover_art_clicked), self);
GtkWidget *info_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); GtkWidget * info_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_size_request(info_box, -1, 220); gtk_widget_set_size_request(info_box, -1, 220);
gtk_widget_add_css_class(info_box, "playlist-page-header-info"); gtk_widget_add_css_class(info_box, "playlist-page-header-info");
gtk_widget_set_hexpand(info_box, TRUE); gtk_widget_set_hexpand(info_box, TRUE);
@ -176,8 +195,14 @@ static void koto_playlist_page_init(KotoPlaylistPage *self) {
g_signal_connect(action_bar, "closed", G_CALLBACK(koto_playlist_page_handle_action_bar_closed), self); // Handle closed action bar g_signal_connect(action_bar, "closed", G_CALLBACK(koto_playlist_page_handle_action_bar_closed), self); // Handle closed action bar
} }
static void koto_playlist_page_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_playlist_page_get_property(
KotoPlaylistPage *self = KOTO_PLAYLIST_PAGE(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoPlaylistPage * self = KOTO_PLAYLIST_PAGE(obj);
switch (prop_id) { switch (prop_id) {
case PROP_PLAYLIST_UUID: case PROP_PLAYLIST_UUID:
@ -189,8 +214,15 @@ static void koto_playlist_page_get_property(GObject *obj, guint prop_id, GValue
} }
} }
static void koto_playlist_page_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_playlist_page_set_property(
KotoPlaylistPage *self = KOTO_PLAYLIST_PAGE(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoPlaylistPage * self = KOTO_PLAYLIST_PAGE(obj);
switch (prop_id) { switch (prop_id) {
case PROP_PLAYLIST_UUID: case PROP_PLAYLIST_UUID:
koto_playlist_page_set_playlist_uuid(self, g_strdup(g_value_get_string(val))); // Call to our playlist UUID set function koto_playlist_page_set_playlist_uuid(self, g_strdup(g_value_get_string(val))); // Call to our playlist UUID set function
@ -201,23 +233,29 @@ static void koto_playlist_page_set_property(GObject *obj, guint prop_id, const G
} }
} }
void koto_playlist_page_bind_track_item(GtkListItemFactory *factory, GtkListItem *item, KotoPlaylistPage *self) { void koto_playlist_page_bind_track_item(
GtkListItemFactory * factory,
GtkListItem * item,
KotoPlaylistPage * self
) {
(void) factory; (void) factory;
GtkWidget *track_position_label = gtk_widget_get_first_child(gtk_list_item_get_child(item)); GtkWidget * track_position_label = gtk_widget_get_first_child(gtk_list_item_get_child(item));
GtkWidget *track_name_label = gtk_widget_get_next_sibling(track_position_label); GtkWidget * track_name_label = gtk_widget_get_next_sibling(track_position_label);
GtkWidget *track_album_label = gtk_widget_get_next_sibling(track_name_label); GtkWidget * track_album_label = gtk_widget_get_next_sibling(track_name_label);
GtkWidget *track_artist_label = gtk_widget_get_next_sibling(track_album_label); GtkWidget * track_artist_label = gtk_widget_get_next_sibling(track_album_label);
KotoIndexedTrack * track = gtk_list_item_get_item(item); // Get the track UUID from our model
KotoIndexedTrack *track = gtk_list_item_get_item(item); // Get the track UUID from our model
if (!KOTO_IS_INDEXED_TRACK(track)) { if (!KOTO_IS_INDEXED_TRACK(track)) {
return; return;
} }
gchar *track_name = NULL; gchar * track_name = NULL;
gchar *album_uuid = NULL; gchar * album_uuid = NULL;
gchar *artist_uuid = NULL; gchar * artist_uuid = NULL;
g_object_get( g_object_get(
track, track,
@ -232,23 +270,26 @@ void koto_playlist_page_bind_track_item(GtkListItemFactory *factory, GtkListItem
guint track_position = koto_playlist_get_position_of_track(self->playlist, track); guint track_position = koto_playlist_get_position_of_track(self->playlist, track);
gtk_label_set_label(GTK_LABEL(track_position_label), g_strdup_printf("%u", track_position)); // Set the track position gtk_label_set_label(GTK_LABEL(track_position_label), g_strdup_printf("%u", track_position)); // Set the track position
gtk_label_set_label(GTK_LABEL(track_name_label), track_name); // Set our track name gtk_label_set_label(GTK_LABEL(track_name_label), track_name); // Set our track name
KotoIndexedAlbum *album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid); KotoIndexedAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid);
if (KOTO_IS_INDEXED_ALBUM(album)) { if (KOTO_IS_INDEXED_ALBUM(album)) {
gtk_label_set_label(GTK_LABEL(track_album_label), koto_indexed_album_get_album_name(album)); // Get the name of the album and set it to the label gtk_label_set_label(GTK_LABEL(track_album_label), koto_indexed_album_get_album_name(album)); // Get the name of the album and set it to the label
} }
KotoIndexedArtist *artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid); KotoIndexedArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid);
if (KOTO_IS_INDEXED_ARTIST(artist)) { if (KOTO_IS_INDEXED_ARTIST(artist)) {
gtk_label_set_label(GTK_LABEL(track_artist_label), koto_indexed_artist_get_name(artist)); // Get the name of the artist and set it to the label gtk_label_set_label(GTK_LABEL(track_artist_label), koto_indexed_artist_get_name(artist)); // Get the name of the artist and set it to the label
} }
} }
void koto_playlist_page_create_tracks_header(KotoPlaylistPage *self) { void koto_playlist_page_create_tracks_header(KotoPlaylistPage * self) {
self->track_num_button = koto_button_new_with_icon("#", "pan-down-symbolic", "pan-up-symbolic", KOTO_BUTTON_PIXBUF_SIZE_SMALL); self->track_num_button = koto_button_new_with_icon("#", "pan-down-symbolic", "pan-up-symbolic", KOTO_BUTTON_PIXBUF_SIZE_SMALL);
koto_button_add_click_handler(self->track_num_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playlist_page_handle_track_num_clicked), self); koto_button_add_click_handler(self->track_num_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playlist_page_handle_track_num_clicked), self);
koto_button_set_image_position(self->track_num_button, KOTO_BUTTON_IMAGE_POS_RIGHT); // Move the image to the right koto_button_set_image_position(self->track_num_button, KOTO_BUTTON_IMAGE_POS_RIGHT); // Move the image to the right
@ -276,13 +317,17 @@ void koto_playlist_page_create_tracks_header(KotoPlaylistPage *self) {
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_artist_button)); gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_artist_button));
} }
GtkWidget* koto_playlist_page_get_main(KotoPlaylistPage *self) { GtkWidget * koto_playlist_page_get_main(KotoPlaylistPage * self) {
return self->main; return self->main;
} }
void koto_playlist_page_handle_action_bar_closed(KotoActionBar *bar, gpointer data) { void koto_playlist_page_handle_action_bar_closed(
KotoActionBar * bar,
gpointer data
) {
(void) bar; (void) bar;
KotoPlaylistPage *self = data; KotoPlaylistPage * self = data;
if (!KOTO_IS_PLAYLIST(self->playlist)) { // No playlist set if (!KOTO_IS_PLAYLIST(self->playlist)) { // No playlist set
return; return;
@ -292,9 +337,19 @@ void koto_playlist_page_handle_action_bar_closed(KotoActionBar *bar, gpointer da
gtk_widget_grab_focus(GTK_WIDGET(main_window)); // Focus on the window gtk_widget_grab_focus(GTK_WIDGET(main_window)); // Focus on the window
} }
void koto_playlist_page_handle_cover_art_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_playlist_page_handle_cover_art_clicked(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
KotoPlaylistPage *self = user_data; int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoPlaylistPage * self = user_data;
if (!KOTO_IS_PLAYLIST(self->playlist)) { // No playlist set if (!KOTO_IS_PLAYLIST(self->playlist)) { // No playlist set
return; return;
@ -303,64 +358,127 @@ void koto_playlist_page_handle_cover_art_clicked(GtkGestureClick *gesture, int n
koto_current_playlist_set_playlist(current_playlist, self->playlist); koto_current_playlist_set_playlist(current_playlist, self->playlist);
} }
void koto_playlist_page_handle_edit_button_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_playlist_page_handle_edit_button_clicked(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
KotoPlaylistPage *self = user_data; int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoPlaylistPage * self = user_data;
koto_create_modify_playlist_dialog_set_playlist_uuid(playlist_create_modify_dialog, koto_playlist_get_uuid(self->playlist)); koto_create_modify_playlist_dialog_set_playlist_uuid(playlist_create_modify_dialog, koto_playlist_get_uuid(self->playlist));
koto_window_show_dialog(main_window, "create-modify-playlist"); koto_window_show_dialog(main_window, "create-modify-playlist");
} }
void koto_playlist_page_handle_playlist_modified(KotoPlaylist *playlist, gpointer user_data) { void koto_playlist_page_handle_playlist_modified(
KotoPlaylist * playlist,
gpointer user_data
) {
if (!KOTO_IS_PLAYLIST(playlist)) { if (!KOTO_IS_PLAYLIST(playlist)) {
return; return;
} }
KotoPlaylistPage *self = user_data; KotoPlaylistPage * self = user_data;
if (!KOTO_IS_PLAYLIST_PAGE(self)) { if (!KOTO_IS_PLAYLIST_PAGE(self)) {
return; return;
} }
gchar *artwork = koto_playlist_get_artwork(playlist); // Get the artwork gchar * artwork = koto_playlist_get_artwork(playlist); // Get the artwork
if (koto_utils_is_string_valid(artwork)) { // Have valid artwork if (koto_utils_is_string_valid(artwork)) { // Have valid artwork
koto_cover_art_button_set_art_path(self->playlist_image, artwork); // Update our Koto Cover Art Button koto_cover_art_button_set_art_path(self->playlist_image, artwork); // Update our Koto Cover Art Button
} }
gchar *name = koto_playlist_get_name(playlist); // Get the name gchar * name = koto_playlist_get_name(playlist); // Get the name
if (koto_utils_is_string_valid(name)) { // Have valid name if (koto_utils_is_string_valid(name)) { // Have valid name
gtk_label_set_label(GTK_LABEL(self->name_label), name); // Update the name label gtk_label_set_label(GTK_LABEL(self->name_label), name); // Update the name label
} }
} }
void koto_playlist_page_handle_track_album_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_playlist_page_handle_track_album_clicked(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
KotoPlaylistPage *self = user_data; int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoPlaylistPage * self = user_data;
gtk_widget_add_css_class(GTK_WIDGET(self->track_album_button), "active"); gtk_widget_add_css_class(GTK_WIDGET(self->track_album_button), "active");
koto_button_hide_image(self->track_num_button); // Go back to hiding the image koto_button_hide_image(self->track_num_button); // Go back to hiding the image
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM); koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM);
} }
void koto_playlist_page_handle_track_artist_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_playlist_page_handle_track_artist_clicked(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
KotoPlaylistPage *self = user_data; int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoPlaylistPage * self = user_data;
gtk_widget_add_css_class(GTK_WIDGET(self->track_artist_button), "active"); gtk_widget_add_css_class(GTK_WIDGET(self->track_artist_button), "active");
koto_button_hide_image(self->track_num_button); // Go back to hiding the image koto_button_hide_image(self->track_num_button); // Go back to hiding the image
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST); koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST);
} }
void koto_playlist_page_handle_track_name_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_playlist_page_handle_track_name_clicked(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
KotoPlaylistPage *self = user_data; int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoPlaylistPage * self = user_data;
gtk_widget_add_css_class(GTK_WIDGET(self->track_title_button), "active"); gtk_widget_add_css_class(GTK_WIDGET(self->track_title_button), "active");
koto_button_hide_image(self->track_num_button); // Go back to hiding the image koto_button_hide_image(self->track_num_button); // Go back to hiding the image
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME); koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME);
} }
void koto_playlist_page_handle_track_num_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_playlist_page_handle_track_num_clicked(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
KotoPlaylistPage *self = user_data; int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoPlaylistPage * self = user_data;
KotoPreferredModelType current_model = koto_playlist_get_current_model(self->playlist); KotoPreferredModelType current_model = koto_playlist_get_current_model(self->playlist);
if (current_model == KOTO_PREFERRED_MODEL_TYPE_DEFAULT) { // Set to newest currently if (current_model == KOTO_PREFERRED_MODEL_TYPE_DEFAULT) { // Set to newest currently
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST); // Sort reversed (oldest) koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST); // Sort reversed (oldest)
koto_button_show_image(self->track_num_button, TRUE); // Use inverted value (pan-up-symbolic) koto_button_show_image(self->track_num_button, TRUE); // Use inverted value (pan-up-symbolic)
@ -370,21 +488,29 @@ void koto_playlist_page_handle_track_num_clicked(GtkGestureClick *gesture, int n
} }
} }
void koto_playlist_page_handle_tracks_selected(GtkSelectionModel *model, guint position, guint n_items, gpointer user_data) { void koto_playlist_page_handle_tracks_selected(
GtkSelectionModel * model,
guint position,
guint n_items,
gpointer user_data
) {
(void) position; (void) position;
KotoPlaylistPage *self = user_data; KotoPlaylistPage * self = user_data;
if (n_items == 0) { // No items selected if (n_items == 0) { // No items selected
koto_action_bar_toggle_reveal(action_bar, FALSE); // Hide the action bar koto_action_bar_toggle_reveal(action_bar, FALSE); // Hide the action bar
return; return;
} }
GtkBitset *selected_items_bitset = gtk_selection_model_get_selection(model); // Get the selected items as a GtkBitSet GtkBitset * selected_items_bitset = gtk_selection_model_get_selection(model); // Get the selected items as a GtkBitSet
GtkBitsetIter iter; GtkBitsetIter iter;
GList *selected_tracks = NULL; GList * selected_tracks = NULL;
GList *selected_tracks_pos = NULL; GList * selected_tracks_pos = NULL;
guint first_track_pos; guint first_track_pos;
if (!gtk_bitset_iter_init_first(&iter, selected_items_bitset, &first_track_pos)) { // Failed to get the first item if (!gtk_bitset_iter_init_first(&iter, selected_items_bitset, &first_track_pos)) { // Failed to get the first item
return; return;
} }
@ -392,6 +518,8 @@ void koto_playlist_page_handle_tracks_selected(GtkSelectionModel *model, guint p
selected_tracks_pos = g_list_append(selected_tracks_pos, GUINT_TO_POINTER(first_track_pos)); selected_tracks_pos = g_list_append(selected_tracks_pos, GUINT_TO_POINTER(first_track_pos));
gboolean have_more_items = TRUE; gboolean have_more_items = TRUE;
while (have_more_items) { // While we are able to get selected items while (have_more_items) { // While we are able to get selected items
guint track_pos; guint track_pos;
have_more_items = gtk_bitset_iter_next(&iter, &track_pos); have_more_items = gtk_bitset_iter_next(&iter, &track_pos);
@ -400,9 +528,11 @@ void koto_playlist_page_handle_tracks_selected(GtkSelectionModel *model, guint p
} }
} }
GList *cur_pos_list; GList * cur_pos_list;
for (cur_pos_list = selected_tracks_pos; cur_pos_list != NULL; cur_pos_list = cur_pos_list->next) { // Iterate over every position that we accumulated for (cur_pos_list = selected_tracks_pos; cur_pos_list != NULL; cur_pos_list = cur_pos_list->next) { // Iterate over every position that we accumulated
KotoIndexedTrack *selected_track = g_list_model_get_item(self->model, GPOINTER_TO_UINT(cur_pos_list->data)); // Get the KotoIndexedTrack in the GListModel for this current position KotoIndexedTrack * selected_track = g_list_model_get_item(self->model, GPOINTER_TO_UINT(cur_pos_list->data)); // Get the KotoIndexedTrack in the GListModel for this current position
selected_tracks = g_list_append(selected_tracks, selected_track); // Add to selected tracks selected_tracks = g_list_append(selected_tracks, selected_track); // Add to selected tracks
} }
@ -410,7 +540,10 @@ void koto_playlist_page_handle_tracks_selected(GtkSelectionModel *model, guint p
koto_action_bar_toggle_reveal(action_bar, TRUE); // Show the items koto_action_bar_toggle_reveal(action_bar, TRUE); // Show the items
} }
void koto_playlist_page_set_playlist_uuid(KotoPlaylistPage *self, gchar *playlist_uuid) { void koto_playlist_page_set_playlist_uuid(
KotoPlaylistPage * self,
gchar * playlist_uuid
) {
if (!KOTO_IS_PLAYLIST_PAGE(self)) { if (!KOTO_IS_PLAYLIST_PAGE(self)) {
return; return;
} }
@ -428,7 +561,9 @@ void koto_playlist_page_set_playlist_uuid(KotoPlaylistPage *self, gchar *playlis
} }
self->uuid = g_strdup(playlist_uuid); // Duplicate the playlist UUID self->uuid = g_strdup(playlist_uuid); // Duplicate the playlist UUID
KotoPlaylist *playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->uuid); KotoPlaylist * playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->uuid);
self->playlist = playlist; self->playlist = playlist;
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_DEFAULT); // TODO: Enable this to be changed koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_DEFAULT); // TODO: Enable this to be changed
koto_playlist_page_update_header(self); // Update our header koto_playlist_page_update_header(self); // Update our header
@ -436,7 +571,10 @@ void koto_playlist_page_set_playlist_uuid(KotoPlaylistPage *self, gchar *playlis
g_signal_connect(playlist, "modified", G_CALLBACK(koto_playlist_page_handle_playlist_modified), self); // Handle playlist modification g_signal_connect(playlist, "modified", G_CALLBACK(koto_playlist_page_handle_playlist_modified), self); // Handle playlist modification
} }
void koto_playlist_page_set_playlist_model(KotoPlaylistPage *self, KotoPreferredModelType model) { void koto_playlist_page_set_playlist_model(
KotoPlaylistPage * self,
KotoPreferredModelType model
) {
if (!KOTO_IS_PLAYLIST_PAGE(self)) { if (!KOTO_IS_PLAYLIST_PAGE(self)) {
return; return;
} }
@ -462,18 +600,27 @@ void koto_playlist_page_set_playlist_model(KotoPlaylistPage *self, KotoPreferred
gtk_list_view_set_model(GTK_LIST_VIEW(self->track_list_view), self->selection_model); // Set our multi selection model to our provided model gtk_list_view_set_model(GTK_LIST_VIEW(self->track_list_view), self->selection_model); // Set our multi selection model to our provided model
} }
void koto_playlist_page_setup_track_item(GtkListItemFactory *factory, GtkListItem *item, gpointer user_data) { void koto_playlist_page_setup_track_item(
GtkListItemFactory * factory,
GtkListItem * item,
gpointer user_data
) {
(void) factory; (void) factory;
KotoPlaylistPage *self = user_data; KotoPlaylistPage * self = user_data;
if (!KOTO_IS_PLAYLIST_PAGE(self)) { if (!KOTO_IS_PLAYLIST_PAGE(self)) {
return; return;
} }
GtkWidget *item_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); // Have a horizontal box for our content GtkWidget * item_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); // Have a horizontal box for our content
gtk_widget_add_css_class(item_content, "track-list-columned-item"); gtk_widget_add_css_class(item_content, "track-list-columned-item");
GtkWidget *track_number = gtk_label_new(NULL); // Our track number GtkWidget * track_number = gtk_label_new(NULL); // Our track number
gtk_label_set_xalign(GTK_LABEL(track_number), 0); gtk_label_set_xalign(GTK_LABEL(track_number), 0);
gtk_widget_add_css_class(track_number, "track-column-number"); gtk_widget_add_css_class(track_number, "track-column-number");
gtk_widget_set_halign(track_number, GTK_ALIGN_END); gtk_widget_set_halign(track_number, GTK_ALIGN_END);
@ -482,7 +629,9 @@ void koto_playlist_page_setup_track_item(GtkListItemFactory *factory, GtkListIte
gtk_box_append(GTK_BOX(item_content), track_number); gtk_box_append(GTK_BOX(item_content), track_number);
gtk_size_group_add_widget(self->track_pos_size_group, track_number); gtk_size_group_add_widget(self->track_pos_size_group, track_number);
GtkWidget *track_name = gtk_label_new(NULL); // Our track name GtkWidget * track_name = gtk_label_new(NULL); // Our track name
gtk_label_set_xalign(GTK_LABEL(track_name), 0); gtk_label_set_xalign(GTK_LABEL(track_name), 0);
gtk_widget_add_css_class(track_name, "track-column-name"); gtk_widget_add_css_class(track_name, "track-column-name");
gtk_widget_set_halign(track_name, GTK_ALIGN_START); gtk_widget_set_halign(track_name, GTK_ALIGN_START);
@ -491,7 +640,9 @@ void koto_playlist_page_setup_track_item(GtkListItemFactory *factory, GtkListIte
gtk_box_append(GTK_BOX(item_content), track_name); gtk_box_append(GTK_BOX(item_content), track_name);
gtk_size_group_add_widget(self->track_name_size_group, track_name); gtk_size_group_add_widget(self->track_name_size_group, track_name);
GtkWidget *track_album = gtk_label_new(NULL); // Our track album GtkWidget * track_album = gtk_label_new(NULL); // Our track album
gtk_label_set_xalign(GTK_LABEL(track_album), 0); gtk_label_set_xalign(GTK_LABEL(track_album), 0);
gtk_widget_add_css_class(track_album, "track-column-album"); gtk_widget_add_css_class(track_album, "track-column-album");
gtk_widget_set_halign(track_album, GTK_ALIGN_START); gtk_widget_set_halign(track_album, GTK_ALIGN_START);
@ -501,7 +652,9 @@ void koto_playlist_page_setup_track_item(GtkListItemFactory *factory, GtkListIte
gtk_box_append(GTK_BOX(item_content), track_album); gtk_box_append(GTK_BOX(item_content), track_album);
gtk_size_group_add_widget(self->track_album_size_group, track_album); gtk_size_group_add_widget(self->track_album_size_group, track_album);
GtkWidget *track_artist = gtk_label_new(NULL); // Our track artist GtkWidget * track_artist = gtk_label_new(NULL); // Our track artist
gtk_label_set_xalign(GTK_LABEL(track_artist), 0); gtk_label_set_xalign(GTK_LABEL(track_artist), 0);
gtk_widget_add_css_class(track_artist, "track-column-artist"); gtk_widget_add_css_class(track_artist, "track-column-artist");
gtk_widget_set_halign(track_artist, GTK_ALIGN_START); gtk_widget_set_halign(track_artist, GTK_ALIGN_START);
@ -514,7 +667,7 @@ void koto_playlist_page_setup_track_item(GtkListItemFactory *factory, GtkListIte
gtk_list_item_set_child(item, item_content); gtk_list_item_set_child(item, item_content);
} }
void koto_playlist_page_update_header(KotoPlaylistPage *self) { void koto_playlist_page_update_header(KotoPlaylistPage * self) {
if (!KOTO_IS_PLAYLIST_PAGE(self)) { if (!KOTO_IS_PLAYLIST_PAGE(self)) {
return; return;
} }
@ -524,6 +677,8 @@ void koto_playlist_page_update_header(KotoPlaylistPage *self) {
} }
gboolean ephemeral = TRUE; gboolean ephemeral = TRUE;
g_object_get( g_object_get(
self->playlist, self->playlist,
"ephemeral", "ephemeral",
@ -535,22 +690,28 @@ void koto_playlist_page_update_header(KotoPlaylistPage *self) {
gtk_label_set_text(GTK_LABEL(self->name_label), koto_playlist_get_name(self->playlist)); // Set the name label to our playlist name gtk_label_set_text(GTK_LABEL(self->name_label), koto_playlist_get_name(self->playlist)); // Set the name label to our playlist name
guint track_count = koto_playlist_get_length(self->playlist); // Get the number of tracks guint track_count = koto_playlist_get_length(self->playlist); // Get the number of tracks
gtk_label_set_text(GTK_LABEL(self->tracks_count_label), g_strdup_printf(track_count != 1 ? "%u tracks" : "%u track", track_count)); // Set the text to "N tracks" where N is the number gtk_label_set_text(GTK_LABEL(self->tracks_count_label), g_strdup_printf(track_count != 1 ? "%u tracks" : "%u track", track_count)); // Set the text to "N tracks" where N is the number
gchar *artwork = koto_playlist_get_artwork(self->playlist); gchar * artwork = koto_playlist_get_artwork(self->playlist);
if (koto_utils_is_string_valid(artwork)) { // Have artwork if (koto_utils_is_string_valid(artwork)) { // Have artwork
koto_cover_art_button_set_art_path(self->playlist_image, artwork); // Update our artwork koto_cover_art_button_set_art_path(self->playlist_image, artwork); // Update our artwork
} }
KotoPreferredModelType current_model = koto_playlist_get_current_model(self->playlist); // Get the current model KotoPreferredModelType current_model = koto_playlist_get_current_model(self->playlist); // Get the current model
if (current_model == KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST) { if (current_model == KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST) {
koto_button_show_image(self->track_num_button, TRUE); // Immediately use pan-up-symbolic koto_button_show_image(self->track_num_button, TRUE); // Immediately use pan-up-symbolic
} }
} }
KotoPlaylistPage* koto_playlist_page_new(gchar *playlist_uuid) { KotoPlaylistPage * koto_playlist_page_new(gchar * playlist_uuid) {
return g_object_new(KOTO_TYPE_PLAYLIST_PAGE, return g_object_new(
KOTO_TYPE_PLAYLIST_PAGE,
"uuid", "uuid",
playlist_uuid, playlist_uuid,
NULL NULL

View file

@ -26,31 +26,122 @@
G_BEGIN_DECLS G_BEGIN_DECLS
#define KOTO_TYPE_PLAYLIST_PAGE koto_playlist_page_get_type() #define KOTO_TYPE_PLAYLIST_PAGE koto_playlist_page_get_type()
G_DECLARE_FINAL_TYPE (KotoPlaylistPage, koto_playlist_page, KOTO, PLAYLIST_PAGE, GObject); G_DECLARE_FINAL_TYPE(KotoPlaylistPage, koto_playlist_page, KOTO, PLAYLIST_PAGE, GObject);
#define KOTO_IS_PLAYLIST_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYLIST_PAGE)) #define KOTO_IS_PLAYLIST_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYLIST_PAGE))
KotoPlaylistPage* koto_playlist_page_new(gchar *playlist_uuid); KotoPlaylistPage * koto_playlist_page_new(gchar * playlist_uuid);
void koto_playlist_page_add_track(KotoPlaylistPage* self, const gchar *track_uuid);
void koto_playlist_page_create_tracks_header(KotoPlaylistPage *self);
void koto_playlist_page_bind_track_item(GtkListItemFactory *factory, GtkListItem *item, KotoPlaylistPage *self);
void koto_playlist_page_remove_track(KotoPlaylistPage *self, const gchar *track_uuid);
GtkWidget* koto_playlist_page_get_main(KotoPlaylistPage *self);
void koto_playlist_page_handle_action_bar_closed(KotoActionBar *bar, gpointer data);
void koto_playlist_page_handle_cover_art_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
void koto_playlist_page_handle_edit_button_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
void koto_playlist_page_handle_playlist_modified(KotoPlaylist *playlist, gpointer user_data);
void koto_playlist_page_handle_track_album_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
void koto_playlist_page_handle_track_artist_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
void koto_playlist_page_handle_track_name_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
void koto_playlist_page_handle_track_num_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
void koto_playlist_page_handle_tracks_selected(GtkSelectionModel *model, guint position, guint n_items, gpointer user_data);
void koto_playlist_page_hide_popover(KotoPlaylistPage *self);
void koto_playlist_page_setup_track_item(GtkListItemFactory *factory, GtkListItem *item, gpointer user_data);
void koto_playlist_page_set_playlist_uuid(KotoPlaylistPage *self, gchar *playlist_uuid);
void koto_playlist_page_set_playlist_model(KotoPlaylistPage *self, KotoPreferredModelType model);
void koto_playlist_page_show_popover(KotoPlaylistPage *self);
void koto_playlist_page_update_header(KotoPlaylistPage *self);
void koto_playlist_page_handle_listitem_activate(GtkListView *list, guint position, gpointer user_data); void koto_playlist_page_add_track(
KotoPlaylistPage* self,
const gchar * track_uuid
);
void koto_playlist_page_create_tracks_header(KotoPlaylistPage * self);
void koto_playlist_page_bind_track_item(
GtkListItemFactory * factory,
GtkListItem * item,
KotoPlaylistPage * self
);
void koto_playlist_page_remove_track(
KotoPlaylistPage * self,
const gchar * track_uuid
);
GtkWidget * koto_playlist_page_get_main(KotoPlaylistPage * self);
void koto_playlist_page_handle_action_bar_closed(
KotoActionBar * bar,
gpointer data
);
void koto_playlist_page_handle_cover_art_clicked(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_playlist_page_handle_edit_button_clicked(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_playlist_page_handle_playlist_modified(
KotoPlaylist * playlist,
gpointer user_data
);
void koto_playlist_page_handle_track_album_clicked(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_playlist_page_handle_track_artist_clicked(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_playlist_page_handle_track_name_clicked(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_playlist_page_handle_track_num_clicked(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_playlist_page_handle_tracks_selected(
GtkSelectionModel * model,
guint position,
guint n_items,
gpointer user_data
);
void koto_playlist_page_hide_popover(KotoPlaylistPage * self);
void koto_playlist_page_setup_track_item(
GtkListItemFactory * factory,
GtkListItem * item,
gpointer user_data
);
void koto_playlist_page_set_playlist_uuid(
KotoPlaylistPage * self,
gchar * playlist_uuid
);
void koto_playlist_page_set_playlist_model(
KotoPlaylistPage * self,
KotoPreferredModelType model
);
void koto_playlist_page_show_popover(KotoPlaylistPage * self);
void koto_playlist_page_update_header(KotoPlaylistPage * self);
void koto_playlist_page_handle_listitem_activate(
GtkListView * list,
guint position,
gpointer user_data
);
G_END_DECLS G_END_DECLS

View file

@ -38,25 +38,26 @@ enum {
N_SIGNALS N_SIGNALS
}; };
static guint playback_engine_signals[N_SIGNALS] = { 0 }; static guint playback_engine_signals[N_SIGNALS] = {
0
};
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoCurrentPlaylist *current_playlist; extern KotoCurrentPlaylist * current_playlist;
KotoPlaybackEngine *playback_engine; KotoPlaybackEngine * playback_engine;
struct _KotoPlaybackEngine { struct _KotoPlaybackEngine {
GObject parent_class; GObject parent_class;
GstElement *player; GstElement * player;
GstElement *playbin; GstElement * playbin;
GstElement *suppress_video; GstElement * suppress_video;
GstBus *monitor; GstBus * monitor;
GstQuery *duration_query; GstQuery * duration_query;
GstQuery *position_query; GstQuery * position_query;
KotoIndexedTrack *current_track; KotoIndexedTrack * current_track;
NotifyNotification *track_notification;
gboolean is_muted; gboolean is_muted;
gboolean is_repeat_enabled; gboolean is_repeat_enabled;
@ -74,22 +75,24 @@ struct _KotoPlaybackEngine {
struct _KotoPlaybackEngineClass { struct _KotoPlaybackEngineClass {
GObjectClass parent_class; GObjectClass parent_class;
void (* is_playing) (KotoPlaybackEngine *engine); void (* is_playing) (KotoPlaybackEngine * engine);
void (* is_paused) (KotoPlaybackEngine *engine); void (* is_paused) (KotoPlaybackEngine * engine);
void (* play_state_changed) (KotoPlaybackEngine *engine); void (* play_state_changed) (KotoPlaybackEngine * engine);
void (* tick_duration) (KotoPlaybackEngine *engine); void (* tick_duration) (KotoPlaybackEngine * engine);
void (* tick_track) (KotoPlaybackEngine *engine); void (* tick_track) (KotoPlaybackEngine * engine);
void (* track_changed) (KotoPlaybackEngine *engine); void (* track_changed) (KotoPlaybackEngine * engine);
void (* track_repeat_changed) (KotoPlaybackEngine *engine); void (* track_repeat_changed) (KotoPlaybackEngine * engine);
void (* track_shuffle_changed) (KotoPlaybackEngine *engine); void (* track_shuffle_changed) (KotoPlaybackEngine * engine);
}; };
G_DEFINE_TYPE(KotoPlaybackEngine, koto_playback_engine, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoPlaybackEngine, koto_playback_engine, G_TYPE_OBJECT);
static void koto_playback_engine_class_init(KotoPlaybackEngineClass *c) { static void koto_playback_engine_class_init(KotoPlaybackEngineClass * c) {
c->play_state_changed = NULL; c->play_state_changed = NULL;
GObjectClass *gobject_class; GObjectClass * gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
playback_engine_signals[SIGNAL_IS_PLAYING] = g_signal_new( playback_engine_signals[SIGNAL_IS_PLAYING] = g_signal_new(
@ -189,7 +192,7 @@ static void koto_playback_engine_class_init(KotoPlaybackEngineClass *c) {
); );
} }
static void koto_playback_engine_init(KotoPlaybackEngine *self) { static void koto_playback_engine_init(KotoPlaybackEngine * self) {
self->current_track = NULL; self->current_track = NULL;
self->player = gst_pipeline_new("player"); self->player = gst_pipeline_new("player");
@ -223,8 +226,9 @@ static void koto_playback_engine_init(KotoPlaybackEngine *self) {
} }
} }
void koto_playback_engine_backwards(KotoPlaybackEngine *self) { void koto_playback_engine_backwards(KotoPlaybackEngine * self) {
KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); // Get the current playlist KotoPlaylist * playlist = koto_current_playlist_get_playlist(current_playlist); // Get the current playlist
if (!KOTO_IS_PLAYLIST(playlist)) { // If we do not have a playlist currently if (!KOTO_IS_PLAYLIST(playlist)) { // If we do not have a playlist currently
return; return;
@ -242,7 +246,8 @@ void koto_playback_engine_current_playlist_changed() {
return; return;
} }
KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); // Get the current playlist KotoPlaylist * playlist = koto_current_playlist_get_playlist(current_playlist); // Get the current playlist
if (!KOTO_IS_PLAYLIST(playlist)) { // If we do not have a playlist currently if (!KOTO_IS_PLAYLIST(playlist)) { // If we do not have a playlist currently
return; return;
@ -251,8 +256,9 @@ void koto_playback_engine_current_playlist_changed() {
koto_playback_engine_set_track_by_uuid(playback_engine, koto_playlist_go_to_next(playlist)); // Go to "next" which is the first track koto_playback_engine_set_track_by_uuid(playback_engine, koto_playlist_go_to_next(playlist)); // Go to "next" which is the first track
} }
void koto_playback_engine_forwards(KotoPlaybackEngine *self) { void koto_playback_engine_forwards(KotoPlaybackEngine * self) {
KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); // Get the current playlist KotoPlaylist * playlist = koto_current_playlist_get_playlist(current_playlist); // Get the current playlist
if (!KOTO_IS_PLAYLIST(playlist)) { // If we do not have a playlist currently if (!KOTO_IS_PLAYLIST(playlist)) { // If we do not have a playlist currently
return; return;
@ -265,12 +271,14 @@ void koto_playback_engine_forwards(KotoPlaybackEngine *self) {
} }
} }
KotoIndexedTrack* koto_playback_engine_get_current_track(KotoPlaybackEngine *self) { KotoIndexedTrack * koto_playback_engine_get_current_track(KotoPlaybackEngine * self) {
return self->current_track; return self->current_track;
} }
gint64 koto_playback_engine_get_duration(KotoPlaybackEngine *self) { gint64 koto_playback_engine_get_duration(KotoPlaybackEngine * self) {
gint64 duration = 0; gint64 duration = 0;
if (gst_element_query(self->player, self->duration_query)) { // Able to query our duration if (gst_element_query(self->player, self->duration_query)) { // Able to query our duration
gst_query_parse_duration(self->duration_query, NULL, &duration); // Get the duration gst_query_parse_duration(self->duration_query, NULL, &duration); // Get the duration
duration = duration / GST_SECOND; // Divide by NS to get seconds duration = duration / GST_SECOND; // Divide by NS to get seconds
@ -279,9 +287,11 @@ gint64 koto_playback_engine_get_duration(KotoPlaybackEngine *self) {
return duration; return duration;
} }
gdouble koto_playback_engine_get_progress(KotoPlaybackEngine *self) { gdouble koto_playback_engine_get_progress(KotoPlaybackEngine * self) {
gdouble progress = 0.0; gdouble progress = 0.0;
gint64 gstprog = 0; gint64 gstprog = 0;
if (gst_element_query(self->playbin, self->position_query)) { // Able to get our position if (gst_element_query(self->playbin, self->position_query)) { // Able to get our position
gst_query_parse_position(self->position_query, NULL, &gstprog); // Get the progress gst_query_parse_position(self->position_query, NULL, &gstprog); // Get the progress
@ -295,31 +305,39 @@ gdouble koto_playback_engine_get_progress(KotoPlaybackEngine *self) {
return progress; return progress;
} }
GstState koto_playback_engine_get_state(KotoPlaybackEngine *self) { GstState koto_playback_engine_get_state(KotoPlaybackEngine * self) {
return GST_STATE(self->player); return GST_STATE(self->player);
} }
gboolean koto_playback_engine_get_track_repeat(KotoPlaybackEngine *self) { gboolean koto_playback_engine_get_track_repeat(KotoPlaybackEngine * self) {
return self->is_repeat_enabled; return self->is_repeat_enabled;
} }
gboolean koto_playback_engine_get_track_shuffle(KotoPlaybackEngine *self) { gboolean koto_playback_engine_get_track_shuffle(KotoPlaybackEngine * self) {
(void) self; (void) self;
KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); KotoPlaylist * playlist = koto_current_playlist_get_playlist(current_playlist);
if (!KOTO_IS_PLAYLIST(playlist)) { // Don't have a playlist currently if (!KOTO_IS_PLAYLIST(playlist)) { // Don't have a playlist currently
return FALSE; return FALSE;
} }
gboolean currently_shuffling = FALSE; gboolean currently_shuffling = FALSE;
g_object_get(playlist, "is-shuffle-enabled", &currently_shuffling, NULL); // Get the current is-shuffle-enabled g_object_get(playlist, "is-shuffle-enabled", &currently_shuffling, NULL); // Get the current is-shuffle-enabled
return currently_shuffling; return currently_shuffling;
} }
gboolean koto_playback_engine_monitor_changed(GstBus *bus, GstMessage *msg, gpointer user_data) { gboolean koto_playback_engine_monitor_changed(
GstBus * bus,
GstMessage * msg,
gpointer user_data
) {
(void) bus; (void) bus;
KotoPlaybackEngine *self = user_data; KotoPlaybackEngine * self = user_data;
switch (GST_MESSAGE_TYPE(msg)) { switch (GST_MESSAGE_TYPE(msg)) {
case GST_MESSAGE_ASYNC_DONE: case GST_MESSAGE_ASYNC_DONE:
@ -357,7 +375,7 @@ gboolean koto_playback_engine_monitor_changed(GstBus *bus, GstMessage *msg, gpoi
return TRUE; return TRUE;
} }
void koto_playback_engine_play(KotoPlaybackEngine *self) { void koto_playback_engine_play(KotoPlaybackEngine * self) {
self->is_playing = TRUE; self->is_playing = TRUE;
gst_element_set_state(self->player, GST_STATE_PLAYING); // Set our state to play gst_element_set_state(self->player, GST_STATE_PLAYING); // Set our state to play
@ -374,23 +392,33 @@ void koto_playback_engine_play(KotoPlaybackEngine *self) {
koto_update_mpris_playback_state(GST_STATE_PLAYING); koto_update_mpris_playback_state(GST_STATE_PLAYING);
} }
void koto_playback_engine_pause(KotoPlaybackEngine *self) { void koto_playback_engine_pause(KotoPlaybackEngine * self) {
self->is_playing = FALSE; self->is_playing = FALSE;
gst_element_change_state(self->player, GST_STATE_CHANGE_PLAYING_TO_PAUSED); gst_element_change_state(self->player, GST_STATE_CHANGE_PLAYING_TO_PAUSED);
koto_update_mpris_playback_state(GST_STATE_PAUSED); koto_update_mpris_playback_state(GST_STATE_PAUSED);
} }
void koto_playback_engine_set_position(KotoPlaybackEngine *self, int position) { void koto_playback_engine_set_position(
KotoPlaybackEngine * self,
int position
) {
gst_element_seek_simple(self->playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, position * GST_SECOND); gst_element_seek_simple(self->playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, position * GST_SECOND);
} }
void koto_playback_engine_set_track_repeat(KotoPlaybackEngine *self, gboolean enable_repeat) { void koto_playback_engine_set_track_repeat(
KotoPlaybackEngine * self,
gboolean enable_repeat
) {
self->is_repeat_enabled = enable_repeat; self->is_repeat_enabled = enable_repeat;
g_signal_emit(self, playback_engine_signals[SIGNAL_TRACK_REPEAT_CHANGE], 0); // Emit our track repeat changed event g_signal_emit(self, playback_engine_signals[SIGNAL_TRACK_REPEAT_CHANGE], 0); // Emit our track repeat changed event
} }
void koto_playback_engine_set_track_shuffle(KotoPlaybackEngine *self, gboolean enable_shuffle) { void koto_playback_engine_set_track_shuffle(
KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); KotoPlaybackEngine * self,
gboolean enable_shuffle
) {
KotoPlaylist * playlist = koto_current_playlist_get_playlist(current_playlist);
if (!KOTO_IS_PLAYLIST(playlist)) { // Don't have a playlist currently if (!KOTO_IS_PLAYLIST(playlist)) { // Don't have a playlist currently
return; return;
@ -400,12 +428,16 @@ void koto_playback_engine_set_track_shuffle(KotoPlaybackEngine *self, gboolean e
g_signal_emit(self, playback_engine_signals[SIGNAL_TRACK_SHUFFLE_CHANGE], 0); // Emit our track shuffle changed event g_signal_emit(self, playback_engine_signals[SIGNAL_TRACK_SHUFFLE_CHANGE], 0); // Emit our track shuffle changed event
} }
void koto_playback_engine_set_track_by_uuid(KotoPlaybackEngine *self, gchar *track_uuid) { void koto_playback_engine_set_track_by_uuid(
KotoPlaybackEngine * self,
gchar * track_uuid
) {
if (track_uuid == NULL) { if (track_uuid == NULL) {
return; return;
} }
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, track_uuid); // Get the track from cartographer KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, track_uuid); // Get the track from cartographer
if (!KOTO_IS_INDEXED_TRACK(track)) { // Not a track if (!KOTO_IS_INDEXED_TRACK(track)) { // Not a track
return; return;
@ -413,12 +445,15 @@ void koto_playback_engine_set_track_by_uuid(KotoPlaybackEngine *self, gchar *tra
self->current_track = track; self->current_track = track;
gchar *track_file_path = NULL; gchar * track_file_path = NULL;
g_object_get(track, "path", &track_file_path, NULL); // Get the path to the track g_object_get(track, "path", &track_file_path, NULL); // Get the path to the track
koto_playback_engine_stop(self); // Stop current track koto_playback_engine_stop(self); // Stop current track
gchar *gst_filename = gst_filename_to_uri(track_file_path, NULL); // Get the gst supported file naem gchar * gst_filename = gst_filename_to_uri(track_file_path, NULL); // Get the gst supported file naem
g_object_set(self->playbin, "uri", gst_filename, NULL); g_object_set(self->playbin, "uri", gst_filename, NULL);
g_free(gst_filename); // Free the filename g_free(gst_filename); // Free the filename
@ -429,34 +464,36 @@ void koto_playback_engine_set_track_by_uuid(KotoPlaybackEngine *self, gchar *tra
koto_playback_engine_set_position(self, 0); koto_playback_engine_set_position(self, 0);
koto_playback_engine_set_volume(self, self->volume); // Re-enforce our volume on the updated playbin koto_playback_engine_set_volume(self, self->volume); // Re-enforce our volume on the updated playbin
GVariant *metadata = koto_indexed_track_get_metadata_vardict(track); // Get the GVariantBuilder variable dict for the metadata GVariant * metadata = koto_indexed_track_get_metadata_vardict(track); // Get the GVariantBuilder variable dict for the metadata
GVariantDict *metadata_dict = g_variant_dict_new(metadata); GVariantDict * metadata_dict = g_variant_dict_new(metadata);
g_signal_emit(self, playback_engine_signals[SIGNAL_TRACK_CHANGE], 0); // Emit our track change signal g_signal_emit(self, playback_engine_signals[SIGNAL_TRACK_CHANGE], 0); // Emit our track change signal
koto_update_mpris_info_for_track(self->current_track); koto_update_mpris_info_for_track(self->current_track);
GVariant *track_name_var = g_variant_dict_lookup_value(metadata_dict, "xesam:title", NULL); // Get the GVariant for the name of the track GVariant * track_name_var = g_variant_dict_lookup_value(metadata_dict, "xesam:title", NULL); // Get the GVariant for the name of the track
const gchar *track_name = g_variant_get_string(track_name_var, NULL); // Get the string of the track name const gchar * track_name = g_variant_get_string(track_name_var, NULL); // Get the string of the track name
GVariant *album_name_var = g_variant_dict_lookup_value(metadata_dict, "xesam:album", NULL); // Get the GVariant for the album name GVariant * album_name_var = g_variant_dict_lookup_value(metadata_dict, "xesam:album", NULL); // Get the GVariant for the album name
const gchar *album_name = g_variant_get_string(album_name_var, NULL); // Get the string for the album name const gchar * album_name = g_variant_get_string(album_name_var, NULL); // Get the string for the album name
GVariant *artist_name_var = g_variant_dict_lookup_value(metadata_dict, "playbackengine:artist", NULL); // Get the GVariant for the name of the artist GVariant * artist_name_var = g_variant_dict_lookup_value(metadata_dict, "playbackengine:artist", NULL); // Get the GVariant for the name of the artist
const gchar *artist_name = g_variant_get_string(artist_name_var, NULL); // Get the string for the artist name const gchar * artist_name = g_variant_get_string(artist_name_var, NULL); // Get the string for the artist name
gchar *artist_album_combo = g_strjoin(" - ", artist_name, album_name, NULL); // Join artist and album name separated by " - " gchar * artist_album_combo = g_strjoin(" - ", artist_name, album_name, NULL); // Join artist and album name separated by " - "
gchar * icon_name = "audio-x-generic-symbolic";
gchar *icon_name = "audio-x-generic-symbolic";
if (g_variant_dict_contains(metadata_dict, "mpris:artUrl")) { // If we have artwork specified if (g_variant_dict_contains(metadata_dict, "mpris:artUrl")) { // If we have artwork specified
GVariant *art_url_var = g_variant_dict_lookup_value(metadata_dict, "mpris:artUrl", NULL); // Get the GVariant for the art URL GVariant * art_url_var = g_variant_dict_lookup_value(metadata_dict, "mpris:artUrl", NULL); // Get the GVariant for the art URL
const gchar *art_uri = g_variant_get_string(art_url_var, NULL); // Get the string for the artwork const gchar * art_uri = g_variant_get_string(art_url_var, NULL); // Get the string for the artwork
icon_name = koto_utils_replace_string_all(g_strdup(art_uri), "file://", ""); icon_name = koto_utils_replace_string_all(g_strdup(art_uri), "file://", "");
} }
// Super important note: We are not using libnotify directly because the synchronous nature of notify_notification_send seems to result in dbus timeouts // Super important note: We are not using libnotify directly because the synchronous nature of notify_notification_send seems to result in dbus timeouts
if (g_find_program_in_path("notify-send") != NULL) { // Have notify-send if (g_find_program_in_path("notify-send") != NULL) { // Have notify-send
char *argv[12]; char * argv[12];
argv[0] = "notify-send"; argv[0] = "notify-send";
argv[1] = "-a"; argv[1] = "-a";
argv[2] = "Koto"; argv[2] = "Koto";
@ -474,14 +511,18 @@ void koto_playback_engine_set_track_by_uuid(KotoPlaybackEngine *self, gchar *tra
} }
} }
void koto_playback_engine_set_volume(KotoPlaybackEngine *self, gdouble volume) { void koto_playback_engine_set_volume(
KotoPlaybackEngine * self,
gdouble volume
) {
self->volume = volume; self->volume = volume;
g_object_set(self->playbin, "volume", self->volume, NULL); g_object_set(self->playbin, "volume", self->volume, NULL);
} }
void koto_playback_engine_stop(KotoPlaybackEngine *self) { void koto_playback_engine_stop(KotoPlaybackEngine * self) {
gst_element_set_state(self->player, GST_STATE_NULL); gst_element_set_state(self->player, GST_STATE_NULL);
GstPad *pad = gst_element_get_static_pad(self->player, "sink"); // Get the static pad of the audio element GstPad * pad = gst_element_get_static_pad(self->player, "sink"); // Get the static pad of the audio element
if (!GST_IS_PAD(pad)) { if (!GST_IS_PAD(pad)) {
return; return;
@ -491,7 +532,7 @@ void koto_playback_engine_stop(KotoPlaybackEngine *self) {
koto_update_mpris_playback_state(GST_STATE_NULL); koto_update_mpris_playback_state(GST_STATE_NULL);
} }
void koto_playback_engine_toggle(KotoPlaybackEngine *self) { void koto_playback_engine_toggle(KotoPlaybackEngine * self) {
if (self->is_playing) { // Currently playing if (self->is_playing) { // Currently playing
koto_playback_engine_pause(self); // Pause koto_playback_engine_pause(self); // Pause
} else { } else {
@ -500,7 +541,8 @@ void koto_playback_engine_toggle(KotoPlaybackEngine *self) {
} }
gboolean koto_playback_engine_tick_duration(gpointer user_data) { gboolean koto_playback_engine_tick_duration(gpointer user_data) {
KotoPlaybackEngine *self = user_data; KotoPlaybackEngine * self = user_data;
if (self->is_playing) { // Is playing if (self->is_playing) { // Is playing
g_signal_emit(self, playback_engine_signals[SIGNAL_TICK_DURATION], 0); // Emit our 1s track tick g_signal_emit(self, playback_engine_signals[SIGNAL_TICK_DURATION], 0); // Emit our 1s track tick
@ -512,7 +554,8 @@ gboolean koto_playback_engine_tick_duration(gpointer user_data) {
} }
gboolean koto_playback_engine_tick_track(gpointer user_data) { gboolean koto_playback_engine_tick_track(gpointer user_data) {
KotoPlaybackEngine *self = user_data; KotoPlaybackEngine * self = user_data;
if (self->is_playing) { // Is playing if (self->is_playing) { // Is playing
g_signal_emit(self, playback_engine_signals[SIGNAL_TICK_TRACK], 0); // Emit our 100ms track tick g_signal_emit(self, playback_engine_signals[SIGNAL_TICK_TRACK], 0); // Emit our 100ms track tick
@ -523,14 +566,14 @@ gboolean koto_playback_engine_tick_track(gpointer user_data) {
return self->is_playing; return self->is_playing;
} }
void koto_playback_engine_toggle_track_repeat(KotoPlaybackEngine *self) { void koto_playback_engine_toggle_track_repeat(KotoPlaybackEngine * self) {
koto_playback_engine_set_track_repeat(self, !self->is_repeat_enabled); koto_playback_engine_set_track_repeat(self, !self->is_repeat_enabled);
} }
void koto_playback_engine_toggle_track_shuffle(KotoPlaybackEngine *self) { void koto_playback_engine_toggle_track_shuffle(KotoPlaybackEngine * self) {
koto_playback_engine_set_track_shuffle(self, !koto_playback_engine_get_track_shuffle(self)); // Invert the currently shuffling vale koto_playback_engine_set_track_shuffle(self, !koto_playback_engine_get_track_shuffle(self)); // Invert the currently shuffling vale
} }
KotoPlaybackEngine* koto_playback_engine_new() { KotoPlaybackEngine * koto_playback_engine_new() {
return g_object_new(KOTO_TYPE_PLAYBACK_ENGINE, NULL); return g_object_new(KOTO_TYPE_PLAYBACK_ENGINE, NULL);
} }

View file

@ -25,10 +25,10 @@ G_BEGIN_DECLS
/** /**
* Type Definition * Type Definition
**/ **/
#define KOTO_TYPE_PLAYBACK_ENGINE (koto_playback_engine_get_type()) #define KOTO_TYPE_PLAYBACK_ENGINE (koto_playback_engine_get_type())
#define KOTO_PLAYBACK_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), KOTO_TYPE_PLAYBACK_ENGINE, KotoPlaybackEngine)) #define KOTO_PLAYBACK_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), KOTO_TYPE_PLAYBACK_ENGINE, KotoPlaybackEngine))
#define KOTO_IS_PLAYBACK_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYBACK_ENGINE)) #define KOTO_IS_PLAYBACK_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYBACK_ENGINE))
typedef struct _KotoPlaybackEngine KotoPlaybackEngine; typedef struct _KotoPlaybackEngine KotoPlaybackEngine;
@ -39,32 +39,75 @@ GType koto_playback_engine_get_type(void) G_GNUC_CONST;
/** /**
* Playback Engine Functions * Playback Engine Functions
**/ **/
KotoPlaybackEngine * koto_playback_engine_new();
void koto_playback_engine_backwards(KotoPlaybackEngine * self);
KotoPlaybackEngine* koto_playback_engine_new();
void koto_playback_engine_backwards(KotoPlaybackEngine *self);
void koto_playback_engine_current_playlist_changed(); void koto_playback_engine_current_playlist_changed();
void koto_playback_engine_forwards(KotoPlaybackEngine *self);
KotoIndexedTrack* koto_playback_engine_get_current_track(KotoPlaybackEngine *self); void koto_playback_engine_forwards(KotoPlaybackEngine * self);
gint64 koto_playback_engine_get_duration(KotoPlaybackEngine *self);
GstState koto_playback_engine_get_state(KotoPlaybackEngine *self); KotoIndexedTrack * koto_playback_engine_get_current_track(KotoPlaybackEngine * self);
gdouble koto_playback_engine_get_progress(KotoPlaybackEngine *self);
gboolean koto_playback_engine_get_track_repeat(KotoPlaybackEngine *self); gint64 koto_playback_engine_get_duration(KotoPlaybackEngine * self);
gboolean koto_playback_engine_get_track_shuffle(KotoPlaybackEngine *self);
void koto_playback_engine_mute(KotoPlaybackEngine *self); GstState koto_playback_engine_get_state(KotoPlaybackEngine * self);
gboolean koto_playback_engine_monitor_changed(GstBus *bus, GstMessage *msg, gpointer user_data);
void koto_playback_engine_pause(KotoPlaybackEngine *self); gdouble koto_playback_engine_get_progress(KotoPlaybackEngine * self);
void koto_playback_engine_play(KotoPlaybackEngine *self);
void koto_playback_engine_toggle(KotoPlaybackEngine *self); gboolean koto_playback_engine_get_track_repeat(KotoPlaybackEngine * self);
void koto_playback_engine_set_position(KotoPlaybackEngine *self, int position);
void koto_playback_engine_set_track_repeat(KotoPlaybackEngine *self, gboolean enable_repeat); gboolean koto_playback_engine_get_track_shuffle(KotoPlaybackEngine * self);
void koto_playback_engine_set_track_shuffle(KotoPlaybackEngine *self, gboolean enable_shuffle);
void koto_playback_engine_set_track_by_uuid(KotoPlaybackEngine *self, gchar *track_uuid); void koto_playback_engine_mute(KotoPlaybackEngine * self);
void koto_playback_engine_set_volume(KotoPlaybackEngine *self, gdouble volume);
void koto_playback_engine_stop(KotoPlaybackEngine *self); gboolean koto_playback_engine_monitor_changed(
void koto_playback_engine_toggle_track_repeat(KotoPlaybackEngine *self); GstBus * bus,
void koto_playback_engine_toggle_track_shuffle(KotoPlaybackEngine *self); GstMessage * msg,
void koto_playback_engine_update_duration(KotoPlaybackEngine *self); gpointer user_data
);
void koto_playback_engine_pause(KotoPlaybackEngine * self);
void koto_playback_engine_play(KotoPlaybackEngine * self);
void koto_playback_engine_toggle(KotoPlaybackEngine * self);
void koto_playback_engine_set_position(
KotoPlaybackEngine * self,
int position
);
void koto_playback_engine_set_track_repeat(
KotoPlaybackEngine * self,
gboolean enable_repeat
);
void koto_playback_engine_set_track_shuffle(
KotoPlaybackEngine * self,
gboolean enable_shuffle
);
void koto_playback_engine_set_track_by_uuid(
KotoPlaybackEngine * self,
gchar * track_uuid
);
void koto_playback_engine_set_volume(
KotoPlaybackEngine * self,
gdouble volume
);
void koto_playback_engine_stop(KotoPlaybackEngine * self);
void koto_playback_engine_toggle_track_repeat(KotoPlaybackEngine * self);
void koto_playback_engine_toggle_track_shuffle(KotoPlaybackEngine * self);
void koto_playback_engine_update_duration(KotoPlaybackEngine * self);
gboolean koto_playback_engine_tick_duration(gpointer user_data); gboolean koto_playback_engine_tick_duration(gpointer user_data);
gboolean koto_playback_engine_tick_track(gpointer user_data); gboolean koto_playback_engine_tick_track(gpointer user_data);

View file

@ -21,30 +21,30 @@
#include "engine.h" #include "engine.h"
#include "media-keys.h" #include "media-keys.h"
extern GtkWindow *main_window; extern GtkWindow * main_window;
extern KotoPlaybackEngine *playback_engine; extern KotoPlaybackEngine * playback_engine;
GDBusConnection *media_keys_dbus_conn = NULL; GDBusConnection * media_keys_dbus_conn = NULL;
GDBusProxy *media_keys_proxy = NULL; GDBusProxy * media_keys_proxy = NULL;
GDBusNodeInfo *media_keys_introspection_data = NULL; GDBusNodeInfo * media_keys_introspection_data = NULL;
static const gchar introspection_xml[] = static const gchar introspection_xml[] =
"<node name='/org/gnome/SettingsDaemon/MediaKeys'>" "<node name='/org/gnome/SettingsDaemon/MediaKeys'>"
" <interface name='org.gnome.SettingsDaemon.MediaKeys'>" " <interface name='org.gnome.SettingsDaemon.MediaKeys'>"
" <annotation name='org.freedesktop.DBus.GLib.CSymbol' value='gsd_media_keys_manager'/>" " <annotation name='org.freedesktop.DBus.GLib.CSymbol' value='gsd_media_keys_manager'/>"
" <method name='GrabMediaPlayerKeys'>" " <method name='GrabMediaPlayerKeys'>"
" <arg name='application' direction='in' type='s'/>" " <arg name='application' direction='in' type='s'/>"
" <arg name='time' direction='in' type='u'/>" " <arg name='time' direction='in' type='u'/>"
" </method>" " </method>"
" <method name='ReleaseMediaPlayerKeys'>" " <method name='ReleaseMediaPlayerKeys'>"
" <arg name='application' direction='in' type='s'/>" " <arg name='application' direction='in' type='s'/>"
" </method>" " </method>"
" <signal name='MediaPlayerKeyPressed'>" " <signal name='MediaPlayerKeyPressed'>"
" <arg name='application' type='s'/>" " <arg name='application' type='s'/>"
" <arg name='key' type='s'/>" " <arg name='key' type='s'/>"
" </signal>" " </signal>"
" </interface>" " </interface>"
"</node>"; "</node>";
void grab_media_keys() { void grab_media_keys() {
if (media_keys_proxy == NULL) { // No connection if (media_keys_proxy == NULL) { // No connection
@ -63,19 +63,32 @@ void grab_media_keys() {
); );
} }
void handle_media_keys_async_done(GObject *source_object, GAsyncResult *res, gpointer user_data) { void handle_media_keys_async_done(
GObject * source_object,
GAsyncResult * res,
gpointer user_data
) {
(void) user_data; (void) user_data;
g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object), res, NULL); // Ensure we finish our call g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object), res, NULL); // Ensure we finish our call
} }
void handle_media_keys_signal(GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { void handle_media_keys_signal(
(void) proxy; (void) sender_name; (void) user_data; GDBusProxy * proxy,
const gchar * sender_name,
const gchar * signal_name,
GVariant * parameters,
gpointer user_data
) {
(void) proxy;
(void) sender_name;
(void) user_data;
if (g_strcmp0(signal_name, "MediaPlayerKeyPressed") != 0) { // Not MediaPlayerKeyPressed if (g_strcmp0(signal_name, "MediaPlayerKeyPressed") != 0) { // Not MediaPlayerKeyPressed
return; return;
} }
gchar *application_name = NULL; gchar * application_name = NULL;
gchar *key = NULL; gchar * key = NULL;
g_variant_get(parameters, "(ss)", &application_name, &key); g_variant_get(parameters, "(ss)", &application_name, &key);
@ -100,13 +113,21 @@ void handle_media_keys_signal(GDBusProxy *proxy, const gchar *sender_name, const
} }
} }
void handle_window_enter(GtkEventControllerFocus *controller, gpointer user_data) { void handle_window_enter(
(void) controller; (void) user_data; GtkEventControllerFocus * controller,
gpointer user_data
) {
(void) controller;
(void) user_data;
grab_media_keys(); // Grab our media keys grab_media_keys(); // Grab our media keys
} }
void handle_window_leave(GtkEventControllerFocus *controller, gpointer user_data) { void handle_window_leave(
(void) controller; (void) user_data; GtkEventControllerFocus * controller,
gpointer user_data
) {
(void) controller;
(void) user_data;
release_media_keys(); // Release our media keys release_media_keys(); // Release our media keys
} }
@ -115,7 +136,8 @@ void release_media_keys() {
return; return;
} }
GVariant *params = g_variant_new_string(g_strdup("com.github.joshstrobl.koto")); GVariant * params = g_variant_new_string(g_strdup("com.github.joshstrobl.koto"));
g_dbus_proxy_call( g_dbus_proxy_call(
media_keys_proxy, media_keys_proxy,
@ -133,8 +155,9 @@ void setup_mediakeys_interface() {
media_keys_introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); media_keys_introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
g_assert(media_keys_introspection_data != NULL); g_assert(media_keys_introspection_data != NULL);
GDBusConnection *bus; GDBusConnection * bus;
GError *error = NULL; GError * error = NULL;
bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
@ -169,7 +192,9 @@ void setup_mediakeys_interface() {
0 0
); );
GtkEventController *focus_controller = gtk_event_controller_focus_new(); // Create a new focus controller GtkEventController * focus_controller = gtk_event_controller_focus_new(); // Create a new focus controller
g_signal_connect(focus_controller, "enter", G_CALLBACK(handle_window_enter), NULL); g_signal_connect(focus_controller, "enter", G_CALLBACK(handle_window_enter), NULL);
g_signal_connect(focus_controller, "leave", G_CALLBACK(handle_window_leave), NULL); g_signal_connect(focus_controller, "leave", G_CALLBACK(handle_window_leave), NULL);
gtk_widget_add_controller(GTK_WIDGET(main_window), focus_controller); gtk_widget_add_controller(GTK_WIDGET(main_window), focus_controller);

View file

@ -22,11 +22,33 @@
G_BEGIN_DECLS G_BEGIN_DECLS
void grab_media_keys(); void grab_media_keys();
void handle_media_keys_async_done(GObject *source_object, GAsyncResult *res, gpointer user_data);
void handle_media_keys_signal(GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, gpointer user_data); void handle_media_keys_async_done(
void handle_window_enter(GtkEventControllerFocus *controller, gpointer user_data); GObject * source_object,
void handle_window_leave(GtkEventControllerFocus *controller, gpointer user_data); GAsyncResult * res,
gpointer user_data
);
void handle_media_keys_signal(
GDBusProxy * proxy,
const gchar * sender_name,
const gchar * signal_name,
GVariant * parameters,
gpointer user_data
);
void handle_window_enter(
GtkEventControllerFocus * controller,
gpointer user_data
);
void handle_window_leave(
GtkEventControllerFocus * controller,
gpointer user_data
);
void release_media_keys(); void release_media_keys();
void setup_mediakeys_interface(); void setup_mediakeys_interface();
G_END_DECLS G_END_DECLS

View file

@ -20,12 +20,18 @@
#include <gstreamer-1.0/gst/gst.h> #include <gstreamer-1.0/gst/gst.h>
#include "../koto-utils.h" #include "../koto-utils.h"
GHashTable *supported_mimes_hash = NULL; GHashTable * supported_mimes_hash = NULL;
GList *supported_mimes = NULL; GList * supported_mimes = NULL;
gboolean koto_playback_engine_gst_caps_iter(
GstCapsFeatures * features,
GstStructure * structure,
gpointer user_data
) {
(void) features;
(void) user_data;
gchar * caps_name = (gchar*) gst_structure_get_name(structure); // Get the name, typically a mimetype
gboolean koto_playback_engine_gst_caps_iter(GstCapsFeatures *features, GstStructure *structure, gpointer user_data) {
(void) features; (void) user_data;
gchar *caps_name = (gchar*) gst_structure_get_name(structure); // Get the name, typically a mimetype
if (g_str_has_prefix(caps_name, "unknown")) { // Is unknown if (g_str_has_prefix(caps_name, "unknown")) { // Is unknown
return TRUE; return TRUE;
@ -42,11 +48,16 @@ gboolean koto_playback_engine_gst_caps_iter(GstCapsFeatures *features, GstStruct
return TRUE; return TRUE;
} }
void koto_playback_engine_gst_pad_iter(gpointer list_data, gpointer user_data) { void koto_playback_engine_gst_pad_iter(
gpointer list_data,
gpointer user_data
) {
(void) user_data; (void) user_data;
GstStaticPadTemplate *templ = list_data; GstStaticPadTemplate * templ = list_data;
if (templ->direction == GST_PAD_SINK) { // Is a sink pad if (templ->direction == GST_PAD_SINK) { // Is a sink pad
GstCaps *capabilities = gst_static_pad_template_get_caps(templ); // Get the capabilities GstCaps * capabilities = gst_static_pad_template_get_caps(templ); // Get the capabilities
gst_caps_foreach(capabilities, koto_playback_engine_gst_caps_iter, NULL); // Iterate over and add to mimes gst_caps_foreach(capabilities, koto_playback_engine_gst_caps_iter, NULL); // Iterate over and add to mimes
gst_caps_unref(capabilities); gst_caps_unref(capabilities);
} }
@ -55,12 +66,14 @@ void koto_playback_engine_gst_pad_iter(gpointer list_data, gpointer user_data) {
void koto_playback_engine_get_supported_mimetypes() { void koto_playback_engine_get_supported_mimetypes() {
// Credit for code goes to https://github.com/mopidy/mopidy/issues/812#issuecomment-75868363 // Credit for code goes to https://github.com/mopidy/mopidy/issues/812#issuecomment-75868363
// These are GstElementFactory // These are GstElementFactory
GList *elements = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEPAYLOADER | GST_ELEMENT_FACTORY_TYPE_DEMUXER | GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_NONE); GList * elements = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEPAYLOADER | GST_ELEMENT_FACTORY_TYPE_DEMUXER | GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_NONE);
GList * ele;
GList *ele;
for (ele = elements; ele != NULL; ele = ele->next) { // For each of the elements for (ele = elements; ele != NULL; ele = ele->next) { // For each of the elements
// GList of GstStaticPadTemplate // GList of GstStaticPadTemplate
GList *static_pads = (GList*) gst_element_factory_get_static_pad_templates(ele->data); // Get the pads GList * static_pads = (GList*) gst_element_factory_get_static_pad_templates(ele->data); // Get the pads
g_list_foreach(static_pads, koto_playback_engine_gst_pad_iter, NULL); g_list_foreach(static_pads, koto_playback_engine_gst_pad_iter, NULL);
} }
} }

View file

@ -21,8 +21,17 @@
G_BEGIN_DECLS G_BEGIN_DECLS
gboolean koto_bplayback_engine_gst_caps_iter(GstCapsFeatures *features, GstStructure *structure, gpointer user_data); gboolean koto_bplayback_engine_gst_caps_iter(
void koto_playback_engine_gst_pad_iter(gpointer list_data, gpointer user_data); GstCapsFeatures * features,
void koto_playback_engine_get_supported_mimetypes(GList *mimes); GstStructure * structure,
gpointer user_data
);
void koto_playback_engine_gst_pad_iter(
gpointer list_data,
gpointer user_data
);
void koto_playback_engine_get_supported_mimetypes(GList * mimes);
G_END_DECLS G_END_DECLS

View file

@ -28,73 +28,78 @@
#include "mimes.h" #include "mimes.h"
#include "mpris.h" #include "mpris.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoCurrentPlaylist *current_playlist; extern KotoCurrentPlaylist * current_playlist;
extern GtkApplication *app; extern GtkApplication * app;
extern GtkWindow *main_window; extern GtkWindow * main_window;
extern KotoPlaybackEngine *playback_engine; extern KotoPlaybackEngine * playback_engine;
extern GList *supported_mimes; extern GList * supported_mimes;
GDBusConnection *dbus_conn = NULL; GDBusConnection * dbus_conn = NULL;
guint mpris_bus_id = 0; guint mpris_bus_id = 0;
GDBusNodeInfo *introspection_data = NULL; GDBusNodeInfo * introspection_data = NULL;
static const gchar introspection_xml[] = static const gchar introspection_xml[] =
"<node>" "<node>"
" <interface name='org.mpris.MediaPlayer2'>" " <interface name='org.mpris.MediaPlayer2'>"
" <method name='Raise' />" " <method name='Raise' />"
" <method name='Quit' />" " <method name='Quit' />"
" <property type='b' name='CanQuit' access='read' />" " <property type='b' name='CanQuit' access='read' />"
" <property type='b' name='CanRaise' access='read' />" " <property type='b' name='CanRaise' access='read' />"
" <property type='b' name='HasTrackList' access='read' />" " <property type='b' name='HasTrackList' access='read' />"
" <property type='s' name='Identity' access='read' />" " <property type='s' name='Identity' access='read' />"
" <property type='s' name='DesktopEntry' access='read' />" " <property type='s' name='DesktopEntry' access='read' />"
" <property type='as' name='SupportedUriSchemas' access='read' />" " <property type='as' name='SupportedUriSchemas' access='read' />"
" <property type='as' name='SupportedMimeTypes' access='read' />" " <property type='as' name='SupportedMimeTypes' access='read' />"
" </interface>" " </interface>"
" <interface name='org.mpris.MediaPlayer2.Player'>" " <interface name='org.mpris.MediaPlayer2.Player'>"
" <method name='Next' />" " <method name='Next' />"
" <method name='Previous' />" " <method name='Previous' />"
" <method name='Pause' />" " <method name='Pause' />"
" <method name='PlayPause' />" " <method name='PlayPause' />"
" <method name='Stop' />" " <method name='Stop' />"
" <method name='Play' />" " <method name='Play' />"
" <method name='Seek'>" " <method name='Seek'>"
" <arg type='x' name='offset' />" " <arg type='x' name='offset' />"
" </method>" " </method>"
" <method name='SetPosition'>" " <method name='SetPosition'>"
" <arg type='o' name='track_id' />" " <arg type='o' name='track_id' />"
" <arg type='x' name='pos' />" " <arg type='x' name='pos' />"
" </method>" " </method>"
" <property type='s' name='PlaybackStatus' access='read' />" " <property type='s' name='PlaybackStatus' access='read' />"
" <property type='s' name='LoopStatus' access='readwrite' />" " <property type='s' name='LoopStatus' access='readwrite' />"
" <property type='d' name='Rate' access='readwrite' />" " <property type='d' name='Rate' access='readwrite' />"
" <property type='b' name='Shuffle' access='readwrite' />" " <property type='b' name='Shuffle' access='readwrite' />"
" <property type='a{sv}' name='Metadata' access='read' />" " <property type='a{sv}' name='Metadata' access='read' />"
" <property type='d' name='Volume' access='readwrite' />" " <property type='d' name='Volume' access='readwrite' />"
" <property type='x' name='Position' access='read' />" " <property type='x' name='Position' access='read' />"
" <property type='d' name='MinimumRate' access='read' />" " <property type='d' name='MinimumRate' access='read' />"
" <property type='d' name='MaximumRate' access='read' />" " <property type='d' name='MaximumRate' access='read' />"
" <property type='b' name='CanGoNext' access='read' />" " <property type='b' name='CanGoNext' access='read' />"
" <property type='b' name='CanGoPrevious' access='read' />" " <property type='b' name='CanGoPrevious' access='read' />"
" <property type='b' name='CanPlay' access='read' />" " <property type='b' name='CanPlay' access='read' />"
" <property type='b' name='CanPause' access='read' />" " <property type='b' name='CanPause' access='read' />"
" <property type='b' name='CanSeek' access='read' />" " <property type='b' name='CanSeek' access='read' />"
" <property type='b' name='CanControl' access='read' />" " <property type='b' name='CanControl' access='read' />"
" </interface>" " </interface>"
"</node>"; "</node>";
void handle_method_call( void handle_method_call(
GDBusConnection *connection, GDBusConnection * connection,
const gchar *sender, const gchar * sender,
const gchar *object_path, const gchar * object_path,
const gchar *interface_name, const gchar * interface_name,
const gchar *method_name, const gchar * method_name,
GVariant *parameters, GVariant * parameters,
GDBusMethodInvocation *invocation, GDBusMethodInvocation * invocation,
gpointer user_data gpointer user_data
) { ) {
(void) connection; (void) sender; (void) object_path; (void) parameters; (void) invocation; (void) user_data; (void) connection;
(void) sender;
(void) object_path;
(void) parameters;
(void) invocation;
(void) user_data;
if (g_strcmp0(interface_name, "org.mpris.MediaPlayer2") == 0) { // Root mediaplayer2 interface if (g_strcmp0(interface_name, "org.mpris.MediaPlayer2") == 0) { // Root mediaplayer2 interface
if (g_strcmp0(method_name, "Raise") == 0) { // Raise the window if (g_strcmp0(method_name, "Raise") == 0) { // Raise the window
@ -140,17 +145,24 @@ void handle_method_call(
} }
} }
GVariant* handle_get_property( GVariant * handle_get_property(
GDBusConnection *connection, GDBusConnection * connection,
const gchar *sender, const gchar * sender,
const gchar *object_path, const gchar * object_path,
const gchar *interface_name, const gchar * interface_name,
const gchar *property_name, const gchar * property_name,
GError **error, GError ** error,
gpointer user_data gpointer user_data
) { ) {
(void) connection; (void) sender; (void) object_path; (void) interface_name; (void) error; (void) user_data; (void) connection;
GVariant *ret; (void) sender;
(void) object_path;
(void) interface_name;
(void) error;
(void) user_data;
GVariant * ret;
ret = NULL; ret = NULL;
if (g_strcmp0(property_name, "CanQuit") == 0) { // If property is CanQuit if (g_strcmp0(property_name, "CanQuit") == 0) { // If property is CanQuit
@ -162,7 +174,7 @@ GVariant* handle_get_property(
} }
if (g_strcmp0(property_name, "HasTrackList") == 0) { // If property is HasTrackList if (g_strcmp0(property_name, "HasTrackList") == 0) { // If property is HasTrackList
KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); KotoPlaylist * playlist = koto_current_playlist_get_playlist(current_playlist);
if (KOTO_IS_PLAYLIST(playlist)) { if (KOTO_IS_PLAYLIST(playlist)) {
ret = g_variant_new_boolean(koto_playlist_get_length(playlist) > 0); ret = g_variant_new_boolean(koto_playlist_get_length(playlist) > 0);
} else { // Don't have a playlist } else { // Don't have a playlist
@ -179,15 +191,15 @@ GVariant* handle_get_property(
} }
if (g_strcmp0(property_name, "SupportedUriSchemas") == 0) { // Supported URI Schemas if (g_strcmp0(property_name, "SupportedUriSchemas") == 0) { // Supported URI Schemas
GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE("as")); // Array of strings GVariantBuilder * builder = g_variant_builder_new(G_VARIANT_TYPE("as")); // Array of strings
g_variant_builder_add(builder, "s", "file"); g_variant_builder_add(builder, "s", "file");
ret = g_variant_new("as", builder); ret = g_variant_new("as", builder);
g_variant_builder_unref(builder); // Unref builder since we no longer need it g_variant_builder_unref(builder); // Unref builder since we no longer need it
} }
if (g_strcmp0(property_name, "SupportedMimeTypes") == 0) { // Supported mimetypes if (g_strcmp0(property_name, "SupportedMimeTypes") == 0) { // Supported mimetypes
GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE("as")); // Array of strings GVariantBuilder * builder = g_variant_builder_new(G_VARIANT_TYPE("as")); // Array of strings
GList *mimes; GList * mimes;
mimes = NULL; mimes = NULL;
for (mimes = supported_mimes; mimes != NULL; mimes = mimes->next) { // For each mimetype for (mimes = supported_mimes; mimes != NULL; mimes = mimes->next) { // For each mimetype
@ -200,12 +212,12 @@ GVariant* handle_get_property(
} }
if (g_strcmp0(property_name, "Metadata") == 0) { // Metadata if (g_strcmp0(property_name, "Metadata") == 0) { // Metadata
KotoIndexedTrack *current_track = koto_playback_engine_get_current_track(playback_engine); KotoIndexedTrack * current_track = koto_playback_engine_get_current_track(playback_engine);
if (KOTO_IS_INDEXED_TRACK(current_track)) { // Currently playing a track if (KOTO_IS_INDEXED_TRACK(current_track)) { // Currently playing a track
ret = koto_indexed_track_get_metadata_vardict(current_track); ret = koto_indexed_track_get_metadata_vardict(current_track);
} else { // No track } else { // No track
GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT); // Create an empty builder GVariantBuilder * builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT); // Create an empty builder
ret = g_variant_builder_end(builder); // return the vardict ret = g_variant_builder_end(builder); // return the vardict
} }
} }
@ -214,7 +226,7 @@ GVariant* handle_get_property(
(g_strcmp0(property_name, "CanPlay") == 0) || (g_strcmp0(property_name, "CanPlay") == 0) ||
(g_strcmp0(property_name, "CanPause") == 0) (g_strcmp0(property_name, "CanPause") == 0)
) { ) {
KotoIndexedTrack *current_track = koto_playback_engine_get_current_track(playback_engine); KotoIndexedTrack * current_track = koto_playback_engine_get_current_track(playback_engine);
ret = g_variant_new_boolean(KOTO_IS_INDEXED_TRACK(current_track)); ret = g_variant_new_boolean(KOTO_IS_INDEXED_TRACK(current_track));
} }
@ -232,7 +244,7 @@ GVariant* handle_get_property(
ret = g_variant_new_boolean(TRUE); ret = g_variant_new_boolean(TRUE);
} }
if (g_strcmp0(property_name, "CanControl") == 0){ // Can Control if (g_strcmp0(property_name, "CanControl") == 0) { // Can Control
ret = g_variant_new_boolean(TRUE); ret = g_variant_new_boolean(TRUE);
} }
@ -252,16 +264,21 @@ GVariant* handle_get_property(
} }
gboolean handle_set_property( gboolean handle_set_property(
GDBusConnection *connection, GDBusConnection * connection,
const gchar *sender, const gchar * sender,
const gchar *object_path, const gchar * object_path,
const gchar *interface_name, const gchar * interface_name,
const gchar *property_name, const gchar * property_name,
GVariant *value, GVariant * value,
GError **error, GError ** error,
gpointer user_data gpointer user_data
) { ) {
(void) connection; (void) sender; (void) interface_name; (void) object_path; (void) error; (void) user_data; (void) connection;
(void) sender;
(void) interface_name;
(void) object_path;
(void) error;
(void) user_data;
if (g_strcmp0(property_name, "LoopStatus") == 0) { // Changing LoopStatus if (g_strcmp0(property_name, "LoopStatus") == 0) { // Changing LoopStatus
koto_playback_engine_set_track_repeat(playback_engine, g_variant_get_boolean(value)); // Set the loop status state koto_playback_engine_set_track_repeat(playback_engine, g_variant_get_boolean(value)); // Set the loop status state
@ -277,7 +294,8 @@ gboolean handle_set_property(
} }
void koto_update_mpris_playback_state(GstState state) { void koto_update_mpris_playback_state(GstState state) {
GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); GVariantBuilder * builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY);
if (state == GST_STATE_PLAYING) { if (state == GST_STATE_PLAYING) {
g_variant_builder_add(builder, "{sv}", "PlaybackStatus", g_variant_new_string("Playing")); g_variant_builder_add(builder, "{sv}", "PlaybackStatus", g_variant_new_string("Playing"));
@ -287,7 +305,8 @@ void koto_update_mpris_playback_state(GstState state) {
g_variant_builder_add(builder, "{sv}", "PlaybackStatus", g_variant_new_string("Stopped")); g_variant_builder_add(builder, "{sv}", "PlaybackStatus", g_variant_new_string("Stopped"));
} }
g_dbus_connection_emit_signal(dbus_conn, g_dbus_connection_emit_signal(
dbus_conn,
NULL, NULL,
"/org/mpris/MediaPlayer2", "/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties", "org.freedesktop.DBus.Properties",
@ -297,24 +316,32 @@ void koto_update_mpris_playback_state(GstState state) {
); );
} }
void koto_update_mpris_info_for_track(KotoIndexedTrack *track) { void koto_update_mpris_info_for_track(KotoIndexedTrack * track) {
if (!KOTO_IS_INDEXED_TRACK(track)) { if (!KOTO_IS_INDEXED_TRACK(track)) {
return; return;
} }
GVariant *metadata = koto_indexed_track_get_metadata_vardict(track); // Get the GVariantBuilder variable dict for the metadata GVariant * metadata = koto_indexed_track_get_metadata_vardict(track); // Get the GVariantBuilder variable dict for the metadata
koto_update_mpris_info_for_track_with_metadata(track, metadata); koto_update_mpris_info_for_track_with_metadata(track, metadata);
} }
void koto_update_mpris_info_for_track_with_metadata(KotoIndexedTrack *track, GVariant *metadata) { void koto_update_mpris_info_for_track_with_metadata(
KotoIndexedTrack * track,
GVariant * metadata
) {
if (!KOTO_IS_INDEXED_TRACK(track)) { if (!KOTO_IS_INDEXED_TRACK(track)) {
return; return;
} }
GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); GVariantBuilder * builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY);
g_variant_builder_add(builder, "{sv}", "Metadata", metadata); g_variant_builder_add(builder, "{sv}", "Metadata", metadata);
g_dbus_connection_emit_signal(dbus_conn, g_dbus_connection_emit_signal(
dbus_conn,
NULL, NULL,
"/org/mpris/MediaPlayer2", "/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties", "org.freedesktop.DBus.Properties",
@ -331,10 +358,16 @@ static const GDBusInterfaceVTable main_mpris_interface_vtable = {
{ 0 } { 0 }
}; };
void on_main_mpris_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { void on_main_mpris_bus_acquired(
(void) name; (void) user_data; GDBusConnection * connection,
const gchar * name,
gpointer user_data
) {
(void) name;
(void) user_data;
dbus_conn = connection; dbus_conn = connection;
g_dbus_connection_register_object(dbus_conn, g_dbus_connection_register_object(
dbus_conn,
"/org/mpris/MediaPlayer2", "/org/mpris/MediaPlayer2",
introspection_data->interfaces[0], introspection_data->interfaces[0],
&main_mpris_interface_vtable, &main_mpris_interface_vtable,
@ -343,7 +376,8 @@ void on_main_mpris_bus_acquired(GDBusConnection *connection, const gchar *name,
NULL NULL
); );
g_dbus_connection_register_object(dbus_conn, g_dbus_connection_register_object(
dbus_conn,
"/org/mpris/MediaPlayer2", "/org/mpris/MediaPlayer2",
introspection_data->interfaces[1], introspection_data->interfaces[1],
&main_mpris_interface_vtable, &main_mpris_interface_vtable,
@ -357,7 +391,8 @@ void setup_mpris_interfaces() {
introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
g_assert(introspection_data != NULL); g_assert(introspection_data != NULL);
mpris_bus_id = g_bus_own_name(G_BUS_TYPE_SESSION, mpris_bus_id = g_bus_own_name(
G_BUS_TYPE_SESSION,
"org.mpris.MediaPlayer2.koto", "org.mpris.MediaPlayer2.koto",
G_BUS_NAME_OWNER_FLAGS_NONE, G_BUS_NAME_OWNER_FLAGS_NONE,
on_main_mpris_bus_acquired, on_main_mpris_bus_acquired,

View file

@ -22,10 +22,50 @@
#include "../indexer/structs.h" #include "../indexer/structs.h"
void koto_update_mpris_playback_state(GstState state); void koto_update_mpris_playback_state(GstState state);
void koto_update_mpris_info_for_track(KotoIndexedTrack *track);
void koto_update_mpris_info_for_track_with_metadata(KotoIndexedTrack *track, GVariant *metadata); void koto_update_mpris_info_for_track(KotoIndexedTrack * track);
void handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data);
GVariant* handle_get_property(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data); void koto_update_mpris_info_for_track_with_metadata(
gboolean handle_set_property(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error, gpointer user_data); KotoIndexedTrack * track,
void on_main_mpris_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data); GVariant * metadata
);
void handle_method_call(
GDBusConnection * connection,
const gchar * sender,
const gchar * object_path,
const gchar * interface_name,
const gchar * method_name,
GVariant * parameters,
GDBusMethodInvocation * invocation,
gpointer user_data
);
GVariant * handle_get_property(
GDBusConnection * connection,
const gchar * sender,
const gchar * object_path,
const gchar * interface_name,
const gchar * property_name,
GError ** error,
gpointer user_data
);
gboolean handle_set_property(
GDBusConnection * connection,
const gchar * sender,
const gchar * object_path,
const gchar * interface_name,
const gchar * property_name,
GVariant * value,
GError ** error,
gpointer user_data
);
void on_main_mpris_bus_acquired(
GDBusConnection * connection,
const gchar * name,
gpointer user_data
);
void setup_mpris_interfaces(); void setup_mpris_interfaces();

View file

@ -22,27 +22,27 @@
#include "../playlist/playlist.h" #include "../playlist/playlist.h"
#include "add-remove-track-popover.h" #include "add-remove-track-popover.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
struct _KotoAddRemoveTrackPopover { struct _KotoAddRemoveTrackPopover {
GtkPopover parent_instance; GtkPopover parent_instance;
GtkWidget *list_box; GtkWidget * list_box;
GHashTable *checkbox_to_playlist_uuid; GHashTable * checkbox_to_playlist_uuid;
GHashTable *playlist_uuid_to_checkbox; GHashTable * playlist_uuid_to_checkbox;
GList *tracks; GList * tracks;
GHashTable *checkbox_to_signal_ids; GHashTable * checkbox_to_signal_ids;
}; };
G_DEFINE_TYPE(KotoAddRemoveTrackPopover, koto_add_remove_track_popover, GTK_TYPE_POPOVER); G_DEFINE_TYPE(KotoAddRemoveTrackPopover, koto_add_remove_track_popover, GTK_TYPE_POPOVER);
KotoAddRemoveTrackPopover *koto_add_remove_track_popup = NULL; KotoAddRemoveTrackPopover * koto_add_remove_track_popup = NULL;
static void koto_add_remove_track_popover_class_init(KotoAddRemoveTrackPopoverClass *c) { static void koto_add_remove_track_popover_class_init(KotoAddRemoveTrackPopoverClass * c) {
(void) c; (void) c;
} }
static void koto_add_remove_track_popover_init(KotoAddRemoveTrackPopover *self) { static void koto_add_remove_track_popover_init(KotoAddRemoveTrackPopover * self) {
self->list_box = gtk_list_box_new(); // Create our new GtkListBox self->list_box = gtk_list_box_new(); // Create our new GtkListBox
gtk_list_box_set_selection_mode(GTK_LIST_BOX(self->list_box), GTK_SELECTION_NONE); gtk_list_box_set_selection_mode(GTK_LIST_BOX(self->list_box), GTK_SELECTION_NONE);
@ -58,7 +58,10 @@ static void koto_add_remove_track_popover_init(KotoAddRemoveTrackPopover *self)
g_signal_connect(koto_maps, "playlist-removed", G_CALLBACK(koto_add_remove_track_popover_handle_playlist_removed), self); g_signal_connect(koto_maps, "playlist-removed", G_CALLBACK(koto_add_remove_track_popover_handle_playlist_removed), self);
} }
void koto_add_remove_track_popover_add_playlist(KotoAddRemoveTrackPopover *self, KotoPlaylist *playlist) { void koto_add_remove_track_popover_add_playlist(
KotoAddRemoveTrackPopover * self,
KotoPlaylist * playlist
) {
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) { if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
return; return;
} }
@ -67,24 +70,29 @@ void koto_add_remove_track_popover_add_playlist(KotoAddRemoveTrackPopover *self,
return; return;
} }
gchar *playlist_uuid = koto_playlist_get_uuid(playlist); // Get the UUID of the playlist gchar * playlist_uuid = koto_playlist_get_uuid(playlist); // Get the UUID of the playlist
if (GTK_IS_CHECK_BUTTON(g_hash_table_lookup(self->playlist_uuid_to_checkbox, playlist_uuid))) { // Already have a check button for this if (GTK_IS_CHECK_BUTTON(g_hash_table_lookup(self->playlist_uuid_to_checkbox, playlist_uuid))) { // Already have a check button for this
g_free(playlist_uuid); g_free(playlist_uuid);
return; return;
} }
GtkWidget *playlist_button = gtk_check_button_new_with_label(koto_playlist_get_name(playlist)); // Create our GtkCheckButton GtkWidget * playlist_button = gtk_check_button_new_with_label(koto_playlist_get_name(playlist)); // Create our GtkCheckButton
g_hash_table_insert(self->checkbox_to_playlist_uuid, playlist_button, playlist_uuid); g_hash_table_insert(self->checkbox_to_playlist_uuid, playlist_button, playlist_uuid);
g_hash_table_insert(self->playlist_uuid_to_checkbox, playlist_uuid, playlist_button); g_hash_table_insert(self->playlist_uuid_to_checkbox, playlist_uuid, playlist_button);
gulong playlist_sig_id = g_signal_connect(playlist_button, "toggled", G_CALLBACK(koto_add_remove_track_popover_handle_checkbutton_toggle), self); gulong playlist_sig_id = g_signal_connect(playlist_button, "toggled", G_CALLBACK(koto_add_remove_track_popover_handle_checkbutton_toggle), self);
g_hash_table_insert(self->checkbox_to_signal_ids, playlist_button, GUINT_TO_POINTER(playlist_sig_id)); // Add our GSignal handler ID g_hash_table_insert(self->checkbox_to_signal_ids, playlist_button, GUINT_TO_POINTER(playlist_sig_id)); // Add our GSignal handler ID
gtk_list_box_append(GTK_LIST_BOX(self->list_box), playlist_button); // Add the playlist to the list box gtk_list_box_append(GTK_LIST_BOX(self->list_box), playlist_button); // Add the playlist to the list box
} }
void koto_add_remove_track_popover_clear_tracks(KotoAddRemoveTrackPopover *self) { void koto_add_remove_track_popover_clear_tracks(KotoAddRemoveTrackPopover * self) {
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) { if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
return; return;
} }
@ -95,7 +103,10 @@ void koto_add_remove_track_popover_clear_tracks(KotoAddRemoveTrackPopover *self)
} }
} }
void koto_add_remove_track_popover_remove_playlist(KotoAddRemoveTrackPopover *self, gchar *playlist_uuid) { void koto_add_remove_track_popover_remove_playlist(
KotoAddRemoveTrackPopover * self,
gchar * playlist_uuid
) {
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) { if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
return; return;
} }
@ -104,7 +115,8 @@ void koto_add_remove_track_popover_remove_playlist(KotoAddRemoveTrackPopover *se
return; return;
} }
GtkCheckButton *btn = GTK_CHECK_BUTTON(g_hash_table_lookup(self->playlist_uuid_to_checkbox, playlist_uuid)); // Get the check button GtkCheckButton * btn = GTK_CHECK_BUTTON(g_hash_table_lookup(self->playlist_uuid_to_checkbox, playlist_uuid)); // Get the check button
if (GTK_IS_CHECK_BUTTON(btn)) { // Is a check button if (GTK_IS_CHECK_BUTTON(btn)) { // Is a check button
g_hash_table_remove(self->checkbox_to_playlist_uuid, btn); // Remove uuid based on btn g_hash_table_remove(self->checkbox_to_playlist_uuid, btn); // Remove uuid based on btn
@ -114,31 +126,38 @@ void koto_add_remove_track_popover_remove_playlist(KotoAddRemoveTrackPopover *se
g_hash_table_remove(self->playlist_uuid_to_checkbox, playlist_uuid); g_hash_table_remove(self->playlist_uuid_to_checkbox, playlist_uuid);
} }
void koto_add_remove_track_popover_handle_checkbutton_toggle(GtkCheckButton *btn, gpointer user_data) { void koto_add_remove_track_popover_handle_checkbutton_toggle(
KotoAddRemoveTrackPopover *self = user_data; GtkCheckButton * btn,
gpointer user_data
) {
KotoAddRemoveTrackPopover * self = user_data;
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) { if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
return; return;
} }
gboolean should_add = gtk_check_button_get_active(btn); // Get the now active state gboolean should_add = gtk_check_button_get_active(btn); // Get the now active state
gchar *playlist_uuid = g_hash_table_lookup(self->checkbox_to_playlist_uuid, btn); // Get the playlist UUID for this button gchar * playlist_uuid = g_hash_table_lookup(self->checkbox_to_playlist_uuid, btn); // Get the playlist UUID for this button
KotoPlaylist * playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, playlist_uuid); // Get the playlist
KotoPlaylist *playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, playlist_uuid); // Get the playlist
if (!KOTO_IS_PLAYLIST(playlist)) { // Failed to get the playlist if (!KOTO_IS_PLAYLIST(playlist)) { // Failed to get the playlist
return; return;
} }
GList *pos; GList * pos;
for (pos = self->tracks; pos != NULL; pos = pos->next) { // Iterate over our KotoIndexedTracks for (pos = self->tracks; pos != NULL; pos = pos->next) { // Iterate over our KotoIndexedTracks
KotoIndexedTrack *track = pos->data; KotoIndexedTrack * track = pos->data;
if (!KOTO_INDEXED_TRACK(track)) { // Not a track if (!KOTO_INDEXED_TRACK(track)) { // Not a track
continue; // Skip this continue; // Skip this
} }
gchar *track_uuid = koto_indexed_track_get_uuid(track); // Get the track gchar * track_uuid = koto_indexed_track_get_uuid(track); // Get the track
if (should_add) { // Should be adding if (should_add) { // Should be adding
koto_playlist_add_track_by_uuid(playlist, track_uuid, FALSE, TRUE); // Add the track to the playlist koto_playlist_add_track_by_uuid(playlist, track_uuid, FALSE, TRUE); // Add the track to the playlist
@ -150,9 +169,14 @@ void koto_add_remove_track_popover_handle_checkbutton_toggle(GtkCheckButton *btn
gtk_popover_popdown(GTK_POPOVER(self)); // Temporary to hopefully prevent a bork gtk_popover_popdown(GTK_POPOVER(self)); // Temporary to hopefully prevent a bork
} }
void koto_add_remove_track_popover_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playlist, gpointer user_data) { void koto_add_remove_track_popover_handle_playlist_added(
KotoCartographer * carto,
KotoPlaylist * playlist,
gpointer user_data
) {
(void) carto; (void) carto;
KotoAddRemoveTrackPopover *self = user_data; KotoAddRemoveTrackPopover * self = user_data;
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) { if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
return; return;
@ -161,9 +185,14 @@ void koto_add_remove_track_popover_handle_playlist_added(KotoCartographer *carto
koto_add_remove_track_popover_add_playlist(self, playlist); koto_add_remove_track_popover_add_playlist(self, playlist);
} }
void koto_add_remove_track_popover_handle_playlist_removed(KotoCartographer *carto, gchar *playlist_uuid, gpointer user_data) { void koto_add_remove_track_popover_handle_playlist_removed(
KotoCartographer * carto,
gchar * playlist_uuid,
gpointer user_data
) {
(void) carto; (void) carto;
KotoAddRemoveTrackPopover *self = user_data; KotoAddRemoveTrackPopover * self = user_data;
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) { if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
return; return;
@ -172,7 +201,11 @@ void koto_add_remove_track_popover_handle_playlist_removed(KotoCartographer *car
koto_add_remove_track_popover_remove_playlist(self, playlist_uuid); koto_add_remove_track_popover_remove_playlist(self, playlist_uuid);
} }
void koto_add_remove_track_popover_set_pointing_to_widget(KotoAddRemoveTrackPopover *self, GtkWidget *widget, GtkPositionType pos) { void koto_add_remove_track_popover_set_pointing_to_widget(
KotoAddRemoveTrackPopover * self,
GtkWidget * widget,
GtkPositionType pos
) {
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) { if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
return; return;
} }
@ -183,6 +216,7 @@ void koto_add_remove_track_popover_set_pointing_to_widget(KotoAddRemoveTrackPopo
GtkWidget* existing_parent = gtk_widget_get_parent(GTK_WIDGET(self)); GtkWidget* existing_parent = gtk_widget_get_parent(GTK_WIDGET(self));
if (existing_parent != NULL) { if (existing_parent != NULL) {
g_object_ref(GTK_WIDGET(self)); // Increment widget ref since unparent will do an unref g_object_ref(GTK_WIDGET(self)); // Increment widget ref since unparent will do an unref
gtk_widget_unparent(GTK_WIDGET(self)); // Unparent the popup gtk_widget_unparent(GTK_WIDGET(self)); // Unparent the popup
@ -192,30 +226,35 @@ void koto_add_remove_track_popover_set_pointing_to_widget(KotoAddRemoveTrackPopo
gtk_popover_set_position(GTK_POPOVER(self), pos); gtk_popover_set_position(GTK_POPOVER(self), pos);
} }
void koto_add_remove_track_popover_set_tracks(KotoAddRemoveTrackPopover *self, GList *tracks) { void koto_add_remove_track_popover_set_tracks(
KotoAddRemoveTrackPopover * self,
GList * tracks
) {
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) { if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
return; return;
} }
gint tracks_len = g_list_length(tracks); gint tracks_len = g_list_length(tracks);
if (tracks_len == 0) { // No tracks if (tracks_len == 0) { // No tracks
return; return;
} }
self->tracks = g_list_copy(tracks); self->tracks = g_list_copy(tracks);
GHashTable *playlists = koto_cartographer_get_playlists(koto_maps); // Get our playlists GHashTable * playlists = koto_cartographer_get_playlists(koto_maps); // Get our playlists
GHashTableIter playlists_iter; GHashTableIter playlists_iter;
gpointer uuid, playlist_ptr; gpointer uuid, playlist_ptr;
g_hash_table_iter_init(&playlists_iter, playlists); // Init our HashTable iterator g_hash_table_iter_init(&playlists_iter, playlists); // Init our HashTable iterator
while (g_hash_table_iter_next(&playlists_iter, &uuid, &playlist_ptr)) { // While we are iterating through our playlists while (g_hash_table_iter_next(&playlists_iter, &uuid, &playlist_ptr)) { // While we are iterating through our playlists
KotoPlaylist *playlist = playlist_ptr; KotoPlaylist * playlist = playlist_ptr;
gboolean should_be_checked = FALSE; gboolean should_be_checked = FALSE;
if (tracks_len > 1) { // More than one track if (tracks_len > 1) { // More than one track
GList *pos; GList * pos;
for (pos = self->tracks; pos != NULL; pos = pos->next) { // Iterate over our tracks for (pos = self->tracks; pos != NULL; pos = pos->next) { // Iterate over our tracks
should_be_checked = (koto_playlist_get_position_of_track(playlist, pos->data) != -1); should_be_checked = (koto_playlist_get_position_of_track(playlist, pos->data) != -1);
@ -224,7 +263,7 @@ void koto_add_remove_track_popover_set_tracks(KotoAddRemoveTrackPopover *self, G
} }
} }
} else { } else {
KotoIndexedTrack *track = g_list_nth_data(self->tracks, 0); // Get the first track KotoIndexedTrack * track = g_list_nth_data(self->tracks, 0); // Get the first track
if (KOTO_IS_INDEXED_TRACK(track)) { if (KOTO_IS_INDEXED_TRACK(track)) {
gint pos = koto_playlist_get_position_of_track(playlist, track); gint pos = koto_playlist_get_position_of_track(playlist, track);
@ -232,7 +271,7 @@ void koto_add_remove_track_popover_set_tracks(KotoAddRemoveTrackPopover *self, G
} }
} }
GtkCheckButton *playlist_check = g_hash_table_lookup(self->playlist_uuid_to_checkbox, uuid); // Get the GtkCheckButton for this playlist GtkCheckButton * playlist_check = g_hash_table_lookup(self->playlist_uuid_to_checkbox, uuid); // Get the GtkCheckButton for this playlist
if (GTK_IS_CHECK_BUTTON(playlist_check)) { // Is a checkbox if (GTK_IS_CHECK_BUTTON(playlist_check)) { // Is a checkbox
gpointer sig_id_ptr = g_hash_table_lookup(self->checkbox_to_signal_ids, playlist_check); gpointer sig_id_ptr = g_hash_table_lookup(self->checkbox_to_signal_ids, playlist_check);
@ -244,6 +283,6 @@ void koto_add_remove_track_popover_set_tracks(KotoAddRemoveTrackPopover *self, G
} }
} }
KotoAddRemoveTrackPopover* koto_add_remove_track_popover_new() { KotoAddRemoveTrackPopover * koto_add_remove_track_popover_new() {
return g_object_new(KOTO_TYPE_ADD_REMOVE_TRACK_POPOVER, NULL); return g_object_new(KOTO_TYPE_ADD_REMOVE_TRACK_POPOVER, NULL);
} }

View file

@ -25,7 +25,7 @@ G_BEGIN_DECLS
/** /**
* Type Definition * Type Definition
**/ **/
#define KOTO_TYPE_ADD_REMOVE_TRACK_POPOVER koto_add_remove_track_popover_get_type() #define KOTO_TYPE_ADD_REMOVE_TRACK_POPOVER koto_add_remove_track_popover_get_type()
G_DECLARE_FINAL_TYPE(KotoAddRemoveTrackPopover, koto_add_remove_track_popover, KOTO, ADD_REMOVE_TRACK_POPOVER, GtkPopover); G_DECLARE_FINAL_TYPE(KotoAddRemoveTrackPopover, koto_add_remove_track_popover, KOTO, ADD_REMOVE_TRACK_POPOVER, GtkPopover);
@ -33,16 +33,48 @@ G_DECLARE_FINAL_TYPE(KotoAddRemoveTrackPopover, koto_add_remove_track_popover, K
/** /**
* Functions * Functions
**/ **/
KotoAddRemoveTrackPopover* koto_add_remove_track_popover_new(); KotoAddRemoveTrackPopover * koto_add_remove_track_popover_new();
void koto_add_remove_track_popover_add_playlist(KotoAddRemoveTrackPopover *self, KotoPlaylist *playlist);
void koto_add_remove_track_popover_clear_tracks(KotoAddRemoveTrackPopover *self); void koto_add_remove_track_popover_add_playlist(
void koto_add_remove_track_popover_remove_playlist(KotoAddRemoveTrackPopover *self, gchar *playlist_uuid); KotoAddRemoveTrackPopover * self,
void koto_add_remove_track_popover_handle_checkbutton_toggle(GtkCheckButton *btn, gpointer user_data); KotoPlaylist * playlist
void koto_add_remove_track_popover_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playlist, gpointer user_data); );
void koto_add_remove_track_popover_handle_playlist_removed(KotoCartographer *carto, gchar *playlist_uuid, gpointer user_data);
void koto_add_remove_track_popover_set_pointing_to_widget(KotoAddRemoveTrackPopover *self, GtkWidget *widget, GtkPositionType pos); void koto_add_remove_track_popover_clear_tracks(KotoAddRemoveTrackPopover * self);
void koto_add_remove_track_popover_set_tracks(KotoAddRemoveTrackPopover *self, GList *tracks);
void koto_add_remove_track_popover_remove_playlist(
KotoAddRemoveTrackPopover * self,
gchar * playlist_uuid
);
void koto_add_remove_track_popover_handle_checkbutton_toggle(
GtkCheckButton * btn,
gpointer user_data
);
void koto_add_remove_track_popover_handle_playlist_added(
KotoCartographer * carto,
KotoPlaylist * playlist,
gpointer user_data
);
void koto_add_remove_track_popover_handle_playlist_removed(
KotoCartographer * carto,
gchar * playlist_uuid,
gpointer user_data
);
void koto_add_remove_track_popover_set_pointing_to_widget(
KotoAddRemoveTrackPopover * self,
GtkWidget * widget,
GtkPositionType pos
);
void koto_add_remove_track_popover_set_tracks(
KotoAddRemoveTrackPopover * self,
GList * tracks
);
G_END_DECLS G_END_DECLS

View file

@ -24,8 +24,8 @@
#include "../koto-window.h" #include "../koto-window.h"
#include "create-modify-dialog.h" #include "create-modify-dialog.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern KotoWindow *main_window; extern KotoWindow * main_window;
enum { enum {
PROP_DIALOG_0, PROP_DIALOG_0,
@ -33,29 +33,44 @@ enum {
N_PROPS N_PROPS
}; };
static GParamSpec *dialog_props[N_PROPS] = { NULL, }; static GParamSpec * dialog_props[N_PROPS] = {
NULL,
};
struct _KotoCreateModifyPlaylistDialog { struct _KotoCreateModifyPlaylistDialog {
GtkBox parent_instance; GtkBox parent_instance;
GtkWidget *playlist_image; GtkWidget * playlist_image;
GtkWidget *name_entry; GtkWidget * name_entry;
GtkWidget *create_button; GtkWidget * create_button;
gchar *playlist_image_path; gchar * playlist_image_path;
gchar *playlist_uuid; gchar * playlist_uuid;
}; };
G_DEFINE_TYPE(KotoCreateModifyPlaylistDialog, koto_create_modify_playlist_dialog, GTK_TYPE_BOX); G_DEFINE_TYPE(KotoCreateModifyPlaylistDialog, koto_create_modify_playlist_dialog, GTK_TYPE_BOX);
KotoCreateModifyPlaylistDialog *playlist_create_modify_dialog; KotoCreateModifyPlaylistDialog * playlist_create_modify_dialog;
static void koto_create_modify_playlist_dialog_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_create_modify_playlist_dialog_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_create_modify_playlist_dialog_class_init(KotoCreateModifyPlaylistDialogClass * c) {
GObjectClass * gobject_class;
static void koto_create_modify_playlist_dialog_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_create_modify_playlist_dialog_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_create_modify_playlist_dialog_class_init(KotoCreateModifyPlaylistDialogClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_create_modify_playlist_dialog_set_property; gobject_class->set_property = koto_create_modify_playlist_dialog_set_property;
gobject_class->get_property = koto_create_modify_playlist_dialog_get_property; gobject_class->get_property = koto_create_modify_playlist_dialog_get_property;
@ -65,13 +80,13 @@ static void koto_create_modify_playlist_dialog_class_init(KotoCreateModifyPlayli
"Playlist UUID", "Playlist UUID",
"Playlist UUID", "Playlist UUID",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPS, dialog_props); g_object_class_install_properties(gobject_class, N_PROPS, dialog_props);
} }
static void koto_create_modify_playlist_dialog_init(KotoCreateModifyPlaylistDialog *self) { static void koto_create_modify_playlist_dialog_init(KotoCreateModifyPlaylistDialog * self) {
self->playlist_image_path = NULL; self->playlist_image_path = NULL;
gtk_widget_set_halign(GTK_WIDGET(self), GTK_ALIGN_CENTER); gtk_widget_set_halign(GTK_WIDGET(self), GTK_ALIGN_CENTER);
@ -82,11 +97,15 @@ static void koto_create_modify_playlist_dialog_init(KotoCreateModifyPlaylistDial
gtk_widget_set_size_request(self->playlist_image, 220, 220); gtk_widget_set_size_request(self->playlist_image, 220, 220);
gtk_box_append(GTK_BOX(self), self->playlist_image); // Add our image gtk_box_append(GTK_BOX(self), self->playlist_image); // Add our image
GtkDropTarget *target = gtk_drop_target_new(G_TYPE_FILE, GDK_ACTION_COPY); GtkDropTarget * target = gtk_drop_target_new(G_TYPE_FILE, GDK_ACTION_COPY);
g_signal_connect(GTK_EVENT_CONTROLLER(target), "drop", G_CALLBACK(koto_create_modify_playlist_dialog_handle_drop), self); g_signal_connect(GTK_EVENT_CONTROLLER(target), "drop", G_CALLBACK(koto_create_modify_playlist_dialog_handle_drop), self);
gtk_widget_add_controller(self->playlist_image, GTK_EVENT_CONTROLLER(target)); gtk_widget_add_controller(self->playlist_image, GTK_EVENT_CONTROLLER(target));
GtkGesture *image_click_controller = gtk_gesture_click_new(); // Create a click gesture for the image clicking GtkGesture * image_click_controller = gtk_gesture_click_new(); // Create a click gesture for the image clicking
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(image_click_controller), 1); // Only allow left click gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(image_click_controller), 1); // Only allow left click
g_signal_connect(GTK_EVENT_CONTROLLER(image_click_controller), "pressed", G_CALLBACK(koto_create_modify_playlist_dialog_handle_image_click), self); g_signal_connect(GTK_EVENT_CONTROLLER(image_click_controller), "pressed", G_CALLBACK(koto_create_modify_playlist_dialog_handle_image_click), self);
@ -105,8 +124,14 @@ static void koto_create_modify_playlist_dialog_init(KotoCreateModifyPlaylistDial
gtk_box_append(GTK_BOX(self), self->create_button); // Add the create button gtk_box_append(GTK_BOX(self), self->create_button); // Add the create button
} }
static void koto_create_modify_playlist_dialog_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec){ static void koto_create_modify_playlist_dialog_get_property(
KotoCreateModifyPlaylistDialog *self = KOTO_CREATE_MODIFY_PLAYLIST_DIALOG(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoCreateModifyPlaylistDialog * self = KOTO_CREATE_MODIFY_PLAYLIST_DIALOG(obj);
switch (prop_id) { switch (prop_id) {
case PROP_PLAYLIST_UUID: case PROP_PLAYLIST_UUID:
@ -118,9 +143,17 @@ static void koto_create_modify_playlist_dialog_get_property(GObject *obj, guint
} }
} }
static void koto_create_modify_playlist_dialog_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_create_modify_playlist_dialog_set_property(
KotoCreateModifyPlaylistDialog *self = KOTO_CREATE_MODIFY_PLAYLIST_DIALOG(obj); GObject * obj,
(void) self; (void) val; guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoCreateModifyPlaylistDialog * self = KOTO_CREATE_MODIFY_PLAYLIST_DIALOG(obj);
(void) self;
(void) val;
switch (prop_id) { switch (prop_id) {
case PROP_PLAYLIST_UUID: case PROP_PLAYLIST_UUID:
@ -132,19 +165,26 @@ static void koto_create_modify_playlist_dialog_set_property(GObject *obj, guint
} }
} }
void koto_create_modify_playlist_dialog_handle_chooser_response(GtkNativeDialog *native, int response, gpointer user_data) { void koto_create_modify_playlist_dialog_handle_chooser_response(
GtkNativeDialog * native,
int response,
gpointer user_data
) {
if (response != GTK_RESPONSE_ACCEPT) { // Not accept if (response != GTK_RESPONSE_ACCEPT) { // Not accept
g_object_unref(native); g_object_unref(native);
return; return;
} }
KotoCreateModifyPlaylistDialog *self = user_data; KotoCreateModifyPlaylistDialog * self = user_data;
if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) { if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) {
return; return;
} }
GFile *file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(native)); GFile * file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(native));
gchar *file_path = g_file_get_path(file); // Get the absolute path gchar * file_path = g_file_get_path(file); // Get the absolute path
if (file_path != NULL) { if (file_path != NULL) {
self->playlist_image_path = g_strdup(file_path); self->playlist_image_path = g_strdup(file_path);
@ -156,10 +196,14 @@ void koto_create_modify_playlist_dialog_handle_chooser_response(GtkNativeDialog
g_object_unref(native); g_object_unref(native);
} }
void koto_create_modify_playlist_dialog_handle_create_click(GtkButton *button, gpointer user_data) { void koto_create_modify_playlist_dialog_handle_create_click(
GtkButton * button,
gpointer user_data
) {
(void) button; (void) button;
KotoCreateModifyPlaylistDialog *self = user_data; KotoCreateModifyPlaylistDialog * self = user_data;
if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) { if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) {
return; return;
@ -170,9 +214,10 @@ void koto_create_modify_playlist_dialog_handle_create_click(GtkButton *button, g
return; return;
} }
KotoPlaylist *playlist = NULL; KotoPlaylist * playlist = NULL;
gboolean modify_existing_playlist = koto_utils_is_string_valid(self->playlist_uuid); gboolean modify_existing_playlist = koto_utils_is_string_valid(self->playlist_uuid);
if (modify_existing_playlist) { // Modifying an existing playlist if (modify_existing_playlist) { // Modifying an existing playlist
playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->playlist_uuid); playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->playlist_uuid);
} else { // Creating a new playlist } else { // Creating a new playlist
@ -196,21 +241,32 @@ void koto_create_modify_playlist_dialog_handle_create_click(GtkButton *button, g
koto_window_hide_dialogs(main_window); // Hide the dialogs koto_window_hide_dialogs(main_window); // Hide the dialogs
} }
gboolean koto_create_modify_playlist_dialog_handle_drop(GtkDropTarget *target, const GValue *val, double x, double y, gpointer user_data) { gboolean koto_create_modify_playlist_dialog_handle_drop(
(void) target; (void) x; (void) y; GtkDropTarget * target,
const GValue * val,
double x,
double y,
gpointer user_data
) {
(void) target;
(void) x;
(void) y;
if (!G_VALUE_HOLDS(val, G_TYPE_FILE)) { // Not a file if (!G_VALUE_HOLDS(val, G_TYPE_FILE)) { // Not a file
return FALSE; return FALSE;
} }
KotoCreateModifyPlaylistDialog *self = user_data; KotoCreateModifyPlaylistDialog * self = user_data;
if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) { // No dialog if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) { // No dialog
return FALSE; return FALSE;
} }
GFile *dropped_file = g_value_get_object(val); // Get the GValue GFile * dropped_file = g_value_get_object(val); // Get the GValue
gchar *file_path = g_file_get_path(dropped_file); // Get the absolute path gchar * file_path = g_file_get_path(dropped_file); // Get the absolute path
g_object_unref(dropped_file); // Unref the file g_object_unref(dropped_file); // Unref the file
if (file_path == NULL) { if (file_path == NULL) {
@ -219,6 +275,7 @@ gboolean koto_create_modify_playlist_dialog_handle_drop(GtkDropTarget *target, c
magic_t magic_cookie = magic_open(MAGIC_MIME); magic_t magic_cookie = magic_open(MAGIC_MIME);
if (magic_cookie == NULL) { if (magic_cookie == NULL) {
return FALSE; return FALSE;
} }
@ -227,7 +284,8 @@ gboolean koto_create_modify_playlist_dialog_handle_drop(GtkDropTarget *target, c
goto cookie_closure; goto cookie_closure;
} }
const char *mime_type = magic_file(magic_cookie, file_path); const char * mime_type = magic_file(magic_cookie, file_path);
if ((mime_type != NULL) && g_str_has_prefix(mime_type, "image/")) { // Is an image if ((mime_type != NULL) && g_str_has_prefix(mime_type, "image/")) { // Is an image
self->playlist_image_path = g_strdup(file_path); self->playlist_image_path = g_strdup(file_path);
@ -241,17 +299,28 @@ cookie_closure:
return FALSE; return FALSE;
} }
void koto_create_modify_playlist_dialog_handle_image_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { void koto_create_modify_playlist_dialog_handle_image_click(
(void) gesture; (void) n_press; (void) x; (void) y; GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
) {
(void) gesture;
(void) n_press;
(void) x;
(void) y;
KotoCreateModifyPlaylistDialog *self = user_data; KotoCreateModifyPlaylistDialog * self = user_data;
GtkFileChooserNative* chooser = koto_utils_create_image_file_chooser("Choose playlist image"); GtkFileChooserNative* chooser = koto_utils_create_image_file_chooser("Choose playlist image");
g_signal_connect(chooser, "response", G_CALLBACK(koto_create_modify_playlist_dialog_handle_chooser_response), self); g_signal_connect(chooser, "response", G_CALLBACK(koto_create_modify_playlist_dialog_handle_chooser_response), self);
gtk_native_dialog_show(GTK_NATIVE_DIALOG(chooser)); // Show our file chooser gtk_native_dialog_show(GTK_NATIVE_DIALOG(chooser)); // Show our file chooser
} }
void koto_create_modify_playlist_dialog_reset(KotoCreateModifyPlaylistDialog *self) { void koto_create_modify_playlist_dialog_reset(KotoCreateModifyPlaylistDialog * self) {
if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) { if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) {
return; return;
} }
@ -262,12 +331,16 @@ void koto_create_modify_playlist_dialog_reset(KotoCreateModifyPlaylistDialog *se
gtk_button_set_label(GTK_BUTTON(self->create_button), "Create"); gtk_button_set_label(GTK_BUTTON(self->create_button), "Create");
} }
void koto_create_modify_playlist_dialog_set_playlist_uuid(KotoCreateModifyPlaylistDialog *self, gchar *playlist_uuid) { void koto_create_modify_playlist_dialog_set_playlist_uuid(
KotoCreateModifyPlaylistDialog * self,
gchar * playlist_uuid
) {
if (!koto_utils_is_string_valid(playlist_uuid)) { // Not a valid playlist UUID string if (!koto_utils_is_string_valid(playlist_uuid)) { // Not a valid playlist UUID string
return; return;
} }
KotoPlaylist *playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, playlist_uuid); KotoPlaylist * playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, playlist_uuid);
if (!KOTO_IS_PLAYLIST(playlist)) { if (!KOTO_IS_PLAYLIST(playlist)) {
return; return;
@ -277,7 +350,8 @@ void koto_create_modify_playlist_dialog_set_playlist_uuid(KotoCreateModifyPlayli
gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(self->name_entry)), koto_playlist_get_name(playlist), -1); // Update the input buffer gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(self->name_entry)), koto_playlist_get_name(playlist), -1); // Update the input buffer
gtk_entry_set_placeholder_text(GTK_ENTRY(self->name_entry), ""); // Clear placeholder gtk_entry_set_placeholder_text(GTK_ENTRY(self->name_entry), ""); // Clear placeholder
gchar *art = koto_playlist_get_artwork(playlist); gchar * art = koto_playlist_get_artwork(playlist);
if (!koto_utils_is_string_valid(art)) { // If art is not defined if (!koto_utils_is_string_valid(art)) { // If art is not defined
gtk_image_set_from_icon_name(GTK_IMAGE(self->playlist_image), "insert-image-symbolic"); // Reset the image gtk_image_set_from_icon_name(GTK_IMAGE(self->playlist_image), "insert-image-symbolic"); // Reset the image
@ -289,13 +363,8 @@ void koto_create_modify_playlist_dialog_set_playlist_uuid(KotoCreateModifyPlayli
gtk_button_set_label(GTK_BUTTON(self->create_button), "Save"); gtk_button_set_label(GTK_BUTTON(self->create_button), "Save");
} }
KotoCreateModifyPlaylistDialog* koto_create_modify_playlist_dialog_new(char *playlist_uuid) { KotoCreateModifyPlaylistDialog * koto_create_modify_playlist_dialog_new(char * playlist_uuid) {
(void) playlist_uuid; (void) playlist_uuid;
return g_object_new(KOTO_TYPE_CREATE_MODIFY_PLAYLIST_DIALOG, return g_object_new(KOTO_TYPE_CREATE_MODIFY_PLAYLIST_DIALOG, "orientation", GTK_ORIENTATION_VERTICAL, "spacing", 40, NULL);
"orientation",
GTK_ORIENTATION_VERTICAL,
"spacing",
40,
NULL);
} }

View file

@ -23,7 +23,7 @@ G_BEGIN_DECLS
/** /**
* Type Definition * Type Definition
**/ **/
#define KOTO_TYPE_CREATE_MODIFY_PLAYLIST_DIALOG koto_create_modify_playlist_dialog_get_type() #define KOTO_TYPE_CREATE_MODIFY_PLAYLIST_DIALOG koto_create_modify_playlist_dialog_get_type()
G_DECLARE_FINAL_TYPE(KotoCreateModifyPlaylistDialog, koto_create_modify_playlist_dialog, KOTO, CREATE_MODIFY_PLAYLIST_DIALOG, GtkBox); G_DECLARE_FINAL_TYPE(KotoCreateModifyPlaylistDialog, koto_create_modify_playlist_dialog, KOTO, CREATE_MODIFY_PLAYLIST_DIALOG, GtkBox);
@ -31,14 +31,42 @@ G_DECLARE_FINAL_TYPE(KotoCreateModifyPlaylistDialog, koto_create_modify_playlist
/** /**
* Functions * Functions
**/ **/
KotoCreateModifyPlaylistDialog* koto_create_modify_playlist_dialog_new(); KotoCreateModifyPlaylistDialog * koto_create_modify_playlist_dialog_new();
void koto_create_modify_playlist_dialog_handle_chooser_response(GtkNativeDialog *native, int response, gpointer user_data);
void koto_create_modify_playlist_dialog_handle_create_click(GtkButton *button, gpointer user_data); void koto_create_modify_playlist_dialog_handle_chooser_response(
gboolean koto_create_modify_playlist_dialog_handle_drop(GtkDropTarget *target, const GValue *val, double x, double y, gpointer user_data); GtkNativeDialog * native,
void koto_create_modify_playlist_dialog_handle_image_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data); int response,
void koto_create_modify_playlist_dialog_reset(KotoCreateModifyPlaylistDialog *self); gpointer user_data
void koto_create_modify_playlist_dialog_set_playlist_uuid(KotoCreateModifyPlaylistDialog *self, gchar *playlist_uuid); );
void koto_create_modify_playlist_dialog_handle_create_click(
GtkButton * button,
gpointer user_data
);
gboolean koto_create_modify_playlist_dialog_handle_drop(
GtkDropTarget * target,
const GValue * val,
double x,
double y,
gpointer user_data
);
void koto_create_modify_playlist_dialog_handle_image_click(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer user_data
);
void koto_create_modify_playlist_dialog_reset(KotoCreateModifyPlaylistDialog * self);
void koto_create_modify_playlist_dialog_set_playlist_uuid(
KotoCreateModifyPlaylistDialog * self,
gchar * playlist_uuid
);
G_END_DECLS G_END_DECLS

View file

@ -24,22 +24,37 @@ enum {
N_PROPERTIES N_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec * props[N_PROPERTIES] = {
NULL,
};
KotoCurrentPlaylist *current_playlist = NULL; KotoCurrentPlaylist * current_playlist = NULL;
struct _KotoCurrentPlaylist { struct _KotoCurrentPlaylist {
GObject parent_class; GObject parent_class;
KotoPlaylist *current_playlist; KotoPlaylist * current_playlist;
}; };
G_DEFINE_TYPE(KotoCurrentPlaylist, koto_current_playlist, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoCurrentPlaylist, koto_current_playlist, G_TYPE_OBJECT);
static void koto_current_playlist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); static void koto_current_playlist_get_property(
static void koto_current_playlist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
static void koto_current_playlist_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_current_playlist_class_init(KotoCurrentPlaylistClass * c) {
GObjectClass * gobject_class;
static void koto_current_playlist_class_init(KotoCurrentPlaylistClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_current_playlist_set_property; gobject_class->set_property = koto_current_playlist_set_property;
gobject_class->get_property = koto_current_playlist_get_property; gobject_class->get_property = koto_current_playlist_get_property;
@ -49,18 +64,24 @@ static void koto_current_playlist_class_init(KotoCurrentPlaylistClass *c) {
"Current Playlist", "Current Playlist",
"Current Playlist", "Current Playlist",
KOTO_TYPE_PLAYLIST, KOTO_TYPE_PLAYLIST,
G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_current_playlist_init(KotoCurrentPlaylist *self) { static void koto_current_playlist_init(KotoCurrentPlaylist * self) {
self->current_playlist = NULL; self->current_playlist = NULL;
} }
void koto_current_playlist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { void koto_current_playlist_get_property(
KotoCurrentPlaylist *self = KOTO_CURRENT_PLAYLIST(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoCurrentPlaylist * self = KOTO_CURRENT_PLAYLIST(obj);
switch (prop_id) { switch (prop_id) {
case PROP_CURRENT_PLAYLIST: case PROP_CURRENT_PLAYLIST:
@ -72,8 +93,14 @@ void koto_current_playlist_get_property(GObject *obj, guint prop_id, GValue *val
} }
} }
void koto_current_playlist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { void koto_current_playlist_set_property(
KotoCurrentPlaylist *self = KOTO_CURRENT_PLAYLIST(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoCurrentPlaylist * self = KOTO_CURRENT_PLAYLIST(obj);
switch (prop_id) { switch (prop_id) {
case PROP_CURRENT_PLAYLIST: case PROP_CURRENT_PLAYLIST:
@ -85,11 +112,14 @@ void koto_current_playlist_set_property(GObject *obj, guint prop_id, const GValu
} }
} }
KotoPlaylist* koto_current_playlist_get_playlist(KotoCurrentPlaylist *self) { KotoPlaylist * koto_current_playlist_get_playlist(KotoCurrentPlaylist * self) {
return self->current_playlist; return self->current_playlist;
} }
void koto_current_playlist_set_playlist(KotoCurrentPlaylist *self, KotoPlaylist *playlist) { void koto_current_playlist_set_playlist(
KotoCurrentPlaylist * self,
KotoPlaylist * playlist
) {
if (!KOTO_IS_CURRENT_PLAYLIST(self)) { if (!KOTO_IS_CURRENT_PLAYLIST(self)) {
return; return;
} }
@ -99,7 +129,7 @@ void koto_current_playlist_set_playlist(KotoCurrentPlaylist *self, KotoPlaylist
} }
if (self->current_playlist != NULL && KOTO_IS_PLAYLIST(self->current_playlist)) { if (self->current_playlist != NULL && KOTO_IS_PLAYLIST(self->current_playlist)) {
gboolean *is_temp = FALSE; gboolean * is_temp = FALSE;
g_object_get(self->current_playlist, "ephemeral", &is_temp, NULL); // Get the current ephemeral value g_object_get(self->current_playlist, "ephemeral", &is_temp, NULL); // Get the current ephemeral value
if (is_temp) { // Is a temporary playlist if (is_temp) { // Is a temporary playlist
@ -118,6 +148,6 @@ void koto_current_playlist_set_playlist(KotoCurrentPlaylist *self, KotoPlaylist
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CURRENT_PLAYLIST]); g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CURRENT_PLAYLIST]);
} }
KotoCurrentPlaylist* koto_current_playlist_new() { KotoCurrentPlaylist * koto_current_playlist_new() {
return g_object_new(KOTO_TYPE_CURRENT_PLAYLIST, NULL); return g_object_new(KOTO_TYPE_CURRENT_PLAYLIST, NULL);
} }

View file

@ -23,7 +23,7 @@ G_BEGIN_DECLS
/** /**
* Type Definition * Type Definition
**/ **/
#define KOTO_TYPE_CURRENT_PLAYLIST koto_current_playlist_get_type() #define KOTO_TYPE_CURRENT_PLAYLIST koto_current_playlist_get_type()
G_DECLARE_FINAL_TYPE(KotoCurrentPlaylist, koto_current_playlist, KOTO, CURRENT_PLAYLIST, GObject); G_DECLARE_FINAL_TYPE(KotoCurrentPlaylist, koto_current_playlist, KOTO, CURRENT_PLAYLIST, GObject);
@ -31,10 +31,15 @@ G_DECLARE_FINAL_TYPE(KotoCurrentPlaylist, koto_current_playlist, KOTO, CURRENT_P
/** /**
* Current Playlist Functions * Current Playlist Functions
**/ **/
KotoCurrentPlaylist* koto_current_playlist_new(); KotoCurrentPlaylist * koto_current_playlist_new();
KotoPlaylist* koto_current_playlist_get_playlist(KotoCurrentPlaylist *self);
void koto_current_playlist_set_playlist(KotoCurrentPlaylist *self, KotoPlaylist *playlist); KotoPlaylist * koto_current_playlist_get_playlist(KotoCurrentPlaylist * self);
void koto_current_playlist_set_playlist(
KotoCurrentPlaylist * self,
KotoPlaylist * playlist
);
G_END_DECLS G_END_DECLS

View file

@ -23,8 +23,8 @@
#include "../koto-utils.h" #include "../koto-utils.h"
#include "playlist.h" #include "playlist.h"
extern KotoCartographer *koto_maps; extern KotoCartographer * koto_maps;
extern sqlite3 *koto_db; extern sqlite3 * koto_db;
enum { enum {
PROP_0, PROP_0,
@ -46,11 +46,11 @@ enum {
struct _KotoPlaylist { struct _KotoPlaylist {
GObject parent_instance; GObject parent_instance;
gchar *uuid; gchar * uuid;
gchar *name; gchar * name;
gchar *art_path; gchar * art_path;
gint current_position; gint current_position;
gchar *current_uuid; gchar * current_uuid;
KotoPreferredModelType model; KotoPreferredModelType model;
@ -58,32 +58,56 @@ struct _KotoPlaylist {
gboolean is_shuffle_enabled; gboolean is_shuffle_enabled;
gboolean finalized; gboolean finalized;
GListStore *store; GListStore * store;
GQueue *sorted_tracks; GQueue * sorted_tracks;
GQueue *tracks; // This is effectively our vanilla value that should never change GQueue * tracks; // This is effectively our vanilla value that should never change
GQueue *played_tracks; GQueue * played_tracks;
}; };
struct _KotoPlaylistClass { struct _KotoPlaylistClass {
GObjectClass parent_class; GObjectClass parent_class;
void (* modified) (KotoPlaylist *playlist); void (* modified) (KotoPlaylist * playlist);
void (* track_added) (KotoPlaylist *playlist, gchar *track_uuid); void (* track_added) (
void (* track_load_finalized) (KotoPlaylist *playlist); KotoPlaylist * playlist,
void (* track_removed) (KotoPlaylist *playlist, gchar *track_uuid); gchar * track_uuid
);
void (* track_load_finalized) (KotoPlaylist * playlist);
void (* track_removed) (
KotoPlaylist * playlist,
gchar * track_uuid
);
}; };
G_DEFINE_TYPE(KotoPlaylist, koto_playlist, G_TYPE_OBJECT); G_DEFINE_TYPE(KotoPlaylist, koto_playlist, G_TYPE_OBJECT);
static GParamSpec *props[N_PROPERTIES] = { NULL }; static GParamSpec * props[N_PROPERTIES] = {
static guint playlist_signals[N_SIGNALS] = { 0 }; NULL
};
static guint playlist_signals[N_SIGNALS] = {
0
};
static void koto_playlist_get_property(
GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
);
;
static void koto_playlist_set_property(
GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
);
static void koto_playlist_class_init(KotoPlaylistClass * c) {
GObjectClass * gobject_class;
static void koto_playlist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);;
static void koto_playlist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_playlist_class_init(KotoPlaylistClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c); gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_playlist_set_property; gobject_class->set_property = koto_playlist_set_property;
gobject_class->get_property = koto_playlist_get_property; gobject_class->get_property = koto_playlist_get_property;
@ -93,7 +117,7 @@ static void koto_playlist_class_init(KotoPlaylistClass *c) {
"UUID of the Playlist", "UUID of the Playlist",
"UUID of the Playlist", "UUID of the Playlist",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_NAME] = g_param_spec_string( props[PROP_NAME] = g_param_spec_string(
@ -101,7 +125,7 @@ static void koto_playlist_class_init(KotoPlaylistClass *c) {
"Name of the Playlist", "Name of the Playlist",
"Name of the Playlist", "Name of the Playlist",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_ART_PATH] = g_param_spec_string( props[PROP_ART_PATH] = g_param_spec_string(
@ -109,7 +133,7 @@ static void koto_playlist_class_init(KotoPlaylistClass *c) {
"Path to any associated artwork of the Playlist", "Path to any associated artwork of the Playlist",
"Path to any associated artwork of the Playlist", "Path to any associated artwork of the Playlist",
NULL, NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_EPHEMERAL] = g_param_spec_boolean( props[PROP_EPHEMERAL] = g_param_spec_boolean(
@ -117,7 +141,7 @@ static void koto_playlist_class_init(KotoPlaylistClass *c) {
"Is the playlist ephemeral (temporary)", "Is the playlist ephemeral (temporary)",
"Is the playlist ephemeral (temporary)", "Is the playlist ephemeral (temporary)",
FALSE, FALSE,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
props[PROP_IS_SHUFFLE_ENABLED] = g_param_spec_boolean( props[PROP_IS_SHUFFLE_ENABLED] = g_param_spec_boolean(
@ -125,7 +149,7 @@ static void koto_playlist_class_init(KotoPlaylistClass *c) {
"Is shuffling enabled", "Is shuffling enabled",
"Is shuffling enabled", "Is shuffling enabled",
FALSE, FALSE,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
@ -181,8 +205,14 @@ static void koto_playlist_class_init(KotoPlaylistClass *c) {
); );
} }
static void koto_playlist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_playlist_get_property(
KotoPlaylist *self = KOTO_PLAYLIST(obj); GObject * obj,
guint prop_id,
GValue * val,
GParamSpec * spec
) {
KotoPlaylist * self = KOTO_PLAYLIST(obj);
switch (prop_id) { switch (prop_id) {
case PROP_UUID: case PROP_UUID:
@ -206,8 +236,14 @@ static void koto_playlist_get_property(GObject *obj, guint prop_id, GValue *val,
} }
} }
static void koto_playlist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { static void koto_playlist_set_property(
KotoPlaylist *self = KOTO_PLAYLIST(obj); GObject * obj,
guint prop_id,
const GValue * val,
GParamSpec * spec
) {
KotoPlaylist * self = KOTO_PLAYLIST(obj);
switch (prop_id) { switch (prop_id) {
case PROP_UUID: case PROP_UUID:
@ -231,7 +267,7 @@ static void koto_playlist_set_property(GObject *obj, guint prop_id, const GValue
} }
} }
static void koto_playlist_init(KotoPlaylist *self) { static void koto_playlist_init(KotoPlaylist * self) {
self->current_position = -1; // Default to -1 so first time incrementing puts it at 0 self->current_position = -1; // Default to -1 so first time incrementing puts it at 0
self->current_uuid = NULL; self->current_uuid = NULL;
self->model = KOTO_PREFERRED_MODEL_TYPE_DEFAULT; // Default to default model self->model = KOTO_PREFERRED_MODEL_TYPE_DEFAULT; // Default to default model
@ -245,7 +281,10 @@ static void koto_playlist_init(KotoPlaylist *self) {
self->store = g_list_store_new(KOTO_TYPE_INDEXED_TRACK); self->store = g_list_store_new(KOTO_TYPE_INDEXED_TRACK);
} }
void koto_playlist_add_to_played_tracks(KotoPlaylist *self, gchar *uuid) { void koto_playlist_add_to_played_tracks(
KotoPlaylist * self,
gchar * uuid
) {
if (g_queue_index(self->played_tracks, uuid) != -1) { // Already added if (g_queue_index(self->played_tracks, uuid) != -1) { // Already added
return; return;
} }
@ -253,18 +292,31 @@ void koto_playlist_add_to_played_tracks(KotoPlaylist *self, gchar *uuid) {
g_queue_push_tail(self->played_tracks, uuid); // Add to end g_queue_push_tail(self->played_tracks, uuid); // Add to end
} }
void koto_playlist_add_track(KotoPlaylist *self, KotoIndexedTrack *track, gboolean current, gboolean commit_to_table) { void koto_playlist_add_track(
KotoPlaylist * self,
KotoIndexedTrack * track,
gboolean current,
gboolean commit_to_table
) {
koto_playlist_add_track_by_uuid(self, koto_indexed_track_get_uuid(track), current, commit_to_table); koto_playlist_add_track_by_uuid(self, koto_indexed_track_get_uuid(track), current, commit_to_table);
} }
void koto_playlist_add_track_by_uuid(KotoPlaylist *self, gchar *uuid, gboolean current, gboolean commit_to_table) { void koto_playlist_add_track_by_uuid(
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, uuid); // Get the track KotoPlaylist * self,
gchar * uuid,
gboolean current,
gboolean commit_to_table
) {
KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, uuid); // Get the track
if (!KOTO_IS_INDEXED_TRACK(track)) { if (!KOTO_IS_INDEXED_TRACK(track)) {
return; return;
} }
GList *found_tracks_uuids = g_queue_find_custom(self->tracks, uuid, koto_playlist_compare_track_uuids); GList * found_tracks_uuids = g_queue_find_custom(self->tracks, uuid, koto_playlist_compare_track_uuids);
if (found_tracks_uuids != NULL) { // Is somewhere in the tracks already if (found_tracks_uuids != NULL) { // Is somewhere in the tracks already
g_list_free(found_tracks_uuids); g_list_free(found_tracks_uuids);
return; return;
@ -296,8 +348,13 @@ void koto_playlist_add_track_by_uuid(KotoPlaylist *self, gchar *uuid, gboolean c
); );
} }
void koto_playlist_apply_model(KotoPlaylist *self, KotoPreferredModelType preferred_model) { void koto_playlist_apply_model(
GList *sort_user_data = NULL; KotoPlaylist * self,
KotoPreferredModelType preferred_model
) {
GList * sort_user_data = NULL;
sort_user_data = g_list_prepend(sort_user_data, GUINT_TO_POINTER(preferred_model)); // Prepend our preferred model first sort_user_data = g_list_prepend(sort_user_data, GUINT_TO_POINTER(preferred_model)); // Prepend our preferred model first
sort_user_data = g_list_prepend(sort_user_data, self); // Prepend ourself sort_user_data = g_list_prepend(sort_user_data, self); // Prepend ourself
@ -311,12 +368,12 @@ void koto_playlist_apply_model(KotoPlaylist *self, KotoPreferredModelType prefer
}*/ }*/
} }
void koto_playlist_commit(KotoPlaylist *self) { void koto_playlist_commit(KotoPlaylist * self) {
if (self->ephemeral) { // Temporary playlist if (self->ephemeral) { // Temporary playlist
return; return;
} }
gchar *commit_op = g_strdup_printf( gchar * commit_op = g_strdup_printf(
"INSERT INTO playlist_meta(id, name, art_path, preferred_model)" "INSERT INTO playlist_meta(id, name, art_path, preferred_model)"
"VALUES('%s', quote(\"%s\"), quote(\"%s\"), 0)" "VALUES('%s', quote(\"%s\"), quote(\"%s\"), 0)"
"ON CONFLICT(id) DO UPDATE SET name=excluded.name, art_path=excluded.art_path;", "ON CONFLICT(id) DO UPDATE SET name=excluded.name, art_path=excluded.art_path;",
@ -325,9 +382,10 @@ void koto_playlist_commit(KotoPlaylist *self) {
self->art_path self->art_path
); );
gchar *commit_op_errmsg = NULL; gchar * commit_op_errmsg = NULL;
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg); int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_warning("Failed to save playlist: %s", commit_op_errmsg); g_warning("Failed to save playlist: %s", commit_op_errmsg);
} else { // Successfully saved our playlist } else { // Successfully saved our playlist
@ -338,49 +396,59 @@ void koto_playlist_commit(KotoPlaylist *self) {
g_free(commit_op_errmsg); g_free(commit_op_errmsg);
} }
void koto_playlist_commit_tracks(gpointer data, gpointer user_data) { void koto_playlist_commit_tracks(
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, data); // Get the track gpointer data,
gpointer user_data
) {
KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, data); // Get the track
if (track == NULL) { // Not a track if (track == NULL) { // Not a track
KotoPlaylist *self = user_data; KotoPlaylist * self = user_data;
gchar *playlist_uuid = self->uuid; // Get the playlist UUID gchar * playlist_uuid = self->uuid; // Get the playlist UUID
gchar *current_track = g_queue_peek_nth(self->tracks, self->current_position); // Get the UUID of the current track gchar * current_track = g_queue_peek_nth(self->tracks, self->current_position); // Get the UUID of the current track
//koto_indexed_track_save_to_playlist(track, playlist_uuid, (data == current_track) ? 1 : 0); // Call to save the playlist to the track //koto_indexed_track_save_to_playlist(track, playlist_uuid, (data == current_track) ? 1 : 0); // Call to save the playlist to the track
g_free(playlist_uuid); g_free(playlist_uuid);
g_free(current_track); g_free(current_track);
} }
} }
gint koto_playlist_compare_track_uuids(gconstpointer a, gconstpointer b) { gint koto_playlist_compare_track_uuids(
gconstpointer a,
gconstpointer b
) {
return g_strcmp0(a, b); return g_strcmp0(a, b);
} }
gchar* koto_playlist_get_artwork(KotoPlaylist *self) { gchar * koto_playlist_get_artwork(KotoPlaylist * self) {
return (self->art_path == NULL) ? NULL : g_strdup(self->art_path); // Return a duplicate of our art path return (self->art_path == NULL) ? NULL : g_strdup(self->art_path); // Return a duplicate of our art path
} }
KotoPreferredModelType koto_playlist_get_current_model(KotoPlaylist *self) { KotoPreferredModelType koto_playlist_get_current_model(KotoPlaylist * self) {
return self->model; return self->model;
} }
guint koto_playlist_get_current_position(KotoPlaylist *self) { guint koto_playlist_get_current_position(KotoPlaylist * self) {
return self->current_position; return self->current_position;
} }
gboolean koto_playlist_get_is_finalized(KotoPlaylist *self) { gboolean koto_playlist_get_is_finalized(KotoPlaylist * self) {
return self->finalized; return self->finalized;
} }
guint koto_playlist_get_length(KotoPlaylist *self) { guint koto_playlist_get_length(KotoPlaylist * self) {
return g_queue_get_length(self->tracks); // Get the length of the tracks return g_queue_get_length(self->tracks); // Get the length of the tracks
} }
gchar* koto_playlist_get_name(KotoPlaylist *self) { gchar * koto_playlist_get_name(KotoPlaylist * self) {
return (self->name == NULL) ? NULL : g_strdup(self->name); return (self->name == NULL) ? NULL : g_strdup(self->name);
} }
gint koto_playlist_get_position_of_track(KotoPlaylist *self, KotoIndexedTrack *track) { gint koto_playlist_get_position_of_track(
KotoPlaylist * self,
KotoIndexedTrack * track
) {
if (!KOTO_IS_PLAYLIST(self)) { if (!KOTO_IS_PLAYLIST(self)) {
return -1; return -1;
} }
@ -396,17 +464,19 @@ gint koto_playlist_get_position_of_track(KotoPlaylist *self, KotoIndexedTrack *t
gint position = -1; gint position = -1;
guint found_pos = 0; guint found_pos = 0;
if (g_list_store_find(self->store , track, &found_pos)) { // Found the item
if (g_list_store_find(self->store, track, &found_pos)) { // Found the item
position = (gint) found_pos; // Cast our found position from guint to gint position = (gint) found_pos; // Cast our found position from guint to gint
} }
return position; return position;
} }
gchar* koto_playlist_get_random_track(KotoPlaylist *self) { gchar * koto_playlist_get_random_track(KotoPlaylist * self) {
gchar *track_uuid = NULL; gchar * track_uuid = NULL;
guint tracks_len = g_queue_get_length(self->sorted_tracks); guint tracks_len = g_queue_get_length(self->sorted_tracks);
if (tracks_len == g_queue_get_length(self->played_tracks)) { // Played all tracks if (tracks_len == g_queue_get_length(self->played_tracks)) { // Played all tracks
track_uuid = g_list_nth_data(self->sorted_tracks->head, 0); // Get the first track_uuid = g_list_nth_data(self->sorted_tracks->head, 0); // Get the first
g_queue_clear(self->played_tracks); // Clear our played tracks g_queue_clear(self->played_tracks); // Clear our played tracks
@ -417,7 +487,7 @@ gchar* koto_playlist_get_random_track(KotoPlaylist *self) {
while (track_uuid == NULL) { // Haven't selected a track yet while (track_uuid == NULL) { // Haven't selected a track yet
attempt++; attempt++;
gint32 selected_item = g_rand_int_range(rando_calrissian, 0, (gint32) tracks_len); gint32 selected_item = g_rand_int_range(rando_calrissian, 0, (gint32) tracks_len);
gchar *selected_track = g_queue_peek_nth(self->sorted_tracks, (guint) selected_item); // Get the UUID of the selected item gchar * selected_track = g_queue_peek_nth(self->sorted_tracks, (guint) selected_item); // Get the UUID of the selected item
if (g_queue_index(self->played_tracks, selected_track) == -1) { // Haven't played the track if (g_queue_index(self->played_tracks, selected_track) == -1) { // Haven't played the track
self->current_position = (gint) selected_item; self->current_position = (gint) selected_item;
@ -436,25 +506,25 @@ gchar* koto_playlist_get_random_track(KotoPlaylist *self) {
return track_uuid; return track_uuid;
} }
GListStore* koto_playlist_get_store(KotoPlaylist *self) { GListStore * koto_playlist_get_store(KotoPlaylist * self) {
return self->store; return self->store;
} }
GQueue* koto_playlist_get_tracks(KotoPlaylist *self) { GQueue * koto_playlist_get_tracks(KotoPlaylist * self) {
return self->tracks; return self->tracks;
} }
gchar* koto_playlist_get_uuid(KotoPlaylist *self) { gchar * koto_playlist_get_uuid(KotoPlaylist * self) {
return g_strdup(self->uuid); return g_strdup(self->uuid);
} }
gchar* koto_playlist_go_to_next(KotoPlaylist *self) { gchar * koto_playlist_go_to_next(KotoPlaylist * self) {
if (!KOTO_IS_PLAYLIST(self)) { if (!KOTO_IS_PLAYLIST(self)) {
return NULL; return NULL;
} }
if (self->is_shuffle_enabled) { // Shuffling enabled if (self->is_shuffle_enabled) { // Shuffling enabled
gchar *random_track_uuid = koto_playlist_get_random_track(self); // Get a random track gchar * random_track_uuid = koto_playlist_get_random_track(self); // Get a random track
koto_playlist_add_to_played_tracks(self, random_track_uuid); koto_playlist_add_to_played_tracks(self, random_track_uuid);
return random_track_uuid; return random_track_uuid;
} }
@ -462,7 +532,7 @@ gchar* koto_playlist_go_to_next(KotoPlaylist *self) {
if (!koto_utils_is_string_valid(self->current_uuid)) { // No valid UUID yet if (!koto_utils_is_string_valid(self->current_uuid)) { // No valid UUID yet
self->current_position++; self->current_position++;
} else { // Have a UUID currently } else { // Have a UUID currently
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, self->current_uuid); KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, self->current_uuid);
if (!KOTO_IS_INDEXED_TRACK(track)) { if (!KOTO_IS_INDEXED_TRACK(track)) {
return NULL; return NULL;
@ -474,7 +544,7 @@ gchar* koto_playlist_go_to_next(KotoPlaylist *self) {
return NULL; return NULL;
} }
self->current_position = pos_of_song+1; // Increment our position based on position of song self->current_position = pos_of_song + 1; // Increment our position based on position of song
} }
self->current_uuid = g_queue_peek_nth(self->sorted_tracks, self->current_position); self->current_uuid = g_queue_peek_nth(self->sorted_tracks, self->current_position);
@ -483,7 +553,7 @@ gchar* koto_playlist_go_to_next(KotoPlaylist *self) {
return self->current_uuid; return self->current_uuid;
} }
gchar* koto_playlist_go_to_previous(KotoPlaylist *self) { gchar * koto_playlist_go_to_previous(KotoPlaylist * self) {
if (self->is_shuffle_enabled) { // Shuffling enabled if (self->is_shuffle_enabled) { // Shuffling enabled
return koto_playlist_get_random_track(self); // Get a random track return koto_playlist_get_random_track(self); // Get a random track
} }
@ -492,7 +562,8 @@ gchar* koto_playlist_go_to_previous(KotoPlaylist *self) {
return NULL; return NULL;
} }
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, self->current_uuid); KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, self->current_uuid);
if (!KOTO_IS_INDEXED_TRACK(track)) { if (!KOTO_IS_INDEXED_TRACK(track)) {
return NULL; return NULL;
@ -500,6 +571,7 @@ gchar* koto_playlist_go_to_previous(KotoPlaylist *self) {
gint pos_of_song = koto_playlist_get_position_of_track(self, track); // Get the position of the current track based on the current model gint pos_of_song = koto_playlist_get_position_of_track(self, track); // Get the position of the current track based on the current model
if (pos_of_song == 0) { if (pos_of_song == 0) {
return NULL; return NULL;
} }
@ -510,7 +582,7 @@ gchar* koto_playlist_go_to_previous(KotoPlaylist *self) {
return self->current_uuid; return self->current_uuid;
} }
void koto_playlist_mark_as_finalized(KotoPlaylist *self) { void koto_playlist_mark_as_finalized(KotoPlaylist * self) {
if (self->finalized) { // Already finalized if (self->finalized) { // Already finalized
return; return;
} }
@ -525,21 +597,31 @@ void koto_playlist_mark_as_finalized(KotoPlaylist *self) {
); );
} }
gint koto_playlist_model_sort_by_uuid(gconstpointer first_item, gconstpointer second_item, gpointer data_list) { gint koto_playlist_model_sort_by_uuid(
KotoIndexedTrack *first_track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) first_item); gconstpointer first_item,
KotoIndexedTrack *second_track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) second_item); gconstpointer second_item,
gpointer data_list
) {
KotoIndexedTrack * first_track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) first_item);
KotoIndexedTrack * second_track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) second_item);
return koto_playlist_model_sort_by_track(first_track, second_track, data_list); return koto_playlist_model_sort_by_track(first_track, second_track, data_list);
} }
gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer second_item, gpointer data_list) { gint koto_playlist_model_sort_by_track(
KotoIndexedTrack *first_track = (KotoIndexedTrack*) first_item; gconstpointer first_item,
KotoIndexedTrack *second_track = (KotoIndexedTrack*) second_item; gconstpointer second_item,
gpointer data_list
) {
KotoIndexedTrack * first_track = (KotoIndexedTrack*) first_item;
KotoIndexedTrack * second_track = (KotoIndexedTrack*) second_item;
GList* ptr_list = data_list; GList* ptr_list = data_list;
KotoPlaylist *self = g_list_nth_data(ptr_list, 0); // First item in the GPtrArray is a pointer to our playlist KotoPlaylist * self = g_list_nth_data(ptr_list, 0); // First item in the GPtrArray is a pointer to our playlist
KotoPreferredModelType model = GPOINTER_TO_UINT(g_list_nth_data(ptr_list, 1)); // Second item in the GPtrArray is a pointer to our KotoPreferredModelType KotoPreferredModelType model = GPOINTER_TO_UINT(g_list_nth_data(ptr_list, 1)); // Second item in the GPtrArray is a pointer to our KotoPreferredModelType
if ( if (
(model == KOTO_PREFERRED_MODEL_TYPE_DEFAULT) || // Newest first model (model == KOTO_PREFERRED_MODEL_TYPE_DEFAULT) || // Newest first model
(model == KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST) // Oldest first (model == KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST) // Oldest first
@ -563,8 +645,8 @@ gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer s
} }
if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM) { // Sort by album name if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM) { // Sort by album name
gchar *first_album_uuid = NULL; gchar * first_album_uuid = NULL;
gchar *second_album_uuid = NULL; gchar * second_album_uuid = NULL;
g_object_get( g_object_get(
first_track, first_track,
@ -586,8 +668,8 @@ gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer s
return 0; // Don't get too granular, just consider them equal return 0; // Don't get too granular, just consider them equal
} }
KotoIndexedAlbum *first_album = koto_cartographer_get_album_by_uuid(koto_maps, first_album_uuid); KotoIndexedAlbum * first_album = koto_cartographer_get_album_by_uuid(koto_maps, first_album_uuid);
KotoIndexedAlbum *second_album = koto_cartographer_get_album_by_uuid(koto_maps, second_album_uuid); KotoIndexedAlbum * second_album = koto_cartographer_get_album_by_uuid(koto_maps, second_album_uuid);
g_free(first_album_uuid); g_free(first_album_uuid);
g_free(second_album_uuid); g_free(second_album_uuid);
@ -600,8 +682,8 @@ gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer s
} }
if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST) { // Sort by artist name if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST) { // Sort by artist name
gchar *first_artist_uuid = NULL; gchar * first_artist_uuid = NULL;
gchar *second_artist_uuid = NULL; gchar * second_artist_uuid = NULL;
g_object_get( g_object_get(
first_track, first_track,
@ -617,8 +699,8 @@ gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer s
NULL NULL
); );
KotoIndexedArtist *first_artist = koto_cartographer_get_artist_by_uuid(koto_maps, first_artist_uuid); KotoIndexedArtist * first_artist = koto_cartographer_get_artist_by_uuid(koto_maps, first_artist_uuid);
KotoIndexedArtist *second_artist = koto_cartographer_get_artist_by_uuid(koto_maps, second_artist_uuid); KotoIndexedArtist * second_artist = koto_cartographer_get_artist_by_uuid(koto_maps, second_artist_uuid);
g_free(first_artist_uuid); g_free(first_artist_uuid);
g_free(second_artist_uuid); g_free(second_artist_uuid);
@ -631,8 +713,8 @@ gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer s
} }
if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME) { // Track name if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME) { // Track name
gchar *first_track_name = NULL; gchar * first_track_name = NULL;
gchar *second_track_name = NULL; gchar * second_track_name = NULL;
g_object_get( g_object_get(
first_track, first_track,
@ -658,28 +740,37 @@ gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer s
return 0; return 0;
} }
void koto_playlist_remove_from_played_tracks(KotoPlaylist *self, gchar *uuid) { void koto_playlist_remove_from_played_tracks(
KotoPlaylist * self,
gchar * uuid
) {
g_queue_remove(self->played_tracks, uuid); g_queue_remove(self->played_tracks, uuid);
} }
void koto_playlist_remove_track_by_uuid(KotoPlaylist *self, gchar *uuid) { void koto_playlist_remove_track_by_uuid(
KotoPlaylist * self,
gchar * uuid
) {
if (!KOTO_IS_PLAYLIST(self)) { if (!KOTO_IS_PLAYLIST(self)) {
return; return;
} }
gint file_index = g_queue_index(self->tracks, uuid); // Get the position of this uuid gint file_index = g_queue_index(self->tracks, uuid); // Get the position of this uuid
if (file_index != -1) { // Have in tracks if (file_index != -1) { // Have in tracks
g_queue_pop_nth(self->tracks, file_index); // Remove nth where it is the file index g_queue_pop_nth(self->tracks, file_index); // Remove nth where it is the file index
} }
gint file_index_in_sorted = g_queue_index(self->sorted_tracks, uuid); // Get position in sorted tracks gint file_index_in_sorted = g_queue_index(self->sorted_tracks, uuid); // Get position in sorted tracks
if (file_index_in_sorted != -1) { // Have in sorted tracks if (file_index_in_sorted != -1) { // Have in sorted tracks
g_queue_pop_nth(self->sorted_tracks, file_index_in_sorted); // Remove nth where it is the index in sorted tracks g_queue_pop_nth(self->sorted_tracks, file_index_in_sorted); // Remove nth where it is the index in sorted tracks
} }
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, uuid); // Get the track KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, uuid); // Get the track
if (!KOTO_IS_INDEXED_TRACK(track)) { // Is not a track if (!KOTO_IS_INDEXED_TRACK(track)) { // Is not a track
return; return;
@ -687,6 +778,7 @@ void koto_playlist_remove_track_by_uuid(KotoPlaylist *self, gchar *uuid) {
guint position = 0; guint position = 0;
if (g_list_store_find(self->store, track, &position)) { // Got the position if (g_list_store_find(self->store, track, &position)) { // Got the position
g_list_store_remove(self->store, position); // Remove from the store g_list_store_remove(self->store, position); // Remove from the store
} }
@ -701,13 +793,17 @@ void koto_playlist_remove_track_by_uuid(KotoPlaylist *self, gchar *uuid) {
); );
} }
void koto_playlist_set_artwork(KotoPlaylist *self, const gchar *path) { void koto_playlist_set_artwork(
KotoPlaylist * self,
const gchar * path
) {
if (path == NULL) { if (path == NULL) {
return; return;
} }
magic_t cookie = magic_open(MAGIC_MIME); // Create our magic cookie so we can validate if what we are setting is an image magic_t cookie = magic_open(MAGIC_MIME); // Create our magic cookie so we can validate if what we are setting is an image
if (cookie == NULL) { // Failed to allocate if (cookie == NULL) { // Failed to allocate
return; return;
} }
@ -716,7 +812,8 @@ void koto_playlist_set_artwork(KotoPlaylist *self, const gchar *path) {
goto free_cookie; goto free_cookie;
} }
const gchar *mime_type = magic_file(cookie, path); // Get the mimetype for this file const gchar * mime_type = magic_file(cookie, path); // Get the mimetype for this file
if ((mime_type == NULL) || !g_str_has_prefix(mime_type, "image/")) { // Failed to get our mimetype or not an image if ((mime_type == NULL) || !g_str_has_prefix(mime_type, "image/")) { // Failed to get our mimetype or not an image
goto free_cookie; goto free_cookie;
@ -740,7 +837,10 @@ free_cookie:
magic_close(cookie); // Close and free the cookie to the cookie monster magic_close(cookie); // Close and free the cookie to the cookie monster
} }
void koto_playlist_set_name(KotoPlaylist *self, const gchar *name) { void koto_playlist_set_name(
KotoPlaylist * self,
const gchar * name
) {
if (name == NULL) { if (name == NULL) {
return; return;
} }
@ -760,19 +860,29 @@ void koto_playlist_set_name(KotoPlaylist *self, const gchar *name) {
} }
} }
void koto_playlist_set_position(KotoPlaylist *self, gint position) { void koto_playlist_set_position(
KotoPlaylist * self,
gint position
) {
self->current_position = position; self->current_position = position;
} }
void koto_playlist_set_track_as_current(KotoPlaylist *self, gchar *track_uuid) { void koto_playlist_set_track_as_current(
KotoPlaylist * self,
gchar * track_uuid
) {
gint position_of_track = g_queue_index(self->sorted_tracks, track_uuid); // Get the position of the UUID in our tracks gint position_of_track = g_queue_index(self->sorted_tracks, track_uuid); // Get the position of the UUID in our tracks
if (position_of_track != -1) { // In tracks if (position_of_track != -1) { // In tracks
self->current_position = position_of_track; self->current_position = position_of_track;
} }
} }
void koto_playlist_set_uuid(KotoPlaylist *self, const gchar *uuid) { void koto_playlist_set_uuid(
KotoPlaylist * self,
const gchar * uuid
) {
if (uuid == NULL) { // No actual UUID if (uuid == NULL) { // No actual UUID
return; return;
} }
@ -784,9 +894,13 @@ void koto_playlist_set_uuid(KotoPlaylist *self, const gchar *uuid) {
self->uuid = g_strdup(uuid); // Set the new UUID self->uuid = g_strdup(uuid); // Set the new UUID
} }
void koto_playlist_tracks_queue_push_to_store(gpointer data, gpointer user_data) { void koto_playlist_tracks_queue_push_to_store(
gchar *track_uuid = (gchar *) data; gpointer data,
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, track_uuid); gpointer user_data
) {
gchar * track_uuid = (gchar*) data;
KotoIndexedTrack * track = koto_cartographer_get_track_by_uuid(koto_maps, track_uuid);
if (!KOTO_IS_INDEXED_TRACK(track)) { // Not a track if (!KOTO_IS_INDEXED_TRACK(track)) { // Not a track
return; return;
@ -795,20 +909,24 @@ void koto_playlist_tracks_queue_push_to_store(gpointer data, gpointer user_data)
g_list_store_append(G_LIST_STORE(user_data), track); g_list_store_append(G_LIST_STORE(user_data), track);
} }
void koto_playlist_unmap(KotoPlaylist *self) { void koto_playlist_unmap(KotoPlaylist * self) {
koto_cartographer_remove_playlist_by_uuid(koto_maps, self->uuid); // Remove from our cartographer koto_cartographer_remove_playlist_by_uuid(koto_maps, self->uuid); // Remove from our cartographer
} }
KotoPlaylist* koto_playlist_new() { KotoPlaylist * koto_playlist_new() {
return g_object_new(KOTO_TYPE_PLAYLIST, return g_object_new(
"uuid", g_uuid_string_random(), KOTO_TYPE_PLAYLIST,
"uuid",
g_uuid_string_random(),
NULL NULL
); );
} }
KotoPlaylist* koto_playlist_new_with_uuid(const gchar *uuid) { KotoPlaylist * koto_playlist_new_with_uuid(const gchar * uuid) {
return g_object_new(KOTO_TYPE_PLAYLIST, return g_object_new(
"uuid", uuid, KOTO_TYPE_PLAYLIST,
"uuid",
uuid,
NULL NULL
); );
} }

View file

@ -32,10 +32,10 @@ typedef enum {
/** /**
* Type Definition * Type Definition
**/ **/
#define KOTO_TYPE_PLAYLIST koto_playlist_get_type() #define KOTO_TYPE_PLAYLIST koto_playlist_get_type()
#define KOTO_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), KOTO_TYPE_PLAYLIST, KotoPlaylist)) #define KOTO_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), KOTO_TYPE_PLAYLIST, KotoPlaylist))
#define KOTO_IS_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYLIST)) #define KOTO_IS_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYLIST))
typedef struct _KotoPlaylist KotoPlaylist; typedef struct _KotoPlaylist KotoPlaylist;
@ -46,41 +46,131 @@ GType koto_playlist_get_type(void) G_GNUC_CONST;
/** /**
* Playlist Functions * Playlist Functions
**/ **/
KotoPlaylist* koto_playlist_new(); KotoPlaylist * koto_playlist_new();
KotoPlaylist* koto_playlist_new_with_uuid(const gchar *uuid);
void koto_playlist_add_to_played_tracks(KotoPlaylist *self, gchar *uuid); KotoPlaylist * koto_playlist_new_with_uuid(const gchar * uuid);
void koto_playlist_add_track(KotoPlaylist *self, KotoIndexedTrack *track, gboolean current, gboolean commit_to_table);
void koto_playlist_add_track_by_uuid(KotoPlaylist *self, gchar *uuid, gboolean current, gboolean commit_to_table); void koto_playlist_add_to_played_tracks(
void koto_playlist_apply_model(KotoPlaylist *self, KotoPreferredModelType preferred_model); KotoPlaylist * self,
void koto_playlist_commit(KotoPlaylist *self); gchar * uuid
void koto_playlist_commit_tracks(gpointer data, gpointer user_data); );
gint koto_playlist_compare_track_uuids(gconstpointer a, gconstpointer b);
gchar* koto_playlist_get_artwork(KotoPlaylist *self); void koto_playlist_add_track(
KotoPreferredModelType koto_playlist_get_current_model(KotoPlaylist *self); KotoPlaylist * self,
guint koto_playlist_get_current_position(KotoPlaylist *self); KotoIndexedTrack * track,
guint koto_playlist_get_length(KotoPlaylist *self); gboolean current,
gboolean koto_playlist_get_is_finalized(KotoPlaylist *self); gboolean commit_to_table
gchar* koto_playlist_get_name(KotoPlaylist *self); );
gint koto_playlist_get_position_of_track(KotoPlaylist *self, KotoIndexedTrack *track);
GListStore* koto_playlist_get_store(KotoPlaylist *self); void koto_playlist_add_track_by_uuid(
GQueue* koto_playlist_get_tracks(KotoPlaylist *self); KotoPlaylist * self,
gchar* koto_playlist_get_uuid(KotoPlaylist *self); gchar * uuid,
gchar* koto_playlist_go_to_next(KotoPlaylist *self); gboolean current,
gchar* koto_playlist_go_to_previous(KotoPlaylist *self); gboolean commit_to_table
void koto_playlist_mark_as_finalized(KotoPlaylist *self); );
gint koto_playlist_model_sort_by_uuid(gconstpointer first_item, gconstpointer second_item, gpointer data_list);
gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer second_item, gpointer model_ptr); void koto_playlist_apply_model(
void koto_playlist_remove_from_played_tracks(KotoPlaylist *self, gchar *uuid); KotoPlaylist * self,
void koto_playlist_remove_track_by_uuid(KotoPlaylist *self, gchar *uuid); KotoPreferredModelType preferred_model
void koto_playlist_set_artwork(KotoPlaylist *self, const gchar *path); );
void koto_playlist_save_state(KotoPlaylist *self);
void koto_playlist_set_name(KotoPlaylist *self, const gchar *name); void koto_playlist_commit(KotoPlaylist * self);
void koto_playlist_set_position(KotoPlaylist *self, gint position);
void koto_playlist_set_track_as_current(KotoPlaylist *self, gchar *track_uuid); void koto_playlist_commit_tracks(
void koto_playlist_set_uuid(KotoPlaylist *self, const gchar *uuid); gpointer data,
void koto_playlist_tracks_queue_push_to_store(gpointer data, gpointer user_data); gpointer user_data
void koto_playlist_unmap(KotoPlaylist *self); );
gint koto_playlist_compare_track_uuids(
gconstpointer a,
gconstpointer b
);
gchar * koto_playlist_get_artwork(KotoPlaylist * self);
KotoPreferredModelType koto_playlist_get_current_model(KotoPlaylist * self);
guint koto_playlist_get_current_position(KotoPlaylist * self);
guint koto_playlist_get_length(KotoPlaylist * self);
gboolean koto_playlist_get_is_finalized(KotoPlaylist * self);
gchar * koto_playlist_get_name(KotoPlaylist * self);
gint koto_playlist_get_position_of_track(
KotoPlaylist * self,
KotoIndexedTrack * track
);
GListStore * koto_playlist_get_store(KotoPlaylist * self);
GQueue * koto_playlist_get_tracks(KotoPlaylist * self);
gchar * koto_playlist_get_uuid(KotoPlaylist * self);
gchar * koto_playlist_go_to_next(KotoPlaylist * self);
gchar * koto_playlist_go_to_previous(KotoPlaylist * self);
void koto_playlist_mark_as_finalized(KotoPlaylist * self);
gint koto_playlist_model_sort_by_uuid(
gconstpointer first_item,
gconstpointer second_item,
gpointer data_list
);
gint koto_playlist_model_sort_by_track(
gconstpointer first_item,
gconstpointer second_item,
gpointer model_ptr
);
void koto_playlist_remove_from_played_tracks(
KotoPlaylist * self,
gchar * uuid
);
void koto_playlist_remove_track_by_uuid(
KotoPlaylist * self,
gchar * uuid
);
void koto_playlist_set_artwork(
KotoPlaylist * self,
const gchar * path
);
void koto_playlist_save_state(KotoPlaylist * self);
void koto_playlist_set_name(
KotoPlaylist * self,
const gchar * name
);
void koto_playlist_set_position(
KotoPlaylist * self,
gint position
);
void koto_playlist_set_track_as_current(
KotoPlaylist * self,
gchar * track_uuid
);
void koto_playlist_set_uuid(
KotoPlaylist * self,
const gchar * uuid
);
void koto_playlist_tracks_queue_push_to_store(
gpointer data,
gpointer user_data
);
void koto_playlist_unmap(KotoPlaylist * self);
G_END_DECLS G_END_DECLS