diff --git a/woman/lib.py b/woman/lib.py
index 791457ab85b9dc6de6d11f5f5915939b53071c83..4d36a3810028a4d556e5487c7cb15758c3b1b734 100644
--- a/woman/lib.py
+++ b/woman/lib.py
@@ -53,6 +53,94 @@ def move_container(master, slave):
     shared.qt_task("ws_changed", master, slave)
 
 
+def swap_workspace(a_master, a_slave, b_master, b_slave):
+    if (a_master, a_slave) == (b_master, b_slave):
+        return
+    def rename_workspace(a_master, a_slave, b_master, b_slave):
+        a = (a_master, a_slave)
+        b = (b_master, b_slave)
+        shared.i3_cmd(f'rename workspace {workspace(*a)} to {workspace(*b)}')
+        shared.qt_task("ws_rename", a_master, a_slave, b_master, b_slave)
+        for i in shared.workspace_on:
+            if shared.workspace_on[i] == a:
+                shared.workspace_on[i] = b
+        event_workspace_is_on(b, shared.output_of_workspace[a_master][a_slave])
+        event_workspace_deleted(a)
+        if a_master == b_master and a_master is not None:
+            if shared.slave_for[a_master] == a_slave:
+                shared.slave_for[a_master] = b_slave
+            for i in shared.slave_on_for:
+                if shared.slave_on_for[i][a_master] == a_slave:
+                    shared.slave_on_for[i][a_master] = b_slave
+        else:
+            for i in set(shared.slave_on).union(set(shared.master_on)):
+                def set_ws(master, slave):
+                    if master is None:
+                        shared.slave_on[i] = slave
+                    else:
+                        shared.master_on[i] = master
+                        shared.slave_on_for[i][b_master] = slave
+                if a == (None, shared.slave_on[i]):
+                    shared.slave_on[i] = None
+                    set_ws(*b)
+                elif a == (shared.master_on[i], shared.slave_on_for[i][a_master]):
+                    set_ws(*b)
+
+    with shared.lock:
+        a = (a_master, a_slave)
+        b = (b_master, b_slave)
+        if a_master not in shared.output_of_workspace or a_slave not in shared.output_of_workspace[a_master]:
+            return rename_workspace(*b, *a)
+        if b_master not in shared.output_of_workspace or b_slave not in shared.output_of_workspace[b_master]:
+            return rename_workspace(*a, *b)
+        shared.i3_cmd(f'rename workspace {workspace(*a)} to tmp_swap')
+        try:
+            shared.i3_cmd(f'rename workspace {workspace(*b)} to {workspace(*a)}')
+        except I3CmdException as e:
+            shared.i3_cmd(f'rename workspace tmp_swap to {workspace(*a)}')
+            raise e
+        shared.i3_cmd(f'rename workspace tmp_swap to {workspace(*b)}')
+        for i in shared.workspace_on:
+            if shared.workspace_on[i] == a:
+                shared.workspace_on[i] = b
+            elif shared.workspace_on[i] == b:
+                shared.workspace_on[i] = a
+
+        if a_master == b_master and a_master is not None:
+            if shared.slave_for[a_master] == a_slave:
+                shared.slave_for[a_master] = b_slave
+            elif shared.slave_for[a_master] == b_slave:
+                shared.slave_for[a_master] = a_slave
+            for i in shared.slave_on_for:
+                if shared.slave_on_for[i][a_master] == a_slave:
+                    shared.slave_on_for[i][a_master] = b_slave
+                elif shared.slave_on_for[i][a_master] == b_slave:
+                    shared.slave_on_for[i][a_master] = a_slave
+        else:
+            for i in set(shared.slave_on).union(set(shared.master_on)):
+                def set_ws(master, slave):
+                    if master is None:
+                        shared.slave_on[i] = slave
+                    else:
+                        shared.master_on[i] = master
+                        shared.slave_on_for[i][b_master] = slave
+                if a == (None, shared.slave_on[i]):
+                    shared.slave_on[i] = None
+                    set_ws(*b)
+                elif b == (None, shared.slave_on[i]):
+                    shared.slave_on[i] = None
+                    set_ws(*a)
+                elif a == (shared.master_on[i], shared.slave_on_for[i][a_master]):
+                    set_ws(*b)
+                elif b == (shared.master_on[i], shared.slave_on_for[i][b_master]):
+                    set_ws(*a)
+
+        tmp = shared.output_of_workspace[a_master][a_slave]
+        shared.output_of_workspace[a_master][a_slave] = shared.output_of_workspace[b_master][b_slave]
+        shared.output_of_workspace[b_master][b_slave] = tmp
+    shared.qt_task("ws_swap", a_master, a_slave, b_master, b_slave)
+
+
 def load_workspaces():
     i3_workspaces = shared.i3.value.get_workspaces()
 
diff --git a/woman/qt.py b/woman/qt.py
index a2c0eac6782133f3c4dd5dadad7bd4ae68305bb7..20099d552704b63ba7b5ed31e18e8f9ea8dbc251 100755
--- a/woman/qt.py
+++ b/woman/qt.py
@@ -92,6 +92,18 @@ def qt_main():
             def ws_metadata_changed(master, slave):
                 qt_workspace_widget_func(master, slave, lambda x: x.metadata_changed())
 
+            @func_add
+            def ws_swap(a_master, a_slave, b_master, b_slave):
+                qt_workspace_widget_func(a_master, a_slave,
+                        lambda x: qt_workspace_widget_func(b_master, b_slave, lambda y: x.swapped(y)))
+                qt_workspace_widget_func(a_master, a_slave, lambda x: x.metadata_changed())
+                qt_workspace_widget_func(b_master, b_slave, lambda x: x.metadata_changed())
+
+            @func_add
+            def ws_rename(a_master, a_slave, b_master, b_slave):
+                qt_workspace_widget_func(a_master, a_slave,
+                        lambda x: qt_workspace_widget_func(b_master, b_slave, lambda y: x.moved(y)))
+
             @func_add
             def screenshot_and_goto(n_master, n_slave, master, slave):
                 qt_workspace_widget_func(master, slave, lambda x: x.make_screenshot())
@@ -653,6 +665,25 @@ def qt_main():
             self.setPalette(p)
             self._name.setPalette(p_name)
 
+        def swapped(self, other):
+            tmp = self.screenshot_is_old
+            self.screenshot_is_old = other.screenshot_is_old
+            other.screenshot_is_old = tmp
+
+            tmp = self.screenshot
+            self.screenshot = other.screenshot
+            other.screenshot = tmp
+
+            other.redraw_pic()
+            self.redraw_pic()
+
+        def moved(self, other):
+            other.screenshot_is_old = self.screenshot_is_old
+            other.screenshot = self.screenshot
+
+            other.redraw_pic()
+            self.redraw_pic()
+
         def metadata_changed(self):
             self._name.setText(f"{workspace(self.master, self.slave)}")
             self.name_color = QColor(0, 0, 0)