Added utilities > Frequency manager + load/save

This commit is contained in:
furrtek 2016-12-26 01:31:38 +01:00
parent ad2a4b6743
commit 7df5987b3b
21 changed files with 526 additions and 219 deletions

View File

@ -140,13 +140,13 @@ set(CPPSRC
afsk.cpp
rds.cpp
ctcss.cpp
freqman.cpp
${COMMON}/lcd_ili9341.cpp
${COMMON}/ui.cpp
${COMMON}/ui_text.cpp
${COMMON}/ui_widget.cpp
${COMMON}/ui_painter.cpp
${COMMON}/ui_focus.cpp
${COMMON}/msgpack.cpp
ui_about.cpp
ui_adsbtx.cpp
ui_afsksetup.cpp

View File

@ -2408,33 +2408,6 @@ __/common/message_queue.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/message_queue.cpp.s
.PHONY : __/common/message_queue.cpp.s
__/common/msgpack.obj: __/common/msgpack.cpp.obj
.PHONY : __/common/msgpack.obj
# target to build an object file
__/common/msgpack.cpp.obj:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/msgpack.cpp.obj
.PHONY : __/common/msgpack.cpp.obj
__/common/msgpack.i: __/common/msgpack.cpp.i
.PHONY : __/common/msgpack.i
# target to preprocess a source file
__/common/msgpack.cpp.i:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/msgpack.cpp.i
.PHONY : __/common/msgpack.cpp.i
__/common/msgpack.s: __/common/msgpack.cpp.s
.PHONY : __/common/msgpack.s
# target to generate assembly for a file
__/common/msgpack.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/msgpack.cpp.s
.PHONY : __/common/msgpack.cpp.s
__/common/png_writer.obj: __/common/png_writer.cpp.obj
.PHONY : __/common/png_writer.obj
@ -3299,6 +3272,33 @@ filewriter.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/filewriter.cpp.s
.PHONY : filewriter.cpp.s
freqman.obj: freqman.cpp.obj
.PHONY : freqman.obj
# target to build an object file
freqman.cpp.obj:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/freqman.cpp.obj
.PHONY : freqman.cpp.obj
freqman.i: freqman.cpp.i
.PHONY : freqman.i
# target to preprocess a source file
freqman.cpp.i:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/freqman.cpp.i
.PHONY : freqman.cpp.i
freqman.s: freqman.cpp.s
.PHONY : freqman.s
# target to generate assembly for a file
freqman.cpp.s:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/freqman.cpp.s
.PHONY : freqman.cpp.s
hackrf_cpld_data.obj: hackrf_cpld_data.cpp.obj
.PHONY : hackrf_cpld_data.obj
@ -5316,9 +5316,6 @@ help:
@echo "... __/common/message_queue.obj"
@echo "... __/common/message_queue.i"
@echo "... __/common/message_queue.s"
@echo "... __/common/msgpack.obj"
@echo "... __/common/msgpack.i"
@echo "... __/common/msgpack.s"
@echo "... __/common/png_writer.obj"
@echo "... __/common/png_writer.i"
@echo "... __/common/png_writer.s"
@ -5415,6 +5412,9 @@ help:
@echo "... filewriter.obj"
@echo "... filewriter.i"
@echo "... filewriter.s"
@echo "... freqman.obj"
@echo "... freqman.i"
@echo "... freqman.s"
@echo "... hackrf_cpld_data.obj"
@echo "... hackrf_cpld_data.i"
@echo "... hackrf_cpld_data.s"

View File

@ -76,26 +76,18 @@ static constexpr Bitmap bitmap_speaker {
};
static constexpr uint8_t bitmap_more_data[] = {
0x00, 0x00,
0xC0, 0x03,
0xC0, 0x03,
0xC0, 0x03,
0xC0, 0x03,
0xC0, 0x03,
0xC0, 0x03,
0xC0, 0x03,
0xC6, 0x63,
0xCF, 0xF3,
0xDE, 0x7B,
0xFC, 0x3F,
0xF8, 0x1F,
0xF0, 0x0F,
0xE0, 0x07,
0xC0, 0x03
0x18,
0x18,
0x18,
0x18,
0xFF,
0x7E,
0x3C,
0x18,
};
static constexpr Bitmap bitmap_more {
{ 16, 16 }, bitmap_more_data
{ 8, 8 }, bitmap_more_data
};
static constexpr uint8_t bitmap_rssipwm_data[] = {

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "freqman.hpp"
bool load_freqman_file(std::vector<freqman_entry> &frequencies) {
File freqs_file;
size_t end, n = 0;
char * file_buffer;
char * desc_pos;
char desc_buffer[32] = { 0 };
std::string description;
rf::Frequency value;
file_buffer = (char *)chHeapAlloc(0, 2048);
while (freqs_file.open("freqman.txt").is_valid()) {
auto result = freqs_file.create("freqman.txt");
if (result.is_valid()) {
return false;
}
}
freqs_file.read(file_buffer, 2048);
char * pos = file_buffer;
while ((pos = strstr(pos, "f=")) && n < 32) {
pos += 2;
value = strtol(pos, nullptr, 10);
desc_pos = strstr(pos, "d=");
if (desc_pos) {
desc_pos += 2;
end = strcspn(desc_pos, ",\x0D\x0A"); // CR LF
if (end > 31) end = 31;
memcpy(desc_buffer, desc_pos, end);
desc_buffer[end] = (char)0;
description = desc_buffer;
pos = desc_pos;
} else {
description = "-";
}
frequencies.push_back({ value, "", description });
}
chHeapFree(file_buffer);
return true;
}
bool save_freqman_file(std::vector<freqman_entry> &frequencies) {
File freqs_file;
size_t n;
std::string item_string;
auto result = freqs_file.create("freqman.txt");
if (result.is_valid()) return false;
for (n = 0; n < frequencies.size(); n++) {
item_string = "f=" + to_string_dec_uint(frequencies[n].value);
if (frequencies[n].description.size())
item_string += ",d=" + frequencies[n].description;
freqs_file.write_line(item_string);
}
return true;
}
std::string freqman_item_string(freqman_entry &entry) {
std::string item_string, frequency_str, description;
char temp_buffer[32];
rf::Frequency value;
value = entry.value;
entry.frequency_str = to_string_dec_int(value / 1000000, 4) + "." +
to_string_dec_int((value / 100) % 10000, 4, '0');
if (entry.description.size() <= 19) {
item_string = entry.frequency_str + ":" + entry.description;
} else {
memcpy(temp_buffer, entry.description.c_str(), 16);
temp_buffer[16] = (char)0;
item_string = entry.frequency_str + ":" + temp_buffer + "...";
}
return item_string;
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include <cstring>
#include <string>
#include "file.hpp"
#include "ui_receiver.hpp"
#include "string_format.hpp"
#ifndef __FREQMAN_H__
#define __FREQMAN_H__
using namespace ui;
struct freqman_entry {
rf::Frequency value;
std::string frequency_str;
std::string description;
};
bool load_freqman_file(std::vector<freqman_entry> &frequencies);
bool save_freqman_file(std::vector<freqman_entry> &frequencies);
std::string freqman_item_string(freqman_entry &frequencies);
#endif/*__FREQMAN_H__*/

View File

@ -23,30 +23,29 @@
// Color bitmaps generated with:
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
//BUG: Xylos doesn't play last tone ?
//BUG (fixed ?): Unistroke text entry screen doesn't care about string max length parameter
//TEST: Imperial in whipcalc
//TEST: Numbers
//TEST: Jammer
//TEST: Frequency manager + save/load
//BUG: Xylos doesn't play last tone ?
//BUG (fixed ?): Soundboard crashes on exit if no wav files on sd card
//BUG (fixed ?): Unistroke text entry screen doesn't care about string max length parameter
//BUG: POCSAG RX sometimes misses the first codeword after SYNC
//BUG: Check AFSK transmit end, skips last bits ?
//BUG: RDS doesn't stop baseband when stopping tx ?
//TODO: File browser ?
//TODO: Mousejack ?
//TODO: Waveform widget ?
//TODO: Frequency manager save/load (ui_freqman)
//TODO: Wav visualizer
//TODO: Move frequencykeypad from ui_receiver to ui_widget (used everywhere)
//TODO: ADS-B draw trajectory + GPS coordinates + scale, and playback
//TODO: Finish EPAR tx
//TODO: Wav visualizer
//TODO: Analog TV tx with camcorder font character generator
//TODO: Test dual tone in proc_tones and remove proc_dtmf_tx
//TODO: Morse coder for foxhunts
//TODO: Make Morse coder and Whistle use proc_tones
//TODO: RDS multiple groups (sequence)
//TODO: Frequency manager
//TODO: IQ replay
//TODO: Use ModalMessageView confirmation for TX ?
//TODO: Show address/data bit fields in OOK TX

View File

@ -62,7 +62,7 @@ private:
void update_text();
Text text_input {
{ 8, 0, 224, 16 }
{ 8, 0, 232, 16 }
};
std::array<Button, 40> buttons;

View File

@ -24,7 +24,6 @@
#include "portapack.hpp"
#include "event_m0.hpp"
#include "portapack_shared_memory.hpp"
#include <cstring>
@ -32,73 +31,197 @@ using namespace portapack;
namespace ui {
void FrequencySaveView::on_save_name(NavigationView& nav) {
textentry(nav, desc_buffer, 28);
frequencies.push_back({ value_, "", desc_buffer });
nav.pop();
}
void FrequencySaveView::on_save_timestamp(NavigationView& nav) {
frequencies.push_back({ value_, "", text_timestamp.text() });
nav.pop();
}
void FrequencySaveView::focus() {
button_save_timestamp.focus();
if (error)
nav_.display_modal("Error", "File acces error !", ABORT, nullptr);
}
void FrequencySaveView::on_tick_second() {
rtcGetTime(&RTCD1, &datetime);
text_timestamp.set(to_string_dec_uint(datetime.month(), 2, '0') + "/" + to_string_dec_uint(datetime.day(), 2, '0') + " " +
to_string_dec_uint(datetime.hour(), 2, '0') + ":" + to_string_dec_uint(datetime.minute(), 2, '0'));
}
FrequencySaveView::~FrequencySaveView() {
time::signal_tick_second -= signal_token_tick_second;
save_freqman_file(frequencies);
}
FrequencySaveView::FrequencySaveView(
NavigationView& nav,
const rf::Frequency value
) {
) : nav_ (nav),
value_ (value)
{
error = !load_freqman_file(frequencies);
signal_token_tick_second = time::signal_tick_second += [this]() {
this->on_tick_second();
};
add_children({ {
&big_display,
&text_save,
&button_save_name,
&button_save_timestamp,
&text_timestamp,
&button_cancel
} });
on_tick_second();
big_display.set(value);
button_save_name.on_select = [this, &nav](Button&) {
on_save_name(nav);
};
button_save_timestamp.on_select = [this, &nav](Button&) {
on_save_timestamp(nav);
};
button_cancel.on_select = [this, &nav](Button&) {
nav.pop();
};
}
void FrequencyLoadView::setup_list() {
size_t n;
menu_view.clear();
for (n = 0; n < frequencies.size(); n++) {
menu_view.add_item({ freqman_item_string(frequencies[n]), ui::Color::white(), [this](){ on_frequency_select(); } });
}
menu_view.set_parent_rect({ 0, 0, 240, 216 });
menu_view.set_highlighted(menu_view.highlighted()); // Refresh
}
void FrequencyLoadView::on_frequency_select() {
nav_.pop();
if (on_changed) on_changed(frequencies[menu_view.highlighted()].value);
}
void FrequencyLoadView::focus() {
menu_view.focus();
if (error)
nav_.display_modal("Error", "File acces error !", ABORT, nullptr);
}
FrequencyLoadView::FrequencyLoadView(
NavigationView& nav
) : nav_ (nav)
{
error = !load_freqman_file(frequencies);
add_children({ {
&menu_view,
&button_cancel
} });
setup_list();
button_cancel.on_select = [this, &nav](Button&) {
nav.pop();
};
}
void FrequencyLoadView::focus() {
button_exit.focus();
void FreqManView::on_frequency_select() {
button_edit_freq.focus();
}
FrequencyLoadView::FrequencyLoadView(
NavigationView& nav,
const rf::Frequency value
) {
void FreqManView::on_edit_freq(rf::Frequency f) {
frequencies[menu_view.highlighted()].value = f;
setup_list();
}
add_children({ {
&button_exit
} });
void FreqManView::on_edit_desc(NavigationView& nav) {
char desc_buffer[32] = { 0 };
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
strcpy(desc_buffer, frequencies[menu_view.highlighted()].description.c_str());
textentry(nav, desc_buffer, 28, [this, &desc_buffer](char * buffer) {
frequencies[menu_view.highlighted()].description = buffer;
setup_list();
});
}
void FreqManView::on_delete() {
frequencies.erase(frequencies.begin() + menu_view.highlighted());
setup_list();
}
void FreqManView::setup_list() {
size_t n;
menu_view.clear();
for (n = 0; n < frequencies.size(); n++) {
menu_view.add_item({ freqman_item_string(frequencies[n]), ui::Color::white(), [this](){ on_frequency_select(); } });
}
menu_view.set_parent_rect({ 0, 0, 240, 168 });
menu_view.set_highlighted(menu_view.highlighted()); // Refresh
}
void FreqManView::focus() {
button_exit.focus();
menu_view.focus();
if (error)
nav_.display_modal("Error", "File acces error !", ABORT, nullptr);
}
FreqManView::~FreqManView() {
save_freqman_file(frequencies);
}
FreqManView::FreqManView(
NavigationView& nav
) {
) : nav_ (nav)
{
error = !load_freqman_file(frequencies);
add_children({ {
&menu_view,
&button_edit_freq,
&button_edit_desc,
&button_del,
&button_exit
} });
size_t n = 0;
for(auto& text : text_list) {
add_child(&text);
text.set_parent_rect({
static_cast<Coord>(0),
static_cast<Coord>(16 + (n * 16)),
240, 16
});
const std::string label {
(char)(n + 0x30)
setup_list();
button_edit_freq.on_select = [this, &nav](Button&) {
auto new_view = nav.push<FrequencyKeypadView>(frequencies[menu_view.highlighted()].value);
new_view->on_changed = [this](rf::Frequency f) {
on_edit_freq(f);
};
text.set(label);
n++;
}
};
button_edit_desc.on_select = [this, &nav](Button&) {
on_edit_desc(nav);
};
button_del.on_select = [this, &nav](Button&) {
nav.push<ModalMessageView>("Confirm", "Are you sure ?", YESNO,
[this](bool choice) {
if (choice) {
on_delete();
}
}
);
};
button_exit.on_select = [this, &nav](Button&) {
nav.pop();

View File

@ -25,35 +25,57 @@
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_textentry.hpp"
#include "freqman.hpp"
#include "time.hpp"
namespace ui {
class FrequencySaveView : public View {
public:
FrequencySaveView(NavigationView& nav, const rf::Frequency value);
//~FrequencySaveView();
~FrequencySaveView();
void focus() override;
std::string title() const override { return "Save frequency"; };
private:
NavigationView& nav_;
bool error = false;
char desc_buffer[32] = { 0 };
rtc::RTC datetime;
rf::Frequency value_;
void on_save_name(NavigationView& nav);
void on_save_timestamp(NavigationView& nav);
void on_tick_second();
std::vector<freqman_entry> frequencies;
SignalToken signal_token_tick_second;
BigFrequency big_display {
{ 4, 2 * 16, 28 * 8, 32 },
0
};
Text text_save {
{ 72, 124, 10 * 8, 16 },
"Save with:",
{ 88, 120, 8 * 8, 16 },
"Save as:",
};
Button button_save_name {
{ 72, 144, 96, 32 },
"Name"
"Name (set)"
};
Button button_save_timestamp {
{ 72, 184, 96, 32 },
"Timestamp"
"Timestamp:"
};
Text text_timestamp {
{ 76, 220, 11 * 8, 16 },
"MM/DD HH:MM",
};
Button button_cancel {
@ -64,34 +86,69 @@ private:
class FrequencyLoadView : public View {
public:
FrequencyLoadView(NavigationView& nav, const rf::Frequency value);
//~FrequencySaveView();
std::function<void(rf::Frequency)> on_changed;
FrequencyLoadView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Load frequency"; };
private:
Button button_exit {
NavigationView& nav_;
bool error = false;
void on_frequency_select();
void setup_list();
std::vector<freqman_entry> frequencies;
MenuView menu_view;
Button button_cancel {
{ 72, 264, 96, 32 },
"Exit"
"Cancel"
};
};
class FreqManView : public View {
public:
FreqManView(NavigationView& nav);
//~FreqManView();
~FreqManView();
void focus() override;
std::string title() const override { return "Freq. manager"; };
private:
std::array<Text, 10> text_list;
NavigationView& nav_;
bool error = false;
void on_frequency_select();
void on_edit_freq(rf::Frequency f);
void on_edit_desc(NavigationView& nav);
void on_delete();
void setup_list();
std::vector<freqman_entry> frequencies;
MenuView menu_view { true };
Button button_edit_freq {
{ 52, 194, 106, 30 },
"Edit freq."
};
Button button_edit_desc {
{ 52, 192 + 32, 106, 30 },
"Edit desc."
};
Button button_del {
{ 168, 192, 64, 64 },
"Del"
};
Button button_exit {
{ 72, 264, 96, 32 },
{ 168, 264, 64, 32 },
"Exit"
};
};

View File

@ -142,7 +142,7 @@ void HandWriteView::clear_zone(const Color color, const bool flash) {
color
);
if (flash) {
flash_timer = 4;
flash_timer = 8;
} else {
// Draw grid
_painter->draw_rectangle(
@ -239,7 +239,7 @@ void HandWriteView::guess_letter() {
}
} else {
// Short tap is space
txtinput[txtidx++] = ' ';
char_add(' ');
clear_zone(Color::green(), true); // Green flash
}
update_text();

View File

@ -71,7 +71,7 @@ private:
void update_text();
Text text_input {
{ 8, 0, 224, 16 }
{ 8, 0, 232, 16 }
};
std::array<Button, 10> num_buttons;

View File

@ -46,7 +46,7 @@ void MenuItemView::unhighlight() {
void MenuItemView::paint(Painter& painter) {
const auto r = screen_rect();
const auto paint_style = (highlighted() && parent()->has_focus()) ? style().invert() : style();
const auto paint_style = (highlighted() && (parent()->has_focus() || keep_highlight_)) ? style().invert() : style();
const auto font_height = paint_style.font.line_height();
@ -75,15 +75,18 @@ void MenuItemView::paint(Painter& painter) {
/* MenuView **************************************************************/
MenuView::MenuView() {
MenuView::MenuView(
bool keep_highlight
) : keep_highlight_ { keep_highlight }
{
set_focusable(true);
add_child(&arrow_more);
signal_token_tick_second = time::signal_tick_second += [this]() {
this->on_tick_second();
};
arrow_more.set_parent_rect( { 216, 320 - 16 - 24, 16, 16 } );
add_child(&arrow_more);
arrow_more.id = 1; // Special flag
arrow_more.set_focusable(false);
arrow_more.set_foreground(Color::black());
}
@ -97,39 +100,47 @@ void MenuView::on_tick_second() {
arrow_more.set_foreground(Color::white());
else
arrow_more.set_foreground(Color::black());
blink_ = !blink_;
arrow_more.set_dirty();
}
void MenuView::clear() {
children_.erase(children_.begin() + 1, children_.end());
}
void MenuView::add_item(const MenuItem item) {
add_child(new MenuItemView { item });
add_child(new MenuItemView { item, keep_highlight_ });
}
void MenuView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
displayed_max_ = new_parent_rect.size.h / 24;
arrow_more.set_parent_rect( { 228, (Coord)(displayed_max_ * item_height), 8, 8 } );
update_items();
}
void MenuView::update_items() {
constexpr size_t item_height = 24;
size_t i = 0;
Coord y_pos;
int32_t y_pos;
if ((children_.size() - 1) > MENU_MAX + offset_)
if ((children_.size() - 1) > displayed_max_ + offset_) {
more_ = true;
else
blink_ = true;
} else
more_ = false;
for (auto child : children_) {
if (i) { // Skip arrow widget
y_pos = (i - 1 - offset_) * item_height;
if (!child->id) {
y_pos = (i - offset_ - 1) * item_height;
child->set_parent_rect({
{ 0, y_pos },
{ size().w, item_height }
{ size().w, (Coord)item_height }
});
if ((y_pos < 0) || (y_pos >= (320 - 16 - 24 - 16)))
if ((y_pos < 0) || (y_pos > (Coord)(screen_rect().size.h - item_height)))
child->hidden(true);
else
child->hidden(false);
@ -142,20 +153,24 @@ MenuItemView* MenuView::item_view(size_t index) const {
/* TODO: Terrible cast! Take it as a sign I must be doing something
* shamefully wrong here, right?
*/
return static_cast<MenuItemView*>(children_[index + 1]); // Skip arrow widget
return static_cast<MenuItemView*>(children_[index + 1]);
}
size_t MenuView::highlighted() const {
return highlighted_;
}
bool MenuView::set_highlighted(const size_t new_value) {
if( new_value >= (children_.size() - 1) ) // Skip arrow widget
bool MenuView::set_highlighted(int32_t new_value) {
int32_t item_count = (int32_t)children_.size() - 1;
if (new_value < 0)
return false;
if ((new_value > offset_) && ((new_value - offset_ + 1) >= MENU_MAX)) {
if (new_value >= item_count)
new_value = item_count - 1;
if ((new_value > offset_) && ((new_value - offset_ + 1) >= displayed_max_)) {
// Shift MenuView up
offset_ = new_value - MENU_MAX + 1;
offset_ = new_value - displayed_max_ + 1;
update_items();
} else if (new_value < offset_) {
// Shift MenuView down
@ -175,7 +190,7 @@ void MenuView::on_focus() {
}
void MenuView::on_blur() {
item_view(highlighted())->unhighlight();
if (!keep_highlight_) item_view(highlighted())->unhighlight();
}
bool MenuView::on_key(const KeyEvent key) {

View File

@ -33,8 +33,6 @@
#include <string>
#include <functional>
#define MENU_MAX 11
namespace ui {
struct MenuItem {
@ -50,8 +48,10 @@ struct MenuItem {
class MenuItemView : public Widget {
public:
MenuItemView(
MenuItem item
) : item(item)
MenuItem item,
bool keep_highlight
) : item(item),
keep_highlight_ { keep_highlight }
{
}
@ -63,16 +63,19 @@ public:
private:
const MenuItem item;
bool keep_highlight_ = false;
};
class MenuView : public View {
public:
std::function<void(void)> on_left;
MenuView();
MenuView(bool keep_highlight = false);
~MenuView();
void add_item(const MenuItem item);
void clear();
template<size_t N>
void add_items(const std::array<MenuItem, N>& items) {
@ -86,7 +89,7 @@ public:
MenuItemView* item_view(size_t index) const;
size_t highlighted() const;
bool set_highlighted(const size_t new_value);
bool set_highlighted(int32_t new_value);
void on_focus() override;
void on_blur() override;
@ -98,17 +101,21 @@ private:
void update_items();
void on_tick_second();
bool keep_highlight_ = false;
SignalToken signal_token_tick_second;
Image arrow_more {
{ 216, 320 - 16 - 24, 16, 16 },
{ 228, 320 - 8, 8, 8 },
&bitmap_more,
Color::white(),
Color::black()
};
const size_t item_height = 24;
bool blink_ = false;
bool more_ = false;
size_t displayed_max_;
size_t highlighted_ { 0 };
size_t offset_ { 0 };
};

View File

@ -76,7 +76,7 @@ SystemStatusView::SystemStatusView() {
&sd_card_status_view,
} });
if (portapack::persistent_memory::ui_config_textentry())
if (!portapack::persistent_memory::ui_config_textentry())
button_textentry.set_bitmap(&bitmap_keyboard);
else
button_textentry.set_bitmap(&bitmap_unistroke);
@ -326,6 +326,16 @@ UtilitiesView::UtilitiesView(NavigationView& nav) {
}
/* SystemMenuView ********************************************************/
void SystemMenuView::hackrf_mode(NavigationView& nav) {
nav.push<ModalMessageView>("Confirm", "Switch to HackRF mode ?", YESNO,
[this](bool choice) {
if (choice) {
EventDispatcher::request_stop();
}
}
);
}
SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items<11>({ {
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(); } },
@ -339,7 +349,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
//{ "Analyze", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Setup", ui::Color::white(), [&nav](){ nav.push<SetupMenuView>(); } },
//{ "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } },
{ "HackRF mode", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
{ "HackRF mode", ui::Color::white(), [this, &nav](){ hackrf_mode(nav); } },
{ "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } }
} });
@ -399,28 +409,6 @@ Context& SystemView::context() const {
return context_;
}
/* HackRFFirmwareView ****************************************************/
HackRFFirmwareView::HackRFFirmwareView(NavigationView& nav) {
button_yes.on_select = [](Button&){
EventDispatcher::request_stop();
};
button_no.on_select = [&nav](Button&){
nav.pop();
};
add_children({ {
&text_title,
&text_description_1,
&text_description_2,
&text_description_3,
&text_description_4,
&button_yes,
&button_no,
} });
}
/* ***********************************************************************/
void BMPView::focus() {
@ -490,10 +478,6 @@ PlayDeadView::PlayDeadView(NavigationView& nav) {
};
}
void HackRFFirmwareView::focus() {
button_no.focus();
}
/* NotImplementedView ****************************************************/
NotImplementedView::NotImplementedView(NavigationView& nav) {
@ -563,7 +547,7 @@ ModalMessageView::ModalMessageView(
}
void ModalMessageView::paint(Painter&) {
portapack::display.drawBMP({96, 64}, modal_warning_bmp, false);
portapack::display.drawBMP({ 100, 64 }, modal_warning_bmp, false);
}
void ModalMessageView::focus() {

View File

@ -223,6 +223,8 @@ public:
class SystemMenuView : public MenuView {
public:
SystemMenuView(NavigationView& nav);
private:
void hackrf_mode(NavigationView& nav);
};
class SystemView : public View {
@ -240,49 +242,6 @@ private:
Context& context_;
};
class HackRFFirmwareView : public View {
public:
HackRFFirmwareView(NavigationView& nav);
void focus() override;
private:
Text text_title {
{ 76, 4 * 16, 19 * 8, 16 },
"HackRF Mode"
};
Text text_description_1 {
{ 4, 7 * 16, 19 * 8, 16 },
"Run stock HackRF firmware and"
};
Text text_description_2 {
{ 12, 8 * 16, 19 * 8, 16 },
"disable PortaPack until the"
};
Text text_description_3 {
{ 4, 9 * 16, 19 * 8, 16 },
"unit is reset or disconnected"
};
Text text_description_4 {
{ 76, 10 * 16, 19 * 8, 16 },
"from power?"
};
Button button_yes {
{ 4 * 8, 13 * 16, 8 * 8, 24 },
"Yes",
};
Button button_no {
{ 18 * 8, 13 * 16, 8 * 8, 24 },
"No",
};
};
class NotImplementedView : public View {
public:
NotImplementedView(NavigationView& nav);
@ -329,11 +288,12 @@ private:
};
Button button_yes {
{ 40, 13 * 16, 64, 24 },
{ 5 * 8, 13 * 16, 8 * 8, 48 },
"YES",
};
Button button_no {
{ 152, 13 * 16, 64, 24 },
{ 17 * 8, 13 * 16, 8 * 8, 48 },
"NO",
};
};

View File

@ -158,7 +158,10 @@ FrequencyKeypadView::FrequencyKeypadView(
nav.push<FrequencySaveView>(this->value());
};
button_load.on_select = [this, &nav](Button&) {
nav.push<FrequencyLoadView>(this->value());
auto load_view = nav.push<FrequencyLoadView>();
load_view->on_changed = [this](rf::Frequency value) {
set_value(value);
};
};
button_close.on_select = [this, &nav](Button&) {

View File

@ -24,16 +24,19 @@
namespace ui {
bool textentry(NavigationView& nav, char * str, size_t max_length) {
bool textentry(NavigationView& nav, char * str, const size_t max_length, const std::function<void(char *)> on_done) {
if (portapack::persistent_memory::ui_config_textentry() == 0) {
auto an_view = nav.push<AlphanumView>(str, max_length);
an_view->on_changed = [str, max_length](char * value) {
memcpy(str, value, max_length + 1);
auto te_view = nav.push<AlphanumView>(str, max_length);
te_view->on_changed = [str, max_length, on_done](char * value) {
//memcpy(str, value, max_length + 1);
if (on_done) on_done(value);
};
} else {
auto an_view = nav.push<HandWriteView>(str, max_length);
an_view->on_changed = [str, max_length](char * value) {
memcpy(str, value, max_length + 1);
auto te_view = nav.push<HandWriteView>(str, max_length);
te_view->on_changed = [str, max_length, on_done](char * value) {
//memcpy(str, value, max_length + 1);
if (on_done) on_done(value);
};
}

View File

@ -20,14 +20,21 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_TEXTENTRY_H__
#define __UI_TEXTENTRY_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_handwrite.hpp"
#include "ui_alphanum.hpp"
#include "portapack_persistent_memory.hpp"
// TODO: Make class
namespace ui {
bool textentry(NavigationView& nav, char * str, size_t max_length);
bool textentry(NavigationView& nav, char * str, size_t max_length, const std::function<void(char *)> on_done = nullptr);
} /* namespace ui */
#endif/*__UI_TEXTENTRY_H__*/

View File

@ -316,7 +316,7 @@ Text::Text(
Rect parent_rect,
std::string text
) : Widget { parent_rect },
text { text }
text_ { text }
{
}
@ -327,10 +327,14 @@ Text::Text(
}
void Text::set(const std::string value) {
text = value;
text_ = value;
set_dirty();
}
std::string Text::text() const {
return text_;
}
void Text::paint(Painter& painter) {
const auto rect = screen_rect();
const auto s = style();
@ -340,7 +344,7 @@ void Text::paint(Painter& painter) {
painter.draw_string(
rect.pos,
s,
text
text_
);
}

View File

@ -104,6 +104,7 @@ public:
void set_style(const Style* new_style);
const Style& style() const;
uint16_t id = 0;
// State management methods.
void set_dirty();
@ -115,8 +116,6 @@ public:
bool highlighted() const;
void set_highlighted(const bool value);
uint16_t id = 0;
protected:
void dirty_overlapping_children_in_rect(const Rect& child_rect);
@ -192,18 +191,19 @@ private:
class Text : public Widget {
public:
Text(
) : text { "" } {
) : text_ { "" } {
}
Text(Rect parent_rect, std::string text);
Text(Rect parent_rect);
void set(const std::string value);
std::string text() const;
void paint(Painter& painter) override;
private:
std::string text;
std::string text_;
};
class BigFrequency : public Widget {
@ -265,7 +265,6 @@ public:
}
void set_text(const std::string value);
// std::string text() const;
bool set_value(const bool value);
bool value() const;

Binary file not shown.