]> git.mxchange.org Git - flightgear.git/blob - utils/fgadmin/src/fgadmin_funcs.cxx
Revive master sound enable switch.
[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 #  include <io.h>
32 #define unlink _unlink
33 #define mkdir _mkdir
34 #else // !_WIN32
35 #include <unistd.h>
36 #endif
37
38 #include <FL/Fl_File_Chooser.H>
39
40 #include <simgear/misc/sg_path.hxx>
41
42 #include "fgadmin.h"
43 #include "untarka.h"
44
45 using std::cout;
46 using std::endl;
47 using std::set;
48 using std::string;
49
50 extern string def_install_source;
51 extern string def_scenery_dest;
52
53 static const float min_progress = 0.0;
54 static const float max_progress = 5000.0;
55
56 // FIXME: Ugly hack to detect the situation below 
57 #ifdef FL_Volume_Down
58 //#if (FL_MAJOR_VERSION > 1)||((FL_MAJOR_VERSION == 1)&&(FL_MINOR_VERSION >= 3))
59     // Fltk 1.3 or newer, need to use "fl_filename_free_list"
60     #define FL_FREE_DIR_ENTRY(e) // do nothing, since "fl_filename_free_list" frees entire list
61     #define FL_FREE_DIR_LIST(list,count) fl_filename_free_list(&list, count)
62     #define FL_STAT(file,info) fl_stat( file.str().c_str(), info )
63 #else
64     // Fltk < 1.3, "fl_filename_free_list", "fl_stat" not available
65     #define FL_FREE_DIR_ENTRY(e) free(e)
66     #define FL_FREE_DIR_LIST(list,count) free(list)
67     #define FL_STAT(file,info) stat( file.str().c_str(), info );
68 #endif
69
70 /** Strip a single trailing '/' or '\\' */
71 static char* stripSlash(char* str)
72 {
73     int l = strlen(str);
74     if ((l>0)&&
75         ((str[l-1]=='/')||(str[l-1]=='\\')))
76     {
77         str[l-1] = 0;
78     }
79     return str;
80 }
81
82 // destructor
83 FGAdminUI::~FGAdminUI() {
84     delete prefs;
85 }
86
87
88 // initialize additional class elements
89 void FGAdminUI::init() {
90     prefs = new Fl_Preferences( Fl_Preferences::USER,
91                                 "flightgear.org",
92                                 "fgadmin" );
93     char buf[FL_PATH_MAX];
94     prefs->get( "install-source", buf, def_install_source.c_str(), FL_PATH_MAX );
95     source_text->value( buf );
96     source = buf;
97
98     prefs->get( "scenery-dest", buf, def_scenery_dest.c_str(), FL_PATH_MAX );
99     dest_text->value( buf );
100     dest = buf;
101
102     refresh_lists();
103
104     progress->minimum( min_progress );
105     progress->maximum( max_progress );
106
107     main_window->size_range( 465, 435 );
108 }
109
110 // show our UI
111 void FGAdminUI::show() {
112     main_window->show();
113 }
114
115
116 // refresh the check box lists
117 void FGAdminUI::refresh_lists() {
118     update_install_box();
119     update_remove_box();
120 }
121
122
123 // scan the source directory and update the install_box contents
124 // quit
125 void FGAdminUI::quit() {
126     main_window->hide();
127 }
128
129
130 // select the install source
131 void FGAdminUI::select_install_source() {
132     char* p = fl_dir_chooser( "Select Scenery Source Directory",
133                               source.c_str(),
134                               0 );
135     if ( p != 0 ) {
136         source = p;
137         source_text->value( p );
138         prefs->set( "install-source", p );
139         prefs->flush();
140     }
141 }
142
143
144 // select the install source
145 void FGAdminUI::select_install_dest() {
146     char* p = fl_dir_chooser( "Select Scenery Install Directory",
147                               dest.c_str(),
148                               0 );
149     if ( p != 0 ) {
150         dest = p;
151         dest_text->value( p );
152         prefs->set( "scenery-dest", p );
153         prefs->flush();
154     }
155 }
156
157 #if 0
158 // Like strcmp, but for strings
159 static int stringCompare(const void *string1, const void *string2)
160 {
161     const string *s1 = (const string *)string1;
162     const string *s2 = (const string *)string2;
163
164     // Compare name first, and then index.
165     return strcmp(s1->c_str(), s2->c_str());
166 }
167 #endif
168
169 void FGAdminUI::update_install_box() {
170     set<string> file_list;
171
172     install_box->clear();
173
174     if ( source.length() ) {
175         struct dirent **list;
176         int nb = fl_filename_list( source.c_str(), &list );
177         for ( int i = 0; i < nb; ++i ) {
178             // find base name of archive file
179             char base[FL_PATH_MAX];
180             dirent *ent = list[i];
181             stripSlash(ent->d_name);
182             strncpy( base, ent->d_name, FL_PATH_MAX );
183             const char *p = fl_filename_ext( base );
184             int offset;
185             string::size_type expected_length = 0;
186             if ( strcmp( p, ".gz" ) == 0 ) {
187                 offset = p - base;
188                 base[offset] = '\0';
189                 p = fl_filename_ext( base );
190                 if ( strcmp( p, ".tar" ) == 0 ) {
191                     offset = p - base;
192                     base[offset] = '\0';
193                     expected_length = 14;
194                 }
195             } else if ( strcmp( p, ".tgz" ) == 0 ) {
196                 offset = p - base;
197                 base[offset] = '\0';
198                 expected_length = 11;
199             } else if ( strcmp( p, ".zip" ) == 0 ) {
200                 offset = p - base;
201                 base[offset] = '\0';
202                 expected_length = 11;
203             }
204
205             if ( strlen(ent->d_name) != expected_length ) {
206                 // simple heuristic to ignore non-scenery files
207             } else if ( ent->d_name[0] != 'e' && ent->d_name[0] != 'w' ) {
208                 // further sanity checks on name
209             } else if ( ent->d_name[4] != 'n' && ent->d_name[4] != 's' ) {
210                 // further sanity checks on name
211             } else {
212                 // add to list if not installed
213                 SGPath install( dest );
214                 install.append( base );
215                 if ( ! fl_filename_isdir( install.c_str() ) ) {
216                     // cout << install.str() << " install candidate." << endl;
217                     file_list.insert( ent->d_name );
218                 } else {
219                     // cout << install.str() << " exists." << endl;
220                 }
221             }
222             FL_FREE_DIR_ENTRY(ent);
223         }
224         FL_FREE_DIR_LIST(list, nb);
225
226         for ( set<string>::iterator it = file_list.begin(); it != file_list.end(); ++it ) {
227             install_box->add( it->c_str() );
228         }
229
230         install_box->redraw();
231     }
232 }
233
234
235 // scan the source directory and update the install_box contents
236 void FGAdminUI::update_remove_box() {
237
238     remove_box->clear();
239
240     if ( dest.length() ) {
241         string path[2];
242         path[0] = dest + "/Terrain";
243         path[1] = dest + "/Objects";
244         if ( !fl_filename_isdir( path[0].c_str() ) ) {
245             path[0] = dest;
246             path[1] = "";
247         } else if ( !fl_filename_isdir( path[1].c_str() ) ) {
248             path[1] = "";
249         }
250
251         set<string> dir_list;
252         for ( int i = 0; i < 2; i++ ) {
253             if ( !path[i].empty() ) {
254                 dirent **list;
255                 int nb = fl_filename_list( path[i].c_str(), &list );
256                 for ( int i = 0; i < nb; ++i ) {
257                     dirent *ent = list[i];
258                     stripSlash(ent->d_name);
259                     if ( strlen(ent->d_name) == 7 &&
260                             ( ent->d_name[0] == 'e' || ent->d_name[0] == 'w' ) &&
261                             ( ent->d_name[4] == 'n' || ent->d_name[4] == 's' ) ) {
262                         dir_list.insert( ent->d_name );
263                     }
264                     FL_FREE_DIR_ENTRY(ent);
265                 }
266                 FL_FREE_DIR_LIST(list, nb);
267             }
268         }
269
270         for ( set<string>::iterator it = dir_list.begin(); it != dir_list.end(); ++it ) {
271             remove_box->add( it->c_str() );
272         }
273
274         remove_box->redraw();
275     }
276 }
277
278
279 // install selected files
280 void FGAdminUI::install_selected() {
281     char *f;
282
283     install_b->deactivate();
284     remove_b->deactivate();
285     quit_b->deactivate();
286
287     // traverse install box and install each item
288     for ( int i = 0; i <= install_box->nitems(); ++i ) {
289         if ( install_box->checked( i ) ) {
290             f = install_box->text( i );
291             SGPath file( source );
292             file.append( f );
293             struct ::stat info;
294             FL_STAT( file, &info );
295             float old_max = progress->maximum();
296             progress->maximum( info.st_size );
297             progress_label = "Installing ";
298             progress_label += f;
299             progress->label( progress_label.c_str() );
300             progress->value( min_progress );
301             main_window->cursor( FL_CURSOR_WAIT );
302             tarextract( (char *)file.c_str(), (char *)dest.c_str(), true, &FGAdminUI::step, this );
303             progress->value( min_progress );
304             main_window->cursor( FL_CURSOR_DEFAULT );
305             progress->label( "" );
306             progress->maximum( old_max );
307         }
308     }
309     quit_b->activate();
310     install_b->activate();
311     remove_b->activate();
312
313     refresh_lists();
314 }
315
316
317 static unsigned long count_dir( const char *dir_name, bool top = true ) {
318     unsigned long cnt = 0L;
319     dirent **list;
320     int nb = fl_filename_list( dir_name, &list );
321     if ( nb != 0 ) {
322         for ( int i = 0; i < nb; ++i ) {
323             dirent *ent = list[i];
324             stripSlash(ent->d_name);
325             if ( strcmp( ent->d_name, "." ) == 0 ) {
326                 // ignore "."
327             } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
328                 // ignore ".."
329             } else if ( fl_filename_isdir( ent->d_name ) ) {
330                 SGPath child( dir_name );
331                 child.append( ent->d_name );
332                 cnt += count_dir( child.c_str(), false );
333             } else {
334                 cnt += 1;
335             }
336             FL_FREE_DIR_ENTRY(ent);
337         }
338         FL_FREE_DIR_LIST(list, nb);
339     } else if ( top ) {
340         string base = dir_name;
341         size_t pos = base.rfind('/');
342         string file = base.substr( pos );
343         base.erase( pos );
344         string path = base + "/Terrain" + file;
345         cnt = count_dir( path.c_str(), false );
346         path = base + "/Objects" + file;
347         cnt += count_dir( path.c_str(), false );
348     }
349     return cnt;
350 }
351
352 static void remove_dir( const char *dir_name, void (*step)(void*,int), void *data, bool top = true ) {
353     dirent **list;
354     int nb = fl_filename_list( dir_name, &list );
355     if ( nb > 0 ) {
356         for ( int i = 0; i < nb; ++i ) {
357             dirent *ent = list[i];
358             SGPath child( dir_name );
359             child.append( ent->d_name );
360             stripSlash(ent->d_name);
361             if ( strcmp( ent->d_name, "." ) == 0 ) {
362                 // ignore "."
363             } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
364                 // ignore ".."
365             } else if ( child.isDir() ) {
366                 remove_dir( child.c_str(), step, data, false );
367             } else {
368                 unlink( child.c_str() );
369                 if (step) step( data, 1 );
370             }
371             FL_FREE_DIR_ENTRY(ent);
372         }
373         FL_FREE_DIR_LIST(list, nb);
374         rmdir( dir_name );
375     } else if ( top ) {
376         string base = dir_name;
377         size_t pos = base.rfind('/');
378         string file = base.substr( pos );
379         base.erase( pos );
380         string path = base + "/Terrain" + file;
381         remove_dir( path.c_str(), step, data, false );
382         path = base + "/Objects" + file;
383         remove_dir( path.c_str(), step, data, false );
384     }
385 }
386
387
388 // remove selected files
389 void FGAdminUI::remove_selected() {
390     char *f;
391
392     install_b->deactivate();
393     remove_b->deactivate();
394     quit_b->deactivate();
395     // traverse remove box and recursively remove each item
396     for ( int i = 0; i <= remove_box->nitems(); ++i ) {
397         if ( remove_box->checked( i ) ) {
398             f = remove_box->text( i );
399             SGPath dir( dest );
400             dir.append( f );
401             float old_max = progress->maximum();
402             progress_label = "Removing ";
403             progress_label += f;
404             progress->label( progress_label.c_str() );
405             progress->value( min_progress );
406             main_window->cursor( FL_CURSOR_WAIT );
407             progress->maximum( count_dir( dir.c_str() ) );
408             remove_dir( dir.c_str(), &FGAdminUI::step, this );
409             progress->value( min_progress );
410             main_window->cursor( FL_CURSOR_DEFAULT );
411             progress->label( "" );
412             progress->maximum( old_max );
413         }
414     }
415     quit_b->activate();
416     install_b->activate();
417     remove_b->activate();
418
419     refresh_lists();
420    
421 }
422
423 void FGAdminUI::step(void *data)
424 {
425    Fl_Progress *p = ((FGAdminUI*)data)->progress;
426
427    // we don't actually know the total work in advanced due to the
428    // nature of tar archives, and it would be inefficient to prescan
429    // the files, so just cycle the progress bar until we are done.
430    float tmp = p->value() + 1;
431    if ( tmp > max_progress ) {
432        tmp = 0.0;
433    }
434    p->value( tmp );
435
436    Fl::check();
437 }
438
439 void FGAdminUI::step(void *data, int n)
440 {
441    Fl_Progress *p = ((FGAdminUI*)data)->progress;
442
443    float tmp = p->value() + n;
444    p->value( tmp );
445
446    Fl::check();
447 }