mirror of https://github.com/qarmin/czkawka
Add sort button (#894)
* Add sort button * Update lofty and gtk-rs * Fix bug with invalid music tags with reference folders * Remove try at * Fix loading of certain directories with disabled loading settings at start * Change FileChooserDialog to FileChooserNative * Any * Copy Clone * Popover sort basic * Builder using * Basic sorting * Fix not working sorting by size * Changelogpull/921/head
parent
8ea9b4b800
commit
f1c6e6da17
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<polygon style="fill:#298983;" points="358.206,345.663 357.688,227.522 292.942,227.806 293.462,345.948 325.975,378.178 "/>
|
||||
<rect x="359.234" y="336.67" transform="matrix(0.7102 0.704 -0.704 0.7102 381.6591 -165.2999)" style="fill:#A5F2E5;" width="64.748" height="88.511"/>
|
||||
<path d="M462.868,366.489l-57.464-56.96c-1.514-1.501-3.559-2.342-5.69-2.342c-0.012,0-0.024,0-0.036,0
|
||||
c-2.144,0.01-4.197,0.871-5.705,2.392L374.3,329.428l-0.484-110.058c-0.019-4.452-3.635-8.047-8.082-8.047c-0.012,0-0.025,0-0.037,0
|
||||
l-80.91,0.356c-2.144,0.01-4.197,0.87-5.705,2.392s-2.352,3.582-2.342,5.726l0.484,110.059l-19.848-19.674
|
||||
c-3.169-3.143-8.287-3.121-11.43,0.051l-56.96,57.464c-3.143,3.169-3.12,8.287,0.051,11.43l32.136,31.855
|
||||
c3.169,3.143,8.287,3.121,11.43-0.051l0,0c3.143-3.169,3.12-8.287-0.051-11.43l-26.396-26.164l45.58-45.983l120.207,119.152
|
||||
l-45.58,45.983l-62.014-61.471c-3.169-3.143-8.287-3.121-11.43,0.051l0,0c-3.143,3.169-3.12,8.287,0.051,11.43l67.754,67.162
|
||||
c1.514,1.501,3.559,2.342,5.69,2.342c0.012,0,0.024,0,0.036,0c0.035,0,0.069-0.005,0.103-0.005c0.031,0,0.061,0.004,0.092,0.004
|
||||
c0.012,0,0.024,0,0.036,0c2.144-0.01,4.197-0.871,5.705-2.392l130.535-131.686C466.061,374.75,466.039,369.632,462.868,366.489z
|
||||
M383.439,435.138l-45.983-45.581l62.308-62.858l45.983,45.58L383.439,435.138z M292.942,227.806l64.745-0.285l0.519,118.141
|
||||
l-32.23,32.515l-32.515-32.23L292.942,227.806z"/>
|
||||
<path style="fill:#62D9C7;" d="M264.35,431.016l62.014,61.471l45.58-45.983L251.738,327.351l-45.58,45.983l26.396,26.164
|
||||
c3.171,3.143,3.193,8.261,0.051,11.43l0,0l20.316,20.138C256.061,427.895,261.179,427.874,264.35,431.016z"/>
|
||||
<polygon style="fill:#298983;" points="153.794,166.333 154.313,284.474 219.058,284.189 218.539,166.049 186.024,133.818 "/>
|
||||
<path d="M235.259,292.201l-0.484-110.058l19.848,19.674c1.576,1.563,3.633,2.342,5.69,2.342c2.08,0,4.16-0.799,5.741-2.392
|
||||
l56.96-57.464c3.143-3.169,3.12-8.287-0.051-11.43l-32.136-31.855c-3.169-3.141-8.287-3.121-11.43,0.051l0,0
|
||||
c-3.143,3.171-3.12,8.287,0.051,11.43l26.396,26.165l-45.58,45.983L140.057,65.492l45.58-45.983L247.65,80.98
|
||||
c3.169,3.143,8.287,3.121,11.43-0.05l0,0c3.143-3.171,3.12-8.287-0.051-11.431L191.275,2.338c-1.614-1.6-3.733-2.375-5.84-2.336
|
||||
c-2.107-0.02-4.219,0.773-5.821,2.387L49.081,134.076c-3.143,3.171-3.12,8.289,0.051,11.431l57.464,56.96
|
||||
c1.514,1.501,3.559,2.342,5.69,2.342c0.012,0,0.024,0,0.036,0c2.144-0.01,4.197-0.871,5.705-2.392l19.673-19.848l0.484,110.058
|
||||
c0.01,2.144,0.871,4.197,2.392,5.705c1.514,1.501,3.559,2.342,5.69,2.342c0.012,0,0.024,0,0.036,0l80.91-0.356
|
||||
C231.675,300.3,235.278,296.665,235.259,292.201z M219.058,284.189l-64.745,0.285l-0.519-118.141l32.23-32.515l32.515,32.23
|
||||
L219.058,284.189z M66.253,139.716l62.308-62.858l45.983,45.581l-62.308,62.858L66.253,139.716z"/>
|
||||
<rect x="88.025" y="86.823" transform="matrix(-0.7102 -0.704 0.704 -0.7102 113.6271 308.9317)" style="fill:#A5F2E5;" width="64.748" height="88.511"/>
|
||||
<path style="fill:#62D9C7;" d="M247.651,80.98l-62.014-61.471l-45.581,45.983l120.207,119.155l45.58-45.983l-26.396-26.165
|
||||
c-3.171-3.143-3.193-8.26-0.051-11.43l-20.316-20.138C255.939,84.1,250.821,84.122,247.651,80.98z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
@ -0,0 +1,46 @@
|
||||
use crate::gui_structs::gui_data::GuiData;
|
||||
use crate::gui_structs::gui_popovers_sort::GuiSortPopovers;
|
||||
use crate::help_functions::PopoverTypes;
|
||||
use crate::notebook_enums::{to_notebook_main_enum, NotebookMainEnum};
|
||||
use crate::notebook_info::NOTEBOOKS_INFO;
|
||||
use gtk4::prelude::*;
|
||||
|
||||
pub fn connect_button_sort(gui_data: &GuiData) {
|
||||
let popovers_sort = gui_data.popovers_sort.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let gc_buttons_sort = gui_data.bottom_buttons.gc_buttons_sort.clone();
|
||||
|
||||
gc_buttons_sort.connect_pressed(move |_, _, _, _| {
|
||||
show_required_popovers(&popovers_sort, &to_notebook_main_enum(notebook_main.current_page().unwrap()));
|
||||
});
|
||||
}
|
||||
|
||||
fn show_required_popovers(popovers_sort: &GuiSortPopovers, current_mode: &NotebookMainEnum) {
|
||||
let buttons_popover_sort_file_name = popovers_sort.buttons_popover_sort_file_name.clone();
|
||||
let buttons_popover_sort_size = popovers_sort.buttons_popover_sort_size.clone();
|
||||
let buttons_popover_sort_folder_name = popovers_sort.buttons_popover_sort_folder_name.clone();
|
||||
let buttons_popover_sort_full_name = popovers_sort.buttons_popover_sort_full_name.clone();
|
||||
let buttons_popover_sort_selection = popovers_sort.buttons_popover_sort_selection.clone();
|
||||
|
||||
let arr = &NOTEBOOKS_INFO[*current_mode as usize].available_modes;
|
||||
|
||||
buttons_popover_sort_full_name.hide();
|
||||
|
||||
if arr.contains(&PopoverTypes::All) {
|
||||
buttons_popover_sort_selection.show();
|
||||
buttons_popover_sort_file_name.show();
|
||||
buttons_popover_sort_folder_name.show();
|
||||
// buttons_popover_sort_full_name.show(); // TODO, this needs to be handled a little different
|
||||
} else {
|
||||
buttons_popover_sort_selection.hide();
|
||||
buttons_popover_sort_file_name.hide();
|
||||
buttons_popover_sort_folder_name.hide();
|
||||
// buttons_popover_sort_full_name.hide();
|
||||
}
|
||||
|
||||
if arr.contains(&PopoverTypes::Size) {
|
||||
buttons_popover_sort_size.show();
|
||||
} else {
|
||||
buttons_popover_sort_size.hide();
|
||||
}
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
use gtk4::prelude::*;
|
||||
use gtk4::{ListStore, TreeIter};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::gui_structs::gui_data::GuiData;
|
||||
use crate::help_functions::*;
|
||||
use crate::notebook_info::NOTEBOOKS_INFO;
|
||||
|
||||
fn popover_sort_general<T>(popover: >k4::Popover, tree_view: >k4::TreeView, column_sort: i32, column_header: i32)
|
||||
where
|
||||
T: Ord + for<'b> glib::value::FromValue<'b> + 'static + Debug,
|
||||
{
|
||||
let model = get_list_store(tree_view);
|
||||
|
||||
if let Some(curr_iter) = model.iter_first() {
|
||||
assert!(model.get::<bool>(&curr_iter, column_header));
|
||||
assert!(model.iter_next(&curr_iter)); // Must be at least one item
|
||||
loop {
|
||||
let mut iters = Vec::new();
|
||||
let mut all_have = false;
|
||||
loop {
|
||||
if model.get::<bool>(&curr_iter, column_header) {
|
||||
assert!(model.iter_next(&curr_iter), "Empty header, this should not happens");
|
||||
break;
|
||||
}
|
||||
iters.push(curr_iter);
|
||||
if !model.iter_next(&curr_iter) {
|
||||
all_have = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if iters.len() == 1 {
|
||||
continue; // Can be equal 1 in reference folders
|
||||
}
|
||||
|
||||
sort_iters::<T>(&model, iters, column_sort);
|
||||
if all_have {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
popover.popdown();
|
||||
}
|
||||
|
||||
fn sort_iters<T>(model: &ListStore, mut iters: Vec<TreeIter>, column_sort: i32)
|
||||
where
|
||||
T: Ord + for<'b> glib::value::FromValue<'b> + 'static + Debug,
|
||||
{
|
||||
assert!(iters.len() >= 2);
|
||||
loop {
|
||||
let mut changed_item = false;
|
||||
for idx in 0..(iters.len() - 1) {
|
||||
if model.get::<T>(&iters[idx], column_sort) > model.get::<T>(&iters[idx + 1], column_sort) {
|
||||
model.swap(&iters[idx], &iters[idx + 1]);
|
||||
iters.swap(idx, idx + 1);
|
||||
changed_item = true;
|
||||
}
|
||||
}
|
||||
if !changed_item {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect_popover_sort(gui_data: &GuiData) {
|
||||
let popover_sort = gui_data.popovers_sort.popover_sort.clone();
|
||||
let buttons_popover_file_name = gui_data.popovers_sort.buttons_popover_sort_file_name.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
|
||||
buttons_popover_file_name.connect_clicked(move |_| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFO[nb_number as usize];
|
||||
|
||||
popover_sort_general::<String>(&popover_sort, tree_view, nb_object.column_name, nb_object.column_header.unwrap());
|
||||
});
|
||||
|
||||
let popover_sort = gui_data.popovers_sort.popover_sort.clone();
|
||||
let buttons_popover_sort_folder_name = gui_data.popovers_sort.buttons_popover_sort_folder_name.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
|
||||
buttons_popover_sort_folder_name.connect_clicked(move |_| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFO[nb_number as usize];
|
||||
|
||||
popover_sort_general::<String>(&popover_sort, tree_view, nb_object.column_path, nb_object.column_header.unwrap());
|
||||
});
|
||||
|
||||
let popover_sort = gui_data.popovers_sort.popover_sort.clone();
|
||||
let buttons_popover_sort_selection = gui_data.popovers_sort.buttons_popover_sort_selection.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
|
||||
buttons_popover_sort_selection.connect_clicked(move |_| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFO[nb_number as usize];
|
||||
|
||||
popover_sort_general::<bool>(&popover_sort, tree_view, nb_object.column_selection, nb_object.column_header.unwrap());
|
||||
});
|
||||
|
||||
let popover_sort = gui_data.popovers_sort.popover_sort.clone();
|
||||
let buttons_popover_sort_size = gui_data.popovers_sort.buttons_popover_sort_size.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
|
||||
buttons_popover_sort_size.connect_clicked(move |_| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFO[nb_number as usize];
|
||||
|
||||
popover_sort_general::<u64>(&popover_sort, tree_view, nb_object.column_size_as_bytes.unwrap(), nb_object.column_header.unwrap());
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::connect_things::connect_popovers_sort::{popover_sort_general, sort_iters};
|
||||
use gtk4::prelude::*;
|
||||
use gtk4::{Popover, TreeView};
|
||||
|
||||
#[gtk4::test]
|
||||
fn test_sort_iters() {
|
||||
let columns_types: &[glib::types::Type] = &[glib::types::Type::U32, glib::types::Type::STRING];
|
||||
let list_store = gtk4::ListStore::new(columns_types);
|
||||
|
||||
let values_to_add: &[&[(u32, &dyn ToValue)]] = &[&[(0, &2), (1, &"AAA")], &[(0, &3), (1, &"CCC")], &[(0, &1), (1, &"BBB")]];
|
||||
for i in values_to_add {
|
||||
list_store.set(&list_store.append(), i);
|
||||
}
|
||||
let mut iters = Vec::new();
|
||||
let iter = list_store.iter_first().unwrap();
|
||||
iters.push(iter);
|
||||
list_store.iter_next(&iter);
|
||||
iters.push(iter);
|
||||
list_store.iter_next(&iter);
|
||||
iters.push(iter);
|
||||
|
||||
sort_iters::<String>(&list_store, iters, 1);
|
||||
|
||||
let expected = [(2, "AAA"), (1, "BBB"), (3, "CCC")];
|
||||
let curr_iter = list_store.iter_first().unwrap();
|
||||
for exp in expected {
|
||||
let real_0 = list_store.get::<u32>(&curr_iter, 0);
|
||||
assert_eq!(real_0, exp.0);
|
||||
let real_1 = list_store.get::<String>(&curr_iter, 1);
|
||||
assert_eq!(real_1, exp.1);
|
||||
list_store.iter_next(&curr_iter);
|
||||
}
|
||||
}
|
||||
|
||||
#[gtk4::test]
|
||||
pub fn test_popover_sort_general_simple() {
|
||||
let columns_types: &[glib::types::Type] = &[glib::types::Type::BOOL, glib::types::Type::STRING];
|
||||
let list_store = gtk4::ListStore::new(columns_types);
|
||||
let tree_view = TreeView::builder().model(&list_store).build();
|
||||
let popover = Popover::new();
|
||||
|
||||
let values_to_add: &[&[(u32, &dyn ToValue)]] = &[&[(0, &true), (1, &"DDD")], &[(0, &false), (1, &"CCC")], &[(0, &false), (1, &"BBB")]];
|
||||
for i in values_to_add {
|
||||
list_store.set(&list_store.append(), i);
|
||||
}
|
||||
|
||||
popover_sort_general::<String>(&popover, &tree_view, 1, 0);
|
||||
|
||||
let expected = ["DDD", "BBB", "CCC"];
|
||||
let curr_iter = list_store.iter_first().unwrap();
|
||||
for exp in expected {
|
||||
let real = list_store.get::<String>(&curr_iter, 1);
|
||||
assert_eq!(real, exp);
|
||||
list_store.iter_next(&curr_iter);
|
||||
}
|
||||
}
|
||||
|
||||
#[gtk4::test]
|
||||
pub fn test_popover_sort_general() {
|
||||
let columns_types: &[glib::types::Type] = &[glib::types::Type::BOOL, glib::types::Type::STRING];
|
||||
let list_store = gtk4::ListStore::new(columns_types);
|
||||
let tree_view = TreeView::builder().model(&list_store).build();
|
||||
let popover = Popover::new();
|
||||
|
||||
let values_to_add: &[&[(u32, &dyn ToValue)]] = &[
|
||||
&[(0, &true), (1, &"AAA")],
|
||||
&[(0, &false), (1, &"CCC")],
|
||||
&[(0, &false), (1, &"BBB")],
|
||||
&[(0, &true), (1, &"TTT")],
|
||||
&[(0, &false), (1, &"PPP")],
|
||||
&[(0, &false), (1, &"AAA")],
|
||||
&[(0, &true), (1, &"RRR")],
|
||||
&[(0, &false), (1, &"WWW")],
|
||||
&[(0, &false), (1, &"ZZZ")],
|
||||
];
|
||||
for i in values_to_add {
|
||||
list_store.set(&list_store.append(), i);
|
||||
}
|
||||
|
||||
popover_sort_general::<String>(&popover, &tree_view, 1, 0);
|
||||
|
||||
let expected = ["AAA", "BBB", "CCC", "TTT", "AAA", "PPP", "RRR", "WWW", "ZZZ"];
|
||||
let curr_iter = list_store.iter_first().unwrap();
|
||||
for exp in expected {
|
||||
let real = list_store.get::<String>(&curr_iter, 1);
|
||||
assert_eq!(real, exp);
|
||||
list_store.iter_next(&curr_iter);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
use gtk4::prelude::*;
|
||||
use gtk4::Builder;
|
||||
|
||||
use crate::flg;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GuiSortPopovers {
|
||||
pub buttons_popover_sort_file_name: gtk4::Button,
|
||||
pub buttons_popover_sort_folder_name: gtk4::Button,
|
||||
pub buttons_popover_sort_full_name: gtk4::Button,
|
||||
pub buttons_popover_sort_size: gtk4::Button,
|
||||
pub buttons_popover_sort_selection: gtk4::Button,
|
||||
|
||||
pub popover_sort: gtk4::Popover,
|
||||
}
|
||||
|
||||
impl GuiSortPopovers {
|
||||
pub fn create_from_builder() -> Self {
|
||||
let glade_src = include_str!("../../ui/popover_sort.ui").to_string();
|
||||
let builder = Builder::from_string(glade_src.as_str());
|
||||
|
||||
let buttons_popover_sort_file_name: gtk4::Button = builder.object("buttons_popover_sort_file_name").unwrap();
|
||||
let buttons_popover_sort_folder_name: gtk4::Button = builder.object("buttons_popover_sort_folder_name").unwrap();
|
||||
let buttons_popover_sort_full_name: gtk4::Button = builder.object("buttons_popover_sort_full_name").unwrap();
|
||||
let buttons_popover_sort_size: gtk4::Button = builder.object("buttons_popover_sort_size").unwrap();
|
||||
let buttons_popover_sort_selection: gtk4::Button = builder.object("buttons_popover_sort_selection").unwrap();
|
||||
|
||||
let popover_sort: gtk4::Popover = builder.object("popover_sort").unwrap();
|
||||
|
||||
Self {
|
||||
buttons_popover_sort_file_name,
|
||||
buttons_popover_sort_folder_name,
|
||||
buttons_popover_sort_full_name,
|
||||
buttons_popover_sort_size,
|
||||
buttons_popover_sort_selection,
|
||||
popover_sort,
|
||||
}
|
||||
}
|
||||
pub fn update_language(&self) {
|
||||
self.buttons_popover_sort_file_name.set_label(&flg!("popover_sort_file_name"));
|
||||
self.buttons_popover_sort_folder_name.set_label(&flg!("popover_sort_folder_name"));
|
||||
self.buttons_popover_sort_full_name.set_label(&flg!("popover_sort_full_name"));
|
||||
self.buttons_popover_sort_size.set_label(&flg!("popover_sort_size"));
|
||||
self.buttons_popover_sort_selection.set_label(&flg!("popover_sort_selection"));
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- Created with Cambalache 0.10.3 -->
|
||||
<interface>
|
||||
<!-- interface-name popover_sort.ui -->
|
||||
<requires lib="gtk" version="4.6"/>
|
||||
<object class="GtkPopover" id="popover_sort">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="buttons_popover_sort_file_name">
|
||||
<property name="focusable">1</property>
|
||||
<property name="label">File name</property>
|
||||
<property name="receives-default">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="buttons_popover_sort_folder_name">
|
||||
<property name="focusable">1</property>
|
||||
<property name="label">Folder name</property>
|
||||
<property name="receives-default">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="buttons_popover_sort_full_name">
|
||||
<property name="focusable">1</property>
|
||||
<property name="label">Full name</property>
|
||||
<property name="receives-default">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="buttons_popover_sort_size">
|
||||
<property name="focusable">1</property>
|
||||
<property name="label">Size</property>
|
||||
<property name="receives-default">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="buttons_popover_sort_selection">
|
||||
<property name="focusable">1</property>
|
||||
<property name="label">Selection</property>
|
||||
<property name="receives-default">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
<property name="position">top</property>
|
||||
</object>
|
||||
</interface>
|
Loading…
Reference in New Issue