Incremental find/replace pane with regex

Are you writing your own components and need help with how to set them up or have questions about the components you are deriving from ? Ask them here.
Post Reply
geralds
I live to help wx-kind
I live to help wx-kind
Posts: 186
Joined: Tue Nov 01, 2005 9:22 am
Contact:

Incremental find/replace pane with regex

Post by geralds » Mon Feb 26, 2007 9:02 pm

In case anyone else isn't 100% happy with the built-in find/replace dialog, this panel is intended as a drop-in replacement.

It uses the standard wxFindReplaceData class so you shouldn't need to change your main frame's find/replace handlers much. (There are some differences, e.g. you will need to distinguish between standard Find Next and an incremental match that's triggered by adding a character at the end, but the class offers get functions for the relevant information.)

The class should work with all wxStyledTextCtrl derivates, wxRichTextCtrl and multiline wxTextCtrls.

It's very rough around the edges, so please use with caution.

findreplacepanel.h

Code: Select all

#ifndef FINDREPLACEPANEL_H
#define FINDREPLACEPANEL_H

#include <wx/wx.h>
#include <iostream>
#include <fstream>

class wxFindReplaceData;

enum
{
  ID_FINDREPLACE_FIND_NEXT,
  ID_FINDREPLACE_REPLACE,
  ID_FINDREPLACE_REPLACE_ALL,
  ID_FINDREPLACE_MATCH_CASE,
  ID_FINDREPLACE_REGEX
};

class FindReplacePanel : public wxPanel
{
  public:
    FindReplacePanel(
      wxWindow *parent,
      int id,
      wxFindReplaceData *findDataParameter,
      bool isReplacePanel = true,
      bool isRegex = true);
    ~FindReplacePanel();
    void OnFindNext(wxCommandEvent& event);
    void OnReplace(wxCommandEvent& event);
    void OnReplaceAll(wxCommandEvent& event);
    void focusOnFind();
    bool getIncrementalFind();
    bool getRegex();
    void refresh();
    void setReplaceVisible(bool b);
    void setMatchCase(bool b);
    void setRegex(bool b);
    void flagNotFound(bool b);
    void enableButtons(bool b);
  private:
    wxTextCtrl *findEdit, *replaceEdit;
    wxStaticText *label1, *label2, *spacer1, *spacer2;
    wxButton *findNextButton, *replaceButton, *replaceAllButton;
    wxCheckBox *matchCaseBox, *regexBox;
    wxFindReplaceData *findData;
    wxBoxSizer *sizer;
    wxWindow *parent;
    size_t findEditLength;
    bool matchCaseMemory, regexMemory;
    bool incrementalFind, isReplaceDialog, notFoundSet, isRegex;

    void OnIdle(wxIdleEvent& event);
    void sendFindEvent(size_t flags);

    DECLARE_EVENT_TABLE()
};
#endif

findreplacepanel.cpp

Code: Select all

#include <wx/fdrepdlg.h>
#include "findreplacepanel.h"

BEGIN_EVENT_TABLE(FindReplacePanel, wxPanel)
  EVT_BUTTON(ID_FINDREPLACE_FIND_NEXT, FindReplacePanel::OnFindNext)
  EVT_BUTTON(ID_FINDREPLACE_REPLACE, FindReplacePanel::OnReplace)
  EVT_BUTTON(ID_FINDREPLACE_REPLACE_ALL, FindReplacePanel::OnReplaceAll)
  EVT_IDLE(FindReplacePanel::OnIdle)
END_EVENT_TABLE()

FindReplacePanel::FindReplacePanel(
  wxWindow *parentParameter,
  int id,
  wxFindReplaceData *findDataParameter,
  bool isReplacePanel,
  bool isRegexParameter) : wxPanel(parentParameter, id)
{
  parent = parentParameter;
  findData = findDataParameter;
  incrementalFind = notFoundSet = false;
  isRegex = isRegexParameter;

  matchCaseMemory = (findData->GetFlags()) & wxFR_MATCHCASE;
  regexMemory = isRegex;

  label1 = new wxStaticText(this, wxID_ANY, _("Find:"));
  spacer1 = new wxStaticText(this, wxID_ANY, _(" "));
  spacer2 = new wxStaticText(this, wxID_ANY, _(" "));

  int editWidth = 140;
  findEdit = new wxTextCtrl(
    this,
    ID_FINDREPLACE_FIND_NEXT,
    _T(""),
    wxDefaultPosition,
    wxSize(editWidth, -1)
  );
  findEdit->SetValue(findData->GetFindString());

  label2 = new wxStaticText(this, wxID_ANY, _("Replace with:"));
  replaceEdit = new wxTextCtrl(
    this,
    ID_FINDREPLACE_REPLACE,
    _T(""),
    wxDefaultPosition,
    wxSize(editWidth, -1));
  replaceEdit->SetValue(findData->GetReplaceString());

  findNextButton = new wxButton(
    this,
    ID_FINDREPLACE_FIND_NEXT,
    _("Find &Next"),
    wxDefaultPosition,
    wxDefaultSize,
    wxBU_EXACTFIT | wxNO_BORDER);
  replaceButton = new wxButton(
    this,
    ID_FINDREPLACE_REPLACE,
    _("&Replace"),
    wxDefaultPosition,
    wxDefaultSize,
    wxBU_EXACTFIT | wxNO_BORDER);
  replaceAllButton = new wxButton(
    this,
    ID_FINDREPLACE_REPLACE_ALL,
    _("Replace &All"),
    wxDefaultPosition,
    wxDefaultSize,
    wxBU_EXACTFIT | wxNO_BORDER);

  matchCaseBox = new wxCheckBox(
    this,
    ID_FINDREPLACE_MATCH_CASE,
    _("&Match case"));
  size_t flags = findData->GetFlags();
  matchCaseBox->SetValue(flags & wxFR_MATCHCASE);
 
  regexBox = new wxCheckBox(
    this,
    ID_FINDREPLACE_REGEX,
    _("Re&gex"));
   
  int sizerOffset = 2;
  sizer = new wxBoxSizer(wxHORIZONTAL);
  sizer->Add(label1, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  sizer->Add(findEdit, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  
  sizer->Add(label2, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  sizer->Add(replaceEdit, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  
  sizer->Add(spacer1, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  sizer->Add(findNextButton, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);

  sizer->Add(replaceButton, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  sizer->Add(replaceAllButton, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  
  sizer->Add(spacer2, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  sizer->Add(matchCaseBox, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);  
  sizer->Add(regexBox, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, sizerOffset);
  sizer->Layout();

  this->SetSizer(sizer);
  this->SetSize(-1, findNextButton->GetSize().GetHeight() + 10);

  findEditLength = findEdit->GetValue().Length();
  
  if (!isReplacePanel)
  {
    label2->Hide();
    replaceEdit->Hide();
    replaceButton->Hide();
    replaceAllButton->Hide();
  }

  refresh();
}

FindReplacePanel::~FindReplacePanel()
{
}

void FindReplacePanel::OnFindNext(wxCommandEvent& event)
{
  findData->SetFindString(findEdit->GetValue());
  findData->SetReplaceString(replaceEdit->GetValue());

  incrementalFind = false;
  size_t flags = 0;
  flags |= wxFR_DOWN;
  if (matchCaseBox->GetValue())
    flags |= wxFR_MATCHCASE;
  sendFindEvent(flags);
}

void FindReplacePanel::OnReplace(wxCommandEvent& event)
{
  wxFindDialogEvent replaceEvent(wxEVT_COMMAND_FIND_REPLACE, 0);
  replaceEvent.SetFlags(wxFR_DOWN);
  replaceEvent.SetFindString(findEdit->GetValue());
  replaceEvent.SetReplaceString(replaceEdit->GetValue());
  parent->ProcessEvent(replaceEvent);
}

void FindReplacePanel::OnReplaceAll(wxCommandEvent& event)
{
  wxFindDialogEvent replaceAllEvent(wxEVT_COMMAND_FIND_REPLACE_ALL, 0);
  replaceAllEvent.SetFlags(wxFR_DOWN);
  replaceAllEvent.SetFindString(findEdit->GetValue());
  replaceAllEvent.SetReplaceString(replaceEdit->GetValue());
  parent->ProcessEvent(replaceAllEvent);
}

void FindReplacePanel::focusOnFind()
{
  findEdit->SetFocus();  
}

void FindReplacePanel::OnIdle(wxIdleEvent& event)
{
  size_t newLength = findEdit->GetValue().Length();
  
  if (!newLength)
  {
    enableButtons(false);
  }

  bool settingsChanged = false;
  if (matchCaseMemory != matchCaseBox->GetValue() ||
    regexMemory != regexBox->GetValue())
  {
    settingsChanged = true;
    matchCaseMemory = matchCaseBox->GetValue();
    regexMemory = regexBox->GetValue();
  }
  
  if (newLength != findEditLength || settingsChanged)
  {
    incrementalFind = true;
    
    size_t flags = 0;
    flags |= wxFR_DOWN;
    if (matchCaseBox->GetValue())
      flags |= wxFR_MATCHCASE;

    sendFindEvent(flags);
    findEditLength = newLength;
    findData->SetFlags(flags);
  }
}

void FindReplacePanel::sendFindEvent(size_t flags)
{
  wxFindDialogEvent findEvent(wxEVT_COMMAND_FIND_NEXT, 0);
  findEvent.SetFlags(flags);
  findEvent.SetFindString(findEdit->GetValue());

  parent->ProcessEvent(findEvent);

  findData->SetFindString(findEdit->GetValue());
  findData->SetReplaceString(replaceEdit->GetValue());
}

bool FindReplacePanel::getIncrementalFind()
{
  return incrementalFind; 
}

void FindReplacePanel::refresh()
{
  incrementalFind = false;
  findEdit->SetValue(findData->GetFindString());
  replaceEdit->SetValue(findData->GetReplaceString());
  
  size_t flags = findData->GetFlags();

  bool matchCase;
  matchCase = flags & wxFR_MATCHCASE;
  
  matchCaseBox->SetValue(matchCase);
  matchCaseMemory = matchCase;

  regexBox->SetValue(isRegex);
  regexMemory = isRegex;
}

void FindReplacePanel::setReplaceVisible(bool b)
{
  label2->Show(b);
  replaceEdit->Show(b);
  replaceButton->Show(b);
  replaceAllButton->Show(b);
  sizer->Layout();
}

void FindReplacePanel::flagNotFound(bool b)
{
  if (notFoundSet && b || !notFoundSet && !b)
    return;

  notFoundSet = b;
}

bool FindReplacePanel::getRegex()
{
  return regexBox->GetValue();
}

void FindReplacePanel::setMatchCase(bool b)
{
  matchCaseBox->SetValue(b);
}

void FindReplacePanel::setRegex(bool b)
{
  regexBox->SetValue(b);
}

void FindReplacePanel::enableButtons(bool b)
{
  findNextButton->Enable(b);
  replaceButton->Enable(b);
  replaceAllButton->Enable(b); 
}

Post Reply