extern crate chrono;
use chrono::prelude::*;
use std::collections::HashMap;
use std::collections::HashSet;
use crate::model;
use crate::model_internal::LogEntryExt;
pub struct ScrollBarVert {
pub x: f64,
pub y: f64,
pub bar_padding: f64,
pub bar_width: f64,
pub bar_height: f64,
pub thumb_x: f64,
pub thumb_y: f64,
pub thumb_margin: f64,
pub thumb_width: f64,
pub thumb_height: f64,
pub thumb_rel_offset: f64,
pub scroll_perc: f64,
}
pub struct LogStoreLinear {
pub store: Vec<LogEntryExt>,
pub entry_count: usize, pub first_offset: usize, pub last_offset: usize, pub anchor_offset: Option<usize>,
pub show_crit: bool,
pub show_err: bool,
pub show_warn: bool,
pub show_info: bool,
pub show_dbg: bool,
pub show_trace: bool,
pub selected_single: HashSet<usize>,
pub excluded_single: HashSet<usize>,
pub selected_single_last: Option<usize>,
pub selected_range: Option<(usize, usize)>,
pub pressed_shift: bool,
pub pressed_ctrl: bool,
pub log_sources: HashMap<u32, String>,
pub visible_lines: usize, pub hover_line: Option<usize>, pub viewport_offset: usize, pub mouse_down: bool,
pub thumb_drag: bool,
pub thumb_drag_x: f64,
pub thumb_drag_y: f64,
pub scroll_bar: ScrollBarVert,
pub border_left: f64,
pub border_top: f64,
pub border_bottom: f64,
pub line_spacing: f64,
pub font_size: f64,
}
impl LogStoreLinear {
pub fn rel_to_abs_offset(&self, rel_offset: usize) -> Option<usize> {
self.store
.iter()
.enumerate() .skip(self.viewport_offset)
.filter(|(_, x)| x.is_visible())
.skip(rel_offset)
.take(1)
.next()
.map(|(offset, _)| offset)
}
pub fn abs_to_rel_offset(&self, abs_offset: usize) -> Option<usize> {
for (i, (offset, _)) in self
.store
.iter()
.enumerate() .skip(self.viewport_offset)
.filter(|(_, x)| x.is_visible())
.take(self.visible_lines)
.enumerate()
{
if offset == abs_offset {
return Some(i);
}
}
None
}
pub fn filter_store(&mut self, filter: &dyn Fn(&LogEntryExt) -> bool, active: bool, mask: u8) {
let (tmp_anchor_offset, rel_offset) = {
if let Some(anchor_offset) = self.anchor_offset {
if let Some(rel_offset) = self.abs_to_rel_offset(anchor_offset) {
(anchor_offset, rel_offset)
} else {
let mut rel_offset = None;
if let Some(mut abs_offset) = self.rel_to_abs_offset(0) {
let mut prev_offset = abs_offset;
for i in 1..self.visible_lines {
abs_offset = self.store[abs_offset].next_offset as usize;
if anchor_offset >= prev_offset && anchor_offset <= abs_offset {
rel_offset = Some(i);
break;
}
prev_offset = abs_offset;
}
}
if let Some(rel_offset) = rel_offset {
(anchor_offset, rel_offset)
} else {
(
anchor_offset,
(std::cmp::max(1, self.visible_lines) - 1) / 2,
)
}
}
} else if self.entry_count >= self.visible_lines {
if let Some(mut abs_offset) = self.rel_to_abs_offset(0) {
let rel_offset = (std::cmp::max(1, self.visible_lines) - 1) / 2;
for _ in 0..rel_offset {
abs_offset = self.store[abs_offset].next_offset as usize;
}
(abs_offset, rel_offset)
} else {
(0, 0)
}
} else {
(0, 0)
}
};
let mut next_entry_id = 0;
let mut dummy = LogEntryExt {
timestamp: DateTime::<Utc>::from_utc(
NaiveDateTime::from_timestamp_opt(0, 0).unwrap(),
Utc,
),
severity: model::LogLevel::Error,
message: "Foo".to_string(),
source_id: 0,
visible: crate::model_internal::VISIBLE_ON,
entry_id: 0,
prev_offset: 0,
next_offset: 0,
};
{
let mut prev = &mut dummy;
let mut prev_offset = 0;
for (offset, entry) in self.store.iter_mut().enumerate() {
if filter(entry) {
if active {
entry.visible &= !mask; } else {
entry.visible |= mask; }
}
if entry.is_visible() {
entry.entry_id = next_entry_id;
next_entry_id += 1;
prev.next_offset = offset as u32;
entry.prev_offset = prev_offset;
prev_offset = offset as u32;
prev = entry;
}
}
self.last_offset = prev_offset as usize;
prev.next_offset = prev_offset; }
self.entry_count = next_entry_id as usize; self.first_offset = dummy.next_offset as usize; if !self.store.is_empty() {
self.store[self.first_offset].prev_offset = self.first_offset as u32;
}
if !self.store.is_empty() && self.store[tmp_anchor_offset].is_visible() {
self.viewport_offset = tmp_anchor_offset;
self.scroll(-(rel_offset as i64), self.visible_lines);
} else {
let mut found = false;
for (offset, _) in self
.store
.iter()
.enumerate() .skip(tmp_anchor_offset)
.filter(|(_, x)| x.is_visible())
.take(1)
{
self.viewport_offset = offset;
found = true;
}
if !found {
self.viewport_offset = self.last_offset;
println!("Not found {}!!", rel_offset);
}
self.scroll(-(rel_offset as i64), self.visible_lines);
}
}
pub fn percentage_to_offset(&self, perc: f64, window_size: usize) -> Option<usize> {
if perc < 0.0 || perc > 1.0 {
return None;
}
if window_size == 0 {
return None;
}
if self.entry_count == 0 {
return None;
}
if window_size >= self.entry_count {
return Some(0);
}
let entry_id = ((self.entry_count - window_size) as f64 * perc).round() as u32;
self.store
.iter()
.enumerate()
.skip(std::cmp::max(1, entry_id as usize) - 1)
.filter(|(_, x)| x.is_visible() && x.entry_id == entry_id)
.take(1)
.next()
.map_or_else(|| unreachable!(), |(offset, _)| Some(offset))
}
pub fn get_scroll_percentage(&self, window_size: usize) -> f64 {
if self.entry_count == 0 {
return 0.0; }
if self.entry_count <= window_size {
return 0.0; }
let percentage = (self.store[self.viewport_offset].entry_id as f64)
/ ((self.entry_count - window_size) as f64);
if percentage > 1.0 {
return 1.0; }
percentage
}
pub fn scroll(&mut self, lines: i64, window_size: usize) -> bool {
if self.entry_count == 0 {
return false; }
let viewport_offset_old = self.viewport_offset;
let mut abs_lines = lines.abs();
if lines < 0 {
while abs_lines > 0 {
let new_offset = self.store[self.viewport_offset].prev_offset as usize;
if self.viewport_offset == new_offset {
break; }
self.viewport_offset = new_offset;
abs_lines -= 1;
}
} else {
if self.entry_count <= window_size {
return false; }
while abs_lines > 0 {
if (self.entry_count - window_size)
<= self.store[self.viewport_offset].entry_id as usize
{
break; }
let new_offset = self.store[self.viewport_offset].next_offset as usize;
if self.viewport_offset == new_offset {
break; }
self.viewport_offset = new_offset;
abs_lines -= 1;
}
}
viewport_offset_old != self.viewport_offset
}
}