,
+}
+
+
#[derive(Deserialize, Debug, Serialize, Default)]
#[serde(default)]
/// Main preferences struct, holding all sub-preferences.
@@ -191,8 +203,8 @@ pub struct Prefs {
pub view_prefs: ViewPrefs,
pub behavior_prefs: BehaviorPrefs,
#[cfg(feature = "notify")]
- pub notify_prefs: NotifyPrefs,
- // TODO: HotKeys?
+ pub notify_prefs: NotifyPrefs,
+ pub hotkey_prefs: HotkeyPrefs,
}
impl Prefs {
@@ -223,6 +235,7 @@ impl Prefs {
}
+ // TODO: unused
/// Reload the current preferences from the config file.
pub fn reload_config(&mut self) -> Result<()> {
debug!("Reloading config...");
@@ -241,8 +254,10 @@ impl Prefs {
debug!("Storing config in {:?}", config_path);
- let mut f = File::create(config_path)?;
- f.write_all(self.to_str().as_bytes())?;
+ let mut f = File::create(config_path)
+ .chain_err(|| "Could not open/create config file for writing")?;
+ f.write_all(self.to_str().as_bytes())
+ .chain_err(|| "Could not write to config file")?;
return Ok(());
}
diff --git a/src/support_cmd.rs b/src/support_cmd.rs
index ba3122c4d..36414f9ba 100644
--- a/src/support_cmd.rs
+++ b/src/support_cmd.rs
@@ -1,5 +1,6 @@
//! Helper functions for invoking system commands.
+
use errors::*;
use glib;
use prefs::Prefs;
diff --git a/src/ui_hotkey_dialog.rs b/src/ui_hotkey_dialog.rs
new file mode 100644
index 000000000..f1e858f86
--- /dev/null
+++ b/src/ui_hotkey_dialog.rs
@@ -0,0 +1,134 @@
+//! The ui hotkey preferences dialog.
+//!
+//! Usually run from the preferences window.
+
+
+use errors::*;
+use gdk::DeviceExt;
+use gdk;
+use gdk_sys;
+use glib::translate::*;
+use gtk::prelude::*;
+use gtk;
+use gtk_sys;
+use libc::c_uint;
+use std;
+
+
+
+/// Hotkey dialog struct holding the relevant gtk widgets.
+pub struct HotkeyDialog {
+ hotkey_dialog: gtk::Dialog,
+ // instruction_label: gtk::Label, // not needed
+ key_pressed_label: gtk::Label,
+}
+
+impl HotkeyDialog {
+ /// Creates a new hotkey dialog.
+ pub fn new(parent: &P, hotkey: String) -> HotkeyDialog
+ where P: IsA
+ {
+ let builder =
+ gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"),
+ "/data/ui/hotkey-dialog.glade")));
+
+ let hotkey_dialog: gtk::Dialog = builder.get_object("hotkey_dialog")
+ .unwrap();
+ let instruction_label: gtk::Label = builder.get_object("instruction_label")
+ .unwrap();
+ let key_pressed_label: gtk::Label = builder.get_object("key_pressed_label")
+ .unwrap();
+
+ hotkey_dialog.set_title(format!("Set {} HotKey", hotkey).as_str());
+ instruction_label.set_markup(format!("Press new HotKey for {}",
+ hotkey)
+ .as_str());
+
+ hotkey_dialog.set_transient_for(parent);
+
+ {
+ let key_pressed_label = key_pressed_label.clone();
+ hotkey_dialog.connect_key_press_event(move |_, e| {
+ let mut state = e.get_state();
+
+ unsafe {
+ let mut keyval: c_uint = 0;
+ let mut consumed: gdk_sys::GdkModifierType =
+ gdk_sys::GdkModifierType::empty();
+ gdk_sys::gdk_keymap_translate_keyboard_state(
+ gdk_sys::gdk_keymap_get_default(),
+ e.get_hardware_keycode() as u32,
+ state.to_glib(),
+ e.get_group() as i32,
+ &mut keyval as *mut c_uint,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ &mut consumed as *mut gdk_sys::GdkModifierType);
+
+ let consumed: gdk::ModifierType = from_glib(!consumed);
+ state = state & consumed;
+ state = state & gtk::accelerator_get_default_mod_mask();
+
+ let key_text = gtk::accelerator_name(keyval, state);
+ key_pressed_label.set_text(key_text
+ .unwrap_or(String::from("(None)"))
+ .as_str());
+ };
+ return Inhibit(false);
+ });
+ }
+
+
+
+ hotkey_dialog.connect_key_release_event(move |w, _| {
+ w.response(gtk_sys::GtkResponseType::Ok as i32);
+ return Inhibit(false);
+ });
+
+ return HotkeyDialog {
+ hotkey_dialog,
+ key_pressed_label,
+ };
+ }
+
+ /// Runs the hotkey dialog and returns a String representing the hotkey
+ /// that has been pressed.
+ pub fn run(&self) -> Result {
+ self.hotkey_dialog.show_now();
+ let device = gtk::get_current_event_device()
+ .ok_or("Could not get current device")?;
+ let window = self.hotkey_dialog
+ .get_window()
+ .ok_or("Could not get window")?;
+
+ let m_grab_status =
+ device.grab(&window,
+ gdk::GrabOwnership::Application,
+ true,
+ gdk::KEY_PRESS_MASK,
+ None,
+ gdk_sys::GDK_CURRENT_TIME as u32);
+
+ if m_grab_status != gdk::GrabStatus::Success {
+ bail!("Could not grab the keyboard");
+ }
+
+ let resp = self.hotkey_dialog.run();
+ device.ungrab(gdk_sys::GDK_CURRENT_TIME as u32);
+
+ if resp != gtk::ResponseType::Ok.into() {
+ bail!(ErrorKind::GtkResponseCancel(String::from("not assigning hotkey")));
+ }
+
+ return Ok(self.key_pressed_label
+ .get_text()
+ .ok_or("Could not get text")?);
+ }
+}
+
+
+impl Drop for HotkeyDialog {
+ fn drop(&mut self) {
+ self.hotkey_dialog.destroy();
+ }
+}
diff --git a/src/ui_popup_menu.rs b/src/ui_popup_menu.rs
index e4611fff7..dc25cc69a 100644
--- a/src/ui_popup_menu.rs
+++ b/src/ui_popup_menu.rs
@@ -74,7 +74,8 @@ pub fn init_popup_menu(appstate: Rc) {
let apps = appstate.clone();
let mixer_item = &appstate.gui.popup_menu.mixer_item;
mixer_item.connect_activate(move |_| {
- try_w!(execute_vol_control_command(&apps.prefs.borrow()));
+ let _ = result_warn!(execute_vol_control_command(&apps.prefs.borrow()),
+ Some(&apps.gui.popup_menu.menu_window));
});
}
diff --git a/src/ui_popup_window.rs b/src/ui_popup_window.rs
index 57136b9c8..1fc0af032 100644
--- a/src/ui_popup_window.rs
+++ b/src/ui_popup_window.rs
@@ -205,7 +205,8 @@ pub fn init_popup_window(appstate: Rc) {
.popup_window
.popup_window
.hide();
- try_w!(execute_vol_control_command(&apps.prefs.borrow()));
+ let _ = result_warn!(execute_vol_control_command(&apps.prefs.borrow()),
+ Some(&apps.gui.popup_menu.menu_window));
});
}
}
diff --git a/src/ui_prefs_dialog.rs b/src/ui_prefs_dialog.rs
index 942fc6d77..88eb15e72 100644
--- a/src/ui_prefs_dialog.rs
+++ b/src/ui_prefs_dialog.rs
@@ -4,13 +4,17 @@
use app_state::*;
use audio::{AudioUser, AudioSignal};
+use errors::*;
use gdk;
use gtk::ResponseType;
use gtk::prelude::*;
use gtk;
use prefs::*;
+use std::ascii::AsciiExt;
+use std::cell::RefCell;
use std::rc::Rc;
use support_audio::*;
+use ui_hotkey_dialog::HotkeyDialog;
@@ -51,6 +55,21 @@ pub struct PrefsDialog {
noti_popup_check: gtk::CheckButton,
#[cfg(feature = "notify")]
noti_ext_check: gtk::CheckButton,
+ #[cfg(feature = "notify")]
+ noti_hotkey_check: gtk::CheckButton,
+
+ /* HotkeyPrefs */
+ hotkeys_enable_check: gtk::CheckButton,
+ hotkeys_mute_label: gtk::Label,
+ hotkeys_up_label: gtk::Label,
+ hotkeys_down_label: gtk::Label,
+
+ /* Hotkey stuff (not prefs) */
+ hotkeys_mute_eventbox: gtk::EventBox,
+ hotkeys_up_eventbox: gtk::EventBox,
+ hotkeys_down_eventbox: gtk::EventBox,
+
+ hotkey_dialog: RefCell