From 797de26ec27d1ea91e144f9e677b759c9b22df39 Mon Sep 17 00:00:00 2001 From: daa84 Date: Fri, 17 Nov 2017 18:40:09 +0300 Subject: [PATCH 01/59] Add ext_cmd event processing Update event processing code --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 1 + src/nvim/cmd_line.rs | 30 +++++++++++++++++++ src/nvim/handler.rs | 2 +- src/nvim/mod.rs | 37 ++++++++++++++--------- src/nvim/redraw_handler.rs | 56 ++++++++++++++++++++++++++--------- src/shell.rs | 60 +++++++++++++++----------------------- src/ui_model/cell.rs | 35 ++++++++++++++++++++++ 9 files changed, 158 insertions(+), 65 deletions(-) create mode 100644 src/nvim/cmd_line.rs diff --git a/Cargo.lock b/Cargo.lock index ea6948b..1cd681f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "pangocairo 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", + "rmpv 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 0db4b11..736920d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ phf = "0.7" log = "0.3" env_logger = "0.4" htmlescape = "0.3" +rmpv = "0.4" serde = "1.0" serde_derive = "1.0" diff --git a/src/main.rs b/src/main.rs index 6dcf074..8c1b907 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ extern crate phf; extern crate log; extern crate env_logger; extern crate htmlescape; +extern crate rmpv; extern crate serde; #[macro_use] diff --git a/src/nvim/cmd_line.rs b/src/nvim/cmd_line.rs new file mode 100644 index 0000000..5b539a1 --- /dev/null +++ b/src/nvim/cmd_line.rs @@ -0,0 +1,30 @@ +use ui_model; + +pub struct CmdLine { + content: Vec<(ui_model::Attrs, String)>, + pos: u64, + firstc: String, + prompt: String, + indent: u64, + level: u64, +} + +impl CmdLine { + pub fn new( + content: Vec<(ui_model::Attrs, String)>, + pos: u64, + firstc: String, + prompt: String, + indent: u64, + level: u64, + ) -> Self { + CmdLine { + content, + pos, + firstc, + prompt, + indent, + level, + } + } +} diff --git a/src/nvim/handler.rs b/src/nvim/handler.rs index 4128617..34e1c44 100644 --- a/src/nvim/handler.rs +++ b/src/nvim/handler.rs @@ -38,7 +38,7 @@ impl NvimHandler { _ => vec![], }; let call_reapint_mode = - redraw_handler::call(ui, &ev_name, &args)?; + redraw_handler::call(ui, &ev_name, args)?; repaint_mode = repaint_mode.join(call_reapint_mode); } } else { diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index 504f8fe..d7f477c 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -4,11 +4,13 @@ mod handler; mod mode_info; mod redraw_handler; mod repaint_mode; +mod cmd_line; pub use self::redraw_handler::{RedrawEvents, GuiApi}; pub use self::repaint_mode::RepaintMode; pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef}; pub use self::mode_info::{ModeInfo, CursorShape}; +pub use self::cmd_line::{CmdLine}; use std::error; use std::fmt; @@ -136,20 +138,28 @@ pub fn post_start_init( cols: u64, rows: u64, ) -> result::Result<(), NvimInitError> { - let mut opts = UiAttachOptions::new(); - opts.set_popupmenu_external(true); - opts.set_tabline_external(true); - nvim.borrow().unwrap().ui_attach(cols, rows, &opts).map_err( - NvimInitError::new_post_init, - )?; - nvim.borrow().unwrap().command("runtime! ginit.vim").map_err( - NvimInitError::new_post_init, - )?; + nvim.borrow() + .unwrap() + .ui_attach( + cols, + rows, + UiAttachOptions::new() + .set_popupmenu_external(true) + .set_tabline_external(true) + .set_cmdline_external(true), + ) + .map_err(NvimInitError::new_post_init)?; + + nvim.borrow() + .unwrap() + .command("runtime! ginit.vim") + .map_err(NvimInitError::new_post_init)?; if let Some(path) = open_path { - nvim.borrow().unwrap().command(&format!("e {}", path)).map_err( - NvimInitError::new_post_init, - )?; + nvim.borrow() + .unwrap() + .command(&format!("e {}", path)) + .map_err(NvimInitError::new_post_init)?; } Ok(()) @@ -159,7 +169,7 @@ pub fn post_start_init( pub trait ErrorReport { fn report_err(&self, nvim: &mut NeovimApi); - fn ok_and_report(self, nvim: &mut NeovimApi) -> Option; + fn ok_and_report(self, nvim: &mut NeovimApi) -> Option; } impl ErrorReport for result::Result { @@ -175,4 +185,3 @@ impl ErrorReport for result::Result { self.ok() } } - diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 59f3390..c1c1e83 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -1,4 +1,5 @@ use std::result; +use std::collections::HashMap; use neovim_lib::{Value, UiOption}; use neovim_lib::neovim_api::Tabpage; @@ -6,6 +7,7 @@ use neovim_lib::neovim_api::Tabpage; use shell; use value::ValueMapExt; +use rmpv; use super::repaint_mode::RepaintMode; use super::mode_info::ModeInfo; @@ -21,7 +23,7 @@ pub trait RedrawEvents { fn on_redraw(&mut self, mode: &RepaintMode); - fn on_highlight_set(&mut self, attrs: &[(Value, Value)]) -> RepaintMode; + fn on_highlight_set(&mut self, attrs: HashMap) -> RepaintMode; fn on_eol_clear(&mut self) -> RepaintMode; @@ -64,6 +66,16 @@ pub trait RedrawEvents { cursor_style_enabled: bool, mode_info: Vec, ) -> RepaintMode; + + fn cmdline_show( + &mut self, + content: Vec<(HashMap, String)>, + pos: u64, + firstc: String, + prompt: String, + indent: u64, + level: u64, + ) -> RepaintMode; } pub trait GuiApi { @@ -103,6 +115,31 @@ macro_rules! map_array { ); } +macro_rules! try_arg { + ($value:expr, bool) => (try_bool!($value)); + ($value:expr, uint) => (try_uint!($value)); + ($value:expr, str) => ( + match $value { + Value::String(s) => { + if let Some(s) = s.into_str() { + Ok(s) + } else { + Err("Can't convert to utf8 string".to_owned()) + } + } + _ => Err("Can't convert to string".to_owned()), + }?); + ($value:expr, ext) => (rmpv::ext::from_value($value).map_err(|e| e.to_string())?); +} + +macro_rules! call { + ($s:ident -> $c:ident ($args:ident : $($arg_type:ident),+ )) => ( + { + let mut iter = $args.into_iter(); + $s.$c($( try_arg!(iter.next().unwrap(), $arg_type)),+ ) + } + ) +} pub fn call_gui_event( ui: &mut shell::State, @@ -140,7 +177,7 @@ pub fn call_gui_event( pub fn call( ui: &mut shell::State, method: &str, - args: &[Value], + args: Vec, ) -> result::Result { let repaint_mode = match method { "cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])), @@ -148,21 +185,12 @@ pub fn call( "clear" => ui.on_clear(), "resize" => ui.on_resize(try_uint!(args[0]), try_uint!(args[1])), "highlight_set" => { - if let Value::Map(ref attrs) = args[0] { - ui.on_highlight_set(attrs); - } else { - panic!("Supports only map value as argument"); - } + call!(ui->on_highlight_set(args: ext)); RepaintMode::Nothing } "eol_clear" => ui.on_eol_clear(), "set_scroll_region" => { - ui.on_set_scroll_region( - try_uint!(args[0]), - try_uint!(args[1]), - try_uint!(args[2]), - try_uint!(args[3]), - ); + call!(ui->on_set_scroll_region(args: uint, uint, uint, uint)); RepaintMode::Nothing } "scroll" => ui.on_scroll(try_int!(args[0])), @@ -221,6 +249,7 @@ pub fn call( )?; ui.mode_info_set(try_bool!(args[0]), mode_info) } + "cmdline_show" => call!(ui->cmdline_show(args: ext, uint, str, str, uint, uint)), _ => { println!("Event {}({:?})", method, args); RepaintMode::Nothing @@ -229,4 +258,3 @@ pub fn call( Ok(repaint_mode) } - diff --git a/src/shell.rs b/src/shell.rs index 6726608..a10a2ae 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -3,6 +3,7 @@ use std::rc::Rc; use std::sync::{Arc, Condvar, Mutex}; use std::ops::Deref; use std::thread; +use std::collections::HashMap; use cairo; use pangocairo::CairoContextExt; @@ -20,8 +21,10 @@ use neovim_lib::neovim_api::Tabpage; use settings::{Settings, FontSource}; use ui_model::{UiModel, Attrs, ModelRect}; use color::{ColorModel, Color, COLOR_BLACK, COLOR_WHITE, COLOR_RED}; -use nvim; -use nvim::{RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient, NeovimRef, NeovimClientAsync}; + +use nvim::{self, RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient, NeovimRef, + NeovimClientAsync, CmdLine}; + use input; use input::keyval_to_input_string; use cursor::Cursor; @@ -599,7 +602,8 @@ impl Deref for Shell { fn gtk_focus_in(state: &mut State) -> Inhibit { if let Some(mut nvim) = state.nvim() { - nvim.command("if exists('#FocusGained') | doautocmd FocusGained | endif").report_err(&mut *nvim); + nvim.command("if exists('#FocusGained') | doautocmd FocusGained | endif") + .report_err(&mut *nvim); } state.im_context.focus_in(); @@ -611,7 +615,8 @@ fn gtk_focus_in(state: &mut State) -> Inhibit { fn gtk_focus_out(state: &mut State) -> Inhibit { if let Some(mut nvim) = state.nvim() { - nvim.command("if exists('#FocusLost') | doautocmd FocusLost | endif").report_err(&mut *nvim); + nvim.command("if exists('#FocusLost') | doautocmd FocusLost | endif") + .report_err(&mut *nvim); } state.im_context.focus_out(); @@ -959,38 +964,8 @@ impl RedrawEvents for State { RepaintMode::Area(self.model.scroll(count)) } - fn on_highlight_set(&mut self, attrs: &[(Value, Value)]) -> RepaintMode { - let mut model_attrs = Attrs::new(); - - for &(ref key_val, ref val) in attrs { - if let Some(key) = key_val.as_str() { - match key { - "foreground" => { - if let Some(fg) = val.as_u64() { - model_attrs.foreground = Some(Color::from_indexed_color(fg)); - } - } - "background" => { - if let Some(bg) = val.as_u64() { - model_attrs.background = Some(Color::from_indexed_color(bg)); - } - } - "special" => { - if let Some(bg) = val.as_u64() { - model_attrs.special = Some(Color::from_indexed_color(bg)); - } - } - "reverse" => model_attrs.reverse = true, - "bold" => model_attrs.bold = true, - "italic" => model_attrs.italic = true, - "underline" => model_attrs.underline = true, - "undercurl" => model_attrs.undercurl = true, - attr_key => println!("unknown attribute {}", attr_key), - }; - } else { - panic!("attr key must be string"); - } - } + fn on_highlight_set(&mut self, attrs: HashMap) -> RepaintMode { + let model_attrs = Attrs::from_value_map(&attrs); self.cur_attrs = Some(model_attrs); RepaintMode::Nothing @@ -1094,6 +1069,19 @@ impl RedrawEvents for State { self.mode.set_info(cursor_style_enabled, mode_info); RepaintMode::Nothing } + + fn cmdline_show( + &mut self, + content: Vec<(HashMap, String)>, + pos: u64, + firstc: String, + prompt: String, + indent: u64, + level: u64, + ) -> RepaintMode { + // TODO: implement + RepaintMode::Nothing + } } impl GuiApi for State { diff --git a/src/ui_model/cell.rs b/src/ui_model/cell.rs index b2a5a57..b981ed0 100644 --- a/src/ui_model/cell.rs +++ b/src/ui_model/cell.rs @@ -1,4 +1,7 @@ +use std::collections::HashMap; + use color::Color; +use neovim_lib::Value; #[derive(Clone)] pub struct Attrs { @@ -28,6 +31,38 @@ impl Attrs { } } + pub fn from_value_map(attrs: &HashMap) -> Attrs { + let mut model_attrs = Attrs::new(); + + for (ref key, ref val) in attrs { + match key.as_ref() { + "foreground" => { + if let Some(fg) = val.as_u64() { + model_attrs.foreground = Some(Color::from_indexed_color(fg)); + } + } + "background" => { + if let Some(bg) = val.as_u64() { + model_attrs.background = Some(Color::from_indexed_color(bg)); + } + } + "special" => { + if let Some(bg) = val.as_u64() { + model_attrs.special = Some(Color::from_indexed_color(bg)); + } + } + "reverse" => model_attrs.reverse = true, + "bold" => model_attrs.bold = true, + "italic" => model_attrs.italic = true, + "underline" => model_attrs.underline = true, + "undercurl" => model_attrs.undercurl = true, + attr_key => error!("unknown attribute {}", attr_key), + }; + } + + model_attrs + } + fn clear(&mut self) { self.italic = false; self.bold = false; From 56bc8f32de3ec4914a11ccb441ed56e23dbd230c Mon Sep 17 00:00:00 2001 From: daa Date: Fri, 17 Nov 2017 23:48:33 +0300 Subject: [PATCH 02/59] Small improvements --- src/nvim/redraw_handler.rs | 15 ++++++++++----- src/shell.rs | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index c1c1e83..c3b90cd 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -15,7 +15,7 @@ use super::mode_info::ModeInfo; pub trait RedrawEvents { fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode; - fn on_put(&mut self, text: &str) -> RepaintMode; + fn on_put(&mut self, text: String) -> RepaintMode; fn on_clear(&mut self) -> RepaintMode; @@ -136,7 +136,12 @@ macro_rules! call { ($s:ident -> $c:ident ($args:ident : $($arg_type:ident),+ )) => ( { let mut iter = $args.into_iter(); - $s.$c($( try_arg!(iter.next().unwrap(), $arg_type)),+ ) + $s.$c($( + try_arg!(iter.next() + .ok_or_else(|| format!("No such argument for {}", stringify!($c)))?, + $arg_type + ) + ),+ ) } ) } @@ -180,10 +185,10 @@ pub fn call( args: Vec, ) -> result::Result { let repaint_mode = match method { - "cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])), - "put" => ui.on_put(try_str!(args[0])), + "cursor_goto" => call!(ui->on_cursor_goto(args: uint, uint)), + "put" => call!(ui->on_put(args: str)), "clear" => ui.on_clear(), - "resize" => ui.on_resize(try_uint!(args[0]), try_uint!(args[1])), + "resize" => call!(ui->on_resize(args: uint, uint)), "highlight_set" => { call!(ui->on_highlight_set(args: ext)); RepaintMode::Nothing diff --git a/src/shell.rs b/src/shell.rs index a10a2ae..476735a 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -907,8 +907,8 @@ impl RedrawEvents for State { RepaintMode::AreaList(repaint_area) } - fn on_put(&mut self, text: &str) -> RepaintMode { - RepaintMode::Area(self.model.put(text, self.cur_attrs.as_ref())) + fn on_put(&mut self, text: String) -> RepaintMode { + RepaintMode::Area(self.model.put(&text, self.cur_attrs.as_ref())) } fn on_clear(&mut self) -> RepaintMode { From 932b7d98b4973221eed92169279b21107fb02001 Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 18 Nov 2017 15:56:37 +0300 Subject: [PATCH 03/59] Refactoring, ext_cmdline window --- src/cmd_line.rs | 23 ++++++++++++++ src/main.rs | 1 + src/nvim/cmd_line.rs | 30 ------------------ src/nvim/mod.rs | 2 -- src/nvim/redraw_handler.rs | 32 ++++++------------- src/popup_menu.rs | 63 +++++++++++++++++++------------------- src/shell.rs | 55 ++++++++++++++++++++------------- 7 files changed, 98 insertions(+), 108 deletions(-) create mode 100644 src/cmd_line.rs delete mode 100644 src/nvim/cmd_line.rs diff --git a/src/cmd_line.rs b/src/cmd_line.rs new file mode 100644 index 0000000..78c2894 --- /dev/null +++ b/src/cmd_line.rs @@ -0,0 +1,23 @@ +use gtk; +use gtk::prelude::*; + +pub struct CmdLine { + dlg: gtk::Dialog, +} + +impl CmdLine { + pub fn new() -> Self { + let dlg = gtk::Dialog::new(); + dlg.set_modal(true); + dlg.set_destroy_with_parent(true); + + CmdLine { + dlg, + } + } + + pub fn show>(&self, parent: &W) { + self.dlg.set_transient_for(parent); + self.dlg.show(); + } +} diff --git a/src/main.rs b/src/main.rs index 8c1b907..e2bf109 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,6 +43,7 @@ mod shell; mod input; mod settings; mod cursor; +mod cmd_line; mod shell_dlg; mod popup_menu; mod project; diff --git a/src/nvim/cmd_line.rs b/src/nvim/cmd_line.rs deleted file mode 100644 index 5b539a1..0000000 --- a/src/nvim/cmd_line.rs +++ /dev/null @@ -1,30 +0,0 @@ -use ui_model; - -pub struct CmdLine { - content: Vec<(ui_model::Attrs, String)>, - pos: u64, - firstc: String, - prompt: String, - indent: u64, - level: u64, -} - -impl CmdLine { - pub fn new( - content: Vec<(ui_model::Attrs, String)>, - pos: u64, - firstc: String, - prompt: String, - indent: u64, - level: u64, - ) -> Self { - CmdLine { - content, - pos, - firstc, - prompt, - indent, - level, - } - } -} diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index d7f477c..74d42c5 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -4,13 +4,11 @@ mod handler; mod mode_info; mod redraw_handler; mod repaint_mode; -mod cmd_line; pub use self::redraw_handler::{RedrawEvents, GuiApi}; pub use self::repaint_mode::RepaintMode; pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef}; pub use self::mode_info::{ModeInfo, CursorShape}; -pub use self::cmd_line::{CmdLine}; use std::error; use std::fmt; diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index c3b90cd..83bfd31 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -37,7 +37,7 @@ pub trait RedrawEvents { fn on_update_sp(&mut self, sp: i64) -> RepaintMode; - fn on_mode_change(&mut self, mode: &str, idx: u64) -> RepaintMode; + fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode; fn on_mouse(&mut self, on: bool) -> RepaintMode; @@ -45,7 +45,7 @@ pub trait RedrawEvents { fn popupmenu_show( &mut self, - menu: &[Vec<&str>], + menu: Vec>, selected: i64, row: u64, col: u64, @@ -118,6 +118,7 @@ macro_rules! map_array { macro_rules! try_arg { ($value:expr, bool) => (try_bool!($value)); ($value:expr, uint) => (try_uint!($value)); + ($value:expr, int) => (try_int!($value)); ($value:expr, str) => ( match $value { Value::String(s) => { @@ -198,31 +199,18 @@ pub fn call( call!(ui->on_set_scroll_region(args: uint, uint, uint, uint)); RepaintMode::Nothing } - "scroll" => ui.on_scroll(try_int!(args[0])), - "update_bg" => ui.on_update_bg(try_int!(args[0])), - "update_fg" => ui.on_update_fg(try_int!(args[0])), - "update_sp" => ui.on_update_sp(try_int!(args[0])), - "mode_change" => ui.on_mode_change(try_str!(args[0]), try_uint!(args[1])), + "scroll" => call!(ui->on_scroll(args: int)), + "update_bg" => call!(ui->on_update_bg(args: int)), + "update_fg" => call!(ui->on_update_fg(args: int)), + "update_sp" => call!(ui->on_update_sp(args: int)), + "mode_change" => call!(ui->on_mode_change(args: str, uint)), "mouse_on" => ui.on_mouse(true), "mouse_off" => ui.on_mouse(false), "busy_start" => ui.on_busy(true), "busy_stop" => ui.on_busy(false), - "popupmenu_show" => { - let menu_items = map_array!(args[0], "Error get menu list array", |item| { - map_array!(item, "Error get menu item array", |col| { - col.as_str().ok_or("Error get menu column") - }) - })?; - - ui.popupmenu_show( - &menu_items, - try_int!(args[1]), - try_uint!(args[2]), - try_uint!(args[3]), - ) - } + "popupmenu_show" => call!(ui->popupmenu_show(args: ext, int, uint, uint)), "popupmenu_hide" => ui.popupmenu_hide(), - "popupmenu_select" => ui.popupmenu_select(try_int!(args[0])), + "popupmenu_select" => call!(ui->popupmenu_select(args: int)), "tabline_update" => { let tabs_out = map_array!(args[1], "Error get tabline list".to_owned(), |tab| { tab.as_map() diff --git a/src/popup_menu.rs b/src/popup_menu.rs index fdb7224..d3a10ed 100644 --- a/src/popup_menu.rs +++ b/src/popup_menu.rs @@ -10,9 +10,10 @@ use gdk::{EventButton, EventType}; use neovim_lib::{Neovim, NeovimApi}; use color::ColorModel; -use nvim::{self, ErrorReport}; +use nvim::{self, ErrorReport, NeovimClient}; use shell; use input; +use render; const MAX_VISIBLE_ROWS: i32 = 10; @@ -41,25 +42,25 @@ impl State { } } - fn before_show(&mut self, shell: &shell::State, menu_items: &[Vec<&str>], selected: i64) { + fn before_show(&mut self, ctx: PopupMenuContext) { if self.nvim.is_none() { - self.nvim = Some(shell.nvim_clone()); + self.nvim = Some(ctx.nvim.clone()); } - self.update_tree(menu_items, shell); - self.select(selected); + self.update_tree(&ctx); + self.select(ctx.selected); } - fn update_tree(&self, menu: &[Vec<&str>], shell: &shell::State) { - if menu.is_empty() { + fn update_tree(&self, ctx: &PopupMenuContext) { + if ctx.menu_items.is_empty() { return; } - self.renderer.set_property_font( - Some(&shell.get_font_desc().to_string()), - ); + self.renderer.set_property_font(Some( + &ctx.font_ctx.font_description().to_string(), + )); - let color_model = &shell.color_model; + let color_model = &ctx.color_model; self.renderer.set_property_foreground_rgba( Some(&color_model.pmenu_fg().into()), ); @@ -69,7 +70,7 @@ impl State { self.update_css(color_model); - let col_count = menu[0].len(); + let col_count = ctx.menu_items[0].len(); let columns = self.tree.get_columns(); if columns.len() != col_count { @@ -85,7 +86,7 @@ impl State { let list_store = gtk::ListStore::new(&vec![gtk::Type::String; col_count]); let all_column_ids: Vec = (0..col_count).map(|i| i as u32).collect(); - for line in menu { + for line in ctx.menu_items { let line_array: Vec<&glib::ToValue> = line.iter().map(|v| v as &glib::ToValue).collect(); list_store.insert_with_values(None, &all_column_ids, &line_array[..]); @@ -215,30 +216,17 @@ impl PopupMenu { self.open } - pub fn show( - &mut self, - shell: &shell::State, - menu_items: &[Vec<&str>], - selected: i64, - x: i32, - y: i32, - width: i32, - height: i32, - ) { + pub fn show(&mut self, ctx: PopupMenuContext) { self.open = true; self.popover.set_pointing_to(>k::Rectangle { - x, - y, - width, - height, + x: ctx.x, + y: ctx.y, + width: ctx.width, + height: ctx.height, }); - self.state.borrow_mut().before_show( - shell, - menu_items, - selected, - ); + self.state.borrow_mut().before_show(ctx); self.popover.popup() } @@ -255,6 +243,17 @@ impl PopupMenu { } } +pub struct PopupMenuContext<'a> { + pub nvim: &'a Rc, + pub color_model: &'a ColorModel, + pub font_ctx: &'a render::Context, + pub menu_items: &'a [Vec], + pub selected: i64, + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, +} fn tree_button_press(tree: >k::TreeView, ev: &EventButton, nvim: &mut Neovim) -> Inhibit { if ev.get_event_type() != EventType::ButtonPress { diff --git a/src/shell.rs b/src/shell.rs index 476735a..ddb3275 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -23,14 +23,14 @@ use ui_model::{UiModel, Attrs, ModelRect}; use color::{ColorModel, Color, COLOR_BLACK, COLOR_WHITE, COLOR_RED}; use nvim::{self, RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient, NeovimRef, - NeovimClientAsync, CmdLine}; + NeovimClientAsync}; -use input; -use input::keyval_to_input_string; +use input::{self, keyval_to_input_string}; use cursor::Cursor; use ui::UiMutex; -use popup_menu::PopupMenu; +use popup_menu::{self, PopupMenu}; use tabline::Tabline; +use cmd_line::CmdLine; use error; use mode; use render; @@ -66,7 +66,8 @@ pub struct State { nvim: Rc, pub font_ctx: render::Context, cursor: Option, - popup_menu: RefCell, + popup_menu: PopupMenu, + cmd_line: CmdLine, settings: Rc>, pub mode: mode::Mode, @@ -88,7 +89,7 @@ pub struct State { impl State { pub fn new(settings: Rc>, options: ShellOptions) -> State { let drawing_area = gtk::DrawingArea::new(); - let popup_menu = RefCell::new(PopupMenu::new(&drawing_area)); + let popup_menu = PopupMenu::new(&drawing_area); let font_ctx = render::Context::new(FontDescription::from_string(DEFAULT_FONT_NAME)); State { @@ -100,6 +101,7 @@ impl State { font_ctx, cursor: None, popup_menu, + cmd_line: CmdLine::new(), settings, mode: mode::Mode::new(), @@ -183,7 +185,7 @@ impl State { } fn close_popup_menu(&self) { - if self.popup_menu.borrow().is_open() { + if self.popup_menu.is_open() { if let Some(mut nvim) = self.nvim() { nvim.input("").report_err(&mut *nvim); } @@ -306,6 +308,14 @@ impl State { } + fn get_window(&self) -> gtk::Window { + self.drawing_area + .get_toplevel() + .unwrap() + .downcast() + .unwrap() + } + fn resize_main_window(&mut self) { let &CellMetrics { line_height, @@ -319,11 +329,7 @@ impl State { let request_width = (self.model.columns as f64 * char_width) as i32; if width != request_width || height != request_height { - let window: gtk::Window = self.drawing_area - .get_toplevel() - .unwrap() - .downcast() - .unwrap(); + let window = self.get_window(); let (win_width, win_height) = window.get_size(); let h_border = win_width - width; let v_border = win_height - height; @@ -998,8 +1004,8 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn on_mode_change(&mut self, mode: &str, idx: u64) -> RepaintMode { - self.mode.update(mode, idx as usize); + fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode { + self.mode.update(&mode, idx as usize); RepaintMode::Area(self.model.cur_point()) } @@ -1019,7 +1025,7 @@ impl RedrawEvents for State { fn popupmenu_show( &mut self, - menu: &[Vec<&str>], + menu: Vec>, selected: i64, row: u64, col: u64, @@ -1027,26 +1033,30 @@ impl RedrawEvents for State { let point = ModelRect::point(col as usize, row as usize); let (x, y, width, height) = point.to_area(self.font_ctx.cell_metrics()); - self.popup_menu.borrow_mut().show( - self, - menu, + let context = popup_menu::PopupMenuContext { + nvim: &self.nvim, + color_model: &self.color_model, + font_ctx: &self.font_ctx, + menu_items: &menu, selected, x, y, width, - height, - ); + height + }; + + self.popup_menu.show(context); RepaintMode::Nothing } fn popupmenu_hide(&mut self) -> RepaintMode { - self.popup_menu.borrow_mut().hide(); + self.popup_menu.hide(); RepaintMode::Nothing } fn popupmenu_select(&mut self, selected: i64) -> RepaintMode { - self.popup_menu.borrow().select(selected); + self.popup_menu.select(selected); RepaintMode::Nothing } @@ -1079,6 +1089,7 @@ impl RedrawEvents for State { indent: u64, level: u64, ) -> RepaintMode { + self.cmd_line.show(&self.get_window()); // TODO: implement RepaintMode::Nothing } From 333c28e0662e1af406d3040f87d89fb8f8aca569 Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 18 Nov 2017 23:15:03 +0300 Subject: [PATCH 04/59] Use popup instead of dialog --- src/cmd_line.rs | 24 +++++++++++++++--------- src/popup_menu.rs | 1 - src/shell.rs | 5 +++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 78c2894..4180bb2 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -1,23 +1,29 @@ use gtk; use gtk::prelude::*; +//TODO: levels pub struct CmdLine { - dlg: gtk::Dialog, + popover: gtk::Popover, } impl CmdLine { - pub fn new() -> Self { - let dlg = gtk::Dialog::new(); - dlg.set_modal(true); - dlg.set_destroy_with_parent(true); + pub fn new(drawing: >k::DrawingArea) -> Self { + let popover = gtk::Popover::new(Some(drawing)); + popover.set_modal(false); + let edit_frame = gtk::Frame::new(None); + edit_frame.set_shadow_type(gtk::ShadowType::In); + let drawing_area = gtk::DrawingArea::new(); + edit_frame.add(&drawing_area); + edit_frame.show_all(); + + popover.add(&edit_frame); CmdLine { - dlg, + popover, } } - pub fn show>(&self, parent: &W) { - self.dlg.set_transient_for(parent); - self.dlg.show(); + pub fn show(&self) { + self.popover.popup(); } } diff --git a/src/popup_menu.rs b/src/popup_menu.rs index d3a10ed..18e45ae 100644 --- a/src/popup_menu.rs +++ b/src/popup_menu.rs @@ -11,7 +11,6 @@ use neovim_lib::{Neovim, NeovimApi}; use color::ColorModel; use nvim::{self, ErrorReport, NeovimClient}; -use shell; use input; use render; diff --git a/src/shell.rs b/src/shell.rs index ddb3275..5fcd907 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -90,6 +90,7 @@ impl State { pub fn new(settings: Rc>, options: ShellOptions) -> State { let drawing_area = gtk::DrawingArea::new(); let popup_menu = PopupMenu::new(&drawing_area); + let cmd_line = CmdLine::new(&drawing_area); let font_ctx = render::Context::new(FontDescription::from_string(DEFAULT_FONT_NAME)); State { @@ -101,7 +102,7 @@ impl State { font_ctx, cursor: None, popup_menu, - cmd_line: CmdLine::new(), + cmd_line, settings, mode: mode::Mode::new(), @@ -1089,7 +1090,7 @@ impl RedrawEvents for State { indent: u64, level: u64, ) -> RepaintMode { - self.cmd_line.show(&self.get_window()); + self.cmd_line.show(); // TODO: implement RepaintMode::Nothing } From f58e398c48aa5d13be88004d496206e5eb1592ad Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 19 Nov 2017 12:44:28 +0300 Subject: [PATCH 05/59] Starting point on conversion cmdline input to UiModel --- src/cmd_line.rs | 43 +++++++++++++++++++++++++++++++++++++++++-- src/shell.rs | 10 ++++++---- src/ui_model/mod.rs | 6 +++--- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 4180bb2..3b82c6a 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -1,9 +1,46 @@ +use std::collections::HashMap; + use gtk; use gtk::prelude::*; -//TODO: levels +use neovim_lib::Value; + +use ui_model::{UiModel, Attrs}; + +pub struct Level { + model: UiModel, +} + +impl Level { + const COLUMNS_STEP: u64 = 50; + + pub fn from( + content: Vec<(HashMap, String)>, + pos: u64, + firstc: String, + prompt: String, + indent: u64, + level: u64, + ) -> Self { + //TODO: double width chars + //TODO: multiline prompt + + let content: Vec<(Attrs, Vec)> = content + .iter() + .map(|c| (Attrs::from_value_map(&c.0), c.1.chars().collect())) + .collect(); + + let width = content.iter().map(|c| c.1.len()).count() as u64 + indent + 1/*firstc*/; + let columns = ((width / Level::COLUMNS_STEP) + 1) * Level::COLUMNS_STEP; + + let model = UiModel::new(1, columns); + Level { model } + } +} + pub struct CmdLine { popover: gtk::Popover, + levels: Vec, } impl CmdLine { @@ -19,11 +56,13 @@ impl CmdLine { popover.add(&edit_frame); CmdLine { + levels: Vec::new(), popover, } } - pub fn show(&self) { + pub fn show_level(&mut self, level: Level) { + self.levels.push(level); self.popover.popup(); } } diff --git a/src/shell.rs b/src/shell.rs index 5fcd907..b5d1d9c 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -30,7 +30,7 @@ use cursor::Cursor; use ui::UiMutex; use popup_menu::{self, PopupMenu}; use tabline::Tabline; -use cmd_line::CmdLine; +use cmd_line::{self, CmdLine}; use error; use mode; use render; @@ -915,7 +915,9 @@ impl RedrawEvents for State { } fn on_put(&mut self, text: String) -> RepaintMode { - RepaintMode::Area(self.model.put(&text, self.cur_attrs.as_ref())) + let ch = text.chars().last().unwrap_or(' '); + let double_width = text.is_empty(); + RepaintMode::Area(self.model.put(ch, double_width, self.cur_attrs.as_ref())) } fn on_clear(&mut self) -> RepaintMode { @@ -1090,8 +1092,8 @@ impl RedrawEvents for State { indent: u64, level: u64, ) -> RepaintMode { - self.cmd_line.show(); - // TODO: implement + let level = cmd_line::Level::from(content, pos, firstc, prompt, indent, level); + self.cmd_line.show_level(level); RepaintMode::Nothing } } diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index ec71f26..6e67adc 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -91,16 +91,16 @@ impl UiModel { (self.cur_row, self.cur_col) } - pub fn put(&mut self, text: &str, attrs: Option<&Attrs>) -> ModelRect { + pub fn put(&mut self, ch: char, double_width: bool, attrs: Option<&Attrs>) -> ModelRect { let mut changed_region = self.cur_point(); let line = &mut self.model[self.cur_row]; line.dirty_line = true; let cell = &mut line[self.cur_col]; - cell.ch = text.chars().last().unwrap_or(' '); + cell.ch = ch; cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(Attrs::new); - cell.attrs.double_width = text.is_empty(); + cell.attrs.double_width = double_width; cell.dirty = true; self.cur_col += 1; if self.cur_col >= self.columns { From 519c279c8dbdd92a03991f259b3876530441d53c Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 19 Nov 2017 23:13:06 +0300 Subject: [PATCH 06/59] Build prompt line --- src/cmd_line.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 3b82c6a..c46ed4b 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -13,6 +13,7 @@ pub struct Level { impl Level { const COLUMNS_STEP: u64 = 50; + const ROWS_STEP: u64 = 10; pub fn from( content: Vec<(HashMap, String)>, @@ -23,21 +24,33 @@ impl Level { level: u64, ) -> Self { //TODO: double width chars - //TODO: multiline prompt + let prompt = prompt_lines(firstc, prompt, indent); let content: Vec<(Attrs, Vec)> = content .iter() .map(|c| (Attrs::from_value_map(&c.0), c.1.chars().collect())) .collect(); - let width = content.iter().map(|c| c.1.len()).count() as u64 + indent + 1/*firstc*/; + let width = (content.iter().map(|c| c.1.len()).count() + prompt.last().map_or(0, |p| p.len())) as u64; let columns = ((width / Level::COLUMNS_STEP) + 1) * Level::COLUMNS_STEP; + let rows = ((prompt.len() as u64 / Level::ROWS_STEP) + 1) * Level::ROWS_STEP; + + let model = UiModel::new(rows, columns); - let model = UiModel::new(1, columns); Level { model } } } +fn prompt_lines(firstc: String, prompt: String, indent: u64) -> Vec> { + if !firstc.is_empty() { + vec![firstc.chars().chain((0..indent).map(|_| ' ')).collect()] + } else if !prompt.is_empty() { + prompt.lines().map(|l| l.chars().collect()).collect() + } else { + vec![] + } +} + pub struct CmdLine { popover: gtk::Popover, levels: Vec, From d6aa19510c97d8bdfa1c759fd99684cf3137df41 Mon Sep 17 00:00:00 2001 From: daa84 Date: Thu, 23 Nov 2017 17:57:39 +0300 Subject: [PATCH 07/59] One step to cmd line implementation --- src/cmd_line.rs | 134 ++++++++++++++++++++++++++++++++++++--- src/cursor.rs | 38 +++++------ src/render/mod.rs | 4 +- src/shell.rs | 150 +++++++++++++++++++++++++++----------------- src/ui_model/mod.rs | 2 +- 5 files changed, 240 insertions(+), 88 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index c46ed4b..9e6ed1b 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -1,11 +1,19 @@ use std::collections::HashMap; +use std::rc::Rc; +use std::sync::Arc; +use std::cell::RefCell; use gtk; use gtk::prelude::*; +use cairo; use neovim_lib::Value; use ui_model::{UiModel, Attrs}; +use ui::UiMutex; +use render; +use shell; +use cursor; pub struct Level { model: UiModel, @@ -21,9 +29,9 @@ impl Level { firstc: String, prompt: String, indent: u64, - level: u64, ) -> Self { //TODO: double width chars + //TODO: im let prompt = prompt_lines(firstc, prompt, indent); let content: Vec<(Attrs, Vec)> = content @@ -31,14 +39,44 @@ impl Level { .map(|c| (Attrs::from_value_map(&c.0), c.1.chars().collect())) .collect(); - let width = (content.iter().map(|c| c.1.len()).count() + prompt.last().map_or(0, |p| p.len())) as u64; + let width = (content.iter().map(|c| c.1.len()).count() + + prompt.last().map_or(0, |p| p.len())) as u64; let columns = ((width / Level::COLUMNS_STEP) + 1) * Level::COLUMNS_STEP; let rows = ((prompt.len() as u64 / Level::ROWS_STEP) + 1) * Level::ROWS_STEP; - let model = UiModel::new(rows, columns); + let mut model = UiModel::new(rows, columns); + + for (row_idx, prompt_line) in prompt.iter().enumerate() { + for (col_idx, &ch) in prompt_line.iter().enumerate() { + model.set_cursor(row_idx, col_idx); + model.put(ch, false, None); + } + } + + let mut col_idx = 0; + let row_idx = if prompt.len() > 0 { + prompt.len() - 1 + } else { + 0 + }; + for (attr, ch_list) in content { + for ch in ch_list { + model.set_cursor(row_idx, col_idx); + model.put(ch, false, Some(&attr)); + col_idx += 1; + } + } Level { model } } + + fn update_cache(&mut self, render_state: &shell::RenderState) { + render::shape_dirty( + &render_state.font_ctx, + &mut self.model, + &render_state.color_model, + ); + } } fn prompt_lines(firstc: String, prompt: String, indent: u64) -> Vec> { @@ -51,31 +89,109 @@ fn prompt_lines(firstc: String, prompt: String, indent: u64) -> Vec> { } } +struct State { + levels: Vec, + render_state: Rc>, + drawing_area: gtk::DrawingArea, +} + +impl State { + fn new(drawing_area: gtk::DrawingArea, render_state: Rc>) -> Self { + State { + levels: Vec::new(), + render_state, + drawing_area, + } + } +} + +impl cursor::CursorRedrawCb for State { + fn queue_redraw_cursor(&mut self) { + // TODO: implement + } +} + pub struct CmdLine { popover: gtk::Popover, - levels: Vec, + displyed: bool, + state: Arc>, } impl CmdLine { - pub fn new(drawing: >k::DrawingArea) -> Self { + pub fn new(drawing: >k::DrawingArea, render_state: Rc>) -> Self { let popover = gtk::Popover::new(Some(drawing)); popover.set_modal(false); let edit_frame = gtk::Frame::new(None); edit_frame.set_shadow_type(gtk::ShadowType::In); let drawing_area = gtk::DrawingArea::new(); + drawing_area.set_size_request(50, 50); edit_frame.add(&drawing_area); edit_frame.show_all(); popover.add(&edit_frame); + let state = Arc::new(UiMutex::new(State::new(drawing_area.clone(), render_state))); + let weak_cb = Arc::downgrade(&state); + let cursor = cursor::Cursor::new(weak_cb); + + drawing_area.connect_draw( + clone!(state => move |_, ctx| gtk_draw(ctx, &state, &cursor)), + ); + CmdLine { - levels: Vec::new(), popover, + state, + displyed: false, } } - pub fn show_level(&mut self, level: Level) { - self.levels.push(level); - self.popover.popup(); + pub fn show_level( + &mut self, + content: Vec<(HashMap, String)>, + pos: u64, + firstc: String, + prompt: String, + indent: u64, + level_idx: u64, + ) { + let mut state = self.state.borrow_mut(); + + let mut level = Level::from(content, pos, firstc, prompt, indent); + level.update_cache(&*state.render_state.borrow()); + + if level_idx as usize == state.levels.len() { + // TODO: update level + state.levels.pop(); + } + state.levels.push(level); + if !self.displyed { + self.displyed = true; + self.popover.popup(); + } else { + state.drawing_area.queue_draw() + } } } + +fn gtk_draw( + ctx: &cairo::Context, + state: &Arc>, + cursor: &cursor::Cursor, +) -> Inhibit { + let state = state.borrow(); + let level = state.levels.last(); + + if let Some(level) = level { + let render_state = state.render_state.borrow(); + + render::render( + ctx, + cursor, + &render_state.font_ctx, + &level.model, + &render_state.color_model, + &render_state.mode, + ); + } + Inhibit(false) +} diff --git a/src/cursor.rs b/src/cursor.rs index 500c7af..0cb2491 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -1,10 +1,8 @@ use cairo; use color::Color; use ui::UiMutex; -use shell; use mode; use nvim; -use nvim::{RepaintMode, RedrawEvents}; use std::sync::{Arc, Weak}; use render; use render::CellMetrics; @@ -44,20 +42,20 @@ enum AnimPhase { Busy, } -struct State { +struct State { alpha: Alpha, anim_phase: AnimPhase, - shell: Weak>, + redraw_cb: Weak>, timer: Option, } -impl State { - fn new(shell: Weak>) -> State { +impl State { + fn new(redraw_cb: Weak>) -> Self { State { alpha: Alpha(1.0), anim_phase: AnimPhase::Shown, - shell: shell, + redraw_cb, timer: None, } } @@ -72,13 +70,13 @@ impl State { } } -pub struct Cursor { - state: Arc>, +pub struct Cursor { + state: Arc>>, } -impl Cursor { - pub fn new(shell: Weak>) -> Cursor { - Cursor { state: Arc::new(UiMutex::new(State::new(shell))) } +impl Cursor { + pub fn new(redraw_cb: Weak>) -> Self { + Cursor { state: Arc::new(UiMutex::new(State::new(redraw_cb))) } } pub fn start(&mut self) { @@ -205,7 +203,8 @@ fn cursor_rect( (line_y, cursor_width, line_height) } } -fn anim_step(state: &Arc>) -> glib::Continue { + +fn anim_step (state: &Arc>>) -> glib::Continue { let mut mut_state = state.borrow_mut(); let next_event = match mut_state.anim_phase { @@ -240,10 +239,9 @@ fn anim_step(state: &Arc>) -> glib::Continue { AnimPhase::Busy => None, }; - let shell = mut_state.shell.upgrade().unwrap(); - let mut shell = shell.borrow_mut(); - let point = shell.model.cur_point(); - shell.on_redraw(&RepaintMode::Area(point)); + let redraw_cb = mut_state.redraw_cb.upgrade().unwrap(); + let mut redraw_cb = redraw_cb.borrow_mut(); + redraw_cb.queue_redraw_cursor(); if let Some(timeout) = next_event { @@ -257,7 +255,7 @@ fn anim_step(state: &Arc>) -> glib::Continue { } -impl Drop for Cursor { +impl Drop for Cursor { fn drop(&mut self) { if let Some(timer_id) = self.state.borrow().timer { glib::source_remove(timer_id); @@ -265,6 +263,10 @@ impl Drop for Cursor { } } +pub trait CursorRedrawCb { + fn queue_redraw_cursor(&mut self); +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/render/mod.rs b/src/render/mod.rs index 79e6ac5..20441a7 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -15,9 +15,9 @@ use cursor; use pangocairo::CairoContextExt; use ui_model; -pub fn render( +pub fn render( ctx: &cairo::Context, - cursor: &cursor::Cursor, + cursor: &cursor::Cursor, font_ctx: &context::Context, ui_model: &ui_model::UiModel, color_model: &color::ColorModel, diff --git a/src/shell.rs b/src/shell.rs index b5d1d9c..3b530a6 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -26,7 +26,7 @@ use nvim::{self, RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient, N NeovimClientAsync}; use input::{self, keyval_to_input_string}; -use cursor::Cursor; +use cursor::{Cursor, CursorRedrawCb}; use ui::UiMutex; use popup_menu::{self, PopupMenu}; use tabline::Tabline; @@ -58,19 +58,32 @@ enum ResizeState { Wait, } +pub struct RenderState { + pub font_ctx: render::Context, + pub color_model: ColorModel, + pub mode: mode::Mode, +} + +impl RenderState { + pub fn new() -> Self { + RenderState { + font_ctx: render::Context::new(FontDescription::from_string(DEFAULT_FONT_NAME)), + color_model: ColorModel::new(), + mode: mode::Mode::new(), + } + } +} + pub struct State { pub model: UiModel, - pub color_model: ColorModel, cur_attrs: Option, mouse_enabled: bool, nvim: Rc, - pub font_ctx: render::Context, - cursor: Option, + cursor: Option>, popup_menu: PopupMenu, cmd_line: CmdLine, settings: Rc>, - - pub mode: mode::Mode, + render_state: Rc>, stack: gtk::Stack, drawing_area: gtk::DrawingArea, @@ -89,23 +102,20 @@ pub struct State { impl State { pub fn new(settings: Rc>, options: ShellOptions) -> State { let drawing_area = gtk::DrawingArea::new(); + let render_state = Rc::new(RefCell::new(RenderState::new())); let popup_menu = PopupMenu::new(&drawing_area); - let cmd_line = CmdLine::new(&drawing_area); - let font_ctx = render::Context::new(FontDescription::from_string(DEFAULT_FONT_NAME)); + let cmd_line = CmdLine::new(&drawing_area, render_state.clone()); State { model: UiModel::empty(), - color_model: ColorModel::new(), nvim: Rc::new(NeovimClient::new()), cur_attrs: None, mouse_enabled: true, - font_ctx, cursor: None, popup_menu, cmd_line, settings, - - mode: mode::Mode::new(), + render_state, // UI stack: gtk::Stack::new(), @@ -162,12 +172,10 @@ impl State { } } - pub fn get_font_desc(&self) -> &FontDescription { - self.font_ctx.font_description() - } - pub fn set_font_desc(&mut self, desc: &str) { - self.font_ctx.update(FontDescription::from_string(desc)); + self.render_state.borrow_mut().font_ctx.update( + FontDescription::from_string(desc), + ); self.model.clear_glyphs(); self.try_nvim_resize(); self.on_redraw(&RepaintMode::All); @@ -207,18 +215,25 @@ impl State { self.update_dirty_glyphs(); + let render_state = self.render_state.borrow(); + let cell_metrics = render_state.font_ctx.cell_metrics(); + for mut rect in rects { rect.extend_by_items(&self.model); - let (x, y, width, height) = - rect.to_area_extend_ink(&self.model, self.font_ctx.cell_metrics()); + let (x, y, width, height) = rect.to_area_extend_ink(&self.model, cell_metrics); self.drawing_area.queue_draw_area(x, y, width, height); } } #[inline] fn update_dirty_glyphs(&mut self) { - render::shape_dirty(&self.font_ctx, &mut self.model, &self.color_model); + let render_state = self.render_state.borrow(); + render::shape_dirty( + &render_state.font_ctx, + &mut self.model, + &render_state.color_model, + ); } fn im_commit(&self, ch: &str) { @@ -232,7 +247,7 @@ impl State { line_height, char_width, .. - } = self.font_ctx.cell_metrics(); + } = self.render_state.borrow().font_ctx.cell_metrics(); let alloc = self.drawing_area.get_allocation(); ( (alloc.width as f64 / char_width).trunc() as usize, @@ -252,7 +267,7 @@ impl State { let (row, col) = self.model.get_cursor(); let (x, y, width, height) = - ModelRect::point(col, row).to_area(self.font_ctx.cell_metrics()); + ModelRect::point(col, row).to_area(self.render_state.borrow().font_ctx.cell_metrics()); self.im_context.set_cursor_location(&gdk::Rectangle { x, @@ -310,11 +325,11 @@ impl State { } fn get_window(&self) -> gtk::Window { - self.drawing_area - .get_toplevel() - .unwrap() - .downcast() - .unwrap() + self.drawing_area + .get_toplevel() + .unwrap() + .downcast() + .unwrap() } fn resize_main_window(&mut self) { @@ -322,7 +337,7 @@ impl State { line_height, char_width, .. - } = self.font_ctx.cell_metrics(); + } = self.render_state.borrow().font_ctx.cell_metrics(); let width = self.drawing_area.get_allocated_width(); let height = self.drawing_area.get_allocated_height(); @@ -341,7 +356,10 @@ impl State { fn edit_paste(&self, clipboard: &str) { let nvim = self.nvim(); if let Some(mut nvim) = nvim { - if self.mode.is(&mode::NvimMode::Insert) || self.mode.is(&mode::NvimMode::Normal) { + let render_state = self.render_state.borrow(); + if render_state.mode.is(&mode::NvimMode::Insert) || + render_state.mode.is(&mode::NvimMode::Normal) + { let paste_code = format!("normal! \"{}P", clipboard); nvim.command(&paste_code).report_err(&mut *nvim); } else { @@ -682,7 +700,7 @@ fn mouse_input(shell: &mut State, input: &str, state: ModifierType, position: (f line_height, char_width, .. - } = shell.font_ctx.cell_metrics(); + } = shell.render_state.borrow().font_ctx.cell_metrics(); let (x, y) = position; let col = (x / char_width).trunc() as u64; let row = (y / line_height).trunc() as u64; @@ -722,13 +740,14 @@ fn gtk_draw(state_arc: &Arc>, ctx: &cairo::Context) -> Inhibit { let state = state_arc.borrow(); if state.nvim.is_initialized() { + let render_state = state.render_state.borrow(); render::render( ctx, state.cursor.as_ref().unwrap(), - &state.font_ctx, + &render_state.font_ctx, &state.model, - &state.color_model, - &state.mode, + &render_state.color_model, + &render_state.mode, ); } else if state.nvim.is_initializing() { draw_initializing(&*state, ctx); @@ -852,14 +871,16 @@ fn set_nvim_initialized(state_arc: Arc>) { } fn draw_initializing(state: &State, ctx: &cairo::Context) { + let render_state = state.render_state.borrow(); + let color_model = &render_state.color_model; let layout = ctx.create_pango_layout(); - let desc = state.get_font_desc(); + let desc = render_state.font_ctx.font_description(); let alloc = state.drawing_area.get_allocation(); ctx.set_source_rgb( - state.color_model.bg_color.0, - state.color_model.bg_color.1, - state.color_model.bg_color.2, + color_model.bg_color.0, + color_model.bg_color.1, + color_model.bg_color.2, ); ctx.paint(); @@ -872,9 +893,9 @@ fn draw_initializing(state: &State, ctx: &cairo::Context) { ctx.move_to(x, y); ctx.set_source_rgb( - state.color_model.fg_color.0, - state.color_model.fg_color.1, - state.color_model.fg_color.2, + color_model.fg_color.0, + color_model.fg_color.1, + color_model.fg_color.2, ); ctx.update_pango_layout(&layout); ctx.show_pango_layout(&layout); @@ -883,11 +904,11 @@ fn draw_initializing(state: &State, ctx: &cairo::Context) { ctx.move_to(x + width as f64, y); state.cursor.as_ref().unwrap().draw( ctx, - &state.font_ctx, - &state.mode, + &render_state.font_ctx, + &render_state.mode, y, false, - &state.color_model.bg_color, + &color_model.bg_color, ); } @@ -947,7 +968,8 @@ impl RedrawEvents for State { } if let Some(mut nvim) = self.nvim.nvim() { - self.color_model.theme.update(&mut *nvim); + let mut render_state = self.render_state.borrow_mut(); + render_state.color_model.theme.update(&mut *nvim); } RepaintMode::Nothing } @@ -981,34 +1003,38 @@ impl RedrawEvents for State { } fn on_update_bg(&mut self, bg: i64) -> RepaintMode { + let mut render_state = self.render_state.borrow_mut(); if bg >= 0 { - self.color_model.bg_color = Color::from_indexed_color(bg as u64); + render_state.color_model.bg_color = Color::from_indexed_color(bg as u64); } else { - self.color_model.bg_color = COLOR_BLACK; + render_state.color_model.bg_color = COLOR_BLACK; } RepaintMode::Nothing } fn on_update_fg(&mut self, fg: i64) -> RepaintMode { + let mut render_state = self.render_state.borrow_mut(); if fg >= 0 { - self.color_model.fg_color = Color::from_indexed_color(fg as u64); + render_state.color_model.fg_color = Color::from_indexed_color(fg as u64); } else { - self.color_model.fg_color = COLOR_WHITE; + render_state.color_model.fg_color = COLOR_WHITE; } RepaintMode::Nothing } fn on_update_sp(&mut self, sp: i64) -> RepaintMode { + let mut render_state = self.render_state.borrow_mut(); if sp >= 0 { - self.color_model.sp_color = Color::from_indexed_color(sp as u64); + render_state.color_model.sp_color = Color::from_indexed_color(sp as u64); } else { - self.color_model.sp_color = COLOR_RED; + render_state.color_model.sp_color = COLOR_RED; } RepaintMode::Nothing } fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode { - self.mode.update(&mode, idx as usize); + let mut render_state = self.render_state.borrow_mut(); + render_state.mode.update(&mode, idx as usize); RepaintMode::Area(self.model.cur_point()) } @@ -1034,18 +1060,19 @@ impl RedrawEvents for State { col: u64, ) -> RepaintMode { let point = ModelRect::point(col as usize, row as usize); - let (x, y, width, height) = point.to_area(self.font_ctx.cell_metrics()); + let render_state = self.render_state.borrow(); + let (x, y, width, height) = point.to_area(render_state.font_ctx.cell_metrics()); let context = popup_menu::PopupMenuContext { nvim: &self.nvim, - color_model: &self.color_model, - font_ctx: &self.font_ctx, + color_model: &render_state.color_model, + font_ctx: &render_state.font_ctx, menu_items: &menu, selected, x, y, width, - height + height, }; self.popup_menu.show(context); @@ -1079,7 +1106,8 @@ impl RedrawEvents for State { cursor_style_enabled: bool, mode_info: Vec, ) -> RepaintMode { - self.mode.set_info(cursor_style_enabled, mode_info); + let mut render_state = self.render_state.borrow_mut(); + render_state.mode.set_info(cursor_style_enabled, mode_info); RepaintMode::Nothing } @@ -1092,12 +1120,18 @@ impl RedrawEvents for State { indent: u64, level: u64, ) -> RepaintMode { - let level = cmd_line::Level::from(content, pos, firstc, prompt, indent, level); - self.cmd_line.show_level(level); + self.cmd_line.show_level(content, pos, firstc, prompt, indent, level); RepaintMode::Nothing } } +impl CursorRedrawCb for State { + fn queue_redraw_cursor(&mut self) { + let cur_point = self.model.cur_point(); + self.on_redraw(&RepaintMode::Area(cur_point)); + } +} + impl GuiApi for State { fn set_font(&mut self, font_desc: &str) { { diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index 6e67adc..19cdc52 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -293,7 +293,7 @@ mod tests { model.set_cursor(1, 1); - let rect = model.put(" ", None); + let rect = model.put(' ', false, None); assert_eq!(1, rect.top); assert_eq!(1, rect.left); From 83061366dd5ddea1654a9a969ed19b1e0705c899 Mon Sep 17 00:00:00 2001 From: daa Date: Wed, 3 Jan 2018 01:26:04 +0300 Subject: [PATCH 08/59] Wrap text, hide popover --- src/cmd_line.rs | 81 +++++++++++++++++++----------------- src/nvim/redraw_handler.rs | 6 +++ src/shell.rs | 5 +++ src/ui_model/mod.rs | 2 + src/ui_model/model_layout.rs | 45 ++++++++++++++++++++ 5 files changed, 101 insertions(+), 38 deletions(-) create mode 100644 src/ui_model/model_layout.rs diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 9e6ed1b..3756896 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -9,19 +9,17 @@ use cairo; use neovim_lib::Value; -use ui_model::{UiModel, Attrs}; +use ui_model::{Attrs, ModelLayout}; use ui::UiMutex; use render; use shell; use cursor; pub struct Level { - model: UiModel, + model_layout: ModelLayout, } impl Level { - const COLUMNS_STEP: u64 = 50; - const ROWS_STEP: u64 = 10; pub fn from( content: Vec<(HashMap, String)>, @@ -33,57 +31,41 @@ impl Level { //TODO: double width chars //TODO: im - let prompt = prompt_lines(firstc, prompt, indent); - let content: Vec<(Attrs, Vec)> = content + let content_line: Vec<(Option, Vec)> = content .iter() - .map(|c| (Attrs::from_value_map(&c.0), c.1.chars().collect())) + .map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect())) .collect(); + let prompt_lines = prompt_lines(firstc, prompt, indent); - let width = (content.iter().map(|c| c.1.len()).count() + - prompt.last().map_or(0, |p| p.len())) as u64; - let columns = ((width / Level::COLUMNS_STEP) + 1) * Level::COLUMNS_STEP; - let rows = ((prompt.len() as u64 / Level::ROWS_STEP) + 1) * Level::ROWS_STEP; + let mut content: Vec<_> = prompt_lines.into_iter().map(|line| vec![line]).collect(); - let mut model = UiModel::new(rows, columns); - - for (row_idx, prompt_line) in prompt.iter().enumerate() { - for (col_idx, &ch) in prompt_line.iter().enumerate() { - model.set_cursor(row_idx, col_idx); - model.put(ch, false, None); - } - } - - let mut col_idx = 0; - let row_idx = if prompt.len() > 0 { - prompt.len() - 1 + if content.is_empty() { + content.push(content_line); } else { - 0 - }; - for (attr, ch_list) in content { - for ch in ch_list { - model.set_cursor(row_idx, col_idx); - model.put(ch, false, Some(&attr)); - col_idx += 1; - } + content.last_mut().map(|line| line.extend(content_line)); } - Level { model } + let mut model_layout = ModelLayout::new(); + // TODO: calculate width + model_layout.layout(content, 5); + + Level { model_layout } } fn update_cache(&mut self, render_state: &shell::RenderState) { render::shape_dirty( &render_state.font_ctx, - &mut self.model, + &mut self.model_layout.model, &render_state.color_model, ); } } -fn prompt_lines(firstc: String, prompt: String, indent: u64) -> Vec> { +fn prompt_lines(firstc: String, prompt: String, indent: u64) -> Vec<(Option, Vec)> { if !firstc.is_empty() { - vec![firstc.chars().chain((0..indent).map(|_| ' ')).collect()] + vec![(None, firstc.chars().chain((0..indent).map(|_| ' ')).collect())] } else if !prompt.is_empty() { - prompt.lines().map(|l| l.chars().collect()).collect() + prompt.lines().map(|l| (None, l.chars().collect())).collect() } else { vec![] } @@ -121,10 +103,12 @@ impl CmdLine { pub fn new(drawing: >k::DrawingArea, render_state: Rc>) -> Self { let popover = gtk::Popover::new(Some(drawing)); popover.set_modal(false); + popover.set_position(gtk::PositionType::Top); + let edit_frame = gtk::Frame::new(None); edit_frame.set_shadow_type(gtk::ShadowType::In); let drawing_area = gtk::DrawingArea::new(); - drawing_area.set_size_request(50, 50); + drawing_area.set_size_request(150, 50); edit_frame.add(&drawing_area); edit_frame.show_all(); @@ -166,11 +150,32 @@ impl CmdLine { state.levels.push(level); if !self.displyed { self.displyed = true; + let allocation = self.popover.get_relative_to().unwrap().get_allocation(); + self.popover.set_pointing_to(>k::Rectangle { + x: allocation.width / 2, + y: allocation.height / 2, + width: 1, + height: 1, + }); + self.popover.popup(); } else { state.drawing_area.queue_draw() } } + + pub fn hide_level(&mut self, level_idx: u64) { + let mut state = self.state.borrow_mut(); + + if level_idx as usize == state.levels.len() { + state.levels.pop(); + } + + if state.levels.is_empty() { + self.popover.hide(); + self.displyed = false; + } + } } fn gtk_draw( @@ -188,7 +193,7 @@ fn gtk_draw( ctx, cursor, &render_state.font_ctx, - &level.model, + &level.model_layout.model, &render_state.color_model, &render_state.mode, ); diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index be846c1..86432e7 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -76,6 +76,11 @@ pub trait RedrawEvents { indent: u64, level: u64, ) -> RepaintMode; + + fn cmdline_hide( + &mut self, + level: u64, + ) -> RepaintMode; } pub trait GuiApi { @@ -256,6 +261,7 @@ pub fn call( ui.mode_info_set(try_bool!(args[0]), mode_info) } "cmdline_show" => call!(ui->cmdline_show(args: ext, uint, str, str, uint, uint)), + "cmdline_hide" => call!(ui->cmdline_hide(args: uint)), _ => { println!("Event {}({:?})", method, args); RepaintMode::Nothing diff --git a/src/shell.rs b/src/shell.rs index 3c866e3..67589b8 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1189,6 +1189,11 @@ impl RedrawEvents for State { self.cmd_line.show_level(content, pos, firstc, prompt, indent, level); RepaintMode::Nothing } + + fn cmdline_hide(&mut self, level: u64) -> RepaintMode { + self.cmd_line.hide_level(level); + RepaintMode::Nothing + } } impl CursorRedrawCb for State { diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index 19cdc52..277ae25 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -2,11 +2,13 @@ mod cell; mod line; mod item; mod model_rect; +mod model_layout; pub use self::cell::{Cell, Attrs}; pub use self::line::{Line, StyledLine}; pub use self::item::Item; pub use self::model_rect::{ModelRect, ModelRectVec}; +pub use self::model_layout::ModelLayout; pub struct UiModel { diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs new file mode 100644 index 0000000..b4506e7 --- /dev/null +++ b/src/ui_model/model_layout.rs @@ -0,0 +1,45 @@ +use ui_model::{UiModel, Attrs}; + +pub struct ModelLayout { + pub model: UiModel, +} + +impl ModelLayout { + const COLUMNS_STEP: u64 = 50; + const ROWS_STEP: u64 = 10; + + pub fn new() -> Self { + ModelLayout { model: UiModel::new(ModelLayout::ROWS_STEP, ModelLayout::COLUMNS_STEP) } + } + + /// Wrap all lines into model + pub fn layout(&mut self, lines: Vec, Vec)>>, columns: u64) { + if lines.len() > self.model.rows || columns as usize > self.model.columns { + let model_cols = ((columns / ModelLayout::COLUMNS_STEP) + 1) * + ModelLayout::COLUMNS_STEP; + let model_rows = ((lines.len() as u64 / ModelLayout::ROWS_STEP) + 1) * + ModelLayout::ROWS_STEP; + + self.model = UiModel::new(model_rows, model_cols); + } + + + let mut col_idx = 0; + let mut row_idx = 0; + for content in lines { + for (attr, ch_list) in content { + for ch in ch_list { + if col_idx >= columns { + col_idx = 0; + row_idx += 1; + } + + self.model.set_cursor(row_idx, col_idx as usize); + self.model.put(ch, false, attr.as_ref()); + col_idx += 1; + } + } + row_idx += 1; + } + } +} From c27fbff586462e862213706c6135204cae0c99ff Mon Sep 17 00:00:00 2001 From: daa Date: Wed, 3 Jan 2018 12:04:01 +0300 Subject: [PATCH 09/59] Point to cursor --- src/cmd_line.rs | 73 +++++++++++++++++++++--------------- src/shell.rs | 21 ++++++++++- src/ui_model/model_layout.rs | 17 +++++++-- 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 3756896..bd154a5 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -11,31 +11,27 @@ use neovim_lib::Value; use ui_model::{Attrs, ModelLayout}; use ui::UiMutex; -use render; +use render::{self, CellMetrics}; use shell; use cursor; pub struct Level { model_layout: ModelLayout, + preferred_width: i32, + preferred_height: i32, } impl Level { - pub fn from( - content: Vec<(HashMap, String)>, - pos: u64, - firstc: String, - prompt: String, - indent: u64, - ) -> Self { - //TODO: double width chars + pub fn from(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { + //TODO: double width chars render, also note in text wrapping //TODO: im - let content_line: Vec<(Option, Vec)> = content + let content_line: Vec<(Option, Vec)> = ctx.content .iter() .map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect())) .collect(); - let prompt_lines = prompt_lines(firstc, prompt, indent); + let prompt_lines = prompt_lines(&ctx.firstc, &ctx.prompt, ctx.indent); let mut content: Vec<_> = prompt_lines.into_iter().map(|line| vec![line]).collect(); @@ -45,11 +41,20 @@ impl Level { content.last_mut().map(|line| line.extend(content_line)); } - let mut model_layout = ModelLayout::new(); - // TODO: calculate width - model_layout.layout(content, 5); + let &CellMetrics { + line_height, + char_width, + .. + } = render_state.font_ctx.cell_metrics(); - Level { model_layout } + let max_width_chars = (ctx.max_width as f64 / char_width) as u64; + + let mut model_layout = ModelLayout::new(); + let (columns, rows) = model_layout.layout(content, max_width_chars); + + let preferred_width = (char_width * columns as f64) as i32; + let preferred_height = (line_height * rows as f64) as i32; + Level { model_layout, preferred_width, preferred_height } } fn update_cache(&mut self, render_state: &shell::RenderState) { @@ -61,7 +66,7 @@ impl Level { } } -fn prompt_lines(firstc: String, prompt: String, indent: u64) -> Vec<(Option, Vec)> { +fn prompt_lines(firstc: &str, prompt: &str, indent: u64) -> Vec<(Option, Vec)> { if !firstc.is_empty() { vec![(None, firstc.chars().chain((0..indent).map(|_| ' ')).collect())] } else if !prompt.is_empty() { @@ -103,7 +108,7 @@ impl CmdLine { pub fn new(drawing: >k::DrawingArea, render_state: Rc>) -> Self { let popover = gtk::Popover::new(Some(drawing)); popover.set_modal(false); - popover.set_position(gtk::PositionType::Top); + popover.set_position(gtk::PositionType::Right); let edit_frame = gtk::Frame::new(None); edit_frame.set_shadow_type(gtk::ShadowType::In); @@ -131,31 +136,25 @@ impl CmdLine { pub fn show_level( &mut self, - content: Vec<(HashMap, String)>, - pos: u64, - firstc: String, - prompt: String, - indent: u64, - level_idx: u64, + ctx: &CmdLineContext, ) { let mut state = self.state.borrow_mut(); - let mut level = Level::from(content, pos, firstc, prompt, indent); + let mut level = Level::from(ctx, &*state.render_state.borrow()); level.update_cache(&*state.render_state.borrow()); - if level_idx as usize == state.levels.len() { + if ctx.level_idx as usize == state.levels.len() { // TODO: update level state.levels.pop(); } state.levels.push(level); if !self.displyed { self.displyed = true; - let allocation = self.popover.get_relative_to().unwrap().get_allocation(); self.popover.set_pointing_to(>k::Rectangle { - x: allocation.width / 2, - y: allocation.height / 2, - width: 1, - height: 1, + x: ctx.x, + y: ctx.y, + width: ctx.width, + height: ctx.height, }); self.popover.popup(); @@ -200,3 +199,17 @@ fn gtk_draw( } Inhibit(false) } + +pub struct CmdLineContext { + pub content: Vec<(HashMap, String)>, + pub pos: u64, + pub firstc: String, + pub prompt: String, + pub indent: u64, + pub level_idx: u64, + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, + pub max_width: i32, +} diff --git a/src/shell.rs b/src/shell.rs index 67589b8..46c3faf 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -30,7 +30,7 @@ use cursor::{Cursor, CursorRedrawCb}; use ui::UiMutex; use popup_menu::{self, PopupMenu}; use tabline::Tabline; -use cmd_line::CmdLine; +use cmd_line::{CmdLine, CmdLineContext}; use error; use mode; use render; @@ -1186,7 +1186,24 @@ impl RedrawEvents for State { indent: u64, level: u64, ) -> RepaintMode { - self.cmd_line.show_level(content, pos, firstc, prompt, indent, level); + let cursor = self.model.cur_point(); + let render_state = self.render_state.borrow(); + let (x, y, width, height) = cursor.to_area(render_state.font_ctx.cell_metrics()); + let ctx = CmdLineContext { + content, + pos, + firstc, + prompt, + indent, + level_idx: level, + x, + y, + width, + height, + max_width: self.drawing_area.get_allocated_width() - 20, + }; + + self.cmd_line.show_level(&ctx); RepaintMode::Nothing } diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index b4506e7..f7667a5 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -13,9 +13,11 @@ impl ModelLayout { } /// Wrap all lines into model - pub fn layout(&mut self, lines: Vec, Vec)>>, columns: u64) { - if lines.len() > self.model.rows || columns as usize > self.model.columns { - let model_cols = ((columns / ModelLayout::COLUMNS_STEP) + 1) * + /// + /// returns actual width + pub fn layout(&mut self, lines: Vec, Vec)>>, max_columns: u64) -> (u64, u64) { + if lines.len() > self.model.rows || max_columns as usize > self.model.columns { + let model_cols = ((max_columns / ModelLayout::COLUMNS_STEP) + 1) * ModelLayout::COLUMNS_STEP; let model_rows = ((lines.len() as u64 / ModelLayout::ROWS_STEP) + 1) * ModelLayout::ROWS_STEP; @@ -24,16 +26,21 @@ impl ModelLayout { } + let mut max_col_idx = 0; let mut col_idx = 0; let mut row_idx = 0; for content in lines { for (attr, ch_list) in content { for ch in ch_list { - if col_idx >= columns { + if col_idx >= max_columns { col_idx = 0; row_idx += 1; } + if max_col_idx < col_idx { + max_col_idx = col_idx; + } + self.model.set_cursor(row_idx, col_idx as usize); self.model.put(ch, false, attr.as_ref()); col_idx += 1; @@ -41,5 +48,7 @@ impl ModelLayout { } row_idx += 1; } + + (max_col_idx + 1, row_idx as u64) } } From 02cf3be95eb0b374c3a78fec7e035845a5d3e55e Mon Sep 17 00:00:00 2001 From: daa Date: Wed, 3 Jan 2018 23:20:22 +0300 Subject: [PATCH 10/59] Use preferred size, center position --- src/cmd_line.rs | 56 +++++++++++++++++++++++++----------- src/ui_model/model_layout.rs | 1 + 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index bd154a5..3bc5bc9 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -22,14 +22,15 @@ pub struct Level { } impl Level { - pub fn from(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { //TODO: double width chars render, also note in text wrapping //TODO: im let content_line: Vec<(Option, Vec)> = ctx.content .iter() - .map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect())) + .map(|c| { + (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) + }) .collect(); let prompt_lines = prompt_lines(&ctx.firstc, &ctx.prompt, ctx.indent); @@ -52,9 +53,20 @@ impl Level { let mut model_layout = ModelLayout::new(); let (columns, rows) = model_layout.layout(content, max_width_chars); + let columns = if columns < 5 { + 5 + } else { + columns + }; + let preferred_width = (char_width * columns as f64) as i32; let preferred_height = (line_height * rows as f64) as i32; - Level { model_layout, preferred_width, preferred_height } + + Level { + model_layout, + preferred_width, + preferred_height, + } } fn update_cache(&mut self, render_state: &shell::RenderState) { @@ -68,9 +80,17 @@ impl Level { fn prompt_lines(firstc: &str, prompt: &str, indent: u64) -> Vec<(Option, Vec)> { if !firstc.is_empty() { - vec![(None, firstc.chars().chain((0..indent).map(|_| ' ')).collect())] + vec![ + ( + None, + firstc.chars().chain((0..indent).map(|_| ' ')).collect() + ), + ] } else if !prompt.is_empty() { - prompt.lines().map(|l| (None, l.chars().collect())).collect() + prompt + .lines() + .map(|l| (None, l.chars().collect())) + .collect() } else { vec![] } @@ -110,14 +130,9 @@ impl CmdLine { popover.set_modal(false); popover.set_position(gtk::PositionType::Right); - let edit_frame = gtk::Frame::new(None); - edit_frame.set_shadow_type(gtk::ShadowType::In); let drawing_area = gtk::DrawingArea::new(); - drawing_area.set_size_request(150, 50); - edit_frame.add(&drawing_area); - edit_frame.show_all(); - - popover.add(&edit_frame); + drawing_area.show_all(); + popover.add(&drawing_area); let state = Arc::new(UiMutex::new(State::new(drawing_area.clone(), render_state))); let weak_cb = Arc::downgrade(&state); @@ -134,15 +149,20 @@ impl CmdLine { } } - pub fn show_level( - &mut self, - ctx: &CmdLineContext, - ) { + pub fn show_level(&mut self, ctx: &CmdLineContext) { let mut state = self.state.borrow_mut(); let mut level = Level::from(ctx, &*state.render_state.borrow()); level.update_cache(&*state.render_state.borrow()); + let preferred_height = if level.preferred_height < 40 { + 40 + } else { + level.preferred_height + }; + + state.drawing_area.set_size_request(level.preferred_width, preferred_height); + if ctx.level_idx as usize == state.levels.len() { // TODO: update level state.levels.pop(); @@ -187,6 +207,10 @@ fn gtk_draw( if let Some(level) = level { let render_state = state.render_state.borrow(); + let gap = state.drawing_area.get_allocated_height() - level.preferred_height; + if gap > 0 { + ctx.translate(0.0, gap as f64 / 2.0); + } render::render( ctx, diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index f7667a5..bbd67e8 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -16,6 +16,7 @@ impl ModelLayout { /// /// returns actual width pub fn layout(&mut self, lines: Vec, Vec)>>, max_columns: u64) -> (u64, u64) { + //FIXME: lines.len is not real lines count if lines.len() > self.model.rows || max_columns as usize > self.model.columns { let model_cols = ((max_columns / ModelLayout::COLUMNS_STEP) + 1) * ModelLayout::COLUMNS_STEP; From 0191579f4dbf0232bad7a2dcce72b573e5c0e606 Mon Sep 17 00:00:00 2001 From: daa Date: Mon, 8 Jan 2018 23:14:17 +0300 Subject: [PATCH 11/59] Fix wrapping calculation --- src/ui_model/model_layout.rs | 44 ++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index bbd67e8..7314090 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -15,13 +15,18 @@ impl ModelLayout { /// Wrap all lines into model /// /// returns actual width - pub fn layout(&mut self, lines: Vec, Vec)>>, max_columns: u64) -> (u64, u64) { - //FIXME: lines.len is not real lines count - if lines.len() > self.model.rows || max_columns as usize > self.model.columns { + pub fn layout( + &mut self, + lines: Vec, Vec)>>, + max_columns: u64, + ) -> (u64, u64) { + let rows = ModelLayout::count_lines(&lines, max_columns); + + if rows as usize > self.model.rows || max_columns as usize > self.model.columns { let model_cols = ((max_columns / ModelLayout::COLUMNS_STEP) + 1) * ModelLayout::COLUMNS_STEP; - let model_rows = ((lines.len() as u64 / ModelLayout::ROWS_STEP) + 1) * - ModelLayout::ROWS_STEP; + + let model_rows = ((rows as u64 / ModelLayout::ROWS_STEP) + 1) * ModelLayout::ROWS_STEP; self.model = UiModel::new(model_rows, model_cols); } @@ -36,6 +41,8 @@ impl ModelLayout { if col_idx >= max_columns { col_idx = 0; row_idx += 1; + } else { + col_idx += 1; } if max_col_idx < col_idx { @@ -44,12 +51,35 @@ impl ModelLayout { self.model.set_cursor(row_idx, col_idx as usize); self.model.put(ch, false, attr.as_ref()); - col_idx += 1; } } row_idx += 1; } - (max_col_idx + 1, row_idx as u64) + (max_col_idx + 1, rows) + } + + fn count_lines(lines: &Vec, Vec)>>, max_columns: u64) -> u64 { + let mut row_count = 0; + + for line in lines { + let len: usize = line.iter().map(|c| c.1.len()).sum(); + row_count += len as u64 / (max_columns + 1) + 1; + } + + row_count + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_count_lines() { + let lines = vec![vec![(None, vec!['a'; 5])]]; + + let rows = ModelLayout::count_lines(&lines, 4); + assert_eq!(2, rows); } } From 3d1c73ee2a3b07f494287545006d579f089e8d55 Mon Sep 17 00:00:00 2001 From: daa Date: Thu, 11 Jan 2018 22:44:19 +0300 Subject: [PATCH 12/59] Start block implementation --- src/cmd_line.rs | 26 ++++++++++++-------------- src/nvim/redraw_handler.rs | 16 ++++++++++++---- src/shell.rs | 15 +++++++++++++++ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 3bc5bc9..6be50ee 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::rc::Rc; use std::sync::Arc; use std::cell::RefCell; +use std::cmp::max; use gtk; use gtk::prelude::*; @@ -22,9 +23,10 @@ pub struct Level { } impl Level { - pub fn from(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { + pub fn from_ctx(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { //TODO: double width chars render, also note in text wrapping //TODO: im + //TODO: cursor let content_line: Vec<(Option, Vec)> = ctx.content .iter() @@ -53,11 +55,7 @@ impl Level { let mut model_layout = ModelLayout::new(); let (columns, rows) = model_layout.layout(content, max_width_chars); - let columns = if columns < 5 { - 5 - } else { - columns - }; + let columns = max(columns, 5); let preferred_width = (char_width * columns as f64) as i32; let preferred_height = (line_height * rows as f64) as i32; @@ -98,6 +96,7 @@ fn prompt_lines(firstc: &str, prompt: &str, indent: u64) -> Vec<(Option, struct State { levels: Vec, + block: Option, render_state: Rc>, drawing_area: gtk::DrawingArea, } @@ -106,6 +105,7 @@ impl State { fn new(drawing_area: gtk::DrawingArea, render_state: Rc>) -> Self { State { levels: Vec::new(), + block: None, render_state, drawing_area, } @@ -152,16 +152,10 @@ impl CmdLine { pub fn show_level(&mut self, ctx: &CmdLineContext) { let mut state = self.state.borrow_mut(); - let mut level = Level::from(ctx, &*state.render_state.borrow()); + let mut level = Level::from_ctx(ctx, &*state.render_state.borrow()); level.update_cache(&*state.render_state.borrow()); - let preferred_height = if level.preferred_height < 40 { - 40 - } else { - level.preferred_height - }; - - state.drawing_area.set_size_request(level.preferred_width, preferred_height); + state.drawing_area.set_size_request(level.preferred_width, max(level.preferred_height, 40)); if ctx.level_idx as usize == state.levels.len() { // TODO: update level @@ -195,6 +189,10 @@ impl CmdLine { self.displyed = false; } } + + + pub fn show_block(&mut self, content: Vec, String)>>) { + } } fn gtk_draw( diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 86432e7..1b6883b 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -76,10 +76,17 @@ pub trait RedrawEvents { indent: u64, level: u64, ) -> RepaintMode; - - fn cmdline_hide( + + fn cmdline_hide(&mut self, level: u64) -> RepaintMode; + + fn cmdline_block_show( &mut self, - level: u64, + content: Vec, String)>>, + ) -> RepaintMode; + + fn cmdline_block_append( + &mut self, + content: Vec, String)>>, ) -> RepaintMode; } @@ -261,6 +268,8 @@ pub fn call( ui.mode_info_set(try_bool!(args[0]), mode_info) } "cmdline_show" => call!(ui->cmdline_show(args: ext, uint, str, str, uint, uint)), + "cmdline_block_show" => call!(ui->cmdline_block_show(args: ext)), + "cmdline_block_append" => call!(ui->cmdline_block_append(args: ext)), "cmdline_hide" => call!(ui->cmdline_hide(args: uint)), _ => { println!("Event {}({:?})", method, args); @@ -292,4 +301,3 @@ impl<'a> CompleteItem<'a> { .collect() } } - diff --git a/src/shell.rs b/src/shell.rs index 46c3faf..b2fd4fb 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1211,6 +1211,21 @@ impl RedrawEvents for State { self.cmd_line.hide_level(level); RepaintMode::Nothing } + + fn cmdline_block_show( + &mut self, + content: Vec, String)>>, + ) -> RepaintMode { + self.cmd_line.show_block(content); + RepaintMode::Nothing + } + + fn cmdline_block_append( + &mut self, + content: Vec, String)>>, + ) -> RepaintMode { + RepaintMode::Nothing + } } impl CursorRedrawCb for State { From cc082ab6f7864f2d1db86d5728a5e641565a062b Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 13 Jan 2018 18:02:47 +0300 Subject: [PATCH 13/59] Build block --- src/cmd_line.rs | 121 +++++++++++++++++++++++++++---------- src/nvim/redraw_handler.rs | 3 + src/shell.rs | 17 +++++- 3 files changed, 107 insertions(+), 34 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 6be50ee..99c3871 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -23,11 +23,65 @@ pub struct Level { } impl Level { - pub fn from_ctx(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { - //TODO: double width chars render, also note in text wrapping - //TODO: im - //TODO: cursor + //TODO: double width chars render, also note in text wrapping + //TODO: im + //TODO: cursor + + pub fn append_line(&mut self, + content: &Vec, String)>>) { + // TODO: implement + } + + pub fn from_multiline_content( + content: &Vec, String)>>, + max_width: i32, + render_state: &shell::RenderState, + ) -> Self { + let lines: Vec, Vec)>> = content + .iter() + .map(|line_chars| { + line_chars + .iter() + .map(|c| { + (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) + }) + .collect() + }) + .collect(); + + Level::from_lines(lines, max_width, render_state) + } + + pub fn from_lines( + lines: Vec, Vec)>>, + max_width: i32, + render_state: &shell::RenderState, + ) -> Self { + let &CellMetrics { + line_height, + char_width, + .. + } = render_state.font_ctx.cell_metrics(); + + let max_width_chars = (max_width as f64 / char_width) as u64; + + let mut model_layout = ModelLayout::new(); + let (columns, rows) = model_layout.layout(lines, max_width_chars); + + let columns = max(columns, 5); + + let preferred_width = (char_width * columns as f64) as i32; + let preferred_height = (line_height * rows as f64) as i32; + + Level { + model_layout, + preferred_width, + preferred_height, + } + } + + pub fn from_ctx(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { let content_line: Vec<(Option, Vec)> = ctx.content .iter() .map(|c| { @@ -44,27 +98,7 @@ impl Level { content.last_mut().map(|line| line.extend(content_line)); } - let &CellMetrics { - line_height, - char_width, - .. - } = render_state.font_ctx.cell_metrics(); - - let max_width_chars = (ctx.max_width as f64 / char_width) as u64; - - let mut model_layout = ModelLayout::new(); - let (columns, rows) = model_layout.layout(content, max_width_chars); - - let columns = max(columns, 5); - - let preferred_width = (char_width * columns as f64) as i32; - let preferred_height = (line_height * rows as f64) as i32; - - Level { - model_layout, - preferred_width, - preferred_height, - } + Level::from_lines(content, ctx.max_width, render_state) } fn update_cache(&mut self, render_state: &shell::RenderState) { @@ -81,7 +115,7 @@ fn prompt_lines(firstc: &str, prompt: &str, indent: u64) -> Vec<(Option, vec![ ( None, - firstc.chars().chain((0..indent).map(|_| ' ')).collect() + firstc.chars().chain((0..indent).map(|_| ' ')).collect(), ), ] } else if !prompt.is_empty() { @@ -138,9 +172,7 @@ impl CmdLine { let weak_cb = Arc::downgrade(&state); let cursor = cursor::Cursor::new(weak_cb); - drawing_area.connect_draw( - clone!(state => move |_, ctx| gtk_draw(ctx, &state, &cursor)), - ); + drawing_area.connect_draw(clone!(state => move |_, ctx| gtk_draw(ctx, &state, &cursor))); CmdLine { popover, @@ -155,7 +187,10 @@ impl CmdLine { let mut level = Level::from_ctx(ctx, &*state.render_state.borrow()); level.update_cache(&*state.render_state.borrow()); - state.drawing_area.set_size_request(level.preferred_width, max(level.preferred_height, 40)); + // TODO: request size depends on leve size + block size + state + .drawing_area + .set_size_request(level.preferred_width, max(level.preferred_height, 40)); if ctx.level_idx as usize == state.levels.len() { // TODO: update level @@ -190,8 +225,31 @@ impl CmdLine { } } + pub fn show_block( + &mut self, + content: &Vec, String)>>, + max_width: i32, + ) { + let mut state = self.state.borrow_mut(); + let mut block = Level::from_multiline_content(content, max_width, &*state.render_state.borrow()); + block.update_cache(&*state.render_state.borrow()); + state.block = Some(block); + } - pub fn show_block(&mut self, content: Vec, String)>>) { + + pub fn block_append( + &mut self, + content: &Vec, String)>>, + ) { + let mut state = self.state.borrow_mut(); + let render_state = state.render_state.clone(); + let block = state.block.as_mut().unwrap(); + block.append_line(content); + block.update_cache(& *render_state.borrow()); + } + + pub fn block_hide(&mut self) { + self.state.borrow_mut().block = None; } } @@ -203,6 +261,7 @@ fn gtk_draw( let state = state.borrow(); let level = state.levels.last(); + //TODO: draw block if let Some(level) = level { let render_state = state.render_state.borrow(); let gap = state.drawing_area.get_allocated_height() - level.preferred_height; diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 1b6883b..3db3da2 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -88,6 +88,8 @@ pub trait RedrawEvents { &mut self, content: Vec, String)>>, ) -> RepaintMode; + + fn cmdline_block_hide(&mut self) -> RepaintMode; } pub trait GuiApi { @@ -271,6 +273,7 @@ pub fn call( "cmdline_block_show" => call!(ui->cmdline_block_show(args: ext)), "cmdline_block_append" => call!(ui->cmdline_block_append(args: ext)), "cmdline_hide" => call!(ui->cmdline_hide(args: uint)), + "cmdline_block_hide" => ui.cmdline_block_hide(), _ => { println!("Event {}({:?})", method, args); RepaintMode::Nothing diff --git a/src/shell.rs b/src/shell.rs index b2fd4fb..2b94aed 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -391,6 +391,10 @@ impl State { } } + + fn max_popup_width(&self) -> i32 { + self.drawing_area.get_allocated_width() - 20 + } } pub struct UiState { @@ -1138,7 +1142,7 @@ impl RedrawEvents for State { y, width, height, - max_width: self.drawing_area.get_allocated_width() - 20, + max_width: self.max_popup_width(), }; self.popup_menu.show(context); @@ -1200,7 +1204,7 @@ impl RedrawEvents for State { y, width, height, - max_width: self.drawing_area.get_allocated_width() - 20, + max_width: self.max_popup_width(), }; self.cmd_line.show_level(&ctx); @@ -1216,7 +1220,8 @@ impl RedrawEvents for State { &mut self, content: Vec, String)>>, ) -> RepaintMode { - self.cmd_line.show_block(content); + let max_width = self.max_popup_width(); + self.cmd_line.show_block(&content, max_width); RepaintMode::Nothing } @@ -1224,6 +1229,12 @@ impl RedrawEvents for State { &mut self, content: Vec, String)>>, ) -> RepaintMode { + self.cmd_line.block_append(&content); + RepaintMode::Nothing + } + + fn cmdline_block_hide(&mut self) -> RepaintMode { + self.cmd_line.block_hide(); RepaintMode::Nothing } } From 2fb6b7a556071d29e2ab3762b14d48e14747bab4 Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 13 Jan 2018 21:32:21 +0300 Subject: [PATCH 14/59] Change model size without data loss --- src/cmd_line.rs | 15 +++++++++++-- src/ui_model/mod.rs | 10 +++++++++ src/ui_model/model_layout.rs | 43 ++++++++++++++++++++++-------------- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 99c3871..19ee4d4 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -30,7 +30,18 @@ impl Level { pub fn append_line(&mut self, content: &Vec, String)>>) { - // TODO: implement + let lines: Vec, Vec)>> = content + .iter() + .map(|line_chars| { + line_chars + .iter() + .map(|c| { + (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) + }) + .collect() + }) + .collect(); + //TODO: implement } pub fn from_multiline_content( @@ -64,7 +75,7 @@ impl Level { .. } = render_state.font_ctx.cell_metrics(); - let max_width_chars = (max_width as f64 / char_width) as u64; + let max_width_chars = (max_width as f64 / char_width) as usize; let mut model_layout = ModelLayout::new(); let (columns, rows) = model_layout.layout(lines, max_width_chars); diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index 277ae25..5bb14e5 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -121,6 +121,16 @@ impl UiModel { self.right = right as usize; } + /// Copy rows from 0 to to_row, col from 0 self.columns + /// + /// Don't do any validation! + pub fn copy_rows(&self, target: &mut UiModel, to_row: usize) { + for (row_idx, line) in self.model[0..to_row + 1].iter().enumerate() { + let mut target_row = &mut target.model[row_idx]; + line.copy_to(target_row, 0, self.columns); + } + } + #[inline] fn copy_row(&mut self, target_row: i64, offset: i64, left_col: usize, right_col: usize) { debug_assert_ne!(0, offset); diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index 7314090..04a9b0d 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -1,15 +1,31 @@ -use ui_model::{UiModel, Attrs}; +use ui_model::{Attrs, UiModel}; pub struct ModelLayout { pub model: UiModel, + rows_filled: usize, } impl ModelLayout { - const COLUMNS_STEP: u64 = 50; - const ROWS_STEP: u64 = 10; + const COLUMNS_STEP: usize = 50; + const ROWS_STEP: usize = 10; pub fn new() -> Self { - ModelLayout { model: UiModel::new(ModelLayout::ROWS_STEP, ModelLayout::COLUMNS_STEP) } + ModelLayout { + model: UiModel::new(ModelLayout::ROWS_STEP as u64, ModelLayout::COLUMNS_STEP as u64), + rows_filled: 0, + } + } + + fn check_model_size(&mut self, rows: usize, columns: usize) { + if rows > self.model.rows || columns > self.model.columns { + let model_cols = + ((columns / ModelLayout::COLUMNS_STEP) + 1) * ModelLayout::COLUMNS_STEP; + + let model_rows = ((rows / ModelLayout::ROWS_STEP) + 1) * ModelLayout::ROWS_STEP; + + let mut model = UiModel::new(model_rows as u64, model_cols as u64); + self.model.copy_rows(&mut model, self.rows_filled); + } } /// Wrap all lines into model @@ -18,19 +34,12 @@ impl ModelLayout { pub fn layout( &mut self, lines: Vec, Vec)>>, - max_columns: u64, - ) -> (u64, u64) { + max_columns: usize, + ) -> (usize, usize) { let rows = ModelLayout::count_lines(&lines, max_columns); - if rows as usize > self.model.rows || max_columns as usize > self.model.columns { - let model_cols = ((max_columns / ModelLayout::COLUMNS_STEP) + 1) * - ModelLayout::COLUMNS_STEP; - - let model_rows = ((rows as u64 / ModelLayout::ROWS_STEP) + 1) * ModelLayout::ROWS_STEP; - - self.model = UiModel::new(model_rows, model_cols); - } - + self.check_model_size(rows, max_columns); + self.rows_filled = rows; let mut max_col_idx = 0; let mut col_idx = 0; @@ -59,12 +68,12 @@ impl ModelLayout { (max_col_idx + 1, rows) } - fn count_lines(lines: &Vec, Vec)>>, max_columns: u64) -> u64 { + fn count_lines(lines: &Vec, Vec)>>, max_columns: usize) -> usize { let mut row_count = 0; for line in lines { let len: usize = line.iter().map(|c| c.1.len()).sum(); - row_count += len as u64 / (max_columns + 1) + 1; + row_count += len / (max_columns + 1) + 1; } row_count From 86be47e0ec068613acffa8a8d0dac5e6da7fd33d Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 14 Jan 2018 12:25:59 +0300 Subject: [PATCH 15/59] Append block, update level instead of recreate on every user input --- src/cmd_line.rs | 162 ++++++++++++++++++++++------------- src/input.rs | 2 - src/ui_model/model_layout.rs | 42 +++++---- 3 files changed, 129 insertions(+), 77 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 19ee4d4..a431cec 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -27,10 +27,38 @@ impl Level { //TODO: im //TODO: cursor + pub fn replace_from_ctx(&mut self, ctx: &CmdLineContext, render_state: &shell::RenderState) { + self.replace_line(&ctx.get_lines(), render_state, false); + } - pub fn append_line(&mut self, - content: &Vec, String)>>) { - let lines: Vec, Vec)>> = content + fn replace_line( + &mut self, + lines: &Vec, Vec)>>, + render_state: &shell::RenderState, + append: bool, + ) { + let &CellMetrics { + line_height, + char_width, + .. + } = render_state.font_ctx.cell_metrics(); + + let (columns, rows) = if append { + self.model_layout.layout_append(lines) + } else { + self.model_layout.layout(lines) + }; + + let columns = max(columns, 5); + + self.preferred_width = (char_width * columns as f64) as i32; + self.preferred_height = (line_height * rows as f64) as i32; + } + + fn to_attributed_content( + content: &Vec, String)>>, + ) -> Vec, Vec)>> { + content .iter() .map(|line_chars| { line_chars @@ -40,8 +68,7 @@ impl Level { }) .collect() }) - .collect(); - //TODO: implement + .collect() } pub fn from_multiline_content( @@ -49,23 +76,15 @@ impl Level { max_width: i32, render_state: &shell::RenderState, ) -> Self { - let lines: Vec, Vec)>> = content - .iter() - .map(|line_chars| { - line_chars - .iter() - .map(|c| { - (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) - }) - .collect() - }) - .collect(); - - Level::from_lines(lines, max_width, render_state) + Level::from_lines( + &Level::to_attributed_content(content), + max_width, + render_state, + ) } pub fn from_lines( - lines: Vec, Vec)>>, + lines: &Vec, Vec)>>, max_width: i32, render_state: &shell::RenderState, ) -> Self { @@ -75,10 +94,10 @@ impl Level { .. } = render_state.font_ctx.cell_metrics(); - let max_width_chars = (max_width as f64 / char_width) as usize; + let max_width_chars = (max_width as f64 / char_width) as u64; - let mut model_layout = ModelLayout::new(); - let (columns, rows) = model_layout.layout(lines, max_width_chars); + let mut model_layout = ModelLayout::new(max_width_chars); + let (columns, rows) = model_layout.layout(lines); let columns = max(columns, 5); @@ -93,23 +112,7 @@ impl Level { } pub fn from_ctx(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { - let content_line: Vec<(Option, Vec)> = ctx.content - .iter() - .map(|c| { - (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) - }) - .collect(); - let prompt_lines = prompt_lines(&ctx.firstc, &ctx.prompt, ctx.indent); - - let mut content: Vec<_> = prompt_lines.into_iter().map(|line| vec![line]).collect(); - - if content.is_empty() { - content.push(content_line); - } else { - content.last_mut().map(|line| line.extend(content_line)); - } - - Level::from_lines(content, ctx.max_width, render_state) + Level::from_lines(&ctx.get_lines(), ctx.max_width, render_state) } fn update_cache(&mut self, render_state: &shell::RenderState) { @@ -194,20 +197,36 @@ impl CmdLine { pub fn show_level(&mut self, ctx: &CmdLineContext) { let mut state = self.state.borrow_mut(); - - let mut level = Level::from_ctx(ctx, &*state.render_state.borrow()); - level.update_cache(&*state.render_state.borrow()); - - // TODO: request size depends on leve size + block size - state - .drawing_area - .set_size_request(level.preferred_width, max(level.preferred_height, 40)); + let render_state = state.render_state.clone(); + let render_state = render_state.borrow(); if ctx.level_idx as usize == state.levels.len() { - // TODO: update level - state.levels.pop(); + state + .levels + .last_mut() + .unwrap() + .replace_from_ctx(ctx, &*render_state); + } else { + let level = Level::from_ctx(ctx, &*render_state); + state.levels.push(level); } - state.levels.push(level); + + + + let drawing_area = state.drawing_area.clone(); + let block_height = state + .block + .as_ref() + .map(|b| b.preferred_height) + .unwrap_or(0); + let level = state.levels.last_mut().unwrap(); + + level.update_cache(&*render_state); + drawing_area.set_size_request( + level.preferred_width, + max(block_height + level.preferred_height, 40), + ); + if !self.displyed { self.displyed = true; self.popover.set_pointing_to(>k::Rectangle { @@ -219,7 +238,7 @@ impl CmdLine { self.popover.popup(); } else { - state.drawing_area.queue_draw() + drawing_area.queue_draw() } } @@ -242,21 +261,25 @@ impl CmdLine { max_width: i32, ) { let mut state = self.state.borrow_mut(); - let mut block = Level::from_multiline_content(content, max_width, &*state.render_state.borrow()); + let mut block = + Level::from_multiline_content(content, max_width, &*state.render_state.borrow()); block.update_cache(&*state.render_state.borrow()); state.block = Some(block); + //TODO: drawing size update } - pub fn block_append( - &mut self, - content: &Vec, String)>>, - ) { + pub fn block_append(&mut self, content: &Vec, String)>>) { let mut state = self.state.borrow_mut(); let render_state = state.render_state.clone(); let block = state.block.as_mut().unwrap(); - block.append_line(content); - block.update_cache(& *render_state.borrow()); + block.replace_line( + &Level::to_attributed_content(content), + &*render_state.borrow(), + true, + ); + block.update_cache(&*render_state.borrow()); + //TODO: drawing size update } pub fn block_hide(&mut self) { @@ -280,6 +303,7 @@ fn gtk_draw( ctx.translate(0.0, gap as f64 / 2.0); } + //TODO: limit model to row filled render::render( ctx, cursor, @@ -305,3 +329,25 @@ pub struct CmdLineContext { pub height: i32, pub max_width: i32, } + +impl CmdLineContext { + fn get_lines(&self) -> Vec, Vec)>> { + let content_line: Vec<(Option, Vec)> = self.content + .iter() + .map(|c| { + (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) + }) + .collect(); + let prompt_lines = prompt_lines(&self.firstc, &self.prompt, self.indent); + + let mut content: Vec<_> = prompt_lines.into_iter().map(|line| vec![line]).collect(); + + if content.is_empty() { + content.push(content_line); + } else { + content.last_mut().map(|line| line.extend(content_line)); + } + + content + } +} diff --git a/src/input.rs b/src/input.rs index b650258..e0785f1 100644 --- a/src/input.rs +++ b/src/input.rs @@ -5,8 +5,6 @@ use gdk::EventKey; use phf; use neovim_lib::{Neovim, NeovimApi}; -use std::ascii::AsciiExt; - include!(concat!(env!("OUT_DIR"), "/key_map_table.rs")); diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index 04a9b0d..5494865 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -6,21 +6,29 @@ pub struct ModelLayout { } impl ModelLayout { - const COLUMNS_STEP: usize = 50; const ROWS_STEP: usize = 10; - pub fn new() -> Self { + pub fn new(columns: u64) -> Self { ModelLayout { - model: UiModel::new(ModelLayout::ROWS_STEP as u64, ModelLayout::COLUMNS_STEP as u64), + model: UiModel::new(ModelLayout::ROWS_STEP as u64, columns), rows_filled: 0, } } - fn check_model_size(&mut self, rows: usize, columns: usize) { - if rows > self.model.rows || columns > self.model.columns { - let model_cols = - ((columns / ModelLayout::COLUMNS_STEP) + 1) * ModelLayout::COLUMNS_STEP; + pub fn layout_append(&mut self, lines: &Vec, Vec)>>) -> (usize, usize) { + let rows_filled = self.rows_filled; + self.layout_replace(rows_filled, lines) + } + pub fn layout(&mut self, + lines: &Vec, Vec)>>, + ) -> (usize, usize) { + self.layout_replace(0, lines) + } + + fn check_model_size(&mut self, rows: usize) { + if rows > self.model.rows { + let model_cols = self.model.columns; let model_rows = ((rows / ModelLayout::ROWS_STEP) + 1) * ModelLayout::ROWS_STEP; let mut model = UiModel::new(model_rows as u64, model_cols as u64); @@ -31,23 +39,23 @@ impl ModelLayout { /// Wrap all lines into model /// /// returns actual width - pub fn layout( + fn layout_replace( &mut self, - lines: Vec, Vec)>>, - max_columns: usize, + row_offset: usize, + lines: &Vec, Vec)>>, ) -> (usize, usize) { - let rows = ModelLayout::count_lines(&lines, max_columns); + let rows = ModelLayout::count_lines(&lines, self.model.columns); - self.check_model_size(rows, max_columns); - self.rows_filled = rows; + self.check_model_size(rows + row_offset); + self.rows_filled = rows + row_offset; let mut max_col_idx = 0; let mut col_idx = 0; - let mut row_idx = 0; + let mut row_idx = row_offset; for content in lines { - for (attr, ch_list) in content { + for &(ref attr, ref ch_list) in content { for ch in ch_list { - if col_idx >= max_columns { + if col_idx >= self.model.columns { col_idx = 0; row_idx += 1; } else { @@ -59,7 +67,7 @@ impl ModelLayout { } self.model.set_cursor(row_idx, col_idx as usize); - self.model.put(ch, false, attr.as_ref()); + self.model.put(*ch, false, attr.as_ref()); } } row_idx += 1; From 642fd772f04120993939bf71c4ce680d155c37c4 Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 14 Jan 2018 22:18:40 +0300 Subject: [PATCH 16/59] Test, fix model layout calculation, fix build --- appveyor.yml | 2 +- src/sys/pango/item.rs | 5 ----- src/ui_model/mod.rs | 2 +- src/ui_model/model_layout.rs | 38 +++++++++++++++++++++++++----------- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 36154b2..99dd72a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,7 @@ environment: PROJECT_NAME: rustfmt matrix: - TARGET: x86_64-pc-windows-gnu - RUST_VERSION: 1.20.0 + RUST_VERSION: 1.23.0 install: # - ps: Start-FileDownload "https://static.rust-lang.org/dist/channel-rust-stable" diff --git a/src/sys/pango/item.rs b/src/sys/pango/item.rs index 0237edb..036ecbf 100644 --- a/src/sys/pango/item.rs +++ b/src/sys/pango/item.rs @@ -20,11 +20,6 @@ glib_wrapper! { } impl Item { - #[cfg(test)] - pub fn new() -> Self { - unsafe { from_glib_full(pango_sys::pango_item_new()) } - } - pub fn analysis(&self) -> analysis::Analysis { analysis::Analysis::from(&self.0.analysis) } diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index 5bb14e5..1ac54f9 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -127,7 +127,7 @@ impl UiModel { pub fn copy_rows(&self, target: &mut UiModel, to_row: usize) { for (row_idx, line) in self.model[0..to_row + 1].iter().enumerate() { let mut target_row = &mut target.model[row_idx]; - line.copy_to(target_row, 0, self.columns); + line.copy_to(target_row, 0, self.columns - 1); } } diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index 5494865..7a482e6 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -29,10 +29,11 @@ impl ModelLayout { fn check_model_size(&mut self, rows: usize) { if rows > self.model.rows { let model_cols = self.model.columns; - let model_rows = ((rows / ModelLayout::ROWS_STEP) + 1) * ModelLayout::ROWS_STEP; + let model_rows = ((rows / (ModelLayout::ROWS_STEP + 1)) + 1) * ModelLayout::ROWS_STEP; let mut model = UiModel::new(model_rows as u64, model_cols as u64); - self.model.copy_rows(&mut model, self.rows_filled); + self.model.copy_rows(&mut model, self.rows_filled - 1); + self.model = model; } } @@ -55,25 +56,25 @@ impl ModelLayout { for content in lines { for &(ref attr, ref ch_list) in content { for ch in ch_list { - if col_idx >= self.model.columns { - col_idx = 0; - row_idx += 1; - } else { - col_idx += 1; - } + self.model.set_cursor(row_idx, col_idx as usize); + self.model.put(*ch, false, attr.as_ref()); if max_col_idx < col_idx { max_col_idx = col_idx; } - self.model.set_cursor(row_idx, col_idx as usize); - self.model.put(*ch, false, attr.as_ref()); + col_idx += 1; + + if col_idx >= self.model.columns { + col_idx = 0; + row_idx += 1; + } } } row_idx += 1; } - (max_col_idx + 1, rows) + (max_col_idx + 1, self.rows_filled) } fn count_lines(lines: &Vec, Vec)>>, max_columns: usize) -> usize { @@ -99,4 +100,19 @@ mod tests { let rows = ModelLayout::count_lines(&lines, 4); assert_eq!(2, rows); } + + #[test] + fn test_resize() { + let lines = vec![vec![(None, vec!['a'; 5])]; ModelLayout::ROWS_STEP]; + let mut model = ModelLayout::new(5); + + let (cols, rows) = model.layout(&lines); + assert_eq!(5, cols); + assert_eq!(ModelLayout::ROWS_STEP, rows); + + let (cols, rows) = model.layout_append(&lines); + assert_eq!(5, cols); + assert_eq!(ModelLayout::ROWS_STEP * 2, rows); + assert_eq!(ModelLayout::ROWS_STEP * 2, model.model.rows); + } } From 6a782ffc9dbab20049122248898504df6ac3a7d9 Mon Sep 17 00:00:00 2001 From: daa Date: Mon, 15 Jan 2018 00:18:32 +0300 Subject: [PATCH 17/59] Draw block --- src/cmd_line.rs | 95 +++++++++++++++++++++++--------------- src/ui_model/model_rect.rs | 4 +- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index a431cec..352afd0 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -158,6 +158,20 @@ impl State { drawing_area, } } + + fn request_area_size(&self) { + let drawing_area = self.drawing_area.clone(); + let block = self.block.as_ref(); + let level = self.levels.last(); + + let (block_width, block_height) = block.map(|b| (b.preferred_width, b.preferred_height)).unwrap_or((0, 0)); + let (level_width, level_height) = level.map(|l| (l.preferred_width, l.preferred_height)).unwrap_or((0, 0)); + + drawing_area.set_size_request( + max(level_width, block_width), + max(block_height + level_height, 40), + ); + } } impl cursor::CursorRedrawCb for State { @@ -201,31 +215,16 @@ impl CmdLine { let render_state = render_state.borrow(); if ctx.level_idx as usize == state.levels.len() { - state - .levels - .last_mut() - .unwrap() - .replace_from_ctx(ctx, &*render_state); + let level = state.levels.last_mut().unwrap(); + level.replace_from_ctx(ctx, &*render_state); + level.update_cache(&*render_state); } else { - let level = Level::from_ctx(ctx, &*render_state); + let mut level = Level::from_ctx(ctx, &*render_state); + level.update_cache(&*render_state); state.levels.push(level); } - - - let drawing_area = state.drawing_area.clone(); - let block_height = state - .block - .as_ref() - .map(|b| b.preferred_height) - .unwrap_or(0); - let level = state.levels.last_mut().unwrap(); - - level.update_cache(&*render_state); - drawing_area.set_size_request( - level.preferred_width, - max(block_height + level.preferred_height, 40), - ); + state.request_area_size(); if !self.displyed { self.displyed = true; @@ -238,7 +237,7 @@ impl CmdLine { self.popover.popup(); } else { - drawing_area.queue_draw() + state.drawing_area.queue_draw() } } @@ -265,21 +264,23 @@ impl CmdLine { Level::from_multiline_content(content, max_width, &*state.render_state.borrow()); block.update_cache(&*state.render_state.borrow()); state.block = Some(block); - //TODO: drawing size update + state.request_area_size(); } pub fn block_append(&mut self, content: &Vec, String)>>) { let mut state = self.state.borrow_mut(); let render_state = state.render_state.clone(); - let block = state.block.as_mut().unwrap(); - block.replace_line( - &Level::to_attributed_content(content), - &*render_state.borrow(), - true, - ); - block.update_cache(&*render_state.borrow()); - //TODO: drawing size update + { + let block = state.block.as_mut().unwrap(); + block.replace_line( + &Level::to_attributed_content(content), + &*render_state.borrow(), + true, + ); + block.update_cache(&*render_state.borrow()); + } + state.request_area_size(); } pub fn block_hide(&mut self) { @@ -294,15 +295,33 @@ fn gtk_draw( ) -> Inhibit { let state = state.borrow(); let level = state.levels.last(); + let block = state.block.as_ref(); + + let preferred_height = level.map(|l| l.preferred_height).unwrap_or(0) + + block.as_ref().map(|b| b.preferred_height).unwrap_or(0); + + let render_state = state.render_state.borrow(); + + let gap = state.drawing_area.get_allocated_height() - preferred_height; + if gap > 0 { + ctx.translate(0.0, gap as f64 / 2.0); + } + + if let Some(block) = block { + // TODO: disable cursor + render::render( + ctx, + cursor, + &render_state.font_ctx, + &block.model_layout.model, + &render_state.color_model, + &render_state.mode, + ); + + ctx.translate(0.0, block.preferred_height as f64); + } - //TODO: draw block if let Some(level) = level { - let render_state = state.render_state.borrow(); - let gap = state.drawing_area.get_allocated_height() - level.preferred_height; - if gap > 0 { - ctx.translate(0.0, gap as f64 / 2.0); - } - //TODO: limit model to row filled render::render( ctx, diff --git a/src/ui_model/model_rect.rs b/src/ui_model/model_rect.rs index 8fad371..9c7873e 100644 --- a/src/ui_model/model_rect.rs +++ b/src/ui_model/model_rect.rs @@ -56,8 +56,8 @@ pub struct ModelRect { impl ModelRect { pub fn new(top: usize, bot: usize, left: usize, right: usize) -> ModelRect { - debug_assert!(top <= bot); - debug_assert!(left <= right); + debug_assert!(top <= bot, "{} <= {}", top, bot); + debug_assert!(left <= right, "{} <= {}", left, right); ModelRect { top: top, From 29cf31914d9f2b533ab177aa2e3a144204e9ec35 Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 21 Jan 2018 14:14:07 +0300 Subject: [PATCH 18/59] Numver of fixes --- src/cmd_line.rs | 25 ++++++++++++++++--------- src/nvim/redraw_handler.rs | 2 +- src/render/model_clip_iterator.rs | 16 +++++++++------- src/shell.rs | 2 +- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 352afd0..cb1f0e0 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -164,8 +164,12 @@ impl State { let block = self.block.as_ref(); let level = self.levels.last(); - let (block_width, block_height) = block.map(|b| (b.preferred_width, b.preferred_height)).unwrap_or((0, 0)); - let (level_width, level_height) = level.map(|l| (l.preferred_width, l.preferred_height)).unwrap_or((0, 0)); + let (block_width, block_height) = block + .map(|b| (b.preferred_width, b.preferred_height)) + .unwrap_or((0, 0)); + let (level_width, level_height) = level + .map(|l| (l.preferred_width, l.preferred_height)) + .unwrap_or((0, 0)); drawing_area.set_size_request( max(level_width, block_width), @@ -268,16 +272,19 @@ impl CmdLine { } - pub fn block_append(&mut self, content: &Vec, String)>>) { + pub fn block_append(&mut self, content: &Vec<(HashMap, String)>) { let mut state = self.state.borrow_mut(); let render_state = state.render_state.clone(); { + let attr_content = content + .iter() + .map(|c| { + (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) + }) + .collect(); + let block = state.block.as_mut().unwrap(); - block.replace_line( - &Level::to_attributed_content(content), - &*render_state.borrow(), - true, - ); + block.replace_line(&vec![attr_content], &*render_state.borrow(), true); block.update_cache(&*render_state.borrow()); } state.request_area_size(); @@ -297,7 +304,7 @@ fn gtk_draw( let level = state.levels.last(); let block = state.block.as_ref(); - let preferred_height = level.map(|l| l.preferred_height).unwrap_or(0) + let preferred_height = level.map(|l| l.preferred_height).unwrap_or(0) + block.as_ref().map(|b| b.preferred_height).unwrap_or(0); let render_state = state.render_state.borrow(); diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 3db3da2..1e6feb6 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -86,7 +86,7 @@ pub trait RedrawEvents { fn cmdline_block_append( &mut self, - content: Vec, String)>>, + content: Vec<(HashMap, String)>, ) -> RepaintMode; fn cmdline_block_hide(&mut self) -> RepaintMode; diff --git a/src/render/model_clip_iterator.rs b/src/render/model_clip_iterator.rs index b849333..452cb1f 100644 --- a/src/render/model_clip_iterator.rs +++ b/src/render/model_clip_iterator.rs @@ -1,4 +1,6 @@ +use std::cmp::min; use std::slice::Iter; + use cairo; use super::context::CellMetrics; @@ -76,17 +78,17 @@ impl ModelClipIteratorFactory for ui_model::UiModel { let model = self.model(); let (x1, y1, x2, y2) = ctx.clip_extents(); - let model_clip = ui_model::ModelRect::from_area(cell_metrics, x1, y1, x2, y2); - let model_clip_top = if model_clip.top <= 0 { + + // in case ctx.translate is used y1 can be less then 0 + // in this case just use 0 as top value + let model_clip = ui_model::ModelRect::from_area(cell_metrics, x1, y1.max(0.0), x2, y2); + + let model_clip_top = if model_clip.top == 0 { 0 } else { model_clip.top - 1 }; - let model_clip_bot = if model_clip.bot >= model.len() - 1 { - model.len() - 1 - } else { - model_clip.bot + 1 - }; + let model_clip_bot = min(model.len() - 1, model_clip.bot + 1); ModelClipIterator { model_idx: model_clip_top, diff --git a/src/shell.rs b/src/shell.rs index 2b94aed..5bdab2c 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1227,7 +1227,7 @@ impl RedrawEvents for State { fn cmdline_block_append( &mut self, - content: Vec, String)>>, + content: Vec<(HashMap, String)>, ) -> RepaintMode { self.cmd_line.block_append(&content); RepaintMode::Nothing From 3f62906faca91ec61c9b93b5ba759c08bca97c76 Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 21 Jan 2018 23:19:00 +0300 Subject: [PATCH 19/59] Show block, blink cursor --- src/cmd_line.rs | 32 ++++++++++++---- src/cursor.rs | 87 ++++++++++++++++++++++++++++--------------- src/render/context.rs | 18 ++++----- src/render/mod.rs | 78 +++++++++++++++++++------------------- src/shell.rs | 7 ++-- 5 files changed, 136 insertions(+), 86 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index cb1f0e0..dfc4d7e 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -26,6 +26,7 @@ impl Level { //TODO: double width chars render, also note in text wrapping //TODO: im //TODO: cursor + //TODO: delete pub fn replace_from_ctx(&mut self, ctx: &CmdLineContext, render_state: &shell::RenderState) { self.replace_line(&ctx.get_lines(), render_state, false); @@ -147,6 +148,7 @@ struct State { block: Option, render_state: Rc>, drawing_area: gtk::DrawingArea, + cursor: Option>, } impl State { @@ -156,6 +158,7 @@ impl State { block: None, render_state, drawing_area, + cursor: None, } } @@ -180,7 +183,19 @@ impl State { impl cursor::CursorRedrawCb for State { fn queue_redraw_cursor(&mut self) { - // TODO: implement + // TODO: take gap and preview offset here + if let Some(ref level) = self.levels.last() { + let model = &level.model_layout.model; + + let mut cur_point = model.cur_point(); + cur_point.extend_by_items(model); + + let render_state = self.render_state.borrow(); + let cell_metrics = render_state.font_ctx.cell_metrics(); + + let (x, y, width, height) = cur_point.to_area_extend_ink(model, cell_metrics); + self.drawing_area.queue_draw_area(x, y, width, height); + } } } @@ -202,9 +217,10 @@ impl CmdLine { let state = Arc::new(UiMutex::new(State::new(drawing_area.clone(), render_state))); let weak_cb = Arc::downgrade(&state); - let cursor = cursor::Cursor::new(weak_cb); + let cursor = cursor::BlinkCursor::new(weak_cb); + state.borrow_mut().cursor = Some(cursor); - drawing_area.connect_draw(clone!(state => move |_, ctx| gtk_draw(ctx, &state, &cursor))); + drawing_area.connect_draw(clone!(state => move |_, ctx| gtk_draw(ctx, &state))); CmdLine { popover, @@ -240,6 +256,7 @@ impl CmdLine { }); self.popover.popup(); + state.cursor.as_mut().unwrap().start(); } else { state.drawing_area.queue_draw() } @@ -255,6 +272,7 @@ impl CmdLine { if state.levels.is_empty() { self.popover.hide(); self.displyed = false; + state.cursor.as_mut().unwrap().leave_focus(); } } @@ -298,7 +316,6 @@ impl CmdLine { fn gtk_draw( ctx: &cairo::Context, state: &Arc>, - cursor: &cursor::Cursor, ) -> Inhibit { let state = state.borrow(); let level = state.levels.last(); @@ -314,11 +331,12 @@ fn gtk_draw( ctx.translate(0.0, gap as f64 / 2.0); } + render::clear(ctx, &render_state.color_model); + if let Some(block) = block { - // TODO: disable cursor render::render( ctx, - cursor, + &cursor::EmptyCursor::new(), &render_state.font_ctx, &block.model_layout.model, &render_state.color_model, @@ -332,7 +350,7 @@ fn gtk_draw( //TODO: limit model to row filled render::render( ctx, - cursor, + state.cursor.as_ref().unwrap(), &render_state.font_ctx, &level.model_layout.model, &render_state.color_model, diff --git a/src/cursor.rs b/src/cursor.rs index 8070452..8c5068b 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -50,7 +50,7 @@ struct State { timer: Option, } -impl State { +impl State { fn new(redraw_cb: Weak>) -> Self { State { alpha: Alpha(1.0), @@ -69,13 +69,48 @@ impl State { } } -pub struct Cursor { +pub trait Cursor { + fn draw( + &self, + ctx: &cairo::Context, + font_ctx: &render::Context, + mode: &mode::Mode, + line_y: f64, + double_width: bool, + bg: &Color, + ); +} + +pub struct EmptyCursor; + +impl EmptyCursor { + pub fn new() -> Self { + EmptyCursor { } + } +} + +impl Cursor for EmptyCursor { + fn draw( + &self, + _ctx: &cairo::Context, + _font_ctx: &render::Context, + _mode: &mode::Mode, + _line_y: f64, + _double_width: bool, + _bg: &Color, + ) { + } +} + +pub struct BlinkCursor { state: Arc>>, } -impl Cursor { +impl BlinkCursor { pub fn new(redraw_cb: Weak>) -> Self { - Cursor { state: Arc::new(UiMutex::new(State::new(redraw_cb))) } + BlinkCursor { + state: Arc::new(UiMutex::new(State::new(redraw_cb))), + } } pub fn start(&mut self) { @@ -110,8 +145,10 @@ impl Cursor { pub fn busy_off(&mut self) { self.start(); } +} - pub fn draw( +impl Cursor for BlinkCursor { + fn draw( &self, ctx: &cairo::Context, font_ctx: &render::Context, @@ -120,7 +157,6 @@ impl Cursor { double_width: bool, bg: &Color, ) { - let state = self.state.borrow(); if state.anim_phase == AnimPhase::Busy { @@ -155,9 +191,7 @@ fn cursor_rect( if let Some(mode_info) = mode.mode_info() { match mode_info.cursor_shape() { - None | - Some(&nvim::CursorShape::Unknown) | - Some(&nvim::CursorShape::Block) => { + None | Some(&nvim::CursorShape::Unknown) | Some(&nvim::CursorShape::Block) => { let cursor_width = if double_width { char_width * 2.0 } else { @@ -203,7 +237,7 @@ fn cursor_rect( } } -fn anim_step (state: &Arc>>) -> glib::Continue { +fn anim_step(state: &Arc>>) -> glib::Continue { let mut mut_state = state.borrow_mut(); let next_event = match mut_state.anim_phase { @@ -211,30 +245,26 @@ fn anim_step (state: &Arc>>) -> mut_state.anim_phase = AnimPhase::Hide; Some(60) } - AnimPhase::Hide => { - if !mut_state.alpha.hide(0.3) { - mut_state.anim_phase = AnimPhase::Hidden; + AnimPhase::Hide => if !mut_state.alpha.hide(0.3) { + mut_state.anim_phase = AnimPhase::Hidden; - Some(300) - } else { - None - } - } + Some(300) + } else { + None + }, AnimPhase::Hidden => { mut_state.anim_phase = AnimPhase::Show; Some(60) } - AnimPhase::Show => { - if !mut_state.alpha.show(0.3) { - mut_state.anim_phase = AnimPhase::Shown; + AnimPhase::Show => if !mut_state.alpha.show(0.3) { + mut_state.anim_phase = AnimPhase::Shown; - Some(500) - } else { - None - } - } - AnimPhase::NoFocus => None, + Some(500) + } else { + None + }, + AnimPhase::NoFocus => None, AnimPhase::Busy => None, }; @@ -251,10 +281,9 @@ fn anim_step (state: &Arc>>) -> } else { glib::Continue(true) } - } -impl Drop for Cursor { +impl Drop for BlinkCursor { fn drop(&mut self) { if let Some(timer_id) = self.state.borrow_mut().timer.take() { glib::source_remove(timer_id); diff --git a/src/render/context.rs b/src/render/context.rs index f8d6ba8..fbbb9eb 100644 --- a/src/render/context.rs +++ b/src/render/context.rs @@ -14,7 +14,9 @@ pub struct Context { impl Context { pub fn new(font_desc: pango::FontDescription) -> Self { - Context { state: ContextState::new(font_desc) } + Context { + state: ContextState::new(font_desc), + } } pub fn update(&mut self, font_desc: pango::FontDescription) { @@ -84,20 +86,18 @@ pub struct CellMetrics { impl CellMetrics { fn new(font_metrics: &pango::FontMetrics) -> Self { - CellMetrics { pango_ascent: font_metrics.get_ascent(), pango_descent: font_metrics.get_descent(), pango_char_width: font_metrics.get_approximate_digit_width(), ascent: font_metrics.get_ascent() as f64 / pango::SCALE as f64, - line_height: (font_metrics.get_ascent() + font_metrics.get_descent()) as f64 / - pango::SCALE as f64, + line_height: (font_metrics.get_ascent() + font_metrics.get_descent()) as f64 + / pango::SCALE as f64, char_width: font_metrics.get_approximate_digit_width() as f64 / pango::SCALE as f64, - underline_position: (font_metrics.get_ascent() - - font_metrics.get_underline_position()) as - f64 / pango::SCALE as f64, - underline_thickness: font_metrics.get_underline_thickness() as f64 / - pango::SCALE as f64, + underline_position: (font_metrics.get_ascent() - font_metrics.get_underline_position()) + as f64 / pango::SCALE as f64, + underline_thickness: font_metrics.get_underline_thickness() as f64 + / pango::SCALE as f64, } } diff --git a/src/render/mod.rs b/src/render/mod.rs index 823f517..0f4f996 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -4,7 +4,7 @@ mod model_clip_iterator; pub use self::context::Context; pub use self::context::CellMetrics; -use self::model_clip_iterator::{RowView, ModelClipIteratorFactory}; +use self::model_clip_iterator::{ModelClipIteratorFactory, RowView}; use mode; use color; @@ -14,24 +14,26 @@ use pango; use cairo; use pangocairo; -use cursor; +use cursor::Cursor; use ui_model; -pub fn render( - ctx: &cairo::Context, - cursor: &cursor::Cursor, - font_ctx: &context::Context, - ui_model: &ui_model::UiModel, - color_model: &color::ColorModel, - mode: &mode::Mode, -) { +pub fn clear(ctx: &cairo::Context, color_model: &color::ColorModel) { ctx.set_source_rgb( color_model.bg_color.0, color_model.bg_color.1, color_model.bg_color.2, ); ctx.paint(); +} +pub fn render( + ctx: &cairo::Context, + cursor: &C, + font_ctx: &context::Context, + ui_model: &ui_model::UiModel, + color_model: &color::ColorModel, + mode: &mode::Mode, +) { let cell_metrics = font_ctx.cell_metrics(); let &CellMetrics { char_width, .. } = cell_metrics; let (cursor_row, cursor_col) = ui_model.get_cursor(); @@ -47,19 +49,19 @@ pub fn render( for cell_view in ui_model.get_clip_iterator(ctx, cell_metrics) { let mut line_x = 0.0; - let RowView { line, row, line_y, .. } = cell_view; + let RowView { + line, row, line_y, .. + } = cell_view; for (col, cell) in line.line.iter().enumerate() { - draw_cell(&cell_view, color_model, cell, col, line_x); draw_underline(&cell_view, color_model, cell, line_x); if row == cursor_row && col == cursor_col { - let double_width = line.line.get(col + 1).map_or( - false, - |c| c.attrs.double_width, - ); + let double_width = line.line + .get(col + 1) + .map_or(false, |c| c.attrs.double_width); ctx.move_to(line_x, line_y); cursor.draw( ctx, @@ -82,19 +84,18 @@ fn draw_underline( cell: &ui_model::Cell, line_x: f64, ) { - if cell.attrs.underline || cell.attrs.undercurl { - let &RowView { ctx, line_y, - cell_metrics: &CellMetrics { - line_height, - char_width, - underline_position, - underline_thickness, - .. - }, + cell_metrics: + &CellMetrics { + line_height, + char_width, + underline_position, + underline_thickness, + .. + }, .. } = cell_view; @@ -106,7 +107,13 @@ fn draw_underline( let undercurl_height = (underline_thickness * 4.0).min(max_undercurl_height); let undercurl_y = line_y + underline_position - undercurl_height / 2.0; - pangocairo::functions::error_underline_path(ctx, line_x, undercurl_y, char_width, undercurl_height); + pangocairo::functions::error_underline_path( + ctx, + line_x, + undercurl_y, + char_width, + undercurl_height, + ); } else if cell.attrs.underline { let fg = color_model.actual_cell_fg(cell); ctx.set_source_rgb(fg.0, fg.1, fg.2); @@ -129,11 +136,12 @@ fn draw_cell_bg( ctx, line, line_y, - cell_metrics: &CellMetrics { - char_width, - line_height, - .. - }, + cell_metrics: + &CellMetrics { + char_width, + line_height, + .. + }, .. } = cell_view; @@ -157,7 +165,6 @@ fn draw_cell_bg( ctx.fill(); } } - } fn draw_cell( @@ -167,15 +174,11 @@ fn draw_cell( col: usize, line_x: f64, ) { - let &RowView { ctx, line, line_y, - cell_metrics: &CellMetrics { - ascent, - .. - }, + cell_metrics: &CellMetrics { ascent, .. }, .. } = cell_view; @@ -188,7 +191,6 @@ fn draw_cell( show_glyph_string(ctx, item.font(), glyphs); } - } } diff --git a/src/shell.rs b/src/shell.rs index 5bdab2c..74f0de5 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -26,7 +26,7 @@ use nvim::{self, RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient, NeovimRef, NeovimClientAsync, CompleteItem}; use input; use input::keyval_to_input_string; -use cursor::{Cursor, CursorRedrawCb}; +use cursor::{BlinkCursor, Cursor, CursorRedrawCb}; use ui::UiMutex; use popup_menu::{self, PopupMenu}; use tabline::Tabline; @@ -94,7 +94,7 @@ pub struct State { cur_attrs: Option, mouse_enabled: bool, nvim: Rc, - cursor: Option>, + cursor: Option>, popup_menu: PopupMenu, cmd_line: CmdLine, settings: Rc>, @@ -449,7 +449,7 @@ impl Shell { }; let shell_ref = Arc::downgrade(&shell.state); - shell.state.borrow_mut().cursor = Some(Cursor::new(shell_ref)); + shell.state.borrow_mut().cursor = Some(BlinkCursor::new(shell_ref)); shell } @@ -807,6 +807,7 @@ fn gtk_draw(state_arc: &Arc>, ctx: &cairo::Context) -> Inhibit { let state = state_arc.borrow(); if state.nvim.is_initialized() { let render_state = state.render_state.borrow(); + render::clear(ctx, &render_state.color_model); render::render( ctx, state.cursor.as_ref().unwrap(), From d9b840335bea31a83b7ce1133b605e5ffead1546 Mon Sep 17 00:00:00 2001 From: daa Date: Thu, 25 Jan 2018 23:22:07 +0300 Subject: [PATCH 20/59] Remove columns on replace, fix cursor repaint --- src/cmd_line.rs | 28 +++++++++++++++++++--------- src/ui_model/model_layout.rs | 4 ++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index dfc4d7e..dbc4988 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -179,12 +179,22 @@ impl State { max(block_height + level_height, 40), ); } + + fn preferred_height(&self) -> i32 { + let level = self.levels.last(); + level.map(|l| l.preferred_height).unwrap_or(0) + + self.block.as_ref().map(|b| b.preferred_height).unwrap_or(0) + } } impl cursor::CursorRedrawCb for State { fn queue_redraw_cursor(&mut self) { - // TODO: take gap and preview offset here if let Some(ref level) = self.levels.last() { + let level_preferred_height = level.preferred_height; + let block_preferred_height = self.block.as_ref().map(|b| b.preferred_height).unwrap_or(0); + + let gap = self.drawing_area.get_allocated_height() - level_preferred_height - block_preferred_height; + let model = &level.model_layout.model; let mut cur_point = model.cur_point(); @@ -194,7 +204,12 @@ impl cursor::CursorRedrawCb for State { let cell_metrics = render_state.font_ctx.cell_metrics(); let (x, y, width, height) = cur_point.to_area_extend_ink(model, cell_metrics); - self.drawing_area.queue_draw_area(x, y, width, height); + + if gap > 0 { + self.drawing_area.queue_draw_area(x, y + gap / 2, width, height); + } else { + self.drawing_area.queue_draw_area(x, y + block_preferred_height, width, height); + } } } } @@ -313,17 +328,12 @@ impl CmdLine { } } -fn gtk_draw( - ctx: &cairo::Context, - state: &Arc>, -) -> Inhibit { +fn gtk_draw(ctx: &cairo::Context, state: &Arc>) -> Inhibit { let state = state.borrow(); + let preferred_height = state.preferred_height(); let level = state.levels.last(); let block = state.block.as_ref(); - let preferred_height = level.map(|l| l.preferred_height).unwrap_or(0) - + block.as_ref().map(|b| b.preferred_height).unwrap_or(0); - let render_state = state.render_state.borrow(); let gap = state.drawing_area.get_allocated_height() - preferred_height; diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index 7a482e6..b0d4da7 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -70,6 +70,10 @@ impl ModelLayout { row_idx += 1; } } + + if col_idx < self.model.columns { + self.model.model[row_idx].clear(col_idx, self.model.columns - 1); + } } row_idx += 1; } From 83a390b187ba6fdeed731982612f098da8e52103 Mon Sep 17 00:00:00 2001 From: daa Date: Mon, 29 Jan 2018 22:30:55 +0300 Subject: [PATCH 21/59] Fix test --- src/ui_model/model_layout.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index b0d4da7..d24813b 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -56,6 +56,11 @@ impl ModelLayout { for content in lines { for &(ref attr, ref ch_list) in content { for ch in ch_list { + if col_idx >= self.model.columns { + col_idx = 0; + row_idx += 1; + } + self.model.set_cursor(row_idx, col_idx as usize); self.model.put(*ch, false, attr.as_ref()); @@ -64,11 +69,6 @@ impl ModelLayout { } col_idx += 1; - - if col_idx >= self.model.columns { - col_idx = 0; - row_idx += 1; - } } if col_idx < self.model.columns { From d02c1fb3d65f36847928f5eb2d50a941470180bb Mon Sep 17 00:00:00 2001 From: daa Date: Mon, 29 Jan 2018 22:39:29 +0300 Subject: [PATCH 22/59] Fix cursor repaint problem --- src/cmd_line.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index dbc4988..8838010 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -338,7 +338,7 @@ fn gtk_draw(ctx: &cairo::Context, state: &Arc>) -> Inhibit { let gap = state.drawing_area.get_allocated_height() - preferred_height; if gap > 0 { - ctx.translate(0.0, gap as f64 / 2.0); + ctx.translate(0.0, (gap / 2) as f64); } render::clear(ctx, &render_state.color_model); From 18843eaba2a9f3618ea633a55446d8c6ea5bfd38 Mon Sep 17 00:00:00 2001 From: daa Date: Thu, 1 Feb 2018 23:13:09 +0300 Subject: [PATCH 23/59] Keep width after add new lines, fix indent with firstc --- src/cmd_line.rs | 31 +++++++++++++++++++++---------- src/ui_model/model_layout.rs | 20 +++++++++++++++++++- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 8838010..22f67ed 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -127,12 +127,19 @@ impl Level { fn prompt_lines(firstc: &str, prompt: &str, indent: u64) -> Vec<(Option, Vec)> { if !firstc.is_empty() { - vec![ - ( - None, - firstc.chars().chain((0..indent).map(|_| ' ')).collect(), - ), - ] + if firstc.len() >= indent as usize { + vec![(None, firstc.chars().collect())] + } else { + vec![ + ( + None, + firstc + .chars() + .chain((firstc.len()..indent as usize).map(|_| ' ')) + .collect(), + ), + ] + } } else if !prompt.is_empty() { prompt .lines() @@ -191,9 +198,11 @@ impl cursor::CursorRedrawCb for State { fn queue_redraw_cursor(&mut self) { if let Some(ref level) = self.levels.last() { let level_preferred_height = level.preferred_height; - let block_preferred_height = self.block.as_ref().map(|b| b.preferred_height).unwrap_or(0); + let block_preferred_height = + self.block.as_ref().map(|b| b.preferred_height).unwrap_or(0); - let gap = self.drawing_area.get_allocated_height() - level_preferred_height - block_preferred_height; + let gap = self.drawing_area.get_allocated_height() - level_preferred_height + - block_preferred_height; let model = &level.model_layout.model; @@ -206,9 +215,11 @@ impl cursor::CursorRedrawCb for State { let (x, y, width, height) = cur_point.to_area_extend_ink(model, cell_metrics); if gap > 0 { - self.drawing_area.queue_draw_area(x, y + gap / 2, width, height); + self.drawing_area + .queue_draw_area(x, y + gap / 2, width, height); } else { - self.drawing_area.queue_draw_area(x, y + block_preferred_height, width, height); + self.drawing_area + .queue_draw_area(x, y + block_preferred_height, width, height); } } } diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index d24813b..7113a86 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -1,8 +1,11 @@ +use std::cmp::max; + use ui_model::{Attrs, UiModel}; pub struct ModelLayout { pub model: UiModel, rows_filled: usize, + cols_filled: usize, } impl ModelLayout { @@ -12,6 +15,7 @@ impl ModelLayout { ModelLayout { model: UiModel::new(ModelLayout::ROWS_STEP as u64, columns), rows_filled: 0, + cols_filled: 0, } } @@ -78,7 +82,8 @@ impl ModelLayout { row_idx += 1; } - (max_col_idx + 1, self.rows_filled) + self.cols_filled = max(self.cols_filled, max_col_idx + 1); + (self.cols_filled, self.rows_filled) } fn count_lines(lines: &Vec, Vec)>>, max_columns: usize) -> usize { @@ -119,4 +124,17 @@ mod tests { assert_eq!(ModelLayout::ROWS_STEP * 2, rows); assert_eq!(ModelLayout::ROWS_STEP * 2, model.model.rows); } + + #[test] + fn test_cols_filled() { + let lines = vec![vec![(None, vec!['a'; 3])]; 1]; + let mut model = ModelLayout::new(5); + + let (cols, _) = model.layout(&lines); + assert_eq!(3, cols); + + let lines = vec![vec![(None, vec!['a'; 2])]; 1]; + let (cols, _) = model.layout_append(&lines); + assert_eq!(3, cols); + } } From 89499763ece20ce15443dc27204e97b9248b9d36 Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 10 Feb 2018 12:51:47 +0300 Subject: [PATCH 24/59] Add cmdline_pos processing --- src/cmd_line.rs | 4 ++++ src/nvim/redraw_handler.rs | 3 +++ src/shell.rs | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 22f67ed..7d96373 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -337,6 +337,10 @@ impl CmdLine { pub fn block_hide(&mut self) { self.state.borrow_mut().block = None; } + + pub fn pos(&mut self, pos: u64, level: u64) { + //TODO: move cursor + } } fn gtk_draw(ctx: &cairo::Context, state: &Arc>) -> Inhibit { diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 1e6feb6..f724c99 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -90,6 +90,8 @@ pub trait RedrawEvents { ) -> RepaintMode; fn cmdline_block_hide(&mut self) -> RepaintMode; + + fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode; } pub trait GuiApi { @@ -274,6 +276,7 @@ pub fn call( "cmdline_block_append" => call!(ui->cmdline_block_append(args: ext)), "cmdline_hide" => call!(ui->cmdline_hide(args: uint)), "cmdline_block_hide" => ui.cmdline_block_hide(), + "cmdline_pos" => call!(ui->cmdline_pos(args: uint, uint)), _ => { println!("Event {}({:?})", method, args); RepaintMode::Nothing diff --git a/src/shell.rs b/src/shell.rs index 74f0de5..5203854 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1238,6 +1238,11 @@ impl RedrawEvents for State { self.cmd_line.block_hide(); RepaintMode::Nothing } + + fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode { + self.cmd_line.pos(pos, level); + RepaintMode::Nothing + } } impl CursorRedrawCb for State { From 69dbc990c6939eeeb479476d895282589fbc8eb0 Mon Sep 17 00:00:00 2001 From: daa Date: Mon, 12 Feb 2018 00:34:51 +0300 Subject: [PATCH 25/59] Change cursor position, change preffered size calculation for cursor --- src/cmd_line.rs | 132 ++++++++++++++++++++++++----------- src/shell.rs | 3 +- src/ui_model/model_layout.rs | 51 +++++++++----- 3 files changed, 126 insertions(+), 60 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 7d96373..b83db66 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -18,6 +18,7 @@ use cursor; pub struct Level { model_layout: ModelLayout, + prompt_offset: usize, preferred_width: i32, preferred_height: i32, } @@ -29,27 +30,43 @@ impl Level { //TODO: delete pub fn replace_from_ctx(&mut self, ctx: &CmdLineContext, render_state: &shell::RenderState) { - self.replace_line(&ctx.get_lines(), render_state, false); + let content = ctx.get_lines(); + self.replace_line(&content.lines, false); + self.prompt_offset = content.prompt_offset; + self.model_layout + .set_cursor(self.prompt_offset + ctx.pos as usize); + self.update_preferred_size(render_state); } - fn replace_line( - &mut self, - lines: &Vec, Vec)>>, - render_state: &shell::RenderState, - append: bool, - ) { + pub fn from_ctx(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { + let content = ctx.get_lines(); + let mut level = Level::from_lines(&content.lines, ctx.max_width, render_state); + + level.prompt_offset = content.prompt_offset; + level + .model_layout + .set_cursor(level.prompt_offset + ctx.pos as usize); + level.update_preferred_size(render_state); + + level + } + + fn replace_line(&mut self, lines: &Vec, Vec)>>, append: bool) { + if append { + self.model_layout.layout_append(lines); + } else { + self.model_layout.layout(lines); + } + } + + fn update_preferred_size(&mut self, render_state: &shell::RenderState) { let &CellMetrics { line_height, char_width, .. } = render_state.font_ctx.cell_metrics(); - let (columns, rows) = if append { - self.model_layout.layout_append(lines) - } else { - self.model_layout.layout(lines) - }; - + let (columns, rows) = self.model_layout.size(); let columns = max(columns, 5); self.preferred_width = (char_width * columns as f64) as i32; @@ -89,31 +106,22 @@ impl Level { max_width: i32, render_state: &shell::RenderState, ) -> Self { - let &CellMetrics { - line_height, - char_width, - .. - } = render_state.font_ctx.cell_metrics(); + let &CellMetrics { char_width, .. } = render_state.font_ctx.cell_metrics(); let max_width_chars = (max_width as f64 / char_width) as u64; let mut model_layout = ModelLayout::new(max_width_chars); - let (columns, rows) = model_layout.layout(lines); + model_layout.layout(lines); - let columns = max(columns, 5); - - let preferred_width = (char_width * columns as f64) as i32; - let preferred_height = (line_height * rows as f64) as i32; - - Level { + let mut level = Level { model_layout, - preferred_width, - preferred_height, - } - } + preferred_width: -1, + preferred_height: -1, + prompt_offset: 0, + }; - pub fn from_ctx(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { - Level::from_lines(&ctx.get_lines(), ctx.max_width, render_state) + level.update_preferred_size(render_state); + level } fn update_cache(&mut self, render_state: &shell::RenderState) { @@ -123,10 +131,19 @@ impl Level { &render_state.color_model, ); } + + fn set_cursor(&mut self, render_state: &shell::RenderState, pos: usize) { + self.model_layout.set_cursor(self.prompt_offset + pos); + self.update_preferred_size(render_state); + } } -fn prompt_lines(firstc: &str, prompt: &str, indent: u64) -> Vec<(Option, Vec)> { - if !firstc.is_empty() { +fn prompt_lines( + firstc: &str, + prompt: &str, + indent: u64, +) -> (usize, Vec<(Option, Vec)>) { + let prompt: Vec<(Option, Vec)> = if !firstc.is_empty() { if firstc.len() >= indent as usize { vec![(None, firstc.chars().collect())] } else { @@ -147,7 +164,11 @@ fn prompt_lines(firstc: &str, prompt: &str, indent: u64) -> Vec<(Option, .collect() } else { vec![] - } + }; + + let prompt_offset = prompt.last().map(|l| l.1.len()).unwrap_or(0); + + (prompt_offset, prompt) } struct State { @@ -192,9 +213,18 @@ impl State { level.map(|l| l.preferred_height).unwrap_or(0) + self.block.as_ref().map(|b| b.preferred_height).unwrap_or(0) } -} -impl cursor::CursorRedrawCb for State { + fn set_cursor(&mut self, render_state: &shell::RenderState, pos: usize, level: usize) { + debug_assert!(level > 0); + + // queue old cursor position + self.queue_redraw_cursor(); + + self.levels + .get_mut(level - 1) + .map(|l| l.set_cursor(render_state, pos)); + } + fn queue_redraw_cursor(&mut self) { if let Some(ref level) = self.levels.last() { let level_preferred_height = level.preferred_height; @@ -225,6 +255,13 @@ impl cursor::CursorRedrawCb for State { } } +impl cursor::CursorRedrawCb for State { + + fn queue_redraw_cursor(&mut self) { + self.queue_redraw_cursor(); + } +} + pub struct CmdLine { popover: gtk::Popover, displyed: bool, @@ -328,7 +365,8 @@ impl CmdLine { .collect(); let block = state.block.as_mut().unwrap(); - block.replace_line(&vec![attr_content], &*render_state.borrow(), true); + block.replace_line(&vec![attr_content], true); + block.update_preferred_size(&*render_state.borrow()); block.update_cache(&*render_state.borrow()); } state.request_area_size(); @@ -338,8 +376,10 @@ impl CmdLine { self.state.borrow_mut().block = None; } - pub fn pos(&mut self, pos: u64, level: u64) { - //TODO: move cursor + pub fn pos(&mut self, render_state: &shell::RenderState, pos: u64, level: u64) { + self.state + .borrow_mut() + .set_cursor(render_state, pos as usize, level as usize); } } @@ -400,14 +440,14 @@ pub struct CmdLineContext { } impl CmdLineContext { - fn get_lines(&self) -> Vec, Vec)>> { + fn get_lines(&self) -> LineContent { let content_line: Vec<(Option, Vec)> = self.content .iter() .map(|c| { (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) }) .collect(); - let prompt_lines = prompt_lines(&self.firstc, &self.prompt, self.indent); + let (prompt_offset, prompt_lines) = prompt_lines(&self.firstc, &self.prompt, self.indent); let mut content: Vec<_> = prompt_lines.into_iter().map(|line| vec![line]).collect(); @@ -417,6 +457,14 @@ impl CmdLineContext { content.last_mut().map(|line| line.extend(content_line)); } - content + LineContent { + lines: content, + prompt_offset, + } } } + +struct LineContent { + lines: Vec, Vec)>>, + prompt_offset: usize, +} diff --git a/src/shell.rs b/src/shell.rs index 5203854..9428d93 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1240,7 +1240,8 @@ impl RedrawEvents for State { } fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode { - self.cmd_line.pos(pos, level); + let render_state = self.render_state.borrow(); + self.cmd_line.pos(&* render_state, pos, level); RepaintMode::Nothing } } diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index 7113a86..aaac0f8 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -19,24 +19,41 @@ impl ModelLayout { } } - pub fn layout_append(&mut self, lines: &Vec, Vec)>>) -> (usize, usize) { + pub fn layout_append(&mut self, lines: &Vec, Vec)>>) { let rows_filled = self.rows_filled; - self.layout_replace(rows_filled, lines) + self.layout_replace(rows_filled, lines); } - pub fn layout(&mut self, - lines: &Vec, Vec)>>, - ) -> (usize, usize) { - self.layout_replace(0, lines) + pub fn layout(&mut self, lines: &Vec, Vec)>>) { + self.layout_replace(0, lines); + } + + pub fn set_cursor(&mut self, col: usize) { + let row = if self.rows_filled > 0 { + self.rows_filled - 1 + } else { + 0 + }; + + self.model.set_cursor(row, col); + } + + pub fn size(&self) -> (usize, usize) { + ( + max(self.cols_filled, self.model.get_cursor().1 + 1), + self.rows_filled, + ) } fn check_model_size(&mut self, rows: usize) { if rows > self.model.rows { let model_cols = self.model.columns; let model_rows = ((rows / (ModelLayout::ROWS_STEP + 1)) + 1) * ModelLayout::ROWS_STEP; + let (cur_row, cur_col) = self.model.get_cursor(); let mut model = UiModel::new(model_rows as u64, model_cols as u64); self.model.copy_rows(&mut model, self.rows_filled - 1); + model.set_cursor(cur_row, cur_col); self.model = model; } } @@ -44,11 +61,7 @@ impl ModelLayout { /// Wrap all lines into model /// /// returns actual width - fn layout_replace( - &mut self, - row_offset: usize, - lines: &Vec, Vec)>>, - ) -> (usize, usize) { + fn layout_replace(&mut self, row_offset: usize, lines: &Vec, Vec)>>) { let rows = ModelLayout::count_lines(&lines, self.model.columns); self.check_model_size(rows + row_offset); @@ -83,7 +96,6 @@ impl ModelLayout { } self.cols_filled = max(self.cols_filled, max_col_idx + 1); - (self.cols_filled, self.rows_filled) } fn count_lines(lines: &Vec, Vec)>>, max_columns: usize) -> usize { @@ -115,11 +127,13 @@ mod tests { let lines = vec![vec![(None, vec!['a'; 5])]; ModelLayout::ROWS_STEP]; let mut model = ModelLayout::new(5); - let (cols, rows) = model.layout(&lines); + model.layout(&lines); + let (cols, rows) = model.size(); assert_eq!(5, cols); assert_eq!(ModelLayout::ROWS_STEP, rows); - let (cols, rows) = model.layout_append(&lines); + model.layout_append(&lines); + let (cols, rows) = model.size(); assert_eq!(5, cols); assert_eq!(ModelLayout::ROWS_STEP * 2, rows); assert_eq!(ModelLayout::ROWS_STEP * 2, model.model.rows); @@ -130,11 +144,14 @@ mod tests { let lines = vec![vec![(None, vec!['a'; 3])]; 1]; let mut model = ModelLayout::new(5); - let (cols, _) = model.layout(&lines); - assert_eq!(3, cols); + model.layout(&lines); + let (cols, _) = model.size(); + assert_eq!(4, cols); // size is 3 and 4 - is with cursor position let lines = vec![vec![(None, vec!['a'; 2])]; 1]; - let (cols, _) = model.layout_append(&lines); + + model.layout_append(&lines); + let (cols, _) = model.size(); assert_eq!(3, cols); } } From 50695ba829273eaa1013459290f863a13daedbff Mon Sep 17 00:00:00 2001 From: daa Date: Fri, 23 Feb 2018 14:16:20 +0300 Subject: [PATCH 26/59] Disable main cursor when cmdline is displayed --- src/shell.rs | 58 ++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/shell.rs b/src/shell.rs index 17a0d1d..a2d2c12 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -180,9 +180,10 @@ impl State { } pub fn set_font_desc(&mut self, desc: &str) { - self.render_state.borrow_mut().font_ctx.update( - FontDescription::from_string(desc), - ); + self.render_state + .borrow_mut() + .font_ctx + .update(FontDescription::from_string(desc)); self.model.clear_glyphs(); self.try_nvim_resize(); self.on_redraw(&RepaintMode::All); @@ -328,8 +329,8 @@ impl State { let nvim = self.nvim(); if let Some(mut nvim) = nvim { let render_state = self.render_state.borrow(); - if render_state.mode.is(&mode::NvimMode::Insert) || - render_state.mode.is(&mode::NvimMode::Normal) + if render_state.mode.is(&mode::NvimMode::Insert) + || render_state.mode.is(&mode::NvimMode::Normal) { let paste_code = format!("normal! \"{}P", clipboard); nvim.command_async(&paste_code) @@ -1124,30 +1125,33 @@ impl RedrawEvents for State { indent: u64, level: u64, ) -> RepaintMode { - let cursor = self.model.cur_point(); - let render_state = self.render_state.borrow(); - let (x, y, width, height) = cursor.to_area(render_state.font_ctx.cell_metrics()); - let ctx = CmdLineContext { - content, - pos, - firstc, - prompt, - indent, - level_idx: level, - x, - y, - width, - height, - max_width: self.max_popup_width(), - }; + { + let cursor = self.model.cur_point(); + let render_state = self.render_state.borrow(); + let (x, y, width, height) = cursor.to_area(render_state.font_ctx.cell_metrics()); + let ctx = CmdLineContext { + content, + pos, + firstc, + prompt, + indent, + level_idx: level, + x, + y, + width, + height, + max_width: self.max_popup_width(), + }; - self.cmd_line.show_level(&ctx); - RepaintMode::Nothing + self.cmd_line.show_level(&ctx); + } + + self.on_busy(true) } fn cmdline_hide(&mut self, level: u64) -> RepaintMode { self.cmd_line.hide_level(level); - RepaintMode::Nothing + self.on_busy(false) } fn cmdline_block_show( @@ -1156,7 +1160,7 @@ impl RedrawEvents for State { ) -> RepaintMode { let max_width = self.max_popup_width(); self.cmd_line.show_block(&content, max_width); - RepaintMode::Nothing + self.on_busy(true) } fn cmdline_block_append( @@ -1169,12 +1173,12 @@ impl RedrawEvents for State { fn cmdline_block_hide(&mut self) -> RepaintMode { self.cmd_line.block_hide(); - RepaintMode::Nothing + self.on_busy(false) } fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode { let render_state = self.render_state.borrow(); - self.cmd_line.pos(&* render_state, pos, level); + self.cmd_line.pos(&*render_state, pos, level); RepaintMode::Nothing } } From fc7016caa957376325c913981cf3abd732e78b26 Mon Sep 17 00:00:00 2001 From: daa Date: Fri, 23 Feb 2018 19:39:53 +0300 Subject: [PATCH 27/59] Add logs --- src/nvim/redraw_handler.rs | 2 +- src/shell.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index b82cb3c..daefd9d 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -324,7 +324,7 @@ pub fn call( "cmdline_block_hide" => ui.cmdline_block_hide(), "cmdline_pos" => call!(ui->cmdline_pos(args: uint, uint)), _ => { - println!("Event {}({:?})", method, args); + warn!("Event {}({:?})", method, args); RepaintMode::Nothing } }; diff --git a/src/shell.rs b/src/shell.rs index 38e0e44..312cddd 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -971,6 +971,8 @@ impl RedrawEvents for State { } fn on_clear(&mut self) -> RepaintMode { + debug!("clear model"); + self.model.clear(); RepaintMode::All } From f6aced4102876ca9227c9317661df434e7c09d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sat, 24 Feb 2018 18:01:17 +0100 Subject: [PATCH 28/59] Introduce close buttons for tabs --- src/tabline.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/tabline.rs b/src/tabline.rs index b09fbc7..f6eb8b3 100644 --- a/src/tabline.rs +++ b/src/tabline.rs @@ -39,6 +39,12 @@ impl State { } } } + + fn close_tab(&self, idx: u32) { + if let Some(mut nvim) = self.nvim.as_ref().unwrap().nvim() { + nvim.command(&format!(":tabc {}", idx + 1)).report_err(); + } + } } pub struct Tabline { @@ -55,6 +61,7 @@ impl Tabline { tabs.set_scrollable(true); tabs.set_show_border(false); tabs.set_border_width(0); + tabs.set_hexpand(true); tabs.hide(); let state = Rc::new(RefCell::new(State::new())); @@ -113,7 +120,33 @@ impl Tabline { let title = gtk::Label::new(None); title.set_ellipsize(pango::EllipsizeMode::Middle); title.set_width_chars(25); - self.tabs.append_page(&empty, Some(&title)); + let close_btn = gtk::Button::new_from_icon_name( + "window-close-symbolic", + gtk::IconSize::Menu.into(), + ); + close_btn.set_relief(gtk::ReliefStyle::None); + close_btn.get_style_context().unwrap().add_class("small-button"); + close_btn.set_focus_on_click(false); + let label_box = gtk::Box::new(gtk::Orientation::Horizontal, 0); + label_box.pack_start(&title, true, false, 0); + label_box.pack_start(&close_btn, false, false, 0); + title.show(); + close_btn.show(); + self.tabs.append_page(&empty, Some(&label_box)); + + let tabs = self.tabs.clone(); + let state_ref = Rc::clone(&self.state); + close_btn.connect_clicked(move |btn| { + let current_label = btn + .get_parent().unwrap(); + for i in 0..tabs.get_n_pages() { + let page = tabs.get_nth_page(Some(i)).unwrap(); + let label = tabs.get_tab_label(&page).unwrap(); + if label == current_label { + state_ref.borrow().close_tab(i); + } + } + }); } } else if count > tabs.len() { for _ in tabs.len()..count { @@ -126,6 +159,12 @@ impl Tabline { let tab_label = self.tabs .get_tab_label(&tab_child.unwrap()) .unwrap() + .downcast::() + .unwrap() + .get_children() + .into_iter() + .next() + .unwrap() .downcast::() .unwrap(); tab_label.set_text(tab.1.as_ref().unwrap_or(&"??".to_owned())); From 4eb025ba918e1b8dfeb87a6f51516c352be505d8 Mon Sep 17 00:00:00 2001 From: daa Date: Tue, 27 Feb 2018 22:24:17 +0300 Subject: [PATCH 29/59] Make popup smaller on char deletion --- src/cmd_line.rs | 2 -- src/ui_model/model_layout.rs | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index b83db66..8ae848b 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -26,8 +26,6 @@ pub struct Level { impl Level { //TODO: double width chars render, also note in text wrapping //TODO: im - //TODO: cursor - //TODO: delete pub fn replace_from_ctx(&mut self, ctx: &CmdLineContext, render_state: &shell::RenderState) { let content = ctx.get_lines(); diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index aaac0f8..4bf172f 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -95,7 +95,11 @@ impl ModelLayout { row_idx += 1; } - self.cols_filled = max(self.cols_filled, max_col_idx + 1); + if self.rows_filled == 1 { + self.cols_filled = max_col_idx + 1; + } else { + self.cols_filled = max(self.cols_filled, max_col_idx + 1); + } } fn count_lines(lines: &Vec, Vec)>>, max_columns: usize) -> usize { From c339a2748110aa6430af5dbe4a623852280691f6 Mon Sep 17 00:00:00 2001 From: daa Date: Tue, 27 Feb 2018 22:51:23 +0300 Subject: [PATCH 30/59] Starting point for special char processing --- src/cmd_line.rs | 39 ++++++++++++++++++++++++++---------- src/nvim/redraw_handler.rs | 3 +++ src/shell.rs | 6 ++++++ src/ui_model/model_layout.rs | 12 +++++++++++ 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 8ae848b..7221891 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -27,6 +27,11 @@ impl Level { //TODO: double width chars render, also note in text wrapping //TODO: im + pub fn insert(&mut self, c: &str, shift: bool, render_state: &shell::RenderState) { + self.model_layout.insert(c, shift); + self.update_preferred_size(render_state); + } + pub fn replace_from_ctx(&mut self, ctx: &CmdLineContext, render_state: &shell::RenderState) { let content = ctx.get_lines(); self.replace_line(&content.lines, false); @@ -79,9 +84,7 @@ impl Level { .map(|line_chars| { line_chars .iter() - .map(|c| { - (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) - }) + .map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect())) .collect() }) .collect() @@ -254,7 +257,6 @@ impl State { } impl cursor::CursorRedrawCb for State { - fn queue_redraw_cursor(&mut self) { self.queue_redraw_cursor(); } @@ -323,6 +325,26 @@ impl CmdLine { } } + pub fn special_char( + &self, + render_state: &shell::RenderState, + c: String, + shift: bool, + level: u64, + ) { + let mut state = self.state.borrow_mut(); + + if let Some(level) = state.levels.get_mut((level - 1) as usize) { + level.insert(&c, shift, render_state); + level.update_cache(&*render_state); + } else { + error!("Level {} does not exists", level); + } + + state.request_area_size(); + state.drawing_area.queue_draw() + } + pub fn hide_level(&mut self, level_idx: u64) { let mut state = self.state.borrow_mut(); @@ -350,16 +372,13 @@ impl CmdLine { state.request_area_size(); } - pub fn block_append(&mut self, content: &Vec<(HashMap, String)>) { let mut state = self.state.borrow_mut(); let render_state = state.render_state.clone(); { let attr_content = content .iter() - .map(|c| { - (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) - }) + .map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect())) .collect(); let block = state.block.as_mut().unwrap(); @@ -441,9 +460,7 @@ impl CmdLineContext { fn get_lines(&self) -> LineContent { let content_line: Vec<(Option, Vec)> = self.content .iter() - .map(|c| { - (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()) - }) + .map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect())) .collect(); let (prompt_offset, prompt_lines) = prompt_lines(&self.firstc, &self.prompt, self.indent); diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index daefd9d..364c2b1 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -95,6 +95,8 @@ pub trait RedrawEvents { fn cmdline_block_hide(&mut self) -> RepaintMode; fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode; + + fn cmdline_special_char(&mut self, c: String, shift: bool, level: u64) -> RepaintMode; } pub trait GuiApi { @@ -323,6 +325,7 @@ pub fn call( "cmdline_hide" => call!(ui->cmdline_hide(args: uint)), "cmdline_block_hide" => ui.cmdline_block_hide(), "cmdline_pos" => call!(ui->cmdline_pos(args: uint, uint)), + "cmdline_special_char" => call!(ui->cmdline_special_char(args: str, bool, uint)), _ => { warn!("Event {}({:?})", method, args); RepaintMode::Nothing diff --git a/src/shell.rs b/src/shell.rs index 312cddd..62ec42a 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1197,6 +1197,12 @@ impl RedrawEvents for State { self.cmd_line.pos(&*render_state, pos, level); RepaintMode::Nothing } + + fn cmdline_special_char(&mut self, c: String, shift: bool, level: u64) -> RepaintMode { + let render_state = self.render_state.borrow(); + self.cmd_line.special_char(&*render_state, c, shift, level); + RepaintMode::Nothing + } } impl CursorRedrawCb for State { diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index 4bf172f..ed23774 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -58,6 +58,18 @@ impl ModelLayout { } } + pub fn insert(&mut self, c: &str, shift: bool) { + if c.is_empty() { + return; + } + + if shift { + //TODO: insert special char + } else { + self.model.put(c.chars().next().unwrap(), false, None); + } + } + /// Wrap all lines into model /// /// returns actual width From 9967b4bdb494fe09c843a1f444223ec6c07c2ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sun, 25 Feb 2018 15:54:15 +0100 Subject: [PATCH 31/59] Show filename and dir in header bar --- src/main.rs | 1 + src/nvim/handler.rs | 8 ++- src/project.rs | 2 +- src/shell.rs | 33 +++++++++ src/subscriptions.rs | 158 +++++++++++++++++++++++++++++++++++++++++++ src/ui.rs | 154 +++++++++++++++++++++++++++-------------- 6 files changed, 304 insertions(+), 52 deletions(-) create mode 100644 src/subscriptions.rs diff --git a/src/main.rs b/src/main.rs index 2faa62d..92f0cb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,7 @@ mod popup_menu; mod project; mod tabline; mod error; +mod subscriptions; use std::env; use std::time::Duration; diff --git a/src/nvim/handler.rs b/src/nvim/handler.rs index fe25085..74b5a05 100644 --- a/src/nvim/handler.rs +++ b/src/nvim/handler.rs @@ -83,6 +83,12 @@ impl NvimHandler { error!("Unsupported event {:?}", params); } } + "subscription" => { + self.safe_call(move |ui| { + let ui = &ui.borrow(); + ui.notify(params) + }); + } _ => { error!("Notification {}({:?})", method, params); } @@ -130,7 +136,7 @@ impl NvimHandler { } } } - + fn safe_call(&self, cb: F) where F: FnOnce(&Arc>) -> result::Result<(), String> + 'static + Send, diff --git a/src/project.rs b/src/project.rs index 8ce293e..6423fa8 100644 --- a/src/project.rs +++ b/src/project.rs @@ -59,7 +59,7 @@ pub struct Projects { } impl Projects { - pub fn new(ref_widget: >k::ToolButton, shell: Rc>) -> Rc> { + pub fn new(ref_widget: >k::Button, shell: Rc>) -> Rc> { let projects = Projects { shell, popup: Popover::new(Some(ref_widget)), diff --git a/src/shell.rs b/src/shell.rs index d1c495f..1bb3c15 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -33,6 +33,7 @@ use error; use mode; use render; use render::CellMetrics; +use subscriptions::{Subscriptions, SubscriptionHandle}; const DEFAULT_FONT_NAME: &str = "DejaVu Sans Mono 12"; pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.2.2"; @@ -78,6 +79,8 @@ pub struct State { detach_cb: Option>>, nvim_started_cb: Option>>, + + subscriptions: RefCell, } impl State { @@ -116,6 +119,8 @@ impl State { detach_cb: None, nvim_started_cb: None, + + subscriptions: RefCell::new(Subscriptions::new()), } } @@ -328,6 +333,25 @@ impl State { }; } } + + pub fn subscribe(&self, event_name: &str, args: &[&str], cb: F) -> SubscriptionHandle + where + F: Fn(Vec) + 'static, + { + self.subscriptions.borrow_mut().subscribe(event_name, args, cb) + } + + pub fn set_autocmds(&self) { + self.subscriptions.borrow().set_autocmds(&mut self.nvim().unwrap()); + } + + pub fn notify(&self, params: Vec) -> Result<(), String> { + self.subscriptions.borrow().notify(params) + } + + pub fn run_now(&self, handle: &SubscriptionHandle) { + self.subscriptions.borrow().run_now(handle, &mut self.nvim().unwrap()); + } } pub struct UiState { @@ -574,6 +598,15 @@ impl Shell { } } + pub fn new_tab(&self) { + let state = self.state.borrow(); + + let nvim = state.nvim(); + if let Some(mut nvim) = nvim { + nvim.command(":tabe").report_err(); + } + } + pub fn set_detach_cb(&self, cb: Option) where F: FnMut() + Send + 'static, diff --git a/src/subscriptions.rs b/src/subscriptions.rs new file mode 100644 index 0000000..cd7d23e --- /dev/null +++ b/src/subscriptions.rs @@ -0,0 +1,158 @@ +use std::collections::HashMap; + +use neovim_lib::{NeovimApi, Value}; + +use nvim::NeovimRef; + +/// A subscription to a Neovim autocmd event. +struct Subscription { + /// A callback to be executed each time the event triggers. + cb: Box) + 'static>, + /// A list of expressions which will be evaluated when the event triggers. The result is passed + /// to the callback. + args: Vec, +} + +/// A map of all registered subscriptions. +pub struct Subscriptions(HashMap>); + +/// A handle to identify a `Subscription` within the `Subscriptions` map. +/// +/// Can be used to trigger the subscription manually even when the event was not triggered. +/// +/// Could be used in the future to suspend individual subscriptions. +#[derive(Debug)] +pub struct SubscriptionHandle { + event_name: String, + index: usize, +} + +impl Subscriptions { + pub fn new() -> Self { + Subscriptions(HashMap::new()) + } + + /// Subscribe to a Neovim autocmd event. + /// + /// Subscriptions are not active immediately but only after `set_autocmds` is called. At the + /// moment, all calls to `subscribe` must be made before calling `set_autocmds`. + /// + /// This function is wrapped by `shell::State`. + /// + /// # Arguments: + /// + /// - `event_name`: The event to register. + /// See `:help autocmd-events` for a list of supported event names. Event names can be + /// comma-separated. + /// + /// - `args`: A list of expressions to be evaluated when the event triggers. + /// Expressions are evaluated using Vimscript. The results are passed to the callback as a + /// list of Strings. + /// This is especially useful as `Neovim::eval` is synchronous and might block if called from + /// the callback function; so always use the `args` mechanism instead. + /// + /// - `cb`: The callback function. + /// This will be called each time the event triggers or when `run_now` is called. + /// It is passed a vector with the results of the evaluated expressions given with `args`. + /// + /// # Example + /// + /// Call a function each time a buffer is entered or the current working directory is changed. + /// Pass the current buffer name and directory to the callback. + /// ``` + /// let my_subscription = shell.state.borrow() + /// .subscribe("BufEnter,DirChanged", &["expand(@%)", "getcwd()"], move |args| { + /// let filename = &args[0]; + /// let dir = &args[1]; + /// // do stuff + /// }); + /// ``` + pub fn subscribe(&mut self, event_name: &str, args: &[&str], cb: F) -> SubscriptionHandle + where + F: Fn(Vec) + 'static, + { + let entry = self.0.entry(event_name.to_owned()).or_insert(Vec::new()); + let index = entry.len(); + entry.push(Subscription { + cb: Box::new(cb), + args: args.into_iter().map(|&s| s.to_owned()).collect(), + }); + SubscriptionHandle { + event_name: event_name.to_owned(), + index, + } + } + + /// Register all subscriptions with Neovim. + /// + /// This function is wrapped by `shell::State`. + pub fn set_autocmds(&self, nvim: &mut NeovimRef) { + for (event_name, subscriptions) in &self.0 { + for (i, subscription) in subscriptions.iter().enumerate() { + let args = subscription.args + .iter() + .fold("".to_owned(), |acc, arg| acc + ", " + &arg); + nvim.command(&format!( + "au {} * call rpcnotify(1, 'subscription', '{}', {} {})", + event_name, + event_name, + i, + args, + )).expect("Could not set autocmd"); + } + } + } + + /// Trigger given event. + fn on_notify(&self, event_name: &str, index: usize, args: Vec) { + if let Some(subscription) = self.0.get(event_name).and_then(|v| v.get(index)) { + (*subscription.cb)(args); + } + } + + /// Wrapper around `on_notify` for easy calling with a `neovim_lib::Handler` implementation. + /// + /// This function is wrapped by `shell::State`. + pub fn notify(&self, params: Vec) -> Result<(), String> { + let mut params_iter = params.into_iter(); + let ev_name = params_iter.next(); + let ev_name = ev_name + .as_ref() + .and_then(Value::as_str) + .ok_or("Error reading event name")?; + let index = params_iter + .next() + .and_then(|i| i.as_u64()) + .ok_or("Error reading index")? as usize; + let args = params_iter + .map(|arg| arg.as_str().map(|s| s.to_owned())) + .collect::>>() + .ok_or("Error reading args")?; + self.on_notify(ev_name, index, args); + Ok(()) + } + + /// Manually trigger the given subscription. + /// + /// The `nvim` instance is needed to evaluate the `args` expressions. + /// + /// This function is wrapped by `shell::State`. + pub fn run_now(&self, handle: &SubscriptionHandle, nvim: &mut NeovimRef) { + let subscription = &self.0.get(&handle.event_name).unwrap()[handle.index]; + let args = subscription.args + .iter() + .map(|arg| nvim.eval(arg)) + .map(|res| { + res.ok() + .and_then(|val| { + val.as_str().map(|s: &str| s.to_owned()) + }) + }) + .collect::>>(); + if let Some(args) = args { + self.on_notify(&handle.event_name, handle.index, args); + } else { + error!("Error manually running {:?}", handle); + } + } +} diff --git a/src/ui.rs b/src/ui.rs index fd77e4b..5407e6a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,13 +1,13 @@ use std::cell::{Ref, RefCell, RefMut}; use std::{env, thread}; +use std::path::Path; use std::rc::Rc; use std::sync::Arc; use gdk; use gtk; -use gtk_sys; use gtk::prelude::*; -use gtk::{AboutDialog, ApplicationWindow, HeaderBar, Image, SettingsExt, ToolButton}; +use gtk::{AboutDialog, ApplicationWindow, Button, HeaderBar, SettingsExt}; use gio::prelude::*; use gio::{Menu, MenuExt, MenuItem, SimpleAction}; use toml; @@ -17,6 +17,7 @@ use shell::{self, Shell, ShellOptions}; use shell_dlg; use project::Projects; use plug_manager; +use subscriptions::SubscriptionHandle; macro_rules! clone { (@param _) => ( _ ); @@ -50,16 +51,22 @@ pub struct Ui { pub struct Components { window: Option, window_state: WindowState, - open_btn: ToolButton, + open_btn: Button, } impl Components { fn new() -> Components { - let save_image = - Image::new_from_icon_name("document-open", gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32); - + let open_btn = Button::new(); + let open_btn_box = gtk::Box::new(gtk::Orientation::Horizontal, 3); + open_btn_box.pack_start(>k::Label::new("Open"), false, false, 3); + open_btn_box.pack_start( + >k::Image::new_from_icon_name("pan-down-symbolic", gtk::IconSize::Menu.into()), + false, false, 3 + ); + open_btn.add(&open_btn_box); + open_btn.set_can_focus(false); Components { - open_btn: ToolButton::new(Some(&save_image), "Open"), + open_btn, window: None, window_state: WindowState::load(), } @@ -126,48 +133,6 @@ impl Ui { } } - // Client side decorations including the toolbar are disabled via NVIM_GTK_NO_HEADERBAR=1 - let use_header_bar = env::var("NVIM_GTK_NO_HEADERBAR") - .map(|opt| opt.trim() != "1") - .unwrap_or(true); - - if app.prefers_app_menu() || use_header_bar { - self.create_main_menu(app, &window); - } - - if use_header_bar { - let header_bar = HeaderBar::new(); - - let projects = self.projects.clone(); - header_bar.pack_start(&comps.open_btn); - comps - .open_btn - .connect_clicked(move |_| projects.borrow_mut().show()); - - let save_image = Image::new_from_icon_name( - "document-save", - gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32, - ); - let save_btn = ToolButton::new(Some(&save_image), "Save"); - - let shell = self.shell.clone(); - save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all()); - header_bar.pack_start(&save_btn); - - let paste_image = Image::new_from_icon_name( - "edit-paste", - gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32, - ); - let paste_btn = ToolButton::new(Some(&paste_image), "Paste"); - let shell = self.shell.clone(); - paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste()); - header_bar.pack_start(&paste_btn); - - header_bar.set_show_close_button(true); - - window.set_titlebar(Some(&header_bar)); - } - if restore_win_state { if comps.window_state.is_maximized { window.maximize(); @@ -182,6 +147,21 @@ impl Ui { } } + // Client side decorations including the toolbar are disabled via NVIM_GTK_NO_HEADERBAR=1 + let use_header_bar = env::var("NVIM_GTK_NO_HEADERBAR") + .map(|opt| opt.trim() != "1") + .unwrap_or(true); + + if app.prefers_app_menu() || use_header_bar { + self.create_main_menu(app, &window); + } + + let update_subtitle = if use_header_bar { + Some(self.create_header_bar()) + } else { + None + }; + let comps_ref = self.comps.clone(); window.connect_size_allocate(move |window, _| { gtk_window_size_allocate(window, &mut *comps_ref.borrow_mut()) @@ -202,7 +182,27 @@ impl Ui { window.add(&**shell); window.show_all(); - window.set_title("NeovimGtk"); + + let comps_ref = self.comps.clone(); + let update_title = shell.state.borrow() + .subscribe("BufEnter,DirChanged", &["expand('%:p')", "getcwd()"], move |args| { + let comps = comps_ref.borrow(); + let window = comps.window.as_ref().unwrap(); + let file_path = &args[0]; + let dir = Path::new(&args[1]); + let filename = if file_path.is_empty() { + "[No Name]" + } else if let Some(rel_path) = Path::new(&file_path) + .strip_prefix(&dir) + .ok() + .and_then(|p| p.to_str()) + { + rel_path + } else { + &file_path + }; + window.set_title(filename); + }); let comps_ref = self.comps.clone(); let shell_ref = self.shell.clone(); @@ -222,12 +222,66 @@ impl Ui { let state_ref = self.shell.borrow().state.clone(); let plug_manager_ref = self.plug_manager.clone(); shell.set_nvim_started_cb(Some(move || { + let state = state_ref.borrow(); plug_manager_ref .borrow_mut() .init_nvim_client(state_ref.borrow().nvim_clone()); + state.set_autocmds(); + state.run_now(&update_title); + if let Some(ref update_subtitle) = update_subtitle { + state.run_now(&update_subtitle); + } })); } + fn create_header_bar(&self) -> SubscriptionHandle { + let header_bar = HeaderBar::new(); + let comps = self.comps.borrow(); + let window = comps.window.as_ref().unwrap(); + + let projects = self.projects.clone(); + header_bar.pack_start(&comps.open_btn); + comps + .open_btn + .connect_clicked(move |_| projects.borrow_mut().show()); + + let new_tab_btn = Button::new_from_icon_name( + "tab-new-symbolic", + gtk::IconSize::SmallToolbar.into(), + ); + let shell_ref = Rc::clone(&self.shell); + new_tab_btn.connect_clicked(move |_| shell_ref.borrow_mut().new_tab()); + new_tab_btn.set_can_focus(false); + header_bar.pack_start(&new_tab_btn); + + let paste_btn = Button::new_from_icon_name( + "edit-paste", + gtk::IconSize::SmallToolbar.into(), + ); + let shell = self.shell.clone(); + paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste()); + paste_btn.set_can_focus(false); + header_bar.pack_end(&paste_btn); + + let save_btn = Button::new_with_label("Save All"); + let shell = self.shell.clone(); + save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all()); + save_btn.set_can_focus(false); + header_bar.pack_end(&save_btn); + + header_bar.set_show_close_button(true); + + window.set_titlebar(Some(&header_bar)); + + let shell = self.shell.borrow(); + let update_subtitle = shell.state.borrow() + .subscribe("DirChanged", &["getcwd()"], move |args| { + header_bar.set_subtitle(&*args[0]); + }); + + update_subtitle + } + fn create_main_menu(&self, app: >k::Application, window: >k::ApplicationWindow) { let plug_manager = self.plug_manager.clone(); From d30f65c45fa0fa717b8eb9a697f0288984ee49af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sun, 4 Mar 2018 13:22:56 +0100 Subject: [PATCH 32/59] Apply rustfmt --- src/shell.rs | 14 ++++++++++---- src/subscriptions.rs | 15 ++++++--------- src/ui.rs | 34 +++++++++++++++++++--------------- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/shell.rs b/src/shell.rs index 1bb3c15..ae4be61 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -33,7 +33,7 @@ use error; use mode; use render; use render::CellMetrics; -use subscriptions::{Subscriptions, SubscriptionHandle}; +use subscriptions::{SubscriptionHandle, Subscriptions}; const DEFAULT_FONT_NAME: &str = "DejaVu Sans Mono 12"; pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.2.2"; @@ -338,11 +338,15 @@ impl State { where F: Fn(Vec) + 'static, { - self.subscriptions.borrow_mut().subscribe(event_name, args, cb) + self.subscriptions + .borrow_mut() + .subscribe(event_name, args, cb) } pub fn set_autocmds(&self) { - self.subscriptions.borrow().set_autocmds(&mut self.nvim().unwrap()); + self.subscriptions + .borrow() + .set_autocmds(&mut self.nvim().unwrap()); } pub fn notify(&self, params: Vec) -> Result<(), String> { @@ -350,7 +354,9 @@ impl State { } pub fn run_now(&self, handle: &SubscriptionHandle) { - self.subscriptions.borrow().run_now(handle, &mut self.nvim().unwrap()); + self.subscriptions + .borrow() + .run_now(handle, &mut self.nvim().unwrap()); } } diff --git a/src/subscriptions.rs b/src/subscriptions.rs index cd7d23e..91f45f9 100644 --- a/src/subscriptions.rs +++ b/src/subscriptions.rs @@ -89,15 +89,13 @@ impl Subscriptions { pub fn set_autocmds(&self, nvim: &mut NeovimRef) { for (event_name, subscriptions) in &self.0 { for (i, subscription) in subscriptions.iter().enumerate() { - let args = subscription.args + let args = subscription + .args .iter() .fold("".to_owned(), |acc, arg| acc + ", " + &arg); nvim.command(&format!( "au {} * call rpcnotify(1, 'subscription', '{}', {} {})", - event_name, - event_name, - i, - args, + event_name, event_name, i, args, )).expect("Could not set autocmd"); } } @@ -139,14 +137,13 @@ impl Subscriptions { /// This function is wrapped by `shell::State`. pub fn run_now(&self, handle: &SubscriptionHandle, nvim: &mut NeovimRef) { let subscription = &self.0.get(&handle.event_name).unwrap()[handle.index]; - let args = subscription.args + let args = subscription + .args .iter() .map(|arg| nvim.eval(arg)) .map(|res| { res.ok() - .and_then(|val| { - val.as_str().map(|s: &str| s.to_owned()) - }) + .and_then(|val| val.as_str().map(|s: &str| s.to_owned())) }) .collect::>>(); if let Some(args) = args { diff --git a/src/ui.rs b/src/ui.rs index 5407e6a..d58d688 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -61,7 +61,9 @@ impl Components { open_btn_box.pack_start(>k::Label::new("Open"), false, false, 3); open_btn_box.pack_start( >k::Image::new_from_icon_name("pan-down-symbolic", gtk::IconSize::Menu.into()), - false, false, 3 + false, + false, + 3, ); open_btn.add(&open_btn_box); open_btn.set_can_focus(false); @@ -184,8 +186,10 @@ impl Ui { window.show_all(); let comps_ref = self.comps.clone(); - let update_title = shell.state.borrow() - .subscribe("BufEnter,DirChanged", &["expand('%:p')", "getcwd()"], move |args| { + let update_title = shell.state.borrow().subscribe( + "BufEnter,DirChanged", + &["expand('%:p')", "getcwd()"], + move |args| { let comps = comps_ref.borrow(); let window = comps.window.as_ref().unwrap(); let file_path = &args[0]; @@ -202,7 +206,8 @@ impl Ui { &file_path }; window.set_title(filename); - }); + }, + ); let comps_ref = self.comps.clone(); let shell_ref = self.shell.clone(); @@ -245,19 +250,15 @@ impl Ui { .open_btn .connect_clicked(move |_| projects.borrow_mut().show()); - let new_tab_btn = Button::new_from_icon_name( - "tab-new-symbolic", - gtk::IconSize::SmallToolbar.into(), - ); + let new_tab_btn = + Button::new_from_icon_name("tab-new-symbolic", gtk::IconSize::SmallToolbar.into()); let shell_ref = Rc::clone(&self.shell); new_tab_btn.connect_clicked(move |_| shell_ref.borrow_mut().new_tab()); new_tab_btn.set_can_focus(false); header_bar.pack_start(&new_tab_btn); - let paste_btn = Button::new_from_icon_name( - "edit-paste", - gtk::IconSize::SmallToolbar.into(), - ); + let paste_btn = + Button::new_from_icon_name("edit-paste", gtk::IconSize::SmallToolbar.into()); let shell = self.shell.clone(); paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste()); paste_btn.set_can_focus(false); @@ -274,10 +275,13 @@ impl Ui { window.set_titlebar(Some(&header_bar)); let shell = self.shell.borrow(); - let update_subtitle = shell.state.borrow() - .subscribe("DirChanged", &["getcwd()"], move |args| { + let update_subtitle = shell.state.borrow().subscribe( + "DirChanged", + &["getcwd()"], + move |args| { header_bar.set_subtitle(&*args[0]); - }); + }, + ); update_subtitle } From ba60a863b25fca861340c2a2185e250f5e67d757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sun, 4 Mar 2018 13:39:01 +0100 Subject: [PATCH 33/59] Don't crash on error setting autocmd --- src/subscriptions.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/subscriptions.rs b/src/subscriptions.rs index 91f45f9..40b7629 100644 --- a/src/subscriptions.rs +++ b/src/subscriptions.rs @@ -96,7 +96,9 @@ impl Subscriptions { nvim.command(&format!( "au {} * call rpcnotify(1, 'subscription', '{}', {} {})", event_name, event_name, i, args, - )).expect("Could not set autocmd"); + )).unwrap_or_else(|err| { + error!("Could not set autocmd: {}", err); + }); } } } From ec69fb54416fad3f46afae0552a950faa04bedb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sun, 4 Mar 2018 13:42:44 +0100 Subject: [PATCH 34/59] Use edit-paste-symbolic icon --- src/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui.rs b/src/ui.rs index d58d688..b9bc378 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -258,7 +258,7 @@ impl Ui { header_bar.pack_start(&new_tab_btn); let paste_btn = - Button::new_from_icon_name("edit-paste", gtk::IconSize::SmallToolbar.into()); + Button::new_from_icon_name("edit-paste-symbolic", gtk::IconSize::SmallToolbar.into()); let shell = self.shell.clone(); paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste()); paste_btn.set_can_focus(false); From 1e8e7feb924ae7bdf566bceec41d0f7061bded2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sun, 4 Mar 2018 16:33:02 +0100 Subject: [PATCH 35/59] Tooltips for icon-only headerbar buttons --- src/ui.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ui.rs b/src/ui.rs index b9bc378..5b4bc37 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -255,6 +255,7 @@ impl Ui { let shell_ref = Rc::clone(&self.shell); new_tab_btn.connect_clicked(move |_| shell_ref.borrow_mut().new_tab()); new_tab_btn.set_can_focus(false); + new_tab_btn.set_tooltip_text("Open a new tab"); header_bar.pack_start(&new_tab_btn); let paste_btn = @@ -262,6 +263,7 @@ impl Ui { let shell = self.shell.clone(); paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste()); paste_btn.set_can_focus(false); + paste_btn.set_tooltip_text("Paste from clipboard"); header_bar.pack_end(&paste_btn); let save_btn = Button::new_with_label("Save All"); From e5ef4a60030e276af25ad2c1d0655ff6f5dafdfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Panadero=20Guarde=C3=B1o?= Date: Tue, 6 Mar 2018 10:10:25 +0100 Subject: [PATCH 36/59] Proposed updated screenshot --- screenshots/neovimgtk-screen.png | Bin 88562 -> 84901 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/screenshots/neovimgtk-screen.png b/screenshots/neovimgtk-screen.png index 05f745cf948078059e01220d088c18abdcb405ca..04917d5ce87641264b55ffc897903244c2279386 100644 GIT binary patch literal 84901 zcmZ^~by!s07dDKdbV(y6(%oH3NP{5V-Q5jJw-VAVHFU?&NJ&d~Gr-U=bi7A>e%~M8 z^}bwiF>^Sx*=Oyw@3q#w?j7+_Q3@U9H3}RY9J-9OgbExSJOLcsb4w%y;0i7!Ne1u> z*-=`{1r845=bxWvNsJi8aBy$nWF+3JdS)Cfd-&p-J$9as4L8`L?1j9e%cXTi(vwtv z*W3MYq`I~>^+{>00y2-m>R}<=HYw;M=p$K``<5=}f`J)#kC|eg>W4!OnDhLO(sZ!U zJ{}$U#IHmD9A(~O$E%%_IR577(}!F)x^mS@EHNaCV643D=$T?wyBOJ!fA1~GWLSd# zy?jP7_}|61Z>XC8T@!mDVWZG`S-Ns@;hK8Q{P#|Yz=EQpJ3%AC92~ID=1#kMCXD(O z5fM?m?w@}khV;s?q@+YdMjU?d16Yv-w>VRNDp`V$1ml1I2&rmrPBZ&QSDY#38<`KK z)C9n1WlaXfot&zv2>wn|?1d)ql{qM;_WN5}n)Q@4+k%?&dU;XB&pd_*I#yN=9pJ#x7hFu3m11QflHN|w7R9orz1rSrF}tO_Qr8i|l!!FlE!$|@>( zaD17Hd)5DaGHLG69sUg$*A%VRT!S6)`}glZz^F_v59Yn=^%l6APxh)cYfK}wb#=?A zXlWU#si|d0sPgji#GW}e>$)`%laeyag#8=J|JTq^DJM~tb~!@eh{`a23AB72@$*@?{;A4b|mlqd*)MZIl5jHB8WmvLrqUcowhv0+#MbQnF6XbWa_2U!1T3YFDS8u37KVC z_V=rSX%}{PizO`EYY3CYV!eHRpfF5wo_Sjy9K#CWiS2mO z_nn(n!OInvX@$V|ZcD|SEsx}^TgL*-L-eorP>fQ6Ks=t425S1s%0{XWi`Mn~iba#a z480{KL&&hP^Sp$}%7HZ~%`&X}ftar~DCsAR&yqa!JVgO`*4EY~(`rrCxc+?Ya&=(f zY-T#Tfpj5h?0;X~+VaK3K{HTJiH=s+Lzw5Ul zpGPN~$$JGXe^Kkcjha|4UF7)8%ROCb^bvd29mv58c5d=Cwn>FDCpb)F+&1d};!P{pydn)B~m* zFMWi_(g#gzUWXbf&C{-K$GH+WEUH9Sa&ES97P(OW8 za^fjeudjjIJMN|@x?+<{9J+p3J?_@l%@&Q)!}hCO-6o_0iuWQ1v8QsYdzB0|VG#Fz z^+}||?FeE}$4p#89MgOUV%f~&Im7GCh;a3UJD}j zsBZcc^z9s0(=jrBij0g@?F28CRX}Ng%`n3?0)l#fH6nB`8afYS-SLZ=+(Wxh`seOq zb>WkHrv0oPYZF2wbB$LD34=d~LX9lb%$t_&mk-^ENd;VUnwnAoH%-XQjOpK8PqV2o z8jPn_o1Pjb#)Ye=|NP^}kB_h|JMi+6p1r!7TEet7H(*(44IU%B=l;Jv8a`9yyUpOL z*SOze54e#jN2Yu_Rk_q_&>3p!HO3LC8^u=TZK-r}oGxJ(_vvfpfEbKRHFNw-8zHp*!QBJuZ+b zD=UlA+uIx2v;Oh- zO(7yr$?sH?Lwk@Sly?QXgMa_<$Nz6LP!5AYX~X8V*7e$Lm>rj%LMwNVG-~Pw???2b z5)*MrFc9I$;-r+-@DwI-OQ%7eLS%ras7-QpB%td2NZN|pj5xBVnX)cu{qd#dzD1*f zLnjw%iI;$fQ89g@$_3;47f$#j=~1cyY^QOf5#ULC111f0E=mxRB1;X!*c@;QT%UD0 z@+IjA(!RckhUNmh5d;E#S=l}9B0aoF!IrX=Vr*Hbpi(b@(lH7WsOI=s9~-!3w#2Gey9F*s zaW8Qg{0mbVJ7PUzL>WAZ#VIKQzq*moQS)t_dCy#2=9}E%se7pvKn9as34^98&!#s| zi-C}?{0q82ODGs-08pta>; zn0l(BdmP<0?z6klE9B$CakIq1CrA_k7f4M%e~h{!9gpw&)5G88)5Sb>Ujkq&arc|H zp4?C6uHKEXD095(NQNEd)^YPcbwtvqXeH??l%dShIe5LabkU`5B4#+TeZy0p)7$DORH z^odD26X>n!+`80GO(FYGMWR5dQh7EIj~@IM4hzLkE1G5v%*(@t$zA`Dmm#*=SLZU} z#m^M7d3SrrNTP-LQkF=h$~p8Vuf0YDe#y$|%!Fg*O;5=){XpCd4*n^Djr|~(vX4zn z0Of2Q%(p7jYLCxPO|8EaPD>qJ*e)Hf!#U5&H#QEBiGr0f*Um=|nxf1pxFyiIf^}IW z)zk`5a$dZA2?U;2958YD)^!BqJAEqxchX-L?}m-CD^4)<)Qs;^2L{JGi%IYcyxx^u z<|Y@`pH!w{iM^0cK3d+LC?X(4U`dXuAJQ=5NG0U)ei3#_{zE73=wPWKC|`&HL0DKf1*h0e~oak(4Ihvh{;W;<6KF0u-MkP za<*>rp=Nh6L^Uy)^E4iHKD9b?F4$9^28Xc9AEqs2-?(b0)MWWKeda;F<5>w`jmsR^ zhPUTK%o{p7q(DFLRZW1>!zq$eq2S{l57wLn;5U_OK9qUJ#%!soB=h{}M+5}qaZ-~5 zV7Ecs`HZ zNv(-Z$G3mN{cs{P=;VoS?}&v)$&Tu>-x zm)g4V>HTrFCR4QFG07Q8bD|(re__m&U`D@BRmGYlmQZwot;04ucK*BG%0j||3uZ$bt;dots zb|b=rxtf@YW$^Q)>_9V8elaVVZR{|9sakpwk=brro`jUCd7rYy!yQX?e0KDEBj{2+ zYHC<)&MZ4-e(rg(it5LAnqe3U_II6GewJCklt92V{y-qst5_pK)x;t3#NgsMBSKqU z@aOgXNkv&>cH7~a6iJqcwXE)u`Le3N0;F8oYw zbulQ29W4k59X>-$>ZrXTFr}YIGx>t0$=ruH-Jl<34E720Q(Fy;42o1(cUK2MIe>sc z8a;1JVY0b{zpRPm)FKGrX-krg3^6CRq_7hv{_fAWSlRcpst+GJy#2UwzI^Y~tIfJy z5YDXUYcAez{)qdsb{?(b!z0^_JWFhxgY(YoE&EF>&besolH1Q}NJpmKpNw+$l7va? zOTWDkN1A8=cFSDrqlK!P+4b%>DmR6}ZL`8ong0U1fC^~BAT>&6`Qr%b0b zX#$DiJ}JjEu7Vx75ZLA}j;sD}^YaNs%&Aa2ZX0^pk)2uq$94UvTA+pf$xs|=r zDl}o9o?I?|ORj?jJ~XxFKT7iJ%*N z=9XrRU2Ek>Hm>p+buGA;9lN-sQ*oF+zb9Nt#o3YM)RpZ(W7f)Pv0Hdz;qDY~YtBlt z)Oa^w3aNZ`$8bQR;gyVEmgX`+@s>nyl8)s}K>!661vyUW^yhdQ&i(VjO?Q_I>F*v* zL3wl+2eaw$K9|qFx?Ljy;JWgHmWwI+B-}B_nNG#s9KY?;tv9q55%CjjmlCM6d!09G z%eM4|=kJD&wArVY#>ivpNr~_xg!!6AO@hS5ioI`by;3JDsfk1Q64c1!Yu$UTT@~_A zG>zUt?ROKN-l2U1fhw)%)ZWD7D@Gc9)sp)7_TOwPn%MavEQkN)dgan*Q!j zM{Q_C{29s?F_Ox7XD;Whycgw0n=R-UT-2jxeU2Me*jNia+uv_R3cgjEY z6iO5v;&)y987WUZYyZaW1a36bRRnMzef`lsZfHqyvEl4xW;8#9=}&kzJodMJ&l9`7 zJ!{=@>o1tbHUZ9UNbtF$0wUe0TFF6|y9#2=JMPp@wz*a#6V8e>EQ`<PfuU(*x1>uyA6M76KrjZ33080d=rinMl1NAU7zF zrF`Up_yuOIKI2i<@>?w`Dq;k3`#<#z2}amLfgAxfEp0z8F0QExZf_a1r5C75-1HjL z-@CdrM7_p zuj}=-W9h}`W{kfDTOP)=MkL6KQ{T^PVJ9*&qRW@=BV({ zU0KxqlCDtNPJ)msWMH(*RHf|>!tbor{<9-&H)LU|q?27zInq^+_2n-+9vw+7tyCQ~ zH4h*T;N#;L3X)(5^YEz0#l&!{eEJk~adFYY!24VvcQHE)bz1F%?}7L-8XLIaCyeQ$;@16L1t0@w)x?~SVcHJ zY}ZFal~DP&&#PVCZG6uHehIb<%xd9`_8Qk;3w^9)na*}vcL&ec*^8&6!-V!qB_&C; zC6w>!mS&{im2fu0(JdF5V1(sV(BKw058yJ;siPjr2i0Vv4AcUZ$d_mZodtmxK-S7K zKDPQQhLN7d2v(C6O~Sh%s8Pvo-}t@c4vIyLaxt%KlJa#tC@{pOu=2<@?Vc ziW2>Op5$LOj$Gp*1Jvi^uSob~-K+QY(y0jG%#~g##}*_8!>k-d8+&qWlVe|_Z0oq8 z#a(Fr+i9A*J)VPyelX=8jV_5SWiYtsWORipo)$n*pf0+ zkQy5syE`@$ydaRs<5Za40gMv+niOA6S9hsq1(gV&I+#yE*L|>8*MA=OZFkRnn+0oS z>!qms@jPnh`O^)7&1^Ysfq{YdVaq7kp}nso+b=eo*AC$g4NaQI;}t_kDo?`i-zr&Nw-LXL#8BHExJDsQ z=d3(-D`Lm`!h7E`qI=fYR-e4GrV{<-+YYb~F5mcTw`4%{eR~Db=-&!u5IZ!*QUoCw z8&>&0jJ4kOrQ;H4RD_Y?5e*(TE)NGz;X1pVU+<^iB*hQ~8X01u81GC^5^-8n0TJ5N zc5yU{wc~?gY!7dfbpQ@1J7DB24RUuM@YuW04V6~dldH@U9vBntldEhW4bA3#cD?Ff zZ=-tt5E+hCdG!DULQXw^xAD0;QiC%#F@d&qIXv(alrIMY#(a?}Kz()x_3!t?6d67e z_K`kX$#fYd>#wlp&Bhta?xc)+N+9hx5Sw*-yiLACMn&IV?+}*Ne`Fse;|=``k5J`( zC;;~%mT;87!0+%u*eY!Q;gI-)R) zjD%p*KiRj!)Ugxj9u|q4upW z!ONrY0qu*l?LJ2>q@~l{+2k3L%1+3WmAB}%WUX|Qw3=z7`TZF;pbmK4YH=Ej29HvpiBxJE0Dl_XfsmyI75* zHyaTZV>A`c_4tqt;rHBSD<~-Ny;~M@Fn;y=wJfkangRC-SMTmNN*(6veK1GQoc%Rh zgL87;=<~1OI8kv%#ia90)=F`}AtRrn?PmW%1=d^NUnF)(SKl9o{yP+tnt>xe78&_M z>98M_wN3WD5rvji+*wECDy#ty2WKQ@g3{}ou>&#>1QUymKpl^uIZOM4=c( z8zUqC&i`~>HJr8@63QGarrb;aN6~o>48*{9*{x^Bx_s6Js{x|C1 zUTkum9cs7`FGITJ{N1qMzRLT(&uydCX3iX$=Xi8o>$M*tl_GL}1+cB7xyAmA*$J6| z|GLUczu{yoRoA1G`cdsYB5-_#SAkNdo_=)~esZEN7?(!U+zj+9HjGUMoVK2SovY4o z1lZ44xVRD$o_sGuLf)Ah1O@mM{%Ay&D8(}I}86Ki%j<9&ZD zDSPTjF$iD-#g!QXXq8Q#Y^7LAXDc=<1nnv4OE>|1lFxE|M)^B5KTRHum?v1xX2$Dk zwbnxvXG}Ei>sKt}P2=XHwA7DMZI8BYq@T{~_uVW=N6d$2?Cwu@h$EsSsc2|I)9qWc z0BHW291uNRmTzji+C}cr@)={DR9ZS5vatFvvnVeKQ>v*Nt_-&5EHe#Y#!g;5Q?! zwYUQ1`M}?PQ~Ze4wy|MM&Cjpp#76=Gx*$p4zk^yTbiM=yF$3K#lozT05_4C*I0ly4 z_F{bvnEhiRox*Eb|Bk`H_i^D#7wCHX%r3UmgW8|Gj1pMSu+lVdj=S4X5b^7mrCBAF zJ)y8rM$DaswPb+kM%JiO7Xp-*YD zh>_#0)oWuuYIw2xE%-&)R{-3`IxZqTw0i%1V!CuTKK^&|N}G3lc0e?Nfqx84)In&z zXxVya>@Irn)4d#FT1_RE-C-ZJ66ma)?<^kIyV2Qg$cqM0jN*QK^~(2pC4I^HZuwvy zTsi9Z_&D3a_STLt)aYx%rr~4v@y+h062Lgf73u!`5dm=-I`r_lQVNfQjLaHrq(L3t z3JMlY+!eI^KqvooT#?n4d9~Cp_~es>Xi-r6)cM}wruNCZ#yPgv0~LDTUVrVvinF+T zNcTT2K=>VL@{mWdMX^FA3dtwj0_j$bKAJ^PxKb#z{$04BvZvqWg(c7z^`y_VJ@v((5QcV*Qv|i$?DP^SZj^h78r~jPSPcH&zNhrVC+!wA zgbQeaU|{3mG+#Yx-Cr&c^SNHLVT!tnsuF);H|q9W@r3GC+#l96>DHzA95+wutRx;r z8Yr1PwvN4CGMDc-`(ZFvcz<8FYoWk1uXx7eETNqc`XbYQG1dREX3)LRwjTwRKR&fL zdQanbqk{v}%t8;9#lzt%8imwflF1Kg9io?11oC{zE~EUY@YhCzO0F{d^3Kj4$BzUV z0c7bs4%?RQ7zi<|tkd;+Twd5NqEFb3{XNMB0X-oJw6}>GCC7I&3-?x(SWa9&+0e2T z5##zNx2BDiy8G{AGCv4Q5ebAAIlDI$rt`Oo8`7_VM00;J~&8xi`?g+m-Ot_O9g7dkXAyznF7_3p(1%{TF|8{U>$VO#}JV2Yj zuK1Slcz@+#jdJB~EuUtZh)=;wbn&Q-AdkKRe7a*!PE1bTFX+&!Z86_Xj(>W=`DEw5 zOe-fhYJlXl&xRHxDtbataX<2O3h_CjV&RF^1AB1#96^{2s()J;=*`ZxSzsFY!npIL z3adQ_vUcm*Wq*!(wC4-`3Bl9fAOv$`9WIETN z4m;;(s88!LysL%XYJP-fCM6$i*stwZAocp0T-#@_tLqGCjp!H5$%=^9CSqrE*N52$ z7IU#C8qcV?RVivZBBgo+A!3u!JZ&p4LnPsTN=PO^PujXP)RKw^1~%pB)!W^<7#C?* z@}=UiaY)6*bhjCp$Xm}lwC#TO_f|U?YzKV*^5yyQX+`9(CoJ{Sdac8zBut%&(tFSKD2p+*@(WjXF=WqChY+mw*}M z{bFO}a^GfgQM$2DCnQQNuH|->m$~9$gM&_N>#e5;b2m{+^UaWq5D-YjC|JWky#rEDgVDQC1BJF| zpbjDta7_WG9u^>UNhRvA1d!GI$p!);r;vpF^_@jN&y%lLFd%fDf=i3;XbAG=pU3M0z1As`{B35NqbfQ*aSsg^X%=$ROdAHRhBiNYMrF*^1w z-!Mh86x#CO;lx+9>$ql|^f7;H*W*bJMT5G)suE z`c#!N#M+i3(&lqobVq*n-AvH=zR5sznV+bKo?$V+tk z_Z?#Z5h2s{?au{i9M<--QNQPdrX0`cTF6qudh3skl#f70$}NpqXC!n>c38jjIlv!- zaL0!|e=S*hFJeSJ*Q~c@z7oD><1Ex0tSr)xowwcS>=JLi*oQJ{UiU)G9yht~ zILH7c;&2%g*)4RHo~hXP-E@MI=#WE^!C-#C6C)754n!Tl#)&-QHVz#Mx35$(w;~+3 zuU^W8Xb3}U*3xmFPC-Dz(^`BP@Qz~N1r}{}xNqUtSI2n0o4{|GHZzkO4rl2gY9Lk@ zva}?$8aTU`ct#B zkmDy0RR1*)qyNZfc*OZ;mt-a4m=#tvm(#7mGCgqnOj}6*q0wgHh_3ItX;#2#(l3HV zM@`lIE6&4v&S`f+re9kO9Xc52A4rA8gY9dvjTrX=s-ppOT#BE7+E9(fv#)G*p)MSNCyyxjqffqrxEZP z4#yN>o{ADvj`GKT*G>sgd&~t6vVcmhAwuqzj)tcjY_SaQ{g&gBlC~#%t43i0IHQ+3 zoosaB@}+9(-_Q#X(QJj^GtSW8>gQ%zk#FFtaYk4kiHN=1PWfFtAPrPKE3+!Mm()PA z`j<7pAQeD+SELw$M$QrZ^{b)t`I%O;H+pYBE$V0ndrVAv<9Lw=E84Q*d}NaAVZ1~f z*o7c5FHapm!E})C$!F}Cp;XfsP0Fw8!^vc6M5!1_coR!>v#8Ot5=xjffyz$ z?$M9QJKkrpAIgf{{y2N~$wVqH&jq5WIczQ~JCt&FL*Ok?Bqi zNA4@M=o1YBMZZ?Q;kBJktN&d~5nMdG=d3j8578wdP_L?YmRS(kr(b;T{q?g;%yWf+ zKB@HE9%hd^HzctPW$ca3!O_vKaix2XYVC60%U&S>wEwVM;)s|gjf?y?2ZY~Q^xnr( zhCM4Oi6-VubAYg(kQKpY`#s_b`%rPNFS%%f?cuYBvxYh!OVFeh%m8!G*{XjiD5wVH zMJOmJ5s}fR_xu*8^8^ODR@#T2``)rDewD;P-Ad^W&QyZ<>f8J_)`M8r&R^e0d3g$D zeK3KqS_}f-REEbytRK|&oq5H=ODaR#cobraDr{Ci>H;xyrQzyeen>Kbw%WhvgR5SV z&uj${E(pg?CEr`?iwCT9qya6be5qK&!8p0Wp)reroX;yQ1k8`$N-cDI@5pa4qBzor zGhAl)072JWlb2k%KnT``>y$lto`#<0U_*R#eM}i~fbujn-g{R3xv()dP-e?Y_TU;2VBEvbKA8yPv zwqNx0NX4?*?izN!Vd42cFo0C3%Qa6MTUGTDu+V;ZQ9Gn~=E2WN`PDk90GsQ{yI6Az zmuu-lUiRWU-Nv<3)xK3tTe$Q_+pa_Rgw&*vo^NvUu3R9{jE`L!nbP4On~-*Q@7sav z>JkB7M1b0TYka-uVs|359?CQ2gZ|+4bX!!&<~m>qH&e0r&dQ2@o`0TXL(l;NTz{ z5l1j9#4o1M$}Ow3bpMmDsBvlPsH$kuZH2<8&64AVSpp3}GuviI+*#4)Bcphvs4Rnw zA636lvs!<31$kJEua03Rq7|9jU!X2oxy*HL9!{I3W4xow#1^df857ZZDUamT_fe-U zq~(!ghz6+NGk-Gk(1e=w$L6Y`SC`r3pV>?oU1Vb8u%{2By=Du%Z}nYbQ3~pxULxVg z0O;Sn>3l@<=_ya4ey9^zXtzbN|Jrkyh7(>z#c#3r^7wZ=M}x!aAVA_&Ycsu=xAo7o z5wBLWgTK4#H|CjSr}zwnYiAc1;x?X~*JKW5L)RIW<<`-?zgM2l9SOu!Qiy8muecb7$r z!WIkvE-kz7>f7xITa|Kq_|EMpFr?XH%ij}*K|#e83`D!{EC$MU2Kjv*&_`{3<-RKO=d;r@2znfLAG&*8jm$N~M6qOq_ZqJU0laQQt<$7A z;FOM!H;}WQJnH(Ld?aAAedxL?gW>?vz$Adq?_GDK1H^)|&dxPJ4?q=Alrk_gs{!q_ z7l?>DfZP}lNkxtOCtEaI58K(V_Jh@*PNo~Cf~-csHeoyIp{NFhyBGB&ePU6#I(U!+<*HNSRyO_R}T#s zxq8}Xn@V=1Uw|Ho4~MzTe@b*L4kp?E%ggdC-QX3`#`^UCRHN6qTM-8J)(^n-R!;7A z;5xIT{gu5P$>%>eNK9TF%-5ESTLK+7n(gP!Mye$)E{gZ)DW{;TL zK(Qp?6$iJ(X7MUsi>78U`J|Hs2zYIjlL&Yu(Dwb&c;-FSYL^4FTZ0>6-m@Z8XZ8R^ z4X!vbT`7yGxYc(0$XBG5hMxCHt{VPQPPz3fn@4T#Oablowk|#P$#3(x9&Z$Rl&iIM z!ruOAQz^3oIuv8$x1jyiLui>+ZHj=Kb<`rMh%aAd>lup6&hUB&f&LqM`VyIKi?oC) zZPhj5r(L+CfSSi@Qw9F^)g0d$Q$I1SlGLejB6Kp|8x2I|#7PHOX{-#{{R!Pio$z7VBN;;^Jv~onYo`HS;|yRVdPseRd_%@cCKKnf2k_ z5+!`|A#1v?{@tcSUjh$Hy3O$%M(2>yrNv?&-Q(T5l0WHcisZR~%M-eZBUn{Tut}@M zL9zU@KQntUQj%1wktsHO7ezngfpXA~fm5R=Fgc5`v%m7HTg_X`%G z?}RJN%^(f$bKt_ddx=oGNcfViAb9!Mkb_vE#UGtW2$azw1by=`!-v);6ZTimTNfD| z`~r|@rMcP4%cI4KKJp2BUl;-h0)${wpSHHuR>()vxTDH-8qek}vY$C_Cu#i7m%=3T zIbe72dU||#<_Rrt9Cynk(D(ejJ(PT>RzZiNq34DNyr;mA3c+JkKl#O2x#rvq*YtP` zZNA=!@&blnOCK4|;7=GBkfu?}iY~MQba&2>Ob-sv6GL4V0z^!*tr*@F!`TYGk=A%x zDe!Ky=X&)sz`$A!0T@G=zP}D@d0xFz^TEqEL&b6Wp3499TYSX2sJTCzcbmygmy$VtUG@bkXn^mrfy0Xy)L&yt=*FJU|_4IzLvBll|Ly)fU&)C84087!eU+ zW{!bm`NUdH7OuMrG{a_e9z-t5pRN&kmtAq-W_4ZACySJ>XfPfVl~OrEl=DA00rui> z*^z(y_HBxYUrXaXY}UZyXsJ2FW5w%F_*``8$gHX1;3_}sfdGR7-JxI6M4Tz?CJ4uE zS7ZQMjjBzp!=12yw3}}>(xY&JlF=UqItPeG&p*eITHbn*fyrw(Mg1e%1 zXN9d#ni^4P<$Kyo5KYMb_T2>cQ8y~9 zDR3eM7Y`3;@TUs<@ND+Ql-3(m>bH4b&RYN}=+3goDy-fAV2-`T<9HN!o~n6U+vS!d zfJDivyG}3FLyNk7&|BP%*X_;#bXh0Nl+9b9hoy(OY(Eh{127j^zp@~~!_K?f1OdZ;XmPGiVi(!e#An~Ujc?zyj&N+_RuA^QadwFN zS(NR+@8>s~!Eb72=G66)z;V&8vD)+0c*Pr9?!M|X!4i)uS<>p1<$3e|jP$)Oui5i4 zQI4-2_deww*$^_K0dn%WFQ6G=nLf7HN3oU%*E5MJs=pMj?6x-;FL|3*+YJN^8+Kur z0>yYk$LRZXJHpMUb`c`jYUA`&*u4_DBN?h^uYNLl1?vJ=3KYmcrC}t4m6f^qW9X86R^Th1#TH;FPI;^(49xebojehJ=of)L~8AT_Y=Je^vu9JWSEBVy{K8@w|UE zqi2q#qwuLpsG`Z9Kxank&d*m3E#Tq!^-+pH&!r(I?>&7`YU2Ui+7KrA{5m^q6ZDaO zxRG68kJt4`z5AeG_14XQ^63PXS`xoP$bzFc1mF9TajTuyH+%9G{!!s-j>(HKPox?j z-(OkYj&$U$zlr0d%%T(T#XrOW&16t(H6jqVE~meMXm$&!0zk;kG|fpeY~nB$Vj>r zV9KY*s}5vjN>UiI$zB{KA zay20-C0JY(zy(69Shzq)aPWAwi40Jefh1vYSdkp#ylRo(XoLEbXK{)*9ChmXc6!d2wp}{|T8PrkX|JhQO zv%pgT^@DH$UKY=P3&?6F-iFwb0-6aR8B4&mUtTc0If~p7hRW4t z{Qq?{e{OkK7X{Hpd(9O{pR5}`sdeOMxBi8Eu7(;OPE9S9&*^!|!(A>^pyR$K{brR# zV$w9P;V*z;9ZCY=4+mgvzZCgsMXl|@oU(+(hm+q?mFv#UH;2=cgo5^5q7O|6R8sVR zgH>?x7(F`^L-nV(g6;=D%7!EQH;wu)GlBDZb#9t}A5$#cS}|g4W8j;-%BMS^(Hsl{ zT^uA0U)l*S2r&J5T41`|hvu59Vq!wG`X1chzI{7b2FF!GMu0P1@Kwe)4A|Hk&Cnwq ze)qD#=<>xmn*W{AJd_0{>wja$tEDA1I5N^>%i{tYwu&Z6>Z>VWgxj4iMKm(X^*wp) z2WXV!x>r2|gHBa0kP4pI|Aa@%1;9c&3uFT9^0*t&c#{0k=icHf^SslP!|7&9?%MwG ziiFQG{N|kCY-D!TX@2|D$7%Oh?f)$G#XVkk%9K65O+4?*7hB*69|~$z>ozbGWE7Nc z>-q+@@c*9v^@60c(KCE7oG;@rxXwF?n!6^7{iCJVUk@yZ5j(2Dyls zmzP(Y+uzIyi9)f$ogK2#`3RrEyG{a}xCCVGWm;`n<^AM2h$p8~E-73$zDKQ4{CDdo zxjn;=TeS_S|I}Sb%5rzoEEpsk<-Dt5{;{W{pScJ^-zjIbx}+nTG@}e$0fI$BDzSi zur*D4zY^ln$d;!u*s(WR2S3z7BG{d(@dU0izyn9?2CV4(vmNl~(=NngfaqgqpSakY z%+AX*?ifnn0CL$rHngJ_=nBFW7&enGqoA<9lkQ-nE@($>HzV2=SRZ%Kzvk8f)RV0b zm&Cg*qb_4@geSK;#up~6U%^(SU=e@LH8-h?{eG>n_K-Z#+}r${`}_Aza%orR91gPy`UefHn-BZ-ZKZ}@!_CYd^7 z9-jcNb>6l{>*?HctvQ8^{3yJ%fsUvBT%dSfE1Z%6V|-T3_Z{(Y7t_nug46HAOTdH6 zcT`0?zV`({G8@kiYM;u_KONc^I?l9kp60k{GHwW4tR-C9?;wpfxFdVEea6Pd{X7E5 z2E|36eN^4)WE(9bt9gmqZn6GHuNbYK(0qVc^PSnc{uUJl#q;qd`MHRYtmOLW*Kb1P zJjm2J+>NXNibTZ4*+AKXv7U|Fd$a7DaHImddj-MM2n8n@WB}Fd2-PN|{V%+X5zrA0 z&ko4n@m}8;?9u)r_xXHKKNLYMl9uwm#|aO=>a0tLPNZtK^7hb(VUj%dhle&6S@QcC zeO`md#;-w(3>qL6+!Rke#J+om7qx0e=f<=yPileSJdWlYXfi2s#K-EFj6yAMUIhj` zu!P++J&C^G=cNgkb3mtUC`~ny;@;^{|9N|3=3l?M@LEu`R&xDJu!qRkpAC*UfNt~L z0`;^Bpki~+iGABMjS{{9<3$`>uSY%MbL?FiCF1P~c_hFmPp;h-<higo1XoO7hQI@Zu!J@kQi>3=28T0#jl;+m0x5L!KSEJ?F3^F5V9^kBP^gP zygNa8^Kkt1(c=DG&!=fdBwP~jjvthKov2U^+xVTcB0=x&okHnVW=hnE7?;yY2l@4- zIg!X-A2Yk#`s$TF;~i0;PSE78L+o~8o&%gFnIHN@WF}T`_ZWMUcnu5xC3_h6Q|JqX z#qL!8b)s9En4bLRf#Jo$jP0kjccXp3pmE(HRvymoPo?bXwzf;>=;W*qsP;-B2C!A% z)AS2jl9!z(vt>POt}6z<z^QotrGTgG-IX@M)YQ~_i0Ih)4a7jbuaa)b zeUUs><`g|&`)Ie_`g0W33mZS z2cPRM7Et4!?X6$Wc09_Mn!cFT^Pq&Q?0DqB2n#|aOW9tQQpOdzJHU~Z8%kl%w{r2f z#(?hmz!W4dj`~4b9&m=p(9Wtma%%fIOV{M6JU{T<+3!yzi^?}L-u+Gx_WRHM%FOsn z1=PNrFx%|ggwgEoVyQ>y`9fCnaqW6`E$B6y`-QA(Y0_|ZyTxPxnWW3Kf56Vj zU1-2PW#-9=BMK_~q#Ve^(XETX_m$U6%&@GHRat*)XHo#qN4+nZ-+C8)Z1O?0dGco+ zU9yE!jSBhPxFl8N!un!uApXYI|53)Zaj5d_9Z>q^ekzK6R{jZTf@eWtO!B`R!eiA% zFj=YXMO5!|Rd-$sb8~jjfj4kK5aqi&s0Kuq4QP!r>LRX;s1}OP3?Ch6*p7d*liWCc z9ux61U+Nt7&^KC1oJce$Eu6)hz1Dl$f(qV?`dpEcsa1{ZgXj%bPD>a8GGm+X6SJvA>$<>%IITiz)kx`-M0*78246Y>Kb#^CL5> z%{%abmb^ZH}A7#x#7#r?{}xLNO^PD){)T0 zqXf6(F2ky(%OM@72PiBO@iTgc78_egNRUeL&5tkon}=hSO^?&oWrjV`ce;G#`3j!Jm(8b>c@Cmp`%XGo$%V4prQ{y+(CfwYHj>p4M*H($FxD+tx;O zsJEG=5f*M6XBAAZKmC#Ba zJY-7(6#@qyRl2*&X5)kqLSj6u{2iD_j?3$P44;h`!HA0!vs&v#5{aPcHIT3ZOV_;_ zPe@_DCH_=Gh)xTKjS1=$gXnF;{~_%wfa2V`Y)ME$f+oQ|BuMZCcY+6Zx8UyXA%qZu zCAbqDLU7l{U4py2HSXQ?JKQ_-&zqV1s^-;HHC0s6O?Q9aIeV|Y_S)-I`SlvCH7S0! z>qWG3Z$`z+=;JHpAcy?9p{1B~^b3zlKwv39m%ExFv=N@#8efopb!u<* z`zW5)eZs1}C4HXjvKZ>l{y66-esV87E*3acNa6a;RN06rjqI}&s6YMOyZSUBq%RJ8 zy1{}t&9^N0YfQn>*;%^mtlGroAZdNRdNI@JLcGj@z30=d0gXn(-OCN9x%ssY=M`E&MMv*n3-mReVxmk znSFMu;O0A*yF3!X!(jTp)^bVeG@!t9ecsoia{r*SbjY#0ukz=p#@Cr>+1)0(`UvJC zKZq{t_b3Bun)&v#6VFgjv_u(JscIDcvZlpsinE+&QNykIq(nsgCJ3CoEjvex;WMG8 zfm8>o>m;4sgQg~dKuiY84+WDiYlkJA{ObSSXwcBa=2F z!0eT|VzGYUnrXyvrv69--(J+@}z`=Iyju9G({^qDs%a4a?!ZulRePm7>#7*W9h=)5`|UREe0~_ zdPZN>Va~%<_;&dvKXesw(dyRV6NgcA=eM7A>#FiqUcNF^XQDXSG1=XfM<-~_mbC)w z%7|mDw!(vSocH2&MJg!xVKCU9ZFNK$S@o|5y}cn#NGTuf1yz3|c4dlGmEUM_Jwjrz zkXS>8`Z)v^`q4F|u#~4UZS^?Tg`l#Qz=>UFy6VRae_AE4o)&W>+oy7~A%$!y+$!67 zHPbWHPYNC+hFZ~}vwCZmHMuMKzUIBcAMA-s_>F~k*;|BEB&B315&Y$1+HJa$BO|BZ zHP+1S<%^4NTnlOI@2)L$svGo}+?h%%hr^|1;Z=YCag7RdGN;YK zs;1baByIDnZ)^MeVM_UwR8-mBj>E%dZwb&f81PnBu~8Y6_1uz}?dCsHa|h2fq6`~$ z@ zw04BV(NVB=8JE7>d&^kbRZ-*B7f-M(Kjc7Wtsjd>v%0nu$F%9D-cT6U*C2DL67D3$ zUbq+NM_64AKUPyfFyM7dv8JDU9GWb&sf%}#-lX*^O~6kMW2R;3Z$u0ZA{6Z?&huhT zbyed-@dil;J7BqO#rlx&xH$ZUg$2+On?Z{N@c!F=q0q`jVB|As>VG`0=}R z1^W}zo`Yl@F-F>{9rmy0BB;-51!I0OsYB=p*tiKQuMm3c+4JSK%FvbFh;!&cfMiJa zmOJ6_?iuLlViUZc(0Sad?=K<~vUBQ;vP+^WFOQX2{kW2lucoeUB;AOYT81P5};Th7{$KUjoZA@K~$~?5WWvilI+>X6b4r_Y<&dYI>w_ znB^4VAq#1Jf)aTSYErLtC{u6a63Ne}1Qkkh{W_jNmp=81d}$TS7=7yID`4prQ(bdG z1EO!Dn^U%jddxud>yKk&Uddbz-|iukrPATp$#kejvUaaaB6%AZvBjuU6hDY)Xt0|Y z8L1^=q1@r|U~SJn5;3t&Dl>m^L7|Y-DHf?LYl=W7X{kKqnh|Cs>eY!Z{HSyrSDrC$fzwqf8?ND zWo^{`y`{o-9#t|j4`jIgnaavePMewDXxga9kMq7!O0wB4gg(O9nH@_X-Tq@)`S}Zn z7*m|!>AR4Xm55%z3!R$d7aI1B?r~zu zVY24PO(QQ3w014ei)*|4-nUgtI#DK|yX?QPydX5jE*`z`;M?WsMuGUX4FHRH0d;<1`*b-9cEPOG=QT8{3i1$imYz?Um}5)>fn%TVBX$3b2l~~!wcocMr;gros370$EEz0kHiLIMF4zY#*10G4+T_ZL5-VxM zd4<0cXuEfr2ED)RDsa|m{g$=P>w&-y(fdfzy~=<+_)$yD(W*n*QicMn)1jVD3|Ur| z?nXh>iSc_ybNw>!rtqte|yXl7ItHoMhC^W-Ex4Gm3F?j-JJd$4kU z{M!&JX*`8;Q`yMKNNWzQ8`z0#-Z*q`>PV1}Z%n5)vO7cUU?vs0JfYU94M-=cvs9>OxW+Rdfp47QR0l9d7 zqG`G`k_f8QU9YD0o1X6|4;k>Z26VsCy?9C)yoN1Nd)Xr~VP6P^oAY>Zums)@45IOI zOB0WB49Qd1LpmnUS>7w!NI2sy?9zenI1T!pk|9ktEqp3ZN*pYxUfC<6-klU(QG;)$ z^TFZpcrFK+NRmoyEFl`XmC@Ic#b(&o3j!zG`K-P~#PZ6j#LkeQqwo52D+Z8EKR4ZS z@FU!C7)))&NY_?!3--d1f!1!YQ81$FM6f+?)X`&9>pA2)Ir8!2$1f<-oxLg_e_A5Q zTJ*C9w7)cHYbb$48=aEE2nN0+frlSUpzw0mu(>fXx6;g3;wDCa^l<5ARL>mn6q_+D zbvN6UslU~kJfp{_`^Eh5_>^I~wYrSW4XShM$!`aNyZRrAh2Y)8A;P~U>ts4v-{Iz0 zOd_)}n-y%R=ngtvl-bkMJoMh1)bonTO@p?7ea4(Qx{b>43deS_skqh`P8~%po4=>> z03}l?f2!e1`Fx+Zd8Trgo;RgyWF%X16}bagF8vYt3?eo=R&qZ)zu=?h%vEp@aSb;-SU zOt1X_F=+ep)nAO3QTJM4dHJBkZgH~0X126!^x1=Soc1N**Gm2Ab5i zmA|rb%}Pv8;>_8w&MVAEb8ri_5XdEm@C!jlj%%HDs+;)hKbgFEA1o)*@2~3ORC;tZ zVvid`=h)0xXit!Wb1EDM@^kzikEX&wW;;~4V&yKwoN4$p`SkPHElmLLU|U}!(FKcL z33Ta!(n!(N{6xr!Pl?7-NJ-g#RAWRWW5IcS#Q9Hg5i^c{?}|#IZ2qQ(=*b(SVnWu1 z7i<+42~g#yBy(Q}k_08Fqn0-}rQ5zxfR6PnDRVF`eXK#LMm&d&_>XmY8rY zepzWbUO}iaR;(WfV74i|PW}fap;2d(=nS0A7k(yAWeUHXj*HXS!jl>7*4Uhm2-dve zj&{QkO!&s;bR}rYPRpLkIUx`{qS^6FvKp0flhSEpyeX}0Bgvs%kN0CxZ<@7DPZJ5k zTSc0i$OTsl*&7>jJhkQePqsRd*3Z`e&lsc4b z6;yOF-wM>p7he3XahKd%^!qtCVYj}%4yJ#M!%2CY6Icb0N9Bj9ibV2Nj(3YE-x7qb ze^0-=)2~5oq!LBlLP>)V_tv>@TAr{wX@i0>kQfB+Ey?usU%h{*4g-+F6lPhI>jxbd zIdC0l`M+gOK(mcxr8$#Cnq%D*IJPYTjph!t>~dlV<@&)+a!k^<4nrnIQjl|l5k<%6 z*?D{C(ru70$zN=Adn>~-2IW1f1zmP<)P7$cyjo}`@N`~#9q;21ar&0MRJxJg%0V}* zt)oXx?ylOha9MQC%&i2DJNr7MA4i**jqYt2ldmQ<6u6ETD|xaUCt$gknWBVJ^Y>gZ zm%_*H+s+LvV!sp4P%p@LM)i1B8x@&W3^kh#t|bUP-%sT(9TML`se1U~sBc(YgFH zt2!7uW(l$r@i^U-`425fzG59WR=+WdyE^Vm;3KM1RI09hF8d|HW%R&d)AJTr51*5; zt+m18k5mub82iGG`0AuaKJqx1r;>Q>iw=c@fK*@jIw8R^gDa_ia>@L-{YhlHiX;OM z&g16eIA~;SWLnys=E`(_D%{u`?H%#J}+hc2w%CAd*~M*2(!*YCL-K zv}O}@5d&FW1njk%rfPEU#fmExDlKtSJ%3>sbv7uOho|rg4*b>}^;ij}0ibN74eQnA z_3k{Rn)ztPs+h%kN+DY6AqpnROFnN358Mh~>uTGf66!Hxo&t1U)0v?{Kyo&21br@6cYLhF*~D`WI+*^72O|%(5ofO&F*5Fih+I=-k(xs-jHc zo{t<}*K#+gs-E9RcJ`4RNXMA&-%jj@8^1~k$}cS4Ts&?SqgqW#rTv{4{SD9w1Er*o z>POcs)Nff~m%c^Lx-IsF%&$Z?xNg4RtROYFlmE=BQX&0|EbU8Am({FhiVQ@H`;(`rOf3`N zKVR=wc=Y3y#GgWKb)dPBPvRg2oSeyy%FQHe^u%(T#1jmgp6(w;sG&ni_+RSlc?-2F zjV=%4o;=R8#KuCqlV=!to^FX>Ky@v~@+d&pfT^UY-c5PfvU2Q~>L?Uu%tEO8>vFWp zh90E5*~PaYbuE{!JqhLTWg+v?C5MkAnQTGM=TWL38#B&q^)@&qf&ez6E#@GMNd_f z+SdoE)4!kVX8FF+qTTwFU9~axT)MHcd((WZ$Vdl_mr4>JBhTkpFfsB0qgOsx#alFy z)Q4cpe#YiWGMdG5TW5R|iaCVHJFy>s%8P7|}Qrp(!SnSic>6PJg}BEg|LBEWOgvoRG+)Q;4<4IS^=KY1V7{!<7UNIgY!M}e>MAV^!*y=!ac z?(j2C&5{?%S0`=pY!-A{Hp`v?h0qn~<>FB}~m>8R9~4T6kbN)9v# z-E@i&4x7*l>dmwa9^h*q=w0R&u>L-&M~6N&mgQKP!$a^lvabZhrF*+0mN-A!z z7jcc`Cb5^$4)U^tq`N9!^|?o_>!9GZ^4pXRp?cd?mgJb{gl;l{WILoU%nljsQd4$J zd>G}eN^dSlzEd8ay1hJE2)HhXCP{m{WwCwR@iVw>fL<*$-ne^s;A&O5?rV|ox!3%0 z|L}nwk1?@puC~0eczt7I=7-1C$@#f>z6yv%O2)NYTjd1kk3h+Gbo8WSA_0h>-7twc zeLHhxwXc=}vO`I@KSc;=*<6%@%hS5rMezvV!h+D<)ARn7JX&PmI>$-O!JH)3ix(ev zC++S(eX0<{>$Zm&l6|d@$B$u)2&IQv4U z(JjddFHk+=)&8CD`qQRwzR$g&_t56tw)CA^VvlF;!{#=1HKu@sR*od*Ux@P>UpSAR zo}RmxS5#{1tpXo6cVKgKvv7pGoE*i=myz@J7XnH*>$I$_*ik*70siB(onxq&12ojv zZ~^J@U#bZJCrQUO>3^vG_z?*QRV-N02&}=H8u$Ci9f@clFhh92%E2Wjme}QBRD6vC z+>Aja?&&~-Xsm7;`sSDv-~p}(gyf74!Nj8z?$T#bnOjf!l6RWIKEf;r9BCekj|Sv( z(yR#Px)oTs*FJnfaU}WuAAF`1N_qNl{8RG2zyg^Cgz(8@$4=hnCuZ57)nicHs534$ zkB6RFf+dNHADoNG-U#A1@mg=nI}h1`@GB`FIIOhOK0<%({%awYah8C^_zh4G3rA9d z#3fztYEyV5n^eVO(o1>&{;!vk&j*q?Glc@KIb61!c`CFRLB#z57Lrx(Ua%ltR8RYG zrXrBPq9(|QhY^hvvTbQ5u`p^^eV8gq{~XA;R9J1wYCA`e$Z4nc!3X)#DHz=`GB&|& zz?W70rHZx$)pbyO58`L>AOKCt5n*kkqX7=95)^TLwV;0PibyNzWY%l=Tpht_)YbU8 zdw98#2$E-uwhWyiHiA!)SER$5TX%B-}J4VR-)4q2eTH6Brhg z8XrVp6?v0I+&sV!OiT#4SowAOf@+^dKPA~rIjFM|+8)p6+Jsmtl5p6^H|zW4EY5UjFueQ@3bS0BTh_UMd%AwL zrp{ec*B4)vj#^|%2pTnqXF-In^I5a;d)IF@EP^(Wo>b3W&bmH%Kr1iVjN{#^_3FD4}NpP7&` z1CFEwUd~X}0=0~BOAxx-I>XYYNtt__{gK@_Q>I_zpaSmyYcRKn(g@=tba%3k39OqX zh8>QEhQ2S}9?=1FqFi6N=eyfB7PTTBA&n_-78aK7?(Pp)$GD=R-tQ)h8f_-@K!N<$ zVdYX{!T1e`LZ3g6?5|URSk5J|eL+qPie@EY;ZMs-(q_2bA~*{b)O%9PG? zp{Ldj=Q9O-aAz9vQ1`3!dpnCMQrU(l8}ifD2S`?vMa;J!?=$GueQx06C-*+ohc3GP zo?m__Pbn6R4wT)gvhjmGb-h1_s!{EJG%6 zp_440OoGAT13(mCo;Pk80)arNxVWb7KP4l(-P|U_3#xIVcpUyq%ncXtag1-O?zjNK zTX)-U7eU9+#Z~89aMc2Pj19N`;9ss`-$03?tAR7lG0>9X5fJmb3jaVG=o=|95Rqap zK>~EUPixzv0iZZ6W~+<{O=<3~;q$jOPd~qGC+YQB3v7)DxcjfJw`zr2%G0z{Q;lB5 zA`CeC)wV|YyER^%G3xp$N{^=R(r9=;1)eh+_~q_#c!}-Cbh|?5H6W>&6m?h7fQKI= z;4%LG`-{gW!stv7gkDd86eTIs@Xm1MVbisxl}YZZJ&-(F@C&k@8(7DNU^6~2gWjXf5Yv&1bHr7wpQjL3g=4>j>MW80wpEerDes- zjZfl-Zppoij9wJOvu;+iL0S$;N{XGZ)Ge-mKwMRw#f? zh$>{9E0y^ST*5RF<}wy45j=U&&n-~?r*(|inx=I z$WsVc#OhUP+?=G4LS?LubZ`-1vhMO1cMyRNo5gb($78$pE-EB@_%%jS4fyWi`}IK2 zu^jV6UPb)drvN9XBjN46PG-r)d*5S{v{rb)$gYz4Z@H8i=e?=)Rup_HMU~rD{To=a z?3_rwxJbv6Fd$;`oIyj&{L&bCWv~mM2;zS99I2`4p#=aWz2ee7VCFe;6o z#$k^FC}Y~i`@dISm-)(yAX_eNY>3PB6Cwf6ns5O2HaD3i@3t_oCc?>9YVk)7dW@z2 zUQdtA=lV3m577i(p->IQX3&F?=g1_8ii=Z=1ckrgtUmR6hZBdGsr=!6F)zvEY%Z}o zQTSe7-fXTWSf|mma0<3RUVAbMRN5&tsqUZF){F&UZ$1@67IaO|;=~;`B??{5wnyA7 z7Mn;VB_-z?5{#XKjINFmOZ)pBTf?jOG^%ac0l5iOm~w$@b`kOIjWSTgLv2XPiGmC? zYv~Hp0G6qTSnPu?_eGt&Njx4xvnraD}AemjEYXjTL1RSu3W0n`aa?-C~{cj7w=Ks4C zss=rB&GHS$TW>x_t%_{1kg5s)ht2<>^w_bQa5pzs*7GoVuBf>9*8R{4i2Tt4TN40i zolY^bI7R_nGaukZKo2M}D@!|+l(!cO3&AFuiFVn2saa$H1SmgDT0i&9HF{O|?=Tmx z3=TDT7}V6)kN#5S{etC}@bTj>j-!$ij|V86IT8`J&Ib!I09mm3-LA0o+4*~!6un%Q`4p5*4KX4ArFWU6ULPspRLsINs9 zbY|mkGt_HeEGQ-^Xv&j1i*S#l{_?;}+*=I*L>2p-r}c`9_miEO4_=ei8~=lUa~ zEL+nBUM9Iz(LesNV6*$@f^9XDP{8~3j~`ZG!ATTPmV2IQd0s#*!t|TG11l=nMn*DKjckDB%V zzCEUtd=6xuFJ8WUo+2j!$}5R*l5*Op|E7L0ubg4&TTV${9g__!xq32mRV3=d#m#*J z@35(`VtY{DVUBIJc&ShFO;apYC_pZ)ZQ04neWwTCx)*?;p|JW=q`4a65Uq8)u<4U4 zwtwj;U?!7*?|?3x+AxP~9bO6BJ_s88aC6bNFl45QS#uXjyW&L9 znMOufd>18o5EkzoYa3cOjL_DHJ_Uj(t=`iJ=Q15E< zB2U+x*PEDR(CF@!6)W_#&+Tn1q}%51ahE2 zl7I*Ye7&!Lf4+AQs|aR0QxQX}O#SSs0#g%y24m#24Z`p)cMs2`* zE3~%a9ZqPM-*p8BlHvwD2|sdAD~`$OTAV14_;-*uL2APM#t!UGW)k$9GPF;EU#~H} zUMu3m=7}A}-&=e<47PrGdGdUHb-C4~y5RsoHLZ)ufTiOiCl1QZMOp2P5N3*V0+2PF zT+kql(}Jj#b;!B1NAimfli5y14MR&G2nGwe%4$a08IxNd%H13a~!rYHYHm zryqv|7e524GMp6A)+RC#Lm$kbb!r%wH%iMz`%%&;f&;n;7@C@{JOFsEw9b;wQ{|4{~xo3H6$avOkH!NFJl?d=6%ge2|yZ1)~W z$eq10^pDY!QYW91hGkzSqrAp8YFtB6RWR&zV;C_o;wDl9d>ns2osOm{d42Z^@-cb^nQu^kylKzWCyPs6lJ1l zzWJA`6&)|G(9_#TJG3!ko!A|x*TUoZi&GnJ@-ZjxUVu>;vqYI39m&WFscaTzPR_{> zz#=3yHI=$i&+ho*f>XcVbs$5nfbsVBHg;+01KK-x_sg_mI?X#7*9(~L*}-C5R#w5o zTi^mwSTDDZTNY4AvaptOIbIS32T#&*8>PP-JH;}XDZe6i^2mcFkZ7XV=(U81i&!F} zT1YpS*Y(1@5;!&7?C;ZBsQ0o%mX^e=7hv2h3Fv}?`a~S)r3qeoI&(jMYm}k`MOB3b zD_w&PKpT}-oY7aAw)DsF(hHbuD&LQ#rB0dT%fxvXMCk|b!Q=!kUS2^Z-}RpPjv)Lr zkF-BDo^L2Jv`lhefv>Id66E=snwmE&9qA<8j>!W_OiBf6vb)W#)6)rqx;zehTMo7h z4INSP97~`|(Y#uG&z3l6Xo*MWCYgWKPKyO_RV{d=8IhUkrV9K68&x z<<{8hAkNG8$rF6QG6zd#uk4J6P=(LZvsKW7JIy@lLw$ZAsIc|O9Cz5)BQNg)<(q(? zp9w`JqGtFg@3$b%e>qEYfM3gk zJ&w-=`=nMt^#(YPV3XPZC!h~{T19KWd*xDiDz~RfMQW=Yw*EW=vr$?W`-0GKJm4** z%s2%E-lQT@F5OIWUxJImg>+X@fVNefZ>_bPN4AorZBZ2o>+{}7&~5mo3ERlz|KT5TtCjXK#JQ|R{A7#s|?4?W0=?Ob{e4lC*-Muc^WWBw!Y5J(FWVJ z=j&4dXp?Z21WCuO1b^dcFCRMh`<_Pm7EnxJYPSWnoJ)**?nig*p=-s;rF>hNcf%j4 zuz39Tt=S#h0BEH`2QxFXjUfc5*^uwhbonBypDzIj5zy;;7q2fE&;-3urXG)YL`adgT^P)QDg5yPOKN{2o?5uURKJ zO@^?fhqp!wj|OEWh8gl`(5jtHCOYx*RDDlv;5qYW5>R5X%N#kR{;Mpi(aAu2fI#Xa zurO?T>s)>?=+t}`4@+bL-BRd6qg7Z0V0&%n>qZLH{bnk*zZMrWE9cG#>Ni!)R3Ehd z)^y_b6G+8loEgaw(*{qo34>g1?6O&oeU(e8J3bu+x&q30&V_hGH#l&3rUK2`R}$eK zS?3|Vf}l7BCIFJ6Vm@60z7w|8{R0AlR1 zd}TDrNV*CeP-mwba)V%%t@Lap>n(59f#FpdA~H2rXh?6KoNjYvMK~gL$bRiT4mg+Z zISVeCSk49}JM5E%S~WguEt-1dkUjHm%UE?d1+(Ll^#krYw$L0t%+#=-VvWAIv5Q;7 z%%wgza&oDBVbj(dBiVh)0zREY;uppLo=$FsXgWnIn?9uF*-WKX$=sng2C(QMlZYC z4fW(trcppF3>a>puQ2l&SX_imF_)W+3q&KXJaiV)J{g;ug7Xg#Pfpf5!;%7HlR%#U zNR0EfD(L_oH`pGz=wk@#*kM9dAMqYsvsL7a*nz%*WxjGY$b2N~fpwzX{|RneV$YVD5Q}_4 z&vDsR12VZrPZx{HqC)m2i&3>Z&g0!XRRE)bVv@~VVKE0YMJ@;=29R?cYhmf^A{`we zPB-T^L~Q2oK74q=#KiRA!2=B=fPxHRm%|_rf>v2C*d-_HakJ7Yeik#;o3QB4)x%qy z&i}5}^qk0%>5F9~0H#I;257)qWEx z)04`X6jI*XprD{cdiCd;69ecS#q>3shW* zupFXKCW{xFd*#wq{u|mJ{5Q0%uBkZ~DLpTAT z2LSWH+ynL7qfc#&=B_w?4CZn&4b7p1b}Ftse?1MS)nG#B^6F}&T-^=0BZ1>9A8B*= z1&e3i`)NB*vo)8E4TS!{|L%Cdnnv<#@tnjQDsJXq3buho_db#Jp|x%v^Al7(93rKg zG;}Bz7W0MWTwg;=T>$ob$$BMYI7rt(66pjK4S<(mvnEj5nmhatEn5D~8G;sP5G29$ ze)0}29fZ^fVr9z9_dhAr5E_5@%m!tWQ&PxjAuBV?Ll^3Ig~BZpiJ|@S%1Y~3{2<4* zZU+v#y7z@z?1ji^9|3`0W_IBVU67@EorlSc?h#(^28~SgEPh^&M<=Xg$!GW5=XrMd zH`ORxT&+RLjs5~3L6ZT_)vt}NNLVp~(=#_66KUV=fBg9q3&^@6y+#e=Y4{BmpDXdl z|DepT%17470qK~^U(dTIKhBke4p(+2Da@}5I=khiWZKe^*%(~ z7YuT3KhQs7myTt4T@Jfb=)e|zz3?Eg=UtH7v-<$?EWxhIv$E#iF%3aDoOV=j4w~w} z=g5lOp?E;(TaM*neyp!2hTM7v16o0Iw?H34re36zS8`!~BrW)MC9Us{wgrK#+}fJ2 ziHyDQXp{GIspCqq^J6kOFt>R50Y)_;FDpXbcd!V^^goqX53H{A81*+}^mczkW>2nS z{)N&RHy2RBO^6u)3W$H8(A|<)K$0f3lU)Z8s?=*z0C)z{(`u(@U03)bgC0lqL=cOS zkMcRJ1SgDX9wc>VoPkfCW%knQrZ(38TI&*v6LvGYet!*3?mTEtme*qO(R~c&YUs;h zSmXjy=H}91LP*&>AsD+0``I(elZ`@2sk#nuQpE4lf7T`ugDDA<`MUR@xEIb8mrZzM zz~XkWz*v4QT~Kqv`aeV4`mX9zA$PY0pV2c8u%@i`OXZ-?liK8yMnxkLFl_$dr_T)& z=y=4PY_3`iXD|Y;e|@5G773_t?#4GkL#1qDGFP6t>1qQB*l4(}Lz){|)!{U=u+Q~w zW%URr0hYo0<|=5VcP%>=Slq0gu>HDX3#oFI$Q^32=9p21 z@Tcj6>~1;!OA^$g-zj^be=+S{5rA}6p~Iz+B`P<%VTxtGVcxbNJlz|g&yfLSSYNDf zH-4I($sZ?VFdHgPWkMgV@=usQCHJ2&e@whXkIi3n`VkTs2wDY5FbsoSAR1`!(sm8vgltPgGPiV{{u(_?tkrz+nOk0$|pY#GvtX z5O!rClgNf!sHSbOKU-ii`qZ>PUKX^)W0?`n&{bFHoY-O$7BuT@do%=?Le2WXc)7ds zlZXsXuv1TTk5FP2_UY2SU7d+8%5BSTf-d$122+QMht0FV?Ey|5tEGBzcSHrw5XFSf z)|qEUxQ{ewYax}EI-nB^tfe~wCnkV<0p1M!PTexspm}*;zr}$hJeZRGB8N8n$@nWQU|LJ79PD| zyx~|B_3M_8gD_3<0e8GNL>Qn)r{cZL68$d{-e+wU85y~+@b)+w*rD|pw+;=(5p&w& zX#t{|R#YM_8>~xUSQVHpy~ymK{jd>oUS)?BmA~cZgqpMu7RuE|5;CwPK?Af45NZ`F z|Jp14FARNUefUdkSjx1LJAx8baAIM|?*02Wj{^LqAV6tq@ID zcoEfeN6X_g!|s2STJJ4c7CF2aN;7>9gjj(2(s_TG&7zDa^;6`~{?`r1{o~yH?i3sX zSsp9c|71=}IyxXC0;?;6oSJUuX;D!TR)-4@DHv)2bKKovHgN)=dLR=^U@>_P2C_lD zx-pP6{8PxMK4S47iVK_I9WXn*KJNGbBk_43_K8(erG)Ub8ToZ_(D`Vb_I;caKEj)8w5+>st( zk~H%k^oyq}EUmALf%FH|qHjMmpSV`SuQrnu3g)LvSN+`G^J*Qbeu#xERPtVcjq=v0 zlOdKt^Bs_}OG-%@uJ@`^{etA(PGd7mIc%D3^m79uG&w$={Jl&=@7QPi?{s$G<271AvI_8ETGcqD|tObanKOq18(p$;c@^V<+@#UHyG9bMSF zg8f(W|NpllIrrO}_s~&MMKm7`r4T+MFh*(dS;Qa18DOMWmK^;papFKfsUI5?8)Fl3 z9Asoob-7K=+^4&D?;ewlAS>%=_WmN% z?ad1>(#rAX+%XtS&|)nbU%5mQ75O@^!5+FEMhw|jnZ zQB4pgE^Tg}ch;Z4%Av2XuMK`Ll#HV0_}CLgqkmKQV}LP7?1@7)E0Js{5qmQ5@;F(I z5Uk-LtzT&L`VsAX{RL6y=9f2jtWj?IFjtP$?DCN2ZGz1DL=mgIyE$+;AzN*_;VJ*n zqFyA|MFnpDx3o0!MW2ht0%c%crPw(K`Wa8%>Vf@b9Qc_Wx6rv8#kVb0fc=oTuHy;R z=5XC(1r5AdU-+%8<&KGZ?&@<;8lG+)JqF!~A~`W&PDU6t2#!;TPLB{sT;IXbVWOu` z0&NNPTwr*WLDF6E`fuZ_&&u!b{I2gzu$FN~YX3F~6Xpt>{ap(<+8hZLPC8Xoz^(;= zBWC~uhd;A%ivnF;>5wK7#M{kaxI3@`yqI$p?-f311)-C%d~6Gfc(WRyX+7NluL}>9 z%Fa+NcwD5@w35LfW-*3tCAoNBT+f5?*E=eHnagk*{;i1}8O3bRwm|?6A_qsP?^Xd9 z2TX2mPbR&FwNL0`7OJPklB<@wIw{~dMDT8}MQj08FVuG3PQtCV#_^0X(dqK=hhy)1 zuF(n_#LrJ|PgBVRyx){9UBI9tuZxVS?j06~N7WQhD>F=AQ4>|h3hYy_z?W#aJ{;#-9k3G?L1bfos{{u3(V?$xcr*V)UOsjw!2ud zTyKNT<+c47dxO)Y6_#@z{;)X$(loFkk~Uhj3^7;P7s$E&6UJwRsa7zZeqGN-E*>(& z@4k6cBm?_}+f?_|Q>;lvtsZ!kgKJ>h%E*Hcj2LnTh{|EPs{NNA@b1vu*B#RcPP>I@ zR`vUwO72Ja4H54y}Vl*CM}Et`MH3PJUGBgMn>g%dUf^i zWSyUn@6WcgDF0kaUQj(afd26Kc%>sQb#pZ#4IHno6JPuKnHVjLb)}}AUUX8NY@fi; z(2d|m#AQr%GC1k}ZV~WTZ|R6ibUmG}Vs~($d7CsKT4f`m*pKs$ANvY3$qMzQ(BN_YnVv%l*plE3o_r0gwHxUyjboD&td<<>OAu5KS9Hc~u zq^z+5PNXRpX9}!p!l9lMubwgeEK_&s<1o+2(Uo3Hx4#YJFlxj$SKd3L-`@I$67i#l ziHmr{Bwb*WXr z9(hd<1Z}*@*_@Oie3`g8Z?TD(U zf5RFOM!pM(QsrZuzwS$QCnFRb1p4<4hfDsyg1}RgS0MO~)85@m;ji_dL;wBs(_~>D ze7Ad*TC&#GY|?Q|IhNyeMRK|QV|j`xCVfpb92_&_`G37=E@sYDO9g;5{){@sd?kYI zZ9;E|JE3PI*~hP6O;0^3Q3EK3n&2C<+QGrx8^4_G=mFN=7|=es-YsUaopX|je=}_1 z!a4l;*yo+T%InX0&SUcMPzLR$(H-XCqt&#{X8!;%I^0gR|JSLnbNe8sEU&M3EKwi> zb5N8J{0^l?Pj;`a6}wz{L=V;?vyAQ}g4k~o`k4kq?!Lr|?K)mci0MG$_hfJXOusNuN=nK)u1sEK4GpouGo$WA^6OS*;3ihp(%geEPFh# z^8X|D`1{|>%F_8TuyY2csivx*U||_e9a21a^vH3eKkc`6{k^E3wbtKuf=`|VJ45G! z7kzJ1fGWS=v`e5taGO+!b754LhEOb=txXCv{WL^I-xLy7z>n!GKZI+?p zC{O0g{?2v6$V9PH{#{nkt>@UAKB*Tm`Fr`C)OO5gIP;LoCb64d$rr>QwJ_YNxSov< zkVQlYcC3g=UvBJ*0k20Va0H$CY*82*YZE`CPR-*&wZ1PHR$Ge*hoC-;W82>3Ie3=# z$(tF5{vQ5!@{9B_&{UU0B{rBX&yZ%{_@-TS)h$gLNxwg`SH4E+OF>vk z`?BL-i@LRT&TtKkOO*M{G8-G?crS(iTj+8KX!!^{rR==+G#Egc?kHJQUH;=$(4Ech zd|g=2kQ*#qE=IMfVbx2}PHoHBPu9-Kd(7MxdhaofJ9sL; za0Y>2wDedh()%jYli*t{wau{P$-v*4wxFh?B5!Mp;N@(Uc)>UjuYR?jO`KW7X{-D(mGJ>wr6y&wbUM*x5*!^Wl@Czzd78Q z&TnCyj#aE(%%}ka{bsc{SG`YdB7F8HQ|RdQad0^P4{2{1Rn^<>jbaA^DkVs$h%|_F zD+nklf*_4_ch^#p5ClY|OF&Axnk}+jiU`FE= zbWjV`TO8vlnzpu|cnp;y2ujDaYvqV;XCaf;yDO#`+xRpV5B-8eLPWP_+Xl<*_+TbR z_e%ZG^GldMt-feg!Pd9XMCn`4!9$$}DWw#7{bX6p=WpL0_d|x%qQak*zbawQW_`Cg z0Mr5FE_~&@{*N3MUeb5)Z&jrC{M}ehntuILBGu68PEX=0S^8-Xl!kCn{P|OpyZ5Px z|NJC;IpyM9Rr3hVl(U zhg7}^zoE2Qj*h&wncqyO^XI&fyJ_B43MZ`g`sXvILwO$ce%fgol`8Q8B34!t=w5of z*o7S&8?d|Ej=Ad2YZvwK+WE+1eG(i)?vK64nLMY| zn?8#B)(9eb_4D%#kJ8C_D{-}x#&}C=*U>?)~AH=*uGA}XwVNWEI zh1B0`g39aWty_;JnlJQcXvo^z!!=Kg&t}~Aievs7DAu@fIFQ-IHACb}qv5MRQAAuC z?(P-&hADh~UqUYwtXRX}@{?6DPeV=O0K4+A)YGbVwFA{zrW_9kKmsaf7Gw zo~CwvtdY!^?BAm@Ms<`)BBiIRKv^dgQ{YQN*?Lca$%8^}j4Z%!L{-t8p3aCScbGA{ z)bj0Cm`)m5@6UrUO7V!jcBRymYIxn`8i$=@*1ym`AtodwWdu5Q}2cf zejGhhUiLj*E_Lc{Ok&KR#HdD!PlUz9s?QB1B1|3`i(vg(J%65}ib{i~ z>=)*e+n>b}?+OW_CQ3@&(g--FDAb!BMjqs_xL?4+Kgwp2Bj+@Q2O1!fGBB}3UW4uC z{!MB6eCV?4IS{pNot7YIAI*jB<3B6C?iJMW^Z@MrD#`?~50C}e)gJ8b|M}`kFVi-* zJKy9H*b*^Z=u1gJnDGnPdKR%o0wbK5%ioPJs;zl)CHoQYBK9Z!bGPwY^;NfBu`)Or zYRlUm{3@pRO}Or+Pi3;_X2?=bHEU8)P*pK(!89K0{{4IX)>O7d*iqFVf8^1HuShiE zBd1N>88E$+0IAKFn3znkSx^PH-B(71Duo)^O3Imvcrh|_kY7{qHcTREGk3_eiZhDo zAzw${;&9nb848wcx6=6c3kq4mK|vGUyXVfHRXgS2B}{}~4-ao%Ki95sDlK|qQg&TeT5+OCuWIKKz)wh6Y57zb)<`%FZaxBuj44*yY zZh;a)Eo9ZKUOTt5!pTGfaq_Cc-zDG*{(~}J>OX(c>XJ3Q@?l}b9^TiqT0+SgH3%ek z7Wz=vHp<}AlxqfX)jyX+piNT25rp+|5M_9C=n?Yol7|FLt}g#3`atlsJQJG zuQ|J!lNi{|UBSh@$MH(c_0Mg4I%Mf*B!@;j)rfxFj|U_JlW;5vf2O^x>Cwt1MJm1=B(D>?qoQe!PerTpy}S0BQ!pB~MBdb+C=`$XjAwLhQ8suhn3 zWU5;IhRsK2Er8W}+7g)Zsj9`S2L}gPG7u327clw;s;bk8F#>@&>LB%>EyF_sdU@LB z8Y=&CYk>*DTxWc3ckbWwOYgKf?doEMhRk0vT@*M*(4*xx$`TAGze*>nYmE8oIhyqO zPhs-#&o}~B!=ERx_hU9j3r2kq$ME}(aMKut+ z(a26Q`vWr+)Q=MILi`!0z$G})LPty89IBG3zcNfXZz~8^MLm5Bf3D7l@9W4XHJ?14 z+}9<0aW;dELI1k{-e;%(sW9qwib6-?ghcriX-w-#?I;B2- z9OM=j5mEcPl8KF-{_9|?>0fun=g|aIxA0EjJKRGjZ@}9CRyj9d@&mi~Vt@K+13nWT zZv$CXkP@}H$o_ZNpv5-_Q5OnD>@QZkenaHSwQF~vTQX71rs3ne^j4sJ?E8<$pri&=kD#)dR(6{fL?F zPINpSemXAVadAg2d?OQNFgIW>k8``f6N#Dn*TTM>a=L4PPtsA5Cdekw-+{x3l;g*_ z3&mD$1#48MqdA^+b@w?<2U`#AZZB*ZvMA6OKSRBt633;1h?_FI+xf5_|Mj>($d}fq zZ2c|#SA9zDC0fVX`pT6N>(x;w$nR5&u>Y8ptnq-6v9-_W&x5@z_h3}}$zIU> zE$BRMaX8SupHczH0lg+g<)R@Qc{E5vFl?gBwOb&SqXH({3KK9PEb<460I;j(|` zad(?PKzWB{zcSjE6;p0;s|BQ_O?X0~He&6BWMooXv(init+xNMlER7XQsmS7fUj^i zrrn!_O}~tn{Lg+2p9C+}XcNfh_r^zt{{0n`|Kp`{IR53CVfA;HO3#(Rt@bxRibZ9_5>XECEO}AU0t7 zyX5wx;7UL|dy8KeOSNcOYa`+H52bAVwbxkNP6fnD%qbgP-udueOg5!l3<`>asm-Vh}-1P4y)%c3;zt38j`zK6~5B@zp{BIrn z=eRgoYwN2_Dq`zPSpv{bb(Pv&0yhPs(ik!0pxNQJk62q$^v8@}I-e`|q0q7iRmPwc^@8wW7;$a|nAC+W0P(Z^A=x zb8sKoDwnkW+s+D)hO1_jK9g+u37%W%{6FO%rfdbSP9TAzMnC_??b}MRbpM1u_)fqa z`0v&)tv31`DAPe^s`xI)9|TrcR@&g8xp|}O(+44Z<^7dCn2Y{hw^meeMgMzOXZDl(gC{yhmc@BR07%;7VbU^K2z$o_djXt_Wd^V^fSp^VpmLZLPArjCxQNl;E ziMnITxo`e5QgRq^Fhl&&$FM!;P2$x`ij2+Kb0}}atTdl9_-oV0&0fcRQx)k%!(TT7 z(LM>Z(9DtWr9ALD&bVzpQqXX2duf~V3?aQ04_nZ|?YIZjE4n-eYWDi=$5>Hq4f`7` z<-8#`=~j=)`0TGi4&BY6p&@v=WTJhh5SXayYLC?oF0I3D2dC*Ejd|N~tm#?O{f)+% zii6qj)<(#R09m7s>^y#<)As{X5YE6bUsmv-bzvv0p|VGC#(LXbhWVg*!R}J8$(!Z5kAKC^em?gz- z_pxUY9to+pST~<{XMNw8i~Beev&bZG#6EhoYH*D1$G(p#BkcI8^(kT@OQt2u8K3Ef zdWG_q(LvS?9!i7B!b`zj&J3;v#l|0}0_X!z_$vC|w#`QF^4XipV$hh$>PzL2T5z>q zV{hS^7gJ!7Qq_Gm^>@s}q+Dq;{i237^ApP6dpj=C8V+(_UIuPXR(Lr&&+jh}*>H`? zWN!YB&_F%ho0EGQH}n=7Ow-yB>(+whOikpO5sMpGW&h#=NXz{6@tFmYivJz}TG9FR zu~c(&zzPA5Cp(NCZcuqam{u|HNIYA@)G>Un2QveXr~fb~Y2j@| zLw_&>`VSQ&Ob3_0FV~H=t|W^(xw!Ovd*cS81#Lv8Xv<~XWud3`_P78jk?|5p`w_LN zhwC*Not*7-Tq?2s&4snuldY9s*1@$N|B8d%aBgOiy;;g>S+kYpV!$eU$@dehsaU{I zm38&hF`2KV3xyEjUs!c!qN)AXuCdBkAd?@wZ698g$sSO4tvI{By*}}@TLvr7T0VDZ zbnayC8W4rw0EdU`Q(uO%2goYm5DK7cIxxz84S9J7fY05d8sQcQyGrPKYG&?n@gxe2 zM^gG!H5!qOfu~C`p_xwvI1*s_Y<0LzOL%q4b@@6d;*1sA_H=4EOXS{XLz zwqx?r?g*~PGoy2(d^6Xm?^7Z&R7Pw_ z)6kR{7QuxwoV~;o9YMo_tKvnsUkg&5lXtH!eip2#R73OFGezdjVsbN9tK;TPBf0>D zM^|bkab@EnzKV#*;RWp5vwBuqB3U9UVTF^svQzQv^V|p(+rBfFjwZ|u2Kw5ZyrG&s z?c>)X34De{?#f!s2+7e=8zWPvdJ@vP_G||x@W0*5@Vb<)uh;GNfu&SgQP7E(FtJ(; zrr4{M#Y|LU$0(ew5iS0h#!{>mS^rx7cBHYe6R(K?R=G#qz1JHJ;v2tTJgfeiA>JKk*={|x z@Er4fYujMngR(0}MZA@Ws!3^bAJB@MEac4Gg0xQyNyO)pMLJUjcwC(YB<(9dHbt+! zIMH!-7p-hw!3y8)b%#(1zanIFe9saYy=Ju2)+{6=WuFqRe*NMEsu7!U?=6`)W=9OY zZ;efdKrQ%atZ(<`y{UchK-S$GR8&Na;VrQO$Xu=J0nbIS|G7iXS2VYf;)nUR-6?{% z39&edea^sN7SYx97Ty5OB0ezSf#PuqZ}q}AhqXo?hgH|ygPmm(QAmRXYn}w)7)nL$ z3dJJO$^ey-Jk)v!DDp0^V#aGR!2A%~)wgy_ zoa^m;YufCjEUnAe|1L@TYr?@ejKX~D2oIm6nzw`Dtr&fG>BlDxy0N^~ zlo&UX+8IBrRDOO#^m4jFDN+m5u{QWKXlPA`gM{3duLb(D8fq*5CfOdXJq8U2RS;o$h7rMTDKsxie`hv`&2KIO7$ zc;pk{5Le>UAQG?0CW{5E$|ugwhtimEW==BdzQ`7f>RRrbk0L*G&T$hSGOis8Vi8Cg zk;W7TtQ=g~-ZF0WSU|Mhv3>|luDQ#*)MvMI8vbNv_^HrKVe$?wfage5?w`E+if zg8}=Vqtg?c{$oLMxA8tY5A{gAM9~$BiY+kw1WHPtV}l+n5b&9mgi>Lh1?rGY8q z06f`F1<)<>fjH#6;ocOB5()|y!$m7kAq8)$LEzHQT%E== z=)|%7h!J2oV{+!Ss-=;saDUII(E918$)VX#cfVWKE5C$W2CZZ$VoNFe$K$PaR9 z3d@)!?3D2E#EQ-4C3X{gJowy+5G9T8&x6H0n+bQ0B3P#o_aDU0R2`Ld!;lB;n}Iw* zZqR;w<)bvE|)5~ZVX!6iEu zPbgYcOw3C_V|XjQWHW#amEevTD6^#_^r(IeEu#4?~bpVLjzT;4Y!605LtZ*ML-XN z^ZYry!FN8K8~tG;mm*V5IV6OUqTMc&NP;Z2mzgTBPN^Mz#-f>u<#%fHY!(|mzc&)L zMznHU5kQIJ-J-8v^dc$tQd^nh)I^%);R`{fcFmLZ;R#mCBG+I~!CK!TL!G7Kp{KSx z_A0A;IH^CY?3^ceXWrxsHyh#8>}j4TmS&8wWZGA{4epxZ9+Yx!ZgUo~G(X5wIVjK1I)6)(=LOHyZIuZTh#U3vjLt8+~BJ*Hv z#=5_6M_f74)q3P+QH$~I=8$V4|`rN*&S=;m~X}U>DNmUE?gIJoPD>q z-{TqrMFvU;f52&eSBt=yL-yow2fI+dcP+(g$_qMV?6>fl+B&kGxl?@>14nxpVvq@( z3eJKP4aO^2oQ^jd$;KNexu^Dt?Oc6ZFx^QCq#VXi_g3*v=P^}o2+Lj5FFm{2+<(^^ z2FQ=fy2o+O&iylL^~Am>4tI3#<8V|<{pgG!UHl>-wA9fj*~Am()22Pqn`d#w0##3u zey~1OhY+I7ze{YB6&oyBl^_-oiMuxd2s z3&pbLUYSOABlu*-xvO1yJXTiN)MRD4e_UkMMyV^GRrOS~*e$GWW=nrqaIzLZ`;xVm z2(yV$TS1`4c`uWBF4oWDjp{KX(H55DU%!9;L8@&(>X`Y&sX&v|_bfWz_)#{AQB#?A zy->k1Z+5Y@Yu3rJ{lR8PR6Cxy00{BNuG zWDPV&trd?s+XS6&OsjgnQy)*%Y)p4RHL-;(3%uOTqR2*QMA0i{)%B)XY|Tbx4{08` z@JgDRGQxvD6=X;AIZ(jcnka@N$!>kR3>-2|_;9!uAK$vhg}gZWDRFKNIx9$e-^S5_ zy=S^liRRRm6wcsOO(YseOx7HIbZ{^yeSCyE;c;BQgG0*xO})J66sUb%{p|7U*SCmS zH1aIsA|oYUzkdC3H=nkh8_mTh7%+{gC*FE4#ovN2%ij(s@|&6Fz6uQblW-!jUj4?D z%N7bHbm8K~FOiYJJsMC6L-_s(6J)k82k2JlJb%NF3dfsF#ey^REGv(UGigXI$Y=S_ zY1}1jDtF~TRWPJEdL9>Id|%p*Ua2}523q54|IYhsjYku)gtY(hyC%<+i z!>_gg3!^|;Vd1ktwFBC~Ff#KvDkY&oi!H1y{Tp}A*RCKl^ExkofOM?pw|QMFMI7Th z@V0%Ue}0LvzyfcM*tk7JE9C}*MVh;kq>RiPID;}YDtF5UVfX-_cb#O&3wC{Y*;NXB zzG>ILNI|%`=5J-E9!u2p^x&uWIggDs`!dQmZ^>sW<{NemfT;XIx?<)Tt-c?Wk_wH` zTD`7vy^^o*_7n_=09z$H+C4c#;OZkQU8y!7wcgqJ0#b3!700AQb~cbV#5ao}LwPVK z2B zYoC3?w|T;GE({zU7qocl(>q@9)-Kr|K#!P;HpbK$vfum{8gGdUqbbniV^2=W5UKn+ zDHgee*Hb3V0Icnejjpc6SRyjP`*_X=Bb9-NFG{m6y|8?@xBlXxdj2h^J!^q6@xuwq zA7XOf^yQgCj9mzRP_4X=SRbZ1@0Gr>u5dYZexGa4MRDF}pwXL`NkER^a{A1Z%Klht zW7JoR+HiwJsBh>w91l|eBwZX8BixwruI<*>%0F33vP2=>4&3~0_=H6Z*M4o#Nx%?_X^|e0}A(%=Zh68v^hG56K~QJET*1ws!89l8;LH~ zmD9w3su#kd;p-*w<0@C`LIW=+$WnCZ(2reoZtVD`0WKl z1!0H>DZ!6g!}eN3&KLRGk_z-V<0_ioBN)(9qP9-wSzP~MHcE$#*EU=(F?Mz# zr7_QLX;*jyLfBcIw=LoG)^~UH_t#Ti7cJ9xFL~z)p+)JKF=x&l_T;>&K6)JqE34@tCbyrFU6`<(;8H|ksu7u*AHC$-7XL-6CV!OMO*rSj4D`UFmU0*mE zrtPiWxNlZE9K>I4#IbYnn#jt6=Wt6@*@@Se68F+tD=bNOictP@@BAaIjyzt1nH+ax={b>9IBAX2O&$p78_P@VW zazXKBBJEXHdQ*5HyhJOXr4@s%LTo<3% z<8Tf|{2t7499?r_bmxnq(ZIW3;v2~@$7HJZB5&7B@%63-Z|yjCO}K&164%i2rbs~T z#w0-@!bIO-bN>U@gUqZCZTeH|p*4-!kiKTSO-}h%EdAS-604oOUe^7ngY~p@k(0lx zuicx{y%lWhltGUe-{r?26;VyX;MBP1j+T$d=lp%CHuvUOz^>n@N9*2N{kzXWWUT7b zM-_?e0|XSIC<}=(Cx2+ZH;oEpxGV_`HHW(^ zwW=1CUZD5|Lq(5EdtbiV1_-*L^-%g4vdB1ZESXnBTx-XLh3LWGq6)2j^(P3%yUKTGF122PL42sYz9}8 zf4GgOoU2WU1u-CJf@zRVt79X%DrOm)7&b}-|A>Y=h%2_2NltzLz%NCkl0v`UH)MAT zTMu#U_ficFlQ^(>|0WSaPkYrA41<*O1q&M`|7i@#2uVq6AW$*e#mCS{Id{Wz;?9Ww z$JZ8CpP1%Gl~?1+39`Rke~Gs?^^0b0SVw%${aGEhMW@b!VvaITu^Uw`%5~O~?5BRA z^?Yry(H<(0b&kYGlRZ#gh?!9?0Nf?X_=sF2k7(+I=ny9ix|`EY&Cq^)1@jqdFrP_- zBNS`iVjlYd>ZE*Z&Do}$)I?$h0~U<=||oTYv~4qU({r~E<`b27 z_dLqemlmLyQ*dt*`@Q;rV~H^K-S6zJd2HR+nf+3J^I<4XkO1 z61xlmgDZ^Xv1yoHS~9_gpE-=W>n=WY+ME_{;T+SS7A{Fq9J9UVvew{`6}dO57eJ8v zxt#y2o%?DPX5er-xC1Pdt;}tJ@h!?*LN_pjo$L3q|Df~;jp_Nrn!j3{1PXizLcsOLRYw%_DydXDn?*2g z6J7vSes<+9(bil?F3O5Y5Pcs+^5se8npXxul+2)%VmVT7OBZ4g8ziSXL3YY0AKm%# zrF{c^L|MX%GjUcj4E?J+iMqTN_`yC=!R?|X3{8^_c`v$CX8wW*R_UX&iUog1`9332 zw#=spG?9k05m~Lb&;pF?)b)E)Z&a+;yaw>N&a4)&l%0R*0+@_{(*>5ZPsf@0N_Y?z z(^8PuqaHMCM)ysFinr~UR5YoE?O}L`I@QA&-E!W{k?z(AO0E2%TS90c(p!rJ%yQW; z--={60+6^PL|$Jn<^SYXSsZ`*lFUKzO*5tVO6rS`EfF7EQTyz`=xJd`{(m0guWrxX z;`S@oq+At~j%3%~nDhh`75ETFo!{s{(~R4Q*(K?|I!5NC(?fR-M~_v+=MoJRD(by+;}I02W{j!64~1zj=_Eyx62>(G&kOVyikiuq1G_Qd-3 z4(TtFE)tH|(&itU^k+N)t;JXw8qAb)oVNNOr6UV`o7c?erU>7rc;nH0xf)9v3-J?e zv$0g?i|-Zu@VdBC(Olpku6d1Y%xP*QKh*9B1|%J=5&7o(nR(@wdrKm6=H5%$>3X`l z4_$W2A}FY#)z_Y$RAEvTvs~;Y**;9~AOb<~xjWqZ4R~z$N#AMN$qv~IU4-6Aq*FW1 z5hV!4`qsZ*zn(@RSv*N{-KSy1TO)lBE62=M$eJbEA=Oz(NGMqr5r#{?S`XDFbh<`8 z=*A!_R;=am%9wB8?!Xv?+tHE*tI|cNz0~Hyj?nsJ;)Vcn5zq$zYH69;>X_Ster06` zxzX^{>l#Vjh=pu*tw_s=m6y|UDldz%1f!zWckc(RA;|?G-%@hudThH%*4Q=m< zi^p?+T5C+ss`}}^=Etd&G5Kc|3hS*`E_kMLPR)7>RKNZyjjYTRS0zxK94WTMLoT`>RS-0$^% zHO2PUI#XLlWO7U;2|L~OLT};N@n%HA)xbN%1MV9ARobp<_9JV;HFs%PLgP2%~5|nA*z(@E#4b9d@W4>~! zwOwvw$54NkdIw~_!KjOdZEKM>CV>{zX#|ymVNX1Cr9WRc=d!i}E)m%v;#Z&1=&QwT z@@F#vczJq`n*Bok7dqbFA;od&r$BXLp5UU_guXYD>zS?XW~_{jPki>D;^|WpFI;k~T1TrLy#Hl3 zsjj|u{YF1t|guh!E@ zJ4eBv?x{Qyv0dsv^(|dTVPtGl&EC=xmRa)Jn#Lo78EoxKTswXdcR3wyHNU^Cq0t%5 zfs_@G3o{RNFcSPkROqtazNa3s|BAuY?(Vj)YtzKxT*mE&Y0(P@6=*w?2dx$=2?3%z z4^+h3F29*+mC{(Xjb+g+74Fyb6By6cybw7y*M_;MuKTis09x{S*6xmpL&>F;aXQ9` zNST)zAGyk?x5SEdk1n~qO~q^_TD##TZ%a?aG`>vje<9gx*to@4*7MT@@4?o=R+vTF z$z*`+PJ>F-sT=#=bl2_;@-;!L8~)zHnfkFa+;P?2tz;}4dE-V~gb)IEtzwHAj9~%% zXtZAFruGhuV^c5t>RYY#4Orx!8cOMX3fUU|ksRqQD=5#OZM?(~$)h%nIcBRa{5Yy- zyyNt~M0D$+elj2dA65C|=+(>kGSQM@FCoF>##p(~2jJpCqYeoY&vC<`Rc}p`PXUG# zKrTM`G?QsCqp=&|0I5=#rn~?5?>4RB8$msrESX^F^w6MPt-GfOVq{&>2mBQ7CEfxQ z|N8{+#bObm=tOSg;pyHCoS0Lq;g(P{&mu5^+N!~3fudR$B!kcSkCsbr3dHPZMw9nG zk5QjTO-IMzhIN3f$M_k(O7BPE^o>c~O$}xP>fx#XZID@a`Z%l35Dpg0$c|R=n<_rz zF`Jx5r=xuB9b3JbP`dFQIoh1VgNJ6EH`FUzn2?hNODnZS7B;^+3WHLLbKT@PgDP!j z`T?-O9$y(CNQmt~r!oq)_ zXkgqo`<2MYPiPt-Zx0U-4>Z&ZmU9UO&4-7U<(dd@Jj9ncca`dFn(2jq%`h(vYPN8dG+w$UdIs}?BVyoFfv$S)B;^n0VlwlYeKnWEQy}N<+qY2` zYA~ zGK>{bmu<_PqACZ1jaL00JJh1STEX>O?pt_M{~jzhub%U#GPnJYIbz|TIb!G%7qiE1 z{B|SF-MjAqhle2B=dxgG@l%Lk2hHcg`SIsZEWqopjVZeEOPn`# z$78KTeOhA<3PC0BUW^)KpUMVQ^WDEj;Q6iL#(v)qrBv6|dfj8=J`rNYD}^b<g0GMk{4&r~@DD zql{9X5jj^nO4;`ZqAP(Cj1CPzZzUo}pi*S|1Rw?9)HWwzyn%zdL7mSVDCWV|twtwM zK*cZ$R3oh5OZ7lHvW6kMq9Ij|%5bPV_h1w&+rT14Y>~zj%`=>fHUEdrjy-G1;QL-I zv);5RztMqQ{a3A71O9LPfcL_g>xv4v#xGKF6M66whaR{2GRdW}JDRwWn^WGQrS&1_ zYbLzK!rg*K2bO^zx9^lN+9$YR%-Q!ctw=nc zSL%TClN09;uC9ca9$|q$o^DKn{u*Cn#N($W2Sx1FGlbdpUc10Ud{HumDsabgaIu9!AfNRy?Bx${U3>bJT^v${ zfI0Vc1G%%Xbs2JSsTk7N^KJS0*)udw@Y5AfXibUG4JEh!E7&#FS)M z2jXYqdlqFx?7i813?V&&diD-Yn7j0V#nmcuiDM_8E=pOuRI~KHW@r~rQ^W}_a6$xc zhH@Oy@PEvGKE*Ps?&8Cc#sh*Sj`zbNcz4V+oMx!O9yh$Bcvj?HUCKl zY4{WY{`oi5%LUb#MNSl>LMF!a@uyV@Mdf@1^MeYHvI3%)eGW)jGFh^BRsD)nDCvwg zj`x>FJQs6@G8o6v^ofa8KMgr^0OBhW?Z6nwbA^E0+s0I5Q(GG~3rqG3#??K3B4kWE zfK4~mMB75Ph&Y)`4ksr6x4~qrwRV0@-d;D!uEzBS{@bd^rr9t`Yq9|1xeh}rJ|Y7) ztLN3;MW}&!W%QFLxc_7PNObP@9BTiG2Uj<*gg9#B3=nE~5rpzwuVy9kBJkqM%2NL> z&-#P)+Q0}t2TlML=r-m>t=5o(gRf7$B2+D;8}#$L(&WX#>00Bdzh`OfPJHUt9@OBg z!-Y{{nz6r5DH(u#Lo_-e&_bvhJB$lAXQP7mm-A&|g@QXkSa#K6{{;7AL&M7ez*kL( zkV!va1Faf}j_$zP#QJ}idtf@oV;V(Yogl~%ZP%_nT#mZ4nm`->%}3IlX|*(=^tg99 z%1AHLC`noK$T(8fSXtZ9F!bnU=uuXPk>XkU)r5qS`?OIR=h(K6YS$dT4qm*V-`laZ zu^w1DH)?|#ZTTI|GdBu$=eoMIJIhM(ip5eCDL9M_RoUEdNV@Ix$&V&-8$rmDmB z8@b;*dAqt0*Kcg#Xu1`qzV8tzT^t-FP^D1CEv9l;>&-Y)s2XS|U3zgxGd(;9F?~%K3*_QnCC8XpO>ZZ87E# z54|Y_%7C;(>_Ru$o%v!R{t%8@XK0wCZt z`)I_1LBCgJ95J&b$Adg~X_uu)M-E_lC;-4Q$L<0Re$At%U!pw0L8sc`$qg{T_kqH7 zuA?!MuNhxdjF}?(AHnnM)Pe%O?@42<4%hB7dewup>o0kA!|;#I!s&k!r2er|nE3$~ z>KU5I$et8fc)r&YqLu3+{i;r!<6{xT;evuy`agL196;NRig0<+Ea_|vMV0Hq{B+%@wCS)cp!`n1^@=Dw0aNj{ET~* z9-62?zp-waqEUJAU=}w_fw{SzScaSxOQ+kOPz4=bc9D{&ASti=9mAo5Sn}!}#W023 zyKTR^QHbQ(+Z@K{&zzBE)vi+|{JK0*XmNaT<-OONpFguwv-c17yI&Tw+Aka4qe{HR zy`E(KjP^s$eWI??*GOi}IX_{M^5D;(FTX7;+W5Ira>(^vVgxt(^|o?T2<77!FYd(fIq3Fh@DPoli_G|Z zT_3-6nqy(1k&S&oOM4#F0-o3@lIrM^oDWOI95kT`6i7Y)FZJzXBtb>~>O907FN~jc zhmDzO&z$V7`?l=SVrLA!lc}bPC`vvjk>rcO8*FQv#*Pla6e_F?4h~u_yd*0vb(;CA zaQO`#qqzzMCX??h9$9VATVa8z%YlB=BX}_129|h0#{;y-{e1;an{FCEkq}Y^TAtX?sOdV$Oi<-9OQAp z9@HJYhpJ^@))bAH>kTpr=#Ue6WnZji(WCVcS?4M#(SW)?R@(3mzr~2~VNUPLXvqc_ zII1@@hF!0*R#ux!u$sD~t;0gZNB>4gM?^n)hJSP+DCNZq%nVFGYddU5Tz!69$-hWj z8xtzqDyuvn828?!@}ljCt&e#WZu~};w)K{bj0~en0eC_s`1taI6IsiZYo>;bLXMU~ zYzFSaT3Y8ICJo%^!hgJ4E5sacbpTsV1;`Q-3cYVMT+Ciq?P5;Wu0-?T*J4gIgJY*sqGd^Q^AxV0@1o*y-N%B>0o1b!rf3($ zDpUwLhFxd-vo)npKiV~y3yCfZlaAv2)@3Nw4m)Z`j!vU~d#pe;_(RY4rF%bbWWlCZ zM|rndn3=VGrNi8PwCQk<3SrRv^_0}EpiM_~|#xo)Y#~ac&2_z!5Dh+)|96Vc>*q?gmGx zn-eBv?I`=ZYd`=lquMPoDJcS&M1Ftb@z!<5X3o8t3*~C&TIskU48CK=-MJVel|zf~ z^Z7%|!*SH@V@CQbTaQ;pv&E9c)Q*pQHg=Mtx_UEuQ{jnLM7;GDpTILE5a!ol%(Cga z|NTagc!D2ewtQn)G#L{DS6=2`{$NST1g80!&Ow0tiqGM; z(ET`p>QD-%q3%TL2!tkE=}9q)!ZSY~A10mm?tiC_bKBLc^2RPQ-XX((AT_NJc-S&$PD{94Km)Wx7R z7e^EHPDN+%cBwEPKW82E^SgIAx42f% zO-^M$a#)?y3&p$vdJC``L}6T**k`O<^6)*e#=I$`ki#lJ%x0l_xj{$>T0CkV9+FF! zE(H%$@^0(7?@x%kAFW)6K$%mH^nB-=)8|96dl>o^sfi?3E}(r6)YI-BHXomTFF`lw zF#0L5^7i>yT0Tn)D~f`GB54@OO__xGWTE{U9!-xV7iA@X(^0l73Q-RUkK3zb!8tjW zdHMOzWn}a_V%-iFPZe;xBWsWB7IopK*MY^~uV&|xpaU`yiu630b<^%g`nqJg-`y@p zsT~QuP^C}08Y76O8K>X7VdSL|=PhAmwuxpz@`wNWRjcqr;Ul;S3%$ zcm(ED3K$aSK3oUx?b2v_KJF4$Kv^k3r+Sx~`T~sS34Gt9YRphoJ%h6~o;OZ!_QAe_ z{%}EH1oy_dsiM}__dV!U&1))=p79*U3I=oSV|Z@Imv(Xnic60F=88bF0oRwH@Ng(C zl;U^%?AKhAQ&PgLQQ}b*nC86`B{C%?B~w$=67bK>-3cK3c%O@l801AC@o8G72eH~R z+-_+TKch|(@OzX>Z_MX}w|fQ$Q`s%{>Mw<^c!oNxZJecRYC1k_u^^0I-UxHr;x!0ky63*uuf0UL z`>uunob{IYd;(OQN+$< zF*2H_0hc~m}lhguSAU?&_)i+jEeY}xt=a482C_nJ&b#cHJ0qIkpWd21BzIa`8f4k<^N&J0r9{vU zR6cD%R6&s7dRm$2cet;YZz?B#_s*ouHhG^bT%XWNEB4)tW?)?_@npa}CP6I8E4tEo z4>P_P`%bO=RT;}Z1hlZS(oBmn3iID(1Y;UYORDx50nK^gRf9M)59k>|J*@w1*RgX( zH?gI?rVn}dYRFZSBNq{L7-v=zx`irdYyGU}+2WMk$WZK6LVS*G7 zl-4AbWi_=Uy`+*|Fr%o20~9RZh2vjzmRM08A1@O1s4M`jDirbwyEByK39eqSTiox? z)AM^tF2u(N&R&e~-=EFR%d6??s-Cu%&swXU#%whDLyFrAQ!}&N{Cu7HPDHNz30}h{ zWY7S}2;P$WhyBU$rb`R%V)$Fv8oeEM<*BUPs}P~xxma)-?j2=w!^Q)?om-3P+23HN zQz^U(|7%vFxqm>?6I@UA$Ao(?tip|^vpJV*zf#lA~nLBwV)m-GYA z8<&DPM?L9f*KS1rG-TDIPjVYpAVPfdw*^v3w@y6rNjA-4exMQS4EAPTK6mk>_k(L; z=PWl;`d*LlZ7$bvyyoNnVow(uo-No`j9Ce%ZO=Xm-9?R^?5=I@;*cF_M?P=tU)pJn z;_{|{_2b)%pIPcIkhFf`U=aou^gsER&F=!pV7#BdD+C2`>PAQaM?t~c}< zF^5akPoI9kCpqAQhBU`{XZR&5bMcM45QSo@nE-mKlZb;}?Q79fmuqvr%ptK&OU>W^ zT?D}vco(cusOcB+p#03yYvBY=;<>k#l>&hK>vVso17Fh5U%u3gmTq0fK8oI5(WKoO z_G2hx*N{S2?^}v;C6S9PpLH5y{+v3B(md9WFFsxQUM+VqB;a5)>5Q zyR_3!>fgZ2#ifKZNFO5M;!*wE@Zl}BHQlAnvCaFd6;1=<+|?@TWC#om)&dD)&&4Jgh9mCNQQFVRJ_cUVuipq zR=4vf&*k%1AonOsBfG=K-o78a8)6`m6g^q* zcTrqi+yj2Td*{witW(a%5wh^%>sGMbYmJ9|y}CwC+HFlhs$0oBCbvSSh!WGbSU21GM7u^S*_jW4oe#wy`}!RR&vqHa_zd)3DJMd zWqf=)dxyOj>1jCi;;6zVi3hPaQiFs1E-8&u5fkOl{vlG}t!9rd9f!D#jk12YF3Y{3 z#y{ICU>yuu*8Nj|T(f+9sbE_1kA5GVy@Y*kN=NUX$<0mi@fWDyLuIo%klm&4ooy2$ zMPM5JT^dKNT4%=`8Xwxuw_GT~qeRnNAO)JQePooBn-lk;@fVfyTO7&e0y6Kzs-nES zd(5j>(-pHW{2VAIf^Lxr4m_}PKbQ$aaXZyiVAqS*r_K(&&8@5w2k*}B8@dXP(s|b1 zS2IVxHEeC0ZLu=rhLnalN7VD7ev5TG1qT9hLK>>NFBsb2A%rBGtxa+pEKE0Mei75G60-L5HC{LI}IL zx$W#$Wzft2+?P*CnBRyo!6w&z%*!=fM5KJq@G$=$v zp{{OB)jOzN>#1V5*?^5_-P{bhU9{Xs2Ao(9U$#dvbAS+bwMOmf1tlh1?iDRTl_5L5 zO)G!;wQl;|DlO5RYIsuDpmCpyD!+3RP)i;7o})=Egway`wEq5VBExPnC&rmgJw~PO zM~p&b(akd-60vAsbS5p_8s>Do!VrQZ&SI*w^6umGY=nK_;tln%mjfvmuzE0d=RKZu zG@q>DyS+(&e&jIU834g%@rtT8b27Y;TGOoLzW&>NbaXVQxR{21@gbIg^Y+AIpNYd3 z13|@3e-t>=VcovX!Dqj$eR`BMHN~3@FoW_Fhl`8L)y;&}?J(sNg}|4uUllH1yCtQs zKY^S}l6#5v@v(G2+2K+tHh-S4FMV1-@2}s0fb875<77zl(cWDqDKOq)2TKDGDzL$a zkO@)fbRW0?B0&P}@Xpcz6;uTf!m+h8;OD=BIYQw(E+jp1J>F2r$n2}w8-EQISgK-M z7!Y7@jvxlN3|5ykUkZwgUxG^gdAcIg$H2h3=8%0?Dl%w(ebdviMZBvn{+z7Gel8_7 z?SUuwXyKSYJF7``S2`_w^G*~$k-k_SEnD;J)r_1sMe;5x%1R=B0r^hF5wY%{r<}N? zjWG)g*d=z$m*)nUFMLBwTKt}z>zD`}zxZ>w>*E9bWVy7ap7}zRZsr)82mcRgZy8nP z+J%jRZVMDq8VNxE5CQ2f>25f4;ok4N_cz|} zjB&>K=RnqCt>?L)`=0ZfSGaJr8c3nMsMy_p@AB|++LuQa1WP99rm^{_Mypl5)1l1U zrd0)6lro8D`wNOR8G^I7jP@6UWOL__@}0T%8rf}ziz>w39viFjR8PN`CN{RRzPY0A z=H=SH?t1-;wH)h)VBW!_FO<^8lTD3k9InxA;V)G}hu05F*-VdkHtOTBb8>KOh^OAC zRQZ;@o9SJCob)D8IR5p}2nqd{cQePuKOb^>j>c6X9^`k~HFF-UZC|1?&+~tBds&~# z+C&o<1ppj}qow)kqhHIE`a|IK3~Ih65L2h}Ug0n@7Y~&pTu~2m=r7QpiOjTd>JRQ< z=ii?AQzTZ8X7l7$?2MF&F2=3lVVN%pj|RIIidH5hQpdkX+nm9t+98~Ima<2P61dhm zo9>u7Yj}$)e&Tx^DOmzGE~nLB&;q*2;A?jOdx9TepD8e$8DKqpK}9DvsK5TH&{F>- zmL!Ibfcs3u@-Z_(N&z9=F>_ zErAr;Js~sZ&#k$QE(x30Xg`>QjxhV2x0@&1S!f&QbC|65$Gn{A5RbK6_gD^?*ee9^ z6Qfq~KyCkkUsY)$nPXY?hyLIsUe9ML>nX3fCaZ~_w+AI=%?U|KCd;P`986{h2-c#G zEluwD_FvK=(HoRm`h9Qt^O5Th+Znvf z6;(^G&&;ng21h>(F;jkw*Lr+D(LIoR%+s?$z$-v(6Dpx@J~^3{y8V!P=Im+Hn7$aI ziuh~mnEA$f;9vk)$bg>dV_ckxtO2OBf}zAjx@B0M{gTc+)E_mKB5wQaXn%|CXriG_ zRjNfHJfxi_KB>4qQrD=m zca!8nq;bQ^Nc&H8Bn;>YtOk2{IZCx3#$BDjrn4VSikd{usOfo@(;q!8W#~rqs$H6b z9l0gW52Q^As~ZAa9)2$lZRwjUU}N>4DT-Rboh7oWF;Mu&16{r40cKi#=i~z=s>6XuMhvpN4$==#4-K#`D&~KX5m0Oa6C@maP^f)U{xAeP~ z^Iy;`j#W{^sibRQ5E>T;yqv@It7-oO?r5V+ZhUulH@e|hNRQTix_$FE5k1v^a{;<$ z$G&y9bt)x%>Yk;gzs)mrpq*VX9W-jJ)*C){Eq7(ahifo1NIG^8TFnaBO9=*_pPwF) zLe8YLv}Rr5A={rd7G32v3f*UM>I82&9uc_aMHFY_zMLD90^Fj@l$euwE_qN`%|Bwf zutF}+b-mtln2}iimrCD;GH;G4wwia>)ZUkBS<>_vL`?t7P>ITf=2Fl7F8A#5y_G|z z0Do+LN0rOYkz-^THSw*hl~q;YzdWpsjErg#%#?@6+DuEdPTxaq8;A!((jR>c&T4Mb zKn-&_LvfpkdNLi(XXAJk!>pz5+)x$PNwOFJaYR~ZsNsFy`nt1l`_}`{Uw+b%Stu(( z7Lad6pZghs@r&+kFM0c|tCt}JXmTf$jr}!#T{yic^9D|Yj=db0PiCay?hll!f}F8} z-DZ=`6Y2NFnMaATA8rgq@0jwpmP^~2eW;j@hzXL`14VR{50>CVGkO!<)w4`G9K^%@ z@l7BaZHGt2AZRq*YogjC^Lr5PqyX@?Neh~459CxFiZT0Q5sgOePq+txW&k>#cVN_w z=9&B$oOtXBN7Tw6ew%ff>x>(m=!|A7>72#dT24-&?-y(;OB7c zjlJBAEGu%+#DK)`dy;GFiK5ZLY6EFK-5hVCF}k;3b}8%Y6OoH*a-`rb47dn4?K&7$ z_ry+Gl42p8(W!A=KX7!GOyr*%ZhgHOA|--VHh1hY@<>B~-Oc{)+Y~DNvN^qov5CZ( zTm3eqbN;bGIMJC-)yAqR`p!iwbfxoo)g6;RN&3s=?xJ-Y&aBtB4@<*Em!jT}CubK;Pbb~}NzouelE|Z(c zn#ljyU4^2CaPt5x5$5e%m`fcCTHE(-UbS2K3FdvgmT<}(Zkglr?lA(nA|_u$Ye70q z7QnDFFb)m|B>8fI<;?yS`b#|Oo9lpN4ogI%-qr@)cuFm~y+2tjUbyq|uVWND zIXmCMob1jFHR!nWlJCs`sz=7On>|L2P*R;_8kMyDx@zZT>WFQjzQXdK&MA+dFs{FH z8T~b9EF2pdz`^Vn6Hb^Sr=pc^#~^1BWJG7|h)~myhU^m^^-26H) zfNO4Ut~1mYZj8_GL@_?DL4%D($Z7UIQFBa|G=&T5CZKQt71=vH949bPk2CF;*7j}u zo}i=Mbq!|EOFxY66%}tYe_;G|LK`j#q3M>J4r7(J8Q|te`24HkjYitFqp&O1n&-)Y zBkrw@bpyrjoor?*_*L($#h+ic0cUC;K)?E)nhDheYP}aia;E8X?*0q9u-DiFv-oRL zeVN8{VvOPFgpDr3CAO{JI`FJM7SyW33oCA0U7eeG7v zm`20UKB8|H8!c?WXeUj{WOW+vu?Oe)-2QFxkVyHB%X?`7#+m0^*H`$eA1BqVX5No8 z3ST?I+1(e-lzpX>et#d~=TT#3z|>*mu=q~iZ)6-t488S5J^0nnFZOm27tMzrp>wI_IjM1KAJoEIwrbJNKI*^ zdd2jZ*TN=$sqZY%q2zFD9=VE#l^<)Mu3VTg83N z_~b54$U}l{KnEax0>LwNsYHSHADMj+qJl@SkaKC$<8~99Zp(c+I(j7QtI94OJ%@k> z*4XblbKL!07*bxI%KadPb`-2AO~ED+T;QFZ=erY|Qzc%44h#kho)y*$cX0Q(S4R*p zricC1JOT^t?O4X9xW~)PTbHU;pFJ0RY_94aVK7#6FN)-{CYNop@Ao@b(CiGEw?vx~ zsm@)XkD7y)YO!*YGo(B2zS*{u+A?RkBJg*Y8irn1yFN~uJbGIbceXdauDBaGc7tda zRq_krmr%Ab4r20|Ciy*toBjK=@lR8Lm3}vC4Qg^_azVt+pzEXlPaO3Tw;=ZEJ|**D zoo}nePVA)QesGr>Z(@5;V*2--><<&Q2wwi)PhOFq%coS7J%y6)&qp%YF)^JyN%CA` z8IB%c=J{0gu97S~dVJr!WN4p<8sVB481E5MI3f3~OQv-K&-!r5?|Xo+%ia(kg9>&N zgoXN@*p2t~dE9xRLBB(0)p&Z!7)ZNt`#!yTcNAr~MPBOkXyTAbg831lcc)h7j$^l{ zL@b@oLwv4E>hnY9B}l7977{+wi1V#zKF1<(Uk~+dXb@=x#sM~*FA5Y?RCN7DU+5k@ z5Y8N2&V57}U~6WUM_3?F7ruAZ<7@A$hvnm~1E$r8+;Yeg2`McuE>;dQ|5d%crM*`1 zsi1ugt8sMy)LczBQlm0KX-zKRSenV6X2Q0f3bJ?Z3s(q^tg z{%GE$h-aUXH34Mt!t8u*v3I8WU>e0*oYf~SQS)gx_1bXtq4>$^y40y^Nz2ZiJ};b6 znRhhXvebcMjL1?)rc6Ya-|9?{O6%lzDI|Gh4P5M-T5zOFVdhH}_18>B8$z zWri(6hKsNt8NKr!^Zb51x=bG3nIpy_&R+E&7q)*g0u2Ave~JIr ze9sl-3&1$OrmC_sF1)Q7uQajBH}Lr)Lce{O3nL%h7>Pt9IkoBOHR~e5iy!91@a< z@i6V#hDT*Cz)%uHTu#QeuC73*-BT{79f#Ug+vOvqOJ!&54Sh)}5=B6|Txw<>4Gyga$8(%d2O``u0d)3k3% zOZUEJhk)i-EEZ}w)wLG0g*(Eeeshduu>A-tfcST%F72u1Vd=H29FyP)338n2rA#Ii6v8A93P>QtnJmOD3r! zH;iE|eu4QqkSldi+fq}D|N4>HIlwrwZCX}ZDwfjQGv_H~dqvi8%JA9x&VTBM_}JKX18W3(ZJt6d-I#NAog#WI@|N5vb2~Kx6VgM( zdFUg-i`(H@7}W(5X8|G}ho7S?4OojyN2iN@M}iq)14^nx4-J0a2w7J%7#9{a9f*ic{zQKisSxX$&)9ymIi9jLGjnbQ)RzONj0zQX@C=y z4~NsMhxqusc5xLcd>;#S#IXnn8tdLNBPnHY2@_siaIwsUYX_5(#w7myv!iVFo9ELU z<(gV@9Utlr7y|)C(KF)8l`G#Xti}B_ewga$)WZq_ocF(P@z3R5UXkKW4NP9=pOEdG zqzB{mPtRiqaN;s-%tjCHG3QJEz7F2hQKI1Ong5%b6cQY4xJEO5qc=&IcfKRqQ^mA1 zo_7fhX6ZB!zj_>7T3gqTk0)eg)T03E=>>sxsGnZYfSVO30ha{|2Zs@q@r|>si628l zg$MK07a_{r%*+fXliEi+(gaWt($Z$$#D5so(NT2Fk8osrOD9hksqB2QgwpgZAK*O~ zBD~{BQCVkaZqBShaCCRQnm0xxX{@lcltY+jlWX{ICd-yl(p;_zfAjkrSln(We|-JQ zcpbw`Yefm==KmnKRO>O^284fiP`t?fJb!0(#A|Q0>XU*h^nw}Bw&=qp>EyRkMg2a* z`k;5b@Vxf6(w2!w*PKv6QLDs`G(WDeF3eG>h*+;xm-u<&SzcAuTP^yMjRHIQ6G&Vz zNou`kXLVr|jgBX_@eMsbU~Zu+o*I&o0kTL=P6i+Ym>e^clP{>6!SL~`h#1Ld*)+)< z<>Jz3!x!-_4w$(86|a9c$d^Wni;BQ2K_q_=$RFH}TkQSWifqC{WN2HPnH?pDq}Gc) z%n*r%uBEjNvrNVp>t~S3!ARte3AruxgM*3XEBSL zp0kf_Zf*|R=?0aEqbv>D1n50(0bDm!V)(T2YGn|RN`MR^AW-ys&@`{Z@&FGqZn^CD zT$0c$Mg;nXxnz+*-pu5ED5O+-?=)hqz&H;gcBf`$JaTgxVfxop6iz?*&}rvSAn3fH z!Dcrby)KBT#7QW$(p$e&W^v)Jn(^m9X2T^bR-<|e=KD8csl5K8a(40{1ZxY0Uq(zw z*mjjl%H1ibk|^Zul5* z6>U686_NYzv^#8Zg;t`y=rYWjfeRyP^mGpc970YGX2jf1EOmT#^}FKgJEE_UCZ`WB z-#|yFW?-m(JOu6YQ04BL90JD01ZB604z`3TnAB8M2!XpZRAnz%W-XUTPQCwgST|n9pX=qt z8((FUYNf{<68ci9#)uyZ|9NlvhRe~M<`}@`0BV0UuVnO%>l+6BS#vbulCY0Lb{DFl zq5U%^Je>3;@x*tiXGgj&wW}JPBR!M5_67TyQc`WEQjF2kCu^PiZLy`(nNvCzd8^{{ zoy$atf}x1p=d!(iQz;n*)kTa}2w?aiu4gP>XRPCMBe6bL$6aPFI*^ZF2X(|rP*|#n?qJWgovPlcx7Vd{OFEdw`dZ3$=r8-UYeOoW^mzz8EJAc$@!aY;mEl?jw9Lw#z_XqUw$%^?TRKy4dE^h8ZAasI1EIIrE;{gWhhXppI z8h-8nfB2dqd72Ulii5M_gSByc_)kijEi?xISfIC8__^`LT2acW8u_Dv@*S$4w)WXQ z`ZBkKHP!P^3wbme2b+W1%T8Cz9K7y-RcB=9Pts$pIMVSuLK<83%n%HPe`m+;}%Wrv0M@BAhf4c>#-(b2)T zOdnaC{47xP*P~QNgfz@jK4dawzGcQZ+yA4}zsy|XO?Z0YiRrOE7p=+likc{@GTd#) zCel>=qUUDkP3FfxN561io4BW;OMDNH(`n5=buPyu&}B=2n^wYeVB76T*^2GE1h;$J z-HDOK>fe-5J4{Vzxa@m|L@#HZq&!mI-r{4 zllchS% zN4GZa_6~pH)WzqdH4i<3F17T-GH-1}YFCOM1_$1S&kRkUw<}Mn4Sk`Vh)+cp)b2(9 zq6%JV3ki`uJ5HU;t{YSBpP!wTvk)R{ixiSM*jpYn$Hc|$f?!*ctW!A4Ln9+qU&&1# z?cM|9PwWa!Wnu))|Aj{QIi|$j@(}QRAD^9L` z!0pbY)Qo6nL7p4}|K;^1nv?CHg9VLn@jMwYvBD%NJ7c^oj(!lZrD7+${2#2mkrAa* zAW@11k@%FA;h}lH1NYyl1;sMC)vAh$&F${0AqZ!;v$H#F10m+q^D|e&fgD=+H6B|R z!=WP4%)rF>_~!}=xIoZQQdY)M!je}ze1m!lhIAwr`3&2At4a2CYC8Ty{sAwtPJ+UpXV<}cj*IVlSEPqsG2Px%}|}5 z(ZTEK~d2(kh(z0`xZ@# zrorRJt<_d>Qr9;sxlN6Yo>0cau9Ifa?M$j2MC7C08e$H;0zJy|;CUSs@hAmA(j+1# z{#l?!EF~2fCrH{}V#xM1eCprmULO&c#Yf8-Dx@?3rYd4CE_~Yy?s^QIa|eLQ0qu3O zUvay&t&N<|sC(j2CrM93J-nh543MzN{Gce+Swc zQ9d=-;Yr13B-@C*eWyO`=Mub`-a3?Jzall6ar;G9e@1-VQt1*V0cT#(`K}wFzSerp zVlmQZM7+|>MWJSQxQ)rn0?)3z`L5FWGCxo(y)$VrL8pmBn)%jDN0Db5wnn?z(A*IRcU4RM+U%CGCAs;RF;(MX*YNQ0ib>8T&JSO{Y`2@rMYe{YfW0OX6uz`A znx(v->5?D+XdvmauSULf|B_8KnkRXCQxg?68ylNq{@?;PFYhv#;dS-(y>#E47WGI; zNdY^fK1@u^S13_@j$5bb{I_t~kaDN9%zu;Dq>%;W`731ciaDJK#e%FEdMmP`mD&4G z%7Y9Q{F84$x6=dpxkMU+FN9Xee?~*znZrt^938X;O_jFU8Bm>6%v{u@e5DMK;6341 z11_~6HopMwZ)vHh^u;PyVonGfn)^HW_}_q~)g8rRzB%rU)j=$QeA#rN?wxqQ-DQqh zXJW$P`1kKcLfbuQBmm5{w2U+yF7f#rpeg;A^k?%L9bPgGP&Uf#Rx2xX{{8?U)F`*a zgz;}H0DPaU$b12Q5YtZfceu!63B&VSR%5?mC^8$oc#R2OkNguOrGUUmYr(LB?b1bI zPeKn(+TXa%Z8;_pOGt|Lfef1Fz|NiJLEzB+hJko-cXv&HV>AL56_}0SYDojM{U4dz z*xcLzK3pXDZ2l~a3P%46qA4WDH3FrVhyV+AzKIxM#Vxa!8K% z`#0`lnS)2EyzU|35y~}A%2%i^@Cb)$08o${0QmzyjSO|!3QWoh=&q3pY$Sm`1MnLl z0%XW#p-E98-+R-cAnFkwy(0E`$!bWOCru!TK%kZR^Z9ee61D%}Es)KP6L*1HO>e$} zPEaTe4>;oyjDpF%KiTZps)d$+4h{7dXbIyopM8GUcX2I|+x4uF_f~S>60l}U8wWtl zX7Kf$MRLlVnJ%o9_n5aOK>~<^R67_C5Lq`PSJHcjQ~(oc6wy4tA#p_Hg1YkWwUvPh zqnx%8^B`7G>>+8&8_Kh|KcROnhZ%DQu>$Ycl&9NBKb zPY<^dtU!Uhcq5E|b|xT#d+jm?Mv!W1>etWdF6DAWeq}x-%~cWX%txFk^vX0|Zfk9w z3g8qgG@ro0AUz93obhO{{dw`tlKquOOK5m`d4Kiq&n))7*t%ZyHy2>0$y7kzh0A)L z0lZ{^1Z_FLqYNN#N0cm|cUTxUwBUM97ZJM+I&ITDc>lp0pUspS59VQEP6k201b7*b zt+6%oy!(uO+X$u+eEXwpu+_)@#*p6A=->_lj_}>Pj6iAYECI}jh^KZ{t zjp9g2EKCQ7ef;<*9BW8a!5;hfC20Bwk7LU;Uv&@nYg?e*R2QHU^@(($dn5 z{$IXYBjz^io7; zh0`t}3>xRX=}3gS6UK&$4M+_Q4Jj3W2fGAe`#E%7dHfr!Ebl4$V(j+m<-(pn0q@-x z7LU&8mIqIj3Q-${w|914L3!>l7e4hDyhzse7M@6jJSA0KWl2di<3ShAw}v9heXrSp zm<{pYLdhvX5!Dxa<9ml$Bt>}B0nl_o)v~qwaZ#M7hsEt#MhCF z5x$?E5a?8`;Y!a3-oN-!0w$}~#chI^WWXsME`bxlzjoQMSV%c421pius=r2a@h;L7 zeo?snWt!#ywQ9MT>gkE9jp8WeRy#YZT)XDXW54w0?k{;@HO&0%oq_ZFy2sa7c`ZTM z@lxRTbe+`gKtQLdy3u+4IZbitpb6 zs?QDWECkhsc9HYoEf1Qu?DMdf@B%xwX^`L9c4jY^Lg1X?CLqolXM^`;W&60?7(OSb z2o!-x6B_JiU<%PzG8XFS11d1WfNwE=2`OzQtKW+X3#Y!Paamb!cf<%IFU+)ro@HP1 z%F0TH;zUr2c+h2gaTYjg8-d*Z_Uo(rFb_=WZ3ggy)B3x3P^2OyC=|`s@aN{60uY;9 zS-NZZzb_2KzxkzDR5rs+Jx4rDN`BrQO$P^3L{ii+ih%2lp6Mp<)XQOZ_0cZTXU|Is8z|vrT zQ0wunBICyn^LIjahhE>}`NsBO$>?SC>?zhxG@stw6Y6`KCO8Tw!Oj8nmCoH04nOxa z-5ehV2YY48W!?qXwnvV?bFF1c8H`lq{vKAi9oyZVU{c3;Ba}25p2WO)oFqucGycH* zXF(DO+GNtZUe3-&xrtMgraegH5@;6j;%G7@_@EZLuNg+rs;7>2YH~ShX@d`l{(AaN zWU@O$df(4Lz@}7Tbt}R>Zp*4ckFuGkg`eLbv!?gtC zlGm^w>lmA#$N;+8AhiMW2@Vq&POs&$OBrJOcL!_@fTa z&emJAOS0?K7wf3)!^O5o<3wf#nO$DD&y{s6XrljHGE{5{F0x=o9O7w3yW^;u%MaX8 z(2J)ja8=)<9ned3THJdY0?Ev;UI`-!8>=H_q*;s?8=8z0vJ?#?{by0ZXWdN8S#03` zAvTs?wd_pyzxx@WopVqwZtnD%b`qzIQc_TWcSA}i@$b#Shw|^RCvR4=2Y5l;1_lOD zNIw~HxHvRGE%FaM^}>bnVzWg~A%SQD+EMYxm)&=q_!qhwi*%07Y%XRvq3I?UdJ8^Zm=Y#K2t#>!CFYs@AKM5u@APzPs2WTe;53a) zSLK+gGFL2Esp>B=9f^UFXB0~z&C^>(eBEJ@7}~kAk!s2pA5)0|rkV$Gn^Ry~sH?9Z z46!27eFmF5I}any-JSP|Nkl6+EDktF`u-I9lWgbiAMb2F(%lz*@rMYiXwW;~Wb$`y zsJk>YzrJFKr@DBnhv`2N#axmX@qL^@{^;!=e|DRdcDT6bDBr!_`oW|KHSxG|Pm*;# z%z(41c)MdHrtEdo`iR}f=x9h=xC#{aWXJ8#ZvILKAw&2L=Zwx#SC%z*?0+9G$=Gxv zR+hiy$Tv3wz}pnKIKP_v^)$b^NjsXD+)S6EbUKsb3m55EGUTz&JviOiUlA-EpV0b! zD`c}UU70vcM}?j}W7kO}6g4s;bl#`<_>s8+bQqAd(7X3x;B`#XQYsq+rX8{TJ)3NV zKY0gFF$j#3=3QA5!5i-TXH$BBvcdNXb8>AXH;WGN81X*#yV8_NgUhExUp5AIBx{y* z@-$x!1yy+A#}|%D-?=DKy^ALIXN8~*o!lEaSm|R17X0+jLM)I$_@7-q+8T)Mdm$gb zvSM=S5(9(A4kHQ>@G0r(+q;9&%>&w$=GQTQZ?D`N&WY%Ljs8O>RQ&w)#SFAd?6`hc zLR`W#kmN^)~CZ;KKIZpGnwg059eYMNBY=J81i5m?CpzyC{&z zhpIT=vgjmi*#G7NOcTn(w*Z62JGdlG9R$`6>+Z$g{{E>t>3a!VP`hp|>fF|PxajxO zkH)utYI3nxM~MN~-RpN+Y@Q4bd4+c_Zu{BkwU*YxX&as)#5ziFusq)!RcLTN98FkPx;4nW62R<8oDtWhjD_Lb*-C-|8cH>R9W-aE#xyxa*S`E2Wc zheW5#_@=|9)0NB9C&UAf7K*!T>vUNQO@{VwlDLS!WlissXbv1(99Y$t_}ykU;A}c~ zdcvKlTP@ohO4kg50QN#1Y9L79pI+OU%jHZ@%F03rSL^fb^jwa+FmJyX8XM~@QPFR- zD=hc>POD7q$NaBHVYay?k6%8!3Ctk6gs{-xP(7cnSgX7`NAx?yxm@9ZzXIm?u;1wp zmzZ8DU#NaPF?AKjJ)WKV+txON5n*A)BI4qBD*Eh%N=3RXV77E!RHlua_4g%Y+;A?Y zd2^`AI6ptmkI%Me`FLv9CzL^LBOv|c==1Z%@fv!k8VpchM-`?3w`a1xBJqfMF&iGw z))HfT;Uql_uTapy-XS5Oa{awh8*nl)TSZ~Aw~bJ-8QL0iU(wnpb#7xxHkK9<`2+kd z$oTnBiwN(4;ni1AuZ@ z@9(|GSkyD40#mPj&Z@WeG(a>jG$BE1eY`9Edb%sRdHtU3%m1{|s!N{*&la0m|sd8rD+@jmH&>So`*CEfGd z%OYY5sC82o8&jjw+)g`OSy>?;3rMs3B~47^1;zi`*;?|2DDdFWxJx>5-LLT->;!IV z>U)$ch?OJ#;^;UcV{S2;iyikpjg;r2b)n%5E@(*!j(^Y~ZFZ`t-@b3Bz(b0pI0w66 zK-1<~|A93}IOkGwx?c;D7I-#x+qD8TkRj9b&lh6@@Y_3_^3prIDSvTbUYB`~)|=#frs!8uVSl^H-<9YE zSRqiLm^A^C4!+^y&8UCXVRlGIF2` z{hpxLkN@K(bJO5Ix>0v||CsltWjF!L^pgY^t1v`8rjSFX5WC;^C7YdJ{qYA>0dTx} zT)SPol8#fZ#)JZAfiSEshz$mJ8w}$?vsdd0e+Y`hJkQTqWFeC@Wzi<8{=q@1I2)02 z^chCBoLGQU*ug;)-_ar#<*?bWA7wr40WiG|m(fj|F?CTqI-yS{41SK!`|1uj(a}B( zkzq5|>FlJj8Ke4Qru||e@;Z86+t7}VOMwf8#KP@xLzn(MHdZ4s3SSH%x@E`DGqgF% zS%Y&ptq6@g|Ic3+u)2@u4!}xU6vTVLwR^7_s2S($=C%>O#pmA8%n3G5-LU;b_w&_u z@DbUA2Yt(1e2H#H+=(u%c=Ga&>oo*=BxRV%Hc-I)fT@RPrL8fWLklrQq68J?Tu$PH{Dbcpt24){=5?5uEvwo4YMXA~?x{a^tOoUe6TVJNIUwa5~j>65Y5GO@_yia_ewa7Q!W%rjY z1$8=#1-uG`;?lZusfcf<;%IpaeiEm?t zqW4-HR}0S@cu-iaWP3_{m4z)R$+Pd3=f$Tz3o3NZ4d-9ZapOLNXaunEbN3t%&!eF` zI-E*mg6L=1GbFcZ$nq}XB+vhKq8GYPJXx{h-JSVWbh;n!nWqYIR+lba;yM~Ev{Jq8{-ut7XQ z5&^1{8-b*PGReFrKm%XMEz@Y$WetHUeWdhCXllScMP!o7yLaz?*1DpjoF0vRZ4FKL z4LBcAJ?_cZ)CBX^XArgOu=<4)pZ~}b;@~Tm^PFK~OKa=W$bVio;pPF+EhG+;S||bs zuW)H;X~AC?T>~MOcIP$5TYmtHhr`?0&Xr>mR_Qw8%*p)}nG%R=P@w zgW6*vvf`Z6_fic>hKSeOC-~3M+;d8|c1H}ktlHCY4W~o0{_tFSc#W?5$eIDgd?K;q zkb)ZJYRb1aEJ^I^cdplUg>$>`J|a#Zt2&4o#joSXs_(srV=_d?yeC1|yXoiT8nb`> zQ-9}Mco*+Hz1gbsH0x~-vRU1XYEITGRq_v@l-pZ1pZt1_l$O1ATHsvN^=!T?zVq|* z*l6DUnM=nYsZFwhbsqe{4}qDxxNyxqQ4keLt9XB-`vqwI!M*@I9(X3)sv5EnT$a2@ zBUl}`C?I5ALK^X+{WB1&T#Da7nZH&cezbFa${ZG!c3%_Y?EEnD)FN@4MGYk8mlFDR zc4-+xl~~s%bny9RF(O>{QxvK7rsAE-Zc~=Nw0Coyw2Q!4@#W584fr6qMyttD&O0*U z5d7n^^Ffm4HMhmVugeXWCTow^V1`Gsv(y)K&usA-lqot**e2`8iz&B3OZ-fwXfQ-WaFPXdg1 z@%D&SK0J)^h6%7KA#t3wgjs3xrziG3DdMEhs2(t?a_mbW*O3pFCb>W@3+KdWs78%b zPe!k8^~sj82JyXDRu+n9{zUSVEVKt=`DKVNt1thE5kA}>)%TAb30rq}4K-2CO0PEV zn_|x&iB+s!`T9Oc@O zOU@o@_#~h^zYz$MBQJd$+*(Gx*;Yqk#LnKkptTO60bOmd2}VDv+|W;LBu11Ert9lq zkp}XLf1|p`If~oj?iKg-m0X5(4Z$1e;)5YJY*Kqe8|;>nsO|)&H0m5Ht&zLu& z!oGAZul5ypzo?Pq>s;Rbs#{<|V~K9S#`J^1&TNZ0;#44>VZ<>mPc7nHAURq0t&=i6KkcASti8;jQ?o>f`I*RF21QxoW>XL79)(hGK`DhIu>^2_I1J-Sl( ztB=9Tn%!iGFdNa5YC2J8#p`C7-@Wtzqv*R_k*qq=X?a;rQv>f=XDe=(%f(jHF*O>U8Z;KA|9AyhWSz&3_JvK#X9$b4&v zHKvtusSQg=(3m_$owVUu`dN5yta9umXt|6^M06IK0_fUM%O;u(65S-UweLbjiR?dj zNP8m7#^XL@X}ngi#E(7qFQw=hZtjT01jbA%&v&%$4V~%e zWN-b-X5LIZ=UY~`Gwkg1WPT6A-tKis<%#Ol2WjjE-uiTUU1Ust?PRs<*+}VWVExqN zKK8wpq2*8=XXD1b=+sow6rGu>^0JXw6aI}y6DDq*FP&w$QwfEuq_ke7`y`l}U`*5? zd|#$jz8y1WN;L6o#>ZzTi$m4<#@B9@i!*acROUXrth7^YOoYERT~hFomEV-4M&gP2 zIhc;!!Ns*Z3?EZ@VCKuq)EzR*d+-sn)o<2 zAEE;r4r&G5u^+RN*|Atyp-}>^R8J#LUQC<9(A%;?4Ox>xVVJ*oFkU&@NB{gJZEkg& zl-G_Ou8x$A4LeZm<~uoSNc)$+d^8)?F_e8ZL_Hwrz+#-MIAZ*IG93&LH^ryhaE3ZN=yk6#3uvGQF$VuPqXA9CK7Z@uG=bMWBR7UXlXmM5L1EM*up76%;&#_ydU7V082F z!`?mQ1xoYOloqlk7^~WURW@QcUQHk)6C7E6`{LR~I-egMpF~GRiGYqIcy7+t&ELo8 zt1c_f5yJF41&LSvS5S_%^I?#Q^q!0}(DCwmu^B)VNh!``!gjK&*F7%BEo_LE0vqoS z<7^FVPI7?60h0IU&niU5n6F>Cm1((&0+g6R$%8sKD_>Ba587`VI-IBcA?I^dG|6g4 zUk(CNi%Zyi$Lo^pSphrcacjvQjMn-UUBW#x=ZW24;L2kzU z#JO9e`#HTan&OWI={|N0624~$H`l|Nps^J=MApaop;wNQHh&zxdzh55pO5HH z)O6!cgds<7sp&n)*#kc^o5h|ykG4DpGa?7{5|e6N zVkvJapW<$h+4~`(0-QG!#!M)H^+m-x@4xE!*^KHLN-b4n@s$&0c2)^#6Iph91x}jl zFg-mvsAo-_2(KJ4%xVj?*GL{~@3R=rAv)2<@WOMwS{H1NXu!u78dTQQx`{zHlV87* zuqpO-wR?5EE9nYp-wf9NSf-AkYiFKezLV`rx4`i`z-?>KoKVB*JT1VTP{Y;Mcd#&s z^qdG%MmqOa5mpBqi63%k~#M@e&$!Sz%Xc+FMoSvYe)XZ?>dt0M0ob z@yBHJ3gyP;xJ+stsS^1I6ldR?6n|_3%YV9N|1+!QbbD%AT5v=}69nk!9qrhIWqE{^ zA>Ahg8F~o(mZP11nj<74Zg|rUm7gaMc zcWG9w^8_+~_&o7savbYHH$MYuK(1Ds5K=Cfk9ES5x8$MUj`zodHmj&Ib ze-i50ygA4O++v-xsaJFp8;QwKkOFK z?{K(%VVGVFVQSJfOxL{qbH0JDQ*DvL|6f2_1{h_NUV)TO2_VPk!_kxs!wfob#*>F| zPaCRG37D1TzLf9B$H#wd!$m|@2W*jd?9b7A*%|rz=8u@pYXeEP)}IY0B_+A>yQ!$O z*1mUrzBYZGjP4zEf`dPky4mcwvlq_rsJu7$-`><6ua-X8Y}Nw<@3J0SyOo1*ZfgPE zCjX&1qWF!-BYo@OIj{CtT(icatL05s&MiKHCUE-^JG&clOqwuGes02JA)M{?`0VkRYSmWzvt` zn!R}ug9eK|WTI5KFt$hsK?u^ZY>p@zGx?#cwM z6A@~y-ma~q!$Md?YORSa-AjA2oPB&n_`nAKwdYE}g2+42V9imo%%~ zrBEgiqY9{f8-S1#?KQMni*E6I>HdJG;cIDt%*|gvu32<{BD|xQCQ~ezaL`R!vgGnX zUQl7-YwZ~7&+sG4JvDw)?W>m`n)h1`+w!2OnZYpL(joLU`=i6H6Pr$!syH;O% zOQxFrRB_F@S~qmx^ZhUZuimwn2`0ivjxU!qa$K-QgEy;DYc!AaO z{x&4ME2=T!LHYvllnJl7);+gO-`1S(bMr6x&i6QkgjV(Lb~oa>%LXlw7)rlB z@SNe_AK@vfdU;TQkD2eFX{-@d7->MMmvUcX(8Y{ypp8BTU$(7i+F8IyCmy;Mq zH$n}FJ!IxIgNP@~c4czTxg#ckGv?B5uWO)Do_)G-_98A2ojNJUA6?HlQoi2Aa;jii zsVYb1?QC*MR)dn`7J}Eo+JrubJI%QjVv){{*VBO=@+^)k80cc8qRKqT2y95d)H|AU z;f5OsAqY@v&-E!gGMBm!ea>Es7>;&zXZf<~ihr5Wf1;`<+mt7Oup^6>G}7TXdvo_N zJ;TYBXmGdk?IUwMx2Xk2p#H#;1gUg7C#Ns3JNVLTxNM4QIIlh`H5n3ugzcrJadif$ zRKQw1n^n8vI@BGoYIwk)c51u(>Ap(X4@mWG-*xc3anp`fzYT*t*))S`q9j+Nj4#5n zp4ZZKxj0h2c~0)&mj{=Cx<)}zb4g^3*2CXloEg_t~An0w65of6?jQatRV$a81`PHR7wOpMF%oX*_2yDWN4K76q zat5;bymculIXSAb`4!zTy6$R0&wI@Qo$K0S&4})-(SD=5%$o6a<}nLBvfQWdE$>Yp z(M3im$8U6D;1Av%7OC%Up4xm=u!mt!j=DCxu#3@ipNNC<>x1l1T3ijDj->T#)@!bs zOoz10QoJX%mvWTXjDOMZ+6(@Y&3xD@I=1}%(Au4*`Rpcbfy>yIlAUR;(6cW@xTXit zFD|~=mMS&%*KUu&zbUrgm^zfRHT@%(YlUZ6f%w_5BU`Lu^Q2W}s1IGQ0>(PJs4cBr zV2PzbqmaUfDk?Ji`j+)ra>F43&ysC@H@Hf6pMyVPPK*s@%5a7@YmPVmFi^$*(?XIQ z=A{td(FYJRwy4(8&JwiZ?JHwdg9erwnXpCx(pid}y)?Rtfcy-k6HF$vQ_RgpN!VQw zU5pR2SiKa?82jDwuUd|cB=f|dRG2yYl$}yn3n*r9sIdH4`dKa4>X}Rf8MoT3hj~{y zq%9XoKGun zrozZ^^h`}~yq@kayk+meFIG>tzs0uYYN*7w;L``hsK-29+#%$t`H=kh|7q*G!>NAX zzZIF4O`(ONkeyxG<47p;pd@?GtV&jrkR6JwV`Y@>7$M`>GmaIGk)6GMw@-b(-|rv4 zuFG|Gxt!W(=2!; zm+7yO^BH8i;LU21BeBH3@BMB2u&ryHL@e326Yq+~-Y=Y*)Jd-h_?F!4KmTHp`$8+a zh|T@ol`>JcrT1n~?p@f3@3ZWK8qZ9&ctft(<#kxm{fAm$=c1O4A~?;EJfn1Z9vRbK zW=f9E3r&9%_K9F&!I?RJ1~H_ieSy2@fYz$%v7>u(#b~a4;x#wNRkxwWC~XHZ=i>Rd zs`EDmNVZ-Uc(_nV?$Q}eBdG_3R33jT@{ASQd8+=J{f5J&EY&&D8d!eH`N*p6-#zMJ zIhf%A3Y>6nF#k9Vf|7AOhUKT3nUz7aa~q<5+1^q`b6cA!BrpMK@Z+mZ>)Q)=G7T$) z%gV~GAuD4WLKr+Z&w#^v?2Sj}U`ydzk@&MI3_!jHR4igdHNY8=H8;Nkk_4c#d+Hfn zsU2rt@5yFgA%nqY0P);fbHPFjpuBV>g<_ZT(!FOt29|F0>&HoY3Illr9v%iR?O=3k zn39fwR-mNo1e22*{T1(wOLo_jwMRO){d9Nt_mZX^M7~V)UseAg!u%am$X~VYhg>c) zNv0C4{vz;vA}Z5BB)ZSeOS!3u*hHmcGGq_=E&S0PJHbxxIzHyjxs#<@M9Lbo+p~#+Y~AF3&EKEXq1Rfoog8MOD}Pk2)7;TH z8r`S1(HvQDYI;q&|HXv!i@0E_{6eEn55La-AZw%jSF%NJlBFv=@ zZFgUwUasmL;AiqML@VDKS^IItvINbVfvp|7!>MyHlW}=5kMU9{9m5fe0Ehi#3DH-d zVxrPwT?2=X#}5IAn{L0Rc-cz7*HXh^Dkg*;=^AkdwdL#L*u?3b%f4BJR-^lBa} zvMWV$;IB-E@Pemvi|29`&gFGO*PI}$kB`Vk!uDZHY6nXbo*PX@tM0aj_tLpd9D8f> z`_d53K-Za`IZ6-9H7YZNMV)u*CXMF7{6dDiy&Ypy*vroIQlVxx*e{}-VZ?-Yi}E~U zN5++@U}HB;PY`{Y8NpfBDahEJCUUE`-7&XoC7!H}h-z2-t;$B$^r6SM*86sGu9;qO zJ%iCbZ==ZyB zsohDpSZUHz3Nyw6lgQ-H6ML8 zweH~a9sCuBP2#-5Patx4YpTi04$TfN6D4Cxsh){BFr6D^2tej*6xniAe7(QgYml=e z{?VfEg8%vxjF(UbK5Xvm9*+2(g--liucc~mJ<2{f>+VlVunNoF+_o+NRGK7AYBv*i z@xT#MAvHAW615=6sR+Z)bkp%NXicV>)Cc?;DT{>79*oSjj&*hXh$-EFhKj(jktn<= z9X18^94rwDi4)krLb3?NKEY44E_M<$K{5b64x*)RyHXX59QVPD3cRAl;cDmKXZaXn z0R@X#uT#t#Jn+1TNKArld2m~sDln!nU~!P2c}fmS1yKKIx8yP7OPJjrJg;}KKrgIn zRAZOy6*g1;R*+3^CsB5MON1D1F1;d z?<8rawOaCGrv9+UyzRf0_Op>kL>U-_7n)~~zi``pTtH&K$1~Fx?JFiF#i){YestmY zd62&8(-*FbzYi;qm*;Sjc5B~kL@=V!TimIh@LxOohXr=g(%a5OsfB;A#z{Tsmf zfsCE*XJdS@?yWZ~iZVX}+xbs$E%bsY42aeOGYm7h#No16@EHZoPv=*zksbd)9@MPn z8Gh0fXbD|EUeep!?n&O8Ln>brg{&J|!_0;TzQjtTnhHrAE;)tc8k&YZ ze0-NMHm}{ycwHt(E;%j^vDQv_e-!=oRNX16#yI1NcVC+QWIpWQiROB9@x`^!FDH}ETFS1akkyx7Q92D9Wh+ra8RrL2 zhVn9JPgPPr^&&?R3se|N?ObU?4NnB7u9y&bdAFub|I~lpfTwc!LYZSr%sYCUrvOiJ zkfOO7ZE!t`EYd*7iNg6;__y8UM;6$*+q+S2f#^~WW2%ZbTO1;U9}s>NGx>({(~1e4 z&ofzKYWp|37%8}_NLWc$emCh!=#W@%V^=#rIXMZg^?To2S|)*9 z&E0il4NT(M!9w1ta&qKg^|GUYQ>;_#ECIt}wd3XrspCC??@DT%+|v>~6@Kq~RaI}{ z;|W%+b3{u?6MDYcJF3`7U7p)XCc(l{tMijD!wgpBOa|snj~Q+SWg{z6-T5=8;VshI z-50-KYwN^x^vyBH;j`+|qGhx19$0A*A8>s2-o2EuRl01zvA7fYomfcnarG;-W#rS! zu?>@)Io+Iu&9o;T_m#N@#zsSlC2hyYTXNqIMvwbFnevPYL5n^<*oRUkz_}yTx8_v9 zg68<5aUwd`2t6t=uL(7eprD}fGF%d*klKGQj~#c}@#df0umVmn9+r+^1}vMa_oZ@F z_#gRccRx6hrlPpOTMhL9=VIrw;P#^38?j^U%f5lZxy%h-3JlZJp1kJZo7gzF0!NDqwEV z$z`PHr95hoz<`keB~_16XFwKRz$&z4RUbzip($smuinse4tJ8bn{p9@qTlR z#oYcGM&660W#{601t*I4XD-RRN9*SHtMm_-I;1{H4xzu!wYuK=sARagWv%-9!cJ;% zuQ@ihfn zV=SKu0Z@^Dj@SBuQh9=5h3gI55z(c}bwqL~U(AtLe5B?Yha@Q}sc0fXOM7GI+Xr-R z@I*R3XHeEea=JXZG^!(##tq-SI)-->9h1;yRRihNf)hp6NK8uDyud+Ux~luZ*2Ask zWhsWma-?~HC){IInX&W<+8r>gSp$5Dr=oLAAsmbP_P?&rd$KktENJPP6qNZw$etag zbmjRD07NO%fZQSekf}FJt1(Q?BKQR+dgF95{y(Z=N&4 zb%%dgo250^YrwY6S6r%A=Xu)q@-y0L{LE_gu$?N4X!V%nzO~$>wqy9)w=z%=5VGUt z$bXrBt3)5m&?a6Y|1`R!YL%jB%)IVnm5C^em>}hwbM|9$t!L$h)xAR2fB*7*K{wVn zj=|~i+l-2-FUrCSVN~NX3uzTdxpP0DOY=_GXA3SHJLi&B?B^6N1*(^YuDU|WoD4Y4 zYpUK~*>CEOc3@VPeUHvQTgXQz?Jc8|p+fEv9Tknp`1xf3`P27kj}7oi_~Q^Hu77@$ zZUN`jV?|GDW@a!SudI{Y8mR|O9I_m@vN)DVFcp;cm9iS3_ui{#IB)}$5EPZze+-0m zAUK!Og@1c`UQ-ZRHk-C$i-^=i(5PB44?WQ&eO3DQE{Q@wWcU=qF^5UloF}R#=>&Ozhj#&9>TtR~fhZah17Sk8(gbtEUaFz)n}!IVLoX#H2>5%{igU$cDHRwV@mCC%Wb<43pc?nc z``4O1L(Q=_;si1XcIN#`9YZ&iZS=IG=ojyuJn3BMlUpe>8nyXmps zskuFId|ELsvplshM23lA)S^y?aP~*jU{yXoD)$qL*UhkGmiru;Op~l;Uqw;GyWEUU z8Kq>thD?dKUz?ft(fAn7vj%+4c`xpUASWfg>eGPM&Q$uH^2o`&d+l5km61CE*7l|n z%%}HKbl>0dR55uyVrx$#{h)>uNHRT-=Hx6_pxHUh7W*|yKcU3aM$cfBvmY1cyL%#X z$h(9~yl2*e+GXHQp!-~vkq7+DN%u-_m?CwPI!5ij!)_oER@pu?+R`q_?{b^`o~E*J zkqvY=+*EPsqXS;>lri33nb_Zmu}g32opS~draC$lpe@vA&+32p36dLrxZxMBzhXwOzMW53iUaT*a4S?+^hQ$hsh*D5D z4FUgme*1Bmw z2+m3hty9f1IqQLG8SI=7Wh%Qdc9p*n7?-E+FNnd=S>v_IuF7NTs{TJ;+(dXS> znB>SrwKj|fkdcyhSqtkAjec#$rB`zf6&=(uB&XCcaK{MD1)m)(MsbN%HI9=|-Eh#G zfYmsFtlU*S+d-n8F<{xiua@*GT|- z?14)>c0`%nUi}GZB>>x;l32lmYYDi%_T=aau)?2Fm)z_Z3q=9I{Uhc5=VwV>pU%QcNp|Lve}E@e4mbu8!fc#h3LSsO+oIXODpQ2 zRk&#N;6&0|S_itr#x8Wa8N=qZ*%A3mzdW34gV(^zE_sV)W?be_$d@eotxP+sR4rVw=Az2pt~(9-xO6ZIu6vhw^2QD4j+dDbB=A^0f8D6wo+R%m;80Kc z)w3PaovSY*|C;f6v+2dlmtTThpu22YP)w_}Li=(Ha7w_&v-3qzioFk`&S z(3lq}aPuxZ9?}MvM3Z`1PrhzN-hN?g3B8<2(oy1YbU5R>Wv+qflVt8wKNz_)MR3`y zHm_)DN0hp~A_gZq(n=kYw6IBCYtoq_ZoM}odCf)_q!iEd^WO`3{rVa|<^3z43cVov z(lT1*83l!u6X)64Z9|k8l zNVaz$EC-NRMrZN(&+!UB+Uk!O3k9qkxb>}lzZ7P_?@qcbynknko&G_0|4wq=Jyp6T zqu*izw#;)(yZ3`qAbp_m#Zp^v%(tcq507k2N%oeO8u$9l?dfSyxofcevTKvXYA3n< z!7~82XXYZ8GY+R z?NMZJ)#_G|?vd@>nCPnrM*`P8*CQ#ZYx{)@eQ&G`#+QwYmR!$kYrLb-)-J+N7_x51 zG&ef{aW4>K=H&nKWhNkhElo{7#m5+cPni{7B!?PTXLfO?nFjo?d-CVc5R9tqsvxD5 zPsJ)7JN-Cmf>)G))X-kiM%a^;u)4AfmGk2E0fUf*Jr;@Z@~XVAlY8Rb9gJLSON`z6M#tTKQS9xL4nn&J zZa2|}n5jKo(3|~R_P>2{g66+mjXJz3J6@OEP^&*;0WYs`-upvOzVLE|ROx@%cmEGj z=zq9-Z>NCKam=y#-?smU2cr0|1i=bvG6%z}6!V6B_CJ)IQhFuKf7`)1bY6B;;+O_}``|9+ZDX=A%C~ z*3CKZUgY@8ow?caz*7UoOp)~;0-|hi1bJlhC#Fz&j4_`(KXW1?WBN;9gw$VJqJiXE z-UJI3$$0hG61`l#!~4qY7XZQW=k1bLxXOUOss|9(=f7Zkvl2^06b)IQu;gvw_{-LL znSb?FN|`|(!6kVhcfljf`2AQs%y|<=A122C{!9Y`7ag8 zYd=vd(*w(*yinNO@JT7M7&3*F=PzHDRZ&!2c?b3W>-il(n(TRxc!Cta{_jYaDrcfl zGcGp3N6`cQYvQGagG!edettqB4Jr6j(Md=Wd8KbjAeYf#nYxFz0K8TwBjl{iBftcEh52^G6yLgN# zf0hUEt=A_vESrR?yZ<(Fp(R>VTYF&4J43r@C+FlKHHKCU}yAx z_;8+KT#L5Bgpcu8eERf{oSTC-H#6gZSN$FDlwhEW<}YE*dr<7pU^e`45lbYrhp~z2 znRA8_LqXY0@Ahp1(DCu#D7n%(c{S`_TY=;>gy(zxIl8IP%UU2d>kLXKV$fktSiAah z$FkS2Qgpz~4&*Ha7dDFm*Hk5a z_DkuRn6l*M><5fHTv8wST0;GP5GSx9tE?YVhn)LXtn)5TEuH;YlHk2 zED*6O6iONS5PlW0<%V1{7Gx%u%>2)So_B2aub)`yHQ1MAW8sP6&fmNZh_z{FF>Tl` z?$S1d$7n&Qi-Fx!PEM{Y58@O}Ovx(lD7BQC0+vR}kTp#G{0G|FHyA0zw6&uu0~;RV z%V=w}AF2O|W#6J;4B}x0!|mZYdYu)Kp((^jftqKX>#JijT&zlUFf z^E-^J1!~CJl>iELnPx>pk4uJKU{U~>e*o57EN13*BxzzhHs2uPk3`Z1WuE+K0(j(F zKo|yt=>yacEY@ehq!p^b=0O068M4058>4f)-@|xab2!Id70cUM>$yByrHrJ>+7|-M z$K39-XLyM)`YwCn6&HcTas5e)wY^6Qh6lXYrqX8Vr?IsqY8|OF`XJ)nnd#=`2TDw zr$;M9<3R5^WA8^x5!0godv4t*NZ6 ztYXC9&obSFviI{(7muumkJp^N{b$#y?`MwRpt~PFzbJS3*ox0uIH9}L;SpY8VIkQ$ zPTsjcGxYX0QbtDR`P6w)Q5`~vcX%}q3k-M)N>e^v4C{y>hjoB=jD*A!PROl!>Y)_) z=TmiCYY{;b9;pg&4s(wnF@mS8YE#y+3o+t(C^a^$MNs{DvzN=$>KE5q6A%}V0f{Tx zp>nK{e;Y^e;jKly4l(5U_a)vTk<<@%)hmWJQ zrmpr(O*+0(5&PTcX%7_n9SX=24c=||{N2&ha6RzmdDmRI{`}20?rJ_Wan|4USm)dF hDSh(c|9jwAJ*rT^`*KDsA`=h(Daj#Z^KKiz_&?TKVUYj; literal 88562 zcmd42^MB-B6E+x3FtP1SY&)6Q=EP1WwmP7V3iQ{@ z6WTcR>oc~KsD_iWt*Mi%frAN%nT@Tr37w;ngNccaqq(iqC3rVK2nZ2~q==x3Tjtri zt0x|Cx#xQG;$HbOet!ed3*j0GKd3(o@CC7@%nQ^-A-Y5H2mb~}+yFw{FvSFHFBMHPPp3EIeljFXquCiW|r6$EeGifnmR^JwXPg0 z8_`Q(BCd!Yh$I9}9Eg-pH|vk{!7KpeBEwrr6YjwU`3X9P1QWb7yKBD40bf4vKW_lE zVk6y-{kMH33mo|P@BV|D*eU+#$on)jeA+&cF-W8o0`U^TbNfNg?@w(0g77&45|k<^ z|D8f8v`JbK4Gn@80ava*%tn}}KBDuhmk>76>Npl_s{na<*iWLR6=!P}Z4&|#QoJWm zP%Cz6OLg)8;1hzLCW66;_)eEwdl2HB$^(BKXrXxhN`?g!Vvy*Q^r`i4BMhvkcfm1J zmaGt`K0NHh0R}!{1D26bh3Dq~;fDSOEiWmPtPwf zO65GZGThx!M1zo>J$avT#+RLqKKe~VLu0bOg6_6vQrST9-^q4F%#x>dqmH#Iv&i*)nm* z8qG>hJUJ9p)a%#x3a#d7dYv{TRMY^5qwW38&+iHr9iv+t9G_o!dv>pLf8z`f4;K~{ z)w*7OEeW+(zlP1_5Sl$B3ltm@q{e<%3{ACn>RzoX>AyQ_UQy!_>gg?D)10Eu$U5*H zPNpn;+@{~6QNQ_DhDsW{YS*;quVz$~1Z*}7G}HRl8^G=1Wc4E-85!A^K&G2*^<2(Z zfWQ=8AO&H!ysnMhEcL7iGV(NKxPLz7*-~vlo^Xg^BLRO^F!I2@mijqgT$3Qkz`%fh zqvr6$1f&oRRQcD07e5pDGFT#Lnx@wKr@?}n7S#CME<{{hX+Md4%Nr8c+aF_-vb-{r^pL-^@sZ51!iy?WYCWT* z^OU>+P^V9i-6`o1kuv2d@VT9OPVXJ|ZL5SAo!3n0Hvo@OYQx@`*w|q}Lor3Po!wn~ zPCO0TWCevn5c9?knmayG#jlMM5fRCygb6m#Z(g-q!fTPz5 zRUmes+rp`mBA3-(W9&|^E_Vj%Yj9+2t4DsS^nKbC!X7h4@nEq=WC+=^d^v+I5NSum zX|50k)F-wN1+%1R=(Rn&>Ku82>`{ z2Jt`%sRk)0S;oIJ2pmzEVhhn97+$n;%Rq4~95P#O#7ky*SX275%7i$O6MK%wnEuai zwB(kSB3GEhrWmou*ORpc$hCPEzXt@COPf(*e)g9?e01phhAv zA|f^Jx5;V@o92VWj=_2Arkdrs5RD$-`O@^QXQ7vL+E3CZ3+$?FMp$%&NTNdnhOV4n zTex)Y^5!i-pAym0!P`Uh2LgI76%mb9wYp^7v^^pOF#{Urx1CwA@1+KlT@xXBIhI6Qt)Cg7%-te3;P21*bE*OJUt^Hs0e)(TT z14Ckq?bec#*ypn|67urmvpDR(yOf6zTGnkIE>}0(_Br(Af=0GVER=$BF)BES%4C56 zR@wf-bWJinF)JBfj0gx-)oZ8@-nadrZ~l2dR}31rVco7@-sd-r6gxG>O+Vhom-9X| ztk-z1g~!SuGRDPJRGiB9Kc?XEb*c#@Y$;_$6vtBOFlbF#OzR;cfI=>%*wQa(^zC(T zuNbfx^$_*INU*nCLWbrExzL1|^szZJ-MyO&!H0<_TzStVS*J*J2n8PPzvb03MFF)L zli6MYz@TxYNJ`|$&O@ClTnxXsTU2=gt_ZNv?MW7$%l=Bjj=|rG+^;@vY2DT5!+$sI zIb{_iPY8!bAfD-5!&ZYGFGCht`ZC8OyF34$RZt7GT96q>aeW=N-*a5X6NiYohIV=! zP#(Dky$V5_#ad}u=u(FIjSJb_D~E{sAYQMJd=`_kvg>Uw#EyEVOZ$^)EX^AZ6td|{ zTQ@Xn#1_$@O6gUE$ZzIR(4nk!8fmmbY+o95r1qsjruAQTsAqw0mCNS}e{A!GUR|C$ zN+JX`XES0^y|ZMx%%k-&Uz+OHcS!178^Q|1N2$k;7o3^r*Q??6*>meWTj!bulL`K{SlbeB!7YN+LY`=ztSE)xg zbAk(Fav4jqj|y&XNqs{>-0(#(r9oDl{xQH^vt@ePCYy+WFX*(x7~J$4meyK);~5X> z(QE)0rNZd{s?o}x@zaLgwtL&m%@AQUH!k^o3m4oRU<0;Z(-Mz?HkBx zbkv7XN)*(BnnfN?q6P*CGJ+0JcyRQD<;Zz#S%-X7SeUYW5XgJ*g5P z3z+Rs2m16r7qdkUxVVgzgJW%VKeHU|J^2NGmkz^Uw3bp46G!Lo?9_}3Jg)t2wE0|j zWht`;G~B?$!Sz!T+DzXS!mweI!xNbN)wcHU4F}3OtB6`B6~eG*;MXsYJ)Eyf%F9#m zdxPZOZn|GOF1N&Cp&`wcJt9E_v@0fw@ar{XZsRi;m=94UCML$Fq=n5(fqH8;OV|)5ety5W{uLIZ!QQDiJe|D5(tuC}I4~adT6*<UgX{yb9n$P2< zhN&3_SbIJ%&=}(1`kn9>AIqtmnQ)Le5oVol1oWgX^Qe$H%>CTi z0R~uKmwU~Zn`LNdsGx$LFg$nUSVU!UO6)&%Sz*YRUoh>AU%fcHriz*m3a9NJl!~I1 z=DGS=yBo)`q`^YJ$QnQKP8y@ThFz{ECfEflZYVGJc^=@@ATV?X1Zvz(vB&7FVbkE~ zHWO+&Aj-Ax@V_*@LdEzrVJ-p7=368ANQ0h``;Xt&)$x9cDg#(7YQTY4bte0eF!0gZ z;WA+bLp7jxp@F`OK{Q81U8B(S=iFfZ&^vUnxne)2U3Z*$u_(8~2odtcSh_6zs+%J4=T&#byqL|cz#>W~pJ`?tWr+Cd32+z_^Mr!UIR74o!N8p4o2%5`}? zDt{0D@<_g^njywj;odR6PQTCVU79f89;GVsw%H*=!o);ji+<*whGrLDm`*vLcv4rL zLduzm6xceDlHeb;5x6|xzju^X<5#{ZFhdmFY|1LenM^Rksi?$@%YX>MSLajib0Aun zgDR|p2!pTpOtHf0^iRP}Iiu-_PdnYsklf{KNA}@46reIjwgvPYeBH)@+3bMMFBeng zbQ@>(+)#=yXiS4UWw_*y6zK{KvWzSodvtM%&49O{T~~AC6yxQ_jrUuCpo(a(Mg1!*xnpYlFZk!Ub0e-!=G4{EHDez@bh^5^sbmnv>g^U{uts$S5yS zm9X0{+KTuLEk_~$$A z)EyHl(fOFjP?yRDftRiy87MP)YH2jkruc&T(MfA0@x(hS%^XcH4>MS#90&?x9qjIE&=a{j%@`H@)z($ zhf0tzvTPl=y1h319m&q>S$T9fHo$Dt5W3fHDuy0cuo%?M3;Ct$ZP%Eib8SoJ4{9V2 zO4^ewueTo{3z}b9M;?bHl=)}OdE7@)HOSc}NEv?~Jan8uz-O3tZ(CVhUlBc9vo7*= z1<1sh?{Tqk&d*Y&xpA_f=&h)TTw5Emn922i_x)b2sc>W`v#6-ZAO#Wrl9*fbIYb$0 zzFD?k1ujh)4T3aR6#+sKt&x|Axko`EF+AftyfCccH!m!HLXVyhMKYwmL-FeuMo;gz zs=@JTi^$MW3EJc-_t^M&Ax5kLi$=9mD(?2ranIJ`K%iDL2$JO`aQWS)90QIj+<)?r zl#LDjD^yxNTf#@s^eg+R+u_f$Arsc&MP4s zpx1g_dlWz53(n=*!yU)<1=^TGpBY@&b$+vy9sgB7N_qFGPo&b8Pf3Y|rO9}As{#R0 zGyk_DSEEv63_T2wm6u+iZC<8kWgqK>OX`n)WcagRu%F_GqtGW}tlkzeJAe8Hrq1Ls zuC8g`P*}aG+5a=og_fs>4lMF(1usn;ZM*&)<w0QC#nig0>&L|qyOY- zE(MJ8k4A3ZAa|jc!F$ z!U7gT$~wnLO%3zqddtYb5yX_N#MZ(>uJ@bK1=uTTM;t3xVhDbCNYI^AVhhSU}M|Q3G{30Yt>R*LGG>OAzJ+h7-Ozw_F z?Ai#EW>GjBE+nH)MMoX!m3erEe_4HDa>*U(%R$`cHNojmP?@S*_UXaLfeG;g+!`jE zBAPg85_;c;R4vIuUPMJ`cuNR)!q0{JSUk+&O&h6@nleWc;R<+$t_oCiWyPCQ(3ns4 zFT~iFENO6;`z9)9C3Q*!BjY@8!DH!%73oxjc$G&mp+QOhW>Q^{yQ%v~;20Lz#h|~K z53#&xP4tTKkxaG_HYE9S^sndATviR4YR8Hkw5;wkdP73sL}+*e)i9*;r&g!j-Ts1r zrSX(mH@C?EPgobzES3zJ6crRiJUn)77GNX7?Kvw9Qo3EvU${r%AQ3N5r$+=btD26( znrZ6BzRq}e3)Z@_RPGT{IKcoTD;%9~-m|cu!W1D)Fc?3a59LoKFu_D$$PXeZB8}(f z9Yn)#k>rcjd4`5YOldMny+XkJis|X;uN!Ua@%1~TaA|Kf?js(O$4hw`^;aysl(UR#VIb#7TdTMN9YaEx0J^9E^2`d>%uaI9#B!|8^kWuG8Q0X_y40b2RZIQC z^KY+BYO8WIU$<2y|L@PsAf|8V5Fw=(KtVcF7Tq^vW8;}FFE_zyzcFxMKd!MG@n}3% z+g2b@6rR3YGh!W%PXxWcLYs#J`L95z?Pl?mU28b9`@W7+QW%;)jucS+r||b+|NQVR z2`a>JC!g8VMFnK!Jhsp#t7V}W0zL)Rq`ErhH5-n&xH!%%x%~Wm#*Fd2ZNGg!#$J7- zFQP_cuHwt1{vSDhi z76=+GJBt*s1Dh^JRBys`;C=SUqE4R!v@L@!r+Y-;X2M|PPnVNiILXRR0jn7ygrMa! z!0YVgA?K~Y80RqhRKCOww>MvAp(+))C{R`KXRxNhfB*IuS1YE`E*cmB^LkXyC+c=n zvnx3^e7p?r*$3RJX)2a;9-|Cg&64ua&jN_^!qj-?N@dCKndXDXnQ-m3f?SjwcPei> z;HxrqH>Gu_r1o*?6+d^2wMvte>XtNP1uh;4Ry2QVfL3*ITMad{yVCnZwk5ipgKv^z z)wA(U|H~>`r!kw{1ajNXvg!<}9&C?baU61u;;3xN#3VezkcNgkB~SL2YKcCVQVoAe ziS5r@em+TE9YUkW^T$u5a$saxnL#zC%4|`JUjNZ_LGRSC&z9%Q+8d8Hu34~uUif#> zebBgk+}xnL;k@IJfczTByG%Uj*r?zCPnnyl)fKYd88|jI#**!OIGys;WJw zm8T|L8YpVB$U;`va>PhRkv*-mR;b;IqRRds!~vQS0VhhRm_>0Z2c@i4oM1sYIRqfE zXh`%~EULQr4;FLCBzQPF@Vr;$q!a}SI|LARcatkojg5A!P`OBnhJ?+}5?mC9Zgu4y z2u57~Ma`6^%iPEOjR_6yIfWlLvOIpp(o-lQGV`IUb^%_h?5=S!u zho>^KcLqN|VPHq6@a%txY5t_q2gZ-Lz_%iNMc;U(7ZsrUwB)GdC0Rh-q#_#{Qhe{h zCG7?d2nD#MyyYh#<}i0Pgs@_0WKD>N8jhQ=wqn;8%Q_Q}l3zd)78?1*C4#_s=OCtx zik6N><^MDe+ZmGJ!h7_>s^ba=Vsy@q10wInXg#z`uh6IN+RLoN6j8r$c*YtOBepe z@%!ofMfDc{PLd?2{%~nuw_0lnJNk!qlR|K85SMmAMbRlSDC9`C%X^?T&F!TNwXOZ(YiB~SYEl{;ZcI%={Lw1f9 z?RCbbvrwB_!CTV=DP3KDclYPB<%DUgeQMuX#Y3DvI=Myo4Ufa9noY-!Ilcd;eejAi znSN)k6|)Hu{V`z5V9b#DcKYj#4KFXxkULn>b%7heSFNgmMwU1{Ubv$!L+3$AfCGkU zu7W6KjrJAiHR7>7`~Q>Biki;l63NFve?~L!Y9%hS5K=wuzP2^@zZ^lT4D#tNv*@$2 z{n!$nQ2y^)>}m1XMl=5e*u#;0c@6&=K_tQ=FK~<^yG%Lc|CXZ4u&f#y9_)cGx}g6* zskMhcl{%pRbP6=5n-QMV5PQ~S7291XME)oH)xiHNhRnt}WAE!dDO&)qgv(#vA|J zKq#|eY&qmcs~zE=DWRvSjSbZP0W~05J`O1&B|^;ya3Z4WoBKcA8x*bCiI|&KTu?GzWiY_qJJ}vfko#~BHtx{DE(BEt7$AJ`4^4Ris#S5rOG8Vh=i2` zSJ8jQp6*+*N^XS_D-|3XK6IEq_6k)S7XhYGF6YE(&5VElM*`9L<4__`2zBnwQkS#j zEY>3;6X{r^#fXP>%3@RhYty&3f^JUT_)aGC`ReSXeNW)J{t0f$d_#5J;8OWNz=B_Z zWsdQWb1!yJ7dOl{Fe)3%N&m$syy8g?wyr9M?Ski0^#6oyymuHcj#oOouuv8L&j#m5 z{k{c_(@m5YR3PIb>pH)T{P%m&#&;|d4H55JA#I_x?=>v{2sku(&Lx4G5+h%kX9h5* zB1U1cXM9-D?2`3AU{V?Ip=02-2fMD-xn4ECber!<;-9VxVE;SK8i^O{?YNaXVS@@J ztJCcPSv%mboEjwMHUDH^iTf5!!T)U=XihyWO!UvdoWCW1{(Bn!KYTPR)`0zAEx`XD z@ch3pKqq8nr||c76JSYp%&a(nDJKYv4ug9*2LYCF-P&@4&H~no^SNNkH)+d7kxb(~ zBVp)iJ1MjPTQ|Svi*wP9HaMxsg{oMjq42Oj1Dt0lcOinkY5GGe)I&16QwNo)CA`Bt z@>!zdtvNPYi4u@{ue5rTFUf@6ZV4fip-H$Z)A|3|+~#vF9J1uSSsUY{!dVF^>4ClR z0^?1gg|0`ic89x#s`aq(%(3}Tz6i$|gl2C)(TJ9P`F1-F6D|8pCCQ8zG(!H32vwgC zaK!gS(J!r=TPU?(b6F_W#;|{CyE3ah>=T8u!$0o~PvXyNcl2sDei2z_g!00p9oa5m z(@Nd7Fd{rX0A2gMx71GXe!}?My!D%W;~uq0cDZR9O^G3WA_Wn#|9w0l^Kowm(nl^$ zzBK{cHftyts@U!`^Jo41(NjsR-GxYYHwXMB;%&=iHPls;5I)I^PQT@6NAh2W%k8%A z>Virg7`9UU*V_R=z;)*f*`()wG0?RnXvO9<6p(uTl7{E`>8|@`L)!jus94Oli-@#C zIaaCTSImUU@m!thXrc~t*f}|KiIBgn6ndJwOKq{*8;gQ1nWfdpi%#%U6G)FA*hcxN`~VIQaLQ z5=p?``Gmpqkmb|MqLSv#Lyc@Eq6gI`ypYOV;Qv$@_N+ffuuRy(4>OOM3E#m2a$ z7T?pjOWOdJF(7)@t1_l3WRjpYBZwVB2EXNM(v_u_tH~P7R+k(KmRq@_TLbJ9lIaRF zFAZGO~A-7DX^VQ*%C2RR=>T@aEF_WL1>CM>sF9~eV zUP(@Kw%$+9Rd%HePE)$AR6HoMY^r9x$*H=Wmfd-DXT+rXLB|V6GY10|@|;g2ejaUh z?x@rLphv{rsix<*_*DnY->aO2{%4buW&*w_dx^mt?ljYx%VY>z9~jx%8NGQ;AIoMf ze>K{};Xo21knbU!x2;y6U=;60rL`Rnq=$0PP}Ps~_uuTNZ`7)S20s`+2L&j)C*NJ) zgBg;$COmvb-<=!{p{zt-sdyCgk1)*JK|P-#5~5l4L!M|?Rwor8R3@Q-a&rAc&(M1fylfb_)K*<|CY)8NK30J$KEOi zMiplL+N9E;W8EI|2Q;zav~}xv^lzFs`YKA5>C_Tap|e~-KK$9s4g;-LZO$M17yGNvca>Sj ziK@vGiy4!LVdeQuNj_$4dR1k3Ldp-fyfy$20a^E@qCqm2XV-UZE=oTHNf@$M*kx{8 zR6TF+*fk3Xj>4{?05Lokpww6DsC6%swavvomyp_cD8AD3UNz z1KIAi>Qf24=?)04`>oo|ASU@lml;vX1FPd!gkIW*9J@_{=i5^^p!T%u^11O=pFTZZ zE=DCFv+@_Mrwb*Z zq_D|QBxlmc0dJQ^Tl>ehaIl}B|GZ|S;_UStrx}hjo&~nJEe>mD%Ev}RR*Y944Q#_z zv@j`UG9~%L$prA&-pL{TyT|$&4ZZPp6`v>Iua40~pp=xRy3I*kpRWc5yAQTj{p0mQ zv5ZSLh_dwDlc2WysA?u#1Lk^yD$K*V@%?1Z)u8w&r^Y#mmO00T#YKw;udqjRzivZJ zR7Cgo{9>2csr_Q5t-*|*_ZF}oPi1Y=f@gDggl5Ck(i*X{@;K9d)cVb3hp0_7xdt## zyOHf`Tz`V~1o*gJunkRnW=!F4s^JY)mFza=&Ur7zEYE54uiv;#nXaWELjN&*MTxpnx% zLc9s(d)MeZZ@rlOo_-AKx=_a;M(+o$s@1i^^=xf%bYIP(upDf>x127hxO8&GRC5HM z98OYM&EUaFr|7P6d2t5NJ=4BffZsY26))8{y4QVLWOurv0&wS%0`14PIua0VjM{Cyae5LRxbw4ee!M61-OvEVCVVx((!Yrs1Gdf9I|AGWn~j!;Sjv;}x-=47oPyyM{yvig@b&h;F0^Vg9?!=gtvhO25GfP?wLr`zkZ*VFDR)AN2uru~CWEfHV7nO{(?`<;lxt0*6- zH+3~<>!ghvYtBXc>dlguUDss1gz#$31M@pL#dS|kNNoO*)t|ajvv&aRbxvFdVBLC+ z+lXzFoZ)vuZ z*w`Qe)u=euJ9%NMJC`BD?aGY`Yiqc$#y8HGBbB#KhT1~M5W$sGwYPX=Os+c{fvX!m z=x}P~y`es>7C7C7U*A+y+y2KF%%vS3;s+7Re^uqBD<2f?gc0a7Poqc`iDxjxXUSCkdE2v{ zQBlotEf679Gcq0>zmiq%of7%F%Ef!{fMEl!&k&*h?7pM--uiS_Y4yAo1nO1~yOa%Y zYN_szm3TI8vif3*C0{PWyd9kf7dE!t%`veZ;)QNvv%lIrmfG1nf|zS}0^o%{OigQh z*e;7OvL8?Fub)Ls7MR}bA2|!_u-p7e&u7Fj>vI*@X$#M1niqv$*Y%dfAwbuAgfi}~ zPPY9r$-D8q5U@hw7r}5LBoz*pCOZ7mglgDS9jA)!Nc@?rh)O+jzl-M zyQLe~>!D5o871G_w)fK{Msh9`;jS)`*-=n1NlcrVoE}D%q3Fn5{tg^stmTp-{1R`n zkwN$Gd?Ot0cUqk>!jGBM-6_VQ6C5SlYZ-~got0+45{^BBt^?Qe1AJ97h5cwX`=-N% zuIm<;>aA$D=fI2Ay40-Q!}~Cu)-Gj8*K@c}URaFF{p+A*jd^o=?CFSYvUWFPN5Mbz zf{0q0Bq@>UgF%REBQ1a6-_FYbPm>$rq}LFaT^2(-_i7g@>Fh}WTWpzQ%*PuapI^WD zCmq=ZDk%5+5vz8RZ@cv8hZ?_uR}SXd9ai+Dq+045g8^6@NyWl6qRv$S_pVJk1ySr@%nA&t|XC>uF^B3)vvX=3j<#hm18{>R0yL zW@_u65xAu8uBm`h%pWY5Ny6WttG#&CtPR|F_H%c)4DQaCvIUhHOl0(-s;fu%qWKXA zh6}&$hD4LtPAc#*ZXGcF#IvofVv~EPFv~rL*Jy25V39QvHbL9)+xC#vnkv7JKj5LB-VwwMQLgW!6;{5$Vun%5 z0!j>#Rq^eX{NSwI+tJ)DU_LvY5b>Um9ZgBBcvjb$$Ijk>rr4sSsMoJ3Z9bSdsd8x&Ll0B#IKfw$ z>c<&?OdUfyDv8i!y#7K$L!SO8TE`EidrbJFZ z!94DF(Np64K$Nj_&gpVbgwKB3BaCXVx3W@U9LgbXyEJ^2owM&H`oZk7F?P@|?Cpiv z3~Gg@vqw7yFa7=e==SVt_TWglxK}HU5PJJ#57%^YD6m;kntVrIbF1#X?nAc-cN%iL zTTxJgC2)~?%<-Uta(C|ecD_LUS`k-Ifr`lnPSVpJ#F6t7YJaKSo0D@Yc!w4A3$b%R z-r4l$UEN3JD%ZO;M8Em+koOZhhQbINoFF);iI?A$^A6YzuOULQkN` z-s}?szOQ#q!S^h+293ceX@fP&GI(#i0XR%7CPg-I38*FTC;Wc2a4iOKm@bot=bsrv z{?r^EwdbBkVzKX*bxSIcQra!|drAb#P>GHKJ znjf6jsfiL**{nh3mQ>VRjav#^EjV-wKZ~QYj*VxQ8m%7{!I+W;=0wJdC)TbuA=VT| zG5p>rGpSov)<_L_exL6s1Y|6^)@&y|TED~|@@M>QUyST&K5{Rfu{EA=!AQ@;SIQLOM!!Q*->qh8Tl2FHNWkY^;Nc7iawOa^cdKG#+Sy=9Nya-*eM>ED7rneSIFy1b?I9 zjz&H{=WKTb*x<~^Nh4V8Axg89rh;uRI>Ge=7e^Ovh3Tx@k*^e%)DYtgVWO@DG+l`y zy{id@S4T^HJc6qc@mf1CMo7BHA%uPN^UGi;$z@DW+$9tO>Y+-aWPq0shPa*>X3j5R zh%w9z)s3tTUY2OU;XeQ|CSv*+dBR_l(VA_<>2GNk*5V;(5RV5k4!D5m-?$nNJ&eh0 zJC(%^j%jw6{Z3BrA^ zeOz>RrsUupv^O!GYQM-*`}XwbveqCrXJ1S`>j&99mWoXT5f#DPs!m{cv2B0MuX+>1 zMlQ)vMQEExg|(&R{w!c!k+)dP#m^KU3?x2!_|i3e5*| z(;8{DD@0rKa#w>gB{e^C(#W32*4mQt0>rL`nhCVConliQgcjfO;+c7WAR8psR$e`I zY+KXP(If=4g(0kS8xl8X>yl(u@V(m6kcQ1MFEzAsEjHSk1c$8gVg^&iC4NI+gXi2$ zf(~B4AyTKP-f6|{GP#P7cU;Y3Fc`iRFj%yIWdbxjR5Gb3F<^~5m!vIS?T~mcscIR| z-?{vqaYX{h_hzqJnJFBrh{`M0?I{%C7ZU%3J9vA~G3F3Y(Ud)aR#HN{12;i4N78+D zsaWY7RJL-lf=O&0iOzT)-q~}&i<+`ELZkU!x^TkL(v~CqOBW6pIPda=zIg(thWQKI zMO1!8s|?+rbVTWtq4x^%+JJ<5VU3n!+T*}m?VMeLvz~Bwwk+k((wIh>XF1b7M3z2M zOW8W7OVka(O3i)dkFJJ>l$q$ivRf?$FQ zXY6O<@4%pXYe$$m_jxhi;_OVWQZJKm*F@--=rj2>WM{=t9DCFlo$SBqJ|9`tO>ZNc zdg(EMt4p`LvMLP=8wB61kHya{t_Yu<@*z2jqOk7fnyS1nJ81XI<8C*3vjkbACbPMV zrji9*YnER*D>BmshpKAdv%987)V3z+DJ-)6P~M4|&SZFWvaY1YJwN=YA=DxVHjcEN ziFQN(GrZzv>mI#EILC%g@_2!JA>?+KolN z3Bkm3GjG4y+(ocW;UhccFvgzRTE&>G<7~LmwA+8#1YXl{Qoq`iqW*X~+7)DxFv){E zSS5RgrN4D>;A~7$EX9KfueTTrrnvjn%>_o?2yiFny*qX@zpZ?#I%`R%f33Pb?sx9Z zxw+v?v=;0i7v`)+5PKON#hw{_(BLj>YL9*#L!40`{cTm3%M+jA^0{o9YogV z{ToT}o7mO+$SaBu0UmkLfXFsyAtY>CE#HcLCo>N2Vk$}tK!I|+GK8Dqh-d~oyMA^h z#+avNR$R2Ch=y8fFPS-Aj_8Av8A-zuaW2k#w?Nc}uRLRPYz|h>mmY_|Q-g+1*lO3l zW2_S6~P)MFX6}? zcDa;dKU=luH)Ly~@jz>OPW=h`A=7l=vNtBpu)Px}Ng1Bt;w5G`(dc@62VOw=?v9Zm zZJB&2cFdf1OYu-r4?Y&ghs9w+lx(fVX*XadiM7fe8dR7Ke!NH)b?0)9&FM`Nkngx$ zSCII(K_$M@1dkf#p2LmB(Jq=Nfyyj0pT?%)??hzli_HZW%XjI(toM3; zJRfPKC1u3}d{WEBu+ur3sFDTi`jAtxnF=<*6~eAq49N^$K|$L)cqO6bt-1gkTWhTD z(qU=^IFo1jte!$y0W3mO%gl)S9WufNx!$99Ng%Z0lVkM4;65Jv(B?-H!gABqJ;Py- zd8*r$(FjP-j?c}9m$*!u=y5{Tz;nV|k2$qDmlB$g+jw4;s~ee&_)=rB#-O1;=a7t# z{1IA{(T4ltdbParLL}2SKZq=hf9|bj0E5BFm0o|aTs2%|daD82O0dsz>it~xmIfB$ zPgdJqFU_XYL9V@Ku=rbf3PJ{V?hfj&vKq%W&-j>yvky#H!$}gWjwAU?pmqnz5)7~7 z8`(lC)1q+okjJu=lV-)v2ZO;k0bYEC2MHU{y+cjB?V zO*sZ5rSo(O1sBra&-e}NL^xN2^8|-zB^KiOtQ!EL^|5V$e-5#;WL5Qh{Q_kwqqzWt?GJVVT% zjeLA8bDk*F4ZW1rnt-W(Zr~MV&sDX9XS)AQGFo2WYUm*|4MozpCNPKH`Y^=Phpt}@ z9$_Cd-ZsWad&r5rWN3C8?9~ymw(($Gj1x3&yDy73`Zi?1Xpe5U`;uY%C+Z~<__Af{ z^)36IOD8t>g;Q}BQz}BV(t_7Dpy_)2 z)b@HlcqP8$+puPP^jaXO46hl?EIC}UbmGJYdUwrnDBEt|auev>w+XKU{NhdKsM|@N z0AecgxuXji&PTin0b3YUE6ZoQUOxpb)G>qoiu;zYGX9&f(oBvj*yyn|-p?8Gwj2St z$IKgFiLmVk0shH#a2EI5uH&&%=)ovv7b_>0r@q@%cFLZ<%f&eL`^WqgC5wx`tqV2g zuFufNgUia>6{}drs=S&N4q$JKc!~Ytr?c7Hvd&RbVXg>}#Yu`|FlRkVxgzB0x8iTg z3}Yn<`nE2h9y?`wkKOF+VEPPV*Fgs8AD zS9A;iI_2w&V6qjZRWeCZsMeA0bC{zt1II-OMASfiqpq5Q|)vBb+F zKQ4#JBjaNigB!qP)dDLz#EXaE#7?GE&O`pZ_gERg)D{tvL#PW}Nq6Kr+1cnFdi;!D zp4b^IsYj<9nh7EHqC=Z_&%|ylWZ;jZlEl0^hU&1@@kcGZb5Xn2$J=aD+eH0^`>I2!2-md@ zvC-Cy>3+&g$@SYTAU#wnvVViRnR(SHwyrz_@G^TgMAEkL!%=rf`73ot;DvQcS zmoRdnST8-}O&gnw&cnwLqOh%vSm=X$-s}?DgY2=iG{A)C!PwYxeNs1j)m|qBZg@r$ z5ws8;(&7SlQBT?8VaL@oeKz4Er)!6+6ukY}Jq9?2)hWrKJ$N!bO1%<66z8YG zXd>u47Z&fH_MEJDl6ThAP7h`Ay$V0lP^Z9!-fAf}1U>lXL4vwA zCzB84txJ&-#cLsU<8GyO2sA98!~16G!5ULe*AN|jSCw6w)HAIW9bU}_SWWPrqcF?BupWIS8ILKq?00x$izt4s~~0y2As-ozXlWKj-Jv?y~xd)ZQiwSt{uO;U|UI|*$-wBcc$JZP+6Tk`qI9;v2P#h z`jWK$iC>16whq<3D&e1Zo*Pn3o<29z$;6&TvnItpi5WS6e}sO`qu}A00#d@Oaj~*@G(ffp;KId2hzW^DGw(u#o3LtbuD>Wi9pg##sF0 z6>)wtp7K@Z?n0RO+vatFggs#_D~{d@oEg%X%H2%1J?bikmfqeCT1R0p`S}|fyBX5^ zSM}|SVmwa#(CCe>n~PS&e8tgf`bwX?6qeQ>7aFbliD}&}c^c1l15N~I8-(TtZr=5N z2k+z7^BVKs=QtQWzW{|XCfE}X*nei0v@>TDdV|rA3d=uP{y>Lv?qs zNpQ<#Hc;AwfzIkbYjwZQu@-IWx7JDSoFMuqJmiiUP}6hRJysT7pD-{EPp9Zz=|%`X za;F56g8SmFOMVx0qajQnWnc z`ngwy4kcDPqNU7G%nH|W3`3((Zso$_X~#-f%OI3&)(T)#rXr`5L*Nrt zVmM!jH2vJ^55y^67qbq7BMA8n)aiB@F~do(jGTKLF2g|ppwZ)|tPQl-BYK~ObdlC| zxp!e7(8(Jq@xfgH&bqf|NJ_WPt*B2f5?fmZHeQipv*}eo`nvR*gi@!1cqY$IR^S?x z$m163axHfh1Lu!c-lYJ*2MoYv)NE1*DioTdveQt$5lg>^o-ka!apz%VQm|@$>d;;V z^A87Mkhqj2L(av%SNlylsF-)g(UX)A8mkrm){)ya-`Y=}baXz=2wwuvv)=AE4Ao9P zMo)+pn@HxFi4?j+`pv7vp%EPc06-s;B=VZjMuVem*6>$IkcvV$Q;6Wu27q68mpNQU zS+b~YMCfC^I#9}tA;RM;8k-Z#T-@N@<7WD;bRz`ykjJ?5Tv}Y`nX@vr*e|Q4z@9vl zT8?f%@;zmn(qbORA!h&b)QNs6NFpj%kJH!i&IJMV07@BJwVrkyNdBcNV_Q}H2oHBg zi{I|)MA$pXv5EfgNl!}-2SYUq$?eqsa55-4HphMABOt|a9>cj6O^|_=u}}!XBmR>U zT-6Dav93)*Bw~0zDJkR#!GA=7i6}AnPqFl|EO7=2J47^wzEbhGHjRAA=LAoQr#!{R z?2EEF)7Uq{HrA|`*5Nj$lvEnE=PiXHQhZmHfit}LR{Gv`#OKdb`t^@MsmM+U-U{nGEHluss1yFx-|+Wx9-swQz)UzRMX8ibtaQ$31>zp2>Xs zr<2&k!dOc8Ym4y(?e>(dooZZE6TcL4``t<0`x2(LX{)X zc-9d~Ng;q?mFrW%45!q$3!h<1Wbxuh~Fsn_Ty zBPl?k+LnaY+o9ECVprQKokI8GR8A73HHNWklLxI>gMnQRKRx(sK3Mbq$hx`3ue-{; zl-0h(<}6bBld!w^ujlI{m&Z}d*S~`(N|gMo)O$}aDbZdJ(LOv*Sg{H__-QZjH#KV0 z>N%1B&}6N9V26UinBvLR{Sjpt+UO#z*udOlJyye^Z5v-Q>A$q|#RG2;la#Eq4&4he{kR-ZH6>9~VH6XFTDCYXN!tnO ziE6HRr07(L#Ha?Nems+OYU_Bv!t>v4SPUaWaTHPl@);3FmMqH+w%LbcUkrDjgy6Mu z7!*}P$$P==!+>*x?Rl`?AQcp*-{j9T8sIOHJBDKFo z6k_}Vd+;vN7pcKXl-}ApqbfH%qGg6$=rn406}6*TLQmbsu~1e~`#`JnB_&t$VF9*E zIS7SpTtNp$*@1zBC?0t=a#j?AgSwS-P{0S;(pr4+QsdC(`xtt1pWHB8K zQnW**uidDXLPG#k@5(YTe}stpLYfqRW_sppKrM(&1y-NJLjLrAvMvo@h~==YMVNm_ z8Zx964Z6~uth%UhV|Yj&F$|KdJZ<0%uxXW;yV~M%ef13sKv4?2tqLJ1Hn>eo=Mg;V z3;FS6u|XdvAJNFUlds44NEG98sm3s%K#>;1ZkhejX(}7ZnBa->kFx-?!3|WKKl`H$ zI22x{5E3sI?B62|eG&rvX}YQpmqfIfD0Q&L!G=g2++eTR`-HBcCF^{A)?-S@gWXX zQbP0{9+<#^cplB&Mn9U@gF`>ujo6iUKfr%EU=7w&>(tB4IxwWCe}aYvanLqzHtH@mza`Nc zj;`xQcD!KDII9|%uCK!B*fSqZYEE-LwpuAmPS7O)UkFv)Iq(J?6XB3+0*_<#!s7yL z4`7qa@e4#Fq~-P1_3m{NQU+_}3BRVb?WvUnh^nTdZRE}zPY>gfBd`DNWAYxd_rY}pC9;YQunI2xrw_`t^&o3&CCMG5+bawW@L>&`9vR+sSi*#ZLDU!DU zl%ysudo#+BUmUN`y}T?=iw?RtIdr#T0$7kO z1Q{7es^e_@CNR4xfiaoIE)r79?&-!_IqgMlw$VV{$a)~u+j=K>$Zaii%$ikq(hJY? zdH-eLV-L_JH$hjK+KvF#3VRu%up`!pJJ~`C625;N?58-MKsi8w_qX^I66fS>13Yme z!tokVt-ssaIQFe}LP%e{?pmfyCiH)mJlCqGqsc9<7I&8333Tu3SvI-a+lr%i=2q%$ z452M^Ya|#R{BEJ3U`btmZM{3NpZKVXOmckz7{}x!I|*$mugyd)YQ3%Bg3-w_Q?}Hc`32Agg|G%2!jcz)H8e_FDHlTomdTO48f045<)9UvX9W z6!*&>0$kTcIy}(z)#jkXq+6O{&qvPWY1N3f4p;FK$Eo>c9Oi>Xa^2KZ{+`&lc2w}K zVZV%O&^zBZW*QCN4U)+f?a&^0IeI#^&6vq-@7{rw!9a?vyvH_`+3W$297?o)PuC20 zZ8CN^I|wYx=d)P&(q+sc2tlKBcRhP=@lU!+pj9^N2Iq=id18%SUD5j&-1gA95Q;2kB9nvYi*4zu zqIT0cSz(jkAqr(Ld>|BjE;G~8Cjm19PT~k9J7OsnGtWj$L+ty$F{Ah z?w;&m_VE*8u=p_KD7?^L3UF~@A zE$pEZx;`G1WH-7b`^9v?`Mi75ce?oOm(G;nn?2}<3)t(dy0mT|uA*(>@I9{>+U?kZ zDAP=`r6R>-x<8;tP>_EM>*l%luEm@n6Sg?$*~>Gz7RLP%PCX&IUYE#yu!Ud3hidH2 zg8aFxG_L9vZ;6dGlu6ojpexL^0Q-=m2h5+Hp$=2j{%1H0;a4=;Rb>!v)wVe2|z!$QBeY2May1 zL7%MWTW?hBObp@_#Ak26l#g0gZZMbGUOxWXM#AhaRN%o(4^c+OCsOt|@jZD68Rgye zQTVOQO|F?L`b`>!kk?+o%^o;`#WO5Hi4UO+m(dV__y=IAg8ehu(4VGmBv>Bj-|x!-+6krN#D)%wY$48yAH~%>u?=5O_|sP%iA=Y3 z!JKZo3-<{$IzKX{c3KuBLFS^}9(5Qn8wgExy}_Pthvw8p)=LBg*O8deVwK@Sg`w5% zzm1W=9G2wB%x;Q>^bV$J3xM6uPk-DqaF4C}ji{VS#r2$U>mhMA4Emor?$FODN8T4;;u{ewtPOY<)rk#TI7VyYXne==8X1ia@>WPG&(&y7iujx|!TJ zI`7y^(}2m7f;iGtqXdgY?`x)?+D+>4vx3*>jsc#lgCz4O1V1y;>tTCcC#98jQ>eO1 z@0Rs%!-W&Kx{ei3CEU?+p8cFj^tD~x7!l(M6uAYc*uJ^)^+ysd$=`9JNfWpVfppDW|W`Da33+W-)Oj8;h0;Y;go`&hcTD?hV*fjBM%iii1M)29YyqCm2fuU%IHegNBPpE+k z7Gq7opdJ&*jMQ?Pd_?Z_nZaCnu-Df&JxF2p~i- z*!LF~Vl4MJ^#e?2UvbkTNZ&ID2pL*}c!-;)ikv0Q=8eBgB=Yvupo>6Al@)1GwM0MH znFRlkMVVE*(v|zPb2&znH)f{M@ng6@UKEl#b}*etN85LcL4+_rJ&_Fn#?SSapKaq1 z4{3(}3G~Ah%q_V?gy8(_Y6%l5T*O*jS~l^Q>Dqr|?za%|VJjs@*Fc=_P22h1$kaf` zpGQH0PaD)XQ)4MbTUmP+|CKb!Sn}RBS4#j&azRM2koekD5JB$Y(kGO@gLk#(*6UF7 zOu#JfV{90{EO;JB!SXNanak0SygX|@qoInTNw$Dw}MK( zlKA_o!+d$5{vqoUZhY}#0-nW@DK$yx;%cMvRh^UX(ACMSBYoUkJ%9p!U(K<7=omIy1)p++-oid-h%V zXsc2?;`+vNattTJ)XX5XvH4H%yh~?Sw6J^tgR&~?UK)=SyZ1A$ld20tZOJ#U#zRr! zmUB+rp3(eW3agE2i!5}jAsSIZX> z{_b*&M)+#Kukqt#xapP9?$!=g+a^G9d!wCjY;7ev1#M`xPAEQQWp=0th7zkh*%L3e zju|{O9DDUz1J3!p>ftBmYaQn@?w1UTm?o$6J%w&W@ktn;-qHlYK zQ4M>$C*GElO-XT6FHRHvV^bBj@;#s+DTu-PZLJ(8+~jJC5^($`%Hwil6le))q-Ad9 zwEjRHWCy8%lLi3o2+`kr`K-05gOZU(n<@q{219tqMJydLV93IPG2CDbl2R7VZR`_x z|5oiQWJ(d{0fENiOgjdYnJ4oU0XHvZ({QuFW4fny~7kEdgWsQ>^kSmWYiw)Yepd7@uf~|9zTQQ= z6K}F%^&N=&6hUOXwNCqae1u2pd#AH=YrOo#nofN#XS8hN(W(Vo;vuAcfraW`k@vaV z_HlUK#nZSwRnR~J<~}Ciklh?&XemN#f(VJ&tA>{8nimI;-3JE=YJ}`27b|2y($tto zcZf8{Uq|*8FQEh7yc5=Zu(|N29Y;z3`prv98|x0PNH4bz8PBokjNX7Wpe)@ zO4|MbzyDwPVxczm4>5v|rhvDt)bEDYtbmT(zuHn(@)O_h}`f+XlgLaTr>@cHw7IKL!Brp}p_#JTqaBJ*@qS5MH zn`iay#O;@2o<$Z(YR6hH8b=N&Xq&TrtrPAyC}2i%LjZ}fK~z(a)Q50zy4gjB!%mB< z6QgKz+oPFNx)fUBV4smz>kn)`zlH5l`Um&CKU50}R2586e96#ul|_Zml=}{O{9oWx zIf2~#A;4d&>DQYSf?6GMd}#P?4gG(+0JWpJkv2p(#Dfl&egjn{2M2g@S#eRY(M;-0 z$HSDL-UZ5GV8p_sFO6jU68o3f>1|$i3{q2p+>I}Ly~94`>Kel+M7qceu8DOME&iheYFD08GtV#=A+AL>CnX`inCw?bz)i%J(L|*uMQ>-;C}J6rq1tx=VBC?epi-?u)uit_T&#p#zMfgB$h()ddpCMENb zPBiT?o6nS;UGp%0L_Yq|j}+Pn)v;X|zBjf)#)3~Oy_L=LAVU!r2nezH* z9|piYD?Tb*QITk9yjaD@)6h^TW|EQG=4_htf8;?An}}g5rR8&58y5u>eaR_`yhXw^utg2zI#l z4jthNc|wal)z3)+7Sb(`-hVh!gxuC@~-Ye{jF=N+>lO zsR3}@Z5(+t%6g|;QC?EcwJep9n|6lu(Ox>6zem>@vAR5xTw}tPv6Cjyx(4s-M)*C$ zSnQgTa!RvMmw+(TKPU*;{%g}QaajYEKmsTvjIg~F5!K=0vWXuc!|3ow(#;E=m`#@) z)t0^pr(JxuA2usdI=){pR?(y_bPV!r)u>|W+Wfuzm2fAnzFi278=cN*a(h4a z$5U&;r2*LJf=`4_b6ENXmN$L7+0r%PDOm<|F|l;ZGl{OkMau={4=v*w$PB~4I(Aw4 z9TjoL^oeYyCVxvmT4d&~#}pRV@a>O@Vctj5wW))Jyar2Pq(#x(;1J|s46>=Asa&>? zVaSKh9}^K5j$-mE8x`7?&j)uOUb-loOCcnZ2sfGPaxgK7ih~wDF|0v43wt(1Zh32u zBswy>jGu1$t2g-L_$+v`i6-xR~a1+%Drwyd9H-7cXcQ(QVYVR`t(u{}Frx@67<)pE`Igy7IY)>#4GB}7Km7z(%8GJN zj|cW63nYzZD5>C<*{=n93+X>q#E?Wtm0r&+WNdCNr{MMUuoG+BdW0Z}F3VpIC+lHN zqZ*_O&&8QO2ay$1aq>dAx-XM=+V4lP3Nfw-YP*q^7OAH%-%}fQ4y+9+-hz;`-lT#6 zhyc9;N?vOSVy)VtB75A-lfx6A@wEPvxf4k&p4gTu^g7a3=pkbg|Edqa zxTU6N=60-md5weYh@GSYi{l%B>gM}ZQa!+cR}Y=xQQM1)J)0UrV(4zM^0tAVdBjiK zNZGI6ZKjFF(C$-vsFfZY{qt0xhe5qhCyA*U42-eVosdcCqkcY{k@1i#K;XGaax2*% zwz<|BGz$)V`#q%2gRDG|DLqn(xWWvv6+^-_j`EF!H1TRTJ^5-Ovp}%! zU7UPyQTlt6O*YltS6-qL3zm#Zk|iFd;xSZqF=G_fV0{HFt_-~! zk)70O+vXHVjn%~C=s82_x~%_cON)8GR1gp6J>l5-C=F&^f+dQ$lnjUNi_>Ce&x)xPJ$Kq?+6XglP; zJ@61J5GM2Nhy_Yr_~pe5cqp=Uc~l2xG`vXXlq#lc(+33<&Y`iMD;=i9+Y$5l_hzKA zrKs>U6p7$tdPZGIQL|A7zGP{ z_>OW2lc6e`M4uBk&@f&Zp!s%}Sr_fEfxc$EU3PnW^55LXUBdJplb+oUh4m|qB$9d> zjmprPFJVX~u}5Mc%0k-H_=4T?sW?NlT%}xFaukj35STyxvGyL?QNpcO<415vioV1Iv-EummgHvs+_@pdEWjc#VOTN29{?ynz2nyO{$ON;Ju#uf<-Hs zs_b})S17*^V^O9L>;e5h6H0_qMHL22Z8YI9b*RGH-JJ=>peUZ{M2vMK1? zyvxH!e&Q=^j!00E3x>6s!1BgFTIm3LG%BBMRN)1Eh1HFz*t`DfhWkR- zZ$eyGHU>B_%XCH7!*iqSC7~pH5+JLt%tMQkr@Kej_TUiUJDZ`e!b;!$w zEKReja`nO=G~gx#)HtYu-$d;%Zavj(#%FT9U$#?dczaK7&xP!2i30C{zvAC8$Xn?@ zF-U;(OZoo|gSbuWF`Ex*q^ZyiUI>#`-f#2!YHx}4Y~*`pi?Q+6#ea4z#q3hop}+Ll zhF&=*UJ}_OB7%ALjy`@23m?3v(=xosrFw!lxFZ?{E_DSUbN0e`GZy5}RvAlS|Na^_`M%A%RG~mU~!Y zT);lf;eoj8Nr>BmGuyiZf;p!jxbTGjk?Fu7W^lRa%}$l+0VFZ&&i@WP%>D&npl~9Kw@)w zjMAxDlQH~K?RCOIY*kdDH^KIFhWQN|;K3{KC3wbgGbwcE12QEBaH(*b9ZH}fi&zQ_2X+z)k zjZtJJGrFrbd>*cav|N>y13o}8&fTI+j<9KP=ViB1+QVV4>hTPFz86|cALwNmYVF84o1RN_n2pAwxLSrHzXI~t*^-cPOEYb0((PvzepaSgsFjxvk zm~J6-x0hEWWiY1J-0eq2tffrZ4-GogHq-C5y6ViuzM8kOK;v-7Er8%iqBr>}2t5=S zlo1|A0opy5q4QX1t38B&OnL%XC_fjzFch53D4v!{EIMS|AZJC`;klhvc{JdELyY<* z9O;^+^QH*^F1iGl%gN^B8!lLq{FcYl8`)FXGO1T{Jb@ms!gs`PVR-AP!P0z%lAwh% z+I((O!$h0f%u>0X0mOJ*?~XyQAdR*v_MKhsSpI#KKp<5zwQGG{xsm)UbtZPJ`7r>G z#*6EZY+@X}ltk1Jue4WHC!Vuf*q3B z8tjgADKbfL6FnD_rM;A6G%r~)va)Hv7%B|LmImwq`l4i%R3Z2;|1NbE1)1LNCVR8N zAO^tnWN1v2^QN*N)m0q|Df9-UKIS6PK&LUM{5rGYL4N?V*saXi5cYbTg*Heyb<;+s z@k?eO$?}b0bOq$iX=2ZBjYTSemTPf`iV3jV9WK2vv{Eai+l5Rc(Xm{}psUv!^g_T? z@L(vp@s5qhWbS`lNs-WSsA*fbtz^B>ryaKU&x3g1tR|jgNS?)vgh1{HBH=N26Uzje zb1nuRsBE=HgAj#0Z=u|JTOdStY@qR0Vz=E6WCLNS>#k7S_}PnVo4~onkx<%u zLww!}x@#RfoJyUen7*U<54(t#zR&=Z%1f%_4ya2;eto*!xR%ea{p~Iq&>ADQRTsBs z?UbY8>`m45rX@2e_1W6&M#BJL2ONJ9%Mf%H)z4@0kZUvL{32X`vKb)U zD)H9JT-(cTF%m458(j(lpWWI$3VH_qruW?*^;P$-xj)mH*T_T|+wnTgx!U3_61Y{% zUIycpWvITjkjR3({f+fx#ToIY1pL*DLZ&=6+6W#Rm{8_P;D17}&K*>rs(x1nnLuBx zVeGk(Xo=4p{CF1pkR&gj?uj!s)C-lb)pxl12(LVpHKH&WCoZO~^ZOq){Ismb~2M9YZ|Y9QEV z;7_X`-O#8i8c3<&2=ez+9%lXODAwS+L-ZNs?!2JZyS=R!p)I(>X<6SN2N z`kfqtZcY)XdmC)=;DNyGY@Puun9WP<5CNt-3|lM9vnvw`AUF{z%M%&f`}XZlTp{td z?)BVhXmSc{gB3u7O;9ZjoZ zi6Ej~>0ZoYTKVI8vy3R3CF_|BrkbHlgsXCEf99)=TVh;1Uf4y~5ILzF6 zHtM@0DEjCxwB_CK`~Y60|En~xCa^jl9+}ct@j_`MAcVL+8ywQ+;kt_MEkft`MZ(<` zjDS@gAJ&|#0C%{Pw!AYp8xor=8e6(DB9WFV7%DA>Nb4Lv(mo;D^)F(LW?D<7f%>dG z!!$XTt-U3>FjC6_ZvcPcILzU4Sr@z(YMX50hs1L!d%xluTj+>b*>B8_m!A3&gYQq)!XGuf zD_%5JI2G)`Ii*W;@Y7F-1-ikJ-e$Wa`Ne4Yd|^^@`U*NB!T)LDFpK@e!tpuzhlP{8 zIc7=uZxT+L`>$4i?`3bbu(>~|Xs61qm0`W}<+E;oVijK5lMRHfP=j`q%pBBu|N1UR zg*lSnVpGVCozF8cW0k%CyYfc?@r``WBNgk8*!;GttjbBdGu=k3H4)10N*3RGI=IE& z#at2QekNzi>0G$;mSP`ImNr-9ymzKiNT@d3YN26iJsRfkcoZ`3zL_~~&0{hbK{sjK)V(`Y)t`4NdkQebydD5p!k+&sHgfqyAZ$55AJfv6E~e^=a^4?5!e zo9Fj)e108GN%UIeL8`O!rY1y2j_qCHbxfe|C6_oz%@J|n(66E zQA~|7@jw4C=Zj=H411IB%LH zjV2AyWJMQ=Bbc?PoT{58?Rr@ZFx>R*$!qJ0d9aL9#Am)gNI6*)G{nGSNjm3J)KIK)!JIecFu<4It{37B`3K}}|B~Ha~7Xg#K|7iMEoCMO-L=VPPu_|5cu;^ZaV%o57d429D8WbT^7 z;1S5N)Z3!QoV$p`fMr&3;9tI43|Y&jl%E}vft|6jjy69Z!jkwi4r$<)vBqDig&g(& zLmM#}7|sXkK1Rk8*=?Af8|OBxf&JqS1Z7T#&=A2)D#H!ec^Xg>46twgNuO<^znMKN zYU^czEB(Qf4a6WmGIY;k-l@r`*H?qq0CNo_(525{Nxpf$#w?ot5-ky5Fj!+#gpxpOtODNf* z;$svCb~iswYIc<`sJACoh5yaf+|=7GCDZ7lXMa?|`+l^_%TcZj1Rl$;@{t^-HMwRd#j?yrRd7ZSA?a_7!r%CUZqIYY79i$NXVws`F7ASgX?T1TOk z;8V9Y^i3c|m!GiMqD&!G=w5q!kv_y|{4x-pf@8S_WXrQiV-NTm5MceS|EcvA{ubqb zz_gOzY?I19wNM6U^%PV!CSYy(sTpnwTs{agottY$OR4sG4v)O#SqQtQAU6uO02lfH z%!F|+{6ydn@%z_IluaFCsBvpVvqpDA@jD(i?M{tnrEso4p-E3KdWvjtMooAlavTPT zjIG-UoRWcqd9e_%xb%M+{V$(l1I4^HbUDu*VxqX9%GPt(PnYc{%R%vfCoD<|KeQeC$aMtKM|d)b4!~z*J1L;+Be%gL*MA%m9!T8A6i8)cI)38 z-{~O&Y8%sG2o~;`qqTOj3xHBiVr_vf-5YR`_$4pJzs-T)&l|hOzcth<>vntJc2{Z% z_0H}mL%JFCw7)*ZLjL!<;NXCl=qLc(;^?L2GI={V+{(cmuJ=i#;8HXtPEvn+ZJeG& zSWzxTKN}w#o7U4h4Ry}A|L`Du;bL(IIoP~$Jf4W7DLQ?|N#5)UX5(c{{^?#tIG|z7 zc<;A|boQxol`$?QiFOY`dS;hoC@@++cwiBnx)kuASwd^v+EG(xJrEHBlP5TEyU724 zrhvvqRT*K_myV9blJINAU(ep;Xa14M|Mn~*^t>N>j73v6ga8=3F%@AH=6P5|7S0?_ z*MjypKXc6BmJCQomJxV=P}aAg`=jz|9+3c>Y90G;O9=|-E2D&bi4BB(dE6$#d~sRT zPj&Qxn;eYuVtq&ZpPs}Yagu-T|GTI8c>XpT%}{0wz=+QgX`-Ph$*`j{XY?f=%i3NJ z&+{+j)}*y$uDvtVW;Kz}Xi(I$+nCwqeW7A@b@ z8-_fryq}Lp!>iU;sb8LU=-)^#27gZr8n@qn_maAxlh7PHHt8bdFZ*xnV=fXu>&gWD z3j<(nE9WVlF#fCP;=RrbXBa2WPDBsXijLwA9L2e-(}3Hi=DZCtsGc@BV*^up1?@gj}-Wd-Bs4X;ZEi=HGizS=Y@ZgaWdZFI5?C$_)bXZ)2cT!5q6zeyH*Bf<{Qykto6O2_!(C=n^^tC79Q z3X)J0yQhC_Dcl+7J+SA)ZKd{Hxl6wbSnvJcceF_hiyPVH^0UE%io)28#O*kNz>8ak zJuTt!_A<%ioY}(D*3uti8GL& zgUNnp0mHEii5fVODw*xbmKE;i$85V~{+p23%Pg?wbdb#He;E?k++`&H%LVvXTO#t~ z>OXJw`pD{djjYNY{p9^fb+`V_kAM*Sb5`Kbv_>>RaPS{I$Y4-+owcRb*2e`c$AyNE z^N#bIiYwRMED)CoAzUGA77aHqW25ap6+h0Z=ww^_ZdwlQ`~v%C(~Pw zYN|%e0=}ARwnr4lRqD$(Cg|bnL=f9?H0h-DkuGTuLZw-tJx!TOhLCZ(Y)VnoJyw%L zwYL}Tbqnd;DzjK|tY<*S)iP76YB>??k&Y}qRbk!w(PkE#11#^ufuG+y%dhA>-*(K_hDIqN+O+rD?3 zqjS*j|r6LC+afbpcJd} zrHA)yCYhWtNeM7ygY`h?Tz}w1;M3ECZcx6~6UTQnnPL;aRaL>6TFKeCmMSlOY6BaP z)#zDlhpg^~prI=pTZvb>KATT`92uyrLan=4!if_z(&i~}=$V>=Wc%W`ZHp*cU3Awq z#kz2&$&S!<^m;GR;xe!6ab$$+$$l%KNa9C7WY*B`@)5H}OD>GXhmT3j z_ZRh=((1>wXx!ti#We}X=u3xZJ|CIT6Ef$3svA9Z$cEwf=%~T?&}1^=K*cszi8QBe zMVqcP)@dLK+KX;GKxqwf9x{&D8+BH(J=o!qpGMY!if6TR=*7Skktqwy-xv`Q!_uG$ zY&;SWH2jHjA?g%;Kgv$Cx>=W;)qH^`bx3bE5ZK zKJg>SgnLts{VcIE&*7Af1g@#h*ppg#X+{#qiqvS6!U^{>r806@YqWNL){k(XKV}v&3C?z}H-#R+P|17Pj9B?SO?%9C5Q#-tZ8q&&?@*(v!=nDl&zRW{* zff}4%goZ*10|`VfgE{5KyA|_)G4Rt=`oaYqea~ks>W8y1* zC!BcUrTbcj^=%2WU)Bw;B=Tm_!n!CEE;zZ|1z&A6SI`YxD*Eq32EyKW(TL4k&{osc zgg!p(BrTkK`Ke@A{*+H%8_YL2+sQ>maM>MvMaXmuKKrB(V*Y&M5u2zH-^E=Jli9U` zqwv~4yOLlx>irJJ+Yyv4mUkR`4=;h3M#rUi$EdN@oMgCX(^Z~NR^$oGjm@&|#?>Y5 zHiR{le$qj%RTnL^CcZku=t)4}P2&a^gB6IX*Qx!`bu2}yhTPB$L%F%Q<)xXC)t8Ea ziTE-wTc&Nra9y_+DciHoCPi0{(w@d>DM5wWd5weH@s5$z@x_2x zxtd`rD}|v{Rob75mzd0!>1f*l`vCF6VHI~?g)3h^BRG$TVxt~azj8*g#%({tDl(yG zDjZ)e^~gA1ZjPX^;4PF6`EV>Tg60K_z~-O6(x*S>I5fZHY~oDQ_DYm-_L{1bsmf>A zf&6gQvxa3=ogovch>M75GD=ZOP~4Gf!rRJoxGz)fa$qLj*-LR4HzkR>Beq?kh)tU{ za5joWR{7`)3c?)5Q; z*~v)2r&ls(Lw*GYOdW$*spi-fL`W>WWCp+HY>KuVkf6(XCxH;Q|W91dhw~+0Qbiyt6_L`qpc!#`qyN0OwvEjkB zY#TqgqGsTlw}maTMl^piAG=VkC`yr)gh%>g!5C|KDZJeN4~i2` zMb@q!Bmn=qzfEY^f-h);90bYgnP1!x6*#CV0clMD18uKDGl7Pq8$_OhnRn9?{XJ$_hxQHJ7DCYA$K^;n*s2y-P}gkp0)}+_S=L+y7tb=TyBE< z(Fq6EmGd~xx?a0GJI7Y6Xx2QA!`1k)?yc~+oz5i<2^~;L`l*+{?s8l$R!;Job5cBC z9D;mLsxvnd^^;LwrN_HhQ=A$5A}{1jlSfJ<^=Z;Z2)N@cw4mYHmqmptAM`?Mgr?=% zo#(I*E-aNbIEj`TVm&n#Pn1uxyn!| z-^Uf$v&n~|Pz-3uxqrdb96+y9=p=0yq>Wtd(BsGJ4Ad`Y8wxq2_0Xkh)f5;l)>jol zZ70{4KOz~f`@}MU{MFS$4ypOZZ?&&D7Gnj5+Zk!+g?!GGg3{eDw9?cPrN4*bMFmN3 z8F%&e)^3%tVz&2e^9!$6D_@@Mih;{};p^mP8xKv#iz09I%@rkiZX$_f<9PI(j3JW% zJCp9vp{8BR3jUQ>r19I1(4$_}71vFJQtwv5=GxuSw!q-WI5-sUaJMcuL|U_fo|CoQf4!gT*w=4DURJNKwZ2_Pra1>!ww z%oEEs%T$*8v7er+r@Gw`rjF-DUD-BA(@w?LjC-VfM3=iZ7dTrAEpR*0IJ`4x#ZttK z#)E+2W=*qE@!*U@5=rCnpIfeGqD?Q+zOvd3nSev&Zjxr;`wAh^Z9ZjEAukW?ayXC+O)>bCD z`9Af%vwWzrr3X&r2yu@ZGVf`P<03%aDfFQC@dDlScPr20hKyDtEQ+GSZ>NLdLFL=8 z7iaV~GKCKp2bVR6zOJ?gocvSFYY*}OP^$?!V$mUEU1A^M5KDivC>qEwX{@FMwsFDf&+FziJr(GH=7m_;XRjOFR4kTbyb1Vv|3$!pD+7L;p}x^0iNk)FT>3 z1=w;Z{A zjnysa5KStQvQ76lxFTJQFjn`%Cy%!>=_cy8Sv7eJo3{-<`3D3&tzI@#9EJ>s!`Dr# zG?bjFjULWHsMP`uZ3al@{kK694`DkG#6W#}o~k zs;gq1d>KU9>j__NDpv}fKYMM*aVN-pb3&o@yEagnUL~3FofSi{R|haKvk>tS_7^kq zUy}oZIz3rY7*<3wQvU+q18C3F1t?PlHC!~G$$+gZagzG*R1k-aZ=#CKaZ_}E9cbri#i`tRcLIRL+Pf5FWX_j> zMm?|jFHADBGN}oPCrYxF$Fm5YH{SoUMV|_ZXHUSi)8|s4cr1#ieT=zN6^Buc)yb0T z?Pd7pqO!*M14)8lRNtc44({sY)bkea1N&iRsu+ZWJ134t4QEfGL(Qi2<+~F+sVhny zIggu_Imp)z50((euRPzip4v3Wb7!-(WNUg87L@Zib5?&ChBHbp=U&Xa#YX$!E(y~iR{zWBOPv=dv@GDJC z#4+zf+;#}tzZhWr^|Y|n72ZRi_hGHdqB_jTOZh_Vg9n9`p7{xqe@36en?Nz_zvhd32n zqqFiJ=N-Z;4n6W&@HCUhPyx`<~Hk zMqnVxJiGnWyfbpQT@=4?q(CkDf>@)$XX)N|OV4A!lt=o)z~Q;?N`#;y=NRTPgT%Mt zW|BGq4skx4Ik$dW`a}O5>f0Zq^<;8w@bgd z1d|k&j%b#Ap^lPQ6NDa{Y=29ee)=C3XHC6U6J3q?s@QtOE~$7OwO__T1pag#k$pAx z-eO9iE##_oYdX&*zO@q}1r-2V$-oP3-xjs%_<+zY$>G^E8wS|l$O(rgn>-7QVOezJ zD)vez!7>4X1hQ~UEr)qQ0q8Y#*!iWA5`uPsJQXBf^0Zc%R9+m-&a{+5a8z>pM~3Tp zLsl}1Y)G3&*G3qhKIhMt)^FI@wG)3nvJph5x^MXkct2ifu?`I*K9uI{}Q_T>9mO_AxuR9c@-yQb<=y z|DM&E8cR?Dp7)%Ckt~c;xai{vMqOIHg0FU68Qio#&~H$ zEo<_(e!s4~+8eepyTVcxTd_^pHWMoeDo*tATCVwWO5-EjwKWy>dlciF&e>?ejx2xv z{8Y;ytKxg!ecVs|G&cFX_1s=H5P&Nx0>TYejXjZKyGG_H;p# z{tdP3m=gjDlS!wrthmeeLM>i0tS&DS z+@2owp3#W�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

fpPyGas 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 RXA=Vuc)I$ztaD0e0swZK43Pi; From f7c3ea46ad2935dd4dbb7acc1969264044f6d05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Panadero=20Guarde=C3=B1o?= Date: Wed, 7 Mar 2018 09:55:32 +0100 Subject: [PATCH 37/59] screenshot with CSD enabled --- screenshots/neovimgtk-screen.png | Bin 84901 -> 93227 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/screenshots/neovimgtk-screen.png b/screenshots/neovimgtk-screen.png index 04917d5ce87641264b55ffc897903244c2279386..cc21790cc9d74df7940d51914773886d67fecb98 100644 GIT binary patch literal 93227 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Π| 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 AC;$Ke literal 84901 zcmZ^~by!s07dDKdbV(y6(%oH3NP{5V-Q5jJw-VAVHFU?&NJ&d~Gr-U=bi7A>e%~M8 z^}bwiF>^Sx*=Oyw@3q#w?j7+_Q3@U9H3}RY9J-9OgbExSJOLcsb4w%y;0i7!Ne1u> z*-=`{1r845=bxWvNsJi8aBy$nWF+3JdS)Cfd-&p-J$9as4L8`L?1j9e%cXTi(vwtv z*W3MYq`I~>^+{>00y2-m>R}<=HYw;M=p$K``<5=}f`J)#kC|eg>W4!OnDhLO(sZ!U zJ{}$U#IHmD9A(~O$E%%_IR577(}!F)x^mS@EHNaCV643D=$T?wyBOJ!fA1~GWLSd# zy?jP7_}|61Z>XC8T@!mDVWZG`S-Ns@;hK8Q{P#|Yz=EQpJ3%AC92~ID=1#kMCXD(O z5fM?m?w@}khV;s?q@+YdMjU?d16Yv-w>VRNDp`V$1ml1I2&rmrPBZ&QSDY#38<`KK z)C9n1WlaXfot&zv2>wn|?1d)ql{qM;_WN5}n)Q@4+k%?&dU;XB&pd_*I#yN=9pJ#x7hFu3m11QflHN|w7R9orz1rSrF}tO_Qr8i|l!!FlE!$|@>( zaD17Hd)5DaGHLG69sUg$*A%VRT!S6)`}glZz^F_v59Yn=^%l6APxh)cYfK}wb#=?A zXlWU#si|d0sPgji#GW}e>$)`%laeyag#8=J|JTq^DJM~tb~!@eh{`a23AB72@$*@?{;A4b|mlqd*)MZIl5jHB8WmvLrqUcowhv0+#MbQnF6XbWa_2U!1T3YFDS8u37KVC z_V=rSX%}{PizO`EYY3CYV!eHRpfF5wo_Sjy9K#CWiS2mO z_nn(n!OInvX@$V|ZcD|SEsx}^TgL*-L-eorP>fQ6Ks=t425S1s%0{XWi`Mn~iba#a z480{KL&&hP^Sp$}%7HZ~%`&X}ftar~DCsAR&yqa!JVgO`*4EY~(`rrCxc+?Ya&=(f zY-T#Tfpj5h?0;X~+VaK3K{HTJiH=s+Lzw5Ul zpGPN~$$JGXe^Kkcjha|4UF7)8%ROCb^bvd29mv58c5d=Cwn>FDCpb)F+&1d};!P{pydn)B~m* zFMWi_(g#gzUWXbf&C{-K$GH+WEUH9Sa&ES97P(OW8 za^fjeudjjIJMN|@x?+<{9J+p3J?_@l%@&Q)!}hCO-6o_0iuWQ1v8QsYdzB0|VG#Fz z^+}||?FeE}$4p#89MgOUV%f~&Im7GCh;a3UJD}j zsBZcc^z9s0(=jrBij0g@?F28CRX}Ng%`n3?0)l#fH6nB`8afYS-SLZ=+(Wxh`seOq zb>WkHrv0oPYZF2wbB$LD34=d~LX9lb%$t_&mk-^ENd;VUnwnAoH%-XQjOpK8PqV2o z8jPn_o1Pjb#)Ye=|NP^}kB_h|JMi+6p1r!7TEet7H(*(44IU%B=l;Jv8a`9yyUpOL z*SOze54e#jN2Yu_Rk_q_&>3p!HO3LC8^u=TZK-r}oGxJ(_vvfpfEbKRHFNw-8zHp*!QBJuZ+b zD=UlA+uIx2v;Oh- zO(7yr$?sH?Lwk@Sly?QXgMa_<$Nz6LP!5AYX~X8V*7e$Lm>rj%LMwNVG-~Pw???2b z5)*MrFc9I$;-r+-@DwI-OQ%7eLS%ras7-QpB%td2NZN|pj5xBVnX)cu{qd#dzD1*f zLnjw%iI;$fQ89g@$_3;47f$#j=~1cyY^QOf5#ULC111f0E=mxRB1;X!*c@;QT%UD0 z@+IjA(!RckhUNmh5d;E#S=l}9B0aoF!IrX=Vr*Hbpi(b@(lH7WsOI=s9~-!3w#2Gey9F*s zaW8Qg{0mbVJ7PUzL>WAZ#VIKQzq*moQS)t_dCy#2=9}E%se7pvKn9as34^98&!#s| zi-C}?{0q82ODGs-08pta>; zn0l(BdmP<0?z6klE9B$CakIq1CrA_k7f4M%e~h{!9gpw&)5G88)5Sb>Ujkq&arc|H zp4?C6uHKEXD095(NQNEd)^YPcbwtvqXeH??l%dShIe5LabkU`5B4#+TeZy0p)7$DORH z^odD26X>n!+`80GO(FYGMWR5dQh7EIj~@IM4hzLkE1G5v%*(@t$zA`Dmm#*=SLZU} z#m^M7d3SrrNTP-LQkF=h$~p8Vuf0YDe#y$|%!Fg*O;5=){XpCd4*n^Djr|~(vX4zn z0Of2Q%(p7jYLCxPO|8EaPD>qJ*e)Hf!#U5&H#QEBiGr0f*Um=|nxf1pxFyiIf^}IW z)zk`5a$dZA2?U;2958YD)^!BqJAEqxchX-L?}m-CD^4)<)Qs;^2L{JGi%IYcyxx^u z<|Y@`pH!w{iM^0cK3d+LC?X(4U`dXuAJQ=5NG0U)ei3#_{zE73=wPWKC|`&HL0DKf1*h0e~oak(4Ihvh{;W;<6KF0u-MkP za<*>rp=Nh6L^Uy)^E4iHKD9b?F4$9^28Xc9AEqs2-?(b0)MWWKeda;F<5>w`jmsR^ zhPUTK%o{p7q(DFLRZW1>!zq$eq2S{l57wLn;5U_OK9qUJ#%!soB=h{}M+5}qaZ-~5 zV7Ecs`HZ zNv(-Z$G3mN{cs{P=;VoS?}&v)$&Tu>-x zm)g4V>HTrFCR4QFG07Q8bD|(re__m&U`D@BRmGYlmQZwot;04ucK*BG%0j||3uZ$bt;dots zb|b=rxtf@YW$^Q)>_9V8elaVVZR{|9sakpwk=brro`jUCd7rYy!yQX?e0KDEBj{2+ zYHC<)&MZ4-e(rg(it5LAnqe3U_II6GewJCklt92V{y-qst5_pK)x;t3#NgsMBSKqU z@aOgXNkv&>cH7~a6iJqcwXE)u`Le3N0;F8oYw zbulQ29W4k59X>-$>ZrXTFr}YIGx>t0$=ruH-Jl<34E720Q(Fy;42o1(cUK2MIe>sc z8a;1JVY0b{zpRPm)FKGrX-krg3^6CRq_7hv{_fAWSlRcpst+GJy#2UwzI^Y~tIfJy z5YDXUYcAez{)qdsb{?(b!z0^_JWFhxgY(YoE&EF>&besolH1Q}NJpmKpNw+$l7va? zOTWDkN1A8=cFSDrqlK!P+4b%>DmR6}ZL`8ong0U1fC^~BAT>&6`Qr%b0b zX#$DiJ}JjEu7Vx75ZLA}j;sD}^YaNs%&Aa2ZX0^pk)2uq$94UvTA+pf$xs|=r zDl}o9o?I?|ORj?jJ~XxFKT7iJ%*N z=9XrRU2Ek>Hm>p+buGA;9lN-sQ*oF+zb9Nt#o3YM)RpZ(W7f)Pv0Hdz;qDY~YtBlt z)Oa^w3aNZ`$8bQR;gyVEmgX`+@s>nyl8)s}K>!661vyUW^yhdQ&i(VjO?Q_I>F*v* zL3wl+2eaw$K9|qFx?Ljy;JWgHmWwI+B-}B_nNG#s9KY?;tv9q55%CjjmlCM6d!09G z%eM4|=kJD&wArVY#>ivpNr~_xg!!6AO@hS5ioI`by;3JDsfk1Q64c1!Yu$UTT@~_A zG>zUt?ROKN-l2U1fhw)%)ZWD7D@Gc9)sp)7_TOwPn%MavEQkN)dgan*Q!j zM{Q_C{29s?F_Ox7XD;Whycgw0n=R-UT-2jxeU2Me*jNia+uv_R3cgjEY z6iO5v;&)y987WUZYyZaW1a36bRRnMzef`lsZfHqyvEl4xW;8#9=}&kzJodMJ&l9`7 zJ!{=@>o1tbHUZ9UNbtF$0wUe0TFF6|y9#2=JMPp@wz*a#6V8e>EQ`<PfuU(*x1>uyA6M76KrjZ33080d=rinMl1NAU7zF zrF`Up_yuOIKI2i<@>?w`Dq;k3`#<#z2}amLfgAxfEp0z8F0QExZf_a1r5C75-1HjL z-@CdrM7_p zuj}=-W9h}`W{kfDTOP)=MkL6KQ{T^PVJ9*&qRW@=BV({ zU0KxqlCDtNPJ)msWMH(*RHf|>!tbor{<9-&H)LU|q?27zInq^+_2n-+9vw+7tyCQ~ zH4h*T;N#;L3X)(5^YEz0#l&!{eEJk~adFYY!24VvcQHE)bz1F%?}7L-8XLIaCyeQ$;@16L1t0@w)x?~SVcHJ zY}ZFal~DP&&#PVCZG6uHehIb<%xd9`_8Qk;3w^9)na*}vcL&ec*^8&6!-V!qB_&C; zC6w>!mS&{im2fu0(JdF5V1(sV(BKw058yJ;siPjr2i0Vv4AcUZ$d_mZodtmxK-S7K zKDPQQhLN7d2v(C6O~Sh%s8Pvo-}t@c4vIyLaxt%KlJa#tC@{pOu=2<@?Vc ziW2>Op5$LOj$Gp*1Jvi^uSob~-K+QY(y0jG%#~g##}*_8!>k-d8+&qWlVe|_Z0oq8 z#a(Fr+i9A*J)VPyelX=8jV_5SWiYtsWORipo)$n*pf0+ zkQy5syE`@$ydaRs<5Za40gMv+niOA6S9hsq1(gV&I+#yE*L|>8*MA=OZFkRnn+0oS z>!qms@jPnh`O^)7&1^Ysfq{YdVaq7kp}nso+b=eo*AC$g4NaQI;}t_kDo?`i-zr&Nw-LXL#8BHExJDsQ z=d3(-D`Lm`!h7E`qI=fYR-e4GrV{<-+YYb~F5mcTw`4%{eR~Db=-&!u5IZ!*QUoCw z8&>&0jJ4kOrQ;H4RD_Y?5e*(TE)NGz;X1pVU+<^iB*hQ~8X01u81GC^5^-8n0TJ5N zc5yU{wc~?gY!7dfbpQ@1J7DB24RUuM@YuW04V6~dldH@U9vBntldEhW4bA3#cD?Ff zZ=-tt5E+hCdG!DULQXw^xAD0;QiC%#F@d&qIXv(alrIMY#(a?}Kz()x_3!t?6d67e z_K`kX$#fYd>#wlp&Bhta?xc)+N+9hx5Sw*-yiLACMn&IV?+}*Ne`Fse;|=``k5J`( zC;;~%mT;87!0+%u*eY!Q;gI-)R) zjD%p*KiRj!)Ugxj9u|q4upW z!ONrY0qu*l?LJ2>q@~l{+2k3L%1+3WmAB}%WUX|Qw3=z7`TZF;pbmK4YH=Ej29HvpiBxJE0Dl_XfsmyI75* zHyaTZV>A`c_4tqt;rHBSD<~-Ny;~M@Fn;y=wJfkangRC-SMTmNN*(6veK1GQoc%Rh zgL87;=<~1OI8kv%#ia90)=F`}AtRrn?PmW%1=d^NUnF)(SKl9o{yP+tnt>xe78&_M z>98M_wN3WD5rvji+*wECDy#ty2WKQ@g3{}ou>&#>1QUymKpl^uIZOM4=c( z8zUqC&i`~>HJr8@63QGarrb;aN6~o>48*{9*{x^Bx_s6Js{x|C1 zUTkum9cs7`FGITJ{N1qMzRLT(&uydCX3iX$=Xi8o>$M*tl_GL}1+cB7xyAmA*$J6| z|GLUczu{yoRoA1G`cdsYB5-_#SAkNdo_=)~esZEN7?(!U+zj+9HjGUMoVK2SovY4o z1lZ44xVRD$o_sGuLf)Ah1O@mM{%Ay&D8(}I}86Ki%j<9&ZD zDSPTjF$iD-#g!QXXq8Q#Y^7LAXDc=<1nnv4OE>|1lFxE|M)^B5KTRHum?v1xX2$Dk zwbnxvXG}Ei>sKt}P2=XHwA7DMZI8BYq@T{~_uVW=N6d$2?Cwu@h$EsSsc2|I)9qWc z0BHW291uNRmTzji+C}cr@)={DR9ZS5vatFvvnVeKQ>v*Nt_-&5EHe#Y#!g;5Q?! zwYUQ1`M}?PQ~Ze4wy|MM&Cjpp#76=Gx*$p4zk^yTbiM=yF$3K#lozT05_4C*I0ly4 z_F{bvnEhiRox*Eb|Bk`H_i^D#7wCHX%r3UmgW8|Gj1pMSu+lVdj=S4X5b^7mrCBAF zJ)y8rM$DaswPb+kM%JiO7Xp-*YD zh>_#0)oWuuYIw2xE%-&)R{-3`IxZqTw0i%1V!CuTKK^&|N}G3lc0e?Nfqx84)In&z zXxVya>@Irn)4d#FT1_RE-C-ZJ66ma)?<^kIyV2Qg$cqM0jN*QK^~(2pC4I^HZuwvy zTsi9Z_&D3a_STLt)aYx%rr~4v@y+h062Lgf73u!`5dm=-I`r_lQVNfQjLaHrq(L3t z3JMlY+!eI^KqvooT#?n4d9~Cp_~es>Xi-r6)cM}wruNCZ#yPgv0~LDTUVrVvinF+T zNcTT2K=>VL@{mWdMX^FA3dtwj0_j$bKAJ^PxKb#z{$04BvZvqWg(c7z^`y_VJ@v((5QcV*Qv|i$?DP^SZj^h78r~jPSPcH&zNhrVC+!wA zgbQeaU|{3mG+#Yx-Cr&c^SNHLVT!tnsuF);H|q9W@r3GC+#l96>DHzA95+wutRx;r z8Yr1PwvN4CGMDc-`(ZFvcz<8FYoWk1uXx7eETNqc`XbYQG1dREX3)LRwjTwRKR&fL zdQanbqk{v}%t8;9#lzt%8imwflF1Kg9io?11oC{zE~EUY@YhCzO0F{d^3Kj4$BzUV z0c7bs4%?RQ7zi<|tkd;+Twd5NqEFb3{XNMB0X-oJw6}>GCC7I&3-?x(SWa9&+0e2T z5##zNx2BDiy8G{AGCv4Q5ebAAIlDI$rt`Oo8`7_VM00;J~&8xi`?g+m-Ot_O9g7dkXAyznF7_3p(1%{TF|8{U>$VO#}JV2Yj zuK1Slcz@+#jdJB~EuUtZh)=;wbn&Q-AdkKRe7a*!PE1bTFX+&!Z86_Xj(>W=`DEw5 zOe-fhYJlXl&xRHxDtbataX<2O3h_CjV&RF^1AB1#96^{2s()J;=*`ZxSzsFY!npIL z3adQ_vUcm*Wq*!(wC4-`3Bl9fAOv$`9WIETN z4m;;(s88!LysL%XYJP-fCM6$i*stwZAocp0T-#@_tLqGCjp!H5$%=^9CSqrE*N52$ z7IU#C8qcV?RVivZBBgo+A!3u!JZ&p4LnPsTN=PO^PujXP)RKw^1~%pB)!W^<7#C?* z@}=UiaY)6*bhjCp$Xm}lwC#TO_f|U?YzKV*^5yyQX+`9(CoJ{Sdac8zBut%&(tFSKD2p+*@(WjXF=WqChY+mw*}M z{bFO}a^GfgQM$2DCnQQNuH|->m$~9$gM&_N>#e5;b2m{+^UaWq5D-YjC|JWky#rEDgVDQC1BJF| zpbjDta7_WG9u^>UNhRvA1d!GI$p!);r;vpF^_@jN&y%lLFd%fDf=i3;XbAG=pU3M0z1As`{B35NqbfQ*aSsg^X%=$ROdAHRhBiNYMrF*^1w z-!Mh86x#CO;lx+9>$ql|^f7;H*W*bJMT5G)suE z`c#!N#M+i3(&lqobVq*n-AvH=zR5sznV+bKo?$V+tk z_Z?#Z5h2s{?au{i9M<--QNQPdrX0`cTF6qudh3skl#f70$}NpqXC!n>c38jjIlv!- zaL0!|e=S*hFJeSJ*Q~c@z7oD><1Ex0tSr)xowwcS>=JLi*oQJ{UiU)G9yht~ zILH7c;&2%g*)4RHo~hXP-E@MI=#WE^!C-#C6C)754n!Tl#)&-QHVz#Mx35$(w;~+3 zuU^W8Xb3}U*3xmFPC-Dz(^`BP@Qz~N1r}{}xNqUtSI2n0o4{|GHZzkO4rl2gY9Lk@ zva}?$8aTU`ct#B zkmDy0RR1*)qyNZfc*OZ;mt-a4m=#tvm(#7mGCgqnOj}6*q0wgHh_3ItX;#2#(l3HV zM@`lIE6&4v&S`f+re9kO9Xc52A4rA8gY9dvjTrX=s-ppOT#BE7+E9(fv#)G*p)MSNCyyxjqffqrxEZP z4#yN>o{ADvj`GKT*G>sgd&~t6vVcmhAwuqzj)tcjY_SaQ{g&gBlC~#%t43i0IHQ+3 zoosaB@}+9(-_Q#X(QJj^GtSW8>gQ%zk#FFtaYk4kiHN=1PWfFtAPrPKE3+!Mm()PA z`j<7pAQeD+SELw$M$QrZ^{b)t`I%O;H+pYBE$V0ndrVAv<9Lw=E84Q*d}NaAVZ1~f z*o7c5FHapm!E})C$!F}Cp;XfsP0Fw8!^vc6M5!1_coR!>v#8Ot5=xjffyz$ z?$M9QJKkrpAIgf{{y2N~$wVqH&jq5WIczQ~JCt&FL*Ok?Bqi zNA4@M=o1YBMZZ?Q;kBJktN&d~5nMdG=d3j8578wdP_L?YmRS(kr(b;T{q?g;%yWf+ zKB@HE9%hd^HzctPW$ca3!O_vKaix2XYVC60%U&S>wEwVM;)s|gjf?y?2ZY~Q^xnr( zhCM4Oi6-VubAYg(kQKpY`#s_b`%rPNFS%%f?cuYBvxYh!OVFeh%m8!G*{XjiD5wVH zMJOmJ5s}fR_xu*8^8^ODR@#T2``)rDewD;P-Ad^W&QyZ<>f8J_)`M8r&R^e0d3g$D zeK3KqS_}f-REEbytRK|&oq5H=ODaR#cobraDr{Ci>H;xyrQzyeen>Kbw%WhvgR5SV z&uj${E(pg?CEr`?iwCT9qya6be5qK&!8p0Wp)reroX;yQ1k8`$N-cDI@5pa4qBzor zGhAl)072JWlb2k%KnT``>y$lto`#<0U_*R#eM}i~fbujn-g{R3xv()dP-e?Y_TU;2VBEvbKA8yPv zwqNx0NX4?*?izN!Vd42cFo0C3%Qa6MTUGTDu+V;ZQ9Gn~=E2WN`PDk90GsQ{yI6Az zmuu-lUiRWU-Nv<3)xK3tTe$Q_+pa_Rgw&*vo^NvUu3R9{jE`L!nbP4On~-*Q@7sav z>JkB7M1b0TYka-uVs|359?CQ2gZ|+4bX!!&<~m>qH&e0r&dQ2@o`0TXL(l;NTz{ z5l1j9#4o1M$}Ow3bpMmDsBvlPsH$kuZH2<8&64AVSpp3}GuviI+*#4)Bcphvs4Rnw zA636lvs!<31$kJEua03Rq7|9jU!X2oxy*HL9!{I3W4xow#1^df857ZZDUamT_fe-U zq~(!ghz6+NGk-Gk(1e=w$L6Y`SC`r3pV>?oU1Vb8u%{2By=Du%Z}nYbQ3~pxULxVg z0O;Sn>3l@<=_ya4ey9^zXtzbN|Jrkyh7(>z#c#3r^7wZ=M}x!aAVA_&Ycsu=xAo7o z5wBLWgTK4#H|CjSr}zwnYiAc1;x?X~*JKW5L)RIW<<`-?zgM2l9SOu!Qiy8muecb7$r z!WIkvE-kz7>f7xITa|Kq_|EMpFr?XH%ij}*K|#e83`D!{EC$MU2Kjv*&_`{3<-RKO=d;r@2znfLAG&*8jm$N~M6qOq_ZqJU0laQQt<$7A z;FOM!H;}WQJnH(Ld?aAAedxL?gW>?vz$Adq?_GDK1H^)|&dxPJ4?q=Alrk_gs{!q_ z7l?>DfZP}lNkxtOCtEaI58K(V_Jh@*PNo~Cf~-csHeoyIp{NFhyBGB&ePU6#I(U!+<*HNSRyO_R}T#s zxq8}Xn@V=1Uw|Ho4~MzTe@b*L4kp?E%ggdC-QX3`#`^UCRHN6qTM-8J)(^n-R!;7A z;5xIT{gu5P$>%>eNK9TF%-5ESTLK+7n(gP!Mye$)E{gZ)DW{;TL zK(Qp?6$iJ(X7MUsi>78U`J|Hs2zYIjlL&Yu(Dwb&c;-FSYL^4FTZ0>6-m@Z8XZ8R^ z4X!vbT`7yGxYc(0$XBG5hMxCHt{VPQPPz3fn@4T#Oablowk|#P$#3(x9&Z$Rl&iIM z!ruOAQz^3oIuv8$x1jyiLui>+ZHj=Kb<`rMh%aAd>lup6&hUB&f&LqM`VyIKi?oC) zZPhj5r(L+CfSSi@Qw9F^)g0d$Q$I1SlGLejB6Kp|8x2I|#7PHOX{-#{{R!Pio$z7VBN;;^Jv~onYo`HS;|yRVdPseRd_%@cCKKnf2k_ z5+!`|A#1v?{@tcSUjh$Hy3O$%M(2>yrNv?&-Q(T5l0WHcisZR~%M-eZBUn{Tut}@M zL9zU@KQntUQj%1wktsHO7ezngfpXA~fm5R=Fgc5`v%m7HTg_X`%G z?}RJN%^(f$bKt_ddx=oGNcfViAb9!Mkb_vE#UGtW2$azw1by=`!-v);6ZTimTNfD| z`~r|@rMcP4%cI4KKJp2BUl;-h0)${wpSHHuR>()vxTDH-8qek}vY$C_Cu#i7m%=3T zIbe72dU||#<_Rrt9Cynk(D(ejJ(PT>RzZiNq34DNyr;mA3c+JkKl#O2x#rvq*YtP` zZNA=!@&blnOCK4|;7=GBkfu?}iY~MQba&2>Ob-sv6GL4V0z^!*tr*@F!`TYGk=A%x zDe!Ky=X&)sz`$A!0T@G=zP}D@d0xFz^TEqEL&b6Wp3499TYSX2sJTCzcbmygmy$VtUG@bkXn^mrfy0Xy)L&yt=*FJU|_4IzLvBll|Ly)fU&)C84087!eU+ zW{!bm`NUdH7OuMrG{a_e9z-t5pRN&kmtAq-W_4ZACySJ>XfPfVl~OrEl=DA00rui> z*^z(y_HBxYUrXaXY}UZyXsJ2FW5w%F_*``8$gHX1;3_}sfdGR7-JxI6M4Tz?CJ4uE zS7ZQMjjBzp!=12yw3}}>(xY&JlF=UqItPeG&p*eITHbn*fyrw(Mg1e%1 zXN9d#ni^4P<$Kyo5KYMb_T2>cQ8y~9 zDR3eM7Y`3;@TUs<@ND+Ql-3(m>bH4b&RYN}=+3goDy-fAV2-`T<9HN!o~n6U+vS!d zfJDivyG}3FLyNk7&|BP%*X_;#bXh0Nl+9b9hoy(OY(Eh{127j^zp@~~!_K?f1OdZ;XmPGiVi(!e#An~Ujc?zyj&N+_RuA^QadwFN zS(NR+@8>s~!Eb72=G66)z;V&8vD)+0c*Pr9?!M|X!4i)uS<>p1<$3e|jP$)Oui5i4 zQI4-2_deww*$^_K0dn%WFQ6G=nLf7HN3oU%*E5MJs=pMj?6x-;FL|3*+YJN^8+Kur z0>yYk$LRZXJHpMUb`c`jYUA`&*u4_DBN?h^uYNLl1?vJ=3KYmcrC}t4m6f^qW9X86R^Th1#TH;FPI;^(49xebojehJ=of)L~8AT_Y=Je^vu9JWSEBVy{K8@w|UE zqi2q#qwuLpsG`Z9Kxank&d*m3E#Tq!^-+pH&!r(I?>&7`YU2Ui+7KrA{5m^q6ZDaO zxRG68kJt4`z5AeG_14XQ^63PXS`xoP$bzFc1mF9TajTuyH+%9G{!!s-j>(HKPox?j z-(OkYj&$U$zlr0d%%T(T#XrOW&16t(H6jqVE~meMXm$&!0zk;kG|fpeY~nB$Vj>r zV9KY*s}5vjN>UiI$zB{KA zay20-C0JY(zy(69Shzq)aPWAwi40Jefh1vYSdkp#ylRo(XoLEbXK{)*9ChmXc6!d2wp}{|T8PrkX|JhQO zv%pgT^@DH$UKY=P3&?6F-iFwb0-6aR8B4&mUtTc0If~p7hRW4t z{Qq?{e{OkK7X{Hpd(9O{pR5}`sdeOMxBi8Eu7(;OPE9S9&*^!|!(A>^pyR$K{brR# zV$w9P;V*z;9ZCY=4+mgvzZCgsMXl|@oU(+(hm+q?mFv#UH;2=cgo5^5q7O|6R8sVR zgH>?x7(F`^L-nV(g6;=D%7!EQH;wu)GlBDZb#9t}A5$#cS}|g4W8j;-%BMS^(Hsl{ zT^uA0U)l*S2r&J5T41`|hvu59Vq!wG`X1chzI{7b2FF!GMu0P1@Kwe)4A|Hk&Cnwq ze)qD#=<>xmn*W{AJd_0{>wja$tEDA1I5N^>%i{tYwu&Z6>Z>VWgxj4iMKm(X^*wp) z2WXV!x>r2|gHBa0kP4pI|Aa@%1;9c&3uFT9^0*t&c#{0k=icHf^SslP!|7&9?%MwG ziiFQG{N|kCY-D!TX@2|D$7%Oh?f)$G#XVkk%9K65O+4?*7hB*69|~$z>ozbGWE7Nc z>-q+@@c*9v^@60c(KCE7oG;@rxXwF?n!6^7{iCJVUk@yZ5j(2Dyls zmzP(Y+uzIyi9)f$ogK2#`3RrEyG{a}xCCVGWm;`n<^AM2h$p8~E-73$zDKQ4{CDdo zxjn;=TeS_S|I}Sb%5rzoEEpsk<-Dt5{;{W{pScJ^-zjIbx}+nTG@}e$0fI$BDzSi zur*D4zY^ln$d;!u*s(WR2S3z7BG{d(@dU0izyn9?2CV4(vmNl~(=NngfaqgqpSakY z%+AX*?ifnn0CL$rHngJ_=nBFW7&enGqoA<9lkQ-nE@($>HzV2=SRZ%Kzvk8f)RV0b zm&Cg*qb_4@geSK;#up~6U%^(SU=e@LH8-h?{eG>n_K-Z#+}r${`}_Aza%orR91gPy`UefHn-BZ-ZKZ}@!_CYd^7 z9-jcNb>6l{>*?HctvQ8^{3yJ%fsUvBT%dSfE1Z%6V|-T3_Z{(Y7t_nug46HAOTdH6 zcT`0?zV`({G8@kiYM;u_KONc^I?l9kp60k{GHwW4tR-C9?;wpfxFdVEea6Pd{X7E5 z2E|36eN^4)WE(9bt9gmqZn6GHuNbYK(0qVc^PSnc{uUJl#q;qd`MHRYtmOLW*Kb1P zJjm2J+>NXNibTZ4*+AKXv7U|Fd$a7DaHImddj-MM2n8n@WB}Fd2-PN|{V%+X5zrA0 z&ko4n@m}8;?9u)r_xXHKKNLYMl9uwm#|aO=>a0tLPNZtK^7hb(VUj%dhle&6S@QcC zeO`md#;-w(3>qL6+!Rke#J+om7qx0e=f<=yPileSJdWlYXfi2s#K-EFj6yAMUIhj` zu!P++J&C^G=cNgkb3mtUC`~ny;@;^{|9N|3=3l?M@LEu`R&xDJu!qRkpAC*UfNt~L z0`;^Bpki~+iGABMjS{{9<3$`>uSY%MbL?FiCF1P~c_hFmPp;h-<higo1XoO7hQI@Zu!J@kQi>3=28T0#jl;+m0x5L!KSEJ?F3^F5V9^kBP^gP zygNa8^Kkt1(c=DG&!=fdBwP~jjvthKov2U^+xVTcB0=x&okHnVW=hnE7?;yY2l@4- zIg!X-A2Yk#`s$TF;~i0;PSE78L+o~8o&%gFnIHN@WF}T`_ZWMUcnu5xC3_h6Q|JqX z#qL!8b)s9En4bLRf#Jo$jP0kjccXp3pmE(HRvymoPo?bXwzf;>=;W*qsP;-B2C!A% z)AS2jl9!z(vt>POt}6z<z^QotrGTgG-IX@M)YQ~_i0Ih)4a7jbuaa)b zeUUs><`g|&`)Ie_`g0W33mZS z2cPRM7Et4!?X6$Wc09_Mn!cFT^Pq&Q?0DqB2n#|aOW9tQQpOdzJHU~Z8%kl%w{r2f z#(?hmz!W4dj`~4b9&m=p(9Wtma%%fIOV{M6JU{T<+3!yzi^?}L-u+Gx_WRHM%FOsn z1=PNrFx%|ggwgEoVyQ>y`9fCnaqW6`E$B6y`-QA(Y0_|ZyTxPxnWW3Kf56Vj zU1-2PW#-9=BMK_~q#Ve^(XETX_m$U6%&@GHRat*)XHo#qN4+nZ-+C8)Z1O?0dGco+ zU9yE!jSBhPxFl8N!un!uApXYI|53)Zaj5d_9Z>q^ekzK6R{jZTf@eWtO!B`R!eiA% zFj=YXMO5!|Rd-$sb8~jjfj4kK5aqi&s0Kuq4QP!r>LRX;s1}OP3?Ch6*p7d*liWCc z9ux61U+Nt7&^KC1oJce$Eu6)hz1Dl$f(qV?`dpEcsa1{ZgXj%bPD>a8GGm+X6SJvA>$<>%IITiz)kx`-M0*78246Y>Kb#^CL5> z%{%abmb^ZH}A7#x#7#r?{}xLNO^PD){)T0 zqXf6(F2ky(%OM@72PiBO@iTgc78_egNRUeL&5tkon}=hSO^?&oWrjV`ce;G#`3j!Jm(8b>c@Cmp`%XGo$%V4prQ{y+(CfwYHj>p4M*H($FxD+tx;O zsJEG=5f*M6XBAAZKmC#Ba zJY-7(6#@qyRl2*&X5)kqLSj6u{2iD_j?3$P44;h`!HA0!vs&v#5{aPcHIT3ZOV_;_ zPe@_DCH_=Gh)xTKjS1=$gXnF;{~_%wfa2V`Y)ME$f+oQ|BuMZCcY+6Zx8UyXA%qZu zCAbqDLU7l{U4py2HSXQ?JKQ_-&zqV1s^-;HHC0s6O?Q9aIeV|Y_S)-I`SlvCH7S0! z>qWG3Z$`z+=;JHpAcy?9p{1B~^b3zlKwv39m%ExFv=N@#8efopb!u<* z`zW5)eZs1}C4HXjvKZ>l{y66-esV87E*3acNa6a;RN06rjqI}&s6YMOyZSUBq%RJ8 zy1{}t&9^N0YfQn>*;%^mtlGroAZdNRdNI@JLcGj@z30=d0gXn(-OCN9x%ssY=M`E&MMv*n3-mReVxmk znSFMu;O0A*yF3!X!(jTp)^bVeG@!t9ecsoia{r*SbjY#0ukz=p#@Cr>+1)0(`UvJC zKZq{t_b3Bun)&v#6VFgjv_u(JscIDcvZlpsinE+&QNykIq(nsgCJ3CoEjvex;WMG8 zfm8>o>m;4sgQg~dKuiY84+WDiYlkJA{ObSSXwcBa=2F z!0eT|VzGYUnrXyvrv69--(J+@}z`=Iyju9G({^qDs%a4a?!ZulRePm7>#7*W9h=)5`|UREe0~_ zdPZN>Va~%<_;&dvKXesw(dyRV6NgcA=eM7A>#FiqUcNF^XQDXSG1=XfM<-~_mbC)w z%7|mDw!(vSocH2&MJg!xVKCU9ZFNK$S@o|5y}cn#NGTuf1yz3|c4dlGmEUM_Jwjrz zkXS>8`Z)v^`q4F|u#~4UZS^?Tg`l#Qz=>UFy6VRae_AE4o)&W>+oy7~A%$!y+$!67 zHPbWHPYNC+hFZ~}vwCZmHMuMKzUIBcAMA-s_>F~k*;|BEB&B315&Y$1+HJa$BO|BZ zHP+1S<%^4NTnlOI@2)L$svGo}+?h%%hr^|1;Z=YCag7RdGN;YK zs;1baByIDnZ)^MeVM_UwR8-mBj>E%dZwb&f81PnBu~8Y6_1uz}?dCsHa|h2fq6`~$ z@ zw04BV(NVB=8JE7>d&^kbRZ-*B7f-M(Kjc7Wtsjd>v%0nu$F%9D-cT6U*C2DL67D3$ zUbq+NM_64AKUPyfFyM7dv8JDU9GWb&sf%}#-lX*^O~6kMW2R;3Z$u0ZA{6Z?&huhT zbyed-@dil;J7BqO#rlx&xH$ZUg$2+On?Z{N@c!F=q0q`jVB|As>VG`0=}R z1^W}zo`Yl@F-F>{9rmy0BB;-51!I0OsYB=p*tiKQuMm3c+4JSK%FvbFh;!&cfMiJa zmOJ6_?iuLlViUZc(0Sad?=K<~vUBQ;vP+^WFOQX2{kW2lucoeUB;AOYT81P5};Th7{$KUjoZA@K~$~?5WWvilI+>X6b4r_Y<&dYI>w_ znB^4VAq#1Jf)aTSYErLtC{u6a63Ne}1Qkkh{W_jNmp=81d}$TS7=7yID`4prQ(bdG z1EO!Dn^U%jddxud>yKk&Uddbz-|iukrPATp$#kejvUaaaB6%AZvBjuU6hDY)Xt0|Y z8L1^=q1@r|U~SJn5;3t&Dl>m^L7|Y-DHf?LYl=W7X{kKqnh|Cs>eY!Z{HSyrSDrC$fzwqf8?ND zWo^{`y`{o-9#t|j4`jIgnaavePMewDXxga9kMq7!O0wB4gg(O9nH@_X-Tq@)`S}Zn z7*m|!>AR4Xm55%z3!R$d7aI1B?r~zu zVY24PO(QQ3w014ei)*|4-nUgtI#DK|yX?QPydX5jE*`z`;M?WsMuGUX4FHRH0d;<1`*b-9cEPOG=QT8{3i1$imYz?Um}5)>fn%TVBX$3b2l~~!wcocMr;gros370$EEz0kHiLIMF4zY#*10G4+T_ZL5-VxM zd4<0cXuEfr2ED)RDsa|m{g$=P>w&-y(fdfzy~=<+_)$yD(W*n*QicMn)1jVD3|Ur| z?nXh>iSc_ybNw>!rtqte|yXl7ItHoMhC^W-Ex4Gm3F?j-JJd$4kU z{M!&JX*`8;Q`yMKNNWzQ8`z0#-Z*q`>PV1}Z%n5)vO7cUU?vs0JfYU94M-=cvs9>OxW+Rdfp47QR0l9d7 zqG`G`k_f8QU9YD0o1X6|4;k>Z26VsCy?9C)yoN1Nd)Xr~VP6P^oAY>Zums)@45IOI zOB0WB49Qd1LpmnUS>7w!NI2sy?9zenI1T!pk|9ktEqp3ZN*pYxUfC<6-klU(QG;)$ z^TFZpcrFK+NRmoyEFl`XmC@Ic#b(&o3j!zG`K-P~#PZ6j#LkeQqwo52D+Z8EKR4ZS z@FU!C7)))&NY_?!3--d1f!1!YQ81$FM6f+?)X`&9>pA2)Ir8!2$1f<-oxLg_e_A5Q zTJ*C9w7)cHYbb$48=aEE2nN0+frlSUpzw0mu(>fXx6;g3;wDCa^l<5ARL>mn6q_+D zbvN6UslU~kJfp{_`^Eh5_>^I~wYrSW4XShM$!`aNyZRrAh2Y)8A;P~U>ts4v-{Iz0 zOd_)}n-y%R=ngtvl-bkMJoMh1)bonTO@p?7ea4(Qx{b>43deS_skqh`P8~%po4=>> z03}l?f2!e1`Fx+Zd8Trgo;RgyWF%X16}bagF8vYt3?eo=R&qZ)zu=?h%vEp@aSb;-SU zOt1X_F=+ep)nAO3QTJM4dHJBkZgH~0X126!^x1=Soc1N**Gm2Ab5i zmA|rb%}Pv8;>_8w&MVAEb8ri_5XdEm@C!jlj%%HDs+;)hKbgFEA1o)*@2~3ORC;tZ zVvid`=h)0xXit!Wb1EDM@^kzikEX&wW;;~4V&yKwoN4$p`SkPHElmLLU|U}!(FKcL z33Ta!(n!(N{6xr!Pl?7-NJ-g#RAWRWW5IcS#Q9Hg5i^c{?}|#IZ2qQ(=*b(SVnWu1 z7i<+42~g#yBy(Q}k_08Fqn0-}rQ5zxfR6PnDRVF`eXK#LMm&d&_>XmY8rY zepzWbUO}iaR;(WfV74i|PW}fap;2d(=nS0A7k(yAWeUHXj*HXS!jl>7*4Uhm2-dve zj&{QkO!&s;bR}rYPRpLkIUx`{qS^6FvKp0flhSEpyeX}0Bgvs%kN0CxZ<@7DPZJ5k zTSc0i$OTsl*&7>jJhkQePqsRd*3Z`e&lsc4b z6;yOF-wM>p7he3XahKd%^!qtCVYj}%4yJ#M!%2CY6Icb0N9Bj9ibV2Nj(3YE-x7qb ze^0-=)2~5oq!LBlLP>)V_tv>@TAr{wX@i0>kQfB+Ey?usU%h{*4g-+F6lPhI>jxbd zIdC0l`M+gOK(mcxr8$#Cnq%D*IJPYTjph!t>~dlV<@&)+a!k^<4nrnIQjl|l5k<%6 z*?D{C(ru70$zN=Adn>~-2IW1f1zmP<)P7$cyjo}`@N`~#9q;21ar&0MRJxJg%0V}* zt)oXx?ylOha9MQC%&i2DJNr7MA4i**jqYt2ldmQ<6u6ETD|xaUCt$gknWBVJ^Y>gZ zm%_*H+s+LvV!sp4P%p@LM)i1B8x@&W3^kh#t|bUP-%sT(9TML`se1U~sBc(YgFH zt2!7uW(l$r@i^U-`425fzG59WR=+WdyE^Vm;3KM1RI09hF8d|HW%R&d)AJTr51*5; zt+m18k5mub82iGG`0AuaKJqx1r;>Q>iw=c@fK*@jIw8R^gDa_ia>@L-{YhlHiX;OM z&g16eIA~;SWLnys=E`(_D%{u`?H%#J}+hc2w%CAd*~M*2(!*YCL-K zv}O}@5d&FW1njk%rfPEU#fmExDlKtSJ%3>sbv7uOho|rg4*b>}^;ij}0ibN74eQnA z_3k{Rn)ztPs+h%kN+DY6AqpnROFnN358Mh~>uTGf66!Hxo&t1U)0v?{Kyo&21br@6cYLhF*~D`WI+*^72O|%(5ofO&F*5Fih+I=-k(xs-jHc zo{t<}*K#+gs-E9RcJ`4RNXMA&-%jj@8^1~k$}cS4Ts&?SqgqW#rTv{4{SD9w1Er*o z>POcs)Nff~m%c^Lx-IsF%&$Z?xNg4RtROYFlmE=BQX&0|EbU8Am({FhiVQ@H`;(`rOf3`N zKVR=wc=Y3y#GgWKb)dPBPvRg2oSeyy%FQHe^u%(T#1jmgp6(w;sG&ni_+RSlc?-2F zjV=%4o;=R8#KuCqlV=!to^FX>Ky@v~@+d&pfT^UY-c5PfvU2Q~>L?Uu%tEO8>vFWp zh90E5*~PaYbuE{!JqhLTWg+v?C5MkAnQTGM=TWL38#B&q^)@&qf&ez6E#@GMNd_f z+SdoE)4!kVX8FF+qTTwFU9~axT)MHcd((WZ$Vdl_mr4>JBhTkpFfsB0qgOsx#alFy z)Q4cpe#YiWGMdG5TW5R|iaCVHJFy>s%8P7|}Qrp(!SnSic>6PJg}BEg|LBEWOgvoRG+)Q;4<4IS^=KY1V7{!<7UNIgY!M}e>MAV^!*y=!ac z?(j2C&5{?%S0`=pY!-A{Hp`v?h0qn~<>FB}~m>8R9~4T6kbN)9v# z-E@i&4x7*l>dmwa9^h*q=w0R&u>L-&M~6N&mgQKP!$a^lvabZhrF*+0mN-A!z z7jcc`Cb5^$4)U^tq`N9!^|?o_>!9GZ^4pXRp?cd?mgJb{gl;l{WILoU%nljsQd4$J zd>G}eN^dSlzEd8ay1hJE2)HhXCP{m{WwCwR@iVw>fL<*$-ne^s;A&O5?rV|ox!3%0 z|L}nwk1?@puC~0eczt7I=7-1C$@#f>z6yv%O2)NYTjd1kk3h+Gbo8WSA_0h>-7twc zeLHhxwXc=}vO`I@KSc;=*<6%@%hS5rMezvV!h+D<)ARn7JX&PmI>$-O!JH)3ix(ev zC++S(eX0<{>$Zm&l6|d@$B$u)2&IQv4U z(JjddFHk+=)&8CD`qQRwzR$g&_t56tw)CA^VvlF;!{#=1HKu@sR*od*Ux@P>UpSAR zo}RmxS5#{1tpXo6cVKgKvv7pGoE*i=myz@J7XnH*>$I$_*ik*70siB(onxq&12ojv zZ~^J@U#bZJCrQUO>3^vG_z?*QRV-N02&}=H8u$Ci9f@clFhh92%E2Wjme}QBRD6vC z+>Aja?&&~-Xsm7;`sSDv-~p}(gyf74!Nj8z?$T#bnOjf!l6RWIKEf;r9BCekj|Sv( z(yR#Px)oTs*FJnfaU}WuAAF`1N_qNl{8RG2zyg^Cgz(8@$4=hnCuZ57)nicHs534$ zkB6RFf+dNHADoNG-U#A1@mg=nI}h1`@GB`FIIOhOK0<%({%awYah8C^_zh4G3rA9d z#3fztYEyV5n^eVO(o1>&{;!vk&j*q?Glc@KIb61!c`CFRLB#z57Lrx(Ua%ltR8RYG zrXrBPq9(|QhY^hvvTbQ5u`p^^eV8gq{~XA;R9J1wYCA`e$Z4nc!3X)#DHz=`GB&|& zz?W70rHZx$)pbyO58`L>AOKCt5n*kkqX7=95)^TLwV;0PibyNzWY%l=Tpht_)YbU8 zdw98#2$E-uwhWyiHiA!)SER$5TX%B-}J4VR-)4q2eTH6Brhg z8XrVp6?v0I+&sV!OiT#4SowAOf@+^dKPA~rIjFM|+8)p6+Jsmtl5p6^H|zW4EY5UjFueQ@3bS0BTh_UMd%AwL zrp{ec*B4)vj#^|%2pTnqXF-In^I5a;d)IF@EP^(Wo>b3W&bmH%Kr1iVjN{#^_3FD4}NpP7&` z1CFEwUd~X}0=0~BOAxx-I>XYYNtt__{gK@_Q>I_zpaSmyYcRKn(g@=tba%3k39OqX zh8>QEhQ2S}9?=1FqFi6N=eyfB7PTTBA&n_-78aK7?(Pp)$GD=R-tQ)h8f_-@K!N<$ zVdYX{!T1e`LZ3g6?5|URSk5J|eL+qPie@EY;ZMs-(q_2bA~*{b)O%9PG? zp{Ldj=Q9O-aAz9vQ1`3!dpnCMQrU(l8}ifD2S`?vMa;J!?=$GueQx06C-*+ohc3GP zo?m__Pbn6R4wT)gvhjmGb-h1_s!{EJG%6 zp_440OoGAT13(mCo;Pk80)arNxVWb7KP4l(-P|U_3#xIVcpUyq%ncXtag1-O?zjNK zTX)-U7eU9+#Z~89aMc2Pj19N`;9ss`-$03?tAR7lG0>9X5fJmb3jaVG=o=|95Rqap zK>~EUPixzv0iZZ6W~+<{O=<3~;q$jOPd~qGC+YQB3v7)DxcjfJw`zr2%G0z{Q;lB5 zA`CeC)wV|YyER^%G3xp$N{^=R(r9=;1)eh+_~q_#c!}-Cbh|?5H6W>&6m?h7fQKI= z;4%LG`-{gW!stv7gkDd86eTIs@Xm1MVbisxl}YZZJ&-(F@C&k@8(7DNU^6~2gWjXf5Yv&1bHr7wpQjL3g=4>j>MW80wpEerDes- zjZfl-Zppoij9wJOvu;+iL0S$;N{XGZ)Ge-mKwMRw#f? zh$>{9E0y^ST*5RF<}wy45j=U&&n-~?r*(|inx=I z$WsVc#OhUP+?=G4LS?LubZ`-1vhMO1cMyRNo5gb($78$pE-EB@_%%jS4fyWi`}IK2 zu^jV6UPb)drvN9XBjN46PG-r)d*5S{v{rb)$gYz4Z@H8i=e?=)Rup_HMU~rD{To=a z?3_rwxJbv6Fd$;`oIyj&{L&bCWv~mM2;zS99I2`4p#=aWz2ee7VCFe;6o z#$k^FC}Y~i`@dISm-)(yAX_eNY>3PB6Cwf6ns5O2HaD3i@3t_oCc?>9YVk)7dW@z2 zUQdtA=lV3m577i(p->IQX3&F?=g1_8ii=Z=1ckrgtUmR6hZBdGsr=!6F)zvEY%Z}o zQTSe7-fXTWSf|mma0<3RUVAbMRN5&tsqUZF){F&UZ$1@67IaO|;=~;`B??{5wnyA7 z7Mn;VB_-z?5{#XKjINFmOZ)pBTf?jOG^%ac0l5iOm~w$@b`kOIjWSTgLv2XPiGmC? zYv~Hp0G6qTSnPu?_eGt&Njx4xvnraD}AemjEYXjTL1RSu3W0n`aa?-C~{cj7w=Ks4C zss=rB&GHS$TW>x_t%_{1kg5s)ht2<>^w_bQa5pzs*7GoVuBf>9*8R{4i2Tt4TN40i zolY^bI7R_nGaukZKo2M}D@!|+l(!cO3&AFuiFVn2saa$H1SmgDT0i&9HF{O|?=Tmx z3=TDT7}V6)kN#5S{etC}@bTj>j-!$ij|V86IT8`J&Ib!I09mm3-LA0o+4*~!6un%Q`4p5*4KX4ArFWU6ULPspRLsINs9 zbY|mkGt_HeEGQ-^Xv&j1i*S#l{_?;}+*=I*L>2p-r}c`9_miEO4_=ei8~=lUa~ zEL+nBUM9Iz(LesNV6*$@f^9XDP{8~3j~`ZG!ATTPmV2IQd0s#*!t|TG11l=nMn*DKjckDB%V zzCEUtd=6xuFJ8WUo+2j!$}5R*l5*Op|E7L0ubg4&TTV${9g__!xq32mRV3=d#m#*J z@35(`VtY{DVUBIJc&ShFO;apYC_pZ)ZQ04neWwTCx)*?;p|JW=q`4a65Uq8)u<4U4 zwtwj;U?!7*?|?3x+AxP~9bO6BJ_s88aC6bNFl45QS#uXjyW&L9 znMOufd>18o5EkzoYa3cOjL_DHJ_Uj(t=`iJ=Q15E< zB2U+x*PEDR(CF@!6)W_#&+Tn1q}%51ahE2 zl7I*Ye7&!Lf4+AQs|aR0QxQX}O#SSs0#g%y24m#24Z`p)cMs2`* zE3~%a9ZqPM-*p8BlHvwD2|sdAD~`$OTAV14_;-*uL2APM#t!UGW)k$9GPF;EU#~H} zUMu3m=7}A}-&=e<47PrGdGdUHb-C4~y5RsoHLZ)ufTiOiCl1QZMOp2P5N3*V0+2PF zT+kql(}Jj#b;!B1NAimfli5y14MR&G2nGwe%4$a08IxNd%H13a~!rYHYHm zryqv|7e524GMp6A)+RC#Lm$kbb!r%wH%iMz`%%&;f&;n;7@C@{JOFsEw9b;wQ{|4{~xo3H6$avOkH!NFJl?d=6%ge2|yZ1)~W z$eq10^pDY!QYW91hGkzSqrAp8YFtB6RWR&zV;C_o;wDl9d>ns2osOm{d42Z^@-cb^nQu^kylKzWCyPs6lJ1l zzWJA`6&)|G(9_#TJG3!ko!A|x*TUoZi&GnJ@-ZjxUVu>;vqYI39m&WFscaTzPR_{> zz#=3yHI=$i&+ho*f>XcVbs$5nfbsVBHg;+01KK-x_sg_mI?X#7*9(~L*}-C5R#w5o zTi^mwSTDDZTNY4AvaptOIbIS32T#&*8>PP-JH;}XDZe6i^2mcFkZ7XV=(U81i&!F} zT1YpS*Y(1@5;!&7?C;ZBsQ0o%mX^e=7hv2h3Fv}?`a~S)r3qeoI&(jMYm}k`MOB3b zD_w&PKpT}-oY7aAw)DsF(hHbuD&LQ#rB0dT%fxvXMCk|b!Q=!kUS2^Z-}RpPjv)Lr zkF-BDo^L2Jv`lhefv>Id66E=snwmE&9qA<8j>!W_OiBf6vb)W#)6)rqx;zehTMo7h z4INSP97~`|(Y#uG&z3l6Xo*MWCYgWKPKyO_RV{d=8IhUkrV9K68&x z<<{8hAkNG8$rF6QG6zd#uk4J6P=(LZvsKW7JIy@lLw$ZAsIc|O9Cz5)BQNg)<(q(? zp9w`JqGtFg@3$b%e>qEYfM3gk zJ&w-=`=nMt^#(YPV3XPZC!h~{T19KWd*xDiDz~RfMQW=Yw*EW=vr$?W`-0GKJm4** z%s2%E-lQT@F5OIWUxJImg>+X@fVNefZ>_bPN4AorZBZ2o>+{}7&~5mo3ERlz|KT5TtCjXK#JQ|R{A7#s|?4?W0=?Ob{e4lC*-Muc^WWBw!Y5J(FWVJ z=j&4dXp?Z21WCuO1b^dcFCRMh`<_Pm7EnxJYPSWnoJ)**?nig*p=-s;rF>hNcf%j4 zuz39Tt=S#h0BEH`2QxFXjUfc5*^uwhbonBypDzIj5zy;;7q2fE&;-3urXG)YL`adgT^P)QDg5yPOKN{2o?5uURKJ zO@^?fhqp!wj|OEWh8gl`(5jtHCOYx*RDDlv;5qYW5>R5X%N#kR{;Mpi(aAu2fI#Xa zurO?T>s)>?=+t}`4@+bL-BRd6qg7Z0V0&%n>qZLH{bnk*zZMrWE9cG#>Ni!)R3Ehd z)^y_b6G+8loEgaw(*{qo34>g1?6O&oeU(e8J3bu+x&q30&V_hGH#l&3rUK2`R}$eK zS?3|Vf}l7BCIFJ6Vm@60z7w|8{R0AlR1 zd}TDrNV*CeP-mwba)V%%t@Lap>n(59f#FpdA~H2rXh?6KoNjYvMK~gL$bRiT4mg+Z zISVeCSk49}JM5E%S~WguEt-1dkUjHm%UE?d1+(Ll^#krYw$L0t%+#=-VvWAIv5Q;7 z%%wgza&oDBVbj(dBiVh)0zREY;uppLo=$FsXgWnIn?9uF*-WKX$=sng2C(QMlZYC z4fW(trcppF3>a>puQ2l&SX_imF_)W+3q&KXJaiV)J{g;ug7Xg#Pfpf5!;%7HlR%#U zNR0EfD(L_oH`pGz=wk@#*kM9dAMqYsvsL7a*nz%*WxjGY$b2N~fpwzX{|RneV$YVD5Q}_4 z&vDsR12VZrPZx{HqC)m2i&3>Z&g0!XRRE)bVv@~VVKE0YMJ@;=29R?cYhmf^A{`we zPB-T^L~Q2oK74q=#KiRA!2=B=fPxHRm%|_rf>v2C*d-_HakJ7Yeik#;o3QB4)x%qy z&i}5}^qk0%>5F9~0H#I;257)qWEx z)04`X6jI*XprD{cdiCd;69ecS#q>3shW* zupFXKCW{xFd*#wq{u|mJ{5Q0%uBkZ~DLpTAT z2LSWH+ynL7qfc#&=B_w?4CZn&4b7p1b}Ftse?1MS)nG#B^6F}&T-^=0BZ1>9A8B*= z1&e3i`)NB*vo)8E4TS!{|L%Cdnnv<#@tnjQDsJXq3buho_db#Jp|x%v^Al7(93rKg zG;}Bz7W0MWTwg;=T>$ob$$BMYI7rt(66pjK4S<(mvnEj5nmhatEn5D~8G;sP5G29$ ze)0}29fZ^fVr9z9_dhAr5E_5@%m!tWQ&PxjAuBV?Ll^3Ig~BZpiJ|@S%1Y~3{2<4* zZU+v#y7z@z?1ji^9|3`0W_IBVU67@EorlSc?h#(^28~SgEPh^&M<=Xg$!GW5=XrMd zH`ORxT&+RLjs5~3L6ZT_)vt}NNLVp~(=#_66KUV=fBg9q3&^@6y+#e=Y4{BmpDXdl z|DepT%17470qK~^U(dTIKhBke4p(+2Da@}5I=khiWZKe^*%(~ z7YuT3KhQs7myTt4T@Jfb=)e|zz3?Eg=UtH7v-<$?EWxhIv$E#iF%3aDoOV=j4w~w} z=g5lOp?E;(TaM*neyp!2hTM7v16o0Iw?H34re36zS8`!~BrW)MC9Us{wgrK#+}fJ2 ziHyDQXp{GIspCqq^J6kOFt>R50Y)_;FDpXbcd!V^^goqX53H{A81*+}^mczkW>2nS z{)N&RHy2RBO^6u)3W$H8(A|<)K$0f3lU)Z8s?=*z0C)z{(`u(@U03)bgC0lqL=cOS zkMcRJ1SgDX9wc>VoPkfCW%knQrZ(38TI&*v6LvGYet!*3?mTEtme*qO(R~c&YUs;h zSmXjy=H}91LP*&>AsD+0``I(elZ`@2sk#nuQpE4lf7T`ugDDA<`MUR@xEIb8mrZzM zz~XkWz*v4QT~Kqv`aeV4`mX9zA$PY0pV2c8u%@i`OXZ-?liK8yMnxkLFl_$dr_T)& z=y=4PY_3`iXD|Y;e|@5G773_t?#4GkL#1qDGFP6t>1qQB*l4(}Lz){|)!{U=u+Q~w zW%URr0hYo0<|=5VcP%>=Slq0gu>HDX3#oFI$Q^32=9p21 z@Tcj6>~1;!OA^$g-zj^be=+S{5rA}6p~Iz+B`P<%VTxtGVcxbNJlz|g&yfLSSYNDf zH-4I($sZ?VFdHgPWkMgV@=usQCHJ2&e@whXkIi3n`VkTs2wDY5FbsoSAR1`!(sm8vgltPgGPiV{{u(_?tkrz+nOk0$|pY#GvtX z5O!rClgNf!sHSbOKU-ii`qZ>PUKX^)W0?`n&{bFHoY-O$7BuT@do%=?Le2WXc)7ds zlZXsXuv1TTk5FP2_UY2SU7d+8%5BSTf-d$122+QMht0FV?Ey|5tEGBzcSHrw5XFSf z)|qEUxQ{ewYax}EI-nB^tfe~wCnkV<0p1M!PTexspm}*;zr}$hJeZRGB8N8n$@nWQU|LJ79PD| zyx~|B_3M_8gD_3<0e8GNL>Qn)r{cZL68$d{-e+wU85y~+@b)+w*rD|pw+;=(5p&w& zX#t{|R#YM_8>~xUSQVHpy~ymK{jd>oUS)?BmA~cZgqpMu7RuE|5;CwPK?Af45NZ`F z|Jp14FARNUefUdkSjx1LJAx8baAIM|?*02Wj{^LqAV6tq@ID zcoEfeN6X_g!|s2STJJ4c7CF2aN;7>9gjj(2(s_TG&7zDa^;6`~{?`r1{o~yH?i3sX zSsp9c|71=}IyxXC0;?;6oSJUuX;D!TR)-4@DHv)2bKKovHgN)=dLR=^U@>_P2C_lD zx-pP6{8PxMK4S47iVK_I9WXn*KJNGbBk_43_K8(erG)Ub8ToZ_(D`Vb_I;caKEj)8w5+>st( zk~H%k^oyq}EUmALf%FH|qHjMmpSV`SuQrnu3g)LvSN+`G^J*Qbeu#xERPtVcjq=v0 zlOdKt^Bs_}OG-%@uJ@`^{etA(PGd7mIc%D3^m79uG&w$={Jl&=@7QPi?{s$G<271AvI_8ETGcqD|tObanKOq18(p$;c@^V<+@#UHyG9bMSF zg8f(W|NpllIrrO}_s~&MMKm7`r4T+MFh*(dS;Qa18DOMWmK^;papFKfsUI5?8)Fl3 z9Asoob-7K=+^4&D?;ewlAS>%=_WmN% z?ad1>(#rAX+%XtS&|)nbU%5mQ75O@^!5+FEMhw|jnZ zQB4pgE^Tg}ch;Z4%Av2XuMK`Ll#HV0_}CLgqkmKQV}LP7?1@7)E0Js{5qmQ5@;F(I z5Uk-LtzT&L`VsAX{RL6y=9f2jtWj?IFjtP$?DCN2ZGz1DL=mgIyE$+;AzN*_;VJ*n zqFyA|MFnpDx3o0!MW2ht0%c%crPw(K`Wa8%>Vf@b9Qc_Wx6rv8#kVb0fc=oTuHy;R z=5XC(1r5AdU-+%8<&KGZ?&@<;8lG+)JqF!~A~`W&PDU6t2#!;TPLB{sT;IXbVWOu` z0&NNPTwr*WLDF6E`fuZ_&&u!b{I2gzu$FN~YX3F~6Xpt>{ap(<+8hZLPC8Xoz^(;= zBWC~uhd;A%ivnF;>5wK7#M{kaxI3@`yqI$p?-f311)-C%d~6Gfc(WRyX+7NluL}>9 z%Fa+NcwD5@w35LfW-*3tCAoNBT+f5?*E=eHnagk*{;i1}8O3bRwm|?6A_qsP?^Xd9 z2TX2mPbR&FwNL0`7OJPklB<@wIw{~dMDT8}MQj08FVuG3PQtCV#_^0X(dqK=hhy)1 zuF(n_#LrJ|PgBVRyx){9UBI9tuZxVS?j06~N7WQhD>F=AQ4>|h3hYy_z?W#aJ{;#-9k3G?L1bfos{{u3(V?$xcr*V)UOsjw!2ud zTyKNT<+c47dxO)Y6_#@z{;)X$(loFkk~Uhj3^7;P7s$E&6UJwRsa7zZeqGN-E*>(& z@4k6cBm?_}+f?_|Q>;lvtsZ!kgKJ>h%E*Hcj2LnTh{|EPs{NNA@b1vu*B#RcPP>I@ zR`vUwO72Ja4H54y}Vl*CM}Et`MH3PJUGBgMn>g%dUf^i zWSyUn@6WcgDF0kaUQj(afd26Kc%>sQb#pZ#4IHno6JPuKnHVjLb)}}AUUX8NY@fi; z(2d|m#AQr%GC1k}ZV~WTZ|R6ibUmG}Vs~($d7CsKT4f`m*pKs$ANvY3$qMzQ(BN_YnVv%l*plE3o_r0gwHxUyjboD&td<<>OAu5KS9Hc~u zq^z+5PNXRpX9}!p!l9lMubwgeEK_&s<1o+2(Uo3Hx4#YJFlxj$SKd3L-`@I$67i#l ziHmr{Bwb*WXr z9(hd<1Z}*@*_@Oie3`g8Z?TD(U zf5RFOM!pM(QsrZuzwS$QCnFRb1p4<4hfDsyg1}RgS0MO~)85@m;ji_dL;wBs(_~>D ze7Ad*TC&#GY|?Q|IhNyeMRK|QV|j`xCVfpb92_&_`G37=E@sYDO9g;5{){@sd?kYI zZ9;E|JE3PI*~hP6O;0^3Q3EK3n&2C<+QGrx8^4_G=mFN=7|=es-YsUaopX|je=}_1 z!a4l;*yo+T%InX0&SUcMPzLR$(H-XCqt&#{X8!;%I^0gR|JSLnbNe8sEU&M3EKwi> zb5N8J{0^l?Pj;`a6}wz{L=V;?vyAQ}g4k~o`k4kq?!Lr|?K)mci0MG$_hfJXOusNuN=nK)u1sEK4GpouGo$WA^6OS*;3ihp(%geEPFh# z^8X|D`1{|>%F_8TuyY2csivx*U||_e9a21a^vH3eKkc`6{k^E3wbtKuf=`|VJ45G! z7kzJ1fGWS=v`e5taGO+!b754LhEOb=txXCv{WL^I-xLy7z>n!GKZI+?p zC{O0g{?2v6$V9PH{#{nkt>@UAKB*Tm`Fr`C)OO5gIP;LoCb64d$rr>QwJ_YNxSov< zkVQlYcC3g=UvBJ*0k20Va0H$CY*82*YZE`CPR-*&wZ1PHR$Ge*hoC-;W82>3Ie3=# z$(tF5{vQ5!@{9B_&{UU0B{rBX&yZ%{_@-TS)h$gLNxwg`SH4E+OF>vk z`?BL-i@LRT&TtKkOO*M{G8-G?crS(iTj+8KX!!^{rR==+G#Egc?kHJQUH;=$(4Ech zd|g=2kQ*#qE=IMfVbx2}PHoHBPu9-Kd(7MxdhaofJ9sL; za0Y>2wDedh()%jYli*t{wau{P$-v*4wxFh?B5!Mp;N@(Uc)>UjuYR?jO`KW7X{-D(mGJ>wr6y&wbUM*x5*!^Wl@Czzd78Q z&TnCyj#aE(%%}ka{bsc{SG`YdB7F8HQ|RdQad0^P4{2{1Rn^<>jbaA^DkVs$h%|_F zD+nklf*_4_ch^#p5ClY|OF&Axnk}+jiU`FE= zbWjV`TO8vlnzpu|cnp;y2ujDaYvqV;XCaf;yDO#`+xRpV5B-8eLPWP_+Xl<*_+TbR z_e%ZG^GldMt-feg!Pd9XMCn`4!9$$}DWw#7{bX6p=WpL0_d|x%qQak*zbawQW_`Cg z0Mr5FE_~&@{*N3MUeb5)Z&jrC{M}ehntuILBGu68PEX=0S^8-Xl!kCn{P|OpyZ5Px z|NJC;IpyM9Rr3hVl(U zhg7}^zoE2Qj*h&wncqyO^XI&fyJ_B43MZ`g`sXvILwO$ce%fgol`8Q8B34!t=w5of z*o7S&8?d|Ej=Ad2YZvwK+WE+1eG(i)?vK64nLMY| zn?8#B)(9eb_4D%#kJ8C_D{-}x#&}C=*U>?)~AH=*uGA}XwVNWEI zh1B0`g39aWty_;JnlJQcXvo^z!!=Kg&t}~Aievs7DAu@fIFQ-IHACb}qv5MRQAAuC z?(P-&hADh~UqUYwtXRX}@{?6DPeV=O0K4+A)YGbVwFA{zrW_9kKmsaf7Gw zo~CwvtdY!^?BAm@Ms<`)BBiIRKv^dgQ{YQN*?Lca$%8^}j4Z%!L{-t8p3aCScbGA{ z)bj0Cm`)m5@6UrUO7V!jcBRymYIxn`8i$=@*1ym`AtodwWdu5Q}2cf zejGhhUiLj*E_Lc{Ok&KR#HdD!PlUz9s?QB1B1|3`i(vg(J%65}ib{i~ z>=)*e+n>b}?+OW_CQ3@&(g--FDAb!BMjqs_xL?4+Kgwp2Bj+@Q2O1!fGBB}3UW4uC z{!MB6eCV?4IS{pNot7YIAI*jB<3B6C?iJMW^Z@MrD#`?~50C}e)gJ8b|M}`kFVi-* zJKy9H*b*^Z=u1gJnDGnPdKR%o0wbK5%ioPJs;zl)CHoQYBK9Z!bGPwY^;NfBu`)Or zYRlUm{3@pRO}Or+Pi3;_X2?=bHEU8)P*pK(!89K0{{4IX)>O7d*iqFVf8^1HuShiE zBd1N>88E$+0IAKFn3znkSx^PH-B(71Duo)^O3Imvcrh|_kY7{qHcTREGk3_eiZhDo zAzw${;&9nb848wcx6=6c3kq4mK|vGUyXVfHRXgS2B}{}~4-ao%Ki95sDlK|qQg&TeT5+OCuWIKKz)wh6Y57zb)<`%FZaxBuj44*yY zZh;a)Eo9ZKUOTt5!pTGfaq_Cc-zDG*{(~}J>OX(c>XJ3Q@?l}b9^TiqT0+SgH3%ek z7Wz=vHp<}AlxqfX)jyX+piNT25rp+|5M_9C=n?Yol7|FLt}g#3`atlsJQJG zuQ|J!lNi{|UBSh@$MH(c_0Mg4I%Mf*B!@;j)rfxFj|U_JlW;5vf2O^x>Cwt1MJm1=B(D>?qoQe!PerTpy}S0BQ!pB~MBdb+C=`$XjAwLhQ8suhn3 zWU5;IhRsK2Er8W}+7g)Zsj9`S2L}gPG7u327clw;s;bk8F#>@&>LB%>EyF_sdU@LB z8Y=&CYk>*DTxWc3ckbWwOYgKf?doEMhRk0vT@*M*(4*xx$`TAGze*>nYmE8oIhyqO zPhs-#&o}~B!=ERx_hU9j3r2kq$ME}(aMKut+ z(a26Q`vWr+)Q=MILi`!0z$G})LPty89IBG3zcNfXZz~8^MLm5Bf3D7l@9W4XHJ?14 z+}9<0aW;dELI1k{-e;%(sW9qwib6-?ghcriX-w-#?I;B2- z9OM=j5mEcPl8KF-{_9|?>0fun=g|aIxA0EjJKRGjZ@}9CRyj9d@&mi~Vt@K+13nWT zZv$CXkP@}H$o_ZNpv5-_Q5OnD>@QZkenaHSwQF~vTQX71rs3ne^j4sJ?E8<$pri&=kD#)dR(6{fL?F zPINpSemXAVadAg2d?OQNFgIW>k8``f6N#Dn*TTM>a=L4PPtsA5Cdekw-+{x3l;g*_ z3&mD$1#48MqdA^+b@w?<2U`#AZZB*ZvMA6OKSRBt633;1h?_FI+xf5_|Mj>($d}fq zZ2c|#SA9zDC0fVX`pT6N>(x;w$nR5&u>Y8ptnq-6v9-_W&x5@z_h3}}$zIU> zE$BRMaX8SupHczH0lg+g<)R@Qc{E5vFl?gBwOb&SqXH({3KK9PEb<460I;j(|` zad(?PKzWB{zcSjE6;p0;s|BQ_O?X0~He&6BWMooXv(init+xNMlER7XQsmS7fUj^i zrrn!_O}~tn{Lg+2p9C+}XcNfh_r^zt{{0n`|Kp`{IR53CVfA;HO3#(Rt@bxRibZ9_5>XECEO}AU0t7 zyX5wx;7UL|dy8KeOSNcOYa`+H52bAVwbxkNP6fnD%qbgP-udueOg5!l3<`>asm-Vh}-1P4y)%c3;zt38j`zK6~5B@zp{BIrn z=eRgoYwN2_Dq`zPSpv{bb(Pv&0yhPs(ik!0pxNQJk62q$^v8@}I-e`|q0q7iRmPwc^@8wW7;$a|nAC+W0P(Z^A=x zb8sKoDwnkW+s+D)hO1_jK9g+u37%W%{6FO%rfdbSP9TAzMnC_??b}MRbpM1u_)fqa z`0v&)tv31`DAPe^s`xI)9|TrcR@&g8xp|}O(+44Z<^7dCn2Y{hw^meeMgMzOXZDl(gC{yhmc@BR07%;7VbU^K2z$o_djXt_Wd^V^fSp^VpmLZLPArjCxQNl;E ziMnITxo`e5QgRq^Fhl&&$FM!;P2$x`ij2+Kb0}}atTdl9_-oV0&0fcRQx)k%!(TT7 z(LM>Z(9DtWr9ALD&bVzpQqXX2duf~V3?aQ04_nZ|?YIZjE4n-eYWDi=$5>Hq4f`7` z<-8#`=~j=)`0TGi4&BY6p&@v=WTJhh5SXayYLC?oF0I3D2dC*Ejd|N~tm#?O{f)+% zii6qj)<(#R09m7s>^y#<)As{X5YE6bUsmv-bzvv0p|VGC#(LXbhWVg*!R}J8$(!Z5kAKC^em?gz- z_pxUY9to+pST~<{XMNw8i~Beev&bZG#6EhoYH*D1$G(p#BkcI8^(kT@OQt2u8K3Ef zdWG_q(LvS?9!i7B!b`zj&J3;v#l|0}0_X!z_$vC|w#`QF^4XipV$hh$>PzL2T5z>q zV{hS^7gJ!7Qq_Gm^>@s}q+Dq;{i237^ApP6dpj=C8V+(_UIuPXR(Lr&&+jh}*>H`? zWN!YB&_F%ho0EGQH}n=7Ow-yB>(+whOikpO5sMpGW&h#=NXz{6@tFmYivJz}TG9FR zu~c(&zzPA5Cp(NCZcuqam{u|HNIYA@)G>Un2QveXr~fb~Y2j@| zLw_&>`VSQ&Ob3_0FV~H=t|W^(xw!Ovd*cS81#Lv8Xv<~XWud3`_P78jk?|5p`w_LN zhwC*Not*7-Tq?2s&4snuldY9s*1@$N|B8d%aBgOiy;;g>S+kYpV!$eU$@dehsaU{I zm38&hF`2KV3xyEjUs!c!qN)AXuCdBkAd?@wZ698g$sSO4tvI{By*}}@TLvr7T0VDZ zbnayC8W4rw0EdU`Q(uO%2goYm5DK7cIxxz84S9J7fY05d8sQcQyGrPKYG&?n@gxe2 zM^gG!H5!qOfu~C`p_xwvI1*s_Y<0LzOL%q4b@@6d;*1sA_H=4EOXS{XLz zwqx?r?g*~PGoy2(d^6Xm?^7Z&R7Pw_ z)6kR{7QuxwoV~;o9YMo_tKvnsUkg&5lXtH!eip2#R73OFGezdjVsbN9tK;TPBf0>D zM^|bkab@EnzKV#*;RWp5vwBuqB3U9UVTF^svQzQv^V|p(+rBfFjwZ|u2Kw5ZyrG&s z?c>)X34De{?#f!s2+7e=8zWPvdJ@vP_G||x@W0*5@Vb<)uh;GNfu&SgQP7E(FtJ(; zrr4{M#Y|LU$0(ew5iS0h#!{>mS^rx7cBHYe6R(K?R=G#qz1JHJ;v2tTJgfeiA>JKk*={|x z@Er4fYujMngR(0}MZA@Ws!3^bAJB@MEac4Gg0xQyNyO)pMLJUjcwC(YB<(9dHbt+! zIMH!-7p-hw!3y8)b%#(1zanIFe9saYy=Ju2)+{6=WuFqRe*NMEsu7!U?=6`)W=9OY zZ;efdKrQ%atZ(<`y{UchK-S$GR8&Na;VrQO$Xu=J0nbIS|G7iXS2VYf;)nUR-6?{% z39&edea^sN7SYx97Ty5OB0ezSf#PuqZ}q}AhqXo?hgH|ygPmm(QAmRXYn}w)7)nL$ z3dJJO$^ey-Jk)v!DDp0^V#aGR!2A%~)wgy_ zoa^m;YufCjEUnAe|1L@TYr?@ejKX~D2oIm6nzw`Dtr&fG>BlDxy0N^~ zlo&UX+8IBrRDOO#^m4jFDN+m5u{QWKXlPA`gM{3duLb(D8fq*5CfOdXJq8U2RS;o$h7rMTDKsxie`hv`&2KIO7$ zc;pk{5Le>UAQG?0CW{5E$|ugwhtimEW==BdzQ`7f>RRrbk0L*G&T$hSGOis8Vi8Cg zk;W7TtQ=g~-ZF0WSU|Mhv3>|luDQ#*)MvMI8vbNv_^HrKVe$?wfage5?w`E+if zg8}=Vqtg?c{$oLMxA8tY5A{gAM9~$BiY+kw1WHPtV}l+n5b&9mgi>Lh1?rGY8q z06f`F1<)<>fjH#6;ocOB5()|y!$m7kAq8)$LEzHQT%E== z=)|%7h!J2oV{+!Ss-=;saDUII(E918$)VX#cfVWKE5C$W2CZZ$VoNFe$K$PaR9 z3d@)!?3D2E#EQ-4C3X{gJowy+5G9T8&x6H0n+bQ0B3P#o_aDU0R2`Ld!;lB;n}Iw* zZqR;w<)bvE|)5~ZVX!6iEu zPbgYcOw3C_V|XjQWHW#amEevTD6^#_^r(IeEu#4?~bpVLjzT;4Y!605LtZ*ML-XN z^ZYry!FN8K8~tG;mm*V5IV6OUqTMc&NP;Z2mzgTBPN^Mz#-f>u<#%fHY!(|mzc&)L zMznHU5kQIJ-J-8v^dc$tQd^nh)I^%);R`{fcFmLZ;R#mCBG+I~!CK!TL!G7Kp{KSx z_A0A;IH^CY?3^ceXWrxsHyh#8>}j4TmS&8wWZGA{4epxZ9+Yx!ZgUo~G(X5wIVjK1I)6)(=LOHyZIuZTh#U3vjLt8+~BJ*Hv z#=5_6M_f74)q3P+QH$~I=8$V4|`rN*&S=;m~X}U>DNmUE?gIJoPD>q z-{TqrMFvU;f52&eSBt=yL-yow2fI+dcP+(g$_qMV?6>fl+B&kGxl?@>14nxpVvq@( z3eJKP4aO^2oQ^jd$;KNexu^Dt?Oc6ZFx^QCq#VXi_g3*v=P^}o2+Lj5FFm{2+<(^^ z2FQ=fy2o+O&iylL^~Am>4tI3#<8V|<{pgG!UHl>-wA9fj*~Am()22Pqn`d#w0##3u zey~1OhY+I7ze{YB6&oyBl^_-oiMuxd2s z3&pbLUYSOABlu*-xvO1yJXTiN)MRD4e_UkMMyV^GRrOS~*e$GWW=nrqaIzLZ`;xVm z2(yV$TS1`4c`uWBF4oWDjp{KX(H55DU%!9;L8@&(>X`Y&sX&v|_bfWz_)#{AQB#?A zy->k1Z+5Y@Yu3rJ{lR8PR6Cxy00{BNuG zWDPV&trd?s+XS6&OsjgnQy)*%Y)p4RHL-;(3%uOTqR2*QMA0i{)%B)XY|Tbx4{08` z@JgDRGQxvD6=X;AIZ(jcnka@N$!>kR3>-2|_;9!uAK$vhg}gZWDRFKNIx9$e-^S5_ zy=S^liRRRm6wcsOO(YseOx7HIbZ{^yeSCyE;c;BQgG0*xO})J66sUb%{p|7U*SCmS zH1aIsA|oYUzkdC3H=nkh8_mTh7%+{gC*FE4#ovN2%ij(s@|&6Fz6uQblW-!jUj4?D z%N7bHbm8K~FOiYJJsMC6L-_s(6J)k82k2JlJb%NF3dfsF#ey^REGv(UGigXI$Y=S_ zY1}1jDtF~TRWPJEdL9>Id|%p*Ua2}523q54|IYhsjYku)gtY(hyC%<+i z!>_gg3!^|;Vd1ktwFBC~Ff#KvDkY&oi!H1y{Tp}A*RCKl^ExkofOM?pw|QMFMI7Th z@V0%Ue}0LvzyfcM*tk7JE9C}*MVh;kq>RiPID;}YDtF5UVfX-_cb#O&3wC{Y*;NXB zzG>ILNI|%`=5J-E9!u2p^x&uWIggDs`!dQmZ^>sW<{NemfT;XIx?<)Tt-c?Wk_wH` zTD`7vy^^o*_7n_=09z$H+C4c#;OZkQU8y!7wcgqJ0#b3!700AQb~cbV#5ao}LwPVK z2B zYoC3?w|T;GE({zU7qocl(>q@9)-Kr|K#!P;HpbK$vfum{8gGdUqbbniV^2=W5UKn+ zDHgee*Hb3V0Icnejjpc6SRyjP`*_X=Bb9-NFG{m6y|8?@xBlXxdj2h^J!^q6@xuwq zA7XOf^yQgCj9mzRP_4X=SRbZ1@0Gr>u5dYZexGa4MRDF}pwXL`NkER^a{A1Z%Klht zW7JoR+HiwJsBh>w91l|eBwZX8BixwruI<*>%0F33vP2=>4&3~0_=H6Z*M4o#Nx%?_X^|e0}A(%=Zh68v^hG56K~QJET*1ws!89l8;LH~ zmD9w3su#kd;p-*w<0@C`LIW=+$WnCZ(2reoZtVD`0WKl z1!0H>DZ!6g!}eN3&KLRGk_z-V<0_ioBN)(9qP9-wSzP~MHcE$#*EU=(F?Mz# zr7_QLX;*jyLfBcIw=LoG)^~UH_t#Ti7cJ9xFL~z)p+)JKF=x&l_T;>&K6)JqE34@tCbyrFU6`<(;8H|ksu7u*AHC$-7XL-6CV!OMO*rSj4D`UFmU0*mE zrtPiWxNlZE9K>I4#IbYnn#jt6=Wt6@*@@Se68F+tD=bNOictP@@BAaIjyzt1nH+ax={b>9IBAX2O&$p78_P@VW zazXKBBJEXHdQ*5HyhJOXr4@s%LTo<3% z<8Tf|{2t7499?r_bmxnq(ZIW3;v2~@$7HJZB5&7B@%63-Z|yjCO}K&164%i2rbs~T z#w0-@!bIO-bN>U@gUqZCZTeH|p*4-!kiKTSO-}h%EdAS-604oOUe^7ngY~p@k(0lx zuicx{y%lWhltGUe-{r?26;VyX;MBP1j+T$d=lp%CHuvUOz^>n@N9*2N{kzXWWUT7b zM-_?e0|XSIC<}=(Cx2+ZH;oEpxGV_`HHW(^ zwW=1CUZD5|Lq(5EdtbiV1_-*L^-%g4vdB1ZESXnBTx-XLh3LWGq6)2j^(P3%yUKTGF122PL42sYz9}8 zf4GgOoU2WU1u-CJf@zRVt79X%DrOm)7&b}-|A>Y=h%2_2NltzLz%NCkl0v`UH)MAT zTMu#U_ficFlQ^(>|0WSaPkYrA41<*O1q&M`|7i@#2uVq6AW$*e#mCS{Id{Wz;?9Ww z$JZ8CpP1%Gl~?1+39`Rke~Gs?^^0b0SVw%${aGEhMW@b!VvaITu^Uw`%5~O~?5BRA z^?Yry(H<(0b&kYGlRZ#gh?!9?0Nf?X_=sF2k7(+I=ny9ix|`EY&Cq^)1@jqdFrP_- zBNS`iVjlYd>ZE*Z&Do}$)I?$h0~U<=||oTYv~4qU({r~E<`b27 z_dLqemlmLyQ*dt*`@Q;rV~H^K-S6zJd2HR+nf+3J^I<4XkO1 z61xlmgDZ^Xv1yoHS~9_gpE-=W>n=WY+ME_{;T+SS7A{Fq9J9UVvew{`6}dO57eJ8v zxt#y2o%?DPX5er-xC1Pdt;}tJ@h!?*LN_pjo$L3q|Df~;jp_Nrn!j3{1PXizLcsOLRYw%_DydXDn?*2g z6J7vSes<+9(bil?F3O5Y5Pcs+^5se8npXxul+2)%VmVT7OBZ4g8ziSXL3YY0AKm%# zrF{c^L|MX%GjUcj4E?J+iMqTN_`yC=!R?|X3{8^_c`v$CX8wW*R_UX&iUog1`9332 zw#=spG?9k05m~Lb&;pF?)b)E)Z&a+;yaw>N&a4)&l%0R*0+@_{(*>5ZPsf@0N_Y?z z(^8PuqaHMCM)ysFinr~UR5YoE?O}L`I@QA&-E!W{k?z(AO0E2%TS90c(p!rJ%yQW; z--={60+6^PL|$Jn<^SYXSsZ`*lFUKzO*5tVO6rS`EfF7EQTyz`=xJd`{(m0guWrxX z;`S@oq+At~j%3%~nDhh`75ETFo!{s{(~R4Q*(K?|I!5NC(?fR-M~_v+=MoJRD(by+;}I02W{j!64~1zj=_Eyx62>(G&kOVyikiuq1G_Qd-3 z4(TtFE)tH|(&itU^k+N)t;JXw8qAb)oVNNOr6UV`o7c?erU>7rc;nH0xf)9v3-J?e zv$0g?i|-Zu@VdBC(Olpku6d1Y%xP*QKh*9B1|%J=5&7o(nR(@wdrKm6=H5%$>3X`l z4_$W2A}FY#)z_Y$RAEvTvs~;Y**;9~AOb<~xjWqZ4R~z$N#AMN$qv~IU4-6Aq*FW1 z5hV!4`qsZ*zn(@RSv*N{-KSy1TO)lBE62=M$eJbEA=Oz(NGMqr5r#{?S`XDFbh<`8 z=*A!_R;=am%9wB8?!Xv?+tHE*tI|cNz0~Hyj?nsJ;)Vcn5zq$zYH69;>X_Ster06` zxzX^{>l#Vjh=pu*tw_s=m6y|UDldz%1f!zWckc(RA;|?G-%@hudThH%*4Q=m< zi^p?+T5C+ss`}}^=Etd&G5Kc|3hS*`E_kMLPR)7>RKNZyjjYTRS0zxK94WTMLoT`>RS-0$^% zHO2PUI#XLlWO7U;2|L~OLT};N@n%HA)xbN%1MV9ARobp<_9JV;HFs%PLgP2%~5|nA*z(@E#4b9d@W4>~! zwOwvw$54NkdIw~_!KjOdZEKM>CV>{zX#|ymVNX1Cr9WRc=d!i}E)m%v;#Z&1=&QwT z@@F#vczJq`n*Bok7dqbFA;od&r$BXLp5UU_guXYD>zS?XW~_{jPki>D;^|WpFI;k~T1TrLy#Hl3 zsjj|u{YF1t|guh!E@ zJ4eBv?x{Qyv0dsv^(|dTVPtGl&EC=xmRa)Jn#Lo78EoxKTswXdcR3wyHNU^Cq0t%5 zfs_@G3o{RNFcSPkROqtazNa3s|BAuY?(Vj)YtzKxT*mE&Y0(P@6=*w?2dx$=2?3%z z4^+h3F29*+mC{(Xjb+g+74Fyb6By6cybw7y*M_;MuKTis09x{S*6xmpL&>F;aXQ9` zNST)zAGyk?x5SEdk1n~qO~q^_TD##TZ%a?aG`>vje<9gx*to@4*7MT@@4?o=R+vTF z$z*`+PJ>F-sT=#=bl2_;@-;!L8~)zHnfkFa+;P?2tz;}4dE-V~gb)IEtzwHAj9~%% zXtZAFruGhuV^c5t>RYY#4Orx!8cOMX3fUU|ksRqQD=5#OZM?(~$)h%nIcBRa{5Yy- zyyNt~M0D$+elj2dA65C|=+(>kGSQM@FCoF>##p(~2jJpCqYeoY&vC<`Rc}p`PXUG# zKrTM`G?QsCqp=&|0I5=#rn~?5?>4RB8$msrESX^F^w6MPt-GfOVq{&>2mBQ7CEfxQ z|N8{+#bObm=tOSg;pyHCoS0Lq;g(P{&mu5^+N!~3fudR$B!kcSkCsbr3dHPZMw9nG zk5QjTO-IMzhIN3f$M_k(O7BPE^o>c~O$}xP>fx#XZID@a`Z%l35Dpg0$c|R=n<_rz zF`Jx5r=xuB9b3JbP`dFQIoh1VgNJ6EH`FUzn2?hNODnZS7B;^+3WHLLbKT@PgDP!j z`T?-O9$y(CNQmt~r!oq)_ zXkgqo`<2MYPiPt-Zx0U-4>Z&ZmU9UO&4-7U<(dd@Jj9ncca`dFn(2jq%`h(vYPN8dG+w$UdIs}?BVyoFfv$S)B;^n0VlwlYeKnWEQy}N<+qY2` zYA~ zGK>{bmu<_PqACZ1jaL00JJh1STEX>O?pt_M{~jzhub%U#GPnJYIbz|TIb!G%7qiE1 z{B|SF-MjAqhle2B=dxgG@l%Lk2hHcg`SIsZEWqopjVZeEOPn`# z$78KTeOhA<3PC0BUW^)KpUMVQ^WDEj;Q6iL#(v)qrBv6|dfj8=J`rNYD}^b<g0GMk{4&r~@DD zql{9X5jj^nO4;`ZqAP(Cj1CPzZzUo}pi*S|1Rw?9)HWwzyn%zdL7mSVDCWV|twtwM zK*cZ$R3oh5OZ7lHvW6kMq9Ij|%5bPV_h1w&+rT14Y>~zj%`=>fHUEdrjy-G1;QL-I zv);5RztMqQ{a3A71O9LPfcL_g>xv4v#xGKF6M66whaR{2GRdW}JDRwWn^WGQrS&1_ zYbLzK!rg*K2bO^zx9^lN+9$YR%-Q!ctw=nc zSL%TClN09;uC9ca9$|q$o^DKn{u*Cn#N($W2Sx1FGlbdpUc10Ud{HumDsabgaIu9!AfNRy?Bx${U3>bJT^v${ zfI0Vc1G%%Xbs2JSsTk7N^KJS0*)udw@Y5AfXibUG4JEh!E7&#FS)M z2jXYqdlqFx?7i813?V&&diD-Yn7j0V#nmcuiDM_8E=pOuRI~KHW@r~rQ^W}_a6$xc zhH@Oy@PEvGKE*Ps?&8Cc#sh*Sj`zbNcz4V+oMx!O9yh$Bcvj?HUCKl zY4{WY{`oi5%LUb#MNSl>LMF!a@uyV@Mdf@1^MeYHvI3%)eGW)jGFh^BRsD)nDCvwg zj`x>FJQs6@G8o6v^ofa8KMgr^0OBhW?Z6nwbA^E0+s0I5Q(GG~3rqG3#??K3B4kWE zfK4~mMB75Ph&Y)`4ksr6x4~qrwRV0@-d;D!uEzBS{@bd^rr9t`Yq9|1xeh}rJ|Y7) ztLN3;MW}&!W%QFLxc_7PNObP@9BTiG2Uj<*gg9#B3=nE~5rpzwuVy9kBJkqM%2NL> z&-#P)+Q0}t2TlML=r-m>t=5o(gRf7$B2+D;8}#$L(&WX#>00Bdzh`OfPJHUt9@OBg z!-Y{{nz6r5DH(u#Lo_-e&_bvhJB$lAXQP7mm-A&|g@QXkSa#K6{{;7AL&M7ez*kL( zkV!va1Faf}j_$zP#QJ}idtf@oV;V(Yogl~%ZP%_nT#mZ4nm`->%}3IlX|*(=^tg99 z%1AHLC`noK$T(8fSXtZ9F!bnU=uuXPk>XkU)r5qS`?OIR=h(K6YS$dT4qm*V-`laZ zu^w1DH)?|#ZTTI|GdBu$=eoMIJIhM(ip5eCDL9M_RoUEdNV@Ix$&V&-8$rmDmB z8@b;*dAqt0*Kcg#Xu1`qzV8tzT^t-FP^D1CEv9l;>&-Y)s2XS|U3zgxGd(;9F?~%K3*_QnCC8XpO>ZZ87E# z54|Y_%7C;(>_Ru$o%v!R{t%8@XK0wCZt z`)I_1LBCgJ95J&b$Adg~X_uu)M-E_lC;-4Q$L<0Re$At%U!pw0L8sc`$qg{T_kqH7 zuA?!MuNhxdjF}?(AHnnM)Pe%O?@42<4%hB7dewup>o0kA!|;#I!s&k!r2er|nE3$~ z>KU5I$et8fc)r&YqLu3+{i;r!<6{xT;evuy`agL196;NRig0<+Ea_|vMV0Hq{B+%@wCS)cp!`n1^@=Dw0aNj{ET~* z9-62?zp-waqEUJAU=}w_fw{SzScaSxOQ+kOPz4=bc9D{&ASti=9mAo5Sn}!}#W023 zyKTR^QHbQ(+Z@K{&zzBE)vi+|{JK0*XmNaT<-OONpFguwv-c17yI&Tw+Aka4qe{HR zy`E(KjP^s$eWI??*GOi}IX_{M^5D;(FTX7;+W5Ira>(^vVgxt(^|o?T2<77!FYd(fIq3Fh@DPoli_G|Z zT_3-6nqy(1k&S&oOM4#F0-o3@lIrM^oDWOI95kT`6i7Y)FZJzXBtb>~>O907FN~jc zhmDzO&z$V7`?l=SVrLA!lc}bPC`vvjk>rcO8*FQv#*Pla6e_F?4h~u_yd*0vb(;CA zaQO`#qqzzMCX??h9$9VATVa8z%YlB=BX}_129|h0#{;y-{e1;an{FCEkq}Y^TAtX?sOdV$Oi<-9OQAp z9@HJYhpJ^@))bAH>kTpr=#Ue6WnZji(WCVcS?4M#(SW)?R@(3mzr~2~VNUPLXvqc_ zII1@@hF!0*R#ux!u$sD~t;0gZNB>4gM?^n)hJSP+DCNZq%nVFGYddU5Tz!69$-hWj z8xtzqDyuvn828?!@}ljCt&e#WZu~};w)K{bj0~en0eC_s`1taI6IsiZYo>;bLXMU~ zYzFSaT3Y8ICJo%^!hgJ4E5sacbpTsV1;`Q-3cYVMT+Ciq?P5;Wu0-?T*J4gIgJY*sqGd^Q^AxV0@1o*y-N%B>0o1b!rf3($ zDpUwLhFxd-vo)npKiV~y3yCfZlaAv2)@3Nw4m)Z`j!vU~d#pe;_(RY4rF%bbWWlCZ zM|rndn3=VGrNi8PwCQk<3SrRv^_0}EpiM_~|#xo)Y#~ac&2_z!5Dh+)|96Vc>*q?gmGx zn-eBv?I`=ZYd`=lquMPoDJcS&M1Ftb@z!<5X3o8t3*~C&TIskU48CK=-MJVel|zf~ z^Z7%|!*SH@V@CQbTaQ;pv&E9c)Q*pQHg=Mtx_UEuQ{jnLM7;GDpTILE5a!ol%(Cga z|NTagc!D2ewtQn)G#L{DS6=2`{$NST1g80!&Ow0tiqGM; z(ET`p>QD-%q3%TL2!tkE=}9q)!ZSY~A10mm?tiC_bKBLc^2RPQ-XX((AT_NJc-S&$PD{94Km)Wx7R z7e^EHPDN+%cBwEPKW82E^SgIAx42f% zO-^M$a#)?y3&p$vdJC``L}6T**k`O<^6)*e#=I$`ki#lJ%x0l_xj{$>T0CkV9+FF! zE(H%$@^0(7?@x%kAFW)6K$%mH^nB-=)8|96dl>o^sfi?3E}(r6)YI-BHXomTFF`lw zF#0L5^7i>yT0Tn)D~f`GB54@OO__xGWTE{U9!-xV7iA@X(^0l73Q-RUkK3zb!8tjW zdHMOzWn}a_V%-iFPZe;xBWsWB7IopK*MY^~uV&|xpaU`yiu630b<^%g`nqJg-`y@p zsT~QuP^C}08Y76O8K>X7VdSL|=PhAmwuxpz@`wNWRjcqr;Ul;S3%$ zcm(ED3K$aSK3oUx?b2v_KJF4$Kv^k3r+Sx~`T~sS34Gt9YRphoJ%h6~o;OZ!_QAe_ z{%}EH1oy_dsiM}__dV!U&1))=p79*U3I=oSV|Z@Imv(Xnic60F=88bF0oRwH@Ng(C zl;U^%?AKhAQ&PgLQQ}b*nC86`B{C%?B~w$=67bK>-3cK3c%O@l801AC@o8G72eH~R z+-_+TKch|(@OzX>Z_MX}w|fQ$Q`s%{>Mw<^c!oNxZJecRYC1k_u^^0I-UxHr;x!0ky63*uuf0UL z`>uunob{IYd;(OQN+$< zF*2H_0hc~m}lhguSAU?&_)i+jEeY}xt=a482C_nJ&b#cHJ0qIkpWd21BzIa`8f4k<^N&J0r9{vU zR6cD%R6&s7dRm$2cet;YZz?B#_s*ouHhG^bT%XWNEB4)tW?)?_@npa}CP6I8E4tEo z4>P_P`%bO=RT;}Z1hlZS(oBmn3iID(1Y;UYORDx50nK^gRf9M)59k>|J*@w1*RgX( zH?gI?rVn}dYRFZSBNq{L7-v=zx`irdYyGU}+2WMk$WZK6LVS*G7 zl-4AbWi_=Uy`+*|Fr%o20~9RZh2vjzmRM08A1@O1s4M`jDirbwyEByK39eqSTiox? z)AM^tF2u(N&R&e~-=EFR%d6??s-Cu%&swXU#%whDLyFrAQ!}&N{Cu7HPDHNz30}h{ zWY7S}2;P$WhyBU$rb`R%V)$Fv8oeEM<*BUPs}P~xxma)-?j2=w!^Q)?om-3P+23HN zQz^U(|7%vFxqm>?6I@UA$Ao(?tip|^vpJV*zf#lA~nLBwV)m-GYA z8<&DPM?L9f*KS1rG-TDIPjVYpAVPfdw*^v3w@y6rNjA-4exMQS4EAPTK6mk>_k(L; z=PWl;`d*LlZ7$bvyyoNnVow(uo-No`j9Ce%ZO=Xm-9?R^?5=I@;*cF_M?P=tU)pJn z;_{|{_2b)%pIPcIkhFf`U=aou^gsER&F=!pV7#BdD+C2`>PAQaM?t~c}< zF^5akPoI9kCpqAQhBU`{XZR&5bMcM45QSo@nE-mKlZb;}?Q79fmuqvr%ptK&OU>W^ zT?D}vco(cusOcB+p#03yYvBY=;<>k#l>&hK>vVso17Fh5U%u3gmTq0fK8oI5(WKoO z_G2hx*N{S2?^}v;C6S9PpLH5y{+v3B(md9WFFsxQUM+VqB;a5)>5Q zyR_3!>fgZ2#ifKZNFO5M;!*wE@Zl}BHQlAnvCaFd6;1=<+|?@TWC#om)&dD)&&4Jgh9mCNQQFVRJ_cUVuipq zR=4vf&*k%1AonOsBfG=K-o78a8)6`m6g^q* zcTrqi+yj2Td*{witW(a%5wh^%>sGMbYmJ9|y}CwC+HFlhs$0oBCbvSSh!WGbSU21GM7u^S*_jW4oe#wy`}!RR&vqHa_zd)3DJMd zWqf=)dxyOj>1jCi;;6zVi3hPaQiFs1E-8&u5fkOl{vlG}t!9rd9f!D#jk12YF3Y{3 z#y{ICU>yuu*8Nj|T(f+9sbE_1kA5GVy@Y*kN=NUX$<0mi@fWDyLuIo%klm&4ooy2$ zMPM5JT^dKNT4%=`8Xwxuw_GT~qeRnNAO)JQePooBn-lk;@fVfyTO7&e0y6Kzs-nES zd(5j>(-pHW{2VAIf^Lxr4m_}PKbQ$aaXZyiVAqS*r_K(&&8@5w2k*}B8@dXP(s|b1 zS2IVxHEeC0ZLu=rhLnalN7VD7ev5TG1qT9hLK>>NFBsb2A%rBGtxa+pEKE0Mei75G60-L5HC{LI}IL zx$W#$Wzft2+?P*CnBRyo!6w&z%*!=fM5KJq@G$=$v zp{{OB)jOzN>#1V5*?^5_-P{bhU9{Xs2Ao(9U$#dvbAS+bwMOmf1tlh1?iDRTl_5L5 zO)G!;wQl;|DlO5RYIsuDpmCpyD!+3RP)i;7o})=Egway`wEq5VBExPnC&rmgJw~PO zM~p&b(akd-60vAsbS5p_8s>Do!VrQZ&SI*w^6umGY=nK_;tln%mjfvmuzE0d=RKZu zG@q>DyS+(&e&jIU834g%@rtT8b27Y;TGOoLzW&>NbaXVQxR{21@gbIg^Y+AIpNYd3 z13|@3e-t>=VcovX!Dqj$eR`BMHN~3@FoW_Fhl`8L)y;&}?J(sNg}|4uUllH1yCtQs zKY^S}l6#5v@v(G2+2K+tHh-S4FMV1-@2}s0fb875<77zl(cWDqDKOq)2TKDGDzL$a zkO@)fbRW0?B0&P}@Xpcz6;uTf!m+h8;OD=BIYQw(E+jp1J>F2r$n2}w8-EQISgK-M z7!Y7@jvxlN3|5ykUkZwgUxG^gdAcIg$H2h3=8%0?Dl%w(ebdviMZBvn{+z7Gel8_7 z?SUuwXyKSYJF7``S2`_w^G*~$k-k_SEnD;J)r_1sMe;5x%1R=B0r^hF5wY%{r<}N? zjWG)g*d=z$m*)nUFMLBwTKt}z>zD`}zxZ>w>*E9bWVy7ap7}zRZsr)82mcRgZy8nP z+J%jRZVMDq8VNxE5CQ2f>25f4;ok4N_cz|} zjB&>K=RnqCt>?L)`=0ZfSGaJr8c3nMsMy_p@AB|++LuQa1WP99rm^{_Mypl5)1l1U zrd0)6lro8D`wNOR8G^I7jP@6UWOL__@}0T%8rf}ziz>w39viFjR8PN`CN{RRzPY0A z=H=SH?t1-;wH)h)VBW!_FO<^8lTD3k9InxA;V)G}hu05F*-VdkHtOTBb8>KOh^OAC zRQZ;@o9SJCob)D8IR5p}2nqd{cQePuKOb^>j>c6X9^`k~HFF-UZC|1?&+~tBds&~# z+C&o<1ppj}qow)kqhHIE`a|IK3~Ih65L2h}Ug0n@7Y~&pTu~2m=r7QpiOjTd>JRQ< z=ii?AQzTZ8X7l7$?2MF&F2=3lVVN%pj|RIIidH5hQpdkX+nm9t+98~Ima<2P61dhm zo9>u7Yj}$)e&Tx^DOmzGE~nLB&;q*2;A?jOdx9TepD8e$8DKqpK}9DvsK5TH&{F>- zmL!Ibfcs3u@-Z_(N&z9=F>_ zErAr;Js~sZ&#k$QE(x30Xg`>QjxhV2x0@&1S!f&QbC|65$Gn{A5RbK6_gD^?*ee9^ z6Qfq~KyCkkUsY)$nPXY?hyLIsUe9ML>nX3fCaZ~_w+AI=%?U|KCd;P`986{h2-c#G zEluwD_FvK=(HoRm`h9Qt^O5Th+Znvf z6;(^G&&;ng21h>(F;jkw*Lr+D(LIoR%+s?$z$-v(6Dpx@J~^3{y8V!P=Im+Hn7$aI ziuh~mnEA$f;9vk)$bg>dV_ckxtO2OBf}zAjx@B0M{gTc+)E_mKB5wQaXn%|CXriG_ zRjNfHJfxi_KB>4qQrD=m zca!8nq;bQ^Nc&H8Bn;>YtOk2{IZCx3#$BDjrn4VSikd{usOfo@(;q!8W#~rqs$H6b z9l0gW52Q^As~ZAa9)2$lZRwjUU}N>4DT-Rboh7oWF;Mu&16{r40cKi#=i~z=s>6XuMhvpN4$==#4-K#`D&~KX5m0Oa6C@maP^f)U{xAeP~ z^Iy;`j#W{^sibRQ5E>T;yqv@It7-oO?r5V+ZhUulH@e|hNRQTix_$FE5k1v^a{;<$ z$G&y9bt)x%>Yk;gzs)mrpq*VX9W-jJ)*C){Eq7(ahifo1NIG^8TFnaBO9=*_pPwF) zLe8YLv}Rr5A={rd7G32v3f*UM>I82&9uc_aMHFY_zMLD90^Fj@l$euwE_qN`%|Bwf zutF}+b-mtln2}iimrCD;GH;G4wwia>)ZUkBS<>_vL`?t7P>ITf=2Fl7F8A#5y_G|z z0Do+LN0rOYkz-^THSw*hl~q;YzdWpsjErg#%#?@6+DuEdPTxaq8;A!((jR>c&T4Mb zKn-&_LvfpkdNLi(XXAJk!>pz5+)x$PNwOFJaYR~ZsNsFy`nt1l`_}`{Uw+b%Stu(( z7Lad6pZghs@r&+kFM0c|tCt}JXmTf$jr}!#T{yic^9D|Yj=db0PiCay?hll!f}F8} z-DZ=`6Y2NFnMaATA8rgq@0jwpmP^~2eW;j@hzXL`14VR{50>CVGkO!<)w4`G9K^%@ z@l7BaZHGt2AZRq*YogjC^Lr5PqyX@?Neh~459CxFiZT0Q5sgOePq+txW&k>#cVN_w z=9&B$oOtXBN7Tw6ew%ff>x>(m=!|A7>72#dT24-&?-y(;OB7c zjlJBAEGu%+#DK)`dy;GFiK5ZLY6EFK-5hVCF}k;3b}8%Y6OoH*a-`rb47dn4?K&7$ z_ry+Gl42p8(W!A=KX7!GOyr*%ZhgHOA|--VHh1hY@<>B~-Oc{)+Y~DNvN^qov5CZ( zTm3eqbN;bGIMJC-)yAqR`p!iwbfxoo)g6;RN&3s=?xJ-Y&aBtB4@<*Em!jT}CubK;Pbb~}NzouelE|Z(c zn#ljyU4^2CaPt5x5$5e%m`fcCTHE(-UbS2K3FdvgmT<}(Zkglr?lA(nA|_u$Ye70q z7QnDFFb)m|B>8fI<;?yS`b#|Oo9lpN4ogI%-qr@)cuFm~y+2tjUbyq|uVWND zIXmCMob1jFHR!nWlJCs`sz=7On>|L2P*R;_8kMyDx@zZT>WFQjzQXdK&MA+dFs{FH z8T~b9EF2pdz`^Vn6Hb^Sr=pc^#~^1BWJG7|h)~myhU^m^^-26H) zfNO4Ut~1mYZj8_GL@_?DL4%D($Z7UIQFBa|G=&T5CZKQt71=vH949bPk2CF;*7j}u zo}i=Mbq!|EOFxY66%}tYe_;G|LK`j#q3M>J4r7(J8Q|te`24HkjYitFqp&O1n&-)Y zBkrw@bpyrjoor?*_*L($#h+ic0cUC;K)?E)nhDheYP}aia;E8X?*0q9u-DiFv-oRL zeVN8{VvOPFgpDr3CAO{JI`FJM7SyW33oCA0U7eeG7v zm`20UKB8|H8!c?WXeUj{WOW+vu?Oe)-2QFxkVyHB%X?`7#+m0^*H`$eA1BqVX5No8 z3ST?I+1(e-lzpX>et#d~=TT#3z|>*mu=q~iZ)6-t488S5J^0nnFZOm27tMzrp>wI_IjM1KAJoEIwrbJNKI*^ zdd2jZ*TN=$sqZY%q2zFD9=VE#l^<)Mu3VTg83N z_~b54$U}l{KnEax0>LwNsYHSHADMj+qJl@SkaKC$<8~99Zp(c+I(j7QtI94OJ%@k> z*4XblbKL!07*bxI%KadPb`-2AO~ED+T;QFZ=erY|Qzc%44h#kho)y*$cX0Q(S4R*p zricC1JOT^t?O4X9xW~)PTbHU;pFJ0RY_94aVK7#6FN)-{CYNop@Ao@b(CiGEw?vx~ zsm@)XkD7y)YO!*YGo(B2zS*{u+A?RkBJg*Y8irn1yFN~uJbGIbceXdauDBaGc7tda zRq_krmr%Ab4r20|Ciy*toBjK=@lR8Lm3}vC4Qg^_azVt+pzEXlPaO3Tw;=ZEJ|**D zoo}nePVA)QesGr>Z(@5;V*2--><<&Q2wwi)PhOFq%coS7J%y6)&qp%YF)^JyN%CA` z8IB%c=J{0gu97S~dVJr!WN4p<8sVB481E5MI3f3~OQv-K&-!r5?|Xo+%ia(kg9>&N zgoXN@*p2t~dE9xRLBB(0)p&Z!7)ZNt`#!yTcNAr~MPBOkXyTAbg831lcc)h7j$^l{ zL@b@oLwv4E>hnY9B}l7977{+wi1V#zKF1<(Uk~+dXb@=x#sM~*FA5Y?RCN7DU+5k@ z5Y8N2&V57}U~6WUM_3?F7ruAZ<7@A$hvnm~1E$r8+;Yeg2`McuE>;dQ|5d%crM*`1 zsi1ugt8sMy)LczBQlm0KX-zKRSenV6X2Q0f3bJ?Z3s(q^tg z{%GE$h-aUXH34Mt!t8u*v3I8WU>e0*oYf~SQS)gx_1bXtq4>$^y40y^Nz2ZiJ};b6 znRhhXvebcMjL1?)rc6Ya-|9?{O6%lzDI|Gh4P5M-T5zOFVdhH}_18>B8$z zWri(6hKsNt8NKr!^Zb51x=bG3nIpy_&R+E&7q)*g0u2Ave~JIr ze9sl-3&1$OrmC_sF1)Q7uQajBH}Lr)Lce{O3nL%h7>Pt9IkoBOHR~e5iy!91@a< z@i6V#hDT*Cz)%uHTu#QeuC73*-BT{79f#Ug+vOvqOJ!&54Sh)}5=B6|Txw<>4Gyga$8(%d2O``u0d)3k3% zOZUEJhk)i-EEZ}w)wLG0g*(Eeeshduu>A-tfcST%F72u1Vd=H29FyP)338n2rA#Ii6v8A93P>QtnJmOD3r! zH;iE|eu4QqkSldi+fq}D|N4>HIlwrwZCX}ZDwfjQGv_H~dqvi8%JA9x&VTBM_}JKX18W3(ZJt6d-I#NAog#WI@|N5vb2~Kx6VgM( zdFUg-i`(H@7}W(5X8|G}ho7S?4OojyN2iN@M}iq)14^nx4-J0a2w7J%7#9{a9f*ic{zQKisSxX$&)9ymIi9jLGjnbQ)RzONj0zQX@C=y z4~NsMhxqusc5xLcd>;#S#IXnn8tdLNBPnHY2@_siaIwsUYX_5(#w7myv!iVFo9ELU z<(gV@9Utlr7y|)C(KF)8l`G#Xti}B_ewga$)WZq_ocF(P@z3R5UXkKW4NP9=pOEdG zqzB{mPtRiqaN;s-%tjCHG3QJEz7F2hQKI1Ong5%b6cQY4xJEO5qc=&IcfKRqQ^mA1 zo_7fhX6ZB!zj_>7T3gqTk0)eg)T03E=>>sxsGnZYfSVO30ha{|2Zs@q@r|>si628l zg$MK07a_{r%*+fXliEi+(gaWt($Z$$#D5so(NT2Fk8osrOD9hksqB2QgwpgZAK*O~ zBD~{BQCVkaZqBShaCCRQnm0xxX{@lcltY+jlWX{ICd-yl(p;_zfAjkrSln(We|-JQ zcpbw`Yefm==KmnKRO>O^284fiP`t?fJb!0(#A|Q0>XU*h^nw}Bw&=qp>EyRkMg2a* z`k;5b@Vxf6(w2!w*PKv6QLDs`G(WDeF3eG>h*+;xm-u<&SzcAuTP^yMjRHIQ6G&Vz zNou`kXLVr|jgBX_@eMsbU~Zu+o*I&o0kTL=P6i+Ym>e^clP{>6!SL~`h#1Ld*)+)< z<>Jz3!x!-_4w$(86|a9c$d^Wni;BQ2K_q_=$RFH}TkQSWifqC{WN2HPnH?pDq}Gc) z%n*r%uBEjNvrNVp>t~S3!ARte3AruxgM*3XEBSL zp0kf_Zf*|R=?0aEqbv>D1n50(0bDm!V)(T2YGn|RN`MR^AW-ys&@`{Z@&FGqZn^CD zT$0c$Mg;nXxnz+*-pu5ED5O+-?=)hqz&H;gcBf`$JaTgxVfxop6iz?*&}rvSAn3fH z!Dcrby)KBT#7QW$(p$e&W^v)Jn(^m9X2T^bR-<|e=KD8csl5K8a(40{1ZxY0Uq(zw z*mjjl%H1ibk|^Zul5* z6>U686_NYzv^#8Zg;t`y=rYWjfeRyP^mGpc970YGX2jf1EOmT#^}FKgJEE_UCZ`WB z-#|yFW?-m(JOu6YQ04BL90JD01ZB604z`3TnAB8M2!XpZRAnz%W-XUTPQCwgST|n9pX=qt z8((FUYNf{<68ci9#)uyZ|9NlvhRe~M<`}@`0BV0UuVnO%>l+6BS#vbulCY0Lb{DFl zq5U%^Je>3;@x*tiXGgj&wW}JPBR!M5_67TyQc`WEQjF2kCu^PiZLy`(nNvCzd8^{{ zoy$atf}x1p=d!(iQz;n*)kTa}2w?aiu4gP>XRPCMBe6bL$6aPFI*^ZF2X(|rP*|#n?qJWgovPlcx7Vd{OFEdw`dZ3$=r8-UYeOoW^mzz8EJAc$@!aY;mEl?jw9Lw#z_XqUw$%^?TRKy4dE^h8ZAasI1EIIrE;{gWhhXppI z8h-8nfB2dqd72Ulii5M_gSByc_)kijEi?xISfIC8__^`LT2acW8u_Dv@*S$4w)WXQ z`ZBkKHP!P^3wbme2b+W1%T8Cz9K7y-RcB=9Pts$pIMVSuLK<83%n%HPe`m+;}%Wrv0M@BAhf4c>#-(b2)T zOdnaC{47xP*P~QNgfz@jK4dawzGcQZ+yA4}zsy|XO?Z0YiRrOE7p=+likc{@GTd#) zCel>=qUUDkP3FfxN561io4BW;OMDNH(`n5=buPyu&}B=2n^wYeVB76T*^2GE1h;$J z-HDOK>fe-5J4{Vzxa@m|L@#HZq&!mI-r{4 zllchS% zN4GZa_6~pH)WzqdH4i<3F17T-GH-1}YFCOM1_$1S&kRkUw<}Mn4Sk`Vh)+cp)b2(9 zq6%JV3ki`uJ5HU;t{YSBpP!wTvk)R{ixiSM*jpYn$Hc|$f?!*ctW!A4Ln9+qU&&1# z?cM|9PwWa!Wnu))|Aj{QIi|$j@(}QRAD^9L` z!0pbY)Qo6nL7p4}|K;^1nv?CHg9VLn@jMwYvBD%NJ7c^oj(!lZrD7+${2#2mkrAa* zAW@11k@%FA;h}lH1NYyl1;sMC)vAh$&F${0AqZ!;v$H#F10m+q^D|e&fgD=+H6B|R z!=WP4%)rF>_~!}=xIoZQQdY)M!je}ze1m!lhIAwr`3&2At4a2CYC8Ty{sAwtPJ+UpXV<}cj*IVlSEPqsG2Px%}|}5 z(ZTEK~d2(kh(z0`xZ@# zrorRJt<_d>Qr9;sxlN6Yo>0cau9Ifa?M$j2MC7C08e$H;0zJy|;CUSs@hAmA(j+1# z{#l?!EF~2fCrH{}V#xM1eCprmULO&c#Yf8-Dx@?3rYd4CE_~Yy?s^QIa|eLQ0qu3O zUvay&t&N<|sC(j2CrM93J-nh543MzN{Gce+Swc zQ9d=-;Yr13B-@C*eWyO`=Mub`-a3?Jzall6ar;G9e@1-VQt1*V0cT#(`K}wFzSerp zVlmQZM7+|>MWJSQxQ)rn0?)3z`L5FWGCxo(y)$VrL8pmBn)%jDN0Db5wnn?z(A*IRcU4RM+U%CGCAs;RF;(MX*YNQ0ib>8T&JSO{Y`2@rMYe{YfW0OX6uz`A znx(v->5?D+XdvmauSULf|B_8KnkRXCQxg?68ylNq{@?;PFYhv#;dS-(y>#E47WGI; zNdY^fK1@u^S13_@j$5bb{I_t~kaDN9%zu;Dq>%;W`731ciaDJK#e%FEdMmP`mD&4G z%7Y9Q{F84$x6=dpxkMU+FN9Xee?~*znZrt^938X;O_jFU8Bm>6%v{u@e5DMK;6341 z11_~6HopMwZ)vHh^u;PyVonGfn)^HW_}_q~)g8rRzB%rU)j=$QeA#rN?wxqQ-DQqh zXJW$P`1kKcLfbuQBmm5{w2U+yF7f#rpeg;A^k?%L9bPgGP&Uf#Rx2xX{{8?U)F`*a zgz;}H0DPaU$b12Q5YtZfceu!63B&VSR%5?mC^8$oc#R2OkNguOrGUUmYr(LB?b1bI zPeKn(+TXa%Z8;_pOGt|Lfef1Fz|NiJLEzB+hJko-cXv&HV>AL56_}0SYDojM{U4dz z*xcLzK3pXDZ2l~a3P%46qA4WDH3FrVhyV+AzKIxM#Vxa!8K% z`#0`lnS)2EyzU|35y~}A%2%i^@Cb)$08o${0QmzyjSO|!3QWoh=&q3pY$Sm`1MnLl z0%XW#p-E98-+R-cAnFkwy(0E`$!bWOCru!TK%kZR^Z9ee61D%}Es)KP6L*1HO>e$} zPEaTe4>;oyjDpF%KiTZps)d$+4h{7dXbIyopM8GUcX2I|+x4uF_f~S>60l}U8wWtl zX7Kf$MRLlVnJ%o9_n5aOK>~<^R67_C5Lq`PSJHcjQ~(oc6wy4tA#p_Hg1YkWwUvPh zqnx%8^B`7G>>+8&8_Kh|KcROnhZ%DQu>$Ycl&9NBKb zPY<^dtU!Uhcq5E|b|xT#d+jm?Mv!W1>etWdF6DAWeq}x-%~cWX%txFk^vX0|Zfk9w z3g8qgG@ro0AUz93obhO{{dw`tlKquOOK5m`d4Kiq&n))7*t%ZyHy2>0$y7kzh0A)L z0lZ{^1Z_FLqYNN#N0cm|cUTxUwBUM97ZJM+I&ITDc>lp0pUspS59VQEP6k201b7*b zt+6%oy!(uO+X$u+eEXwpu+_)@#*p6A=->_lj_}>Pj6iAYECI}jh^KZ{t zjp9g2EKCQ7ef;<*9BW8a!5;hfC20Bwk7LU;Uv&@nYg?e*R2QHU^@(($dn5 z{$IXYBjz^io7; zh0`t}3>xRX=}3gS6UK&$4M+_Q4Jj3W2fGAe`#E%7dHfr!Ebl4$V(j+m<-(pn0q@-x z7LU&8mIqIj3Q-${w|914L3!>l7e4hDyhzse7M@6jJSA0KWl2di<3ShAw}v9heXrSp zm<{pYLdhvX5!Dxa<9ml$Bt>}B0nl_o)v~qwaZ#M7hsEt#MhCF z5x$?E5a?8`;Y!a3-oN-!0w$}~#chI^WWXsME`bxlzjoQMSV%c421pius=r2a@h;L7 zeo?snWt!#ywQ9MT>gkE9jp8WeRy#YZT)XDXW54w0?k{;@HO&0%oq_ZFy2sa7c`ZTM z@lxRTbe+`gKtQLdy3u+4IZbitpb6 zs?QDWECkhsc9HYoEf1Qu?DMdf@B%xwX^`L9c4jY^Lg1X?CLqolXM^`;W&60?7(OSb z2o!-x6B_JiU<%PzG8XFS11d1WfNwE=2`OzQtKW+X3#Y!Paamb!cf<%IFU+)ro@HP1 z%F0TH;zUr2c+h2gaTYjg8-d*Z_Uo(rFb_=WZ3ggy)B3x3P^2OyC=|`s@aN{60uY;9 zS-NZZzb_2KzxkzDR5rs+Jx4rDN`BrQO$P^3L{ii+ih%2lp6Mp<)XQOZ_0cZTXU|Is8z|vrT zQ0wunBICyn^LIjahhE>}`NsBO$>?SC>?zhxG@stw6Y6`KCO8Tw!Oj8nmCoH04nOxa z-5ehV2YY48W!?qXwnvV?bFF1c8H`lq{vKAi9oyZVU{c3;Ba}25p2WO)oFqucGycH* zXF(DO+GNtZUe3-&xrtMgraegH5@;6j;%G7@_@EZLuNg+rs;7>2YH~ShX@d`l{(AaN zWU@O$df(4Lz@}7Tbt}R>Zp*4ckFuGkg`eLbv!?gtC zlGm^w>lmA#$N;+8AhiMW2@Vq&POs&$OBrJOcL!_@fTa z&emJAOS0?K7wf3)!^O5o<3wf#nO$DD&y{s6XrljHGE{5{F0x=o9O7w3yW^;u%MaX8 z(2J)ja8=)<9ned3THJdY0?Ev;UI`-!8>=H_q*;s?8=8z0vJ?#?{by0ZXWdN8S#03` zAvTs?wd_pyzxx@WopVqwZtnD%b`qzIQc_TWcSA}i@$b#Shw|^RCvR4=2Y5l;1_lOD zNIw~HxHvRGE%FaM^}>bnVzWg~A%SQD+EMYxm)&=q_!qhwi*%07Y%XRvq3I?UdJ8^Zm=Y#K2t#>!CFYs@AKM5u@APzPs2WTe;53a) zSLK+gGFL2Esp>B=9f^UFXB0~z&C^>(eBEJ@7}~kAk!s2pA5)0|rkV$Gn^Ry~sH?9Z z46!27eFmF5I}any-JSP|Nkl6+EDktF`u-I9lWgbiAMb2F(%lz*@rMYiXwW;~Wb$`y zsJk>YzrJFKr@DBnhv`2N#axmX@qL^@{^;!=e|DRdcDT6bDBr!_`oW|KHSxG|Pm*;# z%z(41c)MdHrtEdo`iR}f=x9h=xC#{aWXJ8#ZvILKAw&2L=Zwx#SC%z*?0+9G$=Gxv zR+hiy$Tv3wz}pnKIKP_v^)$b^NjsXD+)S6EbUKsb3m55EGUTz&JviOiUlA-EpV0b! zD`c}UU70vcM}?j}W7kO}6g4s;bl#`<_>s8+bQqAd(7X3x;B`#XQYsq+rX8{TJ)3NV zKY0gFF$j#3=3QA5!5i-TXH$BBvcdNXb8>AXH;WGN81X*#yV8_NgUhExUp5AIBx{y* z@-$x!1yy+A#}|%D-?=DKy^ALIXN8~*o!lEaSm|R17X0+jLM)I$_@7-q+8T)Mdm$gb zvSM=S5(9(A4kHQ>@G0r(+q;9&%>&w$=GQTQZ?D`N&WY%Ljs8O>RQ&w)#SFAd?6`hc zLR`W#kmN^)~CZ;KKIZpGnwg059eYMNBY=J81i5m?CpzyC{&z zhpIT=vgjmi*#G7NOcTn(w*Z62JGdlG9R$`6>+Z$g{{E>t>3a!VP`hp|>fF|PxajxO zkH)utYI3nxM~MN~-RpN+Y@Q4bd4+c_Zu{BkwU*YxX&as)#5ziFusq)!RcLTN98FkPx;4nW62R<8oDtWhjD_Lb*-C-|8cH>R9W-aE#xyxa*S`E2Wc zheW5#_@=|9)0NB9C&UAf7K*!T>vUNQO@{VwlDLS!WlissXbv1(99Y$t_}ykU;A}c~ zdcvKlTP@ohO4kg50QN#1Y9L79pI+OU%jHZ@%F03rSL^fb^jwa+FmJyX8XM~@QPFR- zD=hc>POD7q$NaBHVYay?k6%8!3Ctk6gs{-xP(7cnSgX7`NAx?yxm@9ZzXIm?u;1wp zmzZ8DU#NaPF?AKjJ)WKV+txON5n*A)BI4qBD*Eh%N=3RXV77E!RHlua_4g%Y+;A?Y zd2^`AI6ptmkI%Me`FLv9CzL^LBOv|c==1Z%@fv!k8VpchM-`?3w`a1xBJqfMF&iGw z))HfT;Uql_uTapy-XS5Oa{awh8*nl)TSZ~Aw~bJ-8QL0iU(wnpb#7xxHkK9<`2+kd z$oTnBiwN(4;ni1AuZ@ z@9(|GSkyD40#mPj&Z@WeG(a>jG$BE1eY`9Edb%sRdHtU3%m1{|s!N{*&la0m|sd8rD+@jmH&>So`*CEfGd z%OYY5sC82o8&jjw+)g`OSy>?;3rMs3B~47^1;zi`*;?|2DDdFWxJx>5-LLT->;!IV z>U)$ch?OJ#;^;UcV{S2;iyikpjg;r2b)n%5E@(*!j(^Y~ZFZ`t-@b3Bz(b0pI0w66 zK-1<~|A93}IOkGwx?c;D7I-#x+qD8TkRj9b&lh6@@Y_3_^3prIDSvTbUYB`~)|=#frs!8uVSl^H-<9YE zSRqiLm^A^C4!+^y&8UCXVRlGIF2` z{hpxLkN@K(bJO5Ix>0v||CsltWjF!L^pgY^t1v`8rjSFX5WC;^C7YdJ{qYA>0dTx} zT)SPol8#fZ#)JZAfiSEshz$mJ8w}$?vsdd0e+Y`hJkQTqWFeC@Wzi<8{=q@1I2)02 z^chCBoLGQU*ug;)-_ar#<*?bWA7wr40WiG|m(fj|F?CTqI-yS{41SK!`|1uj(a}B( zkzq5|>FlJj8Ke4Qru||e@;Z86+t7}VOMwf8#KP@xLzn(MHdZ4s3SSH%x@E`DGqgF% zS%Y&ptq6@g|Ic3+u)2@u4!}xU6vTVLwR^7_s2S($=C%>O#pmA8%n3G5-LU;b_w&_u z@DbUA2Yt(1e2H#H+=(u%c=Ga&>oo*=BxRV%Hc-I)fT@RPrL8fWLklrQq68J?Tu$PH{Dbcpt24){=5?5uEvwo4YMXA~?x{a^tOoUe6TVJNIUwa5~j>65Y5GO@_yia_ewa7Q!W%rjY z1$8=#1-uG`;?lZusfcf<;%IpaeiEm?t zqW4-HR}0S@cu-iaWP3_{m4z)R$+Pd3=f$Tz3o3NZ4d-9ZapOLNXaunEbN3t%&!eF` zI-E*mg6L=1GbFcZ$nq}XB+vhKq8GYPJXx{h-JSVWbh;n!nWqYIR+lba;yM~Ev{Jq8{-ut7XQ z5&^1{8-b*PGReFrKm%XMEz@Y$WetHUeWdhCXllScMP!o7yLaz?*1DpjoF0vRZ4FKL z4LBcAJ?_cZ)CBX^XArgOu=<4)pZ~}b;@~Tm^PFK~OKa=W$bVio;pPF+EhG+;S||bs zuW)H;X~AC?T>~MOcIP$5TYmtHhr`?0&Xr>mR_Qw8%*p)}nG%R=P@w zgW6*vvf`Z6_fic>hKSeOC-~3M+;d8|c1H}ktlHCY4W~o0{_tFSc#W?5$eIDgd?K;q zkb)ZJYRb1aEJ^I^cdplUg>$>`J|a#Zt2&4o#joSXs_(srV=_d?yeC1|yXoiT8nb`> zQ-9}Mco*+Hz1gbsH0x~-vRU1XYEITGRq_v@l-pZ1pZt1_l$O1ATHsvN^=!T?zVq|* z*l6DUnM=nYsZFwhbsqe{4}qDxxNyxqQ4keLt9XB-`vqwI!M*@I9(X3)sv5EnT$a2@ zBUl}`C?I5ALK^X+{WB1&T#Da7nZH&cezbFa${ZG!c3%_Y?EEnD)FN@4MGYk8mlFDR zc4-+xl~~s%bny9RF(O>{QxvK7rsAE-Zc~=Nw0Coyw2Q!4@#W584fr6qMyttD&O0*U z5d7n^^Ffm4HMhmVugeXWCTow^V1`Gsv(y)K&usA-lqot**e2`8iz&B3OZ-fwXfQ-WaFPXdg1 z@%D&SK0J)^h6%7KA#t3wgjs3xrziG3DdMEhs2(t?a_mbW*O3pFCb>W@3+KdWs78%b zPe!k8^~sj82JyXDRu+n9{zUSVEVKt=`DKVNt1thE5kA}>)%TAb30rq}4K-2CO0PEV zn_|x&iB+s!`T9Oc@O zOU@o@_#~h^zYz$MBQJd$+*(Gx*;Yqk#LnKkptTO60bOmd2}VDv+|W;LBu11Ert9lq zkp}XLf1|p`If~oj?iKg-m0X5(4Z$1e;)5YJY*Kqe8|;>nsO|)&H0m5Ht&zLu& z!oGAZul5ypzo?Pq>s;Rbs#{<|V~K9S#`J^1&TNZ0;#44>VZ<>mPc7nHAURq0t&=i6KkcASti8;jQ?o>f`I*RF21QxoW>XL79)(hGK`DhIu>^2_I1J-Sl( ztB=9Tn%!iGFdNa5YC2J8#p`C7-@Wtzqv*R_k*qq=X?a;rQv>f=XDe=(%f(jHF*O>U8Z;KA|9AyhWSz&3_JvK#X9$b4&v zHKvtusSQg=(3m_$owVUu`dN5yta9umXt|6^M06IK0_fUM%O;u(65S-UweLbjiR?dj zNP8m7#^XL@X}ngi#E(7qFQw=hZtjT01jbA%&v&%$4V~%e zWN-b-X5LIZ=UY~`Gwkg1WPT6A-tKis<%#Ol2WjjE-uiTUU1Ust?PRs<*+}VWVExqN zKK8wpq2*8=XXD1b=+sow6rGu>^0JXw6aI}y6DDq*FP&w$QwfEuq_ke7`y`l}U`*5? zd|#$jz8y1WN;L6o#>ZzTi$m4<#@B9@i!*acROUXrth7^YOoYERT~hFomEV-4M&gP2 zIhc;!!Ns*Z3?EZ@VCKuq)EzR*d+-sn)o<2 zAEE;r4r&G5u^+RN*|Atyp-}>^R8J#LUQC<9(A%;?4Ox>xVVJ*oFkU&@NB{gJZEkg& zl-G_Ou8x$A4LeZm<~uoSNc)$+d^8)?F_e8ZL_Hwrz+#-MIAZ*IG93&LH^ryhaE3ZN=yk6#3uvGQF$VuPqXA9CK7Z@uG=bMWBR7UXlXmM5L1EM*up76%;&#_ydU7V082F z!`?mQ1xoYOloqlk7^~WURW@QcUQHk)6C7E6`{LR~I-egMpF~GRiGYqIcy7+t&ELo8 zt1c_f5yJF41&LSvS5S_%^I?#Q^q!0}(DCwmu^B)VNh!``!gjK&*F7%BEo_LE0vqoS z<7^FVPI7?60h0IU&niU5n6F>Cm1((&0+g6R$%8sKD_>Ba587`VI-IBcA?I^dG|6g4 zUk(CNi%Zyi$Lo^pSphrcacjvQjMn-UUBW#x=ZW24;L2kzU z#JO9e`#HTan&OWI={|N0624~$H`l|Nps^J=MApaop;wNQHh&zxdzh55pO5HH z)O6!cgds<7sp&n)*#kc^o5h|ykG4DpGa?7{5|e6N zVkvJapW<$h+4~`(0-QG!#!M)H^+m-x@4xE!*^KHLN-b4n@s$&0c2)^#6Iph91x}jl zFg-mvsAo-_2(KJ4%xVj?*GL{~@3R=rAv)2<@WOMwS{H1NXu!u78dTQQx`{zHlV87* zuqpO-wR?5EE9nYp-wf9NSf-AkYiFKezLV`rx4`i`z-?>KoKVB*JT1VTP{Y;Mcd#&s z^qdG%MmqOa5mpBqi63%k~#M@e&$!Sz%Xc+FMoSvYe)XZ?>dt0M0ob z@yBHJ3gyP;xJ+stsS^1I6ldR?6n|_3%YV9N|1+!QbbD%AT5v=}69nk!9qrhIWqE{^ zA>Ahg8F~o(mZP11nj<74Zg|rUm7gaMc zcWG9w^8_+~_&o7savbYHH$MYuK(1Ds5K=Cfk9ES5x8$MUj`zodHmj&Ib ze-i50ygA4O++v-xsaJFp8;QwKkOFK z?{K(%VVGVFVQSJfOxL{qbH0JDQ*DvL|6f2_1{h_NUV)TO2_VPk!_kxs!wfob#*>F| zPaCRG37D1TzLf9B$H#wd!$m|@2W*jd?9b7A*%|rz=8u@pYXeEP)}IY0B_+A>yQ!$O z*1mUrzBYZGjP4zEf`dPky4mcwvlq_rsJu7$-`><6ua-X8Y}Nw<@3J0SyOo1*ZfgPE zCjX&1qWF!-BYo@OIj{CtT(icatL05s&MiKHCUE-^JG&clOqwuGes02JA)M{?`0VkRYSmWzvt` zn!R}ug9eK|WTI5KFt$hsK?u^ZY>p@zGx?#cwM z6A@~y-ma~q!$Md?YORSa-AjA2oPB&n_`nAKwdYE}g2+42V9imo%%~ zrBEgiqY9{f8-S1#?KQMni*E6I>HdJG;cIDt%*|gvu32<{BD|xQCQ~ezaL`R!vgGnX zUQl7-YwZ~7&+sG4JvDw)?W>m`n)h1`+w!2OnZYpL(joLU`=i6H6Pr$!syH;O% zOQxFrRB_F@S~qmx^ZhUZuimwn2`0ivjxU!qa$K-QgEy;DYc!AaO z{x&4ME2=T!LHYvllnJl7);+gO-`1S(bMr6x&i6QkgjV(Lb~oa>%LXlw7)rlB z@SNe_AK@vfdU;TQkD2eFX{-@d7->MMmvUcX(8Y{ypp8BTU$(7i+F8IyCmy;Mq zH$n}FJ!IxIgNP@~c4czTxg#ckGv?B5uWO)Do_)G-_98A2ojNJUA6?HlQoi2Aa;jii zsVYb1?QC*MR)dn`7J}Eo+JrubJI%QjVv){{*VBO=@+^)k80cc8qRKqT2y95d)H|AU z;f5OsAqY@v&-E!gGMBm!ea>Es7>;&zXZf<~ihr5Wf1;`<+mt7Oup^6>G}7TXdvo_N zJ;TYBXmGdk?IUwMx2Xk2p#H#;1gUg7C#Ns3JNVLTxNM4QIIlh`H5n3ugzcrJadif$ zRKQw1n^n8vI@BGoYIwk)c51u(>Ap(X4@mWG-*xc3anp`fzYT*t*))S`q9j+Nj4#5n zp4ZZKxj0h2c~0)&mj{=Cx<)}zb4g^3*2CXloEg_t~An0w65of6?jQatRV$a81`PHR7wOpMF%oX*_2yDWN4K76q zat5;bymculIXSAb`4!zTy6$R0&wI@Qo$K0S&4})-(SD=5%$o6a<}nLBvfQWdE$>Yp z(M3im$8U6D;1Av%7OC%Up4xm=u!mt!j=DCxu#3@ipNNC<>x1l1T3ijDj->T#)@!bs zOoz10QoJX%mvWTXjDOMZ+6(@Y&3xD@I=1}%(Au4*`Rpcbfy>yIlAUR;(6cW@xTXit zFD|~=mMS&%*KUu&zbUrgm^zfRHT@%(YlUZ6f%w_5BU`Lu^Q2W}s1IGQ0>(PJs4cBr zV2PzbqmaUfDk?Ji`j+)ra>F43&ysC@H@Hf6pMyVPPK*s@%5a7@YmPVmFi^$*(?XIQ z=A{td(FYJRwy4(8&JwiZ?JHwdg9erwnXpCx(pid}y)?Rtfcy-k6HF$vQ_RgpN!VQw zU5pR2SiKa?82jDwuUd|cB=f|dRG2yYl$}yn3n*r9sIdH4`dKa4>X}Rf8MoT3hj~{y zq%9XoKGun zrozZ^^h`}~yq@kayk+meFIG>tzs0uYYN*7w;L``hsK-29+#%$t`H=kh|7q*G!>NAX zzZIF4O`(ONkeyxG<47p;pd@?GtV&jrkR6JwV`Y@>7$M`>GmaIGk)6GMw@-b(-|rv4 zuFG|Gxt!W(=2!; zm+7yO^BH8i;LU21BeBH3@BMB2u&ryHL@e326Yq+~-Y=Y*)Jd-h_?F!4KmTHp`$8+a zh|T@ol`>JcrT1n~?p@f3@3ZWK8qZ9&ctft(<#kxm{fAm$=c1O4A~?;EJfn1Z9vRbK zW=f9E3r&9%_K9F&!I?RJ1~H_ieSy2@fYz$%v7>u(#b~a4;x#wNRkxwWC~XHZ=i>Rd zs`EDmNVZ-Uc(_nV?$Q}eBdG_3R33jT@{ASQd8+=J{f5J&EY&&D8d!eH`N*p6-#zMJ zIhf%A3Y>6nF#k9Vf|7AOhUKT3nUz7aa~q<5+1^q`b6cA!BrpMK@Z+mZ>)Q)=G7T$) z%gV~GAuD4WLKr+Z&w#^v?2Sj}U`ydzk@&MI3_!jHR4igdHNY8=H8;Nkk_4c#d+Hfn zsU2rt@5yFgA%nqY0P);fbHPFjpuBV>g<_ZT(!FOt29|F0>&HoY3Illr9v%iR?O=3k zn39fwR-mNo1e22*{T1(wOLo_jwMRO){d9Nt_mZX^M7~V)UseAg!u%am$X~VYhg>c) zNv0C4{vz;vA}Z5BB)ZSeOS!3u*hHmcGGq_=E&S0PJHbxxIzHyjxs#<@M9Lbo+p~#+Y~AF3&EKEXq1Rfoog8MOD}Pk2)7;TH z8r`S1(HvQDYI;q&|HXv!i@0E_{6eEn55La-AZw%jSF%NJlBFv=@ zZFgUwUasmL;AiqML@VDKS^IItvINbVfvp|7!>MyHlW}=5kMU9{9m5fe0Ehi#3DH-d zVxrPwT?2=X#}5IAn{L0Rc-cz7*HXh^Dkg*;=^AkdwdL#L*u?3b%f4BJR-^lBa} zvMWV$;IB-E@Pemvi|29`&gFGO*PI}$kB`Vk!uDZHY6nXbo*PX@tM0aj_tLpd9D8f> z`_d53K-Za`IZ6-9H7YZNMV)u*CXMF7{6dDiy&Ypy*vroIQlVxx*e{}-VZ?-Yi}E~U zN5++@U}HB;PY`{Y8NpfBDahEJCUUE`-7&XoC7!H}h-z2-t;$B$^r6SM*86sGu9;qO zJ%iCbZ==ZyB zsohDpSZUHz3Nyw6lgQ-H6ML8 zweH~a9sCuBP2#-5Patx4YpTi04$TfN6D4Cxsh){BFr6D^2tej*6xniAe7(QgYml=e z{?VfEg8%vxjF(UbK5Xvm9*+2(g--liucc~mJ<2{f>+VlVunNoF+_o+NRGK7AYBv*i z@xT#MAvHAW615=6sR+Z)bkp%NXicV>)Cc?;DT{>79*oSjj&*hXh$-EFhKj(jktn<= z9X18^94rwDi4)krLb3?NKEY44E_M<$K{5b64x*)RyHXX59QVPD3cRAl;cDmKXZaXn z0R@X#uT#t#Jn+1TNKArld2m~sDln!nU~!P2c}fmS1yKKIx8yP7OPJjrJg;}KKrgIn zRAZOy6*g1;R*+3^CsB5MON1D1F1;d z?<8rawOaCGrv9+UyzRf0_Op>kL>U-_7n)~~zi``pTtH&K$1~Fx?JFiF#i){YestmY zd62&8(-*FbzYi;qm*;Sjc5B~kL@=V!TimIh@LxOohXr=g(%a5OsfB;A#z{Tsmf zfsCE*XJdS@?yWZ~iZVX}+xbs$E%bsY42aeOGYm7h#No16@EHZoPv=*zksbd)9@MPn z8Gh0fXbD|EUeep!?n&O8Ln>brg{&J|!_0;TzQjtTnhHrAE;)tc8k&YZ ze0-NMHm}{ycwHt(E;%j^vDQv_e-!=oRNX16#yI1NcVC+QWIpWQiROB9@x`^!FDH}ETFS1akkyx7Q92D9Wh+ra8RrL2 zhVn9JPgPPr^&&?R3se|N?ObU?4NnB7u9y&bdAFub|I~lpfTwc!LYZSr%sYCUrvOiJ zkfOO7ZE!t`EYd*7iNg6;__y8UM;6$*+q+S2f#^~WW2%ZbTO1;U9}s>NGx>({(~1e4 z&ofzKYWp|37%8}_NLWc$emCh!=#W@%V^=#rIXMZg^?To2S|)*9 z&E0il4NT(M!9w1ta&qKg^|GUYQ>;_#ECIt}wd3XrspCC??@DT%+|v>~6@Kq~RaI}{ z;|W%+b3{u?6MDYcJF3`7U7p)XCc(l{tMijD!wgpBOa|snj~Q+SWg{z6-T5=8;VshI z-50-KYwN^x^vyBH;j`+|qGhx19$0A*A8>s2-o2EuRl01zvA7fYomfcnarG;-W#rS! zu?>@)Io+Iu&9o;T_m#N@#zsSlC2hyYTXNqIMvwbFnevPYL5n^<*oRUkz_}yTx8_v9 zg68<5aUwd`2t6t=uL(7eprD}fGF%d*klKGQj~#c}@#df0umVmn9+r+^1}vMa_oZ@F z_#gRccRx6hrlPpOTMhL9=VIrw;P#^38?j^U%f5lZxy%h-3JlZJp1kJZo7gzF0!NDqwEV z$z`PHr95hoz<`keB~_16XFwKRz$&z4RUbzip($smuinse4tJ8bn{p9@qTlR z#oYcGM&660W#{601t*I4XD-RRN9*SHtMm_-I;1{H4xzu!wYuK=sARagWv%-9!cJ;% zuQ@ihfn zV=SKu0Z@^Dj@SBuQh9=5h3gI55z(c}bwqL~U(AtLe5B?Yha@Q}sc0fXOM7GI+Xr-R z@I*R3XHeEea=JXZG^!(##tq-SI)-->9h1;yRRihNf)hp6NK8uDyud+Ux~luZ*2Ask zWhsWma-?~HC){IInX&W<+8r>gSp$5Dr=oLAAsmbP_P?&rd$KktENJPP6qNZw$etag zbmjRD07NO%fZQSekf}FJt1(Q?BKQR+dgF95{y(Z=N&4 zb%%dgo250^YrwY6S6r%A=Xu)q@-y0L{LE_gu$?N4X!V%nzO~$>wqy9)w=z%=5VGUt z$bXrBt3)5m&?a6Y|1`R!YL%jB%)IVnm5C^em>}hwbM|9$t!L$h)xAR2fB*7*K{wVn zj=|~i+l-2-FUrCSVN~NX3uzTdxpP0DOY=_GXA3SHJLi&B?B^6N1*(^YuDU|WoD4Y4 zYpUK~*>CEOc3@VPeUHvQTgXQz?Jc8|p+fEv9Tknp`1xf3`P27kj}7oi_~Q^Hu77@$ zZUN`jV?|GDW@a!SudI{Y8mR|O9I_m@vN)DVFcp;cm9iS3_ui{#IB)}$5EPZze+-0m zAUK!Og@1c`UQ-ZRHk-C$i-^=i(5PB44?WQ&eO3DQE{Q@wWcU=qF^5UloF}R#=>&Ozhj#&9>TtR~fhZah17Sk8(gbtEUaFz)n}!IVLoX#H2>5%{igU$cDHRwV@mCC%Wb<43pc?nc z``4O1L(Q=_;si1XcIN#`9YZ&iZS=IG=ojyuJn3BMlUpe>8nyXmps zskuFId|ELsvplshM23lA)S^y?aP~*jU{yXoD)$qL*UhkGmiru;Op~l;Uqw;GyWEUU z8Kq>thD?dKUz?ft(fAn7vj%+4c`xpUASWfg>eGPM&Q$uH^2o`&d+l5km61CE*7l|n z%%}HKbl>0dR55uyVrx$#{h)>uNHRT-=Hx6_pxHUh7W*|yKcU3aM$cfBvmY1cyL%#X z$h(9~yl2*e+GXHQp!-~vkq7+DN%u-_m?CwPI!5ij!)_oER@pu?+R`q_?{b^`o~E*J zkqvY=+*EPsqXS;>lri33nb_Zmu}g32opS~draC$lpe@vA&+32p36dLrxZxMBzhXwOzMW53iUaT*a4S?+^hQ$hsh*D5D z4FUgme*1Bmw z2+m3hty9f1IqQLG8SI=7Wh%Qdc9p*n7?-E+FNnd=S>v_IuF7NTs{TJ;+(dXS> znB>SrwKj|fkdcyhSqtkAjec#$rB`zf6&=(uB&XCcaK{MD1)m)(MsbN%HI9=|-Eh#G zfYmsFtlU*S+d-n8F<{xiua@*GT|- z?14)>c0`%nUi}GZB>>x;l32lmYYDi%_T=aau)?2Fm)z_Z3q=9I{Uhc5=VwV>pU%QcNp|Lve}E@e4mbu8!fc#h3LSsO+oIXODpQ2 zRk&#N;6&0|S_itr#x8Wa8N=qZ*%A3mzdW34gV(^zE_sV)W?be_$d@eotxP+sR4rVw=Az2pt~(9-xO6ZIu6vhw^2QD4j+dDbB=A^0f8D6wo+R%m;80Kc z)w3PaovSY*|C;f6v+2dlmtTThpu22YP)w_}Li=(Ha7w_&v-3qzioFk`&S z(3lq}aPuxZ9?}MvM3Z`1PrhzN-hN?g3B8<2(oy1YbU5R>Wv+qflVt8wKNz_)MR3`y zHm_)DN0hp~A_gZq(n=kYw6IBCYtoq_ZoM}odCf)_q!iEd^WO`3{rVa|<^3z43cVov z(lT1*83l!u6X)64Z9|k8l zNVaz$EC-NRMrZN(&+!UB+Uk!O3k9qkxb>}lzZ7P_?@qcbynknko&G_0|4wq=Jyp6T zqu*izw#;)(yZ3`qAbp_m#Zp^v%(tcq507k2N%oeO8u$9l?dfSyxofcevTKvXYA3n< z!7~82XXYZ8GY+R z?NMZJ)#_G|?vd@>nCPnrM*`P8*CQ#ZYx{)@eQ&G`#+QwYmR!$kYrLb-)-J+N7_x51 zG&ef{aW4>K=H&nKWhNkhElo{7#m5+cPni{7B!?PTXLfO?nFjo?d-CVc5R9tqsvxD5 zPsJ)7JN-Cmf>)G))X-kiM%a^;u)4AfmGk2E0fUf*Jr;@Z@~XVAlY8Rb9gJLSON`z6M#tTKQS9xL4nn&J zZa2|}n5jKo(3|~R_P>2{g66+mjXJz3J6@OEP^&*;0WYs`-upvOzVLE|ROx@%cmEGj z=zq9-Z>NCKam=y#-?smU2cr0|1i=bvG6%z}6!V6B_CJ)IQhFuKf7`)1bY6B;;+O_}``|9+ZDX=A%C~ z*3CKZUgY@8ow?caz*7UoOp)~;0-|hi1bJlhC#Fz&j4_`(KXW1?WBN;9gw$VJqJiXE z-UJI3$$0hG61`l#!~4qY7XZQW=k1bLxXOUOss|9(=f7Zkvl2^06b)IQu;gvw_{-LL znSb?FN|`|(!6kVhcfljf`2AQs%y|<=A122C{!9Y`7ag8 zYd=vd(*w(*yinNO@JT7M7&3*F=PzHDRZ&!2c?b3W>-il(n(TRxc!Cta{_jYaDrcfl zGcGp3N6`cQYvQGagG!edettqB4Jr6j(Md=Wd8KbjAeYf#nYxFz0K8TwBjl{iBftcEh52^G6yLgN# zf0hUEt=A_vESrR?yZ<(Fp(R>VTYF&4J43r@C+FlKHHKCU}yAx z_;8+KT#L5Bgpcu8eERf{oSTC-H#6gZSN$FDlwhEW<}YE*dr<7pU^e`45lbYrhp~z2 znRA8_LqXY0@Ahp1(DCu#D7n%(c{S`_TY=;>gy(zxIl8IP%UU2d>kLXKV$fktSiAah z$FkS2Qgpz~4&*Ha7dDFm*Hk5a z_DkuRn6l*M><5fHTv8wST0;GP5GSx9tE?YVhn)LXtn)5TEuH;YlHk2 zED*6O6iONS5PlW0<%V1{7Gx%u%>2)So_B2aub)`yHQ1MAW8sP6&fmNZh_z{FF>Tl` z?$S1d$7n&Qi-Fx!PEM{Y58@O}Ovx(lD7BQC0+vR}kTp#G{0G|FHyA0zw6&uu0~;RV z%V=w}AF2O|W#6J;4B}x0!|mZYdYu)Kp((^jftqKX>#JijT&zlUFf z^E-^J1!~CJl>iELnPx>pk4uJKU{U~>e*o57EN13*BxzzhHs2uPk3`Z1WuE+K0(j(F zKo|yt=>yacEY@ehq!p^b=0O068M4058>4f)-@|xab2!Id70cUM>$yByrHrJ>+7|-M z$K39-XLyM)`YwCn6&HcTas5e)wY^6Qh6lXYrqX8Vr?IsqY8|OF`XJ)nnd#=`2TDw zr$;M9<3R5^WA8^x5!0godv4t*NZ6 ztYXC9&obSFviI{(7muumkJp^N{b$#y?`MwRpt~PFzbJS3*ox0uIH9}L;SpY8VIkQ$ zPTsjcGxYX0QbtDR`P6w)Q5`~vcX%}q3k-M)N>e^v4C{y>hjoB=jD*A!PROl!>Y)_) z=TmiCYY{;b9;pg&4s(wnF@mS8YE#y+3o+t(C^a^$MNs{DvzN=$>KE5q6A%}V0f{Tx zp>nK{e;Y^e;jKly4l(5U_a)vTk<<@%)hmWJQ zrmpr(O*+0(5&PTcX%7_n9SX=24c=||{N2&ha6RzmdDmRI{`}20?rJ_Wan|4USm)dF hDSh(c|9jwAJ*rT^`*KDsA`=h(Daj#Z^KKiz_&?TKVUYj; From 25a567d62fb215b3a32a3503e3ef60c1102620d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sun, 11 Mar 2018 13:49:13 +0100 Subject: [PATCH 38/59] Introduce sidebar with file browser --- resources/side-panel.ui | 141 +++++++++++ src/file_browser.rs | 540 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/ui.rs | 65 ++++- 4 files changed, 742 insertions(+), 5 deletions(-) create mode 100644 resources/side-panel.ui create mode 100644 src/file_browser.rs diff --git a/resources/side-panel.ui b/resources/side-panel.ui new file mode 100644 index 0000000..2e595cb --- /dev/null +++ b/resources/side-panel.ui @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + True + False + + + True + False + filebrowser.cd + Go To Directory + + + + + True + False + + + + + True + False + filebrowser.reload + Reload + + + + + True + False + Show Hidden Files + + + + + + + + + + + + + + + + + 150 + False + vertical + + + False + False + 6 + dir_list_model + 1 + + + 6 + + + 1 + + + + + 6 + end + + + 0 + + + + + False + True + 0 + + + + + False + + + False + file_browser_tree_store + False + False + 20 + True + + + + + + autosize + + + 6 + + + 3 + + + + + + 0 + + + + + + + + + True + True + 1 + + + + + diff --git a/src/file_browser.rs b/src/file_browser.rs new file mode 100644 index 0000000..04f0f8b --- /dev/null +++ b/src/file_browser.rs @@ -0,0 +1,540 @@ +use std::cell::RefCell; +use std::cmp::Ordering; +use std::io; +use std::fs; +use std::fs::DirEntry; +use std::path::{Component, Path, PathBuf}; +use std::rc::Rc; +use std::ops::Deref; + +use gio; +use gio::prelude::*; +use gtk; +use gtk::MenuExt; +use gtk::prelude::*; + +use neovim_lib::NeovimApi; + +use nvim::{ErrorReport, NeovimClient, NeovimRef}; +use shell; + +const ICON_FOLDER_CLOSED: &str = "folder-symbolic"; +const ICON_FOLDER_OPEN: &str = "folder-open-symbolic"; +const ICON_FILE: &str = "text-x-generic-symbolic"; + +struct Components { + dir_list_model: gtk::TreeStore, + dir_list: gtk::ComboBox, + context_menu: gtk::Menu, + show_hidden_checkbox: gtk::CheckMenuItem, + cd_action: gio::SimpleAction, +} + +struct State { + current_dir: String, + show_hidden: bool, + selected_path: Option, +} + +pub struct FileBrowserWidget { + store: gtk::TreeStore, + tree: gtk::TreeView, + widget: gtk::Box, + nvim: Option>, + comps: Components, + state: Rc>, +} + +impl Deref for FileBrowserWidget { + type Target = gtk::Box; + + fn deref(&self) -> >k::Box { + &self.widget + } +} + +#[derive(Copy, Clone, Debug)] +enum FileType { + File, + Dir, +} + +#[allow(dead_code)] +enum Column { + Filename, + Path, + FileType, + IconName, +} + +impl FileBrowserWidget { + pub fn new() -> Self { + let builder = gtk::Builder::new_from_string(include_str!("../resources/side-panel.ui")); + let widget: gtk::Box = builder.get_object("file_browser").unwrap(); + let tree: gtk::TreeView = builder.get_object("file_browser_tree_view").unwrap(); + let store: gtk::TreeStore = builder.get_object("file_browser_tree_store").unwrap(); + let dir_list_model: gtk::TreeStore = builder.get_object("dir_list_model").unwrap(); + let dir_list: gtk::ComboBox = builder.get_object("dir_list").unwrap(); + let context_menu: gtk::Menu = builder.get_object("file_browser_context_menu").unwrap(); + let show_hidden_checkbox: gtk::CheckMenuItem = builder + .get_object("file_browser_show_hidden_checkbox") + .unwrap(); + + let file_browser = FileBrowserWidget { + store, + tree, + widget, + nvim: None, + comps: Components { + dir_list_model, + dir_list, + context_menu, + show_hidden_checkbox, + cd_action: gio::SimpleAction::new("cd", None), + }, + state: Rc::new(RefCell::new(State { + current_dir: "".to_owned(), + show_hidden: false, + selected_path: None, + })), + }; + file_browser + } + + fn nvim(&self) -> Option { + self.nvim.as_ref().unwrap().nvim() + } + + pub fn init(&mut self, shell_state: &shell::State) { + // Initialize values. + let nvim = shell_state.nvim_clone(); + self.nvim = Some(nvim); + let dir = get_current_dir(&mut self.nvim().unwrap()); + update_dir_list(&dir, &self.comps.dir_list_model, &self.comps.dir_list); + self.state.borrow_mut().current_dir = dir; + + // Populate tree. + tree_reload(&self.store, &self.state.borrow()); + + let store = &self.store; + let state_ref = &self.state; + self.tree.connect_test_expand_row(clone!(store, state_ref => move |_, iter, _| { + store.set(&iter, &[Column::IconName as u32], &[&ICON_FOLDER_OPEN]); + // We cannot recursively populate all directories. Instead, we have prepared a single + // empty child entry for all non-empty directories, so the row will be expandable. Now, + // when a directory is expanded, populate its children. + let state = state_ref.borrow(); + if let Some(child) = store.iter_children(iter) { + let filename = store.get_value(&child, Column::Filename as i32); + if filename.get::<&str>().is_none() { + store.remove(&child); + let dir_value = store.get_value(&iter, Column::Path as i32); + if let Some(dir) = dir_value.get() { + populate_tree_nodes(&store, &state, dir, Some(iter)); + } + } else { + // This directory is already populated, i.e. it has been expanded and collapsed + // again. Rows further down the tree might have been silently collapsed without + // getting an event. Update their folder icon. + let mut tree_path = store.get_path(&child).unwrap(); + while let Some(iter) = store.get_iter(&tree_path) { + tree_path.next(); + let file_type = store + .get_value(&iter, Column::FileType as i32) + .get::(); + if file_type == Some(FileType::Dir as u8) { + store.set(&iter, &[Column::IconName as u32], &[&ICON_FOLDER_CLOSED]); + } + } + } + } + Inhibit(false) + })); + + self.tree.connect_row_collapsed(clone!(store => move |_, iter, _| { + store.set(&iter, &[Column::IconName as u32], &[&ICON_FOLDER_CLOSED]); + })); + + // Further initialization. + self.init_actions(); + self.init_subscriptions(shell_state); + self.connect_events(); + } + + fn init_actions(&self) { + let actions = gio::SimpleActionGroup::new(); + + let store = &self.store; + let state_ref = &self.state; + let nvim_ref = self.nvim.as_ref().unwrap(); + + let reload_action = gio::SimpleAction::new("reload", None); + reload_action.connect_activate(clone!(store, state_ref => move |_, _| { + tree_reload(&store, &state_ref.borrow()); + })); + actions.add_action(&reload_action); + + let cd_action = &self.comps.cd_action; + cd_action.connect_activate(clone!(state_ref, nvim_ref => move |_, _| { + let mut nvim = nvim_ref.nvim().unwrap(); + if let Some(ref path) = state_ref.borrow().selected_path { + nvim.set_current_dir(&path).report_err(); + } + })); + actions.add_action(cd_action); + + self.comps + .context_menu + .insert_action_group("filebrowser", &actions); + } + + fn init_subscriptions(&self, shell_state: &shell::State) { + // Always set the current working directory as the root of the file browser. + let store = &self.store; + let state_ref = &self.state; + let dir_list_model = &self.comps.dir_list_model; + let dir_list = &self.comps.dir_list; + shell_state.subscribe( + "DirChanged", + &["getcwd()"], + clone!(store, state_ref, dir_list_model, dir_list => move |args| { + let dir = args.into_iter().next().unwrap(); + let mut state = state_ref.borrow_mut(); + if dir != *state.current_dir { + update_dir_list(&dir, &dir_list_model, &dir_list); + state.current_dir = dir; + tree_reload(&store, &state); + } + }), + ); + + // Reveal the file of an entered buffer in the file browser and select the entry. + let tree = &self.tree; + let subscription = shell_state.subscribe( + "BufEnter", + &["getcwd()", "expand('%:p')"], + clone!(tree, store => move |args| { + let mut args_iter = args.into_iter(); + let dir = args_iter.next().unwrap(); + let file_path = args_iter.next().unwrap(); + let could_reveal = + if let Ok(rel_path) = Path::new(&file_path).strip_prefix(&Path::new(&dir)) { + reveal_path_in_tree(&store, &tree, &rel_path) + } else { + false + }; + if !could_reveal { + tree.get_selection().unselect_all(); + } + }), + ); + shell_state.run_now(&subscription); + } + + fn connect_events(&self) { + // Open file / go to dir, when user clicks on an entry. + let store = &self.store; + let nvim_ref = self.nvim.as_ref().unwrap(); + self.tree.connect_row_activated(clone!(store, nvim_ref => move |tree, path, _| { + let mut nvim = nvim_ref.nvim().unwrap(); + let iter = store.get_iter(path).unwrap(); + let file_type = store + .get_value(&iter, Column::FileType as i32) + .get::() + .unwrap(); + let file_path = store + .get_value(&iter, Column::Path as i32) + .get::() + .unwrap(); + if file_type == FileType::Dir as u8 { + let expanded = tree.row_expanded(path); + if expanded { + tree.collapse_row(path); + } else { + tree.expand_row(path, false); + } + } else { + // FileType::File + let dir = get_current_dir(&mut nvim); + let dir = Path::new(&dir); + let file_path = if let Some(rel_path) = Path::new(&file_path) + .strip_prefix(&dir) + .ok() + .and_then(|p| p.to_str()) + { + rel_path + } else { + &file_path + }; + nvim.command(&format!(":e {}", file_path)).report_err(); + } + })); + + // Connect directory list. + let nvim_ref = self.nvim.as_ref().unwrap(); + self.comps.dir_list.connect_changed(clone!(nvim_ref => move |dir_list| { + if let Some(iter) = dir_list.get_active_iter() { + let model = dir_list.get_model().unwrap(); + if let Some(dir) = model.get_value(&iter, 2).get::<&str>() { + let mut nvim = nvim_ref.nvim().unwrap(); + nvim.set_current_dir(dir).report_err(); + } + } + })); + + let store = &self.store; + let state_ref = &self.state; + let context_menu = &self.comps.context_menu; + let cd_action = &self.comps.cd_action; + self.tree.connect_button_press_event( + clone!(store, state_ref, context_menu, cd_action => move |tree, ev_btn| { + // Open context menu on right click. + if ev_btn.get_button() == 3 { + context_menu.popup_at_pointer(&**ev_btn); + let (pos_x, pos_y) = ev_btn.get_position(); + let iter = tree + .get_path_at_pos(pos_x as i32, pos_y as i32) + .and_then(|(path, _, _, _)| path) + .and_then(|path| store.get_iter(&path)); + let file_type = iter + .as_ref() + .and_then(|iter| { + store + .get_value(&iter, Column::FileType as i32) + .get::() + }); + // Enable the "Go To Directory" action only if the user clicked on a folder. + cd_action.set_enabled(file_type == Some(FileType::Dir as u8)); + let path = iter + .and_then(|iter| { + store + .get_value(&iter, Column::Path as i32) + .get::() + }); + state_ref.borrow_mut().selected_path = path; + } + Inhibit(false) + }), + ); + + // Show / hide hidden files when corresponding menu item is toggled. + self.comps.show_hidden_checkbox.connect_toggled(clone!(state_ref, store => move |ev| { + let mut state = state_ref.borrow_mut(); + state.show_hidden = ev.get_active(); + tree_reload(&store, &state); + })); + } +} + +/// Compare function for dir entries. +/// +/// Sorts directories above files. +fn cmp_dirs_first(lhs: &DirEntry, rhs: &DirEntry) -> io::Result { + let lhs_metadata = fs::metadata(lhs.path())?; + let rhs_metadata = fs::metadata(rhs.path())?; + if lhs_metadata.file_type() == rhs_metadata.file_type() { + Ok(lhs.path().cmp(&rhs.path())) + } else { + if lhs_metadata.is_dir() { + Ok(Ordering::Less) + } else { + Ok(Ordering::Greater) + } + } +} + +/// Clears an repopulate the entire tree. +fn tree_reload(store: >k::TreeStore, state: &State) { + let dir = &state.current_dir; + store.clear(); + populate_tree_nodes(store, state, dir, None); +} + +/// Updates the dirctory list on top of the file browser. +/// +/// The list represents the path the the current working directory. If the new cwd is a parent of +/// the old one, the list is kept and only the active entry is updated. Otherwise, the list is +/// replaced with the new path and the last entry is marked active. +fn update_dir_list(dir: &str, dir_list_model: >k::TreeStore, dir_list: >k::ComboBox) { + // The current working directory path. + let complete_path = Path::new(dir).canonicalize().unwrap(); + let mut path = PathBuf::new(); + let mut components = complete_path.components(); + let mut next = components.next(); + + // Iterator over existing dir_list model. + let mut dir_list_iter = dir_list_model.get_iter_first(); + + // Whether existing entries up to the current position of dir_list_iter are a prefix of the + // new current working directory path. + let mut is_prefix = true; + + // Iterate over components of the cwd. Simultaneously move dir_list_iter forward. + while let Some(dir) = next { + next = components.next(); + let dir_name = &*dir.as_os_str().to_string_lossy(); + // Assemble path up to current component. + path.push(Path::new(&dir)); + let path_str = path.to_str().unwrap_or_else(|| { + error!( + "Could not convert path to string: {}\n + Directory chooser will not work for that entry.", + path.to_string_lossy() + ); + "" + }); + // Use the current entry of dir_list, if any, otherwise append a new one. + let current_iter = dir_list_iter.unwrap_or_else(|| dir_list_model.append(None)); + // Check if the current entry is still part of the new cwd. + if is_prefix && dir_list_model.get_value(¤t_iter, 0).get::<&str>() != Some(&dir_name) + { + is_prefix = false; + } + if next.is_some() { + // Update dir_list entry. + dir_list_model.set( + ¤t_iter, + &[0, 1, 2], + &[&dir_name, &ICON_FOLDER_CLOSED, &path_str], + ); + } else { + // We reached the last component of the new cwd path. Set the active entry of dir_list + // to this one. + dir_list_model.set( + ¤t_iter, + &[0, 1, 2], + &[&dir_name, &ICON_FOLDER_OPEN, &path_str], + ); + dir_list.set_active_iter(¤t_iter); + }; + // Advance dir_list_iter. + dir_list_iter = if dir_list_model.iter_next(¤t_iter) { + Some(current_iter) + } else { + None + } + } + // We updated the dir list to the point of the current working directory. + if let Some(iter) = dir_list_iter { + if is_prefix { + // If we didn't change any entries to this point and the list contains further entries, + // the remaining ones are subdirectories of the cwd and we keep them. + loop { + dir_list_model.set(&iter, &[1], &[&ICON_FOLDER_CLOSED]); + if !dir_list_model.iter_next(&iter) { + break; + } + } + } else { + // If we needed to change entries, the following ones are not directories under the + // cwd and we clear them. + while dir_list_model.remove(&iter) {} + } + } +} + +/// Populates one level, i.e. one directory of the file browser tree. +fn populate_tree_nodes( + store: >k::TreeStore, + state: &State, + dir: &str, + parent: Option<>k::TreeIter>, +) { + let path = Path::new(dir); + let iter = path.read_dir() + .expect("read dir failed") + .filter_map(Result::ok); + let mut entries: Vec = if state.show_hidden { + iter.collect() + } else { + iter.filter(|entry| !entry.file_name().to_string_lossy().starts_with(".")) + .filter(|entry| !entry.file_name().to_string_lossy().ends_with("~")) + .collect() + }; + entries.sort_unstable_by(|lhs, rhs| cmp_dirs_first(lhs, rhs).unwrap_or(Ordering::Equal)); + for entry in entries { + let path = if let Some(path) = entry.path().to_str() { + path.to_owned() + } else { + // Skip paths that contain invalid unicode. + continue; + }; + let filename = entry.file_name().to_str().unwrap().to_owned(); + let file_type = if let Ok(metadata) = fs::metadata(entry.path()) { + let file_type = metadata.file_type(); + if file_type.is_dir() { + FileType::Dir + } else if file_type.is_file() { + FileType::File + } else { + continue; + } + } else { + // In case of invalid symlinks, we cannot obtain metadata. + continue; + }; + let icon = match file_type { + FileType::Dir => ICON_FOLDER_CLOSED, + FileType::File => ICON_FILE, + }; + // When we get until here, we want to show the entry. Append it to the tree. + let iter = store.append(parent); + store.set( + &iter, + &[0, 1, 2, 3], + &[&filename, &path, &(file_type as u8), &icon], + ); + // For directories, check whether the directory is empty. If not, append a single empty + // entry, so the expand arrow is shown. Its contents are dynamically populated when + // expanded (see `init`). + if let FileType::Dir = file_type { + let not_empty = if let Ok(mut dir) = entry.path().read_dir() { + dir.next().is_some() + } else { + false + }; + if not_empty { + let iter = store.append(&iter); + store.set(&iter, &[], &[]); + } + } + } +} + +fn get_current_dir(nvim: &mut NeovimRef) -> String { + nvim.eval("getcwd()") + .as_ref() + .ok() + .and_then(|s| s.as_str()) + .expect("Couldn't get working directory") + .to_owned() +} + +/// Reveals and selects the given file in the file browser. +/// +/// Returns `true` if the file could be successfully revealed. +fn reveal_path_in_tree(store: >k::TreeStore, tree: >k::TreeView, rel_file_path: &Path) -> bool { + let mut tree_path = gtk::TreePath::new(); + 'components: for component in rel_file_path.components() { + if let Component::Normal(component) = component { + tree_path.down(); + while let Some(iter) = store.get_iter(&tree_path) { + let entry_value = store.get_value(&iter, Column::Filename as i32); + let entry = entry_value.get::<&str>().unwrap(); + if component == entry { + tree.expand_row(&tree_path, false); + continue 'components; + } + tree_path.next(); + } + return false; + } else { + return false; + } + } + if tree_path.get_depth() == 0 { + return false; + } + tree.set_cursor(&tree_path, None, false); + true +} diff --git a/src/main.rs b/src/main.rs index 92f0cb9..75f9600 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,7 @@ mod popup_menu; mod project; mod tabline; mod error; +mod file_browser; mod subscriptions; use std::env; diff --git a/src/ui.rs b/src/ui.rs index 5b4bc37..a01706f 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use gdk; use gtk; use gtk::prelude::*; -use gtk::{AboutDialog, ApplicationWindow, Button, HeaderBar, SettingsExt}; +use gtk::{AboutDialog, ApplicationWindow, Button, HeaderBar, Orientation, Paned, SettingsExt}; use gio::prelude::*; use gio::{Menu, MenuExt, MenuItem, SimpleAction}; use toml; @@ -17,6 +17,7 @@ use shell::{self, Shell, ShellOptions}; use shell_dlg; use project::Projects; use plug_manager; +use file_browser::FileBrowserWidget; use subscriptions::SubscriptionHandle; macro_rules! clone { @@ -38,6 +39,7 @@ macro_rules! clone { const DEFAULT_WIDTH: i32 = 800; const DEFAULT_HEIGHT: i32 = 600; +const DEFAULT_SIDEBAR_WIDTH: i32 = 200; pub struct Ui { initialized: bool, @@ -46,6 +48,7 @@ pub struct Ui { shell: Rc>, projects: Rc>, plug_manager: Arc>, + file_browser: Arc>, } pub struct Components { @@ -88,6 +91,7 @@ impl Ui { let plug_manager = plug_manager::Manager::new(); let plug_manager = Arc::new(UiMutex::new(plug_manager)); + let file_browser = Arc::new(UiMutex::new(FileBrowserWidget::new())); let comps = Arc::new(UiMutex::new(Components::new())); let settings = Rc::new(RefCell::new(Settings::new())); let shell = Rc::new(RefCell::new(Shell::new(settings.clone(), options))); @@ -102,6 +106,7 @@ impl Ui { settings, projects, plug_manager, + file_browser, } } @@ -115,6 +120,7 @@ impl Ui { settings.init(); let window = ApplicationWindow::new(app); + let main = Paned::new(Orientation::Horizontal); { // initialize window from comps @@ -144,8 +150,11 @@ impl Ui { comps.window_state.current_width, comps.window_state.current_height, ); + + main.set_position(comps.window_state.sidebar_width); } else { window.set_default_size(DEFAULT_WIDTH, DEFAULT_HEIGHT); + main.set_position(DEFAULT_SIDEBAR_WIDTH); } } @@ -164,10 +173,28 @@ impl Ui { None }; + let show_sidebar_action = + SimpleAction::new_stateful("show-sidebar", None, &false.to_variant()); + let file_browser_ref = self.file_browser.clone(); let comps_ref = self.comps.clone(); - window.connect_size_allocate(move |window, _| { - gtk_window_size_allocate(window, &mut *comps_ref.borrow_mut()) + show_sidebar_action.connect_change_state(move |action, value| { + if let Some(ref value) = *value { + action.set_state(value); + let is_active = value.get::().unwrap(); + file_browser_ref.borrow().set_visible(is_active); + comps_ref.borrow_mut().window_state.show_sidebar = is_active; + } }); + app.add_action(&show_sidebar_action); + + let comps_ref = self.comps.clone(); + window.connect_size_allocate(clone!(main => move |window, _| { + gtk_window_size_allocate( + window, + &mut *comps_ref.borrow_mut(), + &main, + ); + })); let comps_ref = self.comps.clone(); window.connect_window_state_event(move |_, event| { @@ -181,10 +208,21 @@ impl Ui { }); let shell = self.shell.borrow(); - window.add(&**shell); + let file_browser = self.file_browser.borrow(); + main.pack1(&**file_browser, false, false); + main.pack2(&**shell, true, false); + + window.add(&main); window.show_all(); + if restore_win_state { + // Hide sidebar, if it wasn't shown last time. + // Has to be done after show_all(), so it won't be shown again. + let show_sidebar = self.comps.borrow().window_state.show_sidebar; + show_sidebar_action.change_state(&show_sidebar.to_variant()); + } + let comps_ref = self.comps.clone(); let update_title = shell.state.borrow().subscribe( "BufEnter,DirChanged", @@ -225,12 +263,14 @@ impl Ui { })); let state_ref = self.shell.borrow().state.clone(); + let file_browser_ref = self.file_browser.clone(); let plug_manager_ref = self.plug_manager.clone(); shell.set_nvim_started_cb(Some(move || { let state = state_ref.borrow(); plug_manager_ref .borrow_mut() .init_nvim_client(state_ref.borrow().nvim_clone()); + file_browser_ref.borrow_mut().init(&state); state.set_autocmds(); state.run_now(&update_title); if let Some(ref update_subtitle) = update_subtitle { @@ -297,6 +337,10 @@ impl Ui { section.append_item(&MenuItem::new("New Window", "app.new-window")); menu.append_section(None, §ion); + let section = Menu::new(); + section.append_item(&MenuItem::new("Sidebar", "app.show-sidebar")); + menu.append_section(None, §ion); + let section = Menu::new(); section.append_item(&MenuItem::new("Plugins", "app.Plugins")); section.append_item(&MenuItem::new("About", "app.HelpAbout")); @@ -353,12 +397,19 @@ fn gtk_delete(comps: &UiMutex, shell: &RefCell) -> Inhibit { }) } -fn gtk_window_size_allocate(app_window: >k::ApplicationWindow, comps: &mut Components) { +fn gtk_window_size_allocate( + app_window: >k::ApplicationWindow, + comps: &mut Components, + main: &Paned, +) { if !app_window.is_maximized() { let (current_width, current_height) = app_window.get_size(); comps.window_state.current_width = current_width; comps.window_state.current_height = current_height; } + if comps.window_state.show_sidebar { + comps.window_state.sidebar_width = main.get_position(); + } } fn gtk_window_state_event(event: &gdk::EventWindowState, comps: &mut Components) { @@ -372,6 +423,8 @@ struct WindowState { current_width: i32, current_height: i32, is_maximized: bool, + show_sidebar: bool, + sidebar_width: i32, } impl WindowState { @@ -380,6 +433,8 @@ impl WindowState { current_width: DEFAULT_WIDTH, current_height: DEFAULT_HEIGHT, is_maximized: false, + show_sidebar: false, + sidebar_width: DEFAULT_SIDEBAR_WIDTH, } } } From a971d5f56ee8e034c2d313af7e375ab9a30d1c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Wed, 14 Mar 2018 20:36:00 +0100 Subject: [PATCH 39/59] Don't canonicalize cwd path for dir list --- src/file_browser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file_browser.rs b/src/file_browser.rs index 04f0f8b..660d21d 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -357,7 +357,7 @@ fn tree_reload(store: >k::TreeStore, state: &State) { /// replaced with the new path and the last entry is marked active. fn update_dir_list(dir: &str, dir_list_model: >k::TreeStore, dir_list: >k::ComboBox) { // The current working directory path. - let complete_path = Path::new(dir).canonicalize().unwrap(); + let complete_path = Path::new(dir); let mut path = PathBuf::new(); let mut components = complete_path.components(); let mut next = components.next(); From d02b54a521f0124c29962d79f8e582e965259260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sat, 17 Mar 2018 16:30:49 +0100 Subject: [PATCH 40/59] Use error! in place of expect() calls --- src/file_browser.rs | 58 ++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/file_browser.rs b/src/file_browser.rs index 660d21d..2b992ac 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -109,9 +109,10 @@ impl FileBrowserWidget { // Initialize values. let nvim = shell_state.nvim_clone(); self.nvim = Some(nvim); - let dir = get_current_dir(&mut self.nvim().unwrap()); - update_dir_list(&dir, &self.comps.dir_list_model, &self.comps.dir_list); - self.state.borrow_mut().current_dir = dir; + if let Some(dir) = get_current_dir(&mut self.nvim().unwrap()) { + update_dir_list(&dir, &self.comps.dir_list_model, &self.comps.dir_list); + self.state.borrow_mut().current_dir = dir; + } // Populate tree. tree_reload(&self.store, &self.state.borrow()); @@ -255,18 +256,19 @@ impl FileBrowserWidget { } } else { // FileType::File - let dir = get_current_dir(&mut nvim); - let dir = Path::new(&dir); - let file_path = if let Some(rel_path) = Path::new(&file_path) - .strip_prefix(&dir) - .ok() - .and_then(|p| p.to_str()) - { - rel_path - } else { - &file_path - }; - nvim.command(&format!(":e {}", file_path)).report_err(); + if let Some(dir) = get_current_dir(&mut nvim) { + let dir = Path::new(&dir); + let file_path = if let Some(rel_path) = Path::new(&file_path) + .strip_prefix(&dir) + .ok() + .and_then(|p| p.to_str()) + { + rel_path + } else { + &file_path + }; + nvim.command(&format!(":e {}", file_path)).report_err(); + } } })); @@ -441,9 +443,14 @@ fn populate_tree_nodes( parent: Option<>k::TreeIter>, ) { let path = Path::new(dir); - let iter = path.read_dir() - .expect("read dir failed") - .filter_map(Result::ok); + let read_dir = match path.read_dir() { + Ok(read_dir) => read_dir, + Err(err) => { + error!("Couldn't populate tree: {}", err); + return; + } + }; + let iter = read_dir.filter_map(Result::ok); let mut entries: Vec = if state.show_hidden { iter.collect() } else { @@ -501,13 +508,14 @@ fn populate_tree_nodes( } } -fn get_current_dir(nvim: &mut NeovimRef) -> String { - nvim.eval("getcwd()") - .as_ref() - .ok() - .and_then(|s| s.as_str()) - .expect("Couldn't get working directory") - .to_owned() +fn get_current_dir(nvim: &mut NeovimRef) -> Option { + match nvim.eval("getcwd()") { + Ok(cwd) => cwd.as_str().map(|s| s.to_owned()), + Err(err) => { + error!("Couldn't get cwd: {}", err); + None + } + } } /// Reveals and selects the given file in the file browser. From a1452561829e7bb7df04be4f71d6f5c0bc8adf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sun, 18 Mar 2018 18:45:06 +0100 Subject: [PATCH 41/59] Use nvim.command_async where possible --- src/file_browser.rs | 33 +++++++++++++++++---------------- src/plug_manager/vim_plug.rs | 6 ++++-- src/shell.rs | 12 ++++++++---- src/subscriptions.rs | 11 +++++------ src/tabline.rs | 6 ++++-- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/file_browser.rs b/src/file_browser.rs index 2b992ac..810546f 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -13,7 +13,7 @@ use gtk; use gtk::MenuExt; use gtk::prelude::*; -use neovim_lib::NeovimApi; +use neovim_lib::{NeovimApi, NeovimApiAsync}; use nvim::{ErrorReport, NeovimClient, NeovimRef}; use shell; @@ -235,9 +235,9 @@ impl FileBrowserWidget { fn connect_events(&self) { // Open file / go to dir, when user clicks on an entry. let store = &self.store; + let state_ref = &self.state; let nvim_ref = self.nvim.as_ref().unwrap(); - self.tree.connect_row_activated(clone!(store, nvim_ref => move |tree, path, _| { - let mut nvim = nvim_ref.nvim().unwrap(); + self.tree.connect_row_activated(clone!(store, state_ref, nvim_ref => move |tree, path, _| { let iter = store.get_iter(path).unwrap(); let file_type = store .get_value(&iter, Column::FileType as i32) @@ -256,19 +256,20 @@ impl FileBrowserWidget { } } else { // FileType::File - if let Some(dir) = get_current_dir(&mut nvim) { - let dir = Path::new(&dir); - let file_path = if let Some(rel_path) = Path::new(&file_path) - .strip_prefix(&dir) - .ok() - .and_then(|p| p.to_str()) - { - rel_path - } else { - &file_path - }; - nvim.command(&format!(":e {}", file_path)).report_err(); - } + let cwd = &state_ref.borrow().current_dir; + let cwd = Path::new(cwd); + let file_path = if let Some(rel_path) = Path::new(&file_path) + .strip_prefix(&cwd) + .ok() + .and_then(|p| p.to_str()) + { + rel_path + } else { + &file_path + }; + nvim_ref.nvim().unwrap().command_async(&format!(":e {}", file_path)) + .cb(|r| r.report_err()) + .call(); } })); diff --git a/src/plug_manager/vim_plug.rs b/src/plug_manager/vim_plug.rs index e30e5b0..3bff9a6 100644 --- a/src/plug_manager/vim_plug.rs +++ b/src/plug_manager/vim_plug.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use neovim_lib::NeovimApi; +use neovim_lib::{NeovimApi, NeovimApiAsync}; use nvim::{NeovimClient, ErrorReport, NeovimRef}; use value::ValueMapExt; @@ -84,7 +84,9 @@ impl Manager { pub fn reload(&self, path: &str) { if let Some(mut nvim) = self.nvim() { - nvim.command(&format!("source {}", path)).report_err(); + nvim.command_async(&format!("source {}", path)) + .cb(|r| r.report_err()) + .call() } } } diff --git a/src/shell.rs b/src/shell.rs index ae4be61..48cca72 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -184,13 +184,17 @@ impl State { pub fn open_file(&self, path: &str) { if let Some(mut nvim) = self.nvim() { - nvim.command(&format!("e {}", path)).report_err(); + nvim.command_async(&format!("e {}", path)) + .cb(|r| r.report_err()) + .call(); } } pub fn cd(&self, path: &str) { if let Some(mut nvim) = self.nvim() { - nvim.command(&format!("cd {}", path)).report_err(); + nvim.command_async(&format!("cd {}", path)) + .cb(|r| r.report_err()) + .call(); } } @@ -600,7 +604,7 @@ impl Shell { let nvim = state.nvim(); if let Some(mut nvim) = nvim { - nvim.command(":wa").report_err(); + nvim.command_async(":wa").cb(|r| r.report_err()).call(); } } @@ -609,7 +613,7 @@ impl Shell { let nvim = state.nvim(); if let Some(mut nvim) = nvim { - nvim.command(":tabe").report_err(); + nvim.command_async(":tabe").cb(|r| r.report_err()).call(); } } diff --git a/src/subscriptions.rs b/src/subscriptions.rs index 40b7629..50db097 100644 --- a/src/subscriptions.rs +++ b/src/subscriptions.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; -use neovim_lib::{NeovimApi, Value}; +use neovim_lib::{NeovimApi, NeovimApiAsync, Value}; -use nvim::NeovimRef; +use nvim::{ErrorReport, NeovimRef}; /// A subscription to a Neovim autocmd event. struct Subscription { @@ -93,12 +93,11 @@ impl Subscriptions { .args .iter() .fold("".to_owned(), |acc, arg| acc + ", " + &arg); - nvim.command(&format!( + nvim.command_async(&format!( "au {} * call rpcnotify(1, 'subscription', '{}', {} {})", event_name, event_name, i, args, - )).unwrap_or_else(|err| { - error!("Could not set autocmd: {}", err); - }); + )).cb(|r| r.report_err()) + .call(); } } } diff --git a/src/tabline.rs b/src/tabline.rs index f6eb8b3..20f4d57 100644 --- a/src/tabline.rs +++ b/src/tabline.rs @@ -10,7 +10,7 @@ use glib::signal; use pango; -use neovim_lib::NeovimApi; +use neovim_lib::{NeovimApi, NeovimApiAsync}; use neovim_lib::neovim_api::Tabpage; use nvim; @@ -42,7 +42,9 @@ impl State { fn close_tab(&self, idx: u32) { if let Some(mut nvim) = self.nvim.as_ref().unwrap().nvim() { - nvim.command(&format!(":tabc {}", idx + 1)).report_err(); + nvim.command_async(&format!(":tabc {}", idx + 1)) + .cb(|r| r.report_err()) + .call(); } } } From fdaff9748aa9e81db53a705c8aec2277e75143d9 Mon Sep 17 00:00:00 2001 From: daa Date: Mon, 19 Mar 2018 00:15:58 +0300 Subject: [PATCH 42/59] Update gtk-rs version --- Cargo.lock | 528 ++++++++++++++++++++----------------------- Cargo.toml | 26 +-- src/sys/pango/mod.rs | 10 +- 3 files changed, 265 insertions(+), 299 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 737c315..d84f4da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,22 +8,22 @@ dependencies = [ [[package]] name = "atk-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "atty" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -45,30 +45,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "c_vec" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cairo-rs" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "c_vec 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "c_vec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cairo-sys-rs" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -76,15 +75,6 @@ name = "cfg-if" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "chrono" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "dtoa" version = "0.4.2" @@ -92,14 +82,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "env_logger" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -118,158 +108,162 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "gdk" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-pixbuf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gdk-pixbuf" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gdk-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gio" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gio-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glib" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "glib-sys" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glib-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gobject-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gtk" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-pixbuf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gtk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gtk-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -278,6 +272,14 @@ name = "htmlescape" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "humantime" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.3.4" @@ -290,7 +292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.36" +version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -306,7 +308,7 @@ name = "memchr" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -320,127 +322,100 @@ dependencies = [ "unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "num" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-integer" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-iter" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "num-traits" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "nvim-gtk" version = "0.2.0" dependencies = [ - "cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gtk 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gtk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "neovim-lib 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pangocairo 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pangocairo-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pango" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pango-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pangocairo" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pangocairo-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pangocairo-sys" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -484,9 +459,25 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "quote" -version = "0.3.15" +name = "proc-macro2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-error" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "rand" @@ -494,7 +485,7 @@ version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -504,7 +495,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -523,20 +514,23 @@ dependencies = [ [[package]] name = "regex" -version = "0.2.6" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.4.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "rmp" @@ -554,13 +548,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", "rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde" -version = "1.0.27" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -568,37 +562,38 @@ name = "serde_bytes" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.27" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive_internals" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -608,25 +603,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.11.11" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "termcolor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -637,7 +624,7 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -651,27 +638,22 @@ dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "time" -version = "0.1.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "toml" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ucd-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" -version = "0.0.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -680,7 +662,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -701,11 +683,6 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "winapi" version = "0.3.4" @@ -735,80 +712,77 @@ dependencies = [ [metadata] "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" -"checksum atk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "33a67fd81e1922dddc335887516f2f5254534e89c9d39fa89bca5d79bd150d34" -"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859" +"checksum atk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8dc233521f7bffd3042c31082ea71bd08820abf44bac938fb36591e20f76f39" +"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" -"checksum c_vec 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6237ac5a4b1e81c213c24c6437964c61e646df910a914b4ab1487b46df20bd13" -"checksum cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6b5695f59fd036fe5741bc5a4eb20c78fbe42256e3b08a2af26bbcbe8070bf3" -"checksum cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6e18fecaeac51809db57f45f4553cc0975225a7eb435a7a7e91e5e8113a84d" +"checksum c_vec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11f56b93adbca81a4ed1bb654070013b496a9b0b259a296281f4be24af84debd" +"checksum cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a110f269c2fd382df5fe8bd46dfa5f1b83608aa717fecb6e7a28c08c202f0e13" +"checksum cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0395175ecba60accac076a02c31d143b9dcd9d5eb5316d7163a3273803b765c7" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" -"checksum env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f15f0b172cb4f52ed5dbf47f774a387cd2315d1bf7894ab5af9b083ae27efa5a" +"checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum gdk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e51db95be6565011bcd5cd99f9b17fdd585001057a999b21e09f1e8c28deb9" -"checksum gdk-pixbuf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16160d212ae91abe9f3324c3fb233929ba322dde63585d15cda3336f8c529ed1" -"checksum gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "798f97101eea8180da363d0e80e07ec7ec6d1809306601c0100c1de5bc8b4f52" -"checksum gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4ee916f5f25c5f4b21bd9dcb12a216ae697406940ff9476358c308a8ececada" -"checksum gio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "84ba5a2beb559059a0c9c2bd3681743cdede8d9a36c775840bca800333b22867" -"checksum gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a303bbf7a5e75ab3b627117ff10e495d1b9e97e1d68966285ac2b1f6270091bc" -"checksum glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9b0452824cc63066940f01adc721804919f0b76cdba3cfab977b00b87f16d4a" -"checksum glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9693049613ff52b93013cc3d2590366d8e530366d288438724b73f6c7dc4be8" -"checksum gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60d507c87a71b1143c66ed21a969be9b99a76df234b342d733e787e6c9c7d7c2" -"checksum gtk 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0847c507e52c1feaede13ef56fb4847742438602655449d5f1f782e8633f146f" -"checksum gtk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "905fcfbaaad1b44ec0b4bba9e4d527d728284c62bc2ba41fccedace2b096766f" +"checksum gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd30051ff3d908ff2fc7e5776ffe1c699821e043809f294c3a61004f11d6c3a9" +"checksum gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2d2199eba47ebcb9977ce28179649bdd59305ef465c4e6f9b65aaa41c24e6b5" +"checksum gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df6a3b73e04fafc07f5ebc083f1096a773412e627828e1103a55e921f81187d8" +"checksum gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3162ff940526ddff71bf1f630facee6b5e05d282d125ba0c4c803842819b80c3" +"checksum gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "071fd4e5592b39fdc31318e32d2497842501f539bcedb60c75cd365946642adc" +"checksum gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a57872499171d279f8577ce83837da4cae62b08dd32892236ed67ab7ea61030" +"checksum glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e0be1b1432e227bcd1a9b28db9dc1474a7e7fd4227e08e16f35304f32d09b61" +"checksum glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615bef979b5838526aee99241afc80cfb2e34a8735d4bcb8ec6072598c18a408" +"checksum gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70409d6405db8b1591602fcd0cbe8af52cd9976dd39194442b4c149ba343f86d" +"checksum gtk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "414f3522f550a0b4f65e089f00ffcd3987dab8b0be284cb979aa7f6a03d60516" +"checksum gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d9554cf5b3a85a13fb39258c65b04b262989c1d7a758f8f555b77a478621a91" "checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" +"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" -"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" +"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum neovim-lib 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "db5378fd4e5e33e3f3fd7d2d6519a6598fcb9b4c0c5959da1307e16241cef696" -"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" -"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" -"checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10" -"checksum pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e81c404ab81ea7ea2fc2431a0a7672507b80e4b8bf4b41eac3fc83cc665104e" -"checksum pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34f34a1be107fe16abb2744e0e206bee4b3b07460b5fddd3009a6aaf60bd69ab" -"checksum pangocairo 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41a8620ece55098d741bacf4d3aa52398f85ce83cfe0d8f670fa11de88f52c40" -"checksum pangocairo-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34ec010b38a2a6dafdf3d86ffe1251a0ae759208ec85f78c66c0445481bd5a8" +"checksum num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3c2bd9b9d21e48e956b763c9f37134dc62d9e95da6edb3f672cacb6caf3cd3" +"checksum pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45374801e224373c3c0393cd48073c81093494c8735721e81d1dbaa4096b2767" +"checksum pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94039b3921a4af4058a3e4335e5d15099101f298a92f5afc40bab3a3027594a1" +"checksum pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db4130d0e0567b177178c9568466277ff167cb43a245b2881e203509ea5fbd84" +"checksum pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "decc9523cd5bbba49a67ed8d2ef7dfca957f4760bf420a1ea4a82634da26381c" "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" +"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" +"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5be5347bde0c48cfd8c3fdc0766cdfe9d8a755ef84d620d6794c778c91de8b2b" -"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" +"checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" +"checksum regex-syntax 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2550876c31dc914696a6c2e01cbce8afba79a93c8ae979d2fe051c0230b3756" "checksum rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a3d45d7afc9b132b34a2479648863aa95c5c88e98b32285326a6ebadc80ec5c9" "checksum rmpv 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29af0205707de955a396a1d3c657677c65f791ebabb63c0596c0b2fec0bf6325" -"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" +"checksum serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe95aa0d46f04ce5c3a88bdcd4114ecd6144ed0b2725ebca2f1127744357807" "checksum serde_bytes 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "52b678af90a3aebc4484c22d639bf374eb7d598988edb33fa73c4febd6046a59" -"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" -"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" -"checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb" +"checksum serde_derive 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "23b163a6ce7e1aa897919f9d8e40bd1f8a6f95342ed57727ae31387a01a7a356" +"checksum serde_derive_internals 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "370aa477297975243dc914d0b0e1234927520ec311de507a560fbd1c80f7ab8c" +"checksum serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fab6c4d75bedcf880711c85e39ebf8ccc70d0eba259899047ec5d7436643ee17" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum termcolor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "73e83896da740a4541a6f21606b35f2aa4bada5b65d89dc61114bf9d6ff2dc7e" +"checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd" +"checksum termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "56c456352e44f9f91f774ddeeed27c1ec60a2455ed66d692059acfb1d731bda1" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" -"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" "checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" +"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index fe5f19e..d98b19f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,17 +5,17 @@ authors = ["daa84 "] build = "build.rs" [dependencies] -cairo-rs = "0.3" -pango = "0.3" -pango-sys = "0.5" -pangocairo = "0.4" -pangocairo-sys = "0.6" -glib = "0.4" -glib-sys = "0.5" -gdk = "0.7" -gdk-sys = "0.5" -gio = "0.3" -gobject-sys = "0.5" +cairo-rs = "0.4" +pango = "0.4" +pango-sys = "0.6" +pangocairo = "0.5" +pangocairo-sys = "0.7" +glib = "0.5" +glib-sys = "0.6" +gdk = "0.8" +gdk-sys = "0.6" +gio = "0.4" +gobject-sys = "0.6" #gdk = { git = 'https://github.com/gtk-rs/gdk' } #gdk-sys = { git = 'https://github.com/gtk-rs/sys' } #glib = { git = 'https://github.com/gtk-rs/glib' } @@ -44,12 +44,12 @@ serde_json = "1.0" phf_codegen = "0.7" [dependencies.gtk] -version = "0.3" +version = "0.4" features = ["v3_22"] #git = "https://github.com/gtk-rs/gtk" [dependencies.gtk-sys] -version = "0.5" +version = "0.6" features = ["v3_22"] #git = 'https://github.com/gtk-rs/sys' diff --git a/src/sys/pango/mod.rs b/src/sys/pango/mod.rs index 881bfd9..c0a008d 100644 --- a/src/sys/pango/mod.rs +++ b/src/sys/pango/mod.rs @@ -10,7 +10,6 @@ use std::ptr; use pango; use pango_sys; -use glib_ffi; use glib::translate::*; @@ -23,8 +22,7 @@ pub fn pango_itemize( cached_iter: Option<&mut AttrIterator>, ) -> Vec { unsafe { - //FromGlibPtrContainer::from_glib_full(pango_sys::pango_itemize( - from_glib_full_as_vec(pango_sys::pango_itemize( + FromGlibPtrContainer::from_glib_full(pango_sys::pango_itemize( context.to_glib_none().0, text.as_ptr() as *const i8, start_index as i32, @@ -37,12 +35,6 @@ pub fn pango_itemize( } } - -unsafe fn from_glib_full_as_vec(ptr: *mut glib_ffi::GList) -> Vec { - let num = glib_ffi::g_list_length(ptr) as usize; - FromGlibContainer::from_glib_full_num(ptr, num) -} - pub fn pango_shape( text: &str, offset: usize, From 4cd6cf46dd91e527f7a95c2f757289656d53360d Mon Sep 17 00:00:00 2001 From: daa Date: Mon, 19 Mar 2018 00:40:18 +0300 Subject: [PATCH 43/59] Wrap model on insert single char --- src/ui_model/mod.rs | 9 +++++++++ src/ui_model/model_layout.rs | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index 1ac54f9..5778ffb 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -174,6 +174,15 @@ impl UiModel { ModelRect::new(top as usize, bot as usize, left, right) } + /// Move down all lines except first one + pub fn move_down(&mut self) { + let right = self.columns + 1; + + for row in (1..self.rows + 1).rev() { + self.copy_row(row as i64, 1, 0, right); + } + } + pub fn clear(&mut self) { let (rows, columns) = (self.rows, self.columns); self.clear_region(0, rows - 1, 0, columns - 1); diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index ed23774..9571f06 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -65,6 +65,14 @@ impl ModelLayout { if shift { //TODO: insert special char + if self.cols_filled + 1 >= self.model.columns { + let rows_filled = self.rows_filled + 1; + + self.check_model_size(rows_filled); + self.model.move_down(); + + self.rows_filled = rows_filled; + } } else { self.model.put(c.chars().next().unwrap(), false, None); } From 0c398c893a4db873c344ee8b775c8f44de72e8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Mon, 19 Mar 2018 19:00:40 +0100 Subject: [PATCH 44/59] Open files with drag and drop, escape filenames --- Cargo.lock | 9 +++++++++ Cargo.toml | 3 +++ src/file_browser.rs | 2 ++ src/main.rs | 10 +++++++++- src/misc.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/shell.rs | 23 +++++++++++++++++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/misc.rs diff --git a/Cargo.lock b/Cargo.lock index d84f4da..36c8973 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -350,14 +350,17 @@ dependencies = [ "gtk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "neovim-lib 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -419,6 +422,11 @@ dependencies = [ "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "phf" version = "0.7.21" @@ -750,6 +758,7 @@ dependencies = [ "checksum pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94039b3921a4af4058a3e4335e5d15099101f298a92f5afc40bab3a3027594a1" "checksum pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db4130d0e0567b177178c9568466277ff167cb43a245b2881e203509ea5fbd84" "checksum pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "decc9523cd5bbba49a67ed8d2ef7dfca957f4760bf420a1ea4a82634da26381c" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" diff --git a/Cargo.toml b/Cargo.toml index d98b19f..bfbe349 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,9 @@ phf = "0.7" log = "0.4" env_logger = "0.5" htmlescape = "0.3" +percent-encoding = "1.0" +regex = "0.2" +lazy_static = "1.0" serde = "1.0" serde_derive = "1.0" diff --git a/src/file_browser.rs b/src/file_browser.rs index 810546f..9865296 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -15,6 +15,7 @@ use gtk::prelude::*; use neovim_lib::{NeovimApi, NeovimApiAsync}; +use misc::escape_filename; use nvim::{ErrorReport, NeovimClient, NeovimRef}; use shell; @@ -267,6 +268,7 @@ impl FileBrowserWidget { } else { &file_path }; + let file_path = escape_filename(file_path); nvim_ref.nvim().unwrap().command_async(&format!(":e {}", file_path)) .cb(|r| r.report_err()) .call(); diff --git a/src/main.rs b/src/main.rs index 75f9600..95a7794 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,13 +11,17 @@ extern crate gtk; extern crate gtk_sys; extern crate htmlescape; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate log; extern crate neovim_lib; extern crate pango; extern crate pango_cairo_sys; extern crate pango_sys; extern crate pangocairo; +extern crate percent_encoding; extern crate phf; +extern crate regex; extern crate serde; #[macro_use] @@ -50,6 +54,7 @@ mod tabline; mod error; mod file_browser; mod subscriptions; +mod misc; use std::env; use std::time::Duration; @@ -58,6 +63,7 @@ use gio::prelude::*; use ui::Ui; +use misc::escape_filename; use shell::ShellOptions; const BIN_PATH_ARG: &str = "--nvim-bin-path"; @@ -101,7 +107,9 @@ fn open(app: >k::Application, files: &[gio::File], _: &str) { for f in files { let mut ui = Ui::new(ShellOptions::new( nvim_bin_path(std::env::args()), - f.get_path().and_then(|p| p.to_str().map(str::to_owned)), + f.get_path().and_then(|p| { + p.to_str().map(|path| escape_filename(path).to_string()) + }), nvim_timeout(std::env::args()), )); diff --git a/src/misc.rs b/src/misc.rs new file mode 100644 index 0000000..2c3c768 --- /dev/null +++ b/src/misc.rs @@ -0,0 +1,38 @@ +use std::borrow::Cow; + +use regex::Regex; +use percent_encoding::percent_decode; + +/// Escape special ASCII characters with a backslash. +pub fn escape_filename<'t>(filename: &'t str) -> Cow<'t, str> { + lazy_static! { + static ref SPECIAL_CHARS: Regex = if cfg!(target_os = "windows") { + // On Windows, don't escape `:` and `\`, as these are valid components of the path. + Regex::new(r"[[:ascii:]&&[^0-9a-zA-Z._:\\-]]").unwrap() + } else { + // Similarly, don't escape `/` on other platforms. + Regex::new(r"[[:ascii:]&&[^0-9a-zA-Z._/-]]").unwrap() + }; + } + SPECIAL_CHARS.replace_all(&*filename, r"\$0") +} + +/// Decode a file URI. +/// +/// - On UNIX: `file:///path/to/a%20file.ext` -> `/path/to/a file.ext` +/// - On Windows: `file:///C:/path/to/a%20file.ext` -> `C:\path\to\a file.ext` +pub fn decode_uri(uri: &str) -> Option { + let path = match uri.split_at(8) { + ("file:///", path) => path, + _ => return None, + }; + let path = percent_decode(path.as_bytes()).decode_utf8().ok()?; + if cfg!(target_os = "windows") { + lazy_static! { + static ref SLASH: Regex = Regex::new(r"/").unwrap(); + } + Some(String::from(SLASH.replace_all(&*path, r"\"))) + } else { + Some("/".to_owned() + &path) + } +} diff --git a/src/shell.rs b/src/shell.rs index 48cca72..5fc1688 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -18,6 +18,7 @@ use pangocairo; use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value}; use neovim_lib::neovim_api::Tabpage; +use misc::{decode_uri, escape_filename}; use settings::{FontSource, Settings}; use ui_model::{Attrs, ModelRect, UiModel}; use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE}; @@ -567,6 +568,28 @@ impl Shell { state.drawing_area.connect_size_allocate(move |_, _| { init_nvim(&ref_state); }); + + let ref_state = self.state.clone(); + let targets = vec![ + gtk::TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP, 0), + ]; + state + .drawing_area + .drag_dest_set(gtk::DestDefaults::ALL, &targets, gdk::DragAction::COPY); + state + .drawing_area + .connect_drag_data_received(move |_, _, _, _, s, _, _| { + let uris = s.get_uris(); + let command = uris.iter() + .filter_map(|uri| decode_uri(uri)) + .fold(":ar".to_owned(), |command, filename| { + let filename = escape_filename(&filename); + command + " " + &filename + }); + let state = ref_state.borrow_mut(); + let mut nvim = state.nvim().unwrap(); + nvim.command_async(&command).cb(|r| r.report_err()).call() + }); } #[cfg(unix)] From bb9adcc1d59f46315c00b9de8a8e642a97b83af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Tue, 20 Mar 2018 18:43:31 +0100 Subject: [PATCH 45/59] Open multiple files in one instance --- src/main.rs | 23 +++++++++++------------ src/nvim/mod.rs | 18 +++++++++++++----- src/shell.rs | 19 +++++++++---------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/main.rs b/src/main.rs index 95a7794..c269fbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,7 +63,6 @@ use gio::prelude::*; use ui::Ui; -use misc::escape_filename; use shell::ShellOptions; const BIN_PATH_ARG: &str = "--nvim-bin-path"; @@ -104,23 +103,23 @@ fn main() { } fn open(app: >k::Application, files: &[gio::File], _: &str) { - for f in files { - let mut ui = Ui::new(ShellOptions::new( - nvim_bin_path(std::env::args()), - f.get_path().and_then(|p| { - p.to_str().map(|path| escape_filename(path).to_string()) - }), - nvim_timeout(std::env::args()), - )); + let files_list: Vec = files + .into_iter() + .filter_map(|f| f.get_path()?.to_str().map(str::to_owned)) + .collect(); + let mut ui = Ui::new(ShellOptions::new( + nvim_bin_path(std::env::args()), + files_list, + nvim_timeout(std::env::args()), + )); - ui.init(app, !nvim_disable_win_state(std::env::args())); - } + ui.init(app, !nvim_disable_win_state(std::env::args())); } fn activate(app: >k::Application) { let mut ui = Ui::new(ShellOptions::new( nvim_bin_path(std::env::args()), - None, + Vec::new(), nvim_timeout(std::env::args()), )); diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index 925db89..01100a8 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -20,8 +20,9 @@ use std::result; use std::sync::Arc; use std::time::Duration; -use neovim_lib::{Neovim, NeovimApi, Session, UiAttachOptions}; +use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Session, UiAttachOptions}; +use misc::escape_filename; use ui::UiMutex; use shell; use nvim_config::NvimConfig; @@ -138,7 +139,7 @@ pub fn start( pub fn post_start_init( nvim: NeovimClientAsync, - open_path: Option<&String>, + open_paths: Vec, cols: u64, rows: u64, ) -> result::Result<(), NvimInitError> { @@ -154,11 +155,18 @@ pub fn post_start_init( .command("runtime! ginit.vim") .map_err(NvimInitError::new_post_init)?; - if let Some(path) = open_path { + if !open_paths.is_empty() { + let command = open_paths + .iter() + .fold(":ar".to_owned(), |command, filename| { + let filename = escape_filename(filename); + command + " " + &filename + }); nvim.borrow() .unwrap() - .command(&format!("e {}", path)) - .map_err(NvimInitError::new_post_init)?; + .command_async(&command) + .cb(|r| r.report_err()) + .call(); } Ok(()) diff --git a/src/shell.rs b/src/shell.rs index 5fc1688..df91d60 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -382,19 +382,19 @@ impl UiState { #[derive(Clone)] pub struct ShellOptions { nvim_bin_path: Option, - open_path: Option, + open_paths: Vec, timeout: Option, } impl ShellOptions { pub fn new( nvim_bin_path: Option, - open_path: Option, + open_paths: Vec, timeout: Option, ) -> Self { ShellOptions { nvim_bin_path, - open_path, + open_paths, timeout, } } @@ -580,12 +580,13 @@ impl Shell { .drawing_area .connect_drag_data_received(move |_, _, _, _, s, _, _| { let uris = s.get_uris(); - let command = uris.iter() - .filter_map(|uri| decode_uri(uri)) - .fold(":ar".to_owned(), |command, filename| { + let command = uris.iter().filter_map(|uri| decode_uri(uri)).fold( + ":ar".to_owned(), + |command, filename| { let filename = escape_filename(&filename); command + " " + &filename - }); + }, + ); let state = ref_state.borrow_mut(); let mut nvim = state.nvim().unwrap(); nvim.command_async(&command).cb(|r| r.report_err()).call() @@ -884,9 +885,7 @@ fn init_nvim_async( }); // attach ui - if let Err(err) = - nvim::post_start_init(nvim, options.open_path.as_ref(), cols as u64, rows as u64) - { + if let Err(err) = nvim::post_start_init(nvim, options.open_paths, cols as u64, rows as u64) { show_nvim_init_error(&err, state_arc.clone()); } else { set_nvim_initialized(state_arc); From de43a4e5b78d2d2cdade8957a63fe30505cbbde0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Wed, 21 Mar 2018 18:01:17 +0100 Subject: [PATCH 46/59] Fix inconsistent ordering in the file tree --- src/file_browser.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/file_browser.rs b/src/file_browser.rs index 9865296..ad59d81 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -337,8 +337,11 @@ impl FileBrowserWidget { fn cmp_dirs_first(lhs: &DirEntry, rhs: &DirEntry) -> io::Result { let lhs_metadata = fs::metadata(lhs.path())?; let rhs_metadata = fs::metadata(rhs.path())?; - if lhs_metadata.file_type() == rhs_metadata.file_type() { - Ok(lhs.path().cmp(&rhs.path())) + if lhs_metadata.is_dir() == rhs_metadata.is_dir() { + Ok(lhs.path() + .to_string_lossy() + .to_lowercase() + .cmp(&rhs.path().to_string_lossy().to_lowercase())) } else { if lhs_metadata.is_dir() { Ok(Ordering::Less) From 783eb1a8ad1d1a055886b448fbde83640d914584 Mon Sep 17 00:00:00 2001 From: daa Date: Fri, 23 Mar 2018 00:36:43 +0300 Subject: [PATCH 47/59] Implementation of "insert char" command --- src/cmd_line.rs | 14 +++--- src/ui_model/mod.rs | 9 ---- src/ui_model/model_layout.rs | 95 +++++++++++++++++++++++++++--------- 3 files changed, 80 insertions(+), 38 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 7221891..ee588f4 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -28,13 +28,13 @@ impl Level { //TODO: im pub fn insert(&mut self, c: &str, shift: bool, render_state: &shell::RenderState) { - self.model_layout.insert(c, shift); + self.model_layout.insert_char(c, shift); self.update_preferred_size(render_state); } pub fn replace_from_ctx(&mut self, ctx: &CmdLineContext, render_state: &shell::RenderState) { let content = ctx.get_lines(); - self.replace_line(&content.lines, false); + self.replace_line(content.lines, false); self.prompt_offset = content.prompt_offset; self.model_layout .set_cursor(self.prompt_offset + ctx.pos as usize); @@ -43,7 +43,7 @@ impl Level { pub fn from_ctx(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self { let content = ctx.get_lines(); - let mut level = Level::from_lines(&content.lines, ctx.max_width, render_state); + let mut level = Level::from_lines(content.lines, ctx.max_width, render_state); level.prompt_offset = content.prompt_offset; level @@ -54,7 +54,7 @@ impl Level { level } - fn replace_line(&mut self, lines: &Vec, Vec)>>, append: bool) { + fn replace_line(&mut self, lines: Vec, Vec)>>, append: bool) { if append { self.model_layout.layout_append(lines); } else { @@ -96,14 +96,14 @@ impl Level { render_state: &shell::RenderState, ) -> Self { Level::from_lines( - &Level::to_attributed_content(content), + Level::to_attributed_content(content), max_width, render_state, ) } pub fn from_lines( - lines: &Vec, Vec)>>, + lines: Vec, Vec)>>, max_width: i32, render_state: &shell::RenderState, ) -> Self { @@ -382,7 +382,7 @@ impl CmdLine { .collect(); let block = state.block.as_mut().unwrap(); - block.replace_line(&vec![attr_content], true); + block.replace_line(vec![attr_content], true); block.update_preferred_size(&*render_state.borrow()); block.update_cache(&*render_state.borrow()); } diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index 5778ffb..1ac54f9 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -174,15 +174,6 @@ impl UiModel { ModelRect::new(top as usize, bot as usize, left, right) } - /// Move down all lines except first one - pub fn move_down(&mut self) { - let right = self.columns + 1; - - for row in (1..self.rows + 1).rev() { - self.copy_row(row as i64, 1, 0, right); - } - } - pub fn clear(&mut self) { let (rows, columns) = (self.rows, self.columns); self.clear_region(0, rows - 1, 0, columns - 1); diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index 9571f06..211584e 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -6,6 +6,7 @@ pub struct ModelLayout { pub model: UiModel, rows_filled: usize, cols_filled: usize, + lines: Vec, Vec)>>, } impl ModelLayout { @@ -16,16 +17,22 @@ impl ModelLayout { model: UiModel::new(ModelLayout::ROWS_STEP as u64, columns), rows_filled: 0, cols_filled: 0, + lines: Vec::new(), } } - pub fn layout_append(&mut self, lines: &Vec, Vec)>>) { + pub fn layout_append(&mut self, mut lines: Vec, Vec)>>) { let rows_filled = self.rows_filled; - self.layout_replace(rows_filled, lines); + let take_from = self.lines.len(); + + self.lines.append(&mut lines); + + self.layout_replace(rows_filled, take_from); } - pub fn layout(&mut self, lines: &Vec, Vec)>>) { - self.layout_replace(0, lines); + pub fn layout(&mut self, lines: Vec, Vec)>>) { + self.lines = lines; + self.layout_replace(0, 0); } pub fn set_cursor(&mut self, col: usize) { @@ -58,35 +65,51 @@ impl ModelLayout { } } - pub fn insert(&mut self, c: &str, shift: bool) { + pub fn insert_char(&mut self, c: &str, shift: bool) { if c.is_empty() { return; } + let ch = c.chars().next().unwrap(); + let (row, col) = self.model.get_cursor(); + if shift { - //TODO: insert special char - if self.cols_filled + 1 >= self.model.columns { - let rows_filled = self.rows_filled + 1; - - self.check_model_size(rows_filled); - self.model.move_down(); - - self.rows_filled = rows_filled; - } + self.insert_into_lines(ch); + self.layout_replace(0, 0); } else { - self.model.put(c.chars().next().unwrap(), false, None); + self.model.put(ch, false, None); + } + + self.model.set_cursor(row, col); + } + + fn insert_into_lines(&mut self, ch: char) { + let line = &mut self.lines[0]; + + let cur_col = self.model.cur_col; + + let mut col_idx = 0; + for &mut (_, ref mut chars) in line { + if cur_col < col_idx + chars.len() { + let col_sub_idx = cur_col - col_idx; + chars.insert(col_sub_idx, ch); + } else { + col_idx += chars.len(); + } } } /// Wrap all lines into model /// /// returns actual width - fn layout_replace(&mut self, row_offset: usize, lines: &Vec, Vec)>>) { - let rows = ModelLayout::count_lines(&lines, self.model.columns); + fn layout_replace(&mut self, row_offset: usize, take_from: usize) { + let rows = ModelLayout::count_lines(&self.lines[take_from..], self.model.columns); self.check_model_size(rows + row_offset); self.rows_filled = rows + row_offset; + let lines = &self.lines[take_from..]; + let mut max_col_idx = 0; let mut col_idx = 0; let mut row_idx = row_offset; @@ -122,7 +145,7 @@ impl ModelLayout { } } - fn count_lines(lines: &Vec, Vec)>>, max_columns: usize) -> usize { + fn count_lines(lines: &[Vec<(Option, Vec)>], max_columns: usize) -> usize { let mut row_count = 0; for line in lines { @@ -151,12 +174,12 @@ mod tests { let lines = vec![vec![(None, vec!['a'; 5])]; ModelLayout::ROWS_STEP]; let mut model = ModelLayout::new(5); - model.layout(&lines); + model.layout(lines.clone()); let (cols, rows) = model.size(); assert_eq!(5, cols); assert_eq!(ModelLayout::ROWS_STEP, rows); - model.layout_append(&lines); + model.layout_append(lines); let (cols, rows) = model.size(); assert_eq!(5, cols); assert_eq!(ModelLayout::ROWS_STEP * 2, rows); @@ -168,14 +191,42 @@ mod tests { let lines = vec![vec![(None, vec!['a'; 3])]; 1]; let mut model = ModelLayout::new(5); - model.layout(&lines); + model.layout(lines); let (cols, _) = model.size(); assert_eq!(4, cols); // size is 3 and 4 - is with cursor position let lines = vec![vec![(None, vec!['a'; 2])]; 1]; - model.layout_append(&lines); + model.layout_append(lines); let (cols, _) = model.size(); assert_eq!(3, cols); } + + #[test] + fn test_insert_shift() { + let lines = vec![vec![(None, vec!['a'; 3])]; 1]; + let mut model = ModelLayout::new(5); + model.layout(lines); + model.set_cursor(1); + + model.insert_char("b", true); + + let (cols, _) = model.size(); + assert_eq!(4, cols); + assert_eq!('b', model.model.model()[0].line[1].ch); + } + + #[test] + fn test_insert_no_shift() { + let lines = vec![vec![(None, vec!['a'; 3])]; 1]; + let mut model = ModelLayout::new(5); + model.layout(lines); + model.set_cursor(1); + + model.insert_char("b", false); + + let (cols, _) = model.size(); + assert_eq!(3, cols); + assert_eq!('b', model.model.model()[0].line[1].ch); + } } From 65c266291568c9320b0b15e1ec24397b1a0d57be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sat, 24 Mar 2018 13:53:54 +0100 Subject: [PATCH 48/59] Fix bug where rapid `cd`s cause infinite loop --- src/file_browser.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/file_browser.rs b/src/file_browser.rs index ad59d81..2d01070 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -201,11 +201,10 @@ impl FileBrowserWidget { &["getcwd()"], clone!(store, state_ref, dir_list_model, dir_list => move |args| { let dir = args.into_iter().next().unwrap(); - let mut state = state_ref.borrow_mut(); - if dir != *state.current_dir { + if dir != state_ref.borrow().current_dir { + state_ref.borrow_mut().current_dir = dir.to_owned(); update_dir_list(&dir, &dir_list_model, &dir_list); - state.current_dir = dir; - tree_reload(&store, &state); + tree_reload(&store, &state_ref.borrow()); } }), ); @@ -277,18 +276,18 @@ impl FileBrowserWidget { // Connect directory list. let nvim_ref = self.nvim.as_ref().unwrap(); - self.comps.dir_list.connect_changed(clone!(nvim_ref => move |dir_list| { + self.comps.dir_list.connect_changed(clone!(nvim_ref, state_ref => move |dir_list| { if let Some(iter) = dir_list.get_active_iter() { let model = dir_list.get_model().unwrap(); if let Some(dir) = model.get_value(&iter, 2).get::<&str>() { - let mut nvim = nvim_ref.nvim().unwrap(); - nvim.set_current_dir(dir).report_err(); + if dir != state_ref.borrow().current_dir { + let mut nvim = nvim_ref.nvim().unwrap(); + nvim.set_current_dir(dir).report_err(); + } } } })); - let store = &self.store; - let state_ref = &self.state; let context_menu = &self.comps.context_menu; let cd_action = &self.comps.cd_action; self.tree.connect_button_press_event( From 18583abae7078519d51167fe36de0ee3927ae8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Sat, 24 Mar 2018 13:59:19 +0100 Subject: [PATCH 49/59] Use set_current_dir_async --- src/file_browser.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/file_browser.rs b/src/file_browser.rs index 2d01070..cab0fc1 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -180,7 +180,9 @@ impl FileBrowserWidget { cd_action.connect_activate(clone!(state_ref, nvim_ref => move |_, _| { let mut nvim = nvim_ref.nvim().unwrap(); if let Some(ref path) = state_ref.borrow().selected_path { - nvim.set_current_dir(&path).report_err(); + nvim.set_current_dir_async(&path) + .cb(|r| r.report_err()) + .call(); } })); actions.add_action(cd_action); @@ -282,7 +284,9 @@ impl FileBrowserWidget { if let Some(dir) = model.get_value(&iter, 2).get::<&str>() { if dir != state_ref.borrow().current_dir { let mut nvim = nvim_ref.nvim().unwrap(); - nvim.set_current_dir(dir).report_err(); + nvim.set_current_dir_async(dir) + .cb(|r| r.report_err()) + .call(); } } } From a853083de210c87bb8d7824596760bd649aa2e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20L=C3=BCbbemeier?= Date: Mon, 26 Feb 2018 13:29:36 +0100 Subject: [PATCH 50/59] Single click activation on projects popover Plus some padding and font size changes --- src/project.rs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/project.rs b/src/project.rs index 6423fa8..a442e11 100644 --- a/src/project.rs +++ b/src/project.rs @@ -73,6 +73,9 @@ impl Projects { projects.setup_tree(); + projects.tree.set_activate_on_single_click(true); + projects.tree.set_hover_selection(true); + projects.tree.set_grid_lines(gtk::TreeViewGridLines::Horizontal); let vbox = gtk::Box::new(Orientation::Vertical, 5); vbox.set_border_width(5); @@ -89,11 +92,12 @@ impl Projects { ); projects.scroll.add(&projects.tree); + projects.scroll.set_shadow_type(gtk::ShadowType::In); vbox.pack_start(&projects.scroll, true, true, 0); let open_btn = gtk::Button::new_with_label("Other Documents…"); - vbox.pack_start(&open_btn, true, true, 0); + vbox.pack_start(&open_btn, true, true, 5); vbox.show_all(); projects.popup.add(&vbox); @@ -129,7 +133,12 @@ impl Projects { let prj_ref = projects.clone(); projects.borrow().tree.connect_row_activated( - move |tree, _, _| { + move |tree, _, column| { + // Don't activate if the user clicked the checkbox. + let toggle_column = tree.get_column(2).unwrap(); + if *column == toggle_column { + return; + } let selection = tree.get_selection(); if let Some((model, iter)) = selection.get_selected() { prj_ref.borrow().open_uri(&model, &iter); @@ -281,6 +290,7 @@ impl Projects { let image_column = TreeViewColumn::new(); let icon_renderer = CellRendererPixbuf::new(); + icon_renderer.set_padding(5, 0); image_column.pack_start(&icon_renderer, true); image_column.add_attribute( @@ -293,18 +303,23 @@ impl Projects { let text_column = TreeViewColumn::new(); - self.name_renderer.set_property_width_chars(60); - self.path_renderer.set_property_width_chars(60); + self.name_renderer.set_property_width_chars(45); + self.path_renderer.set_property_width_chars(45); + self.name_renderer.set_property_ellipsize( + pango::EllipsizeMode::Middle, + ); self.path_renderer.set_property_ellipsize( pango::EllipsizeMode::Start, ); + self.name_renderer.set_padding(0, 5); + self.path_renderer.set_padding(0, 5); text_column.pack_start(&self.name_renderer, true); text_column.pack_start(&self.path_renderer, true); text_column.add_attribute( &self.name_renderer, - "markup", + "text", ProjectViewColumns::Name as i32, ); text_column.add_attribute( @@ -492,7 +507,7 @@ impl Entry { format!("{}", encode_minimal(&s.to_string_lossy())) }) .unwrap_or_else(|| "".to_owned()), - file_name: format!("{}", encode_minimal(name)), + file_name: encode_minimal(name), name: name.to_owned(), pixbuf: BOOKMARKED_PIXBUF, project: true, @@ -513,7 +528,7 @@ impl Entry { format!("{}", encode_minimal(&s.to_string_lossy())) }) .unwrap_or_else(|| "".to_owned()), - file_name: format!("{}", encode_minimal(&name)), + file_name: encode_minimal(&name), name, pixbuf: CURRENT_DIR_PIXBUF, project: true, @@ -534,7 +549,7 @@ impl Entry { format!("{}", encode_minimal(&s.to_string_lossy())) }) .unwrap_or_else(|| "".to_owned()), - file_name: format!("{}", encode_minimal(&name)), + file_name: encode_minimal(&name), name, pixbuf: PLAIN_FILE_PIXBUF, project: false, From adec73ab81a84a02656f20eed5cb573ea84b4735 Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 25 Mar 2018 16:29:59 +0300 Subject: [PATCH 51/59] double width chars --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/cmd_line.rs | 1 - src/main.rs | 1 + src/ui_model/model_layout.rs | 29 ++++++++++++++++++++++++----- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b435889..a265f22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -394,6 +394,7 @@ dependencies = [ "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -681,6 +682,11 @@ dependencies = [ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "unicode-width" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.0.4" @@ -811,6 +817,7 @@ dependencies = [ "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" "checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" +"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" diff --git a/Cargo.toml b/Cargo.toml index 345a294..32a7b5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ rmpv = "0.4" percent-encoding = "1.0" regex = "0.2" lazy_static = "1.0" +unicode-width = "0.1.4" serde = "1.0" serde_derive = "1.0" diff --git a/src/cmd_line.rs b/src/cmd_line.rs index ee588f4..02324a9 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -24,7 +24,6 @@ pub struct Level { } impl Level { - //TODO: double width chars render, also note in text wrapping //TODO: im pub fn insert(&mut self, c: &str, shift: bool, render_state: &shell::RenderState) { diff --git a/src/main.rs b/src/main.rs index 4003ead..7873512 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ extern crate percent_encoding; extern crate phf; extern crate rmpv; extern crate regex; +extern crate unicode_width; extern crate serde; #[macro_use] diff --git a/src/ui_model/model_layout.rs b/src/ui_model/model_layout.rs index 211584e..0ff40b4 100644 --- a/src/ui_model/model_layout.rs +++ b/src/ui_model/model_layout.rs @@ -1,5 +1,7 @@ use std::cmp::max; +use unicode_width::UnicodeWidthChar; + use ui_model::{Attrs, UiModel}; pub struct ModelLayout { @@ -116,19 +118,24 @@ impl ModelLayout { for content in lines { for &(ref attr, ref ch_list) in content { for ch in ch_list { - if col_idx >= self.model.columns { + let ch_width = ch.width().unwrap_or(1); + + if col_idx + ch_width > self.model.columns { col_idx = 0; row_idx += 1; } self.model.set_cursor(row_idx, col_idx as usize); self.model.put(*ch, false, attr.as_ref()); - - if max_col_idx < col_idx { - max_col_idx = col_idx; + if ch_width > 1 { + self.model.put(' ', true, attr.as_ref()); } - col_idx += 1; + if max_col_idx < col_idx { + max_col_idx = col_idx + ch_width - 1; + } + + col_idx += ch_width; } if col_idx < self.model.columns { @@ -229,4 +236,16 @@ mod tests { assert_eq!(3, cols); assert_eq!('b', model.model.model()[0].line[1].ch); } + + #[test] + fn test_double_width() { + let lines = vec![vec![(None, vec!['あ'; 3])]; 1]; + let mut model = ModelLayout::new(7); + model.layout(lines); + model.set_cursor(1); + + let (cols, rows) = model.size(); + assert_eq!(1, rows); + assert_eq!(6, cols); + } } From 5b6afc6804148895275aa60ca690395c7a57f624 Mon Sep 17 00:00:00 2001 From: daa84 Date: Mon, 26 Mar 2018 11:24:36 +0300 Subject: [PATCH 52/59] Fix #81 --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index c269fbc..f53a39b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![windows_subsystem = "windows"] + extern crate cairo; extern crate env_logger; extern crate gdk; From a1f122d19c09d6a783a5886cf42d7c95e9b5d8c0 Mon Sep 17 00:00:00 2001 From: daa84 Date: Tue, 27 Mar 2018 17:47:42 +0300 Subject: [PATCH 53/59] Hide nvim.exe window (fix #81) --- src/nvim/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index 01100a8..a30cb81 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -100,6 +100,11 @@ pub fn start( .arg("let g:GtkGuiLoaded = 1") .stderr(Stdio::inherit()); + if cfg!(windows) { + use std::os::windows::process::CommandExt; + cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW + } + if let Ok(runtime_path) = env::var("NVIM_GTK_RUNTIME_PATH") { cmd.arg("--cmd").arg( format!("let &rtp.=',{}'", runtime_path), From 53958171c21a378234d6587a13964230cd58e742 Mon Sep 17 00:00:00 2001 From: daa Date: Fri, 30 Mar 2018 00:13:39 +0300 Subject: [PATCH 54/59] Fix non-windows builds #83 --- src/nvim/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index a30cb81..8374895 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -79,6 +79,13 @@ impl error::Error for NvimInitError { } } +#[cfg(target_os = "windows")] +fn set_windows_creation_flags() { + use std::os::windows::process::CommandExt; + cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW +} + + pub fn start( shell: Arc>, nvim_bin_path: Option<&String>, @@ -100,10 +107,8 @@ pub fn start( .arg("let g:GtkGuiLoaded = 1") .stderr(Stdio::inherit()); - if cfg!(windows) { - use std::os::windows::process::CommandExt; - cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW - } + #[cfg(target_os = "windows")] + set_windows_creation_flags(); if let Ok(runtime_path) = env::var("NVIM_GTK_RUNTIME_PATH") { cmd.arg("--cmd").arg( From 5b706f01b5ee2b938497044434cffc875169c79d Mon Sep 17 00:00:00 2001 From: daa Date: Fri, 30 Mar 2018 00:23:25 +0300 Subject: [PATCH 55/59] Fix windows build --- src/nvim/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index 8374895..76b350d 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -80,7 +80,7 @@ impl error::Error for NvimInitError { } #[cfg(target_os = "windows")] -fn set_windows_creation_flags() { +fn set_windows_creation_flags(cmd: &mut Command) { use std::os::windows::process::CommandExt; cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW } @@ -108,7 +108,7 @@ pub fn start( .stderr(Stdio::inherit()); #[cfg(target_os = "windows")] - set_windows_creation_flags(); + set_windows_creation_flags(&mut cmd); if let Ok(runtime_path) = env::var("NVIM_GTK_RUNTIME_PATH") { cmd.arg("--cmd").arg( From 7b59a2c1eb0e894f9c13e255c4600d028fd1e219 Mon Sep 17 00:00:00 2001 From: daa84 Date: Fri, 30 Mar 2018 13:01:14 +0300 Subject: [PATCH 56/59] Add option to enable external cmdline --- src/cmd_line.rs | 3 - src/nvim/mod.rs | 28 ++++----- src/nvim/redraw_handler.rs | 114 ++++++++++++++++++------------------- 3 files changed, 65 insertions(+), 80 deletions(-) diff --git a/src/cmd_line.rs b/src/cmd_line.rs index 02324a9..6ab4bc2 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -24,8 +24,6 @@ pub struct Level { } impl Level { - //TODO: im - pub fn insert(&mut self, c: &str, shift: bool, render_state: &shell::RenderState) { self.model_layout.insert_char(c, shift); self.update_preferred_size(render_state); @@ -428,7 +426,6 @@ fn gtk_draw(ctx: &cairo::Context, state: &Arc>) -> Inhibit { } if let Some(level) = level { - //TODO: limit model to row filled render::render( ctx, state.cursor.as_ref().unwrap(), diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index e800413..1916fe6 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -1,4 +1,3 @@ - mod client; mod handler; mod mode_info; @@ -6,16 +5,16 @@ mod redraw_handler; mod repaint_mode; mod ext; -pub use self::redraw_handler::{RedrawEvents, GuiApi, CompleteItem}; +pub use self::redraw_handler::{CompleteItem, GuiApi, RedrawEvents}; pub use self::repaint_mode::RepaintMode; pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef}; -pub use self::mode_info::{ModeInfo, CursorShape}; +pub use self::mode_info::{CursorShape, ModeInfo}; pub use self::ext::ErrorReport; use std::error; use std::fmt; use std::env; -use std::process::{Stdio, Command}; +use std::process::{Command, Stdio}; use std::result; use std::sync::Arc; use std::time::Duration; @@ -85,7 +84,6 @@ fn set_windows_creation_flags(cmd: &mut Command) { cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW } - pub fn start( shell: Arc>, nvim_bin_path: Option<&String>, @@ -111,14 +109,11 @@ pub fn start( set_windows_creation_flags(&mut cmd); if let Ok(runtime_path) = env::var("NVIM_GTK_RUNTIME_PATH") { - cmd.arg("--cmd").arg( - format!("let &rtp.=',{}'", runtime_path), - ); + cmd.arg("--cmd") + .arg(format!("let &rtp.=',{}'", runtime_path)); } else if let Some(prefix) = option_env!("PREFIX") { - cmd.arg("--cmd").arg(format!( - "let &rtp.=',{}/share/nvim-gtk/runtime'", - prefix - )); + cmd.arg("--cmd") + .arg(format!("let &rtp.=',{}/share/nvim-gtk/runtime'", prefix)); } else { cmd.arg("--cmd").arg("let &rtp.=',runtime'"); } @@ -140,9 +135,8 @@ pub fn start( let mut nvim = Neovim::new(session); - nvim.session.start_event_loop_handler( - handler::NvimHandler::new(shell), - ); + nvim.session + .start_event_loop_handler(handler::NvimHandler::new(shell)); Ok(nvim) } @@ -160,8 +154,7 @@ pub fn post_start_init( rows, UiAttachOptions::new() .set_popupmenu_external(true) - .set_tabline_external(true) - .set_cmdline_external(true), + .set_tabline_external(true), ) .map_err(NvimInitError::new_post_init)?; @@ -186,4 +179,3 @@ pub fn post_start_init( Ok(()) } - diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 364c2b1..9e52b87 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -2,7 +2,7 @@ use std::result; use std::collections::HashMap; use std::sync::Arc; -use neovim_lib::{Value, UiOption}; +use neovim_lib::{UiOption, Value}; use neovim_lib::neovim_api::Tabpage; use ui::UiMutex; @@ -158,9 +158,9 @@ macro_rules! call { ($s:ident -> $c:ident ($args:ident : $($arg_type:ident),+ )) => ( { let mut iter = $args.into_iter(); - $s.$c($( + $s.$c($( try_arg!(iter.next() - .ok_or_else(|| format!("No such argument for {}", stringify!($c)))?, + .ok_or_else(|| format!("No such argument for {}", stringify!($c)))?, $arg_type ) ),+ ) @@ -175,38 +175,34 @@ pub fn call_gui_event( ) -> result::Result<(), String> { match method { "Font" => ui.set_font(try_str!(args[0])), - "Clipboard" => { - match try_str!(args[0]) { - "Set" => { - match try_str!(args[1]) { - "*" => ui.clipboard_primary_set(try_str!(args[2])), - _ => ui.clipboard_clipboard_set(try_str!(args[2])), - } - }, - opt => error!("Unknown option {}", opt), - } + "Clipboard" => match try_str!(args[0]) { + "Set" => match try_str!(args[1]) { + "*" => ui.clipboard_primary_set(try_str!(args[2])), + _ => ui.clipboard_clipboard_set(try_str!(args[2])), + }, + opt => error!("Unknown option {}", opt), + }, + "Option" => match try_str!(args[0]) { + "Popupmenu" => ui.nvim() + .ok_or_else(|| "Nvim not initialized".to_owned()) + .and_then(|mut nvim| { + nvim.set_option(UiOption::ExtPopupmenu(try_uint!(args[1]) == 1)) + .map_err(|e| e.to_string()) + })?, + "Tabline" => ui.nvim() + .ok_or_else(|| "Nvim not initialized".to_owned()) + .and_then(|mut nvim| { + nvim.set_option(UiOption::ExtTabline(try_uint!(args[1]) == 1)) + .map_err(|e| e.to_string()) + })?, + "Cmdline" => ui.nvim() + .ok_or_else(|| "Nvim not initialized".to_owned()) + .and_then(|mut nvim| { + nvim.set_option(UiOption::ExtCmdline(try_uint!(args[1]) == 1)) + .map_err(|e| e.to_string()) + })?, + opt => error!("Unknown option {}", opt), }, - "Option" => { - match try_str!(args[0]) { - "Popupmenu" => { - ui.nvim() - .ok_or_else(|| "Nvim not initialized".to_owned()) - .and_then(|mut nvim| { - nvim.set_option(UiOption::ExtPopupmenu(try_uint!(args[1]) == 1)) - .map_err(|e| e.to_string()) - })? - } - "Tabline" => { - ui.nvim() - .ok_or_else(|| "Nvim not initialized".to_owned()) - .and_then(|mut nvim| { - nvim.set_option(UiOption::ExtTabline(try_uint!(args[1]) == 1)) - .map_err(|e| e.to_string()) - })? - } - opt => error!("Unknown option {}", opt), - } - } _ => return Err(format!("Unsupported event {}({:?})", method, args)), } Ok(()) @@ -232,15 +228,19 @@ pub fn call_gui_request( } }; let t = clipboard.wait_for_text().unwrap_or_else(|| String::new()); - Ok(Value::Array(t.split("\n").map(|s| s.into()).collect::>())) - }, + Ok(Value::Array( + t.split("\n").map(|s| s.into()).collect::>(), + )) + } opt => { error!("Unknown option {}", opt); Err(Value::Nil) - }, + } } - }, - _ => Err(Value::String(format!("Unsupported request {}({:?})", method, args).into())), + } + _ => Err(Value::String( + format!("Unsupported request {}({:?})", method, args).into(), + )), } } @@ -273,11 +273,11 @@ pub fn call( "busy_start" => ui.on_busy(true), "busy_stop" => ui.on_busy(false), "popupmenu_show" => { - let menu_items = map_array!(args[0], "Error get menu list array", |item| { - map_array!(item, "Error get menu item array", |col| { - col.as_str().ok_or("Error get menu column") - }) - })?; + let menu_items = map_array!(args[0], "Error get menu list array", |item| map_array!( + item, + "Error get menu item array", + |col| col.as_str().ok_or("Error get menu column") + ))?; ui.popupmenu_show( &CompleteItem::map(&menu_items), @@ -294,9 +294,9 @@ pub fn call( .ok_or_else(|| "Error get map for tab".to_owned()) .and_then(|tab_map| tab_map.to_attrs_map()) .map(|tab_attrs| { - let name_attr = tab_attrs.get("name").and_then( - |n| n.as_str().map(|s| s.to_owned()), - ); + let name_attr = tab_attrs + .get("name") + .and_then(|n| n.as_str().map(|s| s.to_owned())); let tab_attr = tab_attrs .get("tab") .map(|&tab_id| Tabpage::new(tab_id.clone())) @@ -311,11 +311,9 @@ pub fn call( let mode_info = map_array!( args[1], "Error get array key value for mode_info".to_owned(), - |mi| { - mi.as_map() - .ok_or_else(|| "Erro get map for mode_info".to_owned()) - .and_then(|mi_map| ModeInfo::new(mi_map)) - } + |mi| mi.as_map() + .ok_or_else(|| "Erro get map for mode_info".to_owned()) + .and_then(|mi_map| ModeInfo::new(mi_map)) )?; ui.mode_info_set(try_bool!(args[0]), mode_info) } @@ -345,13 +343,11 @@ pub struct CompleteItem<'a> { impl<'a> CompleteItem<'a> { fn map(menu: &'a [Vec<&str>]) -> Vec { menu.iter() - .map(|menu| { - CompleteItem { - word: menu[0], - kind: menu[1], - menu: menu[2], - info: menu[3], - } + .map(|menu| CompleteItem { + word: menu[0], + kind: menu[1], + menu: menu[2], + info: menu[3], }) .collect() } From 9ef88bc30b0ae9d800cbda53fc6fe1b08a8c0e29 Mon Sep 17 00:00:00 2001 From: daa Date: Fri, 30 Mar 2018 23:54:37 +0300 Subject: [PATCH 57/59] Add NGToggleSidebar command (#79) --- runtime/plugin/nvim_gui_shim.vim | 3 + src/nvim/handler.rs | 3 +- src/nvim/mod.rs | 2 +- src/nvim/redraw_handler.rs | 96 ++------------------------ src/shell.rs | 114 ++++++++++++++++++++----------- src/ui.rs | 18 +++++ 6 files changed, 101 insertions(+), 135 deletions(-) diff --git a/runtime/plugin/nvim_gui_shim.vim b/runtime/plugin/nvim_gui_shim.vim index 565b2ff..2815e55 100644 --- a/runtime/plugin/nvim_gui_shim.vim +++ b/runtime/plugin/nvim_gui_shim.vim @@ -54,3 +54,6 @@ function s:GuiFontCommand(fname, bang) abort endfunction command! -nargs=? -bang Guifont call s:GuiFontCommand("", "") command! -nargs=? -bang GuiFont call s:GuiFontCommand("", "") + +command! NGToggleSidebar call rpcnotify(1, 'Gui', 'Command', 'ToggleSidebar') + diff --git a/src/nvim/handler.rs b/src/nvim/handler.rs index 3718dc1..c6ee331 100644 --- a/src/nvim/handler.rs +++ b/src/nvim/handler.rs @@ -9,7 +9,6 @@ use glib; use super::repaint_mode::RepaintMode; use super::redraw_handler; -use super::redraw_handler::RedrawEvents; pub struct NvimHandler { shell: Arc>, @@ -68,7 +67,7 @@ impl NvimHandler { redraw_handler::call_gui_event( ui, ev_name.as_str().ok_or_else(|| "Event name does not exists")?, - &args, + args, )?; ui.on_redraw(&RepaintMode::All); Ok(()) diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index 1916fe6..95a1c78 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -5,7 +5,7 @@ mod redraw_handler; mod repaint_mode; mod ext; -pub use self::redraw_handler::{CompleteItem, GuiApi, RedrawEvents}; +pub use self::redraw_handler::CompleteItem; pub use self::repaint_mode::RepaintMode; pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef}; pub use self::mode_info::{CursorShape, ModeInfo}; diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 9e52b87..bef2e3f 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -1,5 +1,4 @@ use std::result; -use std::collections::HashMap; use std::sync::Arc; use neovim_lib::{UiOption, Value}; @@ -15,94 +14,6 @@ use rmpv; use super::repaint_mode::RepaintMode; use super::mode_info::ModeInfo; -pub trait RedrawEvents { - fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode; - - fn on_put(&mut self, text: String) -> RepaintMode; - - fn on_clear(&mut self) -> RepaintMode; - - fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode; - - fn on_redraw(&mut self, mode: &RepaintMode); - - fn on_highlight_set(&mut self, attrs: HashMap) -> RepaintMode; - - fn on_eol_clear(&mut self) -> RepaintMode; - - fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) -> RepaintMode; - - fn on_scroll(&mut self, count: i64) -> RepaintMode; - - fn on_update_bg(&mut self, bg: i64) -> RepaintMode; - - fn on_update_fg(&mut self, fg: i64) -> RepaintMode; - - fn on_update_sp(&mut self, sp: i64) -> RepaintMode; - - fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode; - - fn on_mouse(&mut self, on: bool) -> RepaintMode; - - fn on_busy(&mut self, busy: bool) -> RepaintMode; - - fn popupmenu_show( - &mut self, - menu: &[CompleteItem], - selected: i64, - row: u64, - col: u64, - ) -> RepaintMode; - - fn popupmenu_hide(&mut self) -> RepaintMode; - - fn popupmenu_select(&mut self, selected: i64) -> RepaintMode; - - fn tabline_update( - &mut self, - selected: Tabpage, - tabs: Vec<(Tabpage, Option)>, - ) -> RepaintMode; - - fn mode_info_set( - &mut self, - cursor_style_enabled: bool, - mode_info: Vec, - ) -> RepaintMode; - - fn cmdline_show( - &mut self, - content: Vec<(HashMap, String)>, - pos: u64, - firstc: String, - prompt: String, - indent: u64, - level: u64, - ) -> RepaintMode; - - fn cmdline_hide(&mut self, level: u64) -> RepaintMode; - - fn cmdline_block_show( - &mut self, - content: Vec, String)>>, - ) -> RepaintMode; - - fn cmdline_block_append( - &mut self, - content: Vec<(HashMap, String)>, - ) -> RepaintMode; - - fn cmdline_block_hide(&mut self) -> RepaintMode; - - fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode; - - fn cmdline_special_char(&mut self, c: String, shift: bool, level: u64) -> RepaintMode; -} - -pub trait GuiApi { - fn set_font(&mut self, font_desc: &str); -} - macro_rules! try_str { ($exp:expr) => ($exp.as_str().ok_or_else(|| "Can't convert argument to string".to_owned())?) } @@ -171,10 +82,10 @@ macro_rules! call { pub fn call_gui_event( ui: &mut shell::State, method: &str, - args: &Vec, + args: Vec, ) -> result::Result<(), String> { match method { - "Font" => ui.set_font(try_str!(args[0])), + "Font" => call!(ui->set_font(args: str)), "Clipboard" => match try_str!(args[0]) { "Set" => match try_str!(args[1]) { "*" => ui.clipboard_primary_set(try_str!(args[2])), @@ -203,6 +114,9 @@ pub fn call_gui_event( })?, opt => error!("Unknown option {}", opt), }, + "Command" => { + ui.on_command(args); + }, _ => return Err(format!("Unsupported event {}({:?})", method, args)), } Ok(()) diff --git a/src/shell.rs b/src/shell.rs index 444487f..fd7a8fa 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -23,8 +23,8 @@ use misc::{decode_uri, escape_filename}; use settings::{FontSource, Settings}; use ui_model::{Attrs, ModelRect, UiModel}; use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE}; -use nvim::{self, CompleteItem, ErrorReport, GuiApi, NeovimClient, NeovimClientAsync, NeovimRef, - RedrawEvents, RepaintMode}; +use nvim::{self, CompleteItem, ErrorReport, NeovimClient, NeovimClientAsync, NeovimRef, + RepaintMode}; use input; use input::keyval_to_input_string; use cursor::{BlinkCursor, Cursor, CursorRedrawCb}; @@ -98,6 +98,7 @@ pub struct State { detach_cb: Option>>, nvim_started_cb: Option>>, + command_cb: Option) + Send + 'static>>, subscriptions: RefCell, } @@ -139,6 +140,7 @@ impl State { detach_cb: None, nvim_started_cb: None, + command_cb: None, subscriptions: RefCell::new(Subscriptions::new()), } @@ -191,6 +193,17 @@ impl State { } } + pub fn set_nvim_command_cb(&mut self, cb: Option) + where + F: FnMut(Vec) + Send + 'static, + { + if cb.is_some() { + self.command_cb = Some(Box::new(cb.unwrap())); + } else { + self.command_cb = None; + } + } + pub fn set_font_desc(&mut self, desc: &str) { self.render_state .borrow_mut() @@ -395,6 +408,21 @@ impl State { .borrow() .run_now(handle, &mut self.nvim().unwrap()); } + + pub fn set_font(&mut self, font_desc: String) { + { + let mut settings = self.settings.borrow_mut(); + settings.set_font_source(FontSource::Rpc); + } + + self.set_font_desc(&font_desc); + } + + pub fn on_command(&mut self, args: Vec) { + if let Some(ref mut cb) = self.command_cb { + cb(args); + } + } } pub struct UiState { @@ -688,6 +716,14 @@ impl Shell { let mut state = self.state.borrow_mut(); state.set_nvim_started_cb(cb); } + + pub fn set_nvim_command_cb(&self, cb: Option) + where + F: FnMut(Vec) + Send + 'static, + { + let mut state = self.state.borrow_mut(); + state.set_nvim_command_cb(cb); + } } impl Deref for Shell { @@ -1022,31 +1058,32 @@ fn init_nvim(state_ref: &Arc>) { } } -impl RedrawEvents for State { - fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode { +// Neovim redraw events +impl State { + pub fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode { let repaint_area = self.model.set_cursor(row as usize, col as usize); self.set_im_location(); RepaintMode::AreaList(repaint_area) } - fn on_put(&mut self, text: String) -> RepaintMode { + pub fn on_put(&mut self, text: String) -> RepaintMode { let ch = text.chars().last().unwrap_or(' '); let double_width = text.is_empty(); RepaintMode::Area(self.model.put(ch, double_width, self.cur_attrs.as_ref())) } - fn on_clear(&mut self) -> RepaintMode { + pub fn on_clear(&mut self) -> RepaintMode { debug!("clear model"); self.model.clear(); RepaintMode::All } - fn on_eol_clear(&mut self) -> RepaintMode { + pub fn on_eol_clear(&mut self) -> RepaintMode { RepaintMode::Area(self.model.eol_clear()) } - fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode { + pub fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode { debug!("on_resize {}/{}", columns, rows); if self.model.columns != columns as usize || self.model.rows != rows as usize { @@ -1060,7 +1097,7 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn on_redraw(&mut self, mode: &RepaintMode) { + pub fn on_redraw(&mut self, mode: &RepaintMode) { match *mode { RepaintMode::All => { self.update_dirty_glyphs(); @@ -1072,23 +1109,29 @@ impl RedrawEvents for State { } } - fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) -> RepaintMode { + pub fn on_set_scroll_region( + &mut self, + top: u64, + bot: u64, + left: u64, + right: u64, + ) -> RepaintMode { self.model.set_scroll_region(top, bot, left, right); RepaintMode::Nothing } - fn on_scroll(&mut self, count: i64) -> RepaintMode { + pub fn on_scroll(&mut self, count: i64) -> RepaintMode { RepaintMode::Area(self.model.scroll(count)) } - fn on_highlight_set(&mut self, attrs: HashMap) -> RepaintMode { + pub fn on_highlight_set(&mut self, attrs: HashMap) -> RepaintMode { let model_attrs = Attrs::from_value_map(&attrs); self.cur_attrs = Some(model_attrs); RepaintMode::Nothing } - fn on_update_bg(&mut self, bg: i64) -> RepaintMode { + pub fn on_update_bg(&mut self, bg: i64) -> RepaintMode { let mut render_state = self.render_state.borrow_mut(); if bg >= 0 { render_state.color_model.bg_color = Color::from_indexed_color(bg as u64); @@ -1098,7 +1141,7 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn on_update_fg(&mut self, fg: i64) -> RepaintMode { + pub fn on_update_fg(&mut self, fg: i64) -> RepaintMode { let mut render_state = self.render_state.borrow_mut(); if fg >= 0 { render_state.color_model.fg_color = Color::from_indexed_color(fg as u64); @@ -1108,7 +1151,7 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn on_update_sp(&mut self, sp: i64) -> RepaintMode { + pub fn on_update_sp(&mut self, sp: i64) -> RepaintMode { let mut render_state = self.render_state.borrow_mut(); if sp >= 0 { render_state.color_model.sp_color = Color::from_indexed_color(sp as u64); @@ -1118,18 +1161,18 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode { + pub fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode { let mut render_state = self.render_state.borrow_mut(); render_state.mode.update(&mode, idx as usize); RepaintMode::Area(self.model.cur_point()) } - fn on_mouse(&mut self, on: bool) -> RepaintMode { + pub fn on_mouse(&mut self, on: bool) -> RepaintMode { self.mouse_enabled = on; RepaintMode::Nothing } - fn on_busy(&mut self, busy: bool) -> RepaintMode { + pub fn on_busy(&mut self, busy: bool) -> RepaintMode { if busy { self.cursor.as_mut().unwrap().busy_on(); } else { @@ -1138,7 +1181,7 @@ impl RedrawEvents for State { RepaintMode::Area(self.model.cur_point()) } - fn popupmenu_show( + pub fn popupmenu_show( &mut self, menu: &[CompleteItem], selected: i64, @@ -1167,17 +1210,17 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn popupmenu_hide(&mut self) -> RepaintMode { + pub fn popupmenu_hide(&mut self) -> RepaintMode { self.popup_menu.hide(); RepaintMode::Nothing } - fn popupmenu_select(&mut self, selected: i64) -> RepaintMode { + pub fn popupmenu_select(&mut self, selected: i64) -> RepaintMode { self.popup_menu.select(selected); RepaintMode::Nothing } - fn tabline_update( + pub fn tabline_update( &mut self, selected: Tabpage, tabs: Vec<(Tabpage, Option)>, @@ -1187,7 +1230,7 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn mode_info_set( + pub fn mode_info_set( &mut self, cursor_style_enabled: bool, mode_info: Vec, @@ -1197,7 +1240,7 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn cmdline_show( + pub fn cmdline_show( &mut self, content: Vec<(HashMap, String)>, pos: u64, @@ -1230,12 +1273,12 @@ impl RedrawEvents for State { self.on_busy(true) } - fn cmdline_hide(&mut self, level: u64) -> RepaintMode { + pub fn cmdline_hide(&mut self, level: u64) -> RepaintMode { self.cmd_line.hide_level(level); self.on_busy(false) } - fn cmdline_block_show( + pub fn cmdline_block_show( &mut self, content: Vec, String)>>, ) -> RepaintMode { @@ -1244,7 +1287,7 @@ impl RedrawEvents for State { self.on_busy(true) } - fn cmdline_block_append( + pub fn cmdline_block_append( &mut self, content: Vec<(HashMap, String)>, ) -> RepaintMode { @@ -1252,18 +1295,18 @@ impl RedrawEvents for State { RepaintMode::Nothing } - fn cmdline_block_hide(&mut self) -> RepaintMode { + pub fn cmdline_block_hide(&mut self) -> RepaintMode { self.cmd_line.block_hide(); self.on_busy(false) } - fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode { + pub fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode { let render_state = self.render_state.borrow(); self.cmd_line.pos(&*render_state, pos, level); RepaintMode::Nothing } - fn cmdline_special_char(&mut self, c: String, shift: bool, level: u64) -> RepaintMode { + pub fn cmdline_special_char(&mut self, c: String, shift: bool, level: u64) -> RepaintMode { let render_state = self.render_state.borrow(); self.cmd_line.special_char(&*render_state, c, shift, level); RepaintMode::Nothing @@ -1276,14 +1319,3 @@ impl CursorRedrawCb for State { self.on_redraw(&RepaintMode::Area(cur_point)); } } - -impl GuiApi for State { - fn set_font(&mut self, font_desc: &str) { - { - let mut settings = self.settings.borrow_mut(); - settings.set_font_source(FontSource::Rpc); - } - - self.set_font_desc(font_desc); - } -} diff --git a/src/ui.rs b/src/ui.rs index a01706f..d3831b4 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -10,8 +10,12 @@ use gtk::prelude::*; use gtk::{AboutDialog, ApplicationWindow, Button, HeaderBar, Orientation, Paned, SettingsExt}; use gio::prelude::*; use gio::{Menu, MenuExt, MenuItem, SimpleAction}; +use glib::variant::FromVariant; + use toml; +use neovim_lib::Value; + use settings::{Settings, SettingsLoader}; use shell::{self, Shell, ShellOptions}; use shell_dlg; @@ -277,6 +281,20 @@ impl Ui { state.run_now(&update_subtitle); } })); + + let sidebar_action = UiMutex::new(show_sidebar_action); + shell.set_nvim_command_cb(Some(move |args: Vec| { + if let Some(cmd) = args[0].as_str() { + match cmd { + "ToggleSidebar" => { + let action = sidebar_action.borrow(); + let state = !bool::from_variant(&action.get_state().unwrap()).unwrap(); + action.change_state(&state.to_variant()); + } + _ => {} + } + } + })); } fn create_header_bar(&self) -> SubscriptionHandle { From 82c7f0818a688013d1266f2847657dc3054d468f Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 31 Mar 2018 19:56:44 +0300 Subject: [PATCH 58/59] Prevent popup blink in case events hide/show come together --- src/nvim/handler.rs | 4 +++- src/nvim/redraw_handler.rs | 45 +++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/nvim/handler.rs b/src/nvim/handler.rs index c6ee331..3c62eae 100644 --- a/src/nvim/handler.rs +++ b/src/nvim/handler.rs @@ -19,9 +19,11 @@ impl NvimHandler { NvimHandler { shell: shell } } - fn nvim_cb(&self, method: &str, params: Vec) { + fn nvim_cb(&self, method: &str, mut params: Vec) { match method { "redraw" => { + redraw_handler::remove_uneeded_events(&mut params); + self.safe_call(move |ui| { let ui = &mut ui.borrow_mut(); let mut repaint_mode = RepaintMode::Nothing; diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index bef2e3f..f82d55b 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -116,7 +116,7 @@ pub fn call_gui_event( }, "Command" => { ui.on_command(args); - }, + } _ => return Err(format!("Unsupported event {}({:?})", method, args)), } Ok(()) @@ -247,6 +247,30 @@ pub fn call( Ok(repaint_mode) } +// menu content update call popupmenu_hide followed by popupmenu_show +// this generates unneded hide event +// so in case we get both events, just romove one +pub fn remove_uneeded_events(params: &mut Vec) { + let mut show_popup_finded = false; + let mut to_remove = Vec::new(); + + for (idx, val) in params.iter().enumerate().rev() { + if let Some(args) = val.as_array() { + match args[0].as_str() { + Some("popupmenu_show") => show_popup_finded = true, + Some("popupmenu_hide") if show_popup_finded => { + to_remove.push(idx); + } + _ => (), + } + } + } + + to_remove.iter().for_each(|&idx| { + params.remove(idx); + }); +} + pub struct CompleteItem<'a> { pub word: &'a str, pub kind: &'a str, @@ -266,3 +290,22 @@ impl<'a> CompleteItem<'a> { .collect() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_remove_popup_menu_hide() { + // remove only first hide + let mut params = vec![ + Value::from(vec![Value::from("popupmenu_hide")]), + Value::from(vec![Value::from("popupmenu_show")]), + Value::from(vec![Value::from("popupmenu_hide")]), + ]; + + remove_uneeded_events(&mut params); + + assert_eq!(2, params.len()); + } +} From e5994b79d476d2869c5ed363453f31ac06f9616f Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 31 Mar 2018 22:57:53 +0300 Subject: [PATCH 59/59] Use timeout to hide popup menu this prevent "blinking animation" of menu in case of popup population take some time. --- src/nvim/client.rs | 18 +++-- src/nvim/ext.rs | 1 - src/nvim/handler.rs | 161 +++++++++++++++++++++++++------------ src/nvim/mod.rs | 9 +-- src/nvim/mode_info.rs | 6 +- src/nvim/redraw_handler.rs | 49 +++++------ src/nvim/repaint_mode.rs | 1 - src/popup_menu.rs | 10 +-- src/shell.rs | 8 +- src/ui.rs | 7 ++ 10 files changed, 167 insertions(+), 103 deletions(-) diff --git a/src/nvim/client.rs b/src/nvim/client.rs index a70b98b..5e34554 100644 --- a/src/nvim/client.rs +++ b/src/nvim/client.rs @@ -36,9 +36,7 @@ impl<'a> NeovimRef<'a> { pub fn non_blocked(mut self) -> Option { self.get_mode().ok_and_report().and_then(|mode| { mode.iter() - .find(|kv| { - kv.0.as_str().map(|key| key == "blocking").unwrap_or(false) - }) + .find(|kv| kv.0.as_str().map(|key| key == "blocking").unwrap_or(false)) .map(|kv| kv.1.as_bool().unwrap_or(false)) .and_then(|block| if block { None } else { Some(self) }) }) @@ -71,7 +69,9 @@ pub struct NeovimClientAsync { impl NeovimClientAsync { fn new() -> Self { - NeovimClientAsync { nvim: Arc::new(Mutex::new(None)) } + NeovimClientAsync { + nvim: Arc::new(Mutex::new(None)), + } } pub fn borrow(&self) -> Option { @@ -81,7 +81,9 @@ impl NeovimClientAsync { impl Clone for NeovimClientAsync { fn clone(&self) -> Self { - NeovimClientAsync { nvim: self.nvim.clone() } + NeovimClientAsync { + nvim: self.nvim.clone(), + } } } @@ -147,9 +149,9 @@ impl NeovimClient { pub fn nvim(&self) -> Option { let nvim = self.nvim.borrow_mut(); if nvim.is_some() { - Some(NeovimRef::from_nvim( - RefMut::map(nvim, |n| n.as_mut().unwrap()), - )) + Some(NeovimRef::from_nvim(RefMut::map(nvim, |n| { + n.as_mut().unwrap() + }))) } else { self.nvim_async.borrow() } diff --git a/src/nvim/ext.rs b/src/nvim/ext.rs index 8dc7283..f50961c 100644 --- a/src/nvim/ext.rs +++ b/src/nvim/ext.rs @@ -1,4 +1,3 @@ - use std::result; use neovim_lib::CallError; diff --git a/src/nvim/handler.rs b/src/nvim/handler.rs index 3c62eae..4e471ec 100644 --- a/src/nvim/handler.rs +++ b/src/nvim/handler.rs @@ -1,5 +1,5 @@ use std::result; -use std::sync::{Arc, mpsc}; +use std::sync::{mpsc, Arc}; use neovim_lib::{Handler, Value}; @@ -12,51 +12,60 @@ use super::redraw_handler; pub struct NvimHandler { shell: Arc>, + + delayed_redraw_event_id: Arc>>, } impl NvimHandler { pub fn new(shell: Arc>) -> NvimHandler { - NvimHandler { shell: shell } + NvimHandler { + shell, + delayed_redraw_event_id: Arc::new(UiMutex::new(None)), + } + } + + pub fn schedule_redraw_event(&self, event: Value) { + let shell = self.shell.clone(); + let delayed_redraw_event_id = self.delayed_redraw_event_id.clone(); + + glib::idle_add(move || { + let id = Some(glib::timeout_add( + 250, + clone!(shell, event, delayed_redraw_event_id => move || { + delayed_redraw_event_id.replace(None); + + if let Err(msg) = call_redraw_handler(vec![event.clone()], &shell) { + error!("Error call function: {}", msg); + } + + glib::Continue(false) + }), + )); + + delayed_redraw_event_id.replace(id); + + glib::Continue(false) + }); + } + + pub fn remove_scheduled_redraw_event(&self) { + let delayed_redraw_event_id = self.delayed_redraw_event_id.clone(); + glib::idle_add(move || { + let id = delayed_redraw_event_id.replace(None); + if let Some(ev_id) = id { + glib::source_remove(ev_id); + } + + glib::Continue(false) + }); } fn nvim_cb(&self, method: &str, mut params: Vec) { match method { "redraw" => { - redraw_handler::remove_uneeded_events(&mut params); + redraw_handler::remove_or_delay_uneeded_events(self, &mut params); - self.safe_call(move |ui| { - let ui = &mut ui.borrow_mut(); - let mut repaint_mode = RepaintMode::Nothing; - - for ev in params { - if let Value::Array(ev_args) = ev { - let mut args_iter = ev_args.into_iter(); - let ev_name = args_iter.next(); - if let Some(ev_name) = ev_name { - if let Some(ev_name) = ev_name.as_str() { - for local_args in args_iter { - let args = match local_args { - Value::Array(ar) => ar, - _ => vec![], - }; - let call_reapint_mode = - redraw_handler::call(ui, &ev_name, args)?; - repaint_mode = repaint_mode.join(call_reapint_mode); - } - } else { - error!("Unsupported event"); - } - } else { - error!("Event name does not exists"); - } - } else { - error!("Unsupported event type {:?}", ev); - } - } - - ui.on_redraw(&repaint_mode); - Ok(()) - }); + self.safe_call(move |ui| call_redraw_handler(params, ui)); } "Gui" => { if !params.is_empty() { @@ -68,7 +77,9 @@ impl NvimHandler { let ui = &mut ui.borrow_mut(); redraw_handler::call_gui_event( ui, - ev_name.as_str().ok_or_else(|| "Event name does not exists")?, + ev_name + .as_str() + .ok_or_else(|| "Event name does not exists")?, args, )?; ui.on_redraw(&RepaintMode::All); @@ -96,7 +107,7 @@ impl NvimHandler { } } - fn nvim_cb_req (&self, method: &str, params: Vec) -> result::Result { + fn nvim_cb_req(&self, method: &str, params: Vec) -> result::Result { match method { "Gui" => { if !params.is_empty() { @@ -106,11 +117,15 @@ impl NvimHandler { let args = params_iter.collect(); let (sender, receiver) = mpsc::channel(); self.safe_call(move |ui| { - sender.send(redraw_handler::call_gui_request( - &ui.clone(), - req_name.as_str().ok_or_else(|| "Event name does not exists")?, - &args, - )).unwrap(); + sender + .send(redraw_handler::call_gui_request( + &ui.clone(), + req_name + .as_str() + .ok_or_else(|| "Event name does not exists")?, + &args, + )) + .unwrap(); { let ui = &mut ui.borrow_mut(); ui.on_redraw(&RepaintMode::All); @@ -130,7 +145,7 @@ impl NvimHandler { error!("Unsupported request {:?}", params); Err(Value::Nil) } - }, + } _ => { error!("Request {}({:?})", method, params); Err(Value::Nil) @@ -142,17 +157,59 @@ impl NvimHandler { where F: FnOnce(&Arc>) -> result::Result<(), String> + 'static + Send, { - let mut cb = Some(cb); - let shell = self.shell.clone(); - glib::idle_add(move || { - if let Err(msg) = cb.take().unwrap()(&shell) { - error!("Error call function: {}", msg); - } - glib::Continue(false) - }); + safe_call(self.shell.clone(), cb); } } +fn call_redraw_handler( + params: Vec, + ui: &Arc>, +) -> result::Result<(), String> { + let ui = &mut ui.borrow_mut(); + let mut repaint_mode = RepaintMode::Nothing; + + for ev in params { + if let Value::Array(ev_args) = ev { + let mut args_iter = ev_args.into_iter(); + let ev_name = args_iter.next(); + if let Some(ev_name) = ev_name { + if let Some(ev_name) = ev_name.as_str() { + for local_args in args_iter { + let args = match local_args { + Value::Array(ar) => ar, + _ => vec![], + }; + let call_reapint_mode = redraw_handler::call(ui, &ev_name, args)?; + repaint_mode = repaint_mode.join(call_reapint_mode); + } + } else { + error!("Unsupported event"); + } + } else { + error!("Event name does not exists"); + } + } else { + error!("Unsupported event type {:?}", ev); + } + } + + ui.on_redraw(&repaint_mode); + Ok(()) +} + +fn safe_call(shell: Arc>, cb: F) +where + F: FnOnce(&Arc>) -> result::Result<(), String> + 'static + Send, +{ + let mut cb = Some(cb); + glib::idle_add(move || { + if let Err(msg) = cb.take().unwrap()(&shell) { + error!("Error call function: {}", msg); + } + glib::Continue(false) + }); +} + impl Handler for NvimHandler { fn handle_notify(&mut self, name: &str, args: Vec) { self.nvim_cb(name, args); diff --git a/src/nvim/mod.rs b/src/nvim/mod.rs index 95a1c78..5ec8c92 100644 --- a/src/nvim/mod.rs +++ b/src/nvim/mod.rs @@ -10,20 +10,18 @@ pub use self::repaint_mode::RepaintMode; pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef}; pub use self::mode_info::{CursorShape, ModeInfo}; pub use self::ext::ErrorReport; +pub use self::handler::NvimHandler; use std::error; use std::fmt; use std::env; use std::process::{Command, Stdio}; use std::result; -use std::sync::Arc; use std::time::Duration; use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Session, UiAttachOptions}; use misc::escape_filename; -use ui::UiMutex; -use shell; use nvim_config::NvimConfig; #[derive(Debug)] @@ -85,7 +83,7 @@ fn set_windows_creation_flags(cmd: &mut Command) { } pub fn start( - shell: Arc>, + handler: NvimHandler, nvim_bin_path: Option<&String>, timeout: Option, ) -> result::Result { @@ -135,8 +133,7 @@ pub fn start( let mut nvim = Neovim::new(session); - nvim.session - .start_event_loop_handler(handler::NvimHandler::new(shell)); + nvim.session.start_event_loop_handler(handler); Ok(nvim) } diff --git a/src/nvim/mode_info.rs b/src/nvim/mode_info.rs index 4d0de6f..7515d7e 100644 --- a/src/nvim/mode_info.rs +++ b/src/nvim/mode_info.rs @@ -12,9 +12,9 @@ pub enum CursorShape { impl CursorShape { fn new(shape_code: &Value) -> Result { - let str_code = shape_code.as_str().ok_or_else(|| { - "Can't convert cursor shape to string".to_owned() - })?; + let str_code = shape_code + .as_str() + .ok_or_else(|| "Can't convert cursor shape to string".to_owned())?; Ok(match str_code { "block" => CursorShape::Block, diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index f82d55b..865f91b 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -13,6 +13,7 @@ use rmpv; use super::repaint_mode::RepaintMode; use super::mode_info::ModeInfo; +use super::handler::NvimHandler; macro_rules! try_str { ($exp:expr) => ($exp.as_str().ok_or_else(|| "Can't convert argument to string".to_owned())?) @@ -247,18 +248,32 @@ pub fn call( Ok(repaint_mode) } -// menu content update call popupmenu_hide followed by popupmenu_show +// Here two cases processed: +// +// 1. menu content update call popupmenu_hide followed by popupmenu_show in same batch // this generates unneded hide event // so in case we get both events, just romove one -pub fn remove_uneeded_events(params: &mut Vec) { +// +// 2. hide event postpone in case show event come bit later +// but in new event batch +pub fn remove_or_delay_uneeded_events(handler: &NvimHandler, params: &mut Vec) { let mut show_popup_finded = false; let mut to_remove = Vec::new(); + let mut delayed_hide_event = None; for (idx, val) in params.iter().enumerate().rev() { if let Some(args) = val.as_array() { match args[0].as_str() { - Some("popupmenu_show") => show_popup_finded = true, - Some("popupmenu_hide") if show_popup_finded => { + Some("popupmenu_show") => { + show_popup_finded = true; + handler.remove_scheduled_redraw_event(); + } + Some("popupmenu_hide") if !show_popup_finded && delayed_hide_event.is_none() => { + to_remove.push(idx); + delayed_hide_event = Some(idx); + handler.remove_scheduled_redraw_event(); + } + Some("popupmenu_hide") => { to_remove.push(idx); } _ => (), @@ -267,7 +282,12 @@ pub fn remove_uneeded_events(params: &mut Vec) { } to_remove.iter().for_each(|&idx| { - params.remove(idx); + let ev = params.remove(idx); + if let Some(delayed_hide_event_idx) = delayed_hide_event { + if delayed_hide_event_idx == idx { + handler.schedule_redraw_event(ev); + } + } }); } @@ -290,22 +310,3 @@ impl<'a> CompleteItem<'a> { .collect() } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_remove_popup_menu_hide() { - // remove only first hide - let mut params = vec![ - Value::from(vec![Value::from("popupmenu_hide")]), - Value::from(vec![Value::from("popupmenu_show")]), - Value::from(vec![Value::from("popupmenu_hide")]), - ]; - - remove_uneeded_events(&mut params); - - assert_eq!(2, params.len()); - } -} diff --git a/src/nvim/repaint_mode.rs b/src/nvim/repaint_mode.rs index de967e7..70bc4d6 100644 --- a/src/nvim/repaint_mode.rs +++ b/src/nvim/repaint_mode.rs @@ -38,7 +38,6 @@ impl RepaintMode { } } - #[cfg(test)] mod tests { use super::*; diff --git a/src/popup_menu.rs b/src/popup_menu.rs index 3f5207a..b8c678b 100644 --- a/src/popup_menu.rs +++ b/src/popup_menu.rs @@ -32,6 +32,7 @@ struct State { impl State { pub fn new() -> Self { let tree = gtk::TreeView::new(); + tree.get_selection().set_mode(gtk::SelectionMode::Single); let css_provider = gtk::CssProvider::new(); let style_context = tree.get_style_context().unwrap(); @@ -140,9 +141,6 @@ impl State { self.renderer.set_property_foreground_rgba( Some(&color_model.pmenu_fg().into()), ); - self.renderer.set_property_background_rgba( - Some(&color_model.pmenu_bg().into()), - ); self.update_css(color_model); @@ -164,9 +162,11 @@ impl State { match gtk::CssProviderExt::load_from_data( &self.css_provider, &format!( - ".view {{ color: {}; background-color: {};}}", + ".view :selected {{ color: {}; background-color: {};}}\n + .view {{ background-color: {}; }}", fg.to_hex(), - bg.to_hex() + bg.to_hex(), + color_model.pmenu_bg().to_hex(), ).as_bytes(), ) { Err(e) => error!("Can't update css {}", e), diff --git a/src/shell.rs b/src/shell.rs index fd7a8fa..c1b2ed1 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -24,7 +24,7 @@ use settings::{FontSource, Settings}; use ui_model::{Attrs, ModelRect, UiModel}; use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE}; use nvim::{self, CompleteItem, ErrorReport, NeovimClient, NeovimClientAsync, NeovimRef, - RepaintMode}; + RepaintMode, NvimHandler}; use input; use input::keyval_to_input_string; use cursor::{BlinkCursor, Cursor, CursorRedrawCb}; @@ -919,13 +919,14 @@ fn show_nvim_init_error(err: &nvim::NvimInitError, state_arc: Arc fn init_nvim_async( state_arc: Arc>, + nvim_handler: NvimHandler, options: ShellOptions, cols: usize, rows: usize, ) { // execute nvim let nvim = match nvim::start( - state_arc.clone(), + nvim_handler, options.nvim_bin_path.as_ref(), options.timeout, ) { @@ -1053,8 +1054,9 @@ fn init_nvim(state_ref: &Arc>) { state.model = UiModel::new(rows as u64, cols as u64); let state_arc = state_ref.clone(); + let nvim_handler = NvimHandler::new(state_ref.clone()); let options = state.options.clone(); - thread::spawn(move || init_nvim_async(state_arc, options, cols, rows)); + thread::spawn(move || init_nvim_async(state_arc, nvim_handler, options, cols, rows)); } } diff --git a/src/ui.rs b/src/ui.rs index d3831b4..14ef1f0 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -486,6 +486,13 @@ impl UiMutex { } } +impl UiMutex { + pub fn replace(&self, t: T) -> T { + self.assert_ui_thread(); + self.data.replace(t) + } +} + impl UiMutex { pub fn borrow(&self) -> Ref { self.assert_ui_thread();