1// ServerDelegate.mm: OVDisplayServer.app delegated object
2//
3// Copyright (c) 2004-2006 The OpenVanilla Project (http://openvanilla.org)
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions
8// are met:
9//
10// 1. Redistributions of source code must retain the above copyright
11//    notice, this list of conditions and the following disclaimer.
12// 2. Redistributions in binary form must reproduce the above copyright
13//    notice, this list of conditions and the following disclaimer in the
14//    documentation and/or other materials provided with the distribution.
15// 3. Neither the name of OpenVanilla nor the names of its contributors
16//    may be used to endorse or promote products derived from this software
17//    without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29// POSSIBILITY OF SUCH DAMAGE.
30
31#import "ServerDelegate.h"
32#import "NSDictionaryExtension.h"
33#import "NSStringExtension.h"
34#import <Carbon/Carbon.h>
35
36#define CVIB_FADEWAIT       0.5     // fade after 0.5 sec
37#define CVIB_FADEINTERVAL   0.05    // 0.05 sec/frame
38#define CVIB_FADEVALUE      0.025   // alphaValue-=0.025/frame
39
40enum {
41    OVDPS_NOTIFY_DEFAULT=0,
42    OVDPS_NOTIFY_SILENT=1
43};
44
45// nop function to prevent Objective-C remote object failure
46// in some architectures--DO NOT REMOVE THIS WITHOUT EXTENSIVE TESTING!
47
48void nop() {
49    volatile a=10;
50}
51
52
53void CVFixWindowOrigin(NSWindow *w, Point p);
54
55@implementation ServerDelegate
56- (void)setConfig:(NSDictionary*)cfg {
57	[self applyConfig:cfg window:candi textField:canditext];
58	[self applyConfig:cfg window:noti textField:notitext];
59	notialpha=[noti alphaValue];
60
61    notistyle=OVDPS_NOTIFY_DEFAULT;
62    NSString *ns=[cfg valueForKey:@"notificationStyle" default:@"default"];
63    if ([ns isEqualToString:@"silent"]) notistyle=OVDPS_NOTIFY_SILENT;
64}
65- (void)candidateShow {
66	candion=YES;
67	[self solveConflict];
68	[candi orderFront:self];
69    // nop();
70}
71- (void)candidateHide {
72	candion=NO;
73	[candi orderOut:self];
74    // nop();
75}
76- (void)candidateUpdate:(bycopy NSString*)s position:(Point)p {
77	[self updateTextInWindow:candi textField:canditext text:s position:p];
78}
79- (void)notifyMessage:(bycopy NSString*)s position:(Point)p {
80	[self updateTextInWindow:noti textField:notitext text:s position:p];
81	[self solveConflict];
82
83    if (notistyle!=OVDPS_NOTIFY_DEFAULT) return;
84    [noti orderFront:self];
85    // nop();
86}
87- (void)notifyClose {
88	[noti orderOut:self];
89    // nop();
90}
91- (void)notifyFade {
92    if (fadetimer) [self stopTimer];
93
94    if (notistyle!=OVDPS_NOTIFY_DEFAULT) return;
95    fadetimer=[NSTimer scheduledTimerWithTimeInterval:CVIB_FADEWAIT target:self selector:@selector(fadeStart) userInfo:nil repeats:NO];
96}
97- (void)aboutDialog {
98    [aboutdialog setLevel:NSScreenSaverWindowLevel];
99    [aboutdialog orderFront:self];
100    // nop();
101}
102- (void)dealloc {
103    [defaultbackground release];
104	[candi release];
105	[super dealloc];
106}
107- (void)awakeFromNib {
108	// NSLog(@"OVDisplayServer started");
109	NSConnection *c=[NSConnection defaultConnection];
110	[c setRootObject:self];
111    if ([c registerName:OVDSPSRVR_NAME]) ; // NSLog(@"OVDisplayServer registered");
112	else NSLog(@"OVDisplayServer registration failed");
113
114	NSRect fr=NSMakeRect(0, 0, 100, 20);
115	candion=NO;
116	candi=[self createWindow:fr];
117    defaultbackground=[[candi backgroundColor] retain];
118    canditext=[self createTextField:fr];
119    [[candi contentView] addSubview:canditext];
120	noti=[self createWindow:fr];
121	notitext=[self createTextField:fr];
122    [[noti contentView] addSubview:notitext];
123
124	fadetimer=nil;
125}
126- (void)stopTimer {
127    if (fadetimer) [fadetimer invalidate];
128    fadetimer=nil;
129	[noti setAlphaValue:notialpha];
130}
131- (void)fadeStart {
132    [fadetimer invalidate];
133    fadetimer=[NSTimer scheduledTimerWithTimeInterval:CVIB_FADEINTERVAL target:self selector:@selector(fadeWork) userInfo:nil repeats:YES];
134    fadealpha=notialpha;
135}
136- (void)fadeWork {
137    fadealpha-=CVIB_FADEVALUE;
138    if (fadealpha<=0) {
139        [noti orderOut:self];
140        [self stopTimer];
141        // nop();
142        return;
143    }
144	[noti setAlphaValue:fadealpha];
145
146}
147- (void)solveConflict {
148	if (!candion) return;
149	NSRect cfr, nfr;
150	cfr=[candi frame];
151	nfr=[noti frame];
152	if (cfr.origin.y+cfr.size.height == nfr.origin.y+nfr.size.height) {
153		nfr.origin.y = cfr.origin.y - (nfr.size.height + 5);
154		[noti setFrameOrigin:nfr.origin];
155	}
156}
157- (void)updateTextInWindow:(NSWindow*)w textField:(NSTextField*)t text:(NSString*)s position:(Point)p {
158	[t setStringValue:s];
159    [t sizeToFit];
160    NSRect r=[t frame];
161    [w setContentSize:r.size];
162
163	CVFixWindowOrigin(w, p);
164}
165- (NSWindow*)createWindow:(NSRect)fr {
166	NSWindow *w=[[NSWindow alloc] initWithContentRect:fr styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
167    [w setLevel:NSScreenSaverWindowLevel];
168	[w setMovableByWindowBackground:YES];
169	[w setContentView:[[NSView alloc] initWithFrame:[w contentRectForFrameRect:fr]]];
170	return w;
171}
172- (NSTextField*)createTextField:(NSRect)fr {
173	NSTextField *t=[[NSTextField alloc] initWithFrame:fr];
174    [t setEditable:NO];
175    [t setSelectable:NO];
176    [t setBordered:NO];
177    [t setDrawsBackground:NO];
178    [t setFrameOrigin:NSMakePoint(0,0)];
179	return t;
180}
181- (void)applyConfig:(NSDictionary*)d window:(NSWindow*)w textField:(NSTextField*)t {
182	float alpha=[[d valueForKey:@"opacity" default:@"1.0"] floatValue];
183	NSColor *fc=[[d valueForKey:@"foreground" default:@"1.0 1.0 1.0"] colorByString];
184	NSString *img=[d valueForKey:@"backgroundImage" default:@""];
185	NSString *font=[d valueForKey:@"font" default:@"Lucida Grande"];
186	float s=[[d valueForKey:@"size" default:@"18"] floatValue];
187
188	[w setAlphaValue:alpha];
189    [w setHasShadow:YES];
190    [w setOpaque:YES];
191    if ([img length]) {
192		NSImage *i=[[NSImage alloc] initByReferencingFile:img];
193		if (i && [i isValid]) {
194			[i autorelease];
195			NSColor *c=[NSColor colorWithPatternImage:i];
196			[w setBackgroundColor:c];
197		}
198	}
199    else {
200        // no image, use color
201        NSString *bcstr=[d valueForKey:@"background" default:@"1.0 1.0 1.0"];
202        if ([bcstr isEqualToString:@"transparent"]) {
203            [w setHasShadow:NO];
204            [w setOpaque:NO];
205            [w setBackgroundColor:[NSColor clearColor]];
206        }
207        else if ([bcstr isEqualToString:@"none"]) {
208            [w setBackgroundColor:defaultbackground];
209        }
210        else {
211            NSColor *bc=[bcstr colorByString];
212            [w setBackgroundColor:bc];
213        }
214    }
215
216    [t setFont:[NSFont fontWithName:font size:s]];
217	[t setTextColor:fc];
218}
219@end
220
221void CVFixWindowOrigin(NSWindow *w, Point p) {
222	// first of all, we do a y-coordiate transformation (mirroring)
223	NSPoint np=NSMakePoint((float)p.h, (float)p.v);
224
225	float maxh=480;		// a silly default, but anyway the below loop always runs
226	NSArray *sa=[NSScreen screens];
227	int i, c=[sa count];
228	for (i=0; i<c; i++) {
229		float h=[[sa objectAtIndex:i] frame].size.height;
230		if (h > maxh) maxh=h;
231	}
232
233	np.y=maxh-np.y;
234
235	NSSize s=[w frame].size;
236
237	// now we determine the window frame
238	NSRect f=[[NSScreen mainScreen] frame];
239	BOOL found=NO;
240
241	for (i=0; i<c; i++) {
242		NSRect sf=[[sa objectAtIndex:i] frame];
243		if (NSPointInRect(np, sf)) {
244			f=sf;
245			found=YES;
246		}
247	}
248
249	// if the origin is out of bound, we place the origin in the center of the main screen
250	if (found) {
251		np.y-=s.height;		// must offset the height of the window
252		if (np.y > f.origin.y+f.size.height-s.height) np.y=f.origin.y+f.size.height-s.height;
253		if (np.x > f.origin.x+f.size.width-s.width) np.x=f.origin.x+f.size.width-s.width;
254		if (np.y < f.origin.y) np.y=f.origin.y;
255		if (np.x < f.origin.x) np.x=f.origin.x;
256	}
257	else {
258		np.x = f.origin.x + (f.size.width-s.width)/2;
259		np.y = f.origin.y + (f.size.height-s.height)/2;
260	}
261
262	[w setFrameOrigin:np];
263}
264