Exciting new Alt/h help key provided by sge (Georgy) with many thanks!
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / binfolder.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "awindowgui.h"
22 #include "bchash.h"
23 #include "binfolder.h"
24 #include "cstrdup.h"
25 #include "edl.h"
26 #include "filexml.h"
27 #include "guicast.h"
28 #include "indexable.h"
29 #include "language.h"
30 #include "localsession.h"
31 #include "mainerror.h"
32 #include "mainsession.h"
33 #include "mutex.h"
34 #include "mwindow.h"
35
36 #include <time.h>
37
38 const char *BinFolderEnabled::types[] = {
39         N_("Off"),
40         N_("And"),
41         N_("Or"),
42         N_("And Not"),
43         N_("Or Not"),
44 };
45
46 const char *BinFolderTarget::types[] = {
47         N_("Patterns"),
48         N_("Filesize"),
49         N_("Time"),
50         N_("Track type"),
51         N_("Width"),
52         N_("Height"),
53         N_("Framerate"),
54         N_("Samplerate"),
55         N_("Channels"),
56         N_("Duration"),
57 };
58
59 const char *BinFolderOp::types[] = {
60         N_("Around"),
61         N_("Eq  =="),
62         N_("Ge  >="),
63         N_("Gt  > "),
64         N_("Ne  !="),
65         N_("Le  <="),
66         N_("Lt  < "),
67         N_("Matches"),
68 };
69
70 static const struct {
71         const char *sfx, *sfxs;
72         double radix;
73 } scan_ranges[] = {
74         { N_("min"),   N_("mins"),   60, },
75         { N_("hour"),  N_("hours"),  60*60, },
76         { N_("day"),   N_("days"),   24*60*60, },
77         { N_("week"),  N_("weeks"),  24*60*60*7, },
78         { N_("month"), N_("months"), 24*60*60*31, },
79         { N_("year"),  N_("years"),  24*60*60*365.25, },
80 };
81 #define SCAN_RANGE_DAYS 2
82 #define DBL_INF 9007199254740991.
83
84 static int scan_inf(const char *cp, char *&bp)
85 {
86         bp = (char *)cp;
87         const char *inf = _("inf");
88         const int linf = strlen(inf);
89         if( strncasecmp(cp,inf,linf) ) return 0;
90         bp = (char *)(cp + linf);
91         return 1;
92 }
93
94 static double scan_no(const char *cp, char *&bp)
95 {
96         while( *cp == ' ' ) ++cp;
97         if( scan_inf(cp, bp) ) return DBL_INF;
98         if( *cp == '+' ) return 0;
99         double v = strtod(cp, &bp);
100         switch( *bp ) {
101         case 'T': v *= 1099511627776.;  break;
102         case 't': v *= 1e12;            break;
103         case 'G': v *= 1073741824.;     break;
104         case 'g': v *= 1e9;             break;
105         case 'M': v *= 1048576.;        break;
106         case 'm': v *= 1e6;             break;
107         case 'K': v *= 1024.;           break;
108         case 'k': v *= 1e3;             break;
109         default:  return v;
110         }
111         ++bp;
112         return v;
113 }
114
115 static void scan_around(const char *cp, char *&bp, double &v, double &a)
116 {
117         v = 0;  a = -1;
118         double sv = scan_no(cp, bp);
119         if( bp > cp ) v = sv;
120         if( *bp == '+' ) {
121                 double sa = scan_no(cp=bp+1, bp);
122                 if( bp > cp ) a = sa;
123         }
124 }
125
126 static void show_no(double v, char *&cp, char *ep, const char *fmt="%0.0f")
127 {
128         if( v == DBL_INF ) {
129                 cp += snprintf(cp, ep-cp, "%s", _("inf"));
130                 return;
131         }
132         const char *sfx = 0;
133         static const struct { double radix; const char *sfx; } sfxs[] = {
134                 { 1024.,          "K" }, { 1e3,  "k" },
135                 { 1048576.,       "M" }, { 1e6,  "m" },
136                 { 1073741824.,    "G" }, { 1e9,  "g" },
137                 { 1099511627776., "T" }, { 1e12, "t" },
138         };
139         for( int i=sizeof(sfxs)/sizeof(sfxs[0]); --i>=0; ) {
140                 if( v < sfxs[i].radix ) continue;
141                 if( fmod(v, sfxs[i].radix) == 0 ) {
142                         v /= sfxs[i].radix;
143                         sfx = sfxs[i].sfx;
144                         break;
145                 }
146         }
147         cp += snprintf(cp, ep-cp, fmt, v);
148         if( sfx ) cp += snprintf(cp, ep-cp, "%s", sfx);
149 }
150
151 static double scan_duration(const char *cp, char *&bp)
152 {
153         if( scan_inf(cp, bp) ) return DBL_INF;
154         double secs = 0;
155         while( *cp ) {
156                 double v = strtod(cp, &bp);
157                 if( cp >= bp ) break;
158                 int k = sizeof(scan_ranges)/sizeof(scan_ranges[0]);
159                 while( --k >= 0 ) {
160                         const char *tsfx  = _(scan_ranges[k].sfx);
161                         int lsfx  = strlen(tsfx), msfx  = strncasecmp(bp, tsfx,  lsfx);
162                         const char *tsfxs = _(scan_ranges[k].sfxs);
163                         int lsfxs = strlen(tsfxs), msfxs = strncasecmp(bp, tsfxs, lsfxs);
164                         int len = !msfx && !msfxs ? (lsfx > lsfxs ? lsfx : lsfxs) :
165                                 !msfx ? lsfx : !msfxs ? lsfxs : -1;
166                         if( len >= 0 ) {
167                                 secs += v * scan_ranges[k].radix;
168                                 bp += len;
169                                 break;
170                         }
171                 }
172                 if( k < 0 ) {
173                         int hour = 0, mins = 0;
174                         if( *bp == ':' && v == (int)v ) {
175                                 mins = v;
176                                 v = strtod(cp=bp+1, &bp);
177                                 if( *bp == ':' && v == (int)v ) {
178                                         hour = mins;  mins = v;
179                                         v = strtod(cp=bp+1, &bp);
180                                 }
181                         }
182                         secs += hour*3600 + mins*60 + v;
183                 }
184                 while( *bp && (*bp<'0' || *bp>'9') ) ++bp;
185                 cp = bp;
186         }
187         return secs;
188 }
189
190 static void show_duration(double v, char *&cp, char *ep)
191 {
192         if( v == DBL_INF ) {
193                 cp += snprintf(cp, ep-cp, "%s", _("inf"));
194                 return;
195         }
196         double secs = v;  char *bp = cp;
197         int k = sizeof(scan_ranges)/sizeof(scan_ranges[0]);
198         while( --k >= SCAN_RANGE_DAYS ) {
199                 if( secs >= scan_ranges[k].radix ) {
200                         int v = secs/scan_ranges[k].radix;
201                         secs -= v * scan_ranges[k].radix;
202                         cp += snprintf(cp, ep-cp,"%d%s", v,
203                                 v > 1 ? _(scan_ranges[k].sfxs) : _(scan_ranges[k].sfx));
204                 }
205         }
206         if( secs > 0 ) {
207                 if( cp > bp && cp < ep ) *cp++ = ' ';
208                 int64_t n = secs; int hour = n/3600, min = (n/60)%60, sec = n%60;
209                 if( hour > 0 ) cp += snprintf(cp, ep-cp, "%d:", hour);
210                 if( hour > 0 || min > 0 ) cp += snprintf(cp, ep-cp, "%02d:", min);
211                 cp += snprintf(cp, ep-cp, n>=10 ? "%02d" : "%d", sec);
212         }
213 }
214
215 static int64_t scan_date(const char *cp, char *&bp)
216 {
217         double year=0, mon=1, day=1;
218         double hour=0, min=0;
219         bp = (char *)cp;
220         while( *bp == ' ' ) ++bp;
221         if( *bp == '+' ) return 0;
222         double secs = strtod(cp=bp, &bp);
223         if( *bp == '/' && secs == (int)secs ) {
224                 year = secs;  secs = 0;
225                 mon = strtod(cp=bp+1, &bp);
226                 if( *bp == '/' && mon == (int)mon ) {
227                         day = strtod(cp=bp+1, &bp);
228                         while( *bp == ' ' ) ++bp;
229                         secs = *bp != '+' ? strtod(cp=bp, &bp) : 0;
230                 }
231         }
232         if( *bp == ':' && secs == (int)secs ) {
233                 hour = secs;  secs = 0;
234                 min = strtod(cp=bp+1, &bp);
235                 if( *bp == ':' && min == (int)min ) {
236                         secs = strtod(cp=bp+1, &bp);
237                 }
238         }
239         struct tm ttm;  memset(&ttm, 0, sizeof(ttm));
240         ttm.tm_year = year-1900;  ttm.tm_mon = mon-1;  ttm.tm_mday = day;
241         ttm.tm_hour = hour;       ttm.tm_min = min;    ttm.tm_sec = secs;
242         time_t t = mktime(&ttm);
243         return (int64_t)t;
244 }
245
246 static void show_date(time_t t, char *&cp, char *ep)
247 {
248         struct tm tm;  localtime_r(&t, &tm);
249         cp += snprintf(cp, ep-cp, "%04d/%02d/%02d %02d:%02d:%02d",
250                 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
251                 tm.tm_hour, tm.tm_min, tm.tm_sec);
252 }
253
254 double BinFolder::matches_indexable(Indexable *idxbl)
255 {
256         double result = -1;
257         for( int i=0; i<filters.size(); ++i ) {
258                 BinFolderFilter *filter = filters[i];
259                 double ret = filter->op->test(filter->target, idxbl);
260                 switch( filter->enabled->type ) {
261                 case FOLDER_ENABLED_OR: {
262                         if( result < 0 ) result = ret;
263                         break; }
264                 case FOLDER_ENABLED_AND: {
265                         if( ret < 0 ) result = -1;
266                         break; }
267                 case FOLDER_ENABLED_OR_NOT: {
268                         if( ret < 0 ) result = 1;
269                         break; }
270                 case FOLDER_ENABLED_AND_NOT: {
271                         if( ret >= 0 ) result = -1;
272                         break; }
273                 }
274         }
275         return result;
276 }
277
278
279 BinFolder::BinFolder(int awindow_folder, int is_clips, const char *title)
280 {
281         this->awindow_folder = awindow_folder;
282         this->is_clips = is_clips;
283         char *bp = this->title;
284         int len = sizeof(this->title);
285         while( --len>0 && *title ) *bp++ = *title++;
286         *bp = 0;
287 }
288
289 BinFolder::BinFolder(BinFolder &that)
290 {
291         copy_from(&that);
292 }
293
294 void BinFolder::copy_from(BinFolder *that)
295 {
296         strcpy(title, that->title);
297         awindow_folder = that->awindow_folder;
298         is_clips = that->is_clips;
299         filters.copy_from(&that->filters);
300 }
301
302 BinFolder::~BinFolder()
303 {
304 }
305
306 void BinFolder::save_xml(FileXML *file)
307 {
308         file->tag.set_title("FOLDER");
309         file->tag.set_property("TITLE", title);
310         file->tag.set_property("AWINDOW_FOLDER", awindow_folder);
311         file->tag.set_property("IS_CLIPS", is_clips);
312         file->append_tag();
313         file->append_newline();
314         for( int i=0; i<filters.size(); ++i )
315                 filters[i]->save_xml(file);
316         file->tag.set_title("/FOLDER");
317         file->append_tag();
318         file->append_newline();
319 }
320
321 int BinFolder::load_xml(FileXML *file)
322 {
323         title[0] = 0;
324         file->tag.get_property("TITLE", title);
325         awindow_folder = file->tag.get_property("AWINDOW_FOLDER", -1);
326         is_clips = file->tag.get_property("IS_CLIPS", 0);
327         filters.remove_all_objects();
328
329         int ret = 0;
330         while( !(ret=file->read_tag()) ) {
331                 if( file->tag.title_is("/FOLDER") ) break;
332                 if( file->tag.title_is("FILTER") ) {
333                         BinFolderFilter *filter = new BinFolderFilter();
334                         filter->load_xml(file);
335                         filters.append(filter);
336                 }
337         }
338         return ret;
339 }
340
341 int BinFolder::add_patterns(ArrayList<Indexable*> *drag_idxbls, int use_basename)
342 {
343         int n = drag_idxbls->size();
344         if( !n ) return 1;
345         Indexable *idxbl;
346         int len = 0;
347         for( int i=0; i<n; ++i ) {
348                 idxbl = drag_idxbls->get(i);
349                 if( !idxbl->is_asset &&
350                     idxbl->folder_no == AW_PROXY_FOLDER )
351                         continue;
352
353                 const char *tp = idxbl->get_title();
354                 if( use_basename && idxbl->is_asset ) {
355                         const char *cp = strrchr(tp, '/');
356                         if( cp ) tp = cp + 1;
357                         len += 1;  // "*" + fn
358                 }
359                 len += strlen(tp) + 1;
360         }
361         if( !len ) return 1;
362         char *pats = new char[len+1], *bp = pats;
363         for( int i=0; i<n; ++i ) {
364                 idxbl = drag_idxbls->get(i);
365                 if( !idxbl->is_asset &&
366                     idxbl->folder_no == AW_PROXY_FOLDER )
367                         continue;
368                 if( i > 0 ) *bp++ = '\n';
369                 const char *tp = idxbl->get_title();
370                 if( use_basename && idxbl->is_asset ) {
371                         const char *cp = strrchr(tp, '/');
372                         if( cp ) tp = cp + 1;
373                         *bp++ = '*';
374                 }
375                 while( *tp ) *bp++ = *tp++;
376         }
377         *bp = 0;
378 // new pattern filter
379         BinFolderFilter *filter = new BinFolderFilter();
380         filter->update_enabled(FOLDER_ENABLED_OR);
381         filter->update_target(FOLDER_TARGET_PATTERNS);
382         filter->update_op(FOLDER_OP_MATCHES);
383         BinFolderTargetPatterns *patterns = (BinFolderTargetPatterns *)(filter->target);
384         patterns->update(pats);
385         filters.append(filter);
386         return 0;
387 }
388
389
390 double BinFolders::matches_indexable(int folder, Indexable *idxbl)
391 {
392         int k = size();
393         BinFolder *bin_folder = 0;
394         while( --k>=0 && (bin_folder=get(k)) && bin_folder->awindow_folder!=folder );
395         if( k < 0 ) return -1;
396         if( bin_folder->is_clips && idxbl->folder_no != AW_CLIP_FOLDER ) return -1;
397         if( !bin_folder->is_clips && idxbl->folder_no != AW_MEDIA_FOLDER ) return -1;
398         return bin_folder->matches_indexable(idxbl);
399 }
400
401 void BinFolders::save_xml(FileXML *file)
402 {
403         file->tag.set_title("FOLDERS");
404         file->append_tag();
405         file->append_newline();
406         for( int i=0; i<size(); ++i )
407                 get(i)->save_xml(file);
408         file->tag.set_title("/FOLDERS");
409         file->append_tag();
410         file->append_newline();
411 }
412
413 int BinFolders::load_xml(FileXML *file)
414 {
415         clear();
416         int ret = 0;
417         while( !(ret=file->read_tag()) ) {
418                 if( file->tag.title_is("/FOLDERS") ) break;
419                 if( file->tag.title_is("FOLDER") ) {
420                         BinFolder *folder = new BinFolder(-1, -1, "folder");
421                         folder->load_xml(file);
422                         append(folder);
423                 }
424         }
425         return ret;
426 }
427
428 void BinFolders::copy_from(BinFolders *that)
429 {
430         clear();
431         for( int i=0; i<that->size(); ++i )
432                 append(new BinFolder(*that->get(i)));
433 }
434
435
436 BinFolderFilter::BinFolderFilter()
437 {
438         enabled = 0;
439         target = 0;
440         op = 0;
441         value = 0;
442 }
443 BinFolderFilter::~BinFolderFilter()
444 {
445         delete enabled;
446         delete target;
447         delete op;
448         delete value;
449 }
450
451 void BinFolderFilter::update_enabled(int type)
452 {
453         if( !enabled )
454                 enabled = new BinFolderEnabled(this, type);
455         else
456                 enabled->update(type);
457 }
458
459 void BinFolderFilter::update_target(int type)
460 {
461         if( target ) {
462                 if( target->type == type ) return;
463                 delete target;  target = 0;
464         }
465         switch( type ) {
466         case FOLDER_TARGET_PATTERNS:    target = new BinFolderTargetPatterns(this);    break;
467         case FOLDER_TARGET_FILE_SIZE:   target = new BinFolderTargetFileSize(this);    break;
468         case FOLDER_TARGET_MOD_TIME:    target = new BinFolderTargetTime(this);        break;
469         case FOLDER_TARGET_TRACK_TYPE:  target = new BinFolderTargetTrackType(this);   break;
470         case FOLDER_TARGET_WIDTH:       target = new BinFolderTargetWidth(this);       break;
471         case FOLDER_TARGET_HEIGHT:      target = new BinFolderTargetHeight(this);      break;
472         case FOLDER_TARGET_FRAMERATE:   target = new BinFolderTargetFramerate(this);   break;
473         case FOLDER_TARGET_SAMPLERATE:  target = new BinFolderTargetSamplerate(this);  break;
474         case FOLDER_TARGET_CHANNELS:    target = new BinFolderTargetChannels(this);    break;
475         case FOLDER_TARGET_DURATION:    target = new BinFolderTargetDuration(this);    break;
476         }
477 }
478
479 void BinFolderFilter::update_op(int type)
480 {
481         if( op ) {
482                 if( op->type == type ) return;
483                 delete op;  op = 0;
484         }
485         switch( type ) {
486         case FOLDER_OP_AROUND:  op = new BinFolderOpAround(this);  break;
487         case FOLDER_OP_EQ:      op = new BinFolderOpEQ(this);      break;
488         case FOLDER_OP_GE:      op = new BinFolderOpGE(this);      break;
489         case FOLDER_OP_GT:      op = new BinFolderOpGT(this);      break;
490         case FOLDER_OP_NE:      op = new BinFolderOpNE(this);      break;
491         case FOLDER_OP_LE:      op = new BinFolderOpLE(this);      break;
492         case FOLDER_OP_LT:      op = new BinFolderOpLT(this);      break;
493         case FOLDER_OP_MATCHES: op = new BinFolderOpMatches(this); break;
494         }
495 }
496
497 void BinFolderFilter::update_value(const char *text)
498 {
499         if( !value )
500                 value = new BinFolderValue(this, text);
501         else
502                 value->update(text);
503 }
504
505 void BinFolderFilter::save_xml(FileXML *file)
506 {
507         file->tag.set_title("FILTER");
508         file->tag.set_property("ENABLED", enabled->type);
509         file->tag.set_property("OP", op->type);
510         file->tag.set_property("TARGET", target->type);
511         target->save_xml(file);
512         file->append_tag();
513         if( target->type == FOLDER_TARGET_PATTERNS )
514                 file->append_text(((BinFolderTargetPatterns *)target)->text);
515         file->tag.set_title("/FILTER");
516         file->append_tag();
517         file->append_newline();
518 }
519
520 int BinFolderFilter::load_xml(FileXML *file)
521 {
522         int enabled_type = file->tag.get_property("ENABLED", FOLDER_ENABLED_AND);
523         int op_type = file->tag.get_property("OP", FOLDER_OP_MATCHES);
524         int target_type = file->tag.get_property("TARGET", FOLDER_TARGET_PATTERNS);
525         XMLBuffer data;
526         file->read_text_until("/FILTER", &data, 0);
527         update_enabled(enabled_type);
528         update_target(target_type);
529         update_op(op_type);
530         target->load_xml(file);
531         if( target->type == FOLDER_TARGET_PATTERNS )
532                 ((BinFolderTargetPatterns *)target)->update(data.cstr());
533         return 0;
534 }
535
536 void BinFolderFilters::copy_from(BinFolderFilters *that)
537 {
538         clear();
539         for( int i=0; i<that->size(); ++i ) {
540                 BinFolderFilter *filter = new BinFolderFilter();
541                 BinFolderFilter *tp = that->get(i);
542                 filter->update_enabled(tp->enabled->type);
543                 filter->update_target(tp->target->type);
544                 filter->update_op(tp->op->type);
545                 filter->target->copy_from(tp->target);
546                 filter->op->copy_from(tp->op);
547                 append(filter);
548         }
549 }
550
551 double BinFolderOp::around(double v, double a)
552 {
553         if( type != FOLDER_OP_AROUND ) return v;
554         v = fabs(v);
555         return a>0 ? v/a : v;
556 }
557
558 // string theory: Feynman, Einstein and Schrodinger string compare
559 //   Feynman: try all possible matches, weight the outcomes
560 //   Schrodinger: it may be there several ways at the same time
561 //   Einstein: the more matches there are, the heavier it is
562 double BinFolderOp::around(const char *ap, const char *bp)
563 {
564         int64_t v = 0, vmx = 0;
565         int alen = strlen(ap), blen = strlen(bp);
566         if( alen > blen ) {
567                 const char *cp = ap;  ap = bp;  bp = cp;
568                 int clen = alen;  alen = blen;  blen = clen;
569         }
570 // 4 level loop (with strncmp), don't try long strings
571         for( int n=0; ++n<=alen; ) {
572                 int64_t nn = n*n;
573                 int an = alen-n+1, bn = blen-n+1;
574                 for( int i=an; --i>=0; ) {
575                         for( int j=bn; --j>=0; ) {
576                                 if( !strncmp(ap+i, bp+j, n) ) v += nn;
577                         }
578                 }
579                 vmx += an*bn*nn;
580         }
581         return !vmx ? -1 : 1 - v / (double)vmx;
582 }
583
584
585 double BinFolderOp::compare(BinFolderTarget *target, Indexable *idxbl)
586 {
587         double v = -1;
588         switch( target->type ) {
589         case FOLDER_TARGET_PATTERNS: {
590                 BinFolderTargetPatterns *tgt = (BinFolderTargetPatterns *)target;
591                 switch( type ) {
592                 case FOLDER_OP_AROUND: {
593                         const char *cp = idxbl->path;
594                         const char *bp = strrchr(cp, '/');
595                         if( bp ) cp = bp + 1;
596                         v = around(cp, tgt->text);
597                         break; }
598                 case FOLDER_OP_EQ:  case FOLDER_OP_GT:  case FOLDER_OP_GE:
599                 case FOLDER_OP_NE:  case FOLDER_OP_LT:  case FOLDER_OP_LE: {
600                         const char *cp = idxbl->path;
601                         const char *bp = strrchr(cp, '/');
602                         if( bp ) cp = bp + 1;
603                         v = strcmp(cp, tgt->text);
604                         break; }
605                 case FOLDER_OP_MATCHES: {
606                         v = -1;
607                         char *cp = tgt->text;
608                         while( v < 0 && *cp ) {
609                                 char pattern[BCTEXTLEN], *bp = pattern, ch;
610                                 while( *cp && (ch=*cp++)!='\n' ) *bp++ = ch;
611                                 *bp = 0;
612                                 if( !pattern[0] ) continue;
613                                 const char *title = idxbl->get_title();
614                                 if( !FileSystem::test_filter(title, pattern) )
615                                         v = 1;
616                         }
617                         break; }
618                 }
619                 break; }
620         case FOLDER_TARGET_FILE_SIZE: {
621                 BinFolderTargetFileSize *tgt = (BinFolderTargetFileSize *)target;
622                 int64_t file_size = !idxbl->is_asset ? -1 :
623                         FileSystem::get_size(idxbl->path);
624                 v = around(file_size - tgt->file_size, tgt->around);
625                 break; }
626         case FOLDER_TARGET_MOD_TIME: {
627                 BinFolderTargetTime *tgt = (BinFolderTargetTime *)target;
628                 struct stat st;
629                 if( stat(idxbl->path, &st) ) break;
630                 v = around(st.st_mtime - tgt->mtime, tgt->around);
631                 break; }
632         case FOLDER_TARGET_TRACK_TYPE: {
633                 BinFolderTargetTrackType *tgt = (BinFolderTargetTrackType *)target;
634                 int want_audio = (tgt->data_types&(1<<TRACK_AUDIO)) ? 1 : 0;
635                 int has_audio = idxbl->have_audio();
636                 if( want_audio != has_audio ) break;
637                 int want_video = (tgt->data_types&(1<<TRACK_VIDEO)) ? 1 : 0;
638                 int has_video = idxbl->have_video();
639                 if( want_video != has_video ) break;
640                 v = 1;
641                 break; }
642         case FOLDER_TARGET_WIDTH: {
643                 BinFolderTargetWidth *tgt = (BinFolderTargetWidth *)target;
644                 int w = idxbl->get_w();
645                 v = around(w - tgt->width, tgt->around);
646                 break; }
647         case FOLDER_TARGET_HEIGHT: {
648                 BinFolderTargetHeight *tgt = (BinFolderTargetHeight *)target;
649                 int h = idxbl->get_h();
650                 v = around(h - tgt->height, tgt->around);
651                 break; }
652         case FOLDER_TARGET_FRAMERATE: {
653                 BinFolderTargetFramerate *tgt = (BinFolderTargetFramerate *)target;
654                 double rate = idxbl->get_frame_rate();
655                 v = around(rate - tgt->framerate, tgt->around);
656                 break; }
657         case FOLDER_TARGET_SAMPLERATE: {
658                 BinFolderTargetSamplerate *tgt = (BinFolderTargetSamplerate *)target;
659                 double rate = idxbl->get_sample_rate();
660                 v = around(rate - tgt->samplerate, tgt->around);
661                 break; }
662         case FOLDER_TARGET_CHANNELS: {
663                 BinFolderTargetChannels *tgt = (BinFolderTargetChannels *)target;
664                 double chs = idxbl->get_audio_channels();
665                 v = around(chs - tgt->channels, tgt->around);
666                 break; }
667         case FOLDER_TARGET_DURATION: {
668                 BinFolderTargetDuration *tgt = (BinFolderTargetDuration *)target;
669                 double len = 0;
670                 double video_rate = !idxbl->have_video() ? 0 : idxbl->get_frame_rate();
671                 if( video_rate > 0 ) {
672                         double video_length = idxbl->get_video_frames() / video_rate;
673                         if( video_length > len ) len = video_length;
674                 }
675                 double audio_rate = !idxbl->have_audio() ? 0 : idxbl->get_sample_rate();
676                 if( audio_rate > 0 ) {
677                         double audio_length = idxbl->get_audio_samples() / audio_rate;
678                         if( audio_length > len ) len = audio_length;
679                 }
680                 v = around(len - tgt->duration, tgt->around);
681                 break; }
682         }
683
684         return v;
685 }
686
687 BinFolderEnabled::BinFolderEnabled(BinFolderFilter *filter, int type)
688  : BC_ListBoxItem(_(types[type]))
689 {
690         this->filter = filter;
691         this->type = type;
692 }
693
694 BinFolderEnabled::~BinFolderEnabled()
695 {
696 }
697
698 void BinFolderEnabled::update(int type)
699 {
700         this->type = type;
701         set_text(_(types[type]));
702 }
703
704 BinFolderEnabledType::BinFolderEnabledType(int no)
705  : BC_MenuItem(_(BinFolderEnabled::types[no]))
706 {
707         this->no = no;
708 }
709 BinFolderEnabledType::~BinFolderEnabledType()
710 {
711 }
712
713 int BinFolderEnabledType::handle_event()
714 {
715         BinFolderEnabledPopup *enabled_popup = (BinFolderEnabledPopup *)get_popup_menu();
716         BinFolderList *folder_list = enabled_popup->folder_list;
717         int i = folder_list->get_selection_number(FOLDER_COLUMN_ENABLE, 0);
718         if( i >= 0 ) {
719                 BinFolder *folder = folder_list->folder;
720                 BinFolderFilter *filter = folder->filters[i];
721                 filter->update_enabled(no);
722                 folder_list->create_list();
723         }
724         return 1;
725 }
726
727 BinFolderEnabledPopup::BinFolderEnabledPopup(BinFolderList *folder_list)
728  : BC_PopupMenu(0, 0, 0, "", 0)
729 {
730         this->folder_list = folder_list;
731         enabled = 0;
732 }
733
734 void BinFolderEnabledPopup::create_objects()
735 {
736         add_item(new BinFolderEnabledType(FOLDER_ENABLED_OFF));
737         add_item(new BinFolderEnabledType(FOLDER_ENABLED_AND));
738         add_item(new BinFolderEnabledType(FOLDER_ENABLED_OR));
739         add_item(new BinFolderEnabledType(FOLDER_ENABLED_AND_NOT));
740         add_item(new BinFolderEnabledType(FOLDER_ENABLED_OR_NOT));
741 }
742
743 void BinFolderEnabledPopup::activate_menu(BC_ListBoxItem *item)
744 {
745         this->enabled = (BinFolderEnabled *)item;
746         BC_PopupMenu::activate_menu();
747 }
748
749 BinFolderTarget::BinFolderTarget(BinFolderFilter *filter, int type)
750  : BC_ListBoxItem(_(types[type]))
751 {
752         this->filter = filter;
753         this->type = type;
754         around = -1;
755 }
756
757 BinFolderTarget::~BinFolderTarget()
758 {
759 }
760
761 BC_Window *BinFolderTarget::new_gui(ModifyTargetThread *thread)
762 {
763         ModifyTargetGUI *window = new ModifyTargetGUI(thread);
764         window->create_objects();
765         return window;
766 }
767
768 BinFolderTargetType::BinFolderTargetType(int no)
769  : BC_MenuItem(_(BinFolderTarget::types[no]))
770 {
771         this->no = no;
772 }
773 BinFolderTargetType::~BinFolderTargetType()
774 {
775 }
776
777 int BinFolderTargetType::handle_event()
778 {
779         BinFolderTargetPopup *target_popup = (BinFolderTargetPopup *)get_popup_menu();
780         BinFolderList *folder_list = target_popup->folder_list;
781         int i = folder_list->get_selection_number(FOLDER_COLUMN_TARGET, 0);
782         if( i >= 0 ) {
783                 BinFolder *folder = folder_list->folder;
784                 BinFolderFilter *filter = folder->filters[i];
785                 filter->update_target(no);
786                 folder_list->create_list();
787         }
788         return 1;
789 }
790
791 BinFolderTargetPopup::BinFolderTargetPopup(BinFolderList *folder_list)
792  : BC_PopupMenu(0, 0, 0, "", 0)
793 {
794         this->folder_list = folder_list;
795         target = 0;
796 }
797
798 void BinFolderTargetPopup::create_objects()
799 {
800         add_item(new BinFolderTargetType(FOLDER_TARGET_PATTERNS));
801         add_item(new BinFolderTargetType(FOLDER_TARGET_FILE_SIZE));
802         add_item(new BinFolderTargetType(FOLDER_TARGET_MOD_TIME));
803         add_item(new BinFolderTargetType(FOLDER_TARGET_TRACK_TYPE));
804         add_item(new BinFolderTargetType(FOLDER_TARGET_WIDTH));
805         add_item(new BinFolderTargetType(FOLDER_TARGET_HEIGHT));
806         add_item(new BinFolderTargetType(FOLDER_TARGET_FRAMERATE));
807         add_item(new BinFolderTargetType(FOLDER_TARGET_SAMPLERATE));
808         add_item(new BinFolderTargetType(FOLDER_TARGET_CHANNELS));
809         add_item(new BinFolderTargetType(FOLDER_TARGET_DURATION));
810 }
811
812 void BinFolderTargetPopup::activate_menu(BC_ListBoxItem *item)
813 {
814         this->target = (BinFolderTarget *)item;
815         BC_PopupMenu::activate_menu();
816 }
817
818
819 BinFolderTargetPatterns::BinFolderTargetPatterns(BinFolderFilter *filter)
820  : BinFolderTarget(filter, FOLDER_TARGET_PATTERNS)
821 {
822         text = 0;
823         update("*");
824 }
825 BinFolderTargetPatterns::~BinFolderTargetPatterns()
826 {
827         delete [] text;
828 }
829
830 void BinFolderTargetPatterns::save_xml(FileXML *file) {}
831 void BinFolderTargetPatterns::load_xml(FileXML *file) {}
832
833 void BinFolderTargetPatterns::copy_from(BinFolderTarget *that)
834 {
835         BinFolderTargetPatterns *tp = (BinFolderTargetPatterns*)that;
836         update(tp->text);
837 }
838
839 void BinFolderTargetPatterns::update(const char *text)
840 {
841         delete [] this->text;
842         this->text = cstrdup(text);
843         filter->update_value(text);
844 }
845
846 BC_Window *BinFolderTargetPatterns::new_gui(ModifyTargetThread *thread)
847 {
848         return new ModifyTargetPatternsGUI(thread);
849 }
850
851
852 BinFolderTargetFileSize::BinFolderTargetFileSize(BinFolderFilter *filter)
853  : BinFolderTarget(filter, FOLDER_TARGET_FILE_SIZE)
854 {
855         file_size = 0;
856         update(file_size, -1);
857 }
858 BinFolderTargetFileSize::~BinFolderTargetFileSize()
859 {
860 }
861
862 void BinFolderTargetFileSize::save_xml(FileXML *file)
863 {
864         file->tag.set_property("FILE_SIZE", file_size);
865         file->tag.set_property("AROUND", around);
866 }
867
868 void BinFolderTargetFileSize::load_xml(FileXML *file)
869 {
870         int64_t file_size = file->tag.get_property("FILE_SIZE", this->file_size);
871         double around = file->tag.get_property("AROUND", this->around);
872         update(file_size, around);
873 }
874
875 void BinFolderTargetFileSize::copy_from(BinFolderTarget *that)
876 {
877         BinFolderTargetFileSize *tp = (BinFolderTargetFileSize *)that;
878         update(tp->file_size, tp->around);
879 }
880
881 void BinFolderTargetFileSize::update(int64_t file_size, double around)
882 {
883         this->file_size = file_size;
884         this->around = around;
885         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
886         show_no(file_size, cp, ep);
887         if( around >= 0 && filter->op->type == FOLDER_OP_AROUND ) {
888                 if( cp < ep ) *cp++ = '+';
889                 show_no(around, cp, ep);
890         }
891         *cp = 0;
892         filter->update_value(txt);
893 }
894
895 BC_Window *BinFolderTargetFileSize::new_gui(ModifyTargetThread *thread)
896 {
897         return new ModifyTargetFileSizeGUI(thread);
898 }
899
900
901 BinFolderTargetTime::BinFolderTargetTime(BinFolderFilter *filter)
902  : BinFolderTarget(filter, FOLDER_TARGET_MOD_TIME)
903 {
904         time_t t;  time(&t);
905         mtime = (int64_t)t;
906         update(mtime, -1);
907 }
908 BinFolderTargetTime::~BinFolderTargetTime()
909 {
910 }
911
912 void BinFolderTargetTime::save_xml(FileXML *file)
913 {
914         file->tag.set_property("MTIME", mtime);
915         file->tag.set_property("AROUND", around);
916 }
917
918 void BinFolderTargetTime::load_xml(FileXML *file)
919 {
920         int64_t mtime = file->tag.get_property("MTIME", this->mtime);
921         double around = file->tag.get_property("AROUND", this->around);
922         update(mtime, around);
923 }
924
925 void BinFolderTargetTime::copy_from(BinFolderTarget *that)
926 {
927         BinFolderTargetTime *tp = (BinFolderTargetTime *)that;
928         update(tp->mtime, tp->around);
929 }
930
931 void BinFolderTargetTime::update(int64_t mtime, double around)
932 {
933         this->mtime = mtime;
934         this->around = around;
935         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
936         show_date(mtime, cp, ep);
937         if( around >= 0 && filter->op->type == FOLDER_OP_AROUND ) {
938                 if( cp < ep ) *cp++ = '+';
939                 show_duration(around, cp, ep);
940         }
941         *cp = 0;
942         filter->update_value(txt);
943 }
944
945 BC_Window *BinFolderTargetTime::new_gui(ModifyTargetThread *thread)
946 {
947         return new ModifyTargetTimeGUI(thread);
948 }
949
950
951 BinFolderTargetTrackType::BinFolderTargetTrackType(BinFolderFilter *filter)
952  : BinFolderTarget(filter, FOLDER_TARGET_TRACK_TYPE)
953 {
954         data_types = (1<<TRACK_AUDIO);
955         update(data_types);
956 }
957 BinFolderTargetTrackType::~BinFolderTargetTrackType()
958 {
959 }
960
961 void BinFolderTargetTrackType::save_xml(FileXML *file)
962 {
963         file->tag.set_property("DATA_TYPES", data_types);
964 }
965
966 void BinFolderTargetTrackType::load_xml(FileXML *file)
967 {
968         int data_types = file->tag.get_property("DATA_TYPES", this->data_types);
969         update(data_types);
970 }
971
972 void BinFolderTargetTrackType::copy_from(BinFolderTarget *that)
973 {
974         BinFolderTargetTrackType *tp = (BinFolderTargetTrackType *)that;
975         update(tp->data_types);
976 }
977
978 void BinFolderTargetTrackType::update(int data_types)
979 {
980         this->data_types = data_types;
981         this->around = -1;
982         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
983         if( data_types & (1<<TRACK_AUDIO) ) {
984                 if( cp > txt && cp < ep ) *cp++ = ' ';
985                 cp += snprintf(cp, ep-cp, "%s",_("audio"));
986         }
987         if( data_types & (1<<TRACK_VIDEO) ) {
988                 if( cp > txt && cp < ep ) *cp++ = ' ';
989                 cp += snprintf(cp, ep-cp, "%s",_("video"));
990         }
991         *cp = 0;
992         filter->update_value(txt);
993 }
994
995 BC_Window *BinFolderTargetTrackType::new_gui(ModifyTargetThread *thread)
996 {
997         return new ModifyTargetTrackTypeGUI(thread);
998 }
999
1000
1001 BinFolderTargetWidth::BinFolderTargetWidth(BinFolderFilter *filter)
1002  : BinFolderTarget(filter, FOLDER_TARGET_WIDTH)
1003 {
1004         width = 0;
1005         update(width, -1);
1006 }
1007 BinFolderTargetWidth::~BinFolderTargetWidth()
1008 {
1009 }
1010
1011 void BinFolderTargetWidth::save_xml(FileXML *file)
1012 {
1013         file->tag.set_property("WIDTH", width);
1014         file->tag.set_property("AROUND", around);
1015 }
1016 void BinFolderTargetWidth::load_xml(FileXML *file)
1017 {
1018         int width = file->tag.get_property("WIDTH", this->width);
1019         double around = file->tag.get_property("AROUND", this->around);
1020         update(width, around);
1021 }
1022
1023 void BinFolderTargetWidth::copy_from(BinFolderTarget *that)
1024 {
1025         BinFolderTargetWidth *tp = (BinFolderTargetWidth *)that;
1026         update(tp->width, tp->around);
1027 }
1028
1029 void BinFolderTargetWidth::update(int width, double around)
1030 {
1031         this->width = width;
1032         this->around = around;
1033         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
1034         show_no(width, cp, ep);
1035         if( around >= 0 && filter->op->type == FOLDER_OP_AROUND ) {
1036                 if( cp < ep ) *cp++ = '+';
1037                 show_no(around, cp, ep);
1038         }
1039         *cp = 0;
1040         filter->update_value(txt);
1041 }
1042
1043 BC_Window *BinFolderTargetWidth::new_gui(ModifyTargetThread *thread)
1044 {
1045         return new ModifyTargetWidthGUI(thread);
1046 }
1047
1048
1049 BinFolderTargetHeight::BinFolderTargetHeight(BinFolderFilter *filter)
1050  : BinFolderTarget(filter, FOLDER_TARGET_HEIGHT)
1051 {
1052         height = 0;
1053         update(height, -1);
1054 }
1055 BinFolderTargetHeight::~BinFolderTargetHeight()
1056 {
1057 }
1058
1059 void BinFolderTargetHeight::save_xml(FileXML *file)
1060 {
1061         file->tag.set_property("HEIGHT", height);
1062         file->tag.set_property("AROUND", around);
1063 }
1064 void BinFolderTargetHeight::load_xml(FileXML *file)
1065 {
1066         int height = file->tag.get_property("HEIGHT", this->height);
1067         double around = file->tag.get_property("AROUND", this->around);
1068         update(height, around);
1069 }
1070
1071 void BinFolderTargetHeight::copy_from(BinFolderTarget *that)
1072 {
1073         BinFolderTargetHeight *tp = (BinFolderTargetHeight *)that;
1074         update(tp->height, tp->around);
1075 }
1076
1077 void BinFolderTargetHeight::update(int height, double around)
1078 {
1079         this->height = height;
1080         this->around = around;
1081         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
1082         show_no(height, cp, ep);
1083         if( around >= 0 && filter->op->type == FOLDER_OP_AROUND ) {
1084                 if( cp < ep ) *cp++ = '+';
1085                 show_no(around, cp, ep);
1086         }
1087         *cp = 0;
1088         filter->update_value(txt);
1089 }
1090
1091 BC_Window *BinFolderTargetHeight::new_gui(ModifyTargetThread *thread)
1092 {
1093         return new ModifyTargetHeightGUI(thread);
1094 }
1095
1096
1097 BinFolderTargetFramerate::BinFolderTargetFramerate(BinFolderFilter *filter)
1098  : BinFolderTarget(filter, FOLDER_TARGET_FRAMERATE)
1099 {
1100         framerate = 0;
1101         update(framerate, -1);
1102 }
1103 BinFolderTargetFramerate::~BinFolderTargetFramerate()
1104 {
1105 }
1106
1107 void BinFolderTargetFramerate::save_xml(FileXML *file)
1108 {
1109         file->tag.set_property("FRAMERATE", framerate);
1110         file->tag.set_property("AROUND", around);
1111 }
1112
1113 void BinFolderTargetFramerate::load_xml(FileXML *file)
1114 {
1115         double framerate = file->tag.get_property("FRAMERATE", this->framerate);
1116         double around = file->tag.get_property("AROUND", this->around);
1117         update(framerate, around);
1118 }
1119
1120 void BinFolderTargetFramerate::copy_from(BinFolderTarget *that)
1121 {
1122         BinFolderTargetFramerate *tp = (BinFolderTargetFramerate *)that;
1123         update(tp->framerate, tp->around);
1124 }
1125
1126 void BinFolderTargetFramerate::update(double framerate, double around)
1127 {
1128         this->framerate = framerate;
1129         this->around = around;
1130         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
1131         show_no(framerate, cp, ep, "%0.3f");
1132         if( around >= 0 && filter->op->type == FOLDER_OP_AROUND ) {
1133                 if( cp < ep ) *cp++ = '+';
1134                 show_no(around, cp, ep, "%0.3f");
1135         }
1136         *cp = 0;
1137         filter->update_value(txt);
1138 }
1139
1140 BC_Window *BinFolderTargetFramerate::new_gui(ModifyTargetThread *thread)
1141 {
1142         return new ModifyTargetFramerateGUI(thread);
1143 }
1144
1145
1146 BinFolderTargetSamplerate::BinFolderTargetSamplerate(BinFolderFilter *filter)
1147  : BinFolderTarget(filter, FOLDER_TARGET_SAMPLERATE)
1148 {
1149         samplerate = 0;
1150         update(samplerate, -1);
1151 }
1152
1153 BinFolderTargetSamplerate::~BinFolderTargetSamplerate()
1154 {
1155 }
1156
1157 void BinFolderTargetSamplerate::save_xml(FileXML *file)
1158 {
1159         file->tag.set_property("SAMPLERATE", samplerate);
1160         file->tag.set_property("AROUND", around);
1161 }
1162
1163 void BinFolderTargetSamplerate::load_xml(FileXML *file)
1164 {
1165         double samplerate = file->tag.get_property("SAMPLERATE", this->samplerate);
1166         double around = file->tag.get_property("AROUND", this->around);
1167         update(samplerate, around);
1168 }
1169
1170 void BinFolderTargetSamplerate::copy_from(BinFolderTarget *that)
1171 {
1172         BinFolderTargetSamplerate *tp = (BinFolderTargetSamplerate *)that;
1173         update(tp->samplerate, tp->around);
1174 }
1175
1176 void BinFolderTargetSamplerate::update(int samplerate, double around)
1177 {
1178         this->samplerate = samplerate;
1179         this->around = around;
1180         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
1181         show_no(samplerate, cp, ep);
1182         if( around >= 0 && filter->op->type == FOLDER_OP_AROUND ) {
1183                 if( cp < ep ) *cp++ = '+';
1184                 show_no(around, cp, ep);
1185         }
1186         *cp = 0;
1187         filter->update_value(txt);
1188 }
1189
1190 BC_Window *BinFolderTargetSamplerate::new_gui(ModifyTargetThread *thread)
1191 {
1192         return new ModifyTargetSamplerateGUI(thread);
1193 }
1194
1195
1196 BinFolderTargetChannels::BinFolderTargetChannels(BinFolderFilter *filter)
1197  : BinFolderTarget(filter, FOLDER_TARGET_CHANNELS)
1198 {
1199         channels = 0;
1200         update(channels, -1);
1201 }
1202 BinFolderTargetChannels::~BinFolderTargetChannels()
1203 {
1204 }
1205
1206 void BinFolderTargetChannels::save_xml(FileXML *file)
1207 {
1208         file->tag.set_property("CHANNELS", channels);
1209         file->tag.set_property("AROUND", around);
1210 }
1211
1212 void BinFolderTargetChannels::load_xml(FileXML *file)
1213 {
1214         int channels = file->tag.get_property("CHANNELS", this->channels);
1215         double around = file->tag.get_property("AROUND", this->around);
1216         update(channels, around);
1217 }
1218
1219 void BinFolderTargetChannels::copy_from(BinFolderTarget *that)
1220 {
1221         BinFolderTargetChannels *tp = (BinFolderTargetChannels *)that;
1222         update(tp->channels, tp->around);
1223 }
1224
1225 void BinFolderTargetChannels::update(int channels, double around)
1226 {
1227         this->channels = channels;
1228         this->around = around;
1229         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
1230         show_no(channels, cp, ep);
1231         if( around >= 0 && filter->op->type == FOLDER_OP_AROUND ) {
1232                 if( cp < ep ) *cp++ = '+';
1233                 show_no(around, cp, ep);
1234         }
1235         *cp = 0;
1236         filter->update_value(txt);
1237 }
1238
1239 BC_Window *BinFolderTargetChannels::new_gui(ModifyTargetThread *thread)
1240 {
1241         return new ModifyTargetChannelsGUI(thread);
1242 }
1243
1244
1245 BinFolderTargetDuration::BinFolderTargetDuration(BinFolderFilter *filter)
1246  : BinFolderTarget(filter, FOLDER_TARGET_DURATION)
1247 {
1248         duration = 0;
1249         update(duration, -1);
1250 }
1251 BinFolderTargetDuration::~BinFolderTargetDuration()
1252 {
1253 }
1254
1255 void BinFolderTargetDuration::save_xml(FileXML *file)
1256 {
1257         file->tag.set_property("DURATION", duration);
1258         file->tag.set_property("AROUND", around);
1259 }
1260
1261 void BinFolderTargetDuration::load_xml(FileXML *file)
1262 {
1263         int64_t duration = file->tag.get_property("DURATION", this->duration);
1264         double around = file->tag.get_property("AROUND", this->around);
1265         update(duration, around);
1266 }
1267
1268 void BinFolderTargetDuration::copy_from(BinFolderTarget *that)
1269 {
1270         BinFolderTargetDuration *tp = (BinFolderTargetDuration *)that;
1271         update(tp->duration, tp->around);
1272 }
1273
1274 void BinFolderTargetDuration::update(int64_t duration, double around)
1275 {
1276         this->duration = duration;
1277         this->around = around;
1278         char txt[BCSTRLEN], *cp = txt, *ep = cp + sizeof(txt)-1;
1279         show_duration(duration, cp, ep);
1280         if( around >= 0 && filter->op->type == FOLDER_OP_AROUND ) {
1281                 if( cp < ep ) *cp++ = '+';
1282                 show_duration(around, cp, ep);
1283         }
1284         *cp = 0;
1285         filter->update_value(txt);
1286 }
1287
1288 BC_Window *BinFolderTargetDuration::new_gui(ModifyTargetThread *thread)
1289 {
1290         return new ModifyTargetDurationGUI(thread);
1291 }
1292
1293
1294 BinFolderOp::BinFolderOp(BinFolderFilter *filter, int type)
1295  : BC_ListBoxItem(_(types[type]))
1296 {
1297         this->filter = filter;
1298         this->type = type;
1299 }
1300
1301 BinFolderOp::~BinFolderOp()
1302 {
1303 }
1304
1305 void BinFolderOp::copy_from(BinFolderOp *that)
1306 {
1307         type = that->type;
1308 }
1309
1310 BinFolderOpType::BinFolderOpType(int no)
1311  : BC_MenuItem(_(BinFolderOp::types[no]))
1312 {
1313         this->no = no;
1314 }
1315 BinFolderOpType::~BinFolderOpType()
1316 {
1317 }
1318
1319 int BinFolderOpType::handle_event()
1320 {
1321         BinFolderOpPopup *op_popup = (BinFolderOpPopup *)get_popup_menu();
1322         BinFolderList *folder_list = op_popup->folder_list;
1323         int i = folder_list->get_selection_number(FOLDER_COLUMN_OP, 0);
1324         if( i >= 0 ) {
1325                 BinFolder *folder = folder_list->folder;
1326                 BinFolderFilter *filter = folder->filters[i];
1327                 filter->update_op(no);
1328                 folder_list->create_list();
1329         }
1330         return 1;
1331 }
1332
1333 BinFolderOpPopup::BinFolderOpPopup(BinFolderList *folder_list)
1334  : BC_PopupMenu(0, 0, 0, "", 0)
1335 {
1336         this->folder_list = folder_list;
1337         op = 0;
1338 }
1339
1340 void BinFolderOpPopup::create_objects()
1341 {
1342         add_item(new BinFolderOpType(FOLDER_OP_AROUND));
1343         add_item(new BinFolderOpType(FOLDER_OP_EQ));
1344         add_item(new BinFolderOpType(FOLDER_OP_GE));
1345         add_item(new BinFolderOpType(FOLDER_OP_GT));
1346         add_item(new BinFolderOpType(FOLDER_OP_NE));
1347         add_item(new BinFolderOpType(FOLDER_OP_LE));
1348         add_item(new BinFolderOpType(FOLDER_OP_LT));
1349         add_item(new BinFolderOpType(FOLDER_OP_MATCHES));
1350 }
1351
1352 void BinFolderOpPopup::activate_menu(BC_ListBoxItem *item)
1353 {
1354         this->op = (BinFolderOp *)item;
1355         BC_PopupMenu::activate_menu();
1356 }
1357
1358 double BinFolderOp::test(BinFolderTarget *target, Indexable *idxbl)
1359 {
1360         return -1;
1361 }
1362
1363 double BinFolderOpEQ::test(BinFolderTarget *target, Indexable *idxbl)
1364 {
1365         double v = compare(target, idxbl);
1366         return v == 0 ? 1 : -1;
1367 }
1368
1369 double BinFolderOpGT::test(BinFolderTarget *target, Indexable *idxbl)
1370 {
1371         double v = compare(target, idxbl);
1372         return v > 0 ? 1 : -1;
1373 }
1374
1375 double BinFolderOpGE::test(BinFolderTarget *target, Indexable *idxbl)
1376 {
1377         double v = compare(target, idxbl);
1378         return v >= 0 ? 1 : -1;
1379 }
1380
1381 double BinFolderOpNE::test(BinFolderTarget *target, Indexable *idxbl)
1382 {
1383         double v = compare(target, idxbl);
1384         return v != 0 ? 1 : -1;
1385 }
1386
1387 double BinFolderOpLT::test(BinFolderTarget *target, Indexable *idxbl)
1388 {
1389         double v = compare(target, idxbl);
1390         return v < 0 ? 1 : -1;
1391 }
1392
1393 double BinFolderOpLE::test(BinFolderTarget *target, Indexable *idxbl)
1394 {
1395         double v = compare(target, idxbl);
1396         return v <= 0 ? 1 : -1;
1397 }
1398
1399 double BinFolderOpMatches::test(BinFolderTarget *target, Indexable *idxbl)
1400 {
1401         double v = compare(target, idxbl);
1402         return v;
1403 }
1404
1405 double BinFolderOpAround::test(BinFolderTarget *target, Indexable *idxbl)
1406 {
1407         double v = compare(target, idxbl);
1408         return v;
1409 }
1410
1411 BinFolderValue::BinFolderValue(BinFolderFilter *filter, const char *text)
1412  : BC_ListBoxItem()
1413 {
1414         this->filter = filter;
1415         update(text);
1416 }
1417
1418 BinFolderValue::~BinFolderValue()
1419 {
1420 }
1421
1422
1423 void BinFolderValue::update(const char *text)
1424 {
1425         const char *cp = text;
1426         char txt[BCSTRLEN], *tp = txt;
1427         for( int i=sizeof(txt); --i>0 && *cp!=0 && *cp!='\n'; ++tp,++cp ) *tp = *cp;
1428         *tp = 0;
1429         set_text(txt);
1430 }
1431
1432
1433 BinFolderList::BinFolderList(BinFolder *folder, MWindow *mwindow,
1434                 ModifyFolderGUI *window, int x, int y, int w, int h)
1435  : BC_ListBox(x, y, w, h, LISTBOX_TEXT, 0,
1436         0, 0, 1, 0, 0, LISTBOX_SINGLE, ICON_LEFT, 1)
1437 {
1438         this->folder = folder;
1439         this->mwindow = mwindow;
1440         this->window = window;
1441         dragging_item = 0;
1442         set_process_drag(1);
1443         enabled_popup = 0;
1444         op_popup = 0;
1445         target_popup = 0;
1446         modify_target = 0;
1447 }
1448
1449 BinFolderList::~BinFolderList()
1450 {
1451         save_defaults(mwindow->defaults);
1452         delete modify_target;
1453 }
1454
1455 void BinFolderList::create_objects()
1456 {
1457         list_titles[FOLDER_COLUMN_ENABLE] = _("Enable");
1458         list_titles[FOLDER_COLUMN_TARGET] = _("Target");
1459         list_titles[FOLDER_COLUMN_OP]     = _("Op");
1460         list_titles[FOLDER_COLUMN_VALUE]  = _("Value");
1461         list_width[FOLDER_COLUMN_ENABLE]  = xS(80);
1462         list_width[FOLDER_COLUMN_TARGET]  = xS(80);
1463         list_width[FOLDER_COLUMN_OP]      = xS(50);
1464         list_width[FOLDER_COLUMN_VALUE]   = xS(180);
1465         load_defaults(mwindow->defaults);
1466         create_list();
1467         add_subwindow(enabled_popup = new BinFolderEnabledPopup(this));
1468         enabled_popup->create_objects();
1469         add_subwindow(op_popup = new BinFolderOpPopup(this));
1470         op_popup->create_objects();
1471         add_subwindow(target_popup = new BinFolderTargetPopup(this));
1472         target_popup->create_objects();
1473
1474         modify_target = new ModifyTargetThread(this);
1475 }
1476
1477 void BinFolderList::create_list()
1478 {
1479         for( int i=0; i<FOLDER_COLUMNS; ++i )
1480                 list_items[i].remove_all();
1481         for( int i=0; i<folder->filters.size(); ++i ) {
1482                 BinFolderFilter *filter = folder->filters[i];
1483                 list_items[FOLDER_COLUMN_ENABLE].append(filter->enabled);
1484                 list_items[FOLDER_COLUMN_TARGET].append(filter->target);
1485                 list_items[FOLDER_COLUMN_OP].append(filter->op);
1486                 list_items[FOLDER_COLUMN_VALUE].append(filter->value);
1487         }
1488         update(list_items, list_titles, list_width, FOLDER_COLUMNS,
1489                 get_xposition(), get_yposition(), get_highlighted_item(),
1490                 1, 1);
1491 }
1492
1493 int BinFolderList::handle_event()
1494 {
1495         return 1;
1496 }
1497
1498 int BinFolderList::selection_changed()
1499 {
1500         if( !cursor_above() ) return 0;
1501         int no = get_selection_number(0, 0);
1502         if( no < 0 ) return 0;
1503         BinFolderFilter *filter = folder->filters[no];
1504         if( get_button_down() && get_buttonpress() == 3 ) {
1505                 int cx = get_cursor_x(), col = -1;
1506                 for( int i=0; col<0 && i<FOLDER_COLUMNS; ++i ) {
1507                         int ofs = get_column_offset(i);
1508                         if( cx >= ofs && cx < ofs+get_column_width(i) ) {
1509                                 col = i;  break;
1510                         }
1511                 }
1512                 BC_ListBoxItem *item = col >= 0 ? get_selection(col, 0) : 0;
1513                 if( item ) {
1514                         deactivate_selection();
1515                         switch( col ) {
1516                         case FOLDER_COLUMN_ENABLE:
1517                                 enabled_popup->activate_menu(item);
1518                                 break;
1519                         case FOLDER_COLUMN_TARGET:
1520                                 target_popup->activate_menu(item);
1521                                 break;
1522                         case FOLDER_COLUMN_OP:
1523                                 op_popup->activate_menu(item);
1524                                 break;
1525                         case FOLDER_COLUMN_VALUE: {
1526                                 modify_target->close_window();
1527                                 int cw = xS(filter->target->type == FOLDER_TARGET_PATTERNS ? 400 : 320);
1528                                 int ch = yS(filter->target->type == FOLDER_TARGET_PATTERNS ? 300 : 120);
1529                                 int cx, cy;  get_abs_cursor(cx, cy);
1530                                 if( (cx-=cw/2) < xS(50) ) cx = xS(50);
1531                                 if( (cy-=ch/2) < yS(50) ) cy = yS(50);
1532                                 modify_target->start(filter->target, cx, cy, cw, ch);
1533                                 break; }
1534                         }
1535                 }
1536         }
1537         return 1;
1538 }
1539
1540 int BinFolderList::column_resize_event()
1541 {
1542         for( int i = 0; i < FOLDER_COLUMNS; i++ ) {
1543                 list_width[i] = get_column_width(i);
1544         }
1545         return 1;
1546 }
1547
1548 int BinFolderList::drag_start_event()
1549 {
1550         if( BC_ListBox::drag_start_event() ) {
1551                 dragging_item = 1;
1552                 return 1;
1553         }
1554
1555         return 0;
1556 }
1557
1558 int BinFolderList::drag_motion_event()
1559 {
1560         if( BC_ListBox::drag_motion_event() ) {
1561                 return 1;
1562         }
1563         return 0;
1564 }
1565
1566 int BinFolderList::drag_stop_event()
1567 {
1568         if( dragging_item ) {
1569                 int src = get_selection_number(0, 0);
1570                 int dst = get_highlighted_item();
1571                 if( src != dst ) {
1572                         move_filter(src, dst);
1573                 }
1574                 BC_ListBox::drag_stop_event();
1575                 dragging_item = 0;
1576         }
1577         return 0;
1578 }
1579
1580 void BinFolderList::move_filter(int src, int dst)
1581 {
1582         BinFolderFilters &filters = folder->filters;
1583         BinFolderFilter *src_filter = filters[src];
1584         if( dst < 0 ) dst = filters.size()-1;
1585
1586         if( dst != src ) {
1587                 for( int i=src; i<filters.size()-1; ++i )
1588                         filters[i] = filters[i+1];
1589                 for( int i=filters.size(); --i>dst; )
1590                         filters[i] = filters[i-1];
1591                 filters[dst] = src_filter;
1592         }
1593 }
1594
1595 void BinFolderList::save_defaults(BC_Hash *defaults)
1596 {
1597         defaults->update("BIN_FOLDER_ENA", list_width[FOLDER_COLUMN_ENABLE]);
1598         defaults->update("BIN_FOLDER_TGT", list_width[FOLDER_COLUMN_TARGET]);
1599         defaults->update("BIN_FOLDER_OPR", list_width[FOLDER_COLUMN_OP]);
1600         defaults->update("BIN_FOLDER_VAL", list_width[FOLDER_COLUMN_VALUE]);
1601 }
1602 void BinFolderList::load_defaults(BC_Hash *defaults)
1603 {
1604         list_width[FOLDER_COLUMN_ENABLE] = defaults->get("BIN_FOLDER_ENA", list_width[FOLDER_COLUMN_ENABLE]);
1605         list_width[FOLDER_COLUMN_TARGET] = defaults->get("BIN_FOLDER_TGT", list_width[FOLDER_COLUMN_TARGET]);
1606         list_width[FOLDER_COLUMN_OP]     = defaults->get("BIN_FOLDER_OPR", list_width[FOLDER_COLUMN_OP]);
1607         list_width[FOLDER_COLUMN_VALUE]  = defaults->get("BIN_FOLDER_VAL", list_width[FOLDER_COLUMN_VALUE]);
1608 }
1609
1610 BinFolderAddFilter::BinFolderAddFilter(BinFolderList *folder_list, int x, int y)
1611  : BC_GenericButton(x, y, _("Add"))
1612 {
1613         this->folder_list = folder_list;
1614 }
1615 BinFolderAddFilter::~BinFolderAddFilter()
1616 {
1617 }
1618
1619 int BinFolderAddFilter::handle_event()
1620 {
1621         folder_list->modify_target->close_window();
1622 // default new filter
1623         BinFolderFilter *filter = new BinFolderFilter();
1624         filter->update_enabled(FOLDER_ENABLED_OR);
1625         filter->update_target(FOLDER_TARGET_PATTERNS);
1626         filter->update_op(FOLDER_OP_MATCHES);
1627         BinFolderTargetPatterns *patterns = (BinFolderTargetPatterns *)(filter->target);
1628         filter->update_value(patterns->text);
1629         folder_list->folder->filters.append(filter);
1630         folder_list->create_list();
1631         return 1;
1632 }
1633
1634 BinFolderDelFilter::BinFolderDelFilter(BinFolderList *folder_list, int x, int y)
1635  : BC_GenericButton(x, y, _("Del"))
1636 {
1637         this->folder_list = folder_list;
1638 }
1639 BinFolderDelFilter::~BinFolderDelFilter()
1640 {
1641 }
1642
1643 int BinFolderDelFilter::handle_event()
1644 {
1645         folder_list->modify_target->close_window();
1646         int no = folder_list->get_selection_number(0, 0);
1647         if( no >= 0 ) {
1648                 folder_list->folder->filters.remove_object_number(no);
1649                 folder_list->create_list();
1650         }
1651         return 1;
1652 }
1653
1654 BinFolderApplyFilter::BinFolderApplyFilter(BinFolderList *folder_list, int x, int y)
1655  : BC_GenericButton(x, y, _("Apply"))
1656 {
1657         this->folder_list = folder_list;
1658 }
1659 BinFolderApplyFilter::~BinFolderApplyFilter()
1660 {
1661 }
1662
1663 int BinFolderApplyFilter::handle_event()
1664 {
1665         ModifyFolderThread *thread = folder_list->window->thread;
1666         thread->original->copy_from(thread->folder);
1667         thread->agui->async_update_assets();
1668         return 1;
1669 }
1670
1671
1672 NewFolderGUI::NewFolderGUI(NewFolderThread *thread, int x, int y, int w, int h)
1673  : BC_Window(_(PROGRAM_NAME ": New folder"),
1674                 x, y, xS(w), yS(h), -1, -1, 0, 0, 1)
1675 {
1676         this->thread = thread;
1677 // *** CONTEXT_HELP ***
1678         context_help_set_keyword("User Folders");
1679 }
1680
1681 NewFolderGUI::~NewFolderGUI()
1682 {
1683 }
1684
1685 void NewFolderGUI::create_objects()
1686 {
1687         int xs10 = xS(10);
1688         int ys5 = yS(5), ys10 = yS(10);
1689         lock_window("NewFolderGUI::create_objects");
1690         int x = xs10, y = ys10;
1691         BC_Title *title;
1692         add_subwindow(title = new BC_Title(x, y, _("Folder name:")));
1693         y += title->get_h() + ys5;
1694         const char *text = !thread->is_clips ? _("media bin") : _("clip bin");
1695         add_subwindow(text_box = new BC_TextBox(x, y, xS(300), 1, text));
1696         add_subwindow(new BC_OKButton(this));
1697         add_subwindow(new BC_CancelButton(this));
1698         show_window();
1699         unlock_window();
1700 }
1701
1702 const char* NewFolderGUI::get_text()
1703 {
1704         return text_box->get_text();
1705 }
1706
1707
1708 NewFolderThread::NewFolderThread(AWindowGUI *agui)
1709  : BC_DialogThread()
1710 {
1711         this->agui = agui;
1712         is_clips = -1;
1713 }
1714
1715 NewFolderThread::~NewFolderThread()
1716 {
1717         close_window();
1718 }
1719
1720 void NewFolderThread::start(int x, int y, int w, int h, int is_clips)
1721 {
1722         close_window();
1723         this->is_clips = is_clips;
1724         this->wx = x;  this->wy = y;
1725         this->ww = w;  this->wh = h;
1726         Thread::start();
1727 }
1728
1729 BC_Window *NewFolderThread::new_gui()
1730 {
1731         window = new NewFolderGUI(this, wx, wy, ww, wh);
1732         window->create_objects();
1733         return window;
1734 }
1735
1736 void NewFolderThread::handle_done_event(int result)
1737 {
1738         if( !result ) {
1739                 const char *text = window->get_text();
1740                 agui->mwindow->new_folder(text, is_clips);
1741         }
1742 }
1743
1744 void NewFolderThread::handle_close_event(int result)
1745 {
1746         is_clips = -1;
1747 }
1748
1749 ModifyFolderGUI::ModifyFolderGUI(ModifyFolderThread *thread, int x, int y, int w, int h)
1750  : BC_Window(_(PROGRAM_NAME ": Modify folder"), x, y, w, h, xS(320), yS(200), 1, 0, 1)
1751 {
1752         this->thread = thread;
1753 // *** CONTEXT_HELP ***
1754         context_help_set_keyword("User Folders");
1755 }
1756
1757 ModifyFolderGUI::~ModifyFolderGUI()
1758 {
1759 }
1760
1761 int ModifyFolderGUI::receive_custom_xatoms(xatom_event *event)
1762 {
1763         if( event->message_type == modify_folder_xatom ) {
1764                 update_filters();
1765                 return 1;
1766         }
1767         return 0;
1768 }
1769
1770 void ModifyFolderGUI::async_update_filters()
1771 {
1772         xatom_event event;
1773         event.message_type = modify_folder_xatom;
1774         send_custom_xatom(&event);
1775 }
1776
1777
1778 void ModifyFolderGUI::create_objects()
1779 {
1780         int xs10 = xS(10), xs15 = xS(15);
1781         int ys10 = yS(10), ys30 = yS(30);
1782         lock_window("ModifyFolderGUI::create_objects");
1783         modify_folder_xatom = create_xatom("CWINDOWGUI_UPDATE_FILTERS");
1784         int x = xs10, y = ys10;
1785         BC_Title *title;
1786         add_subwindow(title = new BC_Title(x, y, _("Enter the name of the folder:")));
1787         const char *text = !thread->folder->is_clips ? _("Media") : _("Clips");
1788         int tw = BC_Title::calculate_w(this, text, LARGEFONT);
1789         int x0 = get_w() - xS(50) - tw;
1790         add_subwindow(text_title = new BC_Title(x0, y, text, LARGEFONT, YELLOW));
1791         y += title->get_h() + ys10;
1792         add_subwindow(text_box = new BC_TextBox(x, y, xS(300), 1, thread->folder->title));
1793         y += text_box->get_h() + ys10;
1794         int lh = get_h() - y - BC_OKButton::calculate_h() - ys30;
1795         int lw = get_w() - x - xS(160);
1796         add_subwindow(folder_list =
1797                 new BinFolderList(thread->folder, thread->agui->mwindow, this, x, y, lw, lh));
1798         folder_list->create_objects();
1799         int x1 = x + folder_list->get_w() + xs15, y1 = y;
1800         add_subwindow(add_filter = new BinFolderAddFilter(folder_list, x1, y1));
1801         y1 += add_filter->get_h() + ys10;
1802         add_subwindow(del_filter = new BinFolderDelFilter(folder_list, x1, y1));
1803         y1 += del_filter->get_h() + ys10;
1804         add_subwindow(apply_filter = new BinFolderApplyFilter(folder_list, x1, y1));
1805         add_subwindow(ok_button = new BC_OKButton(this));
1806         add_subwindow(cancel_button = new BC_CancelButton(this));
1807         show_window();
1808         unlock_window();
1809 }
1810
1811 int ModifyFolderGUI::resize_event(int w, int h)
1812 {
1813         MWindow *mwindow = thread->agui->mwindow;
1814         mwindow->session->bwindow_w = w;
1815         mwindow->session->bwindow_h = h;
1816         int tx = text_title->get_x() + w - get_w();
1817         int ty = text_title->get_y();
1818         text_title->reposition_window(tx, ty);
1819         int lx = folder_list->get_x();
1820         int ly = folder_list->get_y();
1821         int lh = h - ly - BC_OKButton::calculate_h() - yS(30);
1822         int lw = w - lx - xS(160);
1823         folder_list->reposition_window(lx, ly, lw, lh);
1824         int x1 = lx + lw + xS(15);
1825         add_filter->reposition_window(x1, add_filter->get_y());
1826         del_filter->reposition_window(x1, del_filter->get_y());
1827         apply_filter->reposition_window(x1,apply_filter->get_y());
1828         ok_button->resize_event(w, h);
1829         cancel_button->resize_event(w, h);
1830         return 1;
1831 }
1832
1833 const char* ModifyFolderGUI::get_text()
1834 {
1835         return text_box->get_text();
1836 }
1837
1838 void ModifyFolderGUI::update_filters()
1839 {
1840         folder_list->create_list();
1841 }
1842
1843
1844 ModifyFolderThread::ModifyFolderThread(AWindowGUI *agui)
1845  : BC_DialogThread()
1846 {
1847         this->agui = agui;
1848         original = 0;
1849         modify_edl = 0;
1850         folder = 0;
1851 }
1852
1853 ModifyFolderThread::~ModifyFolderThread()
1854 {
1855         close_window();
1856         delete folder;
1857 }
1858
1859 void ModifyFolderThread::start(BinFolder *folder, int x, int y, int w, int h)
1860 {
1861         close_window();
1862         this->original = folder;
1863         this->modify_edl = agui->mwindow->edl;
1864         this->modify_edl->add_user();
1865         this->folder = new BinFolder(*folder);
1866         this->wx = x;  this->wy = y;
1867         this->ww = w;  this->wh = h;
1868         Thread::start();
1869 }
1870
1871 BC_Window *ModifyFolderThread::new_gui()
1872 {
1873         window = new ModifyFolderGUI(this, wx, wy, ww, wh);
1874         window->create_objects();
1875         return window;
1876 }
1877
1878 void ModifyFolderThread::handle_done_event(int result)
1879 {
1880         if( !result ) {
1881                 const char *title = window->get_text();
1882                 if( strcmp(folder->title, title) ) {
1883                         if( agui->mwindow->edl->get_folder_number(title) >= 0 ) {
1884                                 eprintf("folder already exists: %s", title);
1885                                 result = 1;
1886                         }
1887                         else
1888                                 strncpy(folder->title, title,sizeof(folder->title));
1889                 }
1890         }
1891         if( !result ) {
1892                 original->copy_from(folder);
1893                 agui->async_update_assets();
1894         }
1895         delete folder;  folder = 0;
1896         original = 0;
1897         modify_edl->remove_user();
1898 }
1899
1900 void ModifyFolderThread::handle_close_event(int result)
1901 {
1902 }
1903
1904
1905 ModifyTargetThread::ModifyTargetThread(BinFolderList *folder_list)
1906  : BC_DialogThread()
1907 {
1908         this->folder_list = folder_list;
1909         target = 0;
1910 }
1911
1912 ModifyTargetThread::~ModifyTargetThread()
1913 {
1914         close_window();
1915 }
1916
1917 void ModifyTargetThread::start(BinFolderTarget *target, int x, int y, int w, int h)
1918 {
1919         this->target = target;
1920         wx = x;  wy = y;
1921         ww = w;  wh = h;
1922         Thread::start();
1923 }
1924
1925 BC_Window *ModifyTargetThread::new_gui()
1926 {
1927         window = (ModifyTargetGUI *)target->new_gui(this);
1928         window->create_objects();
1929         return window;
1930 }
1931
1932 void ModifyTargetThread::handle_done_event(int result)
1933 {
1934         if( !result ) {
1935                 window->update();
1936                 folder_list->window->async_update_filters();
1937         }
1938 }
1939
1940 void ModifyTargetThread::handle_close_event(int result)
1941 {
1942         target = 0;
1943 }
1944
1945 ModifyTargetGUI::ModifyTargetGUI(ModifyTargetThread *thread, int allow_resize)
1946  : BC_Window(_(PROGRAM_NAME ": Modify target"),
1947                 thread->wx, thread->wy, xS(thread->ww), yS(thread->wh),
1948                 -1, -1, allow_resize, 0, 1)
1949 {
1950         this->thread = thread;
1951 // *** CONTEXT_HELP ***
1952         context_help_set_keyword("User Folders");
1953 }
1954
1955 ModifyTargetGUI::~ModifyTargetGUI()
1956 {
1957 }
1958
1959 void ModifyTargetGUI::create_objects(BC_TextBox *&text_box)
1960 {
1961         int xs10 = xS(10), xs20 = xS(20);
1962         int ys10 = yS(10);
1963         lock_window("ModifyTargetGUI::create_objects");
1964         int x = xs10, y = ys10;
1965         const char *text = thread->target->filter->value->get_text();
1966         add_subwindow(text_box = new BC_TextBox(x, y, get_w()-xs20, 1, text));
1967         add_subwindow(new BC_OKButton(this));
1968         add_subwindow(new BC_CancelButton(this));
1969         show_window();
1970         unlock_window();
1971 }
1972
1973 int ModifyTargetGUI::resize_event(int w, int h)
1974 {
1975         return BC_WindowBase::resize_event(w, h);
1976 }
1977
1978 ModifyTargetPatternsGUI::ModifyTargetPatternsGUI(ModifyTargetThread *thread)
1979  : ModifyTargetGUI(thread, 1)
1980 {
1981         this->thread = thread;
1982         scroll_text_box = 0;
1983         text_rowsz = 0;
1984 }
1985
1986 ModifyTargetPatternsGUI::~ModifyTargetPatternsGUI()
1987 {
1988         delete scroll_text_box;
1989 }
1990
1991 void ModifyTargetPatternsGUI::create_objects()
1992 {
1993         int xs10 = xS(10), xs20 = xS(20);
1994         int ys10 = yS(10), ys20 = yS(20);
1995         lock_window("ModifyTargetPatternsGUI::create_objects");
1996         BinFolderTargetPatterns *target = (BinFolderTargetPatterns *)thread->target;
1997         int x = xs10, y = ys10;
1998         int text_font = MEDIUMFONT;
1999         text_rowsz = get_text_ascent(text_font)+1 + get_text_descent(text_font)+1;
2000         int th = get_h() - y - BC_OKButton::calculate_h() - ys20;
2001         int rows = th / text_rowsz;
2002         int text_len = strlen(target->text);
2003         if( text_len < BCTEXTLEN ) text_len = BCTEXTLEN;
2004         scroll_text_box = new BC_ScrollTextBox(this, x, y, get_w()-xs20, rows,
2005                 target->text, 2*text_len);
2006         scroll_text_box->create_objects();
2007         add_subwindow(ok_button = new BC_OKButton(this));
2008         add_subwindow(cancel_button = new BC_CancelButton(this));
2009         show_window();
2010         unlock_window();
2011 }
2012
2013 int ModifyTargetPatternsGUI::resize_event(int w, int h)
2014 {
2015         int xs20 = xS(20);
2016         int ys20 = yS(20);
2017         int tx = scroll_text_box->get_x();
2018         int ty = scroll_text_box->get_y();
2019         int th = h - ty - BC_OKButton::calculate_h() - ys20;
2020         int tw = w - xs20;
2021         int rows = th / text_rowsz;
2022         scroll_text_box->reposition_window(tx, ty, tw, rows);
2023         ok_button->resize_event(w, h);
2024         cancel_button->resize_event(w, h);
2025         return 1;
2026 }
2027
2028 void ModifyTargetPatternsGUI::update()
2029 {
2030         BinFolderTargetPatterns *target = (BinFolderTargetPatterns *)thread->target;
2031         const char *cp = scroll_text_box->get_text();
2032         target->update(cp);
2033 }
2034
2035
2036 ModifyTargetFileSizeGUI::ModifyTargetFileSizeGUI(ModifyTargetThread *thread)
2037  : ModifyTargetGUI(thread)
2038 {
2039 }
2040
2041 ModifyTargetFileSizeGUI::~ModifyTargetFileSizeGUI()
2042 {
2043 }
2044
2045 void ModifyTargetFileSizeGUI::create_objects()
2046 {
2047         ModifyTargetGUI::create_objects(text_box);
2048 }
2049
2050 void ModifyTargetFileSizeGUI::update()
2051 {
2052         BinFolderTargetFileSize *target = (BinFolderTargetFileSize *)thread->target;
2053         double file_size = target->file_size, around = target->around;
2054         const char *cp = text_box->get_text();  char *bp = 0;
2055         scan_around(cp, bp, file_size, around);
2056         target->update(file_size, around);
2057 }
2058
2059
2060 ModifyTargetTimeGUI::ModifyTargetTimeGUI(ModifyTargetThread *thread)
2061  : ModifyTargetGUI(thread)
2062 {
2063 }
2064
2065 ModifyTargetTimeGUI::~ModifyTargetTimeGUI()
2066 {
2067 }
2068
2069 void ModifyTargetTimeGUI::create_objects()
2070 {
2071         ModifyTargetGUI::create_objects(text_box);
2072 }
2073
2074 void ModifyTargetTimeGUI::update()
2075 {
2076         BinFolderTargetTime *target = (BinFolderTargetTime *)thread->target;
2077         int64_t mtime = target->mtime;  double around = target->around;
2078         const char *cp = text_box->get_text(); char *bp = 0;
2079         int64_t v = scan_date(cp, bp);
2080         if( bp > cp ) {
2081                 mtime = v;
2082                 if( *bp == '+' ) {
2083                         v = scan_duration(cp=bp+1, bp);
2084                         if( bp > cp ) around = v;
2085                 }
2086         }
2087         target->update(mtime, around);
2088 }
2089
2090
2091 ModifyTargetTrackTypeGUI::ModifyTargetTrackTypeGUI(ModifyTargetThread *thread)
2092  : ModifyTargetGUI(thread)
2093 {
2094 }
2095
2096 ModifyTargetTrackTypeGUI::~ModifyTargetTrackTypeGUI()
2097 {
2098 }
2099
2100 void ModifyTargetTrackTypeGUI::create_objects()
2101 {
2102         ModifyTargetGUI::create_objects(text_box);
2103 }
2104
2105 void ModifyTargetTrackTypeGUI::update()
2106 {
2107         BinFolderTargetTrackType *target = (BinFolderTargetTrackType *)thread->target;
2108         const char *cp = text_box->get_text();
2109         int data_types = 0;
2110         if( bstrcasestr(cp, _("audio")) ) data_types |= (1<<TRACK_AUDIO);
2111         if( bstrcasestr(cp, _("video")) ) data_types |= (1<<TRACK_VIDEO);
2112         target->update(data_types);
2113 }
2114
2115
2116 ModifyTargetWidthGUI::ModifyTargetWidthGUI(ModifyTargetThread *thread)
2117  : ModifyTargetGUI(thread)
2118 {
2119 }
2120
2121 ModifyTargetWidthGUI::~ModifyTargetWidthGUI()
2122 {
2123 }
2124
2125 void ModifyTargetWidthGUI::create_objects()
2126 {
2127         ModifyTargetGUI::create_objects(text_box);
2128 }
2129
2130 void ModifyTargetWidthGUI::update()
2131 {
2132         BinFolderTargetWidth *target = (BinFolderTargetWidth *)thread->target;
2133         double width = target->width, around = target->around;
2134         const char *cp = text_box->get_text();  char *bp = 0;
2135         scan_around(cp, bp, width, around);
2136         target->update(width, around);
2137 }
2138
2139
2140 ModifyTargetHeightGUI::ModifyTargetHeightGUI(ModifyTargetThread *thread)
2141  : ModifyTargetGUI(thread)
2142 {
2143 }
2144
2145 ModifyTargetHeightGUI::~ModifyTargetHeightGUI()
2146 {
2147 }
2148
2149 void ModifyTargetHeightGUI::create_objects()
2150 {
2151         ModifyTargetGUI::create_objects(text_box);
2152 }
2153
2154 void ModifyTargetHeightGUI::update()
2155 {
2156         BinFolderTargetHeight *target = (BinFolderTargetHeight *)thread->target;
2157         double height = target->height, around = target->around;
2158         const char *cp = text_box->get_text();  char *bp = 0;
2159         scan_around(cp, bp, height, around);
2160         target->update(height, around);
2161 }
2162
2163
2164 ModifyTargetFramerateGUI::ModifyTargetFramerateGUI(ModifyTargetThread *thread)
2165  : ModifyTargetGUI(thread)
2166 {
2167 }
2168
2169 ModifyTargetFramerateGUI::~ModifyTargetFramerateGUI()
2170 {
2171 }
2172
2173 void ModifyTargetFramerateGUI::create_objects()
2174 {
2175         ModifyTargetGUI::create_objects(text_box);
2176 }
2177
2178 void ModifyTargetFramerateGUI::update()
2179 {
2180         BinFolderTargetFramerate *target = (BinFolderTargetFramerate *)thread->target;
2181         double framerate = target->framerate, around = target->around;
2182         const char *cp = text_box->get_text();  char *bp = 0;
2183         scan_around(cp, bp, framerate, around);
2184         target->update(framerate, around);
2185 }
2186
2187
2188 ModifyTargetSamplerateGUI::ModifyTargetSamplerateGUI(ModifyTargetThread *thread)
2189  : ModifyTargetGUI(thread)
2190 {
2191 }
2192
2193 ModifyTargetSamplerateGUI::~ModifyTargetSamplerateGUI()
2194 {
2195 }
2196
2197 void ModifyTargetSamplerateGUI::create_objects()
2198 {
2199         ModifyTargetGUI::create_objects(text_box);
2200 }
2201
2202 void ModifyTargetSamplerateGUI::update()
2203 {
2204         BinFolderTargetSamplerate *target = (BinFolderTargetSamplerate *)thread->target;
2205         double samplerate = target->samplerate, around = target->around;
2206         const char *cp = text_box->get_text();  char *bp = 0;
2207         scan_around(cp, bp, samplerate, around);
2208         target->update(samplerate, around);
2209 }
2210
2211
2212 ModifyTargetChannelsGUI::ModifyTargetChannelsGUI(ModifyTargetThread *thread)
2213  : ModifyTargetGUI(thread)
2214 {
2215 }
2216
2217 ModifyTargetChannelsGUI::~ModifyTargetChannelsGUI()
2218 {
2219 }
2220
2221 void ModifyTargetChannelsGUI::create_objects()
2222 {
2223         ModifyTargetGUI::create_objects(text_box);
2224 }
2225
2226 void ModifyTargetChannelsGUI::update()
2227 {
2228         BinFolderTargetChannels *target = (BinFolderTargetChannels *)thread->target;
2229         double channels = target->channels, around = target->around;
2230         const char *cp = text_box->get_text();  char *bp = 0;
2231         scan_around(cp, bp, channels, around);
2232         target->update(channels, around);
2233 }
2234
2235
2236 ModifyTargetDurationGUI::ModifyTargetDurationGUI(ModifyTargetThread *thread)
2237  : ModifyTargetGUI(thread)
2238 {
2239 }
2240
2241 ModifyTargetDurationGUI::~ModifyTargetDurationGUI()
2242 {
2243 }
2244
2245 void ModifyTargetDurationGUI::create_objects()
2246 {
2247         ModifyTargetGUI::create_objects(text_box);
2248 }
2249
2250 void ModifyTargetDurationGUI::update()
2251 {
2252         BinFolderTargetDuration *target = (BinFolderTargetDuration *)thread->target;
2253         int64_t duration = target->duration, around = target->around;
2254         const char *cp = text_box->get_text();  char *bp = 0;
2255         int64_t v = scan_duration(cp, bp);
2256         if( bp > cp ) {
2257                 duration = v;
2258                 if( *bp == '+' ) {
2259                         v = scan_duration(cp=bp+1, bp);
2260                         if( bp > cp ) around = v;
2261                 }
2262         }
2263         target->update(duration, around);
2264 }
2265