/* * meli - status tab module. * * Copyright 2019 Manos Pitsidianakis * * This file is part of meli. * * meli is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * meli is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ use super::*; #[derive(Debug)] pub struct AccountStatus { cursor: (usize, usize), account_pos: usize, content: CellBuffer, dirty: bool, theme_default: ThemeAttribute, id: ComponentId, } impl fmt::Display for AccountStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "status") } } impl AccountStatus { pub fn new(account_pos: usize, theme_default: ThemeAttribute) -> AccountStatus { let default_cell = { let mut ret = Cell::with_char(' '); ret.set_fg(theme_default.fg) .set_bg(theme_default.bg) .set_attrs(theme_default.attrs); ret }; let mut content = CellBuffer::new(120, 5, default_cell); content.set_growable(true); AccountStatus { cursor: (0, 0), account_pos, content, dirty: true, theme_default, id: ComponentId::new_v4(), } } } impl Component for AccountStatus { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { if !self.dirty { return; } self.dirty = false; let (mut width, _) = self.content.size(); let a = &context.accounts[self.account_pos]; let (_x, _y) = write_string_to_grid( "Account ", &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs | Attr::UNDERLINE, ((1, 0), (width - 1, 0)), None, ); let (_x, _y) = write_string_to_grid( a.name(), &mut self.content, self.theme_default.fg, self.theme_default.bg, Attr::BOLD | Attr::UNDERLINE, ((_x, _y), (width - 1, _y)), None, ); width = self.content.size().0; let mut line = 2; let (_x, _y) = write_string_to_grid( "Tag support: ", &mut self.content, self.theme_default.fg, self.theme_default.bg, Attr::BOLD, ((1, line), (width - 1, line)), None, ); width = self.content.size().0; write_string_to_grid( if a.backend_capabilities.supports_tags { "yes" } else { "no" }, &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((_x, _y), (width - 1, line)), None, ); width = self.content.size().0; line += 1; let (_x, _y) = write_string_to_grid( "Search backend: ", &mut self.content, self.theme_default.fg, self.theme_default.bg, Attr::BOLD, ((1, line), (width - 1, line)), None, ); width = self.content.size().0; write_string_to_grid( &match ( a.settings.conf.search_backend(), a.backend_capabilities.supports_search, ) { (SearchBackend::Auto, true) | (SearchBackend::None, true) => { "backend-side search".to_string() } (SearchBackend::Auto, false) | (SearchBackend::None, false) => { "none (search will be slow)".to_string() } #[cfg(feature = "sqlite3")] (SearchBackend::Sqlite3, _) => { if let Ok(path) = crate::sqlite3::db_path() { format!("sqlite3 database {}", path.display()) } else { "sqlite3 database".to_string() } } }, &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((_x, _y), (width - 1, _y)), None, ); width = self.content.size().0; line += 1; write_string_to_grid( "Special Mailboxes:", &mut self.content, self.theme_default.fg, self.theme_default.bg, Attr::BOLD, ((1, line), (width - 1, line)), None, ); for f in a .mailbox_entries .values() .map(|entry| &entry.ref_mailbox) .filter(|f| f.special_usage() != SpecialUsageMailbox::Normal) { width = self.content.size().0; line += 1; write_string_to_grid( &format!("{}: {}", f.path(), f.special_usage()), &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((1, line), (width - 1, line)), None, ); } line += 2; width = self.content.size().0; write_string_to_grid( "Subscribed mailboxes:", &mut self.content, self.theme_default.fg, self.theme_default.bg, Attr::BOLD, ((1, line), (width - 1, line)), None, ); line += 2; for mailbox_node in a.list_mailboxes() { width = self.content.size().0; let f: &Mailbox = &a[&mailbox_node.hash].ref_mailbox; if f.is_subscribed() { write_string_to_grid( f.path(), &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((1, line), (width - 1, line)), None, ); line += 1; } } line += 1; width = self.content.size().0; if let Some(ref extensions) = a.backend_capabilities.extensions { write_string_to_grid( "Server Extensions:", &mut self.content, self.theme_default.fg, self.theme_default.bg, Attr::BOLD, ((1, line), (width - 1, line)), None, ); let max_name_width = std::cmp::max( "Server Extensions:".len(), extensions .iter() .map(|(n, _)| std::cmp::min(30, n.len())) .max() .unwrap_or(0), ); width = self.content.size().0; write_string_to_grid( "meli support:", &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((max_name_width + 6, line), (width - 1, line)), None, ); line += 1; for (name, status) in extensions.into_iter() { width = self.content.size().0; write_string_to_grid( name.trim_at_boundary(30), &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((1, line), (width - 1, line)), None, ); width = self.content.size().0; let (x, y) = match status { MailBackendExtensionStatus::Unsupported { comment: _ } => write_string_to_grid( "not supported", &mut self.content, Color::Red, self.theme_default.bg, self.theme_default.attrs, ((max_name_width + 6, line), (width - 1, line)), None, ), MailBackendExtensionStatus::Supported { comment: _ } => write_string_to_grid( "supported", &mut self.content, Color::Green, self.theme_default.bg, self.theme_default.attrs, ((max_name_width + 6, line), (width - 1, line)), None, ), MailBackendExtensionStatus::Enabled { comment: _ } => write_string_to_grid( "enabled", &mut self.content, Color::Green, self.theme_default.bg, self.theme_default.attrs, ((max_name_width + 6, line), (width - 1, line)), None, ), }; match status { MailBackendExtensionStatus::Unsupported { comment } | MailBackendExtensionStatus::Supported { comment } | MailBackendExtensionStatus::Enabled { comment } => { if let Some(s) = comment { let (x, y) = write_string_to_grid( " (", &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((x, y), (width - 1, y)), None, ); let (x, y) = write_string_to_grid( s, &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((x, y), (width - 1, y)), None, ); write_string_to_grid( ")", &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((x, y), (width - 1, y)), None, ); } } }; line += 1; } } line += 2; width = self.content.size().0; write_string_to_grid( "In-progress jobs:", &mut self.content, self.theme_default.fg, self.theme_default.bg, Attr::BOLD, ((1, line), (width - 1, line)), None, ); for (job_id, req) in a.active_jobs.iter() { width = self.content.size().0; use crate::conf::accounts::JobRequest; let (x, y) = write_string_to_grid( &format!("{} {}", req, job_id), &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((1, line), (width - 1, line)), None, ); if let JobRequest::DeleteMailbox { mailbox_hash, .. } | JobRequest::SetMailboxPermissions { mailbox_hash, .. } | JobRequest::SetMailboxSubscription { mailbox_hash, .. } | JobRequest::CopyTo { dest_mailbox_hash: mailbox_hash, .. } | JobRequest::Refresh { mailbox_hash, .. } | JobRequest::Fetch { mailbox_hash, .. } = req { write_string_to_grid( a.mailbox_entries[mailbox_hash].name(), &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, ((x + 1, y), (width - 1, y)), None, ); } line += 1; } /* self.content may have been resized with write_string_to_grid() calls above since it has * growable set */ let (width, height) = self.content.size(); let (cols, rows) = (width!(area), height!(area)); self.cursor = ( std::cmp::min(width.saturating_sub(cols), self.cursor.0), std::cmp::min(height.saturating_sub(rows), self.cursor.1), ); clear_area(grid, area, self.theme_default); copy_area( grid, &self.content, area, ( ( std::cmp::min((width - 1).saturating_sub(cols), self.cursor.0), std::cmp::min((height - 1).saturating_sub(rows), self.cursor.1), ), ( std::cmp::min(self.cursor.0 + cols, width - 1), std::cmp::min(self.cursor.1 + rows, height - 1), ), ), ); context.dirty_areas.push_back(area); } fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { match *event { UIEvent::Resize => { self.dirty = true; } UIEvent::Input(Key::Left) if self.cursor.0 != 0 => { self.cursor.0 -= 1; self.dirty = true; return true; } UIEvent::Input(Key::Right) => { self.cursor.0 = self.cursor.0 + 1; self.dirty = true; return true; } UIEvent::Input(Key::Up) => { self.cursor.1 = self.cursor.1.saturating_sub(1); self.dirty = true; return true; } UIEvent::Input(Key::Down) => { self.cursor.1 = self.cursor.1 + 1; self.dirty = true; return true; } UIEvent::MailboxUpdate(_) | UIEvent::StatusEvent(StatusEvent::NewJob(_)) | UIEvent::StatusEvent(StatusEvent::JobFinished(_)) | UIEvent::StatusEvent(StatusEvent::JobCanceled(_)) => { self.set_dirty(true); } _ => {} } false } fn is_dirty(&self) -> bool { self.dirty } fn set_dirty(&mut self, value: bool) { self.dirty = value; } fn id(&self) -> ComponentId { self.id } fn set_id(&mut self, id: ComponentId) { self.id = id; } }