diff --git a/.gitignore b/.gitignore index d31369c..4360b5d 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,10 @@ ipch/ *.log rpl4.log +# Where the exploded view's panes were last dragged to (RP412MFDLAYOUT). +# Per-machine by nature - it is screen coordinates on somebody's desk. +mfd_layout.cfg + # Build-output static libs that land in lib/ (the two committed dependency # libs, OpenAL32.lib and libsndfile-1.lib, stay tracked). /lib/Munga_L4.lib diff --git a/MUNGA_L4/L4MFDVIEW.cpp b/MUNGA_L4/L4MFDVIEW.cpp index a762ef7..03bae49 100644 --- a/MUNGA_L4/L4MFDVIEW.cpp +++ b/MUNGA_L4/L4MFDVIEW.cpp @@ -10,6 +10,52 @@ namespace const int buttonGap = 4; + //--------------------------------------------------------------- + // Sticky placement for the exploded view. See L4MFDVIEW.h; ported + // from BT411's BT_GLASS_LAYOUT. + //--------------------------------------------------------------- + enum { LayoutOff = 0, LayoutLoad, LayoutSave }; + + const char layoutFileName[] = "mfd_layout.cfg"; + + // the panes that own a desktop window, so Save can walk them + enum { maxLayoutPanes = 8 }; + MFDSplitView *layoutPanes[maxLayoutPanes]; + int layoutPaneCount = 0; + + int LayoutMode() + { + static int mode = -1; + if (mode < 0) + { + const char *setting = getenv("RP412MFDLAYOUT"); + if (setting == NULL || setting[0] == '\0') + { + mode = LayoutOff; + } + else if (!stricmp(setting, "off") || !stricmp(setting, "0")) + { + mode = LayoutOff; + } + else if (!stricmp(setting, "load") || !stricmp(setting, "restore")) + { + mode = LayoutLoad; + } + else // save / adjust / on / 1 + { + mode = LayoutSave; + } + + if (mode != LayoutOff) + { + DEBUG_STREAM << "MFDLayout: RP412MFDLAYOUT=" + << ((mode == LayoutSave) ? "save" : "load") + << " (" << layoutFileName << ")\n" << std::flush; + } + } + return mode; + } + //--------------------------------------------------------------- // Button banks run UNDER the glass: a button reaches this far in // from the display edge, but only the indicator strip clears that @@ -108,6 +154,15 @@ namespace } break; + case WM_EXITSIZEMOVE: + // + // A drag just finished. Write the whole arrangement now rather + // than trusting teardown, so a hard kill still leaves the + // layout that was on screen. No-op unless the mode is save. + // + MFDSplitView_SaveLayout(); + return 0; + case WM_CLOSE: // Part of the cockpit; just hide it. ShowWindow(hwnd, SW_HIDE); @@ -148,6 +203,8 @@ MFDSplitView::MFDSplitView( sourceHeight = source_height; buttonCount = 0; pressedIndex = -1; + paneTitle = title; + ownWindow = (parent == NULL) ? True : False; pixels = new unsigned long[source_width * source_height]; memset(pixels, 0, source_width * source_height * sizeof(unsigned long)); @@ -230,6 +287,13 @@ MFDSplitView::MFDSplitView( { SetWindowLongPtrA((HWND) window, GWLP_USERDATA, (LONG_PTR) this); ShowWindow((HWND) window, SW_SHOWNOACTIVATE); + + // only the exploded view's own windows can be dragged, so only + // those have a position worth remembering + if (ownWindow && layoutPaneCount < maxLayoutPanes) + { + layoutPanes[layoutPaneCount++] = this; + } } else { @@ -241,6 +305,17 @@ MFDSplitView::~MFDSplitView() { Check_Pointer(this); + // out of the layout registry before the window goes + for (int i = 0; i < layoutPaneCount; ++i) + { + if (layoutPanes[i] == this) + { + layoutPanes[i] = layoutPanes[--layoutPaneCount]; + layoutPanes[layoutPaneCount] = NULL; + break; + } + } + if (window != NULL) { SetWindowLongPtrA((HWND) window, GWLP_USERDATA, 0); @@ -259,6 +334,139 @@ Logical return pixels != NULL; } +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Sticky placement: restore saved positions over the computed ones. +// +// Called after the exploded view has built and placed every pane, so a +// pane the file does not mention simply keeps where the layout put it. +// Position only - the saved size is read and discarded, because a pane's +// size comes from its content and its button banks, and letting an old +// w,h back in would misshape it after any geometry change. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void + MFDSplitView_LoadLayout() +{ + if (LayoutMode() == LayoutOff || layoutPaneCount == 0) + { + return; + } + + FILE *file = fopen(layoutFileName, "rt"); + if (file == NULL) + { + DEBUG_STREAM << "MFDLayout: no " << layoutFileName + << " yet (using the computed arrangement)\n" << std::flush; + return; + } + + int restored = 0; + char line[256]; + while (fgets(line, sizeof(line), file) != NULL) + { + char *cursor = line; + while (*cursor == ' ' || *cursor == '\t') + { + ++cursor; + } + if (*cursor == '#' || *cursor == '\r' || *cursor == '\n' || *cursor == '\0') + { + continue; + } + + // titles carry no '=', so the first one splits the line + char *equals = strchr(cursor, '='); + if (equals == NULL) + { + continue; + } + *equals = '\0'; + char *end = equals; + while (end > cursor && (end[-1] == ' ' || end[-1] == '\t')) + { + --end; + } + *end = '\0'; + + int x = 0, y = 0, w = 0, h = 0; + if (sscanf(equals + 1, "%d,%d,%d,%d", &x, &y, &w, &h) < 2) + { + continue; + } + + for (int i = 0; i < layoutPaneCount; ++i) + { + MFDSplitView *pane = layoutPanes[i]; + if (pane != NULL && pane->Title() != NULL && + !strcmp(pane->Title(), cursor)) + { + pane->SetPosition(x, y); + ++restored; + break; + } + } + } + fclose(file); + + DEBUG_STREAM << "MFDLayout: restored " << restored << " pane position(s) from " + << layoutFileName << "\n" << std::flush; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Write where every open pane currently is. The whole file each time - it +// is a handful of lines, and a rewrite leaves nothing half-written for a +// hard kill to catch. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void + MFDSplitView_SaveLayout() +{ + if (LayoutMode() != LayoutSave || layoutPaneCount == 0) + { + return; + } + + FILE *file = fopen(layoutFileName, "wt"); + if (file == NULL) + { + DEBUG_STREAM << "MFDLayout: could not write " << layoutFileName + << "\n" << std::flush; + return; + } + + fputs("# RP412 exploded-view pane placement. RP412MFDLAYOUT=save writes\n" + "# this on each finished drag and on exit; =load restores it.\n" + "#