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 afsk.cpp
rds.cpp rds.cpp
ctcss.cpp ctcss.cpp
freqman.cpp
${COMMON}/lcd_ili9341.cpp ${COMMON}/lcd_ili9341.cpp
${COMMON}/ui.cpp ${COMMON}/ui.cpp
${COMMON}/ui_text.cpp ${COMMON}/ui_text.cpp
${COMMON}/ui_widget.cpp ${COMMON}/ui_widget.cpp
${COMMON}/ui_painter.cpp ${COMMON}/ui_painter.cpp
${COMMON}/ui_focus.cpp ${COMMON}/ui_focus.cpp
${COMMON}/msgpack.cpp
ui_about.cpp ui_about.cpp
ui_adsbtx.cpp ui_adsbtx.cpp
ui_afsksetup.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 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 .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 __/common/png_writer.obj: __/common/png_writer.cpp.obj
.PHONY : __/common/png_writer.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 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 .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 hackrf_cpld_data.obj: hackrf_cpld_data.cpp.obj
.PHONY : hackrf_cpld_data.obj .PHONY : hackrf_cpld_data.obj
@ -5316,9 +5316,6 @@ help:
@echo "... __/common/message_queue.obj" @echo "... __/common/message_queue.obj"
@echo "... __/common/message_queue.i" @echo "... __/common/message_queue.i"
@echo "... __/common/message_queue.s" @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.obj"
@echo "... __/common/png_writer.i" @echo "... __/common/png_writer.i"
@echo "... __/common/png_writer.s" @echo "... __/common/png_writer.s"
@ -5415,6 +5412,9 @@ help:
@echo "... filewriter.obj" @echo "... filewriter.obj"
@echo "... filewriter.i" @echo "... filewriter.i"
@echo "... filewriter.s" @echo "... filewriter.s"
@echo "... freqman.obj"
@echo "... freqman.i"
@echo "... freqman.s"
@echo "... hackrf_cpld_data.obj" @echo "... hackrf_cpld_data.obj"
@echo "... hackrf_cpld_data.i" @echo "... hackrf_cpld_data.i"
@echo "... hackrf_cpld_data.s" @echo "... hackrf_cpld_data.s"

View file

@ -76,26 +76,18 @@ static constexpr Bitmap bitmap_speaker {
}; };
static constexpr uint8_t bitmap_more_data[] = { static constexpr uint8_t bitmap_more_data[] = {
0x00, 0x00, 0x18,
0xC0, 0x03, 0x18,
0xC0, 0x03, 0x18,
0xC0, 0x03, 0x18,
0xC0, 0x03, 0xFF,
0xC0, 0x03, 0x7E,
0xC0, 0x03, 0x3C,
0xC0, 0x03, 0x18,
0xC6, 0x63,
0xCF, 0xF3,
0xDE, 0x7B,
0xFC, 0x3F,
0xF8, 0x1F,
0xF0, 0x0F,
0xE0, 0x07,
0xC0, 0x03
}; };
static constexpr Bitmap bitmap_more { static constexpr Bitmap bitmap_more {
{ 16, 16 }, bitmap_more_data { 8, 8 }, bitmap_more_data
}; };
static constexpr uint8_t bitmap_rssipwm_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: // Color bitmaps generated with:
// Gimp image > indexed colors (16), then "xxd -i *.bmp" // 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: Imperial in whipcalc
//TEST: Numbers //TEST: Numbers
//TEST: Jammer //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: POCSAG RX sometimes misses the first codeword after SYNC
//BUG: Check AFSK transmit end, skips last bits ? //BUG: Check AFSK transmit end, skips last bits ?
//BUG: RDS doesn't stop baseband when stopping tx ? //BUG: RDS doesn't stop baseband when stopping tx ?
//TODO: File browser ?
//TODO: Mousejack ? //TODO: Mousejack ?
//TODO: Waveform widget ? //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: Move frequencykeypad from ui_receiver to ui_widget (used everywhere)
//TODO: ADS-B draw trajectory + GPS coordinates + scale, and playback //TODO: ADS-B draw trajectory + GPS coordinates + scale, and playback
//TODO: Finish EPAR tx //TODO: Finish EPAR tx
//TODO: Wav visualizer
//TODO: Analog TV tx with camcorder font character generator //TODO: Analog TV tx with camcorder font character generator
//TODO: Test dual tone in proc_tones and remove proc_dtmf_tx //TODO: Test dual tone in proc_tones and remove proc_dtmf_tx
//TODO: Morse coder for foxhunts //TODO: Morse coder for foxhunts
//TODO: Make Morse coder and Whistle use proc_tones //TODO: Make Morse coder and Whistle use proc_tones
//TODO: RDS multiple groups (sequence) //TODO: RDS multiple groups (sequence)
//TODO: Frequency manager
//TODO: IQ replay //TODO: IQ replay
//TODO: Use ModalMessageView confirmation for TX ? //TODO: Use ModalMessageView confirmation for TX ?
//TODO: Show address/data bit fields in OOK TX //TODO: Show address/data bit fields in OOK TX

View file

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

View file

@ -24,7 +24,6 @@
#include "portapack.hpp" #include "portapack.hpp"
#include "event_m0.hpp" #include "event_m0.hpp"
#include "portapack_shared_memory.hpp"
#include <cstring> #include <cstring>
@ -32,73 +31,197 @@ using namespace portapack;
namespace ui { 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() { void FrequencySaveView::focus() {
button_save_timestamp.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( FrequencySaveView::FrequencySaveView(
NavigationView& nav, NavigationView& nav,
const rf::Frequency value 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({ { add_children({ {
&big_display, &big_display,
&text_save, &text_save,
&button_save_name, &button_save_name,
&button_save_timestamp, &button_save_timestamp,
&text_timestamp,
&button_cancel &button_cancel
} }); } });
on_tick_second();
big_display.set(value); 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&) { button_cancel.on_select = [this, &nav](Button&) {
nav.pop(); nav.pop();
}; };
} }
void FrequencyLoadView::focus() { void FreqManView::on_frequency_select() {
button_exit.focus(); button_edit_freq.focus();
} }
FrequencyLoadView::FrequencyLoadView( void FreqManView::on_edit_freq(rf::Frequency f) {
NavigationView& nav, frequencies[menu_view.highlighted()].value = f;
const rf::Frequency value setup_list();
) { }
add_children({ { void FreqManView::on_edit_desc(NavigationView& nav) {
&button_exit char desc_buffer[32] = { 0 };
} });
button_exit.on_select = [this, &nav](Button&) { strcpy(desc_buffer, frequencies[menu_view.highlighted()].description.c_str());
nav.pop(); 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() { 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( FreqManView::FreqManView(
NavigationView& nav NavigationView& nav
) { ) : nav_ (nav)
{
error = !load_freqman_file(frequencies);
add_children({ { add_children({ {
&menu_view,
&button_edit_freq,
&button_edit_desc,
&button_del,
&button_exit &button_exit
} }); } });
size_t n = 0; setup_list();
for(auto& text : text_list) {
add_child(&text); button_edit_freq.on_select = [this, &nav](Button&) {
text.set_parent_rect({ auto new_view = nav.push<FrequencyKeypadView>(frequencies[menu_view.highlighted()].value);
static_cast<Coord>(0), new_view->on_changed = [this](rf::Frequency f) {
static_cast<Coord>(16 + (n * 16)), on_edit_freq(f);
240, 16
});
const std::string label {
(char)(n + 0x30)
}; };
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&) { button_exit.on_select = [this, &nav](Button&) {
nav.pop(); nav.pop();

View file

@ -25,35 +25,57 @@
#include "ui_painter.hpp" #include "ui_painter.hpp"
#include "ui_menu.hpp" #include "ui_menu.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_textentry.hpp"
#include "freqman.hpp"
#include "time.hpp"
namespace ui { namespace ui {
class FrequencySaveView : public View { class FrequencySaveView : public View {
public: public:
FrequencySaveView(NavigationView& nav, const rf::Frequency value); FrequencySaveView(NavigationView& nav, const rf::Frequency value);
//~FrequencySaveView(); ~FrequencySaveView();
void focus() override; void focus() override;
std::string title() const override { return "Save frequency"; }; std::string title() const override { return "Save frequency"; };
private: 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 { BigFrequency big_display {
{ 4, 2 * 16, 28 * 8, 32 }, { 4, 2 * 16, 28 * 8, 32 },
0 0
}; };
Text text_save { Text text_save {
{ 72, 124, 10 * 8, 16 }, { 88, 120, 8 * 8, 16 },
"Save with:", "Save as:",
}; };
Button button_save_name { Button button_save_name {
{ 72, 144, 96, 32 }, { 72, 144, 96, 32 },
"Name" "Name (set)"
}; };
Button button_save_timestamp { Button button_save_timestamp {
{ 72, 184, 96, 32 }, { 72, 184, 96, 32 },
"Timestamp" "Timestamp:"
};
Text text_timestamp {
{ 76, 220, 11 * 8, 16 },
"MM/DD HH:MM",
}; };
Button button_cancel { Button button_cancel {
@ -64,34 +86,69 @@ private:
class FrequencyLoadView : public View { class FrequencyLoadView : public View {
public: public:
FrequencyLoadView(NavigationView& nav, const rf::Frequency value); std::function<void(rf::Frequency)> on_changed;
//~FrequencySaveView();
FrequencyLoadView(NavigationView& nav);
void focus() override; void focus() override;
std::string title() const override { return "Load frequency"; }; std::string title() const override { return "Load frequency"; };
private: 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 }, { 72, 264, 96, 32 },
"Exit" "Cancel"
}; };
}; };
class FreqManView : public View { class FreqManView : public View {
public: public:
FreqManView(NavigationView& nav); FreqManView(NavigationView& nav);
//~FreqManView(); ~FreqManView();
void focus() override; void focus() override;
std::string title() const override { return "Freq. manager"; }; std::string title() const override { return "Freq. manager"; };
private: 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 { Button button_exit {
{ 72, 264, 96, 32 }, { 168, 264, 64, 32 },
"Exit" "Exit"
}; };
}; };

View file

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

View file

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

View file

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

View file

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

View file

@ -76,7 +76,7 @@ SystemStatusView::SystemStatusView() {
&sd_card_status_view, &sd_card_status_view,
} }); } });
if (portapack::persistent_memory::ui_config_textentry()) if (!portapack::persistent_memory::ui_config_textentry())
button_textentry.set_bitmap(&bitmap_keyboard); button_textentry.set_bitmap(&bitmap_keyboard);
else else
button_textentry.set_bitmap(&bitmap_unistroke); button_textentry.set_bitmap(&bitmap_unistroke);
@ -326,6 +326,16 @@ UtilitiesView::UtilitiesView(NavigationView& nav) {
} }
/* SystemMenuView ********************************************************/ /* 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) { SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items<11>({ { add_items<11>({ {
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(); } }, { "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>(); } }, //{ "Analyze", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Setup", ui::Color::white(), [&nav](){ nav.push<SetupMenuView>(); } }, { "Setup", ui::Color::white(), [&nav](){ nav.push<SetupMenuView>(); } },
//{ "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } }, //{ "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>(); } } { "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } }
} }); } });
@ -399,28 +409,6 @@ Context& SystemView::context() const {
return context_; 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() { void BMPView::focus() {
@ -490,10 +478,6 @@ PlayDeadView::PlayDeadView(NavigationView& nav) {
}; };
} }
void HackRFFirmwareView::focus() {
button_no.focus();
}
/* NotImplementedView ****************************************************/ /* NotImplementedView ****************************************************/
NotImplementedView::NotImplementedView(NavigationView& nav) { NotImplementedView::NotImplementedView(NavigationView& nav) {
@ -563,7 +547,7 @@ ModalMessageView::ModalMessageView(
} }
void ModalMessageView::paint(Painter&) { 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() { void ModalMessageView::focus() {

View file

@ -223,6 +223,8 @@ public:
class SystemMenuView : public MenuView { class SystemMenuView : public MenuView {
public: public:
SystemMenuView(NavigationView& nav); SystemMenuView(NavigationView& nav);
private:
void hackrf_mode(NavigationView& nav);
}; };
class SystemView : public View { class SystemView : public View {
@ -240,49 +242,6 @@ private:
Context& context_; 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 { class NotImplementedView : public View {
public: public:
NotImplementedView(NavigationView& nav); NotImplementedView(NavigationView& nav);
@ -329,11 +288,12 @@ private:
}; };
Button button_yes { Button button_yes {
{ 40, 13 * 16, 64, 24 }, { 5 * 8, 13 * 16, 8 * 8, 48 },
"YES", "YES",
}; };
Button button_no { Button button_no {
{ 152, 13 * 16, 64, 24 }, { 17 * 8, 13 * 16, 8 * 8, 48 },
"NO", "NO",
}; };
}; };

View file

@ -158,7 +158,10 @@ FrequencyKeypadView::FrequencyKeypadView(
nav.push<FrequencySaveView>(this->value()); nav.push<FrequencySaveView>(this->value());
}; };
button_load.on_select = [this, &nav](Button&) { 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&) { button_close.on_select = [this, &nav](Button&) {

View file

@ -24,16 +24,19 @@
namespace ui { 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) { if (portapack::persistent_memory::ui_config_textentry() == 0) {
auto an_view = nav.push<AlphanumView>(str, max_length); auto te_view = nav.push<AlphanumView>(str, max_length);
an_view->on_changed = [str, max_length](char * value) { te_view->on_changed = [str, max_length, on_done](char * value) {
memcpy(str, value, max_length + 1); //memcpy(str, value, max_length + 1);
if (on_done) on_done(value);
}; };
} else { } else {
auto an_view = nav.push<HandWriteView>(str, max_length); auto te_view = nav.push<HandWriteView>(str, max_length);
an_view->on_changed = [str, max_length](char * value) { te_view->on_changed = [str, max_length, on_done](char * value) {
memcpy(str, value, max_length + 1); //memcpy(str, value, max_length + 1);
if (on_done) on_done(value);
}; };
} }

View file

@ -20,14 +20,21 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#ifndef __UI_TEXTENTRY_H__
#define __UI_TEXTENTRY_H__
#include "ui.hpp" #include "ui.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_handwrite.hpp" #include "ui_handwrite.hpp"
#include "ui_alphanum.hpp" #include "ui_alphanum.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
// TODO: Make class
namespace ui { 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 */ } /* namespace ui */
#endif/*__UI_TEXTENTRY_H__*/

View file

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

View file

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

Binary file not shown.