In this post I’ll quickly show how you can write a very simple text editor using CoreGTK. This example is purposely very basic, has no real error handling and few features but it does show how to use CoreGTK in various ways.
To start with I quickly threw something together in GLADE (which if you aren’t aware is an excellent drag and drop GUI editor for GTK+).
Next I created a SimpleTextEditor class that will house the majority of my logic and stubbed out my callbacks and methods.
@interface SimpleTextEditor : NSObject { CGTKTextView *txtView; CGTKWidget *window; } -(void)show; // Callbacks -(void)winMain_Destroy; -(void)btnNew_Clicked; -(void)btnOpen_Clicked; -(void)btnSave_Clicked; // Helper methods to deal with the text view -(NSString *)getText; -(void)setText:(NSString *)text; @end
Now the fun part begins: filling in the implementation of the methods. First create the init and dealloc methods:
-(id)init { self = [super init]; if(self) { CGTKBuilder *builder = [[CGTKBuilder alloc] init]; if(![builder addFromFileWithFilename:@"gui.glade" andErr:NULL]) { NSLog(@"Error loading GUI file"); return nil; } NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys: [CGTKCallbackData withObject:self andSEL:@selector(winMain_Destroy)], @"winMain_Destroy", [CGTKCallbackData withObject:self andSEL:@selector(btnNew_Clicked)], @"btnNew_Clicked", [CGTKCallbackData withObject:self andSEL:@selector(btnOpen_Clicked)], @"btnOpen_Clicked", [CGTKCallbackData withObject:self andSEL:@selector(btnSave_Clicked)], @"btnSave_Clicked", nil]; [CGTKBaseBuilder connectSignalsToObjectsWithBuilder:builder andSignalDictionary:dic]; // Get a reference to the window window = [CGTKBaseBuilder getWidgetFromBuilder:builder withName:@"winMain"]; // Get a reference to the text view txtView = [[CGTKTextView alloc] initWithGObject:[[CGTKBaseBuilder getWidgetFromBuilder:builder withName:@"txtView"] WIDGET]]; [builder release]; } return self; }
-(void)dealloc { [txtView release]; [window release]; [super dealloc]; }
OK let’s break down what we’ve done so far.
CGTKBuilder *builder = [[CGTKBuilder alloc] init]; if(![builder addFromFileWithFilename:@"gui.glade" andErr:NULL]) { NSLog(@"Error loading GUI file"); return nil; }
First thing is to parse the GLADE file which is what this code does. Next we need to connect the signals we defined for the different events in GLADE to the callback methods we defined in our code:
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys: [CGTKCallbackData withObject:self andSEL:@selector(winMain_Destroy)], @"winMain_Destroy", [CGTKCallbackData withObject:self andSEL:@selector(btnNew_Clicked)], @"btnNew_Clicked", [CGTKCallbackData withObject:self andSEL:@selector(btnOpen_Clicked)], @"btnOpen_Clicked", [CGTKCallbackData withObject:self andSEL:@selector(btnSave_Clicked)], @"btnSave_Clicked", nil]; [CGTKBaseBuilder connectSignalsToObjectsWithBuilder:builder andSignalDictionary:dic]
Finally extract and store references to the window and the text view for later:
// Get a reference to the window window = [CGTKBaseBuilder getWidgetFromBuilder:builder withName:@"winMain"]; // Get a reference to the text view txtView = [[CGTKTextView alloc] initWithGObject:[[CGTKBaseBuilder getWidgetFromBuilder:builder withName:@"txtView"] WIDGET]];
Before we can test anything out we need to fill in a few more basic methods to show the window on command and to exit the GTK+ loop when we close the window:
-(void)show { [window showAll]; } -(void)winMain_Destroy { [CGTK mainQuit]; }
Now we can actually use our SimpleTextEditor so let’s write a main method to create it:
int main(int argc, char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; /* * This is called in all GTK applications. Arguments are parsed * from the command line and are returned to the application. */ [CGTK autoInitWithArgc:argc andArgv:argv]; // Create and display editor SimpleTextEditor *editor = [[SimpleTextEditor alloc] init]; // Check for error if(editor == nil) { return 1; } // Show the window [editor show]; // Start GTK+ loop [CGTK main]; // Release allocated memory [editor release]; [pool release]; // Return success return 0; }
Compile and run this and you’ll be presented with a cool Simple Text Editor window!
So far so good. Let’s keep filling in our stubbed in methods starting with our helper methods that will allow us to manipulate the underlying text buffer:
-(NSString *)getText { gchar *gText = NULL; GtkTextBuffer *buf = NULL; GtkTextIter start, end; NSString *nsText = nil; // Grab reference to text buffer buf = [txtView getBuffer]; // Determine the bounds of the buffer gtk_text_buffer_get_bounds (buf, &start, &end); // Get the gchar text from the buffer gText = gtk_text_buffer_get_text(buf, &start, &end, FALSE); // Convert it to an NSString nsText = [NSString stringWithUTF8String:gText]; // Free the allocated gchar string g_free(gText); // Return the text return nsText; } -(void)setText:(NSString *)text { // Get reference to text buffer GtkTextBuffer *buf = [txtView getBuffer]; // Set contents of text buffer gtk_text_buffer_set_text(buf, [text UTF8String], -1); }
At this point we have everything we need to implement our New button click callback method:
-(void)btnNew_Clicked { [self setText:@""]; }
Like I said this is a pretty basic example so in a real world application I would hope you would prompt the user before blowing away all of their text!
All that’s left to do at this point is to implement the Open and Save callback methods. For these I’m going to create a new class, MultiDialog, to show how you can still really dig into the GTK+ C code when you need to.
@interface MultiDialog : NSObject { } +(NSString *)presentOpenDialog; +(NSString *)presentSaveDialog; @end
And here is the implementation:
@implementation MultiDialog +(NSString *)presentOpenDialog { // Variables CGTKFileChooserDialog *dialog = nil; gchar *gText = NULL; gint result; NSString *filename = nil; // Create the dialog itself dialog = [[CGTKFileChooserDialog alloc] initWithTitle:@"Open File" andParent:nil andAction:GTK_FILE_CHOOSER_ACTION_OPEN]; // Add cancel and open buttons gtk_dialog_add_button ([dialog DIALOG], "_Cancel", GTK_RESPONSE_CANCEL); gtk_dialog_add_button ([dialog DIALOG], "_Open", GTK_RESPONSE_ACCEPT); // Run the dialog result = gtk_dialog_run (GTK_DIALOG ([dialog WIDGET])); // If the user clicked Open if(result == GTK_RESPONSE_ACCEPT) { // Extract the filename and convert it to an NSString gText = gtk_file_chooser_get_filename ([dialog FILECHOOSERDIALOG]); filename = [NSString stringWithUTF8String:gText]; } // Cleanup g_free(gText); gtk_widget_destroy ([dialog WIDGET]); [dialog release]; return filename; } +(NSString *)presentSaveDialog { // Variables CGTKFileChooserDialog *dialog = nil; gchar *gText = NULL; gint result; NSString *filename = nil; // Create the dialog itself dialog = [[CGTKFileChooserDialog alloc] initWithTitle:@"Save File" andParent:nil andAction:GTK_FILE_CHOOSER_ACTION_SAVE]; // Add cancel and save buttons gtk_dialog_add_button ([dialog DIALOG], "_Cancel", GTK_RESPONSE_CANCEL); gtk_dialog_add_button ([dialog DIALOG], "_Save", GTK_RESPONSE_ACCEPT); // Set settings gtk_file_chooser_set_do_overwrite_confirmation ([dialog FILECHOOSERDIALOG], TRUE); gtk_file_chooser_set_current_name([dialog FILECHOOSERDIALOG], "Untitled document"); // Run the dialog result = gtk_dialog_run (GTK_DIALOG ([dialog WIDGET])); // If the user clicked Save if(result == GTK_RESPONSE_ACCEPT) { // Extract the filename and convert it to an NSString gText = gtk_file_chooser_get_filename ([dialog FILECHOOSERDIALOG]); filename = [NSString stringWithUTF8String:gText]; } // Cleanup g_free(gText); gtk_widget_destroy ([dialog WIDGET]); [dialog release]; return filename; } @end
There is quite a bit of code there but hopefully the comments make it pretty easy to follow. Now that we have our MultiDialog class we can use it in our SimpleTextEditor methods:
-(void)btnOpen_Clicked { NSString *text = [NSString stringWithContentsOfFile:[MultiDialog presentOpenDialog]]; [self setText:text]; } -(void)btnSave_Clicked { NSString *filename = [MultiDialog presentSaveDialog]; NSString *text = [self getText]; NSError *error; BOOL succeed = [text writeToFile:filename atomically:YES encoding:NSUTF8StringEncoding error:&error]; if(!succeed) { NSLog(@"%@:%s Error saving: %@", [self class], _cmd, [error localizedDescription]); } }
And there you have it a very simple text editor that lets you open text file and save them. You can find the full source for this application under the examples directory of the CoreGTK github repository.
This post originally appeared on my website here.
Leave a Reply