1/*
2  copyright 2002, 2003 Alexander Malmberg <alexander@malmberg.org>
3
4  2009-2016 GAP Project
5
6  Authors: Alexander Malmberg <alexander@malmberg.org>
7           Riccardo Mottola
8           Tim Sheridan
9
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; version 2 of the License.
13
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22*/
23
24#import <Foundation/NSAutoreleasePool.h>
25#import <Foundation/NSString.h>
26#import <Foundation/NSBundle.h>
27#import <Foundation/NSDebug.h>
28#import <Foundation/NSNotification.h>
29#import <Foundation/NSProcessInfo.h>
30#import <Foundation/NSRunLoop.h>
31#import <Foundation/NSUserDefaults.h>
32#import <Foundation/NSDictionary.h>
33#import <AppKit/NSApplication.h>
34#import <AppKit/NSControl.h>
35#import <AppKit/NSEvent.h>
36#import <AppKit/NSMenu.h>
37#import <AppKit/NSMenuItem.h>
38#import <AppKit/NSPanel.h>
39#import <AppKit/NSView.h>
40
41/* For the quit panel: */
42#import <AppKit/NSTextField.h>
43#import <AppKit/NSButton.h>
44#import <AppKit/NSImage.h>
45#import <AppKit/NSImageView.h>
46#import <AppKit/NSScreen.h>
47#import <AppKit/NSBox.h>
48#import <GNUstepGUI/GSVbox.h>
49#import <GNUstepGUI/GSHbox.h>
50#import "Label.h"
51
52
53#import "PreferencesWindowController.h"
54#import "Services.h"
55#import "ServicesPrefs.h"
56#import "TerminalView.h"
57#import "TerminalViewPrefs.h"
58#import "TerminalWindow.h"
59#import "TerminalWindowPrefs.h"
60#import "TerminalParser_LinuxPrefs.h"
61
62@interface NSMenu (helpers)
63-(id <NSMenuItem>) addItemWithTitle: (NSString *)s;
64-(id <NSMenuItem>) addItemWithTitle: (NSString *)s  action: (SEL)sel;
65@end
66@implementation NSMenu (im_lazy)
67-(id <NSMenuItem>) addItemWithTitle: (NSString *)s
68{
69	return [self addItemWithTitle: s  action: NULL  keyEquivalent: nil];
70}
71
72-(id <NSMenuItem>) addItemWithTitle: (NSString *)s  action: (SEL)sel
73{
74	return [self addItemWithTitle: s  action: sel  keyEquivalent: nil];
75}
76@end
77
78
79@interface Terminal : NSObject
80{
81	PreferencesWindowController *pwc;
82
83	NSPanel *quitPanel;
84	BOOL quitPanelOpen;
85}
86
87@end
88
89@implementation Terminal
90
91- init
92{
93	if (!(self=[super init])) return nil;
94	return self;
95}
96
97-(void) dealloc
98{
99	[[NSNotificationCenter defaultCenter]
100		removeObserver: self];
101
102	DESTROY(pwc);
103	[super dealloc];
104}
105
106
107-(void) openPreferences: (id)sender
108{
109	if (!pwc)
110	{
111		NSObject<PrefBox> *pb;
112		pwc=[[PreferencesWindowController alloc] init];
113
114		pb=[[TerminalViewDisplayPrefs alloc] init];
115		[pwc addPrefBox: pb];
116		DESTROY(pb);
117
118		pb=[[TerminalViewShellPrefs alloc] init];
119		[pwc addPrefBox: pb];
120		DESTROY(pb);
121
122		pb=[[TerminalViewKeyboardPrefs alloc] init];
123		[pwc addPrefBox: pb];
124		DESTROY(pb);
125
126		pb=[[TerminalWindowPrefs alloc] init];
127		[pwc addPrefBox: pb];
128		DESTROY(pb);
129
130		pb=[[TerminalServicesPrefs alloc] init];
131		[pwc addPrefBox: pb];
132		DESTROY(pb);
133
134		pb=[[TerminalParser_LinuxPrefs alloc] init];
135		[pwc addPrefBox: pb];
136		DESTROY(pb);
137	}
138	[pwc showWindow: self];
139}
140
141
142-(void) applicationWillTerminate: (NSNotification *)n
143{
144}
145
146
147-(void) applicationWillFinishLaunching: (NSNotification *)n
148{
149	NSMenu *menu,*m/*,*m2*/;
150
151	[TerminalView registerPasteboardTypes];
152
153	menu=[[NSMenu alloc] init];
154
155	/* 'Info' menu */
156	m=[[NSMenu alloc] init];
157	[m addItemWithTitle: _(@"Info...")
158		action: @selector(orderFrontStandardInfoPanel:)];
159	[m addItemWithTitle: _(@"Preferences...")
160		action: @selector(openPreferences:)];
161	[m addItemWithTitle: _(@"Benchmark")
162		action: @selector(benchmark:)];
163	[menu setSubmenu: m forItem: [menu addItemWithTitle: _(@"Info")]];
164	[m release];
165
166	/* 'Terminal' menu */
167	m=[[NSMenu alloc] init];
168	[m addItemWithTitle: _(@"New window")
169		action: @selector(openWindow:)
170              keyEquivalent: @"n"];
171	[m addItemWithTitle: _(@"New tab")
172		action: @selector(openTab:)
173		keyEquivalent: @"t"];
174        [menu setSubmenu: m forItem: [menu addItemWithTitle: _(@"Shell")]];
175	[m release];
176
177
178	/* 'Edit' menu */
179	m=[[NSMenu alloc] init];
180	[m addItemWithTitle: _(@"Cut")
181		action: @selector(cut:)
182		keyEquivalent: @"x"];
183	[m addItemWithTitle: _(@"Copy")
184		action: @selector(copy:)
185		keyEquivalent: @"c"];
186	[m addItemWithTitle: _(@"Paste")
187		action: @selector(paste:)
188		keyEquivalent: @"v"];
189	[menu setSubmenu: m forItem: [menu addItemWithTitle: _(@"Edit")]];
190	[m release];
191
192	/* 'View' menu */
193	m=[[NSMenu alloc] init];
194	[m addItemWithTitle: _(@"Show tab bar")
195		action: @selector(showTabBarToggle:)
196		keyEquivalent: @"T"];
197	[menu setSubmenu: m forItem: [menu addItemWithTitle: _(@"View")]];
198	[m release];
199
200	/* 'Windows' menu */
201	m=[[NSMenu alloc] init];
202	[m addItemWithTitle: _(@"Close window")
203		action: @selector(performClose:)
204		keyEquivalent: @"W"];
205	[m addItemWithTitle: _(@"Close tab")
206		action: @selector(closeTab:)
207		keyEquivalent: @"w"];
208	[m addItemWithTitle: _(@"Show previous tab")
209		action: @selector(showPreviousTab:)
210		keyEquivalent: @"{"];
211	[m addItemWithTitle: _(@"Show next tab")
212		action: @selector(showNextTab:)
213		keyEquivalent: @"}"];
214	[m addItemWithTitle: _(@"Move tab left")
215		action: @selector(moveTabLeft:)
216		keyEquivalent: @"("];
217	[m addItemWithTitle: _(@"Move tab right")
218		action: @selector(moveTabRight:)
219		keyEquivalent: @")"];
220	[m addItemWithTitle: _(@"Miniaturize all")
221		action: @selector(miniaturizeAll:)
222		keyEquivalent: @"m"];
223	[menu setSubmenu: m forItem: [menu addItemWithTitle: _(@"Windows")]];
224	[NSApp setWindowsMenu: m];
225	[m release];
226
227	m=[[NSMenu alloc] init];
228	[menu setSubmenu: m forItem: [menu addItemWithTitle: _(@"Services")]];
229	[NSApp setServicesMenu: m];
230	[m release];
231
232	[menu addItemWithTitle: _(@"Hide")
233		action: @selector(hide:)
234		keyEquivalent: @"h"];
235
236	[menu addItemWithTitle: _(@"Quit")
237		action: @selector(terminate:)
238		keyEquivalent: @"q"];
239
240	[NSApp setMainMenu: menu];
241	[menu release];
242
243	[[NSNotificationCenter defaultCenter]
244		addObserver: self
245		selector: @selector(noMoreActiveWindows:)
246		name: TerminalWindowNoMoreActiveWindowsNotification
247		object: nil];
248}
249
250-(void) openWindow: (id)sender
251{
252	TerminalWindowController *twc;
253	twc=[TerminalWindowController newTerminalWindow];
254	[[twc frontTerminalView] runShell];
255}
256
257-(void) openTab: (id)sender
258{
259	TerminalWindowController *twc = [[NSApp keyWindow] windowController];
260	[twc newTerminalTabInWindow:[NSApp keyWindow]];
261	[[twc frontTerminalView] runShell];
262}
263
264-(void) closeTab: (id)sender
265{
266	TerminalWindowController *twc = [[NSApp keyWindow] windowController];
267	TerminalView *tv = [twc frontTerminalView];
268	[twc closeTerminalTab:tv inWindow:[NSApp keyWindow]];
269}
270
271-(void) showTabBarToggle: (id)sender
272{
273	TerminalWindowController *twc = [[NSApp keyWindow] windowController];
274	[twc setShowTabBar:(! [twc showTabBar]) inWindow:[NSApp keyWindow]];
275}
276
277-(void) showPreviousTab: (id)sender
278{
279	TerminalWindowController *twc = [[NSApp keyWindow] windowController];
280	[twc showPreviousTab];
281}
282
283-(void) showNextTab: (id)sender
284{
285	TerminalWindowController *twc = [[NSApp keyWindow] windowController];
286	[twc showNextTab];
287}
288
289-(void) moveTabLeft: (id)sender
290{
291	TerminalWindowController *twc = [[NSApp keyWindow] windowController];
292	[twc moveTabLeft];
293}
294
295-(void) moveTabRight: (id)sender
296{
297	TerminalWindowController *twc = [[NSApp keyWindow] windowController];
298	[twc moveTabRight];
299}
300
301
302-(void) applicationDidFinishLaunching: (NSNotification *)n
303{
304	NSArray *args=[[NSProcessInfo processInfo] arguments];
305
306	[TerminalServices updateServicesPlist];
307
308	[NSApp setServicesProvider: [[TerminalServices alloc] init]];
309
310	if ([args count]>1)
311	{
312		TerminalWindowController *twc;
313		NSString *cmdline;
314
315		args=[args subarrayWithRange: NSMakeRange(1,[args count]-1)];
316		cmdline=[args componentsJoinedByString: @" "];
317
318		twc=[TerminalWindowController newTerminalWindow];
319		[[twc frontTerminalView] runProgram: @"/bin/sh"
320			withArguments: [NSArray arrayWithObjects: @"-c",cmdline,nil]
321			initialInput: nil];
322                [twc release];
323	}
324	else
325		[self openWindow: self];
326}
327
328
329-(NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *)sender
330{
331	if (![TerminalWindowController numberOfActiveWindows])
332		return NSTerminateNow;
333
334	if (!quitPanel)
335	{
336		NSButton *b_quit,*b_dont;
337
338		{
339			GSVbox *vb=[[GSVbox alloc] init];
340
341			[vb setBorder: 10.0];
342
343			{
344				NSButton *butt;
345				GSHbox *hb;
346
347				hb=[[GSHbox alloc] init];
348				[hb setAutoresizingMask: NSViewMinXMargin];
349
350				butt=b_quit=[[NSButton alloc] init];
351				[butt setTitle: _(@"Quit anyway")];
352				[butt setTarget: self];
353				[butt setAction: @selector(quitAnyway:)];
354				[butt sizeToFit];
355				[hb addView: butt  enablingXResizing: NO];
356				DESTROY(butt);
357
358				butt=b_dont=[[NSButton alloc] init];
359				[butt setTitle: _(@"Don't quit")];
360				[butt setImagePosition: NSImageRight];
361				[butt setImage: [NSImage imageNamed: @"common_ret"]];
362				[butt setTarget: self];
363				[butt setAction: @selector(dontQuit:)];
364				[butt sizeToFit];
365				[hb addView: butt  enablingXResizing: NO withMinXMargin: 8.0];
366				DESTROY(butt);
367
368				[vb addView: hb enablingYResizing: NO];
369				DESTROY(hb);
370			}
371
372			{
373				NSTextField *text;
374
375				text=[NSTextField newLabel:
376					_(@"There are active windows. Quitting will close them.")];
377				[text setAutoresizingMask: NSViewWidthSizable|NSViewMinYMargin];
378				[text sizeToFit];
379
380				[vb addView: text enablingYResizing: YES withMinYMargin: 8.0];
381				DESTROY(text);
382			}
383
384			[vb addSeparatorWithMinYMargin: 8.0];
385
386			{
387				NSImageView *iv;
388				NSTextField *title;
389				GSHbox *hb;
390
391				hb=[[GSHbox alloc] init];
392
393				iv=[[NSImageView alloc] init];
394				[iv setImage: [NSApp applicationIconImage]];
395				[iv setEditable: NO];
396				[iv sizeToFit];
397				[hb addView: iv enablingXResizing: NO];
398				DESTROY(iv);
399
400				title=[NSTextField newLabel: _(@"Quit?")];
401				[title setAutoresizingMask: NSViewMinYMargin|NSViewMaxYMargin];
402				[title setFont: [NSFont systemFontOfSize: 18.0]];
403				[title sizeToFit];
404				[hb addView: title enablingXResizing: NO withMinXMargin: 8.0];
405				DESTROY(title);
406
407				[vb addView: hb enablingYResizing: NO withMinYMargin: 8.0];
408				DESTROY(hb);
409			}
410
411			[vb sizeToFit];
412
413			quitPanel=[[NSPanel alloc] initWithContentRect: [vb frame]
414				styleMask: NSTitledWindowMask
415				backing: NSBackingStoreRetained
416				defer: YES];
417			[quitPanel setContentView: vb];
418			DESTROY(vb);
419		}
420
421		[quitPanel setTitle: _(@"Quit?")];
422		[quitPanel setOneShot: YES];
423		[quitPanel setHidesOnDeactivate: NO];
424		[quitPanel setExcludedFromWindowsMenu: NO];
425
426		[quitPanel setInitialFirstResponder: b_dont];
427		[b_dont setNextKeyView: b_quit];
428		[b_quit setNextKeyView: b_dont];
429	}
430
431	{
432		/* TODO: always using +mainScreen is probably incorrect */
433		NSRect r=[[NSScreen mainScreen] frame];
434		NSPoint o;
435
436		o.x=r.origin.x+r.size.width/2.0-[quitPanel frame].size.width/2.0;
437		o.y=r.origin.y+r.size.height/2.0-[quitPanel frame].size.height/2.0;
438		[quitPanel setFrameOrigin: o];
439	}
440
441	[quitPanel setExcludedFromWindowsMenu: NO];
442	[quitPanel makeKeyAndOrderFront: self];
443	quitPanelOpen=YES;
444
445	return NSTerminateLater;
446}
447
448-(void) quitAnyway: (id)sender
449{
450	[NSApp replyToApplicationShouldTerminate: YES];
451}
452
453-(void) dontQuit: (id)sender
454{
455	[NSApp replyToApplicationShouldTerminate: NO];
456	quitPanelOpen=NO;
457	[quitPanel orderOut: self];
458	[quitPanel setExcludedFromWindowsMenu: YES];
459}
460
461-(void) noMoreActiveWindows: (NSNotification *)n
462{
463	if (quitPanelOpen)
464		[NSApp replyToApplicationShouldTerminate: YES];
465}
466
467
468-(BOOL) application: (NSApplication *)sender
469	openFile: (NSString *)filename
470{
471	TerminalWindowController *twc;
472
473	NSDebugLLog(@"Application",@"openFile: '%@'",filename);
474
475	/* TODO: shouldn't ignore other apps */
476	[NSApp activateIgnoringOtherApps: YES];
477
478	twc=[TerminalWindowController newTerminalWindow];
479	[[twc frontTerminalView] runProgram: filename
480		withArguments: nil
481		initialInput: nil];
482
483	return YES;
484}
485
486
487
488-(BOOL) terminalRunProgram: (NSString *)path
489	withArguments: (NSArray *)args
490	inDirectory: (NSString *)directory
491	properties: (NSDictionary *)properties
492{
493	TerminalWindowController *twc;
494
495	NSDebugLLog(@"Application",
496		@"terminalRunProgram: %@ withArguments: %@ inDirectory: %@ properties: %@",
497		path,args,directory,properties);
498
499	/* TODO: shouldn't ignore other apps */
500	[NSApp activateIgnoringOtherApps: YES];
501
502	{
503		id o;
504		o=[properties objectForKey: @"CloseOnExit"];
505		if (o && [o respondsToSelector: @selector(boolValue)] &&
506		    ![o boolValue])
507		{
508			twc=[TerminalWindowController idleTerminalWindow];
509		}
510		else
511		{
512			twc=[TerminalWindowController newTerminalWindow];
513		}
514	}
515
516	[[twc frontTerminalView] runProgram: path
517		withArguments: args
518		inDirectory: directory
519		initialInput: nil
520		arg0: nil];
521
522	return YES;
523}
524
525-(BOOL) terminalRunCommand: (NSString *)cmdline
526	inDirectory: (NSString *)directory
527	properties: (NSDictionary *)properties
528{
529	NSDebugLLog(@"Application",
530		@"terminalRunCommand: %@ inDirectory: %@ properties: %@",
531		cmdline,directory,properties);
532
533	return [self terminalRunProgram: @"/bin/sh"
534		withArguments: [NSArray arrayWithObjects: @"-c",cmdline,nil]
535		inDirectory: directory
536		properties: properties];
537}
538
539
540@end
541
542
543/* TODO */
544
545#include <AppKit/NSWindow.h>
546#include <AppKit/NSEvent.h>
547
548@interface NSWindow (avoid_warnings)
549-(void) sendEvent: (NSEvent *)e;
550@end
551
552
553@interface TerminalApplication : NSApplication
554@end
555
556@implementation TerminalApplication
557-(void) sendEvent: (NSEvent *)e
558{
559	if ([e type]==NSKeyDown && [e modifierFlags]&NSCommandKeyMask &&
560	    [TerminalViewKeyboardPrefs commandAsMeta])
561	{
562		NSDebugLLog(@"key",@"intercepting key equivalent");
563		[[e window] sendEvent: e];
564		return;
565	}
566	[super sendEvent: e];
567}
568@end
569
570
571int main(int argc, char **argv)
572{
573	Terminal *term;
574
575	CREATE_AUTORELEASE_POOL(arp);
576
577/*	[NSObject enableDoubleReleaseCheck: YES];*/
578
579	[TerminalApplication sharedApplication];
580
581	term = [[Terminal alloc] init];
582	[NSApp setDelegate: term];
583	[NSApp run];
584
585	[term release];
586
587	DESTROY(arp);
588	return 0;
589}
590
591