PureBasic Docs- Ideas/Help needed for a "We start" chapter!?

Everything else that doesn't fall into one of the other PB categories.
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2139
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

I've reviewed all the contributions (thanks again spikey!), tested and reworked the examples (being aware of most of the "rules", Timo has posted), and finally included the "beginners" or "user guide" into the reference manual, including a lot of links to the commands/keywords used in the guide.... :D

The current status of the project, how it looks, when included into the reference manual of PureBasic:
=> http://www.purearea.net/pb/english/manu ... rview.html

This is the overview of topics included for now:
Topics in this chapter:
- First steps
- Variables and Processing of variables
- Constants
- Decisions & Conditions
- Loops
- String Manipulation [missing]
- Storing data in memory
- Input & Output
- Displaying text output (Console)
- Building a graphical user interface (GUI)
- Displaying graphics output & simple drawing
- Structuring code in Procedures
- Compiler directives (for different behaviour on different OS)
- Reading and writing files
- Memory access
- Other Compiler keywords [missing]
- Other Library functions [missing]
- Advanced functions [missing]
- Some Tipps & Tricks [one example until now]
- ......
If you have any further suggestions, or best of all: do you have contributions to the missing topics, you're welcome to post them here! Thank you very much :)
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by IdeasVacuum »

Looks really good Andre.

In places longs are used where ints should be (re Freak's blog on apps for 64bit).
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2139
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

@IdeasVacuum: thanks for the note about longs/integers.

As I'm currently doing the german translation of the user guide for the PB4.61 final release, feel free to point me to needed changes in this chapter. Thanks!


@All: anyone with contributions to the missing contents (see above or first post)? Thank you!
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2139
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

Anyone with ideas for the missing chapters, especially "String manipulation"?
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2139
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

As I've nearly finished also the german translation of this new beginners chapter, there is a suggestion about implementing also a "beginners chapter for 3D stuff" here:
http://www.purebasic.fr/english/viewtop ... 08&start=5
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2139
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

Latest status of the project (in finished PB doc format) is now online.
See the already known link: http://www.purearea.net/pb/english/manu ... rview.html

Any further help is welcome! :)
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
spikey
Enthusiast
Enthusiast
Posts: 769
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Revised UserGuide - Displaying graphics output & simple draw

Post by spikey »

Andre,
I've revised this example as I discovered that the window layout wasn't very good on Ubuntu Linux.

Code: Select all

; Window
Enumeration
  #WinHarmonic
EndEnumeration

; Gadgets
Enumeration
  #txtPlot1
  #cboPlot1
  #txtPlot2
  #cboPlot2
  #imgPlot
EndEnumeration

; Image
Enumeration
  #drgPlot
EndEnumeration

; Image dimensions are used in several places so define constants.
#imgPlotX = 8
#imgPlotY = 40
#imgPlotW = 745
#imgPlotH = 645

; Event variables
Define.l Event, EventWindow, EventGadget, EventType, EventMenu

; Implementation
Procedure CreateWindow()
  ; Creates the window and gadgets.  
  
  If OpenWindow(#WinHarmonic, 30, 30, #imgPlotW + 20, #imgPlotH + 55, "Harmonics", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)
    
    ; This is a non-visual gadget used to draw the image, later its contents will be displayed in #imgPlot.
    CreateImage(#drgPlot, #imgPlotW - 5, #imgPlotH - 5, 24)
    
    ; Label for the Plot 1 combo.
    TextGadget(#txtPlot1, 2, 5, 50, 25, "Plot 1:")
    
    ; The Plot 1 combo.
    ComboBoxGadget(#cboPlot1, 55, 5, 150, 25)
    AddGadgetItem(#cboPlot1, 0, "Sin(X)")
    AddGadgetItem(#cboPlot1, 1, "Sin(X * 2)")
    AddGadgetItem(#cboPlot1, 2, "Sin(X * 3)")
    AddGadgetItem(#cboPlot1, 3, "Sin(X * 4)")
    AddGadgetItem(#cboPlot1, 4, "Sin(X * 5)")
    AddGadgetItem(#cboPlot1, 5, "Sin(X * 6)")
    
    ; Select Sin(X)
    SetGadgetState(#cboPlot1, 0)
    
    ; Label for the Plot 2 combo.
    TextGadget(#txtPlot2, 230, 5, 50, 25, "Plot 2:")
    
    ; The Plot 2 combo.
    ComboBoxGadget(#cboPlot2, 280, 5, 150, 25)
    AddGadgetItem(#cboPlot2, 0, "Sin(X)")
    AddGadgetItem(#cboPlot2, 1, "Sin(X * 2)")
    AddGadgetItem(#cboPlot2, 2, "Sin(X * 3)")
    AddGadgetItem(#cboPlot2, 3, "Sin(X * 4)")
    AddGadgetItem(#cboPlot2, 4, "Sin(X * 5)")
    AddGadgetItem(#cboPlot2, 5, "Sin(X * 6)")
    
    ; Select Sin(X * 2), otherwise the initial display is a bit uninteresting.
    SetGadgetState(#cboPlot2, 1)
    
    ; The visual image gadget on the window.
    ImageGadget(#imgPlot, #imgPlotX, #imgPlotY, #imgPlotW, #imgPlotH, 0, #PB_Image_Border)
    
  EndIf
  
EndProcedure

Procedure PlotAxes()
  ; Draws the axes on the image #drgPlot.
  
  ; Send drawing commands to #drgPlot.
  StartDrawing(ImageOutput(#drgPlot))
  
  ; Draw a white background.
  Box(0, 0, ImageWidth(#drgPlot), ImageHeight(#drgPlot), RGB(255, 255, 255))
  
  ; Draw the axes in black.
  Line(1, 1, 1, ImageHeight(#drgPlot) - 2, RGB(0, 0, 0))
  Line(1, (ImageHeight(#drgPlot) - 2) /2, ImageWidth(#drgPlot) -2, 1, RGB(0, 0, 0))
  
  ; Finished drawing.
  StopDrawing()
EndProcedure

Procedure PlotLegend(alngPlot1, alngPlot2)
  ; Draws the legend on the image #drgPlot.
  
  Protected.s strFunc1, strFunc2, strLabel1, strLabel2, strLabel3
  
  ; Set label text 1.
  If alngPlot1 = 0 
    strFunc1 = "Sin(X)"
    
  Else
    strFunc1 = "Sin(X * " + StrU(alngPlot1 + 1) + ")"
    
  EndIf
  
  ; Set label text 2.
  If alngPlot2 = 0 
    strFunc2 = "Sin(X)"
    
  Else
    strFunc2 = "Sin(X * " + StrU(alngPlot2 + 1) + ")"
    
  EndIf
  
  ; Set label text.
  strLabel1 = "Y = " + strFunc1
  strLabel2 = "Y = " + strFunc2
  strLabel3 = "Y = " + strFunc1 + " + " + strFunc2 
  
  ; Draw legend.
  StartDrawing(ImageOutput(#drgPlot))
  
  ; Box.
  DrawingMode(#PB_2DDrawing_Outlined)
  Box(20, 10, TextWidth(strLabel3) + 85, 80, RGB(0, 0, 0))
  
  ; Label 1.
  Line(30, 30, 50, 1, RGB(0, 0, 255))
  DrawText(95, 22, strLabel1, RGB(0, 0, 0), RGB(255, 255, 255))
  
  ; Label 2.
  Line(30, 50, 50, 1, RGB(0, 255, 200))
  DrawText(95, 42, strLabel2, RGB(0, 0, 0), RGB(255, 255, 255))
  
  ; Label 3.
  Line(30, 70, 50, 1, RGB(255, 0, 0))
  DrawText(95, 62, strLabel3, RGB(0, 0, 0), RGB(255, 255, 255))
  
  StopDrawing()
  
EndProcedure

Procedure PlotFunction(alngPlot1, alngPlot2)
  ; Draws the waveforms on the image #drgPlot.
  
  Protected.l lngSX, lngEX
  Protected.f fltRad1, fltRad2, fltSY1, fltEY1, fltSY2, fltEY2, fltSY3, fltEY3
  
  StartDrawing(ImageOutput(#drgPlot))
  
  ; Set initial start points for each wave.
  lngSX = 1
  fltSY1 = ImageHeight(#drgPlot) / 2
  fltSY2 = fltSY1
  fltSY3 = fltSY1
  
  ; Plot wave forms.
  For lngEX = 1 To 720
    ; Sine function works in radians, so convert from degrees and calculate sine.
    
    ; Function 1
    If alngPlot1 = 0 
      fltRad1 = Sin(Radian(lngEX))
    Else
      ; If the function should have a multiplier, account for this.
      fltRad1 = Sin(Radian(lngEX) * (alngPlot1 + 1))
    EndIf
    
    ; Function 2
    If alngPlot2 = 0 
      fltRad2 = Sin(Radian(lngEX))
    Else
      fltRad2 = Sin(Radian(lngEX) * (alngPlot2 + 1))
    EndIf
    
    ; Plot function 1 in blue.
    ; Calculate end Y point.
    fltEY1 = (ImageHeight(#drgPlot) / 2) + (fltRad1 * 100)
    ; Draw a line from the start point to the end point.
    LineXY(lngSX, fltSY1, lngEX, fltEY1, RGB(0, 0, 255))
    ; Update the next start Y point to be the current end Y point.
    fltSY1 = fltEY1
    
    ; Plot function 2 in green.
    fltEY2 = (ImageHeight(#drgPlot) / 2) + (fltRad2 * 100)
    LineXY(lngSX, fltSY2, lngEX, fltEY2, RGB(0, 255, 200))
    fltSY2 = fltEY2
    
    ; Plot harmonic in red.
    fltEY3 = (ImageHeight(#drgPlot) / 2) + ((fltRad1 + fltRad2) * 100)
    LineXY(lngSX, fltSY3, lngEX, fltEY3, RGB(255, 0, 0))
    fltSY3 = fltEY3
    
    ; Update the start X point to be the current end X point.
    lngSX = lngEX
  Next lngEX
  
  StopDrawing()
  
EndProcedure

;- Main
CreateWindow()
PlotAxes()
PlotLegend(GetGadgetState(#cboPlot1), GetGadgetState(#cboPlot2))
PlotFunction(GetGadgetState(#cboPlot1), GetGadgetState(#cboPlot2))

; Reload the image gadget now drawing is complete.
ImageGadget(#imgPlot, #imgPlotX, #imgPlotY, #imgPlotW, #imgPlotH, ImageID(#drgPlot), #PB_Image_Border)

;- Event loop
Repeat
  Event = WaitWindowEvent()
  EventWindow = EventWindow()
  EventGadget = EventGadget()
  EventType = EventType()
  
  Select Event
    Case #PB_Event_Gadget
      If EventGadget = #txtPlot1 Or EventGadget = #txtPlot2
        ; Do nothing.
      ElseIf EventGadget = #imgPlot
        ; Do nothing.
      ElseIf EventGadget = #cboPlot1 Or EventGadget = #cboPlot2
        ; If one of the combo boxes changed, redraw the image.
        PlotAxes()
        PlotLegend(GetGadgetState(#cboPlot1), GetGadgetState(#cboPlot2))
        PlotFunction(GetGadgetState(#cboPlot1), GetGadgetState(#cboPlot2))
        ImageGadget(#imgPlot, #imgPlotX, #imgPlotY, #imgPlotW, #imgPlotH, ImageID(#drgPlot), #PB_Image_Border)
      EndIf
    Case #PB_Event_CloseWindow
      If EventWindow = #WinHarmonic
        CloseWindow(#WinHarmonic)
        Break
      EndIf
  EndSelect
ForEver
oldefoxx
Enthusiast
Enthusiast
Posts: 532
Joined: Fri Jul 25, 2003 11:24 pm

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by oldefoxx »

I use Google a lot to search out points of interest related to PureBasic. For instance,
I might use the phrase "purebasic console" to see if I can find anything anywhere that
relates to both purebasic and to console. If I want to know if there is anything on
the forums, I might enter instead "site:www.purebasic.fr/english/ console".

Using search engines in this manner, I come onto many things that I would have missed
otherwise. I found Horst S. website and he has a lot of good stuff there for downloading.
And he is not the only one.

My thought is that you should devote a bit of a Beginner's Book to the notion of searching
for helpful code, downloading it or copying it (say out of the Help files) into their own
file(s), and how Include files can be used to make that code a part of any program in the
the future where they might want to make use of it.
has-been wanna-be (You may not agree with what I say, but it will make you think).
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2139
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

spikey wrote: I've revised this example as I discovered that the window layout wasn't very good on Ubuntu Linux.
Thanks, spikey. I've updated the example in the manual.


Any further contributions (see missing chapters) are welcome, if anyone is able to and willing to share... :D
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
spikey
Enthusiast
Enthusiast
Posts: 769
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Multiple Instances of a Single Window

Post by spikey »

If you’ve looked at the help articles for the OpenWindow command or for any of the gadget creation commands (for example ButtonGadget) or if you have experimented with the Visual Designer tool, you may have noticed references to a special constant called #PB_Any. In this article we’re going to look into this a little further to find out why its so important.

So far all of our examples have used a group of constants, an enumeration, to identify a single window and each gadget on that window. This is fine in the simple programs we’ve demonstrated so far but presents a problem in more complex programs – Only one of each of these windows can exist at any one time.

So what happens if a program needs to provide more than one copy of a window? Maybe to have several files open at once or possibly to provide several different views of the same file.

This is where the special #PB_Any constant comes in. When this constant is used as the argument to the functions that support it, a unique reference number is automatically generated and returned as a result of the function.

Providing we keep track of all these references we can use this to our advantage. Organising this is a little more complex than the examples we’ve seen so far – but the increased flexibility that it can provide makes this well worth the effort.

This example program provides a window upon which a regular polygon is drawn in blue on a circumscribing grey circle. A combo box is provided to allow a selection of polygons to be drawn. A menu is provided to allow the creation of new windows or the closing of the current window.

There are several things to notice about this program:-

In the Enumerations section, note that there are only enumerations for the menu items. These will be shared on all the windows – although each window will have its own menu bar.

In the Structures section, note that the POLYGONWINDOW structure contains four integer values and so provides a place to store references for a menu bar, a label, a combo box and an image plot.

In the Variables section note that in addition to the usual variables to receive event details, a map called ActiveWindows is created using the POLYGONWINDOW Structure previously defined.

Look at the CreatePolygonWindow procedure.
When we create the window we capture the result of the OpenWindow function in a variable called ThisWindow. We then convert this to a string value and use this as the key for a new map entry.

When we then create the menu, label, combo and image gadgets on the new window the references returned by these functions are stored in the map too.

Look at the ResizePolygonWindow procedure.
Notice how the value of EvtWindow is passed in from the event loop into this procedure. This value is then used to retrieve child control references from the map and these references are then used to resize the gadgets.

Look at the DestroyPolygonWindow procedure.
Here the child control references are removed from the map when a window is closed.
If the map size reaches zero – there are no more open windows and DestroyPolygonWindow sets an event flag to tell the program to end.

Start the program up.
· Use the New Window menu item to open up two or three new polygon windows.
· Use the combo box in each one to select a different shape – notice that each window works independently of all the others.
· Resize some of the windows – notice that they can all be resized independently of each other and that the polygon resizes with the window too.

Finally, note that a map isn’t the only way to achieve this effect, a LinkedList or an Array could be used to do the job too, if you prefer, although the code to implement these alternatives would need to be slightly different to that presented here because of the differences in the way those collections work.

Code: Select all

; Compiler Directives
EnableExplicit

; Constants
CompilerIf Defined(Blue, #PB_Constant) = #False
  #Blue = 16711680
  #Gray = 8421504
  #White = 16777215
CompilerEndIf

;- Enumerations
; The menu commands will be the same on all the windows.
Enumeration
  #mnuNew
  #mnuClose
EndEnumeration

;- Structures
; This structure will hold references to the unique elements of a window.
Structure POLYGONWINDOW
  Menu.i
  LabelSides.i
  ComboSides.i
  ImagePlot.i
EndStructure

;- Variables

; This map uses the previously defined structure to hold references for all the open windows.
NewMap ActiveWindows.POLYGONWINDOW()

; Event variables.
Define.i Evt, EvtWindow, EvtGadget, EvtType, EvtMenu, EvtQuit
Define.s EvtWindowKey

; Implementation.
Procedure.i CreatePolygonWindow()
  
  ; Creates a new window and gadgets, adding it and its child gadgets to the tracking map.
  
  Shared ActiveWindows()
  Protected.i ThisWindow
  Protected.s ThisKey
  
  ThisWindow = OpenWindow(#PB_Any, 50, 50, 300, 300, "Polygon", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)  
  
  WindowBounds(ThisWindow, 250, 250, #PB_Ignore, #PB_Ignore)
  
  If ThisWindow
    
    ; Maps take a string value as key so convert the integer ThisWindow to a string.
    ThisKey = StrU(ThisWindow)
    
    ; Add a map element to hold the new gadget references.
    AddMapElement(ActiveWindows(), ThisKey)
    
    ; Create the menu bar.
    ActiveWindows(ThisKey)\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow))
    MenuTitle("Window")
    MenuItem(#mnuNew, "New Window")
    MenuItem(#mnuClose, "Close Window")
    
    ; Create the child gadgets and store their references in the map.
    With ActiveWindows()
      
      ; A label for the combo.
      \LabelSides = TextGadget(#PB_Any, 5, 5, 150, 20, "Number of Sides:")
      
      ; The Sides combo.
      \ComboSides = ComboBoxGadget(#PB_Any, 160, 5, 100, 25)
      AddGadgetItem(\ComboSides, 0, "Triangle")
      AddGadgetItem(\ComboSides, 1, "Diamond")
      AddGadgetItem(\ComboSides, 2, "Pentagon")
      AddGadgetItem(\ComboSides, 3, "Hexagon")
      AddGadgetItem(\ComboSides, 4, "Heptagon")
      AddGadgetItem(\ComboSides, 5, "Octagon")
      AddGadgetItem(\ComboSides, 6, "Nonagon")
      AddGadgetItem(\ComboSides, 7, "Decagon")
      
      ; Select Triangle.
      SetGadgetState(\ComboSides, 0)
      
      ; The visual image gadget on the window.
      \ImagePlot = ImageGadget(#PB_Any, 5, 35, 290, 240, 0, #PB_Image_Border)
      
    EndWith
    
  EndIf
  
  ; Return the reference to the new window.
  ProcedureReturn ThisWindow
  
EndProcedure

Procedure DestroyPolygonWindow(Window.i)
  
  ; Remove Window from the ActiveWindows map, close the window and set the quit flag, if appropriate.
  
  Shared EvtQuit, ActiveWindows()
  Protected.s ThisKey
  
  ; Convert the integer Window to a string.
  ThisKey = StrU(Window)
  
  ; Delete the map entry.
  DeleteMapElement(ActiveWindows(), ThisKey)
  
  ; Close the window.
  CloseWindow(Window)
  
  ; Check if there are still open windows.
  If MapSize(ActiveWindows()) = 0
    EvtQuit = #True
  EndIf
  
EndProcedure

Procedure.i ResizePolygonWindow(Window.i)
  
  ; Resize the child gadgets on Window.
  ; In practice only the ImageGadget needs to be resized in this example.
  
  Shared ActiveWindows()
  Protected.i ThisImage
  Protected.i X, Y, W, H
  Protected.s ThisKey
  
  ; Obtain references to the affected gadgets from the map.
  ThisKey = StrU(Window)
  ThisImage = ActiveWindows(ThisKey)\ImagePlot
  
  ; Resize gadgets.
  W = WindowWidth(Window) - 15
  H = WindowHeight(Window) - 70
  ResizeGadget(ThisImage, #PB_Ignore, #PB_Ignore, W, H)
  
EndProcedure

Procedure PlotPolygon(Window.i)
  
  ; Draw the polygon image and transfer to the on screen gadget.
  
  Shared ActiveWindows()
  Protected.f Radius, OriginX, OriginY, StartX, StartY, EndX, EndY
  Protected.i Sides, Vertex, Width, Height, ThisCombo, ThisImage, ThisPlot
  Protected.s ThisKey
  
  ; Obtain references to the affected gadgets from the map.
  ThisKey = StrU(Window)
  ThisCombo = ActiveWindows(ThisKey)\ComboSides
  ThisImage = ActiveWindows(ThisKey)\ImagePlot
  
  ; Calculate dimensions and origin.
  Sides = GetGadgetState(ThisCombo) + 3
  Width = GadgetWidth(ThisImage) - 4
  Height = GadgetHeight(ThisImage) - 4
  OriginX = Width/2
  OriginY = Height/2
  If Width < Height
    Radius = OriginX - 50
  Else
    Radius = OriginY - 50
  EndIf
  
  ; Create a new image.
  ThisPlot = CreateImage(#PB_Any, Width, Height)
  StartDrawing(ImageOutput(ThisPlot))
  
  ; Draw a white background.
  Box(0, 0, Width, Height, #White)
  
  ; Draw a gray circumscribing circle.
  Circle(OriginX, OriginY, Radius, #Gray)
  
  ; Draw the polygon.
  For Vertex = 0 To Sides
    
    ; Calculate side start point.
    StartX =  OriginX + (Radius * Cos(2 * #PI * Vertex/Sides))
    StartY = OriginY + (Radius * Sin(2 * #PI * Vertex/Sides)) 
    
    ; and end point.
    EndX = OriginX + (Radius * Cos(2 * #PI * (Vertex + 1)/Sides))
    EndY = OriginY + (Radius * Sin(2 * #PI * (Vertex + 1)/Sides))
    
    ; Draw the side in blue.
    LineXY(StartX, StartY, EndX, EndY, #Blue)
    
  Next Vertex
  
  ; Fill the polygon in blue
  FillArea(OriginX, OriginY, #Blue, #Blue)
  
  StopDrawing()
  
  ; Transfer the image contents to the visible gadget.
  SetGadgetState(ThisImage, ImageID(ThisPlot))
  
  ; Destroy the temporary image.
  FreeImage(ThisPlot)
  
EndProcedure

;- Main

; Create the first window.
EvtWindow = CreatePolygonWindow()
ResizePolygonWindow(EvtWindow)
PlotPolygon(EvtWindow)

;- Event loop
Repeat
  
  Evt = WaitWindowEvent()
  EvtWindow = EventWindow()
  EvtWindowKey = StrU(EvtWindow)
  EvtGadget = EventGadget()
  EvtType = EventType()
  EvtMenu = EventMenu()
  
  Select Evt
      
    Case #PB_Event_Gadget
      ; A gadget event has occurred.
      If EvtGadget = ActiveWindows(EvtWindowKey)\LabelSides
        ; Do nothing.
        
      ElseIf EvtGadget = ActiveWindows(EvtWindowKey)\ComboSides
        ; If the combo box changes, redraw the image.
        PlotPolygon(EvtWindow)
        
        ; Update the windows title to reflect the new shape.
        SetWindowTitle(EvtWindow, GetGadgetText(ActiveWindows(EvtWindowKey)\ComboSides))
        
      ElseIf EvtGadget = ActiveWindows(EvtWindowKey)\ImagePlot
        ; Do nothing.
        
      EndIf
      
    Case #PB_Event_Menu
      ; A menu event has occurred.
      If EvtMenu = #mnuNew
        EvtWindow = CreatePolygonWindow()
        ResizePolygonWindow(EvtWindow)
        PlotPolygon(EvtWindow)
        
      ElseIf EvtMenu = #mnuClose
        DestroyPolygonWindow(EvtWindow)
        
      EndIf
      
    Case #PB_Event_Repaint
      ; A window's content has been invalidated.
      PlotPolygon(EvtWindow)
      
    Case #PB_Event_SizeWindow
      ; A window has been resized.
      ResizePolygonWindow(EvtWindow)
      PlotPolygon(EvtWindow)
      
    Case #PB_Event_CloseWindow
      ; A window has been closed.
      DestroyPolygonWindow(EvtWindow)
      
  EndSelect
  
Until EvtQuit = #True
User avatar
spikey
Enthusiast
Enthusiast
Posts: 769
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Multiple Instances of Multiple Windows

Post by spikey »

In the previous article we examined one way in which a program can support multiple instances of a single type of window. In this one we are going to extend this concept further – developing a program that can support multiple instances of several different types of window, in this case three:-

The Button window – contains a list view and two buttons labelled Add and Remove. When the Add button is clicked a random integer is added to the list view, when the remove button is clicked the currently highlighted entry in the list view is removed.

The Date window – contains a list view and two buttons in the same way as the Button window but also contains a calendar gadget too, the window layout is altered to accommodate this additional control. When the Add button is clicked it is the currently selected date that is added to the list view.

The Track window – contains two track bars, with a value between 0 and 100, and a string gadget. When the track bars are moved the string gadget is updated with the value of the second track bar subtracted from the first.

Each window contains a menu bar with items to create a new instance of any of the three supported window types or to close the current window.

Things to notice about this program are:

In the Structures section 4 structures are defined. The first, BASEWINDOW, defines a WindowClass value and a Menu value – these values are common to each window type.

The remaining structures extend the BASEWINDOW structure with values for each of the unique controls that they require and which are not provided for by the BASEWINDOW structure.

In the Variables section note that again a map called ActiveWindows is created, however this time it is of integer type, it doesn’t use any of the defined structures. There is a good reason for this, we need to store three different structure types to make this program work – and we can’t do this in a single map.

Also notice that *EvtGadgets is defined using the BASEWINDOW structure.

Now look at the CreateButtonWindow procedure. As before we use #PB_Any to create the window and all the gadgets.

However this time the results are not stored directly in the ActiveWindows map. Instead we use the AllocateMemory function to allocate memory for a BUTTONWINDOW structure, we then store a pointer to this memory allocation in the ActiveWindows map. This is how we get around the problem of not being able to store all three of the different structures in the same map.

We also set the WindowClass value in the structure to #WindowClassButton to indicate which type of window, and therefore which type of structure, has been created – we will need to know this later on.

There are two more CreateWindow procedures this time – one for each class of the other window types. They work in a similar way to that described, differing only where the window’s gadgets are different and setting a different value in WindowClass.

Similarly we provide DestroyWindow and ResizeWindow procedures to take care of these functions.

We also provide a new procedure – EventsButtonWindow. This procedure knows what to do when any of the gadgets on the window are activated by the user. Similar procedures are provided for the other window types too.

In all these procedures we use the ActiveWindows map to retrieve the pointer to the memory allocation. We can then use this pointer to retrieve the references to the actual controls that we need to work with in each of these procedures:-
*ThisData = ActiveWindows(ThisKey)
*ThisData\ListView …

Each procedure only knows how to handle one type of window – so before we start work we check the WindowClass value to make sure that a window of the correct type has been supplied as the argument something like this:-
If *ThisData\WindowClass <> #WindowClassButton

The event loop is a bit different too. For each event type there is a determination like this:-
; Use *EvtGadgets\WindowClass to call the correct resize window procedure.
Select *EvtGadgets\WindowClass …

Although the memory allocations actually made by the CreateWindow procedures will be of the BUTTONWINDOW, DATEWINDOW or TRACKWINDOW type we can use *EvtGadgets this way because it is defined as the BASEWINDOW type and BASEWINDOW is the ancestor structure to the other structures.

Providing we don’t attempt to change any of the stored values using *EvtGadgets – and we’ve no reason to do so – all should be well.

Finally, we despatch the recorded event values in EvtWindow, EvtGadget, EvtType straight through to the event procedures and let them worry about getting the jobs done.

Code: Select all

;- Constants
#DateFormat = "%dd/%mm/%yyyy"

;- Enumerations
Enumeration 
  #WindowClassButton = 1
  #WindowClassDate
  #WindowClassTrack
EndEnumeration

; The menu commands will be the same on all the windows.
Enumeration
  #mnuNewButton
  #mnuNewDate
  #mnuNewTrack
  #mnuClose
EndEnumeration

;- Structures
Structure BASEWINDOW
  WindowClass.i 
  Menu.i  
EndStructure

Structure BUTTONWINDOW Extends BASEWINDOW
  ListView.i
  AddButton.i
  RemoveButton.i  
EndStructure

Structure DATEWINDOW Extends BASEWINDOW
  Calendar.i
  AddButton.i
  RemoveButton.i
  ListView.i
EndStructure

Structure TRACKWINDOW Extends BASEWINDOW
  TrackBar1.i
  TrackBar2.i
  Label.i
  Difference.i
EndStructure

;- Variables
; This map will track all the active windows as before, however as the structure for each window class
; differs we will be storing pointers to structures in the map not the gadget references directly.
NewMap ActiveWindows.i()

; These values will be used to give new windows a unique label.
Define.i Buttons, Dates, Tracks

; Event variables.
; Notice the type of *EvtGadgets.
Define.i Evt, EvtWindow, EvtGadget, EvtType, EvtMenu, EvtQuit
Define.s EvtWindowKey
Define *EvtGadgets.BASEWINDOW  

;- Button Window
Procedure.i CreateButtonWindow()
  
  ; Creates a new Button window and gadgets, adds it to the tracking map, 
  ; allocates memory for gadget references and stores these in the map.
  
  Shared Buttons, ActiveWindows()
  Protected *ThisData.BUTTONWINDOW
  Protected.i ThisWindow
  Protected.s ThisKey, ThisTitle
  
  ; Set the window caption.
  Buttons + 1
  ThisTitle = "Button Window " + StrU(Buttons)
  
  ; Open the window.
  ThisWindow = OpenWindow(#PB_Any, 30, 30, 225, 300, ThisTitle, #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)
  
  ; Set minimum window dimensions.
  WindowBounds(ThisWindow, 220, 100, #PB_Ignore, #PB_Ignore)
  
  ; Check that the OpenWindow command worked.
  If ThisWindow
    
    ; Convert the window reference to a string to use as the map key value.
    ThisKey = StrU(ThisWindow)
    
    ; Allocate memory to store the gadget references in.
    *ThisData = AllocateMemory(SizeOf(BUTTONWINDOW))
    
    ; Store the window reference and memory pointer values in the map.
    ActiveWindows(ThisKey) = *ThisData
    
  EndIf
  
  ; Check that the memory allocation worked.
  If *ThisData
    
    ; Set the window class.
    *ThisData\WindowClass = #WindowClassButton
    
    ; Create the menu bar.
    *ThisData\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow))
    
    ; If the menu creation worked, create the menu items.
    If *ThisData\Menu
      MenuTitle("Window")
      MenuItem(#mnuNewButton, "New Button Window")
      MenuItem(#mnuNewDate, "New Date Window")
      MenuItem(#mnuNewTrack, "New Track Window")
      MenuItem(#mnuClose, "Close Window")
    EndIf
    
    ; Create the window gadgets.
    *ThisData\ListView = ListViewGadget(#PB_Any, 10, 10, 200, 225)
    *ThisData\AddButton = ButtonGadget(#PB_Any, 15, 245, 90, 30, "Add")
    *ThisData\RemoveButton = ButtonGadget(#PB_Any, 115, 245, 90, 30, "Remove")
    
  EndIf
  
  ; Return the reference to the new window.
  ProcedureReturn ThisWindow
  
EndProcedure

Procedure.i DestroyButtonWindow(Window.i)
  
  ; Remove Window from the ActiveWindows map, release the allocated memory,
  ; close the window and set the quit flag, if appropriate.
  
  Shared EvtQuit, ActiveWindows()
  Protected *ThisData.BUTTONWINDOW
  Protected.s ThisKey
  
  ; Convert the integer Window to a string.
  ThisKey = StrU(Window)
  
  ; Obtain the reference structure pointer.
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained, if not stop.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop.
  If *ThisData\WindowClass <> #WindowClassButton
    ProcedureReturn #False
  EndIf
  
  ; Release the memory allocation.
  FreeMemory(*ThisData)
  
  ; Delete the map entry.
  DeleteMapElement(ActiveWindows(), ThisKey)
  
  ; Close the window.
  CloseWindow(Window)
  
  ; Check if there are still open windows.
  If MapSize(ActiveWindows()) = 0
    EvtQuit = #True
  EndIf
  
  ; Set the successful return value.
  ProcedureReturn #True
  
EndProcedure

Procedure.i ResizeButtonWindow(Window.i)
  
  ; Resize the child gadgets on Window.
  
  Shared ActiveWindows()
  Protected *ThisData.BUTTONWINDOW
  Protected.i X, Y, W, H
  Protected.s ThisKey
  
  ; Obtain the reference structure pointer.
  ThisKey = StrU(Window)
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained, if not stop.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop.
  If *ThisData\WindowClass <> #WindowClassButton
    ProcedureReturn #False
  EndIf
  
  ; Resize list view.
  W = WindowWidth(Window) - 25
  H = WindowHeight(Window) - 85
  ResizeGadget(*ThisData\ListView, #PB_Ignore, #PB_Ignore, W, H)
  
  ; Recentre buttons.
  X = WindowWidth(Window)/2 - 95
  Y = WindowHeight(Window) - 65
  ResizeGadget(*ThisData\AddButton, X, Y, #PB_Ignore, #PB_Ignore)
  
  X = WindowWidth(Window)/2 + 5
  ResizeGadget(*ThisData\RemoveButton, X, Y, #PB_Ignore, #PB_Ignore)
  
  ProcedureReturn #True
  
EndProcedure

Procedure.i EventsButtonWindow(Window, Gadget, Type)
  
  ; Handle events for a button window.
  
  Shared Buttons, ActiveWindows()
  Protected *ThisData.BUTTONWINDOW
  Protected.i NewValue, Index
  Protected.s ThisKey
  
  ; Convert the integer Window to a string.
  ThisKey = StrU(Window)
  
  ; Obtain the reference structure pointer.
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained, if not stop.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop.
  If *ThisData\WindowClass <> #WindowClassButton
    ProcedureReturn #False
  EndIf

  Select Gadget
      
    Case *ThisData\AddButton
      NewValue = Random(2147483647)
      AddGadgetItem(*ThisData\ListView, -1, StrU(NewValue))
      
    Case *ThisData\RemoveButton
      Index = GetGadgetState(*ThisData\ListView)
      If Index >= 0 And Index <= CountGadgetItems(*ThisData\ListView)
        RemoveGadgetItem(*ThisData\ListView, Index)
      EndIf
      
    Case *ThisData\ListView
      ; Do nothing.
      
  EndSelect
  
EndProcedure

;- Date Window
Procedure.i CreateDateWindow()
  
  ; Creates a new Date window and gadgets, adds it to the tracking map, 
  ; allocates memory for gadget references and stores these.
  
  Shared Dates, ActiveWindows()
  Protected *ThisData.DATEWINDOW
  Protected.i ThisWindow
  Protected.s ThisKey, ThisTitle
  
  Dates + 1
  ThisTitle = "Date Window " + StrU(Dates)
  ThisWindow = OpenWindow(#PB_Any, 30, 30, 310, 420, ThisTitle , #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)
  
  ; Set minimum window dimensions.
  WindowBounds(ThisWindow, 310, 245, #PB_Ignore, #PB_Ignore)
    
  ; Allocate memory to store the gadget references in.
  If ThisWindow
    ThisKey = StrU(ThisWindow)
    *ThisData = AllocateMemory(SizeOf(DATEWINDOW))
    ActiveWindows(ThisKey) = *ThisData
  EndIf
  
  ; Check that the memory allocation worked.
  If *ThisData
    
    ; Set the window class.
    *ThisData\WindowClass = #WindowClassDate
    
    ; Create the menu bar.
    *ThisData\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow))
    
    ; If the menu creation worked, create the menu items.
    If *ThisData\Menu
      MenuTitle("Window")
      MenuItem(#mnuNewButton, "New Button Window")
      MenuItem(#mnuNewDate, "New Date Window")
      MenuItem(#mnuNewTrack, "New Track Window")
      MenuItem(#mnuClose, "Close Window")
    EndIf
    
    ; Create the window gadgets.
    *ThisData\Calendar = CalendarGadget(#PB_Any, 10, 10, 182, 162)
    *ThisData\AddButton = ButtonGadget(#PB_Any, 210, 10, 90, 30, "Add")
    *ThisData\RemoveButton = ButtonGadget(#PB_Any, 210, 45, 90, 30, "Remove")
    *ThisData\ListView = ListViewGadget(#PB_Any, 10, 187, 290, 200)
    
  EndIf
  
  ; Return the reference to the new window.
  ProcedureReturn ThisWindow
  
EndProcedure

Procedure.i DestroyDateWindow(Window.i)
  
  ; Remove Window from the ActiveWindows map, release the allocated memory,
  ; close the window and set the quit flag, if appropriate.
  
  Shared EvtQuit, ActiveWindows()
  Protected *ThisData.DATEWINDOW
  Protected.s ThisKey
  
  ; Convert the integer Window to a string.
  ThisKey = StrU(Window)
  
  ; Obtain the reference structure pointer.
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop as this procedure can't destroy this window.
  If *ThisData\WindowClass <> #WindowClassDate
    ProcedureReturn #False
  EndIf
  
  ; Release the memory allocation.
  FreeMemory(*ThisData)
  
  ; Delete the map entry.
  DeleteMapElement(ActiveWindows(), ThisKey)
  
  ; Close the window.
  CloseWindow(Window)
  
  ; Check if there are still open windows.
  If MapSize(ActiveWindows()) = 0
    EvtQuit = #True
  EndIf
  
  ; Set the successful return value.
  ProcedureReturn #True
  
EndProcedure

Procedure.i ResizeDateWindow(Window.i)
  
  ; Resize the child gadgets on Window.
  
  Shared ActiveWindows()
  Protected *ThisData.DATEWINDOW
  Protected.i X, Y, W, H
  Protected.s ThisKey
  
  ; Obtain the reference structure pointer.
  ThisKey = StrU(Window)
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained, if not stop.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop.
  If *ThisData\WindowClass <> #WindowClassDate
    ProcedureReturn #False
  EndIf
  
  ; Resize list view.
  W = WindowWidth(Window) - 20
  H = WindowHeight(Window) - 220
  ResizeGadget(*ThisData\ListView, #PB_Ignore, #PB_Ignore, W, H)
  
  ProcedureReturn #True
  
EndProcedure

Procedure.i EventsDateWindow(Window, Gadget, Type)
  
  ; Handle events for a Date window.
  
  Shared Buttons, ActiveWindows()
  Protected *ThisData.DATEWINDOW
  Protected.i NewValue, Index
  Protected.s ThisKey
  
  ; Convert the integer Window to a string.
  ThisKey = StrU(Window)
  
  ; Obtain the reference structure pointer.
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained, if not stop.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop.
  If *ThisData\WindowClass <> #WindowClassDate
    ProcedureReturn #False
  EndIf
  
  Select Gadget
      
    Case *ThisData\AddButton
      NewValue = GetGadgetState(*ThisData\Calendar)
      AddGadgetItem(*ThisData\ListView, -1, FormatDate(#DateFormat, NewValue))
      
    Case *ThisData\RemoveButton
      Index = GetGadgetState(*ThisData\ListView)
      If Index >= 0 And Index <= CountGadgetItems(*ThisData\ListView)
        RemoveGadgetItem(*ThisData\ListView, Index)
      EndIf
      
    Case *ThisData\Calendar, *ThisData\ListView
      ; Do nothing.
      
  EndSelect
  
EndProcedure

;- Track Window
Procedure.i CreateTrackWindow()
  
  ; Creates a new Track window and gadgets, adds it to the tracking map, 
  ; allocates memory for gadget references and stores these.
  
  Shared Tracks, ActiveWindows()
  Protected *ThisData.TRACKWINDOW
  Protected.i ThisWindow, ThisSum
  Protected.s ThisKey, ThisTitle
  
  Tracks + 1
  ThisTitle = "Track Bar Window " + StrU(Tracks)
  ThisWindow = OpenWindow(#PB_Any, 30, 30, 398, 130, ThisTitle, #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)
  
  ; Set minimum window dimensions.
  WindowBounds(ThisWindow, 135, 130, #PB_Ignore, 130)
  
  ; Allocate memory to store the gadget references in.
  If ThisWindow
    ThisKey = StrU(ThisWindow)
    *ThisData = AllocateMemory(SizeOf(TRACKWINDOW))
    ActiveWindows(ThisKey) = *ThisData
  EndIf
  
  ; Check that the memory allocation worked.
  If *ThisData
    
    ; Set the window class.
    *ThisData\WindowClass = #WindowClassTrack
    
    ; Create the menu bar.
    *ThisData\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow))
    
    ; If the menu creation worked, create the menu items.
    If *ThisData\Menu
      MenuTitle("Window")
      MenuItem(#mnuNewButton, "New Button Window")
      MenuItem(#mnuNewDate, "New Date Window")
      MenuItem(#mnuNewTrack, "New Track Window")
      MenuItem(#mnuClose, "Close Window")
    EndIf
    
    ; Create the window gadgets.
    *ThisData\TrackBar1 = TrackBarGadget(#PB_Any, 10, 10, 375, 25, 0, 100, #PB_TrackBar_Ticks)
    *ThisData\TrackBar2 = TrackBarGadget(#PB_Any, 10, 40, 375, 25, 0, 100, #PB_TrackBar_Ticks)
    *ThisData\Label = TextGadget(#PB_Any, 10, 75, 80, 25, "Difference:")
    *ThisData\Difference = StringGadget(#PB_Any, 90, 75, 290, 25, "0", #PB_String_ReadOnly)
    
  EndIf
  
  ; Return the reference to the new window.
  ProcedureReturn ThisWindow
  
EndProcedure

Procedure.i DestroyTrackWindow(Window.i)
  
  ; Remove Window from the ActiveWindows map, release the allocated memory,
  ; close the window and set the quit flag, if appropriate.
  
  Shared EvtQuit, ActiveWindows()
  Protected *ThisData.DATEWINDOW
  Protected.s ThisKey
  
  ; Convert the integer Window to a string.
  ThisKey = StrU(Window)
  
  ; Obtain the reference structure pointer.
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop as this procedure can't destroy this window.
  If *ThisData\WindowClass <> #WindowClassTrack
    ProcedureReturn #False
  EndIf
  
  ; Release the memory allocation.
  FreeMemory(*ThisData)
  
  ; Delete the map entry.
  DeleteMapElement(ActiveWindows(), ThisKey)
  
  ; Close the window.
  CloseWindow(Window)
  
  ; Check if there are still open windows.
  If MapSize(ActiveWindows()) = 0
    EvtQuit = #True
  EndIf
  
  ; Set the successful return value.
  ProcedureReturn #True
  
EndProcedure

Procedure.i ResizeTrackWindow(Window.i)
  
  ; Resize the child gadgets on Window.
  
  Shared ActiveWindows()
  Protected *ThisData.TRACKWINDOW
  Protected.i X, Y, W, H
  Protected.s ThisKey
  
  ; Obtain the reference structure pointer.
  ThisKey = StrU(Window)
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained, if not stop.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop.
  If *ThisData\WindowClass <> #WindowClassTrack
    ProcedureReturn #False
  EndIf
  
  ; Resize track bars.
  W = WindowWidth(Window) - 20
  ResizeGadget(*ThisData\TrackBar1, #PB_Ignore, #PB_Ignore, W, #PB_Ignore)
  ResizeGadget(*ThisData\TrackBar2, #PB_Ignore, #PB_Ignore, W, #PB_Ignore)
  
  ; Resize string.
  W = WindowWidth(Window) - 110
  ResizeGadget(*ThisData\Difference, #PB_Ignore, #PB_Ignore, W, #PB_Ignore)
  
  ProcedureReturn #True
  
EndProcedure

Procedure.i EventsTrackWindow(Window, Gadget, Type)
  
  ; Handle events for a Track window.
  
  Shared Buttons, ActiveWindows()
  Protected *ThisData.TRACKWINDOW
  Protected.i NewValue
  Protected.s ThisKey
  
  ; Convert the integer Window to a string.
  ThisKey = StrU(Window)
  
  ; Obtain the reference structure pointer.
  *ThisData = ActiveWindows(ThisKey)
  
  ; Check that a valid pointer was obtained, if not stop.
  If *ThisData = 0
    ProcedureReturn #False
  EndIf
  
  ; Check that it is the correct window type, if not stop.
  If *ThisData\WindowClass <> #WindowClassTrack
    ProcedureReturn #False
  EndIf
  
  Select Gadget
      
    Case *ThisData\TrackBar1, *ThisData\TrackBar2
      NewValue = GetGadgetState(*ThisData\TrackBar1) - GetGadgetState(*ThisData\TrackBar2)
      SetGadgetText(*ThisData\Difference, Str(NewValue))
      
    Case *ThisData\Label, *ThisData\Difference
      ; Do nothing.
      
  EndSelect
  
EndProcedure

;- Main

; Create the first window.
EvtWindow = CreateButtonWindow()
ResizeButtonWindow(EvtWindow)

;- Event loop
Repeat
  
  Evt = WaitWindowEvent()
  EvtWindow = EventWindow()
  EvtWindowKey = StrU(EvtWindow)
  EvtGadget = EventGadget()
  EvtType = EventType()
  EvtMenu = EventMenu()
  *EvtGadgets = ActiveWindows(EvtWindowKey)
  
  Select Evt
      
    Case #PB_Event_Gadget
      
      ; Use *EvtGadgets\WindowClass to despatch events to the correct event procedure.
      Select *EvtGadgets\WindowClass
          
        Case #WindowClassButton
          EventsButtonWindow(EvtWindow, EvtGadget, EvtType)
          
        Case #WindowClassDate
          EventsDateWindow(EvtWindow, EvtGadget, EvtType)
          
        Case #WindowClassTrack
          EventsTrackWindow(EvtWindow, EvtGadget, EvtType)
          
        Default
          ; Do nothing
          
      EndSelect
      
    Case #PB_Event_Menu
      
      Select EvtMenu
          
        Case #mnuNewButton
          EvtWindow = CreateButtonWindow()
          ResizeButtonWindow(EvtWindow)
          
        Case #mnuNewDate
          EvtWindow = CreateDateWindow()
          ResizeDateWindow(EvtWindow)
          
        Case #mnuNewTrack
          EvtWindow = CreateTrackWindow()
          ResizeTrackWindow(EvtWindow)
          
        Case #mnuClose
          
          ; Use *EvtGadgets\WindowClass to call the correct destroy window procedure.    
          Select *EvtGadgets\WindowClass
              
            Case #WindowClassButton
              DestroyButtonWindow(EvtWindow)
              
            Case #WindowClassDate
              DestroyDateWindow(EvtWindow)
              
            Case #WindowClassTrack
              DestroyTrackWindow(EvtWindow)
              
            Default
              ; Do nothing
              
          EndSelect
          
      EndSelect
      
    Case #PB_Event_CloseWindow
      
      ; Use *EvtGadgets\WindowClass to call the correct destroy window procedure.
      Select *EvtGadgets\WindowClass
          
        Case #WindowClassButton
          DestroyButtonWindow(EvtWindow)
          
        Case #WindowClassDate
          DestroyDateWindow(EvtWindow)
          
        Case #WindowClassTrack
          DestroyTrackWindow(EvtWindow)
          
        Default
          ; Do nothing
          
      EndSelect
      
    Case #PB_Event_SizeWindow     
      
      ; Use *EvtGadgets\WindowClass to call the correct resize window procedure.
      Select *EvtGadgets\WindowClass
          
        Case #WindowClassButton
          ResizeButtonWindow(EvtWindow)
          
        Case #WindowClassDate
          ResizeDateWindow(EvtWindow)
          
        Case #WindowClassTrack
          ResizeTrackWindow(EvtWindow)
          
        Default
          ; Do nothing
          
      EndSelect
      
  EndSelect
  
Until EvtQuit = #True
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2139
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

Hi spikey,

thanks a lot for your further contributions. :D
It's really a lot of text and code.... :wink:

I finished implementing the first new posting of 31th January 2013 into the 'Beginners' chapter of the PB manual. Need to do it for the other one too...

What I noticed, when testing the code on my MacBook, OS X 10.5.8:

- First example crashes with an IMA at WaitWindowEvent(), when I had several windows opened and then close the last one via the menu item 'Close window'. Also not the actual window is closed, but the previously opened window instead (maybe this is a OS X-specific issue, as there is only one menu-bar for all windows of a program).

- Similar problem with the second example when several windows are opened (IMA at WaitWindowEvent(), sometimes also a 'Pointer is null' on line 624 ( Select *EvtGadgets\WindowClass).
Furthermore I noticed a problem with the DateGadget: after moving from Feb'2013 to July'2013 and selecting 24th July 2013 as data in the gadget, it will output 23th July 2013 after clicking on 'Add'...

Don't know if this issues can be confirmed on Windows too!?


PS: about the 'String handling' chapter - would be good, if you can contribute it until tomorrow as the final release of PB5.10 is close... 8)
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
spikey
Enthusiast
Enthusiast
Posts: 769
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

String Manipulation article

Post by spikey »

Code: Select all

Define.s String1, String2, String3

String1 = "The quick brown fox jumps over the lazy dog."

; Left returns a number of characters from the left hand end of a string.
; Mid returns a number of characters from the given start location in the middle of a string.
; Right returns a number of characters from the right hand end of a string.
; Space returns the specified number of space characters as a string.
; Shows "The brown dog."
Debug "* Left, Mid and Right"
String2 = Left(String1, 3) + Space(1) + Mid(String1, 11, 5) + Space(1) + Right(String1, 4)
Debug String2

; CountString returns the number of instances of the second string in the first string, it is case sensitive.
; Shows 1.
Debug "* CountString"
Debug CountString(String1, "the")

; However the LCase (and UCase) functions can be used to switch a string to all lower (or upper) case
; Shows 2
Debug "* CountString and LCase"
String2 = LCase(String1)
Debug CountString(String2, "the")

; FindString can be used to find the location of one string within another.
; Shows 17.
Debug "* FindString"
Debug FindString(String1, "fox")

; RemoveString can be used to remove one string from within another.
; Shows The quick fox jumps over the lazy dog.
Debug "* RemoveString"
String2 = RemoveString(String1, " brown")
Debug String2

; ReplaceString can be used to change the occurence of a substring within another string.
; Shows The quick brown fox jumps over the sleeping dog.
Debug "* ReplaceString"
String2 = ReplaceString(String1, "lazy", "sleeping")
Debug String2

; StringByteLength returns the length of a string in bytes in the specified format, or the current default
; if one is not specified (excluding the terminating null).
Debug "* StringByteLength"
; Shows 44.
Debug StringByteLength(String1, #PB_Ascii)
; Shows 88.
Debug StringByteLength(String1, #PB_Unicode)

; StringField can be used to obtain an indexed substring from a target string. 
; Useful for converting strings to lists for example.
; StringField will work with space as a delimiter too 
; but hopefully this example makes the functions behaviour more apparent.
; Shows jumps.
Debug "* StringField"
String2 = ReplaceString(String1, " ", "\")
Debug String2
String3 = StringField(String2, 5, "\")
Debug String3

; Trim removes white space characters from the start and end of a given string.
; Similarly, LTrim acts on the left hand end (start) of a string and RTrim the right hand end.
Debug "* Trim, LTrim and RTrim"
String2 = Space(10) + String1 + Space(8)
Debug #DQUOTE$ + String2 + #DQUOTE$
String3 = Trim(String2)
Debug #DQUOTE$ + String3 + #DQUOTE$
String3 = LTrim(String2)
Debug #DQUOTE$ + String3 + #DQUOTE$
String3 = RTrim(String2)
Debug #DQUOTE$ + String3 + #DQUOTE$

; LSet sets a string to be a specific length from the left hand end, padding with spaces, 
; or other specified character, as necessary.
; If the string is already longer than the specified length it will be truncated.
Debug "*LSet"
Debug LSet("Abc", 10, "*")
Debug LSet("Abcd", 10, "*")
Debug LSet("Abcde", 10, "*")

; Similarly RSet pads a string from its right hand end.
Debug "* RSet"
Debug RSet("1.23", 10, "0")
Debug RSet("10.23", 10, "0")
Debug RSet("100.23", 10, "0")

; Str converts a signed quad value to a string, similarly StrF converts floats, 
; StrD converts doubles and StrU converts unsigned values, these two function have an optional 
; parameter to specify the number of decimal places to show.
Debug "* Str, StrF and StrD"
Debug Str(100)
Debug StrF(1.234, 3)
Debug StrD(123456.789, 3)

; Val will convert a string value into its numeric (quad) equivalent.
; ValD and ValF perform the same function for floats and doubles.
Debug "* Val"
Debug Val("123")

; Bin will convert a numeric value into its binary equivalent.
; Hex will convert one into its hexadecimal equivalent.
Debug "* Bin and Hex"
Debug Bin(19)
Debug Hex(19)

User avatar
spikey
Enthusiast
Enthusiast
Posts: 769
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by spikey »

Andre wrote: - First example crashes with an IMA at WaitWindowEvent(), when I had several windows opened and then close the last one via the menu item 'Close window'. Also not the actual window is closed, but the previously opened window instead (maybe this is a OS X-specific issue, as there is only one menu-bar for all windows of a program).

- Similar problem with the second example when several windows are opened (IMA at WaitWindowEvent(), sometimes also a 'Pointer is null' on line 624 ( Select *EvtGadgets\WindowClass).
Furthermore I noticed a problem with the DateGadget: after moving from Feb'2013 to July'2013 and selecting 24th July 2013 as data in the gadget, it will output 23th July 2013 after clicking on 'Add'...

Don't know if this issues can be confirmed on Windows too!?
Hmm - no can't replicate the IMA or date problem on Windows XP, 7 or Ubuntu...
I can't think of any reason why it should IMA at WaitWindowEvent off the top of my head and I don't have access to any Mac OS machines so I can't test for myself :(

Mea culpa on the null pointer however - there are several points where a theoretical null pointer could arise, I wasn't expecting them to go beyond theoretical though so I left some traps out to keep the example code a bit shorter - I'll put some more in... :lol:
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2139
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

spikey wrote: Hmm - no can't replicate the IMA or date problem on Windows XP, 7 or Ubuntu...
I can't think of any reason why it should IMA at WaitWindowEvent off the top of my head and I don't have access to any Mac OS machines so I can't test for myself :(

Mea culpa on the null pointer however - there are several points where a theoretical null pointer could arise, I wasn't expecting them to go beyond theoretical though so I left some traps out to keep the example code a bit shorter - I'll put some more in... :lol:
Thanks in advance.
You are absolutely right, when you think about descriptions and code examples as short as possible! :D

Just to let you know, what I've changed in your first code example (e.g. more "speaking" variable names), here is the updated code. You should use it as "base" for further updates.... Maybe also the "CompilerIf Defined...." at the color constants is not needed, as I read several times on the forum?

Code: Select all

  ; Compiler Directives
  EnableExplicit
  
  ; Constants
  CompilerIf Defined(Blue, #PB_Constant) = #False
    #Blue = 16711680
    #Gray = 8421504
    #White = 16777215
  CompilerEndIf
  
  ;- Enumerations
  ; The menu commands will be the same on all the windows.
  Enumeration
    #MenuNew
    #MenuClose
  EndEnumeration
  
  ;- Structures
  ; This structure will hold references to the unique elements of a window.
  Structure POLYGONWINDOW
    Menu.i
    LabelSides.i
    ComboSides.i
    ImagePlot.i
  EndStructure
  
  ;- Variables
  
  ; This map uses the previously defined structure to hold references for all the open windows.
  NewMap ActiveWindows.POLYGONWINDOW()
  
  ; Event variables.
  Define.i Event, EventWindow, EventGadget, EventType, EventMenu, EventQuit
  Define.s EventWindowKey
  
  ; Implementation.
  Procedure.i CreatePolygonWindow()
    ; Creates a new window and gadgets, adding it and its child gadgets to the tracking map.
    Shared ActiveWindows()
    Protected.i ThisWindow
    Protected.s ThisKey
    
    ThisWindow = OpenWindow(#PB_Any, 50, 50, 300, 300, "Polygon", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)  
    
    WindowBounds(ThisWindow, 250, 250, #PB_Ignore, #PB_Ignore)
    
    If ThisWindow
      ; Maps take a string value as key so convert the integer ThisWindow to a string.
      ThisKey = StrU(ThisWindow)
      
      ; Add a map element to hold the new gadget references.
      AddMapElement(ActiveWindows(), ThisKey)
      
      ; Create the menu bar.
      ActiveWindows(ThisKey)\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow))
      MenuTitle("Window")
      MenuItem(#MenuNew, "New Window")
      MenuItem(#MenuClose, "Close Window")
      
      ; Create the child gadgets and store their references in the map.
      With ActiveWindows()
        ; A label for the combo.
        \LabelSides = TextGadget(#PB_Any, 5, 5, 150, 20, "Number of Sides:")
        
        ; The Sides combo.
        \ComboSides = ComboBoxGadget(#PB_Any, 160, 5, 100, 25)
        AddGadgetItem(\ComboSides, 0, "Triangle")
        AddGadgetItem(\ComboSides, 1, "Diamond")
        AddGadgetItem(\ComboSides, 2, "Pentagon")
        AddGadgetItem(\ComboSides, 3, "Hexagon")
        AddGadgetItem(\ComboSides, 4, "Heptagon")
        AddGadgetItem(\ComboSides, 5, "Octagon")
        AddGadgetItem(\ComboSides, 6, "Nonagon")
        AddGadgetItem(\ComboSides, 7, "Decagon")
        
        ; Select Triangle.
        SetGadgetState(\ComboSides, 0)
        
        ; The visual image gadget on the window.
        \ImagePlot = ImageGadget(#PB_Any, 5, 35, 290, 240, 0, #PB_Image_Border)
      EndWith
    EndIf
    
    ; Return the reference to the new window.
    ProcedureReturn ThisWindow
  EndProcedure
  
  Procedure DestroyPolygonWindow(Window.i)
    ; Remove Window from the ActiveWindows map, close the window and set the quit flag, if appropriate.
    Shared EventQuit, ActiveWindows()
    Protected.s ThisKey
    
    ; Convert the integer Window to a string.
    ThisKey = StrU(Window)
    
    ; Delete the map entry.
    DeleteMapElement(ActiveWindows(), ThisKey)
    
    ; Close the window.
    CloseWindow(Window)
    
    ; Check if there are still open windows.
    If MapSize(ActiveWindows()) = 0
      EventQuit = #True
    EndIf
  EndProcedure
  
  Procedure.i ResizePolygonWindow(Window.i)
    ; Resize the child gadgets on Window.
    ; In practice only the ImageGadget needs to be resized in this example.
    Shared ActiveWindows()
    Protected.i ThisImage
    Protected.i X, Y, W, H
    Protected.s ThisKey
    
    ; Obtain references to the affected gadgets from the map.
    ThisKey = StrU(Window)
    ThisImage = ActiveWindows(ThisKey)\ImagePlot
    
    ; Resize gadgets.
    W = WindowWidth(Window) - 15
    H = WindowHeight(Window) - 70
    ResizeGadget(ThisImage, #PB_Ignore, #PB_Ignore, W, H)
  EndProcedure
  
  Procedure PlotPolygon(Window.i)
    ; Draw the polygon image and transfer it to the image gadget.
    Shared ActiveWindows()
    Protected.f Radius, OriginX, OriginY, StartX, StartY, EndX, EndY
    Protected.i Sides, Vertex, Width, Height, ThisCombo, ThisImage, ThisPlot
    Protected.s ThisKey
    
    ; Obtain references to the affected gadgets from the map.
    ThisKey = StrU(Window)
    ThisCombo = ActiveWindows(ThisKey)\ComboSides
    ThisImage = ActiveWindows(ThisKey)\ImagePlot
    
    ; Calculate dimensions and origin.
    Sides = GetGadgetState(ThisCombo) + 3
    Width = GadgetWidth(ThisImage) - 4
    Height = GadgetHeight(ThisImage) - 4
    OriginX = Width/2
    OriginY = Height/2
    If Width < Height
      Radius = OriginX - 50
    Else
      Radius = OriginY - 50
    EndIf
    
    ; Create a new image.
    ThisPlot = CreateImage(#PB_Any, Width, Height)
    StartDrawing(ImageOutput(ThisPlot))
    
    ; Draw a white background.
    Box(0, 0, Width, Height, #White)
    
    ; Draw a gray circumscribing circle.
    Circle(OriginX, OriginY, Radius, #Gray)
    
    ; Draw the polygon.
    For Vertex = 0 To Sides
      
      ; Calculate side start point.
      StartX =  OriginX + (Radius * Cos(2 * #PI * Vertex/Sides))
      StartY = OriginY + (Radius * Sin(2 * #PI * Vertex/Sides)) 
      
      ; and end point.
      EndX = OriginX + (Radius * Cos(2 * #PI * (Vertex + 1)/Sides))
      EndY = OriginY + (Radius * Sin(2 * #PI * (Vertex + 1)/Sides))
      
      ; Draw the side in blue.
      LineXY(StartX, StartY, EndX, EndY, #Blue)
      
    Next Vertex
    
    ; Fill the polygon in blue
    FillArea(OriginX, OriginY, #Blue, #Blue)
    
    StopDrawing()
    
    ; Transfer the image contents to the visible gadget.
    SetGadgetState(ThisImage, ImageID(ThisPlot))
    
    ; Destroy the temporary image.
    FreeImage(ThisPlot)
  EndProcedure
  
  ;- Main
  
  ; Create the first window.
  EventWindow = CreatePolygonWindow()
  ResizePolygonWindow(EventWindow)
  PlotPolygon(EventWindow)
  
  ;- Event loop
  Repeat
    Event = WaitWindowEvent()
    EventWindow = EventWindow()
    EventWindowKey = StrU(EventWindow)
    EventGadget = EventGadget()
    EventType = EventType()
    EventMenu = EventMenu()
    
    Select Event
      Case #PB_Event_Gadget
        ; A gadget event has occurred.
        If EventGadget = ActiveWindows(EventWindowKey)\LabelSides
          ; Do nothing.
          
        ElseIf EventGadget = ActiveWindows(EventWindowKey)\ComboSides
          ; If the combo box changes, redraw the image.
          PlotPolygon(EventWindow)
          
          ; Update the windows title to reflect the new shape.
          SetWindowTitle(EventWindow, GetGadgetText(ActiveWindows(EventWindowKey)\ComboSides))
          
        ElseIf EventGadget = ActiveWindows(EventWindowKey)\ImagePlot
          ; Do nothing.
          
        EndIf
        
      Case #PB_Event_Menu
        ; A menu event has occurred.
        If EventMenu = #MenuNew
          EventWindow = CreatePolygonWindow()
          ResizePolygonWindow(EventWindow)
          PlotPolygon(EventWindow)
          
        ElseIf EventMenu = #MenuClose
          DestroyPolygonWindow(EventWindow)
          
        EndIf
        
      Case #PB_Event_Repaint
        ; A window's content has been invalidated.
        PlotPolygon(EventWindow)
        
      Case #PB_Event_SizeWindow
        ; A window has been resized.
        ResizePolygonWindow(EventWindow)
        PlotPolygon(EventWindow)
        
      Case #PB_Event_CloseWindow
        ; A window has been closed.
        DestroyPolygonWindow(EventWindow)
        
    EndSelect
    
  Until EventQuit = #True
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
Post Reply