app: enhance element uri handler

Open the given dialog according to the
type of property or uri handler supported
This commit is contained in:
Stéphane Cerveau 2024-10-22 17:35:08 +02:00
parent 7bb0d73e40
commit 8905ff6592
4 changed files with 179 additions and 40 deletions

View file

@ -448,20 +448,24 @@ impl GPSApp {
let app_weak = self.downgrade(); let app_weak = self.downgrade();
self.connect_app_menu_action("open", move |_, _| { self.connect_app_menu_action("open", move |_, _| {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
GPSUI::dialog::get_file_from_dialog(&app, false, move |app, filename| { GPSUI::dialog::get_file_from_dialog(
app.load_graph(&filename, false) &app,
.unwrap_or_else(|_| GPS_ERROR!("Unable to open file {}", filename)); GPSUI::dialog::FileDialogType::Open,
}); move |app, filename| {
app.load_graph(&filename, false)
.unwrap_or_else(|_| GPS_ERROR!("Unable to open file {}", filename));
},
);
}); });
let app_weak = self.downgrade(); let app_weak = self.downgrade();
self.connect_app_menu_action("open_pipeline", move |_, _| { self.connect_app_menu_action("open_pipeline", move |_, _| {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
GPSUI::dialog::create_input_dialog( GPSUI::dialog::create_input_dialog(
&app,
"Enter pipeline description with gst-launch format", "Enter pipeline description with gst-launch format",
"description", "description",
&Settings::recent_pipeline_description(), &Settings::recent_pipeline_description(),
&app,
move |app, pipeline_desc| { move |app, pipeline_desc| {
app.load_pipeline(&pipeline_desc).unwrap_or_else(|_| { app.load_pipeline(&pipeline_desc).unwrap_or_else(|_| {
GPS_ERROR!("Unable to open pipeline description {}", pipeline_desc) GPS_ERROR!("Unable to open pipeline description {}", pipeline_desc)
@ -475,12 +479,16 @@ impl GPSApp {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
let gt = graphbook::current_graphtab(&app); let gt = graphbook::current_graphtab(&app);
if gt.undefined() { if gt.undefined() {
GPSUI::dialog::get_file_from_dialog(&app, true, move |app, filename| { GPSUI::dialog::get_file_from_dialog(
GPS_DEBUG!("Save file {}", filename); &app,
app.save_graph(&filename) GPSUI::dialog::FileDialogType::Save,
.unwrap_or_else(|_| GPS_ERROR!("Unable to save file to {}", filename)); move |app, filename| {
graphbook::current_graphtab_set_filename(&app, filename.as_str()); GPS_DEBUG!("Save file {}", filename);
}); app.save_graph(&filename)
.unwrap_or_else(|_| GPS_ERROR!("Unable to save file to {}", filename));
graphbook::current_graphtab_set_filename(&app, filename.as_str());
},
);
} else if gt.modified() { } else if gt.modified() {
let filename = gt.filename(); let filename = gt.filename();
app.save_graph(&filename) app.save_graph(&filename)
@ -492,12 +500,16 @@ impl GPSApp {
let app_weak = self.downgrade(); let app_weak = self.downgrade();
self.connect_app_menu_action("save_as", move |_, _| { self.connect_app_menu_action("save_as", move |_, _| {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
GPSUI::dialog::get_file_from_dialog(&app, true, move |app, filename| { GPSUI::dialog::get_file_from_dialog(
GPS_DEBUG!("Save file {}", filename); &app,
app.save_graph(&filename) GPSUI::dialog::FileDialogType::Save,
.unwrap_or_else(|_| GPS_ERROR!("Unable to save file to {}", filename)); move |app, filename| {
graphbook::current_graphtab_set_filename(&app, filename.as_str()); GPS_DEBUG!("Save file {}", filename);
}); app.save_graph(&filename)
.unwrap_or_else(|_| GPS_ERROR!("Unable to save file to {}", filename));
graphbook::current_graphtab_set_filename(&app, filename.as_str());
},
);
}); });
let app_weak = self.downgrade(); let app_weak = self.downgrade();
@ -589,15 +601,78 @@ impl GPSApp {
.graphview() .graphview()
.create_node(element_name, GPS::ElementInfo::element_type(element_name)); .create_node(element_name, GPS::ElementInfo::element_type(element_name));
let node_id = node.id(); let node_id = node.id();
if GPS::ElementInfo::element_is_uri_src_handler(element_name) { if let Some((prop_name, file_chooser)) =
GPSUI::dialog::get_file_from_dialog(self, false, move |app, filename| { GPS::ElementInfo::element_is_uri_src_handler(element_name)
GPS_DEBUG!("Open file {}", filename); {
let mut properties: HashMap<String, String> = HashMap::new(); if file_chooser {
properties.insert(String::from("location"), filename); GPSUI::dialog::get_file_from_dialog(
if let Some(node) = graphbook::current_graphtab(&app).graphview().node(node_id) { self,
node.update_properties(&properties); GPSUI::dialog::FileDialogType::OpenAll,
} move |app, filename| {
}); GPS_DEBUG!("Open file {}", filename);
let mut properties: HashMap<String, String> = HashMap::new();
properties.insert(prop_name.clone(), filename);
if let Some(node) =
graphbook::current_graphtab(&app).graphview().node(node_id)
{
node.update_properties(&properties);
}
},
);
} else {
GPSUI::dialog::create_input_dialog(
self,
"Enter uri",
"uri",
"",
move |app, uri| {
GPS_DEBUG!("Open uri {}", uri);
let mut properties: HashMap<String, String> = HashMap::new();
properties.insert(String::from("uri"), uri);
if let Some(node) =
graphbook::current_graphtab(&app).graphview().node(node_id)
{
node.update_properties(&properties);
}
},
);
}
} else if let Some((prop_name, file_chooser)) =
GPS::ElementInfo::element_is_uri_sink_handler(element_name)
{
if file_chooser {
GPSUI::dialog::get_file_from_dialog(
self,
GPSUI::dialog::FileDialogType::SaveAll,
move |app, filename| {
GPS_DEBUG!("Save file {}", filename);
let mut properties: HashMap<String, String> = HashMap::new();
properties.insert(prop_name.clone(), filename);
if let Some(node) =
graphbook::current_graphtab(&app).graphview().node(node_id)
{
node.update_properties(&properties);
}
},
);
} else {
GPSUI::dialog::create_input_dialog(
self,
"Enter uri",
"uri",
"",
move |app, uri| {
GPS_DEBUG!("Save uri {}", uri);
let mut properties: HashMap<String, String> = HashMap::new();
properties.insert(String::from("uri"), uri);
if let Some(node) =
graphbook::current_graphtab(&app).graphview().node(node_id)
{
node.update_properties(&properties);
}
},
);
}
} }
graphbook::current_graphtab(self).graphview().add_node(node); graphbook::current_graphtab(self).graphview().add_node(node);
for input in inputs { for input in inputs {

View file

@ -237,9 +237,17 @@ impl ElementInfo {
ElementInfo::element_properties(&element) ElementInfo::element_properties(&element)
} }
pub fn element_is_uri_src_handler(element_name: &str) -> bool { pub fn element_has_property(element: &gst::Element, property_name: &str) -> bool {
let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature"); let properties = ElementInfo::element_properties(element)
.unwrap_or_else(|_| panic!("Couldn't get properties for {}", element.name()));
properties.keys().any(|name| name == property_name)
}
pub fn element_is_uri_src_handler(element_name: &str) -> Option<(String, bool)> {
let feature: gst::PluginFeature =
ElementInfo::element_feature(element_name).expect("Unable to get feature");
let mut file_chooser = false;
let factory = feature let factory = feature
.downcast::<gst::ElementFactory>() .downcast::<gst::ElementFactory>()
.expect("Unable to get the factory from the feature"); .expect("Unable to get the factory from the feature");
@ -247,10 +255,52 @@ impl ElementInfo {
.create() .create()
.build() .build()
.expect("Unable to create an element from the feature"); .expect("Unable to create an element from the feature");
match element.dynamic_cast::<gst::URIHandler>() { if let Ok(uri_handler) = element.clone().dynamic_cast::<gst::URIHandler>() {
Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src, let search_strings = ["file", "pushfile"];
Err(_e) => false, file_chooser = search_strings
.iter()
.any(|s| uri_handler.protocols().contains(&glib::GString::from(*s)));
} }
if element.is::<gst::Bin>() || ElementInfo::element_type(element_name) == NodeType::Source {
if ElementInfo::element_has_property(&element, "uri") {
return Some((String::from("uri"), file_chooser));
}
if ElementInfo::element_has_property(&element, "location") {
return Some((String::from("location"), file_chooser));
}
}
None
}
pub fn element_is_uri_sink_handler(element_name: &str) -> Option<(String, bool)> {
let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature");
let mut file_chooser = false;
let factory = feature
.downcast::<gst::ElementFactory>()
.expect("Unable to get the factory from the feature");
let element = factory
.create()
.build()
.expect("Unable to create an element from the feature");
if let Ok(uri_handler) = element.clone().dynamic_cast::<gst::URIHandler>() {
file_chooser = uri_handler
.protocols()
.contains(&glib::GString::from("file"))
}
if ElementInfo::element_type(element_name) == NodeType::Sink {
if ElementInfo::element_has_property(&element, "uri") {
return Some((String::from("uri"), file_chooser));
}
if ElementInfo::element_has_property(&element, "location") {
return Some((String::from("location"), file_chooser));
}
}
None
} }
pub fn element_supports_new_pad_request( pub fn element_supports_new_pad_request(

View file

@ -534,10 +534,10 @@ pub fn create_graphtab(app: &GPSApp, id: u32, name: Option<&str>) {
GPS_TRACE!("link double clicked id={}", link_id); GPS_TRACE!("link double clicked id={}", link_id);
let link = current_graphtab(&app).graphview().link(link_id).unwrap(); let link = current_graphtab(&app).graphview().link(link_id).unwrap();
GPSUI::dialog::create_input_dialog( GPSUI::dialog::create_input_dialog(
&app,
"Enter caps filter description", "Enter caps filter description",
"description", "description",
&link.name(), &link.name(),
&app,
move |app, link_desc| { move |app, link_desc| {
current_graphtab(&app) current_graphtab(&app)
.graphview() .graphview()

View file

@ -12,6 +12,14 @@ use gtk::glib;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{ApplicationWindow, FileChooserAction, FileChooserDialog, FileFilter, ResponseType}; use gtk::{ApplicationWindow, FileChooserAction, FileChooserDialog, FileFilter, ResponseType};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FileDialogType {
Save,
Open,
OpenAll,
SaveAll,
}
pub fn create_dialog<F: Fn(GPSApp, gtk::Dialog) + 'static>( pub fn create_dialog<F: Fn(GPSApp, gtk::Dialog) + 'static>(
name: &str, name: &str,
app: &GPSApp, app: &GPSApp,
@ -50,10 +58,10 @@ pub fn create_dialog<F: Fn(GPSApp, gtk::Dialog) + 'static>(
} }
pub fn create_input_dialog<F: Fn(GPSApp, String) + 'static>( pub fn create_input_dialog<F: Fn(GPSApp, String) + 'static>(
app: &GPSApp,
dialog_name: &str, dialog_name: &str,
input_name: &str, input_name: &str,
default_value: &str, default_value: &str,
app: &GPSApp,
f: F, f: F,
) { ) {
let dialog = gtk::Dialog::with_buttons( let dialog = gtk::Dialog::with_buttons(
@ -104,12 +112,16 @@ pub fn create_input_dialog<F: Fn(GPSApp, String) + 'static>(
dialog.show(); dialog.show();
} }
pub fn get_file_from_dialog<F: Fn(GPSApp, String) + 'static>(app: &GPSApp, save: bool, f: F) { pub fn get_file_from_dialog<F: Fn(GPSApp, String) + 'static>(
app: &GPSApp,
dlg_type: FileDialogType,
f: F,
) {
let mut message = "Open file"; let mut message = "Open file";
let mut ok_button = "Open"; let mut ok_button = "Open";
let cancel_button = "Cancel"; let cancel_button = "Cancel";
let mut action = FileChooserAction::Open; let mut action = FileChooserAction::Open;
if save { if dlg_type == FileDialogType::Save || dlg_type == FileDialogType::SaveAll {
message = "Save file"; message = "Save file";
ok_button = "Save"; ok_button = "Save";
action = FileChooserAction::Save; action = FileChooserAction::Save;
@ -127,13 +139,15 @@ pub fn get_file_from_dialog<F: Fn(GPSApp, String) + 'static>(app: &GPSApp, save:
(cancel_button, ResponseType::Cancel), (cancel_button, ResponseType::Cancel),
], ],
); );
if save { if dlg_type == FileDialogType::Save {
file_chooser.set_current_name("untitled.gps"); file_chooser.set_current_name("untitled.gps");
} }
let filter = FileFilter::new(); if dlg_type == FileDialogType::Open {
filter.add_pattern("*.gps"); let filter = FileFilter::new();
filter.set_name(Some("GPS Files (*.gps)")); filter.add_pattern("*.gps");
file_chooser.add_filter(&filter); filter.set_name(Some("GPS Files (*.gps)"));
file_chooser.add_filter(&filter);
}
let app_weak = app.downgrade(); let app_weak = app.downgrade();
file_chooser.connect_response(move |d: &FileChooserDialog, response: ResponseType| { file_chooser.connect_response(move |d: &FileChooserDialog, response: ResponseType| {