wxWidgets/samples/fswatcher/fswatcher.cpp
Vadim Zeitlin 5cd9986613 Refactor wxEventLoopSource-related code.
Currently wxEventLoopSource can't be created directly and can only be used to
monitor file descriptors so reduce the API to just wxEventLoop::AddSourceForFD()
and remove AddSource(), RemoveSource() and RemoveAllSources() which couldn't
be implemented for all ports. This makes the code much simpler without any
loss of functionality.

Make wxEventLoopSource responsible for removing itself from the event loop
when it is deleted. This allows to remove IsOk() and Invalidate() methods
making the code simpler and gets rid of various sets/maps which were used
before.

This also allows to support event loop sources in Carbon as well: wxOSX/Carbon
now compiles and works with wxUSE_FSWATCHER==1.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62475 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2009-10-22 11:36:35 +00:00

364 lines
11 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: samples/fswatcher/fswatcher.cpp
// Purpose: wxFileSystemWatcher sample
// Author: Bartosz Bekier
// Created: 2009-06-27
// RCS-ID: $Id$
// Copyright: (c) Bartosz Bekier
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#ifndef __WXMSW__
#include "../sample.xpm"
#endif
#include "wx/fswatcher.h"
#include "wx/listctrl.h"
// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
virtual ~MyFrame();
private:
// file system watcher creation
void OnEventLoopEnter();
void CreateWatcher();
// event handlers
void OnClear(wxCommandEvent& WXUNUSED(event)) { m_evtConsole->Clear(); }
void OnQuit(wxCommandEvent& WXUNUSED(event)) { Close(true); }
void OnWatch(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnAdd(wxCommandEvent& event);
void OnRemove(wxCommandEvent& event);
void OnFileSystemEvent(wxFileSystemWatcherEvent& event);
void LogEvent(const wxFileSystemWatcherEvent& event);
wxTextCtrl *m_evtConsole; // events console
wxListView *m_filesList; // list of watched paths
wxFileSystemWatcher* m_watcher; // file system watcher
friend class MyApp;
const static wxString LOG_FORMAT; // how to format events
};
const wxString MyFrame::LOG_FORMAT = " %-12s %-36s %-36s";
// Define a new application type, each program should derive a class from wxApp
class MyApp : public wxApp
{
public:
// 'Main program' equivalent: the program execution "starts" here
virtual bool OnInit()
{
wxLog::AddTraceMask("EventSource");
wxLog::AddTraceMask(wxTRACE_FSWATCHER);
// create the main application window
m_frame = new MyFrame("File System Watcher wxWidgets App");
// If we returned false here, the application would exit immediately.
return true;
}
// create the file system watcher here, because it needs an active loop
virtual void OnEventLoopEnter(wxEventLoopBase* WXUNUSED(loop))
{
m_frame->OnEventLoopEnter();
}
private:
MyFrame *m_frame;
};
// Create a new application object: this macro will allow wxWidgets to create
// the application object during program execution (it's better than using a
// static object for many reasons) and also declares the accessor function
// wxGetApp() which will return the reference of the right type (i.e. MyApp and
// not wxApp)
IMPLEMENT_APP(MyApp)
// ============================================================================
// implementation
// ============================================================================
// frame constructor
MyFrame::MyFrame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title),
m_watcher(NULL)
{
SetIcon(wxICON(sample));
// IDs for menu and buttons
enum
{
MENU_ID_QUIT = wxID_EXIT,
MENU_ID_CLEAR = wxID_CLEAR,
MENU_ID_WATCH = 101,
BTN_ID_ADD = 200,
BTN_ID_REMOVE = 201,
};
// ================================================================
// menu
// create a menu bar
wxMenu *menuFile = new wxMenu;
menuFile->Append(MENU_ID_CLEAR, "&Clear log\tCtrl-L");
menuFile->AppendSeparator();
menuFile->Append(MENU_ID_QUIT, "E&xit\tAlt-X", "Quit this program");
// "Watch" menu
wxMenu *menuMon = new wxMenu;
wxMenuItem* it = menuMon->AppendCheckItem(MENU_ID_WATCH, "&Watch\tCtrl-W");
// started by default, because file system watcher is started by default
it->Check(true);
// the "About" item should be in the help menu
wxMenu *menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT, "&About...\tF1", "Show about dialog");
// now append the freshly created menu to the menu bar...
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(menuFile, "&File");
menuBar->Append(menuMon, "&Watch");
menuBar->Append(menuHelp, "&Help");
// ... and attach this menu bar to the frame
SetMenuBar(menuBar);
// ================================================================
// upper panel
// panel
wxPanel *panel = new wxPanel(this);
wxSizer *panelSizer = new wxGridSizer(2);
wxBoxSizer *leftSizer = new wxBoxSizer(wxVERTICAL);
// label
wxStaticText* label = new wxStaticText(panel, wxID_ANY, "Watched paths");
leftSizer->Add(label, wxSizerFlags().Center().Border(wxALL));
// list of files
m_filesList = new wxListView(panel, wxID_ANY, wxPoint(-1,-1),
wxSize(300,200), wxLC_LIST | wxLC_SINGLE_SEL);
leftSizer->Add(m_filesList, wxSizerFlags(1).Expand());
// buttons
wxButton* buttonAdd = new wxButton(panel, BTN_ID_ADD, "&Add");
wxButton* buttonRemove = new wxButton(panel, BTN_ID_REMOVE, "&Remove");
wxSizer *btnSizer = new wxGridSizer(2);
btnSizer->Add(buttonAdd, wxSizerFlags().Center().Border(wxALL));
btnSizer->Add(buttonRemove, wxSizerFlags().Center().Border(wxALL));
// and put it all together
leftSizer->Add(btnSizer, wxSizerFlags(0).Expand());
panelSizer->Add(leftSizer, wxSizerFlags(1).Expand());
panel->SetSizerAndFit(panelSizer);
// ================================================================
// lower panel
wxTextCtrl *headerText = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize,
wxTE_READONLY);
wxString h = wxString::Format(LOG_FORMAT, "event", "path", "new path");
headerText->SetValue(h);
// event console
m_evtConsole = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(200,200),
wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL);
// set monospace font to have output in nice columns
wxFont font(9, wxFONTFAMILY_TELETYPE,
wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
headerText->SetFont(font);
m_evtConsole->SetFont(font);
// ================================================================
// laying out whole frame
wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(panel, wxSizerFlags(1).Expand());
sizer->Add(headerText, wxSizerFlags().Expand());
sizer->Add(m_evtConsole, wxSizerFlags(1).Expand());
SetSizerAndFit(sizer);
// set size and position on screen
SetSize(800, 600);
CentreOnScreen();
// ================================================================
// event handlers & show
// menu
Connect(MENU_ID_CLEAR, wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(MyFrame::OnClear));
Connect(MENU_ID_QUIT, wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(MyFrame::OnQuit));
Connect(MENU_ID_WATCH, wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(MyFrame::OnWatch));
Connect(wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(MyFrame::OnAbout));
// buttons
Connect(BTN_ID_ADD, wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(MyFrame::OnAdd));
Connect(BTN_ID_REMOVE, wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(MyFrame::OnRemove));
// and show itself (the frames, unlike simple controls, are not shown when
// created initially)
Show(true);
}
MyFrame::~MyFrame()
{
delete m_watcher;
}
void MyFrame::OnEventLoopEnter()
{
if (m_watcher)
return;
CreateWatcher();
Connect(wxEVT_FSWATCHER,
wxFileSystemWatcherEventHandler(MyFrame::OnFileSystemEvent));
}
void MyFrame::CreateWatcher()
{
wxCHECK_RET(!m_watcher, "Watcher already initialized");
m_watcher = new wxFileSystemWatcher();
m_watcher->SetOwner(this);
}
// ============================================================================
// event handlers
// ============================================================================
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox("Demonstrates the usage of file system watcher, "
"the wxWidgets monitoring system notifying you of "
"changes done to your files.\n"
"(c) 2009 Bartosz Bekier\n",
"About wxWidgets File System Watcher Sample",
wxOK | wxICON_INFORMATION, this);
}
void MyFrame::OnWatch(wxCommandEvent& event)
{
wxLogDebug("%s start=%d", __WXFUNCTION__, event.IsChecked());
if (event.IsChecked())
{
wxCHECK_RET(!m_watcher, "Watcher already initialized");
CreateWatcher();
}
else
{
wxCHECK_RET(m_watcher, "Watcher not initialized");
m_filesList->DeleteAllItems();
delete m_watcher;
m_watcher = NULL;
}
}
void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event))
{
wxCHECK_RET(m_watcher, "Watcher not initialized");
// TODO account for adding the files as well
const wxString& dir = wxDirSelector("Choose a folder to watch", "",
wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
if ( dir.empty() )
return;
wxLogDebug("Adding directory: '%s'", dir);
if (!m_watcher->Add(wxFileName::DirName(dir), wxFSW_EVENT_ALL))
{
wxLogError("Error adding '%s' to watched paths", dir);
}
else
{
m_filesList->InsertItem(m_filesList->GetItemCount(), dir);
}
}
void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event))
{
wxCHECK_RET(m_watcher, "Watcher not initialized");
long idx = m_filesList->GetFirstSelected();
if (idx == -1)
return;
wxString path = m_filesList->GetItemText(idx);
// TODO we know it is a dir, but it doesn't have to be
if (!m_watcher->Remove(wxFileName::DirName(path)))
{
wxLogError("Error removing '%s' from watched paths", path);
}
else
{
m_filesList->DeleteItem(idx);
}
}
void MyFrame::OnFileSystemEvent(wxFileSystemWatcherEvent& event)
{
// TODO remove when code is rock-solid
wxLogDebug(wxTRACE_FSWATCHER, "*** %s ***", event.ToString());
LogEvent(event);
}
static wxString GetFSWEventChangeTypeName(int changeType)
{
switch (changeType)
{
case wxFSW_EVENT_CREATE:
return "CREATE";
case wxFSW_EVENT_DELETE:
return "DELETE";
case wxFSW_EVENT_RENAME:
return "RENAME";
case wxFSW_EVENT_MODIFY:
return "MODIFY";
case wxFSW_EVENT_ACCESS:
return "ACCESS";
}
return "INVALID_TYPE";
}
void MyFrame::LogEvent(const wxFileSystemWatcherEvent& event)
{
wxString entry = wxString::Format(LOG_FORMAT + "\n",
GetFSWEventChangeTypeName(event.GetChangeType()),
event.GetPath().GetFullPath(),
event.GetNewPath().GetFullPath());
m_evtConsole->AppendText(entry);
}