source: branches/mbutscher/work/lib/pwiki/SearchAndReplaceDialogs.py @ 452

Last change on this file since 452 was 452, checked in by mbutscher, 4 years ago

branches/mbutscher/work:

  • Bug fixed: For some wxPython versions radio button grouping on tab "Set page list" of wiki-wide search failed
File size: 93.9 KB
Line 
1# import profilehooks
2# profile = profilehooks.profile(filename="profile.prf", immediate=False)
3
4import sys, traceback, re, time
5
6import wx, wx.html, wx.xrc
7
8from rtlibRepl import minidom
9
10import Consts
11from .MiscEvent import MiscEventSourceMixin, KeyFunctionSink
12from WikiExceptions import *
13from .Utilities import DUMBTHREADSTOP, callInMainThread, callInMainThreadAsync, \
14        ThreadHolder, FunctionThreadStop
15
16from .SystemInfo import isLinux
17
18from . import wxHelper
19from wxHelper import *
20
21from .StringOps import uniToGui, guiToUni, escapeHtml
22
23from .WikiPyparsing import ParseException
24
25from .WindowLayout import setWindowPos, setWindowSize, \
26        getRelativePositionTupleToAncestor, LayeredControlPanel
27
28from .Configuration import MIDDLE_MOUSE_CONFIG_TO_TABMODE
29
30from .SearchAndReplace import SearchReplaceOperation, ListWikiPagesOperation, \
31        stripSearchString
32
33
34
35
36class _SearchResultItemInfo(object):
37    __slots__ = ("__weakref__", "wikiWord", "occCount", "occNumber", "occHtml",
38            "occPos", "html", "maxCountOccurrences")
39
40    def __init__(self, wikiWord, occPos = (-1, -1), occCount = -1,
41            maxOccCount=100):
42        self.wikiWord = wikiWord
43        if occPos[0] != -1:
44            self.occNumber = 1
45        else:
46            self.occNumber = -1  # -1: No specific occurrence
47
48        self.occHtml = u""  # HTML presentation of the occurrence
49        self.occPos = occPos  # Tuple (start, end) with position of occurrence in characters
50        self.occCount = occCount # -1: Undefined; -2: More than maxCountOccurrences
51        self.maxCountOccurrences = maxOccCount
52        self.html = None
53
54
55    def buildOccurrence(self, text, before, after, pos, occNumber, maxOccCount):
56        self.html = None
57        basum = before + after
58        self.occNumber = -1
59        self.occPos = pos
60        self.maxCountOccurrences = maxOccCount
61
62        if basum == 0:
63            # No context
64            self.occHtml = u""
65            return self
66       
67        if pos[0] is None:
68            # All occurences where deleted meanwhile dialog was open
69            self.occHtml = u""
70            self.occNumber = 0
71            self.occCount = 0
72            return self
73       
74        if pos[0] == -1:
75            # No position -> use beginning of text
76            self.occHtml = escapeHtml(text[0:basum])
77            return self
78       
79        s = max(0, pos[0] - before)
80        e = min(len(text), pos[1] + after)
81        self.occHtml = u"".join([escapeHtml(text[s:pos[0]]),
82            "<b>", escapeHtml(text[pos[0]:pos[1]]), "</b>",
83            escapeHtml(text[pos[1]:e])])
84           
85        self.occNumber = occNumber
86        return self
87
88
89    def setHtmlDirectly(self, occHtml):
90        self.occNumber = -1
91        self.occCount = -1
92        self.occHtml = occHtml
93
94
95
96    def getHtml(self):
97        if self.html is None:
98            result = [u'<table><tr><td bgcolor="#0000ff" width="6"></td>'
99                    u'<td><font color="BLUE"><b>%s</b></font>' % \
100                    escapeHtml(self.wikiWord)]
101           
102            if self.occNumber != -1:
103                stroc = [unicode(self.occNumber), u"/"]
104            else:
105                stroc = []
106               
107            if self.occCount > -1:
108                stroc.append(unicode(self.occCount))
109            elif len(stroc) > 0:
110                if self.occCount == -1:
111                    stroc.append(u"?")
112                elif self.occCount == -2:
113                    stroc.append(u">%s" % self.maxCountOccurrences)
114
115            stroc = u"".join(stroc)
116           
117            if stroc != u"":
118                result.append(u' <b>(%s)</b>' % stroc)
119               
120            if self.occHtml != u"":
121                result.append(u'<br>\n')
122                result.append(self.occHtml)
123               
124            result.append('</td></tr></table>')
125            self.html = u"".join(result)
126           
127        return self.html
128
129
130
131class SearchResultListBox(wx.HtmlListBox, MiscEventSourceMixin):
132    def __init__(self, parent, pWiki, ID):
133        wx.HtmlListBox.__init__(self, parent, ID, style = wx.SUNKEN_BORDER)
134
135        self.pWiki = pWiki
136        self.searchWikiDialog = parent
137        self.found = []
138        self.foundinfo = []
139        self.searchOp = None # last search operation set by showFound
140        self.SetItemCount(0)
141        self.isShowingSearching = False  # Show a visual feedback only while searching
142        self.contextMenuSelection = -2
143
144        wx.EVT_LEFT_DOWN(self, self.OnLeftDown)
145        wx.EVT_LEFT_DCLICK(self, self.OnLeftDown)
146        wx.EVT_MIDDLE_DOWN(self, self.OnMiddleButtonDown)
147        wx.EVT_KEY_DOWN(self, self.OnKeyDown)
148        wx.EVT_LISTBOX_DCLICK(self, ID, self.OnDClick)
149        wx.EVT_CONTEXT_MENU(self, self.OnContextMenu)
150
151
152        wx.EVT_MENU(self, GUI_ID.CMD_ACTIVATE_THIS, self.OnActivateThis)
153        wx.EVT_MENU(self, GUI_ID.CMD_ACTIVATE_NEW_TAB_THIS,
154                self.OnActivateNewTabThis)
155        wx.EVT_MENU(self, GUI_ID.CMD_ACTIVATE_NEW_TAB_BACKGROUND_THIS,
156                self.OnActivateNewTabBackgroundThis)
157
158
159    def OnGetItem(self, i):
160        if self.isShowingSearching:
161            return u"<b>" + _(u"Searching... (click into field to abort)") + u"</b>"
162        elif self.GetCount() == 0:
163            return u"<b>" + _(u"Not found") + u"</b>"
164
165        try:
166            return self.foundinfo[i].getHtml()
167        except IndexError:
168            return u""
169
170    def showSearching(self):
171        """
172        Shows a "Searching..." as visual feedback while search runs
173        """
174        self.isShowingSearching = True
175        self.SetItemCount(1)
176        self.Refresh()
177        self.Update()
178       
179    def ensureNotShowSearching(self):
180        """
181        This function is called after a search operation and a call to
182        showFound may have happened. If it did not happen,
183        the list is cleared.
184        """
185        if self.isShowingSearching:
186            # This can only happen if showFound wasn't called
187            self.showFound(None, None, None)
188
189
190    def _displayFound(self, itemCount, threadstop):
191        """
192        Called by showFound(), must be called in main thread.
193        """
194        if threadstop.isValidThread():
195            self.SetItemCount(itemCount)
196            self.Refresh()
197
198
199    def showFound(self, sarOp, found, wikiDocument,
200            threadstop=DUMBTHREADSTOP):
201        """
202        Shows the results of search operation sarOp
203        found -- list of matching wiki words
204        wikiDocument -- WikiDocument(=WikiDataManager) object
205        """
206        if found is None or len(found) == 0:
207            self.found = []
208            self.foundinfo = []
209            self.searchOp = None
210            self.isShowingSearching = False
211            callInMainThreadAsync(self._displayFound, 1, threadstop)   # For the "Not found" entry
212        else:
213            try:
214                # Store and prepare clone of search operation
215                self.searchOp = sarOp.clone()
216                self.searchOp.replaceOp = False
217                self.searchOp.cycleToStart = True
218   
219                self.found = found
220                self.foundinfo = []
221                # Load context settings
222                before = self.pWiki.configuration.getint("main",
223                        "search_wiki_context_before")
224                after = self.pWiki.configuration.getint("main",
225                        "search_wiki_context_after")
226                       
227                countOccurrences = self.pWiki.getConfig().getboolean("main",
228                        "search_wiki_count_occurrences")
229                maxCountOccurrences = self.pWiki.getConfig().getint("main",
230                        "search_wiki_max_count_occurrences", 100)
231
232                context = before + after
233
234                if sarOp.hasParticularTextPosition():
235                    if context == 0 and not countOccurrences:
236                        # No context, no occurrence counting
237                        # -> just a list of found pages
238                        self.foundinfo = [_SearchResultItemInfo(w) for w in found]
239                    else:
240                        # "As is" or regex search
241                        sarOp.beginWikiSearch(self.pWiki.getWikiDocument())
242                        try:
243                            for w in found:
244                                threadstop.testValidThread()
245                                docPage = wikiDocument.getWikiPageNoError(w)
246                                text = docPage.getLiveTextNoTemplate()
247                                if text is None:
248                                    continue
249   
250    #                             pos = sarOp.searchText(text)
251                                pos = sarOp.searchDocPageAndText(docPage, text)
252                                if pos[0] is None:
253                                    # This can happen e.g. for boolean searches like
254                                    # 'foo or not bar' on a page which has neither 'foo'
255                                    # nor 'bar'.
256                                   
257                                    # Similar as if no particular text position available
258                                    if context == 0:
259                                        self.foundinfo.append(
260                                                _SearchResultItemInfo(w))
261                                    else:
262                                        self.foundinfo.append(
263                                                _SearchResultItemInfo(w).buildOccurrence(
264                                                text, before, after, (-1, -1), -1,
265                                                100))
266                                    continue
267                                firstpos = pos
268                               
269                                info = _SearchResultItemInfo(w, occPos=pos,
270                                        maxOccCount=maxCountOccurrences)
271   
272                                if countOccurrences:
273                                    occ = 1
274                                    while True:
275                                        pos = sarOp.searchDocPageAndText(
276                                                docPage, text, pos[1])
277                                        if pos[0] is None or pos[0] == pos[1]:
278                                            break
279                                        occ += 1
280                                        if occ > maxCountOccurrences:
281                                            occ = -2
282                                            break
283   
284                                    info.occCount = occ
285   
286                                self.foundinfo.append(info.buildOccurrence(
287                                        text, before, after, firstpos, 1,
288                                        maxCountOccurrences))
289                        finally:
290                            sarOp.endWikiSearch()
291                elif sarOp.hasWhooshHighlighting():
292                    # Index search
293                    if context == 0:
294                        # No context, occurrence counting doesn't matter
295                        # -> just a list of found pages
296                        self.foundinfo = [_SearchResultItemInfo(w) for w in found]
297                    else:
298                        sarOp.beginWikiSearch(self.pWiki.getWikiDocument())
299                        try:
300                            for w in found:
301                                threadstop.testValidThread()
302                                docPage = wikiDocument.getWikiPageNoError(w)
303                                text = docPage.getLiveTextNoTemplate()
304                                if text is None:
305                                    continue
306   
307                                html, firstPos = sarOp.highlightWhooshIndexFound(
308                                        text, docPage, before, after)
309                               
310                                info = _SearchResultItemInfo(w, occPos=(firstPos, firstPos))
311                                info.setHtmlDirectly(html)
312   
313                                self.foundinfo.append(info)
314                        finally:
315                            sarOp.endWikiSearch()
316                else:  # not sarOp.hasParticularTextPosition():
317                    # No specific position to show as context, so show beginning of page
318                    # Also, no occurrence counting possible
319                    if context == 0:
320                        self.foundinfo = [_SearchResultItemInfo(w) for w in found]
321                    else:
322                        for w in found:
323                            text = wikiDocument.getWikiPageNoError(w).\
324                                    getLiveTextNoTemplate()
325                            if text is None:
326                                continue
327                            self.foundinfo.append(
328                                    _SearchResultItemInfo(w).buildOccurrence(
329                                    text, before, after, (-1, -1), -1, 100))
330                    threadstop.testValidThread()
331               
332                threadstop.testValidThread()
333                self.isShowingSearching = False
334#                 callInMainThreadAsync(self.SetItemCount, len(self.foundinfo))
335                callInMainThreadAsync(self._displayFound, len(self.foundinfo),
336                        threadstop)
337
338            except NotCurrentThreadException:
339                self.found = []
340                self.foundinfo = []
341                self.isShowingSearching = False
342                # For the "Not found" entry
343                callInMainThreadAsync(self._displayFound, 1, threadstop)
344                raise
345
346
347    def GetSelectedWord(self):
348        sel = self.GetSelection()
349        if sel == -1 or self.GetCount() == 0:
350            return None
351        else:
352            return self.foundinfo[sel].wikiWord
353           
354    def GetCount(self):
355        return len(self.found)
356
357    def IsEmpty(self):
358        return self.GetCount() == 0
359
360
361    def _pageListFindNext(self):
362        """
363        After pressing F3 or clicking blue bar of an entry, position of
364        next found element should be shown
365        """
366        sel = self.GetSelection()
367        if sel == -1:
368            return
369       
370        info = self.foundinfo[sel]
371        if info.occPos[0] == -1 or info.occPos[1] is None:
372            return
373        if info.occNumber == -1:
374            return
375
376        before = self.pWiki.configuration.getint("main",
377                "search_wiki_context_before")
378        after = self.pWiki.configuration.getint("main",
379                "search_wiki_context_after")
380
381        maxCountOccurrences = self.pWiki.getConfig().getint("main",
382                "search_wiki_max_count_occurrences", 100)
383
384        wikiDocument = self.pWiki.getWikiDocument()
385        docPage = wikiDocument.getWikiPageNoError(info.wikiWord)
386        text = docPage.getLiveTextNoTemplate()
387        if text is not None:
388            self.searchOp.beginWikiSearch(self.pWiki.getWikiDocument())
389            try:
390#                 pos = self.searchOp.searchText(text, info.occPos[1])
391                pos = self.searchOp.searchDocPageAndText(docPage, text,
392                        info.occPos[1])
393            finally:
394                self.searchOp.endWikiSearch()
395        else:
396            pos = (-1, -1)
397
398        if pos[0] == -1:
399            # Page was changed after last search and doesn't contain any occurrence anymore
400            info.occCount = 0
401            info.buildOccurrence(text, 0, 0, pos, -1, maxCountOccurrences)
402        elif pos[0] < info.occPos[1]:
403            # Search went back to beginning, number of last occ. is also occ.count
404            info.occCount = info.occNumber
405            info.buildOccurrence(text, before, after, pos, 1,
406                    maxCountOccurrences)
407        elif info.occPos[0] == info.occPos[1]:    # pos[0] == info.occPos[1]:
408            # Match is empty
409            info.occCount = info.occNumber
410            info.buildOccurrence(text, before, after, pos, 1,
411                    maxCountOccurrences)           
412        else:
413            info.buildOccurrence(text, before, after, pos, info.occNumber + 1,
414                    maxCountOccurrences)
415
416        # TODO nicer refresh
417        self.SetSelection(-1)
418        self.SetSelection(sel)
419        self.Refresh()
420
421
422    def OnDClick(self, evt):
423        sel = self.GetSelection()
424        if sel == -1 or self.GetCount() == 0:
425            return
426
427        info = self.foundinfo[sel]
428
429        self.pWiki.openWikiPage(info.wikiWord)
430
431        editor = self.pWiki.getActiveEditor()
432        if editor is not None:
433            if info.occPos[0] != -1:
434                self.pWiki.getActiveEditor().showSelectionByCharPos(info.occPos[0],
435                        info.occPos[1])
436#                 self.pWiki.getActiveEditor().ensureSelectionExpanded()
437
438            # Works in fast search popup only if called twice
439            editor.SetFocus()
440            editor.SetFocus()
441            # Sometimes not even then
442            self.fireMiscEventKeys(("opened in foreground",))
443
444
445    def OnLeftDown(self, evt):
446        if self.isShowingSearching:
447            self.searchWikiDialog.stopSearching()
448
449        if self.GetCount() == 0:
450            return  # no evt.Skip()?
451
452        pos = evt.GetPosition()
453        hitsel = self.HitTest(pos)
454       
455        if hitsel == wx.NOT_FOUND:
456            evt.Skip()
457            return
458       
459        if pos.x < (5 + 6):
460            # Click inside the blue bar
461            self.SetSelection(hitsel)
462            self._pageListFindNext()
463            return
464       
465        evt.Skip()
466
467
468    def OnMiddleButtonDown(self, evt):
469        if self.GetCount() == 0:
470            return  # no evt.Skip()?
471
472        pos = evt.GetPosition()
473        if pos == wx.DefaultPosition:
474            hitsel = self.GetSelection()
475
476        hitsel = self.HitTest(pos)
477
478        if hitsel == wx.NOT_FOUND:
479            evt.Skip()
480            return
481
482        if pos.x < (5 + 6):
483            # Click inside the blue bar
484            self.SetSelection(hitsel)
485            self._pageListFindNext()
486            return
487       
488        info = self.foundinfo[hitsel]
489
490        if evt.ControlDown():
491            configCode = self.pWiki.getConfig().getint("main",
492                    "mouse_middleButton_withCtrl")
493        else:
494            configCode = self.pWiki.getConfig().getint("main",
495                    "mouse_middleButton_withoutCtrl")
496                   
497        tabMode = MIDDLE_MOUSE_CONFIG_TO_TABMODE[configCode]
498
499        presenter = self.pWiki.activatePageByUnifiedName(
500                u"wikipage/" + info.wikiWord, tabMode)
501       
502        if presenter is None:
503            return
504
505        if info.occPos[0] != -1:
506            presenter.getSubControl("textedit").showSelectionByCharPos(
507                    info.occPos[0], info.occPos[1])
508
509        if configCode != 1:
510            # If not new tab opened in background -> focus editor
511
512            # Works in fast search popup only if called twice
513            self.pWiki.getActiveEditor().SetFocus()
514            self.pWiki.getActiveEditor().SetFocus()
515           
516            self.fireMiscEventKeys(("opened in foreground",))
517
518       
519    def OnKeyDown(self, evt):
520        if self.GetCount() == 0:
521            return  # no evt.Skip()?
522
523        accP = getAccelPairFromKeyDown(evt)
524        matchesAccelPair = self.pWiki.keyBindings.matchesAccelPair
525       
526        if matchesAccelPair("ContinueSearch", accP):
527            # ContinueSearch is normally F3
528            self._pageListFindNext()
529        elif accP == (wx.ACCEL_NORMAL, wx.WXK_RETURN) or \
530                accP == (wx.ACCEL_NORMAL, wx.WXK_NUMPAD_ENTER):
531            self.OnDClick(evt)
532        else:
533            evt.Skip()
534
535
536    def OnContextMenu(self, evt):
537        if self.GetCount() == 0:
538            return  # no evt.Skip()?
539
540        pos = evt.GetPosition()
541        if pos == wx.DefaultPosition:
542            hitsel = self.GetSelection()
543        else:
544            hitsel = self.HitTest(self.ScreenToClient(pos))
545
546        if hitsel == wx.NOT_FOUND:
547            evt.Skip()
548            return
549
550        self.contextMenuSelection = hitsel
551        try:
552            menu = wx.Menu()
553            appendToMenuByMenuDesc(menu, _CONTEXT_MENU_ACTIVATE)
554            self.PopupMenu(menu)
555            menu.Destroy()
556        finally:
557            self.contextMenuSelection = -2
558
559
560
561    def OnActivateThis(self, evt):
562        if self.contextMenuSelection > -1:
563            info = self.foundinfo[self.contextMenuSelection]
564
565#             presenter = self.pWiki.activateWikiWord(info.wikiWord, 0)
566            presenter = self.pWiki.activatePageByUnifiedName(
567                    u"wikipage/" + info.wikiWord, 0)
568
569            if presenter is None:
570                return
571
572            if info.occPos[0] != -1:
573                presenter.getSubControl("textedit").showSelectionByCharPos(
574                        info.occPos[0], info.occPos[1])
575   
576            # Works in fast search popup only if called twice
577            self.pWiki.getActiveEditor().SetFocus()
578            self.pWiki.getActiveEditor().SetFocus()
579           
580            # Context menu is open yet -> send later
581            wx.CallAfter(self.fireMiscEventKeys, ("opened in foreground",))
582
583
584    def OnActivateNewTabThis(self, evt):
585        if self.contextMenuSelection > -1:
586            info = self.foundinfo[self.contextMenuSelection]
587
588#             presenter = self.pWiki.activateWikiWord(info.wikiWord, 2)
589            presenter = self.pWiki.activatePageByUnifiedName(
590                    u"wikipage/" + info.wikiWord, 2)
591
592            if presenter is None:
593                return
594
595            if info.occPos[0] != -1:
596                presenter.getSubControl("textedit").showSelectionByCharPos(
597                        info.occPos[0], info.occPos[1])
598   
599            # Works in fast search popup only if called twice
600            self.pWiki.getActiveEditor().SetFocus()
601            self.pWiki.getActiveEditor().SetFocus()
602           
603            # Context menu is open yet -> send later
604            wx.CallAfter(self.fireMiscEventKeys, ("opened in foreground",))
605
606
607    def OnActivateNewTabBackgroundThis(self, evt):
608        if self.contextMenuSelection > -1:
609            info = self.foundinfo[self.contextMenuSelection]
610
611#             presenter = self.pWiki.activateWikiWord(info.wikiWord, 3)
612            presenter = self.pWiki.activatePageByUnifiedName(
613                    u"wikipage/" + info.wikiWord, 3)
614           
615            if presenter is None:
616                return
617
618            if info.occPos[0] != -1:
619                presenter.getSubControl("textedit").showSelectionByCharPos(
620                        info.occPos[0], info.occPos[1])
621
622
623class SearchPageDialog(wx.Dialog):
624    def __init__(self, mainControl, ID, title="",
625                 pos=wx.DefaultPosition, size=wx.DefaultSize,
626                 style=wx.NO_3D|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
627        d = wx.PreDialog()
628        self.PostCreate(d)
629
630        self.mainControl = mainControl
631
632        res = wx.xrc.XmlResource.Get()
633        res.LoadOnDialog(self, self.mainControl, "SearchPageDialog")
634
635        self.ctrls = XrcControls(self)
636
637        self.ctrls.btnClose.SetId(wx.ID_CANCEL)
638       
639        self.showExtended = True
640        self.mainSizer = self.GetSizer()
641#         self.mainSizer.Detach(self.ctrls.lbSavedSearches)
642        self.OnToggleExtended(None)
643       
644        self.firstFind = True
645        self.savedSearches = None
646
647        self._refreshSavedSearchesList()
648        self._refreshSearchHistoryCombo()
649       
650
651        # Fixes focus bug under Linux
652        self.SetFocus()
653
654        wx.EVT_BUTTON(self, GUI_ID.btnFindNext, self.OnFindNext)       
655        wx.EVT_BUTTON(self, GUI_ID.btnReplace, self.OnReplace)
656        wx.EVT_BUTTON(self, GUI_ID.btnReplaceAll, self.OnReplaceAll)
657
658        wx.EVT_BUTTON(self, GUI_ID.btnToggleExtended, self.OnToggleExtended)
659        wx.EVT_BUTTON(self, GUI_ID.btnSaveSearch, self.OnSaveSearch)
660        wx.EVT_BUTTON(self, GUI_ID.btnDeleteSearches, self.OnDeleteSearches)
661        wx.EVT_BUTTON(self, GUI_ID.btnLoadSearch, self.OnLoadSearch)
662#         wx.EVT_BUTTON(self, GUI_ID.btnLoadAndRunSearch, self.OnLoadAndRunSearch)
663
664        wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnClose)
665        wx.EVT_COMBOBOX(self, GUI_ID.cbSearch, self.OnSearchComboSelected)
666        wx.EVT_LISTBOX_DCLICK(self, GUI_ID.lbSavedSearches, self.OnLoadSearch)
667        wx.EVT_CLOSE(self, self.OnClose)
668       
669
670    def OnClose(self, evt):
671        self.mainControl.nonModalFindDlg = None
672        self.Destroy()
673
674
675    def OnToggleExtended(self, evt):
676        self.showExtended = not self.showExtended
677        winMin = self.GetMinSize()
678        winCurr = self.GetSize()
679        oldSizerMin = self.mainSizer.CalcMin()
680
681#         print "--OnToggleExtended3", repr((self.showExtended, self.mainSizer.CalcMin()))
682       
683        if not self.showExtended:
684            self.ctrls.panelSavedSearchButtons.Show(False)
685            self.mainSizer.Detach(self.ctrls.panelSavedSearchButtons)
686            self.ctrls.btnToggleExtended.SetLabel(_(u"More >>"))
687        else:
688            self.ctrls.panelSavedSearchButtons.Show(True)
689            self.mainSizer.Add(self.ctrls.panelSavedSearchButtons, (1,0), (1,2),
690                    flag=wx.ALL | wx.EXPAND | wx.ALIGN_CENTRE_VERTICAL,
691                    border=5)
692#             self.mainSizer.AddGrowableRow(1)
693            self.ctrls.btnToggleExtended.SetLabel(_(u"<< Less"))
694
695#         print "--OnToggleExtended13", repr(self.mainSizer.CalcMin())
696        self.Layout()
697
698        newSizerMin = self.mainSizer.CalcMin()
699
700        self.SetMinSize((winMin.GetWidth() - oldSizerMin.GetWidth() +
701                newSizerMin.GetWidth(),
702                winMin.GetHeight() - oldSizerMin.GetHeight() +
703                newSizerMin.GetHeight()))
704       
705        self.SetSize((winCurr.GetWidth() - oldSizerMin.GetWidth() +
706                newSizerMin.GetWidth(),
707                winCurr.GetHeight() - oldSizerMin.GetHeight() +
708                newSizerMin.GetHeight()))
709
710
711    def _buildSearchReplaceOperation(self):
712        sarOp = SearchReplaceOperation()
713        sarOp.searchStr = stripSearchString(
714                guiToUni(self.ctrls.cbSearch.GetValue()))
715        sarOp.replaceStr = guiToUni(self.ctrls.txtReplace.GetValue())
716        sarOp.replaceOp = True
717        sarOp.booleanOp = False
718        sarOp.caseSensitive = self.ctrls.cbCaseSensitive.GetValue()
719        sarOp.wholeWord = self.ctrls.cbWholeWord.GetValue()
720        sarOp.cycleToStart = False
721       
722        if self.ctrls.cbRegEx.GetValue():
723            sarOp.wildCard = 'regex'
724        else:
725            sarOp.wildCard = 'no'
726
727        sarOp.wikiWide = False
728
729        return sarOp
730
731
732    def showSearchReplaceOperation(self, sarOp):
733        """
734        Load settings from search operation into controls
735        """
736        self.ctrls.cbSearch.SetValue(uniToGui(sarOp.searchStr))
737        self.ctrls.txtReplace.SetValue(uniToGui(sarOp.replaceStr))
738        self.ctrls.cbCaseSensitive.SetValue(sarOp.caseSensitive)
739        self.ctrls.cbWholeWord.SetValue(sarOp.wholeWord)
740        self.ctrls.cbRegEx.SetValue(sarOp.wildCard == 'regex')
741
742
743
744    def buildHistoryTuple(self):
745        """
746        Build a tuple for the search history from current settings
747        """
748        return (
749                guiToUni(self.ctrls.cbSearch.GetValue()),
750                guiToUni(self.ctrls.txtReplace.GetValue()),
751                bool(self.ctrls.cbCaseSensitive.GetValue()),
752                bool(self.ctrls.cbWholeWord.GetValue()),
753                bool(self.ctrls.cbRegEx.GetValue())
754                )
755
756
757    def showHistoryTuple(self, tpl):
758        """
759        Load settings from history tuple into controls
760        """
761        self.ctrls.cbSearch.SetValue(uniToGui(tpl[0]))
762        self.ctrls.txtReplace.SetValue(uniToGui(tpl[1]))
763        self.ctrls.cbCaseSensitive.SetValue(bool(tpl[2]))
764        self.ctrls.cbWholeWord.SetValue(bool(tpl[3]))
765        self.ctrls.cbRegEx.SetValue(bool(tpl[4]))
766
767
768    def addCurrentToHistory(self):
769        tpl = self.buildHistoryTuple()
770        hist = wx.GetApp().getPageSearchHistory()
771        try:
772            pos = hist.index(tpl)
773            del hist[pos]
774            hist.insert(0, tpl)
775        except ValueError:
776            # tpl not in hist
777            hist.insert(0, tpl)
778            if len(hist) > 10:
779                hist = hist[:10]
780           
781        wx.GetApp().setPageSearchHistory(hist)
782#         self.ctrls.cbSearch.Clear()
783#         self.ctrls.cbSearch.AppendItems([tpl[0] for tpl in hist])
784
785        self.ctrls.cbSearch.Freeze()
786        try:
787            text = self.ctrls.cbSearch.GetValue()
788            self._refreshSearchHistoryCombo()
789            self.ctrls.cbSearch.SetValue(text)
790        finally:
791            self.ctrls.cbSearch.Thaw()
792
793
794    def _refreshSavedSearchesList(self):
795        wikiData = self.mainControl.getWikiData()
796        unifNames = wikiData.getDataBlockUnifNamesStartingWith(u"savedpagesearch/")
797
798        result = []
799#         suppExTypes = PluginManager.getSupportedExportTypes(mainControl,
800#                     None, continuousExport)
801
802        for un in unifNames:
803            name = un[16:]
804            content = wikiData.retrieveDataBlock(un)
805            xmlDoc = minidom.parseString(content)
806            xmlNode = xmlDoc.firstChild
807#             etype = Serialization.serFromXmlUnicode(xmlNode, u"exportTypeName")
808#             if etype not in suppExTypes:
809#                 # Export type of saved export not supported
810#                 continue
811
812            result.append((name, xmlNode))
813   
814        self.mainControl.getCollator().sortByFirst(result)
815   
816        self.savedSearches = result
817
818        self.ctrls.lbSavedSearches.Clear()
819        for search in self.savedSearches:
820            self.ctrls.lbSavedSearches.Append(uniToGui(search[0]))
821
822
823
824    def _refreshSearchHistoryCombo(self):
825        hist = wx.GetApp().getPageSearchHistory()
826        self.ctrls.cbSearch.Clear()
827        self.ctrls.cbSearch.AppendItems([tpl[0] for tpl in hist])
828
829
830    def OnDeleteSearches(self, evt):
831        sels = self.ctrls.lbSavedSearches.GetSelections()
832       
833        if len(sels) == 0:
834            return
835           
836        answer = wx.MessageBox(
837                _(u"Do you want to delete %i search(es)?") % len(sels),
838                _(u"Delete search"),
839                wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION, self)
840        if answer != wx.YES:
841            return
842
843        for s in sels:
844#             self.mainControl.getWikiData().deleteSavedSearch(self.savedSearches[s])
845            self.mainControl.getWikiData().deleteDataBlock(
846                    u"savedpagesearch/" + self.savedSearches[s][0])
847        self._refreshSavedSearchesList()
848
849
850    def OnLoadSearch(self, evt):
851        self._loadSearch()
852       
853#     def OnLoadAndRunSearch(self, evt):
854#         if self._loadSearch():
855#             try:
856#                 self._refreshPageList()
857#             except UserAbortException:
858#                 return
859#             except re.error, e:
860#                 self.displayErrorMessage(_(u'Error in regular expression'),
861#                         _(unicode(e)))
862#             except ParseException, e:
863#                 self.displayErrorMessage(_(u'Error in boolean expression'),
864#                         _(unicode(e)))
865#             except DbReadAccessError, e:
866#                 self.displayErrorMessage(_(u'Error. Maybe wiki rebuild is needed'),
867#                         _(unicode(e)))
868
869
870    def _loadSearch(self):
871        sels = self.ctrls.lbSavedSearches.GetSelections()
872       
873        if len(sels) != 1:
874            return False
875       
876        xmlNode = self.savedSearches[sels[0]][1]
877
878        sarOp = SearchReplaceOperation()
879        sarOp.serializeFromXml(xmlNode)
880        self.showSearchReplaceOperation(sarOp)
881       
882        return True
883
884
885    # TODO Store search mode
886    def OnSaveSearch(self, evt):
887        sarOp = self._buildSearchReplaceOperation()
888        try:
889            sarOp.rebuildSearchOpTree()
890        except re.error, e:
891            self.mainControl.displayErrorMessage(_(u'Error in regular expression'),
892                    _(unicode(e)))
893            return
894        except ParseException, e:
895            self.mainControl.displayErrorMessage(_(u'Error in boolean expression'),
896                    _(unicode(e)))
897            return
898
899        if len(sarOp.searchStr) > 0:
900            title = sarOp.getTitle()
901            while True:
902                title = guiToUni(wx.GetTextFromUser(_(u"Title:"),
903                        _(u"Choose search title"), title, self))
904                if title == u"":
905                    return  # Cancel
906                   
907#                 if title in self.mainControl.getWikiData().getSavedSearchTitles():
908                if (u"savedpagesearch/" + title) in self.mainControl.getWikiData()\
909                        .getDataBlockUnifNamesStartingWith(
910                        u"savedpagesearch/" + title):
911
912                    answer = wx.MessageBox(
913                            _(u"Do you want to overwrite existing search '%s'?") %
914                            title, _(u"Overwrite search"),
915                            wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION, self)
916                    if answer != wx.YES:
917                        continue
918
919#                 self.mainControl.getWikiData().saveSearch(title,
920#                         sarOp.getPackedSettings())
921
922                xmlDoc = minidom.getDOMImplementation().createDocument(None,
923                        None, None)
924                xmlNode = xmlDoc.createElement(u"savedpagesearch")
925                sarOp.serializeToXml(xmlNode, xmlDoc)
926               
927                xmlDoc.appendChild(xmlNode)
928                content = xmlDoc.toxml("utf-8")
929                xmlDoc.unlink()
930                self.mainControl.getWikiData().storeDataBlock(
931                        u"savedpagesearch/" + title, content,
932                        storeHint=Consts.DATABLOCK_STOREHINT_INTERN)
933
934                self._refreshSavedSearchesList()
935                break
936        else:
937            self.mainControl.displayErrorMessage(
938                    _(u"Invalid search string, can't save as view"))
939
940
941    def displayErrorMessage(self, errorStr, e=u""):
942        """
943        Pops up an error dialog box
944        """
945        wx.MessageBox(uniToGui(u"%s. %s." % (errorStr, e)), _(u"Error!"),
946            wx.OK, self)
947
948
949    def _nextSearch(self, sarOp):
950        editor = self.mainControl.getActiveEditor()
951        if self.ctrls.rbSearchFrom.GetSelection() == 0:
952            # Search from cursor
953            contPos = editor.getContinuePosForSearch(sarOp)
954        else:
955            # Search from beginning
956            contPos = 0
957            self.ctrls.rbSearchFrom.SetSelection(0)
958           
959        self.addCurrentToHistory()
960        start, end = editor.executeSearch(sarOp,
961                contPos)[:2]
962        if start == -1:
963            # No matches found
964            if contPos != 0:
965                # We started not at beginning, so ask if to wrap around
966                result = wx.MessageBox(_(u"End of document reached. "
967                        u"Continue at beginning?"),
968                        _(u"Continue at beginning?"),
969                        wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, self)
970                if result != wx.YES:
971                    return
972
973                start, end = editor.executeSearch(
974                        sarOp, 0)[:2]
975                if start != -1:
976                    return
977
978            # no more matches possible -> show dialog
979            wx.MessageBox(_(u"No matches found"),
980                    _(u"No matches found"), wx.OK, self)
981
982
983    def OnFindNext(self, evt):
984        if guiToUni(self.ctrls.cbSearch.GetValue()) == u"":
985            return
986
987        sarOp = self._buildSearchReplaceOperation()
988        sarOp.replaceOp = False
989        self.addCurrentToHistory()
990        try:
991            self._nextSearch(sarOp)
992            self.firstFind = False
993        except re.error, e:
994            self.displayErrorMessage(_(u'Error in regular expression'),
995                    _(unicode(e)))
996
997
998    def OnReplace(self, evt):
999        sarOp = self._buildSearchReplaceOperation()
1000#         sarOp.replaceStr = guiToUni(self.ctrls.txtReplace.GetValue())
1001#         sarOp.replaceOp = True
1002        self.addCurrentToHistory()
1003        try:
1004            self.mainControl.getActiveEditor().executeReplace(sarOp)
1005            self._nextSearch(sarOp)
1006        except re.error, e:
1007            self.displayErrorMessage(_(u'Error in regular expression'),
1008                    _(unicode(e)))
1009
1010
1011    def OnReplaceAll(self, evt):
1012        sarOp = self._buildSearchReplaceOperation()
1013#         sarOp.replaceStr = guiToUni(self.ctrls.txtReplace.GetValue())
1014#         sarOp.replaceOp = True
1015        sarOp.cycleToStart = False
1016        lastSearchPos = 0
1017        editor = self.mainControl.getActiveEditor()
1018        self.addCurrentToHistory()
1019        replaceCount = 0
1020        editor.BeginUndoAction()
1021        try:
1022            while True:
1023                nextReplacePos = editor.executeSearch(sarOp, lastSearchPos)[1]
1024                if nextReplacePos == -1:
1025                    break
1026                replaceCount += 1
1027                nextSearchPos = editor.executeReplace(sarOp)
1028                if lastSearchPos == nextReplacePos:
1029                    # Otherwise it would run infinitely
1030                    break
1031                lastSearchPos = nextSearchPos
1032        finally:
1033            editor.EndUndoAction()
1034           
1035        wx.MessageBox(_(u"%i replacements done") % replaceCount,
1036                _(u"Replace All"), wx.OK, self)
1037
1038
1039    def OnSearchComboSelected(self, evt):
1040        hist = wx.GetApp().getPageSearchHistory()
1041        self.showHistoryTuple(hist[evt.GetSelection()])
1042
1043
1044
1045class SearchWikiDialog(wx.Dialog, MiscEventSourceMixin):
1046    def __init__(self, parent, mainControl, ID, srListBox=None,
1047            allowOrdering=True, allowOkCancel=True, value=None,
1048            title="Search Wiki", pos=wx.DefaultPosition, size=wx.DefaultSize,
1049            style=wx.NO_3D|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
1050
1051#         _prof.start()
1052        d = wx.PreDialog()
1053        self.PostCreate(d)
1054
1055        self.mainControl = mainControl
1056
1057        res = wx.xrc.XmlResource.Get()
1058        res.LoadOnDialog(self, parent, "SearchWikiDialog")
1059        if srListBox is None:
1060            srListBox = SearchResultListBox(self, self.mainControl,
1061                    GUI_ID.htmllbPages)
1062        else:
1063            srListBox.Reparent(self)
1064
1065        res.AttachUnknownControl("htmllbPages", srListBox, self)
1066        # Necessary to workaround a bug in layout mechanism
1067        srListBox.GetGrandParent().Layout()
1068        self.ctrls = XrcControls(self)
1069
1070        self.allowOkCancel = allowOkCancel
1071        self.allowOrdering = allowOrdering
1072        if allowOkCancel:
1073            self.ctrls.btnOk.SetId(wx.ID_OK)
1074            self.ctrls.btnCancel.SetId(wx.ID_CANCEL)
1075        else:
1076            self.ctrls.btnOk.SetLabel(_(u"Close"))
1077            self.ctrls.btnOk.SetId(wx.ID_CANCEL)
1078            self.ctrls.btnCancel.Show(False)
1079
1080        currWord = self.mainControl.getCurrentWikiWord()
1081        if currWord is not None:
1082            self.ctrls.tfPageListToAdd.SetValue(uniToGui(currWord))
1083
1084        self.ctrls.cbSearch.SetWindowStyle(self.ctrls.cbSearch.GetWindowStyle()
1085                | wx.TE_PROCESS_ENTER)
1086
1087        self.listNeedsRefresh = True  # Reflects listbox content current
1088                                      # search criteria?
1089
1090        self.searchingStartTime = None
1091
1092        self.savedSearches = None
1093        self.foundPages = []
1094        self.pageListData = []
1095
1096        if not self.allowOrdering:
1097            self.ctrls.chOrdering.SetSelection(self._ORDERNAME_TO_CHOICE["no"])
1098            self.ctrls.chOrdering.Enable(False)
1099
1100        self.ctrls.rboxSearchType.EnableItem(Consts.SEARCHTYPE_INDEX,
1101                self.mainControl.getWikiDocument() is not None and \
1102                self.mainControl.getWikiDocument().isSearchIndexEnabled())
1103
1104        self.pageListRadioButtons = (self.ctrls.rbPagesAll,
1105                self.ctrls.rbPagesMatchRe, self.ctrls.rbPagesInList)
1106
1107        self.panelPageListLastFocused = None 
1108
1109
1110        self._refreshSavedSearchesList()
1111        self._refreshSearchHistoryCombo()
1112
1113        if value is not None:
1114            self.showSearchReplaceOperation(value)
1115        else:
1116            config = self.mainControl.getConfig()
1117            self.ctrls.rboxSearchType.SetSelection(config.getint("main",
1118                    "search_wiki_searchType", 0))
1119            self.ctrls.cbCaseSensitive.SetValue(config.getboolean("main",
1120                    "search_wiki_caseSensitive", False))
1121            self.ctrls.cbWholeWord.SetValue(config.getboolean("main",
1122                    "search_wiki_wholeWord", False))
1123
1124            self.listPagesOperation = ListWikiPagesOperation()
1125            self._showListPagesOperation(self.listPagesOperation)
1126
1127            self.OnRadioBox(None)  # Refresh settings
1128            self._updateTabTitle()
1129           
1130           
1131        # Fixes focus bug under Linux
1132        self.SetFocus()
1133        self.ctrls.cbSearch.SetFocus()
1134
1135        # Events from text search tab
1136        wx.EVT_BUTTON(self, GUI_ID.btnFindPages, self.OnSearchWiki)
1137        wx.EVT_BUTTON(self, GUI_ID.btnFindNext, self.OnFindNext)       
1138        wx.EVT_BUTTON(self, GUI_ID.btnReplace, self.OnReplace)
1139        wx.EVT_BUTTON(self, GUI_ID.btnReplaceAll, self.OnReplaceAll)
1140        wx.EVT_BUTTON(self, GUI_ID.btnSaveSearch, self.OnSaveSearch)
1141        wx.EVT_BUTTON(self, GUI_ID.btnDeleteSearches, self.OnDeleteSearches)
1142        wx.EVT_BUTTON(self, GUI_ID.btnLoadSearch, self.OnLoadSearch)
1143        wx.EVT_BUTTON(self, GUI_ID.btnLoadAndRunSearch, self.OnLoadAndRunSearch)
1144        wx.EVT_BUTTON(self, GUI_ID.btnOptions, self.OnOptions)
1145        wx.EVT_BUTTON(self, GUI_ID.btnCopyPageNamesToClipboard,
1146                self.OnCopyPageNamesToClipboard)
1147        wx.EVT_BUTTON(self, GUI_ID.btnAsResultlist, self.OnCmdAsResultlist)
1148        wx.EVT_BUTTON(self, GUI_ID.btnAsTab, self.OnCmdAsTab)
1149
1150        wx.EVT_CHAR(self.ctrls.cbSearch, self.OnCharToFind)
1151        wx.EVT_CHAR(self.ctrls.rboxSearchType, self.OnCharToFind)
1152        wx.EVT_CHAR(self.ctrls.cbCaseSensitive, self.OnCharToFind)
1153        wx.EVT_CHAR(self.ctrls.cbWholeWord, self.OnCharToFind)
1154
1155        wx.EVT_COMBOBOX(self, GUI_ID.cbSearch, self.OnSearchComboSelected)
1156        wx.EVT_LISTBOX_DCLICK(self, GUI_ID.lbSavedSearches, self.OnLoadAndRunSearch)
1157        wx.EVT_RADIOBOX(self, GUI_ID.rboxSearchType, self.OnRadioBox)
1158
1159        wx.EVT_TEXT(self, GUI_ID.cbSearch, self.OnListRefreshNeeded)
1160        wx.EVT_CHECKBOX(self, GUI_ID.cbCaseSensitive, self.OnListRefreshNeeded)
1161        wx.EVT_CHECKBOX(self, GUI_ID.cbWholeWord, self.OnListRefreshNeeded)
1162
1163
1164        # Events from page list construction tab
1165
1166        wx.EVT_TEXT(self, GUI_ID.tfSubtreeLevels, self.OnTextSubtreeLevels)
1167        wx.EVT_TEXT(self, GUI_ID.tfMatchRe, self.OnTextPageNameMatchRe)
1168
1169        wx.EVT_RADIOBUTTON(self, GUI_ID.rbPagesAll, self.OnPageListRadioButtons)
1170        wx.EVT_RADIOBUTTON(self, GUI_ID.rbPagesMatchRe, self.OnPageListRadioButtons)
1171        wx.EVT_RADIOBUTTON(self, GUI_ID.rbPagesInList, self.OnPageListRadioButtons)
1172
1173        wx.EVT_TEXT_ENTER(self, GUI_ID.tfPageListToAdd, self.OnPageListAdd)
1174        wx.EVT_BUTTON(self, GUI_ID.btnPageListUp, self.OnPageListUp)
1175        wx.EVT_BUTTON(self, GUI_ID.btnPageListDown, self.OnPageListDown)
1176        wx.EVT_BUTTON(self, GUI_ID.btnPageListSort, self.OnPageListSort)
1177
1178        wx.EVT_BUTTON(self, GUI_ID.btnPageListAdd, self.OnPageListAdd)
1179        wx.EVT_BUTTON(self, GUI_ID.btnPageListDelete, self.OnPageListDelete)
1180        wx.EVT_BUTTON(self, GUI_ID.btnPageListClearList, self.OnPageListClearList)
1181
1182        wx.EVT_BUTTON(self, GUI_ID.btnPageListCopyToClipboard,
1183                self.OnPageListCopyToClipboard)
1184
1185        wx.EVT_BUTTON(self, GUI_ID.btnPageListAddFromClipboard,
1186                self.OnPageListAddFromClipboard)
1187        wx.EVT_BUTTON(self, GUI_ID.btnPageListOverwriteFromClipboard,
1188                self.OnPageListOverwriteFromClipboard)
1189        wx.EVT_BUTTON(self, GUI_ID.btnPageListIntersectWithClipboard,
1190                self.OnPageListIntersectWithClipboard)
1191
1192        wx.EVT_BUTTON(self, GUI_ID.btnResultListPreview, self.OnResultListPreview)
1193        wx.EVT_BUTTON(self, GUI_ID.btnResultCopyToClipboard,
1194                self.OnResultCopyToClipboard)
1195
1196
1197#         wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnClose)       
1198#         wx.EVT_CLOSE(self, self.OnClose)
1199
1200        # Common events on OK, Close, Cancel
1201        wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnClose)       
1202        wx.EVT_CLOSE(self, self.OnClose)
1203        wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk)
1204       
1205       
1206        wx.EVT_CHILD_FOCUS(self.ctrls.panelPageList, self.OnPageListChildFocus)
1207       
1208       
1209#         _prof.stop()
1210       
1211
1212
1213
1214    _ORDERCHOICE_TO_NAME = {
1215            0: "natural",
1216            1: "ascending",
1217            2: "asroottree",
1218            3: "no"
1219    }
1220
1221    _ORDERNAME_TO_CHOICE = {
1222            "natural": 0,
1223            "ascending": 1,
1224            "asroottree": 2,
1225            "no": 3
1226    }
1227
1228
1229    def setValue(self, value):
1230        self.value = value
1231
1232    def getValue(self):
1233        return self.value
1234
1235
1236    def displayErrorMessage(self, errorStr, e=u""):
1237        """
1238        Pops up an error dialog box
1239        """
1240        wx.MessageBox(uniToGui(u"%s. %s." % (errorStr, e)), _(u"Error!"),
1241            wx.OK, self)
1242
1243
1244    def _buildListPagesOperation(self):
1245        """
1246        Construct a ListWikiPagesOperation according to current content of the
1247        second tab of the dialog
1248        """
1249        import SearchAndReplace as Sar
1250       
1251        lpOp = Sar.ListWikiPagesOperation()
1252       
1253        if self.ctrls.rbPagesAll.GetValue():
1254            item = Sar.AllWikiPagesNode(lpOp)
1255        elif self.ctrls.rbPagesMatchRe.GetValue():
1256            pattern = self.ctrls.tfMatchRe.GetValue()
1257            try:
1258                re.compile(pattern, re.DOTALL | re.UNICODE | re.MULTILINE)
1259            except re.error, e:
1260                wx.MessageBox(_(u"Bad regular expression '%s':\n%s") %
1261                        (pattern, _(unicode(e))), _(u"Error in regular expression"),
1262                        wx.OK, self)
1263                return None
1264               
1265            item = Sar.RegexWikiPageNode(lpOp, pattern)
1266        elif self.ctrls.rbPagesInList.GetValue():
1267            try:
1268                level = int(self.ctrls.tfSubtreeLevels.GetValue())
1269                if level < 0:
1270                    raise ValueError
1271            except ValueError:
1272                level = -1
1273
1274            item = Sar.ListItemWithSubtreeWikiPagesNode(lpOp,
1275                    self.pageListData[:], level)
1276        else:
1277            return None
1278
1279        lpOp.setSearchOpTree(item)
1280        lpOp.ordering = self._ORDERCHOICE_TO_NAME[
1281                self.ctrls.chOrdering.GetSelection()]
1282
1283        return lpOp
1284
1285
1286    def OnPageListChildFocus(self, evt):
1287        self.panelPageListLastFocused = evt.GetEventObject()
1288        evt.Skip()
1289
1290
1291    def _buildSearchReplaceOperation(self):
1292        searchType = self.ctrls.rboxSearchType.GetSelection()
1293       
1294        sarOp = SearchReplaceOperation()
1295        sarOp.searchStr = stripSearchString(
1296                guiToUni(self.ctrls.cbSearch.GetValue()))
1297        sarOp.booleanOp = searchType == Consts.SEARCHTYPE_BOOLEANREGEX
1298       
1299        sarOp.indexSearch = 'no' if searchType != Consts.SEARCHTYPE_INDEX \
1300                else 'default'
1301        sarOp.caseSensitive = self.ctrls.cbCaseSensitive.GetValue()
1302        sarOp.wholeWord = self.ctrls.cbWholeWord.GetValue()
1303        sarOp.cycleToStart = False
1304        sarOp.wildCard = 'regex' if searchType != Consts.SEARCHTYPE_ASIS else 'no'
1305        sarOp.wikiWide = True
1306        self.listPagesOperation = self._buildListPagesOperation()
1307        sarOp.listWikiPagesOp = self.listPagesOperation
1308
1309        if not sarOp.booleanOp:
1310            sarOp.replaceStr = guiToUni(self.ctrls.txtReplace.GetValue())
1311
1312        return sarOp
1313
1314
1315    def _showListPagesOperation(self, lpOp):
1316        if lpOp is not None:
1317            item = self.listPagesOperation.searchOpTree
1318           
1319            if item.CLASS_PERSID == "AllPages":
1320                self._setPageListRadioButton(self.ctrls.rbPagesAll)
1321            elif item.CLASS_PERSID == "RegexPage":
1322                self._setPageListRadioButton(self.ctrls.rbPagesMatchRe)
1323                self.ctrls.tfMatchRe.SetValue(item.getPattern())
1324            elif item.CLASS_PERSID == "ListItemWithSubtreePages":
1325                self._setPageListRadioButton(self.ctrls.rbPagesInList)
1326                self.pageListData = item.rootWords[:]
1327                self.ctrls.lbPageList.AppendItems(self.pageListData)
1328                if item.level == -1:
1329                    self.ctrls.tfSubtreeLevels.SetValue(u"")
1330                else:
1331                    self.ctrls.tfSubtreeLevels.SetValue(u"%i" % item.level)
1332                   
1333            self.ctrls.chOrdering.SetSelection(
1334                    self._ORDERNAME_TO_CHOICE[self.listPagesOperation.ordering])
1335           
1336            self._updateTabTitle()
1337
1338
1339    def showSearchReplaceOperation(self, sarOp):
1340        self.ctrls.cbSearch.SetValue(uniToGui(sarOp.searchStr))
1341        if sarOp.booleanOp:
1342            self.ctrls.rboxSearchType.SetSelection(Consts.SEARCHTYPE_BOOLEANREGEX)
1343        elif sarOp.indexSearch == 'default':
1344            if self.mainControl.getWikiDocument() is not None and \
1345                    self.mainControl.getWikiDocument().isSearchIndexEnabled():
1346                self.ctrls.rboxSearchType.SetSelection(Consts.SEARCHTYPE_INDEX)
1347            else:
1348                self.ctrls.rboxSearchType.SetSelection(Consts.SEARCHTYPE_BOOLEANREGEX)
1349        else:
1350            if sarOp.wildCard == 'regex':
1351                self.ctrls.rboxSearchType.SetSelection(Consts.SEARCHTYPE_REGEX)
1352            else:
1353                self.ctrls.rboxSearchType.SetSelection(Consts.SEARCHTYPE_ASIS)
1354
1355        self.ctrls.cbCaseSensitive.SetValue(sarOp.caseSensitive)
1356        self.ctrls.cbWholeWord.SetValue(sarOp.wholeWord)
1357
1358        if not sarOp.booleanOp and sarOp.replaceOp:
1359            self.ctrls.txtReplace.SetValue(uniToGui(sarOp.replaceStr))
1360
1361        self.listPagesOperation = sarOp.listWikiPagesOp
1362        self._showListPagesOperation(self.listPagesOperation)
1363
1364        self.OnRadioBox(None)  # Refresh settings
1365        self._updateTabTitle()
1366
1367
1368#     @profile
1369    def _refreshPageList(self):
1370        sarOp = self._buildSearchReplaceOperation()
1371
1372        # If allowOkCancel is True, the dialog is used to create a set of pages
1373        # so process even for an empty search string
1374        if len(sarOp.searchStr) == 0 and not self.allowOkCancel:
1375            self.foundPages = []
1376            self.ctrls.htmllbPages.showFound(None, None, None)
1377
1378            self.listNeedsRefresh = False
1379            return
1380
1381        disableSet = wxHelper.getAllChildWindows(self)
1382        disableSet.difference_update(wxHelper.getWindowParentsUpTo(
1383                self.ctrls.htmllbPages, self))
1384        disableSet = set(win for win in disableSet if win.IsEnabled())
1385
1386        self.ctrls.htmllbPages.showSearching()
1387        self.SetCursor(wx.HOURGLASS_CURSOR)
1388#         self.Freeze()
1389
1390        self.searchingStartTime = time.time()
1391
1392        if self.mainControl.configuration.getboolean("main",
1393                        "search_dontAllowCancel"):
1394            threadstop = DUMBTHREADSTOP
1395        else:
1396            threadstop = FunctionThreadStop(self._searchPoll)
1397
1398        for win in disableSet:
1399            win.Disable()
1400        try:
1401            self.foundPages = self.mainControl.getWikiDocument().searchWiki(
1402                    sarOp, self.allowOrdering, threadstop=threadstop)
1403            if not self.allowOrdering:
1404                # Use default alphabetical ordering
1405                self.mainControl.getCollator().sort(self.foundPages)
1406
1407            self.ctrls.htmllbPages.showFound(sarOp, self.foundPages,
1408                    self.mainControl.getWikiDocument(),
1409                    threadstop=threadstop)
1410
1411            self.listNeedsRefresh = False
1412
1413        except NotCurrentThreadException:
1414            raise UserAbortException()
1415        finally:
1416            self.searchingStartTime = None
1417            for win in disableSet:
1418                win.Enable()
1419
1420            # "index" option in search type was enabled by the above operation
1421            # so disable again if necessary
1422            self.ctrls.rboxSearchType.EnableItem(Consts.SEARCHTYPE_INDEX,
1423                    self.mainControl.getWikiDocument() is not None and \
1424                    self.mainControl.getWikiDocument().isSearchIndexEnabled())
1425
1426    #         self.Thaw()
1427            self.SetCursor(wx.NullCursor)
1428            self.ctrls.htmllbPages.ensureNotShowSearching()
1429
1430
1431    def _searchPoll(self):
1432        return wx.SafeYield(self, True) and self.searchingStartTime is not None
1433
1434    def stopSearching(self):
1435        self.searchingStartTime = None
1436
1437
1438    def OnOk(self, evt):
1439        self.stopSearching()
1440        val = self._buildSearchReplaceOperation()
1441        self.value = val
1442        if val is None:
1443            return
1444
1445        try:
1446            self.mainControl.nonModalWwSearchDlgs.remove(self)
1447        except ValueError:
1448            if self is self.mainControl.nonModalMainWwSearchDlg:
1449                self.mainControl.nonModalMainWwSearchDlg = None
1450
1451        if self.IsModal():
1452            self.EndModal(wx.ID_OK)
1453        else:
1454            self.Destroy()
1455#             self.fireMiscEventProps({"nonmodal closed": wx.ID_OK,
1456#                     "listWikiPagesOp": self.value})
1457
1458
1459    def OnClose(self, evt):
1460        self.stopSearching()
1461
1462        self.value = None
1463        try:
1464            self.mainControl.nonModalWwSearchDlgs.remove(self)
1465        except ValueError:
1466            if self is self.mainControl.nonModalMainWwSearchDlg:
1467                self.mainControl.nonModalMainWwSearchDlg = None
1468
1469        if self.IsModal():
1470            self.EndModal(wx.ID_CANCEL)
1471        else:
1472            self.Destroy()
1473#             self.fireMiscEventProps({"nonmodal closed": wx.ID_CANCEL,
1474#                     "listWikiPagesOp": None})
1475
1476    def OnSearchWiki(self, evt):
1477        try:
1478            self._refreshPageList()
1479            self.addCurrentToHistory()
1480            if not self.ctrls.htmllbPages.IsEmpty():
1481                self.ctrls.htmllbPages.SetFocus()
1482                self.ctrls.htmllbPages.SetSelection(0)
1483        except UserAbortException:
1484            return
1485        except re.error, e:
1486            self.displayErrorMessage(_(u'Error in regular expression'),
1487                    _(unicode(e)))
1488        except ParseException, e:
1489            self.displayErrorMessage(_(u'Error in boolean expression'),
1490                    _(unicode(e)))
1491        except DbReadAccessError, e:
1492            self.displayErrorMessage(_(u'Error. Maybe wiki rebuild is needed'),
1493                    _(unicode(e)))
1494            return
1495
1496
1497    def OnListRefreshNeeded(self, evt):
1498        self.listNeedsRefresh = True
1499        self._updateTabTitle()
1500
1501    def OnFindNext(self, evt):
1502        self._findNext()
1503
1504    def _findNext(self):
1505        if self.listNeedsRefresh:
1506            try:
1507                # Refresh list and start from beginning
1508                self._refreshPageList()
1509            except UserAbortException:
1510                return
1511            except re.error, e:
1512                self.displayErrorMessage(_(u'Error in regular expression'),
1513                        _(unicode(e)))
1514                return
1515            except ParseException, e:
1516                self.displayErrorMessage(_(u'Error in boolean expression'),
1517                        _(unicode(e)))
1518                return
1519            except DbReadAccessError, e:
1520                self.displayErrorMessage(_(u'Error. Maybe wiki rebuild is needed'),
1521                        _(unicode(e)))
1522                return
1523
1524
1525        self.addCurrentToHistory()
1526        if self.ctrls.htmllbPages.GetCount() == 0:
1527            return
1528       
1529        try:
1530            while True:           
1531                   
1532                #########self.ctrls.lb.SetSelection(self.listPosNext)
1533               
1534                wikiWord = guiToUni(self.ctrls.htmllbPages.GetSelectedWord())
1535               
1536                if not wikiWord:
1537                    self.ctrls.htmllbPages.SetSelection(0)
1538                    wikiWord = guiToUni(self.ctrls.htmllbPages.GetSelectedWord())
1539   
1540                if self.mainControl.getCurrentWikiWord() != wikiWord:
1541                    self.mainControl.openWikiPage(wikiWord)
1542                    nextOnPage = False
1543                else:
1544                    nextOnPage = True
1545   
1546                searchOp = self._buildSearchReplaceOperation()
1547                searchOp.replaceOp = False
1548                if nextOnPage:
1549                    pagePosNext = self.mainControl.getActiveEditor().executeSearch(searchOp,
1550                            -2)[1]
1551                else:
1552                    pagePosNext = self.mainControl.getActiveEditor().executeSearch(searchOp,
1553                            0)[1]
1554                   
1555                if pagePosNext != -1:
1556                    return  # Found
1557                   
1558                if self.ctrls.htmllbPages.GetSelection() == \
1559                        self.ctrls.htmllbPages.GetCount() - 1:
1560                    # Nothing more found on the last page in list, so back to
1561                    # begin of list and stop
1562                    self.ctrls.htmllbPages.SetSelection(0)
1563                    return
1564                   
1565                # Otherwise: Go to next page in list           
1566                self.ctrls.htmllbPages.SetSelection(
1567                        self.ctrls.htmllbPages.GetSelection() + 1)
1568        except re.error, e:
1569            self.displayErrorMessage(_(u'Error in regular expression'),
1570                    _(unicode(e)))
1571        except ParseException, e:
1572            self.displayErrorMessage(_(u'Error in boolean expression'),
1573                    _(unicode(e)))
1574
1575
1576
1577    def OnReplace(self, evt):
1578        sarOp = self._buildSearchReplaceOperation()
1579        sarOp.replaceOp = True
1580        try:
1581            self.mainControl.getActiveEditor().executeReplace(sarOp)
1582        except re.error, e:
1583            self.displayErrorMessage(_(u'Error in regular expression'),
1584                    _(unicode(e)))
1585            return
1586        except ParseException, e:  # Probably this can't happen
1587            self.displayErrorMessage(_(u'Error in boolean expression'),
1588                    _(unicode(e)))
1589            return
1590
1591
1592        self._findNext()
1593
1594
1595    def OnReplaceAll(self, evt):
1596        answer = wx.MessageBox(_(u"Replace all occurrences?"), _(u"Replace All"),
1597                wx.YES_NO | wx.NO_DEFAULT, self)
1598
1599        if answer != wx.YES:
1600            return
1601
1602        try:
1603            self._refreshPageList()
1604
1605            if self.ctrls.htmllbPages.GetCount() == 0:
1606                return
1607
1608            # self.pWiki.saveCurrentDocPage()
1609
1610            sarOp = self._buildSearchReplaceOperation()
1611            sarOp.replaceOp = True
1612           
1613            # wikiData = self.pWiki.getWikiData()
1614            wikiDocument = self.mainControl.getWikiDocument()
1615            self.addCurrentToHistory()
1616           
1617            replaceCount = 0
1618   
1619            for i in xrange(self.ctrls.htmllbPages.GetCount()):
1620                self.ctrls.htmllbPages.SetSelection(i)
1621                wikiWord = guiToUni(self.ctrls.htmllbPages.GetSelectedWord())
1622                wikiPage = wikiDocument.getWikiPageNoError(wikiWord)
1623                text = wikiPage.getLiveTextNoTemplate()
1624                if text is None:
1625                    continue
1626   
1627                charStartPos = 0
1628   
1629                sarOp.beginWikiSearch(self.mainControl.getWikiDocument())
1630                try:
1631                    while True:
1632                        try:
1633                            found = sarOp.searchDocPageAndText(wikiPage, text,
1634                                    charStartPos)
1635                            start, end = found[:2]
1636                        except:
1637                            # Regex error -> Stop searching
1638                            return
1639                           
1640                        if start is None: break
1641                       
1642                        repl = sarOp.replace(text, found)
1643                        text = text[:start] + repl + text[end:]  # TODO Faster?
1644                        charStartPos = start + len(repl)
1645                        replaceCount += 1
1646                        if start == end:
1647                            # Otherwise replacing would go infinitely
1648                            break
1649                finally:
1650                    sarOp.endWikiSearch()
1651
1652                wikiPage.replaceLiveText(text)
1653                   
1654            self._refreshPageList()
1655           
1656            wx.MessageBox(_(u"%i replacements done") % replaceCount,
1657                    _(u"Replace All"),
1658                wx.OK, self)       
1659        except UserAbortException:
1660            return
1661        except re.error, e:
1662            self.displayErrorMessage(_(u'Error in regular expression'),
1663                    _(unicode(e)))
1664        except ParseException, e:
1665            self.displayErrorMessage(_(u'Error in boolean expression'),
1666                    _(unicode(e)))
1667        except DbReadAccessError, e:
1668            self.displayErrorMessage(_(u'Error. Maybe wiki rebuild is needed'),
1669                    _(unicode(e)))
1670
1671
1672    def addCurrentToHistory(self):
1673        sarOp = self._buildSearchReplaceOperation()
1674        try:
1675            sarOp.rebuildSearchOpTree()
1676        except re.error:
1677            # Ignore silently
1678            return
1679        except ParseException, e:
1680            # This too
1681            return
1682
1683        data = sarOp.getPackedSettings()
1684        tpl = (sarOp.searchStr, sarOp.getPackedSettings())
1685        hist = wx.GetApp().getWikiSearchHistory()
1686        try:
1687            pos = hist.index(tpl)
1688            del hist[pos]
1689            hist.insert(0, tpl)
1690        except ValueError:
1691            # tpl not in hist
1692            hist.insert(0, tpl)
1693            if len(hist) > 10:
1694                hist = hist[:10]
1695           
1696        wx.GetApp().setWikiSearchHistory(hist)
1697        text = self.ctrls.cbSearch.GetValue()
1698        self._refreshSearchHistoryCombo()
1699#         self.ctrls.cbSearch.Clear()
1700#         self.ctrls.cbSearch.AppendItems([tpl[0] for tpl in hist])
1701        self.ctrls.cbSearch.SetValue(text)
1702
1703
1704
1705    # TODO Store search mode
1706    def OnSaveSearch(self, evt):
1707        sarOp = self._buildSearchReplaceOperation()
1708        try:
1709            sarOp.rebuildSearchOpTree()
1710        except re.error, e:
1711            self.mainControl.displayErrorMessage(_(u'Error in regular expression'),
1712                    _(unicode(e)))
1713            return
1714        except ParseException, e:
1715            self.mainControl.displayErrorMessage(_(u'Error in boolean expression'),
1716                    _(unicode(e)))
1717            return
1718
1719        if len(sarOp.searchStr) > 0:
1720            title = sarOp.getTitle()
1721            while True:
1722                title = guiToUni(wx.GetTextFromUser(_(u"Title:"),
1723                        _(u"Choose search title"), title, self))
1724                if title == u"":
1725                    return  # Cancel
1726                   
1727#                 if title in self.pWiki.getWikiData().getSavedSearchTitles():
1728                if (u"savedsearch/" + title) in self.mainControl.getWikiData()\
1729                        .getDataBlockUnifNamesStartingWith(
1730                        u"savedsearch/" + title):
1731
1732                    answer = wx.MessageBox(
1733                            _(u"Do you want to overwrite existing search '%s'?") %
1734                            title, _(u"Overwrite search"),
1735                            wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION, self)
1736                    if answer != wx.YES:
1737                        continue
1738
1739#                 self.pWiki.getWikiData().saveSearch(title,
1740#                         sarOp.getPackedSettings())
1741                self.mainControl.getWikiData().storeDataBlock(
1742                        u"savedsearch/" + title, sarOp.getPackedSettings(),
1743                        storeHint=Consts.DATABLOCK_STOREHINT_INTERN)
1744
1745                self._refreshSavedSearchesList()
1746                break
1747        else:
1748            self.mainControl.displayErrorMessage(
1749                    _(u"Invalid search string, can't save as view"))
1750
1751
1752    def OnRadioBox(self, evt):
1753        self.listNeedsRefresh = True
1754        booleanSearch = self.ctrls.rboxSearchType.GetSelection() in (1, 3)
1755
1756        self.ctrls.txtReplace.Enable(not booleanSearch)
1757        self.ctrls.btnFindNext.Enable(not booleanSearch)
1758        self.ctrls.btnReplace.Enable(not booleanSearch)
1759        self.ctrls.btnReplaceAll.Enable(not booleanSearch)
1760
1761
1762    def OnOptions(self, evt):
1763        self.mainControl.showOptionsDialog("OptionsPageSearching")
1764#         dlg = SearchWikiOptionsDialog(self, self.GetParent(), -1)
1765#         dlg.CenterOnParent(wx.BOTH)
1766#
1767#         dlg.ShowModal()
1768#         dlg.Destroy()
1769
1770
1771    def getResultListPositionTuple(self):
1772        return getRelativePositionTupleToAncestor(self.ctrls.htmllbPages, self)
1773
1774
1775    def OnCmdAsResultlist(self, evt):
1776        self.Hide()
1777       
1778        ownPos = self.GetPositionTuple()
1779        oldRelBoxPos = self.getResultListPositionTuple()
1780       
1781        frame = FastSearchPopup(self.GetParent(), self.mainControl, -1,
1782                srListBox=self.ctrls.htmllbPages)
1783        frame.setSearchOp(self._buildSearchReplaceOperation())
1784       
1785        newRelBoxPos = frame.getResultListPositionTuple()
1786
1787        # A bit math to ensure that result list in both windows is placed
1788        # at same position (looks more cool)
1789        otherPos = (ownPos[0] + oldRelBoxPos[0] - newRelBoxPos[0],
1790                ownPos[1] + oldRelBoxPos[1] - newRelBoxPos[1])
1791       
1792        setWindowPos(frame, pos=otherPos, fullVisible=True)
1793        self.mainControl.nonModalWwSearchDlgs.append(frame)
1794        frame.Show()
1795        self.Close()
1796
1797
1798    def OnCmdAsTab(self, evt):
1799        self.Hide()
1800
1801        maPanel = self.mainControl.getMainAreaPanel()
1802        presenter = LayeredControlPanel(maPanel)
1803        subCtl = SearchResultPresenterControl(presenter, self.mainControl,
1804                self.GetParent(), -1, srListBox=self.ctrls.htmllbPages)
1805        presenter.setSubControl("search result list", subCtl)
1806        presenter.switchSubControl("search result list")
1807        maPanel.appendPresenterTab(presenter)
1808        subCtl.setSearchOp(self._buildSearchReplaceOperation())
1809
1810        maPanel.showPresenter(presenter)
1811        self.Close()
1812
1813
1814#     def OnClose(self, evt):
1815#         try:
1816#             self.mainControl.nonModalWwSearchDlgs.remove(self)
1817#         except ValueError:
1818#             if self is self.mainControl.nonModalMainWwSearchDlg:
1819#                 self.mainControl.nonModalMainWwSearchDlg = None
1820#
1821#         self.Destroy()
1822
1823
1824    def _refreshSavedSearchesList(self):
1825        unifNames = self.mainControl.getWikiData()\
1826                .getDataBlockUnifNamesStartingWith(u"savedsearch/")
1827
1828        self.savedSearches = [name[12:] for name in unifNames]
1829        self.mainControl.getCollator().sort(self.savedSearches)
1830
1831        self.ctrls.lbSavedSearches.Clear()
1832        for search in self.savedSearches:
1833            self.ctrls.lbSavedSearches.Append(uniToGui(search))
1834
1835
1836    def _refreshSearchHistoryCombo(self):
1837        hist = wx.GetApp().getWikiSearchHistory()
1838        self.ctrls.cbSearch.Clear()
1839        self.ctrls.cbSearch.AppendItems([tpl[0] for tpl in hist])
1840
1841
1842    def OnDeleteSearches(self, evt):
1843        sels = self.ctrls.lbSavedSearches.GetSelections()
1844       
1845        if len(sels) == 0:
1846            return
1847           
1848        answer = wx.MessageBox(
1849                _(u"Do you want to delete %i search(es)?") % len(sels),
1850                _(u"Delete search"),
1851                wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION, self)
1852        if answer != wx.YES:
1853            return
1854
1855        for s in sels:
1856#             self.pWiki.getWikiData().deleteSavedSearch(self.savedSearches[s])
1857            self.mainControl.getWikiData().deleteDataBlock(
1858                    u"savedsearch/" + self.savedSearches[s])
1859        self._refreshSavedSearchesList()
1860
1861
1862    def OnLoadSearch(self, evt):
1863        self._loadSearch()
1864       
1865    def OnLoadAndRunSearch(self, evt):
1866        if self._loadSearch():
1867            try:
1868                self._refreshPageList()
1869            except UserAbortException:
1870                return
1871            except re.error, e:
1872                self.displayErrorMessage(_(u'Error in regular expression'),
1873                        _(unicode(e)))
1874            except ParseException, e:
1875                self.displayErrorMessage(_(u'Error in boolean expression'),
1876                        _(unicode(e)))
1877            except DbReadAccessError, e:
1878                self.displayErrorMessage(_(u'Error. Maybe wiki rebuild is needed'),
1879                        _(unicode(e)))
1880
1881
1882    def _loadSearch(self):
1883        sels = self.ctrls.lbSavedSearches.GetSelections()
1884       
1885        if len(sels) != 1:
1886            return False
1887       
1888        datablock = self.mainControl.getWikiData().retrieveDataBlock(
1889                u"savedsearch/" + self.savedSearches[sels[0]])
1890
1891        sarOp = SearchReplaceOperation()
1892        sarOp.setPackedSettings(datablock)
1893       
1894        self.showSearchReplaceOperation(sarOp)
1895       
1896        return True
1897
1898
1899    def OnSearchComboSelected(self, evt):
1900        hist = wx.GetApp().getWikiSearchHistory()
1901        sarOp = SearchReplaceOperation()
1902        sarOp.setPackedSettings(hist[evt.GetSelection()][1])
1903       
1904        self.showSearchReplaceOperation(sarOp)
1905        self.ctrls.txtReplace.SetValue(guiToUni(sarOp.replaceStr))
1906
1907
1908    def OnCopyPageNamesToClipboard(self, evt):
1909        langHelper = wx.GetApp().createWikiLanguageHelper(
1910                self.mainControl.getWikiDefaultWikiLanguage())
1911
1912        wordsText = u"".join([
1913                langHelper.createAbsoluteLinksFromWikiWords((w,)) + "\n"
1914                for w in self.foundPages])
1915
1916        copyTextToClipboard(wordsText)
1917
1918
1919    def OnTextSubtreeLevels(self, evt):
1920        self._setPageListRadioButton(self.ctrls.rbPagesInList)
1921        self._updateTabTitle()
1922        self.listNeedsRefresh = True
1923
1924    def OnTextPageNameMatchRe(self, evt):
1925        self._setPageListRadioButton(self.ctrls.rbPagesMatchRe)
1926        self._updateTabTitle()
1927        self.listNeedsRefresh = True
1928
1929
1930    def OnCharToFind(self, evt):
1931        if (evt.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER)):
1932            self.OnSearchWiki(evt)
1933        elif evt.GetKeyCode() == wx.WXK_TAB:
1934            if evt.ShiftDown():
1935                self.ctrls.cbSearch.Navigate(wx.NavigationKeyEvent.IsBackward |
1936                        wx.NavigationKeyEvent.FromTab)
1937            else:
1938                self.ctrls.cbSearch.Navigate(wx.NavigationKeyEvent.IsForward |
1939                        wx.NavigationKeyEvent.FromTab)
1940        else:
1941            evt.Skip()
1942
1943
1944    # Processing of events on second tab
1945   
1946    def _updateTabTitle(self):
1947        if self.ctrls.rbPagesAll.GetValue():
1948            self.ctrls.nbSearchWiki.SetPageText(1, _(u"Set page list"))
1949        else:
1950            self.ctrls.nbSearchWiki.SetPageText(1, _(u"*Set page list*"))           
1951
1952
1953    def _setPageListRadioButton(self, selectedBtn):
1954        refocus = False
1955        focused = self.panelPageListLastFocused
1956       
1957        for btn in self.pageListRadioButtons:
1958            if btn is selectedBtn:
1959                btn.SetValue(True)
1960            else:
1961                if btn is focused:
1962                    refocus = True
1963
1964                btn.SetValue(False)
1965               
1966        if refocus:
1967            self.ctrls.panelPageList.ProcessEvent(wx.ChildFocusEvent(selectedBtn))
1968
1969
1970
1971    def OnPageListRadioButtons(self, evt):
1972        self._setPageListRadioButton(evt.GetEventObject())
1973       
1974        self.OnListRefreshNeeded(evt)
1975
1976
1977    def OnPageListUp(self, evt):
1978        sel = self.ctrls.lbPageList.GetSelection()
1979        if sel == wx.NOT_FOUND or sel == 0:
1980            return
1981           
1982        self.listNeedsRefresh = True
1983           
1984        dispWord = self.ctrls.lbPageList.GetString(sel)
1985        word = self.pageListData[sel]
1986       
1987        self.ctrls.lbPageList.Delete(sel)
1988        del self.pageListData[sel]
1989       
1990        self.ctrls.lbPageList.Insert(dispWord, sel - 1)
1991        self.pageListData.insert(sel - 1, word)
1992        self.ctrls.lbPageList.SetSelection(sel - 1)
1993       
1994       
1995    def OnPageListDown(self, evt):
1996        sel = self.ctrls.lbPageList.GetSelection()
1997        if sel == wx.NOT_FOUND or sel == len(self.pageListData) - 1:
1998            return
1999           
2000        self.listNeedsRefresh = True
2001
2002        dispWord = self.ctrls.lbPageList.GetString(sel)
2003        word = self.pageListData[sel]
2004       
2005        self.ctrls.lbPageList.Delete(sel)
2006        del self.pageListData[sel]
2007       
2008        self.ctrls.lbPageList.Insert(dispWord, sel + 1)
2009        self.pageListData.insert(sel + 1, word)
2010        self.ctrls.lbPageList.SetSelection(sel + 1)
2011
2012
2013    def OnPageListSort(self, evt):
2014        self._setPageListRadioButton(self.ctrls.rbPagesInList)
2015        self._updateTabTitle()
2016        self.listNeedsRefresh = True
2017
2018        self.mainControl.getCollator().sort(self.pageListData)
2019       
2020        self.ctrls.lbPageList.Clear()
2021        self.ctrls.lbPageList.AppendItems(self.pageListData)
2022
2023
2024    def OnPageListAdd(self, evt):
2025        self._setPageListRadioButton(self.ctrls.rbPagesInList)
2026        self._updateTabTitle()
2027
2028        word = guiToUni(self.ctrls.tfPageListToAdd.GetValue())
2029
2030        langHelper = wx.GetApp().createWikiLanguageHelper(
2031                self.mainControl.getWikiDefaultWikiLanguage())
2032        word = langHelper.extractWikiWordFromLink(word,
2033                self.mainControl.getWikiDocument())
2034        if word is None:
2035            return
2036
2037        if word in self.pageListData:
2038            return  # Already in list
2039       
2040        self.listNeedsRefresh = True
2041
2042        sel = self.ctrls.lbPageList.GetSelection()
2043        if sel == wx.NOT_FOUND:
2044            self.ctrls.lbPageList.Append(uniToGui(word))
2045            self.pageListData.append(word)
2046            self.ctrls.lbPageList.SetSelection(len(self.pageListData) - 1)
2047        else:
2048            self.ctrls.lbPageList.Insert(uniToGui(word), sel + 1)
2049            self.pageListData.insert(sel + 1, word)
2050            self.ctrls.lbPageList.SetSelection(sel + 1)
2051           
2052        self.ctrls.tfPageListToAdd.SetValue(u"")
2053
2054
2055    def OnPageListDelete(self, evt):
2056        self._setPageListRadioButton(self.ctrls.rbPagesInList)
2057        self._updateTabTitle()
2058
2059        sel = self.ctrls.lbPageList.GetSelection()
2060        if sel == wx.NOT_FOUND:
2061            return
2062
2063        self.ctrls.lbPageList.Delete(sel)
2064        del self.pageListData[sel]
2065       
2066        count = len(self.pageListData)
2067        if count == 0:
2068            return
2069           
2070        self.listNeedsRefresh = True
2071       
2072        if sel >= count:
2073            sel = count - 1
2074        self.ctrls.lbPageList.SetSelection(sel)
2075
2076
2077    def OnPageListClearList(self, evt):
2078        self._setPageListRadioButton(self.ctrls.rbPagesInList)
2079        self._updateTabTitle()
2080
2081        self.ctrls.lbPageList.Clear()
2082        self.pageListData = []
2083        self.listNeedsRefresh = True
2084       
2085
2086    def OnPageListAddFromClipboard(self, evt):
2087        """
2088        Take wiki words from clipboard and enter them into the list
2089        """
2090        self._setPageListRadioButton(self.ctrls.rbPagesInList)
2091        self._updateTabTitle()
2092
2093        text = getTextFromClipboard()
2094        if text:
2095            self.listNeedsRefresh = True
2096            pageAst = self.mainControl.getCurrentDocPage().parseTextInContext(text)
2097            wwNodes = pageAst.iterDeepByName("wikiWord")
2098            found = {}
2099            # First fill found with already existing entries
2100            for w in self.pageListData:
2101                found[w] = None
2102
2103            for node in wwNodes:
2104                w = node.wikiWord
2105                if not found.has_key(w):
2106                    self.ctrls.lbPageList.Append(uniToGui(w))
2107                    self.pageListData.append(w)
2108                    found[w] = None
2109
2110
2111    def OnPageListOverwriteFromClipboard(self, evt):
2112        self.ctrls.lbPageList.Clear()
2113        self.listNeedsRefresh = True
2114        self.pageListData = []
2115       
2116        self.OnPageListAddFromClipboard(evt)
2117
2118
2119    def OnPageListIntersectWithClipboard(self, evt):
2120        """
2121        Take wiki words from clipboard and intersect with the list
2122        """
2123        self._setPageListRadioButton(self.ctrls.rbPagesInList)
2124        self._updateTabTitle()
2125
2126        text = getTextFromClipboard()
2127       
2128        if text:
2129            self.listNeedsRefresh = True
2130            pageAst = self.mainControl.getCurrentDocPage().parseTextInContext(text)
2131            wwNodes = pageAst.iterDeepByName("wikiWord")
2132            found = {}
2133
2134            for node in wwNodes:
2135                w = node.wikiWord
2136                found[w] = None
2137
2138            pageList = self.pageListData
2139            self.pageListData = []
2140            self.ctrls.lbPageList.Clear()
2141
2142            # Now fill all with already existing entries
2143            for w in pageList:
2144                if found.has_key(w):
2145                    self.ctrls.lbPageList.Append(uniToGui(w))
2146                    self.pageListData.append(w)
2147                    del found[w]
2148
2149
2150    def OnPageListCopyToClipboard(self, evt):
2151        langHelper = wx.GetApp().createWikiLanguageHelper(
2152                self.mainControl.getWikiDefaultWikiLanguage())
2153
2154        wordsText = u"".join([
2155                langHelper.createAbsoluteLinksFromWikiWords((w,)) + "\n"
2156                for w in self.pageListData])
2157
2158        copyTextToClipboard(wordsText)
2159
2160
2161    def OnResultCopyToClipboard(self, evt):
2162        langHelper = wx.GetApp().createWikiLanguageHelper(
2163                self.mainControl.getWikiDefaultWikiLanguage())
2164
2165        wordsText = u"".join([
2166                langHelper.createAbsoluteLinksFromWikiWords((w,)) + "\n"
2167                for w in self.resultListData])
2168
2169        copyTextToClipboard(wordsText)
2170
2171
2172    def OnResultListPreview(self, evt):
2173        lpOp = self._buildListPagesOperation()
2174
2175        if lpOp is None:
2176            return
2177
2178        sarOp = SearchReplaceOperation()
2179        sarOp.listWikiPagesOp = lpOp
2180
2181        self.SetCursor(wx.HOURGLASS_CURSOR)
2182        self.Freeze()
2183        try:
2184            words = self.mainControl.getWikiDocument().searchWiki(sarOp)
2185           
2186            self.ctrls.lbResultPreview.Clear()
2187            self.ctrls.lbResultPreview.AppendItems(words)
2188               
2189            self.resultListData = words
2190        finally:
2191            self.Thaw()
2192            self.SetCursor(wx.NullCursor)
2193
2194
2195
2196class SearchResultPresenterControl(wx.Panel):
2197    """
2198    Panel which can be added to presenter in main area panel as tab showing
2199    search results.
2200    """
2201    def __init__(self, presenter, mainControl, searchDialogParent, ID,
2202            srListBox=None):
2203        super(SearchResultPresenterControl, self).__init__(presenter, ID)
2204
2205        self.mainControl = mainControl
2206        self.presenter = presenter
2207        self.searchDialogParent = searchDialogParent
2208        self.sarOp = None
2209
2210        if srListBox is None:
2211            self.resultBox = SearchResultListBox(self, self.mainControl,
2212                    GUI_ID.htmllbPages)
2213        else:
2214            srListBox.Reparent(self)
2215            self.resultBox = srListBox
2216
2217        sizer = wx.BoxSizer(wx.VERTICAL)
2218        sizer.Add(self.resultBox, 1, wx.EXPAND)
2219
2220
2221        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
2222
2223        self.btnAsResultlist = wx.Button(self,
2224                GUI_ID.CMD_SEARCH_AS_RESULTLIST, label=_(u"As Resultlist"))
2225                # TODO Allow hotkey for button
2226        buttonSizer.Add(self.btnAsResultlist, 0, wx.EXPAND)
2227
2228        self.btnAsWwSearch = wx.Button(self, GUI_ID.CMD_SEARCH_AS_WWSEARCH,
2229                label=_(u"As Full Search"))    # TODO Allow hotkey for button
2230        buttonSizer.Add(self.btnAsWwSearch, 0, wx.EXPAND)
2231
2232#         buttonSizer.AddStretchSpacer()
2233        buttonSizer.Add((0, 0), 1)
2234
2235        res = wx.xrc.XmlResource.Get()
2236        self.tabContextMenu = res.LoadMenu("MenuSearchResultTabPopup")
2237       
2238
2239        sizer.Add(buttonSizer, 0, wx.EXPAND)
2240
2241        self.SetSizer(sizer)
2242
2243        wx.EVT_BUTTON(self, GUI_ID.CMD_SEARCH_AS_RESULTLIST, self.OnCmdAsResultlist)
2244        wx.EVT_BUTTON(self, GUI_ID.CMD_SEARCH_AS_WWSEARCH, self.OnCmdAsWwSearch)
2245
2246        wx.EVT_MENU(self.tabContextMenu, GUI_ID.CMD_SEARCH_AS_RESULTLIST, self.OnCmdAsResultlist)
2247        wx.EVT_MENU(self.tabContextMenu, GUI_ID.CMD_SEARCH_AS_WWSEARCH, self.OnCmdAsWwSearch)
2248
2249
2250    # Next two to fulfill presenter subcontrol protocol
2251    def close(self):
2252        pass
2253
2254    def setLayerVisible(self, vis, scName):
2255        pass
2256
2257
2258    def setSearchOp(self, sarOp):
2259        """
2260        """
2261        self.sarOp = sarOp
2262        self.presenter.setTitle(_(u"<Search: %s>") % self.sarOp.searchStr)
2263
2264
2265    def getTabContextMenu(self):
2266        return self.tabContextMenu
2267
2268
2269    def OnCmdAsResultlist(self, evt):
2270        self.mainControl.getMainAreaPanel().detachPresenterTab(self.presenter)
2271
2272        frame = FastSearchPopup(self.searchDialogParent, self.mainControl,
2273                -1, srListBox=self.resultBox)
2274
2275        self.mainControl.nonModalWwSearchDlgs.append(frame)
2276        frame.setSearchOp(self.sarOp)
2277        frame.fixate()
2278        frame.Show()
2279
2280        self.presenter.close()
2281        self.presenter.Destroy()
2282
2283
2284    def OnCmdAsWwSearch(self, evt):
2285        self.mainControl.getMainAreaPanel().detachPresenterTab(self.presenter)
2286
2287        dlg = SearchWikiDialog(self.searchDialogParent, self.mainControl, -1,
2288                srListBox=self.resultBox, allowOkCancel=False,
2289                allowOrdering=False)
2290        dlg.showSearchReplaceOperation(self.sarOp)
2291
2292        self.mainControl.nonModalWwSearchDlgs.append(dlg)
2293        dlg.Show()
2294
2295        self.presenter.close()
2296        self.presenter.Destroy()
2297
2298#         # Set focus to dialog (hackish)
2299#         wx.CallLater(100, dlg.SetFocus)
2300
2301
2302
2303
2304class FastSearchPopup(wx.Frame):
2305    """
2306    Popup window which appears when hitting Enter in the fast search text field
2307    in the main window.
2308    Using frame because wx.PopupWindow is not available on Mac OS
2309    """
2310    def __init__(self, parent, mainControl, ID, srListBox=None,
2311            pos=wx.DefaultPosition):
2312        wx.Frame.__init__(self, parent, ID, _(u"Fast Search"), pos=pos,
2313                style=wx.RESIZE_BORDER | wx.FRAME_FLOAT_ON_PARENT | wx.SYSTEM_MENU |
2314                wx.FRAME_TOOL_WINDOW | wx.CAPTION | wx.CLOSE_BOX ) # wx.FRAME_NO_TASKBAR)
2315               
2316        self.mainControl = mainControl
2317        self.sarOp = None
2318        self.fixed = False  # if window was moved, fix it to not close automatically
2319
2320        if srListBox is None:
2321            self.resultBox = SearchResultListBox(self, self.mainControl,
2322                    GUI_ID.htmllbPages)
2323        else:
2324            srListBox.Reparent(self)
2325            self.resultBox = srListBox
2326
2327        sizer = wx.BoxSizer(wx.VERTICAL)
2328        sizer.Add(self.resultBox, 1, wx.EXPAND)
2329
2330
2331        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
2332
2333        self.btnAsWwSearch = wx.Button(self, GUI_ID.CMD_SEARCH_AS_WWSEARCH,
2334                label=_(u"As Full Search"))    # TODO Allow hotkey for button
2335        buttonSizer.Add(self.btnAsWwSearch, 0, wx.EXPAND)
2336
2337        self.btnAsTab = wx.Button(self, GUI_ID.CMD_SEARCH_AS_TAB,
2338                label=_(u"As Tab"))    # TODO Allow hotkey for button
2339        buttonSizer.Add(self.btnAsTab, 0, wx.EXPAND)
2340#         buttonSizer.AddStretchSpacer()
2341        buttonSizer.Add((0, 0), 1)
2342
2343        sizer.Add(buttonSizer, 0, wx.EXPAND)
2344
2345        self.SetSizer(sizer)
2346
2347        config = self.mainControl.getConfig()
2348        width = config.getint("main", "fastSearch_sizeX", 200)
2349        height = config.getint("main", "fastSearch_sizeY", 400)
2350
2351        setWindowSize(self, (width, height))
2352        setWindowPos(self, fullVisible=True)
2353
2354        # Fixes focus bug under Linux
2355        self.resultBox.SetFocus()
2356       
2357        self.resultBox.getMiscEvent().addListener(self)
2358
2359#         self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button)
2360
2361        self.resultBox.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
2362        self.btnAsWwSearch.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
2363        self.btnAsTab.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
2364        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
2365#         wx.EVT_KILL_FOCUS(self.resultBox, self.OnKillFocus)
2366        wx.EVT_BUTTON(self, GUI_ID.CMD_SEARCH_AS_WWSEARCH, self.OnCmdAsWwSearch)
2367        wx.EVT_BUTTON(self, GUI_ID.CMD_SEARCH_AS_TAB, self.OnCmdAsTab)
2368        wx.EVT_CLOSE(self, self.OnClose)
2369        wx.EVT_KEY_DOWN(self.resultBox, self.OnKeyDown)
2370#         wx.EVT_LEFT_DOWN(self, self.OnLeftDown)
2371       
2372        # To avoid unwanted move events (resulting in calls to fixate)     
2373        wx.CallAfter(self.Bind, wx.EVT_MOVE, self.OnMove)
2374
2375
2376    def fixate(self):
2377        if self.fixed:
2378            return
2379       
2380        self.resultBox.Unbind(wx.EVT_KILL_FOCUS)
2381        self.btnAsWwSearch.Unbind(wx.EVT_KILL_FOCUS)
2382        self.Unbind(wx.EVT_MOVE)
2383        self.SetTitle(_(u"Search: %s") % self.sarOp.searchStr)
2384        self.fixed = True
2385       
2386
2387    def OnMove(self, evt):
2388        evt.Skip()
2389        self.fixate()
2390
2391
2392    def getResultListPositionTuple(self):
2393        return getRelativePositionTupleToAncestor(self.resultBox, self)
2394
2395
2396    def OnCmdAsWwSearch(self, evt):
2397        self.Hide()
2398        self.fixate()
2399
2400        ownPos = self.GetPositionTuple()
2401        oldRelBoxPos = self.getResultListPositionTuple()
2402
2403        dlg = SearchWikiDialog(self.GetParent(), self.mainControl, -1,
2404                srListBox=self.resultBox, allowOkCancel=False,
2405                allowOrdering=False)
2406        dlg.showSearchReplaceOperation(self.sarOp)
2407
2408        newRelBoxPos = dlg.getResultListPositionTuple()
2409
2410        # A bit math to ensure that result list in both windows is placed
2411        # at same position (looks more cool)
2412        otherPos = (ownPos[0] + oldRelBoxPos[0] - newRelBoxPos[0],
2413                ownPos[1] + oldRelBoxPos[1] - newRelBoxPos[1])
2414
2415        setWindowPos(dlg, pos=otherPos, fullVisible=True)
2416        self.mainControl.nonModalWwSearchDlgs.append(dlg)
2417        self.Close()
2418        dlg.Show()
2419
2420        # Set focus to dialog (hackish)
2421        wx.CallLater(100, dlg.SetFocus)
2422
2423
2424    def OnCmdAsTab(self, evt):
2425        self.Hide()
2426        self.fixate()
2427
2428        maPanel = self.mainControl.getMainAreaPanel()
2429        presenter = LayeredControlPanel(maPanel)
2430        subCtl = SearchResultPresenterControl(presenter, self.mainControl,
2431                self.GetParent(), -1, srListBox=self.resultBox)
2432        presenter.setSubControl("search result list", subCtl)
2433        presenter.switchSubControl("search result list")
2434        maPanel.appendPresenterTab(presenter)
2435        subCtl.setSearchOp(self.sarOp)
2436
2437        maPanel.showPresenter(presenter)
2438        self.Close()
2439       
2440   
2441    def miscEventHappened(self, miscevt):
2442        if miscevt.getSource() is self.resultBox and miscevt.has_key(
2443                "opened in foreground"):
2444
2445            if not self.fixed:
2446                self.Close()
2447
2448       
2449    def displayErrorMessage(self, errorStr, e=u""):
2450        """
2451        Pops up an error dialog box
2452        """
2453        wx.MessageBox(uniToGui(u"%s. %s." % (errorStr, e)), u"Error!",
2454            wx.OK, self)
2455
2456
2457    def OnKeyDown(self, evt):
2458        accP = getAccelPairFromKeyDown(evt)
2459
2460        if accP == (wx.ACCEL_NORMAL, wx.WXK_ESCAPE):
2461            self.Close()
2462        else:
2463            evt.Skip()
2464
2465
2466    # def OnKillFocus(self, evt):
2467
2468    # TODO What about Mac?
2469    if isLinux():
2470        def OnKillFocus(self, evt):
2471            evt.Skip()
2472           
2473            if self.resultBox.contextMenuSelection == -2 and \
2474                    not wx.Window.FindFocus() in \
2475                    (None, self.resultBox, self.btnAsWwSearch, self.btnAsTab):
2476                # Close only if context menu is not open
2477                # otherwise crashes on GTK
2478                self.Close()
2479    else:
2480        def OnKillFocus(self, evt):
2481            evt.Skip()
2482            if not wx.Window.FindFocus() in (self.resultBox, self.btnAsWwSearch,
2483                    self.btnAsTab):
2484                self.Close()
2485
2486
2487    def OnClose(self, evt):
2488        if not self.fixed:
2489            width, height = self.GetSizeTuple()
2490            config = self.mainControl.getConfig()
2491            config.set("main", "fastSearch_sizeX", str(width))
2492            config.set("main", "fastSearch_sizeY", str(height))
2493       
2494        try:
2495            self.mainControl.nonModalWwSearchDlgs.remove(self)
2496        except ValueError:
2497            pass
2498
2499        evt.Skip()
2500
2501
2502    def _buildSearchReplaceOperation(self, searchText):
2503        config = self.mainControl.getConfig()
2504       
2505        searchType = config.getint("main", "fastSearch_searchType")
2506
2507        # TODO Make configurable
2508        sarOp = SearchReplaceOperation()
2509        sarOp.searchStr = stripSearchString(searchText)
2510        sarOp.booleanOp = searchType == Consts.SEARCHTYPE_BOOLEANREGEX
2511        sarOp.caseSensitive = config.getboolean("main",
2512                "fastSearch_caseSensitive")
2513        sarOp.wholeWord = config.getboolean("main", "fastSearch_wholeWord")
2514        sarOp.cycleToStart = False
2515        sarOp.wildCard = 'regex' if searchType != Consts.SEARCHTYPE_ASIS else 'no'
2516        sarOp.wikiWide = True
2517
2518        return sarOp
2519
2520
2521    def runSearchOnWiki(self, text):
2522        self.setSearchOp(self._buildSearchReplaceOperation(text))
2523        try:
2524            self._refreshPageList()
2525        except UserAbortException:
2526            return
2527        except re.error, e:
2528            self.displayErrorMessage(_(u'Error in regular expression'),
2529                    _(unicode(e)))
2530        except ParseException, e:
2531            self.displayErrorMessage(_(u'Error in boolean expression'),
2532                    _(unicode(e)))
2533        except DbReadAccessError, e:
2534            self.displayErrorMessage(_(u'Error. Maybe wiki rebuild is needed'),
2535                    _(unicode(e)))
2536
2537
2538    def setSearchOp(self, sarOp):
2539        """
2540        """
2541        self.sarOp = sarOp
2542
2543
2544#     def _refreshPageList(self):
2545#         self.resultBox.showSearching()
2546#         self.SetCursor(wx.HOURGLASS_CURSOR)
2547#         self.Freeze()
2548#         try:
2549#             # self.mainControl.saveCurrentDocPage()
2550#     
2551#             if len(self.sarOp.searchStr) > 0:
2552#                 self.foundPages = self.mainControl.getWikiDocument().searchWiki(self.sarOp)
2553#                 self.mainControl.getCollator().sort(self.foundPages)
2554#                 self.resultBox.showFound(self.sarOp, self.foundPages,
2555#                         self.mainControl.getWikiDocument())
2556#             else:
2557#                 self.foundPages = []
2558#                 self.resultBox.showFound(None, None, None)
2559#
2560#             self.listNeedsRefresh = False
2561#
2562#         finally:
2563#             self.Thaw()
2564#             self.SetCursor(wx.NullCursor)
2565#             self.resultBox.ensureNotShowSearching()
2566
2567
2568    def _refreshPageList(self):
2569        if len(self.sarOp.searchStr) == 0:
2570            self.foundPages = []
2571            self.resultBox.showFound(None, None, None)
2572
2573            self.listNeedsRefresh = False
2574            return
2575
2576        disableSet = wxHelper.getAllChildWindows(self)
2577        disableSet.difference_update(wxHelper.getWindowParentsUpTo(
2578                self.resultBox, self))
2579        disableSet = set(win for win in disableSet if win.IsEnabled())
2580
2581        self.resultBox.showSearching()
2582        self.SetCursor(wx.HOURGLASS_CURSOR)
2583#         self.Freeze()
2584
2585        if self.mainControl.configuration.getboolean("main",
2586                        "search_dontAllowCancel"):
2587            threadstop = DUMBTHREADSTOP
2588        else:
2589            threadstop = FunctionThreadStop(self._searchPoll)
2590
2591        self.searchingStartTime = time.time()
2592        for win in disableSet:
2593            win.Disable()
2594        try:
2595            self.foundPages = self.mainControl.getWikiDocument().searchWiki(
2596                    self.sarOp, threadstop=threadstop)
2597            self.mainControl.getCollator().sort(self.foundPages)
2598            self.resultBox.showFound(self.sarOp, self.foundPages,
2599                    self.mainControl.getWikiDocument(),
2600                    threadstop=threadstop)
2601
2602            self.listNeedsRefresh = False
2603
2604        except NotCurrentThreadException:
2605            raise UserAbortException()
2606        finally:
2607            self.searchingStartTime = None
2608            for win in disableSet:
2609                win.Enable()
2610   
2611    #         self.Thaw()
2612            self.SetCursor(wx.NullCursor)
2613            self.resultBox.ensureNotShowSearching()
2614
2615
2616    def _searchPoll(self):
2617        return wx.SafeYield(self, True) and self.searchingStartTime is not None
2618
2619    def stopSearching(self):
2620        self.searchingStartTime = None
2621
2622
2623
2624
2625
2626_CONTEXT_MENU_ACTIVATE = \
2627u"""
2628Activate;CMD_ACTIVATE_THIS
2629Activate New Tab;CMD_ACTIVATE_NEW_TAB_THIS
2630Activate New Tab Backgrd.;CMD_ACTIVATE_NEW_TAB_BACKGROUND_THIS
2631"""
2632
2633# Entries to support i18n of context menus
2634if False:
2635    N_(u"Activate")
2636    N_(u"Activate New Tab")
2637    N_(u"Activate New Tab Backgrd.")
Note: See TracBrowser for help on using the repository browser.