OpenCPN Partial API docs
Loading...
Searching...
No Matches
priority_gui.cpp
1/******************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose:
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2022 by David S. Register *
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 *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 ***************************************************************************
25 *
26 *
27 */
28
29#include <wx/wxprec.h>
30
31#ifndef WX_PRECOMP
32#include <wx/wx.h>
33#endif // precompiled headers
34
35#include <wx/app.h>
36#include <wx/tokenzr.h>
37
38#ifdef __OCPN__ANDROID__
39#include "androidUTIL.h"
40#include "qdebug.h"
41#endif
42
43#include "priority_gui.h"
44#include "ocpn_app.h"
45#include "comm_bridge.h"
46#include "ocpn_frame.h"
47
48extern MyFrame *gFrame;
49
50wxDECLARE_APP(MyApp);
51
52class PriorityEntry : public wxTreeItemData {
53public:
54 PriorityEntry(int category, int index){ m_category = category, m_index=index; }
55 ~PriorityEntry() {};
56
57 int m_category, m_index;
58};
59
60
61
62
63PriorityDlg::PriorityDlg(wxWindow* parent)
64 : wxDialog(parent, wxID_ANY, _("Adjust Comm Priorities"), wxDefaultPosition,
65 wxSize(480, 420), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
66{
67
68 m_selIndex = 0;
69 m_selmap_index = 0;
70
71 wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
72 SetSizer(mainSizer);
73
74 wxBoxSizer* secondSizer = new wxBoxSizer(wxHORIZONTAL);
75 mainSizer->Add(secondSizer, 1, wxEXPAND, 5);
76
77 wxStaticBox* pclbBox = new wxStaticBox(this, wxID_ANY, _("Priority List"));
78 wxStaticBoxSizer* stcSizer = new wxStaticBoxSizer(pclbBox, wxVERTICAL);
79 secondSizer->Add(stcSizer, 1, wxALL | wxEXPAND, 5);
80
81 m_prioTree = new wxTreeCtrl(this,wxID_ANY, wxDefaultPosition,
82 wxDefaultSize);
83 stcSizer->Add(m_prioTree, 1, wxALL | wxEXPAND, 5);
84
85 wxBoxSizer* btnEntrySizer = new wxBoxSizer(wxVERTICAL);
86 secondSizer->Add(btnEntrySizer, 0, wxALL | wxEXPAND, 5);
87 btnMoveUp = new wxButton(this, wxID_ANY, _("Move Up"));
88 btnMoveDown = new wxButton(this, wxID_ANY, _("Move Down"));
89 btnMoveUp->Disable();
90 btnMoveDown->Disable();
91
92 btnEntrySizer->Add(btnMoveUp, 0, wxALL, 5);
93 btnEntrySizer->Add(btnMoveDown, 0, wxALL, 5);
94
95 btnEntrySizer->AddSpacer(15);
96
97 btnRefresh = new wxButton(this, wxID_ANY, _("Refresh"));
98 btnClear = new wxButton(this, wxID_ANY, _("Clear All"));
99
100 btnEntrySizer->Add(btnRefresh, 0, wxALL, 5);
101 btnEntrySizer->Add(btnClear, 0, wxALL, 5);
102
103 wxStdDialogButtonSizer* btnSizer = new wxStdDialogButtonSizer();
104 wxButton* btnOK = new wxButton(this, wxID_OK);
105 wxButton* btnCancel = new wxButton(this, wxID_CANCEL, _("Cancel"));
106 btnSizer->AddButton(btnOK);
107 btnSizer->AddButton(btnCancel);
108 btnSizer->Realize();
109 mainSizer->Add(btnSizer, 0, wxALL | wxEXPAND, 5);
110
111 // Connect Events
112 btnMoveUp->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
113 wxCommandEventHandler(PriorityDlg::OnMoveUpClick), NULL,
114 this);
115 btnMoveDown->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
116 wxCommandEventHandler(PriorityDlg::OnMoveDownClick), NULL,
117 this);
118
119 btnRefresh->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
120 wxCommandEventHandler(PriorityDlg::OnRefreshClick), NULL,
121 this);
122
123 btnClear->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
124 wxCommandEventHandler(PriorityDlg::OnClearClick), NULL,
125 this);
126
127 m_prioTree->Connect(wxEVT_TREE_SEL_CHANGED,
128 wxCommandEventHandler(PriorityDlg::OnItemSelected),
129 NULL, this);
130
131 // Get the current status
132 MyApp& app = wxGetApp();
133 m_map = app.m_comm_bridge.GetPriorityMaps();
134
135 Populate();
136
137 int n_lines = wxMax(m_prioTree->GetCount(), 15);
138
139 stcSizer->SetMinSize(m_maxStringLength * GetCharWidth() * 15 / 10,
140 wxMin(gFrame->GetSize().y * 3 /4 , n_lines * GetCharHeight()));
141
142
143 Layout();
144 mainSizer->Fit(this);
145 Centre();
146}
147
148
149void PriorityDlg::AddLeaves(const std::vector<std::string> &map_list,
150 size_t map_index, std::string map_name,
151 wxTreeItemId leaf_parent){
152 if(map_list.size() < (size_t)map_index)
153 return;
154
155 // Get the current Priority container for this branch
156 MyApp& app = wxGetApp();
157 PriorityContainer pc = app.m_comm_bridge.GetPriorityContainer(map_name);
158
159 wxString priority_string(map_list[map_index].c_str());
160 wxStringTokenizer tk(priority_string, "|");
161 size_t index = 0;
162 while (tk.HasMoreTokens()) {
163 wxString item_string = tk.GetNextToken();
164
165 // Record the maximum display string length, for use in dialog sizing.
166 m_maxStringLength = wxMax(m_maxStringLength, item_string.Length());
167
168 PriorityEntry *pe = new PriorityEntry(map_index, index);
169 wxTreeItemId id_tk = m_prioTree->AppendItem(leaf_parent, item_string, -1, -1, pe);
170
171 // Set bold text on item currently active (usually 0)
172 if ( (size_t)(pc.active_priority) == index)
173 m_prioTree->SetItemBold(id_tk);
174
175 index++;
176 }
177}
178
179
180void PriorityDlg::Populate() {
181
182 m_prioTree->Unselect();
183 m_prioTree->DeleteAllItems();
184 m_maxStringLength = 15; // default width calculation
185
186// wxTreeItemId* rootData = new wxDirItemData(_T("Dummy"), _T("Dummy"), TRUE);
187 wxTreeItemId m_rootId = m_prioTree->AddRoot(_("Priorities"), -1, -1, NULL);
188 m_prioTree->SetItemHasChildren(m_rootId);
189
190 wxTreeItemId id_position = m_prioTree->AppendItem(m_rootId, _("Position"), -1, -1, NULL);
191 m_prioTree->SetItemHasChildren(id_position);
192 AddLeaves(m_map, 0, "position", id_position);
193
194 wxTreeItemId id_velocity = m_prioTree->AppendItem(m_rootId, _("Speed/Course"), -1, -1, NULL);
195 m_prioTree->SetItemHasChildren(id_velocity);
196 AddLeaves(m_map, 1, "velocity", id_velocity);
197
198 wxTreeItemId id_heading = m_prioTree->AppendItem(m_rootId, _("Heading"), -1, -1, NULL);
199 m_prioTree->SetItemHasChildren(id_heading);
200 AddLeaves(m_map, 2, "heading", id_heading);
201
202 wxTreeItemId id_magvar = m_prioTree->AppendItem(m_rootId, _("Mag Variation"), -1, -1, NULL);
203 m_prioTree->SetItemHasChildren(id_magvar);
204 AddLeaves(m_map, 3, "variation", id_magvar);
205
206 wxTreeItemId id_sats = m_prioTree->AppendItem(m_rootId, _("Satellites"), -1, -1, NULL);
207 m_prioTree->SetItemHasChildren(id_sats);
208 AddLeaves(m_map, 4, "satellites", id_sats);
209
210 m_prioTree->ExpandAll();
211
212 // Restore selection
213 wxTreeItemId rootID = m_prioTree->GetRootItem();
214 wxTreeItemIdValue cookie;
215 int i = m_selmap_index;
216 wxTreeItemId cid = m_prioTree->GetFirstChild(rootID, cookie);
217
218 while ((i > 0) && cid.IsOk()){
219 cid = m_prioTree->GetNextChild( rootID, cookie);
220 i--;
221 }
222
223 wxTreeItemId ccid = m_prioTree->GetFirstChild(cid, cookie);
224
225 int j = m_selIndex;
226 while ((j > 0) && ccid.IsOk()){
227 ccid = m_prioTree->GetNextChild( cid, cookie);
228 j--;
229 }
230
231 if(ccid.IsOk())
232 m_prioTree->SelectItem(ccid);
233
234}
235
236
237void PriorityDlg::OnItemSelected(wxCommandEvent& event){
238 btnMoveUp->Disable();
239 btnMoveDown->Disable();
240
241 wxTreeItemId id = m_prioTree->GetSelection();
242 PriorityEntry *pe = (PriorityEntry *)m_prioTree->GetItemData(id);
243 if (!pe)
244 return;
245
246 m_selIndex = pe->m_index;
247 m_selmap_index = pe->m_category;
248
249 if (pe->m_index > 0){
250 btnMoveUp->Enable();
251 }
252
253 wxTreeItemId id_parent = m_prioTree->GetItemParent(id);
254
255 // count siblings
256 int n_sibs = 0;
257 wxTreeItemIdValue cookie;
258 wxTreeItemId ch = m_prioTree->GetFirstChild(id_parent, cookie);
259 while (ch.IsOk()) {
260 n_sibs++;
261 ch = m_prioTree->GetNextChild(id_parent, cookie);
262 }
263
264 if (pe->m_index < n_sibs-1)
265 btnMoveDown->Enable();
266}
267
268void PriorityDlg::OnMoveUpClick(wxCommandEvent& event) {
269 ProcessMove(m_prioTree->GetSelection(), -1);
270}
271
272void PriorityDlg::OnMoveDownClick(wxCommandEvent& event) {
273 ProcessMove(m_prioTree->GetSelection(), 1);
274}
275
276void PriorityDlg::ProcessMove(wxTreeItemId id, int dir){
277
278 // Get the extra data
279 PriorityEntry *pe = (PriorityEntry *)m_prioTree->GetItemData(id);
280 if (!pe)
281 return;
282 if(pe->m_category >4)
283 return;
284
285 // Get the selected category string from the map
286 wxString priority_string = wxString(m_map[pe->m_category].c_str());
287
288 // Build an array
289 wxString prio_array[16]; // enough, plus
290
291 wxStringTokenizer tk(priority_string, "|");
292 int index = 0;
293 while (tk.HasMoreTokens() && index < 16) {
294 prio_array[index] = tk.GetNextToken();
295 index++;
296 }
297 int max_index = index;
298
299 // perform the action
300 if (dir == -1){ // Move UP
301 if (pe->m_index > 0){
302 // swap entries in array
303 wxString s_above = prio_array[pe->m_index - 1];
304 wxString s_move = prio_array[pe->m_index];
305 prio_array[pe->m_index - 1] = s_move;
306 prio_array[pe->m_index] = s_above;
307 m_selIndex--;
308 }
309 }
310 else{ // Move DOWN
311 if (pe->m_index < max_index){
312 // swap entries in array
313 wxString s_below = prio_array[pe->m_index + 1];
314 wxString s_move = prio_array[pe->m_index];
315 prio_array[pe->m_index + 1] = s_move;
316 prio_array[pe->m_index] = s_below;
317 m_selIndex++;
318 }
319 }
320
321 // create the new string
322 wxString prio_mod;
323 for (int i=0 ; i < 16 ; i++){
324 if (prio_array[i].Length()){
325 prio_mod += prio_array[i];
326 prio_mod += wxString("|");
327 }
328 }
329
330 // update the string in the map
331 std::string s_upd(prio_mod.c_str());
332 m_map[pe->m_category] = s_upd;
333
334 // Auto-adjust Sat and COG/SOG priorities if POS has been moved up/down
335 if (pe->m_category == 0){
336 AdjustSatPriority();
337 AdjustCOGSOGPriority();
338 }
339
340 // Update the priority mechanism
341 MyApp& app = wxGetApp();
342 app.m_comm_bridge.UpdateAndApplyMaps(m_map);
343
344 // And reload the tree GUI
345 m_map = app.m_comm_bridge.GetPriorityMaps();
346 Populate();
347}
348
349void PriorityDlg::OnRefreshClick(wxCommandEvent& event) {
350 // Reload the tree GUI
351 MyApp& app = wxGetApp();
352 m_map = app.m_comm_bridge.GetPriorityMaps();
353 Populate();
354}
355
356void PriorityDlg::OnClearClick(wxCommandEvent& event) {
357 m_map[0].clear();
358 m_map[1].clear();
359 m_map[2].clear();
360 m_map[3].clear();
361 m_map[4].clear();
362
363 m_selmap_index = m_selIndex = 0;
364
365 // Update the priority mechanism
366 MyApp& app = wxGetApp();
367 app.m_comm_bridge.UpdateAndApplyMaps(m_map);
368
369 // And reload the tree GUI
370 m_map = app.m_comm_bridge.GetPriorityMaps();
371 Populate();
372
373}
374
375void PriorityDlg::AdjustSatPriority() {
376
377 // Get an array of available sat sources
378 std::string sat_prio = m_map[4];
379 wxArrayString sat_sources;
380 wxString sat_priority_string(sat_prio.c_str());
381 wxStringTokenizer tks(sat_priority_string, "|");
382 while (tks.HasMoreTokens()) {
383 wxString item_string = tks.GetNextToken();
384 sat_sources.Add(item_string);
385 }
386
387 // Step thru the POS priority map
388 std::string pos_prio = m_map[0];
389 wxString pos_priority_string(pos_prio.c_str());
390 wxStringTokenizer tk(pos_priority_string, "|");
391 wxArrayString new_sat_prio;
392 while (tk.HasMoreTokens()) {
393 wxString item_string = tk.GetNextToken();
394 wxString pos_channel = item_string.BeforeFirst(';');
395
396 // search the sat sources array for a match
397 // if found, add to proposed new priority array
398 for (size_t i = 0 ; i < sat_sources.GetCount(); i++){
399 if (pos_channel.IsSameAs(sat_sources[i].BeforeFirst(';'))){
400 new_sat_prio.Add(sat_sources[i]);
401 // Mark this source as "used"
402 sat_sources[i] = "USED";
403 break;
404 }
405 else { // no match, what to do? //FIXME (dave)
406 int yyp = 4;
407 }
408 }
409 }
410 // Create a new sat priority string from new_sat_prio array
411 wxString proposed_sat_prio;
412 for (size_t i = 0 ; i < new_sat_prio.GetCount(); i++){
413 proposed_sat_prio += new_sat_prio[i];
414 proposed_sat_prio += wxString("|");
415 }
416
417 // Update the maps with the new sat priority string
418 m_map[4] = proposed_sat_prio.ToStdString();
419}
420
421void PriorityDlg::AdjustCOGSOGPriority() {
422
423 // Get an array of available COG/SOG sources
424 std::string cogsog_prio = m_map[1];
425 wxArrayString cogsog_sources;
426 wxString cogsog_priority_string(cogsog_prio.c_str());
427 wxStringTokenizer tks(cogsog_priority_string, "|");
428 while (tks.HasMoreTokens()) {
429 wxString item_string = tks.GetNextToken();
430 cogsog_sources.Add(item_string);
431 }
432
433 // Step thru the POS priority map
434 std::string pos_prio = m_map[0];
435 wxString pos_priority_string(pos_prio.c_str());
436 wxStringTokenizer tk(pos_priority_string, "|");
437 wxArrayString new_cogsog_prio;
438 while (tk.HasMoreTokens()) {
439 wxString item_string = tk.GetNextToken();
440 wxString pos_channel = item_string.BeforeFirst(';');
441
442 // search the cogsog sources array for a match
443 // if found, add to proposed new priority array
444 for (size_t i = 0 ; i < cogsog_sources.GetCount(); i++){
445 if (pos_channel.IsSameAs(cogsog_sources[i].BeforeFirst(';'))){
446 new_cogsog_prio.Add(cogsog_sources[i]);
447 // Mark this source as "used"
448 cogsog_sources[i] = "USED";
449 break;
450 }
451 else { // no match, what to do? //FIXME (dave)
452 int yyp = 4;
453 }
454 }
455 }
456 // Create a new cog/sog priority string from new_cogsog_prio array
457 wxString proposed_cogsog_prio;
458 for (size_t i = 0 ; i < new_cogsog_prio.GetCount(); i++){
459 proposed_cogsog_prio += new_cogsog_prio[i];
460 proposed_cogsog_prio += wxString("|");
461 }
462
463 // Update the maps with the new cog/sog priority string
464 m_map[1] = proposed_cogsog_prio.ToStdString();
465}
Definition: ocpn_app.h:44
Data for selected source from multiple candidates.
Definition: comm_bridge.h:43