... and you did read Primer I first, didn't you? :-)
In this sequel to the Primer, imaginatively calld Primer II (damn, I'm good) we're going to deal with a little more complicated matters, do a little on screen and check out the wonderful world of files.
With all the stuff from Primer I under our belt, we're ready to get serious and start writing some real stuff. Now is the right time to consider something important and often ignored aspect... your writing style.
you're a newcomer to programming you probably haven't got a clue how to
format your code, but neither do you have any bad habits that you have
to get rid of. If you're an old hat this is the right moment to consider
your style, and perhaps reconsider your style.
Style's just for En Vogue readers.
Nope, it's not. Style is what keeps code readable, even after a number of years, by other persons who don't even speak the same (programming?) language... Best of all, using a consistent style will save you time, and help you solve bugs much quicker.
I'm not going to tell you to use a certain style. That's entirely up to you. (Well, actually I am, but feel fry to cause yourself some troubles :-)) Here are just a few suggestions...
This is ugly:
; survival guide 4_1_100 styleFortunately, a good editor handles this for you. Here are three options:
; survival guide 4_1_110 styleMuch better!
The nice thing about programming in Basic is, well, it's basic. That means most keywords related to flow control come in pairs, so there's little discussion of when to indent and when not. You can see how everything inside a block is indented so you can easily match up the 'pairs' of if / endif, for / next etc.
Some programmers like to enter multiple statements on a single line (using the colon ':'). Here's a horrid example:
; survival guide 4_1_120 styleIt's a matter of taste, but especially beginners should try NOT to do that. (Which is why I didn't mention the colon before :-)) Here's another way to indent the first example above, and this time I've chosen to break the If / Then line apart:
; survival guide 4_1_130 styleAh, that's better!
Obvously it does make sense to name variables and procedures in such a way that they make sense if you want to re-read or re-use your code. The use of 'simple' names (as 'n.l', 'c.l' etc.) is great for short, compact loops and areas where you can clearly see what the variable is or does. Once code gets longer / more complex, it's better to replace them with meaningful names ('loopcounter.l', 'customernumber.l' etc.).
To further clarify the meaning of a variable, you could make use of capitals, or underscores:
customernumber = 100 ; difficult to readIn C(++) and Windows API the use of capitals is common, while for example (former) GfaBasic programmers tend to use underscore. The same applies to procedure names and parameters. First the 'ugly' version that nobody will understand in five years...
; survival guide 4_1_200 styleIt's probably better to to it this way:
; survival guide 4_1_210 styleIf you edit your code in either jaPBe or the PB IDE, you can see in the bottom line of your window the exact sequence and naming of your procedure's parameter. When you were entering the line...
BillCustomer("Fred",79)... you could see at the bottom:
Line: 26 Colum:7 BillCustomer(CustomerName.s,Amount.l)Using sensible names makes you work faster as you do not have to look inside the procedure to see what each parameter is supposed to do.
Wow. I just realized I haven't talked about folding yet (except here but you might have missed that). Better do it now.
Load the code above, and place the cursor on the line with the 'Procedure' statement. Now hit [F4] or click on the 'minus' symbol....Whohaaa! Hit it again!
It turns out you can fold and unfold procedures at will. If you have large blocks of code this turns out to be a great feature to keep an overview of your work. (You can use folding on other things as well, play around with the editor settings if you dare.)
You can (un)fold ALL procedures in your code at once using [Ctrl] + [F4].
I decided to add this little section, as it simply does make sense (yet few people do it, it seems). As you can see in the 'nice fido' example above, I've added a few addtional comment lines immediately after the start. Although I've tried to pick smart, meaningfull parameter names (such as 'CustomerName' and 'Amount') there may be some additional information on those parameters. What's the range? What does the procedure actually do? What is the result returned?
By adding those few lines at the start of the procedure you can save yourself (and others) a lot of headaches. Just go to the procedure, unfold it if necessary, and check out those comments.
In the PB IDE you don't have to look for the procedure yoruself, the IDE will jump towards the proper line. Try it. Use the 'nice fido' code above again, and place the cursor on line 24 on 'BillCustomer'. Now press [Ctrl] and doubleclick. Et voila, the IDE jumps towards the line where you defined that procedure.
To go back to where you came from, hit [Ctrl] + [L].
Those keyboard shortcuts make life a lot easier. I've listed a few here.
CodeCaddy can reformat your code, though originally it wasn't written for that purpose. Its original intention was to have an instant 'lookup' tool. With CodeCaddy you put a cursor on any statement or command, and hit [Ctrl] + [F1]. CodeCaddy will then pop up and show you the relevant piece of code (even if it's part of an include that you haven't opened yet in the IDE). Why do I mention this here? Because this section is about documenting. It's a good idea to include some comments and a good description of the procedure parameters at the start of your procedures, immediately after the Procedure statement. Using either the PB IDE with [Ctrl] + doubleclick, or CodeCaddy with [Ctrl] + [F1] you then have a quick reference immediately under your fingertips.
Variables declared and used within procedures are local. Unless stated otherwise.
If you write code you want to re-use, you may consider using the Protected keyword with a list of all local variables. This allows for two things:
1. you cannot accidently change a global variable that uses the same nameTo beginners I would suggest not to use the Shared keyword, as bugs might be hard to trace when (re)using older code. Better use the Global keyword in those cases. Unless you're an expert, of course.
In normal code, you should consider starting at the top of your code with a list of all constants and global variables. If you're writing a large collection of small procedures you want to use in other code (a very common practice amongst programmers :-)) you may consider adding a list of global variables to the beginning of the procedure as well, if only as a reference and reminder of the global variables used within that specific procedure.
I would suggest to always use Protected as the second line of your procedure (immediately below the line with Procedure()) to define all local variables inside that procedure. You may consider adding a second Global line to list all global variables you access from within this procedure. I tend to do that in 'reusable' procedures in include files. Here is (another) example (yes, I just talked about this a minute ago here, it's either my old age or because I think it is so important...):
Procedure.l here_is_a_sample(race.s,driver_number.l,start_position.l,bad_weather.l)As a generic approach, I tend to list all local procedure parameters in the second line immediately below the Procedure() statement, followed by the third line with a list of all global variables being accessed. (Yes, I know it is not necessary to list globals again, it's just for my own reference.) Yes, I know. As a generic approach, I tend to list all procedure parameters with more detailed use.
In the first few lines of the procedure I use multiple comment lines, to assist my failing memory on the workings of the procedure. The first three blocks I use like this:
Some like it, some hate it. This is strictly a matter of taste!
Postfix: old style basics used special signs to identify variable types on every use of that variable, a% for long integers, b& for words, etc. After the initial declaration as in a.l or b.w you're no longer required to specify the type. You're free to do so though. You could use a postfix of your own to identify different variables related to the same thing. A rather silly example:
user_name.s = "this is a test"For a long time, programmers in various dialects (like C or Pascal) have somewhat looked down upon the postfix variable type declaration in basic. A funny thing is that many modern approaches to programming are doing a similar thing, yet in prefix format... Have a look at the Windows API function SendMessage() and see how they specify their parameters...
LRESULT SendMessage(Ignore the code itself, it's c(++) but pay good attention to the variable names used: hWind, wParam, lParam... it's a so-called polish notation, where the letters in front of the variable (and part of the variable name, obviously) help the programmer remember what the variable actually does, what type it is, and / or what it is used for. For example, wParam is a variable called 'wParam' (the w is part of the variable name) and contains / expects a word, etc. Geez. That's something Basic programmers have been using for ages!
For advanced users / experienced geezers only...
There are basically two ways to interact with users: using the console, or using a window.
The 'console' is more or less equivalent to command prompt, the old Dos prompt or a Dos box. A console program is still supposed to run under (the) windows (core) though, which means it doesn't work without Windows up and running.
When compiling you should tell PureBasic the type of program you want to generate. This is done via Compiler / Compiler Options / Executable format.
Console programs interact with the user via the console. They don't open a regular Window on the desktop, but use either the console window they were started from, or open their own. Here's an example. Only commands such as Input(), Inkey(), PrintN(), OpenConsole(), CloseConsole(), Delay() etc. should be used for 'true' console programs. See the PureBasic help file for all commands related to consoles.
OpenConsole()You won't see much after you entered some data, as the screen is updated, and then immediately closed :-)
Input waits for an input. If you want to implement an 'abort' function, you may want to use Inkey. Inkey returns the Ascii value of a single key, if one was pressed.
; survival guide 4_2_150 inputConsole programs often use commandline parameters to interact with the outside world. There are two modes for consoles, which one you use is set by EnableGraphicalConsole(). See the PureBasic help file for more details.
A note to converts from other (Basic) languages: Inkey only works with consoles!
(Note: in PB 3.98 and earlier the sequence of parameters was different!)
Most programs under Windows will open a window (that's a surprise). In PureBasic there are specific commands for doing so.
OpenWindow(1,200,100,500,500,"Test",#PB_Window_SystemMenu)I typically use a variable to grab the event, as it just might come in handy for other purposes...
OpenWindow(1,200,100,500,500,"Test",#PB_Window_SystemMenu)The repeat / until part is necessary to keep the window on screen. More about that in the next section on events.
While you were entering the OpenWindow() command, you may have noticed that jaPBe and the IDE display the syntax of that command on the bottom of the editor window. This way you always have a quick reminder of what parameters a command has. The sample above opens window number 1, at location 200,100 with size 500,500. It displays a system menu (click on the icon on the left upper corner, that's a system menu), and is called "Test".
As you can see, we did not tell PureBasic the values for the two constants #PB_Window_SystemMemu and #PB_Event_CloseWindow. We didn't have to, because PureBasic already knew them. In effect, PureBasic knows a lot of constants, all Windows or PureBasic related. If you are using a constant PureBasic does not know about, it will generate an error. (This is a place where the autocomplete feature of jaPBe or the PB IDE comes in handy.)
As usual, all options for OpenWindow can be found in the help file. Place the cursor on the OpenWindow() command and press [F1].
One of the things that still baffles me is that Microsoft Windows doesn't do much when it comes to window contents itself, it doesn't even offer an option to handle things for you...
Let's say one window is overlapped by another, or part of it was hidden from view because something was dragged over it, then it's up to the application to redraw whatever was in the window! This is fairly strange and somewhat stupid, as it forces each and every program to check for 'redraw' messages, and do a redraw if necessary. Even the old Amiga OS took care of window refreshes, and kept a (bitmap copy) of the window contents to restore everything on screen when necessary.
If you draw DIRECTLY on a window YOU (your application) has to take care of the refresh, if you use the PureBasic gadgets such as the ImageGadget() then PureBasic will handle that for you. More about that later.
Of course you can have multiple windows open. I think I'll get back on that subject later, but here's the quick on it (and a little but important change for people used to 3.94).
In 3.94 you had to specify the window before you could do anything...
UseWindow(window_nr)In 4.00 you specify the window with the command:
x = WindowX(window_nr)This applies to all window related commands that were assumed to use a previously 'opened' or 'used' window.
Read this section carefully.
The following causes a lot of confusion for PureBasic newcomers... the usage of id's, numbers and handles. (What the frell, until five minutes ago I was confused myself, and sometimes still am...) The new PureBasic help file is a lot clearer on the subject, still here's my old but still somewhat valid :-) explanation...
start with the condensed version:
The condensed version
(After some fine cooperation with FR34K :-))
There are two ways to identify an object:
Be clear! The 3.94 documentation is rather misleading as it sometimes talks about 'ID' when it is actually referring to a PB specific number. Read the documentation carefully and check and doublecheck!4.00 started to fix this but it is still important to check what you are exactly dealing with.
... are used as a parameter. They help you identify things or objects. For example, every window has a 'number'. If you look at the OpenWindow() command in the help file, you can see the first parameter is such a 'number'.
OpenWindow(1,200,100,500,500,"Test",#PB_Window_SystemMenu)In the 3.94 help file these parameters are often marked with a '#' symbol, but not always, and the '#' symbol itself not used in the code itself.
(Personally, I think the # symbol in the documentation should be dropped, it's misleading as # is also used for constants.)
Here the 'number' is important to discern between different windows, window number 1 and window number 2.
OpenWindow(1,200,100,200,200,"Test 1",#PB_Window_SystemMenu)Once you have given a window a number, you can refer to that object by using the same number. For example:
ResizeWindow(1,10,10,100,100)It is possible to let PureBasic generate numbers for you using #PB_Any.
window_nr.i = OpenWindow(#PB_Any,200,100,500,500,"Test 1",#PB_Window_SystemMenu)
Under Windows 'handle' and 'ID' are often (always?) interchangable. Most of the times when a PureBasic command returns an ID, it's actually passing on a Windows handle.
Microsoft Windows uses so called 'handles' to identify each and every object. Other OS'es may use other concepts. To facilitate interaction with the OS you're working on, these specific identifiers are returned by PureBasic on some commands as 'ID'. Using ID's makes it easier to use OS specific features, for example manipulating Windows 'controls' (in PureBasic called gadgets)...
Some commands return an ID / handle when NOT using #PB_Any. For example the OpenWindow() comes in three flavours!
OpenWindow(1,200,100,500,500,"Window 1",#PB_Window_SystemMenu) ; just open a windowI often use the following coding style, to store handle as well as number, as I may need one or the other further down the line:
w_main_nr.i = 1If you try to write your code cross-platform, you may want to use the new (PB4.30) .i integer variable type. That way you make sure all handles, counters, pointers etc. are sufficiently large to deal with whatever Windows returns. Or don't specify the type at all, because as of PB4.30 the default type is .i.
Use .i and do not assume the returned pointer / handle / id will be 32 bits long on a 64 bits platform!
PureBasic can generate numbers for us, for example to take care of window numbering. Instead of specifying the window number, we can let PureBasic generate one for us.
nr1 = OpenWindow(#PB_Any,200,100,500,500,"Test 1",#PB_Window_SystemMenu)Note again:
nr = OpenWindow(#PB_Any,200,100,500,500,"Test",#PB_Window_SystemMenu)#PB_Any is a parameter for some commands. It isn't an independent function or command, so you cannot use it for generating other 'unique' numbers. The following code is wrong, run it and close Window number 1:
; bad use of #PB_AnyWell, that's an error. Here's a better way, using fixed windows numbers:
; specifying the window number manuallyBut you do not want to keep track of those window numbers yourself, so here's how to let PureBasic generate them for you.
; let PureBasic generate the numbers for us
w_main_nr = OpenWindow(#PB_Any,200,100,500,500,"Test",#PB_Window_SystemMenu)Sometimes enumeration is a good alternative to #PB_Any.
The core of PureBasic Windows programming is the event loop, where your program interacts with the user. Pay attention, people, this is essential!
As Windows is a multitasking environment (more or less, ahum), our program should wait, do nothing, and return all possible resources back to the system when idling away... all the time waiting for events to happen....
This means, in essence, that ANY program under windows contains the following loop (pseudocode):
repeatThis kind of loop is also called an 'eventloop'. We keep repeating the loop and process any incoming events or messages. Many things can generate events. Here are just a few...
In PureBasic, you can either use callbacks (for advanced users), WindowEvent(), or WaitWindowEvent().
WaitWindowEvent() does exactly what is says: it halts execution until an event passes. If an event passes, it returns a code. Our program should check that code to decide if it has to take certain action or not.
; survival guide 4_4_100 eventloopRun the code above, and not much happens. The program is waiting for events. Now resize the window and you will see events show up in the debug window.. Exit the program, now remove the semicolon on line 18 (in front of the Debug) and run again... you will see a much larger number of events pass which is correct. Windows is sending messages (events) to our window for all sorts of things. It's up to us to decide if we process them or not.
WaitWindowEvent() allows one optional parameter, if specified the program will wait for 'n' milliseconds before continueing, regardless if there was a message or not.
Under older versions of PureBasic I never saw the 'window moved' message, but it seems to be there these days (PB4.30b1). Not all messages generated by Windows are passed on, and not all messages can be seen here... Fortunately, in most cases it doesn't matter much and you can always use a 'hook' once you're digging deep(er) into PureBasic and Windows.
Al events PureBasic knows about are defined in constants with the form #PB_Event_xxxxx. Microsoft Windows generates more events, and these are also defined in constants like #WM_MOVE. Be aware that PureBasic on Linux doesn't see and / or handle those Microsoft Windows specific messages.
; survival guide 4_4_150 windoweventThe code above shows the use of WindowEvent(), WindowEvent() does NOT wait for an event, so the debug window will shot a lot of dots (no event means WindowEvent() will return 0). On line 19 you will see an extra Delay() instruction. This will tell our program to wait (at least) 50 milliseconds, and during that time return control back to Windows (and any other programs). If we don't do this, we would tie up all Windows resources, and not give Windows a chance to change the screen, display new information, or do any other necessary stuff. When you use WindowEvent() instead of WaitWindowEvent() it's up to you to make sure Windows gets a chance to do other things.
Not only a window (or the desktop in general) can generate an event, there could be more reasons why your program receives a message... Some are WinAPI related, for example a message when a timer expires, network data has come in, etc. but... Messages can be generated by all sorts of events... coming from windows, menus, gadgets, or other operating system stuff (like timers, screen refreshes, and what not).
Gadgets are items such as buttons, panels, edit boxes etc. (In Microsoft Windows called 'controls'). Menus and toolbars are other items that can generate events.
Don't waste timeslices!
Here's another example which shows how to do or check things continuously, without bothering Windows too much. If our program doesn't need to do anything, it's not going to run an endless loop wasting resources, it just gracefully hands control back to Windows until there's something to do.
there are messages they will be rapidly processed. If there are no messages,
the program will wait (for 250 msec) until it executes the event loop.
And on every timer event (set by using AddWindowTimer() to once every 2000
msec) a timer message will be processed.
; survival guide 4_4_200 windowevent
The core of PureBasic Windows programming is the event loop, where your program interacts with the user. Simply put: users click on things on the screen, and we use similar things on screen to return information to the users. Pay attention, people, this is essential!
are building blocks, that we use to piece together a nice GUI, or Graphical
User Interface. Gadgets work the same, no matter if we program under Microsoft
Windows, Amiga OS or Linux, although the gadgets themselves use the underlaying
PureBasic keeps track of all gadgets in a gadgetlist. In the past you had to specify one for each window using the command CreateGadgetList(). As of 4.30 that is no longer necessary, as the OpenWindow() command automatically creates a gadgetlist.
The helpfile contains more information on all possible gadgets and their use.
window_nr = OpenWindow(#PB_Any,200,100,500,500,"Test",#PB_Window_SystemMenu|#PB_Window_SizeGadget)As you can see in the code above, I have given each button a unique number. If you want you can use #PB_Any or Enumeration to have PureBasic generate those numbers.
Under PB4.20 you had to create a gadgetlist for each and every window, like this:
; pb 4.20As of 4.30 the OpenWindow() command now automatically creates a gadgetlist, so you can leave the CreateGadgetList() command out.
; pb 4.30Gadgetlists are still created per window. You can tell PureBasic which window to create the gadgets on, using UseGadgetList():
; survival guide 4_5_200 gadgetlist
You can build one or more menus, multiple levels deep, for each window you open.
; survival guide 4_6_200 menusThe above generates a window with a menu. Each additional window needs its own CreateMenu() statement. See the help file for all posible menu commands.
The first parameter after MenuItem() is the menuitem id. You decide what number it is. You may share the number with a toolbar button, thus creating 'shortcuts' for used menu functions.
A toolbar is very similar to a menu in use and returned values. Here are toolbars and menus in one sample:
As you can see, you can use toolbars and menus independently with each having their own id, or to access the same functions by sharing an id. It's up to you. See the help file for all possible commands.
use 24 bit images to avoid alpha channel issues under 4.40b1 and later.
Not all code has to be located in one single file. We can split up a larger project in smaller components, that will be added together when compiled.
The IncludeFile command tells PureBasic to process another file, then continue the current file. XIncludeFile does the same, but it will only include any specified files once, no matter how many XIncludeFile statements are used.
In the past PureBasic suggested the use of the extension .PBI for include files. I could never figure out why, and neither could the developers as it is no longer suggested :-)
Two additional commands worth mentioning are IncludeBinary and IncludePath. Please see the helpfile for more information.
this is one of the shortest sections so far :-))
Although basically very simple, enumeration is a very powerful tool... In PureBasic you can declare constants like this:
#f_file_open = 0If you would need to define a large number of constants, each with its own unique number (think about lists of images, menu entries, file numbers, etc.) this would mean you a. would have to type it all in, and b. would have to make sure no duplicates exist. In PureBasic, there is a very easy and powerful option: enumeration.
All constants listed between Enumeration and EndEnumeration are given an increasing value. That's it. That's it?
EnumerationHave a look at the code above. By itself, it doesn't do much. Once compiled, the constant #f_file_open will get the value 0, #f_file_save will be 1, etc.
Let's assume we are writing a small text-editor. We will need to be able to load and save files. By creating a list of the functions we need as constants, we have an easy way to match any event with a function, or match any menuitem or toolbarbutton to a function... Instead of actually specifying a number for each function, we use a constant. (To remind myself it's a function I've added an f_ prefix).
Now let's say we have created a toolbar and a menu, then we could add some items to them, the menu...
MenuItem(#f_file_open,"&Open")... and the toolbar...
ToolbarStandardButton(#f_file_open,#PB_ToolBarIcon_Open)Now both the menuitem and toolbar button generate a message with a EventMenu() of #f_file_open. Instead of checking the event against the number 0 we check it against the constant, making our code much more readable.
Another example, we have created a program with two windows, and decide to add another one. But we don't want to keep track of which window has what number. Easy, what we do is, at the start of our code, we add a small Enumeration block, where we give every window it's own number.
EnumerationThis could be used for gadgets, filehandles, etc. It will help us avoid duplicate gadget numbers etc. There are countless other similar examples. Again, enumeration helps us to quickly generate lists of constants and helps us avoid reusing numbers, that's all there is to it.
Normally Enumeration starts with 0, but it can start with any arbitrary value, as hown here:
Every new call to Enumeration would reset the counter, but you can continue (chain) the previous numbering...
Normally Enumeration starts with 0, but it can start with any arbitrary value, as shown here:
Enumeration 30 Step 5
4.9 Visual Designer.
I should play with the new visual designer. But simply haven't done so...
is a little further down.)
Since 3.94 things have greatly improved. We don't have to use UseFile(), UseDirectory() etc. anymore, and there are some other improvements as well.
If you look at the PureBasic help file, you'll find two libraries: one called 'File' and the other 'FileSystem'. Together they allow us to manage files, folders, and all data in there.
FileSize() returns the size of a file, or -1 if it doesn't exist, or -2 if the given path+name was actually a folder.
Don't worry. Everybody asks this same question some time in the forums :-)
Every (open) file is identified by a number. As usual, you can either specify these yourself, or let PureBasic do so.
OpenFile() will open an existing file, or create a new one if there wasn't one yet. The filename has to follow the restrictions of the OS you are running PureBasic on.
Note that OpenFile may fail on read-only devices, so if you plan to just read from a file it's better to use ReadFile().
file_nr = ReadFile(#B_Any,"test2.txt")
CreateFile() is used to create and open new files. If the file already existed, it will be emptied then opened.
file_nr = CreateFile(#B_Any,"test2.txt")
Unfortunately, PureBasic does not allow you to open / close files with the 'shared' flag, unless you go the WinApi way :-(
PureBasic will create a buffer for each file to speed up reading and writing. For each file open you can set the size of the buffer using:
Data is NOT written to the disk, unless you either close the file, or you force a 'flush' with FlushFileBuffers().
If the same file is opened by mutliple aplications, or if you are reading AND writing to the same file, you'd better set buffer size to 0:
PureBasic has a number of standard functions to read from and write to files.
WriteByte(...) ; write a single byteRemember that all these functions start writing the lowest byte first ('little-endian').
ReadCharacter() is a special case. It will write either 8 bits (1 byte) or 16 bits (2 bytes) depending on the Unicode mode.
Of course there is a function for reading strings:
var.s = ReadString(...) ; reads a stringReadString() continues reading data from the specified file until it encounters an 'end of line' condition: this can be either one of the following characters CR/LF ($0D $0A), CR ($0D), LF ($0A), NULL ($00), or the end of the file.
This also means that if you want to store multiple strings in a file, you must make sure they are seperated from each other. You can do this by either adding your own 'end of line' character, or by using a variant of WriteString() called WriteStringN():
CreateFile(1,"test.txt")The code above will generate a tekst file and write some strings to it. As there is no 'seperation' between 'line 1' and 'line 2' they will be appended when reading back the file. WriteStringN() actually adds a CR/LF when writing a string:
WriteStringN(1,"this is line 1")Is equivalent to:
WriteString(1,"this is line 1")You can specify how information is stored, by adding specific flags. See the section on Unicode.
See the help file for more information on:
Folder contents and FTP
The FTP library provides easy access to FTP servers and files storen on them. You can send files to the server, delete them, create folders etc. You can not directly write to them or read from them. See the help file for the different commands.
Getting the contents from a folder on an FTP server is very similar to getting them from a local folder, ie. using the regular file system functions.
; survival guide 4_10_100 file listing and ftpOf course, you'll have to use your own FTP server, user and password information :-)
Sending and receiving files
You cannot read or write data to a file on a remote FTP server, you can only send or receive complete files. Some FTP servers may support paths in filenames, some may not.
To move to a specific folder on the FTP server use SetFTPDirectory().
send a specific file from the local machine to the remote FTP server use
SendFTPFile(). See the help file for more details, but frankly that's pretty
much there is to it.
If you're a lucky bastard you got more than one screen. Isn't it great to watch your favourite movie on the second screen whilst you are coding PureBasic on the first? Or you could watch two movies at the same time! Or code two programms at the same time! :-) Well, maybe not, but still it's great to have a second monitor at hand.
Windows alles multiple monitors, and calls them 'Desktops'. (There are some cheesy flavours of 'desktops' which are NOT monitors, but I've never encountered any in the wild, so I'll ignore that, and stick to the following adagio: 2 windows equals 2 desktops.)
As of 4.31 we got the necessary commands to deal with multiple monitors (and I no longer have to code WinAPI stuff myself when dealing with them, pfew...)
desktop (monitor) has it's own origin (x/y coordinates of top left corner)
as well as height, size, refresh frequency, colour depth etc. Before you
can retrieve those parameters you first need to call ExamineDesktops()
once. The first desktop also known as the primary monitor is always located
Mouse coordinates mean little if you don't know what they refer to. There are different commands for retrieving the mouse pointer position. Where it's on the desktop or where it's on a window. This makes sense as windows could be moved around :-) User interaction is communicated with your programm through 'events', so that's how you can capture mouse clicks if you have to.
Screens are whole different beasts. The mouse pointer in there has little to do with the regular mouse pointer, and querying it is a whole different ball game. (And some day I am going to talk about it in detail, just not now. Just keep in mind that they work differently.
a little list with somewhat related or comparable commands for windows,
desktops, and the mouse, just to point you in the right direction and to
make sure you won't mix up desktop, window and screen related commands...
For any questions related
to PROGRAMMING in PureBasic do not ask me but visit the forum!
(There you will find people who are much more knowledgeable about PureBasic
than I am.) This Survival Guide is more about getting to know PureBasic
than it is about solving problems in PureBasic...