forked from sanyaade-machine-learning/Transana
-
Notifications
You must be signed in to change notification settings - Fork 0
/
NotesBrowser.py
1422 lines (1367 loc) · 83.7 KB
/
NotesBrowser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Copyright (C) 2007-2015 The Board of Regents of the University of Wisconsin System
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
""" This module implements the Notes Browser. It's used for browsing through Notes objects """
DEBUG = False
if DEBUG:
print "NotesBrowser.py DEBUG is ON!!"
__author__ = 'David K. Woods <dwoods@wcer.wisc.edu>'
# import wxPython
import wx
# import Python's sys module
import sys
# import Transana's Clip object
import Clip
# import Transana's Collection object
import Collection
# import Transana's DatabaseTreeTab (for the _nodeData definition)
import DatabaseTreeTab
# import Transana's Database Interface
import DBInterface
# import Transana's Dialogs
import Dialogs
# import Transana's Document object
import Document
# import Transana's Episode object
import Episode
# import Transana's Note object
import Note
# import Transana's Note Editor
import NoteEditor
# Import Transana's Note Properties Form
import NotePropertiesForm
# Import Transana's Quote object
import Quote
# import Notes Report Generator
import ReportGeneratorForNotes
# import Transana's Library object
import Library
# import Transana's Snapshot object
import Snapshot
# import Transana's Constants
import TransanaConstants
# import Transana's Exceptions
import TransanaExceptions
# import Transana's Global variables
import TransanaGlobal
# import Transana's Images
import TransanaImages
# import Transana's Transcript object
import Transcript
class MySplitter(wx.SplitterWindow):
""" A local subclass of the wxSplitterWindow """
def __init__(self, parent, ID):
""" Initialize the Splitter Window """
# Remember your ancestors
self.parent = parent
# Initialize the Splitter Window
wx.SplitterWindow.__init__(self, parent, ID, style = wx.SP_LIVE_UPDATE)
def OnHelp(self, event):
""" Implement the Help function (required by the _NotePanel)"""
# If the global Menu Window exists ...
if (TransanaGlobal.menuWindow != None):
# ... call Help through its Control Object! We need the Notes Browser help.
TransanaGlobal.menuWindow.ControlObject.Help("Notes Browser")
def OnClose(self, event):
""" Implement the Close function (required by the _NotePanel) """
# To close the Splitter Window, we need to close its Parent Object!
self.parent.OnClose(event)
class NotesBrowser(wx.Dialog): # (wx.MDIChildFrame)
""" The Transana Notes Browser. """
def __init__(self,parent,id,title):
# Create a Dialog to house the Notes Browser
wx.Dialog.__init__(self,parent,-1, title, size = (800,600), style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
# wx.MDIChildFrame.__init__(self,parent,-1, title, size = (800,600), style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
# Get the Transana Icon
transanaIcon = wx.Icon("images/Transana.ico", wx.BITMAP_TYPE_ICO)
# Specify the Transana Icon as the Dialog's Icon
self.SetIcon(transanaIcon)
# initialize the pointer to the Current Active Note to None
self.activeNote = None
# Initialize a variable to track if the note ID is changed
self.originalNoteID = ''
# Initialize the variable that indicates which Tree is currently active
# (used in right-click menu methods)
self.activeTree = None
# Initialize the Search Text string
self.searchText = ''
# Define the Control Object
self.ControlObject = None
# Create the Notes Browser's main Sizer
mainSizer = wx.BoxSizer(wx.VERTICAL)
# Create a Splitter Window
splitter = MySplitter(self, -1)
# Put the Splitter Window on the Main Sizer
mainSizer.Add(splitter, 1, wx.EXPAND)
# Create a panel for the Notes Tree, placed on the Splitter Window
self.treePanel = wx.Panel(splitter, -1, style = wx.RAISED_BORDER)
# Create a Notebook control, placed on the Notes Tree panel
self.treeNotebook = wx.Notebook(self.treePanel, -1, style=wx.CLIP_CHILDREN)
# Create a Sizer for the Note Tree panel
noteTreeSizer = wx.BoxSizer(wx.HORIZONTAL)
# Create a Panel for the Notes tab of the Notebook
self.treeNotebookNotesTab = wx.Panel(self.treeNotebook, -1)
# Add the Notes Tab to the Notebook
self.treeNotebook.AddPage(self.treeNotebookNotesTab, _("Notes"), True)
# Create a Sizer for the Notes Tab
noteTreeCtrlSizer = wx.BoxSizer(wx.HORIZONTAL)
# Create a Tree Control and place it on the Notes Tab Panel
self.treeNotebookNotesTabTreeCtrl = wx.TreeCtrl(self.treeNotebookNotesTab, -1, style = wx.TR_HAS_BUTTONS) # | wx.TR_EDIT_LABELS )
# Populate the Tree Control with all the Nodes and Notes it needs!
self.notesRoot = self.PopulateTreeCtrl(self.treeNotebookNotesTabTreeCtrl)
# Place the TreeCtrl on the Notes Tab Sizer
noteTreeCtrlSizer.Add(self.treeNotebookNotesTabTreeCtrl, 1, wx.EXPAND | wx.ALL, 2)
# Set the Sizer on the Notes Tab
self.treeNotebookNotesTab.SetSizer(noteTreeCtrlSizer)
# Fit the controls on the Sizer
self.treeNotebookNotesTab.Fit()
# Capture selection event for the Notes Tree Control
self.treeNotebookNotesTabTreeCtrl.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemSelected)
# Add methods to handle the editing of labels in the Notes Tree
# self.treeNotebookNotesTabTreeCtrl.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginLabelEdit)
# self.treeNotebookNotesTabTreeCtrl.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndLabelEdit)
# Add a method to handle right-mouse-down, which should cause a popup menu
self.treeNotebookNotesTabTreeCtrl.Bind(wx.EVT_RIGHT_DOWN, self.OnTreeCtrlRightDown)
# Create a Panel for the Notes Search tab of the Notebook
self.treeNotebookSearchTab = wx.Panel(self.treeNotebook, style = wx.RAISED_BORDER)
# Add the Notes Search Tab to the Notebook
self.treeNotebook.AddPage(self.treeNotebookSearchTab, _("Note Search"), False)
# Create a Sizer for the Notes Search Tab
noteTreeCtrlSizer2 = wx.BoxSizer(wx.VERTICAL)
# Create a text header
prompt = wx.StaticText(self.treeNotebookSearchTab, -1, _("Search Text:"))
# Add the text to the Note Search Tab sizer
noteTreeCtrlSizer2.Add(prompt, 0, wx.LEFT | wx.TOP | wx.RIGHT, 2)
# Add a spacer to the Note Search Tab sizer
noteTreeCtrlSizer2.AddSpacer((0, 5))
# Add a Horizontal Sizer for the Search Text and button
noteSearchSizer = wx.BoxSizer(wx.HORIZONTAL)
# Create a Search Text Ctrl. We want this control to handle the Enter key itself
self.noteSearch = wx.TextCtrl(self.treeNotebookSearchTab, -1, style=wx.TE_PROCESS_ENTER)
# Capture the Enter key for the Search box. Enter should trigger a search
self.noteSearch.Bind(wx.EVT_TEXT_ENTER, self.OnAllNotesSearch)
# When the noteSearch control loses focus, that should trigger a search too.
self.noteSearch.Bind(wx.EVT_KILL_FOCUS, self.OnAllNotesSearch)
# Add the Search Text box to the note search sizer
noteSearchSizer.Add(self.noteSearch, 1, wx.EXPAND | wx.RIGHT, 5)
# ... and create a button with that graphic
self.searchButton = wx.BitmapButton(self.treeNotebookSearchTab, -1, TransanaImages.ArtProv_FIND.GetBitmap(), (16, 16))
# Link the button to the method that will perform the search
self.searchButton.Bind(wx.EVT_BUTTON, self.OnAllNotesSearch)
# Add the button to the Note Search Sizer
noteSearchSizer.Add(self.searchButton, 0)
# Add the Search Text Siser to the Note Search Tab sizer
noteTreeCtrlSizer2.Add(noteSearchSizer, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 2)
# Add a spacer to the Note Search Tab sizer
noteTreeCtrlSizer2.AddSpacer((0, 10))
# Create a Tree Control and place it on the Notes Search Tab Panel
self.treeNotebookSearchTabTreeCtrl = wx.TreeCtrl(self.treeNotebookSearchTab, -1, style = wx.TR_HAS_BUTTONS) # | wx.TR_EDIT_LABELS )
# Place the TreeCtrl on the Notes Search Tab Sizer
noteTreeCtrlSizer2.Add(self.treeNotebookSearchTabTreeCtrl, 1, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 2)
# Capture selection event for the Notes Tree Control
self.treeNotebookSearchTabTreeCtrl.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemSelected)
# Add methods to handle the editing of labels in the Notes Tree
# self.treeNotebookSearchTabTreeCtrl.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginLabelEdit)
# self.treeNotebookSearchTabTreeCtrl.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndLabelEdit)
# Add a method to handle right-mouse-down, which should cause a popup menu
self.treeNotebookSearchTabTreeCtrl.Bind(wx.EVT_RIGHT_DOWN, self.OnTreeCtrlRightDown)
# Set the Sizer on the Notes Search Tab
self.treeNotebookSearchTab.SetSizer(noteTreeCtrlSizer2)
# Fit the controls on the Sizer
self.treeNotebookSearchTab.Fit()
# Add the Notebook to the Note Tree Panel Sizer
noteTreeSizer.Add(self.treeNotebook, 1, wx.ALL | wx.EXPAND, 2)
# Set the Sizer on the Note Tree Panel
self.treePanel.SetSizer(noteTreeSizer)
# Fit the controls on the Sizer
self.treePanel.Fit()
# Place a Transana Notes Editor Panel in the other half of the Splitter Window
self.noteEdit = NoteEditor._NotePanel(splitter)
# Disable the Notes Editor controls initially
self.noteEdit.EnableControls(False)
# Set the minimum Pane size for the splitter
splitter.SetMinimumPaneSize(100)
# Set the initial split position for the spliter window
splitter.SplitVertically(self.treePanel, self.noteEdit, 300)
# Set the Dialog's Main Sizer
self.SetSizer(mainSizer)
# Add a Page Changing event to the notebook
self.treeNotebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnNotebookPageChanging)
# Add a Page Changed event to the notebook
self.treeNotebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnNotebookPageChanged)
# Capture the Dialog's Close Event
wx.EVT_CLOSE(self, self.OnClose)
# Center the Notes Browser on the screen.
self.CentreOnScreen()
def Register(self, ControlObject=None):
""" Register a Control Object for the Notes Browser to interact with. """
# Register the Control Object with the Notes Browser Object
self.ControlObject = ControlObject
# Register the Notes Browser Object with the Control Object
if self.ControlObject != None:
self.ControlObject.Register(NotesBrowser=self)
def AddNote(self, tree, rootNode, noteNum, noteID, seriesNum, episodeNum, transcriptNum, collectionNum, clipNum, snapshotNum, documentNum, quoteNum, noteTaker):
# If we have a Library Note ...
if seriesNum > 0:
# Load the Library to get the needed data
tempLibrary = Library.Library(seriesNum)
# The path to the Note is the Library Name
pathData = tempLibrary.id
# Create the Node Data
nodeData = DatabaseTreeTab._NodeData('NoteNode', noteNum, seriesNum)
# If we have a Document Note ...
elif documentNum > 0:
# Load the Document and Library to get the needed data
tempDocument = Document.Document(documentNum)
tempLibrary = Library.Library(tempDocument.library_num)
# The path to the Note is the Library Name and the Document Name
pathData = tempLibrary.id + ' > ' + tempDocument.id
# Create the Node Data
nodeData = DatabaseTreeTab._NodeData('NoteNode', noteNum, documentNum)
# If we have an Episode Note ...
elif episodeNum > 0:
# Load the Episode and Library to get the needed data
tempEpisode = Episode.Episode(episodeNum)
tempLibrary = Library.Library(tempEpisode.series_num)
# The path to the Note is the Library Name and the Episode Name
pathData = tempLibrary.id + ' > ' + tempEpisode.id
# Create the Node Data
nodeData = DatabaseTreeTab._NodeData('NoteNode', noteNum, episodeNum)
# If we have a Transcript Note ...
elif transcriptNum > 0:
# Load the Transcript, Episode and Library to get the needed data
# To save time here, we can skip loading the actual transcript text, which can take time once we start dealing with images!
tempTranscript = Transcript.Transcript(transcriptNum, skipText=True)
tempEpisode = Episode.Episode(tempTranscript.episode_num)
tempLibrary = Library.Library(tempEpisode.series_num)
# The path to the Note is the Library Name, the Episode Name, and the Transcript Name
pathData = tempLibrary.id + ' > ' + tempEpisode.id + ' > ' + tempTranscript.id
# Create the Node Data
nodeData = DatabaseTreeTab._NodeData('NoteNode', noteNum, transcriptNum)
# If we have a Collection Note ...
elif collectionNum > 0:
# Load the Collection to get the needed data
tempCollection = Collection.Collection(collectionNum)
# The path to the Note is the Collection's Node String
pathData = tempCollection.GetNodeString()
# Create the Node Data
nodeData = DatabaseTreeTab._NodeData('NoteNode', noteNum, collectionNum)
# If we have a Clip Note ...
elif clipNum > 0:
# Load the Clip and Collection to get the needed data. We can skip loading the Clip Transcript to save load time
tempClip = Clip.Clip(clipNum, skipText=True)
tempCollection = Collection.Collection(tempClip.collection_num)
# The path to the Note is the Collection's Node String and the Clip Name
pathData = tempCollection.GetNodeString() + ' > ' + tempClip.id
# Create the Node Data
nodeData = DatabaseTreeTab._NodeData('NoteNode', noteNum, clipNum)
# If we have a Snapshot Note ...
elif snapshotNum > 0:
# Load the Snapshot and Collection to get the needed data.
tempSnapshot = Snapshot.Snapshot(snapshotNum)
tempCollection = Collection.Collection(tempSnapshot.collection_num)
# The path to the Note is the Collection's Node String and the Clip Name
pathData = tempCollection.GetNodeString() + ' > ' + tempSnapshot.id
# Create the Node Data
nodeData = DatabaseTreeTab._NodeData('NoteNode', noteNum, snapshotNum)
# If we have a Quote Note ...
elif quoteNum > 0:
# Load the Quote and Collection to get the needed data.
tempQuote = Quote.Quote(num=quoteNum)
tempCollection = Collection.Collection(tempQuote.collection_num)
# The path to the Note is the Collection's Node String and the Clip Name
pathData = tempCollection.GetNodeString() + ' > ' + tempQuote.id
# Create the Node Data
nodeData = DatabaseTreeTab._NodeData('NoteNode', noteNum, quoteNum)
# Otherwise, we have an undefined note. (This should never get called)
else:
# There is no Root Node
rootNode = None
# There is no path to the data
pathData = ''
# There is no Node Data
nodeData = None
# If we have a Root node ...
if rootNode != None:
# ... create the Tree Item for the Note ...
item = tree.AppendItem(rootNode, noteID)
# ... add the Item Data ...
tree.SetPyData(item, nodeData)
# ... add the path data if defined ...
if pathData != '':
item2 = tree.AppendItem(item, pathData)
# ... and add the note taker information if defined.
if noteTaker != '':
item3 = tree.AppendItem(item, noteTaker)
def PopulateTreeCtrl(self, tree, searchText=None):
""" Populate the Notes Browser Tree Controls, limiting by SearchText if such a value is passed in """
# Clear all the nodes from the Tree. We're starting over.
tree.DeleteAllItems()
# Include the Database Name as the Tree Root
prompt = _('Database: %s')
# Encode the Database Name if necessary
if ('unicode' in wx.PlatformInfo):
prompt = unicode(prompt, 'utf8')
# Add the Database Name to the prompt
prompt = prompt % TransanaGlobal.configData.database
# If searchText is specified, add it to the Tree Root prompt
if searchText != None:
prompt += ' ' + unicode(_('Text: %s'), 'utf8') % searchText
# Add the Tree's root node
root = tree.AddRoot(prompt)
tree.SetPyData(root, DatabaseTreeTab._NodeData('RootNode'))
# Add a Library Node at the first level
LibraryNode = tree.AppendItem(root, _('Library'))
tree.SetPyData(LibraryNode, DatabaseTreeTab._NodeData('LibraryNode'))
if TransanaConstants.proVersion:
# Add a Document Node at the first level
documentNode = tree.AppendItem(root, _("Document"))
tree.SetPyData(documentNode, DatabaseTreeTab._NodeData('DocumentNode'))
# Add an Episode Node at the first level
episodeNode = tree.AppendItem(root, _("Episode"))
tree.SetPyData(episodeNode, DatabaseTreeTab._NodeData('EpisodeNode'))
# Add a Transcript Node at the first level
transcriptNode = tree.AppendItem(root, _("Transcript"))
tree.SetPyData(transcriptNode, DatabaseTreeTab._NodeData('TranscriptNode'))
# Add a Collection Node at the first level
collectionNode = tree.AppendItem(root, _("Collection"))
tree.SetPyData(collectionNode, DatabaseTreeTab._NodeData('CollectionNode'))
if TransanaConstants.proVersion:
# Add a Quote Node at the first level
quoteNode = tree.AppendItem(root, _("Quote"))
tree.SetPyData(quoteNode, DatabaseTreeTab._NodeData('QuoteNode'))
# Add a Clip Node at the first level
clipNode = tree.AppendItem(root, _("Clip"))
tree.SetPyData(clipNode, DatabaseTreeTab._NodeData('ClipNode'))
if TransanaConstants.proVersion:
# Add a Snapshot Node at the first level
snapshotNode = tree.AppendItem(root, _("Snapshot"))
tree.SetPyData(snapshotNode, DatabaseTreeTab._NodeData('SnapshotNode'))
# Get a list of all Notes from the Database
notes = DBInterface.list_of_all_notes(searchText=searchText)
# Iterate through the list of notes
for note in notes:
if note['SeriesNum'] > 0:
rootNode = LibraryNode
elif TransanaConstants.proVersion and (note['DocumentNum'] > 0):
rootNode = documentNode
elif note['EpisodeNum'] > 0:
rootNode = episodeNode
elif note['TranscriptNum'] > 0:
rootNode = transcriptNode
elif note['CollectNum'] > 0:
rootNode = collectionNode
elif TransanaConstants.proVersion and (note['QuoteNum'] > 0):
rootNode = quoteNode
elif note['ClipNum'] > 0:
rootNode = clipNode
elif TransanaConstants.proVersion and (note['SnapshotNum'] > 0):
rootNode = snapshotNode
else:
rootNode = None
if rootNode != None:
self.AddNote(tree, rootNode, note['NoteNum'], note['NoteID'], note['SeriesNum'], note['EpisodeNum'], note['TranscriptNum'], note['CollectNum'], note['ClipNum'], note['SnapshotNum'], note['DocumentNum'], note['QuoteNum'], note['NoteTaker'])
# Expand the root node so we see the first level nodes
tree.Expand(root)
# Return the root node.
return root
def FindTreeNode(self, note):
""" Find the NODE for a specific Note Object """
# Get the Node Data for the Note Object passed in.
(nodeData, nodeType) = self.GetNodeData(note)
# Initialize that we want to continue looking for the right node
contin = True
# Start at the Root Node of the Notes Tab
rootNode = self.treeNotebookNotesTabTreeCtrl.GetRootItem()
# Get the first child from the Root
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(rootNode)
# While we have valid child nodes and have not yet found what we're looking for ...
while childNode.IsOk() and contin:
# Get the Node Data for the child we're currently examining
childNodeData = self.treeNotebookNotesTabTreeCtrl.GetPyData(childNode)
# See if the Node Type for the Note matches the Node Type for the node we're looking at ...
if ((nodeType == 'LibraryNoteNode') and (childNodeData.nodetype == 'LibraryNode')) or \
((nodeType == 'DocumentNoteNode') and (childNodeData.nodetype == 'DocumentNode')) or \
((nodeType == 'EpisodeNoteNode') and (childNodeData.nodetype == 'EpisodeNode')) or \
((nodeType == 'TranscriptNoteNode') and (childNodeData.nodetype == 'TranscriptNode')) or \
((nodeType == 'CollectionNoteNode') and (childNodeData.nodetype == 'CollectionNode')) or \
((nodeType == 'QuoteNoteNode') and (childNodeData.nodetype == 'QuoteNode')) or \
((nodeType == 'ClipNoteNode') and (childNodeData.nodetype == 'ClipNode')) or \
((nodeType == 'SnapshotNoteNode') and (childNodeData.nodetype == 'SnapshotNode')):
# ... if so, start looking at the children of this node ...
(noteNode, cookie2) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(childNode)
# While we have valid grandchild nodes and have not yet found what we're looking for ...
while noteNode.IsOk() and contin:
# ... see if the node text matches the Note's OLD name (a parameter used only for renaming!)
# AND has the correct Note Number
if (self.treeNotebookNotesTabTreeCtrl.GetItemText(noteNode) == note.id) and \
(self.treeNotebookNotesTabTreeCtrl.GetPyData(noteNode).recNum == note.number):
# If it matches, we've found the node!.
return noteNode
# If the text is NOT a match ...
else:
# ... continue on to the next grandchild node
(noteNode, cookie2) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(childNode, cookie2)
# if we have NOT found what we're looking for ...
else:
# ... then look at the next child node.
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(rootNode, cookie)
# If the node is not found, return None
return None
def UpdateTreeCtrl(self, action, note=None, oldName=''):
""" Update the Tree Control based on an external event, such as multi-user communication """
# If we're on the Notes tab, not the Note Search tab ...
if self.treeNotebook.GetCurrentPage() == self.treeNotebookNotesTab:
# If we're adding a new Note ...
if action == 'A':
# Get the Node Data for the Note Object passed in.
(nodeData, nodeType) = self.GetNodeData(note)
# Initialize that we want to continue looking for the right node
contin = True
# Start at the Root Node of the Notes Tab
rootNode = self.treeNotebookNotesTabTreeCtrl.GetRootItem()
# Get the first child from the Root
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(rootNode)
# While we have valid child nodes and have not yet found what we're looking for ...
while childNode.IsOk() and contin:
# Get the Node Data for the child we're currently examining
childNodeData = self.treeNotebookNotesTabTreeCtrl.GetPyData(childNode)
# See if the Node Type for the Note matches the Node Type for the node we're looking at ...
if ((nodeType == 'LibraryNoteNode') and (childNodeData.nodetype == 'LibraryNode')) or \
((nodeType == 'DocumentNoteNode') and (childNodeData.nodetype == 'DocumentNode')) or \
((nodeType == 'EpisodeNoteNode') and (childNodeData.nodetype == 'EpisodeNode')) or \
((nodeType == 'TranscriptNoteNode') and (childNodeData.nodetype == 'TranscriptNode')) or \
((nodeType == 'CollectionNoteNode') and (childNodeData.nodetype == 'CollectionNode')) or \
((nodeType == 'QuoteNoteNode') and (childNodeData.nodetype == 'QuoteNode')) or \
((nodeType == 'ClipNoteNode') and (childNodeData.nodetype == 'ClipNode')) or \
((nodeType == 'SnapshotNoteNode') and (childNodeData.nodetype == 'SnapshotNode')):
# ... if so, add the Note to the child node ...
self.AddNote(self.treeNotebookNotesTabTreeCtrl, childNode, note.number, note.id, note.series_num, note.episode_num, note.transcript_num, note.collection_num, note.clip_num, note.snapshot_num, note.document_num, note.quote_num, note.author)
# ... and indicate we can stop looking
contin = False
# if we have NOT found what we're looking for ...
else:
# ... then look at the next child node.
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(rootNode, cookie)
# If we're renaming a Note ...
elif action == 'R':
# Get the Node Data for the Note Object passed in.
(nodeData, nodeType) = self.GetNodeData(note)
# Initialize that we want to continue looking for the right node
contin = True
# Start at the Root Node of the Notes Tab
rootNode = self.treeNotebookNotesTabTreeCtrl.GetRootItem()
# Get the first child from the Root
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(rootNode)
# While we have valid child nodes and have not yet found what we're looking for ...
while childNode.IsOk() and contin:
# Get the Node Data for the child we're currently examining
childNodeData = self.treeNotebookNotesTabTreeCtrl.GetPyData(childNode)
# See if the Node Type for the Note matches the Node Type for the node we're looking at ...
if ((nodeType == 'LibraryNoteNode') and (childNodeData.nodetype == 'LibraryNode')) or \
((nodeType == 'DocumentNoteNode') and (childNodeData.nodetype == 'DocumentNode')) or \
((nodeType == 'EpisodeNoteNode') and (childNodeData.nodetype == 'EpisodeNode')) or \
((nodeType == 'TranscriptNoteNode') and (childNodeData.nodetype == 'TranscriptNode')) or \
((nodeType == 'CollectionNoteNode') and (childNodeData.nodetype == 'CollectionNode')) or \
((nodeType == 'QuoteNoteNode') and (childNodeData.nodetype == 'QuoteNode')) or \
((nodeType == 'ClipNoteNode') and (childNodeData.nodetype == 'ClipNode')) or \
((nodeType == 'SnapshotNoteNode') and (childNodeData.nodetype == 'SnapshotNode')):
# ... if so, start looking at the children of this node ...
(noteNode, cookie2) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(childNode)
# While we have valid grandchild nodes and have not yet found what we're looking for ...
while noteNode.IsOk() and contin:
# Get the Node Data for the NOTE we're currently examining ...
noteNodeData = self.treeNotebookNotesTabTreeCtrl.GetPyData(noteNode)
# ... see if the node text matches the Note's OLD name (a parameter used only for renaming!) AND
# make sure the note's Number matches correctly so the wrong entry (with duplicate name) doesn't get renamed!
if (self.treeNotebookNotesTabTreeCtrl.GetItemText(noteNode) == oldName) and \
(noteNodeData.recNum == note.number):
# If it matches, update the Node's text to the Note's NEW name ...
self.treeNotebookNotesTabTreeCtrl.SetItemText(noteNode, note.id)
# ... and signal that we're done.
contin = False
# If the text is NOT a match ...
else:
# ... continue on to the next grandchild node
(noteNode, cookie2) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(childNode, cookie2)
# if we have NOT found what we're looking for ...
else:
# ... then look at the next child node.
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(rootNode, cookie)
# If we're deleting a Note ...
elif action == 'D':
# Initialize that we want to continue looking for the right node
contin = True
# Start at the Root Node of the Notes Tab
rootNode = self.treeNotebookNotesTabTreeCtrl.GetRootItem()
# Get the first child from the Root
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(rootNode)
# While we have valid child nodes and have not yet found what we're looking for ...
while childNode.IsOk() and contin:
# See if the node's text matches the first part of the note tuple (not a note object) passed.
# (Translate the object type and convert it to Unicode HERE, not before sending!)
if self.treeNotebookNotesTabTreeCtrl.GetItemText(childNode) == unicode(_(note[0]), 'utf8'):
# If so, get the first grandchild (note) node
(noteNode, cookie2) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(childNode)
# While we have valid grandchild nodes and have not yet found what we're looking for ...
while noteNode.IsOk() and contin:
(pathNode, cookie3) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(noteNode)
# ... see if the node text matches the Note's ID (passed in the second part of the "note" tuple)
# AND make sure the note's full object path matches correctly
if (self.treeNotebookNotesTabTreeCtrl.GetItemText(noteNode) == note[1][-1]) and \
(list(note[1][1:-1]) == self.treeNotebookNotesTabTreeCtrl.GetItemText(pathNode).split(' > ')):
# If so, remove that node from the tree ...
self.treeNotebookNotesTabTreeCtrl.Delete(noteNode)
# ... and signal that we're done.
contin = False
# If the text is NOT a match ...
else:
# ... continue on to the next grandchild node
(noteNode, cookie2) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(childNode, cookie2)
# This shouldn't happen, but if we don't find the Note node, get the next CHILD node!
if not noteNode.IsOk():
# ... then look at the next child node.
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(rootNode, cookie)
# If the text is NOT a match ...
else:
# ... then look at the next child node.
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(rootNode, cookie)
# If we need to CHECK the Note Tree (as when higher-level nodes get deleted) ...
elif action == 'C':
# Start at the Root Node of the Notes Tab
rootNode = self.treeNotebookNotesTabTreeCtrl.GetRootItem()
# Get the first child from the Root
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(rootNode)
# Initialize a list of nodes to delete
nodesToDelete = []
# While we have valid child nodes and have not yet found what we're looking for ...
while childNode.IsOk():
# Get the first grandchild (note) node
(noteNode, cookie2) = self.treeNotebookNotesTabTreeCtrl.GetFirstChild(childNode)
# While we have valid grandchild nodes and have not yet found what we're looking for ...
while noteNode.IsOk():
# Start exception handling
try:
# Try to load the note associated with the current node
tempNote = Note.Note(self.treeNotebookNotesTabTreeCtrl.GetPyData(noteNode).recNum)
# If the Note record is not found in the database ...
except TransanaExceptions.RecordNotFoundError:
# ... then the note node needs to be deleted. We can't delete it yet, though, or the GetNext fails,
# so we'll just remember to delete it later.
nodesToDelete.append(noteNode)
# If any exception other than RecordNotFoundError is raised ...
except:
# ... we don't need to do anything, I guess.
pass
# Get the next grandchild node
(noteNode, cookie2) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(childNode, cookie2)
# ... then look at the next child node.
(childNode, cookie) = self.treeNotebookNotesTabTreeCtrl.GetNextChild(rootNode, cookie)
# If we have any nodes to delete ... (We need to defer the delete on the Mac or the tree doesn't process correctly!)
for nodeToDelete in nodesToDelete:
# ... then delete them.
self.treeNotebookNotesTabTreeCtrl.Delete(nodeToDelete)
# This should NEVER get triggered!
else:
print 'NotesBrowser.UpdateTreeCtrl(): Unknown action "%s"' % action
print note
print
def GetNodeData(self, note, idChanged=False):
""" Return the Node Data for the Note object passed in. idChanged indicates whether to use the note ID
or its original value, which could be different. """
# Initialize the return values to blanks
nodeData = ''
nodeType = ''
# If it's possible the Note ID has been changed ...
if idChanged:
# use the saved Original Note ID
noteIDToUse = self.originalNoteID
# If it's NOT possible the note ID has been changed ...
else:
# ... then we can just use the Note ID.
# (When working with a note locked by another user, originalNoteID may be BLANK!)
noteIDToUse = note.id
# Determine what type of note we have and set the appropriate data.
# If we have a Library note ...
if note.series_num != 0:
# ... load the Library data ...
tempLibrary = Library.Library(note.series_num)
# ... set up the Tree Node data ...
nodeData = ("Libraries", tempLibrary.id, noteIDToUse)
# ... and signal that it's a Library Note.
nodeType = 'LibraryNoteNode'
# if we have a Document note ...
elif note.document_num != 0:
# ... load the Library and Document data ...
tempDocument = Document.Document(note.document_num)
tempLibrary = Library.Library(tempDocument.library_num)
# ... set up the Tree Node data ...
nodeData = ("Libraries", tempLibrary.id, tempDocument.id, noteIDToUse)
# ... and signal that it's a Document Note.
nodeType = 'DocumentNoteNode'
# if we have an Episode note ...
elif note.episode_num != 0:
# ... load the Library and Episode data ...
tempEpisode = Episode.Episode(note.episode_num)
tempLibrary = Library.Library(tempEpisode.series_num)
# ... set up the Tree Node data ...
nodeData = ("Libraries", tempLibrary.id, tempEpisode.id, noteIDToUse)
# ... and signal that it's an Episode Note.
nodeType = 'EpisodeNoteNode'
# if we have a Transcript note ...
elif note.transcript_num != 0:
# ... load the Library, Episode, and Transcript data ...
# To save time here, we can skip loading the actual transcript text, which can take time once we start dealing with images!
tempTranscript = Transcript.Transcript(note.transcript_num, skipText=True)
tempEpisode = Episode.Episode(tempTranscript.episode_num)
tempLibrary = Library.Library(tempEpisode.series_num)
# ... set up the Tree Node data ...
nodeData = ("Libraries", tempLibrary.id, tempEpisode.id, tempTranscript.id, noteIDToUse)
# ... and signal that it's a Transcript Note.
nodeType = 'TranscriptNoteNode'
# if we have a Collection note ...
elif note.collection_num != 0:
# ... load the Collection data ...
tempCollection = Collection.Collection(note.collection_num)
# ... set up the Tree Node data ...
nodeData = ("Collections",) + tempCollection.GetNodeData() + (noteIDToUse,)
# ... and signal that it's a Collection Note.
nodeType = 'CollectionNoteNode'
# if we have a Quote note ...
elif note.quote_num != 0:
# ... load the Collection and Quote data
tempQuote = Quote.Quote(note.quote_num)
tempCollection = Collection.Collection(tempQuote.collection_num)
# ... set up the Tree Node data ...
nodeData = ("Collections",) + tempCollection.GetNodeData() + (tempQuote.id, noteIDToUse,)
# ... and signal that it's a Quote Note.
nodeType = 'QuoteNoteNode'
# if we have a Clip note ...
elif note.clip_num != 0:
# ... load the Collection and Clip data .... We can skip loading the Clip Transcript to save load time
tempClip = Clip.Clip(note.clip_num, skipText=True)
tempCollection = Collection.Collection(tempClip.collection_num)
# ... set up the Tree Node data ...
nodeData = ("Collections",) + tempCollection.GetNodeData() + (tempClip.id, noteIDToUse,)
# ... and signal that it's a Clip Note.
nodeType = 'ClipNoteNode'
# if we have a Snapshot note ...
elif note.snapshot_num != 0:
# ... load the Collection and Snapshot data ....
tempSnapshot = Snapshot.Snapshot(note.snapshot_num)
tempCollection = Collection.Collection(tempSnapshot.collection_num)
# ... set up the Tree Node data ...
nodeData = ("Collections",) + tempCollection.GetNodeData() + (tempSnapshot.id, noteIDToUse,)
# ... and signal that it's a Snapshot Note.
nodeType = 'SnapshotNoteNode'
return (nodeData, nodeType)
def OpenNote(self, noteNum):
""" Open the specified Note (a note number is passed in) in the Note Browser """
# If there is already an open Note ...
if self.activeNote != None:
# If the open note is a different number than the new note ...
if self.activeNote.number != noteNum:
# ... save the Active Note
self.SaveNoteAndClear()
# If the new note is already open ...
else:
# ... there's nothing more to do!
return
# We need exception handling here
try:
# Make sure the Notes page is showing
self.treeNotebook.SetSelection(0)
# Create a Note object and populated it for the selected note
self.activeNote = Note.Note(noteNum)
# Get the note's Tree Node
sel_item = self.FindTreeNode(self.activeNote)
# So long as the tree node is found ...
if sel_item != None:
# Try to lock the note
self.activeNote.lock_record()
# Remember the note's ID so we can tell later if it's changed.
self.originalNoteID = self.activeNote.id
# If successful, enable the Note Editor controls ...
self.noteEdit.EnableControls(True)
# ... and populate the Note Editor with the note's text
self.noteEdit.set_text(self.activeNote.text)
# Make sure the new note is Selected
self.treeNotebookNotesTabTreeCtrl.SelectItem(sel_item)
# Make sure the new node is Visible
self.treeNotebookNotesTabTreeCtrl.EnsureVisible(sel_item)
# Make sure the selected object is expanded
self.treeNotebookNotesTabTreeCtrl.Expand(sel_item)
# If we're on the Note Search tab ...
if (self.treeNotebookNotesTabTreeCtrl == self.treeNotebookSearchTabTreeCtrl):
# ... then take the search text and stick it in the Note Search Text box too!
self.noteEdit.SetSearchText(self.noteSearch.GetValue())
# Trap Record Lock exceptions
except TransanaExceptions.RecordLockedError, err:
# If the note is locked, we need to inform the user of that fact. First create a prompt
# and encode it as needed.
if 'unicode' in wx.PlatformInfo:
# Encode with UTF-8 rather than TransanaGlobal.encoding because this is a prompt, not DB Data.
prompt = unicode(_('Note "%s" is locked by %s\n'), 'utf8')
else:
prompt = _('Note "%s" is locked by %s\n')
# Populate the read-only Note Editor with the lock prompt and the note text.
self.noteEdit.set_text(prompt % (self.activeNote.id, self.activeNote.record_lock) +
'\n\n%s' % self.activeNote.text)
# Since the record is locked and the control read-only, we can immediately forget that we
# have an active note.
self.activeNote = None
# Make sure the new note is Selected (which does NOT cause lock problems now that activeNote is None)
self.treeNotebookNotesTabTreeCtrl.SelectItem(sel_item)
# Make sure the new node is Visible
self.treeNotebookNotesTabTreeCtrl.EnsureVisible(sel_item)
# Make sure the selected object is expanded
self.treeNotebookNotesTabTreeCtrl.Expand(sel_item)
# Trap Record Not Found exception, which is triggered if a different user deletes a Note's Parent, grandparent, etc.
# while this user has the Notes Browser open, then this user selects the deleted Note. (Deleting the note itself
# causes it to be removed form the Notes Browser, but not deleting its ancestors.)
except TransanaExceptions.RecordNotFoundError, err:
# We can immediately forget that we have an active note, since the note doesn't exist!
self.activeNote = None
# Display an error message to the user
errordlg = Dialogs.ErrorDialog(None, err.explanation)
errordlg.ShowModal()
errordlg.Destroy()
def SaveNoteAndClear(self):
# Let's check to see if we have a note that is already open.
if (self.activeNote != None):
# Start exception handling in case there's a SaveError
try:
# DON'T CHECK if the note's changed here. The ID could be changed!
# ... get the text from the note edit control ...
self.activeNote.text = self.noteEdit.get_text()
# ... and save the active note.
self.activeNote.db_save()
# Unlock the record for the active note
self.activeNote.unlock_record()
# Clear the active node
self.activeNote = None
self.originalNoteID = ''
except TransanaExceptions.SaveError:
# Display the Error Message, allow "continue" flag to remain true
errordlg = Dialogs.ErrorDialog(None, sys.exc_info()[1].reason)
errordlg.ShowModal()
errordlg.Destroy()
# Reset the Note Editor to blank
self.noteEdit.set_text('')
# Disable the Note Editor controls
self.noteEdit.EnableControls(False)
def OnNotebookPageChanging(self, event):
""" This method is triggered when the user starts to change Notebook pages """
# When we start changing tabs, we need to save and clear the active note
self.SaveNoteAndClear()
def OnNotebookPageChanged(self, event):
""" This method is triggered when the user has changed Notebook pages """
# Allow the default method to process as needed (so the Notebook page changes)
event.Skip()
# Determine which page we've changed to and update the Tree control. (If we renamed something, it'll be
# out of date on the tab.
if self.treeNotebook.GetPageText(event.GetSelection()) == unicode(_("Notes"), 'utf8'):
self.notesRoot = self.PopulateTreeCtrl(self.treeNotebookNotesTabTreeCtrl)
elif self.treeNotebook.GetPageText(event.GetSelection()) == unicode(_("Note Search"), 'utf8'):
self.searchRoot = self.PopulateTreeCtrl(self.treeNotebookSearchTabTreeCtrl, self.noteSearch.GetValue())
def OnTreeItemSelected(self, event):
""" Process selection of a tree node """
# Determine which tree is the source of the selection, the Notes tree or the Note Search tree
if event.GetId() == self.treeNotebookNotesTabTreeCtrl.GetId():
tree = self.treeNotebookNotesTabTreeCtrl
elif event.GetId() == self.treeNotebookSearchTabTreeCtrl.GetId():
tree = self.treeNotebookSearchTabTreeCtrl
else:
tree = None
# If we know the source of the selection ...
if tree != None:
# Save the Active Note, if there is one
self.SaveNoteAndClear()
# Get the selected item
sel_item = tree.GetSelection()
# Get the selected item's data
sel_item_data = tree.GetPyData(sel_item)
# If we have selected a node that HAS data, and it's a NOTE node ...
if (sel_item_data != None) and (sel_item_data.nodetype == 'NoteNode'):
# We need exception handling here
try:
# Create a Note object and populated it for the selected note
self.activeNote = Note.Note(sel_item_data.recNum)
# Try to lock the note
self.activeNote.lock_record()
# Remember the note's ID so we can tell later if it's changed.
self.originalNoteID = self.activeNote.id
# If successful, enable the Note Editor controls ...
self.noteEdit.EnableControls(True)
# ... and populate the Note Editor with the note's text
self.noteEdit.set_text(self.activeNote.text)
# Make sure the selected object is expanded
tree.Expand(sel_item)
# If we're on the Note Search tab ...
if (tree == self.treeNotebookSearchTabTreeCtrl):
# ... then take the search text and stick it in the Note Search Text box too!
self.noteEdit.SetSearchText(self.noteSearch.GetValue())
# Trap Record Lock exceptions
except TransanaExceptions.RecordLockedError, err:
# If the note is locked, we need to inform the user of that fact. First create a prompt
# and encode it as needed.
if 'unicode' in wx.PlatformInfo:
# Encode with UTF-8 rather than TransanaGlobal.encoding because this is a prompt, not DB Data.
prompt = unicode(_('Note "%s" is locked by %s\n'), 'utf8')
else:
prompt = _('Note "%s" is locked by %s\n')
# Populate the read-only Note Editor with the lock prompt and the note text.
self.noteEdit.set_text(prompt % (self.activeNote.id, self.activeNote.record_lock) +
'\n\n%s' % self.activeNote.text)
# Since the record is locked and the control read-only, we can immediately forget that we
# have an active note.
self.activeNote = None
# Trap Record Not Found exception, which is triggered if a different user deletes a Note's Parent, grandparent, etc.
# while this user has the Notes Browser open, then this user selects the deleted Note. (Deleting the note itself
# causes it to be removed form the Notes Browser, but not deleting its ancestors.)
except TransanaExceptions.RecordNotFoundError, err:
# We can immediately forget that we have an active note, since the note doesn't exist!
self.activeNote = None
# Display an error message to the user
errordlg = Dialogs.ErrorDialog(None, err.explanation)
errordlg.ShowModal()
errordlg.Destroy()
def OnBeginLabelEdit(self, event):
""" Handle the start of editing Tree Labels """
# LABEL EDITING disabled due to problems. If you start editing a label, but click elsewhere
# before completing the edit, bad things happen. For example, start editing a label, and before
# you press ENTER to complete the edit, right-click a DIFFERENT note. The WRONG note gets renamed!
# Or click on the "Export Note to Text" button. The edit just gets lost, and Transana-MU gets
# out of synch.
# Don't allow label editing!
event.Veto()
## # Determine which tree is the source of the selection, the Notes tree or the Note Search tree
## if event.GetId() == self.treeNotebookNotesTabTreeCtrl.GetId():
## tree = self.treeNotebookNotesTabTreeCtrl
## elif event.GetId() == self.treeNotebookSearchTabTreeCtrl.GetId():
## tree = self.treeNotebookSearchTabTreeCtrl
## else:
## tree = None
## # If we know the source of the selection ...
## if tree != None:
## # Get the current selected item.
## sel_item = tree.GetSelection()
## # Get the item data for the current selection
## sel_item_data = tree.GetPyData(sel_item)
## # If the item data is None or not a Note Node or the note is locked by someone else ...
## if (sel_item_data == None) or (sel_item_data.nodetype != 'NoteNode') or (self.activeNote == None) or \
## (not self.activeNote.isLocked):
## # ... we need to cancel the Label Edit
## event.Veto()
## # If we don't have a known tree ...
## else:
## # ... we need to cancel the Label Edit
## event.Veto()
## def OnEndLabelEdit(self, event):
## """ Handle the end of editing Tree Labels """
## # Determine which tree is the source of the selection, the Notes tree or the Note Search tree
## if event.GetId() == self.treeNotebookNotesTabTreeCtrl.GetId():
## tree = self.treeNotebookNotesTabTreeCtrl
## elif event.GetId() == self.treeNotebookSearchTabTreeCtrl.GetId():
## tree = self.treeNotebookSearchTabTreeCtrl
## else:
## tree = None
## # If we know the source of the selection ...
## if tree != None:
## # Get the current selected item.
## sel_item = tree.GetSelection()
## # Get the item data for the current selection
## sel_item_data = tree.GetPyData(sel_item)
## # If there is an active Note and we have it locked and the user didn't cancel the edit...
## if (self.activeNote != None) and self.activeNote.isLocked and not event.IsEditCancelled():
## # ... update the note ID to match the changed Label
## self.activeNote.id = event.GetLabel().strip()
## # Start exception handling
## try:
## # We need to save here, so we can still veto the change if there's a save error!
## self.activeNote.db_save()
## # If we've changed the Note ID ...
## if self.activeNote.id != self.originalNoteID:
## # ... and we have a known ControlObject ...
## if self.ControlObject != None:
## # ... determine what type of note we have and set the appropriate data. It's LIKELY
## # that the node ID has changed here.
## (nodeData, nodeType) = self.GetNodeData(self.activeNote, idChanged=True)
## # As long as we have good data ...
## if nodeData != '':
## # inform the Database tree of the Note ID change. (We need to translate the Root node.)
## self.ControlObject.DataWindow.DBTab.tree.rename_Node((unicode(_(nodeData[0]), 'utf8'),) + nodeData[1:], nodeType, self.activeNote.id)
## # If we have a Chat Window (and thus are in MU) ...
## if TransanaGlobal.chatWindow != None:
## # Start building a Rename message with the Rename header and the node type.
## msg = "RN %s >|< " % nodeType
## # Add all the nodes, which we already have assembled ...
## for node in nodeData:
## msg += node + ' >|< '
## # ... and finish the message with the new name.
## msg += self.activeNote.id
## # Now send the message via the Chat Window.
## TransanaGlobal.chatWindow.SendMessage(msg)
## # Duplicate Note IDs or other problems can cause SaveError exceptions
## except TransanaExceptions.SaveError:
## # Display the Error Message, allow "continue" flag to remain true
## errordlg = Dialogs.ErrorDialog(None, sys.exc_info()[1].reason)
## errordlg.ShowModal()
## errordlg.Destroy()
## # Veto the Label Edit, since it was rejected
## event.Veto()
## # Restore the ActiveNote's ID to the original value
## self.activeNote.id = self.originalNoteID
## # If there's a problem ...
## else:
## # ... we need to cancel the Label Edit
## event.Veto()
## # if we don't have a known tree ...
## else:
## # ... we need to cancel the Label Edit. (We should never get here!)
## event.Veto()
def OnAllNotesSearch(self, event):
""" "Search All Notes" has been activated. We search for a string across all notes. """
# If the search text has changed ...
if self.searchText != self.noteSearch.GetValue():
# ... update the variable that remembers our search term
self.searchText = self.noteSearch.GetValue()
# If it's empty ...
if self.searchText == '':
# ... we need to pass "None" rather than an empty string so the query works right.
self.searchText = None
# Populate the Tree Control with all the Nodes and Notes it needs!
self.searchRoot = self.PopulateTreeCtrl(self.treeNotebookSearchTabTreeCtrl, self.searchText)
# Don't forget to call the parent method. Forgetting this causes problems when EVT_KILL_FOCUS calls this method
event.Skip()
def OnTreeCtrlRightDown(self, event):
""" Implement the right-click menus for the Tree Control """
# Determine which tree is the source of the selection, the Notes tree or the Note Search tree
if event.GetId() == self.treeNotebookNotesTabTreeCtrl.GetId():
self.activeTree = self.treeNotebookNotesTabTreeCtrl
elif event.GetId() == self.treeNotebookSearchTabTreeCtrl.GetId():
self.activeTree = self.treeNotebookSearchTabTreeCtrl
else:
self.activeTree = None
# If we know the source of the selection ...
if self.activeTree != None:
# See if the label is being edited ...
# if (self.activeTree.GetEditControl() != None) and not ('wxMac' in wx.PlatformInfo):
# If a label is being edited, we need to finish that before doing anything else!