Son Of GeeWhiz Prolog: OlioThursday, August 7. 2008
This article describes an enhancement to GeeWhiz Prolog, a project to implement the Prolog language in the NetBeans IDE. See the GeeWhiz Prolog home page for more information. What We'll Be DoingIn this installment of Son of GeeWhiz Prolog there really won't be much tutorial content. I'll do my best, but to be honest it's been long enough since I made the changes described here that I haven't been able to reassemble my notes properly, and I misplaced my screenshots. However I thought it would be better to post the new version of the source and NBM on the GeeWhiz Prolog home page rather than wait any longer. So here you go. I am not including separate source code in this article, so you'll need to download the source on the Home Page to find the changes. Dang, sorry. Moving the Satellite Window to the NavigatorProbably the most obvious change is that the satellite view of the Prolog diagram has been transformed from a standalone JFrame window into a TopComponent that nestles into the Navigator window area. This is more convenient as it stays out of the way but remains accessible as you scroll around the larger diagram. It also makes us a "better NetBeans citizen": standalone JFrames bad, TopComponents good. We're leaving the popup code JFrame windows alone however, so I guess we're not that good a NetBeans citizen. Another change is that we got rid of VPrologAction.java, which was the action living in the "Window" menu that opened up the demo/dummy diagram. Along with that, we eliminated the code in VPrologGraphScene to create the dummy diagram, and we added code to VPrologTopComponent to open and close the satellite navigator component. The final change regarding the satellite window is that we changed the "show / hide satellite" action in the right-click context menu in the main diagram. It is now just "show satellite", since you can close the satellite window like any other window whenever you want. Minor StuffIn the process of cleaning up GeeWhiz, it seemed to make more sense for the popup editor code to live in SceneUtils, so there it now resides. I also changed the phrase "1 instances" to read "1 instance" in the case where there is only 1 instance. Hey, it is too rocket science. Problems, Setbacks and FailuresFor Son 2 (this article) I wanted to experiment with FreeMarker, which is a scripting language that one can use to insert text into a template. This can automatically insert licensing information or whatever else you want into our trusty Prolog program template. Unfortunately I could not get it to work reliably. In the newest version of the source code I left the FreeMarker code in, but every time I tried to execute it outside of the top level of the source code I got a cryptic error: WARNING [org.openide.WizardDescriptor] java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1938) at java.lang.String.substring(String.java:1905) ... Ouch. As I mentioned this only happened when the template was not in the top level of the source, for some reason. I puzzled over it for a while then gave it up as a bad job. But I thought I'd pass it along anyway. I guess GeeWhiz Prolog was not destined to be the kind of application that has source code licenses.... Another experiment I thought I would try was to use the capability of the Visual Library to directly associate objects with scene nodes. This would allow us to eliminate the Prolog object lookup we need to do to translate from node to Prolog clause. Hmmm, I thought, simpler is better, it sounds good. However, I hammered away at associating any kind of object with the nodes in our diagram scene and kept getting assertion errors in the Visual Library that didn't tell me what the hell was wrong. I finally traced the VL source code and found out that in a GraphScene there is already an object associated with a node that lurks behind the scenes. At least I think that's what's happening; it looks like a String is hooked up to a node as an object, and you can only have one object per node. Consequently, we can't hook up our Prolog clauses to nodes that way unless we quit using GraphScene, which is a poor trade. So the list association we started with remains intact, bloody but unbowed. As a footnote, I've used the Visual Library a great deal since the experiment I described above, and every time I get an accursed assertion error from VL I know that it's a problem with trying to add multiple objects to a node. That would probably be a good thing to fix in the next release of the Visual Library. And it's a good thing to know if you're working with it. Almost forgot - another problem I encountered has nothing directly to do with NetBeans: I have been trying to get some HTML windows to pop up from this blog platform that don't have the sidebars etc. so I can better display a few Going FurtherWell, sort of a lame Son of GeeWhiz Prolog, I know, but I didn't want to post the updated source code and not provide an explanation to go with it. I don't have plans to post anything new here for a while as I am in the middle of a large project utilizing NetBeans, unless.... Well, we'll see. Files From This Entry(see the newly posted source code zip files and NBM on the GeeWhiz Prolog home page.) Installing Wink in Ubuntu Hardy HeronThursday, May 29. 2008
Here's something not directly related to NetBeans for a change. Not being a videohead, I tend to flounder a little bit (well okay, a lot) when it comes time to do video editing and that sort of thing. I am in the process of creating a movie about GeeWhiz (I'll explain why another time) and after several trial runs that generated very large uneditable files and after flailing around with subtitle editors and video editors that crashed I came across a tutorial called "Creating a Flash Demo of NetBeans IDE". Perfect. It would have saved me many hours of work if I'd found it earlier, but hey, that's why I started this blog: so I could save you many hours of work by learning from my mistakes. I'm nice like that. See "donate" button. Ouch. I dislocated my shoulder patting myself on the back. Anyway, in the tutorial the Wink program is used extensively and it looks like just what the doctor ordered. It is available from the Ubuntu Linux repositories so I installed it on Aspen (Ubuntu Hardy Heron) using the Synaptic Package Manager as usual. Upon typing "wink" in the terminal I got an error that Wink couldn't find libexpat.so.0. If you're not a Linux expert you might appreciate knowing that you can fix this glitch by following the following steps: Start a terminal. Type
Note that the above process is a total kludge and I would post this somewhere more appropriate if only I knew where that place might be. Feel free to do so yourself if you'd like. While I'm at it, I might point out that Wink doesn't install itself into the Applications menu. If you're an Ubuntu newbie, to fix this you need to go to the Ubuntu tool bar and select "Systems / Preferences / Main Menu". Click on "Sound & Video" on the left under "Applications", then click "New Item". Select "Application", and in the Name field type "Wink" and in the Command field type "wink" (lower case, Linux is case-sensitive). In the icon window, a Wink icon should eventually appear but if it doesn't don't worry about it. Close everything. Now you should be able to start Wink from the Applications menu. Just thought I'd pass that along. Now back to the tutorial.... Son Of GeeWhiz Prolog: Improving The DiagramTuesday, May 27. 2008
This article describes an enhancement to GeeWhiz Prolog, a project to implement the Prolog language in the NetBeans IDE. See the GeeWhiz Prolog home page for more information. What We'll Be DoingIn this add-on to our GeeWhiz Prolog project, we're going to add some new features and Geewhizability to our predicate modeler. Big thanks go to David Kaspar who provided the Visual Library examples from which much of this material is Adding An Export FunctionThe first change we're going to make to our nifty Prolog predicate diagrammer is that we're going to add a means by which we can export our diagram to a Portable Network Graphics (PNG) file. Once we have a PNG file, we can print it, email it to Aunt Margaret in Philly, or (hypothetically) stick it on our GeeWhiz Prolog home page. So let's get started. First, create a new Java class in your project called "SceneExport". Replace the empty class with this:
public class SceneExport {
public void exportScene(VMDGraphScene scene) {
JComponent view;
Dimension dim;
BufferedImage bufImage;
Graphics2D graphics;
File file;
view = scene.getView();
dim = view.getSize();
bufImage = new BufferedImage (dim.width, dim.height,
BufferedImage.TYPE_4BYTE_ABGR);
graphics = bufImage.createGraphics ();
scene.paint (graphics);
graphics.dispose ();
file = getPNGSaveFile(view);
if (file != null) {
diagramToPNG(file, bufImage);
}
}
private File getPNGSaveFile (JComponent view) {
JFileChooser chooser;
File file;
//TODO: save selected directory as default for next time
chooser = new JFileChooser();
chooser.setDialogTitle("Export Scene As ...");
chooser.setDialogType(JFileChooser.SAVE_DIALOG);
chooser.setMultiSelectionEnabled(false);
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setFileFilter(new FileFilter() {
public boolean accept(File file) {
if (file.isDirectory())
return true;
return file.getName().toLowerCase().endsWith(".png");
}
public String getDescription () {
return "Portable Network Graphics (.png)";
}
});
if (chooser.showSaveDialog (view) != JFileChooser.APPROVE_OPTION) {
return null;
}
file = chooser.getSelectedFile ();
if (! file.getName ().toLowerCase ().endsWith (".png"))
file = new File (file.getParentFile (), file.getName () + ".png");
if (file.exists ()) {
DialogDescriptor descriptor = new DialogDescriptor (
"File (" + file.getAbsolutePath () +
") already exists. Do you want to overwrite it?",
"File Exists", true, DialogDescriptor.YES_NO_OPTION,
DialogDescriptor.NO_OPTION, null);
DialogDisplayer.getDefault().createDialog(descriptor).setVisible(true);
if (descriptor.getValue() != DialogDescriptor.YES_OPTION) {
return null;
}
}
return file;
}
private void diagramToPNG(File file, BufferedImage bufImage) {
try {
ImageIO.write (bufImage, "png", file); // NOI18N
} catch (IOException e) {
ErrorManager.getDefault ().notify (e);
}
}
}
What the above code does is copy the predicate diagram graphics to a BufferedImage then save the image to a PNG file. Pretty straightforward, and taken from Visual Library example code that I haven't been able to find again. So fix imports, save and close the SceneExport class then create a new Java class called "SceneUtils". Replace the empty class with this code:
public class SceneUtils {
private static final String EXPORT_SCENE = "export";
private static final String ACTION_MOVE = "move";
private static final String REMOVE_LABEL = "remove";
private static final String REPLACE_LABEL = "replace";
private LabelWidget label;
private Scene scene = null;
private boolean isVMD;
public SceneUtils(Scene scene) {
this.scene = scene;
isVMD = scene instanceof VMDGraphScene;
}
public void addContextMenu() {
WidgetAction popup;
if (scene == null) {
return;
}
label = new LabelWidget (scene, "Right-click to open popup menu.");
label.setPreferredLocation (new Point (100, 100));
scene.addChild (label);
popup = ActionFactory.createPopupMenuAction (new MyPopupProvider ());
scene.getActions ().addAction (popup);
// leave popup menu up (?)
scene.createActions (ACTION_MOVE).addAction (popup);
label.createActions (ACTION_MOVE).addAction (ActionFactory.
createMoveAction ());
}
private void exportScene() {
SceneExport exporter;
if (isVMD) {
// TODO: thread
exporter = new SceneExport();
exporter.exportScene((VMDGraphScene) scene);
}
}
private void addMoveAction() {
scene.setActiveTool(ACTION_MOVE);
}
private void removeLabel() {
// removes it from the scene, doesn't delete it
this.label.removeFromParent();
}
private void replaceLabel() {
if (label.getParentWidget() == null) {
scene.addChild(label);
scene.validate();
}
}
private final class MyPopupProvider implements PopupMenuProvider,
ActionListener {
private JPopupMenu menu;
public MyPopupProvider () {
menu = new JPopupMenu ("Popup menu");
JMenuItem item;
if (isVMD) {
item = new JMenuItem ("Export scene to PNG...");
item.setActionCommand (EXPORT_SCENE);
item.addActionListener (this);
menu.add (item);
}
item = new JMenuItem ("Move the label");
item.setActionCommand (ACTION_MOVE);
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Delete the label");
item.setActionCommand (REMOVE_LABEL);
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Replace the label");
item.setActionCommand (REPLACE_LABEL);
item.addActionListener (this);
menu.add (item);
}
public JPopupMenu getPopupMenu (Widget widget, Point localLocation) {
return menu;
}
public void actionPerformed (ActionEvent e) {
String cmd;
cmd = e.getActionCommand ();
if (cmd.equals(EXPORT_SCENE)) {
exportScene();
} else if (cmd.equals(ACTION_MOVE)) {
addMoveAction();
} else if (cmd.equals(REMOVE_LABEL)) {
removeLabel();
} else if (cmd.equals(REPLACE_LABEL)) {
replaceLabel();
}
}
}
}
In SceneUtils we're creating a context menu that pops up when you right-click on our lovely model diagram. In this incarnation it was written to cope with any kind of Scene even though the export function only handles VMDGraphScene. The menu includes some choices that are of limited utility in practice but serve to demonstrate the capability of the right-click popup. We also introduce the LabelWidget, which we haven't seen before. So fix imports, save and close the SceneUtils file. Finally, we're going to change the VPrologGraphScene class to use our SceneUtils class. At the beginning of each constructor, add the following code:
SceneUtils sceneUtils;
sceneUtils = new SceneUtils(this);
sceneUtils.addContextMenu();
At the very end of each constructor, add this line:
I found the "layoutScene" method by accident when I was working through some of David Kaspar's Visual Library examples. It automatically cleans up our diagram! Evidentally there is a more robust orthogonal link router in the works, but for our purposes this one works just fine and eliminates much of the haphazardness of the random way we were placing nodes before. Woohoo! Save and close the file. Now we're ready to test the beast. Clean and build the project, then install it to the target IDE. (You missed doing that since completing the original GeeWhiz project, didn't you?) Open your Algorithms project and wait for the indexing to stop. Now we're ready to check out our new popup menu. Open the sieve.pro file (or any other Prolog file) and wait for the Navigator to fill in the statements and for the syntax to color. Then go to the menu and click "View" "Show Prolog Diagram" to bring up the predicate model of our program. ("Gee whiz!") Right-click on the diagram to get our popup menu.
And by the way, don't forget to gasp with delight at the way layoutScene() fixes up our diagram when it is first displayed! When you're done testing and fooling around you can close the target IDE. Adding Editor Windows To NodesThe next feature we're going to add is a window containing the text Prolog source code for the predicate that opens up when we double-click on a predicate node. You wondered why we built the text into our PrologClause class, didn't you? This is why. First, create a new JFrame Java class called "NodeContents" by right-clicking on the package and selecting "New" "JFrame Form..." This is going to be the window in which we display our source code. In the "Design" view, add a scroll pane that snaps to the preferred borders of the frame and then add a text area that fills the scroll pane. Rename the text area to "textArea", then in Properties give it a descriptive tool tip, unclick the "editable" property and change the tab size to 4. In the JFrame properties, change the defaultCloseOperation to "HIDE". Switch to "Source" and delete the Main method. Right below the constructor, insert the following code:
public void setText(String text) {
textArea.setText(text);
}
public void changeTitle(String title) {
this.setTitle(title);
}
Save and close the file. Now open VPrologGraphScene and replace the whole VPrologGraphScene class with this:
public class VPrologGraphScene extends VMDGraphScene {
private static final Image IMAGE_NODE = Utilities.
loadImage ("org/hulles/geewhiz/node.png"); // NOI18N
private static final Image IMAGE_EXTERNAL = Utilities.
loadImage ("org/hulles/geewhiz/external.png"); // NOI18N
private static final Image IMAGE_ITEM = Utilities.
loadImage ("org/hulles/geewhiz/item.gif"); // NOI18N
private static int pinID = 1;
private static int edgeID = 1;
private List<PrologClause> clauses;
/** Creates a new instance of VPrologGraphScene */
public VPrologGraphScene() { // demo
SceneUtils sceneUtils;
sceneUtils = new SceneUtils(this);
sceneUtils.addContextMenu();
createNode (this, 100, 100, IMAGE_NODE, "Clause1", "Internal", null);
createPin (this, "Clause1", "start", IMAGE_ITEM, "Start", "Element");
createNode (this, 400, 100, IMAGE_NODE, "Clause2", "External",
Arrays.asList (IMAGE_EXTERNAL));
createPin (this, "Clause2", "ok", IMAGE_ITEM, "okCommand1", "Command");
createEdge (this, "start", "Clause2");
createEdge (this, "ok", "Clause1");
layoutScene();
}
public VPrologGraphScene(DataObject dObj) {
PrologAST pTree;
List<PrologClause> embedded;
String nodeID;
Integer instances;
String newPinID;
PrologClause existingClause;
String eNodeID;
List<String> eNodes;
SceneUtils sceneUtils;
VMDNodeWidget node;
sceneUtils = new SceneUtils(this);
sceneUtils.addContextMenu();
pTree = new PrologAST(dObj);
clauses = pTree.getClauses();
// create all primary nodes first so they're available
// to create edges to....
for (PrologClause clause : clauses) {
instances = clause.getInstanceCount();
nodeID = makeNodeID(clause);
node = createNode(this, randXPoint(), randYPoint(),
IMAGE_NODE, nodeID, instances.toString() + " instances", null);
addEditor(node, clause);
}
// now create pins and edges
eNodes = new ArrayList<String>();
for (PrologClause clause : clauses) {
nodeID = makeNodeID(clause);
embedded = clause.getBody();
for (PrologClause e : embedded) {
eNodeID = makeNodeID(e);
newPinID = "pin" + VPrologGraphScene.pinID++;
createPin(this, nodeID, newPinID,
IMAGE_ITEM, eNodeID, "Embedded");
existingClause = PrologClause.findClause(clauses, e);
if (existingClause == null) {
if (!eNodes.contains(eNodeID)) {
// externally defined (?)
createNode(this, randXPoint(), randYPoint(),
IMAGE_NODE, eNodeID, "External",
Arrays.asList (IMAGE_EXTERNAL));
eNodes.add(eNodeID);
}
}
createEdge(this, newPinID, eNodeID);
}
}
layoutScene();
}
private String makeNodeID(PrologClause clause) {
String name;
Integer arity;
name = clause.getName();
arity = clause.getArity();
return name + "/" + arity.toString();
}
private int randXPoint() {
return (int) (Math.random() * 800);
}
private int randYPoint() {
return (int) (Math.random() * 800);
}
private VMDNodeWidget createNode (VMDGraphScene scene, int x, int y,
Image image, String name, String type, List<Image> glyphs) {
VMDNodeWidget widget;
widget = (VMDNodeWidget) scene.addNode (name);
widget.setPreferredLocation (new Point (x, y));
widget.setNodeProperties (image, name, type, glyphs);
scene.addPin (name, name + VMDGraphScene.PIN_ID_DEFAULT_SUFFIX);
return widget;
}
private void createPin (VMDGraphScene scene, String nodeID, String pinID,
Image image, String name, String type) {
VMDPinWidget pinWidget;
pinWidget = (VMDPinWidget) scene.addPin (nodeID, pinID);
pinWidget.setProperties (name, null);
}
private void createEdge (VMDGraphScene scene, String sourcePinID,
String targetNodeID) {
String localEdgeId;
localEdgeId = "edge" + VPrologGraphScene.edgeID ++;
scene.addEdge (localEdgeId);
scene.setEdgeSource (localEdgeId, sourcePinID);
scene.setEdgeTarget (localEdgeId, targetNodeID +
VMDGraphScene.PIN_ID_DEFAULT_SUFFIX);
}
private void addEditor(VMDNodeWidget node, PrologClause clause) {
NodeEditor editor;
WidgetAction nodeAction;
editor = new NodeEditor(clause);
nodeAction = ActionFactory.createEditAction(editor);
node.getActions().addAction(nodeAction);
}
public void newLayout() {
layoutScene();
}
private class NodeEditor implements EditProvider {
private final PrologClause clause;
private final String popupTitle;
private final String popupText;
private NodeContents popup;
public NodeEditor(PrologClause clause) {
this.clause = clause;
this.popup = null;
popupTitle = makeNodeID(clause);
popupText = clause.getText();
}
public void edit(Widget node) {
// assumes read-only text
VMDNodeWidget myNode;
myNode = (VMDNodeWidget) node;
// lazy edit window construction
if (popup == null) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
popup = new NodeContents();
popup.changeTitle(popupTitle);
popup.setText(popupText);
popup.setVisible(true);
}
});
} else {
popup.setVisible(true);
}
}
}
}
What we're doing here is, for every defined node (as opposed to external node) that we create, we're adding an EditAction that creates a NodeContents window when the node is double-clicked. We hold off actually creating the NodeContents window until the node is double-clicked so we can be a good NetBeans neighbor. Also, notice that instead of disposing of the window when we close it we just hide it so we can set it visible again the next time it's clicked. Adding A Satellite WindowBefore we test the editor functionality we're going to go ahead and make another window that shows the entire diagram in miniature, called in Visual Library terms a satellite view. We're going to show it and hide it via our context menu. So create another new JFrame called NodeSatelliteView. In "Design" mode add a panel to the frame that takes up the whole frame and make it "BorderLayout". Change the name from jPanel1 to satellitePanel and give it a tooltip "Prolog diagram satellite view". Change the JFrame defaultCloseOperation to "HIDE" and the title to "Prolog Satellite View". Switch to "Source" mode, remove the Main method, and add this method below the constructor.
public void addView(JComponent view) {
satellitePanel.removeAll();
satellitePanel.add(view);
}
Save and close the file. Now open "SceneUtils" and replace the entire class with this.
public class SceneUtils {
private static final String EXPORT_SCENE = "export"; // NOI18N
private static final String LAYOUT_SCENE = "layout"; // NOI18N
private static final String PRINT_SCENE = "print"; // NOI18N
private static final String SATELLITE_VIEW = "satellite"; // NOI18N
private VPrologGraphScene scene = null;
private NodeSatelliteView viewer;
public SceneUtils(VPrologGraphScene scene) {
this.scene = scene;
}
public void addContextMenu() {
WidgetAction popup;
if (scene == null) {
return;
}
popup = ActionFactory.createPopupMenuAction (new MyPopupProvider ());
scene.getActions ().addAction (popup);
// leave popup menu up (?)
scene.createActions (LAYOUT_SCENE).addAction (popup);
}
private void exportScene() {
SceneExport exporter;
// TODO: thread
exporter = new SceneExport();
exporter.exportScene((VMDGraphScene) scene);
}
private void reLayoutScene() {
scene.newLayout();
}
private void printScene() {
// not yet implemented
}
private void makeSatelliteView() {
if (viewer == null) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
JComponent view;
viewer = new NodeSatelliteView();
view = scene.createSatelliteView();
viewer.addView(view);
viewer.setVisible(true);
}
});
} else {
if (viewer.isVisible()) {
viewer.setVisible(false);
} else {
viewer.setVisible(true);
}
}
}
private final class MyPopupProvider implements PopupMenuProvider, ActionListener {
private JPopupMenu menu;
public MyPopupProvider () {
menu = new JPopupMenu ("Popup menu");
JMenuItem item;
item = new JMenuItem ("Print scene...");
item.setActionCommand (PRINT_SCENE);
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Export scene to PNG...");
item.setActionCommand (EXPORT_SCENE);
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Redo scene layout");
item.setActionCommand (LAYOUT_SCENE);
item.addActionListener (this);
menu.add (item);
item = new JMenuItem ("Show / hide satellite view");
item.setActionCommand (SATELLITE_VIEW);
item.addActionListener (this);
menu.add (item);
}
public JPopupMenu getPopupMenu (Widget widget, Point localLocation) {
return menu;
}
public void actionPerformed (ActionEvent e) {
String cmd;
cmd = e.getActionCommand ();
if (cmd.equals(EXPORT_SCENE)) {
exportScene();
} else if (cmd.equals(PRINT_SCENE)) {
printScene();
} else if (cmd.equals(LAYOUT_SCENE)) {
reLayoutScene();
} else if (cmd.equals(SATELLITE_VIEW)) {
makeSatelliteView();
}
}
}
}
Notice that we got rid of the label and label actions and tailored the class to be specific to our Prolog needs instead of generic. We added an empty printing function, a layout function, and a satellite view function to our context menu and implemented the satellite view within the class. Fix imports, save and close the file. Now we're ready to test the work we've done. You'll like this. Testing The New DiagramClean and build the main project, then install to the target IDE. Open the "Algorithms" project and open "sieve.pro". When the Navigator comes up, start up the modeler ("View" "Show Prolog Diagram") and double-click on a node. If the node is defined and not external then a window should open with the text of all the clauses that make up the predicate.
Going FurtherOne of the funky things about what we accomplished in this entry is that the screens for the node text and the screen for the satellite window are all JFrames independent of the diagram TopComponent. If you close the diagram, the frames stick around anyway. While you could possibly make a case that the little text windows are useful independently of the diagram, at least the satellite window should be dependent on the TopComponent and go away when it goes away. It is not useful without the diagram. So a reasonable next step would be to make the "pop up" screens for node text and the satellite window JDialogs or close them when the TopComponent goes away. Now that we have all these new features on our diagram, what else could we do? The first thing that comes to mind is to allow the user to edit the code in the popup node window and have the changes reflected in the editor window source code. This would take some careful design because of the way multiple clauses make up a predicate, but it could certainly be done using techniques we've already looked at. We could disembowel the PrologAST class and store line and column numbers in PrologClause, then use the same way we pop into the source code from the output window to go into the editor and change the text. But the usefulness of editing directly in the popup window is pretty questionable, at least for Prolog programming, so we won't go that route. I won't, anyway, but you certainly can... An even more interesting (to me) idea is to use the stuff we learned in GeeWhiz to write a whole new language interface. It would be based on Java but instead of writing code you would just manipulate nodes and edges then compile the diagram! Theoretically a person would not even have to know Java to "program" with this new language. I know, I know, it sounds like UML but it would be way cooler. Actually, this is something I've always wanted to develop, so look for "Project PipeDreams" coming your way soon.... Files From This EntryGeeWhiz Prolog - Part Eight - Creating An NBMFriday, May 23. 2008
This is Part Eight, the ninth and final part of a series of entries describing an implementation of the Prolog language in the NetBeans IDE. Part Zero, "About The Project," is here. Part One, "Before We Start," is here. Part Two, "Creating A File Type," is here. Part Three, "Adding Language Support," is here. Part Four, "A Visual Prolog Modeler" is here. Part Five, "Adding The Compiler" is here. Part Six, "Creating Prolog Projects" is here. Part Seven, "Adding An Option Panel" is here. What We'll Be DoingWell, we've come a long way. Our NetBeans-embedded Prolog IDE has grown like Topsy (whoever Topsy is) and now threatens to take over NetBeans entirely and make everyone program in Prolog instead of Java. Okay, maybe not, but it has come a long way as I said. We will look at what we've accomplished in a post mortem in a little bit, but right now let's create a NetBeans plug-in module (NBM) from GeeWhiz Prolog and install the module into our development IDE at last. Creating The NBMAs I'm sure you've noticed, one of the options on the GeeWhiz project context menu in NetBeans has always been "Create NBM". Go ahead and clean and build GeeWhiz, right-click on the project and select "Create NBM" now.
What we've done is generate a nifty little zip file called (in my case) "org-hulles-geewhiz.nbm" in the build folder of our project.
I'm not sure you noticed in the above screenshot, but I had the properties for the NBM file open so you could see what I saw the first time I went through this process. "Hmm," says I to myself. "The size is a little larger than I thought it would be. And I guess the NetBeans folks forgot to put the 'kB' on the size property. It's still too large to host as a download, though." Then I went and looked at the file in the folder just to make sure and it's 47kB! Dang, that's like buying a case of beer for a dollar, it just doesn't happen that often these days. So it's a little file, is my point, and I can stick it on my blog site as a download without a further thought. Hell, I can even email it as an attachment to my Aunt Margaret in Philly who still has dial-up. (Not sure she has NetBeans, though....) So now what do we do with our brand new (and shrimpy little) NBM file? For one thing, if we wanted to we could make it available to the whole world via the NetBeans Plug-In Download Center (or whatever it's called) but that's a bit outside the scope of this project and besides, it's not ready for implementation yet. But we can use it to finally install GeeWhiz Prolog in our development IDE if we want to. So let's do that. Installing The NBM In Your IDEThere is probably a more dignified way to install a local NBM into your IDE, but the way I found works pretty well so we'll use that. In the main menu of your development IDE click "Tools", then "Plugins". Once the Plugins manager has started, select the Downloads tab. Click the "Add Plugins..." button and navigate to your NBM file and select it.
Eventually the IDE will come back and tell you the module is installed. To check it, select "Tools" "Module Manager" from the main menu and there GeeWhiz Prolog is, under "Language Support".
Guess What - We're Done!That wraps up our adventures with GeeWhiz Prolog, at least for now. I am in the process of creating a home page for GeeWhiz Prolog on my blog that you can check occasionally if you want. I will post the NBM there, plus a complete set of source files, plus a PDF of all the GeeWhiz blog entries for your convenience in downloading and reading offline. Any addenda and errata (as if) will be posted there as well. I already thought of one thing I forgot to mention, and that's that you can zoom in and out (ctrl + mouse button) and pan (hold middle mouse button) on the modeling diagrams we created earlier. As a special bonus for acting now, you will also receive an additional Java class in the complete source file archive called SceneExport.java that may or may not create a .PNG file from the modeling diagram. I'm still working on it and haven't incorporated it into the diagram yet, but I'm sure I'll write about it when I do. It's based on a slightly cryptic code segment in the Visual Library API and... Oh, just wait and read the article when I write it. It's hard to end this series. I feel like we just scratched the surface with GeeWhiz Prolog, but hey, the surface itched. So... ciao, it's been fun. I hope you found this series useful, informative and occasionally entertaining. -- Hulles GeeWhiz Prolog Feature Summary
Going Further - My GeeWhiz NotesFixes And Improvements To Current Features
Additional Features That Could Be Added To GeeWhiz
Either They're IDE Bugs Or I'm Doing Something Wrong Dept.
Areas For Further Research
GeeWhiz Prolog - Part Seven - Adding An Option PanelThursday, May 22. 2008
This is Part Seven, the eighth part of a series of entries describing an implementation of the Prolog language in the NetBeans IDE. Part Zero, "About The Project," is here. Part One, "Before We Start," is here. Part Two, "Creating A File Type," is here. Part Three, "Adding Language Support," is here. Part Four, "A Visual Prolog Modeler" is here. Part Five, "Adding The Compiler" is here. Part Six, "Creating Prolog Projects" is here. What We'll Be DoingOur Prolog IDE is quickly maturing into a comfortable, fun and feature-rich programming environment. But if you're like me -- and I hope for your sake you are not -- you've been laying awake at night, sweating, thinking to yourself, "Gee whiz! I created this great Prolog IDE but I totally ruined it when I hard-coded the compiler command stuff into the project (see Part Five, "Adding The Compiler"). Now I can't make it into an NBM and distribute it and have everybody in the world use it and love it and have somebody write an article for NetBeans.org called 'Meet A NetBeans Module Writer: Hulles' with my picture in it and everything." Well, we all need our sleep, so lay awake no longer. This time we're going to add an IDE-level Prolog options screen where we can set the compiler command and arguments outside of our code and have it retained between sessions. We're also going to clean up some code while we're at it so our project is a little more compatible with the NetBeans IDE -- a "better NetBeans citizen," as they say. This entry is partially based on a Geertjan article in NetBeans called "NetBeans Options Window Module Tutorial". I recommend checking it out prior to doing this entry if you feel so inclined; he gives you more background than I give you here. And thanks to the author for that tutorial and all the other fine NetBeans articles he has done. Adding An Options PanelFirst off we're going to start up the New Options Wizard. Right-click on your GeeWhiz project and select "New" "Other".
Now we're going to take the panel that the wizard created and modify it for our own nefarious purposes. Open "GeewhizPanel.java" in design mode and from the Swing palette add a panel that fills the frame, then add two labels and two text fields to the panel, as in the screenshot.
What we're doing here is loading and storing the two data we need for our compiler, the compiler command and arguments string, in NetBeans Preferences. It may be necessary at some point to add environment and working directory information here, but for now we'll just leave it at the command and arguments. Save and close the GeewhizPanel class. Modifying The Compiler ClassNow we need to change our PrologCompiler class to use the NbPreferences we created in the Options panel. I went through several iterations of changes and testing with this class, and here is the final version (as if any programming code was ever final). Note: if you've made any changes to the class yourself from the version we created earlier, you should save your old version of PrologCompiler.java prior to making these changes. Whenever you're ready, change the class definition to the one listed below:
Actually, rather a lot has changed. Notice in the constructor that we are now getting the compiler command and arguments from NbPreferences and starting a listener to see if they change. Actually, the listener may be overkill for this application but as I mentioned above we're trying to be better NetBeans citizens so let's leave it in just in case we have a very quick typist on our hands. Also notice that now we are checking for a SaveCookie before we start the compilation. If one exists, and it will if our source file has been modified since the last save, we grab it and use it to save the file prior to compiling it. I love how easy that is in NetBeans. We follow the NetBeans Java example of not asking prior to doing the save; we just go ahead and do it. Note that we also are adding a couple helper methods to clean up our code (and the output) a little bit. White space rocks. And we're adding more cleanup -- we are finally closing the OutputWriter in our class. Since we opened up the compiler command and arguments to user input, now we have to do some reality checks on the data prior to using it. First we check to make sure the NbPreference is there for the compiler command and issue an error if it is not. (The command arguments should be optional.) The compiler command will not be there if the user has not set up the compiler in our new options panel so we firmly order her/him to go to the options panel and set it up right the hell now. Hmph. The nerve. We also changed the try/catch for the compiler invocation, since now we're executing whatever the user typed in for a command and arguments. Instead of doing a stack trace we're issuing an error message in the catch, since
I think that about covers the changes. Let's try it out. But before we do that, you might want to grab a new PrologAST from the archive to this article. I modified it to not print all the debugging information unless a constant is set, and if printing is requested to not keep the OutputWriter tied up quite so long. Good citizenship again, but not worth including the source code in this article. Testing The Option Panel And The CompilerYou know the drill -- clean and build the GeeWhiz project then install it to the target IDE. Don't go into the options menu yet. Open up the "Algorithms" project (!) and open sieve.pro. Try to compile it with the menu bar button. You should get an error.
So now GeeWhiz is back to being platform- and compiler-independent and it's a little easier on the NetBeans enviroment. Now maybe we can all go get some sleep. Next time we'll create a NetBeans module (NBM) file for sharing with others, a fairly trivial task, and sum up what we've accomplished so far. Zzzzzz.... Files From This Entry
(Page 1 of 2, totaling 12 entries)
» next page
|
QuicksearchCategoriesArchivessupersized.orgSyndicate This BlogHelp Support This BlogIf you find this information useful, are entertained by it, or just want to do something nice you can help encourage further entries by clicking the button below. Major credit cards and PayPal are accepted. Thank you for your support.
|
So far we've cobbled together a pretty decent Prolog IDE within the NetBeans environment. As far as features, it has... well, you can review the previous articles in the series yourself but it has a lot of Cool Stuff. Now we're going to create a new project template so when you want to start working on a new Prolog project you don't have to call it a Java class library or a web application just to be able to create new Prolog source files.
First we need to create a sample project to use in the new project template wizard. This project should have what just what we need when we start a new Prolog project and no more. Since we're only working with text source files, that's pretty much all the project needs. We'll just copy one source file into the project since both Nature and Hulles abhor a vacuum. In the main menu, select "File" "New Project" and create yourself a new Java class library project called, appropriately enough, "NewProject".
Find a copy of sieve.pro in your target IDE directory and copy it into the src folder of NewProject. If you would prefer, you can extract a copy from the archive files at the end of this article.
Now we're going to pare down the project as much as we can, which isn't much at this point. Right-click on NewProject and select "Properties". In "Categories" select "Libraries", then click the "Compile Tests" tab. Delete the test libraries that are there.
![]()
Click "OK" to close the properties window. Now we have a simple project we can use as a base for creating new Prolog projects. Time to create our new project template. Right-click the GeeWhiz project and select "New" "Project Template...". In the first screen of the wizard select "NewProject".
![]()
Click "Next". On the next screen of the wizard, enter "NewProject" for the template name, "Prolog Application" for the display name, select "Other" for the category, and make sure the package is correctly filled in.
![]()
Once everything is correct, click "Finish".
Now we're going to customize our new project template a little bit. Edit "NewProjectDescription.html" and change the description to simply "Prolog project".
![]()
Save and close the file. Open the layer.xml file and traverse "this layer" down to "Templates" "Project" "Other".
![]()
Right-click on "Prolog Application" and select "Pick Icon...". Navigate to your package source folder and select "prolog-star.png", the dreaded Black Star of Prolog.
![]()
Click "OK" and you're done. You can now delete the "NewProject" project because we won't need it anymore. The necessary stuff from "NewProject" was zipped into our "GeeWhiz" project by the new project template wizard.
Our new Prolog project template should now be ready to test. Clean and build your main project and install it to the Target IDE. Once your "personal domain" is created, in the main menu select "File" "New Project" and you should see our new Prolog project option, complete with BSofP icon.
![]()
Select "Prolog Application" and fill in the necessary fields, calling your new project "Algorithms" in honor of our two hard-working sample Prolog programs, fib1.pro and sieve.pro.
![]()
Check out "Algorithms" and note that "sieve.pro" is include in the default package of our new project. If you want to be really loyal, copy fib1.pro into our new project and delete the "BraveNewWorld" project since we won't need it anymore.
While we're here, note also that there is a disturbing Java coffee cup icon on our new Prolog project. There is a way to change that, but that procedure is beyond the scope of this how-to series. This is because I have no idea what the procedure is. If you do, please let me know.
There, wasn't that easy? Now we have a way to create a Prolog project as a home for our precious Prolog source code. Next we'll do some housework in GeeWhiz and make it a "good NetBeans IDE citizen" (or at least a better one). In the process we'll make GeeWhiz Prolog platform- and compiler-independent once again. (It was until the last entry when we added the compiler.) Until next time.

Owner login

