diff --git a/spec/more_speech/ui/swing/article_tree_spec.clj b/spec/more_speech/ui/swing/article_tree_spec.clj index a45e678..ff4bfee 100644 --- a/spec/more_speech/ui/swing/article_tree_spec.clj +++ b/spec/more_speech/ui/swing/article_tree_spec.clj @@ -441,7 +441,7 @@ (add-events event-list) (should= ["Root" [88] [99 [88]]] (depict-tree header-tree))))) - (it "adds a chain of replies in order." + (it "adds a complex chain of replies in order." (with-redefs [render-event (stub :render-event)] (let [tab-id :tab filter {:selected [] @@ -464,7 +464,49 @@ (add-events event-list) (should= ["Root" [55] [66] [77 [66]] [88 [77 [66]] [55]] [99 [88 [77 [66]] [55]]]] (depict-tree header-tree))))) - (it "adds a chain of replies in reverse order." + (it "adds a chain of three replies in order." + (with-redefs [render-event (stub :render-event)] + (let [tab-id :tab + filter {:selected [] + :blocked []} + header-tree (make-header-tree tab-id) + _ (config! header-tree :id tab-id :user-data filter) + frame (frame :content header-tree) + event-list [{:id 99 :created-at 1} + {:id 88 :created-at 2 :tags [[:e (hexify 99) "" "reply"]]} + {:id 77 :created-at 3 :tags [[:e (hexify 88) "" "reply"]]} + ] + event-map (make-event-map event-list) + event-state {:tabs {tab-id nil} + :text-event-map event-map} + event-context (atom event-state)] + (reset! ui-context {:frame frame + :event-context event-context}) + (add-events event-list) + (should= ["Root" [77] [88 [77]] [99 [88 [77]]]] (depict-tree header-tree))))) + + (it "adds a chain of three replies in reverse order." + (with-redefs [render-event (stub :render-event)] + (let [tab-id :tab + filter {:selected [] + :blocked []} + header-tree (make-header-tree tab-id) + _ (config! header-tree :id tab-id :user-data filter) + frame (frame :content header-tree) + event-list [{:id 99 :created-at 1} + {:id 88 :created-at 2 :tags [[:e (hexify 99) "" "reply"]]} + {:id 77 :created-at 3 :tags [[:e (hexify 88) "" "reply"]]}] + event-list (reverse event-list) + event-map (make-event-map event-list) + event-state {:tabs {tab-id nil} + :text-event-map event-map} + event-context (atom event-state)] + (reset! ui-context {:frame frame + :event-context event-context}) + (add-events event-list) + (should= ["Root" [77] [88 [77]] [99 [88 [77]]]] (depict-tree header-tree))))) + + (it "adds a complex chain of replies in reverse order." (with-redefs [render-event (stub :render-event)] (let [tab-id :tab filter {:selected [] @@ -486,7 +528,7 @@ (reset! ui-context {:frame frame :event-context event-context}) (add-events event-list) - (should= ["Root" [55] [66] [77 [66]] [88 [77 [66]] [55]] [99 [88 [77 [66]] [55]]]] (depict-tree header-tree))))) + (should= ["Root" [55] [66] [77 [66]] [88 [55] [77 [66]]] [99 [88 [55] [77 [66]]]]] (depict-tree header-tree))))) ) (declare depict-node) @@ -494,16 +536,16 @@ (defn depict-tree [tree] (let [model (config tree :model) root (.getRoot model)] - (depict-node model root))) + (depict-node root))) -(defn depict-node [model node] +(defn depict-node [node] (loop [ns (range (.getChildCount node)) node-depiction [(.getUserObject node)]] (if (empty? ns) node-depiction (let [n (first ns) - child (.getChild model node n)] - (recur (rest ns) (conj node-depiction (depict-node model child))))))) + child (.getChildAt node n)] + (recur (rest ns) (conj node-depiction (depict-node child))))))) (defn make-event-map [event-list] (loop [event-list event-list @@ -515,4 +557,35 @@ (defn add-events [event-list] (doseq [event event-list] - (add-event event))) \ No newline at end of file + (add-event event))) + +(describe "copy-node copies a node and all its children" + (it "copies one node" + (let [node (DefaultMutableTreeNode. 1) + copied-node (copy-node node)] + (should= [1] (depict-node copied-node)))) + + (it "copies a node with one child" + (let [node (DefaultMutableTreeNode. 1) + child (DefaultMutableTreeNode. 2) + _ (.add node child) + copied-node (copy-node node)] + (should= [1 [2]] (depict-node copied-node)))) + + (it "copies a node with two children" + (let [node (DefaultMutableTreeNode. 1) + _ (.add node (DefaultMutableTreeNode. 2)) + _ (.add node (DefaultMutableTreeNode. 3)) + copied-node (copy-node node)] + (should= [1 [2] [3]] (depict-node copied-node)))) + + (it "copies a node with two children and grandchildren" + (let [node (DefaultMutableTreeNode. 1) + child1 (DefaultMutableTreeNode. 2) + _ (.add node child1) + child2 (DefaultMutableTreeNode. 3) + _ (.add node child2) + _ (.add child2 (DefaultMutableTreeNode. 4)) + copied-node (copy-node node)] + (should= [1 [2] [3 [4]]] (depict-node copied-node)))) + ) \ No newline at end of file diff --git a/src/more_speech/ui/swing/article_tree.clj b/src/more_speech/ui/swing/article_tree.clj index 5a1d80a..d853371 100644 --- a/src/more_speech/ui/swing/article_tree.clj +++ b/src/more_speech/ui/swing/article_tree.clj @@ -114,6 +114,8 @@ (add-references event) (resolve-any-orphans event-id))) +(declare build-orphan-node) + (defn resolve-any-orphans [parent-id] (let [parent-nodes (get-in @ui-context [:node-map parent-id]) orphan-set (get-in @ui-context [:orphaned-references parent-id])] @@ -125,12 +127,27 @@ nil (let [orphan-id (first orphan-set)] (doseq [parent-node parent-nodes] - (let [orphan-node (DefaultMutableTreeNode. orphan-id)] + (let [orphan-node (build-orphan-node orphan-id)] (.add ^DefaultMutableTreeNode parent-node orphan-node) (swap! ui-context update-in [:node-map orphan-id] conj orphan-node))) (recur (rest orphan-set))))) - (swap! ui-context assoc-in [:orphaned-references parent-id] #{})))) - ) + (swap! ui-context assoc-in [:orphaned-references parent-id] #{}))))) + +(declare copy-node) + +(defn build-orphan-node [orphan-id] + (let [node-map (:node-map @ui-context) + orphan-nodes (get node-map orphan-id)] + (copy-node (first orphan-nodes)))) + +(defn copy-node [node] + (loop [copied-node (DefaultMutableTreeNode. (.getUserObject node)) + children (enumeration-seq (.children node))] + (if (empty? children) + copied-node + (let [child (copy-node (first children))] + (.add copied-node child) + (recur copied-node (rest children)))))) ;; at the moment an event can appear in several places in the tree. ;; it can be in the reply chain of an event, and it can stand alone.