1use crate::icon_theme::*;
70use crate::basedir::*;
71use std::path::PathBuf;
72use std::path::Path;
73extern crate tini;
74use tini::Ini;
75use std::collections::HashMap;
76
77const EXTENTIONS:[&str; 3] = [".png", ".svg", ".xpm"];
79const INDEX_FILE:&str = "index.theme";
81
82#[derive(Debug, Clone)]
86pub struct DirList {
87 pub dirs:Vec<PathBuf>,
89 pub theme:String,
91}
92impl DirList {
93 pub fn index(&self) -> PathBuf {
95 let mut return_value = self.dirs[0].to_owned();
96 return_value.push(INDEX_FILE);
97 return_value
98 }
99}
100pub fn generate_dir_list() -> Vec<DirList>{
102 let mut return_value:Vec< DirList> = vec![];
103
104 let directory_vec = icon_dirs_vector();
106 let mut themes = HashMap::<String, Vec<PathBuf>>::new(); for directory in directory_vec {
111 let path = Path::new(directory.as_str());
112 if path.is_dir() {
113 let dir_path = std::fs::read_dir(path);
114 if let Ok(dp) = dir_path {
115 for entity in dp.flatten() {
116 let return_path = entity.path();
118 let basename = entity.file_name();
119 let theme_name = match basename.to_str() {
120 Some(v) => v,
121 None => continue
122 };
123 themes.entry(theme_name.to_string()).and_modify(|paths| paths.push(return_path.clone())).or_insert(Vec::<PathBuf>::from([return_path.clone()]));
124 }
125 }
126 }
127 }
128
129 for (_theme_name, ref mut return_paths) in themes {
130 let mut index_theme_path = None;
131 let mut index_theme_index = 0;
132 for (i, return_path) in return_paths.iter().enumerate() {
133 let mut index = return_path.clone();
134 index.push(INDEX_FILE);
135 if index.is_file() {
136 index_theme_path = Some(index);
137 index_theme_index = i;
138 }
139 }
140 if index_theme_path.is_none() {
141 continue
142 }
143 return_paths.swap(0, index_theme_index);
145 let index_theme_path = index_theme_path.unwrap();
146 let theme = IconTheme::new(index_theme_path.to_owned().to_str().unwrap().to_string());
147 let theme_name = match theme.name {
148 Some(name) => name.to_owned(),
149 None => continue,
150 };
151 return_value.push(
155 DirList{
156 theme:theme_name.to_owned(),
157 dirs:return_paths.clone(),
158 }
159 );
160 }
161 return_value
163}
164
165fn get_theme(place: &str, dir_list_vector:Vec<DirList>) -> Option<DirList> {
166 for theme in &dir_list_vector {
167 if theme.theme == place {
168 return Some(theme.clone())
169 }
170 }
171 for theme in dir_list_vector {
173 for dir in theme.dirs.iter() {
174 if let Some(file_name) = dir.file_name() {
175 if file_name == place {
176 return Some(theme.clone())
177 }
178 }
179 }
180 }
181 return None
182}
183
184fn check_user_config(file:&str, section:&str, item:&str, dir_list_vector:Vec<DirList>) -> Option<IconTheme> {
185 let conf:String = match config_home() {
186 Ok(conf) => format!("{}/{}",conf, file),
187 Err(e) => {
188 println!("Error:{}", e);
189 return None
190 },
191 };
192 if Path::new(conf.as_str()).is_file() {
193 let test_ini = Ini::from_file(&conf);
194 if let Ok(conf) = test_ini {
195 let theme:Option<String> = conf.get(section,item);
196 if let Some(themed) = theme {
198 let theme_file:PathBuf = match get_theme(
199 &themed,
200 dir_list_vector.
201 clone()
202 ).map(|theme| theme.index()) {
203 Some(theme_file) =>theme_file,
204 None =>PathBuf::new(),
205 };
206 if theme_file.exists() {
207 return Some(IconTheme::from_pathbuff(theme_file));
208 }
209 }
210 }
211 }
212 None
213}
214pub fn user_theme(dir_list_vector:Vec<DirList>) -> Option<IconTheme> {
216 let theme_dir = icon_dirs();
217 if theme_dir.is_ok() {
218 if let Some(kde) = check_user_config("kdeglobals",
220 "Icons",
221 "Theme",
222 dir_list_vector.clone()) {
223 return Some(kde);
224 };
225 if let Some(gtk) = check_user_config("gtk-3.0/settings.ini",
226 "Settings",
227 "gtk-icon-theme-name",
228 dir_list_vector.clone()) {
229 return Some(gtk);
230 };
231 if let Some(gtk) = check_user_config("gtk-4.0/settings.ini",
232 "Settings",
233 "gtk-icon-theme-name",
234 dir_list_vector.clone()) {
235 return Some(gtk);
236 };
237 }
238 let theme_file:PathBuf = match get_theme("hicolor", dir_list_vector).map(|theme| theme.index()) {
240 Some(theme_file) =>theme_file,
241 None =>PathBuf::new(),
242 };
243 if theme_file.exists() {
244 return Some(IconTheme::from_pathbuff(theme_file))
245 }
246 None
247}
248
249pub fn find_icon(icon:String, size:i32, scale:i32) -> Option<PathBuf> {
260 let dir_list_vector = generate_dir_list();
261 let mut theme = user_theme(dir_list_vector.clone());
262 if theme.is_none() {
263 theme = Some(IconTheme::empty());
265 }
266 let theme:IconTheme = theme.unwrap();
267 multiple_find_icon(icon, size, scale, dir_list_vector, theme)
269}
270pub fn multiple_find_icon(icon:String, size:i32, scale:i32, dir_list_vector:Vec<DirList>, theme:IconTheme) -> Option<PathBuf> {
271 let mut filename:Option<PathBuf> = find_icon_helper(icon.to_owned(), size, scale, theme, dir_list_vector.clone());
273 if filename.is_some(){ return filename }
274
275 let theme_file:PathBuf = match get_theme("hicolor", dir_list_vector.clone()).map(|theme| theme.index()) {
277 Some(theme_file) => theme_file,
278 None => PathBuf::new(),
279 };
280 let i_theme_file:String = match theme_file.as_path().to_str() {
281 Some(t) => String::from(t),
282 None => String::from(""),
283 };
284 let hicolor = IconTheme::new(i_theme_file);
285 filename = find_icon_helper(icon.to_owned(), size, scale, hicolor, dir_list_vector);
286 if filename.is_some(){ return filename }
287
288 lookup_fallback_icon(icon)
290}
291
292pub fn find_icon_helper(icon:String, size:i32, scale:i32, theme:IconTheme, dir_list_vector:Vec<DirList>) -> Option<PathBuf> {
294 let mut filename:Option<PathBuf> = lookup_icon (icon.to_owned(), size, scale, theme.clone(), dir_list_vector.clone());
295 if filename.is_some(){ return filename }
296
297 if let Some(parents) = theme.inherits {
298 for parent in parents {
299 let theme_file:PathBuf = match get_theme(&parent, dir_list_vector.clone()).map(|theme| theme.index()) {
301 Some(theme_file) => theme_file,
302 None => PathBuf::new(),
303 };
304 let i_theme_file:String = match theme_file.as_path().to_str() {
305 Some(t) => String::from(t),
306 None => String::from(""),
307 };
308 let parent_theme = IconTheme::new(i_theme_file.to_owned());
309 filename = find_icon_helper (icon.to_owned(), size, scale, parent_theme.clone(), dir_list_vector.clone());
311 if filename.is_some(){ return filename }
312 }
313 }
314 None
315}
316
317pub fn lookup_icon (iconname:String, size:i32, scale:i32, theme:IconTheme, dir_list_vector:Vec<DirList>) -> Option<PathBuf> {
319 let list = theme.directories.to_owned();
320 match list.as_ref() {
322 Some(_r) => (),
323 None => {
324 println!("Could not turn list into reference");
325 return None;
326 },
327 };
328
329 let theme_name = theme.name
330 .unwrap();
331 let mut closest_filename:PathBuf = PathBuf::new();
332 let theme_subdir_list:Vec<Directory> = list.unwrap();
333
334 if let Some(theme) = get_theme(&theme_name, dir_list_vector) {
335 for directory in theme.dirs {
336 for subdir in theme_subdir_list.clone() {
338 let subdir_name = subdir.name
339 .to_owned()
340 .unwrap();
341 for extension in EXTENTIONS.iter() {
342 let mut path = directory.to_owned();
343 path.push(subdir_name.as_str());
344 let mut file_name:String = iconname.to_owned();
345 file_name.push_str(extension);
346 path.push(file_name.as_str());
347 if directory_matches_size(subdir.to_owned(), size, scale)
349 && path.as_path().is_file() {
350 return Some(path)
351 }
352 }
353 }
354 }
355 }
356 let mut minimal_size:i32 = std::i32::MAX;
358 for subdir in theme_subdir_list {
360 let subdir_name = subdir.name
361 .to_owned()
362 .unwrap();
363 let directory_vec:Vec<PathBuf> = to_pathbuff(icon_dirs_vector());
364 for directory in directory_vec {
365 for extension in EXTENTIONS.iter() {
366 let mut path = directory.to_owned();
367 path.push(theme_name.as_str());
368 path.push(subdir_name.as_str());
369 let mut file_name:String = iconname.to_owned();
370 file_name.push_str(extension);
371 path.push(file_name.as_str());
372 if path.as_path().is_file() && directory_size_distance(subdir.clone(), size, scale) < minimal_size {
373 closest_filename = path.to_owned();
374 minimal_size = directory_size_distance(subdir.clone(), size, scale);
375 }
376 }
377 }
378 }
379 if closest_filename.as_path() == Path::new("") {
380 None
381 } else {
382 Some(closest_filename)
383 }
384}
385
386pub fn lookup_fallback_icon (iconname:String) ->Option<PathBuf> {
388 let directory_vec:Vec<PathBuf> = to_pathbuff(icon_dirs_vector());
389 for directory in directory_vec {
390 for extension in EXTENTIONS.iter() {
391 let mut path = directory.to_owned();
392 let mut file_name:String = iconname.to_owned();
393 file_name.push_str(extension);
394 path.push(file_name.as_str());
395 if path.as_path().is_file() {
396 return Some(path)
397 }
398 }
399 }
400 None
401}
402
403pub fn directory_matches_size(subdir:Directory, iconsize:i32, iconscale:i32) -> bool {
405 let mut scale = 1;
406 if subdir.scale.is_some() {
407 scale = subdir.scale.unwrap();
408 }
409 if scale != iconscale {
411 return false
413 }
414 let mut d_type = DirectoryType::Threshold;
416 if let Some(d) = subdir.xdg_type {
417 d_type = d;
418 }
419 let size = subdir.size;
420 if size.is_none() {
422 return false
423 }
424 let size:i32 = size.unwrap();
425 let min_size:i32 = match subdir.min_size {
428 Some(s) => s,
429 None => size,
430 };
431 let max_size:i32 = match subdir.max_size {
433 Some(s) => s,
434 None => size,
435 };
436 let threshold:i32 = match subdir.threshold {
438 Some(s) => s,
439 None => 2,
440 };
441 match d_type {
443 DirectoryType::Fixed => {
444 return size == iconsize
446 },
447 DirectoryType::Scalable => {
448 if min_size <= iconsize &&
450 iconsize <= max_size {
451 return true
453 }
454 },
455 DirectoryType::Threshold => {
456 if (size - threshold) <= iconsize &&
458 iconsize <= (size + threshold) {
459 return true
461 }
462 }
463 }
464 false
466}
467pub fn directory_size_distance(subdir:Directory, iconsize:i32, iconscale:i32) -> i32{
469 let mut scale = 1;
471 if subdir.scale.is_some() {
472 scale = subdir.scale.unwrap();
473 }
474 let mut d_type = DirectoryType::Threshold;
476 if subdir.xdg_type.is_some() {
477 d_type = subdir.xdg_type.unwrap();
478 }
479 let size = subdir.size;
481 if size.is_none() {return 0}
482 let size:i32 = size.unwrap();
483 let mut min_size:i32 = subdir.size.unwrap();
485 if subdir.min_size.is_some() {
486 min_size = subdir.min_size.unwrap();
487 }
488 let mut max_size:i32 = subdir.size.unwrap();
490 if subdir.max_size.is_some() {
491 max_size = subdir.max_size.unwrap();
492 }
493 let mut threshold:i32 = 2;
495 if subdir.threshold.is_some() {
496 threshold = subdir.threshold.unwrap();
497 }
498
499 match d_type {
502 DirectoryType::Fixed => {
503 let num:i32 = size * scale - iconsize * iconscale;
504 num.abs()
505 },
506 DirectoryType::Scalable => {
507 if iconsize * iconscale < min_size * scale {
508 return min_size * scale - iconsize * iconscale
509 }
510 if iconsize * iconscale > max_size * scale {
511 return iconsize * iconscale - max_size * scale
512 }
513 0
514 },
515 DirectoryType::Threshold => {
516 if iconsize * iconscale < (size - threshold) * scale {
517 return min_size * scale - iconsize * iconscale
518 }
519 if iconsize*iconsize > (size + threshold) * scale {
520 return iconsize * iconsize - max_size * scale
521 }
522 0
523 },
524 }
525}
526
527pub fn find_best_icon(icon_list:Vec<String>, size:i32, scale:i32) -> Option<PathBuf> {
529
530 let dir_list_vector = generate_dir_list();
531 let theme:IconTheme = match user_theme(dir_list_vector.clone()){
532 Some(theme) => theme,
533 None => {
534 IconTheme::empty();
535 return None
536 },
537 };
538
539 let mut filename:Option<PathBuf> = find_best_icon_helper(
541 icon_list.clone(),
542 size,
543 scale,
544 theme,
545 dir_list_vector.clone()
546 );
547 if filename.is_some(){ return filename }
548
549 let theme_file:PathBuf = match get_theme("hicolor", dir_list_vector.clone()).map(|theme| theme.index()) {
552 Some(theme_file) => theme_file,
553 None => PathBuf::new(),
554 };
555 let i_theme_file:String = match theme_file.as_path().to_str() {
556 Some(t) => String::from(t),
557 None => String::from(""),
558 };
559 let hicolor = IconTheme::new(i_theme_file);
560 filename = find_best_icon_helper(icon_list.clone(), size, scale, hicolor, dir_list_vector);
561
562 if filename.is_some(){ return filename }
563
564 for icon in icon_list {
565 filename = lookup_fallback_icon(icon);
566 if filename.is_some(){ return filename }
567 }
568 None
569}
570
571pub fn find_best_icon_helper(icon_list:Vec<String>, size:i32, scale:i32, theme:IconTheme, dir_list_vector:Vec<DirList>) -> Option<PathBuf> {
573 let mut filename = None;
574 let list = icon_list;
575 let other = list.clone();
576 for icon in list {
578 filename = lookup_icon(icon, size, scale, theme.clone(), dir_list_vector.clone());
579
580 if filename.is_some(){ return filename }
581 }
582
583 let inherits = theme.inherits;
585 if let Some(parents) = inherits {
586 for parent in parents {
587 let theme_file:PathBuf = match get_theme(&parent, dir_list_vector.clone()).map(|theme| theme.index()) {
589 Some(theme_file) => theme_file,
590 None => PathBuf::new(),
591 };
592 let i_theme_file:String = match theme_file.as_path().to_str() {
593 Some(t) => String::from(t),
594 None => String::from(""),
595 };
596 let parent_theme = IconTheme::new(i_theme_file);
597 filename = find_best_icon_helper(other.clone(), size, scale, parent_theme.clone(), dir_list_vector.clone());
598
599 if filename.is_some(){ return filename }
600 }
601 }
602 filename
603}