22// Copyright 2024 bbb651 <bar.ye651@gmail.com>
33// SPDX-License-Identifier: GPL-3.0-only
44
5- use std:: borrow:: Cow ;
6- use std:: collections:: { BTreeMap , BTreeSet } ;
7- use std:: path:: { Path , PathBuf } ;
8- use std:: sync:: Arc ;
9-
105use cosmic:: iced:: { Alignment , Length } ;
116use cosmic:: widget:: { self , dropdown, icon, settings} ;
127use cosmic:: { Apply , Element , Task , surface} ;
@@ -17,8 +12,16 @@ use cosmic_settings_page::{self as page, Section, section};
1712use freedesktop_desktop_entry:: {
1813 DesktopEntry , Iter as DesktopEntryIter , default_paths, get_languages_from_env,
1914} ;
15+ use mime:: Mime ;
2016use mime_apps:: App ;
2117use slotmap:: SlotMap ;
18+ use std:: borrow:: Cow ;
19+ use std:: collections:: { BTreeMap , BTreeSet } ;
20+ use std:: io:: SeekFrom ;
21+ use std:: path:: PathBuf ;
22+ use std:: str:: FromStr ;
23+ use std:: sync:: Arc ;
24+ use tokio:: io:: { AsyncSeekExt , AsyncWriteExt } ;
2225
2326const DROPDOWN_WEB_BROWSER : usize = 0 ;
2427const DROPDOWN_FILE_MANAGER : usize = 1 ;
@@ -47,7 +50,7 @@ pub enum Category {
4750#[ derive( Clone , Debug ) ]
4851pub enum Message {
4952 SetDefault ( Category , usize ) ,
50- Update ( CachedMimeApps ) ,
53+ Update ( Arc < CachedMimeApps > ) ,
5154 Surface ( surface:: Action ) ,
5255}
5356
@@ -63,13 +66,11 @@ impl From<Message> for crate::pages::Message {
6366 }
6467}
6568
66- #[ derive( Clone , Debug ) ]
69+ #[ derive( Debug ) ]
6770pub struct CachedMimeApps {
68- pub list : mime_apps:: List ,
69- pub local_list : mime_apps:: List ,
70- pub apps : Vec < AppMeta > ,
71- pub known_mimes : BTreeSet < mime:: Mime > ,
72- pub config_path : Box < Path > ,
71+ local_list : Option < ( mime_apps:: List , tokio:: fs:: File ) > ,
72+ apps : Vec < AppMeta > ,
73+ known_mimes : BTreeSet < mime:: Mime > ,
7374}
7475
7576#[ derive( Clone , Debug ) ]
@@ -80,7 +81,7 @@ pub struct AppMeta {
8081 icons : Vec < icon:: Handle > ,
8182}
8283
83- #[ derive( Clone , Debug , Default ) ]
84+ #[ derive( Debug , Default ) ]
8485pub struct Page {
8586 on_enter_handle : Option < cosmic:: iced:: task:: Handle > ,
8687 mime_apps : Option < CachedMimeApps > ,
@@ -113,41 +114,32 @@ impl page::Page<crate::pages::Message> for Page {
113114 }
114115
115116 let ( task, on_enter_handle) = Task :: future ( async move {
117+ let local_list = mime_apps:: load_user_mimeapps ( ) . await . ok ( ) ;
116118 let mut list = mime_apps:: List :: default ( ) ;
117119 list. load_from_paths ( & mime_apps:: list_paths ( ) ) ;
118-
119- let mut local_list = mime_apps:: List :: default ( ) ;
120-
121- if let Some ( path) = mime_apps:: local_list_path ( )
122- && let Ok ( buffer) = std:: fs:: read_to_string ( & path)
123- {
124- local_list. load_from ( & buffer) ;
125- }
126-
127- let assocs = mime_apps:: associations:: by_app ( ) ;
120+ let assocs = mime_apps:: associations:: by_app ( & list) ;
128121
129122 let apps = vec ! [
130- load_defaults( & assocs, & [ "x-scheme-handler/http" ] ) . await ,
131- load_defaults( & assocs, & [ "inode/directory" ] ) . await ,
132- load_defaults( & assocs, & [ "x-scheme-handler/mailto" ] ) . await ,
133- load_defaults( & assocs, & [ "audio/mp3" , "application/ogg" , "video/mp4" ] ) . await ,
134- load_defaults( & assocs, & [ "video/mp4" ] ) . await ,
135- load_defaults( & assocs, & [ "image/png" ] ) . await ,
136- load_defaults( & assocs, & [ "text/calendar" ] ) . await ,
123+ load_defaults( & list, & assocs, & [ "x-scheme-handler/http" ] ) ,
124+ load_defaults( & list, & assocs, & [ "inode/directory" ] ) ,
125+ load_defaults( & list, & assocs, & [ "x-scheme-handler/mailto" ] ) ,
126+ load_defaults(
127+ & list,
128+ & assocs,
129+ & [ "audio/mpeg" , "application/ogg" , "audio/x-flac" ] ,
130+ ) ,
131+ load_defaults( & list, & assocs, & [ "video/mp4" ] ) ,
132+ load_defaults( & list, & assocs, & [ "image/png" ] ) ,
133+ load_defaults( & list, & assocs, & [ "text/calendar" ] ) ,
137134 load_terminal_apps( & assocs) . await ,
138- load_defaults( & assocs, & [ "text/plain" ] ) . await ,
135+ load_defaults( & list , & assocs, & [ "text/plain" ] ) ,
139136 ] ;
140137
141- Message :: Update ( CachedMimeApps {
138+ Message :: Update ( Arc :: new ( CachedMimeApps {
142139 apps,
143- list,
144140 local_list,
145141 known_mimes : mime_apps:: mime_info:: mime_types ( ) ,
146- config_path : dirs:: config_dir ( )
147- . expect ( "config dir not found" )
148- . join ( "mimeapps.list" )
149- . into ( ) ,
150- } )
142+ } ) )
151143 . into ( )
152144 } )
153145 . abortable ( ) ;
@@ -188,6 +180,7 @@ impl Page {
188180 "application/ogg" ,
189181 "application/x-cue" ,
190182 "application/x-ogg" ,
183+ "audio/mpeg" ,
191184 "audio/mp3" ,
192185 "x-content/audio-cdda" ,
193186 ] )
@@ -238,7 +231,7 @@ impl Page {
238231
239232 let meta = & mut mime_apps. apps [ category_id] ;
240233
241- if meta. selected != Some ( id) {
234+ if meta. selected . is_none_or ( |selected| selected != id) {
242235 meta. selected = Some ( id) ;
243236 let appid = & meta. app_ids [ id] ;
244237
@@ -249,24 +242,36 @@ impl Page {
249242 assign_default_terminal ( config, appid) ;
250243 }
251244
252- for mime in mime_types {
253- if let Ok ( mime) = mime. parse ( ) {
254- mime_apps
255- . local_list
256- . set_default_app ( mime, [ appid, ".desktop" ] . concat ( ) ) ;
257- } ;
245+ if let Some ( ( local_list, local_file) ) = mime_apps. local_list . as_mut ( ) {
246+ for mime in mime_types {
247+ if let Ok ( mime) = mime. parse ( ) {
248+ tracing:: info!( target: "default-apps" , ?mime, appid, "setting default for mime" ) ;
249+ local_list. set_default_app ( mime, [ appid, ".desktop" ] . concat ( ) ) ;
250+ } ;
251+ }
252+
253+ let mut buffer = local_list. to_string ( ) ;
254+ buffer. push ( '\n' ) ;
255+
256+ if let Ok ( mut local_file) =
257+ futures:: executor:: block_on ( local_file. try_clone ( ) )
258+ {
259+ tokio:: spawn ( async move {
260+ tracing:: debug!( target: "default-apps" , buffer, "writing to mimeapps config" ) ;
261+ _ = local_file. seek ( SeekFrom :: Start ( 0 ) ) . await ;
262+ _ = local_file. set_len ( buffer. len ( ) as u64 ) . await ;
263+ _ = local_file. write_all ( buffer. as_bytes ( ) ) . await ;
264+ _ = local_file. flush ( ) . await ;
265+ _ = local_file. seek ( SeekFrom :: Start ( 0 ) ) . await ;
266+ _ = tokio:: process:: Command :: new ( "update-desktop-database" )
267+ . status ( )
268+ . await ;
269+ } ) ;
270+ }
258271 }
259-
260- let mut buffer = mime_apps. local_list . to_string ( ) ;
261- buffer. push ( '\n' ) ;
262-
263- _ = std:: fs:: write ( & mime_apps. config_path , buffer) ;
264- _ = std:: process:: Command :: new ( "update-desktop-database" ) . status ( ) ;
265272 }
266273 }
267- Message :: Update ( mime_apps) => {
268- self . mime_apps = Some ( mime_apps) ;
269- }
274+ Message :: Update ( mime_apps) => self . mime_apps = Arc :: into_inner ( mime_apps) ,
270275 Message :: Surface ( a) => {
271276 return cosmic:: task:: message ( crate :: app:: Message :: Surface ( a) ) ;
272277 }
@@ -286,7 +291,7 @@ fn app_item(meta: &AppMeta, label: String, category: Category) -> widget::FlexRo
286291 } else {
287292 dropdown:: popup_dropdown (
288293 & meta. apps ,
289- meta. selected ,
294+ Some ( meta. selected . unwrap_or ( 0 ) ) ,
290295 move |id| Message :: SetDefault ( category, id) ,
291296 cosmic:: iced:: window:: Id :: RESERVED ,
292297 Message :: Surface ,
@@ -393,12 +398,16 @@ fn assign_default_terminal(config: &cosmic_config::Config, appid: &str) {
393398 }
394399}
395400
396- async fn load_defaults ( assocs : & BTreeMap < Arc < str > , Arc < App > > , for_mimes : & [ & str ] ) -> AppMeta {
401+ fn load_defaults (
402+ list : & mime_apps:: List ,
403+ assocs : & BTreeMap < Arc < str > , Arc < App > > ,
404+ for_mimes : & [ & str ] ,
405+ ) -> AppMeta {
397406 let mut unsorted = Vec :: new ( ) ;
398407 let mut current_app = None ;
399408
400409 for for_mime in for_mimes {
401- let Ok ( mime) = for_mime . parse ( ) else {
410+ let Ok ( mime) = Mime :: from_str ( for_mime ) else {
402411 return AppMeta {
403412 selected : None ,
404413 app_ids : Vec :: new ( ) ,
@@ -407,7 +416,12 @@ async fn load_defaults(assocs: &BTreeMap<Arc<str>, Arc<App>>, for_mimes: &[&str]
407416 } ;
408417 } ;
409418
410- let current_app_entry = xdg_mime_query_default ( for_mime) . await ;
419+ let current_app_entry = dbg ! (
420+ list. default_app_for( & mime)
421+ . and_then( |entries| entries. first( ) )
422+ ) ;
423+
424+ tracing:: info!( target: "default-apps" , ?mime, ?current_app_entry, "entry for mime" ) ;
411425 let current_appid = current_app_entry
412426 . as_ref ( )
413427 . and_then ( |entry| entry. strip_suffix ( ".desktop" ) ) ;
0 commit comments