file size 3dgts+sfx, timebar tweak, mask msg
[goodguy/history.git] / cinelerra-5.1 / guicast / bcfilebox.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2017 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdelete.h"
23 #include "bcfilebox.h"
24 #include "bclistboxitem.h"
25 #include "bcnewfolder.h"
26 #include "bcpixmap.h"
27 #include "bcrename.h"
28 #include "bcresources.h"
29 #include "bcsignals.h"
30 #include "bctitle.h"
31 #include "clip.h"
32 #include "condition.h"
33 #include "filesystem.h"
34 #include "keys.h"
35 #include "language.h"
36 #include "mutex.h"
37 #include <string.h>
38 #include <sys/stat.h>
39
40
41
42
43
44
45
46 BC_FileBoxRecent::BC_FileBoxRecent(BC_FileBox *filebox, int x, int y)
47  : BC_ListBox(x, y, 250,
48                 filebox->get_text_height(MEDIUMFONT) * FILEBOX_HISTORY_SIZE +
49                 BC_ScrollBar::get_span(SCROLL_HORIZ) +
50                 LISTBOX_MARGIN * 2, LISTBOX_TEXT, &filebox->recent_dirs,
51                 0, 0, 1, 0, 1)
52 {
53         this->filebox = filebox;
54 }
55
56 int BC_FileBoxRecent::handle_event()
57 {
58         BC_ListBoxItem *selection = get_selection(0, 0);
59         if( selection != 0 ) {
60                 char *path = selection->get_text();
61                 filebox->submit_dir(path);
62         }
63         return 1;
64 }
65
66
67
68
69
70
71
72
73
74
75
76 BC_FileBoxListBox::BC_FileBoxListBox(int x, int y, BC_FileBox *filebox)
77  : BC_ListBox(x, y, filebox->get_listbox_w(), filebox->get_listbox_h(y),
78                 filebox->get_display_mode(), filebox->list_column,
79                 filebox->column_titles, filebox->column_width,
80                 filebox->columns, 0, 0,
81                 filebox->select_multiple ? LISTBOX_MULTIPLE : LISTBOX_SINGLE,
82                 ICON_LEFT, 0)
83 {
84         this->filebox = filebox;
85         set_sort_column(filebox->sort_column);
86         set_sort_order(filebox->sort_order);
87         set_allow_drag_column(1);
88 }
89
90 BC_FileBoxListBox::~BC_FileBoxListBox()
91 {
92 }
93
94 int BC_FileBoxListBox::handle_event()
95 {
96         filebox->submit_file(filebox->textbox->get_text());
97         return 1;
98 }
99
100 int BC_FileBoxListBox::selection_changed()
101 {
102         BC_ListBoxItem *item = get_selection(
103                 filebox->column_of_type(FILEBOX_NAME), 0);
104
105         if(item) {
106                 char path[BCTEXTLEN];
107                 strcpy(path, item->get_text());
108                 filebox->textbox->update(path);
109                 filebox->fs->extract_dir(filebox->directory, path);
110                 filebox->fs->extract_name(filebox->filename, path);
111                 filebox->fs->complete_path(path);
112                 strcpy(filebox->current_path, path);
113                 strcpy(filebox->submitted_path, path);
114         }
115         return 1;
116 }
117
118 int BC_FileBoxListBox::column_resize_event()
119 {
120         for(int i = 0; i < filebox->columns; i++)
121                 BC_WindowBase::get_resources()->filebox_columnwidth[i] =
122                         filebox->column_width[i] =
123                         get_column_width(i);
124         return 1;
125 }
126
127 int BC_FileBoxListBox::sort_order_event()
128 {
129         get_resources()->filebox_sortcolumn = filebox->sort_column = get_sort_column();
130         get_resources()->filebox_sortorder = filebox->sort_order = get_sort_order();
131         filebox->refresh(-1);
132         return 1;
133 }
134
135 int BC_FileBoxListBox::move_column_event()
136 {
137         filebox->move_column(get_from_column(), get_to_column());
138         return 1;
139 }
140
141 int BC_FileBoxListBox::evaluate_query(char *string)
142 {
143 // Search name column
144         ArrayList<BC_ListBoxItem*> *column =
145                 &filebox->list_column[filebox->column_of_type(FILEBOX_NAME)];
146 // Get current selection
147         int current_selection = get_selection_number(0, 0);
148
149 // Get best score in remaining items
150         int lowest_score = 0x7fffffff;
151         int best_item = -1;
152         if(current_selection < 0) current_selection = 0;
153 //      for(int i = current_selection + 1; i < column->size(); i++)
154         for(int i = current_selection; i < column->size(); i++)
155         {
156                 int len1 = strlen(string);
157                 int len2 = strlen(column->get(i)->get_text());
158                 int current_score = strncasecmp(string,
159                         column->get(i)->get_text(),
160                         MIN(len1, len2));
161 //printf(" %d i=%d %d %s %s\n", __LINE__, i, current_score, string, column->get(i)->get_text());
162
163                 if(abs(current_score) < lowest_score)
164                 {
165                         lowest_score = abs(current_score);
166                         best_item = i;
167                 }
168         }
169
170
171         return best_item;
172 }
173
174
175
176
177 BC_FileBoxTextBox::BC_FileBoxTextBox(int x, int y, BC_FileBox *filebox)
178  : BC_TextBox(x, y, filebox->get_w() - 20, 1,
179         filebox->want_directory ?  filebox->directory : filebox->filename)
180 {
181         this->filebox = filebox;
182 }
183
184 BC_FileBoxTextBox::~BC_FileBoxTextBox()
185 {
186 }
187
188
189
190
191 int BC_FileBoxTextBox::handle_event()
192 {
193         int result = 0;
194         if(get_keypress() != RETURN)
195         {
196                 result = calculate_suggestions(&filebox->list_column[0]);
197         }
198         return result;
199 }
200
201
202
203
204 BC_FileBoxDirectoryText::BC_FileBoxDirectoryText(int x, int y, BC_FileBox *filebox)
205  : BC_TextBox(x, y, filebox->get_w() - 40, 1, filebox->fs->get_current_dir())
206 {
207         this->filebox = filebox;
208 }
209
210 int BC_FileBoxDirectoryText::handle_event()
211 {
212         const char *path = get_text();
213         if( !filebox->fs->is_dir(path) ) return 0;
214         const char *cp = path;
215         while( *cp ) ++cp;
216         if( cp > path && *--cp != '/' ) return 0;
217         char *file_path = FileSystem::basepath(path);
218         char *dir_path = FileSystem::basepath(filebox->directory);
219         int ret = !strcmp(file_path, dir_path) ? 0 : 1;
220         if( ret ) {
221                 strcpy(filebox->directory, file_path);
222                 filebox->update_history();
223                 filebox->fs->change_dir(file_path);
224                 filebox->refresh(1);
225         }
226         delete [] dir_path;
227         delete [] file_path;
228         return ret;
229 }
230
231
232
233
234
235 BC_FileBoxFilterText::BC_FileBoxFilterText(int x, int y, BC_FileBox *filebox)
236  : BC_TextBox(x, y, filebox->get_w() - 50, 1, filebox->get_resources()->filebox_filter)
237 {
238         this->filebox = filebox;
239 }
240
241 int BC_FileBoxFilterText::handle_event()
242 {
243         filebox->update_filter(get_text());
244         return 1;
245 }
246
247
248
249
250 BC_FileBoxFilterMenu::BC_FileBoxFilterMenu(int x, int y, BC_FileBox *filebox)
251  : BC_ListBox(x,
252         y,
253         filebox->get_w() - 30,
254         120,
255         LISTBOX_TEXT,
256         &filebox->filter_list,
257         0,
258         0,
259         1,
260         0,
261         1)
262 {
263         this->filebox = filebox;
264         set_tooltip(_("Change the filter"));
265 }
266
267 int BC_FileBoxFilterMenu::handle_event()
268 {
269         filebox->filter_text->update(
270                 get_selection(filebox->column_of_type(FILEBOX_NAME), 0)->get_text());
271         filebox->update_filter(
272                 get_selection(filebox->column_of_type(FILEBOX_NAME), 0)->get_text());
273         return 1;
274 }
275
276
277
278
279
280
281
282
283
284
285 BC_FileBoxCancel::BC_FileBoxCancel(BC_FileBox *filebox)
286  : BC_CancelButton(filebox)
287 {
288         this->filebox = filebox;
289         set_tooltip(_("Cancel the operation"));
290 }
291
292 BC_FileBoxCancel::~BC_FileBoxCancel()
293 {
294 }
295
296 int BC_FileBoxCancel::handle_event()
297 {
298 //      filebox->submit_file(filebox->textbox->get_text());
299         filebox->newfolder_thread->interrupt();
300         filebox->set_done(1);
301         return 1;
302 }
303
304
305
306
307
308
309
310 BC_FileBoxUseThis::BC_FileBoxUseThis(BC_FileBox *filebox)
311  : BC_Button(filebox->get_w() / 2 -
312                 BC_WindowBase::get_resources()->usethis_button_images[0]->get_w() / 2,
313         filebox->ok_button->get_y(),
314         BC_WindowBase::get_resources()->usethis_button_images)
315 {
316         this->filebox = filebox;
317         set_tooltip(_("Submit the directory"));
318 }
319
320 BC_FileBoxUseThis::~BC_FileBoxUseThis()
321 {
322 }
323
324 int BC_FileBoxUseThis::handle_event()
325 {
326 // printf("BC_FileBoxUseThis::handle_event %d '%s'\n",
327 // __LINE__,
328 // filebox->textbox->get_text());
329         filebox->submit_file(filebox->textbox->get_text(), 1);
330         return 1;
331 }
332
333
334
335
336
337 BC_FileBoxOK::BC_FileBoxOK(BC_FileBox *filebox)
338  : BC_OKButton(filebox,
339         !filebox->want_directory ?
340                 BC_WindowBase::get_resources()->ok_images :
341                 BC_WindowBase::get_resources()->filebox_descend_images)
342 {
343         this->filebox = filebox;
344         if(filebox->want_directory)
345                 set_tooltip(_("Descend directory"));
346         else
347                 set_tooltip(_("Submit the file"));
348 }
349
350 BC_FileBoxOK::~BC_FileBoxOK()
351 {
352 }
353
354 int BC_FileBoxOK::handle_event()
355 {
356 //printf("BC_FileBoxOK::handle_event %d\n", __LINE__);
357         const char *path = filebox->textbox->get_text();
358         if( *path ) filebox->submit_file(path);
359         return 1;
360 }
361
362
363 BC_FileBoxText::BC_FileBoxText(int x, int y, BC_FileBox *filebox)
364  : BC_Button(x, y, BC_WindowBase::get_resources()->filebox_text_images)
365 {
366         this->filebox = filebox;
367         set_tooltip(_("Display text"));
368 }
369 int BC_FileBoxText::handle_event()
370 {
371         filebox->create_listbox(filebox->listbox->get_x(), filebox->listbox->get_y(), LISTBOX_TEXT);
372         filebox->listbox->show_window(1);
373         return 1;
374 }
375
376
377 BC_FileBoxIcons::BC_FileBoxIcons(int x, int y, BC_FileBox *filebox)
378  : BC_Button(x, y, BC_WindowBase::get_resources()->filebox_icons_images)
379 {
380         this->filebox = filebox;
381         set_tooltip(_("Display icons"));
382 }
383 int BC_FileBoxIcons::handle_event()
384 {
385         filebox->create_listbox(filebox->listbox->get_x(), filebox->listbox->get_y(), LISTBOX_ICONS);
386         filebox->listbox->show_window(1);
387         return 1;
388 }
389
390
391 BC_FileBoxNewfolder::BC_FileBoxNewfolder(int x, int y, BC_FileBox *filebox)
392  : BC_Button(x, y, BC_WindowBase::get_resources()->filebox_newfolder_images)
393 {
394         this->filebox = filebox;
395         set_tooltip(_("Create new folder"));
396 }
397 int BC_FileBoxNewfolder::handle_event()
398 {
399         filebox->newfolder_thread->start_new_folder();
400         return 1;
401 }
402
403
404 BC_FileBoxRename::BC_FileBoxRename(int x, int y, BC_FileBox *filebox)
405  : BC_Button(x, y, BC_WindowBase::get_resources()->filebox_rename_images)
406 {
407         this->filebox = filebox;
408         set_tooltip(_("Rename file"));
409 }
410 int BC_FileBoxRename::handle_event()
411 {
412         filebox->rename_thread->start_rename();
413         return 1;
414 }
415
416 BC_FileBoxUpdir::BC_FileBoxUpdir(int x, int y, BC_FileBox *filebox)
417  : BC_Button(x, y, BC_WindowBase::get_resources()->filebox_updir_images)
418 {
419         this->filebox = filebox;
420         set_tooltip(_("Up a directory"));
421 }
422 int BC_FileBoxUpdir::handle_event()
423 {
424 // Need a temp so submit_file can expand it
425         sprintf(string, "..");
426         filebox->submit_file(string);
427         return 1;
428 }
429
430 BC_FileBoxDelete::BC_FileBoxDelete(int x, int y, BC_FileBox *filebox)
431  : BC_Button(x, y, BC_WindowBase::get_resources()->filebox_delete_images)
432 {
433         this->filebox = filebox;
434         set_tooltip(_("Delete files"));
435 }
436 int BC_FileBoxDelete::handle_event()
437 {
438         filebox->unlock_window();
439         filebox->delete_thread->start();
440         filebox->lock_window("BC_FileBoxDelete::handle_event");
441         return 1;
442 }
443
444 BC_FileBoxReload::BC_FileBoxReload(int x, int y, BC_FileBox *filebox)
445  : BC_Button(x, y, BC_WindowBase::get_resources()->filebox_reload_images)
446 {
447         this->filebox = filebox;
448         set_tooltip(_("Refresh"));
449 }
450 int BC_FileBoxReload::handle_event()
451 {
452         filebox->refresh();
453         return 1;
454 }
455
456
457
458
459
460
461
462
463
464
465 BC_FileBox::BC_FileBox(int x, int y, const char *init_path,
466                 const char *title, const char *caption, int show_all_files,
467                 int want_directory, int multiple_files, int h_padding)
468  : BC_Window(title, x, y,
469         BC_WindowBase::get_resources()->filebox_w,
470         BC_WindowBase::get_resources()->filebox_h,
471         400, 300, 1, 0, 1)
472 {
473         fs = new FileSystem;
474 //      if(want_directory)
475 //      {
476 //              fs->set_want_directory();
477 //              columns = DIRBOX_COLUMNS;
478 //              columns = FILEBOX_COLUMNS;
479 //      }
480 //      else
481         {
482                 columns = FILEBOX_COLUMNS;
483         }
484
485         list_column = new ArrayList<BC_ListBoxItem*>[columns];
486         column_type = new int[columns];
487         column_width = new int[columns];
488
489         filter_text = 0;
490         filter_popup = 0;
491         usethis_button = 0;
492
493         strcpy(this->caption, caption);
494         strcpy(this->current_path, init_path);
495         strcpy(this->submitted_path, init_path);
496         select_multiple = multiple_files;
497         this->want_directory = want_directory;
498         if(show_all_files) fs->set_show_all();
499         fs->complete_path(this->current_path);
500         strcpy(this->submitted_path, this->current_path);
501         fs->extract_dir(directory, this->current_path);
502         fs->extract_name(filename, this->current_path);
503
504 // printf("BC_FileBox::BC_FileBox %d '%s' '%s' '%s'\n",
505 // __LINE__,
506 // this->submitted_path,
507 // directory,
508 // filename);
509
510 //      if(want_directory)
511 //      {
512 //              for(int i = 0; i < columns; i++)
513 //              {
514 //                      column_type[i] = get_resources()->dirbox_columntype[i];
515 //                      column_width[i] = get_resources()->dirbox_columnwidth[i];
516 //                      column_titles[i] = BC_FileBox::columntype_to_text(column_type[i]);
517 //              }
518 //              sort_column = get_resources()->dirbox_sortcolumn;
519 //              sort_order = get_resources()->dirbox_sortorder;
520 //      }
521 //      else
522         {
523                 for(int i = 0; i < columns; i++)
524                 {
525                         column_type[i] = get_resources()->filebox_columntype[i];
526                         column_width[i] = get_resources()->filebox_columnwidth[i];
527                         column_titles[i] = (char*)BC_FileBox::columntype_to_text(column_type[i]);
528                 }
529                 sort_column = get_resources()->filebox_sortcolumn;
530                 sort_order = get_resources()->filebox_sortorder;
531         }
532
533
534
535 // Test if current directory exists
536         if(!fs->is_dir(directory))
537         {
538                 sprintf(directory, "~");
539                 fs->complete_path(directory);
540                 strcpy(current_path,directory);
541                 filename[0] = 0;
542         }
543         fs->set_current_dir(directory);
544
545         if(h_padding == -1)
546         {
547                 h_padding = BC_WindowBase::get_resources()->ok_images[0]->get_h() -
548                         20;
549         }
550         this->h_padding = h_padding;
551         delete_thread = new BC_DeleteThread(this);
552         rename_thread = 0;
553 }
554
555 BC_FileBox::~BC_FileBox()
556 {
557 // this has to be destroyed before tables, because it can call for an update!
558         delete newfolder_thread;
559         delete fs;
560         delete_tables();
561         for(int i = 0; i < TOTAL_ICONS; i++)
562                 delete icons[i];
563         filter_list.remove_all_objects();
564         delete [] list_column;
565         delete [] column_type;
566         delete [] column_width;
567         delete delete_thread;
568         delete rename_thread;
569         recent_dirs.remove_all_objects();
570 }
571
572 void BC_FileBox::create_objects()
573 {
574         int x = 10, y = 10;
575         BC_Resources *resources = BC_WindowBase::get_resources();
576         int directory_title_margin = MAX(20,
577                 resources->filebox_text_images[0]->get_h());
578
579 // Directories aren't filtered in FileSystem so skip this
580         if(!want_directory)
581         {
582                 filter_list.append(new BC_ListBoxItem("*"));
583                 filter_list.append(new BC_ListBoxItem("[*.mkv]"));
584                 filter_list.append(new BC_ListBoxItem("[*.mp4]"));
585                 filter_list.append(new BC_ListBoxItem("[*.mp2][*.mp3][*.wav]"));
586                 filter_list.append(new BC_ListBoxItem("[*.avi][*.mpg][*.m2v][*.m1v][*.mov]"));
587                 filter_list.append(new BC_ListBoxItem("*.xml"));
588                 fs->set_filter(get_resources()->filebox_filter);
589         }
590
591         create_icons();
592         create_tables();
593
594         add_subwindow(ok_button = new BC_FileBoxOK(this));
595         if(want_directory)
596                 add_subwindow(usethis_button = new BC_FileBoxUseThis(this));
597         add_subwindow(cancel_button = new BC_FileBoxCancel(this));
598
599         add_subwindow(new BC_Title(x, y, caption));
600
601         x = get_w() - resources->filebox_icons_images[0]->get_w() - 10;
602
603         add_subwindow(icon_button = new BC_FileBoxIcons(x, y, this));
604         x -= resources->filebox_text_images[0]->get_w() + 5;
605
606         add_subwindow(text_button = new BC_FileBoxText(x, y, this));
607         x -= resources->filebox_newfolder_images[0]->get_w() + 5;
608
609         add_subwindow(folder_button = new BC_FileBoxNewfolder(x, y, this));
610         x -= resources->filebox_delete_images[0]->get_w() + 5;
611
612         add_subwindow(rename_button = new BC_FileBoxRename(x, y, this));
613         x -= resources->filebox_delete_images[0]->get_w() + 5;
614
615         add_subwindow(delete_button = new BC_FileBoxDelete(x, y, this));
616         x -= resources->filebox_reload_images[0]->get_w() + 5;
617
618         add_subwindow(reload_button = new BC_FileBoxReload(x, y, this));
619         x -= resources->filebox_updir_images[0]->get_w() + 5;
620
621         add_subwindow(updir_button = new BC_FileBoxUpdir(x, y, this));
622
623         x = 10;
624         y += directory_title_margin + 3;
625
626         add_subwindow(recent_popup = new BC_FileBoxRecent(this, x, y));
627         add_subwindow(directory_title = new BC_FileBoxDirectoryText(x, y, this));
628         directory_title->reposition_window(x, y, get_w() - recent_popup->get_w() -  20, 1);
629         x += directory_title->get_w() + 8;
630         recent_popup->reposition_window(x, y, directory_title->get_w(), 200);
631
632         x = 10;
633         y += directory_title->get_h() + 5;
634
635         int newest_id = 0, newest = -1;
636         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++) {
637                 if( !resources->filebox_history[i].path[0] ) continue;
638                 if( resources->filebox_history[i].id > newest_id ) {
639                         newest_id = resources->filebox_history[i].id;
640                         newest = i;
641                 }
642         }
643         if( newest >= 0 ) {
644                 strcpy(directory, resources->filebox_history[newest].path);
645                 fs->change_dir(directory, 0);
646                 strcpy(directory, fs->get_current_dir());
647                 directory_title->update(fs->get_current_dir());
648         }
649         fs->set_sort_order(sort_order);
650         fs->set_sort_field(column_type[sort_column]);
651         fs->update(directory);
652
653 // Create recent dir list
654         create_history();
655         update_history();
656
657         listbox = 0;
658         create_listbox(x, y, get_display_mode());
659         y += listbox->get_h() + 10;
660         add_subwindow(textbox = new BC_FileBoxTextBox(x, y, this));
661         y += textbox->get_h() + 10;
662
663         if(!want_directory) {
664                 add_subwindow(filter_text = new BC_FileBoxFilterText(x, y, this));
665                 add_subwindow(filter_popup =
666                         new BC_FileBoxFilterMenu(x + filter_text->get_w(), y, this));;
667         }
668
669 // listbox has to be active because refresh might be called from newfolder_thread
670         listbox->activate();
671         newfolder_thread = new BC_NewFolderThread(this);
672
673         rename_thread = new BC_RenameThread(this);
674
675         refresh();
676         show_window();
677 }
678
679 int BC_FileBox::get_listbox_w()
680 {
681         return get_w() - 20;
682 }
683
684 int BC_FileBox::get_listbox_h(int y)
685 {
686         int result = get_h() - y - h_padding - 10;
687         if(want_directory)
688                 result -= BC_WindowBase::get_resources()->dirbox_margin;
689         else
690                 result -= BC_WindowBase::get_resources()->filebox_margin;
691
692         return result;
693 }
694
695 int BC_FileBox::create_icons()
696 {
697         for(int i = 0; i < TOTAL_ICONS; i++)
698         {
699                 icons[i] = new BC_Pixmap(this,
700                         BC_WindowBase::get_resources()->type_to_icon[i],
701                         PIXMAP_ALPHA);
702         }
703         return 0;
704 }
705
706 int BC_FileBox::resize_event(int w, int h)
707 {
708         draw_background(0, 0, w, h);
709         flash(0);
710
711 // OK button handles resize event itself
712 //      ok_button->reposition_window(ok_button->get_x(),
713 //              h - (get_h() - ok_button->get_y()));
714 //      cancel_button->reposition_window(w - (get_w() - cancel_button->get_x()),
715 //              h - (get_h() - cancel_button->get_y()));
716         if(usethis_button)
717                 usethis_button->reposition_window(w / 2 - 50,
718                         h - (get_h() - usethis_button->get_y()));
719
720
721         if(filter_popup) filter_popup->reposition_window(w - (get_w() - filter_popup->get_x()),
722                 h - (get_h() - filter_popup->get_y()),
723                 w - 30,
724                 0);
725
726
727         if(filter_text) filter_text->reposition_window(filter_text->get_x(),
728                 h - (get_h() - filter_text->get_y()),
729                 w - (get_w() - filter_text->get_w()),
730                 1);
731         directory_title->reposition_window(
732                 directory_title->get_x(),
733                 directory_title->get_y(),
734                 get_w() - recent_popup->get_w() -  20,
735                 1);
736         recent_popup->reposition_window(
737                 directory_title->get_x() + directory_title->get_w() + 8,
738                 directory_title->get_y(),
739                 directory_title->get_w() + recent_popup->get_w(),
740                 recent_popup->get_h());
741         textbox->reposition_window(textbox->get_x(),
742                 h - (get_h() - textbox->get_y()),
743                 w - (get_w() - textbox->get_w()),
744                 1);
745         listbox->reposition_window(listbox->get_x(),
746                 listbox->get_y(),
747                 w - (get_w() - listbox->get_w()),
748                 h - (get_h() - listbox->get_h()),
749                 0);
750         int dx = w - get_w();
751         icon_button->reposition_window(icon_button->get_x()+dx, icon_button->get_y());
752         text_button->reposition_window(text_button->get_x()+dx, text_button->get_y());
753         folder_button->reposition_window(folder_button->get_x()+dx, folder_button->get_y());
754         rename_button->reposition_window(rename_button->get_x()+dx, rename_button->get_y());
755         reload_button->reposition_window(reload_button->get_x()+dx, reload_button->get_y());
756         delete_button->reposition_window(delete_button->get_x()+dx, delete_button->get_y());
757         updir_button->reposition_window(updir_button->get_x()+dx, updir_button->get_y());
758         set_w(w);  set_h(h);
759         get_resources()->filebox_w = get_w();
760         get_resources()->filebox_h = get_h();
761         flush();
762         return 1;
763 }
764
765 int BC_FileBox::keypress_event()
766 {
767         switch(get_keypress())
768         {
769                 case 'w':
770                         if(ctrl_down()) set_done(1);
771                         return 1;
772                         break;
773         }
774         return 0;
775 }
776
777 int BC_FileBox::close_event()
778 {
779         set_done(1);
780         return 1;
781 }
782
783 int BC_FileBox::handle_event()
784 {
785         return 0;
786 }
787
788 int BC_FileBox::extract_extension(char *out, const char *in)
789 {
790         *out = 0;
791         int i = strlen(in);
792         while( --i>=0 && in[i]!='.' );
793         if( i >= 0 ) strcpy(out, &in[++i]);
794         return 0;
795 }
796
797 static inline int64_t ipow(int m, int n)
798 {
799         int64_t v = 1;
800         for( int64_t vv=m; n>0; vv*=vv,n>>=1 ) if( n & 1 ) v *= vv;
801         return v;
802 }
803
804 int BC_FileBox::create_tables()
805 {
806         delete_tables();
807         char string[BCTEXTLEN];
808         BC_ListBoxItem *new_item;
809
810         fs->set_sort_order(sort_order);
811         fs->set_sort_field(column_type[sort_column]);
812
813         for(int i = 0; i < fs->total_files(); i++)
814         {
815                 FileItem *file_item = fs->get_entry(i);
816                 int is_dir = file_item->is_dir;
817                 BC_Pixmap* icon = get_icon(file_item->name, is_dir);
818
819 // Name entry
820                 new_item = new BC_ListBoxItem(file_item->name,
821                         icon,
822                         is_dir ? get_resources()->directory_color : get_resources()->file_color);
823                 if(is_dir) new_item->set_searchable(0);
824                 list_column[column_of_type(FILEBOX_NAME)].append(new_item);
825
826 // Size entry
827 //              if(!want_directory)
828 //              {
829                         if(!is_dir)
830                         {
831                                 int64_t size = file_item->size;
832 #if 1
833                                 int len = 1;
834                                 static const char *suffix[] = { "", "K", "M", "G", "T", "P" };
835                                 for( int64_t s=size; len<15 && (s/=10)>0; ++len );
836                                 int drop = len-3;
837                                 if( drop > 0 ) {
838                                         size /= ipow(10,drop);
839                                         int sfx = (len-1)/3;
840                                         int digits = (sfx+1)*3 - len;
841                                         int64_t frac = ipow(10,digits);
842                                         int mantisa = size / frac;
843                                         int fraction = size - mantisa*frac;
844                                         if( fraction )
845                                                 sprintf(string, "%d.%0*d%s", mantisa, digits, fraction, suffix[sfx]);
846                                         else
847                                                 sprintf(string, "%d%s", mantisa, suffix[sfx]);
848                                 }
849                                 else
850 #endif
851                                         sprintf(string, "%jd", size);
852                                 new_item = new BC_ListBoxItem(string, get_resources()->file_color);
853                         }
854                         else
855                         {
856                                 new_item = new BC_ListBoxItem("", get_resources()->directory_color);
857                         }
858
859                         list_column[column_of_type(FILEBOX_SIZE)].append(new_item);
860 //              }
861
862 // Date entry
863                 if(!is_dir || 1)
864                 {
865                         struct tm mod_time;
866                         localtime_r(&file_item->mtime, &mod_time);
867                         sprintf(string, "%04d.%02d.%02d  %02d:%02d:%02d",
868                                 mod_time.tm_year+1900, mod_time.tm_mon+1, mod_time.tm_mday,
869                                 mod_time.tm_hour, mod_time.tm_min, mod_time.tm_sec);
870                         new_item = new BC_ListBoxItem(string, get_resources()->file_color);
871                 }
872                 else
873                 {
874                         new_item = new BC_ListBoxItem("", get_resources()->directory_color);
875                 }
876
877                 list_column[column_of_type(FILEBOX_DATE)].append(new_item);
878
879 // Extension entry
880 //              if(!want_directory)
881 //              {
882                         if(!is_dir) {
883                                 extract_extension(string, file_item->name);
884                                 new_item = new BC_ListBoxItem(string, get_resources()->file_color);
885                         }
886                         else {
887                                 new_item = new BC_ListBoxItem("", get_resources()->directory_color);
888                         }
889                         list_column[column_of_type(FILEBOX_EXTENSION)].append(new_item);
890 //              }
891         }
892
893         return 0;
894 }
895
896 int BC_FileBox::delete_tables()
897 {
898         for(int j = 0; j < columns; j++)
899         {
900                 list_column[j].remove_all_objects();
901         }
902         return 0;
903 }
904
905 BC_Pixmap* BC_FileBox::get_icon(char *path, int is_dir)
906 {
907         if( is_dir ) return icons[ICON_FOLDER];
908         int icon_type = ICON_UNKNOWN;
909         char *suffix = strrchr(path, '.');
910         if( suffix && *++suffix ) {
911                 suffix_to_type_t *stp = &BC_WindowBase::get_resources()->suffix_to_type[0];
912                 while( stp->suffix && strcasecmp(stp->suffix, suffix) ) ++stp;
913                 if( stp->icon_type ) icon_type = stp->icon_type;
914         }
915
916         return icons[icon_type];
917 }
918
919 const char* BC_FileBox::columntype_to_text(int type)
920 {
921         switch(type) {
922                 case FILEBOX_NAME: return FILEBOX_NAME_TEXT;
923                 case FILEBOX_SIZE: return FILEBOX_SIZE_TEXT;
924                 case FILEBOX_DATE: return FILEBOX_DATE_TEXT;
925                 case FILEBOX_EXTENSION: return FILEBOX_EXTENSION_TEXT;
926         }
927         return "";
928 }
929
930 int BC_FileBox::column_of_type(int type)
931 {
932         for(int i = 0; i < columns; i++)
933                 if(column_type[i] == type) return i;
934         return 0;
935 }
936
937
938
939 int BC_FileBox::refresh(int reset)
940 {
941         fs->set_sort_order(sort_order);
942         fs->set_sort_field(column_type[sort_column]);
943         if( reset >= 0 )
944                 fs->update(0);
945         else
946                 fs->update_sort();
947         create_tables();
948         listbox->set_master_column(column_of_type(FILEBOX_NAME), 0);
949         listbox->update(list_column, column_titles, column_width, columns,
950                 reset>0 ? 0 : listbox->get_xposition(),
951                 reset>0 ? 0 : listbox->get_yposition(),
952                 -1, 1);
953         return 0;
954 }
955
956 int BC_FileBox::update_filter(const char *filter)
957 {
958         fs->set_filter(filter);
959 //      fs->update(0);
960         refresh();
961         strcpy(get_resources()->filebox_filter, filter);
962
963         return 0;
964 }
965
966
967 void BC_FileBox::move_column(int src, int dst)
968 {
969         if(src != dst)
970         {
971                 ArrayList<BC_ListBoxItem*> *new_columns =
972                         new ArrayList<BC_ListBoxItem*>[columns];
973                 int *new_types = new int[columns];
974                 int *new_widths = new int[columns];
975
976         // Fill in remaining columns with consecutive data
977                 for(int out_column = 0, in_column = 0;
978                         out_column < columns;
979                         out_column++,
980                         in_column++)
981                 {
982         // Copy destination column from src column
983                         if(out_column == dst)
984                         {
985                                 for(int i = 0; i < list_column[src].total; i++)
986                                 {
987                                         new_columns[out_column].append(list_column[src].values[i]);
988                                 }
989                                 new_types[out_column] = column_type[src];
990                                 new_widths[out_column] = column_width[src];
991                                 in_column--;
992                         }
993                         else
994                         {
995         // Skip source column
996                                 if(in_column == src) in_column++;
997                                 for(int i = 0; i < list_column[src].total; i++)
998                                 {
999                                         new_columns[out_column].append(list_column[in_column].values[i]);
1000                                 }
1001                                 new_types[out_column] = column_type[in_column];
1002                                 new_widths[out_column] = column_width[in_column];
1003                         }
1004                 }
1005
1006         // Swap tables
1007                 delete [] list_column;
1008                 delete [] column_type;
1009                 delete [] column_width;
1010                 list_column = new_columns;
1011                 column_type = new_types;
1012                 column_width = new_widths;
1013
1014                 for(int i = 0; i < columns; i++)
1015                 {
1016                         get_resources()->filebox_columntype[i] = column_type[i];
1017                         get_resources()->filebox_columnwidth[i] = column_width[i];
1018                         column_titles[i] = (char*)BC_FileBox::columntype_to_text(column_type[i]);
1019                 }
1020         }
1021
1022         refresh();
1023 }
1024
1025
1026 int BC_FileBox::submit_dir(char *dir)
1027 {
1028         strcpy(directory, dir);
1029         fs->join_names(current_path, directory, filename);
1030
1031 // printf("BC_FileBox::submit_dir %d '%s' '%s' '%s'\n",
1032 // __LINE__,
1033 // current_path,
1034 // directory,
1035 // filename);
1036         strcpy(submitted_path, current_path);
1037         fs->change_dir(dir, 0);
1038         refresh(1);
1039         directory_title->update(fs->get_current_dir());
1040         if(want_directory)
1041                 textbox->update(fs->get_current_dir());
1042         else
1043                 textbox->update(filename);
1044         listbox->reset_query();
1045         return 0;
1046 }
1047
1048 int BC_FileBox::submit_file(const char *path, int use_this)
1049 {
1050         char path1[BCTEXTLEN];
1051         strcpy(path1, path);
1052         char *cp = strchr(path1,'\n');
1053         if( cp ) *cp = 0;
1054
1055 // Deactivate textbox to hide suggestions
1056         textbox->deactivate();
1057
1058 // If file wanted, take the current directory as the desired file.
1059 // If directory wanted, ignore it.
1060         if(!path1[0] && !want_directory)
1061         {
1062 // save complete path
1063                 strcpy(this->current_path, directory);
1064 // save complete path
1065                 strcpy(this->submitted_path, directory);
1066                 update_history();
1067 // Zero out filename
1068                 filename[0] = 0;
1069                 set_done(0);
1070                 return 0;
1071         }
1072
1073 // is a directory, change directories
1074         if(fs->is_dir(path1) && !use_this)
1075         {
1076                 fs->change_dir(path1, 0);
1077                 refresh(1);
1078                 directory_title->update(fs->get_current_dir());
1079                 strcpy(this->current_path, fs->get_current_dir());
1080                 strcpy(this->submitted_path, fs->get_current_dir());
1081                 strcpy(this->directory, fs->get_current_dir());
1082                 update_history();
1083                 filename[0] = 0;
1084                 if(want_directory)
1085                         textbox->update(fs->get_current_dir());
1086                 else
1087                         textbox->update("");
1088                 listbox->reset_query();
1089                 return 1;
1090         }
1091         else
1092 // Is a file or desired directory.  Quit the operation.
1093         {
1094                 char path2[BCTEXTLEN];
1095                 strcpy(path2, path1);
1096
1097 // save directory for defaults
1098                 fs->extract_dir(directory, path2);
1099
1100 // Just take the directory
1101                 if(want_directory)
1102                 {
1103                         filename[0] = 0;
1104                         strcpy(path2, directory);
1105                 }
1106                 else
1107 // Take the complete path
1108                 {
1109                         fs->extract_name(filename, path2);     // save filename
1110                 }
1111
1112                 fs->complete_path(path2);
1113                 strcpy(this->current_path, path2);          // save complete path
1114                 strcpy(this->submitted_path, path2);          // save complete path
1115                 update_history();
1116                 newfolder_thread->interrupt();
1117                 set_done(0);
1118                 return 0;
1119         }
1120         return 0;
1121 }
1122
1123 void BC_FileBox::update_history()
1124 {
1125 // Look for path already in history
1126         BC_Resources *resources = get_resources();
1127         char path[BCTEXTLEN];
1128         strcpy(path, directory);
1129 // enfore one trailing slash
1130         char *cp = path;
1131         while( *cp && *cp != '\n' ) ++cp;
1132         while( cp > path && *(cp-1) == '/' ) --cp;
1133         *cp++ = '/';  *cp = 0;
1134
1135 //      int new_slot = FILEBOX_HISTORY_SIZE - 1;
1136
1137         for(int i = FILEBOX_HISTORY_SIZE - 1; i >= 0; i--)
1138         {
1139                 if(resources->filebox_history[i].path[0] &&
1140                         !strcmp(resources->filebox_history[i].path, path))
1141                 {
1142 // Got matching path.
1143 // Update ID.
1144                         resources->filebox_history[i].id = resources->get_filebox_id();
1145                         return;
1146                 }
1147 // // Shift down from this point.
1148 //                      while(i > 0)
1149 //                      {
1150 //                              strcpy(resources->filebox_history[i],
1151 //                                      resources->filebox_history[i - 1]);
1152 //                              if(resources->filebox_history[i][0]) new_slot--;
1153 //                              i--;
1154 //                      }
1155 //                      break;
1156 //              }
1157 //              else
1158 //                      if(resources->filebox_history[i][0])
1159 //                              new_slot--;
1160 //              else
1161 //                      break;
1162         }
1163
1164 // Remove oldest entry if full
1165         if(resources->filebox_history[FILEBOX_HISTORY_SIZE - 1].path[0])
1166         {
1167                 int oldest_id = 0x7fffffff;
1168                 int oldest = 0;
1169                 for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
1170                 {
1171                         if(resources->filebox_history[i].path[0] &&
1172                                 resources->filebox_history[i].id < oldest_id)
1173                         {
1174                                 oldest_id = resources->filebox_history[i].id;
1175                                 oldest = i;
1176                         }
1177                 }
1178
1179                 for(int i = oldest; i < FILEBOX_HISTORY_SIZE - 1; i++)
1180                 {
1181                         strcpy(resources->filebox_history[i].path,
1182                                 resources->filebox_history[i + 1].path);
1183                         resources->filebox_history[i].id =
1184                                 resources->filebox_history[i + 1].id;
1185                 }
1186         }
1187
1188 // Create new entry
1189         strcpy(resources->filebox_history[FILEBOX_HISTORY_SIZE - 1].path, path);
1190         resources->filebox_history[FILEBOX_HISTORY_SIZE - 1].id = resources->get_filebox_id();
1191
1192 // Alphabetize
1193         int done = 0;
1194         while(!done)
1195         {
1196                 done = 1;
1197                 for(int i = 1; i < FILEBOX_HISTORY_SIZE; i++)
1198                 {
1199                         if( (resources->filebox_history[i - 1].path[0] &&
1200                                 resources->filebox_history[i].path[0] &&
1201                                 strcasecmp(resources->filebox_history[i - 1].path,
1202                                         resources->filebox_history[i].path) > 0) ||
1203                                 (resources->filebox_history[i - 1].path[0] == 0 &&
1204                                         resources->filebox_history[i].path[0]) )
1205                         {
1206                                 done = 0;
1207                                 char temp[BCTEXTLEN];
1208                                 int id_temp;
1209                                 strcpy(temp, resources->filebox_history[i - 1].path);
1210                                 id_temp = resources->filebox_history[i - 1].id;
1211                                 strcpy(resources->filebox_history[i - 1].path,
1212                                         resources->filebox_history[i].path);
1213                                 resources->filebox_history[i - 1].id =
1214                                         resources->filebox_history[i].id;
1215                                 strcpy(resources->filebox_history[i].path, temp);
1216                                 resources->filebox_history[i].id = id_temp;
1217                         }
1218                 }
1219         }
1220
1221 //      if(new_slot < 0)
1222 //      {
1223 //              for(int i = FILEBOX_HISTORY_SIZE - 1; i > 0; i--)
1224 //              {
1225 //                      strcpy(resources->filebox_history[i],
1226 //                                      resources->filebox_history[i - 1]);
1227 //              }
1228 //              new_slot = 0;
1229 //      }
1230 //
1231 //      strcpy(resources->filebox_history[new_slot], path);
1232
1233         create_history();
1234         recent_popup->update(&recent_dirs, 0, 0, 1);
1235 }
1236
1237 void BC_FileBox::create_history()
1238 {
1239         BC_Resources *resources = get_resources();
1240         recent_dirs.remove_all_objects();
1241         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++) {
1242                 if(resources->filebox_history[i].path[0]) {
1243                         recent_dirs.append(new BC_ListBoxItem(resources->filebox_history[i].path));
1244                 }
1245         }
1246 }
1247
1248
1249 int BC_FileBox::get_display_mode()
1250 {
1251         return top_level->get_resources()->filebox_mode;
1252 }
1253
1254 void BC_FileBox::create_listbox(int x, int y, int mode)
1255 {
1256         if(listbox && listbox->get_display_mode() != mode)
1257         {
1258                 delete listbox;
1259                 listbox = 0;
1260                 top_level->get_resources()->filebox_mode = mode;
1261         }
1262
1263         if(!listbox)
1264                 add_subwindow(listbox = new BC_FileBoxListBox(x, y, this));
1265 }
1266
1267 char* BC_FileBox::get_path(int selection)
1268 {
1269         if(selection == 0)
1270         {
1271                 return get_submitted_path();
1272         }
1273         else
1274         {
1275                 BC_ListBoxItem *item = listbox->get_selection(
1276                         column_of_type(FILEBOX_NAME), selection - 1);
1277                 if(item)
1278                 {
1279                         fs->join_names(string, directory, item->get_text());
1280                         return string;
1281                 }
1282         }
1283         return 0;
1284 }
1285
1286 char* BC_FileBox::get_submitted_path()
1287 {
1288         return submitted_path;
1289 }
1290
1291 char* BC_FileBox::get_current_path()
1292 {
1293 //printf("BC_FileBox::get_current_path 1 %s\n", current_path);
1294         return current_path;
1295 }
1296
1297 char* BC_FileBox::get_newfolder_title()
1298 {
1299         char *letter2 = strchr(title, ':');
1300         new_folder_title[0] = 0;
1301         if(letter2)
1302         {
1303                 memcpy(new_folder_title, title, letter2 - title);
1304                 new_folder_title[letter2 - title] = 0;
1305         }
1306
1307         strcat(new_folder_title, _(": New folder"));
1308
1309         return new_folder_title;
1310 }
1311
1312 char* BC_FileBox::get_rename_title()
1313 {
1314         char *letter2 = strchr(title, ':');
1315         new_folder_title[0] = 0;
1316         if(letter2)
1317         {
1318                 memcpy(new_folder_title, title, letter2 - title);
1319                 new_folder_title[letter2 - title] = 0;
1320         }
1321
1322         strcat(new_folder_title, _(": Rename"));
1323
1324         return new_folder_title;
1325 }
1326
1327 char* BC_FileBox::get_delete_title()
1328 {
1329         char *letter2 = strchr(title, ':');
1330         new_folder_title[0] = 0;
1331         if(letter2)
1332         {
1333                 memcpy(new_folder_title, title, letter2 - title);
1334                 new_folder_title[letter2 - title] = 0;
1335         }
1336
1337         strcat(new_folder_title, _(": Delete"));
1338
1339         return new_folder_title;
1340 }
1341
1342 void BC_FileBox::delete_files()
1343 {
1344 // Starting at 1 causes it to ignore what's in the textbox.
1345         int i = 1;
1346         char *path;
1347         FileSystem fs;
1348         while((path = get_path(i)))
1349         {
1350 // Not directory.  Remove it.
1351                 if(!fs.is_dir(path))
1352                 {
1353 //printf("BC_FileBox::delete_files: removing \"%s\"\n", path);
1354                         remove(path);
1355                 }
1356                 i++;
1357         }
1358         refresh();
1359 }
1360
1361 BC_Button* BC_FileBox::get_ok_button()
1362 {
1363         return ok_button;
1364 }
1365
1366 BC_Button* BC_FileBox::get_cancel_button()
1367 {
1368         return cancel_button;
1369 }
1370