1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2 Copyright (C) 2006 and later, Cockos, Inc.
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
21 #import "swellappmain.h"
24 #include "swell-internal.h"
27 HMENU SWELL_app_stocksysmenu; // exposed to app, simply the contents of the default system menu (as defined in the nib)
28 static bool IsMultiLineEditControl(NSView *cv, id fs)
30 if (fs && [fs isKindOfClass:[NSTextView class]])
32 NSTextView *v = (NSTextView *)fs;
35 NSView *a=[v superview];
38 if ([a isKindOfClass:[NSTextField class]]) return false;
47 @implementation SWELLApplication
48 - (void)sendEvent:(NSEvent *)anEvent
50 int etype = [anEvent type];
53 // toss keyup if next keydown is the same key
54 NSEvent *nextDown = [self nextEventMatchingMask:NSKeyDownMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0.003] inMode:NSDefaultRunLoopMode dequeue:FALSE];
55 if (nextDown && [nextDown keyCode] == [anEvent keyCode]) return;
57 else if (etype == NSKeyDown)
59 NSEvent *nextDown = [self nextEventMatchingMask:NSKeyDownMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0.003] inMode:NSDefaultRunLoopMode dequeue:FALSE];
60 if (nextDown && [nextDown keyCode] == [anEvent keyCode])
63 // no need to check timestamps -- if a queued key is there, just ignore this one(prevent a backlog)
67 struct mach_timebase_info inf={0,};
68 mach_timebase_info(&inf);
69 if (inf.numer && inf.denom) sc = inf.numer / (inf.denom * 1000.0 * 1000.0 * 1000.0);
72 if (sc != 0.0 && [anEvent timestamp] < (double) mach_absolute_time() * sc - 0.05)
79 NSWindow *modalWindow = [NSApp modalWindow];
81 NSWindow *focwnd=[anEvent window];
82 NSView *dest_view=NULL; // only valid when key message
84 if (etype==NSKeyDown||etype==NSKeyUp)
86 const UINT msgtype = etype==NSKeyDown ? WM_KEYDOWN : WM_KEYUP;
87 int flag,code=SWELL_MacKeyToWindowsKey(anEvent,&flag);
91 if (flag&(FCONTROL|FALT))
94 // handle carbon windows, sending all cmd/alt modified keys to their parent NSView (to be handled later)
95 // perhaps it'd be good to have a flag on these to see if they want it .. i.e. SWELL_SetCarbonHostView_WantKeyFlgs()..
98 if ((dest_view=(NSView *)[f delegate]) && [dest_view respondsToSelector:@selector(swellIsCarbonHostingView)] && [(SWELL_hwndCarbonHost*)dest_view swellIsCarbonHostingView])
100 focwnd = [dest_view window];
107 if (!dest_view) // get default dest_view, and validate it as a NSView
109 if ((dest_view=(NSView *)[focwnd firstResponder]) && ![dest_view isKindOfClass:[NSView class]]) dest_view=NULL;
112 if (!modalWindow && (!focwnd || dest_view))
114 MSG msg={(HWND)dest_view,msgtype,(WPARAM)code,(LPARAM)flag}; // LPARAM is treated differently (giving modifier flags/FVIRTKEY etc) in sWELL's WM_KEYDOWN than in windows, deal with it
116 if (SWELLAppMain(SWELLAPP_PROCESSMESSAGE,(INT_PTR)&msg,(INT_PTR)anEvent)>0) return;
119 // default window handling:
120 if (etype == NSKeyDown && focwnd && dest_view)
122 NSView *cv = [focwnd contentView];
123 if (cv && [cv respondsToSelector:@selector(onSwellMessage:p1:p2:)]) //only work for swell windows
125 int flag,code=SWELL_MacKeyToWindowsKey(anEvent,&flag);
128 // todo: other keys (such as shift+insert?)
129 if (((flag&~FVIRTKEY)==FCONTROL && (code=='V'||code=='C' ||code=='X')) && [dest_view isKindOfClass:[NSText class]])
131 if (code=='V') [(NSText *)dest_view paste:(id)cv];
132 else if (code=='C') [(NSText *)dest_view copy:(id)cv];
133 else if (code=='X') [(NSText *)dest_view cut:(id)cv];
137 if ((!(flag&~(FVIRTKEY|FSHIFT)) && code == VK_ESCAPE) ||
138 ((flag&~FVIRTKEY)==FCONTROL && code=='W'))
140 if (code == 'W') cmdid= IDCANCEL; // cmd+w always idcancel's
141 else if (!dest_view || ![dest_view isKindOfClass:[NSTextView class]]) cmdid=IDCANCEL; // not text view, idcancel
142 else if (!(flag&FSHIFT) && !IsMultiLineEditControl(cv,dest_view)) cmdid=IDCANCEL; // if singleline edit and shift not set, idcancel
150 else if (!(flag&~FVIRTKEY) && code == VK_RETURN)
152 // get default button command id, if any, if enabled
153 if (!IsMultiLineEditControl(cv,dest_view))
155 cmdid = SWELL_GetDefaultButtonID((HWND)cv,true);
157 if (!cmdid) // no action, set focus to parent
167 SendMessage((HWND)cv,WM_COMMAND,cmdid,0);
173 [super sendEvent:anEvent];
178 @implementation SWELLAppController
182 SWELL_EnsureMultithreadedCocoa();
183 [NSApp setDelegate:self];
185 SWELL_POSTMESSAGE_INIT
187 HMENU stockMenu=(HMENU)[NSApp mainMenu];
190 HMENU nf = GetSubMenu(stockMenu,0);
191 if (nf) SWELL_app_stocksysmenu = SWELL_DuplicateMenu(nf);
194 SWELLAppMain(SWELLAPP_ONLOAD,0,0);
197 -(IBAction)onSysMenuCommand:(id)sender
199 int a = [sender tag];
200 if (a) SWELLAppMain(SWELLAPP_ONCOMMAND,a,(INT_PTR)sender);
203 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
207 SWELL_CFStringToCString(filename,buf,sizeof(buf));
208 return buf[0] && SWELLAppMain(SWELLAPP_OPENFILE,(INT_PTR)buf,0)>0;
210 - (BOOL)applicationOpenUntitledFile:(NSApplication *)theApplication
212 return SWELLAppMain(SWELLAPP_NEWFILE,0,0)>0;
215 - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
217 return SWELLAppMain(SWELLAPP_SHOULDOPENNEWFILE,0,0)>0;
220 -(void)applicationDidFinishLaunching:(NSNotification *)aNotification
222 SWELLAppMain(SWELLAPP_LOADED,0,0);
225 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
227 return SWELLAppMain(SWELLAPP_SHOULDDESTROY,0,0) > 0 ? NSTerminateLater : NSTerminateNow;
230 - (void)applicationWillTerminate:(NSNotification *)notification
232 SWELLAppMain(SWELLAPP_DESTROY,0,0);
235 - (void)applicationDidBecomeActive:(NSNotification *)aNotification
237 if (!SWELLAppMain(SWELLAPP_ACTIVATE,TRUE,0))
238 SWELL_BroadcastMessage(WM_ACTIVATEAPP,TRUE,0);
241 - (void)applicationDidResignActive:(NSNotification *)aNotification
243 if (!SWELLAppMain(SWELLAPP_ACTIVATE,FALSE,0))
244 SWELL_BroadcastMessage(WM_ACTIVATEAPP,FALSE,0);
247 SWELL_APPAPI_DELEGATE_IMPL
249 SWELL_POSTMESSAGE_DELEGATE_IMPL