add new tracks as master, update msg txt
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / mainmenu.C
index 11edba8fc44f8795f682bc275a23dc7289a5b88c..de902545f4d2a9c6977469b021287effe5b55233 100644 (file)
@@ -28,6 +28,7 @@
 #include "bdcreate.h"
 #include "cache.h"
 #include "channelinfo.h"
+#include "convert.h"
 #include "cplayback.h"
 #include "cropvideo.h"
 #include "cwindow.h"
@@ -36,6 +37,7 @@
 #include "dvdcreate.h"
 #include "edl.h"
 #include "edlsession.h"
+#include "exportedl.h"
 #include "file.h"
 #include "filesystem.h"
 #include "filexml.h"
@@ -55,6 +57,7 @@
 #include "menueditlength.h"
 #include "menutransitionlength.h"
 #include "menuveffects.h"
+#include "mixersalign.h"
 #include "mwindowgui.h"
 #include "mwindow.h"
 #include "new.h"
@@ -76,7 +79,8 @@
 #include "transportque.h"
 #include "viewmenu.h"
 #include "zoombar.h"
-#include "exportedl.h"
+#include "zwindow.h"
+#include "zwindowgui.h"
 
 #include <string.h>
 
@@ -96,7 +100,6 @@ void MainMenu::create_objects()
 {
        BC_Menu *viewmenu, *windowmenu, *settingsmenu, *trackmenu;
        PreferencesMenuitem *preferences;
-       total_loads = 0;
 
        add_menu(filemenu = new BC_Menu(_("File")));
        filemenu->add_item(new_project = new NewProject(mwindow));
@@ -105,6 +108,8 @@ void MainMenu::create_objects()
 // file loaders
        filemenu->add_item(load_file = new Load(mwindow, this));
        load_file->create_objects();
+       filemenu->add_item(load_recent = new LoadRecent(mwindow, this));
+       load_recent->create_objects();
 
 // new and load can be undone so no need to prompt save
        Save *save;                   //  affected by saveas
@@ -134,8 +139,8 @@ void MainMenu::create_objects()
        filemenu->add_item(new BC_MenuItem("-"));
        filemenu->add_item(quit_program = new Quit(mwindow));
        quit_program->create_objects(save);
-       filemenu->add_item(new DumpEDL(mwindow));
-       filemenu->add_item(new DumpPlugins(mwindow));
+       filemenu->add_item(dump_menu = new MainDumpsMenu(mwindow));
+       dump_menu->create_objects();
        filemenu->add_item(new LoadBackup(mwindow));
        filemenu->add_item(new SaveBackup(mwindow));
 
@@ -147,9 +152,9 @@ void MainMenu::create_objects()
        editmenu->add_item(new Cut(mwindow));
        editmenu->add_item(new Copy(mwindow));
        editmenu->add_item(new Paste(mwindow));
-       editmenu->add_item(new Clear(mwindow));
        editmenu->add_item(new PasteSilence(mwindow));
-       editmenu->add_item(new MuteSelection(mwindow));
+       editmenu->add_item(clear_menu = new EditClearMenu(mwindow));
+       clear_menu->create_objects();
        editmenu->add_item(new TrimSelection(mwindow));
        editmenu->add_item(new SelectAll(mwindow));
        editmenu->add_item(new BC_MenuItem("-"));
@@ -160,7 +165,6 @@ void MainMenu::create_objects()
        editmenu->add_item(new MenuTransitionLength(mwindow));
        editmenu->add_item(new DetachTransitions(mwindow));
        editmenu->add_item(new BC_MenuItem("-"));
-       editmenu->add_item(new ClearLabels(mwindow));
        editmenu->add_item(new CutCommercials(mwindow));
        editmenu->add_item(new PasteSubttl(mwindow));
 
@@ -202,7 +206,8 @@ void MainMenu::create_objects()
        trackmenu->add_item(new MoveTracksUp(mwindow));
        trackmenu->add_item(new MoveTracksDown(mwindow));
        trackmenu->add_item(new DeleteTracks(mwindow));
-       trackmenu->add_item(new DeleteTrack(mwindow));
+       trackmenu->add_item(new DeleteFirstTrack(mwindow));
+       trackmenu->add_item(new DeleteLastTrack(mwindow));
        trackmenu->add_item(new ConcatenateTracks(mwindow));
        AppendTracks *append_tracks;
        trackmenu->add_item(append_tracks = new AppendTracks(mwindow));
@@ -216,6 +221,9 @@ void MainMenu::create_objects()
        ProxyMenuItem *proxy;
        settingsmenu->add_item(proxy = new ProxyMenuItem(mwindow));
        proxy->create_objects();
+       ConvertMenuItem *convert;
+       settingsmenu->add_item(convert = new ConvertMenuItem(mwindow));
+       convert->create_objects();
        mwindow->preferences_thread = preferences->thread;
        settingsmenu->add_item(cursor_on_frames = new CursorOnFrames(mwindow));
        settingsmenu->add_item(labels_follow_edits = new LabelsFollowEdits(mwindow));
@@ -271,8 +279,8 @@ void MainMenu::create_objects()
        windowmenu->add_item(new BC_MenuItem("-"));
        windowmenu->add_item(split_x = new SplitX(mwindow));
        windowmenu->add_item(split_y = new SplitY(mwindow));
-       windowmenu->add_item(mixer_viewer = new MixerViewer(mwindow));
-       windowmenu->add_item(new TileMixers(mwindow));
+       windowmenu->add_item(mixer_items = new MixerItems(mwindow));
+       mixer_items->create_objects();
        windowmenu->add_item(new TileWindows(mwindow,_("Tile left"),0));
        windowmenu->add_item(new TileWindows(mwindow,_("Tile right"),1));
        windowmenu->add_item(new BC_MenuItem("-"));
@@ -382,22 +390,21 @@ int MainMenu::init_veffects(BC_Hash *defaults)
 
 void MainMenu::init_loads(BC_Hash *defaults)
 {
-       total_loads = defaults->get((char*)"TOTAL_LOADS", 0);
-       if( !total_loads ) return;
-       filemenu->add_item(new BC_MenuItem("-"));
+// total_loads for legacy xml
+       int total_loads = defaults->get((char*)"TOTAL_LOADS", 0);
+       int loads_total = defaults->get((char*)"LOADS_TOTAL", 0);
+       if( loads_total < total_loads ) loads_total = total_loads;
 
-       char string[BCTEXTLEN], path[BCTEXTLEN], filename[BCTEXTLEN];
+       char string[BCTEXTLEN], path[BCTEXTLEN];
        FileSystem dir;
 //printf("MainMenu::init_loads 2\n");
 
-       for(int i = 0; i < total_loads; i++) {
+       for( int i=0; i<loads_total; ++i ) {
                sprintf(string, "LOADPREVIOUS%d", i);
 //printf("MainMenu::init_loads 3\n");
                defaults->get(string, path);
-               filemenu->add_item(load[i] = new LoadPrevious(mwindow, load_file));
-               dir.extract_name(filename, path, 0);
-               load[i]->set_text(filename);
-               load[i]->set_path(path);
+               if( load.size() < TOTAL_LOADS )
+                       load.append(new LoadRecentItem(path));
        }
 }
 
@@ -429,10 +436,13 @@ int MainMenu::save_veffects(BC_Hash *defaults)
 
 int MainMenu::save_loads(BC_Hash *defaults)
 {
+// legacy to prevent segv, older code cant tolerate total_loads>10
+       int loads_total = load.size();
+       int total_loads = MIN(10, loads_total);
+       defaults->update((char*)"LOADS_TOTAL", loads_total);
        defaults->update((char*)"TOTAL_LOADS", total_loads);
        char string[BCTEXTLEN];
-       for(int i = 0; i < total_loads; i++)
-       {
+       for( int i=0; i<loads_total; ++i ) {
                sprintf(string, "LOADPREVIOUS%d", i);
                defaults->update(string, load[i]->path);
        }
@@ -527,63 +537,42 @@ int MainMenu::add_veffect(char *title)
 
 int MainMenu::add_load(char *path)
 {
-       if(total_loads == 0)
-{
-               filemenu->add_item(new BC_MenuItem("-"));
-       }
-
-// test for existing copy
-       FileSystem fs;
-       char text[BCTEXTLEN], new_path[BCTEXTLEN];      // get text and path
-       fs.extract_name(text, path);
-       strcpy(new_path, path);
-
-       for(int i = 0; i < total_loads; i++)
-       {
-               if(!strcmp(load[i]->get_text(), text))     // already exists
-               {                                // swap for top load
-                       for(int j = i; j > 0; j--)   // move preceeding loads down
-                       {
-                               load[j]->set_text(load[j - 1]->get_text());
-                               load[j]->set_path(load[j - 1]->path);
-       }
-                       load[0]->set_text(text);
-                       load[0]->set_path(new_path);
-
-                       return 1;
-               }
-       }
-
-// add another load
-       if(total_loads < TOTAL_LOADS)
-       {
-               filemenu->add_item(load[total_loads] = new LoadPrevious(mwindow, load_file));
-               total_loads++;
-       }
-
-// cycle loads down
-       for(int i = total_loads - 1; i > 0; i--)
-       {
-       // set menu item text
-               load[i]->set_text(load[i - 1]->get_text());
-       // set filename
-               load[i]->set_path(load[i - 1]->path);
-       }
-
-// set up the new load
-       load[0]->set_text(text);
-       load[0]->set_path(new_path);
-       return 0;
+       return load.add_load(path);
 }
 
 
 
+// ================================== menu items
 
+MainDumpsSubMenu::MainDumpsSubMenu(BC_MenuItem *menu_item)
+ : BC_SubMenu()
+{
+       this->menu_item = menu_item;
+}
+MainDumpsSubMenu::~MainDumpsSubMenu()
+{
+}
 
 
+MainDumpsMenu::MainDumpsMenu(MWindow *mwindow)
+ : BC_MenuItem(_("Dumps..."))
+{
+       this->mwindow = mwindow;
+       this->dumps_menu = 0;
+}
+MainDumpsMenu::~MainDumpsMenu()
+{
+}
 
-
-// ================================== menu items
+void MainDumpsMenu::create_objects()
+{
+       add_submenu(dumps_menu = new MainDumpsSubMenu(this));
+//     dumps_menu->add_item(new DumpCICache(mwindow));
+       dumps_menu->add_item(new DumpEDL(mwindow));
+       dumps_menu->add_item(new DumpPlugins(mwindow));
+       dumps_menu->add_item(new DumpAssets(mwindow));
+       dumps_menu->add_item(new DumpUndo(mwindow));
+};
 
 
 DumpCICache::DumpCICache(MWindow *mwindow)
@@ -604,9 +593,7 @@ DumpEDL::DumpEDL(MWindow *mwindow)
 
 int DumpEDL::handle_event()
 {
-//printf("DumpEDL::handle_event 1\n");
        mwindow->dump_edl();
-//printf("DumpEDL::handle_event 2\n");
        return 1;
 }
 
@@ -618,20 +605,29 @@ DumpPlugins::DumpPlugins(MWindow *mwindow)
 
 int DumpPlugins::handle_event()
 {
-//printf("DumpEDL::handle_event 1\n");
        mwindow->dump_plugins();
-//printf("DumpEDL::handle_event 2\n");
        return 1;
 }
 
-
 DumpAssets::DumpAssets(MWindow *mwindow)
  : BC_MenuItem(_("Dump Assets"))
 { this->mwindow = mwindow; }
 
 int DumpAssets::handle_event()
 {
-       mwindow->assets->dump();
+       mwindow->edl->assets->dump();
+       return 1;
+}
+
+DumpUndo::DumpUndo(MWindow *mwindow)
+ : BC_MenuItem(_("Dump Undo"))
+{
+       this->mwindow = mwindow;
+}
+
+int DumpUndo::handle_event()
+{
+       mwindow->dump_undo();
        return 1;
 }
 
@@ -917,6 +913,35 @@ int Paste::handle_event()
        return 1;
 }
 
+EditClearSubMenu::EditClearSubMenu(BC_MenuItem *menu_item)
+ : BC_SubMenu()
+{
+       this->menu_item = menu_item;
+}
+EditClearSubMenu::~EditClearSubMenu()
+{
+}
+
+EditClearMenu::EditClearMenu(MWindow *mwindow)
+ : BC_MenuItem(_("Clear..."))
+{
+       this->mwindow = mwindow;
+       this->clear_sub_menu = 0;
+}
+EditClearMenu::~EditClearMenu()
+{
+}
+
+void EditClearMenu::create_objects()
+{
+       add_submenu(clear_sub_menu = new EditClearSubMenu(this));
+       clear_sub_menu->add_item(new Clear(mwindow));
+       clear_sub_menu->add_item(new MuteSelection(mwindow));
+       clear_sub_menu->add_item(new ClearSelect(mwindow));
+       clear_sub_menu->add_item(new ClearLabels(mwindow));
+       clear_sub_menu->add_item(new ClearHardEdges(mwindow));
+};
+
 Clear::Clear(MWindow *mwindow)
  : BC_MenuItem(_("Clear"), _("Del"), DELETE)
 {
@@ -926,9 +951,7 @@ Clear::Clear(MWindow *mwindow)
 int Clear::handle_event()
 {
        if( mwindow->session->current_operation == NO_OPERATION ) {
-               mwindow->cwindow->gui->lock_window("Clear::handle_event");
                mwindow->clear_entry();
-               mwindow->cwindow->gui->unlock_window();
        }
        return 1;
 }
@@ -960,6 +983,17 @@ int SelectAll::handle_event()
        return 1;
 }
 
+ClearHardEdges::ClearHardEdges(MWindow *mwindow) : BC_MenuItem(_("Clear Hard Edges"))
+{
+       this->mwindow = mwindow;
+}
+
+int ClearHardEdges::handle_event()
+{
+       mwindow->clear_hard_edges();
+       return 1;
+}
+
 ClearLabels::ClearLabels(MWindow *mwindow) : BC_MenuItem(_("Clear labels"))
 {
        this->mwindow = mwindow;
@@ -971,6 +1005,19 @@ int ClearLabels::handle_event()
        return 1;
 }
 
+ClearSelect::ClearSelect(MWindow *mwindow) : BC_MenuItem(_("Clear Select"),"Ctrl-Shift-A",'A')
+{
+       set_ctrl(1);
+       set_shift(1);
+       this->mwindow = mwindow;
+}
+
+int ClearSelect::handle_event()
+{
+       mwindow->clear_select();
+       return 1;
+}
+
 CutCommercials::CutCommercials(MWindow *mwindow) : BC_MenuItem(_("Cut ads"))
 {
        this->mwindow = mwindow;
@@ -1111,7 +1158,7 @@ AddVideoTrack::AddVideoTrack(MWindow *mwindow)
 int AddVideoTrack::handle_event()
 {
        if( mwindow->session->current_operation == NO_OPERATION )
-               mwindow->add_video_track_entry();
+               mwindow->add_video_track_entry(1, 0);
        return 1;
 }
 
@@ -1183,23 +1230,43 @@ int DeleteTracks::handle_event()
        return 1;
 }
 
-DeleteTrack::DeleteTrack(MWindow *mwindow)
- : BC_MenuItem(_("Delete last track"), "d", 'd')
+DeleteFirstTrack::DeleteFirstTrack(MWindow *mwindow)
+ : BC_MenuItem(_("Delete first track"), "Shift-D", 'D')
 {
+       set_shift(1);
        this->mwindow = mwindow;
 }
 
-int DeleteTrack::handle_event()
+int DeleteFirstTrack::handle_event()
 {
-       if( mwindow->session->current_operation == NO_OPERATION )
-               mwindow->delete_track();
+       if( mwindow->session->current_operation == NO_OPERATION ) {
+               Track *track = mwindow->edl->tracks->first;
+               if( track ) mwindow->delete_track(track);
+       }
+       return 1;
+}
+
+DeleteLastTrack::DeleteLastTrack(MWindow *mwindow)
+ : BC_MenuItem(_("Delete last track"), "Ctrl-d", 'd')
+{
+       set_ctrl(1);
+       this->mwindow = mwindow;
+}
+
+int DeleteLastTrack::handle_event()
+{
+       if( mwindow->session->current_operation == NO_OPERATION ) {
+               Track *track = mwindow->edl->tracks->last;
+               if( track ) mwindow->delete_track(track);
+       }
        return 1;
 }
 
 MoveTracksUp::MoveTracksUp(MWindow *mwindow)
  : BC_MenuItem(_("Move tracks up"), _("Shift-Up"), UP)
 {
-       set_shift(); this->mwindow = mwindow;
+       this->mwindow = mwindow;
+       set_shift();
 }
 
 int MoveTracksUp::handle_event()
@@ -1274,7 +1341,7 @@ AddSubttlTrack::AddSubttlTrack(MWindow *mwindow)
 int AddSubttlTrack::handle_event()
 {
        if( mwindow->session->current_operation == NO_OPERATION )
-               mwindow->add_subttl_track_entry();
+               mwindow->add_subttl_track_entry(1, 0);
        return 1;
 }
 
@@ -1442,7 +1509,9 @@ ShowVWindow::ShowVWindow(MWindow *mwindow)
 }
 int ShowVWindow::handle_event()
 {
+       mwindow->gui->unlock_window();
        mwindow->show_vwindow();
+       mwindow->gui->lock_window("ShowVWindow::handle_event");
        return 1;
 }
 
@@ -1454,7 +1523,9 @@ ShowAWindow::ShowAWindow(MWindow *mwindow)
 }
 int ShowAWindow::handle_event()
 {
+       mwindow->gui->unlock_window();
        mwindow->show_awindow();
+       mwindow->gui->lock_window("ShowAWindow::handle_event");
        return 1;
 }
 
@@ -1466,7 +1537,9 @@ ShowCWindow::ShowCWindow(MWindow *mwindow)
 }
 int ShowCWindow::handle_event()
 {
+       mwindow->gui->unlock_window();
        mwindow->show_cwindow();
+       mwindow->gui->lock_window("ShowCWindow::handle_event");
        return 1;
 }
 
@@ -1481,10 +1554,12 @@ ShowGWindow::ShowGWindow(MWindow *mwindow)
 int ShowGWindow::handle_event()
 {
        if( mwindow->session->current_operation == NO_OPERATION ) {
+               mwindow->gui->unlock_window();
                if( !mwindow->session->show_gwindow )
                        mwindow->show_gwindow();
                else
                        mwindow->hide_gwindow();
+               mwindow->gui->lock_window("ShowGWindow::handle_event");
                set_checked(mwindow->session->show_gwindow);
        }
        return 1;
@@ -1499,7 +1574,9 @@ ShowLWindow::ShowLWindow(MWindow *mwindow)
 }
 int ShowLWindow::handle_event()
 {
+       mwindow->gui->unlock_window();
        mwindow->show_lwindow();
+       mwindow->gui->lock_window("ShowLWindow::handle_event");
        return 1;
 }
 
@@ -1553,37 +1630,119 @@ int SplitY::handle_event()
 }
 
 
-MixerViewer::MixerViewer(MWindow *mwindow)
- : BC_MenuItem(_("Mixer Viewer"), _("Shift-M"), 'M')
+MixerItems::MixerItems(MWindow *mwindow)
+ : BC_MenuItem(_("Mixers..."))
 {
        this->mwindow = mwindow;
+}
+
+void MixerItems::create_objects()
+{
+       BC_SubMenu *mixer_submenu = new BC_SubMenu();
+       add_submenu(mixer_submenu);
+       mixer_submenu->add_submenuitem(new MixerViewer(this));
+       mixer_submenu->add_submenuitem(new TileMixers(this));
+       mixer_submenu->add_submenuitem(new AlignMixers(this));
+}
+
+int MixerItems::activate_submenu()
+{
+       BC_SubMenu *mixer_submenu = (BC_SubMenu *)get_submenu();
+       int k = mixer_submenu->total_items();
+       while( --k >= 0 ) {
+               MixerItem *mixer_item = (MixerItem *)mixer_submenu->get_item(k);
+               if( mixer_item->idx < 0 ) continue;
+               mixer_submenu->del_item(mixer_item);
+       }
+       int n = mwindow->edl->mixers.size();
+       for( int i=0; i<n; ++i ) {
+               Mixer *mixer = mwindow->edl->mixers[i];
+               if( !mixer ) continue;
+               MixerItem *mixer_item = new MixerItem(this, mixer->title, mixer->idx);
+               mixer_submenu->add_submenuitem(mixer_item);
+       }
+       return BC_MenuItem::activate_submenu();
+}
+
+MixerItem::MixerItem(MixerItems *mixer_items, const char *text, int idx)
+ : BC_MenuItem(text)
+{
+       this->mixer_items = mixer_items;
+       this->idx = idx;
+}
+
+MixerItem::MixerItem(MixerItems *mixer_items, const char *text, const char *hotkey_text, int hotkey)
+ : BC_MenuItem(text, hotkey_text, hotkey)
+{
+       this->mixer_items = mixer_items;
+       this->idx = -1;
+}
+
+int MixerItem::handle_event()
+{
+       if( idx < 0 ) return 0;
+       MWindow *mwindow = mixer_items->mwindow;
+       Mixer *mixer = mwindow->edl->mixers.get_mixer(idx);
+       if( !mixer ) return 0;
+       ZWindow *zwindow = mwindow->get_mixer(idx);
+       if( !zwindow )
+               zwindow = mwindow->get_mixer(mixer);
+       if( !zwindow->zgui ) {
+               zwindow->set_title(mixer->title);
+               zwindow->start();
+       }
+       zwindow->zgui->lock_window("MixerItem::handle_event");
+       zwindow->zgui->raise_window();
+       zwindow->zgui->unlock_window();
+       mwindow->refresh_mixers();
+       return 1;
+}
+
+MixerViewer::MixerViewer(MixerItems *mixer_items)
+ : MixerItem(mixer_items, _("Mixer Viewer"), _("Shift-M"), 'M')
+{
        set_shift(1);
 }
 
 int MixerViewer::handle_event()
 {
+       MWindow *mwindow = mixer_items->mwindow;
        mwindow->start_mixer();
        return 1;
 }
 
-TileMixers::TileMixers(MWindow *mwindow)
- : BC_MenuItem(_("Tile mixers"), "Alt-t", 't')
+TileMixers::TileMixers(MixerItems *mixer_items)
+ : MixerItem(mixer_items, _("Tile mixers"), "Alt-t", 't')
 {
-       this->mwindow = mwindow;
        set_alt();
 }
 
 int TileMixers::handle_event()
 {
+       MWindow *mwindow = mixer_items->mwindow;
        mwindow->tile_mixers();
        return 1;
 }
 
+AlignMixers::AlignMixers(MixerItems *mixer_items)
+ : MixerItem(mixer_items, _("Align mixers"), "", 0)
+{
+}
 
-LoadLayoutItem::LoadLayoutItem(LoadLayout *load_layout, const char *text, int no, int hotkey)
+int AlignMixers::handle_event()
+{
+       MWindow *mwindow = mixer_items->mwindow;
+       int wx, wy;
+       mwindow->gui->get_abs_cursor(wx, wy);
+       mwindow->mixers_align->start_dialog(wx, wy);
+       return 1;
+}
+
+
+LoadLayoutItem::LoadLayoutItem(LoadLayout *load_layout, const char *text, int idx, int hotkey)
  : BC_MenuItem(text, "", hotkey)
 {
-       this->no = no;
+       this->idx = idx;
        this->load_layout = load_layout;
        if( hotkey ) {
                char hot_txt[BCSTRLEN];
@@ -1593,16 +1752,21 @@ LoadLayoutItem::LoadLayoutItem(LoadLayout *load_layout, const char *text, int no
        }
 }
 
+
 int LoadLayoutItem::handle_event()
 {
+// key_press hotkey skips over activate_submenu
+       load_layout->update();
        MWindow *mwindow = load_layout->mwindow;
        switch( load_layout->action ) {
        case LAYOUT_LOAD:
-               mwindow->load_layout(no);
-               break;
-       case LAYOUT_SAVE:
-               mwindow->save_layout(no);
+               mwindow->load_layout(layout_file);
                break;
+       case LAYOUT_SAVE: {
+               int wx = 0, wy = 0;
+               mwindow->gui->get_abs_cursor(wx, wy);
+               load_layout->layout_dialog->start_confirm_dialog(wx, wy, idx);
+               break; }
        }
        return 1;
 }
@@ -1612,19 +1776,302 @@ LoadLayout::LoadLayout(MWindow *mwindow, const char *text, int action)
 {
        this->mwindow = mwindow;
        this->action = action;
+       this->layout_dialog = new LoadLayoutDialog(this);
+}
+
+LoadLayout::~LoadLayout()
+{
+       delete layout_dialog;
 }
 
 void LoadLayout::create_objects()
 {
        BC_SubMenu *layout_submenu = new BC_SubMenu();
        add_submenu(layout_submenu);
-       for( int i=1; i<=4; ++i ) {
+
+       for( int i=0; i<LAYOUTS_MAX; ++i ) {
                char text[BCSTRLEN];
-               sprintf(text, _("Layout %d"), i);
-               LoadLayoutItem *load_layout_item =
-                       new LoadLayoutItem(this, text, i,
-                               action==LAYOUT_LOAD ? KEY_F1-1+i : 0);
-               layout_submenu->add_submenuitem(load_layout_item);
+               sprintf(text, _("Layout %d"), i+1);
+               LoadLayoutItem *item = new LoadLayoutItem(this, text, i,
+                               action==LAYOUT_LOAD ? KEY_F1+i : 0);
+               layout_submenu->add_submenuitem(item);
+       }
+}
+
+int LoadLayout::activate_submenu()
+{
+       update();
+       return BC_MenuItem::activate_submenu();
+}
+
+void LoadLayout::update()
+{
+       FileSystem fs;
+       fs.set_filter("layout*_rc");
+       int ret = fs.update(File::get_config_path());
+       int sz = !ret ? fs.dir_list.size() : 0;
+       BC_SubMenu *layout_submenu = get_submenu();
+
+       for( int i=0; i<LAYOUTS_MAX; ++i ) {
+               LoadLayoutItem* item = (LoadLayoutItem *)
+                       layout_submenu->get_item(i);
+               char layout_text[BCSTRLEN];  layout_text[0] = 0;
+               int n = sz, id = i+1;
+               while( --n >= 0 ) {
+                       char *np = fs.dir_list[n]->name;
+                       char *cp = strrchr(np, '_'), *bp = 0;
+                       int no = strtol(np+6, &bp, 10);
+                       if( no != id || !bp ) continue;
+                       if( bp == cp ) {  n = -1;  break; }
+                       if( *bp++ == '_' && bp < cp && !strcmp(cp, "_rc") ) {
+                               int k = cp - bp;  char *tp = layout_text;
+                               if( k > LAYOUT_NAME_LEN ) k = LAYOUT_NAME_LEN;
+                               while( --k >= 0 ) *tp++ = *bp++;
+                               *tp = 0;
+                               break;
+                       }
+               }
+               strcpy(item->layout_text, layout_text);
+               char *lp = item->layout_file;
+               int k = sprintf(lp, LAYOUT_FILE, id);
+               if( n >= 0 && layout_text[0] )
+                       sprintf(lp + k-2, "%s_rc", layout_text);
+               else
+                       sprintf(layout_text, _("Layout %d"), id);
+               item->set_text(layout_text);
+       }
+}
+
+LoadLayoutDialog::LoadLayoutDialog(LoadLayout *load_layout)
+{
+       this->load_layout = load_layout;
+       wx = 0;  wy = 0;
+       idx = -1;
+       lgui = 0;
+}
+
+LoadLayoutDialog::~LoadLayoutDialog()
+{
+       close_window();
+}
+
+void LoadLayoutDialog::handle_done_event(int result)
+{
+       if( result ) return;
+       char layout_file[BCSTRLEN];
+       BC_SubMenu *layout_submenu = load_layout->get_submenu();
+       LoadLayoutItem* item =
+               (LoadLayoutItem *) layout_submenu->get_item(idx);
+       snprintf(layout_file, sizeof(layout_file), "%s", item->layout_file);
+       load_layout->mwindow->delete_layout(layout_file);
+       int k = sprintf(layout_file, LAYOUT_FILE, idx+1);
+       const char *text = lgui->name_text->get_text();
+       if( text[0] )
+               snprintf(layout_file + k-2, sizeof(layout_file)-k+2, "%s_rc", text);
+       load_layout->mwindow->save_layout(layout_file);
+}
+
+void LoadLayoutDialog::handle_close_event(int result)
+{
+       lgui = 0;
+}
+
+BC_Window *LoadLayoutDialog::new_gui()
+{
+       lgui = new LoadLayoutConfirm(this, wx, wy);
+       lgui->create_objects();
+       return lgui;
+}
+
+void LoadLayoutDialog::start_confirm_dialog(int wx, int wy, int idx)
+{
+       close_window();
+       this->wx = wx;  this->wy = wy;
+       this->idx = idx;
+       start();
+}
+
+LoadLayoutNameText::LoadLayoutNameText(LoadLayoutConfirm *confirm,
+               int x, int y, int w, const char *text)
+ : BC_TextBox(x, y, w, 1, text)
+{
+       this->confirm = confirm;
+}
+
+LoadLayoutNameText::~LoadLayoutNameText()
+{
+}
+
+int LoadLayoutNameText::handle_event()
+{
+       const char *text = get_text();
+       int len = strlen(text), k = 0;
+       char new_text[BCTEXTLEN];
+       for( int i=0; i<len; ++i ) {
+               int ch = text[i];
+               if( (ch>='A' && ch<='Z') || (ch>='a' && ch<='z') ||
+                   (ch>='0' && ch<='9') || ch=='_' )
+                       new_text[k++] = ch;
+       }
+       new_text[k] = 0;  len = k;
+       int i = len - LAYOUT_NAME_LEN;
+       if( i >= 0 ) {
+               k = 0;
+               while( i < len ) new_text[k++] = new_text[i++];
+               new_text[k] = 0;
        }
+       update(new_text);
+       return 1;
+}
+
+LoadLayoutConfirm::LoadLayoutConfirm(LoadLayoutDialog *layout_dialog, int x, int y)
+ : BC_Window(_(PROGRAM_NAME ": Layout"), x, y, xS(300),yS(140), xS(300),yS(140), 0)
+{
+       this->layout_dialog = layout_dialog;
+}
+
+LoadLayoutConfirm::~LoadLayoutConfirm()
+{
+}
+
+void LoadLayoutConfirm::create_objects()
+{
+       int xs10 = xS(10), xs20 = xS(20);
+       int ys10 = yS(10);
+       lock_window("LoadLayoutConfirm::create_objects");
+       int x = xs10, y = ys10;
+       BC_SubMenu *layout_submenu = layout_dialog->load_layout->get_submenu();
+       LoadLayoutItem *item = (LoadLayoutItem *)
+               layout_submenu->get_item(layout_dialog->idx);
+       BC_Title *title;
+       add_subwindow(title = new BC_Title(x, y, _("Layout Name:")));
+       int x1 = x + title->get_w() + xs10;
+       add_subwindow(title = new BC_Title(x1, y, item->get_text()));
+       y += title->get_h() + ys10;
+       add_subwindow(name_text = new LoadLayoutNameText(this,
+               x, y, get_w()-x-xs20, item->layout_text));
+       y += name_text->get_h();
+       x1 = x + xS(80);
+       char legend[BCTEXTLEN];
+       sprintf(legend, _("a-z,A-Z,0-9_ only, %dch max"), LAYOUT_NAME_LEN);
+       add_subwindow(title = new BC_Title(x1, y, legend));
+       add_subwindow(new BC_OKButton(this));
+       add_subwindow(new BC_CancelButton(this));
+       unlock_window();
+}
+
+
+LoadRecentItem::LoadRecentItem(const char *path)
+{
+       this->path = cstrdup(path);
+}
+
+LoadRecentItem::~LoadRecentItem()
+{
+       delete [] path;
+}
+
+int LoadRecentItems::add_load(char *path)
+{
+// test for existing copy
+       FileSystem fs;
+       char name[BCTEXTLEN], text[BCTEXTLEN];
+       fs.extract_name(name, path);
+       int loads_total = size();
+       int ret = 0, k = loads_total;
+       LoadRecentItem *load_item = 0;
+
+       for( int i=0; !ret && i<loads_total; ++i ) {
+               load_item = get(i);
+               fs.extract_name(text, load_item->path);
+               if( strcmp(name, text) ) continue;
+               k = i;  ret = 1; // already exists, move to top
+       }
+       if( !ret ) { // adding a new one
+               while( loads_total >= TOTAL_LOADS )
+                       remove_object_number(--loads_total);
+               insert(new LoadRecentItem(path), 0);
+       }
+       else if( k > 0 ) { // cycle loads
+               while( --k >= 0 ) set(k+1, get(k));
+               set(0, load_item);
+       }
+       return ret;
+}
+
+LoadRecentItems::LoadRecentItems()
+{
+}
+
+LoadRecentItems::~LoadRecentItems()
+{
+       remove_all_objects();
+}
+
+LoadRecent::LoadRecent(MWindow *mwindow, MainMenu *main_menu)
+ : BC_MenuItem(_("Load Recent..."))
+{
+       this->mwindow = mwindow;
+       this->main_menu = main_menu;
+       total_items = 0;
+}
+LoadRecent::~LoadRecent()
+{
+}
+
+void LoadRecent::create_objects()
+{
+       add_submenu(submenu = new LoadRecentSubMenu(this));
+}
+
+LoadPrevious *LoadRecent::get_next_item()
+{
+       int k = total_items++;
+       if( k < submenu->total_items() )
+               return (LoadPrevious *)submenu->get_item(k);
+       LoadPrevious *load_prev = new LoadPrevious(mwindow, main_menu->load_file);
+       submenu->add_item(load_prev);
+       return load_prev;
+}
+
+int LoadRecent::activate_submenu()
+{
+       total_items = 0;
+       scan_items(1);
+       if( total_items > 0 ) {
+               LoadPrevious *load_prev = get_next_item();
+               load_prev->set_text("-");
+               load_prev->set_path("");
+       }
+       scan_items(0);
+       while( total_items < submenu->total_items() )
+               submenu->del_item(0);
+       return BC_MenuItem::activate_submenu();
+}
+
+void LoadRecent::scan_items(int use_xml)
+{
+       FileSystem fs;
+       int loads_total = main_menu->load.size();
+       for( int i=0; i<loads_total; ++i ) {
+               LoadRecentItem *recent = main_menu->load[i];
+               char name[BCTEXTLEN];
+               fs.extract_name(name, recent->path);
+               const char *cp = strrchr(name, '.');
+               if( !cp || strcasecmp(cp+1,"xml") ? use_xml : !use_xml ) continue;
+               LoadPrevious *load_prev = get_next_item();
+               load_prev->set_text(name);
+               load_prev->set_path(recent->path);
+       }
+}
+
+LoadRecentSubMenu::LoadRecentSubMenu(LoadRecent *load_recent)
+ : BC_SubMenu()
+{
+       this->load_recent = load_recent;
+}
+
+LoadRecentSubMenu::~LoadRecentSubMenu()
+{
 }