1 /////////////////////////////////////////////////////////////////////////////
2 // Name: samples/fswatcher/fswatcher.cpp
3 // Purpose: wxFileSystemWatcher sample
4 // Author: Bartosz Bekier
7 // Copyright: (c) Bartosz Bekier
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 #include "wx/wxprec.h"
20 #ifndef wxHAS_IMAGES_IN_RESOURCES
21 #include "../sample.xpm"
24 #include "wx/fswatcher.h"
25 #include "wx/listctrl.h"
26 #include "wx/cmdline.h"
28 // Define a new frame type: this is going to be our main frame
29 class MyFrame
: public wxFrame
32 MyFrame(const wxString
& title
);
35 // Add an entry of the specified type asking the user for the filename if
36 // the one passed to this function is empty.
37 void AddEntry(wxFSWPathType type
, wxString filename
= wxString());
39 bool CreateWatcherIfNecessary();
42 // file system watcher creation
46 void OnClear(wxCommandEvent
& WXUNUSED(event
)) { m_evtConsole
->Clear(); }
47 void OnQuit(wxCommandEvent
& WXUNUSED(event
)) { Close(true); }
48 void OnWatch(wxCommandEvent
& event
);
49 void OnAbout(wxCommandEvent
& event
);
51 void OnAdd(wxCommandEvent
& event
);
52 void OnAddTree(wxCommandEvent
& event
);
53 void OnRemove(wxCommandEvent
& event
);
55 void OnFileSystemEvent(wxFileSystemWatcherEvent
& event
);
56 void LogEvent(const wxFileSystemWatcherEvent
& event
);
58 wxTextCtrl
*m_evtConsole
; // events console
59 wxListView
*m_filesList
; // list of watched paths
60 wxFileSystemWatcher
* m_watcher
; // file system watcher
62 const static wxString LOG_FORMAT
; // how to format events
65 const wxString
MyFrame::LOG_FORMAT
= " %-12s %-36s %-36s";
67 // Define a new application type, each program should derive a class from wxApp
68 class MyApp
: public wxApp
71 // 'Main program' equivalent: the program execution "starts" here
74 if ( !wxApp::OnInit() )
77 wxLog::AddTraceMask("EventSource");
78 wxLog::AddTraceMask(wxTRACE_FSWATCHER
);
80 // create the main application window
81 m_frame
= new MyFrame("File System Watcher wxWidgets App");
83 // If we returned false here, the application would exit immediately.
87 // create the file system watcher here, because it needs an active loop
88 virtual void OnEventLoopEnter(wxEventLoopBase
* WXUNUSED(loop
))
90 if ( m_frame
->CreateWatcherIfNecessary() )
92 if ( !m_dirToWatch
.empty() )
93 m_frame
->AddEntry(wxFSWPath_Dir
, m_dirToWatch
);
97 virtual void OnInitCmdLine(wxCmdLineParser
& parser
)
99 wxApp::OnInitCmdLine(parser
);
100 parser
.AddParam("directory to watch",
101 wxCMD_LINE_VAL_STRING
,
102 wxCMD_LINE_PARAM_OPTIONAL
);
105 virtual bool OnCmdLineParsed(wxCmdLineParser
& parser
)
107 if ( !wxApp::OnCmdLineParsed(parser
) )
110 if ( parser
.GetParamCount() )
111 m_dirToWatch
= parser
.GetParam();
119 // The directory to watch if specified on the command line.
120 wxString m_dirToWatch
;
123 // Create a new application object: this macro will allow wxWidgets to create
124 // the application object during program execution (it's better than using a
125 // static object for many reasons) and also declares the accessor function
126 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
131 // ============================================================================
133 // ============================================================================
136 MyFrame::MyFrame(const wxString
& title
)
137 : wxFrame(NULL
, wxID_ANY
, title
),
140 SetIcon(wxICON(sample
));
142 // IDs for menu and buttons
145 MENU_ID_QUIT
= wxID_EXIT
,
146 MENU_ID_CLEAR
= wxID_CLEAR
,
154 // ================================================================
158 wxMenu
*menuFile
= new wxMenu
;
159 menuFile
->Append(MENU_ID_CLEAR
, "&Clear log\tCtrl-L");
160 menuFile
->AppendSeparator();
161 menuFile
->Append(MENU_ID_QUIT
, "E&xit\tAlt-X", "Quit this program");
164 wxMenu
*menuMon
= new wxMenu
;
165 wxMenuItem
* it
= menuMon
->AppendCheckItem(MENU_ID_WATCH
, "&Watch\tCtrl-W");
166 // started by default, because file system watcher is started by default
169 // the "About" item should be in the help menu
170 wxMenu
*menuHelp
= new wxMenu
;
171 menuHelp
->Append(wxID_ABOUT
, "&About\tF1", "Show about dialog");
173 // now append the freshly created menu to the menu bar...
174 wxMenuBar
*menuBar
= new wxMenuBar();
175 menuBar
->Append(menuFile
, "&File");
176 menuBar
->Append(menuMon
, "&Watch");
177 menuBar
->Append(menuHelp
, "&Help");
179 // ... and attach this menu bar to the frame
182 // ================================================================
186 wxPanel
*panel
= new wxPanel(this);
187 wxSizer
*panelSizer
= new wxGridSizer(2);
188 wxBoxSizer
*leftSizer
= new wxBoxSizer(wxVERTICAL
);
191 wxStaticText
* label
= new wxStaticText(panel
, wxID_ANY
, "Watched paths");
192 leftSizer
->Add(label
, wxSizerFlags().Center().Border(wxALL
));
195 m_filesList
= new wxListView(panel
, wxID_ANY
, wxPoint(-1,-1),
196 wxSize(300,200), wxLC_LIST
| wxLC_SINGLE_SEL
);
197 leftSizer
->Add(m_filesList
, wxSizerFlags(1).Expand());
200 wxButton
* buttonAdd
= new wxButton(panel
, BTN_ID_ADD
, "&Add");
201 wxButton
* buttonAddTree
= new wxButton(panel
, BTN_ID_ADD_TREE
, "Add &tree");
202 wxButton
* buttonRemove
= new wxButton(panel
, BTN_ID_REMOVE
, "&Remove");
203 wxSizer
*btnSizer
= new wxGridSizer(2);
204 btnSizer
->Add(buttonAdd
, wxSizerFlags().Center().Border(wxALL
));
205 btnSizer
->Add(buttonAddTree
, wxSizerFlags().Center().Border(wxALL
));
206 btnSizer
->Add(buttonRemove
, wxSizerFlags().Center().Border(wxALL
));
208 // and put it all together
209 leftSizer
->Add(btnSizer
, wxSizerFlags(0).Expand());
210 panelSizer
->Add(leftSizer
, wxSizerFlags(1).Expand());
211 panel
->SetSizerAndFit(panelSizer
);
213 // ================================================================
216 wxTextCtrl
*headerText
= new wxTextCtrl(this, wxID_ANY
, "",
217 wxDefaultPosition
, wxDefaultSize
,
219 wxString h
= wxString::Format(LOG_FORMAT
, "event", "path", "new path");
220 headerText
->SetValue(h
);
223 m_evtConsole
= new wxTextCtrl(this, wxID_ANY
, "",
224 wxDefaultPosition
, wxSize(200,200),
225 wxTE_MULTILINE
|wxTE_READONLY
|wxHSCROLL
);
227 // set monospace font to have output in nice columns
228 wxFont
font(9, wxFONTFAMILY_TELETYPE
,
229 wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
230 headerText
->SetFont(font
);
231 m_evtConsole
->SetFont(font
);
233 // ================================================================
234 // laying out whole frame
236 wxBoxSizer
*sizer
= new wxBoxSizer(wxVERTICAL
);
237 sizer
->Add(panel
, wxSizerFlags(1).Expand());
238 sizer
->Add(headerText
, wxSizerFlags().Expand());
239 sizer
->Add(m_evtConsole
, wxSizerFlags(1).Expand());
240 SetSizerAndFit(sizer
);
242 // set size and position on screen
246 // ================================================================
247 // event handlers & show
250 Connect(MENU_ID_CLEAR
, wxEVT_COMMAND_MENU_SELECTED
,
251 wxCommandEventHandler(MyFrame::OnClear
));
252 Connect(MENU_ID_QUIT
, wxEVT_COMMAND_MENU_SELECTED
,
253 wxCommandEventHandler(MyFrame::OnQuit
));
254 Connect(MENU_ID_WATCH
, wxEVT_COMMAND_MENU_SELECTED
,
255 wxCommandEventHandler(MyFrame::OnWatch
));
256 Connect(wxID_ABOUT
, wxEVT_COMMAND_MENU_SELECTED
,
257 wxCommandEventHandler(MyFrame::OnAbout
));
260 Connect(BTN_ID_ADD
, wxEVT_COMMAND_BUTTON_CLICKED
,
261 wxCommandEventHandler(MyFrame::OnAdd
));
262 Connect(BTN_ID_ADD_TREE
, wxEVT_COMMAND_BUTTON_CLICKED
,
263 wxCommandEventHandler(MyFrame::OnAddTree
));
264 Connect(BTN_ID_REMOVE
, wxEVT_COMMAND_BUTTON_CLICKED
,
265 wxCommandEventHandler(MyFrame::OnRemove
));
267 // and show itself (the frames, unlike simple controls, are not shown when
268 // created initially)
277 bool MyFrame::CreateWatcherIfNecessary()
283 Connect(wxEVT_FSWATCHER
,
284 wxFileSystemWatcherEventHandler(MyFrame::OnFileSystemEvent
));
289 void MyFrame::CreateWatcher()
291 wxCHECK_RET(!m_watcher
, "Watcher already initialized");
292 m_watcher
= new wxFileSystemWatcher();
293 m_watcher
->SetOwner(this);
296 // ============================================================================
298 // ============================================================================
300 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
302 wxMessageBox("Demonstrates the usage of file system watcher, "
303 "the wxWidgets monitoring system notifying you of "
304 "changes done to your files.\n"
305 "(c) 2009 Bartosz Bekier\n",
306 "About wxWidgets File System Watcher Sample",
307 wxOK
| wxICON_INFORMATION
, this);
310 void MyFrame::OnWatch(wxCommandEvent
& event
)
312 wxLogDebug("%s start=%d", __WXFUNCTION__
, event
.IsChecked());
314 if (event
.IsChecked())
316 wxCHECK_RET(!m_watcher
, "Watcher already initialized");
321 wxCHECK_RET(m_watcher
, "Watcher not initialized");
322 m_filesList
->DeleteAllItems();
327 void MyFrame::OnAdd(wxCommandEvent
& WXUNUSED(event
))
329 AddEntry(wxFSWPath_Dir
);
332 void MyFrame::OnAddTree(wxCommandEvent
& WXUNUSED(event
))
334 AddEntry(wxFSWPath_Tree
);
337 void MyFrame::AddEntry(wxFSWPathType type
, wxString filename
)
339 if ( filename
.empty() )
341 // TODO account for adding the files as well
342 filename
= wxDirSelector("Choose a folder to watch", "",
343 wxDD_DEFAULT_STYLE
| wxDD_DIR_MUST_EXIST
);
344 if ( filename
.empty() )
348 wxCHECK_RET(m_watcher
, "Watcher not initialized");
350 wxLogDebug("Adding %s: '%s'",
352 type
== wxFSWPath_Dir
? "directory" : "directory tree");
358 ok
= m_watcher
->Add(wxFileName::DirName(filename
));
362 ok
= m_watcher
->AddTree(wxFileName::DirName(filename
));
367 wxFAIL_MSG( "Unexpected path type." );
372 wxLogError("Error adding '%s' to watched paths", filename
);
376 m_filesList
->InsertItem(m_filesList
->GetItemCount(), filename
);
379 void MyFrame::OnRemove(wxCommandEvent
& WXUNUSED(event
))
381 wxCHECK_RET(m_watcher
, "Watcher not initialized");
382 long idx
= m_filesList
->GetFirstSelected();
386 wxString path
= m_filesList
->GetItemText(idx
);
388 // TODO we know it is a dir, but it doesn't have to be
389 if (!m_watcher
->Remove(wxFileName::DirName(path
)))
391 wxLogError("Error removing '%s' from watched paths", path
);
395 m_filesList
->DeleteItem(idx
);
399 void MyFrame::OnFileSystemEvent(wxFileSystemWatcherEvent
& event
)
401 // TODO remove when code is rock-solid
402 wxLogTrace(wxTRACE_FSWATCHER
, "*** %s ***", event
.ToString());
407 static wxString
GetFSWEventChangeTypeName(int changeType
)
411 case wxFSW_EVENT_CREATE
:
413 case wxFSW_EVENT_DELETE
:
415 case wxFSW_EVENT_RENAME
:
417 case wxFSW_EVENT_MODIFY
:
419 case wxFSW_EVENT_ACCESS
:
423 return "INVALID_TYPE";
426 void MyFrame::LogEvent(const wxFileSystemWatcherEvent
& event
)
428 wxString entry
= wxString::Format(LOG_FORMAT
+ "\n",
429 GetFSWEventChangeTypeName(event
.GetChangeType()),
430 event
.GetPath().GetFullPath(),
431 event
.GetNewPath().GetFullPath());
432 m_evtConsole
->AppendText(entry
);