about summary refs log tree commit diff stats
path: root/src/tracker_frame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tracker_frame.cpp')
-rw-r--r--src/tracker_frame.cpp294
1 files changed, 208 insertions, 86 deletions
diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index e944704..e8d7ef6 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp
@@ -1,30 +1,64 @@
1#include "tracker_frame.h" 1#include "tracker_frame.h"
2 2
3#include <fmt/core.h>
3#include <wx/aboutdlg.h> 4#include <wx/aboutdlg.h>
4#include <wx/choicebk.h> 5#include <wx/choicebk.h>
6#include <wx/filedlg.h>
7#include <wx/notebook.h>
8#include <wx/splitter.h>
9#include <wx/stdpaths.h>
5#include <wx/webrequest.h> 10#include <wx/webrequest.h>
6 11
12#include <algorithm>
7#include <nlohmann/json.hpp> 13#include <nlohmann/json.hpp>
8#include <sstream> 14#include <sstream>
9 15
10#include "achievements_pane.h" 16#include "achievements_pane.h"
11#include "ap_state.h" 17#include "ap_state.h"
12#include "connection_dialog.h" 18#include "connection_dialog.h"
19#include "ipc_dialog.h"
20#include "ipc_state.h"
21#include "items_pane.h"
22#include "log_dialog.h"
23#include "logger.h"
24#include "options_pane.h"
25#include "paintings_pane.h"
13#include "settings_dialog.h" 26#include "settings_dialog.h"
14#include "subway_map.h" 27#include "subway_map.h"
15#include "tracker_config.h" 28#include "tracker_config.h"
16#include "tracker_panel.h" 29#include "tracker_panel.h"
17#include "version.h" 30#include "version.h"
18 31
32namespace {
33
34std::string GetStatusMessage() {
35 std::string msg = AP_GetStatusMessage();
36
37 std::optional<std::string> ipc_msg = IPC_GetStatusMessage();
38 if (ipc_msg) {
39 msg += " ";
40 msg += *ipc_msg;
41 }
42
43 return msg;
44}
45
46} // namespace
47
19enum TrackerFrameIds { 48enum TrackerFrameIds {
20 ID_CONNECT = 1, 49 ID_AP_CONNECT = 1,
21 ID_CHECK_FOR_UPDATES = 2, 50 ID_CHECK_FOR_UPDATES = 2,
22 ID_SETTINGS = 3 51 ID_SETTINGS = 3,
52 ID_ZOOM_IN = 4,
53 ID_ZOOM_OUT = 5,
54 ID_IPC_CONNECT = 7,
55 ID_LOG_DIALOG = 8,
23}; 56};
24 57
25wxDEFINE_EVENT(STATE_RESET, wxCommandEvent); 58wxDEFINE_EVENT(STATE_RESET, wxCommandEvent);
26wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent); 59wxDEFINE_EVENT(STATE_CHANGED, StateChangedEvent);
27wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent); 60wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent);
61wxDEFINE_EVENT(CONNECT_TO_AP, ApConnectEvent);
28 62
29TrackerFrame::TrackerFrame() 63TrackerFrame::TrackerFrame()
30 : wxFrame(nullptr, wxID_ANY, "Lingo Archipelago Tracker", wxDefaultPosition, 64 : wxFrame(nullptr, wxID_ANY, "Lingo Archipelago Tracker", wxDefaultPosition,
@@ -32,52 +66,87 @@ TrackerFrame::TrackerFrame()
32 ::wxInitAllImageHandlers(); 66 ::wxInitAllImageHandlers();
33 67
34 AP_SetTrackerFrame(this); 68 AP_SetTrackerFrame(this);
69 IPC_SetTrackerFrame(this);
70
71 SetTheIconCache(&icons_);
72
73 updater_ = std::make_unique<Updater>(this);
74 updater_->Cleanup();
35 75
36 wxMenu *menuFile = new wxMenu(); 76 wxMenu *menuFile = new wxMenu();
37 menuFile->Append(ID_CONNECT, "&Connect"); 77 menuFile->Append(ID_AP_CONNECT, "&Connect to Archipelago");
78 menuFile->Append(ID_IPC_CONNECT, "&Connect to Lingo");
38 menuFile->Append(ID_SETTINGS, "&Settings"); 79 menuFile->Append(ID_SETTINGS, "&Settings");
39 menuFile->Append(wxID_EXIT); 80 menuFile->Append(wxID_EXIT);
40 81
82 wxMenu *menuView = new wxMenu();
83 zoom_in_menu_item_ = menuView->Append(ID_ZOOM_IN, "Zoom In\tCtrl-+");
84 zoom_out_menu_item_ = menuView->Append(ID_ZOOM_OUT, "Zoom Out\tCtrl--");
85 menuView->AppendSeparator();
86 menuView->Append(ID_LOG_DIALOG, "Show Log Window\tCtrl-L");
87
88 zoom_in_menu_item_->Enable(false);
89 zoom_out_menu_item_->Enable(false);
90
41 wxMenu *menuHelp = new wxMenu(); 91 wxMenu *menuHelp = new wxMenu();
42 menuHelp->Append(wxID_ABOUT); 92 menuHelp->Append(wxID_ABOUT);
43 menuHelp->Append(ID_CHECK_FOR_UPDATES, "Check for Updates"); 93 menuHelp->Append(ID_CHECK_FOR_UPDATES, "Check for Updates");
44 94
45 wxMenuBar *menuBar = new wxMenuBar(); 95 wxMenuBar *menuBar = new wxMenuBar();
46 menuBar->Append(menuFile, "&File"); 96 menuBar->Append(menuFile, "&File");
97 menuBar->Append(menuView, "&View");
47 menuBar->Append(menuHelp, "&Help"); 98 menuBar->Append(menuHelp, "&Help");
48 99
49 SetMenuBar(menuBar); 100 SetMenuBar(menuBar);
50 101
51 CreateStatusBar(); 102 CreateStatusBar();
52 SetStatusText("Not connected to Archipelago.");
53 103
54 Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT); 104 Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT);
55 Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT); 105 Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT);
56 Bind(wxEVT_MENU, &TrackerFrame::OnConnect, this, ID_CONNECT); 106 Bind(wxEVT_MENU, &TrackerFrame::OnApConnect, this, ID_AP_CONNECT);
107 Bind(wxEVT_MENU, &TrackerFrame::OnIpcConnect, this, ID_IPC_CONNECT);
57 Bind(wxEVT_MENU, &TrackerFrame::OnSettings, this, ID_SETTINGS); 108 Bind(wxEVT_MENU, &TrackerFrame::OnSettings, this, ID_SETTINGS);
58 Bind(wxEVT_MENU, &TrackerFrame::OnCheckForUpdates, this, 109 Bind(wxEVT_MENU, &TrackerFrame::OnCheckForUpdates, this,
59 ID_CHECK_FOR_UPDATES); 110 ID_CHECK_FOR_UPDATES);
111 Bind(wxEVT_MENU, &TrackerFrame::OnZoomIn, this, ID_ZOOM_IN);
112 Bind(wxEVT_MENU, &TrackerFrame::OnZoomOut, this, ID_ZOOM_OUT);
113 Bind(wxEVT_MENU, &TrackerFrame::OnOpenLogWindow, this, ID_LOG_DIALOG);
114 Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &TrackerFrame::OnChangePage, this);
115 Bind(wxEVT_SPLITTER_SASH_POS_CHANGED, &TrackerFrame::OnSashPositionChanged,
116 this);
60 Bind(STATE_RESET, &TrackerFrame::OnStateReset, this); 117 Bind(STATE_RESET, &TrackerFrame::OnStateReset, this);
61 Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this); 118 Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this);
62 Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this); 119 Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this);
120 Bind(CONNECT_TO_AP, &TrackerFrame::OnConnectToAp, this);
121
122 wxSize logicalSize = FromDIP(wxSize(1280, 728));
63 123
64 achievements_pane_ = new AchievementsPane(this); 124 splitter_window_ = new wxSplitterWindow(this, wxID_ANY);
125 splitter_window_->SetMinimumPaneSize(logicalSize.x / 5);
65 126
66 wxChoicebook *choicebook = new wxChoicebook(this, wxID_ANY); 127 wxChoicebook *choicebook = new wxChoicebook(splitter_window_, wxID_ANY);
128
129 achievements_pane_ = new AchievementsPane(choicebook);
67 choicebook->AddPage(achievements_pane_, "Achievements"); 130 choicebook->AddPage(achievements_pane_, "Achievements");
68 131
69 wxNotebook *rightpane = new wxNotebook(this, wxID_ANY); 132 items_pane_ = new ItemsPane(choicebook);
70 tracker_panel_ = new TrackerPanel(rightpane); 133 choicebook->AddPage(items_pane_, "Items");
71 subway_map_ = new SubwayMap(rightpane); 134
72 rightpane->AddPage(tracker_panel_, "Map"); 135 options_pane_ = new OptionsPane(choicebook);
73 rightpane->AddPage(subway_map_, "Subway"); 136 choicebook->AddPage(options_pane_, "Options");
137
138 paintings_pane_ = new PaintingsPane(choicebook);
139 choicebook->AddPage(paintings_pane_, "Paintings");
74 140
75 wxBoxSizer *top_sizer = new wxBoxSizer(wxHORIZONTAL); 141 notebook_ = new wxNotebook(splitter_window_, wxID_ANY);
76 top_sizer->Add(choicebook, wxSizerFlags().Expand().Proportion(1)); 142 tracker_panel_ = new TrackerPanel(notebook_);
77 top_sizer->Add(rightpane, wxSizerFlags().Expand().Proportion(3)); 143 subway_map_ = new SubwayMap(notebook_);
144 notebook_->AddPage(tracker_panel_, "Map");
145 notebook_->AddPage(subway_map_, "Subway");
78 146
79 SetSizerAndFit(top_sizer); 147 splitter_window_->SplitVertically(choicebook, notebook_, logicalSize.x / 4);
80 SetSize(1280, 728); 148
149 SetSize(logicalSize);
81 150
82 if (!GetTrackerConfig().asked_to_check_for_updates) { 151 if (!GetTrackerConfig().asked_to_check_for_updates) {
83 GetTrackerConfig().asked_to_check_for_updates = true; 152 GetTrackerConfig().asked_to_check_for_updates = true;
@@ -94,23 +163,28 @@ TrackerFrame::TrackerFrame()
94 } 163 }
95 164
96 if (GetTrackerConfig().should_check_for_updates) { 165 if (GetTrackerConfig().should_check_for_updates) {
97 CheckForUpdates(/*manual=*/false); 166 updater_->CheckForUpdates(/*invisible=*/true);
98 } 167 }
168
169 SetStatusText(GetStatusMessage());
99} 170}
100 171
101void TrackerFrame::SetStatusMessage(std::string message) { 172void TrackerFrame::ConnectToAp(std::string server, std::string user,
102 wxCommandEvent *event = new wxCommandEvent(STATUS_CHANGED); 173 std::string pass) {
103 event->SetString(message.c_str()); 174 QueueEvent(new ApConnectEvent(CONNECT_TO_AP, GetId(), std::move(server),
175 std::move(user), std::move(pass)));
176}
104 177
105 QueueEvent(event); 178void TrackerFrame::UpdateStatusMessage() {
179 QueueEvent(new wxCommandEvent(STATUS_CHANGED));
106} 180}
107 181
108void TrackerFrame::ResetIndicators() { 182void TrackerFrame::ResetIndicators() {
109 QueueEvent(new wxCommandEvent(STATE_RESET)); 183 QueueEvent(new wxCommandEvent(STATE_RESET));
110} 184}
111 185
112void TrackerFrame::UpdateIndicators() { 186void TrackerFrame::UpdateIndicators(StateUpdate state) {
113 QueueEvent(new wxCommandEvent(STATE_CHANGED)); 187 QueueEvent(new StateChangedEvent(STATE_CHANGED, GetId(), std::move(state)));
114} 188}
115 189
116void TrackerFrame::OnAbout(wxCommandEvent &event) { 190void TrackerFrame::OnAbout(wxCommandEvent &event) {
@@ -118,6 +192,7 @@ void TrackerFrame::OnAbout(wxCommandEvent &event) {
118 about_info.SetName("Lingo Archipelago Tracker"); 192 about_info.SetName("Lingo Archipelago Tracker");
119 about_info.SetVersion(kTrackerVersion.ToString()); 193 about_info.SetVersion(kTrackerVersion.ToString());
120 about_info.AddDeveloper("hatkirby"); 194 about_info.AddDeveloper("hatkirby");
195 about_info.AddDeveloper("art0007i");
121 about_info.AddArtist("Brenton Wildes"); 196 about_info.AddArtist("Brenton Wildes");
122 about_info.AddArtist("kinrah"); 197 about_info.AddArtist("kinrah");
123 198
@@ -126,7 +201,7 @@ void TrackerFrame::OnAbout(wxCommandEvent &event) {
126 201
127void TrackerFrame::OnExit(wxCommandEvent &event) { Close(true); } 202void TrackerFrame::OnExit(wxCommandEvent &event) { Close(true); }
128 203
129void TrackerFrame::OnConnect(wxCommandEvent &event) { 204void TrackerFrame::OnApConnect(wxCommandEvent &event) {
130 ConnectionDialog dlg; 205 ConnectionDialog dlg;
131 206
132 if (dlg.ShowModal() == wxID_OK) { 207 if (dlg.ShowModal() == wxID_OK) {
@@ -144,7 +219,7 @@ void TrackerFrame::OnConnect(wxCommandEvent &event) {
144 } 219 }
145 } 220 }
146 221
147 while (new_history.size() > 5) { 222 while (new_history.size() > 10) {
148 new_history.pop_back(); 223 new_history.pop_back();
149 } 224 }
150 225
@@ -156,6 +231,17 @@ void TrackerFrame::OnConnect(wxCommandEvent &event) {
156 } 231 }
157} 232}
158 233
234void TrackerFrame::OnIpcConnect(wxCommandEvent &event) {
235 IpcDialog dlg;
236
237 if (dlg.ShowModal() == wxID_OK) {
238 GetTrackerConfig().ipc_address = dlg.GetIpcAddress();
239 GetTrackerConfig().Save();
240
241 IPC_Connect(dlg.GetIpcAddress());
242 }
243}
244
159void TrackerFrame::OnSettings(wxCommandEvent &event) { 245void TrackerFrame::OnSettings(wxCommandEvent &event) {
160 SettingsDialog dlg; 246 SettingsDialog dlg;
161 247
@@ -163,83 +249,119 @@ void TrackerFrame::OnSettings(wxCommandEvent &event) {
163 GetTrackerConfig().should_check_for_updates = 249 GetTrackerConfig().should_check_for_updates =
164 dlg.GetShouldCheckForUpdates(); 250 dlg.GetShouldCheckForUpdates();
165 GetTrackerConfig().hybrid_areas = dlg.GetHybridAreas(); 251 GetTrackerConfig().hybrid_areas = dlg.GetHybridAreas();
166 GetTrackerConfig().show_hunt_panels = dlg.GetShowHuntPanels(); 252 GetTrackerConfig().visible_panels = dlg.GetVisiblePanels();
253 GetTrackerConfig().track_position = dlg.GetTrackPosition();
167 GetTrackerConfig().Save(); 254 GetTrackerConfig().Save();
168 255
169 UpdateIndicators(); 256 UpdateIndicators(StateUpdate{.cleared_locations = true,
257 .player_position = true,
258 .changed_settings = true});
170 } 259 }
171} 260}
172 261
173void TrackerFrame::OnCheckForUpdates(wxCommandEvent &event) { 262void TrackerFrame::OnCheckForUpdates(wxCommandEvent &event) {
174 CheckForUpdates(/*manual=*/true); 263 updater_->CheckForUpdates(/*invisible=*/false);
175} 264}
176 265
177void TrackerFrame::OnStateReset(wxCommandEvent& event) { 266void TrackerFrame::OnZoomIn(wxCommandEvent &event) {
178 tracker_panel_->UpdateIndicators(); 267 if (notebook_->GetSelection() == 1) {
179 achievements_pane_->UpdateIndicators(); 268 subway_map_->Zoom(true);
180 subway_map_->OnConnect(); 269 }
181 Refresh();
182} 270}
183 271
184void TrackerFrame::OnStateChanged(wxCommandEvent &event) { 272void TrackerFrame::OnZoomOut(wxCommandEvent &event) {
185 tracker_panel_->UpdateIndicators(); 273 if (notebook_->GetSelection() == 1) {
186 achievements_pane_->UpdateIndicators(); 274 subway_map_->Zoom(false);
187 subway_map_->UpdateIndicators(); 275 }
188 Refresh();
189} 276}
190 277
191void TrackerFrame::OnStatusChanged(wxCommandEvent &event) { 278void TrackerFrame::OnOpenLogWindow(wxCommandEvent &event) {
192 SetStatusText(event.GetString()); 279 if (log_dialog_ == nullptr) {
280 log_dialog_ = new LogDialog(this);
281 log_dialog_->Show();
282 TrackerSetLogDialog(log_dialog_);
283
284 log_dialog_->Bind(wxEVT_CLOSE_WINDOW, &TrackerFrame::OnCloseLogWindow,
285 this);
286 } else {
287 log_dialog_->SetFocus();
288 }
193} 289}
194 290
195void TrackerFrame::CheckForUpdates(bool manual) { 291void TrackerFrame::OnCloseLogWindow(wxCloseEvent& event) {
196 wxWebRequest request = wxWebSession::GetDefault().CreateRequest( 292 TrackerSetLogDialog(nullptr);
197 this, "https://code.fourisland.com/lingo-ap-tracker/plain/VERSION"); 293 log_dialog_ = nullptr;
198 294
199 if (!request.IsOk()) { 295 event.Skip();
200 if (manual) { 296}
201 wxMessageBox("Could not check for updates.", "Error", 297
202 wxOK | wxICON_ERROR); 298void TrackerFrame::OnChangePage(wxBookCtrlEvent &event) {
203 } else { 299 zoom_in_menu_item_->Enable(event.GetSelection() == 1);
204 SetStatusText("Could not check for updates."); 300 zoom_out_menu_item_->Enable(event.GetSelection() == 1);
301}
302
303void TrackerFrame::OnSashPositionChanged(wxSplitterEvent& event) {
304 notebook_->Refresh();
305}
306
307void TrackerFrame::OnStateReset(wxCommandEvent &event) {
308 tracker_panel_->UpdateIndicators(/*reset=*/true);
309 achievements_pane_->UpdateIndicators();
310 items_pane_->ResetIndicators();
311 options_pane_->OnConnect();
312 paintings_pane_->ResetIndicators();
313 subway_map_->OnConnect();
314 Refresh();
315}
316
317void TrackerFrame::OnStateChanged(StateChangedEvent &event) {
318 const StateUpdate &state = event.GetState();
319
320 bool hunt_panels = false;
321 if (GetTrackerConfig().visible_panels == TrackerConfig::kHUNT_PANELS) {
322 hunt_panels = std::any_of(
323 state.panels.begin(), state.panels.end(), [](int solve_index) {
324 return GD_GetPanel(GD_GetPanelBySolveIndex(solve_index)).hunt;
325 });
326 } else if (GetTrackerConfig().visible_panels == TrackerConfig::kALL_PANELS) {
327 hunt_panels = true;
328 }
329
330 if (!state.items.empty() || !state.paintings.empty() ||
331 state.cleared_locations || hunt_panels) {
332 // TODO: The only real reason to reset tracker_panel during an active
333 // connection is if the hunt panels setting changes. If we remove hunt
334 // panels later, we can get rid of this.
335 tracker_panel_->UpdateIndicators(/*reset=*/state.changed_settings);
336 subway_map_->UpdateIndicators();
337 Refresh();
338 } else if (state.player_position && GetTrackerConfig().track_position) {
339 if (notebook_->GetSelection() == 0) {
340 tracker_panel_->Refresh();
205 } 341 }
342 }
206 343
207 return; 344 if (std::any_of(state.panels.begin(), state.panels.end(),
345 [](int solve_index) {
346 return GD_GetPanel(GD_GetPanelBySolveIndex(solve_index))
347 .achievement;
348 })) {
349 achievements_pane_->UpdateIndicators();
208 } 350 }
209 351
210 Bind(wxEVT_WEBREQUEST_STATE, [this, manual](wxWebRequestEvent &evt) { 352 if (!state.items.empty()) {
211 if (evt.GetState() == wxWebRequest::State_Completed) { 353 items_pane_->UpdateIndicators(state.items);
212 std::string response = evt.GetResponse().AsString().ToStdString(); 354 }
213 355
214 Version latest_version(response); 356 if (!state.paintings.empty()) {
215 if (kTrackerVersion < latest_version) { 357 paintings_pane_->UpdateIndicators(state.paintings);
216 std::ostringstream message_text; 358 }
217 message_text << "There is a newer version of Lingo AP Tracker " 359}
218 "available. You have " 360
219 << kTrackerVersion.ToString() 361void TrackerFrame::OnStatusChanged(wxCommandEvent &event) {
220 << ", and the latest version is " 362 SetStatusText(wxString::FromUTF8(GetStatusMessage()));
221 << latest_version.ToString() 363}
222 << ". Would you like to update?";
223
224 if (wxMessageBox(message_text.str(), "Update available", wxYES_NO) ==
225 wxYES) {
226 wxLaunchDefaultBrowser(
227 "https://code.fourisland.com/lingo-ap-tracker/about/"
228 "CHANGELOG.md");
229 }
230 } else if (manual) {
231 wxMessageBox("Lingo AP Tracker is up to date!", "Lingo AP Tracker",
232 wxOK);
233 }
234 } else if (evt.GetState() == wxWebRequest::State_Failed) {
235 if (manual) {
236 wxMessageBox("Could not check for updates.", "Error",
237 wxOK | wxICON_ERROR);
238 } else {
239 SetStatusText("Could not check for updates.");
240 }
241 }
242 });
243 364
244 request.Start(); 365void TrackerFrame::OnConnectToAp(ApConnectEvent &event) {
366 AP_Connect(event.GetServer(), event.GetUser(), event.GetPass());
245} 367}