1/** <title>NSFormCell</title>
2
3   <abstract>The cell class for the NSForm control</abstract>
4
5   Copyright (C) 1996, 1999 Free Software Foundation, Inc.
6
7   Author: Ovidiu Predescu <ovidiu@net-community.com>
8   Date: March 1997
9   Author: Nicola Pero <n.pero@mi.flashnet.it>
10   Date: November 1999
11
12   This file is part of the GNUstep GUI Library.
13
14   This library is free software; you can redistribute it and/or
15   modify it under the terms of the GNU Lesser General Public
16   License as published by the Free Software Foundation; either
17   version 2 of the License, or (at your option) any later version.
18
19   This library is distributed in the hope that it will be useful,
20   but WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
22   Lesser General Public License for more details.
23
24   You should have received a copy of the GNU Lesser General Public
25   License along with this library; see the file COPYING.LIB.
26   If not, see <http://www.gnu.org/licenses/> or write to the
27   Free Software Foundation, 51 Franklin Street, Fifth Floor,
28   Boston, MA 02110-1301, USA.
29*/
30
31#include "config.h"
32#import <Foundation/NSNotification.h>
33#import "AppKit/NSAttributedString.h"
34#import "AppKit/NSColor.h"
35#import "AppKit/NSFormCell.h"
36#import "AppKit/NSFont.h"
37#import "AppKit/NSGraphics.h"
38#import "AppKit/NSTextFieldCell.h"
39#import "GNUstepGUI/GSTheme.h"
40
41/** <p>TODO Description </p>
42 */
43@implementation NSFormCell
44+ (void) initialize
45{
46  if (self == [NSFormCell class])
47    {
48      [self setVersion: 1];
49    }
50}
51
52/* The title attributes are those inherited from the NSActionCell class. */
53- (id) init
54{
55  return [self initTextCell: @"Field:"];
56}
57
58/** <p>Initializes and returns new NSFormCell with aString as its title and
59    the text cell with an empty NSString.</p>
60    <p>See Also: [NSCell-initTextCell:]</p>
61 */
62- (id) initTextCell: (NSString *)aString
63{
64  self = [super initTextCell: @""];
65  if (nil == self)
66    return nil;
67
68  _cell.is_bezeled = YES;
69  _cell.is_editable = YES;
70  [self setAlignment: NSLeftTextAlignment];
71  _titleCell = [[NSCell alloc] initTextCell: aString];
72  [_titleCell setAlignment: NSRightTextAlignment];
73  _formcell_auto_title_width = YES;
74  _displayedTitleWidth = -1;
75
76  return self;
77}
78
79- (void)dealloc
80{
81  RELEASE(_titleCell);
82  TEST_RELEASE(_placeholder);
83  [super dealloc];
84}
85
86/** <p>Returns whether the NSFormCell is Opaque. Returns YES if
87    the textCell and the title cell are both Opaque, NO otherwise</p>
88    <p>See Also: [NSCell-isOpaque]</p>
89 */
90- (BOOL)isOpaque
91{
92  return [_titleCell isOpaque] && [super isOpaque];
93}
94
95- (void)setAttributedTitle: (NSAttributedString *)anAttributedString
96{
97  [_titleCell setAttributedStringValue: anAttributedString];
98  if (_formcell_auto_title_width)
99    {
100      // Invalidates title width
101      _displayedTitleWidth = -1;
102      // Update the control(s)
103      [[NSNotificationCenter defaultCenter]
104        postNotificationName: _NSFormCellDidChangeTitleWidthNotification
105        object: self];
106    }
107}
108
109/** <p> Sets the NSFormCell title to aString.
110    TODO => _formcell_auto_title_width / Update the control(s)</p>
111 */
112- (void)setTitle: (NSString*)aString
113{
114  [_titleCell setStringValue: aString];
115
116  if (_formcell_auto_title_width)
117    {
118      // Invalidates title width
119      _displayedTitleWidth = -1;
120      // Update the control(s)
121      [[NSNotificationCenter defaultCenter]
122        postNotificationName: _NSFormCellDidChangeTitleWidthNotification
123        object: self];
124    }
125}
126
127- (void)setTitleWithMnemonic:(NSString *)titleWithAmpersand
128{
129  [_titleCell setTitleWithMnemonic: titleWithAmpersand];
130  if (_formcell_auto_title_width)
131    {
132      // Invalidates title width
133      _displayedTitleWidth = -1;
134      // Update the control(s)
135      [[NSNotificationCenter defaultCenter]
136        postNotificationName: _NSFormCellDidChangeTitleWidthNotification
137        object: self];
138    }
139}
140
141/** <p>Sets the text alignment of the NSFormCell's title to mode.
142     NSRightTextAlignment by default. See <ref type="type"
143     id="NSTextAlignment">NSTextAlignment</ref> for more informations.
144     </p><p>See Also: -titleAlignment [NSCell-setAlignment:]</p>
145 */
146- (void)setTitleAlignment: (NSTextAlignment)mode
147{
148  [_titleCell setAlignment: mode];
149}
150
151/** <p>Set the text font of the NSFormCell's title to fontObject.</p>
152    <p>See Also: -titleFont [NSCell-setFont:]</p>
153 */
154- (void)setTitleFont: (NSFont*)fontObject
155{
156  [_titleCell setFont: fontObject];
157  if (_formcell_auto_title_width)
158    {
159      // Invalidates title width
160      _displayedTitleWidth = -1;
161      // Update the control(s)
162      [[NSNotificationCenter defaultCenter]
163        postNotificationName: _NSFormCellDidChangeTitleWidthNotification
164        object: self];
165    }
166}
167
168/**<p>Sets the width of the NSFormCell's title to width. All NSFormCell
169   of the NSForm are updated</p><p>See Also: -titleWidth</p>
170*/
171- (void)setTitleWidth: (CGFloat)width
172{
173  if (width >= 0)
174    {
175      _formcell_auto_title_width = NO;
176      _displayedTitleWidth = width;
177    }
178  else
179    {
180      _formcell_auto_title_width = YES;
181      _displayedTitleWidth = -1;
182    }
183  // TODO: Don't updated the control if nothing changed.
184
185  // Update the control(s)
186  [[NSNotificationCenter defaultCenter]
187    postNotificationName: _NSFormCellDidChangeTitleWidthNotification
188    object: self];
189}
190
191- (NSAttributedString *)attributedTitle
192{
193  return [_titleCell attributedStringValue];
194}
195
196/** <p>Returns the NSFormCell's title.</p>
197    <p>See Also: -setTitle: [NSCell-stringValue]</p>
198 */
199- (NSString*)title
200{
201  return [_titleCell stringValue];
202}
203
204/** <p>Returns the text alignment of the NSFormCell's title.
205    NSRightTextAlignment by default. See NSTextAlignment for more informations
206    </p><p>See Also: -setTitleAlignment:</p>
207 */
208- (NSTextAlignment)titleAlignment
209{
210  return [_titleCell alignment];
211}
212
213/** <p>Returns the text font of the NSFormCell's title</p>
214    <p>See Also: -setTitleFont: [NSCell-font]</p>
215 */
216- (NSFont*)titleFont
217{
218  return [_titleCell font];
219}
220
221/** <p>Returns the writing direction of the NSFormCell's title</p>
222    <p>See Also: -setTitleBaseWritingDirection: [NSCell-baseWritingDirection]</p>
223 */
224- (NSWritingDirection)titleBaseWritingDirection
225{
226  return [_titleCell baseWritingDirection];
227}
228
229/** <p>Sets the writing direction of the NSFormCell's title</p>
230    <p>See Also: -titleBaseWritingDirection: [NSCell-setBaseWritingDirection]</p>
231 */
232- (void)setTitleBaseWritingDirection: (NSWritingDirection)writingDirection
233{
234  [_titleCell setBaseWritingDirection: writingDirection];
235}
236
237//
238// Warning: this method returns the width of the title; the width the
239// title would have if the cell was the only cell in the form.  This
240// is used by NSForm to align all the cells in its form.  This is to
241// say that this title width is *not* what you are going to see on the
242// screen if more than one cell is present.  Setting a titleWidth
243// manually with setTitleWidth: disables any alignment with other
244// cells.
245//
246- (CGFloat)titleWidth
247{
248  if (_formcell_auto_title_width == NO)
249    return _displayedTitleWidth;
250  else
251    {
252      NSSize titleSize = [_titleCell cellSize];
253      return titleSize.width;
254    }
255}
256
257- (CGFloat)titleWidth: (NSSize)aSize
258{
259  if (_formcell_auto_title_width == NO)
260    return _displayedTitleWidth;
261  else
262    {
263      NSSize titleSize = [_titleCell cellSize];
264
265      if (aSize.width > titleSize.width)
266        return titleSize.width;
267      else
268        return aSize.width;
269    }
270}
271
272- (NSAttributedString*)placeholderAttributedString
273{
274  if (_formcell_placeholder_is_attributed_string == YES)
275    {
276      return (NSAttributedString*)_placeholder;
277    }
278  else
279    {
280      return nil;
281    }
282}
283
284- (NSString*)placeholderString
285{
286  if (_formcell_placeholder_is_attributed_string == YES)
287    {
288      return nil;
289    }
290  else
291    {
292      return (NSString*)_placeholder;
293    }
294}
295
296- (void)setPlaceholderAttributedString: (NSAttributedString*)string
297{
298  ASSIGN(_placeholder, string);
299  _formcell_placeholder_is_attributed_string = YES;
300}
301
302- (void)setPlaceholderString: (NSString*)string
303{
304  ASSIGN(_placeholder, string);
305  _formcell_placeholder_is_attributed_string = NO;
306}
307
308// Updates the title width.  The width of aRect is the new title width
309// to display.  Invoked by NSForm to align the editable parts of the
310// cells.
311- (void) calcDrawInfo: (NSRect)aRect
312{
313  if (_formcell_auto_title_width == NO)
314    return;
315
316  _displayedTitleWidth = aRect.size.width;
317}
318
319
320- (NSSize)cellSize
321{
322  NSSize returnedSize;
323  NSSize titleSize = [_titleCell cellSize];
324  NSSize textSize;
325
326  if (_contents != nil)
327    textSize = [super cellSize];
328  else
329    {
330      ASSIGN (_contents, @"Minimum");
331      _cell.contents_is_attributed_string = NO;
332      textSize = [super cellSize];
333      DESTROY (_contents);
334    }
335
336  returnedSize.width = titleSize.width + 3 + textSize.width;
337
338  if (titleSize.height > textSize.height)
339    returnedSize.height = titleSize.height;
340  else
341    returnedSize.height = textSize.height;
342
343  return returnedSize;
344}
345
346- (NSRect) drawingRectForBounds: (NSRect)theRect
347{
348  // Safety check
349  if (_displayedTitleWidth == -1)
350    _displayedTitleWidth = [self titleWidth];
351
352  theRect.origin.x   += _displayedTitleWidth + 3;
353  theRect.size.width -= _displayedTitleWidth + 3;
354
355  return [super drawingRectForBounds: theRect];
356}
357
358- (void) resetCursorRect: (NSRect)cellFrame inView: (NSView *)controlView
359{
360  NSRect rect = NSMakeRect(cellFrame.origin.x + 3 + [self titleWidth],
361			   NSMinY(cellFrame),
362			   NSWidth(cellFrame) - 3 - [self titleWidth],
363			   NSHeight(cellFrame));
364
365  [super resetCursorRect: rect
366		  inView: controlView];
367}
368
369- (void) _drawBorderAndBackgroundWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
370{
371  NSRect borderedFrame = cellFrame;
372
373  //
374  // Draw border
375  //
376  borderedFrame.origin.x   += _displayedTitleWidth + 3;
377  borderedFrame.size.width -= _displayedTitleWidth + 3;
378
379  [super _drawBorderAndBackgroundWithFrame: borderedFrame inView: controlView];
380  // Draw text background
381  [[NSColor textBackgroundColor] set];
382  NSRectFill([self drawingRectForBounds: cellFrame]);
383}
384
385- (void) drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
386{
387  NSRect titleFrame = cellFrame;
388
389  // Safety check
390  if (_displayedTitleWidth == -1)
391    _displayedTitleWidth = [self titleWidth];
392
393  // Draw title
394  titleFrame.size.width = _displayedTitleWidth;
395  [_titleCell drawWithFrame: titleFrame inView: controlView];
396
397  // Draw text
398  [super drawWithFrame: cellFrame inView: controlView];
399}
400
401/*
402   Attributed string that will be displayed.
403 */
404- (NSAttributedString*)_drawAttributedString
405{
406  NSAttributedString *attrStr;
407
408  attrStr = [super _drawAttributedString];
409  if (attrStr == nil)
410    {
411      attrStr = [self placeholderAttributedString];
412      if (attrStr == nil)
413        {
414          NSString *string;
415          NSDictionary *attributes;
416          NSMutableDictionary *newAttribs;
417
418          string = [self placeholderString];
419          if (string == nil)
420            {
421              return nil;
422            }
423
424          attributes = [self _nonAutoreleasedTypingAttributes];
425          newAttribs = [NSMutableDictionary
426                           dictionaryWithDictionary: attributes];
427          [newAttribs setObject: [NSColor disabledControlTextColor]
428                      forKey: NSForegroundColorAttributeName];
429
430          return AUTORELEASE([[NSAttributedString alloc]
431                                 initWithString: string
432                                 attributes: newAttribs]);
433        }
434      else
435        {
436          return attrStr;
437        }
438    }
439  else
440    {
441      return attrStr;
442    }
443}
444
445/*
446 * Copying
447 */
448- (id) copyWithZone: (NSZone*)zone
449{
450  NSFormCell *c = (NSFormCell *)[super copyWithZone:zone];
451
452  /* We need to copy the title cell (as opposed to simply copying the
453     pointer to it), otherwise if eg we change the string value of the
454     title cell of the copied cell, the string value of the title cell
455     of the original cell would be changed too ! */
456  c->_titleCell = [_titleCell copyWithZone: zone];
457  c->_placeholder = [_placeholder copyWithZone: zone];
458
459  return c;
460}
461
462
463- (void) encodeWithCoder: (NSCoder*)aCoder
464{
465  [super encodeWithCoder: aCoder];
466  if ([aCoder allowsKeyedCoding])
467    {
468      /*
469      if ([self stringValue] != nil)
470        {
471          [aCoder encodeObject: [self stringValue] forKey: @"NSContents"];
472        }
473      */
474      [aCoder encodeFloat: [self titleWidth] forKey: @"NSTitleWidth"];
475      [aCoder encodeObject: _titleCell forKey: @"NSTitleCell"];
476    }
477  else
478    {
479      BOOL tmp = _formcell_auto_title_width;
480      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &tmp];
481      [aCoder encodeValueOfObjCType: @encode(float) at: &_displayedTitleWidth];
482      [aCoder encodeObject: _titleCell];
483    }
484}
485
486- (id) initWithCoder: (NSCoder*)aDecoder
487{
488  self = [super initWithCoder: aDecoder];
489  if (nil == self)
490    return nil;
491
492  if ([aDecoder allowsKeyedCoding])
493    {
494      if ([aDecoder containsValueForKey: @"NSContents"])
495        {
496          [self setStringValue: [aDecoder decodeObjectForKey: @"NSContents"]];
497        }
498      if ([aDecoder containsValueForKey: @"NSTitleWidth"])
499        {
500          [self setTitleWidth: [aDecoder decodeFloatForKey: @"NSTitleWidth"]];
501        }
502      if ([aDecoder containsValueForKey: @"NSTitleCell"])
503        {
504          ASSIGN(_titleCell, [aDecoder decodeObjectForKey: @"NSTitleCell"]);
505        }
506    }
507  else
508    {
509      BOOL tmp;
510      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &tmp];
511      _formcell_auto_title_width = tmp;
512      [aDecoder decodeValueOfObjCType: @encode(float) at: &_displayedTitleWidth];
513      [aDecoder decodeValueOfObjCType: @encode(id) at: &_titleCell];
514    }
515  return self;
516}
517
518@end
519
520