add missing GPL information in guicast program files
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / menueffects.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2010 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 "asset.h"
23 #include "awindow.h"
24 #include "awindowgui.h"
25 #include "clip.h"
26 #include "confirmsave.h"
27 #include "bchash.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "errorbox.h"
31 #include "file.h"
32 #include "formatcheck.h"
33 #include "indexfile.h"
34 #include "keyframe.h"
35 #include "keys.h"
36 #include "labels.h"
37 #include "language.h"
38 #include "loadmode.h"
39 #include "localsession.h"
40 #include "mainmenu.h"
41 #include "mainsession.h"
42 #include "mainundo.h"
43 #include "mwindow.h"
44 #include "mwindowgui.h"
45 #include "menueffects.h"
46 #include "playbackengine.h"
47 #include "pluginarray.h"
48 #include "pluginserver.h"
49 #include "preferences.h"
50 #include "render.h"
51 #include "sighandler.h"
52 #include "theme.h"
53 #include "tracks.h"
54
55
56
57 MenuEffects::MenuEffects(MWindow *mwindow)
58  : BC_MenuItem(_("Render effect..."))
59 {
60         this->mwindow = mwindow;
61 }
62
63 MenuEffects::~MenuEffects()
64 {
65 }
66
67
68 int MenuEffects::handle_event()
69 {
70         thread->set_title("");
71         thread->start();
72         return 1;
73 }
74
75
76
77
78
79 MenuEffectPacket::MenuEffectPacket(char *path, int64_t start, int64_t end)
80 {
81         this->start = start;
82         this->end = end;
83         strcpy(this->path, path);
84 }
85
86 MenuEffectPacket::~MenuEffectPacket()
87 {
88 }
89
90
91
92
93
94
95 MenuEffectThread::MenuEffectThread(MWindow *mwindow, MenuEffects *menu_item)
96 {
97         this->mwindow = mwindow;
98         this->menu_item = menu_item;
99         title[0] = 0;
100         dead_plugins = new ArrayList<PluginServer*>;
101 }
102
103 MenuEffectThread::~MenuEffectThread()
104 {
105         delete dead_plugins;
106 }
107
108
109
110
111
112 int MenuEffectThread::set_title(const char *title)
113 {
114         strcpy(this->title, title);
115         return 0;
116 }
117
118 // for recent effect menu items and running new effects
119 // prompts for an effect if title is blank
120 void MenuEffectThread::run()
121 {
122         for(int i = 0; i < dead_plugins->size(); i++)
123         {
124                 delete dead_plugins->get(i);
125         }
126         dead_plugins->remove_all();
127
128
129
130
131 // get stuff from main window
132         ArrayList<PluginServer*> *plugindb = mwindow->plugindb;
133         BC_Hash *defaults = mwindow->defaults;
134         ArrayList<BC_ListBoxItem*> plugin_list;
135         ArrayList<PluginServer*> local_plugindb;
136         char string[1024];
137         int result = 0;
138 // Default configuration
139         Asset *default_asset = new Asset;
140 // Output
141         ArrayList<Indexable*> assets;
142
143
144 // check for recordable tracks
145         if(!get_recordable_tracks(default_asset))
146         {
147                 sprintf(string, _("No recordable tracks specified."));
148                 ErrorBox error(_(PROGRAM_NAME ": Error"));
149                 error.create_objects(string);
150                 error.run_window();
151                 default_asset->Garbage::remove_user();
152                 return;
153         }
154
155 // check for plugins
156         if(!plugindb->total)
157         {
158                 sprintf(string, _("No plugins available."));
159                 ErrorBox error(_(PROGRAM_NAME ": Error"));
160                 error.create_objects(string);
161                 error.run_window();
162                 default_asset->Garbage::remove_user();
163                 return;
164         }
165
166
167 // get default attributes for output file
168 // used after completion
169         get_derived_attributes(default_asset, defaults);
170 //      to_tracks = defaults->get("RENDER_EFFECT_TO_TRACKS", 1);
171         load_mode = defaults->get("RENDER_EFFECT_LOADMODE", LOADMODE_PASTE);
172         use_labels = defaults->get("RENDER_FILE_PER_LABEL", 0);
173
174 // get plugin information
175         int need_plugin = !strlen(title) ? 1 : 0;
176 // generate a list of plugins for the window
177         if( need_plugin ) {
178                 mwindow->search_plugindb(default_asset->audio_data,
179                         default_asset->video_data, -1, 0, 0, local_plugindb);
180                 for(int i = 0; i < local_plugindb.total; i++) {
181                         plugin_list.append(new BC_ListBoxItem(_(local_plugindb.values[i]->title)));
182                 }
183         }
184
185 // find out which effect to run and get output file
186         int plugin_number, format_error = 0;
187
188         do
189         {
190                 {
191                         MenuEffectWindow window(mwindow,
192                                 this,
193                                 need_plugin ? &plugin_list : 0,
194                                 default_asset);
195                         window.create_objects();
196                         result = window.run_window();
197                         plugin_number = window.result;
198                 }
199
200                 if(!result)
201                 {
202                         FormatCheck format_check(default_asset);
203                         format_error = format_check.check_format();
204                 }
205         }while(format_error && !result);
206
207 // save defaults
208         save_derived_attributes(default_asset, defaults);
209         defaults->update("RENDER_EFFECT_LOADMODE", load_mode);
210         defaults->update("RENDER_EFFECT_FILE_PER_LABEL", use_labels);
211         mwindow->save_defaults();
212
213 // get plugin server to use and delete the plugin list
214         PluginServer *plugin_server = 0;
215         PluginServer *plugin = 0;
216         if(need_plugin)
217         {
218                 plugin_list.remove_all_objects();
219                 if(plugin_number > -1)
220                 {
221                         plugin_server = local_plugindb.values[plugin_number];
222                         strcpy(title, plugin_server->title);
223                 }
224         }
225         else
226         {
227                 for(int i = 0; i < plugindb->total && !plugin_server; i++)
228                 {
229                         if(!strcmp(plugindb->values[i]->title, title))
230                         {
231                                 plugin_server = plugindb->values[i];
232                                 plugin_number = i;
233                         }
234                 }
235         }
236
237 // Update the  most recently used effects and copy the plugin server.
238         if(plugin_server)
239         {
240                 plugin = new PluginServer(*plugin_server);
241                 fix_menu(title);
242         }
243
244         if(!result && !strlen(default_asset->path))
245         {
246                 result = 1;        // no output path given
247                 ErrorBox error(_(PROGRAM_NAME ": Error"));
248                 error.create_objects(_("No output file specified."));
249                 error.run_window();
250         }
251
252         if(!result && plugin_number < 0)
253         {
254                 result = 1;        // no output path given
255                 ErrorBox error(_(PROGRAM_NAME ": Error"));
256                 error.create_objects(_("No effect selected."));
257                 error.run_window();
258         }
259
260 // Configuration for realtime plugins.
261         KeyFrame plugin_data;
262
263 // get selection to render
264 // Range
265         double total_start, total_end;
266
267         total_start = mwindow->edl->local_session->get_selectionstart();
268
269
270         if(mwindow->edl->local_session->get_selectionend() ==
271                 mwindow->edl->local_session->get_selectionstart())
272                 total_end = mwindow->edl->tracks->total_length();
273         else
274                 total_end = mwindow->edl->local_session->get_selectionend();
275
276
277
278 // get native units for range
279         total_start = to_units(total_start, 0);
280         total_end = to_units(total_end, 1);
281
282
283
284 // Trick boundaries in case of a non-realtime synthesis plugin
285         if(plugin &&
286                 !plugin->realtime &&
287                 total_end == total_start) total_end = total_start + 1;
288
289 // Units are now in the track's units.
290         int64_t total_length = (int64_t)total_end - (int64_t)total_start;
291 // length of output file
292
293         if(!result && total_length <= 0)
294         {
295                 result = 1;        // no output path given
296                 ErrorBox error(_(PROGRAM_NAME ": Error"));
297                 error.create_objects(_("No selected range to process."));
298                 error.run_window();
299         }
300
301 // ========================= get keyframe from user
302         if(!result)
303         {
304 // ========================= realtime plugin
305 // no get_parameters
306                 if(plugin->realtime)
307                 {
308 // Open a prompt GUI
309                         MenuEffectPrompt prompt(mwindow);
310                         prompt.create_objects();
311                         char title[BCTEXTLEN];
312                         sprintf(title, _(PROGRAM_NAME ": %s"), plugin->title);
313
314 // Open the plugin GUI
315                         plugin->set_mwindow(mwindow);
316                         plugin->set_keyframe(&plugin_data);
317                         plugin->set_prompt(&prompt);
318                         plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0);
319 // Must set parameters since there is no plugin object to draw from.
320                         plugin->get_parameters((int64_t)total_start,
321                                 (int64_t)total_end,
322                                 1);
323                         plugin->show_gui();
324
325 // wait for user input
326                         result = prompt.run_window();
327
328 // Close plugin.
329                         plugin->save_data(&plugin_data);
330                         plugin->hide_gui();
331
332 // Can't delete here.
333                         dead_plugins->append(plugin);
334                         default_asset->sample_rate = mwindow->edl->session->sample_rate;
335                         default_asset->frame_rate = mwindow->edl->session->frame_rate;
336                         realtime = 1;
337                 }
338                 else
339 // ============================non realtime plugin
340                 {
341                         plugin->set_mwindow(mwindow);
342                         plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0);
343                         result = plugin->get_parameters((int64_t)total_start,
344                                 (int64_t)total_end,
345                                 get_recordable_tracks(default_asset));
346 // some plugins can change the sample rate and the frame rate
347
348
349                         if(!result)
350                         {
351                                 default_asset->sample_rate = plugin->get_samplerate();
352                                 default_asset->frame_rate = plugin->get_framerate();
353                         }
354                         delete plugin;
355                         realtime = 0;
356                 }
357
358 // Should take from first recordable track
359                 default_asset->width = mwindow->edl->session->output_w;
360                 default_asset->height = mwindow->edl->session->output_h;
361         }
362
363         int range = File::is_image_render(default_asset->format) ?
364                 RANGE_1FRAME : RANGE_SELECTION;
365         int strategy = Render::get_strategy(mwindow->preferences->use_renderfarm, use_labels, range);
366 // Process the total length in fragments
367         ArrayList<MenuEffectPacket*> packets;
368         if(!result)
369         {
370                 Label *current_label = mwindow->edl->labels->first;
371                 mwindow->stop_brender();
372
373                 int current_number;
374                 int number_start;
375                 int total_digits;
376                 Render::get_starting_number(default_asset->path,
377                         current_number,
378                         number_start,
379                         total_digits);
380
381
382
383 // Construct all packets for single overwrite confirmation
384                 for(int64_t fragment_start = (int64_t)total_start, fragment_end;
385                         fragment_start < (int64_t)total_end;
386                         fragment_start = fragment_end)
387                 {
388 // Get fragment end
389                         if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM)
390                         {
391                                 while(current_label  &&
392                                         to_units(current_label->position, 0) <= fragment_start)
393                                         current_label = current_label->next;
394                                 if(!current_label)
395                                         fragment_end = (int64_t)total_end;
396                                 else
397                                         fragment_end = to_units(current_label->position, 0);
398                         }
399                         else
400                         {
401                                 fragment_end = (int64_t)total_end;
402                         }
403
404 // Get path
405                         char path[BCTEXTLEN];
406                         if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM)
407                                 Render::create_filename(path,
408                                         default_asset->path,
409                                         current_number,
410                                         total_digits,
411                                         number_start);
412                         else
413                                 strcpy(path, default_asset->path);
414                         current_number++;
415
416                         MenuEffectPacket *packet = new MenuEffectPacket(path,
417                                 fragment_start,
418                                 fragment_end);
419                         packets.append(packet);
420                 }
421
422
423 // Test existence of files
424                 ArrayList<char*> paths;
425                 for(int i = 0; i < packets.total; i++)
426                 {
427                         paths.append(packets.values[i]->path);
428                 }
429                 result = ConfirmSave::test_files(mwindow, &paths);
430                 paths.remove_all();
431         }
432
433
434
435         for(int current_packet = 0;
436                 current_packet < packets.total && !result;
437                 current_packet++)
438         {
439                 Asset *asset = new Asset(*default_asset);
440                 MenuEffectPacket *packet = packets.values[current_packet];
441                 int64_t fragment_start = packet->start;
442                 int64_t fragment_end = packet->end;
443                 strcpy(asset->path, packet->path);
444
445                 assets.append(asset);
446                 File *file = new File;
447
448 // Open the output file after getting the information because the sample rate
449 // is needed here.
450                 if(!result)
451                 {
452 // open output file in write mode
453                         file->set_processors(mwindow->preferences->processors);
454                         if(file->open_file(mwindow->preferences,
455                                 asset,
456                                 0,
457                                 1))
458                         {
459 // open failed
460                                 sprintf(string, _("Couldn't open %s"), asset->path);
461                                 ErrorBox error(_(PROGRAM_NAME ": Error"));
462                                 error.create_objects(string);
463                                 error.run_window();
464                                 result = 1;
465                         }
466                         else
467                         {
468                                 mwindow->sighandler->push_file(file);
469                                 IndexFile::delete_index(mwindow->preferences,
470                                         asset);
471                         }
472                 }
473
474 // run plugins
475                 if(!result)
476                 {
477 // position file
478                         PluginArray *plugin_array;
479                         plugin_array = create_plugin_array();
480
481                         plugin_array->start_plugins(mwindow,
482                                 mwindow->edl,
483                                 plugin_server,
484                                 &plugin_data,
485                                 fragment_start,
486                                 fragment_end,
487                                 file);
488                         plugin_array->run_plugins();
489
490                         plugin_array->stop_plugins();
491                         mwindow->sighandler->pull_file(file);
492                         file->close_file();
493                         asset->audio_length = file->asset->audio_length;
494                         asset->video_length = file->asset->video_length;
495                         delete plugin_array;
496                 }
497
498                 delete file;
499         }
500
501         packets.remove_all_objects();
502
503 // paste output to tracks
504         if(!result && load_mode != LOADMODE_NOTHING)
505         {
506                 mwindow->gui->lock_window("MenuEffectThread::run");
507
508                 mwindow->undo->update_undo_before("", 0);
509                 if(load_mode == LOADMODE_PASTE)
510                         mwindow->clear(0);
511
512                 mwindow->load_assets(&assets, -1, load_mode, 0, 0,
513                         mwindow->edl->session->labels_follow_edits,
514                         mwindow->edl->session->plugins_follow_edits,
515                         mwindow->edl->session->autos_follow_edits,
516                         0); // overwrite
517
518                 mwindow->save_backup();
519                 mwindow->undo->update_undo_after(title, LOAD_ALL);
520
521                 mwindow->restart_brender();
522                 mwindow->update_plugin_guis();
523                 mwindow->gui->update(1, FORCE_REDRAW, 1, 1, 1, 1, 0);
524                 mwindow->sync_parameters(CHANGE_ALL);
525                 mwindow->gui->unlock_window();
526
527                 mwindow->awindow->gui->async_update_assets();
528         }
529
530         for(int i = 0; i < assets.total; i++)
531                 assets.values[i]->Garbage::remove_user();
532         assets.remove_all();
533
534         default_asset->Garbage::remove_user();
535 }
536
537
538
539
540
541
542
543 MenuEffectItem::MenuEffectItem(MenuEffects *menueffect, const char *string)
544  : BC_MenuItem(string)
545 {
546         this->menueffect = menueffect;
547 }
548 int MenuEffectItem::handle_event()
549 {
550         menueffect->thread->set_title(get_text());
551         menueffect->thread->start();
552         return 1;
553 }
554
555
556
557
558
559
560
561
562
563
564
565
566 MenuEffectWindow::MenuEffectWindow(MWindow *mwindow,
567         MenuEffectThread *menueffects,
568         ArrayList<BC_ListBoxItem*> *plugin_list,
569         Asset *asset)
570  : BC_Window(_(PROGRAM_NAME ": Render effect"),
571                 mwindow->gui->get_abs_cursor_x(1),
572                 mwindow->gui->get_abs_cursor_y(1) - mwindow->session->menueffect_h / 2,
573                 mwindow->session->menueffect_w,
574                 mwindow->session->menueffect_h,
575                 xS(580), yS(350), 1, 0, 1)
576 {
577         this->menueffects = menueffects;
578         this->plugin_list = plugin_list;
579         this->asset = asset;
580         this->mwindow = mwindow;
581         file_title = 0;
582         format_tools = 0;
583         loadmode = 0;
584 // *** CONTEXT_HELP ***
585         context_help_set_keyword("Rendered Effects");
586 }
587
588 MenuEffectWindow::~MenuEffectWindow()
589 {
590         lock_window("MenuEffectWindow::~MenuEffectWindow");
591         delete format_tools;
592         unlock_window();
593 }
594
595
596
597 void MenuEffectWindow::create_objects()
598 {
599         int x, y;
600         result = -1;
601         mwindow->theme->get_menueffect_sizes(plugin_list ? 1 : 0);
602
603         lock_window("MenuEffectWindow::create_objects");
604 // only add the list if needed
605         if(plugin_list)
606         {
607                 add_subwindow(list_title = new BC_Title(mwindow->theme->menueffect_list_x,
608                         mwindow->theme->menueffect_list_y,
609                         _("Select an effect")));
610                 int ys5 = yS(5);
611                 add_subwindow(list = new MenuEffectWindowList(this,
612                         mwindow->theme->menueffect_list_x,
613                         mwindow->theme->menueffect_list_y + list_title->get_h() + ys5,
614                         mwindow->theme->menueffect_list_w,
615                         mwindow->theme->menueffect_list_h - list_title->get_h() - ys5,
616                         plugin_list));
617         }
618
619         add_subwindow(file_title = new BC_Title(
620                 mwindow->theme->menueffect_file_x,
621                 mwindow->theme->menueffect_file_y,
622                 (char*)(menueffects->use_labels ?
623                         _("Select the first file to render to:") :
624                         _("Select a file to render to:"))));
625
626         x = mwindow->theme->menueffect_tools_x;
627         y = mwindow->theme->menueffect_tools_y;
628         format_tools = new FormatTools(mwindow,
629                                         this,
630                                         asset);
631         format_tools->create_objects(x, y, asset->audio_data, asset->video_data,
632                 0, 0, 0, 1, 0, 0, &menueffects->use_labels, 0);
633
634         loadmode = new LoadMode(mwindow, this, x, y, &menueffects->load_mode);
635         loadmode->create_objects();
636
637         add_subwindow(new MenuEffectWindowOK(this));
638         add_subwindow(new MenuEffectWindowCancel(this));
639         show_window();
640         unlock_window();
641 }
642
643 int MenuEffectWindow::resize_event(int w, int h)
644 {
645         mwindow->session->menueffect_w = w;
646         mwindow->session->menueffect_h = h;
647         mwindow->theme->get_menueffect_sizes(plugin_list ? 1 : 0);
648
649         if(plugin_list)
650         {
651                 list_title->reposition_window(mwindow->theme->menueffect_list_x,
652                         mwindow->theme->menueffect_list_y);
653                 int ys5 = yS(5);
654                 list->reposition_window(mwindow->theme->menueffect_list_x,
655                         mwindow->theme->menueffect_list_y + list_title->get_h() + ys5,
656                         mwindow->theme->menueffect_list_w,
657                         mwindow->theme->menueffect_list_h - list_title->get_h() - ys5);
658         }
659
660         if(file_title) file_title->reposition_window(mwindow->theme->menueffect_file_x,
661                 mwindow->theme->menueffect_file_y);
662         int x = mwindow->theme->menueffect_tools_x;
663         int y = mwindow->theme->menueffect_tools_y;
664         if(format_tools) format_tools->reposition_window(x, y);
665         if(loadmode) loadmode->reposition_window(x, y);
666         return 0;
667 }
668
669
670
671 MenuEffectWindowOK::MenuEffectWindowOK(MenuEffectWindow *window)
672  : BC_OKButton(window)
673 {
674         this->window = window;
675 }
676
677 int MenuEffectWindowOK::handle_event()
678 {
679         if(window->plugin_list)
680                 window->result = window->list->get_selection_number(0, 0);
681
682         window->set_done(0);
683         return 1;
684 }
685
686 int MenuEffectWindowOK::keypress_event()
687 {
688         if(get_keypress() == RETURN)
689         {
690                 handle_event();
691                 return 1;
692         }
693         return context_help_check_and_show();
694 }
695
696 MenuEffectWindowCancel::MenuEffectWindowCancel(MenuEffectWindow *window)
697  : BC_CancelButton(window)
698 {
699         this->window = window;
700 }
701
702 int MenuEffectWindowCancel::handle_event()
703 {
704         window->set_done(1);
705         return 1;
706 }
707
708 int MenuEffectWindowCancel::keypress_event()
709 {
710         if(get_keypress() == ESC)
711         {
712                 handle_event();
713                 return 1;
714         }
715         return context_help_check_and_show();
716 }
717
718 MenuEffectWindowList::MenuEffectWindowList(MenuEffectWindow *window,
719         int x,
720         int y,
721         int w,
722         int h,
723         ArrayList<BC_ListBoxItem*> *plugin_list)
724  : BC_ListBox(x, y, w, h, LISTBOX_TEXT, plugin_list)
725 {
726         this->window = window;
727 }
728
729 int MenuEffectWindowList::handle_event()
730 {
731         window->result = get_selection_number(0, 0);
732         window->set_done(0);
733         return 1;
734 }
735
736 // *** CONTEXT_HELP ***
737 int MenuEffectWindowList::keypress_event()
738 {
739         int item;
740         char title[BCTEXTLEN];
741
742 //      printf("MenuEffectWindowList::keypress_event: %d\n", get_keypress());
743
744         // If not our context help keystroke, redispatch it
745         // to the event handler of the base class
746         if (get_keypress() != 'h' || ! alt_down() ||
747             ! is_tooltip_event_win() || ! cursor_inside())
748                 return BC_ListBox::keypress_event();
749
750         // Try to show help for the plugin currently under mouse
751         title[0] = '\0';
752         item = get_highlighted_item();
753         if (item >= 0 && item < window->plugin_list->total)
754                 strcpy(title, window->plugin_list->values[item]->get_text());
755
756         // If some plugin is highlighted, show its help
757         // Otherwise show more general help
758         if (title[0]) {
759                 if (! strcmp(title, "Overlay")) {
760                         // "Overlay" plugin title is ambiguous
761                         if (window->asset->audio_data)
762                                 strcat(title, " \\(Audio\\)");
763                         if (window->asset->video_data)
764                                 strcat(title, " \\(Video\\)");
765                 }
766                 if (! strncmp(title, "F_", 2)) {
767                         // FFmpeg plugins can be audio or video
768                         if (window->asset->audio_data)
769                                 strcpy(title, "FFmpeg Audio Plugins");
770                         if (window->asset->video_data)
771                                 strcpy(title, "FFmpeg Video Plugins");
772                 }
773                 context_help_show(title);
774                 return 1;
775         }
776         else {
777                 context_help_show("Rendered Effects");
778                 return 1;
779         }
780         context_help_show("Rendered Effects");
781         return 1;
782 }
783
784 #define PROMPT_TEXT _("Set up effect panel and hit \"OK\"")
785 #define MEP_W xS(260)
786 #define MEP_H yS(100)
787
788 MenuEffectPrompt::MenuEffectPrompt(MWindow *mwindow)
789  : BC_Window(_(PROGRAM_NAME ": Effect Prompt"),
790                 mwindow->gui->get_abs_cursor_x(1) - MEP_W/2,
791                 mwindow->gui->get_abs_cursor_y(1) - MEP_H/2,
792                 MenuEffectPrompt::calculate_w(mwindow->gui),
793                 MenuEffectPrompt::calculate_h(mwindow->gui),
794                 MenuEffectPrompt::calculate_w(mwindow->gui),
795                 MenuEffectPrompt::calculate_h(mwindow->gui),
796                 0, 0, 1)
797 {
798 // *** CONTEXT_HELP ***
799         context_help_set_keyword("Rendered Effects");
800 }
801
802 int MenuEffectPrompt::calculate_w(BC_WindowBase *gui)
803 {
804         int w = BC_Title::calculate_w(gui, PROMPT_TEXT) + xS(10);
805         w = MAX(w, BC_OKButton::calculate_w() + BC_CancelButton::calculate_w() + xS(30));
806         return w;
807 }
808
809 int MenuEffectPrompt::calculate_h(BC_WindowBase *gui)
810 {
811         int h = BC_Title::calculate_h(gui, PROMPT_TEXT);
812         h += BC_OKButton::calculate_h() + yS(30);
813         return h;
814 }
815
816
817 void MenuEffectPrompt::create_objects()
818 {
819         lock_window("MenuEffectPrompt::create_objects");
820         int x = xS(10), y = yS(10);
821         BC_Title *title;
822         add_subwindow(title = new BC_Title(x, y, PROMPT_TEXT));
823         add_subwindow(new BC_OKButton(this));
824         add_subwindow(new BC_CancelButton(this));
825         show_window();
826         raise_window();
827         unlock_window();
828 }
829