Add software
This commit is contained in:
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 "ui_alphanum.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ui {
|
||||
|
||||
void AlphanumView::paint(Painter&) {
|
||||
draw_cursor();
|
||||
}
|
||||
|
||||
AlphanumView::AlphanumView(
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length
|
||||
) : TextEntryView(nav, str, max_length)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
add_children({
|
||||
&button_mode,
|
||||
&text_raw,
|
||||
&field_raw
|
||||
});
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
};
|
||||
|
||||
n = 0;
|
||||
for (auto& button : buttons) {
|
||||
button.id = n;
|
||||
button.on_highlight = [this](Button& button) {
|
||||
focused_button = button.id;
|
||||
};
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
static_cast<Coord>((n % 5) * (240 / 5)),
|
||||
static_cast<Coord>((n / 5) * 38 + 24),
|
||||
240 / 5, 38
|
||||
});
|
||||
add_child(&button);
|
||||
n++;
|
||||
}
|
||||
|
||||
set_mode(mode);
|
||||
|
||||
button_mode.on_select = [this](Button&) {
|
||||
set_mode(mode + 1);
|
||||
};
|
||||
|
||||
field_raw.set_value('0');
|
||||
field_raw.on_select = [this](NumberField&) {
|
||||
char_add(field_raw.value());
|
||||
update_text();
|
||||
};
|
||||
|
||||
button_ok.on_select = [this, &nav](Button&) {
|
||||
if (on_changed)
|
||||
on_changed(_str);
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
update_text();
|
||||
}
|
||||
|
||||
void AlphanumView::set_mode(const uint32_t new_mode) {
|
||||
size_t n = 0;
|
||||
|
||||
if (new_mode < 3)
|
||||
mode = new_mode;
|
||||
else
|
||||
mode = 0;
|
||||
|
||||
const char * key_list = key_sets[mode].second;
|
||||
|
||||
for (auto& button : buttons) {
|
||||
const std::string label {
|
||||
key_list[n]
|
||||
};
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
|
||||
if (mode < 2)
|
||||
button_mode.set_text(key_sets[mode + 1].first);
|
||||
else
|
||||
button_mode.set_text(key_sets[0].first);
|
||||
}
|
||||
|
||||
void AlphanumView::on_button(Button& button) {
|
||||
const auto c = button.text()[0];
|
||||
|
||||
if (c == '<')
|
||||
char_delete();
|
||||
else
|
||||
char_add(c);
|
||||
|
||||
update_text();
|
||||
}
|
||||
|
||||
bool AlphanumView::on_encoder(const EncoderEvent delta) {
|
||||
focused_button += delta;
|
||||
if (focused_button < 0) {
|
||||
focused_button = buttons.size() - 1;
|
||||
}
|
||||
else if (focused_button >= (int16_t)buttons.size()) {
|
||||
focused_button = 0;
|
||||
}
|
||||
buttons[focused_button].focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef __ALPHANUM_H__
|
||||
#define __ALPHANUM_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AlphanumView : public TextEntryView {
|
||||
public:
|
||||
AlphanumView(NavigationView& nav, std::string& str, size_t max_length);
|
||||
|
||||
AlphanumView(const AlphanumView&) = delete;
|
||||
AlphanumView(AlphanumView&&) = delete;
|
||||
AlphanumView& operator=(const AlphanumView&) = delete;
|
||||
AlphanumView& operator=(AlphanumView&&) = delete;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
|
||||
private:
|
||||
const char * const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ' .<";
|
||||
const char * const keys_lower = "abcdefghijklmnopqrstuvwxyz' .<";
|
||||
const char * const keys_digit = "0123456789!\"#'()*+-/:;=>?@[\\]<";
|
||||
|
||||
const std::pair<std::string, const char *> key_sets[3] = {
|
||||
{ "Upper", keys_upper },
|
||||
{ "Lower", keys_lower },
|
||||
{ "Digit", keys_digit }
|
||||
};
|
||||
|
||||
int16_t focused_button = 0;
|
||||
uint32_t mode = 0; // Uppercase
|
||||
|
||||
void set_mode(const uint32_t new_mode);
|
||||
void on_button(Button& button);
|
||||
|
||||
std::array<Button, 30> buttons { };
|
||||
|
||||
Button button_mode {
|
||||
{ 21 * 8, 33 * 8, 8 * 8, 32 },
|
||||
""
|
||||
};
|
||||
|
||||
Text text_raw {
|
||||
{ 1 * 8, 33 * 8, 4 * 8, 16 },
|
||||
"Raw:"
|
||||
};
|
||||
NumberField field_raw {
|
||||
{ 5 * 8, 33 * 8 },
|
||||
3,
|
||||
{ 1, 255 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 10 * 8, 33 * 8, 9 * 8, 32 },
|
||||
"OK"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__ALPHANUM_H__*/
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "ui_audio.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ui {
|
||||
|
||||
void Audio::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int db_min = -96;
|
||||
constexpr int db_max = 0;
|
||||
constexpr int db_delta = db_max - db_min;
|
||||
const range_t<int> x_rms_range { 0, r.width() - 1 };
|
||||
const auto x_rms = x_rms_range.clip((rms_db_ - db_min) * r.width() / db_delta);
|
||||
const range_t<int> x_max_range { x_rms + 1, r.width() };
|
||||
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
|
||||
|
||||
const Rect r0 { r.left(), r.top(), x_rms, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::green()
|
||||
);
|
||||
|
||||
const Rect r1 { r.left() + x_rms, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::black()
|
||||
);
|
||||
|
||||
const Rect r2 { r.left() + x_rms + 1, r.top(), x_max - (x_rms + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
const Rect r3 { r.left() + x_max, r.top(), r.width() - x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::black()
|
||||
);
|
||||
}
|
||||
|
||||
void Audio::on_statistics_update(const AudioStatistics& statistics) {
|
||||
rms_db_ = statistics.rms_db;
|
||||
max_db_ = statistics.max_db;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_AUDIO_H__
|
||||
#define __UI_AUDIO_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class Audio : public Widget {
|
||||
public:
|
||||
Audio(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
rms_db_ { -120 },
|
||||
max_db_ { -120 }
|
||||
{
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int32_t rms_db_;
|
||||
int32_t max_db_;
|
||||
|
||||
MessageHandlerRegistration message_handler_statistics {
|
||||
Message::ID::AudioStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const AudioStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
|
||||
void on_statistics_update(const AudioStatistics& statistics);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__UI_AUDIO_H__*/
|
229
Software/portapack-mayhem/firmware/application/ui/ui_btngrid.cpp
Normal file
229
Software/portapack-mayhem/firmware/application/ui/ui_btngrid.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2019 Elia Yehuda (z4ziggy)
|
||||
*
|
||||
* 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 "ui_btngrid.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* BtnGridView **************************************************************/
|
||||
|
||||
BtnGridView::BtnGridView(
|
||||
Rect new_parent_rect,
|
||||
bool keep_highlight
|
||||
) : keep_highlight { keep_highlight }
|
||||
{
|
||||
set_parent_rect(new_parent_rect);
|
||||
|
||||
set_focusable(true);
|
||||
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
|
||||
add_child(&arrow_more);
|
||||
arrow_more.set_focusable(false);
|
||||
arrow_more.set_foreground(Color::black());
|
||||
}
|
||||
|
||||
BtnGridView::~BtnGridView() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void BtnGridView::set_max_rows(int rows) {
|
||||
rows_ = rows;
|
||||
}
|
||||
|
||||
int BtnGridView::rows() {
|
||||
return rows_;
|
||||
}
|
||||
|
||||
void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
displayed_max = (parent_rect().size().height() / button_h);
|
||||
arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * button_h), 8, 8 } );
|
||||
displayed_max *= rows_;
|
||||
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
button_w = 240 / rows_;
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new NewButton { };
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
|
||||
item->set_parent_rect({
|
||||
(int)(c % rows_) * button_w,
|
||||
(int)(c / rows_) * button_h,
|
||||
button_w, button_h
|
||||
});
|
||||
}
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void BtnGridView::set_arrow_enabled(bool new_value) {
|
||||
if(new_value){
|
||||
add_child(&arrow_more);
|
||||
}
|
||||
else{
|
||||
remove_child(&arrow_more);
|
||||
}
|
||||
};
|
||||
|
||||
void BtnGridView::on_tick_second() {
|
||||
if (more && blink)
|
||||
arrow_more.set_foreground(Color::white());
|
||||
else
|
||||
arrow_more.set_foreground(Color::black());
|
||||
|
||||
blink = !blink;
|
||||
|
||||
arrow_more.set_dirty();
|
||||
}
|
||||
|
||||
void BtnGridView::clear() {
|
||||
menu_items.clear();
|
||||
}
|
||||
|
||||
void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
|
||||
for (auto item : new_items) {
|
||||
menu_items.push_back(item);
|
||||
}
|
||||
update_items();
|
||||
}
|
||||
|
||||
void BtnGridView::update_items() {
|
||||
size_t i = 0;
|
||||
|
||||
if ((menu_items.size()) > (displayed_max + offset)) {
|
||||
more = true;
|
||||
blink = true;
|
||||
} else
|
||||
more = false;
|
||||
|
||||
for (NewButton* item : menu_item_views) {
|
||||
if (i >= menu_items.size()) break;
|
||||
|
||||
// Assign item data to NewButtons according to offset
|
||||
item->set_text(menu_items[i + offset].text);
|
||||
item->set_bitmap(menu_items[i + offset].bitmap);
|
||||
item->set_color(menu_items[i + offset].color);
|
||||
item->on_select = menu_items[i + offset].on_select;
|
||||
item->set_dirty();
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
NewButton* BtnGridView::item_view(size_t index) const {
|
||||
return menu_item_views[index];
|
||||
}
|
||||
|
||||
bool BtnGridView::set_highlighted(int32_t new_value) {
|
||||
int32_t item_count = (int32_t)menu_items.size();
|
||||
|
||||
if (new_value < 0)
|
||||
return false;
|
||||
|
||||
if (new_value >= item_count)
|
||||
new_value = item_count - 1;
|
||||
|
||||
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
|
||||
// Shift BtnGridView up
|
||||
highlighted_item = new_value;
|
||||
offset = new_value - displayed_max + rows_;
|
||||
update_items();
|
||||
} else if ((uint32_t)new_value < offset) {
|
||||
// Shift BtnGridView down
|
||||
highlighted_item = new_value;
|
||||
offset = (new_value / rows_) * rows_;
|
||||
update_items();
|
||||
} else {
|
||||
// Just update highlight
|
||||
highlighted_item = new_value;
|
||||
if (visible())
|
||||
item_view(highlighted_item - offset)->focus();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t BtnGridView::highlighted_index() {
|
||||
return highlighted_item;
|
||||
}
|
||||
|
||||
void BtnGridView::on_focus() {
|
||||
item_view(highlighted_item - offset)->focus();
|
||||
}
|
||||
|
||||
void BtnGridView::on_blur() {
|
||||
#if 0
|
||||
if (!keep_highlight)
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BtnGridView::on_key(const KeyEvent key) {
|
||||
switch(key) {
|
||||
case KeyEvent::Up:
|
||||
return set_highlighted(highlighted_item - rows_);
|
||||
|
||||
case KeyEvent::Down:
|
||||
return set_highlighted(highlighted_item + rows_);
|
||||
|
||||
case KeyEvent::Right:
|
||||
return set_highlighted(highlighted_item + 1);
|
||||
|
||||
case KeyEvent::Left:
|
||||
return set_highlighted(highlighted_item - 1);
|
||||
|
||||
case KeyEvent::Select:
|
||||
if( menu_items[highlighted_item].on_select ) {
|
||||
menu_items[highlighted_item].on_select();
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool BtnGridView::on_encoder(const EncoderEvent event) {
|
||||
return set_highlighted(highlighted_item + event);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
100
Software/portapack-mayhem/firmware/application/ui/ui_btngrid.hpp
Normal file
100
Software/portapack-mayhem/firmware/application/ui/ui_btngrid.hpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2019 Elia Yehuda (z4ziggy)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_BTNGRID_H__
|
||||
#define __UI_BTNGRID_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "bitmap.hpp"
|
||||
#include "signal.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace ui {
|
||||
|
||||
struct GridItem {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
const Bitmap* bitmap;
|
||||
std::function<void(void)> on_select;
|
||||
|
||||
// TODO: Prevent default-constructed GridItems.
|
||||
};
|
||||
|
||||
class BtnGridView : public View {
|
||||
public:
|
||||
BtnGridView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false);
|
||||
|
||||
~BtnGridView();
|
||||
|
||||
void add_items(std::initializer_list<GridItem> new_items);
|
||||
void set_max_rows(int rows);
|
||||
int rows();
|
||||
void clear();
|
||||
|
||||
NewButton* item_view(size_t index) const;
|
||||
|
||||
bool set_highlighted(int32_t new_value);
|
||||
uint32_t highlighted_index();
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void set_arrow_enabled(bool new_value);
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
|
||||
private:
|
||||
int rows_ { 3 };
|
||||
void update_items();
|
||||
void on_tick_second();
|
||||
|
||||
bool keep_highlight { false };
|
||||
|
||||
SignalToken signal_token_tick_second { };
|
||||
std::vector<GridItem> menu_items { };
|
||||
std::vector<NewButton*> menu_item_views { };
|
||||
|
||||
Image arrow_more {
|
||||
{ 228, 320 - 8, 8, 8 },
|
||||
&bitmap_more,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
int button_w = 240 / rows_;
|
||||
static constexpr int button_h = 48;
|
||||
bool blink = false;
|
||||
bool more = false;
|
||||
size_t displayed_max { 0 };
|
||||
size_t highlighted_item { 0 };
|
||||
size_t offset { 0 };
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_BTNGRID_H__*/
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "ui_channel.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ui {
|
||||
|
||||
void Channel::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int db_min = -96;
|
||||
constexpr int db_max = 0;
|
||||
constexpr int db_delta = db_max - db_min;
|
||||
const range_t<int> x_max_range { 0, r.width() - 1 };
|
||||
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
|
||||
|
||||
const Rect r0 { r.left(), r.top(), x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue()
|
||||
);
|
||||
|
||||
const Rect r1 { r.left() + x_max, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::white()
|
||||
);
|
||||
|
||||
const Rect r2 { r.left() + x_max + 1, r.top(), r.width() - (x_max + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::black()
|
||||
);
|
||||
}
|
||||
|
||||
void Channel::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
max_db_ = statistics.max_db;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_CHANNEL_H__
|
||||
#define __UI_CHANNEL_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class Channel : public Widget {
|
||||
public:
|
||||
Channel(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
max_db_ { -120 }
|
||||
{
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int32_t max_db_;
|
||||
|
||||
MessageHandlerRegistration message_handler_stats {
|
||||
Message::ID::ChannelStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__UI_CHANNEL_H__*/
|
@ -0,0 +1,962 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
namespace font {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t fixed_8x16_glyph_data[] = {
|
||||
|
||||
// Font based on a modded https://www.1001freefonts.com/8-bit-operator.font
|
||||
|
||||
// Index: 0 (0x00) Char: 0x0020 (' ')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 1 (0x01) Char: 0x0021 ('!')
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 2 (0x02) Char: 0x0022 ('"')
|
||||
0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 3 (0x03) Char: 0x0023 ('#')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0xFF, 0x66,
|
||||
0x66, 0x66, 0xFF, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 4 (0x04) Char: 0x0024 ('$')
|
||||
0x00, 0x00, 0x18, 0x18, 0x3C, 0x66, 0x66, 0x0C,
|
||||
0x18, 0x30, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x00,
|
||||
|
||||
// Index: 5 (0x05) Char: 0x0025 ('%')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x30,
|
||||
0x18, 0x0C, 0x06, 0x63, 0x61, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 6 (0x06) Char: 0x0026 ('&')
|
||||
0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x36, 0x1C,
|
||||
0x0E, 0x7B, 0x33, 0x33, 0x6E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 7 (0x07) Char: 0x0027 (''')
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 8 (0x08) Char: 0x0028 ('(')
|
||||
0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C,
|
||||
0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 9 (0x09) Char: 0x0029 (')')
|
||||
0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 10 (0x0A) Char: 0x002A ('*')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x1C,
|
||||
0x7F, 0x1C, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 11 (0x0B) Char: 0x002B ('+')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
|
||||
0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 12 (0x0C) Char: 0x002C (',')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00,
|
||||
|
||||
// Index: 13 (0x0D) Char: 0x002D ('-')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 14 (0x0E) Char: 0x002E ('.')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 15 (0x0F) Char: 0x002F ('/')
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x30, 0x30,
|
||||
0x18, 0x18, 0x0C, 0x0C, 0x06, 0x06, 0x00, 0x00,
|
||||
|
||||
// Index: 16 (0x10) Char: 0x0030 ('0')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x76,
|
||||
0x7E, 0x6E, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 17 (0x11) Char: 0x0031 ('1')
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x1C, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 18 (0x12) Char: 0x0032 ('2')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60,
|
||||
0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 19 (0x13) Char: 0x0033 ('3')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60,
|
||||
0x38, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 20 (0x14) Char: 0x0034 ('4')
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x70, 0x78, 0x6C,
|
||||
0x66, 0x66, 0x7E, 0x60, 0x60, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 21 (0x15) Char: 0x0035 ('5')
|
||||
0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x3E,
|
||||
0x60, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 22 (0x16) Char: 0x0036 ('6')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
|
||||
0x3E, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 23 (0x17) Char: 0x0037 ('7')
|
||||
0x00, 0x00, 0x00, 0x00, 0x7E, 0x60, 0x60, 0x30,
|
||||
0x30, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 24 (0x18) Char: 0x0038 ('8')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 25 (0x19) Char: 0x0039 ('9')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x7C, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 26 (0x1A) Char: 0x003A (':')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 27 (0x1B) Char: 0x003B (';')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00,
|
||||
|
||||
// Index: 28 (0x1C) Char: 0x003C ('<')
|
||||
0x00, 0x00, 0x00, 0x00, 0x20, 0x30, 0x18, 0x0C,
|
||||
0x06, 0x0C, 0x18, 0x30, 0x20, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 29 (0x1D) Char: 0x003D ('=')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
|
||||
0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 30 (0x1E) Char: 0x003E ('>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18,
|
||||
0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 31 (0x1F) Char: 0x003F ('?')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60,
|
||||
0x30, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 32 (0x20) Char: 0x0040 ('@')
|
||||
0x00, 0x00, 0x00, 0x00, 0x7E, 0xC3, 0xF3, 0xDB,
|
||||
0xDB, 0xDB, 0xF3, 0x03, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 33 (0x21) Char: 0x0041 ('A')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 34 (0x22) Char: 0x0042 ('B')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
|
||||
0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 35 (0x23) Char: 0x0043 ('C')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
|
||||
0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 36 (0x24) Char: 0x0044 ('D')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 37 (0x25) Char: 0x0045 ('E')
|
||||
0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x06,
|
||||
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 38 (0x26) Char: 0x0046 ('F')
|
||||
0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x06,
|
||||
0x3E, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 39 (0x27) Char: 0x0047 ('G')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
|
||||
0x76, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 40 (0x28) Char: 0x0048 ('H')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x7E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 41 (0x29) Char: 0x0049 ('I')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 42 (0x2A) Char: 0x004A ('J')
|
||||
0x00, 0x00, 0x00, 0x00, 0x70, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 43 (0x2B) Char: 0x004B ('K')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x36,
|
||||
0x1E, 0x36, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 44 (0x2C) Char: 0x004C ('L')
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06,
|
||||
0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 45 (0x2D) Char: 0x004D ('M')
|
||||
0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x77, 0x7F,
|
||||
0x6B, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 46 (0x2E) Char: 0x004E ('N')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x6E, 0x7E,
|
||||
0x76, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 47 (0x2F) Char: 0x004F ('O')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 48 (0x30) Char: 0x0050 ('P')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
|
||||
0x3E, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 49 (0x31) Char: 0x0051 ('Q')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x30, 0x60, 0x00,
|
||||
|
||||
// Index: 50 (0x32) Char: 0x0052 ('R')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
|
||||
0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 51 (0x33) Char: 0x0053 ('S')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x0C,
|
||||
0x18, 0x30, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 52 (0x34) Char: 0x0054 ('T')
|
||||
0x00, 0x00, 0x00, 0x00, 0x7E, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 53 (0x35) Char: 0x0055 ('U')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 54 (0x36) Char: 0x0056 ('V')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 55 (0x37) Char: 0x0057 ('W')
|
||||
0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x6B, 0x6B,
|
||||
0x6B, 0x6B, 0x6B, 0x36, 0x36, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 56 (0x38) Char: 0x0058 ('X')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C,
|
||||
0x18, 0x3C, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 57 (0x39) Char: 0x0059 ('Y')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 58 (0x3A) Char: 0x005A ('Z')
|
||||
0x00, 0x00, 0x00, 0x00, 0x7E, 0x60, 0x60, 0x30,
|
||||
0x18, 0x0C, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 59 (0x3B) Char: 0x005B ('[')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C,
|
||||
0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 60 (0x3C) Char: 0x005C ('\')
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x0C, 0x0C,
|
||||
0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0x00, 0x00,
|
||||
|
||||
// Index: 61 (0x3D) Char: 0x005D (']')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 62 (0x3E) Char: 0x005E ('^')
|
||||
0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x63, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 63 (0x3F) Char: 0x005F ('_')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00,
|
||||
|
||||
// Index: 64 (0x40) Char: 0x0060 ('`')
|
||||
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 65 (0x41) Char: 0x0061 ('a')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
|
||||
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 66 (0x42) Char: 0x0062 ('b')
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 67 (0x43) Char: 0x0063 ('c')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 68 (0x44) Char: 0x0064 ('d')
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x7C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 69 (0x45) Char: 0x0065 ('e')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 70 (0x46) Char: 0x0066 ('f')
|
||||
0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x0C, 0x3E,
|
||||
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 71 (0x47) Char: 0x0067 ('g')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x33,
|
||||
0x33, 0x1E, 0x03, 0x1E, 0x33, 0x33, 0x1E, 0x00,
|
||||
|
||||
// Index: 72 (0x48) Char: 0x0068 ('h')
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 73 (0x49) Char: 0x0069 ('i')
|
||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x1C, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 74 (0x4A) Char: 0x006A ('j')
|
||||
0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x70, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00,
|
||||
|
||||
// Index: 75 (0x4B) Char: 0x006B ('k')
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x66, 0x66,
|
||||
0x36, 0x1E, 0x36, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 76 (0x4C) Char: 0x006C ('l')
|
||||
0x00, 0x00, 0x00, 0x00, 0x1C, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 77 (0x4D) Char: 0x006D ('m')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0xDB,
|
||||
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 78 (0x4E) Char: 0x006E ('n')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 79 (0x4F) Char: 0x006F ('o')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 80 (0x50) Char: 0x0070 ('p')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00,
|
||||
|
||||
// Index: 81 (0x51) Char: 0x0071 ('q')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00,
|
||||
|
||||
// Index: 82 (0x52) Char: 0x0072 ('r')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66,
|
||||
0x66, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 83 (0x53) Char: 0x0073 ('s')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
|
||||
0x06, 0x3C, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 84 (0x54) Char: 0x0074 ('t')
|
||||
0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x3E, 0x0C,
|
||||
0x0C, 0x0C, 0x0C, 0x0C, 0x38, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 85 (0x55) Char: 0x0075 ('u')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 86 (0x56) Char: 0x0076 ('v')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 87 (0x57) Char: 0x0077 ('w')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6B,
|
||||
0x6B, 0x6B, 0x6B, 0x36, 0x36, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 88 (0x58) Char: 0x0078 ('x')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
|
||||
0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 89 (0x59) Char: 0x0079 ('y')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00,
|
||||
|
||||
// Index: 90 (0x5A) Char: 0x007A ('z')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x60,
|
||||
0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 91 (0x5B) Char: 0x007B ('{')
|
||||
0x00, 0x00, 0x00, 0x00, 0x70, 0x18, 0x18, 0x18,
|
||||
0x0E, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 92 (0x5C) Char: 0x007C ('|')
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 93 (0x5D) Char: 0x007D ('}')
|
||||
0x00, 0x00, 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18,
|
||||
0x70, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 94 (0x5E) Char: 0x007E ('~')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E,
|
||||
0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 95 (0x5F) Char: 0x007F ('')
|
||||
0x00, 0x00, 0x7C, 0xC6, 0x92, 0x92, 0x82, 0xBA,
|
||||
0xFE, 0xEE, 0xD6, 0xD6, 0xEE, 0xFE, 0x7C, 0x00,
|
||||
|
||||
// Index: 96 (0x60) Char: 0x00A0 ('<27>')
|
||||
0x00, 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE,
|
||||
0xFE, 0xBA, 0x82, 0xAA, 0x92, 0xC6, 0x7C, 0x00,
|
||||
|
||||
// Index: 97 (0x61) Char: 0x00A1 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
|
||||
0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
|
||||
|
||||
// Index: 98 (0x62) Char: 0x00A2 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3C, 0x66,
|
||||
0x06, 0x66, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 99 (0x63) Char: 0x00A3 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
|
||||
0x1F, 0x06, 0x06, 0x66, 0x3F, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 100 (0x64) Char: 0x00A4 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x22, 0x1C, 0x36, 0x22, 0x36,
|
||||
0x1C, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 101 (0x65) Char: 0x00A5 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x3C, 0x7E, 0x18, 0x7E, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 102 (0x66) Char: 0x00A6 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
|
||||
0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 103 (0x67) Char: 0x00A7 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x3C, 0x46, 0x0E, 0x1A, 0x36,
|
||||
0x6C, 0x58, 0x70, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 104 (0x68) Char: 0x00A8 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 105 (0x69) Char: 0x00A9 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x99,
|
||||
0x85, 0x85, 0x99, 0x42, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 106 (0x6A) Char: 0x00AA ('<27>')
|
||||
0x00, 0x00, 0x1C, 0x20, 0x3C, 0x36, 0x3C, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 107 (0x6B) Char: 0x00AB ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x66,
|
||||
0x33, 0x66, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 108 (0x6C) Char: 0x00AC ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x7E, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 109 (0x6D) Char: 0x00AD ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 110 (0x6E) Char: 0x00AE ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x9D,
|
||||
0xA5, 0x9D, 0xA5, 0x42, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 111 (0x6F) Char: 0x00AF ('<27>')
|
||||
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 112 (0x70) Char: 0x00B0 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x1C, 0x36, 0x36, 0x36, 0x1C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 113 (0x71) Char: 0x00B1 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
|
||||
0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 114 (0x72) Char: 0x00B2 ('<27>')
|
||||
0x00, 0x04, 0x0A, 0x08, 0x04, 0x02, 0x0E, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 115 (0x73) Char: 0x00B3 ('<27>')
|
||||
0x00, 0x06, 0x08, 0x06, 0x08, 0x08, 0x06, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 116 (0x74) Char: 0x00B4 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 117 (0x75) Char: 0x00B5 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00,
|
||||
|
||||
// Index: 118 (0x76) Char: 0x00B6 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x7E, 0x6F, 0x6F, 0x6F,
|
||||
0x6E, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 119 (0x77) Char: 0x00B7 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 120 (0x78) Char: 0x00B8 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0C, 0x00,
|
||||
|
||||
// Index: 121 (0x79) Char: 0x00B9 ('<27>')
|
||||
0x00, 0x04, 0x06, 0x04, 0x04, 0x04, 0x0E, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 122 (0x7A) Char: 0x00BA ('<27>')
|
||||
0x00, 0x0E, 0x0A, 0x0E, 0x00, 0x0E, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 123 (0x7B) Char: 0x00BB ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x66,
|
||||
0xCC, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 124 (0x7C) Char: 0x00BC ('<27>')
|
||||
0x00, 0x04, 0x06, 0x84, 0x44, 0x24, 0x0E, 0xA0,
|
||||
0xA8, 0xA4, 0xE2, 0x80, 0x80, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 125 (0x7D) Char: 0x00BD ('<27>')
|
||||
0x00, 0x04, 0x06, 0x84, 0x44, 0x24, 0x0E, 0x40,
|
||||
0xA8, 0x84, 0x42, 0x20, 0xE0, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 126 (0x7E) Char: 0x00BE ('<27>')
|
||||
0x00, 0x06, 0x08, 0x86, 0x48, 0x28, 0x06, 0xA0,
|
||||
0xA8, 0xA4, 0xE2, 0x80, 0x80, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 127 (0x7F) Char: 0x00BF ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
|
||||
0x00, 0x18, 0x0C, 0x06, 0x66, 0x66, 0x3C, 0x00,
|
||||
|
||||
// Index: 128 (0x80) Char: 0x00C0 ('<27>')
|
||||
0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 129 (0x81) Char: 0x00C1 ('<27>')
|
||||
0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 130 (0x82) Char: 0x00C2 ('<27>')
|
||||
0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 131 (0x83) Char: 0x00C3 ('<27>')
|
||||
0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33, 0x33, 0x33,
|
||||
0x33, 0x3F, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 132 (0x84) Char: 0x00C4 ('<27>')
|
||||
0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 133 (0x85) Char: 0x00C5 ('<27>')
|
||||
0x3C, 0x66, 0x3C, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 134 (0x86) Char: 0x00C6 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0xFE, 0x33, 0x33, 0x33,
|
||||
0xF3, 0x3F, 0x33, 0x33, 0xF3, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 135 (0x87) Char: 0x00C7 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
|
||||
0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x18, 0x0C,
|
||||
|
||||
// Index: 136 (0x88) Char: 0x00C8 ('<27>')
|
||||
0x00, 0x0C, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06,
|
||||
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 137 (0x89) Char: 0x00C9 ('<27>')
|
||||
0x00, 0x30, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06,
|
||||
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 138 (0x8A) Char: 0x00CA ('<27>')
|
||||
0x18, 0x3C, 0x66, 0x00, 0x7E, 0x06, 0x06, 0x06,
|
||||
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 139 (0x8B) Char: 0x00CB ('<27>')
|
||||
0x00, 0x66, 0x66, 0x00, 0x7E, 0x06, 0x06, 0x06,
|
||||
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 140 (0x8C) Char: 0x00CC ('<27>')
|
||||
0x00, 0x0C, 0x18, 0x00, 0x3C, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 141 (0x8D) Char: 0x00CD ('<27>')
|
||||
0x00, 0x30, 0x18, 0x00, 0x3C, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 142 (0x8E) Char: 0x00CE ('<27>')
|
||||
0x0C, 0x1E, 0x32, 0x00, 0x1E, 0x0C, 0x0C, 0x0C,
|
||||
0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 143 (0x8F) Char: 0x00CF ('<27>')
|
||||
0x00, 0x32, 0x32, 0x00, 0x1E, 0x0C, 0x0C, 0x0C,
|
||||
0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 144 (0x90) Char: 0x00D0 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
|
||||
0x6E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 145 (0x91) Char: 0x00D1 ('<27>')
|
||||
0x00, 0x6E, 0x3B, 0x00, 0x33, 0x33, 0x37, 0x3F,
|
||||
0x3B, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 146 (0x92) Char: 0x00D2 ('<27>')
|
||||
0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 147 (0x93) Char: 0x00D3 ('<27>')
|
||||
0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 148 (0x94) Char: 0x00D4 ('<27>')
|
||||
0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 149 (0x95) Char: 0x00D5 ('<27>')
|
||||
0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33, 0x33, 0x33,
|
||||
0x33, 0x33, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 150 (0x96) Char: 0x00D6 ('<27>')
|
||||
0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 151 (0x97) Char: 0x00D7 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66,
|
||||
0x3C, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 152 (0x98) Char: 0x00D8 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x5E, 0x73, 0x33, 0x3B,
|
||||
0x3F, 0x37, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 153 (0x99) Char: 0x00D9 ('<27>')
|
||||
0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 154 (0x9A) Char: 0x00DA ('<27>')
|
||||
0x00, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 155 (0x9B) Char: 0x00DB ('<27>')
|
||||
0x18, 0x3C, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 156 (0x9C) Char: 0x00DC ('<27>')
|
||||
0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 157 (0x9D) Char: 0x00DD ('<27>')
|
||||
0x00, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 158 (0x9E) Char: 0x00DE ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66,
|
||||
0x66, 0x66, 0x3E, 0x06, 0x06, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 159 (0x9F) Char: 0x00DF ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
|
||||
0x36, 0x66, 0x66, 0x66, 0x36, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 160 (0xA0) Char: 0x00E0 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 161 (0xA1) Char: 0x00E1 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 162 (0xA2) Char: 0x00E2 ('<27>')
|
||||
0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66,
|
||||
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 163 (0xA3) Char: 0x00E3 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33,
|
||||
0x30, 0x3E, 0x33, 0x33, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 164 (0xA4) Char: 0x00E4 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66,
|
||||
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 165 (0xA5) Char: 0x00E5 ('<27>')
|
||||
0x00, 0x00, 0x3C, 0x66, 0x3C, 0x00, 0x3C, 0x66,
|
||||
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 166 (0xA6) Char: 0x00E6 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x33,
|
||||
0x30, 0xFE, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 167 (0xA7) Char: 0x00E7 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x18, 0x0C,
|
||||
|
||||
// Index: 168 (0xA8) Char: 0x00E8 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 169 (0xA9) Char: 0x00E9 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 170 (0xAA) Char: 0x00EA ('<27>')
|
||||
0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 171 (0xAB) Char: 0x00EB ('<27>')
|
||||
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 172 (0xAC) Char: 0x00EC ('<27>')
|
||||
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x1C, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 173 (0xAD) Char: 0x00ED ('<27>')
|
||||
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x1C, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 174 (0xAE) Char: 0x00EE ('<27>')
|
||||
0x00, 0x00, 0x0C, 0x1E, 0x32, 0x00, 0x0E, 0x0C,
|
||||
0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 175 (0xAF) Char: 0x00EF ('<27>')
|
||||
0x00, 0x00, 0x00, 0x32, 0x32, 0x00, 0x0E, 0x0C,
|
||||
0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 176 (0xB0) Char: 0x00F0 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x30, 0x7C, 0x30, 0x3E,
|
||||
0x33, 0x33, 0x33, 0x33, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 177 (0xB1) Char: 0x00F1 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1F, 0x33,
|
||||
0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 178 (0xB2) Char: 0x00F2 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 179 (0xB3) Char: 0x00F3 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 180 (0xB4) Char: 0x00F4 ('<27>')
|
||||
0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 181 (0xB5) Char: 0x00F5 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33,
|
||||
0x33, 0x33, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 182 (0xB6) Char: 0x00F6 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 183 (0xB7) Char: 0x00F7 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
|
||||
0x7E, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 184 (0xB8) Char: 0x00F8 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x7E, 0x33,
|
||||
0x3B, 0x3F, 0x37, 0x33, 0x1F, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 185 (0xB9) Char: 0x00F9 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 186 (0xBA) Char: 0x00FA ('<27>')
|
||||
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 187 (0xBB) Char: 0x00FB ('<27>')
|
||||
0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 188 (0xBC) Char: 0x00FC ('<27>')
|
||||
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 189 (0xBD) Char: 0x00FD ('<27>')
|
||||
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00,
|
||||
|
||||
// Index: 190 (0xBE) Char: 0x00FE ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00,
|
||||
|
||||
// Index: 191 (0xBF) Char: 0x00FF ('<27>')
|
||||
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00,
|
||||
|
||||
// Index: 192 (0xC0) Char: 0x010C ('C')
|
||||
0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x06,
|
||||
0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 193 (0xC1) Char: 0x010D ('c')
|
||||
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 194 (0xC2) Char: 0x010E ('D')
|
||||
0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 195 (0xC3) Char: 0x010F ('d')
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x7C, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 196 (0xC4) Char: 0x011A ('E')
|
||||
0x66, 0x3C, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06,
|
||||
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 197 (0xC5) Char: 0x011B ('e')
|
||||
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 198 (0xC6) Char: 0x0131 ('i')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 199 (0xC7) Char: 0x0147 ('N')
|
||||
0x66, 0x3C, 0x18, 0x00, 0x66, 0x66, 0x6E, 0x7E,
|
||||
0x76, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 200 (0xC8) Char: 0x0148 ('n')
|
||||
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 201 (0xC9) Char: 0x0152 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0xFE, 0x33, 0x33, 0x33,
|
||||
0xF3, 0x33, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 202 (0xCA) Char: 0x0153 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x33,
|
||||
0x33, 0xF3, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 203 (0xCB) Char: 0x0158 ('R')
|
||||
0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66, 0x66, 0x66,
|
||||
0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 204 (0xCC) Char: 0x0159 ('r')
|
||||
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66,
|
||||
0x66, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 205 (0xCD) Char: 0x0160 ('<27>')
|
||||
0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x0C,
|
||||
0x18, 0x30, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 206 (0xCE) Char: 0x0161 ('<27>')
|
||||
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66,
|
||||
0x06, 0x3C, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 207 (0xCF) Char: 0x0164 ('T')
|
||||
0x66, 0x3C, 0x18, 0x00, 0x7E, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 208 (0xD0) Char: 0x0165 ('t')
|
||||
0x00, 0x00, 0x00, 0x00, 0x86, 0x86, 0x1F, 0x86,
|
||||
0x06, 0x06, 0x06, 0x06, 0x1C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 209 (0xD1) Char: 0x016E ('U')
|
||||
0x3C, 0x66, 0x3C, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 210 (0xD2) Char: 0x016F ('u')
|
||||
0x00, 0x00, 0x3C, 0x66, 0x3C, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 211 (0xD3) Char: 0x0178 ('<27>')
|
||||
0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66,
|
||||
0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 212 (0xD4) Char: 0x017D ('<27>')
|
||||
0x32, 0x1E, 0x0C, 0x00, 0x3E, 0x30, 0x30, 0x18,
|
||||
0x0C, 0x06, 0x02, 0x02, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 213 (0xD5) Char: 0x017E ('<27>')
|
||||
0x00, 0x00, 0x32, 0x1E, 0x0C, 0x00, 0x3E, 0x30,
|
||||
0x18, 0x0C, 0x06, 0x02, 0x3E, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 214 (0xD6) Char: 0x0192 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x0C, 0x3E,
|
||||
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x06, 0x00,
|
||||
|
||||
// Index: 215 (0xD7) Char: 0x2014 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 216 (0xD8) Char: 0x2018 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 217 (0xD9) Char: 0x2019 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 218 (0xDA) Char: 0x201A ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00,
|
||||
|
||||
// Index: 219 (0xDB) Char: 0x201C ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x36, 0x36,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 220 (0xDC) Char: 0x201D ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 221 (0xDD) Char: 0x201E ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12, 0x00,
|
||||
|
||||
// Index: 222 (0xDE) Char: 0x2020 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 223 (0xDF) Char: 0x2022 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 224 (0xE0) Char: 0x2026 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 225 (0xE1) Char: 0x2039 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18,
|
||||
0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 226 (0xE2) Char: 0x203A ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18,
|
||||
0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 227 (0xE3) Char: 0x20AC ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x1F,
|
||||
0x06, 0x1F, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
|
||||
|
||||
// Index: 228 (0xE4) Char: 0x2122 ('<27>')
|
||||
0x00, 0x00, 0x00, 0x00, 0xDF, 0xAA, 0x8A, 0x8A,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const ui::Font fixed_8x16 {
|
||||
8, 16,
|
||||
fixed_8x16_glyph_data,
|
||||
0x20, 223,
|
||||
};
|
||||
|
||||
} /* namespace font */
|
||||
} /* namespace ui */
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_FONT_FIXED_8X16_H__
|
||||
#define __UI_FONT_FIXED_8X16_H__
|
||||
|
||||
#include "ui_text.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace font {
|
||||
|
||||
extern const ui::Font fixed_8x16;
|
||||
|
||||
} /* namespace font */
|
||||
} /* namspace ui */
|
||||
|
||||
#endif/*__UI_FONT_FIXED_8X16_H__*/
|
378
Software/portapack-mayhem/firmware/application/ui/ui_geomap.cpp
Normal file
378
Software/portapack-mayhem/firmware/application/ui/ui_geomap.cpp
Normal file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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 "ui_geomap.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
GeoPos::GeoPos(
|
||||
const Point pos,
|
||||
const alt_unit altitude_unit
|
||||
) : altitude_unit_(altitude_unit) {
|
||||
|
||||
set_parent_rect({pos, { 30 * 8, 3 * 16 }});
|
||||
|
||||
add_children({
|
||||
&labels_position,
|
||||
&field_altitude,
|
||||
&text_alt_unit,
|
||||
&field_lat_degrees,
|
||||
&field_lat_minutes,
|
||||
&field_lat_seconds,
|
||||
&text_lat_decimal,
|
||||
&field_lon_degrees,
|
||||
&field_lon_minutes,
|
||||
&field_lon_seconds,
|
||||
&text_lon_decimal
|
||||
});
|
||||
|
||||
// Defaults
|
||||
set_altitude(0);
|
||||
set_lat(0);
|
||||
set_lon(0);
|
||||
|
||||
const auto changed_fn = [this](int32_t) {
|
||||
float lat_value = lat();
|
||||
float lon_value = lon();
|
||||
|
||||
text_lat_decimal.set(to_string_decimal(lat_value, 5));
|
||||
text_lon_decimal.set(to_string_decimal(lon_value, 5));
|
||||
|
||||
if (on_change && report_change)
|
||||
on_change(altitude(), lat_value, lon_value);
|
||||
};
|
||||
|
||||
field_altitude.on_change = changed_fn;
|
||||
field_lat_degrees.on_change = changed_fn;
|
||||
field_lat_minutes.on_change = changed_fn;
|
||||
field_lat_seconds.on_change = changed_fn;
|
||||
field_lon_degrees.on_change = changed_fn;
|
||||
field_lon_minutes.on_change = changed_fn;
|
||||
field_lon_seconds.on_change = changed_fn;
|
||||
|
||||
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
|
||||
}
|
||||
|
||||
void GeoPos::set_read_only(bool v) {
|
||||
for(auto child : children_)
|
||||
child->set_focusable(!v);
|
||||
}
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
void GeoPos::set_report_change(bool v) {
|
||||
report_change = v;
|
||||
}
|
||||
|
||||
void GeoPos::focus() {
|
||||
field_altitude.focus();
|
||||
}
|
||||
|
||||
void GeoPos::set_altitude(int32_t altitude) {
|
||||
field_altitude.set_value(altitude);
|
||||
}
|
||||
|
||||
void GeoPos::set_lat(float lat) {
|
||||
field_lat_degrees.set_value(lat);
|
||||
field_lat_minutes.set_value((uint32_t)abs(lat / (1.0 / 60)) % 60);
|
||||
field_lat_seconds.set_value((uint32_t)abs(lat / (1.0 / 3600)) % 60);
|
||||
}
|
||||
|
||||
void GeoPos::set_lon(float lon) {
|
||||
field_lon_degrees.set_value(lon);
|
||||
field_lon_minutes.set_value((uint32_t)abs(lon / (1.0 / 60)) % 60);
|
||||
field_lon_seconds.set_value((uint32_t)abs(lon / (1.0 / 3600)) % 60);
|
||||
}
|
||||
|
||||
float GeoPos::lat() {
|
||||
if (field_lat_degrees.value() < 0) {
|
||||
return -1 * ( -1 * field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0));
|
||||
} else {
|
||||
return field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0);
|
||||
}
|
||||
};
|
||||
|
||||
float GeoPos::lon() {
|
||||
if (field_lon_degrees.value() < 0) {
|
||||
return -1 * (-1 * field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0));
|
||||
} else {
|
||||
return field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0);
|
||||
}
|
||||
};
|
||||
|
||||
int32_t GeoPos::altitude() {
|
||||
return field_altitude.value();
|
||||
};
|
||||
|
||||
GeoMap::GeoMap(
|
||||
Rect parent_rect
|
||||
) : Widget { parent_rect }
|
||||
{
|
||||
//set_focusable(true);
|
||||
}
|
||||
|
||||
void GeoMap::paint(Painter& painter) {
|
||||
u_int16_t line;
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
const auto r = screen_rect();
|
||||
|
||||
// Ony redraw map if it moved by at least 1 pixel
|
||||
if ((x_pos != prev_x_pos) || (y_pos != prev_y_pos)) {
|
||||
for (line = 0; line < r.height(); line++) {
|
||||
map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1));
|
||||
map_file.read(map_line_buffer.data(), r.width() << 1);
|
||||
display.draw_pixels({ 0, r.top() + line, r.width(), 1 }, map_line_buffer);
|
||||
}
|
||||
|
||||
prev_x_pos = x_pos;
|
||||
prev_y_pos = y_pos;
|
||||
}
|
||||
//center tag above point
|
||||
if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces
|
||||
painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_);
|
||||
}
|
||||
if (mode_ == PROMPT) {
|
||||
// Cross
|
||||
display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red());
|
||||
display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red());
|
||||
} else if (angle_ < 360){
|
||||
//if we have a valid angle draw bearing
|
||||
draw_bearing(r.center(), angle_, 10, Color::red());
|
||||
}
|
||||
else {
|
||||
//draw a small cross
|
||||
display.fill_rectangle({ r.center() - Point(8, 1), { 16, 2 } }, Color::red());
|
||||
display.fill_rectangle({ r.center() - Point(1, 8), { 2, 16 } }, Color::red());
|
||||
}
|
||||
}
|
||||
|
||||
bool GeoMap::on_touch(const TouchEvent event) {
|
||||
if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
|
||||
set_highlighted(true);
|
||||
if (on_move) {
|
||||
Point p = event.point - screen_rect().center();
|
||||
on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GeoMap::move(const float lon, const float lat) {
|
||||
lon_ = lon;
|
||||
lat_ = lat;
|
||||
|
||||
Rect map_rect = screen_rect();
|
||||
|
||||
// Using WGS 84/Pseudo-Mercator projection
|
||||
x_pos = map_width * (lon_+180)/360 - (map_rect.width() / 2);
|
||||
|
||||
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
|
||||
double map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat
|
||||
double lat_rad = sin(lat * pi / 180);
|
||||
double map_world_lon = map_width / (2 * pi);
|
||||
double map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
|
||||
y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI
|
||||
|
||||
// Cap position
|
||||
if (x_pos > (map_width - map_rect.width()))
|
||||
x_pos = map_width - map_rect.width();
|
||||
if (y_pos > (map_height + map_rect.height()))
|
||||
y_pos = map_height - map_rect.height();
|
||||
}
|
||||
|
||||
bool GeoMap::init() {
|
||||
auto result = map_file.open("ADSB/world_map.bin");
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
map_file.read(&map_width, 2);
|
||||
map_file.read(&map_height, 2);
|
||||
|
||||
map_center_x = map_width >> 1;
|
||||
map_center_y = map_height >> 1;
|
||||
|
||||
lon_ratio = 180.0 / map_center_x;
|
||||
lat_ratio = -90.0 / map_center_y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeoMap::set_mode(GeoMapMode mode) {
|
||||
mode_ = mode;
|
||||
}
|
||||
|
||||
void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color) {
|
||||
Point arrow_a, arrow_b, arrow_c;
|
||||
|
||||
for (size_t thickness = 0; thickness < 3; thickness++) {
|
||||
arrow_a = polar_to_point(angle, size) + origin;
|
||||
arrow_b = polar_to_point(angle + 180 - 35, size) + origin;
|
||||
arrow_c = polar_to_point(angle + 180 + 35, size) + origin;
|
||||
|
||||
display.draw_line(arrow_a, arrow_b, color);
|
||||
display.draw_line(arrow_b, arrow_c, color);
|
||||
display.draw_line(arrow_c, arrow_a, color);
|
||||
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMapView::focus() {
|
||||
geopos.focus();
|
||||
|
||||
if (!map_opened)
|
||||
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
|
||||
}
|
||||
|
||||
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) {
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
altitude_ = altitude;
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
geopos.set_altitude(altitude_);
|
||||
geopos.set_report_change(true);
|
||||
|
||||
geomap.set_angle(angle);
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
}
|
||||
|
||||
void GeoMapView::setup() {
|
||||
add_child(&geomap);
|
||||
|
||||
geopos.set_altitude(altitude_);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
|
||||
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
|
||||
altitude_ = altitude;
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
|
||||
geomap.on_move = [this](float move_x, float move_y) {
|
||||
lon_ += move_x;
|
||||
lat_ += move_y;
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
geopos.set_lon(lon_);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_report_change(true);
|
||||
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
GeoMapView::~GeoMapView() {
|
||||
if (on_close_)
|
||||
on_close_();
|
||||
}
|
||||
|
||||
// Display mode
|
||||
GeoMapView::GeoMapView(
|
||||
NavigationView& nav,
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
uint16_t angle,
|
||||
const std::function<void(void)> on_close
|
||||
) : nav_ (nav),
|
||||
altitude_ (altitude),
|
||||
altitude_unit_ (altitude_unit),
|
||||
lat_ (lat),
|
||||
lon_ (lon),
|
||||
angle_ (angle),
|
||||
on_close_(on_close)
|
||||
{
|
||||
mode_ = DISPLAY;
|
||||
|
||||
add_child(&geopos);
|
||||
|
||||
map_opened = geomap.init();
|
||||
if (!map_opened) return;
|
||||
|
||||
setup();
|
||||
|
||||
geomap.set_mode(mode_);
|
||||
geomap.set_tag(tag);
|
||||
geomap.set_angle(angle);
|
||||
geomap.move(lon_, lat_);
|
||||
|
||||
geopos.set_read_only(true);
|
||||
}
|
||||
|
||||
// Prompt mode
|
||||
GeoMapView::GeoMapView(
|
||||
NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done
|
||||
) : nav_ (nav),
|
||||
altitude_ (altitude),
|
||||
altitude_unit_ (altitude_unit),
|
||||
lat_ (lat),
|
||||
lon_ (lon)
|
||||
{
|
||||
mode_ = PROMPT;
|
||||
|
||||
add_child(&geopos);
|
||||
|
||||
map_opened = geomap.init();
|
||||
if (!map_opened) return;
|
||||
|
||||
setup();
|
||||
add_child(&button_ok);
|
||||
|
||||
geomap.set_mode(mode_);
|
||||
geomap.move(lon_, lat_);
|
||||
|
||||
button_ok.on_select = [this, on_done, &nav](Button&) {
|
||||
if (on_done)
|
||||
on_done(altitude_, lat_, lon_);
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
219
Software/portapack-mayhem/firmware/application/ui/ui_geomap.hpp
Normal file
219
Software/portapack-mayhem/firmware/application/ui/ui_geomap.hpp
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef __GEOMAP_H__
|
||||
#define __GEOMAP_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "file.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
enum GeoMapMode {
|
||||
DISPLAY,
|
||||
PROMPT
|
||||
};
|
||||
|
||||
class GeoPos : public View {
|
||||
public:
|
||||
enum alt_unit {
|
||||
FEET = 0,
|
||||
METERS
|
||||
};
|
||||
|
||||
std::function<void(int32_t, float, float)> on_change { };
|
||||
|
||||
GeoPos(const Point pos, const alt_unit altitude_unit);
|
||||
|
||||
void focus() override;
|
||||
|
||||
void set_read_only(bool v);
|
||||
void set_altitude(int32_t altitude);
|
||||
void set_lat(float lat);
|
||||
void set_lon(float lon);
|
||||
int32_t altitude();
|
||||
float lat();
|
||||
float lon();
|
||||
|
||||
void set_report_change(bool v);
|
||||
|
||||
private:
|
||||
bool read_only { false };
|
||||
bool report_change { true };
|
||||
alt_unit altitude_unit_ { };
|
||||
|
||||
Labels labels_position {
|
||||
{ { 1 * 8, 0 * 16 }, "Alt:", Color::light_grey() },
|
||||
{ { 1 * 8, 1 * 16 }, "Lat: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
|
||||
{ { 1 * 8, 2 * 16 }, "Lon: * ' \"", Color::light_grey() },
|
||||
};
|
||||
|
||||
NumberField field_altitude {
|
||||
{ 6 * 8, 0 * 16 },
|
||||
5,
|
||||
{ -1000, 50000 },
|
||||
250,
|
||||
' '
|
||||
};
|
||||
Text text_alt_unit {
|
||||
{ 12 * 8, 0 * 16, 2 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
NumberField field_lat_degrees {
|
||||
{ 5 * 8, 1 * 16 }, 4, { -90, 90 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_minutes {
|
||||
{ 10 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_seconds {
|
||||
{ 13 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
Text text_lat_decimal {
|
||||
{ 17 * 8, 1 * 16, 13 * 8, 1 * 16 },
|
||||
""
|
||||
};
|
||||
|
||||
NumberField field_lon_degrees {
|
||||
{ 5 * 8, 2 * 16 }, 4, { -180, 180 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_minutes {
|
||||
{ 10 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_seconds {
|
||||
{ 13 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
Text text_lon_decimal {
|
||||
{ 17 * 8, 2 * 16, 13 * 8, 1 * 16 },
|
||||
""
|
||||
};
|
||||
};
|
||||
|
||||
class GeoMap : public Widget {
|
||||
public:
|
||||
std::function<void(float, float)> on_move { };
|
||||
|
||||
GeoMap(Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
bool init();
|
||||
void set_mode(GeoMapMode mode);
|
||||
void move(const float lon, const float lat);
|
||||
void set_tag(std::string new_tag) {
|
||||
tag_ = new_tag;
|
||||
}
|
||||
|
||||
void set_angle(uint16_t new_angle){
|
||||
angle_ = new_angle;
|
||||
}
|
||||
|
||||
private:
|
||||
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
|
||||
|
||||
GeoMapMode mode_ { };
|
||||
File map_file { };
|
||||
uint16_t map_width { }, map_height { };
|
||||
int32_t map_center_x { }, map_center_y { };
|
||||
float lon_ratio { }, lat_ratio { };
|
||||
int32_t x_pos { }, y_pos { };
|
||||
int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF };
|
||||
float lat_ { };
|
||||
float lon_ { };
|
||||
uint16_t angle_ { };
|
||||
std::string tag_ { };
|
||||
};
|
||||
|
||||
class GeoMapView : public View {
|
||||
public:
|
||||
GeoMapView(
|
||||
NavigationView& nav,
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
uint16_t angle,
|
||||
const std::function<void(void)> on_close = nullptr
|
||||
);
|
||||
GeoMapView(NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done
|
||||
);
|
||||
~GeoMapView();
|
||||
|
||||
GeoMapView(const GeoMapView&) = delete;
|
||||
GeoMapView(GeoMapView&&) = delete;
|
||||
GeoMapView& operator=(const GeoMapView&) = delete;
|
||||
GeoMapView& operator=(GeoMapView&&) = delete;
|
||||
|
||||
void focus() override;
|
||||
|
||||
void update_position(float lat, float lon, uint16_t angle, int32_t altitude);
|
||||
|
||||
std::string title() const override { return "Map view"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void setup();
|
||||
|
||||
const std::function<void(int32_t, float, float)> on_done { };
|
||||
|
||||
const Dim banner_height = 3 * 16;
|
||||
GeoMapMode mode_ { };
|
||||
int32_t altitude_ { };
|
||||
GeoPos::alt_unit altitude_unit_ { };
|
||||
float lat_ { };
|
||||
float lon_ { };
|
||||
uint16_t angle_ { };
|
||||
std::function<void(void)> on_close_ { nullptr };
|
||||
|
||||
bool map_opened { };
|
||||
|
||||
GeoPos geopos {
|
||||
{ 0, 0 },
|
||||
altitude_unit_
|
||||
};
|
||||
|
||||
GeoMap geomap {
|
||||
{ 0, banner_height, 240, 320 - 16 - banner_height }
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 20 * 8, 8, 8 * 8, 2 * 16 },
|
||||
"OK"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif
|
302
Software/portapack-mayhem/firmware/application/ui/ui_menu.cpp
Normal file
302
Software/portapack-mayhem/firmware/application/ui/ui_menu.cpp
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* 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 "ui_menu.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* MenuItemView **********************************************************/
|
||||
|
||||
void MenuItemView::set_item(MenuItem* item_) {
|
||||
item = item_;
|
||||
}
|
||||
|
||||
void MenuItemView::highlight() {
|
||||
set_highlighted(true);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void MenuItemView::unhighlight() {
|
||||
set_highlighted(false);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void MenuItemView::paint(Painter& painter) {
|
||||
Coord offset_x { };
|
||||
|
||||
if (!item) return;
|
||||
|
||||
const auto r = screen_rect();
|
||||
|
||||
const auto paint_style = (highlighted() && (parent()->has_focus() || keep_highlight)) ? style().invert() : style();
|
||||
|
||||
const auto font_height = paint_style.font.line_height();
|
||||
|
||||
ui::Color final_item_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? paint_style.foreground : item->color;
|
||||
ui::Color final_bg_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? item->color : paint_style.background;
|
||||
|
||||
if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground;
|
||||
|
||||
painter.fill_rectangle(
|
||||
r,
|
||||
final_bg_color
|
||||
);
|
||||
|
||||
if (item->bitmap) {
|
||||
painter.draw_bitmap(
|
||||
{ r.location().x() + 4, r.location().y() + 4 },
|
||||
*item->bitmap,
|
||||
final_item_color,
|
||||
final_bg_color
|
||||
);
|
||||
offset_x = 26;
|
||||
} else
|
||||
offset_x = 0;
|
||||
|
||||
Style text_style {
|
||||
.font = paint_style.font,
|
||||
.background = final_bg_color,
|
||||
.foreground = final_item_color
|
||||
};
|
||||
|
||||
painter.draw_string(
|
||||
{ r.location().x() + offset_x, r.location().y() + (r.size().height() - font_height) / 2 },
|
||||
text_style,
|
||||
item->text
|
||||
);
|
||||
}
|
||||
|
||||
/* MenuView **************************************************************/
|
||||
|
||||
MenuView::MenuView(
|
||||
Rect new_parent_rect,
|
||||
bool keep_highlight
|
||||
) : keep_highlight { keep_highlight }
|
||||
{
|
||||
set_parent_rect(new_parent_rect);
|
||||
|
||||
set_focusable(true);
|
||||
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
|
||||
add_child(&arrow_more);
|
||||
arrow_more.set_focusable(false);
|
||||
arrow_more.set_foreground(Color::black());
|
||||
}
|
||||
|
||||
MenuView::~MenuView() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
displayed_max = (parent_rect().size().height() / item_height);
|
||||
arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * item_height), 8, 8 } );
|
||||
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new MenuItemView { keep_highlight };
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
|
||||
Coord y_pos = c * item_height;
|
||||
item->set_parent_rect({
|
||||
{ 0, y_pos },
|
||||
{ size().width(), (Coord)item_height }
|
||||
});
|
||||
}
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void MenuView::on_tick_second() {
|
||||
if (more && blink)
|
||||
arrow_more.set_foreground(Color::white());
|
||||
else
|
||||
arrow_more.set_foreground(Color::black());
|
||||
|
||||
blink = !blink;
|
||||
|
||||
arrow_more.set_dirty();
|
||||
}
|
||||
|
||||
void MenuView::clear() {
|
||||
for (auto item : menu_item_views) {
|
||||
item->set_item(nullptr);
|
||||
}
|
||||
|
||||
menu_items.clear();
|
||||
}
|
||||
|
||||
void MenuView::add_item(MenuItem new_item) {
|
||||
menu_items.push_back(new_item);
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void MenuView::add_items(std::initializer_list<MenuItem> new_items) {
|
||||
for (auto item : new_items) {
|
||||
add_item(item);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuView::update_items() {
|
||||
size_t i = 0;
|
||||
|
||||
if (menu_items.size() > displayed_max + offset) {
|
||||
more = true;
|
||||
blink = true;
|
||||
} else
|
||||
more = false;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
if (i >= menu_items.size()) break;
|
||||
|
||||
// Assign item data to MenuItemViews according to offset
|
||||
item->set_item(&menu_items[i + offset]);
|
||||
item->set_dirty();
|
||||
|
||||
if (highlighted_item == (i + offset)) {
|
||||
item->highlight();
|
||||
} else
|
||||
item->unhighlight();
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
MenuItemView* MenuView::item_view(size_t index) const {
|
||||
return menu_item_views[index];
|
||||
}
|
||||
|
||||
bool MenuView::set_highlighted(int32_t new_value) {
|
||||
int32_t item_count = (int32_t)menu_items.size();
|
||||
|
||||
if (new_value < 0)
|
||||
return false;
|
||||
|
||||
if (new_value >= item_count)
|
||||
new_value = item_count - 1;
|
||||
|
||||
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
|
||||
// Shift MenuView up
|
||||
highlighted_item = new_value;
|
||||
offset = new_value - displayed_max + 1;
|
||||
update_items();
|
||||
} else if ((uint32_t)new_value < offset) {
|
||||
// Shift MenuView down
|
||||
highlighted_item = new_value;
|
||||
offset = new_value;
|
||||
update_items();
|
||||
} else {
|
||||
// Just update highlight
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
highlighted_item = new_value;
|
||||
item_view(highlighted_item - offset)->highlight();
|
||||
}
|
||||
|
||||
if (on_highlight)
|
||||
on_highlight();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t MenuView::highlighted_index() {
|
||||
return highlighted_item;
|
||||
}
|
||||
|
||||
void MenuView::on_focus() {
|
||||
item_view(highlighted_item - offset)->highlight();
|
||||
}
|
||||
|
||||
void MenuView::on_blur() {
|
||||
if (!keep_highlight)
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
}
|
||||
|
||||
bool MenuView::on_key(const KeyEvent key) {
|
||||
switch(key) {
|
||||
case KeyEvent::Up:
|
||||
return set_highlighted(highlighted_item - 1);
|
||||
|
||||
case KeyEvent::Down:
|
||||
return set_highlighted(highlighted_item + 1);
|
||||
|
||||
case KeyEvent::Select:
|
||||
case KeyEvent::Right:
|
||||
if( menu_items[highlighted_item].on_select ) {
|
||||
menu_items[highlighted_item].on_select();
|
||||
}
|
||||
return true;
|
||||
|
||||
case KeyEvent::Left:
|
||||
if( on_left ) {
|
||||
on_left();
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuView::on_encoder(const EncoderEvent event) {
|
||||
set_highlighted(highlighted_item + event);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: This could be handled by default behavior, if the UI stack were to
|
||||
* transmit consumable events from the top of the hit-stack down, and each
|
||||
* MenuItem could respond to a touch and update its parent MenuView.
|
||||
*/
|
||||
/*
|
||||
bool MenuView::on_touch(const TouchEvent event) {
|
||||
size_t i = 0;
|
||||
for(const auto child : children_) {
|
||||
if( child->screen_rect().contains(event.point) ) {
|
||||
return set_highlighted(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
} /* namespace ui */
|
125
Software/portapack-mayhem/firmware/application/ui/ui_menu.hpp
Normal file
125
Software/portapack-mayhem/firmware/application/ui/ui_menu.hpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_MENU_H__
|
||||
#define __UI_MENU_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "bitmap.hpp"
|
||||
#include "signal.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace ui {
|
||||
|
||||
struct MenuItem {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
const Bitmap* bitmap;
|
||||
std::function<void(void)> on_select;
|
||||
|
||||
// TODO: Prevent default-constructed MenuItems.
|
||||
// I managed to construct a menu with three extra, unspecified menu items
|
||||
// in the array that were default constructed...
|
||||
};
|
||||
|
||||
class MenuItemView : public Widget {
|
||||
public:
|
||||
MenuItemView(
|
||||
bool keep_highlight
|
||||
) : keep_highlight { keep_highlight }
|
||||
{
|
||||
}
|
||||
|
||||
MenuItemView(const MenuItemView&) = delete;
|
||||
MenuItemView(MenuItemView&&) = delete;
|
||||
MenuItemView& operator=(const MenuItemView&) = delete;
|
||||
MenuItemView& operator=(MenuItemView&&) = delete;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void set_item(MenuItem* item_);
|
||||
|
||||
void highlight();
|
||||
void unhighlight();
|
||||
|
||||
private:
|
||||
MenuItem* item { nullptr };
|
||||
bool keep_highlight = false;
|
||||
};
|
||||
|
||||
class MenuView : public View {
|
||||
public:
|
||||
std::function<void(void)> on_left { };
|
||||
std::function<void(void)> on_highlight { nullptr };
|
||||
|
||||
MenuView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false);
|
||||
|
||||
~MenuView();
|
||||
|
||||
void add_item(MenuItem new_item);
|
||||
void add_items(std::initializer_list<MenuItem> new_items);
|
||||
void clear();
|
||||
|
||||
MenuItemView* item_view(size_t index) const;
|
||||
|
||||
bool set_highlighted(int32_t new_value);
|
||||
uint32_t highlighted_index();
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
|
||||
private:
|
||||
void update_items();
|
||||
void on_tick_second();
|
||||
|
||||
bool keep_highlight { false };
|
||||
|
||||
SignalToken signal_token_tick_second { };
|
||||
std::vector<MenuItem> menu_items { };
|
||||
std::vector<MenuItemView*> menu_item_views { };
|
||||
|
||||
Image arrow_more {
|
||||
{ 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 { 0 };
|
||||
size_t highlighted_item { 0 };
|
||||
size_t offset { 0 };
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_MENU_H__*/
|
148
Software/portapack-mayhem/firmware/application/ui/ui_qrcode.cpp
Normal file
148
Software/portapack-mayhem/firmware/application/ui/ui_qrcode.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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 "ui_qrcode.hpp"
|
||||
#include "qrcodegen.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
|
||||
namespace ui {
|
||||
|
||||
QRCodeImage::QRCodeImage(
|
||||
Rect parent_rect
|
||||
) : Widget { parent_rect }
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QRCodeImage::~QRCodeImage( )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QRCodeImage::QRCodeImage(const QRCodeImage&Image) : Widget { }
|
||||
{
|
||||
(void)Image;
|
||||
}
|
||||
|
||||
QRCodeImage & QRCodeImage::operator=(const QRCodeImage&Image)
|
||||
{
|
||||
(void)Image;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void QRCodeImage::paint(Painter& painter) {
|
||||
|
||||
(void)painter ;
|
||||
|
||||
// The structure to manage the QR code
|
||||
QRCode qrcode;
|
||||
|
||||
|
||||
//Either small or large QR code can be shown..
|
||||
|
||||
if(portapack::persistent_memory::show_bigger_qr_code()) { // show large QR code
|
||||
int qr_version = 2;
|
||||
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
|
||||
|
||||
display.fill_rectangle(Rect(10, 30, 220, 220), Color::white());
|
||||
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.fill_rectangle(Rect(20+(x*8), 40+(y*8), 8, 8), Color::black());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else { // show small QR code
|
||||
int qr_version = 10;
|
||||
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
|
||||
|
||||
display.fill_rectangle(Rect(92, 97, 63, 63), Color::white());
|
||||
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.draw_pixel(Point(95+x,100+y), Color::black());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void QRCodeView::focus() {
|
||||
button_close.focus();
|
||||
}
|
||||
|
||||
QRCodeView::~QRCodeView() {
|
||||
if (on_close_)
|
||||
on_close_();
|
||||
}
|
||||
|
||||
QRCodeView::QRCodeView(
|
||||
NavigationView& nav,
|
||||
const char * qr_text,
|
||||
const std::function<void(void)> on_close
|
||||
) : nav_ (nav),
|
||||
on_close_(on_close)
|
||||
{
|
||||
|
||||
|
||||
add_children({
|
||||
&qr_code,
|
||||
&button_close});
|
||||
//text_qr.set(qr_text);
|
||||
qr_code.set_text(qr_text);
|
||||
|
||||
button_close.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
} /* namespace ui */
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef __QRCODE_H__
|
||||
#define __QRCODE_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "qrcodegen.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class QRCodeImage : public Widget {
|
||||
public:
|
||||
|
||||
QRCodeImage(Rect parent_rect);
|
||||
void set_text(const char * qr_text) {
|
||||
qr_text_ = qr_text;
|
||||
}
|
||||
void paint(Painter& painter) override;
|
||||
// for -weffc++ to be killed
|
||||
~QRCodeImage(); // destructor
|
||||
QRCodeImage(const QRCodeImage&Image);
|
||||
QRCodeImage & operator=(const QRCodeImage &Image); // assignment
|
||||
|
||||
private:
|
||||
const char * qr_text_ = NULL ;
|
||||
};
|
||||
|
||||
class QRCodeView : public View {
|
||||
public:
|
||||
QRCodeView(
|
||||
NavigationView& nav,
|
||||
const char * qr_text,
|
||||
const std::function<void(void)> on_close = nullptr
|
||||
);
|
||||
~QRCodeView();
|
||||
|
||||
QRCodeView(const QRCodeView&) = delete;
|
||||
QRCodeView(QRCodeView&&) = delete;
|
||||
QRCodeView& operator=(const QRCodeView&) = delete;
|
||||
QRCodeView& operator=(QRCodeView&&) = delete;
|
||||
|
||||
std::string title() const override { return "QR code"; };
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
|
||||
std::function<void(void)> on_close_ { nullptr };
|
||||
|
||||
|
||||
QRCodeImage qr_code {
|
||||
{ 50, 100, 100, 100 }
|
||||
};
|
||||
|
||||
//Text text_qr {
|
||||
// { 0 * 8, 10 * 16, 32 * 8, 1 * 8 },
|
||||
// "-"
|
||||
//};
|
||||
|
||||
Button button_close {
|
||||
{ 9 * 8, 31 * 8, 12 * 8, 3 * 16 },
|
||||
"Back"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif
|
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* 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 "ui_receiver.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "max2837.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* FrequencyField ********************************************************/
|
||||
|
||||
FrequencyField::FrequencyField(
|
||||
const Point parent_pos
|
||||
) : Widget { { parent_pos, { 8 * 10, 16 } } },
|
||||
length_ { 11 },
|
||||
range(rf::tuning_range)
|
||||
{
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyField::value() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void FrequencyField::set_value(rf::Frequency new_value) {
|
||||
new_value = clamp_value(new_value);
|
||||
|
||||
if( new_value != value_ ) {
|
||||
value_ = new_value;
|
||||
if( on_change ) {
|
||||
on_change(value_);
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyField::set_step(rf::Frequency new_value) {
|
||||
step = new_value;
|
||||
// TODO: Quantize current frequency to a step of the new size?
|
||||
}
|
||||
|
||||
void FrequencyField::paint(Painter& painter) {
|
||||
const std::string str_value = to_string_short_freq(value_);
|
||||
|
||||
const auto paint_style = has_focus() ? style().invert() : style();
|
||||
|
||||
painter.draw_string(
|
||||
screen_pos(),
|
||||
paint_style,
|
||||
str_value
|
||||
);
|
||||
}
|
||||
|
||||
bool FrequencyField::on_key(const ui::KeyEvent event) {
|
||||
if( event == ui::KeyEvent::Select ) {
|
||||
if( on_edit ) {
|
||||
on_edit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_encoder(const EncoderEvent delta) {
|
||||
set_value(value() + (delta * step));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_touch(const TouchEvent event) {
|
||||
if( event.type == TouchEvent::Type::Start ) {
|
||||
focus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FrequencyField::on_focus() {
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyField::clamp_value(rf::Frequency value) {
|
||||
return range.clip(value);
|
||||
}
|
||||
|
||||
/* FrequencyKeypadView ***************************************************/
|
||||
|
||||
bool FrequencyKeypadView::on_encoder(const EncoderEvent delta) {
|
||||
focused_button += delta;
|
||||
if (focused_button < 0) {
|
||||
focused_button = buttons.size() - 1;
|
||||
}
|
||||
else if (focused_button >= (int16_t)buttons.size()) {
|
||||
focused_button = 0;
|
||||
}
|
||||
buttons[focused_button].focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
FrequencyKeypadView::FrequencyKeypadView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value
|
||||
) {
|
||||
add_child(&text_value);
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
};
|
||||
|
||||
const char* const key_caps = "123456789<0.";
|
||||
|
||||
int n = 0;
|
||||
for(auto& button : buttons) {
|
||||
add_child(&button);
|
||||
const std::string label {
|
||||
key_caps[n]
|
||||
};
|
||||
button.id = n;
|
||||
button.on_highlight = [this](Button& button) {
|
||||
focused_button = button.id;
|
||||
};
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
(n % 3) * button_w,
|
||||
(n / 3) * button_h + 24,
|
||||
button_w, button_h
|
||||
});
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
|
||||
add_children({
|
||||
&button_save,
|
||||
&button_load,
|
||||
&button_close
|
||||
});
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
nav.push<FrequencySaveView>(this->value());
|
||||
};
|
||||
button_load.on_select = [this, &nav](Button&) {
|
||||
auto load_view = nav.push<FrequencyLoadView>();
|
||||
load_view->on_frequency_loaded = [this](rf::Frequency value) {
|
||||
set_value(value);
|
||||
};
|
||||
};
|
||||
|
||||
button_close.on_select = [this, &nav](Button&) {
|
||||
if( on_changed ) {
|
||||
on_changed(this->value());
|
||||
}
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
set_value(value);
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::focus() {
|
||||
button_close.focus();
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyKeypadView::value() const {
|
||||
return mhz.as_int() * 1000000ULL + submhz.as_int() * submhz_base;
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::set_value(const rf::Frequency new_value) {
|
||||
mhz.set(new_value / 1000000);
|
||||
mhz.remove_leading_zeros();
|
||||
|
||||
submhz.set((new_value % 1000000) / submhz_base);
|
||||
submhz.remove_trailing_zeros();
|
||||
|
||||
update_text();
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::on_button(Button& button) {
|
||||
const auto s = button.text();
|
||||
if( s == "." ) {
|
||||
field_toggle();
|
||||
} else if( s == "<" ) {
|
||||
digit_delete();
|
||||
} else {
|
||||
digit_add(s[0]);
|
||||
}
|
||||
update_text();
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::digit_add(const char c) {
|
||||
if( state == State::DigitMHz ) {
|
||||
if( clear_field_if_digits_entered ) {
|
||||
mhz.clear();
|
||||
}
|
||||
mhz.add_digit(c);
|
||||
} else {
|
||||
submhz.add_digit(c);
|
||||
}
|
||||
clear_field_if_digits_entered = false;
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::digit_delete() {
|
||||
if( state == State::DigitMHz ) {
|
||||
mhz.delete_digit();
|
||||
} else {
|
||||
submhz.delete_digit();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::field_toggle() {
|
||||
if( state == State::DigitMHz ) {
|
||||
state = State::DigitSubMHz;
|
||||
submhz.clear();
|
||||
} else {
|
||||
state = State::DigitMHz;
|
||||
clear_field_if_digits_entered = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::update_text() {
|
||||
const auto s = mhz.as_string() + "." + submhz.as_string();
|
||||
text_value.set(s);
|
||||
}
|
||||
|
||||
/* FrequencyOptionsView **************************************************/
|
||||
|
||||
FrequencyOptionsView::FrequencyOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_style(style);
|
||||
|
||||
field_step.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
this->on_step_changed(v);
|
||||
};
|
||||
|
||||
field_ppm.on_change = [this](int32_t v) {
|
||||
this->on_reference_ppm_correction_changed(v);
|
||||
};
|
||||
|
||||
add_children({
|
||||
&text_step,
|
||||
&field_step,
|
||||
});
|
||||
|
||||
if( portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::Xtal ) {
|
||||
add_child(&field_ppm);
|
||||
add_child(&text_ppm);
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::set_step(rf::Frequency f) {
|
||||
field_step.set_by_value(f);
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::set_reference_ppm_correction(int32_t v) {
|
||||
field_ppm.set_value(v);
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::on_step_changed(rf::Frequency v) {
|
||||
if( on_change_step ) {
|
||||
on_change_step(v);
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::on_reference_ppm_correction_changed(int32_t v) {
|
||||
if( on_change_reference_ppm_correction ) {
|
||||
on_change_reference_ppm_correction(v);
|
||||
}
|
||||
}
|
||||
|
||||
/* RFAmpField ************************************************************/
|
||||
|
||||
RFAmpField::RFAmpField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos,
|
||||
1,
|
||||
{ 0, 1 },
|
||||
1,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.rf_amp());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_rf_amp(v);
|
||||
};
|
||||
}
|
||||
|
||||
/* RadioGainOptionsView **************************************************/
|
||||
|
||||
RadioGainOptionsView::RadioGainOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_style(style);
|
||||
|
||||
add_children({
|
||||
&label_rf_amp,
|
||||
&field_rf_amp,
|
||||
});
|
||||
}
|
||||
|
||||
/* LNAGainField **********************************************************/
|
||||
|
||||
LNAGainField::LNAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max2837::lna::gain_db_range.minimum, max2837::lna::gain_db_range.maximum },
|
||||
max2837::lna::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.lna());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_lna(v);
|
||||
};
|
||||
}
|
||||
|
||||
void LNAGainField::on_focus() {
|
||||
//Widget::on_focus();
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
/* VGAGainField **********************************************************/
|
||||
|
||||
VGAGainField::VGAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
|
||||
max2837::vga::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.vga());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_vga(v);
|
||||
};
|
||||
}
|
||||
|
||||
void VGAGainField::on_focus() {
|
||||
//Widget::on_focus();
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_RECEIVER_H__
|
||||
#define __UI_RECEIVER_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "rf_path.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class FrequencyField : public Widget {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_change { };
|
||||
std::function<void(void)> on_edit { };
|
||||
std::function<void(void)> on_show_options { };
|
||||
|
||||
using range_t = rf::FrequencyRange;
|
||||
|
||||
FrequencyField(const Point parent_pos);
|
||||
|
||||
rf::Frequency value() const;
|
||||
|
||||
void set_value(rf::Frequency new_value);
|
||||
void set_step(rf::Frequency new_value);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const ui::KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
void on_focus() override;
|
||||
|
||||
private:
|
||||
const size_t length_;
|
||||
const range_t range;
|
||||
rf::Frequency value_ { 0 };
|
||||
rf::Frequency step { 25000 };
|
||||
|
||||
rf::Frequency clamp_value(rf::Frequency value);
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
class FieldString {
|
||||
public:
|
||||
enum Justify {
|
||||
Right,
|
||||
Left,
|
||||
};
|
||||
|
||||
constexpr FieldString(
|
||||
Justify justify
|
||||
) : justify { justify }
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t as_int() const {
|
||||
uint32_t value = 0;
|
||||
for(const auto c : s) {
|
||||
const uint_fast8_t digit = (c == ' ') ? 0 : c - '0';
|
||||
value = (value * 10) + digit;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(uint32_t value) {
|
||||
std::generate(s.rbegin(), s.rend(), [&value]() {
|
||||
const char digit = (value % 10) + '0';
|
||||
value /= 10;
|
||||
return digit;
|
||||
});
|
||||
}
|
||||
|
||||
void clear() {
|
||||
s.fill(' ');
|
||||
}
|
||||
|
||||
void add_digit(const char c) {
|
||||
insert_right(c);
|
||||
}
|
||||
|
||||
void delete_digit() {
|
||||
if( justify == Justify::Right ) {
|
||||
shift_right();
|
||||
s.front() = ' ';
|
||||
} else {
|
||||
auto first_digit = std::find_if(s.rbegin(), s.rend(), [](const char& a) {
|
||||
return a != ' ';
|
||||
});
|
||||
if( first_digit != s.rend() ) {
|
||||
*first_digit = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string as_string() const {
|
||||
return { s.data(), s.size() };
|
||||
}
|
||||
|
||||
void remove_leading_zeros() {
|
||||
remove_zeros(s.begin(), s.end());
|
||||
}
|
||||
|
||||
void remove_trailing_zeros() {
|
||||
remove_zeros(s.rbegin(), s.rend());
|
||||
}
|
||||
|
||||
private:
|
||||
using array_type = std::array<char, N>;
|
||||
|
||||
array_type s { };
|
||||
Justify justify { Justify::Left };
|
||||
|
||||
template<typename Iterator>
|
||||
void remove_zeros(Iterator begin, Iterator end) {
|
||||
const auto first_significant_digit =
|
||||
std::find_if(begin, end, [](const char& a) {
|
||||
return a != '0';
|
||||
});
|
||||
std::fill(begin, first_significant_digit, ' ');
|
||||
}
|
||||
|
||||
void insert_right(const char c) {
|
||||
auto insert_point = s.end() - 1;
|
||||
|
||||
if( justify == Justify::Left ) {
|
||||
insert_point = std::find_if(s.begin(), s.end(), [](const char& a) {
|
||||
return a == ' ';
|
||||
});
|
||||
}
|
||||
|
||||
if( *insert_point != ' ' ) {
|
||||
insert_point = shift_left();
|
||||
}
|
||||
|
||||
*insert_point = c;
|
||||
}
|
||||
|
||||
typename array_type::iterator shift_left() {
|
||||
return std::move(s.begin() + 1, s.end(), s.begin());
|
||||
}
|
||||
|
||||
typename array_type::iterator shift_right() {
|
||||
return std::move_backward(s.begin(), s.end() - 1, s.end());
|
||||
}
|
||||
};
|
||||
|
||||
class FrequencyKeypadView : public View {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_changed { };
|
||||
|
||||
FrequencyKeypadView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value
|
||||
);
|
||||
|
||||
void focus() override;
|
||||
|
||||
rf::Frequency value() const;
|
||||
void set_value(const rf::Frequency new_value);
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
|
||||
private:
|
||||
int16_t focused_button = 0;
|
||||
static constexpr int button_w = 240 / 3;
|
||||
static constexpr int button_h = 48;
|
||||
|
||||
static constexpr int mhz_digits = 4;
|
||||
static constexpr int submhz_digits = 4;
|
||||
|
||||
static constexpr int mhz_mod = pow(10, mhz_digits);
|
||||
static constexpr int submhz_base = pow(10, 6 - submhz_digits);
|
||||
static constexpr int text_digits = mhz_digits + 1 + submhz_digits;
|
||||
|
||||
Text text_value {
|
||||
{ 0, 4, 240, 16 }
|
||||
};
|
||||
|
||||
std::array<Button, 12> buttons { };
|
||||
|
||||
Button button_save {
|
||||
{ 0, button_h * 5, 60, button_h },
|
||||
"Save"
|
||||
};
|
||||
Button button_load {
|
||||
{ 60, button_h * 5, 60, button_h },
|
||||
"Load"
|
||||
};
|
||||
Button button_close {
|
||||
{ 128, button_h * 5, 112, button_h },
|
||||
"Done"
|
||||
};
|
||||
|
||||
/* TODO: Template arg required in enum?! */
|
||||
FieldString<mhz_digits> mhz { FieldString<4>::Justify::Right };
|
||||
FieldString<submhz_digits> submhz { FieldString<4>::Justify::Left };
|
||||
|
||||
enum State {
|
||||
DigitMHz,
|
||||
DigitSubMHz
|
||||
};
|
||||
|
||||
State state { DigitMHz };
|
||||
bool clear_field_if_digits_entered { true };
|
||||
|
||||
void on_button(Button& button);
|
||||
|
||||
void digit_add(const char c);
|
||||
void digit_delete();
|
||||
|
||||
void field_toggle();
|
||||
void update_text();
|
||||
};
|
||||
|
||||
class FrequencyStepView : public OptionsField {
|
||||
public:
|
||||
FrequencyStepView(
|
||||
Point parent_pos
|
||||
) : OptionsField {
|
||||
parent_pos,
|
||||
5,
|
||||
{
|
||||
{ " 10", 10 }, /* Fine tuning SSB voice pitch,in HF and QO-100 sat #669 */
|
||||
{ " 50", 50 }, /* added 50Hz/10Hz,but we do not increase GUI digit decimal */
|
||||
{ " 100", 100 },
|
||||
{ " 1k ", 1000 },
|
||||
{ " 3k ", 3000 }, /* Approximate SSB bandwidth */
|
||||
{ " 5k ", 5000 },
|
||||
{ " 6k3", 6250 },
|
||||
{ " 9k ", 9000 }, /* channel spacing for LF, MF in some regions */
|
||||
{ " 10k ", 10000 },
|
||||
{ " 12k5", 12500 },
|
||||
{ " 25k ", 25000 },
|
||||
{ "100k ", 100000 },
|
||||
{ " 1M ", 1000000 },
|
||||
{ " 10M ", 10000000 },
|
||||
}
|
||||
}
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class FrequencyOptionsView : public View {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_change_step { };
|
||||
std::function<void(int32_t)> on_change_reference_ppm_correction { };
|
||||
|
||||
FrequencyOptionsView(const Rect parent_rect, const Style* const style);
|
||||
|
||||
void set_step(rf::Frequency f);
|
||||
void set_reference_ppm_correction(int32_t v);
|
||||
|
||||
private:
|
||||
Text text_step {
|
||||
{ 0 * 8, 0 * 16, 4 * 8, 1 * 16 },
|
||||
"Step"
|
||||
};
|
||||
|
||||
FrequencyStepView field_step {
|
||||
{ 5 * 8, 0 * 16 },
|
||||
};
|
||||
|
||||
void on_step_changed(rf::Frequency v);
|
||||
void on_reference_ppm_correction_changed(int32_t v);
|
||||
|
||||
NumberField field_ppm {
|
||||
{ 23 * 8, 0 * 16 },
|
||||
3,
|
||||
{ -99, 99 },
|
||||
1,
|
||||
'0',
|
||||
};
|
||||
Text text_ext {
|
||||
{ 23 * 8, 0 * 16, 3 * 8, 1 * 16 },
|
||||
"EXT",
|
||||
};
|
||||
Text text_ppm {
|
||||
{ 27 * 8, 0 * 16, 3 * 8, 16 },
|
||||
"PPM",
|
||||
};
|
||||
};
|
||||
|
||||
class RFAmpField : public NumberField {
|
||||
public:
|
||||
RFAmpField(Point parent_pos);
|
||||
};
|
||||
|
||||
class RadioGainOptionsView : public View {
|
||||
public:
|
||||
RadioGainOptionsView(const Rect parent_rect, const Style* const style);
|
||||
|
||||
private:
|
||||
Text label_rf_amp {
|
||||
{ 0 * 8, 0 * 16, 3 * 8, 1 * 16 },
|
||||
"Amp"
|
||||
};
|
||||
|
||||
RFAmpField field_rf_amp {
|
||||
{ 4 * 8, 0 * 16},
|
||||
};
|
||||
};
|
||||
|
||||
class LNAGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
|
||||
LNAGainField(Point parent_pos);
|
||||
|
||||
void on_focus() override;
|
||||
};
|
||||
|
||||
class VGAGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
|
||||
VGAGainField(Point parent_pos);
|
||||
|
||||
void on_focus() override;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_RECEIVER_H__*/
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "ui_rssi.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ui {
|
||||
|
||||
void RSSI::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int rssi_sample_range = 256;
|
||||
constexpr float rssi_voltage_min = 0.4;
|
||||
constexpr float rssi_voltage_max = 2.2;
|
||||
constexpr float adc_voltage_max = 3.3;
|
||||
constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
|
||||
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
|
||||
constexpr int raw_delta = raw_max - raw_min;
|
||||
const range_t<int> x_avg_range { 0, r.width() - 1 };
|
||||
const auto x_avg = x_avg_range.clip((avg_ - raw_min) * r.width() / raw_delta);
|
||||
const range_t<int> x_min_range { 0, x_avg };
|
||||
const auto x_min = x_min_range.clip((min_ - raw_min) * r.width() / raw_delta);
|
||||
const range_t<int> x_max_range { x_avg + 1, r.width() };
|
||||
const auto x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta);
|
||||
|
||||
const Rect r0 { r.left(), r.top(), x_min, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue()
|
||||
);
|
||||
|
||||
const Rect r1 { r.left() + x_min, r.top(), x_avg - x_min, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
const Rect r2 { r.left() + x_avg, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::white()
|
||||
);
|
||||
|
||||
const Rect r3 { r.left() + x_avg + 1, r.top(), x_max - (x_avg + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
const Rect r4 { r.left() + x_max, r.top(), r.width() - x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r4,
|
||||
Color::black()
|
||||
);
|
||||
|
||||
if (pitch_rssi_enabled) {
|
||||
baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true);
|
||||
}
|
||||
}
|
||||
|
||||
void RSSI::set_pitch_rssi(bool enabled) {
|
||||
pitch_rssi_enabled = enabled;
|
||||
if (!enabled) baseband::set_pitch_rssi(0, false);
|
||||
}
|
||||
|
||||
void RSSI::on_statistics_update(const RSSIStatistics& statistics) {
|
||||
min_ = statistics.min;
|
||||
avg_ = statistics.accumulator / statistics.count;
|
||||
max_ = statistics.max;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_RSSI_H__
|
||||
#define __UI_RSSI_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class RSSI : public Widget {
|
||||
public:
|
||||
RSSI(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
min_ { 0 },
|
||||
avg_ { 0 },
|
||||
max_ { 0 }
|
||||
{
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int32_t min_;
|
||||
int32_t avg_;
|
||||
int32_t max_;
|
||||
|
||||
bool pitch_rssi_enabled = false;
|
||||
|
||||
MessageHandlerRegistration message_handler_stats {
|
||||
Message::ID::RSSIStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_pitch_rssi {
|
||||
Message::ID::PitchRSSIConfigure,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const PitchRSSIConfigureMessage*>(p);
|
||||
this->set_pitch_rssi(message.enabled);
|
||||
}
|
||||
};
|
||||
|
||||
void on_statistics_update(const RSSIStatistics& statistics);
|
||||
void set_pitch_rssi(bool enabled);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__UI_RSSI_H__*/
|
@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "ui_spectrum.hpp"
|
||||
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
|
||||
namespace ui {
|
||||
namespace spectrum {
|
||||
|
||||
/* AudioSpectrumView******************************************************/
|
||||
|
||||
AudioSpectrumView::AudioSpectrumView(
|
||||
const Rect parent_rect
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_focusable(true);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&field_frequency,
|
||||
&waveform
|
||||
});
|
||||
|
||||
field_frequency.on_change = [this](int32_t) {
|
||||
set_dirty();
|
||||
};
|
||||
field_frequency.set_value(0);
|
||||
}
|
||||
|
||||
void AudioSpectrumView::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
|
||||
//if( !spectrum_sampling_rate ) return;
|
||||
|
||||
// Cursor
|
||||
const Rect r_cursor {
|
||||
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
|
||||
1, cursor_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red()
|
||||
);
|
||||
}
|
||||
|
||||
void AudioSpectrumView::on_audio_spectrum(const AudioSpectrum* spectrum) {
|
||||
for (size_t i = 0; i < spectrum->db.size(); i++)
|
||||
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
/* FrequencyScale ********************************************************/
|
||||
|
||||
void FrequencyScale::on_show() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void FrequencyScale::set_spectrum_sampling_rate(const int new_sampling_rate) {
|
||||
if( (spectrum_sampling_rate != new_sampling_rate) ) {
|
||||
spectrum_sampling_rate = new_sampling_rate;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::set_channel_filter(
|
||||
const int low_frequency,
|
||||
const int high_frequency,
|
||||
const int transition
|
||||
) {
|
||||
if( (channel_filter_low_frequency != low_frequency) ||
|
||||
(channel_filter_high_frequency != high_frequency) ||
|
||||
(channel_filter_transition != transition) ) {
|
||||
channel_filter_low_frequency = low_frequency;
|
||||
channel_filter_high_frequency = high_frequency;
|
||||
channel_filter_transition = transition;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
clear_background(painter, r);
|
||||
|
||||
if( !spectrum_sampling_rate ) {
|
||||
// Can't draw without non-zero scale.
|
||||
return;
|
||||
}
|
||||
|
||||
draw_filter_ranges(painter, r);
|
||||
draw_frequency_ticks(painter, r);
|
||||
|
||||
if (_blink) {
|
||||
const Rect r_cursor {
|
||||
118 + cursor_position, r.bottom() - filter_band_height,
|
||||
5, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::clear() {
|
||||
spectrum_sampling_rate = 0;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void FrequencyScale::clear_background(Painter& painter, const Rect r) {
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
}
|
||||
|
||||
void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) {
|
||||
const auto x_center = r.width() / 2;
|
||||
|
||||
const Rect tick { r.left() + x_center, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick, Color::white());
|
||||
|
||||
constexpr int tick_count_max = 4;
|
||||
float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max;
|
||||
int magnitude = 1;
|
||||
int magnitude_n = 0;
|
||||
while(rough_tick_interval >= 10.0f) {
|
||||
rough_tick_interval /= 10;
|
||||
magnitude *= 10;
|
||||
magnitude_n += 1;
|
||||
}
|
||||
const int tick_interval = std::ceil(rough_tick_interval);
|
||||
|
||||
auto tick_offset = tick_interval;
|
||||
while((tick_offset * magnitude) < spectrum_sampling_rate / 2) {
|
||||
const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
const std::string zero_pad =
|
||||
((magnitude_n % 3) == 0) ? "" :
|
||||
((magnitude_n % 3) == 1) ? "0" : "00";
|
||||
const std::string unit =
|
||||
(magnitude_n >= 6) ? "M" :
|
||||
(magnitude_n >= 3) ? "k" : "";
|
||||
const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit;
|
||||
const auto label_width = style().font.size_of(label).width();
|
||||
|
||||
const Coord offset_low = r.left() + x_center - pixel_offset;
|
||||
const Rect tick_low { offset_low, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick_low, Color::white());
|
||||
painter.draw_string({ offset_low + 2, r.top() }, style(), label );
|
||||
|
||||
const Coord offset_high = r.left() + x_center + pixel_offset;
|
||||
const Rect tick_high { offset_high, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick_high, Color::white());
|
||||
painter.draw_string({ offset_high - 2 - label_width, r.top() }, style(), label );
|
||||
|
||||
tick_offset += tick_interval;
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::draw_filter_ranges(Painter& painter, const Rect r) {
|
||||
if( channel_filter_low_frequency != channel_filter_high_frequency ) {
|
||||
const auto x_center = r.width() / 2;
|
||||
|
||||
const auto x_low = x_center + channel_filter_low_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
const auto x_high = x_center + channel_filter_high_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
if( channel_filter_transition ) {
|
||||
const auto trans = channel_filter_transition * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
const Rect r_all {
|
||||
r.left() + x_low - trans, r.bottom() - filter_band_height,
|
||||
x_high - x_low + trans*2, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_all,
|
||||
Color::yellow()
|
||||
);
|
||||
}
|
||||
|
||||
const Rect r_pass {
|
||||
r.left() + x_low, r.bottom() - filter_band_height,
|
||||
x_high - x_low, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_pass,
|
||||
Color::green()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::on_focus() {
|
||||
_blink = true;
|
||||
on_tick_second();
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
}
|
||||
|
||||
void FrequencyScale::on_blur() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
_blink = false;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool FrequencyScale::on_encoder(const EncoderEvent delta) {
|
||||
cursor_position += delta;
|
||||
|
||||
cursor_position = std::min<int32_t>(cursor_position, 119);
|
||||
cursor_position = std::max<int32_t>(cursor_position, -120);
|
||||
|
||||
set_dirty();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FrequencyScale::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
if( on_select ) {
|
||||
on_select((cursor_position * spectrum_sampling_rate) / 240);
|
||||
cursor_position = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FrequencyScale::on_tick_second() {
|
||||
set_dirty();
|
||||
_blink = !_blink;
|
||||
}
|
||||
|
||||
/* WaterfallView *********************************************************/
|
||||
|
||||
void WaterfallView::on_show() {
|
||||
clear();
|
||||
|
||||
const auto screen_r = screen_rect();
|
||||
display.scroll_set_area(screen_r.top(), screen_r.bottom());
|
||||
}
|
||||
|
||||
void WaterfallView::on_hide() {
|
||||
/* TODO: Clear region to eliminate brief flash of content at un-shifted
|
||||
* position?
|
||||
*/
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void WaterfallView::paint(Painter& painter) {
|
||||
// Do nothing.
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void WaterfallView::on_channel_spectrum(
|
||||
const ChannelSpectrum& spectrum
|
||||
) {
|
||||
/* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */
|
||||
|
||||
std::array<Color, 240> pixel_row;
|
||||
for(size_t i=0; i<120; i++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]];
|
||||
pixel_row[i] = pixel_color;
|
||||
}
|
||||
|
||||
for(size_t i=120; i<240; i++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]];
|
||||
pixel_row[i] = pixel_color;
|
||||
}
|
||||
|
||||
const auto draw_y = display.scroll(1);
|
||||
|
||||
display.draw_pixels(
|
||||
{ { 0, draw_y }, { pixel_row.size(), 1 } },
|
||||
pixel_row
|
||||
);
|
||||
}
|
||||
|
||||
void WaterfallView::clear() {
|
||||
display.fill_rectangle(
|
||||
screen_rect(),
|
||||
Color::black()
|
||||
);
|
||||
}
|
||||
|
||||
/* WaterfallWidget *******************************************************/
|
||||
|
||||
WaterfallWidget::WaterfallWidget(const bool cursor) {
|
||||
add_children({
|
||||
&waterfall_view,
|
||||
&frequency_scale
|
||||
});
|
||||
|
||||
frequency_scale.set_focusable(cursor);
|
||||
|
||||
// Making the event climb up all the way up to here kinda sucks
|
||||
frequency_scale.on_select = [this](int32_t offset) {
|
||||
if (on_select) on_select(offset);
|
||||
};
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void WaterfallWidget::show_audio_spectrum_view(const bool show) {
|
||||
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
|
||||
|
||||
if (show) {
|
||||
audio_spectrum_view = std::make_unique<AudioSpectrumView>(audio_spectrum_view_rect);
|
||||
add_child(audio_spectrum_view.get());
|
||||
update_widgets_rect();
|
||||
} else {
|
||||
audio_spectrum_update = false;
|
||||
remove_child(audio_spectrum_view.get());
|
||||
audio_spectrum_view.reset();
|
||||
update_widgets_rect();
|
||||
}
|
||||
}
|
||||
|
||||
void WaterfallWidget::update_widgets_rect() {
|
||||
if (audio_spectrum_view) {
|
||||
frequency_scale.set_parent_rect({ 0, audio_spectrum_height, screen_rect().width(), scale_height });
|
||||
waterfall_view.set_parent_rect(waterfall_reduced_rect);
|
||||
} else {
|
||||
frequency_scale.set_parent_rect({ 0, 0, screen_rect().width(), scale_height });
|
||||
waterfall_view.set_parent_rect(waterfall_normal_rect);
|
||||
}
|
||||
waterfall_view.on_show();
|
||||
}
|
||||
|
||||
void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
waterfall_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
|
||||
waterfall_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height };
|
||||
|
||||
update_widgets_rect();
|
||||
}
|
||||
|
||||
void WaterfallWidget::paint(Painter& painter) {
|
||||
// TODO:
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
waterfall_view.on_channel_spectrum(spectrum);
|
||||
sampling_rate = spectrum.sampling_rate;
|
||||
frequency_scale.set_spectrum_sampling_rate(sampling_rate);
|
||||
frequency_scale.set_channel_filter(
|
||||
spectrum.channel_filter_low_frequency,
|
||||
spectrum.channel_filter_high_frequency,
|
||||
spectrum.channel_filter_transition
|
||||
);
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_audio_spectrum() {
|
||||
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
|
||||
}
|
||||
|
||||
} /* namespace spectrum */
|
||||
} /* namespace ui */
|
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_SPECTRUM_H__
|
||||
#define __UI_SPECTRUM_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace ui {
|
||||
namespace spectrum {
|
||||
|
||||
class AudioSpectrumView : public View {
|
||||
public:
|
||||
AudioSpectrumView(const Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_audio_spectrum(const AudioSpectrum* spectrum);
|
||||
|
||||
private:
|
||||
static constexpr int cursor_band_height = 4;
|
||||
|
||||
int16_t audio_spectrum[128] { 0 };
|
||||
|
||||
Labels labels {
|
||||
{ { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() }
|
||||
};
|
||||
|
||||
NumberField field_frequency {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
5,
|
||||
{ 0, 48000 },
|
||||
48000 / 240,
|
||||
' '
|
||||
};
|
||||
|
||||
Waveform waveform {
|
||||
{ 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 },
|
||||
audio_spectrum,
|
||||
128,
|
||||
0,
|
||||
false,
|
||||
Color::white()
|
||||
};
|
||||
};
|
||||
|
||||
class FrequencyScale : public Widget {
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select { };
|
||||
|
||||
void on_show() override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
|
||||
void set_spectrum_sampling_rate(const int new_sampling_rate);
|
||||
void set_channel_filter(const int low_frequency, const int high_frequency, const int transition);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
static constexpr int filter_band_height = 4;
|
||||
|
||||
void on_tick_second();
|
||||
|
||||
bool _blink { false };
|
||||
int32_t cursor_position { 0 };
|
||||
SignalToken signal_token_tick_second { };
|
||||
int spectrum_sampling_rate { 0 };
|
||||
const int spectrum_bins = std::tuple_size<decltype(ChannelSpectrum::db)>::value;
|
||||
int channel_filter_low_frequency { 0 };
|
||||
int channel_filter_high_frequency { 0 };
|
||||
int channel_filter_transition { 0 };
|
||||
|
||||
void clear();
|
||||
void clear_background(Painter& painter, const Rect r);
|
||||
|
||||
void draw_frequency_ticks(Painter& painter, const Rect r);
|
||||
void draw_filter_ranges(Painter& painter, const Rect r);
|
||||
};
|
||||
|
||||
class WaterfallView : public Widget {
|
||||
public:
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
};
|
||||
|
||||
class WaterfallWidget : public View {
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select { };
|
||||
|
||||
WaterfallWidget(const bool cursor = false);
|
||||
|
||||
WaterfallWidget(const WaterfallWidget&) = delete;
|
||||
WaterfallWidget(WaterfallWidget&&) = delete;
|
||||
WaterfallWidget& operator=(const WaterfallWidget&) = delete;
|
||||
WaterfallWidget& operator=(WaterfallWidget&&) = delete;
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void show_audio_spectrum_view(const bool show);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
void update_widgets_rect();
|
||||
|
||||
const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 };
|
||||
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
|
||||
static constexpr Dim scale_height = 20;
|
||||
|
||||
WaterfallView waterfall_view { };
|
||||
FrequencyScale frequency_scale { };
|
||||
|
||||
ChannelSpectrumFIFO* channel_fifo { nullptr };
|
||||
AudioSpectrum* audio_spectrum_data { nullptr };
|
||||
bool audio_spectrum_update { false };
|
||||
|
||||
std::unique_ptr<AudioSpectrumView> audio_spectrum_view { };
|
||||
|
||||
int sampling_rate { 0 };
|
||||
int32_t cursor_position { 0 };
|
||||
ui::Rect waterfall_normal_rect { };
|
||||
ui::Rect waterfall_reduced_rect { };
|
||||
|
||||
MessageHandlerRegistration message_handler_channel_spectrum_config {
|
||||
Message::ID::ChannelSpectrumConfig,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->channel_fifo = message.fifo;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_audio_spectrum {
|
||||
Message::ID::AudioSpectrum,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
|
||||
this->audio_spectrum_data = message.data;
|
||||
this->audio_spectrum_update = true;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_frame_sync {
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
if( this->channel_fifo ) {
|
||||
ChannelSpectrum channel_spectrum;
|
||||
while( channel_fifo->out(channel_spectrum) ) {
|
||||
this->on_channel_spectrum(channel_spectrum);
|
||||
}
|
||||
}
|
||||
if (this->audio_spectrum_update) {
|
||||
this->audio_spectrum_update = false;
|
||||
this->on_audio_spectrum();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_audio_spectrum();
|
||||
};
|
||||
|
||||
} /* namespace spectrum */
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_SPECTRUM_H__*/
|
157
Software/portapack-mayhem/firmware/application/ui/ui_tabview.cpp
Normal file
157
Software/portapack-mayhem/firmware/application/ui/ui_tabview.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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 "ui_tabview.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
Tab::Tab() {
|
||||
set_focusable(true);
|
||||
};
|
||||
|
||||
void Tab::set(
|
||||
uint32_t index,
|
||||
Dim width,
|
||||
std::string text,
|
||||
Color text_color
|
||||
) {
|
||||
set_parent_rect({ (Coord)(index * width), 0, width, 24 });
|
||||
|
||||
text_ = text.substr(0, (width - 8) / 8);
|
||||
text_color_ = text_color;
|
||||
|
||||
index_ = index;
|
||||
}
|
||||
|
||||
void Tab::paint(Painter& painter) {
|
||||
const auto rect = screen_rect();
|
||||
const Color color = highlighted() ? Color::black() : Color::grey();
|
||||
|
||||
painter.fill_rectangle({ rect.left(), rect.top(), rect.width() - 8, rect.height() }, color);
|
||||
|
||||
if (!highlighted())
|
||||
painter.draw_hline({ rect.left(), rect.top() }, rect.width() - 9, Color::light_grey());
|
||||
|
||||
painter.draw_bitmap(
|
||||
{ rect.right() - 8, rect.top() },
|
||||
bitmap_tab_edge,
|
||||
color,
|
||||
Color::dark_grey()
|
||||
);
|
||||
|
||||
auto text_point = rect.center() - Point(4, 0) - Point(text_.size() * 8 / 2, 16 / 2);
|
||||
|
||||
painter.draw_string(
|
||||
text_point,
|
||||
{ ui::font::fixed_8x16, color, text_color_ },
|
||||
text_
|
||||
);
|
||||
|
||||
if (has_focus())
|
||||
painter.draw_hline(text_point + Point(0, 16), text_.size() * 8, Color::white());
|
||||
}
|
||||
|
||||
bool Tab::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
static_cast<TabView*>(parent())->set_selected(index_);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Tab::on_touch(const TouchEvent event) {
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
focus();
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
case TouchEvent::Type::End:
|
||||
static_cast<TabView*>(parent())->set_selected(index_);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TabView::set_selected(uint32_t index) {
|
||||
Tab * tab;
|
||||
|
||||
if (index >= n_tabs)
|
||||
return;
|
||||
|
||||
// Hide previous view
|
||||
views[current_tab]->hidden(true);
|
||||
|
||||
tab = &tabs[current_tab];
|
||||
tab->set_highlighted(false);
|
||||
tab->set_focusable(true);
|
||||
tab->set_dirty();
|
||||
|
||||
// Show new view
|
||||
views[index]->hidden(false);
|
||||
|
||||
tab = &tabs[index];
|
||||
current_tab = index;
|
||||
tab->set_highlighted(true);
|
||||
tab->set_focusable(false);
|
||||
tab->set_dirty();
|
||||
}
|
||||
|
||||
void TabView::on_show() {
|
||||
set_selected(current_tab);
|
||||
}
|
||||
|
||||
void TabView::focus() {
|
||||
views[current_tab]->focus();
|
||||
}
|
||||
|
||||
TabView::TabView(std::initializer_list<TabDef> tab_definitions) {
|
||||
size_t i = 0;
|
||||
|
||||
n_tabs = tab_definitions.size();
|
||||
if (n_tabs > MAX_TABS)
|
||||
n_tabs = MAX_TABS;
|
||||
|
||||
size_t tab_width = 240 / n_tabs;
|
||||
|
||||
set_parent_rect({ 0, 0, 30 * 8, 3 * 8 });
|
||||
|
||||
for (auto &tab_definition : tab_definitions) {
|
||||
tabs[i].set(i, tab_width, tab_definition.text, tab_definition.color);
|
||||
views[i] = tab_definition.view;
|
||||
add_child(&tabs[i]);
|
||||
i++;
|
||||
if (i == MAX_TABS) break;
|
||||
}
|
||||
}
|
||||
|
||||
TabView::~TabView() {
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_TABVIEW_H__
|
||||
#define __UI_TABVIEW_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
#define MAX_TABS 5
|
||||
|
||||
class Tab : public Widget {
|
||||
public:
|
||||
Tab();
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
void set(uint32_t index, Dim width, std::string text, Color text_color);
|
||||
|
||||
private:
|
||||
std::string text_ { };
|
||||
Color text_color_ { };
|
||||
uint32_t index_ { };
|
||||
};
|
||||
|
||||
class TabView : public View {
|
||||
public:
|
||||
struct TabDef {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
View* view;
|
||||
};
|
||||
|
||||
TabView(std::initializer_list<TabDef> tab_definitions);
|
||||
~TabView();
|
||||
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
void set_selected(uint32_t index);
|
||||
uint32_t selected() {
|
||||
return current_tab;
|
||||
};
|
||||
|
||||
private:
|
||||
size_t n_tabs { };
|
||||
std::array<Tab, MAX_TABS> tabs { };
|
||||
std::array<View*, MAX_TABS> views { };
|
||||
uint32_t current_tab { 0 };
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TABVIEW_H__*/
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 "ui_textentry.hpp"
|
||||
//#include "portapack_persistent_memory.hpp"
|
||||
#include "ui_alphanum.hpp"
|
||||
//#include "ui_handwrite.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void text_prompt(NavigationView& nav, std::string& str, const size_t max_length, const std::function<void(std::string&)> on_done) {
|
||||
//if (persistent_memory::ui_config_textentry() == 0) {
|
||||
auto te_view = nav.push<AlphanumView>(str, max_length);
|
||||
te_view->on_changed = [on_done](std::string& value) {
|
||||
if (on_done)
|
||||
on_done(value);
|
||||
};
|
||||
/*} else {
|
||||
auto te_view = nav.push<HandWriteView>(str, max_length);
|
||||
te_view->on_changed = [on_done](std::string * value) {
|
||||
if (on_done)
|
||||
on_done(value);
|
||||
};
|
||||
}*/
|
||||
}
|
||||
|
||||
void TextEntryView::update_text() {
|
||||
if (cursor_pos < 30)
|
||||
text_input.set(_str + std::string(_max_length - _str.length(), ' '));
|
||||
else
|
||||
text_input.set('<' + _str.substr(cursor_pos - 29, 29));
|
||||
|
||||
draw_cursor();
|
||||
}
|
||||
|
||||
void TextEntryView::char_delete() {
|
||||
if (!cursor_pos) return;
|
||||
|
||||
cursor_pos--;
|
||||
_str.resize(cursor_pos);
|
||||
}
|
||||
|
||||
void TextEntryView::char_add(const char c) {
|
||||
if (cursor_pos >= _max_length) return;
|
||||
|
||||
_str += c;
|
||||
cursor_pos++;
|
||||
}
|
||||
|
||||
void TextEntryView::draw_cursor() {
|
||||
Point draw_pos;
|
||||
|
||||
draw_pos = { text_input.screen_rect().location().x() + std::min((Coord)cursor_pos, (Coord)28) * 8,
|
||||
text_input.screen_rect().location().y() + 16 };
|
||||
|
||||
// Erase previous
|
||||
display.fill_rectangle(
|
||||
{ { text_input.screen_rect().location().x(), draw_pos.y() }, { text_input.screen_rect().size().width(), 4 } },
|
||||
Color::black()
|
||||
);
|
||||
// Draw new
|
||||
display.fill_rectangle(
|
||||
{ draw_pos, { 8, 4 } },
|
||||
Color::white()
|
||||
);
|
||||
}
|
||||
|
||||
void TextEntryView::focus() {
|
||||
button_ok.focus();
|
||||
}
|
||||
|
||||
TextEntryView::TextEntryView(
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length
|
||||
) : _str(str),
|
||||
_max_length(max_length)
|
||||
{
|
||||
|
||||
// Trim from right
|
||||
//_str->erase(std::find_if(_str->rbegin(), _str->rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), _str->end());
|
||||
if (_str.length() > _max_length)
|
||||
_str.resize(_max_length);
|
||||
_str.reserve(_max_length);
|
||||
|
||||
cursor_pos = _str.length();
|
||||
|
||||
add_children({
|
||||
&text_input,
|
||||
&button_ok
|
||||
});
|
||||
|
||||
button_ok.on_select = [this, &nav](Button&) {
|
||||
_str.resize(cursor_pos);
|
||||
if (on_changed)
|
||||
on_changed(_str);
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_TEXTENTRY_H__
|
||||
#define __UI_TEXTENTRY_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class TextEntryView : public View {
|
||||
public:
|
||||
std::function<void(std::string&)> on_changed { };
|
||||
|
||||
void focus() override;
|
||||
std::string title() const override { return "Text entry"; };
|
||||
|
||||
protected:
|
||||
TextEntryView(NavigationView& nav, std::string& str, size_t max_length);
|
||||
|
||||
TextEntryView(const TextEntryView&) = delete;
|
||||
TextEntryView(TextEntryView&&) = delete;
|
||||
TextEntryView& operator=(const TextEntryView&) = delete;
|
||||
TextEntryView& operator=(TextEntryView&&) = delete;
|
||||
|
||||
void char_add(const char c);
|
||||
void char_delete();
|
||||
void draw_cursor();
|
||||
void update_text();
|
||||
|
||||
std::string& _str;
|
||||
size_t _max_length;
|
||||
uint32_t cursor_pos { 0 };
|
||||
|
||||
Text text_input {
|
||||
{ 0, 0, 240, 16 }
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 10 * 8, 33 * 8, 9 * 8, 32 },
|
||||
"OK"
|
||||
};
|
||||
};
|
||||
|
||||
void text_prompt(NavigationView& nav, std::string& str, size_t max_length, const std::function<void(std::string&)> on_done = nullptr);
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TEXTENTRY_H__*/
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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 "ui_transmitter.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "max2837.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* TransmitterView *******************************************************/
|
||||
|
||||
void TransmitterView::paint(Painter& painter) {
|
||||
size_t c;
|
||||
Point pos = { 0, screen_pos().y() };
|
||||
|
||||
for (c = 0; c < 20; c++) {
|
||||
painter.draw_bitmap(
|
||||
pos,
|
||||
bitmap_stripes,
|
||||
ui::Color(191, 191, 0),
|
||||
ui::Color::black()
|
||||
);
|
||||
if (c != 9)
|
||||
pos += { 24, 0 };
|
||||
else
|
||||
pos = { 0, screen_pos().y() + 32 + 8 };
|
||||
}
|
||||
}
|
||||
|
||||
void TransmitterView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void TransmitterView::on_channel_bandwidth_changed(uint32_t channel_bandwidth) {
|
||||
transmitter_model.set_channel_bandwidth(channel_bandwidth);
|
||||
}
|
||||
|
||||
void TransmitterView::on_tx_gain_changed(int32_t tx_gain) {
|
||||
transmitter_model.set_tx_gain(tx_gain);
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
void TransmitterView::on_tx_amp_changed(bool rf_amp) {
|
||||
transmitter_model.set_rf_amp(rf_amp);
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
void TransmitterView::update_gainlevel_styles() {
|
||||
const Style *new_style_ptr = NULL;
|
||||
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
if(tot_gain > POWER_THRESHOLD_HIGH) {
|
||||
new_style_ptr = &style_power_high;
|
||||
} else if(tot_gain > POWER_THRESHOLD_MED) {
|
||||
new_style_ptr = &style_power_med;
|
||||
} else if(tot_gain > POWER_THRESHOLD_LOW) {
|
||||
new_style_ptr = &style_power_low;
|
||||
}
|
||||
|
||||
field_gain.set_style(new_style_ptr);
|
||||
text_gain.set_style(new_style_ptr);
|
||||
field_amp.set_style(new_style_ptr);
|
||||
text_amp.set_style(new_style_ptr);
|
||||
}
|
||||
|
||||
void TransmitterView::set_transmitting(const bool transmitting) {
|
||||
if (transmitting) {
|
||||
button_start.set_text("STOP");
|
||||
button_start.set_style(&style_stop);
|
||||
} else {
|
||||
button_start.set_text("START");
|
||||
button_start.set_style(&style_start);
|
||||
}
|
||||
|
||||
transmitting_ = transmitting;
|
||||
}
|
||||
|
||||
void TransmitterView::on_show() {
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency_step.set_by_value(receiver_model.frequency_step());
|
||||
|
||||
field_gain.set_value(transmitter_model.tx_gain());
|
||||
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
void TransmitterView::focus() {
|
||||
button_start.focus();
|
||||
}
|
||||
|
||||
TransmitterView::TransmitterView(
|
||||
const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock
|
||||
) : lock_ { lock }
|
||||
{
|
||||
set_parent_rect({ 0, y, 30 * 8, 6 * 8 });
|
||||
|
||||
add_children({
|
||||
&field_frequency,
|
||||
&field_frequency_step,
|
||||
&text_gain,
|
||||
&field_gain,
|
||||
&button_start,
|
||||
&text_amp,
|
||||
&field_amp,
|
||||
});
|
||||
|
||||
set_transmitting(false);
|
||||
|
||||
if (lock_) {
|
||||
field_frequency.set_focusable(false);
|
||||
field_frequency.set_style(&style_locked);
|
||||
} else {
|
||||
if (channel_bandwidth) {
|
||||
add_children({
|
||||
&text_bw,
|
||||
&field_bw
|
||||
});
|
||||
|
||||
field_bw.on_change = [this](int32_t v) {
|
||||
on_channel_bandwidth_changed(v * 1000);
|
||||
};
|
||||
field_bw.set_value(channel_bandwidth);
|
||||
}
|
||||
}
|
||||
|
||||
//field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency.set_step(frequency_step);
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this]() {
|
||||
if (on_edit_frequency)
|
||||
on_edit_frequency();
|
||||
};
|
||||
|
||||
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->field_frequency.set_step(v);
|
||||
};
|
||||
|
||||
field_gain.on_change = [this](uint32_t tx_gain) {
|
||||
on_tx_gain_changed(tx_gain);
|
||||
};
|
||||
|
||||
field_amp.on_change = [this](uint32_t rf_amp) {
|
||||
on_tx_amp_changed((bool) rf_amp);
|
||||
};
|
||||
|
||||
button_start.on_select = [this](Button&){
|
||||
if (transmitting_) {
|
||||
if (on_stop)
|
||||
on_stop();
|
||||
} else {
|
||||
if (on_start)
|
||||
on_start();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TransmitterView::~TransmitterView() {
|
||||
audio::output::stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_TRANSMITTER_H__
|
||||
#define __UI_TRANSMITTER_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "rf_path.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#define POWER_THRESHOLD_HIGH 47
|
||||
#define POWER_THRESHOLD_MED 38
|
||||
#define POWER_THRESHOLD_LOW 17
|
||||
|
||||
namespace ui {
|
||||
|
||||
class TXGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
|
||||
TXGainField(Point parent_pos);
|
||||
};
|
||||
|
||||
class TransmitterView : public View {
|
||||
public:
|
||||
std::function<void(void)> on_edit_frequency { };
|
||||
std::function<void(void)> on_start { };
|
||||
std::function<void(void)> on_stop { };
|
||||
|
||||
TransmitterView(const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock);
|
||||
TransmitterView(
|
||||
const Coord y, const uint32_t frequency_step, const uint32_t channel_bandwidth
|
||||
) : TransmitterView { y, frequency_step, channel_bandwidth, false }
|
||||
{
|
||||
}
|
||||
|
||||
~TransmitterView();
|
||||
|
||||
void on_show() override;
|
||||
void paint(Painter& painter) override;
|
||||
void focus() override;
|
||||
|
||||
void set_transmitting(const bool transmitting);
|
||||
|
||||
private:
|
||||
const Style style_start {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
const Style style_stop {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
const Style style_locked {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::dark_grey(),
|
||||
};
|
||||
const Style style_power_low {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
const Style style_power_med {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::orange(),
|
||||
};
|
||||
const Style style_power_high {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
|
||||
bool lock_ { false };
|
||||
bool transmitting_ { false };
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 0, 1 * 8 }
|
||||
};
|
||||
|
||||
Text text_gain {
|
||||
{ 0, 3 * 8, 5 * 8, 1 * 16 },
|
||||
"Gain:"
|
||||
};
|
||||
|
||||
NumberField field_gain {
|
||||
{ 5 * 8, 3 * 8 },
|
||||
2,
|
||||
{ max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum },
|
||||
max2837::tx::gain_db_step,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_bw {
|
||||
{ 18 * 8, 1 * 8, 3 * 8, 1 * 16 },
|
||||
"kHz"
|
||||
};
|
||||
NumberField field_bw {
|
||||
{ 15 * 8, 1 * 8 },
|
||||
3,
|
||||
{ 1, 150 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_amp {
|
||||
{ 11 * 8, 3 * 8, 5 * 8, 1 * 16 },
|
||||
"Amp:"
|
||||
};
|
||||
|
||||
NumberField field_amp {
|
||||
{ 16 * 8, 3 * 8 },
|
||||
2,
|
||||
{ 0, 14 },
|
||||
14,
|
||||
' '
|
||||
};
|
||||
|
||||
Button button_start {
|
||||
{ 21 * 8, 1 * 8, 9 * 8, 32 },
|
||||
"START"
|
||||
};
|
||||
|
||||
FrequencyStepView field_frequency_step {
|
||||
{ 10 * 8 - 4, 1 * 8 },
|
||||
};
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_channel_bandwidth_changed(uint32_t channel_bandwidth);
|
||||
void on_tx_gain_changed(int32_t tx_gain);
|
||||
void on_tx_amp_changed(bool rf_amp);
|
||||
|
||||
void update_gainlevel_styles(void);
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TRANSMITTER_H__*/
|
251
Software/portapack-mayhem/firmware/application/ui/ui_tv.cpp
Normal file
251
Software/portapack-mayhem/firmware/application/ui/ui_tv.cpp
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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 "ui_tv.hpp"
|
||||
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
|
||||
namespace ui {
|
||||
namespace tv {
|
||||
|
||||
/* TimeScopeView******************************************************/
|
||||
|
||||
TimeScopeView::TimeScopeView(
|
||||
const Rect parent_rect
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_focusable(true);
|
||||
|
||||
add_children({
|
||||
//&labels,
|
||||
//&field_frequency,
|
||||
&waveform
|
||||
});
|
||||
|
||||
/*field_frequency.on_change = [this](int32_t) {
|
||||
set_dirty();
|
||||
};
|
||||
field_frequency.set_value(10);*/
|
||||
}
|
||||
|
||||
void TimeScopeView::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
|
||||
// Cursor
|
||||
/*
|
||||
const Rect r_cursor {
|
||||
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
|
||||
1, cursor_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red()
|
||||
);*/
|
||||
}
|
||||
|
||||
void TimeScopeView::on_audio_spectrum(const AudioSpectrum* spectrum) {
|
||||
for (size_t i = 0; i < spectrum->db.size(); i++)
|
||||
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
/* TVView *********************************************************/
|
||||
|
||||
void TVView::on_show() {
|
||||
clear();
|
||||
|
||||
const auto screen_r = screen_rect();
|
||||
display.scroll_set_area(screen_r.top(), screen_r.bottom());
|
||||
}
|
||||
|
||||
void TVView::on_hide() {
|
||||
/* TODO: Clear region to eliminate brief flash of content at un-shifted
|
||||
* position?
|
||||
*/
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void TVView::paint(Painter& painter) {
|
||||
// Do nothing.
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void TVView::on_adjust_xcorr(uint8_t xcorr){
|
||||
x_correction = xcorr;
|
||||
}
|
||||
|
||||
void TVView::on_channel_spectrum(
|
||||
const ChannelSpectrum& spectrum
|
||||
) {
|
||||
//portapack has limitations
|
||||
// 1.screen resolution (less than 240x320) 2.samples each call back (128 or 256)
|
||||
// 3.memory size (for ui::Color, the buffer size
|
||||
//spectrum.db[i] is 256 long
|
||||
//768x625 ->128x625 ->128x312 -> 128x104
|
||||
//originally @6MHz sample rate, the PAL should be 768x625
|
||||
//I reduced sample rate to 2MHz(3 times less samples), then calculate mag (effectively decimate by 2)
|
||||
//the resolution is now changed to 128x625. The total decimation factor is 6, which changes how many samples in a line
|
||||
//However 625 is too large for the screen, also interlaced scanning is harder to realize in portapack than normal computer.
|
||||
//So I decided to simply drop half of the lines, once y is larger than 625/2=312.5 or 312, I recognize it as a new frame.
|
||||
//then the resolution is changed to 128x312
|
||||
//128x312 is now able to put into a 240x320 screen, but the buffer for a whole frame is 128x312=39936, which is too large
|
||||
//according to my test, I can only make a buffer with a length of 13312 of type ui::Color. which is 1/3 of what I wanted.
|
||||
//So now the resolution is changed to 128x104, the height is shrinked to 1/3 of the original height.
|
||||
//I was expecting to see 1/3 height of original video.
|
||||
|
||||
//Look how nice is that! I am now able to meet the requirements of 1 and 3 for portapack. Also the length of a line is 128
|
||||
//Each call back gives me 256 samples which is exactly 2 lines. What a coincidence!
|
||||
|
||||
//After some experiment, I did some improvements.
|
||||
//1.I found that instead of 1/3 of the frame is shown, I got 3 whole frames shrinked into one window.
|
||||
//So I made the height twice simply by painting 2 identical lines in the place of original lines
|
||||
//2.I found sometimes there is an horizontal offset, so I added x_correction to move the frame back to center manually
|
||||
//3.I changed video_buffer's type, from ui::Color to uint_8, since I don't need 3 digit to represent a grey scale value.
|
||||
//I was hoping that by doing this, I can have a longer buffer like 39936, then the frame will looks better vertically
|
||||
//however this is useless until now.
|
||||
|
||||
for(size_t i=0; i<256; i++)
|
||||
{
|
||||
//video_buffer[i+count*256] = spectrum_rgb4_lut[spectrum.db[i]];
|
||||
video_buffer_int[i+count*256] = 255 - spectrum.db[i];
|
||||
}
|
||||
count = count + 1;
|
||||
if (count == 52 -1)
|
||||
{
|
||||
ui::Color line_buffer[128];
|
||||
Coord line;
|
||||
uint32_t bmp_px;
|
||||
|
||||
/*for (line = 0; line < 104; line++)
|
||||
{
|
||||
for (bmp_px = 0; bmp_px < 128; bmp_px++)
|
||||
{
|
||||
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
|
||||
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line*128 + x_correction]];
|
||||
}
|
||||
|
||||
display.render_line({ 0, line + 100 }, 128, line_buffer);
|
||||
}*/
|
||||
for (line = 0; line < 208; line=line+2)
|
||||
{
|
||||
for (bmp_px = 0; bmp_px < 128; bmp_px++)
|
||||
{
|
||||
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
|
||||
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line/2*128 + x_correction]];
|
||||
}
|
||||
|
||||
display.render_line({ 0, line + 100 }, 128, line_buffer);
|
||||
display.render_line({ 0, line + 101 }, 128, line_buffer);
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TVView::clear() {
|
||||
display.fill_rectangle(
|
||||
screen_rect(),
|
||||
Color::black()
|
||||
);
|
||||
}
|
||||
|
||||
/* TVWidget *******************************************************/
|
||||
|
||||
TVWidget::TVWidget() {
|
||||
add_children({
|
||||
&tv_view,
|
||||
&field_xcorr
|
||||
});
|
||||
field_xcorr.set_value(10);
|
||||
}
|
||||
|
||||
void TVWidget::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void TVWidget::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void TVWidget::show_audio_spectrum_view(const bool show) {
|
||||
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
|
||||
|
||||
if (show) {
|
||||
audio_spectrum_view = std::make_unique<TimeScopeView>(audio_spectrum_view_rect);
|
||||
add_child(audio_spectrum_view.get());
|
||||
update_widgets_rect();
|
||||
} else {
|
||||
audio_spectrum_update = false;
|
||||
remove_child(audio_spectrum_view.get());
|
||||
audio_spectrum_view.reset();
|
||||
update_widgets_rect();
|
||||
}
|
||||
}
|
||||
|
||||
void TVWidget::update_widgets_rect() {
|
||||
if (audio_spectrum_view) {
|
||||
tv_view.set_parent_rect(tv_reduced_rect);
|
||||
} else {
|
||||
tv_view.set_parent_rect(tv_normal_rect);
|
||||
}
|
||||
tv_view.on_show();
|
||||
}
|
||||
|
||||
void TVWidget::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
tv_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
|
||||
tv_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height };
|
||||
|
||||
update_widgets_rect();
|
||||
}
|
||||
|
||||
void TVWidget::paint(Painter& painter) {
|
||||
// TODO:
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void TVWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
tv_view.on_channel_spectrum(spectrum);
|
||||
tv_view.on_adjust_xcorr(field_xcorr.value());
|
||||
sampling_rate = spectrum.sampling_rate;
|
||||
|
||||
}
|
||||
|
||||
void TVWidget::on_audio_spectrum() {
|
||||
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
|
||||
}
|
||||
|
||||
} /* namespace tv */
|
||||
} /* namespace ui */
|
176
Software/portapack-mayhem/firmware/application/ui/ui_tv.hpp
Normal file
176
Software/portapack-mayhem/firmware/application/ui/ui_tv.hpp
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_TV_H__
|
||||
#define __UI_TV_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace ui {
|
||||
namespace tv {
|
||||
|
||||
class TimeScopeView : public View {
|
||||
public:
|
||||
TimeScopeView(const Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_audio_spectrum(const AudioSpectrum* spectrum);
|
||||
|
||||
private:
|
||||
static constexpr int cursor_band_height = 4;
|
||||
|
||||
int16_t audio_spectrum[128] { 0 };
|
||||
|
||||
/*Labels labels {
|
||||
{ { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() }
|
||||
};*/
|
||||
/*
|
||||
NumberField field_frequency {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
5,
|
||||
{ 0, 48000 },
|
||||
48000 / 240,
|
||||
' '
|
||||
};*/
|
||||
|
||||
Waveform waveform {
|
||||
{ 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 },
|
||||
audio_spectrum,
|
||||
128,
|
||||
0,
|
||||
false,
|
||||
Color::white()
|
||||
};
|
||||
};
|
||||
|
||||
class TVView : public Widget {
|
||||
public:
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_adjust_xcorr(uint8_t xcorr);
|
||||
//ui::Color video_buffer[13312];
|
||||
uint8_t video_buffer_int[13312+128] { 0 }; //128 is for the over length caused by x_correction
|
||||
uint32_t count=0;
|
||||
uint8_t x_correction=0;
|
||||
private:
|
||||
void clear();
|
||||
|
||||
};
|
||||
|
||||
class TVWidget : public View {
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select { };
|
||||
|
||||
TVWidget();
|
||||
|
||||
TVWidget(const TVWidget&) = delete;
|
||||
TVWidget(TVWidget&&) = delete;
|
||||
TVWidget& operator=(const TVWidget&) = delete;
|
||||
TVWidget& operator=(TVWidget&&) = delete;
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void show_audio_spectrum_view(const bool show);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
NumberField field_xcorr {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
5,
|
||||
{ 0, 128 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
private:
|
||||
void update_widgets_rect();
|
||||
|
||||
const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 };
|
||||
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
|
||||
static constexpr Dim scale_height = 20;
|
||||
|
||||
TVView tv_view { };
|
||||
|
||||
ChannelSpectrumFIFO* channel_fifo { nullptr };
|
||||
AudioSpectrum* audio_spectrum_data { nullptr };
|
||||
bool audio_spectrum_update { false };
|
||||
|
||||
std::unique_ptr<TimeScopeView> audio_spectrum_view { };
|
||||
|
||||
int sampling_rate { 0 };
|
||||
int32_t cursor_position { 0 };
|
||||
ui::Rect tv_normal_rect { };
|
||||
ui::Rect tv_reduced_rect { };
|
||||
|
||||
MessageHandlerRegistration message_handler_channel_spectrum_config {
|
||||
Message::ID::ChannelSpectrumConfig,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->channel_fifo = message.fifo;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_audio_spectrum {
|
||||
Message::ID::AudioSpectrum,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
|
||||
this->audio_spectrum_data = message.data;
|
||||
this->audio_spectrum_update = true;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_frame_sync {
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
if( this->channel_fifo ) {
|
||||
ChannelSpectrum channel_spectrum;
|
||||
while( channel_fifo->out(channel_spectrum) ) {
|
||||
this->on_channel_spectrum(channel_spectrum);
|
||||
}
|
||||
}
|
||||
if (this->audio_spectrum_update) {
|
||||
this->audio_spectrum_update = false;
|
||||
this->on_audio_spectrum();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_audio_spectrum();
|
||||
};
|
||||
|
||||
} /* namespace tv */
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TV_H__*/
|
Reference in New Issue
Block a user