source: git/src/aventreectrl.cc @ 9ea7c01

stereo-2025
Last change on this file since 9ea7c01 was ae917b96, checked in by Olly Betts <olly@…>, 4 months ago

Simplify allocation functions

We don't need the xosmalloc(), osfree(), etc as the memory allocation
on a modern OS isn't limited in the size it can allocate by default.

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