source: git/src/aventreectrl.cc @ 76063fc

warn-only-for-hanging-survey
Last change on this file since 76063fc was fccf718, checked in by Olly Betts <olly@…>, 2 months ago

Enable more show and hide actions in survey tree

  • Property mode set to 100644
File size: 17.9 KB
RevLine 
[f17e6dc6]1//
2//  aventreectrl.cc
3//
4//  Tree control used for the survey tree.
5//
6//  Copyright (C) 2001, Mark R. Shinwell.
[672459c]7//  Copyright (C) 2001-2003,2005,2006,2016,2018 Olly Betts
[887c26e]8//  Copyright (C) 2005 Martin Green
[f17e6dc6]9//
10//  This program is free software; you can redistribute it and/or modify
11//  it under the terms of the GNU General Public License as published by
12//  the Free Software Foundation; either version 2 of the License, or
13//  (at your option) any later version.
14//
15//  This program is distributed in the hope that it will be useful,
16//  but WITHOUT ANY WARRANTY; without even the implied warranty of
17//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18//  GNU General Public License for more details.
19//
20//  You should have received a copy of the GNU General Public License
21//  along with this program; if not, write to the Free Software
[ecbc6c18]22//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
[f17e6dc6]23//
24
[cbfa50d]25#include <config.h>
26
[f17e6dc6]27#include "aventreectrl.h"
28#include "mainfrm.h"
[81d2ef0]29#include "osalloc.h"
[f17e6dc6]30
[17a38ded]31#include <stack>
32
33using namespace std;
34
[44e007d]35// STATE_BLANK is used for stations which are siblings of surveys which have
36// select checkboxes.
37enum { STATE_BLANK = 0, STATE_OFF, STATE_ON };
[672459c]38
39/* XPM */
[44e007d]40static const char *blank_xpm[] = {
41/* columns rows colors chars-per-pixel */
42"15 15 1 1",
43"  c None",
44/* pixels */
45"               ",
46"               ",
47"               ",
48"               ",
49"               ",
50"               ",
51"               ",
52"               ",
53"               ",
54"               ",
55"               ",
56"               ",
57"               ",
58"               ",
59"               "
60};
61
62/* XPM */
63static const char *off_xpm[] = {
[672459c]64/* columns rows colors chars-per-pixel */
[c78d5b4]65"15 15 2 1",
66". c #000000",
[672459c]67"  c None",
68/* pixels */
69"               ",
70"               ",
[c78d5b4]71" ............  ",
72" .          .  ",
73" .          .  ",
74" .          .  ",
75" .          .  ",
76" .          .  ",
77" .          .  ",
78" .          .  ",
79" .          .  ",
80" .          .  ",
81" .          .  ",
82" ............  ",
[672459c]83"               "
84};
85
86/* XPM */
[44e007d]87static const char *on_xpm[] = {
[672459c]88/* columns rows colors chars-per-pixel */
[c78d5b4]89"15 15 3 1",
90". c #000000",
[672459c]91"X c #007F28",
92"  c None",
93/* pixels */
94"               ",
[c78d5b4]95"               ",
96" ............XX",
97" .          XXX",
98" .         XXXX",
99" .        XXXX ",
100" .       XXXX  ",
101" .      XXXX.  ",
102" . XX  XXXX .  ",
103" . XXXXXXX  .  ",
104" .  XXXXX   .  ",
105" .   XXX    .  ",
106" .    X     .  ",
107" ............  ",
[672459c]108"               "
109};
110
[f17e6dc6]111BEGIN_EVENT_TABLE(AvenTreeCtrl, wxTreeCtrl)
112    EVT_MOTION(AvenTreeCtrl::OnMouseMove)
[887c26e]113    EVT_LEAVE_WINDOW(AvenTreeCtrl::OnLeaveWindow)
[6969b17]114    EVT_TREE_SEL_CHANGED(wxID_ANY, AvenTreeCtrl::OnSelChanged)
115    EVT_TREE_ITEM_ACTIVATED(wxID_ANY, AvenTreeCtrl::OnItemActivated)
[5901b62]116    EVT_CHAR(AvenTreeCtrl::OnKeyPress)
[6969b17]117    EVT_TREE_ITEM_MENU(wxID_ANY, AvenTreeCtrl::OnMenu)
[d5fd4f4]118    EVT_MENU(menu_SURVEY_SHOW_ALL, AvenTreeCtrl::OnRestrict)
[5e0b9f9d]119    EVT_MENU(menu_SURVEY_RESTRICT, AvenTreeCtrl::OnRestrict)
[672459c]120    EVT_MENU(menu_SURVEY_HIDE, AvenTreeCtrl::OnHide)
121    EVT_MENU(menu_SURVEY_SHOW, AvenTreeCtrl::OnShow)
122    EVT_MENU(menu_SURVEY_HIDE_SIBLINGS, AvenTreeCtrl::OnHideSiblings)
[6969b17]123    EVT_TREE_STATE_IMAGE_CLICK(wxID_ANY, AvenTreeCtrl::OnStateClick)
[f17e6dc6]124END_EVENT_TABLE()
125
126AvenTreeCtrl::AvenTreeCtrl(MainFrm* parent, wxWindow* window_parent) :
[51eab3b]127    wxTreeCtrl(window_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
128               wxTR_DEFAULT_STYLE | wxTR_HIDE_ROOT),
[b623b3e]129    m_Parent(parent),
[b4fe9fb]130    m_Enabled(false),
[3eddcaf4]131    m_LastItem(),
[efb30c4]132    m_BackgroundColour(),
[5e0b9f9d]133    m_SelValid(false),
134    menu_data(NULL)
[f17e6dc6]135{
[672459c]136    wxImageList* img_list = new wxImageList(15, 15, 2);
[44e007d]137    img_list->Add(wxBitmap(blank_xpm));
138    img_list->Add(wxBitmap(off_xpm));
139    img_list->Add(wxBitmap(on_xpm));
[672459c]140    AssignStateImageList(img_list);
[f17e6dc6]141}
142
[17a38ded]143void AvenTreeCtrl::FillTree(const wxString& root_name)
144{
145    Freeze();
146    m_Enabled = false;
147    m_LastItem = wxTreeItemId();
148    m_SelValid = false;
149    DeleteAllItems();
150
151    const wxChar separator = m_Parent->GetSeparator();
152    filter.clear();
153    filter.SetSeparator(separator);
154
[51eab3b]155    // Create the (hidden) real root of the wxTreeCtrl.
156    wxTreeItemId treeroot = AddRoot(wxString());
157
158    // Create the root of the survey tree.
159    wxTreeItemId surveyroot = AppendItem(treeroot, root_name);
[17a38ded]160
161    // Fill the tree of stations and prefixes.
162    stack<wxTreeItemId> previous_ids;
163    wxString current_prefix;
[51eab3b]164    wxTreeItemId current_id = surveyroot;
[17a38ded]165
166    list<LabelInfo*>::const_iterator pos = m_Parent->GetLabels();
167    while (pos != m_Parent->GetLabelsEnd()) {
168        LabelInfo* label = *pos++;
169
170        if (label->IsAnon()) continue;
171
172        // Determine the current prefix.
173        wxString prefix = label->GetText().BeforeLast(separator);
174
175        // Determine if we're still on the same prefix.
176        if (prefix == current_prefix) {
177            // no need to fiddle with branches...
178        }
179        // If not, then see if we've descended to a new prefix.
180        else if (prefix.length() > current_prefix.length() &&
181                 prefix.StartsWith(current_prefix) &&
182                 (prefix[current_prefix.length()] == separator ||
183                  current_prefix.empty())) {
184            // We have, so start as many new branches as required.
185            int current_prefix_length = current_prefix.length();
186            current_prefix = prefix;
187            size_t next_dot = current_prefix_length;
188            if (!next_dot) --next_dot;
189            do {
190                size_t prev_dot = next_dot + 1;
191
192                // Extract the next bit of prefix.
193                next_dot = prefix.find(separator, prev_dot + 1);
194
195                wxString bit = prefix.substr(prev_dot, next_dot - prev_dot);
[ed7f3fc]196                // Sigh, therion can produce files with empty components in
197                // station names!
198                // assert(!bit.empty());
[17a38ded]199
200                // Add the current tree ID to the stack.
201                previous_ids.push(current_id);
202
203                // Append the new item to the tree and set this as the current branch.
204                current_id = AppendItem(current_id, bit);
205                SetItemData(current_id, new TreeData(prefix.substr(0, next_dot)));
206            } while (next_dot != wxString::npos);
207        }
208        // Otherwise, we must have moved up, and possibly then down again.
209        else {
210            size_t count = 0;
211            bool ascent_only = (prefix.length() < current_prefix.length() &&
212                                current_prefix.StartsWith(prefix) &&
213                                (current_prefix[prefix.length()] == separator ||
214                                 prefix.empty()));
215            if (!ascent_only) {
216                // Find out how much of the current prefix and the new prefix
217                // are the same.
218                // Note that we require a match of a whole number of parts
219                // between dots!
220                size_t n = min(prefix.length(), current_prefix.length());
221                size_t i;
222                for (i = 0; i < n && prefix[i] == current_prefix[i]; ++i) {
223                    if (prefix[i] == separator) count = i + 1;
224                }
225            } else {
226                count = prefix.length() + 1;
227            }
228
229            // Extract the part of the current prefix after the bit (if any)
230            // which has matched.
231            // This gives the prefixes to ascend over.
232            wxString prefixes_ascended = current_prefix.substr(count);
233
234            // Count the number of prefixes to ascend over.
235            int num_prefixes = prefixes_ascended.Freq(separator);
236
237            // Reverse up over these prefixes.
238            for (int i = 1; i <= num_prefixes; i++) {
239                previous_ids.pop();
240            }
241            current_id = previous_ids.top();
242            previous_ids.pop();
243
244            if (!ascent_only) {
245                // Add branches for this new part.
246                size_t next_dot = count - 1;
247                do {
248                    size_t prev_dot = next_dot + 1;
249
250                    // Extract the next bit of prefix.
251                    next_dot = prefix.find(separator, prev_dot + 1);
252
253                    wxString bit = prefix.substr(prev_dot, next_dot - prev_dot);
[ed7f3fc]254                    // Sigh, therion can produce files with empty components in
255                    // station names!
256                    // assert(!bit.empty());
[17a38ded]257
258                    // Add the current tree ID to the stack.
259                    previous_ids.push(current_id);
260
261                    // Append the new item to the tree and set this as the current branch.
262                    current_id = AppendItem(current_id, bit);
263                    SetItemData(current_id, new TreeData(prefix.substr(0, next_dot)));
264                } while (next_dot != wxString::npos);
265            }
266
267            current_prefix = prefix;
268        }
269
270        // Now add the leaf.
271        wxString bit = label->GetText().AfterLast(separator);
[ed7f3fc]272        // Sigh, therion can produce files with empty components in station
273        // names!
274        // assert(!bit.empty());
[17a38ded]275        wxTreeItemId id = AppendItem(current_id, bit);
276        SetItemData(id, new TreeData(label));
277        label->tree_id = id;
278        // Set the colour for an item in the survey tree.
279        if (label->IsEntrance()) {
280            // Entrances are green (like entrance blobs).
281            SetItemTextColour(id, wxColour(0, 255, 40));
282        } else if (label->IsSurface()) {
283            // Surface stations are dark green.
284            SetItemTextColour(id, wxColour(49, 158, 79));
285        }
286    }
287
[51eab3b]288    Expand(surveyroot);
[17a38ded]289    m_Enabled = true;
290    Thaw();
291}
292
[9e8e1d6]293constexpr auto TREE_MASK = wxTREE_HITTEST_ONITEMLABEL |
294                           wxTREE_HITTEST_ONITEMRIGHT |
295                           wxTREE_HITTEST_ONITEMSTATEICON;
[97dd0d2]296
[f17e6dc6]297void AvenTreeCtrl::OnMouseMove(wxMouseEvent& event)
298{
[0ae40fa]299    if (!m_Enabled || m_Parent->Animating())
300        return;
301
302    int flags;
303    wxTreeItemId pos = HitTest(event.GetPosition(), flags);
304    if (!(flags & TREE_MASK)) {
305        pos = wxTreeItemId();
306    }
307    if (pos == m_LastItem) return;
308    if (pos.IsOk()) {
[0642381]309        const TreeData* data = static_cast<const TreeData*>(GetItemData(pos));
310        m_Parent->DisplayTreeInfo(data);
311        if (data && !data->IsStation()) {
312            // For stations, MainFrm calls back to SetHere(), but for surveys
313            // we need to do that ourselves.
314            SetHere(pos);
315        }
[0ae40fa]316    } else {
317        m_Parent->DisplayTreeInfo();
[f17e6dc6]318    }
319}
320
[570d62c3]321void AvenTreeCtrl::SetHere(wxTreeItemId pos)
322{
323    if (pos == m_LastItem) return;
324
325    if (m_LastItem.IsOk()) {
326        SetItemBackgroundColour(m_LastItem, m_BackgroundColour);
327    }
328    if (pos.IsOk()) {
329        m_BackgroundColour = GetItemBackgroundColour(pos);
330        SetItemBackgroundColour(pos, wxColour(180, 180, 180));
331    }
332    m_LastItem = pos;
333}
334
[887c26e]335void AvenTreeCtrl::OnLeaveWindow(wxMouseEvent&)
336{
337    if (m_LastItem.IsOk()) {
338        SetItemBackgroundColour(m_LastItem, m_BackgroundColour);
339        m_LastItem = wxTreeItemId();
340    }
[381ae6e]341    m_Parent->DisplayTreeInfo();
[887c26e]342}
343
[5dae1ba]344void AvenTreeCtrl::OnSelChanged(wxTreeEvent&)
[f17e6dc6]345{
[b623b3e]346    m_SelValid = true;
[f17e6dc6]347}
348
[44ed489]349void AvenTreeCtrl::OnItemActivated(wxTreeEvent& e)
350{
[4dc54fa]351    if (!m_Enabled) return;
352
353    m_Parent->TreeItemSelected(GetItemData(e.GetItem()));
[44ed489]354}
355
[5e0b9f9d]356void AvenTreeCtrl::OnMenu(wxTreeEvent& e)
357{
[0ae40fa]358    if (!m_Enabled) return;
359
360    const TreeData* data = static_cast<const TreeData*>(GetItemData(e.GetItem()));
361    menu_data = data;
362    menu_item = e.GetItem();
363    if (!data) {
[51eab3b]364        // Survey tree root:
[0ae40fa]365        wxMenu menu;
366        /* TRANSLATORS: In aven's survey tree, right-clicking on the root
367         * gives a pop-up menu and this is an option (but only enabled if
368         * the view is restricted to a subsurvey). It reloads the current
369         * survey file with the who survey visible.
370         */
371        menu.Append(menu_SURVEY_SHOW_ALL, wmsg(/*Show all*/245));
372        if (m_Parent->GetSurvey().empty())
373            menu.Enable(menu_SURVEY_SHOW_ALL, false);
374        PopupMenu(&menu);
[51eab3b]375    } else if (data->IsStation()) {
[0ae40fa]376        // Station: name is data->GetLabel()->GetText()
[51eab3b]377    } else if (ItemHasChildren(menu_item)) {
[0ae40fa]378        // Survey:
379        wxMenu menu;
380        /* TRANSLATORS: In aven's survey tree, right-clicking on a survey
381         * name gives a pop-up menu and this is an option.  It reloads the
382         * current survey file with the view restricted to the survey
383         * clicked upon.
384         */
385        menu.Append(menu_SURVEY_RESTRICT, wmsg(/*Hide others*/246));
386        menu.AppendSeparator();
[fccf718]387        menu.Append(menu_SURVEY_HIDE, wmsg(/*&Hide*/407));
[0ae40fa]388        menu.Append(menu_SURVEY_SHOW, wmsg(/*&Show*/409));
[fccf718]389        menu.Append(menu_SURVEY_HIDE_SIBLINGS, wmsg(/*Hide si&blings*/388));
[0ae40fa]390        switch (GetItemState(menu_item)) {
[44e007d]391            case STATE_ON: // Currently shown.
[0ae40fa]392                menu.Enable(menu_SURVEY_SHOW, false);
393                break;
[44e007d]394            case STATE_OFF:
[0ae40fa]395                menu.Enable(menu_SURVEY_HIDE, false);
396                break;
[5e0b9f9d]397        }
[0ae40fa]398        PopupMenu(&menu);
[51eab3b]399    } else {
400        // Overlay - FIXME: menu here?
[5e0b9f9d]401    }
[0ae40fa]402    menu_data = NULL;
403    e.Skip();
[5e0b9f9d]404}
405
[570d62c3]406bool AvenTreeCtrl::GetSelectionData(wxTreeItemData** data) const
[f17e6dc6]407{
408    assert(m_Enabled);
[26fac5a]409    assert(data);
[b623b3e]410
411    if (!m_SelValid) {
[421b7d2]412        return false;
[b623b3e]413    }
[421b7d2]414
[f17e6dc6]415    wxTreeItemId id = GetSelection();
416    if (id.IsOk()) {
[421b7d2]417        *data = GetItemData(id);
[f17e6dc6]418    }
419
[2d9ed8ad]420    return id.IsOk() && *data;
[f17e6dc6]421}
[b623b3e]422
423void AvenTreeCtrl::UnselectAll()
424{
425    m_SelValid = false;
426    wxTreeCtrl::UnselectAll();
427}
428
[5901b62]429void AvenTreeCtrl::OnKeyPress(wxKeyEvent &e)
430{
[a6e8d45]431    switch (e.GetKeyCode()) {
[26fac5a]432        case WXK_ESCAPE:
433            m_Parent->ClearTreeSelection();
434            break;
435        case WXK_RETURN: {
436            wxTreeItemId id = GetSelection();
437            if (id.IsOk()) {
438                if (ItemHasChildren(id)) {
439                    // If on a branch, expand/contract it.
440                    if (IsExpanded(id)) {
441                        Collapse(id);
442                    } else {
443                        Expand(id);
444                    }
445                } else {
[672459c]446                    // If on a station, centre on it by selecting it twice.
447                    m_Parent->TreeItemSelected(GetItemData(id));
448                    m_Parent->TreeItemSelected(GetItemData(id));
[26fac5a]449                }
450            }
451            break;
452        }
453        case WXK_LEFT: case WXK_RIGHT: case WXK_UP: case WXK_DOWN:
454        case WXK_HOME: case WXK_END: case WXK_PAGEUP: case WXK_PAGEDOWN:
455            e.Skip();
456            break;
457        default:
458            // Pass key event to MainFrm which will pass to GfxCore which will
459            // pass to GUIControl.
460            m_Parent->OnKeyPress(e);
461            break;
[5901b62]462    }
463}
[5e0b9f9d]464
[5dae1ba]465void AvenTreeCtrl::OnRestrict(wxCommandEvent&)
[5e0b9f9d]466{
[51eab3b]467    m_Parent->RestrictTo(menu_data && menu_data->IsSurvey() ? menu_data->GetSurvey() : wxString());
468    // FIXME: Overlays
[672459c]469}
470
[5dae1ba]471void AvenTreeCtrl::OnHide(wxCommandEvent&)
[672459c]472{
473    // Shouldn't be available for the root item.
474    wxASSERT(menu_data);
475    // Hide should be disabled unless the item is explicitly shown.
[44e007d]476    wxASSERT(GetItemState(menu_item) == STATE_ON);
477    SetItemState(menu_item, STATE_OFF);
[51eab3b]478    // FIXME: Overlays?
[1a46879]479    filter.remove(menu_data->GetSurvey());
[672459c]480#if 0
481    Freeze();
482    // Show siblings if not already shown or hidden.
483    wxTreeItemId i = menu_item;
484    while ((i = GetPrevSibling(i)).IsOk()) {
485        if (GetItemState(i) == wxTREE_ITEMSTATE_NONE)
486            SetItemState(i, 1);
487    }
488    i = menu_item;
489    while ((i = GetNextSibling(i)).IsOk()) {
490        if (GetItemState(i) == wxTREE_ITEMSTATE_NONE)
491            SetItemState(i, 1);
492    }
493    Thaw();
494#endif
495    m_Parent->ForceFullRedraw();
496}
497
[5dae1ba]498void AvenTreeCtrl::OnShow(wxCommandEvent&)
[672459c]499{
500    // Shouldn't be available for the root item.
501    wxASSERT(menu_data);
[44e007d]502    auto old_state = GetItemState(menu_item);
[672459c]503    // Show should be disabled for an explicitly shown item.
[44e007d]504    wxASSERT(old_state != STATE_ON);
[672459c]505    Freeze();
[44e007d]506    SetItemState(menu_item, STATE_ON);
[51eab3b]507    // FIXME: Overlays?
[1a46879]508    filter.add(menu_data->GetSurvey());
[44e007d]509    if (old_state == wxTREE_ITEMSTATE_NONE) {
510        // Hide siblings if not already shown or hidden.
511        wxTreeItemId i = menu_item;
512        while ((i = GetPrevSibling(i)).IsOk()) {
513            if (GetItemState(i) == wxTREE_ITEMSTATE_NONE) {
514                const TreeData* data = static_cast<const TreeData*>(GetItemData(i));
515                SetItemState(i, data->IsStation() ? STATE_BLANK : STATE_OFF);
516            }
517        }
518        i = menu_item;
519        while ((i = GetNextSibling(i)).IsOk()) {
520            if (GetItemState(i) == wxTREE_ITEMSTATE_NONE) {
521                const TreeData* data = static_cast<const TreeData*>(GetItemData(i));
522                SetItemState(i, data->IsStation() ? STATE_BLANK : STATE_OFF);
523            }
524        }
[672459c]525    }
526    Thaw();
527    m_Parent->ForceFullRedraw();
528}
529
[5dae1ba]530void AvenTreeCtrl::OnHideSiblings(wxCommandEvent&)
[672459c]531{
532    // Shouldn't be available for the root item.
533    wxASSERT(menu_data);
534    Freeze();
[51eab3b]535    // FIXME: Overlays?
[44e007d]536    SetItemState(menu_item, STATE_ON);
[1a46879]537    filter.add(menu_data->GetSurvey());
[672459c]538
539    wxTreeItemId i = menu_item;
540    while ((i = GetPrevSibling(i)).IsOk()) {
541        const TreeData* data = static_cast<const TreeData*>(GetItemData(i));
[1a46879]542        filter.remove(data->GetSurvey());
[44e007d]543        SetItemState(i, data->IsStation() ? STATE_BLANK : STATE_OFF);
[672459c]544    }
545    i = menu_item;
546    while ((i = GetNextSibling(i)).IsOk()) {
547        const TreeData* data = static_cast<const TreeData*>(GetItemData(i));
[1a46879]548        filter.remove(data->GetSurvey());
[44e007d]549        SetItemState(i, data->IsStation() ? STATE_BLANK : STATE_OFF);
[672459c]550    }
551    Thaw();
552    m_Parent->ForceFullRedraw();
553}
554
555void AvenTreeCtrl::OnStateClick(wxTreeEvent& e)
556{
557    auto item = e.GetItem();
558    const TreeData* data = static_cast<const TreeData*>(GetItemData(item));
[44e007d]559    switch (GetItemState(item)) {
560        case STATE_BLANK:
561            // Click on blank state icon for a station - let the tree handle
562            // this in the same way as a click on the label.
563            return;
564        case STATE_ON:
[51eab3b]565            if (!ItemHasChildren(item)) {
566                // Overlay.
567                m_Parent->InvalidateOverlays();
568            } else {
569                // Survey.
570                if (data) filter.remove(data->GetSurvey());
571            }
[44e007d]572            SetItemState(item, STATE_OFF);
573            break;
574        case STATE_OFF:
[51eab3b]575            if (!ItemHasChildren(item)) {
576                // Overlay.
577                m_Parent->InvalidateOverlays();
578            } else {
579                // Survey.
580                if (data) filter.add(data->GetSurvey());
581            }
[44e007d]582            SetItemState(item, STATE_ON);
583            break;
[672459c]584    }
[5e0b9f9d]585    e.Skip();
[672459c]586    m_Parent->ForceFullRedraw();
587}
[51eab3b]588
589void AvenTreeCtrl::AddOverlay(const wxString& file)
590{
[81d2ef0]591    char* leaf = leaf_from_fnm(file.utf8_str());
592    auto id = AppendItem(GetRootItem(), leaf);
593    osfree(leaf);
[51eab3b]594    SetItemState(id, STATE_ON);
595    SetItemData(id, new TreeData(file));
596}
597
598void AvenTreeCtrl::RemoveOverlay(const wxString& file)
599{
600    // If we add an overlay but fail to load it and remove it again, the
601    // overlay will be the last one, so search from the last one back.
602    for (auto item = GetLastChild(GetRootItem());
603         item.IsOk();
604         item = GetPrevSibling(item)) {
605        if (ItemHasChildren(item)) {
606            // Not an overlay.
607            continue;
608        }
609        const TreeData* data = static_cast<const TreeData*>(GetItemData(item));
610        if (data->GetSurvey() == file) {
611            Delete(item);
612            break;
613        }
614    }
615}
616
617wxTreeItemId AvenTreeCtrl::FirstOverlay()
618{
619    wxTreeItemIdValue cookie;
620    auto item = GetFirstChild(GetRootItem(), cookie);
621    while (item.IsOk() &&
622           (ItemHasChildren(item) || GetItemState(item) != STATE_ON)) {
623        item = GetNextSibling(item);
624    }
625    return item;
626}
627
628wxTreeItemId AvenTreeCtrl::NextOverlay(wxTreeItemId item)
629{
630    do {
631        item = GetNextSibling(item);
632    } while (item.IsOk() &&
633             (ItemHasChildren(item) || GetItemState(item) != STATE_ON));
634    return item;
635}
636
637wxTreeItemId AvenTreeCtrl::RemoveOverlay(wxTreeItemId id)
638{
639    wxTreeItemId item = NextOverlay(id);
640    Delete(id);
641    return item;
642}
643
644const wxString& AvenTreeCtrl::GetOverlayFilename(wxTreeItemId item)
645{
646    if (ItemHasChildren(item)) {
647not_an_overlay:
648        static const wxString empty_string;
649        return empty_string;
650    }
651
652    const TreeData* data = static_cast<const TreeData*>(GetItemData(item));
653    if (!data) goto not_an_overlay;
654    return data->GetSurvey();
655}
Note: See TracBrowser for help on using the repository browser.