diff --git a/ChangeLog b/ChangeLog index c2eb2fdb..32341d36 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2015-11-01 + Fix debugger launch in linux build + Fix editor display issue with keyword completion + Fix editor case insensitive search + Fix image drawing for large images + Fix PAINT infinite loop + Fix keyboard handling for non-us keymaps + Fix Ctrl+Home editor keystroke handler + Fix crash with online command if site is down + Fix over scroll issue with line number widget + Fix screen dump invalid file name for online files + Fix file manager .bas file case sensitivity + Fix editor markup on map fields that look like keywords + Fix scanner inserting line-no bytecode for empty or comment lines + Implemented editor F2 command to display online help + Implemented calling IMAGE with another image variable + 2015-10-20 Fix LET when assigning a value to a MAP/ARRAY field diff --git a/configure.ac b/configure.ac index 90f10299..57be12e6 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ 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.12.0]) +AC_INIT([smallbasic], [0.12.1]) AC_CONFIG_SRCDIR([configure.ac]) AC_CANONICAL_TARGET diff --git a/debian/changelog b/debian/changelog index b786966a..3f383628 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,13 +1,18 @@ +smallbasic (0.12.1) unstable; urgency=low + * Various - see web site + + -- Chris Warren-Smith Sat, 17 Nov 2015 09:45:25 +1000 + smallbasic (0.12.0) unstable; urgency=low * Various - see web site - -- Chris Warren-Smith Sat, 17 Sept 2015 09:45:25 +1000 + -- Chris Warren-Smith Sat, 17 Sep 2015 09:45:25 +1000 smallbasic (0.11.17) unstable; urgency=low * Fix compiler ignoring unused assignment values * Implement IMAGE - -- Chris Warren-Smith Sat, 4 October 2014 09:45:25 +1000 + -- Chris Warren-Smith Sat, 4 Oct 2014 09:45:25 +1000 smallbasic (0.11.16) unstable; urgency=low * Add support for unary operators on array elements @@ -15,42 +20,42 @@ smallbasic (0.11.16) unstable; urgency=low * Fix INKEY Backspace in FLTK * Fix FOR/NEXT using float increments - -- Chris Warren-Smith Sat, 13 Sept 2014 09:45:25 +1000 + -- Chris Warren-Smith Sat, 13 Sep 2014 09:45:25 +1000 smallbasic (0.11.15) unstable; urgency=low * RTE renamed THROW - -- Chris Warren-Smith Sat, 6 Sept 2014 09:45:25 +1000 + -- Chris Warren-Smith Sat, 6 Sep 2014 09:45:25 +1000 smallbasic (0.11.14) unstable; urgency=low * Added reference variable type - -- Chris Warren-Smith Sat, 30 August 2014 09:45:25 +1000 + -- Chris Warren-Smith Sat, 30 Aug 2014 09:45:25 +1000 smallbasic (0.11.13) unstable; urgency=low * Fixed UDS handling - -- Chris Warren-Smith Sun, 24 August 2014 09:45:25 +1000 + -- Chris Warren-Smith Sun, 24 Aug 2014 09:45:25 +1000 smallbasic (0.11.12) unstable; urgency=low * Fixed/implemented INCLUDE command - -- Chris Warren-Smith Sat, 11 August 2014 09:45:25 +1000 + -- Chris Warren-Smith Sat, 11 Aug 2014 09:45:25 +1000 smallbasic (0.11.11) unstable; urgency=low * Built using common code from the android project and SDL - -- Chris Warren-Smith Sat, 19 July 2014 09:45:25 +1000 + -- Chris Warren-Smith Sat, 19 Jul 2014 09:45:25 +1000 smallbasic (0.11.10) unstable; urgency=low * Built using common code from the android project and SDL - -- Chris Warren-Smith Sat, 19 July 2014 09:45:25 +1000 + -- Chris Warren-Smith Sat, 19 Jul 2014 09:45:25 +1000 smallbasic (0.10.8) unstable; urgency=low * Fix misc issues with const char* uncovered with new gcc - -- Chris Warren-Smith Sat, 31 July 2010 09:45:25 +1000 + -- Chris Warren-Smith Sat, 31 Jul 2010 09:45:25 +1000 smallbasic (0.10.7) unstable; urgency=low * Added defineKey and fixed delay problems with the inkey command diff --git a/debian/rules b/debian/rules index 664b5148..dfe3c09f 100755 --- a/debian/rules +++ b/debian/rules @@ -74,6 +74,7 @@ binary-arch: build install 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 cd3342f1..103b1c90 100644 --- a/documentation/build_kwp.cpp +++ b/documentation/build_kwp.cpp @@ -240,14 +240,15 @@ int main(int argc, char *argv[]) { fprintf(stdout, " const char *package;\n"); fprintf(stdout, " const char *keyword;\n"); fprintf(stdout, " const char *signature;\n"); + fprintf(stdout, " const char *nodeId;\n"); fprintf(stdout, " const char *help;\n"); fprintf(stdout, "} keyword_help[] = {\n"); int max_keyword_len = 0; List_each(HelpItem *, it, helpItems) { HelpItem *item = (*it); - fprintf(stdout, "{\"%s\",\"%s\",\"%s\",\"%s\"},\n", item->package, - item->keyword, item->signature, item->help); + fprintf(stdout, "{\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"},\n", item->package, + item->keyword, item->signature, item->id, item->help); int len = strlen(item->keyword); if (len > max_keyword_len) { max_keyword_len = len; diff --git a/documentation/sbasic_ref.csv b/documentation/sbasic_ref.csv index 4f2f1e5a..dba85357 100644 --- a/documentation/sbasic_ref.csv +++ b/documentation/sbasic_ref.csv @@ -32,9 +32,9 @@ Console,command,PLAY,534,"PLAY string","Play musical notes." Console,command,PRINT,535,"PRINT [USING [format];] [expr|str [,|; [expr|str]] ...","Display text or the value of an expression." Console,command,SOUND,536,"SOUND freq, dur_ms [, vol] [BG]","Plays a sound." Console,function,CAT,538,"CAT (x)","Returns a console code. 0 = reset, 1 = bold, -1 bold-off, 2 = underline, -2 = underline-off, 3 = reverse, -3 = reverse-off." -Console,function,INKEY,539,"INKEY","Returns the last key-code in keyboard buffer, or an empty string if there are no keys. Special key-codes like the function-keys (PC) or the hardware-buttons (PalmOS) are returned as 2-byte string." +Console,function,INKEY,539,"INKEY","Returns the last key-code in keyboard buffer, or an empty string if there are no keys. Special key-codes like the function-keys are returned as 2-byte string." Console,function,TAB,540,"TAB (n)","Moves cursor position to the nth column." -Console,function,DEFINEKEY,1015,"DEFINEKEY k,sub","Binds a keystoke to a user defined function" +Console,function,DEFINEKEY,1015,"DEFINEKEY k,sub","Binds a keystroke to a user defined function" Data,command,DELETE,542,"DELETE a, idx [, count]","Deletes 'count' elements at position 'idx' of array 'a'." Data,command,EMPTY,543,"EMPTY (x)","Returns true if x is: a zero length array, an empty string, an integer or real with the value 0." Data,command,INSERT,544,"INSERT a, idx, val [, val [, ...]]]","Inserts the values to the specified array at the position idx." @@ -66,7 +66,7 @@ Data,statement,RESTORE,572,"RESTORE label","Specifies the position of the next d Date,command,DATEDMY,573,"DATEDMY dmy| julian_date, BYREF d, BYREF m, BYREF y","Returns the day, month and the year as integers." Date,command,TIMEHMS,574,"TIMEHMS hms| timer, BYREF h, BYREF m, BYREF s","Converts a time-value to hours, minutes and seconds integer values." Date,function,DATE,575,"DATE","Returns the current date as string ""DD/MM/YYYY""." -Date,function,DATEFMT,576,"DATEFMT (format, dmy| (d,m,y)| julian_date)","Returns formated date string." +Date,function,DATEFMT,576,"DATEFMT (format, dmy| (d,m,y)| julian_date)","Returns formatted date string." Date,function,JULIAN,577,"JULIAN (dmy| (d,m,y))","Returns the Julian date. (dates must be greater than 1/1/100 AD)." Date,function,TIME,578,"TIME","Returns the current time as string ""HH:MM:SS""." Date,function,WEEKDAY,579,"WEEKDAY (dmy| (d,m,y)| julian_date)","Returns the day of the week (0 = Sunday)." @@ -75,7 +75,7 @@ Data,command,APPEND,581,"APPEND a, val [, val [, ...]]","Inserts the values at t File,command,BLOAD,582,"BLOAD filename[, address]","Loads a specified memory image file into memory." File,command,BPUTC,583,"BPUTC #fileN; byte","Writes a byte on file or device. (Binary mode)." File,command,BSAVE,584,"BSAVE filename, address, length","Copies a specified portion of memory to a specified file." -File,command,CHDIR,585,"CHDIR dir","Changes the current working directory. Not supported on PalmOS version." +File,command,CHDIR,585,"CHDIR dir","Changes the current working directory." File,command,CHMOD,586,"CHMOD file, mode","Change permissions of a file. See also ACCESS." File,command,CLOSE,587,"CLOSE #fileN","Close a file or device." File,command,COPY,588,"COPY ""file"", ""newfile""","Makes a copy of specified file to the 'newfile'." @@ -83,10 +83,10 @@ File,command,DIRWALK,589,"DIRWALK directory [, wildcards] [USE ...]","Walk throu File,command,INPUT,590,"INPUT #fileN; var1 [,delim] [, var2 [,delim]] ...","Reads data from file." File,command,KILL,591,"KILL ""file""","Deletes the specified file." File,command,LOCK,592,"LOCK","Lock a record or an area (not yet implemented)." -File,command,MKDIR,593,"MKDIR dir","Create a directory. Not supported on PalmOS version." +File,command,MKDIR,593,"MKDIR dir","Create a directory." File,command,OPEN,594,"OPEN file [FOR {INPUT|OUTPUT|APPEND}] AS #fileN","Makes a file or device available for sequential input, sequential output." File,command,RENAME,595,"RENAME ""file"", ""newname""","Renames the specified file." -File,command,RMDIR,596,"RMDIR dir","Removes a directory. Not supported on PalmOS version." +File,command,RMDIR,596,"RMDIR dir","Removes a directory." File,command,SEEK,597,"SEEK #fileN; pos","Sets file position for the next read/write." File,command,TLOAD,598,"TLOAD file, BYREF var [, type]","Loads a text file into array variable. Each text-line is an array element. type 0 = load into array (default), 1 = load into string." File,command,TSAVE,599,"TSAVE file, var","Writes an array to a text file. Each array element is a text-line." @@ -107,14 +107,14 @@ Graphics,command,CIRCLE,613,"CIRCLE [STEP] x,y,r [,aspect [, color]] [COLOR colo Graphics,command,COLOR,614,"COLOR foreground-color [, background-color]","Specifies the foreground and background colors." Graphics,command,DRAW,615,"DRAW ""commands""","Draw lines as specified by the given directional commands. " Graphics,command,DRAWPOLY,616,"DRAWPOLY array [,x-origin,y-origin [, scalef [, color]]] [COLOR color] [FILLED]","Draws a polyline. " -Graphics,command,IMAGE,617,"IMAGE #handle, index, x, y [,sx,sy [,w,h]]","Display a graphical image." +Graphics,command,IMAGE,617,"IMAGE [#handle | fileName | http://path-to-file.png | image-var | array of pixmap data]","Creates a graphical image object providing access to the following sub-commands: show([x,y [,zindex [,opacity]]]), hide, save([x,y [,w,h]])" Graphics,command,LINE,618,"LINE [STEP] x,y [,|STEP x2,y2] [, color| COLOR color]","Draws a line." Graphics,command,PAINT,619,"PAINT [STEP] x, y [,fill-color [,border-color]]","Fills an enclosed area on the graphics screen with a specific color. x,y = Screen coordinate (column, row) within the area that is to be filled." Graphics,command,PLOT,620,"PLOT xmin, xmax USE f(x)","Graph of f(x)." Graphics,command,PSET,621,"PSET [STEP] x,y [, color| COLOR color]","Draw a pixel." Graphics,command,RECT,622,"RECT [STEP] x,y [,|STEP x2,y2] [, color| COLOR color] [FILLED]","Draws a rectangular parallelogram." Graphics,command,VIEW,623,"VIEW [x1,y1,x2,y2 [,color [,border-color]]]","Defines a viewport. The viewport defined by VIEW is disabled by a VIEW command with no parameters." -Graphics,command,WINDOW,624,"WINDOW [x1,y1,x2,y2]","The WINDOW command allows you to redefine the corners of the display screen as a pair of ""world"" coordinates." +Graphics,command,WINDOW,624,"WINDOW [left,bottom,right,top]","The WINDOW command allows you to redefine the corners of the display screen as a pair of ""world"" coordinates. WINDOW is also overloaded as a function, returning a system object providing access to the following sub-commands: graphicsScreen1, graphicsScreen2, textScreen, alert, ask, menu, message, showKeypad, insetTextScreen" Graphics,function,PEN,627,"PEN (0..14)","Returns the PEN/MOUSE data." Graphics,function,POINT,628,"POINT (x [, y])","Returns the color of the pixel at x,y." Graphics,function,RGB,629,"RGB (r, g, b)","Returns the RGB color codes for the specified values. Takes values 0..255 for each of the color." diff --git a/ide/android/AndroidManifest.xml b/ide/android/AndroidManifest.xml index 861df1f7..2500765e 100644 --- a/ide/android/AndroidManifest.xml +++ b/ide/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="16" + android:versionName="0.12.1"> diff --git a/ide/android/assets/main.bas b/ide/android/assets/main.bas index 9b8d33b5..c850399c 100644 --- a/ide/android/assets/main.bas +++ b/ide/android/assets/main.bas @@ -55,7 +55,7 @@ sub do_about() print "(_ ._ _ _.|||_) /\ (_ |/ " print "__)| | |(_||||_)/--\__)|\_" print - print "Version 0.12.0" + print "Version 0.12.1" print print "Copyright (c) 2002-2015 Chris Warren-Smith" print "Copyright (c) 1999-2006 Nic Christopoulos" + chr(10) @@ -183,7 +183,7 @@ sub listFiles(byref frm, path, byref basList, byref dirList) name = ent if (isdir(path + name)) then dirList << name - else if (right(ent, 4) == ".bas") then + else if (lower(right(ent, 4)) == ".bas") then basList << name endif next ent diff --git a/src/common/ffill.c b/src/common/ffill.c index 04443359..9ef8f5c4 100644 --- a/src/common/ffill.c +++ b/src/common/ffill.c @@ -11,50 +11,45 @@ #include "common/sys.h" #include "common/device.h" -#define QUEUESIZE 2048 +#define QUEUESIZE 2048 +#define UP 1 +#define DN -1 +#define FILLED 1 +#define NOT_FILLED 0 +#define SCAN_UNTIL 0 +#define SCAN_WHILE 1 struct tagParams { - word xl; /* leftmost pixel in run */ - word xr; /* rightmost pixel in run */ - int16_t y; /* y-coordinate of run */ - byte f; /* TRUE if run is filled (blocked) */ + word xl; // leftmost pixel in run + word xr; // rightmost pixel in run + int16_t y; // y-coordinate of run + byte f; // TRUE if run is filled (blocked) }; struct tagQUEUE { - struct tagQUEUE *pQ; /* pointer to opposite queue */ - int d; /* direction (UP or DN) */ - int n; /* number of unfilled runs in queue */ - int h; /* index of head of queue */ - int t; /* index of tail of queue */ + struct tagQUEUE *pQ; // pointer to opposite queue + int d; // direction (UP or DN) + int n; // number of unfilled runs in queue + int h; // index of head of queue + int t; // index of tail of queue struct tagParams *run; }; typedef struct tagQUEUE QUEUE; -#define UP 1 -#define DN -1 -#define FILLED 1 -#define NOT_FILLED 0 -#define SCAN_UNTIL 0 -#define SCAN_WHILE 1 - -word ff_scan_left(word, word, long, int); -word ff_scan_right(word, word, long, int); -word ff_next_branch(word, word, int); -void ff_add_queue(QUEUE *, word, word, int, int); -int ff_in_queue(QUEUE *, word, word, int); - +// Global variables of the module. static struct tagParams ff_buf1[QUEUESIZE]; static struct tagParams ff_buf2[QUEUESIZE]; - -/* Global variables of the module. */ static long ucBorder, ucFill; static QUEUE Qup; static QUEUE Qdn; static int scan_type; -/* - * fill - */ +word ff_scan_left(word, word, long, int); +word ff_scan_right(word, word, long, int); +word ff_next_branch(word, word, int); +void ff_add_queue(QUEUE *, word, word, int, int); +int ff_in_queue(QUEUE *, word, word, int); + void dev_ffill(word x0, word y0, long fill_color, long border_color) { int y = y0, qp; int bChangeDirection; @@ -62,36 +57,45 @@ void dev_ffill(word x0, word y0, long fill_color, long border_color) { QUEUE *Q; long pcolor; + // reset all globals + memset(ff_buf1, 0, sizeof(ff_buf1)); + memset(ff_buf2, 0, sizeof(ff_buf2)); + memset(&Qup, 0, sizeof(QUEUE)); + memset(&Qdn, 0, sizeof(QUEUE)); + ucBorder = 0; + ucFill = 0; + scan_type = 0; + pcolor = dev_fgcolor; dev_setcolor(fill_color); - /* - * do nothing if the seed pixel is a border pixel - */ + + // do nothing if the seed pixel is a border pixel if (border_color == -1) { border_color = dev_getpixel(x0, y0); scan_type = SCAN_WHILE; - } else + } else { scan_type = SCAN_UNTIL; - + } if (scan_type == SCAN_UNTIL) { - if (dev_getpixel(x0, y0) == border_color) + if (dev_getpixel(x0, y0) == border_color) { return; + } } else { - if (dev_getpixel(x0, y0) == fill_color) + if (dev_getpixel(x0, y0) == fill_color) { return; + } } + Qup.run = ff_buf1; Qdn.run = ff_buf2; - /* - * save the border and fill values in global variables - */ + + // save the border and fill values in global variables ucBorder = border_color; ucFill = fill_color; - /* - * initialize the queues - */ - Qup.pQ = &Qdn; /* pointer to opposite queue */ - Qup.d = UP; /* direction for queue */ + + // initialize the queues + Qup.pQ = &Qdn; // pointer to opposite queue + Qup.d = UP; // direction for queue Qup.h = -1; Qup.t = 0; @@ -100,139 +104,111 @@ void dev_ffill(word x0, word y0, long fill_color, long border_color) { Qdn.h = -1; Qdn.t = 0; - /* - * put the seed run in the up queue - */ + // put the seed run in the up queue Q = &Qup; - ff_add_queue(Q, ff_scan_left(x0, y0, ucBorder, scan_type), ff_scan_right(x0, y0, ucBorder, scan_type), y0, - NOT_FILLED); + ff_add_queue(Q, ff_scan_left(x0, y0, ucBorder, scan_type), + ff_scan_right(x0, y0, ucBorder, scan_type), y0, NOT_FILLED); - /* - * main loop - */ + // main loop for (;;) { if (dev_events(0) < 0) { break; } - /* - * if the queue is empty, switch queues - */ + // if the queue is empty, switch queues if (Q->n == 0) { Q = Q->pQ; - /* - * exit if the other queue is empty - */ - if (Q->n == 0) + + // exit if the other queue is empty + if (Q->n == 0) { break; + } } - /* - * get the first non-filled run from the head of the queue - */ + + // get the first non-filled run from the head of the queue qp = Q->h; while (qp >= Q->t) { if (!Q->run[qp].f) { y = Q->run[qp].y; xl = Q->run[qp].xl; xr = Q->run[qp].xr; - /* - * fill the run - */ + + // fill the run dev_line(xl, y, xr, y); Q->run[qp].f = FILLED; Q->n--; break; - } else + } else { qp--; + } } - /* - * remove previously-filled runs from the current queue - */ - if (Q->d == UP) - while ((Q->h > qp) && (Q->run[Q->h].y < (y - 1))) + + // remove previously-filled runs from the current queue + if (Q->d == UP) { + while ((Q->h > qp) && (Q->run[Q->h].y < (y - 1))) { Q->h--; - else - while ((Q->h > qp) && (Q->run[Q->h].y > (y + 1))) + } + } else { + while ((Q->h > qp) && (Q->run[Q->h].y > (y + 1))) { Q->h--; + } + } - /* - * branch in the current direction - */ + // branch in the current direction xln = ff_next_branch(xl, xr, y + Q->d); while (xln != 0xFFFF) { - /* - * determine the starting point for ScanRight - */ + // determine the starting point for ScanRight x = (xln > xl) ? xln : xl; - /* - * add the new branch if it's not already in the queue - */ + // add the new branch if it's not already in the queue xrn = ff_scan_right(x, y + Q->d, ucBorder, scan_type); - if (!ff_in_queue(Q, xln, xrn, y + Q->d)) + if (!ff_in_queue(Q, xln, xrn, y + Q->d)) { ff_add_queue(Q, xln, xrn, y + Q->d, NOT_FILLED); + } - /* - * if the new branch does not extend beyond the current run, - */ - /* - * look for another branch - */ - if (xrn > (xr - 2)) + // if the new branch does not extend beyond the current run, + // look for another branch + if (xrn > (xr - 2)) { break; + } else { x = xrn + 2; xln = ff_next_branch(x, xr, y + Q->d); } } - /* - * branch in the opposite direction - */ + // branch in the opposite direction bChangeDirection = 0; xln = ff_next_branch(xl, xr, y - Q->d); while (xln != 0xFFFF) { - /* - * determine the starting point for ScanRight - */ + // determine the starting point for ScanRight x = (xln > xl) ? xln : xl; - /* - * add the new branch to the opposite queue if it is not - */ - /* - * already in the current queue - */ + // add the new branch to the opposite queue if it is not + // already in the current queue xrn = ff_scan_right(x, y - Q->d, ucBorder, scan_type); if (!ff_in_queue(Q, xln, xrn, y - Q->d)) { ff_add_queue(Q->pQ, xln, xrn, y - Q->d, NOT_FILLED); bChangeDirection = 1; } - /* - * if the new branch does not extend beyond the current run, - */ - /* - * look for another branch - */ - if (xrn > (xr - 2)) + + // if the new branch does not extend beyond the current run, + // look for another branch + if (xrn > (xr - 2)) { break; + } else { x = xrn + 2; xln = ff_next_branch(x, xr, y - Q->d); } } - /* - * change direction if any turns were detected - */ + + // change direction if any turns were detected if (bChangeDirection) { - /* - * select the opposite queue - */ + // select the opposite queue Q = Q->pQ; - /* - * copy the current run to the opposite queue - */ + // copy the current run to the opposite queue ff_add_queue(Q, xl, xr, y, FILLED); } } @@ -241,91 +217,73 @@ void dev_ffill(word x0, word y0, long fill_color, long border_color) { dev_setcolor(pcolor); } -/* - */ word ff_scan_left(word xl, word y, long ucPixel, int f) { long v; if (f == SCAN_UNTIL) { - /* - * return 0xFFFF if starting pixel is a border pixel - */ - if (dev_getpixel(xl, y) == ucPixel) + // return 0xFFFF if starting pixel is a border pixel + if (dev_getpixel(xl, y) == ucPixel) { return 0xFFFF; + } do { xl--; - - /* - * clip - */ - if (xl == 0xFFFF || xl < dev_Vx1) + // clip + if (xl == 0xFFFF || xl < dev_Vx1) { break; - + } v = dev_getpixel(xl, y); } while (v != ucPixel); - } else { /* f == SCAN_WHILE */ - /* - * return 0xFFFF if starting pixel is not a border pixel - */ - if (dev_getpixel(xl, y) != ucPixel) + } else { + // f == SCAN_WHILE + // return 0xFFFF if starting pixel is not a border pixel + if (dev_getpixel(xl, y) != ucPixel) { return 0xFFFF; + } do { xl--; - - /* - * clip - */ - if (xl == 0xFFFF || xl < dev_Vx1) + // clip + if (xl == 0xFFFF || xl < dev_Vx1) { break; - + } v = dev_getpixel(xl, y); } while (v == ucPixel); } - return ++xl; } -/* - */ word ff_scan_right(word xr, word y, long ucPixel, int f) { long v; if (f == SCAN_UNTIL) { - /* - * return 0xFFFF if starting pixel is a border pixel - */ - if (dev_getpixel(xr, y) == ucPixel) + // return 0xFFFF if starting pixel is a border pixel + if (dev_getpixel(xr, y) == ucPixel) { return 0xFFFF; + } do { xr++; - /* - * clip - */ - if (xr > dev_Vx2) + // clip + if (xr > dev_Vx2) { break; - + } v = dev_getpixel(xr, y); } while (v != ucPixel); - } else { /* f == SCAN_WHILE */ - /* - * return 0xFFFF if starting pixel is not a border pixel - */ - if (dev_getpixel(xr, y) != ucPixel) + } else { + // f == SCAN_WHILE + // return 0xFFFF if starting pixel is not a border pixel + if (dev_getpixel(xr, y) != ucPixel) { return 0xFFFF; + } do { xr++; - - /* - * clip - */ - if (xr > dev_Vx2) + // clip + if (xr > dev_Vx2) { break; - + } v = dev_getpixel(xr, y); } while (v == ucPixel); } @@ -333,118 +291,93 @@ word ff_scan_right(word xr, word y, long ucPixel, int f) { return --xr; } -/* - */ word ff_next_branch(word xl, word xr, int y) { word xln; - /* - * clip y - */ - if ((y < dev_Vy1) || (y > dev_Vy2)) + // clip y + if ((y < dev_Vy1) || (y > dev_Vy2)) { return 0xFFFF; + } - /* - * look for a branch adjacent to the first pixel in the run - */ + // look for a branch adjacent to the first pixel in the run xln = ff_scan_left(xl, y, ucBorder, scan_type); - /* - * if the adjacent pixel is on the border, scan for the - */ - /* - * first non-border pixel - */ + // if the adjacent pixel is on the border, scan for the + // first non-border pixel if (xln == 0xFFFF) { xln = ff_scan_right(xl, y, ucBorder, (scan_type == SCAN_WHILE) ? SCAN_UNTIL : SCAN_WHILE); - if (xln < xr) + if (xln < xr) { xln++; - else + } else { xln = 0xFFFF; + } } return xln; } -/* - */ -void ff_add_queue(QUEUE * Q, word xl, word xr, int y, int f) { +void ff_add_queue(QUEUE *Q, word xl, word xr, int y, int f) { int qp; int i; if (Q->d == UP) { - /* - * find the last larger queue y-value - */ - for (qp = Q->t; qp <= Q->h; qp++) - if (Q->run[qp].y <= y) + // find the last larger queue y-value + for (qp = Q->t; qp <= Q->h; qp++) { + if (Q->run[qp].y <= y) { break; + } + } } else { - /* - * find the last smaller queue y-value - */ - for (qp = Q->t; qp <= Q->h; qp++) - if (Q->run[qp].y >= y) + // find the last smaller queue y-value + for (qp = Q->t; qp <= Q->h; qp++) { + if (Q->run[qp].y >= y) { break; + } + } } - /* - * expand the queue if necessary - */ + // expand the queue if necessary if (qp <= Q->h) { - /* - * update the head pointer - */ + // update the head pointer Q->h++; - /* - * expand up - */ + // expand up for (i = Q->h; i > qp; --i) { Q->run[i].xl = Q->run[i - 1].xl; Q->run[i].xr = Q->run[i - 1].xr; Q->run[i].y = Q->run[i - 1].y; Q->run[i].f = Q->run[i - 1].f; } - } else - /* can't insert in queue; add to head */ + } else { + // can't insert in queue; add to head Q->h++; + } - /* - * enter the data in the queue - */ + // enter the data in the queue Q->run[qp].xl = xl; Q->run[qp].xr = xr; Q->run[qp].y = y; Q->run[qp].f = f; - /* - * update the number of unfilled runs in the queue - */ - if (!f) + // update the number of unfilled runs in the queue + if (!f) { Q->n++; + } } -/* - */ -int ff_in_queue(QUEUE * Q, word xl, word xr, int y) { +int ff_in_queue(QUEUE *Q, word xl, word xr, int y) { int qp; int bRVal = 0; if (Q->d == UP) { - /* - * search from head to tail - */ + // search from head to tail for (qp = Q->h; qp >= 0; --qp) { - /* - * quit searching if y is not in the ordered list - */ - if (Q->run[qp].y > y) + // quit searching if y is not in the ordered list + if (Q->run[qp].y > y) { break; + } - /* - * quit searching if the specified run is in the list - */ + // quit searching if the specified run is in the list if ((Q->run[qp].y == y) && (Q->run[qp].xl == xl) && (Q->run[qp].xr == xr)) { bRVal = 1; break; @@ -453,19 +386,14 @@ int ff_in_queue(QUEUE * Q, word xl, word xr, int y) { } else { - /* - * search from head to tail - */ + // search from head to tail for (qp = Q->h; qp >= 0; --qp) { - /* - * quit searching if y is not in the ordered list - */ - if (Q->run[qp].y < y) + // quit searching if y is not in the ordered list + if (Q->run[qp].y < y) { break; + } - /* - * quit searching if the specified run is in the list - */ + // quit searching if the specified run is in the list if ((Q->run[qp].y == y) && (Q->run[qp].xl == xl) && (Q->run[qp].xr == xr)) { bRVal = 1; break; @@ -475,3 +403,4 @@ int ff_in_queue(QUEUE * Q, word xl, word xr, int y) { return bRVal; } + diff --git a/src/common/fs_socket_client.c b/src/common/fs_socket_client.c index 2e5cfe1a..c47f6be5 100644 --- a/src/common/fs_socket_client.c +++ b/src/common/fs_socket_client.c @@ -18,7 +18,7 @@ int sockcl_open(dev_file_t *f) { int port; char server[129]; - // open "SOCL:smallbasic.sf.net:80" as #1 + // open "SOCL:smallbasic.sf.net:80" as #1 // open "SOCL:80" as #2 f->drv_dw[0] = 1; p = strchr(f->name + 5, ':'); @@ -132,7 +132,10 @@ int http_read(dev_file_t *f, var_t *var_p) { while (1) { int bytes = net_read(f->handle, (char *) rxbuff, sizeof(rxbuff)); - if (bytes == 0) { + if (bytes == -1) { + httpOK = 0; + break; + } else if (bytes == 0) { break; // no more data } // assumes http header < 1024 bytes diff --git a/src/common/keymap.h b/src/common/keymap.h index b4cb7cb5..222456fb 100644 --- a/src/common/keymap.h +++ b/src/common/keymap.h @@ -51,10 +51,11 @@ extern "C" { #define SB_KEY_SF(x) (0xFFE0+(x)) // Control & Alt keys (parameter = capital character) -#define SB_KEY_CTRL(c) (0xF1000000 + (c)) -#define SB_KEY_ALT(c) (0xF2000000 + (c)) -#define SB_KEY_CTRL_ALT(c) (0xF4000000 + (c)) -#define SB_KEY_SHIFT(c) (0xF8000000 + (c)) +#define SB_KEY_CTRL(c) (0xF1000000 + (c)) +#define SB_KEY_ALT(c) (0xF2000000 + (c)) +#define SB_KEY_CTRL_ALT(c) (0xF3000000 + (c)) +#define SB_KEY_SHIFT(c) (0xF4000000 + (c)) +#define SB_KEY_SHIFT_CTRL(c) (0xF5000000 + (c)) // keypad #define SB_KEY_KP_DIV 0xFFDA diff --git a/src/common/scan.c b/src/common/scan.c index 2a2cda04..bfcb1834 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -20,7 +20,7 @@ #include "languages/keywords.en.c" char *comp_array_uds_field(char *p, bc_t *bc); -void comp_text_line(char *text); +void comp_text_line(char *text, int addLineNo); bcip_t comp_search_bc(bcip_t ip, code_t code); extern void expr_parser(bc_t *bc); extern void sc_raise2(const char *fmt, int line, const char *buff); // sberr @@ -1332,7 +1332,7 @@ void comp_expression(char *expr, byte no_parser) { // do additional steps if (kw_exec_more) { - comp_text_line(comp_bc_tmp2); + comp_text_line(comp_bc_tmp2, 0); } } @@ -1589,7 +1589,7 @@ int comp_single_line_if(char *text) { *pelse = '\0'; // scan the commands before ELSE - comp_text_line(buf); + comp_text_line(buf, 0); // add EOC bc_eoc(&comp_prog); @@ -1612,8 +1612,8 @@ int comp_single_line_if(char *text) { } } while (pelse != NULL); } - // scan the rest commands - comp_text_line(buf); + // scan the remaining commands + comp_text_line(buf, 0); // add EOC bc_eoc(&comp_prog); @@ -2081,7 +2081,7 @@ void comp_text_line_func(long idx, int decl) { char *macro = malloc(SB_SOURCELINE_SIZE + 1); sprintf(macro, "%s=%s:%s", pname, eq_ptr, LCN_END); // run comp_text_line again - comp_text_line(macro); + comp_text_line(macro, 0); free(macro); } else { sc_raise(MSG_MISSING_UDP_BODY); @@ -2334,9 +2334,7 @@ int comp_text_line_command(long idx, int decl, int sharp, char *last_cmd) { comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE); if (strlen(vname) != strlen(pars[i])) { // kwTYPE_LINE is required for executor - bc_add_code(&comp_prog, kwTYPE_LINE); - bc_add_addr(&comp_prog, comp_line); - comp_text_line(pars[i]); + comp_text_line(pars[i], 1); } } break; @@ -2553,7 +2551,7 @@ int comp_text_line_command(long idx, int decl, int sharp, char *last_cmd) { /* * Pass 1: scan source line */ -void comp_text_line(char *text) { +void comp_text_line(char *text, int addLineNo) { long idx; int decl = 0; @@ -2566,7 +2564,7 @@ void comp_text_line(char *text) { // EOL if (*p == ':') { p++; - comp_text_line(p); + comp_text_line(p, 0); return; } // remark @@ -2607,6 +2605,11 @@ void comp_text_line(char *text) { if (idx == kwREM) { return; } + if (addLineNo) { + // add debug info: line-number + bc_add_code(&comp_prog, kwTYPE_LINE); + bc_add_addr(&comp_prog, comp_line); + } if (idx == -1) { idx = comp_is_proc(comp_bc_name); if (idx != -1) { @@ -2631,7 +2634,7 @@ void comp_text_line(char *text) { if (*p == ':') { // command separator bc_eoc(&comp_prog); p++; - comp_text_line(p); + comp_text_line(p, 0); } return; } @@ -2691,7 +2694,7 @@ void comp_text_line(char *text) { // command separator bc_eoc(&comp_prog); p++; - comp_text_line(p); + comp_text_line(p, 0); } } @@ -4231,12 +4234,8 @@ int comp_pass1(const char *section, const char *text) { } } - // add debug info: line-number - bc_add_code(&comp_prog, kwTYPE_LINE); - bc_add_addr(&comp_prog, comp_line); - strcpy(code_line, ps); - comp_text_line(code_line); + comp_text_line(code_line, 1); if (comp_error) { break; } diff --git a/src/common/str.c b/src/common/str.c index 71e4dd76..d8422893 100644 --- a/src/common/str.c +++ b/src/common/str.c @@ -134,24 +134,6 @@ int strcaselessn(const char *s1, const char *s2, int len) { return 0; } -/** - * strstr with ignore case - */ -char *stristr(const char *s1, const char *s2) { - char *p; - int l2; - - p = (char *) s1; - l2 = strlen(s2); - while (*p) { - if (strcaselessn(p, s2, l2) == 0) { - return p; - } - p++; - } - return NULL; -} - /** * */ @@ -346,48 +328,6 @@ char *strlower(char *str) { return str; } -/** - * - */ -char *get_keyword(char *text, char *dest) { - char *p = (char *) text; - char *d = dest; - - if (p == NULL) { - *dest = '\0'; - return 0; - } - - while (is_space(*p)) { - p++; - } - while (is_alnum(*p) || (*p == '_')) { - *d = to_upper(*p); - d++; - p++; - } - - // Code to kill the $ - // if ( *p == '$' ) - // p ++; - - if (*p == '$') { - *d++ = *p++; - } - *d = '\0'; - while (is_space(*p)) { - p++; - } - // special case, something wrong, jump to next char - if (p == text) { - *dest = *p; - *(dest + 1) = '\0'; - p++; - } - - return p; -} - /** * Returns the number of a string (constant numeric expression) * @@ -830,60 +770,6 @@ char *ltostr(var_int_t num, char *dest) { return dest; } -/** - * newdir must ends with dirsep - * new_ext must starts with . - */ -char *chgfilename(char *dest, char *source, char *newdir, char *prefix, char *new_ext, char *suffix) { - char *plast_dir; - char *plast_point; - - dest[0] = '\0'; - plast_dir = strrchr(source, OS_DIRSEP); - if (!plast_dir) { - plast_dir = source; - } else { - plast_dir++; - } - plast_point = strrchr(source, '.'); - - if (newdir) { - strcat(dest, newdir); - } - if (prefix) { - strcat(dest, prefix); - } - if (new_ext) { - *plast_point = '\0'; - } - strcat(dest, plast_dir); - - if (suffix) { - strcat(dest, suffix); - } - if (new_ext) { - strcat(dest, new_ext); - *plast_point = '.'; - } - return dest; -} - -/** - * - */ -char *xbasename(char *dest, const char *source) { - char *p; - - p = strrchr(source, OS_DIRSEP); - if (!p) { - p = (char *) source; - } else { - p++; - } - strcpy(dest, p); - return dest; -} - /** * returns whether the character is whitespace */ @@ -1226,15 +1112,4 @@ const char *baseof(const char *source, int delim) { return source; } -/** - * - */ -char char_table_replace(const char *what_table, int ch, const char *replace_table) { - const char *p; - p = strchr(what_table, ch); - if (!p) { - return ch; - } - return *(replace_table + (p - what_table)); -} diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index 00046904..5d57c8d6 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -191,7 +191,7 @@ void Runtime::alert(const char *title, const char *message) { _app->activity->vm->DetachCurrentThread(); } -void Runtime::alert(const char *title, int duration) { +void Runtime::alert(const char *title, bool longDuration) { logEntered(); JNIEnv *env; @@ -199,8 +199,8 @@ void Runtime::alert(const char *title, int duration) { 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); + "(Ljava/lang/String;Z)V"); + env->CallVoidMethod(_app->activity->clazz, method, titleString, longDuration); env->DeleteLocalRef(clazz); env->DeleteLocalRef(titleString); _app->activity->vm->DetachCurrentThread(); diff --git a/src/platform/android/jni/runtime.h b/src/platform/android/jni/runtime.h index 9805c05f..1aff33b5 100644 --- a/src/platform/android/jni/runtime.h +++ b/src/platform/android/jni/runtime.h @@ -22,7 +22,7 @@ struct Runtime : public System { virtual ~Runtime(); void alert(const char *title, const char *message); - void alert(const char *title, int duration = 5000); + void alert(const char *title, bool longDuration=true); int ask(const char *title, const char *prompt, bool cancel); void clearSoundQueue(); void construct(); diff --git a/src/platform/android/src/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/src/net/sourceforge/smallbasic/MainActivity.java index 455ee438..d9df0ee5 100644 --- a/src/platform/android/src/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/src/net/sourceforge/smallbasic/MainActivity.java @@ -301,10 +301,12 @@ public void onClick(DialogInterface dialog, int which) {} }); } - public void showToast(final String message, final int duration) { + public void showToast(final String message, final boolean longDurarion) { + Log.i(TAG, "toast longDuration: " + longDurarion); final Activity activity = this; runOnUiThread(new Runnable() { public void run() { + int duration = longDurarion ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT; Toast.makeText(activity, message, duration).show(); } }); diff --git a/src/platform/sdl/editor.cpp b/src/platform/sdl/editor.cpp index 0a8dcce0..9d16e3a6 100644 --- a/src/platform/sdl/editor.cpp +++ b/src/platform/sdl/editor.cpp @@ -9,8 +9,25 @@ #include "config.h" #include "common/sbapp.h" -#include "ui/system.h" #include "ui/textedit.h" +#include "platform/sdl/runtime.h" + +void onlineHelp(Runtime *runtime, TextEditInput *widget) { + char path[100]; + const char *nodeId = widget->getNodeId(); + if (nodeId != NULL && nodeId[0] != '0') { + sprintf(path, "http://smallbasic.sf.net/?q=node/%s", nodeId); + } else { + char *selection = widget->getWordBeforeCursor(); + if (selection != NULL) { + sprintf(path, "http://smallbasic.sf.net/?q=search/node/%s", selection); + free(selection); + } else { + sprintf(path, "http://smallbasic.sf.net"); + } + } + runtime->browseFile(path); +} void System::editSource(strlib::String &loadPath) { logEntered(); @@ -59,7 +76,11 @@ void System::editSource(strlib::String &loadPath) { _output->clearScreen(); _output->addInput(editWidget); _output->addInput(helpWidget); - _output->setStatus(cleanFile); + if (gsb_last_error && !isBack()) { + _output->setStatus("Error. Esc=Close"); + } else { + _output->setStatus(cleanFile); + } _output->redraw(); _state = kEditState; @@ -86,7 +107,6 @@ void System::editSource(strlib::String &loadPath) { } switch (event.key) { - case SB_KEY_F(2): case SB_KEY_F(3): case SB_KEY_F(8): case SB_KEY_F(10): @@ -127,6 +147,10 @@ void System::editSource(strlib::String &loadPath) { helpWidget->createKeywordIndex(); helpWidget->show(); break; + case SB_KEY_F(2): + redraw = false; + onlineHelp((Runtime *)this, editWidget); + break; case SB_KEY_F(5): saveFile(editWidget, loadPath); _output->setStatus("Debug. F6=Step, F7=Continue, Esc=Close"); diff --git a/src/platform/sdl/keymap.h b/src/platform/sdl/keymap.h index a354176c..68597046 100644 --- a/src/platform/sdl/keymap.h +++ b/src/platform/sdl/keymap.h @@ -10,13 +10,6 @@ #define KEYMAP const int keymap[][2] = { - {SDLK_CAPSLOCK, -1}, - {SDLK_LSHIFT, -1}, - {SDLK_RSHIFT, -1}, - {SDLK_LALT, -1}, - {SDLK_RALT, -1}, - {SDLK_LCTRL, -1}, - {SDLK_RCTRL, -1}, {SDLK_RETURN, SB_KEY_ENTER}, {SDLK_ESCAPE, SB_KEY_ESCAPE}, {SDLK_KP_PERIOD, SB_KEY_KP_DEL}, @@ -26,6 +19,7 @@ const int keymap[][2] = { {SDLK_KP_MINUS, SB_KEY_KP_MINUS}, {SDLK_KP_PLUS, SB_KEY_KP_PLUS}, {SDLK_KP_EQUALS, '='}, + {SDLK_TAB, SB_KEY_TAB}, {SDLK_BACKSPACE, SB_KEY_BACKSPACE}, {SDLK_DELETE, SB_KEY_DELETE}, {SDLK_UP, SB_KEY_UP}, @@ -51,29 +45,5 @@ const int keymap[][2] = { {SDLK_F12, SB_KEY_F(12)} }; -const int shiftmap[][2] = { - {'`', '~'}, - {'1', '!'}, - {'2', '@'}, - {'3', '#'}, - {'4', '$'}, - {'5', '%'}, - {'6', '^'}, - {'7', '&'}, - {'8', '*'}, - {'9', '('}, - {'0', ')'}, - {'-', '_'}, - {'=', '+'}, - {'[', '{'}, - {']', '}'}, - {'\\', '|'}, - {';', ':'}, - {'\'', '"'}, - {',', '<'}, - {'.', '>'}, - {'/', '?'}, -}; - #endif diff --git a/src/platform/sdl/main.cpp b/src/platform/sdl/main.cpp index b391b945..1b86b5ce 100644 --- a/src/platform/sdl/main.cpp +++ b/src/platform/sdl/main.cpp @@ -222,6 +222,16 @@ void setupAppPath(const char *path) { strcpy(g_appPath, cwd); strcat(g_appPath, "/"); strcat(g_appPath, path); +#if defined(__linux__) + if (access(g_appPath, X_OK) != 0) { + // launched via PATH, retrieve full path + ssize_t len = ::readlink("/proc/self/exe", g_appPath, sizeof(g_appPath)); + if (len == -1 || len == sizeof(g_appPath)) { + len = 0; + } + g_appPath[len] = '\0'; + } +#endif } } @@ -349,6 +359,7 @@ int main(int argc, char* argv[]) { if (window != NULL) { String font, fontBold; if (getFontFiles(fontFamily, font, fontBold)) { + SDL_StartTextInput(); loadIcon(window); Runtime *runtime = new Runtime(window); runtime->construct(font.c_str(), fontBold.c_str()); diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index 68c62df2..efa0c184 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -130,6 +130,10 @@ int Runtime::ask(const char *title, const char *prompt, bool cancel) { return buttonId; } +void Runtime::browseFile(const char *url) { + ::browseFile(_window, url); +} + void Runtime::construct(const char *font, const char *boldFont) { logEntered(); _state = kClosingState; @@ -325,14 +329,6 @@ char *Runtime::loadResource(const char *fileName) { } void Runtime::handleKeyEvent(MAEvent &event) { - int lenMap = sizeof(keymap) / sizeof(keymap[0]); - for (int i = 0; i < lenMap && event.key != -1; i++) { - if (event.key == keymap[i][0]) { - event.key = keymap[i][1]; - break; - } - } - // handle keypad keys if (event.key != -1) { if (event.key == SDLK_NUMLOCKCLEAR) { @@ -363,28 +359,15 @@ void Runtime::handleKeyEvent(MAEvent &event) { if ((event.nativeKey & KMOD_CTRL) && (event.nativeKey & KMOD_ALT)) { event.key = SB_KEY_CTRL_ALT(event.key); + } else if ((event.nativeKey & KMOD_CTRL) && + (event.nativeKey & KMOD_SHIFT)) { + event.key = SB_KEY_SHIFT_CTRL(event.key); } else if (event.nativeKey & KMOD_CTRL) { event.key = SB_KEY_CTRL(event.key); } else if (event.nativeKey & KMOD_ALT) { event.key = SB_KEY_ALT(event.key); } else if (event.nativeKey & KMOD_SHIFT) { - bool shifted = false; - if (event.key >= SDLK_a && event.key <= SDLK_z) { - event.key = 'A' + (event.key - SDLK_a); - shifted = true; - } else { - lenMap = sizeof(shiftmap) / sizeof(shiftmap[0]); - for (int i = 0; i < lenMap; i++) { - if (shiftmap[i][0] == event.key) { - event.key = shiftmap[i][1]; - shifted = true; - break; - } - } - } - if (!shifted) { - event.key = SB_KEY_SHIFT(event.key); - } + event.key = SB_KEY_SHIFT(event.key); } } @@ -425,9 +408,24 @@ void Runtime::pause(int timeout) { void Runtime::pollEvents(bool blocking) { if (isActive() && !isRestart()) { SDL_Event ev; + SDL_Keymod mod; if (blocking ? SDL_WaitEvent(&ev) : SDL_PollEvent(&ev)) { MAEvent *maEvent = NULL; switch (ev.type) { + case SDL_TEXTINPUT: + // pre-transformed/composted text + mod = SDL_GetModState(); + if (!mod || (mod & KMOD_SHIFT)) { + // ALT + CTRL keys handled in SDL_KEYDOWN + for (int i = 0; ev.text.text[i] != 0; i++) { + MAEvent *keyEvent = new MAEvent(); + keyEvent->type = EVENT_TYPE_KEY_PRESSED; + keyEvent->key = ev.text.text[i]; + keyEvent->nativeKey = 0; + pushEvent(keyEvent); + } + } + break; case SDL_QUIT: setExit(true); break; @@ -458,10 +456,29 @@ void Runtime::pollEvents(bool blocking) { } else if (ev.key.keysym.sym == SDLK_p && (ev.key.keysym.mod & KMOD_CTRL)) { ::screen_dump(); } else { - maEvent = new MAEvent(); - maEvent->type = EVENT_TYPE_KEY_PRESSED; - maEvent->key = ev.key.keysym.sym; - maEvent->nativeKey = ev.key.keysym.mod; + int lenMap = sizeof(keymap) / sizeof(keymap[0]); + for (int i = 0; i < lenMap; i++) { + if (ev.key.keysym.sym == keymap[i][0]) { + maEvent = new MAEvent(); + maEvent->type = EVENT_TYPE_KEY_PRESSED; + maEvent->key = keymap[i][1]; + maEvent->nativeKey = ev.key.keysym.mod; + break; + } + } + if (maEvent == NULL && + ((ev.key.keysym.sym >= SDLK_KP_1 && ev.key.keysym.sym <= SDLK_KP_9) || + ((ev.key.keysym.mod & KMOD_CTRL) && + ev.key.keysym.sym != SDLK_LSHIFT && + ev.key.keysym.sym != SDLK_RSHIFT && + ev.key.keysym.sym != SDLK_LCTRL && + ev.key.keysym.sym != SDLK_RCTRL) || + (ev.key.keysym.mod & KMOD_ALT))) { + maEvent = new MAEvent(); + maEvent->type = EVENT_TYPE_KEY_PRESSED; + maEvent->key = ev.key.keysym.sym; + maEvent->nativeKey = ev.key.keysym.mod; + } } break; case SDL_MOUSEBUTTONDOWN: @@ -483,6 +500,9 @@ void Runtime::pollEvents(bool blocking) { break; case SDL_WINDOWEVENT: switch (ev.window.event) { + case SDL_WINDOWEVENT_FOCUS_GAINED: + SDL_SetModState(KMOD_NONE); + break; case SDL_WINDOWEVENT_RESIZED: onResize(ev.window.data1, ev.window.data2); break; @@ -503,7 +523,7 @@ void Runtime::pollEvents(bool blocking) { if (!_output->scroll(ev.wheel.y == 1, false)) { maEvent = new MAEvent(); maEvent->type = EVENT_TYPE_KEY_PRESSED; - maEvent->key = ev.wheel.y == 1 ? SDLK_UP : SDLK_DOWN; + maEvent->key = ev.wheel.y == 1 ? SB_KEY_UP : SB_KEY_DN; maEvent->nativeKey = KMOD_CTRL; } break; diff --git a/src/platform/sdl/runtime.h b/src/platform/sdl/runtime.h index cce255d3..5d45b42b 100644 --- a/src/platform/sdl/runtime.h +++ b/src/platform/sdl/runtime.h @@ -23,6 +23,7 @@ struct Runtime : public System { void alert(const char *title, const char *message); int ask(const char *title, const char *prompt, bool cancel); + void browseFile(const char *url); void construct(const char *font, const char *boldFont); void debugStart(TextEditInput *edit, const char *file); void debugStep(TextEditInput *edit, TextEditHelpWidget *help, bool cont); diff --git a/src/platform/sdl/settings.cpp b/src/platform/sdl/settings.cpp index 642a44dd..7287c818 100644 --- a/src/platform/sdl/settings.cpp +++ b/src/platform/sdl/settings.cpp @@ -15,10 +15,9 @@ #include "platform/sdl/settings.h" #include "ui/utils.h" +#include "ui/textedit.h" #include "common/smbas.h" -extern int g_themeId; - static const char *ENV_VARS[] = { "APPDATA", "HOME", "TMP", "TEMP", "TMPDIR" }; @@ -79,6 +78,21 @@ int nextInteger(FILE *fp, int def) { return result; } +// +// returns the next hex value from the file +// +int nextHex(FILE *fp, int def) { + int result = 0; + for (int c = fgetc(fp); c != EOF && c != ',' && c != '\n'; c = fgetc(fp)) { + int val = (c >= 'a') ? (10 + (c - 'a')) : (c - '0'); + result = (result * 16) + val; + } + if (!result) { + result = def; + } + return result; +} + // // restore window position // @@ -93,6 +107,9 @@ void restoreSettings(SDL_Rect &rect, int &fontScale, bool debug) { opt_mute_audio = nextInteger(fp, 0); opt_ide = nextInteger(fp, 0); g_themeId = nextInteger(fp, 0); + for (int i = 0; i < THEME_COLOURS; i++) { + g_user_theme[i] = nextHex(fp, g_user_theme[i]); + } fclose(fp); } else { rect.x = SDL_WINDOWPOS_UNDEFINED; @@ -117,6 +134,11 @@ void saveSettings(SDL_Window *window, int fontScale, bool debug) { SDL_GetWindowSize(window, &w, &h); fprintf(fp, "%d,%d,%d,%d,%d,%d,%d,%d\n", x, y, w, h, fontScale, opt_mute_audio, opt_ide, g_themeId); + // print user theme colours on the second line + for (int i = 0; i < THEME_COLOURS; i++) { + fprintf(fp, (i + 1 < THEME_COLOURS ? "%06x," : "%06x"), g_user_theme[i]); + } + fprintf(fp, "\n"); fclose(fp); } } diff --git a/src/platform/sdl/syswm.cpp b/src/platform/sdl/syswm.cpp index 48290e42..382cfdf1 100644 --- a/src/platform/sdl/syswm.cpp +++ b/src/platform/sdl/syswm.cpp @@ -19,6 +19,7 @@ void appLog(const char *format, ...); #if defined(_Win32) #include +#include void loadIcon(SDL_Window *window) { HINSTANCE handle = ::GetModuleHandle(NULL); @@ -57,11 +58,21 @@ void launchDebug(const char *file) { } } +void browseFile(SDL_Window *window, const char *url) { + SDL_SysWMinfo wminfo; + SDL_VERSION(&wminfo.version); + if (SDL_GetWindowWMInfo(window, &wminfo) == 1) { + HWND hwnd = wminfo.info.win.window; + ::ShellExecute(hwnd, "open", url, 0, 0, SW_SHOWNORMAL); + } +} + #else #include #include void loadIcon(SDL_Window *window) { + // handled via smallbasic.desktop } int getStartupFontSize(SDL_Window *window) { @@ -80,7 +91,7 @@ void launchDebug(const char *file) { // child process sprintf(port, "-p %d", g_debugPort); if (execl(g_appPath, g_appPath, port, "-d", file, (char *)0) == -1) { - fprintf(stderr, "exec failed %s\n", strerror(errno)); + fprintf(stderr, "exec failed [%s] %s\n", strerror(errno), g_appPath); exit(1); } break; @@ -90,4 +101,23 @@ void launchDebug(const char *file) { } } +void browseFile(SDL_Window *window, const char *url) { + if (fork() == 0) { + const char *browser[] = { + "sensible-browser", + "xdg-open", + "gnome-open", + "htmlview", + "firefox", + "google-chrome", + NULL + }; + for (int i = 0; browser[i] != NULL; i++) { + execlp(browser[i], browser[i], url, NULL); + } + fprintf(stderr, "exec browser failed for %s\n", url); + ::exit(1); + } +} + #endif diff --git a/src/platform/sdl/syswm.h b/src/platform/sdl/syswm.h index 09fbee6a..810e0663 100644 --- a/src/platform/sdl/syswm.h +++ b/src/platform/sdl/syswm.h @@ -14,5 +14,6 @@ void loadIcon(SDL_Window *window); int getStartupFontSize(SDL_Window *window); void launchDebug(const char *file); +void browseFile(SDL_Window *window, const char *url); #endif diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index 1eebf9af..8fc55026 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -190,6 +190,10 @@ void Graphics::drawRGB(const MAPoint2d *dstPoint, const void *src, size_t scale = 1; int w = bytesPerLine; + if (opacity > 0 && opacity < 100) { + // higher opacity values should make the image less transparent + opacity = 100 - opacity; + } for (int y = srcRect->top; y < srcRect->height; y += scale) { int dY = dstPoint->y + y; if (dY >= _drawTarget->y() && diff --git a/src/ui/image.cpp b/src/ui/image.cpp index c64c1401..e8547c21 100644 --- a/src/ui/image.cpp +++ b/src/ui/image.cpp @@ -106,6 +106,22 @@ void ImageDisplay::draw(int x, int y, int w, int h, int cw) { maDrawRGB(&dstPoint, _buffer->_image, &srcRect, _opacity, _buffer->_width); } +// share image buffer from another image variable +ImageBuffer *load_image(var_t *map) { + ImageBuffer *result = NULL; + int bid = map->type == V_MAP ? map_get_int(map, IMG_BID, -1) : -1; + if (bid != -1) { + List_each(ImageBuffer *, it, cache) { + ImageBuffer *next = (*it); + if (next->_bid == (unsigned)bid) { + result = next; + break; + } + } + } + return result; +} + ImageBuffer *load_image(dev_file_t *filep) { ImageBuffer *result = NULL; List_each(ImageBuffer *, it, cache) { @@ -294,7 +310,7 @@ void create_image(var_p_t var, ImageBuffer *image) { map_add_var(var, IMG_OFFSET_TOP, 0); map_add_var(var, IMG_OFFSET_LEFT, 0); map_add_var(var, IMG_ZINDEX, 1); - map_add_var(var, IMG_OPACITY, 1); + map_add_var(var, IMG_OPACITY, 0); map_add_var(var, IMG_ID, ++nextId); if (image != NULL) { @@ -363,7 +379,11 @@ void screen_dump() { #endif for (int i = 0; i < 1000; i++) { char file[OS_PATHNAME_SIZE]; - sprintf(file, "%ssbasic_dump_%d.png", path, i); + if (strstr(path, "://") != NULL) { + sprintf(file, "sbasic_dump_%d.png", i); + } else { + sprintf(file, "%ssbasic_dump_%d.png", path, i); + } if (access(file, R_OK) != 0) { g_system->systemPrint("Saving screen to %s\n", file); lodepng_encode32_file(file, image, width, height); @@ -412,6 +432,8 @@ extern "C" void v_create_image(var_p_t var) { } image = load_xpm_image(data); delete [] data; + } else { + image = load_image(&arg); } v_free(&arg); break; diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index da46a86a..ccf4c923 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -18,7 +18,7 @@ #define DRAW_SHAPE \ Shape *rect = (*it); \ if (rect->_y >= _scrollY && \ - rect->_y + rect->_height <= _scrollY + _height) \ + rect->_y <= _scrollY + _height) \ rect->draw(_x + rect->_x, _y + rect->_y - _scrollY, w(), h(), _charWidth) int compareZIndex(const void *p1, const void *p2) { diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 70e3f917..efa21c2b 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -412,13 +412,16 @@ char *System::loadResource(const char *fileName) { _output->setStatus("Loading..."); _output->redraw(); if (dev_fopen(handle, fileName, 0)) { - http_read(f, var_p); - int len = var_p->v.p.size; - buffer = (char *)malloc(len + 1); - memcpy(buffer, var_p->v.p.ptr, len); - buffer[len] = '\0'; + if (http_read(f, var_p) == 0) { + systemPrint("\nfailed to read %s\n", fileName); + } else { + int len = var_p->v.p.size; + buffer = (char *)malloc(len + 1); + memcpy(buffer, var_p->v.p.ptr, len); + buffer[len] = '\0'; + } } else { - systemPrint("\nfailed to open %s", fileName); + systemPrint("\nfailed to open %s\n", fileName); } _output->setStatus(NULL); dev_fclose(handle); diff --git a/src/ui/textedit.cpp b/src/ui/textedit.cpp index 0722ceb2..0d228d6d 100644 --- a/src/ui/textedit.cpp +++ b/src/ui/textedit.cpp @@ -16,7 +16,8 @@ #include "ui/kwp.h" #define STB_TEXTEDIT_IS_SPACE(ch) IS_WHITE(ch) -#define STB_TEXTEDIT_IS_PUNCT(ch) ispunct(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_IMPLEMENTATION #include "lib/stb_textedit.h" @@ -24,7 +25,7 @@ #define LINE_BUFFER_SIZE 200 #define INDENT_LEVEL 2 #define HELP_WIDTH 22 -#define NUM_THEMES 4 +#define NUM_THEMES 5 #define TWISTY1_OPEN "> " #define TWISTY1_CLOSE "< " #define TWISTY2_OPEN " > " @@ -34,6 +35,11 @@ #define HELP_BG 0x73c990 #define HELP_FG 0x20242a +#if defined(_Win32) +#include +#define strcasestr StrStrI +#endif + int g_themeId = 0; int g_lineMarker[MAX_MARKERS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 @@ -58,13 +64,19 @@ const int theme3[] = { }; const int theme4[] = { + 0x4f4a44, 0x222228, 0x77839b, 0x484f5f, 0xa7aebc, 0x5f9e59, + 0xcdc0b0, 0xe1e1e1, 0xefeff0, 0x1f51eb, 0x000000, 0xcbb8a2, + 0x4c9f9a, 0xaf5fd6, 0x0000ff, 0xc679dd, 0x0083f8, 0 +}; + +int g_user_theme[] = { 0xc8cedb, 0xa7aebc, 0x484f5f, 0xa7aebc, 0xa7aebc, 0x00bb00, 0x2e3436, 0x888a85, 0x000000, 0x4d483b, 0x000000, 0x2b313a, 0x0083f8, 0xff9d00, 0x31ccac, 0xc679dd, 0x0083f8 }; const int* themes[] = { - theme1, theme2, theme3, theme4 + theme1, theme2, theme3, theme4, g_user_theme }; const char *helpText = @@ -94,6 +106,7 @@ const char *helpText = "SHIFT- select\n" "TAB indent line\n" "F1,A-h keyword help\n" + "F2 online help\n" "F5 debug\n" "F9, C-r run\n"; @@ -118,10 +131,6 @@ int compareIntegers(const void *p1, const void *p2) { return i1 < i2 ? -1 : i1 == i2 ? 0 : 1; } -void init_edit_markers() { - -} - // // EditTheme // @@ -635,6 +644,12 @@ bool TextEditInput::edit(int key, int screenWidth, int charWidth) { case SB_KEY_ENTER: editEnter(); break; + case SB_KEY_SHIFT_CTRL(SB_KEY_LEFT): + selectNavigate(true); + break; + case SB_KEY_SHIFT_CTRL(SB_KEY_RIGHT): + selectNavigate(false); + break; case -1: return false; break; @@ -651,7 +666,8 @@ bool TextEditInput::edit(int key, int screenWidth, int charWidth) { } } else { int pageRows = _height / _charHeight; - if (_cursorRow - _scroll > pageRows) { + if (_cursorRow - _scroll > pageRows || _cursorRow < _scroll) { + // scroll for cursor outside of current frame updateScroll(); } } @@ -662,14 +678,14 @@ bool TextEditInput::edit(int key, int screenWidth, int charWidth) { bool TextEditInput::find(const char *word, bool next) { bool result = false; if (_buf._buffer != NULL && word != NULL) { - const char *found = strstr(_buf._buffer + _state.cursor, word); + const char *found = strcasestr(_buf._buffer + _state.cursor, word); if (next && found != NULL) { // skip to next word - found = strstr(found + strlen(word), word); + found = strcasestr(found + strlen(word), word); } if (found == NULL) { // start over - found = strstr(_buf._buffer, word); + found = strcasestr(_buf._buffer, word); } if (found != NULL) { result = true; @@ -786,6 +802,14 @@ bool TextEditInput::selected(MAPoint2d pt, int scrollX, int scrollY, bool &redra return focus; } +void TextEditInput::selectNavigate(bool up) { + int start = _state.select_start == _state.select_end ? _state.cursor : _state.select_start; + _state.select_start = _state.select_end = _state.cursor; + stb_textedit_key(&_buf, &_state, up ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_WORDRIGHT); + _state.select_start = start; + _state.select_end = _state.cursor; +} + char *TextEditInput::copy(bool cut) { int selectStart = MIN(_state.select_start, _state.select_end); int selectEnd = MAX(_state.select_start, _state.select_end); @@ -1120,6 +1144,12 @@ uint32_t TextEditInput::getHash(const char *str, int offs, int &count) { for (count = 0; count < keyword_max_len; count++) { char ch = str[offs + count]; if (!isalpha(ch) && ch != '_') { + // non keyword character + while (isalnum(ch) || ch == '.' || ch == '_') { + // skip any program variable characters + count++; + ch = str[offs + count]; + } break; } result += tolower(str[offs + count]); @@ -1203,7 +1233,7 @@ char *TextEditInput::getSelection(int *start, int *end) { } else { *start = wordStart(); int i = _state.cursor; - while (!IS_WHITE(_buf._buffer[i]) && !ispunct(_buf._buffer[i]) && i < _buf._len) { + while (IS_VAR_CHAR(_buf._buffer[i]) && i < _buf._len) { i++; } *end = i; @@ -1212,6 +1242,21 @@ char *TextEditInput::getSelection(int *start, int *end) { return result; } +const char *TextEditInput::getNodeId() { + char *selection = getWordBeforeCursor(); + const char *result = NULL; + int len = selection != NULL ? strlen(selection) : 0; + if (len > 0) { + for (int i = 0; i < keyword_help_len && !result; i++) { + if (strcasecmp(selection, keyword_help[i].keyword) == 0) { + result = keyword_help[i].nodeId; + } + } + } + free(selection); + return result; +} + char *TextEditInput::getWordBeforeCursor() { char *result; if (_state.select_start == _state.select_end) { @@ -1260,13 +1305,25 @@ void TextEditInput::gotoNextMarker() { } } -void TextEditInput::lineNavigate(bool lineDown) { - if (lineDown) { +void TextEditInput::lineNavigate(bool arrowDown) { + if (arrowDown) { + // starting from the cursor position (relative to the screen), + // count the number of rows to the bottom of the document. + int rowCount = _cursorLine - _scroll; for (int i = _state.cursor; i < _buf._len; i++) { - if (_buf._buffer[i] == '\n' && i + 1 < _buf._len) { - _state.cursor = i + 1; - _scroll += 1; - break; + if (_buf._buffer[i] == '\n') { + rowCount++; + } + } + int pageRows = (_height / _charHeight) - 1; + if (rowCount >= pageRows) { + // rows exist below end of page to pull up + for (int i = _state.cursor; i < _buf._len; i++) { + if (_buf._buffer[i] == '\n' && i + 1 < _buf._len) { + _state.cursor = i + 1; + _scroll += 1; + break; + } } } } else if (_scroll > 0) { @@ -1584,13 +1641,15 @@ void TextEditHelpWidget::createCompletionHelp() { _buf.append("\n", 1); } } - const char *found = strstr(_editor->getText(), selection); + const char *text = _editor->getText(); + const char *found = strcasestr(text, selection); while (found != NULL) { const char *end = found; - while (!IS_WHITE(*end) && !ispunct(*end) && *end != '\0') { + const char pre = found > text ? *(found - 1) : ' '; + while (IS_VAR_CHAR(*end) && *end != '\0') { end++; } - if (end - found > len) { + if (end - found > len && (IS_WHITE(pre) || pre == '.')) { String next; next.append(found, end - found); if (!words.exists(next)) { @@ -1599,7 +1658,7 @@ void TextEditHelpWidget::createCompletionHelp() { _buf.append("\n", 1); } } - found = strstr(found + len, selection); + found = strcasestr(end, selection); } } else { const char *package = NULL; diff --git a/src/ui/textedit.h b/src/ui/textedit.h index e77f8162..b982cde4 100644 --- a/src/ui/textedit.h +++ b/src/ui/textedit.h @@ -23,6 +23,10 @@ #include "common/smbas.h" #include "common/keymap.h" +extern int g_themeId; +extern int g_user_theme[]; +#define THEME_COLOURS 17 + struct TextEditInput; struct EditTheme { @@ -102,9 +106,11 @@ struct TextEditInput : public FormEditInput { bool isDirty() { return _dirty && _state.undostate.undo_point > 0; } void setDirty(bool dirty) { _dirty = dirty; } void resize(int w, int h) { _width = w; _height = h; } + const char *getNodeId(); char *getWordBeforeCursor(); bool replaceNext(const char *text); int getCompletions(StringList *list, int max); + void selectNavigate(bool up); protected: enum SyntaxState { @@ -228,8 +234,8 @@ struct TextEditHelpWidget : public TextEditInput { #define STB_TEXTEDIT_K_PGUP SB_KEY_PGUP #define STB_TEXTEDIT_K_PGDOWN SB_KEY_PGDN #define STB_TEXTEDIT_NEWLINE '\n' -#define STB_TEXTEDIT_K_CONTROL 0xF1000000 -#define STB_TEXTEDIT_K_SHIFT 0xF8000000 +#define STB_TEXTEDIT_K_CONTROL SB_KEY_CTRL(0) +#define STB_TEXTEDIT_K_SHIFT SB_KEY_SHIFT(0) #define STB_TEXTEDIT_GETWIDTH_NEWLINE 0.0f #define STB_TEXTEDIT_KEYTOTEXT(k) k #define STB_TEXTEDIT_STRINGLEN(o) o->_len diff --git a/web/link_permission/link_permission.module b/web/link_permission/link_permission.module index 6fb349bb..00bcdfe7 100644 --- a/web/link_permission/link_permission.module +++ b/web/link_permission/link_permission.module @@ -14,7 +14,8 @@ function block_spammer($user, $body) { $result = true; if ($user->uid && // logged in !user_access('post hyperlinks') && // not permitted - (preg_match("@(http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://)+@se", $body) || + (strlen($body) > 3000 || + preg_match("@(http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://)+@se", $body) || preg_match("@(www\.[a-zA-Z0-9\@:%_+*~#?&=.,/;-]*[a-zA-Z0-9\@:%_+~#\&=/;-])+@se", $body))) { // perform block operation watchdog('action', 'Blocked spammer.', array('%name' => check_plain($user->name))); @@ -60,3 +61,4 @@ function link_permission_node_validate($node, $form, &$form_state) { $text = $node->body[$node->language][0]['value']; block_spammer($user, $text); } +