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