I created a command line tool. I have my project almost done, but now I need to create an user interface to ask for user credentials.
Is possible to add a window to my command line project, or I have to create a cocoa application to do that?
A command-line tool won't have a connection to the window server (that's done by NSApplication), so no, it can't create a window. If you need to show a window for any reason, it probably should be an application at that point anyway, so you should go with that and make it one.
A Cocoa App would work. I haven't used Tcl/Tk or MacRuby, but if you're brand new to Cocoa you might have an easier time picking them up.
You can't do this in AppleScript without using AppKit APIs; see Prompt user for password with dialog window when using sudo.
If you're really asking an Xcode question, you might want to repost something more specific, likeĀ "how do I convert my Command-Line project so I can add a Cocoa window."
Yea Peter is right, you should make it into an application but that doesn't mean you need to convert the tool into a Cocoa app, you can keep a command line tool alive with a UI by utilising the NSApplication class but not using NSApplicationMain() to launch it. In your situation however I don't think this is the necessary method but anyway here is how I displayed a window in a command line tool:
#interface CustomAppClass : NSApplication
#property (strong) NSWindow *theWindow;
#end
#implementation CustomAppClass {
BOOL terminate;
}
- (void)finishLaunching {
/* Draw windows and stuff */
self.theWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 300, 200) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
[self.theWindow setTitle:#"Why Hello There!"];
[self.theWindow center];
[self.theWindow makeKeyAndOrderFront:self.theWindow];
}
// Override run and terminate: methods
- (void)run {
terminate = NO;
[self finishLaunching];
do {
NSEvent *event = [self nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES];
[self sendEvent:event];
[self updateWindows];
} while (!terminate);
}
- (void)terminate:(id)sender {
terminate = YES;
}
#end
int main(int argc, const char * argv[]) {
// Do all your pre functions here
/* Make NSApplication from CustomAppClass */
CustomAppClass *appObject = [CustomAppClass sharedApplication];
if ([appObject respondsToSelector:#selector(run)]) {
[appObject performSelectorOnMainThread:#selector(run) withObject:nil waitUntilDone:YES];
}
// Do all your post functions here.
return 0;
}
Sorry I didn't describe how to ask for credentials and stuff but if you want an official window based authentication (not sudo) you should probably look into the Security framework.
Some Credit to https://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html
Hope this helps in any way.
Related
I'm using the Dropbox API for OSX. All works fine, except when I want to make calls in a modal window that is started with [NSApp runModalForWindow:thisWindow]; it seems the modal loop blocks the
DropboxAPI from processing anything.
The DBRestClient delegate methods are never called in response to for example [client loadMetadata:path]; Which is - if understand correctly - in line with what the NSApplication documentation says for this method. The question is:
Is there a way to let calls to Dropbox work from inside a modal window?
I have seen that timers can be added to the NSModalPanelRunLoopMode. Is there perhaps something similar for the DroboxAPI?
And additionally: will Dropbox calls that were started but not yet completed before this or any other modal window is displayed proceed as normal, or are they also blocked?
Yes; further investigation shows any runModalForWindow and even displaying an NSAlert.showModal will completely block the DropboxAPI. Also, inplace mouse handling loops do the same thing. Imo a major design flaw in the OSX DropboxAPI: it should have been running on a background thread. The only way around this, is to not start any user task that could involve blocking Dropbox while the API something is still running. Which is not really feasible in any non-trivial app that needs dropbox to work in the background.
Implementation of NSApplication runModalForWindow is indeed too harsh.
Here is more humanistic version:
void runWindowModal(NSWindow* pnw) {
[pnw retain];
NSApplication* app = [NSApplication sharedApplication];
NSModalSession session = [app beginModalSessionForWindow:pnw];
for( ;; ) {
if ([app runModalSession:session] != NSModalResponseContinue)
break;
NSEvent* e = [app nextEventMatchingMask: NSAnyEventMask
untilDate: [NSDate distantFuture]
inMode: NSDefaultRunLoopMode
dequeue: YES];
if (e != nil)
[app sendEvent: e];
}
[app endModalSession: session];
[pnw release];
}
This will enable NSURLConnection and other callbacks processing while running modal loops.
Dropbox support just confirmed that the DropboxAPI on OSX works on the main runloop and indeed runModalForWindow etc. will block the API. There's no work around.
The problem is more general. All nsurl connections can run only on the main runloop. This means that webviews have the same issue. And there is a sollution to this. You have to create a modal session and run it for short amounts of time, frequently, and lock the main thread in a loop until the modal window is closed. Every time you switch back to the modal session, the main runloop will finish any scheduled tasks, and that includes the nsurlconnections from the modal session.
Now I will show you a piece of code but note that this is c# using Xamarin.Mac library. You should be able to translate this easily to objc. But if you find it difficult, you can look up solutions for nswebview in modal dialog.
//so this is in my NSWindowController - the modal dialog
IntPtr session;
bool running;
public void showDialog ()//this is the method I use to show the dialog
{
running = true;
session = NSApplication.SharedApplication.BeginModalSession (Window);
int resp = (int)NSRunResponse.Continues;
while (running && resp == (int)NSRunResponse.Continues) {
resp = (int)NSApplication.SharedApplication.RunModalSession (session);
NSRunLoop.Current.RunUntil (NSRunLoopMode.Default, NSDate.DistantFuture);
}
NSApplication.SharedApplication.EndModalSession (session);
}
[Export ("windowWillClose:")]
public void WindowWillClose (NSNotification notification)//this is the override of windowWillClose:
{
running = false;
}
I am developing in MAC and need to get the list of all the active applications currently running, I mean the ones which have actual window and the user can see/close/minimize/maximize.
I tried using NSWorkspace runningApplications function, but it gives a long list of applications (most probably it also lists some hidden applications) but I need only the ones that has window UI.
I've also used the suggestion in the following post to get the windows below the Dock and it worked fine in case if the Dock is visible:
CGWindowListCreate generates a hugely long list of windows
However when the Dock is hidden this solution doesn't work.
Does anyone have any idea how to get the list of running applications visible to user on MAC?
It may help you. Try this
for (NSRunningApplication *app in [[NSWorkspace sharedWorkspace] runningApplications]) {
NSLog(#"%#",[app localizedName]);
}
#import <Foundation/Foundation.h>
void ListWindows()
{
NSMutableArray* windows =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly |
kCGWindowListExcludeDesktopElements,
kCGNullWindowID);
for (NSDictionary* window in windows) {
if([[window objectForKey:#"kCGWindowLayer" ] intValue] == 0)
{
NSLog(#"%#", [window objectForKey:#"kCGWindowOwnerName"]);
}
}
}
int main(int argc, const char * argv[])
{
NSLog(#"Active windows:");
ListWindows();
return 0;
}
Sample output :
Active windows:
Xcode
TextEdit
Finder
I create global shortcut with this code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent * theEvent) {
int modiferKeys = NSControlKeyMask | NSAlternateKeyMask;
if (([theEvent keyCode] == 7 && modiferKeys)) {
NSLog (#"%#",#"work");
}
}];
}
This code work perfect, but when some application have a focus (for example Xcode, AppStore...) the system play Error sound.
How fix it?
Thaks.
A different way to implement a global hot key is using RegisterEventHotKey, declared in CarbonEvents.h. The docs for it might be under "legacy", but as far as I can tell from the header, it's not deprecated and is available in 64-bit code.
im using 3.2.6 iOS 4.3 to begins with. now the question i have is its telling me I have an (located on the "(#Implementation window, hvController;"):
Expected Identifier or '(' before '#' token
in the Delegate.m class
//HelloUniverseAppDelegate.M
(#Implementation window, hvController;
(...
//HelloUniverseAppDelegate.m
- (void)applicationDidFinishishLaunching:(UIApplication *)application {
HelloUniverseController *hvc = [[HelloUniverseController alloc]
inWihhNibName:#"HelloUniverse" bundle:[NSBundle mainBundle]];
self.hvController = hvc;
[hvc release];
[window addSubview:[self.hvController view]];
// Override point for customization after application launch
[window makeKeyAndVisible ;
}
- (Void)dealloc {
[hvController release];
[window release];
[super dealloc];
}
HelloUniverseController *hvc = [HelloUniverseController alloc];
hvc = [hvc initWithNibName:#"HelloUniverse" bundle:[NSBundle mainBundle]];
#import "HelloUniverseAppDelegate.h"
#implementation HelloUniverseAppDelegate
#synthesize window;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
}
#pragma mark -
#pragma mark Memory management
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
/*
Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
*/
}
- (void)dealloc {
[window release];
[super dealloc];
}
#end
now also its telling me I have a similar error in the Controller.h with
Expected Identifier before '{' token
its located on the "- (void)dealloc :{" line. the code I have for it is
#import <UIKit/UIKit.h>
#interface HelloUniverseController : UIViewController {
IBOutlet UITextField *txtFirstName;
IBOutlet UITextField *txtLastName;
IBOutlet UILabel *lblMessage;
}
- (IBAction) btnClickMe_Clicked:(id)sender;
//HelloUniverseController.m
- (void)dealloc :{
[txtFirstName release];
[txtLastName release];
[lblMessage release];
[super dealloc];
}
#end
im also very new at this, my second day ever using a mactonish as well as first with Xcode.
Objective-C requires that classes have two parts: the interface, which usually appears in a .h file and declares the class' instance variables, properties, and methods; and the implementation, which should be in a .m file and defines the class' methods. You seem to be trying to define the -dealloc method inside an #interface block rather than inside an #implementation block, and that's leading to at least one of the errors you're getting.
Frankly, one cannot avoid the impression that you've copied and pasted code from somewhere without understanding very much about the language. There are a number of blatant errors that just don't make any sense, including the one I just described and the #import statement that seems to be part of your -applicationDidFinishLaunching method.
StackOverflow is a great community that will, I'm sure, be happy to help you as you learn to program. However, you'll quickly wear out your welcome if you haven't made an effort to learn the basic syntax of the language. More importantly, it's really not going to help you if we just keep correcting your errors. Please, please read Learning Objective-C: A Primer. Or, if you don't have any experience with C-based languages (C, C++, C#, Java...) and some form of object-oriented programming, consider picking up a third-party book aimed at beginners. Thanks to the huge popularity of iPhone and iPad, there are plenty of books to choose from.
Remove this
(
before
(#implementation
I am developing an plugin for OsiriX.
In that app i have 3-4 nib files. Also in for plugin there are files (.h & .m) called PluginFilter where method called - (long) filterImage:(NSString) menuName is present from which the plugin start execution. Now my problem is that, I have return the code to launch main window is in some other .m file and I have to call that file using the method mentioned above.
The app has multiple nib files. I have a plugin name PluginFilter called by:
- (long) filterImage:(NSString*) menuName
The plugin should open a window when called by this method. The code that defines the window controller is in another nib. When I call the filterimage method in the plugin, the window never appears.
Here is my filterImage: method.
#import "XGridInOsiriXFilter.h"
#import "MainWindowController.h"
#implementation XGridInOsiriXFilter
- (void) initPlugin
{
}
- (long) filterImage:(NSString*) menuName
{
MainWindowController *mainWindowController = [[GridSampleMainWindowController alloc] init];
[mainWindowController showWindow:self ];
[mainWindowController release];
return 0;
}
#end
Calling the method produces not warnings or errors, the window simply fails to appear.
I recognize this may be coming a little too late but I was looking for a way to do the same thing you are asking for and found it. You can use NSBundle to load the desired nib and point it to an instantiated controller. Like:
#implementation YourPluginFilter
- (void) initPlugin
{
yourWindowController = [[YourWindowController alloc] init];
NSLog(#"Initialized YourWindowController");
}
- (long) filterImage:(NSString*) menuName
{
if (yourWindowController && [NSBundle loadNibNamed:#"YourNibName" owner:yourWindowController]) {
NSLog(#"Activated yourWindowController");
return 0;
} else {
return -1;
}
}
#end
You would normally not open the app's main window from a plugin. Plugins by definition my not always be present so you should not put critical code in them. Neither would you want multiple plugins opening the same logical window.
Instead, the main window should be displayed by the app delegate as normal but the content of the window can be processed by a plugin if the plugin is available.
The main application should load and configure the main window and only call the plugin to process the contents of the window.
Even so it is technically possible to open a window from a plugin so either (1) the plugin is not being loaded and the method is not being called (insert breakpoint/log to confirm) or (2) the window controller is misconfigured so that it does not open the window. Test the controller outside the plugin to confirm it works. Better yet, move the window opening code outside the plugin.
Edit01:
From comment:
I have made some changes in the above
code as follows
- (long) filterImage:(NSString*) menuName {
MainWindowController *mainWindowController = [[GridSampleMainWindowController alloc] init:self];
[mainWindowController showWindow:self ];
[mainWindowController release];
return 0;
}
but it is showing wanring that no
-init method found. Why it is showing like this because -init method is der
in the MainWindowController.m file
Well, you have two problems here.
(1) You set define mainWindowController as of class MainWindowController but you initialize it with class GridSampleMainWindowController. If MainWindowController is a subclass of GridSampleMainWindowController this will work but will generate warnings. You should instead initialize it like
GridSampleMainWindowController *mainWindowController = [[GridSampleMainWindowController alloc] init:self];
or
MainWindowController *mainWindowController = [[MainWindowController alloc] init:self];
(2) You release the controller without any other object retaining it which will kill it. When a window controller dies it deallocates the windows it controls. This is most likely why you see nothing.
You should sort out what class you want the controller to be and then set it as a retained property of the plugin class so you can keep it an its window around.
Which init method is it complaining about? Your initPlugin does nothing and returns a void if that is the plugin's actual initialization method then the plugin will never load. It should at least look like this:
- (id) initPlugin
{
self=[super init];
return self;
}
It looks like you come from a pure C background which is great for this environment but you need to learn about the object oriented parts of the Objective-C language. You're still writing methods as if they were old school C functions and there are important and oft times subtle differences.
Sorry I missed all this yesterday. I saw "plugin" and focused on the wrong aspect of the problem.
Edit02:
No i m not talking about my initPlugin
method. I am talking about my init
method which is there in
MainWindowController.m file
- (id)init {
self = [super initWithWindowNibName:#"MainWindow"];
return self;
}
This will return an instance of the MainWindowController's super class. If you're not doing any customization you have no need to override the init method in you subclass. Just use the inherited version thusly:
MainWindowController *mainWindowController = [[MainWindowController alloc] initWithWindowNibName:#"MainWindow"];