add browse bg btn in titler, prevent a few badwin signals, titler text popup upgrades
[goodguy/history.git] / cinelerra-5.1 / plugins / titler / titlerwindow.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
24 #include "browsebutton.h"
25 #include "clip.h"
26 #include "cstrdup.h"
27 #include "automation.h"
28 #include "cwindow.h"
29 #include "cwindowgui.h"
30 #include "edl.h"
31 #include "edlsession.h"
32 #include "language.h"
33 #include "mwindow.h"
34 #include "plugin.h"
35 #include "pluginserver.h"
36 #include "theme.h"
37 #include "track.h"
38 #include "titlerwindow.h"
39 #include "bcfontentry.h"
40
41 #include <wchar.h>
42
43 static const int timeunit_formats[] =
44 {
45         TIME_HMS,
46         TIME_SECONDS,
47         TIME_HMSF,
48         TIME_SAMPLES,
49         TIME_SAMPLES_HEX,
50         TIME_FRAMES,
51         TIME_FEET_FRAMES
52 };
53
54 TitleWindow::TitleWindow(TitleMain *client)
55  : PluginClientWindow(client,
56         client->config.window_w, client->config.window_h, 100, 100, 1)
57 {
58 //printf("TitleWindow::TitleWindow %d %d %d\n", __LINE__, client->config.window_w, client->config.window_h);
59         this->client = client;
60
61         font_title = 0;
62         font = 0;
63         font_tumbler = 0;
64         x_title = 0; title_x = 0;
65         y_title = 0; title_y = 0;
66         w_title = 0; title_w = 0;
67         h_title = 0; title_h = 0;
68         dropshadow_title = 0; dropshadow = 0;
69         outline_title = 0;    outline = 0;
70         stroker_title = 0;    stroker = 0;
71         style_title = 0;
72         italic = 0;
73         bold = 0;
74         drag = 0;
75         cur_popup = 0;
76         fonts_popup = 0;
77
78         color_x = color_y = 0;
79         outline_color_x = outline_color_y = 0;
80         drag_dx = drag_dy = dragging = 0;
81         cur_ibeam = -1;
82
83         size_title = 0;
84         size = 0;
85         size_tumbler = 0;
86         pitch_title = 0;
87         pitch = 0;
88         encoding_title = 0;
89         encoding = 0;
90         color_button = 0;
91         color_thread = 0;
92         outline_color_button = 0;
93         outline_color_thread = 0;
94         motion_title = 0;
95         motion = 0;
96         line_pitch = 0;
97         loop = 0;
98         fadein_title = 0;
99         fade_in = 0;
100         fadeout_title = 0;
101         fade_out = 0;
102         text_title = 0;
103         text = 0;
104         justify_title = 0;
105         left = 0;  center = 0;  right = 0;
106         top = 0;   mid = 0;     bottom = 0;
107         speed_title = 0;
108         speed = 0;
109         timecode = 0;
110         timecode_format = 0;
111         background = 0;
112         background_path = 0;
113         loop_playback = 0;
114 }
115
116 TitleWindow::~TitleWindow()
117 {
118         ungrab(client->server->mwindow->cwindow->gui);
119         delete fonts_popup;
120         for( int i=0; i<fonts.size(); ++i )
121                 delete fonts[i]->get_icon();
122
123         sizes.remove_all_objects();
124         delete timecode_format;
125         delete color_thread;
126         delete title_x;
127         delete title_y;
128 }
129
130 void TitleWindow::create_objects()
131 {
132         int x = 10, y = 10;
133         int margin = client->get_theme()->widget_border;
134         char string[BCTEXTLEN];
135
136 #define COLOR_W 50
137 #define COLOR_H 30
138         client->build_previews(this);
139
140         sizes.append(new BC_ListBoxItem("8"));
141         sizes.append(new BC_ListBoxItem("9"));
142         sizes.append(new BC_ListBoxItem("10"));
143         sizes.append(new BC_ListBoxItem("11"));
144         sizes.append(new BC_ListBoxItem("12"));
145         sizes.append(new BC_ListBoxItem("13"));
146         sizes.append(new BC_ListBoxItem("14"));
147         sizes.append(new BC_ListBoxItem("16"));
148         sizes.append(new BC_ListBoxItem("18"));
149         sizes.append(new BC_ListBoxItem("20"));
150         sizes.append(new BC_ListBoxItem("22"));
151         sizes.append(new BC_ListBoxItem("24"));
152         sizes.append(new BC_ListBoxItem("26"));
153         sizes.append(new BC_ListBoxItem("28"));
154         sizes.append(new BC_ListBoxItem("32"));
155         sizes.append(new BC_ListBoxItem("36"));
156         sizes.append(new BC_ListBoxItem("40"));
157         sizes.append(new BC_ListBoxItem("48"));
158         sizes.append(new BC_ListBoxItem("56"));
159         sizes.append(new BC_ListBoxItem("64"));
160         sizes.append(new BC_ListBoxItem("72"));
161         sizes.append(new BC_ListBoxItem("100"));
162         sizes.append(new BC_ListBoxItem("128"));
163         sizes.append(new BC_ListBoxItem("256"));
164         sizes.append(new BC_ListBoxItem("512"));
165         sizes.append(new BC_ListBoxItem("1024"));
166
167         paths.append(new BC_ListBoxItem(TitleMain::motion_to_text(NO_MOTION)));
168         paths.append(new BC_ListBoxItem(TitleMain::motion_to_text(BOTTOM_TO_TOP)));
169         paths.append(new BC_ListBoxItem(TitleMain::motion_to_text(TOP_TO_BOTTOM)));
170         paths.append(new BC_ListBoxItem(TitleMain::motion_to_text(RIGHT_TO_LEFT)));
171         paths.append(new BC_ListBoxItem(TitleMain::motion_to_text(LEFT_TO_RIGHT)));
172
173
174
175 // Construct font list
176         ArrayList<BC_FontEntry*> *fontlist = get_resources()->fontlist;
177
178         for( int i=0; i<fontlist->size(); ++i ) {
179                 int exists = 0;
180                 for( int j=0; j<fonts.size(); ++j ) {
181                         if( !strcasecmp(fonts.get(j)->get_text(),
182                                 fontlist->get(i)->displayname) ) {
183                                 exists = 1;
184                                 break;
185                         }
186                 }
187
188                 BC_ListBoxItem *item = 0;
189                 if( !exists ) {
190                         fonts.append(item = new
191                                 BC_ListBoxItem(fontlist->get(i)->displayname));
192                         if( !strcmp(client->config.font, item->get_text()) )
193                                 item->set_selected(1);
194                         if( fontlist->values[i]->image ) {
195                                 VFrame *vframe = fontlist->get(i)->image;
196                                 BC_Pixmap *icon = new BC_Pixmap(this, vframe, PIXMAP_ALPHA);
197                                 item->set_icon(icon);
198                                 item->set_icon_vframe(vframe);
199                         }
200                 }
201         }
202
203 // Sort font list
204         int done = 0;
205         while(!done) {
206                 done = 1;
207                 for( int i=0; i<fonts.size()-1; ++i ) {
208                         if( strcmp(fonts.values[i]->get_text(),
209                                 fonts.values[i + 1]->get_text()) > 0 ) {
210                                 BC_ListBoxItem *temp = fonts.values[i + 1];
211                                 fonts.values[i + 1] = fonts.values[i];
212                                 fonts.values[i] = temp;
213                                 done = 0;
214                         }
215                 }
216         }
217
218         add_tool(font_title = new BC_Title(x, y, _("Font:")));
219         font = new TitleFont(client, this, x, y + font_title->get_h());
220         font->create_objects();
221         x += font->get_w();
222         add_subwindow(font_tumbler = new TitleFontTumble(client, this, x, y+margin));
223         x += font_tumbler->get_w() + margin;
224
225         int x1 = x, y1 = y;
226         add_tool(size_title = new BC_Title(x1, y1+margin, _("Size:")));
227         sprintf(string, "%.2f", client->config.size);
228         x1 += size_title->get_w() + margin;
229         size = new TitleSize(client, this, x1, y1+margin, string);
230         size->create_objects();
231         int x2 = x1 + size->get_w(), y2 = y1 + size->get_h() + margin;
232         add_subwindow(size_tumbler = new TitleSizeTumble(client, this, x2, y1+margin));
233
234         add_tool(pitch_title = new BC_Title(x-5, y2+margin, _("Pitch:")));
235         pitch = new TitlePitch(client, this, x1, y2+margin, &client->config.line_pitch);
236         pitch->create_objects();
237
238         int x3 = x2 + size_tumbler->get_w() + 50;
239         int y3 = pitch->get_y() + pitch->get_h();
240
241         add_tool(style_title = new BC_Title(x=x3, y, _("Style:")));
242         add_tool(italic = new TitleItalic(client, this, x, y + 20));
243         int w1 = italic->get_w();
244         add_tool(bold = new TitleBold(client, this, x, y + 50));
245         if( bold->get_w() > w1 ) w1 = bold->get_w();
246         add_tool(drag = new TitleDrag(client, this, x, y + 80));
247         if( drag->get_w() > w1 ) w1 = drag->get_w();
248         if( client->config.drag )
249                 grab(client->server->mwindow->cwindow->gui);
250
251         x += w1 + margin;
252         add_tool(justify_title = new BC_Title(x, y, _("Justify:")));
253         add_tool(left = new TitleLeft(client, this, x, y + 20));
254         w1 = left->get_w();
255         add_tool(center = new TitleCenter(client, this, x, y + 50));
256         if( center->get_w() > w1 ) w1 = center->get_w();
257         add_tool(right = new TitleRight(client, this, x, y + 80));
258         if( right->get_w() > w1 ) w1 = right->get_w();
259
260         x += w1 + margin;
261         add_tool(top = new TitleTop(client, this, x, y + 20));
262         add_tool(mid = new TitleMid(client, this, x, y + 50));
263         add_tool(bottom= new TitleBottom(client, this, x, y + 80));
264
265         x = margin;
266         y = y3+10;
267
268         w1 = BC_Title::calculate_w(this, _("X:"));
269         if( (x1 = BC_Title::calculate_w(this, _("Y:"))) > w1 ) w1 = x1;
270         if( (x1 = BC_Title::calculate_w(this, _("W:"))) > w1 ) w1 = x1;
271         if( (x1 = BC_Title::calculate_w(this, _("H:"))) > w1 ) w1 = x1;
272         add_tool(x_title = new BC_Title(x1=x, y, _("X:")));
273         x1 += w1;
274         title_x = new TitleX(client, this, x1, y);
275         title_x->create_objects();
276         x1 += title_x->get_w()+margin;
277         add_tool(y_title = new BC_Title(x1, y, _("Y:")));
278         x1 += w1;
279         title_y = new TitleY(client, this, x1, y);
280         title_y->create_objects();
281         x1 += title_y->get_w();
282         y1 = y + title_y->get_h();
283
284         add_tool(w_title = new BC_Title(x1=x, y1, _("W:")));
285         x1 += w1;
286         title_w = new TitleW(client, this, x1, y1);
287         title_w->create_objects();
288         x1 += title_w->get_w()+margin;
289         add_tool(h_title = new BC_Title(x1, y1, _("H:")));
290         x1 += w1;
291         title_h = new TitleH(client, this, x1, y1);
292         title_h->create_objects();
293         x1 += title_h->get_w();
294
295         x = x1+2*margin;
296         add_tool(motion_title = new BC_Title(x1=x, y, _("Motion:")));
297         x1 += motion_title->get_w()+margin;
298         motion = new TitleMotion(client, this, x1, y);
299         motion->create_objects();
300         add_tool(loop = new TitleLoop(client, x, y1));
301         x = margin;    y = y1 + loop->get_h()+20;
302
303         add_tool(dropshadow_title = new BC_Title(x, y, _("Drop shadow:")));
304         w1 = dropshadow_title->get_w();
305         dropshadow = new TitleDropShadow(client, this, x, y + 20);
306         dropshadow->create_objects();
307         if( dropshadow->get_w() > w1 ) w1 = dropshadow->get_w();
308         x += w1 + margin;
309
310         add_tool(fadein_title = new BC_Title(x, y, _("Fade in (sec):")));
311         w1 = fadein_title->get_w();
312         add_tool(fade_in = new TitleFade(client, this, &client->config.fade_in, x, y + 20));
313         if( fade_in->get_w() > w1 ) w1 = fade_in->get_w();
314         x += w1 + margin;
315
316         add_tool(fadeout_title = new BC_Title(x, y, _("Fade out (sec):")));
317         w1 = fadeout_title->get_w();
318         add_tool(fade_out = new TitleFade(client, this, &client->config.fade_out, x, y + 20));
319         if( fade_out->get_w() > w1 ) w1 = fade_out->get_w();
320         x += w1 + margin;
321
322         add_tool(speed_title = new BC_Title(x, y1=y, _("Speed:")));
323         w1 = speed_title->get_w();
324         y += speed_title->get_h() + 5;
325         speed = new TitleSpeed(client, this, x, y);
326         speed->create_objects();
327         if( speed->get_w() > w1 ) w1 = speed->get_w();
328         x += w1 + margin;
329         y2 = y + speed->get_h() + 10;
330
331         color_x = x3;  color_y = y = y1;
332         color_thread = new TitleColorThread(client, this, 0);
333         x1 = color_x + COLOR_W + 2*margin;
334         y1 = color_y + 5;
335         add_tool(color_button = new TitleColorButton(client, this, x1, y1));
336         y += COLOR_H + 5;
337         outline_color_x = x3;  outline_color_y = y;
338         outline_color_thread = new TitleColorThread(client, this, 1);
339         y1 = outline_color_y + 5;
340         add_tool(outline_color_button = new TitleOutlineColorButton(client, this, x1, y1));
341
342         x = 10;  y = y2;
343         add_tool(outline_title = new BC_Title(x, y, _("Outline:")));
344         y1 =  y + outline_title->get_h() + margin;
345         outline = new TitleOutline(client, this, x, y1);
346         outline->create_objects();
347         x += outline->get_w() + 2*margin;
348 #ifdef USE_STOKER
349 // to different to be used
350         add_tool(stroker_title = new BC_Title(x, y, _("Stroker:")));
351         stroker = new TitleStroker(client, this, x, y1);
352         stroker->create_objects();
353         x += stroker->get_w() + margin;
354 #endif
355         y += outline_title->get_h() + margin;
356         add_tool(timecode = new TitleTimecode(client, x1=x, y));
357         x += timecode->get_w() + margin;
358         add_tool(timecode_format = new TitleTimecodeFormat(client, x, y,
359                 Units::print_time_format(client->config.timecode_format, string)));
360         timecode_format->create_objects();
361         y += timecode_format->get_h() + margin;
362
363         x = 10;
364         add_tool(background = new TitleBackground(client, this, x, y));
365         x += background->get_w() + margin;
366         add_tool(background_path = new TitleBackgroundPath(client, this, x, y));
367         x += background_path->get_w() + margin;
368         add_tool(background_browse = new BrowseButton(
369                 client->server->mwindow->theme, this, background_path,
370                 x, y, 0, _("background media"), _("Select background media path")));
371         x += background_browse->get_w() + 3*margin;
372         add_tool(loop_playback = new TitleLoopPlayback(client, x, y));
373         y += loop_playback->get_h() + 10;
374
375         x = 10;
376         add_tool(text_title = new BC_Title(x, y, _("Text:")));
377         y += text_title->get_h() + margin;
378         x = margin;
379         text = new TitleText(client, this, x, y, get_w()-margin - x, get_h() - y - 10);
380         text->create_objects();
381
382         add_tool(cur_popup = new TitleCurPopup(client, this));
383         cur_popup->create_objects();
384         add_tool(fonts_popup = new TitleFontsPopup(client, this));
385
386         show_window(1);
387         update();
388 }
389
390 int TitleWindow::resize_event(int w, int h)
391 {
392         client->config.window_w = w;
393         client->config.window_h = h;
394
395         clear_box(0, 0, w, h);
396         font_title->reposition_window(font_title->get_x(), font_title->get_y());
397         font->reposition_window(font->get_x(), font->get_y());
398         font_tumbler->reposition_window(font_tumbler->get_x(), font_tumbler->get_y());
399         x_title->reposition_window(x_title->get_x(), x_title->get_y());
400         title_x->reposition_window(title_x->get_x(), title_x->get_y());
401         y_title->reposition_window(y_title->get_x(), y_title->get_y());
402         title_y->reposition_window(title_y->get_x(), title_y->get_y());
403         w_title->reposition_window(w_title->get_x(), w_title->get_y());
404         title_w->reposition_window(title_w->get_x(), title_w->get_y());
405         h_title->reposition_window(h_title->get_x(), h_title->get_y());
406         title_h->reposition_window(title_h->get_x(), title_h->get_y());
407         style_title->reposition_window(style_title->get_x(), style_title->get_y());
408         italic->reposition_window(italic->get_x(), italic->get_y());
409         bold->reposition_window(bold->get_x(), bold->get_y());
410         drag->reposition_window(drag->get_x(), drag->get_y());
411         size_title->reposition_window(size_title->get_x(), size_title->get_y());
412         size->reposition_window(size->get_x(), size->get_y());
413         size_tumbler->reposition_window(size_tumbler->get_x(), size_tumbler->get_y());
414         pitch_title->reposition_window(pitch_title->get_x(), pitch_title->get_y());
415         pitch->reposition_window(pitch->get_x(), pitch->get_y());
416
417         color_button->reposition_window(color_button->get_x(), color_button->get_y());
418         outline_color_button->reposition_window(outline_color_button->get_x(), outline_color_button->get_y());
419         motion_title->reposition_window(motion_title->get_x(), motion_title->get_y());
420         motion->reposition_window(motion->get_x(), motion->get_y());
421         loop->reposition_window(loop->get_x(), loop->get_y());
422         dropshadow_title->reposition_window(dropshadow_title->get_x(), dropshadow_title->get_y());
423         dropshadow->reposition_window(dropshadow->get_x(), dropshadow->get_y());
424         fadein_title->reposition_window(fadein_title->get_x(), fadein_title->get_y());
425         fade_in->reposition_window(fade_in->get_x(), fade_in->get_y());
426         fadeout_title->reposition_window(fadeout_title->get_x(), fadeout_title->get_y());
427         fade_out->reposition_window(fade_out->get_x(), fade_out->get_y());
428         text_title->reposition_window(text_title->get_x(), text_title->get_y());
429         timecode->reposition_window(timecode->get_x(), timecode->get_y());
430         text->reposition_window(text->get_x(), text->get_y(), w - text->get_x() - 10,
431                 BC_TextBox::pixels_to_rows(this, MEDIUMFONT, h - text->get_y() - 10));
432         justify_title->reposition_window(justify_title->get_x(), justify_title->get_y());
433         left->reposition_window(left->get_x(), left->get_y());
434         center->reposition_window(center->get_x(), center->get_y());
435         right->reposition_window(right->get_x(), right->get_y());
436         top->reposition_window(top->get_x(), top->get_y());
437         mid->reposition_window(mid->get_x(), mid->get_y());
438         bottom->reposition_window(bottom->get_x(), bottom->get_y());
439         speed_title->reposition_window(speed_title->get_x(), speed_title->get_y());
440         speed->reposition_window(speed->get_x(), speed->get_y());
441         update_color();
442         flash();
443
444         return 1;
445 }
446
447 int TitleWindow::grab_event(XEvent *event)
448 {
449         switch( event->type ) {
450         case ButtonPress:
451                 if( !dragging ) break;
452                 return 1;
453         case ButtonRelease:
454                 if( !dragging ) return 0;
455                 dragging = 0;
456                 return 1;
457         case MotionNotify:
458                 if( dragging ) break;
459         default:
460                 return 0;
461         }
462         MWindow *mwindow = client->server->mwindow;
463         CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
464         CWindowCanvas *canvas = cwindow_gui->canvas;
465         float cursor_x = canvas->get_canvas()->get_relative_cursor_x();
466         float cursor_y = canvas->get_canvas()->get_relative_cursor_y();
467         canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
468         int64_t position = client->get_source_position();
469         float projector_x, projector_y, projector_z;
470         Track *track = client->server->plugin->track;
471         int track_w = track->track_w, track_h = track->track_h;
472         track->automation->get_projector(
473                 &projector_x, &projector_y, &projector_z,
474                 position, PLAY_FORWARD);
475         projector_x += mwindow->edl->session->output_w / 2;
476         projector_y += mwindow->edl->session->output_h / 2;
477         cursor_x = (cursor_x - projector_x) / projector_z + track_w / 2;
478         cursor_y = (cursor_y - projector_y) / projector_z + track_h / 2;
479         int title_x = client->config.title_x, title_y = client->config.title_y;
480         int title_w = client->config.title_w, title_h = client->config.title_h;
481         if( !title_w ) title_w = track_w;
482         if( !title_h ) title_h = track_h;
483         int r = MIN(track_w, track_h)/100 + 2;
484         int x0 = title_x, x1 = title_x+(title_w+1)/2, x2 = title_x+title_w;
485         int y0 = title_y, y1 = title_y+(title_h+1)/2, y2 = title_y+title_h;
486         int drag_dx = 0, drag_dy = 0;
487         if( !dragging ) {  // clockwise
488                      if( abs(drag_dx = cursor_x-x0) < r &&       // x0,y0
489                          abs(drag_dy = cursor_y-y0) < r ) dragging = 1;
490                 else if( abs(drag_dx = cursor_x-x1) < r &&  // x1,y0
491                          abs(drag_dy = cursor_y-y0) < r ) dragging = 2;
492                 else if( abs(drag_dx = cursor_x-x2) < r &&  // x2,y0
493                          abs(drag_dy = cursor_y-y0) < r ) dragging = 3;
494                 else if( abs(drag_dx = cursor_x-x2) < r &&  // x2,y1
495                          abs(drag_dy = cursor_y-y1) < r ) dragging = 4;
496                 else if( abs(drag_dx = cursor_x-x2) < r &&  // x2,y2
497                          abs(drag_dy = cursor_y-y2) < r ) dragging = 5;
498                 else if( abs(drag_dx = cursor_x-x1) < r &&  // x1,y2
499                          abs(drag_dy = cursor_y-y2) < r ) dragging = 6;
500                 else if( abs(drag_dx = cursor_x-x0) < r &&  // x0,y2
501                          abs(drag_dy = cursor_y-y2) < r ) dragging = 7;
502                 else if( abs(drag_dx = cursor_x-x0) < r &&  // x0,y1
503                          abs(drag_dy = cursor_y-y1) < r ) dragging = 8;
504                 else if( abs(drag_dx = cursor_x-x1) < r &&  // x1,y1
505                          abs(drag_dy = cursor_y-y1) < r ) dragging = 9;
506                         return 0;
507         }
508         switch( dragging ) {
509         case 1: { // x0,y0
510                 int cur_x = cursor_x - drag_dx, dx = cur_x - x0;
511                 int cur_y = cursor_y - drag_dy, dy = cur_y - y0;
512                 if( !dx && !dy ) return 1;
513                 int cur_w = title_w - dx;  if( cur_w < 1 ) cur_w = 1;
514                 int cur_h = title_h - dy;  if( cur_h < 1 ) cur_h = 1;
515                 this->title_x->update((int64_t)(client->config.title_x = cur_x));
516                 this->title_y->update((int64_t)(client->config.title_y = cur_y));
517                 this->title_w->update((int64_t)(client->config.title_w = cur_w));
518                 this->title_h->update((int64_t)(client->config.title_h = cur_h));
519                 break; }
520         case 2: { // x1,y0
521                 int cur_y = cursor_y - drag_dy, dy = cur_y - y0;
522                 if( !dy ) return 1;
523                 int cur_h = title_h - dy;  if( cur_h < 1 ) cur_h = 1;
524                 this->title_y->update((int64_t)(client->config.title_y = cur_y));
525                 this->title_h->update((int64_t)(client->config.title_h = cur_h));
526                 break; }
527         case 3: { // x2,y0
528                 int cur_x = cursor_x - drag_dx, dx = cur_x - x2;
529                 int cur_y = cursor_y - drag_dy, dy = cur_y - y0;
530                 int cur_w = title_w + dx;  if( cur_w < 1 ) cur_w = 1;
531                 int cur_h = title_h - dy;  if( cur_h < 1 ) cur_h = 1;
532                 this->title_w->update((int64_t)(client->config.title_w = cur_w));
533                 this->title_y->update((int64_t)(client->config.title_y = cur_y));
534                 this->title_h->update((int64_t)(client->config.title_h = cur_h));
535                 break; }
536         case 4: { // x2,y1
537                 int cur_x = cursor_x - drag_dx, dx = cur_x - x2;
538                 if( !dx ) return 1;
539                 int cur_w = title_w + dx;  if( cur_w < 1 ) cur_w = 1;
540                 this->title_w->update((int64_t)(client->config.title_w = cur_w));
541                 break; }
542         case 5: { // x2,y2
543                 int cur_x = cursor_x - drag_dx, dx = cur_x - x2;
544                 int cur_y = cursor_y - drag_dy, dy = cur_y - y2;
545                 int cur_w = title_w + dx;  if( cur_w < 1 ) cur_w = 1;
546                 int cur_h = title_h + dy;  if( cur_h < 1 ) cur_h = 1;
547                 this->title_w->update((int64_t)(client->config.title_w = cur_w));
548                 this->title_h->update((int64_t)(client->config.title_h = cur_h));
549                 break; }
550         case 6: { // x1,y2
551                 int cur_y = cursor_y - drag_dy, dy = cur_y - y2;
552                 if( client->config.title_h == cur_y ) return 1;
553                 int cur_h = title_h + dy;  if( cur_h < 1 ) cur_h = 1;
554                 this->title_h->update((int64_t)(client->config.title_h = cur_h));
555                 break; }
556         case 7: { // x0,y2
557                 int cur_x = cursor_x - drag_dx, dx = cur_x - x0;
558                 int cur_y = cursor_y - drag_dy, dy = cur_y - y2;
559                 int cur_w = title_w - dx;  if( cur_w < 1 ) cur_w = 1;
560                 int cur_h = title_h + dy;  if( cur_h < 1 ) cur_h = 1;
561                 this->title_x->update((int64_t)(client->config.title_x = cur_x));
562                 this->title_w->update((int64_t)(client->config.title_w = cur_w));
563                 this->title_h->update((int64_t)(client->config.title_h = cur_h));
564                 break; }
565         case 8: { // x0,y1
566                 int cur_x = cursor_x - drag_dx, dx = cur_x - x0;
567                 if( !dx ) return 1;
568                 int cur_w = title_w - dx;  if( cur_w < 1 ) cur_w = 1;
569                 this->title_x->update((int64_t)(client->config.title_x = cur_x));
570                 this->title_w->update((int64_t)(client->config.title_w = cur_w));
571                 break; }
572         case 9: { // x1,y1
573                 int cur_x = cursor_x - drag_dx, dx = cur_x - x1;
574                 int cur_y = cursor_y - drag_dy, dy = cur_y - y1;
575                 if( title_x == cur_x && title_y == cur_y ) return 1;
576                 this->title_x->update((int64_t)(client->config.title_x += dx));
577                 this->title_y->update((int64_t)(client->config.title_y += dy));
578                 }
579         }
580         client->send_configure_change();
581         return 1;
582 }
583
584 void TitleWindow::previous_font()
585 {
586         int current_font = font->get_number();
587         current_font--;
588         if( current_font < 0 ) current_font = fonts.total - 1;
589
590         if( current_font < 0 || current_font >= fonts.total ) return;
591
592         for( int i=0; i<fonts.total; ++i ) {
593                 fonts.values[i]->set_selected(i == current_font);
594         }
595
596         font->update(fonts.values[current_font]->get_text());
597         strcpy(client->config.font, fonts.values[current_font]->get_text());
598         client->send_configure_change();
599 }
600
601 void  TitleWindow::next_font()
602 {
603         int current_font = font->get_number();
604         current_font++;
605         if( current_font >= fonts.total ) current_font = 0;
606
607         if( current_font < 0 || current_font >= fonts.total ) return;
608
609         for( int i=0; i<fonts.total; ++i ) {
610                 fonts.values[i]->set_selected(i == current_font);
611         }
612
613         font->update(fonts.values[current_font]->get_text());
614         strcpy(client->config.font, fonts.values[current_font]->get_text());
615         client->send_configure_change();
616 }
617
618 int TitleWindow::insert_ibeam(const char *txt, int adv)
619 {
620         int ibeam = cur_ibeam;
621         client->insert_text(txt, ibeam);
622         if( (ibeam += adv) >= client->config.wlen)
623                 ibeam = client->config.wlen;
624         text->wset_selection(-1, -1, ibeam);
625         text->update(client->config.wtext);
626         client->send_configure_change();
627         return 1;
628 }
629
630 void TitleWindow::update_color()
631 {
632 //printf("TitleWindow::update_color %x\n", client->config.color);
633         set_color(client->config.color);
634         draw_box(color_x, color_y, COLOR_W, COLOR_H);
635         flash(color_x, color_y, COLOR_W, COLOR_H);
636         set_color(client->config.outline_color);
637         draw_box(outline_color_x, outline_color_y, COLOR_W, COLOR_H);
638         set_color(BLACK);
639         draw_rectangle(color_x, color_y, COLOR_W, COLOR_H);
640         draw_rectangle(outline_color_x, outline_color_y, COLOR_W, COLOR_H);
641         flash(outline_color_x, outline_color_y, COLOR_W, COLOR_H);
642 }
643
644 void TitleWindow::update_justification()
645 {
646         left->update(client->config.hjustification == JUSTIFY_LEFT);
647         center->update(client->config.hjustification == JUSTIFY_CENTER);
648         right->update(client->config.hjustification == JUSTIFY_RIGHT);
649         top->update(client->config.vjustification == JUSTIFY_TOP);
650         mid->update(client->config.vjustification == JUSTIFY_MID);
651         bottom->update(client->config.vjustification == JUSTIFY_BOTTOM);
652 }
653
654 void TitleWindow::update()
655 {
656         title_x->update((int64_t)client->config.title_x);
657         title_y->update((int64_t)client->config.title_y);
658         title_w->update((int64_t)client->config.title_w);
659         title_h->update((int64_t)client->config.title_h);
660         italic->update(client->config.style & BC_FONT_ITALIC);
661         bold->update(client->config.style & BC_FONT_BOLD);
662         size->update(client->config.size);
663         motion->update(TitleMain::motion_to_text(client->config.motion_strategy));
664         loop->update(client->config.loop);
665         dropshadow->update((int64_t)client->config.dropshadow);
666         fade_in->update((float)client->config.fade_in);
667         fade_out->update((float)client->config.fade_out);
668         font->update(client->config.font);
669         text->update(&client->config.wtext[0]);
670         speed->update(client->config.pixels_per_second);
671         outline->update((int64_t)client->config.outline_size);
672 #ifdef USE_STOKER
673         stroker->update((int64_t)client->config.stroke_width);
674 #endif
675         timecode->update(client->config.timecode);
676         timecode_format->update(client->config.timecode_format);
677         background->update(client->config.background);
678         background_path->update(client->config.background_path);
679         loop_playback->update((int64_t)client->config.loop_playback);
680
681         char string[BCTEXTLEN];
682         for( int i=0; i<lengthof(timeunit_formats); ++i ) {
683                 if( timeunit_formats[i] == client->config.timecode_format ) {
684                         timecode_format->set_text(
685                                 Units::print_time_format(timeunit_formats[i], string));
686                         break;
687                 }
688         }
689         update_justification();
690         update_color();
691 }
692
693
694 TitleFontTumble::TitleFontTumble(TitleMain *client, TitleWindow *window, int x, int y)
695  : BC_Tumbler(x, y)
696 {
697         this->client = client;
698         this->window = window;
699 }
700 int TitleFontTumble::handle_up_event()
701 {
702         window->previous_font();
703         return 1;
704 }
705
706 int TitleFontTumble::handle_down_event()
707 {
708         window->next_font();
709         return 1;
710 }
711
712
713
714 TitleSizeTumble::TitleSizeTumble(TitleMain *client, TitleWindow *window, int x, int y)
715  : BC_Tumbler(x, y)
716 {
717         this->client = client;
718         this->window = window;
719 }
720
721 int TitleSizeTumble::handle_up_event()
722 {
723         int current_index = -1;
724         int current_difference = -1;
725         for( int i=0; i<window->sizes.size(); ++i ) {
726                 int size = atoi(window->sizes.get(i)->get_text());
727                 if( current_index < 0 ||
728                         abs(size - client->config.size) < current_difference ) {
729                         current_index = i;
730                         current_difference = abs(size - client->config.size);
731                 }
732         }
733
734         current_index++;
735         if( current_index >= window->sizes.size() ) current_index = 0;
736
737
738         client->config.size = atoi(window->sizes.get(current_index)->get_text());
739         window->size->update(client->config.size);
740         client->send_configure_change();
741         return 1;
742 }
743
744 int TitleSizeTumble::handle_down_event()
745 {
746         int current_index = -1;
747         int current_difference = -1;
748         for( int i=0; i<window->sizes.size(); ++i ) {
749                 int size = atoi(window->sizes.get(i)->get_text());
750                 if( current_index < 0 ||
751                         abs(size - client->config.size) < current_difference ) {
752                         current_index = i;
753                         current_difference = abs(size - client->config.size);
754                 }
755         }
756
757         current_index--;
758         if( current_index < 0 ) current_index = window->sizes.size() - 1;
759
760
761         client->config.size = atoi(window->sizes.get(current_index)->get_text());
762         window->size->update(client->config.size);
763         client->send_configure_change();
764         return 1;
765 }
766
767 TitleBold::TitleBold(TitleMain *client, TitleWindow *window, int x, int y)
768  : BC_CheckBox(x, y, client->config.style & BC_FONT_BOLD, _("Bold"))
769 {
770         this->client = client;
771         this->window = window;
772 }
773
774 int TitleBold::handle_event()
775 {
776         client->config.style =
777                 (client->config.style & ~BC_FONT_BOLD) |
778                         (get_value() ? BC_FONT_BOLD : 0);
779         client->send_configure_change();
780         return 1;
781 }
782
783 TitleItalic::TitleItalic(TitleMain *client, TitleWindow *window, int x, int y)
784  : BC_CheckBox(x, y, client->config.style & BC_FONT_ITALIC, _("Italic"))
785 {
786         this->client = client;
787         this->window = window;
788 }
789 int TitleItalic::handle_event()
790 {
791         client->config.style =
792                 (client->config.style & ~BC_FONT_ITALIC) |
793                         (get_value() ? BC_FONT_ITALIC : 0);
794         client->send_configure_change();
795         return 1;
796 }
797
798
799
800 TitleSize::TitleSize(TitleMain *client, TitleWindow *window, int x, int y, char *text)
801  : BC_PopupTextBox(window,
802                 &window->sizes,
803                 text,
804                 x,
805                 y,
806                 64,
807                 300)
808 {
809         this->client = client;
810         this->window = window;
811 }
812 TitleSize::~TitleSize()
813 {
814 }
815 int TitleSize::handle_event()
816 {
817         client->config.size = atol(get_text());
818 //printf("TitleSize::handle_event 1 %s\n", get_text());
819         client->send_configure_change();
820         return 1;
821 }
822 void TitleSize::update(int size)
823 {
824         char string[BCTEXTLEN];
825         sprintf(string, "%d", size);
826         BC_PopupTextBox::update(string);
827 }
828
829 TitlePitch::
830 TitlePitch(TitleMain *client, TitleWindow *window, int x, int y, int *value)
831  : BC_TumbleTextBox(window, *value, 0, INT_MAX, x, y, 64)
832 {
833         this->client = client;
834         this->window = window;
835         this->value = value;
836 }
837
838 TitlePitch::
839 ~TitlePitch()
840 {
841 }
842
843 int TitlePitch::handle_event()
844 {
845         *value = atol(get_text());
846         client->send_configure_change();
847         return 1;
848 }
849
850 TitleColorButton::TitleColorButton(TitleMain *client, TitleWindow *window, int x, int y)
851  : BC_GenericButton(x, y, _("Color..."))
852 {
853         this->client = client;
854         this->window = window;
855 }
856 int TitleColorButton::handle_event()
857 {
858         window->color_thread->start_window(client->config.color,
859                 client->config.alpha);
860         return 1;
861 }
862 TitleOutlineColorButton::TitleOutlineColorButton(TitleMain *client, TitleWindow *window, int x, int y)
863  : BC_GenericButton(x, y, _("Outline color..."))
864 {
865         this->client = client;
866         this->window = window;
867 }
868 int TitleOutlineColorButton::handle_event()
869 {
870         window->outline_color_thread->start_window(client->config.outline_color,
871                 client->config.outline_alpha);
872         return 1;
873 }
874
875
876 TitleMotion::TitleMotion(TitleMain *client, TitleWindow *window, int x, int y)
877  : BC_PopupTextBox(window, &window->paths,
878                 client->motion_to_text(client->config.motion_strategy),
879                 x, y, 120, 100)
880 {
881         this->client = client;
882         this->window = window;
883 }
884 int TitleMotion::handle_event()
885 {
886         client->config.motion_strategy = client->text_to_motion(get_text());
887         client->send_configure_change();
888         return 1;
889 }
890
891 TitleLoop::TitleLoop(TitleMain *client, int x, int y)
892  : BC_CheckBox(x, y, client->config.loop, _("Loop"))
893 {
894         this->client = client;
895 }
896 int TitleLoop::handle_event()
897 {
898         client->config.loop = get_value();
899         client->send_configure_change();
900         return 1;
901 }
902 TitleTimecode::TitleTimecode(TitleMain *client, int x, int y)
903  : BC_CheckBox(x, y, client->config.timecode, _("Stamp timecode"))
904 {
905         this->client = client;
906 }
907 int TitleTimecode::handle_event()
908 {
909         client->config.timecode = get_value();
910         client->send_configure_change();
911         return 1;
912 }
913
914 TitleTimecodeFormat::TitleTimecodeFormat(TitleMain *client, int x, int y, const char *text)
915  : BC_PopupMenu(x, y, 100, text, 1)
916 {
917         this->client = client;
918 }
919
920 int TitleTimecodeFormat::handle_event()
921 {
922         client->config.timecode_format = Units::text_to_format(get_text());
923         client->send_configure_change();
924         return 1;
925 }
926
927 void TitleTimecodeFormat::create_objects()
928 {
929         char string[BCTEXTLEN];
930         for( int i=0; i<lengthof(timeunit_formats); ++i ) {
931                 add_item(new BC_MenuItem(
932                         Units::print_time_format(timeunit_formats[i], string)));
933         }
934 }
935
936
937 int TitleTimecodeFormat::update(int timecode_format)
938 {
939         char string[BCTEXTLEN];
940         for( int i=0; i<lengthof(timeunit_formats); ++i ) {
941                 if( timeunit_formats[i] == timecode_format ) {
942                         set_text(Units::print_time_format(timeunit_formats[i], string));
943                         break;
944                 }
945         }
946         return 0;
947 }
948
949 TitleFade::TitleFade(TitleMain *client, TitleWindow *window,
950         double *value, int x, int y)
951  : BC_TextBox(x, y, 80, 1, (float)*value)
952 {
953         this->client = client;
954         this->window = window;
955         this->value = value;
956         set_precision(2);
957 }
958
959 int TitleFade::handle_event()
960 {
961         *value = atof(get_text());
962         client->send_configure_change();
963         return 1;
964 }
965
966 TitleFont::TitleFont(TitleMain *client, TitleWindow *window, int x, int y)
967  : BC_PopupTextBox(window, &window->fonts, client->config.font,
968                 x, y, 200, 500, LISTBOX_ICON_LIST)
969 {
970         this->client = client;
971         this->window = window;
972 }
973 int TitleFont::handle_event()
974 {
975         strcpy(client->config.font, get_text());
976         client->send_configure_change();
977         return 1;
978 }
979
980 TitleText::TitleText(TitleMain *client, TitleWindow *window,
981         int x, int y, int w, int h)
982  : BC_ScrollTextBox(window, x, y, w,
983                 BC_TextBox::pixels_to_rows(window, MEDIUMFONT, h),
984                 client->config.wtext, 8192)
985 {
986         this->client = client;
987         this->window = window;
988 //printf("TitleText::TitleText %s\n", client->config.text);
989 }
990
991 int TitleText::button_press_event()
992 {
993         if( get_buttonpress() == 3 ) {
994                 window->cur_ibeam = get_ibeam_letter();
995                 window->cur_popup->activate_menu();
996                 return 1;
997         }
998         return BC_ScrollTextBox::button_press_event();
999 }
1000
1001 int TitleText::handle_event()
1002 {
1003         window->fonts_popup->deactivate();
1004         int len =  sizeof(client->config.wtext) / sizeof(wchar_t);
1005         wcsncpy(client->config.wtext, get_wtext(), len);
1006         client->config.wtext[len-1] = 0;
1007         client->config.wlen = wcslen(client->config.wtext);
1008         client->send_configure_change();
1009         return 1;
1010 }
1011
1012
1013 TitleDropShadow::TitleDropShadow(TitleMain *client, TitleWindow *window, int x, int y)
1014  : BC_TumbleTextBox(window, (int64_t)client->config.dropshadow,
1015         (int64_t)-1000, (int64_t)1000, x, y, 70)
1016 {
1017         this->client = client;
1018         this->window = window;
1019 }
1020 int TitleDropShadow::handle_event()
1021 {
1022         client->config.dropshadow = atol(get_text());
1023         client->send_configure_change();
1024         return 1;
1025 }
1026
1027
1028 TitleOutline::TitleOutline(TitleMain *client, TitleWindow *window, int x, int y)
1029  : BC_TumbleTextBox(window, (int64_t)client->config.outline_size,
1030         (int64_t)0, (int64_t)1000, x, y, 70)
1031 {
1032         this->client = client;
1033         this->window = window;
1034 }
1035 int TitleOutline::handle_event()
1036 {
1037         client->config.outline_size = atol(get_text());
1038         client->send_configure_change();
1039         return 1;
1040 }
1041
1042 TitleStroker::TitleStroker(TitleMain *client, TitleWindow *window, int x, int y)
1043  : BC_TumbleTextBox(window, (int64_t)client->config.stroke_width,
1044         (int64_t)0, (int64_t)1000, x, y, 70)
1045 {
1046         this->client = client;
1047         this->window = window;
1048 }
1049 int TitleStroker::handle_event()
1050 {
1051         client->config.stroke_width = atol(get_text());
1052         if( client->config.stroke_width > 1 )
1053                 client->config.style |= BC_FONT_OUTLINE;
1054         else
1055                 client->config.style &= ~BC_FONT_OUTLINE;
1056         client->send_configure_change();
1057         return 1;
1058 }
1059
1060
1061 TitleX::TitleX(TitleMain *client, TitleWindow *window, int x, int y)
1062  : BC_TumbleTextBox(window, (int64_t)client->config.title_x,
1063         (int64_t)-32767, (int64_t)32767, x, y, 50)
1064 {
1065         this->client = client;
1066         this->window = window;
1067 }
1068 int TitleX::handle_event()
1069 {
1070         client->config.title_x = atol(get_text());
1071         client->send_configure_change();
1072         return 1;
1073 }
1074
1075 TitleY::TitleY(TitleMain *client, TitleWindow *window, int x, int y)
1076  : BC_TumbleTextBox(window, (int64_t)client->config.title_y,
1077         (int64_t)-32767, (int64_t)32767, x, y, 50)
1078 {
1079         this->client = client;
1080         this->window = window;
1081 }
1082 int TitleY::handle_event()
1083 {
1084         client->config.title_y = atol(get_text());
1085         client->send_configure_change();
1086         return 1;
1087 }
1088
1089 TitleW::TitleW(TitleMain *client, TitleWindow *window, int x, int y)
1090  : BC_TumbleTextBox(window, (int64_t)client->config.title_w,
1091         (int64_t)0, (int64_t)32767, x, y, 50)
1092 {
1093         this->client = client;
1094         this->window = window;
1095 }
1096 int TitleW::handle_event()
1097 {
1098         client->config.title_w = atol(get_text());
1099         client->send_configure_change();
1100         return 1;
1101 }
1102
1103 TitleH::TitleH(TitleMain *client, TitleWindow *window, int x, int y)
1104  : BC_TumbleTextBox(window, (int64_t)client->config.title_h,
1105         (int64_t)0, (int64_t)32767, x, y, 50)
1106 {
1107         this->client = client;
1108         this->window = window;
1109 }
1110 int TitleH::handle_event()
1111 {
1112         client->config.title_h = atol(get_text());
1113         client->send_configure_change();
1114         return 1;
1115 }
1116
1117 TitleSpeed::TitleSpeed(TitleMain *client, TitleWindow *window, int x, int y)
1118  : BC_TumbleTextBox(window, (float)client->config.pixels_per_second,
1119         (float)0, (float)1000, x, y, 100)
1120 {
1121         this->client = client;
1122         set_precision(2);
1123         set_increment(10);
1124 }
1125
1126
1127 int TitleSpeed::handle_event()
1128 {
1129         client->config.pixels_per_second = atof(get_text());
1130         client->send_configure_change();
1131         return 1;
1132 }
1133
1134
1135 TitleLeft::TitleLeft(TitleMain *client, TitleWindow *window, int x, int y)
1136  : BC_Radial(x, y, client->config.hjustification == JUSTIFY_LEFT, _("Left"))
1137 {
1138         this->client = client;
1139         this->window = window;
1140 }
1141 int TitleLeft::handle_event()
1142 {
1143         client->config.hjustification = JUSTIFY_LEFT;
1144         window->update_justification();
1145         client->send_configure_change();
1146         return 1;
1147 }
1148
1149 TitleCenter::TitleCenter(TitleMain *client, TitleWindow *window, int x, int y)
1150  : BC_Radial(x, y, client->config.hjustification == JUSTIFY_CENTER, _("Center"))
1151 {
1152         this->client = client;
1153         this->window = window;
1154 }
1155 int TitleCenter::handle_event()
1156 {
1157         client->config.hjustification = JUSTIFY_CENTER;
1158         window->update_justification();
1159         client->send_configure_change();
1160         return 1;
1161 }
1162
1163 TitleRight::TitleRight(TitleMain *client, TitleWindow *window, int x, int y)
1164  : BC_Radial(x, y, client->config.hjustification == JUSTIFY_RIGHT, _("Right"))
1165 {
1166         this->client = client;
1167         this->window = window;
1168 }
1169 int TitleRight::handle_event()
1170 {
1171         client->config.hjustification = JUSTIFY_RIGHT;
1172         window->update_justification();
1173         client->send_configure_change();
1174         return 1;
1175 }
1176
1177
1178
1179 TitleTop::TitleTop(TitleMain *client, TitleWindow *window, int x, int y)
1180  : BC_Radial(x, y, client->config.vjustification == JUSTIFY_TOP, _("Top"))
1181 {
1182         this->client = client;
1183         this->window = window;
1184 }
1185 int TitleTop::handle_event()
1186 {
1187         client->config.vjustification = JUSTIFY_TOP;
1188         window->update_justification();
1189         client->send_configure_change();
1190         return 1;
1191 }
1192
1193 TitleMid::TitleMid(TitleMain *client, TitleWindow *window, int x, int y)
1194  : BC_Radial(x, y, client->config.vjustification == JUSTIFY_MID, _("Mid"))
1195 {
1196         this->client = client;
1197         this->window = window;
1198 }
1199 int TitleMid::handle_event()
1200 {
1201         client->config.vjustification = JUSTIFY_MID;
1202         window->update_justification();
1203         client->send_configure_change();
1204         return 1;
1205 }
1206
1207 TitleBottom::TitleBottom(TitleMain *client, TitleWindow *window, int x, int y)
1208  : BC_Radial(x, y, client->config.vjustification == JUSTIFY_BOTTOM, _("Bottom"))
1209 {
1210         this->client = client;
1211         this->window = window;
1212 }
1213 int TitleBottom::handle_event()
1214 {
1215         client->config.vjustification = JUSTIFY_BOTTOM;
1216         window->update_justification();
1217         client->send_configure_change();
1218         return 1;
1219 }
1220
1221
1222
1223 TitleColorThread::TitleColorThread(TitleMain *client, TitleWindow *window, int is_outline)
1224  : ColorThread(1)
1225 {
1226         this->client = client;
1227         this->window = window;
1228         this->is_outline = is_outline;
1229 }
1230
1231 int TitleColorThread::handle_new_color(int output, int alpha)
1232 {
1233         if( is_outline ) {
1234                 client->config.outline_color = output;
1235                 client->config.outline_alpha = alpha;
1236         }
1237         else {
1238                 client->config.color = output;
1239                 client->config.alpha = alpha;
1240         }
1241
1242         window->lock_window("TitleColorThread::handle_new_color");
1243         window->update_color();
1244         window->flush();
1245         window->unlock_window();
1246
1247         client->send_configure_change();
1248         return 1;
1249 }
1250
1251 TitleDrag::TitleDrag(TitleMain *client, TitleWindow *window, int x, int y)
1252  : BC_CheckBox(x, y, client->config.drag, _("Drag"))
1253 {
1254         this->client = client;
1255         this->window = window;
1256 }
1257
1258 int TitleDrag::handle_event()
1259 {
1260         int value = get_value();
1261         client->config.drag = value;
1262         if( value )
1263                 window->grab(client->server->mwindow->cwindow->gui);
1264         else
1265                 window->ungrab(client->server->mwindow->cwindow->gui);
1266         client->send_configure_change();
1267         return 1;
1268 }
1269
1270 TitleBackground::TitleBackground(TitleMain *client, TitleWindow *window, int x, int y)
1271  : BC_CheckBox(x, y, client->config.background, _("Background:"))
1272 {
1273         this->client = client;
1274         this->window = window;
1275 }
1276
1277 int TitleBackground::handle_event()
1278 {
1279         client->config.background = get_value();
1280         client->send_configure_change();
1281         return 1;
1282 }
1283
1284 TitleBackgroundPath::TitleBackgroundPath(TitleMain *client, TitleWindow *window, int x, int y)
1285  : BC_TextBox(x, y, 240, 1, client->config.background_path)
1286 {
1287         this->client = client;
1288         this->window = window;
1289 }
1290
1291 int TitleBackgroundPath::handle_event()
1292 {
1293         strncpy(client->config.background_path, get_text(), sizeof(client->config.background_path));
1294         client->send_configure_change();
1295         return 1;
1296 }
1297
1298 TitleLoopPlayback::TitleLoopPlayback(TitleMain *client, int x, int y)
1299  : BC_CheckBox(x, y, client->config.loop_playback, _("Loop playback"))
1300 {
1301         this->client = client;
1302 }
1303 int TitleLoopPlayback::handle_event()
1304 {
1305         client->config.loop_playback = get_value();
1306         client->send_configure_change();
1307         return 1;
1308 }
1309
1310
1311 TitleCurPopup::TitleCurPopup(TitleMain *client, TitleWindow *window)
1312  : BC_PopupMenu(0, 0, 0, "", 0)
1313 {
1314         this->client = client;
1315         this->window = window;
1316 }
1317 int TitleCurPopup::handle_event()
1318 {
1319         return 1;
1320 }
1321 void TitleCurPopup::create_objects()
1322 {
1323         TitleCurItem *cur_item;
1324         TitleCurSubMenu *sub_menu;
1325         add_item(cur_item = new TitleCurItem(this, _("nudge")));
1326         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1327         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("nudge dx,dy")));
1328         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/nudge")));
1329         add_item(cur_item = new TitleCurItem(this, _("color")));
1330         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1331         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("color #")));
1332         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/color")));
1333         add_item(cur_item = new TitleCurItem(this, _("alpha")));
1334         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1335         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("alpha ")));
1336         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("alpha 0.")));
1337         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("alpha .5")));
1338         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("alpha 1.")));
1339         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/alpha")));
1340         add_item(cur_item = new TitleCurItem(this, _("font")));
1341         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1342         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("font name")));
1343         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/font")));
1344         add_item(cur_item = new TitleCurItem(this, _("size")));
1345         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1346         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("size +")));
1347         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("size -")));
1348         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("size ")));
1349         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/size")));
1350         add_item(cur_item = new TitleCurItem(this, _("bold")));
1351         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1352         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("bold 1")));
1353         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("bold 0")));
1354         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/bold")));
1355         add_item(cur_item = new TitleCurItem(this, _("italic")));
1356         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1357         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("italic 1")));
1358         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("italic 0")));
1359         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/italic")));
1360         add_item(cur_item = new TitleCurItem(this, _("caps")));
1361         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1362         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("caps 1")));
1363         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("caps 0")));
1364         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("caps -1")));
1365         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/caps")));
1366         add_item(cur_item = new TitleCurItem(this, _("ul")));
1367         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1368         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("ul 1")));
1369         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("ul 0")));
1370         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/ul")));
1371         add_item(cur_item = new TitleCurItem(this, _("blink")));
1372         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1373         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("blink 1")));
1374         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("blink -1")));
1375         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("blink ")));
1376         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("blink 0")));
1377         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/blink")));
1378         add_item(cur_item = new TitleCurItem(this, _("fixed")));
1379         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1380         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("fixed ")));
1381         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("fixed 20")));
1382         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("fixed 10")));
1383         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("fixed 0")));
1384         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/fixed")));
1385         add_item(cur_item = new TitleCurItem(this, _("sup")));
1386         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1387         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("sup 1")));
1388         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("sup 0")));
1389         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("sup -1")));
1390         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("/sup")));
1391         add_item(cur_item = new TitleCurItem(this, _("png")));
1392         cur_item->add_submenu(sub_menu = new TitleCurSubMenu(cur_item));
1393         sub_menu->add_submenuitem(new TitleCurSubMenuItem(sub_menu,_("png file")));
1394 }
1395
1396 TitleCurItem::TitleCurItem(TitleCurPopup *popup, const char *text)
1397  : BC_MenuItem(text)
1398 {
1399         this->popup = popup;
1400 }
1401 int TitleCurItem::handle_event()
1402 {
1403         return 1;
1404 }
1405
1406 TitleCurSubMenu::TitleCurSubMenu(TitleCurItem *cur_item)
1407 {
1408         this->cur_item = cur_item;
1409 }
1410 TitleCurSubMenu::~TitleCurSubMenu()
1411 {
1412 }
1413
1414 TitleCurSubMenuItem::TitleCurSubMenuItem(TitleCurSubMenu *submenu, const char *text)
1415  : BC_MenuItem(text)
1416 {
1417         this->submenu = submenu;
1418 }
1419 TitleCurSubMenuItem::~TitleCurSubMenuItem()
1420 {
1421 }
1422 int TitleCurSubMenuItem::handle_event()
1423 {
1424         TitleCurPopup *popup = submenu->cur_item->popup;
1425         TitleWindow *window = popup->window;
1426         const char *item_text = get_text();
1427         if( !strcmp(item_text, _("font")) ) {
1428                 int w = 300, h = 200;
1429                 int x = window->get_abs_cursor_x(0) - w + 10;
1430                 int y = window->get_abs_cursor_y(0) - 20;
1431                 if( x < 2 ) x = 2;
1432                 if( y < 2 ) y = 2;
1433                 window->fonts_popup->activate(x, y, w,h);
1434                 return 1;
1435         }
1436         char txt[BCSTRLEN];
1437         sprintf(txt, "<%s>", item_text);
1438         int adv = strlen(txt);
1439         if( adv > 1 && (txt[1] != '/' && strcmp(txt,_("font"))) ) --adv;
1440         return window->insert_ibeam(txt,adv);
1441 }
1442
1443 TitleFontsPopup::TitleFontsPopup(TitleMain *client, TitleWindow *window)
1444  : BC_ListBox(-1, -1, 1, 1, LISTBOX_ICON_LIST,
1445         &window->fonts, 0, 0, 1, 0, 1)
1446 {
1447         this->client = client;
1448         this->window = window;
1449         set_use_button(0);
1450 }
1451 TitleFontsPopup::~TitleFontsPopup()
1452 {
1453 }
1454
1455 int TitleFontsPopup::handle_event()
1456 {
1457         deactivate();
1458         BC_ListBoxItem *item = get_selection(0, 0);
1459         if( !item ) return 1;
1460         const char *item_text = item->get_text();
1461         char txt[BCTEXTLEN];
1462         sprintf(txt, "<font %s>",item_text);
1463         int adv = strlen(txt);
1464         int ret = window->insert_ibeam(txt, adv);
1465         return ret;
1466 }
1467