Graph Paper Maker, Part 2
Create your own customized graph paper
Issue: 7.3 (March/April 2009)
Author: Brad Rhine
Article Description: No description available.
Article Length (in bytes): 10,951
Starting Page Number: 52
RBD Number: 7316
7316.zip Updated: Monday, March 2, 2009 at 1:37 PM
Related Link(s): None
Known Limitations: None
Excerpt of article text...
Last time, we began work on Graph Paper Maker, a little utility that, appropriately enough, makes graph paper. So far, the application is pretty minimal, but that's okay, because it's a good start. This time, we're going to add preset graph paper styles, as well as the ability to add your own presets. Data Storage The first thing we need to figure out is how to store data for our graph paper presets. The easy way out would be to hardcode some preset data into our project file, but, as is often the case, the easy way is not the best way. That would limit us to those presets, and if we ever wanted to add more or change one, we'd need to recompile. Hardly ideal. Now, I'm a database administrator in my day job, and since most people usually reach for the tool they know how to use first, my first inclination was to use a database. However, a database is overkill for this project and will only complicate things. Besides, we already did a project using databases, and I don't want you all to think that I'm a one trick pony. I also pondered using XML, and astute readers will note that that was my intention at the close of the last column. However, I decided that for the purposes of this series, XML might be too much to get into. So I'll save XML for a future project. With all that in mind, we're going to be using plain text for our storage. Plain text has the advantage of being easily human readable, which is a tremendous aid in troubleshooting and debugging. That also makes that human editable, so that you or your users can create and modify preset files using a simple text editor. To begin with, our preset files will have a very simple structure. The first line will contain the name of the preset. Following that, we'll list the horizontal spacing, vertical spacing, margin size, and line color, each on its own line. This document format should prove to be very easy to read and write. However, another question looms large over us: where do we store the files? For Windows and Mac OS X, we'll create a folder inside the SpecialFolder.ApplicationData folder. Since that returns nil on Linux, we'll create a folder inside the SpecialFolder.UserHome folder. A Class Act Before we get too much further, let's create a class to represent a preset. We'll call it "preset" (clever, eh?). Preset will have five properties: Name as String, HorizontalSpacing as Double, VerticalSpacing as Double, Margin as Double, and LineColor as Color. We'll also give Preset a constructor method. A constructor is executed when a new instance of the class is created. And in our case, the constructor will take a parameter: F as FolderItem. Go ahead and give Preset a new method called Constructor, taking F as FolderItem as its parameter. The code for the method will look like this: Dim T As TextInputStream Dim V As Variant If F<>Nil Then T=F.OpenAsTextFile Name=T.ReadLine HorizontalSpacing=CDbl(T.ReadLine) VerticalSpacing=CDbl(T.ReadLine) Margin=CDbl(T.ReadLine) V=T.ReadLine LineColor=V.ColorValue End If In essence, this method will open up the preset file we give it, parse the values, and assign our Preset its properties. Since LineColor is a color, we had to work a little bit of Variant voodoo, but nothing too severe. Getting Ready The first thing we need to do is make a few changes to our interface. We'll need a place to display a list of presets, and for that, we'll use a listbox. I widened my main window to 400 and slid all of the existing controls all the way to the right. Then I added a listbox, which I called "PresetBox" on the left. I also added a button labeled "Save Preset" to the left of the "Create" button. We'll also need to check for our Preset folder and create it if it doesn't exist. The best time to do this is when the window is opening. I added the following code to my window's Open event. It first gets the right parent folder, depending on our platform. Then it checks for the GraphPaper Presets folder, and creates it if it doesn't exist. Next, it reads all the files in the GraphPaper Presets folder and instantiates a new Preset for each one, adding it to the listbox. Dim F, Parent As FolderItem Dim P As Preset Dim I As Integer #If TargetLinux Then Parent=SpecialFolder.UserHome #Else Parent=SpecialFolder.ApplicationData #EndIf If Parent<>Nil Then F=Parent.Child("GraphPaper Presets") If Not F.Exists Then F.CreateAsFolder End If If F<>Nil Then For I=1 To F.Count P=New Preset(F.Item(I)) PresetBox.AddRow P.Name PresetBox.CellTag(PresetBox.LastIndex,0)=P Next End If End If Notice that we store each preset in one of the listbox's CellTags. A CellTag is a special storage area that every cell of every listbox has. It can store any type of data, so it's very flexible and quite handy. Sneak Peek Now, you probably want to test this out. And if you run the project now, you won't see anything in the listbox. That's because we don't have any Presets yet. If you're itching to try it, create a plain text file (don't use Word or Pages or anything like that, just plain vanilla text) and put this in it: Gray Half Inch .5 .5 .5 &c666666 Save the file and place it in the GraphPaper Presets folder that your application created when it ran. When you run this time, you should see "Gray Half Inch" listed in the listbox. Nifty! Loading Presets Now we're ready for the fun part: loading Presets from the listbox. Remember that each row shows us the name of a preset, but hidden in the CellTag is the preset itself. To find out which row of the listbox our user has clicked on, we'll use the Change event. Here's where we pay the price for using some CellTag trickery. While the CellTag has a Preset in it, the listbox has no idea what a Preset is. It's storing in a Variant. In order to treat it like a Preset, we'll have to cast it. Casting a variable means telling REALbasic what kind of variable it is. In other words, we're telling the compiler, "Hey, you have this object, but trust me, it's a Preset. Let me treat it like one." An example of casting is in the code below, which I've added to the change event of the listbox: Dim P As Preset If Me.ListIndex<>-1 Then If Me.CellTag(Me.ListIndex,0) IsA Preset Then P=Preset(Me.CellTag(Me.ListIndex,0)) HorizontalField.Text=Str(P.HorizontalSpacing) VerticalField.Text=Str(P.VerticalSpacing) MarginField.Text=Str(P.Margin) ColorCanvas.SelectedColor=P.LineColor ColorCanvas.Refresh End If End If Now run the project again. If you click on the Gray Half Inch entry in the listbox, you should see the values in the window, including our color picker, change to reflect your choices. Again, nifty! Saving Presets Now, having Presets is great. It can be a real time saver. But if we have to create them in a text editor, that's far less useful. And if our users have to do the same, that's just... sad. So the next thing we need to do is teach our project how to save Presets. We have that "Save Preset" button sitting on the window, daring us to double click on it and start writing code, but we must not be tempted. There's other work to do first. We need a way to enter the name of the Preset before it's written to disk. We could add an EditField for that on our existing window, but that's not the approach we're going to take this time. Go back to your Project tab and create a new window. Named "SavePresetWindow" and set its frame to Sheet Window, so that it will act as a sheet on Mac OS X, but as a modal dialog on other platforms. This window will contain the code that does the actual work of saving our Presets to disk. SavePresetWindow's interface is going to be very simple. We need an EditField, which I've called "NameField", which is where the user will type in the name of the Preset to be saved. That EditField will need a StaticText to act as a label for it. Finally, we'll need two PushButtons, "Cancel" and "Save". On the code side, SavePresetWindow will need one property: MyPreset as Preset. It will also have a method that will be used to assign a value to that property. It will take one parameter: P as Preset. I called the method "LoadPreset", and its code, in its entirety, follows. MyPreset=P Now we need to put in the code for each PushButton. In the "Cancel" PushButton, it's very simple: Self.Close In the "Save" PushButton, things are a bit more complicated, but not terribly so. In this event, we're going to be referring to a method of the Preset class that we haven't yet created, so if your project won't compile yet, fear not. We need to accomplish five things in this event. First, we need to assign the user's chosen name to the Preset. Second, we need to write the Preset to disk. Third, we need to add the name of the Preset to the ListBox in our other window. Which leads to the fourth thing, adding the Preset itself to the ListBox in a CellTag. Finally, we need to close the window. Each of these five tasks will take exactly one line of code: MyPreset.Name=NameField.Text MyPreset.Save Window1.PresetBox.AddRow MyPreset.Name Window1.PresetBox.CellTag(Window1.PresetBox.LastIndex,0)=MyPreset Self.Close Now if you know anything about writing good object-oriented code, you'll see that lines three and four above are pretty poor. But in a project of this scope, I think we can get away with it. The last thing we need to do to enable Preset saving is to give Preset a Save method. This will be the most complicated part of the saving process. First, we'll set up some variables: Dim F, Parent As FolderItem Dim T As TextOutputStream Dim V As Variant Next, we need to work some platform magic to find the right location to save our Presets, as we discussed earlier in the article: #If TargetLinux Then Parent=SpecialFolder.UserHome.Child("GraphPaper Presets") #Else Parent=SpecialFolder.ApplicationData.Child("GraphPaper Presets") #EndIf And as long as that succeeds, we can create a TextOutputStream and write each value of the Preset to disk, in the same order we used to read it in: If Parent<>Nil Then F=Parent.Child(Me.Name) T=F.CreateTextFile T.WriteLine Me.Name T.WriteLine Str(Me.HorizontalSpacing) T.WriteLine Str(Me.VerticalSpacing) T.WriteLine Str(Me.Margin) V=LineColor T.WriteLine V.StringValue End If Note that we used a Variant again here to change our color to a string value so that we could save it in plain text format. Wrapping It Up For Now Go ahead and run your project. You should now be able to create and load Presets. The only hard part left is deciding what to name each one. That's all we have for this issue. In the next issue, we'll put the finishing touches on GraphPaperMaker. Until then, happy coding!
...End of Excerpt. Please purchase the magazine to read the full article.
Article copyrighted by REALbasic Developer magazine. All rights reserved.