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