0115 – GTK/GIO Open Flag

The next ApplicationFlag, HANDLES_OPEN, gives us a mechanism for opening files from the command line. Such things are relatively straightforward anyway, but perhaps we’ll find an advantage or two by using what GIO has to offer. Let’s dig in and find out, shall we?

We’ll do this in two steps. Firstly, we’ll look at the basics—grabbing the file names from the command line—and secondly, we’ll add just enough code to open each file in its own window.

Importing the GIO File Abstraction

Results of this example:
Current example output
Demonstration where multiple file names are given on the command line.
Current example terminal output
Top: no file names given on the command line. Bottom: two file names given.

The GIO construct that helps us handle files is called gio.FileIF. It’s not really an interface, but a sort-of wrapper standing in for a C-language abstraction—GFile—which represents the vital statistics of a file. For our purposes, we don’t need to know a lot about this to use it, so we’ll skip the details. For now, just know we need this import statement to make this stuff work:

import gio.FileIF;

MyApplication Changes

Raise the Flag

As we’ve done before, we declare the appropriate flag in the MyApplication class. And while we’re at it, let’s change the application id as well so it matches our current example:

ApplicationFlags flags = ApplicationFlags.HANDLES_OPEN;
string id = "com.gtkdcoding.app.app_05_open_flag";

Hook up the Callback

The initializer method needs a little something, too:

addOnOpen(&onOpen);

Basically, just hook up the signal. Note that we don’t need the HANDLES_COMMAND_LINE flag to make this work, even though that might seem like the case (it did to me at first).

Messing with the activate() Method

This is a pretty small change from our last demo. There, we passed in an array containing the dimensions of the window we were about to open. This time, we forego that in favour of passing in whatever arguments the user types on the command line. For purposes of demonstration, we hope these arguments will be valid file names so we don’t have to write try/catch statements. But feel free to add those if you want.

Anyway, activate() now looks like this:

void activate(string filename)
{
	writeln("activate called");
	AppWindow appWindow = new AppWindow(this, filename);

} // activate()

We’ve dispensed with the dimensions argument—an array of integers—and replaced it with filename—a string—which is the name of the file we’ll be opening in a window instance.

This method is called once for each file name provided on the command line.

Changes to the onActivate() Method

Here’s another small change:

void onActivate(GioApplication app) // non-advanced syntax
{
	AppWindow appWindow = new AppWindow(this, null);
	writeln("triggered onActivate...");
	writeln("\tApplication ID: ", getApplicationId());
	
} // onActivate()

The reason we have another instantiation of AppWindow here is to deal with the possibility that the user gives no arguments. Note that—depending on circumstances—either activate() or onActivate() is called, but not both. Here’s the low-down:

  • activate() is called once for each file name passed in, whether those file names are valid or not, and
  • onActivate() is called if no arguments whatsoever are given.

Note: If multiple non-valid file names are given, activate() is still called multiple times and multiple windows are opened. A little error checking will not go amiss here, but I’ll leave that to your imagination and skill.

A New Callback – onOpen()

It’s relatively trivial, so here’s the entire method:

void onOpen(FileIF[] files, string hint, GioApplication app)
{
	writeln("triggered onOpen...");
		
	foreach(file; files)
	{
		string name = file.getPath();
		writeln("file name: ", name);
		activate(name);
	}
		
} // onOpen()

This simple method steps through an array of FileIF objects, grabs the full path of each, then calls activate() for each one… as mentioned above.

The second argument—string hint—allows for different modes when opening a file (edit, view, etc.) and it’s suggested that unless we have a specific need for this type of thing, we should just leave it be. So we will.

Let’s move on to the next step where we actually load files into these windows…

Loading Text Files

Results of this example:
Current example output
Two file names given on the command line.
Current example terminal output
Two file names given on the command line. (click for enlarged view)

We’ve seen the GTK code to accomplish this before—the bits and bobs that slap a TextView into a Window—so that part, I’ll skip. If you want a refresher, take a quick look at blog post #0069.

The one thing we haven’t covered is the D code that opens and reads the file. Here’s what that looks like:

if(filename)
{
	file = File(filename, "r");
			
	while (!file.eof())
	{
		string line = file.readln();
		content ~= line;
	}
			
	textBuffer.setText(content);
	file.close();		
}

Pretty straightforward. We start by making sure the filename variable contains a string, then dip into D’s stdio library to open the file in read mode.

The while loop reads the file one line at a time and concatenates it to the content variable.

Once that’s done, content is dumped into the TextBuffer and we close the file.

Conclusion

So, now we’ve read file names from the command line and opened them, each in their own window. Next time, we’ll look at command line switches.

‘Til then, let’s all do our best to stay sane. These trying times are a challenge—like setting out to design a garbage collector on your first day with a new language while simultaneously trying to work out the plot of Lost—but we can survive and flourish if we all keep our heads… and our distance.

Be safe.

Comments? Questions? Observations?

Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.

You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.

© Copyright 2023 Ron Tarrant