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"
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 void AddDirectory(const wxString
& dir
);
37 bool CreateWatcherIfNecessary();
40 // file system watcher creation
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
);
49 void OnAdd(wxCommandEvent
& event
);
50 void OnRemove(wxCommandEvent
& event
);
52 void OnFileSystemEvent(wxFileSystemWatcherEvent
& event
);
53 void LogEvent(const wxFileSystemWatcherEvent
& event
);
55 wxTextCtrl
*m_evtConsole
; // events console
56 wxListView
*m_filesList
; // list of watched paths
57 wxFileSystemWatcher
* m_watcher
; // file system watcher
59 const static wxString LOG_FORMAT
; // how to format events
62 const wxString
MyFrame::LOG_FORMAT
= " %-12s %-36s %-36s";
64 // Define a new application type, each program should derive a class from wxApp
65 class MyApp
: public wxApp
68 // 'Main program' equivalent: the program execution "starts" here
71 if ( !wxApp::OnInit() )
74 wxLog::AddTraceMask("EventSource");
75 wxLog::AddTraceMask(wxTRACE_FSWATCHER
);
77 // create the main application window
78 m_frame
= new MyFrame("File System Watcher wxWidgets App");
80 // If we returned false here, the application would exit immediately.
84 // create the file system watcher here, because it needs an active loop
85 virtual void OnEventLoopEnter(wxEventLoopBase
* WXUNUSED(loop
))
87 if ( m_frame
->CreateWatcherIfNecessary() )
89 if ( !m_dirToWatch
.empty() )
90 m_frame
->AddDirectory(m_dirToWatch
);
94 virtual void OnInitCmdLine(wxCmdLineParser
& parser
)
96 wxApp::OnInitCmdLine(parser
);
97 parser
.AddParam("directory to watch",
98 wxCMD_LINE_VAL_STRING
,
99 wxCMD_LINE_PARAM_OPTIONAL
);
102 virtual bool OnCmdLineParsed(wxCmdLineParser
& parser
)
104 if ( !wxApp::OnCmdLineParsed(parser
) )
107 if ( parser
.GetParamCount() )
108 m_dirToWatch
= parser
.GetParam();
116 // The directory to watch if specified on the command line.
117 wxString m_dirToWatch
;
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
128 // ============================================================================
130 // ============================================================================
133 MyFrame::MyFrame(const wxString
& title
)
134 : wxFrame(NULL
, wxID_ANY
, title
),
137 SetIcon(wxICON(sample
));
139 // IDs for menu and buttons
142 MENU_ID_QUIT
= wxID_EXIT
,
143 MENU_ID_CLEAR
= wxID_CLEAR
,
150 // ================================================================
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");
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
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");
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");
175 // ... and attach this menu bar to the frame
178 // ================================================================
182 wxPanel
*panel
= new wxPanel(this);
183 wxSizer
*panelSizer
= new wxGridSizer(2);
184 wxBoxSizer
*leftSizer
= new wxBoxSizer(wxVERTICAL
);
187 wxStaticText
* label
= new wxStaticText(panel
, wxID_ANY
, "Watched paths");
188 leftSizer
->Add(label
, wxSizerFlags().Center().Border(wxALL
));
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());
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
));
202 // and put it all together
203 leftSizer
->Add(btnSizer
, wxSizerFlags(0).Expand());
204 panelSizer
->Add(leftSizer
, wxSizerFlags(1).Expand());
205 panel
->SetSizerAndFit(panelSizer
);
207 // ================================================================
210 wxTextCtrl
*headerText
= new wxTextCtrl(this, wxID_ANY
, "",
211 wxDefaultPosition
, wxDefaultSize
,
213 wxString h
= wxString::Format(LOG_FORMAT
, "event", "path", "new path");
214 headerText
->SetValue(h
);
217 m_evtConsole
= new wxTextCtrl(this, wxID_ANY
, "",
218 wxDefaultPosition
, wxSize(200,200),
219 wxTE_MULTILINE
|wxTE_READONLY
|wxHSCROLL
);
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
);
227 // ================================================================
228 // laying out whole frame
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
);
236 // set size and position on screen
240 // ================================================================
241 // event handlers & show
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
));
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
));
259 // and show itself (the frames, unlike simple controls, are not shown when
260 // created initially)
269 bool MyFrame::CreateWatcherIfNecessary()
275 Connect(wxEVT_FSWATCHER
,
276 wxFileSystemWatcherEventHandler(MyFrame::OnFileSystemEvent
));
281 void MyFrame::CreateWatcher()
283 wxCHECK_RET(!m_watcher
, "Watcher already initialized");
284 m_watcher
= new wxFileSystemWatcher();
285 m_watcher
->SetOwner(this);
288 // ============================================================================
290 // ============================================================================
292 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
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);
302 void MyFrame::OnWatch(wxCommandEvent
& event
)
304 wxLogDebug("%s start=%d", __WXFUNCTION__
, event
.IsChecked());
306 if (event
.IsChecked())
308 wxCHECK_RET(!m_watcher
, "Watcher already initialized");
313 wxCHECK_RET(m_watcher
, "Watcher not initialized");
314 m_filesList
->DeleteAllItems();
319 void MyFrame::OnAdd(wxCommandEvent
& WXUNUSED(event
))
321 wxCHECK_RET(m_watcher
, "Watcher not initialized");
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
);
332 void MyFrame::AddDirectory(const wxString
& dir
)
334 wxLogDebug("Adding directory: '%s'", dir
);
336 if (!m_watcher
->Add(wxFileName::DirName(dir
), wxFSW_EVENT_ALL
))
338 wxLogError("Error adding '%s' to watched paths", dir
);
342 m_filesList
->InsertItem(m_filesList
->GetItemCount(), dir
);
346 void MyFrame::OnRemove(wxCommandEvent
& WXUNUSED(event
))
348 wxCHECK_RET(m_watcher
, "Watcher not initialized");
349 long idx
= m_filesList
->GetFirstSelected();
353 wxString path
= m_filesList
->GetItemText(idx
);
355 // TODO we know it is a dir, but it doesn't have to be
356 if (!m_watcher
->Remove(wxFileName::DirName(path
)))
358 wxLogError("Error removing '%s' from watched paths", path
);
362 m_filesList
->DeleteItem(idx
);
366 void MyFrame::OnFileSystemEvent(wxFileSystemWatcherEvent
& event
)
368 // TODO remove when code is rock-solid
369 wxLogTrace(wxTRACE_FSWATCHER
, "*** %s ***", event
.ToString());
374 static wxString
GetFSWEventChangeTypeName(int changeType
)
378 case wxFSW_EVENT_CREATE
:
380 case wxFSW_EVENT_DELETE
:
382 case wxFSW_EVENT_RENAME
:
384 case wxFSW_EVENT_MODIFY
:
386 case wxFSW_EVENT_ACCESS
:
390 return "INVALID_TYPE";
393 void MyFrame::LogEvent(const wxFileSystemWatcherEvent
& event
)
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
);