828 lines
25 KiB
Objective-C
828 lines
25 KiB
Objective-C
/*
|
|
* Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
|
|
* Copyright 2020 Anthony Cohn-Richardby <anthonyc@gmx.co.uk>
|
|
*
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
*
|
|
* NetSurf is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* NetSurf is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#import <stdio.h>
|
|
#import <AppKit/AppKit.h>
|
|
#import "PlotView.h"
|
|
#import "BrowserWindowController.h"
|
|
#import "utils/errors.h"
|
|
#import "netsurf/plotters.h"
|
|
#import "netsurf/browser_window.h"
|
|
#import "netsurf/keypress.h"
|
|
#import "utils/nsurl.h"
|
|
#import "utils/utils.h"
|
|
#import "netsurf/content.h"
|
|
#import "utils/nsoption.h"
|
|
#import "utils/messages.h"
|
|
#import "netsurf/content_type.h"
|
|
#import "netsurf/form.h"
|
|
|
|
#define colour_red_component( c ) (((c) >> 0) & 0xFF)
|
|
#define colour_green_component( c ) (((c) >> 8) & 0xFF)
|
|
#define colour_blue_component( c ) (((c) >> 16) & 0xFF)
|
|
#define colour_alpha_component( c ) (((c) >> 24) & 0xFF)
|
|
#define colour_from_rgba( r, g, b, a) ((((colour)(r)) << 0) | \
|
|
(((colour)(g)) << 8) | \
|
|
(((colour)(b)) << 16) | \
|
|
(((colour)(a)) << 24))
|
|
#define colour_from_rgb( r, g, b ) colour_from_rgba( (r), (g), (b), 0xFF )
|
|
|
|
static NSRect cocoa_plot_clip_rect;
|
|
|
|
static NSColor *cocoa_convert_colour( colour clr )
|
|
{
|
|
return [NSColor colorWithDeviceRed: (float)colour_red_component( clr ) / 0xFF
|
|
green: (float)colour_green_component( clr ) / 0xFF
|
|
blue: (float)colour_blue_component( clr ) / 0xFF
|
|
alpha: 1.0];
|
|
}
|
|
|
|
|
|
static void cocoa_plot_path_set_stroke_pattern(NSBezierPath *path, const plot_style_t *pstyle)
|
|
{
|
|
static const CGFloat dashed_pattern[2] = { 5.0, 2.0 };
|
|
static const CGFloat dotted_pattern[2] = { 2.0, 2.0 };
|
|
|
|
switch (pstyle->stroke_type) {
|
|
case PLOT_OP_TYPE_DASH:
|
|
[path setLineDash: dashed_pattern count: 2 phase: 0];
|
|
break;
|
|
|
|
case PLOT_OP_TYPE_DOT:
|
|
[path setLineDash: dotted_pattern count: 2 phase: 0];
|
|
break;
|
|
|
|
default:
|
|
// ignore
|
|
break;
|
|
}
|
|
if (pstyle->stroke_width == 0) {
|
|
[path setLineWidth: 1];
|
|
} else {
|
|
[path setLineWidth: plot_style_fixed_to_double(pstyle->stroke_width)];
|
|
}
|
|
}
|
|
|
|
static void cocoa_plot_render_path(NSBezierPath *path, const plot_style_t *pstyle)
|
|
{
|
|
[NSGraphicsContext saveGraphicsState];
|
|
[NSBezierPath clipRect: cocoa_plot_clip_rect];
|
|
|
|
if (pstyle->fill_type != PLOT_OP_TYPE_NONE) {
|
|
[cocoa_convert_colour( pstyle->fill_colour ) setFill];
|
|
[path fill];
|
|
}
|
|
|
|
if (pstyle->stroke_type != PLOT_OP_TYPE_NONE) {
|
|
cocoa_plot_path_set_stroke_pattern(path,pstyle);
|
|
|
|
[cocoa_convert_colour( pstyle->stroke_colour ) set];
|
|
|
|
[path stroke];
|
|
}
|
|
|
|
[NSGraphicsContext restoreGraphicsState];
|
|
}
|
|
|
|
static nserror plot_clip(const struct redraw_context *ctx, const struct rect *clip) {
|
|
cocoa_plot_clip_rect = NSMakeRect(clip->x0, clip->y0,
|
|
clip->x1 - clip->x0,
|
|
clip->y1 - clip->y0);
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static nserror plot_arc(const struct redraw_context *ctx, const plot_style_t *pstyle, int x, int y, int radius, int angle1, int angle2) {
|
|
NSBezierPath *path = [NSBezierPath bezierPath];
|
|
[path appendBezierPathWithArcWithCenter: NSMakePoint( x, y ) radius: radius
|
|
startAngle: angle1 endAngle: angle2 clockwise: NO];
|
|
|
|
cocoa_plot_render_path(path, pstyle);
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static nserror plot_disc(const struct redraw_context *ctx, const plot_style_t *pstyle, int x, int y, int radius) {
|
|
NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect:
|
|
NSMakeRect( x - radius, y-radius, 2*radius, 2*radius )];
|
|
|
|
cocoa_plot_render_path( path, pstyle );
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static nserror plot_line(const struct redraw_context *ctx, const plot_style_t *pstyle, const struct rect *line) {
|
|
int x0 = line->x0;
|
|
int y0 = line->y0;
|
|
int x1 = line->x1;
|
|
int y1 = line->y1;
|
|
|
|
if (pstyle->stroke_type == PLOT_OP_TYPE_NONE) return NSERROR_OK;
|
|
|
|
[NSGraphicsContext saveGraphicsState];
|
|
[NSBezierPath clipRect: cocoa_plot_clip_rect];
|
|
|
|
NSBezierPath *path = [NSBezierPath bezierPath];
|
|
[path moveToPoint: NSMakePoint( x0, y0 )];
|
|
[path lineToPoint: NSMakePoint( x1, y1 )];
|
|
cocoa_plot_path_set_stroke_pattern( path, pstyle );
|
|
|
|
[cocoa_convert_colour( pstyle->stroke_colour ) set];
|
|
[path stroke];
|
|
|
|
[NSGraphicsContext restoreGraphicsState];
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static nserror plot_rectangle(const struct redraw_context *ctx, const plot_style_t *pstyle, const struct rect *rectangle) {
|
|
NSRect nsrect = NSMakeRect(rectangle->x0, rectangle->y0,
|
|
rectangle->x1 - rectangle->x0,
|
|
rectangle->y1 - rectangle->y0);
|
|
NSBezierPath *path = [NSBezierPath bezierPathWithRect: nsrect];
|
|
cocoa_plot_render_path(path, pstyle);
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static nserror plot_polygon(const struct redraw_context *ctx, const plot_style_t *pstyle, const int *p, unsigned int n) {
|
|
if (n <= 1) return NSERROR_OK;
|
|
|
|
NSBezierPath *path = [NSBezierPath bezierPath];
|
|
[path moveToPoint: NSMakePoint(p[0], p[1])];
|
|
for (unsigned int i = 1; i < n; i++) {
|
|
[path lineToPoint: NSMakePoint(p[2*i], p[2*i+1])];
|
|
}
|
|
[path closePath];
|
|
|
|
cocoa_plot_render_path( path, pstyle );
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static nserror plot_path(const struct redraw_context *ctx, const plot_style_t *pstyle, const float *p, unsigned int n, const float transform[6]) {
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static nserror plot_bitmap(const struct redraw_context *ctx, struct bitmap *bitmap, int x, int y, int width, int height, colour bg, bitmap_flags_t flags) {
|
|
[NSGraphicsContext saveGraphicsState];
|
|
[NSBezierPath clipRect: cocoa_plot_clip_rect];
|
|
|
|
NSBitmapImageRep *bmp = (id)bitmap;
|
|
|
|
NSRect rect = NSMakeRect(x, y, width, height );
|
|
|
|
NSAffineTransform *tf = [GSCurrentContext() GSCurrentCTM];
|
|
int offset = (y + (height / 2));
|
|
[tf translateXBy: 0 yBy: offset];
|
|
[tf scaleXBy: 1.0 yBy: -1.0];
|
|
[tf translateXBy: 0 yBy: -offset];
|
|
[GSCurrentContext() GSSetCTM: tf];
|
|
[bmp drawInRect: rect fromRect: NSMakeRect(0, 0, width, height)
|
|
operation: NSCompositeSourceOver fraction: 1.0 respectFlipped: NO hints: nil];
|
|
|
|
[NSGraphicsContext restoreGraphicsState];
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static NSLayoutManager *cocoa_prepare_layout_manager( const char *bytes, size_t length, const plot_font_style_t *style );
|
|
|
|
extern NSTextStorage *cocoa_text_storage;
|
|
extern NSTextContainer *cocoa_text_container;
|
|
static void gnustep_draw_string( CGFloat x, CGFloat y, const char *bytes, size_t length, const plot_font_style_t *style )
|
|
{
|
|
NSLayoutManager *layout = cocoa_prepare_layout_manager( bytes, length, style );
|
|
if (layout == nil) return;
|
|
NSFont *font = [cocoa_text_storage attribute: NSFontAttributeName atIndex: 0 effectiveRange: NULL];
|
|
|
|
CGFloat baseline = [font defaultLineHeightForFont] * 3.0 / 4.0;
|
|
|
|
NSRange glyphRange = [layout glyphRangeForTextContainer: cocoa_text_container];
|
|
[layout drawGlyphsForGlyphRange: glyphRange atPoint: NSMakePoint( x, y - baseline )];
|
|
}
|
|
|
|
static nserror plot_text(const struct redraw_context *ctx, const plot_font_style_t *fstyle, int x, int y, const char *text, size_t length) {
|
|
[NSGraphicsContext saveGraphicsState];
|
|
[NSBezierPath clipRect: cocoa_plot_clip_rect];
|
|
gnustep_draw_string(x, y, text, length, fstyle);
|
|
|
|
[NSGraphicsContext restoreGraphicsState];
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
static const struct plotter_table gnustep_plotters = {
|
|
.clip = plot_clip,
|
|
.arc = plot_arc,
|
|
.disc = plot_disc,
|
|
.line = plot_line,
|
|
.rectangle = plot_rectangle,
|
|
.polygon = plot_polygon,
|
|
.path = plot_path,
|
|
.bitmap = plot_bitmap,
|
|
.text = plot_text,
|
|
.option_knockout = true
|
|
};
|
|
|
|
@interface PlotView(Private)
|
|
-(NSMenu*)developerOptionsMenu;
|
|
@end
|
|
|
|
@implementation PlotView
|
|
|
|
-(void)awakeFromNib {
|
|
didResize = NO;
|
|
reallyDraw = NO;
|
|
caretRect = NSMakeRect(0, 0, 1, 0);
|
|
}
|
|
|
|
-(BOOL)resignFirstResponder {
|
|
[self removeCaret];
|
|
return [super resignFirstResponder];
|
|
}
|
|
|
|
-(void)setBrowser: (void*)aBrowser {
|
|
browser = aBrowser;
|
|
}
|
|
|
|
-(void)placeCaretAtX: (int)x y: (int)y height: (int)height {
|
|
if (showCaret) {
|
|
[self setNeedsDisplayInRect: caretRect];
|
|
}
|
|
showCaret = YES;
|
|
caretRect.origin.x = x;
|
|
caretRect.origin.y = y;
|
|
caretRect.size.height = height;
|
|
[self setNeedsDisplayInRect: caretRect];
|
|
}
|
|
|
|
-(void)removeCaret {
|
|
showCaret = NO;
|
|
[self setNeedsDisplayInRect: caretRect];
|
|
}
|
|
|
|
/*
|
|
* inLiveRedraw doesn't seem to be implemented so this works around it by only triggering a
|
|
* redraw after a 0.01 sec delay. So if we're in the middle of a resize it won't do the
|
|
* expensive draws.
|
|
*/
|
|
-(void)drawRect: (NSRect)rect {
|
|
NSSize newSize = [[self superview] frame].size;
|
|
BOOL sizeChanged = newSize.width != lastSize.width ||
|
|
newSize.height != lastSize.height;
|
|
if (!reallyDraw && sizeChanged) {
|
|
[NSObject cancelPreviousPerformRequestsWithTarget: self];
|
|
didResize = YES;
|
|
[self performSelector: @selector(reallyTriggerDraw) withObject: nil
|
|
afterDelay: 0.01];
|
|
return;
|
|
}
|
|
struct redraw_context ctx = {
|
|
.interactive = true,
|
|
.background_images = true,
|
|
.plot = &gnustep_plotters
|
|
};
|
|
const struct rect clip = {
|
|
.x0 = NSMinX(rect),
|
|
.y0 = NSMinY(rect),
|
|
.x1 = NSMaxX(rect),
|
|
.y1 = NSMaxY(rect)
|
|
};
|
|
if (didResize) {
|
|
browser_window_schedule_reformat(browser);
|
|
didResize = NO;
|
|
}
|
|
browser_window_redraw(browser, 0, 0, &clip, &ctx);
|
|
if (showCaret && NSIntersectsRect(rect, caretRect)) {
|
|
[[NSColor blackColor] set];
|
|
[NSBezierPath fillRect: caretRect];
|
|
}
|
|
lastSize = newSize;
|
|
}
|
|
|
|
-(void)reallyTriggerDraw {
|
|
reallyDraw = YES;
|
|
[self display];
|
|
reallyDraw = NO;
|
|
}
|
|
|
|
-(BOOL)isFlipped {
|
|
return YES;
|
|
}
|
|
|
|
- (NSPoint) convertMousePoint: (NSEvent *)event {
|
|
NSPoint location = [self convertPoint: [event locationInWindow] fromView: nil];
|
|
return location;
|
|
}
|
|
|
|
- (void) popUpContextMenuForEvent: (NSEvent *) event
|
|
{
|
|
NSMenu *popupMenu = [[NSMenu alloc] initWithTitle: @""];
|
|
NSPoint point = [self convertMousePoint: event];
|
|
|
|
struct browser_window_features cont;
|
|
browser_window_get_features(browser, point.x, point.y, &cont);
|
|
if (cont.object != NULL) {
|
|
id bitmap = (id)content_get_bitmap( cont.object );
|
|
const char *cstr = nsurl_access(hlcache_handle_get_url( cont.object ));
|
|
NSString *imageURL = [NSString stringWithUTF8String: cstr];
|
|
|
|
[[popupMenu addItemWithTitle: @"Open image in new tab"
|
|
action: @selector(cmOpenURLInTab:)
|
|
keyEquivalent: @""] setRepresentedObject: imageURL];
|
|
|
|
[[popupMenu addItemWithTitle: @"Open image in new window"
|
|
action: @selector(cmOpenURLInWindow:)
|
|
keyEquivalent: @""] setRepresentedObject: imageURL];
|
|
[[popupMenu addItemWithTitle: @"Save image as"
|
|
action: @selector(cmDownloadURL:)
|
|
keyEquivalent: @""] setRepresentedObject: imageURL];
|
|
[[popupMenu addItemWithTitle: @"Copy image"
|
|
action: @selector(cmImageCopy:)
|
|
keyEquivalent: @""] setRepresentedObject: bitmap];
|
|
if (cont.link != NULL) {
|
|
[popupMenu addItem: [NSMenuItem separatorItem]];
|
|
}
|
|
}
|
|
if (cont.link != NULL) {
|
|
NSString *target = [NSString stringWithUTF8String: nsurl_access(cont.link)];
|
|
|
|
[[popupMenu addItemWithTitle: @"Open link in new tab"
|
|
action: @selector(cmOpenURLInTab:)
|
|
keyEquivalent: @""] setRepresentedObject: target];
|
|
[[popupMenu addItemWithTitle: @"Open link in new window"
|
|
action: @selector(cmOpenURLInWindow:)
|
|
keyEquivalent: @""] setRepresentedObject: target];
|
|
[[popupMenu addItemWithTitle: @"Save link target"
|
|
action: @selector(cmDownloadURL:)
|
|
keyEquivalent: @""] setRepresentedObject: target];
|
|
[[popupMenu addItemWithTitle: @"Copy link"
|
|
action: @selector(cmLinkCopy:)
|
|
keyEquivalent: @""] setRepresentedObject: target];
|
|
}
|
|
if (cont.link == NULL && cont.object == NULL) {
|
|
[popupMenu addItemWithTitle: @"Back"
|
|
action: @selector(back:) keyEquivalent: @""];
|
|
[popupMenu addItemWithTitle: @"Forward"
|
|
action: @selector(forward:) keyEquivalent: @""];
|
|
[popupMenu addItemWithTitle: @"Stop"
|
|
action: @selector(stopReloading:) keyEquivalent: @""];
|
|
[popupMenu addItemWithTitle: @"Reload"
|
|
action: @selector(reload:) keyEquivalent: @""];
|
|
[popupMenu addItem: [NSMenuItem separatorItem]];
|
|
char *selection = browser_window_get_selection(browser);
|
|
if (selection != NULL) {
|
|
if (cont.form_features == CTX_FORM_TEXT) {
|
|
[popupMenu addItemWithTitle: @"Cut"
|
|
action: @selector(cut:) keyEquivalent: @""];
|
|
}
|
|
[popupMenu addItemWithTitle: @"Copy"
|
|
action: @selector(copy:) keyEquivalent: @""];
|
|
free(selection);
|
|
}
|
|
[popupMenu addItemWithTitle: @"Paste"
|
|
action: @selector(paste:) keyEquivalent: @""];
|
|
[popupMenu addItem: [NSMenuItem separatorItem]];
|
|
id<NSMenuItem> item = [popupMenu addItemWithTitle: @"Developer"
|
|
action: nil keyEquivalent: @""];
|
|
[popupMenu setSubmenu: [self developerOptionsMenu] forItem: item];
|
|
}
|
|
[NSMenu popUpContextMenu: popupMenu withEvent: event forView: self];
|
|
|
|
[popupMenu release];
|
|
}
|
|
|
|
-(NSMenu*)developerOptionsMenu {
|
|
NSMenu *menu = [[NSMenu alloc] initWithTitle: @""];
|
|
[menu autorelease];
|
|
[menu addItemWithTitle: @"Page Source" action: @selector(pageSource:)
|
|
keyEquivalent: @""];
|
|
[menu addItemWithTitle: @"Toggle Debug Rendering" action:
|
|
@selector(debugRendering:) keyEquivalent: @""];
|
|
[menu addItemWithTitle: @"Debug Box Tree" action: @selector(debugBoxTree:)
|
|
keyEquivalent: @""];
|
|
[menu addItemWithTitle: @"Debug DOM Tree" action: @selector(debugDomTree:)
|
|
keyEquivalent: @""];
|
|
|
|
return menu;
|
|
}
|
|
|
|
static browser_mouse_state cocoa_mouse_flags_for_event( NSEvent *evt ) {
|
|
browser_mouse_state result = 0;
|
|
NSUInteger flags = [evt modifierFlags];
|
|
|
|
if (flags & NSShiftKeyMask) result |= BROWSER_MOUSE_MOD_1;
|
|
if (flags & NSAlternateKeyMask) result |= BROWSER_MOUSE_MOD_2;
|
|
|
|
return result;
|
|
}
|
|
|
|
-(void)scrollWheel: (NSEvent*)theEvent {
|
|
NSPoint loc = [self convertMousePoint: theEvent];
|
|
int scroll = (int)[theEvent deltaY] * -25; //todo:- linescroll
|
|
if (!browser_window_scroll_at_point(browser, loc.x, loc.y, 0, scroll)) {
|
|
[[self nextResponder] scrollWheel: theEvent];
|
|
}
|
|
}
|
|
|
|
- (void) mouseDown: (NSEvent *)theEvent {
|
|
if ([theEvent modifierFlags] & NSControlKeyMask) {
|
|
[self popUpContextMenuForEvent: theEvent];
|
|
return;
|
|
}
|
|
dragStart = [self convertMousePoint: theEvent];
|
|
browser_window_mouse_click(browser,
|
|
BROWSER_MOUSE_PRESS_1 | cocoa_mouse_flags_for_event( theEvent ),
|
|
dragStart.x,
|
|
dragStart.y );
|
|
}
|
|
|
|
- (void) rightMouseDown: (NSEvent *)theEvent {
|
|
[self popUpContextMenuForEvent: theEvent];
|
|
}
|
|
|
|
- (void) mouseUp: (NSEvent *)theEvent {
|
|
NSPoint location = [self convertMousePoint: theEvent];
|
|
browser_mouse_state modifierFlags = cocoa_mouse_flags_for_event(theEvent);
|
|
if (isDragging) {
|
|
isDragging = NO;
|
|
browser_window_mouse_track(browser, (browser_mouse_state)0, location.x,
|
|
location.y);
|
|
} else {
|
|
modifierFlags |= BROWSER_MOUSE_CLICK_1;
|
|
if ([theEvent clickCount] == 2) modifierFlags |= BROWSER_MOUSE_DOUBLE_CLICK;
|
|
browser_window_mouse_click(browser, modifierFlags, location.x, location.y);
|
|
}
|
|
}
|
|
|
|
|
|
#define squared(x) ((x)*(x))
|
|
#define MinDragDistance (5.0)
|
|
|
|
- (void) mouseDragged: (NSEvent *)theEvent {
|
|
NSPoint location = [self convertMousePoint: theEvent];
|
|
browser_mouse_state modifierFlags = cocoa_mouse_flags_for_event( theEvent );
|
|
|
|
if (!isDragging) {
|
|
const CGFloat distance = squared( dragStart.x - location.x ) +
|
|
squared( dragStart.y - location.y );
|
|
|
|
if (distance >= squared( MinDragDistance)) {
|
|
isDragging = YES;
|
|
browser_window_mouse_click(browser, BROWSER_MOUSE_DRAG_1 |
|
|
modifierFlags, dragStart.x, dragStart.y);
|
|
}
|
|
}
|
|
if (isDragging) {
|
|
browser_window_mouse_track(browser, BROWSER_MOUSE_HOLDING_1 |
|
|
BROWSER_MOUSE_DRAG_ON | modifierFlags, location.x, location.y );
|
|
}
|
|
}
|
|
|
|
- (void) mouseMoved: (NSEvent *)theEvent {
|
|
NSPoint location = [self convertMousePoint: theEvent];
|
|
browser_window_mouse_track(browser, cocoa_mouse_flags_for_event(theEvent),
|
|
location.x, location.y);
|
|
}
|
|
|
|
- (void) mouseExited: (NSEvent *) theEvent {
|
|
[[NSCursor arrowCursor] set];
|
|
}
|
|
|
|
- (void) keyDown: (NSEvent *)theEvent {
|
|
[self interpretKeyEvents: [NSArray arrayWithObject: theEvent]];
|
|
}
|
|
|
|
|
|
- (void) insertText: (id)string {
|
|
for (NSUInteger i = 0, length = [string length]; i < length; i++) {
|
|
unichar ch = [string characterAtIndex: i];
|
|
if (!browser_window_key_press( browser, ch )) {
|
|
if (ch == ' ') [self scrollPageDown: self];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) moveLeft: (id)sender {
|
|
if (browser_window_key_press( browser, NS_KEY_LEFT )) return;
|
|
[self scrollHorizontal: -[[self enclosingScrollView] horizontalLineScroll]];
|
|
}
|
|
|
|
- (void) moveRight: (id)sender {
|
|
if (browser_window_key_press( browser, NS_KEY_RIGHT )) return;
|
|
[self scrollHorizontal: [[self enclosingScrollView] horizontalLineScroll]];
|
|
}
|
|
|
|
- (void) moveUp: (id)sender {
|
|
if (browser_window_key_press( browser, NS_KEY_UP )) return;
|
|
[self scrollVertical: -([[self enclosingScrollView] lineScroll])];
|
|
}
|
|
|
|
- (void) moveDown: (id)sender {
|
|
if (browser_window_key_press( browser, NS_KEY_DOWN )) return;
|
|
[self scrollVertical: [[self enclosingScrollView] lineScroll]];
|
|
}
|
|
|
|
- (void) deleteBackward: (id)sender {
|
|
if (!browser_window_key_press( browser, NS_KEY_DELETE_LEFT )) {
|
|
[NSApp sendAction: @selector( goBack: ) to: nil from: self];
|
|
}
|
|
}
|
|
|
|
- (void) deleteForward: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_DELETE_RIGHT );
|
|
}
|
|
|
|
- (void) cancelOperation: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_ESCAPE );
|
|
}
|
|
|
|
- (CGFloat) pageScroll {
|
|
return NSHeight( [[self superview] frame] ) - [[self enclosingScrollView] pageScroll];
|
|
}
|
|
|
|
- (void) scrollPageUp: (id)sender {
|
|
if (browser_window_key_press( browser, NS_KEY_PAGE_UP )) {
|
|
return;
|
|
}
|
|
[self scrollVertical: -[self pageScroll]];
|
|
}
|
|
|
|
- (void) scrollPageDown: (id)sender {
|
|
if (browser_window_key_press( browser, NS_KEY_PAGE_DOWN )) {
|
|
return;
|
|
}
|
|
[self scrollVertical: [self pageScroll]];
|
|
}
|
|
|
|
- (void) insertTab: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_TAB );
|
|
}
|
|
|
|
- (void) insertBacktab: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_SHIFT_TAB );
|
|
}
|
|
|
|
- (void) moveToBeginningOfLine: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_LINE_START );
|
|
}
|
|
|
|
- (void) moveToEndOfLine: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_LINE_END );
|
|
}
|
|
|
|
- (void) moveToBeginningOfDocument: (id)sender {
|
|
if (browser_window_key_press( browser, NS_KEY_TEXT_START )) return;
|
|
}
|
|
|
|
- (void) scrollToBeginningOfDocument: (id) sender {
|
|
NSPoint origin = [self visibleRect].origin;
|
|
origin.y = 0;
|
|
[self scrollPoint: origin];
|
|
}
|
|
|
|
- (void) moveToEndOfDocument: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_TEXT_END );
|
|
}
|
|
|
|
- (void) scrollToEndOfDocument: (id) sender {
|
|
NSPoint origin = [self visibleRect].origin;
|
|
origin.y = NSHeight( [self frame] );
|
|
[self scrollPoint: origin];
|
|
}
|
|
|
|
- (void) insertNewline: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_NL );
|
|
}
|
|
|
|
- (void) selectAll: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_SELECT_ALL );
|
|
}
|
|
|
|
- (void) copy: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_COPY_SELECTION );
|
|
}
|
|
|
|
- (void) cut: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_CUT_SELECTION );
|
|
}
|
|
|
|
- (void) paste: (id)sender {
|
|
browser_window_key_press( browser, NS_KEY_PASTE );
|
|
}
|
|
|
|
- (BOOL) acceptsFirstResponder {
|
|
return YES;
|
|
}
|
|
|
|
- (void) adjustFrame {
|
|
if (browser)
|
|
browser_window_schedule_reformat(browser);
|
|
}
|
|
|
|
|
|
- (void) scrollHorizontal: (CGFloat) amount {
|
|
NSPoint currentPoint = [self visibleRect].origin;
|
|
currentPoint.x += amount;
|
|
[self scrollPoint: currentPoint];
|
|
}
|
|
|
|
- (void) scrollVertical: (CGFloat) amount {
|
|
NSPoint currentPoint = [self visibleRect].origin;
|
|
currentPoint.y += amount;
|
|
[self scrollPoint: currentPoint];
|
|
}
|
|
|
|
-(void)back: (id)sender {
|
|
if (browser_window_history_back_available(browser)) {
|
|
browser_window_history_back(browser, false);
|
|
}
|
|
}
|
|
-(void)forward: (id)sender {
|
|
if (browser_window_history_forward_available(browser)) {
|
|
browser_window_history_forward(browser, false);
|
|
}
|
|
}
|
|
-(void)stopReloading: (id)sender {
|
|
if (browser_window_stop_available(browser)) {
|
|
browser_window_stop(browser);
|
|
}
|
|
}
|
|
-(void)reload: (id)sender {
|
|
if (browser_window_reload_available(browser)) {
|
|
browser_window_reload(browser, true);
|
|
}
|
|
}
|
|
|
|
|
|
- (void) cmOpenURLInTab: (id)sender {
|
|
[[[self window] windowController] newTab: [sender representedObject]];
|
|
}
|
|
|
|
- (void) cmOpenURLInWindow: (id)sender {
|
|
struct nsurl *url;
|
|
nserror error;
|
|
|
|
error = nsurl_create([[sender representedObject] UTF8String], &url);
|
|
if (error == NSERROR_OK) {
|
|
error = browser_window_create(BW_CREATE_HISTORY | BW_CREATE_CLONE,
|
|
url, NULL, browser, NULL);
|
|
nsurl_unref(url);
|
|
}
|
|
}
|
|
|
|
- (void) cmDownloadURL: (id)sender {
|
|
struct nsurl *url;
|
|
|
|
if (nsurl_create([[sender representedObject] UTF8String], &url) == NSERROR_OK) {
|
|
browser_window_navigate(browser, url, NULL, BW_NAVIGATE_DOWNLOAD, NULL,
|
|
NULL, NULL);
|
|
nsurl_unref(url);
|
|
}
|
|
}
|
|
|
|
- (void) cmImageCopy: (id)sender {
|
|
NSPasteboard *pb = [NSPasteboard generalPasteboard];
|
|
[pb declareTypes: [NSArray arrayWithObject: NSTIFFPboardType] owner: nil];
|
|
[pb setData: [[sender representedObject] TIFFRepresentation]
|
|
forType: NSTIFFPboardType];
|
|
}
|
|
|
|
- (void) cmLinkCopy: (id)sender {
|
|
NSPasteboard *pb = [NSPasteboard generalPasteboard];
|
|
[pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil];
|
|
[pb setString: [sender representedObject] forType: NSStringPboardType];
|
|
}
|
|
|
|
|
|
-(void)debugRendering: (id)sender {
|
|
NSLog(@"Toggle debug rendering");
|
|
browser_window_debug(browser, CONTENT_DEBUG_RENDER);
|
|
[self setNeedsDisplay: YES];
|
|
}
|
|
|
|
-(void)openDump: (NSString*)path {
|
|
NSLog(@"open result at %@", path);
|
|
[[NSApp delegate] openDeveloperFileAtPath: path];
|
|
}
|
|
|
|
-(NSString*)dumpContentType: (enum content_debug)type {
|
|
char fname[100];
|
|
char *tmp = tmpnam(NULL);
|
|
if (tmp == NULL) {
|
|
NSLog(@"tmpnam error");
|
|
return nil;
|
|
}
|
|
snprintf(fname, 100, "%s.txt", tmp);
|
|
FILE *f = fopen(fname, "w+");
|
|
if (f == NULL) {
|
|
NSLog(@"fopen error");
|
|
return nil;
|
|
}
|
|
nserror err = browser_window_debug_dump(browser, f, type);
|
|
if (err != NSERROR_OK) {
|
|
NSLog(@"browser_window_debug_dump error");
|
|
}
|
|
fclose(f);
|
|
return [NSString stringWithCString: fname];
|
|
}
|
|
|
|
-(void)debugBoxTree: (id)sender {
|
|
NSLog(@"debug box tree");
|
|
[self openDump: [self dumpContentType: CONTENT_DEBUG_RENDER]];
|
|
|
|
}
|
|
|
|
-(void)debugDomTree: (id)sender {
|
|
NSLog(@"debug box tree");
|
|
[self openDump: [self dumpContentType: CONTENT_DEBUG_DOM]];
|
|
}
|
|
|
|
-(void)pageSource: (id)sender {
|
|
NSLog(@"Show page source");
|
|
struct hlcache_handle *hlcontent = browser_window_get_content(browser);
|
|
if (hlcontent == NULL) {
|
|
NSLog(@"get_content error");
|
|
return;
|
|
}
|
|
if (content_get_type(hlcontent) != CONTENT_HTML) {
|
|
NSLog(@"content type not html");
|
|
return;
|
|
}
|
|
size_t sz;
|
|
const uint8_t *data = content_get_source_data(hlcontent, &sz);
|
|
if (data == NULL) {
|
|
NSLog(@"get source data failed");
|
|
return;
|
|
}
|
|
char *tmp = tmpnam(NULL);
|
|
if (tmp == NULL) {
|
|
NSLog(@"tmpnam error");
|
|
return;
|
|
}
|
|
char fname[100];
|
|
snprintf(fname, 100, "%s.txt", tmp);
|
|
FILE *f = fopen(fname, "w+");
|
|
if (f == NULL) {
|
|
NSLog(@"fopen error");
|
|
return;
|
|
}
|
|
size_t written;
|
|
if ((written = fwrite(data, sz, 1, f)) != 1) {
|
|
NSLog(@"fwrite error");
|
|
}
|
|
fclose(f);
|
|
[self openDump: [NSString stringWithCString: fname]];
|
|
}
|
|
|
|
-(void)showDropdownMenuWithOptions: (NSArray*)options atLocation: (NSPoint)location control: (struct form_control*)control {
|
|
NSMenu *popupMenu = [[NSMenu alloc] initWithTitle: @""];
|
|
NSMenuItem *opt;
|
|
for (NSInteger i = 0; i < [options count]; i++) {
|
|
opt = [popupMenu addItemWithTitle: [options objectAtIndex: i] action:
|
|
@selector(didPickDropdownOption:) keyEquivalent: @""];
|
|
[opt setTag: i];
|
|
[opt setRepresentedObject: [NSValue valueWithPointer: control]];
|
|
}
|
|
[NSMenu popUpContextMenu: popupMenu withEvent: nil forView: self];
|
|
[popupMenu release];
|
|
}
|
|
|
|
-(void)didPickDropdownOption: (id)sender {
|
|
NSValue *controlPointer = [sender representedObject];
|
|
struct form_control *control = (struct form_control*)[controlPointer pointerValue];
|
|
if (form_select_process_selection(control, [sender tag]) != NSERROR_OK)
|
|
NSLog(@"Failed to process selection");
|
|
}
|
|
|
|
-(void)resetZoom: (id)sender {
|
|
browser_window_set_scale(browser, 1.0, true);
|
|
}
|
|
|
|
-(void)zoomIn: (id)sender {
|
|
browser_window_set_scale(browser, 0.05, false);
|
|
}
|
|
|
|
-(void)zoomOut: (id)sender {
|
|
browser_window_set_scale(browser, -0.05, false);
|
|
}
|
|
|
|
@end
|