From ca699af5417247ef6b8208d1dd82fe29cf9846d8 Mon Sep 17 00:00:00 2001 From: chrisws Date: Mon, 19 Sep 2022 21:47:33 +1000 Subject: [PATCH 1/9] ANDROID: added privacy policy link in about screen. --- configure.ac | 4 +++ .../android/app/src/main/assets/main.bas | 25 +++++++++++++++---- .../sourceforge/smallbasic/MainActivity.java | 4 +-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index c1eaea0e..5c0389ee 100644 --- a/configure.ac +++ b/configure.ac @@ -345,6 +345,10 @@ function buildWeb() { function buildEmscripten() { TARGET="Building Emscripten version." BUILD_SUBDIRS="src/common src/platform/emcc" + AC_CHECK_PROG(have_xxd, xxd, [yes], [no]) + if test "${have_xxd}" = "no" ; then + AC_MSG_ERROR([xxd command not installed: configure failed.]) + fi AC_CHECK_HEADERS([emscripten.h], [], [AC_MSG_ERROR([emscripten is not installed])]) AM_CONDITIONAL(WITH_CYGWIN_CONSOLE, false) AC_DEFINE(_UnixOS, 1, [Building under Unix like systems.]) diff --git a/src/platform/android/app/src/main/assets/main.bas b/src/platform/android/app/src/main/assets/main.bas index 98a2dc29..34167950 100644 --- a/src/platform/android/app/src/main/assets/main.bas +++ b/src/platform/android/app/src/main/assets/main.bas @@ -76,16 +76,20 @@ func mk_scratch() return result end -sub do_okay_button(bn_extra) +sub do_okay_button(bn_extra1, bn_extra2) local frm, button button.label = "[Close]" button.x = (xmax - txtw(button.label)) / 2 button.y = ypos * char_h button.color = colNav button.type = "link" - if (ismap(bn_extra)) then - frm.inputs << bn_extra + if (ismap(bn_extra1)) then + frm.inputs << bn_extra1 endif + if (ismap(bn_extra2)) then + frm.inputs << bn_extra2 + endif + frm.inputs << button frm = form(frm) print @@ -130,6 +134,17 @@ sub do_about() bn_home.color = colNav print:print + local bn_privacy + color colNav2 + bn_privacy.x = 2 + bn_privacy.y = ypos * char_h + char_h + 2 + bn_privacy.type = "link" + bn_privacy.isExternal = true + bn_privacy.color = colNav + bn_privacy.label = "https://smallbasic.github.io/pages/privacy.html" + print "Privacy Policy:" + print:print + color colText2 print "SmallBASIC comes with ABSOLUTELY NO WARRANTY. "; print "This program is free software; you can use it "; @@ -139,7 +154,7 @@ sub do_about() print color colText server_info() - do_okay_button(bn_home) + do_okay_button(bn_home, bn_privacy) clear_screen() end @@ -538,7 +553,7 @@ sub manageFiles() for i = 0 to len_buffer print buffer(i) next i - do_okay_button(nil) + do_okay_button(nil, nil) clear_screen() wnd.graphicsScreen1() f.value = selectedFile diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java index f3303ee1..e0ab07ba 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java @@ -735,7 +735,7 @@ private Uri getSharedFile(File file) { try { File sharesPath = new File(getExternalFilesDir(null), "shares"); if (sharesPath.mkdirs()) { - Log.i(TAG, "created folder: " + sharesPath.toString()); + Log.i(TAG, "created folder: " + sharesPath); } File shareFile = new File(sharesPath, file.getName()); copy(file, sharesPath); @@ -1056,5 +1056,5 @@ private Collection getFiles(File path) { } return result; } - }; + } } From f44f53f3c9080c94f20e09da77b3c5be1b298ca7 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 6 Nov 2022 19:15:42 +1000 Subject: [PATCH 2/9] ANDROID: fixed some Clang-tidy errors --- samples/distro-examples/tests/output/all.out | 2 +- src/platform/android/app/build.gradle | 6 +-- src/platform/android/build.gradle | 2 +- src/platform/android/jni/main.cpp | 2 +- src/platform/android/jni/runtime.cpp | 10 ++-- src/platform/android/jni/runtime.h | 4 +- src/platform/android/webui/package.json | 12 ++--- src/platform/android/webui/src/App.js | 2 +- src/ui/ansiwidget.cpp | 46 ++++++++--------- src/ui/ansiwidget.h | 2 +- src/ui/canvas.h | 6 +-- src/ui/form.cpp | 50 +++++++++--------- src/ui/graphics.cpp | 26 +++++----- src/ui/graphics.h | 10 ++-- src/ui/image.cpp | 10 +--- src/ui/inputs.cpp | 8 +-- src/ui/inputs.h | 8 +-- src/ui/screen.cpp | 54 ++++++++++---------- src/ui/strlib.cpp | 13 +++-- src/ui/strlib.h | 4 +- src/ui/system.cpp | 26 +++++----- src/ui/textedit.cpp | 46 ++++++++--------- src/ui/textedit.h | 8 +-- src/ui/theme.h | 34 ++++++------ src/ui/window.cpp | 6 +-- 25 files changed, 194 insertions(+), 203 deletions(-) diff --git a/samples/distro-examples/tests/output/all.out b/samples/distro-examples/tests/output/all.out index 35e92e09..98556040 100644 --- a/samples/distro-examples/tests/output/all.out +++ b/samples/distro-examples/tests/output/all.out @@ -206,7 +206,7 @@ STR:5 STRING:StringsStringsStringsStringsStrings SUM:45 SUMSQ:285 -TAB: +TAB: HERE TAN:-0.27285466095512 TANH:0.99999999995856 TEXTHEIGHT:1 diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 55317f9b..21b823de 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -8,8 +8,8 @@ android { defaultConfig { applicationId 'net.sourceforge.smallbasic' minSdkVersion 16 - targetSdkVersion 31 - versionCode 52 + targetSdkVersion 33 + versionCode 53 versionName '12.25' resConfigs 'en' } @@ -50,6 +50,6 @@ android { } dependencies { - implementation 'androidx.core:core:1.8.0' + implementation 'androidx.core:core:1.9.0' testImplementation 'junit:junit:4.13.2' } diff --git a/src/platform/android/build.gradle b/src/platform/android/build.gradle index 76a34721..f3783489 100644 --- a/src/platform/android/build.gradle +++ b/src/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.3.0' } } diff --git a/src/platform/android/jni/main.cpp b/src/platform/android/jni/main.cpp index 51fe7c93..2827b94a 100644 --- a/src/platform/android/jni/main.cpp +++ b/src/platform/android/jni/main.cpp @@ -17,7 +17,7 @@ void android_main(android_app *app) { logEntered(); - Runtime *runtime = new Runtime(app); + auto *runtime = new Runtime(app); // pump events until startup has completed while (runtime->isInitial()) { diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index bc614925..0eb85070 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "platform/android/jni/runtime.h" #include "lib/maapi.h" @@ -570,7 +570,7 @@ bool Runtime::loadSettings(Properties &settings) { FILE *fp = fopen(path.c_str(), "r"); if (fp) { String buffer; - struct stat st; + struct stat st{}; if (stat(path.c_str(), &st) == 0) { int len = st.st_size; buffer.append(fp, len); @@ -775,7 +775,7 @@ void Runtime::pause(int timeout) { } } else { int slept = 0; - while (1) { + while (true) { pollEvents(false); if (isBreak()) { break; @@ -1201,11 +1201,11 @@ void maWait(int timeout) { runtime->pause(timeout); } -void maShowVirtualKeyboard(void) { +void maShowVirtualKeyboard() { runtime->showKeypad(true); } -void maHideVirtualKeyboard(void) { +void maHideVirtualKeyboard() { runtime->showKeypad(false); } diff --git a/src/platform/android/jni/runtime.h b/src/platform/android/jni/runtime.h index 3e048917..b767378e 100644 --- a/src/platform/android/jni/runtime.h +++ b/src/platform/android/jni/runtime.h @@ -87,12 +87,12 @@ struct Runtime : public System { Graphics *_graphics; android_app *_app; Stack *_eventQueue; - pthread_mutex_t _mutex; + pthread_mutex_t _mutex{}; ALooper *_looper; ASensorManager *_sensorManager; const ASensor *_sensor; ASensorEventQueue *_sensorEventQueue; - ASensorEvent _sensorEvent; + ASensorEvent _sensorEvent{}; }; #endif diff --git a/src/platform/android/webui/package.json b/src/platform/android/webui/package.json index 75fe980a..4226644d 100644 --- a/src/platform/android/webui/package.json +++ b/src/platform/android/webui/package.json @@ -4,12 +4,12 @@ "private": true, "proxy": "http://localhost:8080", "dependencies": { - "@emotion/react": "^11.10.4", - "@emotion/styled": "^11.10.4", - "@mui/icons-material": "^5.10.3", - "@mui/material": "^5.10.4", - "@mui/x-data-grid": "^5.17.1", - "npm-check-updates": "^16.1.0", + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", + "@mui/icons-material": "^5.10.9", + "@mui/material": "^5.10.12", + "@mui/x-data-grid": "^5.17.10", + "npm-check-updates": "^16.3.16", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1" diff --git a/src/platform/android/webui/src/App.js b/src/platform/android/webui/src/App.js index 94a910f5..16e1d10a 100644 --- a/src/platform/android/webui/src/App.js +++ b/src/platform/android/webui/src/App.js @@ -182,7 +182,7 @@ function GridToolbarUpload(props) { diff --git a/src/ui/ansiwidget.cpp b/src/ui/ansiwidget.cpp index 7ee1d26f..53938458 100755 --- a/src/ui/ansiwidget.cpp +++ b/src/ui/ansiwidget.cpp @@ -6,13 +6,13 @@ // Download the GNU Public License (GPL) from www.gnu.org // -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "ui/ansiwidget.h" #include "ui/inputs.h" @@ -47,7 +47,6 @@ \e[27m set reverse off */ -#define BUTTON_PADDING 10 #define OVER_SCROLL 100 #define H_SCROLL_SIZE 10 #define SWIPE_MAX_TIMER 3000 @@ -73,8 +72,8 @@ AnsiWidget::AnsiWidget(int width, int height) : _touchTime(0), _swipeExit(false), _autoflush(true) { - for (int i = 0; i < MAX_SCREENS; i++) { - _screens[i] = nullptr; + for (auto & _screen : _screens) { + _screen = nullptr; } _fontSize = MIN(width, height) / FONT_FACTOR; trace("width: %d height: %d fontSize:%d", _width, height, _fontSize); @@ -100,8 +99,8 @@ bool AnsiWidget::construct() { // widget clean up AnsiWidget::~AnsiWidget() { logEntered(); - for (int i = 0; i < MAX_SCREENS; i++) { - delete _screens[i]; + for (auto & _screen : _screens) { + delete _screen; } } @@ -190,7 +189,7 @@ int AnsiWidget::getScreenId(bool back) { // prints the contents of the given string onto the backbuffer void AnsiWidget::print(const char *str) { - int len = (str == nullptr ? 0 : strlen(str)); + unsigned len = (str == nullptr ? 0 : strlen(str)); if (len) { _back->drawInto(); @@ -264,8 +263,7 @@ void AnsiWidget::reset() { // update the widget to new dimensions void AnsiWidget::resize(int newWidth, int newHeight) { int lineHeight = textHeight(); - for (int i = 0; i < MAX_SCREENS; i++) { - Screen *screen = _screens[i]; + for (auto screen : _screens) { if (screen) { screen->resize(newWidth, newHeight, _width, _height, lineHeight); if (screen == _front) { @@ -338,9 +336,9 @@ void AnsiWidget::setFont(int size, bool bold, bool italic) { // sets the text font size void AnsiWidget::setFontSize(int fontSize) { this->_fontSize = fontSize; - for (int i = 0; i < MAX_SCREENS; i++) { - if (_screens[i] != nullptr) { - _screens[i]->reset(fontSize); + for (auto & _screen : _screens) { + if (_screen != nullptr) { + _screen->reset(fontSize); } } redraw(); @@ -383,7 +381,7 @@ void AnsiWidget::insetMenuScreen(int x, int y, int w, int h) { if (_back == _screens[MENU_SCREEN]) { _back = _screens[USER_SCREEN1]; } - TextScreen *menuScreen = (TextScreen *)createScreen(MENU_SCREEN); + auto *menuScreen = (TextScreen *)createScreen(MENU_SCREEN); menuScreen->_x = x; menuScreen->_y = y; menuScreen->_width = w; @@ -397,7 +395,7 @@ void AnsiWidget::insetTextScreen(int x, int y, int w, int h) { if (_back == _screens[TEXT_SCREEN]) { _back = _screens[USER_SCREEN1]; } - TextScreen *textScreen = (TextScreen *)createScreen(TEXT_SCREEN); + auto *textScreen = (TextScreen *)createScreen(TEXT_SCREEN); textScreen->inset(x, y, w, h, _front); _front = _back = textScreen; _front->_dirty = true; @@ -412,10 +410,10 @@ bool AnsiWidget::pointerTouchEvent(MAEvent &event) { _focus = _front; } else { // hit test buttons on remaining screens - for (int i = 0; i < MAX_SCREENS; i++) { - if (_screens[i] != nullptr && _screens[i] != _front) { - if (setActiveButton(event, _screens[i])) { - _focus = _screens[i]; + for (auto & _screen : _screens) { + if (_screen != nullptr && _screen != _front) { + if (setActiveButton(event, _screen)) { + _focus = _screen; break; } } diff --git a/src/ui/ansiwidget.h b/src/ui/ansiwidget.h index f26f7c50..8b6c0348 100755 --- a/src/ui/ansiwidget.h +++ b/src/ui/ansiwidget.h @@ -111,7 +111,7 @@ struct AnsiWidget { void handleEscape(const char *&p, int textHeight); bool setActiveButton(MAEvent &event, Screen *screen); - Screen *_screens[MAX_SCREENS]; + Screen *_screens[MAX_SCREENS]{}; Screen *_back; // screen being painted/written Screen *_front; // screen to display Screen *_focus; // screen with the active button diff --git a/src/ui/canvas.h b/src/ui/canvas.h index 86b0a3ca..339858cd 100644 --- a/src/ui/canvas.h +++ b/src/ui/canvas.h @@ -32,9 +32,9 @@ struct Canvas { int _w; int _h; pixel_t *_pixels; - SDL_Surface *_surface; - SDL_Rect *_clip; - bool _ownerSurface; + SDL_Surface *_surface{}; + SDL_Rect *_clip{}; + bool _ownerSurface{}; }; #else diff --git a/src/ui/form.cpp b/src/ui/form.cpp index 13a44980..8cd9a85f 100644 --- a/src/ui/form.cpp +++ b/src/ui/form.cpp @@ -16,7 +16,7 @@ extern System *g_system; extern FormInput *focusInput; -var_p_t form = NULL; +var_p_t form = nullptr; enum Mode { m_init, @@ -25,11 +25,11 @@ enum Mode { } mode = m_init; const char *FormInput::getValue() { - const char *result = NULL; + const char *result = nullptr; var_p_t field = getField(form); - if (field != NULL) { + if (field != nullptr) { var_p_t value = map_get(field, FORM_INPUT_VALUE); - if (value != NULL && value->type == V_STR) { + if (value != nullptr && value->type == V_STR) { result = value->v.p.ptr; } } @@ -45,10 +45,10 @@ void FormInput::clicked(int x, int y, bool pressed) { cmd_push_args(kwPROC, prog_ip, INVALID_ADDR); bc_loop(2); prog_ip = ip; - } else if (form != NULL) { + } else if (form != nullptr) { if (_exit) { const char *value = getValue(); - g_system->setLoadBreak(value != NULL ? value : getText()); + g_system->setLoadBreak(value != nullptr ? value : getText()); } else { selected(); @@ -58,10 +58,10 @@ void FormInput::clicked(int x, int y, bool pressed) { } void FormInput::selected() { - if (form != NULL && g_system->isRunning()) { + if (form != nullptr && g_system->isRunning()) { setFocus(true); updateForm(form); - if (focusInput != NULL) { + if (focusInput != nullptr) { focusInput->updateField(form); } mode = m_selected; @@ -85,7 +85,7 @@ bool FormLineInput::selected(MAPoint2d pt, int scrollX, int scrollY, bool &redra } void FormDropList::clicked(int x, int y, bool pressed) { - if (form != NULL && !pressed && g_system->isRunning()) { + if (form != nullptr && !pressed && g_system->isRunning()) { setFocus(true); updateForm(form); _listActive = !_listActive; @@ -97,7 +97,7 @@ void FormDropList::clicked(int x, int y, bool pressed) { } void FormListBox::clicked(int x, int y, bool pressed) { - if (form != NULL && !pressed && g_system->isRunning()) { + if (form != nullptr && !pressed && g_system->isRunning()) { setFocus(true); if (_activeIndex != -1) { optionSelected(_activeIndex + _topIndex); @@ -108,9 +108,9 @@ void FormListBox::clicked(int x, int y, bool pressed) { void FormLink::clicked(int x, int y, bool pressed) { setFocus(true); - if (!pressed && _external && form != NULL && g_system->isRunning()) { + if (!pressed && _external && form != nullptr && g_system->isRunning()) { const char *value = getValue(); - g_system->browseFile(value != NULL ? value : _link.c_str()); + g_system->browseFile(value != nullptr ? value : _link.c_str()); } else { FormInput::clicked(x, y, pressed); } @@ -162,7 +162,7 @@ void cmd_form_do_events(var_s *self, var_s *) { dev_clrkb(); focusInput = out->getNextField(focusInput); out->setDirty(); - } else if (focusInput != NULL && + } else if (focusInput != nullptr && event.key != SB_KEY_MENU && focusInput->edit(event.key, sw, charWidth)) { dev_clrkb(); @@ -178,14 +178,14 @@ void cmd_form_do_events(var_s *self, var_s *) { } } } - form = NULL; + form = nullptr; } } int get_selected_index(var_p_t v_field) { var_p_t value = map_get(v_field, FORM_INPUT_INDEX); int result; - if (value == NULL) { + if (value == nullptr) { result = 0; map_add_var(v_field, FORM_INPUT_INDEX, result); } else { @@ -205,17 +205,17 @@ FormInput *create_input(var_p_t v_field) { const char *help = map_get_str(v_field, FORM_INPUT_HELP); var_p_t value = map_get(v_field, FORM_INPUT_VALUE); - if (label == NULL) { + if (label == nullptr) { label = ""; } - if (value == NULL) { + if (value == nullptr) { value = map_add_var(v_field, FORM_INPUT_VALUE, 0); v_setstr(value, label); } - FormInput *widget = NULL; - if (type == NULL || strcasecmp("button", type) == 0) { + FormInput *widget = nullptr; + if (type == nullptr || strcasecmp("button", type) == 0) { widget = new FormButton(label, x, y, w, h); } else if (strcasecmp("label", type) == 0) { widget = new FormLabel(label, x, y, w, h); @@ -237,7 +237,7 @@ FormInput *create_input(var_p_t v_field) { if (maxSize < 1 || maxSize > 1024) { maxSize = 100; } - const char *text = NULL; + const char *text = nullptr; if (value->type == V_STR) { text = value->v.p.ptr; } @@ -249,7 +249,7 @@ FormInput *create_input(var_p_t v_field) { } else if (strcasecmp("image", type) == 0) { const char *name = map_get_str(v_field, FORM_INPUT_NAME); ImageDisplay *image = create_display_image(v_field, name); - if (image != NULL) { + if (image != nullptr) { widget = new FormImage(image, x, y); } } else if (strcasecmp("print", type) == 0) { @@ -270,7 +270,7 @@ extern "C" void v_create_form(var_p_t var) { arg = code_getvarptr(); if (arg->type == V_MAP) { var_p_t inputs = map_get(arg, FORM_INPUTS); - if (inputs != NULL && inputs->type == V_ARRAY) { + if (inputs != nullptr && inputs->type == V_ARRAY) { for (unsigned i = 0; i < v_asize(inputs); i++) { var_p_t elem = v_elem(inputs, i); if (elem->type == V_MAP) { @@ -285,13 +285,13 @@ extern "C" void v_create_form(var_p_t var) { set_input_defaults(out->getColor(), out->getBackgroundColor()); map_set(var, arg); var_p_t v_focus = map_get(var, FORM_FOCUS); - unsigned i_focus = v_focus != NULL ? v_getint(v_focus) : -1; + unsigned i_focus = v_focus != nullptr ? v_getint(v_focus) : -1; var_p_t inputs = map_get(var, FORM_INPUTS); - for (unsigned i = 0; inputs != NULL && i < v_asize(inputs); i++) { + for (unsigned i = 0; inputs != nullptr && i < v_asize(inputs); i++) { var_p_t elem = v_elem(inputs, i); if (elem->type == V_MAP) { FormInput *widget = create_input(elem); - if (widget != NULL) { + if (widget != nullptr) { widget->construct(var, elem, i); out->addInput(widget); if (i_focus == i || v_asize(inputs) == 1) { diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index 16b55d26..18d51397 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -10,7 +10,7 @@ #include "ui/graphics.h" #include "ui/utils.h" -#include +#include #include "common/smbas.h" #include "common/device.h" @@ -31,7 +31,7 @@ inline double fpart(double x) { } #define _SWAP(a, b) \ - { __typeof__(a) tmp; tmp = a; a = b; b = tmp; } + { __typeof__(a) tmp; tmp = a; (a) = b; (b) = tmp; } Font::Font(FT_Face face, int size, bool italic) : _face(face) { @@ -58,7 +58,7 @@ Font::Font(FT_Face face, int size, bool italic) : trace("Failed to get glyph %d", i); } if (italic) { - FT_Glyph_Transform(_glyph[i]._slot, &matrix, 0 ); + FT_Glyph_Transform(_glyph[i]._slot, &matrix, nullptr ); } FT_Vector origin; origin.x = 0; @@ -72,8 +72,8 @@ Font::Font(FT_Face face, int size, bool italic) : } Font::~Font() { - for (int i = 0; i < MAX_GLYPHS; i++) { - FT_Done_Glyph(_glyph[i]._slot); + for (auto & i : _glyph) { + FT_Done_Glyph(i._slot); } } @@ -278,7 +278,7 @@ void Graphics::drawPixel(int posX, int posY) { void Graphics::drawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int stride) { - uint8_t *image = (uint8_t *)src; + auto *image = (uint8_t *)src; float op = opacity / 100.0f; int top = srcRect->top; int left = srcRect->left; @@ -358,7 +358,7 @@ void Graphics::drawText(int left, int top, const char *str, int len) { pen.y = top + _font->_h + ((_font->_spacing - _font->_h) / 2); for (int i = 0; i < len; i++) { uint8_t ch = str[i]; - FT_BitmapGlyph glyph = (FT_BitmapGlyph)_font->_glyph[ch]._slot; + auto glyph = (FT_BitmapGlyph)_font->_glyph[ch]._slot; drawChar(&glyph->bitmap, pen.x + glyph->left, pen.y - glyph->top); @@ -583,7 +583,7 @@ void Graphics::line2(int xc, int yc, int x, int y) { // // maapi implementation // -MAHandle maCreatePlaceholder(void) { +MAHandle maCreatePlaceholder() { MAHandle maHandle = (MAHandle) new Canvas(); return maHandle; } @@ -652,7 +652,7 @@ MAExtent maGetTextSize(const char *str) { return result; } -MAExtent maGetScrSize(void) { +MAExtent maGetScrSize() { short width = graphics->getWidth(); short height = graphics->getHeight(); return (MAExtent)((width << 16) + height); @@ -672,20 +672,20 @@ MAHandle maFontSetCurrent(MAHandle maHandle) { void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d *dstPoint, int transformMode) { Canvas *drawTarget = graphics->getDrawTarget(); - Canvas *src = (Canvas *)maHandle; + auto *src = (Canvas *)maHandle; if (drawTarget && drawTarget != src) { drawTarget->drawRegion(src, srcRect, dstPoint->x, dstPoint->y); } } void maDestroyPlaceholder(MAHandle maHandle) { - Canvas *holder = (Canvas *)maHandle; + auto *holder = (Canvas *)maHandle; delete holder; } void maGetImageData(MAHandle maHandle, void *dst, const MARect *srcRect, int stride) { - Canvas *holder = (Canvas *)maHandle; + auto *holder = (Canvas *)maHandle; if (srcRect->width == 1 && srcRect->height == 1) { *((int *)dst) = graphics->getPixel(holder, srcRect->left, srcRect->top); } else { @@ -703,7 +703,7 @@ int maCreateDrawableImage(MAHandle maHandle, int width, int height) { if (height > maxSize * MAX_CANVAS_SIZE) { result -= 1; } else { - Canvas *drawable = (Canvas *)maHandle; + auto *drawable = (Canvas *)maHandle; result = drawable->create(width, height) ? RES_OK : -1; } return result; diff --git a/src/ui/graphics.h b/src/ui/graphics.h index de48875a..6073a8b3 100644 --- a/src/ui/graphics.h +++ b/src/ui/graphics.h @@ -35,7 +35,7 @@ struct Font { int _h; int _spacing; FT_Face _face; - Glyph _glyph[MAX_GLYPHS]; + Glyph _glyph[MAX_GLYPHS]{}; }; struct Graphics { @@ -76,13 +76,13 @@ struct Graphics { void plot4(int xc, int yc, int x, int y); void line2(int xc, int yc, int x, int y); - FT_Library _fontLibrary; - FT_Face _fontFace; - FT_Face _fontFaceB; + FT_Library _fontLibrary{}; + FT_Face _fontFace{}; + FT_Face _fontFaceB{}; Canvas *_screen; Canvas *_drawTarget; Font *_font; - pixel_t _drawColor; + pixel_t _drawColor{}; }; } diff --git a/src/ui/image.cpp b/src/ui/image.cpp index ddab2f71..b0797ceb 100644 --- a/src/ui/image.cpp +++ b/src/ui/image.cpp @@ -46,13 +46,7 @@ ImageBuffer::ImageBuffer() : _height(0) { } -ImageBuffer::ImageBuffer(ImageBuffer &o) : - _filename(o._filename), - _image(o._image), - _bid(o._bid), - _width(o._width), - _height(o._height) { -} +ImageBuffer::ImageBuffer(ImageBuffer &o) = default; ImageBuffer::~ImageBuffer() { free(_filename); @@ -667,7 +661,7 @@ extern "C" void v_create_image(var_p_t var) { // load from 2d array image = load_image(&arg); } else if (elem0->type == V_INT) { - unsigned char *data = new unsigned char[v_asize(&arg)]; + auto *data = new unsigned char[v_asize(&arg)]; for (unsigned i = 0; i < v_asize(&arg); i++) { var_p_t elem = v_elem(&arg, i); data[i] = (unsigned char)elem->v.i; diff --git a/src/ui/inputs.cpp b/src/ui/inputs.cpp index 932786c2..d64b8d28 100644 --- a/src/ui/inputs.cpp +++ b/src/ui/inputs.cpp @@ -270,16 +270,16 @@ bool FormInput::selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw) { return FormInput::overlaps(pt, scrollX, scrollY); } -void FormInput::setTextColor() { +void FormInput::setTextColor() const { maSetColor(hasFocus() || _noFocus ? _fg : lerp(_bg, _fg, 0.7)); } -void FormInput::setHelpTextColor() { +void FormInput::setHelpTextColor() const { maSetColor(lerp(_bg, _fg, 0.4)); } // returns the field var attached to the field -var_p_t FormInput::getField(var_p_t form) { +var_p_t FormInput::getField(var_p_t form) const { var_p_t result = nullptr; if (form->type == V_MAP) { var_p_t inputs = map_get(form, FORM_INPUTS); @@ -849,7 +849,7 @@ void ListModel::fromArray(var_t *v) { // return the text at the given index const char *ListModel::getTextAt(int index) { - const char *s = 0; + const char *s = nullptr; if (index > -1 && index < _list.size()) { s = _list[index]->c_str(); } diff --git a/src/ui/inputs.h b/src/ui/inputs.h index 3e9ee730..b04307a3 100644 --- a/src/ui/inputs.h +++ b/src/ui/inputs.h @@ -134,19 +134,19 @@ struct FormInput : public Shape { bool hasFocus() const; void hide() { _visible = false; } int getId() { return _id; } - var_p_t getField(var_p_t form); + var_p_t getField(var_p_t form) const; bool isNoFocus() { return _noFocus; } bool isResizable() { return _resizable; } bool isVisible() { return _visible; } void setColor(int bg, int fg) { _bg = bg; _fg = fg; } - void setTextColor(); - void setHelpTextColor(); + void setTextColor() const; + void setHelpTextColor() const; void selected(); void show() { _visible = true; } bool _pressed; protected: - bool __padding[3]; + bool __padding[3]{}; int _id; bool _exit; bool _visible; diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index e91e515d..e4a113f1 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -6,7 +6,7 @@ // Download the GNU Public License (GPL) from www.gnu.org // -#include +#include #include "ui/screen.h" @@ -22,8 +22,8 @@ rect->draw(_x + rect->_x, _y + rect->_y - _scrollY, w(), h(), _charWidth) int compareZIndex(const void *p1, const void *p2) { - ImageDisplay **i1 = (ImageDisplay **)p1; - ImageDisplay **i2 = (ImageDisplay **)p2; + auto **i1 = (ImageDisplay **)p1; + auto **i2 = (ImageDisplay **)p2; return (*i1)->_zIndex < (*i2)->_zIndex ? -1 : (*i1)->_zIndex == (*i2)->_zIndex ? 0 : 1; } @@ -140,7 +140,7 @@ void Screen::drawMenu() { } void Screen::drawShape(Shape *rect) { - if (rect != NULL && + if (rect != nullptr && rect->_y >= _scrollY && rect->_y + rect->_height <= _scrollY + _height) { rect->draw(_x + rect->_x, _y + rect->_y - _scrollY, w(), h(), _charWidth); @@ -167,7 +167,7 @@ void Screen::drawOverlay(bool vscroll) { DRAW_SHAPE; } - FormInput *drawTop = NULL; + FormInput *drawTop = nullptr; List_each(FormInput *, it, _inputs) { FormInput *input = (*it); if (input->_y >= _scrollY - _height && @@ -179,7 +179,7 @@ void Screen::drawOverlay(bool vscroll) { } } } - if (drawTop != NULL) { + if (drawTop != nullptr) { drawTop->draw(_x + drawTop->_x, _y + drawTop->_y - _scrollY, w(), h(), _charWidth); } @@ -220,7 +220,7 @@ void Screen::drawInto(bool background) { int Screen::getIndex(FormInput *input) const { int index; - if (input == NULL) { + if (input == nullptr) { index = -1; } else { index = 0; @@ -238,19 +238,19 @@ int Screen::getIndex(FormInput *input) const { FormInput *Screen::getMenu(FormInput *prev, int px, int py) { FormInput *result = _inputs[0]; - if (result != NULL && overlaps(px, py)) { + if (result != nullptr && overlaps(px, py)) { int item = (py - _y) / result->_height; result = _inputs[item]; } else { - result = NULL; + result = nullptr; } if (result != prev) { MAHandle currentHandle = maSetDrawTarget(HANDLE_SCREEN); - if (prev != NULL) { + if (prev != nullptr) { prev->_pressed = false; drawShape(prev); } - if (result != NULL) { + if (result != nullptr) { result->_pressed = true; drawShape(result); } @@ -262,7 +262,7 @@ FormInput *Screen::getMenu(FormInput *prev, int px, int py) { FormInput *Screen::getNextMenu(FormInput *prev, bool up) { int index; - if (prev == NULL) { + if (prev == nullptr) { index = 0; } else { index = getIndex(prev) + (up ? -1 : 1); @@ -273,7 +273,7 @@ FormInput *Screen::getNextMenu(FormInput *prev, bool up) { next = _inputs.get(index); next->_pressed = true; drawShape(next); - if (prev != NULL) { + if (prev != nullptr) { prev->_pressed = false; drawShape(prev); } @@ -432,15 +432,15 @@ void Screen::setFont(bool bold, bool italic, int size) { } FormInput *Screen::getNextField(FormInput *field) { - FormInput *result = NULL; + FormInput *result = nullptr; bool setNext = false; List_each(FormInput *, it, _inputs) { FormInput *next = (*it); if (!next->isNoFocus()) { - if (result == NULL) { + if (result == nullptr) { // set result to first item result = next; - if (field == NULL) { + if (field == nullptr) { // no next item break; } @@ -463,7 +463,7 @@ void Screen::updateInputs(var_p_t form, bool setVars) { next->updateField(form); } else { var_p_t field = next->getField(form); - if (field == NULL) { + if (field == nullptr) { _inputs.remove(it); delete next; setDirty(); @@ -481,10 +481,10 @@ void Screen::updateInputs(var_p_t form, bool setVars) { GraphicScreen::GraphicScreen(int width, int height, int fontSize) : Screen(0, 0, width, height, fontSize), _image(0), - _underline(0), - _invert(0), - _bold(0), - _italic(0), + _underline(false), + _invert(false), + _bold(false), + _italic(false), _imageWidth(width), _imageHeight(height), _curYSaved(0), @@ -939,9 +939,9 @@ struct RectFilledShape : Shape { // TextScreen::TextScreen(int width, int height, int fontSize) : Screen(0, 0, width, height, fontSize), - _over(NULL), + _over(nullptr), _inset(0, 0, 0, 0), - _buffer(NULL), + _buffer(nullptr), _head(0), _tail(0), _rows(TEXT_ROWS), @@ -960,7 +960,7 @@ void TextScreen::calcTab() { bool TextScreen::construct() { reset(_fontSize); _buffer = new Row[_rows]; - return (_buffer != NULL); + return (_buffer != nullptr); } // @@ -995,7 +995,7 @@ void TextScreen::drawBase(bool vscroll, bool update) { numRows = textRows - firstRow; } - if (_over != NULL && _over != this) { + if (_over != nullptr && _over != this) { _over->drawBase(vscroll, false); } @@ -1013,7 +1013,7 @@ void TextScreen::drawBase(bool vscroll, bool update) { Row *line = getLine(row); // next logical row TextSeg *seg = line->_head; int px = (_x + INITXY) - _scrollX; - while (seg != NULL) { + while (seg != nullptr) { if (seg->escape(&bold, &italic, &underline, &invert)) { setFont(bold, italic, _fontSize); } else if (seg->isReset()) { @@ -1135,7 +1135,7 @@ void TextScreen::resize(int newWidth, int newHeight, int, int, int) { // int TextScreen::print(const char *p, int lineHeight, bool allChars) { Row *line = getLine(_head); - TextSeg *segment = new TextSeg(); + auto *segment = new TextSeg(); line->append(segment); int numChars = Screen::print(p, lineHeight, true); diff --git a/src/ui/strlib.cpp b/src/ui/strlib.cpp index b91be42e..c9525ec3 100644 --- a/src/ui/strlib.cpp +++ b/src/ui/strlib.cpp @@ -51,11 +51,11 @@ const String &String::operator=(const char *s) { return *this; } -const void String::operator+=(const String &s) { +void String::operator+=(const String &s) { append(s._buffer); } -const void String::operator+=(const char *s) { +void String::operator+=(const char *s) { append(s); } @@ -259,8 +259,7 @@ template<> void List::add(const char *s) { template<> bool List::contains(const char *s) { bool result = false; - for (String **it = begin(); it != end(); it++) { - String *next = (*it); + for (auto next : *this) { if (next->equals(s)) { result = true; break; @@ -278,7 +277,7 @@ template<> void Properties::load(const char *s) { } template<> void Properties::load(const char *s, int slen) { - if (s == 0 || s[0] == 0 || slen == 0) { + if (s == nullptr || s[0] == 0 || slen == 0) { return; } @@ -366,11 +365,11 @@ template<> void Properties::put(const char *key, const char *value) { template<> void Properties::get(const char *key, List *arrayValues) { for (int i = 0; i < _count; i++) { - String *nextKey = (String *)_head[i++]; + auto *nextKey = (String *)_head[i++]; if (nextKey == nullptr || i == _count) { break; } - String *nextValue = (String *)_head[i]; + auto *nextValue = (String *)_head[i]; if (nextValue == nullptr) { break; } diff --git a/src/ui/strlib.h b/src/ui/strlib.h index 6779de73..d35722b5 100644 --- a/src/ui/strlib.h +++ b/src/ui/strlib.h @@ -33,8 +33,8 @@ struct String { const String &operator=(const String &s); const String &operator=(const char *s); const String &operator=(const char c); - const void operator+=(const String &s); - const void operator+=(const char *s); + void operator+=(const String &s); + void operator+=(const char *s); String &append(const String &s); String &append(const String *s); String &append(int i); diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 58555df6..dd50437e 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -8,10 +8,10 @@ #include "config.h" -#include +#include #include -#include -#include +#include +#include #include "include/osd.h" #include "common/sbapp.h" @@ -225,7 +225,7 @@ bool System::fileExists(strlib::String &path) { if (path.indexOf("://", 1) != -1) { result = true; } else if (!path.empty()) { - struct stat st_file; + struct stat st_file{}; result = stat(path.c_str(), &st_file) == 0; } return result; @@ -337,7 +337,7 @@ char *System::getText(char *dest, int maxSize) { uint32_t System::getModifiedTime() { uint32_t result = 0; if (!_activeFile.empty()) { - struct stat st_file; + struct stat st_file{}; if (!stat(_activeFile.c_str(), &st_file)) { result = st_file.st_mtime; } @@ -687,19 +687,19 @@ void System::optionsBox(StringList *items) { _menuX = 0; _menuY = 0; if (selectedIndex != -1) { - if (_systemMenu == NULL && isRunning() && + if (_systemMenu == nullptr && isRunning() && !form_ui::optionSelected(selectedIndex)) { dev_clrkb(); dev_pushkey(selectedIndex); } else { - MAEvent *maEvent = new MAEvent(); + auto *maEvent = new MAEvent(); maEvent->type = EVENT_TYPE_OPTIONS_BOX_BUTTON_CLICKED; maEvent->optionsBoxButtonIndex = selectedIndex; maPushEvent(maEvent); } } else { delete [] _systemMenu; - _systemMenu = NULL; + _systemMenu = nullptr; } _output->redraw(); @@ -715,7 +715,7 @@ char *System::readSource(const char *fileName) { if (!buffer) { int h = open(fileName, O_BINARY | O_RDONLY); if (h != -1) { - struct stat st; + struct stat st{}; if (fstat(h, &st) == 0) { int len = st.st_size; buffer = (char *)malloc(len + 1); @@ -919,9 +919,9 @@ void System::setBack() { if (!_mainBas) { // remove the current item strlib::String *old = _history.pop(); - if (old) { + delete old; - } + if (_history.peek() != nullptr) { _loadPath.clear(); _loadPath.append(_history.peek()); @@ -980,7 +980,7 @@ void System::setupPath(String &loadPath) { strncpy(path, filename, len); path[len] = 0; chdir(path); - struct stat st_file; + struct stat st_file{}; if (stat(loadPath.c_str(), &st_file) < 0) { // reset relative path back to full path getcwd(path, FILENAME_MAX); @@ -1478,7 +1478,7 @@ char *dev_read(const char *fileName) { return g_system->readSource(fileName); } -int maGetMilliSecondCount(void) { +int maGetMilliSecondCount() { return dev_get_millisecond_count(); } diff --git a/src/ui/textedit.cpp b/src/ui/textedit.cpp index d106f13b..6102582a 100644 --- a/src/ui/textedit.cpp +++ b/src/ui/textedit.cpp @@ -6,8 +6,8 @@ // Download the GNU Public License (GPL) from www.gnu.org // -#include -#include +#include +#include #include "ui/textedit.h" #include "ui/inputs.h" @@ -22,8 +22,8 @@ void safe_memmove(void *dest, const void *src, size_t n) { } #define STB_TEXTEDIT_IS_SPACE(ch) IS_WHITE(ch) -#define STB_TEXTEDIT_IS_PUNCT(ch) (ch != '_' && ch != '$' && ispunct(ch)) -#define IS_VAR_CHAR(ch) (ch == '_' || ch == '$' || isalpha(ch) || isdigit(ch)) +#define STB_TEXTEDIT_IS_PUNCT(ch) ((ch) != '_' && (ch) != '$' && ispunct(ch)) +#define IS_VAR_CHAR(ch) ((ch) == '_' || (ch) == '$' || isalpha(ch) || isdigit(ch)) #define STB_TEXTEDIT_memmove safe_memmove #define STB_TEXTEDIT_IMPLEMENTATION @@ -402,7 +402,7 @@ int EditBuffer::deleteChars(int pos, int num) { return 1; } -char EditBuffer::getChar(int pos) { +char EditBuffer::getChar(int pos) const { char result; if (_buffer != nullptr && pos >= 0 && pos < _len) { result = _buffer[pos]; @@ -443,7 +443,7 @@ int EditBuffer::lineCount() { return _lines; } -char *EditBuffer::textRange(int start, int end) { +char *EditBuffer::textRange(int start, int end) const { char *result; int len; if (start < 0 || start > _len || end <= start) { @@ -546,13 +546,13 @@ const char *TextEditInput::completeKeyword(int index) { if (selection != nullptr) { int len = strlen(selection); int count = 0; - for (int i = 0; i < keyword_help_len; i++) { - if (strncasecmp(selection, keyword_help[i].keyword, len) == 0 && + for (auto & i : keyword_help) { + if (strncasecmp(selection, i.keyword, len) == 0 && count++ == index) { if (IS_WHITE(_buf._buffer[_state.cursor]) || _buf._buffer[_state.cursor] == '\0') { - completeWord(keyword_help[i].keyword); + completeWord(i.keyword); } - help = keyword_help[i].signature; + help = i.signature; break; } } @@ -1488,7 +1488,7 @@ int TextEditInput::getCompletions(StringList *list, int max) { if (len > 0) { for (int i = 0; i < keyword_help_len && count < max; i++) { if (strncasecmp(selection, keyword_help[i].keyword, len) == 0) { - String *s = new String(); + auto *s = new String(); s->append(" "); s->append(keyword_help[i].keyword); list->add(s); @@ -1601,7 +1601,7 @@ int TextEditInput::getIndent(char *spaces, int len, int pos) { return i; } -int TextEditInput::getLineChars(StbTexteditRow *row, int pos) { +int TextEditInput::getLineChars(StbTexteditRow *row, int pos) const { int numChars = row->num_chars; if (numChars > 0 && _buf._buffer[pos + row->num_chars - 1] == '\r') { numChars--; @@ -2084,10 +2084,10 @@ void TextEditHelpWidget::createCompletionHelp() { int len = selection != nullptr ? strlen(selection) : 0; if (len > 0) { StringList words; - for (int i = 0; i < keyword_help_len; i++) { - if (strncasecmp(selection, keyword_help[i].keyword, len) == 0) { - words.add(keyword_help[i].keyword); - _buf.append(keyword_help[i].keyword); + for (auto & i : keyword_help) { + if (strncasecmp(selection, i.keyword, len) == 0) { + words.add(i.keyword); + _buf.append(i.keyword); _buf.append("\n", 1); } } @@ -2112,15 +2112,15 @@ void TextEditHelpWidget::createCompletionHelp() { } } else { const char *package = nullptr; - for (int i = 0; i < keyword_help_len; i++) { - if (package == nullptr || strcasecmp(package, keyword_help[i].package) != 0) { + for (auto & i : keyword_help) { + if (package == nullptr || strcasecmp(package, i.package) != 0) { // next package - package = keyword_help[i].package; + package = i.package; _buf.append("["); _buf.append(package); _buf.append("]\n"); } - _buf.append(keyword_help[i].keyword); + _buf.append(i.keyword); _buf.append("\n", 1); } } @@ -2164,9 +2164,9 @@ void TextEditHelpWidget::createKeywordIndex() { if (!keywordFound) { const char *package = nullptr; - for (int i = 0; i < keyword_help_len; i++) { - if (package == nullptr || strcasecmp(package, keyword_help[i].package) != 0) { - package = keyword_help[i].package; + for (auto & i : keyword_help) { + if (package == nullptr || strcasecmp(package, i.package) != 0) { + package = i.package; _buf.append(TWISTY1_OPEN, TWISTY1_LEN); _buf.append(package); _buf.append("\n", 1); diff --git a/src/ui/textedit.h b/src/ui/textedit.h index f82558f3..b9939f5b 100644 --- a/src/ui/textedit.h +++ b/src/ui/textedit.h @@ -52,11 +52,11 @@ struct EditBuffer { void clear(); int countNewlines(const char *text, int num); int deleteChars(int pos, int num); - char getChar(int pos); + char getChar(int pos) const; int insertChars(int pos, const char *text, int num); int lineCount(); void removeTrailingSpaces(STB_TexteditState *state); - char *textRange(int start, int end); + char *textRange(int start, int end) const; }; struct TextEditInput : public FormEditInput { @@ -136,7 +136,7 @@ struct TextEditInput : public FormEditInput { int getCursorRow(); uint32_t getHash(const char *str, int offs, int &count); int getIndent(char *spaces, int len, int pos); - int getLineChars(StbTexteditRow *row, int pos); + int getLineChars(StbTexteditRow *row, int pos) const; char *getSelection(int *start, int *end); void gotoNextMarker(); void killWord(); @@ -157,7 +157,7 @@ struct TextEditInput : public FormEditInput { int wordStart(); EditBuffer _buf; - STB_TexteditState _state; + STB_TexteditState _state{}; EditTheme *_theme; int _charWidth; int _charHeight; diff --git a/src/ui/theme.h b/src/ui/theme.h index 5ddccee4..ae5c35f2 100644 --- a/src/ui/theme.h +++ b/src/ui/theme.h @@ -24,23 +24,23 @@ struct EditTheme { void selectTheme(const int theme[]); void contrast(EditTheme *other); - int _color; - int _background; - int _selection_color; - int _selection_background; - int _number_color; - int _number_selection_color; - int _number_selection_background; - int _cursor_color; - int _cursor_background; - int _match_background; - int _row_cursor; - int _syntax_comments; - int _syntax_text; - int _syntax_command; - int _syntax_statement; - int _syntax_digit; - int _row_marker; + int _color{}; + int _background{}; + int _selection_color{}; + int _selection_background{}; + int _number_color{}; + int _number_selection_color{}; + int _number_selection_background{}; + int _cursor_color{}; + int _cursor_background{}; + int _match_background{}; + int _row_cursor{}; + int _syntax_comments{}; + int _syntax_text{}; + int _syntax_command{}; + int _syntax_statement{}; + int _syntax_digit{}; + int _row_marker{}; }; #endif diff --git a/src/ui/window.cpp b/src/ui/window.cpp index c6fe0f0b..7f31b125 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -35,7 +35,7 @@ extern System *g_system; StringList *get_items() { var_t arg; bool done = false; - StringList *items = new StringList(); + auto *items = new StringList(); do { switch (code_peek()) { case kwTYPE_LINE: @@ -100,9 +100,9 @@ void cmd_window_inset(var_s *self, var_s *) { void cmd_window_set_font(var_s *self, var_s *) { var_num_t size; var_int_t bold, italic; - char *unit = NULL; + char *unit = nullptr; par_massget("FSII", &size, &unit, &bold, &italic); - if (unit != NULL && strcmp(unit, "em") == 0) { + if (unit != nullptr && strcmp(unit, "em") == 0) { size *= g_system->getOutput()->getFontSize(); } g_system->getOutput()->setFont(size, bold, italic); From e49e432e31e8662e726af3fca5ca9c76afaa418e Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 6 Nov 2022 19:16:14 +1000 Subject: [PATCH 3/9] ANDROID: fix crash with INPUT command while scrolled #160 --- ChangeLog | 3 +++ src/platform/android/jni/display.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index ce3c3d0e..f3f9e75d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2022-11-06 (12.25) + ANDROID: fix crash with INPUT command while scrolled #160 + 2022-08-26 (12.25) CONSOLE: Fixed TAB handling COMMON: Only TRIM Strings diff --git a/src/platform/android/jni/display.cpp b/src/platform/android/jni/display.cpp index 511d9e03..b4833d34 100644 --- a/src/platform/android/jni/display.cpp +++ b/src/platform/android/jni/display.cpp @@ -7,7 +7,7 @@ // #include "config.h" -#include +#include #include "platform/android/jni/display.h" #include "ui/utils.h" #include "common/device.h" @@ -82,13 +82,13 @@ void Canvas::fillRect(int left, int top, int width, int height, pixel_t drawColo } else { for (int y = 0; y < height; y++) { int posY = y + top; - if (posY == _h) { + if (posY >= _h) { break; } else if (posY >= dtY) { pixel_t *line = getLine(posY); for (int x = 0; x < width && line; x++) { int posX = x + left; - if (posX == _w) { + if (posX >= _w) { break; } else if (posX >= dtX) { line[posX] = drawColor; @@ -187,7 +187,7 @@ void Graphics::redraw() { bool Graphics::resize() { bool result; if (_screen == nullptr || _screen->_w != _w || _screen->_h != _h) { - Canvas *newScreen = new Canvas(); + auto *newScreen = new Canvas(); result = newScreen->create(_w, _h); if (result) { delete _screen; From 5fadbeb27294212a08410709744958da16f617f7 Mon Sep 17 00:00:00 2001 From: chrisws Date: Tue, 8 Nov 2022 05:44:59 +1000 Subject: [PATCH 4/9] COMMON: SPLIT with empty input now gives zero length output #147 --- samples/distro-examples/tests/split-join.bas | 9 +++++++++ src/common/blib.c | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/samples/distro-examples/tests/split-join.bas b/samples/distro-examples/tests/split-join.bas index 62f3ae70..9456ed59 100644 --- a/samples/distro-examples/tests/split-join.bas +++ b/samples/distro-examples/tests/split-join.bas @@ -60,3 +60,12 @@ if (9 != ubound(a)) then throw "Final empty entry was ignored" endif +row = "" +split row, ":", fields() +if (len(fields) != 0) then throw "Empty input gave non-empty output" + +s="$$$1$2$3$4$5$$$6$7$" +split s, "!@#$%^", a +if (ubound(a) - lbound(a) != len(a) - 1) then throw "Dimension error" + + diff --git a/src/common/blib.c b/src/common/blib.c index 9024db0b..4db0d7c6 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -2249,10 +2249,11 @@ void cmd_wsplit() { elem_p = v_elem(var_p, count); if (*ps) { v_setstr(elem_p, ps); - } else { + count++; + } else if (count) { v_setstr(elem_p, ""); + count++; } - count++; // final resize v_resize_array(var_p, count); From 5857efbcee4efa8a3274ee967076dee7892d4d92 Mon Sep 17 00:00:00 2001 From: chrisws Date: Tue, 8 Nov 2022 20:15:19 +1000 Subject: [PATCH 5/9] ANDROID: fix webui date column handling --- .../java/net/sourceforge/smallbasic/WebServer.java | 11 +++++++---- .../java/net/sourceforge/smallbasic/Server.java | 14 +++++++++----- src/platform/android/webui/src/App.js | 13 ++++++++++++- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java index f984bdbe..8ba211de 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java @@ -11,7 +11,10 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.URLDecoder; -import java.text.DateFormat; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -250,9 +253,9 @@ public FileData(String fileName, String date, long size) { } public FileData(File file) { - DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.DEFAULT); + ZonedDateTime zonedDateTime = Instant.ofEpochMilli(file.lastModified()).atZone(ZoneId.of("UTC")); this.fileName = file.getName(); - this.date = dateFormat.format(file.lastModified()); + this.date = DateTimeFormatter.RFC_1123_DATE_TIME.format(zonedDateTime); this.size = file.length(); } } @@ -321,7 +324,7 @@ public void run() { log("Invalid request"); } } - } catch (IOException e) { + } catch (Exception e) { log("Request failed", e); } finally { diff --git a/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java b/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java index 8047e615..091a3820 100644 --- a/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java +++ b/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java @@ -4,14 +4,16 @@ import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; -import java.text.DateFormat; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.Objects; @@ -19,7 +21,9 @@ public class Server { private static final String BASIC_HOME = "../basic/"; - public static void main(String[] args ) { + public static void main(String[] args ) throws IOException { + + // ln -s ../../../../../../../../app/src/main/java/net/sourceforge/smallbasic/WebServer.java . WebServer webServer = new WebServer() { @Override @@ -41,9 +45,9 @@ protected Collection getFileData() throws IOException { BasicFileAttributes attr = Files.readAttributes(fileEntry.toPath(), BasicFileAttributes.class); if (!attr.isDirectory()) { FileTime lastModifiedTime = attr.lastModifiedTime(); - DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.DEFAULT); String fileName = fileEntry.getName(); - String date = dateFormat.format(lastModifiedTime.toMillis()); + ZonedDateTime zonedDateTime = Instant.ofEpochMilli(lastModifiedTime.toMillis()).atZone(ZoneId.of("UTC")); + String date = DateTimeFormatter.RFC_1123_DATE_TIME.format(zonedDateTime); long size = attr.size(); result.add(new FileData(fileName, date, size)); } diff --git a/src/platform/android/webui/src/App.js b/src/platform/android/webui/src/App.js index 16e1d10a..8a4ce38e 100644 --- a/src/platform/android/webui/src/App.js +++ b/src/platform/android/webui/src/App.js @@ -49,7 +49,11 @@ const columns = [{ }, { field: 'date', headerName: 'Modified', - type: 'number', + type: 'date', + minWidth: 110, + renderCell: (params) => { + return new Date(params.row.date).toLocaleDateString(); + } }]; function getFetchHeader(body) { @@ -227,10 +231,17 @@ function FileList(props) { rows: props.rows }; + const sorting = { + sorting: { + sortModel: [{ field: 'fileName', sort: 'asc' }], + }, + }; + return ( onCellEditCommit(props, params, setError)} autoPageSize From af16976df6eece626bbc95304a53c827d1e23c07 Mon Sep 17 00:00:00 2001 From: chrisws Date: Thu, 10 Nov 2022 21:43:47 +1000 Subject: [PATCH 6/9] ANDROID: fix binary data upload in webui --- src/lib/lodepng | 2 +- src/lib/miniaudio | 2 +- src/lib/stb | 2 +- src/platform/android/app/build.gradle | 3 +- .../android/app/src/main/AndroidManifest.xml | 3 +- .../sourceforge/smallbasic/MainActivity.java | 2 +- .../net/sourceforge/smallbasic/WebServer.java | 52 ++++++++++++------- src/platform/android/build.gradle | 2 +- src/platform/android/jni/runtime.cpp | 4 +- src/platform/android/jni/runtime.h | 4 +- src/platform/android/webui/public/index.html | 2 +- .../net/sourceforge/smallbasic/Server.java | 2 - src/platform/android/webui/src/App.js | 6 +-- src/ui/system.h | 2 +- 14 files changed, 49 insertions(+), 39 deletions(-) diff --git a/src/lib/lodepng b/src/lib/lodepng index 18964554..997936fd 160000 --- a/src/lib/lodepng +++ b/src/lib/lodepng @@ -1 +1 @@ -Subproject commit 18964554bc769255401942e0e6dfd09f2fab2093 +Subproject commit 997936fd2b45842031e4180d73d7880e381cf33f diff --git a/src/lib/miniaudio b/src/lib/miniaudio index 4d813cfe..c153a947 160000 --- a/src/lib/miniaudio +++ b/src/lib/miniaudio @@ -1 +1 @@ -Subproject commit 4d813cfe23c28db165cce6785419fee9d2399766 +Subproject commit c153a947919808419b0bf3f56b6f2ee606d6c5f4 diff --git a/src/lib/stb b/src/lib/stb index af1a5bc3..8b5f1f37 160000 --- a/src/lib/stb +++ b/src/lib/stb @@ -1 +1 @@ -Subproject commit af1a5bc352164740c1cc1354942b1c6b72eacb8a +Subproject commit 8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55 diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 21b823de..9d5887aa 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -9,7 +9,7 @@ android { applicationId 'net.sourceforge.smallbasic' minSdkVersion 16 targetSdkVersion 33 - versionCode 53 + versionCode 54 versionName '12.25' resConfigs 'en' } @@ -47,6 +47,7 @@ android { path '../jni/Android.mk' } } + namespace 'net.sourceforge.smallbasic' } dependencies { diff --git a/src/platform/android/app/src/main/AndroidManifest.xml b/src/platform/android/app/src/main/AndroidManifest.xml index cba6af8b..f3640aba 100644 --- a/src/platform/android/app/src/main/AndroidManifest.xml +++ b/src/platform/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java index e0ab07ba..7b1fb4ed 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java @@ -1047,7 +1047,7 @@ private long getFileLength(String name) throws IOException { private Collection getFiles(File path) { Collection result = new ArrayList<>(); if (path.isDirectory() && path.canRead()) { - File[] files = path.listFiles(new BasFileFilter()); + File[] files = path.listFiles((FilenameFilter)null); if (files != null) { for (File file : files) { result.add(new FileData(file)); diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java index 8ba211de..912c1f44 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java @@ -11,15 +11,16 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.URLDecoder; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Base64; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.TimeZone; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.ZipEntry; @@ -32,7 +33,7 @@ * @author chrisws */ public abstract class WebServer { - private static final int BUFFER_SIZE = 32768; + private static final int BUFFER_SIZE = 32768 / 2; private static final int SEND_SIZE = BUFFER_SIZE / 4; private static final int LINE_SIZE = 128; private static final String UTF_8 = "utf-8"; @@ -106,6 +107,7 @@ public abstract static class AbstractRequest { final String tokenKey; final List headers; final InputStream inputStream; + private static final String BASE_64_PREFIX = ";base64,"; public AbstractRequest(Socket socket, String tokenKey) throws IOException { this.socket = socket; @@ -156,16 +158,24 @@ protected Map> getParameters(String url) throws IOExc /** * Parses HTTP POST data from the given input stream */ - protected Map getPostData(InputStream inputStream) throws IOException { + protected Map getPostData(InputStream inputStream) throws IOException { String postData = getLine(inputStream); String[] fields = postData.split("&"); - Map result = new HashMap<>(); + Map result = new HashMap<>(); for (String nextField : fields) { int eq = nextField.indexOf("="); if (eq != -1) { String key = nextField.substring(0, eq); - String value = URLDecoder.decode(nextField.substring(eq + 1), UTF_8); - result.put(key, value); + String value = nextField.substring(eq + 1); + int index = value.indexOf(BASE_64_PREFIX); + if (index != -1) { + ByteArrayOutputStream data = new ByteArrayOutputStream(); + value = value.substring(index + BASE_64_PREFIX.length()); + data.write(Base64.getDecoder().decode(value)); + result.put(key, data); + } else { + result.put(key, URLDecoder.decode(nextField.substring(eq + 1), UTF_8)); + } } } return result; @@ -253,9 +263,11 @@ public FileData(String fileName, String date, long size) { } public FileData(File file) { - ZonedDateTime zonedDateTime = Instant.ofEpochMilli(file.lastModified()).atZone(ZoneId.of("UTC")); + final DateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH); + format.setLenient(false); + format.setTimeZone(TimeZone.getTimeZone("UTC")); this.fileName = file.getName(); - this.date = DateTimeFormatter.RFC_1123_DATE_TIME.format(zonedDateTime); + this.date = format.format(file.lastModified()); this.size = file.length(); } } @@ -432,8 +444,8 @@ private void handleGet(Map> parameters) throws IOExce /** * Handler for POST requests */ - private void handlePost(Map data) throws IOException { - String userToken = data.get(TOKEN); + private void handlePost(Map data) throws IOException { + String userToken = (String) data.get(TOKEN); if (userToken == null) { userToken = requestToken; } @@ -464,9 +476,9 @@ private void handlePost(Map data) throws IOException { /** * Handler for File rename operations */ - private Response handleRename(Map data) throws IOException { - String from = data.get("from"); - String to = data.get("to"); + private Response handleRename(Map data) throws IOException { + String from = (String) data.get("from"); + String to = (String) data.get("to"); Response result; try { renameFile(from, to); @@ -503,15 +515,15 @@ private Response handleStatus(boolean success, String message) throws IOExceptio /** * Handler for file uploads */ - private Response handleUpload(Map data) throws IOException { - String fileName = data.get("fileName"); - String content = data.get("data"); + private Response handleUpload(Map data) throws IOException { + String fileName = (String) data.get("fileName"); + ByteArrayOutputStream content = (ByteArrayOutputStream) data.get("data"); Response result; try { if (fileName == null || content == null) { result = handleStatus(false, "Invalid input"); } else { - saveFile(fileName, content.getBytes(UTF_8)); + saveFile(fileName, content.toByteArray()); result = handleStatus(true, "File saved"); } } catch (Exception e) { diff --git a/src/platform/android/build.gradle b/src/platform/android/build.gradle index f3783489..51c6da8d 100644 --- a/src/platform/android/build.gradle +++ b/src/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' + classpath 'com.android.tools.build:gradle:7.3.1' } } diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index 0eb85070..1d1c27cb 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -572,7 +572,7 @@ bool Runtime::loadSettings(Properties &settings) { String buffer; struct stat st{}; if (stat(path.c_str(), &st) == 0) { - int len = st.st_size; + long len = st.st_size; buffer.append(fp, len); settings.load(buffer.c_str(), buffer.length()); result = true; @@ -959,7 +959,7 @@ bool System::getPen3() { return result; } -void System::completeKeyword(int index) { +void System::completeKeyword(int index) const { if (get_focus_edit() && isEditing()) { const char *help = get_focus_edit()->completeKeyword(index); if (help) { diff --git a/src/platform/android/jni/runtime.h b/src/platform/android/jni/runtime.h index b767378e..9ac072cc 100644 --- a/src/platform/android/jni/runtime.h +++ b/src/platform/android/jni/runtime.h @@ -54,7 +54,7 @@ struct Runtime : public System { void pushEvent(MAEvent *event); void readSensorEvents(); void setFloat(const char *methodName, float value); - void setLocationData(var_t *retval); + static void setLocationData(var_t *retval); void setSensorData(var_t *retval); void setString(const char *methodName, const char *value); void speak(const char *text) { setString("speak", text); } @@ -72,7 +72,7 @@ struct Runtime : public System { void onRunCompleted(); void onUnicodeChar(int ch); void loadConfig(); - void loadEnvConfig(Properties &settings, const char *key); + static void loadEnvConfig(Properties &settings, const char *key); bool loadSettings(Properties &settings); void saveConfig(); void runPath(const char *path); diff --git a/src/platform/android/webui/public/index.html b/src/platform/android/webui/public/index.html index 7f11fe3a..031a3e9e 100644 --- a/src/platform/android/webui/public/index.html +++ b/src/platform/android/webui/public/index.html @@ -1,7 +1,7 @@ - + diff --git a/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java b/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java index 091a3820..63fe17b2 100644 --- a/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java +++ b/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java @@ -22,8 +22,6 @@ public class Server { private static final String BASIC_HOME = "../basic/"; public static void main(String[] args ) throws IOException { - - // ln -s ../../../../../../../../app/src/main/java/net/sourceforge/smallbasic/WebServer.java . WebServer webServer = new WebServer() { @Override diff --git a/src/platform/android/webui/src/App.js b/src/platform/android/webui/src/App.js index 8a4ce38e..a53a10ee 100644 --- a/src/platform/android/webui/src/App.js +++ b/src/platform/android/webui/src/App.js @@ -87,7 +87,7 @@ function login(token, success, fail) { } function upload(name, data, success, fail) { - let body = "fileName=" + encodeURIComponent(name) + "&data=" + encodeURIComponent(data); + let body = "fileName=" + encodeURIComponent(name) + "&data=" + data; callApi('/api/upload', body, success, fail); } @@ -105,7 +105,7 @@ function copyFiles(event, success, progress, fail) { upload(files[index].name, fileReader.result, () => { if (++index < files.length) { progress(index); - fileReader.readAsText(files[index]); + fileReader.readAsDataURL(files[index]); } else { getFiles(success, fail); // reset input control @@ -113,7 +113,7 @@ function copyFiles(event, success, progress, fail) { } }, fail); }; - fileReader.readAsText(files[index]); + fileReader.readAsDataURL(files[index]); } function GridToolbarDownload(props) { diff --git a/src/ui/system.h b/src/ui/system.h index 40abf8f0..a4391526 100755 --- a/src/ui/system.h +++ b/src/ui/system.h @@ -106,7 +106,7 @@ struct System { // platform static virtual bool getPen3(); - void completeKeyword(int index); + void completeKeyword(int index) const; strlib::Stack _history; StackTrace _stackTrace; From cb470ce12265ba1a8fb6db0594387ed5aedbb0a6 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 12 Nov 2022 11:00:49 +1000 Subject: [PATCH 7/9] ANDROID: handle non-.bas file in UI --- .../android/app/src/main/assets/main.bas | 13 +++- .../net/sourceforge/smallbasic/WebServer.java | 77 +++++++++++++------ 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/platform/android/app/src/main/assets/main.bas b/src/platform/android/app/src/main/assets/main.bas index 34167950..a8df594b 100644 --- a/src/platform/android/app/src/main/assets/main.bas +++ b/src/platform/android/app/src/main/assets/main.bas @@ -34,6 +34,7 @@ const aboutId = "_about" const backId = "_back" const scratchId = "_scratch" const scratch_file = HOME + "/scratch.bas" +const sortEnd = chr(255) func mk_menu(value, lab, x) local bn @@ -230,6 +231,8 @@ end func fileCmpFunc0(l, r) local f1 = lower(l.name) local f2 = lower(r.name) + if (right(f1, 4) != ".bas") then f1 = sortEnd + f1 + if (right(f2, 4) != ".bas") then f2 = sortEnd + f2 local n = iff(f1 == f2, 0, iff(f1 > f2, 1, -1)) return iff(l.dir == r.dir, n, iff(l.dir, 1, -1)) end @@ -237,6 +240,8 @@ end func fileCmpFunc1(l, r) local f1 = lower(l.name) local f2 = lower(r.name) + if (right(f1, 4) != ".bas") then f1 = sortEnd + f1 + if (right(f2, 4) != ".bas") then f2 = sortEnd + f2 local n = iff(f1 == f2, 0, iff(f1 > f2, -1, 1)) return iff(l.dir == r.dir, n, iff(l.dir, 1, -1)) end @@ -285,7 +290,7 @@ sub loadFileList(path, byref basList) end func androidWalker(node) - if (node.dir == 0 && lower(right(node.name, 4)) == ".bas") then + if (node.dir == 0) then basList << node endif return node.depth == 0 @@ -392,7 +397,8 @@ sub listFiles(byref frm, path, sortDir, byref basList) endif if (abbr) then bn = mk_bn(path + name, name, txtcol) - bn.type = "link" + bn.type = iff(lower(right(name, 4)) == ".bas", "link", "label") + if (bn.type == "label") then bn.color = colText if (!node.dir) then bn.isExit = true else if (len(name) > 27) then @@ -401,7 +407,8 @@ sub listFiles(byref frm, path, sortDir, byref basList) lab = name endif bn = mk_bn(path + name, lab, txtcol) - bn.type = "link" + bn.type = iff(lower(right(name, 4)) == ".bas", "link", "label") + if (bn.type == "label") then bn.color = colText if (!node.dir) then bn.isExit = true frm.inputs << bn gap = 12 - len(str(node.size)) diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java index 912c1f44..265da6fa 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java @@ -1,5 +1,7 @@ package net.sourceforge.smallbasic; +import android.util.Base64; + import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -14,7 +16,6 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Base64; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -107,7 +108,6 @@ public abstract static class AbstractRequest { final String tokenKey; final List headers; final InputStream inputStream; - private static final String BASE_64_PREFIX = ";base64,"; public AbstractRequest(Socket socket, String tokenKey) throws IOException { this.socket = socket; @@ -158,24 +158,16 @@ protected Map> getParameters(String url) throws IOExc /** * Parses HTTP POST data from the given input stream */ - protected Map getPostData(InputStream inputStream) throws IOException { + protected Map getPostData(InputStream inputStream) throws IOException { String postData = getLine(inputStream); String[] fields = postData.split("&"); - Map result = new HashMap<>(); + Map result = new HashMap<>(); for (String nextField : fields) { int eq = nextField.indexOf("="); if (eq != -1) { String key = nextField.substring(0, eq); String value = nextField.substring(eq + 1); - int index = value.indexOf(BASE_64_PREFIX); - if (index != -1) { - ByteArrayOutputStream data = new ByteArrayOutputStream(); - value = value.substring(index + BASE_64_PREFIX.length()); - data.write(Base64.getDecoder().decode(value)); - result.put(key, data); - } else { - result.put(key, URLDecoder.decode(nextField.substring(eq + 1), UTF_8)); - } + result.put(key, new FormField(key, value)); } } return result; @@ -272,6 +264,37 @@ public FileData(File file) { } } + /** + * Holder for POST form data + */ + public static class FormField { + private static final String BASE_64_PREFIX = ";base64,"; + private final String string; + private final byte[] bytes; + + public FormField(String key, String value) throws IOException { + int index = value.indexOf(BASE_64_PREFIX); + if (index != -1 && "data".equals(key)) { + ByteArrayOutputStream data = new ByteArrayOutputStream(); + String base64Value = value.substring(index + BASE_64_PREFIX.length()); + data.write(Base64.decode(base64Value, Base64.DEFAULT)); + this.string = null; + this.bytes = data.toByteArray(); + } else { + this.string = URLDecoder.decode(value, UTF_8); + this.bytes = null; + } + } + + public String getString() { + return string; + } + + public byte[] toByteArray() { + return bytes; + } + } + /** * Build JSON string */ @@ -369,6 +392,16 @@ private Collection getAllFileNames() throws IOException { return result; } + private byte[] getData(Map data) { + FormField field = data.get("data"); + return field == null ? null : field.toByteArray(); + } + + private String getString(Map data, String key) { + FormField field = data.get(key); + return field == null ? null : field.toString(); + } + /** * Download files button handler */ @@ -444,8 +477,8 @@ private void handleGet(Map> parameters) throws IOExce /** * Handler for POST requests */ - private void handlePost(Map data) throws IOException { - String userToken = (String) data.get(TOKEN); + private void handlePost(Map data) throws IOException { + String userToken = getString(data, TOKEN); if (userToken == null) { userToken = requestToken; } @@ -476,9 +509,9 @@ private void handlePost(Map data) throws IOException { /** * Handler for File rename operations */ - private Response handleRename(Map data) throws IOException { - String from = (String) data.get("from"); - String to = (String) data.get("to"); + private Response handleRename(Map data) throws IOException { + String from = getString(data, "from"); + String to = getString(data, "to"); Response result; try { renameFile(from, to); @@ -515,15 +548,15 @@ private Response handleStatus(boolean success, String message) throws IOExceptio /** * Handler for file uploads */ - private Response handleUpload(Map data) throws IOException { - String fileName = (String) data.get("fileName"); - ByteArrayOutputStream content = (ByteArrayOutputStream) data.get("data"); + private Response handleUpload(Map data) throws IOException { + String fileName = getString(data, "fileName"); + byte[] content = getData(data); Response result; try { if (fileName == null || content == null) { result = handleStatus(false, "Invalid input"); } else { - saveFile(fileName, content.toByteArray()); + saveFile(fileName, content); result = handleStatus(true, "File saved"); } } catch (Exception e) { From 24f2027df939c1294931ceda524372267d32c8be Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 12 Nov 2022 16:37:57 +1000 Subject: [PATCH 8/9] ANDROID: implemented file manager delete file operation. --- .../sourceforge/smallbasic/MainActivity.java | 23 +++++- .../net/sourceforge/smallbasic/WebServer.java | 33 ++++++-- .../net/sourceforge/smallbasic/Server.java | 28 +++++-- src/platform/android/webui/src/App.js | 75 +++++++++++++++++++ 4 files changed, 142 insertions(+), 17 deletions(-) diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java index 7b1fb4ed..6d968edb 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java @@ -938,6 +938,25 @@ private boolean isPublicStorage(String dir) { private class WebServerImpl extends WebServer { private final Map fileLengths = new HashMap<>(); + @Override + protected byte[] decodeBase64(String data) { + return Base64.decode(data, Base64.DEFAULT); + } + + @Override + protected void deleteFile(String fileName) throws IOException { + if (fileName == null) { + throw new IOException("Empty file name"); + } + File file = getFile(fileName); + if (file == null) { + throw new IOException("File not found"); + } + if (!file.delete()) { + throw new IOException("Failed to delete file:" + fileName); + } + } + @Override protected void execStream(InputStream inputStream) throws IOException { MainActivity.this.execStream(inputStream); @@ -983,8 +1002,8 @@ protected void log(String message) { @Override protected void renameFile(String from, String to) throws IOException { - if (to == null || !to.endsWith(".bas")) { - throw new IOException("Invalid file name: " + to); + if (to == null) { + throw new IOException("Empty file name"); } File toFile = getFile(to); if (toFile != null) { diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java index 265da6fa..2818ba05 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java @@ -1,7 +1,5 @@ package net.sourceforge.smallbasic; -import android.util.Base64; - import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -64,9 +62,11 @@ public void run() { socketThread.start(); } + protected abstract void deleteFile(String fileName) throws IOException; protected abstract void execStream(InputStream inputStream) throws IOException; protected abstract Response getFile(String path, boolean asset) throws IOException; protected abstract Collection getFileData() throws IOException; + protected abstract byte[] decodeBase64(String data); protected abstract void log(String message); protected abstract void log(String message, Exception exception); protected abstract void renameFile(String from, String to) throws IOException; @@ -100,7 +100,7 @@ private void runServer(final int socketNum, final String token) throws IOExcepti /** * Server Request base class */ - public abstract static class AbstractRequest { + public abstract class AbstractRequest { final Socket socket; final String method; final String url; @@ -267,7 +267,7 @@ public FileData(File file) { /** * Holder for POST form data */ - public static class FormField { + public class FormField { private static final String BASE_64_PREFIX = ";base64,"; private final String string; private final byte[] bytes; @@ -277,7 +277,7 @@ public FormField(String key, String value) throws IOException { if (index != -1 && "data".equals(key)) { ByteArrayOutputStream data = new ByteArrayOutputStream(); String base64Value = value.substring(index + BASE_64_PREFIX.length()); - data.write(Base64.decode(base64Value, Base64.DEFAULT)); + data.write(decodeBase64(base64Value)); this.string = null; this.bytes = data.toByteArray(); } else { @@ -286,7 +286,8 @@ public FormField(String key, String value) throws IOException { } } - public String getString() { + @Override + public String toString() { return string; } @@ -438,7 +439,7 @@ private Response handleDownload(Map> data) throws IOE * Handler for files API */ private Response handleFileList() throws IOException { - log("Sending file list"); + log("Creating file list"); JsonBuilder builder = new JsonBuilder(); builder.append('['); long id = 0; @@ -492,6 +493,8 @@ private void handlePost(Map data) throws IOException { handleUpload(data).send(socket, null); } else if (url.startsWith("/api/rename")) { handleRename(data).send(socket, null); + } else if (url.startsWith("/api/delete")) { + handleDelete(data).send(socket, null); } else { new Response(SC_NOT_FOUND).send(socket, null); } @@ -506,6 +509,22 @@ private void handlePost(Map data) throws IOException { } } + /** + * Handler for File delete + */ + private Response handleDelete(Map data) throws IOException { + String fileName = getString(data, "fileName"); + Response result; + try { + deleteFile(fileName); + log("Deleted " + fileName); + result = handleFileList(); + } catch (IOException e) { + result = handleStatus(false, e.getMessage()); + } + return result; + } + /** * Handler for File rename operations */ diff --git a/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java b/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java index 63fe17b2..285bfa5d 100644 --- a/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java +++ b/src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java @@ -8,6 +8,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; +import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.time.Instant; @@ -15,6 +16,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Base64; import java.util.Collection; import java.util.Objects; @@ -24,6 +26,16 @@ public class Server { public static void main(String[] args ) throws IOException { // ln -s ../../../../../../../../app/src/main/java/net/sourceforge/smallbasic/WebServer.java . WebServer webServer = new WebServer() { + @Override + protected byte[] decodeBase64(String data) { + return Base64.getDecoder().decode(data); + } + + @Override + protected void deleteFile(String fileName) throws IOException { + Files.delete(Paths.get(BASIC_HOME, fileName)); + } + @Override protected void execStream(InputStream inputStream) { try { @@ -35,6 +47,13 @@ protected void execStream(InputStream inputStream) { } } + @Override + protected Response getFile(String path, boolean asset) throws IOException { + String prefix = asset ? "../build/" : BASIC_HOME; + File file = new File(prefix + path); + return new Response(Files.newInputStream(file.toPath()), file.length()); + } + @Override protected Collection getFileData() throws IOException { final File folder = new File(BASIC_HOME); @@ -53,13 +72,6 @@ protected Collection getFileData() throws IOException { return result; } - @Override - protected Response getFile(String path, boolean asset) throws IOException { - String prefix = asset ? "../build/" : BASIC_HOME; - File file = new File(prefix + path); - return new Response(Files.newInputStream(file.toPath()), file.length()); - } - @Override protected void log(String message) { System.err.println(message); @@ -73,7 +85,7 @@ protected void log(String message, Exception exception) { @Override protected void renameFile(String from, String to) throws IOException { - if (to == null || !to.endsWith(".bas")) { + if (to == null) { throw new IOException("Invalid File Name: " + to); } File toFile = new File(BASIC_HOME, to); diff --git a/src/platform/android/webui/src/App.js b/src/platform/android/webui/src/App.js index a53a10ee..782b14cc 100644 --- a/src/platform/android/webui/src/App.js +++ b/src/platform/android/webui/src/App.js @@ -9,6 +9,11 @@ import { Box, Button, CssBaseline, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, Link, Snackbar, TextField, @@ -26,6 +31,7 @@ import { } from '@mui/x-data-grid'; import { + Delete as DeleteIcon, Download as DownloadIcon, Upload as UploadIcon } from '@mui/icons-material'; @@ -96,6 +102,11 @@ function renameFile(from, to, success, fail) { callApi('/api/rename', body, success, fail); } +function deleteFile(fileName, success, fail) { + let body = "fileName=" + encodeURIComponent(fileName); + callApi('/api/delete', body, success, fail); +} + function copyFiles(event, success, progress, fail) { const fileReader = new FileReader(); const input = event.target; @@ -116,6 +127,68 @@ function copyFiles(event, success, progress, fail) { fileReader.readAsDataURL(files[index]); } +function ConfirmDeleteDialog(props) { + return ( +
+ + + {"Delete file"} + + + + Are you sure you want to permanently delete {props.name}? You cannot undo this. + + + + + + + +
+ ); +} + +function GridToolbarDelete(props) { + const [error, setError] = useState(null); + const [open, setOpen] = useState(false); + + const showPrompt = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleDelete = () => { + setOpen(false); + deleteFile(props.rows[props.selections[0]].fileName, (data) => { + props.setRows(data); + props.setSelections([]); + }, (error) => { + setError(error); + setError(true); + }); + }; + + return props.selections.length !== 1 ? null : ( + + + + + + ); +} + function GridToolbarDownload(props) { let color = useTheme().palette.primary.main; let args = ""; @@ -203,6 +276,7 @@ function AppToolbar(props) { + ); } @@ -227,6 +301,7 @@ function FileList(props) { const toolbarProps = { selections: selectionModel, + setSelections: setSelectionModel, setRows: props.setRows, rows: props.rows }; From fc659cf83be922ce4d661b89452d4b87024330d6 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 13 Nov 2022 12:51:48 +1000 Subject: [PATCH 9/9] COMMON: updated changelog --- ChangeLog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog b/ChangeLog index f3f9e75d..2177101a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2022-11-13 (12.25) + ANDROID: Updated file manager web UI: + - Implemented delete command + - Implemented upload support for non .bas files + - Fixed date column sorting + COMMON: SPLIT with empty input now gives zero length output #147 + COMMON: Fix for bug #166: Display floating point numbers with high precision (by J7M) + COMMON: Fixed functions Polyarea, Polycent (by J7M) + CONSOLE: vt100 (esc sequences) support for console (by J7M) + 2022-11-06 (12.25) ANDROID: fix crash with INPUT command while scrolled #160