• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Aucun tag

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

BathyScapheで画像のインラインプレビューを可能にするプラグイン


Commit MetaInfo

Révision9ad10f9ecb1c550c2951ad9267e3d4439b61bd64 (tree)
l'heure2012-05-18 23:05:51
Auteurmasakih <masakih@user...>
Commitermasakih

Message de Log

[New] BSIPEReplacerを新設。置換用

Change Summary

Modification

--- a/BSInlinePreviewer.xcodeproj/project.pbxproj
+++ b/BSInlinePreviewer.xcodeproj/project.pbxproj
@@ -19,6 +19,7 @@
1919 F4ADF9591565114F00F666EB /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F468C69C1220CA07009EFA3E /* WebKit.framework */; };
2020 F4ADF9671565124200F666EB /* BSInlinePreviewerEx.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ADF9661565124200F666EB /* BSInlinePreviewerEx.m */; };
2121 F4ADF9711565150D00F666EB /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F4ADF96F156514F800F666EB /* InfoPlist.strings */; };
22+ F4ADF98E1566774F00F666EB /* BSIPEReplacer.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ADF98C15666FAD00F666EB /* BSIPEReplacer.m */; };
2223 F4C8070C0E53CCB000BF4144 /* BSInlinePreviewer.m in Sources */ = {isa = PBXBuildFile; fileRef = F4C8070B0E53CCB000BF4144 /* BSInlinePreviewer.m */; };
2324 F4F1DC700E546EC800055177 /* notFound.png in Resources */ = {isa = PBXBuildFile; fileRef = F4F1DC6F0E546EC800055177 /* notFound.png */; };
2425 F4FE88A010F772DD0076B366 /* Panel.xib in Resources */ = {isa = PBXBuildFile; fileRef = F4FE889E10F772DD0076B366 /* Panel.xib */; };
@@ -43,6 +44,8 @@
4344 F4ADF9661565124200F666EB /* BSInlinePreviewerEx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSInlinePreviewerEx.m; sourceTree = "<group>"; };
4445 F4ADF96E156514F800F666EB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
4546 F4ADF970156514FE00F666EB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
47+ F4ADF98B15666FAD00F666EB /* BSIPEReplacer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BSIPEReplacer.h; path = Ex/BSIPEReplacer.h; sourceTree = "<group>"; };
48+ F4ADF98C15666FAD00F666EB /* BSIPEReplacer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BSIPEReplacer.m; path = Ex/BSIPEReplacer.m; sourceTree = "<group>"; };
4649 F4C807090E53CBE500BF4144 /* BSImagePreviewerInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSImagePreviewerInterface.h; sourceTree = "<group>"; };
4750 F4C8070A0E53CCB000BF4144 /* BSInlinePreviewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSInlinePreviewer.h; sourceTree = "<group>"; };
4851 F4C8070B0E53CCB000BF4144 /* BSInlinePreviewer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSInlinePreviewer.m; sourceTree = "<group>"; };
@@ -159,6 +162,8 @@
159162 children = (
160163 F4ADF9651565124200F666EB /* BSInlinePreviewerEx.h */,
161164 F4ADF9661565124200F666EB /* BSInlinePreviewerEx.m */,
165+ F4ADF98B15666FAD00F666EB /* BSIPEReplacer.h */,
166+ F4ADF98C15666FAD00F666EB /* BSIPEReplacer.m */,
162167 F4ADF95F1565115000F666EB /* BSInlinePreviewerEx-Info.plist */,
163168 F4ADF96F156514F800F666EB /* InfoPlist.strings */,
164169 );
@@ -306,6 +311,7 @@
306311 F4ADF9551565114F00F666EB /* BSInlinePreviewer.m in Sources */,
307312 F4ADF9561565114F00F666EB /* BSILinkInfomation.m in Sources */,
308313 F4ADF9671565124200F666EB /* BSInlinePreviewerEx.m in Sources */,
314+ F4ADF98E1566774F00F666EB /* BSIPEReplacer.m in Sources */,
309315 );
310316 runOnlyForDeploymentPostprocessing = 0;
311317 };
--- a/BSInlinePreviewerEx.m
+++ b/BSInlinePreviewerEx.m
@@ -9,8 +9,7 @@
99 #import "BSInlinePreviewerEx.h"
1010
1111 NSString *const CMRThreadViewerDidChangeThreadNotification = @"CMRThreadViewerDidChangeThreadNotification";
12-
13-static NSString *const BSInlinePreviewerPreviewed = @"BSInlinePreviewerPreviewed";
12+#import "BSIPEReplacer.h"
1413
1514
1615 @interface BSInlinePreviewer(Private)
@@ -42,99 +41,13 @@ static NSString *const BSInlinePreviewerPreviewed = @"BSInlinePreviewerPreviewed
4241 return self;
4342 }
4443
45-- (id)keyOfObject:(id)obj
46-{
47- return [NSString stringWithFormat:@"%p", obj];
48-}
49-- (void)insertImage:(NSDictionary *)attr
50-{
51- NSTextView *tv = [attr objectForKey:@"View"];
52- NSRange range = NSRangeFromString([attr objectForKey:@"Range"]);
53- id newInsertion = [attr objectForKey:@"Image"];
54- NSUInteger offset = [[attr objectForKey:@"Offset"] unsignedIntegerValue];
55- range.location += offset;
56-
57- NSTextStorage *ts = [tv textStorage];
58- [ts beginEditing];
59- {
60- [ts addAttribute:BSInlinePreviewerPreviewed
61- value:[NSNumber numberWithBool:YES]
62- range:range];
63-
64- [ts insertAttributedString:newInsertion atIndex:range.location];
65- }
66- [ts endEditing];
67-}
6844 - (void)viewerDidEndFinishing:(id)no
6945 {
70- NSLog(@"CMRThreadViewerDidChangeThreadNotification %@", no);
7146 id threadViewer = [no object];
72- NSTextView *tv = [[threadViewer textView] retain];
73- @synchronized(tasking) {
74- id check = [tasking objectForKey:[self keyOfObject:tv]];
75- if(check) return;
76- [tasking setObject:tv forKey:[self keyOfObject:tv]];
77- }
78-
79- dispatch_async(dispatch_get_global_queue(0,0), ^{
80- NSUInteger i = 0;
81- while([[tv textStorage] length] == 0) {
82- i++;
83- if(i > NSUIntegerMax - 5) {
84- NSLog(@"Abort");
85- return;
86- }
87- }
88- sleep(1);
89- NSTextStorage *ts = [[tv textStorage] copy];
90-
91- NSMutableArray *links = [NSMutableArray array];
92-
93- [ts enumerateAttribute:NSLinkAttributeName
94- inRange:NSMakeRange(0, [ts length])
95- options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
96- usingBlock:^(id value, NSRange range, BOOL *stop) {
97- NSURL *url = [NSURL URLWithString:value];
98- if([self validateLink:url]) {
99- [links addObject:[NSDictionary dictionaryWithObjectsAndKeys:url, @"Link",
100- NSStringFromRange(range), @"Range", nil]];
101- }
102- }];
103-
104- __block NSUInteger offset = 0;
105- NSUInteger count = [links count];
106- dispatch_apply(count, dispatch_get_global_queue(0,0), ^(size_t index){
107- id dict = [links objectAtIndex:index];
108- NSRange range = NSRangeFromString([dict objectForKey:@"Range"]);
109- if([ts attribute:BSInlinePreviewerPreviewed atIndex:range.location longestEffectiveRange:NULL inRange:range]) {
110- return;
111- }
112- // download image.
113- self.totalDownloads = self.remainder = 1;
114- NSImage *image = [self downloadImageURL:[dict objectForKey:@"Link"]];
115- self.remainder = 0;
116- if(!image) return;
117-
118- id newInsertion = [self attachmentAttributedStringWithImage:image];
119-
120-
121- NSDictionary *attr = [[NSDictionary alloc] initWithObjectsAndKeys:
122- newInsertion, @"Image",
123- [dict objectForKey:@"Range"], @"Range",
124- tv, @"View",
125- [NSNumber numberWithUnsignedInteger:offset], @"Offset",
126- nil];
127- [self performSelectorOnMainThread:@selector(insertImage:) withObject:attr waitUntilDone:NO];
128- offset += [newInsertion length];
129- [attr release];
130- });
131- [ts release];
132- [tv release];
133- @synchronized(tasking) {
134- [tasking removeObjectForKey:[self keyOfObject:tv]];
135- }
136-// NSLog(@"ts \n%@", ts);
137- });
47+ NSTextView *tv = [threadViewer textView];
48+ BSIPEReplacer *replacer = [BSIPEReplacer replaserWithTextView:tv];
49+ replacer.owner = self;
50+ replacer.textView = tv;
13851 }
13952
14053
--- /dev/null
+++ b/Ex/BSIPEReplacer.h
@@ -0,0 +1,26 @@
1+//
2+// BSIPEReplacer.h
3+// BSInlinePreviewer
4+//
5+// Created by 堀 昌樹 on 12/05/18.
6+// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
7+//
8+
9+#import <Foundation/Foundation.h>
10+
11+#import "BSInlinePreviewerEx.h"
12+
13+@interface BSIPEReplacer : NSObject
14+{
15+ NSLock *lock;
16+ NSTextView *_textView;
17+ BSInlinePreviewerEx *_owner;
18+
19+ BOOL _newStorage;
20+ NSUInteger _selfAwaking;
21+}
22+@property (retain) NSTextView *textView;
23+@property (assign) BSInlinePreviewerEx *owner;
24+
25++ (id)replaserWithTextView:(NSTextView *)tv;
26+@end
--- /dev/null
+++ b/Ex/BSIPEReplacer.m
@@ -0,0 +1,196 @@
1+//
2+// BSIPEReplacer.m
3+// BSInlinePreviewer
4+//
5+// Created by 堀 昌樹 on 12/05/18.
6+// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
7+//
8+
9+#import "BSIPEReplacer.h"
10+
11+@interface BSIPEReplacer ()
12+@property BOOL newStorage;
13+
14+@property NSUInteger selfAwaking;
15+
16+@end
17+
18+@implementation BSIPEReplacer
19+@synthesize textView = _textView;
20+@synthesize owner = _owner;
21+
22+@synthesize newStorage = _newStorage;
23+@synthesize selfAwaking = _selfAwaking;
24+
25+
26+static NSMutableDictionary *instances = nil;
27+id keyForTextView(NSTextView *view)
28+{
29+ return [NSString stringWithFormat:@"%p", view];
30+}
31++ (id)replaserWithTextView:(NSTextView *)tv
32+{
33+ if(!instances) {
34+ instances = [[NSMutableDictionary alloc] init];
35+ }
36+ id result = nil;
37+ @synchronized(self) {
38+ result = [instances objectForKey:keyForTextView(tv)];
39+ [result retain];
40+ }
41+ if(result) return [result autorelease];
42+
43+ result = [[[self alloc] init] autorelease];
44+ @synchronized(self) {
45+ [instances setObject:result forKey:keyForTextView(tv)];
46+ }
47+ return result;
48+}
49+
50+- (void)setTextView:(NSTextView *)textView
51+{
52+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
53+ [nc removeObserver:self name:nil object:nil];
54+
55+ @synchronized(self) {
56+ [_textView release];
57+ _textView = [textView retain];
58+ }
59+ if(textView) {
60+ [nc addObserver:self
61+ selector:@selector(textDidChange:)
62+ name:NSTextStorageDidProcessEditingNotification
63+ object:[_textView textStorage]];
64+ [nc addObserver:self
65+ selector:@selector(windowWillClose:)
66+ name:NSWindowWillCloseNotification
67+ object:[_textView window]];
68+ }
69+ [self textDidChange:nil];
70+}
71+- (NSTextView *)textView
72+{
73+ id result = nil;
74+ @synchronized(self) {
75+ result = [_textView retain];
76+ }
77+ return [result autorelease];
78+}
79+- (id)init
80+{
81+ self = [super init];
82+ if(self) {
83+ lock = [[NSLock alloc] init];
84+ }
85+ return self;
86+}
87+- (void)dealloc
88+{
89+ [_textView release];
90+ [lock release];
91+
92+ [super dealloc];
93+}
94+
95+NSRange fixRange(NSRange range, NSTextStorage *ts)
96+{
97+ NSRange fixedRange = {0,0};
98+ NSRange searchRange = NSMakeRange(MAX(0, range.location - 20), range.length + 20);
99+ searchRange = NSIntersectionRange(searchRange, NSMakeRange(0, [ts length]));
100+ [ts attribute:NSLinkAttributeName atIndex:range.location longestEffectiveRange:&fixedRange inRange:searchRange];
101+ return fixedRange;
102+}
103+- (void)insertImage:(NSDictionary *)attr
104+{
105+ NSTextView *tv = [attr objectForKey:@"View"];
106+ NSRange range = NSRangeFromString([attr objectForKey:@"Range"]);
107+ id newInsertion = [attr objectForKey:@"Image"];
108+ NSUInteger offset = [[attr objectForKey:@"Offset"] unsignedIntegerValue];
109+ range.location += offset;
110+
111+ NSTextStorage *ts = [tv textStorage];
112+ range = fixRange(range, ts);
113+ [ts beginEditing];
114+ {
115+ [ts addAttribute:BSInlinePreviewerPreviewed
116+ value:[NSNumber numberWithBool:YES]
117+ range:range];
118+
119+ [ts insertAttributedString:newInsertion atIndex:range.location];
120+ self.selfAwaking++;
121+ }
122+ [ts endEditing];
123+}
124+- (void)textDidChange:(NSNotification *)no
125+{
126+ NSUInteger length = [[self.textView textStorage] length];
127+ if(length == 0) {
128+ self.newStorage = YES;
129+ self.selfAwaking = 0;
130+ return;
131+ }
132+ if(self.selfAwaking > 0) {
133+ self.selfAwaking--;
134+ return;
135+ }
136+
137+ NSTextStorage *ts = [[self.textView textStorage] copy];
138+ dispatch_async(dispatch_get_global_queue(0,0), ^{
139+
140+ [lock lock];
141+
142+
143+ NSMutableArray *links = [NSMutableArray array];
144+
145+ [ts enumerateAttribute:NSLinkAttributeName
146+ inRange:NSMakeRange(0, [ts length])
147+ options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
148+ usingBlock:^(id value, NSRange range, BOOL *stop) {
149+ NSURL *url = [NSURL URLWithString:value];
150+ if([self.owner validateLink:url]) {
151+ [links addObject:[NSDictionary dictionaryWithObjectsAndKeys:url, @"Link",
152+ NSStringFromRange(range), @"Range", nil]];
153+ }
154+ }];
155+
156+ __block NSUInteger offset = 0;
157+ NSUInteger count = [links count];
158+ dispatch_apply(count, dispatch_get_global_queue(0,0), ^(size_t index){
159+ if(!self.textView) return;
160+
161+ id dict = [links objectAtIndex:index];
162+ NSRange range = NSRangeFromString([dict objectForKey:@"Range"]);
163+ if([ts attribute:BSInlinePreviewerPreviewed atIndex:range.location longestEffectiveRange:NULL inRange:range]) {
164+ return;
165+ }
166+ // download image.
167+ NSImage *image = [self.owner downloadImageURL:[dict objectForKey:@"Link"]];
168+ if(!image) return;
169+
170+ id newInsertion = [self.owner attachmentAttributedStringWithImage:image];
171+
172+
173+ NSDictionary *attr = [[NSDictionary alloc] initWithObjectsAndKeys:
174+ newInsertion, @"Image",
175+ [dict objectForKey:@"Range"], @"Range",
176+ self.textView, @"View",
177+ [NSNumber numberWithUnsignedInteger:offset], @"Offset",
178+ nil];
179+ [self performSelectorOnMainThread:@selector(insertImage:) withObject:attr waitUntilDone:NO];
180+ offset += [newInsertion length];
181+ [attr release];
182+ });
183+ [ts release];
184+ // NSLog(@"ts \n%@", ts);
185+
186+ [lock unlock];
187+ });
188+}
189+
190+- (void)windowWillClose:(NSNotification *)notification
191+{
192+ [[self retain] autorelease];
193+ [instances removeObjectForKey:keyForTextView(_textView)];
194+ self.textView = nil;
195+}
196+@end