1/** <title>GSCharacterPanel</title> 2 3 <abstract>Character Panel.</abstract> 4 5 Copyright (C) 2011 Free Software Foundation, Inc. 6 7 Author: Eric Wasylishen <ewasylishen@gmail.com> 8 Date: July 2011 9 10 This file is part of the GNUstep Application Kit 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 "config.h" 30 31#import <Foundation/NSIndexSet.h> 32#import <Foundation/NSBundle.h> 33#import <Foundation/NSAutoreleasePool.h> 34#import "AppKit/NSApplication.h" 35#import "AppKit/NSStringDrawing.h" 36#import "AppKit/NSPasteboard.h" 37#import "AppKit/NSTableView.h" 38#import "AppKit/NSTableColumn.h" 39#import "AppKit/NSTextFieldCell.h" 40#import "AppKit/NSScrollView.h" 41#import "AppKit/NSSearchField.h" 42#import "GNUstepGUI/GSCharacterPanel.h" 43#import "GSGuiPrivate.h" 44 45@implementation NSApplication (CharacterPanel) 46 47- (void) orderFrontCharacterPalette: (id)sender 48{ 49 [[GSCharacterPanel sharedCharacterPanel] orderFront: sender]; 50} 51 52@end 53 54#if defined(HAVE_UNICODE_UCHAR_H) && defined(HAVE_UNICODE_USTRING_H) 55#include <unicode/uchar.h> 56#include <unicode/ustring.h> 57 58@interface GSVerticallyCenteredTextFieldCell : NSTextFieldCell 59{ 60} 61@end 62 63@implementation GSVerticallyCenteredTextFieldCell 64 65- (NSRect) titleRectForBounds: (NSRect)aRect 66{ 67 NSRect titleRect = [super titleRectForBounds: aRect]; 68 NSSize titleSize = [[self attributedStringValue] size]; 69 titleRect.origin.y = aRect.origin.y + (aRect.size.height - titleSize.height) / 2.0; 70 titleRect.size.height = titleSize.height; 71 return titleRect; 72} 73 74@end 75 76// Enumerating assigned codepoints 77 78static UBool enumCharNamesFn(void *context, UChar32 code, UCharNameChoice nameChoice, const char *name, int32_t length) 79{ 80 [(NSMutableIndexSet*)context addIndex: (NSUInteger)code]; 81 return true; 82} 83 84static NSIndexSet *AssignedCodepoints() 85{ 86 UErrorCode err = U_ZERO_ERROR; 87 NSMutableIndexSet *set = [NSMutableIndexSet indexSet]; 88 u_enumCharNames(UCHAR_MIN_VALUE, UCHAR_MAX_VALUE + 1, enumCharNamesFn, set, U_UNICODE_CHAR_NAME, &err); 89 return set; 90} 91 92// Searching for codepoints 93 94struct searchContext { 95 const char *searchString; 96 NSMutableIndexSet *set; 97}; 98 99static UBool searchCharNamesFn(void *context, UChar32 code, UCharNameChoice nameChoice, const char *name, int32_t length) 100{ 101 struct searchContext *ctx = (struct searchContext *)context; 102 if (strstr(name, ctx->searchString) != NULL) 103 { 104 [ctx->set addIndex: (NSUInteger)code]; 105 } 106 return true; 107} 108 109static NSIndexSet *CodepointsWithNameContainingSubstring(NSString *str) 110{ 111 UErrorCode err = U_ZERO_ERROR; 112 struct searchContext ctx; 113 114 ctx.set = [NSMutableIndexSet indexSet]; 115 ctx.searchString = [[str uppercaseString] UTF8String]; 116 117 u_enumCharNames(UCHAR_MIN_VALUE, UCHAR_MAX_VALUE + 1, searchCharNamesFn, &ctx, U_UNICODE_CHAR_NAME, &err); 118 119 return ctx.set; 120} 121 122 123@implementation GSCharacterPanel 124 125- (void)setVisibleCodepoints: (NSIndexSet*)set 126{ 127 ASSIGN(visibleCodepoints, set); 128} 129 130 131+ (GSCharacterPanel *) sharedCharacterPanel 132{ 133 static GSCharacterPanel *shared = nil; 134 if (nil == shared) 135 { 136 shared = [[self alloc] init]; 137 } 138 return shared; 139} 140 141- (id) init 142{ 143 const NSRect contentRect = NSMakeRect(100, 100, 276, 420); 144 self = [super initWithContentRect: contentRect 145 styleMask: NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSUtilityWindowMask 146 backing: NSBackingStoreBuffered 147 defer: YES]; 148 if (nil != self) 149 { 150 // Setup assignedCodepoints and visibleCodepointsArray 151 assignedCodepoints = [AssignedCodepoints() retain]; 152 [self setVisibleCodepoints: assignedCodepoints]; 153 154 [self setTitle: _(@"Character Panel")]; 155 156 // Set up the table view 157 table = [[[NSTableView alloc] initWithFrame: NSMakeRect(0, 0, contentRect.size.width - 18, contentRect.size.height - 52)] autorelease]; 158 159 // Set up table columns 160 { 161 NSTableColumn *col = [[[NSTableColumn alloc] initWithIdentifier: @"char"] autorelease]; 162 [col setDataCell: [[[GSVerticallyCenteredTextFieldCell alloc] init] autorelease]]; 163 [[col dataCell] setFont:[NSFont systemFontOfSize: 24]]; 164 [[col dataCell] setAlignment: NSCenterTextAlignment]; 165 [col setMinWidth: 40]; 166 [col setWidth: 40]; 167 [table addTableColumn: col]; 168 } 169 { 170 NSTableColumn *col = [[[NSTableColumn alloc] initWithIdentifier: @"name"] autorelease]; 171 [col setDataCell: [[[GSVerticallyCenteredTextFieldCell alloc] init] autorelease]]; 172 [[col dataCell] setFont:[NSFont systemFontOfSize: 10]]; 173 [[col headerCell] setStringValue: _(@"Name")]; 174 [col setWidth: 195]; 175 [table addTableColumn: col]; 176 } 177 { 178 NSTableColumn *col = [[[NSTableColumn alloc] initWithIdentifier: @"code"] autorelease]; 179 [col setDataCell: [[[GSVerticallyCenteredTextFieldCell alloc] init] autorelease]]; 180 [[col dataCell] setFont:[NSFont systemFontOfSize: 10]]; 181 [[col dataCell] setAlignment: NSCenterTextAlignment]; 182 [[col headerCell] setStringValue: _(@"Code Point")]; 183 [col setMinWidth: 80]; 184 [col setWidth: 80]; 185 [table addTableColumn: col]; 186 } 187 { 188 NSTableColumn *col = [[[NSTableColumn alloc] initWithIdentifier: @"block"] autorelease]; 189 [col setDataCell: [[[GSVerticallyCenteredTextFieldCell alloc] init] autorelease]]; 190 [[col dataCell] setFont:[NSFont systemFontOfSize: 10]]; 191 [[col headerCell] setStringValue: _(@"Unicode Block")]; 192 [col setMinWidth: 140]; 193 [table addTableColumn: col]; 194 } 195 196 [table setRowHeight: 32]; 197 [table setDataSource: self]; 198 [table setDelegate: self]; 199 [table setTarget: self]; 200 [table setDoubleAction: @selector(doubleClickRow:)]; 201 202 // Allow dragging out of the application 203 [table setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO]; 204 205 // Set up scroll view 206 { 207 NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: NSMakeRect(9, 41, contentRect.size.width - 18, contentRect.size.height - 52)]; 208 [scrollView setDocumentView: table]; 209 [scrollView setHasHorizontalScroller: YES]; 210 [scrollView setHasVerticalScroller: YES]; 211 [scrollView setBorderType: NSBezelBorder]; 212 [scrollView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; 213 [[self contentView] addSubview: scrollView]; 214 [scrollView release]; 215 } 216 217 // Set up search field 218 { 219 searchfield = [[NSSearchField alloc] initWithFrame: NSMakeRect(9,9,186,22)]; 220 [searchfield setTarget: self]; 221 [searchfield setAction: @selector(search:)]; 222 [[self contentView] addSubview: searchfield]; 223 [searchfield release]; 224 } 225 } 226 227 return self; 228} 229 230- (void)dealloc 231{ 232 [assignedCodepoints release]; 233 [visibleCodepoints release]; 234 [super dealloc]; 235} 236 237- (void)search: (id)sender 238{ 239 NSString *str = [searchfield stringValue]; 240 241 if ([str length] == 0) 242 { 243 [self setVisibleCodepoints: assignedCodepoints]; 244 } 245 else 246 { 247 NSIndexSet *set = CodepointsWithNameContainingSubstring(str); 248 [self setVisibleCodepoints: set]; 249 } 250 251 [table reloadData]; 252} 253 254- (NSUInteger) codepointAtVisibleRow:(NSUInteger)row 255{ 256 //FIXME: Use a binary search 257 NSUInteger curr = 0; 258 NSUInteger currValue = [visibleCodepoints firstIndex]; 259 260 while (currValue != NSNotFound) 261 { 262 if (curr == row) 263 { 264 return currValue; 265 } 266 currValue = [visibleCodepoints indexGreaterThanIndex: currValue]; 267 curr++; 268 } 269 return NSNotFound; 270} 271 272- (NSString *)characterForRow: (NSInteger)row 273{ 274 if (row >= 0 && row < [visibleCodepoints count]) 275 { 276 UChar32 utf32 = [self codepointAtVisibleRow: row]; 277 UChar utf16buf[2]; 278 int32_t utf16bufLength = 0; 279 UErrorCode error = U_ZERO_ERROR; 280 u_strFromUTF32(utf16buf, 2, &utf16bufLength, &utf32, 1, &error); 281 282 return [[[NSString alloc] initWithCharacters: utf16buf 283 length: utf16bufLength] autorelease]; 284 } 285 return @""; 286} 287 288- (void) doubleClickRow: (id)sender 289{ 290 NSWindow *mainWindow = [NSApp mainWindow]; 291 NSResponder *firstResponder = [mainWindow firstResponder]; 292 NSString *str = [self characterForRow: [table clickedRow]]; 293 294 [firstResponder insertText: str]; 295} 296 297- (BOOL) tableView: (NSTableView *)aTable shouldEditTableColumn: (NSTableColumn *)aColumn row: (NSInteger)row 298{ 299 return NO; 300} 301 302// NSTableViewDataSource protocol 303 304- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView 305{ 306 return [visibleCodepoints count]; 307} 308 309- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 310{ 311 UChar32 utf32 = [self codepointAtVisibleRow: row]; 312 313 if ([[tableColumn identifier] isEqualToString: @"char"]) 314 { 315 return [self characterForRow: row]; 316 } 317 else if ([[tableColumn identifier] isEqualToString: @"name"]) 318 { 319 UErrorCode error = U_ZERO_ERROR; 320 int32_t size = u_charName(utf32, U_UNICODE_CHAR_NAME, NULL, 0, &error); 321 322 if (size > 0) 323 { 324 char name[512]; 325 error = U_ZERO_ERROR; 326 u_charName(utf32, U_UNICODE_CHAR_NAME, name, 512, &error); 327 328 NSString *nameObj = [[[NSString alloc] initWithBytes: name 329 length: size 330 encoding: NSASCIIStringEncoding] autorelease]; 331 return [[nameObj lowercaseString] capitalizedString]; 332 } 333 return @""; 334 } 335 else if ([[tableColumn identifier] isEqualToString: @"code"]) 336 { 337 return [NSString stringWithFormat:@"U+%04X", (int)utf32]; 338 } 339 else if ([[tableColumn identifier] isEqualToString: @"block"]) 340 { 341 int32_t val = u_getIntPropertyValue(utf32, UCHAR_BLOCK); 342 const char *name = u_getPropertyValueName(UCHAR_BLOCK, val, U_LONG_PROPERTY_NAME); 343 if (name != NULL) 344 { 345 return [[[[NSString alloc] initWithBytes: name 346 length: strlen(name) 347 encoding: NSASCIIStringEncoding] autorelease] stringByReplacingOccurrencesOfString: @"_" withString: @" "]; 348 } 349 } 350 return nil; 351} 352 353- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard 354{ 355 NSString *str = [self characterForRow: [rowIndexes firstIndex]]; 356 357 [pboard declareTypes: [NSArray arrayWithObject: NSStringPboardType] 358 owner: nil]; 359 [pboard setString: str 360 forType: NSStringPboardType]; 361 362 return YES; 363} 364 365@end 366 367#else // !(defined(HAVE_UNICODE_UCHAR_H) && defined(HAVE_UNICODE_USTRING_H)) 368 369@implementation GSCharacterPanel 370 371+ (GSCharacterPanel *) sharedCharacterPanel 372{ 373 return nil; 374} 375 376@end 377 378#endif 379