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