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