1
////////////////////////////////////////////////////////////////////////////
2
// NoteCase notes manager project <http://notecase.sf.net>
4
// This code is licensed under BSD license.See "license.txt" for more details.
6
// File: Defines atomic action performed on NoteCase document
7
// (base for Undo/Redo framework)
8
////////////////////////////////////////////////////////////////////////////
10
#include "DocAction.h"
11
#include "callbacks.h"
13
#include "lib/NoteDocument.h"
14
#include "lib/DocumentIterator.h"
16
#include "lib/debug.h"
18
extern NoteDocument g_doc;
19
extern GtkWidget *window1;
20
extern int g_nActiveNodeIdx;
21
extern TextView g_text;
22
void set_title_bar(const char *szText);
23
void rebuild_gui_tree (int nPID = -1);
25
DocAction::DocAction()
27
m_nType = ACT_UNDEFINED;
33
DocAction::~DocAction()
37
void DocAction::Redo()
67
void DocAction::Undo()
97
void DocAction::DoTextInsert()
99
InsertNodeText(m_nNodeIndex, m_nTextStartPos, m_strNodeText.c_str());
101
g_text.SelectRange(m_nTextStartPos, m_nTextStartPos+m_strNodeText.size());
103
GtkWidget *textview = lookup_widget(window1, "textview1");
104
gtk_window_set_focus(GTK_WINDOW(window1), textview);
108
void DocAction::DoTextDelete()
110
DeleteNodeText(m_nNodeIndex, m_nTextStartPos, m_strNodeText.size());
113
void DocAction::DoTreeInsert()
115
TRACE("UNDO/REDO: insert %d nodes under PID=%d\n", m_objSubTree.GetNodeCount(), m_nNodePID);
117
int nCount = g_doc.GetNodeCount();
119
TRACE("subtree dump:\n");
121
TRACE("UNDO/REDO insert: %d nodes before\n", g_doc.GetNodeCount());
122
TRACE("UNDO/REDO insert: PID=%d, Sibling=%d\n", m_nNodePID, m_nNodeSibling);
123
g_doc.Merge(m_objSubTree, m_nNodePID, m_nNodeSibling, true);
124
TRACE("UNDO/REDO insert: %d nodes after\n", g_doc.GetNodeCount());
127
//rebuild affected branch in the gui tree
128
rebuild_gui_tree (m_nNodePID);
130
g_nActiveNodeIdx = -1; //no selection
132
//select the node in the GUI tree
133
SelectNodeByIdx(nCount); //select subtree 'root' node in new tree
136
void DocAction::DoTreeDelete()
138
TRACE("UNDO/REDO: delete node ID=%d\n", m_nNodeID);
140
TRACE("subtree dump:\n");
144
TreeIterFromID(iter, m_nNodeID);
146
g_doc.NodeDelete(m_nNodeID);
148
g_nActiveNodeIdx = -1; //no selection
151
GtkTreeView *treeview = (GtkTreeView *)lookup_widget(window1, "treeview1");
152
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)treeview);
154
//remove GUI tree node
155
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
157
//clear edit belonging to selected node
158
g_text.Clear(); // TOFIX only if current node is one of being deleted
160
set_title_bar(_("Untitled")); //update node title label
164
void DocAction::DoNodeRename()
166
SetNodeTitle(m_nNodeIndex, m_strNodeNameNew.c_str()); //TOFIX recursive index ?
169
void DocAction::DoNodeUnrename()
171
SetNodeTitle(m_nNodeIndex, m_strNodeNameOld.c_str()); //TOFIX recursive index ?
174
void DocAction::DoTreeMove()
176
SelectNodeByIdx(m_nNodeIndex);//select proper node
178
switch(m_nMoveDirection){
180
do_node_move_left(false);
183
do_node_move_right(false);
186
do_node_move_up(false);
189
do_node_move_down(false);
194
//reverse the moving operation
195
void DocAction::DoTreeUnmove()
199
SelectNodeByIdx(m_nNodeIndex);//select proper node
201
switch(m_nMoveDirection){
203
do_node_move_right(false);
204
//restore sibling index position when moving back from left to right
205
for(i=0; i<m_nNodeSibling; i++)
206
do_node_move_down(false);
209
do_node_move_left(false);
212
do_node_move_down(false);
215
do_node_move_up(false);
220
void DocAction::DoTreeImport()
222
DocumentIterator it(g_doc);
223
int nCnt = it.GetChildCount(-1);
225
//TOFIX ask user for import details and merge with current document
226
g_doc.Merge(m_objSubTree);
228
//refresh tree starting from new content
229
add_child_nodes(NULL, -1, nCnt);
232
void DocAction::DoTreeUnimport()
234
DocumentIterator it(g_doc);
235
int nCount = it.GetNodeCount();
238
g_nActiveNodeIdx = -1; //no selection
240
GtkTreeView *treeview = (GtkTreeView *)lookup_widget(window1, "treeview1");
241
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)treeview);
243
//delete all nodes after idx = m_nNodeIndex
244
//delete in backward direction
245
for(i=nCount-1; i>=m_nNodeIndex; i--)
247
int nID = g_doc.GetNodeByIdx(i).m_nID;
250
TreeIterFromID(iter, nID);
253
g_doc.NodeDelete(nID);
255
//remove GUI tree node
256
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
258
//clear edit belonging to selected node
259
g_text.Clear(); // TOFIX only if current node is one of being deleted
261
//update node title label
262
set_title_bar(_("Untitled"));
267
void DocAction::DoTreeDND(bool bForward)
269
GtkTreeView *treeview = (GtkTreeView *)lookup_widget(window1, "treeview1");
270
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)treeview);
274
//redo - move node branch from original position to new one
278
TreeIterFromID(iter, nID);
280
TRACE("REDO dnd: tree dump on start\n");
285
TRACE("REDO dnd: delete node ID=%d\n", nID);
288
g_doc.NodeDelete(nID);
290
TRACE("REDO dnd: tree dump after delete\n");
295
//remove GUI tree node
296
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
298
//clear edit belonging to selected node
299
g_text.Clear(); // TOFIX only if current node is one of being deleted
301
TRACE("REDO dnd: merge subtree under PID=%d, SIB=%d\n", m_nNodeNewPID, m_nNodeNewSibling);
303
//insert node branch into new position
304
g_doc.Merge(m_objSubTree, m_nNodeNewPID, m_nNodeNewSibling, true); //keep IDs
306
TRACE("REDO dnd: tree dump after merge\n");
312
rebuild_gui_tree (m_nNodeNewPID);
316
//undo - move new node branch back to original position
317
int nID = m_nNewNodeID;
320
TreeIterFromID(iter, nID);
322
TRACE("UNDO dnd: tree dump on start\n");
327
TRACE("UNDO dnd: delete node ID=%d\n", nID);
330
g_doc.NodeDelete(nID);
332
TRACE("UNDO dnd: tree dump after delete\n");
337
//remove GUI tree node
338
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
340
//clear edit belonging to selected node
341
g_text.Clear(); // TOFIX only if current node is one of being deleted
343
TRACE("UNDO dnd: merge subtree under PID=%d\n", m_nNodePID);
345
//insert node branch into original position
346
g_doc.Merge(m_objSubTree, m_nNodePID, m_nNodeSibling, true); //keep IDs
348
TRACE("UNDO dnd: tree dump after merge\n");
354
rebuild_gui_tree (m_nNodePID);
358
void rebuild_gui_tree (int nPID)
360
GtkTreeView *treeview = (GtkTreeView *)lookup_widget(window1, "treeview1");
361
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)treeview);
366
gtk_tree_store_clear(GTK_TREE_STORE(model));
368
//rebuild parent node children
369
add_child_nodes(NULL, nPID, 0);
374
TreeIterFromID(iter, nPID);
376
//remove all parent node children
377
GtkTreeIter iterChild;
378
while(gtk_tree_model_iter_children(model, &iterChild, &iter))
379
gtk_tree_store_remove(GTK_TREE_STORE(model), &iterChild); //delete all children
381
//rebuild parent node children
382
add_child_nodes(&iter, nPID, 0);
1
////////////////////////////////////////////////////////////////////////////
2
// NoteCase notes manager project <http://notecase.sf.net>
4
// This code is licensed under BSD license.See "license.txt" for more details.
6
// File: Defines atomic action performed on NoteCase document
7
// (base for Undo/Redo framework)
8
////////////////////////////////////////////////////////////////////////////
10
#include "DocAction.h"
11
#include "callbacks.h"
13
#include "interface.h"
14
#include "lib/NoteDocument.h"
15
#include "lib/DocumentIterator.h"
18
#include "lib/debug.h"
20
extern NoteDocument g_doc;
21
extern GtkWidget *window1;
22
extern TextView g_text;
23
extern TreeView g_tree;
25
void rebuild_gui_tree (int nPID = -1);
26
bool IteratorFromNodeIdx(int nIdx, GtkTreeIter &iter);
27
void RefreshAllLinkTags();
28
int GetSelectedNodeIdx();
30
DocAction::DocAction()
32
m_nType = ACT_UNDEFINED;
40
DocAction::~DocAction()
44
void DocAction::Exec(bool bInteractive)
71
case ACT_TEXT_REPLACE:
74
case ACT_TEXT_LINK_ADD:
77
case ACT_TEXT_LINK_REMOVE:
83
void DocAction::Undo()
104
case ACT_TREE_IMPORT:
110
case ACT_TEXT_REPLACE:
113
case ACT_TEXT_LINK_ADD:
116
case ACT_TEXT_LINK_REMOVE:
122
void DocAction::DoTextInsert()
124
InsertNodeText(m_nNodeIndex, m_nTextStartPos, m_strNodeText.c_str());
126
g_text.SelectRange(m_nTextStartPos, m_nTextStartPos+m_strNodeText.size());
128
GtkWidget *textview = lookup_widget(window1, "textview1");
129
gtk_window_set_focus(GTK_WINDOW(window1), textview);
133
void DocAction::DoTextDelete()
135
DeleteNodeText(m_nNodeIndex, m_nTextStartPos, m_strNodeText.size());
138
void DocAction::DoTextReplace()
140
TRACE("UNDO/REDO: replace text\n");
142
SelectNodeByIdx(m_nNodeIndex);//select proper node
144
//TOFIX this code is similar to the one in replace dialog - use this only?
145
int nLen = g_utf8_strlen(m_strFind.c_str(), -1);
147
NoteNode &node = g_doc.GetNodeByIdx(m_nNodeIndex);
149
// some text selected, replace it with the given string
152
strTxt = node.GetTitle();
154
strTxt = node.GetText();
156
//convert from character offset to buffer offset
157
int nCursorBuf, nSelBuf;
158
const char *szBuf = strTxt.c_str();
159
gchar* pszPos = g_utf8_offset_to_pointer(szBuf, m_nOffset);
160
nCursorBuf = pszPos - szBuf;
161
nSelBuf = m_nOffset + nLen;
163
//now replace the text part with new content
164
//TOFIX what if cursor < nSelBuf - convert both values using min
165
strTxt = strTxt.erase(nCursorBuf, nSelBuf-nCursorBuf);
166
strTxt.insert(nCursorBuf, m_strReplace.c_str());
170
node.SetTitle(strTxt.c_str());
174
IteratorFromNodeIdx(m_nNodeIndex, iter);
175
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)g_tree.m_pWidget);
176
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, 0, strTxt.c_str(), -1);
178
set_title_bar(strTxt.c_str());
182
node.SetText(strTxt.c_str());
185
GtkTextView *textview = (GtkTextView *)g_text.m_pWidget;
186
GtkTextBuffer* buffer1 = gtk_text_view_get_buffer(textview);
188
g_signal_handlers_block_by_func(buffer1, (void *)on_delete_text, 0);
189
g_signal_handlers_block_by_func(buffer1, (void *)on_insert_text, 0);
191
g_text.ReplaceText(m_nOffset, m_nOffset+nLen, m_strReplace.c_str());
192
g_text.SelectRange(m_nOffset, m_nOffset+g_utf8_strlen(m_strReplace.c_str(), -1));
194
g_signal_handlers_unblock_by_func(buffer1, (void *)on_delete_text, 0);
195
g_signal_handlers_unblock_by_func(buffer1, (void *)on_insert_text, 0);
197
node.OnTxtReplaced(m_nOffset, m_nOffset+nLen, m_strReplace.c_str());
198
RefreshAllLinkTags();
202
void DocAction::DoTextUnreplace()
204
TRACE("UNDO/REDO: undo replace text\n");
206
SelectNodeByIdx(m_nNodeIndex);//select proper node
208
//TOFIX this code is similar to the one in replace dialog - use this only?
209
int nLen = g_utf8_strlen(m_strReplace.c_str(), -1);
211
NoteNode &node = g_doc.GetNodeByIdx(m_nNodeIndex);
213
// some text selected, replace it with the given string
216
strTxt = node.GetTitle();
218
strTxt = node.GetText();
220
//convert from character offset to buffer offset
221
int nCursorBuf, nSelBuf;
222
const char *szBuf = strTxt.c_str();
223
gchar* pszPos = g_utf8_offset_to_pointer(szBuf, m_nOffset);
224
nCursorBuf = pszPos - szBuf;
225
nSelBuf = m_nOffset + nLen;
227
//now replace the text part with new content
228
//TOFIX what if cursor < nSelBuf - convert both values using min
229
strTxt = strTxt.erase(nCursorBuf, nSelBuf-nCursorBuf);
230
strTxt.insert(nCursorBuf, m_strFind.c_str());
234
node.SetTitle(strTxt.c_str());
238
IteratorFromNodeIdx(m_nNodeIndex, iter);
239
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)g_tree.m_pWidget);
240
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, 0, strTxt.c_str(), -1);
242
set_title_bar(strTxt.c_str());
246
node.SetText(strTxt.c_str());
249
GtkTextView *textview = (GtkTextView *)g_text.m_pWidget;
250
GtkTextBuffer* buffer1 = gtk_text_view_get_buffer(textview);
252
g_signal_handlers_block_by_func(buffer1, (void *)on_delete_text, 0);
253
g_signal_handlers_block_by_func(buffer1, (void *)on_insert_text, 0);
255
g_text.ReplaceText(m_nOffset, m_nOffset+nLen, m_strFind.c_str());
256
g_text.SelectRange(m_nOffset, m_nOffset + g_utf8_strlen(m_strFind.c_str(), -1));
258
g_signal_handlers_unblock_by_func(buffer1, (void *)on_delete_text, 0);
259
g_signal_handlers_unblock_by_func(buffer1, (void *)on_insert_text, 0);
261
node.OnTxtReplaced(m_nOffset, m_nOffset+nLen, m_strFind.c_str());
262
RefreshAllLinkTags();
266
void DocAction::DoTreeInsert()
268
TRACE("UNDO/REDO: insert %d nodes under PID=%d\n", m_objSubTree.GetNodeCount(), m_nNodePID);
270
int nCount = g_doc.GetNodeCount();
272
TRACE("subtree dump:\n");
274
TRACE("UNDO/REDO insert: %d nodes before\n", g_doc.GetNodeCount());
275
TRACE("UNDO/REDO insert: PID=%d, Sibling=%d\n", m_nNodePID, m_nNodeSibling);
276
g_doc.Merge(m_objSubTree, m_nNodePID, m_nNodeSibling, true);
277
TRACE("UNDO/REDO insert: %d nodes after\n", g_doc.GetNodeCount());
279
//rebuild affected branch in the gui tree
280
rebuild_gui_tree (m_nNodePID);
282
g_doc.m_nActiveNodeIdx = -1; //no selection
284
//select the node in the GUI tree
285
SelectNodeByIdx(nCount); //select subtree 'root' node in new tree
288
void DocAction::DoTreeDelete()
290
TRACE("UNDO/REDO: delete node ID=%d\n", m_nNodeID);
292
TRACE("subtree dump:\n");
296
TreeIterFromID(iter, m_nNodeID);
298
g_doc.NodeDelete(m_nNodeID);
300
g_doc.m_nActiveNodeIdx = -1; //no selection
303
GtkTreeView *treeview = (GtkTreeView *)lookup_widget(window1, "treeview1");
304
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)treeview);
306
//remove GUI tree node
307
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
309
//clear edit belonging to selected node
312
set_title_bar(""); //update node title label
316
void DocAction::DoNodeRename()
318
SetNodeTitle(m_nNodeIndex, m_strNodeNameNew.c_str()); //TOFIX recursive index ?
321
void DocAction::DoNodeUnrename()
323
SetNodeTitle(m_nNodeIndex, m_strNodeNameOld.c_str()); //TOFIX recursive index ?
326
void DocAction::DoTreeMove()
328
SelectNodeByIdx(m_nNodeIndex);//select proper node
330
switch(m_nMoveDirection){
332
do_node_move_left(false);
335
do_node_move_right(false);
338
do_node_move_up(false);
341
do_node_move_down(false);
346
//reverse the moving operation
347
void DocAction::DoTreeUnmove()
351
SelectNodeByIdx(m_nNodeIndex);//select proper node
353
switch(m_nMoveDirection){
355
do_node_move_right(false);
356
//restore sibling index position when moving back from left to right
357
for(i=0; i<m_nNodeSibling; i++)
358
do_node_move_down(false);
361
do_node_move_left(false);
364
do_node_move_down(false);
367
do_node_move_up(false);
372
void DocAction::DoTreeImport()
374
DocumentIterator it(g_doc);
375
int nCnt = it.GetChildCount(-1);
377
//TOFIX ask user for import details and merge with current document
378
g_doc.Merge(m_objSubTree);
380
//refresh tree starting from new content
381
add_child_nodes(NULL, -1, nCnt);
384
void DocAction::DoTreeUnimport()
386
DocumentIterator it(g_doc);
387
int nCount = g_doc.GetNodeCount();
390
g_doc.m_nActiveNodeIdx = -1; //no selection
392
GtkTreeView *treeview = (GtkTreeView *)lookup_widget(window1, "treeview1");
393
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)treeview);
395
//delete all nodes after idx = m_nNodeIndex
396
//delete in backward direction
397
for(i=nCount-1; i>=m_nNodeIndex; i--)
399
int nID = g_doc.GetNodeByIdx(i).m_nID;
402
TreeIterFromID(iter, nID);
405
g_doc.NodeDelete(nID);
407
//remove GUI tree node
408
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
410
//clear edit belonging to selected node
411
g_text.Clear(); // TOFIX only if current node is one of being deleted
413
//update node title label
419
void DocAction::DoTreeDND(bool bForward)
421
GtkTreeView *treeview = (GtkTreeView *)lookup_widget(window1, "treeview1");
422
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)treeview);
426
//redo - move node branch from original position to new one
430
TreeIterFromID(iter, nID);
432
TRACE("REDO dnd: tree dump on start\n");
437
TRACE("REDO dnd: delete node ID=%d\n", nID);
440
g_doc.NodeDelete(nID);
442
TRACE("REDO dnd: tree dump after delete\n");
447
//remove GUI tree node
448
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
450
//clear edit belonging to selected node
453
TRACE("REDO dnd: merge subtree under PID=%d, SIB=%d\n", m_nNodeNewPID, m_nNodeNewSibling);
455
//insert node branch into new position
456
g_doc.Merge(m_objSubTree, m_nNodeNewPID, m_nNodeNewSibling, true); //keep IDs
458
TRACE("REDO dnd: tree dump after merge\n");
464
rebuild_gui_tree (m_nNodeNewPID);
468
//undo - move new node branch back to original position
469
int nID = m_nNewNodeID;
472
TreeIterFromID(iter, nID);
474
TRACE("UNDO dnd: tree dump on start\n");
479
TRACE("UNDO dnd: delete node ID=%d\n", nID);
482
g_doc.NodeDelete(nID);
484
TRACE("UNDO dnd: tree dump after delete\n");
489
//remove GUI tree node
490
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
492
//clear edit belonging to selected node
495
TRACE("UNDO dnd: merge subtree under PID=%d, SIdx=%d\n", m_nNodePID, m_nNodeSibling);
498
//m_objSubTree.Dump();
500
//insert node branch into original position
501
g_doc.Merge(m_objSubTree, m_nNodePID, m_nNodeSibling, true); //keep IDs
503
TRACE("UNDO dnd: tree dump after merge\n");
509
rebuild_gui_tree (m_nNodePID);
513
void rebuild_gui_tree (int nPID)
515
GtkTreeView *treeview = (GtkTreeView *)lookup_widget(window1, "treeview1");
516
GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)treeview);
521
gtk_tree_store_clear(GTK_TREE_STORE(model));
523
//rebuild parent node children
524
add_child_nodes(NULL, nPID, 0);
529
TreeIterFromID(iter, nPID);
531
//remove all parent node children
532
GtkTreeIter iterChild;
533
while(gtk_tree_model_iter_children(model, &iterChild, &iter))
534
gtk_tree_store_remove(GTK_TREE_STORE(model), &iterChild); //delete all children
536
//rebuild parent node children
537
add_child_nodes(&iter, nPID, 0);
541
void DocAction::DoLinkAdd()
544
info.m_nStartOffset = m_nLinkOffset;
545
info.m_strText = m_strLinkTitle;
546
info.m_strTargetURL = m_strLinkUrl;
547
info.m_nTargetNodeID = m_nLinkNodeID;
548
info.RefreshLength();
550
// add link into the node
551
g_doc.GetNodeByIdx(m_nNodeIndex).m_lstLinks.push_back(info);
554
int nIdx = GetSelectedNodeIdx();
555
if(nIdx == m_nNodeIndex)
557
//underline the text as a link
558
g_text.RemoveTextStyles(info.m_nStartOffset, info.m_nStartOffset+info.m_nTextLength);
559
g_text.SetTextUnderlined(info.m_nStartOffset, info.m_nStartOffset+info.m_nTextLength);
563
void DocAction::DoLinkRemove()
565
NoteNode &node = g_doc.GetNodeByIdx(m_nNodeIndex);
568
int nPos = node.m_lstLinks.Find(m_nLinkOffset);
572
info = node.m_lstLinks[nPos];
575
int nIdx = GetSelectedNodeIdx();
576
if(nIdx == m_nNodeIndex)
578
g_text.RemoveTextStyles(info.m_nStartOffset, info.m_nStartOffset+info.m_nTextLength);
581
//remove link from the storage
582
node.m_lstLinks.erase(g_doc.GetNodeByIdx(nIdx).m_lstLinks.begin()+nPos);