URI: 
       Added color switcher. - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
  HTML git clone https://git.drkhsh.at/icy_draw.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 360d5fbd58baf9244b233fee2ac71f7812bcff82
   DIR parent 26831819b399f8f8ec0e567e3f039debf870cab2
  HTML Author: Mike Krueger <mkrueger@posteo.de>
       Date:   Sat, 10 Dec 2022 17:05:49 +0100
       
       Added color switcher.
       
       Diffstat:
         M src/model/editor/mod.rs             |     624 ++++++++++++++++++++++++++++---
         A src/model/editor/undo_stack.rs      |      97 ++++++++++++++++++++++++++++++
         M src/model/tool/icons.rs             |       4 ----
         M src/ui/ansi_editor/buffer_view.rs   |      28 +++++++++++++---------------
         M src/ui/ansi_editor/mod.rs           |      12 ++++++------
         M src/ui/ansi_editor/render.rs        |      38 ++++++++++++++++----------------
         M src/ui/ansi_editor/sixel.rs         |       8 ++++----
         A src/ui/icons.rs                     |       6 ++++++
         M src/ui/main_window.rs               |       3 +++
         M src/ui/mod.rs                       |       2 ++
         M src/ui/palette_editor.rs            |     123 +++++++++++++++++++++++++++++--
       
       11 files changed, 835 insertions(+), 110 deletions(-)
       ---
   DIR diff --git a/src/model/editor/mod.rs b/src/model/editor/mod.rs
       @@ -1,25 +1,27 @@
       -use std::{cmp::{max, min}, path::Path, io::Write, fs::File, ffi::OsStr};
       +use std::{cmp::{max, min}, path::{Path, PathBuf}, io::{Write, self}, fs::File, ffi::OsStr};
        
       -use icy_engine::{Position, Rectangle, Buffer, Caret, convert_to_binary, convert_to_xb, SaveOptions, convert_to_avt, convert_to_pcb, convert_to_asc};
       +use icy_engine::{Rectangle, Buffer, Caret, TextAttribute, Position, Layer, AttributedChar, Size, Line, SaveOptions};
       +
       +mod undo_stack;
       +pub use undo_stack::*;
        
        pub enum Event {
            None,
            CursorPositionChange(Position, Position)
        }
        
       -#[derive(Debug)]
       +#[derive(Clone, Debug)]
        pub enum Shape {
            Rectangle,
            Elipse
        }
        
       -#[derive(Debug)]
       +#[derive(Clone, Debug)]
        pub struct Selection
        {
            pub shape: Shape,
            pub rectangle: Rectangle,
            pub is_preview: bool,
       -    pub is_active: bool
        }
        
        impl Selection {
       @@ -29,7 +31,6 @@ impl Selection {
                    shape: Shape::Rectangle,
                    rectangle:  Rectangle::from(-1, -1, 0, 0),
                    is_preview: true,
       -            is_active: false
                }
            }
        }
       @@ -40,13 +41,33 @@ impl Default for Selection {
            }
        }
        
       -#[derive(Debug)]
        pub struct Editor {
            pub id: usize,
            pub buf: Buffer,
            
       -    pub cursor: Caret,
       -    pub cur_selection: Selection
       +    pub caret: Caret,
       +    pub cur_selection: Option<Selection>,
       +
       +    cur_outline: i32,
       +    pub is_inactive: bool,
       +
       +    pub reference_image: Option<PathBuf>,
       +    pub cur_layer: i32,
       +    // pub outline_changed: std::boxed::Box<dyn Fn(&Editor)>,
       +    //pub request_refresh: Box<dyn Fn ()>,
       +    atomic_undo_stack: Vec<usize>,
       +
       +    pub undo_stack: Vec<Box<dyn UndoOperation>>,
       +    pub redo_stack: Vec<Box<dyn UndoOperation>>,
       +
       +    //pub pos_changed: std::boxed::Box<dyn Fn(&Editor, Position)>,
       +    //pub attr_changed: std::boxed::Box<dyn Fn(TextAttribute)>
       +}
       +
       +impl std::fmt::Debug for Editor {
       +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
       +        f.debug_struct("Editor").field("id", &self.id).field("buf", &self.buf).field("caret", &self.caret).field("cur_selection", &self.cur_selection).field("cur_outline", &self.cur_outline).field("is_inactive", &self.is_inactive).finish()
       +    }
        }
        
        impl Default for Editor 
       @@ -64,81 +85,574 @@ impl Editor
                Editor {
                    id,
                    buf, 
       -            cursor: Caret::default(),
       -            cur_selection: Selection::default()
       +            caret: Caret::default(),
       +            cur_selection: None,
       +            cur_outline: 0,
       +            is_inactive: false,
       +            reference_image: None,
       +           //outline_changed: Box::new(|_| {}),
       +           // request_refresh: Box::new(|| {}),
       +            cur_layer: 0,
       +            atomic_undo_stack: Vec::new(),
       +            //pos_changed: Box::new(|_, _| {}),
       +            //attr_changed: Box::new(|_| {}),
       +            undo_stack: Vec::new(),
       +            redo_stack: Vec::new()
                }
            }
       -/* 
       -    pub fn handle_key(&mut self, key: gtk4::gdk::Key, key_code: u32, modifier: gtk4::gdk::ModifierType) -> Event
       +
       +    pub fn get_caret_position(&self) -> Position
            {
       -        unsafe {
       -            crate::WORKSPACE.cur_tool().handle_key( self, key, key_code, modifier)
       -        }
       +        self.caret.get_position()
            }
        
       -    pub fn handle_click(&mut self, button: u32, x: i32, y: i32) -> Event
       +    pub fn set_caret_position(&mut self, pos: Position)
            {
       -        unsafe {
       -            crate::WORKSPACE.cur_tool().handle_click( self, button, x, y)
       -        }
       +        let pos = Position::new(
       +            min(self.buf.get_buffer_width() as i32 - 1, max(0, pos.x)),
       +            min(self.buf.get_real_buffer_height() as i32 - 1, max(0, pos.y))
       +        );
       +        self.caret.set_position(pos);
       +        //(self.pos_changed)(self, pos);
            }
        
       -    pub fn handle_drag_begin(&mut self, start: Position, cur: Position) -> Event
       +    pub fn set_caret_attribute(&mut self, attr: TextAttribute)
            {
       -        unsafe {
       -            crate::WORKSPACE.cur_tool().handle_drag_begin( self, start, cur)
       +        if attr == self.caret.get_attribute() {
       +            return;
       +        }
       +
       +        self.caret.set_attr(attr);
       +       // (self.attr_changed)(attr);
       +    }
       +
       +    pub fn clear_layer(&mut self, layer_num: i32) -> ClearLayerOperation {
       +        let layers = std::mem::take(&mut self.buf.layers[layer_num as usize].lines);
       +        ClearLayerOperation {
       +            layer_num,
       +            lines: layers,
                }
            }
        
       -    pub fn handle_drag(&mut self, start: Position, cur: Position) -> Event
       +    pub fn get_cur_layer(&mut self) -> Option<&Layer>
            {
       -        unsafe {
       -            crate::WORKSPACE.cur_tool().handle_drag( self, start, cur)
       +        self.buf.layers.get(self.cur_layer as usize)
       +    }
       +
       +    pub fn get_cur_layer_mut(&mut self) -> Option<&mut Layer>
       +    {
       +        self.buf.layers.get_mut(self.cur_layer as usize)
       +    }
       +    
       +    pub fn get_overlay_layer(&mut self) -> &mut Option<Layer>
       +    {
       +        self.buf.get_overlay_layer()
       +    }
       +    
       +    pub fn join_overlay(&mut self)
       +    {
       +        self.begin_atomic_undo();
       +        let opt_layer = self.buf.remove_overlay();
       +
       +        if let Some(layer) = &opt_layer {
       +            for y in 0..layer.lines.len() {
       +                let line = &layer.lines[y];
       +                for x in 0..line.chars.len() {
       +                    let ch =line.chars[x];
       +                    if ch.is_some()  {
       +                        self.set_char(Position::new(x as i32, y as i32), ch);
       +                    }
       +                }
       +            }
                }
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn delete_line(&mut self, line: i32)
       +    {
       +        // TODO: Undo
       +        let layer = &mut self.buf.layers[self.cur_layer as usize];
       +        layer.remove_line(line);
            }
        
       -    pub fn handle_drag_end(&mut self, start: Position, cur: Position) -> Event
       +    pub fn insert_line(&mut self, line: i32) {
       +        // TODO: Undo
       +        let layer = &mut self.buf.layers[self.cur_layer as usize];
       +        layer.insert_line(line, Line::new());
       +    }
       +
       +    pub fn pickup_color(&mut self, pos: Position)
            {
       -        unsafe {
       -            crate::WORKSPACE.cur_tool().handle_drag_end( self, start, cur)
       +        let ch = self.buf.get_char(pos);
       +        if let Some(ch) = ch {
       +            self.caret.set_attr(ch.attribute);
                }
            }
       -    */
       -    pub fn set_cursor(&mut self, x: i32, y: i32) -> Event
       +
       +    pub fn set_caret(&mut self, x: i32, y: i32) -> Event
       +    {
       +        let old = self.caret.get_position();
       +        self.set_caret_position(Position::new(
       +            min(max(0, x), self.buf.get_buffer_width() as i32 - 1),
       +            min(max(0, y), self.buf.get_real_buffer_height() as i32 - 1)));
       +        Event::CursorPositionChange(old, self.caret.get_position())
       +    }
       +    
       +    pub fn get_cur_outline(&self) -> i32
       +    {
       +        self.cur_outline
       +    }
       +
       +    pub fn set_cur_outline(&mut self, outline: i32)
            {
       -        let old = self.cursor.get_position();
       -        self.cursor.set_position_xy(
       -            min(max(0, x), self.buf.get_buffer_width()),
       -            min(max(0, y), self.buf.get_real_buffer_height()));
       -        Event::CursorPositionChange(old, self.cursor.get_position())
       +        self.cur_outline = outline;
       +        //(self.outline_changed)(self);
            }
        
       -    pub fn save_content(&self, file_name: &Path)
       +    pub fn save_content(&self, file_name: &Path, options: &SaveOptions) -> io::Result<bool>
            {
       -        let mut f = File::create(file_name).expect("Can't create file.");
       +        let mut f = File::create(file_name)?;
        
                let content = 
       -            if let Some(ext) = file_name.extension() {
       -                let ext = OsStr::to_str(ext).unwrap().to_lowercase();
       -                self.get_file_content(ext.as_str())
       +        if let Some(ext) = file_name.extension() {
       +            let ext = OsStr::to_str(ext).unwrap().to_lowercase();
       +                self.buf.to_bytes(ext.as_str(), options)?
                    } else {
       -                self.get_file_content("")
       +                self.buf.to_bytes("mdf", options)?
                    };
       -        
       -        f.write_all(&content).expect("Can't write file.");
       +        f.write_all(&content)?;
       +        Ok(true)
            }
        
       -    pub fn get_file_content(&self, extension: &str) -> Vec<u8>
       +    pub fn get_outline_char_code(&self, i: i32) -> Result<u16, &str>
            {
       -        let options = SaveOptions::new();
       +        if self.cur_outline < 0 || self.cur_outline >= DEFAULT_OUTLINE_TABLE.len() as i32 {
       +            return Err("current outline out of range.");
       +        }
       +        if !(0..=10).contains(&i) {
       +            return Err("outline char# out of range.");
       +        }
       +        
       +        Ok(DEFAULT_OUTLINE_TABLE[self.cur_outline as usize][i as usize] as u16)
       +    }
       +    
       +    pub fn get_outline_char_code_from(outline:i32, i: i32) -> Result<u16, &'static str>
       +    {
       +        if outline < 0 || outline >= DEFAULT_OUTLINE_TABLE.len() as i32 {
       +            return Err("current outline out of range.");
       +        }
       +        if !(0..=10).contains(&i) {
       +            return Err("outline char# out of range.");
       +        }
       +        Ok(DEFAULT_OUTLINE_TABLE[outline as usize][i as usize] as u16)
       +    }
       +    
       +    pub fn get_char(&self, pos: Position) -> Option<AttributedChar> {
       +        self.buf.get_char(pos)
       +    }
       +
       +    pub fn get_char_from_cur_layer(&self, pos: Position) -> Option<AttributedChar> {
       +        if self.cur_layer >= self.buf.layers.len() as i32 {
       +            return None;
       +        }
       +        self.buf.layers[self.cur_layer as usize].get_char(pos)
       +    }
       +
       +    pub fn set_char(&mut self, pos: Position, dos_char: Option<AttributedChar>) {
       +        if self.point_is_valid(pos) {
       +            self.redo_stack.clear();
       +            let old = self.buf.get_char_from_layer(self.cur_layer as usize, pos);
       +            self.buf.set_char(self.cur_layer as usize, pos, dos_char);
       +            self.undo_stack.push(Box::new(UndoSetChar { pos, layer: self.cur_layer as usize, old, new: dos_char } ));
       +        }
       +    }
       +    pub fn begin_atomic_undo(&mut self) {
       +        self.atomic_undo_stack.push(self.undo_stack.len());
       +    }
       +
       +    pub fn end_atomic_undo(&mut self) {
       +        let base_count = self.atomic_undo_stack.pop().unwrap();
       +        let count = self.undo_stack.len();
       +        if base_count == count { return; }
       +
       +        let mut stack = Vec::new();
       +        while base_count < self.undo_stack.len() {
       +            let op = self.undo_stack.pop().unwrap();
       +            stack.push(op);
       +        }
       +        self.undo_stack.push(Box::new(super::AtomicUndo { stack }));
       +    }
       +
       +    pub fn undo(&mut self) {
       +        if let Some(op) = self.undo_stack.pop() {
       +            op.undo(&mut self.buf);
       +            self.redo_stack.push(op);
       +        }
       +    }
       +
       +    pub fn redo(&mut self) {
       +        if let Some(op) = self.redo_stack.pop() {
       +            op.redo(&mut self.buf);
       +            self.undo_stack.push(op);
       +        }
       +    }
       +
       +    pub fn fill(&mut self, rect: Rectangle, dos_char: Option<AttributedChar>) {
       +        let mut pos = rect.start;
       +        self.begin_atomic_undo();
       +        for _ in 0..rect.size.height {
       +            for _ in 0..rect.size.width {
       +                self.set_char(pos, dos_char);
       +                pos.x += 1;
       +            }
       +            pos.y += 1;
       +            pos.x = rect.start.x;
       +        }
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn point_is_valid(&self, pos: Position) -> bool {
       +        if let Some(selection) = &self.cur_selection {
       +            return selection.rectangle.is_inside(pos);
       +        }
       +
       +        pos.x >= 0 &&
       +        pos.y >= 0 && 
       +        pos.x < self.buf.get_buffer_width() as i32 &&
       +        pos.y < self.buf.get_real_buffer_height() as i32
       +    }
        
       -        match extension {
       -            "bin" => convert_to_binary(&self.buf, &options).unwrap(),
       -            "xb" => convert_to_xb(&self.buf, &options).unwrap(),
       -            "ans" => convert_to_xb(&self.buf, &options).unwrap(),
       -            "avt" => convert_to_avt(&self.buf, &options).unwrap(),
       -            "pcb" => convert_to_pcb(&self.buf, &options).unwrap(),
       -            _ => convert_to_asc(&self.buf, &options).unwrap()
       +    pub fn type_key(&mut self, char_code: char) {
       +        let pos = self.caret.get_position();
       +        if self.caret.insert_mode {
       +            for i in (self.buf.get_buffer_width() as i32 - 1)..=pos.x {
       +                let next = self.get_char_from_cur_layer( Position::new(i - 1, pos.y));
       +                self.set_char(Position::new(i, pos.y), next);
       +            }
                }
       +
       +        self.set_char(pos, Some(AttributedChar::new(char_code, self.caret.get_attribute())));
       +        self.set_caret(pos.x + 1, pos.y);
       +    }
       +
       +    pub fn delete_selection(&mut self) {
       +        if let Some(selection) = &self.cur_selection.clone() {
       +            self.begin_atomic_undo();
       +            let mut pos = selection.rectangle.start;
       +            for _ in 0..selection.rectangle.size.height {
       +                for _ in 0..selection.rectangle.size.width {
       +                    if self.cur_layer == self.buf.layers.len() as i32 - 1 {
       +                        self.set_char(pos, Some(AttributedChar::default()));
       +                    } else {
       +                        self.set_char(pos, None);
       +                    }
       +                    pos.x += 1;
       +                }
       +                pos.y += 1;
       +                pos.x = selection.rectangle.start.x;
       +            }
       +            self.end_atomic_undo();
       +            self.cur_selection = None;
       +        }
       +    }
       +
       +    pub fn clear_cur_layer(&mut self) {
       +        let b = Box::new(self.clear_layer(self.cur_layer));
       +        self.undo_stack.push(b);
            }
       -}
       -\ No newline at end of file
       +
       +    fn get_blockaction_rectangle(&self) -> (i32, i32, i32, i32) {
       +        if let Some(selection) = &self.cur_selection {
       +            let r = &selection.rectangle;
       +            (r.start.x, r.start.y, r.start.x + r.size.width as i32 - 1, r.start.y + r.size.height as i32 - 1)
       +        } else {
       +            (0, self.caret.get_position().y, self.buf.get_buffer_width() as i32 - 1, self.caret.get_position().y)
       +        }
       +    }
       +
       +    pub fn justify_left(&mut self) {
       +        self.begin_atomic_undo();
       +        let (x1, y1, x2, y2) = self.get_blockaction_rectangle();
       +        let is_bg_layer = self.cur_layer as usize == self.buf.layers.len() - 1;
       +        let layer = &mut self.buf.layers[self.cur_layer as usize];
       +        for y in y1..=y2 {
       +            let mut removed_chars = 0;
       +            let len = x2 - x1 + 1;
       +            while removed_chars < len {
       +                let ch = layer.get_char(Position::new(x1 + removed_chars, y));
       +                if let Some(ch) = ch {
       +                    if !ch.is_transparent() {
       +                        break;
       +                    }
       +                }
       +                removed_chars += 1;
       +            }
       +            if len == removed_chars { continue; }
       +            for x in x1..=x2 {
       +                let ch = if x + removed_chars <= x2 {
       +                    layer.get_char(Position::new(x + removed_chars, y))
       +                } else if is_bg_layer {
       +                    Some(AttributedChar::default())
       +                } else {
       +                    None
       +                };
       +
       +                let pos = Position::new(x, y);
       +                let old = layer.get_char(pos);
       +                layer.set_char(pos, ch);
       +                self.undo_stack.push(Box::new(UndoSetChar { pos, layer: self.cur_layer as usize, old, new: ch } ));
       +            }
       +        }
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn justify_center(&mut self) {
       +        self.begin_atomic_undo();
       +        self.justify_left();
       +
       +        let (x1, y1, x2, y2) = self.get_blockaction_rectangle();
       +        let is_bg_layer = self.cur_layer as usize == self.buf.layers.len() - 1;
       +        let layer = &mut self.buf.layers[self.cur_layer as usize];
       +        for y in y1..=y2 {
       +            let mut removed_chars = 0;
       +            let len = x2 - x1 + 1;
       +            while removed_chars < len {
       +                let ch = layer.get_char(Position::new(x2 - removed_chars, y));
       +                if let Some(ch) = ch {
       +                    if !ch.is_transparent() {
       +                        break;
       +                    }
       +                }
       +                removed_chars += 1;
       +            }
       +            
       +            if len == removed_chars { continue; }
       +            removed_chars /= 2;
       +            for x in 0..len {
       +                let ch = if x2 - x - removed_chars >= x1 {
       +                    layer.get_char(Position::new(x2 - x - removed_chars, y))
       +                } else if is_bg_layer {
       +                    Some(AttributedChar::default())
       +                } else {
       +                    None
       +                };
       +
       +                let pos = Position::new(x2 - x, y);
       +                let old = layer.get_char(pos);
       +                layer.set_char(pos, ch);
       +                self.undo_stack.push(Box::new(UndoSetChar { pos, layer: self.cur_layer as usize, old, new: ch } ));
       +            }
       +        }
       +        self.end_atomic_undo();
       +    }
       +    
       +    pub fn justify_right(&mut self) {
       +        self.begin_atomic_undo();
       +        let (x1, y1, x2, y2) = self.get_blockaction_rectangle();
       +        let is_bg_layer = self.cur_layer as usize == self.buf.layers.len() - 1;
       +        let layer = &mut self.buf.layers[self.cur_layer as usize];
       +        for y in y1..=y2 {
       +            let mut removed_chars = 0;
       +            let len = x2 - x1 + 1;
       +            while removed_chars < len {
       +                let ch = layer.get_char(Position::new(x2 - removed_chars, y));
       +                if let Some(ch) = ch {
       +                    if !ch.is_transparent() {
       +                        break;
       +                    }
       +                }
       +                removed_chars += 1;
       +            }
       +            
       +            if len == removed_chars { continue; }
       +            for x in 0..len {
       +                let ch = if x2 - x - removed_chars >= x1 {
       +                    layer.get_char(Position::new(x2 - x - removed_chars, y))
       +                } else if is_bg_layer {
       +                    Some(AttributedChar::default())
       +                } else {
       +                    None
       +                };
       +
       +                let pos = Position::new(x2 - x, y);
       +                let old = layer.get_char(pos);
       +                layer.set_char(pos, ch);
       +                self.undo_stack.push(Box::new(UndoSetChar { pos, layer: self.cur_layer as usize, old, new: ch } ));
       +            }
       +        }
       +        self.end_atomic_undo();
       +    }
       +
       +
       +    pub fn flip_x(&mut self) {
       +        self.begin_atomic_undo();
       +        let (x1, y1, x2, y2) = self.get_blockaction_rectangle();
       +        let layer = &mut self.buf.layers[self.cur_layer as usize];
       +        for y in y1..=y2 {
       +            for x in 0..=(x2 - x1) / 2 {
       +                let pos1 = Position::new(x1 + x, y);
       +                let pos2 = Position::new(x2 - x, y);
       +                layer.swap_char(pos1, pos2);
       +                self.undo_stack.push(Box::new(super::UndoSwapChar { layer: self.cur_layer as usize, pos1, pos2 } ));
       +            }
       +        }
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn flip_y(&mut self) {
       +        self.begin_atomic_undo();
       +        let (x1, y1, x2, y2) = self.get_blockaction_rectangle();
       +        let layer = &mut self.buf.layers[self.cur_layer as usize];
       +        for x in x1..=x2 {
       +            for y in 0..=(y2 - y1) / 2 {
       +                let pos1 = Position::new(x, y1 + y);
       +                let pos2 = Position::new(x, y2 - y);
       +                layer.swap_char(pos1, pos2);
       +                self.undo_stack.push(Box::new(super::UndoSwapChar { layer: self.cur_layer as usize, pos1, pos2 } ));
       +            }
       +        }
       +        self.end_atomic_undo();
       +    }
       +    pub fn crop(&mut self) {
       +        self.begin_atomic_undo();
       +        let (x1, y1, x2, y2) = self.get_blockaction_rectangle();
       +
       +        let new_height = y2 - y1;
       +        let new_width = x2 - x1;
       +
       +        if new_height == self.buf.get_real_buffer_height() && new_width == self.buf.get_buffer_width() {
       +            return;
       +        }
       +
       +        let mut new_layers = Vec::new();
       +        for l in 0..self.buf.layers.len() {
       +            let old_layer = &self.buf.layers[l];
       +            let mut new_layer = Layer {
       +                title: old_layer.title.clone(),
       +                is_visible: old_layer.is_visible,
       +                is_locked: false,
       +                is_position_locked: false,
       +                offset: Position::new(0, 0),
       +                lines: Vec::new(),
       +                ..Default::default()
       +            };
       +            for y in y1..=y2 {
       +                for x in x1..=x2 {
       +                    new_layer.set_char(Position::new(x - x1, y - y1), old_layer.get_char(Position::new(x, y)));
       +                }
       +            }
       +
       +            new_layer.is_locked = old_layer.is_locked;
       +            new_layer.is_position_locked = old_layer.is_position_locked;
       +            new_layers.push(new_layer);
       +        }
       +
       +        self.undo_stack.push(Box::new(super::UndoReplaceLayers { 
       +            old_layer: self.buf.layers.clone(), 
       +            new_layer: new_layers.clone(), 
       +            old_size: Size::new(self.buf.get_buffer_width(), self.buf.get_real_buffer_height()), 
       +            new_size: Size::new(new_width, new_height) 
       +        }));
       +
       +        self.buf.layers = new_layers;
       +        self.buf.set_buffer_width(new_width);
       +        self.buf.set_buffer_height(new_height);
       +        self.end_atomic_undo();
       +    }
       +    pub fn switch_fg_bg_color(&mut self) 
       +    {
       +        let mut attr = self.caret.get_attribute();
       +        let bg = attr.get_background();
       +        attr.set_background(attr.get_foreground());
       +        attr.set_foreground(bg);
       +        self.set_caret_attribute(attr);
       +    }
       +
       +    pub fn erase_line(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn erase_line_to_start(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn erase_line_to_end(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn erase_column(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn erase_column_to_start(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn erase_column_to_end(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn delete_row(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn insert_row(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +
       +    pub fn delete_column(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +    
       +    pub fn insert_column(&mut self) 
       +    {
       +        self.begin_atomic_undo();
       +        // TODO
       +        self.end_atomic_undo();
       +    }
       +}
       +
       +pub const DEFAULT_OUTLINE_TABLE: [[u8;10];15] = [
       +    [218, 191, 192, 217, 196, 179, 195, 180, 193, 194 ],
       +    [201, 187, 200, 188, 205, 186, 204, 185, 202, 203 ],
       +    [213, 184, 212, 190, 205, 179, 198, 181, 207, 209 ],
       +    [214, 183, 211, 189, 196, 186, 199, 182, 208, 210 ],
       +    [197, 206, 216, 215, 232, 233, 155, 156, 153, 239 ],
       +    [176, 177, 178, 219, 223, 220, 221, 222, 254, 250 ],
       +    [1, 2, 3, 4, 5, 6, 240, 127, 14, 15 ],
       +    [24, 25, 30, 31, 16, 17, 18, 29, 20, 21 ],
       +    [174, 175, 242, 243, 169, 170, 253, 246, 171, 172 ],
       +    [227, 241, 244, 245, 234, 157, 228, 248, 251, 252 ],
       +    [224, 225, 226, 229, 230, 231, 235, 236, 237, 238 ],
       +    [128, 135, 165, 164, 152, 159, 247, 249, 173, 168 ],
       +    [131, 132, 133, 160, 166, 134, 142, 143, 145, 146 ],
       +    [136, 137, 138, 130, 144, 140, 139, 141, 161, 158 ],
       +    [147, 148, 149, 162, 167, 150, 129, 151, 163, 154 ]
       +];
       +\ No newline at end of file
   DIR diff --git a/src/model/editor/undo_stack.rs b/src/model/editor/undo_stack.rs
       @@ -0,0 +1,97 @@
       +use icy_engine::AttributedChar;
       +
       +use super::{Buffer, Layer, Position, Size};
       +
       +pub trait UndoOperation : Send {
       +    fn undo(&self, buffer: &mut Buffer);
       +    fn redo(&self, buffer: &mut Buffer);
       +}
       +
       +pub struct UndoSetChar {
       +    pub pos: Position,
       +    pub layer: usize,
       +    pub old: Option<AttributedChar>,
       +    pub new: Option<AttributedChar>,
       +}
       +
       +impl UndoOperation for UndoSetChar {
       +    fn undo(&self, buffer: &mut Buffer) {
       +        buffer.set_char(self.layer, self.pos, self.old);
       +    }
       +
       +    fn redo(&self, buffer: &mut Buffer) {
       +        buffer.set_char(self.layer, self.pos, self.new);
       +    }
       +}
       +
       +pub struct AtomicUndo {
       +    pub stack: Vec<Box<dyn UndoOperation>>,
       +}
       +
       +impl UndoOperation for AtomicUndo {
       +    fn undo(&self, buffer: &mut Buffer) {
       +        for op in &self.stack {
       +            op.undo(buffer);
       +        }
       +    }
       +
       +    fn redo(&self, buffer: &mut Buffer) {
       +        for op in self.stack.iter().rev() {
       +            op.redo(buffer);
       +        }
       +    }
       +}
       +
       +pub struct UndoSwapChar {
       +    pub layer: usize,
       +    pub pos1: Position,
       +    pub pos2: Position,
       +}
       +impl UndoOperation for UndoSwapChar {
       +    fn undo(&self, buffer: &mut Buffer) {
       +        buffer.layers[self.layer as usize].swap_char(self.pos1, self.pos2);
       +    }
       +
       +    fn redo(&self, buffer: &mut Buffer) {
       +        buffer.layers[self.layer as usize].swap_char(self.pos1, self.pos2);
       +    }
       +}
       +
       +pub struct UndoReplaceLayers {
       +    pub old_layer: Vec<Layer>,
       +    pub new_layer: Vec<Layer>,
       +    pub old_size: Size<i32>,
       +    pub new_size: Size<i32>,
       +}
       +
       +impl UndoOperation for UndoReplaceLayers {
       +    fn undo(&self, buffer: &mut Buffer) {
       +        buffer.layers = self.old_layer.clone();
       +        buffer.set_buffer_width(self.old_size.width);
       +        buffer.set_buffer_height(self.old_size.height);
       +    }
       +
       +    fn redo(&self, buffer: &mut Buffer) {
       +        buffer.layers = self.new_layer.clone();
       +        buffer.set_buffer_width(self.new_size.width);
       +        buffer.set_buffer_height(self.new_size.height);
       +    }
       +}
       +
       +pub struct ClearLayerOperation {
       +    pub layer_num: i32,
       +    pub lines: Vec<super::Line>,
       +}
       +
       +impl UndoOperation for ClearLayerOperation {
       +    fn undo(&self, buffer: &mut Buffer) {
       +        buffer.layers[self.layer_num as usize].lines.clear();
       +        buffer.layers[self.layer_num as usize]
       +            .lines
       +            .extend(self.lines.clone());
       +    }
       +
       +    fn redo(&self, buffer: &mut Buffer) {
       +        buffer.layers[self.layer_num as usize].lines.clear();
       +    }
       +}
   DIR diff --git a/src/model/tool/icons.rs b/src/model/tool/icons.rs
       @@ -4,8 +4,6 @@ lazy_static::lazy_static! {
            pub static ref ADD_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("add.svg", include_bytes!("../../../data/icons/add.svg")).unwrap();
            pub static ref BRUSH_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("brush.svg", include_bytes!("../../../data/icons/brush.svg")).unwrap();
            pub static ref CURSOR_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("cursor.svg", include_bytes!("../../../data/icons/cursor.svg")).unwrap();
       -    pub static ref DELETE_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("delete.svg", include_bytes!("../../../data/icons/delete.svg")).unwrap();
       -    pub static ref DOWN_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("down.svg", include_bytes!("../../../data/icons/down.svg")).unwrap();
            pub static ref DROPPER_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("dropper.svg", include_bytes!("../../../data/icons/dropper.svg")).unwrap();
            pub static ref ELLIPSE_FILLED_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("ellipse_filled.svg", include_bytes!("../../../data/icons/ellipse_filled.svg")).unwrap();
            pub static ref ELLIPSE_OUTLINE_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("ellipse_outline.svg", include_bytes!("../../../data/icons/ellipse_outline.svg")).unwrap();
       @@ -16,7 +14,5 @@ lazy_static::lazy_static! {
            pub static ref MOVE_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("move.svg", include_bytes!("../../../data/icons/move.svg")).unwrap();
            pub static ref RECTANGLE_FILLED_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("rectangle_filled.svg", include_bytes!("../../../data/icons/rectangle_filled.svg")).unwrap();
            pub static ref RECTANGLE_OUTLINE_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("rectangle_outline.svg", include_bytes!("../../../data/icons/rectangle_outline.svg")).unwrap();
       -    pub static ref SWAP_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("swap.svg", include_bytes!("../../../data/icons/swap.svg")).unwrap();
       -    pub static ref UP_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("up.svg", include_bytes!("../../../data/icons/up.svg")).unwrap();
        }
        
   DIR diff --git a/src/ui/ansi_editor/buffer_view.rs b/src/ui/ansi_editor/buffer_view.rs
       @@ -2,9 +2,9 @@ use std::{cmp::{max, min},  sync::Arc};
        use eframe::{epaint::{Vec2}};
        use glow::NativeTexture;
        
       -use icy_engine::{Buffer, BufferParser, CallbackAction, Caret, EngineResult, Position, Selection};
       +use icy_engine::{Buffer, BufferParser, CallbackAction, EngineResult, Position, Selection};
        
       -use crate::{ui::ansi_editor::{create_buffer_texture, create_palette_texture, create_font_texture}};
       +use crate::{ui::ansi_editor::{create_buffer_texture, create_palette_texture, create_font_texture}, model::Editor};
        
        use super::SixelCacheEntry;
        
       @@ -39,9 +39,8 @@ impl Blink {
        }
        
        pub struct BufferView {
       -    pub buf: Buffer,
       +    pub editor: Editor,
            pub sixel_cache: Vec<SixelCacheEntry>,
       -    pub caret: Caret,
        
            pub caret_blink: Blink,
            pub character_blink: Blink,
       @@ -442,8 +441,7 @@ void main() {
                    gl.bind_framebuffer(glow::FRAMEBUFFER, None);
        
                    Self {
       -                buf,
       -                caret: Caret::default(),
       +                editor: Editor::new(0, buf),
                        caret_blink: Blink::new((1000.0 / 1.875) as u128 / 2),
                        character_blink: Blink::new((1000.0 / 1.8) as u128),
                        scale: 1.0,
       @@ -476,7 +474,7 @@ void main() {
            }
        
            pub fn clear(&mut self) {
       -        self.caret.ff(&mut self.buf);
       +        self.editor.caret.ff(&mut self.editor.buf);
            }
        
            pub fn get_copy_text(&mut self, buffer_parser: &Box<dyn BufferParser>) -> Option<String> {
       @@ -496,7 +494,7 @@ void main() {
                    );
                    for y in start.y..=end.y {
                        for x in start.x..end.x {
       -                    let ch = self.buf.get_char(Position::new(x, y)).unwrap();
       +                    let ch = self.editor.get_char(Position::new(x, y)).unwrap();
                            res.push(buffer_parser.to_unicode(ch.ch));
                        }
                        res.push('\n');
       @@ -508,25 +506,25 @@ void main() {
                        (selection.lead_pos, selection.anchor_pos)
                    };
                    if start.y != end.y {
       -                for x in start.x..self.buf.get_line_length(start.y) {
       -                    let ch = self.buf.get_char(Position::new(x, start.y)).unwrap();
       +                for x in start.x..self.editor.buf.get_line_length(start.y) {
       +                    let ch = self.editor.get_char(Position::new(x, start.y)).unwrap();
                            res.push(buffer_parser.to_unicode(ch.ch));
                        }
                        res.push('\n');
                        for y in start.y + 1..end.y {
       -                    for x in 0..self.buf.get_line_length(y) {
       -                        let ch = self.buf.get_char(Position::new(x, y)).unwrap();
       +                    for x in 0..self.editor.buf.get_line_length(y) {
       +                        let ch = self.editor.get_char(Position::new(x, y)).unwrap();
                                res.push(buffer_parser.to_unicode(ch.ch));
                            }
                            res.push('\n');
                        }
                        for x in 0..=end.x {
       -                    let ch = self.buf.get_char(Position::new(x, end.y)).unwrap();
       +                    let ch = self.editor.get_char(Position::new(x, end.y)).unwrap();
                            res.push(buffer_parser.to_unicode(ch.ch));
                        }
                    } else {
                        for x in start.x..=end.x {
       -                    let ch = self.buf.get_char(Position::new(x, start.y)).unwrap();
       +                    let ch = self.editor.get_char(Position::new(x, start.y)).unwrap();
                            res.push(buffer_parser.to_unicode(ch.ch));
                        }
                    }
       @@ -552,7 +550,7 @@ void main() {
                parser: &mut Box<dyn BufferParser>,
                c: char,
            ) -> EngineResult<CallbackAction> {
       -        let res = parser.print_char(&mut self.buf, &mut self.caret, c);
       +        let res = parser.print_char(&mut self.editor.buf, &mut self.editor.caret, c);
                self.redraw_view();
                res
            }
   DIR diff --git a/src/ui/ansi_editor/mod.rs b/src/ui/ansi_editor/mod.rs
       @@ -59,7 +59,7 @@ impl AnsiEditor {
        
        impl Document for AnsiEditor {
            fn get_title(&self) -> String {
       -        if let Some(file_name) = &self.buffer_view.lock().unwrap().buf.file_name {
       +        if let Some(file_name) = &self.buffer_view.lock().unwrap().editor.buf.file_name {
                    file_name.file_name().unwrap().to_str().unwrap().to_string()
                } else {
                    "Untitled".to_string()
       @@ -73,7 +73,7 @@ impl Document for AnsiEditor {
            fn save(&mut self, file_name: &str) -> TerminalResult<()> {
                let file =  PathBuf::from(file_name);
                let options = SaveOptions::new();
       -        let bytes = self.buffer_view.lock().unwrap().buf.to_bytes(file.extension().unwrap().to_str().unwrap(), &options)?;
       +        let bytes = self.buffer_view.lock().unwrap().editor.buf.to_bytes(file.extension().unwrap().to_str().unwrap(), &options)?;
                fs::write(file_name, bytes)?;
                self.is_dirty = false;
                Ok(())
       @@ -81,10 +81,10 @@ impl Document for AnsiEditor {
        
            fn show_ui(&mut self, ui: &mut eframe::egui::Ui) {
                let size = ui.max_rect().size();
       -        let buf_w = self.buffer_view.lock().unwrap().buf.get_buffer_width();
       -        let buf_h = self.buffer_view.lock().unwrap().buf.get_buffer_height();
       +        let buf_w = self.buffer_view.lock().unwrap().editor.buf.get_buffer_width();
       +        let buf_h = self.buffer_view.lock().unwrap().editor.buf.get_buffer_height();
                // let h = max(buf_h, buffer_view.lock().unwrap().buf.get_real_buffer_height());
       -        let font_dimensions = self.buffer_view.lock().unwrap().buf.get_font_dimensions();
       +        let font_dimensions = self.buffer_view.lock().unwrap().editor.buf.get_font_dimensions();
        
                let mut scale_x = (size.x - 4.0) / font_dimensions.width as f32 / buf_w as f32;
                let mut scale_y = size.y / font_dimensions.height as f32 / buf_h as f32;
       @@ -122,7 +122,7 @@ impl Document for AnsiEditor {
                                .ceil(),
                            Vec2::new(rect_w, rect_h),
                        );
       -                let real_height = self.buffer_view.lock().unwrap().buf.get_real_buffer_height();
       +                let real_height = self.buffer_view.lock().unwrap().editor.buf.get_real_buffer_height();
                        let max_lines = max(0, real_height - buf_h);
                        ui.set_height(scale_y * max_lines as f32 * font_dimensions.height as f32);
        
   DIR diff --git a/src/ui/ansi_editor/render.rs b/src/ui/ansi_editor/render.rs
       @@ -42,18 +42,18 @@ impl BufferView {
                        0.0,
                    );
        
       -            let sbl = (self.buf.get_first_visible_line() - self.scroll_back_line) as f32;
       +            let sbl = (self.editor.buf.get_first_visible_line() - self.scroll_back_line) as f32;
                    gl.uniform_4_f32(
                        gl.get_uniform_location(self.program, "u_caret_position")
                            .as_ref(),
       -                self.caret.get_position().x as f32,
       -                self.caret.get_position().y as f32 - sbl,
       -                if self.caret_blink.is_on() && self.caret.is_visible {
       +                self.editor.caret.get_position().x as f32,
       +                self.editor.caret.get_position().y as f32 - sbl,
       +                if self.caret_blink.is_on() && self.editor.caret.is_visible {
                            1.0
                        } else {
                            0.0
                        },
       -                if self.caret.insert_mode { 1.0 } else { 0.0 }, // shape
       +                if self.editor.caret.insert_mode { 1.0 } else { 0.0 }, // shape
                    );
        
                    gl.uniform_1_f32(
       @@ -68,8 +68,8 @@ impl BufferView {
                    gl.uniform_2_f32(
                        gl.get_uniform_location(self.program, "u_terminal_size")
                            .as_ref(),
       -                self.buf.get_buffer_width() as f32 - 0.0001,
       -                self.buf.get_buffer_height() as f32 - 0.0001,
       +                self.editor.buf.get_buffer_width() as f32 - 0.0001,
       +                self.editor.buf.get_buffer_height() as f32 - 0.0001,
                    );
        
                    gl.uniform_1_i32(gl.get_uniform_location(self.program, "u_fonts").as_ref(), 0);
       @@ -211,8 +211,8 @@ impl BufferView {
                                self.render_buffer_size.y,
                            );
        
       -                    let x = sixel.pos.x as f32 * self.buf.get_font_dimensions().width as f32;
       -                    let y = sixel.pos.y as f32 * self.buf.get_font_dimensions().height as f32;
       +                    let x = sixel.pos.x as f32 * self.editor.buf.get_font_dimensions().width as f32;
       +                    let y = sixel.pos.y as f32 * self.editor.buf.get_font_dimensions().height as f32;
        
                            let w = sixel.size.width as f32;
                            let h = sixel.size.height as f32;
       @@ -297,24 +297,24 @@ impl BufferView {
        
                if self.redraw_view {
                    self.redraw_view = false;
       -            create_buffer_texture(gl, &self.buf, self.scroll_back_line, self.buffer_texture);
       +            create_buffer_texture(gl, &self.editor.buf, self.scroll_back_line, self.buffer_texture);
                }
        
       -        if self.redraw_palette || self.colors != self.buf.palette.colors.len() {
       +        if self.redraw_palette || self.colors != self.editor.buf.palette.colors.len() {
                    self.redraw_palette = false;
       -            create_palette_texture(gl, &self.buf, self.palette_texture);
       -            self.colors = self.buf.palette.colors.len();
       +            create_palette_texture(gl, &self.editor.buf, self.palette_texture);
       +            self.colors = self.editor.buf.palette.colors.len();
                }
        
       -        if self.redraw_font || self.fonts != self.buf.font_table.len() {
       +        if self.redraw_font || self.fonts != self.editor.buf.font_table.len() {
                    self.redraw_font = false;
       -            create_font_texture(gl, &self.buf, self.palette_texture);
       -            self.fonts = self.buf.font_table.len();
       +            create_font_texture(gl, &self.editor.buf, self.palette_texture);
       +            self.fonts = self.editor.buf.font_table.len();
                }
       -        let buf = &self.buf;
       +        let editor = &self.editor;
                let render_buffer_size = Vec2::new(
       -            buf.get_font_dimensions().width as f32 * buf.get_buffer_width() as f32,
       -            buf.get_font_dimensions().height as f32 * buf.get_buffer_height() as f32,
       +            editor.buf.get_font_dimensions().width as f32 * editor.buf.get_buffer_width() as f32,
       +            editor.buf.get_font_dimensions().height as f32 * editor.buf.get_buffer_height() as f32,
                );
        
                if render_buffer_size != self.render_buffer_size {
   DIR diff --git a/src/ui/ansi_editor/sixel.rs b/src/ui/ansi_editor/sixel.rs
       @@ -27,8 +27,8 @@ impl SixelCacheEntry {
        
        impl BufferView {
            pub fn update_sixels(&mut self, gl: &Arc<glow::Context>) -> bool {
       -        let buffer = &self.buf;
       -        let l = buffer.layers[0].sixels.len();
       +        let buf = &self.editor.buf;
       +        let l = buf.layers[0].sixels.len();
                if l == 0 {
                    for sx in &self.sixel_cache {
                        if let Some(tex) = sx.texture_opt {
       @@ -43,7 +43,7 @@ impl BufferView {
                let mut res = false;
                let mut i = 0;
                while i < l {
       -            let sixel = &buffer.layers[0].sixels[i];
       +            let sixel = &buf.layers[0].sixels[i];
        
                    if sixel.width() == 0 || sixel.height() == 0 {
                        i += 1;
       @@ -205,7 +205,7 @@ impl BufferView {
                                }
                            }
        
       -                    self.buf.layers[0].sixels.remove(i);
       +                    self.editor.buf.layers[0].sixels.remove(i);
                        }
                        if i == 0 {
                            break;
   DIR diff --git a/src/ui/icons.rs b/src/ui/icons.rs
       @@ -0,0 +1,6 @@
       +use egui_extras::RetainedImage;
       +
       +lazy_static::lazy_static! {
       +    pub static ref SWAP_SVG: RetainedImage = egui_extras::RetainedImage::from_svg_bytes("swap.svg", include_bytes!("../../data/icons/swap.svg")).unwrap();
       +}
       +
   DIR diff --git a/src/ui/main_window.rs b/src/ui/main_window.rs
       @@ -175,6 +175,9 @@ impl eframe::App for MainWindow {
                    if let Some((_, t)) = self.tree.find_active_focused() {
                        buffer_opt = t.1.get_buffer_view();
                    }
       +            ui.vertical_centered(|ui| {
       +                ui.add(crate::palette_switcher(ctx, buffer_opt.clone()));
       +            });
                    ui.add(crate::palette_editor_16(buffer_opt));
                    crate::add_tool_switcher(ctx, ui, self);
                });
   DIR diff --git a/src/ui/mod.rs b/src/ui/mod.rs
       @@ -15,5 +15,7 @@ pub use palette_editor::*;
        
        mod tool_switcher;
        pub use tool_switcher::*;
       +mod icons;
       +pub use icons::*;
        
        pub type TerminalResult<T> = Result<T, Box<dyn Error>>;
   DIR diff --git a/src/ui/palette_editor.rs b/src/ui/palette_editor.rs
       @@ -1,8 +1,118 @@
       +use std::cmp::min;
        use std::sync::{Arc, Mutex};
        use eframe::epaint::{Vec2, Rect, Pos2, Rounding, Color32, Stroke};
        use eframe::egui::{self, Sense};
       +use crate::SWAP_SVG;
       +
        use super::ansi_editor::BufferView;
        
       +pub fn palette_switcher(ctx: &egui::Context, buffer_opt: Option<Arc<Mutex<BufferView>>>) -> impl egui::Widget {
       +    let tex_id = SWAP_SVG.texture_id(ctx);
       +    move |ui: &mut egui::Ui| {
       +        let height  = 42.0;
       +        let (id, rect) = ui.allocate_space(Vec2::new(height, height));
       +        let mut response = ui.interact(rect, id, Sense::click());
       +        let painter = ui.painter_at(rect);
       +
       +        let rect_height = height * 0.618;
       +        if let Some(buffer_view) = buffer_opt {
       +            let caret_attr = &buffer_view.lock().unwrap().editor.caret.get_attribute();
       +            let palette = buffer_view.lock().unwrap().editor.buf.palette.clone();
       +
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(height - rect_height, height - rect_height) + rect.left_top().to_vec2(),
       +                Vec2::new(rect_height, rect_height)
       +            ), Rounding::none(), Color32::BLACK);
       +
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(height - rect_height + 1., height - rect_height + 1.) + rect.left_top().to_vec2(),
       +                Vec2::new(rect_height - 2., rect_height - 2.)
       +            ), Rounding::none(), Color32::WHITE);
       +
       +            let (r, g, b) = palette.colors[caret_attr.get_background() as usize].get_rgb();
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(height - rect_height + 2., height - rect_height + 2.) + rect.left_top().to_vec2(),
       +                Vec2::new(rect_height - 4., rect_height - 4.)
       +            ), Rounding::none(), Color32::from_rgb(r, g, b));
       +
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(0., 0.) + rect.left_top().to_vec2(),
       +                Vec2::new(rect_height, rect_height)
       +            ), Rounding::none(), Color32::BLACK);
       +
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(1., 1.) + rect.left_top().to_vec2(),
       +                Vec2::new(rect_height - 2., rect_height - 2.)
       +            ), Rounding::none(), Color32::WHITE);
       +
       +            let (r, g, b) = palette.colors[caret_attr.get_foreground() as usize].get_rgb();
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(2., 2.) + rect.left_top().to_vec2(),
       +                Vec2::new(rect_height - 4., rect_height - 4.)
       +            ), Rounding::none(), Color32::from_rgb(r, g, b));
       +
       +            let s_rect_height = height * 0.382;
       +            let rh = s_rect_height / 1.8;
       +            let (r, g, b) = palette.colors[7].get_rgb();
       +
       +            let overlap = 2.0;
       +
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(rh - overlap,  height - rh - overlap) + rect.left_top().to_vec2(),
       +                Vec2::new(rh, rh)
       +            ), Rounding::none(), Color32::from_rgb(r ^ 0xFF, g ^ 0xFF, b ^ 0xFF));
       +
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(rh - overlap + 1.,  height - rh - overlap + 1.) + rect.left_top().to_vec2(),
       +                Vec2::new(rh - 2., rh - 2.)
       +            ), Rounding::none(), Color32::from_rgb(r, g, b));
       +
       +            let (r, g, b) = palette.colors[0].get_rgb();
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new( overlap,  height - 2. * rh + 2. + overlap) + rect.left_top().to_vec2(),
       +                Vec2::new(rh, rh)
       +            ), Rounding::none(), Color32::from_rgb(r ^ 0xFF, g ^ 0xFF, b ^ 0xFF));
       +
       +            painter.rect_filled(Rect::from_min_size(
       +                Pos2::new(1. + overlap,  height - 2. * rh + 3. + overlap) + rect.left_top().to_vec2(),
       +                Vec2::new(rh - 2., rh - 2.)
       +            ), Rounding::none(), Color32::from_rgb(r, g, b));
       +
       +            painter.image(
       +                tex_id, 
       +                Rect::from_min_size(
       +                    Pos2::new(rect_height + 1., 0.) + rect.left_top().to_vec2(), 
       +                    Vec2::new(s_rect_height, s_rect_height)), 
       +                Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(1.0, 1.0)), 
       +                Color32::WHITE);
       +
       +            if let Some(hp) = response.hover_pos() {
       +                let pos = hp.to_vec2() - rect.left_top().to_vec2();
       +
       +                if response.clicked() {
       +                    if pos.x > rect_height && pos.y < rect_height {
       +                        let caret = &mut buffer_view.lock().unwrap().editor.caret;
       +                        let fg = caret.get_attribute().get_foreground();
       +                        let bg = caret.get_attribute().get_background();
       +                        caret.set_foreground(bg);
       +                        caret.set_background(fg);
       +                        response.mark_changed();
       +                    }
       +
       +                    if pos.x < rect_height && pos.y > rect_height {
       +                        let caret = &mut buffer_view.lock().unwrap().editor.caret;
       +                        caret.set_foreground(7);
       +                        caret.set_background(0);
       +                        response.mark_changed();
       +                    }
       +                }
       +            }
       +        }
       +        response
       +    }
       +}
       +
       +
        pub fn palette_editor_16(buffer_opt: Option<Arc<Mutex<BufferView>>>) -> impl egui::Widget {
            move |ui: &mut egui::Ui| {
                let height  = ui.available_width() / 8.0;
       @@ -13,8 +123,8 @@ pub fn palette_editor_16(buffer_opt: Option<Arc<Mutex<BufferView>>>) -> impl egu
                let painter = ui.painter_at(stroke_rect);
        
                if let Some(buffer_view) = buffer_opt {
       -            let caret_attr = &buffer_view.lock().unwrap().caret.get_attribute();
       -            let palette = buffer_view.lock().unwrap().buf.palette.clone();
       +            let caret_attr = &buffer_view.lock().unwrap().editor.caret.get_attribute();
       +            let palette = buffer_view.lock().unwrap().editor.buf.palette.clone();
        
                    for i in 0..16 {
                        let (r, g, b) = palette.colors[i].get_rgb();
       @@ -41,7 +151,6 @@ pub fn palette_editor_16(buffer_opt: Option<Arc<Mutex<BufferView>>>) -> impl egu
                    painter.line_segment([origin + Vec2::new(marker_len, 0.), origin + Vec2::new(0., marker_len)], stroke);
        
                    // paint bg marker
       -            println!("{}", caret_attr.get_background());
                    let stroke = Stroke::new(1., Color32::WHITE);
                    let origin = Pos2::new(
                        stroke_rect.left() + (1 + caret_attr.get_background() % 8) as f32 * height,  
       @@ -56,14 +165,14 @@ pub fn palette_editor_16(buffer_opt: Option<Arc<Mutex<BufferView>>>) -> impl egu
                    painter.line_segment([origin, origin - Vec2::new(0., marker_len)], stroke);
                    painter.line_segment([origin - Vec2::new(marker_len, 0.), origin - Vec2::new(0., marker_len)], stroke);
                    if let Some(hp) = response.hover_pos() {
       -                let pos = hp.to_vec2() / Vec2::new(height, height);
       -                let color = pos.x as u32 + (pos.y - 1.) as u32 * 8;
       +                let pos = (hp.to_vec2() - stroke_rect.left_top().to_vec2()) / Vec2::new(height, height);
       +                let color = min(palette.len() - 1,  pos.x as u32 + pos.y as u32 * 8);
                        if response.clicked() {
       -                    buffer_view.lock().unwrap().caret.set_foreground(color);
       +                    buffer_view.lock().unwrap().editor.caret.set_foreground(color);
                            response.mark_changed();
                        }
                        if response.secondary_clicked() {
       -                    buffer_view.lock().unwrap().caret.set_background(color);
       +                    buffer_view.lock().unwrap().editor.caret.set_background(color);
                            response.mark_changed();
                        }
                    }