]> git.saurik.com Git - wxWidgets.git/blob - samples/fswatcher/fswatcher.cpp
Use an even more informative error message in wxFSW unit test.
[wxWidgets.git] / samples / fswatcher / fswatcher.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: samples/fswatcher/fswatcher.cpp
3 // Purpose: wxFileSystemWatcher sample
4 // Author: Bartosz Bekier
5 // Created: 2009-06-27
6 // RCS-ID: $Id$
7 // Copyright: (c) Bartosz Bekier
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #ifdef __BORLANDC__
14 #pragma hdrstop
15 #endif
16 #ifndef WX_PRECOMP
17 #include "wx/wx.h"
18 #endif
19
20 #ifndef __WXMSW__
21 #include "../sample.xpm"
22 #endif
23
24 #include "wx/fswatcher.h"
25 #include "wx/listctrl.h"
26
27 // Define a new frame type: this is going to be our main frame
28 class MyFrame : public wxFrame
29 {
30 public:
31 MyFrame(const wxString& title);
32 virtual ~MyFrame();
33
34 private:
35 // file system watcher creation
36 void OnEventLoopEnter();
37 void CreateWatcher();
38
39 // event handlers
40 void OnClear(wxCommandEvent& WXUNUSED(event)) { m_evtConsole->Clear(); }
41 void OnQuit(wxCommandEvent& WXUNUSED(event)) { Close(true); }
42 void OnWatch(wxCommandEvent& event);
43 void OnAbout(wxCommandEvent& event);
44
45 void OnAdd(wxCommandEvent& event);
46 void OnRemove(wxCommandEvent& event);
47
48 void OnFileSystemEvent(wxFileSystemWatcherEvent& event);
49 void LogEvent(const wxFileSystemWatcherEvent& event);
50
51 wxTextCtrl *m_evtConsole; // events console
52 wxListView *m_filesList; // list of watched paths
53 wxFileSystemWatcher* m_watcher; // file system watcher
54
55 friend class MyApp;
56
57 const static wxString LOG_FORMAT; // how to format events
58 };
59
60 const wxString MyFrame::LOG_FORMAT = " %-12s %-36s %-36s";
61
62 // Define a new application type, each program should derive a class from wxApp
63 class MyApp : public wxApp
64 {
65 public:
66 // 'Main program' equivalent: the program execution "starts" here
67 virtual bool OnInit()
68 {
69 wxLog::AddTraceMask("EventSource");
70 wxLog::AddTraceMask(wxTRACE_FSWATCHER);
71
72 // create the main application window
73 m_frame = new MyFrame("File System Watcher wxWidgets App");
74
75 // If we returned false here, the application would exit immediately.
76 return true;
77 }
78
79 // create the file system watcher here, because it needs an active loop
80 virtual void OnEventLoopEnter(wxEventLoopBase* WXUNUSED(loop))
81 {
82 m_frame->OnEventLoopEnter();
83 }
84
85 private:
86 MyFrame *m_frame;
87 };
88
89 // Create a new application object: this macro will allow wxWidgets to create
90 // the application object during program execution (it's better than using a
91 // static object for many reasons) and also declares the accessor function
92 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
93 // not wxApp)
94 IMPLEMENT_APP(MyApp)
95
96
97 // ============================================================================
98 // implementation
99 // ============================================================================
100
101 // frame constructor
102 MyFrame::MyFrame(const wxString& title)
103 : wxFrame(NULL, wxID_ANY, title),
104 m_watcher(NULL)
105 {
106 SetIcon(wxICON(sample));
107
108 // IDs for menu and buttons
109 enum
110 {
111 MENU_ID_QUIT = wxID_EXIT,
112 MENU_ID_CLEAR = wxID_CLEAR,
113 MENU_ID_WATCH = 101,
114
115 BTN_ID_ADD = 200,
116 BTN_ID_REMOVE = 201,
117 };
118
119 // ================================================================
120 // menu
121
122 // create a menu bar
123 wxMenu *menuFile = new wxMenu;
124 menuFile->Append(MENU_ID_CLEAR, "&Clear log\tCtrl-L");
125 menuFile->AppendSeparator();
126 menuFile->Append(MENU_ID_QUIT, "E&xit\tAlt-X", "Quit this program");
127
128 // "Watch" menu
129 wxMenu *menuMon = new wxMenu;
130 wxMenuItem* it = menuMon->AppendCheckItem(MENU_ID_WATCH, "&Watch\tCtrl-W");
131 // started by default, because file system watcher is started by default
132 it->Check(true);
133
134 // the "About" item should be in the help menu
135 wxMenu *menuHelp = new wxMenu;
136 menuHelp->Append(wxID_ABOUT, "&About...\tF1", "Show about dialog");
137
138 // now append the freshly created menu to the menu bar...
139 wxMenuBar *menuBar = new wxMenuBar();
140 menuBar->Append(menuFile, "&File");
141 menuBar->Append(menuMon, "&Watch");
142 menuBar->Append(menuHelp, "&Help");
143
144 // ... and attach this menu bar to the frame
145 SetMenuBar(menuBar);
146
147 // ================================================================
148 // upper panel
149
150 // panel
151 wxPanel *panel = new wxPanel(this);
152 wxSizer *panelSizer = new wxGridSizer(2);
153 wxBoxSizer *leftSizer = new wxBoxSizer(wxVERTICAL);
154
155 // label
156 wxStaticText* label = new wxStaticText(panel, wxID_ANY, "Watched paths");
157 leftSizer->Add(label, wxSizerFlags().Center().Border(wxALL));
158
159 // list of files
160 m_filesList = new wxListView(panel, wxID_ANY, wxPoint(-1,-1),
161 wxSize(300,200), wxLC_LIST | wxLC_SINGLE_SEL);
162 leftSizer->Add(m_filesList, wxSizerFlags(1).Expand());
163
164 // buttons
165 wxButton* buttonAdd = new wxButton(panel, BTN_ID_ADD, "&Add");
166 wxButton* buttonRemove = new wxButton(panel, BTN_ID_REMOVE, "&Remove");
167 wxSizer *btnSizer = new wxGridSizer(2);
168 btnSizer->Add(buttonAdd, wxSizerFlags().Center().Border(wxALL));
169 btnSizer->Add(buttonRemove, wxSizerFlags().Center().Border(wxALL));
170
171 // and put it all together
172 leftSizer->Add(btnSizer, wxSizerFlags(0).Expand());
173 panelSizer->Add(leftSizer, wxSizerFlags(1).Expand());
174 panel->SetSizerAndFit(panelSizer);
175
176 // ================================================================
177 // lower panel
178
179 wxTextCtrl *headerText = new wxTextCtrl(this, wxID_ANY, "",
180 wxDefaultPosition, wxDefaultSize,
181 wxTE_READONLY);
182 wxString h = wxString::Format(LOG_FORMAT, "event", "path", "new path");
183 headerText->SetValue(h);
184
185 // event console
186 m_evtConsole = new wxTextCtrl(this, wxID_ANY, "",
187 wxDefaultPosition, wxSize(200,200),
188 wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL);
189
190 // set monospace font to have output in nice columns
191 wxFont font(9, wxFONTFAMILY_TELETYPE,
192 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
193 headerText->SetFont(font);
194 m_evtConsole->SetFont(font);
195
196 // ================================================================
197 // laying out whole frame
198
199 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
200 sizer->Add(panel, wxSizerFlags(1).Expand());
201 sizer->Add(headerText, wxSizerFlags().Expand());
202 sizer->Add(m_evtConsole, wxSizerFlags(1).Expand());
203 SetSizerAndFit(sizer);
204
205 // set size and position on screen
206 SetSize(800, 600);
207 CentreOnScreen();
208
209 // ================================================================
210 // event handlers & show
211
212 // menu
213 Connect(MENU_ID_CLEAR, wxEVT_COMMAND_MENU_SELECTED,
214 wxCommandEventHandler(MyFrame::OnClear));
215 Connect(MENU_ID_QUIT, wxEVT_COMMAND_MENU_SELECTED,
216 wxCommandEventHandler(MyFrame::OnQuit));
217 Connect(MENU_ID_WATCH, wxEVT_COMMAND_MENU_SELECTED,
218 wxCommandEventHandler(MyFrame::OnWatch));
219 Connect(wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED,
220 wxCommandEventHandler(MyFrame::OnAbout));
221
222 // buttons
223 Connect(BTN_ID_ADD, wxEVT_COMMAND_BUTTON_CLICKED,
224 wxCommandEventHandler(MyFrame::OnAdd));
225 Connect(BTN_ID_REMOVE, wxEVT_COMMAND_BUTTON_CLICKED,
226 wxCommandEventHandler(MyFrame::OnRemove));
227
228 // and show itself (the frames, unlike simple controls, are not shown when
229 // created initially)
230 Show(true);
231 }
232
233 MyFrame::~MyFrame()
234 {
235 delete m_watcher;
236 }
237
238 void MyFrame::OnEventLoopEnter()
239 {
240 if (m_watcher)
241 return;
242
243 CreateWatcher();
244 Connect(wxEVT_FSWATCHER,
245 wxFileSystemWatcherEventHandler(MyFrame::OnFileSystemEvent));
246 }
247
248 void MyFrame::CreateWatcher()
249 {
250 wxCHECK_RET(!m_watcher, "Watcher already initialized");
251 m_watcher = new wxFileSystemWatcher();
252 m_watcher->SetOwner(this);
253 }
254
255 // ============================================================================
256 // event handlers
257 // ============================================================================
258
259 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
260 {
261 wxMessageBox("Demonstrates the usage of file system watcher, "
262 "the wxWidgets monitoring system notifying you of "
263 "changes done to your files.\n"
264 "(c) 2009 Bartosz Bekier\n",
265 "About wxWidgets File System Watcher Sample",
266 wxOK | wxICON_INFORMATION, this);
267 }
268
269 void MyFrame::OnWatch(wxCommandEvent& event)
270 {
271 wxLogDebug("%s start=%d", __WXFUNCTION__, event.IsChecked());
272
273 if (event.IsChecked())
274 {
275 wxCHECK_RET(!m_watcher, "Watcher already initialized");
276 CreateWatcher();
277 }
278 else
279 {
280 wxCHECK_RET(m_watcher, "Watcher not initialized");
281 m_filesList->DeleteAllItems();
282 delete m_watcher;
283 m_watcher = NULL;
284 }
285 }
286
287 void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event))
288 {
289 wxCHECK_RET(m_watcher, "Watcher not initialized");
290
291 // TODO account for adding the files as well
292 const wxString& dir = wxDirSelector("Choose a folder to watch", "",
293 wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
294 if ( dir.empty() )
295 return;
296
297 wxLogDebug("Adding directory: '%s'", dir);
298
299 if (!m_watcher->Add(wxFileName::DirName(dir), wxFSW_EVENT_ALL))
300 {
301 wxLogError("Error adding '%s' to watched paths", dir);
302 }
303 else
304 {
305 m_filesList->InsertItem(m_filesList->GetItemCount(), dir);
306 }
307 }
308
309 void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event))
310 {
311 wxCHECK_RET(m_watcher, "Watcher not initialized");
312 long idx = m_filesList->GetFirstSelected();
313 if (idx == -1)
314 return;
315
316 wxString path = m_filesList->GetItemText(idx);
317
318 // TODO we know it is a dir, but it doesn't have to be
319 if (!m_watcher->Remove(wxFileName::DirName(path)))
320 {
321 wxLogError("Error removing '%s' from watched paths", path);
322 }
323 else
324 {
325 m_filesList->DeleteItem(idx);
326 }
327 }
328
329 void MyFrame::OnFileSystemEvent(wxFileSystemWatcherEvent& event)
330 {
331 // TODO remove when code is rock-solid
332 wxLogDebug(wxTRACE_FSWATCHER, "*** %s ***", event.ToString());
333 LogEvent(event);
334 }
335
336
337 static wxString GetFSWEventChangeTypeName(int changeType)
338 {
339 switch (changeType)
340 {
341 case wxFSW_EVENT_CREATE:
342 return "CREATE";
343 case wxFSW_EVENT_DELETE:
344 return "DELETE";
345 case wxFSW_EVENT_RENAME:
346 return "RENAME";
347 case wxFSW_EVENT_MODIFY:
348 return "MODIFY";
349 case wxFSW_EVENT_ACCESS:
350 return "ACCESS";
351 }
352
353 return "INVALID_TYPE";
354 }
355
356 void MyFrame::LogEvent(const wxFileSystemWatcherEvent& event)
357 {
358 wxString entry = wxString::Format(LOG_FORMAT + "\n",
359 GetFSWEventChangeTypeName(event.GetChangeType()),
360 event.GetPath().GetFullPath(),
361 event.GetNewPath().GetFullPath());
362 m_evtConsole->AppendText(entry);
363 }