1/* NSTextBlock.m
2
3   Copyright (C) 2008 Free Software Foundation, Inc.
4
5   Author:  H. Nikolaus Schaller
6   Date: 2007
7   Author:  Fred Kiefer <fredkiefer@gmx.de>
8   Date: January 2008
9
10   This file is part of the GNUstep GUI Library.
11
12   This library is free software; you can redistribute it and/or
13   modify it under the terms of the GNU Lesser General Public
14   License as published by the Free Software Foundation; either
15   version 2 of the License, or (at your option) any later version.
16
17   This library is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
20   Lesser General Public License for more details.
21
22   You should have received a copy of the GNU Lesser General Public
23   License along with this library; see the file COPYING.LIB.
24   If not, see <http://www.gnu.org/licenses/> or write to the
25   Free Software Foundation, 51 Franklin Street, Fifth Floor,
26   Boston, MA 02110-1301, USA.
27*/
28
29#import <Foundation/NSCoder.h>
30#import <Foundation/NSException.h>
31#import <Foundation/NSString.h>
32
33#import "AppKit/NSColor.h"
34#import "AppKit/NSGraphics.h"
35#import "AppKit/NSTextTable.h"
36#import "GSGuiPrivate.h"
37
38@implementation NSTextBlock
39
40- (id) init
41{
42  // FIXME
43  return self;
44}
45
46- (void) dealloc
47{
48  RELEASE(_backgroundColor);
49  RELEASE(_borderColorForEdge[NSMinXEdge]);
50  RELEASE(_borderColorForEdge[NSMinYEdge]);
51  RELEASE(_borderColorForEdge[NSMaxXEdge]);
52  RELEASE(_borderColorForEdge[NSMaxYEdge]);
53  [super dealloc];
54}
55
56- (NSColor *) backgroundColor
57{
58  return _backgroundColor;
59}
60
61- (void) setBackgroundColor: (NSColor *)color
62{
63    ASSIGN(_backgroundColor, color);
64}
65
66- (NSColor *) borderColorForEdge: (NSRectEdge)edge
67{
68  return _borderColorForEdge[edge];
69}
70
71- (void) setBorderColor: (NSColor *)color forEdge: (NSRectEdge)edge
72{
73  if (edge >= sizeof(_borderColorForEdge) / sizeof(_borderColorForEdge[0]))
74    [NSException raise: NSInvalidArgumentException
75                 format: @"invalid edge %lu", (unsigned long) edge];
76  ASSIGN(_borderColorForEdge[edge], color);
77}
78
79- (void) setBorderColor: (NSColor *)color
80{
81  ASSIGN(_borderColorForEdge[NSMinXEdge], color);
82  ASSIGN(_borderColorForEdge[NSMinYEdge], color);
83  ASSIGN(_borderColorForEdge[NSMaxXEdge], color);
84  ASSIGN(_borderColorForEdge[NSMaxYEdge], color);
85}
86
87- (CGFloat) contentWidth
88{
89  return [self valueForDimension: NSTextBlockWidth];
90}
91
92- (NSTextBlockValueType) contentWidthValueType
93{
94  return [self valueTypeForDimension: NSTextBlockWidth];
95}
96
97- (void) setContentWidth: (CGFloat)val type: (NSTextBlockValueType)type
98{
99  [self setValue: val type: type forDimension: NSTextBlockWidth];
100}
101
102- (NSTextBlockVerticalAlignment) verticalAlignment
103{
104  return _verticalAlignment;
105}
106
107- (void) setVerticalAlignment: (NSTextBlockVerticalAlignment)alignment
108{
109 _verticalAlignment = alignment;
110}
111
112- (CGFloat) valueForDimension: (NSTextBlockDimension)dimension
113{
114  if (dimension >= sizeof(_valueType) / sizeof(_valueType[0]))
115    [NSException raise: NSInvalidArgumentException
116		 format: @"invalid dimension %d", dimension];
117  return _value[dimension];
118}
119
120- (NSTextBlockValueType) valueTypeForDimension: (NSTextBlockDimension)dimension
121{
122  if (dimension >= sizeof(_valueType) / sizeof(_valueType[0]))
123    [NSException raise: NSInvalidArgumentException
124		 format: @"invalid dimension %d", dimension];
125  return _valueType[dimension];
126}
127
128- (CGFloat) _scaledValue: (NSTextBlockDimension)dimension : (NSSize)size
129{
130  if (_valueType[dimension] == NSTextBlockAbsoluteValueType)
131    {
132      return _value[dimension];
133    }
134  else
135    {
136      // specified in percent
137      switch(dimension)
138        {
139        case NSTextBlockWidth:
140        case NSTextBlockMinimumWidth:
141        case NSTextBlockMaximumWidth:
142          return _value[dimension] * size.width;
143        case NSTextBlockHeight:
144        case NSTextBlockMinimumHeight:
145        case NSTextBlockMaximumHeight:
146          return _value[dimension] * size.height;
147        }
148    }
149  return 0.0;
150}
151
152- (void) setValue: (CGFloat)val
153             type: (NSTextBlockValueType)type
154     forDimension: (NSTextBlockDimension)dimension
155{
156  if (dimension >= sizeof(_valueType) / sizeof(_valueType[0]))
157    [NSException raise: NSInvalidArgumentException
158		 format: @"invalid dimension %d", dimension];
159  _value[dimension] = val;
160  _valueType[dimension] = type;
161}
162
163- (CGFloat) widthForLayer: (NSTextBlockLayer)layer edge: (NSRectEdge)edge
164{
165  if (layer >= sizeof(_width) / sizeof(_width[0]))
166    [NSException raise: NSInvalidArgumentException
167		 format: @"invalid layer %d", layer];
168  if (edge >= sizeof(_width[0]) / sizeof(_width[0][0]))
169    [NSException raise: NSInvalidArgumentException
170		 format: @"invalid edge %lu", (unsigned long) edge];
171  return _width[layer][edge];
172}
173
174- (NSTextBlockValueType) widthValueTypeForLayer: (NSTextBlockLayer)layer
175                                           edge: (NSRectEdge)edge
176{
177  if (layer >= sizeof(_width) / sizeof(_width[0]))
178    [NSException raise: NSInvalidArgumentException
179		 format: @"invalid layer %d", layer];
180  if (edge >= sizeof(_width[0]) / sizeof(_width[0][0]))
181    [NSException raise: NSInvalidArgumentException
182		 format: @"invalid edge %lu", (unsigned long) edge];
183  return _widthType[layer][edge];
184}
185
186- (void) setWidth: (CGFloat)val
187             type: (NSTextBlockValueType)type
188         forLayer: (NSTextBlockLayer)layer
189             edge: (NSRectEdge)edge
190{
191  if (layer >= sizeof(_width) / sizeof(_width[0]))
192    [NSException raise: NSInvalidArgumentException
193		 format: @"invalid layer %d", layer];
194  if (edge >= sizeof(_width[0]) / sizeof(_width[0][0]))
195    [NSException raise: NSInvalidArgumentException
196		 format: @"invalid edge %lu", (unsigned long) edge];
197  _width[layer][edge] = val;
198  _widthType[layer][edge] = type;
199}
200
201- (void) setWidth: (CGFloat)val
202             type: (NSTextBlockValueType)type
203         forLayer: (NSTextBlockLayer)layer
204{
205  if (layer >= sizeof(_width) / sizeof(_width[0]))
206    [NSException raise: NSInvalidArgumentException
207		 format: @"invalid layer %d", layer];
208  _width[layer][NSMinXEdge] = val;
209  _widthType[layer][NSMinXEdge] = type;
210  _width[layer][NSMinYEdge] = val;
211  _widthType[layer][NSMinYEdge] = type;
212  _width[layer][NSMaxXEdge] = val;
213  _widthType[layer][NSMaxXEdge] = type;
214  _width[layer][NSMaxYEdge] = val;
215  _widthType[layer][NSMaxYEdge] = type;
216}
217
218- (CGFloat) _scaledWidthValue: (NSTextBlockLayer) layer : (NSRectEdge) edge : (NSSize) size
219{
220  if (_widthType[layer][edge] == NSTextBlockAbsoluteValueType)
221    {
222      // absolute
223      return _width[layer][edge];
224    }
225  else
226    {
227      // specified in percent
228      switch(edge)
229        {
230        case NSMinXEdge:
231        case NSMaxXEdge:
232          return _widthType[layer][edge]*size.width;
233        case NSMinYEdge:
234        case NSMaxYEdge:
235          return _widthType[layer][edge]*size.height;
236        }
237    }
238  return 0.0;
239}
240
241- (NSRect) boundsRectForContentRect: (NSRect)cont
242                             inRect: (NSRect)rect
243                      textContainer: (NSTextContainer *)container
244                     characterRange: (NSRange)range
245{
246  CGFloat minx = [self _scaledWidthValue: NSTextBlockPadding : NSMinXEdge: rect.size]
247    + [self _scaledWidthValue: NSTextBlockBorder : NSMinXEdge : rect.size]
248    + [self _scaledWidthValue: NSTextBlockMargin : NSMinXEdge : rect.size];
249
250  CGFloat maxx = [self _scaledWidthValue: NSTextBlockPadding: NSMaxXEdge: rect.size]
251    + [self _scaledWidthValue: NSTextBlockBorder : NSMaxXEdge : rect.size]
252    + [self _scaledWidthValue: NSTextBlockMargin : NSMaxXEdge : rect.size];
253  CGFloat miny= [self _scaledWidthValue: NSTextBlockPadding : NSMinYEdge: rect.size]
254    + [self _scaledWidthValue: NSTextBlockBorder : NSMinYEdge : rect.size]
255    + [self _scaledWidthValue: NSTextBlockMargin : NSMinYEdge : rect.size];
256
257  CGFloat maxy = [self _scaledWidthValue: NSTextBlockPadding: NSMaxYEdge: rect.size]
258    + [self _scaledWidthValue: NSTextBlockBorder : NSMaxYEdge : rect.size]
259    + [self _scaledWidthValue: NSTextBlockMargin : NSMaxYEdge : rect.size];
260
261  cont.origin.x -= minx;
262  cont.size.width += minx + maxx;
263  cont.origin.y -= miny;
264  cont.size.height += miny + maxy;
265  return cont;
266}
267
268/**
269 * POINT is the point in NSTextContainer where the TextBlock should be laid out.
270 * RECT is the bounding rect (e.g. the rect of the container or the rect of the
271 * outer table cell)
272 * what are we doing with CONT? Do we limit to width of container?
273 * what are we doing with RANGE? We don't know the layout manager
274 * This is the default implementation for a single-cell
275 * (NSTextTableBlock can handle cell span)
276 * raises internal inconsisteny exception if the layout manager
277 * (the one owning the textContainer)
278 * does not have a table at the given characterRange
279 */
280- (NSRect) rectForLayoutAtPoint: (NSPoint)point
281                         inRect: (NSRect)rect
282                  textContainer: (NSTextContainer *)cont
283                 characterRange: (NSRange)range
284{
285  NSRect r;
286  NSSize size = (NSSize){[self _scaledValue: NSTextBlockWidth : rect.size],
287			 [self _scaledValue: NSTextBlockHeight : rect.size]};
288  // when and how do we define size by content? If size is (0, 0)?
289  // or is this the input to calculating size of enclosed text?
290  size.width = MAX(size.width, [self _scaledValue: NSTextBlockMinimumWidth : rect.size]);
291  // not smaller than minimum
292  size.height = MAX(size.height, [self _scaledValue: NSTextBlockMinimumHeight : rect.size]);
293  size.width = MIN(size.width, [self _scaledValue: NSTextBlockMaximumWidth : rect.size]);
294  // but also not larger than maximum
295  size.height = MIN(size.height, [self _scaledValue: NSTextBlockMaximumHeight : rect.size]);
296  r = (NSRect){point, size};
297  // who handles vertical alignment?
298  // limit to what is available
299  return NSIntersectionRect(r, rect);
300}
301
302- (void) drawBackgroundWithFrame: (NSRect)rect	// this is the frame of the cell
303                          inView: (NSView *)view
304                  characterRange: (NSRange)range
305                   layoutManager: (NSLayoutManager *)lm
306{
307  CGFloat minx = [self _scaledWidthValue: NSTextBlockPadding : NSMinXEdge : rect.size];
308  CGFloat maxx = [self _scaledWidthValue: NSTextBlockPadding : NSMaxXEdge : rect.size];
309  CGFloat miny = [self _scaledWidthValue: NSTextBlockPadding : NSMinYEdge : rect.size];
310  CGFloat maxy = [self _scaledWidthValue: NSTextBlockPadding : NSMaxYEdge : rect.size];
311
312  // FIXME - inset from frame by margin in the first step
313  rect.origin.x -= minx;
314  rect.size.width += minx + maxx;
315  rect.origin.y -= miny;
316  rect.size.height += miny + maxy;
317  [_backgroundColor set];
318  // fill inner rect
319  NSRectFill(rect);
320
321  minx = [self _scaledWidthValue: NSTextBlockBorder : NSMinXEdge : rect.size];
322  maxx = [self _scaledWidthValue: NSTextBlockBorder : NSMaxXEdge : rect.size];
323  miny = [self _scaledWidthValue: NSTextBlockBorder : NSMinYEdge : rect.size];
324  maxy = [self _scaledWidthValue: NSTextBlockBorder : NSMaxYEdge : rect.size];
325  [_borderColorForEdge[NSMinXEdge] set];
326  NSRectFill(NSMakeRect(rect.origin.x - minx, rect.origin.y, minx, rect.size.height));
327  [_borderColorForEdge[NSMaxYEdge] set];
328  NSRectFill(NSMakeRect(rect.origin.x, rect.origin.y + rect.size.height + maxy, rect.size.width, maxy));
329  [_borderColorForEdge[NSMaxXEdge] set];
330  NSRectFill(NSMakeRect(rect.origin.x + rect.size.width, rect.origin.y, maxx, rect.size.height));
331  [_borderColorForEdge[NSMinYEdge] set];
332  NSRectFill(NSMakeRect(rect.origin.x, rect.origin.y - maxy, rect.size.width, miny));
333  // FIXME: how do we handle the corners of differenly sized and colored borders?
334  // Do we have to fill trapezoids?
335}
336
337- (id) copyWithZone: (NSZone*)zone
338{
339  NSTextBlock *t = (NSTextBlock*)NSCopyObject(self, 0, zone);
340
341  _backgroundColor = TEST_RETAIN(_backgroundColor);
342  _borderColorForEdge[NSMinXEdge] =
343      TEST_RETAIN(_borderColorForEdge[NSMinXEdge]);
344  _borderColorForEdge[NSMinYEdge] =
345      TEST_RETAIN(_borderColorForEdge[NSMinYEdge]);
346  _borderColorForEdge[NSMaxXEdge] =
347      TEST_RETAIN(_borderColorForEdge[NSMaxXEdge]);
348  _borderColorForEdge[NSMaxYEdge] =
349      TEST_RETAIN(_borderColorForEdge[NSMaxYEdge]);
350
351  return t;
352}
353
354- (void) encodeWithCoder: (NSCoder*)aCoder
355{
356  // FIXME
357  if ([aCoder allowsKeyedCoding])
358    {
359    }
360  else
361    {
362    }
363}
364
365- (id) initWithCoder: (NSCoder*)aDecoder
366{
367  // FIXME
368  if ([aDecoder allowsKeyedCoding])
369    {
370    }
371  else
372    {
373    }
374  return self;
375}
376
377@end
378