| [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 |
|
|---|
| 33 | using namespace std;
|
|---|
| 34 |
|
|---|
| [44e007d] | 35 | // STATE_BLANK is used for stations which are siblings of surveys which have
|
|---|
| 36 | // select checkboxes.
|
|---|
| 37 | enum { STATE_BLANK = 0, STATE_OFF, STATE_ON };
|
|---|
| [672459c] | 38 |
|
|---|
| 39 | /* XPM */
|
|---|
| [44e007d] | 40 | static 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 */
|
|---|
| 63 | static 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] | 87 | static 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] | 111 | BEGIN_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] | 124 | END_EVENT_TABLE()
|
|---|
| 125 |
|
|---|
| 126 | AvenTreeCtrl::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] | 143 | void 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] | 293 | constexpr auto TREE_MASK = wxTREE_HITTEST_ONITEMLABEL |
|
|---|
| 294 | wxTREE_HITTEST_ONITEMRIGHT |
|
|---|
| 295 | wxTREE_HITTEST_ONITEMSTATEICON;
|
|---|
| [97dd0d2] | 296 |
|
|---|
| [f17e6dc6] | 297 | void 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] | 321 | void 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] | 335 | void 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] | 344 | void AvenTreeCtrl::OnSelChanged(wxTreeEvent&)
|
|---|
| [f17e6dc6] | 345 | {
|
|---|
| [b623b3e] | 346 | m_SelValid = true;
|
|---|
| [f17e6dc6] | 347 | }
|
|---|
| 348 |
|
|---|
| [44ed489] | 349 | void AvenTreeCtrl::OnItemActivated(wxTreeEvent& e)
|
|---|
| 350 | {
|
|---|
| [4dc54fa] | 351 | if (!m_Enabled) return;
|
|---|
| 352 |
|
|---|
| 353 | m_Parent->TreeItemSelected(GetItemData(e.GetItem()));
|
|---|
| [44ed489] | 354 | }
|
|---|
| 355 |
|
|---|
| [5e0b9f9d] | 356 | void 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] | 406 | bool 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 |
|
|---|
| 423 | void AvenTreeCtrl::UnselectAll()
|
|---|
| 424 | {
|
|---|
| 425 | m_SelValid = false;
|
|---|
| 426 | wxTreeCtrl::UnselectAll();
|
|---|
| 427 | }
|
|---|
| 428 |
|
|---|
| [5901b62] | 429 | void 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] | 465 | void 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] | 471 | void 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] | 498 | void 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] | 530 | void 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 |
|
|---|
| 555 | void 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 |
|
|---|
| 589 | void 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 |
|
|---|
| 598 | void 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 |
|
|---|
| 617 | wxTreeItemId 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 |
|
|---|
| 628 | wxTreeItemId 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 |
|
|---|
| 637 | wxTreeItemId AvenTreeCtrl::RemoveOverlay(wxTreeItemId id)
|
|---|
| 638 | {
|
|---|
| 639 | wxTreeItemId item = NextOverlay(id);
|
|---|
| 640 | Delete(id);
|
|---|
| 641 | return item;
|
|---|
| 642 | }
|
|---|
| 643 |
|
|---|
| 644 | const wxString& AvenTreeCtrl::GetOverlayFilename(wxTreeItemId item)
|
|---|
| 645 | {
|
|---|
| 646 | if (ItemHasChildren(item)) {
|
|---|
| 647 | not_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 | }
|
|---|