Merge remote-tracking branch 'upstream/master' into edge
[clinton/Smoothieware.git] / src / modules / utils / panel / Panel.cpp
CommitLineData
399cb110 1/*
35089dc7
JM
2 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
3 Smoothie 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 3 of the License, or (at your option) any later version.
4 Smoothie 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.
399cb110 5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
35089dc7
JM
6*/
7
8#include "libs/Kernel.h"
9#include "Panel.h"
ca6effd7
JM
10#include "PanelScreen.h"
11
35089dc7
JM
12#include "libs/nuts_bolts.h"
13#include "libs/utils.h"
35089dc7 14#include "Button.h"
ca6effd7 15
35089dc7 16#include "modules/utils/player/PlayerPublicAccess.h"
ca6effd7
JM
17#include "screens/CustomScreen.h"
18#include "screens/MainMenuScreen.h"
61134a65
JM
19#include "SlowTicker.h"
20#include "Gcode.h"
21#include "Pauser.h"
cee1bb2d
JM
22#include "TemperatureControlPublicAccess.h"
23#include "ModifyValuesScreen.h"
24#include "PublicDataRequest.h"
61134a65 25#include "PublicData.h"
35089dc7
JM
26
27#include "panels/I2CLCD.h"
28#include "panels/VikiLCD.h"
1c2b594a 29#include "panels/Smoothiepanel.h"
357d0eda 30#include "panels/ReprapDiscountGLCD.h"
84e7fd5a 31#include "panels/ST7565.h"
3ae0e673
JM
32#include "panels/UniversalAdapter.h"
33
f140cf3a 34#include "version.h"
7af0714f 35#include "checksumm.h"
8d54c34c 36#include "ConfigValue.h"
35089dc7 37
ca6effd7
JM
38#define panel_checksum CHECKSUM("panel")
39#define enable_checksum CHECKSUM("enable")
40#define lcd_checksum CHECKSUM("lcd")
41#define i2c_lcd_checksum CHECKSUM("i2c_lcd")
42#define viki_lcd_checksum CHECKSUM("viki_lcd")
43#define smoothiepanel_checksum CHECKSUM("smoothiepanel")
44#define panelolu2_checksum CHECKSUM("panelolu2")
45#define rrd_glcd_checksum CHECKSUM("reprap_discount_glcd")
46#define st7565_glcd_checksum CHECKSUM("st7565_glcd")
3ae0e673 47#define universal_adapter_checksum CHECKSUM("universal_adapter")
ca6effd7 48
fa3f7b30
JM
49#define menu_offset_checksum CHECKSUM("menu_offset")
50#define encoder_resolution_checksum CHECKSUM("encoder_resolution")
51#define jog_x_feedrate_checksum CHECKSUM("alpha_jog_feedrate")
52#define jog_y_feedrate_checksum CHECKSUM("beta_jog_feedrate")
53#define jog_z_feedrate_checksum CHECKSUM("gamma_jog_feedrate")
f1e696a3 54#define longpress_delay_checksum CHECKSUM("longpress_delay")
ca6effd7
JM
55
56#define hotend_temp_checksum CHECKSUM("hotend_temperature")
57#define bed_temp_checksum CHECKSUM("bed_temperature")
58
cee1bb2d
JM
59Panel* Panel::instance= nullptr;
60
862fc625
JM
61Panel::Panel()
62{
cee1bb2d 63 instance= this;
35089dc7
JM
64 this->counter_changed = false;
65 this->click_changed = false;
66 this->refresh_flag = false;
67 this->enter_menu_mode();
862fc625 68 this->lcd = NULL;
58d6d841 69 this->do_buttons = false;
e456d044 70 this->do_encoder = false;
862fc625
JM
71 this->idle_time = 0;
72 this->start_up = true;
73 this->current_screen = NULL;
7aa9fef3 74 strcpy(this->playing_file, "Playing file");
35089dc7
JM
75}
76
862fc625
JM
77Panel::~Panel()
78{
58d6d841 79 delete this->lcd;
35089dc7
JM
80}
81
862fc625
JM
82void Panel::on_module_loaded()
83{
35089dc7 84 // Exit if this module is not enabled
314ab8f7 85 if ( !THEKERNEL->config->value( panel_checksum, enable_checksum )->by_default(false)->as_bool() ) {
58d6d841
JM
86 delete this;
87 return;
399cb110 88 }
35089dc7 89
58d6d841
JM
90 // Initialise the LCD, see which LCD to use
91 if (this->lcd != NULL) delete this->lcd;
314ab8f7 92 int lcd_cksm = get_checksum(THEKERNEL->config->value(panel_checksum, lcd_checksum)->by_default("i2c")->as_string());
58d6d841
JM
93
94 // Note checksums are not const expressions when in debug mode, so don't use switch
862fc625 95 if (lcd_cksm == i2c_lcd_checksum) {
58d6d841 96 this->lcd = new I2CLCD();
862fc625 97 } else if (lcd_cksm == viki_lcd_checksum) {
58d6d841 98 this->lcd = new VikiLCD();
e0107f6b 99 this->lcd->set_variant(0);
862fc625 100 } else if (lcd_cksm == panelolu2_checksum) {
e0107f6b
JM
101 this->lcd = new VikiLCD();
102 this->lcd->set_variant(1);
862fc625 103 } else if (lcd_cksm == smoothiepanel_checksum) {
1c2b594a 104 this->lcd = new Smoothiepanel();
862fc625 105 } else if (lcd_cksm == rrd_glcd_checksum) {
357d0eda 106 this->lcd = new ReprapDiscountGLCD();
862fc625 107 } else if (lcd_cksm == st7565_glcd_checksum) {
84e7fd5a 108 this->lcd = new ST7565();
3ae0e673
JM
109 } else if (lcd_cksm == universal_adapter_checksum) {
110 this->lcd = new UniversalAdapter();
862fc625 111 } else {
58d6d841
JM
112 // no lcd type defined
113 return;
114 }
115
cee1bb2d
JM
116 // these need to be called here as they need the config cache loaded as they enumerate modules
117 this->custom_screen= new CustomScreen();
118 setup_temperature_screen();
ca6effd7 119
58d6d841
JM
120 // some panels may need access to this global info
121 this->lcd->setPanel(this);
122
f65ce58f 123 // the number of screen lines the panel supports
862fc625 124 this->screen_lines = this->lcd->get_screen_lines();
399cb110 125
19fb4629
JM
126 // some encoders may need more clicks to move menu, this is a divisor and is in config as it is
127 // an end user usability issue
314ab8f7 128 this->menu_offset = THEKERNEL->config->value( panel_checksum, menu_offset_checksum )->by_default(0)->as_number();
19fb4629 129
fa3f7b30 130 // override default encoder resolution if needed
314ab8f7 131 this->encoder_click_resolution = THEKERNEL->config->value( panel_checksum, encoder_resolution_checksum )->by_default(this->lcd->getEncoderResolution())->as_number();
fa3f7b30 132
58d6d841 133 // load jogging feedrates in mm/min
1ad23cd3
MM
134 jogging_speed_mm_min[0] = THEKERNEL->config->value( panel_checksum, jog_x_feedrate_checksum )->by_default(3000.0f)->as_number();
135 jogging_speed_mm_min[1] = THEKERNEL->config->value( panel_checksum, jog_y_feedrate_checksum )->by_default(3000.0f)->as_number();
136 jogging_speed_mm_min[2] = THEKERNEL->config->value( panel_checksum, jog_z_feedrate_checksum )->by_default(300.0f )->as_number();
f19a841a
JM
137
138 // load the default preset temeratures
1ad23cd3
MM
139 default_hotend_temperature = THEKERNEL->config->value( panel_checksum, hotend_temp_checksum )->by_default(185.0f )->as_number();
140 default_bed_temperature = THEKERNEL->config->value( panel_checksum, bed_temp_checksum )->by_default(60.0f )->as_number();
f19a841a 141
399cb110 142
95195a29
JM
143 this->up_button.up_attach( this, &Panel::on_up );
144 this->down_button.up_attach( this, &Panel::on_down );
ab4abea9 145 this->click_button.up_attach( this, &Panel::on_select );
95195a29 146 this->back_button.up_attach( this, &Panel::on_back );
52500e58 147 this->pause_button.up_attach( this, &Panel::on_pause );
35089dc7 148
355c5796
L
149
150 //setting longpress_delay
151 int longpress_delay = THEKERNEL->config->value( panel_checksum, longpress_delay_checksum )->by_default(0)->as_number();
152 this->up_button.set_longpress_delay(longpress_delay);
f1e696a3 153 this->down_button.set_longpress_delay(longpress_delay);
355c5796
L
154// this->click_button.set_longpress_delay(longpress_delay);
155// this->back_button.set_longpress_delay(longpress_delay);
156// this->pause_button.set_longpress_delay(longpress_delay);
61134a65
JM
157
158
c206438f 159 THEKERNEL->slow_ticker->attach( 50, this, &Panel::button_tick );
e456d044
JM
160 if(lcd->encoderReturnsDelta()) {
161 // panel handles encoder pins and returns a delta
10d11dfd 162 THEKERNEL->slow_ticker->attach( 10, this, &Panel::encoder_tick );
e456d044
JM
163 }else{
164 // read encoder pins
165 THEKERNEL->slow_ticker->attach( 1000, this, &Panel::encoder_check );
166 }
399cb110 167
767e7c51
JM
168 // Register for events
169 this->register_for_event(ON_IDLE);
170 this->register_for_event(ON_MAIN_LOOP);
399cb110 171 this->register_for_event(ON_GCODE_RECEIVED);
767e7c51 172
35089dc7 173 // Refresh timer
314ab8f7 174 THEKERNEL->slow_ticker->attach( 20, this, &Panel::refresh_tick );
35089dc7
JM
175}
176
177// Enter a screen, we only care about it now
862fc625
JM
178void Panel::enter_screen(PanelScreen *screen)
179{
35089dc7
JM
180 this->current_screen = screen;
181 this->reset_counter();
182 this->current_screen->on_enter();
183}
184
185// Reset the counter
862fc625
JM
186void Panel::reset_counter()
187{
35089dc7
JM
188 *this->counter = 0;
189 this->counter_changed = false;
190}
191
192// Indicate the idle loop we want to call the refresh hook in the current screen
5ff3e912 193// called 20 times a second
862fc625
JM
194uint32_t Panel::refresh_tick(uint32_t dummy)
195{
58d6d841
JM
196 this->refresh_flag = true;
197 this->idle_time++;
198 return 0;
35089dc7
JM
199}
200
e93caece 201// Encoder pins changed in interrupt or call from on_idle
862fc625
JM
202uint32_t Panel::encoder_check(uint32_t dummy)
203{
e456d044
JM
204 // if encoder reads go through SPI like on universal panel adapter this needs to be
205 // done in idle loop, this is indicated by lcd->encoderReturnsDelta()
206 // however when reading encoder directly it needs to be done
207 // frequently, universal panel adapter will return an actual delta count so won't miss any if polled slowly
e93caece 208 // NOTE FIXME (WHY is it in menu only?) this code will not work if change is not 1,0,-1 anything greater (as in above case) will not work properly
10d11dfd
JM
209 static int encoder_counter = 0; // keeps track of absolute encoder position
210 static int last_encoder_click= 0; // last readfing of divided encoder count
35089dc7 211 int change = lcd->readEncoderDelta();
58d6d841 212 encoder_counter += change;
10d11dfd
JM
213 int clicks= encoder_counter/this->encoder_click_resolution;
214 int delta= clicks - last_encoder_click; // the number of clicks this time
215 last_encoder_click= clicks;
b6e81799 216
10d11dfd 217 if ( delta != 0 ) {
58d6d841 218 this->counter_changed = true;
10d11dfd 219 (*this->counter) += delta;
862fc625 220 this->idle_time = 0;
35089dc7 221 }
58d6d841 222 return 0;
35089dc7
JM
223}
224
225// Read and update each button
862fc625
JM
226uint32_t Panel::button_tick(uint32_t dummy)
227{
58d6d841
JM
228 this->do_buttons = true;
229 return 0;
35089dc7
JM
230}
231
e456d044
JM
232// Read and update encoder
233uint32_t Panel::encoder_tick(uint32_t dummy)
234{
235 this->do_encoder = true;
236 return 0;
237}
238
862fc625 239void Panel::on_gcode_received(void *argument)
399cb110 240{
862fc625
JM
241 Gcode *gcode = static_cast<Gcode *>(argument);
242 if ( gcode->has_m) {
243 if ( gcode->m == 117 ) { // set LCD message
8090190d 244 this->message = get_arguments(gcode->get_command());
862fc625 245 if (this->message.size() > 20) this->message = this->message.substr(0, 20);
399cb110
JM
246 gcode->mark_as_taken();
247 }
248 }
249}
250
35089dc7 251// on main loop, we can send gcodes or do anything that waits in this loop
862fc625
JM
252void Panel::on_main_loop(void *argument)
253{
254 if (this->current_screen != NULL) {
f140cf3a
JM
255 this->current_screen->on_main_loop();
256 this->lcd->on_main_loop();
257 }
35089dc7
JM
258}
259
c96fd70a
JM
260
261#define ohw_logo_antipixel_width 80
262#define ohw_logo_antipixel_height 15
161164be 263static const uint8_t ohw_logo_antipixel_bits[] = {
862fc625
JM
264 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
265 0x00, 0x00, 0x00, 0x01, 0x80, 0x0C, 0x00, 0x33, 0x18, 0xBB, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0x5E,
266 0x80, 0x2D, 0x6B, 0x9B, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0xFF, 0xC0, 0x2D, 0x18, 0xAB, 0xFF, 0xFF,
267 0xFF, 0xFD, 0x80, 0xFF, 0xC0, 0x2D, 0x7B, 0xB3, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0x7F, 0x80, 0x33,
268 0x78, 0xBB, 0xFF, 0xFF, 0xFF, 0xFD, 0x81, 0xF3, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD,
269 0x81, 0xF3, 0xE0, 0x3F, 0xFD, 0xB3, 0x18, 0xDD, 0x98, 0xC5, 0x81, 0xF3, 0xE0, 0x3F, 0xFD, 0xAD,
270 0x6B, 0x5D, 0x6B, 0x5D, 0x80, 0x73, 0x80, 0x3F, 0xFC, 0x21, 0x1B, 0x55, 0x08, 0xC5, 0x80, 0xF3,
271 0xC0, 0x3F, 0xFD, 0xAD, 0x5B, 0x49, 0x6A, 0xDD, 0x80, 0xE1, 0xC0, 0x3F, 0xFD, 0xAD, 0x68, 0xDD,
272 0x6B, 0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
273 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
c96fd70a
JM
274};
275
35089dc7
JM
276// On idle things, we don't want to do shit in interrupts
277// don't queue gcodes in this
862fc625
JM
278void Panel::on_idle(void *argument)
279{
280 if (this->start_up) {
f140cf3a
JM
281 this->lcd->init();
282
ebc64506
JM
283 Version v;
284 string build(v.get_build());
285 string date(v.get_build_date());
286 this->lcd->clear();
862fc625
JM
287 this->lcd->setCursor(0, 0); this->lcd->printf("Welcome to Smoothie");
288 this->lcd->setCursor(0, 1); this->lcd->printf("%s", build.substr(0, 20).c_str());
289 this->lcd->setCursor(0, 2); this->lcd->printf("%s", date.substr(0, 20).c_str());
290 this->lcd->setCursor(0, 3); this->lcd->printf("Please wait....");
ebc64506 291
862fc625 292 if (this->lcd->hasGraphics()) {
c96fd70a
JM
293 this->lcd->bltGlyph(24, 40, ohw_logo_antipixel_width, ohw_logo_antipixel_height, ohw_logo_antipixel_bits);
294 }
295
f54d25cb
JM
296 this->lcd->on_refresh(true); // tell lcd to display now
297
f140cf3a 298 // Default top screen
ca6effd7 299 this->top_screen= new MainMenuScreen();
ca6effd7 300 this->custom_screen->set_parent(this->top_screen);
862fc625 301 this->start_up = false;
5ff3e912 302 return;
f140cf3a 303 }
35089dc7 304
ca6effd7 305 MainMenuScreen *mms= static_cast<MainMenuScreen*>(this->top_screen);
58d6d841 306 // after being idle for a while switch to Watch screen
5ff3e912 307 if (this->current_screen != NULL && this->idle_time > this->current_screen->idle_timeout_secs()*20) {
862fc625 308 this->idle_time = 0;
ca6effd7
JM
309 if (mms->watch_screen != this->current_screen) {
310 this->enter_screen(mms->watch_screen);
58d6d841
JM
311 // TODO do we need to reset any state?
312 }
399cb110 313
58d6d841
JM
314 return;
315 }
399cb110 316
5ff3e912
JM
317 if(current_screen == NULL && this->idle_time > 20*4) {
318 this->enter_screen(mms->watch_screen);
319 return;
320 }
321
e456d044
JM
322 if(this->do_encoder) {
323 this->do_encoder= false;
324 encoder_check(0);
325 }
326
862fc625 327 if (this->do_buttons) {
e456d044 328 // we don't want to do SPI in interrupt mode
58d6d841
JM
329 this->do_buttons = false;
330
331 // read the actual buttons
862fc625
JM
332 int but = lcd->readButtons();
333 if (but != 0) {
334 this->idle_time = 0;
5ff3e912
JM
335 if(current_screen == NULL) {
336 // we were in startup screen so go to watch screen
337 this->enter_screen(mms->watch_screen);
338 return;
339 }
f140cf3a
JM
340 }
341
58d6d841 342 // fire events if the buttons are active and debounce is satisfied
862fc625
JM
343 this->up_button.check_signal(but & BUTTON_UP);
344 this->down_button.check_signal(but & BUTTON_DOWN);
345 this->back_button.check_signal(but & BUTTON_LEFT);
346 this->click_button.check_signal(but & BUTTON_SELECT);
347 this->pause_button.check_signal(but & BUTTON_PAUSE);
348 }
399cb110 349
35089dc7 350 // If we are in menu mode and the position has changed
862fc625 351 if ( this->mode == MENU_MODE && this->counter_change() ) {
35089dc7
JM
352 this->menu_update();
353 }
354
355 // If we are in control mode
862fc625 356 if ( this->mode == CONTROL_MODE && this->counter_change() ) {
58d6d841 357 this->control_value_update();
35089dc7
JM
358 }
359
360 // If we must refresh
862fc625 361 if ( this->refresh_flag ) {
35089dc7 362 this->refresh_flag = false;
862fc625 363 if (this->current_screen != NULL) {
f140cf3a
JM
364 this->current_screen->on_refresh();
365 this->lcd->on_refresh();
366 }
35089dc7
JM
367 }
368}
369
370// Hooks for button clicks
862fc625
JM
371uint32_t Panel::on_up(uint32_t dummy)
372{
25121d12 373 // this is simulating encoder clicks, but needs to be inverted to
b6e81799
JM
374 // increment values on up,increment by
375 int inc = (this->mode == CONTROL_MODE) ? 1 : -(this->menu_offset+1);
25121d12 376 *this->counter += inc;
58d6d841
JM
377 this->counter_changed = true;
378 return 0;
35089dc7 379}
5ff3e912 380
862fc625
JM
381uint32_t Panel::on_down(uint32_t dummy)
382{
b6e81799 383 int inc = (this->mode == CONTROL_MODE) ? -1 : (this->menu_offset+1);
25121d12 384 *this->counter += inc;
58d6d841
JM
385 this->counter_changed = true;
386 return 0;
35089dc7
JM
387}
388
389// on most menu screens will go back to previous higher menu
862fc625
JM
390uint32_t Panel::on_back(uint32_t dummy)
391{
392 if (this->mode == MENU_MODE && this->current_screen != NULL && this->current_screen->parent != NULL) {
58d6d841
JM
393 this->enter_screen(this->current_screen->parent);
394 }
395 return 0;
35089dc7
JM
396}
397
862fc625
JM
398uint32_t Panel::on_select(uint32_t dummy)
399{
58d6d841
JM
400 // TODO make configurable, including turning off
401 // buzz is ignored on panels that do not support buzz
58d6d841 402 this->click_changed = true;
862fc625
JM
403 this->idle_time = 0;
404 lcd->buzz(60, 300); // 50ms 300Hz
58d6d841 405 return 0;
35089dc7
JM
406}
407
862fc625
JM
408uint32_t Panel::on_pause(uint32_t dummy)
409{
20e69746 410 if (!THEKERNEL->pauser->paused()) {
52500e58 411 THEKERNEL->pauser->take();
862fc625 412 } else {
52500e58 413 THEKERNEL->pauser->release();
52500e58
L
414 }
415 return 0;
416}
417
862fc625
JM
418bool Panel::counter_change()
419{
420 if ( this->counter_changed ) {
421 this->counter_changed = false;
422 return true;
423 } else {
424 return false;
425 }
426}
427bool Panel::click()
428{
429 if ( this->click_changed ) {
430 this->click_changed = false;
431 return true;
432 } else {
433 return false;
434 }
435}
35089dc7
JM
436
437
438// Enter menu mode
5971142d 439void Panel::enter_menu_mode(bool force)
862fc625 440{
35089dc7
JM
441 this->mode = MENU_MODE;
442 this->counter = &this->menu_selected_line;
5971142d 443 this->menu_changed = force;
35089dc7
JM
444}
445
862fc625
JM
446void Panel::setup_menu(uint16_t rows)
447{
f65ce58f
JM
448 this->setup_menu(rows, min(rows, this->max_screen_lines()));
449}
450
862fc625
JM
451void Panel::setup_menu(uint16_t rows, uint16_t lines)
452{
35089dc7 453 this->menu_selected_line = 0;
862fc625 454 this->menu_current_line = 0;
399cb110 455 this->menu_start_line = 0;
35089dc7 456 this->menu_rows = rows;
206167cf 457 this->panel_lines = lines;
35089dc7
JM
458}
459
862fc625
JM
460void Panel::menu_update()
461{
399cb110 462 // Limits, up and down
446deda2 463 // NOTE menu_selected_line is changed in an interrupt and can change at any time
862fc625 464 int msl = this->menu_selected_line; // hopefully this is atomic
206167cf
JM
465
466 #if 0
467 // this allows it to wrap but with new method we dont; want to wrap
862fc625
JM
468 msl = msl % ( this->menu_rows << this->menu_offset );
469 while ( msl < 0 ) {
470 msl += this->menu_rows << this->menu_offset;
471 }
206167cf
JM
472 #else
473 // limit selected line to screen lines available
474 if(msl >= this->menu_rows<<this->menu_offset){
475 msl= (this->menu_rows-1)<<this->menu_offset;
476 }else if(msl < 0) msl= 0;
477 #endif
446deda2 478
206167cf 479 this->menu_selected_line = msl; // update atomically we hope
b6e81799
JM
480 // figure out which actual line to select, if we have a menu offset it means we want to move one line per two clicks
481 if(msl % (this->menu_offset+1) == 0) { // only if divisible by offset
482 this->menu_current_line = msl >> this->menu_offset;
483 }
35089dc7
JM
484
485 // What to display
206167cf
JM
486 if ( this->menu_rows > this->panel_lines ) {
487 #if 0
488 // old way of scrolling not nice....
862fc625 489 if ( this->menu_current_line >= 2 ) {
446deda2 490 this->menu_start_line = this->menu_current_line - 1;
35089dc7 491 }
206167cf
JM
492 if ( this->menu_current_line > this->menu_rows - this->panel_lines ) {
493 this->menu_start_line = this->menu_rows - this->panel_lines;
494 }
495 #else
496 // new way we only scroll the lines when the cursor hits the bottom of the screen or the top of the screen
497 // do we want to scroll up?
498 int sl= this->menu_current_line - this->menu_start_line; // screen line we are on
499 if(sl >= this->panel_lines) {
500 this->menu_start_line += ((sl+1)-this->panel_lines); // scroll up to keep it on the screen
501
502 }else if(sl < 0 ) { // do we want to scroll down?
503 this->menu_start_line += sl; // scroll down
35089dc7 504 }
206167cf
JM
505 #endif
506
507 }else{
508 this->menu_start_line = 0;
35089dc7
JM
509 }
510
511 this->menu_changed = true;
512}
513
862fc625
JM
514bool Panel::menu_change()
515{
516 if ( this->menu_changed ) {
517 this->menu_changed = false;
518 return true;
519 } else {
520 return false;
521 }
35089dc7
JM
522}
523
862fc625
JM
524bool Panel::control_value_change()
525{
526 if ( this->control_value_changed ) {
527 this->control_value_changed = false;
528 return true;
529 } else {
530 return false;
531 }
35089dc7
JM
532}
533
1ad23cd3 534bool Panel::enter_control_mode(float passed_normal_increment, float passed_pressed_increment)
862fc625 535{
35089dc7
JM
536 this->mode = CONTROL_MODE;
537 this->normal_increment = passed_normal_increment;
35089dc7
JM
538 this->counter = &this->control_normal_counter;
539 this->control_normal_counter = 0;
58d6d841
JM
540 this->control_base_value = 0;
541 return true;
35089dc7
JM
542}
543
862fc625
JM
544void Panel::control_value_update()
545{
58d6d841
JM
546 // TODO what do we do here?
547 this->control_value_changed = true;
35089dc7
JM
548}
549
1ad23cd3 550void Panel::set_control_value(float value)
862fc625 551{
58d6d841 552 this->control_base_value = value;
35089dc7
JM
553}
554
1ad23cd3 555float Panel::get_control_value()
862fc625
JM
556{
557 return this->control_base_value + (this->control_normal_counter * this->normal_increment);
35089dc7
JM
558}
559
862fc625
JM
560bool Panel::is_playing() const
561{
58d6d841
JM
562 void *returned_data;
563
75e6428d 564 bool ok = PublicData::get_value( player_checksum, is_playing_checksum, &returned_data );
862fc625
JM
565 if (ok) {
566 bool b = *static_cast<bool *>(returned_data);
58d6d841
JM
567 return b;
568 }
569 return false;
7aa9fef3
JM
570}
571
862fc625
JM
572void Panel::set_playing_file(string f)
573{
7aa9fef3 574 // just copy the first 20 characters after the first / if there
862fc625
JM
575 size_t n = f.find_last_of('/');
576 if (n == string::npos) n = 0;
577 strncpy(playing_file, f.substr(n + 1, 19).c_str(), sizeof(playing_file));
578 playing_file[sizeof(playing_file) - 1] = 0;
7aa9fef3 579}
cee1bb2d
JM
580
581static float getTargetTemperature(uint16_t heater_cs)
582{
583 void *returned_data;
75e6428d 584 bool ok = PublicData::get_value( temperature_control_checksum, heater_cs, current_temperature_checksum, &returned_data );
cee1bb2d
JM
585
586 if (ok) {
587 struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_data);
588 return temp.target_temperature;
589 }
590
591 return 0.0F;
592}
593
594void Panel::setup_temperature_screen()
595{
596 // setup temperature screen
597 auto mvs= new ModifyValuesScreen();
598 this->temperature_screen= mvs;
599
600 // enumerate heaters and add a menu item for each one
601 vector<uint16_t> modules;
602 THEKERNEL->config->get_module_list( &modules, temperature_control_checksum );
603
604 for(auto i : modules) {
605 if (!THEKERNEL->config->value(temperature_control_checksum, i, enable_checksum )->as_bool()) continue;
606 void *returned_data;
75e6428d 607 bool ok = PublicData::get_value( temperature_control_checksum, i, current_temperature_checksum, &returned_data );
cee1bb2d
JM
608 if (!ok) continue;
609
610 struct pad_temperature t = *static_cast<struct pad_temperature *>(returned_data);
611
612 // rename if two of the known types
613 const char *name;
614 if(t.designator == "T") name= "Hotend";
615 else if(t.designator == "B") name= "Bed";
616 else name= t.designator.c_str();
617
618 mvs->addMenuItem(name, // menu name
619 [i]() -> float { return getTargetTemperature(i); }, // getter
75e6428d 620 [i](float t) { PublicData::set_value( temperature_control_checksum, i, &t ); }, // setter
cee1bb2d
JM
621 1.0F, // increment
622 0.0F, // Min
623 500.0F // Max
624 );
625 }
626}