Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions blanket/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from blanket.settings import Settings
from blanket.widgets import PresetDialog
from blanket.window import BlanketWindow
from blanket.widgets.sound_rename_dialog import SoundRenameDialog


class Application(Adw.Application):
Expand Down Expand Up @@ -130,11 +131,16 @@ def setup_actions(self):
action.connect('activate', self.on_open)
self.add_action(action)

# Add sound file
action = Gio.SimpleAction.new('remove-sound', GLib.VariantType('s'))
# Remove sound file
action = Gio.SimpleAction.new('remove-sound', GLib.VariantType('u'))
action.connect('activate', self.on_remove_sound)
self.add_action(action)

# Rename sound file
action = Gio.SimpleAction.new('rename-sound', GLib.VariantType('u'))
action.connect('activate', self.on_rename_sound)
self.add_action(action)

# Setup accelerator
self.set_accels_for_action('app.quit', ['<Ctl>q'])
self.set_accels_for_action('app.preferences', ['<Ctl>comma'])
Expand Down Expand Up @@ -207,13 +213,22 @@ def on_add_preset(self, _action, _param):
dialog = PresetDialog()
dialog.present(self.window)

def on_remove_sound(self, _action, name: GLib.Variant):
sound, index = MainPlayer.get().get_by_name(name.get_string())
def on_remove_sound(self, _action, index_variant: GLib.Variant):
index = index_variant.get_uint32()
sound = MainPlayer.get().get_by_index(index)

if sound and index:
sound.remove() # type: ignore
MainPlayer.get().remove(index)

def on_rename_sound(self, _action, index_variant: GLib.Variant):
# Open edit dialog
index = index_variant.get_uint32()
sound = MainPlayer.get().get_by_index(index)
if sound and index:
dialog = SoundRenameDialog(sound, index)
dialog.present(self.window)

def on_background(self, action, value):
action.set_state(value)
Settings.get().background = value
Expand Down Expand Up @@ -241,6 +256,12 @@ def on_about(self, _action, _param):

about.present(self.window)

def set_space_accel(self, _action):
self.set_accels_for_action('app.playpause', ['<Ctl>m', 'space'])

def unset_space_accel(self, _action):
self.set_accels_for_action('app.playpause', ['<Ctl>m'])

def on_quit(self, _action, _param):
self.quit()

Expand Down
6 changes: 6 additions & 0 deletions blanket/main_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,9 @@ def get_by_name(self, name: str) -> tuple[GObject.Object, int] | tuple[None, Non
return (sound, position)

return (None, None)

def get_index(self, name: str) -> int | None:
return self.get_by_name(name)[1]

def get_by_index(self, index: int) -> GObject.Object:
return self._sounds[index]
14 changes: 14 additions & 0 deletions blanket/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,20 @@ def remove_custom_audio(self, name: str):
del self.volume[name]
"""

def rename_custom_audio(self, old_name: str, new_name: str):
if old_name not in self.custom_audios:
return

if new_name in self.custom_audios:
# TODO: Do something if True
return

saved_audios = self.custom_audios
uri = saved_audios[old_name]
del saved_audios[old_name]
saved_audios[new_name] = uri
self.custom_audios = saved_audios

""" Presets """

@property
Expand Down
3 changes: 3 additions & 0 deletions blanket/widgets/preset_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def __init__(self, preset=None, **kwargs):
# Wire buttons
self.accept_btn.connect('clicked', self._on_rename_preset)

self.connect('realize', Gio.Application.get_default().unset_space_accel)
self.connect('closed', Gio.Application.get_default().set_space_accel)

@Gtk.Template.Callback()
def _on_cancel_clicked(self, _button):
self.close()
Expand Down
21 changes: 14 additions & 7 deletions blanket/widgets/sound_context_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,34 @@
from gi.repository import Gio, GObject, GLib, Gtk

from blanket.define import RES_PATH

from blanket.sound import Sound
from blanket.main_player import MainPlayer

@Gtk.Template(resource_path=f'{RES_PATH}/sound-context-menu.ui')
class SoundContextMenu(Gtk.PopoverMenu):
__gtype_name__ = 'SoundContextMenu'

volume: Gtk.Scale = Gtk.Template.Child() # type: ignore

def __init__(self, sound):
def __init__(self, sound: Sound):
super().__init__()

self.sound = sound
self.sound: Sound = sound

if self.sound.custom:
index = MainPlayer.get().get_index(sound.name)
# Set remove menu item
custom_section = Gio.Menu()
remove_item = Gio.MenuItem.new(_('Remove'), None)
remove_item.set_action_and_target_value(
'app.remove-sound', GLib.Variant.new_string(self.sound.name)
self.rename_item = Gio.MenuItem.new(_('Rename'), None)
self.rename_item.set_action_and_target_value(
'app.rename-sound', GLib.Variant.new_uint32(index)
)
custom_section.append_item(self.rename_item)
self.remove_item = Gio.MenuItem.new(_('Remove'), None)
self.remove_item.set_action_and_target_value(
'app.remove-sound', GLib.Variant.new_uint32(index)
)
custom_section.insert_item(-1, remove_item)
custom_section.append_item(self.remove_item)

self.props.menu_model.append_section(None, custom_section) # type: ignore

Expand Down
20 changes: 11 additions & 9 deletions blanket/widgets/sound_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,17 @@ def _on_secondary_click(self, _ctrl, _n, x: int, y: int):
self._context_popover(x, y)

def _context_popover(self, x: int, y: int):
if self.sound is not None:
if self._menu is None:
self._menu = SoundContextMenu(self.sound)
if self.sound is None:
return

rec = Gdk.Rectangle()
rec.x = x
rec.y = y
if self._menu is None:
self._menu = SoundContextMenu(self.sound)

self._menu.set_parent(self)
self._menu.set_pointing_to(rec)
rec = Gdk.Rectangle()
rec.x = x
rec.y = y

self._menu.popup()
self._menu.set_parent(self)
self._menu.set_pointing_to(rec)

self._menu.popup()
78 changes: 78 additions & 0 deletions blanket/widgets/sound_rename_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright 2025 Rafael Mardojai CM
# SPDX-License-Identifier: GPL-3.0-or-later

from gettext import gettext as _
from gi.repository import Gio, Gtk, Adw

from blanket.define import RES_PATH
from blanket.sound import Sound
from blanket.settings import Settings

@Gtk.Template(resource_path=f'{RES_PATH}/sound-rename-dialog.ui')
class SoundRenameDialog(Adw.Dialog):
__gtype_name__ = 'SoundRenameDialog'

headerbar: Adw.HeaderBar = Gtk.Template.Child() # type: ignore
title_widget: Adw.WindowTitle = Gtk.Template.Child() # type: ignore
accept_btn: Gtk.Button = Gtk.Template.Child() # type: ignore
name_entry: Adw.EntryRow = Gtk.Template.Child() # type: ignore

def __init__(self, sound: Sound, index: int, **kwargs):
super().__init__()

self.sound = sound
self.index = index
app = Gio.Application.get_default()
self.window = app.get_active_window() # type: ignore

self.set_title(_('Rename Sound'))
self.title_widget.set_subtitle(self.sound.name)
self.name_entry.set_text(self.sound.name)
# Wire buttons
self.accept_btn.connect('clicked', self._on_rename_sound)

self.connect('realize', Gio.Application.get_default().unset_space_accel)
self.connect('closed', Gio.Application.get_default().set_space_accel)

@Gtk.Template.Callback()
def _on_cancel_clicked(self, _button):
self.close()

@Gtk.Template.Callback()
def _on_entry_changed(self, _entry):
name = self.__get_name()

if self.sound is not None and (self.sound.name == name or name in Settings.get().custom_audios):
self.accept_btn.set_sensitive(False)
return

if name:
self.accept_btn.set_sensitive(True)
else:
self.accept_btn.set_sensitive(False)

def _on_rename_sound(self, _button):
new_name = self.__get_name()

if new_name == self.sound.name:
self.close()
return

if not new_name or new_name in Settings.get().custom_audios:
self.__invalid_name()
return

Settings.get().rename_custom_audio(self.sound.name, new_name)
self.sound.name = new_name
self.sound.title = new_name

self.close()

def __get_name(self):
name = self.name_entry.get_text()
name = name.strip() # Strip name

return name

def __invalid_name(self):
pass
27 changes: 17 additions & 10 deletions blanket/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,18 @@ def populate_sounds(self):
alert.present(self)

def open_audio(self):
def on_response(_filechooser, _id):
gfile = self.filechooser.get_file()
if gfile:
def on_response(dialog, result):
try:
gfiles = dialog.open_multiple_finish(result)
except GLib.Error as e:
if e.code != Gtk.DialogError.DISMISSED:
print(f"Error: {e.message}")
return
for gfile in gfiles:
filename = gfile.get_path()
if filename:
name = os.path.basename(filename).split('.')[0]
basename = os.path.basename(filename)
name = basename[:basename.rfind('.')]
uri = gfile.get_uri()

# Create a new Sound
Expand Down Expand Up @@ -175,20 +181,21 @@ def on_response(_filechooser, _id):
'AAC': ['audio/aac'],
}

self.filechooser = Gtk.FileChooserNative.new( # type: ignore
_('Open audio'), self, Gtk.FileChooserAction.OPEN, None, None
)
self.filechooser = Gtk.FileDialog.new()
self.filechooser.set_title(_('Open audio'))
self.filechooser.set_modal(True)
self.filechooser.connect('response', on_response)

filter_store = Gio.ListStore.new(Gtk.FileFilter)
for f, mts in filters.items():
audio_filter = Gtk.FileFilter()
audio_filter.set_name(f)
for mt in mts:
audio_filter.add_mime_type(mt)
self.filechooser.add_filter(audio_filter)
filter_store.append(audio_filter)

self.filechooser.set_filters(filter_store)

self.filechooser.show()
self.filechooser.open_multiple(self, None, on_response)

@Gtk.Template.Callback()
def _on_narrow_window_apply(self, _breakpoint):
Expand Down
1 change: 1 addition & 0 deletions data/resources/blanket.gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<file>volume-row.ui</file>
<file>sound-item.ui</file>
<file>sound-context-menu.ui</file>
<file>sound-rename-dialog.ui</file>

<!-- CSS -->
<file>style.css</file>
Expand Down
1 change: 1 addition & 0 deletions data/resources/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ blueprints = custom_target('blueprints',
'sound-item.blp',
'volume-row.blp',
'window.blp',
'sound-rename-dialog.blp',
),
output: '.',
command: [blueprint, 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'],
Expand Down
2 changes: 1 addition & 1 deletion data/resources/preset-dialog.blp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ using Adw 1;

template $PresetDialog : Adw.Dialog {
width-request: 360;
height-request: 220;
height-request: 148;
focus-widget: name_entry;

Adw.ToolbarView {
Expand Down
48 changes: 48 additions & 0 deletions data/resources/sound-rename-dialog.blp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Gtk 4.0;
using Adw 1;

template $SoundRenameDialog : Adw.Dialog {
width-request: 360;
height-request: 148;
focus-widget: name_entry;

Adw.ToolbarView {

[top]
Adw.HeaderBar headerbar {
show-end-title-buttons: false;
show-start-title-buttons: false;

title-widget: Adw.WindowTitle title_widget {
title: bind template.title;
};

Button {
label: _("Cancel");

clicked => $_on_cancel_clicked();
}

[end]
Button accept_btn {
sensitive: false;
label: _("Save");

styles [
"suggested-action"
]
}
}

Adw.PreferencesPage {
Adw.PreferencesGroup {
Adw.EntryRow name_entry {
title: _("Sound Name");
selectable: false;

changed => $_on_entry_changed();
}
}
}
}
}
2 changes: 2 additions & 0 deletions po/POTFILES
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data/resources/preset-dialog.blp
data/resources/preset-row.blp
data/resources/shortcuts.blp
data/resources/sound-context-menu.blp
data/resources/sound-rename-dialog.blp
data/resources/sound-item.blp
data/resources/volume-row.blp
data/resources/window.blp
Expand All @@ -27,5 +28,6 @@ blanket/widgets/preset_chooser.py
blanket/widgets/preset_dialog.py
blanket/widgets/preset_row.py
blanket/widgets/sound_context_menu.py
blanket/widgets/sound_rename_dialog.py
blanket/widgets/sound_item.py
blanket/widgets/volume_row.py
Loading
Loading