Changeset 173
- Timestamp:
- 01/04/09 19:50:36 (4 years ago)
- Location:
- branches/mbutscher/next
- Files:
-
- 33 modified
-
Consts.py (modified) (2 diffs)
-
WikidPad.xrc (modified) (3 diffs)
-
WikidPadStarter.py (modified) (1 diff)
-
extensions/WikidPadParser.py (modified) (4 diffs)
-
lib/pwiki/AdditionalDialogs.py (modified) (19 diffs)
-
lib/pwiki/Configuration.py (modified) (1 diff)
-
lib/pwiki/DocPages.py (modified) (42 diffs)
-
lib/pwiki/DocStructureCtrl.py (modified) (1 diff)
-
lib/pwiki/Exporters.py (modified) (4 diffs)
-
lib/pwiki/I18nPoUpdater.py (modified) (1 diff)
-
lib/pwiki/Importers.py (modified) (4 diffs)
-
lib/pwiki/Localization.py (modified) (13 diffs)
-
lib/pwiki/OptionsDialog.py (modified) (1 diff)
-
lib/pwiki/PersonalWikiFrame.py (modified) (25 diffs)
-
lib/pwiki/Printing.py (modified) (1 diff)
-
lib/pwiki/PropertyHandling.py (modified) (6 diffs)
-
lib/pwiki/SearchAndReplaceDialogs.py (modified) (6 diffs)
-
lib/pwiki/SpellChecker.py (modified) (1 diff)
-
lib/pwiki/StringOps.py (modified) (5 diffs)
-
lib/pwiki/Utilities.py (modified) (9 diffs)
-
lib/pwiki/WikiHtmlViewIE.py (modified) (5 diffs)
-
lib/pwiki/WikiPyparsing.py (modified) (2 diffs)
-
lib/pwiki/WikiTreeCtrl.py (modified) (16 diffs)
-
lib/pwiki/WikiTxtCtrl.py (modified) (7 diffs)
-
lib/pwiki/timeView/TimelinePanel.py (modified) (2 diffs)
-
lib/pwiki/timeView/WikiWordListPopup.py (modified) (2 diffs)
-
lib/pwiki/wikidata/WikiDataManager.py (modified) (18 diffs)
-
lib/pwiki/wikidata/compact_sqlite/WikiData.py (modified) (8 diffs)
-
lib/pwiki/wikidata/original_gadfly/DbStructure.py (modified) (30 diffs)
-
lib/pwiki/wikidata/original_gadfly/WikiData.py (modified) (39 diffs)
-
lib/pwiki/wikidata/original_sqlite/DbStructure.py (modified) (2 diffs)
-
lib/pwiki/wikidata/original_sqlite/WikiData.py (modified) (9 diffs)
-
lib/pwiki/wxHelper.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/mbutscher/next/Consts.py
r169 r173 27 27 28 28 29 DEADBLOCKTIMEOUT = 12029 DEADBLOCKTIMEOUT = 30 30 30 31 31 32 32 # Scintilla known format types and numbers 33 33 FormatTypes = Enumeration("FormatTypes", ["Default", "WikiWord", 34 34 "AvailWikiWord", "Bold", "Italic", "Heading1", "Heading2", "Heading3", … … 36 36 ], 0) 37 37 38 39 # Store hints for WikiData.storeDataBlock() 40 41 DATABLOCK_STOREHINT_INTERN = 0 42 DATABLOCK_STOREHINT_EXTERN = 1 -
branches/mbutscher/next/WikidPad.xrc
r166 r173 640 640 <object class="sizeritem"> 641 641 <object class="wxStaticText"> 642 <label>Props. to</label> 643 <style></style> 642 <label>Attrs. to</label> 644 643 </object> 645 644 <option>0</option> … … 673 672 <object class="sizeritem"> 674 673 <object class="wxStaticText"> 675 <label>Props. to</label> 676 <style></style> 674 <label>Attrs. to</label> 677 675 </object> 678 676 <option>0</option> … … 1909 1907 <orient>wxVERTICAL</orient> 1910 1908 <object class="sizeritem"> 1911 <object class="wxStaticText"> 1912 <label>Change these only if you know what you are doing!</label> 1913 <fg>#A80000</fg> 1914 </object> 1915 <option>0</option> 1916 <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL</flag> 1917 <border>5</border> 1918 </object> 1919 <object class="sizeritem"> 1920 <object class="wxCheckBox" name="cbWikiLockFileIgnore"> 1921 <label>Ignore wiki lock file</label> 1922 </object> 1923 <option>0</option> 1924 <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL</flag> 1925 <border>5</border> 1926 </object> 1927 <object class="sizeritem"> 1928 <object class="wxCheckBox" name="cbWikiLockFileCreate"> 1929 <label>Create wiki lock file</label> 1930 </object> 1931 <option>0</option> 1932 <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL</flag> 1933 <border>5</border> 1909 <object class="wxStaticBoxSizer"> 1910 <orient>wxVERTICAL</orient> 1911 <object class="sizeritem"> 1912 <object class="wxStaticText"> 1913 <label>Change these only if you know what you are doing!</label> 1914 <fg>#A80000</fg> 1915 </object> 1916 <option>0</option> 1917 <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL</flag> 1918 <border>5</border> 1919 </object> 1920 <object class="sizeritem"> 1921 <object class="wxCheckBox" name="cbWikiLockFileIgnore"> 1922 <label>Ignore wiki lock file</label> 1923 </object> 1924 <option>0</option> 1925 <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL</flag> 1926 <border>5</border> 1927 </object> 1928 <object class="sizeritem"> 1929 <object class="wxCheckBox" name="cbWikiLockFileCreate"> 1930 <label>Create wiki lock file</label> 1931 </object> 1932 <option>0</option> 1933 <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL</flag> 1934 <border>5</border> 1935 </object> 1936 <label></label> 1937 </object> 1938 <option>0</option> 1939 <flag>wxALL|wxEXPAND</flag> 1940 <border>0</border> 1934 1941 </object> 1935 1942 <object class="spacer"> 1936 1943 <size>5,5</size> 1944 </object> 1945 <object class="sizeritem"> 1946 <object class="wxCheckBox" name="cbEditorUseImeWorkaround"> 1947 <label>Use IME workaround for editor input*</label> 1948 </object> 1949 <option>0</option> 1950 <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL</flag> 1951 <border>5</border> 1952 </object> 1953 <object class="spacer"> 1954 <size>5,5</size> 1955 </object> 1956 <object class="sizeritem"> 1957 <object class="wxStaticText" name=""> 1958 <label>* Needs WikidPad restart</label> 1959 </object> 1960 <option>0</option> 1961 <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL</flag> 1962 <border>5</border> 1937 1963 </object> 1938 1964 </object> -
branches/mbutscher/next/WikidPadStarter.py
r169 r173 166 166 exception = None 167 167 168 # 169 # try: 170 # import psyco 171 # psyco.full() 172 # except ImportError: 173 # traceback.print_exc() 174 175 176 168 177 169 178 def main(): -
branches/mbutscher/next/extensions/WikidPadParser.py
r171 r173 661 661 662 662 def actionWikiWordNcc(s, l, st, t): 663 t.wikiWord = t.findFlatByName("word") 664 if t.wikiWord is not None: 665 t.wikiWord = t.wikiWord.getString() 666 667 t.titleNode = t.findFlatByName("title") 668 663 669 fragmentNode = t.findFlatByName("searchFragment") 664 670 if fragmentNode is not None: … … 671 677 t.anchorLink = t.anchorLink.getString() 672 678 673 t.wikiWord = t.findFlatByName("word")674 if t.wikiWord is not None:675 t.wikiWord = t.wikiWord.getString()676 677 t.titleNode = t.findFlatByName("title")678 679 679 680 … … 710 711 t.anchorLink = t.anchorLink.getString() 711 712 712 t.titleNode = t.findFlatByName("title") 713 713 714 714 715 … … 791 792 792 793 # TODO anchor/fragment 793 wikiWordCc = Group(buildRegex(ur"\b(?<!~)" + WikiWordCcPAT + ur"\b", "word")) 794 wikiWordCc = buildRegex(ur"\b(?<!~)" + WikiWordCcPAT + ur"\b", "word") + \ 795 Optional(MatchFirst([searchFragmentExtern, wikiWordAnchorLink])) # Group( ) 794 796 wikiWordCc = wikiWordCc.setResultsName("wikiWord").setName("wikiWordCc")\ 795 797 .setParseStartAction(preActCheckWikiWordCcAllowed)\ -
branches/mbutscher/next/lib/pwiki/AdditionalDialogs.py
r171 r173 29 29 class SelectWikiWordDialog(wx.Dialog): 30 30 """ 31 Called for "Append/Prepend wiki word" in tree node con ext menu31 Called for "Append/Prepend wiki word" in tree node context menu 32 32 """ 33 33 34 def __init__(self, pWiki, ID, title="Select Wiki Word",34 def __init__(self, pWiki, parent, ID, title=u"Select Wiki Word", 35 35 pos=wx.DefaultPosition, size=wx.DefaultSize, 36 36 style=wx.NO_3D): … … 40 40 41 41 self.pWiki = pWiki 42 self.wikiWord = None 42 self.wikiWord = None 43 self.listContent = [] 44 self.ignoreTextChange = 0 45 43 46 res = wx.xrc.XmlResource.Get() 44 res.LoadOnDialog(self, self.pWiki, "SelectWikiWordDialog")47 res.LoadOnDialog(self, parent, "SelectWikiWordDialog") 45 48 46 49 self.SetTitle(title) … … 62 65 wx.EVT_LISTBOX_DCLICK(self, GUI_ID.lb, self.OnOk) 63 66 67 64 68 def OnOk(self, evt): 65 if not self.pWiki.getWikiDocument().isDefinedWikiWord(self.wikiWord): 66 words = self.pWiki.getWikiData().getWikiWordsWith(self.wikiWord, 67 True) 68 if len(words) > 0: 69 self.wikiWord = words[0] 70 else: 71 langHelper = wx.GetApp().createWikiLanguageHelper( 72 self.pWiki.getWikiDefaultWikiLanguage()) 73 wikiWord = langHelper.extractWikiWordFromLink(self.wikiWord, 74 self.pWiki.getWikiDocument()) 75 76 if wikiWord is None: 77 # Entered text is not a valid wiki word 78 self.ctrls.text.SetFocus() 79 return 80 81 self.wikiWord = wikiWord 69 sel = self.ctrls.lb.GetSelection() 70 if sel != wx.NOT_FOUND: 71 term = self.listContent[sel] 72 self.wikiWord = term[2] 73 else: 74 self.wikiWord = guiToUni(self.ctrls.text.GetValue()) 75 76 if not self.pWiki.getWikiDocument().isDefinedWikiWord(self.wikiWord): 77 terms = self.pWiki.getWikiData().getWikiWordMatchTermsWith( 78 self.wikiWord) 79 if len(terms) > 0: 80 self.wikiWord = terms[0][2] 81 else: 82 langHelper = wx.GetApp().createWikiLanguageHelper( 83 self.pWiki.getWikiDefaultWikiLanguage()) 84 wikiWord = langHelper.extractWikiWordFromLink(self.wikiWord, 85 self.pWiki.getWikiDocument()) 86 87 if wikiWord is None: 88 # Entered text is not a valid wiki word 89 # TODO Error message? 90 self.ctrls.text.SetFocus() 91 return 92 93 self.wikiWord = wikiWord 82 94 83 95 self.EndModal(wx.ID_OK) … … 88 100 89 101 def OnText(self, evt): 90 self.wikiWord = guiToUni(evt.GetString()) 102 if self.ignoreTextChange: 103 self.ignoreTextChange -= 1 104 return 105 106 text = guiToUni(evt.GetString()) 91 107 self.ctrls.lb.Clear() 92 if len(self.wikiWord) > 0: 93 words = self.pWiki.getWikiData().getWikiWordsWith(self.wikiWord, 94 True) 95 for word in words: 96 self.ctrls.lb.Append(word) 108 if len(text) > 0: 109 terms = self.pWiki.getWikiData().getWikiWordMatchTermsWith(text) 110 111 self.listContent = terms 112 for term in self.listContent: 113 self.ctrls.lb.Append(term[0]) 114 97 115 98 116 def OnListBox(self, evt): 99 self.wikiWord = guiToUni(evt.GetString()) 117 sel = self.ctrls.lb.GetSelection() 118 if sel != wx.NOT_FOUND: 119 self.ignoreTextChange += 1 120 self.ctrls.text.SetValue(self.listContent[sel][0]) 121 100 122 101 123 def OnCharText(self, evt): … … 107 129 else: 108 130 evt.Skip() 109 131 110 132 111 133 def OnCharListBox(self, evt): … … 115 137 else: 116 138 evt.Skip() 139 140 141 SelectWikiWordDialog.runModal = staticmethod(runDialogModalFactory(SelectWikiWordDialog)) 142 117 143 118 144 119 145 class OpenWikiWordDialog(wx.Dialog): 120 def __init__(self, pWiki, ID, title="Open Wiki Word",146 def __init__(self, pWiki, parent, ID, title=u"Open Wiki Word", 121 147 pos=wx.DefaultPosition, size=wx.DefaultSize, 122 148 style=wx.NO_3D): … … 126 152 127 153 self.pWiki = pWiki 128 self.wikiWord = u"" 154 self.value = None 155 self.listContent = [] 156 self.ignoreTextChange = 0 157 129 158 res = wx.xrc.XmlResource.Get() 130 res.LoadOnDialog(self, self.pWiki, "OpenWikiWordDialog")159 res.LoadOnDialog(self, parent, "OpenWikiWordDialog") 131 160 132 161 self.SetTitle(title) … … 158 187 159 188 def activateSelectedWikiWord(self, tabMode): 160 if len(self.wikiWord) == 0: 161 # Nothing entered means probably the user doesn't want to 162 # continue, so return True 163 return True 164 165 166 167 if not self.pWiki.getWikiDocument().isDefinedWikiWord(self.wikiWord): 168 langHelper = wx.GetApp().createWikiLanguageHelper( 169 self.pWiki.getWikiDefaultWikiLanguage()) 170 wikiWord = langHelper.extractWikiWordFromLink(self.wikiWord, 171 self.pWiki.getWikiDocument()) 172 173 if wikiWord is not None and \ 174 self.pWiki.getWikiDocument().isDefinedWikiWord(wikiWord): 175 self.wikiWord = wikiWord 176 189 sel = self.ctrls.lb.GetSelection() 190 if sel != wx.NOT_FOUND: 191 self.value = self.listContent[sel] 192 else: 193 entered = guiToUni(self.ctrls.text.GetValue()) 194 195 if len(entered) == 0: 196 # Nothing entered probably means the user doesn't want to 197 # continue, so return True 198 return True 199 200 if not self.pWiki.getWikiDocument().isDefinedWikiWord(entered): 201 langHelper = wx.GetApp().createWikiLanguageHelper( 202 self.pWiki.getWikiDefaultWikiLanguage()) 203 wikiWord = langHelper.extractWikiWordFromLink(entered, 204 self.pWiki.getWikiDocument()) 205 206 if wikiWord is not None and \ 207 self.pWiki.getWikiDocument().isDefinedWikiWord(wikiWord): 208 self.value = (wikiWord, 0, wikiWord, -1) 209 else: 210 terms = self.pWiki.getWikiData().getWikiWordMatchTermsWith( 211 entered) 212 if len(terms) > 0: 213 self.value = terms[0] 214 else: 215 if wikiWord is None: 216 self.pWiki.displayErrorMessage( 217 _(u"'%s' is an invalid WikiWord") % entered) 218 # Entered text is not a valid wiki word 219 self.ctrls.text.SetFocus() 220 return False 221 222 # wikiWord is valid but nonexisting, so maybe create it? 223 result = wx.MessageBox( 224 uniToGui(_(u"'%s' is not an existing wikiword. Create?") % 225 wikiWord), uniToGui(_(u"Create")), 226 wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, self) 227 228 if result == wx.NO: 229 self.ctrls.text.SetFocus() 230 return False 231 232 self.value = (wikiWord, 0, wikiWord, -1) 177 233 else: 178 words = self.pWiki.getWikiData().getWikiWordsWith(self.wikiWord, 179 True) 180 if len(words) > 0: 181 self.wikiWord = words[0] 182 else: 183 if wikiWord is None: 184 self.pWiki.displayErrorMessage( 185 _(u"'%s' is an invalid WikiWord") % self.wikiWord) 186 # Entered text is not a valid wiki word 187 self.ctrls.text.SetFocus() 188 return False 189 190 # wikiWord is valid but nonexisting, so maybe create it? 191 result = wx.MessageBox( 192 uniToGui(_(u"'%s' is not an existing wikiword. Create?") % 193 wikiWord), uniToGui(_(u"Create")), 194 wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, self) 195 196 if result == wx.NO: 197 self.ctrls.text.SetFocus() 198 return False 199 200 self.wikiWord = wikiWord 201 202 self.pWiki.activatePageByUnifiedName(u"wikipage/" + self.wikiWord, 203 tabMode=tabMode) 234 self.value = (entered, 0, entered, -1) 235 236 self.pWiki.activatePageByUnifiedName(u"wikipage/" + self.value[2], 237 tabMode=tabMode) # TODO: Go to charPos 204 238 205 239 return True … … 207 241 208 242 def GetValue(self): 209 return self. wikiWord243 return self.value 210 244 211 245 def OnText(self, evt): 212 self.wikiWord = guiToUni(evt.GetString()) 246 if self.ignoreTextChange: 247 self.ignoreTextChange -= 1 248 return 249 213 250 self.ctrls.lb.Clear() 214 if len(self.wikiWord) > 0: 215 words = self.pWiki.getWikiData().getWikiWordsWith(self.wikiWord, 216 True) 217 for word in words: 218 self.ctrls.lb.Append(word) 251 text = guiToUni(evt.GetString()) 252 253 if len(text) > 0: 254 terms = self.pWiki.getWikiData().getWikiWordMatchTermsWith(text) 255 256 self.listContent = terms 257 for term in self.listContent: 258 self.ctrls.lb.Append(term[0]) 259 219 260 220 261 def OnListBox(self, evt): 221 self.wikiWord = guiToUni(evt.GetString()) 262 sel = self.ctrls.lb.GetSelection() 263 if sel != wx.NOT_FOUND: 264 self.ignoreTextChange += 1 265 self.ctrls.text.SetValue(self.listContent[sel][0]) 266 222 267 223 268 def OnCharText(self, evt): … … 246 291 self.pWiki.getWikiDefaultWikiLanguage(), 247 292 self.pWiki.getWikiDocument()) 248 wikiWord = langHelper.extractWikiWordFromLink(self.wikiWord) 293 entered = guiToUni(self.ctrls.text.GetValue()) 294 wikiWord = langHelper.extractWikiWordFromLink(entered) 249 295 250 296 if wikiWord is None: 251 print "--OnCreate5"252 self.pWiki.displayErrorMessage(_(u"'%s' is an invalid WikiWord") % self.wikiWord)297 self.pWiki.displayErrorMessage(_(u"'%s' is an invalid WikiWord") % 298 entered) 253 299 self.ctrls.text.SetFocus() 254 300 return … … 259 305 return 260 306 261 self. wikiWord = wikiWord262 self.pWiki.activatePageByUnifiedName(u"wikipage/" + self.wikiWord,307 self.value = (wikiWord, 0, wikiWord, -1) 308 self.pWiki.activatePageByUnifiedName(u"wikipage/" + wikiWord, 263 309 tabMode=0) 264 310 self.EndModal(wx.ID_OK) … … 272 318 273 319 320 OpenWikiWordDialog.runModal = staticmethod(runDialogModalFactory(OpenWikiWordDialog)) 321 274 322 275 323 … … 337 385 self.SetFocus() 338 386 339 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk Pressed)340 wx.EVT_LIST_ITEM_ACTIVATED(self, self.lc.GetId(), self.OnOk Pressed)387 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) 388 wx.EVT_LIST_ITEM_ACTIVATED(self, self.lc.GetId(), self.OnOk) 341 389 342 390 def GetValue(self): … … 346 394 return self.value 347 395 348 @staticmethod 349 def runModal(parent, ID, iconCache, title="Select Icon", 350 pos=wx.DefaultPosition, size=wx.DefaultSize, 351 style=wx.NO_3D|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER): 352 353 dlg = SelectIconDialog(parent, ID, iconCache, title, pos, size, style) 354 try: 355 dlg.CenterOnParent(wx.BOTH) 356 if dlg.ShowModal() == wx.ID_OK: 357 return dlg.GetValue() 358 else: 359 return None 360 361 finally: 362 dlg.Destroy() 363 364 365 def OnOkPressed(self, evt): 396 397 # @staticmethod 398 # def runModal(parent, ID, iconCache, title="Select Icon", 399 # pos=wx.DefaultPosition, size=wx.DefaultSize, 400 # style=wx.NO_3D|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER): 401 # 402 # dlg = SelectIconDialog(parent, ID, iconCache, title, pos, size, style) 403 # try: 404 # dlg.CenterOnParent(wx.BOTH) 405 # if dlg.ShowModal() == wx.ID_OK: 406 # return dlg.GetValue() 407 # else: 408 # return None 409 # 410 # finally: 411 # dlg.Destroy() 412 413 414 def OnOk(self, evt): 366 415 no = self.lc.GetNextItem(-1, state = wx.LIST_STATE_SELECTED) 367 416 if no > -1: … … 374 423 375 424 376 class SavedVersionsDialog(wx.Dialog): 377 def __init__(self, pWiki, ID, title="Saved Versions", 378 pos=wx.DefaultPosition, size=wx.DefaultSize, 379 style=wx.NO_3D): 380 wx.Dialog.__init__(self, pWiki, ID, title, pos, size, style) 381 self.pWiki = pWiki 382 self.value = None 383 384 # Now continue with the normal construction of the dialog 385 # contents 386 sizer = wx.BoxSizer(wx.VERTICAL) 387 388 label = wx.StaticText(self, -1, _(u"Saved Versions")) 389 sizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 390 391 box = wx.BoxSizer(wx.VERTICAL) 392 393 self.lb = wx.ListBox(self, -1, wx.DefaultPosition, wx.Size(165, 200), 394 [], wx.LB_SINGLE) 395 396 # fill in the listbox 397 self.versions = self.pWiki.getWikiData().getStoredVersions() 398 399 for version in self.versions: 400 self.lb.Append(version[1]) 401 402 box.Add(self.lb, 1, wx.ALIGN_CENTRE|wx.ALL, 5) 403 404 sizer.AddSizer(box, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 405 406 line = wx.StaticLine(self, -1, size=(20,-1), style=wx.LI_HORIZONTAL) 407 sizer.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, 5) 408 409 box = wx.BoxSizer(wx.HORIZONTAL) 410 411 btn = wx.Button(self, wx.ID_OK, _(u" Retrieve ")) 412 btn.SetDefault() 413 box.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 414 415 btn = wx.Button(self, wx.ID_CANCEL, _(u" Cancel ")) 416 box.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 417 418 sizer.AddSizer(box, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 419 420 self.SetSizer(sizer) 421 self.SetAutoLayout(True) 422 sizer.Fit(self) 423 424 # Fixes focus bug under Linux 425 self.SetFocus() 426 427 ## wx.EVT_BUTTON(self, wxID_OK, self.OnRetrieve) 428 wx.EVT_LISTBOX(self, ID, self.OnListBox) 429 wx.EVT_LISTBOX_DCLICK(self, ID, lambda evt: self.EndModal(wx.ID_OK)) 430 431 ## def OnRetrieve(self, evt): 432 ## if self.value: 433 ## self.pWiki.getWikiData().deleteSavedSearch(self.value) 434 ## self.EndModal(wxID_CANCEL) 435 436 def GetValue(self): 437 """ Returns None or tuple (<id>, <description>, <creation date>) 438 """ 439 return self.value 440 441 def OnListBox(self, evt): 442 self.value = self.versions[evt.GetSelection()] 425 # class SavedVersionsDialog(wx.Dialog): 426 # def __init__(self, pWiki, ID, title="Saved Versions", 427 # pos=wx.DefaultPosition, size=wx.DefaultSize, 428 # style=wx.NO_3D): 429 # wx.Dialog.__init__(self, pWiki, ID, title, pos, size, style) 430 # self.pWiki = pWiki 431 # self.value = None 432 # 433 # # Now continue with the normal construction of the dialog 434 # # contents 435 # sizer = wx.BoxSizer(wx.VERTICAL) 436 # 437 # label = wx.StaticText(self, -1, _(u"Saved Versions")) 438 # sizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 439 # 440 # box = wx.BoxSizer(wx.VERTICAL) 441 # 442 # self.lb = wx.ListBox(self, -1, wx.DefaultPosition, wx.Size(165, 200), 443 # [], wx.LB_SINGLE) 444 # 445 # # fill in the listbox 446 # self.versions = self.pWiki.getWikiData().getStoredVersions() 447 # 448 # for version in self.versions: 449 # self.lb.Append(version[1]) 450 # 451 # box.Add(self.lb, 1, wx.ALIGN_CENTRE|wx.ALL, 5) 452 # 453 # sizer.AddSizer(box, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 454 # 455 # line = wx.StaticLine(self, -1, size=(20,-1), style=wx.LI_HORIZONTAL) 456 # sizer.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, 5) 457 # 458 # box = wx.BoxSizer(wx.HORIZONTAL) 459 # 460 # btn = wx.Button(self, wx.ID_OK, _(u" Retrieve ")) 461 # btn.SetDefault() 462 # box.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 463 # 464 # btn = wx.Button(self, wx.ID_CANCEL, _(u" Cancel ")) 465 # box.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 466 # 467 # sizer.AddSizer(box, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 468 # 469 # self.SetSizer(sizer) 470 # self.SetAutoLayout(True) 471 # sizer.Fit(self) 472 # 473 # # Fixes focus bug under Linux 474 # self.SetFocus() 475 # 476 # ## wx.EVT_BUTTON(self, wxID_OK, self.OnRetrieve) 477 # wx.EVT_LISTBOX(self, ID, self.OnListBox) 478 # wx.EVT_LISTBOX_DCLICK(self, ID, lambda evt: self.EndModal(wx.ID_OK)) 479 # 480 # ## def OnRetrieve(self, evt): 481 # ## if self.value: 482 # ## self.pWiki.getWikiData().deleteSavedSearch(self.value) 483 # ## self.EndModal(wxID_CANCEL) 484 # 485 # def GetValue(self): 486 # """ Returns None or tuple (<id>, <description>, <creation date>) 487 # """ 488 # return self.value 489 # 490 # def OnListBox(self, evt): 491 # self.value = self.versions[evt.GetSelection()] 492 493 494 SelectIconDialog.runModal = staticmethod(runDialogModalFactory(SelectIconDialog)) 495 443 496 444 497 … … 1295 1348 1296 1349 1297 class WikiPropertiesDialog(wx.Dialog): 1298 """ 1299 Show general information about currently open wiki 1300 """ 1301 def __init__(self, parent, id, mainControl): 1302 wx.Dialog.__init__(self, parent, id, 'Wiki Info', 1303 size=(470, 330) ) 1304 1350 class SimpleInfoDialog(wx.Dialog): 1351 def __init__(self, *args, **kwargs): 1352 wx.Dialog.__init__(self, *args, **kwargs) 1353 1305 1354 self.txtBgColor = self.GetBackgroundColour() 1306 1355 1307 1356 button = wx.Button(self, wx.ID_OK) 1308 1357 button.SetDefault() 1309 wd = mainControl.getWikiDocument() 1310 1311 wikiData = wd.getWikiData() 1312 1313 mainsizer = wx.BoxSizer(wx.VERTICAL) 1314 1315 label = _(u"Wiki database backend:") 1316 if wd is None: 1317 value = _(u"N/A") 1318 else: 1319 value = wd.getDbtype() 1320 1321 mainsizer.Add(self._buildLine(label, value), 0, wx.EXPAND) 1322 1323 label = _(u"Number of wiki pages:") 1324 if wd is None: 1325 value = _(u"N/A") 1326 else: 1327 value = unicode(len(wikiData.getAllDefinedWikiPageNames())) 1328 mainsizer.Add(self._buildLine(label, value), 0, wx.EXPAND) 1358 1359 self.mainsizer = wx.BoxSizer(wx.VERTICAL) 1360 1361 self.fillInfoLines() 1329 1362 1330 1363 inputsizer = wx.BoxSizer(wx.HORIZONTAL) … … 1332 1365 inputsizer.Add((0, 0), 1) # Stretchable spacer 1333 1366 1334 mainsizer.Add(inputsizer, 0, wx.ALL | wx.EXPAND, 5) 1335 1336 self.SetSizer(mainsizer) 1367 self.mainsizer.Add(inputsizer, 0, wx.ALL | wx.EXPAND, 5) 1368 1369 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) 1370 1371 self.SetSizer(self.mainsizer) 1337 1372 self.Fit() 1338 1373 … … 1341 1376 1342 1377 1343 def _ buildLine(self, label, value):1378 def _addLine(self, label, value): 1344 1379 inputsizer = wx.BoxSizer(wx.HORIZONTAL) 1345 1380 inputsizer.Add(wx.StaticText(self, -1, label), 1, … … 1349 1384 inputsizer.Add(ctl, 1, wx.ALL | wx.EXPAND, 5) 1350 1385 1351 return inputsizer 1352 1386 self.mainsizer.Add(inputsizer, 0, wx.EXPAND) 1387 1388 return ctl 1389 1390 1391 def fillInfoLines(self): 1392 raise NotImplementedError #abstract 1393 1394 1395 def OnOk(self, evt): 1396 evt.Skip() 1397 1398 1399 1400 1401 class WikiPropertiesDialog(SimpleInfoDialog): 1402 """ 1403 Show general information about currently open wiki 1404 """ 1405 def __init__(self, parent, id, mainControl): 1406 self.mainControl = mainControl 1407 SimpleInfoDialog.__init__(self, parent, id, 'Wiki Info', 1408 size=(470, 330) ) 1409 1410 def fillInfoLines(self): 1411 wd = self.mainControl.getWikiDocument() 1412 1413 wikiData = wd.getWikiData() 1414 1415 label = _(u"Wiki database backend:") 1416 if wd is None: 1417 value = _(u"N/A") 1418 else: 1419 value = wd.getDbtype() 1420 1421 self._addLine(label, value) 1422 1423 label = _(u"Number of wiki pages:") 1424 if wd is None: 1425 value = _(u"N/A") 1426 else: 1427 value = unicode(len(wikiData.getAllDefinedWikiPageNames())) 1428 1429 self._addLine(label, value) 1430 1431 1432 1433 class WikiJobDialog(SimpleInfoDialog): 1434 """ 1435 Show information about currently open wiki 1436 """ 1437 def __init__(self, parent, id, mainControl): 1438 self.jobTxtCtrl = None 1439 self.mainControl = mainControl 1440 1441 SimpleInfoDialog.__init__(self, parent, id, 'Jobs', 1442 size=(470, 330) ) 1443 1444 # Start timer 1445 self.timer = wx.Timer(self, GUI_ID.TIMER_JOBDIALOG) 1446 self.OnTimer(None) 1447 self.timer.Start(500, False) 1448 1449 wx.EVT_TIMER(self, GUI_ID.TIMER_JOBDIALOG, self.OnTimer) 1450 # wx.EVT_CLOSE(self, self.OnClose) 1451 1452 1453 def OnOk(self, evt): 1454 evt.Skip() 1455 print "--timer stop" 1456 self.timer.Stop() 1457 1458 1459 def fillInfoLines(self): 1460 label = _(u"Number of Jobs:") 1461 value = u"0" 1462 1463 self.jobTxtCtrl = self._addLine(label, value) 1464 1465 def OnTimer(self, evt): 1466 wd = self.mainControl.getWikiDocument() 1467 if wd is not None: 1468 exe = wd.getUpdateExecutor() 1469 if exe is not None: 1470 self.jobTxtCtrl.SetValue(unicode(exe.getJobCount())) 1353 1471 1354 1472 -
branches/mbutscher/next/lib/pwiki/Configuration.py
r166 r173 529 529 ("main", "show_lineNumbers"): "False", 530 530 ("main", "editor_useFolding"): "False", 531 ("main", "editor_useImeWorkaround"): "False", # Special workaround by handling input by WikidPad instead of Scintilla. 532 # Seems to help against some problems with Vietnamese input programs 531 533 ("main", "wikiWord_rename_wikiLinks"): "2", # When renaming wiki word, should it try to rename links to the word, too? 532 534 # 0:No, 1:Yes, 2:Ask for each renaming -
branches/mbutscher/next/lib/pwiki/DocPages.py
r169 r173 1 1 from __future__ import with_statement 2 from time import time 3 import os.path, re, struct, t raceback, threading2 3 import os.path, re, struct, time, traceback, threading 4 4 5 5 import wx … … 7 7 from MiscEvent import MiscEventSourceMixin 8 8 9 from Consts import DEADBLOCKTIMEOUT 9 import Consts 10 10 from WikiExceptions import * 11 11 12 12 from StringOps import strToBool, fileContentToUnicode, BOM_UTF8, utf8Enc, \ 13 utf8Dec, pathEnc, loadEntireTxtFile, writeEntire TxtFile13 utf8Dec, pathEnc, loadEntireTxtFile, writeEntireFile 14 14 15 15 from Utilities import DUMBTHREADSTOP, FunctionThreadStop, TimeoutRLock, \ … … 25 25 26 26 27 class DocPage( MiscEventSourceMixin):27 class DocPage(object, MiscEventSourceMixin): 28 28 """ 29 29 Abstract common base class for WikiPage and FunctionalPage 30 30 """ 31 32 # STATE_DEAD = 0xFFFF # Page is dead (can only happen just before wiki document 33 # # is closed) 34 # STATE_INVALID = 1 # After page got new name or was deleted. 35 # 36 # # The following two can be ORed together 37 # STATE_TEXTCACHE_MATCHES_DB = 2 # text cache is equal to database 38 # STATE_TEXTCACHE_MATCHES_EDITOR = 4 # text cache is equal to editor(s)' 39 # # content (always true for current implementation). This is also 40 # # true if no editor is assigned at all. 41 31 42 def __init__(self, wikiDocument): 32 43 MiscEventSourceMixin.__init__(self) … … 35 46 self.txtEditors = [] # List of all editors (views) showing this page 36 47 self.livePageAst = None # Cached page AST of live text 37 # self.livePageAstBuildLock = threading.RLock() # lock while building live AST 38 # self.textOperationLock = threading.RLock() # lock while setting, getting or saving self.textCache 39 self.livePageAstBuildLock = TimeoutRLock(DEADBLOCKTIMEOUT) # lock while building live AST 40 self.textOperationLock = TimeoutRLock(DEADBLOCKTIMEOUT) # lock while setting, getting or saving self.textCache 41 self.textCacheUpdatedEvent = threading.Event() 42 self.textCache = None # Caches live text instead of asking an editor for it. 48 49 # lock while building live AST 50 self.livePageAstBuildLock = TimeoutRLock(Consts.DEADBLOCKTIMEOUT) 51 52 # lock while setting, getting or saving self.editorText and some other ops 53 self.textOperationLock = TimeoutRLock(Consts.DEADBLOCKTIMEOUT) 54 55 # lock while changing or reading self.txtEditors list 56 self.txtEditorListLock = TimeoutRLock(Consts.DEADBLOCKTIMEOUT) 57 self.editorText = None # Contains editor text, 43 58 # if no text editor is registered, this cache is invalid 59 # self.pageState = STATE_TEXTCACHE_MATCHES_EDITOR 44 60 45 61 def invalidate(self): … … 49 65 """ 50 66 with self.textOperationLock: 51 self.wikiDocument = None 52 self.txtEditors = None 53 self.livePageAst = None 54 self.textCache = None 55 67 with self.txtEditorListLock: 68 self.wikiDocument = None 69 self.txtEditors = None 70 self.livePageAst = None 71 self.setEditorText(None) 72 73 74 def isInvalid(self): 75 return self.txtEditors is None 76 77 def getTextOperationLock(self): 78 return self.textOperationLock 56 79 57 80 def getWikiDocument(self): 58 81 return self.wikiDocument 82 83 def setEditorText(self, text, dirty=True): 84 """ 85 Just set editor text. Derived class overwrites this to set flags 86 """ 87 with self.textOperationLock: 88 self.editorText = text 89 self.livePageAst = None 90 91 92 def getEditorText(self): 93 return self.editorText 59 94 60 95 def addTxtEditor(self, txted): … … 63 98 """ 64 99 # TODO Set text in editor if first editor is created? 65 with self.t extOperationLock:100 with self.txtEditorListLock: 66 101 if not txted in self.txtEditors: 67 102 if len(self.txtEditors) == 0: 68 self.livePageAst = None 69 self.textCache = None 70 103 with self.textOperationLock: 104 # We are assuming that editor content came from 105 # database 106 self.setEditorText(txted.GetText(), dirty=False) 107 71 108 self.txtEditors.append(txted) 72 109 … … 77 114 If the last is removed, text is saved to database. 78 115 """ 79 with self.t extOperationLock:116 with self.txtEditorListLock: 80 117 try: 81 118 idx = self.txtEditors.index(txted) 82 119 if len(self.txtEditors) == 1: 83 self.writeToDatabase() 84 self.livePageAst = None 85 self.textCache = None 120 self.setEditorText(None) 86 121 87 122 del self.txtEditors[idx] … … 96 131 or None if no editor is associated. 97 132 """ 98 with self.t extOperationLock:133 with self.txtEditorListLock: 99 134 if len(self.txtEditors) > 0: 100 135 return self.txtEditors[0] … … 116 151 self.setDirty(True) 117 152 txtEditor = self.getTxtEditor() 153 self.livePageAst = None 118 154 if txtEditor is not None: 119 155 # page is in text editor(s), so call AppendText on one of it 120 156 # TODO Call self.SetReadOnly(False) first? 121 self.livePageAst = None122 157 txtEditor.AppendText(text) 123 158 return 124 159 125 160 # Modify database 126 text = self.getLiveText() + text127 self.livePageAst = None128 self.textCache = text129 161 self.writeToDatabase(fireEvent=fireEvent) 162 130 163 # self.save(text, fireEvent=fireEvent) 131 164 # … … 134 167 135 168 136 def _mainThreadUpdateTextCache(self): 137 with self.textOperationLock: 138 try: 139 txtEditor = self.getTxtEditor() 140 if txtEditor is not None: 141 # page is in text editor(s), so use cache or call GetText on one of it 142 if self.textCache is None: 143 self.textCache = txtEditor.GetText() 144 else: 145 self.textCache = self.getContent() 146 finally: 147 self.textCacheUpdatedEvent.set() 169 # def _mainThreadUpdateTextCache(self): 170 # # with self.textOperationLock: # Unnecessary as the function is only called 171 # # by a different thread within the lock and is waited for termination 172 # 173 # txtEditor = self.getTxtEditor() 174 # if txtEditor is not None: 175 # # page is in text editor(s), so use cache or call GetText on one of it 176 # if self.textCache is None: 177 # self.textCache = txtEditor.GetText() 178 # else: 179 # self.textCache = self.getContent() 148 180 149 181 … … 153 185 from the database 154 186 """ 155 for i in xrange(50): # Actually "while True:", but this may run without end156 with self.textOperationLock:157 if self.textCacheis not None:158 return self.textCache159 160 if self.getTxtEditor() is None:161 self.textCache = self.getContent() 162 return self.textCache 163 else: 164 if wx.Thread_IsMain():165 self._mainThreadUpdateTextCache() 166 return self.textCache 167 168 169 callInMainThread(self._mainThreadUpdateTextCache)170 171 with self.textOperationLock:172 if self.textCache is None:173 continue174 175 return self.textCache176 187 # for i in xrange(50): # Actually "while True:", but this may run without end 188 with self.textOperationLock: 189 if self.getEditorText() is not None: 190 return self.getEditorText() 191 192 return self.getContent() 193 194 195 196 # else: 197 # if wx.Thread_IsMain(): 198 # self._mainThreadUpdateTextCache() 199 # return self.textCache 200 201 # callInMainThread(self._mainThreadUpdateTextCache) 202 203 # with self.textOperationLock: 204 # if self.textCache is None: 205 # continue 206 # 207 # return self.textCache 208 177 209 # Something went terribly wrong 178 raise DeadBlockPreventionTimeOutError()210 # raise DeadBlockPreventionTimeOutError() 179 211 180 212 … … 194 226 self.setDirty(True) 195 227 txtEditor = self.getTxtEditor() 228 self.livePageAst = None 196 229 if txtEditor is not None: 197 230 # page is in text editor(s), so call replace on one of it 198 231 # TODO Call self.SetReadOnly(False) first? 199 232 txtEditor.replaceText(text) 200 self.livePageAst = None201 self.textCache = text202 233 return 203 234 204 self.livePageAst = None205 self.textCache = text206 235 self.writeToDatabase(fireEvent=fireEvent) 207 236 208 # self.save(text, fireEvent=fireEvent)209 #210 # self.update(text, fireEvent=fireEvent) # TODO Handle auto-generated areas211 212 237 213 238 def informEditorTextChanged(self, changer): 214 239 """ 215 Called by the txt editor control 216 """ 217 with self.textOperationLock: 218 # Clear cache 219 self.livePageAst = None 220 self.textCache = None 221 self.setDirty(True) 240 Called by the txt editor control. Must be called in GUI(=main) thread 241 """ 242 with self.textOperationLock: 243 txtEditor = self.getTxtEditor() 244 self.setEditorText(txtEditor.GetText()) 222 245 223 246 self.fireMiscEventProps({"changed editor text": True, … … 300 323 directly if can be done fast 301 324 """ 302 self.update(fireEvent=fireEvent)303 304 305 def update(self, fireEvent=True):306 """307 Update additional cached informations of doc page308 """309 325 raise NotImplementedError #abstract 326 327 328 # def _update(self, fireEvent=True): 329 # """ 330 # Update additional cached informations of doc page 331 # """ 332 # raise NotImplementedError #abstract 310 333 311 334 … … 367 390 return self.realWikiPage.initiateUpdate() 368 391 369 def update(self, fireEvent=True):370 return self.realWikiPage.update(fireEvent)392 # def update(self, fireEvent=True): 393 # return self.realWikiPage.update(fireEvent) 371 394 372 395 def getLivePageAst(self, fireEvent=True, dieOnChange=False, … … 389 412 390 413 # does this page need to be saved? 391 self.saveDirtySince = None # None, if not dirty or timestamp when it became dirty 414 415 # None, if not dirty or timestamp when it became dirty 416 # Inside self.textOperationLock, it is ensured that it is None iff 417 # the editorText is None or is in sync with the database. 418 # This applies not only to editorText, but also to the text returned 419 # by getLiveText(). 420 421 self.saveDirtySince = None 392 422 self.updateDirtySince = None 423 424 # To not store the content of the page here, a placeholder 425 # object is stored instead. Each time, the live text may have changed, 426 # a new object is created. Functions running in a separate thread can 427 # keep a reference to the object at beginning and check for 428 # identity at the end of their work. 429 self.liveTextPlaceHold = object() 430 431 432 # # To not store the full DB content of the page here, a placeholder 433 # # object is stored instead. Each time, text is written to DB, 434 # # a new object is created. Functions running in a separate task can 435 # # keep a reference to the object at beginning and check for 436 # # identity at the end of their work. 437 # self.dbContentPlaceHold = object() 393 438 394 439 … … 399 444 if dirt: 400 445 if self.saveDirtySince is None: 401 ti = time ()446 ti = time.time() 402 447 self.saveDirtySince = ti 403 448 self.updateDirtySince = ti … … 414 459 415 460 461 def setEditorText(self, text, dirty=True): 462 with self.textOperationLock: 463 super(DataCarryingPage, self).setEditorText(text) 464 if text is None: 465 if self.saveDirtySince is not None: 466 """ 467 Editor text was removed although it wasn't in sync with 468 database, so the self.liveTextPlaceHold must be updated, 469 but self.saveDirtySince is set to None because 470 self.editorText isn't valid anymore 471 """ 472 self.saveDirtySince = None 473 self.liveTextPlaceHold = object() 474 else: 475 if dirty: 476 self.setDirty(True) 477 self.liveTextPlaceHold = object() 478 479 480 416 481 417 482 class WikiPage(DataCarryingPage): … … 421 486 Fetched via the WikiDataManager.getWikiPage method. 422 487 """ 423 DB_CACHE_UPD_NO = 0 424 DB_CACHE_UPD_PROPERTIES = 1 425 DB_CACHE_UPD_ALL = 2 488 489 # # More states for wiki pages 490 # 491 # STATE_PROPERTIES_MATCH_TEXTCACHE = 32 # Properties (=attributes) were 492 # # processed and 493 # STATE_TEXTCACHE_MATCHES_EDITOR = 4 494 495 426 496 427 497 def __init__(self, wikiDocument, wikiWord): 428 498 DataCarryingPage.__init__(self, wikiDocument) 429 499 430 self.livePageBase Text = None # Cached text on which the page-ast431 # of this pageis based.500 self.livePageBasePlaceHold = None # liveTextPlaceHold object on which 501 # the livePageAst is based. 432 502 # This is needed to check for changes when saving 433 503 self.livePageBaseFormatDetails = None # Cached format details on which the 434 504 # page-ast bases 505 435 506 # self.metaDataProcessLock = threading.RLock() # lock while processing 436 507 # # meta-data … … 445 516 # newly created 446 517 518 if self.getWikiData().getMetaDataState(self.wikiWord) != 1: 519 self.updateDirtySince = time.time() 520 447 521 def getWikiWord(self): 448 522 return self.wikiWord … … 475 549 476 550 if self.modified is None: 477 ti = time ()478 self.modified, self.created, self.visited = ti, ti, 0.0551 ti = time.time() 552 self.modified, self.created, self.visited = ti, ti, ti 479 553 480 554 return self.modified, self.created, self.visited … … 499 573 return self.getWikiData().getParentRelationships(self.wikiWord) 500 574 501 575 502 576 def getChildRelationships(self, existingonly=False, selfreference=True, 503 577 withFields=(), excludeSet=frozenset(), … … 782 856 formatting 783 857 """ 784 withCamelCase = strToBool(self.getPropertyOrGlobal( 785 u"camelCaseWordsEnabled"), True) 786 787 footnotesAsWws = self.wikiDocument.getWikiConfig().getboolean( 788 "main", "footnotes_as_wikiwords", False) 789 790 autoLinkMode = self.getPropertyOrGlobal(u"auto_link", u"off").lower() 791 792 paragraphMode = strToBool(self.getPropertyOrGlobal( 793 u"paragraph_mode"), False) 794 795 return ParseUtilities.WikiPageFormatDetails( 796 withCamelCase=withCamelCase, 797 footnotesAsWws=footnotesAsWws, 798 wikiDocument=self.wikiDocument, 799 autoLinkMode=autoLinkMode, 800 paragraphMode=paragraphMode 801 ) 858 with self.textOperationLock: 859 withCamelCase = strToBool(self.getPropertyOrGlobal( 860 u"camelCaseWordsEnabled"), True) 861 862 footnotesAsWws = self.wikiDocument.getWikiConfig().getboolean( 863 "main", "footnotes_as_wikiwords", False) 864 865 autoLinkMode = self.getPropertyOrGlobal(u"auto_link", u"off").lower() 866 867 paragraphMode = strToBool(self.getPropertyOrGlobal( 868 u"paragraph_mode"), False) 869 870 return ParseUtilities.WikiPageFormatDetails( 871 withCamelCase=withCamelCase, 872 footnotesAsWws=footnotesAsWws, 873 wikiDocument=self.wikiDocument, 874 autoLinkMode=autoLinkMode, 875 paragraphMode=paragraphMode 876 ) 802 877 803 878 … … 827 902 self.getWikiData().setContent(self.wikiWord, self.getLiveText()) 828 903 self.saveDirtySince = None 829 904 # self.dbContentPlaceHold = object() 905 if self.getEditorText() is None: 906 self.liveTextPlaceHold = object() 907 908 830 909 # Clear timestamp cache 831 910 self.modified = None 911 912 913 def getLivePageAstIfAvailable(self): 914 """ 915 Return the current, up-to-data page AST if available, None otherwise 916 """ 917 with self.textOperationLock: 918 # Current state 919 text = self.getLiveText() 920 formatDetails = self.getFormatDetails() 921 922 # AST state 923 pageAst = self.livePageAst 924 baseFormatDetails = self.livePageBaseFormatDetails 925 926 if pageAst is not None and \ 927 formatDetails.isEquivTo(baseFormatDetails) and \ 928 self.liveTextPlaceHold is self.livePageBasePlaceHold: 929 return pageAst 930 931 return None 832 932 833 933 … … 848 948 849 949 with self.textOperationLock: 850 # Current state851 950 text = self.getLiveText() 951 liveTextPlaceHold = self.liveTextPlaceHold 852 952 formatDetails = self.getFormatDetails() 853 953 854 # AST state 855 pageAst = self.livePageAst 856 baseText = self.livePageBaseText 857 baseFormatDetails = self.livePageBaseFormatDetails 858 859 if pageAst is not None and len(text) == len(baseText) and \ 860 formatDetails.isEquivTo(baseFormatDetails) and \ 861 text == baseText: 862 return pageAst 954 pageAst = self.getLivePageAstIfAvailable() 955 # if pageAst is not None: 956 # return pageAst 957 958 # # Current state 959 # text = self.getLiveText() 960 # 961 # # AST state 962 # pageAst = self.livePageAst 963 # baseText = self.livePageBaseText 964 # baseFormatDetails = self.livePageBaseFormatDetails 965 # 966 # if pageAst is not None and len(text) == len(baseText) and \ 967 # formatDetails.isEquivTo(baseFormatDetails) and \ 968 # text == baseText: 969 # return pageAst 970 971 if pageAst is not None: 972 return pageAst 863 973 864 974 if dieOnChange: 865 975 if threadstop is DUMBTHREADSTOP: 866 threadstop = FunctionThreadStop(lambda: text is self.textCache) 976 threadstop = FunctionThreadStop( 977 lambda: liveTextPlaceHold is self.liveTextPlaceHold) 867 978 else: 868 979 origThreadstop = threadstop 869 980 threadstop = FunctionThreadStop( 870 981 lambda: origThreadstop.isRunning() and 871 text is self.textCache)982 liveTextPlaceHold is self.liveTextPlaceHold) 872 983 873 984 pageAst = self.parseTextInContext(text, formatDetails=formatDetails, 874 985 threadstop=threadstop) 875 986 987 with self.textOperationLock: 988 threadstop.testRunning() 989 990 self.livePageAst = pageAst 991 # self.livePageBaseText = text 992 self.livePageBasePlaceHold = liveTextPlaceHold 993 self.livePageBaseFormatDetails = formatDetails 994 995 996 # with self.textOperationLock: 997 # clt = time.clock() 998 if self.isReadOnlyEffect(): 876 999 threadstop.testRunning() 877 878 self.livePageAst = pageAst 879 self.livePageBaseText = text 880 self.livePageBaseFormatDetails = formatDetails 881 882 with self.textOperationLock: 883 if self.isReadOnlyEffect(): 884 threadstop.testRunning() 885 return pageAst 886 887 self.refreshPropertiesFromPageAst(pageAst) 888 889 formatDetails2 = self.getFormatDetails() 890 if not formatDetails.isEquivTo(formatDetails2): 891 # Formatting details have changed -> stop and wait for 892 # new round to update 893 threadstop.testRunning() 894 return pageAst 895 896 self.refreshMainDbCacheFromPageAst(pageAst, fireEvent=fireEvent) 897 898 threadstop.testRunning() 899 return pageAst 1000 return pageAst 1001 1002 if False: # TODO: Option 1003 self._refreshMetaData(pageAst, formatDetails, fireEvent=fireEvent, 1004 threadstop=threadstop) 1005 1006 # self.refreshPropertiesFromPageAst(pageAst, threadstop=threadstop) 1007 # 1008 # formatDetails2 = self.getFormatDetails() 1009 # if not formatDetails.isEquivTo(formatDetails2): 1010 # # Formatting details have changed -> stop and wait for 1011 # # new round to update 1012 # threadstop.testRunning() 1013 # return pageAst 1014 # 1015 # self.refreshMainDbCacheFromPageAst(pageAst, fireEvent=fireEvent, 1016 # threadstop=threadstop) 1017 # 1018 # # print "--refreshTime14", repr(time.clock()-clt) 1019 # with self.textOperationLock: 1020 # threadstop.testRunning() 1021 # if self.saveDirtySince is None: 1022 # self.getWikiData().setMetaDataProcessed(1) 1023 # self.updateDirtySince = None 1024 # 1025 # return pageAst 1026 1027 with self.textOperationLock: 1028 threadstop.testRunning() 1029 return pageAst 900 1030 901 1031 … … 926 1056 927 1057 928 def refreshPropertiesFromPageAst(self, pageAst): 1058 def _refreshMetaData(self, pageAst, formatDetails, fireEvent=True, 1059 threadstop=DUMBTHREADSTOP): 1060 1061 self.refreshPropertiesFromPageAst(pageAst, threadstop=threadstop) 1062 1063 formatDetails2 = self.getFormatDetails() 1064 if not formatDetails.isEquivTo(formatDetails2): 1065 # Formatting details have changed -> stop and wait for 1066 # new round to update 1067 return False 1068 1069 return self.refreshMainDbCacheFromPageAst(pageAst, fireEvent=fireEvent, 1070 threadstop=threadstop) 1071 1072 # print "--refreshTime14", repr(time.clock()-clt) 1073 1074 1075 # with self.textOperationLock: 1076 # threadstop.testRunning() 1077 # if self.saveDirtySince is None: 1078 # self.getWikiData().setMetaDataProcessed(1) 1079 # self.updateDirtySince = None 1080 # return True 1081 # 1082 # return False 1083 1084 1085 1086 def refreshPropertiesFromPageAst(self, pageAst, threadstop=DUMBTHREADSTOP): 929 1087 """ 930 1088 Update properties (aka attributes) only. … … 942 1100 943 1101 def addProperty(key, value): 1102 threadstop.testRunning() 944 1103 values = props.get(key) 945 1104 if not values: … … 952 1111 for node in propNodes: 953 1112 for propKey, propValue in node.props: 954 if propKey == u"alias": 955 if not langHelper.checkForInvalidWikiWord(propValue, 956 self.getWikiDocument()): 957 958 addProperty(u"alias", propValue) 959 else: 960 addProperty(propKey, propValue) 961 962 963 self.props = props 964 965 propAliases = self.props.get("alias") 966 if propAliases is not None: 967 for alias in propAliases: 968 self.getWikiData().setAsAlias(alias) 969 970 self.getWikiData().updateProperties(self.wikiWord, self.props) 971 972 973 974 def refreshMainDbCacheFromPageAst(self, pageAst, fireEvent=True): 1113 # if propKey == u"alias": 1114 # if not langHelper.checkForInvalidWikiWord(propValue, 1115 # self.getWikiDocument()): 1116 # 1117 # addProperty(u"alias", propValue) 1118 # else: 1119 addProperty(propKey, propValue) 1120 1121 with self.textOperationLock: 1122 threadstop.testRunning() 1123 1124 # self.props = props 1125 self.props = None 1126 1127 # propAliases = self.props.get("alias") 1128 # if propAliases is not None: 1129 # for alias in propAliases: 1130 # self.getWikiData().setAsAlias(alias) 1131 try: 1132 self.getWikiData().updateProperties(self.wikiWord, props) 1133 except WikiWordNotFoundException: 1134 return 1135 1136 1137 1138 def refreshMainDbCacheFromPageAst(self, pageAst, fireEvent=True, 1139 threadstop=DUMBTHREADSTOP): 975 1140 """ 976 1141 Update everything else (todos, relations). … … 978 1143 """ 979 1144 if self.isReadOnlyEffect(): 980 return 1145 return True # return True or False? 981 1146 982 1147 todos = [] … … 985 1150 986 1151 def addTodo(todo): 1152 threadstop.testRunning() 987 1153 if todo not in todos: 988 1154 todos.append(todo) 989 1155 990 def addChildRelationship(toWord, pos): 1156 def addChildRelationship(toWord, pos): 1157 threadstop.testRunning() 991 1158 if toWord not in childRelationSet: 992 1159 childRelations.append((toWord, pos)) 993 1160 childRelationSet.add(toWord) 994 1161 1162 # for t in pageAst.iterDeep(): 1163 # if t.name == "todoEntry": 1164 # addTodo(t.key + t.delimiter + t.valueNode.getString()) 1165 # elif t.name == "wikiWord": 1166 # addChildRelationship(t.wikiWord, t.pos) 1167 995 1168 todoTokens = pageAst.iterDeepByName("todoEntry") 996 1169 for t in todoTokens: 997 1170 addTodo(t.key + t.delimiter + t.valueNode.getString()) 1171 1172 threadstop.testRunning() 998 1173 999 1174 wwTokens = pageAst.iterDeepByName("wikiWord") … … 1001 1176 addChildRelationship(t.wikiWord, t.pos) 1002 1177 1003 # clear the dirty flag 1004 self.updateDirtySince = None 1005 1006 self.todos = todos 1007 self.childRelations = childRelations 1008 self.childRelationSet = childRelationSet 1009 1010 self.getWikiData().updateTodos(self.wikiWord, self.todos) 1011 self.getWikiData().updateChildRelations(self.wikiWord, self.childRelations) 1012 1013 self.modified = None # ? 1014 self.created = None 1178 1179 threadstop.testRunning() 1180 1181 matchTerms = [(self.wikiWord, 0, self.wikiWord, -1)] 1182 for w, k, v in self.getWikiDocument().getPropertyTriples( 1183 self.wikiWord, u"alias", None): 1184 threadstop.testRunning() 1185 matchTerms.append((v, 1, self.wikiWord, -1)) 1186 1187 with self.textOperationLock: 1188 clt = time.clock() 1189 threadstop.testRunning() 1190 1191 # self.todos = todos 1192 # self.childRelations = childRelations 1193 # self.childRelationSet = childRelationSet 1194 self.todos = None 1195 self.childRelations = None 1196 self.childRelationSet = set() 1197 try: 1198 # print "--refreshMainDbCacheFromPageAst24", repr((time.clock()-clt, len(self.todos))) 1199 self.getWikiData().updateTodos(self.wikiWord, todos) 1200 threadstop.testRunning() 1201 # print "--refreshMainDbCacheFromPageAst25", repr((time.clock()-clt, len(self.childRelations))) 1202 self.getWikiData().updateChildRelations(self.wikiWord, childRelations) 1203 threadstop.testRunning() 1204 # print "--refreshMainDbCacheFromPageAst26", repr((time.clock()-clt, len(matchTerms))) 1205 self.getWikiData().updateWikiWordMatchTerms(self.wikiWord, matchTerms) 1206 threadstop.testRunning() 1207 # print "--refreshMainDbCacheFromPageAst27", repr(time.clock()-clt) 1208 except WikiWordNotFoundException: 1209 return False 1210 # self.modified = None # ? 1211 # self.created = None 1212 1213 1214 # Now we check the whole chain if flags can be set: 1215 # db content is identical to liveText 1216 # liveText is basis of current livePageAst 1217 # formatDetails are same as the ones used for livePageAst 1218 # and livePageAst is identical to pageAst processed in this method 1219 1220 valid = False 1221 with self.textOperationLock: 1222 # print "--refreshMainDbCacheFromPageAst43", repr((self.saveDirtySince, 1223 # self.livePageBaseFormatDetails is not None, 1224 # self.getFormatDetails().isEquivTo(self.livePageBaseFormatDetails), 1225 # pageAst is self.livePageAst)) 1226 if self.saveDirtySince is None and \ 1227 self.livePageBasePlaceHold is self.liveTextPlaceHold and \ 1228 self.livePageBaseFormatDetails is not None and \ 1229 self.getFormatDetails().isEquivTo(self.livePageBaseFormatDetails) and \ 1230 pageAst is self.livePageAst: 1231 1232 threadstop.testRunning() 1233 # clear the dirty flag 1234 self.updateDirtySince = None 1235 1236 if self.saveDirtySince is None: 1237 # print "--refreshMainDbCacheFromPageAst48", repr(self.wikiWord) 1238 self.getWikiData().setMetaDataProcessed(self.wikiWord, 1) 1239 1240 valid = True 1015 1241 1016 1242 if fireEvent: 1017 1243 callInMainThreadAsync(self.fireMiscEventKeys, 1018 1244 ("updated wiki page", "updated page")) 1245 1246 return valid 1019 1247 1020 1248 … … 1026 1254 if self.isReadOnlyEffect(): 1027 1255 return 1028 # if self.getTxtEditor() is not None: 1029 # return True 1030 1031 text = self.getLiveText() 1256 1257 # sd, ud = self.getDirty() 1258 # 1259 # if not ud: 1260 # return 1261 1262 liveTextPlaceHold = self.liveTextPlaceHold 1032 1263 formatDetails = self.getFormatDetails() 1033 1264 1034 1265 try: 1035 self.getLivePageAst(dieOnChange=True)1266 pageAst = self.getLivePageAst(dieOnChange=True) 1036 1267 except NotCurrentThreadException: 1037 self.initiateUpdate()1268 # self.initiateUpdate() 1038 1269 return 1039 1270 1040 1041 with self.textOperationLock: 1042 if not text is self.textCache:1043 self.initiateUpdate()1271 # Check within lock if data is current yet 1272 with self.textOperationLock: 1273 if not liveTextPlaceHold is self.liveTextPlaceHold: 1274 # self.initiateUpdate() 1044 1275 return 1045 1276 1046 formatDetails2 = self.getFormatDetails() 1047 if formatDetails.isEquivTo(formatDetails2): 1277 # sd, ud = self.getDirty() 1278 # 1279 # if not ud: 1280 # return 1281 1282 # In the rare case that update is needed, but saving is not 1283 # after calling getLivePageAst() (can only happen after first save 1284 # of a newly created page 1285 self._refreshMetaData(pageAst, formatDetails) 1286 1287 # if not self._refreshMetaData(pageAst, formatDetails): 1288 # print "--runDatabaseUpdate26" 1289 # self.initiateUpdate() 1290 # return 1291 1292 1293 with self.textOperationLock: 1294 if not liveTextPlaceHold is self.liveTextPlaceHold: 1295 # self.initiateUpdate() 1048 1296 return 1049 1050 try: 1051 self.getLivePageAst(dieOnChange=True) 1052 except NotCurrentThreadException: 1053 self.initiateUpdate() 1054 return 1297 if not formatDetails.isEquivTo(self.getFormatDetails()): 1298 self.initiateUpdate() 1299 return 1300 1301 # sd, ud = self.getDirty() 1302 # 1303 # if not ud: 1304 # return 1305 # 1306 # self.initiateUpdate() 1055 1307 1056 1308 … … 1059 1311 """ 1060 1312 Initiate update of page meta-data. This function may call update 1061 directly if can be done fast1313 directly if it can be done fast 1062 1314 """ 1063 1315 with self.textOperationLock: … … 1065 1317 1066 1318 1067 def update(self, text=None, fireEvent=True): 1068 """ 1069 Update additional cached informations (properties, todos, relations) 1070 """ 1071 if self.isReadOnlyEffect(): 1072 return 1073 1074 details1 = self.getFormatDetails() 1075 pageAst = self.parseTextInContext(text, details1) 1076 1077 self.refreshPropertiesFromPageAst(pageAst) 1078 1079 details2 = self.getFormatDetails() 1080 if not details1.isEquivTo(details2): 1081 # Formatting details have changed -> create new AST for second 1082 # step of refresh. (Maybe repeat step one?) 1083 pageAst = self.parseTextInContext(text, details2) 1084 1085 self.refreshMainDbCacheFromPageAst(pageAst, fireEvent) 1319 # def update(self, text=None, fireEvent=True): 1320 # """ 1321 # Update additional cached informations (properties, todos, relations) 1322 # """ 1323 # if self.isReadOnlyEffect(): 1324 # return 1325 # 1326 # details1 = self.getFormatDetails() 1327 # pageAst = self.parseTextInContext(text, details1) 1328 # 1329 # self._refreshMetaData(pageAst, details1) 1330 # 1331 # self.refreshPropertiesFromPageAst(pageAst) 1332 # 1333 # details2 = self.getFormatDetails() 1334 # if not details1.isEquivTo(details2): 1335 # # Formatting details have changed -> create new AST for second 1336 # # step of refresh. (Maybe repeat step one?) 1337 # pageAst = self.parseTextInContext(text, details2) 1338 # 1339 # self.refreshMainDbCacheFromPageAst(pageAst, fireEvent) 1086 1340 1087 1341 … … 1446 1700 def _loadGlobalPage(self, subtag): 1447 1701 tbLoc = os.path.join(GetApp().getGlobalConfigSubDir(), 1448 subtag + ".wiki")1702 "[%s].wiki" % subtag) 1449 1703 try: 1450 1704 tbContent = loadEntireTxtFile(tbLoc) … … 1454 1708 1455 1709 1456 def _loadDbSpecificPage(self, subtag): 1457 if self.wikiDocument.isDefinedWikiWord(subtag): 1458 return self.wikiDocument.getWikiData().getContent(subtag) 1459 else: 1710 def _loadDbSpecificPage(self, funcTag): 1711 content = self.wikiDocument.getWikiData().retrieveDataBlockAsText(funcTag) 1712 if content is None: 1460 1713 return u"" 1714 1715 return content 1716 1717 # if self.wikiDocument.isDefinedWikiWord(subtag): 1718 # return self.wikiDocument.getWikiData().getContent(subtag) 1719 # else: 1720 # return u"" 1461 1721 1462 1722 … … 1470 1730 1471 1731 1472 def getContent(self): 1473 if self.funcTag in (u"global/ [TextBlocks]", u"global/[PWL]",1474 u"global/ [CCBlacklist]", u"global/[FavoriteWikis]"):1732 def getContent(self): 1733 if self.funcTag in (u"global/TextBlocks", u"global/PWL", 1734 u"global/CCBlacklist", u"global/FavoriteWikis"): 1475 1735 return self._loadGlobalPage(self.funcTag[7:]) 1476 elif self.funcTag in (u"wiki/ [TextBlocks]", u"wiki/[PWL]",1477 u"wiki/ [CCBlacklist]"):1478 return self._loadDbSpecificPage(self.funcTag [5:])1736 elif self.funcTag in (u"wiki/TextBlocks", u"wiki/PWL", 1737 u"wiki/CCBlacklist"): 1738 return self._loadDbSpecificPage(self.funcTag) 1479 1739 1480 1740 … … 1488 1748 """ 1489 1749 return ParseUtilities.WikiPageFormatDetails(noFormat=True) 1750 1751 1752 def getLivePageAstIfAvailable(self): 1753 return self.getLivePageAst() 1490 1754 1491 1755 … … 1504 1768 if pageAst is not None: 1505 1769 return pageAst 1506 1507 if text is None: 1508 text = self.getLiveText() # TODO May call wxPython method, therefore 1509 # not really threadsafe 1510 1511 pageAst = buildSyntaxNode([buildSyntaxNode(text, 0, "plainText")], 1512 0, "text") 1513 1514 threadstop.testRunning() 1515 1516 self.livePageAst = pageAst 1517 1518 return pageAst 1770 1771 with self.textOperationLock: 1772 pageAst = buildSyntaxNode([buildSyntaxNode( 1773 self.getLiveText(), 0, "plainText")], 0, "text") 1774 1775 threadstop.testRunning() 1776 1777 self.livePageAst = pageAst 1778 1779 return pageAst 1519 1780 1520 1781 … … 1522 1783 def _saveGlobalPage(self, text, subtag): 1523 1784 tbLoc = os.path.join(GetApp().getGlobalConfigSubDir(), 1524 subtag+".wiki")1525 1526 writeEntire TxtFile(tbLoc, (BOM_UTF8, utf8Enc(text)[0]))1527 1528 1529 def _saveDbSpecificPage(self, text, subtag):1785 "[%s].wiki" % subtag) 1786 1787 writeEntireFile(tbLoc, text, True) 1788 1789 1790 def _saveDbSpecificPage(self, text, funcTag): 1530 1791 if self.isReadOnlyEffect(): 1531 1792 return 1532 1793 1533 1794 wikiData = self.wikiDocument.getWikiData() 1534 1535 if self.wikiDocument.isDefinedWikiWord(subtag) and text == u"": 1536 # Delete content 1537 wikiData.deleteContent(subtag) 1795 1796 if text == u"": 1797 wikiData.deleteDataBlock(funcTag) 1538 1798 else: 1539 if text != u"": 1540 wikiData.setContent(subtag, text) 1799 wikiData.storeDataBlock(funcTag, text, 1800 storeHint=Consts.DATABLOCK_STOREHINT_EXTERN) 1801 1802 1803 # if self.wikiDocument.isDefinedWikiWord(subtag) and text == u"": 1804 # # Delete content 1805 # wikiData.deleteContent(subtag) 1806 # else: 1807 # if text != u"": 1808 # wikiData.setContent(subtag, text) 1541 1809 1542 1810 … … 1548 1816 return 1549 1817 1550 text = self.getLiveText() 1551 1552 if self.funcTag in (u"global/[TextBlocks]", u"global/[PWL]", 1553 u"global/[CCBlacklist]", u"global/[FavoriteWikis]"): 1554 self._saveGlobalPage(text, self.funcTag[7:]) 1555 elif self.funcTag in (u"wiki/[TextBlocks]", u"wiki/[PWL]", 1556 u"wiki/[CCBlacklist]"): 1557 self._saveDbSpecificPage(text, self.funcTag[5:]) 1558 1559 self.saveDirtySince = None 1560 1561 1562 1563 def update(self, fireEvent=True): 1564 """ 1565 Update additional cached informations (properties, todos, relations) 1818 with self.textOperationLock: 1819 text = self.getLiveText() 1820 1821 if self.funcTag in (u"global/TextBlocks", u"global/PWL", 1822 u"global/CCBlacklist", u"global/FavoriteWikis"): 1823 self._saveGlobalPage(text, self.funcTag[7:]) 1824 elif self.funcTag in (u"wiki/TextBlocks", u"wiki/PWL", 1825 u"wiki/CCBlacklist"): 1826 self._saveDbSpecificPage(text, self.funcTag) 1827 1828 self.saveDirtySince = None 1829 1830 1831 1832 def initiateUpdate(self, fireEvent=True): 1833 """ 1834 Update additional cached informations (properties, todos, relations). 1835 Here it is done directly in initiateUpdate() because it doesn't need 1836 much work. 1566 1837 """ 1567 1838 if self.isReadOnlyEffect(): 1568 1839 return 1569 1840 1570 # clear the dirty flag 1571 self.updateDirtySince = None 1572 1573 if fireEvent: 1574 if self.funcTag.startswith(u"wiki/"): 1575 evtSource = self 1576 else: 1577 evtSource = GetApp() 1578 1579 if self.funcTag in (u"global/[TextBlocks]", u"wiki/[TextBlocks]"): 1580 # The text blocks for the text blocks submenu was updated 1581 evtSource.fireMiscEventKeys(("updated func page", "updated page", 1582 "reread text blocks needed")) 1583 elif self.funcTag in (u"global/[PWL]", u"wiki/[PWL]"): 1584 # The personal word list (words to ignore by spell checker) 1585 # was updated 1586 evtSource.fireMiscEventKeys(("updated func page", "updated page", 1587 "reread personal word list needed")) 1588 elif self.funcTag in (u"global/[CCBlacklist]", u"wiki/[CCBlacklist]"): 1589 # The blacklist of camelcase words not to mark as wiki links 1590 # was updated 1591 evtSource.fireMiscEventKeys(("updated func page", "updated page", 1592 "reread cc blacklist needed")) 1593 elif self.funcTag == u"global/[FavoriteWikis]": 1594 # The list of favorite wikis was updated 1595 evtSource.fireMiscEventKeys(("updated func page", "updated page", 1596 "reread favorite wikis needed")) 1841 with self.textOperationLock: 1842 # clear the dirty flag 1843 self.updateDirtySince = None 1844 1845 if fireEvent: 1846 if self.funcTag.startswith(u"wiki/"): 1847 evtSource = self 1848 else: 1849 evtSource = GetApp() 1850 1851 if self.funcTag in (u"global/TextBlocks", u"wiki/TextBlocks"): 1852 # The text blocks for the text blocks submenu was updated 1853 evtSource.fireMiscEventKeys(("updated func page", "updated page", 1854 "reread text blocks needed")) 1855 elif self.funcTag in (u"global/PWL", u"wiki/PWL"): 1856 # The personal word list (words to ignore by spell checker) 1857 # was updated 1858 evtSource.fireMiscEventKeys(("updated func page", "updated page", 1859 "reread personal word list needed")) 1860 elif self.funcTag in (u"global/CCBlacklist", u"wiki/CCBlacklist"): 1861 # The blacklist of camelcase words not to mark as wiki links 1862 # was updated 1863 evtSource.fireMiscEventKeys(("updated func page", "updated page", 1864 "reread cc blacklist needed")) 1865 elif self.funcTag == u"global/FavoriteWikis": 1866 # The list of favorite wikis was updated (there is no 1867 # wiki-bound version of favorite wikis 1868 evtSource.fireMiscEventKeys(("updated func page", "updated page", 1869 "reread favorite wikis needed")) 1597 1870 1598 1871 def isReadOnlyEffect(self): … … 1634 1907 1635 1908 1909 1910 # TODO: Remove for Python 3.0 1636 1911 def _cmpNumbersItem1(a, b): 1637 1912 """ … … 1654 1929 # set or changed. 1655 1930 _FUNCTAG_TO_HR_NAME_MAP = { 1656 u"global/ [TextBlocks]": N_(u"Global text blocks"),1657 u"wiki/ [TextBlocks]": N_(u"Wiki text blocks"),1658 u"global/ [PWL]": N_(u"Global spell list"),1659 u"wiki/ [PWL]": N_(u"Wiki spell list"),1660 u"global/ [CCBlacklist]": N_(u"Global cc. blacklist"),1661 u"wiki/ [CCBlacklist]": N_(u"Wiki cc. blacklist"),1662 u"global/ [FavoriteWikis]": N_(u"Favorite wikis"),1931 u"global/TextBlocks": N_(u"Global text blocks"), 1932 u"wiki/TextBlocks": N_(u"Wiki text blocks"), 1933 u"global/PWL": N_(u"Global spell list"), 1934 u"wiki/PWL": N_(u"Wiki spell list"), 1935 u"global/CCBlacklist": N_(u"Global cc. blacklist"), 1936 u"wiki/CCBlacklist": N_(u"Wiki cc. blacklist"), 1937 u"global/FavoriteWikis": N_(u"Favorite wikis"), 1663 1938 } 1664 1939 -
branches/mbutscher/next/lib/pwiki/DocStructureCtrl.py
r169 r173 162 162 depth = max(depth, 1) 163 163 164 pageAst = docPage.getLivePageAst(t ext,threadstop)164 pageAst = docPage.getLivePageAst(threadstop=threadstop) 165 165 166 166 result = [] -
branches/mbutscher/next/lib/pwiki/Exporters.py
r171 r173 122 122 """ 123 123 return unicodeToCompFilename(removeBracketsFilename(fn)) 124 124 125 125 126 def _escapeAnchor(name): … … 991 992 content = wikiPage.getLiveText() 992 993 formatDetails = wikiPage.getFormatDetails() 993 self.basePageAst = wikiPage.getLivePageAst( content)994 self.basePageAst = wikiPage.getLivePageAst() 994 995 995 996 if self.linkConverter is None: … … 1258 1259 1259 1260 elif key == u"savedsearch": 1260 datablock = self.wikiDocument.getWikiData().getSearchDatablock(value) 1261 # datablock = self.wikiDocument.getWikiData().getSearchDatablock(value) 1262 datablock = self.wikiDocument.getWikiData().retrieveDataBlock( 1263 u"savedsearch/" + value) 1261 1264 if datablock is not None: 1262 1265 searchOp = SearchReplaceOperation() … … 2165 2168 if self.writeSavedSearches: 2166 2169 wikiData = self.wikiDocument.getWikiData() 2167 searchTitles = wikiData.getSavedSearchTitles() 2168 2169 for st in searchTitles: 2170 self.exportFile.write(u"savedsearch/%s\n" % st) 2171 datablock = wikiData.getSearchDatablock(st) 2170 # searchTitles = wikiData.getSavedSearchTitles() 2171 unifNames = wikiData.getDataBlockUnifNamesStartingWith( 2172 u"savedsearch/") 2173 2174 for un in unifNames: 2175 # self.exportFile.write(u"savedsearch/%s\n" % st) 2176 # datablock = wikiData.getSearchDatablock(st) 2177 self.exportFile.write(un + u"\n") 2178 datablock = wikiData.retrieveDataBlock(un) 2179 2172 2180 self.exportFile.write(base64BlockEncode(datablock)) 2173 2181 2174 2182 self.exportFile.write("\n%s\n" % self.separator) 2175 2183 -
branches/mbutscher/next/lib/pwiki/I18nPoUpdater.py
r166 r173 9 9 10 10 11 # Nearly same asfunctions in StringOps, but copied here to reduce dependencies11 # Similar to functions in StringOps, but copied here to reduce dependencies 12 12 def loadEntireTxtFile(filename): 13 13 """ -
branches/mbutscher/next/lib/pwiki/Importers.py
r168 r173 7 7 # import wx 8 8 9 import Consts 9 10 from StringOps import * 10 11 … … 177 178 self.importItemFuncPage(tag[9:]) 178 179 elif tag.startswith(u"savedsearch/"): 179 self.importItemSavedSearch(tag [12:])180 self.importItemSavedSearch(tag) 180 181 elif tag.startswith(u"wikipage/"): 181 182 self.importItemWikiPage(tag[9:]) … … 236 237 237 238 238 def importItemSavedSearch(self, subtag):239 def importItemSavedSearch(self, unifName): 239 240 # The subtag is the title of the search 240 241 … … 244 245 try: 245 246 datablock = base64BlockDecode(b64Content) 246 self.wikiDataManager.getWikiData().saveSearch(subtag, datablock) 247 # self.wikiDataManager.getWikiData().saveSearch(subtag, datablock) 248 self.wikiDataManager.getWikiData().storeDataBlock(unifName, datablock, 249 storeHint=Consts.DATABLOCK_STOREHINT_INTERN) 250 251 252 247 253 except TypeError: 248 254 # base64 decoding failed -
branches/mbutscher/next/lib/pwiki/Localization.py
r166 r173 5 5 from xml.dom import minidom 6 6 7 from StringOps import utf8Enc, loadEntire TxtFile, writeEntireTxtFile, pathEnc7 from StringOps import utf8Enc, loadEntireFile, writeEntireFile, pathEnc 8 8 9 9 … … 70 70 ascend -- If True sort ascending, descending otherwise 71 71 """ 72 # TODO Python 3.0 will no longer support compare function in sort 72 73 lst.sort(self.strcoll, reverse=not ascend) 73 74 # if not ascend: … … 111 112 raise NotImplementedError # abstract 112 113 113 # def normCase(self, s):114 # """115 # Normalize case for string s. It is recommended to just return116 # the UTF-8 encoding of the "lowered" string s. This should be even117 # true if the collator is case-sensitive.118 #119 # The collator must fulfill:120 # For all unicode strings a, b:121 #122 # 1. normCase(a) == normCase(b) iff a == b or a is equal b except for case.123 # 2. normCase(a) is part of normCase(b) iff a is part of b (at least if124 # case is ignored)125 #126 # """127 # assert 0 # abstract128 114 129 115 … … 161 147 162 148 163 # def normCase(self, s):164 # """165 # Normalize case for unicode string s and return byte string166 # """167 # result = []168 # for c in s:169 # o = ord(c)170 # if o < 65 or o > 90:171 # result.append(c)172 # else:173 # result.append(unichr(o+32))174 #175 # return utf8Enc(u"".join(result))[0]176 149 177 150 … … 196 169 return locale.strxfrm(s) 197 170 198 # def normCase(self, s):199 # return utf8Enc(s.lower)[0]200 171 201 172 … … 242 213 243 214 def strxfrm(self, s): 244 assert 0 # Not properly implemented 215 assert 0 # Not properly implemented. TODO Needed for Python 3.0 245 216 246 217 return locale.strxfrm(s) … … 344 315 345 316 try: 346 data = loadEntire TxtFile(os.path.join(appDir, "langlist.txt"))317 data = loadEntireFile(os.path.join(appDir, "langlist.txt"), True) 347 318 data = data.decode("utf-8") 348 319 … … 591 562 if i18nPoPath is None: 592 563 # No translation 593 return loadEntire TxtFile(os.path.join(appDir, baseXrcName + ".xrc"))564 return loadEntireFile(os.path.join(appDir, baseXrcName + ".xrc"), True) 594 565 595 566 # Retrieve modification time of .po and .xrc file to compare with cache … … 608 579 os.stat(pathEnc(cachePath)).st_mtime > poModTime: 609 580 # Valid cache found 610 return loadEntire TxtFile(cachePath)581 return loadEntireFile(cachePath, True) 611 582 except: 612 583 traceback.print_exc() # Really? … … 621 592 os.stat(pathEnc(cachePath)).st_mtime > poModTime: 622 593 # Valid cache found 623 return loadEntire TxtFile(cachePath)594 return loadEntireFile(cachePath, True) 624 595 except: 625 596 traceback.print_exc() # Really? … … 628 599 # No valid cache -> build content 629 600 630 untranslated = loadEntireTxtFile(os.path.join(appDir, baseXrcName + ".xrc")) 601 untranslated = loadEntireFile( 602 os.path.join(appDir, baseXrcName + ".xrc"), True) 631 603 632 604 xmlDoc = minidom.parseString(untranslated) … … 673 645 baseXrcName + "_" + i18nLocale + ".xrc") 674 646 675 writeEntire TxtFile(cachePath, translated)647 writeEntireFile(cachePath, translated, True) 676 648 677 649 return translated … … 684 656 baseXrcName + "_" + i18nLocale + ".xrc") 685 657 686 writeEntire TxtFile(cachePath, translated)658 writeEntireFile(cachePath, translated, True) 687 659 688 660 return translated -
branches/mbutscher/next/lib/pwiki/OptionsDialog.py
r166 r173 528 528 ("wikiLockFile_ignore", "cbWikiLockFileIgnore", "b"), 529 529 ("wikiLockFile_create", "cbWikiLockFileCreate", "b"), 530 ("editor_useImeWorkaround", "cbEditorUseImeWorkaround", "b"), 530 531 531 532 -
branches/mbutscher/next/lib/pwiki/PersonalWikiFrame.py
r171 r173 65 65 unescapeWithRe, escapeForIni, unescapeForIni, \ 66 66 wikiUrlToPathWordAndAnchor, urlFromPathname, flexibleUrlUnquote, \ 67 strftimeUB, pathEnc, loadEntire TxtFile, writeEntireTxtFile, \67 strftimeUB, pathEnc, loadEntireFile, writeEntireFile, \ 68 68 pathWordAndAnchorToWikiUrl, relativeFilePath, pathnameFromUrl 69 69 … … 101 101 self.progDlg = wx.ProgressDialog(self.title, self.msg + u" " * 20, 102 102 sum + self.addsteps, self.parent, self.flags) 103 103 104 104 def update(self, step, msg): 105 105 """ … … 211 211 tbLoc = join(self.globalConfigSubDir, "[TextBlocks].wiki") 212 212 if not exists(pathEnc(tbLoc)): 213 writeEntire TxtFile(tbLoc, (BOM_UTF8,213 writeEntireFile(tbLoc, 214 214 """importance: high;a=[importance: high]\\n 215 215 importance: low;a=[importance: low]\\n … … 217 217 wrap: 80;a=[wrap: 80]\\n 218 218 camelCaseWordsEnabled: false;a=[camelCaseWordsEnabled: false]\\n 219 """ ))219 """, True) 220 220 # self.globalConfigLoc = join(globalConfigDir, "WikidPad.config") 221 221 self.configuration = wx.GetApp().createCombinedConfiguration() … … 419 419 fileName) 420 420 if exists(pathEnc(extensionFileName)): 421 userUserExtension = loadEntire TxtFile(extensionFileName)421 userUserExtension = loadEntireFile(extensionFileName, True) 422 422 else: 423 423 userUserExtension = None … … 425 425 extensionFileName = join(self.wikiAppDir, 'user_extensions', fileName) 426 426 if exists(pathEnc(extensionFileName)): 427 userExtension = loadEntire TxtFile(extensionFileName)427 userExtension = loadEntireFile(extensionFileName, True) 428 428 else: 429 429 userExtension = None 430 430 431 431 extensionFileName = join(self.wikiAppDir, 'extensions', fileName) 432 systemExtension = loadEntire TxtFile(extensionFileName)432 systemExtension = loadEntireFile(extensionFileName, True) 433 433 434 434 return importCode(systemExtension, userExtension, userUserExtension, … … 754 754 if wikiData is not None: 755 755 if wikiData.checkCapability("rebuild") == 1: 756 self.addMenuItem(maintenanceMenu, _(u'&Update cache...'), 757 _(u'Update cache were marked as not up to date'), 758 lambda evt: self.rebuildWiki(onlyDirty=True), 759 menuID=GUI_ID.MENU_UPDATE_WIKI_CACHE, 760 updatefct=self.OnUpdateDisReadOnlyWiki) 761 756 762 self.addMenuItem(maintenanceMenu, _(u'&Rebuild Wiki...'), 757 _(u'Rebuild this wiki'), lambda evt: self.rebuildWiki(), 763 _(u'Rebuild this wiki and its cache completely'), 764 lambda evt: self.rebuildWiki(onlyDirty=False), 758 765 menuID=GUI_ID.MENU_REBUILD_WIKI, 759 766 updatefct=self.OnUpdateDisReadOnlyWiki) 760 767 768 769 self.addMenuItem(maintenanceMenu, _(u'Show job count...'), 770 _(u'Show how many update jobs are waiting in background'), 771 self.OnCmdShowWikiJobDialog) 772 773 761 774 self.addMenuItem(maintenanceMenu, _(u'Reconnect...'), 762 775 _(u'Reconnect to database after connection failure'), … … 931 944 if wikiDoc is not None and self.requireReadAccess(): 932 945 try: 933 page = wikiDoc.getFuncPage(u"wiki/ [TextBlocks]")946 page = wikiDoc.getFuncPage(u"wiki/TextBlocks") 934 947 treeData = TextTree.buildTreeFromText(page.getContent(), 935 948 TextTree.TextBlocksEntry.factory) … … 944 957 945 958 946 page = WikiDataManager.getGlobalFuncPage(u"global/ [TextBlocks]")959 page = WikiDataManager.getGlobalFuncPage(u"global/TextBlocks") 947 960 treeData = TextTree.buildTreeFromText(page.getContent(), 948 961 TextTree.TextBlocksEntry.factory) … … 1001 1014 wikiDoc = self.getWikiDocument() 1002 1015 1003 page = WikiDataManager.getGlobalFuncPage(u"global/ [FavoriteWikis]")1016 page = WikiDataManager.getGlobalFuncPage(u"global/FavoriteWikis") 1004 1017 treeData = TextTree.buildTreeFromText(page.getContent(), 1005 1018 TextTree.FavoriteWikisEntry.factory) … … 1088 1101 1089 1102 if entry is not None: 1090 page = WikiDataManager.getGlobalFuncPage(u"global/ [FavoriteWikis]")1103 page = WikiDataManager.getGlobalFuncPage(u"global/FavoriteWikis") 1091 1104 text = page.getLiveText() 1092 1105 if len(text) == 0 or text[-1] == u"\n": … … 1099 1112 1100 1113 def OnManageFavoriteWikis(self, evt): 1101 self.activatePageByUnifiedName(u"global/ [FavoriteWikis]", tabMode=2)1114 self.activatePageByUnifiedName(u"global/FavoriteWikis", tabMode=2) 1102 1115 1103 1116 … … 2007 2020 # Build favorite wikis tool buttons 2008 2021 wikiDoc = self.getWikiDocument() 2009 page = WikiDataManager.getGlobalFuncPage(u"global/ [FavoriteWikis]")2022 page = WikiDataManager.getGlobalFuncPage(u"global/FavoriteWikis") 2010 2023 treeData = TextTree.buildTreeFromText(page.getContent(), 2011 2024 TextTree.FavoriteWikisEntry.factory) … … 2952 2965 2953 2966 # reset the gui 2954 # self.resetGui()2955 2967 self.buildMainMenu() 2956 2968 … … 3002 3014 wikiWordsToOpen = wwo 3003 3015 3004 # set status3005 # self.statusBar.SetStatusText(3006 # uniToGui(u"Opened wiki '%s'" % self.wikiName), 0)3007 3008 3016 # now try and open the last wiki page as leftmost tab 3009 3017 if len(wikiWordsToOpen) > 0 and wikiWordsToOpen[0] != self.wikiName: … … 3140 3148 self.clipboardInterceptor.catchOff() 3141 3149 3150 self.fireMiscEventKeys(("closed current wiki",)) 3142 3151 self.resetGui() 3143 self.fireMiscEventKeys(("closed current wiki",))3144 3152 3145 3153 … … 3404 3412 self.statusBar.PushStatusText(u"Saving page", 0) 3405 3413 try: 3406 # Try to retrieve text from editor 3407 editor = page.getTxtEditor() 3408 if editor is None: 3414 # Test if editor is active 3415 if page.getTxtEditor() is None: 3409 3416 # No editor -> nothing to do 3410 3417 return False … … 3425 3432 # page.update(self.getActiveEditor().updateAutoGenAreas(text)) # ? 3426 3433 page.writeToDatabase() 3427 3428 3429 # pageAst = page.getLivePageAst()3430 3434 self.propertyChecker.initiateCheckPage(page) 3431 # if pageAst is not None: 3432 # self.propertyChecker.checkPage(page, pageAst) # !! 3435 3436 3433 3437 3434 3438 # trigger hooks 3435 3439 self.hooks.savedWikiWord(self, word) 3436 3440 else: 3437 # for functional pages3438 # page.save(text)3439 # page.update(text)3440 3441 page.writeToDatabase() 3441 3442 … … 3646 3647 return 3647 3648 try: 3648 bookmarked = self.getWikiData().getWordsWithPropertyValue(3649 "bookmarked", u"true")3649 bookmarked = [w for w,k,v in self.getWikiDocument() 3650 .getPropertyTriples(None, "bookmarked", u"true")] 3650 3651 except (IOError, OSError, DbAccessError), e: 3651 3652 self.lostAccess(e) … … 3984 3985 3985 3986 def showWikiWordOpenDialog(self): 3986 dlg = OpenWikiWordDialog(self, -1) 3987 try: 3988 dlg.CenterOnParent(wx.BOTH) 3989 dlg.ShowModal() 3990 # if dlg.ShowModal() == wxID_OK: 3991 # wikiWord = dlg.GetValue() 3992 # if wikiWord: 3993 # self.openWikiPage(wikiWord, forceTreeSyncFromRoot=True) 3994 self.getActiveEditor().SetFocus() 3995 finally: 3996 dlg.Destroy() 3987 OpenWikiWordDialog.runModal(self, self, -1) 3988 # dlg = OpenWikiWordDialog(self, -1) 3989 # try: 3990 # dlg.CenterOnParent(wx.BOTH) 3991 # dlg.ShowModal() 3992 self.getActiveEditor().SetFocus() 3993 # finally: 3994 # dlg.Destroy() 3997 3995 3998 3996 … … 4046 4044 4047 4045 4048 def showSavedVersionsDialog(self):4049 if not self.getWikiData().hasVersioningData():4050 dlg=wx.MessageDialog(self,4051 _(u"This wiki does not contain any version information"),4052 _(u'Retrieve version'), wx.OK)4053 dlg.ShowModal()4054 dlg.Destroy()4055 return4056 4057 dlg = SavedVersionsDialog(self, -1)4058 dlg.CenterOnParent(wx.BOTH)4059 4060 version = None4061 if dlg.ShowModal() == wx.ID_OK:4062 version = dlg.GetValue()4063 dlg.Destroy()4064 4065 if version:4066 dlg=wx.MessageDialog(self,4067 _(u"This will overwrite current content if not stored as "4068 u"version. Continue?"),4069 _(u'Retrieve version'), wx.YES_NO)4070 if dlg.ShowModal() == wx.ID_YES:4071 dlg.Destroy()4072 self.saveAllDocPages()4073 word = self.getCurrentWikiWord()4074 self.getWikiData().applyStoredVersion(version[0])4075 self.rebuildWiki(skipConfirm=True)4076 ## self.tree.collapse()4077 self.openWikiPage(self.getCurrentWikiWord(), forceTreeSyncFromRoot=True, forceReopen=True)4078 ## self.findCurrentWordInTree()4079 else:4080 dlg.Destroy()4046 # def showSavedVersionsDialog(self): 4047 # if not self.getWikiData().hasVersioningData(): 4048 # dlg=wx.MessageDialog(self, 4049 # _(u"This wiki does not contain any version information"), 4050 # _(u'Retrieve version'), wx.OK) 4051 # dlg.ShowModal() 4052 # dlg.Destroy() 4053 # return 4054 # 4055 # dlg = SavedVersionsDialog(self, -1) 4056 # dlg.CenterOnParent(wx.BOTH) 4057 # 4058 # version = None 4059 # if dlg.ShowModal() == wx.ID_OK: 4060 # version = dlg.GetValue() 4061 # dlg.Destroy() 4062 # 4063 # if version: 4064 # dlg=wx.MessageDialog(self, 4065 # _(u"This will overwrite current content if not stored as " 4066 # u"version. Continue?"), 4067 # _(u'Retrieve version'), wx.YES_NO) 4068 # if dlg.ShowModal() == wx.ID_YES: 4069 # dlg.Destroy() 4070 # self.saveAllDocPages() 4071 # word = self.getCurrentWikiWord() 4072 # self.getWikiData().applyStoredVersion(version[0]) 4073 # self.rebuildWiki(skipConfirm=True) 4074 # ## self.tree.collapse() 4075 # self.openWikiPage(self.getCurrentWikiWord(), forceTreeSyncFromRoot=True, forceReopen=True) 4076 # ## self.findCurrentWordInTree() 4077 # else: 4078 # dlg.Destroy() 4081 4079 4082 4080 … … 4572 4570 4573 4571 4574 def rebuildWiki(self, skipConfirm=False ):4572 def rebuildWiki(self, skipConfirm=False, onlyDirty=False): 4575 4573 if self.isReadOnlyWiki(): 4576 4574 return … … 4587 4585 progresshandler = wxGuiProgressHandler(_(u"Rebuilding wiki"), 4588 4586 _(u"Rebuilding wiki"), 0, self) 4589 self.getWikiDataManager().rebuildWiki(progresshandler) 4587 self.getWikiDataManager().rebuildWiki(progresshandler, 4588 onlyDirty=onlyDirty) 4590 4589 4591 4590 self.tree.collapse() … … 4804 4803 dlg.Destroy() 4805 4804 4805 def OnCmdShowWikiJobDialog(self, evt): 4806 dlg = WikiJobDialog(self, -1, self) 4807 dlg.ShowModal() 4808 dlg.Destroy() 4809 4806 4810 4807 4811 # ---------------------------------------------------------------------------------------- … … 5083 5087 self.Iconize(True) 5084 5088 else: 5089 # tracer.runctx('self._prepareExitWiki()', globals(), locals()) 5085 5090 self._prepareExitWiki() 5086 5091 self.Destroy() -
branches/mbutscher/next/lib/pwiki/Printing.py
r166 r173 379 379 380 380 def OnBeginPrinting(self): 381 self.base_OnBeginPrinting()381 wx.Printout.OnBeginPrinting(self) 382 382 self.mm2logUnitsFactor = None 383 383 return True -
branches/mbutscher/next/lib/pwiki/PropertyHandling.py
r168 r173 3 3 """ 4 4 5 from __future__ import with_statement 6 7 5 8 import traceback, threading 6 9 … … 13 16 from WikiExceptions import * 14 17 15 from Utilities import callInMainThread 18 from Utilities import callInMainThreadAsync 16 19 from Configuration import isUnicode 17 20 18 21 from LogWindow import LogMessage 22 23 from DocPages import WikiPage 24 19 25 20 26 … … 296 302 return 297 303 298 words = wikiData.getWordsWithPropertyValue(u"alias", propValue) 304 # words = wikiData.getWordsWithPropertyValue(u"alias", propValue) 305 words = [w for w,k,v in wikiDocument 306 .getPropertyTriples(None, "bookmarked", propValue)] 307 308 299 309 if len(words) > 1 or (len(words) > 0 and words[0] != wikiWord): 300 310 msg = LogMessage(self.mainControl, LogMessage.SEVERITY_WARNING, … … 493 503 494 504 def initiateCheckPage(self, wikiPage): 495 th = threading.Thread(target=self.checkPage, args=(wikiPage,)) 496 th.setDaemon(True) 497 th.start() 505 with wikiPage.getTextOperationLock(): 506 s, u = wikiPage.getDirty() 507 if u: 508 # Otherwise, updating and an "updated wiki page" event will 509 # follow, so listen for it. 510 wikiPage.getMiscEvent().addListener(self) 511 return 512 513 # TODO: Not completely threadsafe 514 515 # if page needs no update, check it directly 516 self.checkPage(wikiPage) 517 518 # th = threading.Thread(target=self.checkPage, args=(wikiPage,)) 519 # th.setDaemon(True) 520 # th.start() 521 522 523 def miscEventHappened(self, miscevt): 524 src = miscevt.getSource() 525 if isinstance(src, WikiPage): 526 # Event from wiki document aka wiki data manager 527 if miscevt.has_key("updated wiki page"): 528 src.getMiscEvent().removeListener(self) 529 self.checkPage(src) 498 530 499 531 … … 503 535 log window with messages if necessary 504 536 """ 537 if wikiPage.isInvalid(): 538 return 539 505 540 foundProps = set() 506 541 wikiWord = wikiPage.getWikiWord() 507 pageAst = wikiPage.getLivePageAst() 542 pageAst = wikiPage.getLivePageAstIfAvailable() 543 if pageAst is None: 544 return 545 508 546 propNodes = wikiPage.extractPropertyNodesFromPageAst(pageAst) 509 547 … … 531 569 node.pos + node.strLength, match) 532 570 533 callInMainThread (self.mainControl.getLogWindow().updateForWikiWord,571 callInMainThreadAsync(self.mainControl.getLogWindow().updateForWikiWord, 534 572 wikiWord, self.msgCollector) 535 573 except: -
branches/mbutscher/next/lib/pwiki/SearchAndReplaceDialogs.py
r167 r173 4 4 5 5 from MiscEvent import MiscEventSourceMixin, KeyFunctionSink 6 import Consts 6 7 from wxHelper import * 7 8 … … 881 882 return # Cancel 882 883 883 if title in self.pWiki.getWikiData().getSavedSearchTitles(): 884 # if title in self.pWiki.getWikiData().getSavedSearchTitles(): 885 if (u"savedsearch/" + title) in self.pWiki.getWikiData()\ 886 .getDataBlockUnifNamesStartingWith( 887 u"savedsearch/" + title): 888 884 889 answer = wx.MessageBox( 885 890 _(u"Do you want to overwrite existing search '%s'?") % … … 889 894 continue 890 895 891 self.pWiki.getWikiData().saveSearch(title, 892 sarOp.getPackedSettings()) 896 # self.pWiki.getWikiData().saveSearch(title, 897 # sarOp.getPackedSettings()) 898 self.pWiki.getWikiData().storeDataBlock( 899 u"savedsearch/" + title, sarOp.getPackedSettings(), 900 storeHint=Consts.DATABLOCK_STOREHINT_INTERN) 901 893 902 self._refreshSavedSearchesList() 894 903 break … … 922 931 923 932 def _refreshSavedSearchesList(self): 924 self.savedSearches = self.pWiki.getWikiData().getSavedSearchTitles() 933 unifNames = self.pWiki.getWikiData().getDataBlockUnifNamesStartingWith( 934 u"savedsearch/") 935 936 # self.savedSearches = self.pWiki.getWikiData().getSavedSearchTitles() 937 self.savedSearches = [name[12:] for name in unifNames] 925 938 self.pWiki.getCollator().sort(self.savedSearches) 926 939 927 940 self.ctrls.lbSavedSearches.Clear() 928 941 for search in self.savedSearches: … … 950 963 951 964 for s in sels: 952 self.pWiki.getWikiData().deleteSavedSearch(self.savedSearches[s]) 965 # self.pWiki.getWikiData().deleteSavedSearch(self.savedSearches[s]) 966 self.pWiki.getWikiData().deleteDataBlock( 967 u"savedsearch/" + self.savedSearches[s]) 953 968 self._refreshSavedSearchesList() 954 969 … … 972 987 return False 973 988 974 datablock = self.pWiki.getWikiData().getSearchDatablock( 975 self.savedSearches[sels[0]]) 989 # datablock = self.pWiki.getWikiData().getSearchDatablock( 990 # self.savedSearches[sels[0]]) 991 datablock = self.pWiki.getWikiData().retrieveDataBlock( 992 u"savedsearch/" + self.savedSearches[sels[0]]) 993 976 994 sarOp = SearchReplaceOperation() 977 995 sarOp.setPackedSettings(datablock) -
branches/mbutscher/next/lib/pwiki/SpellChecker.py
r167 r173 131 131 def rereadPersonalWordLists(self): 132 132 wdm = self.mainControl.getWikiDataManager() 133 self.globalPwlPage = wdm.getFuncPage("global/ [PWL]")133 self.globalPwlPage = wdm.getFuncPage("global/PWL") 134 134 self.spellChkAddedGlobal = \ 135 135 set(self.globalPwlPage.getLiveText().split("\n")) 136 136 137 self.localPwlPage = wdm.getFuncPage("wiki/ [PWL]")137 self.localPwlPage = wdm.getFuncPage("wiki/PWL") 138 138 self.spellChkAddedLocal = \ 139 139 set(self.localPwlPage.getLiveText().split("\n")) -
branches/mbutscher/next/lib/pwiki/StringOps.py
r168 r173 12 12 from struct import pack, unpack 13 13 14 import difflib, codecs, os.path, random, base64, locale 14 import difflib, codecs, os.path, random, base64, locale, hashlib, tempfile 15 15 16 16 # import urllib_red as urllib … … 151 151 152 152 153 # TODO! 153 154 def unicodeToCompFilename(us): 154 155 """ … … 236 237 237 238 238 def writeEntireTxtFile(filename, content): 239 """ 240 Write entire file (text mode). 241 content can either be a byte string or a tuple or list of byte strings 242 which are then written one by one to the file. 243 """ 244 rf = open(pathEnc(filename), "w") 239 # def writeEntireTxtFile(filename, content): 240 # """ 241 # Write entire file (text mode). 242 # content can either be a byte string or a tuple or list of byte strings 243 # which are then written one by one to the file. 244 # """ 245 # rf = open(pathEnc(filename), "w") 246 # try: 247 # if isinstance(content, tuple) or isinstance(content, list): 248 # for c in content: 249 # rf.write(c) 250 # else: 251 # rf.write(content) 252 # return 253 # finally: 254 # rf.close() 255 256 257 # def writeEntireFileFast(filename, content, textMode=False): 258 # """ 259 # Fast write of bytestring content without temporary file and 260 # error checking. 261 # """ 262 # if textMode: 263 # rf = open(pathEnc(filename), "w") 264 # else: 265 # rf = open(pathEnc(filename), "wb") 266 # 267 # try: 268 # rf.write(content) 269 # finally: 270 # rf.close() 271 272 273 def loadEntireFile(filename, textMode=False): 274 """ 275 Load entire file and return its content. 276 """ 277 if textMode: 278 rf = open(pathEnc(filename), "rU") 279 else: 280 rf = open(pathEnc(filename), "rb") 281 245 282 try: 246 if isinstance(content, tuple) or isinstance(content, list): 247 for c in content: 248 rf.write(c) 249 else: 250 rf.write(content) 251 return 283 return rf.read() 252 284 finally: 253 285 rf.close() 254 286 287 288 def writeEntireFile(filename, content, textMode=False): 289 """ 290 Write entire file (binary mode). 291 content can either be a bytestring or a tuple or list of bytestrings 292 which are then written one by one to the file. 293 If textMode is True, content can also be a unistring or sequence 294 of them (no mixed bytestring/unistring sequences allowed!) 295 which are then converted to UTF-8 and written to file with prefixed BOM 296 for utf-8. In textMode, lineEndings are properly converted to the 297 appropriate for the OS. 298 """ 299 basePath = os.path.split(filename)[0] 300 suffix = os.path.splitext(filename)[1] 301 302 if basePath == "": 303 basePath = u"." 304 305 fd, tempPath = tempfile.mkstemp(suffix=pathEnc(suffix), dir=pathEnc(basePath), 306 text=textMode) 307 308 try: 309 try: 310 if isinstance(content, unicode): 311 assert textMode 312 content = content.encode("utf-8") 313 os.write(fd, BOM_UTF8) 314 os.write(fd, content) 315 elif isinstance(content, str): 316 os.write(fd, content) 317 else: # content is a sequence 318 try: 319 iCont = iter(content) 320 321 firstContent = iCont.next() 322 323 unic = False 324 if isinstance(firstContent, unicode): 325 firstContent = firstContent.encode("utf-8") 326 os.write(fd, BOM_UTF8) 327 unic = True 328 329 assert isinstance(firstContent, str) 330 os.write(fd, firstContent) 331 332 while True: 333 content = iCont.next() 334 335 if unic: 336 assert isinstance(content, unicode) 337 content = content.encode("utf-8") 338 339 assert isinstance(content, str) 340 os.write(fd, content) 341 except StopIteration: 342 pass 343 344 finally: 345 os.close(fd) 346 347 except Exception, e: 348 # Something went wrong -> try to remove temporary file 349 try: 350 os.unlink(tempPath) 351 except: 352 traceback.print_exc() 353 354 raise e 355 else: 356 # Successful operation so far 357 if os.path.exists(filename): 358 os.unlink(filename) 359 360 os.rename(tempPath, filename) 361 362 363 def getFileSignatureBlock(filename): 364 statinfo = os.stat(pathEnc(filename)) 365 366 return pack(">BQd", 0, statinfo.st_size, statinfo.st_mtime) 255 367 256 368 … … 691 803 692 804 return unicode(result.value()) 693 694 695 696 697 698 # def flexibleUrlUnquote(link):699 # if link is None:700 # return None701 #702 # try:703 # linkAscii = link.encode("ascii", "strict")704 # except UnicodeEncodeError:705 # # URL contains non-ascii characters, so skip the following706 # # unquoting707 # linkAscii = None708 #709 # if linkAscii:710 # # Get bytes out of percent-quoted URL711 # linkBytes = urllib.unquote(linkAscii)712 # # Try to interpret bytes as UTF-8713 # try:714 # link = linkBytes.decode("utf8", "strict")715 # except UnicodeDecodeError:716 # # Failed -> try mbcs717 # try:718 # link = mbcsDec(linkBytes, "strict")[0]719 # except UnicodeDecodeError:720 # # Failed, too -> leave link unmodified721 # pass722 #723 # return link724 805 725 806 … … 973 1054 974 1055 975 976 977 _RNDBASESEQ = u"1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1056 def _quoteChar(c): 1057 oc = ord(c) 1058 if oc < 256: 1059 return u"%%%02X" % oc 1060 else: 1061 return u"@%04X" % oc 1062 1063 1064 _ESCAPING_CHARACTERS = u"%@~" 1065 1066 _FORBIDDEN_CHARACTERS = frozenset(u":/\\*?\"'<>|;![]" + _ESCAPING_CHARACTERS) 1067 _FORBIDDEN_START = _FORBIDDEN_CHARACTERS | frozenset(u".$ -") 1068 1069 # Allowed ascii characters remaining: #&()+,=[]^_`{} 1070 1071 1072 def iterCompatibleFilename(baseName, suffix, asciiOnly=False, maxLength=250, 1073 randomLength=10): 1074 """ 1075 Generator to create filenames compatible to (hopefully) all major 1076 OSs/filesystems. 1077 1078 Encode a unicode filename to a filename compatible to (hopefully) 1079 any filesystem encoding by converting unicode to '%xx' for 1080 characters up to 250 and '@xxxx' above. Each 'x represents a hex 1081 character. 1082 1083 If the resulting name is too long it is shortened. 1084 1085 If the first returned filename isn't accepted, a sequence of random 1086 characters, delimited by a tilde '~' is added. If the filename is then 1087 too long it is also shortened. 1088 1089 The first random sequence isn't random but a MD5-hash of baseName. 1090 1091 Each time you ask for next filename, a new sequence of random characters 1092 is created. 1093 1094 baseName - Base name to use for the filename 1095 suffix - Suffix (must include the dot) of the filename. The suffix must not 1096 be empty, is not quoted in any way and should follow the 1097 rules of the filesystem(s) 1098 asciiOnly - Iff True, all non-ascii characters are replaced. 1099 maxLength - Maximum length of filename including encoded basename, 1100 random sequence and suffix 1101 randomLength - Length of the random sequence (without leading tilde) 1102 1103 """ 1104 if len(baseName) > 0: 1105 c = baseName[0] 1106 if ord(c) < 32 or c in _FORBIDDEN_START or \ 1107 (asciiOnly and ord(c) > 127): 1108 baseQuoted = [_quoteChar(c)] 1109 else: 1110 baseQuoted = [c] 1111 1112 for c in baseName[1:]: 1113 if ord(c) < 32 or c in _FORBIDDEN_CHARACTERS or \ 1114 (asciiOnly and ord(c) > 127): 1115 baseQuoted.append(_quoteChar(c)) 1116 else: 1117 baseQuoted.append(c) 1118 1119 else: 1120 baseQuoted = [] 1121 1122 overallLength = sum(len(bq) for bq in baseQuoted) + len(suffix) 1123 1124 # Shorten baseQuoted if needed. This method ensures that no half-quoted 1125 # character (e.g. "@3") is remaining 1126 while overallLength > maxLength: 1127 overallLength -= len(baseQuoted.pop()) 1128 1129 if len(baseName) > 0: 1130 # First try, no random part 1131 yield u"".join(baseQuoted) + suffix 1132 1133 # Add random part to length 1134 overallLength += 1 + randomLength 1135 1136 # Shorten baseQuoted again 1137 while overallLength > maxLength: 1138 overallLength -= len(baseQuoted.pop()) 1139 1140 beforeRandom = u"".join(baseQuoted) + u"~" 1141 1142 # Now we try MD5-Hash. This is one last try to create a filename which 1143 # is non-ambigously connected to the baseName 1144 hashStr = getMd5B36ByString(baseName)[-randomLength:] 1145 if len(hashStr) < randomLength: 1146 hashStr = u"0" * (randomLength - len(hashStr)) + hashStr 1147 1148 yield beforeRandom + hashStr + suffix 1149 1150 # Now build infinite random names 1151 while True: 1152 yield beforeRandom + createRandomString(randomLength) + suffix 1153 1154 1155 1156 1157 def _unquoteCharRepl(matchObj): 1158 s = matchObj.group(0) 1159 1160 if s[0] == "%": 1161 v = int(s[1:3], 16) 1162 return unichr(v) 1163 else: # s[0] == "@": 1164 v = int(s[1:5], 16) 1165 return unichr(v) 1166 1167 1168 _FILENAME_UNQUOTE_RE = _re.compile(ur"%[A-Fa-f0-9]{2}|@[A-Fa-f0-9]{4}", 1169 _re.UNICODE | _re.DOTALL | _re.MULTILINE) 1170 1171 1172 def guessBaseNameByFilename(filename, suffix=u""): 1173 """ 1174 Try to guess the basename for a particular file name created by 1175 iterCompatibleFilename() as far as it can be reconstructed. 1176 """ 1177 # Filename may contain a path, so at first, strip it 1178 filename = os.path.basename(filename) 1179 1180 if filename.endswith(suffix): 1181 filename = filename[:-len(suffix)] 1182 # else? 1183 1184 # After a tilde begins the random part, so remove 1185 tildI = filename.find(u"~") 1186 if tildI > 0: # tildI == 0 would mean a nameless file 1187 filename = filename[:tildI] 1188 1189 return _FILENAME_UNQUOTE_RE.sub(_unquoteCharRepl, filename) 1190 1191 1192 1193 1194 _RNDBASESEQ = u"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 978 1195 979 1196 def createRandomString(length): 980 1197 """ 981 Create a unicode string of random characters and digits 982 """ 983 return u"".join([random.choice(_RNDBASESEQ) for i in xrange(length)]) 1198 Create a unicode string of length random characters and digits 1199 """ 1200 return u"".join([random.choice(_RNDBASESEQ) for i in range(length)]) 1201 1202 1203 1204 # _RNDBASENOHEX = u"GHIJKLMNOPQRSTUVWXYZ" 1205 # 1206 # def createRandomStringNoHexFirst(length): 1207 # """ 1208 # Create a unicode string of length random characters and digits. 1209 # First char. must not be a possible hexadecimal digit. 1210 # """ 1211 # if length == 0: 1212 # return u"" 1213 # 1214 # return random.choice(_RNDBASENOHEX) + u"".join([random.choice(_RNDBASESEQ) 1215 # for i in range(length - 1)]) 1216 1217 1218 def getMd5B36ByString(text): 1219 """ 1220 Calculate the MD5 hash of text (if unicode after conversion to utf-8) 1221 and return it as unistring for numeric base 36. 1222 1223 Based on http://code.activestate.com/recipes/111286/ 1224 """ 1225 if isinstance(text, unicode): 1226 text = text.encode("utf-8") 1227 1228 digest = hashlib.md5(text).digest() 1229 1230 # make an integer out of the number 1231 x = 0L 1232 for digit in digest: 1233 x = x*256 + ord(digit) 1234 1235 # create the result in base len(_RNDBASESEQ) (=36) 1236 res="" 1237 if x == 0: 1238 res = _RNDBASESEQ[0] 1239 while x>0: 1240 digit = x % len(_RNDBASESEQ) 1241 res = _RNDBASESEQ[digit] + res 1242 x //= len(_RNDBASESEQ) 1243 1244 return res 1245 984 1246 985 1247 -
branches/mbutscher/next/lib/pwiki/Utilities.py
r169 r173 80 80 81 81 class ExecutionResult(object): 82 __slots__ = ("result", "exception" )82 __slots__ = ("result", "exception", "state") 83 83 84 84 def __init__(self): 85 85 self.result = None 86 86 self.exception = None 87 self.state = 0 # 0: Invalid, 1: result is valid, 2: exception is valid 88 89 def setResult(self, result): 90 self.result = result 91 self.state = 1 92 93 def getReturn(self): 94 if self.state == 1: 95 return self.result 96 elif self.state == 2: 97 raise self.exception 98 99 # else ? 100 101 def setException(self, exception): 102 self.exception = exception 103 self.state = 2 104 105 # def getState(self): 106 # return self.state 87 107 88 108 … … 92 112 def __init__(self, daemon=False): 93 113 self.dequeCondition = threading.Condition() 94 self.deque = collections.deque() 95 self.thread = threading.Thread(target=self._runQueue) 96 self.thread.setDaemon(daemon) 97 self.thread.start() 114 self.daemon = daemon 115 116 self.deques = None 117 self.thread = None 118 119 120 def start(self): 121 if self.thread is not None and self.thread.isAlive(): 122 return 123 124 with self.dequeCondition: 125 self.deques = (collections.deque(), collections.deque()) 126 self.thread = threading.Thread(target=self._runQueue) 127 self.thread.setDaemon(self.daemon) 128 self.thread.start() 129 130 131 def getDeque(self, idx=0): 132 return self.deques[idx] 133 134 135 def getDequeCondition(self): 136 return self.dequeCondition 98 137 99 138 100 139 STOPOBJECT = object() 140 141 def _getNextJob(self): 142 for deque in self.deques: 143 if len(deque) != 0: 144 return deque.pop() 145 146 return None 147 148 149 def getJobCount(self): 150 with self.dequeCondition: 151 if self.deques is None: 152 return 0 153 154 return sum((len(deque) for deque in self.deques), 0) 155 156 # for deque in self.deques: 157 # if len(deque) != 0: 158 # return True 159 # 160 # return False 161 101 162 102 163 def _runQueue(self): 103 164 while True: 104 165 with self.dequeCondition: 105 while len(self.deque) == 0: 166 while self.deques is not None: 167 job = self._getNextJob() 168 if job is not None: 169 break 106 170 self.dequeCondition.wait() 107 108 fct, args, kwargs, event, retObj = self.deque.pop() 171 172 if self.deques is None: 173 # Executor terminated 174 return 175 176 fct, args, kwargs, event, retObj = job 109 177 try: 110 178 if fct is SingleThreadExecutor.STOPOBJECT: 111 # We should stop here, but the problem is that previously112 # updated pages may have pushed itselfback on the deque179 # We should stop here, but the problem is that other 180 # operations may itself pushed back on the deque 113 181 # and must be processed before thread can end 114 return 182 with self.dequeCondition: 183 if self.getJobCount() == 0: 184 return 185 186 self.deques[0].appendleft( 187 (SingleThreadExecutor.STOPOBJECT, None, None, 188 None, None)) 189 continue 190 115 191 116 192 # tracer.runctx('retObj.result = fct(*args, **kwargs)', globals(), locals()) 117 retObj. result = fct(*args, **kwargs)193 retObj.setResult(fct(*args, **kwargs)) 118 194 119 195 except Exception, e: 120 196 traceback.print_exc() # ? 121 retObj. exception = e197 retObj.setException(e) 122 198 finally: 123 199 if event is not None: … … 125 201 126 202 127 def execute(self, fct, *args, **kwargs):203 def execute(self, idx, fct, *args, **kwargs): 128 204 if threading.currentThread() is self.thread: 129 205 return fct(*args, **kwargs) … … 133 209 134 210 with self.dequeCondition: 135 self.deque .appendleft((fct, args, kwargs, event, retObj))211 self.deques[idx].appendleft((fct, args, kwargs, event, retObj)) 136 212 self.dequeCondition.notify() 137 213 … … 145 221 return retObj.result 146 222 223 224 def executeAsync(self, idx, fct, *args, **kwargs): 225 retObj = ExecutionResult() 226 if threading.currentThread() is self.thread: 227 try: 228 retObj.setResult(fct(*args, **kwargs)) 229 230 except Exception, e: 231 retObj.setException(e) 232 233 return retObj 234 235 236 with self.dequeCondition: 237 self.deques[idx].appendleft((fct, args, kwargs, None, retObj)) 238 self.dequeCondition.notify() 239 240 return retObj 241 242 147 243 __call__ = execute 148 244 149 245 150 def end(self): 151 if not self.thread.isAlive(): 246 def end(self, hardEnd=False): 247 """ 248 Wait (up to 120 seconds) to end the running jobs. 249 250 If hardEnd is False, all jobs in the queue are processed yet. 251 Even new jobs can be added during this time because 252 the called jobs may generate new ones under certain conditions. 253 254 If hardEnd is True, executor stops after the current job. 255 256 If the queue is empty, executor stops in each case. 257 """ 258 if self.thread is None or not self.thread.isAlive(): 152 259 return 153 260 154 261 with self.dequeCondition: 155 self.deque.appendleft( 156 (SingleThreadExecutor.STOPOBJECT, None, None, None, None)) 262 if hardEnd: 263 self.deques = None 264 else: 265 self.deques[0].appendleft( 266 (SingleThreadExecutor.STOPOBJECT, None, None, None, None)) 157 267 self.dequeCondition.notify() 158 268 159 269 self.thread.join(120) 270 160 271 if self.thread.isAlive(): 161 272 raise DeadBlockPreventionTimeOutError() 273 274 self.thread = None 275 162 276 163 277 … … 265 379 endtime = _time() + self.__timeout 266 380 delay = 0.0005 # 500 us -> initial delay of 1 ms 381 382 # if wx.Thread_IsMain(): 383 # if not self.__block.acquire(0): 384 # _sleep(0.2) 385 # if not self.__block.acquire(0): 386 # print "----Lock acquired by" 387 # print "".join(traceback.format_list(self.__acquiredStackTrace)) 388 # print "\n----Lock requested by" 389 # traceback.print_stack() 390 # else: 391 # self.__block.release() 392 # else: 393 # self.__block.release() 394 267 395 while True: 268 396 gotit = self.__block.acquire(0) … … 272 400 if remaining <= 0: 273 401 break 274 delay = min(delay * 2, remaining, .05) 402 # delay = min(delay * 2, remaining, .05) 403 delay = min(delay * 2, remaining, .02) 275 404 _sleep(delay) 276 405 if not gotit: … … 279 408 280 409 print "----Lock acquired by" 281 print "".join( self.__acquiredStackTrace)410 print "".join(traceback.format_list(self.__acquiredStackTrace)) 282 411 283 412 raise DeadBlockPreventionTimeOutError() … … 288 417 self.__owner = me 289 418 self.__count = 1 419 self.__acquiredStackTrace = traceback.extract_stack() 290 420 291 421 return gotit -
branches/mbutscher/next/lib/pwiki/WikiHtmlViewIE.py
r171 r173 1 from __future__ import with_statement 2 1 3 ## import hotshot 2 4 ## _prof = hotshot.Profile("hotshot.prf") … … 18 20 try: 19 21 # Generate dependencies for py2exe 22 import comtypes.gen._99AB80C4_5E19_4FD5_B3CA_5EF62FC3F765_0_1_0 as _dummy 23 import comtypes.gen.myole4ax as _dummy 20 24 import comtypes.gen._3050F1C5_98B5_11CF_BB82_00AA00BDCE0B_0_4_0 as _dummy 21 25 import comtypes.gen.MSHTML as _dummy … … 32 36 33 37 from StringOps import uniToGui, utf8Enc, utf8Dec, urlFromPathname, urlQuote, \ 34 writeEntireTxtFile,pathnameFromUrl38 pathnameFromUrl 35 39 36 40 from TempFileSet import TempFileSet … … 240 244 htpath = self.htpaths[self.currentHtpath] 241 245 242 writeEntireTxtFile(htpath, utf8Enc(html)[0]) 246 with open(htpath, "w") as f: 247 f.write(utf8Enc(html)[0]) 248 243 249 url = "file:" + urlFromPathname(htpath) 244 250 self.currentLoadedUrl = url … … 252 258 htpath = self.htpaths[self.currentHtpath] 253 259 254 writeEntireTxtFile(htpath, utf8Enc(html)[0]) 260 with open(htpath, "w") as f: 261 f.write(utf8Enc(html)[0]) 262 255 263 url = "file:" + urlFromPathname(htpath) 256 264 self.currentLoadedUrl = url -
branches/mbutscher/next/lib/pwiki/WikiPyparsing.py
r168 r173 461 461 yield node 462 462 463 # Just for symmetry 464 iterFlat = __iter__ 465 463 466 464 467 def iterSelectedDeepByName(self, name, deepSet, start=0): … … 500 503 yield inner 501 504 502 503 # Just for symmetry 504 iterDeep = __iter__ 505 def iterDeep(self, start=0): 506 """ 507 Iterate over all elements with the given name. If an NTNode 508 is found that is contained in deepSet, its children are searched, too. 509 """ 510 for node in self.sub[start:]: 511 yield node 512 if isinstance(node, NonTerminalNode): 513 for inner in node.iterDeep(): 514 yield inner 515 505 516 506 517 -
branches/mbutscher/next/lib/pwiki/WikiTreeCtrl.py
r169 r173 13 13 14 14 from WikiExceptions import WikiWordNotFoundException, InternalError 15 from Utilities import StringPathSet 15 from Utilities import StringPathSet, SingleThreadExecutor 16 16 17 17 from Configuration import MIDDLE_MOUSE_CONFIG_TO_TABMODE … … 771 771 772 772 def listChildren(self): 773 wikiD ata = self.treeCtrl.pWiki.getWikiData()773 wikiDocument = self.treeCtrl.pWiki.getWikiDocument() 774 774 result = [] 775 775 key = u".".join(self.categories) 776 words = wikiData.getWordsWithPropertyValue(key, self.value) 776 # words = wikiData.getWordsWithPropertyValue(key, self.value) 777 words = list(set(w for w,k,v in wikiDocument.getPropertyTriples( 778 None, key, self.value))) 777 779 self.treeCtrl.pWiki.getCollator().sort(words) 778 # return [WikiWordSearchNode(self.treeCtrl, self, w) for w in words]779 780 return [WikiWordPropertySearchNode(self.treeCtrl, self, w, 780 781 key, self.value) for w in words] 781 782 782 # return map(lambda w: WikiWordSearchNode(self.treeCtrl,783 # wikiData.getPage(w, toload=[""])), words)784 783 785 784 def nodeEquality(self, other): … … 845 844 846 845 def isVisible(self): 847 return bool(self.treeCtrl.pWiki.getWikiData().getSavedSearchTitles()) 848 846 # return bool(self.treeCtrl.pWiki.getWikiData().getSavedSearchTitles()) 847 return bool(self.treeCtrl.pWiki.getWikiData()\ 848 .getDataBlockUnifNamesStartingWith(u"savedsearch/")) 849 849 850 def listChildren(self): 850 851 wikiData = self.treeCtrl.pWiki.getWikiData() 851 852 searchTitles = wikiData.getSavedSearchTitles() 853 return map(lambda s: SearchNode(self.treeCtrl, self, s), searchTitles) 852 853 unifNames = wikiData.getDataBlockUnifNamesStartingWith( 854 u"savedsearch/") 855 856 # return map(lambda s: SearchNode(self.treeCtrl, self, s), searchTitles) 857 return [SearchNode(self.treeCtrl, self, name[12:]) for name in unifNames] 858 854 859 855 860 … … 876 881 def listChildren(self): 877 882 pWiki = self.treeCtrl.pWiki 878 datablock = pWiki.getWikiData().getSearchDatablock(self.searchTitle) 883 # datablock = pWiki.getWikiData().getSearchDatablock(self.searchTitle) 884 datablock = pWiki.getWikiData().retrieveDataBlock( 885 u"savedsearch/" + self.searchTitle) 886 879 887 searchOp = SearchReplaceOperation() 880 888 searchOp.setPackedSettings(datablock) … … 1048 1056 def listChildren(self): 1049 1057 return [ 1050 FuncPageNode(self.treeCtrl, self, u"global/ [TextBlocks]"),1051 FuncPageNode(self.treeCtrl, self, u"wiki/ [TextBlocks]"),1052 FuncPageNode(self.treeCtrl, self, u"global/ [PWL]"),1053 FuncPageNode(self.treeCtrl, self, u"wiki/ [PWL]"),1054 FuncPageNode(self.treeCtrl, self, u"global/ [CCBlacklist]"),1055 FuncPageNode(self.treeCtrl, self, u"wiki/ [CCBlacklist]"),1056 FuncPageNode(self.treeCtrl, self, u"global/ [FavoriteWikis]")1058 FuncPageNode(self.treeCtrl, self, u"global/TextBlocks"), 1059 FuncPageNode(self.treeCtrl, self, u"wiki/TextBlocks"), 1060 FuncPageNode(self.treeCtrl, self, u"global/PWL"), 1061 FuncPageNode(self.treeCtrl, self, u"wiki/PWL"), 1062 FuncPageNode(self.treeCtrl, self, u"global/CCBlacklist"), 1063 FuncPageNode(self.treeCtrl, self, u"wiki/CCBlacklist"), 1064 FuncPageNode(self.treeCtrl, self, u"global/FavoriteWikis") 1057 1065 ] 1058 1066 … … 1065 1073 __slots__ = ("funcTag", "label") 1066 1074 1067 # TAG_TO_LABEL_MAP = { # Maps the func tag to the node's label1068 # "global/[TextBlocks]": u"Global text blocks",1069 # "wiki/[TextBlocks]": u"Wiki text blocks",1070 # "global/[PWL]": "Global spell list",1071 # "wiki/[PWL]": "Wiki spell list",1072 # "global/[CCBlacklist]": "Global cc. blacklist",1073 # "wiki/[CCBlacklist]": "Wiki cc. blacklist"1074 # }1075 1076 1075 def __init__(self, tree, parentNode, funcTag): 1077 1076 AbstractNode.__init__(self, tree, parentNode) … … 1133 1132 self.SetSpacing(0) 1134 1133 self.refreshGenerator = None # Generator called in OnIdle 1134 self.refreshExecutor = SingleThreadExecutor() 1135 1135 # self.refreshCheckChildren = [] # List of nodes to check for new/deleted children 1136 1136 self.sizeVisible = True … … 1244 1244 ("options changed", self.onOptionsChanged), 1245 1245 ), wx.GetApp().getMiscEvent(), self) 1246 1247 1248 self.refreshExecutor.start() 1246 1249 1247 1250 … … 1259 1262 self.__sinkWikiDoc.disconnect() 1260 1263 self.__sinkApp.disconnect() 1264 self.refreshExecutor.end(hardEnd=True) 1261 1265 1262 1266 def collapse(self): … … 1461 1465 1462 1466 def _generatorRefreshNodeAndChildren(self, parentnodeid): 1467 """ 1468 This is some sort of user-space thread. It is executed inside 1469 the main thread as most things are GUI operations which must 1470 be done in GUI thread. 1471 Some non-GUI things are handed over to the refreshExecutor which 1472 runs them in a different thread. 1473 """ 1463 1474 try: 1464 1475 nodeObj = self.GetPyData(parentnodeid) 1465 1476 except Exception: 1466 1477 raise StopIteration 1467 1478 1468 1479 wikiData = self.pWiki.getWikiData() 1469 1470 nodeStyle = nodeObj.getNodePresentation() 1471 1480 1481 1482 retObj = self.refreshExecutor.executeAsync(0, nodeObj.getNodePresentation) 1483 while retObj.state == 0: # Dangerous! 1484 # print "--_generatorRefreshNodeAndChildren13" 1485 yield None 1486 # print "--_generatorRefreshNodeAndChildren14" 1487 nodeStyle = retObj.getReturn() 1488 # nodeStyle = nodeObj.getNodePresentation() 1489 1472 1490 self.setNodePresentation(parentnodeid, nodeStyle) 1473 1491 … … 1478 1496 if not self.IsExpanded(parentnodeid): 1479 1497 raise StopIteration 1480 1498 1481 1499 if nodeObj.representsWikiWord(): 1482 wikiWord = wikiData.getAliasesWikiWord(nodeObj.getWikiWord()) 1483 1484 ## if not nodeObj.representsFamilyWikiWord() or \ 1485 ## wikiWord in self.refreshCheckChildren: 1500 retObj = self.refreshExecutor.executeAsync( 1501 0, wikiData.getAliasesWikiWord, nodeObj.getWikiWord()) 1502 while retObj.state == 0: # Dangerous! 1503 # print "--_generatorRefreshNodeAndChildren19" 1504 yield None 1505 wikiWord = retObj.getReturn() 1506 # wikiWord = wikiData.getAliasesWikiWord(nodeObj.getWikiWord()) 1507 1508 1486 1509 if True: 1487 1510 # We have to recreate the children of this node 1488 1511 1512 1489 1513 # This is time consuming 1490 children = nodeObj.listChildren() 1514 retObj = self.refreshExecutor.executeAsync(0, nodeObj.listChildren) 1515 while retObj.state == 0: # Dangerous! 1516 # print "--_generatorRefreshNodeAndChildren34" 1517 yield None 1518 children = retObj.getReturn() 1519 1520 # # This is time consuming 1521 # children = nodeObj.listChildren() 1491 1522 1492 1523 if self.expandedNodePathes is not None: … … 1517 1548 yield sg 1518 1549 else: 1519 nodeStyle = nodeObj.getNodePresentation() 1550 retObj = self.refreshExecutor.executeAsync(0, 1551 nodeObj.getNodePresentation) 1552 while retObj.state == 0: # Dangerous! 1553 yield None 1554 nodeStyle = retObj.getReturn() 1555 # nodeStyle = nodeObj.getNodePresentation() 1556 1520 1557 self.setNodePresentation(nodeid, nodeStyle) 1521 1558 … … 1536 1573 tci += 1 1537 1574 self.SetPyData(newnodeid, c) 1538 self.setNodePresentation(newnodeid, 1539 c.getNodePresentation()) 1540 1575 1576 retObj = self.refreshExecutor.executeAsync(0, 1577 c.getNodePresentation) 1578 while retObj.state == 0: # Dangerous! 1579 yield None 1580 nodeStyle = retObj.getReturn() 1581 self.setNodePresentation(newnodeid, nodeStyle) 1582 # self.setNodePresentation(newnodeid, 1583 # c.getNodePresentation()) 1584 1541 1585 yield None 1542 1586 … … 1546 1590 newnodeid = self.AppendItem(parentnodeid, "") 1547 1591 self.SetPyData(newnodeid, c) 1548 self.setNodePresentation(newnodeid, 1549 c.getNodePresentation()) 1592 1593 retObj = self.refreshExecutor.executeAsync(0, 1594 c.getNodePresentation) 1595 while retObj.state == 0: # Dangerous! 1596 yield None 1597 nodeStyle = retObj.getReturn() 1598 self.setNodePresentation(newnodeid, nodeStyle) 1599 # self.setNodePresentation(newnodeid, 1600 # c.getNodePresentation()) 1550 1601 1551 1602 1552 1603 # End of loop, no more new children, remove possible remaining 1553 1604 # children in tree 1554 1555 1605 1556 1606 while idIdx < len(oldChildNodeIds): … … 1579 1629 else: 1580 1630 nodeObj = self.GetPyData(nodeid) 1581 self.setNodePresentation(nodeid, nodeObj.getNodePresentation()) 1631 retObj = self.refreshExecutor.executeAsync(0, 1632 nodeObj.getNodePresentation) 1633 while retObj.state == 0: # Dangerous! 1634 yield None 1635 nodeStyle = retObj.getReturn() 1636 self.setNodePresentation(nodeid, nodeStyle) 1637 # self.setNodePresentation(nodeid, nodeObj.getNodePresentation()) 1582 1638 1583 1639 yield None … … 1647 1703 1648 1704 def OnAppendWikiWord(self, evt): 1649 dlg = SelectWikiWordDialog(self.pWiki, -1, title=_(u"Append Wiki Word")) 1650 if dlg.ShowModal() == wx.ID_OK: 1705 toWikiWord = SelectWikiWordDialog.runModal(self.pWiki, self.pWiki, -1, 1706 title=_(u"Append Wiki Word")) 1707 1708 if toWikiWord is not None: 1651 1709 parentWord = self.GetPyData(self.contextMenuNode).getWikiWord() 1652 1710 page = self.pWiki.getWikiDataManager().getWikiPageNoError(parentWord) 1653 page.appendLiveText("\n[%s]" % dlg.GetValue()) 1654 1655 dlg.Destroy() 1711 1712 langHelper = wx.GetApp().createWikiLanguageHelper( 1713 page.getWikiLanguageName()) 1714 1715 page.appendLiveText(u"\n" + 1716 langHelper.createStableLinksFromWikiWords(toWikiWord)) 1717 1656 1718 1657 1719 def OnPrependWikiWord(self, evt): 1658 dlg = SelectWikiWordDialog(self.pWiki, -1, title=_(u"Prepend Wiki Word")) 1659 if dlg.ShowModal() == wx.ID_OK: 1720 toWikiWord = SelectWikiWordDialog.runModal(self.pWiki, self.pWiki, -1, 1721 title=_(u"Prepend Wiki Word")) 1722 1723 if toWikiWord is not None: 1660 1724 parentWord = self.GetPyData(self.contextMenuNode).getWikiWord() 1661 1725 page = self.pWiki.getWikiDataManager().getWikiPageNoError(parentWord) 1662 text = page.getLiveText() 1663 page.replaceLiveText("[%s]\n%s" % (dlg.GetValue(), text)) 1664 1665 dlg.Destroy() 1726 1727 langHelper = wx.GetApp().createWikiLanguageHelper( 1728 page.getWikiLanguageName()) 1729 1730 page.replaceLiveText( 1731 langHelper.createStableLinksFromWikiWords(toWikiWord) + 1732 u"\n" + text) 1666 1733 1667 1734 -
branches/mbutscher/next/lib/pwiki/WikiTxtCtrl.py
r171 r173 433 433 434 434 wx.EVT_KEY_DOWN(self, self.OnKeyDown) 435 wx.EVT_CHAR(self, self.OnChar) 435 if config.getboolean("main", "editor_useImeWorkaround", False): 436 wx.EVT_CHAR(self, self.OnChar_ImeWorkaround) 437 else: 438 wx.EVT_CHAR(self, self.OnChar) 439 436 440 wx.EVT_SET_FOCUS(self, self.OnSetFocus) 437 441 … … 495 499 """ 496 500 self.stylingThreadHolder.setThread(None) 501 self.calltipThreadHolder.setThread(None) 502 497 503 self.unloadCurrentDocPage({}) # ? 498 504 self.presenterListener.disconnect() … … 788 794 # return 789 795 790 text = self.GetText()796 # text = self.GetText() 791 797 # page.replaceLiveText(text) 792 798 if self.presenter.getMainControl().saveDocPage(page): … … 796 802 def unloadCurrentDocPage(self, evtprops=None): 797 803 ## _prof.start() 798 # Unload current page 804 # Stop threads 805 self.stylingThreadHolder.setThread(None) 806 self.calltipThreadHolder.setThread(None) 807 799 808 docPage = self.getLoadedDocPage() 800 809 if docPage is not None: … … 809 818 self.saveLoadedDocPage() 810 819 811 self.wikiPageSink.disconnect()812 820 docPage.removeTxtEditor(self) 821 813 822 self.SetDocPointer(None) 814 823 self.applyBasicSciSettings() 815 824 816 docPage.removeTxtEditor(self) 825 self.wikiPageSink.disconnect() 826 817 827 self.presenter.setDocPage(None) 818 828 … … 1349 1359 return 1350 1360 1351 pageAst = docPage.getLivePageAst(text, threadstop) 1352 threadstop.testRunning() 1361 for i in range(20): # "while True" is too dangerous 1362 formatDetails = docPage.getFormatDetails() 1363 pageAst = docPage.getLivePageAst(threadstop=threadstop) 1364 threadstop.testRunning() 1365 if not formatDetails.isEquivTo(docPage.getFormatDetails()): 1366 continue 1367 else: 1368 break 1353 1369 1354 1370 # print "--buildStyling12", repr(pageAst) … … 2449 2465 2450 2466 evt.Skip() 2467 2468 2469 def OnChar_ImeWorkaround(self, evt): 2470 """ 2471 Workaround for problem of Scintilla with some input method editors, 2472 e.g. UniKey vietnamese IME. 2473 """ 2474 key = evt.GetKeyCode() 2475 2476 # Return if this doesn't seem to be a real character input 2477 if evt.ControlDown() or key < 32: 2478 evt.Skip() 2479 return 2480 2481 if key >= wx.WXK_START and (not isUnicode() or evt.GetUnicodeKey() != key): 2482 evt.Skip() 2483 return 2484 2485 if isWin9x() and (WindowsHacks is not None): 2486 unichar = WindowsHacks.ansiInputToUnicodeChar(key) 2487 else: 2488 if isUnicode(): 2489 unichar = unichr(evt.GetUnicodeKey()) 2490 else: 2491 unichar = mbcsDec(chr(key))[0] 2492 2493 self.ReplaceSelection(unichar) 2494 2451 2495 2452 2496 -
branches/mbutscher/next/lib/pwiki/timeView/TimelinePanel.py
r166 r173 72 72 73 73 # wx.EVT_SIZE(self, self.OnSize) 74 # wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBg) 74 75 wx.EVT_CONTEXT_MENU(self, self.OnContextMenu) 75 76 # wx.EVT_MOTION(self, self.OnMouseMotion) … … 375 376 self.listContent = content 376 377 378 379 380 # def OnEraseBg(self, evt): 381 # dc = evt.GetDC() 382 # return 377 383 378 384 -
branches/mbutscher/next/lib/pwiki/timeView/WikiWordListPopup.py
r166 r173 12 12 13 13 from pwiki.WindowLayout import setWindowPos, setWindowClientSize 14 from pwiki.Configuration import MIDDLE_MOUSE_CONFIG_TO_TABMODE, isWindows 15 16 14 from pwiki.Configuration import MIDDLE_MOUSE_CONFIG_TO_TABMODE, isWindows, isOSX 15 16 17 18 # if isOSX(): 19 # _POPUP_PARENT = wx.Frame 20 # else: 21 # _POPUP_PARENT = wx.PopupWindow 17 22 18 23 … … 36 41 ID = GUI_ID.TIMESHOW_WIKIWORDLIST_POPUP 37 42 43 # if _POPUP_PARENT is wx.Frame: 38 44 wx.Frame.__init__(self, parent, ID, "WikiWordList", pos=pos, 39 45 style=wx.FRAME_FLOAT_ON_PARENT | self.FRAME_BORDER | 40 46 wx.FRAME_NO_TASKBAR) # wx.RESIZE_BORDER | 47 # else: 48 # wx.PopupWindow.__init__(self, parent, flags=self.FRAME_BORDER) 41 49 42 50 self.mainControl = mainControl -
branches/mbutscher/next/lib/pwiki/wikidata/WikiDataManager.py
r169 r173 1 from __future__ import with_statement 2 1 3 from weakref import WeakValueDictionary 2 4 import os, os.path, time, shutil, traceback … … 9 11 from wx import GetApp 10 12 11 from pwiki.Utilities import callInMainThread, TimeoutRLock13 from pwiki.Utilities import TimeoutRLock, SingleThreadExecutor 12 14 13 15 from Consts import DEADBLOCKTIMEOUT … … 157 159 if len(funcTag) == 0: 158 160 return None # TODO throw exception? 159 161 160 162 if not funcTag.startswith(u"global/"): 161 163 return None # TODO throw exception? … … 401 403 self.wikiPageDict = WeakValueDictionary() 402 404 self.funcPageDict = WeakValueDictionary() 403 self.pageUpdateDequeCondition = Condition() 404 self.pageUpdateDeque = deque() 405 self.pageUpdateThread = Thread(target=self._runUpdateQueue) 405 406 self.updateExecutor = SingleThreadExecutor() 407 self.pageRetrievingLock = TimeoutRLock(DEADBLOCKTIMEOUT) 408 # self.pageUpdateDequeCondition = Condition() 409 # self.pageUpdateDeque = deque() 410 # self.pageUpdateThread = Thread(target=self._runUpdateQueue) 406 411 407 412 self.wikiName = wikiName … … 470 475 raise writeException 471 476 472 self.pageUpdateThread.start() 473 474 477 self.updateExecutor.start() 478 479 if not self.isReadOnlyEffect(): 480 words = self.getWikiData().getWikiWordsForMetaDataState(0) 481 for word in words: 482 self.updateExecutor.executeAsync(1, self._runDatabaseUpdate, 483 word) 484 485 def _runDatabaseUpdate(self, word): 486 time.sleep(0.1) 487 try: 488 page = self.getWikiPage(word) 489 page.runDatabaseUpdate() 490 491 except WikiWordNotFoundException: 492 return 475 493 476 494 def incRefCount(self): … … 503 521 504 522 if self.refCount <= 0: 505 self. endUpdateThread() # TODO Inform user as this may take some time506 523 self.updateExecutor.end(hardEnd=True) # TODO Inform user as this may take some time 524 507 525 # Invalidate all cached pages to prevent yet running threads from 508 526 # using them … … 661 679 662 680 663 STOPUPDATEOBJECT = object() 664 665 def _runUpdateQueue(self): 666 while True: 667 with self.pageUpdateDequeCondition: 668 while len(self.pageUpdateDeque) == 0: 669 self.pageUpdateDequeCondition.wait() 670 671 page = self.pageUpdateDeque.pop() 672 673 try: 674 if page is WikiDataManager.STOPUPDATEOBJECT: 675 # We should stop here, but the problem is that previously 676 # updated pages may have pushed itself back on the deque 677 # and must be processed before thread can end 678 with self.pageUpdateDequeCondition: 679 if len(self.pageUpdateDeque) == 0: 680 return 681 else: 682 self.pushUpdatePage(WikiDataManager.STOPUPDATEOBJECT) 683 continue 684 685 page.runDatabaseUpdate() 686 except: 687 traceback.print_exc() 681 # STOPUPDATEOBJECT = object() 682 # 683 # def _runUpdateQueue(self): 684 # while True: 685 # with self.pageUpdateDequeCondition: 686 # while len(self.pageUpdateDeque) == 0: 687 # self.pageUpdateDequeCondition.wait() 688 # 689 # page = self.pageUpdateDeque.pop() 690 # 691 # try: 692 # if page is WikiDataManager.STOPUPDATEOBJECT: 693 # # We should stop here, but the problem is that previously 694 # # updated pages may have pushed itself back on the deque 695 # # and must be processed before thread can end 696 # with self.pageUpdateDequeCondition: 697 # if len(self.pageUpdateDeque) == 0: 698 # return 699 # else: 700 # self.pushUpdatePage(WikiDataManager.STOPUPDATEOBJECT) 701 # continue 702 # 703 # page.runDatabaseUpdate() 704 # # with self.pageUpdateDequeCondition: 705 # # for p in self.pageUpdateDeque: 706 # # if p is page: 707 # # break 708 # # else: 709 # 710 # 711 # except: 712 # traceback.print_exc() 688 713 689 714 690 715 def pushUpdatePage(self, page): 691 with self.pageUpdateDequeCondition: 692 self.pageUpdateDeque.appendleft(page) 693 self.pageUpdateDequeCondition.notify() 694 695 696 def endUpdateThread(self): 697 """ 698 Let update thread terminate after all pushed pages are 699 processed. 700 """ 701 if not self.pageUpdateThread.isAlive(): 702 return 703 704 self.pushUpdatePage(WikiDataManager.STOPUPDATEOBJECT) 705 706 self.pageUpdateThread.join(DEADBLOCKTIMEOUT) 707 if self.pageUpdateThread.isAlive(): 708 raise DeadBlockPreventionTimeOutError() 709 710 self.pageUpdateThread = Thread(target=self._runUpdateQueue) 711 712 713 def terminateUpdateThread(self): 714 """ 715 Let update thread terminate as soon as possible 716 """ 717 if not self.pageUpdateThread.isAlive(): 718 return 719 720 with self.pageUpdateDequeCondition: 721 self.pageUpdateDeque.clear() 722 self.pageUpdateDeque.appendleft(WikiDataManager.STOPUPDATEOBJECT) 723 self.pageUpdateDequeCondition.notify() 724 725 self.pageUpdateThread.join(DEADBLOCKTIMEOUT) 726 if self.pageUpdateThread.isAlive(): 727 raise DeadBlockPreventionTimeOutError() 728 self.pageUpdateThread = Thread(target=self._runUpdateQueue) 729 730 716 self.updateExecutor.executeAsync(0, page.runDatabaseUpdate) 717 # with self.pageUpdateDequeCondition: 718 # self.pageUpdateDeque.appendleft(page) 719 # self.pageUpdateDequeCondition.notify() 720 721 722 def getUpdateExecutor(self): 723 return self.updateExecutor 731 724 732 725 def isReadOnlyEffect(self): … … 793 786 if word doesn't exist 794 787 """ 795 if not self.wikiData.isDefinedWikiWord(wikiWord): 796 raise WikiWordNotFoundException( 797 _(u"Word '%s' not in wiki") % wikiWord) 798 799 return self.getWikiPageNoError(wikiWord) 788 with self.pageRetrievingLock: 789 if not self.wikiData.isDefinedWikiWord(wikiWord): 790 raise WikiWordNotFoundException( 791 _(u"Word '%s' not in wiki") % wikiWord) 792 793 return self.getWikiPageNoError(wikiWord) 794 800 795 801 796 def getWikiPageNoError(self, wikiWord): … … 807 802 it wasn't garbage collected yet. 808 803 """ 809 value = self.wikiPageDict.get(wikiWord) 810 811 if value is not None and isinstance(value, AliasWikiPage): 812 # Check if existing alias page is up to date 813 realWikiWord1 = value.getNonAliasPage().getWikiWord() 814 realWikiWord2 = self.wikiData.getAliasesWikiWord(wikiWord) 815 816 if realWikiWord1 != realWikiWord2: 817 # if not, retrieve new page 818 value = None 819 820 if value is None: 821 # No active page available 822 realWikiWord = self.wikiData.getAliasesWikiWord(wikiWord) 823 if wikiWord == realWikiWord: 824 # no alias 825 value = WikiPage(self, wikiWord) 826 else: 827 realpage = self.getWikiPageNoError(realWikiWord) 828 value = AliasWikiPage(self, wikiWord, realpage) 829 830 self.wikiPageDict[wikiWord] = value 831 832 value.getMiscEvent().addListener(self) 833 834 return value 804 with self.pageRetrievingLock: 805 value = self.wikiPageDict.get(wikiWord) 806 807 if value is not None and isinstance(value, AliasWikiPage): 808 # Check if existing alias page is up to date 809 realWikiWord1 = value.getNonAliasPage().getWikiWord() 810 realWikiWord2 = self.wikiData.getAliasesWikiWord(wikiWord) 811 812 if realWikiWord1 != realWikiWord2: 813 # if not, retrieve new page 814 value = None 815 816 if value is None: 817 # No active page available 818 realWikiWord = self.wikiData.getAliasesWikiWord(wikiWord) 819 if wikiWord == realWikiWord: 820 # no alias 821 value = WikiPage(self, wikiWord) 822 else: 823 realpage = self.getWikiPageNoError(realWikiWord) 824 value = AliasWikiPage(self, wikiWord, realpage) 825 826 self.wikiPageDict[wikiWord] = value 827 828 value.getMiscEvent().addListener(self) 829 830 return value 835 831 836 832 … … 840 836 page in cache if it isn't there yet. 841 837 """ 842 value = self.wikiPageDict.get(wikiWord) 843 844 if value is not None and isinstance(value, AliasWikiPage): 845 # Check if existing alias page is up to date 846 realWikiWord1 = value.getNonAliasPage().getWikiWord() 847 realWikiWord2 = self.wikiData.getAliasesWikiWord(wikiWord) 838 with self.pageRetrievingLock: 839 value = self.wikiPageDict.get(wikiWord) 840 841 if value is not None and isinstance(value, AliasWikiPage): 842 # Check if existing alias page is up to date 843 realWikiWord1 = value.getNonAliasPage().getWikiWord() 844 realWikiWord2 = self.wikiData.getAliasesWikiWord(wikiWord) 845 846 if realWikiWord1 != realWikiWord2: 847 # if not, retrieve new page 848 value = None 848 849 849 if realWikiWord1 != realWikiWord2: 850 # if not, retrieve new page 851 value = None 852 853 if value is None: 854 # No active page available 855 realWikiWord = self.wikiData.getAliasesWikiWord(wikiWord) 856 if wikiWord == realWikiWord: 857 # no alias 858 value = WikiPage(self, wikiWord) 859 else: 860 # realpage = WikiPage(self, realWikiWord) 861 realpage = self.getWikiPageNoError(realWikiWord) 862 value = AliasWikiPage(self, wikiWord, realpage) 863 864 return value 850 if value is None: 851 # No active page available 852 realWikiWord = self.wikiData.getAliasesWikiWord(wikiWord) 853 if wikiWord == realWikiWord: 854 # no alias 855 value = WikiPage(self, wikiWord) 856 else: 857 # realpage = WikiPage(self, realWikiWord) 858 realpage = self.getWikiPageNoError(realWikiWord) 859 value = AliasWikiPage(self, wikiWord, realpage) 860 861 return value 865 862 866 863 … … 872 869 (without syntax specific prefix). 873 870 """ 874 page = self.getWikiPageNoError(wikiWord) 875 page.setSuggNewPageTitle(suggNewPageTitle) 876 return page 871 with self.pageRetrievingLock: 872 page = self.getWikiPageNoError(wikiWord) 873 page.setSuggNewPageTitle(suggNewPageTitle) 874 return page 877 875 878 876 … … 882 880 """ 883 881 global _globalFuncPages 884 if funcTag.startswith(u"global/"): 885 value = getGlobalFuncPage(funcTag) 886 else: 887 value = self.funcPageDict.get(funcTag) 888 if value is None: 889 value = FunctionalPage(self, funcTag) 890 self.funcPageDict[funcTag] = value 891 892 if not value.getMiscEvent().hasListener(self): 893 value.getMiscEvent().addListener(self) 894 895 return value 896 897 898 def rebuildWiki(self, progresshandler): 882 883 with self.pageRetrievingLock: 884 if funcTag.startswith(u"global/"): 885 value = getGlobalFuncPage(funcTag) 886 else: 887 value = self.funcPageDict.get(funcTag) 888 if value is None: 889 value = FunctionalPage(self, funcTag) 890 self.funcPageDict[funcTag] = value 891 892 if not value.getMiscEvent().hasListener(self): 893 value.getMiscEvent().addListener(self) 894 895 return value 896 897 898 def rebuildWiki(self, progresshandler, onlyDirty): 899 899 """ 900 900 Rebuild the wiki … … 903 903 PersonalWikiFrame.GuiProgressHandler protocol 904 904 """ 905 self.updateExecutor.end(hardEnd=True) 905 906 self.getWikiData().refreshDefinedContentNames() 906 907 907 # get all of the wikiWords 908 wikiWords = self.getWikiData().getAllDefinedWikiPageNames() 908 if onlyDirty: 909 wikiWords = self.getWikiData().getWikiWordsForMetaDataState(0) 910 else: 911 # get all of the wikiWords 912 wikiWords = self.getWikiData().getAllDefinedWikiPageNames() 909 913 910 914 progresshandler.open(len(wikiWords) * 2 + 1) 911 progresshandler.update(0, _(u"Waiting for update thread to end"))912 self.endUpdateThread() 915 # progresshandler.update(0, _(u"Waiting for update thread to end")) 916 913 917 # re-save all of the pages 914 918 try: 915 919 step = 1 916 920 917 self.getWikiData().clearCacheTables() 918 921 if not onlyDirty: 922 self.getWikiData().clearCacheTables() 923 919 924 # Step one: update properties. There may be properties which 920 925 # define how the rest has to be interpreted, therefore they … … 954 959 955 960 wikiPage.refreshMainDbCacheFromPageAst(pageAst) 961 self.getWikiData().setMetaDataProcessed(wikiWord, 1) 956 962 except: 957 963 traceback.print_exc() … … 966 972 finally: 967 973 progresshandler.close() 968 self. pageUpdateThread.start()974 self.updateExecutor.start() 969 975 970 976 … … 1071 1077 1072 1078 wikiPage.replaceLiveText(text) 1073 wikiPage.update(text)1079 # wikiPage.update(text) 1074 1080 1075 1081 … … 1082 1088 u"\n" + content[len(prevTitle):] 1083 1089 page.replaceLiveText(content) 1084 page.update(content)1090 # page.update(content) 1085 1091 1086 1092 … … 1195 1201 text. 1196 1202 """ 1197 pg = self.getFuncPage("global/ [CCBlacklist]")1203 pg = self.getFuncPage("global/CCBlacklist") 1198 1204 bls = set(pg.getLiveText().split("\n")) 1199 pg = self.getFuncPage("wiki/ [CCBlacklist]")1205 pg = self.getFuncPage("wiki/CCBlacklist") 1200 1206 bls.update(pg.getLiveText().split("\n")) 1201 1207 self.ccWordBlacklist = bls -
branches/mbutscher/next/lib/pwiki/wikidata/compact_sqlite/WikiData.py
r167 r173 625 625 626 626 627 def addRelationship(self, word, rel):627 def _addRelationship(self, word, rel): 628 628 """ 629 629 Add a relationship from word to rel. rel is a tuple (toWord, pos). … … 631 631 """ 632 632 try: 633 # self.connWrap.execSql(634 # "insert or replace into wikirelations(word, relation) "635 # "values (?, ?)", (word, toWord))636 633 self.connWrap.execSql( 637 634 "insert or replace into wikirelations(word, relation, firstcharpos) " … … 644 641 self.deleteChildRelationships(word) 645 642 for r in childRelations: 646 self. addRelationship(word, r)643 self._addRelationship(word, r) 647 644 648 645 def deleteChildRelationships(self, fromWord): … … 1140 1137 raise DbReadAccessError(e) 1141 1138 1142 1143 def getWordsWithPropertyValue(self, key, value):1144 try:1145 return self.connWrap.execSqlQuerySingleColumn(1146 "select word from wikiwordprops where key = ? and value = ?",1147 (key, value))1148 except (IOError, OSError, sqlite.Error), e:1149 traceback.print_exc()1150 raise DbReadAccessError(e)1139 # 1140 # def getWordsWithPropertyValue(self, key, value): 1141 # try: 1142 # return self.connWrap.execSqlQuerySingleColumn( 1143 # "select word from wikiwordprops where key = ? and value = ?", 1144 # (key, value)) 1145 # except (IOError, OSError, sqlite.Error), e: 1146 # traceback.print_exc() 1147 # raise DbReadAccessError(e) 1151 1148 1152 1149 … … 1164 1161 1165 1162 1166 def setProperty(self, word, key, value):1163 def _setProperty(self, word, key, value): 1167 1164 try: 1168 1165 self.connWrap.execSql("insert into wikiwordprops(word, key, value) " … … 1178 1175 values = props[k] 1179 1176 for v in values: 1180 self. setProperty(word, k, v)1177 self._setProperty(word, k, v) 1181 1178 if k == "alias": 1182 1179 self.setAsAlias(v) # TODO … … 1224 1221 return alias 1225 1222 1226 aliases = self.getWordsWithPropertyValue("alias", alias) 1223 # aliases = self.getWordsWithPropertyValue("alias", alias) 1224 aliases = list(set(w for w,k,v in self.getPropertyTriples( 1225 None, "alias", alias))) 1226 1227 1227 if len(aliases) > 0: 1228 1228 return aliases[0] … … 1276 1276 self.deleteTodos(word) 1277 1277 for t in todos: 1278 self. addTodo(word, t)1279 1280 1281 def addTodo(self, word, todo):1278 self._addTodo(word, t) 1279 1280 1281 def _addTodo(self, word, todo): 1282 1282 try: 1283 1283 self.connWrap.execSql("insert into todos(word, todo) values (?, ?)", (word, todo)) -
branches/mbutscher/next/lib/pwiki/wikidata/original_gadfly/DbStructure.py
r169 r173 6 6 7 7 8 import string, codecs, types , traceback8 import string, codecs, types, traceback 9 9 10 10 from os import mkdir, unlink, rename … … 14 14 from pwiki.WikiExceptions import * 15 15 from pwiki.StringOps import mbcsDec, mbcsEnc, utf8Enc, utf8Dec, \ 16 removeBracketsFilename, pathEnc 16 removeBracketsFilename, pathEnc, getFileSignatureBlock, \ 17 iterCompatibleFilename 17 18 from pwiki.SearchAndReplace import SearchReplaceOperation 18 19 19 20 import gadfly 20 21 # from SqliteThin3 import *22 21 23 22 … … 35 34 return ob 36 35 36 def _dummy(ob): 37 return ob 37 38 38 39 … … 43 44 self.__dict__["dbConn"] = connection 44 45 self.__dict__["dbCursor"] = connection.cursor() 45 46 # # To make access a bit faster47 # self.__dict__["execSql"] = self.dbCursor.execute48 46 49 47 self.__dict__["execute"] = self.dbCursor.execute … … 53 51 self.__dict__["fetchone"] = self.dbCursor.fetchone 54 52 self.__dict__["fetchall"] = self.dbCursor.fetchall 55 53 54 self.__dict__["_defaultValues"] = DEFAULT_VALS 55 56 56 57 57 def __setattr__(self, attr, value): … … 60 60 def __getattr__(self, attr): 61 61 return getattr(self.dbCursor, attr) 62 63 def readDefaultValues(self): 64 self.fillDefaultValues() # DEBUG ONLY!!! 65 result = {} 66 entries = self.execSqlQuery( 67 "select tablename, field, value from defaultvalues", strConv=False) 68 69 for t, f, v in entries: 70 table = result.setdefault(t, {}) 71 table[f] = v 72 73 self.__dict__["_defaultValues"] = result 74 75 76 def fillDefaultValues(self): 77 """ 78 Write the global standard default values to database. 79 Should be called only during updateDatabase() 80 """ 81 global DEFAULT_VALS, TABLE_DEFINITIONS 82 83 # Remove "defaultvalues" if existing to fill it freshly 84 try: 85 self.execSql("drop table defaultvalues") 86 self.commit() 87 except: 88 pass 89 90 changeTableSchema(self, "defaultvalues", 91 TABLE_DEFINITIONS["defaultvalues"]) 92 93 for tn in DEFAULT_VALS.keys(): 94 for fn in DEFAULT_VALS[tn]: 95 self.execSqlInsert("defaultvalues", ("tablename", "field", 96 "value"), (tn, fn, DEFAULT_VALS[tn][fn])) 97 62 98 63 99 … … 75 111 utility method, executes the sql, returns query result 76 112 params -- Tuple of parameters for sql statement 77 strConv -- Should returned strings be seen as UTF8 and converted to unicode 78 or instead left as they are? 113 strConv -- Should returned bytestrings be seen as UTF8 and converted to 114 unicode or instead left as they are? 115 If it is a tuple of truth values, each truth value matches 116 to one of the columns of the result of db query 79 117 """ 80 118 ## print "execSqlQuery sql", sql, repr(params) … … 86 124 result = self.dbCursor.fetchall() 87 125 88 if strConv: 89 result = [tuple(map(_utf8ToUni, row)) for row in result] 126 if strConv == True: 127 result = [tuple(_utf8ToUni(v) for v in row) for row in result] 128 elif isinstance(strConv, tuple): 129 strConv = [(_utf8ToUni if sc else _dummy) for sc in strConv] 130 131 tup = tuple(range(len(strConv))) 132 result = [tuple(strConv[i](row[i]) for i in tup) for row in result] 90 133 91 134 return result … … 154 197 """ 155 198 Gadfly-specific function. In 2.0 it will be able to create default 156 values for non-existant fields. 157 """ 199 values for non-existant fields. Sqlite provides this for free. 200 """ 201 assert len(fields) == len(values) 202 tableDefs = self._defaultValues.get(table) 203 if tableDefs is not None: 204 for k in tableDefs.keys(): 205 if k not in fields: 206 fields += (k,) 207 values += (tableDefs[k],) 208 158 209 fieldStr = ", ".join(fields) 159 210 qmStr = ", ".join(["?"] * len(fields)) … … 198 249 199 250 200 VERSION_DB = 3201 VERSION_WRITECOMPAT = 3202 VERSION_READCOMPAT = 2251 VERSION_DB = 4 252 VERSION_WRITECOMPAT = 4 253 VERSION_READCOMPAT = 4 203 254 204 255 … … 223 274 ("created", t.t), 224 275 ("modified", t.t), 225 ("presentationdatablock", t.t), 226 ("wordnormcase", t.t) # TODO: Remove on next format update 227 ), 228 229 230 "wikirelations": ( # Cache 276 ("visited", t.t), 277 ("filepath", t.t), 278 ("filenamelowercase", t.t), 279 ("filesignature", t.t), 280 ("readonly", t.t), 281 ("metadataprocessed", t.t), 282 ("presentationdatablock", t.t) 283 ), 284 285 286 "wikirelations": ( # Cache, but main 231 287 ("word", t.t), 232 288 ("relation", t.t), … … 235 291 236 292 237 "wikiwordprops": ( # Cache 293 "wikiwordprops": ( # Cache, but main 238 294 ("word", t.t), 239 295 ("key", t.t), … … 242 298 243 299 244 "todos": ( # Cache 300 "todos": ( # Cache, but main 245 301 ("word", t.t), 246 302 ("todo", t.t) … … 248 304 249 305 250 "search_views": ( # Essential306 "search_views": ( # Deleted since 2.0alpha1. For updating format only 251 307 ("title", t.t), 252 308 ("datablock", t.t) … … 257 313 ("key", t.t), 258 314 ("value", t.t) 315 ), 316 317 318 "defaultvalues": ( # Essential since 2.0alpha1 319 ("tablename", t.t), 320 ("field", t.t), 321 ("value", t.t) 322 ), 323 324 325 "wikiwordmatchterms": ( 326 ("matchterm", t.t), 327 # ("matchtermnormcase", t.t), # Does not apply for Gadfly 328 ("type", t.t), 329 ("word", t.t), 330 ("firstcharpos", t.t) 331 ), 332 333 334 "datablocks": ( 335 ("unifiedname", t.t), 336 ("data", t.t) 337 ), 338 339 340 "datablocksexternal": ( 341 ("unifiedname", t.t), 342 ("filepath", t.t), 343 ("filenamelowercase", t.t), 344 ("filesignature", t.t) 259 345 ) 260 261 262 # For 2.0 versions:263 # "defaultvals": ( # Default values for new database fields264 # ("table", t.t),265 # ("field", t.t),266 # ("value", t.t)267 # )268 269 270 346 } 271 347 272 348 349 # Recycling t for setting of default values 350 351 t.r = 0.0 352 t.i = 0 353 t.imo = -1 354 t.t = u"" 355 t.b = "" 356 357 358 DEFAULT_VALS = { 359 "wikiwords": { 360 "word": t.t, 361 "created": t.r, 362 "modified": t.r, 363 "visited": t.r, 364 "filepath": t.t, 365 "filenamelowercase": t.t, 366 "filesignature": t.b, 367 "readonly": t.i, 368 "metadataprocessed": t.i, 369 "presentationdatablock": t.b 370 }, 371 372 "wikirelations": { 373 "word": t.t, 374 "relation": t.t, 375 }, 376 377 378 "wikiwordprops": { 379 "word": t.t, 380 "key": t.t, 381 "value": t.t, 382 }, 383 384 385 "todos": { 386 "word": t.t, 387 "todo": t.t, 388 }, 389 390 391 "settings": { 392 "value": t.t 393 }, 394 395 396 "wikiwordmatchterms": { 397 "matchterm": t.t, 398 # "matchtermnormcase": t.t, 399 "type": t.i, 400 "word": t.t, 401 "firstcharpos": t.imo 402 }, 403 404 405 "datablocks": { 406 "unifiedname": t.t, 407 "data": t.b 408 }, 409 410 411 "datablocksexternal": { 412 "unifiedname": t.t, 413 "filepath": t.t, 414 "filenamelowercase": t.t, 415 "filesignature": t.b 416 } 417 } 418 419 420 273 421 del t 422 274 423 275 424 … … 279 428 "wikiwordprops", 280 429 "todos", 281 "search_views", 282 "settings" 430 # "search_views", 431 "settings", 432 "wikiwordmatchterms", 433 "datablocks", 434 "datablocksexternal" 283 435 ) 284 436 … … 300 452 connwrap.execSqlNoError("create unique index wikirelations_pkey on wikirelations(word, relation)") 301 453 connwrap.execSqlNoError("create unique index settings_pkey on settings(key)") 302 connwrap.execSqlNoError("create unique index search_views_pkey on search_views(title)")454 # connwrap.execSqlNoError("create unique index search_views_pkey on search_views(title)") 303 455 connwrap.execSqlNoError("create index wikirelations_word on wikirelations(word)") 304 456 connwrap.execSqlNoError("create index wikiwordprops_word on wikiwordprops(word)") … … 312 464 associated indices 313 465 """ 314 CACHE_TABLES = ("wikirelations", "wikiwordprops", "todos") 466 CACHE_TABLES = ("wikirelations", "wikiwordprops", "todos", 467 "wikiwordmatchterms") 315 468 316 469 for tn in CACHE_TABLES: … … 348 501 forcechange -- Create a new table in any case 349 502 """ 350 503 351 504 # print "changeTableSchema1", repr(tablename), repr(schema) 352 505 353 506 # Build the sql command to create the table with new schema (needed later) 354 507 355 508 newtabletyped = ", ".join(map(lambda sc: "%s %s" % sc[:2], schema)) 356 509 newtableselect = ", ".join(map(lambda sc: sc[0], schema)) … … 359 512 newtablecreate += newtabletyped 360 513 newtablecreate += ")" 361 514 362 515 363 516 # Test if table already exists 364 517 365 518 tn = connwrap.execSqlQuerySingleItem( 366 519 "select table_name from __table_names__ where table_name='%s'" % … … 369 522 if tn is None: 370 523 # Does not exist, so simply create 371 connwrap.commit() 524 connwrap.commit() 525 print "--changeTableSchema13", repr(newtablecreate) 372 526 connwrap.execSql(newtablecreate) 373 527 connwrap.commit() … … 388 542 if n in oldcolumns: 389 543 intersect.append(n) 390 544 391 545 recreate = False 392 546 393 547 if forcechange: 394 548 recreate = True … … 413 567 df = sc[2] 414 568 else: 415 df = None 416 569 try: 570 dob = DEFAULT_VALS[tablename][n.lower()] 571 df = {0: "0", 0.0:"0.0", -1:"-1"}[dob] 572 except KeyError: 573 df = None 574 417 575 if df is None: 418 576 if t.lower() == "varchar": 419 577 df = "''" 420 578 421 579 newinsertcols.append(n) 422 580 newinsertvalues.append(df) … … 437 595 connwrap.execSql("create table tmptable(%s)" % newtabletyped) 438 596 data = connwrap.execSqlQuery("select %s from %s" % 439 (intersectselect, tablename) )440 597 (intersectselect, tablename), strConv=False) 598 441 599 for row in data: 442 600 connwrap.execSql("insert into tmptable (%s) values (%s)" % … … 459 617 460 618 619 # TODO: Does this something? 620 461 621 # Table exists, so retrieve list of columns 462 622 oldcolumns = connwrap.execSqlQuerySingleColumn( … … 486 646 connection.startup("wikidb", dataDir) 487 647 connwrap = ConnectWrap(connection) 488 648 489 649 try: 490 650 for tn in MAIN_TABLES: … … 595 755 596 756 597 def updateDatabase(connwrap, dataDir): 757 758 def updateDatabase(connwrap, dataDir, pagefileSuffix): 598 759 """ 599 760 Update a database from an older version to current (checkDatabaseFormat() … … 783 944 784 945 formatver = 3 785 786 787 # Will be used later 788 # if formatver == 2: 789 # print "format update" 790 # changeTableSchema(connwrap, "wikiwords", 791 # TABLE_DEFINITIONS["wikiwords"]) 792 # 793 # connwrap.execSql("update wikiwords set word_sk = word") 794 # 795 # formatver = 3 946 947 948 if formatver == 3: 949 950 # Update "wikiwords" schema and create new tables 951 for tn in ("wikiwords", "wikiwordmatchterms", "datablocks", 952 "datablocksexternal", "defaultvalues"): 953 changeTableSchema(connwrap, tn, TABLE_DEFINITIONS[tn]) 954 955 # (Re)fill "defaultvalues" and read them into connection wrapper 956 connwrap.fillDefaultValues() 957 connwrap.readDefaultValues() 958 959 960 # Transfer "search_views" data to "datablocks" table 961 searches = connwrap.execSqlQuery( 962 "select title, datablock from search_views", 963 strConv=(True, False)) 964 965 for title, data in searches: 966 connwrap.execSql( 967 "insert into datablocks(unifiedname, data) "+\ 968 "values (?, ?)", (u"savedsearch/" + title, data)) 969 970 connwrap.execSql("drop table search_views") 971 972 allWords = connwrap.execSqlQuerySingleColumn("select word from wikiwords") 973 974 # Divide into functional and wiki pages 975 wikiWords = [] 976 funcWords = [] 977 for w in allWords: 978 if w.startswith('['): 979 funcWords.append(w) 980 else: 981 wikiWords.append(w) 982 983 # Fill the new fields in table "wikiwords" 984 for wikiWord in wikiWords: 985 filename = wikiWord + pagefileSuffix 986 fullPath = join(dataDir, filename) 987 filesig = getFileSignatureBlock(fullPath) 988 989 connwrap.execSql("update wikiwords set filepath = ?, " 990 "filenamelowercase = ?, filesignature = ? " 991 "where word = ?", (filename, filename.lower(), filesig, 992 wikiWord)) 993 994 # Move functional pages to new table "datablocksexternal" and rename them 995 for funcWord in funcWords: 996 if funcWord not in (u"[TextBlocks]", u"[PWL]", u"[CCBlacklist]"): 997 continue # Error ?! 998 999 unifName = u"wiki/" + funcWord[1:-1] 1000 fullPath = join(dataDir, funcWord + pagefileSuffix) 1001 1002 icf = iterCompatibleFilename(unifName, u".data") 1003 1004 for i in range(10): # Actual "while True", but that's too dangerous 1005 newFilename = icf.next() 1006 newPath = join(dataDir, newFilename) 1007 1008 if exists(pathEnc(newPath)): 1009 # A file with the designated new name of fn already exists 1010 # -> do nothing 1011 continue 1012 1013 try: 1014 print "--rename34", repr((pathEnc(fullPath), pathEnc(newPath))) 1015 rename(pathEnc(fullPath), pathEnc(newPath)) 1016 connwrap.execSqlInsert("datablocksexternal", ("unifiedname", 1017 "filepath", "filenamelowercase", "filesignature"), 1018 (unifName, newFilename, newFilename.lower(), 1019 getFileSignatureBlock(newPath))) 1020 connwrap.execSql("delete from wikiwords where word = ?", 1021 (funcWord,)) 1022 break 1023 except (IOError, OSError): 1024 traceback.print_exc() 1025 continue 1026 1027 1028 # --- WikiPad 2.0alpha1 reached (formatver=4, writecompatver=4, 1029 # readcompatver=4) --- 1030 1031 formatver = 4 1032 1033 1034 1035 796 1036 797 1037 … … 813 1053 def updateDatabase2(connwrap): 814 1054 """ 815 Second update function. Called when database version is current.1055 Second update function. Called even when database version is current. 816 1056 Performs further updates 817 1057 """ … … 919 1159 create the "wordnormcase" column content. A "-" means the column contains 920 1160 invalid data. 1161 1162 1163 ++ 1.9final to 2.0alpha1 (formatver=4): 1164 1165 Table "search_views" removed (taken over by "datablocks") 1166 1167 Table "wikiwords" modified: 1168 "wordnormcase" removed (taken over by table "wikiwordmatchterms") 1169 "visited" added, float, time as returned by time.time() 1170 "metadataprocessed" added, int, state how far meta-data like attributes 1171 or todos are processed. 1172 0: Processing needed yet 1173 1: Processed. Meta-data in db is in sync with page text. 1174 1175 "filepath" added, unistring. Name of the wiki file containing the 1176 content (not applicable for compact sqlite db). The path is relative 1177 to "data" directory, so it shouldn't contain more than the filename 1178 under most circumstances 1179 "filenamelowercase" added, unistring. Lower case of filename in filepath 1180 (not applicable for compact sqlite db). Needed when testing if a new 1181 filename is already in use. It is allowed that multiple pages have 1182 the same filenamelowercase, but this should only happen by explicit 1183 user interaction. 1184 "filesignature" added, binary. Data block to identify file 1185 (probably size + mod. date is enough) to check for external 1186 modification of file (not applicable for compact sqlite db) 1187 "readonly" added, boolean (actually integer), mark page as currently 1188 read-only 1189 1190 Table "wikiwordmatchterms" added: 1191 To enlist all possible strings to search for a word. This includes the 1192 words itself, explicit aliases (via "alias" attribute) and implicit 1193 aliases (headings, optional). 1194 1195 matchterm: unistring to search for. Same string may appear multiple 1196 times if no instance of it has type 2. 1197 matchtermnormcase: unistring. Matchterm in lowercase (not applicable 1198 for original gadfly db). 1199 type: integer: 1200 0: Real word, overwriting is an error and not possible 1201 1: Explicit alias, overwriting issues a warning. 1202 2: Term is implicit alias and may be overwritten by explicit 1203 alias or real word 1204 word: unistring. Real wiki word to go to 1205 firstcharpos: integer. Position in real wiki word page to go to 1206 (-1 for default position) 1207 1208 Table "datablocks" added: 1209 Store searches and other small data directly in database. 1210 1211 unifiedname: unistring with unified name of the data 1212 data: binary data 1213 1214 1215 Table "datablocksexternal" added: 1216 Store links to e.g. functional pages, revision infos here 1217 (not applicable for compact sqlite db) 1218 1219 unifiedname: String with unified name of the data 1220 filepath: Name of the binary data file containing the content. The path 1221 is relative to "data" directory, so it shouldn't contain more than 1222 the filename under most circumstances. 1223 filenamelowercase: Lower case of filename in filepath. Needed when 1224 testing if a new filename is already in use. It is allowed that 1225 multiple pages have the same filenamelowercase, but this should 1226 only happen by explicit user interaction. 1227 filesignature: Binary data block to identify file (probably size + mod. 1228 date is enough) to check for external modification of file. 1229 1230 Table "defaultvalues" added: 1231 Gadfly-specific to provide default values for fields for better 1232 future format-compatibility. Sqlite has built-in support for defaults 1233 tablename: bytestring, name of table 1234 field: bytestring, name of field in table 1235 value: arbitrary, default object for this field in table 1236 921 1237 """ -
branches/mbutscher/next/lib/pwiki/wikidata/original_gadfly/WikiData.py
r169 r173 16 16 from os import mkdir, unlink, rename, stat # listdir 17 17 from os.path import exists, join, basename 18 import os.path 19 18 20 from time import time, localtime 19 21 import datetime … … 34 36 35 37 38 import Consts 36 39 from pwiki.WikiExceptions import * # TODO make normal import? 37 40 from pwiki import SearchAndReplace 38 41 39 42 from pwiki.StringOps import pathEnc, pathDec, utf8Enc, utf8Dec, BOM_UTF8, \ 40 fileContentToUnicode, loadEntireTxtFile, writeEntireTxtFile, Conjunction 43 fileContentToUnicode, loadEntireTxtFile, loadEntireFile, \ 44 writeEntireFile, Conjunction, iterCompatibleFilename, \ 45 getFileSignatureBlock, lineendToInternal, guessBaseNameByFilename, \ 46 createRandomString 41 47 42 48 … … 83 89 if formatcheck == 1: 84 90 try: 85 DbStructure.updateDatabase(self.connWrap, self.dataDir) 91 DbStructure.updateDatabase(self.connWrap, self.dataDir, 92 self.pagefileSuffix) 86 93 except: 87 94 self.connWrap.rollback() … … 136 143 def getContent(self, word): 137 144 try: 138 if (not exists(self.getWikiWordFileName(word))):139 raise WikiFileNotFoundException(140 _(u"Wiki page not found for word: %s") % word)145 # if (not exists(self.getWikiWordFileName(word))): 146 # raise WikiFileNotFoundException( 147 # _(u"Wiki page not found for word: %s") % word) 141 148 142 149 content = loadEntireTxtFile(self.getWikiWordFileName(word)) 143 # fp = open(self.getWikiWordFileName(word), "rU")144 # try:145 # content = fp.read()146 # finally:147 # fp.close()148 150 149 151 return fileContentToUnicode(content) … … 151 153 traceback.print_exc() 152 154 raise DbReadAccessError(e) 155 153 156 154 157 # TODO Remove method … … 174 177 if creadate is None: 175 178 creadate = ti 176 179 180 fileName = self.createWikiWordFileName(word) 177 181 self.connWrap.execSqlInsert("wikiwords", ("word", "created", 178 "modified", "presentationdatablock", "wordnormcase"), 179 (word, creadate, moddate, "", "")) 180 181 # self.execSql("insert into wikiwords(word, created, modified, " 182 # "presentationdatablock, wordnormcase) " 183 # "values (?, ?, ?, '', '')", (word, creadate, moddate)) 182 "modified", "presentationdatablock", "filepath", 183 "filenamelowercase"), 184 (word, creadate, moddate, "", fileName, 185 fileName.lower())) 184 186 else: 185 187 self.execSql("update wikiwords set modified = ? where word = ?", 186 188 (moddate, word)) 187 189 188 190 self.commitNeeded = True 189 191 except (IOError, OSError, ValueError), e: 190 192 traceback.print_exc() 191 193 raise DbWriteAccessError(e) 192 194 193 195 self._getCachedContentNames()[word] = 1 194 196 … … 204 206 """ 205 207 try: 206 # output = open(self.getWikiWordFileName(word), 'w') 207 # try: 208 # output.write(BOM_UTF8) 209 # output.write(utf8Enc(content)[0]) 210 # finally: 211 # output.close() 212 writeEntireTxtFile(self.getWikiWordFileName(word), 213 (BOM_UTF8, utf8Enc(content)[0])) 208 self._updatePageEntry(word, moddate, creadate) 209 210 filePath = self.getWikiWordFileName(word) 211 writeEntireFile(filePath, content, True) 212 213 fileSig = getFileSignatureBlock(filePath) 214 self.execSql("update wikiwords set filesignature = ?, " 215 "metadataprocessed = ? where word = ?", (fileSig, 0, word)) 216 214 217 except (IOError, OSError, ValueError), e: 215 218 traceback.print_exc() 216 219 raise DbWriteAccessError(e) 217 218 self._updatePageEntry(word, moddate, creadate)219 220 220 221 … … 226 227 """ 227 228 try: 228 self.execSql("update wikiwords set word = ? where word = ?", 229 (newWord, oldWord)) 229 oldFilePath = self.getWikiWordFileNameRaw(oldWord) 230 head, oldFileName = os.path.split(oldFilePath) 231 # head = pathDec(head) 232 # oldFileName = pathDec(oldFileName) 233 234 fileName = self.createWikiWordFileName(word) 235 newFilePath = os.path.join(head, fileName) 236 237 os.rename(pathEnc(os.path.join(self.dataDir, oldFilePath)), 238 pathEnc(os.path.join(self.dataDir, newFilePath))) 239 240 self.execSql("update wikiwords set word = ?, filepath = ?, " 241 "filenamelowercase = ?, metadataprocessed = ? where word = ?", 242 (newWord, newFilePath, fileName.lower(), 0, oldWord)) 230 243 self.commitNeeded = True 231 232 rename(self.getWikiWordFileName(oldWord), 233 self.getWikiWordFileName(newWord)) 244 234 245 del self._getCachedContentNames()[oldWord] 235 246 self._getCachedContentNames()[newWord] = 1 … … 242 253 def deleteContent(self, word): 243 254 try: 255 fileName = self.getWikiWordFileName(word) 244 256 self.execSql("delete from wikiwords where word = ?", (word,)) 245 257 self.commitNeeded = True 246 if exists( self.getWikiWordFileName(word)):247 unlink( self.getWikiWordFileName(word))258 if exists(fileName): 259 unlink(fileName) 248 260 del self._getCachedContentNames()[word] 249 261 except (IOError, OSError, ValueError), e: … … 327 339 elif field == "visited":
