2

On this question I have been attempting to capture a AUI pane configuration so that it can be restored if any panes have been closed. The docs are somewhat limited for wxPHP, and upstream for wxWidgets, so I am largely feeling my way around.

I have realised that SavePaneInfo will help me capture the state of a pane - it outputs a perspective string that represents the position and options for a pane at a given moment. All I need to do therefore is to capture when the pane changes and update my internal representation of it.

For the sake of interest, a perspective looks like this:

name=auiPane3;caption=Caption 3;state=2099196;dir=3;layer=0;row=0;pos=1;prop=100000;bestw=90;besth=25;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1

However, capturing a move/dock event is not proving to be trivial. I can see six events connected with AUI:

wxEVT_AUI_FIND_MANAGER
wxEVT_AUI_PANE_BUTTON
wxEVT_AUI_PANE_CLOSE
wxEVT_AUI_PANE_MAXIMISE
wxEVT_AUI_PANE_RESTORE
wxEVT_AUI_PANE_RENDER

I have been able to capture the restore and close events, and the find_manager doesn't seem to do anything. I've tried wxEVT_ANY on this window, which also does not seem to capture anything. I've also tried it on individual panes too, to no avail (nothing is called as far as I can tell):

$managedWindow->getWindowByIndex(0)->Connect(wxEVT_ANY, array($this, "onAny"));

The docs for the upstream library wxWidgets mention this event:

EVT_AUI_PANE_ACTIVATED

However that does not seem to be implemented in wxPHP - is that what I want? It does not quite sound right, but if I can access it without the constant I would certainly try it.

I guess I could use wxAuiManager::SetArtProvider with the standard art provider object, modified to capture pane state, but that feels like a sledgehammer to crack a nut. I could also capture the close event and change the perspective string returned so the 'closed' bit is not set, but that too is not particularly elegant.

What I want to do feels really trivial, and would be in keeping with other parts of wxWidgets, but it is not so. Any suggestions for things to try?

halfer
  • 19,824
  • 17
  • 99
  • 186
  • It's not really clear what exactly are you trying to do. Typically you save the perspective on program shutdown and restore it on the (next) startup. Do you want to save it automatically after every layout change? If so, why (and are you sure it's a good idea)? – VZ. Jan 16 '16 at 19:29
  • @VZ.: users can close some panes, and I want them to be able to re-open the panes in the last place they were docked or floated. I have a button on another window for this purpose. – halfer Jan 17 '16 at 12:30
  • Saving the perspective on `wxEVT_AUI_PANE_CLOSE` should be enough then (notice that you can also veto this event to prevent the pane from being closed if needed). – VZ. Jan 17 '16 at 12:58
  • @VZ.: I am doing that already: when closing a pane I capture the perspective string. However when I load perspectives not all panes are restored, which makes me wonder if sometimes the `state` bitfield contains an "is open/closed" bit that is not correctly set (by virtue of the pane having been closed). That's why I wondered if I needed to modify the perspective before using it - otherwise I might be restoring it to a closed state. – halfer Jan 17 '16 at 13:03
  • I wonder if the next thing for me to debug is to see if there is a particular pane (or pane type) that does not re-open - there may be another bug that prevents it. Thanks for your reply. – halfer Jan 17 '16 at 13:05
  • Gah! I am very close. I think the reason why some of my pane are not reopening is that I am saving all perspectives when anything is closed. Thus if something is closed already I am re-saving it in a closed state. The solution ought to be to only save perspectives for panes that close, but as far as I can tell there is no way to determine from the `wxAuiManagerEvent` what pane has closed. The `wxAuiPaneInfo` can be retrieved, but the `Name` property is not visible. – halfer Jan 17 '16 at 15:04

2 Answers2

2

I have a solution. I would have liked to detect from the wxAuiManagerEvent which pane is closing, so that I just record the perspective string of a pane as it closes. However this does not seem to be possible:

  • The reference from $event->GetEventObject() is NULL - that may be a wxPHP bug;
  • The pane returned by $event->GetPane() does not have a property or method to read the name of the pane.

I have therefore taken the approach of saving all perspective strings whenever one pane is closed.

I discovered that perspective strings contain a bit to represent the closed status of a pane, so when storing these strings I make sure this bit is unset. Reassembling perspective strings is not the most elegant thing, but it works, and is much better than undocking and redocking (see the linked question in the original post).

Here is some code that loops through my panes, gets the perspective string, unsets the closed flag and saves the perspective in a window list:

public function onPaneClose(wxAuiManagerEvent $event)
{
    for($i = 0; $i <= 7; $i++)
    {
        $pi = $this->getPaneInfoByIndex($i);
        $persp = $this->getManagedWindow()->getAuiManager()->SavePaneInfo($pi);

        // Split perspective string into pieces, get the second one (state)
        $items = explode(';', $persp);
        $state = $items[2];

        // Decode the bitfield within
        $stateItems = explode('=', $state);
        $stateBitfield = (int) $stateItems[1];

        // Set up bitmask to ignore closed state
        $bitMask = (-1 ^ 2);

        // Reset the perspective string minus the closed state bit
        $replacementBitfield = $stateBitfield & $bitMask;
        $items[2] = "state=" . $replacementBitfield;
        $newPersp = implode(';', $items);

        // Finally save the perspective
        $this->windowSaves[$i] = $newPersp;
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
0

I've found another solution, which I think I moderately prefer. It turns out it is possible to get the pane name from the wxAuiPaneInfo object - the perspective contains it! This allows me to simplify the algorithm - I just convert the name to an ordinal, and then save pane perspectives individually.

Since pane close events are always triggered before the close (i.e. when they are still vetoable) they will not have the close bit set, and so happily I do not have to modify that. Here's my new event handler:

public function onPaneClose(wxAuiManagerEvent $event)
{
    // In the absence of being able to read the pane name from a paneinfo
    // method, we can parse it out from the perpective string
    $info = $event->GetPane();
    $persp = $this->getManagedWindow()->getAuiManager()->SavePaneInfo($info);

    // Fish out the number, which represents the pane ordinal
    $matches = [];
    preg_match('#name=auiPane(\d+)#', $persp, $matches);
    if ($matches)
    {
        $index = $matches[1];
        $this->windowSaves[$index] = $persp;
    }
}

I've just used a regex on the perspective string that matches my naming format of auiPane<index>.

halfer
  • 19,824
  • 17
  • 99
  • 186
  • This looks like a nice solution. – VZ. Jan 18 '16 at 13:39
  • Thanks @VZ. - my preference would have been to do `$event->GetPane()->GetName()` to save on the home-made string parsing, but that is not available (at least in wxPHP, I don't know about other bindings). – halfer Jan 18 '16 at 15:57
  • At least in C++ `name` is just a public variable in this class (yes, this is pretty strange and unusual...). – VZ. Jan 19 '16 at 00:55
  • Thanks @VZ.. I tried that in the PHP object (`$event->GetPane()->Name`) to no avail - I might flag it as a bug. – halfer Jan 19 '16 at 08:13