editors/CodeFileEditor.py
changeset 1784 64beb9e9c749
parent 1782 5b6ad7a7fd9d
child 1834 cd42b426028b
equal deleted inserted replaced
1729:31e63e25b4cc 1784:64beb9e9c749
    35 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
    35 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
    36 from util.BitmapLibrary import GetBitmap
    36 from util.BitmapLibrary import GetBitmap
    37 from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS
    37 from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS
    38 from controls.VariablePanel import VARIABLE_NAME_SUFFIX_MODEL
    38 from controls.VariablePanel import VARIABLE_NAME_SUFFIX_MODEL
    39 from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
    39 from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
       
    40 from util.TranslationCatalogs import NoTranslate
    40 
    41 
    41 [STC_CODE_ERROR, STC_CODE_SEARCH_RESULT,
    42 [STC_CODE_ERROR, STC_CODE_SEARCH_RESULT,
    42  STC_CODE_SECTION] = range(15, 18)
    43  STC_CODE_SECTION] = range(15, 18)
    43 
    44 
    44 HIGHLIGHT_TYPES = {
    45 HIGHLIGHT_TYPES = {
    46     SEARCH_RESULT_HIGHLIGHT: STC_CODE_SEARCH_RESULT,
    47     SEARCH_RESULT_HIGHLIGHT: STC_CODE_SEARCH_RESULT,
    47 }
    48 }
    48 
    49 
    49 EDGE_COLUMN = 80
    50 EDGE_COLUMN = 80
    50 
    51 
       
    52 
    51 class CodeEditor(CustomStyledTextCtrl):
    53 class CodeEditor(CustomStyledTextCtrl):
    52 
    54 
    53     KEYWORDS = []
    55     KEYWORDS = []
    54     COMMENT_HEADER = ""
    56     COMMENT_HEADER = ""
    55 
    57 
    56     def __init__(self, parent, window, controler):
    58     def __init__(self, parent, window, controler):
    57         CustomStyledTextCtrl.__init__(self, parent, -1, wx.DefaultPosition,
    59         CustomStyledTextCtrl.__init__(self, parent, -1, wx.DefaultPosition,
    58                  wx.Size(-1, 300), 0)
    60                                       wx.Size(-1, 300), 0)
    59 
    61 
    60         self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
    62         self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
    61         self.SetMarginWidth(1, 25)
    63         self.SetMarginWidth(1, 25)
    62 
    64 
    63         self.SetProperty("fold", "1")
    65         self.SetProperty("fold", "1")
    64         self.SetProperty("tab.timmy.whinge.level", "1")
    66         self.SetProperty("tab.timmy.whinge.level", "1")
    65         self.SetMargins(0,0)
    67         self.SetMargins(0, 0)
    66 
    68 
    67         self.SetViewWhiteSpace(False)
    69         self.SetViewWhiteSpace(False)
    68 
    70 
    69         self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
    71         self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
    70         self.SetEdgeColumn(EDGE_COLUMN)
    72         self.SetEdgeColumn(EDGE_COLUMN)
   131 
   133 
   132         self.SectionsComments = {}
   134         self.SectionsComments = {}
   133         for section in self.Controler.SECTIONS_NAMES:
   135         for section in self.Controler.SECTIONS_NAMES:
   134             section_comment = " %s section " % (section)
   136             section_comment = " %s section " % (section)
   135             len_headers = EDGE_COLUMN - len(section_comment)
   137             len_headers = EDGE_COLUMN - len(section_comment)
   136             section_comment = self.COMMENT_HEADER * (len_headers / 2) + \
   138             section_comment = \
   137                               section_comment + \
   139                 self.COMMENT_HEADER * (len_headers / 2) + \
   138                               self.COMMENT_HEADER * (len_headers - len_headers / 2)
   140                 section_comment + \
       
   141                 self.COMMENT_HEADER * (len_headers - len_headers / 2)
   139 
   142 
   140             self.SectionsComments[section] = {
   143             self.SectionsComments[section] = {
   141                  "comment": section_comment,
   144                  "comment": section_comment,
   142             }
   145             }
   143 
   146 
   150                 section_end = "$"
   153                 section_end = "$"
   151             section_infos["pattern"] = re.compile(
   154             section_infos["pattern"] = re.compile(
   152                 section_infos["comment"] + "(.*)" +
   155                 section_infos["comment"] + "(.*)" +
   153                 section_end, re.DOTALL)
   156                 section_end, re.DOTALL)
   154 
   157 
   155         self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|wx.stc.STC_MOD_BEFOREDELETE)
   158         self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT | wx.stc.STC_MOD_BEFOREDELETE)
   156 
   159 
   157         self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop)
   160         self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop)
   158         self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
   161         self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
   159         self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification)
   162         self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification)
   160 
   163 
   168         self.CurrentFindHighlight = None
   171         self.CurrentFindHighlight = None
   169 
   172 
   170     def OnModification(self, event):
   173     def OnModification(self, event):
   171         if not self.DisableEvents:
   174         if not self.DisableEvents:
   172             mod_type = event.GetModificationType()
   175             mod_type = event.GetModificationType()
   173             if not (mod_type&wx.stc.STC_PERFORMED_UNDO or mod_type&wx.stc.STC_PERFORMED_REDO):
   176             if not (mod_type & wx.stc.STC_PERFORMED_UNDO or mod_type & wx.stc.STC_PERFORMED_REDO):
   174                 if mod_type&wx.stc.STC_MOD_BEFOREINSERT:
   177                 if mod_type & wx.stc.STC_MOD_BEFOREINSERT:
   175                     if self.CurrentAction == None:
   178                     if self.CurrentAction is None:
   176                         self.StartBuffering()
   179                         self.StartBuffering()
   177                     elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1:
   180                     elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1:
   178                         self.Controler.EndBuffering()
   181                         self.Controler.EndBuffering()
   179                         self.StartBuffering()
   182                         self.StartBuffering()
   180                     self.CurrentAction = ("Add", event.GetPosition())
   183                     self.CurrentAction = ("Add", event.GetPosition())
   181                     wx.CallAfter(self.RefreshModel)
   184                     wx.CallAfter(self.RefreshModel)
   182                 elif mod_type&wx.stc.STC_MOD_BEFOREDELETE:
   185                 elif mod_type & wx.stc.STC_MOD_BEFOREDELETE:
   183                     if self.CurrentAction == None:
   186                     if self.CurrentAction is None:
   184                         self.StartBuffering()
   187                         self.StartBuffering()
   185                     elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1:
   188                     elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1:
   186                         self.Controler.EndBuffering()
   189                         self.Controler.EndBuffering()
   187                         self.StartBuffering()
   190                         self.StartBuffering()
   188                     self.CurrentAction = ("Delete", event.GetPosition())
   191                     self.CurrentAction = ("Delete", event.GetPosition())
   191         event.Skip()
   194         event.Skip()
   192 
   195 
   193     def OnDoDrop(self, event):
   196     def OnDoDrop(self, event):
   194         try:
   197         try:
   195             values = eval(event.GetDragText())
   198             values = eval(event.GetDragText())
   196         except:
   199         except Exception:
   197             values = event.GetDragText()
   200             values = event.GetDragText()
   198         if isinstance(values, tuple):
   201         if isinstance(values, tuple):
   199             message = None
   202             message = None
   200             if values[3] == self.Controler.GetCurrentLocation():
   203             if values[3] == self.Controler.GetCurrentLocation():
   201                 self.ResetBuffer()
   204                 self.ResetBuffer()
   224             self.ParentWindow.RefreshFileMenu()
   227             self.ParentWindow.RefreshFileMenu()
   225             self.ParentWindow.RefreshEditMenu()
   228             self.ParentWindow.RefreshEditMenu()
   226             self.ParentWindow.RefreshPageTitles()
   229             self.ParentWindow.RefreshPageTitles()
   227 
   230 
   228     def ResetBuffer(self):
   231     def ResetBuffer(self):
   229         if self.CurrentAction != None:
   232         if self.CurrentAction is not None:
   230             self.Controler.EndBuffering()
   233             self.Controler.EndBuffering()
   231             self.CurrentAction = None
   234             self.CurrentAction = None
   232 
   235 
   233     def GetCodeText(self):
   236     def GetCodeText(self):
   234         parts = self.Controler.GetTextParts()
   237         parts = self.Controler.GetTextParts()
   256         new_text = self.GetCodeText()
   259         new_text = self.GetCodeText()
   257         if old_text != new_text:
   260         if old_text != new_text:
   258             self.SetText(new_text)
   261             self.SetText(new_text)
   259             new_cursor_pos = GetCursorPos(old_text, new_text)
   262             new_cursor_pos = GetCursorPos(old_text, new_text)
   260             self.LineScroll(column, line)
   263             self.LineScroll(column, line)
   261             if new_cursor_pos != None:
   264             if new_cursor_pos is not None:
   262                 self.GotoPos(new_cursor_pos)
   265                 self.GotoPos(new_cursor_pos)
   263             else:
   266             else:
   264                 self.GotoPos(old_cursor_pos)
   267                 self.GotoPos(old_cursor_pos)
   265             self.EmptyUndoBuffer()
   268             self.EmptyUndoBuffer()
   266         self.DisableEvents = False
   269         self.DisableEvents = False
   379                 braceAtCaret = caretPos
   382                 braceAtCaret = caretPos
   380 
   383 
   381         if braceAtCaret >= 0:
   384         if braceAtCaret >= 0:
   382             braceOpposite = self.BraceMatch(braceAtCaret)
   385             braceOpposite = self.BraceMatch(braceAtCaret)
   383 
   386 
   384         if braceAtCaret != -1  and braceOpposite == -1:
   387         if braceAtCaret != -1 and braceOpposite == -1:
   385             self.BraceBadLight(braceAtCaret)
   388             self.BraceBadLight(braceAtCaret)
   386         else:
   389         else:
   387             self.BraceHighlight(braceAtCaret, braceOpposite)
   390             self.BraceHighlight(braceAtCaret, braceOpposite)
   388 
   391 
   389         selected_text = self.GetSelectedText()
   392         selected_text = self.GetSelectedText()
   440 
   443 
   441                     if lastChild > lineNum:
   444                     if lastChild > lineNum:
   442                         self.HideLines(lineNum+1, lastChild)
   445                         self.HideLines(lineNum+1, lastChild)
   443 
   446 
   444             lineNum = lineNum + 1
   447             lineNum = lineNum + 1
   445 
       
   446 
       
   447 
   448 
   448     def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
   449     def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
   449         lastChild = self.GetLastChild(line, level)
   450         lastChild = self.GetLastChild(line, level)
   450         line = line + 1
   451         line = line + 1
   451 
   452 
   546         else:
   547         else:
   547             if self.CurrentFindHighlight is not None:
   548             if self.CurrentFindHighlight is not None:
   548                 self.RemoveHighlight(*self.CurrentFindHighlight)
   549                 self.RemoveHighlight(*self.CurrentFindHighlight)
   549             self.CurrentFindHighlight = None
   550             self.CurrentFindHighlight = None
   550 
   551 
   551 #-------------------------------------------------------------------------------
   552     # -------------------------------------------------------------------------------
   552 #                        Highlights showing functions
   553     #                        Highlights showing functions
   553 #-------------------------------------------------------------------------------
   554     # -------------------------------------------------------------------------------
   554 
   555 
   555     def OnRefreshHighlightsTimer(self, event):
   556     def OnRefreshHighlightsTimer(self, event):
   556         self.RefreshView(True)
   557         self.RefreshView(True)
   557         event.Skip()
   558         event.Skip()
   558 
   559 
   573             self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
   574             self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
   574             self.RefreshView()
   575             self.RefreshView()
   575 
   576 
   576     def RemoveHighlight(self, start, end, highlight_type):
   577     def RemoveHighlight(self, start, end, highlight_type):
   577         highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
   578         highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
   578         if (highlight_type is not None and
   579         if highlight_type is not None and \
   579             (start, end, highlight_type) in self.Highlights):
   580            (start, end, highlight_type) in self.Highlights:
   580             self.Highlights.remove((start, end, highlight_type))
   581             self.Highlights.remove((start, end, highlight_type))
   581             self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
   582             self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
   582 
   583 
   583     def ShowHighlights(self):
   584     def ShowHighlights(self):
   584         for start, end, highlight_type in self.Highlights:
   585         for start, end, highlight_type in self.Highlights:
   594             self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type)
   595             self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type)
   595             self.StartStyling(highlight_end_pos, 0x00)
   596             self.StartStyling(highlight_end_pos, 0x00)
   596             self.SetStyling(len(self.GetText()) - highlight_end_pos, stc.STC_STYLE_DEFAULT)
   597             self.SetStyling(len(self.GetText()) - highlight_end_pos, stc.STC_STYLE_DEFAULT)
   597 
   598 
   598 
   599 
   599 #-------------------------------------------------------------------------------
   600 # -------------------------------------------------------------------------------
   600 #                         Helper for VariablesGrid values
   601 #                         Helper for VariablesGrid values
   601 #-------------------------------------------------------------------------------
   602 # -------------------------------------------------------------------------------
   602 
   603 
   603 class VariablesTable(CustomTable):
   604 class VariablesTable(CustomTable):
   604 
   605 
   605     def GetValue(self, row, col):
   606     def GetValue(self, row, col):
   606         if row < self.GetNumberRows():
   607         if row < self.GetNumberRows():
   658                 ("AddVariableButton", "add_element", _("Add variable")),
   659                 ("AddVariableButton", "add_element", _("Add variable")),
   659                 ("DeleteVariableButton", "remove_element", _("Remove variable")),
   660                 ("DeleteVariableButton", "remove_element", _("Remove variable")),
   660                 ("UpVariableButton", "up", _("Move variable up")),
   661                 ("UpVariableButton", "up", _("Move variable up")),
   661                 ("DownVariableButton", "down", _("Move variable down"))]:
   662                 ("DownVariableButton", "down", _("Move variable down"))]:
   662             button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
   663             button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
   663                   size=wx.Size(28, 28), style=wx.NO_BORDER)
   664                                                     size=wx.Size(28, 28), style=wx.NO_BORDER)
   664             button.SetToolTipString(help)
   665             button.SetToolTipString(help)
   665             setattr(self, name, button)
   666             setattr(self, name, button)
   666             controls_sizer.AddWindow(button, border=5, flag=wx.BOTTOM)
   667             controls_sizer.AddWindow(button, border=5, flag=wx.BOTTOM)
   667 
   668 
   668         self.VariablesGrid = CustomGrid(self, style=wx.VSCROLL)
   669         self.VariablesGrid = CustomGrid(self, style=wx.VSCROLL)
   674         self.SetSizer(main_sizer)
   675         self.SetSizer(main_sizer)
   675 
   676 
   676         self.ParentWindow = window
   677         self.ParentWindow = window
   677         self.Controler = controler
   678         self.Controler = controler
   678 
   679 
   679         self.VariablesDefaultValue = {"Name" : "", "Type" : DefaultType, "Initial": "",
   680         self.VariablesDefaultValue = {
   680                                       "Description":"", "OnChange":"", "Options":""}
   681             "Name":        "",
       
   682             "Type":        DefaultType,
       
   683             "Initial":     "",
       
   684             "Description": "",
       
   685             "OnChange":    "",
       
   686             "Options":     ""
       
   687         }
   681         self.Table = VariablesTable(self, [], self.GetVariableTableColnames())
   688         self.Table = VariablesTable(self, [], self.GetVariableTableColnames())
   682         self.ColAlignements = [wx.ALIGN_RIGHT] +  \
   689         self.ColAlignements = [wx.ALIGN_RIGHT] +  \
   683                               [wx.ALIGN_LEFT]*(len(self.VariablesDefaultValue))
   690                               [wx.ALIGN_LEFT]*(len(self.VariablesDefaultValue))
   684         self.ColSizes = [20, 150] + [130]*(len(self.VariablesDefaultValue)-1)
   691         self.ColSizes = [20, 150] + [130]*(len(self.VariablesDefaultValue)-1)
   685         self.VariablesGrid.SetTable(self.Table)
   692         self.VariablesGrid.SetTable(self.Table)
   733             self.VariablesGrid.SetColAttr(col, attr)
   740             self.VariablesGrid.SetColAttr(col, attr)
   734             self.VariablesGrid.SetColSize(col, self.ColSizes[col])
   741             self.VariablesGrid.SetColSize(col, self.ColSizes[col])
   735         self.Table.ResetView(self.VariablesGrid)
   742         self.Table.ResetView(self.VariablesGrid)
   736 
   743 
   737     def GetVariableTableColnames(self):
   744     def GetVariableTableColnames(self):
   738         _ = lambda x : x
   745         _ = NoTranslate
   739     	return ["#", _("Name"),_("Type"), _("Initial"), _("Description"), _("OnChange"), _("Options")]
   746         return ["#",
       
   747                 _("Name"),
       
   748                 _("Type"),
       
   749                 _("Initial"),
       
   750                 _("Description"),
       
   751                 _("OnChange"),
       
   752                 _("Options")]
   740 
   753 
   741     def RefreshModel(self):
   754     def RefreshModel(self):
   742         self.Controler.SetVariables(self.Table.GetData())
   755         self.Controler.SetVariables(self.Table.GetData())
   743         self.RefreshBuffer()
   756         self.RefreshBuffer()
   744 
   757 
   757 
   770 
   758     def DoGetBestSize(self):
   771     def DoGetBestSize(self):
   759         return self.ParentWindow.GetPanelBestSize()
   772         return self.ParentWindow.GetPanelBestSize()
   760 
   773 
   761     def ShowErrorMessage(self, message):
   774     def ShowErrorMessage(self, message):
   762         dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR)
   775         dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR)
   763         dialog.ShowModal()
   776         dialog.ShowModal()
   764         dialog.Destroy()
   777         dialog.Destroy()
   765 
   778 
   766     def OnVariablesGridCellChange(self, event):
   779     def OnVariablesGridCellChange(self, event):
   767         row, col = event.GetRow(), event.GetCol()
   780         row, col = event.GetRow(), event.GetCol()
   829         if event.GetCol() == 0:
   842         if event.GetCol() == 0:
   830             row = event.GetRow()
   843             row = event.GetRow()
   831             data_type = self.Table.GetValueByName(row, "Type")
   844             data_type = self.Table.GetValueByName(row, "Type")
   832             var_name = self.Table.GetValueByName(row, "Name")
   845             var_name = self.Table.GetValueByName(row, "Name")
   833             data = wx.TextDataObject(str((var_name, "Global", data_type,
   846             data = wx.TextDataObject(str((var_name, "Global", data_type,
   834                     self.Controler.GetCurrentLocation())))
   847                                           self.Controler.GetCurrentLocation())))
   835             dragSource = wx.DropSource(self.VariablesGrid)
   848             dragSource = wx.DropSource(self.VariablesGrid)
   836             dragSource.SetData(data)
   849             dragSource.SetData(data)
   837             dragSource.DoDragDrop()
   850             dragSource.DoDragDrop()
   838             return
   851             return
   839         event.Skip()
   852         event.Skip()
   840 
   853 
   841 
   854 
   842 #-------------------------------------------------------------------------------
   855 # -------------------------------------------------------------------------------
   843 #                          CodeFileEditor Main Frame Class
   856 #                          CodeFileEditor Main Frame Class
   844 #-------------------------------------------------------------------------------
   857 # -------------------------------------------------------------------------------
   845 
   858 
   846 class CodeFileEditor(ConfTreeNodeEditor):
   859 class CodeFileEditor(ConfTreeNodeEditor):
   847 
   860 
   848     CONFNODEEDITOR_TABS = []
   861     CONFNODEEDITOR_TABS = []
   849     CODE_EDITOR = None
   862     CODE_EDITOR = None
   851     def _create_CodePanel(self, prnt):
   864     def _create_CodePanel(self, prnt):
   852         self.CodeEditorPanel = wx.SplitterWindow(prnt)
   865         self.CodeEditorPanel = wx.SplitterWindow(prnt)
   853         self.CodeEditorPanel.SetMinimumPaneSize(1)
   866         self.CodeEditorPanel.SetMinimumPaneSize(1)
   854 
   867 
   855         self.VariablesPanel = VariablesEditor(self.CodeEditorPanel,
   868         self.VariablesPanel = VariablesEditor(self.CodeEditorPanel,
   856                 self.ParentWindow, self.Controler)
   869                                               self.ParentWindow,
       
   870                                               self.Controler)
   857 
   871 
   858         if self.CODE_EDITOR is not None:
   872         if self.CODE_EDITOR is not None:
   859             self.CodeEditor = self.CODE_EDITOR(self.CodeEditorPanel,
   873             self.CodeEditor = self.CODE_EDITOR(self.CodeEditorPanel,
   860                         self.ParentWindow, self.Controler)
   874                                                self.ParentWindow, self.Controler)
   861 
   875 
   862             self.CodeEditorPanel.SplitHorizontally(self.VariablesPanel,
   876             self.CodeEditorPanel.SplitHorizontally(self.VariablesPanel,
   863                     self.CodeEditor, 150)
   877                                                    self.CodeEditor, 150)
   864         else:
   878         else:
   865             self.CodeEditorPanel.Initialize(self.VariablesPanel)
   879             self.CodeEditorPanel.Initialize(self.VariablesPanel)
   866 
   880 
   867         return self.CodeEditorPanel
   881         return self.CodeEditorPanel
   868 
   882 
   888         self.VariablesPanel.RefreshView()
   902         self.VariablesPanel.RefreshView()
   889         self.CodeEditor.RefreshView()
   903         self.CodeEditor.RefreshView()
   890 
   904 
   891     def Find(self, direction, search_params):
   905     def Find(self, direction, search_params):
   892         self.CodeEditor.Find(direction, search_params)
   906         self.CodeEditor.Find(direction, search_params)
   893