]> git.mxchange.org Git - flightgear.git/blob - utils/fgadmin/src/fgadmin_funcs.cxx
Merge branch 'fredb/winbuild'
[flightgear.git] / utils / fgadmin / src / fgadmin_funcs.cxx
1 // fgadmin_funcs.cxx -- FG Admin UI functions.
2 //
3 // Written by Curtis Olson, started February 2004.
4 //
5 // Copyright (c) 2004  Curtis L. Olson - http://www.flightgear.org/~curt
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22
23
24 #include <iostream>
25 #include <string>
26 #include <set>
27 #include <sys/stat.h>
28
29 #ifdef _WIN32
30 #  include <direct.h>
31 #endif
32
33 #include <FL/Fl_File_Chooser.H>
34 #include <plib/ul.h>
35
36 #include <simgear/misc/sg_path.hxx>
37
38 #include "fgadmin.h"
39 #include "untarka.h"
40
41 using std::cout;
42 using std::endl;
43 using std::set;
44 using std::string;
45
46 extern string def_install_source;
47 extern string def_scenery_dest;
48
49 static const float min_progress = 0.0;
50 static const float max_progress = 5000.0;
51
52 // destructor
53 FGAdminUI::~FGAdminUI() {
54     delete prefs;
55 }
56
57
58 // initialize additional class elements
59 void FGAdminUI::init() {
60     prefs = new Fl_Preferences( Fl_Preferences::USER,
61                                 "flightgear.org",
62                                 "fgadmin" );
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 );
66     source = buf;
67
68     prefs->get( "scenery-dest", buf, def_scenery_dest.c_str(), FL_PATH_MAX );
69     dest_text->value( buf );
70     dest = buf;
71
72     refresh_lists();
73
74     progress->minimum( min_progress );
75     progress->maximum( max_progress );
76
77     main_window->size_range( 465, 435 );
78 }
79
80 // show our UI
81 void FGAdminUI::show() {
82     main_window->show();
83 }
84
85
86 // refresh the check box lists
87 void FGAdminUI::refresh_lists() {
88     update_install_box();
89     update_remove_box();
90 }
91
92
93 // scan the source directory and update the install_box contents
94 // quit
95 void FGAdminUI::quit() {
96     main_window->hide();
97 }
98
99
100 // select the install source
101 void FGAdminUI::select_install_source() {
102     char* p = fl_dir_chooser( "Select Scenery Source Directory",
103                               source.c_str(),
104                               0 );
105     if ( p != 0 ) {
106         source = p;
107         source_text->value( p );
108         prefs->set( "install-source", p );
109         prefs->flush();
110     }
111 }
112
113
114 // select the install source
115 void FGAdminUI::select_install_dest() {
116     char* p = fl_dir_chooser( "Select Scenery Install Directory",
117                               dest.c_str(),
118                               0 );
119     if ( p != 0 ) {
120         dest = p;
121         dest_text->value( p );
122         prefs->set( "scenery-dest", p );
123         prefs->flush();
124     }
125 }
126
127
128 // Like strcmp, but for strings
129 static int stringCompare(const void *string1, const void *string2)
130 {
131     const string *s1 = (const string *)string1;
132     const string *s2 = (const string *)string2;
133
134     // Compare name first, and then index.
135     return strcmp(s1->c_str(), s2->c_str());
136 }
137
138
139 void FGAdminUI::update_install_box() {
140     set<string> file_list;
141
142     install_box->clear();
143
144     if ( source.length() ) {
145         ulDir *dir = ulOpenDir( source.c_str() ) ;
146         ulDirEnt *ent;
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 ) {
154                 offset = p - base;
155                 base[offset] = '\0';
156                 p = fl_filename_ext( base );
157                 if ( strcmp( p, ".tar" ) == 0 ) {
158                     offset = p - base;
159                     base[offset] = '\0';
160                     expected_length = 14;
161                 }
162             } else if ( strcmp( p, ".tgz" ) == 0 ) {
163                 offset = p - base;
164                 base[offset] = '\0';
165                 expected_length = 11;
166             } else if ( strcmp( p, ".zip" ) == 0 ) {
167                 offset = p - base;
168                 base[offset] = '\0';
169                 expected_length = 11;
170             }
171
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
178             } else {
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 );
185                 } else {
186                     // cout << install.str() << " exists." << endl;
187                 }
188             }
189         }
190
191         ulCloseDir( dir );
192         for ( set<string>::iterator it = file_list.begin(); it != file_list.end(); ++it ) {
193             install_box->add( it->c_str() );
194         }
195
196         install_box->redraw();
197     }
198 }
199
200
201 // scan the source directory and update the install_box contents
202 void FGAdminUI::update_remove_box() {
203
204     remove_box->clear();
205
206     if ( dest.length() ) {
207         string path[2];
208         path[0] = dest + "/Terrain";
209         path[1] = dest + "/Objects";
210         if ( !fl_filename_isdir( path[0].c_str() ) ) {
211             path[0] = dest;
212             path[1] = "";
213         } else if ( !fl_filename_isdir( path[1].c_str() ) ) {
214             path[1] = "";
215         }
216
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() ) ;
221                 ulDirEnt *ent;
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 );
227                     }
228                 }
229                 ulCloseDir( dir );
230             }
231         }
232
233         for ( set<string>::iterator it = dir_list.begin(); it != dir_list.end(); ++it ) {
234             remove_box->add( it->c_str() );
235         }
236
237         remove_box->redraw();
238     }
239 }
240
241
242 // install selected files
243 void FGAdminUI::install_selected() {
244     char *f;
245
246     install_b->deactivate();
247     remove_b->deactivate();
248     quit_b->deactivate();
249
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 );
255             file.append( f );
256             struct stat info;
257             stat( file.str().c_str(), &info );
258             float old_max = progress->maximum();
259             progress->maximum( info.st_size );
260             progress_label = "Installing ";
261             progress_label += f;
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 );
270         }
271     }
272     quit_b->activate();
273     install_b->activate();
274     remove_b->activate();
275
276     refresh_lists();
277 }
278
279
280 static unsigned long count_dir( const char *dir_name, bool top = true ) {
281     unsigned long cnt = 0L;
282     ulDir *dir = ulOpenDir( dir_name ) ;
283     if ( dir ) {
284         ulDirEnt *ent;
285         while ( ent = ulReadDir( dir ) ) {
286             if ( strcmp( ent->d_name, "." ) == 0 ) {
287                 // ignore "."
288             } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
289                 // ignore ".."
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 );
294             } else {
295                 cnt += 1;
296             }
297         }
298         ulCloseDir( dir );
299     } else if ( top ) {
300         string base = dir_name;
301         size_t pos = base.rfind('/');
302         string file = base.substr( pos );
303         base.erase( 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 );
308     }
309     return cnt;
310 }
311
312 static void remove_dir( const char *dir_name, void (*step)(void*,int), void *data, bool top = true ) {
313     ulDir *dir = ulOpenDir( dir_name ) ;
314     if ( dir ) {
315         ulDirEnt *ent;
316         while ( ent = ulReadDir( dir ) ) {
317             if ( strcmp( ent->d_name, "." ) == 0 ) {
318                 // ignore "."
319             } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
320                 // ignore ".."
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 );
325             } else {
326                 SGPath child( dir_name );
327                 child.append( ent->d_name );
328                 unlink( child.c_str() );
329                 if (step) step( data, 1 );
330             }
331         }
332         ulCloseDir( dir );
333         rmdir( dir_name );
334     } else if ( top ) {
335         string base = dir_name;
336         size_t pos = base.rfind('/');
337         string file = base.substr( pos );
338         base.erase( 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 );
343     }
344 }
345
346
347 // remove selected files
348 void FGAdminUI::remove_selected() {
349     char *f;
350
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 );
358             SGPath dir( dest );
359             dir.append( f );
360             float old_max = progress->maximum();
361             progress_label = "Removing ";
362             progress_label += f;
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 );
372         }
373     }
374     quit_b->activate();
375     install_b->activate();
376     remove_b->activate();
377
378     refresh_lists();
379    
380 }
381
382 void FGAdminUI::step(void *data)
383 {
384    Fl_Progress *p = ((FGAdminUI*)data)->progress;
385
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 ) {
391        tmp = 0.0;
392    }
393    p->value( tmp );
394
395    Fl::check();
396 }
397
398 void FGAdminUI::step(void *data, int n)
399 {
400    Fl_Progress *p = ((FGAdminUI*)data)->progress;
401
402    float tmp = p->value() + n;
403    p->value( tmp );
404
405    Fl::check();
406 }