1 // fgadmin_funcs.cxx -- FG Admin UI functions.
3 // Written by Curtis Olson, started February 2004.
5 // Copyright (c) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
33 #include <FL/Fl_File_Chooser.H>
36 #include <simgear/misc/sg_path.hxx>
46 extern string def_install_source;
47 extern string def_scenery_dest;
49 static const float min_progress = 0.0;
50 static const float max_progress = 5000.0;
53 FGAdminUI::~FGAdminUI() {
58 // initialize additional class elements
59 void FGAdminUI::init() {
60 prefs = new Fl_Preferences( Fl_Preferences::USER,
63 char buf[FL_PATH_MAX];
64 prefs->get( "install-source", buf, def_install_source.c_str(), FL_PATH_MAX );
65 source_text->value( buf );
68 prefs->get( "scenery-dest", buf, def_scenery_dest.c_str(), FL_PATH_MAX );
69 dest_text->value( buf );
74 progress->minimum( min_progress );
75 progress->maximum( max_progress );
77 main_window->size_range( 465, 435 );
81 void FGAdminUI::show() {
86 // refresh the check box lists
87 void FGAdminUI::refresh_lists() {
93 // scan the source directory and update the install_box contents
95 void FGAdminUI::quit() {
100 // select the install source
101 void FGAdminUI::select_install_source() {
102 char* p = fl_dir_chooser( "Select Scenery Source Directory",
107 source_text->value( p );
108 prefs->set( "install-source", p );
114 // select the install source
115 void FGAdminUI::select_install_dest() {
116 char* p = fl_dir_chooser( "Select Scenery Install Directory",
121 dest_text->value( p );
122 prefs->set( "scenery-dest", p );
128 // Like strcmp, but for strings
129 static int stringCompare(const void *string1, const void *string2)
131 const string *s1 = (const string *)string1;
132 const string *s2 = (const string *)string2;
134 // Compare name first, and then index.
135 return strcmp(s1->c_str(), s2->c_str());
139 void FGAdminUI::update_install_box() {
140 set<string> file_list;
142 install_box->clear();
144 if ( source.length() ) {
145 ulDir *dir = ulOpenDir( source.c_str() ) ;
147 while ( dir != 0 && ( ent = ulReadDir( dir ) ) ) {
148 // find base name of archive file
149 char base[FL_PATH_MAX];
150 strncpy( base, ent->d_name, FL_PATH_MAX );
151 const char *p = fl_filename_ext( base );
152 int offset, expected_length = 0;
153 if ( strcmp( p, ".gz" ) == 0 ) {
156 p = fl_filename_ext( base );
157 if ( strcmp( p, ".tar" ) == 0 ) {
160 expected_length = 14;
162 } else if ( strcmp( p, ".tgz" ) == 0 ) {
165 expected_length = 11;
166 } else if ( strcmp( p, ".zip" ) == 0 ) {
169 expected_length = 11;
172 if ( strlen(ent->d_name) != expected_length ) {
173 // simple heuristic to ignore non-scenery files
174 } else if ( ent->d_name[0] != 'e' && ent->d_name[0] != 'w' ) {
175 // further sanity checks on name
176 } else if ( ent->d_name[4] != 'n' && ent->d_name[4] != 's' ) {
177 // further sanity checks on name
179 // add to list if not installed
180 SGPath install( dest );
181 install.append( base );
182 if ( ! fl_filename_isdir( install.c_str() ) ) {
183 // cout << install.str() << " install candidate." << endl;
184 file_list.insert( ent->d_name );
186 // cout << install.str() << " exists." << endl;
192 for ( set<string>::iterator it = file_list.begin(); it != file_list.end(); ++it ) {
193 install_box->add( it->c_str() );
196 install_box->redraw();
201 // scan the source directory and update the install_box contents
202 void FGAdminUI::update_remove_box() {
206 if ( dest.length() ) {
208 path[0] = dest + "/Terrain";
209 path[1] = dest + "/Objects";
210 if ( !fl_filename_isdir( path[0].c_str() ) ) {
213 } else if ( !fl_filename_isdir( path[1].c_str() ) ) {
217 set<string> dir_list;
218 for ( int i = 0; i < 2; i++ ) {
219 if ( !path[i].empty() ) {
220 ulDir *dir = ulOpenDir( path[i].c_str() ) ;
222 while ( dir != 0 && ( ent = ulReadDir( dir ) ) ) {
223 if ( strlen(ent->d_name) == 7 &&
224 ( ent->d_name[0] == 'e' || ent->d_name[0] == 'w' ) &&
225 ( ent->d_name[4] == 'n' || ent->d_name[4] == 's' ) ) {
226 dir_list.insert( ent->d_name );
233 for ( set<string>::iterator it = dir_list.begin(); it != dir_list.end(); ++it ) {
234 remove_box->add( it->c_str() );
237 remove_box->redraw();
242 // install selected files
243 void FGAdminUI::install_selected() {
246 install_b->deactivate();
247 remove_b->deactivate();
248 quit_b->deactivate();
250 // traverse install box and install each item
251 for ( int i = 0; i <= install_box->nitems(); ++i ) {
252 if ( install_box->checked( i ) ) {
253 f = install_box->text( i );
254 SGPath file( source );
257 stat( file.str().c_str(), &info );
258 float old_max = progress->maximum();
259 progress->maximum( info.st_size );
260 progress_label = "Installing ";
262 progress->label( progress_label.c_str() );
263 progress->value( min_progress );
264 main_window->cursor( FL_CURSOR_WAIT );
265 tarextract( (char *)file.c_str(), (char *)dest.c_str(), true, &FGAdminUI::step, this );
266 progress->value( min_progress );
267 main_window->cursor( FL_CURSOR_DEFAULT );
268 progress->label( "" );
269 progress->maximum( old_max );
273 install_b->activate();
274 remove_b->activate();
280 static unsigned long count_dir( const char *dir_name, bool top = true ) {
281 unsigned long cnt = 0L;
282 ulDir *dir = ulOpenDir( dir_name ) ;
285 while ( ent = ulReadDir( dir ) ) {
286 if ( strcmp( ent->d_name, "." ) == 0 ) {
288 } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
290 } else if ( ent->d_isdir ) {
291 SGPath child( dir_name );
292 child.append( ent->d_name );
293 cnt += count_dir( child.c_str(), false );
300 string base = dir_name;
301 size_t pos = base.rfind('/');
302 string file = base.substr( pos );
304 string path = base + "/Terrain" + file;
305 cnt = count_dir( path.c_str(), false );
306 path = base + "/Objects" + file;
307 cnt += count_dir( path.c_str(), false );
312 static void remove_dir( const char *dir_name, void (*step)(void*,int), void *data, bool top = true ) {
313 ulDir *dir = ulOpenDir( dir_name ) ;
316 while ( ent = ulReadDir( dir ) ) {
317 if ( strcmp( ent->d_name, "." ) == 0 ) {
319 } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
321 } else if ( ent->d_isdir ) {
322 SGPath child( dir_name );
323 child.append( ent->d_name );
324 remove_dir( child.c_str(), step, data, false );
326 SGPath child( dir_name );
327 child.append( ent->d_name );
328 unlink( child.c_str() );
329 if (step) step( data, 1 );
335 string base = dir_name;
336 size_t pos = base.rfind('/');
337 string file = base.substr( pos );
339 string path = base + "/Terrain" + file;
340 remove_dir( path.c_str(), step, data, false );
341 path = base + "/Objects" + file;
342 remove_dir( path.c_str(), step, data, false );
347 // remove selected files
348 void FGAdminUI::remove_selected() {
351 install_b->deactivate();
352 remove_b->deactivate();
353 quit_b->deactivate();
354 // traverse remove box and recursively remove each item
355 for ( int i = 0; i <= remove_box->nitems(); ++i ) {
356 if ( remove_box->checked( i ) ) {
357 f = remove_box->text( i );
360 float old_max = progress->maximum();
361 progress_label = "Removing ";
363 progress->label( progress_label.c_str() );
364 progress->value( min_progress );
365 main_window->cursor( FL_CURSOR_WAIT );
366 progress->maximum( count_dir( dir.c_str() ) );
367 remove_dir( dir.c_str(), &FGAdminUI::step, this );
368 progress->value( min_progress );
369 main_window->cursor( FL_CURSOR_DEFAULT );
370 progress->label( "" );
371 progress->maximum( old_max );
375 install_b->activate();
376 remove_b->activate();
382 void FGAdminUI::step(void *data)
384 Fl_Progress *p = ((FGAdminUI*)data)->progress;
386 // we don't actually know the total work in advanced due to the
387 // nature of tar archives, and it would be inefficient to prescan
388 // the files, so just cycle the progress bar until we are done.
389 float tmp = p->value() + 1;
390 if ( tmp > max_progress ) {
398 void FGAdminUI::step(void *data, int n)
400 Fl_Progress *p = ((FGAdminUI*)data)->progress;
402 float tmp = p->value() + n;