File: GSCharacterPanel.m

package info (click to toggle)
gnustep-gui 0.25.0-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 13,088 kB
  • ctags: 3,417
  • sloc: objc: 153,800; ansic: 18,239; cpp: 579; yacc: 462; makefile: 143; sh: 5
file content (378 lines) | stat: -rw-r--r-- 11,013 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/** <title>GSCharacterPanel</title>

   <abstract>Character Panel.</abstract>

   Copyright (C) 2011 Free Software Foundation, Inc.
   
   Author:  Eric Wasylishen <ewasylishen@gmail.com>
   Date: July 2011
   
   This file is part of the GNUstep Application Kit Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; see the file COPYING.LIB.
   If not, see <http://www.gnu.org/licenses/> or write to the 
   Free Software Foundation, 51 Franklin Street, Fifth Floor, 
   Boston, MA 02110-1301, USA.
*/ 

#import "config.h"

#import <Foundation/NSIndexSet.h>
#import <Foundation/NSBundle.h>
#import <Foundation/NSAutoreleasePool.h>
#import "AppKit/NSApplication.h"
#import "AppKit/NSStringDrawing.h"
#import "AppKit/NSPasteboard.h"
#import "AppKit/NSTableView.h"
#import "AppKit/NSTableColumn.h"
#import "AppKit/NSTextFieldCell.h"
#import "AppKit/NSScrollView.h"
#import "AppKit/NSSearchField.h"
#import "GNUstepGUI/GSCharacterPanel.h"
#import "GSGuiPrivate.h"

@implementation NSApplication (CharacterPanel)

- (void) orderFrontCharacterPalette: (id)sender
{
  [[GSCharacterPanel sharedCharacterPanel] orderFront: sender];
}

@end

#if defined(HAVE_UNICODE_UCHAR_H) && defined(HAVE_UNICODE_USTRING_H)
#include <unicode/uchar.h>
#include <unicode/ustring.h>

@interface GSVerticallyCenteredTextFieldCell : NSTextFieldCell
{
}
@end

@implementation GSVerticallyCenteredTextFieldCell

- (NSRect) titleRectForBounds: (NSRect)aRect
{
  NSRect titleRect = [super titleRectForBounds: aRect];
  NSSize titleSize = [[self attributedStringValue] size];
  titleRect.origin.y = aRect.origin.y + (aRect.size.height - titleSize.height) / 2.0;
  titleRect.size.height = titleSize.height;
  return titleRect;
}

@end

// Enumerating assigned codepoints

static UBool enumCharNamesFn(void *context, UChar32 code, UCharNameChoice nameChoice, const char *name, int32_t length)
{
  [(NSMutableIndexSet*)context addIndex: (NSUInteger)code];
  return TRUE;
}

static NSIndexSet *AssignedCodepoints()
{
  UErrorCode err = U_ZERO_ERROR;
  NSMutableIndexSet *set = [NSMutableIndexSet indexSet];
  u_enumCharNames(UCHAR_MIN_VALUE, UCHAR_MAX_VALUE + 1, enumCharNamesFn, set, U_UNICODE_CHAR_NAME, &err);
  return set;
}

// Searching for codepoints

struct searchContext {
  const char *searchString;
  NSMutableIndexSet *set;
};

static UBool searchCharNamesFn(void *context, UChar32 code, UCharNameChoice nameChoice, const char *name, int32_t length)
{
  struct searchContext *ctx = (struct searchContext *)context;
  if (strstr(name, ctx->searchString) != NULL)
    {
      [ctx->set addIndex: (NSUInteger)code];
    }
  return TRUE;
}

static NSIndexSet *CodepointsWithNameContainingSubstring(NSString *str)
{
  UErrorCode err = U_ZERO_ERROR;
  struct searchContext ctx;
	
  ctx.set = [NSMutableIndexSet indexSet];
  ctx.searchString = [[str uppercaseString] UTF8String];

  u_enumCharNames(UCHAR_MIN_VALUE, UCHAR_MAX_VALUE + 1, searchCharNamesFn, &ctx, U_UNICODE_CHAR_NAME, &err);
	
  return ctx.set;
}


@implementation GSCharacterPanel 

- (void)setVisibleCodepoints: (NSIndexSet*)set
{
  ASSIGN(visibleCodepoints, set);
}


+ (GSCharacterPanel *) sharedCharacterPanel
{
  static GSCharacterPanel *shared = nil;
  if (nil == shared)
    {
      shared = [[self alloc] init];
    }
  return shared;
}

- (id) init
{
  const NSRect contentRect = NSMakeRect(100, 100, 276, 420);
  self = [super initWithContentRect: contentRect
			  styleMask: NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSUtilityWindowMask
			    backing: NSBackingStoreBuffered
			      defer: YES];
  if (nil != self)
    {
      // Setup assignedCodepoints and  visibleCodepointsArray
      assignedCodepoints = [AssignedCodepoints() retain];
      [self setVisibleCodepoints: assignedCodepoints];

      [self setTitle: _(@"Character Panel")];
		
      // Set up the table view
      table = [[[NSTableView alloc] initWithFrame: NSMakeRect(0, 0, contentRect.size.width - 18, contentRect.size.height - 52)] autorelease];

      // Set up table columns
      {
	NSTableColumn *col = [[[NSTableColumn alloc] initWithIdentifier: @"char"] autorelease];
	[col setDataCell: [[[GSVerticallyCenteredTextFieldCell alloc] init] autorelease]];
	[[col dataCell] setFont:[NSFont systemFontOfSize: 24]];
	[[col dataCell] setAlignment: NSCenterTextAlignment];
	[col setMinWidth: 40];
	[col setWidth: 40];
	[table addTableColumn: col];
      }
      {
	NSTableColumn *col = [[[NSTableColumn alloc] initWithIdentifier: @"name"] autorelease];
	[col setDataCell: [[[GSVerticallyCenteredTextFieldCell alloc] init] autorelease]];
	[[col dataCell] setFont:[NSFont systemFontOfSize: 10]];
	[[col headerCell] setStringValue: _(@"Name")];
	[col setWidth: 195];
	[table addTableColumn: col];
      }
      {
	NSTableColumn *col = [[[NSTableColumn alloc] initWithIdentifier: @"code"] autorelease];
	[col setDataCell: [[[GSVerticallyCenteredTextFieldCell alloc] init] autorelease]];
	[[col dataCell] setFont:[NSFont systemFontOfSize: 10]];
	[[col dataCell] setAlignment: NSCenterTextAlignment];
	[[col headerCell] setStringValue: _(@"Code Point")];
	[col setMinWidth: 80];
	[col setWidth: 80];
	[table addTableColumn: col];
      }
      {
	NSTableColumn *col = [[[NSTableColumn alloc] initWithIdentifier: @"block"] autorelease];
	[col setDataCell: [[[GSVerticallyCenteredTextFieldCell alloc] init] autorelease]];
	[[col dataCell] setFont:[NSFont systemFontOfSize: 10]];
	[[col headerCell] setStringValue: _(@"Unicode Block")];
	[col setMinWidth: 140];
	[table addTableColumn: col];
      }      

      [table setRowHeight: 32];
      [table setDataSource: self];
      [table setDelegate: self];
      [table setTarget: self];
      [table setDoubleAction: @selector(doubleClickRow:)];

      // Allow dragging out of the application
      [table setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
      
      // Set up scroll view
      {
	NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: NSMakeRect(9, 41, contentRect.size.width - 18, contentRect.size.height - 52)];
	[scrollView setDocumentView: table];
	[scrollView setHasHorizontalScroller: YES];
	[scrollView setHasVerticalScroller: YES];
	[scrollView setBorderType: NSBezelBorder];
	[scrollView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)];
	[[self contentView] addSubview: scrollView];
	[scrollView release];
      }

      // Set up search field
      {
	searchfield = [[NSSearchField alloc] initWithFrame: NSMakeRect(9,9,186,22)];
	[searchfield setTarget: self];
	[searchfield setAction: @selector(search:)];
	[[self contentView] addSubview: searchfield];
	[searchfield release];
      }
    }

  return self;
}

- (void)dealloc
{
  [assignedCodepoints release];
  [visibleCodepoints release];
  [super dealloc];
}

- (void)search: (id)sender
{
  NSString *str = [searchfield stringValue];
	
  if ([str length] == 0)
    {
      [self setVisibleCodepoints: assignedCodepoints];
    }
  else
    {
      NSIndexSet *set = CodepointsWithNameContainingSubstring(str);
      [self setVisibleCodepoints: set];
    }
	
  [table reloadData];
}

- (NSUInteger) codepointAtVisibleRow:(NSUInteger)row
{
  //FIXME: Use a binary search
  NSUInteger curr = 0;
  NSUInteger currValue = [visibleCodepoints firstIndex];
	
  while (currValue != NSNotFound)
    {
      if (curr == row)
	{
	  return currValue;
	}
      currValue = [visibleCodepoints indexGreaterThanIndex: currValue];
      curr++;
    }
  return NSNotFound;
}

- (NSString *)characterForRow: (NSInteger)row
{
  if (row >= 0 && row < [visibleCodepoints count])
    {
      UChar32 utf32 = [self codepointAtVisibleRow: row];
      UChar utf16buf[2];
      int32_t utf16bufLength = 0;
      UErrorCode error = U_ZERO_ERROR;
      u_strFromUTF32(utf16buf, 2, &utf16bufLength, &utf32, 1, &error);
	  
      return [[[NSString alloc] initWithCharacters: utf16buf
					    length: utf16bufLength] autorelease];
    }
  return @"";
}

- (void) doubleClickRow: (id)sender
{
  NSWindow *mainWindow = [NSApp mainWindow];
  NSResponder *firstResponder = [mainWindow firstResponder];
  NSString *str = [self characterForRow: [table clickedRow]];

  [firstResponder insertText: str];
}

- (BOOL) tableView: (NSTableView *)aTable shouldEditTableColumn: (NSTableColumn *)aColumn row: (NSInteger)row
{
  return NO;
}

// NSTableViewDataSource protocol

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
  return [visibleCodepoints count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
  UChar32 utf32 = [self codepointAtVisibleRow: row];
  
  if ([[tableColumn identifier] isEqualToString: @"char"])
    {
      return [self characterForRow: row];
    }
  else if ([[tableColumn identifier] isEqualToString: @"name"])
    {
      UErrorCode error = U_ZERO_ERROR;
      int32_t size = u_charName(utf32, U_UNICODE_CHAR_NAME, NULL, 0, &error);

      if (size > 0)
	{
	  char name[512];
	  error = U_ZERO_ERROR;
	  u_charName(utf32, U_UNICODE_CHAR_NAME, name, 512, &error);

	  NSString *nameObj = [[[NSString alloc] initWithBytes: name
							length: size
						      encoding: NSASCIIStringEncoding] autorelease];
	  return [[nameObj lowercaseString] capitalizedString];
	}
      return @"";
    }
  else if ([[tableColumn identifier] isEqualToString: @"code"])
    {
      return [NSString stringWithFormat:@"U+%04X", (int)utf32];
    }
  else if ([[tableColumn identifier] isEqualToString: @"block"])
    {
      int32_t val = u_getIntPropertyValue(utf32, UCHAR_BLOCK);
      const char *name = u_getPropertyValueName(UCHAR_BLOCK, val, U_LONG_PROPERTY_NAME);
      if (name != NULL)
	{
	  return [[[[NSString alloc] initWithBytes: name
					    length: strlen(name)
					  encoding: NSASCIIStringEncoding] autorelease] stringByReplacingOccurrencesOfString: @"_" withString: @" "];
	}
    }  
  return nil; 
}

- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard
{
  NSString *str = [self characterForRow: [rowIndexes firstIndex]];
	
	[pboard declareTypes: [NSArray arrayWithObject: NSStringPboardType]
			owner: nil];
	[pboard setString: str
		forType: NSStringPboardType];
	
	return YES;
}

@end

#else // !(defined(HAVE_UNICODE_UCHAR_H) && defined(HAVE_UNICODE_USTRING_H))

@implementation GSCharacterPanel

+ (GSCharacterPanel *) sharedCharacterPanel
{
  return nil;
}

@end

#endif