SKLineWell.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. //
  2. // SKLineWell.m
  3. // Skim
  4. //
  5. // Created by Christiaan Hofman on 6/22/07.
  6. /*
  7. This software is Copyright (c) 2007-2018
  8. Christiaan Hofman. All rights reserved.
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions
  11. are met:
  12. - Redistributions of source code must retain the above copyright
  13. notice, this list of conditions and the following disclaimer.
  14. - Redistributions in binary form must reproduce the above copyright
  15. notice, this list of conditions and the following disclaimer in
  16. the documentation and/or other materials provided with the
  17. distribution.
  18. - Neither the name of Christiaan Hofman nor the names of any
  19. contributors may be used to endorse or promote products derived
  20. from this software without specific prior written permission.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. */
  33. #import "SKLineWell.h"
  34. //#import "SKLineInspector.h"
  35. //#import "NSGraphics_SKExtensions.h"
  36. //#import "NSBezierPath_SKExtensions.h"
  37. #import "NSImage_SKExtensions.h"
  38. //#import "NSView_SKExtensions.h"
  39. NSString *SKPasteboardTypeLineStyle = @"net.sourceforge.skim-app.pasteboard.line-style";
  40. NSString *SKLineWellLineWidthKey = @"lineWidth";
  41. NSString *SKLineWellStyleKey = @"style";
  42. NSString *SKLineWellDashPatternKey = @"dashPattern";
  43. NSString *SKLineWellStartLineStyleKey = @"startLineStyle";
  44. NSString *SKLineWellEndLineStyleKey = @"endLineStyle";
  45. #define DISPLAYSTYLE_KEY @"lwFlags.displayStyle"
  46. #define ACTIVE_KEY @"active"
  47. #define ACTION_KEY @"action"
  48. #define TARGET_KEY @"target"
  49. #define SKLineWellWillBecomeActiveNotification @"SKLineWellWillBecomeActiveNotification"
  50. #define EXCLUSIVE_KEY @"exclusive"
  51. @implementation SKLineWell
  52. @synthesize lineWidth, style, dashPattern, startLineStyle, endLineStyle;
  53. @dynamic isActive, canActivate, displayStyle;
  54. + (void)initialize {
  55. // SKINITIALIZE;
  56. [self exposeBinding:SKLineWellLineWidthKey];
  57. [self exposeBinding:SKLineWellStyleKey];
  58. [self exposeBinding:SKLineWellDashPatternKey];
  59. [self exposeBinding:SKLineWellStartLineStyleKey];
  60. [self exposeBinding:SKLineWellEndLineStyleKey];
  61. }
  62. - (Class)valueClassForBinding:(NSString *)binding {
  63. if ([binding isEqualToString:SKLineWellDashPatternKey])
  64. return [NSArray class];
  65. else
  66. return [NSNumber class];
  67. }
  68. - (void)commonInit {
  69. lwFlags.canActivate = 1;
  70. lwFlags.existsActiveLineWell = 0;
  71. [self registerForDraggedTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]];
  72. }
  73. - (id)initWithFrame:(NSRect)frame {
  74. self = [super initWithFrame:frame];
  75. if (self) {
  76. lineWidth = 1.0;
  77. style = kPDFBorderStyleSolid;
  78. dashPattern = nil;
  79. startLineStyle = kPDFLineStyleNone;
  80. endLineStyle = kPDFLineStyleNone;
  81. lwFlags.displayStyle = SKLineWellDisplayStyleLine;
  82. lwFlags.active = 0;
  83. lwFlags.canActivate = 0;
  84. lwFlags.existsActiveLineWell = 0;
  85. target = nil;
  86. action = NULL;
  87. [self commonInit];
  88. }
  89. return self;
  90. }
  91. - (id)initWithCoder:(NSCoder *)decoder {
  92. self = [super initWithCoder:decoder];
  93. if (self) {
  94. lineWidth = [decoder decodeDoubleForKey:SKLineWellLineWidthKey];
  95. style = [decoder decodeIntegerForKey:SKLineWellStyleKey];
  96. dashPattern = [decoder decodeObjectForKey:SKLineWellDashPatternKey];
  97. startLineStyle = [decoder decodeIntegerForKey:SKLineWellStartLineStyleKey];
  98. endLineStyle = [decoder decodeIntegerForKey:SKLineWellEndLineStyleKey];
  99. lwFlags.displayStyle = [decoder decodeIntegerForKey:DISPLAYSTYLE_KEY];
  100. lwFlags.active = [decoder decodeBoolForKey:ACTIVE_KEY];
  101. action = NSSelectorFromString([decoder decodeObjectForKey:ACTION_KEY]);
  102. target = [decoder decodeObjectForKey:TARGET_KEY];
  103. [self commonInit];
  104. }
  105. return self;
  106. }
  107. - (void)encodeWithCoder:(NSCoder *)coder {
  108. [super encodeWithCoder:coder];
  109. [coder encodeDouble:lineWidth forKey:SKLineWellLineWidthKey];
  110. [coder encodeInteger:style forKey:SKLineWellStyleKey];
  111. [coder encodeObject:dashPattern forKey:SKLineWellDashPatternKey];
  112. [coder encodeInteger:startLineStyle forKey:SKLineWellStartLineStyleKey];
  113. [coder encodeInteger:endLineStyle forKey:SKLineWellEndLineStyleKey];
  114. [coder encodeInteger:(NSInteger)(lwFlags.displayStyle) forKey:DISPLAYSTYLE_KEY];
  115. [coder encodeBool:(BOOL)(lwFlags.active) forKey:ACTIVE_KEY];
  116. [coder encodeObject:NSStringFromSelector(action) forKey:ACTION_KEY];
  117. [coder encodeConditionalObject:target forKey:TARGET_KEY];
  118. }
  119. - (void)dealloc {
  120. // SKENSURE_MAIN_THREAD(
  121. [self unbind:SKLineWellLineWidthKey];
  122. [self unbind:SKLineWellStyleKey];
  123. [self unbind:SKLineWellDashPatternKey];
  124. [self unbind:SKLineWellStartLineStyleKey];
  125. [self unbind:SKLineWellEndLineStyleKey];
  126. // );
  127. [[NSNotificationCenter defaultCenter] removeObserver:self];
  128. // SKDESTROY(dashPattern);
  129. // [super dealloc];
  130. }
  131. - (BOOL)isOpaque{ return YES; }
  132. - (BOOL)acceptsFirstResponder { return [self canActivate]; }
  133. - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { return [self canActivate]; }
  134. - (void)viewWillMoveToWindow:(NSWindow *)newWindow {
  135. [self deactivate];
  136. [super viewWillMoveToWindow:newWindow];
  137. }
  138. - (NSBezierPath *)path {
  139. NSBezierPath *path = [NSBezierPath bezierPath];
  140. NSRect bounds = [self bounds];
  141. if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  142. CGFloat offset = 0.5 * lineWidth - floor(0.5 * lineWidth);
  143. NSPoint startPoint = NSMakePoint(NSMinX(bounds) + ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset);
  144. NSPoint endPoint = NSMakePoint(NSMaxX(bounds) - ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset);
  145. switch (startLineStyle) {
  146. case kPDFLineStyleNone:
  147. break;
  148. case kPDFLineStyleSquare:
  149. [path appendBezierPathWithRect:NSMakeRect(startPoint.x - 1.5 * lineWidth, startPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth)];
  150. break;
  151. case kPDFLineStyleCircle:
  152. [path appendBezierPathWithOvalInRect:NSMakeRect(startPoint.x - 1.5 * lineWidth, startPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth)];
  153. break;
  154. case kPDFLineStyleDiamond:
  155. [path moveToPoint:NSMakePoint(startPoint.x - 1.5 * lineWidth, startPoint.y)];
  156. [path lineToPoint:NSMakePoint(startPoint.x, startPoint.y + 1.5 * lineWidth)];
  157. [path lineToPoint:NSMakePoint(startPoint.x + 1.5 * lineWidth, startPoint.y)];
  158. [path lineToPoint:NSMakePoint(startPoint.x, startPoint.y - 1.5 * lineWidth)];
  159. [path closePath];
  160. break;
  161. case kPDFLineStyleOpenArrow:
  162. [path moveToPoint:NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y - 1.5 * lineWidth)];
  163. [path lineToPoint:NSMakePoint(startPoint.x, startPoint.y)];
  164. [path lineToPoint:NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y + 1.5 * lineWidth)];
  165. break;
  166. case kPDFLineStyleClosedArrow:
  167. [path moveToPoint:NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y - 1.5 * lineWidth)];
  168. [path lineToPoint:NSMakePoint(startPoint.x, startPoint.y)];
  169. [path lineToPoint:NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y + 1.5 * lineWidth)];
  170. [path closePath];
  171. break;
  172. }
  173. [path moveToPoint:startPoint];
  174. [path lineToPoint:endPoint];
  175. switch (endLineStyle) {
  176. case kPDFLineStyleNone:
  177. break;
  178. case kPDFLineStyleSquare:
  179. [path appendBezierPathWithRect:NSMakeRect(endPoint.x - 1.5 * lineWidth, endPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth)];
  180. break;
  181. case kPDFLineStyleCircle:
  182. [path appendBezierPathWithOvalInRect:NSMakeRect(endPoint.x - 1.5 * lineWidth, endPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth)];
  183. break;
  184. case kPDFLineStyleDiamond:
  185. [path moveToPoint:NSMakePoint(endPoint.x + 1.5 * lineWidth, endPoint.y)];
  186. [path lineToPoint:NSMakePoint(endPoint.x, endPoint.y + 1.5 * lineWidth)];
  187. [path lineToPoint:NSMakePoint(endPoint.x - 1.5 * lineWidth, endPoint.y)];
  188. [path lineToPoint:NSMakePoint(endPoint.x, endPoint.y - 1.5 * lineWidth)];
  189. [path closePath];
  190. break;
  191. case kPDFLineStyleOpenArrow:
  192. [path moveToPoint:NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y + 1.5 * lineWidth)];
  193. [path lineToPoint:NSMakePoint(endPoint.x, endPoint.y)];
  194. [path lineToPoint:NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y - 1.5 * lineWidth)];
  195. break;
  196. case kPDFLineStyleClosedArrow:
  197. [path moveToPoint:NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y + 1.5 * lineWidth)];
  198. [path lineToPoint:NSMakePoint(endPoint.x, endPoint.y)];
  199. [path lineToPoint:NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y - 1.5 * lineWidth)];
  200. [path closePath];
  201. break;
  202. }
  203. } else if ([self displayStyle] == SKLineWellDisplayStyleSimpleLine) {
  204. CGFloat offset = 0.5 * lineWidth - floor(0.5 * lineWidth);
  205. [path moveToPoint:NSMakePoint(NSMinX(bounds) + ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset)];
  206. [path lineToPoint:NSMakePoint(NSMaxX(bounds) - ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset)];
  207. } else if ([self displayStyle] == SKLineWellDisplayStyleRectangle) {
  208. CGFloat inset = 7.0 + 0.5 * lineWidth;
  209. [path appendBezierPathWithRect:NSInsetRect(bounds, inset, inset)];
  210. } else {
  211. CGFloat inset = 7.0 + 0.5 * lineWidth;
  212. [path appendBezierPathWithOvalInRect:NSInsetRect(bounds, inset, inset)];
  213. }
  214. [path setLineWidth:lineWidth];
  215. // if (style == kPDFBorderStyleDashed)
  216. // [path setDashPattern:dashPattern];
  217. return path;
  218. }
  219. - (void)drawRect:(NSRect)rect {
  220. NSRect bounds = [self bounds];
  221. // SKDrawTextFieldBezel(bounds, self);
  222. if ([self isActive]) {
  223. [NSGraphicsContext saveGraphicsState];
  224. [[NSColor selectedControlColor] setFill];
  225. NSRectFillUsingOperation(bounds, NSCompositePlusDarker);
  226. [NSGraphicsContext restoreGraphicsState];
  227. }
  228. if ([self isHighlighted]) {
  229. [NSGraphicsContext saveGraphicsState];
  230. [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] setFill];
  231. NSFrameRectWithWidthUsingOperation([self bounds], 1.0, NSCompositePlusDarker);
  232. [NSGraphicsContext restoreGraphicsState];
  233. }
  234. if (lineWidth > 0.0) {
  235. [NSGraphicsContext saveGraphicsState];
  236. [[NSBezierPath bezierPathWithRect:NSInsetRect(bounds, 2.0, 2.0)] addClip];
  237. [[NSColor blackColor] setStroke];
  238. [[self path] stroke];
  239. [NSGraphicsContext restoreGraphicsState];
  240. }
  241. if ([self refusesFirstResponder] == NO && [NSApp isActive] && [[self window] isKeyWindow] && [[self window] firstResponder] == self) {
  242. [NSGraphicsContext saveGraphicsState];
  243. NSSetFocusRingStyle(NSFocusRingOnly);
  244. NSRectFill(bounds);
  245. [NSGraphicsContext restoreGraphicsState];
  246. }
  247. }
  248. - (NSImage *)dragImage {
  249. NSRect bounds = [self bounds];
  250. NSBezierPath *path = lineWidth > 0.0 ? [self path] : nil;
  251. // CGFloat scale = [self backingScale];
  252. CGFloat scale = 1;
  253. NSImage *image = [NSImage bitmapImageWithSize:bounds.size scale:scale drawingHandler:^(NSRect rect){
  254. CGContextSetAlpha([[NSGraphicsContext currentContext] graphicsPort], 0.7);
  255. [[NSColor darkGrayColor] setFill];
  256. NSRectFill(NSInsetRect(rect, 1.0, 1.0));
  257. [[NSColor controlBackgroundColor] setFill];
  258. NSRectFill(NSInsetRect(rect, 2.0, 2.0));
  259. [[NSColor blackColor] setStroke];
  260. [path stroke];
  261. }];
  262. return image;
  263. }
  264. - (void)changedValueForKey:(NSString *)key {
  265. if ([self isActive])
  266. // [[SKLineInspector sharedLineInspector] setValue:[self valueForKey:key] forKey:key];
  267. [self setNeedsDisplay:YES];
  268. }
  269. - (void)takeValueForKey:(NSString *)key from:(id)object {
  270. [self setValue:[object valueForKey:key] forKey:key];
  271. NSDictionary *info = [self infoForBinding:key];
  272. [[info objectForKey:NSObservedObjectKey] setValue:[self valueForKey:key] forKeyPath:[info objectForKey:NSObservedKeyPathKey]];
  273. }
  274. - (void)mouseDown:(NSEvent *)theEvent {
  275. if ([self isEnabled]) {
  276. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  277. [self setNeedsDisplay:YES];
  278. NSUInteger modifiers = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
  279. BOOL keepOn = YES;
  280. while (keepOn) {
  281. theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask];
  282. switch ([theEvent type]) {
  283. case NSLeftMouseDragged:
  284. {
  285. NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  286. [NSNumber numberWithDouble:lineWidth], SKLineWellLineWidthKey, [NSNumber numberWithInteger:style], SKLineWellStyleKey, dashPattern, SKLineWellDashPatternKey, nil];
  287. if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  288. [dict setObject:[NSNumber numberWithInteger:startLineStyle] forKey:SKLineWellStartLineStyleKey];
  289. [dict setObject:[NSNumber numberWithInteger:endLineStyle] forKey:SKLineWellEndLineStyleKey];
  290. }
  291. NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
  292. [pboard clearContents];
  293. [pboard setPropertyList:dict forType:SKPasteboardTypeLineStyle];
  294. NSRect bounds = [self bounds];
  295. [self dragImage:[self dragImage] at:bounds.origin offset:NSZeroSize event:theEvent pasteboard:pboard source:self slideBack:YES];
  296. keepOn = NO;
  297. break;
  298. }
  299. case NSLeftMouseUp:
  300. if ([self isActive])
  301. [self deactivate];
  302. else
  303. [self activate:(modifiers & NSShiftKeyMask) == 0];
  304. keepOn = NO;
  305. break;
  306. default:
  307. break;
  308. }
  309. }
  310. }
  311. }
  312. - (void)performClick:(id)sender {
  313. if ([self isEnabled]) {
  314. if ([self isActive])
  315. [self deactivate];
  316. else
  317. [self activate:YES];
  318. }
  319. }
  320. - (void)existsActiveLineWell {
  321. lwFlags.existsActiveLineWell = 1;
  322. }
  323. - (void)lineWellWillBecomeActive:(NSNotification *)notification {
  324. id sender = [notification object];
  325. if (sender != self && [self isActive]) {
  326. if ([[[notification userInfo] valueForKey:EXCLUSIVE_KEY] boolValue])
  327. [self deactivate];
  328. else
  329. [sender existsActiveLineWell];
  330. }
  331. }
  332. - (void)lineInspectorWindowWillClose:(NSNotification *)notification {
  333. [self deactivate];
  334. }
  335. - (void)activate:(BOOL)exclusive {
  336. if ([self canActivate]) {
  337. NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
  338. // SKLineInspector *inspector = [SKLineInspector sharedLineInspector];
  339. lwFlags.existsActiveLineWell = 0;
  340. [nc postNotificationName:SKLineWellWillBecomeActiveNotification object:self
  341. userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:exclusive], EXCLUSIVE_KEY, nil]];
  342. if (lwFlags.existsActiveLineWell) {
  343. // [self takeValueForKey:SKLineWellLineWidthKey from:inspector];
  344. // [self takeValueForKey:SKLineWellDashPatternKey from:inspector];
  345. // [self takeValueForKey:SKLineWellStyleKey from:inspector];
  346. if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  347. // [self takeValueForKey:SKLineWellStartLineStyleKey from:inspector];
  348. // [self takeValueForKey:SKLineWellEndLineStyleKey from:inspector];
  349. }
  350. } else {
  351. // [inspector setLineWidth:[self lineWidth]];
  352. // [inspector setDashPattern:[self dashPattern]];
  353. // [inspector setStyle:[self style]];
  354. if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  355. // [inspector setStartLineStyle:[self startLineStyle]];
  356. // [inspector setEndLineStyle:[self endLineStyle]];
  357. }
  358. }
  359. // [[inspector window] orderFront:self];
  360. [nc addObserver:self selector:@selector(lineWellWillBecomeActive:)
  361. name:SKLineWellWillBecomeActiveNotification object:nil];
  362. // [nc addObserver:self selector:@selector(lineInspectorWindowWillClose:)
  363. // name:NSWindowWillCloseNotification object:[inspector window]];
  364. // [nc addObserver:self selector:@selector(lineInspectorLineAttributeChanged:)
  365. // name:SKLineInspectorLineAttributeDidChangeNotification object:inspector];
  366. lwFlags.active = 1;
  367. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  368. [self setNeedsDisplay:YES];
  369. }
  370. }
  371. - (void)deactivate {
  372. if ([self isActive]) {
  373. [[NSNotificationCenter defaultCenter] removeObserver:self];
  374. lwFlags.active = 0;
  375. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  376. [self setNeedsDisplay:YES];
  377. }
  378. }
  379. #pragma mark Accessors
  380. - (SEL)action { return action; }
  381. - (void)setAction:(SEL)newAction { action = newAction; }
  382. - (id)target { return target; }
  383. - (void)setTarget:(id)newTarget { target = newTarget; }
  384. - (BOOL)isActive {
  385. return lwFlags.active;
  386. }
  387. - (BOOL)canActivate {
  388. return lwFlags.canActivate;
  389. }
  390. - (void)setCanActivate:(BOOL)flag {
  391. if (lwFlags.canActivate != flag) {
  392. lwFlags.canActivate = flag;
  393. if ([self isActive] && lwFlags.canActivate == 0)
  394. [self deactivate];
  395. }
  396. }
  397. - (BOOL)isHighlighted {
  398. return lwFlags.highlighted;
  399. }
  400. - (void)setHighlighted:(BOOL)flag {
  401. if (lwFlags.highlighted != flag) {
  402. lwFlags.highlighted = flag;
  403. }
  404. }
  405. - (SKLineWellDisplayStyle)displayStyle {
  406. return lwFlags.displayStyle;
  407. }
  408. - (void)setDisplayStyle:(SKLineWellDisplayStyle)newStyle {
  409. if (lwFlags.displayStyle != newStyle) {
  410. lwFlags.displayStyle = newStyle;
  411. [self setNeedsDisplay:YES];
  412. }
  413. }
  414. - (void)setLineWidth:(CGFloat)width {
  415. if (fabs(lineWidth - width) > 0.00001) {
  416. lineWidth = width;
  417. [self changedValueForKey:SKLineWellLineWidthKey];
  418. }
  419. }
  420. - (void)setStyle:(PDFBorderStyle)newStyle {
  421. if (newStyle != style) {
  422. style = newStyle;
  423. [self changedValueForKey:SKLineWellStyleKey];
  424. }
  425. }
  426. - (void)setDashPattern:(NSArray *)pattern {
  427. if (NSIsControllerMarker(pattern)) pattern = nil;
  428. if ([pattern isEqualToArray:dashPattern] == NO && (pattern || dashPattern)) {
  429. dashPattern = pattern;
  430. [self changedValueForKey:SKLineWellDashPatternKey];
  431. }
  432. }
  433. - (void)setStartLineStyle:(PDFLineStyle)newStyle {
  434. if (newStyle != startLineStyle) {
  435. startLineStyle = newStyle;
  436. [self changedValueForKey:SKLineWellStartLineStyleKey];
  437. }
  438. }
  439. - (void)setEndLineStyle:(PDFLineStyle)newStyle {
  440. if (newStyle != endLineStyle) {
  441. endLineStyle = newStyle;
  442. [self changedValueForKey:SKLineWellEndLineStyleKey];
  443. }
  444. }
  445. - (void)setNilValueForKey:(NSString *)key {
  446. if ([key isEqualToString:SKLineWellLineWidthKey] || [key isEqualToString:SKLineWellStyleKey] ||
  447. [key isEqualToString:SKLineWellStartLineStyleKey] || [key isEqualToString:SKLineWellEndLineStyleKey]) {
  448. [self setValue:[NSNumber numberWithInteger:0] forKey:key];
  449. } else {
  450. [super setNilValueForKey:key];
  451. }
  452. }
  453. #pragma mark Notification handlers
  454. - (void)lineInspectorLineAttributeChanged:(NSNotification *)notification {
  455. // SKLineInspector *inspector = [notification object];
  456. // NSString *key = nil;
  457. // switch ([inspector currentLineChangeAction]) {
  458. // case SKLineChangeActionLineWidth:
  459. // key = SKLineWellLineWidthKey;
  460. // break;
  461. // case SKLineChangeActionStyle:
  462. // key = SKLineWellStyleKey;
  463. // break;
  464. // case SKLineChangeActionDashPattern:
  465. // key = SKLineWellDashPatternKey;
  466. // break;
  467. // case SKLineChangeActionStartLineStyle:
  468. // if ([self displayStyle] == SKLineWellDisplayStyleLine)
  469. // key = SKLineWellStartLineStyleKey;
  470. // break;
  471. // case SKLineChangeActionEndLineStyle:
  472. // if ([self displayStyle] == SKLineWellDisplayStyleLine)
  473. // key = SKLineWellEndLineStyleKey;
  474. // break;
  475. // case SKNoLineChangeAction:
  476. // break;
  477. // }
  478. // if (key) {
  479. // [self takeValueForKey:key from:inspector];
  480. // [self sendAction:[self action] to:[self target]];
  481. // }
  482. }
  483. #pragma mark NSDraggingSource protocol
  484. - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
  485. return NSDragOperationGeneric;
  486. }
  487. #pragma mark NSDraggingDestination protocol
  488. - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
  489. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]]) {
  490. [self setHighlighted:YES];
  491. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  492. [self setNeedsDisplay:YES];
  493. return NSDragOperationEvery;
  494. } else
  495. return NSDragOperationNone;
  496. }
  497. - (void)draggingExited:(id <NSDraggingInfo>)sender {
  498. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]]) {
  499. [self setHighlighted:NO];
  500. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  501. [self setNeedsDisplay:YES];
  502. }
  503. }
  504. - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
  505. return [self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]];
  506. }
  507. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender{
  508. NSPasteboard *pboard = [sender draggingPasteboard];
  509. [pboard types];
  510. NSDictionary *dict = [pboard propertyListForType:SKPasteboardTypeLineStyle];
  511. if ([dict objectForKey:SKLineWellLineWidthKey])
  512. [self takeValueForKey:SKLineWellLineWidthKey from:dict];
  513. if ([dict objectForKey:SKLineWellStyleKey])
  514. [self takeValueForKey:SKLineWellStyleKey from:dict];
  515. [self takeValueForKey:SKLineWellDashPatternKey from:dict];
  516. if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  517. if ([dict objectForKey:SKLineWellStartLineStyleKey])
  518. [self takeValueForKey:SKLineWellStartLineStyleKey from:dict];
  519. if ([dict objectForKey:SKLineWellEndLineStyleKey])
  520. [self takeValueForKey:SKLineWellEndLineStyleKey from:dict];
  521. }
  522. [self sendAction:[self action] to:[self target]];
  523. [self setHighlighted:NO];
  524. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  525. [self setNeedsDisplay:YES];
  526. return dict != nil;
  527. }
  528. #pragma mark Accessibility
  529. - (NSArray *)accessibilityAttributeNames {
  530. static NSArray *attributes = nil;
  531. if (attributes == nil) {
  532. attributes = [[NSArray alloc] initWithObjects:
  533. NSAccessibilityRoleAttribute,
  534. NSAccessibilityRoleDescriptionAttribute,
  535. NSAccessibilityValueAttribute,
  536. NSAccessibilityTitleAttribute,
  537. NSAccessibilityHelpAttribute,
  538. NSAccessibilityFocusedAttribute,
  539. NSAccessibilityParentAttribute,
  540. NSAccessibilityWindowAttribute,
  541. NSAccessibilityTopLevelUIElementAttribute,
  542. NSAccessibilityPositionAttribute,
  543. NSAccessibilitySizeAttribute,
  544. nil];
  545. }
  546. return attributes;
  547. }
  548. - (id)accessibilityAttributeValue:(NSString *)attribute {
  549. if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
  550. return NSAccessibilityCheckBoxRole;
  551. } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
  552. return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, nil);
  553. } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
  554. return [NSNumber numberWithInteger:[self isActive]];
  555. } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
  556. return [NSString stringWithFormat:@"%@ %ld", NSLocalizedString(@"line width", @"Accessibility description"), (long)[self lineWidth]];
  557. } else if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) {
  558. return [self toolTip];
  559. } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  560. // Just check if the app thinks we're focused.
  561. id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
  562. return [NSNumber numberWithBool:[focusedElement isEqual:self]];
  563. } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
  564. return NSAccessibilityUnignoredAncestor([self superview]);
  565. } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
  566. // We're in the same window as our parent.
  567. return [NSAccessibilityUnignoredAncestor([self superview]) accessibilityAttributeValue:NSAccessibilityWindowAttribute];
  568. } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
  569. // We're in the same top level element as our parent.
  570. return [NSAccessibilityUnignoredAncestor([self superview]) accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
  571. } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
  572. return [NSValue valueWithPoint:[[self window] convertBaseToScreen:[self convertPoint:[self bounds].origin toView:nil]]];
  573. } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
  574. return [NSValue valueWithSize:[self convertSize:[self bounds].size toView:nil]];
  575. } else {
  576. return nil;
  577. }
  578. }
  579. - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
  580. if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  581. return [self canActivate];
  582. } else {
  583. return NO;
  584. }
  585. }
  586. - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
  587. if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  588. [[self window] makeFirstResponder:self];
  589. }
  590. }
  591. // actions
  592. - (NSArray *)accessibilityActionNames {
  593. return [NSArray arrayWithObject:NSAccessibilityPressAction];
  594. }
  595. - (NSString *)accessibilityActionDescription:(NSString *)anAction {
  596. return NSAccessibilityActionDescription(anAction);
  597. }
  598. - (void)accessibilityPerformAction:(NSString *)anAction {
  599. if ([anAction isEqualToString:NSAccessibilityPressAction])
  600. [self performClick:self];
  601. }
  602. // misc
  603. - (BOOL)accessibilityIsIgnored {
  604. return NO;
  605. }
  606. - (id)accessibilityHitTest:(NSPoint)point {
  607. return NSAccessibilityUnignoredAncestor(self);
  608. }
  609. - (id)accessibilityFocusedUIElement {
  610. return NSAccessibilityUnignoredAncestor(self);
  611. }
  612. @end