CHAPTER ONE APPLICATIONS DEVELOPED INA FEW HOURS Ring is a new programming language that focuses on the Natural Language Programming and Declarative Programming paradigms and will let you think different about programming and how to solve your problems in a better way. In little days after the first release we got thousands of downloads and many developers started learning and using the language. Their feedback are the secret behind the language progress and success. They said that Ring is powerful, beautiful and easy to learn, Some of them provided good examples about what can be done using Ring in a few hours. They are very happy with the language productivity. 1.1 Quotes about Ring “I like Smalltalk very much but now I like Ring better!” , Gal Zsolt (Hungary) “I find the language and its syntax very natural and easy to follow.” , Bhudda (United States) “Very nice approach for a new language.” , Matth Moestl (Austria) “Very interesting! I will keep an eye on it!” , Eslipak (Argentina) “I’d like to see some benchmarks. Otherwise, at first glance, it looks really promising.” , Alex Deva (Sweden) “Excellent” , Liju Sankar (United States) “I wish you the best with this project.” , David O’Neil (United States) “Just fantastic.” 2
47.
Ring Documentation, Release1.21.2 , Jose Antonio (Mexico) “This looks like it was developed by some very competent people.” , Jim Clack (United States) “The Ring programming language seems pretty interesting so far.” , Eric Johnson (United States) “Thank you for this awesome language and wonderful ready to use Qt binding.” , Martial FAESSEL “I think it’s great what he does for the community of developers and novice programming.” , Marino Esteban “Ring is just awesome. The language is so cool and fluent. I am sure it’s going to be BIG.” , Ahmed Zain “What a proud, really wish you Eng. Mahmoud Samir and Your Team moving forward ^_^ and from now , considered me a big fan of the Ring programming language.” , Zainab Mahmoud “Well guys I love this language and it appears that you have created perfect language.” , Moemen Ghulmi “Good work Mahmoud, I’ve installed ring pl, and it’s very perfect language.” , Ahmed Omar (Egypt) “Thanks for this great startup programming language. I wish you best of luck.” , Elkhouaja Khalid “Congratulations! I am very happy and I wish you Success and good luck.” , Abobasmla Hassan “Good Features of multi-use language on the Web, Mobile and Desktop.” , Abdelrhman Haider “Very interesting effort.” , Giannakakis Kostas (Greece) 1.1. Quotes about Ring 3
48.
Ring Documentation, Release1.21.2 “I am too lazy to open comment window and write message. But in your case I must say (Perfect) Really, create new remarkable language like your Ring is really perfect job. Even create editor for your language in your language with only few rows... Even noticed in rosettacode.org ! I read your previous articles and I tried these examples a few days ago and I will continue. I love Ring. P.S.: Anders Hejlsberg, Niklaus Wirth, Bjarne Stroustrup, Ada Lovelace Hall of fame is waiting....” , Martin Nedopil (Czech Republic ) “Ring seems very attractive to me through its very easy design and the Qt bindings. I like its declarative approach and the generous documentation.” , Shalok Shalom (Austria) “Ring (and plenty of extension library + Qt) is wonderful.” , Kovacs Attila (Hungary) “Since two days I’m trying Ring and I’m really impressed, in add to power commands and easy use, it’s really very efficient and very fast. Each day I hope to find the couple of the year PWCT+RING ... Maybe for my Christmas gift!!!! HO HO HO HO Continue your fantastic job and congratulations.” , Jose Le Roux (France) “There are 3 different styles, it looks like Python and C” , 64remy “I was taking a tour around Rosettacode and have found Ring. I like the syntax a lot. It’s clean and easy to understand. It looks like a very clean BASIC dialect without sigils. I can say that this is the easiest and the most BASIC-like language I’ve ever tried.” , Tomaaz “Thanks for your effort. I took a quick look and found it interesting. You are trying to follow more or less like Clipper with simple command and no rigid declaration rules. Good.” , Anand Gupta “Thanks for this wonderful language” , Vinod kc (India) “Very enlightening. good job!” , Southmountain (United States) “The thing I liked was the loop exiting” , Leon de boer (Australia) “An outstanding and easy language to program with.” , Kenneth Burgo (Philippines) 1.1. Quotes about Ring 4
49.
Ring Documentation, Release1.21.2 “I chose your language as I feel I can understand it better than other languages” , Harry Singh “I like the totality of the language, far more features than expected and the freedom of expressiveness is unique.” , Evikone “Thank you very much Mahmoud! I am using ring for many experiments and so far I love it. I really want to continue using ring and contribute what I can.” , John (SienSystem) “Sir, Very Good” , Kamlesh Patel “That’s more than a “cool” syntax, the example of writing free-form text between curly-brackets such that each word calls a function. Which could be interesting (A syntax like that would be nice for declaring text styles)” , LaurieCheers “If you browse around you see they have listed 160 contributors. This year they have entered Top 100 in the TIOBE index. Lot of effort seem to have been made to make this language pop out and catch the attention of masses.” , Htuhola “I like the idea of The Ring being in ANSI C it’s an impressive creation, and a lot of skill went into it” , Garry Taylor “Very innovative language! Syntactically clean” , CodeProject Member “The author must be commended for the readily-obvious hard work and effort that has gone into creating a rich ecosystem for his language. It seems that the language is quite extensive as well. I would find it useful to see a BNF grammar and concise coverage of its semantics.” , Xx-Leninist-1917-Xx (Reddit) “I can see the AI of the future using this technology to solve computational problems for..... the humans.” , Cryptonite (United States) “I like your programming language, I like you are going to develop mobile app using RingQt and also I appreciate your web library.” , Domenico D’Oria (Italy) 1.1. Quotes about Ring 5
50.
Ring Documentation, Release1.21.2 “Congratulations for the great work with this new programming language.” , Kenny Silva (Venezuela) “Ring is an amazingly full-featured language and so well documented (the bane of most newer languages out there!)” , Alex McCullie ” I found the language yesterday, and liked the Qt bindings, as they give a declarative way to create a QtWidgets GUI.” , Cochise Cesar “Ring does look intriguing, and I’ll be reading more of the documentation soon” , Jamie Cooper “I was recently considering designing my own dynamically-typed, prototypical language and then developing a means to compile it into C/C++. However, last night I was surfing the web and noticed a little-known language called “Ring” which you’ve recently created. I began reading the Motivation section in Wikibooks to see why the language was designed and implemented, and I was shocked to see that someone else had created a language with the same intention and need as myself. I mean, it’s mind-blowing that someone would have addressed every issue I have with the currently accepted languages. It’s crazy to think it’s only been around for a year and yet, it’s already, practically a batteries-included language.” , Gedalya (YouTube) “Thank you Mr. Mahmoud for all the wonderful work, whenever I dive in the Source code I see the great effort, further development more excellence, God will reward you with what you wish” , Azzedine Ramal “What a great joy to find this surprisingly genius language !!!. It was a total joy to go through the documentation and look at the samples in Rosetta code. Marvelous work. I would even leave my fortune to the development of this language. Keep the good work going and wishing this language will go viral. ps: Thanks for keeping array index to start with 1. It means a lot.” , Nehemiah Jacob (Sydney, Australia) “I find it very interesting. Especially the fact that it is cross platform makes it something to keep an eye on.” , Boudewijn Lutgerink “The language I like the most. Efficient, simple, easy, flexible and wonderful language.” , Roshan Ali “I like variety. The richer the toolbox, the more appropriate the tool.” , Jonathan Day (Quora) 1.1. Quotes about Ring 6
51.
Ring Documentation, Release1.21.2 “I greatly appreciated your work and congratulations on what has been achieved.” , Umberto Meglio (Italy) “Thanks a lot I just found Ring two days ago and I decided to learn it, it is amazing, the samples are helping a lot.” , Zaraki Ken “Extraordinary. well done sir” , RugbyLeague (CodeProject Member - United Kingdom) “The Ring language is pleasant. You get ahead very quickly.” , Neskuk (CodeProject Member - Switzerland) “I’m happy to use your language.” , Akhil Reddy “I am enjoying using the Ring Programming language.” , Gabriel Wendt “I’m loving this language!” , Pablo Prieto “Ring Notepad is a an example of the power of Ring. It is like NotePad++ with built in form design capability, drag and drop, object attributes etc. It is a marvelous application” , Bert Mariani (Italy) “Ring is the language that offers the greatest potential for converting programmers frustrated with the amount of time it takes to develop apps in C/C++, C# and other OOP-based languages. Considering the extent to which Ring has evolved since the first release in 2016, the Ring team has proven itself worthy of a very high achievement award in the world of programming languages. The extent to which Ring has simplified the development database apps, web apps and GUI apps is a great credit to the Ring team. Ring’s implementation of OOP and GUI based apps is far superior to C++ and C#. Another major achievement of the Ring team is the ease at which programmers can get on-line access to Ring documentation, compared to on-line C++ and C# documentation. The Ring Game Engine is truly elegant and it’s designer (s) deserve lots of credit for such an impressive bit of software.” , L Godioleskky “Strongly speaking, it is a strong and new game programming language.” , isVowel (Japan) “The language seems to have some interesting features, specially the use of braces to access object fields and methods.” 1.1. Quotes about Ring 7
52.
Ring Documentation, Release1.21.2 , Nuaua (Reddit) “The code that implements the Ring VM actually looks quite nice. There is a bunch of test code – great!” , Peterfirefly (Reddit) “Ring seems promising. It first appeared in 2016 although it’s concept is older. It is meant to be portable and can be embedded in C/C++ projects” , Wim ten Brink (Quora - Top Writer (2018)) “Interesting alternative to Lua.” , djxtc (Sourceforge) “Ring is a serious thing in the programming language landscape. One should understand its foundation before she can shape an opinion worth considering. There are several innovations outthere with a clear commitment from its designer to simplicity, flexibility and learnability. Natural language as its imagined by Ring opens a window for an unlimited set of applications otherwise impossible to think about. One can built an interactive chatbot based on a rich domain specific language in a matter of hours not months. Combined with a smart yet effective implementation of a declarative programming paradigm, Ring empowers the creation of any kind of programming language on top of it, with any set of syntax. Even the language keywords can be overwritten to serve one’s own keywords in any human language not only english. Ring is a disruption, a big intellectual step forward but also a beautiful reincarnation of several legacy ideas and best practices from other old languages like Basic, Lisp and others. Those ideas are a humanity heritage that new visionaries like Mahmoud S. Fayed, the Ring designer, but also Rick Hickey, the Clojure designer, are trying to callback in today’s world of complex programmability. An other dimension of Ring, which formed one of its basic motivations, relates to visual programming. Software is a fluid and evolutionary creature and textual code is not accessible to the most of common humans. The promise of visual programming is to empower people, enhance understandability and invite business users to the arena of software development more intuitively. Ring was made by design with an objective of having a programming language capable of better supporting the visual paradigm. Gamification is a first-class citizen in the language. This is a tremendous advantage compared to other general-purpose programming languages. Ring supports a large number of gaming libraries and open a large window of capabilities to use, not only in gaming but also in business and education applications. The best way to assessing the power of Ring is to looking into it and reflecting about the number of problems it is capable to solve.” , Mansour Ayouni (Tunisia) “I have only known ring-lang for few weeks, really love features and visions of ring: bracket access, class region, declarative and natural programming. It’s simple and beautiful the way ring deal with these paradigms. I think it’s really great work what you’re done, and doing. I want to develop ethereum client in ring language, not only but it’s style. It’ll have natural and declarative code on it’s main part. Furthermore, It will be fantastic if there is “natural language -> evm bytecode” compiler, maybe ring have the solution” , Asta “The language looks great!” , Eric Hawk “You put a lot of work in the Ring and you’re a great programmer. I saw your examples in 3D I’ve looked at 1.1. Quotes about Ring 8
53.
Ring Documentation, Release1.21.2 part of your project, it’s really great” , Rafal Jopek “Looks like a big project and an impressive piece of work” , Rochus Keller (PhD From ETH Zurich, Switzerland) “I see Ring a very useful and easy in PROGRAMMING IN REGIONAL LANGUAGE. I tried to program in my mother language Hindi ( India) using UNICODE It is working. No other language give me such an easy platform.” , Negi Manoj (India) “I’m glad I could help improve the Ring language. It has good and yet easy to understand design and architecture. I believe it has big potential ahead.” , Mounir IDRASSI (Creator of VeraCrypt) “What I like most about Ring Lang is its ease to create complex applications in WebAssembly.” , Luis Lozad “I just discovered Ring today and immediately liked it and decided to give it a try. As a senior JAVA / ADA and Python developer I am very curious about the multi-paradigm” , Bertrand Pivaty “Wow, that is pretty amazing! It’s not every day to hear about a new programming language that looks actually cool! Oh wow, didn’t even see the VB-like features there. This has some real potential!” , EternityForest (reddit) “I am a big fan of your works I look forward to contributing to some of your projects in the future.” , Adewale Azeez (Nigeria) “I am a software development manager working in a Canadian Firm. I have been around for 25 years playing with different Programming languages like Python, Perl, VB, C#, Java, REBOL, Euphoria etc. Thanks a lot for the wonderful software and the great documentations” , Lijo Joseph (Canada) “I am studying Ring (I am a senior programmer). I certainly never cease to be amazed about this language; it seems to be a wish from a marvellous lamp of computer genius” , Antonio F.S. (Spain) “I’m learning the language and so far I’m finding it wonderful. It has great potential.” , Jose Luis Cuesta (Spain) 1.1. Quotes about Ring 9
54.
Ring Documentation, Release1.21.2 “Ring is a breath of fresh air in language designs for application development.” , Gary Chike 1.2 FetchStockData Application It will fetch stock data from Yahoo and draw various types of charts. 1.2. FetchStockData Application 10
55.
Ring Documentation, Release1.21.2 1.3 Google API Shortener Application A small but very nice tool to shorten urls using the Google shortener API. 1.3. Google API Shortener Application 11
56.
Ring Documentation, Release1.21.2 1.4 Video-Music-Player Application An application to play multi-media files. 1.5 Fifteen Puzzle Game An implementation for the Fifteen Puzzle game. 1.4. Video-Music-Player Application 12
57.
Ring Documentation, Release1.21.2 1.6 Analog Clock Simple implementation for the Analog Clock. 1.6. Analog Clock 13
58.
Ring Documentation, Release1.21.2 1.7 Squares Puzzle Game An implementation for the popular Squares Puzzle game. 1.7. Squares Puzzle Game 14
59.
Ring Documentation, Release1.21.2 1.8 Samples in this book The next samples are developed in a few hours and we will introduce them through this book. The next screen shot for the Ring Notepad application 1.8. Samples in this book 15
60.
Ring Documentation, Release1.21.2 The next screen shot for the Cards Game The next screen shot for the Cards Game (Android) 1.8. Samples in this book 16
61.
Ring Documentation, Release1.21.2 The next screen shot from the Web Development chapter 1.8. Samples in this book 17
62.
Ring Documentation, Release1.21.2 The next screen shots for simple 2D Games that we will present in the Game Engine Chapter. Stars Fighter Game Flappy Bird 3000 Game 1.8. Samples in this book 18
Ring Documentation, Release1.21.2 The next screen shot for the TicTacToe 3D Game Screen Shot: 1.8. Samples in this book 20
65.
Ring Documentation, Release1.21.2 The next screen shot for the Gold Magic 800 Game The next screen shot from the RingQt3D chapter 1.8. Samples in this book 21
66.
Ring Documentation, Release1.21.2 1.9 Innovative The language comes with better support for Natural Language Programming and Declarative Programming. The inno- vation comes in supporting these paradigms with new practical techniques on the top of Object-Oriented Programming and Functional Programming. No need to know anything about (Compilers and Parsing). You get the language con- structs ready for use to create domain-specific languages in a fraction of time. Articles:- • Natural Language Programming Library : https://www.codeproject.com/Articles/1200766/Using-the-Natural-Language-Programming-Library-NLP • Natural Language Programming : https://www.codeproject.com/Articles/1138605/Natural-Language-Programming-in-the-Ring-Programmi • The Declarative Approach : https://www.codeproject.com/Articles/1222105/The-declarative-approach-of-the-Ring-programming-l • Syntax Flexibility : https://www.codeproject.com/Articles/1137388/Syntax-Flexibility-in-the-Ring-Programming-Languag • The Ring Programming Language : https://www.codeproject.com/Articles/1089887/The-Ring-Programming-Language 1.9. Innovative 22
67.
Ring Documentation, Release1.21.2 1.10 Practical Many of the Ring libraries (StdLib, WebLib, Natural Library, Games Engine, etc.) and the Ring IDE (Ring Notepad, Form Designer, etc.) are written in the Ring language itself. Ring is ready for use in production and increase the developers productivity. Check the Form Designer source code : https://github.com/ring-lang/ring/tree/master/tools/formdesigner We can run the Form Designer as Android application! 1.10. Practical 23
68.
Ring Documentation, Release1.21.2 Also we can run it in the Web Browser as a web application using WebAssembly 1.10. Practical 24
69.
CHAPTER TWO INTRODUCTION Welcome to theRing programming language! Ring is an Innovative and practical general-purpose multi-paradigm language that can be embedded in C/C++ projects, extended using C/C++ code and/or used as standalone language. The supported programming paradigms are Impera- tive, Procedural, Object-Oriented, Functional, Meta programming, Declarative programming using nested structures, and Natural programming. The language is portable (MS-DOS, Windows, Linux, macOS, Android, WebAssembly, etc.) and can be used to create Console, GUI, Web, Games and Mobile applications. The language is designed to be Simple, Small and Flexible. Also, It’s fast enough for many applications. Its Dynamic Language that compile the source code to byte code then execute it by the Ring Virtual Machine, which is integrated with the Ring Compiler in one program. In this chapter we are going to discuss the goals behind the language design and implementation. 2.1 Motivation (1) In Nov. 2011, I started to think about creating a new version of the Programming Without Coding Technology (PWCT) software from scratch. I was interested in creating multi-platform edition of the software beside adding support for Web & Mobile development. What I was looking for is a programming language that can be used to build the development environment, provides multi-platform support, more productivity, better performance, can be used for components scripting and developing different kinds of applications. Instead of using a mix of programming languages, I decided to use one programming language. I looked at many programming languages, but I discovered that I need a different language that comes with new ideas and intelligent implementation. Note: The importance of the PWCT2 project to the Ring language design is similar to the importance of the Unix operating system to the C language design. Having a specific project in the mind of a programming language designer help a lot in taking the right decisions towards a clear goal. For example, We know that the PWCT2 visual programming 25
70.
Ring Documentation, Release1.21.2 language will provide readability, this let us focus in Ring design on writability. So, we can get maximum readability and maximum writability at the same time since both languages are designed together. Also, PWCT2 as a visual language requires powerful GUI library and fast graphics. That’s why Ring comes with support for the Qt framework as it’s primary GUI toolkit. 2.2 Motivation (2) In 2009-2010, I developed a small domain-specific programming language for GUI development called Supernova. I invested many months of my time to create this language and get something that I can use for small programs. After creating Supernova, I realized that if the programming language have some new features on the top of object-oriented programming, it could help me create a domain-specific language like Supernova in days instead of many months. So Ring as a language provide these features, and using Ring we can create Supernova and enjoy its features in real-world applications development. Ring is designed to be a superset of Supernova and enable us to create many domain-specific languages. Note: The role that Supernova language played for the Ring language design is similar to the role played by the ABC language for the Python language design. When we create a programming language and try to use it in practical, we will learn a lot of lessons that help us when designing the next language. 2.3 Motivation (3) We can think of a programming language as a user interface between the user and the machine. The user could be a programmer have deep knowledge about the machine architecture, a developer interested in the application and the requirements of his/her users, or a child wants to discover programming. Also, we have many types of other users like researchers, people with deep knowledge about specific domains and so on. Providing a programming language that uses just a specific syntax doesn’t match what we learned about user interfaces. Ring is a language designed to bring translation, and customization at the level of the language syntax. It’s not about providing multiple versions of the software where each version uses a different human language. It’s about the ability to change the syntax at the project level, where using one version of Ring, we can switch between different human languages, and different customization of syntax. Instead of developing different programming languages that uses the same VM, Ring uses one compiler that support changing the syntax and this can be done many times in the same project. Note: Choosing the programming language syntax is a huge power and provide a great joy. Using Ring we transfer this power from the programming language designer to the language users. 2.2. Motivation (2) 26
71.
Ring Documentation, Release1.21.2 2.4 Motivation (4) Many programming languages for application development come and go. i.e., Becomes no longer under development! I remember having a good time while using Clipper, Classic Visual Basic, Visual FoxPro, etc. Ring is designed to be a language for application development that can stay for many decades in the future. To achieve this, Ring is a free and open-source project that works on different platforms and has a small implementation that other programmers could understand and improve. It comes with a visual implementation that could help in this process. Ring is designed and implemented in a way that enables other programmers to easily understand its design and implementation in a short time. The selection of data types, How the Garbage Collector works, How does the language support threads? All of these decisions are made in a specific way to keep the language very portable and ready for the future (As much as we can, Since we don’t know the future, but we can try to invent/predict it!). Note: We can run Ring on a very old operating system like MS-DOS. Also, we can run it using a modern platform like WebAssembly. 2.5 Motivation (5) I love programming languages, and I have used a lot of them during my work. When I think about a programming language, I feel something. It’s not about the language features; it’s about the language spirit and the unique things that can be done using this language. I am not talking about the final result, where we can use many languages in the same group to achieve the same goal. I am talking about the solution itself. the design and the code. How does it look like? Is it beautiful? What about the code size? Is it simple or complex? and things like that, which have a direct effect on source code comprehension and software development productivity. Ring is designed to include the spirit of many other languages. We don’t do this by copying features or mixing things that don’t match together! Our technique is different. It’s similar to how a child could look like his or her parents. You get the feeling, not a copy! Note: Ring is designed to be small in size, like Lua, but it comes with standard support for many programming paradigms, like Python and Ruby. This is a challenge when we talk about implementation and development. 2.6 Ring and other languages Ring is a programming language that comes with better support for Natural Language Programming and Declarative Programming. The innovation comes in supporting these paradigms with new practical techniques on the top of Object- Oriented Programming and Functional Programming. Also, Ring is influenced by the next programming languages • Lua • Python • Ruby • C 2.4. Motivation (4) 27
72.
Ring Documentation, Release1.21.2 • C# • BASIC • QML • xBase • Supernova 2.7 History In Sept. 2013 I started the design and the implementation of the Ring programming language. After 21 months of development, In May 2015 the language Compiler & Virtual Machine were ready for use! After that I spent three months testing the language again, trying to discover any bug to fix, writing better tests, by the end of August 2015, all know bugs were fixed, Writing many tests and testing automation helped a lot in getting a stable product. In September 12, 2015, most of the documentation was written. Before releasing the language I started the marketing by writing a post in Arabic language about it to my facebook profile page asking for contributors interested in the language idea after reading a short description, In the same day I got a lot of emails from developers and friends interested to contribute! Ring 1.0 is released on January 25, 2016 Ring 1.1 is released on October 6, 2016 Ring 1.2 is released on January 25, 2017 Ring 1.3 is released on May 15, 2017 Ring 1.4 is released on June 29, 2017 Ring 1.5 is released on August 21, 2017 Ring 1.6 is released on November 30, 2017 Ring 1.7 is released on January 25, 2018 Ring 1.8 is released on June 25, 2018 Ring 1.9 is released on October 6, 2018 Ring 1.10 is released on January 25, 2019 Ring 1.11 is released on September 15, 2019 Ring 1.12 is released on January 25, 2020 Ring 1.13 is released on September 15, 2020 Ring 1.14 is released on January 25, 2021 Ring 1.15 is released on September 24, 2021 Ring 1.16 is released on October 23, 2021 Ring 1.17 is released on May 14, 2022 Ring 1.18 is released on July 12, 2023 Ring 1.19 is released on December 31, 2023 Ring 1.20 is released on April 11, 2024 2.7. History 28
73.
Ring Documentation, Release1.21.2 Ring 1.21 is released on September 1, 2024 2.8 Features The Ring language comes with the next features Tip: The language is ready for production! • Free Open Source (MIT License) • Hybrid Implementation (Compiler + Virtual Machine) • Declarative programming on the top of Object-Oriented programming • Natural Language Programming on the top of Object-Oriented programming • Natural Language Programming Library • Three different styles for writing the code and you can create your style • Syntax Flexibility (You can change the language keywords and operators) • The language keywords can be translated from English to other languages (Arabic, French, etc) • Compact Syntax, No explicit end for statements (No ; or ENTER is required) • Using braces { } we can access objects and use attributes/methods as variables/functions • Using lists/objects during definition • Transparent Implementation (See the Tokens, Grammar, and Byte Code for each program) • Visual Implementation - Developed using Visual Programming (PWCT) • Written in ANSI C (The code is generated + Looks identical to Handwritten Code) • A small language – The Compiler + The Virtual Machine + Standard Functions are less than 25,000 lines of C code – The other 500,000 lines of code are related to libraries! • Portable (MS-DOS, Windows, Linux, macOS, Android, WebAssembly, 32-bit Microcontrollers, etc) • Comments (One line & Multi-lines) • Not Case-Sensitive • Dynamic Typing • Weakly typed (Automatic conversion between numbers and strings only) • Lexical Scoping (Global, Local & Object State) • Default scope for variables inside functions (Local) • Default scope for variables outside functions (global) • We can have separate global scope for each library or sub project • Garbage Collector - Automatic Memory Management (Escape Analysis and Reference Counting) • In most cases (90%) Ring VM uses Escape Analysis an no need to run the Garbage Collector (Faster) • Structure Programming 2.8. Features 29
74.
Ring Documentation, Release1.21.2 • Rich control structures & Operators • For in get item by reference not value, you can read/edit the item • Use exit to go outside from more than one loop (Use it for programming in the small only) • Procedures/Functions • Main Function (Optional - To avoid using the Global Scope) • Call Function before the definition (Top-Down Programming) • Recursion • Multi-line literals • Access (read/write) string letter by index • The list index start by 1 • No keyword to end Functions/Classes/Packages • Range operator ex: 1:10 and “a”:”z” • First Class Variables, Lists, Objects and Functions • Store/Copy Lists/Objects by value (Deep Copy) • Pass Lists/Objects by reference • Create references at any time using Ref() function • Native Object-Oriented Support – Encapsulation – Setter/Getter (optional) – private state (optional) – Instantiation – Polymorphism – Composition – Inheritance (Single Inheritance) – Operator Overloading – Packages • Reflection and Meta-programming • Clear program structure (Statements then functions then packages & classes) • Exception Handling • Eval() to execute code during run-time • 8-bit clean, work on binary data directly • I/O commands • Math functions • String functions • List functions • File processing functions 2.8. Features 30
75.
Ring Documentation, Release1.21.2 • Database support (ODBC, SQLite, MySQL & PostgreSQL) • Security Functions (OpenSSL) • Internet Functions (LibCurl) • Zip Functions • CSV Functions • JSON Functions • CGI Library (Written in Ring) – HTTP Get – HTTP Post – File upload – Cookies – URL Encode – HTML Templates – HTML Special Characters – HTML Generation using Functions – HTML Generation using Classes – CRUD Example (using MVC) – Users Example (Register, Login and Check) • Deploying web applications in the Cloud • Extension using C/C++ (Simple API) • Embedding the language in C/C++ programs • Embedding Ring in Ring (Support Pause/Resume) • Comes with code generator (Written in Ring) to quickly wrap C/C++ Libraries – Used to Support Allegro by creating RingAllegro – Used to Support LibSDL by creating RingLibSDL – Used to Support Qt by creating RingQt • Create 2D Games for Desktop and Mobile (Using the Allegro Library) • RingLibSDL Extension • Comes with simple Game Engine for 2D Games • RingOpenGL Extension • RingFreeGLUT Extension • RingRayLib Extension • RingTilengine Extension • Create GUI Applications for Desktop, WebAssembly and Mobile (Using the Qt Framework) • Comes with IDE contains the Code Editor (Ring Notepad) and the Form Designer • RingREPL (Read-Eval-Print-Loop) 2.8. Features 31
76.
Ring Documentation, Release1.21.2 • Tracing and Debugging • Type Hints Library • Comes with Ring2EXE to distribute applications • RingLibuv Extension • RingLibui Extension • RingSockets Extension • RingThreads Extension • RingHTTPLib Extension • RingFastPro Extension • RingRogueUtil Extension • RingPDFGen • RingPico - Support Raspberry Pi Pico Microcontroller • No Global Interpreter (VM) lock (No GIL) - Better for threads and concurrency (Faster) • Comes with RingPM (Package Manager) • Many Samples and Applications • Complete Documentation. 2.9 License The Ring Programming Language http://ring-lang.net/ Version 1.21.2 The MIT License (MIT) Copyright (c) Mahmoud Fayed Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documen- tation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PAR- TICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFT- WARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2.9. License 32
77.
CHAPTER THREE USING RING NOTEPAD Inthis chapter we will learn about using Ring Notepad to write and execute Ring programs quickly Ring Notepad is just an application developed using the Ring programming language. The source code exist in the ring/tools/ringnotepad folder. 3.1 Ring Notepad - Main Window When we run the Ring Notepad we get the next dockable windows • Project Files Window : where we can select and open any ring file (*.ring) quickly. • Source Code Window : Where we write the source code. • Form Designer Window : The Form Designer to create GUI application forms. • Web Browser Window : Where we read the documentation or quickly open any website. • Output Window : Output when we run programs that print to the standard output • Function Window : List of functions in the current source file • Classes Window : List of classes in the current source file 33
78.
Ring Documentation, Release1.21.2 3.2 Creating and running your first Console Application At first we will type the source code see "Hello, World!" As in the next image Then we will click on the “Save” button from the toolbar (or press CTRL+S) Determine the source code file name and location. For example type : hello This will create a new source code file called : hello.ring 3.2. Creating and running your first Console Application 34
79.
Ring Documentation, Release1.21.2 To run the program click on “Debug (Run then wait!)” button from the toolbar The next screen shot present the application during the runtime Press Enter to continue and return to the Ring Notepad. 3.2. Creating and running your first Console Application 35
80.
Ring Documentation, Release1.21.2 3.3 Creating and running your first GUI/WebAssembly/Mobile Appli- cation To learn how to create GUI applications using Ring check the “Desktop, WebAssembly and Mobile development using RingQt” chapter. Source Code: load "guilib.ring" new qApp { new qWidget() { resize(400,400) setWindowTitle("Hello, World!") show() } exec() } In Ring notepad we have a special button to run GUI applications without displaying the console window. The next screen shot present the application during the runtime 3.3. Creating and running your first GUI/WebAssembly/Mobile Application 36
81.
Ring Documentation, Release1.21.2 3.4 Creating and running your first Web Application To learn how to support Ring in your web server and how to create web applications check the “Web Development (CGI Library)” chapter. Note: You need to support the Ring language in your web server to be able to run the next example. Tip: For Windows users, Ring comes with Apache Web server! Using Ring Notepad we can run any web application from any folder directly without doing any configuration. Source Code: 3.4. Creating and running your first Web Application 37
82.
Ring Documentation, Release1.21.2 #!ring -cgi load "weblib.ring" import System.Web new Page { text("Hello, World!") } We can run the application in any web browser or in the browser that are embedded in Ring Notepad. We can run the web application using the Web icon. 3.5 Creating and running your first Desktop/Mobile Game To learn about creating 2D Games using Ring check the “Demo Project - Game Engine for 2D Games” chapter. Source Code: load "gameengine.ring" func main oGame = New Game { title = "My First Game" sprite { type = GE_TYPE_PLAYER x=400 y=400 width=100 height=100 file = "images/player.png" transparent = true Animate=false Move=true (continues on next page) 3.5. Creating and running your first Desktop/Mobile Game 38
83.
Ring Documentation, Release1.21.2 (continued from previous page) Scaled=true } } We can run the application as any GUI application. 3.6 The Main File in the Project The idea of the Main File ToolBar is to determine the main file in the project When the project contains many source code files Using this feature we can run the project (Main File) at any time while opening other files in the project without the need to switch to the Main File to run the project. To quickly use this feature (Open the project main file) Press Ctrl+Shift+M to set the current source code file as the main file Open and modify other source code files in the project To run the project (Main File) at any time press Ctrl+Shift+F5 (GUI) or Ctrl+Shift+D (Console) Screen Shots: 3.6. The Main File in the Project 39
84.
Ring Documentation, Release1.21.2 3.7 The File Menu From this menu we can create, open and save the source code files. Another feature in this menu is “Print to PDF” 3.7. The File Menu 40
85.
Ring Documentation, Release1.21.2 3.8 The Edit Menu From the Edit menu we can Cut, Copy and Paste text. Also we can change the font and the colors. We can Go to a specific line or use the Find and Replace window to find and replace text. Also We can set the Tab Width (Number of Spaces) Starting from Ring 1.8 we have the (Find in Files) option. Starting from Ring 1.11 we have the (Insert Text, Lower Case, Upper Case & Capitalize) options. 3.8. The Edit Menu 41
86.
Ring Documentation, Release1.21.2 3.9 The View Menu From this menu we can show/hide the dockable windows Also we can change the Style of the Ring Notepad Common Styles are (Fusion White and Modern Black) Also we can quickly show/hide group of dockable windows based on the context 3.9. The View Menu 42
87.
Ring Documentation, Release1.21.2 3.10 The Program Menu From this menu we can run the programs Also we can set the Main file in the project 3.11 The Browser Menu From this menu we can quickly open common links in the browser 3.10. The Program Menu 43
88.
Ring Documentation, Release1.21.2 3.12 The Tools Menu From this menu we can run the Form Designer in separate window Also we can run the REPL (Read-Eval-Print-Loop) application 3.13 The Distribute Menu From this menu we can build an executable file for the application Also we can prepare the application for distribution 3.14 The Help Menu From this menu we can open the help files (CHM & PDF) We can download these files from Ring website or using the Ring package manager (ringpm) 3.12. The Tools Menu 44
89.
Ring Documentation, Release1.21.2 ringpm install ringhelpchm ringpm install ringhelppdf 3.14. The Help Menu 45
90.
CHAPTER FOUR GETTING STARTED -FIRST STYLE 4.1 Hello World The next program prints the Hello World message on the screen (std-out). see "Hello World" 4.2 Run the program to run the program, save the code in a file, for example : hello.ring then from the command line or terminal, run it using Ring ring hello.ring 4.3 Create Executable File Using Ring2EXE we can create executable file for our application ring2exe hello.ring -static 4.4 Not Case-Sensitive Since the Ring language is not case-sensitive, the same program can be written in different styles Tip: It’s better to select one style and use it in all of the program source code SEE "Hello World" See "Hello World" 46
91.
Ring Documentation, Release1.21.2 4.5 Multi-Line literals Using Ring we can write multi-line literal, see the next example See " Hello Welcome to the Ring programming language How are you? " Also you can use the nl variable to insert new line and you can use the + operator to concatenate strings As we have NL for new lines, we have Tab and CR (Carriage return) too! Note: nl value means a new line and the actual codes that represent a newline is different between operating systems See "Hello" + nl + "Welcome to the Ring programming language" + nl + "How are you?" 4.6 Getting Input You can get the input from the user using the give command See "What is your name? " Give cName See "Hello " + cName 4.7 No Explicit End For Statements You don’t need to use ‘;’ or press ENTER to separate statements. The previous program can be written in one line. See "What is your name? " give cName see "Hello " + cName 4.8 Using ? to print expression then new line It’s common to print new line after printing an expression, We can use the ? operator to do that! Example: ? "Hello, World!" for x = 1 to 10 ? x next Output: 4.5. Multi-Line literals 47
92.
Ring Documentation, Release1.21.2 Hello, World! 1 2 3 4 5 6 7 8 9 10 4.9 Writing Comments We can write one line comments and multi-line comments The comment starts with # or // Multi-lines comments are written between /* and */ /* Program Name : My first program using Ring Date : 2016.09.09 Author : Mahmoud Fayed */ See "What is your name? " # print message on screen give cName # get input from the user see "Hello " + cName # say hello! // See "Bye!" Note: Using // to comment a lines of code is just a code style. 4.9. Writing Comments 48
93.
CHAPTER FIVE GETTING STARTED -SECOND STYLE 5.1 Hello World The next program prints the Hello World message on the screen (std-out). put "Hello World" 5.2 Run the program to run the program, save the code in a file, for example : hello.ring then from the command line or terminal, run it using Ring ring hello.ring 5.3 Create Executable File Using Ring2EXE we can create executable file for our application ring2exe hello.ring -static 5.4 Not Case-Sensitive Since the Ring language is not case-sensitive, the same program can be written in different styles Tip: It’s better to select one style and use it in all of the program source code PUT "Hello World" Put "Hello World" 49
94.
Ring Documentation, Release1.21.2 5.5 Multi-Line literals Using Ring we can write multi-line literal, see the next example Put " Hello Welcome to the Ring programming language How are you? " Also you can use the nl variable to insert new line and you can use the + operator to concatenate strings As we have NL for new lines, we have Tab and CR (Carriage return) too! Note: nl value means a new line and the actual codes that represent a newline is different between operating systems Put "Hello" + nl + "Welcome to the Ring programming language" + nl + "How are you?" 5.6 Getting Input You can get the input from the user using the get command Put "What is your name? " Get cName Put "Hello " + cName 5.7 No Explicit End For Statements You don’t need to use ‘;’ or press ENTER to separate statements. The previous program can be written in one line. Put "What is your name? " get cName put "Hello " + cName 5.8 Writing Comments We can write one line comments and multi-line comments The comment starts with # or // Multi-lines comments are written between /* and */ /* Program Name : My first program using Ring Date : 2016.09.09 Author : Mahmoud Fayed */ (continues on next page) 5.5. Multi-Line literals 50
95.
Ring Documentation, Release1.21.2 (continued from previous page) Put "What is your name? " # print message on screen get cName # get input from the user put "Hello " + cName # say hello! // Put "Bye!" Note: Using // to comment a lines of code is just a code style. 5.8. Writing Comments 51
96.
CHAPTER SIX GETTING STARTED -THIRD STYLE 6.1 Hello World The next program prints the Hello World message on the screen (std-out). print("Hello, World!n") 6.2 Run the program to run the program, save the code in a file, for example : hello.ring then from the command line or terminal, run it using Ring ring hello.ring 6.3 Create Executable File Using Ring2EXE we can create executable file for our application ring2exe hello.ring -static The -static option will avoid the need to ring.dll|ring.so|ring.dylib ring2exe hello.ring -dist -allruntime -noqt -noallegro 6.4 Not Case-Sensitive Since the Ring language is not case-sensitive, the same program can be written in different styles Tip: It’s better to select one style and use it in all of the program source code PRINT("Hello World") Print("Hello World") 52
97.
Ring Documentation, Release1.21.2 6.5 Multi-Line literals Using Ring we can write multi-line literal, see the next example Print(" Hello Welcome to the Ring programming language How are you? ") Also you can use the n to insert new line and you can use #{variable_name} to insert variables values. Print( "HellonWelcome to the Ring programming languagenHow are you?") 6.6 Getting Input You can get the input from the user using the getstring() function Print("What is your name? ") cName = GetString() Print("Hello #{cName}") 6.7 No Explicit End For Statements You don’t need to use ‘;’ or press ENTER to separate statements. The previous program can be written in one line. Print("What is your name? ") cName=getstring() print("Hello #{cName}") 6.8 Writing Comments We can write one line comments and multi-line comments The comment starts with # or // Multi-lines comments are written between /* and */ /* Program Name : My first program using Ring Date : 2016.09.09 Author : Mahmoud Fayed */ Print("What is your name? ") # print message on screen cName=GetString() # get input from the user print("Hello #{cName}") # say hello! // print("Bye!") 6.5. Multi-Line literals 53
98.
Ring Documentation, Release1.21.2 Note: Using // to comment a lines of code is just a code style. 6.9 Puts() function print the value then print new line (nl) Syntax: puts(expr) Example: Puts("Hello, World!") 6.10 Print() function print string - support n,t and r Also we can use #{variable_name} to insert variables values. Syntax: print(string) ---> String Example: print("nHello, WorldnnHow are you? tt I'm fine!n") x=10 y=20 print("nx value = #{x} , y value = #{y} n") 6.11 Print2Str() Function Syntax: print2Str(string) ---> String Example: world = "World!" mystring = print2str("Hello, #{world} nIn Year n#{2000+17} n") see mystring + nl Output: Hello, World! In Year 2017 6.9. Puts() function 54
99.
Ring Documentation, Release1.21.2 6.12 GetString() function Get input from the keyboard - return value as string getstring() ---> string 6.13 GetNumber() function Get input from the keyboard - return value as number getnumber() ---> number 6.12. GetString() function 55
100.
CHAPTER SEVEN VARIABLES To create anew variable, you just need to determine the variable name & value. The value will determine the variable type and you can change the value to switch between the types using the same variable name. Syntax: <Variable Name> = <Value> Tip: The operator ‘=’ is used here as an Assignment operator and the same operator can be used in conditions, but for testing equality of expressions. Note: The Variable will contains the real value (not a reference). This means that once you change the variable value, the old value will be removed from memory (even if the variable contains a list or object). 7.1 Dynamic Typing Ring is a dynamic programming language that uses Dynamic Typing. x = "Hello" # x is a string see x + nl x = 5 # x is a number (int) see x + nl x = 1.2 # x is a number (double) see x + nl x = [1,2,3,4] # x is a list see x # print list items x = date() # x is a string contains date see x + nl x = time() # x is a string contains time see x + nl x = true # x is a number (logical value = 1) see x + nl x = false # x is a number (logical value = 0) see x + nl 56
101.
Ring Documentation, Release1.21.2 7.2 Deep Copy We can use the assignment operator ‘=’ to copy variables. We can do that to copy values like strings & numbers. Also, we can copy complete lists & objects. The assignment operator will do a complete duplication for us. This operation called Deep Copy list = [1,2,3,"four","five"] list2 = list list = [] See list # print the first list - no items to print See "********" + nl See list2 # print the second list - contains 5 items Note: To copy lists/objects by reference, See the (Using References) chapter about the Ref() function 7.3 Implicit Conversion Using the plus operator, The language can automatically convert between numbers and strings. Rules: <NUMBER> + <STRING> --> <NUMBER> <STRING> + <NUMBER> --> <STRING> Note: The same operator ‘+’ can be used as an arithmetic operator or for string concatenation. Example: x = 10 # x is a number y = "20" # y is a string sum = x + y # sum is a number (y will be converted to a number) Msg = "Sum = " + sum # Msg is a string (sum will be converted to a string) ? Msg Note: See the Operators chapter to learn more about implicit conversion and mixing operators and types. 7.2. Deep Copy 57
102.
CHAPTER EIGHT OPERATORS In this chapterwe will introduce the operators provided by the Ring programming language. 8.1 Arithmetic Operators The next table presents all of the arithmetic operators provided by the Ring language. Assume variable X=50 and variable Y=10 then: Operator Description Example Result + Add x+y 60 - Subtract x-y 40 * Multiplies x*y 500 / Divide x/y 5 % Modulus x%y 0 ++ Increment x++ 51 - - Decrement x- - 49 ** OR ^^ Power x**3 125000 8.2 Relational Operators The next table presents all of the relational operators provided by the Ring language. Assume variable X=50 and variable Y=10 then: Operator Description Example Result = Equal x = y False != Not Equal x != y True > Greater than x > y True < Less than x < y False >= Greater or Equal x >= y True <= Less than or Equal x <= y False 58
103.
Ring Documentation, Release1.21.2 8.3 Logical Operators The next table presents all of the logical operators provided by the Ring language. Assume variable X=True and variable Y=False then: Operator Description Example Result and Logical AND x and y False or Logical OR x or y True not Logical Not not x False Another style Operator Description Example Result && Logical AND x && y False || Logical OR x || y True ! Logical Not ! x False 8.4 Bitwise Operators The next table presents all of the bitwise operators provided by the Ring language. Assume variable X=8 and variable Y=2 then: Operator Description Example Result & Binary AND x & y 0 | Binary OR x | y 10 ^ Binary XOR x ^ y 10 ~ Binary Ones Complement ~x -9 << Binary Left Shift x << y 32 >> Binary Right Shift x >> y 2 8.5 Assignment Operators The next table presents all of the assignment operators provided by the Ring language. Assume variable X=8 then: 8.3. Logical Operators 59
104.
Ring Documentation, Release1.21.2 Operator Description Example Result = Assignment x = 10 x=10 += Add AND assignment x += 5 x=13 -= Subtract AND assignment x -= 3 x=5 *= Multiply AND assignment x *= 2 x=16 /= Divide AND assignment x /= 3 x=2.67 %= Modulus AND assignment x %= 2 x=0 <<= Left shift AND assignment x <<= 2 x=32 >>= Right shift AND assignment x >>= 2 x=2 &= Bitwise AND assignment x &= 4 x=0 |= Bitwise OR and assignment x |= 3 x=11 ^= Bitwise XOR and assignment x ^= 4 x=12 8.6 Misc Operators Operator Description :literal using : before identifier mean literal Start:End create list contains items from start to end [list items] define list items list[index] access list item obj.name using the dot operator to access object members (attributes/methods). obj {stmts} execute statements with direct access to object attributes & methods func(para,...) call function using parameters separated by comma ? <expr> Print expression then new line 8.7 Operators Precedence The next table present operators from higher precedence (Evaluated first) to lower precedence. 8.6. Misc Operators 60
105.
Ring Documentation, Release1.21.2 Operator . [] () {} ~ :Literal [list items] ++ - - - (Unary minus) Start:End * / % + - << >> & | ^ < > <= >= = != not ! and or && || Assignment = += -= *= /= %=>>= <<= &= ^= |= ? Example (1): See 3+5*4 # prints 23 8.8 Mixing Arithmetic Operators and Types The next table demonstrates what happens when mixing arithmetic operators and different types First Type Operator Second Type Output Type OR Behavior Example Number “+” Number Number 5+5 Number “+” String Number 5+”5” String “+” Number String “5”+5 String “+” String String “5”+”5” List “+” Number Add number to List [1,2,3] + 4 List “+” String Add string to List [1,2,3] + “four” List “+” List Add list to List [1,2,3] + [“sub”] List “+” Object Add object to List [1,2,3] + new Point Number “+” List Runtime Error 4 + [1,2,3] Number “+” Object Check Operator Overloading 4 + new point String “+” List Runtime Error “4” + [1,2,3] String “+” Object Check Operator Overloading “4” + new point Object “+” Number Check Operator Overloading new point + 1 Object “+” String Check Operator Overloading new point + “test” Object “+” List Check Operator Overloading new point + [10,10] Object “+” Object Check Operator Overloading new point + new point Number “-” Number Number 5-5 Number “-” String Number 5-“5” String “-” Number Number “5”-5 String “-” String Number “5”-“5” List “-” Number Runtime Error [1,2,3] - 4 continues on next page 8.8. Mixing Arithmetic Operators and Types 61
106.
Ring Documentation, Release1.21.2 Table 1 – continued from previous page First Type Operator Second Type Output Type OR Behavior Example List “-” String Runtime Error [1,2,3] - “four” List “-” List Runtime Error [1,2,3] - [“sub”] List “-” Object Check Operator Overloading [1,2,3] - new Point Number “-” List Runtime Error 4 - [1,2,3] Number “-” Object Check Operator Overloading 4 - new point String “-” List Runtime Error “4” - [1,2,3] String “-” Object Check Operator Overloading “4” - new point Object “-” Number Check Operator Overloading new point - 1 Object “-” String Check Operator Overloading new point - “test” Object “-” List Check Operator Overloading new point - [10,10] Object “-” Object Check Operator Overloading new point - new point Number “*” Number Number 5*5 Number “*” String Number 5*”5” String “*” Number Number “5”*5 String “*” String Number “5”*”5” List “*” Number Runtime Error [1,2,3] * 4 List “*” String Runtime Error [1,2,3] * “four” List “*” List Runtime Error [1,2,3] * [“sub”] List “*” Object Check Operator Overloading [1,2,3] * new Point Number “*” List Runtime Error 4 * [1,2,3] Number “*” Object Check Operator Overloading 4 * new point String “*” List Runtime Error “4” * [1,2,3] String “*” Object Check Operator Overloading “4” * new point Object “*” Number Check Operator Overloading new point * 1 Object “*” String Check Operator Overloading new point * “test” Object “*” List Check Operator Overloading new point * [10,10] Object “*” Object Check Operator Overloading new point * new point Number “/” Number Number 5/5 Number “/” String Number 5/”5” String “/” Number Number “5”/5 String “/” String Number “5”/”5” List “/” Number Runtime Error [1,2,3] / 4 List “/” String Runtime Error [1,2,3] / “four” List “/” List Runtime Error [1,2,3] / [“sub”] List “/” Object Check Operator Overloading [1,2,3] / new Point Number “/” List Runtime Error 4 / [1,2,3] Number “/” Object Check Operator Overloading 4 / new point String “/” List Runtime Error “4” / [1,2,3] String “/” Object Check Operator Overloading “4” / new point Object “/” Number Check Operator Overloading new point / 1 Object “/” String Check Operator Overloading new point / “test” Object “/” List Check Operator Overloading new point / [10,10] Object “/” Object Check Operator Overloading new point / new point Number “%” Number Number 5%5 Number “%” String Number 5%”5” String “%” Number Number “5”%5 String “%” String Number “5”%”5” List “%” Number Runtime Error [1,2,3] % 4 List “%” String Runtime Error [1,2,3] % “four” List “%” List Runtime Error [1,2,3] % [“sub”] continues on next page 8.8. Mixing Arithmetic Operators and Types 62
107.
Ring Documentation, Release1.21.2 Table 1 – continued from previous page First Type Operator Second Type Output Type OR Behavior Example List “%” Object Check Operator Overloading [1,2,3] % new Point Number “%” List Runtime Error 4 % [1,2,3] Number “%” Object Check Operator Overloading 4 % new point String “%” List Runtime Error “4” % [1,2,3] String “%” Object Check Operator Overloading “4” % new point Object “%” Number Check Operator Overloading new point % 1 Object “%” String Check Operator Overloading new point % “test” Object “%” List Check Operator Overloading new point % [10,10] Object “%” Object Check Operator Overloading new point % new point Number “++” ... Number 5++ String “++” ... Syntax Error/Runtime Error x=”5” x++ List “++” ... Syntax Error/Runtime Error x=[1,2,3] x++ Object “++” ... Syntax Error/Runtime Error x=new point x++ Number “–” ... Number 5– String “–” ... Syntax Error/Runtime Error x=”5” x– List “–” ... Syntax Error/Runtime Error x=[1,2,3] x– Object “–” ... Syntax Error/Runtime Error x=new point x– Note: The behavior of the power operator with respect to different types is similar to the -, *, / and % operators. 8.9 Mixing Relational Operators and Types Using Relational Operators like <, <=, >, >= could produce True, False OR runtime error. When mixing Strings and Numbers with these operators, The string will be converted to a number. Example (2): ? 5 < 7 # 1 (True) ? "5" < 7 # 1 (True) ? 5 < "7" # 1 (True) ? "5" < "7" # 1 (True) ? "test" < 5 # Runtime Error (Invalid numeric string) Note: Using these operators with lists or objects will produce a runtime error. An exception to this rule is having an object the comes first before the operator and this object support operator overloading. Using relational operators like = or != will only produce True OR False (i.e. no runtime error) Also, when mixing Strings and Numbers with these operators, The string will be converted to a number. Example (3): ? "5" = 5 # 1 (True) ? 5 = "5" # 1 (True) ? 5 = 5 # 1 (True) ? "5" = "5" # 1 (True) (continues on next page) 8.9. Mixing Relational Operators and Types 63
108.
Ring Documentation, Release1.21.2 (continued from previous page) ? 5 = 7 # 0 (False) ? "5" = 7 # 0 (False) ? 5 = "7" # 0 (False) ? "5" = "7" # 0 (False) ? "test" = 5 # 0 (False) ? "5" != 5 # 0 (False) ? 5 != "5" # 0 (False) ? 5 != 5 # 0 (False) ? "5" != "5" # 0 (False) ? 5 != 7 # 1 (True) ? "5" != 7 # 1 (True) ? 5 != "7" # 1 (True) ? "5" != "7" # 1 (True) ? "test" != 5 # 1 (True) Example (4): ? 12500 = "0012500" # 1 (True) ? 12500 = "0012500-PRY-09" # 0 (False) # When we compare between number and a string # If we found the number --> Then we ignore Space, Tab, n, r after that number # We consider "" to be like Zero but we don't do that for Space, Tab, n and r # Note: if 0 -> False while if " " -> True ? 1 = "1 x" # 0 (False) ? 1 = "1 " # 1 (True) ? 0 = "" # 1 (True) ? 0 = " 0 " # 1 (True) ? 1 = " 1 " # 1 (True) ? 0 = "000000" # 1 (True) ? 0 = "00000 " # 1 (True) ? 1 = " 1 " # 1 (True) ? 0 = " " # 0 (False) if 0 # False ? :fail else ? :pass ok # pass if "" # False ? :fail else ? :pass ok # pass if " " # True (continues on next page) 8.9. Mixing Relational Operators and Types 64
109.
Ring Documentation, Release1.21.2 (continued from previous page) ? :pass else ? :fail ok # pass Note: Using these operators to compare between objects or lists will compare between them at the reference level (not the value) Example (5): aList = [1,2,3] aList2 = [1,2,3] ? aList = aList # 1 (True) ? aList = aList2 # 0 (False) aList3 = ref(aList) ? aList3 = aList # 1 (True) 8.10 Mixing Logical Operators and Types We have the next rules: • Logical operators always produce True/False • The Zero number is considered False • The Empty string is considered False • The Empty list is considered False • The list that wrap C pointer is considered False if the pointer is NULL • All other values are True Example (6): ? 1 and 1 # 1 (True) ? "test" and "test" # 1 (True) ? [1,2,3] and "test" # 1 (True) ? 1 and "test" and [1,2,3] # 1 (True) ? 1 and new point # 1 (True) ? 1 and 0 # 0 (False) ? 1 and "" # 0 (False) ? 1 and [] # 0 (False) ? 1 and NULLPointer() # 0 (False) class point 8.10. Mixing Logical Operators and Types 65
110.
Ring Documentation, Release1.21.2 8.11 Mixing Bitwise Operators and Types These operators support numbers. Also, it will automatically convert strings to numbers if this is possible or produce a runtime error if the string can’t be converted. Using these operators with lists or objects produce a runtime error with an exception to this rule. The exception is using objects that support operator overloading where the object comes first before the operator. Example (7): ? 1 & 1 # 1 ? "1" & 1 # 1 ? 1 & "3" # 1 ? "3" & "3" # 3 ? "123" & "123" # 123 8.12 Mixing Assignment Operators and Types Using assignment we can assign any value to any variable. Using += support Strings & Numbers and will produce a runtime error if used with other types Using other assignment operators like -=, *=, /=, %=, <<=, >>=, etc. support only numbers and will produce a runtime error if used with other types. Example (8): cStr = "one" cStr += " two" ? cStr # one two nNum = 100 nNum += 200 ? nNum # 300 8.11. Mixing Bitwise Operators and Types 66
111.
CHAPTER NINE CONTROL STRUCTURES -FIRST STYLE In this chapter we are going to learn about the control structures provided by the Ring programming language. 9.1 Branching • If Statement Syntax: if Expression Block of statements but Expression Block of statements else Block of statements ok Example: see " Main Menu --------- (1) Say Hello (2) About (3) Exit " give nOption if nOption = 1 see "Enter your name : " give name see "Hello " + name + nl but nOption = 2 see "Sample : using if statement" + nl but nOption = 3 bye else see "bad option..." + nl ok • Switch Statement Syntax: switch Expression on Expression Block of statements (continues on next page) 67
112.
Ring Documentation, Release1.21.2 (continued from previous page) other Block of statements off Example: See " Main Menu --------- (1) Say Hello (2) About (3) Exit " Give nOption Switch nOption On 1 See "Enter your name : " Give name See "Hello " + name + nl On 2 See "Sample : using switch statement" + nl On 3 Bye Other See "bad option..." + nl Off 9.2 Looping • While Loop Syntax: while Expression Block of statements end Example: While True See " Main Menu --------- (1) Say Hello (2) About (3) Exit " Give nOption Switch nOption On 1 See "Enter your name : " Give name See "Hello " + name + nl On 2 (continues on next page) 9.2. Looping 68
113.
Ring Documentation, Release1.21.2 (continued from previous page) See "Sample : using while loop" + nl On 3 Bye Other See "bad option..." + nl Off End • For Loop Syntax: for identifier=expression to expression [step expression] Block of statements next Example: # print numbers from 1 to 10 for x = 1 to 10 see x + nl next Example: # Dynamic loop See "Start : " give nStart nStart = 0+nStart See "End : " give nEnd nEnd = 0+nEnd See "Step : " give nStep nStep = 0+nStep For x = nStart to nEnd Step nStep see x + nl Next Example: # print even numbers from 0 to 10 for x = 0 to 10 step 2 see x + nl next Example: # print even numbers from 10 to 0 for x = 10 to 0 step -2 see x + nl next • For in Loop Syntax: for|foreach identifier in List/String [step expression] Block of statements next Example: 9.2. Looping 69
114.
Ring Documentation, Release1.21.2 aList = 1:10 # create list contains numbers from 1 to 10 for x in aList see x + nl next # print numbers from 1 to 10 Note: We can use the ForEach keyword instead of the For keyword 9.3 Using The Step option with For in We can use the Step option with For in to skip number of items in each iteration Example: aList = 1:10 # create list contains numbers from 1 to 10 # print odd items inside the list for x in aList step 2 see x + nl next 9.4 Using For in to modify lists When we use (For in) we get items by reference. This means that we can read/edit items inside the loop. Example: aList = 1:5 # create list contains numbers from 1 to 5 # replace list numbers with strings for x in aList switch x on 1 x = "one" on 2 x = "two" on 3 x = "three" on 4 x = "four" on 5 x = "five" off next see aList # print the list items 9.5 Do Again Loop Syntax: do Block of statements again expression Example: 9.3. Using The Step option with For in 70
115.
Ring Documentation, Release1.21.2 x = 1 do see x + nl x++ again x <= 10 9.6 Exit Command Used to go outside one or more of loops. Syntax: exit [expression] # inside loop Example: for x = 1 to 10 see x + nl if x = 5 exit ok next 9.7 Exit from two loops The next example presents how to use the exit command to exit from two loops in one jump. Example: for x = 1 to 10 for y = 1 to 10 see "x=" + x + " y=" + y + nl if x = 3 and y = 5 exit 2 # exit from 2 loops ok next next 9.8 Loop Command Used to jump to the next iteration in the loop. Syntax: loop [expression] # inside loop Example: for x = 1 to 10 if x = 3 see "Number Three" + nl (continues on next page) 9.6. Exit Command 71
116.
Ring Documentation, Release1.21.2 (continued from previous page) loop ok see x + nl next 9.9 Short-circuit evaluation The logical operators and/or follow the short-circuit evaluation. If the first argument of the AND operator is zero, then there is no need to evaluate the second argument and the result will be zero. If the first argument of the OR operator is one, then there is no need to evaluate the second argument and the result will be one. Example: /* output ** nice ** nice ** great */ x = 0 y = 10 if (x = 0 and nice()) and (y = 10 and nice()) see "great" + nl ok func nice see "nice" + nl return 1 Example: # No output x = 0 y = 10 if (x = 1 and nice()) and (y = 10 and nice()) see "great" + nl ok func nice see "nice" + nl return 1 Example: /* output ** nice ** great */ x = 0 y = 10 (continues on next page) 9.9. Short-circuit evaluation 72
117.
Ring Documentation, Release1.21.2 (continued from previous page) if (x = 0 and nice()) or (y = 10 and nice()) see "great" + nl ok func nice see "nice" + nl return 1 9.10 Comments about evaluation • True, False, nl & NULL are variables defined by the language • True = 1 • False = 0 • nl = new line • NULL = empty string = “” • Everything evaluates to True except 0 (False), NULL (Empty String), Empty List and Lists that wrap C pointer where the pointer value is NULL. Example: # output = message from the if statement if 5 # 5 evaluates to true because it's not zero (0). see "message from the if statement" + nl ok 9.10. Comments about evaluation 73
118.
CHAPTER TEN CONTROL STRUCTURES -SECOND STYLE In this chapter we are going to learn about the second style of control structures provided by the Ring programming language. 10.1 Branching • If Statement Syntax: if Expression Block of statements elseif Expression Block of statements else Block of statements end Example: put " Main Menu --------- (1) Say Hello (2) About (3) Exit " get nOption if nOption = 1 put "Enter your name : " get name put "Hello " + name + nl elseif nOption = 2 put "Sample : using if statement" + nl elseif nOption = 3 bye else put "bad option..." + nl end • Switch Statement Syntax: switch Expression case Expression (continues on next page) 74
119.
Ring Documentation, Release1.21.2 (continued from previous page) Block of statements else Block of statements end Example: Put " Main Menu --------- (1) Say Hello (2) About (3) Exit " Get nOption Switch nOption Case 1 Put "Enter your name : " Get name Put "Hello " + name + nl Case 2 Put "Sample : using switch statement" + nl Case 3 Bye Else Put "bad option..." + nl End 10.2 Looping • While Loop Syntax: while Expression Block of statements end Example: While True Put " Main Menu --------- (1) Say Hello (2) About (3) Exit " Get nOption Switch nOption Case 1 Put "Enter your name : " Get name Put "Hello " + name + nl (continues on next page) 10.2. Looping 75
120.
Ring Documentation, Release1.21.2 (continued from previous page) Case 2 Put "Sample : using while loop" + nl Case 3 Bye Else Put "bad option..." + nl End End • For Loop Syntax: for identifier=expression to expression [step expression] Block of statements end Example: # print numbers from 1 to 10 for x = 1 to 10 put x + nl end Example: # Dynamic loop Put "Start : " get nStart nStart= 0+nStart Put "End : " get nEnd nEnd = 0+nEnd Put "Step : " get nStep nStep = 0+nStep For x = nStart to nEnd Step nStep Put x + nl End Example: # print even numbers from 0 to 10 for x = 0 to 10 step 2 Put x + nl end Example: # print even numbers from 10 to 0 for x = 10 to 0 step -2 put x + nl end • For in Loop Syntax: for identifier in List/String [step expression] Block of statements end Example: 10.2. Looping 76
121.
Ring Documentation, Release1.21.2 aList = 1:10 # create list contains numbers from 1 to 10 for x in aList put x + nl end # print numbers from 1 to 10 10.3 Exceptions try Block of statements catch Block of statements end 10.3. Exceptions 77
122.
CHAPTER ELEVEN CONTROL STRUCTURES -THIRD STYLE In this chapter we are going to learn about the third style of control structures provided by the Ring programming language. 11.1 Branching • If Statement Syntax: if Expression { Block of statements elseif Expression Block of statements else Block of statements } Example: print(" Main Menu --------- (1) Say Hello (2) About (3) Exit ") nOption = getnumber() if nOption = 1 { print("Enter your name : ") name = getstring() print("Hello #{name}n") elseif nOption = 2 print("Sample : using if statementn") elseif nOption = 3 bye else print("bad option...n") } 78
123.
Ring Documentation, Release1.21.2 • Switch Statement Syntax: switch Expression { case Expression Block of statements else Block of statements } Example: print(" Main Menu --------- (1) Say Hello (2) About (3) Exit ") nOption = GetString() switch nOption { case 1 print("Enter your name : ") name = getstring() print("Hello #{name}n") case 2 print("Sample : using switch statementn") case 3 Bye else print("bad option...n") } 11.2 Looping • While Loop Syntax: while Expression { Block of statements } Example: While True { print(" Main Menu (continues on next page) 11.2. Looping 79
124.
Ring Documentation, Release1.21.2 (continued from previous page) --------- (1) Say Hello (2) About (3) Exit ") nOption = GetString() switch nOption { case 1 print("Enter your name : ") name = getstring() print("Hello #{name}n") case 2 print("Sample : using switch statementn") case 3 Bye else print("bad option...n") } } • For Loop Syntax: for identifier=expression to expression [step expression] { Block of statements } Example: # print numbers from 1 to 10 for x = 1 to 10 { print("#{x}n") } Example: # Dynamic loop print("Start : ") nStart = getnumber() print("End : ") nEnd = getnumber() print("Step : ") nStep = getnumber() for x = nStart to nEnd step nStep { print("#{x}n") } Example: # print even numbers from 0 to 10 for x = 0 to 10 step 2 { (continues on next page) 11.2. Looping 80
125.
Ring Documentation, Release1.21.2 (continued from previous page) print("#{x}n") } Example: # print even numbers from 10 to 0 for x = 10 to 0 step -2 { print("#{x}n") } • For in Loop Syntax: for identifier in List/String [step expression] { Block of statements } Example: aList = 1:10 # create list contains numbers from 1 to 10 for x in aList { print("#{x}n") } # print numbers from 1 to 10 Example: aList = 1:10 # create list contains numbers from 1 to 10 # print odd items inside the list for x in aList step 2 { print("#{x}n") } When we use (For in) we get items by reference. This means that we can read/edit items inside the loop. Example: aList = 1:5 # create list contains numbers from 1 to 5 # replace list numbers with strings for x in aList { switch x { case 1 x = "one" case 2 x = "two" case 3 x = "three" case 4 x = "four" case 5 x = "five" } } print(aList) # print the list items 11.2. Looping 81
126.
Ring Documentation, Release1.21.2 11.3 Exceptions try { Block of statements catch Block of statements } 11.3. Exceptions 82
127.
CHAPTER TWELVE GETTING INPUT We canget input from the keyboard using • The Give Command • The GetChar() Function • The Input() Function 12.1 Give Command Syntax: Give VariableName Example: See "Enter the first number : " Give nNum1 See "Enter the second number : " Give nNum2 See "Sum : " + ( 0 + nNum1 + nNum2 ) Output: Enter the first number : 3 Enter the second number : 4 Sum : 7 12.2 GetChar() Function We can get one character from the standard input using the GetChar() function Syntax: GetChar() ---> Character Example: While True See " Main Menu (continues on next page) 83
128.
Ring Documentation, Release1.21.2 (continued from previous page) (1) Say Hello (2) Exit " Option = GetChar() GetChar() GetChar() # End of line # the previous two lines can be replaced with the next line # Give Option if Option = 1 see "Enter your name : " give cName see "Hello " + cName else bye ok End 12.3 Input() Function We can get input from the keyboard using the Input() function Syntax: Input([nCount]) ---> string The function will wait until nCount characters (at least) are read Tip: If the nCount is not passed as parameter, the function will read a line. Example: See "Enter message (30 characters) : " cMsg = input(30) See "Message : " + cMsg 12.3. Input() Function 84
129.
CHAPTER THIRTEEN FUNCTIONS - FIRSTSTYLE In this chapter we are going to learn about the next topics :- • Define functions • Call functions • Declare parameters • Send parameters • Main Function • Variables Scope • Return Value • Recursion 13.1 Define Functions To define new function Syntax: func <function_name> [parameters] Block of statements Note: No keyword is required to end the function definition. Example: func hello see "Hello from function" + nl 85
130.
Ring Documentation, Release1.21.2 13.2 Call Functions To call function without parameters, we type the function name then () Tip: We can call the function before the function definition and the function code. Example: hello() func hello see "Hello from function" + nl Example: first() second() func first see "message from the first function" + nl func second see "message from the second function" + nl 13.3 Declare parameters To declare the function parameters, after the function name type the list of parameters as a group of identifiers separated by comma. Example: func sum x,y see x+y+nl 13.4 Send Parameters To send parameters to function, type the parameters inside () after the function name Syntax: funcname(parameters) Example: /* output ** 8 ** 3000 */ sum(3,5) sum(1000,2000) func sum x,y see x+y+nl 13.2. Call Functions 86
131.
Ring Documentation, Release1.21.2 13.5 Main Function Using the Ring programming language, the Main Function is optional, when it’s defined, it will be executed after the end of other statements. if no other statements comes alone, the main function will be the first entry point Example: # this program will print the hello world message first then execute the main function See "Hello World!" + nl func main see "Message from the main function" + nl 13.6 Variables Scope The Ring programming language uses lexical scoping to determine the scope of a variable. Variables defined inside functions (including function parameters) are local variables. Variables defined outside func- tions (before any function) are global variables. Inside any function we can access the variables defined inside this function beside the global variables. Example: # the program will print numbers from 10 to 1 x = 10 # x is a global variable. func main for t = 1 to 10 # t is a local variable mycounter() # call function next func mycounter see x + nl # print the global variable value x-- # decrement Note: Using the main function before the for loop declare the t variable as a local variable, It’s recommended to use the main functions instead of typing the instructions directly to set the scope of the new variables to local. 13.5. Main Function 87
132.
Ring Documentation, Release1.21.2 13.7 Return Value The function can return a value using the Return command. Syntax: Return [Expression] Tip: the Expression after the return command is optional and we can use the return command to end the function execution without returning any value. Note: if the function doesn’t return explicit value, it will return NULL (empty string = “” ). Example: if novalue() = NULL See "the function doesn't return a value" + nl ok func novalue 13.8 Recursion The Ring programming language support Recursion and the function can call itself using different parameters. Example: see fact(5) # output = 120 func fact x if x = 0 return 1 else return x * fact(x-1) ok 13.7. Return Value 88
133.
CHAPTER FOURTEEN FUNCTIONS - SECONDSTYLE In this chapter we are going to learn about the next topics :- • Define functions • Call functions • Declare parameters • Send parameters • Main Function • Variables Scope • Return Value • Recursion 14.1 Define Functions To define new function Syntax: def <function_name> [parameters] Block of statements [end] Note: the keyword ‘end’ is optional. Example: def hello put "Hello from function" + nl end 89
134.
Ring Documentation, Release1.21.2 14.2 Call Functions To call function without parameters, we type the function name then () Tip: We can call the function before the function definition and the function code. Example: hello() def hello put "Hello from function" + nl end Example: first() second() def first put "message from the first function" + nl def second put "message from the second function" + nl 14.3 Declare parameters To declare the function parameters, after the function name type the list of parameters as a group of identifiers separated by comma. Example: def sum x,y put x+y+nl end 14.4 Send Parameters To send parameters to function, type the parameters inside () after the function name Syntax: funcname(parameters) Example: /* output ** 8 ** 3000 */ sum(3,5) sum(1000,2000) (continues on next page) 14.2. Call Functions 90
135.
Ring Documentation, Release1.21.2 (continued from previous page) def sum x,y put x+y+nl 14.5 Main Function Using the Ring programming language, the Main Function is optional, when it’s defined, it will be executed after the end of other statements. if no other statements comes alone, the main function will be the first entry point Example: # this program will print the hello world message first then execute the main function put "Hello World!" + nl def main put "Message from the main function" + nl end 14.6 Variables Scope The Ring programming language uses lexical scoping to determine the scope of a variable. Variables defined inside functions (including function parameters) are local variables. Variables defined outside func- tions (before any function) are global variables. Inside any function we can access the variables defined inside this function beside the global variables. Example: # the program will print numbers from 10 to 1 x = 10 # x is a global variable. def main for t = 1 to 10 # t is a local variable mycounter() # call function end end def mycounter put x + nl # print the global variable value x-- # decrement end Note: Using the main function before the for loop declare the t variable as a local variable, It’s recommended to use the main functions instead of typing the instructions directly to set the scope of the new variables to local. 14.5. Main Function 91
136.
Ring Documentation, Release1.21.2 14.7 Return Value The function can return a value using the Return command. Syntax: Return [Expression] Tip: the Expression after the return command is optional and we can use the return command to end the function execution without returning any value. Note: if the function doesn’t return explicit value, it will return NULL (empty string = “” ). Example: if novalue() = NULL put "the function doesn't return a value" + nl end def novalue 14.8 Recursion The Ring programming language support Recursion and the function can call itself using different parameters. Example: put fact(5) # output = 120 def fact x if x = 0 return 1 else return x * fact(x-1) end 14.7. Return Value 92
137.
CHAPTER FIFTEEN FUNCTIONS - THIRDSTYLE In this chapter we are going to learn about the next topics :- • Define functions • Call functions • Declare parameters • Send parameters • Main Function • Variables Scope • Return Value • Recursion 15.1 Define Functions To define new function Syntax: func <function_name> [parameters] ['{'] Block of statements ['}'] Example: func hello { print("Hello from function n") } 93
138.
Ring Documentation, Release1.21.2 15.2 Call Functions To call function without parameters, we type the function name then () Tip: We can call the function before the function definition and the function code. Example: hello() func hello { print("Hello from function n") } Example: first() second() func first { print("message from the first function n") } func second { print("message from the second function n") } 15.3 Declare parameters To declare the function parameters, after the function name type the list of parameters as a group of identifiers separated by comma. Example: func sum(x,y) { print(x+y) } 15.4 Send Parameters To send parameters to function, type the parameters inside () after the function name Syntax: funcname(parameters) Example: /* output ** 8 ** 3000 */ sum(3,5) sum(1000,2000) (continues on next page) 15.2. Call Functions 94
139.
Ring Documentation, Release1.21.2 (continued from previous page) func sum(x,y) { print(x+y) } 15.5 Main Function Using the Ring programming language, the Main Function is optional, when it’s defined, it will be executed after the end of other statements. if no other statements comes alone, the main function will be the first entry point Example: # this program will print the hello world message first then execute the main function print("Hello, World! n") func main { print("Message from the main function n") } 15.6 Variables Scope The Ring programming language uses lexical scoping to determine the scope of a variable. Variables defined inside functions (including function parameters) are local variables. Variables defined outside func- tions (before any function) are global variables. Inside any function we can access the variables defined inside this function beside the global variables. Example: # the program will print numbers from 10 to 1 x = 10 # x is a global variable. func main { for t = 1 to 10 { # t is a local variable mycounter() # call function } } func mycounter { print("#{x}n") # print the global variable value x-- # decrement } Note: Using the main function before the for loop declare the t variable as a local variable, It’s recommended to use the main functions instead of typing the instructions directly to set the scope of the new variables to local. 15.5. Main Function 95
140.
Ring Documentation, Release1.21.2 15.7 Return Value The function can return a value using the Return command. Syntax: Return [Expression] Tip: the Expression after the return command is optional and we can use the return command to end the function execution without returning any value. Note: if the function doesn’t return explicit value, it will return NULL (empty string = “” ). Example: if novalue() = NULL { print("the function doesn't return a valuen") } func novalue { } 15.8 Recursion The Ring programming language support Recursion and the function can call itself using different parameters. Example: print( fact(5) ) # output = 120 func fact(x) { if x = 0 { return 1 else return x * fact(x-1) } } 15.7. Return Value 96
141.
CHAPTER SIXTEEN PROGRAM STRUCTURE In thischapter we will learn about using many source code files in the same project. 16.1 Source Code File Sections Each source code file may contains the next sections (in the same order). Source Code File Sections Load Files Statements and Global Variables Functions Packages and Classes The application maybe one or more of files. 16.2 Using Many Source Code Files To include another source file in the project, just use the load command. Syntax: Load "filename.ring" Note: The Load command is executed directly by the compiler in the parsing stage Tip: if you don’t know the file name until the runtime, or you need to use functions to get the file path, just use eval(). Example: # File : Start.ring Load "sub.ring" sayhello("Mahmoud") 97
142.
Ring Documentation, Release1.21.2 # File : sub.ring func sayhello cName see "Hello " + cName + nl 16.3 Load Package Using the ‘load’ command we can use many ring source files in the same project But all of these files will share the same global scope We have also the “Load Package” command Using “Load Package” we can load a library (*.ring file) in new global scope This is very useful to create libraries that avoid conflicts in global variables Example: File: loadpackage.ring x = 100 ? "Hello, World!" load package "testloadpackage.ring" ? x test() File: testloadpackage.ring ? "Hello from testloadpackage.ring" x = 1000 test() func test ? x Output: Hello, World! Hello from testloadpackage.ring 1000 100 1000 16.3. Load Package 98
143.
Ring Documentation, Release1.21.2 16.4 Load Again Ring 1.12 comes with the Load Again command Using this command we can load the Ring source file which contains constants more than one time. This is useful when using Ring source files for translations through global constants. Example: The next function is part from a project which support Arabic and English languages The files english.ring and arabic.ring contains constants for translation One of these files is loaded in the start of the program Loading the same file again using the (Load) command is not possible Because the (Load) command load the same source file only for the first time and ignore next times. So we have to use the (Load Again) command. Where we can use these files again during the runtime as in the next code func setLang nLanguage if C_ENV_DEFAULT_LANG = nLanguage return ok C_ENV_DEFAULT_LANG = nLanguage # Change the language switch nLanguage on C_TRANSLATION_ENGLISH load again "translation/english.ring" on C_TRANSLATION_ARABIC load again "translation/arabic.ring" off 16.4. Load Again 99
144.
CHAPTER SEVENTEEN LISTS In this chapterwe are going to learn how to deal with lists. 17.1 Create Lists We can create new lists by defining the list items inside square brackets. Example: aList = [1,2,3,4,5] Also we can create new lists using the : operator Example: aList = 1:5 aList2 = "a":"z" Example: aList = 5:1 aList2 = "z":"a" Also we can create lists using the list() function Syntax: list = list(size) To create 2D list list = list(nRows,nCols) Example (1) aList = list(10) # aList contains 10 items Example (2) aList = list(5,4) # Create 2D List contains 5 rows and 4 columns Note: the list index start from 1 100
145.
Ring Documentation, Release1.21.2 17.2 Add Items To add new items to the list, we can use the Add() function. When adding a list to another list it will be added as one item. If you want to change this and add each item in the added list alone pass the third parameter as True Syntax: Add(List,Item,[lManyItems]) Example: aList = ["one","two"] add(aList,"three") see aList Also we can do that using the + operator. Syntax: List + item Example: aList = 1:10 # create list contains numbers from 1 to 10 aList + 11 # add number 11 to the list see aList # print the list Example: aList = 1:3 add(aList,4:6) # Add the list as one item ? len(aList) # 4 aList = 1:3 add(aList,4:6,True) # Add each item alone ? len(aList) # 6 ? aList 17.3 Get List Size We can get the list size using the len() function Syntax: Len(List) Example: aList = 1:20 see len(aList) # print 20 17.2. Add Items 101
146.
Ring Documentation, Release1.21.2 17.4 Delete Item From List To delete an item from the list, we can use the del() function Syntax: del(list,index) Example: aList = ["one","two","other","three"] Del(aList,3) # delete item number three see aList # print one two three 17.5 Get List Item To get an item from the list, we uses the next syntax List[Index] Example: aList = ["Cairo","Riyadh"] see "Egypt : " + aList[1] + nl + "KSA : " + aList[2] + nl 17.6 Set List Item To set the value of an item inside the list, we can use the next syntax List[Index] = Expression Example: aList = list(3) # create list contains three items aList[1] = "one" aList[2] = "two" aList[3] = "three" see aList 17.7 Search To find an item inside the list we can use the find() function Syntax: Find(List,ItemValue) ---> Item Index Find(List,ItemValue,nColumn) ---> Search in nColumn, returns the Item Index Find(List,ItemValue,nColumn,cAttribute) ---> Item Index Example: 17.4. Delete Item From List 102
147.
Ring Documentation, Release1.21.2 aList = ["one","two","three","four","five"] see find(aList,"three") # print 3 Example: mylist = [["one",1], ["two",2], ["three",3]] see find(mylist,"two",1) + nl # print 2 see find(mylist,2,2) + nl # print 2 Also we can use the binarysearch() function to search in sorted list. Syntax: BinarySearch(List,ItemValue) ---> Item Index BinarySearch(List,ItemValue,nColumn) ---> Search in nColumn, returns the Item Index Example: aList = ["one","two","three","four","five"] aList = sort(aList) see binarysearch(aList,"three") Output: five four one three two 4 17.8 Sort We can sort the list using the sort() function. Syntax: Sort(List) ---> Sorted List Sort(List,nColumn) ---> Sorted List based on nColumn Sort(List,nColumn,cAttribute) ---> Sorted List based on Object Attribute Example: aList = [10,12,3,5,31,15] aList = sort(aList) see aList # print 3 5 10 12 15 31 We can sort list of strings Example: 17.8. Sort 103
148.
Ring Documentation, Release1.21.2 mylist = ["mahmoud","samir","ahmed","ibrahim","mohammed"] see mylist # print list before sorting mylist = sort(mylist) # sort list see "list after sort"+nl see mylist # print ahmed ibrahim mahmoud mohammed samir We can sort a list based on a specific column. Example: aList = [ ["mahmoud",15000] , ["ahmed", 14000 ] , ["samir", 16000 ] , ["mohammed", 12000 ] , ["ibrahim",11000 ] ] aList2 = sort(aList,1) see aList2 Output: ahmed 14000 ibrahim 11000 mahmoud 15000 mohammed 12000 samir 16000 17.9 Reverse We can reverse a list using the reverse() function. Note: This functions support strings too Syntax: Reverse(List) ---> Reversed List Example: aList = [10,20,30,40,50] aList = reverse(aList) see aList # print 50 40 30 20 10 17.9. Reverse 104
149.
Ring Documentation, Release1.21.2 17.10 Insert Items To insert an item in the list we can use the insert() function. Syntax: Insert(List,Index,Item) The inserted item will be AFTER the Index Example: aList = ["A","B","D","E"] insert(aList,2,"C") # Inserts AFTER Index 2, "C" into Position 3 see aList # print A B C D E 17.11 Nested Lists The list may contain other lists Example: aList = [ 1 , [10,20,30] , 5 , [100,1000,5000] ] aList2 = [ "one","two", [3,4], [20,30], ["three", "four", "five",[100,200,300] ] ] see aList[2] # print 10 20 30 see aList[4][3] + nl # print 5000 see aList2[5][2] + nl # print four see aList2[5][4][3] # print 300 17.12 Copy Lists We can copy lists (including nested lists) using the Assignment operator. Example: aList = [ "one","two", [3,4], [20,30], ["three", "four", "five",[100,200,300] ] ] (continues on next page) 17.10. Insert Items 105
150.
Ring Documentation, Release1.21.2 (continued from previous page) aList2 = aList # Copy aList to aList2 aList2[5] = "other" # modify item number five see aList2[5] + nl # print other see aList[5] # print three four five 100 200 300 17.13 First-class lists Lists are first-class citizens where we can store lists in variables, pass lists to functions, and return lists from functions. Example: aList = duplicate( [1,2,3,4,5] ) see aList[10] + nl # print 5 see mylist() # print 10 20 30 40 50 func duplicate list nMax = len(list) for x = 1 to nMax list + list[x] next return list func mylist return [10,20,30,40,50] 17.14 Using Lists during definition We can use the list and the list items while we are defining the list for the first time. Example: aList = [ [1,2,3,4,5] , aList[1] , aList[1] ] see aList # print 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 Example: x = [ 1, 2, x ] ? x # print 1 2 1 2 ? len(x) # print 3 ? x[1] # print 1 ? x[2] # print 2 ? x[3] # print 1 2 Output: 1 2 1 (continues on next page) 17.13. First-class lists 106
151.
Ring Documentation, Release1.21.2 (continued from previous page) 2 3 1 2 1 2 17.15 Passing Lists to Functions Lists are passed to functions by reference, This means that the called function will work on the same list and can modify it. Example: func main aList = [1,2,3,4,5] # create list, local in function main myfunc(aList) # call function, pass list by reference see aList # print 1 2 3 4 5 6 7 8 9 10 func myfunc list list + [6,7,8,9,10] 17.16 Access List Items by String Index Instead of using numbers to determine the item index when we get item value or set item value, We can access items using string index if the item is a list contains two items and the first item is a string. Example: aList = [ ["one",1] , ["two",2] , ["three",3] ] see aList["one"] + nl + aList["two"] + nl + aList["three"] # print 1 2 3 This type of lists can be defined in a better syntax using the : and = operators. Example: aList = [ :one = 1 , :two = 2 , :three = 3 ] see aList["one"] + nl + aList["two"] + nl + aList["three"] + nl # print 1 2 3 see aList[1] # print one 1 Tip: using : before identifier (one word) means literal 17.15. Passing Lists to Functions 107
152.
Ring Documentation, Release1.21.2 Note: using = inside list definition create a list of two items where the first item is the left side and the second item is the right side. We can add new items to the list using the string index Example: aList = [] aList["Egypt"] = "Cairo" aList["KSA"] = "Riyadh" see aList["Egypt"] + nl + # print Cairo aList["KSA"] + nl # print Riyadh 17.17 Passing Parameters or Arguments Using List This type of lists is very good for passing parameters to functions Where the order of parameters will not be important (we can change the order). Also some parameters maybe optional. Example: myconnect ( [ :server = "myserver.com" , :port = 80 , :username = "mahmoud" , :password = "password" ] ) func myconnect mypara # print connection details see "User Name : " + mypara[:username] + nl + "Password : " + mypara[:password] + nl + "Server : " + mypara[:server] + nl + "Port : " + mypara[:port] 17.18 Passing Parameters or Arguments Using List Array Passing Arguments or Parameters to a Function in an array format Example: myList = [5,7,3,9] ### list with args or parms in an array result = sum(myList) See "Sum result: "+ result +n func sum(aList) acc = 0 sizeList = len(aList) for i = 1 to sizeList See aList[i] +nl acc = acc + aList[i] (continues on next page) 17.17. Passing Parameters or Arguments Using List 108
153.
Ring Documentation, Release1.21.2 (continued from previous page) next return acc 17.19 Return Parameters as List or Hash Table Return Parameters from a Function in an Array or Hash Format Example: sudoku = [ [2,9,0], [0,0,1], [0,0,0] ] aOutput = myFunctionArray(sudoku) See "Return Array: T/F: "+ aOutput[1] +" Row: "+ aOutput[2] +" Col: "+␣ ˓ →aOutput[3] +nl aOutput = myFunctionHash(sudoku) See "Return Hash.: T/F: "+ aOutput[:lValue] +" Row: "+ aOutput[:nRow] +" Col: "+␣ ˓ →aOutput[:nCol] +nl ###---------------------------------- ### isSolvedSoduku - Return ARRAY Func myFunctionArray(sudoku) for Row = 1 to 9 for Col = 1 to 9 if sudoku[Row][Col] = 0 //---------------------------- // Return Array with 3 fields return [False, Row, Col] ok next next return [True, Row, Col] ###---------------------------------- ### isSolvedSoduku - Return HASH Func myFunctionHash(sudoku) for Row = 1 to 3 for Col = 1 to 3 if sudoku[Row][Col] = 0 //--------------------------------- // Return Hash Table with 3 fields return [ :lValue = False, :nRow = Row, :nCol = Col ] (continues on next page) 17.19. Return Parameters as List or Hash Table 109
154.
Ring Documentation, Release1.21.2 (continued from previous page) ok next next return [ :lValue = False, :nRow = Row, :nCol = Col ] ###----------------------------- 17.20 Creating a Multi-Dimensional Array using List A Multi-Dimensional Array of any size can be built using recursion in a Function Example: ###--------------------------------------------------------- ### Create Array -- Dimensions Any Size: 3D, 4D, 5D etc dimList = [4,3,4] bList = createDimList(dimList) ###--------------------------------------------------------- ### Populate the arrays using a counter 1 , 4x4x4 = 256 , 2x3x4x5x6 = 720 Counter = 1 for Col=1 to dimList[1] for Row=1 to dimList[2] for Dep=1 to dimList[3] blist[Col][Row][Dep] = Counter Counter++ next next next ###----------------------------------------------- ### Print the array elements in block format for Col=1 to dimList[1] for Row=1 to dimList[2] for Dep=1 to dimList[3] See bList[Col][Row][Dep] See " " next See nl next See nl next ###=========================== ### FUNCTIONS ###----------------------------------------------------------------------- (continues on next page) 17.20. Creating a Multi-Dimensional Array using List 110
155.
Ring Documentation, Release1.21.2 (continued from previous page) ### Recursive Create a Dimension Array ### Call by passing an array of dimensions: dimList = [2,3,4,5] ### Drop the first entry every iteration call, making newParms ### ### Example: ### dimList = [4,2,3,2] <<< Number and size of dimensions in array␣ ˓ →format ### bList = createDimList(dimList) <<< Call using the array as input func createDimList(dimArray) sizeList = len(dimArray) newParms = [] for i = 2 to sizeList Add(newParms, dimArray[i]) next alist = list(dimArray[1]) if sizeList = 1 return aList ok for t in alist t = createDimList(newParms) next return alist 17.21 Swap Lists and Items We can swap lists/items using the Swap() function. Syntax: swap(aList1,aList2) swap(aList,nItem1,nItem2) Example: aList = [:one,:two,:four,:three] see aList see copy("*",50) + nl swap(aList,3,4) see aList Output one two (continues on next page) 17.21. Swap Lists and Items 111
CHAPTER EIGHTEEN STRINGS In this chapterwe are going to learn about strings creation and manipulation. 18.1 String Literals Syntax: cStr = "This is a string" cStr2 = 'Another string' cStr3 = :JustAnotherString cStr4 = `Yet "another" 'string' ! ` 18.2 Get String Length We can get the string length (letters count inside a string) using the len() function Syntax: len(string) ---> string length Example: cStr = "How are you?" see cStr + nl see "String size : " + len(cStr) + nl 18.3 Convert Letters Case Syntax: lower(string) ---> convert string letters to lower case upper(string) ---> convert string letters to UPPER case Example: cStr = "Welcome To The Ring Programming Language" see cStr + nl + upper(cStr) + nl + lower(cStr) 113
158.
Ring Documentation, Release1.21.2 18.4 Access String Letters We can access a letter inside a string by the letter index Syntax: string[index] ---> get string letter string[index] = letter # set string letter Example: # print user name letter by letter (each letter in new line) See "Hello, Enter your name : " give cName for x = 1 to len(cName) see nl + cName[x] next We can use for in to get string letters. Example: # print user name letter by letter (each letter in new line) See "Hello, Enter your name : " give cName for x in cName see nl + x next We can modify the string letters Example: # convert the first letter to UPPER case See "Enter your name : " give cName cName[1] = upper(cName[1]) see "Hello " + cName 18.5 Left() Function We can get a specified number of characters from a string using the Left() function. The starting position is 1. Syntax: Left(string,count) Example: see left("Hello World!",5) # print Hello 18.4. Access String Letters 114
159.
Ring Documentation, Release1.21.2 18.6 Right() Function We can get a specified number of characters from a string using the Right() function. The starting position is the last character on the right. Syntax: Right(string,count) Example: see Right("Hello World!",6) # print World! 18.7 Trim() Function We can remove all leading and trailing spaces from a string using the Trim() function. Syntax: trim(string) Example: cMsg = " Welcome " see trim(cMsg) # print Welcome 18.8 Copy() Function We can duplicate a string more than one time using the copy() function. Syntax: copy(string,nCount) ---> string replicated nCount times Example see copy("***hello***",3) # print ***hello******hello******hello*** 18.9 Lines() Function We can count the number of lines inside a string using the Lines() function. Syntax: lines(string) ---> Number of lines inside the string Example: 18.6. Right() Function 115
160.
Ring Documentation, Release1.21.2 cStr = "Hello How are you? are you fine?" see lines(cStr) # print 3 18.10 Substr() Function We can work on sub strings inside a string using the substr() function. Using Substr() we can • Find substring • Get substring from position to end • Get Number of characters from position • Transform Substring To Another Substring 18.11 Find substring Syntax: substr(string,substring) ---> the starting position of substring in string Example: cStr = "Welcome to the Ring programming language" see substr(cStr,"Ring") # print 16 18.12 Get substring from position to end Syntax: substr(string,position) ---> Get substring starting from position to end Example: cStr = "Welcome to the Ring programming language" nPos = substr(cStr,"Ring") # nPos = 16 see substr(cStr,nPos) # print Ring programming language 18.10. Substr() Function 116
161.
Ring Documentation, Release1.21.2 18.13 Get Number of Characters From Position Syntax: substr(string,position,count) ---> Get characters starting from position Example: cStr = "Welcome to the Ring programming language" nPos = substr(cStr,"Ring") # nPos = 16 see substr(cStr,nPos,4) # print Ring 18.14 Transform Substring To Another Substring Syntax: substr(string,substring,newsubstring) ---> Transformed string (Match case) substr(string,substring,newsubstring,1) ---> Transformed string (Ignore case) Example: cStr = "Welcome to the New programming language" see substr(cStr,"New","Ring") + nl # print Welcome to the Ring programming language see substr(cStr,"new","Ring",1)+ nl # print Welcome to the Ring programming language 18.15 strcmp() Function In Ring we can use the = operator to compare between strings Also, we can compare between two strings using the strcmp() function. Syntax: strcmp(cString1,cString2) ---> value = 0 if cString1 = cString2 value < 0 if cString1 < cString2 value > 0 if cString1 > cString2 Example: see strcmp("hello","hello") + nl + strcmp("abc","bcd") + nl + strcmp("bcd","abc") + nl Output: 0 -1 1 18.13. Get Number of Characters From Position 117
162.
Ring Documentation, Release1.21.2 18.16 Reverse() Function Using the Reverse() function we can reverse the string characters Note: This functions support lists too Syntax: Reverse(cString) ---> cReversedString cStr = "Welcome to Ring" ? reverse(cStr) # gniR ot emocleW 18.17 str2list() and list2str() Functions We can convert string lines to list items using the str2list() function. Also we can convert the list to a string using list2str() function. Syntax: str2list(cString) ---> list contains the string lines list2str(aList) ---> string contains the list items list2str(aList,[nStart],[nEnd]) ---> string contains the list items from nStart to nEnd Example: /* output: ** Items : 4 ** Item : Hello ** Item : How are you ? ** Item : are you fine ? ** Item : ok ** list2Str result = Hello ** How are you ? ** are you fine ? ** ok ** Done */ mystr = "Hello How are you ? are you fine ? ok" mylist = str2list(mystr) see "Items : " + len(mylist) + nl for x in mylist see "Item : " + x + nl next (continues on next page) 18.16. Reverse() Function 118
163.
Ring Documentation, Release1.21.2 (continued from previous page) newstr = list2str(mylist) see "list2Str result = " + newstr if mystr = newstr see nl + "Done" else see nl + "Error!" ok Example: aList = 1:10 cStr = list2str(aList,6,10) ? cStr # 6 7 8 9 10 18.18 Merge binary characters From Ring 1.0 we can create binary strings and do operations on these strings. Starting from Ring 1.8, we can get individual characters from these strings and merge them together using the ‘+’ operator. Example: cStr = "Welcome" ? cstr[1] + cstr[2] + cStr[5] v = cstr[1] + cstr[2] + cStr[5] ? v ? len(v) c1 = cStr[1] ? c1 aList = [1,2,3] cStr = "" for item in aList cStr += int2bytes(item) next ? "All String" ? len(cStr) ? "First Part" n1 = cStr[1] + cStr[2] + cStr[3] + cStr[4] ? len(n1) ? "Second Part" n2 = cStr[5] + cStr[6] + cStr[7] + cStr[8] ? len(n2) ? "Third Part" n3 = cStr[9] + cStr[10] + cStr[11] + cStr[12] ? len(n3) ? "All String" cString = cStr[1] + cStr[2] + cStr[3] + cStr[4] + cStr[5] + cStr[6] + cStr[7] + cStr[8] + (continues on next page) 18.18. Merge binary characters 119
164.
Ring Documentation, Release1.21.2 (continued from previous page) cStr[9] + cStr[10] + cStr[11] + cStr[12] ? len(cString) ? ascii(cStr[1]) ? len(cStr[2]) Output: Weo Weo 3 W All String 12 First Part 4 Second Part 4 } Third Part 4 All String 12 1 1 18.18. Merge binary characters 120
165.
CHAPTER NINETEEN DATE AND TIME Inthis chapter we are going to learn about the date and time functions. 19.1 Clock() Function Syntax: Clock() ---> The number of clock ticks from program start Example: See "Calculate performance" + nl t1 = clock() for x = 1 to 1000000 next see clock() - t1 19.2 ClocksPerSecond() Function Return how many clocks in one second Syntax: clockspersecond() ---> Number of clocks in one second Example: # Wait 1 second t = clock() while clock() - t <= clockspersecond() end 121
166.
Ring Documentation, Release1.21.2 19.3 Time() Function We can get the system time using the Time() function. Example: See "Time : " + time() 19.4 Date() Function We can get the date using the Date() function. Syntax: Date() ---> String represent the date "dd/mm/yyyy" Example: See "Date : " + date() # Date : 24/05/2015 19.5 TimeList() Function We can print the date and the time information using the TimeList() function. Syntax: TimeList() ---> List contains the time and date information. The next table presents the list items 19.3. Time() Function 122
167.
Ring Documentation, Release1.21.2 index value 1 abbreviated weekday name 2 full weekday name 3 abbreviated month name 4 full month name 5 Date & Time 6 Day of the month 7 Hour (24) 8 Hour (12) 9 Day of the year 10 Month of the year 11 Minutes after hour 12 AM or PM 13 Seconds after the hour 14 Week of the year (sun-sat) 15 day of the week 16 date 17 time 18 year of the century 19 year 20 time zone 21 percent sign Example: /* Output: ** Sun abbreviated weekday name ** Sunday full weekday name ** May abbreviated month name ** May full month name ** 05/24/15 09:58:38 Date & Time ** 24 Day of the month ** 09 Hour (24) ** 09 Hour (12) ** 144 Day of the year ** 05 Month of the year ** 58 Minutes after hour ** AM AM or PM ** 38 Seconds after the hour ** 21 Week of the year (sun-sat) ** 0 day of the week ** 05/24/15 date ** 09:58:38 time ** 15 year of the century ** 2015 year ** Arab Standard Time time zone ** % percent sign */ See TimeList() Example: 19.5. TimeList() Function 123
168.
Ring Documentation, Release1.21.2 See "Day Name : " + TimeList()[2] # Sunday Example: See "Month Name : " + TimeList()[4] # May 19.6 AddDays() Function Syntax: AddDays(cDate,nDays) ---> Date from cDate and after nDays Example: cDate = date() see cDate + nl # 24/05/2015 cDate = adddays(cDate,10) see cDate + nl # 03/06/2015 19.7 DiffDays() Function Syntax: DiffDays(cDate1,cDate2) ---> number of days (Date1 - Date2) Example: cDate1 = date() see cDate1 + nl # 24/05/2015 cDate2 = adddays(cDate1,10) see cDate2 + nl # 03/06/2015 see "DiffDays = " + diffdays(cDate1,cDate2) + nl # -10 see "DiffDays = " + diffdays(cDate2,cDate1) + nl # 10 19.8 EpochTime() Function Syntax: EpochTime( cDate, cTime ) ---> Epoch Seconds Example: ###------------------------------------------------------------- # EpochTime() # Example --- EpochSec = EpochTime( Date(), Time() ) # Call Format: EpochSec = EpochTime( "15/07/2016", "10:15:30" ) # EpochSec = 1468577730 (continues on next page) 19.6. AddDays() Function 124
CHAPTER TWENTY CHECK DATA TYPEAND CONVERSION In this chapter we are going to learn about the functions that can be used for • Checking Data Type • Checking Character • Conversion 20.1 Check Data Type The next functions can be used to check the data type • isstring() • isnumber() • islist() • type() • isnull() 20.2 IsString() Function Using the IsString() function we can know if the value is a string or not Syntax: IsString(value) ---> 1 if the value is a string or 0 if not Example: see isstring(5) + nl + # print 0 isstring("hello") + nl # print 1 126
171.
Ring Documentation, Release1.21.2 20.3 IsNumber() Function Using the IsNumber() function we can know if the value is a number or not Syntax: IsNumber(value) ---> 1 if the value is a number or 0 if not Example: see isnumber(5) + nl + # print 1 isnumber("hello") + nl # print 0 20.4 IsList() Function Using the IsList() function we can know if the value is a list or not Syntax: IsList(value) ---> 1 if the value is a list or 0 if not Example: see islist(5) + nl + # print 0 islist("hello") + nl + # print 0 islist([1,3,5]) # print 1 20.5 Type() Function We can know the type of a value using the Type() Function. Syntax: Type(value) ---> The Type as String Example: see Type(5) + nl + # print NUMBER Type("hello") + nl + # print STRING Type([1,3,5]) # print LIST 20.6 IsNULL() Function We can check the value to know if it’s null or not using the IsNULL() function Syntax: IsNULL(value) ---> 1 if the value is NULL or 0 if not Example: 20.3. IsNumber() Function 127
172.
Ring Documentation, Release1.21.2 see isnull(5) + nl + # print 0 isnull("hello") + nl + # print 0 isnull([1,3,5]) + nl + # print 0 isnull("") + nl + # print 1 isnull("NULL") # print 1 20.7 Check Character The next functions can be used to check character • isalnum() • isalpha() • iscntrl() • isdigit() • isgraph() • islower() • isprint() • ispunct() • isspace() • isupper() • isxdigit() 20.8 IsAlNum() Function We can test a character or a string using the IsAlNum() Function Syntax: IsAlNum(value) ---> 1 if the value is digit/letter or 0 if not Example: see isalnum("Hello") + nl + # print 1 isalnum("123456") + nl + # print 1 isalnum("ABCabc123") + nl + # print 1 isalnum("How are you") # print 0 because of spaces 20.7. Check Character 128
173.
Ring Documentation, Release1.21.2 20.9 IsAlpha() Function We can test a character or a string using the IsAlpha() Function Syntax: IsAlpha(value) ---> 1 if the value is a letter or 0 if not Example: see isalpha("Hello") + nl + # print 1 isalpha("123456") + nl + # print 0 isalpha("ABCabc123") + nl + # print 0 isalpha("How are you") # print 0 20.10 IsCntrl() Function We can test a character or a string using the IsCntrl() Function Syntax: IsCntrl(value) ---> 1 if the value is a control character (no printing position) or 0 if not Example: See iscntrl("hello") + nl + # print 0 iscntrl(nl) # print 1 20.11 IsDigit() Function We can test a character or a string using the IsDigit() Function Syntax: IsDigit(value) ---> 1 if the value is a digit or 0 if not Example: see isdigit("0123456789") + nl + # print 1 isdigit("0123a") # print 0 20.9. IsAlpha() Function 129
174.
Ring Documentation, Release1.21.2 20.12 IsGraph() Function We can test a character or a string using the IsGraph() Function Syntax: IsGraph(value) ---> 1 if the value can be printed (Except space) or 0 if not Example: see isgraph("abcdef") + nl + # print 1 isgraph("abc def") # print 0 20.13 IsLower() Function We can test a character or a string using the IsLower() Function Syntax: IsLower(value) ---> 1 if the value is lowercase letter or 0 if not Example: see islower("abcDEF") + nl + # print 0 islower("ghi") # print 1 20.14 IsPrint() Function We can test a character or a string using the IsPrint() Function Syntax: IsPrint(value) ---> 1 if the value occupies a printing position or 0 if not Example: see isprint("Hello") + nl + # print 1 isprint("Nice to see you") + nl + # print 1 isprint(nl) # print 0 20.15 IsPunct() Function We can test a character or a string using the IsPunct() Function Syntax: IsPunct(value) ---> 1 if the value is a punctuation character or 0 if not Example: 20.12. IsGraph() Function 130
175.
Ring Documentation, Release1.21.2 see ispunct("hello") + nl + # print 0 ispunct(",") # print 1 20.16 IsSpace() Function We can test a character or a string using the IsSpace() Function Syntax: IsSpace(value) ---> 1 if the value is a white-space or 0 if not Example: see isspace(" ") + nl + # print 1 isspace("test") # print 0 20.17 IsUpper() Function We can test a character or a string using the IsUpper() Function Syntax: IsUpper(value) ---> 1 if the value is an uppercase alphabetic letter or 0 if not Example: see isupper("welcome") + nl + # print 0 isupper("WELCOME") # print 1 20.18 IsXdigit() Function We can test a character or a string using the IsXdigit() Function Syntax: IsXdigit(value) ---> 1 if the value is a hexadecimal digit character or 0 if not Example: see isxdigit("0123456789abcdef") + nl + # print 1 isxdigit("123z") # print 0 20.16. IsSpace() Function 131
176.
Ring Documentation, Release1.21.2 20.19 Conversion The next functions can be used for conversion • number() • string() • ascii() • char() • hex() • dec() • str2hex() • hex2str() 20.20 Number() Function We can convert strings to numbers using the Number() function or the + operator. Syntax: Number(string) ---> Number 0 + string ---> Number Example: see number("5") + 5 + nl # print 10 see 0 + "10" + 2 # print 12 20.21 String() Function We can convert numbers to strings using the String() function or the + operator. Syntax: String(number) ---> String "" + number ---> String Example: see string(5) + 5 + nl # print 55 see "" + 10 + 2 # print 102 20.19. Conversion 132
177.
Ring Documentation, Release1.21.2 20.22 Ascii() Function We can get the ASCII code for a letter using the Ascii() function Syntax: Ascii(character) ---> ASCII Code Example: See ascii("m") + nl + # print 109 ascii("M") # print 77 20.23 Char() Function We can convert the ASCII code to character using the Char() function. Syntax: Char(ASCII Code) ---> character Example: See char(109) + nl + # print m char(77) # print M 20.24 Hex() Function We can convert decimal to hexadecimal using the Hex() function. Syntax: Hex(decimal) ---> hexadecimal Example: See hex(10) + nl + # print a hex(200) # print c8 20.25 Dec() Function We can convert hexadecimal to decimal using the Dec() function Syntax: Dec(hexadecimal) ---> decimal Example: 20.22. Ascii() Function 133
178.
Ring Documentation, Release1.21.2 See dec("a") + nl + # print 10 dec("c8") # print 200 20.26 Str2hex() Function We can convert string characters to hexadecimal characters using the Str2hex() function. Syntax: Str2hex(string) ---> hexadecimal string Example: See str2hex("hello") # print 68656c6c6f 20.27 Hex2str() Function We can convert hexadecimal characters to string using the Hex2str() function Syntax: Hex2Str(Hexadecimal string) ---> string Example: See hex2str("68656c6c6f") # print hello 20.26. Str2hex() Function 134
179.
CHAPTER TWENTYONE MATHEMATICAL FUNCTIONS In thischapter we are going to learn about the mathematical functions 21.1 List of functions The Ring programming language comes with the next mathematical functions Function Description sin(x) Returns the sine of an angle of x radians cos(x) Returns the cosine of an angle of x radians tan(x) Returns the tangent of an angle of x radians asin(x) Returns the principal value of the arc sine of x, expressed in radians acos(x) Returns the principal value of the arc cosine of x, expressed in radians atan(x) Returns the principal value of the arc tangent of x, expressed in radians atan2(y,x) Returns the principal arc tangent of y/x, in the interval [-pi,+pi] radians sinh(x) Returns the hyperbolic sine of x radians cosh(x) Returns the hyperbolic cosine of x radians tanh(x) Returns the hyperbolic tangent of x radians exp(x) Returns the value of e raised to the xth power log(x) Returns the natural logarithm of x (the base of e) log(x,b) Returns the logarithm of x to the base of b log10(x) Returns the common logarithm (base-10 logarithm) of x ceil(x) Returns the smallest integer value greater than or equal to x floor(x) Returns the largest integer value less than or equal to x fabs(x) Returns the absolute value of x. pow(x,y) Returns x raised to the power of y sqrt(x) Returns the square root of x random(x) Returns a random number in the range [0,x] or [0,-x] srandom(x) Initialize random number generator unsigned(n,n,c) Perform operation using unsigned numbers decimals(n) Determine the decimals digits after the point in float/double numbers 135
180.
Ring Documentation, Release1.21.2 21.2 Example See "Mathematical Functions" + nl See "Sin(0) = " + sin(0) + nl See "Sin(90) radians = " + sin(90) + nl See "Sin(90) degree = " + sin(90*3.14/180) + nl See "Cos(0) = " + cos(0) + nl See "Cos(90) radians = " + cos(90) + nl See "Cos(90) degree = " + cos(90*3.14/180) + nl See "Tan(0) = " + tan(0) + nl See "Tan(90) radians = " + tan(90) + nl See "Tan(90) degree = " + tan(90*3.14/180) + nl See "asin(0) = " + asin(0) + nl See "acos(0) = " + acos(0) + nl See "atan(0) = " + atan(0) + nl See "atan2(1,1) = " + atan2(1,1) + nl See "sinh(0) = " + sinh(0) + nl See "sinh(1) = " + sinh(1) + nl See "cosh(0) = " + cosh(0) + nl See "cosh(1) = " + cosh(1) + nl See "tanh(0) = " + tanh(0) + nl See "tanh(1) = " + tanh(1) + nl See "exp(0) = " + exp(0) + nl See "exp(1) = " + exp(1) + nl See "log(1) = " + log(1) + nl See "log(2) = " + log(2) + nl See "log10(1) = " + log10(1) + nl See "log10(2) = " + log10(2) + nl See "log10(10) = " + log10(10) + nl See "Ceil(1.12) = " + Ceil(1.12) + nl See "Ceil(1.72) = " + Ceil(1.72) + nl See "Floor(1.12) = " + floor(1.12) + nl See "Floor(1.72) = " + floor(1.72) + nl See "fabs(1.12) = " + fabs(1.12) + nl See "fabs(1.72) = " + fabs(1.72) + nl See "pow(2,3) = " + pow(2,3) + nl see "sqrt(16) = " + sqrt(16) + nl Program Output: Mathematical Functions Sin(0) = 0 Sin(90) radians = 0.89 (continues on next page) 21.2. Example 136
181.
Ring Documentation, Release1.21.2 (continued from previous page) Sin(90) degree = 1.00 Cos(0) = 1 Cos(90) radians = -0.45 Cos(90) degree = 0.00 Tan(0) = 0 Tan(90) radians = -2.00 Tan(90) degree = 1255.77 asin(0) = 0 acos(0) = 1.57 atan(0) = 0 atan2(1,1) = 0.79 sinh(0) = 0 sinh(1) = 1.18 cosh(0) = 1 cosh(1) = 1.54 tanh(0) = 0 tanh(1) = 0.76 exp(0) = 1 exp(1) = 2.72 log(1) = 0 log(2) = 0.69 log10(1) = 0 log10(2) = 0.30 log10(10) = 1 Ceil(1.12) = 2 Ceil(1.72) = 2 Floor(1.12) = 1 Floor(1.72) = 1 fabs(1.12) = 1.12 fabs(1.72) = 1.72 pow(2,3) = 8 sqrt(16) = 4 21.3 Random() Function The Random() function generate a random number and we can set the maximum value (optional). Syntax: Random(x) ---> Random number in the range [0,x] Example: for x = 1 to 20 see "Random number : " + random() + nl + "Random number Max (100) : " + random(100) + nl next Program Output: 21.3. Random() Function 137
182.
Ring Documentation, Release1.21.2 Random number : 31881 Random number Max (100) : 80 Random number : 5573 Random number Max (100) : 63 Random number : 2231 Random number Max (100) : 43 Random number : 12946 Random number Max (100) : 39 Random number : 22934 Random number Max (100) : 48 Random number : 4690 Random number Max (100) : 52 Random number : 13196 Random number Max (100) : 65 Random number : 30390 Random number Max (100) : 87 Random number : 4327 Random number Max (100) : 77 Random number : 12456 Random number Max (100) : 17 Random number : 28438 Random number Max (100) : 13 Random number : 30503 Random number Max (100) : 6 Random number : 31769 Random number Max (100) : 94 Random number : 8274 Random number Max (100) : 65 Random number : 14390 Random number Max (100) : 90 Random number : 28866 Random number Max (100) : 12 Random number : 24558 Random number Max (100) : 70 Random number : 29981 Random number Max (100) : 77 Random number : 12847 Random number Max (100) : 63 Random number : 6632 Random number Max (100) : 60 21.4 SRandom() Function The SRandom() function initialize random number generator. Syntax: SRandom(x) 21.4. SRandom() Function 138
183.
Ring Documentation, Release1.21.2 21.5 Unsigned() Function We can use unsigned numbers using the Unsigned() function. Syntax: Unsigned(nNum1,nNum2,cOperator) --> result of cOperator operation on nNum1,nNum2 Example: see oat_hash("hello") + nl # Jenkins hash function - https://en.wikipedia.org/wiki/Jenkins_hash_function func oat_hash cKey h = 0 for x in cKey h = unsigned(h,ascii(x),"+") h = unsigned(h,unsigned(h,10,"<<"),"+") r = unsigned(h,6,">>") h = unsigned(h, r,"^") next h = unsigned(h,unsigned(h,3,"<<"),"+") h = unsigned(h,unsigned(h,11,">>"),"^") h = unsigned(h,unsigned(h,15,"<<"),"+") return h Output: 3372029979.00 21.6 Decimals() Functions We can determine the decimals numbers count after the point in float/double numbers using the decimals() function. Syntax: Decimals(nDecimalsCount) Example: x = 1.1234567890123 for d = 0 to 14 decimals(d) see x + nl next Output: 1 1.1 1.12 1.123 1.1235 (continues on next page) 21.5. Unsigned() Function 139
184.
Ring Documentation, Release1.21.2 (continued from previous page) 1.12346 1.123457 1.1234568 1.12345679 1.123456789 1.1234567890 1.12345678901 1.123456789012 1.1234567890123 1.12345678901230 21.7 Using _ in numbers We can use _ between numbers digits. Example: x = 1_000_000 see type(x)+nl see x+1+nl Output: NUMBER 100000001 21.8 Using f after numbers We can use the ‘f’ letter after numbers. Example: x = 19.99f see type(x) + nl Output: NUMBER 21.9 Using Hexadecimal Numbers We can write Hexadecimal number by preceding with “0x” or “0X” Example: x = 0x10 ? x # 16 x = 0xff (continues on next page) 21.7. Using _ in numbers 140
185.
Ring Documentation, Release1.21.2 (continued from previous page) ? x # 255 x = 0x0A ? x # 10 ? 0xFFFF # 65535 ? 0x0A + 1 # 10+1 = 11 21.9. Using Hexadecimal Numbers 141
Ring Documentation, Release1.21.2 • fwrite() • fexists() • direxists() • getpathtype() • getfilesize() • Numbers and Bytes 22.1 Read() Function We can read the file content using the Read() function Syntax: Read(cFileName) ---> String contains the file content Example: see read("myfile.txt") The read function can read binary files too Example: see read("myapp.exe") 22.2 Write() Function We can write string to file using the Write() function The write function can write binary data to binary files. Syntax: Write(cFileName,cString) # write string cString to file cFileName Example: # copy file cFile = read("ring.exe") write("ring2.exe",cFile) 22.1. Read() Function 143
188.
Ring Documentation, Release1.21.2 22.3 Dir() Function We can get the folder contents (files & sub folders) using the Dir() function. Syntax: Dir(cFolderPath) ---> List contains files & sub folders. This function returns a list and each list item is a list of two items • File/sub folder name • Type (0 = File , 1 = Folder/Directory) Example: see "Testing DIR() " + nl mylist = dir("C:myfolder") for x in mylist if x[2] see "Directory : " + x[1] + nl else see "File : " + x[1] + nl ok next see "Files count : " + len(mylist) 22.4 Rename() Function We can rename files using the Rename() function Syntax: Rename(cOldFileName,cNewFileName) ---> Number ( Status: Success (0) , Error (-1) ) Example: rename("file.txt","help.txt") 22.5 Remove() Function We can delete a file using the Remove() function Syntax: Remove(cFileName) Example: remove("test.txt") 22.3. Dir() Function 144
189.
Ring Documentation, Release1.21.2 22.6 Fopen() Function We can open a file using the Fopen() function Syntax: Fopen(cFileName,cMode) ---> File Handle Mode Description “r” Reading (The file must exist) “w” Writing (create empty file / overwrite) “a” Appends (create file if it doesn’t exist) “r+” update (reading/writing) “w+” Create empty file (reading/writing) “a+” reading & appending 22.7 Fclose() Function When we open a file using fopen() function, we can close it using the Fclose() function Syntax: Fclose(file handle) 22.8 Fflush() Function We can flushes the output buffer of a stream using the Fflush() function Syntax: Fflush(file handle) 22.9 Freopen() Function We can open another file using the same file handle and at the same time close the old file Syntax: Freopen(cFileName,cMode,file handle) ---> file handle Example: freopen("myprogoutput.txt","w+",stdout) see "welcome" + nl for x = 1 to 10 see x + nl next (continues on next page) 22.6. Fopen() Function 145
190.
Ring Documentation, Release1.21.2 (continued from previous page) /* ** Read : https://en.wikipedia.org/wiki/Device_file#Device_files ** The next code is not portable, we can use iswindows() before ** using it and we can write special code for each operating system. */ freopen("CON","w",stdout) # For Microsoft Windows see "Done" + nl # print to stdout again Output: # Output to stdout Done # Output to file : myprogoutput.txt welcome 1 2 3 4 5 6 7 8 9 10 22.10 Tempfile() Function The function Tempfile() creates a temp. file (binary). The file will be deleted automatically when the stream is closed Syntax: TempFile() ---> file handle 22.11 Tempname() Function We can generate temp. file name using the Tempname() function The generated name will be different from the name of any existing file Syntax: Tempname() ---> generated file name as string 22.10. Tempfile() Function 146
191.
Ring Documentation, Release1.21.2 22.12 Fseek() Function We can set the file position of the stream using the Fseek() function Syntax: Fseek(file handle, nOffset, nWhence) ---> zero if successful The next table presents the nWhence values Value Description 0 Beginning of file 1 Current position 2 End of file 22.13 Ftell() Function We can know the current file position of a stream using the Ftell() function Syntax: Ftell(file handle) ---> file position as number 22.14 Rewind() Function We can set the file position to the beginning of the file using the Rewind() function Syntax: Rewind(file handle) 22.15 Fgetpos() Function We can get handle to the current file position using the Fgetpos() function Syntax: Fgetpos(file handle) ---> position handle 22.12. Fseek() Function 147
192.
Ring Documentation, Release1.21.2 22.16 Fsetpos() Function We can set the current file position using the Fsetpos() function Syntax: Fsetpos(file handle,position handle) 22.17 Clearerr() Function We can clear the EOF error and the error indicators of a stream using the clearerr() function Syntax: Clearerr(file handle) 22.18 Feof() Function We can test the end-of-file indicator using the Feof() function Syntax: Feof(file handle) ---> returns 1 if EOF and 0 if not 22.19 Ferror() Function We can test the error indicator of a given stream using the Ferror() function Syntax: Ferror(file handle) ---> returns 1 if error and 0 if not 22.20 Perror() Function We can print error message to the stderr using the Perror() function Syntax: Perror(cErrorMessage) 22.16. Fsetpos() Function 148
193.
Ring Documentation, Release1.21.2 22.21 Fgetc() Function We can get the next character from the stream using the Fgetc() function Syntax: Fgetc(file handle) ---> returns character or EOF 22.22 Fgets() Function We can read new line from the stream using the Fgets() function Syntax: Fgets(file handle,nSize) ---> string The function stop when nSize characters are read, new line character is read or EOF. 22.23 Fputc() Function We can write a character to the stream using the Fputc() function Syntax: Fputc(file handle,cChar) 22.24 Fputs() Function We can write a string to the stream using the Fputs() function Syntax: Fputs(file handle,cString) 22.25 Ungetc() Function We can push a character to the stream using the Ungetc() function The character will be available for the next read Syntax: Ungetc(file handle,character) 22.21. Fgetc() Function 149
194.
Ring Documentation, Release1.21.2 22.26 Fread() Function We can read data from a stream using the Fread() function Syntax: Fread(file handle,nSize) 22.27 Fwrite() Function We can write data to a stream using the Fwrite() function Syntax: Fwrite(file handle,cString) 22.28 Fexists() Function We can check if a file exists using the Fexists() function Syntax: Fexists(cFileName) ---> returns 1 if the file exists Example: see fexists(exefolder()+"ring.exe") + nl + fexists(exefolder()+"nothing.exe") + nl Output: 1 0 22.29 Direxists() Function Syntax: Direxists(cDirPath) ---> returns 1 if the directory exists Example: ? direxists("b:ring") + nl + direxists("b:ringbin2") Output: 1 0 22.26. Fread() Function 150
195.
Ring Documentation, Release1.21.2 22.30 Getpathtype() Function We can get the type a given path (file or directory) using the Getpathtype() function Syntax: Getpathtype(cPath) ---> 0 if the path doesn't exists 1 if it corresponds to existing file 2 if it corresponds to existing directory -1 if the path exists but has an unknown type (e.g. a pipe) Example: ? Getpathtype("b:ringbinring.exe") + nl + Getpathtype("b:ring") + nl + Getpathtype("b:ringbin2") Output: 1 2 0 22.31 Getfilesize() Function We can get the size in bytes of a given file using the Getfilesize() function Syntax: Getfilesize(cFilePath) ---> file size in bytes as a positive Number or -1 in case of failure (e.g. path doesn't exist or not a regular file) Example: ? Getfilesize("b:ringbinring.exe") + nl + Getfilesize("b:ring") + nl + Getfilesize("b:ringring2.exe") Output: 80384 -1 -1 22.30. Getpathtype() Function 151
196.
Ring Documentation, Release1.21.2 22.32 Example The next program test some of the file functions See "testing file functions" + nl See "open file" + nl fp = fopen(exefolder() + "../tests/scripts/s65.ring","r") See "reopen" + nl fp = freopen(exefolder() + "../tests/scripts/s78.ring","r",fp) See "close file" + nl fclose(fp) see "temp file" + nl fp = tempfile() fclose(fp) see "temp name" + nl see tempname() + nl remove(exefolder() + "../tests/scripts/mytest2.txt") write(exefolder() + "../tests/scripts/tests1.txt","hello") rename(exefolder() + "../tests/scripts/test1.txt",exefolder() + "../tests/scripts/mytests2.txt") see "print file" + nl fp = fopen(exefolder() + "../samples/fromdoc/filefuncs.ring","r") r = fgetc(fp) while isstring(r) see r r = fgetc(fp) end fclose(fp) see nl+"print line from the file" + nl fp = fopen(exefolder() + "../samples/fromdoc/filefuncs.ring","r") r = fgets(fp,33) see r + nl fclose(fp) fp = fopen(exefolder() + "../tests/scripts/test78.txt","w+") fseek(fp,0,2) # goto end of file fputc(fp,"t") fputc(fp,"e") fputc(fp,"s") fputc(fp,"t") fputs(fp,"tests2") fclose(fp) see "print file" + nl see read(exefolder() + "../tests/scripts/test78.txt") fp = fopen(exefolder() + "../tests/scripts/test78.txt","r") (continues on next page) 22.32. Example 152
197.
Ring Documentation, Release1.21.2 (continued from previous page) see "testing ungetc() " + nl for x = 1 to 3 r = fgetc(fp) see r + nl ungetc(fp,r) next fclose(fp) see "testing fread() " + nl fp = fopen(exefilename(),"rb") r = fread(fp,100) see r + nl fclose(fp) see "testing fwrite() " + nl fp = fopen(exefolder() + "../tests/scripts/test1.txt","wb") fwrite(fp,r) fclose(fp) The next example print part of the content of a binary file see "Testing: fread()" +" FileName: "+ exefilename() +nl +nl fp = fopen(exefilename(),"rb") r = fread(fp,800) for n =1 to len(r) if isprint(substr(r, n, 1)) see substr(r, n, 1) else see "." ok ### 80 char per line if n % 80 = 0 see nl ok next fclose(fp) 22.33 Numbers and Bytes The next functions to convert between Numbers and Bytes. • Int2Bytes() • Float2Bytes() • Double2Bytes() • Bytes2Int() • Bytes2Float() • Bytes2Double() Example: 22.33. Numbers and Bytes 153
198.
Ring Documentation, Release1.21.2 see "Test Int2Bytes() and Bytes2Int() - Value : 77" + nl r = Int2Bytes(77) see "Int Size : " + len(r) + nl see r + nl see Bytes2Int(r) + nl see "Test Float2Bytes() and Bytes2Float() - Value 77.12" + nl r = Float2Bytes(77.12) see "Float Size : " + len(r) + nl see r + nl see Bytes2Float(r) + nl see "Test Double2Bytes() and Bytes2Double() - Value 9999977.12345" + nl r = Double2Bytes(9999977.12345) see "Double Size : " + len(r) + nl see r + nl decimals(5) see Bytes2Double(r) + nl 22.33. Numbers and Bytes 154
199.
CHAPTER TWENTYTHREE SYSTEM FUNCTIONS In thischapter we are going to learn about the system functions • System() • SysGet() • SysSet() • SysUnset() • IsMSDOS() • IsWindows() • IsWindows64() • IsUnix() • IsMacOSX() • IsLinux() • IsFreeBSD() • IsAndroid() • GetArch() • Windowsnl() • Get Command Line Arguments • Get Active Source File Name • CurrentDir() • ExeFileName() • ChDir() • ExeFolder() • Version() • Shutdown() • NofProcessors() 155
200.
Ring Documentation, Release1.21.2 23.1 System() Function We can execute system commands using the system() function Syntax: System(cCommand) Example: System("myapp.exe") # Run myapp.exe System("ls") # print list of files 23.2 SysGet() Function We can get environment variables using the Get() function Syntax: SysGet(cVariable) Example: see sysget("path") # print system path information 23.3 SysSet() Function We can set environment variables using the SysSet() function SysSet(cVariable, cValue) ---> Returns 1 for success and return 0 for failure 23.4 SysUnset() Function We can delete an environment variables using the SysUnset() function SysUnset(cVariable) ---> Returns 1 for success and return 0 for failure 23.5 IsMSDOS() Function We can check if the operating system is MSDOS or not using the IsMSDOS() function Syntax: IsMSDOS() ---> Returns 1 if the operating system is MS-DOS, Returns 0 if it's not 23.1. System() Function 156
201.
Ring Documentation, Release1.21.2 23.6 IsWindows() Function We can check if the operating system is Windows or not using the IsWindows() function Syntax: IsWindows() ---> Returns 1 if the operating system is Windows, Returns 0 if it's not 23.7 IsWindows64() Function We can check if the operating system is Windows 64bit or not using the IsWindows64() function Syntax: IsWindows64() ---> Returns 1 if the operating system is Windows64, Returns 0 if it's not 23.8 IsUnix() Function We can check if the operating system is Unix or not using the IsUnix() function Syntax: IsUnix() ---> Returns 1 if the operating system is Unix, Returns 0 if it's not 23.9 IsMacOSX() Function We can check if the operating system is macOS or not using the IsMacOSX() function Syntax: IsMacOSX() ---> Returns 1 if the operating system is Mac OS X, Returns 0 if it's not 23.10 IsLinux() Function We can check if the operating system is Linux or not using the IsLinux() function Syntax: IsLinux() ---> Returns 1 if the operating system is Linux, Returns 0 if it's not 23.6. IsWindows() Function 157
202.
Ring Documentation, Release1.21.2 23.11 IsFreeBSD() Function We can check if the operating system is FreeBSD or not using the IsFreeBSD() function Syntax: IsFreeBSD() ---> Returns 1 if the operating system is FreeBSD, Returns 0 if it's not 23.12 IsAndroid() Function We can check if the operating system is Android or not using the IsAndroid() function Syntax: IsAndroid() ---> Returns 1 if the operating system is Android, Returns 0 if it's not 23.13 Example see "IsMSDOS() --> " + ismsdos() + nl see "IsWindows() --> " + iswindows() + nl see "IsWindows64() --> " + iswindows64() + nl see "IsUnix() --> " + isunix() + nl see "IsMacOSX() --> " + ismacosx() + nl see "IsLinux() --> " + islinux() + nl see "IsFreeBSD() --> " + isfreebsd() + nl see "IsAndroid() --> " + isandroid() + nl Output: IsMSDOS() --> 0 IsWindows() --> 1 IsWindows64() --> 0 IsUnix() --> 0 IsMacOSX() --> 0 IsLinux() --> 0 IsFreeBSD() --> 0 IsAndroid() --> 0 23.14 GetArch() Function We can detect the architecture of the Ring executable using the GetArch() function Syntax: GetArch() ---> cString (The name of the architecture of the Ring executable) Possible values are: 23.11. IsFreeBSD() Function 158
203.
Ring Documentation, Release1.21.2 x86 x64 arm64 arm unknown Example: switch getarch() on "x86" ? "x86 32bit architecture" on "x64" ? "x64 64bit architecture" on "arm64" ? "ARM64 64bit architecture" on "arm" ? "ARM 32bit architecture" other ? "Unknown architecture" off 23.15 Windowsnl() Function We can get the windows new line string using the Windowsnl() function. Syntax: WindowsNL() ---> Returns a string contains CR+LF = CHAR(13) + CHAR(10) Example: cStr = read("input.txt") if iswindows() cStr = substr(cStr,windowsnl(),nl) ok aList = str2list(cStr) # to do - list items processing using "for in" cStr = list2str(aList) if iswindows() cStr = substr(cStr,nl,windowsnl()) ok write("output.txt",cStr) 23.15. Windowsnl() Function 159
204.
Ring Documentation, Release1.21.2 23.16 Get Command Line Arguments We can get the command line arguments passed to the ring script using the sysargv variable. The sysargv variable is a list contains the command line parameters. Example see copy("=",30) + nl see "Command Line Parameters" + nl see "Size : " + len(sysargv) + nl see sysargv see copy("=",30) + nl if len(sysargv) < 4 return ok nStart = sysargv[3] nEnd = sysargv[4] for x = nStart to nEnd see x + nl next Output b:mahmoudappsring>ring testssyspara.ring 1 10 ============================== Command Line Parameters Size : 4 ring testssyspara.ring 1 10 ============================== 1 2 3 4 5 6 7 8 9 10 23.17 Get Active Source File Name We can get the active source file name (*.ring) using the filename() function Syntax: filename() ---> String contains the active source file name. Example: 23.16. Get Command Line Arguments 160
205.
Ring Documentation, Release1.21.2 see "Active Source File Name : " + filename() + nl Output: Active Source File Name : testsfilename.ring Example: if sysargv[2] = filename() see "I'm the main program file!" + nl # we can run tests here! else see "I'm a sub file in a program" + nl ok 23.18 PrevFileName() Function Using the PrevFileName() function we can get the previous active source file name. The previous file would be the file of the caller function, Or the file of the function that we called before calling PrevFileName(). Syntax: prevfilename() ---> String contains the previous source file name. Example: The next function in stdlib.ring uses the PrevFileName() to know if the file of the caller function is the main source file of the program or not. Func IsMainSourceFile if PrevFileName() = sysargv[2] return true ok return false 23.19 CurrentDir() Function Return the path of the current directory Syntax: CurrentDir() ---> String contains the path of the current directory 23.18. PrevFileName() Function 161
206.
Ring Documentation, Release1.21.2 23.20 ExeFileName() Function Return the Ring executable file name Syntax: exefilename() ---> String contains the Ring executable file name 23.21 ChDir() Function Change the current directory Syntax: ChDir(cNewPath) 23.22 ExeFolder() Function Return the Ring executable file path Syntax: exefolder() ---> String contains the Ring executable path 23.23 Version() Function Return the Ring version Syntax: version([lPatch]) ---> String contains the Ring version Example: ? version() ? version(True) Output: 1.21 1.21.2 23.20. ExeFileName() Function 162
207.
Ring Documentation, Release1.21.2 23.24 Shutdown() Function Close the application Syntax: shutdown(nStatus) ---> Close the application 23.25 NofProcessors() Function We can detect the number of processors using the NofProcessors() Function Syntax: NofProcessors() ---> nProcessors Example: ? NofProcessors() 23.24. Shutdown() Function 163
208.
CHAPTER TWENTYFOUR EVAL() AND DEBUGGING Inthis chapter we are going to learn about • Error Handling using Try/Catch/Done • Eval() function • Raise() function • Assert() function 24.1 Try/Catch/Done Syntax: Try Statements... Catch Statements... Done The statements in the Try block will be executed, if any error happens then the statements in the catch block will be executed. Inside the catch block we can use the variable cCatchError to get the error message Example: Try see 5/0 Catch see "Catch!" + nl + cCatchError Done Output: Catch! Error (R1) : Can't divide by zero ! 164
209.
Ring Documentation, Release1.21.2 24.2 Eval() Function We can execute code during the runtime from string using the Eval() function Syntax: Eval(cCode) Example: Eval("nOutput = 5+2*5 " ) See "5+2*5 = " + nOutput + nl Eval("for x = 1 to 10 see x + nl next") Eval("func test see 'message from test!' ") test() Output: 5+2*5 = 15 1 2 3 4 5 6 7 8 9 10 message from test! We can use the Return command to return a value Example: see Eval("return 5*5") Output: 25 24.3 Raise() Function We can raise an exception using the Raise() function Syntax: Raise(cErrorMessage) The function will display the error message then end the execution of the program. We can use Try/Catch/Done to avoid exceptions generated by raise() function. Example: 24.2. Eval() Function 165
210.
Ring Documentation, Release1.21.2 nMode = 10 if nMode < 0 or nMode > 5 raise("Error : nMode not in the range 1:4") ok Output: Line 4 Error : nMode not in the range 1:4 In raise in file testsraise.ring Example: try testmode(6) catch see "avoid raise!" done testmode(-1) func testmode nMode if nMode < 0 or nMode > 5 raise("Error : nMode not in the range 1:4") ok Output: avoid raise! Line 12 Error : nMode not in the range 1:4 In raise In function testmode() in file testsraise2.ring called from line 7 in file testsraise2.ring 24.4 Assert() Function We can use the Assert() function to test conditions before executing the code If the test fail the program will be terminated with an error message contains the assert condition. Syntax: Assert( condition ) Example: x = 10 assert( x = 10) assert( x = 100 ) Output: 24.4. Assert() Function 166
211.
Ring Documentation, Release1.21.2 Line 3 Assertion Failed! In assert in file testsassert.ring 24.4. Assert() Function 167
212.
CHAPTER TWENTYFIVE DEMO PROGRAMS In thischapter we will see simple demo programs • Language Shell • Main Menu 25.1 Language Shell We can create simple interactive programming environment using the next program while true see nl + "code:> " give cCode try eval(cCode) catch see cCatchError done end Output: code:> see "hello world" hello world code:> for x = 1 to 10 see x + nl next 1 2 3 4 5 6 7 8 9 10 code:> func test see "Hello from test" + nl code:> test() Hello from test (continues on next page) 168
213.
Ring Documentation, Release1.21.2 (continued from previous page) code:> bye 25.2 Main Menu Example: # Demo Program while true see " Main Menu =========== [1] Say Hello [2] Sum two numbers [3] Stars [4] Fact [5] Exit " give nMenu see nl # we can use Switch-ON-Other-OFF instead of IF-BUT-ELSE-OK Switch nMenu On 1 sayhello() On 2 Sum() On 3 Stars() On 4 see "Enter Number : " give x see "Output : " Try see Fact(number(x)) Catch see "Error in parameters!" + nl Done On "5" return Other see "bad option" + nl Off end func sayhello see "Enter your name ? " give fname see "Hello " + fname + nl func sum (continues on next page) 25.2. Main Menu 169
214.
Ring Documentation, Release1.21.2 (continued from previous page) see "number 1 : " give num1 see "number 2 : " give num2 see "Sum : " see 0 + num1 + num2 func stars for x = 1 to 10 see space(8) for y = 1 to x see "*" next see nl next func fact x if x = 0 return 1 else return x * fact(x-1) ok func space x y = "" for t=1 to x y += " " next return y Output: Main Menu =========== [1] Say Hello [2] Sum two numbers [3] Stars [4] Fact [5] Exit 1 Enter your name ? Mahmoud Fayed Hello Mahmoud Fayed Main Menu =========== [1] Say Hello [2] Sum two numbers [3] Stars [4] Fact [5] Exit 2 number 1 : 3 number 2 : 4 Sum : 7 Main Menu =========== [1] Say Hello [2] Sum two numbers [3] Stars [4] Fact [5] Exit 3 (continues on next page) 25.2. Main Menu 170
215.
Ring Documentation, Release1.21.2 (continued from previous page) * ** *** **** ***** ****** ******* ******** ********* ********** Main Menu =========== [1] Say Hello [2] Sum two numbers [3] Stars [4] Fact [5] Exit 4 Enter Number : 5 Output : 120 Main Menu =========== [1] Say Hello [2] Sum two numbers [3] Stars [4] Fact [5] Exit 5 25.2. Main Menu 171
216.
CHAPTER TWENTYSIX ODBC FUNCTIONS This chaptercontains the ODBC functions provided by the Ring programming language. • odbc_init() • odbc_drivers() • odbc_datasources() • odbc_close() • odbc_connect() • odbc_disconnect() • odbc_execute() • odbc_colcount() • odbc_fetch() • odbc_getdata() • odbc_tables() • odbc_columns() • odbc_autocommit() • odbc_commit() • odbc_rollback() Before using the next function load the odbclib.ring library load "odbclib.ring" # Use ODBC functions 26.1 odbc_init() Function We can create ODBC Handle using the odbc_init() function Syntax: odbc_init() ---> ODBC Handle 172
217.
Ring Documentation, Release1.21.2 26.2 odbc_drivers() Function We can get a list of ODBC drivers using the odbc_drivers() function Syntax: odbc_drivers(ODBC Handle) ---> List of Drivers 26.3 odbc_datasources() Function We can get a list of ODBC data sources using the odbc_datasources() function Syntax: odbc_datasources(ODBC Handle) ---> List of Data sources 26.4 odbc_close() Function After the end of using ODBC functions we can free resources using ODBC_Close() function Syntax: odbc_close(ODBC Handle) 26.5 Print List of ODBC Drivers The next example print a list of ODBC drivers. See "ODBC test 1" + nl oODBC = odbc_init() See "Drivers " + nl see odbc_drivers(oODBC) odbc_close(oODBC) Output: ODBC test 1 Drivers Microsoft Access-Treiber (*.mdb) - SQLLevel=0 Driver do Microsoft Paradox (*.db ) - SQLLevel=0 Driver do Microsoft Excel(*.xls) - SQLLevel=0 Microsoft Text Driver (*.txt; *.csv) - SQLLevel=0 Driver da Microsoft para arquivos texto (*.txt; *.csv) - SQLLevel=0 Microsoft dBase-Treiber (*.dbf) - SQLLevel=0 SQL Server - CPTimeout=60 Microsoft Excel Driver (*.xls) - SQLLevel=0 Driver do Microsoft dBase (*.dbf) - SQLLevel=0 Microsoft Paradox-Treiber (*.db ) - SQLLevel=0 (continues on next page) 26.2. odbc_drivers() Function 173
218.
Ring Documentation, Release1.21.2 (continued from previous page) Microsoft ODBC for Oracle - CPTimeout=120 Microsoft Text-Treiber (*.txt; *.csv) - SQLLevel=0 Microsoft Excel-Treiber (*.xls) - SQLLevel=0 Microsoft Access Driver (*.mdb) - SQLLevel=0 Driver do Microsoft Access (*.mdb) - SQLLevel=0 Microsoft Paradox Driver (*.db ) - SQLLevel=0 Microsoft dBase Driver (*.dbf) - SQLLevel=0 Microsoft Access Driver (*.mdb, *.accdb) - UsageCount=3 Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb) - UsageCount=3 Microsoft Access Text Driver (*.txt, *.csv) - UsageCount=3 SQL Server Native Client 10.0 - UsageCount=1 SQL Server Native Client 11.0 - UsageCount=1 Microsoft Access dBASE Driver (*.dbf, *.ndx, *.mdx) - UsageCount=3 Microsoft Access Paradox Driver (*.db) - UsageCount=3 MySQL ODBC 5.3 ANSI Driver - UsageCount=1 MySQL ODBC 5.3 Unicode Driver - UsageCount=1 ODBC Driver 11 for SQL Server - UsageCount=1 Lianja ODBC Driver - CPTimeout=60 Microsoft Visual FoxPro Driver - UsageCount=1 Microsoft Visual FoxPro-Treiber - UsageCount=1 Driver para o Microsoft Visual FoxPro - UsageCount=1 Microsoft FoxPro VFP Driver (*.dbf) - UsageCount=1 26.6 Print List of ODBC Data Sources The next example print a list of ODBC data sources. See "ODBC test 2" + nl pODBC = odbc_init() See "Data Sources " + nl see odbc_datasources(pODBC) odbc_close(pODBC) Output: ODBC test 2 Data Sources Excel Files - Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb) MS Access Database - Microsoft Access Driver (*.mdb, *.accdb) Customer - Microsoft Access Driver (*.mdb) IdCardData - Microsoft Access Driver (*.mdb) MyProjectData2 - Microsoft Access Driver (*.mdb) MyData - Microsoft Access Driver (*.mdb) MonprojetData - Microsoft Access Driver (*.mdb) dBASE Files - Microsoft Access dBASE Driver (*.dbf, *.ndx, *.mdx) myvfpdata - Microsoft Visual FoxPro Driver FACTORYDATA - Microsoft Access Driver (*.mdb) TRAININGSYSDATA - Microsoft Access Driver (*.mdb) RVCSYSDATASQLDB - SQL Server Native Client 11.0 PWCTRVCDATA - Microsoft Access Driver (*.mdb) (continues on next page) 26.6. Print List of ODBC Data Sources 174
219.
Ring Documentation, Release1.21.2 (continued from previous page) MyCompany - Microsoft Access Driver (*.mdb) HCS - Microsoft Access Driver (*.mdb) HCS2 - Microsoft Access Driver (*.mdb, *.accdb) MyProjectData - Microsoft Access Driver (*.mdb) Xtreme Sample Database 2008 - Microsoft Access Driver (*.mdb) Lianja_Southwind - Lianja ODBC Driver Visual FoxPro Database - Microsoft Visual FoxPro Driver Visual FoxPro Tables - Microsoft Visual FoxPro Driver 26.7 odbc_connect() Function We can connect to the database using the odbc_connect() function. Syntax: odbc_connect(ODBC Handle, cConnectionString) 26.8 odbc_disconnect() Function We can close the connection to the database using the odbc_disconnect() function. Syntax: odbc_disconnect(ODBC Handle) 26.9 Open and Close Connection The next example connect to the database then close the connection See "ODBC test 3" + nl pODBC = odbc_init() See "Connect to database" + nl see odbc_connect(pODBC,"DBQ=test.mdb;Driver={Microsoft Access Driver (*.mdb)}") + nl See "disconnect" + nl odbc_disconnect(pODBC) See "Close database..." + nl odbc_close(pODBC) Output: ODBC test 3 Connect to database 1 disconnect Close database... 26.7. odbc_connect() Function 175
220.
Ring Documentation, Release1.21.2 26.10 odbc_execute() Function We can execute SQL Statements on the database using the odbc_execute() function. Syntax: odbc_execute(ODBC Handle, cSQLStatement) 26.11 odbc_colcount() Function We can get columns count in the query result using the odbc_colcount() function. Syntax: odbc_colcount(ODBC Handle) ---> Columns Count as Number 26.12 odbc_fetch() Function We can fetch a row from the query result using the odbc_fetch() function. Syntax: odbc_fetch(ODBC Handle) 26.13 odbc_getdata() Function We can get column value from the fetched row using the odbc_getdata() function. Syntax: odbc_getdata(ODBC Handle, nColumnNumber) ---> Column Value 26.14 Execute Query and Print Result The next example execute query then print the query result. See "ODBC test 4" + nl pODBC = odbc_init() See "Connect to database" + nl see odbc_connect(pODBC,"DBQ=test.mdb;Driver={Microsoft Access Driver (*.mdb)}") + nl See "Select data" + nl see odbc_execute(pODBC,"select * from person") + nl nMax = odbc_colcount(pODBC) See "Columns Count : " + nMax + nl while odbc_fetch(pODBC) See "Row data:" + nl for x = 1 to nMax (continues on next page) 26.10. odbc_execute() Function 176
221.
Ring Documentation, Release1.21.2 (continued from previous page) see odbc_getdata(pODBC,x) + " - " next end See "Close database..." + nl odbc_disconnect(pODBC) odbc_close(pODBC) 26.15 odbc_tables() Function We can get a list of tables inside the database using the odbc_tables() function. We can access the result of this function as we get any query result. Syntax: odbc_tables(ODBC Handle) Example: See "ODBC test - Get Database Tables" + nl pODBC = odbc_init() See "Connect to database" + nl odbc_connect(pODBC,"DBQ=test.mdb;Driver={Microsoft Access Driver (*.mdb)}") + nl See "Select data" + nl odbc_tables(pODBC) + nl nMax = odbc_colcount(pODBC) See "Columns Count : " + nMax + nl while odbc_fetch(pODBC) for x = 1 to nMax see odbc_getdata(pODBC,x) if x != nMax see " - " ok next See nl end See "Close database..." odbc_disconnect(pODBC) odbc_close(pODBC) Output: ODBC test - Get Database Tables Connect to database Select data Columns Count : 5 .test - NULL - Customer - TABLE - NULL .test - NULL - employee - TABLE - NULL .test - NULL - person - TABLE - NULL .test - NULL - tel - TABLE - NULL Close database... 26.15. odbc_tables() Function 177
222.
Ring Documentation, Release1.21.2 26.16 odbc_columns() Function We can get a list of columns inside the table using the odbc_columns() function. Syntax: odbc_columns(ODBC Handle, cTableName) Example: See "ODBC test - Get Table Columns" + nl pODBC = odbc_init() See "Connect to database" + nl odbc_connect(pODBC,"DBQ=test.mdb;Driver={Microsoft Access Driver (*.mdb)}") + nl See "Get Columns inside the Person Table" + nl odbc_columns(pODBC,"person") + nl while odbc_fetch(pODBC) see odbc_getdata(pODBC,4) + nl end See "Close database..." + nl odbc_disconnect(pODBC) odbc_close(pODBC) Output: ODBC test - Get Table Columns Connect to database Get Columns inside the Person Table FIRST LAST STREET CITY STATE ZIP HIREDATE MARRIED AGE SALARY NOTES Close database... 26.17 odbc_autocommit() Function We can enable or disable the auto commit feature using the odbc_autocommit() function. Syntax: odbc_autocommit(ODBC Handle, lStatus) # lStatus can be True or False 26.16. odbc_columns() Function 178
223.
Ring Documentation, Release1.21.2 26.18 odbc_commit() Function We can commit updates to the database using the odbc_commit() function. Syntax: odbc_commit(ODBC Handle) 26.19 odbc_rollback() Function We can rollback updates to the database using the odbc_rollback() function. Syntax: odbc_rollback(ODBC Handle) 26.20 Transactions and Using Commit and Rollback Example: See "ODBC Test - Transactions and using Commit and Rollback" + nl pODBC = odbc_init() See "Connect to database" + nl see odbc_connect(pODBC,"DBQ=test.mdb;Driver={Microsoft Access Driver (*.mdb)}") + nl see "insert data..." + nl odbc_autocommit(pODBC,0) for x = 1 to 10000 odbc_execute(pODBC,"insert into tel values (" + x + ",'mahmoud')") next for x = 10001 to 15000 odbc_execute(pODBC,"insert into tel values (" + x + ",'samir')") next odbc_commit(pODBC) for x = 15001 to 20000 odbc_execute(pODBC,"insert into tel values (" + x + ",'fayed')") next ODBC_ROLLBACK(pODBC) odbc_execute(pODBC,"insert into tel values (" + x + ",'fayed')") odbc_commit(pODBC) See "Close database..." + nl odbc_disconnect(pODBC) odbc_close(pODBC) Output: ODBC Test - Transactions and using Commit and Rollback Connect to database (continues on next page) 26.18. odbc_commit() Function 179
224.
Ring Documentation, Release1.21.2 (continued from previous page) 1 insert data... Close database... 26.21 Save and Restore images The next example save an image inside the database See "ODBC test - Save image in the database" + nl pODBC = odbc_init() See "Connect to database" + nl see odbc_connect(pODBC,"DBQ=test.mdb;Driver={Microsoft Access Driver (*.mdb)}") + nl see "Read Image File..." + nl cFile = str2hex(read("testsmahmoud.jpg")) see "size " + len(CFile)+nl see "Save image in the database..." + nl stmt = "insert into tel values (20000,'mahmoud','" + cFile + "');" odbc_execute(pODBC,stmt) See "Close database..." + nl odbc_disconnect(pODBC) odbc_close(pODBC) The next example restore the image from the database See "ODBC Test - Restore image from the database" + nl pODBC = odbc_init() See "Connect to database" + nl see odbc_connect(pODBC,"DBQ=test.mdb;Driver={Microsoft Access Driver (*.mdb)}") + nl See "Select data" + nl see odbc_execute(pODBC,"select * from tel where id = 20000") + nl nMax = odbc_colcount(pODBC) See "Columns Count : " + nMax + nl if odbc_fetch(pODBC) See "Write image file" + nl write("testsgreat.jpg",hex2str( odbc_getdata(pODBC,3) ) ) ok See "Close database..." + nl odbc_disconnect(pODBC) odbc_close(pODBC) 26.21. Save and Restore images 180
225.
CHAPTER TWENTYSEVEN MYSQL FUNCTIONS In thischapter we are going to learn about the MySQL functions provided by the Ring programming language. • MySQL_Info() • MySQL_Init() • MySQL_Error() • MySQL_Connect() • MySQL_Close() • MySQL_Query() • MySQL_Insert_ID() • MySQL_Result() • MySQL_Next_Result() • MySQL_Columns() • MySQL_Result2() • MySQL_Escape_String() • MySQL_AutoCommit() • MySQL_Commit() • MySQL_Rollback() Before using the next function load the mysqllib.ring library load "mysqllib.ring" # Use MySQL functions 27.1 MySQL_Info() Function We can get the MySQL Client version using the MySQL_Info() function. Syntax: MySQL_Info() ---> string contains the MySQL Client version Example: 181
226.
Ring Documentation, Release1.21.2 see "MySQL Client Version : " + mysql_info() Output: MySQL Client Version : 6.1.5 27.2 MySQL_Init() Function We can start using MySQL Client through the MySQL_Init() function. Syntax: MySQL_Init() ---> MySQL Handle 27.3 MySQL_Error() Function We can get the error message from the MySQL Client using the MySQL_Error() function. Syntax: MySQL_Error(MySQL Handle) ---> Error message as string 27.4 MySQL_Connect() Function We can connect to the MySQL database server using the MySQL_Connect() function. Syntax: MySQL_Connect(MySQL Handle, cServer, cUserName, cPassword) ---> lStatus 27.5 MySQL_Close() Function We can close the connection to the MySQL database using the MySQL_Close() function Syntax: MySQL_Close(MySQL Handle) 27.2. MySQL_Init() Function 182
227.
Ring Documentation, Release1.21.2 27.6 MySQL_Query() Function We can execute SQL queries using the MySQL_Query() function Syntax: MySQL_Query(MySQL Handle, cSQLQuery) 27.7 Create Database The next example connect to MySQL Server then create new database. See "MySQL Test - Create Database" + nl con = mysql_init() See "Connect" + nl if mysql_connect(con,"localhost","root","root") = 0 see "Can't connect" + nl see "Error : " + mysql_error(con) + nl mysql_close(con) bye ok See "Create Database..." + nl mysql_query(con,"CREATE DATABASE mahdb") See "Close Connection" + nl mysql_close(con) Output: MySQL Test - Create Database Connect Create Database... Close Connection 27.8 Create Table and Insert Data The next example create new table and insert records func main see "Create Table and Insert Records" + nl con = mysql_init() see "Connect" + nl if mysql_connect(con, "localhost", "root", "root","mahdb") = 0 system_error(con) ok (continues on next page) 27.6. MySQL_Query() Function 183
228.
Ring Documentation, Release1.21.2 (continued from previous page) see "Drop table" + nl if mysql_query(con, "DROP TABLE IF EXISTS Employee") system_error(con) ok see "Create table" + nl if mysql_query(con, "CREATE TABLE Employee(Id INT, Name TEXT, Salary INT)") system_error(con) ok see "Insert data" + nl if mysql_query(con, "INSERT INTO Employee VALUES(1,'Mahmoud',15000)") system_error(con) ok if mysql_query(con, "INSERT INTO Employee VALUES(2,'Samir',16000)") system_error(con) ok if mysql_query(con, "INSERT INTO Employee VALUES(3,'Fayed',17000)") system_error(con) ok see "Close connection" + nl mysql_close(con) func system_error con see mysql_error(con) mysql_close(con) bye Output: Create Table and Insert Records Connect Drop table Create table Insert data Close connection 27.9 MySQL_Insert_ID() Function We can get the inserted row id using the MySQL_Insert_ID() function Syntax: MySQL_Insert_ID() ---> Inserted row id as number Example: con = mysql_init() see "connect to database" + nl mysql_connect(con,"localhost","root","root","mahdb") see "drop table" + nl mysql_query(con, "DROP TABLE IF EXISTS Customers") see "create table" + nl mysql_query(con, "CREATE TABLE Customers(Id INT PRIMARY KEY AUTO_INCREMENT, Name TEXT)") see "insert record" + nl mysql_query(con, "INSERT INTO Customers(Name) VALUES('Mahmoud')") (continues on next page) 27.9. MySQL_Insert_ID() Function 184
229.
Ring Documentation, Release1.21.2 (continued from previous page) see "insert record" + nl mysql_query(con, "INSERT INTO Customers(Name) VALUES('Samir')") see "insert record" + nl mysql_query(con, "INSERT INTO Customers(Name) VALUES('Fayed')") see "insert record" + nl mysql_query(con, "INSERT INTO Customers(Name) VALUES('Test 2015')") see "inserted row id : " + mysql_insert_id(con) + nl see "close database" + nl mysql_close(con) Output: connect to database drop table create table insert record insert record insert record insert record inserted row id : 4 close database 27.10 MySQL_Result() Function We can get the query result (data without column names) using the MySQL_Result() function. Syntax: MySQL_Result(MySQL Handle) ---> List contains the query result 27.11 MySQL_Next_Result() Function We can move to the next query result using the MySQL_Next_Result() function. We use this function when we have multiple SQL statements in the same query. Syntax: MySQL_Next_Result(MySQL Handle) 27.10. MySQL_Result() Function 185
230.
Ring Documentation, Release1.21.2 27.12 Print Query Result The next example execute a query on the database then print the result. con = mysql_init() see "Connect to database" + nl mysql_connect(con, "localhost", "root", "root","mahdb") see "Execute Query" + nl mysql_query(con, "SELECT Name FROM Employee WHERE Id=1;"+ "SELECT Name FROM Employee WHERE Id=3") see "Print Result" + nl see mysql_result(con) mysql_next_result(con) see mysql_result(con) see "close database" + nl mysql_close(con) Output: Connect to database Execute Query Print Result Mahmoud Fayed close database 27.13 MySQL_Columns() Function We can get a list of columns names using the MySQL_Columns() function. Syntax: MySQL_Columns(MySQL Handle) ---> List contains columns information Example: con = mysql_init() see "Connect to database" + nl mysql_connect(con, "localhost", "root", "root","mahdb") see "Execute Query" + nl mysql_query(con, "SELECT * FROM Employee") see "Result" + nl see mysql_columns(con) see "Close database" + nl mysql_close(con) Output: Connect to database Execute Query Result Id (continues on next page) 27.12. Print Query Result 186
231.
Ring Documentation, Release1.21.2 (continued from previous page) 11 3 32768 Name 65535 252 16 Salary 11 3 32768 Close database 27.14 MySQL_Result2() Function Instead of using MySQL_Result() to get the result data without columns names, we can use the MySQL_Result2() to get all of the column names then the query result in one list. Syntax: MySQL_Result2(MySQL Handle) ---> List (query result starts with columns names) Example: con = mysql_init() see "Connect to database" + nl mysql_connect(con, "localhost", "root", "root","mahdb") see "Execute Query" + nl mysql_query(con, "SELECT * FROM Employee") see "Print Result" + nl see mysql_result2(con) see "Close database" + nl mysql_close(con) Output: Connect to database Execute Query Print Result Id Name Salary 1 Mahmoud 15000 2 Samir 16000 3 Fayed (continues on next page) 27.14. MySQL_Result2() Function 187
232.
Ring Documentation, Release1.21.2 (continued from previous page) 17000 Close database 27.15 MySQL_Escape_String() Function We can store binary data and special characters in the database after processing using MySQL_Escape_String() function Syntax: MySQL_Escape_String(MySQL Handle, cString) ---> String after processing 27.16 Save Image inside the database Example: See "Read file" + nl cFile = read("testsmahmoud.jpg") con = mysql_init() See "Connect to database..." + nl mysql_connect(con, "localhost", "root", "root","mahdb") See "Escape string..." + nl cFile = mysql_escape_string(con,cFile) stmt = "INSERT INTO photo(id, data) VALUES(1, '" + cFile + "')" See "Insert data..." + nl mysql_query(con,stmt) See "Close database..." + nl mysql_close(con) Output: Read file Connect to database... Escape string... Insert data... Close database... 27.17 Restore Image From The Database Example: con = mysql_init() See "Connect to database..." + nl mysql_connect(con, "localhost", "root", "root","mahdb") See "Read data from database..." + nl mysql_query(con,"SELECT data FROM photo WHERE id=1") See "Write new file" + nl result = mysql_result(con) (continues on next page) 27.15. MySQL_Escape_String() Function 188
233.
Ring Documentation, Release1.21.2 (continued from previous page) write("testsmahmoud2.jpg",result[1][1]) See "Close database..." + nl mysql_close(con) Output: Connect to database... Read data from database... Write new file Close database... 27.18 MySQL_AutoCommit() Function We can enable or disable the auto commit feature using the MySQL_AutoCommit() function. Syntax: MySQL_AutoCommit(MySQL Handle, lStatus) # lstatus can be True/False 27.19 MySQL_Commit() Function We can commit updates to the database using the MySQL_Commit() function. Syntax: MySQL_Commit(MySQL Handle) 27.20 MySQL_Rollback() Function We can rollback updates to the database using the MySQL_Rollback() function. Syntax: MySQL_Rollback(MySQL Handle) 27.21 Transaction Example The next example presents the usage of MySQL_Autocommit(), MySQL_Commit() & MySQL_RollBack() functions. Example: func main con = mysql_init() see "Connect" + nl (continues on next page) 27.18. MySQL_AutoCommit() Function 189
234.
Ring Documentation, Release1.21.2 (continued from previous page) if mysql_connect(con, "localhost", "root", "root","mahdb") = 0 system_error(con) ok see "Drop table" + nl if mysql_query(con, "DROP TABLE IF EXISTS Employee2") system_error(con) ok see "Create table" + nl if mysql_query(con, "CREATE TABLE Employee2(Id INT, Name TEXT, Salary INT)") system_error(con) ok see "Insert data" + nl if mysql_query(con, "INSERT INTO Employee2 VALUES(1,'Mahmoud',15000)") system_error(con) ok if mysql_query(con, "INSERT INTO Employee2 VALUES(2,'Samir',16000)") system_error(con) ok if mysql_query(con, "INSERT INTO Employee2 VALUES(3,'Fayed',17000)") system_error(con) ok mysql_autocommit(con,False) mysql_query(con, "INSERT INTO Employee2 VALUES(4,'Ahmed',5000)") mysql_query(con, "INSERT INTO Employee2 VALUES(5,'Ibrahim',50000)") mysql_query(con, "INSERT INTO Employee2 VALUES(6,'Mohammed',50000)") See "Save transaction (y/n) " give nChoice if upper(nChoice) = "Y" mysql_commit(con) else mysql_rollback(con) ok see "Close connection" + nl mysql_close(con) func system_error con see mysql_error(con) mysql_close(con) bye Output: Connect Drop table Create table Insert data Save transaction (y/n) y Close connection 27.21. Transaction Example 190
235.
CHAPTER TWENTYEIGHT SQLITE FUNCTIONS In thischapter we will learn about using the SQLite database in the Ring programming language. Before using the next function load the sqlitelib.ring library load "sqlitelib.ring" # Use SQLite functions 28.1 sqlite_init() function Syntax: sqlite_init() ---> SQLite Object 28.2 sqlite_open() function Syntax: sqlite_open(SQLite Object,cFileName) 28.3 sqlite_execute() function Syntax: sqlite_execute(SQLite Object,cSQLStatement) 28.4 sqlite_close() function Syntax: sqlite_close(SQLite Object) 191
236.
Ring Documentation, Release1.21.2 28.5 Example The next code create a SQLite database, add new records then display the data. load "sqlitelib.ring" oSQLite = sqlite_init() sqlite_open(oSQLite,"mytest.db") sql = " CREATE TABLE COMPANY ( ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, AGE INT NOT NULL, ADDRESS CHAR(50), SALARY REAL ); " sqlite_execute(oSQLite,sql) sql = " INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Mahmoud' , 29, 'Jeddah', 20000.00 ), (2, 'Ahmed' , 27, 'Jeddah', 15000.00 ), (3, 'Mohammed', 31, 'Egypt' , 20000.00 ), (4, 'Ibrahim' , 24, 'Egypt ', 65000.00 ); " sqlite_execute(oSQLite,sql) aResult = sqlite_execute(oSQLite,"select * from COMPANY") for x in aResult for t in x ? t[2] + nl next next ? copy("*",50) for x in aResult ? x[:name] next sqlite_close(oSQLite) Output: 1 Mahmoud 29 Jeddah 20000.0 2 Ahmed 27 Jeddah (continues on next page) 28.5. Example 192
237.
Ring Documentation, Release1.21.2 (continued from previous page) 15000.0 3 Mohammed 31 Egypt 20000.0 4 Ibrahim 24 Egypt 65000.0 ************************************************** Mahmoud Ahmed Mohammed Ibrahim 28.5. Example 193
238.
CHAPTER TWENTYNINE POSTGRESQL FUNCTIONS In thischapter we will learn about using the PostgreSQL database in the Ring programming language. 29.1 Loading the library Before using the next function load the postgresqllib.ring library load "postgresqllib.ring" # Use PostgreSQL functions 29.2 Examples Example (1): load "postgresqllib.ring" conninfo = "user=postgres password=sa dbname = postgres" exit_nicely = func conn { PQfinish(conn) shutdown(1) } conn = PQconnectdb(conninfo) if (PQstatus(conn) != CONNECTION_OK) fputs(stderr, "Connection to database failed: "+PQerrorMessage(conn)) call exit_nicely(conn) ok res = PQexec(conn, "select * from pg_database") if PQresultStatus(res) != PGRES_TUPLES_OK fputs(stderr, "Select failed: " + PQerrorMessage(conn)) PQclear(res) exit_nicely(conn) ok nFields = PQnfields(res) (continues on next page) 194
239.
Ring Documentation, Release1.21.2 (continued from previous page) for i = 1 to nFields ? PQfname(res, i-1) next ? copy("*",60) for i = 1 to PQntuples(res) for j=1 to nFields see PQgetvalue(res, i-1, j-1) + " " next see nl next PQclear(res) PQfinish(conn) Output: datname datdba encoding datcollate datctype datistemplate datallowconn datconnlimit datlastsysoid datfrozenxid datminmxid dattablespace datacl ************************************************************ postgres 10 6 English_United States.1252 English_United States.1252 f t -1 12937 549 1 1663 template1 10 6 English_United States.1252 English_United States.1252 t t -1 12937 549 1 1663 {=c/postgres,postgres=CTc/postgres} template0 10 6 English_United States.1252 English_United States.1252 t f -1 12937 549 1 1663 {=c/postgres,postgres=CTc/postgres} mahdb 10 6 English_United States.1252 English_United States.1252 f t -1 12937 549 1 1663 Example(2): load "postgresqllib.ring" conninfo = "user=postgres password=sa" exit_nicely = func conn { PQfinish(conn) shutdown(1) } (continues on next page) 29.2. Examples 195
240.
Ring Documentation, Release1.21.2 (continued from previous page) conn = PQconnectdb(conninfo) if (PQstatus(conn) != CONNECTION_OK) fputs(stderr, "Connection to database failed: "+PQerrorMessage(conn)) call exit_nicely(conn) ok res = PQexec(conn, "DROP DATABASE IF EXISTS mahdb;") if PQresultStatus(res) != PGRES_TUPLES_OK fputs(stderr, "Remove failed: " + PQerrorMessage(conn)) ok PQclear(res) res = PQexec(conn, "CREATE DATABASE mahdb;") if PQresultStatus(res) != PGRES_TUPLES_OK fputs(stderr, "Create database failed: " + PQerrorMessage(conn)) ok PQclear(res) res = PQexec(conn, " CREATE TABLE COMPANY ( ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, AGE INT NOT NULL, ADDRESS CHAR(50), SALARY REAL ); ") if PQresultStatus(res) != PGRES_TUPLES_OK fputs(stderr, "Create Table failed: " + PQerrorMessage(conn)) ok PQclear(res) res = PQexec(conn, " INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Mahmoud' , 31, 'Jeddah', 10.00 ), (2, 'Ahmed' , 27, 'Jeddah', 20.00 ), (3, 'Mohammed', 33, 'Egypt' , 30.00 ), (4, 'Ibrahim' , 24, 'Egypt ', 40.00 ); ") if PQresultStatus(res) != PGRES_TUPLES_OK fputs(stderr, "Insert Table failed: " + PQerrorMessage(conn)) ok PQclear(res) res = PQexec(conn, " select * from COMPANY ") if PQresultStatus(res) != PGRES_TUPLES_OK fputs(stderr, "Select failed: " + PQerrorMessage(conn)) (continues on next page) 29.2. Examples 196
241.
Ring Documentation, Release1.21.2 (continued from previous page) PQclear(res) call exit_nicely(conn) ok nFields = PQnfields(res) for i = 1 to nFields ? PQfname(res, i-1) next ? copy("*",60) for i = 1 to PQntuples(res) for j=1 to nFields see PQgetvalue(res, i-1, j-1) + " " next see nl next PQclear(res) PQfinish(conn) Output: id name age address salary ************************************************************ 1 Mahmoud 31 Jeddah 10 2 Ahmed 27 Jeddah 20 3 Mohammed 31 Egypt 30 4 Ibrahim 24 Egypt 40 29.3 RingPostgreSQL Constants The next constants are define by the RingPostgreSQL Library CONNECTION_STARTED CONNECTION_MADE CONNECTION_AWAITING_RESPONSE CONNECTION_AUTH_OK CONNECTION_SSL_STARTUP CONNECTION_SETENV CONNECTION_OK PQPING_OK PQPING_REJECT (continues on next page) 29.3. RingPostgreSQL Constants 197
Ring Documentation, Release1.21.2 (continued from previous page) int PQftablecol(const PGresult *res,int column_number) int PQfformat(const PGresult *res,int column_number) Oid PQftype(const PGresult *res,int column_number) int PQfmod(const PGresult *res,int column_number) int PQfsize(const PGresult *res,int column_number) int PQbinaryTuples(const PGresult *res) char *PQgetvalue(const PGresult *res,int row_number,int column_number) int PQgetisnull(const PGresult *res,int row_number,int column_number) int PQgetlength(const PGresult *res,int row_number,int column_number) int PQnparams(const PGresult *res) Oid PQparamtype(const PGresult *res, int param_number) void PQprint(FILE *fout,const PGresult *res,const PQprintOpt *po) char *PQcmdStatus(PGresult *res) char *PQcmdTuples(PGresult *res) Oid PQoidValue(const PGresult *res) char *PQoidStatus(const PGresult *res) char *PQescapeLiteral(PGconn *conn, const char *str, size_t length) char *PQescapeIdentifier(PGconn *conn, const char *str, size_t length) size_t PQescapeStringConn(PGconn *conn,char *to, const char *from, size_t length,int *error) size_t PQescapeString(char *to, const char *from, size_t length) unsigned char *PQescapeByteaConn(PGconn *conn, const unsigned char *from,size_t from_length,size_t *to_length) unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length,size_t *to_length) unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length) int PQsendQuery(PGconn *conn, const char *command) int PQsendQueryParams(PGconn *conn,const char *command, int nParams,const Oid *paramTypes,const char **paramValues, const int *paramLengths,const int *paramFormats,int resultFormat) int PQsendPrepare(PGconn *conn,const char *stmtName, const char *query,int nParams,const Oid *paramTypes) int PQsendQueryPrepared(PGconn *conn,const char *stmtName, int nParams,const char **paramValues, const int *paramLengths,const int *paramFormats,int resultFormat) int PQsendDescribePrepared(PGconn *conn, const char *stmtName) int PQsendDescribePortal(PGconn *conn, const char *portalName) PGresult *PQgetResult(PGconn *conn) int PQconsumeInput(PGconn *conn) int PQisBusy(PGconn *conn) int PQsetnonblocking(PGconn *conn, int arg) int PQisnonblocking(const PGconn *conn) int PQflush(PGconn *conn) int PQsetSingleRowMode(PGconn *conn) PGcancel *PQgetCancel(PGconn *conn) void PQfreeCancel(PGcancel *cancel) int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize) int PQrequestCancel(PGconn *conn) PGresult *PQfn(PGconn *conn,int fnid,int *result_buf, int *result_len,int result_is_int,const PQArgBlock *args,int nargs) PGnotify *PQnotifies(PGconn *conn) int PQputCopyData(PGconn *conn,const char *buffer,int nbytes) (continues on next page) 29.4. RingPostgreSQL Functions 200
245.
Ring Documentation, Release1.21.2 (continued from previous page) int PQputCopyEnd(PGconn *conn,const char *errormsg) int PQgetCopyData(PGconn *conn,char **buffer,int async) int PQgetline(PGconn *conn,char *buffer,int length) int PQgetlineAsync(PGconn *conn,char *buffer,int bufsize) int PQputline(PGconn *conn,const char *string) int PQputnbytes(PGconn *conn,const char *buffer,int nbytes) int PQendcopy(PGconn *conn) int PQclientEncoding(const PGconn *conn) char *pg_encoding_to_char(int encoding_id) int PQsetClientEncoding(PGconn *conn, const char *encoding) void PQtrace(PGconn *conn, FILE *stream) void PQuntrace(PGconn *conn) void PQfreemem(void *ptr) void PQconninfoFree(PQconninfoOption *connOptions) char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm) char *PQencryptPassword(const char *passwd, const char *user) PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) int PQfireResultCreateEvents(PGconn *conn, PGresult *res) PGresult *PQcopyResult(const PGresult *src, int flags) int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs) int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) void *PQresultAlloc(PGresult *res, size_t nBytes) int PQlibVersion(void) PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc,void *arg) PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc,void *arg) void PQinitOpenSSL(int do_ssl, int do_crypto) void PQinitSSL(int do_ssl) int PQisthreadsafe(void) 29.4. RingPostgreSQL Functions 201
246.
CHAPTER THIRTY SECURITY AND INTERNETFUNCTIONS This chapter contains the security and internet functions provided by the Ring programming language for Hashing, Encryption & Decryption. Before using the next functions load the openssllib.ring library load "openssllib.ring" # Use OpenSSL functions • MD5() • SHA1() • SHA256() • SHA512() • SHA384() • SHA224() • SupportedCiphers() • Encrypt() • Decrypt() • Randbytes() • rsa_generate • rsa_export_params • rsa_import_params • rsa_export_pem • rsa_import_pem • rsa_is_privatekey • rsa_encrypt_pkcs • rsa_decrypt_pkcs • rsa_encrypt_oaep • rsa_decrypt_oaep • rsa_encrypt_raw • rsa_decrypt_raw • rsa_sign_pkcs 202
247.
Ring Documentation, Release1.21.2 • rsa_signhash_pkcs • rsa_verify_pkcs • rsa_verifyhash_pkcs • rsa_sign_pss • rsa_signhash_pss • rsa_verify_pss • rsa_verifyhash_pss • openssl_versiontext • openssl_version • MD5Init(), MD5Update(), MD5Final() • SHA1Init(), SHA1Update(), SHA1Final() • SHA256Init(), SHA256Update(), SHA256Final() • SHA512Init(), SHA512Update(), SHA512Final() • SHA384Init(), SHA384Update(), SHA384Final() • SHA224Init(), SHA224Update(), SHA224Final() Before using the next functions load the internetlib.ring library load "internetlib.ring" # Use the Internet functions • Download() • SendEmail() 30.1 MD5() Function We can calculate the MD5 hash using the MD5() Function Syntax: MD5(cString) ---> String contains the MD5 hash of the string cString Example: see "md5('happy') = " + md5("happy") + nl + "md5('Hello') = " + md5("Hello") + nl Output: md5('happy') = 56ab24c15b72a457069c5ea42fcfc640 md5('Hello') = 8b1a9953c4611296a827abf8c47804d7 30.1. MD5() Function 203
248.
Ring Documentation, Release1.21.2 30.2 SHA1() Function We can calculate the SHA1 hash using the SHA1() Function Syntax: SHA1(cString) ---> String contains the SHA1 hash of the string cString Example: see "sha1('hello') : " + sha1("hello") + nl + "sha1('apple') : " + sha1("apple") + nl Output: sha1('hello') : aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d sha1('apple') : d0be2dc421be4fcd0172e5afceea3970e2f3d940 30.3 SHA256() Function We can calculate the SHA256 hash using the SHA256() Function Syntax: SHA256(cString) ---> String contains the SHA256 hash of the string cString Example: see "sha256('hello') : " + sha256("hello") + nl + "sha256('apple') : " + sha256("apple") + nl Output: sha256('hello') : 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 sha256('apple') : 3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b 30.4 SHA512() Function We can calculate the SHA512 hash using the SHA512() Function Syntax: SHA512(cString) ---> String contains the SHA512 hash of the string cString Example: see "sha512('hello') : " + sha512("hello") + nl + "sha512('apple') : " + sha512("apple") + nl + "sha512('hello world') : " + sha512("hello world") + nl Output: 30.2. SHA1() Function 204
249.
Ring Documentation, Release1.21.2 sha512('hello') : 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673c a72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043 sha512('apple') : 844d8779103b94c18f4aa4cc0c3b4474058580a991fba85d3ca698a0bc9e52 c5940feb7a65a3a290e17e6b23ee943ecc4f73e7490327245b4fe5d5efb590feb2 sha512('hello world') : 309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca8 6d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f 30.5 SHA384() Function We can calculate the SHA384 hash using the SHA384() Function Syntax: SHA384(cString) ---> String contains the SHA384 hash of the string cString Example: see "sha384('hello') : " + sha384("hello") + nl + "sha384('apple') : " + sha384("apple") + nl + "sha384('hello world') : " + sha384("hello world") + nl Output: sha384('hello') : 59e1748777448c69de6b800d7a33bbfb9ff1b463e44354c3553bcdb9c666fa 90125a3c79f90397bdf5f6a13de828684f sha384('apple') : 3d8786fcb588c93348756c6429717dc6c374a14f7029362281a3b21dc10250 ddf0d0578052749822eb08bc0dc1e68b0f sha384('hello world') : fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcb b83578b3e417cb71ce646efd0819dd8c088de1bd 30.6 SHA224() Function We can calculate the SHA224 hash using the SHA224() Function Syntax: SHA224(cString) ---> String contains the SHA224 hash of the string cString Example: see "sha224('hello') : " + sha224("hello") + nl + "sha224('apple') : " + sha224("apple") + nl + "sha224('hello world') : " + sha224("hello world") + nl Output: sha224('hello') : ea09ae9cc6768c50fcee903ed054556e5bfc8347907f12598aa24193 sha224('apple') : b7bbfdf1a1012999b3c466fdeb906a629caa5e3e022428d1eb702281 sha224('hello world') : 2f05477fc24bb4faefd86517156dafdecec45b8ad3cf2522a563582b 30.5. SHA384() Function 205
250.
Ring Documentation, Release1.21.2 30.7 SupportedCiphers() Function The function SupportedCiphers() retrieves the list of all algorithms supported by Encrypt()/Decrypt() functions. Syntax: SupportedCiphers() ---> List of names of cipher algorithms supported by Encrypt()/ ˓ →Decrypt() functions 30.8 Encrypt() Function We can use the Encrypt() function to encrypt the data using the specified algorithm. If no algorithm is specified, Blowfish algorithm is used in CBC mode. Typical algorithm values: “bf”, “des”, “des3”, “aes128”, “aes192”, “aes256” which all use CBC mode. The function CipherAlgorithms() return the list of all supported cipher algorithms. Syntax: Encrypt(cString, cKey, cIV[, cCipherAlgorithmName]) ---> Encrypted string 30.9 Decrypt() Function We can use the Decrypt() function to decrypt the data encrypted using the Encrypt() function. If no algorithm is specified, Blowfish algorithm is used in CBC mode. Typical algorithm values: “bf”, “des”, “des3”, “aes128”, “aes192”, “aes256” which all use CBC mode. The function CipherAlgorithms() return the list of all supported cipher algorithms. Syntax: Decrypt(cCipher, cKey, cIV[, cCipherAlgorithm]) ---> Decrypted string 30.10 Encryption and Decryption Example The next example demonstrates how to use the Encrypt() and Decrypt() functions. These functions use the AES-128 algorithm (AES with 128-bits key) See "Enter a string : " give cStr list = 0:15 cKey="" for x in list cKey += char(x) next list = 1:16 cIV = "" for x in list cIV += char(x) next cStr = Encrypt(cStr,cKey,cIV,"aes128") See "Cipher Text : " + cStr + nl + "Plain Text : " + Decrypt(cStr,cKey,cIV,"aes128") + nl We can write the same example using normal for loop See "Enter a string : " give cStr cKey="" # 16 bytes for x = 0 to 15 cKey += char(x) (continues on next page) 30.7. SupportedCiphers() Function 206
251.
Ring Documentation, Release1.21.2 (continued from previous page) next cIV = "" for x = 1 to 16 cIV += char(x) next cStr = Encrypt(cStr,cKey,cIV,"aes128") See "Cipher Text : " + cStr + nl + "Plain Text : " + Decrypt(cStr,cKey,cIV,"aes128") + nl Also we can write the key and the IV directly using strings See "Enter a string : " give cStr # Note: Don't use simple key in real applications! cKey = "1234567890@#$%^&" cIV = "FEDCBA0987654321" cStr = Encrypt(cStr,cKey,cIV,"aes128") See "Cipher Text : " + cStr + nl + "Plain Text : " + Decrypt(cStr,cKey,cIV,"aes128") + nl Finally we can specify the key and the IV values using hexadecimal notation See "Enter a string : " give cStr # Note: Don't use simple key in real applications! cKey = hex2str("A0A1A2A3A5A6A7A8AAABACADAFB0B1B2") cIV = hex2str("00112233445566778899AABBCCDDEEFF") cStr = Encrypt(cStr,cKey,cIV,"aes128") See "Cipher Text : " + cStr + nl + "Plain Text : " + Decrypt(cStr,cKey,cIV,"aes128") + nl 30.11 File Hash The next example demonstrates how to calculate the hash functions for files cStr = read("myapp.exe") see "Size : " + len(cStr) + nl + "md5 : " + md5(cStr) + nl + "sha1 : " + sha1(cStr) + nl + "sha256 : " + sha256(cStr) + nl + "sha224 : " + sha224(cStr) + nl + "sha384 : " + sha384(cStr) + nl + "sha512 : " + sha512(cStr) + nl Output: 30.11. File Hash 207
252.
Ring Documentation, Release1.21.2 Size : 58079876 md5 : 762eee15d8d2fd73b71ea52538b28667 sha1 : 9212c0c7258bad89a62bd239e1358a9276a9d070 sha256 : 7d6724e69b6c553da749ba31b6185dddc965129b64d9e9bf3de88f67df3b1cdc sha224 : 5a9c8a7d662bce4f880ba94f90a79362b672528b9efd5abc718c7a3d sha384 : 18e23f973abedbeb3981c423f12aeadecf96f9c6fb28aeabe3be4c484f8540afcc3861b b370ce2b59cf3c99c130b856b sha512 : da3d5e997d06f8b2a7a9964b77f7d82eedb76b245c611082c1639f83f51d83880bcd08f cd53dcab1167bdca0b82fec5071971ac17c76479d76985ced4ab0d18e 30.12 Randbytes() Function We can generate a string of cryptographically secure pseudo-random bytes using the Randbytes() function. Syntax: Randbytes(nSize) ---> String contains random bytes (bytes count = nSize) Example: salt = randbytes(32) password = "SecretPassWord@$%123" see salt + nl see sha256("test" + salt) + nl 30.13 rsa_generate() Function We can generate a random RSA key pair using the rsa_generate() function. Syntax: rsa_generate(nBits[,nPublicExponent]) ---> a random RSA key pair with nBits as size in␣ ˓ →bits If nPublicExponent is omited, then the standard public exponent value␣ ˓ →65537 is used. Example: /* generate a new 2048-bit RSA key pair */ try rsaKey = rsa_generate(2048) rsaKeyParams = rsa_export_params(rsaKey) See "Modulus = " + rsaKeyParams[:n] + nl catch See "Failed to generate the RSA key pair: " + cCatchError + nl done 30.12. Randbytes() Function 208
253.
Ring Documentation, Release1.21.2 30.14 rsa_export_params() Function We can export the parameters of an RSA key to a string-indexed list using the rsa_export_params() function. The list contains the following string indexes: • “type” for the key type as a string equal to “RSA” in our case • “bits” for the bot length of the key as an integer • “n” for the Modulus as a hexadecimal string • “e” for the Public Exponent as a hexadecimal string • “d” for the Private Exponent as a hexadecimal string • “p” for the first prime as a hexadecimal string • “q” for the second prime as a hexadecimal string • “dmp1” for the first CRT exponent as a hexadecimal string • “dmq1” for the second CRT exponent as a hexadecimal string • “iqmp” for the CRT coefficent as a hexadecimal string If the key contains only the public part, then “d”, “p”, “q”, “dmp1”, “dmq1” and “iqmp” will be empty strings. Syntax: rsa_export_params(pRsaKey) ---> list of the key parameters Example: /* generate a new 2048-bit RSA key pair */ try rsaKey = rsa_generate(2048) rsaKeyParams = rsa_export_params(rsaKey) See "Key Type = " + rsaKeyParams[:type] + nl See "Key Size = " + rsaKeyParams[:bits] + " bits" + nl See "Modulus = " + rsaKeyParams[:n] + nl See "Public Exponent = " + rsaKeyParams[:e] + nl See "Private Exponent = " + rsaKeyParams[:d] + nl See "Prime 1 = " + rsaKeyParams[:p] + nl See "Prime 2 = " + rsaKeyParams[:q] + nl See "CRT Exponent 1 = " + rsaKeyParams[:dmp1] + nl See "CRT Exponent 2 = " + rsaKeyParams[:dmq1] + nl See "CRT Coefficient = " + rsaKeyParams[:iqmp] + nl catch See "Failed to generate the RSA key pair: " + cCatchError + nl done 30.14. rsa_export_params() Function 209
254.
Ring Documentation, Release1.21.2 30.15 rsa_import_params() Function We can create a new RSA key from parameters stored in a string-indexed list using the rsa_import_params() function. The format of the input list is the one described in the function rsa_export_params The indexes “n” and “e” must not be empty, otherwise an exception is thrown. If we need to import only an RSA public key, then the indexes “d”, “p”, “q”, “dmp1”, “dmq1” and “iqmp” must be empty. Syntax: rsa_import_params(pParamsList) ---> a new RSA key Example: /* create an RSA public key from a generated RSA key pair */ try rsaKey = rsa_generate(2048) rsaKeyParams = rsa_export_params(rsaKey) /* create parameters of public key: modulus and public exponent */ rsaPublicKeyParam = [:n = rsaKeyParams[:n], :e = rsaKeyParams[:e]] /* create the public key using rsa_import_params */ rsaPublicKey = rsa_import_params(rsaPublicKeyParam) catch See "Unexpected error occured: " + cCatchError + nl done 30.16 rsa_export_pem() Function We can export an RSA key to a string in PEM format using the rsa_export_pem() function. If the RSA key contains both public and private parts, then returned string will start with “—–BEGIN PRIVATE KEY—–” If the RSA key contains only the public part, then returned string will start with “—–BEGIN PUBLIC KEY—–” Syntax: rsa_export_pem(pRsaKey) ---> string encoding of the key in PEM format Example: /* generate an RSA key and save it to a file in PEM format */ try rsaKey = rsa_generate(2048) rsaKeyPEM = rsa_export_pem(rsaKey) /* save private key to a file */ write ("privateKey.pem", rsaKeyPEM) /* save public key to a file */ rsaKeyParams = rsa_export_params(rsaKey) rsaPublicKeyParam = [:n = rsaKeyParams[:n], :e = rsaKeyParams[:e]] rsaPublicKey = rsa_import_params(rsaPublicKeyParam) rsaPublicKeyPEM = rsa_export_pem(rsaPublicKey) write ("publicKey.pem", rsaPublicKeyPEM) (continues on next page) 30.15. rsa_import_params() Function 210
255.
Ring Documentation, Release1.21.2 (continued from previous page) catch See "Unexpected error occured: " + cCatchError + nl done 30.17 rsa_import_pem() Function We can create an RSA key from PEM encoded string using the rsa_import_pem() function. If the PEM string starts with “—–BEGIN PRIVATE KEY—–”, then a full RSA key pair will be created. if the PEM string starts with “—–BEGIN PUBLIC KEY—–”, then an RSA public key will be created. Syntax: rsa_import_pem(cStrPEM) ---> a new RSA key Example: /* create an RSA key from a PEM file */ try rsaKeyPEM = Read("privateKey.pem") rsaKey = rsa_import_pem(rsaKeyPEM) rsaPublicKeyPEM = Read("publicKey.pem") rsaPublicKey = rsa_import_pem(rsaPublicKeyPEM) catch See "Unexpected error occured: " + cCatchError + nl done 30.18 rsa_is_privatekey() Function We can check whether an RSA key is a private key or public key using the rsa_is_privatekey() function. Syntax: rsa_is_privatekey(pRsaKey) ---> returns 1 if pRsaKey is an RSA private key and 0 if it␣ ˓ →is an RSA public key Example: /* create an RSA key from a PEM file and check if it is a private key */ try rsaKeyPEM = Read("key.pem") rsaKey = rsa_import_pem(rsaKeyPEM) if rsa_is_privatekey(rsaKey) See "an RSA private key was loaded" + nl else See "an RSA public key was loaded" + nl ok (continues on next page) 30.17. rsa_import_pem() Function 211
256.
Ring Documentation, Release1.21.2 (continued from previous page) catch See "Unexpected error occured: " + cCatchError + nl done 30.19 rsa_encrypt_pkcs() Function We can encrypt data with an RSA key and PKCS#1 v1.5 padding using the rsa_encrypt_pkcs() function. The maximum size of data that can be encrypted by rsa_encrypt_pkcs is (modulusLen - 11), with modulusLen the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the maximum size of data that can be encrypted is 256 - 11 = 245 bytes. RSA encryption is usually applied to a symmetric key (e.g. AES) which is used to encrypt much larger data. RSA encryption needs only the public part of an RSA key, so rsa_encrypt_pkcs can be used with both RSA private key and RSA public key Syntax: rsa_encrypt_pkcs(pRsaKey,cPlainData) ---> return a string containing the encryption of␣ ˓ →cPlainData Example: /* encrypt a file using AES key and then encrypt the AES key using an RSA public key */ try /* read Alice public key */ rsaPublicKeyPEM = Read("alice_public_key.pem") rsaPublicKey = rsa_import_pem(rsaPublicKeyPEM) /* encrypt file with random AES-128 key */ cData = Read ("secret_document.txt") cKey = RandBytes(16) cIV = RandBytes(16) cEncryptedData = Encrypt(cData,cKey,cIV,"aes128") /* encrypt the AES-128 key with the RSA public key */ cEncryptedKey = rsa_encrypt_pkcs(rsaPublicKey,cKey) /* store IV, encrypted AES key and encrypted data in a file to be sent to Alice*/ Write("encrypted_document.enc", cIV + cEncryptedKey + cEncryptedData) catch See "Unexpected error occured: " + cCatchError + nl done 30.19. rsa_encrypt_pkcs() Function 212
257.
Ring Documentation, Release1.21.2 30.20 rsa_decrypt_pkcs() Function We can decrypt data encrypted with an RSA key and PKCS#1 v1.5 padding using the rsa_decrypt_pkcs() function. The size of data that can be decrypted by rsa_decrypt_pkcs must be equal to modulusLen which is the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the size of encrypted data that can be decrypted must be 256 bytes. For RSA decryption, the RSA key must contain the private key part. Syntax: rsa_decrypt_pkcs(pRsaKey,cEncryptedData) ---> return a string containing the decryption␣ ˓ →of cEncryptedData Example: /* decrypt a file by first decrypting AES key that was used to encrypt it * and then decrypt the whole content using the AES key */ try /* read Alice private key */ rsaKeyPEM = Read("alice_private_key.pem") rsaKey = rsa_import_pem(rsaKeyPEM) /* calculate the modulus length */ rsaKeyParams = rsa_export_params(rsaKey) modulusLen = rsaKeyParams[:bits]/ 8 /* read encrypted file */ cEncryptedContent = Read ("encrypted_document.enc") /* IV is the first 16 bytes if the file */ cIV = substr(cEncryptedContent, 1, 16) /* encrypted key follows IV and its length is modulusLen */ cEncryptedKey = substr(cEncryptedContent, 17, modulusLen) /* encrypted data follows the key */ cEncryptedData = substr(cEncryptedContent, 17 + modulusLen) /* decrypt the AES-128 key */ cKey = rsa_decrypt_pkcs(rsaKey,cEncryptedKey) /* decrypt the data using the AES-128 key */ cPlainData = Decrypt(cEncryptedData,cKey,cIV,"aes128") /* store the decrypted data to a file */ Write("decrypted_document.txt", cPlainData) catch See "Unexpected error occured: " + cCatchError + nl done 30.20. rsa_decrypt_pkcs() Function 213
258.
Ring Documentation, Release1.21.2 30.21 rsa_encrypt_oaep() Function We can encrypt data with an RSA key and OAEP padding using the rsa_encrypt_oaep() function. The maximum size of data that can be encrypted by rsa_encrypt_oaep is (modulusLen - 2*hashLen -2), with modulusLen the length of the RSA key modulus in bytes and hashLen and the length of hash algorithm used. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the maximum size of data that can be encrypted using OAEP padding with SHA-1 is 256 - 2*20 - 2 = 214 bytes. RSA encryption is usually applied to a symmetric key (e.g. AES) which is used to encrypt much larger data. RSA encryption needs only the public part of an RSA key, so rsa_encrypt_oaep can be used with both RSA private key and RSA public key. Syntax: rsa_encrypt_oaep(pRsaKey,cPlainData[,nHashAlgorithm]) ---> return a string containing␣ ˓ →the OAEP encryption of cPlainData nHashAlgorithm indicates the hash algorithm to use for OAEP padding. If omited,␣ ˓ →SHA-1 is used by default. Possible values for nHashAlgorithm argument are: - $OSSL_HASH_MD5 which is equal to 0 - $OSSL_HASH_SHA1 which is equal to 1 - $OSSL_HASH_SHA256 which is equal to 2 - $OSSL_HASH_SHA384 which is equal to 3 - $OSSL_HASH_SHA512 which is equal to 4 Example: /* encrypt a file using AES key and then encrypt the AES key using an RSA public key␣ ˓ →using OAEP padding */ try /* read Alice public key */ rsaPublicKeyPEM = Read("alice_public_key.pem") rsaPublicKey = rsa_import_pem(rsaPublicKeyPEM) /* encrypt file with random AES-128 key */ cData = Read ("secret_document.txt") cKey = RandBytes(16) cIV = RandBytes(16) cEncryptedData = Encrypt(cData,cKey,cIV,"aes128") /* encrypt the AES-128 key with the RSA public key */ cEncryptedKey = rsa_encrypt_oaep(rsaPublicKey,cKey) /* store IV, encrypted AES key and encrypted data in a file to be sent to Alice*/ Write("oaep_encrypted_document.enc", cIV + cEncryptedKey + cEncryptedData) catch See "Unexpected error occured: " + cCatchError + nl done 30.21. rsa_encrypt_oaep() Function 214
259.
Ring Documentation, Release1.21.2 30.22 rsa_decrypt_oaep() Function We can decrypt data encrypted with an RSA key and OAEP padding using the rsa_decrypt_oaep() function. The size of data that can be decrypted by rsa_decrypt_oaep must be equal to modulusLen which is the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the size of encrypted data that can be decrypted must be 256 bytes. For RSA decryption, the RSA key must contain the private key part. The hash algorithm specified in rsa_decrypt_oaep() call must be the same as the one used during OAEP encryption. Syntax: rsa_decrypt_oaep(pRsaKey,cEncryptedData[,nHashAlgorithm]) ---> return a string␣ ˓ →containing the decryption of cEncryptedData nHashAlgorithm indicates the hash algorithm to use for OAEP padding. If omited,␣ ˓ →SHA-1 is used by default. Possible values for nHashAlgorithm argument are: - $OSSL_HASH_MD5 which is equal to 0 - $OSSL_HASH_SHA1 which is equal to 1 - $OSSL_HASH_SHA256 which is equal to 2 - $OSSL_HASH_SHA384 which is equal to 3 - $OSSL_HASH_SHA512 which is equal to 4 Example: /* decrypt a file by first decrypting AES key that was used to encrypt it * and then decrypt the whole content using the AES key */ try /* read Alice private key */ rsaKeyPEM = Read("alice_private_key.pem") rsaKey = rsa_import_pem(rsaKeyPEM) /* calculate the modulus length */ rsaKeyParams = rsa_export_params(rsaKey) modulusLen = rsaKeyParams[:bits]/ 8 /* read encrypted file */ cEncryptedContent = Read ("oaep_encrypted_document.enc") /* IV is the first 16 bytes if the file */ cIV = substr(cEncryptedContent, 1, 16) /* encrypted key follows IV and its length is modulusLen */ cEncryptedKey = substr(cEncryptedContent, 17, modulusLen) /* encrypted data follows the key */ cEncryptedData = substr(cEncryptedContent, 17 + modulusLen) /* decrypt the AES-128 key */ cKey = rsa_decrypt_oaep(rsaKey,cEncryptedKey) /* decrypt the data using the AES-128 key */ (continues on next page) 30.22. rsa_decrypt_oaep() Function 215
260.
Ring Documentation, Release1.21.2 (continued from previous page) cPlainData = Decrypt(cEncryptedData,cKey,cIV,"aes128") /* store the decrypted data to a file */ Write("oaep_decrypted_document.txt", cPlainData) catch See "Unexpected error occured: " + cCatchError + nl done 30.23 rsa_encrypt_raw() Function We can perform raw RSA encryption on data using the function rsa_encrypt_raw() The size of data must be equal to the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the size of input data that can be encrypted using raw RSA is 256 bytes. Raw RSA encryption needs only the public part of an RSA key, so rsa_encrypt_raw can be used with both RSA private key and RSA public key. Raw RSA should only be used to implement secure cryptographic protocols. Encrypting user data directly with raw RSA is insecure. Syntax: rsa_encrypt_raw(pRsaKey,cPlainData) ---> return a string containing the raw RSA␣ ˓ →encryption of cPlainData Example: /* encrypt a file using AES key and then encrypt the AES key using an RSA public key␣ ˓ →using PKCS1 padding */ /* we manually add PKCS1 padding and then perform raw RSA encryption */ try /* read Alice public key */ rsaPublicKeyPEM = Read("alice_public_key.pem") rsaPublicKey = rsa_import_pem(rsaPublicKeyPEM) /* encrypt file with random AES-128 key */ cData = Read ("secret_document.txt") cKey = RandBytes(16) cIV = RandBytes(16) cEncryptedData = Encrypt(cData,cKey,cIV,"aes128") /* encrypt the AES-128 key with the RSA public key */ /* calculate the modulus length */ rsaKeyParams = rsa_export_params(rsaPublicKey) modulusLen = rsaKeyParams[:bits]/ 8 /* we manually add PKCS1 padding */ paddingSize = modulusLen - Len(cKey) - 2 - 1 paddingStr = space (paddingSize) /* encryption case. Add random bytes */ for i=1 to paddingSize (continues on next page) 30.23. rsa_encrypt_raw() Function 216
261.
Ring Documentation, Release1.21.2 (continued from previous page) paddingStr[i] = Char (1 + Random(254)) next paddedData = Char(0) + Char(2) + paddingStr + Char (0) + cKey cEncryptedKey = rsa_encrypt_raw(rsaPublicKey,paddedData) /* store IV, encrypted AES key and encrypted data in a file to be sent to Alice*/ Write("raw_encrypted_document.enc", cIV + cEncryptedKey + cEncryptedData) catch See "Unexpected error occured: " + cCatchError + nl done 30.24 rsa_decrypt_raw() Function We can perform raw RSA decryption of data using the rsa_decrypt_pkcs() function. The size of data that can be decrypted by rsa_decrypt_raw must be equal to modulusLen which is the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the size of encrypted data that can be decrypted must be 256 bytes. For raw RSA decryption, the RSA key must contain the private key part. The size of the result of raw RSA decryption is equal to the length of RSA modulus in bytes. Syntax: rsa_decrypt_raw(pRsaKey,cEncryptedData) ---> return a string containing the decryption␣ ˓ →of cEncryptedData Example: /* decrypt a file by first decrypting AES key that was used to encrypt it * and then decrypt the whole content using the AES key /* We decrypt AES using rsa_decrypt_raw and then remove padding manually */ try /* read Alice private key */ rsaKeyPEM = Read("alice_private_key.pem") rsaKey = rsa_import_pem(rsaKeyPEM) /* calculate the modulus length */ rsaKeyParams = rsa_export_params(rsaKey) modulusLen = rsaKeyParams[:bits]/ 8 /* read encrypted file */ cEncryptedContent = Read ("encrypted_document.enc") /* IV is the first 16 bytes if the file */ cIV = substr(cEncryptedContent, 1, 16) /* encrypted key follows IV and its length is modulusLen */ cEncryptedKey = substr(cEncryptedContent, 17, modulusLen) (continues on next page) 30.24. rsa_decrypt_raw() Function 217
262.
Ring Documentation, Release1.21.2 (continued from previous page) /* encrypted data follows the key */ cEncryptedData = substr(cEncryptedContent, 17 + modulusLen) /* decrypt the AES-128 key */ cPaddedKey = rsa_decrypt_raw(rsaKey,cEncryptedKey) /* remove PKCS1 padding */ paddedInputLength = len(cPaddedKey) cKey = "" if paddedInputLength > 11 AND Ascii(cPaddedKey[1]) = 0 AND Ascii(cPaddedKey[2])␣ ˓ →= 2 zeroFound = false for j = 3 to paddedInputLength if Ascii(cPaddedKey[j]) = 0 i = j zeroFound = true exit ok next if zeroFound if i = paddedInputLength /* unpadded data is empty */ Raise("Empty data recovered from padding") else cKey = substr(cPaddedKey,i+1) ok else Raise ("Invalid data padding") ok else Raise("the decrypted data is invalid") ok /* decrypt the data using the AES-128 key */ cPlainData = Decrypt(cEncryptedData,cKey,cIV,"aes128") /* store the decrypted data to a file */ Write("decrypted_document.txt", cPlainData) catch See "Unexpected error occured: " + cCatchError + nl done 30.24. rsa_decrypt_raw() Function 218
263.
Ring Documentation, Release1.21.2 30.25 rsa_sign_pkcs() Function We can sign data with RSA PKCS#1 v1.5 padding using the function rsa_sign_pkcs() The maximum size of data that can be signed by rsa_sign_pkcs is (modulusLen - 11), with modulusLen the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the maximum size of data that can be signed is 256 - 11 = 245 bytes. For RSA PKCS signature, the RSA key must contain the private key part. The size of the result of RSA PKCS signature is equal to the length of RSA modulus in bytes. Syntax: rsa_sign_pkcs(pRsaKey,cData) ---> return a string containing RSA PKCS signature Example: /* sign a document using RSA-PKCS with SHA256. * digest OID added manually */ try /* read Alice private key */ rsaKeyPEM = Read("alice_private_key.pem") rsaKey = rsa_import_pem(rsaKeyPEM) /* read file content */ cFileContent = Read ("document.txt") /* hash content */ digest = SHA256(cFileContent) /* digest OID of SHA256 */ digestOID = hex2str("3031300d060960864801650304020105000420") /* perform PKCS signing */ dataToSign = digestOID + digest cSignature = rsa_sign_pkcs(rsaKey,dataToSign) /* store the signature */ Write("document.txt.pkcs1.sig", cSignature) catch See "Unexpected error occured: " + cCatchError + nl done 30.26 rsa_signhash_pkcs() Function We can sign a hash value with RSA PKCS#1 v1.5 padding using the function rsa_signhash_pkcs() This function infers the hash algorithm from hash value size and it automatically adds OID of hash algorithm before applying the PKCS#1 v1.5 padding. For RSA PKCS signature, the RSA key must contain the private key part. The size of the result of RSA PKCS signature is equal to the length of RSA modulus in bytes. Syntax: rsa_signhash_pkcs(pRsaKey,cHashValue) ---> return a string containing RSA PKCS signature 30.25. rsa_sign_pkcs() Function 219
264.
Ring Documentation, Release1.21.2 Example: /* sign a document using RSA-PKCS with SHA256. */ try /* read Alice private key */ rsaKeyPEM = Read("alice_private_key.pem") rsaKey = rsa_import_pem(rsaKeyPEM) /* read file content */ cFileContent = Read ("document.txt") /* hash content */ digest = SHA256(cFileContent) /* perform PKCS signing */ cSignature = rsa_signhash_pkcs(rsaKey,digest) /* store the signature */ Write("document.txt.pkcs1.sig", cSignature) catch See "Unexpected error occured: " + cCatchError + nl done 30.27 rsa_verify_pkcs() Function We can verify an RSA-PKCS signature of data using the function rsa_verify_pkcs() The size of signature must be equal to the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the size of input signature that can be verified using RSA-PKCS is 256 bytes. RSA-PKCS verification needs only the public part of an RSA key, so rsa_verify_pkcs can be used with both RSA private key and RSA public key. Syntax: rsa_verify_pkcs(pRsaKey,cData,cSignature) ---> returns 1 if signature is valid and 0␣ ˓ →otherwise Example: /* verify a document signature using RSA-PKCS with SHA256 * digest OID is added manually */ try /* read Alice public key */ rsaPublicKeyPEM = Read("alice_public_key.pem") rsaPublicKey = rsa_import_pem(rsaPublicKeyPEM) /* read file content */ cFileContent = Read ("document.txt") /* hash content */ (continues on next page) 30.27. rsa_verify_pkcs() Function 220
265.
Ring Documentation, Release1.21.2 (continued from previous page) digest = SHA256(cFileContent) /* digest OID of SHA256 */ digestOID = hex2str("3031300d060960864801650304020105000420") /* read file signature */ cSignature = Read ("document.txt.pkcs1.sig") /* perform PKCS verification */ dataToVerify = digestOID + digest if rsa_verify_pkcs(rsaPublicKey,dataToVerify,cSignature) See "file signature is valid" + nl else See "file signature is INVALID" + nl ok catch See "Unexpected error occured: " + cCatchError + nl done 30.28 rsa_verifyhash_pkcs() Function We can verify the RSA-PKCS signature of a hash value using the function rsa_verifyhash_pkcs() This function infers the hash algorithm from hash value size and it automatically uses the OID of hash algorithm during verification. The size of signature must be equal to the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the size of input signature that can be verified using RSA-PKCS is 256 bytes. RSA-PKCS verification needs only the public part of an RSA key, so rsa_verifyhash_pkcs can be used with both RSA private key and RSA public key. Syntax: rsa_verifyhash_pkcs(pRsaKey,cHashValue,cSignature) ---> returns 1 if signature is valid␣ ˓ →and 0 otherwise Example: /* verify a document signature using RSA-PKCS with SHA256 */ try /* read Alice public key */ rsaPublicKeyPEM = Read("alice_public_key.pem") rsaPublicKey = rsa_import_pem(rsaPublicKeyPEM) /* read file content */ cFileContent = Read ("document.txt") /* hash content */ digest = SHA256(cFileContent) /* read file signature */ cSignature = Read ("document.txt.pkcs1.sig") (continues on next page) 30.28. rsa_verifyhash_pkcs() Function 221
266.
Ring Documentation, Release1.21.2 (continued from previous page) /* perform PKCS verification */ if rsa_verifyhash_pkcs(rsaPublicKey,digest,cSignature) See "file signature is valid" + nl else See "file signature is INVALID" + nl ok catch See "Unexpected error occured: " + cCatchError + nl done 30.29 rsa_sign_pss() Function We can sign data with RSA PSS using the function rsa_sign_pss() The input data will be first hashed using the specified hash algorithm then RSA PSS signing will be applied to the computed hash value. For RSA PSS signature, the RSA key must contain the private key part. The size of the result of RSA PSS signature is equal to the length of RSA modulus in bytes. Syntax: rsa_sign_pss(pRsaKey,cData,nHashAlgorithm[,nSaltLength]) ---> return a string containing␣ ˓ →RSA PSS signature nHashAlgorithm indicates the hash algorithm to use for hashing and PSS padding. nSaltLength indicates the length of PSS salt to use. If ommited, then maximum␣ ˓ →salt length is used. nSaltLength can have the special values -1 and -2: -1 indicates that salt length␣ ˓ →is equal to hash size and -2 indicates that maximum salt length is used. Possible values for nHashAlgorithm argument are: - $OSSL_HASH_MD5 which is equal to 0 - $OSSL_HASH_SHA1 which is equal to 1 - $OSSL_HASH_SHA256 which is equal to 2 - $OSSL_HASH_SHA384 which is equal to 3 - $OSSL_HASH_SHA512 which is equal to 4 Example: /* sign a document using RSA-PSS with SHA256 and maximal salt length */ try /* read Alice private key */ rsaKeyPEM = Read("alice_private_key.pem") rsaKey = rsa_import_pem(rsaKeyPEM) /* read file content */ cFileContent = Read ("document.txt") /* perform PSS signing */ cSignature = rsa_sign_pss(rsaKey,cFileContent,$OSSL_HASH_SHA256) (continues on next page) 30.29. rsa_sign_pss() Function 222
267.
Ring Documentation, Release1.21.2 (continued from previous page) /* store the signature */ Write("document.txt.sig", cSignature) catch See "Unexpected error occured: " + cCatchError + nl done 30.30 rsa_signhash_pss() Function We can sign a hash value with RSA PSS using the function rsa_signhash_pss() This function infers the hash algorithm from hash value size. For RSA PSS signature, the RSA key must contain the private key part. The size of the result of RSA PSS signature is equal to the length of RSA modulus in bytes. Syntax: rsa_signhash_pss(pRsaKey,cHashValue[,nSaltLength]) ---> return a string containing RSA␣ ˓ →PSS signature nSaltLength indicates the length of PSS salt to use. If ommited, then maximum␣ ˓ →salt length is used. nSaltLength can have the special values -1 and -2: -1 indicates that salt length␣ ˓ →is equal to hash size and -2 indicates that maximum salt length is used. Example: /* sign a document using RSA-PSS with SHA256 and maximal salt length */ try /* read Alice private key */ rsaKeyPEM = Read("alice_private_key.pem") rsaKey = rsa_import_pem(rsaKeyPEM) /* hash file content */ ctx = SHA256Init() cFileContent = Read ("document.txt") SHA256Update(ctx, cFileContent) digest = SHA256Final(ctx) /* perform PSS signing */ cSignature = rsa_signhash_pss(rsaKey,digest) /* store the signature */ Write("document.txt.sig", cSignature) catch See "Unexpected error occured: " + cCatchError + nl done 30.30. rsa_signhash_pss() Function 223
268.
Ring Documentation, Release1.21.2 30.31 rsa_verify_pss() Function We can verify the RSA-PSS signature of data using the function rsa_verify_pss() The input data will be first hashed using the specified hash algorithm then RSA PSS verification will be applied to the computed hash value and the given signature to check if they match or not. The size of signature must be equal to the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the size of input signature that can be verified using RSA-PSS is 256 bytes. RSA-PSS verification needs only the public part of an RSA key, so rsa_verify_pss can be used with both RSA private key and RSA public key. Syntax: rsa_verify_pss(pRsaKey,cData,cSignature,nHashAlgorithm[,nSaltLength]) ---> returns 1 if␣ ˓ →signature is valid and 0 otherwise nHashAlgorithm indicates the hash algorithm to use for hashing and PSS padding. nSaltLength indicates the length of PSS salt to use. If ommited, then maximum␣ ˓ →salt length is used. nSaltLength can have the special values -1 and -2: -1 indicates that salt length␣ ˓ →is equal to hash size and -2 indicates that maximum salt length is used. Possible values for nHashAlgorithm argument are: - $OSSL_HASH_MD5 which is equal to 0 - $OSSL_HASH_SHA1 which is equal to 1 - $OSSL_HASH_SHA256 which is equal to 2 - $OSSL_HASH_SHA384 which is equal to 3 - $OSSL_HASH_SHA512 which is equal to 4 Example: /* verify a document signature using RSA-PSS with SHA256 and maximal salt length */ try /* read Alice public key */ rsaPublicKeyPEM = Read("alice_public_key.pem") rsaPublicKey = rsa_import_pem(rsaPublicKeyPEM) /* read file content */ cFileContent = Read ("document.txt") /* read file signature */ cSignature = Read ("document.txt.sig") /* perform PSS verification */ if rsa_verify_pss(rsaPublicKey,cFileContent,cSignature,$OSSL_HASH_SHA256) See "file signature is valid" + nl else See "file signature is INVALID" + nl ok /* store the signature */ Write("document.txt.sig", cSignature) catch See "Unexpected error occured: " + cCatchError + nl done 30.31. rsa_verify_pss() Function 224
269.
Ring Documentation, Release1.21.2 30.32 rsa_verifyhash_pss() Function We can verify the RSA-PSS signature of a hash value using the function rsa_verifyhash_pss() This function infers the hash algorithm from hash value size. The size of signature must be equal to the length of the RSA key modulus in bytes. For example, for 2048-bit RSA key, the length of modulus is 2048/8 = 256 bytes and so the size of input signature that can be verified using RSA-PSS is 256 bytes. RSA-PSS verification needs only the public part of an RSA key, so rsa_verifyhash_pss can be used with both RSA private key and RSA public key. Syntax: rsa_verifyhash_pss(pRsaKey,cHashValue,cSignature[,nSaltLength]) ---> returns 1 if␣ ˓ →signature is valid and 0 otherwise nSaltLength indicates the length of PSS salt to use. If ommited, then maximum␣ ˓ →salt length is used. nSaltLength can have the special values -1 and -2: -1 indicates that salt length␣ ˓ →is equal to hash size and -2 indicates that maximum salt length is used. Example: /* verify a document signature using RSA-PSS with SHA256 and maximal salt length */ try /* read Alice public key */ rsaPublicKeyPEM = Read("alice_public_key.pem") rsaPublicKey = rsa_import_pem(rsaPublicKeyPEM) /* hash file content */ ctx = SHA256Init() cFileContent = Read ("document.txt") SHA256Update(ctx, cFileContent) digest = SHA256Final(ctx) /* read file signature */ cSignature = Read ("document.txt.sig") /* perform PSS verification */ if rsa_verifyhash_pss(rsaPublicKey,digest,cSignature) See "file signature is valid" + nl else See "file signature is INVALID" + nl ok catch See "Unexpected error occured: " + cCatchError + nl done 30.32. rsa_verifyhash_pss() Function 225
270.
Ring Documentation, Release1.21.2 30.33 openssl_versiontext() Function We can get the full version text of the OpenSSL library using the function openssl_versiontext(). The returned string is equal to the value returned by the command “openssl.exe version” Syntax: openssl_versiontext() ---> return a string containing the full version text of OpenSSL␣ ˓ →library Example: /* Display the version of OpenSSL library used by Ring */ See "Ring is using " + openssl_versionText() + nl 30.34 openssl_version() Function We can get the version numbers (Major,Minor,Fix) of the OpenSSL library using the function openssl_version(). The returned list contains three items corresponding to the the three part of the version. For example, for OpenSSL 1.0.2, openssl_version() returns the list [1,0,2] Syntax: openssl_version() ---> return a list containing the version numbers of the OpenSSL␣ ˓ →library First list item holds the version major number Second list item holds the version minor number Third list item holds the version fix number Example: /* Display the version number of OpenSSL library used by Ring */ ver = openssl_version() OpenSSLVersionMajor = ver[1] OpenSSLVersionMinor = ver[2] OpenSSLVersionFix = ver[3] See "Ring is using OpenSSL version " + OpenSSLVersionMajor + "." + OpenSSLVersionMinor + ˓ →"." + OpenSSLVersionFix + nl 30.35 Large Files Hash These functions compute the hash of large files/data without the need to load all of the content in a single string. md5init() -> MD5_CTX md5update (MD5_CTX, string) -> 1 for success or 0 for failure md5final (MD5_CTX) -> string sha1init() -> SHA_CTX (continues on next page) 30.33. openssl_versiontext() Function 226
271.
Ring Documentation, Release1.21.2 (continued from previous page) sha1update (SHA_CTX, string) -> 1 for success or 0 for failure sha1final (SHA_CTX) -> string sha224init() -> SHA224_CTX sha224update (SHA224_CTX, string) -> 1 for success or 0 for failure sha224final (SHA224_CTX) -> string sha256init() -> SHA256_CTX sha256update (SHA256_CTX, string) -> 1 for success or 0 for failure sha256final (SHA256_CTX) -> string sha384init() -> SHA384_CTX sha384update (SHA384_CTX, string) -> 1 for success or 0 for failure sha384final (SHA384_CTX) -> string sha512init() -> SHA512_CTX sha512update (SHA512_CTX, string) -> 1 for success or 0 for failure sha512final (SHA512_CTX) -> string 30.36 Download() Function Syntax: Download(cURL) ---> String contains the server response Example: cStr= download("https://ring-lang.github.io/") see cStr write("download.txt",cStr) 30.37 SendEmail() Function Syntax: SendEmail(cSMTPServer,cEmail,cPassword,cSender,cReceiver,cCC,cTitle,cContent) Example: See "Send email..." + nl sendemail("smtp://smtp.gmail.com:587", "email@gmail.com", "password", "email@gmail.com", "somebody@yahoo.com", "somebodyelse@yahoo.com", "Sending email from Ring", "Hello (continues on next page) 30.36. Download() Function 227
272.
Ring Documentation, Release1.21.2 (continued from previous page) How are you? Are you fine? Thank you! Greetings, Mahmoud") see "Done.." + nl 30.37. SendEmail() Function 228
273.
CHAPTER THIRTYONE OBJECT ORIENTED PROGRAMMING(OOP) In this chapter we are going to learn how to use the Object-Oriented programming paradigm inside the Ring program- ming language. We will learn about • Classes and Objects • Access Objects Using Braces • Composition • Setter and Getter • Private Attributes and Methods • Operator Overloading • Inheritance • Dynamic Attributes • Packages • Printing Objects • Find() and List of Objects • Sort() and List of Objects • Using Self.Attribute and Self.Method() • Using This.Attribute and This.Method() • Command: New From 31.1 Classes and Objects We can define new classes using the next syntax Syntax: Class <Class Name> [From|<|: <Parent Class Name>] [Attributes] [Methods] [Private [Attributes] [Methods] ] 229
274.
Ring Documentation, Release1.21.2 And we can create objects using the next syntax Syntax: New <Object Name> [ (init method parameters) ] | [ { access object data and methods } ] ---> Object Example: New point { x=10 y=20 z=30 print() } Class Point x y z func print see x + nl + y + nl + z + nl Note: We can use { } to access object data and methods. Tip: we can declare the class attributes directly after the class name. Output: 10 20 30 We can rewrite the same program in another style New point # create new object using the point class { # access the new object attributes and methods x = 10 # set the x attribute to 10 y = 20 # set the y attribute to 20 z = 30 # set the z attribute to 30 print() # call the print method } # end of object access Class Point # define the Point class x y z # the class contains three attributes x, y & z func print # define the print method see x + nl + # print the x attribute y + nl + # print the y attribute z + nl # print the z attribute Also we can write the same program in another way P1 = New Point P1.x = 10 P1.y = 20 P1.z = 30 P1.Print() Class Point x y z func print see x + nl + y + nl + z + nl Note: we can use the dot operator after the object name to access object members. 31.1. Classes and Objects 230
275.
Ring Documentation, Release1.21.2 Also we can write the same program in another way new point { print() } Class Point x = 10 y = 20 z = 30 func print see x + nl + y + nl + z + nl Note: we can set the default values for the class attributes when we declare them. Also we can write the same program in another way new point(10,20,30) Class Point x y z func init p1,p2,p3 x=p1 y=p2 z=p3 print() func print see x + nl + y + nl + z + nl Note: we can call the init method directly using () when we create new objects Also we can write the same program in another way new point( [ :x = 10 , :y = 20 , :z = 30 ] ) Class Point x y z func init aPara x = aPara[:x] y = aPara[:y] z = aPara[:z] print() func print see x + nl + y + nl + z + nl Tip: using Hash for passing method parameters enable us to create optional parameters and change the order of parameters when adding them to the Hash. 31.2 Access Objects Using Braces We can access the object at any time using braces { } Inside the braces we can use the object attributes and methods directly This can be done when we create the object using the New keyword or at any time using the next syntax ObjectName { access object data and methods } Example: See "Creating the Object" + nl o1 = new Point See "Using the Object" + nl o1 { x=5 y=15 z=25 (continues on next page) 31.2. Access Objects Using Braces 231
276.
Ring Documentation, Release1.21.2 (continued from previous page) print() } Class Point x y z func print see x + nl + y + nl + z We can use braces to access objects when we call functions or methods Example: o1 = new Point print( o1 { x=10 y=20 z=30 } ) func print object see object.x + nl + object.y + nl + object.z Class Point x y z We can mix between using braces and the dot operator to access the object in the same expression. Example: o1 = new Point O1 { x=10 y=20 z=30 }.print() Class Point x y z func print see x + nl + y + nl + z 31.3 Composition The object may contains other objects as attributes. Using braces to access objects can be nested. Example: R1 = New Rectangle { Name = "Rectangle 1" P1 { X = 10 Y = 20 } P2 { X = 200 (continues on next page) 31.3. Composition 232
277.
Ring Documentation, Release1.21.2 (continued from previous page) Y = 300 } Color = "Blue" } see "Name : " + R1.Name + nl + "Color: " + R1.Color + nl + "P1 : (" + R1.P1.X + "," + R1.P1.Y + ")" + nl + "P2 : (" + R1.P2.X + "," + R1.P2.Y + ")" Class Rectangle name color p1 = new Point p2 = new Point Class Point x y Output: Name : Rectangle 1 Color: Blue P1 : (10,20) P2 : (200,300) 31.4 Setter and Getter We can define methods to be used when we set and get object attributes. Syntax: Class ClassName AttributeName ... Func SetAttributeName ... Func GetAttributeName ... Example: o1 = new person o1.name = "Mahmoud" see o1.name + nl o1 { name = "Ahmed" see name } (continues on next page) 31.4. Setter and Getter 233
278.
Ring Documentation, Release1.21.2 (continued from previous page) Class Person name family = "Fayed" func setname value see "Message from SetName() Function!" + nl name = value + " " + family func getname see "Message from GetName() Function!" + nl return "Mr. " + name Output: Message from SetName() Function! Message from GetName() Function! Mr. Mahmoud Fayed Message from SetName() Function! Message from GetName() Function! Mr. Ahmed Fayed 31.5 Private Attributes and Methods We can define private attributes and methods after the keyword private inside the class body Note: A subclass could access private attributes/methods defined in the parent class. Tip: Once we create an object, no other object (even from the same class) could access it’s private attributes/methods directly. Example: o1 = new person { name = "Test" age = 20 print() o1.printsalary() } try see o1.salary catch see cCatchError + nl done try o1.increasesalary(1000) catch (continues on next page) 31.5. Private Attributes and Methods 234
279.
Ring Documentation, Release1.21.2 (continued from previous page) see cCatchError + nl done Class Person name age func print see "Name : " + name + nl + "Age : " + age + nl func printsalary see "Salary : " + salary + nl private salary = 15000 func increasesalary x salary += x Output: Name : Test Age : 20 Salary : 15000 Error (R27) : Using private attribute from outside the class : salary Error (R26) : Calling private method from outside the class : increasesalary 31.6 Operator Overloading We can add the operator method to our class to enable using operators with the class objects. Syntax: Class ClassName ... Func operator cOperator,Para ... The function operator takes two parameters, the first represent the operator and the second represent the second param- eter after the operator. Example: o1 = new point { x = 10 y = 10 print("P1 : ") } o2 = new point { x = 20 y = 40 print("P2 : ") } (continues on next page) 31.6. Operator Overloading 235
280.
Ring Documentation, Release1.21.2 (continued from previous page) o3 = o1 + o2 o3.print("P1+P2 : ") class point x y func operator cOperator,Para result = new point switch cOperator on "+" result.x = x + Para.x result.y = y + Para.y on "-" result.x = x - Para.x result.y = y - Para.y off return result func print cPoint see cPoint + "X : " + x + " Y : " + y + nl Output: P1 : X : 10 Y : 10 P2 : X : 20 Y : 40 P1+P2 : X : 30 Y : 50 The next example from the List class in the stdlib.ring Func operator cOperator,Para result = new list switch cOperator on "+" if isobject(para) for t in Para.vValue vValue + t next but islist(para) for t in Para vValue + t next ok on "len" return len( vValue ) on "[]" return &vValue[para] off return result The “len” operator is used with (for in) control structure. The “[]” operator is used when we try to access the list items, In this case we use the & operator to return the item values like strings and numbers by reference, so we can update it when we access the items. Another Example 31.6. Operator Overloading 236
281.
Ring Documentation, Release1.21.2 func main See "----1"+nl a1 = new BigNumber( "123" ) a2 = new BigNumber( "456" ) a3 = new BigNumber( "789" ) See nl+"----2"+nl a1.print() a2.print() a3.print() See nl+"----3"+nl a2 = a1 + "45" See nl+"----4"+nl a2.print() See nl+"----5"+nl a3 = a1 + a2 See nl+"----6"+nl a3.print() See nl+"----7"+nl ###================================== Func FuncAdd( num1, num2) Sum = 0 + num1 + num2 ### Para.aData isNumber Sum = "" +Sum ### Para.adata isString return Sum ### return to Class ###=================================== class BigNumber ### Variables aData = "468" ### Functions INIT default values func init aPara ? "INIT aPara: " ? aPara if isString(aPara) aData = aPara else aData = "" + aPara ok ### Other Functions func operator cOperator, Para whatType = Type(Para) ? nl+"WhatType-PARA: "+ whatType ? Para ? nl+"Operator: " ? cOperator ? nl+"PARA: " ? Para ? " ______" ? nl if whatType = "STRING" dataInfo = Para ? "dataInfo String: " ? dataInfo but whatType = "NUMBER" datinfo = "" + Para ? "dataInfo Number: " ? dataInfo else whatType = "OBJECT" (continues on next page) 31.6. Operator Overloading 237
282.
Ring Documentation, Release1.21.2 (continued from previous page) dataInfo = "" + para.aData ? "dataInfo OBJECT: " ? dataInfo ok ? "dataInfo USING: " ? dataInfo ### Para.aData does NOT exist on first pass ( Object with member) ### Result isObject when assigned "self" result = self switch cOperator on "+" answer = FuncAdd( aData, dataInfo ) ? nl+"AnswerString - FunAdd aData, dataInfo: " ? answer ### result = self, is Object, populate Object with aData member result.aData = answer off ### Result = Self is Object return result func print ? nl+"ClassPrint aData: " ? aData Starting from Ring 1.21, We support that the value could come first before the object and the operator() method will be called but the letter ‘r’ will comes before the operator (i.e. r+ instead of +) Example: ? f(2) mylist = new List([1,2,3]) f(mylist).print() func f x return 2+x*x # Here 2 comes before x and x could be an object class List aList = [] func init vValue aList = vValue func operator cOperator,vValue if cOperator = "r+" cOperator = "+" ok switch cOperator on "+" if isNumber(vValue) { for t in aList t += vValue next (continues on next page) 31.6. Operator Overloading 238
283.
Ring Documentation, Release1.21.2 (continued from previous page) but isObject(vValue) for t = 1 to len(aList) aList[t] += vValue[t] next ok on "*" if isNumber(vValue) { for t in aList t *= vValue next but isObject(vValue) for t = 1 to len(aList) aList[t] *= vValue[t] next ok on "[]" return aList[vValue] on "len" return len(aList) off return self func print ? aList Output: 6 3 6 11 Note: the numbers(3,6,11) are the result of applying the function f to the list items [1,2,3] 31.7 Inheritance We can create class from another class in the class definition using the keyword from. Syntax: Class <Class Name> [From <Parent Class Name>] We can call a method in the parent class from the child class using Super. Note: Super provide access to the methods only (No access to the attributes). Tip: Using ParentClassName(self) we can know the parent class name or get an empty string if no parent class exists. 31.7. Inheritance 239
284.
Ring Documentation, Release1.21.2 Syntax: func methodname ... super.methodname() ... Example: Func main e1 = new Employee { Name = "test" age = 20 job = "programmer" salary = 20000000 print() } Class Human Name Age func print see "Name : " + name + nl + "Age : " + age + nl Class Employee from Human Job Salary func print super.print() see "Job : " + job + nl + "Salary : " + salary + nl Output: Name : test Age : 20 Job : programmer Salary : 20000000 31.8 Dynamic Attributes We can write instructions after the class name to be executed when we create new objects Example: o1 = new dynamicClass see o1.var5 + nl # output 5 Class DynamicClass for x = 1 to 10 cStr = "var" + x + " = " + x eval(cStr) next 31.8. Dynamic Attributes 240
285.
Ring Documentation, Release1.21.2 Tip: in the previous example var1, var2, ..., var10 will be defined as attributes. Tip: The problem with the previous example is that x and cStr will be defined as attributes too! Note: we can write class definitions inside a string then using eval() we can execute the string to define the classes 31.9 Packages We can create a package (a group of classes under a common name) using the next syntax package PackageName Class Class1 ... Class Class2 ... Class Class3 ... ... Example o1 = new System.output.console o1.print("Hello World") Package System.Output Class Console Func Print cText see cText + nl Note: we can use the dot operator as part of the package name Instead of typing the long name PackageName.ClassName we can use the import command When we import a package, we can use any class inside this package directly. Example import system.output o1 = new console { print("Hello World") } Package System.Output Class Console Func Print cText see cText + nl 31.9. Packages 241
286.
Ring Documentation, Release1.21.2 31.10 Printing Objects We can print the object state (attributes and values) using the see command. Example: see new point { x=10 y=20 z=30 } class point x y z Output: x: 10.000000 y: 20.000000 z: 30.000000 31.11 Find() and List of Objects We can use the find() function to search inside a list of objects. Syntax: Find(List,ItemValue,nColumn,cAttribute) ---> Item Index Example: myList1 = [new Company {position=3 name="Mahmoud" symbol="MHD"}, new Company {position=2 name="Bert" symbol="BRT"}, new Company {position=1 name="Ring" symbol="RNG"} ] see find(mylist1,"Bert",1,"name") + nl see find(mylist1,"Ring",1,"name") + nl see find(mylist1,"Mahmoud",1,"name") + nl see find(mylist1,"RNG",1,"symbol") + nl see find(mylist1,"MHD",1,"symbol") + nl see find(mylist1,"BRT",1,"symbol") + nl see find(mylist1,3,1,"position") + nl see find(mylist1,1,1,"position") + nl see "Other" + nl see find(mylist1,"test",1,"name") + nl see find(mylist1,"test",0,"name") + nl see find(mylist1,"test",5,"name") + nl class company position name symbol Output: 2 3 1 3 1 (continues on next page) 31.10. Printing Objects 242
287.
Ring Documentation, Release1.21.2 (continued from previous page) 2 1 3 Other 0 0 0 31.12 Sort() and List of Objects We can sort a list of objects based on an object attribute using the Sort() function. Syntax: Sort(List,nColumn,cAttribute) ---> Sorted List based on Object Attribute Example: myList1 = [ new Company {position=3 name="Mahmoud" symbol="MHD"}, new Company {position=2 name="Bert" symbol="BRT"}, new Company {position=8 name="Charlie" symbol="CHR"}, new Company {position=6 name="Easy" symbol="FEAS"}, new Company {position=7 name="Fox" symbol="EFOX"}, new Company {position=5 name="Dog" symbol="GDOG"}, new Company {position=4 name="George" symbol="DGRG"}, new Company {position=1 name="Ring" symbol="RNG"} ] see sort(mylist1,1,"name") see copy("*",70) + nl see sort(mylist1,1,"symbol") see copy("*",70) + nl see sort(mylist1,1,"position") class company position name symbol Output: position: 2.000000 name: Bert symbol: BRT position: 8.000000 name: Charlie symbol: CHR position: 5.000000 name: Dog symbol: GDOG position: 6.000000 name: Easy symbol: FEAS (continues on next page) 31.12. Sort() and List of Objects 243
288.
Ring Documentation, Release1.21.2 (continued from previous page) position: 7.000000 name: Fox symbol: EFOX position: 4.000000 name: George symbol: DGRG position: 3.000000 name: Mahmoud symbol: MHD position: 1.000000 name: Ring symbol: RNG ********************************************************************** position: 2.000000 name: Bert symbol: BRT position: 8.000000 name: Charlie symbol: CHR position: 4.000000 name: George symbol: DGRG position: 7.000000 name: Fox symbol: EFOX position: 6.000000 name: Easy symbol: FEAS position: 5.000000 name: Dog symbol: GDOG position: 3.000000 name: Mahmoud symbol: MHD position: 1.000000 name: Ring symbol: RNG ********************************************************************** position: 1.000000 name: Ring symbol: RNG position: 2.000000 name: Bert symbol: BRT position: 3.000000 name: Mahmoud symbol: MHD position: 4.000000 name: George symbol: DGRG position: 5.000000 name: Dog (continues on next page) 31.12. Sort() and List of Objects 244
289.
Ring Documentation, Release1.21.2 (continued from previous page) symbol: GDOG position: 6.000000 name: Easy symbol: FEAS position: 7.000000 name: Fox symbol: EFOX position: 8.000000 name: Charlie symbol: CHR 31.13 Using Self.Attribute and Self.Method() Inside the class region (After the class name and before any method) and the class methods we can use self.attribute and self.method() Class Point self.x = 10 self.y = 20 self.z = 30 func print see self.x + nl + self.y + nl + self.z + nl Note: using self.attribute in the class region to define the class attribute protect the class attributes from conflict with global variables. Tip: if we defined the class attributes without using self or this and there are a global variable with the same name it will be used and the attribute will not be defined. Check the “Scope Rules” chapter to know about the conflict between the global variable name and the attribute name What this may happens? Because • Because in the class region we can access global variables. • Before defining any variable, Ring try to find the variable and use it if it’s found. Note: Try to avoid the global variables, use the main function and start their names with $ Tip: In large programs protect your classes and define their members using self.attribute Tip: A better solution to avoid using self and this in the class region is to use different global scope and the load package command 31.13. Using Self.Attribute and Self.Method() 245
290.
Ring Documentation, Release1.21.2 31.14 Using This.Attribute and This.Method() Inside class methods we have access to the object scope directly. we don’t need to use Self.attribute or Self.method to read/write attribute and call methods. But we can use braces {} while we are inside methods to access another object, In this case the current object scope will be changed while we are inside the brace. How we can get access to our class attributes and methods while we are inside braces? This can be done using This.Attribute and This.Method() Example: new point class point x=10 y=20 z=30 print() func print new UI { display(this.x,this.y,this.z) } Class UI func display x,y,z see x + nl + y + nl + z + nl 31.15 Using This in the class region as Self The class region is the region that comes after the class name and before any method. We can use This in the class region as Self. Example: func main o1 = new program { test() } ? o1 class program this.name = "My Application" this.version = "1.0" ? name ? version func test ? "Name = " + name ? "Version = " + version Output 31.14. Using This.Attribute and This.Method() 246
291.
Ring Documentation, Release1.21.2 My Application 1.0 Name = My Application Version = 1.0 name: My Application version: 1.0 Note: When we use braces to change the current active object, Using This we can still point to the class. Tip: The difference between This and Self is that Self point to the current active object that we can change using braces. Remember that in most cases we don’t need to use This or Self in the class region We can write class program name version Or class program name="My Application" version="1.0" Note: We use This or Self in the class region just to avoid conflict with global variables that are defined with the same name. 31.16 Default value for object attributes The default value for object attributes is NULL In Ring, the NULL value is just an empty string or a string that contains “NULL” We can check for NULL values using the isNULL() function Example: oProgram = new Program ? oProgram.name ? oProgram.version ? isNULL(oProgram.name) ? isNULL(oProgram.version) oProgram { name="My Application" version="1.0" } ? isNULL(oProgram.name) ? isNULL(oProgram.version) ? oProgram class program name version 31.16. Default value for object attributes 247
292.
Ring Documentation, Release1.21.2 Output: NULL NULL 1 1 0 0 name: My Application version: 1.0 31.17 Command: New From Using (new) we can create a new object from a specific class Using (new from) we provide a variable which contains the class name Example: cClassName = "myclass2" myobj = new from cClassName cClassName = "myclass" myobj = new from cClassName class myclass ? :hello class myclass2 ? :wow Output: wow hello 31.18 Using Objects During Definition Starting from Ring 1.19, The language provides better support for using objects during definition where we can mix between this feature and other features like operator overloading without missing the output Example: 31.17. Command: New From 248
293.
Ring Documentation, Release1.21.2 • The new point object will be stored directly in myVar during definition • We can pass myVar as parameter to the print() method • Using + 1 will call the operator() method • The operator() method output will be stored in myVar This means that the Assignment operation is executed TWO TIMES! The first Assignment is executed to support (Using objects during definition) where myVar is an object contains the new point while in the second time, the Assignment is executed to support storing the Operator Overloading output. Note: RingQt samples uses this feature to quickly pass the parent window object to the other widgets. 31.18. Using Objects During Definition 249
294.
CHAPTER THIRTYTWO FUNCTIONAL PROGRAMMING In previouschapters we learned about Functions and Recursion. In this chapter we are going to learn about more Functional Programming (FP) concepts like • Pure Functions • First-class functions • Higher-order functions • Anonymous and nested functions. • Equality of functions 32.1 Pure Functions We can create pure functions (functions that doesn’t change the state) by the help of the assignment operator to copy variables (Lists & Objects) by value to create new variables instead of working on the original data that are passed to the function by reference. Example: Func Main aList = [1,2,3,4,5] aList2 = square(aList) see "aList" + nl see aList see "aList2" + nl see aList2 Func Square aPara a1 = aPara # copy the list for x in a1 x *= x next return a1 # return new list Output: aList 1 2 (continues on next page) 250
295.
Ring Documentation, Release1.21.2 (continued from previous page) 3 4 5 aList2 1 4 9 16 25 32.2 First-class Functions Functions inside the Ring programming language are first-class citizens, you can pass functions as parameters, return them as value or store them in variables. We can pass/return the function by typing the function name as literal like “FunctionName” or :FunctionName for example. We can pass/return functions using the variable that contains the function name. We can call function from variables contains the function name using the Call command Syntax: Call Variable([Parameters]) Example: Func Main see "before test2()" + nl f = Test2(:Test) see "after test2()" + nl call f() Func Test see "Message from test!" + nl Func Test2 f1 call f1() See "Message from test2!" + nl return f1 Output: before test2() Message from test! Message from test2! after test2() Message from test! 32.2. First-class Functions 251
296.
Ring Documentation, Release1.21.2 32.3 Higher-order Functions Higher-order functions are the functions that takes other functions as parameters. Example: Func Main times(5,:test) Func Test see "Message from the test function!" + nl Func Times nCount,F for x = 1 to nCount Call F() next Output: Message from the test function! Message from the test function! Message from the test function! Message from the test function! Message from the test function! 32.4 Anonymous and Nested Functions Anonymous Functions are functions without names that can be passed as parameters to other functions or stored in variables. Syntax: Func [Parameters] { [statements] } Example: test( func x,y { see "hello" + nl see "Sum : " + (x+y) + nl } ) new great { f1() } times(3, func { see "hello world" + nl } ) func test x call x(3,3) see "wow!" + nl func times n,x for t=1 to n (continues on next page) 32.3. Higher-order Functions 252
297.
Ring Documentation, Release1.21.2 (continued from previous page) call x() next Class great func f1 f2( func { see "Message from f1" + nl } ) func f2 x call x() Output: hello Sum : 6 wow! Message from f1 hello world hello world hello world Example: Func Main aList = [1,2,3,4] Map (aList , func x { return x*x } ) see aList aList = [4,9,14,25] Map(aList, :myfilter ) see aList aList = [11,12,13,14] Map (aList , func x { if x%2=0 return "even" else return "odd" ok }) see aList Func myfilter x if x = 9 return "True" else return "False" ok Func Map aList,cFunc for x in aList x = call cFunc(x) next 32.4. Anonymous and Nested Functions 253
298.
Ring Documentation, Release1.21.2 Output: 1 4 9 16 False True False False odd even odd even 32.5 Equality of functions We can test if function = function or not using the ‘=’ or ‘!=’ operators Example: f1 = func { see "hello" + nl } f2 = func { see "how are you?" + nl } f3 = f1 call f1() call f2() call f3() see (f1 = f2) + nl see (f2 = f3) + nl see (f1 = f3) + nl Output: hello how are you? hello 0 0 1 32.5. Equality of functions 254
299.
CHAPTER THIRTYTHREE REFLECTION AND META-PROGRAMMING Sincethe Ring programming language is a dynamic language, we can get answers about the program code and we can modify our code during the runtime. In this chapter we will learn about this and the available functions to use. • locals() • globals() • functions() • cfunctions() • islocal() • isglobal() • isfunction() • iscfunction() • packages() • ispackage() • classes() • isclass() • packageclasses() • ispackageclass() • classname() • parentclassname() • objectid() • isobject() • attributes() • methods() • isattribute() • isprivateattribute() • ismethod() • isprivatemethod() • addattribute() 255
300.
Ring Documentation, Release1.21.2 • addmethod() • getattribute() • setattribute() • mergemethods() • packagename() • importpackage() • nothing() • optionalfunc() 33.1 locals() Function We can get a list of variables names in the current scope using the locals() function. Syntax: locals() --> a list contains the variables names in the current scope Example: test("hello") func test cMsg see cMsg + nl x = 10 y = 20 z = 30 see locals() Output: hello cmsg x y z 33.2 globals() Function We can get a list of variables names in the global scope using the globals() function. Syntax: globals() --> a list contains variables names in the global scope Example: 33.1. locals() Function 256
301.
Ring Documentation, Release1.21.2 x=10 y=20 z=30 test() func test see "message from test()" + nl + "Global Variables:" + nl see globals() Output: message from test() Global Variables: x y z 33.3 functions() Function We can get a list of functions names written in the Ring language using the functions() function. Syntax: functions() --> a list contains functions names Example: see functions() func f1 see "f1" + nl func f2 see "f2" + nl func f3 see "f3" + nl Output: f1 f2 f3 33.3. functions() Function 257
302.
Ring Documentation, Release1.21.2 33.4 cfunctions() Function We can get a list of functions names written in the C language using the cfunctions() function. Syntax: cfunctions() --> a list contains functions names Example: aList = cfunctions() See "Count : " + len(aList) + nl for x in aList see x + "()" + nl next Output: Count : 255 len() add() del() get() clock() ... Note: The complete list is removed from the previous output. 33.5 islocal() Function We can check if a variable is defined in the local scope or not using the islocal() function. Syntax: islocal(cVariableName) --> returns 1 if the variable is defined in the local scope returns 0 if the variable is not defined in the local scope Example: test() func test x=10 y=20 see islocal("x") + nl + islocal("y") + nl + islocal("z") + nl Output: 1 1 0 33.4. cfunctions() Function 258
303.
Ring Documentation, Release1.21.2 33.6 isglobal() Function We can check if a variable is defined in the global scope or not using the isglobal() function. Syntax: isglobal(cVariableName) --> returns 1 if the variable is defined in the global scope returns 0 if the variable is not defined in the global scope Example: x=10 y=20 test() func test see isglobal("x") + nl + isglobal("y") + nl + isglobal("z") + nl Output: 1 1 0 33.7 isfunction() Function We can check if a Ring function is defined or not using the isfunction() function. Syntax: isfunction(cFunctionName) --> returns 1 if the Ring function is defined returns 0 if the Ring function is not defined Example: see isfunction("f1") + nl + isfunction("f2") + nl + isfunction("f3") + nl func f1 see "message from f1()" + nl func f2 see "message from f2()" + nl Output: 1 1 0 33.6. isglobal() Function 259
304.
Ring Documentation, Release1.21.2 33.8 iscfunction() Function We can check if a C function is defined or not using the iscfunction() function. Syntax: iscfunction(cFunctionName) --> returns 1 if the C function is defined returns 0 if the C function is not defined Example: see iscfunction("len") + nl + iscfunction("add") + nl + iscfunction("test") + nl Output: 1 1 0 33.9 packages() Function We can get a list of packages names using the packages() function. Syntax: packages() --> a list contains packages names Example: See packages() Package Package1 Class class1 Func f1 Package Package2 Class class1 Func f1 Package Package3 Class class1 Func f1 Package Package4 Class class1 Func f1 Output: 33.8. iscfunction() Function 260
305.
Ring Documentation, Release1.21.2 package1 package2 package3 package4 33.10 ispackage() Function We can check if a package is defined or not using the ispackage() function. Syntax: ispackage(cPackageName) --> returns 1 if the Package is defined returns 0 if the Package is not defined Example: See ispackage("package1") + nl + ispackage("package4") + nl + ispackage("package5") + nl + ispackage("package3") + nl Package Package1 Class class1 Func f1 Package Package2 Class class1 Func f1 Package Package3 Class class1 Func f1 Package Package4 Class class1 Func f1 Output: 1 1 0 1 33.10. ispackage() Function 261
306.
Ring Documentation, Release1.21.2 33.11 classes() Function We can get a list of classes names using the classes() function. Syntax: classes() --> a list contains classes names Example: See classes() Class class1 Func f1 Class class2 Func f1 Class class3 Func f1 Output: class1 class2 class3 33.12 isclass() Function We can check if a class is defined or not using the isclass() function. Syntax: isclass(cClassName) --> returns 1 if the Class is defined returns 0 if the Class is not defined Example: see isclass("class4") + nl + isclass("class3") + nl + isclass("class2") + nl Class class1 func f1 class class2 func f1 class class3 func f1 Output: 33.11. classes() Function 262
307.
Ring Documentation, Release1.21.2 0 1 1 33.13 packageclasses() Function We can get a list of classes names inside a package using the packageclasses() function. Syntax: packageclasses(cPackageName) --> a list contains classes names inside the package Example: see "classes in Package1" + nl see packageclasses("Package1") see "classes in Package2" + nl see packageclasses("Package2") Package Package1 Class class1 Func f1 Package Package2 Class class1 Func f1 Class class2 Func f1 Class class3 func f1 Output: classes in Package1 class1 classes in Package2 class1 class2 class3 33.14 ispackageclass() Function We can check if a class is defined inside package or not using the ispackageclass() function. Syntax: ispackageclass(cPackageName,cClassName) --> returns 1 if the Class is defined returns 0 if the Class is not defined Example: 33.13. packageclasses() Function 263
308.
Ring Documentation, Release1.21.2 see ispackageclass("package1","class1") + nl + ispackageclass("package1","class2") + nl + ispackageclass("package2","class1") + nl + ispackageclass("package2","class2") + nl Package Package1 Class class1 Func f1 Package Package2 Class class1 Func f1 Class class2 Func f1 Class class3 func f1 Output: 1 0 1 1 33.15 classname() Function We can know the class name of an object using the classname() function Syntax: classname(object) --> Returns the object class name Example: o1 = new point o2 = new rect see classname(o1) + nl # print point see classname(o2) + nl # print rect class point class rect 33.15. classname() Function 264
309.
Ring Documentation, Release1.21.2 33.16 parentclassname() Function We can know the parent class name of an object using the parentclassname() function Syntax: parentclassname(object) --> Returns the parent class name of the object class Example: new Child { test() } class Parent class Child from Parent func test ? "Parent: " + parentClassName(self) Output: Parent: parent 33.17 objectid() Function We can know the object id using the objectid() function Syntax: objectid(object) --> Returns the object id Example: o1 = new point see objectid(o1) + nl test(o1) func test v see objectid(v) + nl Class point x y z Output: 021B5808 021B5808 33.16. parentclassname() Function 265
310.
Ring Documentation, Release1.21.2 33.18 isobject() Function We can check the variable to know if it’s an object or not using the isobject() function Syntax: isobject(variable) --> Returns True if it's an object, False if it's not 33.19 attributes() Function We can get the object attributes using the attributes() function Syntax: attributes(object) --> Returns a list contains the object attributes Example: o1 = new point aList = attributes(o1) # we can use see attributes(o1) for t in aList see t next # print xyz Class Point x y z 33.20 methods() Function We can get the object methods using the methods() function Syntax: methods(object) --> Returns a list contains the object methods Example: o1 = new test aList = methods(o1) for x in aList cCode = "o1."+x+"()" eval(cCode) next Class Test func f1 see "hello from f1" + nl func f2 see "hello from f2" + nl func f3 see "hello from f3" + nl func f4 see "hello from f4" + nl Output: 33.18. isobject() Function 266
311.
Ring Documentation, Release1.21.2 hello from f1 hello from f2 hello from f3 hello from f4 33.21 isattribute() Function We can test if the object contains an attribute or not using the isattribute() function Syntax: isattribute(object,cAttributeName) --> Returns True if the object contains the attribute Example: o1 = new point see isattribute(o1,"x") + nl # print 1 see isattribute(o1,"t") + nl # print 0 see isattribute(o1,"y") + nl # print 1 see isattribute(o1,"z") + nl # print 1 class point x y z 33.22 isprivateattribute() Function We can test if the object contains a private attribute or not using the isprivateattribute() function Syntax: isprivateattribute(object,cAttributeName) --> Returns True if the object contains the private attribute Example: o1 = new person see isprivateattribute(o1,"name") + nl + isprivateattribute(o1,"address") + nl + isprivateattribute(o1,"phone") + nl + isprivateattribute(o1,"job") + nl + isprivateattribute(o1,"salary") Class Person name address phone private job salary Output: 33.21. isattribute() Function 267
312.
Ring Documentation, Release1.21.2 0 0 0 1 1 33.23 ismethod() Function We can test if the object class contains a method or not using the ismethod() function Syntax: ismethod(object,cMethodName) --> Returns True if the object class contains the method Example: o1 = new point see ismethod(o1,"print") + nl # print 1 mylist = [] mylist + new point see ismethod(mylist[1],"print") + nl # print 1 class point x y z func print see x + nl + y + nl + z + nl 33.24 isprivatemethod() Function We can test if the object class contains a private method or not using the isprivatemethod() function Syntax: isprivatemethod(object,cMethodName) --> Returns True if the object class contains the private method Example: o1 = new Test see isprivatemethod(o1,"f1") + nl + isprivatemethod(o1,"f2") Class Test func f1 see "message from f1()" + nl private func f2 see "message from f2()" + nl 33.23. ismethod() Function 268
313.
Ring Documentation, Release1.21.2 Output: 0 1 33.25 addattribute() Function We can add an attribute (or a group of attributes) to the object state (not the class) using the addattribute() function Syntax: AddAttribute(object,cAttributeName|aAttributesList) Example(1): see new point {x=10 y=20 z=30} Class Point AddAttribute(self,["x","y","z"]) Example(2): o1 = new point addattribute(o1,"x") addattribute(o1,"y") addattribute(o1,"z") see o1 {x=10 y=20 z=30} class point Output: x: 10.000000 y: 20.000000 z: 30.000000 33.26 addmethod() Function We can add a method to the object class using the addmethod() function This method can be used with any object from the same class. Syntax: AddMethod(Object,cNewMethodName,cMethodName|AnonymousFunction) Example: o1 = new point { x=10 y=20 z=30 } addmethod(o1,"print", func { see x + nl + y + nl + z + nl } ) o1.print() (continues on next page) 33.25. addattribute() Function 269
314.
Ring Documentation, Release1.21.2 (continued from previous page) Class point x y z Output: 10 20 30 Instead of using anonymous function to add new method to the class, we can use the function name Example: o1 = new point { x=10 y=20 z=30 } myfunc = func { see x + nl + y + nl + z + nl } addmethod(o1,"print", myfunc ) addmethod(o1,"display", myfunc ) addmethod(o1,"show", myfunc ) o1.print() o1.display() o1.show() Class point x y z Output: 10 20 30 10 20 30 10 20 30 Since we add the method to the class, any object from that class can use this method Example: o1 = new point { x=10 y=20 z=30 } o2 = new point { x=100 y=200 z=300 } o3 = new point { x=50 y=150 z=250 } addmethod(o1,"print", func { see x + nl + y + nl + z + nl } ) o1.print() o2.print() o3.print() (continues on next page) 33.26. addmethod() Function 270
315.
Ring Documentation, Release1.21.2 (continued from previous page) Class point x y z Output: 10 20 30 100 200 300 50 150 250 33.27 getattribute() function We can get the object attribute value using the getattribute() function Syntax: GetAttribute(oObject,cAttributeName) ---> Attribute Value Example: o1 = new point see getattribute(o1,"name") + nl + getattribute(o1,"x") + nl + getattribute(o1,"y") + nl + getattribute(o1,"z") + nl Class Point x=10 y=20 z=30 name = "3D-Point" Output: 3D-Point 10 20 30 Example: We can Find a Class List Member using GetAttribute() using a function findclass() The Find uses the member name, rather than the column number myList = [new Company {position=3 name="Mahmoud" symbol="MHD"}, new Company {position=2 name="Bert" symbol="BRT"}, new Company {position=1 name="Ring" symbol="RNG"} (continues on next page) 33.27. getattribute() function 271
316.
Ring Documentation, Release1.21.2 (continued from previous page) ] see myList see nl +"=====================" + nl + nl for i = 1 to len(myList) see "Pos: "+ i +" | "+ myList[i].position +" | "+ myList[i].name + " | "+ myList[i].symbol +" | "+ nl next See findclass(myList, "MHD", "symbol") +nl ### Specify Member class name ###--------------------------------------- func findclass classList, cValue, classMember See nl + "FindClass: " +" "+ cValue + nl + nl for i = 1 to len(classList) result = getattribute( classList[i], classMember ) See "Result-Attr: " + i +" "+ result +nl if result = cValue j = i ok next return j ###-------------------------------------- class company position name symbol Output: Pos: 1 | 3 | Mahmoud | MHD | Pos: 2 | 2 | Bert | BRT | Pos: 3 | 1 | Ring | RNG | FindClass: MHD Result-Attr: 1 MHD Result-Attr: 2 BRT Result-Attr: 3 RNG 1 33.27. getattribute() function 272
317.
Ring Documentation, Release1.21.2 33.28 setattribute() function We can set the object attribute value using the setattribute() function Syntax: SetAttribute(oObject,cAttributeName,Value) Example: o1 = new person setattribute(o1,"cName","Mahmoud") setattribute(o1,"nSalary",1000000) setattribute(o1,"aColors",["white","blue","yellow"]) see o1 see o1.aColors Class Person cName nSalary aColors Output: cname: Mahmoud nsalary: 1000000.000000 acolors: List... white blue yellow 33.29 mergemethods() Function We can share methods between classes without inheritance using the MergeMethods() function This function merge class methods to another class. Syntax: MergeMethods(cClassNameDestination,cClassNameSource) Example: mergemethods("count","share") mergemethods("count2","share") o1 = new count { test() } o1 = new count2 { test() } Class Share func one see "one" + nl (continues on next page) 33.28. setattribute() function 273
318.
Ring Documentation, Release1.21.2 (continued from previous page) func two see "two" + nl func three see "three" + nl Class Display Func printline see copy("*",20) + nl Class Count from Display Func test printline() one() two() three() printline() Class Count2 from Display Func test three() two() one() printline() Output: ******************** one two three ******************** three two one ******************** 33.30 packagename() Function We can know the package name of the latest successful import command using the packagename() function Syntax: packagename() --> Returns the package name of the latest successful import Example: load "weblib.ring" import System.web see packagename() # system.web 33.30. packagename() Function 274
319.
Ring Documentation, Release1.21.2 33.31 importpackage() Function Instead of using the import command we can use the importpackage() function This function get the package name through a string or variable This is useful if the package name will be known only during the runtime Syntax: importpackage(cPackageName) Example: importpackage(:mypackage) new myclass { myfunction() } package mypackage class myclass function myfunction ? "Hello, World!" 33.32 Nothing() function This function does nothing and can accept any number/type of parameters. The output will be Zero. Some of the Use Cases (1) Performance measurements, where we can test the performance of calling functions written in C and we can change the number of parameters during tests. (2) In places of code that you want to write a function name, and it’s not defined yet. (3) To disable some feature/code by just changing the function name to nothing without changing the parameters or commenting the code. (4) In small programs, where you want to write a function that you can override from a Test program. 33.33 OptionalFunc() function Using this function we can define functions similar to Nothing() but with a different name. Syntax: OptionalFunc(cFunctionName) Example: File: Question.ring optionalFunc(:reply) ? "I love Programming, What about you?" reply() ? "Ok, Thanks!" 33.31. importpackage() Function 275
320.
Ring Documentation, Release1.21.2 Output: I love Programming, What about you? Ok, Thanks! File: Answer.ring load "Question.ring" func reply ? "Me too!" Output: I love Programming, What about you? Me too! Ok, Thanks! 33.33. OptionalFunc() function 276
321.
CHAPTER THIRTYFOUR DECLARATIVE PROGRAMMING USINGNESTED STRUCTURES In this chapter we are going to learn how to build declarative programming world using nested structures on the top of object oriented. We will learn about • Creating Objects inside Lists • Composition and Returning Objects and Lists by Reference • Executing code after the end of object access • Declarative Programming on the top of Object-Oriented 34.1 Creating Objects inside Lists We can create objects inside lists during list definition. Also we can add objects to the list at any time using the Add() function or the + operator. Example: alist = [new point, new point, new point] # create list contains three objects alist + [1,2,3] # add another item to the list see "Item 4 is a list contains 3 items" + nl see alist[4] add(alist , new point) alist + new point alist[5] { x = 100 y = 200 z = 300 } alist[6] { x = 50 y = 150 z = 250 } see "Object inside item 5" + nl see alist[5] see "Object inside item 6" + nl see alist[6] class point x y z Output: 277
322.
Ring Documentation, Release1.21.2 Item 4 is a list contains 3 items 1 2 3 Object inside item 5 x: 100.000000 y: 200.000000 z: 300.000000 Object inside item 6 x: 50.000000 y: 150.000000 z: 250.000000 34.2 Composition and Returning Objects and Lists by Reference When we use composition and have object as one of the class attributes, when we return that object it will be returned by reference. if the caller used the assignment operator, another copy of the object will be created. The caller can avoid using the assignment operator and use the returned reference directly to access the object. The same is done also if the attribute is a list (not object). Note: Objects and Lists are treated using the same rules. When you pass them to function they are passed by reference, when you return them from functions they are returned by value except if it’s an object attribute where a return by reference will be done. Example: o1 = new Container myobj = o1.addobj() # the assignment will create another copy myobj.x = 100 myobj.y = 200 myobj.z = 300 see o1.aobjs[1] # print the object inside the container see myobj # print the copy Class Container aObjs = [] func addobj aobjs + new point return aobjs[len(aobjs)] # return object by reference Class point x = 10 y = 20 z = 30 Output: 34.2. Composition and Returning Objects and Lists by Reference 278
323.
Ring Documentation, Release1.21.2 x: 10.000000 y: 20.000000 z: 30.000000 x: 100.000000 y: 200.000000 z: 300.000000 Example(2): func main o1 = new screen { content[point()] { x = 100 y = 200 z = 300 } content[point()] { x = 50 y = 150 z = 250 } } see o1.content[1] see o1.content[2] Class Screen content = [] func point content + new point return len(content) Class point x = 10 y = 20 z = 30 Output: x: 100.000000 y: 200.000000 z: 300.000000 x: 50.000000 y: 150.000000 z: 250.000000 Example(3): func main o1 = New Screen { point() { # access the object using reference x = 100 y = 200 z = 300 (continues on next page) 34.2. Composition and Returning Objects and Lists by Reference 279
324.
Ring Documentation, Release1.21.2 (continued from previous page) } point() { # access the object using reference x = 50 y = 150 z = 250 } } see o1.content[1] see o1.content[2] Class Screen content = [] func point content + new point return content[len(content)] # return the object by reference Class point x=10 y=20 z=30 Output: x: 100.000000 y: 200.000000 z: 300.000000 x: 50.000000 y: 150.000000 z: 250.000000 34.3 Executing code after the end of object access We can access an object using { } to use object attributes and methods. if the object contains a method called BraceEnd(), it will be executed before the end of the object access. Example: New Point { See "How are you?" + nl } Class Point x y z func braceend see "I'm fine, Thank you!" + nl Output: How are you? I'm fine, Thank you! 34.3. Executing code after the end of object access 280
325.
Ring Documentation, Release1.21.2 34.4 Declarative Programming on the top of Object-Oriented The next features enable us to build and use declarative programming environment using nested structures on the top of object oriented • using {} to access the object attributes and methods • BraceEnd() Method • returning objects by reference • Setter/Getter Methods (optional) Example: # Declarative Programming (Nested Structures) Screen() { point() { x = 100 y = 200 z = 300 } point() { x = 50 y = 150 z = 250 } } # Functions and Classes Func screen return new screen Class Screen content = [] func point content + new point return content[len(content)] func braceend see "I have " + len(content) + " points!" Class point x=10 y=20 z=30 func braceend (continues on next page) 34.4. Declarative Programming on the top of Object-Oriented 281
326.
Ring Documentation, Release1.21.2 (continued from previous page) see self Output: x: 100.000000 y: 200.000000 z: 300.000000 x: 50.000000 y: 150.000000 z: 250.000000 I have 2 points! 34.5 More Beautiful Code We can get better results and a more beautiful code when we can avoid writing () after the method name when the methods doesn’t take parameters. This feature is not provided directly by the Ring language because there is a difference between object methods and object attributes. We can get a similar effect on the syntax of the code when we define a getter method for the object attribute. For example instead of defining the point() method. we will define the point attribute then the getpoint() method that will be executed once you try to get the value of the point attribute. since we write the variable name directly without () we can write point instead of point() and the method getpoint() will create the object and return the object reference for us. Example: new Container { Point { x=10 y=20 z=30 } } Class Container aObjs = [] point func getpoint aObjs + new Point return aObjs[len(aObjs)] Class Point x y z func braceend see "3D Point" + nl + x + nl + y + nl + z + nl Output 3D Point 10 20 30 34.5. More Beautiful Code 282
327.
CHAPTER THIRTYFIVE NATURAL LANGUAGE PROGRAMMING Usingthe Ring programming language, we can create Natural programming languages based on classes and objects. 35.1 History In 2010, I developed a new programming language called Supernova (developed using PWCT). This language uses a code that looks similar to Natural Language statements to create simple GUI applications. Now after five years, In the Ring programming language, we can get similar results, but now we have the ability to create/use code similar to Natural language statements in any domain that we like or need. The Ring programming language comes with the Supernova spirit, but with more generalization and with mix of other languages spirits. 35.2 Example The next example presents how to create a class that define two instructions The first instruction is : I want window The second instruction is : Window title = <expr> Also keywords that can be ignored like the ‘the’ keyword New App { I want window The window title = "hello world" } Class App # Attributes for the instruction I want window i want window nIwantwindow = 0 # Attributes for the instruction Window title # Here we don't define the window attribute again title nWindowTitle = 0 # Keywords to ignore, just give them any value the=0 (continues on next page) 283
328.
Ring Documentation, Release1.21.2 (continued from previous page) func geti if nIwantwindow = 0 nIwantwindow++ ok func getwant if nIwantwindow = 1 nIwantwindow++ ok func getwindow if nIwantwindow = 2 nIwantwindow= 0 see "Instruction : I want window" + nl ok if nWindowTitle = 0 nWindowTitle++ ok func settitle cValue if nWindowTitle = 1 nWindowTitle=0 see "Instruction : Window Title = " + cValue + nl ok Output: Instruction : I want window Instruction : Window Title = hello world 35.3 Change the Ring Keyword ‘And’ What if we want to connect between the two instructions using ‘and’ We have a problem because in Ring ‘and’ is a keyword We can change that using the ChangeRingKeyword command. Syntax: ChangeRingKeyword <oldkeyword> <newkeyword> Note: remember to restore the keyword again Tip: The ChangeRingKeyword command is executed in the scanner stage by the compiler (before parsing). Example: 35.3. Change the Ring Keyword ‘And’ 284
329.
Ring Documentation, Release1.21.2 ChangeRingKeyword and _and New App { I want window and the window title = "hello world" } Class App # Attributes for the instruction I want window i want window nIwantwindow = 0 # Attributes for the instruction Window title # Here we don't define the window attribute again title nWindowTitle = 0 # Keywords to ignore, just give them any value the=0 and=0 ChangeRingKeyword _and and func geti if nIwantwindow = 0 nIwantwindow++ ok func getwant if nIwantwindow = 1 nIwantwindow++ ok func getwindow if nIwantwindow = 2 nIwantwindow= 0 see "Instruction : I want window" + nl ok if nWindowTitle = 0 nWindowTitle++ ok func settitle cValue if nWindowTitle = 1 nWindowTitle=0 see "Instruction : Window Title = " + cValue + nl ok func getand see "Using : and" + nl Output: Instruction : I want window Using : and (continues on next page) 35.3. Change the Ring Keyword ‘And’ 285
330.
Ring Documentation, Release1.21.2 (continued from previous page) Instruction : Window Title = hello world 35.4 Change the Ring Operator ‘+’ What if we want to define a new behavior for any operator like the “+” operator. We can do this change using the ChangeRingOperator command to hide operator (change it’s name) Then we can use the operator as identifier that we can handle it’s behaviour Syntax: ChangeRingOperator <oldoperator> <newoperator> Note: remember to restore the operator again Tip: The ChangeRingOperator command is executed in the scanner stage by the compiler (before parsing). Example: ChangeRingOperator + _+ New App { + } Class App + func get+ see "Plus operator" ChangeRingOperator _+ + Output: Plus operator 35.5 Change the ‘=’ operator to ‘is’ Example: ChangeRingKeyword and _and ChangeRingOperator = is New App { I want window and the window title is "hello world" (continues on next page) 35.4. Change the Ring Operator ‘+’ 286
331.
Ring Documentation, Release1.21.2 (continued from previous page) } ChangeRingOperator is = Class App # Attributes for the instruction I want window i want window nIwantwindow = 0 # Attributes for the instruction Window title # Here we don't define the window attribute again title nWindowTitle = 0 # Keywords to ignore, just give them any value the=0 and=0 ChangeRingKeyword _and and func geti if nIwantwindow = 0 nIwantwindow++ ok func getwant if nIwantwindow = 1 nIwantwindow++ ok func getwindow if nIwantwindow = 2 nIwantwindow= 0 see "Instruction : I want window" + nl ok if nWindowTitle = 0 nWindowTitle++ ok func settitle cValue if nWindowTitle = 1 nWindowTitle=0 see "Instruction : Window Title = " + cValue + nl ok 35.5. Change the ‘=’ operator to ‘is’ 287
332.
Ring Documentation, Release1.21.2 35.6 Using Eval() with our Natural Code Example: func Main cProgram = ' I want window and the window title is "hello world" ' MyLanguage(cProgram) Func MyLanguage cCode # We add to the code the instructions that change keywords and operators # Because Eval() uses a new Compiler Object (the original keywords and operators). cCode = ' ChangeRingKeyword and _and ChangeRingOperator = is ' + cCode New App { eval(cCode) } Class App # Attributes for the instruction I want window i want window nIwantwindow = 0 # Attributes for the instruction Window title # Here we don't define the window attribute again title nWindowTitle = 0 # Keywords to ignore, just give them any value the=0 ChangeRingKeyword and _and and=0 ChangeRingKeyword _and and func geti if nIwantwindow = 0 nIwantwindow++ ok func getwant if nIwantwindow = 1 nIwantwindow++ ok func getwindow (continues on next page) 35.6. Using Eval() with our Natural Code 288
333.
Ring Documentation, Release1.21.2 (continued from previous page) if nIwantwindow = 2 nIwantwindow= 0 see "Instruction : I want window" + nl ok if nWindowTitle = 0 nWindowTitle++ ok func settitle cValue if nWindowTitle = 1 nWindowTitle=0 see "Instruction : Window Title = " + cValue + nl ok 35.7 BraceStart and BraceEnd Methods We can write code that will be executed before/after using { } Example: o1 = new test { see "Hello" + nl } o1 {} class test func bracestart see "start" + nl func braceend see "end" + nl Output: start Hello end start end 35.7. BraceStart and BraceEnd Methods 289
334.
Ring Documentation, Release1.21.2 35.8 BraceExprEval Method The next example demonstrates how to use the “BraceExprEval” method to get expressions in Natural code. Example: new natural { create 5 } class natural create=0 lkeyword = false func braceexpreval r if lkeyword lkeyword=false return ok see "expr eval" + nl see "type: " + type(r) see nl see "value : " see r see nl func getcreate lkeyword = true see "create" + nl Output: create expr eval type: NUMBER value : 5 35.9 Real Natural Code The next example is a more advanced example # Natural Code new program { Accept 2 numbers then print the sum } # Natural Code Implementation class program # Keywords Accept=0 numbers=0 then=0 print=0 the=0 sum=0 # Execution func braceexpreval x value = x func getnumbers for x=1 to value see "Enter Number ("+x+") :" give nNumber aNumbers + nNumber next (continues on next page) 35.8. BraceExprEval Method 290
335.
Ring Documentation, Release1.21.2 (continued from previous page) func getsum nSUm = 0 for x in aNumbers nSum+= x next see "The Sum : " + nSum private value=0 aNumbers=[] Output: Enter Number (1) :3 Enter Number (2) :4 The Sum : 7 35.10 BraceError() Method The next examples demonstrates how to use the “BraceError” method to handle errors when accessing the object using braces {}. Example: func main o1 = new point { x=10 y=20 z=30 TEST SEE test } class point x y z func braceerror see "Handle Error!" + nl SEE "Message :" + cCatchError + nl if ( left(cCatchError,11) = "Error (R24)" ) and not isattribute(self, ˓ →"test") see "add attribute" + nl addattribute(self,"test") test = 10 ok see "done" + nl return Output: Handle Error! Message :Error (R24) : Using uninitialized variable : test add attribute done 10 Example: 35.10. BraceError() Method 291
336.
Ring Documentation, Release1.21.2 new point { x=10 y=20 z=30 test() see "mmm..." + NL } class point x y z func braceerror see "Handle Error!" + nl see "Message :" + cCatchError + nl see self see "Done" + NL Output: Handle Error! Message :Error (R3) : Calling Function without definition !: test x: 10.000000 y: 20.000000 z: 30.000000 Done mmm... 35.11 Clean Natural Code Instead of typing the literal as “literal” we can accept the words directly. Example: The next example accept hello world instead of “hello world” But this example uses braceend() to check the end of the instruction This means that this class process only one natural statement that end with literal. ChangeRingKeyword and _and New App { I want window and the window title is hello world } Class App # Attributes for the instruction I want window i want window nIwantwindow = 0 # Attributes for the instruction Window title # Here we don't define the window attribute again title is nWindowTitle = 0 # Keywords to ignore, just give them any value the=0 and=0 (continues on next page) 35.11. Clean Natural Code 292
337.
Ring Documentation, Release1.21.2 (continued from previous page) # Data literal = "" ChangeRingKeyword _and and func geti if nIwantwindow = 0 nIwantwindow++ ok func getwant if nIwantwindow = 1 nIwantwindow++ ok func getwindow if nIwantwindow = 2 nIwantwindow= 0 see "Instruction : I want window" + nl ok if nWindowTitle = 0 nWindowTitle++ ok func gettitle if nWindowTitle = 1 nWindowTitle=2 ok func getis if nWindowTitle = 2 nWindowTitle=3 ok func braceend if nWindowTitle = 3 see "Instruction : Window Title = " + literal + nl nWindowTitle = 0 ok func braceerror c= substr(cCatchError,":") while c > 0 c= substr(cCatchError,":") cCatchError=substr(cCatchError,c+1) end literal += substr(cCatchError,1) 35.11. Clean Natural Code 293
338.
CHAPTER THIRTYSIX USING THE NATURALLIBRARY In this chapter we will learn how to use the Natural Library to quickly define a language that contains a group of commands. To start using the library, We need to call naturallib.ring load "naturallib.ring" After loading the library, We can use the NaturalLanguage class that contains the next methods :- • SetLanguageName(cLanguageName) • setCommandsPath(cFolder) • SetPackageName(cPackageName) • UseCommand(cCommandName) • SetOperators(cOperators) • RunFile(cFileName) • RunString(cString) 36.1 Natural Library - Demo Program We will write the natural code in a Text file, for example program.txt File: program.txt Welcome to the Ring programming language! What you are reading now is not comments, I swear! After many years of programming I decided to think different about programming and solve the problems in a better way. We are writing commands or code and the Ring language is reading it to understand us! Sure, What you are seeing now is just ***part of the code - Not the Complete Program*** You have to write little things before and after this part to be able to run it! It is the natural part of our code where we can write in English, Arabic or any Natural Language Then we will tell the computer (continues on next page) 294
339.
Ring Documentation, Release1.21.2 (continued from previous page) through the Ring language what must happens! in a way that we can scale for large frameworks and programs. Just imagine what will happens to the world of programming once we create many powerful frameworks using the Ring language that uses this way (Natural Programming). For example When we say Hello to the Machine, It can reply! and when we say count from 1 to 5 it will understand us, Also if we said count from 5 to 1 it will understand us too! You can see the Output window! This Goal is not new, but the Ring language comes with an innovative solution to this problem. Output: Hello, Sir! The Numbers! 1 2 3 4 5 I will count Again! 5 4 3 2 1 To execute the natural code, We have start.ring In start.ring we define the language and the commands. File: start.ring load "stdlib.ring" load "naturallib.ring" (continues on next page) 36.1. Natural Library - Demo Program 295
340.
Ring Documentation, Release1.21.2 (continued from previous page) New NaturalLanguage { SetLanguageName(:MyLanguage) SetCommandsPath(CurrentDir()+"/../command") SetPackageName("MyLanguage.Natural") UseCommand(:Hello) UseCommand(:Count) RunFile("program.txt") } We defined a language called MyLanguage, We have folder for the language commands. Each command will define a class that belong to the MyLanguage.Natural package. We will define two commands, Hello and Count. So we must have two files for defining the commands in the CurrentDir()+”/../command” folder File: hello.ring DefineNaturalCommand.SyntaxIsKeyword([ :Package = "MyLanguage.Natural", :Keyword = :hello, :Function = func { See "Hello, Sir!" + nl + nl } ]) File: count.ring DefineNaturalCommand.SyntaxIsKeywordNumberNumber([ :Package = "MyLanguage.Natural", :Keyword = :count, :Function = func { if not isattribute(self,:count_times) { AddAttribute(self,:count_times) Count_Times = 0 } if Expr(1) > Expr(2) { nStep = -1 else nStep = 1 } if Count_Times = 0 { see nl+"The Numbers!" + nl Count_Times++ else see nl + "I will count Again!" +nl } for x = Expr(1) to Expr(2) step nStep { see nl+x+nl } CommandReturn(fabs(Expr(1)-Expr(2))+1) } ]) 36.1. Natural Library - Demo Program 296
341.
Ring Documentation, Release1.21.2 36.2 Defining Commands To define new command we can use the DefineNaturalCommand object This object provides the next methods :- • SyntaxIsKeyword(aPara) • SyntaxIsKeywordNumber(aPara) • SyntaxIsKeywordNumberNumber(aPara) • SyntaxIsKeywordNumbers(aPara,nCount) • SyntaxIsKeywordString(aPara) • SyntaxIsKeywordStringString(aPara) • SyntaxIsKeywordStrings(aPara,nCount) • SyntaxIsKeywordExpression(aPara) • SyntaxIsKeywordExpressionExpression(aPara) • SyntaxIsKeywordExpressions(aPara,nCount) • SyntaxIsCommand(aPara) • SyntaxIsCommandNumber(aPara) • SyntaxIsCommandNumberNumber(aPara) • SyntaxIsCommandNumbers(aPara,nCount) • SyntaxIsCommandString(aPara) • SyntaxIsCommandStringString(aPara) • SyntaxIsCommandStrings(aPara,nCount) • SyntaxIsCommandExpression(aPara) • SyntaxIsCommandExpressionExpression(aPara) • SyntaxIsCommandExpressions(aPara,nCount) File: mylanguage.ring load "stdlib.ring" load "naturallib.ring" MyLanguage = New NaturalLanguage { SetLanguageName(:MyLanguage) setCommandsPath(CurrentDir()+"/../command") SetPackageName("MyLanguage.Natural") UseCommand(:Hello) UseCommand(:Count) UseCommand(:Print) UseCommand(:IWantWindow) UseCommand(:WindowTitleIs) UseCommand(:IWantButton) } 36.2. Defining Commands 297
342.
Ring Documentation, Release1.21.2 Example (1) In the next example we will define the Print command. We will use the SyntaxIsKeywordExpression() Method. We pass list (as Hash) to the method. We determine the package name, the keyword and the function that will be executed. Inside this function we uses the Expr(nExprNumber) function to get the expression value that the user will write after the keyword. File: print.ring DefineNaturalCommand.SyntaxIsKeywordExpression([ :Package = "MyLanguage.Natural", :Keyword = :print, :Function = func { See Expr(1) } ]) Usage: load "mylanguage.ring" MyLanguage.RunString(' print "Hello, World!" ') Output: Hello, World! Example (2) File: iwantwindow.ring DefineNaturalCommand.SyntaxIsCommand([ :Package = "MyLanguage.Natural", :Command = "i want window", :Function = func { See "Command: I want window" + nl } ]) Usage: load "mylanguage.ring" MyLanguage.RunString(' i want window ') Output: Command: I want window 36.2. Defining Commands 298
343.
Ring Documentation, Release1.21.2 Example (3) File: windowtitleis.ring DefineNaturalCommand.SyntaxIsCommandString([ :Package = "MyLanguage.Natural", :Command = "window title is", :Function = func { See "Command: Window title is " + Expr(1) + nl } ]) Usage: load "mylanguage.ring" MyLanguage.RunString(' I want window and the window title is "Hello World" ') Output: Command: I want window Command: Window title is Hello World 36.3 Natural Library - Operators In the next example we uses the Count command without using operators load "mylanguage.ring" MyLanguage.RunString(" Hello Count 1 5 Count 5 1 ") We can add more description load "mylanguage.ring" MyLanguage.RunString(" Hello, Please Count from 1 to 5 then count from 5 to 1 ") Also we can use operators like “(” and “)” around the instruction load "mylanguage.ring" MyLanguage { SetOperators("()") RunString(" Here we will play and will try something (continues on next page) 36.3. Natural Library - Operators 299
344.
Ring Documentation, Release1.21.2 (continued from previous page) that looks like Lisp Syntax (count (count 1 5) (count 20 15)) Just for fun! ") } 36.4 Defining commands using classes This section is related to the implementation details. When we define new command, Each command is defined by the Natural Library as a class. We have the choice to define commands using the simple interface provided by the DefineNaturalCommand object or by defining new class as in the next examples. If we used DefineNaturalCommand (More Simple), The class will be defined during the runtime. File: hello.ring Package MyLanguage.Natural class Hello func AddAttributes_Hello AddAttribute(self,:hello) func GetHello See "Hello, Sir!" + nl + nl File: count.ring Package MyLanguage.Natural class Count func Getcount StartCommand() CommandData()[:name] = :Count CommandData()[:nExpr] = 0 CommandData()[:aExpr] = [] func BraceExprEval_Count nValue if isCommand() and CommandData()[:name] = :Count { if isNumber(nValue) { CommandData()[:nExpr]++ CommandData()[:aExpr] + nValue if CommandData()[:nExpr] = 2 { Count_Execute() } } } (continues on next page) 36.4. Defining commands using classes 300
345.
Ring Documentation, Release1.21.2 (continued from previous page) func AddAttributes_Count AddAttribute(self,:count) func Count_Execute if not isattribute(self,:count_times) { AddAttribute(self,:count_times) Count_Times = 0 } if Expr(1) > Expr(2) { nStep = -1 else nStep = 1 } if Count_Times = 0 { see nl+"The Numbers!" + nl Count_Times++ else see nl + "I will count Again!" +nl } for x = Expr(1) to Expr(2) step nStep { see nl+x+nl } CommandReturn(fabs(Expr(1)-Expr(2))+1) 36.4. Defining commands using classes 301
346.
CHAPTER THIRTYSEVEN SCOPE RULES FORVARIABLES AND ATTRIBUTES In this chapter we will learn about scope rules and how Ring find variables. Also we will learn about conflicts and how to solve/avoid them. The next information are important once you start developing large applications using Ring These application may uses • Global variables (Try to avoid them) • Classes (Object-Oriented) • braces { } to access objects • Declarative Programming • Natural Programming 37.1 Three Scopes In Ring we have three scopes :- (1) Public/Global Scope - Each variable you define in the statements part (before functions and classes) (2) Object Scope - When you are inside an object (Inside class method or using { } to access the object ) (3) Local Scope - Related to functions and methods 37.2 Defining Variables and Variables Access (1) Ring uses lexical scoping, i.e. the scope of the variable is based on where we defined the variable. (2) Inside braces { } when you access an object, You will change the current active object scope to this object scope but you still can access the global scope and the local scope. (3) After the ‘Class’ keyword and the class name, when you write variable names to be defined as attributes, You still can access the global scope. In this region (class region - after the class name and before methods) we have • Global Scope —-> The Global Scope • Object Scope —-> The Object Scope • Local Scope —-> The Object Scope 302
347.
Ring Documentation, Release1.21.2 Note: Since the local scope in the class region point also to the object scope in this region, we can use nested braces and still have access to the object scope of the class through the local scope. Tip: You can create windows and controls as attributes by defining them in this region. Tip: In the class region if you created objects and used braces {} to access them then using self.attribute inside braces will use the class (not the object that you access) because you have access to the class through the local scope. (4) Function Parameters are automatically defined in the local scope. 37.3 How Ring find the variable? 1 - Search First in the Local Scope if not found ! 2 - Search in the Object Scope if not found ! 3 - Search in the public scope if not found —-> Runtime Error if found —-> Check if we can do optimization to avoid searching next time (Cache / Pointers for performance). 37.4 Using Object.Attribute When we use object.attribute the search will be in the object attributes only. I.e. no search will be done in the local scope or in the global scope for the object attribute. Note: Using self.attribute will search for the first self before searching for attributes. 37.5 The Self Object The self object is a reference to the current object that we can use from the class methods. When we are inside class method and use Self we mean the object that will be created from this class. Inside the class methods if we used Braces { } this will change the current object scope and self will be changed also inside braces to reference the object that we access using Braces. Inside the Class Region (after the class name and before any method) we have access to the object through the object scope and the local scope also. In this region using Self will always be a reference to the class object. if we used Braces to change the object scope then used Self inside Braces, Also self will be a reference to the class object (not the object that we already access using braces) because in the class region we have :- 37.3. How Ring find the variable? 303
348.
Ring Documentation, Release1.21.2 • Global Scope —> Global Scope • Object Scope —> Object Scope • Local Scope —> Object Scope And using Braces changes the object scope only (not the local scope) and when Ring search for variables it will search in the Local Scope first so it will find self in the class that we are inside. 37.6 How Ring Define Variables and Attributes Ring will use the variable name in the Assignment operation 1 - Search using the variable name 2 - If not found —> Avoid the runtime error and define the variable in the current scope 3 - If found —> Use the variable and don’t define anything in the current scope • In the global region (before any function or class) the current scope is the global scope. • In the class region (after the class name and before any method) the current scope is the object attributes. • In Functions and methods the current scope is the local scope. 37.7 Conflict between Global Variables and Class Attributes Look at this example: name = "test" o1 = new person see o1 class person name address phone In the previous example we have a global variable called ‘name’ inside the class person. when we use the variable ‘name’, Ring will start the search operation and will try to find it. if found —> Use it if not found —> Define new attribute But the variable name is a global variable, so it will be found and used! We will not have the attribute name! added to the object. Solution (1) - Use the Main Function func main name = "test" o1 = new person see o1 class person (continues on next page) 37.6. How Ring Define Variables and Attributes 304
349.
Ring Documentation, Release1.21.2 (continued from previous page) name address phone Solution (2) - Use special mark for global variable names like $ $name = "test" o1 = new person see o1 class person name address phone Solution (3) - Use the AddAttribute() Method name = "test" o1 = new person see o1 class person AddAttribute(self,"name") address phone Solution (4) - Use self before the attribute name name = "test" o1 = new person see o1 class person self.name address phone So what is the best solution to this conflict? 1 - Use the $ Mark for global variables 2 - Optional : Try to avoid global variables and use the Main function In practice i do both of them. The other solution • Use self before the attribute name or use AddAttribute() Note: Ring have a nice feature through the (load package) command which load a Ring source code file in a new global scope different from the current global scope used by the caller. 37.7. Conflict between Global Variables and Class Attributes 305
350.
Ring Documentation, Release1.21.2 37.8 Conflict between Class Attributes and Local Variables This conflict may happen when we access the object using braces Example: func main name = "nice" o1 = new person {name="mahmoud" address="Egypt" phone = 000 } see o1 class person name address phone In the previous example we have the local variable name. The value of this variable will be set to “mahmoud” instead of the object attribute. Solution (1) : Just use Self func main name = "nice" o1 = new person {self.name="mahmoud" address="Egypt" phone = 000 } see o1 class person name address phone Solution (2) : Change the Local variable name func main cName = "nice" o1 = new person {name="mahmoud" address="Egypt" phone = 000 } see o1 class person name address phone Solution (3) : Change Braces and use the Dot operator func main name = "nice" o1 = new person o1.name ="mahmoud" o1.address ="Egypt" o1.phone = 000 see o1 class person (continues on next page) 37.8. Conflict between Class Attributes and Local Variables 306
351.
Ring Documentation, Release1.21.2 (continued from previous page) name address phone 37.9 Using Braces to access objects inside Class Methods Remember that we have Three scopes (Local Scope, Object Scope and Global Scope) and when we are inside a class method, we expect that we have access to the object attributes and methods and this is true until we use braces to access another object attributes and methods because in this case our object scope will be switched to another object. new point { test() } class point x=10 y=20 func test see x + nl + y + nl # works fine myobj = new otherclass { see name + nl see x + nl + y + nl # error ! } class otherclass name = "test" Output: 10 20 test Line 8 Error (R24) : Using uninitialized variable : x In method test() in file methodbraceerror.ring called from line 5 in file methodbraceerror.ring Now what we will do to solve the previous problem? Solution (1) : Write the code that access the class attributes outside braces. new point { test() } class point x=10 y=20 func test see x + nl + y + nl # works fine myobj = new otherclass { see name + nl } see x + nl + y + nl # Outside braces - works fine (continues on next page) 37.9. Using Braces to access objects inside Class Methods 307
352.
Ring Documentation, Release1.21.2 (continued from previous page) class otherclass name = "test" Output: 10 20 test 10 20 Solution (2) : Don’t Use Braces new point { test() } class point x=10 y=20 func test see x + nl + y + nl myobj = new otherclass see myobj.name see x + nl + y + nl class otherclass name = "test" Solution (3) : Copy the self object We may use this solution if we want to use braces and get access to the class attributes (Just Reading). new point { test() } class point x=10 y=20 func test oSelf = self see x + nl + y + nl myobj = new otherclass { see name + nl see oself.x + nl + oself.y + nl } class otherclass name = "test" Output: 10 20 test 10 20 Now look at this line 37.9. Using Braces to access objects inside Class Methods 308
353.
Ring Documentation, Release1.21.2 oself = self The problem with the previous line is that we will have a new copy from the object Because in Ring the assignment operator copy lists and objects by value (not by reference). When we access the new object attributes (reading) we don’t have problems But if we modified the object attributes (Then we will modify the copy!). Note: We can use braces again with the copy new point { test() } class point x=10 y=20 func test oSelf = self see x + nl + y + nl myobj = new otherclass { see name + nl oSelf { see x + nl + y + nl } } class otherclass name = "test" In a GUI application, we may create a class contains the window objects as attributes to be able to access the controls from different methods. Remember the previous information when you try to access objects using braces inside methods because in this case you can’t access the object attributes directly and if you copied the self object you will work on a copy and the new controls that you create will be related to the copy and you can’t access them. 37.10 Accessing the class attributes from braces inside class meth- ods We access the class attributes directly from the class methods, also we have the choice to use the Self reference before the attribute/method name. Using Braces {} inside class method change the active object scope and prevent us from getting direct access to the class attributes. Also using Self will not help because the Self reference will be changed to the object that we access using Braces. In this case if you want to read an attribute you have to copy the Self object before using Braces and if you want to modify an attribute you have to the copy from local variable to the object attribute after using Braces. This case happens when you want to read/modify attribute instead braces. Class MyApp oCon # Attribute # some code here (continues on next page) 37.10. Accessing the class attributes from braces inside class methods 309
354.
Ring Documentation, Release1.21.2 (continued from previous page) Func OpenDatabase # some code here new QSqlDatabase() { oCon = addDatabase("QSQLITE") { setDatabaseName("weighthistory.db") open() } } self.oCon = oCon # some code here In the previous example we want to create the connection object and save it inside the oCon attribute. The object is an output from the addDatabase() method that we use after accessing the QSQLDatabase() object. Inside braces we can’t use the Self reference to use the object created from the MyApp class, Because the Self reference here will be to the object that we access using Braces. We solved the problem in the previous example by creating a local variable called oCon then after Braces we copied that variable to the oCon attribute. The next code is another solution. Class MyApp oCon # Attribute # some code here Func OpenDatabase # some code here oCon = new QSqlDatabase() oCon = oCon.addDatabase("QSQLITE") { setDatabaseName("weighthistory.db") Open() } # some code here The next code is a better solution. Class MyApp oCon # Attribute # some code here Func OpenDatabase # some code here new QSqlDatabase() { this.oCon = addDatabase("QSQLITE") { setDatabaseName("weighthistory.db") Open() } (continues on next page) 37.10. Accessing the class attributes from braces inside class methods 310
355.
Ring Documentation, Release1.21.2 (continued from previous page) } # some code here Note: We used this.attribute to access the class attribute (oCon) while we are inside Braces. 37.11 Creating a Class for each Window in GUI applications A good way for creating classes for windows is to define the window directly after the class name In this area you can use nested braces without problems to define the window and the controls, and they will be attributes that you can access from methods. Example: Load "guilib.ring" new qApp { $ObjectName = "oFirstWindow" oFirstWindow = new FirstWindow $ObjectName = "oSecondWindow" oSecondWindow = new SecondWindow exec() } Class FirstWindow win = new qWidget() { setgeometry(0,50,300,200) setWindowTitle("First Window") label1 = new qLabel(win) { setgeometry(10,10,300,30) setText("0") } btn1 = new qPushButton(win) { move(100,100) setText("Increment") setClickEvent($ObjectName+".increment()") } show() } Func Increment label1 { setText( "" + ( 0 + text() + 1 ) ) (continues on next page) 37.11. Creating a Class for each Window in GUI applications 311
356.
Ring Documentation, Release1.21.2 (continued from previous page) } Class SecondWindow win = new qWidget() { setgeometry(400,50,300,200) setWindowTitle("Second Window") label1 = new qLabel(win) { setgeometry(10,10,300,30) setText("0") } btn1 = new qPushButton(win) { move(100,100) setText("Decrement") setClickEvent($ObjectName+".decrement()") } show() } Func Decrement label1 { setText( "" + ( 0 + text() - 1 ) ) } 37.12 Conflict between self inside braces and self in the class region In the class region (after the class name and before any methods) we define the attributes. In this region we have access to the global scope and the local scope will point to the object scope. Three Scopes • Global Scope —> Global Scope • Object Scope —> Object Scope • Local Scope —> Object Scope Look at this example New Account { see aFriends } Class Account name = "Mahmoud" aFriends = [] aFriends + new Friend { name = "Gal" } (continues on next page) 37.12. Conflict between self inside braces and self in the class region 312
357.
Ring Documentation, Release1.21.2 (continued from previous page) aFriends + new Friend { name = "Bert" } Class Friend name Output: name: NULL name: NULL The problem in the previous example is that the Class account contains an attribute called “name” and the Friend class contains an attribute called “name” also. If you tried using self.name inside braces you will get the same result! New Account { see aFriends } Class Account name = "Mahmoud" aFriends = [] aFriends + new Friend { self.name = "Gal" } aFriends + new Friend { self.name = "Bert" } Class Friend name So why using self.name inside braces doesn’t solve this conflict? Because after the class region we have • global scope —> global scope • object scope —> object scope (Account Class) • local scope —> local scope (Account Class) When we use braces we change the object scope, so we have • global scope —> global scope • object scope —> object scope (Friend Class) • local scope —> local scope (Account Class) Ring search in the local scope first, so using self.name will use the Account class. There are many solution Solution (1) : Access the object through the list 37.12. Conflict between self inside braces and self in the class region 313
358.
Ring Documentation, Release1.21.2 New Account { see aFriends } Class Account name = "Mahmoud" aFriends = [] aFriends + new Friend aFriends[len(aFriends)] { aFriends[len(aFriends)].name = "Gal" } aFriends + new Friend aFriends[len(aFriends)] { aFriends[len(aFriends)].name = "Bert" } Class Friend name Solution (2) : Create Method in the friend class to set the name attribute. New Account { see aFriends } Class Account name = "Mahmoud" aFriends = [] aFriends + new Friend { setname("Gal") } aFriends + new Friend { setname("Bert") } Class Friend name func setname cName name = cName Solution (3) : Create a method in the account class to set the attribute New Account { see aFriends } Class Account name = "Mahmoud" aFriends = [] friend("Gal") friend("Bert") func friend cName (continues on next page) 37.12. Conflict between self inside braces and self in the class region 314
359.
Ring Documentation, Release1.21.2 (continued from previous page) aFriends + new Friend { name = cName } Class Friend name Solution (4) : Declarative Programming New Account { name = "mahmoud" friend { name = "Gal" } friend { name = "Bert" } see aFriends } Class Account name aFriends = [] friend func getfriend aFriends + new Friend return aFriends[len(aFriends)] Class Friend name Output: name: Gal name: Bert 37.13 Using braces to escape from the current object scope Since braces change the current object scope to another object. we can use it to do some work without modifying the class attributes and using the same variable names. new point {x=10 y=20 z=30 start() } class point x y z func start see self # print the x y z values (10,20,30) new Local { x = 100 y = 200 z = 300 } (continues on next page) 37.13. Using braces to escape from the current object scope 315
360.
Ring Documentation, Release1.21.2 (continued from previous page) see self # print the x y z values (10,20,30) see x + nl # will print 100 see y + nl # will print 200 see z + nl # will print 300 Self { # NO Advantage - Search is done in local scope first see x + nl # will print 100 see y + nl # will print 200 see z + nl # will print 300 } see self.x + nl # will print 10 see self.y + nl # will print 20 see self.z + nl # will print 30 class Local Output: x: 10.000000 y: 20.000000 z: 30.000000 x: 10.000000 y: 20.000000 z: 30.000000 100 200 300 100 200 300 10 20 30 37.14 The For Loops uses the local scope Starting from Ring 1.8, when the For Loop defines new identifier (variable) it will define it in the local scope. Example: x = 10 ? x # Print 10 test1() ? x # Print 10 test2() ? x # Print 10 func test1 for x = 1 to 5 next ? x # Print 6 (continues on next page) 37.14. The For Loops uses the local scope 316
361.
Ring Documentation, Release1.21.2 (continued from previous page) func test2 list = 1:5 for x in list next ? x # Print NULL (The "For In" loop will kill the reference after the loop) Output: 10 6 10 NULL 10 37.15 Summary of Scope Rules At first remember that 1 - Each programming language comes with it’s scope rules based on the language goals 2 - Programming in the small is different than Programming in the Large 3 - Some programming language are designed for developing small programs while others are designed for large pro- grams 4 - In programming, If we have access to more than one scope - Then problems may come if we don’t manage things correctly 5 - It’s always more secure to reduce the number of visible scopes 6 - Some programming languages force you to manage the scope in some way, while others not! In Ring 1 - Special and very simple scope rules that are designed for Flexibility first then Security 2 - Ring is designed to support programming in the small and programming in the large. 3 - The language provide the different programming paradigms that you may select from based on the project size. Errors comes only if you selected a bad paradigm for the target project or you are using the paradigm in a way that is not correct or at least not common. 4 - In Ring you have the choice, you can use global variables or avoid them. you can give them a special $ mark or leave them. you can use object-oriented or stay with procedures. you can use the class region (after the class name and before any method) just for attributes or use it for code too. 5 - Just read the next scope rules and think about them then use them in your favorite way. Scope Rules: 1 - At any place in our program code we have only at maximum Three Scopes (Local Scope, Object Scope and Global Scope). 2 - When Ring find a variable it will search in the local scope first then in the object scope then in the global scope. 3 - At any time inside procedures or methods you can use braces { } to access an object and change the current object scope. 37.15. Summary of Scope Rules 317
362.
Ring Documentation, Release1.21.2 4 - In the class region (After the class name and before any method) this is a special region where both of the object scope and the local scope point to the object scope. I.e. No local variables where each variable you define in this region will become an attribute. 5 - Before defining any variable (in any scope and in the class region too) a search process will be done to use the variable if it’s found. 6 - Functions and Methods parameters are defined automatically as local variables to these functions or methods. 7 - Using Object.Attribute will search in the object attributes only. 8 - Using Self.Attribute will lead to a search for Self first then search in Self Attributes. 9 - The Self reference inside class region (after the class name and before any method) always point to the object scope created from the class. 10- The Self reference inside methods will be changed when we uses Braces to be a reference to the object that we access. 11- Writing variable names directly in the class region (after the class name and before any method) means using them or define then (in order). 12- Using self.attribute in the class region reduce search to the object scope (avoid conflict with global scope). From these rules you can understand all types of conflicts and why you may have them and how to avoid them Simple advices to avoid any conflict and use the scope rules in a better way 1 - Try to avoid global variables 2 - Use the Main Function - This will help you to avoid global variables 3 - If you are going to use many global variables use the $ mark before the variable name 4 - In the class region if you don’t respect the advice number three ($) then use self.attribute when you define your attributes 5 - You can use object.attribute and object.method() instead of object { attribute } and object { method() } if you don’t like changing the object scope. 6 - If you will use nested braces in a class - think about using the class region if possible because in this region you will have access to the object that you access using { } + access to the class attributes 7 - If you are inside a class method and used nested braces you will change the object scope with each brace and you will loss the access to the class attributes directly but you have access to the local scope before and after using brace { } , if you will read/modify the class attribute from braces then use This.Attribute because using ‘This’ means (The object created from this class) while using ‘Self’ means (The object in the current object scope). After understanding all of the previous points, You will master this topic. 37.15. Summary of Scope Rules 318
363.
CHAPTER THIRTYEIGHT SCOPE RULES FORFUNCTIONS AND METHODS In this chapter we will learn about the scope rules for functions and methods. You need to know the next information once you started using Ring for large applications. These applications may contains and use • Many Packages and Classes written in Ring • Many Functions written in Ring • Standard Ring Functions (Written in C language) • Functions and Classes written in C/C++ languages 38.1 How Ring find the Functions and Methods? When you call a method or function, Ring will start a search process to find this function If found –> Call the function and store the function pointer in the cache so Ring can use it again with doing another search. If not found —> Runtime error message (That you can avoid using Try/Catch) How the search process is done? Search for functions/methods follow the next order 1 - Search in methods (if we are inside class method or object using braces {}) 2 - Search in functions written by the programmer using Ring Code 3 - Search in functions written in C/C++ like standard Ring functions This enable us to write clean code inside classes methods and avoid any conflict with functions. If we want to call a function with the same name as a method in the class we will need a wrapper function or we will access a temp. object using { } then call that function there. We can replace C/C++ Functions with Ring Functions. We can replace Ring Functions with Ring Methods. Note: Using self.method() is not necessary in any use case. 319
364.
Ring Documentation, Release1.21.2 Tip: We can use this.method() to escape from the current active scope that we access using braces {} and call a method in the class that we are inside. 38.2 Example about Sharing Names between Functions and Methods Look at the next example func main o1 = new myclass { test() test2() } test2() func f1 see "f1 function" + nl func f2 see "f2 function" + nl func f3 see "f3 function" + nl func test2 myline() see "test2 function" + nl new myclass { f1() f2() f3() self.f3() } myobj = new myclass myobj.f3() myline() func myline see copy("=",40) + nl Class myclass func test myline() see "test method" + nl f1() f2() f3() myline() func f3 see "f3 method" + nl func test2 (continues on next page) 38.2. Example about Sharing Names between Functions and Methods 320
365.
Ring Documentation, Release1.21.2 (continued from previous page) myline() see "test2 method" + nl self { f1() f2() f3() } myline() Output: ======================================== test method f1 function f2 function f3 method ======================================== ======================================== test2 method f1 function f2 function f3 method ======================================== ======================================== test2 function f1 function f2 function f3 method f3 method f3 method ======================================== 38.3 Calling a function sharing the name with a method in the current class In the previous example we have a function called f3() and we have a method called f3() How we can call the f3() function from the test() method ? Solution (1) : Change the current object scope to another object scope In this solution we will have an empty class called local that we will use to change the current object scope. func main o1 = new myclass { test()} func f1 see "f1 function" + nl func f2 see "f2 function" + nl (continues on next page) 38.3. Calling a function sharing the name with a method in the current class 321
366.
Ring Documentation, Release1.21.2 (continued from previous page) func f3 see "f3 function" + nl func myline see copy("=",40) + nl Class myclass func test myline() see "test method" + nl f1() f2() f3() # call f3() method new local { f3() } # call f3() function myline() func f3 see "f3 method" + nl class local Output: ======================================== test method f1 function f2 function f3 method f3 function ======================================== 38.3. Calling a function sharing the name with a method in the current class 322
367.
CHAPTER THIRTYNINE SYNTAX FLEXIBILITY In thischapter we will learn about some options that are provided automatically by the Ring compiler for syntax flexibility. 39.1 Change Language Keywords We can change any keyword using the ChangeRingKeyword command. Note: Remember to restore the keyword again if the team will mix between styles in the same project. Tip: The ChangeRingKeyword command is executed in the scanner stage by the compiler (before parsing). Syntax: ChangeRingKeyword <oldkeyword> <newkeyword> Example: ChangeRingKeyword see print print "welcome" + nl ChangeRingKeyword print see see "Welcome" + nl Example: ChangeRingKeyword func function ChangeRingKeyword see print ChangeRingKeyword ok endif ChangeRingKeyword next endfor ChangeRingKeyword end endwhile x = 10 while x > 0 print "x = " + x + nl for t = 1 to 10 (continues on next page) 323
368.
Ring Documentation, Release1.21.2 (continued from previous page) if t = 3 print "number three" + nl endif endfor x-- endwhile test() function test print "message from test" + nl ChangeRingKeyword function func ChangeRingKeyword print see ChangeRingKeyword endif ok ChangeRingKeyword endfor next ChangeRingKeyword endwhile end 39.2 Change Language Operators We can change any operator using the ChangeRingOperator command. Note: Remember to restore the operator again if the team will mix between styles in the same project. Tip: The ChangeRingOperartor command is executed in the scanner stage by the compiler (before parsing). Syntax: ChangeRingOperator <oldkeyword> <newkeyword> Example: The next program hide the + operator by changing it to _+ changeringoperator + _+ changeringkeyword SEE PRINT try print 5 + 10 catch print nl print "error" print nl done changeringoperator _+ + The next program change the + operator to “plus”. 39.2. Change Language Operators 324
369.
Ring Documentation, Release1.21.2 changeringoperator + plus changeringkeyword SEE PRINT Print 5 plus 5 changeringoperator plus + changeringkeyword PRINT SEE 39.3 Load Syntax Files You may store a group of ChangeRingKeyword and ChangeRingOperator commands in a file to use later in many source files. You can’t use the Load command to call these files because • ChangeRingKeyword and ChangeRingOperator commands are executed in the scanner phase by the compiler (before parsing). • The load command is executed in the parsing phase (after the scanner phase). Solution: Use the LoadSyntax Command which is executed in the scanner phase. Syntax: LoadSyntax "syntaxfile.ring" Example: File : StyleBasicOn.ring ChangeRingKeyword see print ChangeRingKeyword ok endif ChangeRingKeyword next endfor ChangeRingKeyword end endwhile File : StyleBasicOff.ring ChangeRingKeyword print see ChangeRingKeyword endif ok ChangeRingKeyword endfor next ChangeRingKeyword endwhile end File : UseStyleBasic.ring LoadSyntax "stylebasicon.ring" x = 10 while x > 0 print "x = " + x + nl for t = 1 to 10 if t = 3 print "number three" + nl endif endfor x-- endwhile (continues on next page) 39.3. Load Syntax Files 325
370.
Ring Documentation, Release1.21.2 (continued from previous page) LoadSyntax "stylebasicoff.ring" see "done" + nl Note: files called by the LoadSyntax command must contains ChangeRingKeyword and ChangeRingOperator com- mands only. Tip: files called by the LoadSyntax command doesn’t support functions, packages and classes. just imperative com- mands only. Note: Using this feature you can create many styles that you can use in the same project and you can support Ring translation to other languages like Arabic, French and so on. Tip: The effect of LoadSyntax command is related to the current source code file only. Tip: Using LoadSyntax command is optional, See the (Automatic loading for syntax files) section. 39.4 Using “()” around the function parameters We can use () around the function parameters (optional). Example: hello() sum(3,4) func hello() see "Hello" + nl func sum(x,y) see x+y+nl Output: Hello 7 Example: myfunc = func x,y { see x + y + nl } call myfunc (3,4) (continues on next page) 39.4. Using “()” around the function parameters 326
371.
Ring Documentation, Release1.21.2 (continued from previous page) myfunc2 = func (x,y) { see x+y+nl } call myfunc(3,4) Output: 7 7 39.5 Using Semi-colon after and between statements In Ring we can use semi-colon after and between statements (optional). Example: # Using semi-colon is optional see "Hello" + nl ; see "How are you?" + nl ; see "Welcome to Ring" + nl ; one() ; two() ; three() ; func one ; see "one" + nl ; func two ; see "two" + nl ; func three ; see "three" + nl ; Output: Hello How are you? Welcome to Ring one two three 39.6 Using $ and @ in the start of the variable name You can use any unicode character in the variable name also we can use $ and @ in the name. This feature may help, for example we can start global variables with $ and the object attributes with @. In other languages like Ruby this is the rule, In the Ring language this is just an option without any force from the Compiler. example: $global_variable = 5 new test { hello() } class test (continues on next page) 39.5. Using Semi-colon after and between statements 327
372.
Ring Documentation, Release1.21.2 (continued from previous page) @instance_variable = 10 func hello local_variable = 15 see "Global : " + $global_variable + nl + "Instance : " + @instance_variable + nl + "Local : " + local_variable + nl Output: Global : 5 Instance : 10 Local : 15 39.7 Using the ‘elseif’ keyword as ‘but’ in if statement if you don’t like the ‘but’ keyword in if statement Then you can use the ‘elseif’ keyword. Example: give x if x = 1 see "one" elseif x=2 see "two" elseif x=3 see "three" elseif x=4 see "four" else see "other" ok see nl 39.8 Using the ‘else’ keyword as ‘other’ in switch statement if you don’t like the ‘other’ keyword in switch statement Then you can use the ‘else’ keyword. Also you can replace ‘else’ with ‘other’ in if statement. i.e. ‘other’ keyword is the same as ‘else’ keyword. Example: x = 1 switch x on 10 see "10" + nl else see "not 10" + nl end Output: 39.7. Using the ‘elseif’ keyword as ‘but’ in if statement 328
373.
Ring Documentation, Release1.21.2 not 10 39.9 Using the ‘end’ keyword in different control structures We can use the ‘end’ keyword to close different control structures • If statement • For loop • Switch • While • Try-Catch Example: see "if statement.." + nl x = 1 if x = 1 see "one" + nl elseif x=2 see "two" + nl elseif x=3 see "three" + nl end see "for loop.." + nl for t = 1 to 10 see t end see nl see "switch..." + nl x = 1 switch x on 1 see "one" + nl on 2 see "two" + nl end see "try catch..." + nl try x = 1 / 0 catch see "catching error" + nl end Output: if statement.. one for loop.. 12345678910 switch... (continues on next page) 39.9. Using the ‘end’ keyword in different control structures 329
374.
Ring Documentation, Release1.21.2 (continued from previous page) one try catch... catching error 39.10 Using braces to start and end different control structures We can use braces { } to start and end different control structures • If statement • For loop • Switch • While • Try-Catch Example: see "if statement.." + nl x = 1 if x = 1 { see "one" + nl elseif x=2 see "two" + nl elseif x=3 see "three" + nl } see "for loop.." + nl for t = 1 to 10 { see t } see nl see "switch..." + nl x = 1 switch x { on 1 see "one" + nl on 2 see "two" + nl } see "try catch..." + nl try { x = 1 / 0 catch see "catching error" + nl } Output: if statement.. one (continues on next page) 39.10. Using braces to start and end different control structures 330
375.
Ring Documentation, Release1.21.2 (continued from previous page) for loop.. 12345678910 switch... one try catch... catching error 39.11 Using ‘put’ and ‘get’ as ‘see’ and ‘give’ We can replace the ‘see’ keyword with the ‘put’ keyword. Also we can replace the ‘give’ keyword with the ‘get’ keyword. Example: put "Hello World" + nl put "Enter Your Name ? " Get Name Put "Hello " + Name 39.12 Using ‘case’ as ‘on’ in switch statements We can replace the ‘on’ keyword with ‘case’ keyword in the switch statement. Example (1) : for x=1 to 10 switch x case 1 put "one" + nl case 2 put "two" + nl case 3 put "three" + nl else put "else" + nl end end Example (2) : for x=1 to 10 { switch x { case 1 put "one" + nl case 2 put "two" + nl case 3 put "three" + nl else put "else" + nl } } 39.11. Using ‘put’ and ‘get’ as ‘see’ and ‘give’ 331
376.
Ring Documentation, Release1.21.2 39.13 Using ‘def’ as ‘func’ in functions/methods definition We can use the ‘def’ keyword as the ‘func’ keyword to define functions and methods. Example: one() two() def one put "one" + nl def two put "two" + nl 39.14 Using braces { } in Packages/Classes/Functions Example: load "stdlib.ring" import mypackage new myclass { myfunc() } package mypackage { class myclass { func myfunc { print("Hello, World!n") } } } 39.15 Using ‘break’/’continue’ keywords Instead of using Exit/Loop commands we can use Break/Continue Example: for t=1 to 10 { if t=3 { continue elseif t=5 break } ? t } Output: 39.13. Using ‘def’ as ‘func’ in functions/methods definition 332
377.
Ring Documentation, Release1.21.2 1 2 4 39.16 Using ‘end’ keyword after Packages/Classes/Functions Example: import mypackage new myclass { myfunc() } package mypackage class myclass def myfunc put "Hello, World!" end end end 39.17 Using ‘function’/’endfunction’ keywords Example: one() two() three() function one ? :one endfunction function two ? :two endfunction function three ? :three endfunction 39.18 Using ‘endif’/’endfor’/’endwhile’/’endswitch’/’endtry’ keywords Example: for t=1 to 10 if t=3 ? :three endif endfor 39.16. Using ‘end’ keyword after Packages/Classes/Functions 333
378.
Ring Documentation, Release1.21.2 39.19 Using ‘endpackage’/’endclass’/’endfunc’ keywords after Pack- ages/Classes/Functions Example: import mypackage new myclass { myfunc() } package mypackage class myclass func myfunc see "welcome" + nl endfunc endclass endpackage 39.20 Ignore new lines after keywords Starting from Ring 1.8 the compiler will ignore new lines after keywords that expect tokens after it Example: see " Hello, World! " test() func #======================# Test #======================# ? " Hello from the Test function " Output: Hello, World! Hello from the Test function 39.19. Using ‘endpackage’/’endclass’/’endfunc’ keywords after Packages/Classes/Functions 334
379.
Ring Documentation, Release1.21.2 39.21 Automatic loading for syntax files Starting from Ring 1.18 we have better syntax flexibility • Support running source code files with any extension • Automatic loading for (ringsyntax.ring) file that exist in the current folder For example in the the next screen shot We have (ringsyntax.ring) that translate some of the Ring keywords to Arabic language When we execute the file with Arabic name which means in English (hello.ring) Ring will automatically execute (ringsyntax.ring) using Load Syntax command Each Folder in the program could have it’s optional (ringsyntax.ring) file We can mix styles in the same project For Windows users, To use Arabic source code files with Ring, Set the language settings. 39.21. Automatic loading for syntax files 335
380.
Ring Documentation, Release1.21.2 39.22 Enable/Disable Hash Comments Starting from Ring 1.20 we have the next two commands supported by the Ring Scanner • EnableHashComments • DisableHashComments Example: DisableHashComments #define = 10 EnableHashComments # Just a comment DisableHashComments ? #define EnableHashComments # End of program 39.22. Enable/Disable Hash Comments 336
381.
CHAPTER FORTY INTRODUCTION TO THETYPE HINTS LIBRARY In this chapter we will learn about the Type Hints Library 40.1 Why Type Hints? Using this library we can add the type information to the source code which will be very useful for tools like • Code Editors • Static-Analysis Note: Ring is a dynamic language, No type checking will be done by the compiler. 40.2 Example The next example will use the Type Hints library load "typehints.ring" see sum(3,4) + nl ; see sayHello("Mahmoud"); int func sum(int x,int y) { return x+y ; } string func sayHello(string name) { return "Hello " + name ; } 337
382.
Ring Documentation, Release1.21.2 40.3 User Types The Type Hints library is very powerful and will support user types (Classes) automatically Example: load "typehints.ring" import mypackage test() { main([:one,:two,:three]) } myclass func test() { see "Testing User Types!" + nl return new myclass } package mypackage { public class myclass { public static void func main(list args) { see "welcome" + nl see args } } } 40.4 Using Types inside Code Also you can use the types inside the code (not only the function prototype) Example: load "typehints.ring" int sum = sum(3,4) string msg = sayHello("Mahmoud") see "Sum = " + sum + nl + msg + nl int func sum(int x,int y) { return x+y ; } string func sayHello(string name) { return "Hello " + name ; } 40.3. User Types 338
383.
Ring Documentation, Release1.21.2 40.5 Using Override We can use override or @override Example: load "typehints.ring" o = new MyNewLib { ? isGreaterThanTwo(10) ? isGreaterThanTwo(1) } class MyLib { boolean func isGreaterThanTwo(int x) { if x > 2 return true else return false ok } } class MyNewLib < MyLib { @override boolean func isGreaterThanTwo(int x) { ? "Using override" return x > 2 } } Output: Using override 1 Using override 0 40.6 Rules • To use the types in the function prototype you must use ‘(’ and ‘)’ around parameters • To use the types in the function code, You must set the variable value (Assignment). The next types are defined by the library # Low Level Types char unsigned signed int short (continues on next page) 40.5. Using Override 339
384.
Ring Documentation, Release1.21.2 (continued from previous page) long float double void byte boolean # High Level Types string list number object # Other public static abstract protected override @override 40.6. Rules 340
385.
CHAPTER FORTYONE THE TRACE LIBRARYAND THE INTERACTIVE DEBUGGER In this chapter we will learn about the Trace Library and the Interactive Debugger 41.1 Loading the Trace library To start using the Trace library, We must load it first! load "tracelib.ring" 41.2 Trace All Events The next example demonstrates the Trace library usage to trace all events. # Trace All Events trace(:AllEvents) see "Hello, world!" + nl see "Welcome" + nl see "How are you?" +nl mytest() new myclass { mymethod() } func mytest see "Message from mytest" + nl class myclass func mymethod see "Message from mymethod" + nl 341
386.
Ring Documentation, Release1.21.2 41.3 Trace control flow between functions The next example demonstrates the Trace library usage to trace the control flow between functions. Trace(:Functions) test1() func test1 see :test1 + nl test2() func test2 see :test2 + nl see test3() + nl func test3 see :test3 + nl return "test 3 output" 41.4 Pass Error The next example demonstrates the Trace library usage to pass an error! Trace(:PassError) test1() func test1 x = 10 see :test1 + nl test2() # Runtime Error! see "We can continue!" 41.5 Interactive Debugger The next example demonstrates the Trace library usage to use the Interactive Debugger Trace(:Debugger) test1() see "good bye!" + nl func test1 x = 10 see :test1 + nl t = 12 test2() # Runtime Error! see "After Error!" +nl (continues on next page) 41.3. Trace control flow between functions 342
387.
Ring Documentation, Release1.21.2 (continued from previous page) see "t = " see t see nl see "x = " see x see nl 41.6 Execute Program Line by Line The next example demonstrates the Trace library usage to execute the program line by line! Trace(:LineByLine) test1() func test1 x = 10 see :test1 + nl t = 12 test2() see "After Error!" +nl see "t = " + t + nl 41.7 BreakPoint The next example demonstrates the Trace library usage to stop at a breakpoint! test1() func test1 x = 10 see :test1 + nl t = 12 BreakPoint() see "After breakpoint!" +nl see "t = " + t + nl see "End of program!" + nl 41.8 Disable BreakPoints The next example demonstrates the Trace library usage and how to disable the Breakpoints! NoBreakPoints() test1() func test1 x = 10 see :test1 + nl t = 12 (continues on next page) 41.6. Execute Program Line by Line 343
388.
Ring Documentation, Release1.21.2 (continued from previous page) BreakPoint() see "After breakpoint!" +nl see "t = " + t + nl see "End of program!" + nl 41.9 Using the Interactive Debugger The next example uses a Breakpoint to open the Interactive Debugger! load "tracelib.ring" test1() func test1 x = 10 see :test1 + nl t = 12 BreakPoint() see "After breakpoint!" +nl see "t = " + t + nl see "End of program!" + nl Screen Shots: We have the Interactive Debugger at the Breakpoint! We can print the variables values 41.9. Using the Interactive Debugger 344
389.
Ring Documentation, Release1.21.2 We can change the variables values then continue execution We can run the Interactive Debugger in the Output Window 41.9. Using the Interactive Debugger 345
390.
CHAPTER FORTYTWO EMBEDDING RING INRING In this chapter we will learn about embedding Ring in Ring programs and applications. 42.1 Embedding Ring in Ring without sharing the State From Ring 1.0 we already have functions for embedding Ring in the C language. Also we can execute Ring code inside Ring programs using the eval() function. In this release we provide functions for embedding Ring in Ring programs without sharing the state. Advantages: (1) Quick integration for Ring programs and applications together without conflicts. (2) Execute and run Ring code in safe environments that we can trace. Example: pState = ring_state_init() ring_state_runcode(pState,"See 'Hello, World!'+nl") ring_state_runcode(pState,"x = 10") pState2 = ring_state_init() ring_state_runcode(pState2,"See 'Hello, World!'+nl") ring_state_runcode(pState2,"x = 20") ring_state_runcode(pState,"see x +nl") ring_state_runcode(pState2,"see x +nl") v1 = ring_state_findvar(pState,"x") v2 = ring_state_findvar(pState2,"x") see v1[3] + nl see V2[3] + nl ring_state_delete(pState) ring_state_delete(pState2) Output: Hello, World! Hello, World! 10 (continues on next page) 346
391.
Ring Documentation, Release1.21.2 (continued from previous page) 20 10 20 42.2 Serial Execution of Programs We can execute application after another application using ring_state_main() Example: chdir(exefolder()+"/../applications/formdesigner") ring_state_main('formdesigner.ring') chdir(exefolder()+"/../applications/cards") ring_state_main('cards.ring') 42.3 ring_state_setvar() Using ring_state_setvar() we can set variables value The value could be (String, Number, List or C Pointer) We need this function to quickly pass lists and C pointers to the Sub Ring Environment Syntax: ring_state_setvar(oState,cVariableName,Value) Example: load "guilib.ring" myapp = null win = null func main myapp = new qApp { win = new qWidget() { setWindowTitle("Advanced Example on using ring_state_setvar()") move(100,100) resize(600,400) new qPushButton(win) { setText("Test") setClickEvent("Test()") } # We need this because using load 'guilib.ring' in # the sub environment # Will create timers by Qt and closing the window # will not be enough to close the application oFilter = new qAllEvents(win) oFilter.setCloseEvent("myapp.quit()") (continues on next page) 42.2. Serial Execution of Programs 347
392.
Ring Documentation, Release1.21.2 (continued from previous page) win.installeventfilter(oFilter) show() } exec() } func test pState = ring_state_init() ring_state_runcode(pstate,"load 'guilib.ring'") ring_state_runcode(pState,"x = NULL") # Pass String ring_state_setvar(pState,"x","hello") ring_state_runcode(pState,"? x") # Pass Number ring_state_setvar(pState,"x",100) ring_state_runcode(pState,"? x") # Pass List ring_state_setvar(pState,"x",["one","two","three"]) ring_state_runcode(pState,"? x") # Pass Object # We can't pass the Ring Object (win) # Because Objects store pointers to the Class Information # And the class is related to the Parent Ring Environment # And the sub Ring environment can't access it # But we can pass C pointers like win.pObject ring_state_setvar(pState,"x",win.pObject) # Now we create the object again but using the same C pointer # So we have access to the Same window in the parent Ring environment ring_state_runcode(pState," new qWidget { pObject = x setwindowtitle('Message from the Sub Ring Environment') } ") ring_state_delete(pState) 42.4 ring_state_new() and ring_state_mainfile() Using ring_state_new() and ring_state_mainfile() we can run Ring programs from Ring programs But unlike ring_state_main(), Here we can control when to delete the Ring state! This is important when we run GUI programs from GUI programs Because they will share the GUI Library (RingQt), And In this case the caller will call qApp.Exec() So the sub program, will not stop and will return to the Main program Here deleting the State of the sub programs will lead to a problem when we run the sub program events So keeping the state is important for sub GUI programs hosted in GUI programs. Example: 42.4. ring_state_new() and ring_state_mainfile() 348
393.
Ring Documentation, Release1.21.2 load "guilib.ring" func main new qApp { win = new qWidget() { setWindowTitle("Test ring_state_mainfile()") resize(400,400) move(100,100) btn = new qPushButton(Win) { settext("test") setclickevent("mytest()") } show() } exec() } func mytest pState = ring_state_new() ring_state_mainfile(pState,"runprogram.ring") # Here we don't delete the state if we will run GUI application # So we can run the GUI application events // ring_state_delete(pState) If you will use this feature, remember to update the previous example based on your application needs So you can call ring_state_delete() at some point to avoid the memory leak! 42.5 Runtime Errors when Embedding Ring in Ring Starting from Ring 1.8 When embedding Ring in Ring, the error in the hosted environment will not close the host Example: ? "Start the test!" pState = ring_state_init() ring_state_runcode(pState," ? 'Let us try having an error' ? x") ring_state_delete(pState) ? "" ? "End of test!" Output: Start the test! Let us try having an error Line 1 Error (R24) : Using uninitialized variable : x (continues on next page) 42.5. Runtime Errors when Embedding Ring in Ring 349
394.
Ring Documentation, Release1.21.2 (continued from previous page) in file Ring_EmbeddedCode End of test! 42.6 ring_state_filetokens() function Starting from Ring 1.12 we have the ring_state_filetokens() function Using this function we can get all the tokens in the ring source code file. C_FILENAME = "test_tokens.ring" C_WIDTH = 12 # write the file write(C_FILENAME,' see "Hello, World!" ? 3*2+3 Name = "Ring" ? Name ') # Token Type C_KEYWORD = 0 C_OPERATOR = 1 C_LITERAL = 2 C_NUMBER = 3 C_IDENTIFIER = 4 C_ENDLINE = 5 # Keywords List aKEYWORDS = ["IF","TO","OR","AND","NOT","FOR","NEW","FUNC", "FROM","NEXT","LOAD","ELSE","SEE","WHILE","OK","CLASS","RETURN","BUT", "END","GIVE","BYE","EXIT","TRY","CATCH","DONE","SWITCH","ON","OTHER","OFF", "IN","LOOP","PACKAGE","IMPORT","PRIVATE","STEP","DO","AGAIN","CALL","ELSEIF", "PUT","GET","CASE","DEF","ENDFUNC","ENDCLASS","ENDPACKAGE", "CHANGERINGKEYWORD","CHANGERINGOPERATOR","LOADSYNTAX"] pState = ring_state_new() aList = ring_state_filetokens(pState,C_FILENAME) PrintTokens(aList) ring_state_delete(pState) func PrintTokens aList for aToken in aList switch aToken[1] on C_KEYWORD ? Width("Keyword",C_WIDTH) + ": " + aKeywords[0+aToken[2]] on C_OPERATOR ? Width("Operator",C_WIDTH) + ": " + aToken[2] on C_LITERAL ? Width("Literal",C_WIDTH) + ": " + aToken[2] on C_NUMBER (continues on next page) 42.6. ring_state_filetokens() function 350
395.
Ring Documentation, Release1.21.2 (continued from previous page) ? Width("Number",C_WIDTH) + ": " + aToken[2] on C_IDENTIFIER ? Width("Identifier",C_WIDTH) + ": " + aToken[2] on C_ENDLINE ? "EndLine" off next func Width cText,nWidth return cText+copy(" ",nWidth-len(cText)) Output: EndLine Keyword : SEE Literal : Hello, World! EndLine Operator : ? Number : 3 Operator : * Number : 2 Operator : + Number : 3 EndLine Identifier : name Operator : = Literal : Ring EndLine Operator : ? Identifier : name EndLine 42.7 ring_state_mainfile() and Ring Object File Starting from Ring 1.20 the ring_state_mainfile() is updated and support Ring Object Files (*.ringo) So, we can write a group of functions/classes then compile them to Ring Object File, send this file to another program- mer who can use the functions/classes inside his program after loading it in isolated embedded Ring State. File: mylib.ring ? "Hello from the object file" func test ? "Hello from test() function" Compiling the code to Ring Object File ring mylib.ring -go This will generate (mylib.ringo) File: use.ring 42.7. ring_state_mainfile() and Ring Object File 351
396.
Ring Documentation, Release1.21.2 pState = ring_state_new() ring_state_mainfile(pstate,"mylib.ringo") ring_state_runcode(pstate,"? 'We can call functions from Ring Object File!' ") ring_state_runcode(pstate,"test()") ring_state_delete(pState) ? :done In practice it’s expected that the programmer who will distribute the (*.ringo) file will distribute a simple *.ring file that create the Ring state, load the Ring Object and call functions/methods. Something like this File: commercialLib.ring mylib = new mylib class mylib pState = ring_state_new() ring_state_mainfile(pstate,"mylib.ringo") func test ring_state_runcode(pstate,"test()") func destroy ring_state_delete(pState) Then we can use this library like this load "commercialLib.ring" mylib.test() 42.8 Pause/Resume the Ring VM Starting from Ring 1.20 we can pause/resume the embedded Ring VM To pause the VM, just use the (Bye) command which as expected will end the execution but will store the nPC value (Program Counter) so using ring_state_resume() we can continue the execution at any time starting from this nPC value. Syntax: ring_state_resume(oState,[cPara|nPara],[lUseReturn]) The second parameter could be a string or number that we will push to the Stack The third parameter determine if we would like to execute the Return command before Resume or not. A common usage for this function exist in the (Try Ring Online) application which uses RingQt for WebAssembly. You will find the source code in the ring/tools/tryringonline folder. The usage of Pause/Resume using (Bye/ring_state_resume()) exist in the ring/tools/tryringonline/ringvm.ring file. This application uses Embedded Ring VM, and replace the Give command with a function like this 42.8. Pause/Resume the Ring VM 352
397.
Ring Documentation, Release1.21.2 func ringvm_give lActiveGive = True bye Once Ring VM find ringvm_give() function is defined, it will call it every time we use the Give command. It’s expected from this function to return a string that represent the value entered by the user. What happens here is using the (bye) command to stop the embedded Ring VM execution. Now, the Ring application that have this embedded Ring VM could provide a UI to get the value from the user and when the user press ENTER or click (Send) the application will resume the execution of the embedded Ring VM The next send() method is part of the RingVM class in the TryRingOnline application func send cInput if ! pState return :NoProgramIsRunning ok vVar = ring_state_findvar(pState,:lActiveGive) lActiveGive = vVar[RING_VAR_VALUE] if ! lActiveGive return :NoInputIsRequired ok lActiveGive = False ring_state_setvar(pState,:lActiveGive, False) ring_state_resume(pState,cInput,True) return :OutputIsReady 42.9 Rules These functions are considered advanced functions and exist for specific use-cases. If you are going to use these functions, you need to know the next rules. (1) ring_state_runcode() expect that the VM is already created and ready for use. So to be able to use this function you have to create the Ring State and the Ring VM using ring_state_init() function. So, using ring_state_new() alone instead of using ring_state_init() will not be enough because this function (ring_state_new()) doesn’t create the Ring VM. (2) Using ring_state_new() then using ring_state_mainfile() will create the Ring State and the Ring VM too so we can use ring_state_runcode() after that. But it’s not recommended to do that. i.e. It’s better to just use ring_state_mainfile() and generate all of the source code before using this function. (3) ring_state_runcode() is very similar to the Eval() function. It’s designed to delete the code after execution if this code doesn’t add new functions/classes. Also, this function uses specific execution loop to execute the generated byte-code. It’s similar to what happens when we have a GUI application and a button event. The event will be executed using it’s execution loop because the main-loop is already passed the control to the GUI library and can’t execute more instructions until the end of the GUI application. 42.9. Rules 353
398.
Ring Documentation, Release1.21.2 (4) The ring_state_resume() function is considered a low-level function and is designed to be used with ring_state_mainfile() function to resume the main-loop. This function is used in the TryRingOnline applica- tion. Using this function with the ring_state_runcode() which uses specific execution loop is not recommended and not supported at the design/implementation level. 42.9. Rules 354
399.
CHAPTER FORTYTHREE STDLIB FUNCTIONS In thischapter we are going to learn about functions in the stdlib.ring Before using the functions in the library, We must load the library first load "stdlib.ring" Instead of using stdlib.ring we can use stdlibcore.ring Using stdlibcore.ring we can use the StdLib functions (Without Classes) load "stdlibcore.ring" This is useful when developing standalone console applications Because using stdlib.ring (functions & classes) will load libraries like RingLibCurl, RingOpenSSL, etc. Also, Using stdlibclasses.ring we can load stdlib classes without loading functions or extensions like RingLibCurl, RingOpenSSL, etc. load "stdlibclasses.ring" 43.1 IsAppCompiled() Function check whether the application has been compiled using Ring2EXE Syntax: IsAppCompiled() ---> True/False Example: Load "stdlibcore.ring" Puts("Test IsAppCompiled()") if IsAppCompiled() see "Application has been compiled using Ring2EXE" else see "Application is running under Ring interpreter" ok 355
400.
Ring Documentation, Release1.21.2 43.2 AppArguments() Function Get the effective arguments passed to the Ring script Syntax: AppArguments() ---> The arguments as a list of strings Example: Load "stdlibcore.ring" # Application Arguments Puts("Test AppArguments()") argsList = AppArguments() argsCount = Len(argsList) if argsCount = 0 see "No arguments passed to the Ring script" + nl else see "Ring script arguments = " + nl + list2str(argsList) + nl ok 43.3 AppPath() Function Get the path of the application folder Syntax: AppPath() ---> The path as String Example: Load "stdlibcore.ring" # Application Path Puts("Test AppPath()") See AppPath() + nl 43.4 JustFilePath() Function Get the path of the file, remove the file name. Syntax: JustFilePath(cFile) ---> The path as String Example: load "stdlibcore.ring" see justfilePath("b:ringapplicationsrnoternote.ring") Output: 43.2. AppArguments() Function 356
401.
Ring Documentation, Release1.21.2 b:ringapplicationsrnote 43.5 JustFileName() Function Get the file, remove the file path. Syntax: JustFileName(cFile) ---> The file name as String Example: load "stdlibcore.ring" see justfileName("b:ringapplicationsrnoternote.ring") Output: rnote.ring 43.6 Value() Function create a copy from a list or object Syntax: value(List) ---> new list Example: Load "stdlibcore.ring" aList = 1:10 del(value(aList),1) # delete first item see aList # print numbers from 1 to 10 43.7 Times() Function Execute a Function nCount times Syntax: Times(nCount,function) Example: Load "stdlibcore.ring" Puts("Test Times()") Times ( 3 , func { see "Hello, World!" + nl } ) 43.5. JustFileName() Function 357
402.
Ring Documentation, Release1.21.2 43.8 Map() Function Execute a Function on each list item Syntax: Map(alist,function) ---> List Example: Load "stdlibcore.ring" Puts("Test Map()") See Map( 1:10, func x { return x*x } ) 43.9 Filter() Function Execute a Function on each list item to filter items Syntax: Filter(alist,function) ---> List Example: Load "stdlibcore.ring" Puts("Test Filter()") See Filter( 1:10 , func x { if x <= 5 return true else return false ok } ) 43.10 Reduce() Function Apply function cFunc to each result xResult from a list aList, return an accumulated value xResult The input list aList, the optional intial value xInitial and the output xResult, need to be the same Type Syntax: Reduce(aList,cFunc,xInitial) ---> final value Example: Load "stdlibcore.ring" ? Reduce( 1:3, func x, y { return x + y }, 0 ) ? Reduce( ["I","Love","Ring"], func x, y { return x + y }, "" ) Output: 6 ILoveRing 43.8. Map() Function 358
403.
Ring Documentation, Release1.21.2 43.11 Split() Function Convert string words to list items Syntax: Split(cstring,delimiter) ---> List Example: Load "stdlibcore.ring" Puts("Test Split()") See Split("one two three four five"," ") 43.12 SplitMany() Function Convert string words to list items. Allow many delimiters. Syntax: SplitMany(cstring,delimiters as string or list) --> List Example: Load "stdlibcore.ring" Puts("Test SplitMany()") See SplitMany("one,two,three,four and five"," ,") 43.13 Capitalized() Function Return a copy of a string with the first letter capitalized Syntax: Capitalized(string) ---> string Example: Load "stdlibcore.ring" Puts("Test Capitalized()") See capitalized("welcome to the Ring Programming Language") 43.11. Split() Function 359
404.
Ring Documentation, Release1.21.2 43.14 IsSpecial() Function Check whether a character is special or not Syntax: IsSpecial(char) ---> True/False Example: Load "stdlibcore.ring" Puts("Test Isspecial()") See "Isspecial = " + isSpecial("%") + nl 43.15 IsVowel() Function Check whether a character is vowel or not Syntax: IsVowel(char) ---> True/False Example: Load "stdlibcore.ring" Puts("Test Isvowel()") See "Isvowel = " + isVowel("c") + nl 43.16 LineCount() Function Return the lines count in a text file. Syntax: LineCount(cFileName) ---> Lines Count as number Example: Load "stdlibcore.ring" Puts("Test Linecount()") See "the number of lines = " + lineCount("test.ring") 43.14. IsSpecial() Function 360
405.
Ring Documentation, Release1.21.2 43.17 Factorial() Function Return the factorial of a number Syntax: Factorial(number) ---> number Example: Load "stdlibcore.ring" Puts("Test Factorial()") see "6 factorial is : " + Factorial(6) 43.18 Fibonacci() Function Return the fibonacci number Syntax: Fibonacci(number) ---> number Example: Load "stdlibcore.ring" Puts("Test Fibonacci()") see "6 Fibonacci is : " + Fibonacci(6) 43.19 IsPrime() Function Check whether a number is prime or not Syntax: isprime(number) ---> Number Example: Load "stdlibcore.ring" Puts("Test Isprime()") if isPrime(16) see "16 is a prime number" else see "16 is not a prime number" ok 43.17. Factorial() Function 361
406.
Ring Documentation, Release1.21.2 43.20 Sign() Function Returns an integer value indicating the sign of a number. Syntax: Sign(number) ---> number ( -1 = negative , 0 , 1 (positive) ) Example: Load "stdlibcore.ring" Puts("Test Sign()") see "sign of 12 is = " + sign(12) + nl 43.21 List2File() Function Write list items to text file (each item in new line). Syntax: List2File(aList,cFileName) Example: Load "stdlibcore.ring" # Test List2File Puts("Test List2File()") list2file(1:100,"myfile.txt") 43.22 File2List() Function Read text file and convert lines to list items Syntax: File2List(cFileName) ---> List Example: Load "stdlibcore.ring" # Test File2List Puts("Test File2List()") see len(file2list("myfile.txt")) 43.20. Sign() Function 362
407.
Ring Documentation, Release1.21.2 43.23 StartsWith() Function Returns true if the given string starts with the specified substring. Leading white spaces are ignored. Syntax: StartsWith(string, substring) ---> True/False Example: Load "stdlibcore.ring" Puts("Test Startswith()") see Startswith("CalmoSoft", "Calmo") + nl 43.24 EndsWith() Function Returns true if the given string ends with the specified substring. Trailing white spaces are ignored. Syntax: Endswith(string, substring) ---> True/False Example: Load "stdlibcore.ring" Puts("Test Endswith()") see endsWith("CalmoSoft", "Soft") + nl 43.25 GCD() Function Finding of the greatest common divisor of two integers. Syntax: Gcd(number,number) ---> number Example: Load "stdlibcore.ring" Puts("Test Gcd()") see gcd (24, 32) + nl 43.23. StartsWith() Function 363
408.
Ring Documentation, Release1.21.2 43.26 LCM() Function Compute the least common multiple of two integers. Syntax: lcm(number,number) ---> number Example: Load "stdlibcore.ring" Puts("Test Lcm()") see Lcm(24,36) + nl 43.27 SumList() Function Compute the sum of a list of integers. Syntax: sumlist(list) ---> number Example: Load "stdlibcore.ring" Puts("Test Sumlist()") aList = [1,2,3,4,5] see Sumlist(aList) + nl 43.28 ProdList() Function Compute the product of a list of integers. Syntax: prodlist(list) ---> number Example: Load "stdlibcore.ring" Puts("Test Prodlist()") aList = [1,2,3,4,5] see Prodlist(aList) + nl 43.26. LCM() Function 364
409.
Ring Documentation, Release1.21.2 43.29 EvenOrOdd() Function Test whether an integer is even or odd. Result of test (1=odd 2=even). Syntax: evenorodd(number) ---> 1 (odd) or 2 (even) Example: Load "stdlibcore.ring" Puts("Test Evenorodd()") nr = 17 see Evenorodd(nr) + nl 43.30 Factors() Function Compute the factors of a positive integer. Syntax: factors(number) ---> list Example: Load "stdlibcore.ring" Puts("Test Factors()") n = 45 aList = factors(n) see "Factors of " + n + " = " for i = 1 to len(aList) see "" + aList[i] + " " next 43.31 IsPalindrome() Function Check if a sequence of characters is a palindrome or not. Syntax: IsPalindrome(String) ---> True/False Example: Load "stdlibcore.ring" Puts("Test IsPalindrome()") (continues on next page) 43.29. EvenOrOdd() Function 365
410.
Ring Documentation, Release1.21.2 (continued from previous page) cString = "radar" see IsPalindrome(cString) 43.32 IsLeapYear() Function Check whether a given year is a leap year in the Gregorian calendar. Syntax: Isleapyear(number) ---> True/False Example: Load "stdlibcore.ring" Puts("Test Isleapyear()") year = 2016 if Isleapyear(year) see "" + year + " is a leap year." else see "" + year + " is not a leap year." ok 43.33 BinaryDigits() Function Compute the sequence of binary digits for a given non-negative integer. Syntax: binarydigits(number) ---> string Example: Load "stdlibcore.ring" Puts("Test Binarydigits()") b = 35 see "Binary digits of " + b + " = " + Binarydigits(b) 43.34 MatrixMulti() Function Multiply two matrices together. Syntax: Matrixmulti(List,List) ---> List Example: 43.32. IsLeapYear() Function 366
411.
Ring Documentation, Release1.21.2 Load "stdlibcore.ring" # Multiply two matrices together. Puts("Test Matrixmulti()") A = [[1,2,3], [4,5,6], [7,8,9]] B = [[1,0,0], [0,1,0], [0,0,1]] see Matrixmulti(A, B) 43.35 MatrixTrans() Function Transpose an arbitrarily sized rectangular Matrix. Syntax: Matrixtrans(List) ---> List Example: Load "stdlibcore.ring" # Transpose an arbitrarily sized rectangular Matrix. Puts("Test Matrixtrans()") matrix = [[78,19,30,12,36], [49,10,65,42,50], [30,93,24,78,10], [39,68,27,64,29]] see Matrixtrans(matrix) 43.36 DayOfWeek() Function Return the day of the week of given date. (yyyy-mm-dd) Syntax: dayofweek(string) ---> string Example: Load "stdlibcore.ring" # Return the day of the week of given date. Puts("Test Dayofweek()") date = "2016-04-24" see "Data : " + date + " - Day : " + Dayofweek(date) + nl 43.35. MatrixTrans() Function 367
412.
Ring Documentation, Release1.21.2 43.37 Permutation() Function Generates all permutations of n different numerals. Syntax: permutation(list) Example: Load "stdlibcore.ring" # Generates all permutations of n different numerals Puts("Test Permutation()") list = [1, 2, 3, 4] for perm = 1 to 24 for i = 1 to len(list) see list[i] + " " next see nl Permutation(list) next 43.38 ReadLine() Function Read line from file Syntax: readline(fp) ---> string Example: Load "stdlibcore.ring" # Read a file line by line. Puts("Test Readline()") fp = fopen("test.ring","r") while not feof(fp) See Readline(fp) end fclose(fp) 43.39 SubString() Function Return a position of a substring starting from a given position in a string. Syntax: Substring(str,substr,npos) ---> string Example: 43.37. Permutation() Function 368
413.
Ring Documentation, Release1.21.2 Load "stdlibcore.ring" # Return a position of a substring starting from a given position in a string. Puts("Test Substring()") a = "abcxyzqweabc" b = "abc" i = 4 see substring(a,b,i) 43.40 ChangeString() Function Change substring from given position to a given position with another substring. Syntax: Changestring(cString, nPos1, nPos2, cSubstr) ---> cString Example: Load "stdlibcore.ring" # Change substring from given position for given position with a substring. Puts("Test Changestring()") see Changestring("Rmasdg",2,5,"in") # Ring 43.41 Sleep() Function Sleep for the given amount of time. Syntax: sleep(nSeconds) Example: Load "stdlibcore.ring" Puts("Test Sleep()") see "Wait 3 Seconds!" Sleep(3) see nl 43.40. ChangeString() Function 369
414.
Ring Documentation, Release1.21.2 43.42 IsMainSourceFile() Function Check if the current file is the main source file Syntax: IsMainSourceFile() ---> True/False Example: Load "stdlibcore.ring" if ismainsourcefile() # code ok 43.43 MakeDir() Function Make Directory Syntax: MakeDir(String) Example: Load "stdlibcore.ring" # Create Directory puts("create Directory : myfolder") makedir("myfolder") 43.44 Fsize() Function The function return the file size in bytes. Syntax: FSize(File Handle) ---> Number (File Size in Bytes) 43.45 TrimAll() Function Remove all spaces and tabs characters from a string Syntax: TrimAll(cString) ---> cString # Without Spaces and Tabs 43.42. IsMainSourceFile() Function 370
415.
Ring Documentation, Release1.21.2 43.46 TrimLeft() Function Remove all spaces and tabs characters from the left side of a string Syntax: TrimLeft(cString) ---> cString # Without Spaces and Tabs from the left side 43.47 TrimRight() Function Remove all spaces and tabs characters from the right side of a string Syntax: TrimRight(cString) ---> cString # Without Spaces and Tabs from the right side 43.48 EpochTime() Function Return the Epoch Time Syntax: EpochTime(cDate,cTime) ---> nEpochTime Example: see EpochTime( Date(), Time() ) 43.49 SystemCmd() Function We can execute system commands using the SystemCmd() function that outputs to a variable Syntax: SystemCmd(cCommand) Example: cYou = SystemCmd("whoami") # User Name logged in is output a variable cThem = SystemCmd("dir c:Users") # Directory List is output to a variable 43.46. TrimLeft() Function 371
416.
Ring Documentation, Release1.21.2 43.50 ListAllFiles() Function Using this function we can quickly do a process on a group of files in a folder and it’s sub folders. Syntax: ListAllFiles(cFolder,cExtension) ---> List of Files Example: aList = ListAllFiles("c:/ring/ringlibs","ring") # *.ring only aList = sort(aList) see aList Example: see listallfiles("b:/ring/libraries/weblib","") # All Files 43.51 SystemSilent() Function We can execute system commands using the SystemSilent() function to avoid displaying the output! Syntax: SystemSilent(cCommand) 43.52 OSCreateOpenFolder() Function Create folder then change the current folder to this new folder Syntax: OSCreateOpenFolder(cCommand) 43.53 OSCopyFolder() Function Copy folder to the current folder Parameters : The path to the parent folder and the folder name to copy Syntax: OSCopyFolder(cParentFolder,cFolderName) Example To copy the folder b:ringringlibsstdlib to the current folder OSCopyFolder("b:ringringlibs","stdlib") 43.50. ListAllFiles() Function 372
417.
Ring Documentation, Release1.21.2 43.54 OSDeleteFolder() Function Delete Folder in the current Directory Syntax: OSDeleteFolder(cFolderName) 43.55 OSCopyFile() Function Copy File to the current directory Syntax: OSCopyFile(cFileName) 43.56 OSDeleteFile() Function Delete File Syntax: OSDeleteFile(cFileName) 43.57 OSRenameFile() Function Rename File Syntax: OSRenameFile(cOldFileName,cNewFileName) 43.58 List2Code() Function This function convert a Ring list during the runtime to Ring source code that we can save to source files. The list may contains strings, numbers or sub lists. Example: load "stdlibcore.ring" aList = 1:10 ? list2Code(aList) Output: [ 1,2,3,4,5,6,7,8,9,10 ] 43.54. OSDeleteFolder() Function 373
418.
Ring Documentation, Release1.21.2 43.59 Str2ASCIIList() Function Convert a string of bytes to a list of numbers where each item represent the ASCII code of one byte in the string. Syntax: Str2ASCIIList(String) ---> List of numbers 43.60 ASCIIList2Str() Function Convert a list of numbers where each item represent the ASCII code of one byte to a string of bytes. Syntax: ASCIIList2Str(List of numbers) ---> String Example: load "stdlibcore.ring" cStr = "MmMm" aList = Str2ASCIILIST(cStr) ? aList cStr2 = ASCIIList2Str(aList) ? cStr2 ? len(cStr2) Output: 77 109 77 109 MmMm 4 43.61 IsListContainsItems() Function Syntax: IsListContainsItems(aParent,aChild) ----> True/False Example: load "stdlibcore.ring" aList1 = "a":"z" aList2 = [:h,:l,:p,:u] ? IsListContainsItems(aList1,aList2) 43.59. Str2ASCIIList() Function 374
419.
Ring Documentation, Release1.21.2 43.62 IsBetween() Function Syntax: IsBetween(nNumber,nMin,nMax) ----> True/False Example: load "stdlibcore.ring" ? isBetween(1,3,4) ? isBetween(1,-3,4) ? isBetween(4,1,6) ? isBetween(4,3,4) 43.63 TimeInfo() Function Syntax: TimeInfo(cInformation) ----> String The cInformation value could be :hour_24 :hour_12 :minutes :seconds :time :day_short :day_long :month_short :month_long :date_time :day :day_year :month_year :am_pm :week_year :day_week :date :year_century :year :time_zone :percent_sign Example: load "stdlibcore.ring" ? timeInfo(:date) ? timeInfo(:time) ? timeInfo(:hour_12) 43.62. IsBetween() Function 375
420.
Ring Documentation, Release1.21.2 43.64 RandomList() Function Syntax: RandomList(aList) --> List contains the same items using Random order Example: load "stdlibcore.ring" aList = 1:5 ? RandomList(aList) 43.65 RandomItem() Function Pick an item from a list (Random Choice) Syntax: RandomItem(aList) --> Item Example: load "stdlibcore.ring" aList = 1:5 ? RandomItem(aList) 43.66 CheckEquality() Function Check if two items are equal. Deep comparison is performed if the two items are lists Return 1 if both items are equal and 0 otherwise Syntax: CheckEquality(aItem1,aItem2) --> value = 1 if aItem1 = aItem2 value = 0 if aItem1 != aItem2 Example: load "stdlibcore.ring" aList1 = ["one", 2, [3]] aList2 = ["one", 2] aList2 + [3] ? CheckEquality(aList1,aList2) 43.64. RandomList() Function 376
421.
Ring Documentation, Release1.21.2 43.67 NumOrZero() Function This is a new function added to stdlibcore.ring Using this function we get a number as output (No runtime errors) Example: load "stdlibcore.ring" ? numorzero(10) ? numorzero("10") ? numorzero("10.2") ? numorzero("10.2 abc") ? numorzero("What") ? numorzero([10]) ? numorzero(new point) class point Output: 10 10 10.20 0 0 0 0 43.67. NumOrZero() Function 377
422.
CHAPTER FORTYFOUR STDLIB CLASSES In thischapter we are going to learn about the classes in the stdlib.ring • StdBase Class • String Class • List Class • Stack Class • Queue Class • HashTable Class • Tree Class • Math Class • DateTime Class • File Class • System Class • Debug Class • DataType Class • Conversion Class • ODBC Class • MySQL Class • SQLite Class • PostgreSQL Class • Security Class • Internet Class 378
423.
Ring Documentation, Release1.21.2 44.1 StdBase Class Attributes: • vValue : Object Value Methods: Method Description/Output Init(x) Set vValue Attribute to x value Print() Print vValue PrintLn() Print vValue then New Line Size() return number represent the size of vValue Value() return vValue Set(x) Call Init(x) 44.2 String Class Parent Class : StdBase Class Methods: Method Description/Output Init(String|Number|List) Lower() New String - Lower case characters Upper() New String - Upper case characters Left(x) New String - contains x characters from the left Right(x) New String - contains x characters from the right Lines() Number - Lines count Trim() New String - Remove Spaces Copy(x) New String - repeat string x times strcmp(cString) Compare string with cString tolist() List (String Lines to String Items) tofile(cFileName) Write string to file mid(nPos1,nPos2) New String - from nPos1 to nPos2 getfrom(nPos1) New String - from nPos1 to the end of the string replace(cStr1,cStr2,lCase) New String - Replace cStr1 with cStr2 , lCase (True=Match Case) split() List - Each Word as list item startswith(substring) Return true if the start starts with a substring endswith(substring) Return true if the start ends with a substring Example: Load "stdlib.ring" See "Testing the String Class" + nl oString = new string("Hello, World!") oString.println() oString.upper().println() (continues on next page) 44.1. StdBase Class 379
424.
Ring Documentation, Release1.21.2 (continued from previous page) oString.lower().println() oString.left(5).println() oString.right(6).println() oString = new string("Hi" + nl + "Hello" ) See oString.lines() + nl oString = new string(" Welcome ") oString.println() oString.trim().println() oString = new string("Hello! ") oString.copy(3).println() see oString.strcmp("Hello! ") + nl see oString.strcmp("Hello ") + nl see oString.strcmp("Hello!! ") + nl oString = new string(["one","two","three"]) oString.print() see oString.lines() + nl oString = new String(1234) oString.println() oString = new String("one"+nl+"two"+nl+"three") aList = oString.tolist() see "List Items" + nl See aList oString = new String( "Welcome to the Ring programming language") See "the - position : " + oString.pos("the") + nl oString = oString.getfrom(oString.pos("Ring")) oString.println() oString.mid(1,4).println() oString = oString.replace("Ring","***Ring***",true) oString.println() oString = oString.replace("ring","***Ring***",false) oString.println() oString1 = new string("First") oString2 = new string("Second") oString = oString1 + oString2 oString.println() oString = oString1 * 3 oString.println() for t in ostring see t next oString.tofile("test.txt") oString = new string("one two three") see nl see ostring.split() oString { set("Hello") println() set("How are you?") println() } Output: Testing the String Class Hello, World! HELLO, WORLD! hello, world! (continues on next page) 44.2. String Class 380
425.
Ring Documentation, Release1.21.2 (continued from previous page) Hello World! 2 Welcome Welcome Hello! Hello! Hello! 0 1 -1 one two three 4 1234 List Items one two three the - position : 12 Ring programming language Ring ***Ring*** programming language ******Ring****** programming language FirstSecond FirstFirstFirst FirstFirstFirst one two three Hello How are you? 44.3 List Class Parent Class : StdBase Class Methods: Method Description/Output Init(String|List) Add(Value) Add item to the list Delete(nIndex) Delete item from the list Item(nIndex) Get item from the list First() Get the first item in the list Last() Get the last item in the list Set(nIndex,Value) Set item value FindInColumn(nCol,Value) Find item in a column Sort() Sort items - return new list Reverse() Reverse items - return new list Insert(nIndex,Value) Inset Item after nIndex 44.3. List Class 381
426.
Ring Documentation, Release1.21.2 example: Load "stdlib.ring" oList = new list ( [1,2,3] ) oList.Add(4) oList.print() see oList.item(1) + nl oList.delete(4) oList.print() see oList.first() + nl see oList.last() + nl oList { set(1,"one") set(2,"two") set(3,"three") print() } see oList.find("two") + nl oList.sort().print() oList.reverse().print() oList.insert(2,"nice") oList.print() oList = new list ( [ [1,"one"],[2,"two"],[3,"three"] ] ) see copy("*",10) + nl oList.print() see "Search two : " + oList.findincolumn(2,"two") + nl see "Search 1 : " + oList.findincolumn(1,1) + nl oList = new list ( [ "Egypt" , "USA" , "KSA" ] ) for x in oList see x + nl next oList = new list ( [1,2,3,4] ) oList + [5,6,7] oList.print() oList = new list ( ["one","two"] ) oList2 = new list ( ["three","four"] ) oList + oList2 oList.print() output: 1 2 3 4 1 1 2 3 1 3 one two three 2 one three two (continues on next page) 44.3. List Class 382
427.
Ring Documentation, Release1.21.2 (continued from previous page) three two one one two nice three ********** 1 one 2 two 3 three Search two : 2 Search 1 : 1 Egypt USA KSA 1 2 3 4 5 6 7 one two three four 44.4 Stack Class Parent Class : List Class Methods: Method Description/Output Init(String|Number|List) Push(Value) Push item to the stack Pop() Pop item from the stack Print() Print the stack items example: Load "stdlib.ring" oStack = new Stack oStack.push(1) (continues on next page) 44.4. Stack Class 383
428.
Ring Documentation, Release1.21.2 (continued from previous page) oStack.push(2) oStack.push(3) see oStack.pop() + nl see oStack.pop() + nl see oStack.pop() + nl oStack.push(4) see oStack.pop() + nl oStack { push("one") push("two") push("three") } oStack.print() output: 3 2 1 4 three two one 44.5 Queue Class Parent Class : List Class Methods: Method Description/Output Init(String|Number|List) Remove() Remove item from the Queue. example: Load "stdlib.ring" oQueue = new Queue oQueue.add(1) oQueue.add(2) oQueue.add(3) see oQueue.remove() + nl see oQueue.remove() + nl see oQueue.remove() + nl oQueue.add(4) see oQueue.remove() + nl oQueue { add("one") add("two") add("three") } oQueue.print() output: 1 2 (continues on next page) 44.5. Queue Class 384
429.
Ring Documentation, Release1.21.2 (continued from previous page) 3 4 one two three 44.6 HashTable Class Parent Class : List Class Methods: Method Description/Output Init(List) Add(cKey,Value) Add item to the HashTable Set(cKey,Value) Set item value using the Key GetValue(cKey) Get item value using the Key Contains(cKey) Check if the HashTable contains item using the Key Index(cKey) Get the item index using the Key example: Load "stdlib.ring" ohashtable = new hashtable See "Test the hashtable Class Methods" + nl ohashtable { Add("Egypt","Cairo") Add("KSA","Riyadh") see self["Egypt"] + nl see self["KSA"] + nl see contains("Egypt") + nl see contains("USA") + nl see index("KSA") + NL print() delete(index("KSA")) see copy("*",60) + nl print() } output: Test the hashtable Class Methods Cairo Riyadh 1 0 2 Egypt (continues on next page) 44.6. HashTable Class 385
430.
Ring Documentation, Release1.21.2 (continued from previous page) Cairo KSA Riyadh ************************************************************ Egypt Cairo 44.7 Tree Class Data: Attribute Description Data Node Value Children Children List Methods: Method Description/Output set(value) Set the node value. value() Get the node value. Add(value) Add new child. parent() Get the parent node. print() Print the tree nodes. example: Load "stdlib.ring" otree = new tree See "Test the tree Class Methods" + nl otree { set("The first step") # set the root node value see value() + nl Add("one") Add("two") Add("three") { Add("3.1") Add("3.2") Add("3.3") see children } see children oTree.children[2] { Add("2.1") Add("2.2") Add("2.3") { Add("2.3.1") Add("2.3.2") Add("test") } } (continues on next page) 44.7. Tree Class 386
431.
Ring Documentation, Release1.21.2 (continued from previous page) oTree.children[2].children[3].children[3].set("2.3.3") } see copy("*",60) + nl oTree.print() output: Test the tree Class Methods The first step data: 3.1 parent: List... children: List... data: 3.2 parent: List... children: List... data: 3.3 parent: List... children: List... data: one parent: List... children: List... data: two parent: List... children: List... data: three parent: List... children: List... ************************************************************ one two 2.1 2.2 2.3 2.3.1 2.3.2 2.3.3 three 3.1 3.2 3.3 44.7. Tree Class 387
432.
Ring Documentation, Release1.21.2 44.8 Math Class Methods: Method Description sin(x) Returns the sine of an angle of x radians cos(x) Returns the cosine of an angle of x radians tan(x) Returns the tangent of an angle of x radians asin(x) Returns the principal value of the arc sine of x, expressed in radians acos(x) Returns the principal value of the arc cosine of x, expressed in radians atan(x) Returns the principal value of the arc tangent of x, expressed in radians atan2(y,x) Returns the principal arc tangent of y/x, in the interval [-pi,+pi] radians sinh(x) Returns the hyperbolic sine of x radians cosh(x) Returns the hyperbolic cosine of x radians tanh(x) Returns the hyperbolic tangent of x radians exp(x) Returns the value of e raised to the xth power log(x) Returns the natural logarithm of x log10(x) Returns the common logarithm (base-10 logarithm) of x ceil(x) Returns the smallest integer value greater than or equal to x floor(x) Returns the largest integer value less than or equal to x fabs(x) Returns the absolute value of x. pow(x,y) Returns x raised to the power of y sqrt(x) Returns the square root of x random(x) Returns a random number in the range [0,x] unsigned(n,n,c) Perform operation using unsigned numbers decimals(n) Determine the decimals digits after the point in float/double numbers example: Load "stdlib.ring" oMath = new Math See "Test the Math Class Methods" + nl See "Sin(0) = " + oMath.sin(0) + nl See "Sin(90) radians = " + oMath.sin(90) + nl See "Sin(90) degree = " + oMath.sin(90*3.14/180) + nl See "Cos(0) = " + oMath.cos(0) + nl See "Cos(90) radians = " + oMath.cos(90) + nl See "Cos(90) degree = " +oMath. cos(90*3.14/180) + nl See "Tan(0) = " + oMath.tan(0) + nl See "Tan(90) radians = " + oMath.tan(90) + nl See "Tan(90) degree = " + oMath.tan(90*3.14/180) + nl See "asin(0) = " + oMath.asin(0) + nl See "acos(0) = " + oMath.acos(0) + nl See "atan(0) = " + oMath.atan(0) + nl See "atan2(1,1) = " +oMath. atan2(1,1) + nl (continues on next page) 44.8. Math Class 388
433.
Ring Documentation, Release1.21.2 (continued from previous page) See "sinh(0) = " + oMath.sinh(0) + nl See "sinh(1) = " + oMath.sinh(1) + nl See "cosh(0) = " + oMath.cosh(0) + nl See "cosh(1) = " + oMath.cosh(1) + nl See "tanh(0) = " + oMath.tanh(0) + nl See "tanh(1) = " + oMath.tanh(1) + nl See "exp(0) = " + oMath.exp(0) + nl See "exp(1) = " + oMath.exp(1) + nl See "log(1) = " + oMath.log(1) + nl See "log(2) = " + oMath.log(2) + nl See "log10(1) = " + oMath.log10(1) + nl See "log10(2) = " + oMath.log10(2) + nl See "log10(10) = " + oMath.log10(10) + nl See "Ceil(1.12) = " + oMath.Ceil(1.12) + nl See "Ceil(1.72) = " + oMath.Ceil(1.72) + nl See "Floor(1.12) = " + oMath.floor(1.12) + nl See "Floor(1.72) = " + oMath.floor(1.72) + nl See "fabs(1.12) = " + oMath.fabs(1.12) + nl See "fabs(1.72) = " + oMath.fabs(1.72) + nl See "pow(2,3) = " + oMath.pow(2,3) + nl see "sqrt(16) = " + oMath.sqrt(16) + nl for x = 1 to 20 see "Random number Max (100) : " + oMath.random(100) + nl next x = 1.1234567890123 for d = 0 to 14 oMath.decimals(d) see x + nl next cKey = "hello" h = 0 for x in cKey h = oMath.unsigned(h,ascii(x),"+") h = oMath.unsigned(h,oMath.unsigned(h,10,"<<"),"+") r = oMath.unsigned(h,6,">>") h = oMath.unsigned(h, r,"^") next h = oMath.unsigned(h,oMath.unsigned(h,3,"<<"),"+") h = oMath.unsigned(h,oMath.unsigned(h,11,">>"),"^") h = oMath.unsigned(h,oMath.unsigned(h,15,"<<"),"+") see "Hash : " + h 44.8. Math Class 389
434.
Ring Documentation, Release1.21.2 output: Test the Math Class Methods Sin(0) = 0 Sin(90) radians = 0.89 Sin(90) degree = 1.00 Cos(0) = 1 Cos(90) radians = -0.45 Cos(90) degree = 0.00 Tan(0) = 0 Tan(90) radians = -2.00 Tan(90) degree = 1255.77 asin(0) = 0 acos(0) = 1.57 atan(0) = 0 atan2(1,1) = 0.79 sinh(0) = 0 sinh(1) = 1.18 cosh(0) = 1 cosh(1) = 1.54 tanh(0) = 0 tanh(1) = 0.76 exp(0) = 1 exp(1) = 2.72 log(1) = 0 log(2) = 0.69 log10(1) = 0 log10(2) = 0.30 log10(10) = 1 Ceil(1.12) = 2 Ceil(1.72) = 2 Floor(1.12) = 1 Floor(1.72) = 1 fabs(1.12) = 1.12 fabs(1.72) = 1.72 pow(2,3) = 8 sqrt(16) = 4 Random number Max (100) : 87 Random number Max (100) : 49 Random number Max (100) : 99 Random number Max (100) : 58 Random number Max (100) : 15 Random number Max (100) : 46 Random number Max (100) : 37 Random number Max (100) : 64 Random number Max (100) : 73 Random number Max (100) : 35 Random number Max (100) : 89 Random number Max (100) : 80 Random number Max (100) : 20 Random number Max (100) : 33 Random number Max (100) : 44 Random number Max (100) : 89 (continues on next page) 44.8. Math Class 390
435.
Ring Documentation, Release1.21.2 (continued from previous page) Random number Max (100) : 82 Random number Max (100) : 94 Random number Max (100) : 83 Random number Max (100) : 68 1 1.1 1.12 1.123 1.1235 1.12346 1.123457 1.1234568 1.12345679 1.123456789 1.1234567890 1.12345678901 1.123456789012 1.1234567890123 1.12345678901230 Hash : 3372029979.00000000000000 44.9 DateTime Class Methods: Method Description/Output clock() The number of clock ticks from program start. time() Get the system time. date() Get the date. timelist() List contains the date and the time information. adddays(cDate,nDays) Return Date from cDate and after nDays diffdays(cDate1,cDate2) Return the Number of days (cDate1 - cDate2) example: Load "stdlib.ring" oDateTime = new datetime See "Test the datetime Class Methods" + nl See "Calculate performance" + nl t1 = oDateTime.clock() for x = 1 to 1000000 next see oDateTime.clock() - t1 + nl See "Time : " + oDateTime.time() + nl See "Date : " + oDateTime.date() + nl (continues on next page) 44.9. DateTime Class 391
436.
Ring Documentation, Release1.21.2 (continued from previous page) See oDateTime.TimeList() See "Month Name : " + oDateTime.TimeList()[4] cDate = oDateTime.date() see cDate + nl cDate = oDateTime.adddays(cDate,10) see cDate + nl cDate1 = oDateTime.date() see cDate1 + nl cDate2 = oDateTime.adddays(cDate1,10) see cDate2 + nl see "DiffDays = " + oDateTime.diffdays(cDate1,cDate2) + nl see "DiffDays = " + oDateTime.diffdays(cDate2,cDate1) + nl output: Test the datetime Class Methods Calculate performance 85 Time : 02:53:35 Date : 31/08/2016 Wed Wednesday Aug August 08/31/16 02:53:35 31 02 02 244 08 53 AM 35 35 3 08/31/16 02:53:35 16 2016 Arab Standard Time % Month Name : August31/08/2016 10/09/2016 31/08/2016 10/09/2016 DiffDays = -10 DiffDays = 10 44.9. DateTime Class 392
437.
Ring Documentation, Release1.21.2 44.10 File Class Methods: Method Description/Output read(cFileName) Read the file content write(cFileName,cStr) Write string to file dir(cFolderPath) Get the folder contents (files & sub folders) rename(cOld,cNew) Rename files using the Rename() function remove(cFileName) Delete a file using the Remove() function open(cFileName,cMode) Open a file using the Fopen() function close() Close file flush() Flushes the output buffer of a stream reopen(cFileName,cMode) Open another file using the same file handle tempfile() Creates a temp. file (binary). seek(noffset,nwhence) Set the file position of the stream tell() Know the current file position of a stream rewind() Set the file position to the beginning of the file getpos() Get handle to the current file position setpos(poshandle) Set the current file position clearerr() Clear the EOF error and the error indicators of a stream eof() Test the end-of-file indicator error() Test the error indicator perror(cErrorMessage) Print error message to the stderr getc() Get the next character from the stream gets(nsize) Read new line from the stream putc(cchar) Write a character to the stream puts(cStr) Write a string to the stream ungetc(cchar) Push a character to the stream fread(nsize) Read data from a stream fwrite(cString) Write data to a stream exists(cFileName) Check if a file exists example: Load "stdlib.ring" ofile = new file See "Test the file Class Methods" + nl see ofile.read(filename()) see nl ofile.open(filename(),"r") see ofile.gets(100) + nl ofile.close() 44.10. File Class 393
438.
Ring Documentation, Release1.21.2 44.11 System Class Methods: Method Description/Output system() Execute system commands sysget() Get environment variables ismsdos() Check if the operating system is MSDOS or not iswindows() Check if the operating system is Windows or not iswindows64() Check if the operating system is Windows 64bit or not isunix() Check if the operating system is Unix or not ismacosx() Check if the operating system is macOS or not islinux() Check if the operating system is Linux or not isfreebsd() Check if the operating system is FreeBSD or not isandroid() Check if the operating system is Android or not windowsnl() Get the windows new line string sysargv() Get the command line arguments passed to the ring script filename() Get the active source file example: Load "stdlib.ring" oSystem = new System See "Test the System Class Methods" + nl oSystem.system("dir") see oSystem.sysget("path") + nl see oSystem.ismsdos() + nl see oSystem.iswindows() + nl see oSystem.iswindows64() + nl see oSystem.isunix() + nl see oSystem.ismacosx() + nl see oSystem.islinux() + nl see oSystem.isfreebsd() + nl see oSystem.isandroid() + nl see oSystem.windowsnl() + nl see oSystem.sysargv() + nl see oSystem.filename() + nl 44.12 Debug Class Methods: Method Description/Output eval(cCode) Execute code during the runtime from string. raise(cError) Raise an exception. assert(cCondition) Test condition before executing the code. 44.11. System Class 394
439.
Ring Documentation, Release1.21.2 example: Load "stdlib.ring" oDebug = new Debug See "Test the Debug Class Methods" + nl oDebug.eval("see 'Hello'+nl") try x = 10 oDebug.assert(x=11) catch see "assert" + nl done raise("Error!") 44.13 DataType Class Methods: Method Description/Output isstring(vValue) We can know if the value is a string or not. isnumber(vValue) We can know if the value is a number or not. islist(vValue) We can know if the value is a list or not. type(vValue) Know the type of a value isnull(vValue) Check the value to know if it’s null or not. isalnum(vValue) 1 if the value is digit/letter or 0 if not isalpha(vValue) 1 if the value is a letter or 0 if not iscntrl(vValue) 1 if the value is a control character (no printing position) isdigit(vValue) 1 if the value is a digit or 0 if not isgraph(vValue) 1 if the value can be printed (Except space) or 0 if not islower(vValue) 1 if the value is lowercase letter or 0 if not isprint(vValue) 1 if the value occupies a printing position or 0 if not ispunct(vValue) 1 if the value is a punctuation character or 0 if not isspace(vValue) 1 if the value is a white-space or 0 if not isupper(vValue) 1 if the value is an uppercase alphabetic letter or 0 if not isxdigit(vValue) 1 if the value is a hexadecimal digit character or 0 if not example: Load "stdlib.ring" oDataType = new DataType See "Test the DataType Class Methods" + nl see oDataType.isstring("test") + nl see oDataType.isnumber(1) + nl see oDataType.islist(1:3) + nl see oDataType.type("test") + nl see oDataType.isnull(null) + nl see oDataType.isalnum("Hello") + nl + # print 1 oDataType.isalnum("123456") + nl + # print 1 oDataType.isalnum("ABCabc123") + nl + # print 1 oDataType.isalnum("How are you") + nl # print 0 because of spaces (continues on next page) 44.13. DataType Class 395
Ring Documentation, Release1.21.2 44.14 Conversion Class Methods: Method Description/Output number(vValue) Convert strings to numbers. string(vValue) Convert numbers to strings. ascii(vValue) Get the ASCII code for a letter. char(vValue) Convert the ASCII code to character. hex(vValue) Convert decimal to hexadecimal. dec(vValue) Convert hexadecimal to decimal. str2hex(vValue) Convert string characters to hexadecimal characters. hex2str(vValue) Convert hexadecimal characters to string. example: Load "stdlib.ring" oConversion = new conversion See "Test the conversion Class Methods" + nl See oConversion.number("3") + 5 + nl See oConversion.string(3) + "5" + nl See oConversion.Ascii("m") + nl See oConversion.char(77) + nl see oConversion.hex(162) + nl see oConversion.dec("a2") + nl cHex = oConversion.str2hex("Hello") see cHex + nl see oConversion.hex2str(cHex) + nl Output: Test the conversion Class Methods 8 35 109 M a2 162 48656c6c6f Hello 44.14. Conversion Class 397
442.
Ring Documentation, Release1.21.2 44.15 ODBC Class Methods: Method Description/Output drivers() Get a list of ODBC drivers. datasources() Get a list of ODBC data sources. close() Free resources. connect(cConString) Connect to the database. disconnect() Close the connection. execute(cSQL) Execute SQL Statements colcount() Get columns count in the query result fetch() Fetch a row from the query result getdata(nCol) Get column value from the fetched row tables() Get a list of tables inside the database columns(cTableName) Get a list of columns inside the table autocommit(lStatus) Enable or disable the auto commit feature commit() Commit updates to the database rollback() Rollback updates to the database example: Load "stdlib.ring" oodbc = new odbc See "Test the odbc Class Methods" + nl oODBC { see drivers() see datasources() See "Connect to database" + nl see connect("DBQ=test.mdb;Driver={Microsoft Access Driver (*.mdb)}") + nl See "Select data" + nl see execute("select * from person") + nl nMax = colcount() See "Columns Count : " + nMax + nl while fetch() See "Row data:" + nl for x = 1 to nMax see getdata(x) + " - " next end See "Close database..." + nl disconnect() close() } 44.15. ODBC Class 398
443.
Ring Documentation, Release1.21.2 44.16 MySQL Class Methods: Method Description/Output info() Return string contains the MySQL Client version. error() Get the error message from the MySQL Client. connect(cServer,cUser,cPass,cDatabase) Connect to the MySQL database server. close() Close the connection to the MySQL database. query(cQuery) Execute SQL queries. insert_id() Get the inserted row id. result() Get the query result (data without column names). next_result() Move to the next query result. columns() Get a list of columns names. result2() Get all of the column names then the query result in one list. escape_string(cStr) Before storing binary data and special characters in the database. autocommit(lStatus) Enable or disable the auto commit feature. commit() Commit updates to the database. rollback() Rollback updates to the database. example: Load "stdlib.ring" omysql = new mysql See "Test the MySQL Class Methods" + nl omysql { see info() + nl connect("localhost", "root", "root","mahdb") see "Execute Query" + nl query("SELECT * FROM Employee") see "Print Result" + nl see result2() see "Close database" + nl close() } Output: Test the MySQL Class Methods 5.5.30 Execute Query Print Result Id Name Salary 1 Mahmoud 15000 2 Samir 16000 (continues on next page) 44.16. MySQL Class 399
444.
Ring Documentation, Release1.21.2 (continued from previous page) 3 Fayed 17000 Close database 44.17 SQLite Class Methods: Method Description/Output open(cDatabase) Open Database. close() Close Database. errormessage() Get Error Message. execute(cSQL) Execute Query. example: Load "stdlib.ring" osqlite = new sqlite See "Test the sqlite Class Methods" + nl osqlite { open("test.db") sql = "CREATE TABLE COMPANY(" + "ID INT PRIMARY KEY NOT NULL," + "NAME TEXT NOT NULL," + "AGE INT NOT NULL," + "ADDRESS CHAR(50)," + "SALARY REAL );" execute(sql) sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " + "VALUES (1, 'Mahmoud', 29, 'Jeddah', 20000.00 ); " + "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " + "VALUES (2, 'Ahmed', 27, 'Jeddah', 15000.00 ); " + "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" + "VALUES (3, 'Mohammed', 31, 'Egypt', 20000.00 );" + "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" + "VALUES (4, 'Ibrahim', 24, 'Egypt ', 65000.00 );" execute(sql) aResult = execute("select * from COMPANY") for x in aResult for t in x see t[2] + nl next next (continues on next page) 44.17. SQLite Class 400
445.
Ring Documentation, Release1.21.2 (continued from previous page) see copy("*",50) + nl for x in aResult see x["name"] + nl next close() } Output: Test the sqlite Class Methods 1 Mahmoud 29 Jeddah 20000.0 2 Ahmed 27 Jeddah 15000.0 3 Mohammed 31 Egypt 20000.0 4 Ibrahim 24 Egypt 65000.0 ************************************************** Mahmoud Ahmed Mohammed Ibrahim 44.18 PostgreSQL Class Methods: Method Description/Output init(cConString) Open Database. close() Close Database. execute(cSQL) Execute Query. example: load "stdlib.ring" (continues on next page) 44.18. PostgreSQL Class 401
446.
Ring Documentation, Release1.21.2 (continued from previous page) oPostgreSQL = new PostgreSQL("user=postgres password=sa dbname = mahdb") See "Test the PostgreSQL Class Methods" + nl oPostgreSQL { sql = "CREATE TABLE COMPANY_TEST(" + "ID INT PRIMARY KEY NOT NULL," + "NAME TEXT NOT NULL," + "AGE INT NOT NULL," + "ADDRESS CHAR(50)," + "SALARY REAL );" execute(sql) sql = "INSERT INTO COMPANY_TEST (ID,NAME,AGE,ADDRESS,SALARY) " + "VALUES (1, 'Mahmoud', 29, 'Jeddah', 20000.00 ); " + "INSERT INTO COMPANY_TEST (ID,NAME,AGE,ADDRESS,SALARY) " + "VALUES (2, 'Ahmed', 27, 'Jeddah', 15000.00 ); " + "INSERT INTO COMPANY_TEST (ID,NAME,AGE,ADDRESS,SALARY)" + "VALUES (3, 'Mohammed', 31, 'Egypt', 20000.00 );" + "INSERT INTO COMPANY_TEST (ID,NAME,AGE,ADDRESS,SALARY)" + "VALUES (4, 'Ibrahim', 24, 'Egypt ', 65000.00 );" execute(sql) ? execute("select * from COMPANY_TEST") ? copy("*",50) close() } Output: Test the PostgreSQL Class Methods id name age address salary 1 Mahmoud 29 Jeddah 20000 2 Ahmed 27 Jeddah 15000 3 (continues on next page) 44.18. PostgreSQL Class 402
447.
Ring Documentation, Release1.21.2 (continued from previous page) Mohammed 31 Egypt 20000 4 Ibrahim 24 Egypt 65000 ************************************************** 44.19 Security Class Methods: Method Description/Output md5(cString) Calculate the MD5 hash. sha1(cString) Calculate the SHA1 hash. sha256(cString) Calculate the SHA256 hash. sha512(cString) Calculate the SHA512 hash. sha384(cString) Calculate the SHA384 hash. sha224(cString) Calculate the SHA224 hash. encrypt(cString,cKey,cIV) Encrypts the data using the Blowfish algorithm in CBC mode. decrypt(cString,cKey,cIV) Decrypt the encrypted data using the Blowfish algorithm in CBC mode. encrypt_ex(cString,cKey,cIV,cCipher) Encrypts the data using the given cipher algorithm. decrypt_ex(cString,cKey,cIV,cCipher) Decrypt the encrypted data using the given cipher algorithm. supportedciphers() List all supported cipher algorithms for encryption/decryption. randbytes(nSize) Generate a string of cryptographically secure pseudo-random bytes. example: Load "stdlib.ring" oSecurity = new security See "Test the security Class Methods" + nl oSecurity { see md5("hello") + nl + sha1("hello") + nl + sha256("hello") + nl + sha512("hello") + nl + sha384("hello") + nl + sha256("hello") + nl list = 0:15 cKey="" for x in list cKey += char(x) next list = 1:8 cIV = "" for x in list cIV += char(x) next cCipher = encrypt("hello",cKey,cIV) see cCipher + nl + decrypt(cCipher,cKey,cIV) + nl } 44.19. Security Class 403
448.
Ring Documentation, Release1.21.2 44.20 Internet Class Methods: • download(cURL) • sendemail(cSMTPServer,cEmail,cPassword,cSender,cReceiver,cCC,cTitle,cContent) example: Load "stdlib.ring" ointernet = new internet See "Test the internet Class Methods" + nl ointernet { see download("www.ring-lang.sf.net") } 44.20. Internet Class 404
449.
CHAPTER FORTYFIVE DESKTOP, WEBASSEMBLY ANDMOBILE DEVELOPMENT USING RINGQT In this chapter we will learn how to use the Qt framework classes in our Ring applications to create Desktop, We- bAssembly and Mobile Applications. 45.1 The First GUI Application In this example we will create an application to ask the user about his/her name. When the user type the name in the textbox then click on “Say Hello” button, the textbox value will be updated by adding “Hello ” to the name. load "guilib.ring" oApp = new qApp { win1 = new qWidget() { setwindowtitle("Hello World") setGeometry(100,100,370,250) label1 = new qLabel(win1) { settext("What is your name ?") setGeometry(10,20,350,30) setalignment(Qt_AlignHCenter) } btn1 = new qPushButton(win1) { setGeometry(10,200,100,30) settext("Say Hello") setclickevent("pHello()") } btn2 = new qPushButton(win1) { setGeometry(150,200,100,30) settext("Close") setclickevent("pClose()") } lineedit1 = new qLineEdit(win1) { setGeometry(10,100,350,30) (continues on next page) 405
450.
Ring Documentation, Release1.21.2 (continued from previous page) } show() } exec() } Func pHello lineedit1.settext( "Hello " + lineedit1.text()) Func pClose oApp.quit() Program Output: At first we type the name in the textbox Then we click on the say hello button 45.1. The First GUI Application 406
451.
Ring Documentation, Release1.21.2 45.2 The Events Loop Qt uses Event-Driven and the events loop get the control when we call the exec() method from the qApp class. Once we call exec(), The events loop starts, and the Widgets starts responding to the different events (Mouse, Keyboard, Timers, etc). You get the control back again when an event is fired and your callback function is called. Once the execution of your callback function is done, the control go back again to the events loop. Useful things to remember (1) We can do most of the work using normal events (Events provided directly by each widget). (2) We can add more events to any widget using the Events Filter. (3) Using Timers we can easily get the control back and check for more things to do. Also when our callback function is busy with doing time consuming operations, we can call the ProcessEvents() method from the qApp class to avoid stopping the GUI. oApp.processevents() We can avoid calling the exec() method, and create our main loop It’s not recommended to do that, It’s just an option. # Instead of calling the exec() method while true oApp.processevents() # Respond to GUI Events # More Thing to do, We have the control! # ..... end 45.2. The Events Loop 407
452.
Ring Documentation, Release1.21.2 45.3 Using Layout The next example is just an upgrade to the previous application to use the vertical layout. Load "guilib.ring" MyApp = New qApp { win1 = new qWidget() { setwindowtitle("Hello World") setGeometry(100,100,400,130) label1 = new qLabel(win1) { settext("What is your name ?") setGeometry(10,20,350,30) setalignment(Qt_AlignHCenter) } btn1 = new qpushbutton(win1) { setGeometry(10,200,100,30) settext("Say Hello") setclickevent("pHello()") } btn2 = new qpushbutton(win1) { setGeometry(150,200,100,30) settext("Close") setclickevent("pClose()") } lineedit1 = new qlineedit(win1) { setGeometry(10,100,350,30) } layout1 = new qVBoxLayout() { addwidget(label1) addwidget(lineedit1) addwidget(btn1) addwidget(btn2) } win1.setlayout(layout1) show() } exec() } Func pHello lineedit1.settext( "Hello " + lineedit1.text()) Func pClose MyApp.quit() The application during the runtime! 45.3. Using Layout 408
453.
Ring Documentation, Release1.21.2 45.4 Using the QTextEdit Class In this example we will use the QTextEdit Class Load "guilib.ring" New qApp { win1 = new qWidget() { setwindowtitle("QTextEdit Class") setGeometry(100,100,500,500) new qtextedit(win1) { setGeometry(10,10,480,480) } show() } exec() } During the runtime we can paste rich text in the qtextedit widget 45.4. Using the QTextEdit Class 409
454.
Ring Documentation, Release1.21.2 45.5 Using the QListWidget Class In this example we will use the QListWidget Class Load "guilib.ring" New qApp { win1 = new qWidget() { setGeometry(100,100,400,400) list1 = new qlistwidget(win1) { setGeometry(150,100,200,200) alist = ["one","two","three","four","five"] for x in alist additem(x) next setcurrentrow(3,2) (continues on next page) 45.5. Using the QListWidget Class 410
455.
Ring Documentation, Release1.21.2 (continued from previous page) win1.setwindowtitle("Items Count : " + count() ) } btn1 = new qpushbutton(win1) { setGeometry(10,200,100,30) settext("selected item") setclickevent("pWork()") } btn2 = new qpushbutton(win1) { setGeometry(10,240,100,30) settext("Delete item") setclickevent("pWork2()") } show() } exec() } func pWork btn1.settext(string(list1.currentrow())) func pWork2 list1 { takeitem(currentrow()) } The application during the runtime 45.5. Using the QListWidget Class 411
456.
Ring Documentation, Release1.21.2 Another Example: Load "guilib.ring" New qApp { win1 = new qWidget() { setGeometry(100,100,500,400) list1 = new qlistwidget(win1) { setGeometry(150,100,200,200) alist = ["one","two","three","four","five"] for x in alist additem(x) next setcurrentrow(3,2) win1.setwindowtitle("Items Count : " + count() ) } btn1 = new qpushbutton(win1) { setGeometry(10,200,100,30) settext("selected item") setclickevent("pWork()") } (continues on next page) 45.5. Using the QListWidget Class 412
457.
Ring Documentation, Release1.21.2 (continued from previous page) btn2 = new qpushbutton(win1) { setGeometry(10,240,100,30) settext("Delete item") setclickevent("pWork2()") } show() } exec() } func pWork nbrOfItems = list1.count() curItemNbr = list1.currentrow() curValue = list1.item(list1.currentrow()).text() win1.setwindowtitle( "After Select - NbrOfItems: " + nbrOfItems + " CurItemNbr: " + curItemNbr + " CurValue: " + curValue ) btn1.settext( string(list1.currentrow() ) + " --- " + list1.item(list1.currentrow()).text() ) func pWork2 list1 { takeitem(currentrow()) nbrOfItems = count() curItemNbr = currentrow() curValue = item(currentrow()).text() win1.setwindowtitle("After Delete - NbrOfItems: " + nbrOfItems + " CurItemNbr: " + curItemNbr +" CurValue: " + curValue ) } 45.6 Using QTreeView and QFileSystemModel In this example we will learn how to use the QTreeView widget to represent the File System Load "guilib.ring" New qApp { win1 = New qWidget() { setwindowtitle("Using QTreeView and QFileSystemModel") setGeometry(100,100,500,400) (continues on next page) 45.6. Using QTreeView and QFileSystemModel 413
458.
Ring Documentation, Release1.21.2 (continued from previous page) New qtreeview(win1) { setGeometry(00,00,500,400) oDir = new QDir() ofile = new QFileSystemModel() ofile.setrootpath(oDir.currentpath()) setmodel(ofile) } show() } exec() } The application during the runtime 45.6. Using QTreeView and QFileSystemModel 414
459.
Ring Documentation, Release1.21.2 45.7 Using QTreeWidget and QTreeWidgetItem In this example we will learn about using the QTreeWidget and QTreeWidgetItem classes Load "guilib.ring" New qApp { win1 = new qWidget() { setwindowtitle("TreeWidget") setGeometry(100,100,400,400) layout1 = new qvboxlayout() tree1 = new qtreewidget(win1) { setGeometry(00,00,400,400) setcolumncount(1) myitem = new qtreewidgetitem() myitem.settext(0,"The First Step") addtoplevelitem(myitem) for x = 1 to 10 myitem2 = new qtreewidgetitem() myitem2.settext(0,"hello"+x) myitem.addchild(myitem2) for y = 1 to 10 myitem3 = new qtreewidgetitem() myitem3.settext(0,"hello"+x) myitem2.addchild(myitem3) next next setheaderlabel("Steps Tree") } layout1.addwidget(tree1) setlayout(layout1) show() } exec() } The application during the runtime 45.7. Using QTreeWidget and QTreeWidgetItem 415
460.
Ring Documentation, Release1.21.2 45.8 Using QComboBox Class In this example we will learn about using the QComboBox class Load "guilib.ring" New qApp { win1 = new qWidget() { setwindowtitle("Using QComboBox") setGeometry(100,100,400,400) New QComboBox(win1) { setGeometry(150,100,200,30) alist = ["one","two","three","four","five"] for x in aList additem(x,0) next } show() } exec() (continues on next page) 45.8. Using QComboBox Class 416
461.
Ring Documentation, Release1.21.2 (continued from previous page) } The application during the runtime 45.9 Creating Menubar In this example we will learn about using the QMenuBar class Load "guilib.ring" MyApp = New qApp { win1 = new qWidget() { setwindowtitle("Using QMenubar") setGeometry(100,100,400,400) menu1 = new qmenubar(win1) { sub1 = addmenu("File") sub2 = addmenu("Edit") sub3 = addmenu("Help") sub1 { (continues on next page) 45.9. Creating Menubar 417
462.
Ring Documentation, Release1.21.2 (continued from previous page) oAction = new qAction(win1) { settext("New") } addaction(oAction) oAction = new qAction(win1) { settext("Open") } addaction(oAction) oAction = new qAction(win1) { settext("Save") } addaction(oAction) oAction = new qAction(win1) { settext("Save As") } addaction(oAction) addseparator() oAction = new qaction(win1) { settext("Exit") setclickevent("myapp.quit()") } addaction(oAction) } sub2 { oAction = new qAction(win1) { settext("Cut") } addaction(oAction) oAction = new qAction(win1) { settext("Copy") } addaction(oAction) oAction = new qAction(win1) { settext("Paste") } addaction(oAction) addseparator() oAction = new qAction(win1) { settext("Select All") } addaction(oAction) } sub3 { oAction = new qAction(win1) { settext("Reference") } addaction(oAction) sub4 = addmenu("Sub Menu") sub4 { oAction = new qAction(win1) { settext("Website") } (continues on next page) 45.9. Creating Menubar 418
463.
Ring Documentation, Release1.21.2 (continued from previous page) addaction(oAction) oAction = new qAction(win1) { settext("Forum") } addaction(oAction) oAction = new qAction(win1) { settext("Blog") } addaction(oAction) } addseparator() oAction = new qAction(win1) { settext("About") } addaction(oAction) } } show() } exec() } The application during the runtime 45.9. Creating Menubar 419
464.
Ring Documentation, Release1.21.2 45.10 Context Menu Example: load "guilib.ring" new qApp { win = new qwidget() { setwindowtitle("Context Menu") resize(400,400) myfilter = new qAllEvents(win) { setContextmenuEvent("mymenu()") } installeventfilter(myfilter) show() } exec() } func mymenu new qMenu(win) { oAction = new qAction(win) { settext("new") setClickEvent("See :New") } addaction(oAction) oAction = new qAction(win) { settext("open") setClickEvent("See :Open") } addaction(oAction) oAction = new qAction(win) { settext("save") setClickEvent("See :Save") } addaction(oAction) oAction = new qAction(win) { settext("close") setClickEvent("See :Close") } addaction(oAction) oCursor = new qCursor() exec(oCursor.pos()) } 45.10. Context Menu 420
465.
Ring Documentation, Release1.21.2 45.11 Creating Toolbar In this example we will learn about using the QToolBar class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("Using QToolbar") setGeometry(100,100,600,400) abtns = [ new qpushbutton(win1) { settext("Add") } , new qpushbutton(win1) { settext("Edit") } , new qpushbutton(win1) { settext("Find") } , new qpushbutton(win1) { settext("Delete") } , new qpushbutton(win1) { settext("Exit") setclickevent("win1.close()") } ] tool1 = new qtoolbar(win1) { for x in abtns addwidget(x) addseparator() next setmovable(true) setGeometry(0,0,500,30) setFloatable(true) } show() } exec() } The application during the runtime 45.11. Creating Toolbar 421
466.
Ring Documentation, Release1.21.2 45.12 Creating StatusBar In this example we will learn about using the QStatusBar class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("Using QStatusbar") setGeometry(100,100,400,400) status1 = new qstatusbar(win1) { showmessage("Ready!",0) } setstatusbar(status1) show() } exec() } 45.12. Creating StatusBar 422
467.
Ring Documentation, Release1.21.2 The application during the runtime 45.13 Using QDockWidget In this example we will learn about using the QDockWidget class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("QDockWidget") setGeometry(100,100,400,400) label1 = new qlabel(win1) { settext("Hello") setGeometry(300,300,100,100) } label2 = new qlabel(win1) { settext("How are you ?") setGeometry(100,100,100,100) (continues on next page) 45.13. Using QDockWidget 423
468.
Ring Documentation, Release1.21.2 (continued from previous page) } dock1 = new qdockwidget(win1,0) { setwidget(label1) SetAllowedAreas(1) } dock2 = new qdockwidget(win1,0) { setwidget(label2) SetAllowedAreas(2) } adddockwidget(Qt_LeftDockWidgetArea,dock1,Qt_Horizontal) adddockwidget(Qt_LeftDockWidgetArea,dock2,Qt_Vertical) show() } exec() } The application during the runtime 45.13. Using QDockWidget 424
469.
Ring Documentation, Release1.21.2 45.14 Using QTabWidget In this example we will learn about using the QTabWidget class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("Using QTabWidget") setGeometry(100,100,400,400) page1 = new qwidget() { new qpushbutton(page1) { settext("The First Page") } } page2 = new qwidget() { new qpushbutton(page2) { settext("The Second Page") } } page3 = new qwidget() { new qpushbutton(page3) { settext("The Third Page") } } tab1 = new qtabwidget(win1) { inserttab(0,page1,"Page 1") inserttab(1,page2,"Page 2") inserttab(2,page3,"Page 3") setGeometry(100,100,400,400) } status1 = new qstatusbar(win1) { showmessage("Ready!",0) } setstatusbar(status1) showMaximized() } exec() } The application during the runtime 45.14. Using QTabWidget 425
470.
Ring Documentation, Release1.21.2 45.15 Using QTableWidget In this example we will learn about using the QTableWidget class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setGeometry(100,100,1100,370) setwindowtitle("Using QTableWidget") Table1 = new qTableWidget(win1) { setrowcount(10) setcolumncount(10) setGeometry(0,0,800,400) setselectionbehavior(QAbstractItemView_SelectRows) for x = 1 to 10 for y = 1 to 10 item1 = new qtablewidgetitem("R"+X+"C"+Y) setitem(x-1,y-1,item1) next next } setcentralwidget(table1) show() (continues on next page) 45.15. Using QTableWidget 426
471.
Ring Documentation, Release1.21.2 (continued from previous page) } exec() } The application during the runtime 45.16 Using QProgressBar In this example we will learn about using the QProgressBar class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setGeometry(100,100,600,150) setwindowtitle("Using QProgressBar") for x = 10 to 100 step 10 new qprogressbar(win1) { setGeometry(100,x,350,30) setvalue(x) } next show() } exec() } The application during the runtime 45.16. Using QProgressBar 427
472.
Ring Documentation, Release1.21.2 45.17 Using QSpinBox In this example we will learn about using the QSpinBox class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setGeometry(100,100,450,260) setwindowtitle("Using QSpinBox") new qspinbox(win1) { setGeometry(50,100,350,30) setvalue(50) } show() } exec() } The application during the runtime 45.17. Using QSpinBox 428
473.
Ring Documentation, Release1.21.2 45.18 Using QSlider In this example we will learn about using the QSlider class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setGeometry(100,100,500,400) setwindowtitle("Using QSlider") new qslider(win1) { setGeometry(100,100,50,130) settickinterval(50) } new qslider(win1) { setGeometry(100,250,250,30) settickinterval(50) setorientation(Qt_Horizontal) } show() } exec() } The application during the runtime 45.18. Using QSlider 429
474.
Ring Documentation, Release1.21.2 45.19 Using QDateEdit In this example we will learn about using the QDateEdit class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("Using QDateEdit") setGeometry(100,100,250,100) new qdateedit(win1) { setGeometry(20,40,220,30) } show() } exec() } The application during the runtime 45.19. Using QDateEdit 430
475.
Ring Documentation, Release1.21.2 45.20 Using QDial In this example we will learn about using the QDial class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setGeometry(100,100,450,500) setwindowtitle("Using QDial") new qdial(win1) { setGeometry(100,100,250,300) } show() } exec() } The application during the runtime 45.20. Using QDial 431
476.
Ring Documentation, Release1.21.2 Another Example Load "guilib.ring" New qApp { win1 = new qMainWindow() { setGeometry(100,100,450,500) setwindowtitle("Using QDial") button1 = new QPushButton(win1){ setGeometry(100,350,100,30) settext("Increment") setClickEvent("pIncrement()") } button2 = new QPushButton(win1){ setGeometry(250,350,100,30) settext("Decrement") setClickEvent("pDecrement()") (continues on next page) 45.20. Using QDial 432
Ring Documentation, Release1.21.2 45.21 Using QWebView In this example we will learn about using the QWebView class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("QWebView") myweb = new qwebview(win1) { setGeometry(10,10,600,600) loadpage(new qurl("http://google.com")) } setcentralwidget(myweb) showMaximized() } exec() } 45.21. Using QWebView 434
479.
Ring Documentation, Release1.21.2 The application during the runtime 45.22 Using QCheckBox In this example we will learn about using the QCheckBox class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("Using QCheckBox") new qcheckbox(win1) { setGeometry(100,100,150,30) settext("New Customer!") } showMaximized() } exec() } The application during the runtime 45.22. Using QCheckBox 435
480.
Ring Documentation, Release1.21.2 Another Example: Load "guilib.ring" New qApp { win1 = new qMainWindow() { setGeometry(100,100,400,300) setwindowtitle("Using QCheckBox") ### 0-Unchecked 1-Checked CheckBox = new qcheckbox(win1) { setGeometry(100,100,160,30) settext("New Customer!") setclickedEvent("HandleClickEvent()") } show() } exec() } Func HandleClickEvent if CheckBox.isChecked() = 1 CheckBox.settext("New Customer. Check 1-ON") else CheckBox.settext("New Customer. Check 0-OFF") ok 45.22. Using QCheckBox 436
481.
Ring Documentation, Release1.21.2 45.23 Using QRadioButton and QButtonGroup In this example we will learn about using the QRadioButton and QButtonGroup classes Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("Using QRadioButton") new qradiobutton(win1) { setGeometry(100,100,100,30) settext("One") } new qradiobutton(win1) { setGeometry(100,150,100,30) settext("Two") } new qradiobutton(win1) { setGeometry(100,200,100,30) settext("Three") } group2 = new qbuttongroup(win1) { btn4 = new qradiobutton(win1) { setGeometry(200,150,100,30) settext("Four") } btn5 = new qradiobutton(win1) { setGeometry(200,200,100,30) settext("Five") } addbutton(btn4,0) addbutton(btn5,0) } showMaximized() } exec() } The application during the runtime 45.23. Using QRadioButton and QButtonGroup 437
482.
Ring Documentation, Release1.21.2 45.24 Adding Hyperlink to QLabel In this example we will learn about creating Hyperlink using the QLabel class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("QLabel - Hyperlink") new qlabel(win1) { setGeometry(100,100,100,30) setopenexternallinks(true) settext('<a href="http://google.com">Google</a>') } showMaximized() } exec() } The application during the runtime 45.24. Adding Hyperlink to QLabel 438
483.
Ring Documentation, Release1.21.2 45.25 QVideoWidget and QMediaPlayer In this example we will learn about using the QVideoWidget and QMediaPlayer classes to play a group of movies from different positions at the same time Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("QVideoWidget") btn1 = new qpushbutton(win1) { setGeometry(0,0,100,30) settext("play") setclickevent("player.play() player2.play() player3.play() player4.play()") } videowidget = new qvideowidget(win1) { setGeometry(50,50,600,300) setstylesheet("background-color: black") } videowidget2 = new qvideowidget(win1) { setGeometry(700,50,600,300) setstylesheet("background-color: black") } videowidget3 = new qvideowidget(win1) { setGeometry(50,370,600,300) setstylesheet("background-color: black") (continues on next page) 45.25. QVideoWidget and QMediaPlayer 439
484.
Ring Documentation, Release1.21.2 (continued from previous page) } videowidget4 = new qvideowidget(win1) { setGeometry(700,370,600,300) setstylesheet("background-color: black") } player = new qmediaplayer() { setmedia(new qurl("1.mp4")) setvideooutput(videowidget) setposition(35*60*1000) } player2 = new qmediaplayer() { setmedia(new qurl("2.mp4")) setvideooutput(videowidget2) setposition(23*60*1000) } player3 = new qmediaplayer() { setmedia(new qurl("3.mp4")) setvideooutput(videowidget3) setposition(14.22*60*1000) } player4 = new qmediaplayer() { setmedia(new qurl("4.avi")) setvideooutput(videowidget4) setposition(8*60*1000) } showfullscreen() } exec() } The application during the runtime 45.25. QVideoWidget and QMediaPlayer 440
485.
Ring Documentation, Release1.21.2 45.26 Using QFrame In this example we will learn about using the QFrame class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("Using QFrame") for x = 0 to 10 frame1 = new qframe(win1,0) { setGeometry(100,20+50*x,400,30) setframestyle(QFrame_Raised | QFrame_WinPanel) } next showMaximized() } exec() } The application during the runtime 45.26. Using QFrame 441
486.
Ring Documentation, Release1.21.2 45.27 Display Image using QLabel In this example we will learn about displaying an image using the QLabel widget Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("QLabel - Display image") new qlabel(win1) { image = new qpixmap("images/advice.jpg") setpixmap(image) (continues on next page) 45.27. Display Image using QLabel 442
487.
Ring Documentation, Release1.21.2 (continued from previous page) setGeometry(0,0,image.width(),image.height()) } showMaximized() } exec() } The application during the runtime 45.27. Display Image using QLabel 443
488.
Ring Documentation, Release1.21.2 45.28 Menubar and StyleSheet Example In this example we will learn about creating menubar and setting the window stylesheet Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("Menubar") menu1 = new qmenubar(win1) { sub1 = addmenu("File") sub1 { oAction = new qAction(win1) { settext("New") setenabled(false) } addaction(oAction) oAction = new qAction(win1) { settext("Open") setcheckable(true) setchecked(true) setstatustip("open new file") } addaction(oAction) oAction = new qAction(win1) { settext("Save") } addaction(oAction) oAction = new qAction(win1) { settext("Save As") } addaction(oAction) addseparator() oAction = new qaction(win1) oAction.settext("Exit") oAction.setclickevent("myapp.quit()") addaction(oAction) } } status1 = new qstatusbar(win1) { showmessage("Ready!",0) } setmenubar(menu1) setmousetracking(true) setstatusbar(status1) setStyleSheet("color: black; selection-color: black; selection-background-color:white ; background: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, (continues on next page) 45.28. Menubar and StyleSheet Example 444
489.
Ring Documentation, Release1.21.2 (continued from previous page) stop: 0 #eef, stop: 1 #ccf);") showmaximized() } exec() } The application during the runtime 45.29 QLineEdit Events and QMessageBox In this example we will learn about using QLineEdit Events and displaying a Messagebox Load "guilib.ring" MyApp = New qApp { win1 = new qWidget() { setwindowtitle("Welcome") setGeometry(100,100,400,300) label1 = new qLabel(win1) { settext("What is your name ?") setGeometry(10,20,350,30) setalignment(Qt_AlignHCenter) } btn1 = new qpushbutton(win1) { setGeometry(10,200,100,30) settext("Say Hello") setclickevent("pHello()") } (continues on next page) 45.29. QLineEdit Events and QMessageBox 445
490.
Ring Documentation, Release1.21.2 (continued from previous page) btn1 = new qpushbutton(win1) { setGeometry(150,200,100,30) settext("Close") setclickevent("pClose()") } lineedit1 = new qlineedit(win1) { setGeometry(10,100,350,30) settextchangedevent("pChange()") setreturnpressedevent("penter()") } show() } exec() } Func pHello lineedit1.settext( "Hello " + lineedit1.text()) Func pClose MyApp.quit() Func pChange win1 { setwindowtitle( lineedit1.text() ) } Func pEnter new qmessagebox(win1) { setwindowtitle("Thanks") settext("Hi " + lineedit1.text() ) setstylesheet("background-color : white") show() } The application during the runtime 45.29. QLineEdit Events and QMessageBox 446
Ring Documentation, Release1.21.2 45.30 Other Widgets Events Each Qt signal can be used in RingQt, just add Set before the signal name and add event after the signal name to get the method that can be used to determine the event code. For example the QProgressBar class contains a signal named valueChanged() To use it just use the function setVal- ueChangedEvent() Example: Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("QProgressBar valueChanged Event") progress1 = new qprogressbar(win1) { setGeometry(100,100,350,30) setvalue(10) setvaluechangedevent("pChange()") } new qpushbutton(win1) { setGeometry(10,10,100,30) settext("increase") setclickevent("pIncrease()") } showMaximized() } exec() } func pIncrease progress1 { setvalue(value()+1) } func pchange win1.setwindowtitle("value : " + progress1.value() ) The application during the runtime 45.30. Other Widgets Events 448
493.
Ring Documentation, Release1.21.2 Another example for the stateChanged event of the QCheckBox class Load "guilib.ring" New qApp { win1 = new qMainWindow() { setwindowtitle("QCheckBox") new qcheckbox(win1) { setGeometry(100,100,150,30) settext("New Customer!") setstatechangedevent("pchange()") } showMaximized() } exec() } Func pChange new qMessageBox(Win1) { setWindowTitle("Checkbox") settext("State Changed!") show() } The application during the runtime 45.30. Other Widgets Events 449
494.
Ring Documentation, Release1.21.2 45.31 Using the QTimer Class In this example we will learn about using the QTimer class Load "guilib.ring" new qApp { win1 = new qwidget() { setgeometry(100,100,200,70) setwindowtitle("Timer") label1 = new qlabel(win1) { setgeometry(10,10,200,30) settext(theTime()) } new qtimer(win1) { setinterval(1000) settimeoutevent("pTime()") start() } show() } exec() } func pTime label1.settext(thetime()) func theTime return "Time : " + Time() The application during the runtime 45.31. Using the QTimer Class 450
495.
Ring Documentation, Release1.21.2 45.32 Using QProgressBar and Timer In this example we will learn about using the “animated” QProgressBar class and Timer ###------------------------------------ ### ProgressBar and Timer Example Load "guilib.ring" new qApp { win1 = new qwidget() { setgeometry(100,100,400,100) setwindowtitle("Timer and ProgressBar") LabelMan = new qlabel(win1) { setgeometry(10,10,200,30) settext(theTime()) ### ==>> func } TimerMan = new qtimer(win1) { setinterval(1000) settimeoutevent("pTime()") ### ==>> func start() } BarMan = new qprogressbar(win1) { setGeometry(100,50,300,10) ### Position X y, Length, Thickness setvalue(0) ### Percent filled } show() } exec() } func pTime LabelMan.settext(theTime()) ### ==>> func Increment = 10 (continues on next page) 45.32. Using QProgressBar and Timer 451
496.
Ring Documentation, Release1.21.2 (continued from previous page) if BarMan.value() >= 100 ### ProgressBar start over. BarMan.setvalue(0) ok BarMan{ setvalue(value() + Increment) } Func theTime return "Time : " + Time() 45.33 Display Scaled Image using QLabel In this example we will learn about displaying and scaling an image so that it looks “animated” using the QLabel widget Load "guilib.ring" #---------------------------------------------------- # REQUIRES: image = "C:RINGbinstock.jpg" # imageStock: start dimensions for growing image imageW = 200 ; imageH = 200 ; GrowBy = 4 ###---------------------------------------------------- ### Window and Box Size dimensions WinWidth = 1280 ; WinHeight = 960 BoxWidth = WinWidth -80 ; BoxHeight = WinHeight -80 ###---------------------------------------------------- New qapp { win1 = new qwidget() { setgeometry(50,50, WinWidth,WinHeight) setwindowtitle("Animated Image - Display Image Scaled and Resized") imageStock = new qlabel(win1) { image = new qpixmap("C:RINGbinstock.jpg") AspectRatio = image.width() / image.height() (continues on next page) 45.33. Display Scaled Image using QLabel 452
497.
Ring Documentation, Release1.21.2 (continued from previous page) imageW = 200 imageH = imageH / AspectRatio ### Size-H, Size-V, Aspect, Transform setpixmap(image.scaled(imageW , imageH ,0,0)) PosLeft = (BoxWidth - imageW ) / 2 PosTop = (BoxHeight - imageH ) / 2 setGeometry(PosLeft,PosTop,imageW,imageH) } TimerMan = new qtimer(win1) { setinterval(100) ### interval 100 millisecs. settimeoutevent("pTime()") ### ==>> func start() } show() } exec() } ###------------------------------------------------------ ### Function TimerMan: calling interval 100 milliseconds func pTime ### Stop Timer when image is size of Window area if imageW > BoxWidth TimerMan.stop() imageStock.clear() ### Will clear the image ok ### Grow image imageW += GrowBy imageH = imageW / AspectRatio ### Scaled Image: Size-H, Size-V, Aspect, Transform imageStock.setpixmap(image.scaled(imageW , imageH ,0,0)) ### Center the image PosLeft = (WinWidth - imageW ) / 2 PosTop = (WinHeight - imageH ) / 2 imageStock.setGeometry(PosLeft,PosTop,imageW,imageH) 45.33. Display Scaled Image using QLabel 453
498.
Ring Documentation, Release1.21.2 45.34 Using the QFileDialog Class Example Load "guilib.ring" New qapp { win1 = new qwidget() { setwindowtitle("open file") setgeometry(100,100,400,400) new qpushbutton(win1) { setgeometry(10,10,200,30) settext("open file") setclickevent("pOpen()") } show() } exec() } Func pOpen new qfiledialog(win1) { cName = getopenfilename(win1,"open file","c:","source files(*.ring)") win1.setwindowtitle(cName) } The application during the runtime 45.34. Using the QFileDialog Class 454
499.
Ring Documentation, Release1.21.2 45.35 Drawing using QPainter In this example we will learn about drawing using the QPainter class Load "guilib.ring" New qapp { win1 = new qwidget() { setwindowtitle("Drawing using QPainter") setgeometry(100,100,500,500) label1 = new qlabel(win1) { setgeometry(10,10,400,400) settext("") } new qpushbutton(win1) { setgeometry(200,400,100,30) settext("draw") setclickevent("draw()") } show() } exec() } Func draw p1 = new qpicture() color = new qcolor() { setrgb(0,0,255,255) } pen = new qpen() { setcolor(color) setwidth(10) } new qpainter() { begin(p1) setpen(pen) drawline(500,150,950,450) drawline(950,550,500,150) endpaint() } label1 { setpicture(p1) show() } The application during the runtime 45.35. Drawing using QPainter 455
500.
Ring Documentation, Release1.21.2 45.36 Printing using QPrinter In this example we will learn how to print to PDF file using QPrinter Load "guilib.ring" new qApp { win1 = new qwidget() { setwindowtitle("Printer") setgeometry(100,100,500,500) myweb = new qwebview(win1) { setgeometry(100,100,1000,500) loadpage(new qurl("http://google.com")) } new qpushbutton(win1) { setGeometry(20,20,100,30) settext("Print") setclickevent("print()") (continues on next page) 45.36. Printing using QPrinter 456
501.
Ring Documentation, Release1.21.2 (continued from previous page) } showmaximized() } exec() } func print printer1 = new qPrinter(0) { setoutputformat(1) # 1 = pdf setoutputfilename("test.pdf") painter = new qpainter() { begin(printer1) myfont = new qfont("Times",50,-1,0) setfont(myfont) drawtext(100,100,"test") printer1.newpage() drawtext(100,100,"test2") endpaint() } } printer1 = new qPrinter(0) { setoutputformat(1) setoutputfilename("test2.pdf") myweb.print(printer1,' system("test2.pdf") ') myweb.show() } system ("test.pdf") 45.37 Using QPrintPreviewDialog In this example we will learn how to use the QPrintPreviewDialog class. Example: load "guilib.ring" new qApp { win1 = new qwidget() { setwindowtitle("Printer Preview Dialog") setgeometry(100,100,800,880) printer1 = new qPrinter(0) show() oPreview = new qPrintPreviewDialog(printer1) { setParent(win1) move(10,10) setPaintrequestedevent("printPreview()") exec() } } (continues on next page) 45.37. Using QPrintPreviewDialog 457
502.
Ring Documentation, Release1.21.2 (continued from previous page) exec() } func printPreview printer1 { painter = new qpainter() { begin(printer1) myfont = new qfont("Times",50,-1,0) setfont(myfont) drawtext(100,100,"Test - Page (1)") printer1.newpage() drawtext(100,100,"Test - Page (2)") printer1.newpage() myfont2 = new qfont("Times",14,-1,0) setfont(myfont2) for x = 1 to 30 drawtext(100,100+(20*x),"Number : " + x) next endpaint() } } Screen Shot: 45.37. Using QPrintPreviewDialog 458
Ring Documentation, Release1.21.2 45.38 Creating More than one Window The next example demonstrates how to create more than one window Load "guilib.ring" app1 = new qapp { win1 = new qwidget() { setwindowtitle("First") setgeometry(100,100,500,500) new qpushbutton(win1) { setgeometry(100,100,100,30) settext("close") setclickevent("app1.quit()") } new qpushbutton(win1) { setgeometry(250,100,100,30) settext("Second") setclickevent("second()") } showmaximized() } exec() } func second win2 = new qwidget() { setwindowtitle("Second") setgeometry(100,100,500,500) setwindowflags(Qt_dialog) show() } The application during the runtime 45.38. Creating More than one Window 460
505.
Ring Documentation, Release1.21.2 45.39 Playing Sound Example: Load "guilib.ring" new qapp { win1 = new qwidget() { setwindowtitle("play sound!") show() } new qmediaplayer() { setmedia(new qurl("footstep.wav")) setvolume(50) play() } exec() } 45.40 Using the QColorDialog Class Example: Load "guilib.ring" oApp = new myapp { start() } Class MyApp oColor win1 (continues on next page) 45.39. Playing Sound 461
506.
Ring Documentation, Release1.21.2 (continued from previous page) Func start myapp = new qapp win1 = new qMainWindow() { setwindowtitle("Color Dialog") setgeometry(100,100,400,400) } new qpushbutton(win1) { setgeometry(10,10,100,30) settext("Get Color") setclickevent("oApp.pColor()") } win1.show() myapp.exec() Func pColor myobj = new qcolordialog() aColor = myobj.GetColor() r=acolor[1] g=acolor[2] b=acolor[3] win1.setstylesheet("background-color: rgb("+r+", " + g+ "," + b + ")") The application during the runtime 45.40. Using the QColorDialog Class 462
507.
Ring Documentation, Release1.21.2 45.41 Using qLCDNumber Class In this example we will learn about using the qLCDNumber class Load "guilib.ring" New qApp { win1 = new qWidget() { setwindowtitle("LCD Number") setgeometry(100,100,250,120) new qLCDNumber(win1) { setgeometry(10,10,100,40) display(100) } new qLCDNumber(win1) { setgeometry(10,60,100,40) display(80) } show() } exec() } The application during the runtime 45.41. Using qLCDNumber Class 463
508.
Ring Documentation, Release1.21.2 45.42 Movable Label Example Load "guilib.ring" new qApp { win1 = new qWidget() { label1 = new qLabel(win1) { setText("Welcome") setgeometry(10,10,200,50) setstylesheet("color: purple ; font-size: 30pt;") } new qTimer(win1) { setInterVal(10) setTimeOutEvent("pMove()") start() } setWindowTitle("Movable Label") setgeometry(100,100,600,80) setStyleSheet("background-color: white;") show() } exec() } Func pMove label1 { move(x()+1,y()) if x() > 600 move(10,y()) ok } The application during the runtime 45.42. Movable Label Example 464
509.
Ring Documentation, Release1.21.2 45.43 QMessagebox Example In this section we will learn how to check the output of the Message box Load "guilib.ring" new qApp { win1 = new qWidget() { label1 = new qpushbutton(win1) { setText("Test") setgeometry(10,10,200,50) setstylesheet("color: purple ; font-size: 30pt;") setclickevent("pWork()") } setWindowTitle("Messagebox") setgeometry(100,100,600,80) setStyleSheet("background-color: white;") show() } exec() } func pWork new qmessagebox(win1) { setwindowtitle("messagebox title") settext("messagebox text") setInformativeText("Do you want to save your changes?") setstandardbuttons(QMessageBox_Yes | QMessageBox_No | QMessageBox_Close) result = exec() win1 { if result = QMessageBox_Yes setwindowtitle("Yes") but result = QMessageBox_No setwindowtitle("No") but result = QMessageBox_Close setwindowtitle("Close") ok } } The application during the runtime 45.43. QMessagebox Example 465
510.
Ring Documentation, Release1.21.2 45.44 Using QInputDialog Class In the next example we will learn about using the QInputDialog class Load "guilib.ring" New QApp { Win1 = New QWidget () { SetGeometry(100,100,400,400) SetWindowTitle("Input Dialog") New QPushButton(win1) { SetText ("Input Dialog") SetGeometry(100,100,100,30) SetClickEvent("pWork()") } Show() } exec() } Func pWork oInput = New QInputDialog(win1) { setwindowtitle("What is your name?") setgeometry(100,100,400,50) (continues on next page) 45.44. Using QInputDialog Class 466
511.
Ring Documentation, Release1.21.2 (continued from previous page) setlabeltext("User Name") settextvalue("Mahmoud") lcheck = exec() if lCheck win1.setwindowtitle(oInput.textvalue()) ok } The application during the runtime 45.45 Dialog Functions We have the next functions SetDialogIcon(cIconFile) MsgInfo(cTitle,cMessage) ConfirmMsg(cTitle,cMessage) --> lResult InputBox(cTitle,cMessage) --> cValue InputBoxInt(cTitle,cMessage) --> nValue InputBoxNum(cTitle,cMessage) --> nValue InputBoxPass(cTitle,cMessage) --> cValue Example 45.45. Dialog Functions 467
512.
Ring Documentation, Release1.21.2 load "guilib.ring" new qApp { SetDialogIcon("notepad.png") msginfo(:Ring,:Welcome) see confirmMsg(:Ring,"Are you sure?") + nl see InputBoxNum(:Ring,"Enter Number(double) :") + nl see InputBox(:Ring,"Enter Value :") + nl see InputBoxInt(:Ring,"Enter Number(int)") + nl see InputBoxPass(:Ring,"Enter Password") +nl } 45.46 KeyPress and Mouse Move Events In this example we will learn how to use the Events Filter to know about KeyPress and Mouse Move Events Load "guilib.ring" new qApp { win1 = new qWidget() { setWindowTitle("Test using Event Filter!") setGeometry(100,100,400,400) setmousetracking(true) myfilter = new qallevents(win1) myfilter.setKeyPressEvent("pWork()") myfilter.setMouseButtonPressevent("pClick()") myfilter.setmousemoveevent("pMove()") installeventfilter(myfilter) show() } exec() } func pWork win1.setwindowtitle('KeyPress! : ' + myfilter.getkeycode()) func pClick new qmessagebox(win1) { setgeometry(100,100,400,100) setwindowtitle("click event!") settext("x : " + myfilter.getx() + " y : " + myfilter.gety() + " button : " + myfilter.getbutton() ) show() } (continues on next page) 45.46. KeyPress and Mouse Move Events 468
513.
Ring Documentation, Release1.21.2 (continued from previous page) func pMove win1.setwindowtitle("Mouse Move , X : " + myfilter.getx() + " Y : " + myfilter.gety() ) The application during the runtime 45.47 Moving Objects using the Mouse In the next example we will learn how to program movable objects where the user can move a label Load "guilib.ring" lPress = false nX = 0 nY = 0 new qApp { win1 = new qWidget() { (continues on next page) 45.47. Moving Objects using the Mouse 469
514.
Ring Documentation, Release1.21.2 (continued from previous page) setWindowTitle("Move this label!") setGeometry(100,100,400,400) setstylesheet("background-color:white;") Label1 = new qLabel(Win1){ setGeometry(100,100,200,50) setText("Welcome") setstylesheet("font-size: 30pt") myfilter = new qallevents(label1) myfilter.setEnterevent("pEnter()") myfilter.setLeaveevent("pLeave()") myfilter.setMouseButtonPressEvent("pPress()") myfilter.setMouseButtonReleaseEvent("pRelease()") myfilter.setMouseMoveEvent("pMove()") installeventfilter(myfilter) } show() } exec() } Func pEnter Label1.setStyleSheet("background-color: purple; color:white;font-size: 30pt;") Func pLeave Label1.setStyleSheet("background-color: white; color:black;font-size: 30pt;") Func pPress lPress = True nX = myfilter.getglobalx() ny = myfilter.getglobaly() Func pRelease lPress = False pEnter() Func pMove nX2 = myfilter.getglobalx() ny2 = myfilter.getglobaly() ndiffx = nX2 - nX ndiffy = nY2 - nY if lPress Label1 { move(x()+ndiffx,y()+ndiffy) setStyleSheet("background-color: Green; color:white;font-size: 30pt;") nX = nX2 ny = nY2 } (continues on next page) 45.47. Moving Objects using the Mouse 470
515.
Ring Documentation, Release1.21.2 (continued from previous page) ok The application during the runtime 45.47. Moving Objects using the Mouse 471
Ring Documentation, Release1.21.2 45.48 Inheritance from GUI Classes Example : Load "guilib.ring" New MyWindow() new qApp { exec() } class mywindow from qwidget Func init super.init() setwindowtitle("First Window") setgeometry(100,100,400,400) setstylesheet("background-color: purple;") settooltip("my first window!") show() The application during the runtime 45.48. Inheritance from GUI Classes 473
518.
Ring Documentation, Release1.21.2 45.49 Using QDesktopWidget Class In the next example we will learn about using the QDesktopWidget class Load "guilib.ring" New qApp { win1 = New qWidget() { resize(400,400) btn1 = new qPushbutton(win1) { setText("Center") move(100,100) resize(100,30) setClickEvent("pCenter()") } Show() } exec() } (continues on next page) 45.49. Using QDesktopWidget Class 474
519.
Ring Documentation, Release1.21.2 (continued from previous page) Func pCenter oDesktop = new qDesktopWidget() win1.move((oDesktop.width()-win1.width()) /2 , (oDesktop.Height()-win1.Height())/ ˓ →2 ) win1.show() The application during the runtime 45.50 Rotate Text The next example rotate text using a Timer. Load "guilib.ring" nAngle = 0 New qapp { win1 = new qwidget() { setwindowtitle("Rotate Text") resize(800,600) label1 = new qlabel(win1) { (continues on next page) 45.50. Rotate Text 475
520.
Ring Documentation, Release1.21.2 (continued from previous page) settext("") myfilter = new qallevents(win1) myfilter.setMouseButtonPressevent("pClick()") installeventfilter(myfilter) } new qtimer(win1) { setinterval(50) settimeoutevent("pTime()") start() } pDraw() L1 = new qVBoxLayout() { AddWidget(Label1) } SetLayout(L1) showMaximized() } exec() } Func pDraw p1 = new qpicture() color = new qcolor() { setrgb(0,0,255,255) } pen = new qpen() { setcolor(color) setwidth(50) } painter = new qpainter() { begin(p1) setpen(pen) myfont = font() myfont.setpointsize(50) setfont(myfont) rotate(nAngle) drawtext(350,0*nAngle,"welcome") drawtext(0,0*nAngle,"welcome") endpaint() } label1 { setpicture(p1) show() } Func pClick win1 { setwindowtitle("Click Event") } Func pTime nAngle++ if nAngle = 90 nAngle = 10 ok pDraw() The application during the runtime 45.50. Rotate Text 476
521.
Ring Documentation, Release1.21.2 45.51 Change Focus The next example change the focus using the ENTER key. load "guilib.ring" new qApp { win = new qWidget() { resize(600,600) SetWindowTitle("Change Focus") text1 = new qLineEdit(win) text2 = new qLineEdit(win) text3 = new qLineEdit(win) text4 = new qLineEdit(win) layout1 = new qVBoxLayout() { AddWidget(text1) AddWidget(text2) AddWidget(text3) AddWidget(text4) (continues on next page) 45.51. Change Focus 477
522.
Ring Documentation, Release1.21.2 (continued from previous page) } setLayout(Layout1) aList = [text1,text2,text3,text4] oFilter = new qallevents(win) oFilter.setKeyPressEvent("pWork()") installeventfilter(oFilter) show() } exec() } func pWork nCode = oFilter.getkeycode() if nCode = 16777220 # ENTER Key for x=1 to len(aList) if aList[x].HasFocus() t = x+1 if t > len(aList) t=1 ok aList[t].SetFocus(0) exit ok next ok 45.52 Regular Expressions The next example uses the Regular Expressions classes. load "guilib.ring" new qApp { see "Using Regular Expressions" + nl exp = new qregularexpression() { setPattern("dd w+") see pattern() + nl match = match("33 one",0,0,0) see match.hasmatch() + nl match = match("3 one",0,0,0) see match.hasmatch() + nl match = match("welcome 11 one",0,0,0) see match.hasmatch() + nl matched = match.captured(0) see matched + nl } exp = new qregularexpression() { setPattern("^(dd)/(dd)/(dddd)$") see pattern() + nl match = match("08/12/1985",0,0,0) (continues on next page) 45.52. Regular Expressions 478
523.
Ring Documentation, Release1.21.2 (continued from previous page) see match.hasmatch() + nl day = match.captured(1) month = match.captured(2) year = match.captured(3) see day + nl + month + nl + year + nl see "(" + match.capturedStart(1) + "," + match.capturedEnd(1)+ ")" + nl see "(" + match.capturedStart(2) + "," + match.capturedEnd(2)+ ")" + nl see "(" + match.capturedStart(3) + "," + match.capturedEnd(3)+ ")" + nl } } Output Using Regular Expressions dd w+ 1 0 1 11 one ^(dd)/(dd)/(dddd)$ 1 08 12 1985 (0,2) (3,5) (6,10) 45.53 Simple Client and Server Example In this section we will learn about creating simple Client and Server Application Load "guilib.ring" new qApp { oClient = new Client { client() } oServer = new Server { server() } exec() } Class Client win1 lineedit1 cOutput="" oTcpSocket func client win1 = new qwidget() (continues on next page) 45.53. Simple Client and Server Example 479
524.
Ring Documentation, Release1.21.2 (continued from previous page) new qpushbutton(win1) { setgeometry(50,50,100,30) settext("connect") setclickevent("oClient.Connect()") } lineedit1 = new qtextedit(win1) { setGeometry(150,50,200,300) } win1 { setwindowtitle("client") setgeometry(10,100,400,400) show() } func connect cOutput = "Connect to host 127.0.0.1 port 9999" + nl lineedit1.settext(cOutput) oTcpSocket = new qTcpSocket(win1) { setconnectedevent("oClient.pConnected()") setreadyreadevent("oClient.pRead()") connecttohost("127.0.0.1",9999,3,0) waitforconnected(5000) } func pConnected cOutput += "Connected!" + nl lineedit1.settext(cOutput) func pRead cOutput += "Ready Read!" + nl lineedit1.settext(cOutput) cOutput += oTcpSocket.readall().data() + nl lineedit1.settext(cOutput) Class Server win1 lineedit1 oTcpServer oTcpClient cOutput = "" func server win1 = new qwidget() lineedit1 = new qtextedit(win1) { setGeometry(150,50,200,300) } (continues on next page) 45.53. Simple Client and Server Example 480
525.
Ring Documentation, Release1.21.2 (continued from previous page) win1 { setwindowtitle("Server") setgeometry(450,100,400,400) show() } oTcpServer = new qTcpServer(win1) { setNewConnectionEvent("oServer.pNewConnection()") oHostAddress = new qHostAddress() oHostAddress.SetAddress("127.0.0.1") listen(oHostAddress,9999) } cOutput = "Server Started" + nl + "listen to port 9999" + nl lineedit1.settext(cOutput) Func pNewConnection oTcpClient = oTcpServer.nextPendingConnection() cOutput += "Accept Connection" + nl lineedit1.settext(cOutput) oTcpClient { cStr ="Hello from server to client!"+char(13)+char(10) write(cStr,len(cStr)) flush() waitforbyteswritten(300000) close() } The application during the runtime 45.53. Simple Client and Server Example 481
526.
Ring Documentation, Release1.21.2 45.54 Dynamic Objects We may create objects in the runtime and add them to windows. Example: load "guilib.ring" oFormDesigner = new FormDesigner { start("oFormDesigner") } Class FormDesigner winToolBox winForm aObjects = [] func start cObjectName oApp = new qApp winToolBox = new qWidget() winToolBox.setWindowTitle("ToolBox") winToolBox.move(10,10) winToolBox.resize(300,600) btn = new qPushButton(winToolBox) btn.resize(300,30) btn.setText("Create Button") btn.setClickEvent(cObjectName+".pCreateButton()") btn.show() winToolBox.show() winForm = new qWidget() { move(400,50) setWindowTitle("Form Designer") resize(600,600) show() } oApp.exec() func pCreateButton nCount = len(aObjects) aObjects + new MyButton(winForm) { nIndex = nCount + 1 setText("Button"+ nIndex) Move(30*nIndex,30*nIndex) resize(100,30) (continues on next page) 45.54. Dynamic Objects 482
527.
Ring Documentation, Release1.21.2 (continued from previous page) show() } Class MyButton from qPushButton nIndex = 0 45.55 Weight History Application The next sample help in recording (Date, Time and Weight). Load "guilib.ring" MyApp = new qApp { $ApplicationObject = "oApp" # To be used when calling events oApp = new App exec() oApp.CloseDatabase() } class App cDir = currentdir() + "/" oCon aIDs = [] win1 = new qWidget() { setWindowTitle("Weight History") resize(600,600) layoutButtons = new qhboxlayout() { label1 = new qLabel(win1) { setText("Weight") } text1 = new qlineedit(win1) btnAdd = new qpushbutton(win1) { setText("Add") setClickEvent($ApplicationObject+".AddWeight()") } btnDelete = new qpushbutton(win1) { setText("Delete") setClickEvent($ApplicationObject+".Deleteweight()") } addwidget(label1) addwidget(text1) addwidget(btnAdd) addwidget(btnDelete) } layoutData = new qhboxlayout() { (continues on next page) 45.55. Weight History Application 483
528.
Ring Documentation, Release1.21.2 (continued from previous page) Table1 = new qTableWidget(win1) { setrowcount(0) setcolumncount(3) setselectionbehavior(QAbstractItemView_SelectRows) setHorizontalHeaderItem(0, new QTableWidgetItem("Date")) setHorizontalHeaderItem(1, new QTableWidgetItem("Time")) setHorizontalHeaderItem(2, new QTableWidgetItem("Weight")) setitemChangedEvent($ApplicationObject+".ItemChanged()") setAlternatingRowColors(true) horizontalHeader().setStyleSheet("color: blue") verticalHeader().setStyleSheet("color: red") } addWidget(Table1) } layoutClose = new qhboxlayout() { btnclose = new qpushbutton(win1) { setText("Close") setClickEvent("MyApp.Quit()") } addwidget(btnClose) } layoutMain = new qvboxlayout() { addlayout(layoutButtons) addLayout(LayoutData) addLayout(layoutClose) } setlayout(layoutMain) self.OpenDatabase() self.ShowRecords() show() } Func OpenDatabase lCreate = False if not fexists(cDir + "weighthistory.db") lCreate = True ok new QSqlDatabase() { this.oCon = addDatabase("QSQLITE") { setDatabaseName("weighthistory.db") Open() } } if lCreate new QSqlQuery( ) { exec("create table weighthistory (id integer primary key,"+ " f_date varchar(10),"+ " f_time varchar(8), f_weight varchar(8) );") delete() } (continues on next page) 45.55. Weight History Application 484
529.
Ring Documentation, Release1.21.2 (continued from previous page) ok Func CloseDatabase oCon.Close() Func AddWeight cWeight = text1.text() AddRecord(cWeight) Func DeleteWeight Table1 { nRow = CurrentRow() if nRow >= 0 nID = this.aIDs[nROW+1] new QSqlQuery( ) { exec("delete from weighthistory where id = " + nID ) } Del(this.aIDs,nRow+1) removerow(nRow) selectrow(nRow) ok } Func AddRecord cWeight new QSqlQuery( ) { cStr = "insert into weighthistory (f_date,f_time,f_weight) values"+ " ('%f1','%f2','%f3')" cDate = Date() cTime = Time() cStr = substr(cStr,"%f1",cDate) cStr = substr(cStr,"%f2",cTime) cStr = substr(cStr,"%f3",cWeight) exec(cStr) delete() } ShowRecords() Table1.selectrow(table1.rowcount()-1) Func ShowRecords table1.setitemChangedEvent("") aIDs = [] query = new QSqlQuery() { exec("select * from weighthistory") nRows = 0 this.Table1.setrowcount(0) while movenext() this.table1 { insertRow(nRows) this.aIDs + query.value(0).tostring() (continues on next page) 45.55. Weight History Application 485
530.
Ring Documentation, Release1.21.2 (continued from previous page) for x = 1 to 3 cStr = query.value(x).tostring() item = new qTableWidgetItem(cStr) setItem(nRows,x-1,item) next } nRows++ end delete() } table1.setitemChangedEvent($ApplicationObject+".ItemChanged()") Func ItemChanged nRow = table1.currentrow() if nRow >= 0 myitem = Table1.item(table1.currentrow(),0) cDate = myitem.text() myitem = Table1.item(table1.currentrow(),1) cTime = myitem.text() myitem = Table1.item(table1.currentrow(),2) cWeight = myitem.text() new QSqlQuery( ) { cStr = "update weighthistory set f_date ='%f1' , f_time = '%f2' , "+ "f_weight ='%f3' where id = " + this.aIDs[nROW+1] cStr = substr(cStr,"%f1",cDate) cStr = substr(cStr,"%f2",cTime) cStr = substr(cStr,"%f3",cWeight) exec(cStr) delete() } ok The next screen shot for the application during the runtime 45.55. Weight History Application 486
531.
Ring Documentation, Release1.21.2 45.56 Notepad Application In the next example we will see simple Notepad developed using the RingQt Load "guilib.ring" cActiveFileName = "" aTextColor = [0,0,0] aBackColor = [255,255,255] cFont = "MS Shell Dlg 2,14,-1,5,50,0,0,0,0,0" cWebsite = "http://www.google.com" (continues on next page) 45.56. Notepad Application 487
532.
Ring Documentation, Release1.21.2 (continued from previous page) oSearch = NULL oSearchValue = NULL oSearchCase = NULL oSearchFilter = NULL oReplaceValue = NULL lAskToSave = false MyApp = New qApp { win1 = new qMainWindow() { setwindowtitle("Ring Notepad") setGeometry(100,100,400,400) aBtns = [ new qpushbutton(win1) { setbtnimage(self,"image/new.png") setclickevent("pNew()") settooltip("New File") } , new qpushbutton(win1) { setbtnimage(self,"image/open.png") setclickevent("pOpen()") settooltip("Open File") } , new qpushbutton(win1) { setbtnimage(self,"image/save.png") setclickevent("pSave()") settooltip("Save") } , new qpushbutton(win1) { setbtnimage(self,"image/saveas.png") setclickevent("pSaveAs()") settooltip("Save As") } , new qpushbutton(win1) { setbtnimage(self,"image/cut.png") setclickevent("pCut()") settooltip("Cut") } , new qpushbutton(win1) { setbtnimage(self,"image/copy.png") setclickevent("pCopy()") settooltip("Copy") } , new qpushbutton(win1) { setbtnimage(self,"image/paste.png") setclickevent("pPaste()") settooltip("Paste") } , new qpushbutton(win1) { setbtnimage(self,"image/font.png") (continues on next page) 45.56. Notepad Application 488
533.
Ring Documentation, Release1.21.2 (continued from previous page) setclickevent("pFont()") settooltip("Font") } , new qpushbutton(win1) { setbtnimage(self,"image/colors.jpg") setclickevent("pColor()") settooltip("Text Color") } , new qpushbutton(win1) { setbtnimage(self,"image/search.png") setclickevent("pFind()") settooltip("Find and Replace") } , new qpushbutton(win1) { setbtnimage(self,"image/print.png") setclickevent("pPrint()") settooltip("Print") } , new qpushbutton(win1) { setbtnimage(self,"image/debug.png") setclickevent("pDebug()") settooltip("Debug (Run then wait!)") } , new qpushbutton(win1) { setbtnimage(self,"image/run.png") setclickevent("pRun()") settooltip("Run the program") } , new qpushbutton(win1) { setbtnimage(self,"image/close.png") setclickevent("pQuit()") settooltip("Quit") } ] tool1 = addtoolbar("files") { for x in aBtns addwidget(x) addseparator() next } menu1 = new qmenubar(win1) { sub1 = addmenu("File") sub2 = addmenu("Edit") sub3 = addmenu("View") sub4 = addmenu("Help") sub1 { oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+n")) setbtnimage(self,"image/new.png") settext("New") setclickevent("pNew()") } addaction(oAction) (continues on next page) 45.56. Notepad Application 489
534.
Ring Documentation, Release1.21.2 (continued from previous page) oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+o")) setbtnimage(self,"image/open.png") settext("Open") setclickevent("pOpen()") } addaction(oAction) addseparator() oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+s")) setbtnimage(self,"image/save.png") settext("Save") setclickevent("pSave()") } addaction(oAction) addseparator() oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+e")) setbtnimage(self,"image/saveas.png") settext("Save As") setclickevent("pSaveAs()") } addaction(oAction) addseparator() oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+p")) setbtnimage(self,"image/print.png") settext("Print to PDF") setclickevent("pPrint()") } addaction(oAction) addseparator() oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+d")) setbtnimage(self,"image/debug.png") settext("Debug (Run then wait!)") setclickevent("pDebug()") } addaction(oAction) addseparator() oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+r")) setbtnimage(self,"image/run.png") settext("Run") setclickevent("pRun()") } addaction(oAction) addseparator() oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+F5")) setbtnimage(self,"image/run.png") settext("Run GUI Application (No Console)") (continues on next page) 45.56. Notepad Application 490
535.
Ring Documentation, Release1.21.2 (continued from previous page) setclickevent("pRunNoConsole()") } addaction(oAction) addseparator() oAction = new qaction(win1) { setShortcut(new QKeySequence("Ctrl+q")) setbtnimage(self,"image/close.png") settext("Exit") setstatustip("Exit") setclickevent("pQuit()") } addaction(oAction) } sub2 { oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+x")) setbtnimage(self,"image/cut.png") settext("Cut") setclickevent("pCut()") } addaction(oAction) oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+c")) setbtnimage(self,"image/copy.png") settext("Copy") setclickevent("pCopy()") } addaction(oAction) oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+v")) setbtnimage(self,"image/paste.png") settext("Paste") setclickevent("pPaste()") } addaction(oAction) addseparator() oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+i")) setbtnimage(self,"image/font.png") settext("Font") setclickevent("pFont()") } addseparator() addaction(oAction) oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+t")) setbtnimage(self,"image/colors.jpg") settext("Text Color") setclickevent("pColor()") } addaction(oAction) oAction = new qAction(win1) { (continues on next page) 45.56. Notepad Application 491
536.
Ring Documentation, Release1.21.2 (continued from previous page) setShortcut(new QKeySequence("Ctrl+b")) setbtnimage(self,"image/colors.jpg") settext("Back Color") setclickevent("pColor2()") } addaction(oAction) addseparator() oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+g")) settext("Go to line") setclickevent("pGoto()") } addaction(oAction) oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+f")) setbtnimage(self,"image/search.png") settext("Find and Replace") setclickevent("pFind()") } addaction(oAction) } sub3 { oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+p")) setbtnimage(self,"image/project.png") settext("Project Files") setclickevent("pProject()") } addaction(oAction) oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+u")) setbtnimage(self,"image/source.png") setclickevent("pSourceCode()") settext("Source Code") } addaction(oAction) oAction = new qAction(win1) { setShortcut(new QKeySequence("Ctrl+w")) setbtnimage(self,"image/richtext.png") setclickevent("pWebBrowser()") settext("Web Browser") } addaction(oAction) } sub4 { sub5 = addmenu("Development Tools") sub5 { oAction = new qAction(win1) { settext("Programming Language") setclickevent("pLang()") } (continues on next page) 45.56. Notepad Application 492
537.
Ring Documentation, Release1.21.2 (continued from previous page) addaction(oAction) oAction = new qAction(win1) { settext("GUI Library") setclickevent("pGUI()") } addaction(oAction) } addseparator() oAction = new qAction(win1) { settext("About") setclickevent("pAbout()") } addaction(oAction) } } setmenubar(menu1) status1 = new qstatusbar(win1) { showmessage("Ready!",0) } setstatusbar(status1) tree1 = new qtreeview(win1) { setclickedevent("pChangeFile()") setGeometry(00,00,200,400) oDir = new QDir() ofile = new QFileSystemModel() { setrootpath(oDir.currentpath()) myfiles = new qstringlist() myfiles.append("*.ring") myfiles.append("*.rh") setnamefilters(myfiles) setNameFilterDisables(false) } setmodel(ofile) myindex = ofile.index(oDir.currentpath(),0) for x = 1 to ofile.columncount() hidecolumn(x) next setcurrentindex(myindex) setexpanded(myindex,true) header().hide() } oDock1 = new qdockwidget(win1,0) { setGeometry(00,00,200,200) setwindowtitle("Project Files") setwidget(tree1) } (continues on next page) 45.56. Notepad Application 493
538.
Ring Documentation, Release1.21.2 (continued from previous page) textedit1 = new qtextedit(win1) { setCursorPositionChangedevent("pCursorPositionChanged()") setLineWrapMode(QTextEdit_NoWrap) setAcceptRichText(false) setTextChangedEvent("lAskToSave = true") } oDock2 = new qdockwidget(win1,0) { setwidget(textedit1) setwindowtitle("Source Code") } oWebBrowser = new qWidget() { setWindowFlags(Qt_SubWindow) oWBLabel = new qLabel(win1) { setText("Website: ") } oWBText = new qLineEdit(win1) { setText(cWebSite) setReturnPressedEvent("pWebGo()") } oWBGo = new qPushButton(win1) { setText("Go") setClickEvent("pWebGo()") } oWBBack = new qPushButton(win1) { setText("Back") setClickEvent("pWebBack()") } oWBLayout1 = new qHBoxLayout() { addWidget(oWBLabel) addWidget(oWBText) addWidget(oWBGo) addWidget(oWBBack) } oWebView = new qWebView(win1) { loadpage(new qurl(cWebSite)) } oWBlayout2 = new qVBoxLayout() { addLayout(oWBLayout1) addWidget(oWebView) } setLayout(oWBLayout2) } oDock3 = new qdockwidget(win1,0) { setwidget(oWebBrowser) setwindowtitle("Web Browser") setFeatures(QDockWidget_DocWidgetClosable) } (continues on next page) 45.56. Notepad Application 494
539.
Ring Documentation, Release1.21.2 (continued from previous page) adddockwidget(1,oDock1,1) adddockwidget(2,oDock2,2) adddockwidget(2,oDock3,1) setwinicon(self,"image/notepad.png") showmaximized() } RestoreSettings() exec() } func pWebGo cWebsite = oWBText.text() oWebView.LoadPage( new qurl( cWebSite ) ) func pWebBack oWebView.Back() func pProject oDock1.Show() func pSourceCode oDock2.Show() func pWebBrowser oDock3.Show() func pChangeFile myitem = tree1.currentindex() if ofile.isdir(myitem) return ok cActiveFileName = ofile.filepath(myitem) textedit1.settext(read(cActiveFileName)) textedit1.setfocus(0) pCursorPositionChanged() pSetActiveFileName() func pSetActiveFileName oDock2.setWindowTitle("Source Code : " + cActiveFileName) func pCursorPositionChanged status1.showmessage(" Line : "+(textedit1.textcursor().blocknumber()+1)+ " Column : " +(textedit1.textcursor().columnnumber()+1) + " Total Lines : " + textedit1.document().linecount() ,0) func pGoto oInput = New QInputDialog(win1) { setwindowtitle("Enter the line number?") setgeometry(100,100,400,50) (continues on next page) 45.56. Notepad Application 495
540.
Ring Documentation, Release1.21.2 (continued from previous page) setlabeltext("Line") settextvalue("1") exec() nLine = 0 + oInput.textvalue() oBlock = textedit1.document().findBlockByLineNumber(nLine-1) oCursor = textedit1.textcursor() oCursor.setposition(oBlock.position(),0) textedit1.settextcursor(oCursor) } func pFind if isobject(oSearch) oSearch.activatewindow() return ok oSearch = new qWidget() { new qLabel(oSearch) { setText("Find What : ") setgeometry(10,10,50,30) } oSearchValue = new qlineedit(oSearch) { setgeometry(80,10,460,30) setReturnPressedEvent("pFindValue()") } new qLabel(oSearch) { setText("Replace with ") setgeometry(10,45,80,30) } oReplaceValue = new qlineedit(oSearch) { setgeometry(80,45,460,30) } oSearchCase = new qCheckbox(oSearch) { setText("Case Sensitive") setgeometry(80,85,100,30) } new qPushButton(oSearch) { setText("Find/Find Next") setgeometry(80,120,100,30) setclickevent("pFindValue()") } new qPushButton(oSearch) { setText("Replace") setgeometry(200,120,100,30) setclickevent("pReplace()") (continues on next page) 45.56. Notepad Application 496
541.
Ring Documentation, Release1.21.2 (continued from previous page) } new qPushButton(oSearch) { setText("Replace All") setgeometry(320,120,100,30) setclickevent("pReplaceAll()") } new qPushButton(oSearch) { setText("Close") setgeometry(440,120,100,30) setclickevent("pSearchClose()") } setwinicon(oSearch,"image/notepad.png") setWindowTitle("Find/Replace") setStyleSheet("background-color:white;") setFixedsize(550,160) setwindowflags( Qt_CustomizeWindowHint | Qt_WindowTitleHint | Qt_WindowStaysOnTopHint) oSearchFilter = new qallevents(oSearch) oSearchFilter.setKeyPressEvent("pSearchKeyPress()") installeventfilter(oSearchFilter) show() } Func pReplace oCursor = textedit1.textCursor() if oCursor.HasSelection() = false new qMessagebox(oSearch) { SetWindowTitle("Replace") SetText("No Selection") show() } return false ok cValue = oSearchValue.text() cSelected = oCursor.SelectedText() if oSearchCase.checkState() = Qt_Unchecked cValue = lower(cValue) cSelected = lower(cSelected) ok if cSelected != cValue new qMessagebox(oSearch) { SetWindowTitle("Replace") SetText("No Match") show() } (continues on next page) 45.56. Notepad Application 497
542.
Ring Documentation, Release1.21.2 (continued from previous page) return false ok cValue = oReplaceValue.text() nStart = oCursor.SelectionStart() nEnd = oCursor.SelectionEnd() cStr = textedit1.toPlainText() cStr = left(cStr,nStart)+cValue+substr(cStr,nEnd+1) textedit1.setText(cStr) return pFindValue() Func pReplaceAll cStr = textedit1.toPlainText() cOldValue = oSearchValue.text() cNewValue = oReplaceValue.text() if oSearchCase.checkState() = Qt_Unchecked # Not Case Sensitive cStr = SubStr(cStr,cOldValue,cNewValue,true) else # Case Sensitive cStr = SubStr(cStr,cOldValue,cNewValue) ok textedit1.setText(cStr) new qMessagebox(oSearch) { SetWindowTitle("Replace All") SetText("Operation Done") show() } Func pSearchClose oSearch.close() oSearch = NULL func pSearchKeyPress if oSearchFilter.getKeyCode() = Qt_Key_Escape pSearchClose() ok func pFindValue oCursor = textedit1.textcursor() nPosStart = oCursor.Position() + 1 cValue = oSearchValue.text() cStr = textedit1.toplaintext() cStr = substr(cStr,nPosStart) if oSearchCase.checkState() = Qt_Unchecked cStr = lower(cStr) cValue = lower(cValue) ok nPos = substr(cStr,cValue) if nPos > 0 nPos += nPosStart - 2 oCursor = textedit1.textcursor() oCursor.setposition(nPos,0) (continues on next page) 45.56. Notepad Application 498
543.
Ring Documentation, Release1.21.2 (continued from previous page) textedit1.settextcursor(oCursor) oCursor = textedit1.textcursor() oCursor.setposition(nPos+len(cValue),1) textedit1.settextcursor(oCursor) return true else new qMessagebox(oSearch) { SetWindowTitle("Search") SetText("Cannot find :" + cValue) show() } return false ok func pNofileopened New qMessageBox(win1) { setWindowTitle("Sorry") setText("Save the file first!") show() } func pDebug if cActiveFileName = Null return pNofileopened() ok cCode = "start run " + cActiveFileName + nl system(cCode) func pRun if cActiveFileName = Null return pNofileopened() ok cCode = "start ring " + cActiveFileName + nl system(cCode) func pRunNoConsole if cActiveFileName = Null return pNofileopened() ok cCode = "start /b ring " + cActiveFileName + nl system(cCode) func pSave if cActiveFileName = NULL return pSaveAs() ok writefile(cActiveFileName,textedit1.toplaintext()) status1.showmessage("File : " + cActiveFileName + " saved!",0) lAskToSave = false func pSaveAs new qfiledialog(win1) { cName = getsavefilename(win1,"Save As","","source files(*.ring)") if cName != NULL cActiveFileName = cName writefile(cActiveFileName,textedit1.toplaintext()) status1.showmessage("File : " + cActiveFileName + " saved!",0) pSetActiveFileName() lAskToSave = false (continues on next page) 45.56. Notepad Application 499
Ring Documentation, Release1.21.2 (continued from previous page) func pOpen new qfiledialog(win1) { cName = getopenfilename(win1,"open file","c:","source files(*.ring)") if cName != NULL cActiveFileName = cName textedit1.settext(read(cActiveFileName)) ok } func pNew new qfiledialog(win1) { cName = getsavefilename(win1,"New file","","source files(*.ring)") if cName != NULL write(cName,"") cActiveFileName = cName textedit1.settext(read(cActiveFileName)) ok } Func WriteFile cFileName,cCode aCode = str2list(cCode) fp = fopen(cFileName,"wb") for cLine in aCode fwrite(fp,cLine+char(13)+char(10)) next fclose(fp) Func MsgBox cTitle,cMessage new qMessagebox(win1) { setwindowtitle(cTitle) setText(cMessage) show() } Func pLang MsgBox("Programming Language", "This application developed using the Ring programming language") Func pGUI MsgBox("GUI Library", "This application uses the Qt GUI Library through RingQt") Func pAbout MsgBox("About", "2016, Mahmoud Fayed <msfclipper@yahoo.com>") Func pSaveSettings cSettings = "aTextColor = ["+aTextColor[1]+","+aTextColor[2]+ ","+aTextColor[3]+"]" + nl + "aBackColor = ["+aBackColor[1]+","+aBackColor[2]+ (continues on next page) 45.56. Notepad Application 501
546.
Ring Documentation, Release1.21.2 (continued from previous page) ","+aBackColor[3]+"]" + nl + "cFont = '" + cFont + "'" + nl + "cWebSite = '" + cWebsite + "'" + nl cSettings = substr(cSettings,nl,char(13)+char(10)) write("ringnotepad.ini",cSettings) if lAsktoSave new qmessagebox(win1) { setwindowtitle("Save Changes?") settext("Some changes are not saved!") setInformativeText("Do you want to save your changes?") setstandardbuttons(QMessageBox_Yes | QMessageBox_No | QMessageBox_Cancel) result = exec() win1 { if result = QMessageBox_Yes pSave() but result = QMessageBox_Cancel return false ok } } ok return true Func pSetWebsite oWebView { loadpage(new qurl(cWebSite)) } oWBText { setText(cWebSite) } Func RestoreSettings eval(read("ringnotepad.ini")) pSetColors() pSetFont() pSetWebsite() Func pQuit if pSaveSettings() myapp.quit() ok The application during the runtime The next screen shot demonstrates the “File” menu 45.56. Notepad Application 502
547.
Ring Documentation, Release1.21.2 The next window for “search and replace” The next screen shot demonstrates the application main window 45.56. Notepad Application 503
548.
Ring Documentation, Release1.21.2 Note: the functions pDebug(), pRun() and pRunNoConsole() in the previous sample are not portable! They are written in this sample for MS-Windows and we can update them for other operating systems. 45.57 The Cards Game In the next example we will see a simple Cards game developed using RingQt Each player get 5 cards, the cards are unknown to any one. each time one player click on one card to see it. if the card is identical to another card the play get point for each card. if the card value is “5” the player get points for all visible cards. Load "guilib.ring" nScale = 1 app1 = new qApp mypic = new QPixmap("cards.jpg") mypic2 = mypic.copy(0,(124*4)+1,79,124) Player1EatPic = mypic.copy(80,(124*4)+1,79,124) Player2EatPic= mypic.copy(160,(124*4)+1,79,124) aMyCards = [] aMyValues = [] for x1 = 0 to 3 for y1 = 0 to 12 temppic = mypic.copy((79*y1)+1,(124*x1)+1,79,124) aMyCards + temppic (continues on next page) 45.57. The Cards Game 504
549.
Ring Documentation, Release1.21.2 (continued from previous page) aMyValues + (y1+1) next next nPlayer1Score = 0 nPlayer2Score=0 do Page1 = new Game Page1.Start() again Page1.lnewgame mypic.delete() mypic2.delete() Player1EatPic.delete() Player2EatPic.delete() for t in aMyCards t.delete() next func gui_setbtnpixmap pBtn,pPixmap pBtn { setIcon(new qicon(pPixmap.scaled(width(),height(),0,0))) setIconSize(new QSize(width(),height())) } Class Game nCardsCount = 10 win1 layout1 label1 label2 layout2 layout3 aBtns aBtns2 aCards nRole=1 aStatus = list(nCardsCount) aStatus2 = aStatus aValues aStatusValues = aStatus aStatusValues2 = aStatus Player1EatPic Player2EatPic lnewgame = false nDelayEat = 0.5 nDelayNewGame = 1 func start win1 = new qWidget() { setwindowtitle("Five") setstylesheet("background-color: White") showfullscreen() } layout1 = new qvboxlayout() label1 = new qlabel(win1) { settext("Player (1) - Score : " + nPlayer1Score) setalignment(Qt_AlignHCenter | Qt_AlignVCenter) setstylesheet("color: White; background-color: Purple; font-size:20pt") (continues on next page) 45.57. The Cards Game 505
550.
Ring Documentation, Release1.21.2 (continued from previous page) setfixedheight(200) } closebtn = new qpushbutton(win1) { settext("Close Application") setstylesheet("font-size: 18px ; color : white ; background-color: black ;") setclickevent("Page1.win1.close()") } aCards = aMyCards aValues = aMyValues layout2 = new qhboxlayout() aBtns = [] for x = 1 to nCardsCount aBtns + new qpushbutton(win1) aBtns[x].setfixedwidth(79*nScale) aBtns[x].setfixedheight(124*nScale) gui_setbtnpixmap(aBtns[x],mypic2) layout2.addwidget(aBtns[x]) aBtns[x].setclickevent("Page1.Player1click("+x+")") next layout1.addwidget(label1) layout1.addlayout(layout2) label2 = new qlabel(win1) { settext("Player (2) - Score : " + nPlayer2Score) setalignment(Qt_AlignHCenter | Qt_AlignVCenter) setstylesheet("color: white; background-color: red; font-size:20pt") setfixedheight(200) } layout3 = new qhboxlayout() aBtns2 = [] for x = 1 to nCardsCount aBtns2 + new qpushbutton(win1) aBtns2[x].setfixedwidth(79*nScale) aBtns2[x].setfixedheight(124*nScale) gui_setbtnpixmap(aBtns2[x],mypic2) layout3.addwidget(aBtns2[x]) aBtns2[x].setclickevent("Page1.Player2click("+x+")") next layout1.addwidget(label2) layout1.addlayout(layout3) layout1.addwidget(closebtn) (continues on next page) 45.57. The Cards Game 506
551.
Ring Documentation, Release1.21.2 (continued from previous page) win1.setlayout(layout1) app1.exec() Func Player1Click x if nRole = 1 and aStatus[x] = 0 nPos = ((random(100)+clock())%(len(aCards)-1)) + 1 gui_setbtnpixmap(aBtns[x],aCards[nPos]) del(aCards,nPos) nRole = 2 aStatus[x] = 1 aStatusValues[x] = aValues[nPos] del(aValues,nPos) Player1Eat(x,aStatusValues[x]) checknewgame() ok Func Player2Click x if nRole = 2 and aStatus2[x] = 0 nPos = ((random(100)+clock())%(len(aCards)-1)) + 1 gui_setbtnpixmap(aBtns2[x],aCards[nPos]) del(aCards,nPos) nRole = 1 aStatus2[x] = 1 aStatusValues2[x] = aValues[nPos] del(aValues,nPos) Player2Eat(x,aStatusValues2[x]) checknewgame() ok Func Player1Eat nPos,nValue app1.processEvents() delay(nDelayEat) lEat = false for x = 1 to nCardsCount if aStatus2[x] = 1 and (aStatusValues2[x] = nValue or nValue=5) aStatus2[x] = 2 gui_setbtnpixmap(aBtns2[x],Player1EatPic) lEat = True nPlayer1Score++ ok if (x != nPos) and (aStatus[x] = 1) and (aStatusValues[x] = nValue or nValue=5) aStatus[x] = 2 gui_setbtnpixmap(aBtns[x],Player1EatPic) lEat = True nPlayer1Score++ ok next (continues on next page) 45.57. The Cards Game 507
552.
Ring Documentation, Release1.21.2 (continued from previous page) if lEat nPlayer1Score++ gui_setbtnpixmap(aBtns[nPos],Player1EatPic) aStatus[nPos] = 2 label1.settext("Player (1) - Score : " + nPlayer1Score) ok Func Player2Eat nPos,nValue app1.processEvents() delay(nDelayEat) lEat = false for x = 1 to nCardsCount if aStatus[x] = 1 and (aStatusValues[x] = nValue or nValue = 5) aStatus[x] = 2 gui_setbtnpixmap(aBtns[x],Player2EatPic) lEat = True nPlayer2Score++ ok if (x != nPos) and (aStatus2[x] = 1) and (aStatusValues2[x] = nValue or nValue=5 ) aStatus2[x] = 2 gui_setbtnpixmap(aBtns2[x],Player2EatPic) lEat = True nPlayer2Score++ ok next if lEat nPlayer2Score++ gui_setbtnpixmap(aBtns2[nPos],Player2EatPic) aStatus2[nPos] = 2 label2.settext("Player (2) - Score : " + nPlayer2Score) ok Func checknewgame if isnewgame() lnewgame = true if nPlayer1Score > nPlayer2Score label1.settext("Player (1) Wins!!!") ok if nPlayer2Score > nPlayer1Score label2.settext("Player (2) Wins!!!") ok app1.processEvents() delay(nDelayNewGame) win1.delete() app1.quit() (continues on next page) 45.57. The Cards Game 508
553.
Ring Documentation, Release1.21.2 (continued from previous page) ok Func isnewgame for t in aStatus if t = 0 return false ok next for t in aStatus2 if t = 0 return false ok next return true Func delay x nTime = x * 1000 oTest = new qTest oTest.qsleep(nTime) The application during the runtime Note: in the previous screen shot the player get the card number ‘5’ but his score is not increased because he opened this card while no other cards are visible! The next screen shot while running the game using a Mobile (Android) 45.57. The Cards Game 509
554.
Ring Documentation, Release1.21.2 Note: using Qt we can run the same application on other Mobile systems 45.58 Classes and their Methods to use the default events The next table present the class name and the methods that we have to use the default events. Class Name Methods to use the default Events QPushButton SetClickEvent() QAction SetClickEvent() QLineEdit SetTextChangedEvent() SetCursorPositionChangedEvent() SetEditingFinishedEvent() SetReturnPressedEvent() SetSelectionChangedEvent() SetTextEditedEvent() QTextEdit SetCopyAvailableEvent() SetCurrentCharFormatChangedEvent() continues on next page 45.58. Classes and their Methods to use the default events 510
555.
Ring Documentation, Release1.21.2 Table 1 – continued from previous page Class Name Methods to use the default Events SetCursorPositionChangedEvent() SetRedoAvailableEvent() SetSelectionChangedEvent() SetTextChangedEvent() SetUndoAvailableEvent() QListWidget SetCurrentItemChangedEvent() SetCurrentRowChangedEvent() SetCurrentTextChangedEvent() SetItemActivatedEvent() SetItemChangedEvent() SetItemClickedEvent() SetItemDoubleClickedEvent() SetItemEnteredEvent() SetItemPressedEvent() SetItemSelectionChangedEvent() QTreeView SetCollapseEvent() SetExpandedEvent() SetActivatedEvent() SetClickedEvent() SetDoubleClickedEvent() SetEnteredEvent() SetPressedEvent() SetViewportEnteredEvent() QTreeWidget SetCollapsedEvent() SetExpandedEvent() SetActivatedEvent() SetClickedEvent() SetDoubleClickedEvent() SetEnteredEvent() SetPressedEvent() SetViewportEnteredEvent() SetCurrentItemChangedEvent() SetItemActivatedEvent() SetItemChangedEvent() SetItemClickedEvent() SetItemCollapsedEvent() SetItemDoubleClickedEvent() SetItemEnteredEvent() SetItemExpandedEvent() SetItemPressedEvent() SetItemSelectionChangedEvent() QComboBox SetActivatedEvent() SetCurrentIndexChangedEvent() SetEditTextChangedEvent() SetHighlightedEvent() QTabWidget SetCurrentChangedEvent() SetTabCloseRequestedEvent() QTableWidget SetCellActivatedEvent() SetCellChangedEvent() SetCellClickedEvent() continues on next page 45.58. Classes and their Methods to use the default events 511
556.
Ring Documentation, Release1.21.2 Table 1 – continued from previous page Class Name Methods to use the default Events SetCellDoubleClickedEvent() SetCellEnteredEvent() SetCellPressedEvent() SetCurrentCellChangedEvent() SetCurrentItemChangedEvent() SetItemActivatedEvent() SetItemChangedEvent() SetItemClickedEvent() SetItemDoubleClickedEvent() SetItemEnteredEvent() SetItemPressedEvent() SetItemSelectionChangedEvent() QProgressBar SetValueChangedEvent() QSpinBox SetValueChangedEvent() QSlider SetActionTriggeredEvent() SetRangeChangedEvent() SetSliderMovedEvent() SetSliderPressedEvent() SetSliderReleasedEvent() SetValueChangedEvent() QDial SetActionTriggeredEvent() SetRangeChangedEvent() SetSliderMovedEvent() SetSliderPressedEvent() SetSliderReleasedEvent() SetValueChangedEvent() QWebView SetLoadFinishedEvent() SetLoadProgressEvent() SetLoadStartedEvent() SetSelectionChangedEvent() SetTitleChangedEvent() SetUrlChangedEvent() QCheckBox SetStateChangedEvent() SetClickedEvent() SetPressedEvent() SetReleasedEvent() SetToggledEvent() QRadioButton SetClickedEvent() SetPressedEvent() SetReleasedEvent() SetToggledEvent() QButtonGroup SetButtonClickedEvent() SetButtonPressedEvent() SetButtonReleasedEvent() QVideoWidget SetBrightnessChangedEvent() SetContrastChangedEvent() SetFullScreenChangedEvent() SetHueChangedEvent() SetSaturationChangedEvent() QTimer SetTimeoutEvent() continues on next page 45.58. Classes and their Methods to use the default events 512
557.
Ring Documentation, Release1.21.2 Table 1 – continued from previous page Class Name Methods to use the default Events QTcpServer SetAcceptErrorEvent() SetNewConnectionEvent() QIODevice SetAboutToCloseEvent() SetBytesWrittenEvent() SetReadChannelFinishedEvent() SetReadyReadEvent() QAbstractSocket SetConnectedEvent() SetDisconnectedEvent() SetErrorEvent() SetHostFoundEvent() SetProxyAuthenticationRequiredEvent() SetStateChangedEvent() QTcpSocket SetConnectedEvent() SetDisconnectedEvent() SetErrorEvent() SetHostFoundEvent() SetProxyAuthenticationRequiredEvent() SetStateChangedEvent() SetAboutToCloseEvent() SetBytesWrittenEvent() SetReadChannelFinishedEvent() SetReadyReadEvent() QColorDialog SetColorSelectedEvent() SetCurrentColorChangedEvent() QNetworkAccessManager SetFinishedEvent() QThread SetStartedEvent() SetFinishedEvent() 45.59 Methods to use Events with Events Filter RingQt define a new class called QAllEvents that help you in using Events Filter The next table presents the methods that we have Methods to get parameters Class Name getKeyCode() –> Number QAllEvents getx() –> Number gety() –> Number getglobalx() –> Number getglobaly() –> Number getbutton() –> Number getbuttons() –> Number The next table presents the methods that we have to use events. 45.59. Methods to use Events with Events Filter 513
558.
Ring Documentation, Release1.21.2 Method Name Class Name setKeyPressEvent(cEvent) QAllEvents setMouseButtonPressEvent(cEvent) setMouseButtonReleaseEvent(cEvent) setMouseButtonDblClickEvent(cEvent) setMouseMoveEvent(cEvent) setCloseEvent(cEvent) setContextMenuEvent(cEvent) setDragEnterEvent(cEvent) setDragLeaveEvent(cEvent) setDragMoveEvent(cEvent) setDropEvent(cEvent) setEnterEvent(cEvent) setFocusInEvent(cEvent) setFocusOutEvent(cEvent) setKeyReleaseEvent(cEvent) setLeaveEvent(cEvent) setNonClientAreaMouseButtonDblClickEvent(cEvent) setNonClientAreaMouseButtonPressEvent(cEvent) setNonClientAreaMouseButtonReleaseEvent(cEvent) setNonClientAreaMouseMoveEvent(cEvent) setMoveEvent(cEvent) setResizeEvent(cEvent) setWindowActivateEvent(cEvent) setWindowBlockedEvent(cEvent) setWindowDeactivateEvent(cEvent) setWindowStateChangeEvent(cEvent) setWindowUnblockedEvent(cEvent) 45.60 The Difference between Qt and RingQt (1) RingQt use simple methods to set the code that will be executed for events. Syntax: Set<Event_Name>Event(cEventCode) (2) RingQt change the name of some methods to avoid conflict with Ring Keywords. The next table present these little changes 45.60. The Difference between Qt and RingQt 514
559.
Ring Documentation, Release1.21.2 Class Name Qt Method Name RingQt Method Name QWebView load loadpage QMediaPlaylist load loadfile QMediaPlaylist next movenext QPainter end endpaint QPicture load loadfile QLineEdit end endtext QDialog done donedialog QTextDocument end enddoc QTextBlock next nextblock QSqlQuery next movenext QImage load loadimage QNetworkAccessManager get getvalue QNetworkAccessManager put putvalue QThread exit exitfromthread QRegularExpressionMatchIterator next nextitem QCamera load loadcamera 45.61 RingQt Classes and their Qt Documentation Qt Documentation : http://doc.qt.io/qt-5/classes.html See the “RingQt Classes and Methods Reference” chapter for supported classes and methods. 45.62 New Classes names - Index Start from 1 We added new classes to RingQt - another version of classes where the class names doesn’t start with the “q” letter Also updated methods so the index start from 1 when we deal with the GUI controls like • ComboBox • ListWidget • TableWidget • TreeWidget These classes are inside guilib.ring under the package name : System.GUI To use it load "guilib.ring" import System.GUI This doesn’t have any effect on our previous code, It’s just another choice for better code that is consistent with Ring rules. Also the form designer is updated to provide us the choice between using classes where (index start from 0) or (index start from 1) Example (Uses the Form Designer) 45.61. RingQt Classes and their Qt Documentation 515
560.
Ring Documentation, Release1.21.2 (1) https://github.com/ring-lang/ring/blob/master/samples/UsingFormDesigner/indexstart/indexstartView.ring (2) https://github.com/ring-lang/ring/blob/master/samples/UsingFormDesigner/indexstart/indexstartController. ring 45.63 TableWidget - AddList() Method Using this method we can add Ring List to the TableWidget Tip: TableWidget class is a subclass of QTableWidget class Note: To use TableWidget class, import system.gui after loading guilib.ring or lightguilib.ring Example: Source code: https://github.com/ring-lang/ring/tree/master/samples/UsingQt/TableWidget/AddRingList class addRingListController from windowsControllerParent oView = new addRingListView aList = [["one","two"], ["three","four"], ["five","six"], [7,8], ["I","Love","Ring","Programming"]] oView.tablewidget1.addList(aList) aList = [["Number","Square"]] for t = 1 to 10 aList + [ t, t*t] next oView.tablewidget1.addList(aList) Screen Shot: 45.63. TableWidget - AddList() Method 516
561.
Ring Documentation, Release1.21.2 45.64 Creating Reports using the WebLib and the GUILib The WebLib comes with a class called HtmlPage Using this class we can create reports quickly using WebLib & GUILib together Example: load "stdlib.ring" load "weblib.ring" load "guilib.ring" import System.Web import System.GUI (continues on next page) 45.64. Creating Reports using the WebLib and the GUILib 517
562.
Ring Documentation, Release1.21.2 (continued from previous page) new qApp { open_window(:CustomersReportController) exec() } class CustomersReportController from WindowsControllerParent oView = new CustomersReportView func Start CreateReport() func CreateReport mypage = new HtmlPage { h1 { text("Customers Report") } Table { style = stylewidth("100%") + stylegradient(4) TR { TD { WIDTH="10%" text("Customers Count : " ) } TD { text (100) } } } Table { style = stylewidth("100%") + stylegradient(26) TR { style = stylewidth("100%") + stylegradient(24) TD { text("Name " ) } TD { text("Age" ) } TD { text("Country" ) } TD { text("Job" ) } TD { text("Company" ) } } for x = 1 to 100 TR { TD { text("Test" ) } TD { text("30" ) } TD { text("Egypt" ) } TD { text("Sales" ) } TD { text("Future" ) } } next } } write("report.html",mypage.output()) (continues on next page) 45.64. Creating Reports using the WebLib and the GUILib 518
563.
Ring Documentation, Release1.21.2 (continued from previous page) func PrintEvent printer1 = new qPrinter(0) { setoutputformat(1) setoutputfilename("report.pdf") } oView { web.print(printer1, Method( :OpenPDF ) ) web.show() } func OpenPDF new QDesktopServices { OpenURL(new qURL("report.pdf") ) } class CustomersReportView win = new window() { setwindowtitle("Report Window") setgeometry(100,100,500,500) web = new webview(win) { setgeometry(100,100,1000,500) loadpage(new qurl("file:///"+ currentdir()+"/report.html")) } new pushbutton(win) { setGeometry(100,20,100,30) settext("Print") setclickevent(Method(:PrintEvent)) } showMaximized() } Screen Shot: 45.64. Creating Reports using the WebLib and the GUILib 519
CHAPTER FORTYSIX USING THE FORMDESIGNER In this chapter we will learn about using the Form Designer. We can run the From Designer from Ring Notepad From the Menubar in Ring Notepad - View Menu - We can Show/Hide the Form Designer window. Also we can run the Form Designer in another window. From the Ring Notepad - Tools Menu - Select the Form Designer. 521
566.
Ring Documentation, Release1.21.2 46.1 The Designer Windows • Toolbox : To select controls to be added to the window. • Properties : To set the properties of the active window or controls. • Design Region : To select, move and resize the window and the controls. 46.2 The Toolbox We have many buttons. • Lock : We can use it to draw many controls of the same type quickly. • Select : We can use it to select a control in the Design Region • Controls Buttons : Select a control to be added to the window. 46.3 The Properties • When we select the window or one control, We will have the selected object properties. • Also In the properties window we have a combobox to select the active control. • Some properties provide a button next to the property value. We can click on the button to get more options. • When we select more than one control, We will have options for multi-selection 46.1. The Designer Windows 522
567.
Ring Documentation, Release1.21.2 46.4 Running Forms When we save the form file (*.rform), The Form Designer will create two Ring files • The Controller Class • The View Class For example, if the form file is helloworld.rform The form designer will generate two files • helloworldcontroller.ring • helloworldview.ring To run the program, Open the controller class file then click the Run button (CTRL+F5) Tip: When you open a form using Ring Notepad, the controller class will be opened automatically, So we can press (CTRL+F5) or click on the Run button while the form designer window is active. 46.4. Running Forms 523
568.
Ring Documentation, Release1.21.2 46.5 Events Code (1) Just type the method name in the event property. This will add the next code to the Controller Class func CloseWindow oView { } (2) Then write the method code in the controller class. 46.5. Events Code 524
569.
Ring Documentation, Release1.21.2 In this example we write func CloseWindow oView.win.close() Where inside the controller class, We uses the oView object to access the form. Another Example : 46.5. Events Code 525
570.
Ring Documentation, Release1.21.2 The Event Code func SayHello oView { LineEdit2.setText("Hello "+ LineEdit1.text() ) } 46.6 Keyboard Shortcuts After selecting one or group of controls • Use the Arrows (Up, Down, Left and Right) to move them around. • Shift + the Arrows (Up, Down, Left and Right) to Resize the controls. • Del button to delete the controls. • CTRL+SHIFT+V to Duplicate the controls. 46.7 Menubar Designer From the Window properties we can open the Menubar Designer 46.6. Keyboard Shortcuts 526
571.
Ring Documentation, Release1.21.2 46.8 Window Flags From the Window properties we can open the Window Flags window. 46.8. Window Flags 527
572.
Ring Documentation, Release1.21.2 46.9 Entering Items For some controls like the List Widget we can enter items separated by comma ‘,’ 46.10 Using Layouts (1) To use layouts, At first add the layout control to the window. (2) Use the window “Set Layout” property to determine the main layout. (3) From the layout properties determine the controls and the layout type. 46.11 Objects Order and Layouts All the objects that are added to a layout must comes first! If we have a form that contains two buttons and a Layout Then adding a third button after creating the layout requires changing the objects order We can open the (Objects Order) window using a button from the (Properties) window 46.9. Entering Items 528
573.
Ring Documentation, Release1.21.2 Then we can select the (Button3) and click (Move Up) 46.11. Objects Order and Layouts 529
574.
Ring Documentation, Release1.21.2 Then we close the (Objects Order) window 46.11. Objects Order and Layouts 530
575.
Ring Documentation, Release1.21.2 46.12 More Samples and Tests Check the folder : ring/samples/UsingFormDesigner Online : https://github.com/ring-lang/ring/tree/master/samples/UsingFormDesigner 46.12. More Samples and Tests 531
576.
CHAPTER FORTYSEVEN GRAPHICS PROGRAMMING USINGRINGQT3D In this chapter we will learn how to use Qt3D through many samples. 47.1 Drawing Cube load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) (continues on next page) 532
577.
Ring Documentation, Release1.21.2 (continued from previous page) oCube = new QCuboidMesh(oRootEntity) { setXextent(2) setYextent(2) setZextent(3) } oCubeTransform = new QTransform(oCube) oCubeTransform.setScale(2) oCubeTransform.setTranslation(new QVector3D(3, 3, 3)) oCubeMaterial = new QPhongMaterial(oCube) oCubeMaterial.setDiffuse(new QColor() {setRGB(200,100,100,100)}) oCubeEntity = new QEntity(oRootEntity) oCubeEntity.addComponent(oCube) oCubeEntity.addComponent(oCubeMaterial) oCubeEntity.addComponent(oCubeTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Cube") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.1. Drawing Cube 533
578.
Ring Documentation, Release1.21.2 47.2 Drawing Torus load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) (continues on next page) 47.2. Drawing Torus 534
579.
Ring Documentation, Release1.21.2 (continued from previous page) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oTorus = new QTorusMesh(oRootEntity) oTorus.setRadius(1.0) oTorus.setMinorRadius(0.4) oTorus.setRings(100) oTorus.setSlices(20) oTorusTransform = new QTransform(oTorus) oTorusTransform.setScale(2) oTorusTransform.setTranslation(new QVector3D(3, 3, 3)) oTorusMaterial = new QPhongMaterial(oTorus) oTorusMaterial.setDiffuse(new QColor() {setRGB(200,100,100,100)}) oTorusEntity = new QEntity(oRootEntity) oTorusEntity.addComponent(oTorus) oTorusEntity.addComponent(oTorusMaterial) oTorusEntity.addComponent(oTorusTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Torus") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.2. Drawing Torus 535
580.
Ring Documentation, Release1.21.2 47.3 Drawing Sphere load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) (continues on next page) 47.3. Drawing Sphere 536
581.
Ring Documentation, Release1.21.2 (continued from previous page) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oSphere = new QSphereMesh(oRootEntity) oSphere.setRadius(1.0) oSphere.setRings(100) oSphere.setSlices(20) oSphereTransform = new QTransform(oSphere) oSphereTransform.setScale(2) oSphereTransform.setTranslation(new QVector3D(3, 3, 3)) oSphereMaterial = new QPhongMaterial(oSphere) oSphereMaterial.setDiffuse(new QColor() {setRGB(200,100,100,100)}) oSphereEntity = new QEntity(oRootEntity) oSphereEntity.addComponent(oSphere) oSphereEntity.addComponent(oSphereMaterial) oSphereEntity.addComponent(oSphereTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Sphere") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.3. Drawing Sphere 537
582.
Ring Documentation, Release1.21.2 47.4 Drawing Cylinder load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) (continues on next page) 47.4. Drawing Cylinder 538
583.
Ring Documentation, Release1.21.2 (continued from previous page) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oCylinder = new QCylinderMesh(oRootEntity) oCylinder.setRadius(1) oCylinder.setRings(100) oCylinder.setSlices(20) oCylinder.setLength(5) oCylinderTransform = new QTransform(oCylinder) oCylinderTransform.setScale(2) oCylinderTransform.setTranslation(new QVector3D(1, 0, 3)) oCylinderMaterial = new QPhongMaterial(oCylinder) oCylinderMaterial.setDiffuse(new QColor() {setRGB(200,100,100,100)}) oCylinderEntity = new QEntity(oRootEntity) oCylinderEntity.addComponent(oCylinder) oCylinderEntity.addComponent(oCylinderMaterial) oCylinderEntity.addComponent(oCylinderTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Cylinder") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.4. Drawing Cylinder 539
584.
Ring Documentation, Release1.21.2 47.5 Drawing Cone load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) (continues on next page) 47.5. Drawing Cone 540
585.
Ring Documentation, Release1.21.2 (continued from previous page) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oCone = new QConeMesh(oRootEntity) oCone.setRings(100) oCone.setSlices(20) oCone.setLength(5) oConeTransform = new QTransform(oCone) oConeTransform.setScale(2) oConeTransform.setTranslation(new QVector3D(1, 0, 3)) oConeMaterial = new QPhongMaterial(oCone) oConeMaterial.setDiffuse(new QColor() {setRGB(200,100,100,100)}) oConeEntity = new QEntity(oRootEntity) oConeEntity.addComponent(oCone) oConeEntity.addComponent(oConeMaterial) oConeEntity.addComponent(oConeTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Cone") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.5. Drawing Cone 541
586.
Ring Documentation, Release1.21.2 47.6 Drawing Plane load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) (continues on next page) 47.6. Drawing Plane 542
587.
Ring Documentation, Release1.21.2 (continued from previous page) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oPlane = new QPlaneMesh(oRootEntity) oPlane.setHeight(5) oPlane.setWidth(5) oPlane.setmeshresolution(new qSize(10,10)) oPlaneTransform = new QTransform(oPlane) oPlaneTransform.setScale(2) oPlaneTransform.setTranslation(new QVector3D(0, -4, 4)) oPlaneMaterial = new QPhongMaterial(oPlane) oPlaneMaterial.setDiffuse(new QColor() {setRGB(200,100,100,100)}) oPlaneEntity = new QEntity(oRootEntity) oPlaneEntity.addComponent(oPlane) oPlaneEntity.addComponent(oPlaneMaterial) oPlaneEntity.addComponent(oPlaneTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Plane") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.6. Drawing Plane 543
588.
Ring Documentation, Release1.21.2 47.7 Texture load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) (continues on next page) 47.7. Texture 544
589.
Ring Documentation, Release1.21.2 (continued from previous page) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oCube = new QCuboidMesh(oRootEntity) { setXextent(2) setYextent(2) setZextent(3) } oCubeTransform = new QTransform(oCube) oCubeTransform.setScale(2) oCubeTransform.setTranslation(new QVector3D(3, 3, 3)) oTextureLoader = new QTextureLoader(oCube); oTextureLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/gold.jpg") ) oCubeMaterial = new QTextureMaterial(oCube) oCubeMaterial.setTexture(oTextureLoader) oCubeEntity = new QEntity(oRootEntity) oCubeEntity.addComponent(oCube) oCubeEntity.addComponent(oCubeMaterial) oCubeEntity.addComponent(oCubeTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Adding Texture") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.7. Texture 545
590.
Ring Documentation, Release1.21.2 47.8 Key Press load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oFilter = new qallevents(oView) oFilter.setKeyPressEvent("pKeyPress()") oView.installeventfilter(oFilter) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) (continues on next page) 47.8. Key Press 546
591.
Ring Documentation, Release1.21.2 (continued from previous page) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oCamController.setEnabled(False) oCube = new QCuboidMesh(oRootEntity) { setXextent(2) setYextent(2) setZextent(3) } oCubeTransform = new QTransform(oCube) oCubeTransform.setScale(2) oCubeTransform.setTranslation(new QVector3D(3, 3, 3)) oTextureLoader = new QTextureLoader(oCube); oTextureLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/gold.jpg") ) oCubeMaterial = new QTextureMaterial(oCube) oCubeMaterial.setTexture(oTextureLoader) oCubeEntity = new QEntity(oRootEntity) oCubeEntity.addComponent(oCube) oCubeEntity.addComponent(oCubeMaterial) oCubeEntity.addComponent(oCubeTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Moving Cube using the Keyboard") resize(800,600) setLayout(oLayout) showMaximized() } oContainer.setfocus(0) exec() } func pKeyPress nKey = oFilter.getkeycode() oX = oCubeTransform.translation().x() (continues on next page) 47.8. Key Press 547
592.
Ring Documentation, Release1.21.2 (continued from previous page) oY = oCubeTransform.translation().y() oZ = oCubeTransform.translation().z() switch nKey on Qt_Key_Right oX++ on Qt_Key_Left oX-- on Qt_Key_Up oY++ on Qt_Key_Down oY-- off oCubeTransform.setTranslation(new QVector3D(oX, oY, oZ)) 47.9 Object Picker load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() (continues on next page) 47.9. Object Picker 548
593.
Ring Documentation, Release1.21.2 (continued from previous page) oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oFilter = new qallevents(oView) oFilter.setKeyPressEvent("pKeyPress()") oView.installeventfilter(oFilter) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oCamController.setEnabled(False) oCube = new QCuboidMesh(oRootEntity) { setXextent(2) setYextent(2) setZextent(3) } oCubeTransform = new QTransform(oCube) oCubeTransform.setScale(2) oCubeTransform.setTranslation(new QVector3D(3, 3, 3)) oTextureLoader = new QTextureLoader(oCube); oTextureLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/gold.jpg") ) oCubeMaterial = new QTextureMaterial(oCube) oCubeMaterial.setTexture(oTextureLoader) oCubeEntity = new QEntity(oRootEntity) oCubeEntity.addComponent(oCube) oCubeEntity.addComponent(oCubeMaterial) oCubeEntity.addComponent(oCubeTransform) (continues on next page) 47.9. Object Picker 549
594.
Ring Documentation, Release1.21.2 (continued from previous page) oPicker = new qObjectPicker(oCube) { setclickedevent("pClick()") } oCubeEntity.addComponent(oPicker) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Object Picker - Click on the Cube") resize(800,600) setLayout(oLayout) showMaximized() } oContainer.setfocus(0) exec() } func pKeyPress nKey = oFilter.getkeycode() oX = oCubeTransform.translation().x() oY = oCubeTransform.translation().y() oZ = oCubeTransform.translation().z() switch nKey on Qt_Key_Right oX++ on Qt_Key_Left oX-- on Qt_Key_Up oY++ on Qt_Key_Down oY-- off oCubeTransform.setTranslation(new QVector3D(oX, oY, oZ)) func pClick msginfo("Event","Click") oContainer.setfocus(0) 47.9. Object Picker 550
595.
Ring Documentation, Release1.21.2 47.10 Frame Action load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) (continues on next page) 47.10. Frame Action 551
596.
Ring Documentation, Release1.21.2 (continued from previous page) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oCamController.setEnabled(False) oCube = new QCuboidMesh(oRootEntity) { setXextent(1) setYextent(1) setZextent(1) } oCubeTransform = new QTransform(oCube) oCubeTransform.setScale(2) oCubeTransform.setTranslation(new QVector3D(-5, -5, -5)) oTextureLoader = new QTextureLoader(oCube); oTextureLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/gold.jpg") ) oCubeMaterial = new QTextureMaterial(oCube) oCubeMaterial.setTexture(oTextureLoader) oCubeEntity = new QEntity(oRootEntity) oCubeEntity.addComponent(oCube) oCubeEntity.addComponent(oCubeMaterial) oCubeEntity.addComponent(oCubeTransform) nAngle=0 nSpeed=0.1 oFrameAction = new qFrameAction(oRootEntity) { settriggeredevent("pEvent()") } oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Frame Action") resize(800,600) (continues on next page) 47.10. Frame Action 552
597.
Ring Documentation, Release1.21.2 (continued from previous page) setLayout(oLayout) showMaximized() } exec() } func pEvent # Move the Cube oX = oCubeTransform.translation().x() oY = oCubeTransform.translation().y() oZ = oCubeTransform.translation().z() if oX >= 4 nSpeed=-0.1 but oX <= -10 nSpeed=0.1 ok oCubeTransform.setTranslation( new QVector3D(oX+nSpeed, oY+nSpeed, oZ+nSpeed)) # Rotate the Cube nAngle+=5 if nAngle=360 nAngle=0 ok oQ = new QQuaternion(0,0,0,0) oCubeTransform.setRotation( oQ.fromAxisAndAngle(new QVector3D(0, 1, 0), nAngle)) 47.10. Frame Action 553
598.
Ring Documentation, Release1.21.2 47.11 Text 2D load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oView.defaultframegraph().setclearcolor(new QColor() {setRGB(100,250,150,255)}) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() (continues on next page) 47.11. Text 2D 554
599.
Ring Documentation, Release1.21.2 (continued from previous page) oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oCube = new QCuboidMesh(oRootEntity) { setXextent(2) setYextent(2) setZextent(3) } oCubeTransform = new QTransform(oCube) oCubeTransform.setScale(2) oCubeTransform.setTranslation(new QVector3D(0, 3, 4)) oTextureLoader = new QTextureLoader(oCube); oTextureLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/ring.bmp") ) oCubeMaterial = new QTextureMaterial(oCube) oCubeMaterial.setTexture(oTextureLoader) oCubeEntity = new QEntity(oRootEntity) oCubeEntity.addComponent(oCube) oCubeEntity.addComponent(oCubeMaterial) oCubeEntity.addComponent(oCubeTransform) oText2DEntity = new QText2DEntity(oRootEntity) { setText("Ring programming language") setWidth(400) setHeight(40) setColor(new QColor() {setRGB(128,128,128,255)}) } oText2DTransform = new QTransform(oText2DEntity) oText2DTransform.setScale(0.1) oText2DTransform.setTranslation(new QVector3D(-10.5, -5, 0)) oText2DEntity.addComponent(oText2DTransform) (continues on next page) 47.11. Text 2D 555
600.
Ring Documentation, Release1.21.2 (continued from previous page) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Text2D") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.12 Extruded Text load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oView.defaultframegraph().setclearcolor(new QColor() {setRGB(100,250,150,255)}) oInput = new QInputAspect(oRootEntity) (continues on next page) 47.12. Extruded Text 556
601.
Ring Documentation, Release1.21.2 (continued from previous page) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oCube = new QCuboidMesh(oRootEntity) { setXextent(2) setYextent(2) setZextent(3) } oCubeTransform = new QTransform(oCube) oCubeTransform.setScale(2) oCubeTransform.setTranslation(new QVector3D(0, 3, 4)) oTextureLoader = new QTextureLoader(oCube); oTextureLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/ring.bmp") ) oCubeMaterial = new QTextureMaterial(oCube) oCubeMaterial.setTexture(oTextureLoader) oCubeEntity = new QEntity(oRootEntity) oCubeEntity.addComponent(oCube) oCubeEntity.addComponent(oCubeMaterial) oCubeEntity.addComponent(oCubeTransform) oTextEntity = new QEntity(oRootEntity) oTextMesh = new QExtrudedTextMesh(oTextEntity) { setText("Ring") } oTextTransform = new QTransform(oTextEntity) oTextTransform.setScale(3) oTextTransform.setTranslation(new QVector3D(-5.5, -4, 3)) (continues on next page) 47.12. Extruded Text 557
602.
Ring Documentation, Release1.21.2 (continued from previous page) oTextMaterial = new QPhongMaterial(oTextEntity); oTextMaterial.setDiffuse(new QColor() {setRGB(0,0,255,255)}) oTextEntity.addComponent(oTextMesh) oTextEntity.addComponent(oTextTransform) oTextEntity.addComponent(oTextMaterial) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Extruded Text") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.12. Extruded Text 558
603.
Ring Documentation, Release1.21.2 47.13 Model load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oModel = new qmesh(oRootEntity) oModel.setsource( new qURL("file:///"+currentdir()+"/assets/model/lucky_cat.obj") ) oModelTransform = new QTransform(oModel) oModelTransform.setScale(0.1) oModelTransform.setTranslation(new QVector3D(0, 0, 0)) oQ = new QQuaternion(0,0,0,0) oModelTransform.setRotation(oQ.fromAxisAndAngle(new QVector3D(0, 1, 0), 180)) oModelMaterial = new QPhongMaterial(oModel) oModelMaterial.setDiffuse(new QColor() {setRGB(0,255,128,255)}) oModelEntity = new QEntity(oRootEntity) oModelEntity.addComponent(oModel) (continues on next page) 47.13. Model 559
604.
Ring Documentation, Release1.21.2 (continued from previous page) oModelEntity.addComponent(oModelmaterial) oModelEntity.addComponent(oModelTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Model (Obj File)") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.14 Model Texture load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) (continues on next page) 47.14. Model Texture 560
605.
Ring Documentation, Release1.21.2 (continued from previous page) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 25, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oModel = new qmesh(oRootEntity) oModel.setsource( new qURL("file:///"+currentdir()+"/assets/model/Robot.obj") ) oModelTransform = new QTransform(oModel) oModelTransform.setScale(0.5) oModelTransform.setTranslation(new QVector3D(0, 12, 4)) oLoader = new QTextureLoader(oModel) oModelMaterial = new QTextureMaterial(oModel) oLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/Robot.jpg") ) oModelMaterial.setTexture(oLoader) oModelEntity = new QEntity(oRootEntity) oModelEntity.addComponent(oModel) oModelEntity.addComponent(oModelMaterial) oModelEntity.addComponent(oModelTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Model Texture") (continues on next page) 47.14. Model Texture 561
606.
Ring Documentation, Release1.21.2 (continued from previous page) resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.15 Draw Office load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) (continues on next page) 47.15. Draw Office 562
607.
Ring Documentation, Release1.21.2 (continued from previous page) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 0, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oModel = new qmesh(oRootEntity) oModel.setsource( new qURL("file:///"+currentdir()+"/assets/model/Reception_Table.obj") ) oModelTransform = new QTransform(oModel) oModelTransform.setScale(1) oModelTransform.setTranslation(new QVector3D(0, -2.5, 16)) oModelMaterial = new QPhongMaterial(oModel) oModelMaterial.setDiffuse(new QColor() {setRGB(0,255,128,255)}) oModelEntity = new QEntity(oRootEntity) oModelEntity.addComponent(oModel) oModelEntity.addComponent(oModelmaterial) oModelEntity.addComponent(oModelTransform) oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Model (Obj File) - Office") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.15. Draw Office 563
608.
Ring Documentation, Release1.21.2 47.16 Many Objects load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) (continues on next page) 47.16. Many Objects 564
609.
Ring Documentation, Release1.21.2 (continued from previous page) oCameraEntity.setViewCenter(new QVector3D(0, 20, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oModel = new qmesh(oRootEntity) oModel.setsource( new qURL("file:///"+currentdir()+"/assets/model/Robot.obj") ) oModelTransform = new QTransform(oModel) oModelTransform.setScale(0.2) oModelTransform.setTranslation(new QVector3D(0, 10, 10)) oModelMaterial = new QPhongMaterial(oModel) oModelMaterial.setDiffuse(new QColor() {setRGB(0,100,0,0)}) oModelEntity = new QEntity(oRootEntity) oModelEntity.addComponent(oModel) oLoader = new QTextureLoader(oModel); oModelMaterial = new QTextureMaterial(oModel) oLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/gold.jpg") ) oModelMaterial.setTexture(oLoader) oModelEntity.addComponent(oModelMaterial) oModelEntity.addComponent(oModelTransform) for n = 1 to 10 oTorus = new QTorusMesh(oRootEntity) oTorus.setRadius(1.0*n) oTorus.setMinorRadius(0.4*n) oTorus.setRings(100) oTorus.setSlices(20) oTorusTransform = new QTransform(null) oTorusTransform.setScale(2) oTorusTransform.setTranslation(new QVector3D(5.0*n, 4.0*n, 0.0)) oTorusMaterial = new QPhongMaterial(null); oTorusMaterial.setDiffuse(new QColor() {setRGB(200,100,100,100)}) (continues on next page) 47.16. Many Objects 565
610.
Ring Documentation, Release1.21.2 (continued from previous page) oTorusEntity = new QEntity(oRootEntity) oTorusEntity.addComponent(oTorus) oLoader = new QTextureLoader(oTorus); oTorusMaterial = new QTextureMaterial(oTorus) oLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/gold.jpg") ) oTorusMaterial.setTexture(oLoader) oTorusEntity.addComponent(oTorusMaterial) oTorusEntity.addComponent(oTorusTransform) next oView.setRootEntity(oRootEntity) oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oWidget { setwindowtitle("Using Qt3D - Many Objects") resize(800,600) setLayout(oLayout) showMaximized() } exec() } 47.16. Many Objects 566
611.
Ring Documentation, Release1.21.2 47.17 Camera load "guilib.ring" new qApp { oView = new Qt3dwindow() oWidget = new QWidget() oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(0, 20, 0)) oLightEntity = new QEntity(oRootEntity) oLight = new QPointLight(oLightEntity) (continues on next page) 47.17. Camera 567
612.
Ring Documentation, Release1.21.2 (continued from previous page) oLight.setColor(new QColor() { setRGB(255,255,255,255) }) oLight.setIntensity(1) oLightEntity.addComponent(oLight) oLightTransform = new QTransform(oLightEntity) oLightTransform.setTranslation(oCameraEntity.position()) oLightEntity.addComponent(oLightTransform) oCamController = new QFirstPersonCameraController(oRootEntity) oCamController.setCamera(oCameraEntity) oModel = new qmesh(oRootEntity) oModel.setsource( new qURL("file:///"+currentdir()+"/assets/model/Robot.obj") ) oModelTransform = new QTransform(oModel) oModelTransform.setScale(0.2) oModelTransform.setTranslation(new QVector3D(0, 10, 10)) oModelMaterial = new QPhongMaterial(oModel) oModelMaterial.setDiffuse(new QColor() {setRGB(0,100,0,0)}) oModelEntity = new QEntity(oRootEntity) oModelEntity.addComponent(oModel) oLoader = new QTextureLoader(oModel); oModelMaterial = new QTextureMaterial(oModel) oLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/gold.jpg") ) oModelMaterial.setTexture(oLoader) oModelEntity.addComponent(oModelMaterial) oModelEntity.addComponent(oModelTransform) for n = 1 to 10 oTorus = new QTorusMesh(oRootEntity) oTorus.setRadius(1.0*n) oTorus.setMinorRadius(0.4*n) oTorus.setRings(100) oTorus.setSlices(20) oTorusTransform = new QTransform(null) oTorusTransform.setScale(2) oTorusTransform.setTranslation(new QVector3D(5.0*n, 4.0*n, 0.0)) oTorusMaterial = new QPhongMaterial(null); oTorusMaterial.setDiffuse(new QColor() {setRGB(200,100,100,100)}) oTorusEntity = new QEntity(oRootEntity) oTorusEntity.addComponent(oTorus) (continues on next page) 47.17. Camera 568
613.
Ring Documentation, Release1.21.2 (continued from previous page) oLoader = new QTextureLoader(oTorus); oTorusMaterial = new QTextureMaterial(oTorus) oLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/gold.jpg") ) oTorusMaterial.setTexture(oLoader) oTorusEntity.addComponent(oTorusMaterial) oTorusEntity.addComponent(oTorusTransform) next oView.setRootEntity(oRootEntity) btn1 = new qPushButton(oWidget) { setText("Move the Camera and the Robot") setClickEvent("pMove()") } oLayout = new QVBoxLayout() oLayout.AddWidget(oContainer) oLayout.AddWidget(btn1) oWidget { setwindowtitle("Using Qt3D - Camera") resize(800,600) setLayout(oLayout) showMaximized() } exec() } func pMove oCameraEntity.setPosition(new QVector3D(0, 0, 20.0)) oCameraEntity.setUpVector(new QVector3D(0, 1, 0)) oCameraEntity.setViewCenter(new QVector3D(20, 15, 20)) oModelTransform.setTranslation(new QVector3D(20, 15, 20)) oQ = new QQuaternion(0,0,0,0) oModelTransform.setRotation(oQ.fromAxisAndAngle(new QVector3D(1, 1, 0), 270)) 47.17. Camera 569
614.
Ring Documentation, Release1.21.2 47.18 Scence load "guilib.ring" new qApp { oWidget = new QWidget() oView = new Qt3DWindow() oView.defaultFrameGraph().setClearColor(new QColor() {setRGB(0,0,0,255)}) oContainer = oWidget.createWindowContainer(oView,oWidget,0) oRootEntity = new QEntity(oContainer) oInput = new QInputAspect(oRootEntity) oView.registerAspect(oInput) oCameraEntity = oView.Camera() oCameraEntity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000) oCameraEntity.setPosition(new QVector3D(-13.34, -6.43, 16.47)) oCameraEntity.setUpVector(new QVector3D(0.02, 0, 1)) oCameraEntity.setViewCenter(new QVector3D(-13.34, 17.05, 15.42)) oCameraController = new QFirstPersonCameraController(oRootEntity) oCameraController.setCamera(oCameraEntity) oCameraController.setEnabled(False) (continues on next page) 47.18. Scence 570
615.
Ring Documentation, Release1.21.2 (continued from previous page) oLongRoomEntity = new QEntity(oRootEntity) oLongRoomModel = new QMesh(oLongRoomEntity) oLongRoomModel.setSource( new qURL("file:///"+CurrentDir()+"/assets/model/Long_Room.obj") ) oLongRoomTransform = new QTransform(oLongRoomEntity) oLongRoomTransform.setScale(1) oLongRoomTransform.setTranslation(new QVector3D(5, 0, 15)) oLongRoomLoader = new QTextureLoader(oLongRoomModel) oLongRoomMaterial = new QTextureMaterial(oLongRoomModel) oLongRoomLoader.setSource( new QUrl("file:///"+currentdir()+"/assets/texture/croc.jpg") ) oLongRoomMaterial.setTexture(oLongRoomLoader) oLongRoomEntity.addComponent(oLongRoomModel) oLongRoomEntity.addComponent(oLongRoomMaterial) oLongRoomEntity.addComponent(oLongRoomTransform) oTableEntity = new QEntity(oRootEntity) oTableModel = new QMesh(oTableEntity) oTableModel.setSource( new qURL("file:///"+CurrentDir()+"/assets/model/Reception_Table.obj") ) oTableTransform = new QTransform(oTableEntity) oTableTransform.setScale(0.3) oTableTransform.setTranslation(new QVector3D(5, 0, 15)) oQ = new QQuaternion(0,0,0,0) oTableTransform.setRotation(oQ.fromAxisAndAngle(new QVector3D(0, 1, 1), -180)) oTableModelMaterial = new QPhongMaterial(oTableEntity) oTableModelMaterial.setDiffuse(new QColor() {setRGB(255,255,255,255)}) oTableEntity.addComponent(oTableModel) oTableEntity.addComponent(oTableModelmaterial) oTableEntity.addComponent(oTableTransform) oFirstLightEntity = new QEntity(oRootEntity) oFirstLight = new QPointLight(oFirstLightEntity) oFirstLight.setColor(new qColor() { setRGB(128,128,128,128) }) oFirstLight.setIntensity(1) oFirstLightTransform = new QTransform(oFirstLightEntity) oFirstLightTransform.setTranslation(new QVector3D(5, 0, 20)) oFirstLightEntity.addComponent(oFirstLight) oFirstLightEntity.addComponent(oFirstLightTransform) (continues on next page) 47.18. Scence 571
616.
Ring Documentation, Release1.21.2 (continued from previous page) aCats = list(5) for n = 1 to 5 v = n * 0.1 aCats[n] = [] aCats[n][:oCatModelEntity] = new QEntity(oRootEntity) aCats[n][:oCatModel] = new QMesh(aCats[n][:oCatModelEntity]) aCats[n][:oCatModel].setSource( new qURL("file:///"+CurrentDir()+"/assets/model/Lucky_Cat.obj") ) aCats[n][:oCatModelMaterial] = new QPhongMaterial(aCats[n][:oCatModel]) aCats[n][:oCatModelMaterial].setDiffuse( new QColor() {setRGB(255,255,255,255)}) aCats[n][:oCatModelTransform] = new QTransform(aCats[n][:oCatModelEntity]) aCats[n][:oCatModelTransform].setScale(0.01) aCats[n][:oCatModelTransform].setTranslation( new QVector3D(-5*(v+v), 1, 15.2)) oQ = new QQuaternion(0,0,0,0) aCats[n][:oCatModelTransform].setRotation( oQ.fromAxisAndAngle( new QVector3D(0, 1, 1), 180)) aCats[n][:oCatModelEntity].addComponent(aCats[n][:oCatModel]) aCats[n][:oCatModelEntity].addComponent(aCats[n][:oCatModelmaterial]) aCats[n][:oCatModelEntity].addComponent(aCats[n][:oCatModelTransform]) next oSecondLightEntity = new QEntity(oRootEntity) oSecondLight = new QPointLight(oSecondLightEntity) oSecondLight.setColor(new qColor() { setRGB(255,255,255,255) }) oSecondLight.setIntensity(1) oSecondLightTransform = new QTransform(oSecondLightEntity) oSecondLightTransform.setTranslation(new QVector3D(-5, 1, 15.5)) oSecondLightEntity.addComponent(oSecondLight) oSecondLightEntity.addComponent(oSecondLightTransform) oRobotEntity = new QEntity(oRootEntity) oRobotModel = new QMesh(oRobotEntity) oRobotModel.setSource( new qURL("file:///"+CurrentDir()+"/assets/model/Fat_Robot.obj") ) oRobotTransform = new QTransform(oRobotEntity) oRobotTransform.setScale(0.006) robotX = -15 robotY = -2 robotZ = 15 oRobotTransform.setTranslation(new QVector3D(-15, -2, 15)) oQ = new QQuaternion(0,0,0,0) oRobotTransform.setRotation( (continues on next page) 47.18. Scence 572
617.
Ring Documentation, Release1.21.2 (continued from previous page) oQ.fromAxisAndAngle(new QVector3D(0, 1, 1), 170)) oRobotMaterial = new QPhongMaterial(oRobotEntity) oRobotMaterial.setDiffuse(new QColor() {setRGB(128,128,128,255)}) oRobotEntity.addComponent(oRobotModel) oRobotEntity.addComponent(oRobotTransform) oRobotEntity.addComponent(oRobotMaterial) oView.setRootEntity(oRootEntity) oWidget { setwindowtitle("Using Qt3D - Scene") showfullscreen() } oContainer.resize(oWidget.width(),oWidget.height()) oFilter = new QAllEvents(oView) oFilter.setKeyPressEvent("pKeyPress()") oView.installeventfilter(oFilter) oContainer.setfocus(0) exec() } func pKeyPress nKey = oFilter.getKeyCode() nSpeed = 0.1 cX = oCameraEntity.position().x() CY = oCameraEntity.position().y() cZ = oCameraEntity.position().z() cVCx = oCameraEntity.viewCenter().x() cVCy = oCameraEntity.viewCenter().y() cVCz = oCameraEntity.viewCenter().z() switch nKey on Qt_Key_Right if cX < 4.8 robotX+= nSpeed oCameraEntity.setPosition( new QVector3D(cX+0.1, cY, cZ)) oCameraEntity.setViewCenter( new QVector3D(cVCx+nSpeed, cVCy, cVCz)) oRobotTransform.setRotation( oQ.fromAxisAndAngle(new QVector3D(0, 1, 1), 170)) ok on Qt_Key_Left if cX > - 13.8 robotX-= nSpeed (continues on next page) 47.18. Scence 573
618.
Ring Documentation, Release1.21.2 (continued from previous page) oCameraEntity.setPosition( new QVector3D(cX-0.1, cY, cZ)) oCameraEntity.setViewCenter( new QVector3D(cVCx-nSpeed, cVCy, cVCz)) oRobotTransform.setRotation( oQ.fromAxisAndAngle(new QVector3D(0, 1, 1), 160)) ok on Qt_Key_Down if robotY > -3.5 robotY-= nSpeed oCameraEntity.setPosition( new QVector3D(cX, cY, cZ)) oRobotTransform.setRotation( oQ.fromAxisAndAngle(new QVector3D(0, 1, 1), 190)) ok on Qt_Key_Up if robotY < 2 robotY+= nSpeed oCameraEntity.setPosition( new QVector3D(cX, cY, cZ)) oRobotTransform.setRotation( oQ.fromAxisAndAngle(new QVector3D(0, 1, 1), 180)) ok on Qt_Key_Escape oWidget.close() off oRobotTransform.setTranslation(new QVector3D(robotX, robotY, robotZ)) 47.18. Scence 574
619.
CHAPTER FORTYEIGHT OBJECTS LIBRARY FORRINGQT APPLICATION In this chapter we will learn about the objects library and using it in GUI applications. Instead of using global variables for windows objects and connecting events to objects using the object name, the Objects Library will manage a list of the GUI objects and will provide a more natural API to quickly create one or many windows from the same class. Also the Objects Library provide a way to quickly set methods to be executed when an event is fired. Also the library provide a natural interface to quickly use the parent or the caller windows from the child or sub windows and the other way around. The Objects Library is designed to be used with the MVC Design Pattern. The Objects Library uses reflection and meta-programming to add new methods to Controller classes to provide an easy way for communication between these classes. The Objects Library is called automatically by the GUILib Also we can call it alone by using the next command load "objectslib.ring" The ObjectsLib provide functions like openObject(), lastObject() and a class called ObjectsParent The GUILib provide a new specific API for GUI applications like openWindow(), lastWindow(), etc. 48.1 Library Usage with GUI Applications • Use the openWindow(cWindowControllerClassName) function to open new Windows • Create at least Two Classes for each window, The Controller Class and the View Class • Create each controller class from the WindowsControllerParent Class • Create each view class from the WindowsViewParent Class • Use the lastWindow() function to get the object of the last window created (The Controller object). • When you call a sub window, use the SetParentObject() method and pass the self object. • In the View Class, To determine the event method use the Method(cMethodName) function. • The Method(cMethodName) function determine the method in the controller class that will be executed. • Each controller class contains by default the CloseAction() method that you can call to close the window. • You don’t need to call the Show() Method for each window, When you use openWindow() It will be called. • In the view class, Define the GUI window object as an attribute called win. 575
620.
Ring Documentation, Release1.21.2 • You can use openWindowNoShow() to avoid displaying the window. • You can use openWindowAndLink() to quickly get methods to access the windows. 48.2 Example In the next example we will create two types of windows. • Main Window contains a button. When the user click on the button a sub window will be opened. • The User Can click on the button many times to open many sub windows. • Each Sub Window contains Two buttons. • The first button in the sub window change the Main and the Sub Windows Titles. • The second button in the sub window close the Sub Window. load "guilib.ring" new qApp { openWindow( :MainWindowController ) exec() } class MainWindowController from WindowsControllerParent oView = new MainWindowView func SubWindowAction openWindow( :SubWindowController ) lastWindow().SetParentObject(self) class MainWindowView from WindowsViewParent win = new qWidget() { SetWindowTitle("Main Window") btnSub = new qPushButton(win) { setText("Sub Window") setClickEvent( Method( :SubWindowAction ) ) } resize(400,400) } class SubWindowController from WindowsControllerParent oView = new SubWindowView func SetMainWindowTitleAction Parent().oView.win.SetWindowTitle("Message from the Sub Window") oView.win.SetWindowTitle("Click Event Done!") class SubWindowView from WindowsViewParent win = new qWidget() { SetWindowTitle("Sub Window") btnMsg = new qPushButton(win) { setText("Set Main Window Title") setClickEvent( Method( :SetMainWindowTitleAction ) ) } btnClose = new qPushButton(win) { (continues on next page) 48.2. Example 576
621.
Ring Documentation, Release1.21.2 (continued from previous page) Move(200,0) setText("Close") setClickEvent( Method( :CloseAction ) ) } resize(400,400) } The next screen shot after creating three sub windows. The next screen shot after clicking on the button in each sub window. 48.2. Example 577
622.
Ring Documentation, Release1.21.2 48.3 openWindowAndLink() Function We can use the openWindowAndLink() function to connect between the application windows, pass messages (call methods) between the objects. This function uses Meta-programming to define dynamic methods in the Caller Class to use the dynamic objects of other windows that we create. Example : (Uses the Form Designer) First Window (1) https://github.com/ring-lang/ring/blob/master/samples/UsingFormDesigner/twowindowspart5/ firstwindowView.ring (2) https://github.com/ring-lang/ring/blob/master/samples/UsingFormDesigner/twowindowspart5/ firstwindowController.ring Second Window 48.3. openWindowAndLink() Function 578
623.
Ring Documentation, Release1.21.2 (1) https://github.com/ring-lang/ring/blob/master/samples/UsingFormDesigner/twowindowspart5/ secondwindowView.ring (2) https://github.com/ring-lang/ring/blob/master/samples/UsingFormDesigner/twowindowspart5/ secondwindowController.ring In the next code for example (from FirstWindowController.ring) The openWindowAndLink() will create an object from the SecondWindowController Class Then will add the SecondWindow() and IsSecondWindow() Methods to the FirstWindowController Class Also will add the FirstWindow() and IsFirstWindow() Methods to the SecondWindowController Class So the SendMessage() method in FirstWindowController class can use the SecondWindow() method to access the object. This is more simple than using lastWindow(), Parent() and SetParentObject() methods. class firstwindowController from windowsControllerParent oView = new firstwindowView func OpenSecondWindow openWindowAndLink(:SecondWindowController,self) func SendMessage if IsSecondWindow() SecondWindow().setMessage("Message from the first window") ok func setMessage cMessage oView.Label1.setText(cMessage) 48.4 openWindowInPackages() Function The openWindowInPackages() function is the same as openWindow() but takes an extra list that determine the packages to import before opening the window. Syntax: openWindowInPackages(cClassName,aPackagesList) Example: The next example from the Form Designer source code, Open the Window Flags window using the openWindowIn- Packages() function. We determine the class name “WindowFlagsController” and the packages name. The Window Flags window uses the FormDesigner and System.GUI packages. openWindowInPackages(:WindowFlagsController,[ "formdesigner", "System.GUI" ]) 48.4. openWindowInPackages() Function 579
624.
Ring Documentation, Release1.21.2 48.5 Using ObjectID() and GetWindowByID() Each window created using the Objects Library have a unique ID We can get the window ID using the ObjectID() Method in the Controller Class The Objects Library comes with the GetWindowByID() function Using this function we can get the controller object of a window using the ID Example: load "guilib.ring" new qApp { openWindow( :MainWindowController ) exec() } class MainWindowController from WindowsControllerParent oView = new MainWindowView nFirstWindowID nSecondWindowID nThirdWindowID func CreateThreeWindowsAction openWindow( :SubWindowController ) nFirstWindowID = lastWindow().ObjectID() openWindow( :SubWindowController ) nSecondWindowID = lastWindow().ObjectID() openWindow( :SubWindowController ) nThirdWindowID = lastWindow().ObjectID() FirstWindow().oView.win { setWindowTitle("One") move(100,100) } SecondWindow().oView.win { setWindowTitle("Two") move(200,200) } ThirdWindow().oView.win { setWindowTitle("Three") move(300,300) } func FirstWindow return GetWindowByID(nFirstWindowID) func SecondWindow return GetWindowByID(nSecondWindowID) func ThirdWindow return GetWindowByID(nThirdWindowID) (continues on next page) 48.5. Using ObjectID() and GetWindowByID() 580
625.
Ring Documentation, Release1.21.2 (continued from previous page) class MainWindowView from WindowsViewParent win = new qWidget() { setWindowTitle("Main Window") move(500,100) btnSub = new qPushButton(win) { setText("Create Three Windows") setClickEvent( Method( :CreateThreeWindowsAction ) ) } resize(400,400) } class SubWindowController from WindowsControllerParent oView = new SubWindowView class SubWindowView from WindowsViewParent win = new qWidget() { setWindowTitle("Sub Window") btnClose = new qPushButton(win) { move(200,0) setText("Close") setClickEvent( Method( :CloseAction ) ) } resize(400,400) } Screen Shot: 48.5. Using ObjectID() and GetWindowByID() 581
626.
Ring Documentation, Release1.21.2 48.6 Using ObjectID() and OpenWindowAndLink() The next example demonstrates using OpenWindowAndLink() instead of GetWindowByID() This introduce an interesting question: When to use OpenWindowAndLink()? If the parent window will call one object of the sub window then use OpenWindowAndLink() i.e. the relationship between the parent window and the sub window is one-to-one. If the parent window will call many objects of the sub window at different times Then use GetWindowByID() to determine which object to use i.e. the relationship between the parent window and the sub window is one-to-many. Example: load "guilib.ring" new qApp { openWindow( :MainWindowController ) exec() (continues on next page) 48.6. Using ObjectID() and OpenWindowAndLink() 582
627.
Ring Documentation, Release1.21.2 (continued from previous page) } class MainWindowController from WindowsControllerParent oView = new MainWindowView func SubWindowAction openWindowAndLink( :SubWindowController,self) subWindow().oView.win { move(50,100) setStyleSheet("background-color:yellow;") } class MainWindowView from WindowsViewParent win = new qWidget() { setWindowTitle("Main Window") resize(500,400) btnSub = new qPushButton(win) { setText("Sub Window") setClickEvent( Method( :SubWindowAction␣ ˓ →) ) } } class SubWindowController from WindowsControllerParent oView = new SubWindowView func SetMainWindowTitleAction MainWindow().oView.win.SetWindowTitle( "Message from the Sub Window - Window ID = " + ObjectID() ) oView.win.SetWindowTitle("Click Event Done!") class SubWindowView from WindowsViewParent win = new qWidget() { setWindowTitle("Sub Window") resize(300,400) btnMsg = new qPushButton(win) { setText("Set Main Window Title") setClickEvent( Method(␣ ˓ →:SetMainWindowTitleAction ) ) } btnClose = new qPushButton(win) { Move(200,0) setText("Close") setClickEvent( Method( :CloseAction ) ) } } Screen Shot: 48.6. Using ObjectID() and OpenWindowAndLink() 583
628.
Ring Documentation, Release1.21.2 48.7 Objects Library Source Code The library source code is very simple, You can check the source code files The source code for the Objects Library (can be used without GUILib) • https://github.com/ring-lang/ring/blob/master/libraries/objectslib/objects.ring • https://github.com/ring-lang/ring/blob/master/libraries/objectslib/objectslib.ring The source code for the MVC classes in GUILib • https://github.com/ring-lang/ring/blob/master/libraries/guilib/mvc/controllerparent.ring • https://github.com/ring-lang/ring/blob/master/libraries/guilib/mvc/viewparent.ring 48.7. Objects Library Source Code 584
629.
CHAPTER FORTYNINE MULTI-LANGUAGE APPLICATIONS There aremany ways to create multi-language Ring application! In this chapter we will learn about using the String2Constant tool 49.1 Using String2Constant Starting from Ring 1.8 we have the String2Constant application You will find this tool in the ring/tools/string2constant folder Using this tool we can convert the source code to be based on constants instead of string literals Then we can store constants in separate source code files that we can translate to different languages Where we can have special file for each language, like (English.ring, Arabic.ring and so on) Using this simple tool, the Form Designer is translated to Arabic language too just as an example. 585
630.
Ring Documentation, Release1.21.2 49.2 Form Designer Translation You will find the form designer application in the ring/applications/formdesigner folder The files used for translation are stored in the ring/applications/formdesigner/translation folder You will find two files • Arabic.ring • English.ring You can check these files to get an idea about constants definition. The next section from the English.ring file 49.2. Form Designer Translation 586
631.
Ring Documentation, Release1.21.2 T_LANGUAGE = "english" T_LAYOUTDIRECTION = 0 # Left to Right T_FORMDESIGNER_FORMDESIGNER = "Form Designer" T_FORMDESIGNER_FORMTITLE = "Form1" T_FORMDESIGNER_FILE = "File" T_FORMDESIGNER_NEW = "New" T_FORMDESIGNER_OPEN = "Open" T_FORMDESIGNER_SAVE = "Save" T_FORMDESIGNER_SAVEAS = "Save As" T_FORMDESIGNER_CLOSE = "Close" The form designer source code files will use these constants instead of typing the string literals the next section from the formdesigner/mainwindow/formdesignerview.ring # Create the Main Window and use the Mdi Area win = new qMainwindow() { setWindowTitle(T_FORMDESIGNER_FORMDESIGNER) # "Form Designer" setcentralWidget(this.oArea) setLayoutDirection(T_LAYOUTDIRECTION) } • Using comments we can write the string literal to get more readable code. • Using setLayoutDirection() method we can set the window direction to be Right To Left. • Using the Load command, We can determine which translation file to use. 49.3 Forms Translation After creating the form using the Form Designer, the View class will be generated. We don’t modify the view class, We just add the translation through the Controller class. For example, we have the form file : ring/formdesigner/selobjects/selobjects.rform 49.3. Forms Translation 587
632.
Ring Documentation, Release1.21.2 And we add the translation through the Controller class using the next code And we define the constants in English.ring and Arabic.ring class selobjectsController from windowsControllerParent oView = new selobjectsView { ListObjects.setselectionmode(QAbstractItemView_MultiSelection) win.setwindowmodality(2) # Translation win.setWindowTitle(T_FORMDESIGNER_SELOBJECTS_TITLE) win.setLayoutDirection(T_LAYOUTDIRECTION) labelobjects.setText(T_FORMDESIGNER_SELOBJECTS_OBJECTS) btnSelect.setText(T_FORMDESIGNER_SELOBJECTS_SELECT) btnClose.setText(T_FORMDESIGNER_SELOBJECTS_CLOSE) } 49.3. Forms Translation 588
633.
CHAPTER FIFTY BUILDING RINGQT APPLICATIONSFOR MOBILE In this chapter we will learn about Building RingQt Applications for Mobile. 50.1 Download Requirements • Install Qt 5.15.2 • Install Qt Creator (Tested using Qt Creator 6.0.1) 50.2 Install Qt for Android (1) Check the next link : http://doc.qt.io/qt-5/androidgs.html • Run Qt Creator • Select Tools > Options > Devices > Android • Set the path for JDK, Android NDK and Android SDK Tools (2) Using Qt Creator, We can download these requirements • The Android SDK Tools (Tested using version 2.1) • The Android NDK (Tested using version 21.3) • Java SE Development Kit (JDK) v6 or later (3) Update the Android SDK to get the API and tools packages required for development • Tested using Android 7.1 (API 25) Screen Shot: 589
634.
Ring Documentation, Release1.21.2 50.3 Using Ring2EXE We can use Ring2EXE to quickly prepare Qt project for our application Example: ring2exe myapp.ring -dist -mobileqt Note: We can use the Distribute Menu in Ring Notepad Tip: The option ( Prepare Qt project for Mobile devices ) in the Distribute Menu 50.3. Using Ring2EXE 590
635.
Ring Documentation, Release1.21.2 50.4 The Qt project for your Ring application After using Ring2EXE or the Distribute Menu in Ring Notepad • Using the Qt Creator Open the generated Qt project Folder : target/mobile/qtproject Project file : project.pro • Using Qt Creator, You will find the compiled Ring application in the resources (YourAppName.ringo) This file (Ring Object File) is generated by the Ring compiler using ring YourAppName.ring -go -norun • You can build your application using Qt Creator When we open the project file, We can select the Kit After selecting the Kit, Click Configure Project 50.4. The Qt project for your Ring application 591
636.
Ring Documentation, Release1.21.2 Now We can build & Run the application 50.4. The Qt project for your Ring application 592
637.
Ring Documentation, Release1.21.2 The next screen shot for the application during the runtime 50.4. The Qt project for your Ring application 593
638.
Ring Documentation, Release1.21.2 (1) You can add your application images to the resources Or You can use any text editor (Notepad) and modify : project.qrc (2) To find images from your Ring application, You need to use the file name in resources Example if isandroid() mypic = new QPixmap(":/cards.jpg") else mypic = new QPixmap("cards.jpg") ok 50.5 Comments about developing for Android using RingQt (1) The main project file is main.cpp This file load Ring Compiler/Virtual Machine and RingQt Then get the Ring Object File during the runtime from the resources Then run the Ring Object File (ringapp.ringo) using the Ring VM Through main.cpp you can extract more files from the resources to temp. folder once you add them (create projects with many files). (2) The next functions are missing from this Ring edition • Database (ODBC, SQLite & MySQL) • Security and Internet functions (LibCurl & OpenSSL) 50.5. Comments about developing for Android using RingQt 594
639.
Ring Documentation, Release1.21.2 • RingAllegro (Allegro Library) • RingLibSDL (LibSDL Library) Just use Qt Classes through RingQt. For database access use the QSqlDatabase Class Note: All of the missing libraries (LibCurl, OpenSSL & Allegro) can be compiled for Android, but they are not included in this Qt project. (3) use if isandroid() when you want to modify the code just for android Example: if isandroid() // Android code else // other platforms ok (4) Sometimes you will find that the button text/image is repeated in drawing ! it’s Qt problem that you can avoid using the next code. if isandroid() setStyleSheet(" border-style: outset; border-width: 2px; border-radius: 4px; border-color: black; padding: 6px;") ok (5) Always use Layouts instead of manual setting of controls position and size. This is the best way to get the expected user interface to avoid problems like (controls with small/extra size) (6) When you deal with Qt Classes you can determine the images from resources (you don’t need to copy them using main.cpp) Example: if isandroid() mypic = new QPixmap(":/cards.jpg") else mypic = new QPixmap("cards.jpg") ok Now RingQt comes with the AppFile() function to determine the file name Example: mypic = new QPixmap(AppFile("cards.jpg")) # Desktop or Android (7) When you update your project code, You don’t have to use Ring2EXE to generate the Qt project again Just use the Distribute Menu in Ring Notepad and select (Generate Ring Object File) Then copy the YourAppName.ringo file to target/mobile/qtproject folder and accept replacing files. 50.5. Comments about developing for Android using RingQt 595
640.
Ring Documentation, Release1.21.2 (8) If your application folder contains a Qt resource file (project.qrc) Then when you use Ring2EXE or Ring Notepad (Distribute - Prepare Qt project for Mobile devices) the resource file will be used See ring/applications/cards game as an example. 50.5. Comments about developing for Android using RingQt 596
641.
CHAPTER FIFTYONE BUILDING RINGQT APPLICATIONSFOR WEBASSEMBLY In this chapter we will learn about Building RingQt Applications for WebAssembly. 51.1 Download Requirements Check the next link : https://doc.qt.io/qt-5/wasm.html Tested using • Qt (5.15.2) • Qt Creator (11.0.2) • Emscripten (1.39.8) : https://emscripten.org/docs/getting_started/index.html Use Git to have emsdk # Get the emsdk repo git clone https://github.com/emscripten-core/emsdk.git # Enter that directory cd emsdk Use emsdk to install and activate the required version for Qt 5.15 emsdk install 1.39.8 emsdk activate --embedded 1.39.8 Check Emscripten installation em++ --version Output emcc (Emscripten gcc/clang-like replacement) 1.39.8 (commit 24d88487f47629fac9d4acd231497a3a412bdee8) Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt) This is free and open source software under the MIT license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. • Run Qt Creator • Select Tools > Options > Devices > WebAssembly 597
642.
Ring Documentation, Release1.21.2 51.2 Using Ring2EXE We can use Ring2EXE to quickly prepare Qt project for our application Example: ring2exe myapp.ring -dist -webassemblyqt Note: We can use the Distribute Menu in Ring Notepad Tip: The option ( Prepare Qt project for WebAssembly ) in the Distribute Menu 51.2. Using Ring2EXE 598
643.
Ring Documentation, Release1.21.2 51.3 The Qt project for your Ring application After using Ring2EXE or the Distribute Menu in Ring Notepad • Using the Qt Creator Open the generated Qt project Folder : target/webassembly/qtproject Project file : project.pro 51.3. The Qt project for your Ring application 599
644.
Ring Documentation, Release1.21.2 • Using Qt Creator, You will find the compiled Ring application in the resources (YourAppName.ringo) This file (Ring Object File) is generated by the Ring compiler using ring YourAppName.ring -go -norun • You can build your application using Qt Creator 51.3. The Qt project for your Ring application 600
645.
Ring Documentation, Release1.21.2 The next screen shot for the application during the runtime 51.3. The Qt project for your Ring application 601
646.
Ring Documentation, Release1.21.2 (1) You can add your application images to the resources Or You can use any text editor (Notepad) and modify : project.qrc (2) To find images from your Ring application, You need to use the file name in resources Example if isWebAssembly() mypic = new QPixmap(":/cards.jpg") else mypic = new QPixmap("cards.jpg") ok 51.4 Comments about developing for WebAssembly using RingQt (1) The main project file is main.cpp This file load Ring Compiler/Virtual Machine and RingQt Then get the Ring Object File during the runtime from the resources Then run the Ring Object File (ringapp.ringo) using the Ring VM Through main.cpp you can extract more files from the resources to temp. folder once you add them (create projects with many files). (2) use if isWebAssembly() when you want to modify the code just for WebAssembly Example: 51.4. Comments about developing for WebAssembly using RingQt 602
647.
Ring Documentation, Release1.21.2 if isWebAssembly() // WebAssembly code else // other platforms ok (3) When you deal with Qt Classes you can determine the images from resources (you don’t need to copy them using main.cpp) Example: if isWebAssembly() mypic = new QPixmap(":/cards.jpg") else mypic = new QPixmap("cards.jpg") ok Now RingQt comes with the AppFile() function to determine the file name Example: mypic = new QPixmap(AppFile("cards.jpg")) # Desktop, Android or WebAssembly (4) When you update your project code, You don’t have to use Ring2EXE to generate the Qt project again Just use the Distribute Menu in Ring Notepad and select (Generate Ring Object File) Then copy the YourAppName.ringo file to target/webassembly/qtproject folder and accept replacing files. (5) If your application folder contains a Qt resource file (project.qrc) Then when you use Ring2EXE or Ring Notepad (Distribute - Prepare Qt project for WebAssembly) the resource file will be used See ring/applications/cards game as an example. (6) Use stdlibcore.ring instead of stdlib.ring when using StdLib functions (7) Use ClocksPerSecond() function instead of typing the value (1000) (8) Nested events loops are not supported, use events for dialogs instead of calling the exec() method (9) Using Sleep() or ProcessEvents() doesn’t provide the expected results, use Qt Timers. (10) We don’t have a direct access to the File System because the applications are executed in a secure environment Tip: We can use special functions for Uploading/Downloading files (See FileContent sample) 51.4. Comments about developing for WebAssembly using RingQt 603
648.
Ring Documentation, Release1.21.2 51.5 Dialogs See the folder: ring/samples/UsingQtWASM Folders: • ColorDialog • FontDialog • FileDialog • FileContent 51.6 Online Applications • Hello World : https://ring-lang.github.io/web/helloworld/project.html • Matching Game : https://ring-lang.github.io/web/matching/project.html • Pairs Game : https://ring-lang.github.io/web/pairs/project.html • Othello Game : https://ring-lang.github.io/web/othello/project.html • Game of Life : https://ring-lang.github.io/web/gameoflife/project.html • Form Designer : https://ring-lang.github.io/web/formdesigner/project.html 51.5. Dialogs 604
649.
CHAPTER FIFTYTWO WEB DEVELOPMENT (CGILIBRARY) In this chapter we will learn about developing Web applications using a CGI Library written in the Ring language. 52.1 Configure the Apache web server In this section we will learn about using Ring with the Apache HTTP Server. We can also use Ring with any web server that supports CGI. Note: If you are developing your application using Ring Notepad then there’s no need to configure Apache manually. 52.1.1 Using XAMPP Apache web server Here we will use the XAMPP application that has Apache web server already been included. You can download XAMPP package from this link XAMPP : https://www.apachefriends.org/download.html Install then open the configuration file from the XAMPP application or search for it in the following locations based on your operating system. For Windows: xamppapacheconfhttpd.conf For Linux: /opt/lampp/etc/httpd.conf For macOS: /Applications/XAMPP/xamppfiles/etc/httpd.conf Search for the next line and make sure that it’s not commented LoadModule cgi_module modules/mod_cgi.so Search for : AddHandler cgi-script Then add “.ring” to the supported cgi extensions Example 605
650.
Ring Documentation, Release1.21.2 AddHandler cgi-script .cgi .ring Example AddHandler cgi-script .cgi .pl .asp .ring Restart/Start the server using XAMPP “Manage Servers” or “Services” section Create your web applications in a directory supported by the web server. For Windows: xampphtdocsmywebapplicationfolder For Linux: /opt/lampp/htdocs/mywebapplicationfolder For macOS: Open XAMPP application then go to "Volumes" section Mount the "/opt/lampp" volume Open Finder then you will find it mounted as an IP address under "Locations" on the left␣ ˓ →menu 52.2 Setting the shebang line For Ring files to run properly under CGI we need to set the shebang line to locate the executable file of Ring. So, Inside the source code file (*.ring), Add next line as the very first line in the file: For Windows: #!ring -cgi For Linux: #!/usr/bin/ring -cgi For macOS: #!/usr/local/bin/ring -cgi Note: Change the previous line based on the path to ring executable in your machine 52.2. Setting the shebang line 606
651.
Ring Documentation, Release1.21.2 52.3 Grant Ring files execution permission In Linux and Mac operating systems ring files cannot be run properly using CGI until we grant them executable per- mission. You can do that using the properties section of your preferred File manager or you can do it using Terminal as follow: sudo chmod +x ringAppFile.ring Note: If you are developing your application using Ring Notepad then this step will be done automatically. 52.4 Ring CGI Hello World Program The next program is the Hello World program For Windows: #!ring -cgi See "content-type: text/html" +nl+nl+ "Hello World!" + nl For Linux: #!/usr/bin/ring -cgi See "content-type: text/html" +nl+nl+ "Hello World!" + nl For macOS: #!/usr/local/bin/ring -cgi See "content-type: text/html" +nl+nl+ "Hello World!" + nl 52.5 Hello World Program using the Web Library We can use the web library to write CGI Web applications quickly Example (1) : #!ring -cgi Load "weblib.ring" Import System.Web New Page { (continues on next page) 52.3. Grant Ring files execution permission 607
652.
Ring Documentation, Release1.21.2 (continued from previous page) Text("Hello World!") } Example (2) : #!ring -cgi Load "weblib.ring" Import System.Web WebPage() { Text("Hello World!") } Tip: the difference between ex. 1 and ex. 2 is using WebPage() function to return the page object instead of creating the object using new statement. 52.6 Web Library Features The next features are provided by the Web library to quickly create web applications. • Generate HTML pages using functions • Generate HTML pages using objects • HTTP Get • HTTP Post • Files Upload • URL Encode • Templates • CRUD MVC Sample • Users Logic & Registration Sample 52.7 HTTP Get Example The Page User Interface #!ring -cgi Load "weblib.ring" Import System.Web New Page { Title = "Test HTTP Get" (continues on next page) 52.6. Web Library Features 608
Ring Documentation, Release1.21.2 The Response #!ring -cgi Load "weblib.ring" Import System.Web New Page { divstart([ :style = styledivcenter("800px","500px") ]) boxstart() text ( "HTTP GET Response" ) newline() (continues on next page) 52.7. HTTP Get Example 611
656.
Ring Documentation, Release1.21.2 (continued from previous page) boxend() divstart([ :style = stylefloatleft()+stylewidth("10%")+ stylecolor("black")+stylegradient(58) ]) newline() text ( "Name : " ) newline() newline() text ( "Address : " ) newline() newline() text ( "Phone : " ) newline() newline() text ( "Age : " ) newline() newline() text ( "City : " ) newline() newline() text ( "Country : " ) newline() newline() text ( "Note : " ) newline() newline() divend() divstart([ :style = stylefloatleft()+stylewidth("90%")+ stylecolor("black")+stylegradient(47) ]) divstart([ :style = stylefloatleft() + stylewidth("1%") ]) newline() divend() divstart([ :style = stylefloatleft() + stylewidth("95%") ]) newline() text ( aPageVars["Name"] ) newline() newline() text ( aPageVars["Address"] ) newline() newline() text ( aPageVars["Phone"] ) newline() newline() text ( aPageVars["Age"] ) newline() newline() text ( aPageVars["City"] ) newline() newline() text (aPageVars["Country"] ) newline() newline() text ( aPageVars["Notes"] ) newline() newline() divend() divend() divend() } Screen Shot: 52.7. HTTP Get Example 612
Ring Documentation, Release1.21.2 52.12 Templates Using Templates we can write Ring code inside HTML files Syntax: <%= Ring Expression %> <% Ring Statements %> The HTML Code <h1>Listing Numbers</h1> <table> <tr> <th> <%= myheader.cColumn1 %> </th> <th> <%= myheader.cColumn2 %> </th> <th></th> <th></th> <th></th> </tr> <% for x in aNumbers %> <tr> <td> <%= x.nValue %> </td> <td> <%= x.nSquare %> </td> </tr> <% next %> </table> The Ring Code 52.12. Templates 621
666.
Ring Documentation, Release1.21.2 #!ring -cgi Load "weblib.ring" Import System.Web New NumbersController { start() } Class NumbersController MyHeader aNumbers Func Start MyHeader = New Header { cColumn1 = "Number" cColumn2 = "Square" } aNumbers = list(20) for x = 1 to len(aNumbers) aNumbers[x] = new number { nValue = x nSquare = x*x } next cTemp = Template("mynumbers.html",self) New Page { boxstart() text( "Test Templates" ) newline() boxend() html(cTemp) } Class Header cColumn1 cColumn2 Class Number nValue nSquare Screen Shot: 52.12. Templates 622
Ring Documentation, Release1.21.2 52.13 HTML Special Characters The text() function display HTML special characters. If you want to write html code, use the html() function. #!ring -cgi Load "weblib.ring" Import System.Web New Page { boxstart() text("HTML Special Characters") newline() boxend() text(' <html> <body> <p> "hello world" </p> </body> </html> ') } Screen Shot: 52.14 Hash Functions The Page User Interface #!ring -cgi Load "weblib.ring" Import System.Web New Page { boxstart() text( "Hash Test") newline() (continues on next page) 52.13. HTML Special Characters 624
Ring Documentation, Release1.21.2 (continued from previous page) else text("No images!") newline() ok divend() } Screen Shot: 52.16 HTML Lists The next example print a list contains numbers from 1 to 10 Then print a list from Ring List. Finally we have a list of buttons and when we press on a button we get a message contains the clicked button number. To start the list we uses the ulstart() function. To end the list we uses the ulend() function. We uses listart() and liend() to determine the list item. #!ring -cgi Load "weblib.ring" Import System.Web Func Main New Page { (continues on next page) 52.16. HTML Lists 627
672.
Ring Documentation, Release1.21.2 (continued from previous page) ulstart([]) for x = 1 to 10 listart([]) text(x) liend() next ulend() list2ul(["one","two","three","four","five"]) ulstart([]) for x = 1 to 10 listart([]) cFuncName = "btn"+x+"()" button([ :onclick = cFuncName , :value = x]) script(scriptfuncalert(cFuncName,string(x))) liend() next ulend() } Screen Shot: 52.16. HTML Lists 628
Ring Documentation, Release1.21.2 52.17 HTML Tables In this example we will learn how to generate HTML tables using the tablestart(), tableend(), rowstart(), rowend() ,headerstart(), headerend(), cellstart() and cellend() functions. #!ring -cgi Load "weblib.ring" Import System.Web Func Main New Page { divstart([ :style = styledivcenter("400px","500px") ] ) style(styletable() + styletablerows("t01")) tablestart([ :id = :t01 , :style = stylewidth("100%") ]) rowstart([]) headerstart([]) text("Number") headerend() headerstart([]) text("square") headerend() rowend() for x = 1 to 10 rowstart([]) cellstart([]) text(x) cellend() cellstart([]) text(x*x) cellend() rowend() next tableend() divend() } Screen Shot: 52.17. HTML Tables 630
675.
Ring Documentation, Release1.21.2 52.18 Gradient In this example we will learn how to use the StyleGradient() function. The function takes the style number as input (range from 1 to 60). #!ring -cgi Load "weblib.ring" Import System.Web Func Main New Page { boxstart() text("StyleGradient() Function") boxend() for x = 1 to 60 divstart([ :id = x , :align = "center" , :style = stylefloatleft() + stylesize(string(100/60*6)+"%","50px") + stylegradient(x) ]) h3(x) divend() next } Screen Shot: 52.18. Gradient 631
676.
Ring Documentation, Release1.21.2 52.19 Generating Pages using Objects Instead of using functions/methods to generate HTML pages, we can use an object for each element in the page. This choice means more beautiful code but slower. The fastest method is to print HTML code directly, then using functions then using templates then using objects (slower). #!ring -cgi Load "weblib.ring" Import System.Web Func Main WebPage() { Title = "Using objects to create the Web Page content" h1 { text("welcome") } link { Title = "Google" Link = "http://www.google.com" } div { id = "div1" style = stylegradient(30) + stylesize("50%","50%") text("Outer Div") div { id = "div2" color = "white" backgroundcolor = "green" width = "50%" height = "50%" marginleft = "5%" margintop = "5%" text("Inner Div") } } div { id = "div3" color = "black" backgroundcolor = "silver" width = "100%" height = "100%" text("Form") form { method = "POST" Action = "helloworld.ring" Table { (continues on next page) 52.19. Generating Pages using Objects 632
Ring Documentation, Release1.21.2 52.20 HtmlPage Class Using this class we can create HTML documents without printing the output to the standard output So instead of using the WebLib in Web Applications only We can use it in Console/GUI/Mobile Applications too Example: load "stdlib.ring" load "weblib.ring" import System.Web (continues on next page) 52.20. HtmlPage Class 636
Ring Documentation, Release1.21.2 52.21 Using Bootstrap Library using Functions The next example uses the Bootstrap JavaScript Library when generating the HTML page. #!ring -cgi Load "weblib.ring" Import System.Web Func Main new BootstrapPage { divstart([ :class = "container" ]) divstart([ :class = "jumbotron" ]) h1("Bootstrap Page") divend() divstart([ :class = :row ]) divstart([ :class = "col-sm-4" ]) h3("Welcome to the Ring programming language") p([ :text = "Using a scripting language is very fun!" ]) divend() divstart([ :class = "col-sm-4" ]) h3("Welcome to the Ring programming language") p([ :text = "using a scripting language is very fun!" ]) divend() divstart([ :class = "col-sm-4" ]) h3("Welcome to the Ring programming language") p([ :text = "using a scripting language is very fun!" ]) divend() divend() divend() } Screen Shot: 52.21. Using Bootstrap Library using Functions 638
683.
Ring Documentation, Release1.21.2 52.22 Using Bootstrap Library using Objects The next example uses the Bootstrap JavaScript Library when generating the HTML page. Instead of using functions to generate the HTML elements, we will use objects. #!ring -cgi Load "weblib.ring" Import System.Web Func Main BootStrapWebPage() { div { classname = :container div { classname = :jumbotron H1 { text("Bootstrap Page") } } div { classname = :row for x = 1 to 3 div (continues on next page) 52.22. Using Bootstrap Library using Objects 639
Ring Documentation, Release1.21.2 (continued from previous page) div { classname = "col-sm-4" div { classname = "btn-group btn-group-lg" button { classname="btn btn-primary" text("one") } button { classname="btn btn-primary" text("two") } button { classname="btn btn-primary" text("three") } } } div { classname = "col-sm-4" div { classname = "btn-group-vertical btn-group-lg" button { classname="btn btn-primary" text("one") } button { classname="btn btn-primary" text("two") } button { classname="btn btn-primary" text("three") } } } } div { classname="modal fade" id="myModal" role="dialog" div { classname = "modal-dialog modal-lg" div { classname="modal-content" div { classname="modal-header" button { classname="close" datadismiss="modal" html("×") } h4 { classname="modal-title" text("Modal Header") } } div { classname = "modal-body" p { text("This is a large model.") } } div { classname="modal-footer" button { classname = "btn btn-default" datadismiss="modal" text("close") } } } } } } } Screen Shot: 52.22. Using Bootstrap Library using Objects 641
686.
Ring Documentation, Release1.21.2 52.23 CRUD Example using MVC The next example uses the weblib.ring & datalib.ring. The datalib.ring contains classes for creating database applications using MVC pattern. In this example we create an object from the SalaryController class then call the Routing method. We define the website variable to contains the basic url of the page. When we create the SalaryModel class from the ModelBase class, the salary table will be opened and the columns data will be defined as attributes in the model class. The SalaryView class create an object from the SalaryLanguageEnglish class to be used for translation. The method AddFuncScript is used to call the form for adding/modifying record data. The method FormViewContent is used to determine the controls in the form when we add or modify a record. #!ring -cgi Load "weblib.ring" Load "datalib.ring" Import System.Web website = "ex24.ring" New SalaryController { Routing() } Class SalaryModel from ModelBase (continues on next page) 52.23. CRUD Example using MVC 642
687.
Ring Documentation, Release1.21.2 (continued from previous page) Class SalaryController From ControllerBase Class SalaryView From ViewBase oLanguage = new SalaryLanguageEnglish Func AddFuncScript oPage,oController return oPage.scriptfuncajax("myadd",oController.cMainURL+ oController.cOperation+"=add","mysubpage") Func FormViewContent oController,oTranslation,oPage return [ [ oTranslation.aColumnsTitles[2], "textbox", "name", oController.oModel.Name, oPage.stylewidth("100%") ], [ oTranslation.aColumnsTitles[3], "textbox", "salary", oController.oModel.Salary, oPage.stylewidth("50%") ] ] Class SalaryLanguageEnglish cTitle = "Salary Table" cBack = "back" aColumnsTitles = ["ID","Name","Salary"] cOptions = "Options" cSearch = "Search" comboitems = ["Select Option...","Edit","Delete"] cAddRecord = "Add Record" cEditRecord = "Edit Record" cRecordDeleted = "Record Deleted!" aMovePages = ["First","Prev","Next","Last"] cPage = "Page" cOf = "of" cRecordsCount = "Records Count" cSave = "Save" temp = new page cTextAlign = temp.StyleTextRight() cNoRecords = "No records!" Screen Shot: 52.23. CRUD Example using MVC 643
Ring Documentation, Release1.21.2 The Registration response #!ring -cgi Load "weblib.ring" Load "datalib.ring" Load "ex25_users.ring" Import System.Web oUser = new UsersModel oUser.Connect() if oUser.findwith("username",aPageVars["username"]) new page { text("The user name is already registered") } return ok if oUser.findwith("email",aPageVars["email"]) new page { text("This email is already registered") } return ok aPageVars["salt"] = str2hex(RandBytes(32)) aPageVars["pwhash"] = sha256(aPagevars["password"]+aPageVars["salt"]) aPageVars["sessionid"] = str2hex(randbytes(32)) oUser.Insert() (continues on next page) 52.24. Users registration and Login 648
Ring Documentation, Release1.21.2 (continued from previous page) cellend() rowend() tableend() formend() divend() divend() } Screen Shot: The response page #!ring -cgi Load "weblib.ring" Load "datalib.ring" Load "ex25_users.ring" Import System.Web oUser = new UsersModel oUser.Connect() lResult = oUser.FindWith("username",aPageVars["username"]) new page { if lResult if sha256(aPagevars["password"]+oUser.Salt) = oUser.pwhash text ("Correct Password!") aPageVars["sessionid"] = str2hex(randbytes(32)) (continues on next page) 52.24. Users registration and Login 650
695.
Ring Documentation, Release1.21.2 (continued from previous page) oUser.UpdateColumn("sessionid",aPageVars["sessionid"]) cookie("sessionid",aPageVars["sessionid"]) else text ("Bad password!") ok else text("Bad User Name!") ok } oUser.Disconnect() The next code for checking if the user needs to login or not #!ring -cgi Load "weblib.ring" Load "datalib.ring" Load "ex25_users.ring" Import System.Web oUser = new UsersModel oUser.Connect() lResult = oUser.FindWith("sessionid",aPageVars["sessionid"]) new page { if lResult text("User Name : " + oUser.username ) else text("Please Login First!") ok } oUser.Disconnect() 52.25 Database, ModelBase & ControllerBase classes In this section we will see some code from datalib.ring The next code presents the Database, ModelBase & ControllerBase classes Import System.Web Class Database cServer = "localhost" cUserName = "root" cPassword = "root" cDatabase = "mahdb" Func Connect con = mysql_init() mysql_connect(con, cServer, cUserName, cPassWord,cDatabase) (continues on next page) 52.25. Database, ModelBase & ControllerBase classes 651
696.
Ring Documentation, Release1.21.2 (continued from previous page) Func Disconnect mysql_close(con) Func Query cQuery mysql_query(con,cQuery) Func QueryResult return mysql_result(con) Func QueryResultWithColumns # return columns names + query result return mysql_result2(con) Func QueryValue aResult = mysql_result(con) if islist(aResult) and len(aResult) >= 1 aResult = aResult[1] if len(aResult) >= 1 return aResult[1] ok ok return 0 Func EscapeString x if isstring(x) return MySQL_Escape_String(con,x) else return MySQL_Escape_String(con,string(x)) ok Private con = NULL Class ModelBase from Database cTableName = "" cSearchColumn = "name" aColumns = [] aQueryResult = [] ID = 0 # set table name from class name classname = lower(classname(self)) if right(classname,5) = :model cTablename = left(classname,len(classname)-5) ok Func Insert (continues on next page) 52.25. Database, ModelBase & ControllerBase classes 652
697.
Ring Documentation, Release1.21.2 (continued from previous page) cValues = "" for x in aColumns cValues += "'" + EscapeString(aPageVars[x]) + "'," Next cValues = left(cValues,len(cValues)-1) # remove last comma cColumns = "" for x in aColumns cColumns += x + "," next cColumns = left(cColumns,len(cColumns)-1) query("insert into " + cTableName + "("+cColumns+") values (" + cValues + ")" ) Func Update nID cStr = "" for x in aColumns cStr += x + " = '" + EscapeString(aPageVars[x]) + "' , " # the space after comma is necessary Next cStr = left(cStr,len(cStr)-2) query("update " + cTableName + " set " + cStr + " where id = " + nID ) Func UpdateColumn cColumn,cValue query("update " + cTableName + " set " + cColumn + " = '" + EscapeString(cValue) + "' where id = " + self.ID ) Func Count cValue query("SELECT count(*) FROM " + cTableName + " where "+cSearchColumn+" like '" + EscapeString(cValue) + "%'") return queryValue() Func Read nStart,nRecordsPerPage query("SELECT * FROM "+ cTableName+" limit " + EscapeString(nStart) + "," + EscapeString(nRecordsPerPage) ) aQueryResult = queryResult() Func Search cValue,nStart,nRecordsPerPage query("SELECT * FROM "+ cTableName+" where "+cSearchColumn+" like '" + EscapeString(cValue) + "%'" + " limit " + EscapeString(nStart) + "," + EscapeString(nRecordsPerPage) ) aQueryResult = queryResult() Func Find nID query("select * from " + cTableName + " where id = " + EscapeString(nID) ) aResult = queryResult()[1] (continues on next page) 52.25. Database, ModelBase & ControllerBase classes 653
698.
Ring Documentation, Release1.21.2 (continued from previous page) # move the result from the array to the object attributes ID = nID cCode = "" for x = 2 to len(aResult) cCode += aColumns[x-1] + " = hex2str('" + str2hex(aResult[x]) + "')" + nl next eval(cCode) Func FindWith cColumn,cValue query("select * from " + cTableName + " where "+cColumn+" = '" + EscapeString(cValue) + "'" ) aResult = queryResult() if len(aResult) > 0 aResult = aResult[1] else return 0 ok # move the result from the array to the object attributes ID = aResult[1] cCode = "" for x = 2 to len(aResult) cCode += aColumns[x-1] + " = hex2str('" + str2hex(aResult[x]) + "')" + nl next eval(cCode) return 1 Func Delete ID query("delete from " + cTableName + " where id = " + EscapeString(ID) ) Func Clear cCode = "" for x in aColumns cCode += x + ' = ""' + nl next eval(cCode) Func LoadModel # create the columns array query("SELECT * FROM "+ cTableName + " limit 0,1") aQueryResult = QueryResultWithColumns()[1] for x = 2 to len(aQueryResult) aColumns + lower(trim(aQueryResult[x])) next # create attribute for each column for x in aColumns addattribute(self,x) next (continues on next page) 52.25. Database, ModelBase & ControllerBase classes 654
699.
Ring Documentation, Release1.21.2 (continued from previous page) Func Connect Super.Connect() if nLoadModel = 0 nLoadModel = 1 LoadModel() ok private nLoadModel = 0 Class ControllerBase nRecordsPerPage = 5 nRecordsCount = 0 nPagesCount = 0 nActivePage = 0 # Dynamic creation of oView = new tablenameView and oModel = new tablename.Model classname = lower(classname(self)) if right(classname,10) = :controller tablename = left(classname,len(classname)-10) cCode = "oView = new " + tablename+"View" + nl cCode += "oModel = new " + tablename+"Model" + nl eval(cCode) oModel.connect() ok cSearchName = "searchname" cPart = "part" cPageError = "The page number is not correct" cLast = "last" cOperation = "operation" cRecID = "recid" aColumnsNames = ["id"] for t in oModel.aColumns aColumnsNames + t next cMainURL = website + "?" func Routing switch aPageVars[cOperation] on NULL showtable() on :add addrecord() on :save saverecord() on :delete deleterecord() (continues on next page) 52.25. Database, ModelBase & ControllerBase classes 655
700.
Ring Documentation, Release1.21.2 (continued from previous page) on :edit editrecord() on :update updaterecord() off func ShowTable nRecordsCount = oModel.Count( aPageVars[cSearchName] ) nPagesCount = ceil(nRecordsCount / nRecordsPerPage) if aPageVars[cPart] = cLast aPageVars[cPart] = string(nPagesCount) ok nActivePage = number(aPageVars[cPart]) if nActivePage = 0 nActivePage = 1 ok if ( nActivePage > nPagesCount ) and nRecordsCount > 0 ErrorMsg(cPageError) return ok nStart = (nActivePage-1)*nRecordsPerPage if aPageVars[cSearchName] = NULL oModel.Read( nStart,nRecordsPerPage ) else oModel.Search( aPageVars[cSearchName],nStart,nRecordsPerPage ) ok oView.GridView(self) func AddRecord oModel.clear() oView.FormViewAdd(Self,:save,false) # false mean don't include record id func SaveRecord oModel.Insert() oView.SaveView(self) func EditRecord oModel.Find( aPageVars[cRecID] ) oView.FormViewEdit(Self,:update,true) # true mean include record id func UpdateRecord oModel.update( aPageVars[cRecID] ) oView.UpdateView(self) (continues on next page) 52.25. Database, ModelBase & ControllerBase classes 656
701.
Ring Documentation, Release1.21.2 (continued from previous page) func DeleteRecord oModel.Delete( aPageVars[cRecID] ) oView.DeleteView() func braceend oModel.Disconnect() 52.26 WebLib API In this section we will see the web library functions, classes and methods. Function Parameters Description LoadVars None Save the request parameters and cookies to aPageVars List WebPage None Create new object from the WebPage Class BootStrapWebPage None Create new object from the BootStrapWebPage Class HTMLSpecialChars cString Encode Special characters to HTML equivalent Template cFile,oObject Execute Ring Code in cFile after accessing oObject using {} Alert cMessage Generate HTML Web Page that display cMessage using JavaScript Alert() HTML2PDF cString Generate and Display PDF File from HTML String (cString) The Package System.Web contains the next classes Class Name Description Application Contains methods for Encoding, Decoding, Cookies & More. Page Contains methods to generate HTML pages. ScriptFunctions Contains methods to generate some JavaScript Functions. StyleFunctions Contains methods to generate CSS. PageBuffer Generate HTML Page in memory (don’t print the output). HTML2PDF Generate PDF File from HTML code. BootStrapPage Using BootStrap Library. WebPage Generate page using objects for each element. HtmlPage Like WebPage but doesn’t print the output to stdout. BootStrapWebPage Generate page using objects, using BootStrap Library. ObjsBase Parent Class for page objects. NewObjectsFunctions Methods to create new objects in the page or element. H1 Wraps HTML H1. H2 Wraps HTML H2. H3 Wraps HTML H3. H4 Wraps HTML H4. H5 Wraps HTML H5. H6 Wraps HTML H6. P Wraps HTML P. Link Wraps HTML link. NewLine Wraps HTML NewLine. Div Wraps HTML Div. continues on next page 52.26. WebLib API 657
702.
Ring Documentation, Release1.21.2 Table 1 – continued from previous page Class Name Description Form Wraps HTML Form. Input Wraps HTML Input. TextArea Wraps HTML TextArea. Select Wraps HTML Select. Option Wraps HTML Option. Image Wraps HTML Image. UL Wraps HTML UL. LI Wraps HTML LI. Table Wraps HTML Table. TR Wraps HTML TR. TD Wraps HTML TD. TH Wraps HTML TH. Audio Wraps HTML Audio. Video Wraps HTML Video. Nav Wraps HTML Nav. Span Wraps HTML Span. Button Wraps HTML Button. 52.27 Application Class Method Parameters Description DecodeString cString Decode request parameters Decode cString Decode multipart/form-data GetFileName aArray,cVar Get File Name in aArray using cVar SetCookie name,value,expires,path,domain,secure Set Cookie Cookie name,value Set Cookie using name and value only GetCookies None Get Cookies URLEncode cString URL Encode ScriptLibs None Add JavaScript Libraries like BootStrap Print None Print Page Content Style cStyle Add cStyle to page CSS content StartHTML None Add HTTP Header to page content Redirect cLocation Will redirect the webpage NoJavaScript None Avoid JavaScript links The method DecodeString is used to get HTTP request parameters. The methods Decode and GetFileName are used for uploading files. The methods SetCookie, Cookie & GetCookies are used for adding and reading cookies. The methods StartHTML, ScriptsLibs, Style & Print are used for page structure and JS/CSS support. The method URLEncode is used to encode a URL to be used in HTML pages. 52.27. Application Class 658
703.
Ring Documentation, Release1.21.2 52.28 Page Class Method Parameters Description text x add HTMLSpecialChars(x) to page content (accept strings and numbers) html cString add html code to page content h1 x add x to page content between <h1> and </h1> h2 x add x to page content between <h2> and </h2> h3 x add x to page content between <h3> and </h3> h4 x add x to page content between <h4> and </h4> h5 x add x to page content between <h5> and </h5> h6 x add x to page content between <h6> and </h6> p aPara HTML <p> </p>, uses aPara List as Hash to get attributes NewLine None add <br /> to page content AddAttributes aPara Convert aPara list as hash to HTML element attributes Link aPara HTML <a href> and </a>, uses aPara List as Hash to get attributes Image aPara HTML <img>, uses aPara List as Hash to get attributes Button aPara HTML <input type=”button”>, uses aPara List as Hash to get attributes ButtonLink aPara HTML <input type=”button”>, uses link attribute to navigate to link Textbox aPara HTML <input type=”text”>, uses aPara List as Hash to get attributes Editbox aPara HTML <textarea> and </textarea>, uses aPara to get attributes Combobox aPara HTML <select>, uses items attribute as list for <option> Listbox aPara HTML <select multiple=’multiple’>, uses items attribute for <option> ulstart aPara HTML <ul> ulend aPara HTML </ul> listart aPara HTML <li> liend aPara HTML </li> List2UL aList Generate HTML <ul> including items from Ring List items DivStart aPara HTML <div>, uses aPara List as Hash to get attributes NavStart aPara HTML <nav>, uses aPara List as Hash to get attributes SpanStart aPara HTML <span>, uses aPara List as Hash to get attributes BoxStart None Generate Div with black background to be used as page header DivEnd None HTML </div> NavEnd None HTML </nav> SpanEnd None HTML </span> BoxEnd None HTML </div>, the same as divend() FormStart cAction HTML <form>, with cAction as the action attribute or an empty value FormPost cAction HTML <form method=”post”> , with cAction as the action attribute FormEnd None HTML </form> Submit aPara HTML <input type=”submit”> Hidden cName,cValue HTML <input type=”hidden”> FormUpload x HTML Form, method=”post” enctype=”multipart/form-data” and x = action UploadFile x HTML <input type=”file”> and name = x Video aPara HTML <video> Audio aPara HTML <audio> GetColor aPara Select Color Radio aPara HTML <input type=”radio”> Checkbox aPara HTML <input type=”checkbox”> Spinner aPara HTML <input type=”number”> Slider aPara HTML <input type=”range”> TableStart aPara HTML <table> TableEnd None HTML </table> continues on next page 52.28. Page Class 659
704.
Ring Documentation, Release1.21.2 Table 2 – continued from previous page Method Parameters Description RowStart aPara HTML <tr> RowEnd None HTML </tr> CellStart aPara HTML <td> CellEnd None HTML </td> HeaderStart aPara HTML <th> HeaderEnd None HTML </th> theadStart aPara HTML <thead> theadEnd None HTML </thead> tbodyStart aPara HTML <tbody> tbodyEnd None HTML </tbody> tfootStart aPara HTML <tfoot> tfootEnd None HTML </tfoot> aPara in the page methods is a list contains attributes and values. Using aPara we can set values for the next attributes classname id name align style dir value onclick oncontextmenu ondblclick onmousedown onmouseenter onmouseleave onmousemove onmouseover onmouseout onmouseup onkeydown onkeypress onkeyup onabort onbeforeunload onerror onhashchange onload onpageshow onpagehide onresize onscroll onunload onblur onchange onfocus onfocusin onfocusout oninput oninvalid onreset onsearch onselect onsubmit ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop oncopy oncut onpaste onafterprint onbeforeprint oncanplay oncanplaythrough ondurationchange onemptied onended onloadeddata onloadedmetadata onloadstart onpause onplay onplaying onprogress onratechange onseeked onseeking onstalled onsuspend ontimeupdate onvolumechange onwaiting animationend animationiteration animationstart transitionend onmessage onopen onmousewheel ononline onoffline onpostate onshow onstorage ontoggle onwheel ontouchcancel ontouchend ontouchmove ontouchstart color opacity background backgroundattachment backgroundcolor backgroundimage backgroundposition backgroundrepeat backgroundclip backgroundorigin backgroundsize border borderbottom borderbottomcolor borderbottomleftradius borderbottomrightradius borderbottomstyle borderbottomwidth bordercolor borderimage borderimageoutset borderimagerepeat borderimageslice borderimagesource borderimagewidth borderleft borderleftcolor borderleftstyle borderleftwidth borderradius borderright borderrightcolor borderrightstyle borderrightwidth borderstyle bordertop bordertopcolor bordertopleftradius bordertoprightradius bordertopstyle bordertopwidth borderwidth boxdecorationbreak boxshadow bottom clear clip display float height left margin marginbottom marginleft marginright margintop maxheight maxwidth minheight minwidth overflow overflowx overflowy padding paddingbottom paddingleft paddingright paddingtop position right top visibility width verticalalign zindex aligncontent alignitems alignself flex flexbasis flexdirection flexflow flexgrow flexshrink flexwrap justifycontent order hangingpunctuation hyphens letterspacing linebreak lineheight overflowwrap tabsize textalign textalignlast textcombineupright textindent textjustify texttransform whitespace wordbreak wordspacing wordwrap textdecoration textdecorationcolor textdecorationline textdecorationstyle textshadow textunderlineposition @fontface @fontfeaturevalues font fontfamily fontfeaturesettings fontkerning fontlanguageoverride fontsize fontsizeadjust fontstretch fontstyle fontsynthesis fontvariant fontvariantalternates fontvariantcaps fontvarianteastasian fontvariantligatures fontvariantnumeric fontvariantposition fontweight direction (continues on next page) 52.28. Page Class 660
705.
Ring Documentation, Release1.21.2 (continued from previous page) textorientation unicodebidi writingmode bordercollapse borderspacing captionside emptycells tablelayout counterincrement counterreset liststyle liststyleimage liststyleposition liststyletype @keyframes animation animationdelay animationdirection animationduration animationfillmode animationiterationcount animationname animationplaystate animationtimingfunction backfacevisibility perspective perspectiveorigin transform transformorigin transformstyle transition transitionproperty transitionduration transitiontimingfunction transitiondelay boxsizing content cursor imemode navdown navindex navleft navright navup outline outlinecolor outlineoffset outlinestyle outlinewidth resize textoverflow breakafter breakbefore breakinside columncount columnfill columngap columnrule columnrulecolor columnrulestyle columnrulewidth columnspan columnwidth columns widows orphans pagebreakafter pagebreakbefore pagebreakinside marks quotes filter imageorientation imagerendering imageresolution objectfit objectposition mask masktype mark markafter markbefore phonemes rest restafter restbefore voicebalance voiceduration voicepitch voicepitchrange voicerate voicestress voicevolume marqueedirection marqueeplaycount marqueespeed marqueestyle datatoggle dataride datatarget dataslideto dataslide datadismiss dataplacement datacontent datatrigger dataspy dataoffset dataoffsettop 52.29 ScriptFunctions Class This class contains methods for adding JavaScript code to the generated web page. The class methods are merged to the Page class, so we can use the next methods with page objects directly. Method Parameters Description Script cCode Add cCode string between <script> and </script> ScriptRedirec- tion cURL set window.location to cURL ScriptFunc cFuncName,cCode Define function cFuncName that contains cCode ScriptFuncAlert cFuncName,cMsg Define function cFuncName that uses alert() to print cMsg ScriptFuncAjax cFuncName,cLink,cDiv Define function cFuncName that load cLink in cDiv ScriptFuncClean cFuncName,cDiv Define function cFuncName that clear the cDiv ScriptFuncSe- lect cF,aL,cD,cR,cGR,cFC,nTO,cL1,cL2 Used to Edit/Delete Grid Record ScriptScroll- Fixed cDiv,nSize Set cDiv as Fixed Div with Size = nSize 52.29. ScriptFunctions Class 661
706.
Ring Documentation, Release1.21.2 52.30 StyleFunctions Class This class contains methods for adding CSS to the generated web page. Like ScriptFunctions Class, The StyleFunctions class methods are merged to the Page class, so we can use the next methods with page objects directly. Method Parameters Description StyleFloatLeft None Return float: left ; StyleFloatRight None Return float: right ; StyleSizeFull None Return width: 100% ; height: 100% ; Stylecolor x Return ” color: ” + x + ” ; “ Stylebackcolor x Return ” background-color: ” + x + ” ;” StyleTextCenter None Return “text-align: center ;” StyleTextRight None Return “text-align: right ;” StyleTextLeft None Return “text-align: left ;” StyleSize x,y Return ” width: ” + x + ” ; height: ” + y + ” ;” StyleWidth x Return ” width: ” + x + ” ;” StyleHeight x Return ” height: ” + x + ” ;” StyleTop x Return ” top: ” + x + ” ;” StyleLeft x Return ” Left: ” + x + ” ;” StylePos x,y Return ” top: ” + x + ” ;” + ” Left: ” + y + ” ;” StyleHorizontalCenter None Return ” margin-right:auto ; margin-left:auto; “ StyleMarginTop x Return ” margin-top: ” + x + ” ;” StyleMarginRight x Return ” margin-right: ” + x + ” ;” StyleMarginLeft x Return ” margin-left: ” + x + ” ;” StyleDivCenter nWidth,nHeight Create Div in the center of the page StyleAbsolute None Return ” position:absolute ;” StyleFixed None Return ” position:fixed ;” StyleZIndex x Return ” z-index: ” + x + ” ;” StyleFontSize x Return ” font-size: ” + x + ” ;” StyleGradient x Generate Gradient (x values from 1 to 60) StyleTable None Set table properties StyleTableRows id Set different color to even and odd rows in the table StyleTableNoBorder None Return ” border-style: none;” 52.31 WebPage Class We use braces to access the active WebPage object attributes Each one of these attribute will return a new object to access again using braces. Attribute Description H1 Wraps HTML H1. H2 Wraps HTML H2. H3 Wraps HTML H3. H4 Wraps HTML H4. H5 Wraps HTML H5. H6 Wraps HTML H6. continues on next page 52.30. StyleFunctions Class 662
707.
Ring Documentation, Release1.21.2 Table 3 – continued from previous page Attribute Description P Wraps HTML P. Link Wraps HTML link. NewLine Wraps HTML NewLine. Div Wraps HTML Div. Form Wraps HTML Form. Input Wraps HTML Input. TextArea Wraps HTML TextArea. Select Wraps HTML Select. Option Wraps HTML Option. Image Wraps HTML Image. UL Wraps HTML UL. LI Wraps HTML LI. Table Wraps HTML Table. TR Wraps HTML TR. TD Wraps HTML TD. TH Wraps HTML TH. Audio Wraps HTML Audio. Video Wraps HTML Video. Nav Wraps HTML Nav. Span Wraps HTML Span. Button Wraps HTML Button. THead Wraps HTML THEAD. TBody Wraps HTML TBODY. TFoot Wraps HTML TFOOT. 52.32 HtmlPage Class The same as the WebPage class with the next changes (1) No output to the stdout (2) Provide the Output Method to get the output Syntax: output() ---> The output as string 52.32. HtmlPage Class 663
708.
CHAPTER FIFTYTHREE DEPLOYING WEB APPLICATIONSIN THE CLOUD In this chapter we will learn about deploying Ring Web Applications in the Cloud using Heroku 53.1 Introduction We created a new project and tutorial to explain how to deploy Ring web applications in the Cloud using Heroku Project : https://github.com/ring-lang/RingWebAppOnHeroku Heroku Website : https://www.heroku.com/ 664
709.
Ring Documentation, Release1.21.2 53.2 Usage To use this project and deploy it on Heroku (1) Create Heroku account (2) Open your Heroku account and create new application Example : testring Note (You have to select a unique name for your application) (3) Open the command prompt, Create new folder : MyApp md MyApp (4) Open the application folder cd MyApp (5) Clone this project using Git (Don’t forget the dot in the end to clone in the current directory) git clone https://github.com/ring-lang/RingWebAppOnHeroku . (6) Login to Heroku (Enter your Email and Password) heroku login (7) Add heroku (remote) to your Git project change testring to your application name heroku git:remote -a testring (8) Set the buildpacks (So Heroku can know how to support your project) heroku buildpacks:add --index 1 https://github.com/ring-lang/heroku-buildpack-apt heroku buildpacks:add --index 2 https://github.com/ring-lang/heroku-buildpack-ring (9) Now build your project and deploy it git push heroku master (10) Test your project (In the browser) heroku open 53.3 Ring source code files and permissions To be able to run your new Ring scripts, Set the permission of the file to be executable using Git For example, if you created a file : myscript.ring git update-index --chmod=+x myscript.ring git commit -m "Update file permission" 53.2. Usage 665
710.
Ring Documentation, Release1.21.2 If you are using TortoiseGit, From windows explorer, select the file Right click —> Properties —> Git —> Executable (+x) Then commit and deploy! 53.4 Hello World program file : ringapp/helloworld.ring #!/app/runring.sh -cgi see "content-type: text/html" +nl+nl see "Hello, World!" + nl file : ringapp/helloworld2.ring #!/app/runring.sh -cgi load "weblib.ring" import System.Web new page { text("Hello, World!") } 53.5 Application Database When you deploy the application, Everything will works directly! No change is required, but in practice, You will need to update the next files to use your database There are two scripts to interact with the database (We are using PostgreSQL in the cloud) You will need to update the connection string in these files if you will use another database • file: ringapp/database/newdb.ring (We run it using the browser for one time to create the tables) • file: ringapp/datalib.ring (Class: Database) In your practical projects, You can write better code (To be able to change the database) Also you can create configuration file (To write the connection string in one place) Database service : https://www.heroku.com/postgres 53.6 Deploying after updates Just use Git and commit then push to heroku file: build.bat contains the next commands for quick tests git add . git commit -m "Update RingWebAppOnHeroku" git push heroku master heroku open 53.4. Hello World program 666
711.
Ring Documentation, Release1.21.2 53.7 Local Tests Local tests using Ring Notepad on Windows (Using local Apache Web Server) Replace the first line in the file : ringapp/index.ring with #!ring -cgi Then run it from Ring Notepad (Ctrl+F6) 53.7. Local Tests 667
712.
CHAPTER FIFTYFOUR GRAPHICS AND 2DGAMES PROGRAMMING USING RINGALLEGRO In this chapter we will learn how to use the allegro game programming library in our Ring applications. We have the file gamelib.ring that load the DLL library that contains wrappers for the Allegro functions Load "allegro.rh" if iswindows() LoadLib("ring_allegro.dll") but ismacosx() LoadLib("libringallegro.dylib") else LoadLib("libringallegro.so") ok The file gamelib.ring uses the Load instruction to execute the file allegro.rh which is a ring source code file contains constants to be used in our programs. Then using the function LoadLib() we can load the DLL library “ring_allegro.dll”. To write portable code we can change the gamelib.ring to check the platform before loading the DLL/So file. 54.1 Drawing, Animation and Input The next example uses the Allegro library for drawing, moving objects on the screen and getting input from the keyboard and the mouse. Load "gamelib.ring" al_init() al_init_image_addon() display = al_create_display(640,480) al_show_native_message_box(display, "Hello", "Welcome", "Using Allegro from the Ring programming language", "", 0); al_clear_to_color(al_map_rgb(0,0,255)) BOUNCER_SIZE = 40 bouncer_x = 10 bouncer_y = 20 bouncer = al_create_bitmap(BOUNCER_SIZE, BOUNCER_SIZE) (continues on next page) 668
Ring Documentation, Release1.21.2 (continued from previous page) while true al_init_timeout(timeout, 0.06) al_wait_for_event_until(event_queue, ev, timeout) switch al_get_allegro_event_type(ev) on ALLEGRO_EVENT_DISPLAY_CLOSE exit on ALLEGRO_EVENT_TIMER # Animation if bouncer_x < 0 or bouncer_x > SCREEN_W - BOUNCER_SIZE bouncer_dx = -bouncer_dx ok if bouncer_y < 0 or bouncer_y > SCREEN_H - BOUNCER_SIZE bouncer_dy = -bouncer_dy ok bouncer_x += bouncer_dx bouncer_y += bouncer_dy # Keyboard if key[KEY_UP] and bouncer_y >= 4.0 bouncer_y -= 4.0 ok if key[KEY_DOWN] and bouncer_y <= SCREEN_H - BOUNCER_SIZE - 4.0 bouncer_y += 4.0 ok if key[KEY_LEFT] and bouncer_x >= 4.0 bouncer_x -= 4.0 ok if key[KEY_RIGHT] and bouncer_x <= SCREEN_W - BOUNCER_SIZE - 4.0 bouncer_x += 4.0 ok redraw = true on ALLEGRO_EVENT_MOUSE_AXES bouncer_x = al_get_allegro_event_mouse_x(ev) bouncer_y = al_get_allegro_event_mouse_y(ev) on ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY bouncer_x = al_get_allegro_event_mouse_x(ev) bouncer_y = al_get_allegro_event_mouse_y(ev) on ALLEGRO_EVENT_MOUSE_BUTTON_UP exit on ALLEGRO_EVENT_KEY_DOWN switch al_get_allegro_event_keyboard_keycode(ev) on ALLEGRO_KEY_UP key[KEY_UP] = true on ALLEGRO_KEY_DOWN key[KEY_DOWN] = true on ALLEGRO_KEY_LEFT (continues on next page) 54.1. Drawing, Animation and Input 670
715.
Ring Documentation, Release1.21.2 (continued from previous page) key[KEY_LEFT] = true on ALLEGRO_KEY_RIGHT key[KEY_RIGHT] = true off on ALLEGRO_EVENT_KEY_UP switch al_get_allegro_event_keyboard_keycode(ev) on ALLEGRO_KEY_UP key[KEY_UP] = false on ALLEGRO_KEY_DOWN key[KEY_DOWN] = false on ALLEGRO_KEY_LEFT key[KEY_LEFT] = false on ALLEGRO_KEY_RIGHT key[KEY_RIGHT] = false on ALLEGRO_KEY_ESCAPE exit off off if redraw and al_is_event_queue_empty(event_queue) redraw = false al_clear_to_color(al_map_rgb(0,0,0)) al_draw_bitmap(bouncer, bouncer_x, bouncer_y, 0) al_flip_display() ok callgc() end al_destroy_timer(timer) al_destroy_allegro_event(ev) al_destroy_allegro_timeout(timeout) al_destroy_event_queue(event_queue) al_destroy_bitmap(bouncer) al_destroy_bitmap(image) al_destroy_display(display) Note: In the previous example we used the function callgc() which is a Ring function to force calling the Garbage collector inside the While/End loop. Program Output: At first the program display a messagebox 54.1. Drawing, Animation and Input 671
716.
Ring Documentation, Release1.21.2 Then we see two rectangles are moving on the screen 54.1. Drawing, Animation and Input 672
717.
Ring Documentation, Release1.21.2 Then we see an image displayed on the screen 54.1. Drawing, Animation and Input 673
718.
Ring Documentation, Release1.21.2 Finally we have one rectangle, and we see it moving all of the time on the screen but we can control it using the Mouse and/or the Keyboard 54.1. Drawing, Animation and Input 674
719.
Ring Documentation, Release1.21.2 54.2 Using TrueType Fonts In this example we will see how to use TrueType Fonts *.ttf in our Games using Allegro Load "gamelib.ring" al_init() al_init_font_addon() al_init_ttf_addon() display = al_create_display(800,600) al_clear_to_color(al_map_rgb(0,0,255)) font = al_load_ttf_font("pirulen.ttf",14,0 ) al_draw_text(font, al_map_rgb(255,255,255), 10, 10,ALLEGRO_ALIGN_LEFT, "Welcome to the Ring programming language") al_flip_display() al_rest(2) al_destroy_display(display) 54.2. Using TrueType Fonts 675
720.
Ring Documentation, Release1.21.2 Screen Shot: 54.3 Playing Sound Files The next example play a sound file Load "gamelib.ring" al_init() al_install_audio() al_init_acodec_addon() al_reserve_samples(1) sample = al_load_sample( "footstep.wav" ) sampleid = al_new_allegro_sample_id() al_play_sample(sample, 1.0, 0.0,1.0,ALLEGRO_PLAYMODE_LOOP,sampleid) display = al_create_display(640,480) al_clear_to_color(al_map_rgb(0,0,255)) al_flip_display() (continues on next page) 54.3. Playing Sound Files 676
721.
Ring Documentation, Release1.21.2 (continued from previous page) al_rest(10) al_destroy_allegro_sample_id(sampleid) al_destroy_sample(sample) al_destroy_display(display) al_exit() 54.4 Scaling and Rotating Images The next example display and rotate an image Load "gamelib.ring" al_init() al_init_image_addon() display = al_create_display(640,480) al_set_target_bitmap(al_get_backbuffer(display)) al_clear_to_color(al_map_rgb(255,255,255)) image = al_load_bitmap("man2.jpg") al_draw_rotated_bitmap(image,0,0,250,250,150,0) al_draw_scaled_bitmap(image,0,0,250,250,20,20,400,400,0) al_flip_display() al_rest(2) al_destroy_bitmap(image) al_destroy_display(display) Screen Shot: 54.4. Scaling and Rotating Images 677
722.
Ring Documentation, Release1.21.2 54.5 Display Transparent Image The next example display image with white background on another image Load "gamelib.ring" al_init() al_init_image_addon() display = al_create_display(640,480) imageback = al_load_bitmap("palace.jpg") al_draw_bitmap(imageback,0,0,0) image = al_load_bitmap("man4.png") al_convert_mask_to_alpha(image,al_map_rgb(255,255,255)) al_draw_bitmap(image,0,0,0) al_flip_display() al_rest(10) al_destroy_bitmap(image) (continues on next page) 54.5. Display Transparent Image 678
723.
Ring Documentation, Release1.21.2 (continued from previous page) al_destroy_display(display) Screen Shot: 54.6 Using Threads In this example we will learn how to use threads from the Allegro library Load "gamelib.ring" o1 = new mythreads Func Main al_init() for k = 1 to 5 al_create_thread("o1.thread1()") al_create_thread("o1.thread2()") al_create_thread("o1.thread3()") next (continues on next page) 54.6. Using Threads 679
724.
Ring Documentation, Release1.21.2 (continued from previous page) al_rest(2) Class Mythreads cAppName = "Threads Application" Func Thread1 for x = 1 to 5 see x + nl next See 'Thread(1) : Application Name : ' + cAppName + nl Func Thread2 for x = 1 to 5 see '*****' + x + nl next See 'Thread(2) : Application Name : ' + cAppName + nl Func Thread3 for x = 1 to 5 see '!!!!' + x + nl next See 'Thread(3) : Application Name : ' + cAppName + nl Output: 1 2 3 4 5 Thread(1) : Application Name : Threads Application *****1 *****2 *****3 *****4 *****5 Thread(2) : Application Name : Threads Application !!!!1 !!!!2 !!!!3 !!!!4 !!!!5 Thread(3) : Application Name : Threads Application 1 2 3 4 5 Thread(1) : Application Name : Threads Application !!!!1 !!!!2 (continues on next page) 54.6. Using Threads 680
725.
Ring Documentation, Release1.21.2 (continued from previous page) !!!!3 !!!!4 !!!!5 Thread(3) : Application Name : Threads Application *****1 *****2 *****3 *****4 *****5 Thread(2) : Application Name : Threads Application *****1 *****2 *****3 *****4 *****5 Thread(2) : Application Name : Threads Application !!!!1 !!!!2 !!!!3 !!!!4 !!!!5 Thread(3) : Application Name : Threads Application 1 2 3 4 5 Thread(1) : Application Name : Threads Application *****1 *****2 *****3 *****1 *****4 *****2 !!!!1 *****5 *****3 1 !!!!2 Thread(2) : Application Name : Threads Application 1 *****4 !!!!1 2 !!!!3 !!!!4 *****5 !!!!2 3 2 !!!!5 Thread(2) : Application Name : Threads Application (continues on next page) 54.6. Using Threads 681
726.
Ring Documentation, Release1.21.2 (continued from previous page) !!!!3 4 3 Thread(3) : Application Name : Threads Application !!!!4 5 4 !!!!5 Thread(1) : Application Name : Threads Application 5 Thread(3) : Application Name : Threads Application Thread(1) : Application Name : Threads Application 54.6. Using Threads 682
727.
CHAPTER FIFTYFIVE DEMO PROJECT -GAME ENGINE FOR 2D GAMES In this chapter we will learn about using the different programming paradigms in the same project. We will create a simple Game Engine for 2D Games. You can use the Engine directly to create 2D Games for Desktop or Mobile. 55.1 Project Layers The project contains the next layers • Games Layer (Here we will use declarative programming) • Game Engine Classes (Here we will use the Object-Oriented Programming paradigm) • Interface to graphics library (Here we will use procedural programming) • Graphics Library bindings (Here we have RingAllegro and RingLibSDL) 55.2 Graphics Library bindings We already have RingAllegro to use the Allegro game programming library and we have RingLibSDL to use the LibSDL game programming library. Both of RingAllegro and RingLibSDL are created using the C language with the help of the Ring code generator for extensions. Each of them is over 10,000 lines of C code which is generated after writing simple configuration files (That are processed by the code generator). Each configuration file determines the functions names, structures information and constants then the generator process this configuration file to produce the C code and the library that can be loaded from Ring code. Using RingAllegro and RingLibSDL is very similar to using Allegro and LibSDL from C code where you have the same functions but we can build on that using the Ring language features • RingAllegro Source Code : https://github.com/ring-lang/ring/tree/master/extensions/ringallegro • RingLibSDL Source Code : https://github.com/ring-lang/ring/tree/master/extensions/ringsdl 683
728.
Ring Documentation, Release1.21.2 55.3 Interface to graphics library In this layer we have gl_allegro.ring and gl_libsdl.ring Each library provides the same functions to be used with interacting with the Graphics Library. This layer hides the details and the difference between RingAllegro and RingLibSDL. You have the same functions, Just use it and you can switch between Allegro and LibSDL at anytime. Why ? Allegro is very simple, we can use it to quickly create 2D games for Windows, Linux and MacOS X. In Ring 1.0 we started by supporting Allegro. Also LibSDL is very powerful and popular, very easy to use for Mobile Development. Ring 1.1 comes with support for LibSDL so we can quickly create games for Mobile. Note: We can use just one library for Desktop and Mobile development. • gl_allegro.ring source code : https://github.com/ring-lang/ring/blob/master/libraries/gameengine/gl_allegro. ring • gl_libsdl.ring source code : https://github.com/ring-lang/ring/blob/master/libraries/gameengine/gl_libsdl.ring 55.4 Game Engine Classes The Engine comes with the next classes • GameBase class • Resources class • Game class • GameObject class • Sprite class • Text class • Animate class • Sound class • Map class • Source Code : https://github.com/ring-lang/ring/blob/master/libraries/gameengine/gameengine.ring 55.3. Interface to graphics library 684
729.
Ring Documentation, Release1.21.2 55.5 Games Layer In this layer we create our games using the Game Engine classes The classes are designed to be used through Declarative Programming. In our games we will use the next classes • Game class • Sprite class • Text class • Animate class • Sound class • Map class Note: Other classes in the engine are for internal use by the engine. We will introduce some examples and three simple games :- • Stars Fighter Game • Flappy Bird 3000 Game • Super Man 2016 Game 55.6 Game Class The next table present the class attributes. Attributes Description FPS Number determines how many times the draw() method will be called per second. FixedFPS Number determines how many times the animate() method will be called per second. Title String determines the window title of the game. Icon String determines the window icon (file name) aObjects List contains all objects in the game shutdown True/False value to end the game loop The next table present the class methods. Method Description refresh() Delete objects. settitle(cTitle) Set the window title using a string parameter. shutdown() Close the application. find(cName) Find an object using the object name remove(nID) Remove an object using the object ID The next table present a group of keywords defined by the class. 55.5. Games Layer 685
730.
Ring Documentation, Release1.21.2 Keyword Description sprite Create new Sprite object and add it to the game objects. text Create new Text object and add it to the game objects. animate Create new Animate object and add it to the game objects. sound Create new Sound object and add it to the game objects. map Create new Map object and add it to the game objects. 55.7 GameObject Class The next table present the class attributes. Attributes Description enabled True/False determine the state of the object (Active/Not Active) x Number determine the x position of the object. y Number determine the y position of the object. width Number determine the width of the object. height Number determine the height of the object. nIndex Number determine the ID of the object. name String represent the object name. animate True/False to animate the object or not. move True/False to move the object using the keyboard or not. Scaled True/False to scale the object image or not. draw Function to be called when drawing the object. state Function to be called for object animation. keypress Function to be called when a key is pressed. mouse Function to be called when a mouse event happens. The next table present the class methods. Method Description keyboard(oGame,nkey) Check Keyboard Events mouse(oGame,nType,aMouseList) Check Mouse Events rgb(r,g,b) Return new color using the RGB (Red, Green and Blue) Values. 55.8 Sprite Class Parent Class : GameObject Class The next table present the class attributes. 55.7. GameObject Class 686
731.
Ring Documentation, Release1.21.2 Attributes Description image String determine the image file name. point Number determine the limit of automatic movement of the object. direction Number determine the direction of movement. nstep Number determine the increment/decrement during movement. type Number determine the object type in the game (Optional). transparent True/False value determine if the image is transparent. The next table present the class methods. Method Description Draw(oGame) Draw the object 55.9 Text Class Parent Class : Sprite Class The next table present the class attributes. Attributes Description size Number determine the font size font String determine the font file name text String determine the text to be displayed color Number determine the color The next table present the class methods. Method Description Draw(oGame) Draw the object 55.10 Animate Class Parent Class : Sprite Class The next table present the class attributes. Attributes Description frames Number determine the number of frames frame Number determine the active frame framewidth Number determine the frame width. animate True/False determine using animate or not. scaled True/False determine scaling image or not. 55.9. Text Class 687
732.
Ring Documentation, Release1.21.2 The next table present the class methods. Method Description Draw(oGame) Draw the object 55.11 Sound Class Parent Class : GameObject Class The next table present the class attributes. Attributes Description file String determine the sound file name. once True/False determine to play the file one time or not (loop). The next table present the class methods. Method Description playsound() Play the sound file 55.12 Map Class Parent Class : Sprite Class The next table present the class attributes. Attributes Description aMap List determine the map content using numbers. aImages List determine the image used for each number in the map. BlockWidth Number determine the block width (default = 32). BlockHeight Number determine the block height (default = 32). Animate True/False determine the animation status. The next table present the class methods. Method Description getvalue(x,y) Return the item value in the Map according to the visible part 55.11. Sound Class 688
733.
Ring Documentation, Release1.21.2 55.13 Using the Game Engine - Creating the Game Window Load "gameengine.ring" # Give Control to the Game Engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" } # Start the Events Loop Note: if you want to define global variables, this must be before load “gameengine.ring” because this instruction will give the control to the game engine. Screen Shot: 55.13. Using the Game Engine - Creating the Game Window 689
734.
Ring Documentation, Release1.21.2 55.14 Using the Game Engine - Drawing Text Load "gameengine.ring" # Give Control to the Game Engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" text { x = 10 y=50 animate = false size = 20 file = "fonts/pirulen.ttf" text = "game development using ring is very fun!" color = rgb(0,0,0) } } # Start the Events Loop Screen Shot: 55.14. Using the Game Engine - Drawing Text 690
735.
Ring Documentation, Release1.21.2 55.15 Using the Game Engine - Moving Text Load "gameengine.ring" # Give Control to the Game Engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" text { x = 10 y=50 animate = false size = 20 file = "fonts/pirulen.ttf" text = "game development using ring is very fun!" color = rgb(0,0,0) # Color = black } text { x = 10 y=150 # Animation Part ===================================== animate = true # Use Animation direction = GE_DIRECTION_INCVERTICAL # Increase y point = 400 # Continue until y=400 nStep = 3 # Each time y+= 3 #===================================================== size = 20 file = "fonts/pirulen.ttf" text = "welcome to the real world!" color = rgb(0,0,255) # Color = Blue } } # Start the Events Loop Screen Shot: 55.15. Using the Game Engine - Moving Text 691
736.
Ring Documentation, Release1.21.2 55.16 Using the Game Engine - Playing Sound Load "gameengine.ring" # Give Control to the Game Engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" text { x = 10 y=50 animate = false size = 20 file = "fonts/pirulen.ttf" text = "game development using ring is very fun!" color = rgb(0,0,0) # Color = black } text { x = 10 y=150 # Animation Part ====================================== (continues on next page) 55.16. Using the Game Engine - Playing Sound 692
737.
Ring Documentation, Release1.21.2 (continued from previous page) animate = true # Use Animation direction = GE_DIRECTION_INCVERTICAL # Increase y point = 400 # Continue until y=400 nStep = 3 # Each time y+= 3 #====================================================== size = 20 file = "fonts/pirulen.ttf" text = "welcome to the real world!" color = rgb(0,0,255) # Color = Blue } Sound { # Play Sound file = "sound/music1.wav" # Sound File Name } } # Start the Events Loop 55.17 Using the Game Engine - Animation Load "gameengine.ring" # Give Control to the Game Engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" animate { file = "images/fire.png" x = 100 y = 200 framewidth = 40 height = 42 nStep = 3 # Used for delay transparent = true state = func oGame,oSelf { # Called by engine each frame oSelf { nStep-- if nStep = 0 nStep = 3 if frame < 13 # we have 13 frames in animation frame++ # move to next frame else oGame.remove(oself.nIndex) # remove object ok ok } } } } # Start the Events Loop 55.17. Using the Game Engine - Animation 693
738.
Ring Documentation, Release1.21.2 55.18 Using the Game Engine - Animation and Functions Load "gameengine.ring" # Give Control to the Game Engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" for x = 70 to 700 step 50 for y = 70 to 500 step 50 showfire(oGame,x,y) next next } # Start the Events Loop func showfire oGame,nX,nY oGame { animate { (continues on next page) 55.18. Using the Game Engine - Animation and Functions 694
739.
Ring Documentation, Release1.21.2 (continued from previous page) file = "images/fire.png" x = nX y = nY framewidth = 40 height = 42 nStep = 3 # Used for delay transparent = true state = func oGame,oSelf { # Called by engine each frame oSelf { nStep-- if nStep = 0 nStep = 3 if frame < 13 # we have 13 frames in animation frame++ # move to next frame else frame=1 ok ok } } } } 55.18. Using the Game Engine - Animation and Functions 695
740.
Ring Documentation, Release1.21.2 55.19 Using the Game Engine - Sprite - Automatic Movement using Keyboard Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" sprite { type = GE_TYPE_PLAYER # Just for our usage x=400 y=400 width=100 height=100 file = "images/player.png" transparent = true Animate=false Move=true # we can move it using keyboard arrows Scaled=true (continues on next page) 55.19. Using the Game Engine - Sprite - Automatic Movement using Keyboard 696
741.
Ring Documentation, Release1.21.2 (continued from previous page) } } # Start the Events Loop 55.20 Using the Game Engine - Sprite - Keypress event Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" sprite { type = GE_TYPE_PLAYER # Just for our usage x=400 y=400 width=100 height=100 file = "images/player.png" transparent = true (continues on next page) 55.20. Using the Game Engine - Sprite - Keypress event 697
742.
Ring Documentation, Release1.21.2 (continued from previous page) Animate=false Move=false # Custom Movement Scaled=true keypress = func oGame,oSelf,nKey { oSelf { Switch nKey on KEY_LEFT x -= 10 on KEY_RIGHT x += 10 on KEY_UP y -= 10 on KEY_DOWN y += 10 off } } } } # Start the Events Loop 55.21 Using the Game Engine - Sprite - Mouse event Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" sprite { type = GE_TYPE_PLAYER # Just for our usage x=400 y=400 width=100 height=100 file = "images/player.png" transparent = true Animate=false Move=false # Custom Movement Scaled=true keypress = func oGame,oSelf,nKey { oSelf { Switch nKey on KEY_LEFT x -= 10 on KEY_RIGHT x += 10 on KEY_UP y -= 10 on KEY_DOWN y += 10 off (continues on next page) 55.21. Using the Game Engine - Sprite - Mouse event 698
743.
Ring Documentation, Release1.21.2 (continued from previous page) } } mouse = func oGame,oSelf,nType,aMouseList { if nType = GE_MOUSE_UP oSelf { x = aMouseList[GE_MOUSE_X] y = aMouseList[GE_MOUSE_Y] } ok } } } # Start the Events Loop 55.22 Using the Game Engine - Sprite - State event Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" sprite { type = GE_TYPE_PLAYER # Just for our usage x=400 y=400 width=100 height=100 file = "images/player.png" transparent = true Animate=false Move=false # Custom Movement Scaled=true keypress = func oGame,oSelf,nKey { oSelf { Switch nKey on KEY_LEFT x -= 10 on KEY_RIGHT x += 10 on KEY_UP y -= 10 on KEY_DOWN y += 10 off } } mouse = func oGame,oSelf,nType,aMouseList { if nType = GE_MOUSE_UP oSelf { x = aMouseList[GE_MOUSE_X] y = aMouseList[GE_MOUSE_Y] (continues on next page) 55.22. Using the Game Engine - Sprite - State event 699
744.
Ring Documentation, Release1.21.2 (continued from previous page) } ok } state = func oGame,oSelf { oself { if x < 0 x = 0 ok if y < 0 y = 0 ok if x > ogame.width-width x= ogame.width - width ok if y > ogame.height-height y=ogame.height - height ok } } } } # Start the Events Loop 55.23 Using the Game Engine - Animate - Events Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" animate { file = "images/fbbird.png" x = 10 y = 10 framewidth = 20 scaled = true height = 50 width = 50 nStep = 3 transparent = true state = func oGame,oSelf { oSelf { # Animation nStep-- if nStep = 0 nStep = 3 if frame < 3 frame++ else frame=1 ok (continues on next page) 55.23. Using the Game Engine - Animate - Events 700
745.
Ring Documentation, Release1.21.2 (continued from previous page) ok # Move Down y += 3 if y > 550 y=550 ok } } keypress = func ogame,oself,nKey { oself { if nkey = key_space y -= 55 if y<=0 y=0 ok ok } } mouse = func ogame,oself,nType,aMouseList { if nType = GE_MOUSE_UP cFunc = oself.keypress call cFunc(oGame,oSelf,Key_Space) ok } } } # Start the Events Loop Screen Shot: 55.23. Using the Game Engine - Animate - Events 701
746.
Ring Documentation, Release1.21.2 55.24 Using the Game Engine - Map Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" Map { blockwidth = 80 blockheight = 80 aMap = [ [0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0], (continues on next page) 55.24. Using the Game Engine - Map 702
747.
Ring Documentation, Release1.21.2 (continued from previous page) [0,0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0] ] aImages = ["images/fbwall.png", "images/fbwallup.png", "images/fbwalldown.png"] state = func oGame,oSelf { oSelf { x -= 3 if x < - 2100 x = 0 ok } } } } # Start the Events Loop Screen Shot: 55.24. Using the Game Engine - Map 703
748.
Ring Documentation, Release1.21.2 55.25 Using the Game Engine - Map Events Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" Map { blockwidth = 80 blockheight = 80 aMap = [ [0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0] ] aImages = ["images/fbwall.png", "images/fbwallup.png", "images/fbwalldown.png"] state = func oGame,oSelf { oSelf { x -= 3 if x < - 2100 x = 0 ok } } mouse = func ogame,oself,nType,aMouseList { if nType = GE_MOUSE_UP oSelf { mX = aMouseList[GE_MOUSE_X] mY = aMouseList[GE_MOUSE_Y] nValue = GetValue(mX,mY) nRow = GetRow(mX,mY) nCol = GetCol(mX,mY) Switch nValue On 1 aMap[nRow][nCol] = 0 On 2 aMap[nRow][nCol] = 0 On 3 aMap[nRow][nCol] = 0 On 0 aMap[nRow][nCol] = 1 Off } (continues on next page) 55.25. Using the Game Engine - Map Events 704
749.
Ring Documentation, Release1.21.2 (continued from previous page) ok } } } # Start the Events Loop Screen Shot: 55.26 Using the Game Engine - Object and Drawing We can use the Object keyword (defined by the game engine) to create objects from the GameObject class. Example: Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { (continues on next page) 55.26. Using the Game Engine - Object and Drawing 705
750.
Ring Documentation, Release1.21.2 (continued from previous page) title = "My First Game" Object { x = 0 y=300 width = 200 height=200 draw = func oGame,oSelf { oSelf { for t = 1 to 210 gl_draw_circle(x,y,t, gl_map_rgb(t*random(255), t*2,t*3),1) next } } state = func oGame,oSelf { oSelf { if x <= 800 x+= 3 else x=0 ok } } keypress = func oGame,oSelf,nKey { oSelf { Switch nKey on KEY_LEFT x -= 10 on KEY_RIGHT x += 10 on KEY_UP y -= 10 on KEY_DOWN y += 10 off } } } } # Start the Events Loop Screen Shot: 55.26. Using the Game Engine - Object and Drawing 706
751.
Ring Documentation, Release1.21.2 Example: Load "gameengine.ring" # Give control to the game engine func main # Called by the Game Engine oGame = New Game # Create the Game Object { title = "My First Game" Object { x = 400 y=300 width = 200 height=200 draw = func oGame,oSelf { oSelf { for t = 1 to 210 gl_draw_rectangle(x+t,y+t, x+t*2,y+t*2, gl_map_rgb(t*random(255), t*2,t*3),1) gl_draw_rectangle(x+t*2,y+t*2, x-t*2,y-t*2, gl_map_rgb(t*random(255), t*2,t*3),1) (continues on next page) 55.26. Using the Game Engine - Object and Drawing 707
752.
Ring Documentation, Release1.21.2 (continued from previous page) next } } keypress = func oGame,oSelf,nKey { oSelf { Switch nKey on KEY_LEFT x -= 10 on KEY_RIGHT x += 10 on KEY_UP y -= 10 on KEY_DOWN y += 10 off } } } } # Start the Events Loop Screen Shot: 55.26. Using the Game Engine - Object and Drawing 708
753.
Ring Documentation, Release1.21.2 55.27 Stars Fighter Game The Stars Fighter source code # The Ring Standard Library # Game Engine for 2D Games # 2016, Mahmoud Fayed <msfclipper@yahoo.com> oGameState = NULL load "gameengine.ring" func main oGame = New Game while true oGameState = new GameState (continues on next page) 55.27. Stars Fighter Game 709
754.
Ring Documentation, Release1.21.2 (continued from previous page) oGame { title = "Stars Fighter!" sprite { file = "images/menu1.jpg" x = 0 y=0 width=800 height = 600 scaled = true animate = false keypress = func ogame,oself,nKey { if nkey = key_esc or nKey = GE_AC_BACK ogame.shutdown() but nKey = key_space oGameState.startplay=true ogame.shutdown=true ok } mouse = func ogame,oself,nType,aMouseList { if nType = GE_MOUSE_UP oGameState.startplay=true ogame.shutdown=true ok } } text { animate = false size = 35 file = "fonts/pirulen.ttf" text = "Stars Fighter" x = 10 y=50 } text { animate = false size = 25 file = "fonts/pirulen.ttf" text = "Version 1.0" x = 80 y=100 } text { animate = false size = 16 file = "fonts/pirulen.ttf" text = "(C) 2016, Mahmoud Fayed" x = 45 y=140 } text { animate = false size = 25 file = "fonts/pirulen.ttf" text = "Press Space to start" x = 190 y=470 } text { animate = false (continues on next page) 55.27. Stars Fighter Game 710
755.
Ring Documentation, Release1.21.2 (continued from previous page) size = 20 file = "fonts/pirulen.ttf" text = "Press Esc to Exit" x = 260 y=510 } Sound { file = "sound/music1.wav" } } if oGameState.startplay oGame.refresh() playstart(oGame) oGame.refresh() ok end func playstart oGame oSound = New Sound { file = "sound/music2.wav" } while true play(oGame) if ogame.shutdown = true and oGameState.value = 0 exit ok ogame.refresh() end oSound.Delete() func play oGame oGame { FPS = 60 FixedFPS = 120 title = "Stars Fighter!" sprite { file = "images/stars.jpg" x = 0 y = 0 point = -370 direction = ge_direction_dec type = ge_type_background state = func ogame,oself { oself { if x < -350 (continues on next page) 55.27. Stars Fighter Game 711
756.
Ring Documentation, Release1.21.2 (continued from previous page) direction = ge_direction_inc point = 370 but x = 0 and direction = ge_direction_inc direction = ge_direction_dec point = -370 ok } } } sprite { file = "images/player.png" transparent = true type = ge_type_player x = 400 y =400 width=100 height=100 animate=false move=true Scaled=true mouse = func ogame,oself,nType,aMouseList { if not ( aMouseList[GE_MOUSE_X] >= oSelf.x and aMouseList[GE_MOUSE_X] <= oSelf.x+oSelf.width and aMouseList[GE_MOUSE_Y] >= oself.y and aMouseList[GE_MOUSE_Y] <= oSelf.y+oSelf.height ) if nType = GE_MOUSE_DOWN if aMouseList[1] < oSelf.X # left oSelf.X -= 100 else oSelf.X += 100 ok if aMouseList[2] < oSelf.Y # up oSelf.Y -= 100 else oSelf.Y += 100 ok ok else if nType = GE_MOUSE_UP cFunc = oself.keypress call cFunc(oGame,oSelf,Key_Space) ok ok } keypress = func oGame,oself,nkey { if nkey = key_space ogame { sprite { type = ge_type_fire file = "images/rocket.png" transparent = true x = oself.x + 30 y = oself.y - 30 (continues on next page) 55.27. Stars Fighter Game 712
757.
Ring Documentation, Release1.21.2 (continued from previous page) width = 30 height = 30 point = -30 nstep = 20 direction = ge_direction_decvertical state = func oGame,oSelf { for x in oGame.aObjects if x.type = ge_type_enemy if oself.x >= x.x and oself.y >= x.y and oself.x <= x.x + x.width and oself.y <= x.y + x.height showfire(oGame,x.x+40,x.y+40) ogame.remove(x.nindex) oGameState.score+=10 oGameState.enemies-- checkwin(oGame) exit ok ok next } } } but nkey = key_esc or nKey = GE_AC_BACK ogame.shutdown() ok } state = func oGame,oSelf { oself { if x < 0 x = 0 ok if y < 0 y = 0 ok if x > ogame.screen_w-width x= ogame.screen_w - width ok if y > ogame.screen_h-height y=ogame.screen_h-height ok } } } for g = 1 to oGameState.enemies sprite { type = ge_type_enemy file = "images/enemy.png" transparent = true x = g*random(50) y =g width=100 height=100 animate=true Scaled=true direction = ge_direction_random state = func oGame,oSelf { oself { if x < 0 x = 0 ok if y < 0 y = 0 ok if x > ogame.screen_w-width x= ogame.screen_w - width ok if y > ogame.screen_h-height y=ogame.screen_h-height ok } if random(100) = 1 (continues on next page) 55.27. Stars Fighter Game 713
758.
Ring Documentation, Release1.21.2 (continued from previous page) ogame { sprite { type = ge_type_fire file = "images/rocket2.png" transparent = true x = oself.x + 30 y = oself.y + oself.height+ 30 width = 30 height = 30 point = ogame.screen_h+30 nstep = 10 direction = ge_direction_incvertical state = func oGame,oSelf { x = oGame.aObjects[oGameState.playerindex] if oself.x >= x.x and oself.y >= x.y and oself.x <= x.x + x.width and oself.y <= x.y + x.height if oGameState.value > 0 oGameState.value-=10 ok ogame.remove(oself.nindex) checkgameover(oGame) ok } } } ok } } next text { size = 30 file = "fonts/pirulen.ttf" text = "Destroy All Enemies!" nstep = 3 color = GE_COLOR_GREEN x = 100 y=50 direction = ge_direction_incvertical point = 500 } text { animate = false point = 400 size = 30 file = "fonts/pirulen.ttf" text = "Score : " + oGameState.score x = 500 y=10 state = func oGame,oSelf { oSelf { text = "Score : " + oGameState.score } } } text { animate = false point = 400 (continues on next page) 55.27. Stars Fighter Game 714
759.
Ring Documentation, Release1.21.2 (continued from previous page) size = 30 file = "fonts/pirulen.ttf" text = "Energy : " + oGameState.value x = 500 y=50 state = func oGame,oSelf { oSelf { text = "Energy : " + oGameState.value } } } text { animate = false point = 400 size = 30 file = "fonts/pirulen.ttf" text = "Level : " + oGameState.level x = 500 y=90 } } func checkwin ogame if oGameState.gameresult return ok if oGameState.enemies = 0 oGameState.gameresult = true oGame { if oGameState.level < 30 text { point = 400 size = 30 file = "fonts/pirulen.ttf" text = "Level Completed!" nStep = 3 x = 500 y=10 state = func ogame,oself { if oself.y >= 400 ogame.shutdown = true oGameState.level++ oGameState.enemies = oGameState.level oGameState.gameresult = false ok } } else text { point = 400 size = 30 nStep = 3 file = "fonts/pirulen.ttf" text = "You Win !!!" x = 500 y=10 state = func ogame,oself { if oself.y >= 400 ogame.shutdown = true oGameState.value = 0 ok (continues on next page) 55.27. Stars Fighter Game 715
760.
Ring Documentation, Release1.21.2 (continued from previous page) } } ok } ok func checkgameover ogame if oGameState.gameresult return ok if oGameState.value <= 0 oGameState.gameresult = true oGame { text { point = 400 size = 30 nStep = 3 file = "fonts/pirulen.ttf" text = "Game Over !!!" x = 500 y=10 state = func ogame,oself { if oself.y >= 400 ogame.shutdown = true ok } } } showfire(oGame,oGame.aObjects[oGameState.PlayerIndex].x+40, oGame.aObjects[oGameState.PlayerIndex].y+40) oGame.aObjects[oGameState.PlayerIndex].enabled = false oGame.remove(oGameState.PlayerIndex) ok func showfire oGame,nX,nY oGame { animate { file = "images/fire.png" x = nX y = nY framewidth = 40 height = 42 nStep = 3 transparent = true state = func oGame,oSelf { oSelf { nStep-- if nStep = 0 nStep = 3 if frame < 13 frame++ else frame=1 oGame.remove(oself.nIndex) (continues on next page) 55.27. Stars Fighter Game 716
761.
Ring Documentation, Release1.21.2 (continued from previous page) ok ok } } } } class gamestate score = 0 level = 1 enemies = 1 value = 100 playerindex = 2 gameresult = false startplay=false Screen Shot: 55.27. Stars Fighter Game 717
762.
Ring Documentation, Release1.21.2 55.28 Flappy Bird 3000 Game The Flappy Bird 3000 Game source code # The Ring Standard Library # Game Engine for 2D Games # 2016, Mahmoud Fayed <msfclipper@yahoo.com> oGameState = NULL Load "gameengine.ring" func main oGame = New Game while true oGameState = New GameState oGame { title = "Flappy Bird 3000" sprite { file = "images/fbback.png" x = 0 y=0 width=800 height = 600 scaled = true animate = false keypress = func ogame,oself,nKey { if nkey = key_esc or nKey = GE_AC_BACK ogame.shutdown() but nKey = key_space oGameState.startplay=true ogame.shutdown=true ok } mouse = func ogame,oself,nType,aMouseList { if nType = GE_MOUSE_UP cFunc = oself.keypress call cFunc(oGame,oSelf,Key_Space) ok } } text { animate = false size = 35 file = "fonts/pirulen.ttf" text = "Flappy Bird 3000" x = 150 y=50 } text { animate = false size = 25 file = "fonts/pirulen.ttf" (continues on next page) 55.28. Flappy Bird 3000 Game 718
763.
Ring Documentation, Release1.21.2 (continued from previous page) text = "Version 1.0" x = 280 y=100 } text { animate = false size = 16 file = "fonts/pirulen.ttf" text = "(C) 2016, Mahmoud Fayed" x = 245 y=140 } text { animate = false size = 25 file = "fonts/pirulen.ttf" text = "To Win Get Score = 3000" x = 150 y=270 } text { animate = false size = 25 file = "fonts/pirulen.ttf" text = "Press Space to start" x = 190 y=470 } text { animate = false size = 20 file = "fonts/pirulen.ttf" text = "Press Esc to Exit" x = 260 y=510 } animate { file = "images/fbbird.png" x = 200 y = 200 framewidth = 20 scaled = true height = 50 width = 50 nStep = 3 transparent = true animate = true direction = ge_direction_random state = func oGame,oSelf { oSelf { nStep-- if nStep = 0 nStep = 3 if frame < 3 (continues on next page) 55.28. Flappy Bird 3000 Game 719
764.
Ring Documentation, Release1.21.2 (continued from previous page) frame++ else frame=1 ok ok if x <= 0 x=0 ok if y <= 0 y=0 ok if x >= 750 x= 750 ok if y > 550 y=550 ok } } } Sound { file = "sound/music2.wav" } } if oGameState.startplay oGame.refresh() playstart(oGame) oGame.refresh() ok end func playstart oGame oGame { FPS = 60 FixedFPS = 120 Title = "Flappy Bird 3000" Sprite { file = "images/fbback.png" x = 0 y=0 width=800 height = 600 scaled = true animate = false keypress = func ogame,oself,nKey { if nkey = key_esc or nKey = GE_AC_BACK ogame.shutdown() ok } } Map { blockwidth = 80 blockheight = 80 aMap = [ [0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0], [0,0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], (continues on next page) 55.28. Flappy Bird 3000 Game 720
765.
Ring Documentation, Release1.21.2 (continued from previous page) [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0] ] newmap(aMap) aImages = ["images/fbwall.png","images/fbwallup.png", "images/fbwalldown.png"] state = func oGame,oSelf { if oGameState.gameresult = false px = oGame.aObjects[3].x py = oGame.aObjects[3].y oSelf { x -= 3 if x < - 2100 x = 0 newmap(aMap) ok nCol = getcol(px,0) if nCol=11 or nCol=15 or nCol=19 or nCol=23 or nCol=27 if nCol != oGameState.lastcol oGameState.lastcol = nCol oGameState.Score += 100 oGame { Sound { once = true file = "sound/sfx_point.wav" } } checkwin(oGame) ok ok } if oSelf.getvalue(px+40,py) != 0 or oSelf.getvalue(px+40,py+40) != 0 or oSelf.getvalue(px,py) != 0 or oSelf.getvalue(px,py+40) != 0 oGameState.gameresult = true oGame { text { point = 550 size = 30 nStep = 3 file = "fonts/pirulen.ttf" text = "Game Over !!!" x = 500 y=10 state = func ogame,oself { if oself.y >= 550 ogame.shutdown = true ok if oself.y = 90 ogame { Sound { once = true file = "sound/sfx_die.wav" } (continues on next page) 55.28. Flappy Bird 3000 Game 721
766.
Ring Documentation, Release1.21.2 (continued from previous page) } ok } } Sound { once = true file = "sound/sfx_hit.wav" } } ok ok } } animate { file = "images/fbbird.png" x = 10 y = 10 framewidth = 20 scaled = true height = 50 width = 50 nStep = 3 transparent = true state = func oGame,oSelf { oSelf { nStep-- if nStep = 0 nStep = 3 if frame < 3 frame++ else frame=1 ok ok } if not oGameState.playerwin oGameState.down -- if oGameState.down = 0 oGameState.down = 3 oself { y += 25 if y > 550 y=550 ok } ok ok } keypress = func ogame,oself,nKey { if oGameState.gameresult = false oself { (continues on next page) 55.28. Flappy Bird 3000 Game 722
767.
Ring Documentation, Release1.21.2 (continued from previous page) if nkey = key_space y -= 55 oGameState.down = 60 if y<=0 y=0 ok ok } ok } mouse = func ogame,oself,nType,aMouseList { if nType = GE_MOUSE_UP cFunc = oself.keypress call cFunc(oGame,oSelf,Key_Space) ok } } text { animate = false point = 400 size = 30 file = "fonts/pirulen.ttf" text = "Score : " + oGameState.score x = 500 y=10 state = func oGame,oSelf { oSelf { text = "Score : " + oGameState.score } } } } func newmap aMap aV = [ [1,1,3,0,0,2,1,1], [1,3,0,0,0,2,1,1], [1,1,1,3,0,2,1,1], [1,1,1,3,0,0,0,0], [0,0,0,0,2,1,1,1], [0,0,2,1,1,1,1,1], [0,0,0,2,1,1,1,1], [1,1,1,3,0,2,1,1], [1,1,1,1,1,3,0,0], [3,0,0,2,1,1,1,1], [3,0,0,2,3,0,0,2] ] for x = 10 to 24 step 4 aVar = aV[ (random(10)+1) ] for y = 1 to 8 aMap[y][x] = aVar[y] next next func checkwin ogame (continues on next page) 55.28. Flappy Bird 3000 Game 723
768.
Ring Documentation, Release1.21.2 (continued from previous page) if oGameState.score = 3000 oGameState.gameresult = true oGameState.playerwin = true oGame { text { point = 400 size = 30 nStep = 3 file = "fonts/pirulen.ttf" text = "You Win !!!" x = 500 y=10 state = func ogame,oself { if oself.y >= 400 ogame.shutdown = true oGameState.value = 0 ok } } } ok Class GameState down = 3 gameresult = false Score = 0 startplay=false lastcol = 0 playerwin = false Screen Shot: 55.28. Flappy Bird 3000 Game 724
769.
Ring Documentation, Release1.21.2 55.29 Super Man 2016 Game The Super Man 2016 Game source code # The Ring Standard Library # Game Engine for 2D Games # 2016, Mahmoud Fayed <msfclipper@yahoo.com> oGameState = NULL Load "gameengine.ring" func main oGame = New Game while true oGameState = new GameState oGame { (continues on next page) 55.29. Super Man 2016 Game 725
770.
Ring Documentation, Release1.21.2 (continued from previous page) title = "Super Man 2016" sprite { file = "images/superman.jpg" x = 0 y=0 width=800 height = 600 scaled = true animate = false keypress = func ogame,oself,nKey { if nkey = key_esc or nKey = GE_AC_BACK ogame.shutdown() but nKey = key_space oGameState.startplay=true ogame.shutdown=true ok } mouse = func ogame,oself,nType,aMouseList { if nType = GE_MOUSE_UP oGameState.startplay=true ogame.shutdown=true ok } state = func ogame,oself { oself { if x > -500 x-=1 y-=1 width +=1 height +=4 ok } } } text { animate = false size = 35 file = "fonts/pirulen.ttf" text = "Super Man 2016" x = 20 y=30 } text { animate = false size = 25 file = "fonts/pirulen.ttf" text = "Version 1.0" x = 20 y=80 } text { animate = false size = 16 file = "fonts/pirulen.ttf" text = "(C) 2016, Mahmoud Fayed" x = 20 y=120 } (continues on next page) 55.29. Super Man 2016 Game 726
771.
Ring Documentation, Release1.21.2 (continued from previous page) text { animate = false size = 25 file = "fonts/pirulen.ttf" text = "Press Space to start" x = 190 y=470 } text { animate = false size = 20 file = "fonts/pirulen.ttf" text = "Press Esc to Exit" x = 260 y=510 } animate { file = "images/superman.png" x = 200 y = 200 framewidth = 68 scaled = true height = 86 width = 60 nStep = 10 transparent = true animate = true direction = ge_direction_random state = func oGame,oSelf { oSelf { nStep-- if nStep = 0 nStep = 10 if frame < 1 frame++ else frame=1 ok ok if x <= 0 x=0 ok if y <= 0 y=0 ok if x >= 750 x= 750 ok if y > 550 y=550 ok } } } Sound { file = "sound/music2.wav" } } if oGameState.startplay oGame.refresh() (continues on next page) 55.29. Super Man 2016 Game 727
772.
Ring Documentation, Release1.21.2 (continued from previous page) playstart(oGame) oGame.refresh() ok end func playstart oGame oGame { FPS = 60 FixedFPS = 15 Title = "Super Man 2016" Sprite { file = "images/supermancity.jpg" x = 0 y=0 width=800 height = 600 scaled = true animate = false } Map { blockwidth = 80 blockheight = 80 aMap = [ [0,0,0,4,4,4,0,0,0,1,0,0,0,1,4,4,0,1,0,0,0,0,4,4,0,1,4, 4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,1,0,0,0,1,0,0,0,1,0,3,3,3,5,3,3,3,3,0], [0,0,4,0,4,0,4,0,0,1,0,0,0,3,4,4,4,1,0,0,0,0,4,4,0,1,4, 4,4,0,0,4,4,4,4,4,4,4,4,4,4,4,4,1,4,1,0,0,0,1,0,0,0,1,0,4,4,4,4,4,4,4,4,0], [0,0,0,4,4,4,0,0,0,1,0,0,0,4,4,4,4,1,0,0,0,0,0,0,0,3,4, 4,4,0,0,4,0,0,0,0,0,0,4,2,0,0,4,1,4,1,4,2,4,1,0,2,0,1,0,4,4,4,4,4,4,4,4,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,4,4,4,4,4,4,4,4,1,0,0,4,1,4,1,4,1,4,1,0,1,0,1,0,2,2,2,2,2,2,2,2,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,2,0,0,0,0,0,2,0,3,0,0,0,1,4,1,4,1,4,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,0,0, 0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,4,3,4,1,4,3,0,1,0,3,0,1,0,0,0,0,0,0,0,0], [0,0,2,0,0,2,0,0,2,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0, 0,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,1,0,0,1,0,0,1,3,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,0,0, 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0] ] aImages = ["images/smwall.png","images/smwallup.png", "images/smwalldown.png","images/smstar.png", "images/smkey.png","images/smstar2.png"] } sprite { type = ge_type_enemy animate = false file = "images/smhome.png" x = 5000 y = 400 width = 290 height = 200 transparent = true (continues on next page) 55.29. Super Man 2016 Game 728
773.
Ring Documentation, Release1.21.2 (continued from previous page) state = func oGame,oSelf { oself { x = 5000 + oGame.aObjects[2].x if x < 0 or x > SCREEN_W return ok } if oGameState.gameresult or oGameState.DoorKey = false return ok if oGame.aObjects[oGameState.playerindex].x > oself.x + 100 and oGame.aObjects[oGameState.playerindex].y > oself.y + 50 oGameState.gameresult = true oGame { sprite { file = "images/smwin.jpg" x=0 y=0 width=800 height=600 scaled = true animate=false state = func ogame,oself { oself { x-=5 y-=5 width +=10 height +=10 if x = -300 ogame.shutdown = true ok } } } } ok } } animate { file = "images/superman.png" x = 0 y = 0 framewidth = 60 scaled = true height = 86 width = 60 nStep = 3 transparent = true state = func oGame,oSelf { checkstarskeycol(oGame,oSelf) if not oGameState.playerwin oself { file = "images/superman.png" height = 86 width = 60 (continues on next page) 55.29. Super Man 2016 Game 729
774.
Ring Documentation, Release1.21.2 (continued from previous page) for t=1 to 8 if checkwall2(oGame,oSelf,0,5,[2,1]) y += 5 else exit ok next if y > 500 y=500 ok } ok } keypress = func ogame,oself,nKey { if oGameState.gameresult = false oself { if nkey = key_up and checkwall(oGame,oSelf,0,-40) oGameState.value -= 1 checkgameover(oGame) file = "images/supermanup.png" height = 123 dotransparent() y -= 40 oGameState.down = 10 if y<=0 y=0 ok but nkey = key_down and checkwall(oGame,oSelf,0,40) file = "images/supermandown.png" dotransparent() y += 40 if y>=500 y=500 ok but nKey = key_right and checkwall(oGame,oSelf,10,0) file = "images/supermanright.png" dotransparent() x += 10 if x >= 440 if oGame.aObjects[2].x > -4500 oGame.aObjects[2].x -= 50 callenemystate(oGame) else if x <= 750 if checkwall(oGame,oSelf,10,0) x += 10 ok else if checkwall(oGame,oSelf,-10,0) x -= 10 ok ok return ok x=400 ok (continues on next page) 55.29. Super Man 2016 Game 730
775.
Ring Documentation, Release1.21.2 (continued from previous page) but nKey = key_left and checkwall(oGame,oSelf,-10,0) file = "images/supermanleft.png" dotransparent() x -= 10 if x <= 0 x += 10 if oGame.aObjects[2].x != 0 oGame.aObjects[2].x += 50 callenemystate(oGame) x += 50 ok ok but nkey = key_esc or nKey = GE_AC_BACK ogame.shutdown() ok } ok } mouse = func ogame,oself,nType,aMouseList { if nType = GE_MOUSE_DOWN oGameState.moveplayer = TRUE But nType = GE_MOUSE_UP oGameState.moveplayer = FALSE ok if oGameState.moveplayer = TRUE if aMouseList[GE_MOUSE_X] < oSelf.X # left cFunc = oself.keypress call cFunc(oGame,oSelf,Key_left) else cFunc = oself.keypress call cFunc(oGame,oSelf,Key_right) ok if aMouseList[GE_MOUSE_Y] < oSelf.Y # up cFunc = oself.keypress call cFunc(oGame,oSelf,Key_up) else cFunc = oself.keypress call cFunc(oGame,oSelf,Key_down) ok ok } } addenemy(oGame,600) addenemy(oGame,900) addenemy(oGame,1550) addenemy(oGame,2350) addenemy(oGame,3350) addenemy(oGame,3500) addenemy(oGame,3670) addenemy(oGame,3840) (continues on next page) 55.29. Super Man 2016 Game 731
776.
Ring Documentation, Release1.21.2 (continued from previous page) text { animate = false point = 400 size = 30 file = "fonts/pirulen.ttf" text = "Score : " + oGameState.score x = 500 y=0 state = func oGame,oSelf { oSelf { text = "Score : " + oGameState.score } } } text { animate = false point = 400 size = 30 file = "fonts/pirulen.ttf" text = "Energy : " + oGameState.value x = 10 y=0 state = func oGame,oSelf { oSelf { text = "Energy : " + oGameState.value } } } } func inlist nValue,aList for x in aList if x = nValue return true ok next return false func checkwall oGame,oself,diffx,diffy alist = [1,2,3] return checkwall2(oGame,oself,diffx,diffy,aList) func checkwall2 oGame,oself,diffx,diffy,aList xPos = oSelf.x + diffx yPos = oSelf.y + diffy nValue = oGame.aObjects[2].getvalue(xPos,yPos) nValue = inlist(nValue,aList) nValue = not nValue if nValue = 0 return nValue ok xPos = oSelf.x + diffx yPos = oSelf.y + diffy + oSelf.height nValue = oGame.aObjects[2].getvalue(xPos,yPos) nValue = inlist(nValue,aList) nValue = not nValue if nValue = 0 return nValue ok xPos = oSelf.x + diffx + oSelf.width (continues on next page) 55.29. Super Man 2016 Game 732
777.
Ring Documentation, Release1.21.2 (continued from previous page) yPos = oSelf.y + diffy nValue = oGame.aObjects[2].getvalue(xPos,yPos) nValue = inlist(nValue,aList) nValue = not nValue if nValue = 0 return nValue ok xPos = oSelf.x + diffx + oSelf.width yPos = oSelf.y + diffy + oSelf.height nValue = oGame.aObjects[2].getvalue(xPos,yPos) nValue = inlist(nValue,aList) nValue = not nValue if nValue = 0 return nValue ok return nValue func checkopenwall oGame if oGameState.score = 900 oGame.aObjects[2].aMap[3][10] = 3 oGame.aObjects[2].aMap[4][10] = 0 oGame.aObjects[2].aMap[5][10] = 0 oGame.aObjects[2].aMap[6][10] = 0 oGame.aObjects[2].aMap[7][10] = 0 oGame.aObjects[2].aMap[8][10] = 0 but oGameState.score = 1800 oGame.aObjects[2].aMap[3][18] = 3 oGame.aObjects[2].aMap[4][18] = 0 oGame.aObjects[2].aMap[5][18] = 0 oGame.aObjects[2].aMap[6][18] = 0 oGame.aObjects[2].aMap[7][18] = 0 oGame.aObjects[2].aMap[8][18] = 0 but oGameState.score = 5500 oGame.aObjects[2].aMap[1][44] = 0 oGame.aObjects[2].aMap[2][44] = 0 oGame.aObjects[2].aMap[3][44] = 2 ok func checkgameover ogame if oGameState.gameresult return ok if oGameState.value <= 0 oGameState.value = 0 oGameState.gameresult = true oGame { text { point = 400 size = 30 nStep = 9 file = "fonts/pirulen.ttf" text = "Game Over !!!" x = 500 y=10 state = func ogame,oself { if oself.y >= 400 (continues on next page) 55.29. Super Man 2016 Game 733
778.
Ring Documentation, Release1.21.2 (continued from previous page) ogame.shutdown = true ok } } } showfire(oGame,oGame.aObjects[oGameState.PlayerIndex].x+40, oGame.aObjects[oGameState.PlayerIndex].y+40) oGame.aObjects[oGameState.PlayerIndex].enabled = false oGame.remove(oGameState.PlayerIndex) ok func showfire oGame,nX,nY oGame { animate { file = "images/fire.png" x = nX y = nY framewidth = 40 height = 42 nStep = 3 transparent = true state = func oGame,oSelf { oSelf { nStep-- if nStep = 0 nStep = 3 if frame < 13 frame++ else frame=1 oGame.remove(oself.nIndex) ok ok } } } } func addenemy oGame,xPos oGame { lbraceend = false sprite { type = ge_type_enemy file = "images/smenemy.png" transparent = true x = xPos y =10 width=100 height=100 animate=true Scaled=true direction = GE_DIRECTION_NOMOVE temp = xPos state = func oGame,oSelf { oself { (continues on next page) 55.29. Super Man 2016 Game 734
779.
Ring Documentation, Release1.21.2 (continued from previous page) x = oSelf.temp + oGame.aObjects[2].x if y < 0 y = 0 ok if y > 100 y=100 ok if x > SCREEN_W or x < 0 return ok } if random(10) = 1 if oGameState.gameresult return ok ogame { sprite { type = ge_type_fire file = "images/smrocket.png" scaled = true transparent = true x = oself.x + 30 y = oself.y + oself.height+ 30 width = 30 height = 30 point = ogame.screen_h+30 nstep = 30 direction = ge_direction_incvertical xvalue = oGame.aObjects[2].x temp = oself.x + 30 - xvalue state = func oGame,oSelf { oself { x = oSelf.temp + oGame.aObjects[2].x } x = oGame.aObjects[oGameState.playerindex] if oself.x >= x.x and oself.y >= x.y and oself.x <= x.x + x.width and oself.y <= x.y + x.height if oGameState.value > 0 oGameState.value-=1000 ok ogame.remove(oself.nindex) checkgameover(oGame) ok } } } ok } } } ogame.lbraceend = true func checkstarskey oGame,oSelf,nValue,nRow,nCol switch nValue on 4 oGame.aObjects[2].aMap[nRow][nCol] = 6 oGameState.Score += 100 checkopenwall(oGame) oGame { Sound { (continues on next page) 55.29. Super Man 2016 Game 735
780.
Ring Documentation, Release1.21.2 (continued from previous page) once = true file = "sound/sfx_point.wav" } } on 5 oGame.aObjects[2].aMap[nRow][nCol] = 0 oGameState.DoorKey = true oGameState.Score += 500 checkopenwall(oGame) oGame { Sound { once = true file = "sound/sfx_point.wav" } } off func checkstarskeycol oGame,oSelf nValue = oGame.aObjects[2].getvalue(oSelf.x,oSelf.y) nRow = oGame.aObjects[2].getrow(oSelf.x,oSelf.y) nCol = oGame.aObjects[2].getcol(oSelf.x,oSelf.y) checkstarskey(oGame,oSelf,nValue,nRow,nCol) nValue = oGame.aObjects[2].getvalue(oSelf.x+oSelf.width,oSelf.y+oSelf.height) nRow = oGame.aObjects[2].getrow(oSelf.x+oSelf.width,oSelf.y+oSelf.height) nCol = oGame.aObjects[2].getcol(oSelf.x+oSelf.width,oSelf.y+oSelf.height) checkstarskey(oGame,oSelf,nValue,nRow,nCol) nValue = oGame.aObjects[2].getvalue(oSelf.x+oSelf.width,oSelf.y) nRow = oGame.aObjects[2].getrow(oSelf.x+oSelf.width,oSelf.y) nCol = oGame.aObjects[2].getcol(oSelf.x+oSelf.width,oSelf.y) checkstarskey(oGame,oSelf,nValue,nRow,nCol) nValue = oGame.aObjects[2].getvalue(oSelf.x,oSelf.y+oSelf.height) nRow = oGame.aObjects[2].getrow(oSelf.x,oSelf.y+oSelf.height) nCol = oGame.aObjects[2].getcol(oSelf.x,oSelf.y+oSelf.height) checkstarskey(oGame,oSelf,nValue,nRow,nCol) func callenemystate oGame for t in oGame.aObjects t { if type = GE_TYPE_ENEMY call state(oGame,t) ok } next Class GameState down = 3 gameresult = false Score = 0 startplay=false lastcol = 0 playerwin = false (continues on next page) 55.29. Super Man 2016 Game 736
781.
Ring Documentation, Release1.21.2 (continued from previous page) DoorKey = false playerindex = 4 value = 1000 moveplayer = false Screen Shot: 55.29. Super Man 2016 Game 737
782.
CHAPTER FIFTYSIX BUILDING GAMES FORANDROID In this chapter we will learn about Building RingLibSDL Games for Mobile. So we can create packages (*.apk) for the applications that are developed using Ring Game Engine for 2D Games. 56.1 Download Requirements and Update the Android SDK • The Android SDK Tools https://developer.android.com/studio/index.html • The Android NDK (Tested using android-ndk-r10c) https://developer.android.com/ndk/index.html • Apache Ant v1.8 or later http://ant.apache.org/bindownload.cgi • Java SE Development Kit (JDK) v6 or later http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html • Update the Android SDK to get the API and tools packages required for development Tested using Android 4.4.2 (API 19) • In Windows - Define the next Environment Variables based on your system. (1) JAVA_HOME For Example : C:Program Files (x86)Javajdk1.8.0_05 (2) ANDROID_HOME For Example : B:mahmoudToolsJava-Androidadt-bundle-windows-x86-20140702sdk 738
783.
Ring Documentation, Release1.21.2 56.2 Project Folder Open the project folder : ring/extensions/android/ringlibsdl/project You can add the source code (*.ring) and Images/Sound Files to the assets folder. You will find the Flappy Bird 3000 Game ready for building. The execution starts from the start.ring file load "game2.ring" 56.2. Project Folder 739
784.
Ring Documentation, Release1.21.2 56.3 Building the project Move to the ring/extensions/android/ringlibsdl/project folder We can build using the next command (We need to do this for one time only). ndk-build Then we can create the package (*.apk) using the next command. ant debug We can write a batch file for building the project (file: build.bat) rem You will need to modify this batch file based on your environment set JAVA_HOME=C:Program Files (x86)Javajdk1.8.0_05 set ANDROID_HOME=B:mahmoudToolsJavaAndroidadt-bundle-windows-x86-20140702sdk set NDK_ROOT=B:mahmoudToolsJavaAndroidandroid-ndk-r10c set path=%path%;B:mahmoudToolsJavaAndroidandroid-ndk-r10c set path=%path%;B:mahmoudToolsJavaAndroidapache-ant-1.9.4bin ndk-build 56.3. Building the project 740
785.
CHAPTER FIFTYSEVEN DEVELOPING GAMES USINGRINGRAYLIB In this chapter we will learn how to use the RingRayLib extension. 57.1 Introduction RingRayLib is an extension for the RayLib game programming library. Also RayGUI functions are supported by this extension. 57.2 Basic Window load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window") SetTargetFPS(60) while !WindowShouldClose() BeginDrawing() ClearBackground(RED) DrawText("Congrats! You created your first window!", 190, 200, 20, WHITE) EndDrawing() end CloseWindow() Screen Shot: 741
786.
Ring Documentation, Release1.21.2 57.3 Input Keys load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard input") ballPosition = Vector2(screenWidth/2, screenHeight/2) SetTargetFPS(60) while !WindowShouldClose() if IsKeyDown(KEY_RIGHT) ballPosition.x += 2 ok if IsKeyDown(KEY_LEFT) ballPosition.x -= 2 ok if IsKeyDown(KEY_UP) ballPosition.y -= 2 ok if IsKeyDown(KEY_DOWN) ballPosition.y += 2 ok BeginDrawing() ClearBackground(RAYWHITE) DrawText("move the ball with arrow keys", 10, 10, 20, DARKGRAY) DrawCircleV(ballPosition, 50, MAROON) EndDrawing() end CloseWindow() Screen Shot: 57.3. Input Keys 742
787.
Ring Documentation, Release1.21.2 57.4 Input Mouse load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - mouse input") ballPosition = Vector2(100, 100) ballColor = DARKBLUE SetTargetFPS(60) while ! WindowShouldClose() ballPosition = GetMousePosition() if IsMouseButtonPressed(MOUSE_LEFT_BUTTON) ballColor = MAROON but IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON) ballColor = LIME but IsMouseButtonPressed(MOUSE_RIGHT_BUTTON) ballColor = DARKBLUE ok BeginDrawing() (continues on next page) 57.4. Input Mouse 743
788.
Ring Documentation, Release1.21.2 (continued from previous page) ClearBackground(BLACK) DrawCircleV(ballPosition, 40, ballColor) DrawText("move ball with mouse and click mouse button to change color",␣ ˓ →10, 10, 20, YELLOW) EndDrawing() end CloseWindow() Screen Shot: 57.5 3D Camera load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera mode") camera = Camera3D( 0, 10, 10, // Camera position 0, 0, 0 , // Camera looking at point 0, 1, 0, // Camera up vector (rotation towards target) 45, // Camera field-of-view Y (continues on next page) 57.5. 3D Camera 744
789.
Ring Documentation, Release1.21.2 (continued from previous page) CAMERA_PERSPECTIVE) // Camera mode type cubePosition = Vector3(0, 0, 0) SetTargetFPS(60) while !WindowShouldClose() BeginDrawing() ClearBackground(RAYWHITE) BeginMode3D(camera) DrawCube(cubePosition, 2, 2, 2, RED) DrawCubeWires(cubePosition, 2, 2, 2, MAROON) DrawGrid(10, 1) EndMode3D() DrawText("Welcome to the third dimension!", 10, 40, 20, DARKGRAY) DrawFPS(10, 10) EndDrawing() end CloseWindow() Screen Shot: 57.5. 3D Camera 745
790.
Ring Documentation, Release1.21.2 57.6 3D Camera Free load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free") camera = Camera3D( 10, 10, 10, // Camera position 0, 0, 0 , // Camera looking at point 0, 1, 0, // Camera up vector (rotation towards target) 45, // Camera field-of-view Y CAMERA_PERSPECTIVE) // Camera mode type cubePosition = Vector3(0, 0, 0) SetTargetFPS(60) while !WindowShouldClose() UpdateCamera(camera,CAMERA_FREE) if IsKeyDown("Z") camera.target = Vector3( 0, 0, 0) ok BeginDrawing() (continues on next page) 57.6. 3D Camera Free 746
791.
Ring Documentation, Release1.21.2 (continued from previous page) ClearBackground(RAYWHITE) BeginMode3D(camera) DrawCube(cubePosition, 2, 2, 2, RED) DrawCubeWires(cubePosition, 2, 2, 2, MAROON) DrawGrid(10, 1) EndMode3D() DrawRectangle( 10, 10, 320, 133, Fade(SKYBLUE, 0.5)) DrawRectangleLines( 10, 10, 320, 133, BLUE) DrawText("Free camera default controls:", 20, 20, 10, BLACK) DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, DARKGRAY) DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, DARKGRAY) DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, DARKGRAY) DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100,␣ ˓ →10, DARKGRAY) DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, DARKGRAY) EndDrawing() end CloseWindow() Screen Shot: 57.6. 3D Camera Free 747
792.
Ring Documentation, Release1.21.2 57.7 Mouse Wheel load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - input mouse wheel") boxPositionY = screenHeight/2 - 40 scrollSpeed = 4 SetTargetFPS(60) while !WindowShouldClose() boxPositionY -= (GetMouseWheelMove()*scrollSpeed) BeginDrawing() ClearBackground(RAYWHITE) DrawRectangle(screenWidth/2 - 40, boxPositionY, 80, 80, MAROON) DrawText("Use mouse wheel to move the cube up and down!", 10, 10, 20,␣ ˓ →GRAY) DrawText("Box position Y: "+boxPositionY, 10, 40, 20, LIGHTGRAY) (continues on next page) 57.7. Mouse Wheel 748
793.
Ring Documentation, Release1.21.2 (continued from previous page) EndDrawing() end CloseWindow() Screen Shot: 57.8 Input Multi-touch load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - input multitouch") ballPosition = Vector2(-100, -100) ballColor = BEIGE touchCounter = 0 touchPosition = vector2(0,0) MAX_TOUCH_POINTS = 5 SetTargetFPS(60) (continues on next page) 57.8. Input Multi-touch 749
794.
Ring Documentation, Release1.21.2 (continued from previous page) while !WindowShouldClose() ballPosition = GetMousePosition() ballColor = BEIGE if IsMouseButtonDown(MOUSE_LEFT_BUTTON) ballColor = MAROON ok if IsMouseButtonDown(MOUSE_MIDDLE_BUTTON) ballColor = LIME ok if IsMouseButtonDown(MOUSE_RIGHT_BUTTON) ballColor = DARKBLUE ok if IsMouseButtonPressed(MOUSE_LEFT_BUTTON) touchCounter = 10 ok if IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON) touchCounter = 10 ok if IsMouseButtonPressed(MOUSE_RIGHT_BUTTON) touchCounter = 10 ok if touchCounter > 0 touchCounter-- ok BeginDrawing() ClearBackground(RAYWHITE) for i = 0 to MAX_TOUCH_POINTS-1 touchPosition = GetTouchPosition(i) if touchPosition.x >= 0 && touchPosition.y >= 0 DrawCircleV(touchPosition, 34, ORANGE) DrawText(""+ i, touchPosition.x - 10, touchPosition.y -␣ ˓ →70, 40, BLACK) ok next DrawCircleV(ballPosition, 30 + (touchCounter*3), ballColor) DrawText("move ball with mouse and click mouse button to change color",␣ ˓ →10, 10, 20, DARKGRAY) DrawText("touch the screen at multiple locations to get multiple balls",␣ ˓ →10, 30, 20, DARKGRAY) EndDrawing() end CloseWindow() Screen Shot: 57.8. Input Multi-touch 750
795.
Ring Documentation, Release1.21.2 57.9 Camera First Person load "raylib.ring" MAX_COLUMNS = 20 screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera first person") camera = Camera3d( 4, 2, 4, 0, 1, 0, 0, 1, 0, 60, CAMERA_PERSPECTIVE ) heights = list(MAX_COLUMNS) positions = list(MAX_COLUMNS) for item in positions item = vector3(0,0,0) next colors = list(MAX_COLUMNS) for item in colors item = BLACK next for i = 1 to MAX_COLUMNS heights[i] = GetRandomValue(1, 12) (continues on next page) 57.9. Camera First Person 751
796.
Ring Documentation, Release1.21.2 (continued from previous page) positions[i] = Vector3(GetRandomValue(-15, 15), heights[i]/2, GetRandomValue(-15, ˓ → 15) ) colors[i] = RAYLibColor(GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255␣ ˓ →) next SetTargetFPS(60) while !WindowShouldClose() UpdateCamera(camera,CAMERA_FIRST_PERSON) BeginDrawing() ClearBackground(RAYWHITE) BeginMode3D(camera) DrawPlane(Vector3( 0, 0, 0 ), Vector2(32, 32 ), LIGHTGRAY) // Draw ground DrawCube(Vector3( -16, 2.5, 0 ), 1, 5, 32, BLUE) // Draw a blue wall DrawCube(Vector3( 16, 2.5, 0 ), 1, 5, 32, LIME) // Draw a green wall DrawCube(Vector3( 0, 2.5, 16 ), 32, 5, 1, GOLD) // Draw a yellow␣ ˓ →wall for i = 1 to MAX_COLUMNS DrawCube(positions[i], 2, heights[i], 2, colors[i]) DrawCubeWires(positions[i], 2, heights[i], 2, MAROON) next EndMode3D() DrawRectangle( 10, 10, 220, 70, Fade(SKYBLUE, 0.5f)) DrawRectangleLines( 10, 10, 220, 70, BLUE) DrawText("First person camera default controls:", 20, 20, 10, BLACK) DrawText("- Move with keys: W, A, S, D", 40, 40, 10, DARKGRAY) DrawText("- Mouse move to look around", 40, 60, 10, DARKGRAY) EndDrawing() end CloseWindow() Screen Shot: 57.9. Camera First Person 752
797.
Ring Documentation, Release1.21.2 57.10 3D Picking load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d picking") camera = Camera3D( 10, 10, 10, 0, 0, 0 , 0, 1, 0 , 45, CAMERA_PERSPECTIVE ) cubePosition = Vector3( 0, 1, 0 ) cubeSize = Vector3( 2, 2, 2 ) ray = Ray(0,0,0,0,0,0) collision = false SetTargetFPS(60) while !WindowShouldClose() (continues on next page) 57.10. 3D Picking 753
798.
Ring Documentation, Release1.21.2 (continued from previous page) if IsMouseButtonPressed(MOUSE_LEFT_BUTTON) if !collision ray = GetMouseRay(GetMousePosition(), camera) collision = GetRayCollisionBox(ray, BoundingBox( cubePosition.x - cubeSize.x/2, cubePosition.y - cubeSize.y/ ˓ →2, cubePosition.z - cubeSize.z/2, cubePosition.x + cubeSize.x/2, cubePosition.y + cubeSize.y/2,␣ ˓ →cubePosition.z + cubeSize.z/2 ) ) collision = collision.hit else collision = false ok ok BeginDrawing() ClearBackground(RAYWHITE) BeginMode3D(camera) if collision DrawCube(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, RED) DrawCubeWires(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z,␣ ˓ →MAROON) DrawCubeWires(cubePosition, cubeSize.x + 0.2f, cubeSize.y + 0.2f, ˓ → cubeSize.z + 0.2f, GREEN) else DrawCube(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, GRAY) DrawCubeWires(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z,␣ ˓ →DARKGRAY) ok DrawRay(ray, MAROON) DrawGrid(10, 1) EndMode3D() DrawText("Try selecting the box with mouse!", 240, 10, 20, DARKGRAY) if collision DrawText("BOX SELECTED", (screenWidth - MeasureText("BOX␣ ˓ →SELECTED", 30)) / 2, screenHeight * 0.1f, 30, GREEN) ok DrawFPS(10, 10) EndDrawing() end CloseWindow() Screen Shot: 57.10. 3D Picking 754
799.
Ring Documentation, Release1.21.2 57.11 Full Screen load "raylib.ring" screenWidth = 1024 screenHeight = 768 InitWindow(screenWidth, screenHeight, "Full Screen") ToggleFullScreen() SetTargetFPS(60) while !WindowShouldClose() BeginDrawing() ClearBackground(DARKBLUE) DrawText("Count from 1 to 10", 190, 200, 20, Yellow) for t = 1 to 10 DrawText("Number: " + t, 190, 200+(30*t), 20, WHITE) next EndDrawing() end CloseWindow() Screen Shot: 57.11. Full Screen 755
800.
Ring Documentation, Release1.21.2 57.12 Two Cubes load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - Two Cubes") camera = Camera3D( 10, 10, 10, 0, 0, 0 , 0, 1, 0 , 45, CAMERA_PERSPECTIVE ) cubePosition1 = Vector3( 0, 1, 4 ) cubePosition2 = Vector3( 0, 1, -4 ) cubeSize = Vector3( 2, 2, 2 ) ray = Ray(0,0,0,0,0,0) (continues on next page) 57.12. Two Cubes 756
Ring Documentation, Release1.21.2 (continued from previous page) else DrawText("MODE: AUTO", 600, 270, 10, DARKGRAY) ok DrawFPS(10, 10) EndDrawing() end CloseWindow() Screen Shot: Screen Shot (2): 57.14. Draw Ring 762
807.
Ring Documentation, Release1.21.2 57.15 Bezier Lines load "raylib.ring" screenWidth = 800 screenHeight = 450 SetConfigFlags(FLAG_MSAA_4X_HINT) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - cubic-bezier lines") start = Vector2(0,0) endvec = Vector2(screenWidth,screenHeight) SetTargetFPS(60) while (!WindowShouldClose()) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) start = GetMousePosition() else (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) endvec = GetMousePosition() ok BeginDrawing() ClearBackground(RAYWHITE) (continues on next page) 57.15. Bezier Lines 763
808.
Ring Documentation, Release1.21.2 (continued from previous page) DrawText("USE MOUSE LEFT-RIGHT CLICK to DEFINE LINE START and END POINTS ˓ →", 15, 20, 20, GRAY) DrawLineBezier(start, endvec, 2.0, RED) EndDrawing() end CloseWindow() Screen Shot: 57.16 Collision Area load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [shapes] example - collision area") // Box A: Moving box boxA = Rectangle( 10, GetScreenHeight()/2 - 50, 200, 100 ) boxASpeedX = 4 // Box B: Mouse moved box (continues on next page) 57.16. Collision Area 764
809.
Ring Documentation, Release1.21.2 (continued from previous page) boxB = Rectangle( GetScreenWidth()/2 - 30, GetScreenHeight()/2 - 30, 60, 60 ) boxCollision = GetCollisionRec(boxA, boxB) boxCollision = Rectangle( 0,0,0,0 ) // Collision rectangle screenUpperLimit = 40 // Top menu limits pause = false // Movement pause collision = false // Collision detection SetTargetFPS(60) while !WindowShouldClose() // Move box if not paused if (not pause) boxA.x += boxASpeedX ok // Bounce box on x screen limits if (((boxA.x + boxA.width) >= GetScreenWidth()) or (boxA.x <= 0)) boxASpeedX =␣ ˓ →boxASpeedX*(-1) ok // Update player-controlled-box (box02) boxB.x = GetMouseX() - boxB.width/2 boxB.y = GetMouseY() - boxB.height/2 // Make sure Box B does not go out of move area limits if ((boxB.x + boxB.width) >= GetScreenWidth()) boxB.x = GetScreenWidth() - boxB. ˓ →width else (boxB.x <= 0) boxB.x = 0 ok if ((boxB.y + boxB.height) >= GetScreenHeight()) boxB.y = GetScreenHeight() -␣ ˓ →boxB.height else (boxB.y <= screenUpperLimit) boxB.y = screenUpperLimit ok // Check boxes collision collision = CheckCollisionRecs(boxA, boxB) // Get collision rectangle (only on collision) if (collision) boxCollision = GetCollisionRec(boxA, boxB) ok // Pause Box A movement if (IsKeyPressed(KEY_SPACE)) pause = not pause ok BeginDrawing() ClearBackground(RAYWHITE) if collision = true color = RED else color = BLACK (continues on next page) 57.16. Collision Area 765
Ring Documentation, Release1.21.2 (continued from previous page) // Check not inside the left eye sclera if !CheckCollisionPointCircle(irisLeftPosition, scleraLeftPosition, scleraRadius␣ ˓ →- 20) dx = irisLeftPosition.x - scleraLeftPosition.x dy = irisLeftPosition.y - scleraLeftPosition.y angle = atan2(dy, dx) dxx = (scleraRadius - irisRadius)*cos(angle) dyy = (scleraRadius - irisRadius)*sin(angle) irisLeftPosition.x = scleraLeftPosition.x + dxx irisLeftPosition.y = scleraLeftPosition.y + dyy ok // Check not inside the right eye sclera if !CheckCollisionPointCircle(irisRightPosition, scleraRightPosition,␣ ˓ →scleraRadius - 20) dx = irisRightPosition.x - scleraRightPosition.x dy = irisRightPosition.y - scleraRightPosition.y angle = atan2(dy, dx) dxx = (scleraRadius - irisRadius)*cos(angle) dyy = (scleraRadius - irisRadius)*sin(angle) irisRightPosition.x = scleraRightPosition.x + dxx irisRightPosition.y = scleraRightPosition.y + dyy ok BeginDrawing() ClearBackground(RAYWHITE) DrawCircleV(scleraLeftPosition, scleraRadius, LIGHTGRAY) DrawCircleV(irisLeftPosition, irisRadius, BROWN) DrawCircleV(irisLeftPosition, 10, BLACK) DrawCircleV(scleraRightPosition, scleraRadius, LIGHTGRAY) DrawCircleV(irisRightPosition, irisRadius, DARKGREEN) DrawCircleV(irisRightPosition, 10, BLACK) DrawFPS(10, 10) EndDrawing() end CloseWindow() Screen Shot: 57.17. Following Eyes 768
813.
Ring Documentation, Release1.21.2 57.18 Colors Palette load "raylib.ring" MAX_COLORS_COUNT = 21 // Number of colors available screenWidth = 800 screenHeight = 450 colors = list(MAX_COLORS_COUNT) colorNames = list(MAX_COLORS_COUNT) colorsRecs = list(MAX_COLORS_COUNT) colorState = list(MAX_COLORS_COUNT) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - colors palette") colors = [ DARKGRAY, MAROON, ORANGE, DARKGREEN, DARKBLUE, DARKPURPLE, DARKBROWN, GRAY, RED, GOLD, LIME, BLUE, VIOLET, BROWN, LIGHTGRAY, PINK, YELLOW, GREEN, SKYBLUE, PURPLE, BEIGE ] colorNames = [ "DARKGRAY", "MAROON", "ORANGE", "DARKGREEN", "DARKBLUE", "DARKPURPLE", "DARKBROWN", "GRAY", "RED", "GOLD", "LIME", "BLUE", "VIOLET", "BROWN", "LIGHTGRAY", "PINK", "YELLOW", "GREEN", "SKYBLUE", "PURPLE", "BEIGE" ] for i = 1 to MAX_COLORS_COUNT colorsRecs[i] = new Rectangle(0,0,0,0) (continues on next page) 57.18. Colors Palette 769
814.
Ring Documentation, Release1.21.2 (continued from previous page) next for i = 1 to MAX_COLORS_COUNT colorState[i] = 0 next // Fills colorsRecs data (for every rectangle) for i = 1 to MAX_COLORS_COUNT colorsRecs[i].x = 20 + 100*((i-1)%7) + 10*((i-1)%7) colorsRecs[i].y = 80 + 100*floor((i-1)/7) + 10*floor((i-1)/7) colorsRecs[i].width = 100 colorsRecs[i].height = 100 next mousePoint = Vector2( 0.0, 0.0 ) SetTargetFPS(60) // Main game loop while !WindowShouldClose() mousePoint = GetMousePosition() for i = 1 to MAX_COLORS_COUNT if (CheckCollisionPointRec(mousePoint, colorsRecs[i])) colorState[i] = 1 else colorState[i] = 0 ok next BeginDrawing() ClearBackground(RAYWHITE) DrawText("raylib colors palette", 28, 42, 20, BLACK) DrawText("press SPACE to see all colors", GetScreenWidth() - 180,␣ ˓ →GetScreenHeight() - 40, 10, GRAY) for i = 1 to MAX_COLORS_COUNT // Draw all rectangles if colorState[i] cstate = 0.6 else cstate = 1.0 ok DrawRectangleRec(colorsRecs[i], Fade(colors[i], cstate)) if (IsKeyDown(KEY_SPACE) || colorState[i]) DrawRectangle(colorsRecs[i].x, colorsRecs[i].y +␣ ˓ →colorsRecs[i].height - 26, colorsRecs[i].width, 20, BLACK) DrawRectangleLinesEx(colorsRecs[i], 6, Fade(BLACK, 0.3f)) DrawText(colorNames[i], colorsRecs[i].x + colorsRecs[i]. ˓ →width - MeasureText(colorNames[i], 10) - 12, (continues on next page) 57.18. Colors Palette 770
815.
Ring Documentation, Release1.21.2 (continued from previous page) colorsRecs[i].y + colorsRecs[i].height - 20, 10,␣ ˓ →colors[i]) ok next EndDrawing() end CloseWindow() Screen Shot: 57.19 Rectangle Scaling load "raylib.ring" MOUSE_SCALE_MARK_SIZE = 12 screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle scaling mouse ˓ →") rec = Rectangle( 100, 100, 200, 80 ) (continues on next page) 57.19. Rectangle Scaling 771
816.
Ring Documentation, Release1.21.2 (continued from previous page) mousePosition = Vector2( 0,0 ) mouseScaleReady = false mouseScaleMode = false SetTargetFPS(60) while !WindowShouldClose() mousePosition = GetMousePosition() if (CheckCollisionPointRec(mousePosition, rec) and CheckCollisionPointRec(mousePosition, Rectangle(rec.x + rec.width -␣ ˓ →MOUSE_SCALE_MARK_SIZE, rec.y + rec.height - MOUSE_SCALE_MARK_SIZE, MOUSE_SCALE_MARK_ ˓ →SIZE, MOUSE_SCALE_MARK_SIZE ))) mouseScaleReady = true if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) mouseScaleMode = true ok else mouseScaleReady = false ok if (mouseScaleMode) mouseScaleReady = true rec.width = (mousePosition.x - rec.x) rec.height = (mousePosition.y - rec.y) if (rec.width < MOUSE_SCALE_MARK_SIZE) rec.width = MOUSE_SCALE_MARK_SIZE␣ ˓ →ok if (rec.height < MOUSE_SCALE_MARK_SIZE) rec.height = MOUSE_SCALE_MARK_ ˓ →SIZE ok if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) mouseScaleMode = false ok ok BeginDrawing() ClearBackground(RAYWHITE) DrawText("Scale rectangle dragging from bottom-right corner!", 10, 10,␣ ˓ →20, GRAY) DrawRectangleRec(rec, Fade(GREEN, 0.5f)) if (mouseScaleReady) DrawRectangleLinesEx(rec, 1, RED) DrawTriangle(Vector2( rec.x + rec.width - MOUSE_SCALE_MARK_SIZE,␣ ˓ →rec.y + rec.height ), Vector2( rec.x + rec.width, rec.y + rec.height ), Vector2( rec.x + rec.width, rec.y + rec.height - MOUSE_SCALE_ ˓ →MARK_SIZE ), RED) (continues on next page) 57.19. Rectangle Scaling 772
817.
Ring Documentation, Release1.21.2 (continued from previous page) ok EndDrawing() end CloseWindow() Screen Shot: 57.20 Music Playing load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [audio] example - music playing (streaming) ˓ →") InitAudioDevice() music = LoadMusicStream("guitar_noodling.ogg") PlayMusicStream(music) (continues on next page) 57.20. Music Playing 773
818.
Ring Documentation, Release1.21.2 (continued from previous page) timePlayed = 0.0 pause = false SetTargetFPS(60) while !WindowShouldClose() UpdateMusicStream(music) if IsKeyPressed(KEY_SPACE) StopMusicStream(music) PlayMusicStream(music) ok if IsKeyPressed(KEY_P) pause = !pause if pause PauseMusicStream(music) else ResumeMusicStream(music) ok ok timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) *400 if timePlayed > 400 StopMusicStream(music) ok BeginDrawing() ClearBackground(RAYWHITE) DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, LIGHTGRAY) DrawRectangle(200, 200, 400, 12, LIGHTGRAY) DrawRectangle(200, 200, timePlayed, 12, MAROON) DrawRectangleLines(200, 200, 400, 12, GRAY) DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, LIGHTGRAY) DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, LIGHTGRAY) EndDrawing() end UnloadMusicStream(music) CloseAudioDevice() (continues on next page) 57.20. Music Playing 774
819.
Ring Documentation, Release1.21.2 (continued from previous page) CloseWindow() Screen Shot: 57.21 Sound Loading load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading and playing ˓ →") InitAudioDevice() fxWav = LoadSound("sound.wav") fxOgg = LoadSound("tanatana.ogg") SetTargetFPS(60) while !WindowShouldClose() if IsKeyPressed(KEY_SPACE) PlaySound(fxWav) ok if IsKeyPressed(KEY_ENTER) PlaySound(fxOgg) ok (continues on next page) 57.21. Sound Loading 775
820.
Ring Documentation, Release1.21.2 (continued from previous page) BeginDrawing() ClearBackground(RAYWHITE) DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, LIGHTGRAY) DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, LIGHTGRAY) EndDrawing() end UnloadSound(fxWav) UnloadSound(fxOgg) CloseAudioDevice() CloseWindow() Screen Shot: 57.21. Sound Loading 776
821.
Ring Documentation, Release1.21.2 57.22 Image Drawing load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [textures] example - image drawing") cat = LoadImage("cat.png") ImageCrop( cat, Rectangle( 100, 10, 280, 380 )) ImageFlipHorizontal( cat) ImageResize( cat, 150, 200) parrots = LoadImage("parrots.png") ImageDraw( parrots, cat, Rectangle( 0, 0, cat.width, cat.height ), Rectangle( 30, 40,␣ ˓ →cat.width*1.5, cat.height*1.5 )) ImageCrop( parrots, Rectangle( 0, 50, parrots.width, parrots.height - 100 )) UnloadImage(cat) font = LoadFont("custom_jupiter_crash.png") ImageDrawTextEx(parrots, Vector2( 300, 230 ), font, "PARROTS & CAT", font.baseSize, -2,␣ ˓ →WHITE) UnloadFont(font); texture = LoadTextureFromImage(parrots) UnloadImage(parrots) SetTargetFPS(60) while !WindowShouldClose() BeginDrawing() ClearBackground(RAYWHITE) DrawTexture(texture, screenWidth/2 - texture.width/2, screenHeight/2 -␣ ˓ →texture.height/2 - 40, WHITE) DrawRectangleLines(screenWidth/2 - texture.width/2, screenHeight/2 -␣ ˓ →texture.height/2 - 40, texture.width, texture.height, DARKGRAY) DrawText("We are drawing only one texture from various images composed!", ˓ → 240, 350, 10, DARKGRAY) DrawText("Source images have been cropped, scaled, flipped and copied␣ ˓ →one over the other.", 190, 370, 10, DARKGRAY) EndDrawing() end (continues on next page) 57.22. Image Drawing 777
Ring Documentation, Release1.21.2 (continued from previous page) on 4 DrawText("RADIAL GRADIENT", 580, 10, 20, LIGHTGRAY) on 5 DrawText("SQUARE GRADIENT", 580, 10, 20, LIGHTGRAY) on 6 DrawText("CHECKED", 680, 10, 20, RAYWHITE) on 7 DrawText("WHITE NOISE", 640, 10, 20, RED) on 8 DrawText("PERLIN NOISE", 630, 10, 20, RAYWHITE) on 9 DrawText("CELLULAR", 670, 10, 20, RAYWHITE) off EndDrawing() end for i = 1 to NUM_TEXTURES UnloadTexture( textures[i] ) next CloseWindow() Screen Shot: 57.23. Image Generation 780
825.
Ring Documentation, Release1.21.2 57.24 Texture Source load "raylib.ring" screenWidth = 800 screenHeight = 600 InitWindow(screenWidth, screenHeight, "raylib [textures] examples - texture source and␣ ˓ →destination rectangles") // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) scarfy = LoadTexture("RingLogo.png") // Texture loading frameWidth = scarfy.width frameHeight = scarfy.height // Source rectangle (part of the texture to use for drawing) sourceRec = Rectangle( 0.0, 0.0, frameWidth, frameHeight ) // Destination rectangle (screen rectangle where drawing part of texture) destRec = Rectangle( screenWidth/2, screenHeight/2, frameWidth*2, frameHeight*2 ) // Origin of the texture (rotation/scale point), it's relative to destination rectangle␣ ˓ →size origin = Vector2( frameWidth, frameHeight ) rotation = 0 SetTargetFPS(60) while !WindowShouldClose() rotation = rotation+1 BeginDrawing() ClearBackground(RAYWHITE) // NOTE: Using DrawTexturePro() we can easily rotate and scale the part␣ ˓ →of the texture we draw // sourceRec defines the part of the texture we use for drawing // destRec defines the rectangle where our texture part will fit␣ ˓ →(scaling it to fit) // origin defines the point of the texture used as reference for␣ ˓ →rotation and scaling // rotation defines the texture rotation (using origin as rotation point) DrawTexturePro(scarfy, sourceRec, destRec, origin, rotation, WHITE) DrawLine(destRec.x, 0, destRec.x, screenHeight, GRAY) DrawLine(0, destRec.y, screenWidth, destRec.y, GRAY) (continues on next page) 57.24. Texture Source 781
826.
Ring Documentation, Release1.21.2 (continued from previous page) DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200,␣ ˓ →screenHeight - 20, 10, GRAY) EndDrawing() end UnloadTexture(scarfy) // Texture unloading CloseWindow() Screen Shot: 57.24. Texture Source 782
827.
Ring Documentation, Release1.21.2 57.25 Geometric Shapes load "raylib.ring" FOVY_PERSPECTIVE = 45.0 WIDTH_ORTHOGRAPHIC = 10.0 screenWidth = 800 screenHeight = 450 (continues on next page) 57.25. Geometric Shapes 783
Ring Documentation, Release1.21.2 (continued from previous page) EndDrawing() end UnloadTexture(cubicmap) UnloadTexture(texture) UnloadModel(model) CloseWindow() Screen Shot: 57.27 Implementation The extension exist in the ring/extension/ringraylib5 folder The supported functions are defined in the ring/extensions/ringraylib5/src/raylib.cf file The samples exist in the ring/samples/UsingRayLib folder 57.27. Implementation 787
832.
CHAPTER FIFTYEIGHT USING RINGOPENGL ANDRINGFREEGLUT FOR 3D GRAPHICS In this chapter we will learn about using RingOpenGL 58.1 Samples Source (Authors) The samples in this chapter are based on C Tutorials from (1) http://www.lighthouse3d.com/tutorials/glut-tutorial/ (2) http://www.wikihow.com/Make-a-Cube-in-OpenGL 58.2 What is RingOpenGL? RingOpenGL contains the Ring binding to the OpenGL library You can learn about OpenGL from : https://www.opengl.org/ RingOpenGL comes with support for the next versions • OpenGL 1.1 • OpenGL 1.2 • OpenGL 1.3 • OpenGL 1.4 • OpenGL 1.5 • OpenGL 2.0 • OpenGL 2.1 • OpenGL 3.0 • OpenGL 3.2 • OpenGL 3.3 • OpenGL 4.0 • OpenGL 4.1 • OpenGL 4.2 • OpenGL 4.3 788
833.
Ring Documentation, Release1.21.2 • OpenGL 4.4 • OpenGL 4.5 • OpenGL 4.6 For example, if you want to use OpenGL 2.1 then load RingOpenGL 2.1 library load "opengl21lib.ring" 58.3 What is RingFreeGLUT? RingFreeGLUT contains the Ring binding to the FreeGLUT library You can learn about FreeGLUT from : http://freeglut.sourceforge.net/ To use the RingFreeGLUT library, Just load the library load "freeglut.ring" 58.4 The First Window using RingFreeGLUT Example: load "freeglut.ring" func main glutInit() glutInitDisplayMode(GLUT_SINGLE) glutInitWindowSize(800, 600) glutInitWindowPosition(100, 10) glutCreateWindow("RingFreeGLUT - Test 1") glutDisplayFunc(:displayCode) glutMainLoop() func displaycode Screen Shot 58.3. What is RingFreeGLUT? 789
834.
Ring Documentation, Release1.21.2 58.5 Drawing using RingOpenGL Example: load "freeglut.ring" load "opengl21lib.ring" func main glutInit() glutInitDisplayMode(GLUT_SINGLE) glutInitWindowSize(800, 600) glutInitWindowPosition(100, 10) glutCreateWindow("RingFreeGLUT - Test 2") glutDisplayFunc(:displayCode) glutMainLoop() func displaycode glClear(GL_COLOR_BUFFER_BIT) glColor3f(0,255,0) (continues on next page) 58.5. Drawing using RingOpenGL 790
Ring Documentation, Release1.21.2 58.6 The First Triangle Example: load "freeglut.ring" load "opengl21lib.ring" func main glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowSize(320,320) glutInitWindowPosition(100, 10) glutCreateWindow("RingFreeGLUT - Test 3") glutDisplayFunc(:renderScene) glutMainLoop() func renderScene glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) (continues on next page) 58.6. The First Triangle 792
837.
Ring Documentation, Release1.21.2 (continued from previous page) glBegin(GL_TRIANGLES) glVertex3f(-0.5,-0.5,0.0) glVertex3f(0.5,0.0,0.0) glVertex3f(0.0,0.5,0.0) glEnd() glutSwapBuffers() Screen Shot 58.7 Window Resize Event Example: load "freeglut.ring" load "opengl21lib.ring" func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test 4") glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) (continues on next page) 58.7. Window Resize Event 793
838.
Ring Documentation, Release1.21.2 (continued from previous page) glutMainLoop() func renderScene glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glBegin(GL_TRIANGLES) glVertex3f(-2,-2,-5.0) glVertex3f(2,0.0,-5.0) glVertex3f(0.0,2,-5.0) glEnd() glutSwapBuffers() func changesize h = glutEventHeight() w = glutEventWidth() // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if (h = 0) h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. gluPerspective(45,ratio,1,100) // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) 58.7. Window Resize Event 794
839.
Ring Documentation, Release1.21.2 58.8 Triangle Rotation Example: load "freeglut.ring" load "opengl21lib.ring" angle = 0 func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test 5") glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) glutIdleFunc(:renderScene) glutMainLoop() func renderScene // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Set the camera gluLookAt( 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) glRotatef(angle, 0.0, 1.0, 0.0) glBegin(GL_TRIANGLES) glVertex3f(-2.0,-2.0, 0.0) glVertex3f( 2.0, 0.0, 0.0) glVertex3f( 0.0, 2.0, 0.0) glEnd() angle+=0.1 glutSwapBuffers(); func changesize h = glutEventHeight() w = glutEventWidth() (continues on next page) 58.8. Triangle Rotation 795
840.
Ring Documentation, Release1.21.2 (continued from previous page) // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if (h = 0) h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. gluPerspective(45,ratio,1,100) // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) Screen Shot 58.8. Triangle Rotation 796
841.
Ring Documentation, Release1.21.2 58.9 Keyboard Events and Colors Example: load "freeglut.ring" load "opengl21lib.ring" angle = 0 red=1.0 blue=1.0 green=1.0 func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test 6") glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) glutIdleFunc(:renderScene) // here are the new entries glutKeyboardFunc(:processNormalKeys) glutSpecialFunc(:processSpecialKeys) glutMainLoop() func renderScene // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Set the camera gluLookAt( 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) glRotatef(angle, 0.0, 1.0, 0.0) glColor3f(red,green,blue); glBegin(GL_TRIANGLES) glVertex3f(-2.0,-2.0, 0.0) glVertex3f( 2.0, 0.0, 0.0) glVertex3f( 0.0, 2.0, 0.0) (continues on next page) 58.9. Keyboard Events and Colors 797
842.
Ring Documentation, Release1.21.2 (continued from previous page) glEnd() angle+=0.1 glutSwapBuffers(); func changesize h = glutEventHeight() w = glutEventWidth() // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if (h = 0) h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. gluPerspective(45,ratio,1,100) // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) func processNormalKeys key = GLUTEventKey() if key = 27 shutdown() ok func processSpecialKeys key = GLUTEventKey() switch key on GLUT_KEY_F1 red = 1.0 green = 0.0 blue = 0.0 on GLUT_KEY_F2 red = 0.0 green = 1.0 (continues on next page) 58.9. Keyboard Events and Colors 798
843.
Ring Documentation, Release1.21.2 (continued from previous page) blue = 0.0 on GLUT_KEY_F3 red = 0.0 green = 0.0 blue = 1.0 off Screen Shot 58.10 The Camera Example: load "freeglut.ring" load "opengl21lib.ring" // angle of rotation for the camera direction angle=0.0 // actual vector representing the camera's direction lx=0.0 lz=-1.0 // XZ position of the camera x=0.0 z=5.0 func drawSnowMan glColor3f(1.0, 1.0, 1.0) (continues on next page) 58.10. The Camera 799
844.
Ring Documentation, Release1.21.2 (continued from previous page) // Draw Body glTranslatef(0.0 ,0.75, 0.0) glutSolidSphere(0.75,20,20) // Draw Head glTranslatef(0.0, 1.0, 0.0) glutSolidSphere(0.25,20,20) // Draw Eyes glPushMatrix() glColor3f(0.0,0.0,0.0) glTranslatef(0.05, 0.10, 0.18) glutSolidSphere(0.05,10,10) glTranslatef(-0.1, 0.0, 0.0) glutSolidSphere(0.05,10,10) glPopMatrix() // Draw Nose glColor3f(1.0, 0.5 , 0.5) glutSolidCone(0.08,0.5,10,2) func changeSize w = glutEventWidth() h = glutEventHeight() // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if h = 0 h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. gluPerspective(45.0, ratio, 0.1, 100.0); // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) (continues on next page) 58.10. The Camera 800
845.
Ring Documentation, Release1.21.2 (continued from previous page) func processNormalKeys key = glutEventKey() if key = 27 shutdown() ok func renderScene // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Set the camera gluLookAt( x, 1.0, z, x+lx, 1.0, z+lz, 0.0, 1.0, 0.0) // Draw ground glColor3f(0.9, 0.9, 0.9) glBegin(GL_QUADS) glVertex3f(-100.0, 0.0, -100.0) glVertex3f(-100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, -100.0) glEnd() // Draw 36 SnowMen for i = -3 to 2 for j=-3 to 2 glPushMatrix() glTranslatef(i*10.0,0,j * 10.0) drawSnowMan() glPopMatrix() next next glutSwapBuffers() func processSpecialKeys key = glutEventKey() fraction = 0.1 (continues on next page) 58.10. The Camera 801
846.
Ring Documentation, Release1.21.2 (continued from previous page) switch key on GLUT_KEY_LEFT angle -= 0.01 lx = sin(angle) lz = -cos(angle) on GLUT_KEY_RIGHT angle += 0.01 lx = sin(angle) lz = -cos(angle) on GLUT_KEY_UP x += lx * fraction z += lz * fraction on GLUT_KEY_DOWN x -= lx * fraction z -= lz * fraction off func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test 7") // register callbacks glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) glutIdleFunc(:renderScene) glutKeyboardFunc(:processNormalKeys) glutSpecialFunc(:processSpecialKeys) // OpenGL init glEnable(GL_DEPTH_TEST) // enter GLUT event processing cycle glutMainLoop() Screen Shot 58.10. The Camera 802
847.
Ring Documentation, Release1.21.2 Another Example: load "freeglut.ring" load "opengl21lib.ring" // angle of rotation for the camera direction angle = 0.0 // actual vector representing the camera's direction lx=0.0 lz=-1.0 // XZ position of the camera x=0.0 z=5.0 // the key states. These variables will be zero //when no key is being presses deltaAngle = 0.0 deltaMove = 0 func changeSize w = glutEventWidth() h = glutEventHeight() // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if h = 0 h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) (continues on next page) 58.10. The Camera 803
848.
Ring Documentation, Release1.21.2 (continued from previous page) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. gluPerspective(45.0, ratio, 0.1, 100.0) // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) func drawSnowMan glColor3f(1.0, 1.0, 1.0) // Draw Body glTranslatef(0.0 ,0.75, 0.0) glutSolidSphere(0.75,20,20) // Draw Head glTranslatef(0.0, 1.0, 0.0) glutSolidSphere(0.25,20,20) // Draw Eyes glPushMatrix() glColor3f(0.0,0.0,0.0) glTranslatef(0.05, 0.10, 0.18) glutSolidSphere(0.05,10,10) glTranslatef(-0.1, 0.0, 0.0) glutSolidSphere(0.05,10,10) glPopMatrix() // Draw Nose glColor3f(1.0, 0.5 , 0.5) glRotatef(0.0,1.0, 0.0, 0.0) glutSolidCone(0.08,0.5,10,2) func computePos deltaMove x += deltaMove * lx * 0.1 z += deltaMove * lz * 0.1 func computeDir deltaAngle angle += deltaAngle lx = sin(angle) (continues on next page) 58.10. The Camera 804
849.
Ring Documentation, Release1.21.2 (continued from previous page) lz = -cos(angle) func renderScene if deltaMove computePos(deltaMove) ok if deltaAngle computeDir(deltaAngle) ok // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Set the camera gluLookAt( x, 1.0, z, x+lx, 1.0, z+lz, 0.0, 1.0, 0.0) // Draw ground glColor3f(0.9, 0.9, 0.9) glBegin(GL_QUADS) glVertex3f(-100.0, 0.0, -100.0) glVertex3f(-100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, -100.0) glEnd() // Draw 36 SnowMen for i = -3 to 2 for j=-3 to 2 glPushMatrix() glTranslatef(i*10.0,0,j * 10.0) drawSnowMan() glPopMatrix() next next glutSwapBuffers() func pressKey key = glutEventKey() xx = glutEventX() yy = glutEventY() switch key on GLUT_KEY_LEFT (continues on next page) 58.10. The Camera 805
850.
Ring Documentation, Release1.21.2 (continued from previous page) deltaAngle = -0.01 on GLUT_KEY_RIGHT deltaAngle = 0.01 on GLUT_KEY_UP deltaMove = 0.5 on GLUT_KEY_DOWN deltaMove = -0.5 off func releaseKey key = glutEventKey() switch key on GLUT_KEY_LEFT deltaAngle = 0.0 on GLUT_KEY_RIGHT deltaAngle = 0.0 on GLUT_KEY_UP deltaMove = 0 on GLUT_KEY_DOWN deltaMove = 0 off func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test 8") // register callbacks glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) glutIdleFunc(:renderScene) glutSpecialFunc(:pressKey) // here are the new entries glutIgnoreKeyRepeat(1) glutSpecialUpFunc(:releaseKey) // OpenGL init glEnable(GL_DEPTH_TEST) // enter GLUT event processing cycle glutMainLoop() 58.10. The Camera 806
851.
Ring Documentation, Release1.21.2 58.11 Mouse Events Example: load "freeglut.ring" load "opengl21lib.ring" // angle of rotation for the camera direction angle = 0.0 // actual vector representing the camera's direction lx=0.0 lz=-1.0 // XZ position of the camera x=0.0 z=5.0 // the key states. These variables will be zero //when no key is being presses deltaAngle = 0.0 deltaMove = 0.0 xOrigin = -1 func changeSize w = glutEventWidth() h = glutEventHeight() // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if h = 0 h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. gluPerspective(45.0, ratio, 0.1, 100.0) // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) func drawSnowMan glColor3f(1.0, 1.0, 1.0) (continues on next page) 58.11. Mouse Events 807
852.
Ring Documentation, Release1.21.2 (continued from previous page) // Draw Body glTranslatef(0.0 ,0.75, 0.0) glutSolidSphere(0.75,20,20) // Draw Head glTranslatef(0.0, 1.0, 0.0) glutSolidSphere(0.25,20,20) // Draw Eyes glPushMatrix() glColor3f(0.0,0.0,0.0) glTranslatef(0.05, 0.10, 0.18) glutSolidSphere(0.05,10,10) glTranslatef(-0.1, 0.0, 0.0) glutSolidSphere(0.05,10,10) glPopMatrix() // Draw Nose glColor3f(1.0, 0.5 , 0.5) glRotatef(0.0,1.0, 0.0, 0.0) glutSolidCone(0.08,0.5,10,2) func computePos deltaMove x += deltaMove * lx * 0.1 z += deltaMove * lz * 0.1 func renderScene if deltaMove computePos(deltaMove) ok // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Set the camera gluLookAt( x, 1.0, z, x+lx, 1.0, z+lz, 0.0, 1.0, 0.0) // Draw ground glColor3f(0.9, 0.9, 0.9) glBegin(GL_QUADS) glVertex3f(-100.0, 0.0, -100.0) glVertex3f(-100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, 100.0) (continues on next page) 58.11. Mouse Events 808
853.
Ring Documentation, Release1.21.2 (continued from previous page) glVertex3f( 100.0, 0.0, -100.0) glEnd() // Draw 36 SnowMen for i = -3 to 2 for j=-3 to 2 glPushMatrix() glTranslatef(i*10.0,0,j * 10.0) drawSnowMan() glPopMatrix() next next glutSwapBuffers() func processNormalKeys key = glutEventKey() if key = 27 shutdown() ok func pressKey key = glutEventKey() switch key on GLUT_KEY_UP deltaMove = 0.5 on GLUT_KEY_DOWN deltaMove = -0.5 off func releaseKey key = glutEventKey() switch key on GLUT_KEY_UP deltaMove = 0 on GLUT_KEY_DOWN deltaMove = 0 off func mouseMove xx = glutEventX() yy = glutEventY() // this will only be true when the left button is down if xOrigin >= 0 // update deltaAngle (continues on next page) 58.11. Mouse Events 809
854.
Ring Documentation, Release1.21.2 (continued from previous page) deltaAngle = (xx - xOrigin) * 0.001 // update camera's direction lx = sin(angle + deltaAngle) lz = -cos(angle + deltaAngle) ok func mouseButton button = glutEventButton() state = glutEventState() xx = glutEventX() yy = glutEventY() // only start motion if the left button is pressed if button = GLUT_LEFT_BUTTON // when the button is released if state = GLUT_UP angle += deltaAngle xOrigin = -1 else // state = GLUT_DOWN xOrigin = xx ok fflush(stdout) ok func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test 9") // register callbacks glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) glutIdleFunc(:renderScene) glutIgnoreKeyRepeat(1) glutKeyboardFunc(:processNormalKeys) glutSpecialFunc(:pressKey) glutSpecialUpFunc(:releaseKey) // here are the two new functions glutMouseFunc(:mouseButton) glutMotionFunc(:mouseMove) (continues on next page) 58.11. Mouse Events 810
855.
Ring Documentation, Release1.21.2 (continued from previous page) // OpenGL init glEnable(GL_DEPTH_TEST) // enter GLUT event processing cycle glutMainLoop() 58.12 Menu Events Example: load "freeglut.ring" load "opengl21lib.ring" // angle of rotation for the camera direction angle = 0.0 // actual vector representing the camera's direction lx=0.0 lz=-1.0 // XZ position of the camera x=0.0 z=5.0 // the key states. These variables will be zero //when no key is being presses deltaAngle = 0.0 deltaMove = 0 xOrigin = -1 // Constant definitions for Menus // for RingFreeGLUT - We must have different ID for each menu item C_RED = 1 C_GREEN = 2 C_BLUE = 3 C_ORANGE = 4 C_FILL = 5 C_LINE = 6 C_SHRINK = 7 C_NORMAL = 8 // Pop up menu identifiers fillMenu= 0 shrinkMenu= 0 mainMenu=0 colorMenu=0 (continues on next page) 58.12. Menu Events 811
856.
Ring Documentation, Release1.21.2 (continued from previous page) // color for the nose red = 1.0 blue=0.5 green=0.5 // scale of snowman scale = 1.0 // menu status menuFlag = 0 func changeSize w = glutEventWidth() h = glutEventHeight() // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if h = 0 h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. gluPerspective(45.0, ratio, 0.1, 100.0) // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) func drawSnowMan glScalef(scale, scale, scale) glColor3f(1.0, 1.0, 1.0) // Draw Body glTranslatef(0.0 ,0.75, 0.0) glutSolidSphere(0.75,20,20) // Draw Head glTranslatef(0.0, 1.0, 0.0) glutSolidSphere(0.25,20,20) // Draw Eyes glPushMatrix() glColor3f(0.0,0.0,0.0) (continues on next page) 58.12. Menu Events 812
857.
Ring Documentation, Release1.21.2 (continued from previous page) glTranslatef(0.05, 0.10, 0.18) glutSolidSphere(0.05,10,10) glTranslatef(-0.1, 0.0, 0.0) glutSolidSphere(0.05,10,10) glPopMatrix() // Draw Nose glColor3f(red, green, blue) glRotatef(0.0,1.0, 0.0, 0.0) glutSolidCone(0.08,0.5,10,2) glColor3f(1.0, 1.0, 1.0) func computePos deltaMove x += deltaMove * lx * 0.1 z += deltaMove * lz * 0.1 func renderScene if deltaMove computePos(deltaMove) ok // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Set the camera gluLookAt( x, 1.0, z, x+lx, 1.0, z+lz, 0.0, 1.0, 0.0) // Draw ground glColor3f(0.9, 0.9, 0.9) glBegin(GL_QUADS) glVertex3f(-100.0, 0.0, -100.0) glVertex3f(-100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, -100.0) glEnd() // Draw 36 SnowMen for i = -3 to 2 for j = -3 to 2 glPushMatrix() glTranslatef(i*10.0, 0.0, j * 10.0) (continues on next page) 58.12. Menu Events 813
858.
Ring Documentation, Release1.21.2 (continued from previous page) drawSnowMan() glPopMatrix() next next glutSwapBuffers() // ----------------------------------- // KEYBOARD // ----------------------------------- func processNormalKeys key = glutEventKey() xx = glutEventX() yy = glutEventY() glutSetMenu(mainMenu) switch key on 27 glutDestroyMenu(mainMenu) glutDestroyMenu(fillMenu) glutDestroyMenu(colorMenu) glutDestroyMenu(shrinkMenu) shutdown() on 's' if not menuFlag glutChangeToSubMenu(2,"Shrink",shrinkMenu) ok on 'c' if not menuFlag glutChangeToSubMenu(2,"Color",colorMenu) ok off if key = 27 shutdown() ok func pressKey key = glutEventKey() xx = glutEventX() yy = glutEventY() switch key on GLUT_KEY_UP deltaMove = 0.5 on GLUT_KEY_DOWN deltaMove = -0.5 off (continues on next page) 58.12. Menu Events 814
859.
Ring Documentation, Release1.21.2 (continued from previous page) func releaseKey key = glutEventKey() switch key on GLUT_KEY_UP deltaMove = 0 on GLUT_KEY_DOWN deltaMove = 0 off // ----------------------------------- // MOUSE // ----------------------------------- func mouseMove xx = glutEventX() yy = glutEventY() // this will only be true when the left button is down if xOrigin >= 0 // update deltaAngle deltaAngle = (xx - xOrigin) * 0.001 // update camera's direction lx = sin(angle + deltaAngle) lz = -cos(angle + deltaAngle) ok func mouseButton button = glutEventButton() state = glutEventState() xx = glutEventX() yy = glutEventY() // only start motion if the left button is pressed if button = GLUT_LEFT_BUTTON // when the button is released if state = GLUT_UP angle += deltaAngle xOrigin = -1 else // state = GLUT_DOWN xOrigin = xx ok ok (continues on next page) 58.12. Menu Events 815
860.
Ring Documentation, Release1.21.2 (continued from previous page) // ----------------------------------- // MENUS // ----------------------------------- func processMenuStatus status = glutEventStatus() xx = glutEventX() yy = glutEventY() if status = GLUT_MENU_IN_USE menuFlag = 1 else menuFlag = 0 ok func processMainMenu // nothing to do in here // all actions are for submenus func processFillMenu option = glutEventValue() switch option on C_FILL glPolygonMode(GL_FRONT, GL_FILL) on C_LINE glPolygonMode(GL_FRONT, GL_LINE) off func processShrinkMenu option = glutEventValue() switch option on C_SHRINK scale = 0.5 on C_NORMAL scale = 1.0 off func processColorMenu (continues on next page) 58.12. Menu Events 816
861.
Ring Documentation, Release1.21.2 (continued from previous page) option = glutEventValue() switch option on C_RED red = 1.0 green = 0.0 blue = 0.0 on C_GREEN red = 0.0 green = 1.0 blue = 0.0 on C_BLUE red = 0.0 green = 0.0 blue = 1.0 on C_ORANGE red = 1.0 green = 0.5 blue = 0.5 off func createPopupMenus shrinkMenu = glutCreateMenu(:processShrinkMenu) glutAddMenuEntry("Shrink",C_SHRINK) glutAddMenuEntry("NORMAL",C_NORMAL) fillMenu = glutCreateMenu(:processFillMenu) glutAddMenuEntry("Fill",C_FILL) glutAddMenuEntry("Line",C_LINE) colorMenu = glutCreateMenu(:processColorMenu) glutAddMenuEntry("Red",C_RED) glutAddMenuEntry("Blue",C_BLUE) glutAddMenuEntry("Green",C_GREEN) glutAddMenuEntry("Orange",C_ORANGE) mainMenu = glutCreateMenu(:processMainMenu) glutAddSubMenu("Polygon Mode", fillMenu) glutAddSubMenu("Color", colorMenu) // attach the menu to the right button glutAttachMenu(GLUT_RIGHT_BUTTON) // this will allow us to know if the menu is active glutMenuStatusFunc(:processMenuStatus) // ----------------------------------- (continues on next page) 58.12. Menu Events 817
862.
Ring Documentation, Release1.21.2 (continued from previous page) // MAIN // ----------------------------------- func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test 10") // register callbacks glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) glutIdleFunc(:renderScene) glutIgnoreKeyRepeat(1) glutKeyboardFunc(:processNormalKeys) glutSpecialFunc(:pressKey) glutSpecialUpFunc(:releaseKey) // here are the two new functions glutMouseFunc(:mouseButton) glutMotionFunc(:mouseMove) // OpenGL init glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) // init Menus createPopupMenus() // enter GLUT event processing cycle glutMainLoop() Screen Shot 58.12. Menu Events 818
863.
Ring Documentation, Release1.21.2 58.13 Using Fonts Example: load "freeglut.ring" load "opengl21lib.ring" // angle of rotation for the camera direction angle = 0.0 // actual vector representing the camera's direction lx=0.0 lz=-1.0 // XZ position of the camera x=0.0 z=5.0 // the key states. These variables will be zero //when no key is being presses deltaAngle = 0.0 deltaMove = 0 xOrigin = -1 // Constant definitions for Menus C_RED = 1 C_GREEN = 2 C_BLUE = 3 C_ORANGE = 4 C_FILL = 5 (continues on next page) 58.13. Using Fonts 819
864.
Ring Documentation, Release1.21.2 (continued from previous page) C_LINE = 6 // Pop up menu identifiers fillMenu=NULL fontMenu=NULL mainMenu=NULL colorMenu=NULL // color for the nose red = 1.0 blue=0.5 green=0.5 // scale of snowman scale = 1.0 // menu status menuFlag = 0 // default font font = GLUT_BITMAP_TIMES_ROMAN_24 C_INT_GLUT_BITMAP_8_BY_13 = 7 C_INT_GLUT_BITMAP_9_BY_15 = 8 C_INT_GLUT_BITMAP_TIMES_ROMAN_10 = 9 C_INT_GLUT_BITMAP_TIMES_ROMAN_24 = 10 C_INT_GLUT_BITMAP_HELVETICA_10 = 11 C_INT_GLUT_BITMAP_HELVETICA_12 = 12 C_INT_GLUT_BITMAP_HELVETICA_18 = 13 func changeSize w = glutEventWidth() h = glutEventHeight() // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if h = 0 h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. (continues on next page) 58.13. Using Fonts 820
865.
Ring Documentation, Release1.21.2 (continued from previous page) gluPerspective(45.0, ratio, 0.1, 100.0) // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) func drawSnowMan glScalef(scale, scale, scale) glColor3f(1.0, 1.0, 1.0) // Draw Body glTranslatef(0.0 ,0.75, 0.0) glutSolidSphere(0.75,20,20) // Draw Head glTranslatef(0.0, 1.0, 0.0) glutSolidSphere(0.25,20,20) // Draw Eyes glPushMatrix() glColor3f(0.0,0.0,0.0) glTranslatef(0.05, 0.10, 0.18) glutSolidSphere(0.05,10,10) glTranslatef(-0.1, 0.0, 0.0) glutSolidSphere(0.05,10,10) glPopMatrix() // Draw Nose glColor3f(red, green, blue) glRotatef(0.0,1.0, 0.0, 0.0) glutSolidCone(0.08,0.5,10,2) glColor3f(1.0, 1.0, 1.0) func renderBitmapString x,y,z,font,string glRasterPos3f(x, y,z) for c in string glutBitmapCharacter(font,ascii(c)) next func computePos deltaMove x += deltaMove * lx * 0.1 z += deltaMove * lz * 0.1 func renderScene if deltaMove computePos(deltaMove) ok (continues on next page) 58.13. Using Fonts 821
866.
Ring Documentation, Release1.21.2 (continued from previous page) // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Set the camera gluLookAt( x, 1.0, z, x+lx, 1.0, z+lz, 0.0, 1.0, 0.0) // Draw ground glColor3f(0.9, 0.9, 0.9) glBegin(GL_QUADS) glVertex3f(-100.0, 0.0, -100.0) glVertex3f(-100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, -100.0) glEnd() // Draw 36 SnowMen for i = -3 to 2 for j = -3 to 2 glPushMatrix() glTranslatef(i*10.0, 0.0, j * 10.0) drawSnowMan() number = (i+3)*6+(j+3) renderBitmapString(0.0, 0.5, 0.0,font ,""+number) glPopMatrix() next next glutSwapBuffers() // ----------------------------------- // KEYBOARD // ----------------------------------- func processNormalKeys key = glutEventKey() xx = glutEventX() yy = glutEventY() switch key on 27 glutDestroyMenu(mainMenu) glutDestroyMenu(fillMenu) glutDestroyMenu(colorMenu) glutDestroyMenu(fontMenu) (continues on next page) 58.13. Using Fonts 822
867.
Ring Documentation, Release1.21.2 (continued from previous page) Shutdown() off func pressKey key = glutEventKey() xx = glutEventX() yy = glutEventY() switch key on GLUT_KEY_UP deltaMove = 0.5 on GLUT_KEY_DOWN deltaMove = -0.5 off func releaseKey key = glutEventKey() switch key on GLUT_KEY_UP deltaMove = 0 on GLUT_KEY_DOWN deltaMove = 0 off // ----------------------------------- // MOUSE // ----------------------------------- func mouseMove xx = glutEventX() yy = glutEventY() // this will only be true when the left button is down if xOrigin >= 0 // update deltaAngle deltaAngle = (xx - xOrigin) * 0.001 // update camera's direction lx = sin(angle + deltaAngle) lz = -cos(angle + deltaAngle) ok func mouseButton (continues on next page) 58.13. Using Fonts 823
868.
Ring Documentation, Release1.21.2 (continued from previous page) button = glutEventButton() state = glutEventState() xx = glutEventX() yy = glutEventY() // only start motion if the left button is pressed if button = GLUT_LEFT_BUTTON // when the button is released if state = GLUT_UP angle += deltaAngle xOrigin = -1 else // state = GLUT_DOWN xOrigin = xx ok ok // ----------------------------------- // MENUS // ----------------------------------- func processMenuStatus status = glutEventStatus() if status = GLUT_MENU_IN_USE menuFlag = 1 else menuFlag = 0 ok func processMainMenu // nothing to do in here // all actions are for submenus func processFillMenu option = glutEventValue() switch option on C_FILL glPolygonMode(GL_FRONT, GL_FILL) on C_LINE glPolygonMode(GL_FRONT, GL_LINE) off (continues on next page) 58.13. Using Fonts 824
869.
Ring Documentation, Release1.21.2 (continued from previous page) func processFontMenu option = glutEventValue() switch (option) { on C_INT_GLUT_BITMAP_8_BY_13 font = GLUT_BITMAP_8_BY_13 on C_INT_GLUT_BITMAP_9_BY_15 font = GLUT_BITMAP_9_BY_15 on C_INT_GLUT_BITMAP_TIMES_ROMAN_10 font = GLUT_BITMAP_TIMES_ROMAN_10 on C_INT_GLUT_BITMAP_TIMES_ROMAN_24 font = GLUT_BITMAP_TIMES_ROMAN_24 on C_INT_GLUT_BITMAP_HELVETICA_10 font = GLUT_BITMAP_HELVETICA_10 on C_INT_GLUT_BITMAP_HELVETICA_12 font = GLUT_BITMAP_HELVETICA_12 on C_INT_GLUT_BITMAP_HELVETICA_18 font = GLUT_BITMAP_HELVETICA_18 off func processColorMenu option = glutEventValue() switch option on C_RED red = 1.0 green = 0.0 blue = 0.0 on C_GREEN red = 0.0 green = 1.0 blue = 0.0 on C_BLUE red = 0.0 green = 0.0 blue = 1.0 on C_ORANGE red = 1.0 green = 0.5 blue = 0.5 off func createPopupMenus fontMenu = glutCreateMenu(:processFontMenu) glutAddMenuEntry("BITMAP_8_BY_13 ",C_INT_GLUT_BITMAP_8_BY_13 ) glutAddMenuEntry("BITMAP_9_BY_15",C_INT_GLUT_BITMAP_9_BY_15 ) glutAddMenuEntry("BITMAP_TIMES_ROMAN_10 ",C_INT_GLUT_BITMAP_TIMES_ROMAN_10 ) (continues on next page) 58.13. Using Fonts 825
870.
Ring Documentation, Release1.21.2 (continued from previous page) glutAddMenuEntry("BITMAP_TIMES_ROMAN_24",C_INT_GLUT_BITMAP_TIMES_ROMAN_24 ) glutAddMenuEntry("BITMAP_HELVETICA_10 ",C_INT_GLUT_BITMAP_HELVETICA_10 ) glutAddMenuEntry("BITMAP_HELVETICA_12",C_INT_GLUT_BITMAP_HELVETICA_12 ) glutAddMenuEntry("BITMAP_HELVETICA_18",C_INT_GLUT_BITMAP_HELVETICA_18 ) fillMenu = glutCreateMenu(:processFillMenu) glutAddMenuEntry("Fill",C_FILL) glutAddMenuEntry("Line",C_LINE) colorMenu = glutCreateMenu(:processColorMenu) glutAddMenuEntry("Red",C_RED); glutAddMenuEntry("Blue",C_BLUE); glutAddMenuEntry("Green",C_GREEN); glutAddMenuEntry("Orange",C_ORANGE); mainMenu = glutCreateMenu(:processMainMenu) glutAddSubMenu("Polygon Mode", fillMenu) glutAddSubMenu("Color", colorMenu) glutAddSubMenu("Font",fontMenu) // attach the menu to the right button glutAttachMenu(GLUT_RIGHT_BUTTON) // this will allow us to know if the menu is active glutMenuStatusFunc(:processMenuStatus) // ----------------------------------- // MAIN // ----------------------------------- func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test 11") // register callbacks glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) glutIdleFunc(:renderScene) glutIgnoreKeyRepeat(1) glutKeyboardFunc(:processNormalKeys) glutSpecialFunc(:pressKey) glutSpecialUpFunc(:releaseKey) // here are the two new functions (continues on next page) 58.13. Using Fonts 826
871.
Ring Documentation, Release1.21.2 (continued from previous page) glutMouseFunc(:mouseButton) glutMotionFunc(:mouseMove) // OpenGL init glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) // init Menus createPopupMenus() // enter GLUT event processing cycle glutMainLoop() Screen Shot 58.14 Frames Per Second Example load "freeglut.ring" load "opengl21lib.ring" // angle of rotation for the camera direction angle = 0.0 // actual vector representing the camera's direction lx=0.0 lz=-1.0 // XZ position of the camera (continues on next page) 58.14. Frames Per Second 827
872.
Ring Documentation, Release1.21.2 (continued from previous page) x=0.0 z=5.0 // the key states. These variables will be zero //when no key is being presses deltaAngle = 0.0 deltaMove = 0 xOrigin = -1 // Constant definitions for Menus C_RED = 1 C_GREEN = 2 C_BLUE = 3 C_ORANGE = 4 C_FILL = 5 C_LINE = 6 // Pop up menu identifiers fillMenu=NULL fontMenu=NULL mainMenu=NULL colorMenu=NULL // color for the nose red = 1.0 blue=0.5 green=0.5 // scale of snowman scale = 1.0 // menu status menuFlag = 0 // default font font = GLUT_BITMAP_TIMES_ROMAN_24 C_INT_GLUT_BITMAP_8_BY_13 = 7 C_INT_GLUT_BITMAP_9_BY_15 = 8 C_INT_GLUT_BITMAP_TIMES_ROMAN_10 = 9 C_INT_GLUT_BITMAP_TIMES_ROMAN_24 = 10 C_INT_GLUT_BITMAP_HELVETICA_10 = 11 C_INT_GLUT_BITMAP_HELVETICA_12 = 12 C_INT_GLUT_BITMAP_HELVETICA_18 = 13 // width and height of the window h = 0 w = 0 // variables to compute frames per second frame=0 time=0 (continues on next page) 58.14. Frames Per Second 828
873.
Ring Documentation, Release1.21.2 (continued from previous page) timebase=0 s = "" func changeSize w = glutEventWidth() h = glutEventHeight() // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if h = 0 h = 1 ok ratio = w * 1.0 / h // Use the Projection Matrix glMatrixMode(GL_PROJECTION) // Reset Matrix glLoadIdentity() // Set the viewport to be the entire window glViewport(0, 0, w, h) // Set the correct perspective. gluPerspective(45.0, ratio, 0.1, 100.0) // Get Back to the Modelview glMatrixMode(GL_MODELVIEW) func drawSnowMan glScalef(scale, scale, scale) glColor3f(1.0, 1.0, 1.0) // Draw Body glTranslatef(0.0 ,0.75, 0.0) glutSolidSphere(0.75,20,20) // Draw Head glTranslatef(0.0, 1.0, 0.0) glutSolidSphere(0.25,20,20) // Draw Eyes glPushMatrix() glColor3f(0.0,0.0,0.0) glTranslatef(0.05, 0.10, 0.18) glutSolidSphere(0.05,10,10) glTranslatef(-0.1, 0.0, 0.0) glutSolidSphere(0.05,10,10) glPopMatrix() (continues on next page) 58.14. Frames Per Second 829
874.
Ring Documentation, Release1.21.2 (continued from previous page) // Draw Nose glColor3f(red, green, blue) glRotatef(0.0,1.0, 0.0, 0.0) glutSolidCone(0.08,0.5,10,2) glColor3f(1.0, 1.0, 1.0) func renderBitmapString x,y,z,font,string glRasterPos3f(x, y,z) for c in string glutBitmapCharacter(font,ascii(c)) next func renderStrokeFontString x,y,z,font,string glPushMatrix() glTranslatef(x, y,z) glScalef(0.002, 0.002, 0.002) for c in string glutStrokeCharacter(font, Ascii(c)); next glPopMatrix() func restorePerspectiveProjection glMatrixMode(GL_PROJECTION) // restore previous projection matrix glPopMatrix() // get back to modelview mode glMatrixMode(GL_MODELVIEW) func setOrthographicProjection // switch to projection mode glMatrixMode(GL_PROJECTION) // save previous matrix which contains the //settings for the perspective projection glPushMatrix() // reset matrix glLoadIdentity() // set a 2D orthographic projection gluOrtho2D(0, w, h, 0) // switch back to modelview mode glMatrixMode(GL_MODELVIEW) (continues on next page) 58.14. Frames Per Second 830
875.
Ring Documentation, Release1.21.2 (continued from previous page) func computePos deltaMove x += deltaMove * lx * 0.1 z += deltaMove * lz * 0.1 func renderScene if deltaMove computePos(deltaMove) ok // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Set the camera gluLookAt( x, 1.0, z, x+lx, 1.0, z+lz, 0.0, 1.0, 0.0) // Draw ground glColor3f(0.9, 0.9, 0.9) glBegin(GL_QUADS) glVertex3f(-100.0, 0.0, -100.0) glVertex3f(-100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, 100.0) glVertex3f( 100.0, 0.0, -100.0) glEnd() // Draw 9 SnowMen for i = -3 to -1 for j = -3 to -1 glPushMatrix() glTranslatef(i*10.0, 0.0, j * 10.0) drawSnowMan() number = (i+3)*3+(j+3) renderBitmapString(0.0, 0.5, 0.0,font ,""+number) glPopMatrix() next next // Code to compute frames per second frame++ time=glutGet(GLUT_ELAPSED_TIME) if time - timebase > 1000 s = "RingFreeGLUT - FPS: " + (frame*1000.0/(time-timebase)) (continues on next page) 58.14. Frames Per Second 831
876.
Ring Documentation, Release1.21.2 (continued from previous page) timebase = time frame = 0 ok // Code to display a string (fps) with bitmap fonts setOrthographicProjection() glPushMatrix() glLoadIdentity() renderBitmapString(5,30,0,GLUT_BITMAP_HELVETICA_18,s) glPopMatrix() restorePerspectiveProjection() glutSwapBuffers() // ----------------------------------- // KEYBOARD // ----------------------------------- func processNormalKeys key = glutEventKey() xx = glutEventX() yy = glutEventY() switch key on 27 glutDestroyMenu(mainMenu) glutDestroyMenu(fillMenu) glutDestroyMenu(colorMenu) glutDestroyMenu(fontMenu) Shutdown() off func pressKey key = glutEventKey() xx = glutEventX() yy = glutEventY() switch key on GLUT_KEY_UP deltaMove = 0.5 on GLUT_KEY_DOWN deltaMove = -0.5 off func releaseKey (continues on next page) 58.14. Frames Per Second 832
877.
Ring Documentation, Release1.21.2 (continued from previous page) key = glutEventKey() switch key on GLUT_KEY_UP deltaMove = 0 on GLUT_KEY_DOWN deltaMove = 0 off // ----------------------------------- // MOUSE // ----------------------------------- func mouseMove xx = glutEventX() yy = glutEventY() // this will only be true when the left button is down if xOrigin >= 0 // update deltaAngle deltaAngle = (xx - xOrigin) * 0.001 // update camera's direction lx = sin(angle + deltaAngle) lz = -cos(angle + deltaAngle) ok func mouseButton button = glutEventButton() state = glutEventState() xx = glutEventX() yy = glutEventY() // only start motion if the left button is pressed if button = GLUT_LEFT_BUTTON // when the button is released if state = GLUT_UP angle += deltaAngle xOrigin = -1 else // state = GLUT_DOWN xOrigin = xx ok ok // ----------------------------------- // MENUS (continues on next page) 58.14. Frames Per Second 833
878.
Ring Documentation, Release1.21.2 (continued from previous page) // ----------------------------------- func processMenuStatus status = glutEventStatus() if status = GLUT_MENU_IN_USE menuFlag = 1 else menuFlag = 0 ok func processMainMenu // nothing to do in here // all actions are for submenus func processFillMenu option = glutEventValue() switch option on C_FILL glPolygonMode(GL_FRONT, GL_FILL) on C_LINE glPolygonMode(GL_FRONT, GL_LINE) off func processFontMenu option = glutEventValue() switch (option) { on C_INT_GLUT_BITMAP_8_BY_13 font = GLUT_BITMAP_8_BY_13 on C_INT_GLUT_BITMAP_9_BY_15 font = GLUT_BITMAP_9_BY_15 on C_INT_GLUT_BITMAP_TIMES_ROMAN_10 font = GLUT_BITMAP_TIMES_ROMAN_10 on C_INT_GLUT_BITMAP_TIMES_ROMAN_24 font = GLUT_BITMAP_TIMES_ROMAN_24 on C_INT_GLUT_BITMAP_HELVETICA_10 font = GLUT_BITMAP_HELVETICA_10 on C_INT_GLUT_BITMAP_HELVETICA_12 font = GLUT_BITMAP_HELVETICA_12 on C_INT_GLUT_BITMAP_HELVETICA_18 font = GLUT_BITMAP_HELVETICA_18 off (continues on next page) 58.14. Frames Per Second 834
879.
Ring Documentation, Release1.21.2 (continued from previous page) func processColorMenu option = glutEventValue() switch option on C_RED red = 1.0 green = 0.0 blue = 0.0 on C_GREEN red = 0.0 green = 1.0 blue = 0.0 on C_BLUE red = 0.0 green = 0.0 blue = 1.0 on C_ORANGE red = 1.0 green = 0.5 blue = 0.5 off func createPopupMenus fontMenu = glutCreateMenu(:processFontMenu) glutAddMenuEntry("BITMAP_8_BY_13 ",C_INT_GLUT_BITMAP_8_BY_13 ) glutAddMenuEntry("BITMAP_9_BY_15",C_INT_GLUT_BITMAP_9_BY_15 ) glutAddMenuEntry("BITMAP_TIMES_ROMAN_10 ",C_INT_GLUT_BITMAP_TIMES_ROMAN_10 ) glutAddMenuEntry("BITMAP_TIMES_ROMAN_24",C_INT_GLUT_BITMAP_TIMES_ROMAN_24 ) glutAddMenuEntry("BITMAP_HELVETICA_10 ",C_INT_GLUT_BITMAP_HELVETICA_10 ) glutAddMenuEntry("BITMAP_HELVETICA_12",C_INT_GLUT_BITMAP_HELVETICA_12 ) glutAddMenuEntry("BITMAP_HELVETICA_18",C_INT_GLUT_BITMAP_HELVETICA_18 ) fillMenu = glutCreateMenu(:processFillMenu) glutAddMenuEntry("Fill",C_FILL) glutAddMenuEntry("Line",C_LINE) colorMenu = glutCreateMenu(:processColorMenu) glutAddMenuEntry("Red",C_RED); glutAddMenuEntry("Blue",C_BLUE); glutAddMenuEntry("Green",C_GREEN); glutAddMenuEntry("Orange",C_ORANGE); mainMenu = glutCreateMenu(:processMainMenu) glutAddSubMenu("Polygon Mode", fillMenu) glutAddSubMenu("Color", colorMenu) (continues on next page) 58.14. Frames Per Second 835
880.
Ring Documentation, Release1.21.2 (continued from previous page) glutAddSubMenu("Font",fontMenu) // attach the menu to the right button glutAttachMenu(GLUT_RIGHT_BUTTON) // this will allow us to know if the menu is active glutMenuStatusFunc(:processMenuStatus) // ----------------------------------- // MAIN // ----------------------------------- func main // init GLUT and create window glutInit() glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA) glutInitWindowPosition(100,100) glutInitWindowSize(320,320) glutCreateWindow("RingFreeGLUT - Test - 9 SnowMan") // register callbacks glutDisplayFunc(:renderScene) glutReshapeFunc(:changeSize) glutIdleFunc(:renderScene) glutIgnoreKeyRepeat(1) glutKeyboardFunc(:processNormalKeys) glutSpecialFunc(:pressKey) glutSpecialUpFunc(:releaseKey) // here are the two new functions glutMouseFunc(:mouseButton) glutMotionFunc(:mouseMove) // OpenGL init glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) // init Menus createPopupMenus() // enter GLUT event processing cycle glutMainLoop() Screen Shots: The First screen shot 58.14. Frames Per Second 836
Ring Documentation, Release1.21.2 58.15 Make a Cube using RingOpenGL and RingFreeGLUT Example: load "freeglut.ring" load "opengl21lib.ring" // ---------------------------------------------------------- // Global Variables // ---------------------------------------------------------- rotate_y=0 rotate_x=0 // ---------------------------------------------------------- // display() Callback function // ---------------------------------------------------------- (continues on next page) 58.15. Make a Cube using RingOpenGL and RingFreeGLUT 838
883.
Ring Documentation, Release1.21.2 (continued from previous page) func display // Clear screen and Z-buffer glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) // Reset transformations glLoadIdentity() // Rotate when user changes rotate_x and rotate_y glRotatef( rotate_x, 1.0, 0.0, 0.0 ) glRotatef( rotate_y, 0.0, 1.0, 0.0 ) //Multi-colored side - FRONT glBegin(GL_POLYGON) glColor3f( 1.0, 0.0, 0.0 ) glVertex3f( 0.5, -0.5, -0.5 ) # P1 is red glColor3f( 0.0, 1.0, 0.0 ) glVertex3f( 0.5, 0.5, -0.5 ) # P2 is green glColor3f( 0.0, 0.0, 1.0 ) glVertex3f( -0.5, 0.5, -0.5 ) # P3 is blue glColor3f( 1.0, 0.0, 1.0 ) glVertex3f( -0.5, -0.5, -0.5 ) # P4 is purple glEnd() // White side - BACK glBegin(GL_POLYGON) glColor3f( 1.0, 1.0, 1.0 ) glVertex3f( 0.5, -0.5, 0.5 ) glVertex3f( 0.5, 0.5, 0.5 ) glVertex3f( -0.5, 0.5, 0.5 ) glVertex3f( -0.5, -0.5, 0.5 ) glEnd() // Purple side - RIGHT glBegin(GL_POLYGON) glColor3f( 1.0, 0.0, 1.0 ) glVertex3f( 0.5, -0.5, -0.5 ) glVertex3f( 0.5, 0.5, -0.5 ) glVertex3f( 0.5, 0.5, 0.5 ) glVertex3f( 0.5, -0.5, 0.5 ) glEnd() // Green side - LEFT glBegin(GL_POLYGON) glColor3f( 0.0, 1.0, 0.0 ) glVertex3f( -0.5, -0.5, 0.5 ) glVertex3f( -0.5, 0.5, 0.5 ) glVertex3f( -0.5, 0.5, -0.5 ) glVertex3f( -0.5, -0.5, -0.5 ) glEnd() // Blue side - TOP glBegin(GL_POLYGON) glColor3f( 0.0, 0.0, 1.0 ) (continues on next page) 58.15. Make a Cube using RingOpenGL and RingFreeGLUT 839
884.
Ring Documentation, Release1.21.2 (continued from previous page) glVertex3f( 0.5, 0.5, 0.5 ) glVertex3f( 0.5, 0.5, -0.5 ) glVertex3f( -0.5, 0.5, -0.5 ) glVertex3f( -0.5, 0.5, 0.5 ) glEnd() // Red side - BOTTOM glBegin(GL_POLYGON) glColor3f( 1.0, 0.0, 0.0 ) glVertex3f( 0.5, -0.5, -0.5 ) glVertex3f( 0.5, -0.5, 0.5 ) glVertex3f( -0.5, -0.5, 0.5 ) glVertex3f( -0.5, -0.5, -0.5 ) glEnd() glFlush() glutSwapBuffers() // ---------------------------------------------------------- // specialKeys() Callback Function // ---------------------------------------------------------- func specialKeys key = glutEventKey() // Right arrow - increase rotation by 5 degree switch Key on GLUT_KEY_RIGHT rotate_y += 5 // Left arrow - decrease rotation by 5 degree on GLUT_KEY_LEFT rotate_y -= 5 on GLUT_KEY_UP rotate_x += 5 on GLUT_KEY_DOWN rotate_x -= 5 off // Request display update glutPostRedisplay() // ---------------------------------------------------------- // main() function // ---------------------------------------------------------- (continues on next page) 58.15. Make a Cube using RingOpenGL and RingFreeGLUT 840
885.
Ring Documentation, Release1.21.2 (continued from previous page) func main // Initialize GLUT and process user parameters glutInit() // Request double buffered true color window with Z-buffer glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) // Create window glutCreateWindow("Awesome Cube") // Enable Z-buffer depth test glEnable(GL_DEPTH_TEST) // Callback functions glutDisplayFunc(:display) glutSpecialFunc(:specialKeys) // Pass control to GLUT for events glutMainLoop() // Return to OS Screen Shot: 58.15. Make a Cube using RingOpenGL and RingFreeGLUT 841
886.
CHAPTER FIFTYNINE USING RINGOPENGL ANDRINGALLEGRO FOR 3D GRAPHICS In this chapter we will learn about using RingOpenGL and RingAllegro 59.1 3D Cube and Texture Source Code: # Load Libraries load "gamelib.ring" # RingAllegro Library load "opengl21lib.ring" # RingOpenGL Library #============================================================== # To Support MacOS X al_run_main() func al_game_start # Called by al_run_main() main() # Now we call our main function #============================================================== func main new GraphicsApp { start() } class GraphicsApp from GraphicsAppBase TITLE = "Ring Cube" bitmap texture xrot = 0.0 yrot = 0.0 zrot = 0.0 func loadresources bitmap = al_load_bitmap("ring.bmp") texture = al_get_opengl_texture(bitmap) (continues on next page) 842
887.
Ring Documentation, Release1.21.2 (continued from previous page) func destroyResources al_destroy_bitmap(bitmap) func drawScene w = 800 h = 600 ratio = w / h glViewport(0, 0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(45,ratio,1,100) glMatrixMode(GL_MODELVIEW) glLoadIdentity() glEnable(GL_TEXTURE_2D) glShadeModel(GL_SMOOTH) glClearColor(0.0, 0.0, 0.0, 0.5) glClearDepth(1.0) glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) glDepthFunc(GL_LEQUAL) glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() glTranslatef(0.0,0.0,-5.0) glRotatef(xrot,1.0,0.0,0.0) glRotatef(yrot,0.0,1.0,0.0) glRotatef(zrot,0.0,0.0,1.0) glBindTexture(GL_TEXTURE_2D, texture) glBegin(GL_QUADS) // Front Face glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, -1.0, 1.0) glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, -1.0, 1.0) glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, 1.0) glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, 1.0) // Back Face glTexCoord2f(1.0, 0.0) glVertex3f(-1.0, -1.0, -1.0) glTexCoord2f(1.0, 1.0) glVertex3f(-1.0, 1.0, -1.0) glTexCoord2f(0.0, 1.0) glVertex3f( 1.0, 1.0, -1.0) glTexCoord2f(0.0, 0.0) glVertex3f( 1.0, -1.0, -1.0) // Top Face glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, -1.0) glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, 1.0, 1.0) glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, 1.0, 1.0) glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, -1.0) (continues on next page) 59.1. 3D Cube and Texture 843
Ring Documentation, Release1.21.2 (continued from previous page) al_set_new_display_flags(ALLEGRO_OPENGL) display = al_create_display(SCREEN_W,SCREEN_H) al_set_Window_title(display,TITLE) al_clear_to_color(al_map_rgb(0,0,0)) event_queue = al_create_event_queue() al_register_event_source(event_queue, al_get_display_event_source(display)) ev = al_new_allegro_event() timeout = al_new_allegro_timeout() al_init_timeout(timeout, 0.06) timer = al_create_timer(1.0 / FPS) al_register_event_source(event_queue, al_get_timer_event_source(timer)) al_start_timer(timer) al_install_mouse() al_register_event_source(event_queue, al_get_mouse_event_source()) al_install_keyboard() al_register_event_source(event_queue, al_get_keyboard_event_source()) func eventsLoop while true al_init_timeout(timeout, 0.06) al_wait_for_event_until(event_queue, ev, timeout) switch al_get_allegro_event_type(ev) on ALLEGRO_EVENT_DISPLAY_CLOSE exit on ALLEGRO_EVENT_TIMER redraw = true on ALLEGRO_EVENT_MOUSE_AXES mouse_x = al_get_allegro_event_mouse_x(ev) mouse_y = al_get_allegro_event_mouse_y(ev) on ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY mouse_x = al_get_allegro_event_mouse_x(ev) mouse_y = al_get_allegro_event_mouse_y(ev) on ALLEGRO_EVENT_MOUSE_BUTTON_UP exit on ALLEGRO_EVENT_KEY_DOWN switch al_get_allegro_event_keyboard_keycode(ev) on ALLEGRO_KEY_UP key[KEY_UP] = true on ALLEGRO_KEY_DOWN key[KEY_DOWN] = true on ALLEGRO_KEY_LEFT key[KEY_LEFT] = true on ALLEGRO_KEY_RIGHT key[KEY_RIGHT] = true off on ALLEGRO_EVENT_KEY_UP switch al_get_allegro_event_keyboard_keycode(ev) (continues on next page) 59.1. 3D Cube and Texture 845
890.
Ring Documentation, Release1.21.2 (continued from previous page) on ALLEGRO_KEY_UP key[KEY_UP] = false on ALLEGRO_KEY_DOWN key[KEY_DOWN] = false on ALLEGRO_KEY_LEFT key[KEY_LEFT] = false on ALLEGRO_KEY_RIGHT key[KEY_RIGHT] = false on ALLEGRO_KEY_ESCAPE exit off off if redraw and al_is_event_queue_empty(event_queue) redraw = false drawScene() al_flip_display() ok callgc() end func destroy destroyResources() al_destroy_timer(timer) al_destroy_allegro_event(ev) al_destroy_allegro_timeout(timeout) al_destroy_event_queue(event_queue) al_destroy_display(display) al_exit() func loadresources func drawScene func destroyResources Screen Shot: 59.1. 3D Cube and Texture 846
891.
Ring Documentation, Release1.21.2 59.2 Many Cubes Source Code: # Load Libraries load "gamelib.ring" # RingAllegro Library load "opengl21lib.ring" # RingOpenGL Library #============================================================== # To Support MacOS X al_run_main() func al_game_start # Called by al_run_main() main() # Now we call our main function #============================================================== func main new GraphicsApp { start() (continues on next page) 59.2. Many Cubes 847
892.
Ring Documentation, Release1.21.2 (continued from previous page) } class GraphicsApp from GraphicsAppBase TITLE = "Many Cubes" bitmap bitmap2 bitmap3 texture texture2 texture3 fps = 120 xrot = 0.0 yrot = 0.0 zrot = 0.0 nPerspective = 100 func loadresources bitmap = al_load_bitmap("sky1.jpg") texture = al_get_opengl_texture(bitmap) bitmap2 = al_load_bitmap("sky2.jpg") texture2 = al_get_opengl_texture(bitmap2) bitmap3 = al_load_bitmap("sky3.jpg") texture3 = al_get_opengl_texture(bitmap3) func destroyResources al_destroy_bitmap(bitmap) al_destroy_bitmap(bitmap2) al_destroy_bitmap(bitmap3) func drawScene prepare() cubes() rotate() func Prepare w = 800 h = 600 ratio = w / h glViewport(0, 0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(-nPerspective,ratio,1,nPerspective) glMatrixMode(GL_MODELVIEW) glLoadIdentity() glEnable(GL_TEXTURE_2D) glShadeModel(GL_SMOOTH) glClearColor(0.0, 0.0, 0.0, 0.5) (continues on next page) 59.2. Many Cubes 848
893.
Ring Documentation, Release1.21.2 (continued from previous page) glClearDepth(1.0) glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) glDepthFunc(GL_LEQUAL) glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) func Cubes cube(5,-3.4,-5,:sky1) cube(0,-3,-5,:sky1) cube(-5,-3,-5,:sky1) cube(5,0.5,-5,:sky2) cube(0,0.5,-5,:sky2) cube(-5,0.5,-5,:sky2) cube(5,4,-5,:sky3) cube(0,4,-5,:sky3) cube(-5,4,-5,:sky3) func Rotate xrot += 0.3 * 5 yrot += 0.2 * 5 zrot += 0.4 * 5 nPerspective += 0.5 func cube(x,y,z,nTexture) glLoadIdentity() glTranslatef(x,y,z) glRotatef(xrot,1.0,0.0,0.0) glRotatef(yrot,0.0,1.0,0.0) glRotatef(zrot,0.0,0.0,1.0) drawcube(nTexture) func drawcube(cTexture) switch cTexture on :sky1 glBindTexture(GL_TEXTURE_2D, texture) on :sky2 glBindTexture(GL_TEXTURE_2D, texture2) on :sky3 glBindTexture(GL_TEXTURE_2D, texture3) off glBegin(GL_QUADS) // Front Face glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, -1.0, 1.0) glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, -1.0, 1.0) glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, 1.0) glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, 1.0) // Back Face (continues on next page) 59.2. Many Cubes 849
Ring Documentation, Release1.21.2 (continued from previous page) eventsLoop() destroy() func setup al_init() al_init_image_addon() al_set_new_display_flags(ALLEGRO_OPENGL) display = al_create_display(SCREEN_W,SCREEN_H) al_set_Window_title(display,TITLE) al_clear_to_color(al_map_rgb(0,0,0)) event_queue = al_create_event_queue() al_register_event_source(event_queue, al_get_display_event_source(display)) ev = al_new_allegro_event() timeout = al_new_allegro_timeout() al_init_timeout(timeout, 0.06) timer = al_create_timer(1.0 / FPS) al_register_event_source(event_queue, al_get_timer_event_source(timer)) al_start_timer(timer) al_install_mouse() al_register_event_source(event_queue, al_get_mouse_event_source()) al_install_keyboard() al_register_event_source(event_queue, al_get_keyboard_event_source()) func eventsLoop while true al_init_timeout(timeout, 0.06) al_wait_for_event_until(event_queue, ev, timeout) switch al_get_allegro_event_type(ev) on ALLEGRO_EVENT_DISPLAY_CLOSE exit on ALLEGRO_EVENT_TIMER redraw = true on ALLEGRO_EVENT_MOUSE_AXES mouse_x = al_get_allegro_event_mouse_x(ev) mouse_y = al_get_allegro_event_mouse_y(ev) on ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY mouse_x = al_get_allegro_event_mouse_x(ev) mouse_y = al_get_allegro_event_mouse_y(ev) on ALLEGRO_EVENT_MOUSE_BUTTON_UP exit on ALLEGRO_EVENT_KEY_DOWN switch al_get_allegro_event_keyboard_keycode(ev) on ALLEGRO_KEY_UP key[KEY_UP] = true on ALLEGRO_KEY_DOWN key[KEY_DOWN] = true (continues on next page) 59.2. Many Cubes 851
896.
Ring Documentation, Release1.21.2 (continued from previous page) on ALLEGRO_KEY_LEFT key[KEY_LEFT] = true on ALLEGRO_KEY_RIGHT key[KEY_RIGHT] = true off on ALLEGRO_EVENT_KEY_UP switch al_get_allegro_event_keyboard_keycode(ev) on ALLEGRO_KEY_UP key[KEY_UP] = false on ALLEGRO_KEY_DOWN key[KEY_DOWN] = false on ALLEGRO_KEY_LEFT key[KEY_LEFT] = false on ALLEGRO_KEY_RIGHT key[KEY_RIGHT] = false on ALLEGRO_KEY_ESCAPE exit off off if redraw and al_is_event_queue_empty(event_queue) redraw = false drawScene() al_flip_display() ok callgc() end func destroy destroyResources() al_destroy_timer(timer) al_destroy_allegro_event(ev) al_destroy_allegro_timeout(timeout) al_destroy_event_queue(event_queue) al_destroy_display(display) al_exit() func loadresources func drawScene func destroyResources Screen Shot: 59.2. Many Cubes 852
897.
Ring Documentation, Release1.21.2 59.3 TicTacToe 3D Game Source Code: # Load Libraries load "gamelib.ring" # RingAllegro Library load "opengl21lib.ring" # RingOpenGL Library #============================================================== # To Support MacOS X al_run_main() func al_game_start # Called by al_run_main() main() # Now we call our main function #============================================================== func main new TicTacToe3D { start() } (continues on next page) 59.3. TicTacToe 3D Game 853
898.
Ring Documentation, Release1.21.2 (continued from previous page) class TicTacToe3D from GameLogic FPS = 60 TITLE = "TicTacToe 3D" oBackground = new GameBackground oGameSound = new GameSound oGameCube = new GameCube oGameOver = new GameOver oGameInterface = new GameInterface func loadresources oGameOver.loadresources() oGameSound.loadresources() oBackGround.loadresources() oGameCube.loadresources() func destroyResources oGameOver.destroyResources() oGameSound.destroyResources() oBackGround.destroyResources() oGameCube.destroyResources() func drawScene oBackground.update() oGameInterface.update(self) func MouseClickEvent oGameInterface.MouseClickEvent(self) class GameInterface func Update oGame prepare() cubes(oGame) func Prepare w = 1024 h = 768 ratio = w / h glViewport(0, 0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(-120,ratio,1,120) glMatrixMode(GL_MODELVIEW) glLoadIdentity() glEnable(GL_TEXTURE_2D) glShadeModel(GL_SMOOTH) glClearColor(0.0, 0.0, 0.0, 0.5) glClearDepth(1.0) glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) glDepthFunc(GL_LEQUAL) (continues on next page) 59.3. TicTacToe 3D Game 854
Ring Documentation, Release1.21.2 (continued from previous page) func point2button x,y nRow = 0 nCol = 0 for t = 1 to len(aGameButtons) rect = aGameButtons[t] if x >= rect[1] and x <= rect[3] and y >= rect[2] and y <= rect[4] switch t on 1 nRow = 1 nCol = 1 on 2 nRow = 1 nCol = 2 on 3 nRow = 1 nCol = 3 on 4 nRow = 2 nCol = 1 on 5 nRow = 2 nCol = 2 on 6 nRow = 2 nCol = 3 on 7 nRow = 3 nCol = 1 on 8 nRow = 3 nCol = 2 on 9 nRow = 3 nCol = 3 off exit ok next return [nRow,nCol] func ChangeActivePlayer() if cActivePlayer = :x cActivePlayer = :o else cActivePlayer = :x ok func CheckGameOver aList = [ aGameMap[1][1], aGameMap[1][2], aGameMap[1][3], aGameMap[2][1], aGameMap[2][2], aGameMap[2][3], aGameMap[3][1], aGameMap[3][2], aGameMap[3][3] ] for item in aList switch item on :x item = 1 on :o item = 2 on :n item = 0 off next nStatus = CheckWinner(aList) if nStatus (continues on next page) 59.3. TicTacToe 3D Game 856
901.
Ring Documentation, Release1.21.2 (continued from previous page) oGameOver { Switch nStatus on 1 Player1Win(this) on 2 Player2Win(this) on 3 NoOneWin(this) off } refreshGame() ok func refreshGame aGameMap = [ [ :n , :n , :n ] , [ :n , :n , :n ] , [ :n , :n , :n ] ] cActivePlayer = :x func CheckWinner lst //vertical check for v=1 to 9 step 3 if lst[v]!=0 and lst[v+1]!=0 and lst[v+2]!=0 if lst[v]=lst[v+1] and lst[v+1]=lst[v+2] return lst[v] ok ok next //horizontal for h=1 to 3 if lst[h]!=0 and lst[h+3]!=0 and lst[h+6]!=0 if lst[h]=lst[h+3] and lst[h+3]=lst[h+6] return lst[h] ok ok next //Cross if lst[1]!=0 and lst[5]!=0 and lst[9]!=0 if lst[1]=lst[5] and lst[5]=lst[9] return lst[1] ok ok if lst[3]!=0 and lst[5]!=0 and lst[7]!=0 if lst[3]=lst[5] and lst[5]=lst[7] return lst[3] ok ok //tie tie=true for i=1 to 9 if lst[i]=0 tie=false exit ok next if tie=true return 3 ok return 0 class GameOver (continues on next page) 59.3. TicTacToe 3D Game 857
902.
Ring Documentation, Release1.21.2 (continued from previous page) font bitmap func loadresources font = al_load_ttf_font("font/pirulen.ttf",54,0 ) bitmap = al_load_bitmap("image/ballon.png") func destroyResources al_destroy_bitmap(bitmap) al_destroy_font(font) func Player1Win oGame showMsg(oGame,80,430,"Good job X you won!") func Player2Win oGame showMsg(oGame,80,430,"Good job O you won!") func NoOneWin oGame showMsg(oGame,150,430,"Oh no it's a tie!") func ShowMsg oGame,x,y,cMsg oGame { drawScene() al_flip_display() al_rest(0.3) newdisplay = al_create_display(SCREEN_W,SCREEN_H) al_set_window_title(newdisplay,TITLE) al_clear_to_color(al_map_rgb(255,255,255)) al_draw_bitmap(this.bitmap,200,50,1) al_draw_text(this.font, al_map_rgb(0,0,255), x,y, ALLEGRO_ALIGN_LEFT,cMsg) al_flip_display() al_rest(2) al_destroy_display(newdisplay) al_set_target_backbuffer(display) } class GameCube bitmap bitmap2 bitmap3 textureX textureO textureN xrot = 0.0 yrot = 0.0 zrot = 0.0 func loadresources bitmap = al_load_bitmap("image/o.png") textureO = al_get_opengl_texture(bitmap) bitmap2 = al_load_bitmap("image/x.png") textureX = al_get_opengl_texture(bitmap2) (continues on next page) 59.3. TicTacToe 3D Game 858
903.
Ring Documentation, Release1.21.2 (continued from previous page) bitmap3 = al_load_bitmap("image/empty.png") textureN = al_get_opengl_texture(bitmap3) func destroyResources al_destroy_bitmap(bitmap) al_destroy_bitmap(bitmap2) al_destroy_bitmap(bitmap3) func cube(x,y,z,nTexture) glLoadIdentity() glTranslatef(x,y,z) glRotatef(xrot,1.0,0.0,0.0) glRotatef(yrot,0.0,1.0,0.0) glRotatef(zrot,0.0,0.0,1.0) setCubeTexture(nTexture) drawCube() func setCubeTexture cTexture switch cTexture on :x glBindTexture(GL_TEXTURE_2D, textureX) on :o glBindTexture(GL_TEXTURE_2D, textureO) on :n glBindTexture(GL_TEXTURE_2D, textureN) off func Rotate xrot += 0.3 * 5 yrot += 0.2 * 5 zrot += 0.4 * 5 func drawcube glBegin(GL_QUADS) // Front Face glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, -1.0, 1.0) glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, -1.0, 1.0) glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, 1.0) glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, 1.0) // Back Face glTexCoord2f(1.0, 0.0) glVertex3f(-1.0, -1.0, -1.0) glTexCoord2f(1.0, 1.0) glVertex3f(-1.0, 1.0, -1.0) glTexCoord2f(0.0, 1.0) glVertex3f( 1.0, 1.0, -1.0) glTexCoord2f(0.0, 0.0) glVertex3f( 1.0, -1.0, -1.0) // Top Face glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, -1.0) glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, 1.0, 1.0) glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, 1.0, 1.0) glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, -1.0) // Bottom Face glTexCoord2f(1.0, 1.0) glVertex3f(-1.0, -1.0, -1.0) glTexCoord2f(0.0, 1.0) glVertex3f( 1.0, -1.0, -1.0) (continues on next page) 59.3. TicTacToe 3D Game 859
904.
Ring Documentation, Release1.21.2 (continued from previous page) glTexCoord2f(0.0, 0.0) glVertex3f( 1.0, -1.0, 1.0) glTexCoord2f(1.0, 0.0) glVertex3f(-1.0, -1.0, 1.0) // Right face glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, -1.0, -1.0) glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, -1.0) glTexCoord2f(0.0, 1.0) glVertex3f( 1.0, 1.0, 1.0) glTexCoord2f(0.0, 0.0) glVertex3f( 1.0, -1.0, 1.0) // Left Face glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, -1.0, -1.0) glTexCoord2f(1.0, 0.0) glVertex3f(-1.0, -1.0, 1.0) glTexCoord2f(1.0, 1.0) glVertex3f(-1.0, 1.0, 1.0) glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, -1.0) glEnd() class GameBackground nBackX = 0 nBackY = 0 nBackDiffx = -1 nBackDiffy = -1 nBackMotion = 1 aBackMotionList = [ [ -1, -1 ] , # Down - Right [ 0 , 1 ] , # Up [ -1, -1 ] , # Down - Right [ 0 , 1 ] , # Up [ 1 , -1 ] , # Down - Left [ 0 , 1 ] , # Up [ 1 , -1 ] , # Down - Left [ 0 , 1 ] # Up ] bitmap func Update draw() motion() func draw al_draw_bitmap(bitmap,nBackX,nBackY,1) func motion nBackX += nBackDiffx nBackY += nBackDiffy if (nBackY = -350) or (nBackY = 0) nBackMotion++ if nBackMotion > len(aBackMotionList) nBackMotion = 1 ok (continues on next page) 59.3. TicTacToe 3D Game 860
905.
Ring Documentation, Release1.21.2 (continued from previous page) nBackDiffx = aBackMotionList[nBackMotion][1] nBackDiffy = aBackMotionList[nBackMotion][2] ok func loadResources bitmap = al_load_bitmap("image/back.jpg") func destroyResources al_destroy_bitmap(bitmap) class GameSound sample sampleid func loadresources sample = al_load_sample( "sound/music1.wav" ) sampleid = al_new_allegro_sample_id() al_play_sample(sample, 1.0, 0.0,1.0,ALLEGRO_PLAYMODE_LOOP,sampleid) func destroyResources al_destroy_allegro_sample_id(sampleid) al_destroy_sample(sample) class GraphicsAppBase display event_queue ev timeout timer redraw = true FPS = 60 SCREEN_W = 1024 SCREEN_H = 700 KEY_UP = 1 KEY_DOWN = 2 KEY_LEFT = 3 KEY_RIGHT = 4 Key = [false,false,false,false] Mouse_X = 0 Mouse_Y = 0 TITLE = "Graphics Application" PRINT_MOUSE_XY = False func start SetUp() loadResources() eventsLoop() destroy() func setup al_init() al_init_font_addon() (continues on next page) 59.3. TicTacToe 3D Game 861
906.
Ring Documentation, Release1.21.2 (continued from previous page) al_init_ttf_addon() al_init_image_addon() al_install_audio() al_init_acodec_addon() al_reserve_samples(1) al_set_new_display_flags(ALLEGRO_OPENGL) display = al_create_display(SCREEN_W,SCREEN_H) al_set_window_title(display,TITLE) al_clear_to_color(al_map_rgb(0,0,0)) event_queue = al_create_event_queue() al_register_event_source(event_queue, al_get_display_event_source(display)) ev = al_new_allegro_event() timeout = al_new_allegro_timeout() al_init_timeout(timeout, 0.06) timer = al_create_timer(1.0 / FPS) al_register_event_source(event_queue, al_get_timer_event_source(timer)) al_start_timer(timer) al_install_mouse() al_register_event_source(event_queue, al_get_mouse_event_source()) al_install_keyboard() al_register_event_source(event_queue, al_get_keyboard_event_source()) func eventsLoop while true al_init_timeout(timeout, 0.06) al_wait_for_event_until(event_queue, ev, timeout) switch al_get_allegro_event_type(ev) on ALLEGRO_EVENT_DISPLAY_CLOSE CloseEvent() on ALLEGRO_EVENT_TIMER redraw = true on ALLEGRO_EVENT_MOUSE_AXES mouse_x = al_get_allegro_event_mouse_x(ev) mouse_y = al_get_allegro_event_mouse_y(ev) if PRINT_MOUSE_XY see "x = " + mouse_x + nl see "y = " + mouse_y + nl ok on ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY mouse_x = al_get_allegro_event_mouse_x(ev) mouse_y = al_get_allegro_event_mouse_y(ev) on ALLEGRO_EVENT_MOUSE_BUTTON_UP MouseClickEvent() on ALLEGRO_EVENT_KEY_DOWN switch al_get_allegro_event_keyboard_keycode(ev) on ALLEGRO_KEY_UP key[KEY_UP] = true on ALLEGRO_KEY_DOWN (continues on next page) 59.3. TicTacToe 3D Game 862
907.
Ring Documentation, Release1.21.2 (continued from previous page) key[KEY_DOWN] = true on ALLEGRO_KEY_LEFT key[KEY_LEFT] = true on ALLEGRO_KEY_RIGHT key[KEY_RIGHT] = true off on ALLEGRO_EVENT_KEY_UP switch al_get_allegro_event_keyboard_keycode(ev) on ALLEGRO_KEY_UP key[KEY_UP] = false on ALLEGRO_KEY_DOWN key[KEY_DOWN] = false on ALLEGRO_KEY_LEFT key[KEY_LEFT] = false on ALLEGRO_KEY_RIGHT key[KEY_RIGHT] = false on ALLEGRO_KEY_ESCAPE exit off off if redraw and al_is_event_queue_empty(event_queue) redraw = false drawScene() al_flip_display() ok callgc() end func destroy destroyResources() al_destroy_timer(timer) al_destroy_allegro_event(ev) al_destroy_allegro_timeout(timeout) al_destroy_event_queue(event_queue) al_destroy_display(display) al_exit() func loadresources func drawScene func destroyResources func MouseClickEvent exit # Exit from the Events Loop func CloseEvent exit # Exit from the Events Loop Screen Shot: 59.3. TicTacToe 3D Game 863
908.
Ring Documentation, Release1.21.2 59.4 More 3D Samples You will find the samples in ring/samples/3D folder The next screen shot for the Top-Down view - Many levels of cubes sample 59.4. More 3D Samples 864
Ring Documentation, Release1.21.2 The next screen shot for the Camera and background sample Developer : Azzeddine Remmal 59.4. More 3D Samples 866
911.
CHAPTER SIXTY THE GOLD MAGIC800 GAME In this chapter we will learn about the Gold Magic 800 Game The game is developed using Ring, RingAllegro and RingOpenGL After installing the package: ringpm install goldmagic800 You will find the game in ring/applications/goldmagic800 folder 60.1 The Game Story Your friend discovered a unique and special box, created by the best wizard in the world 7000 years ago, when you close this box and move it; you will find a new gold under the box, it’s an infinite source of gold. The Box exists in a special environment full of puzzles, no one can enter this environment because it’s protected by the magic. Your friend created a new robot using nanotechnology that can move the box using a remote control device. Your mission is to solve all of these puzzles and get this box forever to be the richest people in the world. 60.2 How to play? The Gold Magic 800 is a puzzle game that will teach your unconscious mind to think like an entrepreneur. The game is based on moving your box around to get gold score (=800) to open the First Door (Box Number 1) Then directly put your box on the Door (this will open the next door), Then continue to put your box on all of the next doors in the level, You need the score (800) only for the first door, The next doors doesn’t require this condition, but your way of gold will be converted to a wall once you put the Box on any door, so select your path carefully. 60.3 What will you learn? 1- Plan First 2- Move to your target directly then go to get the required resources 3- Look to the future when you evaluate the different solutions 4- Try to avoid mistakes, Also learn from them 5- Respect the Cost (800) 6- Focus and be careful 867
912.
Ring Documentation, Release1.21.2 7- Be patient and Enjoy! 60.4 Screen Shots We can select the level The next screen shot for level (1) 60.4. Screen Shots 868
913.
Ring Documentation, Release1.21.2 The Gold Magic 800 Level Editor 60.5 Source Code You will find the Level Editor source code in this folder https://github.com/ring-lang/ring/tree/master/applications/goldmagic800/editor The user interface of the Level Editor is designed using the Ring Form Designer. 60.5. Source Code 869
914.
Ring Documentation, Release1.21.2 The next file contains the Level Editor Controller Class https://github.com/ring-lang/ring/blob/master/applications/goldmagic800/editor/editorController.ring You will find the Game Engine source code in this folder https://github.com/ring-lang/ring/tree/master/applications/goldmagic800 60.5. Source Code 870
915.
CHAPTER SIXTYONE RINGTILENGINE EXTENSION In thischapter we will learn about using the RingTilengine extension. This extension provides complete support for Tilengine The 2D retro graphics engine with raster effects Tilengine URL: https://github.com/megamarc/Tilengine Useful links (Original documentation for C programmers) • Getting started: https://github.com/ring-lang/Tilengine/blob/master/docs/quick_guide.md • Initialization: https://github.com/ring-lang/Tilengine/blob/master/docs/starting.md • Using the window: https://github.com/ring-lang/Tilengine/blob/master/docs/windowing.md • External rendering: https://github.com/ring-lang/Tilengine/blob/master/docs/rendering.md • Background layers: https://github.com/ring-lang/Tilengine/blob/master/docs/layers.md • Sprites: https://github.com/ring-lang/Tilengine/blob/master/docs/sprites.md Useful tools • Tiled Map Editor: https://www.mapeditor.org/ • aseprite: https://www.aseprite.org/ • Piskel: https://www.piskelapp.com/ • GrafX2: http://grafx2.chez.com/ Support • Tilengine Forum: http://www.tilengine.org/forum/ 61.1 Getting Started Example (1): load "tilengine.ring" TLN_Init(400, 240, 1, 0, 0) TLN_SetLoadPath("assetssonic") foreground = TLN_LoadTilemap ("Sonic_md_fg1.tmx", NULL) TLN_SetLayerTilemap(0, foreground) (continues on next page) 871
916.
Ring Documentation, Release1.21.2 (continued from previous page) TLN_CreateWindow(NULL, 0) while TLN_ProcessWindow() TLN_DrawFrame(0) end TLN_DeleteTilemap(foreground) TLN_Deinit() Screen Shot: 61.2 More Samples Example (2): load "tilengine.ring" WIDTH = 400 HEIGHT = 240 frame = 0 /* setup engine */ TLN_Init(WIDTH, HEIGHT, 1,0,0) /* load layer */ tilemap = TLN_LoadTilemap("assets/sonic/Sonic_md_fg1.tmx", NULL) (continues on next page) 61.2. More Samples 872
917.
Ring Documentation, Release1.21.2 (continued from previous page) /* setup the layer */ TLN_SetLayer(0, NULL, tilemap) TLN_SetBGColor(32,32,128) /* main loop */ TLN_CreateWindow(NULL, 0) while TLN_ProcessWindow () /* scroll the layer, one pixel per frame */ TLN_SetLayerPosition (0, frame, 0) /* render to the window */ TLN_DrawFrame (0) frame++ end /* release resources */ TLN_DeleteTilemap(tilemap) TLN_Deinit() Screen Shot: Example (3): load "tilengine.ring" (continues on next page) 61.2. More Samples 873
918.
Ring Documentation, Release1.21.2 (continued from previous page) /* engine init */ TLN_Init(640,480,0,0,1) /* load resources */ TLN_SetLoadPath("assets/color") background = TLN_LoadBitmap("beach.png") palette = TLN_GetBitmapPalette(background) sp = TLN_LoadSequencePack("beach.sqx") sequence = TLN_FindSequence(sp, "beach") /* setup */ TLN_SetBGBitmap(background) TLN_SetPaletteAnimation(0, palette, sequence, true) /* main loop */ TLN_CreateWindow(NULL, 0) TLN_DisableCRTEffect() while(TLN_ProcessWindow()) TLN_DrawFrame(0) end TLN_DeleteBitmap(background) TLN_DeleteSequencePack(sp) TLN_Deinit() Screen Shot: Example (4): load "tilengine.ring" (continues on next page) 61.2. More Samples 874
Ring Documentation, Release1.21.2 (continued from previous page) TLN_SetLayer(LAYER_FOREGROUND, NULL, foreground) TLN_SetLayerBitmap(LAYER_MIDDLEGROUND, middleground) TLN_SetLayerBitmap(LAYER_BACKGROUND, background) width = TLN_GetLayerWidth(LAYER_FOREGROUND) height = TLN_GetLayerHeight(LAYER_FOREGROUND) /* objects layer: add back objects (behind sprites) */ TLN_SetLayerObjects(LAYER_PROPS, props_list, NULL) /* sync props layer positions to main layer */ TLN_SetLayerParent(LAYER_PROPS_FRONT, LAYER_FOREGROUND) TLN_SetLayerParent(LAYER_PROPS, LAYER_FOREGROUND) /* create sprite sequences */ idle = TLN_CreateSpriteSequence(NULL, atlas, "player-idle/player-idle-", 6) skip = TLN_CreateSpriteSequence(NULL, atlas, "player-skip/player-skip-", 6) /* setup main player sprite */ xplayer = 48 yplayer = 144 TLN_ConfigSprite(0, atlas, 0) TLN_SetSpriteAnimation(0, idle, 0) /* create window & main loop */ TLN_CreateWindow(NULL, 0) TLN_DisableCRTEffect() while TLN_ProcessWindow() TLN_DrawFrame(0) /* move 3 pixels right/left main layer */ if (TLN_GetInput(INPUT_LEFT) && xworld > 0) xworld -= 3 elseif (TLN_GetInput(INPUT_RIGHT) && xworld < width - HRES) xworld += 3 ok /* update on change */ if xworld != oldx TLN_SetLayerPosition(LAYER_FOREGROUND, xworld, 32) TLN_SetLayerPosition(LAYER_MIDDLEGROUND, xworld / 2, 0) TLN_SetLayerPosition(LAYER_BACKGROUND, xworld / 3, 0) TLN_SetSpritePosition(0, xplayer - xworld, yplayer) oldx = xworld ok end /* release resources */ TLN_DeleteTilemap(foreground) TLN_DeleteBitmap(middleground) TLN_CloseResourcePack() TLN_DeleteSpriteset(atlas) TLN_DeleteObjectList(props_list) (continues on next page) 61.2. More Samples 877
922.
Ring Documentation, Release1.21.2 (continued from previous page) TLN_DeleteWindow() TLN_Deinit() Screen Shot: Example (6): load "tilengine.ring" load "sin.ring" WIDTH = 400 HEIGHT = 240 FIXED_BITS = 16 LAYER_FOREGROUND = 0 LAYER_BACKGROUND = 1 MAX_LAYER = 2 MAP_HORIZON = 0 MAP_TRACK = 1 MAX_MAP = 2 road = NULL x = NULL y = NULL angle = NULL func main /* setup engine */ (continues on next page) 61.2. More Samples 878
923.
Ring Documentation, Release1.21.2 (continued from previous page) TLN_Init (WIDTH,HEIGHT, MAX_LAYER, 0, 0) TLN_SetRasterCallback ("raster_callback()") TLN_SetBGColor (0,0,0) /* load resources*/ TLN_SetLoadPath ("assets/smk") road = TLN_LoadTilemap ("track1.tmx", NULL) horizon = TLN_LoadTilemap ("track1_bg.tmx", NULL) /* startup display */ TLN_CreateWindow (NULL, 0) x = int2fix(-136) y = int2fix(336) s = 0 a = float2fix(0.2) angle = 0 BuildSinTable () /* main loop */ while (TLN_ProcessWindow ()) TLN_SetLayerTilemap (LAYER_FOREGROUND, horizon) TLN_SetLayerTilemap (LAYER_BACKGROUND, horizon) TLN_SetLayerPosition (LAYER_FOREGROUND, lerp(angle*2, 0,360, 0, ˓ →256), 24) TLN_SetLayerPosition (LAYER_BACKGROUND, lerp(angle, 0,360, 0, ˓ →256), 0) TLN_ResetLayerMode (LAYER_BACKGROUND) /* input */ if (TLN_GetInput (INPUT_LEFT)) angle-=2 elseif (TLN_GetInput (INPUT_RIGHT)) angle+=2 ok if (TLN_GetInput (INPUT_UP)) s += a if (s > int2fix(2)) s = int2fix(2) ok elseif (s >= a) s -= a ok if (TLN_GetInput (INPUT_DOWN)) s -= a if (s < -int2fix(2)) s = -int2fix(2) (continues on next page) 61.2. More Samples 879
924.
Ring Documentation, Release1.21.2 (continued from previous page) ok elseif (s <= -a) s += a ok if (s != 0) angle = angle%360 if (angle < 0) angle += 360 ok x += CalcSin (angle, s) y -= CalcCos (angle, s) ok /* render to window */ TLN_DrawFrame (0) end /* deinit */ TLN_DeleteTilemap (road) TLN_DeleteTilemap (horizon) TLN_DeleteWindow () TLN_Deinit () /* raster callback (virtual HBLANK) */ func raster_callback line = TLN_GETSCANLINE() if (line = 24) TLN_SetLayerTilemap (LAYER_BACKGROUND, road) TLN_SetLayerPosition (LAYER_BACKGROUND, fix2int(x), fix2int(y)) TLN_DisableLayer (LAYER_FOREGROUND) elseif (line >= 24) s0 = float2fix (0.2) s1 = float2fix (5.0) s = lerp (line, 24,HEIGHT, s0,s1) scale = fix2float (s) TLN_SetLayerTransform (LAYER_BACKGROUND, angle, WIDTH/2, HEIGHT, scale,␣ ˓ →scale) ok func lerp x,x0,x1,fx0,fx1 return (fx0) + ((fx1) - (fx0)) * ((x) - (x0))/((x1) - (x0)) func float2fix f return f * (1 << FIXED_BITS) func int2fix i return i << FIXED_BITS (continues on next page) 61.2. More Samples 880
925.
Ring Documentation, Release1.21.2 (continued from previous page) func fix2int f return f >> FIXED_BITS func fix2float f return f/(1 << FIXED_BITS) Screen Shot: Example (7): load "tilengine.ring" load "sin.ring" WIDTH = 320 HEIGHT = 192 COLUMNS = WIDTH/8 + 2 /* layers */ LAYER_FOREGROUND = 0 LAYER_BACKGROUND = 1 MAX_LAYER = 2 frame = 0 column = list(COLUMNS) /* entry point */ func main /* setup engine */ TLN_Init (WIDTH,HEIGHT, MAX_LAYER, 0, 5) (continues on next page) 61.2. More Samples 881
926.
Ring Documentation, Release1.21.2 (continued from previous page) TLN_SetRasterCallback ("raster_callback()") TLN_SetBGColor (0,0,0) /* load resources*/ TLN_SetLoadPath ("assets/tf3") foreground = TLN_LoadTilemap ("tf3_bg2.tmx", NULL) background = TLN_LoadTilemap ("tf3_bg3.tmx", NULL) TLN_SetLayerTilemap (LAYER_FOREGROUND, foreground) TLN_SetLayerTilemap (LAYER_BACKGROUND, background) BuildSinTable () # Convert the Column List to Array of Integers intArray = "" for item in column intArray += int2Bytes(item) next TLN_SetLayerColumnOffset (LAYER_BACKGROUND, :intArray) /* main loop */ TLN_CreateWindow (NULL, 0) while (TLN_ProcessWindow ()) /* scroll */ TLN_SetLayerPosition (LAYER_FOREGROUND, frame*3, 0) TLN_SetLayerPosition (LAYER_BACKGROUND, frame, 0) /* update column offset table */ for c=1 to COLUMNS column[c] = CalcSin (frame*5 + c*20, 3) next /* render to window */ TLN_DrawFrame (frame) frame++ end /* deinit */ TLN_DeleteTilemap (foreground) TLN_DeleteTilemap (background) TLN_Deinit () func raster_callback /* raster callback (virtual HBLANK) */ line = TLN_GetScanLine() (continues on next page) 61.2. More Samples 882