diff --git a/ChangeLog b/ChangeLog index 9bdd3597..c2eb2fdb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2015-10-20 + Fix LET when assigning a value to a MAP/ARRAY field + +2015-09-15 + SDL Update PEN(3) to work more like FLTK + Fix display output before PEN + Fix BOLD and ITALIC font display + Context menu edit keyword completion + Updated program icon + Editor line number widget can be used for scrolling + Updated editor help display + Fix TSAVE with try/catch + 2015-08-26 Editor fixes: - now displays an i-beam/edit cursor diff --git a/Makefile.am b/Makefile.am index b29b8ef8..98db526f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ # SmallBASIC -# Copyright(C) 2001-2012 Chris Warren-Smith. +# Copyright(C) 2001-2015 Chris Warren-Smith. # # This program is distributed under the terms of the GPL v2.0 or later # Download the GNU Public License (GPL) from www.gnu.org @@ -7,43 +7,8 @@ SUBDIRS = @BUILD_SUBDIRS@ -EXTRA_DIST = \ - AUTHORS \ - ChangeLog \ - configure.in \ - NEWS \ - README \ - autogen.sh \ - documentation/export_csv.bas \ - documentation/sbasic_ref.csv \ - documentation/HOWTO/HOWTO-DOCUMENT.TXT \ - documentation/HOWTO/HOWTO-PORT.TXT \ - documentation/HOWTO/DEVELOP.TXT \ - documentation/LICENSE \ - documentation/README \ - documentation/README.UNIX \ - images/logo.gif \ - images/sb16x16.png \ - images/sb32x32.png \ - images/sb-desktop-16x16.png \ - images/sb-desktop-32x32.png \ - ide/smallbasic.lang \ - ide/small-basic-mode.el \ - ide/smallbasic.syn \ - rpm/README \ - rpm/SPECS/opensuse.spec - -install-exec-hook: - (mkdir -p $(DESTDIR)$(pkgdatadir) && \ - mkdir -p $(DESTDIR)$(pkgdatadir)/plugins && \ - mkdir -p $(DESTDIR)$(pkgdatadir)/ide && \ - mkdir -p $(DESTDIR)$(pkgdatadir)/samples && \ - cp documentation/sbasic_ref.csv $(DESTDIR)$(pkgdatadir) && \ - cp plugins/*.* $(DESTDIR)$(pkgdatadir)/plugins && \ - cp ide/*.* $(DESTDIR)$(pkgdatadir)/ide) - deb: - fakeroot dpkg-buildpackage + fakeroot dpkg-buildpackage -b test: (cd @TEST_DIR@ && make test) diff --git a/README.md b/README.md index 698f8c2d..ce0cb8ca 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ SmallBASIC is a fast and easy to learn BASIC language interpreter ideal for everyday calculations, scripts and prototypes. SmallBASIC includes trigonometric, matrices and algebra functions, a built in IDE, a powerful string library, system, sound, and graphic commands along with structured programming syntax ## Building the SDL version + +Initial setup on linux +``` + $ sudo apt-get install git autotools-dev automake gcc g++ libsdl2-dev libfreetype6-dev libfontconfig1-dev + $ git clone https://github.com/smallbasic/SmallBASIC.git + $ cd SmallBASIC + $ sh autogen.sh +``` +Build in linux ``` $ ./configure --enable-sdl $ make diff --git a/configure.ac b/configure.ac index c5309fcb..90f10299 100644 --- a/configure.ac +++ b/configure.ac @@ -1,13 +1,13 @@ dnl dnl Configure script for SmallBASIC dnl -dnl Copyright(C) 2001-2014 Chris Warren-Smith. +dnl Copyright(C) 2001-2015 Chris Warren-Smith. dnl dnl This program is distributed under the terms of the GPL v2.0 dnl Download the GNU Public License (GPL) from www.gnu.org dnl -AC_INIT([smallbasic], [0.11.20]) +AC_INIT([smallbasic], [0.12.0]) AC_CONFIG_SRCDIR([configure.ac]) AC_CANONICAL_TARGET @@ -204,9 +204,24 @@ function buildSDL() { dnl do not depend on cygwin.dll under cygwin build PACKAGE_CFLAGS="${PACKAGE_CFLAGS} -mms-bitfields" PACKAGE_LIBS="${PACKAGE_LIBS} -lwsock32 -lws2_32 -static-libgcc -static-libstdc++" + PACKAGE_LIBS="-Wl,-Bstatic ${PACKAGE_LIBS} `sdl2-config --static-libs` `freetype-config --libs`" AC_DEFINE(_Win32, 1, [Windows build]) ;; + *darwin*) + # OSX Check fontconfig configuration + PKG_CHECK_MODULES(FONTCONFIG, fontconfig >= 2.2) + AC_SUBST(FONTCONFIG_CFLAGS) + AC_SUBST(FONTCONFIG_LIBS) + + PACKAGE_CFLAGS="${PACKAGE_CFLAGS} ${FONTCONFIG_CFLAGS}" + + dnl backlinking support for modules + PACKAGE_LIBS="${PACKAGE_LIBS} -ldl" + PACKAGE_LIBS="${PACKAGE_LIBS} ${FONTCONFIG_LIBS}" + PACKAGE_LIBS="${PACKAGE_LIBS} `sdl2-config --libs` `freetype-config --libs`" + ;; + *) # Unix - Check fontconfig configuration PKG_CHECK_MODULES(FONTCONFIG, fontconfig >= 2.2) @@ -218,17 +233,10 @@ function buildSDL() { dnl backlinking support for modules PACKAGE_LIBS="${PACKAGE_LIBS} -ldl" PACKAGE_LIBS="${PACKAGE_LIBS} ${FONTCONFIG_LIBS}" + PACKAGE_LIBS="-static-libgcc ${PACKAGE_LIBS} `sdl2-config --static-libs` `freetype-config --libs`" esac PACKAGE_CFLAGS="${PACKAGE_CFLAGS} `sdl2-config --cflags` `freetype-config --cflags` -fno-exceptions" - case "${host_os}" in - *mingw*) - PACKAGE_LIBS="-Wl,-Bstatic ${PACKAGE_LIBS} `sdl2-config --static-libs` `freetype-config --libs`" - ;; - - *) - PACKAGE_LIBS="${PACKAGE_LIBS} `sdl2-config --libs` `freetype-config --libs`" - esac dnl preconfigured values for SDL build AC_DEFINE(_SDL, 1, [Defined when building SDL version]) @@ -339,19 +347,7 @@ function buildConsole() { AC_SUBST(BUILD_SUBDIRS) } -function buildDist() { - TARGET="Building source code release." - defaultConditionals - BUILD_SUBDIRS="src/common src/platform/gtk/src src/platform/gtk/data src/platform/gtk/icons" - BUILD_SUBDIRS="${BUILD_SUBDIRS} src/platform/fltk" - BUILD_SUBDIRS="${BUILD_SUBDIRS} src/platform/sdl" - BUILD_SUBDIRS="${BUILD_SUBDIRS} src/platform/unix" - AC_SUBST(BUILD_SUBDIRS) -} - -if test x$ac_build_dist = xyes; then - buildDist -elif test x$ac_build_fltk = xyes; then +if test x$ac_build_fltk = xyes; then buildFLTK elif test x$ac_build_sdl = xyes; then buildSDL diff --git a/debian/changelog b/debian/changelog index e98ebfa8..b786966a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,8 @@ +smallbasic (0.12.0) unstable; urgency=low + * Various - see web site + + -- Chris Warren-Smith Sat, 17 Sept 2015 09:45:25 +1000 + smallbasic (0.11.17) unstable; urgency=low * Fix compiler ignoring unused assignment values * Implement IMAGE diff --git a/debian/rules b/debian/rules index afded1fc..664b5148 100755 --- a/debian/rules +++ b/debian/rules @@ -28,7 +28,7 @@ endif ifneq "$(wildcard /usr/share/misc/config.guess)" "" cp -f /usr/share/misc/config.guess config.guess endif - ./configure $(CROSS) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs" --enable-fltk + ./configure $(CROSS) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info --enable-sdl build: build-stamp @@ -69,17 +69,11 @@ binary-indep: build install binary-arch: build install dh_testdir dh_testroot - dh_installchangelogs ChangeLog - dh_installdocs - dh_installexamples - dh_install --exclude=".git" documentation/sbasic_ref.csv plugins samples/distro-examples usr/share/smallbasic - dh_installman dh_link dh_strip dh_compress dh_fixperms dh_installdeb - dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb diff --git a/documentation/build_kwp.cpp b/documentation/build_kwp.cpp index e921e3a5..cd3342f1 100644 --- a/documentation/build_kwp.cpp +++ b/documentation/build_kwp.cpp @@ -236,7 +236,7 @@ int main(int argc, char *argv[]) { } fprintf(stdout, "/* automagicaly generated file */\n"); - fprintf(stdout, "struct KEYWORD_HELP {\n"); + fprintf(stdout, "static struct KEYWORD_HELP {\n"); fprintf(stdout, " const char *package;\n"); fprintf(stdout, " const char *keyword;\n"); fprintf(stdout, " const char *signature;\n"); diff --git a/documentation/sbasic_ref.csv b/documentation/sbasic_ref.csv index ddf3a4ef..4f2f1e5a 100644 --- a/documentation/sbasic_ref.csv +++ b/documentation/sbasic_ref.csv @@ -149,7 +149,7 @@ Language,keyword,SUB,657,"SUB foo (a, b)","Declare a sub procedure. Sub's do not Language,keyword,THEN,658,"THEN","foo = 1: if foo==1 THEN: ? ""one"": fi" Language,keyword,UNIT,659,"UNIT name","Units are a set of procedures, functions and/or variables that can be used by another program or unit." Language,keyword,UNTIL,660,"UNTIL","a = 0: repeat: a++: ? a: UNTIL a = 10" -Language,keyword,USE,661,"USE","Used with various commands for passing a user-defined expression. eg SPLIT s," ",v USE TRIM(x). Trim each element of v." +Language,keyword,USE,661,"USE","Used with various commands for passing a user-defined expression. eg SPLIT s,"" "",v USE TRIM(x). Trim each element of v." Language,operator,AND,662,"a AND b","Logical AND. Right side is not evaluated if left side evaluates to False." Language,operator,BAND,663,"a BAND b","Bitwise AND." Language,operator,BOR,664,"a BOR b","Bitwise OR." diff --git a/ide/android/AndroidManifest.xml b/ide/android/AndroidManifest.xml index dc16476c..861df1f7 100644 --- a/ide/android/AndroidManifest.xml +++ b/ide/android/AndroidManifest.xml @@ -3,7 +3,7 @@ package="net.sourceforge.smallbasic" android:installLocation="preferExternal" android:versionCode="15" - android:versionName="0.11.19"> + android:versionName="0.12.0"> diff --git a/ide/android/assets/main.bas b/ide/android/assets/main.bas index c9608c08..9b8d33b5 100644 --- a/ide/android/assets/main.bas +++ b/ide/android/assets/main.bas @@ -21,7 +21,6 @@ func mk_bn(value, lab, fg) bn.value = value bn.label = lab bn.color = fg - bn.backgroundColor = 0 mk_bn = bn end @@ -56,7 +55,7 @@ sub do_about() print "(_ ._ _ _.|||_) /\ (_ |/ " print "__)| | |(_||||_)/--\__)|\_" print - print "Version 0.11.20" + print "Version 0.12.0" print print "Copyright (c) 2002-2015 Chris Warren-Smith" print "Copyright (c) 1999-2006 Nic Christopoulos" + chr(10) @@ -82,6 +81,40 @@ sub do_about() cls end +sub do_newfile() + color 3, 0 + cls + print boldOn + "Create new program." + print boldOff + "To enable editing, display the menu then select Editor [ON]." + print "Press to leave this screen without making any changes." + print + local valid_file = false + while (!valid_file) + input "Enter file name: ", file + if (len(file) == 0) then + exit loop + endif + if (leftoflast(file, ".bas") == 0) then + file += ".bas" + endif + try + if (exist(file)) then + print "File " + file + " already exists" + else + dim text + text << "REM SmallBASIC" + text << "REM created: " + date + tsave file, text + valid_file = true + endif + catch e + print "Error creating file: " e + end try + wend + color 7, 0 + cls +end + sub do_setup() color 3, 0 cls @@ -177,13 +210,14 @@ end sub main local basList, dirList, path - local frm, bn_about, bn_online + local frm, bn_about, bn_online, bn_new local do_intro dim basList dim dirList bn_setup = mk_menu("_setup", "Setup", -1) + bn_new = mk_menu("_new", "New", -1) bn_about = mk_menu("_about", "About", -1) bn_online = mk_menu(onlineUrl, "Online", 0) bn_online.isExit = true @@ -194,6 +228,7 @@ sub main if (osname != "SDL") then frm.inputs << bn_setup endif + frm.inputs << bn_new frm.inputs << bn_about if (welcome) then @@ -202,7 +237,6 @@ sub main listFiles frm, path, basList, dirList frm.color = 10 - frm.backgroundColor = 0 rect 0, 0, xmax, lineSpacing COLOR 1 filled at 0, 0 make_ui = form(frm) @@ -249,6 +283,10 @@ sub main frm.close() do_setup() frm = make_ui(path, false) + elif frm.value == "_new" then + frm.close() + do_newfile() + frm = make_ui(path, false) elif frm.value == "_back" then frm.close() go_back diff --git a/images/sb-desktop-128x128.png b/images/sb-desktop-128x128.png new file mode 100644 index 00000000..5ff6e8b1 Binary files /dev/null and b/images/sb-desktop-128x128.png differ diff --git a/images/sb-desktop-16x16.png b/images/sb-desktop-16x16.png index b389fe73..59471a4c 100644 Binary files a/images/sb-desktop-16x16.png and b/images/sb-desktop-16x16.png differ diff --git a/images/sb-desktop-32x32.png b/images/sb-desktop-32x32.png index 074a78e6..39e71d1f 100644 Binary files a/images/sb-desktop-32x32.png and b/images/sb-desktop-32x32.png differ diff --git a/images/sb-desktop-48x48.png b/images/sb-desktop-48x48.png new file mode 100644 index 00000000..e492471c Binary files /dev/null and b/images/sb-desktop-48x48.png differ diff --git a/images/sb-desktop-64x64.png b/images/sb-desktop-64x64.png index 538389b9..652da120 100644 Binary files a/images/sb-desktop-64x64.png and b/images/sb-desktop-64x64.png differ diff --git a/images/sb4w.ico b/images/sb4w.ico index ee107abb..4b2bf0b0 100644 Binary files a/images/sb4w.ico and b/images/sb4w.ico differ diff --git a/samples/distro-examples/tests/uds.bas b/samples/distro-examples/tests/uds.bas index 9e40f2a1..1d2b8fb4 100644 --- a/samples/distro-examples/tests/uds.bas +++ b/samples/distro-examples/tests/uds.bas @@ -111,3 +111,28 @@ next i inner.foo="bar" m.b << inner s = " " + m.b(0).foo + +rem ------------------------------------ + +dim bar.x(2), foo(2) +bar.x(0).barx = 1024 +foo(0).fooY = 999 +if (bar.x(0).barx <> 1024) then + throw "Error 1" +fi + +if (foo(0).fooY <> 999) then + throw "Error 2" +fi + +if (1024 <> bar.x(0).barx) then + throw "Error 3" +fi + +if (999 <> foo(0).fooY) then + throw "Error 4" +fi + +bar.x(1).barx=bar.x(0).barx+976 +bar.x(1).barx/=1000 +if bar.x(1).barx <> 2 then throw "Yuck, I can't read red print on black background." diff --git a/src/common/blib_db.c b/src/common/blib_db.c index 290230c6..632bb053 100644 --- a/src/common/blib_db.c +++ b/src/common/blib_db.c @@ -751,9 +751,12 @@ void cmd_fsaveln() { return; } - dev_fopen(handle, (char *)file_name.v.p.ptr, flags); + int success = dev_fopen(handle, (char *)file_name.v.p.ptr, flags); v_free(&file_name); CHK_ERR(FSERR_GENERIC); + if (!success) { + return; + } } if (var_p->type == V_ARRAY) { diff --git a/src/common/brun.c b/src/common/brun.c index 5f73822b..1d62f4d0 100755 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -1700,7 +1700,12 @@ void sbasic_set_bas_dir(const char *bas_file) { cwd[0] = '\0';; gsb_bas_dir[0] = '\0'; getcwd(cwd, sizeof(cwd) - 1); - + char *ch; + for (ch = cwd; *ch != '\0'; ch++) { + if (*ch == '\\') { + *ch = '/'; + } + } if (bas_file[0] == OS_DIRSEP) { // full path strncat(gsb_bas_dir, bas_file, path_len + 1); @@ -1708,12 +1713,14 @@ void sbasic_set_bas_dir(const char *bas_file) { // relative path // append the non file part of bas_file to cwd strcat(gsb_bas_dir, cwd); - strcat(gsb_bas_dir, "/"); + if (gsb_bas_dir[strlen(gsb_bas_dir) - 1] != '/') { + strcat(gsb_bas_dir, "/"); + } strncat(gsb_bas_dir, bas_file, path_len + 1); } else { // in current dir strcat(gsb_bas_dir, cwd); - if (!(cwd[0] == '/' && cwd[1] == '\0')) { + if (gsb_bas_dir[strlen(gsb_bas_dir) - 1] != '/') { strcat(gsb_bas_dir, "/"); } } diff --git a/src/common/sberr.c b/src/common/sberr.c index cc8a5c28..33f397c2 100644 --- a/src/common/sberr.c +++ b/src/common/sberr.c @@ -413,7 +413,7 @@ void err_throw_str(const char *err) { // throw internal error void err_throw(const char *fmt, ...) { - if (!gsb_last_error) { + if (!gsb_last_error && prog_source) { va_list ap; va_start(ap, fmt); char *err = malloc(SB_TEXTLINE_SIZE + 1); diff --git a/src/common/scan.c b/src/common/scan.c index 91222675..2a2cda04 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -1701,7 +1701,9 @@ char *comp_array_params(char *src, char exitChar) { } break; }; - p++; + if (*p != exitChar) { + p++; + } if (*p == exitChar) { p++; break; diff --git a/src/platform/android/jni/Android.mk b/src/platform/android/jni/Android.mk index 1d087953..5d9314dd 100644 --- a/src/platform/android/jni/Android.mk +++ b/src/platform/android/jni/Android.mk @@ -7,7 +7,7 @@ JNI_PATH := $(call my-dir) SB_HOME := $(JNI_PATH)/../../../.. -FREETYPE_HOME := $(HOME)/android-sdk/freetype-2.5.5 +FREETYPE_HOME := $(HOME)/android-sdk/freetype-2.6.1 include $(call all-subdir-makefiles) LOCAL_PATH := $(JNI_PATH) @@ -16,9 +16,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := smallbasic LOCAL_CFLAGS := -DHAVE_CONFIG_H=1 -DLODEPNG_NO_COMPILE_CPP \ -DPIXELFORMAT_RGBA8888 -LOCAL_C_INCLUDES := $(SB_HOME) $(SB_HOME)/src \ - $(FREETYPE_HOME)/freetype/include \ - $(FREETYPE_HOME)/freetype/include/freetype2 +LOCAL_C_INCLUDES := $(SB_HOME) $(SB_HOME)/src \ + $(FREETYPE_HOME)/freetype/include/freetype2 \ + $(FREETYPE_HOME)/freetype/include/freetype2/freetype LOCAL_SRC_FILES := main.cpp \ display.cpp \ runtime.cpp \ @@ -31,7 +31,6 @@ LOCAL_SRC_FILES := main.cpp \ ../../../ui/textedit.cpp \ ../../../ui/strlib.cpp \ ../../../ui/graphics.cpp \ - ../../../ui/editor.cpp \ ../../../ui/system.cpp LOCAL_LDLIBS := -llog -landroid -ljnigraphics LOCAL_STATIC_LIBRARIES := sb_common freetype android_native_app_glue diff --git a/src/platform/android/jni/display.cpp b/src/platform/android/jni/display.cpp index 01adf6b5..b966a0a7 100644 --- a/src/platform/android/jni/display.cpp +++ b/src/platform/android/jni/display.cpp @@ -72,7 +72,7 @@ void Canvas::fillRect(int left, int top, int width, int height, pixel_t drawColo if (top + height > _h) { blockH = height - top; } - memset(getLine(top), drawColor, width * blockH); + memset(getLine(top), drawColor, 4 * width * blockH); } else { for (int y = 0; y < height; y++) { int posY = y + top; @@ -138,12 +138,7 @@ bool Graphics::construct() { if (_screen && _screen->create(_w, _h)) { _drawTarget = _screen; maSetColor(DEFAULT_BACKGROUND); -#if defined(PIXELFORMAT_RGBA8888) - int format = WINDOW_FORMAT_RGBA_8888; -#else - int format = WINDOW_FORMAT_RGB_565; -#endif - ANativeWindow_setBuffersGeometry(_app->window, 0, 0, format); + ANativeWindow_setBuffersGeometry(_app->window, 0, 0, WINDOW_FORMAT_RGBA_8888); result = true; } else { trace("Failed to create canvas"); diff --git a/src/platform/android/jni/freetype/Android.mk b/src/platform/android/jni/freetype/Android.mk index f702cf78..104f63a8 100644 --- a/src/platform/android/jni/freetype/Android.mk +++ b/src/platform/android/jni/freetype/Android.mk @@ -10,8 +10,8 @@ include $(CLEAR_VARS) LOCAL_C_INCLUDES := $(FREETYPE_HOME)/include \ $(FREETYPE_HOME)/builds \ - $(FREETYPE_HOME)/include/config \ - $(FREETYPE_HOME)/include/freetype2 + $(FREETYPE_HOME)/include/freetype/config \ + $(FREETYPE_HOME)/include/freetype LOCAL_MODULE := freetype LOCAL_CFLAGS := -Wall -std=gnu99 \ "-DFT_CONFIG_CONFIG_H=" \ @@ -31,7 +31,6 @@ LOCAL_SRC_FILES:= \ $(FREETYPE_HOME)/src/base/ftbitmap.c \ $(FREETYPE_HOME)/src/base/ftglyph.c \ $(FREETYPE_HOME)/src/base/ftstroke.c \ - $(FREETYPE_HOME)/src/base/ftxf86.c \ $(FREETYPE_HOME)/src/base/ftbase.c \ $(FREETYPE_HOME)/src/base/ftsystem.c \ $(FREETYPE_HOME)/src/base/ftinit.c \ diff --git a/src/platform/android/jni/main.cpp b/src/platform/android/jni/main.cpp index 13f97661..750c05a3 100644 --- a/src/platform/android/jni/main.cpp +++ b/src/platform/android/jni/main.cpp @@ -1,6 +1,6 @@ /// This file is part of SmallBASIC // -// Copyright(C) 2001-2014 Chris Warren-Smith. +// Copyright(C) 2001-2015 Chris Warren-Smith. // // This program is distributed under the terms of the GPL v2.0 or later // Download the GNU Public License (GPL) from www.gnu.org @@ -26,11 +26,6 @@ void android_main(android_app *app) { } ANativeActivity_finish(app->activity); - do { - runtime->pollEvents(true); - } - while (app->destroyRequested != 0); - delete runtime; logLeaving(); diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index f00d7bd0..00046904 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -1,6 +1,6 @@ // This file is part of SmallBASIC // -// Copyright(C) 2001-2014 Chris Warren-Smith. +// Copyright(C) 2001-2015 Chris Warren-Smith. // // This program is distributed under the terms of the GPL v2.0 or later // Download the GNU Public License (GPL) from www.gnu.org @@ -30,11 +30,10 @@ #define SERVER_SOCKET_KEY "serverSocket" #define SERVER_TOKEN_KEY "serverToken" #define MUTE_AUDIO_KEY "muteAudio" +#define OPT_IDE_KEY "optIde" Runtime *runtime; -void launchDebug(const char *file) {} - MAEvent *getMotionEvent(int type, AInputEvent *event) { MAEvent *result = new MAEvent(); result->type = type; @@ -92,6 +91,7 @@ void handleCommand(android_app *app, int32_t cmd) { } break; case APP_CMD_LOST_FOCUS: + // display menu or exit break; } } @@ -191,15 +191,34 @@ void Runtime::alert(const char *title, const char *message) { _app->activity->vm->DetachCurrentThread(); } +void Runtime::alert(const char *title, int duration) { + logEntered(); + + JNIEnv *env; + _app->activity->vm->AttachCurrentThread(&env, NULL); + jstring titleString = env->NewStringUTF(title); + jclass clazz = env->GetObjectClass(_app->activity->clazz); + jmethodID method = env->GetMethodID(clazz, "showToast", + "(Ljava/lang/String;I)V"); + env->CallVoidMethod(_app->activity->clazz, method, titleString, duration); + env->DeleteLocalRef(clazz); + env->DeleteLocalRef(titleString); + _app->activity->vm->DetachCurrentThread(); +} + int Runtime::ask(const char *title, const char *prompt, bool cancel) { JNIEnv *env; _app->activity->vm->AttachCurrentThread(&env, NULL); jclass clazz = env->GetObjectClass(_app->activity->clazz); + jstring titleString = env->NewStringUTF(title); + jstring promptString = env->NewStringUTF(prompt); jmethodID methodId = env->GetMethodID(clazz, "ask", - "(Ljava/lang/String;Ljava/lang/String;)Z"); - jboolean result = (jboolean) env->CallBooleanMethod(_app->activity->clazz, methodId, - title, prompt); + "(Ljava/lang/String;Ljava/lang/String;Z)I"); + jint result = (jint) env->CallIntMethod(_app->activity->clazz, methodId, + titleString, promptString, cancel); env->DeleteLocalRef(clazz); + env->DeleteLocalRef(titleString); + env->DeleteLocalRef(promptString); _app->activity->vm->DetachCurrentThread(); return result; } @@ -390,6 +409,10 @@ void Runtime::loadConfig() { if (s && s->toInteger() == 1) { opt_mute_audio = 1; } + s = profile.get(OPT_IDE_KEY); + if (s) { + opt_ide = s->toInteger(); + } loadEnvConfig(profile, SERVER_SOCKET_KEY); loadEnvConfig(profile, SERVER_TOKEN_KEY); } @@ -417,6 +440,7 @@ void Runtime::saveConfig() { fprintf(fp, "%s='%s'\n", PATH_KEY, path); fprintf(fp, "%s=%d\n", FONT_SCALE_KEY, _fontScale); fprintf(fp, "%s=%d\n", MUTE_AUDIO_KEY, opt_mute_audio); + fprintf(fp, "%s=%d\n", OPT_IDE_KEY, opt_ide); for (int i = 0; environ[i] != NULL; i++) { char *env = environ[i]; if (strstr(env, SERVER_SOCKET_KEY) != NULL) { @@ -692,6 +716,179 @@ char *Runtime::getClipboardText() { return result; } +// +// System platform methods +// +bool System::getPen3() { + bool result = false; + if (_touchX != -1 && _touchY != -1) { + result = true; + } else { + // get mouse + processEvents(0); + if (_touchX != -1 && _touchY != -1) { + result = true; + } + } + return result; +} + +void System::completeKeyword(int index) { + if (get_focus_edit() && isEditing()) { + const char *help = get_focus_edit()->completeKeyword(index); + if (help) { + runtime->alert(help); + runtime->getOutput()->redraw(); + } + } +} + +void System::editSource(strlib::String &loadPath) { + logEntered(); + + strlib::String fileName; + int i = loadPath.lastIndexOf('/', 0); + if (i != -1) { + fileName = loadPath.substring(i + 1); + } else { + fileName = loadPath; + } + + strlib::String dirtyFile; + dirtyFile.append(" * "); + dirtyFile.append(fileName); + strlib::String cleanFile; + cleanFile.append(" - "); + cleanFile.append(fileName); + + int w = _output->getWidth(); + int h = _output->getHeight(); + int charWidth = _output->getCharWidth(); + int charHeight = _output->getCharHeight(); + int prevScreenId = _output->selectScreen(SOURCE_SCREEN); + TextEditInput *editWidget = new TextEditInput(_programSrc, charWidth, charHeight, 0, 0, w, h); + TextEditHelpWidget *helpWidget = new TextEditHelpWidget(editWidget, charWidth, charHeight, false); + TextEditInput *widget = editWidget; + _modifiedTime = getModifiedTime(); + editWidget->updateUI(NULL, NULL); + editWidget->setLineNumbers(); + editWidget->setFocus(true); + if (strcmp(gsb_last_file, loadPath.c_str()) == 0) { + editWidget->setCursorRow(gsb_last_line - 1); + } + if (gsb_last_error && !isBack()) { + editWidget->setCursorRow(gsb_last_line - 1); + runtime->alert(gsb_last_errmsg); + } + _srcRendered = false; + _output->clearScreen(); + _output->addInput(editWidget); + _output->addInput(helpWidget); + _output->setStatus(cleanFile); + _output->redraw(); + _state = kEditState; + runtime->showKeypad(true); + + while (_state == kEditState) { + MAEvent event = getNextEvent(); + if (event.type == EVENT_TYPE_KEY_PRESSED && _userScreenId == -1) { + dev_clrkb(); + int sw = _output->getScreenWidth(); + bool redraw = true; + bool dirty = editWidget->isDirty(); + char *text; + + switch (event.key) { + case SB_KEY_MENU: + redraw = false; + break; + case SB_KEY_F(1): + widget = helpWidget; + helpWidget->createKeywordIndex(); + helpWidget->show(); + helpWidget->setFocus(true); + runtime->showKeypad(false); + break; + case SB_KEY_F(9): + _state = kRunState; + if (editWidget->isDirty()) { + saveFile(editWidget, loadPath); + } + break; + case SB_KEY_CTRL('s'): + saveFile(editWidget, loadPath); + break; + case SB_KEY_CTRL('c'): + case SB_KEY_CTRL('x'): + text = widget->copy(event.key == (int)SB_KEY_CTRL('x')); + if (text) { + setClipboardText(text); + free(text); + } + break; + case SB_KEY_CTRL('v'): + text = getClipboardText(); + widget->paste(text); + free(text); + break; + case SB_KEY_CTRL('o'): + _output->selectScreen(USER_SCREEN1); + showCompletion(true); + _output->redraw(); + _state = kActiveState; + waitForBack(); + runtime->showKeypad(true); + _output->selectScreen(SOURCE_SCREEN); + _state = kEditState; + break; + default: + redraw = widget->edit(event.key, sw, charWidth); + break; + } + if (widget->isDirty() && !dirty) { + _output->setStatus(dirtyFile); + } else if (!widget->isDirty() && dirty) { + _output->setStatus(cleanFile); + } + if (redraw) { + _output->redraw(); + } + } + + if (isBack() && widget == helpWidget) { + runtime->showKeypad(true); + widget = editWidget; + helpWidget->hide(); + editWidget->setFocus(true); + _state = kEditState; + _output->redraw(); + } + + if (widget->isDirty()) { + int choice = -1; + if (isClosing()) { + choice = 0; + } else if (isBack()) { + const char *message = "The current file has not been saved.\n" + "Would you like to save it now?"; + choice = ask("Save changes?", message, isBack()); + } + if (choice == 0) { + widget->save(loadPath); + } else if (choice == 2) { + // cancel + _state = kEditState; + } + } + } + + _output->removeInputs(); + if (!isClosing()) { + _output->selectScreen(prevScreenId); + } + logLeaving(); +} + // // ma event handling // diff --git a/src/platform/android/jni/runtime.h b/src/platform/android/jni/runtime.h index 61be38b2..9805c05f 100644 --- a/src/platform/android/jni/runtime.h +++ b/src/platform/android/jni/runtime.h @@ -22,6 +22,7 @@ struct Runtime : public System { virtual ~Runtime(); void alert(const char *title, const char *message); + void alert(const char *title, int duration = 5000); int ask(const char *title, const char *prompt, bool cancel); void clearSoundQueue(); void construct(); diff --git a/src/platform/android/src/net/sourceforge/smallbasic/AskResult.java b/src/platform/android/src/net/sourceforge/smallbasic/AskResult.java new file mode 100644 index 00000000..9d42750a --- /dev/null +++ b/src/platform/android/src/net/sourceforge/smallbasic/AskResult.java @@ -0,0 +1,37 @@ +package net.sourceforge.smallbasic; + +class AskResult { + private static final int ASK_YES = 0; + private static final int ASK_NO = 1; + private static final int ASK_CANCEL = 2; + int value; + + AskResult() { + value = ASK_CANCEL; + } + + public void setCancel() { + value = ASK_CANCEL; + } + + public void setNo() { + value = ASK_NO; + } + + public void setYes() { + value = ASK_YES; + } + + @Override + public String toString() { + switch (value) { + case ASK_YES: + return "Yes"; + case ASK_NO: + return "No"; + default: + return "cancel"; + } + } +} + diff --git a/src/platform/android/src/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/src/net/sourceforge/smallbasic/MainActivity.java index 1109d2d2..455ee438 100644 --- a/src/platform/android/src/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/src/net/sourceforge/smallbasic/MainActivity.java @@ -40,6 +40,7 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.res.Resources; import android.graphics.Rect; @@ -55,6 +56,7 @@ import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; +import android.widget.Toast; /** * Extends NativeActivity to provide interface methods for runtime.cpp @@ -82,33 +84,53 @@ public class MainActivity extends NativeActivity { public static native void onResize(int width, int height); public static native void runFile(String fileName); - public boolean ask(String prompt, String accept, String cancel) { - // TODO: fixme - boolean result = false; - final Context context = this; + public int ask(final String title, final String prompt, final boolean cancel) { + final AskResult result = new AskResult(); + final Activity activity = this; final Semaphore mutex = new Semaphore(0); final Runnable runnable = new Runnable() { public void run() { - new AlertDialog.Builder(activity) - .setTitle(title).setMessage(message) - .setPositiveButton(accept, new DialogInterface.OnClickListener() { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(title).setMessage(prompt); + builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.setYes(); + mutex.release(); + } + }); + builder.setNegativeButton("No", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.setNo(); + mutex.release(); + } + }); + builder.setOnCancelListener(new OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + result.setCancel(); + mutex.release(); + } + }); + if (cancel) { + builder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { + result.setCancel(); + mutex.release(); } - }) - .setPositiveButton(cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) {} - }).show(); - mutex.release(); + }); + } + builder.show(); } }; runOnUiThread(runnable); try { mutex.acquire(); } catch (InterruptedException e) { - Log.i(TAG, "getClipboardText failed: ", e); + Log.i(TAG, "ask failed: ", e); e.printStackTrace(); } - return result; + Log.i(TAG, "ask result=" + result); + return result.value; } public void clearSoundQueue() { @@ -148,7 +170,7 @@ public void run() { } public String getIPAddress() { - String result = null; + String result = ""; try { for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); @@ -221,7 +243,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } return true; } - + @Override public boolean onPrepareOptionsMenu(Menu menu) { if (this._options != null) { @@ -265,7 +287,7 @@ public void run() { } }); } - + public void showAlert(final String title, final String message) { final Activity activity = this; runOnUiThread(new Runnable() { @@ -279,6 +301,15 @@ public void onClick(DialogInterface dialog, int which) {} }); } + public void showToast(final String message, final int duration) { + final Activity activity = this; + runOnUiThread(new Runnable() { + public void run() { + Toast.makeText(activity, message, duration).show(); + } + }); + } + public void showKeypad(final boolean show) { Log.i(TAG, "showKeypad: " + show); final View view = getWindow().getDecorView(); diff --git a/src/platform/fltk/Makefile.am b/src/platform/fltk/Makefile.am index 799d4ee9..87cb1930 100644 --- a/src/platform/fltk/Makefile.am +++ b/src/platform/fltk/Makefile.am @@ -47,7 +47,6 @@ sbasici_DEPENDENCIES = $(top_srcdir)/src/common/libsb_common.a iconsdir = $(datadir)/icons/hicolor/32x32/apps icons_DATA = ../../../images/sb-desktop-32x32.png -desktopentry_DATA = smallbasic.desktop if WITH_WIN32 sbasici_LDADD += sbfltk.res diff --git a/src/platform/fltk/display.h b/src/platform/fltk/display.h index 2bef7e31..921c5818 100755 --- a/src/platform/fltk/display.h +++ b/src/platform/fltk/display.h @@ -44,7 +44,7 @@ struct Canvas { bool _isScreen; }; -class DisplayWidget : public fltk::Widget, IButtonListener { +class DisplayWidget : public fltk::Widget { public: DisplayWidget(int x, int y, int w, int h, int defsize); virtual ~DisplayWidget(); diff --git a/src/platform/sdl/Makefile.am b/src/platform/sdl/Makefile.am index cfa57b7b..a087e5b8 100644 --- a/src/platform/sdl/Makefile.am +++ b/src/platform/sdl/Makefile.am @@ -1,5 +1,5 @@ # SmallBASIC for SDL -# Copyright(C) 2001-2014 Chris Warren-Smith. +# Copyright(C) 2001-2015 Chris Warren-Smith. # # This program is distributed under the terms of the GPL v2.0 or later # Download the GNU Public License (GPL) from www.gnu.org @@ -7,10 +7,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src -I. @PACKAGE_CFLAGS@ -EXTRA_DIST = \ - fixedfont.xpm \ - sbasic.nsi \ - welcome.bas +EXTRA_DIST = $(desktopentry_DATA) bin_PROGRAMS = sbasicg @@ -20,7 +17,6 @@ sbasicg_SOURCES = \ ../../ui/window.cpp \ ../../ui/screen.cpp \ ../../ui/system.cpp \ - ../../ui/editor.cpp \ ../../ui/form.cpp \ ../../ui/inputs.cpp \ ../../ui/textedit.cpp \ @@ -30,12 +26,18 @@ sbasicg_SOURCES = \ display.cpp \ runtime.cpp \ settings.cpp \ + editor.cpp \ syswm.cpp sbasicg_LDADD = -L$(top_srcdir)/src/common -lsb_common @PACKAGE_LIBS@ sbasicg_DEPENDENCIES = $(top_srcdir)/src/common/libsb_common.a +iconsdir = $(datadir)/icons/hicolor/128x128/apps +icons_DATA = ../../../images/sb-desktop-128x128.png +desktopdir = $(datadir)/applications +desktop_DATA = smallbasic.desktop + if WITH_WIN32 sbasicg_LDADD += win.res sbasicg_DEPENDENCIES += win.res diff --git a/src/ui/editor.cpp b/src/platform/sdl/editor.cpp similarity index 95% rename from src/ui/editor.cpp rename to src/platform/sdl/editor.cpp index ff4f2040..0a8dcce0 100644 --- a/src/ui/editor.cpp +++ b/src/platform/sdl/editor.cpp @@ -12,11 +12,6 @@ #include "ui/system.h" #include "ui/textedit.h" -#define SAVE_FILE() \ - if (!editWidget->save(loadPath)) \ - alert("", "Failed to save file"); \ - else _modifiedTime = getModifiedTime(); - void System::editSource(strlib::String &loadPath) { logEntered(); @@ -50,7 +45,7 @@ void System::editSource(strlib::String &loadPath) { editWidget->updateUI(NULL, NULL); editWidget->setLineNumbers(); - editWidget->setFocus(); + editWidget->setFocus(true); if (strcmp(gsb_last_file, loadPath.c_str()) == 0) { editWidget->setCursorRow(gsb_last_line - 1); } @@ -68,7 +63,6 @@ void System::editSource(strlib::String &loadPath) { _output->redraw(); _state = kEditState; - maShowVirtualKeyboard(); showCursor(kIBeam); while (_state == kEditState) { @@ -98,6 +92,7 @@ void System::editSource(strlib::String &loadPath) { case SB_KEY_F(10): case SB_KEY_F(11): case SB_KEY_F(12): + case SB_KEY_MENU: redraw = false; break; case SB_KEY_ESCAPE: @@ -110,14 +105,15 @@ void System::editSource(strlib::String &loadPath) { case SB_KEY_CTRL('r'): _state = kRunState; if (editWidget->isDirty()) { - SAVE_FILE(); + saveFile(editWidget, loadPath); } break; case SB_KEY_CTRL('s'): - SAVE_FILE(); + saveFile(editWidget, loadPath); break; case SB_KEY_CTRL('c'): case SB_KEY_CTRL('x'): + case SB_KEY_CTRL(SB_KEY_INSERT): text = widget->copy(event.key == (int)SB_KEY_CTRL('x')); if (text) { setClipboardText(text); @@ -132,7 +128,7 @@ void System::editSource(strlib::String &loadPath) { helpWidget->show(); break; case SB_KEY_F(5): - SAVE_FILE(); + saveFile(editWidget, loadPath); _output->setStatus("Debug. F6=Step, F7=Continue, Esc=Close"); widget = helpWidget; helpWidget->createMessage(); @@ -182,6 +178,7 @@ void System::editSource(strlib::String &loadPath) { helpWidget->show(); break; case SB_KEY_CTRL('v'): + case SB_KEY_SHIFT(SB_KEY_INSERT): text = getClipboardText(); widget->paste(text); free(text); @@ -213,6 +210,10 @@ void System::editSource(strlib::String &loadPath) { dirty = !editWidget->isDirty(); } } + + helpWidget->setFocus(widget == helpWidget); + editWidget->setFocus(widget == editWidget); + if (editWidget->isDirty() && !dirty) { _output->setStatus(dirtyFile); } else if (!editWidget->isDirty() && dirty) { diff --git a/src/platform/sdl/main.cpp b/src/platform/sdl/main.cpp index d488c439..b391b945 100644 --- a/src/platform/sdl/main.cpp +++ b/src/platform/sdl/main.cpp @@ -254,6 +254,7 @@ int main(int argc, char* argv[]) { char *runFile = NULL; bool debug = false; int fontScale; + int ide_option = -1; SDL_Rect rect; while (1) { @@ -301,11 +302,11 @@ int main(int argc, char* argv[]) { break; case 'r': runFile = strdup(optarg); - opt_ide = IDE_NONE; + ide_option = IDE_NONE; break; case 'e': runFile = strdup(optarg); - opt_ide = IDE_INTERNAL; + ide_option = IDE_INTERNAL; break; case 'm': opt_loadmod = 1; @@ -313,7 +314,7 @@ int main(int argc, char* argv[]) { break; case 'd': runFile = strdup(optarg); - opt_ide = IDE_EXTERNAL; + ide_option = IDE_EXTERNAL; debug = true; break; case 'p': @@ -334,6 +335,10 @@ int main(int argc, char* argv[]) { } restoreSettings(rect, fontScale, debug); + if (ide_option != -1) { + opt_ide = ide_option; + } + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); SDL_Window *window = SDL_CreateWindow("SmallBASIC", rect.x, rect.y, rect.w, rect.h, diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index 630b9631..68c62df2 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -504,7 +504,7 @@ void Runtime::pollEvents(bool blocking) { maEvent = new MAEvent(); maEvent->type = EVENT_TYPE_KEY_PRESSED; maEvent->key = ev.wheel.y == 1 ? SDLK_UP : SDLK_DOWN; - maEvent->nativeKey = 0; + maEvent->nativeKey = KMOD_CTRL; } break; } @@ -696,6 +696,24 @@ char *Runtime::getClipboardText() { return result; } +// +// System platform methods +// +bool System::getPen3() { + SDL_PumpEvents(); + return (SDL_BUTTON(SDL_BUTTON_LEFT) && SDL_GetMouseState(&_touchCurX, &_touchCurY)); +} + +void System::completeKeyword(int index) { + if (get_focus_edit() && isEditing()) { + const char *help = get_focus_edit()->completeKeyword(index); + if (help) { + runtime->getOutput()->setStatus(help); + runtime->getOutput()->redraw(); + } + } +} + // // ma event handling // diff --git a/src/platform/fltk/smallbasic.desktop b/src/platform/sdl/smallbasic.desktop similarity index 65% rename from src/platform/fltk/smallbasic.desktop rename to src/platform/sdl/smallbasic.desktop index d4db057d..441680d6 100644 --- a/src/platform/fltk/smallbasic.desktop +++ b/src/platform/sdl/smallbasic.desktop @@ -1,11 +1,11 @@ [Desktop Entry] +Encoding=UTF-8 Name=SmallBASIC Comment=SmallBASIC -Exec=sbasici -Icon=/usr/share/icons/hicolor/32x32/apps/sb-desktop-32x32.png +Exec=sbasicg +Icon=sb-desktop-128x128.png Terminal=false Type=Application Categories=Application;Development; MimeType=application/bas - - +NoDisplay=false diff --git a/src/platform/sdl/syswm.cpp b/src/platform/sdl/syswm.cpp index 0173ff97..48290e42 100644 --- a/src/platform/sdl/syswm.cpp +++ b/src/platform/sdl/syswm.cpp @@ -28,7 +28,8 @@ void loadIcon(SDL_Window *window) { SDL_VERSION(&wminfo.version); if (SDL_GetWindowWMInfo(window, &wminfo) == 1) { HWND hwnd = wminfo.info.win.window; - ::SetClassLong(hwnd, GCL_HICON, reinterpret_cast(icon)); + ::SendMessage(hwnd, WM_SETICON, 0, reinterpret_cast(icon)); + ::SendMessage(hwnd, WM_SETICON, 1, reinterpret_cast(icon)); } } } diff --git a/src/ui/ansiwidget.cpp b/src/ui/ansiwidget.cpp index 3409d8e2..0e6d9424 100755 --- a/src/ui/ansiwidget.cpp +++ b/src/ui/ansiwidget.cpp @@ -141,7 +141,7 @@ void AnsiWidget::drawRectFilled(int x1, int y1, int x2, int y2) { flush(false, false, MAX_PENDING_GRAPHICS); } -// display and pending images changed +// display any pending images changed void AnsiWidget::flush(bool force, bool vscroll, int maxPending) { if (_front != NULL && _autoflush) { bool update = false; @@ -587,6 +587,7 @@ void AnsiWidget::drawActiveButton() { } else if (_focus != NULL) { MAHandle currentHandle = maSetDrawTarget(HANDLE_SCREEN); _focus->drawShape(_activeButton); + _focus->drawLabel(); maUpdateScreen(); maSetDrawTarget(currentHandle); } diff --git a/src/ui/ansiwidget.h b/src/ui/ansiwidget.h index 1ee564e3..5fba1e7b 100755 --- a/src/ui/ansiwidget.h +++ b/src/ui/ansiwidget.h @@ -74,6 +74,7 @@ struct AnsiWidget { void removeInputs() { _back->removeInputs(); _activeButton = NULL; } void resetScroll() { _back->resetScroll(); } void reset(); + void resetFont() { _back->reset(_fontSize); _back->updateFont(); } void resize(int width, int height); bool scroll(bool up, bool page); int selectBackScreen(int screenId); diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index eaf1fb48..1eebf9af 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -289,7 +289,7 @@ void Graphics::getImageData(Canvas *canvas, uint8_t *image, for (int dx = 0, x = srcRect->left; x < srcRect->width; x += scale, dx++) { if (x >= canvas->x() && x < canvas->w()) { uint8_t r,g,b; - GET_RGB(line[x], r, g, b); + GET_RGB2(line[x], r, g, b); int offs = (4 * dy * w) + (4 * dx); image[offs + 0] = r; image[offs + 1] = g; diff --git a/src/ui/graphics.h b/src/ui/graphics.h index 66627aac..a229afa4 100644 --- a/src/ui/graphics.h +++ b/src/ui/graphics.h @@ -21,35 +21,34 @@ using namespace strlib; -inline void RGB565_to_RGB(pixel_t c, uint8_t &r, uint8_t &g, uint8_t &b) { - r = (c >> 11) & 0x1f; - g = (c >> 5) & 0x3f; - b = (c) & 0x1f; +inline void RGB888_to_RGB(pixel_t c, uint8_t &r, uint8_t &g, uint8_t &b) { + r = (c & 0xff0000) >> 16; + g = (c & 0xff00) >> 8; + b = (c & 0xff); } -inline pixel_t RGB888_to_RGB565(unsigned rgb) { - return ((((rgb >> 19) & 0x1f) << 11) | - (((rgb >> 10) & 0x3f) << 5) | - (((rgb >> 3) & 0x1f))); +inline pixel_t RGB888_to_RGBA8888(unsigned c) { + uint8_t r = (c & 0xff0000) >> 16; + uint8_t g = (c & 0xff00) >> 8; + uint8_t b = (c & 0xff); + return ((0xff000000) | (b << 16) | (g << 8) | (r)); } -inline void RGB888_to_RGB(pixel_t c, uint8_t &r, uint8_t &g, uint8_t &b) { - r = (c & 0xff0000) >> 16; +inline void RGB888_BE_to_RGB(pixel_t c, uint8_t &r, uint8_t &g, uint8_t &b) { + b = (c & 0xff0000) >> 16; g = (c & 0xff00) >> 8; - b = (c & 0xff); + r = (c & 0xff); } -#if defined(PIXELFORMAT_RGB565) - #define SET_RGB(r, g, b) ((r << 11) | (g << 5) | (b)) - #define GET_RGB RGB565_to_RGB - #define GET_FROM_RGB888 RGB888_to_RGB565 -#elif defined(PIXELFORMAT_ARGB8888) +#if defined(PIXELFORMAT_RGBA8888) #define SET_RGB(r, g, b) ((0xff000000) | (r << 16) | (g << 8) | (b)) - #define GET_RGB RGB888_to_RGB - #define GET_FROM_RGB888(c) (0xff000000 | c) + #define GET_RGB RGB888_to_RGB + #define GET_RGB2 RGB888_BE_to_RGB + #define GET_FROM_RGB888 RGB888_to_RGBA8888 #else #define SET_RGB(r, g, b) ((r << 16) | (g << 8) | (b)) #define GET_RGB RGB888_to_RGB + #define GET_RGB2 RGB888_to_RGB #define GET_FROM_RGB888(c) (c) #endif diff --git a/src/ui/inputs.cpp b/src/ui/inputs.cpp index b6a1b97a..c9412f50 100644 --- a/src/ui/inputs.cpp +++ b/src/ui/inputs.cpp @@ -335,10 +335,10 @@ bool FormInput::hasFocus() const { return (focusInput == this); } -void FormInput::setFocus() { +void FormInput::setFocus(bool focus) { if (!isNoFocus()) { - if (focusInput != this) { - focusInput = this; + if (focus == (focusInput != this)) { + focusInput = focus ? this : NULL; g_system->getOutput()->setDirty(); } } @@ -458,11 +458,11 @@ int FormEditInput::getControlKey(int key) { return result; } -void FormEditInput::setFocus() { +void FormEditInput::setFocus(bool focus) { if (!isNoFocus()) { - if (focusInput != this) { - focusInput = this; - focusEdit = this; + if (focus == (focusInput != this)) { + focusInput = focus ? this : NULL; + focusEdit = focus ? this : NULL; g_system->getOutput()->setDirty(); } } diff --git a/src/ui/inputs.h b/src/ui/inputs.h index cd3c41ce..de826f85 100644 --- a/src/ui/inputs.h +++ b/src/ui/inputs.h @@ -73,11 +73,6 @@ namespace form_ui { int get_color(var_p_t value, int def); -struct IButtonListener { - virtual ~IButtonListener() {} - virtual void buttonClicked(const char *action) = 0; -}; - struct IFormWidgetListModel { virtual ~IFormWidgetListModel() {} virtual const char *getTextAt(int index) = 0; @@ -120,7 +115,7 @@ struct FormInput : public Shape { virtual void clicked(int x, int y, bool pressed); virtual bool isDrawTop() { return false; } virtual bool hasHover() { return false; } - virtual void setFocus(); + virtual void setFocus(bool focus); virtual void resize(int w, int h) {} void construct(var_p_t form, var_p_t field, int id); @@ -212,12 +207,15 @@ struct FormEditInput : public FormInput { virtual char *copy(bool cut) = 0; virtual void paste(const char *text) = 0; virtual void selectAll() = 0; - void setFocus(); + virtual const char *completeKeyword(int index) = 0; + virtual int getCompletions(StringList *list, int max) = 0; + + void setFocus(bool focus); int getControlKey(int key); bool getControlMode() const { return _controlMode; } void setControlMode(bool cursorMode) { _controlMode = cursorMode; } -protected: +protected: bool _controlMode; }; @@ -238,6 +236,8 @@ struct FormLineInput : public FormEditInput { void paste(const char *text); void cut(); void selectAll(); + const char *completeKeyword(int index) { return NULL; } + int getCompletions(StringList *list, int max) { return 0; } private: char *_buffer; diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index 8b1210de..da46a86a 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -281,6 +281,7 @@ void Screen::removeImage(unsigned imageId) { } void Screen::replaceFont(int type) { + logEntered(); if (_font) { maFontDelete(_font); } diff --git a/src/ui/screen.h b/src/ui/screen.h index 9de4433a..bbcfbcc2 100755 --- a/src/ui/screen.h +++ b/src/ui/screen.h @@ -107,9 +107,9 @@ struct GraphicScreen : public Screen { void reset(int fontSize); bool setGraphicsRendition(const char c, int escValue, int lineHeight); void setPixel(int x, int y, int c); - void resize(int newWidth, int newHeight, int oldWidth, + void resize(int newWidth, int newHeight, int oldWidth, int oldHeight, int lineHeight); - void updateFont() { replaceFont(); } + void updateFont() { setFont(_bold, _italic, _fontSize); } int getPixel(int x, int y); int getMaxHScroll() { return 0; } @@ -322,7 +322,7 @@ struct TextScreen : public Screen { void inset(int x, int y, int w, int h, Screen *over); void newLine(int lineHeight); int print(const char *p, int lineHeight, bool allChars=false); - void resize(int newWidth, int newHeight, int oldWidth, + void resize(int newWidth, int newHeight, int oldWidth, int oldHeight, int lineHeight); bool setGraphicsRendition(const char c, int escValue, int lineHeight); void setOver(Screen *over) { _over = over; } diff --git a/src/ui/system.cpp b/src/ui/system.cpp index af4e6149..70e3f917 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -34,14 +34,19 @@ #define MENU_EDITMODE 11 #define MENU_AUDIO 12 #define MENU_SCREENSHOT 13 -#define MENU_SCRATCHPAD 14 -#define MENU_UNDO 15 -#define MENU_REDO 16 -#define MENU_SAVE 17 -#define MENU_RUN 18 -#define MENU_DEBUG 19 -#define MENU_OUTPUT 20 +#define MENU_UNDO 14 +#define MENU_REDO 15 +#define MENU_SAVE 16 +#define MENU_RUN 17 +#define MENU_DEBUG 18 +#define MENU_OUTPUT 19 +#define MENU_HELP 20 #define MENU_SIZE 21 +#define MENU_COMPETION_0 (MENU_SIZE + 1) +#define MENU_COMPETION_1 (MENU_SIZE + 2) +#define MENU_COMPETION_2 (MENU_SIZE + 3) +#define MENU_COMPETION_3 (MENU_SIZE + 4) +#define MAX_COMPLETIONS 4 #define FONT_SCALE_INTERVAL 10 #define FONT_MIN 20 @@ -112,11 +117,13 @@ bool System::execute(const char *bas) { } opt_command[0] = '\0'; + _output->resetFont(); _output->flush(true); return result; } int System::getPen(int code) { + _output->flush(true); int result = 0; if (!isClosing()) { switch (code) { @@ -126,11 +133,7 @@ int System::getPen(int code) { // fallthru case 3: // returns true if the pen is down (and save curpos) - if (_touchX != -1 && _touchY != -1) { - result = 1; - } else { - processEvents(0); - } + result = getPen3(); break; case 1: // last pen-down x @@ -166,7 +169,7 @@ char *System::getText(char *dest, int maxSize) { int charWidth = _output->getCharWidth(); FormInput *widget = new FormLineInput(NULL, maxSize, true, x, y, w, h); - widget->setFocus(); + widget->setFocus(true); _output->addInput(widget); _output->redraw(); _state = kModalState; @@ -283,8 +286,12 @@ void System::handleMenu(MAEvent &event) { } break; case MENU_EDITMODE: +#if defined(_SDL) opt_ide = (opt_ide == IDE_NONE ? IDE_INTERNAL : opt_ide == IDE_INTERNAL ? IDE_EXTERNAL : IDE_NONE); +#else + opt_ide = (opt_ide == IDE_NONE ? IDE_INTERNAL : IDE_NONE); +#endif break; case MENU_AUDIO: opt_mute_audio = !opt_mute_audio; @@ -292,9 +299,6 @@ void System::handleMenu(MAEvent &event) { case MENU_SCREENSHOT: ::screen_dump(); break; - case MENU_SCRATCHPAD: - scratchPad(); - break; case MENU_UNDO: event.type = EVENT_TYPE_KEY_PRESSED; event.key = SB_KEY_CTRL('z'); @@ -319,6 +323,22 @@ void System::handleMenu(MAEvent &event) { event.type = EVENT_TYPE_KEY_PRESSED; event.key = SB_KEY_CTRL('o'); break; + case MENU_HELP: + event.type = EVENT_TYPE_KEY_PRESSED; + event.key = SB_KEY_F(1); + break; + case MENU_COMPETION_0: + completeKeyword(0); + break; + case MENU_COMPETION_1: + completeKeyword(1); + break; + case MENU_COMPETION_2: + completeKeyword(2); + break; + case MENU_COMPETION_3: + completeKeyword(3); + break; } if (fontSize != _output->getFontSize()) { @@ -398,7 +418,7 @@ char *System::loadResource(const char *fileName) { memcpy(buffer, var_p->v.p.ptr, len); buffer[len] = '\0'; } else { - systemPrint("\nfailed"); + systemPrint("\nfailed to open %s", fileName); } _output->setStatus(NULL); dev_fclose(handle); @@ -515,8 +535,8 @@ void System::runMain(const char *mainBasPath) { } } - if (!_mainBas && opt_ide == IDE_INTERNAL && - !isRestart() && loadSource(_loadPath)) { + if (!_mainBas && opt_ide == IDE_INTERNAL && !isRestart() && + _loadPath.indexOf("://", 1) == -1 && loadSource(_loadPath)) { editSource(_loadPath); if (isBack()) { _loadPath.empty(); @@ -528,12 +548,14 @@ void System::runMain(const char *mainBasPath) { } bool success = execute(_loadPath); + bool networkFile = (_loadPath.indexOf("://", 1) != -1); if (!isClosing() && _overruns) { systemPrint("\nOverruns: %d\n", _overruns); } - if (!isBack() && !isClosing() && opt_ide != IDE_INTERNAL) { - // load the next network file without displaying the previous result - bool networkFile = (_loadPath.indexOf("://", 1) != -1); + if (!isBack() && !isClosing() && + (opt_ide != IDE_INTERNAL || success || networkFile)) { + // when editing, only pause here when successful, otherwise the editor shows + // the error. load the next network file without displaying the previous result if (!_mainBas && !networkFile) { // display an indication the program has completed showCompletion(success); @@ -573,6 +595,14 @@ void System::runOnce(const char *startupBas) { } } +void System::saveFile(TextEditInput *edit, strlib::String &path) { + if (!edit->save(path)) { + alert("", "Failed to save file"); + } else { + _modifiedTime = getModifiedTime(); + } +} + void System::setBack() { if (_userScreenId != -1) { // restore user screen @@ -652,7 +682,8 @@ void System::setRunning(bool running) { dev_clrkb(); _output->setAutoflush(!opt_show_page); - if (_mainBas || opt_ide != IDE_INTERNAL) { + if (_mainBas || opt_ide != IDE_INTERNAL || + _loadPath.indexOf("://", 1) != -1) { _loadPath.empty(); } _lastEventTime = maGetMilliSecondCount(); @@ -709,7 +740,14 @@ void System::showMenu() { } StringList *items = new StringList(); - _systemMenu = new int[MENU_SIZE]; + int completions = 0; + + if (get_focus_edit() && isEditing()) { + completions = get_focus_edit()->getCompletions(items, MAX_COMPLETIONS); + } + + _systemMenu = new int[MENU_SIZE + completions]; + int index = 0; if (get_focus_edit() != NULL) { if (isEditing()) { @@ -720,8 +758,14 @@ void System::showMenu() { items->add(new String("Paste")); items->add(new String("Save")); items->add(new String("Run")); +#if defined(_SDL) items->add(new String("Debug")); - items->add(new String("Show output")); + items->add(new String("Show Output")); +#endif + items->add(new String("Help")); + for (int i = 0; i < completions; i++) { + _systemMenu[index++] = MENU_COMPETION_0 + i; + } _systemMenu[index++] = MENU_UNDO; _systemMenu[index++] = MENU_REDO; _systemMenu[index++] = MENU_CUT; @@ -729,8 +773,11 @@ void System::showMenu() { _systemMenu[index++] = MENU_PASTE; _systemMenu[index++] = MENU_SAVE; _systemMenu[index++] = MENU_RUN; +#if defined(_SDL) _systemMenu[index++] = MENU_DEBUG; _systemMenu[index++] = MENU_OUTPUT; +#endif + _systemMenu[index++] = MENU_HELP; } else if (isRunning()) { items->add(new String("Cut")); items->add(new String("Copy")); @@ -745,10 +792,12 @@ void System::showMenu() { #else items->add(new String("Show keypad")); _systemMenu[index++] = MENU_KEYPAD; - bool controlMode = get_focus_edit()->getControlMode(); - sprintf(buffer, "Control Mode [%s]", (controlMode ? "ON" : "OFF")); - items->add(new String(buffer)); - _systemMenu[index++] = MENU_CTRL_MODE; + if (!isEditing()) { + bool controlMode = get_focus_edit()->getControlMode(); + sprintf(buffer, "Control Mode [%s]", (controlMode ? "ON" : "OFF")); + items->add(new String(buffer)); + _systemMenu[index++] = MENU_CTRL_MODE; + } #endif } else { if (_overruns == 0) { @@ -765,29 +814,23 @@ void System::showMenu() { items->add(new String("Show keypad")); _systemMenu[index++] = MENU_KEYPAD; #endif + items->add(new String("Screenshot")); + _systemMenu[index++] = MENU_SCREENSHOT; if (_mainBas) { - sprintf(buffer, "Scratchpad"); - items->add(new String(buffer)); sprintf(buffer, "Font Size %d%%", _fontScale - FONT_SCALE_INTERVAL); items->add(new String(buffer)); sprintf(buffer, "Font Size %d%%", _fontScale + FONT_SCALE_INTERVAL); items->add(new String(buffer)); - _systemMenu[index++] = MENU_SCRATCHPAD; _systemMenu[index++] = MENU_ZOOM_UP; _systemMenu[index++] = MENU_ZOOM_DN; - sprintf(buffer, "Editor [%s]", (opt_ide == IDE_NONE ? "OFF" : - opt_ide == IDE_EXTERNAL ? "Live Mode" : "ON")); + opt_ide == IDE_INTERNAL ? "ON" : "Live Mode")); items->add(new String(buffer)); _systemMenu[index++] = MENU_EDITMODE; } - sprintf(buffer, "Audio [%s]", (opt_mute_audio ? "OFF" : "ON")); items->add(new String(buffer)); _systemMenu[index++] = MENU_AUDIO; - - items->add(new String("Screenshot")); - _systemMenu[index++] = MENU_SCREENSHOT; #if defined(_SDL) items->add(new String("Back")); _systemMenu[index++] = MENU_BACK; @@ -937,28 +980,6 @@ void System::printSource() { } } -void System::scratchPad() { - const char *path = gsb_bas_dir; -#if defined(_ANDROID) - path = "/sdcard/"; -#endif - char file[OS_PATHNAME_SIZE]; - sprintf(file, "%sscratch.bas", path); - bool ready = access(file, R_OK) == 0; - if (!ready) { - FILE *fp = fopen(file, "w"); - if (fp) { - fprintf(fp, "REM scratch buffer\n"); - fclose(fp); - ready = true; - } - } - if (ready) { - setLoadPath(file); - setExit(false); - } -} - void System::setExit(bool quit) { if (!isClosing()) { bool running = isRunning(); @@ -1089,7 +1110,7 @@ void lwrite(const char *str) { } void dev_delay(dword ms) { - g_system->getOutput()->redraw(); + g_system->getOutput()->flush(true); maWait(ms); } diff --git a/src/ui/system.h b/src/ui/system.h index 9bb383ee..a0a12593 100755 --- a/src/ui/system.h +++ b/src/ui/system.h @@ -73,6 +73,7 @@ struct System { void runEdit(const char *startupBas); void runMain(const char *mainBasPath); void runOnce(const char *startupBas); + void saveFile(TextEditInput *edit, strlib::String &path); void setPath(const char *filename); bool setParentPath(); void setDimensions(); @@ -82,13 +83,16 @@ struct System { void printErrorLine(); void printSource(); void printSourceLine(char *text, int line, bool last); - void scratchPad(); void setRestart(); void showMenu(); void showSystemScreen(bool showSrc); void waitForBack(); AnsiWidget *_output; + // platform static virtual + bool getPen3(); + void completeKeyword(int index); + enum { kInitState = 0,// thread not active kActiveState, // thread activated diff --git a/src/ui/textedit.cpp b/src/ui/textedit.cpp index 3d5a6651..0722ceb2 100644 --- a/src/ui/textedit.cpp +++ b/src/ui/textedit.cpp @@ -25,8 +25,19 @@ #define INDENT_LEVEL 2 #define HELP_WIDTH 22 #define NUM_THEMES 4 +#define TWISTY1_OPEN "> " +#define TWISTY1_CLOSE "< " +#define TWISTY2_OPEN " > " +#define TWISTY2_CLOSE " < " +#define TWISTY1_LEN 2 +#define TWISTY2_LEN 4 +#define HELP_BG 0x73c990 +#define HELP_FG 0x20242a int g_themeId = 0; +int g_lineMarker[MAX_MARKERS] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; const int theme1[] = { 0xc8cedb, 0xa7aebc, 0x484f5f, 0xa7aebc, 0xa7aebc, 0x00bb00, @@ -107,6 +118,10 @@ int compareIntegers(const void *p1, const void *p2) { return i1 < i2 ? -1 : i1 == i2 ? 0 : 1; } +void init_edit_markers() { + +} + // // EditTheme // @@ -264,9 +279,9 @@ TextEditInput::TextEditInput(const char *text, int chW, int chH, _cursorLine(0), _indentLevel(INDENT_LEVEL), _matchingBrace(-1), + _ptY(-1), _dirty(false) { stb_textedit_initialize_state(&_state, false); - memset(_lineMarker, -1, sizeof(_lineMarker)); } TextEditInput::~TextEditInput() { @@ -291,6 +306,27 @@ void TextEditInput::completeWord(const char *word) { } } +const char *TextEditInput::completeKeyword(int index) { + const char *help = NULL; + char *selection = getWordBeforeCursor(); + if (selection != NULL) { + 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 && + count++ == index) { + if (IS_WHITE(_buf._buffer[_state.cursor]) || _buf._buffer[_state.cursor] == '\0') { + completeWord(keyword_help[i].keyword); + } + help = keyword_help[i].signature; + break; + } + } + free(selection); + } + return help; +} + void TextEditInput::draw(int x, int y, int w, int h, int chw) { SyntaxState syntax = kReset; StbTexteditRow r; @@ -319,6 +355,7 @@ void TextEditInput::draw(int x, int y, int w, int h, int chw) { if (i == 0 || _buf._buffer[i - 1] == '\r' || _buf._buffer[i - 1] == '\n') { + syntax = kReset; line++; } @@ -405,6 +442,20 @@ void TextEditInput::draw(int x, int y, int w, int h, int chw) { } } baseY += _charHeight; + } else if (row <= _scroll && syntax == kReset) { + int end = i + r.num_chars - 1; + if (_buf._buffer[end] != '\r' && + _buf._buffer[end] != '\n') { + // scrolled line continues to next line + for (int j = i; j < end; j++) { + if (is_comment(_buf._buffer, j)) { + syntax = kComment; + break; + } else if (_buf._buffer[j] == '\"') { + syntax = (syntax == kText) ? kReset : kText; + } + } + } } i += r.num_chars; } @@ -431,6 +482,18 @@ void TextEditInput::draw(int x, int y, int w, int h, int chw) { } } +void TextEditInput::dragPage(int y, bool &redraw) { + int size = abs(y - _ptY); + int minSize = _charHeight / 4; + if (_ptY == -1) { + _ptY = y; + } else if (size > minSize) { + lineNavigate(y < _ptY); + redraw = true; + _ptY = y; + } +} + void TextEditInput::drawText(int x, int y, const char *str, int length, SyntaxState &state) { int i = 0; @@ -581,17 +644,16 @@ bool TextEditInput::edit(int key, int screenWidth, int charWidth) { } _cursorRow = getCursorRow(); - if (key == STB_TEXTEDIT_K_UP) { + if (key == STB_TEXTEDIT_K_UP || + key == (int)SB_KEY_SHIFT(STB_TEXTEDIT_K_UP)) { if (_cursorRow == _scroll) { updateScroll(); } - } else if (key == STB_TEXTEDIT_K_DOWN) { + } else { int pageRows = _height / _charHeight; if (_cursorRow - _scroll > pageRows) { updateScroll(); } - } else { - updateScroll(); } findMatchingBrace(); return true; @@ -621,6 +683,10 @@ bool TextEditInput::find(const char *word, bool next) { return result; } +int *TextEditInput::getMarkers() { + return g_lineMarker; +} + void TextEditInput::gotoLine(const char *buffer) { if (_buf._buffer != NULL && buffer != NULL) { setCursorRow(atoi(buffer) - 1); @@ -678,7 +744,9 @@ void TextEditInput::setCursorRow(int row) { } void TextEditInput::clicked(int x, int y, bool pressed) { - if (pressed) { + if (x < _marginWidth) { + _ptY = -1; + } else if (pressed) { stb_textedit_click(&_buf, &_state, x - _marginWidth, y + (_scroll * _charHeight)); } } @@ -705,10 +773,17 @@ bool TextEditInput::updateUI(var_p_t form, var_p_t field) { } bool TextEditInput::selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw) { - stb_textedit_drag(&_buf, &_state, pt.x - _marginWidth, - pt.y + scrollY + (_scroll * _charHeight)); - redraw = true; - return 1; + bool focus = hasFocus(); + if (focus) { + if (pt.x < _marginWidth) { + dragPage(pt.y, redraw); + } else { + stb_textedit_drag(&_buf, &_state, pt.x - _marginWidth, + pt.y + scrollY + (_scroll * _charHeight)); + redraw = true; + } + } + return focus; } char *TextEditInput::copy(bool cut) { @@ -821,7 +896,7 @@ void TextEditInput::drawLineNumber(int x, int y, int row, bool selected) { if (_marginWidth > 0) { bool markerRow = false; for (int i = 0; i < MAX_MARKERS && !markerRow; i++) { - if (row == _lineMarker[i]) { + if (row == g_lineMarker[i]) { markerRow = true; } } @@ -998,6 +1073,22 @@ void TextEditInput::findMatchingBrace() { _matchingBrace = pair; } +int TextEditInput::getCompletions(StringList *list, int max) { + int count = 0; + char *selection = getWordBeforeCursor(); + unsigned len = selection != NULL ? strlen(selection) : 0; + if (len > 0) { + for (int i = 0; i < keyword_help_len && count < max; i++) { + if (strncasecmp(selection, keyword_help[i].keyword, len) == 0) { + list->add(keyword_help[i].keyword); + count++; + } + } + } + free(selection); + return count; +} + int TextEditInput::getCursorRow() const { StbTexteditRow r; int len = _buf._len; @@ -1149,22 +1240,22 @@ void TextEditInput::gotoNextMarker() { int next = 0; int first = -1; for (int i = 0; i < MAX_MARKERS; i++) { - if (_lineMarker[i] != -1) { + if (g_lineMarker[i] != -1) { if (first == -1) { first = i; } - if (_lineMarker[i] == _cursorLine) { + if (g_lineMarker[i] == _cursorLine) { next = i + 1 == MAX_MARKERS ? first : i + 1; break; } } } if (first != -1) { - if (_lineMarker[next] == -1) { + if (g_lineMarker[next] == -1) { next = first; } - if (_lineMarker[next] != -1) { - setCursorRow(_lineMarker[next] - 1); + if (g_lineMarker[next] != -1) { + setCursorRow(g_lineMarker[next] - 1); } } } @@ -1312,24 +1403,24 @@ void TextEditInput::setColor(SyntaxState &state) { void TextEditInput::toggleMarker() { bool found = false; for (int i = 0; i < MAX_MARKERS && !found; i++) { - if (_cursorLine == _lineMarker[i]) { - _lineMarker[i] = -1; + if (_cursorLine == g_lineMarker[i]) { + g_lineMarker[i] = -1; found = true; } } if (!found) { for (int i = 0; i < MAX_MARKERS && !found; i++) { - if (_lineMarker[i] == -1) { - _lineMarker[i] = _cursorLine; + if (g_lineMarker[i] == -1) { + g_lineMarker[i] = _cursorLine; found = true; break; } } } if (!found) { - _lineMarker[0] = _cursorLine; + g_lineMarker[0] = _cursorLine; } - qsort(_lineMarker, MAX_MARKERS, sizeof(int), compareIntegers); + qsort(g_lineMarker, MAX_MARKERS, sizeof(int), compareIntegers); } void TextEditInput::updateScroll() { @@ -1352,13 +1443,18 @@ int TextEditInput::wordStart() { // // TextEditHelpWidget // -TextEditHelpWidget::TextEditHelpWidget(TextEditInput *editor, int chW, int chH) : - TextEditInput(NULL, chW, chH, editor->_width - (chW * HELP_WIDTH), editor->_y, - chW * HELP_WIDTH, editor->_height), +TextEditHelpWidget::TextEditHelpWidget(TextEditInput *editor, int chW, int chH, bool overlay) : + TextEditInput(NULL, chW, chH, editor->_x, editor->_y, editor->_width, editor->_height), _mode(kNone), - _editor(editor) { - _theme = new EditTheme(0x73c990, 0x20242a); + _editor(editor), + _openPackage(NULL), + _openKeyword(-1) { + _theme = new EditTheme(HELP_BG, HELP_FG); hide(); + if (overlay) { + _x = editor->_width - (chW * HELP_WIDTH); + _width = chW * HELP_WIDTH; + } } TextEditHelpWidget::~TextEditHelpWidget() { @@ -1366,10 +1462,7 @@ TextEditHelpWidget::~TextEditHelpWidget() { } bool TextEditHelpWidget::closeOnEnter() const { - return (_mode != kSearch && - _mode != kKeyword && - _mode != kKeywordIndex && - _mode != kKeywordPackageIndex); + return (_mode != kSearch && _mode != kHelpKeyword); } bool TextEditHelpWidget::edit(int key, int screenWidth, int charWidth) { @@ -1425,22 +1518,12 @@ bool TextEditHelpWidget::edit(int key, int screenWidth, int charWidth) { } break; case SB_KEY_ENTER: - char *text; switch (_mode) { case kCompletion: completeWord(_state.cursor); break; - case kKeywordIndex: - createPackageIndex(); - break; - case kKeywordPackageIndex: - text = lineText(_state.cursor); - createKeywordHelp(text); - free(text); - break; - case kKeyword: - _mode = kHelp; - completeLine(0); + case kHelpKeyword: + toggleKeyword(); break; default: break; @@ -1448,6 +1531,9 @@ bool TextEditHelpWidget::edit(int key, int screenWidth, int charWidth) { result = true; break; default: + if (_mode == kHelpKeyword && _openKeyword != -1 && key < 0) { + result = TextEditInput::edit(key, screenWidth, charWidth); + } break; } } @@ -1474,18 +1560,47 @@ void TextEditHelpWidget::completeWord(int pos) { free(text); } +void TextEditHelpWidget::clicked(int x, int y, bool pressed) { + _ptY = -1; + if (pressed) { + stb_textedit_click(&_buf, &_state, 0, y + (_scroll * _charHeight)); + if (_mode == kHelpKeyword && x - _x <= _charWidth * 3) { + toggleKeyword(); + } + } +} + void TextEditHelpWidget::createCompletionHelp() { reset(kCompletion); char *selection = _editor->getWordBeforeCursor(); int len = selection != NULL ? 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); _buf.append("\n", 1); } } + const char *found = strstr(_editor->getText(), selection); + while (found != NULL) { + const char *end = found; + while (!IS_WHITE(*end) && !ispunct(*end) && *end != '\0') { + end++; + } + if (end - found > len) { + String next; + next.append(found, end - found); + if (!words.exists(next)) { + words.add(next); + _buf.append(found, end - found); + _buf.append("\n", 1); + } + } + found = strstr(found + len, selection); + } } else { const char *package = NULL; for (int i = 0; i < keyword_help_len; i++) { @@ -1513,19 +1628,30 @@ void TextEditHelpWidget::createHelp() { } void TextEditHelpWidget::createKeywordIndex() { - char *selection = _editor->getWordBeforeCursor(); - bool foundKeyword = false; - if (selection != NULL) { - foundKeyword = createKeywordHelp(selection); - free(selection); + char *keyword = _editor->getWordBeforeCursor(); + reset(kHelpKeyword); + + bool keywordFound = false; + if (keyword != NULL) { + for (int i = 0; i < keyword_help_len && !keywordFound; i++) { + if (strcasecmp(keyword, keyword_help[i].keyword) == 0) { + _buf.append(TWISTY2_OPEN, TWISTY2_LEN); + _buf.append(keyword_help[i].keyword); + _openPackage = keyword_help[i].package; + keywordFound = true; + toggleKeyword(); + break; + } + } + free(keyword); } - if (!foundKeyword) { - reset(kKeywordIndex); - _buf.append("[Help Index]\n"); + + if (!keywordFound) { const char *package = NULL; for (int i = 0; i < keyword_help_len; i++) { if (package == NULL || strcasecmp(package, keyword_help[i].package) != 0) { package = keyword_help[i].package; + _buf.append(TWISTY1_OPEN, TWISTY1_LEN); _buf.append(package); _buf.append("\n", 1); } @@ -1533,39 +1659,6 @@ void TextEditHelpWidget::createKeywordIndex() { } } -void TextEditHelpWidget::createPackageIndex() { - char *package = lineText(_state.cursor); - if (package[0] != '\0' && package[0] != '[') { - reset(kKeywordPackageIndex); - _buf.append("[", 1); - _buf.append(package); - _buf.append("]\n", 2); - for (int i = 0; i < keyword_help_len; i++) { - if (strcasecmp(package, keyword_help[i].package) == 0) { - _buf.append(keyword_help[i].keyword); - _buf.append("\n", 1); - } - } - } - free(package); -} - -bool TextEditHelpWidget::createKeywordHelp(const char *keyword) { - bool found = false; - for (int i = 0; i < keyword_help_len; i++) { - if (strcasecmp(keyword, keyword_help[i].keyword) == 0) { - reset(kKeyword); - _buf.append(keyword_help[i].signature); - _buf.append("\n\n", 2); - _buf.append(keyword_help[i].help); - _buf.append("\n", 1); - found = true; - break; - } - } - return found; -} - void TextEditHelpWidget::createOutline() { const char *text = _editor->getText(); int len = _editor->getTextLength(); @@ -1657,3 +1750,91 @@ void TextEditHelpWidget::reset(HelpMode mode) { _scroll = 0; _matchingBrace = -1; } + +bool TextEditHelpWidget::selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw) { + bool result = hasFocus(); + if (result) { + dragPage(pt.y, redraw); + } + return result; +} + +void TextEditHelpWidget::toggleKeyword() { + char *line = lineText(_state.cursor); + bool open1 = strncmp(line, TWISTY1_OPEN, TWISTY1_LEN) == 0; + bool open2 = strncmp(line, TWISTY2_OPEN, TWISTY2_LEN) == 0; + bool close1 = strncmp(line, TWISTY1_CLOSE, TWISTY1_LEN) == 0; + bool close2 = strncmp(line, TWISTY2_CLOSE, TWISTY2_LEN) == 0; + if (open1 || open2 || close1 || close2) { + const char *nextLine = line + TWISTY1_LEN; + const char *package = (open2 || close2) && _openPackage != NULL ? _openPackage : nextLine; + const char *nextPackage = NULL; + int pageRows = _height / _charHeight; + int open1Count = 0; + int open2Count = 0; + _buf.clear(); + _matchingBrace = -1; + _openKeyword = -1; + _state.select_start = _state.select_end = 0; + + for (int i = 0; i < keyword_help_len; i++) { + if (nextPackage == NULL || strcasecmp(nextPackage, keyword_help[i].package) != 0) { + nextPackage = keyword_help[i].package; + if (strcasecmp(package, nextPackage) == 0) { + // selected item + if (open1 || close1) { + _state.cursor = _buf._len; + _cursorRow = open1Count; + } + + _buf.append(open1 || open2 || close2 ? TWISTY1_CLOSE : TWISTY1_OPEN, TWISTY1_LEN); + _buf.append(nextPackage); + _buf.append("\n", 1); + + if (open1) { + _openPackage = nextPackage; + open1Count++; + } else if (open2) { + nextLine = line + TWISTY2_LEN; + open2Count++; + } + if (open1 || open2 || close2) { + while (i < keyword_help_len && + strcasecmp(nextPackage, keyword_help[i].package) == 0) { + open2Count++; + if (open2 && strcasecmp(nextLine, keyword_help[i].keyword) == 0) { + _openKeyword = i; + _state.cursor = _buf._len; + _cursorRow = open1Count + open2Count; + _buf.append(TWISTY2_CLOSE, TWISTY2_LEN); + _buf.append(keyword_help[i].keyword); + _buf.append("\n\n", 2); + _buf.append(keyword_help[i].signature); + _buf.append("\n\n", 2); + _buf.append(keyword_help[i].help); + _buf.append("\n\n", 2); + } else { + _buf.append(TWISTY2_OPEN, TWISTY2_LEN); + _buf.append(keyword_help[i].keyword); + _buf.append("\n", 1); + } + i++; + } + } + } else { + // next package item (level 1) + _buf.append(TWISTY1_OPEN, TWISTY1_LEN); + _buf.append(nextPackage); + _buf.append("\n", 1); + open1Count++; + } + } + } + if (_cursorRow + 4 < pageRows) { + _scroll = 0; + } else { + _scroll = _cursorRow - (pageRows / 4); + } + } + free(line); +} diff --git a/src/ui/textedit.h b/src/ui/textedit.h index adf39983..e77f8162 100644 --- a/src/ui/textedit.h +++ b/src/ui/textedit.h @@ -73,13 +73,14 @@ struct TextEditInput : public FormEditInput { void append(const char *text, int len) { _buf.append(text, len); } void completeWord(const char *word); + const char *completeKeyword(int index); void draw(int x, int y, int w, int h, int chw); bool edit(int key, int screenWidth, int charWidth); bool find(const char *word, bool next); int getCursorPos() const { return _state.cursor; } const char *getText() const { return _buf._buffer; } int getTextLength() const { return _buf._len; } - int *getMarkers() { return _lineMarker; } + int *getMarkers(); void gotoLine(const char *buffer); void reload(const char *text); bool save(const char *filePath); @@ -103,6 +104,7 @@ struct TextEditInput : public FormEditInput { void resize(int w, int h) { _width = w; _height = h; } char *getWordBeforeCursor(); bool replaceNext(const char *text); + int getCompletions(StringList *list, int max); protected: enum SyntaxState { @@ -114,6 +116,7 @@ struct TextEditInput : public FormEditInput { kDigit, }; + void dragPage(int y, bool &redraw); void drawText(int x, int y, const char *str, int length, SyntaxState &state); void changeCase(); void cycleTheme(); @@ -153,21 +156,19 @@ struct TextEditInput : public FormEditInput { int _cursorLine; int _indentLevel; int _matchingBrace; - int _lineMarker[MAX_MARKERS]; + int _ptY; bool _dirty; }; struct TextEditHelpWidget : public TextEditInput { - TextEditHelpWidget(TextEditInput *editor, int chW, int chH); + TextEditHelpWidget(TextEditInput *editor, int chW, int chH, bool overlay=true); virtual ~TextEditHelpWidget(); enum HelpMode { kNone, kHelp, + kHelpKeyword, kCompletion, - kKeyword, - kKeywordIndex, - kKeywordPackageIndex, kOutline, kSearch, kSearchReplace, @@ -177,6 +178,7 @@ struct TextEditHelpWidget : public TextEditInput { kMessage }; + void clicked(int x, int y, bool pressed); void createCompletionHelp(); void createGotoLine(); void createHelp(); @@ -185,7 +187,6 @@ struct TextEditHelpWidget : public TextEditInput { void createOutline(); void createSearch(bool replace); bool edit(int key, int screenWidth, int charWidth); - char *copy(bool cut) { return NULL; } void paste(const char *text) {} bool isDrawTop() { return true; } void resize(int w, int h) { _x = w - _width; _height = h; } @@ -193,16 +194,19 @@ struct TextEditHelpWidget : public TextEditInput { bool closeOnEnter() const; bool replaceMode() const { return _mode == kReplace; } bool replaceDoneMode() const { return _mode == kReplaceDone; } + bool selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw); + void toggleKeyword(); private: void completeLine(int pos); void completeWord(int pos); void createPackageIndex(); - bool createKeywordHelp(const char *keyword); HelpMode _mode; strlib::List _outline; TextEditInput *_editor; + const char *_openPackage; + int _openKeyword; }; #define STB_TEXTEDIT_STRING EditBuffer