CDSDrawView.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. //
  2. // CDSDrawView.m
  3. // PDFViewer-Mac
  4. //
  5. // Copyright © 2014-2023 Cisdem Inc. All Rights Reserved.
  6. //
  7. // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
  8. // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
  9. // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
  10. // This notice may not be removed from this file.
  11. //
  12. #import "CDSDrawView.h"
  13. static NSInteger _index;
  14. static CGPoint _points[5];
  15. @interface CDSDrawView ()
  16. @property (nonatomic,assign) BOOL cursorIsHidden;
  17. @property (nonatomic,assign) BOOL mouseIsInView;
  18. @property (nonatomic,retain) NSTouch *activeTouch;
  19. @property (nonatomic,retain) NSBezierPath *bezierPath;
  20. @property (nonatomic,assign) BOOL isInView;
  21. @end
  22. @implementation CDSDrawView
  23. - (void)dealloc {
  24. }
  25. - (id)initWithFrame:(NSRect)frameRect {
  26. if (self = [super initWithFrame:frameRect]) {
  27. self.drawImage = [[NSImage alloc] initWithSize:self.frame.size];
  28. self.drawColor = [NSColor colorWithDeviceRed:0 green:0 blue:0 alpha:1];
  29. self.strokeRadius = 0.3;
  30. _bezierPath = [[NSBezierPath alloc] init];
  31. self.wantsLayer = YES;
  32. self.layer.borderWidth = 1.0;
  33. self.layer.borderColor =[NSColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.05].CGColor;
  34. }
  35. return self;
  36. }
  37. - (id)initWithCoder:(NSCoder *)coder {
  38. if (self = [super initWithCoder:coder]) {
  39. self.drawImage = [[NSImage alloc] initWithSize:self.frame.size];
  40. self.drawColor = [NSColor colorWithDeviceRed:0 green:0 blue:0 alpha:1];
  41. self.strokeRadius = 0.3;
  42. _bezierPath = [[NSBezierPath alloc] init];
  43. self.wantsLayer = YES;
  44. self.layer.borderWidth = 1.0;
  45. self.layer.borderColor =[NSColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.05].CGColor;
  46. }
  47. return self;
  48. }
  49. - (NSBezierPath *)drawBezierPath {
  50. _drawBezierPath = [_bezierPath copy];
  51. CGRect rect = self.bezierPath.bounds;
  52. NSAffineTransform *transform = [NSAffineTransform transform];
  53. [transform translateXBy:- rect.origin.x + +self.bezierPath.lineWidth/2.0 yBy:-rect.origin.y + self.bezierPath.lineWidth/2.0];
  54. [_drawBezierPath transformUsingAffineTransform:transform];
  55. return _drawBezierPath;
  56. }
  57. /*
  58. ** - (BOOL) acceptsFirstResponder
  59. **
  60. ** Make sure the view will receive
  61. ** events.
  62. **
  63. ** Input: none
  64. **
  65. ** Output: YES to accept, NO to reject
  66. */
  67. - (BOOL)acceptsFirstResponder {
  68. return YES;
  69. }
  70. - (void)setIsAcceptsTouch:(BOOL)isAcceptsTouch {
  71. _isAcceptsTouch = isAcceptsTouch;
  72. // Accept trackpad events
  73. [self setAcceptsTouchEvents:isAcceptsTouch];
  74. size_t screenHeight = CGDisplayPixelsHigh(CGMainDisplayID());
  75. NSRect frameToWindow = [self convertRect:self.bounds toView:nil];
  76. NSRect frameToScreen = [self.window convertRectToScreen:frameToWindow];
  77. if (isAcceptsTouch) {
  78. // If the mouse cursor is not already hidden,
  79. if (!self.cursorIsHidden) {
  80. frameToScreen.origin.x = frameToScreen.origin.x+5;
  81. frameToScreen.origin.y = screenHeight-frameToScreen.origin.y-5;
  82. CGWarpMouseCursorPosition(frameToScreen.origin);
  83. // Detach the mouse cursor from the mouse
  84. // hardware so that moving the mouse (or a
  85. // single finger) will not move the cursor
  86. CGAssociateMouseAndMouseCursorPosition(false);
  87. // Hide the mouse cursor
  88. [NSCursor hide];
  89. // Remember that we detached and hid the
  90. // mouse cursor
  91. self.cursorIsHidden = YES;
  92. }
  93. } else {
  94. frameToScreen.origin.y = screenHeight-frameToScreen.origin.y;
  95. CGWarpMouseCursorPosition(frameToScreen.origin);
  96. // Attach the mouse cursor to the mouse
  97. // hardware so that moving the mouse (or a
  98. // single finger) will move the cursor
  99. CGAssociateMouseAndMouseCursorPosition(true);
  100. // Show the mouse cursor
  101. [NSCursor unhide];
  102. // Remember that we attached and unhid the
  103. // mouse cursor so that the next touch that
  104. // begins will detach and hide it
  105. self.cursorIsHidden = NO;
  106. }
  107. }
  108. - (void)clearImage {
  109. self.drawImage = nil;
  110. [self.bezierPath removeAllPoints];
  111. [self setNeedsDisplay:YES];
  112. }
  113. - (void)setDrawColor:(NSColor *)drawColor {
  114. if (_drawColor != drawColor) {
  115. _drawColor = drawColor;
  116. }
  117. [self setNeedsDisplay:YES];
  118. }
  119. - (void)setStrokeRadius:(float)strokeRadius {
  120. _strokeRadius = strokeRadius;
  121. [self setNeedsDisplay:YES];
  122. }
  123. - (NSImage *)signatureImage {
  124. CGRect rect = CGRectZero;
  125. if (self.bezierPath.empty) {
  126. return nil;
  127. } else {
  128. rect = self.bezierPath.bounds;
  129. }
  130. CGSize size = CGSizeMake(rect.size.width+self.bezierPath.lineWidth,
  131. rect.size.height+self.bezierPath.lineWidth);
  132. NSImage *image = [[NSImage alloc] initWithSize:size];
  133. [image lockFocus];
  134. [self.drawColor set];
  135. [self.drawBezierPath setLineWidth:self.strokeRadius * 2];
  136. [self.drawBezierPath setLineCapStyle:kCGLineCapRound];
  137. [self.drawBezierPath setLineJoinStyle:kCGLineJoinRound];
  138. [self.drawBezierPath stroke];
  139. [image unlockFocus];
  140. return image;
  141. }
  142. #pragma mark Draw
  143. - (void)drawRect:(NSRect)dirtyRect {
  144. [super drawRect:dirtyRect];
  145. [NSGraphicsContext saveGraphicsState];
  146. [[NSColor clearColor] set];
  147. if (([[self window] firstResponder] == self) && self.mouseIsInView) {
  148. NSSetFocusRingStyle(NSFocusRingAbove);
  149. }
  150. [[NSBezierPath bezierPathWithRect:[self bounds]] fill];
  151. [NSGraphicsContext restoreGraphicsState];
  152. if (self.drawImage) {
  153. CGRect imageFrame = [self imageFrameInRect:self.bounds];
  154. [self.drawImage drawInRect:imageFrame];
  155. }
  156. [self.drawColor set];
  157. [self.bezierPath setLineWidth:self.strokeRadius * 2];
  158. [self.bezierPath setLineCapStyle:kCGLineCapRound];
  159. [self.bezierPath setLineJoinStyle:kCGLineJoinRound];
  160. [self.bezierPath stroke];
  161. }
  162. - (CGRect)imageFrameInRect:(CGRect)rect {
  163. CGRect imageRect;
  164. if (self.drawImage.size.width < rect.size.width &&
  165. self.drawImage.size.height < rect.size.height) {
  166. imageRect.origin.x = (rect.size.width-self.drawImage.size.width)/2.0;
  167. imageRect.origin.y = (rect.size.height-self.drawImage.size.height)/2.0;
  168. imageRect.size = self.drawImage.size;
  169. } else {
  170. if (self.drawImage.size.width/self.drawImage.size.height >
  171. rect.size.width/rect.size.height) {
  172. imageRect.size.width = rect.size.width;
  173. imageRect.size.height = rect.size.width*self.drawImage.size.height/self.drawImage.size.width;
  174. } else {
  175. imageRect.size.height = rect.size.height;
  176. imageRect.size.width = rect.size.height*self.drawImage.size.width/self.drawImage.size.height;
  177. }
  178. imageRect.origin.x = (rect.size.width-imageRect.size.width)/2.0;
  179. imageRect.origin.y = (rect.size.height-imageRect.size.height)/2.0;
  180. }
  181. return imageRect;
  182. }
  183. #pragma mark Touch
  184. - (void)touchesBeganWithEvent:(NSEvent *)event {
  185. [super touchesBeganWithEvent:event];
  186. NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:self];
  187. self.activeTouch = [touches anyObject];
  188. CGPoint point = [self.activeTouch normalizedPosition];
  189. point.x = point.x * self.bounds.size.width;
  190. point.y = point.y * self.bounds.size.height;
  191. _index = 0;
  192. _points[0] = point;
  193. [self setNeedsDisplay:YES];
  194. if (!self.cursorIsHidden) {
  195. CGAssociateMouseAndMouseCursorPosition(false);
  196. [NSCursor hide];
  197. self.cursorIsHidden = YES;
  198. }
  199. }
  200. - (void)touchesMovedWithEvent:(NSEvent *)event {
  201. [super touchesMovedWithEvent:event];
  202. NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:self];
  203. BOOL isTouch = NO;
  204. for (NSTouch *touch in touches) {
  205. if (touch.identity == self.activeTouch.identity) {
  206. isTouch = YES;
  207. self.activeTouch = touch;
  208. }
  209. }
  210. if (!isTouch) {
  211. return;
  212. }
  213. NSPoint point = [self.activeTouch normalizedPosition];
  214. point.x = point.x * self.bounds.size.width;
  215. point.y = point.y * self.bounds.size.height;
  216. _index++;
  217. _points[_index] = point;
  218. if (_index == 4) {
  219. _points[3] = CGPointMake((_points[2].x + _points[4].x)/2.0,
  220. (_points[2].y + _points[4].y)/2.0);
  221. [self.bezierPath moveToPoint:_points[0]];
  222. [self.bezierPath curveToPoint:_points[3]
  223. controlPoint1:_points[1]
  224. controlPoint2:_points[2] ];
  225. _points[0] = _points[3];
  226. _points[1] = _points[4];
  227. _index = 1;
  228. [self setNeedsDisplay:YES];
  229. }
  230. }
  231. - (void)touchesEndedWithEvent:(NSEvent *)event {
  232. [super touchesEndedWithEvent:event];
  233. NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:self];
  234. for (NSTouch *touch in touches) {
  235. if (touch.identity == self.activeTouch.identity) {
  236. self.activeTouch = nil;
  237. }
  238. }
  239. if (_index < 4) {
  240. for (int i=0; i<_index; i++) {
  241. [self.bezierPath moveToPoint:_points[i]];
  242. }
  243. [self setNeedsDisplay:YES];
  244. }
  245. }
  246. - (void)touchesCancelledWithEvent:(NSEvent *)event {
  247. [super touchesCancelledWithEvent:event];
  248. for (int i=0; i<_index; i++) {
  249. [self.bezierPath moveToPoint:_points[i]];
  250. }
  251. self.activeTouch = nil;
  252. [self setNeedsDisplay:YES];
  253. }
  254. #pragma mark - Mouse
  255. - (void)viewDidMoveToWindow {
  256. if ([self window] != nil) {
  257. [self addTrackingRect:[self bounds]
  258. owner:self
  259. userData:NULL
  260. assumeInside:NO];
  261. }
  262. }
  263. - (void)mouseEntered:(NSEvent *)theEvent {
  264. [[self window] makeFirstResponder:self];
  265. self.mouseIsInView = YES;
  266. [self setNeedsDisplay:YES];
  267. }
  268. - (void)mouseExited:(NSEvent *)theEvent {
  269. self.mouseIsInView = NO;
  270. [self setNeedsDisplay:YES];
  271. }
  272. - (void)mouseDown:(NSEvent *)theEvent {
  273. if ([self acceptsTouchEvents]) {
  274. return;
  275. }
  276. CGPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  277. if (CGRectContainsPoint(self.bounds, point)) {
  278. self.isInView = YES;
  279. _index = 0;
  280. _points[0] = point;
  281. }
  282. }
  283. - (void)mouseDragged:(NSEvent *)theEvent {
  284. if ([self acceptsTouchEvents]) {
  285. return;
  286. }
  287. NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  288. if (CGRectContainsPoint(self.bounds, point)) {
  289. _index++;
  290. _points[_index] = point;
  291. if (_index == 4) {
  292. if (!self.isInView) {
  293. _points[3] = CGPointMake((_points[2].x + _points[4].x)/2.0,
  294. (_points[2].y + _points[4].y)/2.0);
  295. [self.bezierPath moveToPoint:_points[0]];
  296. [self.bezierPath curveToPoint:_points[3]
  297. controlPoint1:_points[1]
  298. controlPoint2:_points[2] ];
  299. }
  300. self.isInView = NO;
  301. _points[0] = _points[3];
  302. _points[1] = _points[4];
  303. _index = 1;
  304. [self setNeedsDisplay:YES];
  305. }
  306. } else {
  307. self.isInView = YES;
  308. _points[0] = _points[3];
  309. _points[1] = _points[4];
  310. _index = 1;
  311. [self setNeedsDisplay:YES];
  312. }
  313. }
  314. - (void)mouseUp:(NSEvent *)theEvent {
  315. if ([self acceptsTouchEvents]) {
  316. return;
  317. }
  318. if (_index < 4) {
  319. for (int i=0; i<_index; i++) {
  320. [self.bezierPath moveToPoint:_points[i]];
  321. }
  322. [self setNeedsDisplay:YES];
  323. }
  324. }
  325. - (void)keyDown:(NSEvent *)theEvent {
  326. NSString *chars = [theEvent characters];
  327. unichar character = [chars characterAtIndex: 0];
  328. if (character == 27) {
  329. if (self.isAcceptsTouch) {
  330. self.isAcceptsTouch = NO;
  331. if ([self.delegate respondsToSelector:@selector(drawViewDidFinishTouchMode:)]) {
  332. [self.delegate drawViewDidFinishTouchMode:self];
  333. }
  334. return;
  335. }
  336. }
  337. [super keyDown:theEvent];
  338. }
  339. @end