Παρασκευή 28 Ιανουαρίου 2022

Version 11

1. Image Control like a console for input and output:

The Ver11 in Info file has this code:

declare form1 form
declare image1 Image form form1
METHOD image1,"MOVE", 1000,1000, 6000, 3300
With image1,"KeyEvent", true, "Enabled" as enabled, "tabstop", true, "showcaret" as caret, "default", true
enabled=true
caret=true
layer image1 {
form 20, 10
CLS 4,0 : PEN 15
DOUBLE
REPORT 2, "Example"
NORMAL
CLS #336633, 2
REFRESH 10
}
function form1.click {
enabled= not enabled
if enabled then method image1, "GetFocus"
}
bufall=stack
buf$=""
function getline$() {
if stack.size=0 then exit
shift 1,-stack.size
exp$=""
while not empty
exp$+=letter$
end while
=exp$
}
function image1.lostfocus {
layer image1 {
cursor 0, row:print over $(0), getline$(buf$, ! stack(bufall))
}
}
function image1.gotfocus {
layer image1 {
cursor 0, row:print over $(0), buf$;
}
}
function image1.keydown {
read new &k, &s
if s<>2 then exit
if k=86 then
local clip$=clipboard$
if clip$<>"" then keyboard clip$ : call local image1.keypress()
k=0:s=0
else.if k=67 then
local clip$=getline$(buf$, ! stack(bufall))
if clip$<>"" then clipboard clip$
k=0:s=0
end if
}
Def CheckSurrogateLead(w as integer)=uint(w)>0xD7FF and uint(w)<0xDC00
// HERE WE USE THE TAIL WHEN WE DELETE CHARS (SO WE CAN DELETE DOUBLE WORD CHARACTERS)
Def CheckSurrogateTrail(w as integer)=uint(w)>0xDBFF and uint(w)<0xE000
buf1$=""
function image1.keypress {
if not caret then caret=true
layer image1 {
LOCAL a$, onemore as boolean
a$=KEY$
DO
if a$=chr$(8) then
onemore=false
if len(buf$)>0 then
local w=chrcode(right$(buf$,1))
do
onemore=false
if CheckSurrogateTrail(w) then onemore=true
buf$=left$(buf$, len(buf$)-1)
until not onemore or len(buf$)=0
end if
if len(buf$)=0 and len(bufall)>0 then
stack bufall {read buf$}
if onemore then
buf$=left$(buf$, len(buf$)-1)
end if
cursor 0, row:print over $(0),buf$;
else
if pos>0 then cursor 0, row:print over $(0),buf$;
end if
else.if len.disp(a$)=0 and chrcode(a$)>32 or chrcode(a$)<0 then
// THIS IS FOR COMBINING_DIACRITICAL_MARKS LIKE CHRCODE$(0x301)
// COMBINING ACUTE ACCENT
// hold Alt press + 3 0 1 release Alt
buf$+=a$
cursor 0, row:print over $(0), buf$;
else.iF a$=CHR$(13) then
buf$=getline$(buf$, !bufall)
layer {print buf$:refresh}
cursor 0, row:print over $(0),buf$
print: buf$=""
else.if a$>=" " then
if len.disp(buf$)=width-1 then
stack bufall {push buf$}:buf$="": cursor 0, row:print over
end if
print a$; : buf$+=a$
end if
a$=KEY$
UNTIL a$=""
refresh
}
}
METHOD form1 "SHOW", 1


declare form1 NOTHING


The Image1.KeyPress is an event service function. The actual key retrieved using Key$ (which here not waiting for a key, as in pure M2000 console). We use a Do Until, loop to get all keys, before the end of service.

This is the first version, where we can write a big line and under the hood is a stack object, the bufall, which hold chunks of input line (those parts we didn't show). When we press Enter all chunks combined using the getline$() function. The statement: buf$=getline$(buf$, !bufall) call the getline$ and push on getline$() stack on top buf$ and under all items from bufall (so !bufall just send items from bufall which is a stack to functions stack, and this is fast, because each item in stack has one pointer to copy, the bufall get empty after that).

Some times we change the focus of Image1 so we get the lostFocus event and reset the line from the beginning (first character at the left of "console", or we say left justify). Because we have to prepare the final string, immediatetly, we use this getline$(buf$, ! stack(bufall)) where stack(bufall) return a copy of bufall, so the bufall stack remain intact. The ! operator used for arrays (tuple) and stacks, but for stacks this retrieve all items, and leave them empty.

About Stacks and Arrays.

We can make stacks from arrays, and arrays from stacks. The actual type of stack is mStiva (a com object) and for array (tuple) is mArray. 

a=stack((1,2,3))
? type$(a)="mStiva"
? a
? stackitem(a,2)=2
b=array(stack:=1,2,3)
? type$(b)="mArray"
? b
? array(b, 1)=2

Variables a and b are pointers to objects. There is another interface for arrays, which used with identifiers with parenthesis. These arrays are like values. We have to define them using Dim (or Local or Global), We can use up to 10 dimensions, and we can set bounds like this A(1 to 10), B(-4 to -3, 100 to 103) . We can use pointers to arrays like the c variable in the following example:

Dim A(10)=1, B()
B()=A()
B(3)+=10
c=B()
c++
? B(3)=12, B(0)=2, A(0)=1

So c++ add one in each numeric item of B(). Also pointers to arrays handle the array as one dimension. Arrays expand from the first dimension (preserving items). The k variable now is a pointer to k(), but read items as one dimension. Using array() we can use the true dimensions. We can change dimensions with another Dim, and if we place new items these will be of type Empty.

dim K(3,2)
k(0,0)=1,2,3,4,5,6
? K()
dim K(4,2)
k(3,0)=7, 8
? K()

k=k()
? k#val(7)=8

Print array(k, 0,0)


2. Splitter Control using a Listbox

We can make a splitter control using a listbox and proper configuration, through properties and methods, and use of proper events.

In the Info file (info.gsb) included in setup file the new mEditor which utilize the splitter as vertical splitter between the editor's editbox and the help editbox (you have to reload to user directory, using Dir AppDir$ : Load Info  and then press F1 to save it to M2000 user directory - also this change the current directory to user directory). Open the mEditor source using Edit mEditor statement in M2000 console.


We can make a fake splitter control using events from form, but the new splitter can show the new split without actually do the split (without resizing any controls we have to split), until we get the mouseup event. Also we can change the color at the moving stage, and restore it at the final mouseup event. The splitter configure to not included as a tabstop (so pressing TAB and Shift TAB we don't choose it). I am thinking to implement a keyboard control for splitter (so then the TAB Stop  is usefull)..


We can make three types of splitter, horizontal, vertical and both. Because a ListBox can be place on top of an Image control, we can use splitter to split controls on an Image (container).

A listbox can use the included logic for moving, either as HeaderOnly (so we use it as a simple frame, with a color and perhaps some characters as icons, for title), or we can with a list. We can configure to move the control or the container of control. The form's caption is on glis4.cls user control (the same under Listbox) which use this logic to move the form.

From mEditor source, this is the code to configure the Listbox, named Handle1 on form NotePad.
Declare Handle1 Listbox Form NotePad

Because the splitter we want to be visible only if HelpPad (the EditBox for help) is visible, we need to link property "Visible" to handle1.visible (dots can be used for variable names). The handle1.visible is not a boolean variable, so we cant do this handle1.visible~ to invert the value, because it is a PropReference object type (an object to hard link the property with an identifier). This object for this property has a boolean value. Any non zero number converted to True, and zero number converted to False. By default in M2000, True is not boolean, has value -1, and False hase value 0. Comparisons in M2000 return boolean type.

If we don't want a control to be visible when the form shown the first time, we have to lock it. The unlocked control's visible property change from false to true by the form at the show method of form. When a control is not visible tabstop not used. For Handle1 we want this happen always, so we place false in "tabstop" property.
For handling the title back color we link mcolor, and for the wanted return value we need the handle1.top (linked to "top" property, is a single float number),


With Handle1, "Visible" as handle1.visible, "locked" as handle1.locked, "headeronly", true, "tabstop", false, "TitleBackColor" as mcol, "top" as handle1.top
Method Handle1, "UseVerMove"
mcol=#FFA000
handle1.locked=true
handle1.visible=false

So we start using Locked=true, visible=false.

Before the opening of NotePad we have to call the resize event function by code, using this:

Call Local Notepad.Resize()

The Call Local place the same namespace as the current one, so from Notepad.Resize the interpreter think is running in module not in function, so the Call Local means call with same scope as the current. Although the scope is the same (as namespace), actually every new identifier erased at the return from call. Because events also called using a call like call local (except the stack of values is new), we have always use New identifier before we read values from stack, to make new identifiers (to shadow identifiers with same name in same scope, which prevent from altering values). Also if we want variables for local use, we can defined using Local statement. Also if we want a new link for a property, we can use  as new.

So this is the resize function (as event service function):

Function Notepad.Resize {
Local FromHelp=false
if match("N") then Read FromHelp
Layer NotePad { Cls Color(255, 160, 0) ,0}
local tHeight1=theight*2
Local free=NP.Height-tHeight1-twipsX*3
local oldhelpshow=helpshow
If NP.height>1800 Then {
If helpview Then
if oldhelpshow else
Method Handle1, "move",twipsX*3, tHeight1+free-free/3-twipsX*3, NP.Width-twipsX*6,twipsY*3
end if
if handle1.locked then handle1.locked=false
local third=handle1.top-tHeight1
if third<twipsX*10 then third=twipsX*10
if free<third then third=free/3
Method Pad,"move", twipsX*3, tHeight1, NP.Width-twipsX*6, third
third+=tHeight1
Method Handle1, "move",twipsX*3, third, NP.Width-twipsX*6,twipsX*3
Method HelpPad,"move", twipsX*3, third+twipsX*3, NP.Width-twipsX*6,free-third-twipsY*3+tHeight1
handle1.visible=true
helpshow=true
Method HelpPad, "resize"
Else
helpshow=false
handle1.visible=false
Method Pad,"move", twipsX*3, tHeight1, NP.Width-twipsX*6, NP.Height-tHeight1-twipsY*3
End If
Method Pad,"Resize"
}
}

Because we want the color of splitter control (the Handle1, a listbox used as splitter) we have to handle these events:

Function Handle1.ValidMove {
Drop
Read New &Y
mcol=7
if y<tHeight+1000 then y=tHeight+1000 : exit
if y>NP.Height-tHeight then y=NP.Height-tHeight
}
Function Handle1.MouseUp {
Call Local Notepad.Resize()
mcol=#FFA000
}

The ValidMove get two byref values, we drop one and we use only the Y (vertical move). So we can stop the move by handling this Y value, if Y get out of bounds we like.

When the move stopped releasing the mouse button we get the MouseUp event and we call the resize event service function of Notepad (the form), plus we restore the color. The #77A000 is the M2000 orange color.

The Handle1 also change Y when we resize the form Notepad, but not always. See line:

if free<third then third=free/3
in Notepad.resize function

Also for this Notepad, I add a new item in Edit menu, with F1 accelerator key for changing Wrap mode.


Δευτέρα 24 Ιανουαρίου 2022

Αναθεώρηση 55, Έκδοση 10 (Νέο στοιχείο ελέγχου Εικόνα)

Σε αυτήν την αναθεώρηση έγιναν αρκετά πράγματα, με κύριο την δημιουργία ενός νέου στοιχείου ελέγχου για τις φόρμες χρήστη, την Εικόνα.

Το στοιχείο Εικόνα ή Image, έχει τις παρακάτω λειτουργίες:

  • Έχει δικό του Επίπεδο (πχ στο Image1 με Layer Image1 { } δίνουμε εντολές γραφικών στο επίπεδο της εικόνας, αντίστοιχα Επίπεδο Image1 { } στα ελληνικά, το όνομα του στοιχείου μπορεί να είναι οτιδήποτε).
  • Μπορούμε να φτιάξουμε στόχους όπως και στη φόρμα.
  • Οι εντολές Hold και Release εκτελούνται και εδώ (Κράτησε και Άφησε). Έτσι κρατάμε ένα φόντο και το επαναφέρουμε πριν σχεδιάσουμε κάτι/
  • Μπορούμε να περάσουμε το μέρος της φόρμας που "πατάει" η εικόνα στην εικόνα. (αντί να χρησιμοποιούμε τις Κράτησε/Άφησε).
  • Υπάρχουν γεγονότα για click, dblclick, mouseup, mousedown, mousemove, resize.
  • Δεν υπάρχει ιδιότητα εικόνα, για να αλλάξουμε την εικόνα πρέπει να χρησιμοποιήσουμε την εντολή /δομή Επίπεδο ΌνομαΕικόνας { }
  • Όταν ορίζουμε στοιχεία γα τη φόρμα μπορούμε να τα βάλουμε σε μια εικόνα.

Παράδειγμα:  έστω έχουμε ορίσει την φόρμα1 και την εικόνα1: Η παρακάτω εντολή βάσει το Λίστα1 στη φόρμα Φόρμα1 αλλά πάνω στην Εικόνα1.

Όρισε Λίστα1 Λίστα Φόρμα Φόρμα1 Εικόνα Εικόνα1

Αλλάζοντας την ιδιότητα visible στην Εικόνα, ελέγχουμε ταυτόχρονα μια σειρά στοιχείων. Επίσης μπορούμε να μετακινήσουμε την εικόνα χωρίς να χρειαστεί να δώσουμε σε κάθε στοιχείο χωριστά την μέθοδο  "move", αρκεί μια στην εικόνα (Εφόσον τα στοιχεία είναι στην εικόνα). Όταν έχουμε στοιχεία με διαφάνεια, και είναι πάνω σε εικόνα, τότε δείχνουν το περιεχόμενο της εικόνας και όχι της φόρμας.

Ειδικά το Listbox ή Λίστα, έχει τώρα όλη τη λειτουργικότητα (θα την δείξω με παραδείγματα σε άλλη ανάρτηση), για να μετακινείται με drag (σύρσιμο) είτε αυτό με επιλογή μόνο οριζόντια, μόνο κάθετα, οπουδήποτε, είτε αυτό που το περιέχει (φόρμα ή εικόνα). Το είχε αυτό το glis4.cls στοιχείο ελέγχου (δείτε το UseFloatList και FloatList στον κώδικα του στοιχείου στο git), αλλά όχι για οποιοδήποτε container.

Επίσης διορθωθηκαν ορισμένες "αβλεψίες" από την 54 και 53 αναθεώρηση, και έτσι το παράδειγμα εδώ:

https://georgekarras.blogspot.com/2017/07/22-89.html  λειουργεί σωστά (Δείχνει τον δρομέα στην διόρθωση και την αλλαγή χρώματος. Αυτό το παράδειγμα δείχνει δυο στοιχεία Λίστα, να ελέγχει το ένα το άλλο, δηλαδή όταν μετακινείται το πρώτο, ακολουθεί το δεύτερο και το αντίστροφο (χωρίς να μπαίνουν σε συνεχομενη κλήση). Από την αρχική σχεδίαση ένα γεγονός δεν αφήνεται να εκτελεστεί ενώ ήδη βρίσκεται σε εκτέλεση (reentrance). Από λάθος όταν αλλάζαμε μέγεθος φόρμας με τα βελάκια, γίνονταν αλλαγή μεγέθους χωρίς να σταλθεί γεγονός (επειδή ήδη ήταν ενεργό, δηλαδή σε εκτέλεση), αλλά άλλαζε το μέγεθος και αυτό διορθώθηκε στην 55 αναθεώρηση, απλά δεν ακούνε τα "βελάκια" για αλλαγή μεγέθους όσο τρέχει το γεγονός "resize" της φόρμας. Ενώ η Vb6 είναι ενός νήματος, όταν στο γεγονός "resize" δοθεί ένα Doevents, κάτι που δίνει χρόνο στο σύστημα, τότε μπορεί να εκτελεστεί άλλο γεγονός (σε αναμονή), το οποίο μπορεί να είναι ίδιο με εκείνο που ήδη εκτελείται. Τι κάνει το σύστημα; Σταματάει τη τρέχουσα εκτέλεση και εκτελεί το πιο πρόσφατο γεγονός, μέχρι να μην υπάρχουν άλλα και μετά γυρίζει και τελειώνει με ότι είχε αφήσει "σταματημένα" (interrupted). Όταν λοιπόν έφτιαχνα μια εφαρμογή με στοιχεία ελέγχου (karel ide) είδα το πρόβλημα. Βασικά στο προγραμματισμό πάντα υπάρχουν λύσεις, όταν δεις το πρόβλημα (και μπορείς να το αναπαράγεις).

Στο παράδειγμα έχω βάλει πάνω στο Image ένα button και ένα checkbox (το check box δεν είναι διάφανο, δεν έχει λογική για διάφανη εμφάνιση, αντίθετα το button εδώ είναι διάφανο).

Όταν δίνουμε κλικ εκτός εικόνας τότε μια φορά εμφανίζετα και μια εξαφανίζεται η εικόνα.

declare Form1 form

declare Image1 image form Form1
declare button1 button form form1 image image1
declare check1 checkbox form form1 image image1
Method Form1, "move", 1000, 1000, 10000, 8000
Method Image1, "move", 1000, 1000, 6000, 4000
Method check1, "move", 0, 1200
With Image1, "Visible" as Visible
Layer Form1 {
Gradient 1, 5
move 0,0
draw to scale.x/2, scale.y/2
circle 1500
move 0,0
// image "καρελ.emf", scale.x/2
}
a=100
Method button1, "transparent"
Layer Image1 {
// mode 12
// mode 12, 8000, 6000
window 16, 8000, 6000;
form 32, 20
method Image1, "CopyBack"
// image "m2000.ico"  // this icon need two time drawing
// image "m2000.ico"
Report 2, "Ok"
Print Scale.x, Scale.y
Print width, height
hold
cls

}
Function Button1.Click {
beep
print "button1"
refresh
}
Function Image1.Click {
beep
print "beep"
layer image1 {
release
refresh
}
method Form1, "Refreshall"
refresh
}
k=true
function form1.click {
k~
visible=k
}
method Form1, "show", 1
declare Form1 nothing





Παρασκευή 21 Ιανουαρίου 2022

Revision 54, Version 10

A fix for multi monitor support. In Windows 10, when the secondary monitor setup left to primary monitor get negative coordinates (I didn't check this in lower versions of Windows, but I don't remember that this happen to Windows 7). By design of M2000 Window Manager, a user form or dialog can't pass the minimum coordinates (supposed was 0), so now first code check the minimum left and top values from all monitors. The moving procedure  (by dragging) for forms/dialogue happen in glis4.cls user control. Also I put a reading of monitor arrangement every time a form/dialogue start.




Πέμπτη 20 Ιανουαρίου 2022

Αναθεώρηση 53, Έκδοση 10

 Σε αυτήν την αναθεώρηση, τελείωσα τον αρχικό σχεδιασμό για τις πολλαπλές στήλες στο ListBox. Επιπλέον φτιάχτηκε για τα ListBox, EditBox, ComboBox η διόρθωση κειμένου RTL (right to left text). Μπορούσαν να εμφανιστούν κείμενα με εβραϊκά ή αραβικά γράμματα, αλλά ο δρομέας δεν πήγαινε σωστά, όπως στο EditBox. Στο EditBox φτιάχνω την τελική εικόνα κάθε γραμμής στο αντικείμενο EditBox που χειρίζεται το glis4.cls UserControl, πάνω στη φόρμα. Σε αυτά τα τρία στοιχεία η εικόνα φτιάχνεται στο glis4.cls και τελικά η λύση στο πρόβλημα ήταν να βάλω ένα επιπλέον γεγονός που θα "μαθαίνει" που θα βάλει τον δρομέα σε εκείνα τα αντικείμενα που ακούν το συγκεκριμένο γεγονός.

Βρέθηκε και ένα bug στο διερμηνευτή (είχε προστεθεί τελευταία), και δεν μπορούσε να εκτελεστεί το  M2000_Editor_Information στο info.gsb. 

Παρακάτω είναι το πρόγραμμα ελέγχου/επίδειξης της νέας λειτουργίας πολλαπλών σειρών, με τις νέες μεθόδους που γράφτηκαν στην αναθεώρηση 53.

Υπάρχουν "εγγραφές" με RTL κείμενο. Επίσης ως έχει δεν διορθώνουμε τα "κελιά" του ListBox. Αν αλλάξουμε τη Σημ 2 με την Σημ 1 (η Σημ είναι το REM της BASIC), δηλαδή αν ενεργοποιήσουμε την εντολή  ΔιόρθωσεΛίστα= Αληθής  (όπου το ΔιόρθωσεΛίστα έχει συνδεθεί με την ιδιότητα Edit της Λίστας1) θα μπορέσουμε να διορθώσουμε. Θα δώσω άλλη φορά το πώς γίνεται να ελέγχουμε που θα γίνεται και που όχι η διόρθωση (δουλεύει με τα γεγονότα). Επίσης η ιδιότητα mychange που έχει το 0 (false) μπορεί να πάρει το -1 ή 1 ή True, και τότε όταν επιλέγουμε τη Λίστα1, γίνεται αυτόματα επιλογή κελιού όπως περνάει ο δείκτης του ποντικιού.

Οι παρακάτω εντολές δίνουν στη στήλη 4 δυνατότητα αναδίπλωσης και στη στήλη 3 στοίχιση στο κέντρο (οριζόντια). Θα προσθέσω και άλλα "χαρακτηριστικά" σε επόμενες αναθεωρήσεις.

Μέθοδος Λίστα1,"AttribColumn", "WrapText",  True, 4

Μέθοδος Λίστα1,"AttribColumn", "CenterText",  True, 3


Στην εξαγωγή κειμένου από το Editbox και τον διορθωτή της m2000 (είναι ένα εσωτερικό αντικείμενο TextEditor, που διαφέρει κάπως από το Editbox), όταν βρίσκει το TAB στο κείμενο εξάγει HTML με το pre tag, και τα χρώματα. Τώρα έβαλα και το στυλ να επιτρέπει την αναδίπλωση λέξεων - παραγράφων. 


Σταθερή ΣτοίχισηΚέντρο=2

στήλη$={Header 1
Paris
Athens
Rome
Report 1, "ברוכים הבאים الجامعات والكليات"
Nicosia
Brussels
Copenhagen
Berlin
Dublin
Luxembourg
One More
}
στήλη2$={Header 2
100
200
Report 1, "ברוכים הבאים الجامعات والكليات"
}
Όρισε Φόρμα1 Φόρμα
Με Φόρμα1, "title" ως οΤίτλοςμου$
Επίπεδο Φόρμα1 {
Διάστιχο 60
Γραμματοσειρά "Arial Black"
Παράθυρο 16, 12000,6000
Φόντο 11, 1, 0
Δρομέας 0, Ύψος Δια 2
Αναφορά ΣτοίχισηΚέντρο,"Hello World"
}
Όρισε Λίστα1 Λίστα Φόρμα Φόρμα1
Μέθοδος Λίστα1, "move", 1000,1000,7900,4500
Μέθοδος Λίστα1, "Colors", 0,1,,0,7
Με Λίστα1, "Column" ως Λίστα1.Στήλη, "Edit" ως Διόρθωση.Στοιχείου, "Transparent", Αληθές
Με Λίστα1, "maychange", 0, "NarrowSelect", Αληθές, "Edit" ως ΔιόρθωσεΛίστα, "ColumnWidth" ως ΠλάτοςΣτήλης()
Με Λίστα1,"text", στήλη$, "BorderStyle", 1 , "Default", Αληθές, "TopRows", 2, "Columns" ως ΣτήλεςΛίστας
Με Λίστα1, "Find" ως Βρες(), "List" Ως Λιστα$(), "TextColumn" Ως Στήλη$(), "AdjustColumns", Αληθές
Με Λίστα1, "ListIndex" ως Επιλεγμένο.Λίστας, "ListCount" ως Στοιχεία.Λίστας , "ShowAlways",Αληθές
Μέθοδος Λίστα1, "SetGrid", Αληθές, 5
Επιλεγμένο.Λίστας=0
Μέθοδος Λίστα1, "AppendColumn", 1,1
ΠλάτοςΣτήλης(1)=3000
ΠλάτοςΣτήλης(2)=3000
Στήλη$(2)=Στήλη2$
Αναφορά Στήλη$(1)
Επιλεγμένο.Λίστας=4
Μέθοδος Λίστα1, "insert", "**New**"
Μέθοδος Λίστα1, "Delete", Στοιχεία.Λίστας-1
Μέθοδος Λίστα1, "Delete", Στοιχεία.Λίστας-1
Μέθοδος Λίστα1, "InsertColumn", 2,2
ΠλάτοςΣτήλης(2)=600
ΠλάτοςΣτήλης(3)=600
Στήλη$(2)={Εμβόλιμη1
}
Στήλη$(3)={Εμβόλιμη2
}
Επιλεγμένο.Λίστας=0
Μέθοδος Λίστα1, "addheight", 1
εκεί=Βρες("Rome")
Αν εκεί>-1 τότε
Επιλεγμένο.Λίστας=εκεί
Μέθοδος Λίστα1, "addheight", 1
Τελος Αν
Μέθοδος Λίστα1,"AttribColumn", "WrapText", True, 4
Μέθοδος Λίστα1,"AttribColumn", "CenterText", True, 3
Σημ 1. ΔιόρθωσεΛίστα= Αληθής
Σημ 2.
ΔιόρθωσεΛίστα= Ψευδής
// Θέλουμε η φόρμα να αλλάζει μέγεθος. Από κάτω δεξιά.
Με Φόρμα1,"Sizable", Αληθές, "SizerWidth", 6*πλάτος.σημείου
Συνάρτηση Φόρμα1.Unload {
      Πληκτρολόγιο "!"   \\ like press !
}
Συνάρτηση Φόρμα1.click {
Λίστα1.Στήλη=1
ΠλάτοςΣτήλης(Τυχαίος(2, 3))=Αν(Τυχαίος<.3->0, 600)
Μέθοδος Λίστα1, "sort",,,1, 2
}
Συνάρτηση Φόρμα1.Resize {
Επίπεδο Φόρμα1 {
Τύπος 16
Φόντο 11, 1, 0
Δρομέας 0, Ύψος Δια 2
Αναφορά ΣτοίχισηΚέντρο,"Hello World"
}
Επίπεδο { Ανανέωση }
Μέθοδος Φόρμα1, "refreshALL"
}
Μέθοδος Φόρμα1, "Show"
α$=Κομ$
Όρισε Φόρμα1 Τίποτα



Τετάρτη 19 Ιανουαρίου 2022

Ανανθεώρηση 52, Έκδοση 10 (Νέο Listbox με πολλές στήλες).

Σε αυτήν την αναθεώρηση εκτός άλλων έκανα μια μεγάλη μεταμόρφωση στο βασικό σύστημα φορμών της Μ2000. Οι φόρμες της Μ2000, αυτές που φτιάχνει ο χρήστης, έχουν ένα και μόνο ένα βασικό στοιχείο, ένα αντικείμενο glist4. Αυτό το αντικείμενο είναι ένα listbox, το οποίο είναι γραμμένο από το μηδέν, ως ένα User Control της Visual Basic 6. Όλα τα στοιχεία ελέγχου έχουν φτιαχτεί με βάσει αυτό το στοιχείο. Ακόμα και ο επεξεργαστής κειμένου, πάνω στη βασική φόρμα της Μ2000 (τη κονσόλα) έχει από κάτω αυτό το αντικείμενο. Στην ουσία αυτό που ήθελα ήταν να έχω έναν φορέα που θα στέλνει τα κατάλληλα γεγονότα. Στο ερώτημα, γιατί να έπρεπε να φτιάξω κάτι από το μηδέν η απάντηση είναι ότι ήθελα ορισμένα πράγματα που δεν τα εύρισκα αλλού, ειδικά την πληθώρα γεγονότων.

Το στοιχείο λέγεται glist4, το g από το George και το 4 επειδή αυτό είναι το τέταρτο, και καλύτερο. Καλύτερο ως προς την δυνατότητα επέκτασης. Το πρώτο που είχα φτιάξει δεν ήταν Unicode, είχε οριζόντια και κάθετη μπάρα ολίσθησης και είχε εσωτερικά σύστημα χρωματισμού, και στην πράξη ήταν ένα Editbox. Μετά έφτιαξα μια μπάρα ολίσθησης πιο ειδική, ώστε να μην έχει "άνοιγμα" 32768 σημεία, αλλά εκατομμύρια, και την έβαλα με ένα απλό listbox που έφτιαξα για να την δοκιμάσω. Από εκείνο το σημείο ξεκινάει να γράφεται με σωστό τρόπο το στοιχείο glist  (υπήρξε το 2 το 3 και το 4). Αυτό που ήθελα ήταν να μην έχει flickering (δυστυχώς τα στοιχεία της VB6 είχαν θέμα flickering). To flickering είναι αναλαμπές που φαίνονται λόγω κακής χρήσης του χρόνου, για να δοθεί η ανανέωση της προβαλλόμενης εικόνας του στοιχείου. Έχει σημασία το πότε παράγονται γεγονότα σε σχέση με το πότε σχεδιάζεται η εικόνα. Επίσης για ελάφρυνση, υπάρχει τρόπος να συσσωρευθούν ενέργειες μέσω χρονιστή (timer) ώστε σε επαναλαμβανόμενες ενέργειες πχ μια μετακίνηση της μπάρας ολίσθησης από κάτω προς τα πάνω να γίνει χωρίς  να εμφανιστούν όλα τα στοιχεία της λίστας αλλά βάσει χρόνου να ανανεώνεται το στοιχείο δείχνοντας νέες γραμμές με δεδομένα μέχρι αυτά στην κορυφή, τόσα ώστε να  νιώθουμε ότι έγιναν συνεχόμενα, με τη ταχύτητα που κουνήσαμε το χέρι μας. Να μην υπάρχουν δηλαδή καθυστερήσεις, τερμοπαίγματα, κολλήματα!

Οι στόχοι από σχεδιασμό ήταν:

  • Χρήση Unicode (έγινε πράγματι, και μπήκε και η χρήση RTL κειμένου, που γράφεται από δεξιά προς τα αριστερά). Επιπλέον μπορούν να εμφανιστούν και Unicode χαρακτήρες τύπου surrogates (με αριθμό μεγαλύτερο από 65535 που χωράει ο βασικός 16bit στο UTF16LE).. 
  • Διαφάνεια. Να μπορούμε δηλαδή να έχουμε φόντο την όποια εικόνα πάνω στην φόρμα. Και αυτό έγινε. Η διαφάνεια γίνεται με αντιγραφή στην εικόνα του στοιχείου της εικόνας της φόρμας (στο μέρος που το στοιχείο "πατάει" στη φόρμα. Υπάρχει παράδειγμα με φόρμες που ζωγραφίζουμε πίσω από ένα κουμπί (στοιχείο ΠΛΗΚΤΡΟ ή BUTTON) και δεν ξεχωρίζουμε το για πότε έγινε η αντιγραφή στο στοιχείο. Αυτό συμβαίνει γιατί "συγχρονίζονται" τα στοιχεία με τη φόρμα και όλα μαζί "ανανεώνονται".
  • Να μπορούμε στα στοιχεία της λίστας να "διορθώνουμε" με απλό τρόπο (δηλαδή να εμφανίζεται κατ΄επιλογή μας η γραμμή για εισαγωγή κειμένου). Αυτό έγινε για κείμενο με στοίχιση αριστερά και κέντρο (ακόμα δεν έχει φτιαχτεί για δεξιά στοίχιση, δεν ήταν στο σχεδιασμό, αλλά στην επόμενη αναθεώρηση σκοπεύω να γίνει και αυτό).
  • Να μπορεί η λίστα να έχει ή δικά της στοιχεία, δηλαδή σε δικό της σύστημα καταχώρηση, ή να δέχεται εξωτερικά, μέσα από εκπομπές γεγονότων. Ένα γεγονός είναι μια κλήση, για την οποία το αντικείμενο δεν γνωρίζει το πώς και το αν θα ανταποκριθεί ο "εξυπηρετητής" της. Αυτό που γνωρίζει είναι το τι θα κάνει με την επιστροφή των στοιχείων (αν πάρει στοιχεία). Στην ουσία με τα γεγονότα το αντικείμενο "Εκθέτει" ένα μέρος του, και με τις συναρτήσεις εξυπηρέτησης γεγονότων, το χειριζόμαστε. Αυτή είναι μια μορφή "subclassing". Πράγματι όλα τα στοιχεία των φορμών της Μ2000 είναι αντικείμενα που πάνω σε αυτά "πέφτουν" τα γεγονότα των glist4. Προγραμματιστικά, όταν ορίζουμε ένα στοιχείο σε μια φόρμα, το σύστημα δημιουργεί δυο αντικείμενα, το ένα πάνω στη φόρμα ως glist4 (το combo έχει δυο glist4, το δεύτερο σε νέα φόρμα, για να εμφανίζεται η "αναδυόμενη" λίστα) και το άλλο είναι αυτό που χειριζόμαστε μέσα από το πρόγραμμα της Μ2000. Και τα δύο παράγουν γεγονότα, αλλά όχι τα ίδια.
  • Επιπλέον ως "εξωτερικά" στοιχεία μπορεί να είναι περιεχόμενο (το κείμενο στο στη γραμμή της λίστας) ή η εικόνα. Δηλαδή, αντί να πάρει το κείμενο και να το "γράψει" το glist4, το παίρνει έτοιμο ως τελική εικόνα (αφού δώσει το φόντο, που μπορεί να είναι μέρος από την εικόνα του φόντου). Έτσι δουλεύει το EditBox, όπου εμφανίζει χρωματισμένο κείμενο (χρωματισμός κώδικα). Αυτό δεν γίνεται μέσα στο glist4, όμως επειδή αυτό ξέρει το "πλάτος" του, δέχεται παραγράφους για να τις "σπάσει" σε γραμμές, όπως επίσης μέσω των χειρισμών μας πάνω στο glist4, ακολουθεί το EditBox να στέλνει τις κατάλληλες έτοιμες εικόνες. Το EditBox εκτός από το glist4 και τα γεγονότα του έχει και το Document αντικείμενα, με δικά του γεγονότα. Στο Document βρίσκεται το κείμενο αλλά και η πληροφορία για το χρωματισμό. Όταν "ζητάει" το glist4 να φτιάξει την εικόνα του, το EditBox παίρνει στοιχεία από το Document, και παράγει τις εικόνες-απαντήσεις. Ο δρομέας που "αναβοσβήνει" στο κείμενο είναι μέρος της λογικής του glist4, και το EditBox το κατευθύνει ανάλογα.
  • Στο σχεδιασμό του glist4 ήταν και η χρήση στοιχείων του ως Radio Button και ως Επιλογές με "τσεκάρισμα". Και μια πιο προχωρημένη, για πολλαπλή επιλογή που γίνεται στις φόρμες διαλόγου για τα αρχεία και τους φάκελους (όπου και εκεί το ίδιο στοιχείο είναι από κάτω) . Το Combo είναι ένα στοιχείο που χρησιμοποιείται για μενού (αλλά και το Listbox μπορεί να φτιάξει λίστες επιλογής, χωρίς να είναι αναδυόμενο).
  • Λειτουργία σπρωξίματος της λίστας στο κατακόρυφο, και σπρωξίματος στο οριζόντιο (αυτό το τελευταίο ήθελα να μοιάζει σαν τις λίστες στο android, που αντί να δίνουμε διπλό κλικ τραβάμε την λεζάντα δεξιά. Αυτό πράγματι μπορούμε να το κάνουμε και αν θέλουμε το ενεργοποιούμε ή όχι. Στα μενού του Combo έχει μπει αυτή η λειτουργία. Στο Editbox ολισθαίνουμε το κείμενο με σπρώξιμο και είναι πολύ ωραίο και φυσικό σαν να τραβάμε λίγο πάνω το κείμενο για να διαβάσουμε και το ξανακατεβάζουμε, χωρίς να πάνε στη μπάρα ολίσθησης που κάνει μεγάλες μετακινήσεις, ή αν πάμε στα άκρα της να δώσουμε δυο τρια κλικ στο κάτω σημείο και μετά πάλι δυο τρια κλικ στο πάνω σημείο για να κάνουμε το ίδιο πράγμα (αλλά πιο κουραστηκό το πήγαινε έλα με το ποντικί).
  • Η κάθετη μπάρα ολίσθησης κρύβεται, και εμφανίζεται είτε όταν σπρώχνουμε το κείμενο (για να πάμε γρήγορα σε άλλα σημεία) είτε όταν περνάει από το χώρο της ο δείκτης του ποντικιού. Στις παλιότερες εκδόσεις δεν μπορούσε να αλλάξει χρώματα, όπως στις νεότερες. Στο κενό χώρο της μπάρας βλέπουμε το κείμενο! Η μπάρα "σχεδιάζεται" μέσα στο glist4, δεν αποτελεί ξεχωριστό στοιχείο ελέγχου. Σε μια παλιότερη έκδοση είχα και οριζόντια αλλά καταργήθηκε γιατί η οριζόντια μετακίνηση είτε γίνεται ως σπρώξιμο (δες προηγούμενη παράγραφο) είτε σε εγγραφή κειμένου γίνεται αυτόματη μετακίνηση βάσει της θέσης του δρομέα, και με animation. Αν στον διορθωτή της Μ2000 γράψουμε μια πολύ μεγάλη γραμμή (και δεν έχουμε το Word Wrap, αναδίπλωση λέξεων - αυτόι αλλάζει με το F1), και πατήσουμε HOME/END διαδοχικά θα δούμε την γραμμή να πηγαινοέρχεται, όμορφα!
Στην αναθεώρηση 52, άλλαξα τον τρόπο που το glist4 καταχωρεί τα στοιχεία του, έτσι ώστε να έχω και σε αυτήν την αναθεώρηση αλλά και σε επόμενες την ευχέρια να προσθέτω πράγματα χωρίς να χαλάω ότι ήδη έχω φτιάξει. Εδώ λοιπόν έβαλα ένα jsonobject αντικείμενο. Οι δομές json με ενδιέφεραν και έφτιαξα δικό μου αντικείμενο (στην ουσία είναι δυο, ο JsonArray και το JsonObject). Η παλιά δομή ήταν ένας UDT πίνακας. UDT σημαίνει User defined type, δηλαδή ένας τύπος μεταβλητής που ορίζεται από μένα. Δεν είναι αντικείμενο αλλά "μνήμη" με πεδία. Το προ της 52 αναθεώρηση UDT είχε 16 bytes το ελάχιστο για κάθε γραμμή (αν χρησιμοποιούσαμε την εσωτερική δομή), όπου δυο long (32bit, 4bytes) ήταν για αλφαριθμητικά το περιεχόμενο (content) και το επιπλέον (contentID) για τα μενού, ένα όνομα για επιστροφή τιμής, ανεξάρτητα από τη θέση).

Δείτε τη παλιά δομή. Ενας boolean εδώ είναι 16bit (ισόβαθμος με integer). To Line έλεγε αν θα μπει γραμμή αντί για κείμενο (οπότε δεν μπορεί να επιλεχθεί και ο "δρομέας" της λίστας, κατακόρυφος περνάει πάνω από τέτοιες γραμμές, αν υπάρχει κείμενο μπαίνει "μη ενεργό" κείμενο αντί για γραμμή.) Κανονικά θα έπρεπε το Line να είναι πριν το content, για να είναι οι 32bit αριθμοί σε όρια καθαρών 32 bit περιοχών, αλλά δεν με χάλαγε όπως ήταν, αφού έκανα τη δουλειά μου.

Private Type itemlist
selected As Boolean
checked As Boolean
radiobutton As Boolean
content As String
contentID As String
Line As Boolean
End Type

Τώρα η νέα δομή είναι αυτή, με μόλις 8 bytes: Αν έχουμε κενό στοιχείο στη λίστα δεν θα φορτωθεί το content το αντικείμενο JsonObject. Το Flags προς το παρόν χρησιμοποιείται ως προς τα 5 λιγότερα σημαντικά bits. Δηλαδή τώρα χρησιμοποιώ bit για τους boolean, και εδώ έχει προστεθεί ένα ακόμα που λέει ότι η γραμμή είναι κενή γιατί ανήκει απλά σε κάτι προηγούμενο

Το morerows, λέει στο σύστημα του glist4, ότι η γραμμή αυτή έχει επιπλέον γραμμές. Αυτό μπήκε νέο σε αυτή την υλοποίηση. Μπορούμε να έχουμε δυο ή περισσότερες γραμμές σαν μια γραμμή. Αν και το glist4 δεν είχε ως στόχο να έχει στοιχεία με διαφορετικό ύψος (εκτός από την επικεφαλίδα), με το σύστημα με το morerows μπορεί μια γραμμή να έχει ύψος σε ακέραια πολλαπλάσια. Αυτό είχε δυο τρεις επιπτώσεις στο κώδικα, που αντιμετωπίστηκαν, όπως η ταξινόμηση. Στη ταξινόμηση η λύση ήταν απλή, πριν την ταξινόμηση μαζεύουμε σε μια συνέχεια μόνο τις γραμμές που δεν "ανήκουν" σε άλλες. κάνουμε την ταξινόμηση και μετά απλώνουμε τα στοιχεία βάζοντας τόσες "κενές" γραμμές όσες μας λέει το morerows.  Επειδή όλα τα "βαριά" στοιχεία είναι στο JsonObect, η μετακίνηση των itemlist είναι άνευ πρακτικής καθυστέρησης (αντιγράφονται μόνο τα 8bytes). To morerows έχει ανώτερη θετική τιμή το 32767, αλλά δεν υπάρχει περίπτωση να καλυφθεί κάτι τέτοιο. Αν μια λίστα έχει ύψος σε σειρές, πχ 10 ή 20 τότε ένα στοιχείο ύψους πάνω από 20 θα σήμαινε ότι δεν θα μπορεί κανείς να δει όλα τα στοιχεία του (αν είναι γεμάτο) σε μια όψη της λίστας, αλλά θα έπρεπε να ολισθαίνει την λίστα για να βλέπει. Εδώ να κατανοήσει ο αναγνώστης ότι το στοιχείο glist4 ολισθαίνει τη λίστα όχι κατά pixel αλλά κατά γραμμή.  Μπορεί να είναι ωραία η κατά pixel κάθετη ολίσθηση, αλλά προς το παρόν, δεν γίνεται κάτι τέτοιο. Αυτό που γίνεται είναι ότι αν κρατήσουμε πατημένο το αριστερό πλήκτρο και σπρώξουμε την λίστα πάνω ή κάτω και αυτή ακολουθεί, και αν συνεχίσουμε μπαίνει αυτοματισμός και τρέχει η λίστα προς την κατεύθυνση που "σπρώχνουμε" (αυτό δεν το κάνουν στοιχεία της VB6, και παλαιότερα το έκανε ο Firefox για να σπρώχνεις τη σελίδα με το ποντίκι, και σου έδειχνε έναν κύκλο και ένα βελάκι). Το να έχουμε και ολίσθηση κατά pixel για κάτι που μας ενδιαφέρει να τρέχει γρήγορα, δεν λέει, αφού πρακτικά λόγω ταχύτητας θα χαθεί αυτή η "ελάχιστη ολίσθηση".


Private Type itemlist
Flags As Integer
morerows As Integer
content As JsonObject
End Type

Τι είναι λοιπόν το JsonObject και γιατί βοηθάει εδώ; Κάθε στοιχείο στο JsonObject έχει ένα κλειδί και μια τιμή. Η τιμή μπορεί να είναι μια από τα στοιχεία: null, true, false, αριθμός, αλφαριθμητικό, jsonObject, jsonArray. Το JsonArray έχει και αυτό στοιχεία αλλά δεν έχει κλειδιά, αλλά δείκτη, με αρχή το 0. Αν ο πίνακας δεν έχει στοιχεία (γίνεται) και δώσουμε ότι το 5ο στοιχείο (4 στο δείκτη) είναι το 100, τότε θα φτιαχτεί το [null, null, null, null, 100], δηλαδή οι τιμές που δεν δόθηκαν θα είναι null. Γενικά ότι ζητάμε και δεν υπάρχει επιστρέφει τιμή null. Έτσι αν έχω γράψει τις δυο πρώτες στήλες σε μια γραμμή (από το 1 έως το 2) θα έχει το [null, "πρώτη στήλη", "δεύτερη στήλη"] με τη διαφορά ότι δεν θα βλέπω τα ελληνικά (θα είναι κωδικωποιημένα ώστε στο αλφαριθμητικό να είναι μόνο χαρακτήρες ASCII κάτω από το 128). Κατά την εμφάνιση πχ 5 στηλών, οι τρεις που λείπουν θα επιστρέφουν null, και δεν θα απασχολούν μνήμη. Αν γράψω όμως στην πέμπτη στήλη τότε η 3η και η 4η θα πάρουν το null ως εγγραφή. Το JSON σημαίνει "JavaScript object notation", δηλαδή είναι ο τρόπος που αποθηκεύει στοιχεία η javascript (και μπορούν να εμφανιστούν δεδομένα ως κείμενο, τα οποία βολεύει η δομή για την μεταφορά τους μέσω διαδικτύου). H M2000 έχει και αντικείμενο XML. Το έχω γράψει και αυτό! Δεν με συμφέρει όμως η χρήση του εδώ. Το XML είναι και αυτός ένας τρόπος αποθήκευσης στοιχείων, που δίνει κείμενο ως εξαγώμενο. Υπάρχει διαφορά μεταξύ Json και XML. Το json έχει μια τη χρήση του μονοπατιού. Δηλαδή το μονοπάτι (το βάζω σε "αυτάκια" όπως λέμε τα διπλά εισαγωγικά) "C.1.[ALFA BETA]" λέει ότι στο JsonObject στο κλειδί "C" περιμένω ένα JsonArray και ειδικότερα στο δεύτερο στοιχείο του (το 1 είναι το δεύτερο) περιμένω ένα JsonObject, με κλειδί ALFA BETA (έχει διάστημα και για το λόγο αυτό το έβαλα σε τετράγωνες αγκύλες []). Αν περιμένω να πάρω τιμή και δεν υπάρχει όλη αυτή η δομή θα πάρω το null, αφού δώσω το ItemPath(). Αν θέλω να γράψω στο μονοπάτι αυτό την τιμή 100 τότε θα το φτιάξει άμεσα με μια AssignPath. Αν δεν ξέρω τι έχει το μονοπάτι τότε θα το ψάξω! (υπάρχουν εντολές). Το XML  είναι διαφορετικό αρκετά. Αρχικά υπάρχει μια λίστα από στοιχεία σε διάταξη. Κάθε στοιχείο του XML είναι ένα άλλο XML ή απλά είναι κείμενο (το οποίο είναι ό,τι θέλουμε να είναι). Δεν υπάρχει διάταξη με άμεση αναζήτηση με κλειδί όπως το json (πίνακας κατακερματισμού). Στο json τα κλειδιά είναι μοναδικά. Στο XML δεν υπάρχουν κλειδιά! Τα στοιχεία είναι σε μια διάταξη τύπου συλλογής (collection). Κάθε στοιχείο λέγεται child. Η σειρά μετράει. Αν θέλουμε να "μαρκάρουμε" κάποιο στοιχείο, τότε θα δώσουμε ένα χαρακτηριστικό (attribute) το οποίο έχει μοναδικό όνομα για το στοιχείο, και έχει τιμή που γράφεται σε αλφαριθμητικό (μπορεί να είναι αριθμός) αλλά δεν μπορεί να είναι XML. Έτσι το XML ομαδοποιεί τα στοιχεία, σε μια σειρά και τα διαφοροποιεί πέρα από το περιεχόμενό τους και με βάσει τα χαρακτηριστηκά που έχουμε επιλέξει. Το HTML λειτουργεί ως ένα XML. Στο XML δεν έχουμε ευρετήριο εκτός και αν το φτιάξουμε με άλλη δομή. Δεν συζητώ για το XML ως κείμενο. Στο αντικείμενο XMLdata της M2000, κάθε τελικό στοιχείο (έχει μόνο κείμενο ως δεδομένα αλλά και μια σειρά από χαρακτηριστικά) είναι ένα node, ένας κόμβος (εδώ είναι τερματικός). Κάθε node είναι αντικείμενο, και έτσι μπορούμε αντί να αντιγράψουμε τα στοιχεία από το XML να πάρουμε μόνο τους δείκτες του και να τους "καρφώσουμε" σε μια άλλη δομή, για επιλεγμένα σοτιχεία που θέλουμε και με τον τρόπο που μας βολεύει. Έτσι οι δυνατότητες του XML είναι πάνω στη μετέπειτα επεξεργασία, και όχι ειδικά πάνω στην βασική δομή του! Σε αντίθεση το JSON είναι φτιαγμένο για να δουλεύει άμεσα, να γράφουμε και να αλλάζουμε τιμές! Στο JsonObject δεν θα βάλουμε ποτέ ένα JsonArray σε δυο σημεία, αν χρησιμοποιούμε τα AssignPath. Στο XML αν θέλουμε βάζουμε το ίδιο στοιχείο εκατό φορές, δηλαδή το δείκτη του "μοναδικού" στοιχείου, μπορούμε να το γράψουμε σε άλλες 99 θέσεις χωρίς πρόβλημα! Στην παραγωγή του "κειμένου" θα έχουμε 100 φορές το στοιχείο. Αν τώρα από το κείμενο πάρουμε πάλι το XML θα έχουμε 100 διαφορετικά αντικείμενα εκτός αν έχουμε τόσο καλό parser, που βρει τα όμοια και δημιουργήσει το αρχικό. ένα συν 99 αντίγραφα δείκτη. Στο Json δεν γίνεται αυτό. Η "serialize" μορφή (το κείμενο) δίνει πάντα το ίδιο αποτύπωμα όταν περάσει στη δομή, στο αντικείμενο, με αυτό που έκανε την παραγωγή του. Στο Xml επειδή "παίζουμε" με δείκτες συνέχεια γίνεται να έχουμε μπερδέματα όπου ο δείκτης του αντικειμένου είναι και μέρος του, στοιχείο του. Έχει προβλεφθεί στο κώδικα για το XML στη Μ2000 ο έλεγχος "κύκλου" κατά το serialize του XML. Στο json δεν παίζουμε με δείκτες (αν και μπορούμε πρόσκαιρα να πάρουμε ένα αντικείμενο σε δείκτη), αλλά παίρνουμε το κείμενο (τη serialize μορφή) και σε ένα νέο εισάγουμε τα δεδομένα και αυτό τα κάνει πάλι "δυαδικά" με νέους δείκτες. Μια διαφορά μεταξύ json και xml είναι ότι στο xml μπορούμε εύκολα να αλλάξουμε διάταξη στοιχείων (επειδή είναι collection, και μπαίνει και βγαίνει εύκολα στοιχείο), σε αντίθεση, στο JsonObject δεν υπάρχει διάταξη (μόνο κλειδιά), ενώ στο JsonArray, η παρεμβολή/διαγραφή στοιχείου, σημαίνει μετακινήσεις (για μικρούς πίνακες δεν μας χαλάει αυτό). Θα μπορούσε κανείς να υλοποιήσει το JsonArray με collection, αλλά στην ουσία, η σωστή χρήση του πίνακα ως πίνακα του δίνει πλεονέκτημα. Το νιοστό στοιχείο διαβάζεται άμεσα, ενώ για το collection,  που μπορεί να είναι ένα btree, ή κάτι ανάλογο, θέλει κάποιες "επισκέψεις" σε σελίδες. Γενικά όπου αλλάζουν περισσότερο τα δεδομένα και λιγότερο η δομή, βάζουμε json. Πχ για έναν επεξεργαστή κειμένου η μορφή XML θα βοήθαγε περισσότερο γιατί προσθέτουμε παραγράφους ή άλλα "αντικείμενα", σε μια διάταξη και μπορούμε να πάμε στο μέσον και να σβήσουμε. Στο αντικείμενο Document δεν έβαλα XML (δεν το ήξερα τότε, είναι παλιό αντικείμενο, η βάση του είναι 15+ ετών, πολύ πριν φτιαχτεί το glist4, το χρησιμοποιούσα στο αντικείμενο Εγγραφο), αλλά μια διπλή συνδεδεμένη λίστα. σε πίνακες (!!) με τον para() να έχει UDT το οποίο έχει άλλους δυο πίνακες 

Ο τύπος Paragraph έχει από 4 bytes δείκτης σε πίνακα (όταν τον φτιάξουμε), επί δύο, αφού έχουμε δυο πίνακες, και δυο ακόμα 32bit αριθμούς. Τα colordata() και startfrom μπήκαν για τον χρωματισμό κώδικα. Ο πίνακας content() έχει "σπασμένη" την παράγραφο βάσει του πλάτους του στοιχείου προβολής ή είναι ολόκληρη η παράγραφος στο πρώτο στοιχείο του πίνακα αν το έγγραφο δεν είναι συνδεδεμένο με στοιχείο προβολής, ή αν έχει βγει το Word Wrap (κουμπί F1)/ Για να "σπάσει" τη παράγραφο το αντικείμενο "σηκώνει" ένα γεγονός. Αν αυτό είναι συνδεδεμένο τότε γίνεται το σπάσιμο αλλιώς λέμε "Στο Καιρό" και δεν κάνει κάτι άλλο, τοποθετεί το στοιχείο σε μια γραμμή, στη πρώτη.
Τα πλήκτρα  F6, F7, F8 στον διορθωτή και στο EditBox, δουλεύουν σαν σελιδοδείκτες (Εγγραφή της παραγράφου και του δρομέα αν δεν υπάρχει προορισμός, μετακίνηση αν δεν είναι ο δρομέας στο προορισμό, και διαγραφή αν είναι στο προορισμό, όλα με ένα κουμπί). Στην ουσία ο σελιδοδείκτης δείχνει στο Para() άμεσα. Αν έχει διαγραφεί τότε  το lines θα είναι 0, οπότε είναι άκυρος ο δείκτης, και αυτό το ελέγχει εύκολα το πρόγραμμα και βγάζει τα "σινιάλα", απενεργοποιεί τον σελιδοδείκτη. Αν είχαμε XML δεν θα είχε νόημα ένα νούμερο σειράς, που εύκολα έχουμε σε αυτό (αφού μπορούν να παρεμβληθούν άλλες παράγραφοι), παρά μόνο ο δείκτης στο αντικείμενο, και θα μπορούσε αυτός να καταχωρηθεί. σε ένα πίνακα "σελιδοδεικτών". Σε κάθε διαγραφή παραγράφου θα κοιτάζαμε μήπως την έχουμε στο πίνακα για να την αφαιρέσουμε. Το αντικείμενο Document έχει και ένα εκπληκτικό τρόπο να βρίσκει τον αριθμό λέξεων σε χιλιοστά δευτερολέπτου! Πχ βρήκε ότι το Info.gsb έχει 164322 λέξεις, άμεσα, σε ένα κείμενο 34535 παραγράφων.(πατάμε το F9 και μας δίνει το νούμερο). Επίσης μπορούμε να πάρουμε και όλες τις λέξεις με την αριθμό εμφάνισης. (είναι ένας εκπληκτικός κώδικας, που κρατάει δείκτες και κάνει δυαδική αναζήτηση, για να βρει τις λέξεις, δεν φτιάχνει αλφαριθμητικά, που θα ήταν χρονοβόρο και θα έκανε την μνήμη κομμάτια. Γράφτηκε πριν φτιάξω το αντικείμενο FastCollection με Πίνακα κατακερματισμού) (δείτε το Private Function CountWords2(a$, uLCID As Long) As Long στο myDoc.cls στο git) (είναι εκπληκτική η Visual Basic 6, γιατί μπορεί να παίζεις με δείκτες όπως στη c, και επιπλέον μπορείς να εκτελείς την Μ2000 και να την σταματάς να γράφεις κώδικα και να συνεχίζεις την εκτέλεση, μέχρι να διορθώσεις όλα τα λάθη ή να διαμορφώσεις το αποτέλεσμα όπως θα έπρεπε να ήταν και δεν είναι).

Private Type Paragraph
content() As String
colordata() As Long
startfrom As Long
lines As Long 
End Type

Dim DocParaBack() As Long, DocParaNext() As Long, para() As Paragraph

Στo glist4, σε μια γραμμή, στο JsonObject μπαίνουν κλειδιά όπως το C και το P, το C.1 είναι το μονοπάτι για το περιεχόμενο της 1ης στήλης στη συγκεκριμένη σειρά, ή γραμμή. Το P.1 αν υπάρχει έχει ιδιότητες για το C.1.   Όταν θέλει να εμφανίσει τη στήλη 1 το glist4, τότε κοιτάει για τις ιδιότητες. Κοιτάει αν υπάρχει στο στοιχείο, αν όχι τότε αν υπάρχει στη σειρά (στο P.0), αν όχι τότε αν υπάρχει για όλο, και αν όχι τότε σίγουρα είναι false. Μπορεί όμως να προκύψει true σε κάποιο προηγούμενο. Προς το παρόν όταν έχουμε μια μόνο στήλη δεν κοιτάει αυτά τα πράγματα το glist4. Μπορούμε να έχουμε δυο στήλες αλλά η δεύτερη να μην φαίνεται (να έχει πλάτος 0). Δεν θα απασχοληθεί καθόλου μνήμη για την δεύτερη στήλη σε κάθε σειρά!

Προς το παρόν χρειάζεται ακόμα συμπλήρωση ο κώδικας, αλλά προτίμησα να ανεβάσω την 52, γιατί ήδη αρκετές αλλαγές και θα προκύψουν και άλλες, και ίσως κάτι να αλλαχθεί (δεν είμαι και θεός να τα κάνω όλα με τη μία). Παράλληλα με αυτή την εργασία (που κάνω αφιλοκερδώς), έτυχε στις πρώτες μέρες του Ιανουαρίου να βοηθήσω στην κατασκευή μιας αυτόματης πόρτας, γράφοντας κώδικα σε c++ για μικρο-ελεγκτή τύπου ARDUINO.


Δείτε αυτό το παράδειγμα, Καντε αλλαγές να δείτε πως πάει, δεν έχω βάλει γεγονότα από τη λίστα1 για να μην γίνει ακόμα πιο βαρύ. Δείχνει πως προσθέτουμε στήλες, πως αλλάζουμε ύψος γραμμής (σε πολλαπλάσιο του ύψους γραμμής), Πως βρίσκουμε μια γραμμή από το όνομα στη πρώτη στήλη. Πώς βάζουμε επικεφαλίδα που δεν ακολουθεί την ολίσθηση. Πως αλλάζουμε προγραμματιστικά το πλάτος στήλης, πώς κρύβουμε μια στήλη, πως αλλάζουμε μέγεθος με το ποντίκι μιας στήλης (με δεξί κλικ ή με shift αλλάζουμε το μέγεθος δυο συνεχόμενων στηλών ενώ κρατάμε το συνολικό μέγεθος αυτών το ίδιο),

Σταθερή ΣτοίχισηΚέντρο=2

στήλη$={Header 1
Paris
Athens
Rome
Tirana
Nicosia
Brussels
Copenhagen
Berlin
Dublin
Luxembourg
One More
}
στήλη2$={Header 2
100
}
Όρισε Φόρμα1 Φόρμα
Με Φόρμα1, "title" ως οΤίτλοςμου$
Επίπεδο Φόρμα1 {
Διάστιχο 60
Γραμματοσειρά "Arial Black"
Παράθυρο 16, 12000,6000
Φόντο 11, 1, 0
Δρομέας 0, Ύψος Δια 2
Αναφορά ΣτοίχισηΚέντρο,"Hello World"
}
Όρισε Λίστα1 Λίστα Φόρμα Φόρμα1
Μέθοδος Λίστα1, "move", 1000,1000,7900,4500
Μέθοδος Λίστα1, "Colors", 0,1,,0,7
Με Λίστα1, "Column" ως Λίστα1.Στήλη, "Edit" ως Διόρθωση.Στοιχείου, "Transparent", Αληθές
Με Λίστα1, "maychange", Αληθές, "NarrowSelect", Αληθές, "Edit", Ψευδές, "ColumnWidth" ως ΠλάτοςΣτήλης()
Με Λίστα1,"text", στήλη$, "BorderStyle", 1 , "Default", Αληθές, "TopRows", 2, "Columns" ως ΣτήλεςΛίστας
Με Λίστα1, "Find" ως Βρες(), "List" Ως Λιστα$(), "TextColumn" Ως Στήλη$(), "AdjustColumns", Αληθές
Με Λίστα1, "ListIndex" ως Επιλεγμένο.Λίστας, "ListCount" ως Στοιχεία.Λίστας , "ShowAlways",Αληθές
Μέθοδος Λίστα1, "SetGrid", Αληθές, 5
Επιλεγμένο.Λίστας=0
Μέθοδος Λίστα1, "AppendColumn", 1,1
ΠλάτοςΣτήλης(1)=3000
ΠλάτοςΣτήλης(2)=3000
Στήλη$(2)=Στήλη2$
Αναφορά Στήλη$(1)
Επιλεγμένο.Λίστας=4
Μέθοδος Λίστα1, "insert", "**New**"
Μέθοδος Λίστα1, "Delete", Στοιχεία.Λίστας-1
Μέθοδος Λίστα1, "Delete", Στοιχεία.Λίστας-1
Μέθοδος Λίστα1, "InsertColumn", 2,2
ΠλάτοςΣτήλης(2)=600
ΠλάτοςΣτήλης(3)=600
Στήλη$(2)={Εμβόλιμη1
}
Στήλη$(3)={Εμβόλιμη2
}
Επιλεγμένο.Λίστας=0
Μέθοδος Λίστα1, "addheight", 1
εκεί=Βρες("Rome")
Αν εκεί>-1 τότε
Επιλεγμένο.Λίστας=εκεί
Μέθοδος Λίστα1, "addheight", 1
Τελος Αν
// Θέλουμε η φόρμα να αλλάζει μέγεθος. Από κάτω δεξιά.
Με Φόρμα1,"Sizable", Αληθές, "SizerWidth", 6*πλάτος.σημείου
Συνάρτηση Φόρμα1.Unload {
      Πληκτρολόγιο "!"   \\ like press !
}
Συνάρτηση Φόρμα1.click {
ΠλάτοςΣτήλης(Τυχαίος(2, 3))=Αν(Τυχαίος<.3->0, 600)
Μέθοδος Λίστα1, "sort",,,1, 2
}
Συνάρτηση Φόρμα1.Resize {
Επίπεδο Φόρμα1 {
Τύπος 16
Φόντο 11, 1, 0
Δρομέας 0, Ύψος Δια 2
Αναφορά ΣτοίχισηΚέντρο,"Hello World"
}
Επίπεδο { Ανανέωση }
Μέθοδος Φόρμα1, "refreshALL"
}
Μέθοδος Φόρμα1, "Show"
α$=Κομ$
Όρισε Φόρμα1 Τίποτα