スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Cocoaで簡単なファイラーを作る

SimpleFiler
上記のような簡単なファイラーを作ります。
ツリーの使い方とテーブルの使い方はしばらくすると忘れて思い出すのに時間がかかるため、メモとして残しておきたいと思いました。
ソースコード一式はGitHubに置いてあります。
Cocoa Bindingを使うとソース上に現れない情報があるので、画像多めで行ってみます。
開発した環境は
MacBookPro6,2(15インチ)
MacOSX10.6.6
Xcode3.2.5
です。

まずはアプリケーションの新規作成から。Xcodeを起動してファイル→新規プロジェクトメニューを選択します。
新規登録
上のように、MacOSXのApplicationのCocoa Applicationを選択し次へでSimpleFilerと入力します。
プロジェクト
これで雛形のアプリケーションが作成されました。
次にファイル→新規ファイルを選択して、新しいクラスを作ります。
ファイル新規
MaxOSX→Objective-C Class→NSObjectを選択し次へ。
ここでファイルを表現するFileNodeクラスとツリーやテーブルでアイコンを表示するためのCustomCellクラスを作ります。
CustomCellに関してはこのページを参考にしました。

FileNode.h

#import


@interface FileNode : NSObject {
NSMutableArray* m_children;
NSMutableArray* m_files;
BOOL m_isLeaf;
NSString* name;
NSString* path;
NSImage* icon;
}
- (id)initWithPath:(NSString*)filePath;
- (NSMutableArray*) children;
- (BOOL) isLeaf;
- (NSMutableArray*) files;
- (FileNode*) me;

@property (assign,readonly) NSString* name;
@property (assign,readonly) NSString* path;
@property (assign,readonly) NSImage* icon;


@end


FileNode.m

#import "FileNode.h"


@implementation FileNode

@synthesize name;
@synthesize path;
@synthesize icon;

- (id)initWithPath:(NSString*)filePath
{
self = [super init];
if (!self) {
return nil;
}
path = filePath;
[path retain];
name = [[NSFileManager defaultManager] displayNameAtPath:path];
[name retain];
icon = [[NSWorkspace sharedWorkspace] iconForFile:path];
[icon retain];
[icon setSize:NSMakeSize(16, 16)];
return self;
}

- (void) dealloc
{
[path release];
[name release];
[icon release];
[m_children release];
[m_files release];
[super dealloc];
}

- (NSMutableArray*) children
{
if (!m_children) {
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDir, valid = [fileManager fileExistsAtPath:path isDirectory:&isDir];
if (valid & isDir) {
NSError* error;
NSArray *array = [fileManager contentsOfDirectoryAtPath:path error:&error];
NSInteger cnt, numChildren = [array count];
m_children = [[NSMutableArray alloc] initWithCapacity:numChildren];
m_files = [[NSMutableArray alloc] initWithCapacity:numChildren];
for (cnt = 0; cnt < numChildren; cnt++) {
NSString *filePath = [path stringByAppendingPathComponent:[array objectAtIndex:cnt]];
valid = [fileManager fileExistsAtPath:filePath isDirectory:&isDir];
if (valid && isDir) {
FileNode *item = [[[FileNode alloc] initWithPath:filePath] autorelease];
[m_children addObject:item];
}
if (valid && !isDir) {
FileNode *item = [[[FileNode alloc] initWithPath:filePath] autorelease];
[m_files addObject:item];
}
}
if (0 == [m_children count]) {
[m_children release];
m_children = nil;
m_isLeaf = YES;
} else {
m_isLeaf = NO;
}
} else {
m_isLeaf = YES;
m_children = nil;
}
}
return m_children;
}

- (BOOL) isLeaf
{
if (!m_children) {
[self children];
}
return m_isLeaf;
}

- (NSMutableArray*) files
{
if (!m_children) {
[self children];
}
return m_files;
}

- (FileNode*) me
{
return self;
}

- (id)copyWithZone:(NSZone *)zone
{
FileNode *copy = [[FileNode allocWithZone:zone] initWithPath:path];
return copy;
}

@end


CustomCell.h

#import


@interface CustomCell : NSCell {

}

@end


CustomCell.m

#import "CustomCell.h"
#import "FileNode.h"

@implementation CustomCell

- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
FileNode* node = [self objectValue];

[controlView lockFocus];
NSPoint p1 = cellFrame.origin;
p1.x += 0;
p1.y += 0 + [node.icon size].height;
[node.icon compositeToPoint:p1 operation:NSCompositeSourceOver];

NSPoint p2 = cellFrame.origin;
p2.x += 20;
p2.y += 0;
NSDictionary* attrs = [NSDictionary dictionary];
[node.name drawAtPoint:p2 withAttributes:attrs];
[controlView unlockFocus];
}

- (id)copyWithZone:(NSZone *)zone
{
CustomCell* cell = (CustomCell*)[super copyWithZone:zone];
return cell;
}

@end


さらにデフォルトにあるSimpleFilerAppDelegateにも手を入れます。これをメインのコントローラーとして利用しています。
SimpleFilerAppDelegate.h

#import

@class FileNode;

@interface SimpleFilerAppDelegate : NSObject {
NSWindow *window;
NSTableColumn *outlineColumn;
NSTableColumn *tableColumn;
FileNode *root;
}

@property (assign) IBOutlet NSWindow *window;
@property (assign) IBOutlet NSTableColumn *outlineColumn;
@property (assign) IBOutlet NSTableColumn *tableColumn;

@property (assign) FileNode *root;

@end


SimpleFilerAppDelegate.m

#import "SimpleFilerAppDelegate.h"
#import "FileNode.h"
#import "CustomCell.h"

@implementation SimpleFilerAppDelegate

@synthesize window;
@synthesize outlineColumn;
@synthesize tableColumn;
@synthesize root;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[outlineColumn setDataCell:[[[CustomCell alloc] init] autorelease]];
[tableColumn setDataCell:[[[CustomCell alloc] init] autorelease]];

self.root = [[[FileNode alloc] initWithPath:NSHomeDirectory()] autorelease];
}

@end


ソースコードが出そろったので、いよいよInterface Builderの登場です。
ここから、文章での説明が難しくなるのでイメージをガンガン貼っていきます。
MainMenu.xibをダブルクリックしてInterface Builder起動し、MainMenu.xibというウインドウにArray ContorollerとTree Controllerをドロップします。
MainMenu.xib

Tree Controllerを選択し、Attriluteのタブを開いて
Children→children
Leaf→isLeaf
Class Name→FileNode
Tree Controller Attribute

Bindingsタブを開いて
Content Object
 Bind To→Simple File App Delegate
 Model Key Path→root
Tree Controller Bindings

Array Controllerを選択し、Attributesタブを開いて
Class Name→FileNode
Array Controller Attributes

Bingingsタブを開いて
Content Array
 Bind To→Tree Controller
 Controller Key→selection
 Model Key Path→files
Array Controller Bindings

これでコントローラー系はOKです。
次にUIです。
WindowにVertical Split Viewを配置し、左にOutline View右にTable Viewを配置します。
Window

Outelet Viewを選択し、Attributesタブを開いて
Columns→1
Headers→disabled
Outline View Attrilutes

Table Viewを選択し、Attributesタブを開いて
Columns→1
Headers→disabled
Table View Attributes

Outline ViewのTable Columnを選択し、Bindingsタブを開いて
Value
 Bind To→Tree Controller
 Controller Key→arrangedObjects
 Model Key Path→me
Tree Table Column Bindings

Table ViewのTable Columnを選択し、Bindingsタブを開いて
Value
 Bind To→Array Controller
 Controller Key→arrangedObjects
Table View Column Bindings

UI系は以上です。
最後にメインコントローラーとUIオブジェクトを接続します。
MainMenu.xibウインドウからSimple Filer App Delegateを選択し、Connectionsタブを開いて
outlineColumn→Outline ViewのTable Column
tabeleColumn→Table ViewのTable Column
を接続します。接続の仕方は右にある丸をマウスダウンすると十記号になるので、それをターゲットのUIまでドラッグしてマウスアップすると接続されます。
Connections

以上で完成です。Xcodeに戻ってビルドとデバッグボタンを押せば動き出すはずです。
ちなみに苦労したところは、各Table ColumnのBindingされたValueの扱いが違うことです。
Table ViewはModel Key Pathを指定しないとオブジェクトそのものが渡されるのに、Outline Viewではこのキーを設定しないと実行時エラーになります。
仕方がないのでFileNodeクラスに自分自身を返すmeというプロパティを追加しました。
また、Outline ViewではValueに指定した値に対してcopyWithZoneを呼び出すため、FileNodeでは自分の内容をコピーして返すcopyWithZoneを実装しました。

ソースコード一式はGitHubに置いてあります。
スポンサーサイト

Tag : Cocoa Objective-C

ソースコードをblogに貼る

このブログではよくソースコードを貼りますが、そのためのツールとしてソースコード HTML化 コンバーター「唐辛子」を利用していました。
ところが、最近flashのバージョンを上げたせいか、改行がうまく反映されなくなっちゃいました。
しょうがないので、自前でおんなじようなツールをXcodeで作ってみました。
Code2Html.png
おんなじようなと言いつつ、実はかなり劣化してまして、予約語やコメント等に色を付けることができません。HTMLのエスケープくらいしかしてません。
ちなみに、HTMLのエスケープはgoogle-toolbox-for-macを利用しています。
あれ? ってことは、InterfeceBuilderでGUI作っただけとか?
まあ、ほとんどそんなもんですが、一応変換ボタンがクリックされた時のソースを載せます。これだけでも、慣れないもので色々調べながら作ったので、時間はかかりました。
#import "AppController.h"
#import "GTMNSString+HTML.h"

@implementation AppController

- (IBAction) convertAction:(id)sender
{
// ソースコード変換
NSString* convertedText = @"<pre><code>";
convertedText = [convertedText stringByAppendingString:[[[codeTextView textStorage] string] gtm_stringByEscapingForHTML]];
convertedText = [convertedText stringByAppendingString:@"</code></pre>"];

// 変換後コードを設定
NSTextStorage* htmlStorage = [htmlTextView textStorage];
NSString* htmlText = [htmlStorage string];
[htmlStorage replaceCharactersInRange:NSMakeRange(0, [htmlText length]) withString:convertedText];

// ペーストボードにコピー
NSPasteboard* pasteBoard = [NSPasteboard generalPasteboard];
[pasteBoard declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] owner:self];
[pasteBoard setString:convertedText forType:NSStringPboardType];

// プレビューに表示
[[webView mainFrame] loadHTMLString:htmlText baseURL:nil];
[tabView selectLastTabViewItem:nil];

}

NSTextFieldに値を入れたり出したり

XcodeでCocoaでObjective-Cな話です。カテゴリーをXcode、Cocoa、Objective-Cのどれにしようか迷いましたがXcodeで統一します。

Xcodeを軽く触ってみようと思って、2つのテキストフィールドに数字を入力して、ボタンを押すと、もう一つのテキストフィールドに合計値が表示されるアプリケーションをつくってみました。

テキストフィールドに値を設定するのは簡単にわかったんですけど、値を取得する方法がわかりませんでした。実はNSTextFieldクラスでは無くその継承元のNSControlクラスのメソッドにありました。

で、具体的な方法ですが
テキストフィールドに値を設定するには
[_field setIntValue:3]

テキストフィールドから値を取得するには
int val = [_filed intValue]

となります。他にもdoubleValue, floatValue, strigValue, objectValueなどがあります。

最後に実際に足し算を行うプログラムのヘッダーと本体を書きます。


#import <Cocoa/Cocoa.h>

@interface Controller : NSObject {
IBOutlet id _field1;
IBOutlet id _field2;
IBOutlet id _field3;
}
-(void)awakeFromNib;
-(IBAction)buttonAction:(id)sender;

@end



#import "Controller.h"


@implementation Controller
-(void)awakeFromNib
{
[_field1 setIntValue:1];
[_field2 setIntValue:2];
}

-(IBAction)buttonAction:(id)sender
{
int val = [_field1 intValue] + [_field2 intValue];
[_field3 setIntValue:val];
}
@end

Tag : Cocoa Objective-C

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。