]> git.mxchange.org Git - flightgear.git/blob - utils/fgadmin/src/fgadmin_funcs.cxx
Updates from Fred to add a progress bar and fix visual glitches.
[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 - curt@flightgear.org
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
28 #ifdef _MSC_VER
29 #  include <direct.h>
30 #endif
31
32 #include <FL/Fl_File_Chooser.H>
33 #include <plib/ul.h>
34
35 #include <simgear/misc/sg_path.hxx>
36
37 #include "fgadmin.h"
38 #include "untarka.h"
39
40 using std::cout;
41 using std::endl;
42 using std::vector;
43 using std::string;
44
45 static const float min_progress = 0.0;
46 static const float max_progress = 5000.0;
47
48 // destructor
49 FGAdminUI::~FGAdminUI() {
50     delete prefs;
51 }
52
53
54 // initialize additional class elements
55 void FGAdminUI::init() {
56     prefs = new Fl_Preferences( Fl_Preferences::USER,
57                                 "flightgear.org",
58                                 "fgadmin" );
59     char buf[FL_PATH_MAX];
60     prefs->get( "install-source", buf, "", FL_PATH_MAX );
61     source_text->value( buf );
62     source = buf;
63
64     prefs->get( "scenery-dest", buf, "", FL_PATH_MAX );
65     dest_text->value( buf );
66     dest = buf;
67
68     refresh_lists();
69
70     progress->minimum( min_progress );
71     progress->maximum( max_progress );
72 }
73
74 // show our UI
75 void FGAdminUI::show() {
76     main_window->show();
77 }
78
79
80 // refresh the check box lists
81 void FGAdminUI::refresh_lists() {
82     update_install_box();
83     update_remove_box();
84 }
85
86
87 // scan the source directory and update the install_box contents
88 // quit
89 void FGAdminUI::quit() {
90     main_window->hide();
91 }
92
93
94 // select the install source
95 void FGAdminUI::select_install_source() {
96     char* p = fl_dir_chooser( "Select Scenery Source Directory",
97                               source.c_str(),
98                               0 );
99     if ( p != 0 ) {
100         source = p;
101         source_text->value( p );
102         prefs->set( "install-source", p );
103         prefs->flush();
104     }
105 }
106
107
108 // select the install source
109 void FGAdminUI::select_install_dest() {
110     char* p = fl_dir_chooser( "Select Scenery Install Directory",
111                               dest.c_str(),
112                               0 );
113     if ( p != 0 ) {
114         dest = p;
115         dest_text->value( p );
116         prefs->set( "scenery-dest", p );
117         prefs->flush();
118     }
119 }
120
121
122 // Like strcmp, but for strings
123 static int stringCompare(const void *string1, const void *string2)
124 {
125     const string *s1 = (const string *)string1;
126     const string *s2 = (const string *)string2;
127
128     // Compare name first, and then index.
129     return strcmp(s1->c_str(), s2->c_str());
130 }
131
132
133 void FGAdminUI::update_install_box() {
134     int i;
135     vector<string> file_list;
136     file_list.clear();
137
138     install_box->clear();
139
140     if ( source.length() ) {
141         ulDir *dir = ulOpenDir( source.c_str() ) ;
142         ulDirEnt *ent;
143         while ( ent = ulReadDir( dir ) ) {
144             // find base name of archive file
145             char base[FL_PATH_MAX];
146             strncpy( base, ent->d_name, FL_PATH_MAX );
147             const char *p = fl_filename_ext( base );
148             int offset;
149             if ( strcmp( p, ".gz" ) == 0 ) {
150                 offset = p - base;
151                 base[offset] = '\0';
152                 p = fl_filename_ext( base );
153                 if ( strcmp( p, ".tar" ) == 0 ) {
154                     offset = p - base;
155                     base[offset] = '\0';
156                 }
157             } else if ( strcmp( p, ".zip" ) == 0 ) {
158                 offset = p - base;
159                 base[offset] = '\0';
160             }
161
162             // add to list if not installed
163             SGPath install( dest );
164             install.append( base );
165             if ( ! fl_filename_isdir( install.c_str() ) ) {
166                 // cout << install.str() << " install candidate." << endl;
167                 file_list.push_back( ent->d_name );
168             } else {
169                 // cout << install.str() << " exists." << endl;
170             }
171         }
172         ulCloseDir( dir );
173
174         // convert to a qsort()'able array
175         string *sort_list = new string[file_list.size()];
176         for ( i = 0; i < file_list.size(); ++i ) {
177             sort_list[i] = fl_filename_name( file_list[i].c_str() );
178         }
179
180         // Sort the file list into display order
181         qsort(sort_list, file_list.size(), sizeof(string), stringCompare);
182
183         for ( int i = 0; i < file_list.size(); ++i ) {
184             install_box->add( sort_list[i].c_str() );
185         }
186
187         delete [] sort_list;
188
189         install_box->redraw();
190     }
191 }
192
193
194 // scan the source directory and update the install_box contents
195 void FGAdminUI::update_remove_box() {
196     int i;
197     vector<string> dir_list;
198     dir_list.clear();
199
200     remove_box->clear();
201
202     if ( dest.length() ) {
203         ulDir *dir = ulOpenDir( dest.c_str() ) ;
204         ulDirEnt *ent;
205         while ( ent = ulReadDir( dir ) ) {
206             if ( strlen(ent->d_name) != 7 ) {
207                 // simple heuristic to ingore non-scenery directories
208             } else if ( ent->d_name[0] != 'e' && ent->d_name[0] != 'w' ) {
209                 // further sanity checks on name
210             } else if ( ent->d_name[4] != 'n' && ent->d_name[4] != 's' ) {
211                 // further sanity checks on name
212             } else {
213                 dir_list.push_back( ent->d_name );
214             }
215         }
216         ulCloseDir( dir );
217
218         // convert to a qsort()'able array
219         string *sort_list = new string[dir_list.size()];
220         for ( i = 0; i < dir_list.size(); ++i ) {
221             sort_list[i] = fl_filename_name( dir_list[i].c_str() );
222         }
223
224         // Sort the file list into display order
225         qsort(sort_list, dir_list.size(), sizeof(string), stringCompare);
226
227         for ( int i = 0; i < dir_list.size(); ++i ) {
228             remove_box->add( sort_list[i].c_str() );
229         }
230
231         delete [] sort_list;
232
233         remove_box->redraw();
234     }
235 }
236
237
238 // install selected files
239 void FGAdminUI::install_selected() {
240     char *f;
241
242     install_b->deactivate();
243     remove_b->deactivate();
244
245     // traverse install box and install each item
246     for ( int i = 0; i <= install_box->nitems(); ++i ) {
247         if ( install_box->checked( i ) ) {
248             f = install_box->text( i );
249             SGPath file( source );
250             file.append( f );
251             cout << "installing " << file.str() << endl;
252             progress->value( min_progress );
253             main_window->cursor( FL_CURSOR_WAIT );
254             tarextract( (char *)file.c_str(), (char *)dest.c_str(), true, &FGAdminUI::step, this );
255             progress->value( min_progress );
256             main_window->cursor( FL_CURSOR_DEFAULT );
257         }
258     }
259     install_b->activate();
260     remove_b->activate();
261
262     refresh_lists();
263 }
264
265
266 static void remove_dir( const char *dir_name, void (*step)(void*), void *data ) {
267     ulDir *dir = ulOpenDir( dir_name ) ;
268     ulDirEnt *ent;
269     while ( ent = ulReadDir( dir ) ) {
270         if ( strcmp( ent->d_name, "." ) == 0 ) {
271             // ignore "."
272         } else if ( strcmp( ent->d_name, ".." ) == 0 ) {
273             // ignore ".."
274         } else if ( ent->d_isdir ) {
275             SGPath child( dir_name );
276             child.append( ent->d_name );
277             remove_dir( child.c_str(), step, data );
278         } else {
279             SGPath child( dir_name );
280             child.append( ent->d_name );
281             unlink( child.c_str() );
282             if (step) step( data );
283         }
284     }
285     ulCloseDir( dir );
286     rmdir( dir_name );
287 }
288
289
290 // remove selected files
291 void FGAdminUI::remove_selected() {
292     char *f;
293
294     install_b->deactivate();
295     remove_b->deactivate();
296     // traverse remove box and recursively remove each item
297     for ( int i = 0; i <= remove_box->nitems(); ++i ) {
298         if ( remove_box->checked( i ) ) {
299             f = remove_box->text( i );
300             SGPath dir( dest );
301             dir.append( f );
302             progress->value( min_progress );
303             main_window->cursor( FL_CURSOR_WAIT );
304             cout << "removing " << dir.str() << endl;
305             remove_dir( dir.c_str(), &FGAdminUI::step, this );
306             progress->value( min_progress );
307             main_window->cursor( FL_CURSOR_DEFAULT );
308         }
309     }
310     install_b->activate();
311     remove_b->activate();
312
313     refresh_lists();
314    
315 }
316
317
318 void FGAdminUI::step(void *data)
319 {
320    Fl_Progress *p = ((FGAdminUI*)data)->progress;
321
322    // we don't actually know the total work in advanced due to the
323    // nature of tar archives, and it would be inefficient to prescan
324    // the files, so just cycle the progress bar until we are done.
325    float tmp = p->value() + 1;
326    if ( tmp > max_progress ) {
327        tmp = 0.0;
328    }
329    p->value( tmp );
330
331    Fl::check();
332 }