]> git.mxchange.org Git - flightgear.git/blob - utils/fgadmin/src/fgadmin_funcs.cxx
Fix my mailing address by replacing it with my web page.
[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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #include <iostream>
25 #include <string>
26 #include <vector>
27 #include <sys/stat.h>
28
29 #ifdef _MSC_VER
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::vector;
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     int i;
141     vector<string> file_list;
142     file_list.clear();
143
144     install_box->clear();
145
146     if ( source.length() ) {
147         ulDir *dir = ulOpenDir( source.c_str() ) ;
148         ulDirEnt *ent;
149         while ( dir != 0 && ( ent = ulReadDir( dir ) ) ) {
150             // find base name of archive file
151             char base[FL_PATH_MAX];
152             strncpy( base, ent->d_name, FL_PATH_MAX );
153             const char *p = fl_filename_ext( base );
154             int offset;
155             if ( strcmp( p, ".gz" ) == 0 ) {
156                 offset = p - base;
157                 base[offset] = '\0';
158                 p = fl_filename_ext( base );
159                 if ( strcmp( p, ".tar" ) == 0 ) {
160                     offset = p - base;
161                     base[offset] = '\0';
162                 }
163             } else if ( strcmp( p, ".zip" ) == 0 ) {
164                 offset = p - base;
165                 base[offset] = '\0';
166             }
167
168             if ( strlen(ent->d_name) != 14 ) {
169                 // simple heuristic to ignore non-scenery files
170             } else if ( ent->d_name[0] != 'e' && ent->d_name[0] != 'w' ) {
171                 // further sanity checks on name
172             } else if ( ent->d_name[4] != 'n' && ent->d_name[4] != 's' ) {
173                 // further sanity checks on name
174             } else {
175                 // add to list if not installed
176                 SGPath install( dest );
177                 install.append( base );
178                 if ( ! fl_filename_isdir( install.c_str() ) ) {
179                     // cout << install.str() << " install candidate." << endl;
180                     file_list.push_back( ent->d_name );
181                 } else {
182                     // cout << install.str() << " exists." << endl;
183                 }
184             }
185         }
186         ulCloseDir( dir );
187
188         // convert to a qsort()'able array
189         string *sort_list = new string[file_list.size()];
190         for ( i = 0; i < file_list.size(); ++i ) {
191             sort_list[i] = fl_filename_name( file_list[i].c_str() );
192         }
193
194         // Sort the file list into display order
195         qsort(sort_list, file_list.size(), sizeof(string), stringCompare);
196
197         for ( int i = 0; i < file_list.size(); ++i ) {
198             install_box->add( sort_list[i].c_str() );
199         }
200
201         delete [] sort_list;
202
203         install_box->redraw();
204     }
205 }
206
207
208 // scan the source directory and update the install_box contents
209 void FGAdminUI::update_remove_box() {
210     int i;
211     vector<string> dir_list;
212     dir_list.clear();
213
214     remove_box->clear();
215
216     if ( dest.length() ) {
217         ulDir *dir = ulOpenDir( dest.c_str() ) ;
218         ulDirEnt *ent;
219         while ( dir != 0 && ( ent = ulReadDir( dir ) ) ) {
220             if ( strlen(ent->d_name) != 7 ) {
221                 // simple heuristic to ignore non-scenery directories
222             } else if ( ent->d_name[0] != 'e' && ent->d_name[0] != 'w' ) {
223                 // further sanity checks on name
224             } else if ( ent->d_name[4] != 'n' && ent->d_name[4] != 's' ) {
225                 // further sanity checks on name
226             } else {
227                 dir_list.push_back( ent->d_name );
228             }
229         }
230         ulCloseDir( dir );
231
232         // convert to a qsort()'able array
233         string *sort_list = new string[dir_list.size()];
234         for ( i = 0; i < dir_list.size(); ++i ) {
235             sort_list[i] = fl_filename_name( dir_list[i].c_str() );
236         }
237
238         // Sort the file list into display order
239         qsort(sort_list, dir_list.size(), sizeof(string), stringCompare);
240
241         for ( int i = 0; i < dir_list.size(); ++i ) {
242             remove_box->add( sort_list[i].c_str() );
243         }
244
245         delete [] sort_list;
246
247         remove_box->redraw();
248     }
249 }
250
251
252 // install selected files
253 void FGAdminUI::install_selected() {
254     char *f;
255
256     install_b->deactivate();
257     remove_b->deactivate();
258     quit_b->deactivate();
259
260     // traverse install box and install each item
261     for ( int i = 0; i <= install_box->nitems(); ++i ) {
262         if ( install_box->checked( i ) ) {
263             f = install_box->text( i );
264             SGPath file( source );
265             file.append( f );
266             struct stat info;
267             stat( file.str().c_str(), &info );
268             float old_max = progress->maximum();
269             progress->maximum( info.st_size );
270             progress_label = "Installing ";
271             progress_label += f;
272             progress->label( progress_label.c_str() );
273             progress->value( min_progress );
274             main_window->cursor( FL_CURSOR_WAIT );
275             tarextract( (char *)file.c_str(), (char *)dest.c_str(), true, &FGAdminUI::step, this );
276             progress->value( min_progress );
277             main_window->cursor( FL_CURSOR_DEFAULT );
278             progress->label( "" );
279             progress->maximum( old_max );
280         }
281     }
282     quit_b->activate();
283     install_b->activate();
284     remove_b->activate();
285
286     refresh_lists();
287 }
288
289
290 static unsigned long count_dir( const char *dir_name ) {
291     ulDir *dir = ulOpenDir( dir_name ) ;
292     ulDirEnt *ent;
293     unsigned long cnt = 0L;
294     while ( ent = ulReadDir( dir ) ) {
295         if ( strcmp( ent->d_name, "." ) == 0 ) {
296             // ignore "."
297         } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
298             // ignore ".."
299         } else if ( ent->d_isdir ) {
300             SGPath child( dir_name );
301             child.append( ent->d_name );
302             cnt += count_dir( child.c_str() );
303         } else {
304             cnt += 1;
305         }
306     }
307     ulCloseDir( dir );
308     return cnt;
309 }
310
311 static void remove_dir( const char *dir_name, void (*step)(void*,int), void *data ) {
312     ulDir *dir = ulOpenDir( dir_name ) ;
313     ulDirEnt *ent;
314     while ( ent = ulReadDir( dir ) ) {
315         if ( strcmp( ent->d_name, "." ) == 0 ) {
316             // ignore "."
317         } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
318             // ignore ".."
319         } else if ( ent->d_isdir ) {
320             SGPath child( dir_name );
321             child.append( ent->d_name );
322             remove_dir( child.c_str(), step, data );
323         } else {
324             SGPath child( dir_name );
325             child.append( ent->d_name );
326             unlink( child.c_str() );
327             if (step) step( data, 1 );
328         }
329     }
330     ulCloseDir( dir );
331     rmdir( dir_name );
332 }
333
334
335 // remove selected files
336 void FGAdminUI::remove_selected() {
337     char *f;
338
339     install_b->deactivate();
340     remove_b->deactivate();
341     quit_b->deactivate();
342     // traverse remove box and recursively remove each item
343     for ( int i = 0; i <= remove_box->nitems(); ++i ) {
344         if ( remove_box->checked( i ) ) {
345             f = remove_box->text( i );
346             SGPath dir( dest );
347             dir.append( f );
348             float old_max = progress->maximum();
349             progress_label = "Removing ";
350             progress_label += f;
351             progress->label( progress_label.c_str() );
352             progress->value( min_progress );
353             main_window->cursor( FL_CURSOR_WAIT );
354             progress->maximum( count_dir( dir.c_str() ) );
355             remove_dir( dir.c_str(), &FGAdminUI::step, this );
356             progress->value( min_progress );
357             main_window->cursor( FL_CURSOR_DEFAULT );
358             progress->label( "" );
359             progress->maximum( old_max );
360         }
361     }
362     quit_b->activate();
363     install_b->activate();
364     remove_b->activate();
365
366     refresh_lists();
367    
368 }
369
370 void FGAdminUI::step(void *data)
371 {
372    Fl_Progress *p = ((FGAdminUI*)data)->progress;
373
374    // we don't actually know the total work in advanced due to the
375    // nature of tar archives, and it would be inefficient to prescan
376    // the files, so just cycle the progress bar until we are done.
377    float tmp = p->value() + 1;
378    if ( tmp > max_progress ) {
379        tmp = 0.0;
380    }
381    p->value( tmp );
382
383    Fl::check();
384 }
385
386 void FGAdminUI::step(void *data, int n)
387 {
388    Fl_Progress *p = ((FGAdminUI*)data)->progress;
389
390    float tmp = p->value() + n;
391    p->value( tmp );
392
393    Fl::check();
394 }