How to implement NSApplicationDelegate protocol in glfw application - c++

I am currently programming an application using glfw in MacOS X. My only problem is that the application doesn't use an AppDelegate, but does all the initialization in the main.cpp file like a command line utility. I specifically want to implement the function
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
and I already registered the file extensions in the plist file and when I try to open them I get an error message saying "The document "Doc.xxx" could not be opened. MyApp cannot open files in the "xxx" format.
I attempted to create a singleton that sets itself as the delegate but it just doesn't call the function. Can anyone comment on the possibility of creating a class and implementing the NSApplicationDelegate functions?

Basically there is nothing in your code to receive events from the OS and to pump the events received.
The solution, according to another stack overflow Q/A, appears to be to invoke NSApplication manually. You still need to decide where to hook your OpenGL render loop into as [NSApp run] doesn't return until the app finishes. Another solution could be to make a custom NSApplication replacement. http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html
Copied pretty much verbatim from this answer taking into account the modifications for ARC: https://stackoverflow.com/a/2154666/1874323
#autoreleasepool {
AppDelegate * delegate = [[AppDelegate alloc] init];
NSApplication * application = [NSApplication sharedApplication];
[application setDelegate:delegate];
[NSApp run]; // Note this never returns until the app finishes, so you need to decide where to hook your code into
}
As always you can find documentation from the Apple website:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html

Related

JUCE - Making a New Window

Coming from making single-page applications with the visual WYSISWYG editor in JUCE, I'm having a bit of trouble figuring out how to invoke new windows (outside of the main GUI window). I made a test application that just has a small minimal main GUI that I created with the visual editor. It has a button "Make New Window." My goal is to be able to click that button and have a new window pop up and that this new window is a JUCE "GUI component," (AKA, the graphical / visual GUI editor file). Now, I actually have sort of achieved this, however, its throwing errors and assertions, so it would be great to get a very simple, step-by-step tutorial.
I studied the main.cpp file that the Projucer automatically created in order to get a feel for how they are creating a window. Here's what I did.
1) In my project, I added a new GUI Component (which becomes a class) and called it "InvokedWindow."
2) In my main GUI component class header, I added a new scoped pointer of type InvokedWindow: ScopedPointer<InvokedWindow> invokedWindow;
3) I created a new button in the main GUI editor called "Make New Window" and added this to the handler code:
newWindowPtr = new InvokedWindow; so that any time the button is hit, a new object of type InvokedWindow is created.
4) In the InvokedWindow class, in the constructor, on top of the automatically generated code, I added:
setUsingNativeTitleBar (true);
setCentrePosition(400, 400);
setVisible (true);
setResizable(false, false);
Which I sort of got from the main file of the JUCE application.
I also added a slider to this new window just to add functionality to it.
5) I added an overloaded function to let me close the window:
void InvokedWindow::closeButtonPressed()
{
delete this;
}
So, now when I run the app and click the make new window button, a new window does pop up, but I get an assertion:
/* Agh! You shouldn't add components directly to a ResizableWindow - this class
manages its child components automatically, and if you add your own it'll cause
trouble. Instead, use setContentComponent() to give it a component which
will be automatically resized and kept in the right place - then you can add
subcomponents to the content comp. See the notes for the ResizableWindow class
for more info.
If you really know what you're doing and want to avoid this assertion, just call
Component::addAndMakeVisible directly.
*/
Also, I'm able to close the window once and hit the button in the main GUI to create another instance of a newWindow, but closing it a second time leads to an error:
template <typename ObjectType>
struct ContainerDeletePolicy
{
static void destroy (ObjectType* object)
{
// If the line below triggers a compiler error, it means that you are using
// an incomplete type for ObjectType (for example, a type that is declared
// but not defined). This is a problem because then the following delete is
// undefined behaviour. The purpose of the sizeof is to capture this situation.
// If this was caused by a ScopedPointer to a forward-declared type, move the
// implementation of all methods trying to use the ScopedPointer (e.g. the destructor
// of the class owning it) into cpp files where they can see to the definition
// of ObjectType. This should fix the error.
ignoreUnused (sizeof (ObjectType));
delete object;
}
};
This is all a bit over my head. I was figuring it wouldn't be too bad to be able to create a new window, via a button. A new window that I could edit with the graphical GUI editor, but I'm not able to fully figure it out all on my own, through I did try. Could anyone post a step-by-step guide to doing this the correct way? I did post this at the JUCE forums, but due to my lack of GUI programming, I was unable to understand the solutions posted (my own fault), so I'm hoping to get a very simple guide to this. It would be very much appreciated. Thank you.
I figured it out. I needed to create:
A new GUI component (Remember, this is the visual editor in JUCE)
A class (I called it BasicWindow, based on the JUCE demo code) that acts as a shell to run this new window and holds the GUI component.
A JUCE SafePointer that makes a new object of type BasicWindow whenever the button is clicked and sets the attributes to that window.
Here is my code:
Referring to line 3) Inside the handler section of the button to create the new window:
basicWindow = new BasicWindow("Information", Colours::grey, DocumentWindow::allButtons);
basicWindow->setUsingNativeTitleBar(true);
basicWindow->setContentOwned(new InformationComponent(), true);// InformationComponent is my GUI editor component (the visual editor of JUCE)
basicWindow->centreWithSize(basicWindow->getWidth(), basicWindow->getHeight());
basicWindow->setVisible(true);
Referring to line 2) A .cpp file that defines what the BasicWindow is:
#include "../JuceLibraryCode/JuceHeader.h"
class BasicWindow : public DocumentWindow
{
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BasicWindow)
public:
BasicWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
: DocumentWindow (name, backgroundColour, buttonsNeeded)
{
}
void closeButtonPressed() override
{
delete this;
}
};
And referring to line 1) Make the GUI editor component, which this is easy to do. You just right add a new file in the JUCE file manager. "Add New GUI Component," then visually add all your elements and handler code.
My biggest issue was that I was using a JUCE ScopedPointer, so after deleting the object, the pointer pointing to it wasn't being set back to NULL. The SafePointer does this. If any more explanation is needed, I'm happy to help, as this was terrible for someone with not much GUI development "under his belt."

Blackberry MediaPlayer retrieves empty metaData

I have a cascades project where I use the MediaPlayer class in cpp.
I have defined a handler class, which handles metaDataChanged event, but when I set the source url and call mediaPlayer.prepare() method, it doesn't retrieve anything in metadata, so it's simply empty QVariantMap.
What's interesting is that defined event handler for metaDataChaned event is not even called.
I think there could be something that I can add here to be able to get the metadata, however prepare() method workds sucessfully, so I don't know what's the problem
here is a piece of code I've tried.
bb::multimedia::MediaPlayer* mp = new bb::multimedia::MediaPlayer();
mp->setSourceUrl(resultString);
mp->prepare();
MetaDataReader metaDataReader(mp);
and a class
MetaDataReader::MetaDataReader(bb::multimedia::MediaPlayer* mediaPlayer) : QObject(NULL)
{
connect(mediaPlayer, SIGNAL(metaDataChanged(const QVariantMap&)), this, SLOT(onMetaDataChanged(const QVariantMap&)));
}
void MetaDataReader::onMetaDataChanged(const QVariantMap& metaData)
{
someCode
// It doesn't reach this SLOT
}
How can I get the metadata here?
thanks in advance
It's a bit odd, but you may not get metadata until you start playback for the file. Try starting playback, and you should see the metaDataChanged signal get fired shortly after.

WebView not showing in NSWindow

I am developing a C++ app and I need to display a NSWindow with a WebKit WebView inside it. I've coded up the Objective-C class which will manage creating and displaying the window but the WebView contained inside it does not display. Here is my code. Any idea on what is wrong and how I can fix it?
I'm compiling the below code with
$g++ -x objective-c++ -framework Cocoa -framework WebKit Foo.m main.m -o test
Foo.h
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#interface Foo :NSObject {
NSWindow *window;
WebView *view;
}
- (void)displayWindow;
#end
Foo.m
#import "Foo.h"
#implementation Foo
- (id)init {
self = [super init];
// Window Container
window = [[NSWindow alloc] initWithContentRect:NSMakeRect(500.0f,500.0f,250.0f,250.0f)
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreNonretained
defer:NO];
// WebView
view = [[WebView alloc] initWithFrame:NSMakeRect(0, 0, 250.0f, 250.0f)
frameName:#"Frame"
groupName:nil];
[[view mainFrame] loadHTMLString:#"<html><head></head><body><h1>Hello</h1></body></html>"
baseURL:nil];
return self;
}
- (void)displayWindow {
NSLog(#"In Display window");
[window setContentView:view];
[window setLevel:NSStatusWindowLevel];
[window orderFrontRegardless];
sleep(5); // leave it up for 5 seconds
}
- (void)dealloc {
[window release];
[super dealloc];
}
#end
main.m
#import "Foo.h"
int main() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
Foo *foo = [[Foo alloc] init];
[foo displayWindow];
[foo release];
[pool release];
return 0;
}
You need to run the run loop. If you just order the window in and then exit, that's exactly what will happen: The window will appear, and then (five seconds later) your program will exit. You can run the run loop by telling the application (which you create but don't otherwise use) to run.
On the main thread of a Cocoa app, sleep is always the wrong answer. The same goes for its Cocoa cousins, +[NSThread sleepUntilDate:] and +[NSThread sleepForTimeInterval:]. The run loop will let you tell it to run for a fixed amount of time, but that won't get the application running; you do need to send the application the run message, which provides no opportunity to exit after a fixed interval.
The solution there is to first create an NSTimer object whose target is the application and whose selector is #selector(terminate:). Create it scheduled and non-repeating, with the interval set to five seconds. (Creating it scheduled means you don't need to schedule it separately—it is already ready to go from the moment you create it.) Then, send the application the run message. Five seconds later, the run loop will fire the timer, which will tell the application to terminate itself. This is assuming that you actually have a good reason to make your application quit after five seconds.
As noted by Yuji, every window in modern Cocoa should use NSBackingStoreBuffered.
And don't forget to release what you have created; you currently are forgetting that in the case of the view. See the Memory Management Programming Guide for Cocoa.
Once you have this working, I suggest moving toward a more typical architecture for this application:
Create a subclass of NSObject, and make an instance of that class your application's delegate.
Put the window and its WebView into a nib, and have the app delegate create a window controller to load and own the contents of that nib.
The app delegate should also be responsible for loading the page into the WebView and for setting up the self-termination timer.
Finally, create a nib to hold your application's main menu (the contents of the menu bar) and the application delegate. Interface Builder has a template for the first part; you create the app delegate object by dragging a blank Object in from the Library, setting its class on the ⌘6 Inspector, and dragging the connection from the application to the object. Then, you can reduce main to the single line that Xcode's project templates put in it: return NSApplicationMain(argc, argv);.
Doing all this will help your understanding of Cocoa, as well as your maintenance of the application—cramming everything into main will not scale.
You should also read the Cocoa Fundamentals Guide, if you haven't already.
Don't make it sleep. It stops the execution of the main thread, in which the GUI is dealt with. Instead, you need to run the run loop. Also, Cocoa needs to set itself up. So, call [[NSApplication sharedApplication] run] to set it up correctly and run the event loop.
Also, don't use backing mode other than buffered mode. Other modes are remnants from the time immemorial, and only NSBackingStoreBuffered should be used. As discussed in this Apple document, the non-retained mode is a remnant to support Classic Blue Box (OS 9 virtualizer), and newer classes like WebKit just can't operate within it.
So, what you need to do is practically:
change NSBackingStoreNonretained to NSBackingStoreBuffered.
Remove the line
sleep(5);
add a line
[[NSApplication sharedApplication] run];
after
[foo displayWindow];
Also, in order for an app to receive events from the window server correctly, you need to pack it into an app bundle. Compile it into a binary called foo, and create the following structure:
foo.app/
foo.app/Contents/
foo.app/Contents/MacOS/
foo.app/Contents/MacOS/foo <--- this is the executable
Then you can double-click foo.app from the Finder, or just call ./foo from the command line.

NSArray and Sandbox Question

I don't quite understand, but basically I am trying to click a button and have a value added to my NSMutableArray myArray which is a property. I thought my method that would be called when a button is pressed would be something like:
- (IBAction)addInterval {
NSString *string = #"test";
[self.myArray addObject:string];
}
But I don't understand why this doesn't work?
Also, is Wifi, volume control, and bluetooth all not in our sandbox to be used?thanks.
In general, in order for your IBAction to get called, it must have a parameter, e.g.
-(IBAction) addInterval: (id)sender {
}
Also, make sure you have bound the action to the button correctly inside the UI Designer - you should see it as an action outlet for the button.
To make sure your method is being called, stick a line in the method like:
NSLog(#"Called addInterval action");
Bluetooth will not work in sandbox, WiFi will work in the sense that the network connection will function, and the reachability example should help you here.
Could be one of a few things but the classic problem here is forgetting to connect the button to the action in interface builder. Have you checked that?
I'm also assuming that you've synthesized the myArray property so that you can use the dot notation.

Loading Nib and Displaying Window in Objective C++

I am trying to load a Nib from a C++ constructor with Objective C++. I have searched and found enough examples to get some code together but it doesn't display the window. Or any windows for that matter.
Here is an example of the contructor:
JokeSystem::JokeSystem() : fileSystem("/Library/Application Support/Jokes/")
{
try
{
randSystem = new RandSelect<std::string>
(fileSystem.getAllFileContents("%\n"));
}
catch (std::ifstream::failure)
{
NSWindowController * errorWindowControl = [[NSWindowController alloc]
initWithWindowNibName:#"ErrorWindow"];
[errorWindowControl showWindow: nil];
}
}
The purpose of the contructor is to load the contents of a directory into a string. What I am try to do is display the error window when the files fail to open.
ErrorWindow.nib has a single window with an OK button and a NSTextView for the error, I set up a NSWindowController in the nib and connected it to the window.
My only link has been that most examples show this [errorWindowControl showWindow: self];
rather than showWindow: nil but because this is a C++ constructor I it doesn't have self and this doesn't work.
If it matters this contructor is called from the awakeFromNib method of the MainMenu.nib's primary NSObject.
Thanks
A bit of an odd way to approach Cocoa. I would encourage you to step back, learn Objective-C and then write your application with an Objective-C based Cocoa UI layer on top of whatever backing store or model layer you have.
In any case, there isn't anything particularly wrong with that code (save for the odd design).
The first thing to check is the return value of -initWithWindowNibName:. Is errorWindowControl actually non-nil? If it is nil, then the NIB failed to load.
How are you writing the Cocoa application itself? Standard app bundle using Xcode, I hope?
Also, you shouldn't be hardcoding the path to /Library/Application Support/. Actually, your application shouldn't use that directory as the only storage location; many users won't have write access to that directory and won't be able to install your app without administrator access.