Changeset 173

Show
Ignore:
Timestamp:
01/04/09 19:50:36 (4 years ago)
Author:
mbutscher
Message:

2.0preAlpha (internal)

More multithread problems fixed.
Seems to fully work now for Original Gadfly DB.

Location:
branches/mbutscher/next
Files:
33 modified

Legend:

Unmodified
Added
Removed
  • branches/mbutscher/next/Consts.py

    r169 r173  
    2727 
    2828 
    29 DEADBLOCKTIMEOUT = 120 
     29DEADBLOCKTIMEOUT = 30 
    3030 
    3131 
    32  
     32# Scintilla known format types and numbers 
    3333FormatTypes = Enumeration("FormatTypes", ["Default", "WikiWord", 
    3434        "AvailWikiWord", "Bold", "Italic", "Heading1", "Heading2", "Heading3", 
     
    3636        ], 0) 
    3737 
     38 
     39# Store hints for WikiData.storeDataBlock() 
     40 
     41DATABLOCK_STOREHINT_INTERN = 0 
     42DATABLOCK_STOREHINT_EXTERN = 1 
  • branches/mbutscher/next/WikidPad.xrc

    r166 r173  
    640640          <object class="sizeritem"> 
    641641            <object class="wxStaticText"> 
    642               <label>Props. to</label> 
    643               <style></style> 
     642              <label>Attrs. to</label> 
    644643            </object> 
    645644            <option>0</option> 
     
    673672          <object class="sizeritem"> 
    674673            <object class="wxStaticText"> 
    675               <label>Props. to</label> 
    676               <style></style> 
     674              <label>Attrs. to</label> 
    677675            </object> 
    678676            <option>0</option> 
     
    19091907      <orient>wxVERTICAL</orient> 
    19101908      <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> 
    19341941      </object> 
    19351942      <object class="spacer"> 
    19361943        <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> 
    19371963      </object> 
    19381964    </object> 
  • branches/mbutscher/next/WikidPadStarter.py

    r169 r173  
    166166exception = None 
    167167 
     168#  
     169# try: 
     170#     import psyco 
     171#     psyco.full() 
     172# except ImportError: 
     173#     traceback.print_exc() 
     174 
     175 
     176 
    168177 
    169178def main(): 
  • branches/mbutscher/next/extensions/WikidPadParser.py

    r171 r173  
    661661 
    662662def 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 
    663669    fragmentNode = t.findFlatByName("searchFragment") 
    664670    if fragmentNode is not None: 
     
    671677        t.anchorLink = t.anchorLink.getString() 
    672678 
    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") 
    678679 
    679680 
     
    710711        t.anchorLink = t.anchorLink.getString() 
    711712 
    712     t.titleNode = t.findFlatByName("title") 
     713 
    713714 
    714715 
     
    791792 
    792793# TODO anchor/fragment 
    793 wikiWordCc = Group(buildRegex(ur"\b(?<!~)" + WikiWordCcPAT + ur"\b", "word")) 
     794wikiWordCc = buildRegex(ur"\b(?<!~)" + WikiWordCcPAT + ur"\b", "word") + \ 
     795        Optional(MatchFirst([searchFragmentExtern, wikiWordAnchorLink])) # Group( ) 
    794796wikiWordCc = wikiWordCc.setResultsName("wikiWord").setName("wikiWordCc")\ 
    795797        .setParseStartAction(preActCheckWikiWordCcAllowed)\ 
  • branches/mbutscher/next/lib/pwiki/AdditionalDialogs.py

    r171 r173  
    2929class SelectWikiWordDialog(wx.Dialog): 
    3030    """ 
    31     Called for "Append/Prepend wiki word" in tree node conext menu 
     31    Called for "Append/Prepend wiki word" in tree node context menu 
    3232    """ 
    3333 
    34     def __init__(self, pWiki, ID, title="Select Wiki Word", 
     34    def __init__(self, pWiki, parent, ID, title=u"Select Wiki Word", 
    3535                 pos=wx.DefaultPosition, size=wx.DefaultSize, 
    3636                 style=wx.NO_3D): 
     
    4040 
    4141        self.pWiki = pWiki 
    42         self.wikiWord = None   
     42        self.wikiWord = None 
     43        self.listContent = [] 
     44        self.ignoreTextChange = 0 
     45         
    4346        res = wx.xrc.XmlResource.Get() 
    44         res.LoadOnDialog(self, self.pWiki, "SelectWikiWordDialog") 
     47        res.LoadOnDialog(self, parent, "SelectWikiWordDialog") 
    4548 
    4649        self.SetTitle(title) 
     
    6265        wx.EVT_LISTBOX_DCLICK(self, GUI_ID.lb, self.OnOk) 
    6366 
     67 
    6468    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 
    8294 
    8395        self.EndModal(wx.ID_OK) 
     
    88100 
    89101    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()) 
    91107        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 
    97115 
    98116    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 
    100122 
    101123    def OnCharText(self, evt): 
     
    107129        else: 
    108130            evt.Skip() 
    109              
     131 
    110132 
    111133    def OnCharListBox(self, evt): 
     
    115137        else: 
    116138            evt.Skip() 
     139             
     140 
     141SelectWikiWordDialog.runModal = staticmethod(runDialogModalFactory(SelectWikiWordDialog)) 
     142 
    117143      
    118144 
    119145class 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", 
    121147                 pos=wx.DefaultPosition, size=wx.DefaultSize, 
    122148                 style=wx.NO_3D): 
     
    126152 
    127153        self.pWiki = pWiki 
    128         self.wikiWord = u""   
     154        self.value = None 
     155        self.listContent = [] 
     156        self.ignoreTextChange = 0 
     157 
    129158        res = wx.xrc.XmlResource.Get() 
    130         res.LoadOnDialog(self, self.pWiki, "OpenWikiWordDialog") 
     159        res.LoadOnDialog(self, parent, "OpenWikiWordDialog") 
    131160 
    132161        self.SetTitle(title) 
     
    158187 
    159188    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) 
    177233            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 
    204238 
    205239        return True 
     
    207241 
    208242    def GetValue(self): 
    209         return self.wikiWord 
     243        return self.value 
    210244 
    211245    def OnText(self, evt): 
    212         self.wikiWord = guiToUni(evt.GetString()) 
     246        if self.ignoreTextChange: 
     247            self.ignoreTextChange -= 1 
     248            return 
     249 
    213250        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 
    219260 
    220261    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 
    222267 
    223268    def OnCharText(self, evt): 
     
    246291                self.pWiki.getWikiDefaultWikiLanguage(), 
    247292                self.pWiki.getWikiDocument()) 
    248         wikiWord = langHelper.extractWikiWordFromLink(self.wikiWord) 
     293        entered = guiToUni(self.ctrls.text.GetValue()) 
     294        wikiWord = langHelper.extractWikiWordFromLink(entered) 
    249295 
    250296        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) 
    253299            self.ctrls.text.SetFocus() 
    254300            return 
     
    259305            return 
    260306 
    261         self.wikiWord = wikiWord 
    262         self.pWiki.activatePageByUnifiedName(u"wikipage/" + self.wikiWord, 
     307        self.value = (wikiWord, 0, wikiWord, -1) 
     308        self.pWiki.activatePageByUnifiedName(u"wikipage/" + wikiWord, 
    263309                tabMode=0) 
    264310        self.EndModal(wx.ID_OK) 
     
    272318 
    273319 
     320OpenWikiWordDialog.runModal = staticmethod(runDialogModalFactory(OpenWikiWordDialog)) 
     321 
    274322  
    275323 
     
    337385        self.SetFocus() 
    338386 
    339         wx.EVT_BUTTON(self, wx.ID_OK, self.OnOkPressed) 
    340         wx.EVT_LIST_ITEM_ACTIVATED(self, self.lc.GetId(), self.OnOkPressed) 
     387        wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) 
     388        wx.EVT_LIST_ITEM_ACTIVATED(self, self.lc.GetId(), self.OnOk) 
    341389 
    342390    def GetValue(self): 
     
    346394        return self.value 
    347395 
    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): 
    366415        no = self.lc.GetNextItem(-1, state = wx.LIST_STATE_SELECTED) 
    367416        if no > -1: 
     
    374423 
    375424 
    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 
     494SelectIconDialog.runModal = staticmethod(runDialogModalFactory(SelectIconDialog)) 
     495 
    443496 
    444497 
     
    12951348 
    12961349 
    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                            
     1350class SimpleInfoDialog(wx.Dialog): 
     1351    def __init__(self, *args, **kwargs): 
     1352        wx.Dialog.__init__(self, *args, **kwargs) 
     1353 
    13051354        self.txtBgColor = self.GetBackgroundColour() 
    13061355 
    13071356        button = wx.Button(self, wx.ID_OK) 
    13081357        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() 
    13291362 
    13301363        inputsizer = wx.BoxSizer(wx.HORIZONTAL) 
     
    13321365        inputsizer.Add((0, 0), 1)   # Stretchable spacer 
    13331366 
    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) 
    13371372        self.Fit() 
    13381373 
     
    13411376 
    13421377 
    1343     def _buildLine(self, label, value): 
     1378    def _addLine(self, label, value): 
    13441379        inputsizer = wx.BoxSizer(wx.HORIZONTAL) 
    13451380        inputsizer.Add(wx.StaticText(self, -1, label), 1, 
     
    13491384        inputsizer.Add(ctl, 1, wx.ALL | wx.EXPAND, 5) 
    13501385         
    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 
     1401class 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 
     1433class 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())) 
    13531471 
    13541472 
  • branches/mbutscher/next/lib/pwiki/Configuration.py

    r166 r173  
    529529    ("main", "show_lineNumbers"): "False", 
    530530    ("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 
    531533    ("main", "wikiWord_rename_wikiLinks"): "2", # When renaming wiki word, should it try to rename links to the word, too? 
    532534            # 0:No, 1:Yes, 2:Ask for each renaming 
  • branches/mbutscher/next/lib/pwiki/DocPages.py

    r169 r173  
    11from __future__ import with_statement 
    2 from time import time 
    3 import os.path, re, struct, traceback, threading 
     2 
     3import os.path, re, struct, time, traceback, threading 
    44 
    55import wx 
     
    77from MiscEvent import MiscEventSourceMixin 
    88 
    9 from Consts import DEADBLOCKTIMEOUT 
     9import Consts 
    1010from WikiExceptions import * 
    1111 
    1212from StringOps import strToBool, fileContentToUnicode, BOM_UTF8, utf8Enc, \ 
    13         utf8Dec, pathEnc, loadEntireTxtFile, writeEntireTxtFile 
     13        utf8Dec, pathEnc, loadEntireTxtFile, writeEntireFile 
    1414 
    1515from Utilities import DUMBTHREADSTOP, FunctionThreadStop, TimeoutRLock, \ 
     
    2525 
    2626 
    27 class DocPage(MiscEventSourceMixin): 
     27class DocPage(object, MiscEventSourceMixin): 
    2828    """ 
    2929    Abstract common base class for WikiPage and FunctionalPage 
    3030    """ 
     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 
    3142    def __init__(self, wikiDocument): 
    3243        MiscEventSourceMixin.__init__(self) 
     
    3546        self.txtEditors = []  # List of all editors (views) showing this page 
    3647        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,  
    4358                # if no text editor is registered, this cache is invalid 
     59#         self.pageState = STATE_TEXTCACHE_MATCHES_EDITOR 
    4460 
    4561    def invalidate(self): 
     
    4965        """ 
    5066        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 
    5679 
    5780    def getWikiDocument(self): 
    5881        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 
    5994 
    6095    def addTxtEditor(self, txted): 
     
    6398        """ 
    6499        # TODO Set text in editor if first editor is created? 
    65         with self.textOperationLock: 
     100        with self.txtEditorListLock: 
    66101            if not txted in self.txtEditors: 
    67102                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 
    71108                self.txtEditors.append(txted) 
    72109 
     
    77114        If the last is removed, text is saved to database. 
    78115        """ 
    79         with self.textOperationLock: 
     116        with self.txtEditorListLock: 
    80117            try: 
    81118                idx = self.txtEditors.index(txted) 
    82119                if len(self.txtEditors) == 1: 
    83                     self.writeToDatabase() 
    84                     self.livePageAst = None 
    85                     self.textCache = None 
     120                    self.setEditorText(None) 
    86121 
    87122                del self.txtEditors[idx] 
     
    96131        or None if no editor is associated. 
    97132        """ 
    98         with self.textOperationLock: 
     133        with self.txtEditorListLock: 
    99134            if len(self.txtEditors) > 0: 
    100135                return self.txtEditors[0] 
     
    116151            self.setDirty(True) 
    117152            txtEditor = self.getTxtEditor() 
     153            self.livePageAst = None 
    118154            if txtEditor is not None: 
    119155                # page is in text editor(s), so call AppendText on one of it 
    120156                # TODO Call self.SetReadOnly(False) first? 
    121                 self.livePageAst = None 
    122157                txtEditor.AppendText(text) 
    123158                return 
    124159 
    125160            # Modify database 
    126             text = self.getLiveText() + text 
    127             self.livePageAst = None 
    128             self.textCache = text 
    129161            self.writeToDatabase(fireEvent=fireEvent) 
     162 
    130163#             self.save(text, fireEvent=fireEvent) 
    131164#  
     
    134167 
    135168 
    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() 
    148180 
    149181 
     
    153185        from the database 
    154186        """ 
    155         for i in xrange(50):     # Actually "while True:", but this may run without end 
    156             with self.textOperationLock: 
    157                 if self.textCache is not None: 
    158                     return self.textCache 
    159                  
    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                     continue 
    174  
    175                 return self.textCache 
    176          
     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 
    177209        # Something went terribly wrong 
    178         raise DeadBlockPreventionTimeOutError() 
     210#         raise DeadBlockPreventionTimeOutError() 
    179211 
    180212 
     
    194226            self.setDirty(True) 
    195227            txtEditor = self.getTxtEditor() 
     228            self.livePageAst = None 
    196229            if txtEditor is not None: 
    197230                # page is in text editor(s), so call replace on one of it 
    198231                # TODO Call self.SetReadOnly(False) first? 
    199232                txtEditor.replaceText(text) 
    200                 self.livePageAst = None 
    201                 self.textCache = text 
    202233                return 
    203234 
    204             self.livePageAst = None 
    205             self.textCache = text 
    206235            self.writeToDatabase(fireEvent=fireEvent) 
    207236             
    208 #             self.save(text, fireEvent=fireEvent) 
    209 #  
    210 #         self.update(text, fireEvent=fireEvent)   # TODO Handle auto-generated areas 
    211  
    212237 
    213238    def informEditorTextChanged(self, changer): 
    214239        """ 
    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()) 
    222245 
    223246        self.fireMiscEventProps({"changed editor text": True, 
     
    300323        directly if can be done fast 
    301324        """ 
    302         self.update(fireEvent=fireEvent) 
    303  
    304  
    305     def update(self, fireEvent=True): 
    306         """ 
    307         Update additional cached informations of doc page 
    308         """ 
    309325        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 
    310333 
    311334 
     
    367390        return self.realWikiPage.initiateUpdate() 
    368391         
    369     def update(self, fireEvent=True): 
    370         return self.realWikiPage.update(fireEvent) 
     392#     def update(self, fireEvent=True): 
     393#         return self.realWikiPage.update(fireEvent) 
    371394 
    372395    def getLivePageAst(self, fireEvent=True, dieOnChange=False,  
     
    389412         
    390413        # 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 
    392422        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() 
    393438         
    394439 
     
    399444        if dirt: 
    400445            if self.saveDirtySince is None: 
    401                 ti = time() 
     446                ti = time.time() 
    402447                self.saveDirtySince = ti 
    403448                self.updateDirtySince = ti 
     
    414459 
    415460 
     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 
    416481 
    417482class WikiPage(DataCarryingPage): 
     
    421486    Fetched via the WikiDataManager.getWikiPage method. 
    422487    """ 
    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 
    426496 
    427497    def __init__(self, wikiDocument, wikiWord): 
    428498        DataCarryingPage.__init__(self, wikiDocument) 
    429499 
    430         self.livePageBaseText = None   # Cached text on which the page-ast 
    431                 #  of this page is based. 
     500        self.livePageBasePlaceHold = None   # liveTextPlaceHold object on which 
     501                # the livePageAst is based. 
    432502                # This is needed to check for changes when saving 
    433503        self.livePageBaseFormatDetails = None   # Cached format details on which the 
    434504                # page-ast bases 
     505 
    435506#         self.metaDataProcessLock = threading.RLock()  # lock while processing 
    436507#                 # meta-data 
     
    445516                # newly created 
    446517 
     518        if self.getWikiData().getMetaDataState(self.wikiWord) != 1: 
     519            self.updateDirtySince = time.time() 
     520 
    447521    def getWikiWord(self): 
    448522        return self.wikiWord 
     
    475549                     
    476550        if self.modified is None: 
    477             ti = time() 
    478             self.modified, self.created, self.visited = ti, ti, 0.0 
     551            ti = time.time() 
     552            self.modified, self.created, self.visited = ti, ti, ti 
    479553         
    480554        return self.modified, self.created, self.visited 
     
    499573        return self.getWikiData().getParentRelationships(self.wikiWord) 
    500574 
    501          
     575 
    502576    def getChildRelationships(self, existingonly=False, selfreference=True, 
    503577            withFields=(), excludeSet=frozenset(), 
     
    782856        formatting 
    783857        """ 
    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                    ) 
    802877 
    803878 
     
    827902            self.getWikiData().setContent(self.wikiWord, self.getLiveText()) 
    828903            self.saveDirtySince = None 
    829      
     904#             self.dbContentPlaceHold = object() 
     905            if self.getEditorText() is None: 
     906                self.liveTextPlaceHold = object() 
     907 
     908 
    830909            # Clear timestamp cache 
    831910            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 
    832932 
    833933 
     
    848948 
    849949            with self.textOperationLock: 
    850                 # Current state 
    851950                text = self.getLiveText() 
     951                liveTextPlaceHold = self.liveTextPlaceHold 
    852952                formatDetails = self.getFormatDetails() 
    853953 
    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 
    863973 
    864974            if dieOnChange: 
    865975                if threadstop is DUMBTHREADSTOP: 
    866                     threadstop = FunctionThreadStop(lambda: text is self.textCache) 
     976                    threadstop = FunctionThreadStop( 
     977                            lambda: liveTextPlaceHold is self.liveTextPlaceHold) 
    867978                else: 
    868979                    origThreadstop = threadstop 
    869980                    threadstop = FunctionThreadStop( 
    870981                            lambda: origThreadstop.isRunning() and  
    871                             text is self.textCache) 
     982                            liveTextPlaceHold is self.liveTextPlaceHold) 
    872983 
    873984            pageAst = self.parseTextInContext(text, formatDetails=formatDetails, 
    874985                    threadstop=threadstop) 
    875986 
     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(): 
    876999            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 
    9001030 
    9011031 
     
    9261056 
    9271057 
    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): 
    9291087        """ 
    9301088        Update properties (aka attributes) only. 
     
    9421100 
    9431101        def addProperty(key, value): 
     1102            threadstop.testRunning() 
    9441103            values = props.get(key) 
    9451104            if not values: 
     
    9521111        for node in propNodes: 
    9531112            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): 
    9751140        """ 
    9761141        Update everything else (todos, relations). 
     
    9781143        """ 
    9791144        if self.isReadOnlyEffect(): 
    980             return 
     1145            return True   # return True or False? 
    9811146 
    9821147        todos = [] 
     
    9851150 
    9861151        def addTodo(todo): 
     1152            threadstop.testRunning() 
    9871153            if todo not in todos: 
    9881154                todos.append(todo) 
    9891155 
    990         def addChildRelationship(toWord, pos):                 
     1156        def addChildRelationship(toWord, pos): 
     1157            threadstop.testRunning() 
    9911158            if toWord not in childRelationSet: 
    9921159                childRelations.append((toWord, pos)) 
    9931160                childRelationSet.add(toWord) 
    9941161 
     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 
    9951168        todoTokens = pageAst.iterDeepByName("todoEntry") 
    9961169        for t in todoTokens: 
    9971170            addTodo(t.key + t.delimiter + t.valueNode.getString()) 
     1171         
     1172        threadstop.testRunning() 
    9981173 
    9991174        wwTokens = pageAst.iterDeepByName("wikiWord") 
     
    10011176            addChildRelationship(t.wikiWord, t.pos) 
    10021177 
    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 
    10151241 
    10161242        if fireEvent: 
    10171243            callInMainThreadAsync(self.fireMiscEventKeys, 
    10181244                    ("updated wiki page", "updated page")) 
     1245 
     1246        return valid 
    10191247 
    10201248 
     
    10261254            if self.isReadOnlyEffect(): 
    10271255                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 
    10321263            formatDetails = self.getFormatDetails() 
    1033              
     1264 
    10341265        try: 
    1035             self.getLivePageAst(dieOnChange=True) 
     1266            pageAst = self.getLivePageAst(dieOnChange=True) 
    10361267        except NotCurrentThreadException: 
    1037             self.initiateUpdate() 
     1268#             self.initiateUpdate() 
    10381269            return 
    10391270 
    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()        
    10441275                return 
    10451276 
    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()        
    10481296                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()        
    10551307 
    10561308 
     
    10591311        """ 
    10601312        Initiate update of page meta-data. This function may call update 
    1061         directly if can be done fast 
     1313        directly if it can be done fast 
    10621314        """ 
    10631315        with self.textOperationLock: 
     
    10651317 
    10661318 
    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) 
    10861340 
    10871341 
     
    14461700    def _loadGlobalPage(self, subtag): 
    14471701        tbLoc = os.path.join(GetApp().getGlobalConfigSubDir(), 
    1448                 subtag + ".wiki") 
     1702                "[%s].wiki" % subtag) 
    14491703        try: 
    14501704            tbContent = loadEntireTxtFile(tbLoc) 
     
    14541708 
    14551709 
    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: 
    14601713            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"" 
    14611721 
    14621722 
     
    14701730 
    14711731 
    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"): 
    14751735            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) 
    14791739 
    14801740 
     
    14881748        """ 
    14891749        return ParseUtilities.WikiPageFormatDetails(noFormat=True) 
     1750 
     1751 
     1752    def getLivePageAstIfAvailable(self): 
     1753        return self.getLivePageAst() 
    14901754 
    14911755 
     
    15041768            if pageAst is not None: 
    15051769                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 
    15191780 
    15201781 
     
    15221783    def _saveGlobalPage(self, text, subtag): 
    15231784        tbLoc = os.path.join(GetApp().getGlobalConfigSubDir(), 
    1524                 subtag+".wiki") 
    1525  
    1526         writeEntireTxtFile(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): 
    15301791        if self.isReadOnlyEffect(): 
    15311792            return 
    15321793 
    15331794        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) 
    15381798        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) 
    15411809 
    15421810 
     
    15481816            return 
    15491817         
    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. 
    15661837        """ 
    15671838        if self.isReadOnlyEffect(): 
    15681839            return 
    15691840 
    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")) 
    15971870 
    15981871    def isReadOnlyEffect(self): 
     
    16341907 
    16351908 
     1909 
     1910# TODO: Remove for Python 3.0 
    16361911def _cmpNumbersItem1(a, b): 
    16371912    """ 
     
    16541929#     set or changed. 
    16551930_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"), 
    16631938        } 
    16641939 
  • branches/mbutscher/next/lib/pwiki/DocStructureCtrl.py

    r169 r173  
    162162            depth = max(depth, 1) 
    163163 
    164             pageAst = docPage.getLivePageAst(text, threadstop) 
     164            pageAst = docPage.getLivePageAst(threadstop=threadstop) 
    165165 
    166166            result = [] 
  • branches/mbutscher/next/lib/pwiki/Exporters.py

    r171 r173  
    122122    """ 
    123123    return unicodeToCompFilename(removeBracketsFilename(fn)) 
     124 
    124125 
    125126def _escapeAnchor(name): 
     
    991992        content = wikiPage.getLiveText() 
    992993        formatDetails = wikiPage.getFormatDetails() 
    993         self.basePageAst = wikiPage.getLivePageAst(content) 
     994        self.basePageAst = wikiPage.getLivePageAst() 
    994995 
    995996        if self.linkConverter is None: 
     
    12581259 
    12591260        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) 
    12611264            if datablock is not None: 
    12621265                searchOp = SearchReplaceOperation() 
     
    21652168                if self.writeSavedSearches: 
    21662169                    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 
    21722180                        self.exportFile.write(base64BlockEncode(datablock)) 
    2173                          
     2181 
    21742182                        self.exportFile.write("\n%s\n" % self.separator) 
    21752183 
  • branches/mbutscher/next/lib/pwiki/I18nPoUpdater.py

    r166 r173  
    99 
    1010 
    11 # Nearly same as functions in StringOps, but copied here to reduce dependencies 
     11# Similar to functions in StringOps, but copied here to reduce dependencies 
    1212def loadEntireTxtFile(filename): 
    1313    """ 
  • branches/mbutscher/next/lib/pwiki/Importers.py

    r168 r173  
    77# import wx 
    88 
     9import Consts 
    910from StringOps import * 
    1011 
     
    177178                            self.importItemFuncPage(tag[9:]) 
    178179                        elif tag.startswith(u"savedsearch/"): 
    179                             self.importItemSavedSearch(tag[12:]) 
     180                            self.importItemSavedSearch(tag) 
    180181                        elif tag.startswith(u"wikipage/"): 
    181182                            self.importItemWikiPage(tag[9:]) 
     
    236237         
    237238     
    238     def importItemSavedSearch(self, subtag): 
     239    def importItemSavedSearch(self, unifName): 
    239240        # The subtag is the title of the search 
    240241         
     
    244245        try: 
    245246            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             
    247253        except TypeError: 
    248254            # base64 decoding failed 
  • branches/mbutscher/next/lib/pwiki/Localization.py

    r166 r173  
    55from xml.dom import minidom 
    66 
    7 from StringOps import utf8Enc, loadEntireTxtFile, writeEntireTxtFile, pathEnc 
     7from StringOps import utf8Enc, loadEntireFile, writeEntireFile, pathEnc 
    88 
    99 
     
    7070        ascend -- If True sort ascending, descending otherwise 
    7171        """ 
     72        # TODO Python 3.0 will no longer support compare function in sort 
    7273        lst.sort(self.strcoll, reverse=not ascend) 
    7374#         if not ascend: 
     
    111112        raise NotImplementedError   # abstract 
    112113 
    113 #     def normCase(self, s): 
    114 #         """ 
    115 #         Normalize case for string s. It is recommended to just return 
    116 #         the UTF-8 encoding of the "lowered" string s. This should be even 
    117 #         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 if 
    124 #             case is ignored) 
    125 #          
    126 #         """ 
    127 #         assert 0  # abstract 
    128114 
    129115 
     
    161147 
    162148 
    163 #     def normCase(self, s): 
    164 #         """ 
    165 #         Normalize case for unicode string s and return byte string 
    166 #         """ 
    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] 
    176149         
    177150 
     
    196169        return locale.strxfrm(s) 
    197170 
    198 #     def normCase(self, s): 
    199 #         return utf8Enc(s.lower)[0] 
    200171 
    201172 
     
    242213         
    243214    def strxfrm(self, s): 
    244         assert 0 # Not properly implemented 
     215        assert 0 # Not properly implemented. TODO Needed for Python 3.0 
    245216 
    246217        return locale.strxfrm(s) 
     
    344315 
    345316    try: 
    346         data = loadEntireTxtFile(os.path.join(appDir, "langlist.txt")) 
     317        data = loadEntireFile(os.path.join(appDir, "langlist.txt"), True) 
    347318        data = data.decode("utf-8") 
    348319         
     
    591562    if i18nPoPath is None: 
    592563        # No translation 
    593         return loadEntireTxtFile(os.path.join(appDir, baseXrcName + ".xrc")) 
     564        return loadEntireFile(os.path.join(appDir, baseXrcName + ".xrc"), True) 
    594565 
    595566    # Retrieve modification time of .po and .xrc file to compare with cache 
     
    608579                os.stat(pathEnc(cachePath)).st_mtime > poModTime: 
    609580            # Valid cache found 
    610             return loadEntireTxtFile(cachePath) 
     581            return loadEntireFile(cachePath, True) 
    611582    except: 
    612583        traceback.print_exc()  # Really? 
     
    621592                os.stat(pathEnc(cachePath)).st_mtime > poModTime: 
    622593            # Valid cache found 
    623             return loadEntireTxtFile(cachePath) 
     594            return loadEntireFile(cachePath, True) 
    624595    except: 
    625596        traceback.print_exc()  # Really? 
     
    628599    # No valid cache -> build content 
    629600 
    630     untranslated = loadEntireTxtFile(os.path.join(appDir, baseXrcName + ".xrc")) 
     601    untranslated = loadEntireFile( 
     602            os.path.join(appDir, baseXrcName + ".xrc"), True) 
    631603 
    632604    xmlDoc = minidom.parseString(untranslated) 
     
    673645                baseXrcName + "_" + i18nLocale + ".xrc") 
    674646         
    675         writeEntireTxtFile(cachePath, translated) 
     647        writeEntireFile(cachePath, translated, True) 
    676648         
    677649        return translated 
     
    684656                baseXrcName + "_" + i18nLocale + ".xrc") 
    685657         
    686         writeEntireTxtFile(cachePath, translated) 
     658        writeEntireFile(cachePath, translated, True) 
    687659         
    688660        return translated 
  • branches/mbutscher/next/lib/pwiki/OptionsDialog.py

    r166 r173  
    528528            ("wikiLockFile_ignore", "cbWikiLockFileIgnore", "b"), 
    529529            ("wikiLockFile_create", "cbWikiLockFileCreate", "b"), 
     530            ("editor_useImeWorkaround", "cbEditorUseImeWorkaround", "b"), 
    530531 
    531532 
  • branches/mbutscher/next/lib/pwiki/PersonalWikiFrame.py

    r171 r173  
    6565        unescapeWithRe, escapeForIni, unescapeForIni, \ 
    6666        wikiUrlToPathWordAndAnchor, urlFromPathname, flexibleUrlUnquote, \ 
    67         strftimeUB, pathEnc, loadEntireTxtFile, writeEntireTxtFile, \ 
     67        strftimeUB, pathEnc, loadEntireFile, writeEntireFile, \ 
    6868        pathWordAndAnchorToWikiUrl, relativeFilePath, pathnameFromUrl 
    6969 
     
    101101        self.progDlg = wx.ProgressDialog(self.title, self.msg + u" " * 20, 
    102102                sum + self.addsteps, self.parent, self.flags) 
    103  
     103                 
    104104    def update(self, step, msg): 
    105105        """ 
     
    211211        tbLoc = join(self.globalConfigSubDir, "[TextBlocks].wiki") 
    212212        if not exists(pathEnc(tbLoc)): 
    213             writeEntireTxtFile(tbLoc, (BOM_UTF8,  
     213            writeEntireFile(tbLoc,  
    214214"""importance: high;a=[importance: high]\\n 
    215215importance: low;a=[importance: low]\\n 
     
    217217wrap: 80;a=[wrap: 80]\\n 
    218218camelCaseWordsEnabled: false;a=[camelCaseWordsEnabled: false]\\n 
    219 """)) 
     219""", True) 
    220220#         self.globalConfigLoc = join(globalConfigDir, "WikidPad.config") 
    221221        self.configuration = wx.GetApp().createCombinedConfiguration() 
     
    419419                fileName) 
    420420        if exists(pathEnc(extensionFileName)): 
    421             userUserExtension = loadEntireTxtFile(extensionFileName) 
     421            userUserExtension = loadEntireFile(extensionFileName, True) 
    422422        else: 
    423423            userUserExtension = None 
     
    425425        extensionFileName = join(self.wikiAppDir, 'user_extensions', fileName) 
    426426        if exists(pathEnc(extensionFileName)): 
    427             userExtension = loadEntireTxtFile(extensionFileName) 
     427            userExtension = loadEntireFile(extensionFileName, True) 
    428428        else: 
    429429            userExtension = None 
    430430 
    431431        extensionFileName = join(self.wikiAppDir, 'extensions', fileName) 
    432         systemExtension = loadEntireTxtFile(extensionFileName) 
     432        systemExtension = loadEntireFile(extensionFileName, True) 
    433433 
    434434        return importCode(systemExtension, userExtension, userUserExtension, 
     
    754754        if wikiData is not None: 
    755755            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 
    756762                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), 
    758765                        menuID=GUI_ID.MENU_REBUILD_WIKI, 
    759766                        updatefct=self.OnUpdateDisReadOnlyWiki) 
    760767     
     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 
    761774            self.addMenuItem(maintenanceMenu, _(u'Reconnect...'), 
    762775                    _(u'Reconnect to database after connection failure'), 
     
    931944        if wikiDoc is not None and self.requireReadAccess(): 
    932945            try: 
    933                 page = wikiDoc.getFuncPage(u"wiki/[TextBlocks]") 
     946                page = wikiDoc.getFuncPage(u"wiki/TextBlocks") 
    934947                treeData = TextTree.buildTreeFromText(page.getContent(), 
    935948                        TextTree.TextBlocksEntry.factory) 
     
    944957 
    945958 
    946         page = WikiDataManager.getGlobalFuncPage(u"global/[TextBlocks]") 
     959        page = WikiDataManager.getGlobalFuncPage(u"global/TextBlocks") 
    947960        treeData = TextTree.buildTreeFromText(page.getContent(), 
    948961                TextTree.TextBlocksEntry.factory) 
     
    10011014        wikiDoc = self.getWikiDocument() 
    10021015 
    1003         page = WikiDataManager.getGlobalFuncPage(u"global/[FavoriteWikis]") 
     1016        page = WikiDataManager.getGlobalFuncPage(u"global/FavoriteWikis") 
    10041017        treeData = TextTree.buildTreeFromText(page.getContent(), 
    10051018                TextTree.FavoriteWikisEntry.factory) 
     
    10881101         
    10891102        if entry is not None: 
    1090             page = WikiDataManager.getGlobalFuncPage(u"global/[FavoriteWikis]") 
     1103            page = WikiDataManager.getGlobalFuncPage(u"global/FavoriteWikis") 
    10911104            text = page.getLiveText() 
    10921105            if len(text) == 0 or text[-1] == u"\n": 
     
    10991112 
    11001113    def OnManageFavoriteWikis(self, evt): 
    1101         self.activatePageByUnifiedName(u"global/[FavoriteWikis]", tabMode=2) 
     1114        self.activatePageByUnifiedName(u"global/FavoriteWikis", tabMode=2) 
    11021115 
    11031116 
     
    20072020        # Build favorite wikis tool buttons 
    20082021        wikiDoc = self.getWikiDocument() 
    2009         page = WikiDataManager.getGlobalFuncPage(u"global/[FavoriteWikis]") 
     2022        page = WikiDataManager.getGlobalFuncPage(u"global/FavoriteWikis") 
    20102023        treeData = TextTree.buildTreeFromText(page.getContent(), 
    20112024                TextTree.FavoriteWikisEntry.factory) 
     
    29522965 
    29532966            # reset the gui 
    2954 #             self.resetGui() 
    29552967            self.buildMainMenu() 
    29562968     
     
    30023014            wikiWordsToOpen = wwo 
    30033015 
    3004             # set status 
    3005     #         self.statusBar.SetStatusText( 
    3006     #                 uniToGui(u"Opened wiki '%s'" % self.wikiName), 0) 
    3007      
    30083016            # now try and open the last wiki page as leftmost tab 
    30093017            if len(wikiWordsToOpen) > 0 and wikiWordsToOpen[0] != self.wikiName: 
     
    31403148                self.clipboardInterceptor.catchOff() 
    31413149 
     3150            self.fireMiscEventKeys(("closed current wiki",)) 
    31423151            self.resetGui() 
    3143             self.fireMiscEventKeys(("closed current wiki",)) 
    31443152 
    31453153 
     
    34043412        self.statusBar.PushStatusText(u"Saving page", 0) 
    34053413        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: 
    34093416                # No editor -> nothing to do 
    34103417                return False 
     
    34253432#                         page.update(self.getActiveEditor().updateAutoGenAreas(text))   # ? 
    34263433                        page.writeToDatabase() 
    3427  
    3428  
    3429 #                       pageAst = page.getLivePageAst() 
    34303434                        self.propertyChecker.initiateCheckPage(page) 
    3431 #                         if pageAst is not None: 
    3432 #                             self.propertyChecker.checkPage(page, pageAst) #  !! 
     3435 
     3436 
    34333437 
    34343438                        # trigger hooks 
    34353439                        self.hooks.savedWikiWord(self, word) 
    34363440                    else: 
    3437                         # for functional pages 
    3438 #                         page.save(text) 
    3439 #                         page.update(text) 
    34403441                        page.writeToDatabase() 
    34413442 
     
    36463647            return 
    36473648        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")] 
    36503651        except (IOError, OSError, DbAccessError), e: 
    36513652            self.lostAccess(e) 
     
    39843985 
    39853986    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() 
    39973995 
    39983996 
     
    40464044 
    40474045 
    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             return 
    4056  
    4057         dlg = SavedVersionsDialog(self, -1) 
    4058         dlg.CenterOnParent(wx.BOTH) 
    4059  
    4060         version = None 
    4061         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() 
    40814079 
    40824080 
     
    45724570 
    45734571 
    4574     def rebuildWiki(self, skipConfirm=False): 
     4572    def rebuildWiki(self, skipConfirm=False, onlyDirty=False): 
    45754573        if self.isReadOnlyWiki(): 
    45764574            return 
     
    45874585                progresshandler = wxGuiProgressHandler(_(u"Rebuilding wiki"), 
    45884586                        _(u"Rebuilding wiki"), 0, self) 
    4589                 self.getWikiDataManager().rebuildWiki(progresshandler) 
     4587                self.getWikiDataManager().rebuildWiki(progresshandler, 
     4588                        onlyDirty=onlyDirty) 
    45904589 
    45914590                self.tree.collapse() 
     
    48044803        dlg.Destroy() 
    48054804 
     4805    def OnCmdShowWikiJobDialog(self, evt): 
     4806        dlg = WikiJobDialog(self, -1, self) 
     4807        dlg.ShowModal() 
     4808        dlg.Destroy() 
     4809 
    48064810 
    48074811    # ---------------------------------------------------------------------------------------- 
     
    50835087            self.Iconize(True) 
    50845088        else: 
     5089#             tracer.runctx('self._prepareExitWiki()', globals(), locals()) 
    50855090            self._prepareExitWiki() 
    50865091            self.Destroy() 
  • branches/mbutscher/next/lib/pwiki/Printing.py

    r166 r173  
    379379 
    380380    def OnBeginPrinting(self): 
    381         self.base_OnBeginPrinting() 
     381        wx.Printout.OnBeginPrinting(self) 
    382382        self.mm2logUnitsFactor = None 
    383383        return True 
  • branches/mbutscher/next/lib/pwiki/PropertyHandling.py

    r168 r173  
    33""" 
    44 
     5from __future__ import with_statement 
     6 
     7 
    58import traceback, threading 
    69 
     
    1316from WikiExceptions import * 
    1417 
    15 from Utilities import callInMainThread 
     18from Utilities import callInMainThreadAsync 
    1619from Configuration import isUnicode 
    1720 
    1821from LogWindow import LogMessage 
     22 
     23from DocPages import WikiPage 
     24 
    1925 
    2026 
     
    296302            return 
    297303             
    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 
    299309        if len(words) > 1 or (len(words) > 0 and words[0] != wikiWord): 
    300310            msg = LogMessage(self.mainControl, LogMessage.SEVERITY_WARNING, 
     
    493503 
    494504    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) 
    498530 
    499531 
     
    503535        log window with messages if necessary 
    504536        """ 
     537        if wikiPage.isInvalid(): 
     538            return 
     539 
    505540        foundProps = set() 
    506541        wikiWord = wikiPage.getWikiWord() 
    507         pageAst = wikiPage.getLivePageAst() 
     542        pageAst = wikiPage.getLivePageAstIfAvailable() 
     543        if pageAst is None: 
     544            return 
     545 
    508546        propNodes = wikiPage.extractPropertyNodesFromPageAst(pageAst) 
    509547 
     
    531569                                node.pos + node.strLength, match) 
    532570 
    533             callInMainThread(self.mainControl.getLogWindow().updateForWikiWord, 
     571            callInMainThreadAsync(self.mainControl.getLogWindow().updateForWikiWord, 
    534572                    wikiWord, self.msgCollector) 
    535573        except: 
  • branches/mbutscher/next/lib/pwiki/SearchAndReplaceDialogs.py

    r167 r173  
    44 
    55from MiscEvent import MiscEventSourceMixin, KeyFunctionSink 
     6import Consts 
    67from wxHelper import * 
    78 
     
    881882                    return  # Cancel 
    882883                     
    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 
    884889                    answer = wx.MessageBox( 
    885890                            _(u"Do you want to overwrite existing search '%s'?") % 
     
    889894                        continue 
    890895 
    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 
    893902                self._refreshSavedSearchesList() 
    894903                break 
     
    922931 
    923932    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] 
    925938        self.pWiki.getCollator().sort(self.savedSearches) 
    926          
     939 
    927940        self.ctrls.lbSavedSearches.Clear() 
    928941        for search in self.savedSearches: 
     
    950963 
    951964        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]) 
    953968        self._refreshSavedSearchesList() 
    954969 
     
    972987            return False 
    973988         
    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 
    976994        sarOp = SearchReplaceOperation() 
    977995        sarOp.setPackedSettings(datablock) 
  • branches/mbutscher/next/lib/pwiki/SpellChecker.py

    r167 r173  
    131131    def rereadPersonalWordLists(self): 
    132132        wdm = self.mainControl.getWikiDataManager() 
    133         self.globalPwlPage = wdm.getFuncPage("global/[PWL]") 
     133        self.globalPwlPage = wdm.getFuncPage("global/PWL") 
    134134        self.spellChkAddedGlobal = \ 
    135135                set(self.globalPwlPage.getLiveText().split("\n")) 
    136136 
    137         self.localPwlPage = wdm.getFuncPage("wiki/[PWL]") 
     137        self.localPwlPage = wdm.getFuncPage("wiki/PWL") 
    138138        self.spellChkAddedLocal = \ 
    139139                set(self.localPwlPage.getLiveText().split("\n")) 
  • branches/mbutscher/next/lib/pwiki/StringOps.py

    r168 r173  
    1212from struct import pack, unpack 
    1313 
    14 import difflib, codecs, os.path, random, base64, locale 
     14import difflib, codecs, os.path, random, base64, locale, hashlib, tempfile 
    1515 
    1616# import urllib_red as urllib 
     
    151151 
    152152 
     153# TODO! 
    153154def unicodeToCompFilename(us): 
    154155    """ 
     
    236237 
    237238 
    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 
     273def 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 
    245282    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() 
    252284    finally: 
    253285        rf.close() 
    254286 
     287 
     288def 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 
     363def getFileSignatureBlock(filename): 
     364    statinfo = os.stat(pathEnc(filename)) 
     365 
     366    return pack(">BQd", 0, statinfo.st_size, statinfo.st_mtime) 
    255367 
    256368 
     
    691803         
    692804    return unicode(result.value()) 
    693  
    694  
    695  
    696  
    697  
    698 # def flexibleUrlUnquote(link): 
    699 #     if link is None: 
    700 #         return None 
    701 #  
    702 #     try: 
    703 #         linkAscii = link.encode("ascii", "strict") 
    704 #     except UnicodeEncodeError: 
    705 #         # URL contains non-ascii characters, so skip the following 
    706 #         # unquoting 
    707 #         linkAscii = None 
    708 #  
    709 #     if linkAscii: 
    710 #         # Get bytes out of percent-quoted URL 
    711 #         linkBytes = urllib.unquote(linkAscii) 
    712 #         # Try to interpret bytes as UTF-8 
    713 #         try: 
    714 #             link = linkBytes.decode("utf8", "strict") 
    715 #         except UnicodeDecodeError: 
    716 #             # Failed -> try mbcs 
    717 #             try: 
    718 #                 link = mbcsDec(linkBytes, "strict")[0] 
    719 #             except UnicodeDecodeError: 
    720 #                 # Failed, too -> leave link unmodified 
    721 #                 pass 
    722 #      
    723 #     return link 
    724805 
    725806 
     
    9731054 
    9741055 
    975  
    976  
    977 _RNDBASESEQ = u"1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
     1056def _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 
     1072def 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 
     1157def _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 
     1172def 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" 
    9781195 
    9791196def createRandomString(length): 
    9801197    """ 
    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 
     1218def 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 
    9841246 
    9851247 
  • branches/mbutscher/next/lib/pwiki/Utilities.py

    r169 r173  
    8080 
    8181class ExecutionResult(object): 
    82     __slots__ = ("result", "exception") 
     82    __slots__ = ("result", "exception", "state") 
    8383 
    8484    def __init__(self): 
    8585        self.result = None 
    8686        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 
    87107 
    88108 
     
    92112    def __init__(self, daemon=False): 
    93113        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 
    98137 
    99138 
    100139    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 
    101162 
    102163    def _runQueue(self): 
    103164        while True: 
    104165            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 
    106170                    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 
    109177            try: 
    110178                if fct is SingleThreadExecutor.STOPOBJECT: 
    111                     # We should stop here, but the problem is that previously 
    112                     # updated pages may have pushed itself back on the deque 
     179                    # We should stop here, but the problem is that other 
     180                    # operations may itself pushed back on the deque 
    113181                    # 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 
    115191 
    116192#                 tracer.runctx('retObj.result = fct(*args, **kwargs)', globals(), locals()) 
    117                 retObj.result = fct(*args, **kwargs) 
     193                retObj.setResult(fct(*args, **kwargs)) 
    118194 
    119195            except Exception, e: 
    120196                traceback.print_exc() # ? 
    121                 retObj.exception = e 
     197                retObj.setException(e) 
    122198            finally: 
    123199                if event is not None: 
     
    125201 
    126202 
    127     def execute(self, fct, *args, **kwargs): 
     203    def execute(self, idx, fct, *args, **kwargs): 
    128204        if threading.currentThread() is self.thread: 
    129205            return fct(*args, **kwargs) 
     
    133209 
    134210        with self.dequeCondition: 
    135             self.deque.appendleft((fct, args, kwargs, event, retObj)) 
     211            self.deques[idx].appendleft((fct, args, kwargs, event, retObj)) 
    136212            self.dequeCondition.notify() 
    137213 
     
    145221        return retObj.result 
    146222 
     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 
    147243    __call__ = execute 
    148244 
    149245 
    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(): 
    152259            return 
    153260 
    154261        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)) 
    157267            self.dequeCondition.notify() 
    158268 
    159269        self.thread.join(120) 
     270         
    160271        if self.thread.isAlive(): 
    161272            raise DeadBlockPreventionTimeOutError() 
     273         
     274        self.thread = None 
     275 
    162276 
    163277 
     
    265379            endtime = _time() + self.__timeout 
    266380            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 
    267395            while True: 
    268396                gotit = self.__block.acquire(0) 
     
    272400                if remaining <= 0: 
    273401                    break 
    274                 delay = min(delay * 2, remaining, .05) 
     402#                 delay = min(delay * 2, remaining, .05) 
     403                delay = min(delay * 2, remaining, .02) 
    275404                _sleep(delay) 
    276405            if not gotit: 
     
    279408                 
    280409                print "----Lock acquired by" 
    281                 print "".join(self.__acquiredStackTrace) 
     410                print "".join(traceback.format_list(self.__acquiredStackTrace)) 
    282411 
    283412                raise DeadBlockPreventionTimeOutError() 
     
    288417                self.__owner = me 
    289418                self.__count = 1 
     419                self.__acquiredStackTrace = traceback.extract_stack() 
    290420                 
    291421                return gotit 
  • branches/mbutscher/next/lib/pwiki/WikiHtmlViewIE.py

    r171 r173  
     1from __future__ import with_statement 
     2 
    13## import hotshot 
    24## _prof = hotshot.Profile("hotshot.prf") 
     
    1820    try: 
    1921        # 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 
    2024        import comtypes.gen._3050F1C5_98B5_11CF_BB82_00AA00BDCE0B_0_4_0 as _dummy 
    2125        import comtypes.gen.MSHTML as _dummy 
     
    3236 
    3337from StringOps import uniToGui, utf8Enc, utf8Dec, urlFromPathname, urlQuote, \ 
    34         writeEntireTxtFile, pathnameFromUrl 
     38        pathnameFromUrl 
    3539 
    3640from TempFileSet import TempFileSet 
     
    240244                htpath = self.htpaths[self.currentHtpath] 
    241245 
    242                 writeEntireTxtFile(htpath, utf8Enc(html)[0]) 
     246                with open(htpath, "w") as f: 
     247                    f.write(utf8Enc(html)[0]) 
     248 
    243249                url = "file:" + urlFromPathname(htpath) 
    244250                self.currentLoadedUrl = url 
     
    252258                htpath = self.htpaths[self.currentHtpath] 
    253259                 
    254                 writeEntireTxtFile(htpath, utf8Enc(html)[0]) 
     260                with open(htpath, "w") as f: 
     261                    f.write(utf8Enc(html)[0]) 
     262 
    255263                url = "file:" + urlFromPathname(htpath) 
    256264                self.currentLoadedUrl = url 
  • branches/mbutscher/next/lib/pwiki/WikiPyparsing.py

    r168 r173  
    461461                    yield node 
    462462 
     463    # Just for symmetry 
     464    iterFlat = __iter__ 
     465 
    463466 
    464467    def iterSelectedDeepByName(self, name, deepSet, start=0): 
     
    500503                    yield inner 
    501504 
    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 
    505516 
    506517 
  • branches/mbutscher/next/lib/pwiki/WikiTreeCtrl.py

    r169 r173  
    1313 
    1414from WikiExceptions import WikiWordNotFoundException, InternalError 
    15 from Utilities import StringPathSet 
     15from Utilities import StringPathSet, SingleThreadExecutor 
    1616 
    1717from Configuration import MIDDLE_MOUSE_CONFIG_TO_TABMODE 
     
    771771 
    772772    def listChildren(self): 
    773         wikiData = self.treeCtrl.pWiki.getWikiData() 
     773        wikiDocument = self.treeCtrl.pWiki.getWikiDocument() 
    774774        result = [] 
    775775        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))) 
    777779        self.treeCtrl.pWiki.getCollator().sort(words)                 
    778 #         return [WikiWordSearchNode(self.treeCtrl, self, w) for w in words] 
    779780        return [WikiWordPropertySearchNode(self.treeCtrl, self, w, 
    780781                key, self.value) for w in words] 
    781782 
    782 #         return map(lambda w: WikiWordSearchNode(self.treeCtrl, 
    783 #                 wikiData.getPage(w, toload=[""])), words) 
    784783 
    785784    def nodeEquality(self, other): 
     
    845844         
    846845    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 
    849850    def listChildren(self): 
    850851        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 
    854859 
    855860 
     
    876881    def listChildren(self): 
    877882        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 
    879887        searchOp = SearchReplaceOperation() 
    880888        searchOp.setPackedSettings(datablock) 
     
    10481056    def listChildren(self): 
    10491057        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") 
    10571065                ] 
    10581066 
     
    10651073    __slots__ = ("funcTag", "label") 
    10661074     
    1067 #     TAG_TO_LABEL_MAP = {    # Maps the func tag to the node's label 
    1068 #             "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  
    10761075    def __init__(self, tree, parentNode, funcTag): 
    10771076        AbstractNode.__init__(self, tree, parentNode) 
     
    11331132        self.SetSpacing(0) 
    11341133        self.refreshGenerator = None  # Generator called in OnIdle 
     1134        self.refreshExecutor = SingleThreadExecutor() 
    11351135#        self.refreshCheckChildren = [] # List of nodes to check for new/deleted children 
    11361136        self.sizeVisible = True 
     
    12441244                ("options changed", self.onOptionsChanged), 
    12451245        ), wx.GetApp().getMiscEvent(), self) 
     1246         
     1247 
     1248        self.refreshExecutor.start() 
    12461249 
    12471250 
     
    12591262        self.__sinkWikiDoc.disconnect() 
    12601263        self.__sinkApp.disconnect() 
     1264        self.refreshExecutor.end(hardEnd=True) 
    12611265 
    12621266    def collapse(self): 
     
    14611465 
    14621466    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        """ 
    14631474        try:         
    14641475            nodeObj = self.GetPyData(parentnodeid) 
    14651476        except Exception: 
    14661477            raise StopIteration 
    1467          
     1478 
    14681479        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 
    14721490        self.setNodePresentation(parentnodeid, nodeStyle) 
    14731491         
     
    14781496        if not self.IsExpanded(parentnodeid): 
    14791497            raise StopIteration 
    1480              
     1498 
    14811499        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 
    14861509        if True: 
    14871510            # We have to recreate the children of this node 
    14881511             
     1512 
    14891513            # 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() 
    14911522             
    14921523            if self.expandedNodePathes is not None: 
     
    15171548                                yield sg 
    15181549                        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 
    15201557                            self.setNodePresentation(nodeid, nodeStyle) 
    15211558 
     
    15361573                        tci += 1 
    15371574                        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 
    15411585                        yield None 
    15421586 
     
    15461590                    newnodeid = self.AppendItem(parentnodeid, "") 
    15471591                    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()) 
    15501601 
    15511602 
    15521603            # End of loop, no more new children, remove possible remaining 
    15531604            # children in tree 
    1554  
    15551605 
    15561606            while idIdx < len(oldChildNodeIds): 
     
    15791629                else: 
    15801630                    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()) 
    15821638 
    15831639                    yield None 
     
    16471703 
    16481704    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: 
    16511709            parentWord = self.GetPyData(self.contextMenuNode).getWikiWord() 
    16521710            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 
    16561718 
    16571719    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: 
    16601724            parentWord = self.GetPyData(self.contextMenuNode).getWikiWord() 
    16611725            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) 
    16661733 
    16671734 
  • branches/mbutscher/next/lib/pwiki/WikiTxtCtrl.py

    r171 r173  
    433433 
    434434        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 
    436440        wx.EVT_SET_FOCUS(self, self.OnSetFocus) 
    437441 
     
    495499        """ 
    496500        self.stylingThreadHolder.setThread(None) 
     501        self.calltipThreadHolder.setThread(None) 
     502 
    497503        self.unloadCurrentDocPage({})   # ? 
    498504        self.presenterListener.disconnect() 
     
    788794#             return 
    789795 
    790         text = self.GetText() 
     796#         text = self.GetText() 
    791797#         page.replaceLiveText(text) 
    792798        if self.presenter.getMainControl().saveDocPage(page): 
     
    796802    def unloadCurrentDocPage(self, evtprops=None): 
    797803        ## _prof.start() 
    798         # Unload current page 
     804        # Stop threads 
     805        self.stylingThreadHolder.setThread(None) 
     806        self.calltipThreadHolder.setThread(None) 
     807 
    799808        docPage = self.getLoadedDocPage() 
    800809        if docPage is not None: 
     
    809818                self.saveLoadedDocPage() 
    810819 
    811             self.wikiPageSink.disconnect() 
    812              
     820            docPage.removeTxtEditor(self) 
     821 
    813822            self.SetDocPointer(None) 
    814823            self.applyBasicSciSettings() 
    815824 
    816             docPage.removeTxtEditor(self) 
     825            self.wikiPageSink.disconnect() 
     826             
    817827            self.presenter.setDocPage(None) 
    818828 
     
    13491359                return 
    13501360             
    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 
    13531369             
    13541370#             print "--buildStyling12", repr(pageAst) 
     
    24492465 
    24502466            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 
    24512495 
    24522496 
  • branches/mbutscher/next/lib/pwiki/timeView/TimelinePanel.py

    r166 r173  
    7272 
    7373#         wx.EVT_SIZE(self, self.OnSize) 
     74#         wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBg) 
    7475        wx.EVT_CONTEXT_MENU(self, self.OnContextMenu) 
    7576#         wx.EVT_MOTION(self, self.OnMouseMotion) 
     
    375376        self.listContent = content 
    376377 
     378 
     379 
     380#     def OnEraseBg(self, evt): 
     381#         dc = evt.GetDC() 
     382#         return 
    377383 
    378384 
  • branches/mbutscher/next/lib/pwiki/timeView/WikiWordListPopup.py

    r166 r173  
    1212 
    1313from pwiki.WindowLayout import setWindowPos, setWindowClientSize 
    14 from pwiki.Configuration import MIDDLE_MOUSE_CONFIG_TO_TABMODE, isWindows 
    15  
    16  
     14from 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 
    1722 
    1823 
     
    3641            ID = GUI_ID.TIMESHOW_WIKIWORDLIST_POPUP 
    3742 
     43#         if _POPUP_PARENT is wx.Frame: 
    3844        wx.Frame.__init__(self, parent, ID, "WikiWordList", pos=pos, 
    3945                style=wx.FRAME_FLOAT_ON_PARENT | self.FRAME_BORDER |      
    4046                wx.FRAME_NO_TASKBAR)     # wx.RESIZE_BORDER |  
     47#         else: 
     48#             wx.PopupWindow.__init__(self, parent, flags=self.FRAME_BORDER) 
    4149 
    4250        self.mainControl = mainControl 
  • branches/mbutscher/next/lib/pwiki/wikidata/WikiDataManager.py

    r169 r173  
     1from __future__ import with_statement 
     2 
    13from weakref import WeakValueDictionary 
    24import os, os.path, time, shutil, traceback 
     
    911from wx import GetApp 
    1012 
    11 from pwiki.Utilities import callInMainThread, TimeoutRLock 
     13from pwiki.Utilities import TimeoutRLock, SingleThreadExecutor 
    1214 
    1315from Consts import DEADBLOCKTIMEOUT 
     
    157159    if len(funcTag) == 0: 
    158160        return None  # TODO throw exception? 
    159      
     161 
    160162    if not funcTag.startswith(u"global/"): 
    161163        return None  # TODO throw exception? 
     
    401403        self.wikiPageDict = WeakValueDictionary() 
    402404        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) 
    406411 
    407412        self.wikiName = wikiName 
     
    470475            raise writeException 
    471476             
    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 
    475493 
    476494    def incRefCount(self): 
     
    503521 
    504522        if self.refCount <= 0: 
    505             self.endUpdateThread()  # TODO Inform user as this may take some time 
    506              
     523            self.updateExecutor.end(hardEnd=True)  # TODO Inform user as this may take some time 
     524 
    507525            # Invalidate all cached pages to prevent yet running threads from 
    508526            # using them 
     
    661679 
    662680 
    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() 
    688713 
    689714 
    690715    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 
    731724 
    732725    def isReadOnlyEffect(self): 
     
    793786        if word doesn't exist 
    794787        """ 
    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 
    800795 
    801796    def getWikiPageNoError(self, wikiWord): 
     
    807802        it wasn't garbage collected yet. 
    808803        """ 
    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 
    835831 
    836832 
     
    840836        page in cache if it isn't there yet. 
    841837        """ 
    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 
    848849             
    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 
    865862 
    866863 
     
    872869                (without syntax specific prefix). 
    873870        """ 
    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 
    877875 
    878876 
     
    882880        """ 
    883881        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): 
    899899        """ 
    900900        Rebuild  the wiki 
     
    903903            PersonalWikiFrame.GuiProgressHandler protocol 
    904904        """ 
     905        self.updateExecutor.end(hardEnd=True) 
    905906        self.getWikiData().refreshDefinedContentNames() 
    906907 
    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() 
    909913 
    910914        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 
    913917        # re-save all of the pages 
    914918        try: 
    915919            step = 1 
    916920 
    917             self.getWikiData().clearCacheTables() 
    918              
     921            if not onlyDirty: 
     922                self.getWikiData().clearCacheTables() 
     923 
    919924            # Step one: update properties. There may be properties which 
    920925            #   define how the rest has to be interpreted, therefore they 
     
    954959 
    955960                    wikiPage.refreshMainDbCacheFromPageAst(pageAst) 
     961                    self.getWikiData().setMetaDataProcessed(wikiWord, 1) 
    956962                except: 
    957963                    traceback.print_exc() 
     
    966972        finally: 
    967973            progresshandler.close() 
    968             self.pageUpdateThread.start() 
     974            self.updateExecutor.start() 
    969975 
    970976 
     
    10711077 
    10721078                wikiPage.replaceLiveText(text) 
    1073                 wikiPage.update(text) 
     1079#                 wikiPage.update(text) 
    10741080 
    10751081 
     
    10821088                    u"\n" + content[len(prevTitle):] 
    10831089            page.replaceLiveText(content) 
    1084             page.update(content) 
     1090#             page.update(content) 
    10851091 
    10861092 
     
    11951201        text. 
    11961202        """ 
    1197         pg = self.getFuncPage("global/[CCBlacklist]") 
     1203        pg = self.getFuncPage("global/CCBlacklist") 
    11981204        bls = set(pg.getLiveText().split("\n")) 
    1199         pg = self.getFuncPage("wiki/[CCBlacklist]") 
     1205        pg = self.getFuncPage("wiki/CCBlacklist") 
    12001206        bls.update(pg.getLiveText().split("\n")) 
    12011207        self.ccWordBlacklist = bls 
  • branches/mbutscher/next/lib/pwiki/wikidata/compact_sqlite/WikiData.py

    r167 r173  
    625625 
    626626 
    627     def addRelationship(self, word, rel): 
     627    def _addRelationship(self, word, rel): 
    628628        """ 
    629629        Add a relationship from word to rel. rel is a tuple (toWord, pos). 
     
    631631        """ 
    632632        try: 
    633 #             self.connWrap.execSql( 
    634 #                     "insert or replace into wikirelations(word, relation) " 
    635 #                     "values (?, ?)", (word, toWord)) 
    636633            self.connWrap.execSql( 
    637634                    "insert or replace into wikirelations(word, relation, firstcharpos) " 
     
    644641        self.deleteChildRelationships(word) 
    645642        for r in childRelations: 
    646             self.addRelationship(word, r) 
     643            self._addRelationship(word, r) 
    647644 
    648645    def deleteChildRelationships(self, fromWord): 
     
    11401137            raise DbReadAccessError(e) 
    11411138 
    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) 
    11511148 
    11521149 
     
    11641161 
    11651162 
    1166     def setProperty(self, word, key, value): 
     1163    def _setProperty(self, word, key, value): 
    11671164        try: 
    11681165            self.connWrap.execSql("insert into wikiwordprops(word, key, value) " 
     
    11781175            values = props[k] 
    11791176            for v in values: 
    1180                 self.setProperty(word, k, v) 
     1177                self._setProperty(word, k, v) 
    11811178                if k == "alias": 
    11821179                    self.setAsAlias(v)  # TODO 
     
    12241221            return alias 
    12251222 
    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 
    12271227        if len(aliases) > 0: 
    12281228            return aliases[0] 
     
    12761276        self.deleteTodos(word) 
    12771277        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): 
    12821282        try: 
    12831283            self.connWrap.execSql("insert into todos(word, todo) values (?, ?)", (word, todo)) 
  • branches/mbutscher/next/lib/pwiki/wikidata/original_gadfly/DbStructure.py

    r169 r173  
    66 
    77 
    8 import string, codecs, types    , traceback 
     8import string, codecs, types, traceback 
    99 
    1010from os import mkdir, unlink, rename 
     
    1414from pwiki.WikiExceptions import * 
    1515from pwiki.StringOps import mbcsDec, mbcsEnc, utf8Enc, utf8Dec, \ 
    16         removeBracketsFilename, pathEnc 
     16        removeBracketsFilename, pathEnc, getFileSignatureBlock, \ 
     17        iterCompatibleFilename 
    1718from pwiki.SearchAndReplace import SearchReplaceOperation 
    1819 
    1920import gadfly 
    20  
    21 # from SqliteThin3 import * 
    2221 
    2322 
     
    3534        return ob 
    3635 
     36def _dummy(ob): 
     37    return ob 
    3738 
    3839 
     
    4344        self.__dict__["dbConn"] = connection 
    4445        self.__dict__["dbCursor"] = connection.cursor() 
    45          
    46 #         # To make access a bit faster 
    47 #         self.__dict__["execSql"] = self.dbCursor.execute 
    4846         
    4947        self.__dict__["execute"] = self.dbCursor.execute 
     
    5351        self.__dict__["fetchone"] = self.dbCursor.fetchone 
    5452        self.__dict__["fetchall"] = self.dbCursor.fetchall 
    55          
     53 
     54        self.__dict__["_defaultValues"] = DEFAULT_VALS 
     55 
    5656         
    5757    def __setattr__(self, attr, value): 
     
    6060    def __getattr__(self,  attr): 
    6161        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 
    6298 
    6399 
     
    75111        utility method, executes the sql, returns query result 
    76112        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 
    79117        """ 
    80118        ## print "execSqlQuery sql", sql, repr(params) 
     
    86124        result = self.dbCursor.fetchall() 
    87125 
    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] 
    90133 
    91134        return result 
     
    154197        """ 
    155198        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 
    158209        fieldStr = ", ".join(fields) 
    159210        qmStr = ", ".join(["?"] * len(fields)) 
     
    198249 
    199250 
    200 VERSION_DB = 3 
    201 VERSION_WRITECOMPAT = 3 
    202 VERSION_READCOMPAT = 2 
     251VERSION_DB = 4 
     252VERSION_WRITECOMPAT = 4 
     253VERSION_READCOMPAT = 4 
    203254 
    204255 
     
    223274        ("created", t.t), 
    224275        ("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 
    231287        ("word", t.t), 
    232288        ("relation", t.t), 
     
    235291     
    236292     
    237     "wikiwordprops": (     # Cache 
     293    "wikiwordprops": (     # Cache, but main 
    238294        ("word", t.t), 
    239295        ("key", t.t), 
     
    242298     
    243299     
    244     "todos": (     # Cache 
     300    "todos": (     # Cache, but main 
    245301        ("word", t.t), 
    246302        ("todo", t.t) 
     
    248304         
    249305     
    250     "search_views": (     # Essential 
     306    "search_views": (     # Deleted since 2.0alpha1. For updating format only  
    251307        ("title", t.t), 
    252308        ("datablock", t.t) 
     
    257313        ("key", t.t), 
    258314        ("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) 
    259345        ) 
    260  
    261  
    262     # For 2.0 versions: 
    263 #     "defaultvals": (   # Default values for new database fields 
    264 #         ("table", t.t), 
    265 #         ("field", t.t), 
    266 #         ("value", t.t) 
    267 #         ) 
    268          
    269          
    270346    } 
    271347 
    272348 
     349# Recycling t for setting of default values 
     350 
     351t.r = 0.0 
     352t.i = 0 
     353t.imo = -1 
     354t.t = u"" 
     355t.b = "" 
     356 
     357 
     358DEFAULT_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 
    273421del t 
     422 
    274423 
    275424 
     
    279428    "wikiwordprops", 
    280429    "todos", 
    281     "search_views", 
    282     "settings" 
     430#     "search_views", 
     431    "settings", 
     432    "wikiwordmatchterms", 
     433    "datablocks", 
     434    "datablocksexternal" 
    283435    ) 
    284436 
     
    300452    connwrap.execSqlNoError("create unique index wikirelations_pkey on wikirelations(word, relation)") 
    301453    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)") 
    303455    connwrap.execSqlNoError("create index wikirelations_word on wikirelations(word)") 
    304456    connwrap.execSqlNoError("create index wikiwordprops_word on wikiwordprops(word)") 
     
    312464    associated indices 
    313465    """ 
    314     CACHE_TABLES = ("wikirelations", "wikiwordprops", "todos") 
     466    CACHE_TABLES = ("wikirelations", "wikiwordprops", "todos", 
     467            "wikiwordmatchterms") 
    315468     
    316469    for tn in CACHE_TABLES: 
     
    348501    forcechange -- Create a new table in any case 
    349502    """ 
    350      
     503 
    351504#     print "changeTableSchema1", repr(tablename), repr(schema) 
    352      
     505 
    353506    # Build the sql command to create the table with new schema (needed later) 
    354      
     507 
    355508    newtabletyped = ", ".join(map(lambda sc: "%s %s" % sc[:2], schema)) 
    356509    newtableselect = ", ".join(map(lambda sc: sc[0], schema)) 
     
    359512    newtablecreate += newtabletyped 
    360513    newtablecreate += ")" 
    361      
     514 
    362515 
    363516    # Test if table already exists 
    364      
     517 
    365518    tn = connwrap.execSqlQuerySingleItem( 
    366519            "select table_name from __table_names__ where table_name='%s'" % 
     
    369522    if tn is None: 
    370523        # Does not exist, so simply create 
    371         connwrap.commit()         
     524        connwrap.commit() 
     525        print "--changeTableSchema13", repr(newtablecreate) 
    372526        connwrap.execSql(newtablecreate) 
    373527        connwrap.commit()         
     
    388542        if n in oldcolumns: 
    389543            intersect.append(n) 
    390              
     544 
    391545    recreate = False 
    392      
     546 
    393547    if forcechange: 
    394548        recreate = True 
     
    413567                df = sc[2] 
    414568            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 
    417575            if df is None: 
    418576                if t.lower() == "varchar": 
    419577                    df = "''" 
    420                      
     578 
    421579            newinsertcols.append(n) 
    422580            newinsertvalues.append(df) 
     
    437595        connwrap.execSql("create table tmptable(%s)" % newtabletyped) 
    438596        data = connwrap.execSqlQuery("select %s from %s" % 
    439                 (intersectselect, tablename)) 
    440                  
     597                (intersectselect, tablename), strConv=False) 
     598 
    441599        for row in data: 
    442600            connwrap.execSql("insert into tmptable (%s) values (%s)" % 
     
    459617 
    460618 
     619    # TODO: Does this something? 
     620 
    461621    # Table exists, so retrieve list of columns 
    462622    oldcolumns = connwrap.execSqlQuerySingleColumn( 
     
    486646        connection.startup("wikidb", dataDir) 
    487647        connwrap = ConnectWrap(connection) 
    488          
     648 
    489649        try: 
    490650            for tn in MAIN_TABLES: 
     
    595755 
    596756 
    597 def updateDatabase(connwrap, dataDir): 
     757 
     758def updateDatabase(connwrap, dataDir, pagefileSuffix): 
    598759    """ 
    599760    Update a database from an older version to current (checkDatabaseFormat() 
     
    783944 
    784945        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 
    7961036 
    7971037 
     
    8131053def updateDatabase2(connwrap): 
    8141054    """ 
    815     Second update function. Called when database version is current. 
     1055    Second update function. Called even when database version is current. 
    8161056    Performs further updates 
    8171057    """ 
     
    9191159create the "wordnormcase" column content. A "-" means the column contains 
    9201160invalid 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 
    9211237""" 
  • branches/mbutscher/next/lib/pwiki/wikidata/original_gadfly/WikiData.py

    r169 r173  
    1616from os import mkdir, unlink, rename, stat    # listdir 
    1717from os.path import exists, join, basename 
     18import os.path 
     19 
    1820from time import time, localtime 
    1921import datetime 
     
    3436 
    3537 
     38import Consts 
    3639from pwiki.WikiExceptions import *   # TODO make normal import? 
    3740from pwiki import SearchAndReplace 
    3841 
    3942from 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 
    4147 
    4248 
     
    8389        if formatcheck == 1: 
    8490            try: 
    85                 DbStructure.updateDatabase(self.connWrap, self.dataDir) 
     91                DbStructure.updateDatabase(self.connWrap, self.dataDir, 
     92                        self.pagefileSuffix) 
    8693            except: 
    8794                self.connWrap.rollback() 
     
    136143    def getContent(self, word): 
    137144        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) 
    141148 
    142149            content = loadEntireTxtFile(self.getWikiWordFileName(word)) 
    143 #             fp = open(self.getWikiWordFileName(word), "rU") 
    144 #             try: 
    145 #                 content = fp.read() 
    146 #             finally: 
    147 #                 fp.close() 
    148150 
    149151            return fileContentToUnicode(content) 
     
    151153            traceback.print_exc() 
    152154            raise DbReadAccessError(e) 
     155 
    153156 
    154157        # TODO Remove method 
     
    174177                if creadate is None: 
    175178                    creadate = ti 
    176                      
     179 
     180                fileName = self.createWikiWordFileName(word) 
    177181                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())) 
    184186            else: 
    185187                self.execSql("update wikiwords set modified = ? where word = ?", 
    186188                        (moddate, word)) 
    187              
     189 
    188190            self.commitNeeded = True 
    189191        except (IOError, OSError, ValueError), e: 
    190192            traceback.print_exc() 
    191193            raise DbWriteAccessError(e) 
    192                      
     194 
    193195        self._getCachedContentNames()[word] = 1 
    194196 
     
    204206        """ 
    205207        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 
    214217        except (IOError, OSError, ValueError), e: 
    215218            traceback.print_exc() 
    216219            raise DbWriteAccessError(e) 
    217          
    218         self._updatePageEntry(word, moddate, creadate) 
    219220 
    220221 
     
    226227        """ 
    227228        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)) 
    230243            self.commitNeeded = True 
    231      
    232             rename(self.getWikiWordFileName(oldWord), 
    233                     self.getWikiWordFileName(newWord)) 
     244 
    234245            del self._getCachedContentNames()[oldWord] 
    235246            self._getCachedContentNames()[newWord] = 1 
     
    242253    def deleteContent(self, word): 
    243254        try: 
     255            fileName = self.getWikiWordFileName(word) 
    244256            self.execSql("delete from wikiwords where word = ?", (word,)) 
    245257            self.commitNeeded = True 
    246             if exists(self.getWikiWordFileName(word)): 
    247                 unlink(self.getWikiWordFileName(word)) 
     258            if exists(fileName): 
     259                unlink(fileName) 
    248260            del self._getCachedContentNames()[word] 
    249261        except (IOError, OSError, ValueError), e: 
     
    327339            elif field == "visited":