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