diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..04ba0dd --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/usr/include/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "c++20", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5d4fac9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "glib.h": "c" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..06c5717 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,73 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Clean builddir", + "type": "shell", + "command": "rm", + "args": [ + "-rf", + "builddir" + ], + "problemMatcher": [] + }, + { + "label": "Meson Configure", + "type": "shell", + "command": "meson", + "args": [ + "--prefix=/usr", + "--libdir=\"libdir\"", + "--sysconfdir=/etc", + "builddir" + ], + "problemMatcher": [] + }, + { + "label": "Meson Compile", + "type": "shell", + "command": "meson", + "args": [ + "compile", + "-C", + "builddir" + ], + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Meson Dist", + "type": "shell", + "command": "meson", + "args": [ + "dist", + "-C", + "builddir", + "--formats", + "xztar", + "--include-subprojects" + ], + "problemMatcher": [] + }, + { + "label": "Meson Install", + "type": "shell", + "command": "sudo", + "args": [ + "meson", + "install", + "-C", + "builddir", + "--destdir", + "/", + "--no-rebuild" + ], + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/data/com.github.joshstrobl.koto.appdata.xml.in b/data/com.github.joshstrobl.koto.appdata.xml.in index ce3a8a4..04164f4 100644 --- a/data/com.github.joshstrobl.koto.appdata.xml.in +++ b/data/com.github.joshstrobl.koto.appdata.xml.in @@ -2,7 +2,32 @@ com.github.joshstrobl.koto.desktop CC0-1.0 - + Apache-2.0 + Koto + Koto is an in-development audiobook, music, and podcast manager. + Koto is an in-development audiobook, music, and podcast manager that is designed for and caters to a modern desktop Linux experience. + com.github.joshstrobl.koto.desktop + https://github.com/JoshStrobl/koto + https://github.com/JoshStrobl/koto/issues + https://patreon.com/joshuastrobl + https://liberapay.com/joshuastrobl + Joshua Strobl + joshua.strobl_AT_outlook.com + + mild + + + com.github.joshstrobl.koto + + + pointing + touch + 1600 + + + keyboard + 1366 + diff --git a/data/com.github.joshstrobl.koto.desktop.in b/data/com.github.joshstrobl.koto.desktop.in index fb87fd6..eab43a2 100644 --- a/data/com.github.joshstrobl.koto.desktop.in +++ b/data/com.github.joshstrobl.koto.desktop.in @@ -1,7 +1,8 @@ [Desktop Entry] -Name=koto -Exec=koto +Name=Koto +Exec=com.github.joshstrobl.koto +Icon=audio-headphones Terminal=false Type=Application -Categories=GTK; +Categories=AudioVideo;Audio;GTK;Music;Player; StartupNotify=true diff --git a/data/meson.build b/data/meson.build index 3336028..99a118c 100644 --- a/data/meson.build +++ b/data/meson.build @@ -24,8 +24,8 @@ appstream_file = i18n.merge_file( appstream_util = find_program('appstream-util', required: false) if appstream_util.found() - test('Validate appstream file', appstream_util, - args: ['validate', appstream_file] + test('Validate appstream file (relaxed)', appstream_util, + args: ['validate-relax', appstream_file] ) endif diff --git a/src/koto-nav.c b/src/koto-nav.c index 16fc338..f40f2ff 100644 --- a/src/koto-nav.c +++ b/src/koto-nav.c @@ -20,6 +20,9 @@ #include "koto-button.h" #include "koto-expander.h" #include "koto-nav.h" +#include "koto-window.h" + +extern KotoWindow *main_window; struct _KotoNav { GObject parent_instance; @@ -93,6 +96,11 @@ static void koto_nav_init(KotoNav *self) { self->playlists_expander = pl_expander; gtk_box_append(GTK_BOX(self->content), GTK_WIDGET(self->playlists_expander)); } + + GtkGesture *playlist_add_gesture = gtk_gesture_click_new(); // Create a gesture for clicking on the playlist add + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(playlist_add_gesture), 1); // Only allow left click + g_signal_connect(playlist_add_gesture, "pressed", G_CALLBACK(koto_nav_handle_playlist_add_click), NULL); + gtk_widget_add_controller(GTK_WIDGET(playlist_add_button), GTK_EVENT_CONTROLLER(playlist_add_gesture)); } void koto_nav_create_audiobooks_section(KotoNav *self) { @@ -145,6 +153,12 @@ void koto_nav_create_podcasts_section(KotoNav *self) { 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) gesture; (void) n_press; (void) x; (void) y; (void) user_data; + g_message("plz"); + koto_window_show_create_playlist_dialog(main_window); +} + GtkWidget* koto_nav_get_nav(KotoNav *self) { return self->win; } diff --git a/src/koto-nav.h b/src/koto-nav.h index 2d14e51..2961fe5 100644 --- a/src/koto-nav.h +++ b/src/koto-nav.h @@ -28,6 +28,8 @@ KotoNav* koto_nav_new (void); void koto_nav_create_audiobooks_section(KotoNav *self); void koto_nav_create_music_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); + GtkWidget* koto_nav_get_nav(KotoNav *self); G_END_DECLS diff --git a/src/koto-playerbar.c b/src/koto-playerbar.c index 7cca72c..9a5a695 100644 --- a/src/koto-playerbar.c +++ b/src/koto-playerbar.c @@ -26,7 +26,7 @@ #include "koto-playerbar.h" extern KotoCurrentPlaylist *current_playlist; -extern KotoCartographer* koto_maps; +extern KotoCartographer *koto_maps; extern KotoPlaybackEngine *playback_engine; struct _KotoPlayerBar { @@ -91,8 +91,8 @@ static void koto_playerbar_constructed(GObject *obj) { 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 - g_signal_connect(GTK_GESTURE(press_controller), "begin", G_CALLBACK(koto_playerbar_handle_progressbar_gesture_begin), self); - g_signal_connect(GTK_GESTURE(press_controller), "end", G_CALLBACK(koto_playerbar_handle_progressbar_gesture_end), self); + g_signal_connect(press_controller, "begin", G_CALLBACK(koto_playerbar_handle_progressbar_gesture_begin), self); + g_signal_connect(press_controller, "end", G_CALLBACK(koto_playerbar_handle_progressbar_gesture_end), self); g_signal_connect(press_controller, "pressed", G_CALLBACK(koto_playerbar_handle_progressbar_pressed), self); //g_signal_connect(press_controller, "unpaired-release", G_CALLBACK(koto_playerbar_handle_progressbar_unpaired_release), self); diff --git a/src/koto-window.c b/src/koto-window.c index e94e73c..939535b 100644 --- a/src/koto-window.c +++ b/src/koto-window.c @@ -20,6 +20,7 @@ #include "pages/music/music-local.h" #include "playback/engine.h" #include "playlist/current.h" +#include "playlist/create-dialog.h" #include "koto-config.h" #include "koto-nav.h" #include "koto-playerbar.h" @@ -33,6 +34,9 @@ struct _KotoWindow { KotoIndexedLibrary *library; KotoCurrentPlaylist *current_playlist; + KotoCreatePlaylistDialog *playlist_create_dialog; + + GtkWidget *overlay; GtkWidget *header_bar; GtkWidget *menu_button; GtkWidget *search_entry; @@ -61,11 +65,16 @@ static void koto_window_init (KotoWindow *self) { create_new_headerbar(self); // Create our headerbar + self->overlay = gtk_overlay_new(); // Create our overlay + self->playlist_create_dialog = koto_create_playlist_dialog_new(); // Create our Create Playlist dialog + self->primary_layout = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_widget_add_css_class(self->primary_layout, "primary-layout"); gtk_widget_set_hexpand(self->primary_layout, TRUE); gtk_widget_set_vexpand(self->primary_layout, TRUE); + gtk_overlay_set_child(GTK_OVERLAY(self->overlay), self->primary_layout); // Add our primary layout to the overlay + self->content_layout = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_add_css_class(self->content_layout, "content-layout"); gtk_widget_set_hexpand(self->content_layout, TRUE); @@ -95,7 +104,7 @@ static void koto_window_init (KotoWindow *self) { gtk_box_append(GTK_BOX(self->primary_layout), playerbar_main); } - gtk_window_set_child(GTK_WINDOW(self), self->primary_layout); + gtk_window_set_child(GTK_WINDOW(self), self->overlay); #ifdef GDK_WINDOWING_X11 set_optimal_default_window_size(self); #endif @@ -127,6 +136,14 @@ void create_new_headerbar(KotoWindow *self) { gtk_window_set_titlebar(GTK_WINDOW(self), self->header_bar); } +void koto_window_hide_create_playlist_dialog(KotoWindow *self) { + gtk_overlay_remove_overlay(GTK_OVERLAY(self->overlay), koto_create_playlist_dialog_get_content(self->playlist_create_dialog)); +} + +void koto_window_show_create_playlist_dialog(KotoWindow *self) { + gtk_overlay_add_overlay(GTK_OVERLAY(self->overlay), koto_create_playlist_dialog_get_content(self->playlist_create_dialog)); +} + void load_library(KotoWindow *self) { KotoIndexedLibrary *lib = koto_indexed_library_new(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC)); diff --git a/src/koto-window.h b/src/koto-window.h index 59f9077..3dbca29 100644 --- a/src/koto-window.h +++ b/src/koto-window.h @@ -25,8 +25,11 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (KotoWindow, koto_window, KOTO, WINDOW, GtkApplicationWindow) -G_END_DECLS +void koto_window_show_create_playlist_dialog(KotoWindow *self); +void koto_window_hide_create_playlist_dialog(KotoWindow *self); void create_new_headerbar(KotoWindow *self); void load_library(KotoWindow *self); void set_optimal_default_window_size(KotoWindow *self); + +G_END_DECLS diff --git a/src/meson.build b/src/meson.build index e7374b6..a6c9ac5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,6 +15,7 @@ koto_sources = [ 'playback/media-keys.c', 'playback/mimes.c', 'playback/mpris.c', + 'playlist/create-dialog.c', 'playlist/current.c', 'playlist/playlist.c', 'main.c', diff --git a/src/playback/media-keys.c b/src/playback/media-keys.c index 67fd835..28e099c 100644 --- a/src/playback/media-keys.c +++ b/src/playback/media-keys.c @@ -101,10 +101,12 @@ void handle_media_keys_signal(GDBusProxy *proxy, const gchar *sender_name, const } void handle_window_enter(GtkEventControllerFocus *controller, gpointer user_data) { + (void) controller; (void) user_data; grab_media_keys(); // Grab our media keys } void handle_window_leave(GtkEventControllerFocus *controller, gpointer user_data) { + (void) controller; (void) user_data; release_media_keys(); // Release our media keys } diff --git a/src/playback/mimes.c b/src/playback/mimes.c index 2cf8b32..6f1b44d 100644 --- a/src/playback/mimes.c +++ b/src/playback/mimes.c @@ -35,7 +35,7 @@ gboolean koto_playback_engine_gst_caps_iter(GstCapsFeatures *features, GstStruct return TRUE; } - g_hash_table_insert(supported_mimes_hash, g_strdup(caps_name), TRUE); + g_hash_table_add(supported_mimes_hash, g_strdup(caps_name)); supported_mimes = g_list_prepend(supported_mimes, g_strdup(caps_name)); supported_mimes = g_list_prepend(supported_mimes, g_strdup(koto_utils_replace_string_all(caps_name, "x-", ""))); diff --git a/src/playback/mpris.c b/src/playback/mpris.c index bf388a6..c168414 100644 --- a/src/playback/mpris.c +++ b/src/playback/mpris.c @@ -93,7 +93,7 @@ void handle_method_call( GDBusMethodInvocation *invocation, gpointer user_data ) { - (void) connection; (void) sender; (void) object_path; (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(method_name, "Raise") == 0) { // Raise the window @@ -386,6 +386,7 @@ static const GDBusInterfaceVTable main_mpris_interface_vtable = { handle_method_call, handle_get_property, handle_set_property, + { 0 } }; void on_main_mpris_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { diff --git a/src/playlist/create-dialog.c b/src/playlist/create-dialog.c new file mode 100644 index 0000000..6907a0a --- /dev/null +++ b/src/playlist/create-dialog.c @@ -0,0 +1,99 @@ +/* create-dialog.h + * + * Copyright 2021 Joshua Strobl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "../db/cartographer.h" +#include "../db/db.h" +#include "create-dialog.h" + +extern GtkWindow *main_window; + +struct _KotoCreatePlaylistDialog { + GObject parent_instance; + GtkWidget *content; + GtkWidget *playlist_image; + GtkFileChooserNative *playlist_file_chooser; + GtkWidget *name_entry; +}; + +G_DEFINE_TYPE(KotoCreatePlaylistDialog, koto_create_playlist_dialog, G_TYPE_OBJECT); + +static void koto_create_playlist_dialog_class_init(KotoCreatePlaylistDialogClass *c) { + (void) c; +} + +static void koto_create_playlist_dialog_init(KotoCreatePlaylistDialog *self) { + self->content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_halign(self->content, GTK_ALIGN_CENTER); + gtk_widget_set_valign(self->content, GTK_ALIGN_CENTER); + + 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) { + gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self->content)); + GtkIconPaintable* audio_paintable = gtk_icon_theme_lookup_icon(default_icon_theme, "insert-image-symbolic", NULL, 96, scale_factor, GTK_TEXT_DIR_NONE, GTK_ICON_LOOKUP_PRELOAD); + + if (GTK_IS_ICON_PAINTABLE(audio_paintable)) { + self->playlist_image = gtk_image_new_from_paintable(GDK_PAINTABLE(audio_paintable)); + gtk_widget_set_size_request(self->playlist_image, 96, 96); + gtk_box_append(GTK_BOX(self->content), self->playlist_image); // Add our image to our content + + 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 + g_signal_connect(GTK_EVENT_CONTROLLER(image_click_controller), "pressed", G_CALLBACK(koto_create_playlist_dialog_handle_image_click), self); + + gtk_widget_add_controller(self->playlist_image, GTK_EVENT_CONTROLLER(image_click_controller)); + } + } + + self->playlist_file_chooser = gtk_file_chooser_native_new( + "Choose playlist image", + main_window, + GTK_FILE_CHOOSER_ACTION_OPEN, + "Choose", + "Cancel" + ); + + GtkFileFilter *image_filter = gtk_file_filter_new(); // Create our file filter + gtk_file_filter_add_pattern(image_filter, "image/*"); // Only allow for images + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(self->playlist_file_chooser), image_filter); // Only allow picking images + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(self->playlist_file_chooser), FALSE); + + self->name_entry = gtk_entry_new(); // Create our text entry for the name of the playlist + gtk_entry_set_placeholder_text(GTK_ENTRY(self->name_entry), "Name of playlist"); + gtk_entry_set_input_purpose(GTK_ENTRY(self->name_entry), GTK_INPUT_PURPOSE_NAME); + gtk_entry_set_input_hints(GTK_ENTRY(self->name_entry), GTK_INPUT_HINT_SPELLCHECK & GTK_INPUT_HINT_NO_EMOJI & GTK_INPUT_HINT_PRIVATE); + + gtk_box_append(GTK_BOX(self->content), self->name_entry); +} + +GtkWidget* koto_create_playlist_dialog_get_content(KotoCreatePlaylistDialog *self) { + return self->content; +} + +void koto_create_playlist_dialog_handle_image_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) { + (void) gesture; (void) n_press; (void) x; (void) y; + + KotoCreatePlaylistDialog *self = user_data; + + gtk_native_dialog_show(GTK_NATIVE_DIALOG(self->playlist_file_chooser)); // Show our file chooser +} + +KotoCreatePlaylistDialog* koto_create_playlist_dialog_new() { + return g_object_new(KOTO_TYPE_CREATE_PLAYLIST_DIALOG, NULL); +} diff --git a/src/playlist/create-dialog.h b/src/playlist/create-dialog.h new file mode 100644 index 0000000..c210b7b --- /dev/null +++ b/src/playlist/create-dialog.h @@ -0,0 +1,41 @@ +/* create-dialog.h + * + * Copyright 2021 Joshua Strobl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include + +G_BEGIN_DECLS + +/** + * Type Definition +**/ + +#define KOTO_TYPE_CREATE_PLAYLIST_DIALOG koto_create_playlist_dialog_get_type() +G_DECLARE_FINAL_TYPE(KotoCreatePlaylistDialog, koto_create_playlist_dialog, KOTO, CREATE_PLAYLIST_DIALOG, GObject); + +/** + * Create Dialog Functions +**/ + +KotoCreatePlaylistDialog* koto_create_playlist_dialog_new(); +GtkWidget* koto_create_playlist_dialog_get_content(KotoCreatePlaylistDialog *self); +void koto_create_playlist_dialog_handle_close(KotoCreatePlaylistDialog *self); +void koto_create_playlist_dialog_handle_create(KotoCreatePlaylistDialog *self); +void koto_create_playlist_dialog_handle_image_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data); + +G_END_DECLS diff --git a/src/playlist/playlist.c b/src/playlist/playlist.c index 3d85be0..164238e 100644 --- a/src/playlist/playlist.c +++ b/src/playlist/playlist.c @@ -254,11 +254,11 @@ gchar* koto_playlist_get_random_track(KotoPlaylist *self) { while (track_uuid == NULL) { // Haven't selected a track yet 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->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 - self->current_position = (int) selected_item; + self->current_position = (gint) selected_item; track_uuid = selected_track; break; } else { // Failed to get the track
Koto is an in-development audiobook, music, and podcast manager that is designed for and caters to a modern desktop Linux experience.