add new boxblur plugin, mods to videoscope, fix segv for menu btns kfrm-tweak/kfrm...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / dbwindow.C
1
2
3 #include "bcmenuitem.h"
4 #include "bctimer.h"
5 #include "commercials.h"
6 #include "dbwindow.h"
7 #include "keys.h"
8 #include "mediadb.h"
9 #include "mwindow.h"
10 #include "mwindowgui.h"
11 #include "mutex.h"
12 #include "theme.h"
13
14 #include <string.h>
15 #include <strings.h>
16
17 static inline int min(int a,int b) { return a<b ? a : b; }
18 static inline int max(int a,int b) { return a>b ? a : b; }
19
20 #define MAX_SEARCH 100
21
22 DbSearchItem::DbSearchItem(const char *text, int color)
23  : BC_ListBoxItem(text, color)
24 {
25         vicon = 0;
26 }
27
28 DbSearchItem::~DbSearchItem()
29 {
30 }
31
32 DbWindow::
33 DbWindow(MWindow *mwindow)
34  : Thread(1, 0, 0)
35 {
36         this->mwindow = mwindow;
37         window_lock = new Mutex("DbWindow::window_lock");
38         db_lock = new Mutex("DbWindow::db_lock");
39         mdb = new MDb(this);
40         gui = 0;
41 }
42
43 DbWindow::MDb::MDb(DbWindow *d)
44  : Garbage("DbWindow::MDb"), dwin(d)
45 {
46         if( !d->mwindow->has_commercials() ) return;
47          openDb();  detachDb();
48 }
49
50 DbWindow::MDb::~MDb()
51 {
52         closeDb();
53 }
54
55 DbWindow::~DbWindow()
56 {
57         stop();
58         delete gui;
59         mdb->remove_user();
60         delete window_lock;
61         delete db_lock;
62 }
63
64 void DbWindow::
65 start()
66 {
67         window_lock->lock("DbWindow::start1");
68         if( Thread::running() ) {
69                 gui->lock_window("DbWindow::start2");
70                 gui->raise_window();
71                 gui->unlock_window();
72         }
73         else {
74                 delete gui;
75                 gui = new DbWindowGUI(this);
76                 Thread::start();
77         }
78         window_lock->unlock();
79 }
80
81
82 void DbWindow::
83 stop()
84 {
85         if( Thread::running() ) {
86                 window_lock->lock("DbWindow::stop");
87                 if( gui ) gui->set_done(1);
88                 window_lock->unlock();
89                 Thread::cancel();
90         }
91         Thread::join();
92 }
93
94 void DbWindow::
95 run()
96 {
97         gui->lock_window("DbWindow::run");
98         gui->create_objects();
99         gui->search(MAX_SEARCH,"");
100         gui->start_drawing(0);
101         gui->unlock_window();
102         gui->run_window();
103         window_lock->lock("DbWindow::stop");
104         delete gui;  gui = 0;
105         window_lock->unlock();
106 }
107
108
109 DbWindowTitleText::
110 DbWindowTitleText(DbWindowGUI *gui, int x, int y)
111  : BC_CheckBox(x, y, &gui->title_text_enable, _("titles"))
112 {
113         this->gui = gui;
114 }
115
116 DbWindowTitleText::~DbWindowTitleText()
117 {
118 }
119
120 int DbWindowTitleText::
121 handle_event()
122 {
123         gui->title_text_enable = get_value();
124         if( !gui->title_text_enable ) gui->info_text->update(1);
125         return 1;
126 }
127
128
129 DbWindowInfoText::
130 DbWindowInfoText(DbWindowGUI *gui, int x, int y)
131  : BC_CheckBox(x, y, &gui->info_text_enable, _("info"))
132 {
133         this->gui = gui;
134 }
135
136 DbWindowInfoText::~DbWindowInfoText()
137 {
138 }
139
140 int DbWindowInfoText::
141 handle_event()
142 {
143         gui->info_text_enable = get_value();
144         if( !gui->info_text_enable ) gui->title_text->update(1);
145         return 1;
146 }
147
148
149 DbWindowMatchCase::
150 DbWindowMatchCase(DbWindowGUI *gui, int x, int y)
151  : BC_CheckBox(x, y, &gui->match_case_enable, _("match case"))
152 {
153         this->gui = gui;
154 }
155
156 DbWindowMatchCase::~DbWindowMatchCase()
157 {
158 }
159
160 int DbWindowMatchCase::
161 handle_event()
162 {
163         gui->match_case_enable = get_value();
164         return 1;
165 }
166
167
168 DbWindowText::
169 DbWindowText(DbWindowGUI *gui, int x, int y, int w)
170  : BC_TextBox(x, y, w, 1, "")
171 {
172         this->gui = gui;
173 }
174
175 DbWindowText::~DbWindowText()
176 {
177 }
178
179
180 int DbWindowText::
181 handle_event()
182 {
183         return 1;
184 }
185
186 int DbWindowText::
187 keypress_event()
188 {
189         switch(get_keypress())
190         {
191         case RETURN:
192                 gui->search(MAX_SEARCH, get_text());
193                 return 1;
194         }
195
196         return BC_TextBox::keypress_event();
197 }
198
199
200 DbWindowScan::
201 DbWindowScan(MWindow *mwindow)
202  : BC_MenuItem(_("Media DB..."), _("Shift-M"), 'M')
203 {
204         set_shift();
205         this->mwindow = mwindow;
206 }
207
208 DbWindowScan::~DbWindowScan()
209 {
210 }
211
212 int DbWindowScan::
213 handle_event()
214 {
215         mwindow->commit_commercial();
216         mwindow->gui->db_window->start();
217         return 1;
218 }
219
220 DbWindowStart::
221 DbWindowStart(DbWindowGUI *gui, int x, int y)
222  : BC_GenericButton(x, y, _("Search"))
223 {
224         this->gui = gui;
225 }
226
227 DbWindowStart::~DbWindowStart()
228 {
229 }
230
231 int DbWindowStart::
232 handle_event()
233 {
234         gui->search(MAX_SEARCH,"");
235         return 1;
236 }
237
238 DbWindowDeleteItems::
239 DbWindowDeleteItems(DbWindowGUI *gui, int x, int y)
240  : BC_GenericButton(x, y, _("Delete"))
241 {
242         this->gui = gui;
243 }
244
245 DbWindowDeleteItems::~DbWindowDeleteItems()
246 {
247 }
248
249 int DbWindowDeleteItems::
250 handle_event()
251 {
252         gui->search_list->set_view_popup(0);
253         gui->delete_items();
254         return 1;
255 }
256
257
258 DbWindowCancel::
259 DbWindowCancel(DbWindowGUI *gui, int x, int y)
260  : BC_CancelButton(x, y)
261 {
262         this->gui = gui;
263 }
264
265 DbWindowCancel::~DbWindowCancel()
266 {
267 }
268
269 int DbWindowCancel::
270 handle_event()
271 {
272         gui->set_done(1);
273         return 1;
274 }
275
276
277 DbWindowList::
278 DbWindowList(DbWindowGUI *gui, int x, int y, int w, int h)
279  : BC_ListBox(x, y, w, h, LISTBOX_TEXT,
280         (ArrayList<BC_ListBoxItem*> *) &gui->search_items[0],
281         &gui->search_column_titles[0], &gui->search_column_widths[0],
282         sizeof_col, 0, 0, LISTBOX_MULTIPLE)
283 {
284         this->gui = gui;
285         set_sort_column(gui->sort_column);
286         set_sort_order(gui->sort_order);
287         set_allow_drag_column(1);
288 }
289
290 DbWindowList::~DbWindowList()
291 {
292 }
293
294 int DbWindowList::
295 handle_event()
296 {
297         return 1;
298 }
299
300 void DbWindowList::
301 set_view_popup(DbWindowVIcon *vicon)
302 {
303         gui->vicon_thread->set_view_popup(vicon);
304 }
305
306 int DbWindowList::
307 keypress_event()
308 {
309         switch(get_keypress()) {
310         case ESC:
311                 set_view_popup(0);
312                 break;
313         case DELETE:
314                 gui->del_items->handle_event();
315                 break;
316         default:
317                 return 0;
318         }
319         return 1;
320 }
321
322 int DbWindowList::
323 sort_order_event()
324 {
325         gui->stop_drawing();
326         gui->sort_events(get_sort_column(), get_sort_order());
327         gui->start_drawing();
328         return 1;
329 }
330
331 int DbWindowList::
332 move_column_event()
333 {
334         gui->stop_drawing();
335         gui->move_column(get_from_column(), get_to_column());
336         gui->start_drawing();
337         return 1;
338 }
339
340 DbWindowVIcon::DbWindowVIcon()
341  : VIcon(SWIDTH, SHEIGHT, VICON_RATE)
342 {
343         lbox = 0;
344         item = 0;
345 }
346
347 DbWindowVIcon::~DbWindowVIcon()
348 {
349 }
350
351 DbWindowVIconThread::
352 DbWindowVIconThread(DbWindowGUI *gui)
353  : VIconThread(gui->search_list, SWIDTH,SHEIGHT, 4*SWIDTH,4*SHEIGHT)
354 {
355         this->gui = gui;
356         list_update = 0;
357 }
358
359 DbWindowVIconThread::
360 ~DbWindowVIconThread()
361 {
362         t_heap.remove_all();
363         vicons.remove_all_objects();
364 }
365
366 DbWindowVIcon *DbWindowVIconThread::get_vicon(int i, DbSearchItem *item)
367 {
368         while( i >= vicons.size() ) {
369                 DbWindowVIcon *vicon = new DbWindowVIcon();
370                 vicons.append(vicon);
371         }
372         DbWindowVIcon *vicon = vicons[i];
373         vicon->lbox = gui->search_list;
374         vicon->item = item;
375         item->vicon = vicon;
376         return vicon;
377 }
378
379 void DbWindowVIconThread::drawing_started()
380 {
381         if( !list_update ) return;
382         list_update = 0;
383         gui->update();
384         gui->search_list->update_images();
385         reset_images();
386 }
387
388 VFrame *DbWindowVIcon::frame()
389 {
390         if( seq_no >= images.size() )
391                 load_frames(lbox->gui->dwindow->mdb);
392         return *images[seq_no];
393 }
394
395 int64_t DbWindowVIcon::set_seq_no(int64_t no)
396 {
397         if( no >= clip_size ) no = 0;
398         return seq_no = no;
399 }
400
401
402 void DbWindowVIcon::load_frames(DbWindow::MDb *mdb)
403 {
404         if( !mdb->attach_rd() ) {
405                 read_frames(mdb);
406                 mdb->detach();
407         }
408 }
409
410 void DbWindowVIcon::read_frames(DbWindow::MDb *mdb)
411 {
412         while( seq_no >= images.size() ) {
413                 int no = images.size();
414                 if( no >= prefix_size ) no += suffix_offset;
415                 if( mdb->get_sequences(clip_id, no) ) continue;
416                 if( mdb->timeline_sequence_no() != no ) continue;
417                 int frame_id = mdb->timeline_frame_id();
418                 if( frame_id < 0 ) continue;
419                 int swidth = (SWIDTH+1) & ~1, sheight = (SHEIGHT+1) & ~1;
420                 VIFrame *vifrm = new VIFrame(swidth, sheight, BC_YUV420P);
421                 VFrame *img = *vifrm;
422                 memset(img->get_y(),0x00,swidth * sheight);
423                 memset(img->get_u(),0x80,swidth/2 * sheight/2);
424                 memset(img->get_v(),0x80,swidth/2 * sheight/2);
425                 uint8_t *yp = img->get_y();  int sw = -1, sh = -1;
426                 mdb->get_image(frame_id, yp, sw, sh);
427                 images.append(vifrm);
428         }
429 }
430
431 int DbWindowVIcon::get_vx()
432 {
433         return lbox->get_item_x(item);
434 }
435 int DbWindowVIcon::get_vy()
436 {
437         return lbox->get_item_y(item);
438 }
439
440 void DbWindowVIcon::
441 update_image(DbWindowGUI *gui, int clip_id)
442 {
443         DbWindow::MDb *mdb = gui->dwindow->mdb;
444         if( !mdb->attach_rd() ) {
445                 if( !mdb->clip_id(clip_id) ) {
446                         this->clip_id = clip_id;
447                         this->clip_size = mdb->clip_size();;
448                         this->prefix_size = mdb->clip_prefix_size();
449                         this->suffix_offset = mdb->clip_frames() - clip_size;
450                         double framerate = mdb->clip_framerate();
451                         this->frame_rate = framerate > 0 ? framerate : VICON_RATE;
452                         gui->vicon_thread->add_vicon(this);
453                 }
454                 mdb->detach();
455         }
456 }
457
458
459 int DbWindowList::update_images()
460 {
461         DbWindowVIconThread *thread = gui->vicon_thread;
462         thread->t_heap.remove_all();
463
464         int i = get_first_visible();
465         int k = sizeof_col;
466         while( --k >= 0 && gui->search_columns[k] != col_vicon );
467         if( k >= 0 && i >= 0 ) {
468                 int sz = gui->search_results.size();
469                 int last = get_last_visible();
470                 if( last < sz ) sz = last+1;
471                 for( int n=0; i<sz; ++i, ++n ) {
472                         DbSearchItem *item = gui->search_items[k][i];
473                         DbWindowVIcon *vicon = thread->get_vicon(n, item);
474                         vicon->update_image(gui, gui->search_results[i]->id);
475                 }
476         }
477         return 0;
478 }
479
480 int DbWindowList::
481 update()
482 {
483         BC_ListBox::update((ArrayList<BC_ListBoxItem*>*)gui->search_items,
484                         &gui->search_column_titles[0], &gui->search_column_widths[0],
485                         sizeof_col);
486         return 0;
487 }
488
489 int DbWindowList::
490 selection_changed()
491 {
492         gui->stop_drawing();
493         set_view_popup(0);
494         int idx = get_selection_number(0, 0);
495         if( idx >= 0 && get_selection_number(0, 1) < 0 ) {
496                 DbSearchItem *item = gui->search_items[0][idx];
497                 set_view_popup(item->vicon);
498         }
499         gui->start_drawing(0);
500         return 0;
501 }
502
503
504 void DbWindowGUI::
505 create_objects()
506 {
507         int xs10 = xS(10), xs15 = xS(15);
508         int ys5 = yS(5), ys10 = yS(10);
509         int pady = BC_TextBox::calculate_h(this, MEDIUMFONT, 0, 1) + ys5;
510         int padx = BC_Title::calculate_w(this, (char*)"X", MEDIUMFONT);
511         int x = padx/2, y = pady/4;
512         text_x = x;  text_y = y;
513         BC_Title *title = new BC_Title(text_x, text_y, _("Text:"), MEDIUMFONT, YELLOW);
514         add_subwindow(title);  x += title->get_w();
515         search_text = new DbWindowText(this, x, y, get_w()-x-xs10);
516         add_subwindow(search_text);
517         x = padx;  y += pady + ys5;
518         title_text = new DbWindowTitleText(this, x, y);
519         add_subwindow(title_text);  x += title_text->get_w() + padx;
520         info_text = new DbWindowInfoText(this, x, y);
521         add_subwindow(info_text);  x += title_text->get_w() + padx;
522         match_case = new DbWindowMatchCase(this, x, y);
523         add_subwindow(match_case); x += match_case->get_w() + padx;
524         x = padx;  y += pady + ys5;
525         search_x = xs10;
526         search_y = get_h() - DbWindowStart::calculate_h() - ys10;
527         search_start = new DbWindowStart(this, search_x, search_y);
528         add_subwindow(search_start);
529         del_items_x = search_x + DbWindowStart::calculate_w(this, search_start->get_text()) + xs15;
530         del_items_y = search_y;
531         del_items = new DbWindowDeleteItems(this, del_items_x, del_items_y);
532         add_subwindow(del_items);
533         cancel_w = DbWindowCancel::calculate_w();
534         cancel_h = DbWindowCancel::calculate_h();
535         cancel_x = get_w() - cancel_w - xs10;
536         cancel_y = get_h() - cancel_h - ys10;
537         cancel = new DbWindowCancel(this, cancel_x, cancel_y);
538         add_subwindow(cancel);
539         list_x = x;  list_y = y;
540         int list_w = get_w()-xs10 - list_x;
541         int list_h = min(search_y, cancel_y)-ys10 - list_y;
542         search_list = new DbWindowList(this, list_x, list_y, list_w, list_h);
543         add_subwindow(search_list);
544         vicon_thread = new DbWindowVIconThread(this);
545         vicon_thread->start();
546         search_list->show_window();
547         canvas_x = 0;
548         canvas_y = search_list->get_title_h();
549         canvas_w = search_list->get_w();
550         canvas_h = search_list->get_h() - canvas_y;
551         canvas = new DbWindowCanvas(this, canvas_x, canvas_y, canvas_w, canvas_h);
552         canvas->use_auxwindow(search_list);
553         canvas->create_objects(0);
554         set_icon(dwindow->mwindow->theme->get_image("record_icon"));
555 }
556
557 #define DBW_W xS(900)
558 #define DBW_H yS(600)
559
560 DbWindowGUI::
561 DbWindowGUI(DbWindow *dwindow)
562  : BC_Window(_(PROGRAM_NAME ": DbWindow"),
563         dwindow->mwindow->gui->get_abs_cursor_x(1) - DBW_W / 2,
564         dwindow->mwindow->gui->get_abs_cursor_y(1) - DBW_H / 2,
565         DBW_W, DBW_H, xS(400), yS(400))
566 {
567         this->dwindow = dwindow;
568
569         search_start = 0;
570         search_list = 0;
571         cancel = 0;
572
573         search_x = search_y = 0;
574         cancel_x = cancel_y = cancel_w = cancel_h = 0;
575         del_items_x = del_items_y = 0;
576         list_x = list_y = list_w = list_h = 0;
577         sort_column = 1;  sort_order = 0;
578
579         title_text_enable = 1;
580         info_text_enable = 1;
581         match_case_enable = 1;
582
583         search_columns[col_vicon] = col_vicon;
584         search_columns[col_id] = col_id;
585         search_columns[col_length] = col_length;
586         search_columns[col_source] = col_source;
587         search_columns[col_title] = col_title;
588         search_columns[col_start_time] = col_start_time;
589         search_columns[col_access_time] = col_access_time;
590         search_columns[col_access_count] = col_access_count;
591         search_column_titles[col_vicon] = _("vicon");
592         search_column_titles[col_id] = _("Id");
593         search_column_titles[col_length] = _("length");
594         search_column_titles[col_source] = _("Source");
595         search_column_titles[col_title] = C_("Title");
596         search_column_titles[col_start_time] = _("Start time");
597         search_column_titles[col_access_time] = _("Access time");
598         search_column_titles[col_access_count] = _("count");
599         search_column_widths[col_vicon] = xS(90);
600         search_column_widths[col_id] = xS(60);
601         search_column_widths[col_length] = xS(80);
602         search_column_widths[col_source] = xS(50);
603         search_column_widths[col_title] = xS(160);
604         search_column_widths[col_start_time] = xS(140);
605         search_column_widths[col_access_time] = xS(140);
606         search_column_widths[col_access_count] = xS(60);
607         int wd = 0;
608         for( int i=0; i<sizeof_col; ++i )
609                 if( i != col_title ) wd += search_column_widths[i];
610         search_column_widths[col_title] = get_w() - wd - xS(40);
611 }
612
613 DbWindowGUI::~DbWindowGUI()
614 {
615         for( int i=0; i<sizeof_col; ++i )
616                 search_items[i].remove_all_objects();
617         delete vicon_thread;
618         delete canvas;
619 }
620
621 int DbWindowGUI::
622 resize_event(int w, int h)
623 {
624         int xs10 = xS(10), xs15 = xS(15);
625         int ys10 = yS(10);
626         stop_drawing();
627         int cancel_x = w - BC_CancelButton::calculate_w() - xs10;
628         int cancel_y = h - BC_CancelButton::calculate_h() - ys10;
629         cancel->reposition_window(cancel_x, cancel_y);
630         search_x = xs10;
631         search_y = h - BC_GenericButton::calculate_h() - ys10;
632         search_start->reposition_window(search_x, search_y);
633         del_items_x = search_x + DbWindowStart::calculate_w(this, search_start->get_text()) + xs15;
634         del_items_y = search_y;
635         del_items->reposition_window(del_items_x,del_items_y);
636         int list_w = w-xs10 - list_x;
637         int list_h = min(search_y, cancel_y)-ys10 - list_y;
638         canvas_w = SWIDTH;  canvas_h = SHEIGHT;
639         canvas_x = cancel_x - canvas_w - xs15;
640         canvas_y = get_h() - canvas_h - ys10;
641         canvas->reposition_window(0, canvas_x, canvas_y, canvas_w, canvas_h);
642 //      int wd = 0;
643 //      for( int i=0; i<sizeof_col; ++i )
644 //              if( i != col_title ) wd += search_column_widths[i];
645 //      search_column_widths[col_title] = w - wd - xS(40);
646         search_list->reposition_window(list_x, list_y, list_w, list_h);
647         start_drawing();
648         return 1;
649 }
650
651 int DbWindowGUI::
652 close_event()
653 {
654         set_done(1);
655         return 1;
656 }
657
658 int DbWindowGUI::
659 stop_drawing()
660 {
661         vicon_thread->stop_drawing();
662         return 0;
663 }
664
665 int DbWindowGUI::
666 start_drawing(int update)
667 {
668         if( update )
669                 vicon_thread->list_update = 1;
670         vicon_thread->start_drawing();
671         return 0;
672 }
673
674 int DbWindowGUI::search_string(const char *text, const char *sp)
675 {
676         if( sp ) {
677                 int n = strlen(text);
678                 for( const char *cp=sp; *cp!=0; ++cp ) {
679                         if( match_case_enable ? !strncmp(text, cp, n) :
680                                 !strncasecmp(text, cp, n) ) return 1;
681                 }
682         }
683         return 0;
684 }
685
686 void DbWindowGUI::search_clips(MediaDb *mdb, int n, const char *text)
687 {
688         //Db::write_blocked by(mdb->db.Clip_set);
689         if( !mdb->clips_first_id() ) do {
690                 if( (title_text_enable && search_string(text, mdb->clip_title())) ||
691                     (info_text_enable  && search_string(text, mdb->clip_path())) ) {
692                         int id = mdb->clip_id();
693                         double system_time = mdb->clip_system_time();
694                         double start_time = 0;
695                         if( system_time > 0 )
696                                 start_time = system_time + mdb->clip_position();
697                         double access_time = 0;  int access_count = 0;
698                         if( !mdb->views_clip_id(id) ) {
699                                  access_time = mdb->views_access_time();
700                                  access_count =  mdb->views_access_count();
701                         }
702                         double length = mdb->clip_frames() / mdb->clip_framerate();
703                         search_results.append(new DbWindowItem(id, mdb->clip_title(),
704                                 mdb->clip_path(), length, start_time, access_time, access_count));
705                 }
706         } while( --n >= 0 && !mdb->clips_next_id() );
707 }
708
709 void DbWindowGUI::search(int n, const char *text)
710 {
711         stop_drawing();
712         search_results.remove_all();
713         DbWindow::MDb *mdb = dwindow->mdb;
714         if( !mdb->attach_rd() ) {
715                 search_clips(mdb, n, text);
716                 mdb->detach();
717         }
718         start_drawing();
719 }
720
721 int DbWindowGUI::delete_selection(MediaDb *mdb)
722 {
723         int n = 0;
724         for( int k=0; k<search_results.size(); ++k ) {
725                 if( !search_items[0][k]->get_selected() ) continue;
726                 int id = search_results[k]->id;
727                 if( mdb->del_clip_set(id) ) {
728                         printf(_("failed delete clip id %d\n"),id);
729                         continue;
730                 }
731                 search_results.remove_object_number(k);
732                 for( int i=0; i<sizeof_col; ++i )
733                         search_items[i].remove_object_number(k);
734                 ++n;  --k;
735         }
736         if( n > 0 ) mdb->commitDb();
737         return 0;
738 }
739
740 void DbWindowGUI::delete_items()
741 {
742         stop_drawing();
743         DbWindow::MDb *mdb = dwindow->mdb;
744         if( !mdb->attach_wr() ) {
745                 delete_selection(mdb);
746                 mdb->detach();
747         }
748         start_drawing();
749 }
750
751 void DbWindowGUI::
752 update()
753 {
754
755         for( int i=0; i<sizeof_col; ++i )
756                 search_items[i].remove_all_objects();
757
758         char text[BCTEXTLEN];
759         int n = search_results.size();
760         for( int i=0; i<n; ++i ) {
761                 DbWindowItem *item = search_results[i];
762                 for( int k=0; k<sizeof_col; ++k ) {
763                         const char *cp = 0;  text[0] = 0;
764                         switch( search_columns[k] ) {
765                         case col_vicon:
766                                 cp = "";
767                                 break;
768                         case col_id: {
769                                 sprintf(text,"%4d", item->id);
770                                 cp = text;
771                                 break; }
772                         case col_length: {
773                                 sprintf(text, "%8.3f", item->length);
774                                 cp = text;
775                                 break; }
776                         case col_source:
777                                 cp = item->source;
778                                 break;
779                         case col_title:
780                                 cp = item->title;
781                                 break;
782                         case col_start_time: {
783                                 time_t st = item->start_time;
784                                 if( st > 0 ) {
785                                         struct tm stm;  localtime_r(&st,&stm);
786                                         sprintf(text,"%02d:%02d:%02d %02d/%02d/%02d",
787                                                 stm.tm_hour, stm.tm_min, stm.tm_sec,
788                                                 stm.tm_year%100, stm.tm_mon, stm.tm_mday);
789                                 }
790                                 cp = text;
791                                 break; }
792                         case col_access_time: {
793                                 time_t at = item->access_time;
794                                 if( at > 0 ) {
795                                         struct tm atm;  localtime_r(&at,&atm);
796                                         sprintf(text,"%02d/%02d/%02d %02d:%02d:%02d",
797                                                 atm.tm_year%100, atm.tm_mon, atm.tm_mday,
798                                                 atm.tm_hour, atm.tm_min, atm.tm_sec);
799                                 }
800                                 cp = text;
801                                 break; }
802                         case col_access_count: {
803                                 sprintf(text,"%4d", item->access_count);
804                                 cp = text;
805                                 break; }
806                         }
807                         if( !cp ) continue;
808                         DbSearchItem *item = new DbSearchItem(cp, LTYELLOW);
809                         if( search_columns[k] == col_vicon ) {
810                                 item->set_text_w(SWIDTH+xS(10));
811                                 item->set_text_h(SHEIGHT+yS(5));
812                                 item->set_searchable(0);
813                         }
814                         search_items[k].append(item);
815                 }
816         }
817
818         search_list->update();
819 }
820
821
822 DbWindowItem::
823 DbWindowItem(int id, const char *source, const char *title,
824         double length, double start_time,
825         double access_time, int access_count)
826 {
827         this->id = id;
828         if( !source ) source = "";
829         this->source = new char[strlen(source)+1];
830         strcpy(this->source,source);
831         if( !title ) title = "";
832         this->title = new char[strlen(title)+1];
833         strcpy(this->title,title);
834         this->length = length;
835         this->start_time = start_time;
836         this->access_time = access_time;
837         this->access_count = access_count;
838 }
839
840 DbWindowItem::
841 ~DbWindowItem()
842 {
843         delete source;
844         delete title;
845 }
846
847 #define CmprFn(nm,key) int DbWindowGUI:: \
848 cmpr_##nm(const void *a, const void *b) { \
849   DbWindowItem *&ap = *(DbWindowItem **)a; \
850   DbWindowItem *&bp = *(DbWindowItem **)b; \
851   int n = key; if( !n ) n = ap->no-bp->no; \
852   return n; \
853 }
854
855 extern long cin_timezone;
856
857 CmprFn(id_dn, ap->id - bp->id)
858 CmprFn(id_up, bp->id - ap->id)
859 CmprFn(length_dn, ap->length - bp->length)
860 CmprFn(length_up, bp->length - ap->length)
861 CmprFn(Source_dn, strcasecmp(ap->source, bp->source))
862 CmprFn(source_dn, strcmp(ap->source, bp->source))
863 CmprFn(Source_up, strcasecmp(bp->source, ap->source))
864 CmprFn(source_up, strcmp(bp->source, ap->source))
865 CmprFn(Title_dn, strcasecmp(ap->title, bp->title))
866 CmprFn(title_dn, strcmp(ap->title, bp->title))
867 CmprFn(Title_up, strcasecmp(bp->title, ap->title))
868 CmprFn(title_up, strcmp(bp->title, ap->title))
869 CmprFn(start_time_dn, fmod(ap->start_time-cin_timezone,24*3600) - fmod(bp->start_time-cin_timezone,24*3600))
870 CmprFn(start_time_up, fmod(bp->start_time-cin_timezone,24*3600) - fmod(ap->start_time-cin_timezone,24*3600))
871 CmprFn(access_time_dn, ap->access_time - bp->access_time)
872 CmprFn(access_time_up, bp->access_time - ap->access_time)
873 CmprFn(access_count_dn, ap->access_count - bp->access_count)
874 CmprFn(access_count_up, bp->access_count - ap->access_count)
875
876
877 void DbWindowGUI::
878 sort_events(int column, int order)
879 {
880         sort_column = column;  sort_order = order;
881         if( search_columns[sort_column] == col_vicon ) return;
882         int n = search_results.size();
883         if( !n ) return;
884         DbWindowItem **items = &search_results[0];
885         for( int i=0; i<n; ++i ) items[i]->no = i;
886
887         int(*cmpr)(const void *, const void *) = 0;
888         switch( search_columns[sort_column] ) {
889         case col_id:
890                 cmpr = sort_order ? cmpr_id_up : cmpr_id_dn;
891                 break;
892         case col_length:
893                 cmpr = sort_order ? cmpr_length_up : cmpr_length_dn;
894                 break;
895         case col_source:
896                 cmpr = match_case_enable ?
897                         (sort_order ? cmpr_Source_up : cmpr_Source_dn) :
898                         (sort_order ? cmpr_source_up : cmpr_source_dn) ;
899                 break;
900         case col_title:
901                 cmpr = match_case_enable ?
902                         (sort_order ? cmpr_Title_up : cmpr_Title_dn) :
903                         (sort_order ? cmpr_title_up : cmpr_title_dn) ;
904                 break;
905         case col_start_time:
906                 cmpr = sort_order ? cmpr_start_time_up : cmpr_start_time_dn;
907                 break;
908         case col_access_time:
909                 cmpr = sort_order ? cmpr_access_time_up : cmpr_access_time_dn;
910                 break;
911         case col_access_count:
912                 cmpr = sort_order ? cmpr_access_count_up : cmpr_access_count_dn;
913                 break;
914         }
915
916         if( cmpr ) qsort(items, n, sizeof(*items), cmpr);
917 }
918
919 void DbWindowGUI::
920 move_column(int src, int dst)
921 {
922         if( src == dst ) return;
923         int src_column = search_columns[src];
924         const char *src_column_title = search_column_titles[src];
925         int src_column_width = search_column_widths[src];
926         if( src < dst ) {
927                 for( int i=src; i<dst; ++i ) {
928                         search_columns[i] = search_columns[i+1];
929                         search_column_titles[i] = search_column_titles[i+1];
930                         search_column_widths[i] = search_column_widths[i+1];
931                 }
932         }
933         else {
934                 for( int i=src; i>dst; --i ) {
935                         search_columns[i] = search_columns[i-1];
936                         search_column_titles[i] = search_column_titles[i-1];
937                         search_column_widths[i] = search_column_widths[i-1];
938                 }
939         }
940         search_columns[dst] = src_column;
941         search_column_titles[dst] = src_column_title;
942         search_column_widths[dst] = src_column_width;
943         int k = sizeof_col;
944         while( --k >= 0 && search_columns[k] != col_vicon );
945         if( k >= 0 ) search_list->set_master_column(k,0);
946 }
947
948
949 DbWindowCanvas::
950 DbWindowCanvas(DbWindowGUI *gui, int x, int y, int w, int h)
951  : Canvas(gui->dwindow->mwindow, gui, x, y, w, h, w, h, 0)
952 {
953         this->gui = gui;
954 }
955
956 DbWindowCanvas::
957 ~DbWindowCanvas()
958 {
959 }
960
961 void DbWindowCanvas::
962 flash_canvas()
963 {
964         BC_WindowBase* wdw = get_canvas();
965         wdw->lock_window("DbWindowCanvas::flash_canvas");
966         wdw->flash();
967         wdw->unlock_window();
968 }
969
970 void DbWindowCanvas::
971 draw_frame(VFrame *frame, int x, int y, int w, int h)
972 {
973         BC_WindowBase* wdw = get_canvas();
974         int min_y = gui->search_list->get_title_h();
975         int max_y = gui->search_list->get_view_h()+min_y;
976         min_y += 2;  max_y -= 2;
977         int sx = 0, sy = 0, sw = frame->get_w(), sh = frame->get_h();
978         int dy = min_y - y;
979         if( dy > 0 ) { sy += dy;  y = min_y;   sh -= dy;  h -= dy; }
980         dy = y+h - max_y;
981         if( dy > 0 ) { sh -= dy;  h -= dy; }
982         sh &= ~1;
983         if( sh > 0 ) {
984                 //wdw->lock_window("DbWindowCanvas::draw_frame");
985                 wdw->draw_vframe(frame, x,y,w,(h&~1), sx,sy,sw,sh, 0);
986                 //wdw->unlock_window();
987         }
988 }
989
990