diff --git a/src/ui/panes/pid_drawing_tool.rs b/src/ui/panes/pid_drawing_tool.rs
index 34b989c795017af302c9eb76b8ef7af87fd680cc..1dcf51d99fcb73a3648e78af0eb35fcb6a7a67f8 100644
--- a/src/ui/panes/pid_drawing_tool.rs
+++ b/src/ui/panes/pid_drawing_tool.rs
@@ -1,13 +1,16 @@
 mod connections;
 mod elements;
+mod grid;
 mod pos;
 mod symbols;
 
 use connections::Connection;
 use egui::{
-    epaint::PathStroke, Color32, Context, CursorIcon, PointerButton, Pos2, Sense, Theme, Ui, Vec2,
+    epaint::PathStroke, Color32, Context, CursorIcon, PointerButton, Pos2, Rounding, Sense, Stroke,
+    Theme, Ui, Vec2,
 };
 use elements::Element;
+use grid::GridInfo;
 use pos::Pos;
 use serde::{Deserialize, Serialize};
 use std::f32::consts::PI;
@@ -21,8 +24,9 @@ use super::PaneBehavior;
 #[derive(Clone, Serialize, Deserialize, PartialEq)]
 enum Action {
     Connect(usize),
-    ContextMenu(Pos),
-    Drag(usize),
+    ContextMenu(Pos2),
+    DragElement(usize),
+    DragConnection(usize, usize),
 }
 
 /// Piping and instrumentation diagram
@@ -31,7 +35,7 @@ pub struct PidPane {
     elements: Vec<Element>,
     connections: Vec<Connection>,
 
-    grid_size: f32,
+    grid: GridInfo,
 
     #[serde(skip)]
     action: Option<Action>,
@@ -42,7 +46,7 @@ impl Default for PidPane {
         Self {
             elements: Vec::default(),
             connections: Vec::default(),
-            grid_size: 10.0,
+            grid: GridInfo { size: 10.0 },
 
             action: None,
         }
@@ -58,11 +62,13 @@ impl PaneBehavior for PidPane {
 
         // Allocate the space to sense inputs
         let (_, response) = ui.allocate_at_least(ui.max_rect().size(), Sense::click_and_drag());
-        let pointer_pos = response.hover_pos().map(|pos| self.screen_to_grid_pos(pos));
+        let pointer_pos = response.hover_pos();
 
         // Set grab icon when hovering an element
         if let Some(pointer_pos) = &pointer_pos {
-            if self.is_hovering_element(pointer_pos) {
+            if self.is_hovering_element(pointer_pos)
+                || self.is_hovering_connection_point(pointer_pos)
+            {
                 ui.ctx()
                     .output_mut(|output| output.cursor_icon = CursorIcon::Grab);
             }
@@ -74,10 +80,21 @@ impl PaneBehavior for PidPane {
                 println!("Context menu opened");
                 self.action = Some(Action::ContextMenu(pointer_pos.clone()));
             } else if response.drag_started() {
-                println!("Drag started");
-                self.action = self
+                println!("Checking drag start at {:?}", pointer_pos);
+                if let Some(drag_connection_point) = self
+                    .find_hovered_connection_point(pointer_pos)
+                    .map(|(idx1, idx2)| Action::DragConnection(idx1, idx2))
+                {
+                    self.action = Some(drag_connection_point);
+                    println!("Connection point drag started");
+                }
+                if let Some(drag_element_action) = self
                     .find_hovered_element_idx(pointer_pos)
-                    .map(|idx| Action::Drag(idx));
+                    .map(|idx| Action::DragElement(idx))
+                {
+                    self.action = Some(drag_element_action);
+                    println!("Element drag started");
+                }
             } else if response.drag_stopped() {
                 self.action.take();
                 println!("Drag stopped");
@@ -96,7 +113,7 @@ impl PaneBehavior for PidPane {
                     if let Some(end) = self.find_hovered_element_idx(&pointer_pos) {
                         if response.clicked() {
                             if start != end {
-                                self.connections.push(Connection { start, end });
+                                self.connections.push(Connection::new(start, end));
                                 println!("Added connection from {} to {}", start, end);
                             }
                             self.action.take();
@@ -104,10 +121,12 @@ impl PaneBehavior for PidPane {
                         }
                     }
                 }
-                Some(Action::Drag(idx)) => {
-                    let element = &mut self.elements[idx];
-                    element.position.x = pointer_pos.x - element.size / 2;
-                    element.position.y = pointer_pos.y - element.size / 2;
+                Some(Action::DragElement(idx)) => {
+                    self.elements[idx].position = Pos::from_pos2(&self.grid, &pointer_pos)
+                }
+                Some(Action::DragConnection(conn_idx, midpoint_idx)) => {
+                    self.connections[conn_idx].middle_points[midpoint_idx] =
+                        Pos::from_pos2(&self.grid, &pointer_pos);
                 }
                 _ => {}
             }
@@ -122,10 +141,22 @@ impl PaneBehavior for PidPane {
 }
 
 impl PidPane {
-    fn is_hovering_element(&self, pointer_pos: &Pos) -> bool {
+    fn is_hovering_element(&self, pointer_pos: &Pos2) -> bool {
         self.elements
             .iter()
-            .find(|element| element.contains(pointer_pos))
+            .find(|element| element.contains(&self.grid, pointer_pos))
+            .is_some()
+    }
+
+    fn is_hovering_connection_point(&self, pointer_pos: &Pos2) -> bool {
+        self.connections
+            .iter()
+            .find(|conn| {
+                conn.middle_points
+                    .iter()
+                    .find(|p| p.distance(&self.grid, pointer_pos) < 10.0)
+                    .is_some()
+            })
             .is_some()
     }
 
@@ -151,21 +182,37 @@ impl PidPane {
         }
     }
 
-    fn screen_to_grid_pos(&self, screen_pos: Pos2) -> Pos {
-        Pos {
-            x: (screen_pos.x / self.grid_size) as i32,
-            y: (screen_pos.y / self.grid_size) as i32,
-        }
-    }
-
-    fn find_hovered_element_idx(&self, pos: &Pos) -> Option<usize> {
-        self.elements.iter().position(|elem| elem.contains(&pos))
+    fn find_hovered_element_idx(&self, pos: &Pos2) -> Option<usize> {
+        self.elements
+            .iter()
+            .position(|elem| elem.contains(&self.grid, pos))
     }
 
-    fn find_hovered_element_mut(&mut self, pos: &Pos) -> Option<&mut Element> {
+    fn find_hovered_element_mut(&mut self, pos: &Pos2) -> Option<&mut Element> {
         self.elements
             .iter_mut()
-            .find(|element| element.contains(&pos))
+            .find(|element| element.contains(&self.grid, pos))
+    }
+
+    /// Return the connection and segment indexes where the position is on, if any
+    fn find_hovered_connection_idx(&self, pos: &Pos2) -> Option<(usize, usize)> {
+        self.connections
+            .iter()
+            .enumerate()
+            .find_map(|(idx, conn)| Some(idx).zip(conn.contains(&self, pos)))
+    }
+
+    fn find_hovered_connection_point(&self, pos: &Pos2) -> Option<(usize, usize)> {
+        let mut midpoint_idx = Some(0);
+        let connection_idx = self.connections.iter().position(|conn| {
+            midpoint_idx = conn
+                .middle_points
+                .iter()
+                .position(|p| p.distance(&self.grid, pos) < 12.0);
+            midpoint_idx.is_some()
+        });
+
+        connection_idx.zip(midpoint_idx)
     }
 
     fn draw_grid(&self, theme: Theme, ui: &Ui) {
@@ -174,10 +221,10 @@ impl PidPane {
         let dot_color = PidPane::dots_color(theme);
 
         for x in (window_rect.min.x as i32..window_rect.max.x.round() as i32)
-            .step_by(self.grid_size as usize)
+            .step_by(self.grid.size as usize)
         {
             for y in (window_rect.min.y as i32..window_rect.max.y.round() as i32)
-                .step_by(self.grid_size as usize)
+                .step_by(self.grid.size as usize)
             {
                 let rect = egui::Rect::from_min_size(
                     egui::Pos2::new(x as f32, y as f32),
@@ -192,31 +239,55 @@ impl PidPane {
         let painter = ui.painter();
 
         for connection in &self.connections {
-            let elem1 = &self.elements[connection.start];
-            let elem2 = &self.elements[connection.end];
-
-            let x1 = (elem1.position.x + elem1.size / 2) as f32 * self.grid_size;
-            let y1 = (elem1.position.y + elem1.size / 2) as f32 * self.grid_size;
-            let x2 = (elem2.position.x + elem2.size / 2) as f32 * self.grid_size;
-            let y2 = (elem2.position.y + elem2.size / 2) as f32 * self.grid_size;
+            let mut points = Vec::new();
+
+            // Append start point
+            let start = &self.elements[connection.start];
+            points.push(start.position.into_pos2(&self.grid));
+
+            // Append all midpoints
+            connection
+                .middle_points
+                .iter()
+                .map(|p| p.into_pos2(&self.grid))
+                .for_each(|p| points.push(p));
+
+            // Append end point
+            let end = &self.elements[connection.end];
+            points.push(end.position.into_pos2(&self.grid));
+
+            // Draw line segments
+            for i in 0..(points.len() - 1) {
+                let a = points[i];
+                let b = points[i + 1];
+                painter.line_segment([a, b], PathStroke::new(1.0, Color32::GREEN));
+            }
 
-            painter.line_segment(
-                [Pos2::new(x1, y1), Pos2::new(x2, y2)],
-                PathStroke::new(1.0, Color32::GREEN),
-            );
+            // Draw dragging boxes
+            for middle_point in &connection.middle_points {
+                painter.rect(
+                    egui::Rect::from_center_size(
+                        middle_point.into_pos2(&self.grid),
+                        Vec2::new(self.grid.size, self.grid.size),
+                    ),
+                    Rounding::ZERO,
+                    Color32::DARK_GRAY,
+                    Stroke::NONE,
+                );
+            }
         }
     }
 
     fn draw_elements(&self, theme: Theme, ui: &Ui) {
         for element in &self.elements {
-            let image_rect = egui::Rect::from_min_size(
+            let image_rect = egui::Rect::from_center_size(
                 egui::Pos2::new(
-                    element.position.x as f32 * self.grid_size,
-                    element.position.y as f32 * self.grid_size,
+                    element.position.x as f32 * self.grid.size,
+                    element.position.y as f32 * self.grid.size,
                 ),
                 egui::Vec2::new(
-                    element.size as f32 * self.grid_size,
-                    element.size as f32 * self.grid_size,
+                    element.size as f32 * self.grid.size,
+                    element.size as f32 * self.grid.size,
                 ),
             );
 
@@ -226,7 +297,7 @@ impl PidPane {
         }
     }
 
-    fn draw_context_menu(&mut self, pointer_pos: &Pos, ui: &mut Ui) {
+    fn draw_context_menu(&mut self, pointer_pos: &Pos2, ui: &mut Ui) {
         ui.set_max_width(100.0); // To make sure we wrap long text
 
         if self.is_hovering_element(&pointer_pos) {
@@ -264,11 +335,20 @@ impl PidPane {
                 }
                 ui.close_menu();
             }
+        } else if let Some((conn_idx, segm_idx)) = self.find_hovered_connection_idx(&pointer_pos) {
+            if ui.button("Split").clicked() {
+                println!("Splitting connection line");
+                self.connections[conn_idx].split(segm_idx, Pos::from_pos2(&self.grid, pointer_pos));
+                ui.close_menu();
+            }
         } else {
             ui.menu_button("Symbols", |ui| {
                 for symbol in Symbol::iter() {
                     if ui.button(symbol.to_string()).clicked() {
-                        self.elements.push(Element::new(pointer_pos, symbol));
+                        self.elements.push(Element::new(
+                            Pos::from_pos2(&self.grid, &pointer_pos),
+                            symbol,
+                        ));
                         ui.close_menu();
                     }
                 }
diff --git a/src/ui/panes/pid_drawing_tool/connections.rs b/src/ui/panes/pid_drawing_tool/connections.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e1c9a3b9f3fa1e72c3e74e5003f9de828b794113
--- /dev/null
+++ b/src/ui/panes/pid_drawing_tool/connections.rs
@@ -0,0 +1,102 @@
+use egui::Pos2;
+use serde::{Deserialize, Serialize};
+
+use super::{grid::LINE_DISTANCE_THRESHOLD, pos::Pos, PidPane};
+
+#[derive(Clone, Serialize, Deserialize, PartialEq)]
+pub struct Connection {
+    /// Index of the start element
+    pub start: usize,
+
+    /// Index of the end element
+    pub end: usize,
+
+    /// Coordinates of middle points
+    pub middle_points: Vec<Pos>,
+}
+
+impl Connection {
+    pub fn new(start: usize, end: usize) -> Self {
+        Self {
+            start,
+            end,
+            middle_points: Vec::new(),
+        }
+    }
+
+    /// Return the index of the segment the position is on, if any
+    pub fn contains(&self, pid: &PidPane, pos: &Pos2) -> Option<usize> {
+        let mut points = Vec::new();
+
+        // Append start point
+        let start = &pid.elements[self.start];
+        points.push(start.position.into_pos2(&pid.grid));
+
+        // Append all midpoints
+        self.middle_points
+            .iter()
+            .map(|p| p.into_pos2(&pid.grid))
+            .for_each(|p| points.push(p));
+
+        // Append end point
+        let end = &pid.elements[self.end];
+        points.push(end.position.into_pos2(&pid.grid));
+
+        // Check each segment
+        for i in 0..(points.len() - 1) {
+            let a = points[i];
+            let b = points[i + 1];
+            if is_hovering_segment(pos, &a, &b) {
+                return Some(i);
+            }
+        }
+
+        None
+    }
+
+    pub fn split(&mut self, idx: usize, pos: Pos) {
+        self.middle_points.insert(idx, pos.clone());
+    }
+}
+
+fn distance(a: &Pos2, b: &Pos2) -> f32 {
+    ((a.x - b.x).powi(2) + (a.y - b.y).powi(2)).sqrt()
+}
+
+/// Distance of a from the line defined by b and c
+fn distance_from_line(p: &Pos2, m: f32, q: f32) -> f32 {
+    (p.y - m * p.x - q).abs() / (1.0 + m * m).sqrt()
+}
+
+/// True if p hovers the segment defined by a and b
+fn is_hovering_segment(p: &Pos2, a: &Pos2, b: &Pos2) -> bool {
+    if a != b {
+        let midpoint = Pos2 {
+            x: (a.x + b.x) / 2.0,
+            y: (a.y + b.y) / 2.0,
+        };
+        let m = (a.y - b.y) / (a.x - b.x);
+
+        let (d1, d2) = if m == 0.0 {
+            ((p.y - midpoint.y).abs(), (p.x - midpoint.x).abs())
+        } else if m == f32::INFINITY {
+            ((p.x - midpoint.x).abs(), (p.y - midpoint.y).abs())
+        } else {
+            let q = (a.x * b.y - b.x * a.y) / (a.x - b.x);
+
+            let m_inv = -1.0 / m;
+            let q_inv = midpoint.y - m_inv * midpoint.x;
+
+            (
+                distance_from_line(p, m, q),
+                distance_from_line(p, m_inv, q_inv),
+            )
+        };
+
+        let length = distance(a, b);
+
+        d1 <= LINE_DISTANCE_THRESHOLD && d2 <= length
+    } else {
+        false
+    }
+}
diff --git a/src/ui/panes/pid_drawing_tool/elements.rs b/src/ui/panes/pid_drawing_tool/elements.rs
new file mode 100644
index 0000000000000000000000000000000000000000..97f11363b7d26d088d442b05c5d663a71f433f7b
--- /dev/null
+++ b/src/ui/panes/pid_drawing_tool/elements.rs
@@ -0,0 +1,37 @@
+use super::symbols::Symbol;
+use super::{grid::GridInfo, pos::Pos};
+use egui::Pos2;
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Serialize, Deserialize, PartialEq)]
+pub struct Element {
+    /// Ancor postion in the grid, symbol center
+    pub position: Pos,
+
+    /// Size in grid units
+    pub size: i32,
+
+    /// Rotation in radiants
+    pub rotation: f32,
+
+    /// Symbol to be displayed
+    pub symbol: Symbol,
+}
+
+impl Element {
+    pub fn new(pos: Pos, symbol: Symbol) -> Self {
+        Self {
+            position: pos,
+            size: 10,
+            rotation: 0.0,
+            symbol,
+        }
+    }
+
+    pub fn contains(&self, grid: &GridInfo, pos: &Pos2) -> bool {
+        let start = self.position.add_size(-self.size / 2).into_pos2(grid);
+        let end = self.position.add_size(self.size / 2).into_pos2(grid);
+
+        (start.x <= pos.x && pos.x < end.x) && (start.y <= pos.y && pos.y < end.y)
+    }
+}
diff --git a/src/ui/panes/pid_drawing_tool/grid.rs b/src/ui/panes/pid_drawing_tool/grid.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e8adc1bb7b995c37403f58f07efe5df829d767bb
--- /dev/null
+++ b/src/ui/panes/pid_drawing_tool/grid.rs
@@ -0,0 +1,8 @@
+use serde::{Deserialize, Serialize};
+
+pub const LINE_DISTANCE_THRESHOLD: f32 = 5.0; // Pixels
+
+#[derive(Clone, Serialize, Deserialize, PartialEq)]
+pub struct GridInfo {
+    pub size: f32,
+}
diff --git a/src/ui/panes/pid_drawing_tool/pos.rs b/src/ui/panes/pid_drawing_tool/pos.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4a8b0a8873c1db1f0b1e39ef9f1a0c7500be8127
--- /dev/null
+++ b/src/ui/panes/pid_drawing_tool/pos.rs
@@ -0,0 +1,39 @@
+use egui::Pos2;
+use serde::{Deserialize, Serialize};
+
+use super::grid::GridInfo;
+
+#[derive(Clone, Serialize, Deserialize, PartialEq, Default)]
+pub struct Pos {
+    pub x: i32,
+    pub y: i32,
+}
+
+impl Pos {
+    pub fn add_size(&self, size: i32) -> Self {
+        Self {
+            x: self.x + size,
+            y: self.y + size,
+        }
+    }
+
+    pub fn into_pos2(&self, grid: &GridInfo) -> Pos2 {
+        Pos2 {
+            x: self.x as f32 * grid.size,
+            y: self.y as f32 * grid.size,
+        }
+    }
+
+    pub fn from_pos2(grid: &GridInfo, pos: &Pos2) -> Self {
+        Self {
+            x: (pos.x / grid.size) as i32,
+            y: (pos.y / grid.size) as i32,
+        }
+    }
+
+    pub fn distance(&self, grid: &GridInfo, pos: &Pos2) -> f32 {
+        let me = self.into_pos2(grid);
+
+        ((me.x - pos.x).powi(2) + (me.y - pos.y).powi(2)).sqrt()
+    }
+}