]>
Commit | Line | Data |
---|---|---|
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 | wxDELETE(m_watcher); | |
283 | } | |
284 | } | |
285 | ||
286 | void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event)) | |
287 | { | |
288 | wxCHECK_RET(m_watcher, "Watcher not initialized"); | |
289 | ||
290 | // TODO account for adding the files as well | |
291 | const wxString& dir = wxDirSelector("Choose a folder to watch", "", | |
292 | wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); | |
293 | if ( dir.empty() ) | |
294 | return; | |
295 | ||
296 | wxLogDebug("Adding directory: '%s'", dir); | |
297 | ||
298 | if (!m_watcher->Add(wxFileName::DirName(dir), wxFSW_EVENT_ALL)) | |
299 | { | |
300 | wxLogError("Error adding '%s' to watched paths", dir); | |
301 | } | |
302 | else | |
303 | { | |
304 | m_filesList->InsertItem(m_filesList->GetItemCount(), dir); | |
305 | } | |
306 | } | |
307 | ||
308 | void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event)) | |
309 | { | |
310 | wxCHECK_RET(m_watcher, "Watcher not initialized"); | |
311 | long idx = m_filesList->GetFirstSelected(); | |
312 | if (idx == -1) | |
313 | return; | |
314 | ||
315 | wxString path = m_filesList->GetItemText(idx); | |
316 | ||
317 | // TODO we know it is a dir, but it doesn't have to be | |
318 | if (!m_watcher->Remove(wxFileName::DirName(path))) | |
319 | { | |
320 | wxLogError("Error removing '%s' from watched paths", path); | |
321 | } | |
322 | else | |
323 | { | |
324 | m_filesList->DeleteItem(idx); | |
325 | } | |
326 | } | |
327 | ||
328 | void MyFrame::OnFileSystemEvent(wxFileSystemWatcherEvent& event) | |
329 | { | |
330 | // TODO remove when code is rock-solid | |
331 | wxLogDebug(wxTRACE_FSWATCHER, "*** %s ***", event.ToString()); | |
332 | LogEvent(event); | |
333 | } | |
334 | ||
335 | ||
336 | static wxString GetFSWEventChangeTypeName(int changeType) | |
337 | { | |
338 | switch (changeType) | |
339 | { | |
340 | case wxFSW_EVENT_CREATE: | |
341 | return "CREATE"; | |
342 | case wxFSW_EVENT_DELETE: | |
343 | return "DELETE"; | |
344 | case wxFSW_EVENT_RENAME: | |
345 | return "RENAME"; | |
346 | case wxFSW_EVENT_MODIFY: | |
347 | return "MODIFY"; | |
348 | case wxFSW_EVENT_ACCESS: | |
349 | return "ACCESS"; | |
350 | } | |
351 | ||
352 | return "INVALID_TYPE"; | |
353 | } | |
354 | ||
355 | void MyFrame::LogEvent(const wxFileSystemWatcherEvent& event) | |
356 | { | |
357 | wxString entry = wxString::Format(LOG_FORMAT + "\n", | |
358 | GetFSWEventChangeTypeName(event.GetChangeType()), | |
359 | event.GetPath().GetFullPath(), | |
360 | event.GetNewPath().GetFullPath()); | |
361 | m_evtConsole->AppendText(entry); | |
362 | } |