summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Docs/BUILDING-Cocoa87
-rw-r--r--Makefile13
-rw-r--r--docs/Doxyfile1
-rw-r--r--frontends/cocoa/.clang-format4
-rw-r--r--frontends/cocoa/.gitignore1
-rw-r--r--frontends/cocoa/ArrowBox.h32
-rw-r--r--frontends/cocoa/ArrowBox.m180
-rw-r--r--frontends/cocoa/ArrowWindow.h31
-rw-r--r--frontends/cocoa/ArrowWindow.m240
-rw-r--r--frontends/cocoa/BookmarksController.h39
-rw-r--r--frontends/cocoa/BookmarksController.m221
-rw-r--r--frontends/cocoa/BrowserView.h52
-rw-r--r--frontends/cocoa/BrowserView.m749
-rw-r--r--frontends/cocoa/BrowserViewController.h73
-rw-r--r--frontends/cocoa/BrowserViewController.m357
-rw-r--r--frontends/cocoa/BrowserWindow.h22
-rw-r--r--frontends/cocoa/BrowserWindow.m29
-rw-r--r--frontends/cocoa/BrowserWindowController.h47
-rw-r--r--frontends/cocoa/BrowserWindowController.m262
-rw-r--r--frontends/cocoa/DownloadWindowController.h53
-rw-r--r--frontends/cocoa/DownloadWindowController.m414
-rw-r--r--frontends/cocoa/FormSelectMenu.h29
-rw-r--r--frontends/cocoa/FormSelectMenu.m110
-rw-r--r--frontends/cocoa/HistoryView.h30
-rw-r--r--frontends/cocoa/HistoryView.m136
-rw-r--r--frontends/cocoa/HistoryWindowController.h27
-rw-r--r--frontends/cocoa/HistoryWindowController.m49
-rw-r--r--frontends/cocoa/LocalHistoryController.h37
-rw-r--r--frontends/cocoa/LocalHistoryController.m119
-rw-r--r--frontends/cocoa/Makefile258
-rw-r--r--frontends/cocoa/Makefile.defaults28
-rw-r--r--frontends/cocoa/NetSurf.xcodeproj/project.pbxproj2095
-rw-r--r--frontends/cocoa/NetSurf.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--frontends/cocoa/NetSurfAppDelegate.h38
-rw-r--r--frontends/cocoa/NetSurfAppDelegate.m194
-rw-r--r--frontends/cocoa/NetsurfApp.h29
-rw-r--r--frontends/cocoa/NetsurfApp.m302
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front.pngbin0 -> 292 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Pressed.pngbin0 -> 292 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Rollover.pngbin0 -> 297 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front.pngbin0 -> 307 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Pressed.pngbin0 -> 310 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Rollover.pngbin0 -> 317 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabNew.pngbin0 -> 371 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabNewPressed.pngbin0 -> 380 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/AquaTabNewRollover.pngbin0 -> 380 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/overflowImage.pngbin0 -> 256 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/overflowImagePressed.pngbin0 -> 250 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/Images/pi.pngbin0 -> 564 bytes
-rw-r--r--frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.h22
-rw-r--r--frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.m126
-rw-r--r--frontends/cocoa/PSMTabBarControl/NSString_AITruncation.h12
-rw-r--r--frontends/cocoa/PSMTabBarControl/NSString_AITruncation.m34
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.h27
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.m157
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.h14
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.m43
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMRolloverButton.h28
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMRolloverButton.m180
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarCell.h115
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarCell.m536
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarControl+Private.h49
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarControl.h239
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarControl.m2035
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarController.h38
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabBarController.m648
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.h101
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.m850
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragView.h21
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragView.m60
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.h20
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.m51
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.h33
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.m109
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMTabStyle.h57
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.h29
-rw-r--r--frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.m581
-rw-r--r--frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/TXT.rtf186
-rw-r--r--frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/startpage.gifbin0 -> 11246 bytes
-rw-r--r--frontends/cocoa/PreferencesWindowController.h27
-rw-r--r--frontends/cocoa/PreferencesWindowController.m57
-rw-r--r--frontends/cocoa/Prefix.pch11
-rw-r--r--frontends/cocoa/ScrollableView.h27
-rw-r--r--frontends/cocoa/ScrollableView.m75
-rw-r--r--frontends/cocoa/SearchWindowController.h46
-rw-r--r--frontends/cocoa/SearchWindowController.m118
-rw-r--r--frontends/cocoa/Tree.h52
-rw-r--r--frontends/cocoa/Tree.m143
-rw-r--r--frontends/cocoa/TreeView.h28
-rw-r--r--frontends/cocoa/TreeView.m247
-rw-r--r--frontends/cocoa/URLFieldCell.h27
-rw-r--r--frontends/cocoa/URLFieldCell.m254
-rw-r--r--frontends/cocoa/apple_image.h38
-rw-r--r--frontends/cocoa/apple_image.m257
-rw-r--r--frontends/cocoa/arc.h22
-rw-r--r--frontends/cocoa/bitmap.h28
-rw-r--r--frontends/cocoa/bitmap.m264
-rwxr-xr-xfrontends/cocoa/compile-xib.sh20
-rw-r--r--frontends/cocoa/coordinates.h104
-rw-r--r--frontends/cocoa/desktop-tree.h87
-rw-r--r--frontends/cocoa/desktop-tree.m351
-rwxr-xr-xfrontends/cocoa/extract-strings.sh11
-rw-r--r--frontends/cocoa/fetch.h19
-rw-r--r--frontends/cocoa/fetch.m112
-rw-r--r--frontends/cocoa/font.h28
-rw-r--r--frontends/cocoa/font.m251
-rw-r--r--frontends/cocoa/gui.h35
-rw-r--r--frontends/cocoa/gui.m330
-rw-r--r--frontends/cocoa/plotter.h33
-rw-r--r--frontends/cocoa/plotter.m332
-rw-r--r--frontends/cocoa/res/BookmarksWindow.xib94
-rw-r--r--frontends/cocoa/res/Browser.xib399
-rw-r--r--frontends/cocoa/res/BrowserWindow.xib1395
-rw-r--r--frontends/cocoa/res/DownloadWindow.xib493
-rw-r--r--frontends/cocoa/res/HistoryWindow.xib338
-rw-r--r--frontends/cocoa/res/HomeTemplate.pdf106
l---------frontends/cocoa/res/Icons1
-rw-r--r--frontends/cocoa/res/LocalHistoryPanel.xib54
-rw-r--r--frontends/cocoa/res/MainMenu.xib2369
-rw-r--r--frontends/cocoa/res/NetSurf-Info.plist111
-rw-r--r--frontends/cocoa/res/NetSurf.icnsbin0 -> 203268 bytes
-rw-r--r--frontends/cocoa/res/PreferencesWindow.xib512
-rw-r--r--frontends/cocoa/res/SearchWindow.xib100
l---------frontends/cocoa/res/adblock.css1
l---------frontends/cocoa/res/ca-bundle1
-rw-r--r--frontends/cocoa/res/de.lproj/BookmarksWindow.xib.stringsbin0 -> 366 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/BrowserWindow.xib.stringsbin0 -> 1892 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/DownloadWindow.xib.stringsbin0 -> 536 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/HistoryWindow.xib.stringsbin0 -> 172 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/Localizable.strings78
-rw-r--r--frontends/cocoa/res/de.lproj/MainMenu.xib.stringsbin0 -> 11982 bytes
l---------frontends/cocoa/res/de.lproj/Messages1
-rw-r--r--frontends/cocoa/res/de.lproj/PreferencesWindow.xib.stringsbin0 -> 1206 bytes
-rw-r--r--frontends/cocoa/res/de.lproj/SearchWindow.xib.stringsbin0 -> 1148 bytes
l---------frontends/cocoa/res/default.css1
-rw-r--r--frontends/cocoa/res/en.lproj/Localizable.stringsbin0 -> 3322 bytes
l---------frontends/cocoa/res/en.lproj/Messages1
-rw-r--r--frontends/cocoa/res/fr.lproj/Localizable.stringsbin0 -> 3322 bytes
l---------frontends/cocoa/res/fr.lproj/Messages1
l---------frontends/cocoa/res/internal.css1
-rw-r--r--frontends/cocoa/res/it.lproj/Localizable.stringsbin0 -> 3496 bytes
l---------frontends/cocoa/res/it.lproj/Messages1
l---------frontends/cocoa/res/netsurf.png1
-rw-r--r--frontends/cocoa/res/nl.lproj/Localizable.stringsbin0 -> 3322 bytes
l---------frontends/cocoa/res/nl.lproj/Messages1
l---------frontends/cocoa/res/quirks.css1
-rw-r--r--frontends/cocoa/schedule.h19
-rw-r--r--frontends/cocoa/schedule.m91
-rw-r--r--frontends/cocoa/selection.h19
-rw-r--r--frontends/cocoa/selection.m99
150 files changed, 22518 insertions, 1 deletions
diff --git a/Docs/BUILDING-Cocoa b/Docs/BUILDING-Cocoa
new file mode 100644
index 0000000..d83800d
--- a/dev/null
+++ b/Docs/BUILDING-Cocoa
@@ -0,0 +1,87 @@
+--------------------------------------------------------------------------------
+ Build Instructions for Cocoa NetSurf 13 January 2011
+--------------------------------------------------------------------------------
+
+ This document provides instructions for building the Cocoa version of NetSurf
+ and provides guidance on obtaining NetSurf's build dependencies.
+
+ Cocoa NetSurf has been tested on Mac OS X 10.6 on Intel and on Mac OS X 10.5
+ on ppc.
+
+
+ Building NetSurf
+==================
+
+ After installing the dependencies NetSurf can be built either using the Xcode
+ project file 'cocoa/NetSurf.xcodeproj' or on the command line using the
+ Makefile:
+
+ $ make TARGET=cocoa
+
+ In both cases the actual build process is controlled by the Makefile.
+
+ Obtaining NetSurf's build dependencies
+========================================
+
+ Many of NetSurf's dependencies are packaged on various operating systems.
+ The remainder must be installed manually. Currently, some of the libraries
+ developed as part of the NetSurf project have not had official releases.
+ Hopefully they will soon be released with downloadable tarballs and packaged
+ in common distros. For now, you'll have to make do with Git checkouts.
+
+ Package installation
+----------------------
+
+ For building the other NetSurf libraries and for configuring NetSurf the
+ "pkg-config" tool is required. It can be installed either via fink, macports
+ or homebrew or from source.
+
+ OpenSSL, LibPNG, curl, iconv and zlib are provided by Mac OS X.
+
+ The curl library provided by Mac OS X 10.6 causes a crash while fetching
+ https resources, so you should install version 7.21.4 (or newer) of libcurl
+ if you are running on 10.6.
+
+ LibJPEG and LibMNG can be installed from source or using one of the mentioned
+ package managers.
+
+
+ The NetSurf project's libraries
+---------------------------------
+
+ The NetSurf project has developed several libraries which are required by
+ the browser. These are:
+
+ LibParserUtils -- Parser building utility functions
+ LibWapcaplet -- String internment
+ Hubbub -- HTML5 compliant HTML parser
+ LibCSS -- CSS parser and selection engine
+ LibNSGIF -- GIF format image decoder
+ LibNSBMP -- BMP and ICO format image decoder
+ LibROSprite -- RISC OS Sprite format image decoder
+
+ To fetch each of these libraries, run the appropriate commands from the
+ Docs/LIBRARIES file.
+
+ $ make
+ $ sudo make install
+
+ This command builds the libraries only for the active architecture. To build
+ universal binaries use those commands:
+
+ $ make UNIVERSAL="i386 x86_64 ppc ppc64"
+ $ sudo make install
+
+ If you are building NetSurf for using it on only one computer this is not
+ necessary, but if you want to distribute your binary you should build
+ universal binaries. You can also leave some of the platform names out, if
+ you don't require them.
+
+ | Note: We advise enabling iconv() support in libparserutils, which vastly
+ | increases the number of supported character sets. To do this,
+ | create a file called Makefile.config.override in the libparserutils
+ | directory, containing the following line:
+ |
+ | CFLAGS += -DWITH_ICONV_FILTER
+ |
+ | For more information, consult the libparserutils README file.
diff --git a/Makefile b/Makefile
index 0c2933b..fa1420b 100644
--- a/Makefile
+++ b/Makefile
@@ -84,6 +84,13 @@ ifeq ($(HOST),AmigaOS)
endif
endif
+ifeq ($(HOST),Darwin)
+ HOST := macosx
+ ifeq ($(TARGET),)
+ TARGET := cocoa
+ endif
+endif
+
ifeq ($(HOST),FreeMiNT)
HOST := mint
endif
@@ -109,7 +116,7 @@ ifeq ($(TARGET),)
endif
# valid values for the TARGET
-VLDTARGET := riscos gtk gtk3 beos amiga amigaos3 framebuffer windows atari monkey
+VLDTARGET := riscos gtk gtk3 beos amiga amigaos3 framebuffer windows atari cocoa monkey
# Check for valid TARGET
ifeq ($(filter $(VLDTARGET),$(TARGET)),)
@@ -248,6 +255,9 @@ else
PKG_CONFIG := PKG_CONFIG_LIBDIR="$(GCCSDK_INSTALL_ENV)/lib/pkgconfig" pkg-config
endif
else
+ ifeq ($(TARGET),cocoa)
+ PKG_CONFIG := PKG_CONFIG_PATH="$(PKG_CONFIG_PATH):/usr/local/lib/pkgconfig" pkg-config
+ else
ifeq ($(TARGET),atari)
ifeq ($(HOST),atari)
PKG_CONFIG := pkg-config
@@ -313,6 +323,7 @@ else
endif
endif
endif
+ endif
endif
endif
endif
diff --git a/docs/Doxyfile b/docs/Doxyfile
index f106693..e549981 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -761,6 +761,7 @@ INPUT = docs \
frontends/atari \
frontends/atari/plot \
frontends/beos \
+ frontends/cocoa \
frontends/framebuffer \
frontends/framebuffer/fbtk \
frontends/gtk \
diff --git a/frontends/cocoa/.clang-format b/frontends/cocoa/.clang-format
new file mode 100644
index 0000000..4beec66
--- a/dev/null
+++ b/frontends/cocoa/.clang-format
@@ -0,0 +1,4 @@
+---
+BasedOnStyle: WebKit
+PointerAlignment: Right
+SortIncludes: false
diff --git a/frontends/cocoa/.gitignore b/frontends/cocoa/.gitignore
new file mode 100644
index 0000000..99c6c7d
--- a/dev/null
+++ b/frontends/cocoa/.gitignore
@@ -0,0 +1 @@
+**/*.xc*/xcuserdata/
diff --git a/frontends/cocoa/ArrowBox.h b/frontends/cocoa/ArrowBox.h
new file mode 100644
index 0000000..c3f7a44
--- a/dev/null
+++ b/frontends/cocoa/ArrowBox.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 1011 Sven Weidauer
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#import <Cocoa/Cocoa.h>
+
+typedef enum {
+ ArrowNone,
+ ArrowTopEdge,
+ ArrowBottomEdge,
+ ArrowLeftEdge,
+ ArrowRightEdge
+} ArrowEdge;
+
+@interface ArrowBox : NSView {
+ CGFloat arrowPosition;
+ CGFloat arrowSize;
+ ArrowEdge arrowEdge;
+ CGFloat cornerRadius;
+ BOOL updateShadow;
+}
+
+@property (readwrite, assign, nonatomic) CGFloat arrowPosition;
+@property (readwrite, assign, nonatomic) CGFloat arrowSize;
+@property (readwrite, assign, nonatomic) ArrowEdge arrowEdge;
+@property (readwrite, assign, nonatomic) CGFloat cornerRadius;
+
+@end
diff --git a/frontends/cocoa/ArrowBox.m b/frontends/cocoa/ArrowBox.m
new file mode 100644
index 0000000..6d6911d
--- a/dev/null
+++ b/frontends/cocoa/ArrowBox.m
@@ -0,0 +1,180 @@
+/* Copyright 2011 Sven Weidauer
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "ArrowBox.h"
+
+#import "desktop/system_colour.h"
+#import "cocoa/plotter.h"
+
+@interface ArrowBox ()
+
+@property (nonatomic) NSColor *backgroundColor;
+
+@end
+
+@implementation ArrowBox
+
+@synthesize arrowPosition;
+@synthesize arrowSize;
+@synthesize arrowEdge;
+@synthesize cornerRadius;
+
+- (NSColor *)backgroundColor {
+ if (!_backgroundColor) {
+ colour fill_colour;
+ nserror res = ns_system_colour_char("Window", &fill_colour);
+ NSAssert(res == NSERROR_OK, @"Expect to find the window colour");
+ _backgroundColor = cocoa_convert_colour(fill_colour);
+ }
+
+ return _backgroundColor;
+}
+
+- (void)setArrowEdge:(ArrowEdge)newEdge
+{
+ if (arrowEdge == newEdge) {
+ return;
+ }
+
+ arrowEdge = newEdge;
+
+ [self setNeedsDisplay:YES];
+ updateShadow = YES;
+}
+
+- (void)setArrowSize:(CGFloat)newSize
+{
+ arrowSize = newSize;
+ [self setNeedsDisplay:YES];
+ updateShadow = YES;
+}
+
+- (void)setCornerRadius:(CGFloat)newRadius
+{
+ cornerRadius = newRadius;
+ [self setNeedsDisplay:YES];
+ updateShadow = YES;
+}
+
+- (void)setArrowPosition:(CGFloat)newPosition
+{
+ arrowPosition = newPosition;
+
+ [self setNeedsDisplay:YES];
+ updateShadow = YES;
+}
+
+- (id)initWithFrame:(NSRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self) {
+ arrowPosition = 50;
+ cornerRadius = 10;
+ arrowSize = 15;
+ }
+ return self;
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ [[NSColor clearColor] set];
+ [NSBezierPath fillRect:dirtyRect];
+
+ NSBezierPath *path = [NSBezierPath bezierPath];
+
+ NSRect bounds = [self.window convertRectToScreen:NSInsetRect([self bounds], 2, 2)];
+ bounds.origin.x = floor(bounds.origin.x);
+ bounds.origin.y = floor(bounds.origin.y);
+ bounds.size.width = floor(bounds.size.width);
+ bounds.size.height = floor(bounds.size.height);
+ bounds = [self.window convertRectFromScreen:bounds];
+
+ const CGFloat right = bounds.size.width - arrowSize;
+ const CGFloat top = bounds.size.height - arrowSize;
+ const CGFloat left = arrowSize;
+ const CGFloat bottom = arrowSize;
+
+ [path setLineJoinStyle:NSRoundLineJoinStyle];
+
+ [path moveToPoint:NSMakePoint(right - cornerRadius, top)];
+
+ if (arrowEdge == ArrowTopEdge) {
+ [path lineToPoint:NSMakePoint(arrowPosition + arrowSize, top)];
+ [path lineToPoint:NSMakePoint(arrowPosition, top + arrowSize)];
+ [path lineToPoint:NSMakePoint(arrowPosition - arrowSize, top)];
+ }
+
+ [path appendBezierPathWithArcFromPoint:NSMakePoint(left, top)
+ toPoint:NSMakePoint(left, top - cornerRadius)
+ radius:cornerRadius];
+
+ if (arrowEdge == ArrowLeftEdge) {
+ [path lineToPoint:NSMakePoint(left, bottom + arrowPosition + arrowSize)];
+ [path lineToPoint:NSMakePoint(left - arrowSize, bottom + arrowPosition)];
+ [path lineToPoint:NSMakePoint(left, bottom + arrowPosition - arrowSize)];
+ }
+
+ [path appendBezierPathWithArcFromPoint:NSMakePoint(left, bottom)
+ toPoint:NSMakePoint(left + cornerRadius, bottom)
+ radius:cornerRadius];
+
+ if (arrowEdge == ArrowBottomEdge) {
+ [path lineToPoint:NSMakePoint(arrowPosition - arrowSize, bottom)];
+ [path lineToPoint:NSMakePoint(arrowPosition, bottom - arrowSize)];
+ [path lineToPoint:NSMakePoint(arrowPosition + arrowSize, bottom)];
+ }
+
+ [path appendBezierPathWithArcFromPoint:NSMakePoint(right, bottom)
+ toPoint:NSMakePoint(right, bottom + cornerRadius)
+ radius:cornerRadius];
+
+ if (arrowEdge == ArrowRightEdge) {
+ [path lineToPoint:NSMakePoint(right, bottom + arrowPosition - arrowSize)];
+ [path lineToPoint:NSMakePoint(right + arrowSize, bottom + arrowPosition)];
+ [path lineToPoint:NSMakePoint(right, bottom + arrowPosition + arrowSize)];
+ }
+
+ [path appendBezierPathWithArcFromPoint:NSMakePoint(right, top)
+ toPoint:NSMakePoint(right - cornerRadius, top)
+ radius:cornerRadius];
+ [path closePath];
+
+ [[NSColor colorWithDeviceWhite:1.0 alpha:0.4] set];
+ [self.backgroundColor setFill];
+
+ NSAffineTransform *transform = [NSAffineTransform transform];
+ [transform translateXBy:bounds.origin.x yBy:bounds.origin.y];
+ [transform concat];
+
+ [path setLineWidth:2.0];
+ [path fill];
+ [path stroke];
+
+ if (updateShadow) {
+ [[self window] invalidateShadow];
+ [[self window] update];
+ updateShadow = NO;
+ }
+}
+
+@end
diff --git a/frontends/cocoa/ArrowWindow.h b/frontends/cocoa/ArrowWindow.h
new file mode 100644
index 0000000..94b49fb
--- a/dev/null
+++ b/frontends/cocoa/ArrowWindow.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 1011 Sven Weidauer
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#import <Cocoa/Cocoa.h>
+
+#import "ArrowBox.h"
+
+@interface ArrowWindow : NSWindow {
+ ArrowBox *box;
+ NSView *content;
+ BOOL acceptsKey;
+ NSWindow *attachedWindow;
+}
+
+@property (readwrite, assign, nonatomic) BOOL acceptsKey;
+
+@property (readwrite, assign, nonatomic) CGFloat arrowPosition;
+@property (readwrite, assign, nonatomic) CGFloat arrowSize;
+@property (readwrite, assign, nonatomic) ArrowEdge arrowEdge;
+@property (readwrite, assign, nonatomic) CGFloat cornerRadius;
+
+- (void)moveToPoint:(NSPoint)screenPoint;
+- (void)attachToView:(NSView *)view;
+- (void)detach;
+
+@end
diff --git a/frontends/cocoa/ArrowWindow.m b/frontends/cocoa/ArrowWindow.m
new file mode 100644
index 0000000..720e55e
--- a/dev/null
+++ b/frontends/cocoa/ArrowWindow.m
@@ -0,0 +1,240 @@
+/* Copyright 2011 Sven Weidauer
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#import "ArrowWindow.h"
+#import "ArrowBox.h"
+
+@implementation ArrowWindow
+
+@synthesize acceptsKey;
+
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
+{
+ if ((self = [super initWithContentRect:contentRect styleMask:NSWindowStyleMaskBorderless backing:bufferingType defer:flag]) == nil) {
+ return nil;
+ }
+
+ [self setBackgroundColor:[NSColor clearColor]];
+ [self setOpaque:NO];
+ [self setHasShadow:YES];
+
+ return self;
+}
+
+- (void)setContentView:(NSView *)aView
+{
+ if (aView == content)
+ return;
+
+ [content removeFromSuperview];
+ content = aView;
+
+ if (content == nil)
+ return;
+
+ if (box == nil) {
+ box = [[ArrowBox alloc] initWithFrame:NSZeroRect];
+ [box setArrowEdge:ArrowTopEdge];
+ [super setContentView:box];
+ }
+
+ [box addSubview:content];
+
+ NSRect frame = [self contentRectForFrameRect:[self frame]];
+ frame.origin = [self convertRectFromScreen:(CGRect){.origin = frame.origin }].origin;
+ frame = [box convertRect:frame fromView:nil];
+
+ [content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ [content setFrame:frame];
+}
+
+- (void)setContentSize:(NSSize)aSize
+{
+ NSRect frame = [content frame];
+ frame.size = aSize;
+
+ frame = [box convertRect:frame toView:nil];
+ frame.origin = [self convertRectToScreen:(CGRect){.origin = frame.origin }].origin;
+ frame = [self frameRectForContentRect:frame];
+
+ [self setFrame:frame display:YES];
+}
+
+static const CGFloat padding = 0;
+
+- (NSRect)contentRectForFrameRect:(NSRect)frameRect
+{
+ const CGFloat arrowSize = [box arrowSize];
+ const CGFloat offset = 2 * (padding + arrowSize);
+
+ return NSInsetRect(frameRect, offset, offset);
+}
+
+- (NSRect)frameRectForContentRect:(NSRect)contentRect
+{
+ const CGFloat arrowSize = [box arrowSize];
+ const CGFloat offset = -2 * (padding + arrowSize);
+
+ return NSInsetRect(contentRect, offset, offset);
+}
+
++ (NSRect)frameRectForContentRect:(NSRect)cRect styleMask:(NSWindowStyleMask)aStyle
+{
+ const CGFloat DefaultArrowSize = 15;
+ const CGFloat offset = -2 * (padding + DefaultArrowSize);
+
+ return NSInsetRect(cRect, offset, offset);
+}
+
+- (BOOL)canBecomeKeyWindow
+{
+ return acceptsKey;
+}
+
+- (void)moveToPoint:(NSPoint)screenPoint
+{
+ switch ([box arrowEdge]) {
+ case ArrowNone:
+ screenPoint.x -= [box arrowSize];
+ screenPoint.y += [box arrowSize];
+ break;
+
+ case ArrowTopEdge:
+ screenPoint.x -= [box arrowPosition];
+ break;
+
+ case ArrowBottomEdge:
+ screenPoint.x -= [box arrowPosition];
+ screenPoint.y += NSHeight([self frame]);
+ break;
+
+ case ArrowLeftEdge:
+ screenPoint.y += NSHeight([self frame]) - [box arrowPosition] - [box arrowSize];
+ break;
+
+ case ArrowRightEdge:
+ screenPoint.x -= NSWidth([self frame]);
+ screenPoint.y += NSHeight([self frame]) - [box arrowPosition] - [box arrowSize];
+ break;
+ }
+
+ [self setFrameTopLeftPoint:screenPoint];
+}
+
+static NSRect ScreenRectForView(NSView *view)
+{
+ NSRect viewRect = [view bounds]; // in View coordinate system
+ viewRect = [view convertRect:viewRect toView:nil]; // translate to window coordinates
+ viewRect.origin = [[view window] convertRectToScreen:(CGRect){.origin = viewRect.origin }].origin; // translate to screen coordinates
+ return viewRect;
+}
+
+- (void)attachToView:(NSView *)view
+{
+ if (nil != attachedWindow)
+ [self detach];
+
+ NSRect viewRect = ScreenRectForView(view);
+ NSPoint arrowPoint;
+
+ switch ([box arrowEdge]) {
+ case ArrowLeftEdge:
+ arrowPoint = NSMakePoint(NSMaxX(viewRect),
+ NSMidY(viewRect));
+ break;
+
+ case ArrowBottomEdge:
+ arrowPoint = NSMakePoint(NSMidX(viewRect),
+ NSMaxY(viewRect));
+ break;
+
+ case ArrowRightEdge:
+ arrowPoint = NSMakePoint(NSMinX(viewRect),
+ NSMidY(viewRect));
+ break;
+
+ case ArrowNone:
+ case ArrowTopEdge:
+ default:
+ arrowPoint = NSMakePoint(NSMidX(viewRect),
+ NSMinY(viewRect));
+ break;
+ }
+ attachedWindow = [view window];
+ [self moveToPoint:arrowPoint];
+ [attachedWindow addChildWindow:self ordered:NSWindowAbove];
+}
+
+- (void)detach
+{
+ [attachedWindow removeChildWindow:self];
+ [self close];
+ attachedWindow = nil;
+}
+
+//MARK: -
+//MARK: Properties
+
+- (void)setArrowPosition:(CGFloat)newPosition
+{
+ [box setArrowPosition:newPosition];
+}
+
+- (CGFloat)arrowPosition
+{
+ return [box arrowPosition];
+}
+
+- (void)setArrowSize:(CGFloat)newSize
+{
+ NSRect contentRect = [self contentRectForFrameRect:[self frame]];
+ [box setArrowSize:newSize];
+ [self setFrame:[self frameRectForContentRect:contentRect] display:[self isVisible]];
+}
+
+- (CGFloat)arrowSize
+{
+ return [box arrowSize];
+}
+
+- (void)setArrowEdge:(ArrowEdge)newEdge
+{
+ [box setArrowEdge:newEdge];
+}
+
+- (ArrowEdge)arrowEdge
+{
+ return [box arrowEdge];
+}
+
+- (void)setCornerRadius:(CGFloat)newRadius
+{
+ [box setCornerRadius:newRadius];
+}
+
+- (CGFloat)cornerRadius
+{
+ return [box cornerRadius];
+}
+
+@end
diff --git a/frontends/cocoa/BookmarksController.h b/frontends/cocoa/BookmarksController.h
new file mode 100644
index 0000000..476d86c
--- a/dev/null
+++ b/frontends/cocoa/BookmarksController.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@class Tree;
+@class TreeView;
+
+@interface BookmarksController : NSWindowController {
+ Tree *tree;
+ NSMapTable *nodeForMenu;
+}
+
+@property (readwrite, assign, nonatomic) IBOutlet NSMenu *defaultMenu;
+@property (readwrite, assign, nonatomic) IBOutlet TreeView *view;
+
+- (IBAction)openBookmarkURL:(id)sender;
+- (IBAction)addBookmark:(id)sender;
+
+- (IBAction)editSelected:(id)sender;
+- (IBAction)deleteSelected:(id)sender;
+- (IBAction)addFolder:(id)sender;
+
+@end
diff --git a/frontends/cocoa/BookmarksController.m b/frontends/cocoa/BookmarksController.m
new file mode 100644
index 0000000..f2358cd
--- a/dev/null
+++ b/frontends/cocoa/BookmarksController.m
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <stddef.h>
+
+#import "utils/messages.h"
+#import "utils/utils.h"
+#import "utils/nsurl.h"
+#import "netsurf/browser_window.h"
+#import "netsurf/keypress.h"
+#import "desktop/hotlist.h"
+
+#import "cocoa/desktop-tree.h"
+#import "cocoa/BookmarksController.h"
+#import "cocoa/Tree.h"
+#import "cocoa/TreeView.h"
+#import "cocoa/NetsurfApp.h"
+#import "cocoa/BrowserViewController.h"
+#import "cocoa/gui.h"
+
+@interface BookmarksController ()
+- (void)noteAppWillTerminate:(NSNotification *)note;
+- (void)save;
+@end
+
+@implementation BookmarksController
+
+@synthesize defaultMenu;
+@synthesize view;
+
+static const char *cocoa_hotlist_path(void)
+{
+ NSString *path = [[NSUserDefaults standardUserDefaults]
+ stringForKey:kHotlistFileOption];
+ return [path UTF8String];
+}
+
+- (id)init
+{
+ if ((self = [super initWithWindowNibName:@"BookmarksWindow"]) == nil) {
+ return nil;
+ }
+ tree_hotlist_path = cocoa_hotlist_path();
+ tree = [[Tree alloc] initWithFlags:TREE_HOTLIST];
+ nodeForMenu = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
+ NSNonOwnedPointerMapValueCallBacks,
+ 0);
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(noteAppWillTerminate:)
+ name:NSApplicationWillTerminateNotification
+ object:NSApp];
+
+ return self;
+}
+
+- (void)noteAppWillTerminate:(NSNotification *)note
+{
+ [self save];
+}
+
+- (void)save
+{
+ hotlist_export(cocoa_hotlist_path(), NULL);
+}
+
+- (void)dealloc
+{
+ [self setView:nil];
+ NSFreeMapTable(nodeForMenu);
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)menuNeedsUpdate:(NSMenu *)menu
+{
+#if 0
+ for (NSMenuItem *item in [menu itemArray]) {
+ if ([item hasSubmenu]) NSMapRemove( nodeForMenu, [item submenu] );
+ [menu removeItem: item];
+ }
+
+ bool hasSeparator = true;
+ struct node *node = (struct node *)NSMapGet( nodeForMenu, menu );
+ if (node == NULL) {
+ for (NSMenuItem *item in [defaultMenu itemArray]) {
+ [menu addItem: [[item copy] autorelease]];
+ }
+ hasSeparator = false;
+ }
+
+ for (struct node *child = tree_node_get_child( node );
+ child != NULL;
+ child = tree_node_get_next( child )) {
+
+ if (tree_node_is_deleted( child )) continue;
+
+ if (!hasSeparator) {
+ [menu addItem: [NSMenuItem separatorItem]];
+ hasSeparator = true;
+ }
+
+ NSString *title = [NSString stringWithUTF8String: tree_url_node_get_title( child )];
+
+ NSMenuItem *item = [menu addItemWithTitle: title action: NULL keyEquivalent: @""];
+ if (tree_node_is_folder( child )) {
+ NSMenu *subMenu = [[[NSMenu alloc] initWithTitle: title] autorelease];
+ NSMapInsert( nodeForMenu, subMenu, child );
+ [subMenu setDelegate: self];
+ [menu setSubmenu: subMenu forItem: item];
+ } else {
+ [item setRepresentedObject: [NSString stringWithUTF8String: tree_url_node_get_url( child )]];
+ [item setTarget: self];
+ [item setAction: @selector( openBookmarkURL: )];
+ }
+ }
+#endif
+}
+
+- (IBAction)openBookmarkURL:(id)sender
+{
+ const char *urltxt = [[sender representedObject] UTF8String];
+ NSParameterAssert(urltxt != NULL);
+
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create(urltxt, &url);
+ if (error == NSERROR_OK) {
+ BrowserViewController *tab = [(NetSurfApp *)NSApp frontTab];
+ if (tab != nil) {
+ error = browser_window_navigate([tab browser],
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ } else {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ }
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction)addBookmark:(id)sender
+{
+ struct browser_window *bw = [[(NetSurfApp *)NSApp frontTab] browser];
+ if (bw != NULL) {
+ hotlist_add_url(browser_window_get_url(bw));
+ }
+}
+
+- (BOOL)validateUserInterfaceItem:(id)item
+{
+ SEL action = [item action];
+
+ if (action == @selector(addBookmark:)) {
+ return [(NetSurfApp *)NSApp frontTab] != nil;
+ }
+
+ return YES;
+}
+
+- (void)windowDidLoad
+{
+ hotlist_expand(false);
+ hotlist_contract(true);
+
+ [view setTree:tree];
+}
+
++ (void)initialize
+{
+ [[NSUserDefaults standardUserDefaults]
+ registerDefaults:
+ [NSDictionary
+ dictionaryWithObjectsAndKeys:cocoa_get_user_path(@"Bookmarks.html"),
+ kHotlistFileOption,
+ nil]];
+}
+
+- (IBAction)editSelected:(id)sender
+{
+ hotlist_edit_selection();
+}
+
+- (IBAction)deleteSelected:(id)sender
+{
+ hotlist_keypress(NS_KEY_DELETE_LEFT);
+}
+
+- (IBAction)addFolder:(id)sender
+{
+ hotlist_add_folder(NULL, false, 0);
+}
+
+@end
diff --git a/frontends/cocoa/BrowserView.h b/frontends/cocoa/BrowserView.h
new file mode 100644
index 0000000..596100f
--- a/dev/null
+++ b/frontends/cocoa/BrowserView.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+#import "cocoa/ScrollableView.h"
+
+@class LocalHistoryController;
+
+@interface BrowserView : ScrollableView <NSTextInput> {
+ struct browser_window *browser;
+
+ NSPoint caretPoint;
+ CGFloat caretHeight;
+ BOOL caretVisible;
+ BOOL hasCaret;
+ NSTimer *caretTimer;
+
+ BOOL isDragging;
+ NSPoint dragStart;
+
+ BOOL historyVisible;
+ LocalHistoryController *history;
+
+ NSString *markedText;
+}
+
+@property (readwrite, assign, nonatomic) struct browser_window *browser;
+@property (readwrite, retain, nonatomic) NSTimer *caretTimer;
+@property (readwrite, assign, nonatomic, getter=isHistoryVisible) BOOL historyVisible;
+
+- (void)removeCaret;
+- (void)addCaretAt:(NSPoint)point height:(CGFloat)height;
+
+- (void)updateHistory;
+
+@end
diff --git a/frontends/cocoa/BrowserView.m b/frontends/cocoa/BrowserView.m
new file mode 100644
index 0000000..c1cf9be
--- a/dev/null
+++ b/frontends/cocoa/BrowserView.m
@@ -0,0 +1,749 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "utils/nsoption.h"
+#import "utils/messages.h"
+#import "utils/nsurl.h"
+#import "utils/utils.h"
+#import "netsurf/browser_window.h"
+#import "netsurf/plotters.h"
+#import "netsurf/content.h"
+#import "netsurf/keypress.h"
+
+#import "cocoa/gui.h"
+#import "cocoa/BrowserView.h"
+#import "cocoa/HistoryView.h"
+#import "cocoa/font.h"
+#import "cocoa/coordinates.h"
+#import "cocoa/plotter.h"
+#import "cocoa/LocalHistoryController.h"
+#import "cocoa/BrowserWindowController.h"
+
+@interface BrowserView ()
+
+@property (readwrite, copy, nonatomic) NSString *markedText;
+
+- (void)scrollHorizontal:(CGFloat)amount;
+- (void)scrollVertical:(CGFloat)amount;
+- (CGFloat)pageScroll;
+
+- (void)popUpContextMenuForEvent:(NSEvent *)event;
+
+- (IBAction)cmOpenURLInTab:(id)sender;
+- (IBAction)cmOpenURLInWindow:(id)sender;
+- (IBAction)cmDownloadURL:(id)sender;
+
+- (IBAction)cmLinkCopy:(id)sender;
+- (IBAction)cmImageCopy:(id)sender;
+
+@end
+
+@implementation BrowserView
+
+@synthesize browser;
+@synthesize caretTimer;
+@synthesize markedText;
+
+static const CGFloat CaretWidth = 1.0;
+static const NSTimeInterval CaretBlinkTime = 0.8;
+
+- (instancetype)initWithFrame:(NSRect)frame
+{
+ if ((self = [super initWithFrame:frame]) == nil) {
+ return nil;
+ }
+
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:NSURLPboardType, @"public.url", nil]];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setCaretTimer:nil];
+ [self setMarkedText:nil];
+}
+
+- (void)setCaretTimer:(NSTimer *)newTimer
+{
+ if (newTimer != caretTimer) {
+ [caretTimer invalidate];
+ caretTimer = newTimer;
+ }
+}
+
+- (void)updateHistory
+{
+ [history redraw];
+}
+
+static inline NSRect cocoa_get_caret_rect(BrowserView *view)
+{
+ float bscale = browser_window_get_scale(view->browser);
+
+ NSRect caretRect = {
+ .origin = NSMakePoint(view->caretPoint.x * bscale, view->caretPoint.y * bscale),
+ .size = NSMakeSize(CaretWidth, view->caretHeight * bscale)
+ };
+
+ return caretRect;
+}
+
+- (void)removeCaret
+{
+ hasCaret = NO;
+ [self setNeedsDisplayInRect:cocoa_get_caret_rect(self)];
+
+ [self setCaretTimer:nil];
+}
+
+- (void)addCaretAt:(NSPoint)point height:(CGFloat)height
+{
+ if (hasCaret) {
+ [self setNeedsDisplayInRect:cocoa_get_caret_rect(self)];
+ }
+
+ caretPoint = point;
+ caretHeight = height;
+ hasCaret = YES;
+ caretVisible = YES;
+
+ if (nil == caretTimer) {
+ [self setCaretTimer:[NSTimer scheduledTimerWithTimeInterval:CaretBlinkTime target:self selector:@selector(caretBlink:) userInfo:nil repeats:YES]];
+ } else {
+ [caretTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:CaretBlinkTime]];
+ }
+
+ [self setNeedsDisplayInRect:cocoa_get_caret_rect(self)];
+}
+
+- (void)caretBlink:(NSTimer *)timer
+{
+ if (hasCaret) {
+ caretVisible = !caretVisible;
+ [self setNeedsDisplayInRect:cocoa_get_caret_rect(self)];
+ }
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ @autoreleasepool {
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &cocoa_plotters
+ };
+
+ const NSRect *rects = NULL;
+ NSInteger count = 0;
+ [self getRectsBeingDrawn:&rects count:&count];
+
+ for (NSInteger i = 0; i < count; i++) {
+ const struct rect clip = {
+ .x0 = cocoa_pt_to_px(NSMinX(rects[i])),
+ .y0 = cocoa_pt_to_px(NSMinY(rects[i])),
+ .x1 = cocoa_pt_to_px(NSMaxX(rects[i])),
+ .y1 = cocoa_pt_to_px(NSMaxY(rects[i]))
+ };
+
+ browser_window_redraw(browser, 0, 0, &clip, &ctx);
+ }
+
+ NSRect caretRect = cocoa_get_caret_rect(self);
+ if (hasCaret && caretVisible && [self needsToDrawRect:caretRect]) {
+ [[NSColor blackColor] set];
+ [NSBezierPath fillRect:caretRect];
+ }
+ }
+}
+
+- (BOOL)isFlipped
+{
+ return YES;
+}
+
+- (void)viewDidMoveToWindow
+{
+ NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:[self visibleRect]
+ options:NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:area];
+}
+
+static browser_mouse_state cocoa_mouse_flags_for_event(NSEvent *evt)
+{
+ browser_mouse_state result = 0;
+ NSUInteger flags = [evt modifierFlags];
+
+ if (flags & NSEventModifierFlagShift)
+ result |= BROWSER_MOUSE_MOD_1;
+ if (flags & NSEventModifierFlagOption)
+ result |= BROWSER_MOUSE_MOD_2;
+
+ return result;
+}
+
+- (NSPoint)convertMousePoint:(NSEvent *)event
+{
+ NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
+ float bscale = browser_window_get_scale(browser);
+
+ location.x /= bscale;
+ location.y /= bscale;
+
+ location.x = cocoa_pt_to_px(location.x);
+ location.y = cocoa_pt_to_px(location.y);
+ return location;
+}
+
+- (void)mouseDown:(NSEvent *)theEvent
+{
+ if ([theEvent modifierFlags] & NSEventModifierFlagControl) {
+ [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
+{
+ if (historyVisible) {
+ [self setHistoryVisible:NO];
+ return;
+ }
+
+ 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
+{
+ if (historyVisible)
+ return;
+
+ 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
+{
+ if (!historyVisible) {
+ [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
+ } else {
+ [history keyDown: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;
+ }
+ }
+ [self setMarkedText:nil];
+}
+
+- (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);
+}
+
+- (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
+{
+ browser_window_schedule_reformat(browser);
+
+ [super adjustFrame];
+}
+
+- (BOOL)isHistoryVisible
+{
+ return historyVisible;
+}
+
+- (void)setHistoryVisible:(BOOL)newVisible
+{
+ if (newVisible == historyVisible)
+ return;
+ historyVisible = newVisible;
+
+ if (historyVisible) {
+ if (nil == history) {
+ history = [[LocalHistoryController alloc] initWithBrowser:self];
+ }
+ [history attachToView:[(BrowserWindowController *)[[self window] windowController] historyButton]];
+ } else {
+ [history detach];
+ }
+}
+
+- (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];
+}
+
+- (CGFloat)pageScroll
+{
+ return NSHeight([[self superview] frame]) - [[self enclosingScrollView] pageScroll];
+}
+
+- (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) {
+ NSString *imageURL = [NSString stringWithUTF8String:nsurl_access(hlcache_handle_get_url(cont.object))];
+
+ [[popupMenu addItemWithTitle:NSLocalizedString(@"Open image in new tab", @"Context menu")
+ action:@selector(cmOpenURLInTab:)
+ keyEquivalent:@""] setRepresentedObject:imageURL];
+ [[popupMenu addItemWithTitle:NSLocalizedString(@"Open image in new window", @"Context menu")
+ action:@selector(cmOpenURLInWindow:)
+ keyEquivalent:@""] setRepresentedObject:imageURL];
+ [[popupMenu addItemWithTitle:NSLocalizedString(@"Save image as", @"Context menu")
+ action:@selector(cmDownloadURL:)
+ keyEquivalent:@""] setRepresentedObject:imageURL];
+ [[popupMenu addItemWithTitle:NSLocalizedString(@"Copy image", @"Context menu")
+ action:@selector(cmImageCopy:)
+ keyEquivalent:@""] setRepresentedObject:(__bridge id)content_get_bitmap(cont.object)];
+
+ [popupMenu addItem:[NSMenuItem separatorItem]];
+ }
+
+ if (cont.link != NULL) {
+ NSString *target = [NSString stringWithUTF8String:nsurl_access(cont.link)];
+
+ [[popupMenu addItemWithTitle:NSLocalizedString(@"Open link in new tab", @"Context menu")
+ action:@selector(cmOpenURLInTab:)
+ keyEquivalent:@""] setRepresentedObject:target];
+ [[popupMenu addItemWithTitle:NSLocalizedString(@"Open link in new window", @"Context menu")
+ action:@selector(cmOpenURLInWindow:)
+ keyEquivalent:@""] setRepresentedObject:target];
+ [[popupMenu addItemWithTitle:NSLocalizedString(@"Save link target", @"Context menu")
+ action:@selector(cmDownloadURL:)
+ keyEquivalent:@""] setRepresentedObject:target];
+ [[popupMenu addItemWithTitle:NSLocalizedString(@"Copy link", @"Context menu")
+ action:@selector(cmLinkCopy:)
+ keyEquivalent:@""] setRepresentedObject:target];
+
+ [popupMenu addItem:[NSMenuItem separatorItem]];
+ }
+
+ [popupMenu addItemWithTitle:NSLocalizedString(@"Back", @"Context menu")
+ action:@selector(goBack:)
+ keyEquivalent:@""];
+ [popupMenu addItemWithTitle:NSLocalizedString(@"Reload", @"Context menu")
+ action:@selector(reloadPage:)
+ keyEquivalent:@""];
+ [popupMenu addItemWithTitle:NSLocalizedString(@"Forward", @"Context menu")
+ action:@selector(goForward:)
+ keyEquivalent:@""];
+ [popupMenu addItemWithTitle:NSLocalizedString(@"View Source", @"Context menu")
+ action:@selector(viewSource:)
+ keyEquivalent:@""];
+
+ [NSMenu popUpContextMenu:popupMenu withEvent:event forView:self];
+}
+
+- (IBAction)cmOpenURLInTab:(id)sender
+{
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create([[sender representedObject] UTF8String], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY | BW_CREATE_TAB | BW_CREATE_CLONE,
+ url,
+ NULL,
+ browser,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction)cmOpenURLInWindow:(id)sender
+{
+ 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);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction)cmDownloadURL:(id)sender
+{
+ 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);
+ }
+}
+
+- (IBAction)cmImageCopy:(id)sender
+{
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ [pb declareTypes:[NSArray arrayWithObject:NSTIFFPboardType] owner:nil];
+ [pb setData:[[sender representedObject] TIFFRepresentation] forType:NSTIFFPboardType];
+}
+
+- (IBAction)cmLinkCopy:(id)sender
+{
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
+ [pb setString:[sender representedObject] forType:NSStringPboardType];
+}
+
+// MARK: -
+// MARK: Accepting dragged URLs
+
+- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
+{
+ if ((NSDragOperationCopy | NSDragOperationGeneric) & [sender draggingSourceOperationMask]) {
+ return NSDragOperationCopy;
+ }
+
+ return NSDragOperationNone;
+}
+
+- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)sender
+{
+ return YES;
+}
+
+- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
+{
+ nsurl *url;
+ nserror error;
+
+ NSPasteboard *pb = [sender draggingPasteboard];
+
+ NSString *type = [pb availableTypeFromArray:[NSArray arrayWithObjects:@"public.url", NSURLPboardType, nil]];
+
+ NSString *urlstr = nil;
+
+ if ([type isEqualToString:NSURLPboardType]) {
+ urlstr = [[NSURL URLFromPasteboard:pb] absoluteString];
+ } else {
+ urlstr = [pb stringForType:type];
+ }
+
+ error = nsurl_create([urlstr UTF8String], &url);
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(browser,
+ url,
+ NULL,
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return YES;
+}
+
+// MARK: -
+// MARK: NSTextInput protocol implementation
+
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange
+{
+ markedText = [aString isEqualToString:@""] ? nil : [aString copy];
+}
+
+- (void)unmarkText
+{
+ [self setMarkedText:nil];
+}
+
+- (BOOL)hasMarkedText
+{
+ return markedText != nil;
+}
+
+- (NSInteger)conversationIdentifier
+{
+ return (NSInteger)self;
+}
+
+- (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange
+{
+ return [[NSAttributedString alloc] initWithString:@""];
+}
+
+- (NSRange)markedRange
+{
+ return NSMakeRange(NSNotFound, 0);
+}
+
+- (NSRange)selectedRange
+{
+ return NSMakeRange(NSNotFound, 0);
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)theRange
+{
+ return NSZeroRect;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
+{
+ return 0;
+}
+
+- (NSArray *)validAttributesForMarkedText
+{
+ return [NSArray array];
+}
+
+- (void)doCommandBySelector:(SEL)sel
+{
+ [super doCommandBySelector:sel];
+}
+
+@end
diff --git a/frontends/cocoa/BrowserViewController.h b/frontends/cocoa/BrowserViewController.h
new file mode 100644
index 0000000..f499c05
--- a/dev/null
+++ b/frontends/cocoa/BrowserViewController.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+struct browser_window;
+@class BrowserView;
+@class BrowserWindowController;
+
+@interface BrowserViewController : NSViewController {
+ struct browser_window *browser;
+ NSString *url;
+ BrowserWindowController *windowController;
+ NSString *title;
+ NSString *status;
+ BOOL isProcessing;
+ NSImage *favicon;
+ BOOL canGoBack;
+ BOOL canGoForward;
+}
+
+@property (readwrite, assign, nonatomic) struct browser_window *browser;
+@property (readwrite, copy, nonatomic) NSString *url;
+@property (readwrite, assign, nonatomic) IBOutlet BrowserView *browserView;
+@property (readwrite, retain, nonatomic) BrowserWindowController *windowController;
+@property (readwrite, copy) NSString *title;
+@property (readwrite, copy, nonatomic) NSString *status;
+@property (readwrite, assign, nonatomic) BOOL isProcessing;
+@property (readwrite, copy, nonatomic) NSImage *favicon;
+@property (readwrite, assign, nonatomic) BOOL canGoBack;
+@property (readwrite, assign, nonatomic) BOOL canGoForward;
+
+- (id)initWithBrowser:(struct browser_window *)bw;
+
+- (void)contentUpdated;
+- (void)updateBackForward;
+
+- (IBAction)navigate:(id)sender;
+
+- (IBAction)backForwardSelected:(id)sender;
+
+- (IBAction)goHome:(id)sender;
+
+- (IBAction)goBack:(id)sender;
+- (IBAction)goForward:(id)sender;
+- (IBAction)reloadPage:(id)sender;
+- (IBAction)stopLoading:(id)sender;
+
+- (IBAction)zoomIn:(id)sender;
+- (IBAction)zoomOut:(id)sender;
+- (IBAction)zoomOriginal:(id)sender;
+
+- (IBAction)viewSource:(id)sender;
+
+- (void)buildBackMenu:(NSMenu *)menu;
+- (void)buildForwardMenu:(NSMenu *)menu;
+
+@end
diff --git a/frontends/cocoa/BrowserViewController.m b/frontends/cocoa/BrowserViewController.m
new file mode 100644
index 0000000..9848654
--- a/dev/null
+++ b/frontends/cocoa/BrowserViewController.m
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "utils/nsoption.h"
+#import "utils/corestrings.h"
+#import "utils/filename.h"
+#import "utils/file.h"
+#import "utils/messages.h"
+#import "utils/nsurl.h"
+#import "netsurf/content.h"
+#import "netsurf/browser_window.h"
+#import "desktop/browser_history.h"
+
+#import "cocoa/gui.h"
+#import "cocoa/BrowserViewController.h"
+#import "cocoa/BrowserView.h"
+#import "cocoa/BrowserWindowController.h"
+#import "cocoa/fetch.h"
+
+@implementation BrowserViewController
+
+@synthesize browser;
+@synthesize url;
+@synthesize browserView;
+@synthesize windowController;
+@synthesize title;
+@synthesize status;
+@synthesize isProcessing;
+@synthesize favicon;
+@synthesize canGoBack;
+@synthesize canGoForward;
+
+- (instancetype)initWithBrowser:(struct browser_window *)bw
+{
+ if ((self = [super initWithNibName:@"Browser" bundle:nil]) == nil) {
+ return nil;
+ }
+
+ browser = bw;
+
+ return self;
+}
+
+- (IBAction)navigate:(id)sender
+{
+ nsurl *urlns;
+ nserror error;
+
+ error = nsurl_create([url UTF8String], &urlns);
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(browser,
+ urlns,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(urlns);
+ }
+}
+
+- (void)awakeFromNib
+{
+ [browserView setBrowser:browser];
+}
+
+- (IBAction)zoomIn:(id)sender
+{
+ browser_window_set_scale(browser,
+ browser_window_get_scale(browser) * 1.1,
+ true);
+}
+
+- (IBAction)zoomOut:(id)sender
+{
+ browser_window_set_scale(browser,
+ browser_window_get_scale(browser) * 0.9,
+ true);
+}
+
+- (IBAction)zoomOriginal:(id)sender
+{
+ browser_window_set_scale(browser,
+ (float)nsoption_int(scale) / 100.0,
+ true);
+}
+
+- (IBAction)backForwardSelected:(id)sender
+{
+ if ([sender selectedSegment] == 0) {
+ [self goBack:sender];
+ } else {
+ [self goForward:sender];
+ }
+}
+
+- (IBAction)goBack:(id)sender
+{
+ if (browser && browser_window_history_back_available(browser)) {
+ browser_window_history_back(browser, false);
+ [self updateBackForward];
+ }
+}
+
+- (IBAction)goForward:(id)sender
+{
+ if (browser && browser_window_history_forward_available(browser)) {
+ browser_window_history_forward(browser, false);
+ [self updateBackForward];
+ }
+}
+
+- (IBAction)goHome:(id)sender
+{
+ nsurl *urlns;
+ nserror error;
+
+ error = nsurl_create(nsoption_charp(homepage_url), &urlns);
+ if (error == NSERROR_OK) {
+ error = browser_window_navigate(browser,
+ urlns,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(urlns);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction)reloadPage:(id)sender
+{
+ browser_window_reload(browser, true);
+}
+
+- (IBAction)stopLoading:(id)sender
+{
+ browser_window_stop(browser);
+}
+
+- (IBAction)viewSource:(id)sender
+{
+ struct hlcache_handle *content;
+ size_t size;
+ const char *source;
+ char *path = NULL;
+
+ if (browser == NULL) {
+ return;
+ }
+ content = browser_window_get_content(browser);
+ if (content == NULL) {
+ return;
+ }
+ source = content_get_source_data(content, &size);
+ if (source == NULL) {
+ return;
+ }
+
+ /* try to load local files directly. */
+ netsurf_nsurl_to_path(hlcache_handle_get_url(content), &path);
+
+ if (path == NULL) {
+ /* We cannot release the requested filename until after it
+ * has finished being used. As we can't easily find out when
+ * this is, we simply don't bother releasing it and simply
+ * allow it to be re-used next time NetSurf is started. The
+ * memory overhead from doing this is under 1 byte per
+ * filename. */
+ const char *filename = filename_request();
+ const char *extension = "txt";
+ fprintf(stderr, "filename '%p'\n", filename);
+ if (filename == NULL)
+ return;
+ lwc_string *str = content_get_mime_type(content);
+ if (str) {
+ NSString *mime = [NSString stringWithUTF8String:lwc_string_data(str)];
+ NSString *uti = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)mime, NULL);
+ NSString *ext = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)uti, kUTTagClassFilenameExtension);
+ extension = [ext UTF8String];
+ lwc_string_unref(str);
+ }
+
+ NSURL *dataUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%s.%s", filename, extension]
+ relativeToURL:[NSURL fileURLWithPath:@TEMP_FILENAME_PREFIX]];
+
+ NSData *data = [NSData dataWithBytes:source length:size];
+ [data writeToURL:dataUrl atomically:NO];
+ path = (char *)[[dataUrl path] UTF8String];
+ }
+
+ if (path) {
+ NSString *p = [NSString stringWithUTF8String:path];
+ NSWorkspace *ws = [NSWorkspace sharedWorkspace];
+ [ws openFile:p withApplication:@"Xcode"];
+ }
+}
+
+static inline bool
+compare_float(float a, float b)
+{
+ const float epsilon = 0.00001;
+
+ if (a == b) {
+ return true;
+ }
+
+ return fabs((a - b) / b) <= epsilon;
+}
+
+- (BOOL)validateUserInterfaceItem:(id)item
+{
+ SEL action = [item action];
+
+ if (action == @selector(copy:)) {
+ return browser_window_get_editor_flags(browser) & BW_EDITOR_CAN_COPY;
+ }
+
+ if (action == @selector(cut:)) {
+ return browser_window_get_editor_flags(browser) & BW_EDITOR_CAN_CUT;
+ }
+
+ if (action == @selector(paste:)) {
+ return browser_window_get_editor_flags(browser) & BW_EDITOR_CAN_PASTE;
+ }
+
+ if (action == @selector(stopLoading:)) {
+ return browser_window_stop_available(browser);
+ }
+
+ if (action == @selector(zoomOriginal:)) {
+ return !compare_float(browser_window_get_scale(browser), (float)nsoption_int(scale) / 100.0);
+ }
+
+ if (action == @selector(goBack:)) {
+ return canGoBack;
+ }
+
+ if (action == @selector(goForward:)) {
+ return canGoForward;
+ }
+
+ return YES;
+}
+
+- (void)updateBackForward
+{
+ [browserView updateHistory];
+ [self setCanGoBack:browser != NULL && browser_window_history_back_available(browser)];
+ [self setCanGoForward:browser != NULL && browser_window_history_forward_available(browser)];
+}
+
+- (void)contentUpdated
+{
+ [browserView updateHistory];
+}
+
+struct history_add_menu_item_data {
+ NSInteger index;
+ void *menu;
+ void *target;
+};
+
+static NSMenu *get_menu(const struct history_add_menu_item_data *data)
+{
+ return (__bridge NSMenu *)data->menu;
+}
+
+static id get_target(const struct history_add_menu_item_data *data)
+{
+ return (__bridge id)data->target;
+}
+
+static bool
+history_add_menu_item_cb(const struct browser_window *bw,
+ int x0, int y0, int x1, int y1,
+ const struct history_entry *page,
+ void *user_data)
+{
+ struct history_add_menu_item_data *data = user_data;
+
+ NSMenuItem *item = nil;
+ if (data->index < [get_menu(data) numberOfItems]) {
+ item = [get_menu(data) itemAtIndex:data->index];
+ } else {
+ item = [[NSMenuItem alloc] initWithTitle:@""
+ action:@selector(historyItemSelected:)
+ keyEquivalent:@""];
+ [get_menu(data) addItem:item];
+ }
+ ++data->index;
+
+ [item setTarget:get_target(data)];
+ [item setTitle:[NSString stringWithUTF8String:browser_window_history_entry_get_title(page)]];
+ [item setRepresentedObject:[NSValue valueWithPointer:page]];
+
+ return true;
+}
+
+- (IBAction)historyItemSelected:(id)sender
+{
+ struct history_entry *entry = [[sender representedObject] pointerValue];
+ browser_window_history_go(browser, entry, false);
+ [self updateBackForward];
+}
+
+- (void)buildBackMenu:(NSMenu *)menu
+{
+ struct history_add_menu_item_data data = {
+ .index = 0,
+ .menu = (__bridge void *)menu,
+ .target = (__bridge void *)self
+ };
+ browser_window_history_enumerate_back(browser,
+ history_add_menu_item_cb,
+ &data);
+ while (data.index < [menu numberOfItems]) {
+ [menu removeItemAtIndex:data.index];
+ }
+}
+
+- (void)buildForwardMenu:(NSMenu *)menu
+{
+ struct history_add_menu_item_data data = {
+ .index = 0,
+ .menu = (__bridge void *)menu,
+ .target = (__bridge void *)self
+ };
+ browser_window_history_enumerate_forward(browser,
+ history_add_menu_item_cb,
+ &data);
+ while (data.index < [menu numberOfItems]) {
+ [menu removeItemAtIndex:data.index];
+ }
+}
+
+@end
diff --git a/frontends/cocoa/BrowserWindow.h b/frontends/cocoa/BrowserWindow.h
new file mode 100644
index 0000000..b0810c7
--- a/dev/null
+++ b/frontends/cocoa/BrowserWindow.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@interface BrowserWindow : NSWindow
+@end
diff --git a/frontends/cocoa/BrowserWindow.m b/frontends/cocoa/BrowserWindow.m
new file mode 100644
index 0000000..c50e66a
--- a/dev/null
+++ b/frontends/cocoa/BrowserWindow.m
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/BrowserWindow.h"
+#import "cocoa/BrowserWindowController.h"
+
+@implementation BrowserWindow
+
+- (void)performClose:(id)sender
+{
+ [self.windowController closeCurrentTab:sender];
+}
+
+@end
diff --git a/frontends/cocoa/BrowserWindowController.h b/frontends/cocoa/BrowserWindowController.h
new file mode 100644
index 0000000..4cbe02f
--- a/dev/null
+++ b/frontends/cocoa/BrowserWindowController.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@class PSMTabBarControl;
+@class BrowserViewController;
+@class URLFieldCell;
+
+@interface BrowserWindowController : NSWindowController
+
+@property (readwrite, assign, nonatomic) IBOutlet PSMTabBarControl *tabBar;
+@property (readwrite, assign, nonatomic) IBOutlet NSTabView *tabView;
+@property (readwrite, assign, nonatomic) IBOutlet URLFieldCell *urlField;
+@property (readwrite, assign, nonatomic) IBOutlet NSObjectController *activeBrowserController;
+@property (readwrite, assign, nonatomic) IBOutlet NSSegmentedControl *navigationControl;
+@property (readwrite, assign, nonatomic) IBOutlet NSButton *historyButton;
+@property (readwrite, assign, nonatomic) IBOutlet NSMenu *historyBackMenu;
+@property (readwrite, assign, nonatomic) IBOutlet NSMenu *historyForwardMenu;
+
+@property (readwrite, assign, nonatomic) BrowserViewController *activeBrowser;
+
+@property (readwrite, assign, nonatomic) BOOL canGoBack;
+@property (readwrite, assign, nonatomic) BOOL canGoForward;
+
+- (IBAction)newTab:(id)sender;
+- (IBAction)closeCurrentTab:(id)sender;
+
+- (void)addTab:(BrowserViewController *)browser;
+- (void)removeTab:(BrowserViewController *)browser;
+
+@end
diff --git a/frontends/cocoa/BrowserWindowController.m b/frontends/cocoa/BrowserWindowController.m
new file mode 100644
index 0000000..f44e4fb
--- a/dev/null
+++ b/frontends/cocoa/BrowserWindowController.m
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "netsurf/browser_window.h"
+#import "utils/nsoption.h"
+#import "utils/messages.h"
+#import "utils/utils.h"
+#import "utils/nsurl.h"
+
+#import "cocoa/BrowserWindowController.h"
+
+#import "cocoa/BrowserViewController.h"
+#import "cocoa/PSMTabBarControl/PSMTabBarControl.h"
+#import "cocoa/PSMTabBarControl/PSMRolloverButton.h"
+#import "cocoa/URLFieldCell.h"
+#import "cocoa/gui.h"
+#import "cocoa/NetsurfApp.h"
+
+@interface BrowserWindowController ()
+
+- (void)canCloseAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+
+@end
+
+@implementation BrowserWindowController
+
+@synthesize tabBar;
+@synthesize tabView;
+@synthesize urlField;
+@synthesize navigationControl;
+@synthesize historyButton;
+@synthesize historyBackMenu;
+@synthesize historyForwardMenu;
+
+@synthesize activeBrowser;
+@synthesize activeBrowserController;
+
+- (id)init
+{
+ if (nil == (self = [super initWithWindowNibName:@"BrowserWindow"]))
+ return nil;
+
+ return self;
+}
+
+- (void)awakeFromNib
+{
+ [tabBar setShowAddTabButton:YES];
+ [tabBar setTearOffStyle:PSMTabBarTearOffMiniwindow];
+ [tabBar setCanCloseOnlyTab:YES];
+ [tabBar setHideForSingleTab:YES];
+
+ NSButton *b = [tabBar addTabButton];
+ [b setTarget:self];
+ [b setAction:@selector(newTab:)];
+
+ [urlField setRefreshAction:@selector(reloadPage:)];
+ [urlField bind:@"favicon" toObject:activeBrowserController withKeyPath:@"selection.favicon" options:nil];
+
+ [self bind:@"canGoBack"
+ toObject:activeBrowserController
+ withKeyPath:@"selection.canGoBack"
+ options:nil];
+ [self bind:@"canGoForward"
+ toObject:activeBrowserController
+ withKeyPath:@"selection.canGoForward"
+ options:nil];
+
+ [navigationControl setMenu:historyBackMenu forSegment:0];
+ [navigationControl setMenu:historyForwardMenu forSegment:1];
+}
+
+- (void)addTab:(BrowserViewController *)browser
+{
+ NSTabViewItem *item = [[NSTabViewItem alloc] initWithIdentifier:browser];
+
+ [item setView:[browser view]];
+ [item bind:@"label" toObject:browser withKeyPath:@"title" options:nil];
+
+ [tabView addTabViewItem:item];
+ [browser setWindowController:self];
+
+ [tabView selectTabViewItem:item];
+}
+
+- (void)removeTab:(BrowserViewController *)browser
+{
+ NSUInteger itemIndex = [tabView indexOfTabViewItemWithIdentifier:browser];
+ if (itemIndex != NSNotFound) {
+ NSTabViewItem *item = [tabView tabViewItemAtIndex:itemIndex];
+ [tabView removeTabViewItem:item];
+ [browser setWindowController:nil];
+ }
+}
+
+- (BOOL)windowShouldClose:(NSWindow *)window
+{
+ if (tabView.numberOfTabViewItems <= 1 || [[NSUserDefaults standardUserDefaults] boolForKey:kAlwaysCloseMultipleTabs]) {
+ return YES;
+ }
+
+ NSAlert *ask = [[NSAlert alloc] init];
+ ask.messageText = NSLocalizedString(@"Do you really want to close this window?", nil);
+ [ask addButtonWithTitle:NSLocalizedString(@"Yes", @"'Yes' button")];
+ [ask addButtonWithTitle:NSLocalizedString(@"No", @"'No' button")];
+ ask.informativeText = [NSString localizedStringWithFormat:NSLocalizedString(@"There are %d tabs open, do you want to close them all?", nil), tabView.numberOfTabViewItems];
+ ask.showsSuppressionButton = YES;
+
+ [ask beginSheetModalForWindow:window
+ completionHandler:^(NSModalResponse returnCode) {
+ if (returnCode != NSAlertFirstButtonReturn) {
+ return;
+ }
+
+ [[NSUserDefaults standardUserDefaults] setBool:[[ask suppressionButton] state] == NSOnState
+ forKey:kAlwaysCloseMultipleTabs];
+ [self.window close];
+
+ }];
+
+ return NO;
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ for (NSTabViewItem *tab in [tabView tabViewItems]) {
+ [tabView removeTabViewItem:tab];
+ }
+}
+
+- (id)supplementalTargetForAction:(SEL)action sender:(id)sender
+{
+ if ([self.activeBrowser respondsToSelector:action]) {
+ return activeBrowser;
+ }
+
+ return [super supplementalTargetForAction:action sender:sender];
+}
+
+- (IBAction)newTab:(id)sender
+{
+ nsurl *url;
+ nserror error;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ } else {
+ error = nsurl_create(NETSURF_HOMEPAGE, &url);
+ }
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY | BW_CREATE_TAB,
+ url,
+ NULL,
+ [activeBrowser browser],
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction)closeCurrentTab:(id)sender
+{
+ [self removeTab:activeBrowser];
+ if (tabView.numberOfTabViewItems == 0) {
+ [self.window close];
+ }
+}
+
+- (void)setCanGoBack:(BOOL)can
+{
+ [navigationControl setEnabled:can forSegment:0];
+}
+
+- (BOOL)canGoBack
+{
+ return [navigationControl isEnabledForSegment:0];
+}
+
+- (void)setCanGoForward:(BOOL)can
+{
+ [navigationControl setEnabled:can forSegment:1];
+}
+
+- (BOOL)canGoForward
+{
+ return [navigationControl isEnabledForSegment:1];
+}
+
+- (void)windowDidBecomeMain:(NSNotification *)note
+{
+ [(NetSurfApp *)NSApp setFrontTab:[[tabView selectedTabViewItem] identifier]];
+}
+
+- (void)menuNeedsUpdate:(NSMenu *)menu
+{
+ if (menu == historyBackMenu) {
+ [activeBrowser buildBackMenu:menu];
+ } else if (menu == historyForwardMenu) {
+ [activeBrowser buildForwardMenu:menu];
+ }
+}
+
+#pragma mark -
+#pragma mark Tab bar delegate
+
+- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
+{
+ [self setActiveBrowser:[tabViewItem identifier]];
+ if ([[self window] isMainWindow]) {
+ [(NetSurfApp *)NSApp setFrontTab:[tabViewItem identifier]];
+ }
+}
+
+- (BOOL)tabView:(NSTabView *)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl
+{
+ return [aTabView numberOfTabViewItems] > 1;
+}
+
+- (BOOL)tabView:(NSTabView *)aTabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl
+{
+ [[tabViewItem identifier] setWindowController:self];
+ return YES;
+}
+
+- (PSMTabBarControl *)tabView:(NSTabView *)aTabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point
+{
+ BrowserWindowController *newWindow = [[BrowserWindowController alloc] init];
+ [[tabViewItem identifier] setWindowController:newWindow];
+ [[newWindow window] setFrameOrigin:point];
+ return newWindow->tabBar;
+}
+
+- (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem
+{
+ [tabViewItem unbind:@"label"];
+
+ if (activeBrowser == [tabViewItem identifier]) {
+ [self setActiveBrowser:nil];
+ [(NetSurfApp *)NSApp setFrontTab:nil];
+ }
+
+ browser_window_destroy([[tabViewItem identifier] browser]);
+}
+
+@end
diff --git a/frontends/cocoa/DownloadWindowController.h b/frontends/cocoa/DownloadWindowController.h
new file mode 100644
index 0000000..2cc36ed
--- a/dev/null
+++ b/frontends/cocoa/DownloadWindowController.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+struct gui_download_table *cocoa_download_table;
+
+@interface DownloadWindowController : NSWindowController {
+ struct download_context *context;
+ unsigned long totalSize;
+ unsigned long receivedSize;
+
+ NSURL *url;
+ NSString *mimeType;
+ NSURL *saveURL;
+ NSFileHandle *outputFile;
+ NSMutableData *savedData;
+ NSDate *startDate;
+
+ BOOL canClose;
+ BOOL shouldClose;
+}
+
+@property (readwrite, copy, nonatomic) NSURL *URL;
+@property (readwrite, copy, nonatomic) NSString *MIMEType;
+@property (readwrite, assign, nonatomic) unsigned long totalSize;
+@property (readwrite, copy, nonatomic) NSURL *saveURL;
+@property (readwrite, assign, nonatomic) unsigned long receivedSize;
+
+@property (readonly, nonatomic) NSString *fileName;
+@property (readonly, nonatomic) NSImage *icon;
+@property (readonly, nonatomic) NSString *statusText;
+
+- (id)initWithContext:(struct download_context *)ctx;
+
+- (void)abort;
+
+@end
diff --git a/frontends/cocoa/DownloadWindowController.m b/frontends/cocoa/DownloadWindowController.m
new file mode 100644
index 0000000..4137371
--- a/dev/null
+++ b/frontends/cocoa/DownloadWindowController.m
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "utils/log.h"
+#import "utils/nsurl.h"
+#import "desktop/download.h"
+#import "netsurf/download.h"
+
+#import "cocoa/DownloadWindowController.h"
+#import "cocoa/gui.h"
+
+@interface DownloadWindowController ()
+
+@property (readwrite, retain, nonatomic) NSFileHandle *outputFile;
+@property (readwrite, retain, nonatomic) NSMutableData *savedData;
+@property (readwrite, copy, nonatomic) NSDate *startDate;
+
+- (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
+- (void)askCancelDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
+
+- (BOOL)receivedData:(NSData *)data;
+
+- (void)showError:(NSString *)error;
+- (void)downloadDone;
+- (void)removeIfPossible;
+
+@end
+
+static void cocoa_unregister_download(DownloadWindowController *download);
+static void cocoa_register_download(DownloadWindowController *download);
+
+@implementation DownloadWindowController
+
+- (id)initWithContext:(struct download_context *)ctx
+{
+ if ((self = [super initWithWindowNibName:@"DownloadWindow"]) == nil) {
+ return nil;
+ }
+
+ context = ctx;
+ totalSize = download_context_get_total_length(context);
+ [self setURL:[NSURL URLWithString:[NSString stringWithUTF8String:nsurl_access(download_context_get_url(context))]]];
+ [self setMIMEType:[NSString stringWithUTF8String:download_context_get_mime_type(context)]];
+ [self setStartDate:[NSDate date]];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ download_context_destroy(context);
+}
+
+- (void)abort
+{
+ download_context_abort(context);
+ [self removeIfPossible];
+}
+
+- (void)askForSave
+{
+ canClose = NO;
+ [[NSSavePanel savePanel]
+ beginSheetForDirectory:nil
+ file:[NSString stringWithUTF8String:download_context_get_filename(context)]
+ modalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:)
+ contextInfo:NULL];
+}
+
+- (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ canClose = YES;
+
+ if (returnCode == NSModalResponseCancel) {
+ [self abort];
+ return;
+ }
+
+ NSURL *targetURL = [sheet URL];
+ NSString *path = [targetURL path];
+
+ [[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:nil];
+
+ FSRef ref;
+ if (CFURLGetFSRef((CFURLRef)targetURL, &ref)) {
+ NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
+ url, (NSString *)kLSQuarantineDataURLKey,
+ (NSString *)kLSQuarantineTypeWebDownload, (NSString *)kLSQuarantineTypeKey,
+ nil];
+ LSSetItemAttribute(&ref, kLSRolesAll, kLSItemQuarantineProperties, (__bridge CFDictionaryRef)attributes);
+ LOG("Set quarantine attributes on file %s", [path UTF8String]);
+ }
+
+ [self setOutputFile:[NSFileHandle fileHandleForWritingAtPath:path]];
+ [self setSaveURL:targetURL];
+
+ NSWindow *win = [self window];
+ [win setRepresentedURL:targetURL];
+ [win setTitle:[self fileName]];
+
+ if (nil == outputFile) {
+ [self performSelector:@selector(showError:) withObject:@"Cannot create file" afterDelay:0];
+ return;
+ }
+
+ if (nil != savedData) {
+ [outputFile writeData:savedData];
+ [self setSavedData:nil];
+ }
+
+ [self removeIfPossible];
+}
+
+- (BOOL)receivedData:(NSData *)data
+{
+ if (outputFile) {
+ [outputFile writeData:data];
+ } else {
+ if (nil == savedData) {
+ [self setSavedData:[NSMutableData data]];
+ }
+ [savedData appendData:data];
+ }
+
+ [self setReceivedSize:receivedSize + [data length]];
+
+ return YES;
+}
+
+- (void)showError:(NSString *)error
+{
+ canClose = NO;
+ NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Error", @"show error")
+ defaultButton:NSLocalizedString(@"OK", @"'OK' button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:@"%@", error];
+
+ [alert beginSheetModalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
+ contextInfo:NULL];
+}
+
+- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+ [self abort];
+}
+
+- (void)removeIfPossible
+{
+ if (canClose && shouldClose) {
+ cocoa_unregister_download(self);
+ }
+}
+- (void)downloadDone
+{
+ shouldClose = YES;
+ [self removeIfPossible];
+}
+
+- (BOOL)windowShouldClose:(id)sender
+{
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:kAlwaysCancelDownload]) {
+ return YES;
+ }
+
+ NSAlert *ask = [NSAlert alertWithMessageText:NSLocalizedString(@"Cancel download?", @"Download")
+ defaultButton:NSLocalizedString(@"Yes", @"")
+ alternateButton:NSLocalizedString(@"No", @"")
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"Should the download of '%@' really be cancelled?", @"Download"),
+ [self fileName]];
+ [ask setShowsSuppressionButton:YES];
+ [ask beginSheetModalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(askCancelDidEnd:returnCode:contextInfo:)
+ contextInfo:NULL];
+ return NO;
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ [self abort];
+}
+
+- (void)askCancelDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+ if (returnCode == NSModalResponseOK) {
+ [[NSUserDefaults standardUserDefaults]
+ setBool:[[alert suppressionButton] state] == NSOnState
+ forKey:kAlwaysCancelDownload];
+ [self close];
+ }
+}
+
+#pragma mark -
+#pragma mark Properties
+
+@synthesize URL = url;
+@synthesize MIMEType = mimeType;
+@synthesize totalSize;
+@synthesize saveURL;
+@synthesize outputFile;
+@synthesize savedData;
+@synthesize receivedSize;
+@synthesize startDate;
+
++ (NSSet *)keyPathsForValuesAffectingStatusText
+{
+ return [NSSet setWithObjects:@"totalSize", @"receivedSize", nil];
+}
+
+#ifndef NSAppKitVersionNumber10_5
+#define NSAppKitVersionNumber10_5 949
+#endif
+
+static NSString *cocoa_file_size_string(float size)
+{
+ static unsigned factor = 0;
+ if (factor == 0) {
+ if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
+ factor = 1000;
+ } else {
+ factor = 1024;
+ }
+ }
+
+ if (size == 0)
+ return @"nothing";
+ if (size <= 1.0)
+ return @"1 byte";
+
+ if (size < factor - 1)
+ return [NSString stringWithFormat:@"%1.0f bytes", size];
+
+ size /= factor;
+ if (size < factor - 1)
+ return [NSString stringWithFormat:@"%1.1f KB", size];
+
+ size /= factor;
+ if (size < factor - 1)
+ return [NSString stringWithFormat:@"%1.1f MB", size];
+
+ size /= factor;
+ if (size < factor - 1)
+ return [NSString stringWithFormat:@"%1.1f GB", size];
+
+ size /= factor;
+ return [NSString stringWithFormat:@"%1.1f TB", size];
+}
+
+static NSString *cocoa_time_string(unsigned seconds)
+{
+ if (seconds <= 10) {
+ return NSLocalizedString(@"less than 10 seconds",
+ @"time remaining");
+ }
+
+ if (seconds < 60) {
+ return [NSString stringWithFormat:NSLocalizedString(@"%u seconds",
+ @"time remaining"),
+ seconds];
+ }
+
+ unsigned minutes = seconds / 60;
+ seconds = seconds % 60;
+
+ if (minutes < 60) {
+ return [NSString stringWithFormat:NSLocalizedString(@"%u:%02u minutes",
+ @"time remaining: minutes, seconds"),
+ minutes, seconds];
+ }
+
+ unsigned hours = minutes / 60;
+ minutes = minutes % 60;
+
+ return [NSString stringWithFormat:NSLocalizedString(@"%2:%02u hours", @"time remaining: hours, minutes"), hours, minutes];
+}
+
+- (NSString *)statusText
+{
+ NSString *speedString = @"";
+
+ float speed = 0.0;
+ NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
+ if (elapsedTime >= 0.1) {
+ speed = (float)receivedSize / elapsedTime;
+ speedString = [NSString stringWithFormat:@" (%@/s)", cocoa_file_size_string(speed)];
+ }
+
+ NSString *timeRemainingString = @"";
+ NSString *totalSizeString = @"";
+ if (totalSize != 0) {
+ if (speed > 0.0) {
+ float timeRemaining = (float)(totalSize - receivedSize) / speed;
+ timeRemainingString = [NSString stringWithFormat:@": %@", cocoa_time_string(timeRemaining)];
+ }
+ totalSizeString = [NSString stringWithFormat:NSLocalizedString(@" of %@", @"... of (total size)"), cocoa_file_size_string(totalSize)];
+ }
+
+ return [NSString stringWithFormat:@"%@%@%@%@", cocoa_file_size_string(receivedSize),
+ totalSizeString, speedString, timeRemainingString];
+}
+
++ (NSSet *)keyPathsForValuesAffectingFileName
+{
+ return [NSSet setWithObject:@"saveURL"];
+}
+
+- (NSString *)fileName
+{
+ return [[saveURL path] lastPathComponent];
+}
+
++ (NSSet *)keyPathsForValuesAffectingIcon
+{
+ return [NSSet setWithObjects:@"mimeType", @"URL", nil];
+}
+
+- (NSImage *)icon
+{
+ NSString *type = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)mimeType, NULL);
+ if ([type hasPrefix:@"dyn."] || [type isEqualToString:(NSString *)kUTTypeData]) {
+ NSString *pathExt = [[url path] pathExtension];
+ type = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)pathExt, NULL);
+ }
+ return [[NSWorkspace sharedWorkspace] iconForFileType:type];
+}
+
+#pragma mark -
+#pragma mark NetSurf interface functions
+
+static struct gui_download_window *
+gui_download_window_create(download_context *ctx,
+ struct gui_window *parent)
+{
+ DownloadWindowController *const window = [[DownloadWindowController alloc] initWithContext:ctx];
+ cocoa_register_download(window);
+ [window askForSave];
+
+ return (__bridge_retained struct gui_download_window *)window;
+}
+
+static nserror
+gui_download_window_data(struct gui_download_window *dw,
+ const char *data,
+ unsigned int size)
+{
+ DownloadWindowController *const window = (__bridge DownloadWindowController *)dw;
+ return [window receivedData:[NSData dataWithBytes:data length:size]] ? NSERROR_OK : NSERROR_SAVE_FAILED;
+}
+
+static void
+gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+ DownloadWindowController *const window = (__bridge DownloadWindowController *)dw;
+ [window showError:[NSString stringWithUTF8String:error_msg]];
+}
+
+static void
+gui_download_window_done(struct gui_download_window *dw)
+{
+ DownloadWindowController *const window = (__bridge DownloadWindowController *)dw;
+ [window downloadDone];
+}
+
+@end
+
+#pragma mark -
+static NSMutableSet *cocoa_all_downloads = nil;
+
+static void
+cocoa_register_download(DownloadWindowController *download)
+{
+ if (cocoa_all_downloads == nil) {
+ cocoa_all_downloads = [[NSMutableSet alloc] init];
+ }
+ [cocoa_all_downloads addObject:download];
+}
+
+static void
+cocoa_unregister_download(DownloadWindowController *download)
+{
+ [cocoa_all_downloads removeObject:download];
+}
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *cocoa_download_table = &download_table;
diff --git a/frontends/cocoa/FormSelectMenu.h b/frontends/cocoa/FormSelectMenu.h
new file mode 100644
index 0000000..3ed86e3
--- a/dev/null
+++ b/frontends/cocoa/FormSelectMenu.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+struct form_control;
+struct browser_window;
+
+@interface FormSelectMenu : NSObject
+
+- (instancetype)initWithControl:(struct form_control *)control forWindow:(struct browser_window *)window;
+- (void)runInView:(NSView *)view;
+
+@end
diff --git a/frontends/cocoa/FormSelectMenu.m b/frontends/cocoa/FormSelectMenu.m
new file mode 100644
index 0000000..c849ba0
--- a/dev/null
+++ b/frontends/cocoa/FormSelectMenu.m
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/FormSelectMenu.h"
+#import "cocoa/coordinates.h"
+
+#import "netsurf/types.h"
+#import "netsurf/browser_window.h"
+#import "netsurf/form.h"
+
+static inline NSRect cocoa_rect_for_control(struct browser_window *bw, struct form_control *control)
+{
+ struct rect r;
+ form_control_bounding_rect(control, &r);
+ return cocoa_scaled_rect(browser_window_get_scale(bw),
+ r.x0,
+ r.y0,
+ r.x1,
+ r.y1);
+}
+
+@interface FormSelectMenu () <NSMenuDelegate>
+
+@property (nonatomic) NSMenu *menu;
+@property (nonatomic) NSPopUpButtonCell *cell;
+
+@property (nonatomic) struct browser_window *browser;
+@property (nonatomic) struct form_control *control;
+
+- (void)itemSelected:(id)sender;
+
+@end
+
+@implementation FormSelectMenu
+
+- (instancetype)initWithControl:(struct form_control *)c forWindow:(struct browser_window *)w
+{
+ if ((self = [super init]) == nil)
+ return nil;
+
+ _control = c;
+ _browser = w;
+
+ _menu = [[NSMenu alloc] initWithTitle:@"Select"];
+ if (_menu == nil) {
+ return nil;
+ }
+
+ [_menu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
+
+ NSInteger currentItemIndex = 0;
+ struct form_option *opt;
+ for (opt = form_select_get_option(_control, 0);
+ opt != NULL;
+ opt = opt->next) {
+ NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:opt->text]
+ action:@selector(itemSelected:)
+ keyEquivalent:@""];
+ if (opt->selected) {
+ item.state = NSOnState;
+ }
+ item.target = self;
+ item.tag = currentItemIndex++;
+ [_menu addItem:item];
+ }
+
+ _menu.delegate = self;
+
+ return self;
+}
+
+- (void)runInView:(NSView *)view
+{
+ (void)(__bridge_retained void *) self;
+
+ _cell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:YES];
+ _cell.menu = _menu;
+
+ const NSRect rect = cocoa_rect_for_control(_browser, _control);
+
+ [_cell attachPopUpWithFrame:rect inView:view];
+ [_cell performClickWithFrame:rect inView:view];
+}
+
+- (void)itemSelected:(id)sender
+{
+ form_select_process_selection(_control, (int)[sender tag]);
+}
+
+- (void)menuDidClose:(NSMenu *)sender
+{
+ (void)(__bridge_transfer id)((__bridge void *)self);
+}
+
+@end
diff --git a/frontends/cocoa/HistoryView.h b/frontends/cocoa/HistoryView.h
new file mode 100644
index 0000000..0fa2af3
--- a/dev/null
+++ b/frontends/cocoa/HistoryView.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@class BrowserView;
+
+@interface HistoryView : NSView
+
+@property (readwrite, nonatomic) BrowserView *browser;
+@property (readonly, nonatomic) NSSize size;
+
+- (void)updateHistory;
+
+@end
diff --git a/frontends/cocoa/HistoryView.m b/frontends/cocoa/HistoryView.m
new file mode 100644
index 0000000..f935f9f
--- a/dev/null
+++ b/frontends/cocoa/HistoryView.m
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/HistoryView.h"
+#import "cocoa/font.h"
+#import "cocoa/coordinates.h"
+#import "cocoa/plotter.h"
+#import "cocoa/LocalHistoryController.h"
+#import "cocoa/BrowserView.h"
+#import "utils/errors.h"
+#import "netsurf/core_window.h"
+#import "desktop/browser_history.h"
+#import "desktop/local_history.h"
+#import "netsurf/plotters.h"
+
+@interface HistoryView () {
+ struct local_history_session *_session;
+}
+
+@property (nonatomic) NSMutableArray *toolTips;
+@property (readwrite, nonatomic) NSSize size;
+
+@end
+
+static nserror invalidate(struct core_window *cw, const struct rect *rect)
+{
+ return NSERROR_NOT_IMPLEMENTED;
+}
+
+static void update_size(struct core_window *cw, int width, int height)
+{
+ HistoryView *view = (__bridge HistoryView *)cw;
+ NSSize size = cocoa_size(width, height);
+ view.size = size;
+}
+
+static void scroll_visible(struct core_window *cw, const struct rect *r)
+{
+}
+
+static void get_window_dimensions(struct core_window *cw, int *width, int *height)
+{
+}
+
+static void drag_status(struct core_window *cw, core_window_drag_status ds)
+{
+}
+
+static struct core_window_callback_table history_view_table = {
+ .invalidate = invalidate,
+ .update_size = update_size,
+ .scroll_visible = scroll_visible,
+ .get_window_dimensions = get_window_dimensions,
+ .drag_status = drag_status
+};
+
+@implementation HistoryView
+
+- (void)updateHistory
+{
+ if (_session) {
+ local_history_set(_session, self.browser.browser);
+ } else {
+ local_history_init(&history_view_table, (__bridge void *)self, self.browser.browser, &_session);
+ }
+
+ self.frameSize = self.size;
+}
+
+- (void)dealloc
+{
+ if (_session) {
+ local_history_fini(_session);
+ }
+}
+
+- (void)drawRect:(NSRect)rect
+{
+ if (!_session) return;
+
+ struct redraw_context context = {
+ .interactive = true,
+ .background_images = false,
+ .plot = &cocoa_plotters,
+ };
+
+ cocoa_set_clip(rect);
+
+ struct rect clipRect = {
+ .x0 = cocoa_pt_to_px(CGRectGetMinX(rect)),
+ .y0 = cocoa_pt_to_px(CGRectGetMinY(rect)),
+ .x1 = cocoa_pt_to_px(CGRectGetMaxX(rect)),
+ .y1 = cocoa_pt_to_px(CGRectGetMaxY(rect))
+ };
+ local_history_redraw(_session, 0, 0, &clipRect, &context);
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+ if (!_session) return;
+
+ CGPoint location = [self convertPoint: theEvent.locationInWindow fromView: nil];
+
+ browser_mouse_state state = BROWSER_MOUSE_PRESS_1;
+ if (theEvent.modifierFlags & NSEventModifierFlagCommand) {
+ state = BROWSER_MOUSE_PRESS_2;
+ }
+
+ nserror res = local_history_mouse_action(_session, state, cocoa_pt_to_px(location.x), cocoa_pt_to_px(location.y));
+
+ if (res == NSERROR_OK) {
+ self.browser.historyVisible = NO;
+ }
+}
+
+- (BOOL)isFlipped
+{
+ return YES;
+}
+
+@end
diff --git a/frontends/cocoa/HistoryWindowController.h b/frontends/cocoa/HistoryWindowController.h
new file mode 100644
index 0000000..c16236c
--- a/dev/null
+++ b/frontends/cocoa/HistoryWindowController.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@class TreeView;
+
+@interface HistoryWindowController : NSWindowController
+
+@property (nonatomic) IBOutlet TreeView *view;
+
+@end
diff --git a/frontends/cocoa/HistoryWindowController.m b/frontends/cocoa/HistoryWindowController.m
new file mode 100644
index 0000000..8b7067c
--- a/dev/null
+++ b/frontends/cocoa/HistoryWindowController.m
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/HistoryWindowController.h"
+#import "cocoa/Tree.h"
+#import "cocoa/TreeView.h"
+
+#import "desktop/global_history.h"
+
+@interface HistoryWindowController ()
+
+@property (nonatomic) Tree *tree;
+
+@end
+
+@implementation HistoryWindowController
+
+- (instancetype)init
+{
+ if ((self = [super initWithWindowNibName:@"HistoryWindow"]) == nil)
+ return nil;
+
+ _tree = [[Tree alloc] initWithFlags:TREE_HISTORY];
+
+ return self;
+}
+
+- (void)awakeFromNib
+{
+ self.view.tree = self.tree;
+ [[self window] setExcludedFromWindowsMenu:YES];
+}
+
+@end
diff --git a/frontends/cocoa/LocalHistoryController.h b/frontends/cocoa/LocalHistoryController.h
new file mode 100644
index 0000000..d760923
--- a/dev/null
+++ b/frontends/cocoa/LocalHistoryController.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@class HistoryView;
+@class BrowserView;
+
+@interface LocalHistoryController : NSWindowController
+
+@property (readwrite, nonatomic) BrowserView *browser;
+@property (readwrite, nonatomic) IBOutlet HistoryView *history;
+
+- (id)initWithBrowser:(BrowserView *)bw;
+
+- (void)attachToView:(NSView *)view;
+- (void)detach;
+- (void)redraw;
+
+- (void)keyDown:(NSEvent *)theEvent;
+
+@end
diff --git a/frontends/cocoa/LocalHistoryController.m b/frontends/cocoa/LocalHistoryController.m
new file mode 100644
index 0000000..1a60783
--- a/dev/null
+++ b/frontends/cocoa/LocalHistoryController.m
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/LocalHistoryController.h"
+
+#import "cocoa/BrowserView.h"
+#import "cocoa/HistoryView.h"
+#import "cocoa/ArrowWindow.h"
+
+@implementation LocalHistoryController
+
+- (instancetype)initWithBrowser:(BrowserView *)bw
+{
+ if ((self = [super initWithWindowNibName:@"LocalHistoryPanel"]) == nil)
+ return nil;
+
+ _browser = bw;
+
+ return self;
+}
+
+- (void)attachToView:(NSView *)view
+{
+ NSDisableScreenUpdates();
+
+ ArrowWindow *box = (ArrowWindow *)[self window];
+
+ [self.history updateHistory];
+ box.contentSize = self.history.size;
+ [box setArrowPosition:50];
+ [box attachToView:view];
+
+ NSRect frame = [box frame];
+ NSRect screenFrame = [[box screen] visibleFrame];
+
+ const CGFloat arrowSize = [box arrowSize];
+ frame.origin.x += arrowSize;
+ frame.origin.y += arrowSize;
+ frame.size.width -= 2 * arrowSize;
+ frame.size.height -= 2 * arrowSize;
+
+ if (NSMinY(frame) < NSMinY(screenFrame)) {
+ const CGFloat delta = NSMinY(screenFrame) - NSMinY(frame);
+ frame.size.height -= delta;
+ frame.origin.y += delta;
+ }
+
+ CGFloat arrowPositionChange = 50;
+ if (NSMaxX(frame) > NSMaxX(screenFrame)) {
+ const CGFloat delta = NSMaxX(frame) - NSMaxX(screenFrame);
+ arrowPositionChange += delta;
+ frame.origin.x -= delta;
+ }
+
+ if (NSMinX(frame) < NSMinX(screenFrame)) {
+ const CGFloat delta = NSMinX(screenFrame) - NSMinX(frame);
+ arrowPositionChange -= delta;
+ frame.origin.x += delta;
+ frame.size.width -= delta;
+ }
+
+ frame.origin.x -= arrowSize;
+ frame.origin.y -= arrowSize;
+ frame.size.width += 2 * arrowSize;
+ frame.size.height += 2 * arrowSize;
+
+ [box setArrowPosition:arrowPositionChange];
+ [box setFrame:frame display:YES];
+
+ NSEnableScreenUpdates();
+}
+
+- (void)detach
+{
+ [(ArrowWindow *)[self window] detach];
+}
+
+- (void)windowDidLoad
+{
+ [super windowDidLoad];
+
+ self.history.browser = self.browser;
+}
+
+- (void)redraw
+{
+ [self.history setNeedsDisplay:YES];
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ unichar key = [[theEvent characters] characterAtIndex:0];
+ switch (key) {
+ case 27:
+ [self.browser setHistoryVisible:NO];
+ break;
+
+ default:
+ NSBeep();
+ break;
+ };
+}
+
+@end
diff --git a/frontends/cocoa/Makefile b/frontends/cocoa/Makefile
new file mode 100644
index 0000000..212ed04
--- a/dev/null
+++ b/frontends/cocoa/Makefile
@@ -0,0 +1,258 @@
+# ----------------------------------------------------------------------------
+# Mac OS X target setup
+# ----------------------------------------------------------------------------
+
+POSTEXES += NetSurf.app
+
+# shut up zconf.h and zlib.h
+#CFLAGS += -D_LARGEFILE64_SOURCE=1
+
+# add Mac Ports include and library paths for openssl
+ifneq ($(shell test -d /opt/local && echo 'yes'),)
+ LDFLAGS += -L/opt/local/lib
+ CFLAGS += -I/opt/local/include
+else
+ # Check for homebrew installation of openssl
+ ifneq ($(shell test -d /usr/local/opt/openssl/lib && echo 'yes'),)
+ LDFLAGS += -L/usr/local/opt/openssl/lib
+ CFLAGS += -I/usr/local/opt/openssl/include
+ endif
+endif
+
+SDK_PARAM := $(shell xcodebuild -showsdks | awk '/^$$/{p=0};p; /(OS X|macOS) SDKs:/{p=1}' | head -1 | cut -f3)
+
+ifeq ($(DEPLOYMENT_TARGET),)
+ DEPLOYMENT_TARGET := 10.7
+endif
+
+SDK_PATH ?= $(shell xcodebuild -version $(SDK_PARAM) Path)
+SDK_FLAGS := -isysroot $(SDK_PATH) -mmacosx-version-min=$(DEPLOYMENT_TARGET)
+CFLAGS := $(SDK_FLAGS) $(CFLAGS)
+LDFLAGS := $(SDK_FLAGS) -Wl,-syslibroot,$(SDK_PATH) $(LDFLAGS)
+CXXFLAGS := $(SDK_FLAGS) $(CXXFLAGS)
+
+# for timerisset()
+CFLAGS += -D_DARWIN_C_SOURCE
+
+LDFLAGS += -L/usr/lib
+LDFLAGS += -L/usr/X11/lib
+LDFLAGS += -lm -lcurl
+LDFLAGS += -lssl -lcrypto
+
+CFLAGS += -Dnscocoa -D_BSD_SOURCE -D_POSIX_C_SOURCE -std=c99 -fobjc-arc
+
+CFLAGS += -I/usr/X11/include
+CFLAGS += -include cocoa/Prefix.pch
+
+VERSION_FULL := $(shell sed -n '/_version.*=.*"/{s/.*"\(.*\)".*/\1/;p;}' desktop/version.c)
+VERSION_MAJ := $(shell sed -n '/_major/{s/.* = \([0-9]*\).*/\1/;p;}' desktop/version.c)
+VERSION_MIN := $(shell sed -n '/_minor/{s/.* = \([0-9]*\).*/\1/;p;}' desktop/version.c)
+
+LDFLAGS += -Wl,-framework,Cocoa -Wl,-framework,Carbon $(NETLDFLAGS)
+
+$(eval $(call feature_enabled,IMAGEIO,-DWITH_APPLE_IMAGE,,Apple ImageIO ))
+
+ifneq ($(UNIVERSAL),)
+ UNIVERSAL_FLAGS := $(foreach arch,$(UNIVERSAL),-arch $(arch) )
+ CFLAGS += $(UNIVERSAL_FLAGS)
+ LDFLAGS += $(UNIVERSAL_FLAGS)
+ CXXFLAGS += $(UNIVERSAL_FLAGS)
+endif
+
+
+ifeq ($(VARIANT),debug)
+ CFLAGS += -g
+endif
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# sources purely for the Mac OS X build
+S_FRONTEND := \
+ BookmarksController.m \
+ BrowserView.m \
+ BrowserViewController.m \
+ BrowserWindowController.m \
+ BrowserWindow.m \
+ DownloadWindowController.m \
+ NetSurfAppDelegate.m \
+ NetsurfApp.m \
+ PreferencesWindowController.m \
+ ScrollableView.m \
+ SearchWindowController.m \
+ URLFieldCell.m \
+ Tree.m \
+ desktop-tree.m \
+ TreeView.m \
+ HistoryView.m \
+ HistoryWindowController.m \
+ FormSelectMenu.m \
+ bitmap.m \
+ fetch.m \
+ font.m \
+ gui.m \
+ plotter.m \
+ schedule.m \
+ selection.m \
+ ArrowBox.m \
+ ArrowWindow.m \
+ LocalHistoryController.m \
+ apple_image.m
+
+S_TABBAR := \
+ NSBezierPath_AMShading.m \
+ NSString_AITruncation.m \
+ PSMOverflowPopUpButton.m \
+ PSMProgressIndicator.m \
+ PSMRolloverButton.m \
+ PSMTabBarCell.m \
+ PSMTabBarControl.m \
+ PSMTabBarController.m \
+ PSMTabDragAssistant.m \
+ PSMTabDragView.m \
+ PSMTabDragWindow.m \
+ PSMTabDragWindowController.m \
+ PSMUnifiedTabStyle.m
+
+S_FRONTEND += $(addprefix PSMTabBarControl/,$(S_TABBAR))
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(addprefix $(shell pwd)/,$(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND))
+
+# Since we prefix the sources with the pwd, also create a special
+# prefixed rule so that the testament is run
+$(shell pwd)/content/fetchers/about.c: testament
+
+EXETARGET := NetSurf
+
+S_XIBS := \
+ MainMenu.xib \
+ Browser.xib \
+ BrowserWindow.xib \
+ DownloadWindow.xib \
+ SearchWindow.xib \
+ PreferencesWindow.xib \
+ HistoryWindow.xib \
+ BookmarksWindow.xib \
+ LocalHistoryPanel.xib
+
+R_RESOURCES := \
+ default.css \
+ adblock.css \
+ internal.css \
+ quirks.css \
+ NetSurf.icns \
+ HomeTemplate.pdf \
+ Icons \
+ ca-bundle \
+ netsurf.png
+
+
+TABBAR_RESOURCES := \
+ AquaTabClose_Front_Pressed.png \
+ AquaTabClose_Front_Rollover.png \
+ AquaTabClose_Front.png \
+ AquaTabCloseDirty_Front_Pressed.png \
+ AquaTabCloseDirty_Front_Rollover.png \
+ AquaTabCloseDirty_Front.png \
+ AquaTabNew.png \
+ AquaTabNewPressed.png \
+ AquaTabNewRollover.png \
+ overflowImage.png \
+ overflowImagePressed.png \
+ pi.png
+
+R_RESOURCES := $(addprefix $(FRONTEND_RESOURCES_DIR)/,$(R_RESOURCES))
+
+R_RESOURCES += $(addprefix $(FRONTEND_SOURCE_DIR)/PSMTabBarControl/Images/,$(TABBAR_RESOURCES))
+
+LANGUAGES := de en fr it nl
+LOCALIZED_RESOURCES := Localizable.strings
+
+#languiage project macro
+# $1 is language name
+# $2 is list of resources per language
+define make_lproj
+R_RESOURCES += $$(OBJROOT)/$(1).lproj
+$$(OBJROOT)/$(1).lproj: $(2)
+ $(VQ)echo Bundling language $(1)
+ $(Q)$(MKDIR) -p $$@
+ $(Q)cp -pLR $(2) $$@
+ $(Q)$(SPLIT_MESSAGES) -l $(1) -p cocoa -f messages resources/FatMessages > $$@/Messages
+endef
+
+# compile_xib (xib) (lang)
+define compile_xib
+$$(OBJROOT)/$(2).lproj: $$(OBJROOT)/$(2).lproj/$(1:.xib=.nib)
+
+$$(OBJROOT)/$(2).lproj/$(1:.xib=.nib): $(FRONTEND_RESOURCES_DIR)/$(1) $$(OBJROOT)/created
+ $(VQ)echo Compiling XIB $(1) for language $(2)
+ $(Q)$(MKDIR) -p $$(OBJROOT)/$(2).lproj
+ $(Q)$(FRONTEND_SOURCE_DIR)/compile-xib.sh $(FRONTEND_RESOURCES_DIR)/$(1) $(2) $$@
+
+ifeq ($(wildcard $(FRONTEND_RESOURCES_DIR)/$(2).lproj/$(1).strings),$(FRONTEND_RESOURCES_DIR)/$(2).lproj/$(1).strings)
+$$(OBJROOT)/$(2).lproj/$(1:.xib=.nib): $(FRONTEND_RESOURCES_DIR)/$(2).lproj/$(1).strings
+endif
+
+endef
+
+$(foreach lang,$(LANGUAGES),$(eval $(call make_lproj,$(lang),$(addprefix $(FRONTEND_RESOURCES_DIR)/$(lang).lproj/,$(LOCALIZED_RESOURCES)))))
+$(foreach lang,$(LANGUAGES),$(foreach xib,$(S_XIBS),$(eval $(call compile_xib,$(xib),$(lang)))))
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+install-cocoa: NetSurf.app
+
+NetSurf.app: NetSurf $(FRONTEND_SOURCE_DIR)/Makefile $(R_RESOURCES) NetSurf.app/Contents/Info.plist
+ $(VQ)echo Assembling NetSurf.app bundle
+ $(Q)$(MKDIR) -p NetSurf.app/Contents/MacOS
+ $(Q)cp NetSurf NetSurf.app/Contents/MacOS
+ $(Q)rm -rf NetSurf.app/Contents/Resources
+ $(Q)$(MKDIR) -p NetSurf.app/Contents/Resources
+ $(Q)cp -pLR $(R_RESOURCES) NetSurf.app/Contents/Resources
+ $(Q)echo 'APPL????' > NetSurf.app/Contents/PkgInfo
+
+NetSurf.app/Contents/Info.plist: $(FRONTEND_RESOURCES_DIR)/NetSurf-Info.plist $(FRONTEND_SOURCE_DIR)/Makefile
+ $(VQ)echo Generating Info.plist
+ $(Q)$(MKDIR) -p NetSurf.app/Contents
+ $(Q)sed -e 's/$${EXECUTABLE_NAME}/$(EXETARGET)/' \
+ -e 's/$${PRODUCT_NAME.*}/$(EXETARGET)/' \
+ -e 's/$${MACOSX_DEPLOYMENT_TARGET}/$(DEPLOYMENT_TARGET)/' \
+ -e 's/$${NETSURF_VERSION}/$(VERSION_FULL)/' \
+ -e 's/$${NETSURF_SHORT_VERSION}/$(VERSION_MAJ).$(VERSION_MIN)/' \
+ < $(FRONTEND_RESOURCES_DIR)/NetSurf-Info.plist > NetSurf.app/Contents/Info.plist
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-cocoa: NetSurf.dmg
+
+.INTERMEDIATE: NetSurf.tmp.dmg
+
+NetSurf.tmp.dmg: NetSurf.app
+ hdiutil create -size 8m -fs HFS+ -volname "NetSurf" $@
+ sleep 2
+ hdiutil attach $@
+ sleep 2
+ cp -pPR $^ /Volumes/NetSurf/
+ hdiutil detach $$(echo $$(hdiutil attach $@ | cut -f 1) | cut -f 1 -d ' ')
+ sleep 2
+
+NetSurf.dmg: NetSurf.tmp.dmg
+ hdiutil convert $^ -format UDZO -o $@
+
+CLEANS += clean-package-cocoa
+
+clean-package-cocoa:
+ $(VQ)echo " CLEAN: NetSurf.tmp.dmg"
+ $(Q)$(RM) NetSurf.tmp.dmg
+ $(VQ)echo " CLEAN: NetSurf.dmg"
+ $(Q)$(RM) NetSurf.dmg
+ $(VQ)echo " CLEAN: NetSurf.app"
+ $(Q)$(RM) -r NetSurf.app
diff --git a/frontends/cocoa/Makefile.defaults b/frontends/cocoa/Makefile.defaults
new file mode 100644
index 0000000..49e570c
--- a/dev/null
+++ b/frontends/cocoa/Makefile.defaults
@@ -0,0 +1,28 @@
+# ----------------------------------------------------------------------------
+# Cocoa-specific options
+# ----------------------------------------------------------------------------
+
+# Force using glibc internal iconv implementation instead of external libiconv
+# Valid options: YES, NO
+NETSURF_USE_LIBICONV_PLUG := NO
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := NO
+
+# Enable NetSurf's use of librsvg in conjunction with Cairo to display SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_RSVG := AUTO
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_NSSVG := AUTO
+
+NETSURF_USE_BMP := NO
+NETSURF_USE_GIF := NO
+NETSURF_USE_PNG := NO
+NETSURF_USE_JPEG := NO
+NETSURF_USE_IMAGEIO := YES
+
+MACOSX_VERSION := $(shell sw_vers -productVersion | awk -F '.' '{print $$1 "." $$2}')
+
diff --git a/frontends/cocoa/NetSurf.xcodeproj/project.pbxproj b/frontends/cocoa/NetSurf.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..b4475ec
--- a/dev/null
+++ b/frontends/cocoa/NetSurf.xcodeproj/project.pbxproj
@@ -0,0 +1,2095 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 263A28101EE40CCF005C52B9 /* NSString_AITruncation.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26131EE40BFC005C52B9 /* NSString_AITruncation.m */; };
+ 263A28111EE40CCF005C52B9 /* PSMOverflowPopUpButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26151EE40BFC005C52B9 /* PSMOverflowPopUpButton.m */; };
+ 263A28121EE40CCF005C52B9 /* PSMProgressIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26171EE40BFC005C52B9 /* PSMProgressIndicator.m */; };
+ 263A28131EE40CCF005C52B9 /* PSMRolloverButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26191EE40BFC005C52B9 /* PSMRolloverButton.m */; };
+ 263A28141EE40CCF005C52B9 /* PSMTabBarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A261B1EE40BFC005C52B9 /* PSMTabBarCell.m */; };
+ 263A28151EE40CCF005C52B9 /* PSMTabBarControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A261D1EE40BFC005C52B9 /* PSMTabBarControl.m */; };
+ 263A28161EE40CCF005C52B9 /* PSMTabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A261F1EE40BFC005C52B9 /* PSMTabBarController.m */; };
+ 263A28171EE40CCF005C52B9 /* PSMTabDragAssistant.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26211EE40BFC005C52B9 /* PSMTabDragAssistant.m */; };
+ 263A28181EE40CCF005C52B9 /* PSMTabDragView.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26231EE40BFC005C52B9 /* PSMTabDragView.m */; };
+ 263A28191EE40CCF005C52B9 /* PSMTabDragWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26251EE40BFC005C52B9 /* PSMTabDragWindow.m */; };
+ 263A281A1EE40CCF005C52B9 /* PSMTabDragWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26271EE40BFC005C52B9 /* PSMTabDragWindowController.m */; };
+ 263A281B1EE40CCF005C52B9 /* PSMUnifiedTabStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A262A1EE40BFC005C52B9 /* PSMUnifiedTabStyle.m */; };
+ 263A281C1EE40CCF005C52B9 /* apple_image.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26821EE40BFC005C52B9 /* apple_image.m */; };
+ 263A281D1EE40CCF005C52B9 /* ArrowBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26831EE40BFC005C52B9 /* ArrowBox.m */; };
+ 263A281E1EE40CCF005C52B9 /* ArrowWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26841EE40BFC005C52B9 /* ArrowWindow.m */; };
+ 263A281F1EE40CCF005C52B9 /* bitmap.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26851EE40BFC005C52B9 /* bitmap.m */; };
+ 263A28211EE40CCF005C52B9 /* BookmarksController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26871EE40BFC005C52B9 /* BookmarksController.m */; };
+ 263A28221EE40CCF005C52B9 /* BrowserView.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26881EE40BFC005C52B9 /* BrowserView.m */; };
+ 263A28231EE40CCF005C52B9 /* BrowserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26891EE40BFC005C52B9 /* BrowserViewController.m */; };
+ 263A28241EE40CCF005C52B9 /* BrowserWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A268A1EE40BFC005C52B9 /* BrowserWindow.m */; };
+ 263A28251EE40CCF005C52B9 /* BrowserWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A268B1EE40BFC005C52B9 /* BrowserWindowController.m */; };
+ 263A28261EE40CCF005C52B9 /* desktop-tree.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A268C1EE40BFC005C52B9 /* desktop-tree.m */; };
+ 263A28271EE40CCF005C52B9 /* DownloadWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A268D1EE40BFC005C52B9 /* DownloadWindowController.m */; };
+ 263A28281EE40CCF005C52B9 /* fetch.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A268E1EE40BFC005C52B9 /* fetch.m */; };
+ 263A28291EE40CCF005C52B9 /* font.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A268F1EE40BFC005C52B9 /* font.m */; };
+ 263A282A1EE40CCF005C52B9 /* FormSelectMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26901EE40BFC005C52B9 /* FormSelectMenu.m */; };
+ 263A282B1EE40CCF005C52B9 /* gui.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26911EE40BFC005C52B9 /* gui.m */; };
+ 263A282C1EE40CCF005C52B9 /* HistoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26921EE40BFC005C52B9 /* HistoryView.m */; };
+ 263A282D1EE40CCF005C52B9 /* HistoryWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26931EE40BFC005C52B9 /* HistoryWindowController.m */; };
+ 263A282E1EE40CCF005C52B9 /* LocalHistoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26941EE40BFC005C52B9 /* LocalHistoryController.m */; };
+ 263A282F1EE40CCF005C52B9 /* NetsurfApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26951EE40BFC005C52B9 /* NetsurfApp.m */; };
+ 263A28301EE40CCF005C52B9 /* NetSurfAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26961EE40BFC005C52B9 /* NetSurfAppDelegate.m */; };
+ 263A28311EE40CCF005C52B9 /* plotter.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26971EE40BFC005C52B9 /* plotter.m */; };
+ 263A28321EE40CCF005C52B9 /* PreferencesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26981EE40BFC005C52B9 /* PreferencesWindowController.m */; };
+ 263A28331EE40CCF005C52B9 /* schedule.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26991EE40BFC005C52B9 /* schedule.m */; };
+ 263A28341EE40CCF005C52B9 /* ScrollableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A269A1EE40BFC005C52B9 /* ScrollableView.m */; };
+ 263A28351EE40CCF005C52B9 /* SearchWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A269B1EE40BFC005C52B9 /* SearchWindowController.m */; };
+ 263A28361EE40CCF005C52B9 /* selection.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A269C1EE40BFC005C52B9 /* selection.m */; };
+ 263A28371EE40CCF005C52B9 /* Tree.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A269D1EE40BFC005C52B9 /* Tree.m */; };
+ 263A28381EE40CCF005C52B9 /* TreeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A269E1EE40BFC005C52B9 /* TreeView.m */; };
+ 263A28391EE40CCF005C52B9 /* URLFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A269F1EE40BFC005C52B9 /* URLFieldCell.m */; };
+ 263A283A1EE40CF2005C52B9 /* content.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26A31EE40C4A005C52B9 /* content.c */; };
+ 263A283B1EE40CF2005C52B9 /* content_factory.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26A61EE40C4A005C52B9 /* content_factory.c */; };
+ 263A283C1EE40CF2005C52B9 /* dirlist.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26A91EE40C4A005C52B9 /* dirlist.c */; };
+ 263A283D1EE40CF2005C52B9 /* fetch.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26AB1EE40C4A005C52B9 /* fetch.c */; };
+ 263A283E1EE40CF2005C52B9 /* about.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26AE1EE40C4A005C52B9 /* about.c */; };
+ 263A283F1EE40CF2005C52B9 /* curl.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26B01EE40C4A005C52B9 /* curl.c */; };
+ 263A28401EE40CF2005C52B9 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26B21EE40C4A005C52B9 /* data.c */; };
+ 263A28411EE40CF2005C52B9 /* file.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26B41EE40C4A005C52B9 /* file.c */; };
+ 263A28421EE40CF2005C52B9 /* resource.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26B71EE40C4A005C52B9 /* resource.c */; };
+ 263A28431EE40CF2005C52B9 /* fs_backing_store.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26BA1EE40C4A005C52B9 /* fs_backing_store.c */; };
+ 263A28441EE40CF2005C52B9 /* css.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26BD1EE40C4A005C52B9 /* css.c */; };
+ 263A28451EE40CF2005C52B9 /* dump.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26BF1EE40C4A005C52B9 /* dump.c */; };
+ 263A28461EE40CF2005C52B9 /* hints.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26C11EE40C4A005C52B9 /* hints.c */; };
+ 263A28471EE40CF2005C52B9 /* internal.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26C31EE40C4A005C52B9 /* internal.c */; };
+ 263A28481EE40CF2005C52B9 /* select.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26C61EE40C4A005C52B9 /* select.c */; };
+ 263A28491EE40CF2005C52B9 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26C81EE40C4A005C52B9 /* utils.c */; };
+ 263A284A1EE40CF2005C52B9 /* bmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26CB1EE40C4A005C52B9 /* bmp.c */; };
+ 263A284B1EE40CF2005C52B9 /* gif.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26CD1EE40C4A005C52B9 /* gif.c */; };
+ 263A284C1EE40CF2005C52B9 /* ico.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26CF1EE40C4A005C52B9 /* ico.c */; };
+ 263A284D1EE40CF2005C52B9 /* image.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26D11EE40C4A005C52B9 /* image.c */; };
+ 263A284E1EE40CF2005C52B9 /* image_cache.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A26D31EE40C4A005C52B9 /* image_cache.c */; };
+ 263A285A1EE40CF2005C52B9 /* hlcache.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27371EE40C4B005C52B9 /* hlcache.c */; };
+ 263A285B1EE40CF2005C52B9 /* llcache.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27391EE40C4B005C52B9 /* llcache.c */; };
+ 263A285C1EE40CF2005C52B9 /* mimesniff.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A273C1EE40C4B005C52B9 /* mimesniff.c */; };
+ 263A285D1EE40CF2005C52B9 /* no_backing_store.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A273E1EE40C4B005C52B9 /* no_backing_store.c */; };
+ 263A285E1EE40CF2005C52B9 /* urldb.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A273F1EE40C4B005C52B9 /* urldb.c */; };
+ 263A285F1EE40CFB005C52B9 /* browser.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27421EE40C4B005C52B9 /* browser.c */; };
+ 263A28601EE40CFB005C52B9 /* browser_history.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27431EE40C4B005C52B9 /* browser_history.c */; };
+ 263A28611EE40CFB005C52B9 /* cookie_manager.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27461EE40C4B005C52B9 /* cookie_manager.c */; };
+ 263A28621EE40CFB005C52B9 /* download.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27481EE40C4B005C52B9 /* download.c */; };
+ 263A28631EE40CFB005C52B9 /* font_haru.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A274A1EE40C4B005C52B9 /* font_haru.c */; };
+ 263A28641EE40CFB005C52B9 /* frames.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A274D1EE40C4B005C52B9 /* frames.c */; };
+ 263A28651EE40CFB005C52B9 /* global_history.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A274F1EE40C4B005C52B9 /* global_history.c */; };
+ 263A28661EE40CFB005C52B9 /* gui_factory.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27511EE40C4B005C52B9 /* gui_factory.c */; };
+ 263A28671EE40CFB005C52B9 /* hotlist.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27541EE40C4B005C52B9 /* hotlist.c */; };
+ 263A28681EE40CFB005C52B9 /* knockout.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27561EE40C4B005C52B9 /* knockout.c */; };
+ 263A28691EE40CFB005C52B9 /* local_history.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27581EE40C4B005C52B9 /* local_history.c */; };
+ 263A286A1EE40CFB005C52B9 /* mouse.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A275B1EE40C4B005C52B9 /* mouse.c */; };
+ 263A286B1EE40CFB005C52B9 /* netsurf.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A275C1EE40C4B005C52B9 /* netsurf.c */; };
+ 263A286C1EE40CFB005C52B9 /* plot_style.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A275E1EE40C4B005C52B9 /* plot_style.c */; };
+ 263A286D1EE40CFB005C52B9 /* print.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A275F1EE40C4B005C52B9 /* print.c */; };
+ 263A286E1EE40CFB005C52B9 /* save_complete.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27621EE40C4B005C52B9 /* save_complete.c */; };
+ 263A286F1EE40CFB005C52B9 /* save_pdf.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27641EE40C4B005C52B9 /* save_pdf.c */; };
+ 263A28701EE40CFB005C52B9 /* save_text.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27661EE40C4B005C52B9 /* save_text.c */; };
+ 263A28711EE40CFB005C52B9 /* scrollbar.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27681EE40C4B005C52B9 /* scrollbar.c */; };
+ 263A28721EE40CFB005C52B9 /* search.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A276A1EE40C4B005C52B9 /* search.c */; };
+ 263A28731EE40CFB005C52B9 /* searchweb.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A276C1EE40C4B005C52B9 /* searchweb.c */; };
+ 263A28741EE40CFB005C52B9 /* selection.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A276E1EE40C4B005C52B9 /* selection.c */; };
+ 263A28751EE40CFB005C52B9 /* sslcert_viewer.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27701EE40C4B005C52B9 /* sslcert_viewer.c */; };
+ 263A28761EE40CFB005C52B9 /* system_colour.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27721EE40C4B005C52B9 /* system_colour.c */; };
+ 263A28771EE40CFB005C52B9 /* textarea.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27741EE40C4B005C52B9 /* textarea.c */; };
+ 263A28781EE40CFB005C52B9 /* textinput.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27761EE40C4B005C52B9 /* textinput.c */; };
+ 263A28791EE40CFB005C52B9 /* treeview.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27791EE40C4B005C52B9 /* treeview.c */; };
+ 263A287A1EE40CFB005C52B9 /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A277B1EE40C4B005C52B9 /* version.c */; };
+ 263A287B1EE4120F005C52B9 /* idna.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27E41EE40C4B005C52B9 /* idna.c */; };
+ 263A287C1EE4120F005C52B9 /* libdom.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27EA1EE40C4B005C52B9 /* libdom.c */; };
+ 263A287D1EE4120F005C52B9 /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27EC1EE40C4B005C52B9 /* log.c */; };
+ 263A287E1EE4120F005C52B9 /* messages.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27F11EE40C4B005C52B9 /* messages.c */; };
+ 263A287F1EE4120F005C52B9 /* nsoption.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27F31EE40C4B005C52B9 /* nsoption.c */; };
+ 263A28801EE4120F005C52B9 /* nsurl.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27F71EE40C4B005C52B9 /* nsurl.c */; };
+ 263A28811EE4120F005C52B9 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27F81EE40C4B005C52B9 /* parse.c */; };
+ 263A28821EE4120F005C52B9 /* punycode.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27FB1EE40C4B005C52B9 /* punycode.c */; };
+ 263A28831EE4120F005C52B9 /* talloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A28011EE40C4B005C52B9 /* talloc.c */; };
+ 263A28841EE4120F005C52B9 /* time.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A28041EE40C4B005C52B9 /* time.c */; };
+ 263A28851EE4120F005C52B9 /* url.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A28061EE40C4B005C52B9 /* url.c */; };
+ 263A28861EE4120F005C52B9 /* useragent.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A28081EE40C4B005C52B9 /* useragent.c */; };
+ 263A28871EE4120F005C52B9 /* utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A280A1EE40C4B005C52B9 /* utf8.c */; };
+ 263A28881EE4120F005C52B9 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A280C1EE40C4B005C52B9 /* utils.c */; };
+ 263A28891EE41213005C52B9 /* challenge.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27D01EE40C4B005C52B9 /* challenge.c */; };
+ 263A288A1EE41213005C52B9 /* content-disposition.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27D31EE40C4B005C52B9 /* content-disposition.c */; };
+ 263A288B1EE41213005C52B9 /* content-type.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27D51EE40C4B005C52B9 /* content-type.c */; };
+ 263A288C1EE41213005C52B9 /* generics.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27D71EE40C4B005C52B9 /* generics.c */; };
+ 263A288D1EE41213005C52B9 /* parameter.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27DA1EE40C4B005C52B9 /* parameter.c */; };
+ 263A288E1EE41213005C52B9 /* primitives.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27DD1EE40C4B005C52B9 /* primitives.c */; };
+ 263A288F1EE41213005C52B9 /* www-authenticate.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27DF1EE40C4B005C52B9 /* www-authenticate.c */; };
+ 263A28901EE41218005C52B9 /* bloom.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27BA1EE40C4B005C52B9 /* bloom.c */; };
+ 263A28911EE41218005C52B9 /* corestrings.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27BE1EE40C4B005C52B9 /* corestrings.c */; };
+ 263A28921EE41218005C52B9 /* file.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27C51EE40C4B005C52B9 /* file.c */; };
+ 263A28931EE41218005C52B9 /* filename.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27C71EE40C4B005C52B9 /* filename.c */; };
+ 263A28941EE41218005C52B9 /* filepath.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27C91EE40C4B005C52B9 /* filepath.c */; };
+ 263A28951EE41218005C52B9 /* hashtable.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27CD1EE40C4B005C52B9 /* hashtable.c */; };
+ 263A28961EE4121D005C52B9 /* box.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27981EE40C4B005C52B9 /* box.c */; };
+ 263A28971EE4121D005C52B9 /* box_construct.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A279A1EE40C4B005C52B9 /* box_construct.c */; };
+ 263A28981EE4121D005C52B9 /* box_normalise.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A279B1EE40C4B005C52B9 /* box_normalise.c */; };
+ 263A28991EE4121D005C52B9 /* box_textarea.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A279C1EE40C4B005C52B9 /* box_textarea.c */; };
+ 263A289A1EE4121D005C52B9 /* font.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A279E1EE40C4B005C52B9 /* font.c */; };
+ 263A289B1EE4121D005C52B9 /* form.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27A01EE40C4B005C52B9 /* form.c */; };
+ 263A289C1EE4121D005C52B9 /* html.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27A21EE40C4B005C52B9 /* html.c */; };
+ 263A289D1EE4121D005C52B9 /* html_css.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27A41EE40C4B005C52B9 /* html_css.c */; };
+ 263A289E1EE4121D005C52B9 /* html_css_fetcher.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27A51EE40C4B005C52B9 /* html_css_fetcher.c */; };
+ 263A289F1EE4121D005C52B9 /* html_forms.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27A61EE40C4B005C52B9 /* html_forms.c */; };
+ 263A28A01EE4121D005C52B9 /* html_interaction.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27A71EE40C4B005C52B9 /* html_interaction.c */; };
+ 263A28A11EE4121D005C52B9 /* html_object.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27A91EE40C4B005C52B9 /* html_object.c */; };
+ 263A28A21EE4121D005C52B9 /* html_redraw.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27AA1EE40C4B005C52B9 /* html_redraw.c */; };
+ 263A28A31EE4121D005C52B9 /* html_redraw_border.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27AB1EE40C4B005C52B9 /* html_redraw_border.c */; };
+ 263A28A41EE4121D005C52B9 /* html_script.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27AC1EE40C4B005C52B9 /* html_script.c */; };
+ 263A28A51EE4121D005C52B9 /* imagemap.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27AD1EE40C4B005C52B9 /* imagemap.c */; };
+ 263A28A61EE4121D005C52B9 /* layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27AF1EE40C4B005C52B9 /* layout.c */; };
+ 263A28A71EE4121D005C52B9 /* search.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27B21EE40C4B005C52B9 /* search.c */; };
+ 263A28A81EE4121D005C52B9 /* table.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27B41EE40C4B005C52B9 /* table.c */; };
+ 263A28A91EE4121D005C52B9 /* textplain.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A27B61EE40C4B005C52B9 /* textplain.c */; };
+ 263A28AC1EE41245005C52B9 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 263A28AB1EE41245005C52B9 /* libiconv.tbd */; };
+ 263A28AE1EE41266005C52B9 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 263A28AD1EE41266005C52B9 /* libz.tbd */; };
+ 263A29031EE412BE005C52B9 /* content.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A28B01EE412B4005C52B9 /* content.c */; };
+ 263A29041EE412BE005C52B9 /* fetcher.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A28F31EE412B4005C52B9 /* fetcher.c */; };
+ 263A29051EE412BE005C52B9 /* none.c in Sources */ = {isa = PBXBuildFile; fileRef = 263A28F91EE412B4005C52B9 /* none.c */; };
+ 263A29071EE4196E005C52B9 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 263A29061EE4196E005C52B9 /* Cocoa.framework */; };
+ 263A29081EE41A75005C52B9 /* adblock.css in Resources */ = {isa = PBXBuildFile; fileRef = 263A262D1EE40BFC005C52B9 /* adblock.css */; };
+ 263A29091EE41A7D005C52B9 /* BookmarksWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A262E1EE40BFC005C52B9 /* BookmarksWindow.xib */; };
+ 263A290A1EE41A7D005C52B9 /* Browser.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A262F1EE40BFC005C52B9 /* Browser.xib */; };
+ 263A290B1EE41A7D005C52B9 /* BrowserWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A26301EE40BFC005C52B9 /* BrowserWindow.xib */; };
+ 263A290C1EE41A7D005C52B9 /* ca-bundle in Resources */ = {isa = PBXBuildFile; fileRef = 263A26311EE40BFC005C52B9 /* ca-bundle */; };
+ 263A290D1EE41A8C005C52B9 /* BookmarksWindow.xib.strings in Resources */ = {isa = PBXBuildFile; fileRef = 263A26321EE40BFC005C52B9 /* BookmarksWindow.xib.strings */; };
+ 263A290E1EE41A8C005C52B9 /* BrowserWindow.xib.strings in Resources */ = {isa = PBXBuildFile; fileRef = 263A26341EE40BFC005C52B9 /* BrowserWindow.xib.strings */; };
+ 263A290F1EE41A8C005C52B9 /* DownloadWindow.xib.strings in Resources */ = {isa = PBXBuildFile; fileRef = 263A26361EE40BFC005C52B9 /* DownloadWindow.xib.strings */; };
+ 263A29101EE41A8C005C52B9 /* HistoryWindow.xib.strings in Resources */ = {isa = PBXBuildFile; fileRef = 263A26381EE40BFC005C52B9 /* HistoryWindow.xib.strings */; };
+ 263A29111EE41A8C005C52B9 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 263A263A1EE40BFC005C52B9 /* Localizable.strings */; };
+ 263A29121EE41A8F005C52B9 /* MainMenu.xib.strings in Resources */ = {isa = PBXBuildFile; fileRef = 263A263C1EE40BFC005C52B9 /* MainMenu.xib.strings */; };
+ 263A29131EE41A96005C52B9 /* Messages in Resources */ = {isa = PBXBuildFile; fileRef = 263A263E1EE40BFC005C52B9 /* Messages */; };
+ 263A29141EE41A96005C52B9 /* PreferencesWindow.xib.strings in Resources */ = {isa = PBXBuildFile; fileRef = 263A26401EE40BFC005C52B9 /* PreferencesWindow.xib.strings */; };
+ 263A29151EE41A96005C52B9 /* SearchWindow.xib.strings in Resources */ = {isa = PBXBuildFile; fileRef = 263A26421EE40BFC005C52B9 /* SearchWindow.xib.strings */; };
+ 263A29161EE41A9A005C52B9 /* default.css in Resources */ = {isa = PBXBuildFile; fileRef = 263A26441EE40BFC005C52B9 /* default.css */; };
+ 263A29171EE41A9A005C52B9 /* DownloadWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A26451EE40BFC005C52B9 /* DownloadWindow.xib */; };
+ 263A29181EE41A9A005C52B9 /* HistoryWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A264A1EE40BFC005C52B9 /* HistoryWindow.xib */; };
+ 263A29191EE41A9A005C52B9 /* HomeTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 263A264B1EE40BFC005C52B9 /* HomeTemplate.pdf */; };
+ 263A291A1EE41A9F005C52B9 /* arrow-l.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A264D1EE40BFC005C52B9 /* arrow-l.png */; };
+ 263A291B1EE41A9F005C52B9 /* content.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A264E1EE40BFC005C52B9 /* content.png */; };
+ 263A291C1EE41A9F005C52B9 /* directory.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A264F1EE40BFC005C52B9 /* directory.png */; };
+ 263A291D1EE41A9F005C52B9 /* directory2.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26501EE40BFC005C52B9 /* directory2.png */; };
+ 263A291E1EE41A9F005C52B9 /* hotlist-add.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26511EE40BFC005C52B9 /* hotlist-add.png */; };
+ 263A291F1EE41A9F005C52B9 /* hotlist-rmv.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26521EE40BFC005C52B9 /* hotlist-rmv.png */; };
+ 263A29201EE41A9F005C52B9 /* search.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26531EE40BFC005C52B9 /* search.png */; };
+ 263A29211EE41AA4005C52B9 /* internal.css in Resources */ = {isa = PBXBuildFile; fileRef = 263A26541EE40BFC005C52B9 /* internal.css */; };
+ 263A29221EE41AA4005C52B9 /* LocalHistoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A26571EE40BFC005C52B9 /* LocalHistoryPanel.xib */; };
+ 263A29231EE41AA4005C52B9 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A26581EE40BFC005C52B9 /* MainMenu.xib */; };
+ 263A29241EE41AA8005C52B9 /* NetSurf.icns in Resources */ = {isa = PBXBuildFile; fileRef = 263A265A1EE40BFC005C52B9 /* NetSurf.icns */; };
+ 263A29251EE41AA8005C52B9 /* netsurf.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A265B1EE40BFC005C52B9 /* netsurf.png */; };
+ 263A29261EE41AA8005C52B9 /* PreferencesWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A265E1EE40BFC005C52B9 /* PreferencesWindow.xib */; };
+ 263A29271EE41AA8005C52B9 /* quirks.css in Resources */ = {isa = PBXBuildFile; fileRef = 263A265F1EE40BFC005C52B9 /* quirks.css */; };
+ 263A29281EE41AA8005C52B9 /* SearchWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263A26601EE40BFC005C52B9 /* SearchWindow.xib */; };
+ 263A29291EE41B53005C52B9 /* AquaTabClose_Front.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26041EE40BFC005C52B9 /* AquaTabClose_Front.png */; };
+ 263A292A1EE41B53005C52B9 /* AquaTabClose_Front_Pressed.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26051EE40BFC005C52B9 /* AquaTabClose_Front_Pressed.png */; };
+ 263A292B1EE41B53005C52B9 /* AquaTabClose_Front_Rollover.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26061EE40BFC005C52B9 /* AquaTabClose_Front_Rollover.png */; };
+ 263A292C1EE41B53005C52B9 /* AquaTabCloseDirty_Front.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26071EE40BFC005C52B9 /* AquaTabCloseDirty_Front.png */; };
+ 263A292D1EE41B53005C52B9 /* AquaTabCloseDirty_Front_Pressed.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26081EE40BFC005C52B9 /* AquaTabCloseDirty_Front_Pressed.png */; };
+ 263A292E1EE41B53005C52B9 /* AquaTabCloseDirty_Front_Rollover.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A26091EE40BFC005C52B9 /* AquaTabCloseDirty_Front_Rollover.png */; };
+ 263A292F1EE41B53005C52B9 /* AquaTabNew.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A260A1EE40BFC005C52B9 /* AquaTabNew.png */; };
+ 263A29301EE41B53005C52B9 /* AquaTabNewPressed.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A260B1EE40BFC005C52B9 /* AquaTabNewPressed.png */; };
+ 263A29311EE41B53005C52B9 /* AquaTabNewRollover.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A260C1EE40BFC005C52B9 /* AquaTabNewRollover.png */; };
+ 263A29321EE41B53005C52B9 /* overflowImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A260D1EE40BFC005C52B9 /* overflowImage.png */; };
+ 263A29331EE41B53005C52B9 /* overflowImagePressed.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A260E1EE40BFC005C52B9 /* overflowImagePressed.png */; };
+ 263A29341EE41B53005C52B9 /* pi.png in Resources */ = {isa = PBXBuildFile; fileRef = 263A260F1EE40BFC005C52B9 /* pi.png */; };
+ 263A29351EE41B5E005C52B9 /* NSBezierPath_AMShading.m in Sources */ = {isa = PBXBuildFile; fileRef = 263A26111EE40BFC005C52B9 /* NSBezierPath_AMShading.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 261DB24F1318444F00C59F12 /* compile-xib.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "compile-xib.sh"; sourceTree = "<group>"; };
+ 261DB2501318444F00C59F12 /* extract-strings.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "extract-strings.sh"; sourceTree = "<group>"; };
+ 2636299412F699250048542C /* NetSurf.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; name = NetSurf.app; path = ../../NetSurf.app; sourceTree = SOURCE_ROOT; };
+ 263A25EE1EE40BD6005C52B9 /* NetSurfBrowser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetSurfBrowser.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 263A26041EE40BFC005C52B9 /* AquaTabClose_Front.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabClose_Front.png; sourceTree = "<group>"; };
+ 263A26051EE40BFC005C52B9 /* AquaTabClose_Front_Pressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabClose_Front_Pressed.png; sourceTree = "<group>"; };
+ 263A26061EE40BFC005C52B9 /* AquaTabClose_Front_Rollover.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabClose_Front_Rollover.png; sourceTree = "<group>"; };
+ 263A26071EE40BFC005C52B9 /* AquaTabCloseDirty_Front.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabCloseDirty_Front.png; sourceTree = "<group>"; };
+ 263A26081EE40BFC005C52B9 /* AquaTabCloseDirty_Front_Pressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabCloseDirty_Front_Pressed.png; sourceTree = "<group>"; };
+ 263A26091EE40BFC005C52B9 /* AquaTabCloseDirty_Front_Rollover.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabCloseDirty_Front_Rollover.png; sourceTree = "<group>"; };
+ 263A260A1EE40BFC005C52B9 /* AquaTabNew.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabNew.png; sourceTree = "<group>"; };
+ 263A260B1EE40BFC005C52B9 /* AquaTabNewPressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabNewPressed.png; sourceTree = "<group>"; };
+ 263A260C1EE40BFC005C52B9 /* AquaTabNewRollover.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AquaTabNewRollover.png; sourceTree = "<group>"; };
+ 263A260D1EE40BFC005C52B9 /* overflowImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = overflowImage.png; sourceTree = "<group>"; };
+ 263A260E1EE40BFC005C52B9 /* overflowImagePressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = overflowImagePressed.png; sourceTree = "<group>"; };
+ 263A260F1EE40BFC005C52B9 /* pi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pi.png; sourceTree = "<group>"; };
+ 263A26101EE40BFC005C52B9 /* NSBezierPath_AMShading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSBezierPath_AMShading.h; sourceTree = "<group>"; };
+ 263A26111EE40BFC005C52B9 /* NSBezierPath_AMShading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSBezierPath_AMShading.m; sourceTree = "<group>"; };
+ 263A26121EE40BFC005C52B9 /* NSString_AITruncation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSString_AITruncation.h; sourceTree = "<group>"; };
+ 263A26131EE40BFC005C52B9 /* NSString_AITruncation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSString_AITruncation.m; sourceTree = "<group>"; };
+ 263A26141EE40BFC005C52B9 /* PSMOverflowPopUpButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMOverflowPopUpButton.h; sourceTree = "<group>"; };
+ 263A26151EE40BFC005C52B9 /* PSMOverflowPopUpButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMOverflowPopUpButton.m; sourceTree = "<group>"; };
+ 263A26161EE40BFC005C52B9 /* PSMProgressIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMProgressIndicator.h; sourceTree = "<group>"; };
+ 263A26171EE40BFC005C52B9 /* PSMProgressIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMProgressIndicator.m; sourceTree = "<group>"; };
+ 263A26181EE40BFC005C52B9 /* PSMRolloverButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMRolloverButton.h; sourceTree = "<group>"; };
+ 263A26191EE40BFC005C52B9 /* PSMRolloverButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMRolloverButton.m; sourceTree = "<group>"; };
+ 263A261A1EE40BFC005C52B9 /* PSMTabBarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabBarCell.h; sourceTree = "<group>"; };
+ 263A261B1EE40BFC005C52B9 /* PSMTabBarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabBarCell.m; sourceTree = "<group>"; };
+ 263A261C1EE40BFC005C52B9 /* PSMTabBarControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabBarControl.h; sourceTree = "<group>"; };
+ 263A261D1EE40BFC005C52B9 /* PSMTabBarControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabBarControl.m; sourceTree = "<group>"; };
+ 263A261E1EE40BFC005C52B9 /* PSMTabBarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabBarController.h; sourceTree = "<group>"; };
+ 263A261F1EE40BFC005C52B9 /* PSMTabBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabBarController.m; sourceTree = "<group>"; };
+ 263A26201EE40BFC005C52B9 /* PSMTabDragAssistant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabDragAssistant.h; sourceTree = "<group>"; };
+ 263A26211EE40BFC005C52B9 /* PSMTabDragAssistant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabDragAssistant.m; sourceTree = "<group>"; };
+ 263A26221EE40BFC005C52B9 /* PSMTabDragView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabDragView.h; sourceTree = "<group>"; };
+ 263A26231EE40BFC005C52B9 /* PSMTabDragView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabDragView.m; sourceTree = "<group>"; };
+ 263A26241EE40BFC005C52B9 /* PSMTabDragWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabDragWindow.h; sourceTree = "<group>"; };
+ 263A26251EE40BFC005C52B9 /* PSMTabDragWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabDragWindow.m; sourceTree = "<group>"; };
+ 263A26261EE40BFC005C52B9 /* PSMTabDragWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabDragWindowController.h; sourceTree = "<group>"; };
+ 263A26271EE40BFC005C52B9 /* PSMTabDragWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMTabDragWindowController.m; sourceTree = "<group>"; };
+ 263A26281EE40BFC005C52B9 /* PSMTabStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMTabStyle.h; sourceTree = "<group>"; };
+ 263A26291EE40BFC005C52B9 /* PSMUnifiedTabStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSMUnifiedTabStyle.h; sourceTree = "<group>"; };
+ 263A262A1EE40BFC005C52B9 /* PSMUnifiedTabStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSMUnifiedTabStyle.m; sourceTree = "<group>"; };
+ 263A262B1EE40BFC005C52B9 /* ReadMe.rtfd */ = {isa = PBXFileReference; lastKnownFileType = wrapper.rtfd; path = ReadMe.rtfd; sourceTree = "<group>"; };
+ 263A262D1EE40BFC005C52B9 /* adblock.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = adblock.css; sourceTree = "<group>"; };
+ 263A262E1EE40BFC005C52B9 /* BookmarksWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BookmarksWindow.xib; sourceTree = "<group>"; };
+ 263A262F1EE40BFC005C52B9 /* Browser.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = Browser.xib; sourceTree = "<group>"; };
+ 263A26301EE40BFC005C52B9 /* BrowserWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BrowserWindow.xib; sourceTree = "<group>"; };
+ 263A26311EE40BFC005C52B9 /* ca-bundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "ca-bundle"; sourceTree = "<group>"; };
+ 263A26331EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/BookmarksWindow.xib.strings; sourceTree = "<group>"; };
+ 263A26351EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/BrowserWindow.xib.strings; sourceTree = "<group>"; };
+ 263A26371EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/DownloadWindow.xib.strings; sourceTree = "<group>"; };
+ 263A26391EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/HistoryWindow.xib.strings; sourceTree = "<group>"; };
+ 263A263B1EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 263A263D1EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.xib.strings; sourceTree = "<group>"; };
+ 263A263F1EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; lastKnownFileType = file; name = de; path = de.lproj/Messages; sourceTree = "<group>"; };
+ 263A26411EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/PreferencesWindow.xib.strings; sourceTree = "<group>"; };
+ 263A26431EE40BFC005C52B9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SearchWindow.xib.strings; sourceTree = "<group>"; };
+ 263A26441EE40BFC005C52B9 /* default.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = default.css; sourceTree = "<group>"; };
+ 263A26451EE40BFC005C52B9 /* DownloadWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DownloadWindow.xib; sourceTree = "<group>"; };
+ 263A26461EE40BFC005C52B9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 263A26471EE40BFC005C52B9 /* en */ = {isa = PBXFileReference; lastKnownFileType = file; name = en; path = en.lproj/Messages; sourceTree = "<group>"; };
+ 263A26481EE40BFC005C52B9 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 263A26491EE40BFC005C52B9 /* fr */ = {isa = PBXFileReference; lastKnownFileType = file; name = fr; path = fr.lproj/Messages; sourceTree = "<group>"; };
+ 263A264A1EE40BFC005C52B9 /* HistoryWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HistoryWindow.xib; sourceTree = "<group>"; };
+ 263A264B1EE40BFC005C52B9 /* HomeTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = HomeTemplate.pdf; sourceTree = "<group>"; };
+ 263A264D1EE40BFC005C52B9 /* arrow-l.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "arrow-l.png"; sourceTree = "<group>"; };
+ 263A264E1EE40BFC005C52B9 /* content.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = content.png; sourceTree = "<group>"; };
+ 263A264F1EE40BFC005C52B9 /* directory.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = directory.png; sourceTree = "<group>"; };
+ 263A26501EE40BFC005C52B9 /* directory2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = directory2.png; sourceTree = "<group>"; };
+ 263A26511EE40BFC005C52B9 /* hotlist-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "hotlist-add.png"; sourceTree = "<group>"; };
+ 263A26521EE40BFC005C52B9 /* hotlist-rmv.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "hotlist-rmv.png"; sourceTree = "<group>"; };
+ 263A26531EE40BFC005C52B9 /* search.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = search.png; sourceTree = "<group>"; };
+ 263A26541EE40BFC005C52B9 /* internal.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = internal.css; sourceTree = "<group>"; };
+ 263A26551EE40BFC005C52B9 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 263A26561EE40BFC005C52B9 /* it */ = {isa = PBXFileReference; lastKnownFileType = file; name = it; path = it.lproj/Messages; sourceTree = "<group>"; };
+ 263A26571EE40BFC005C52B9 /* LocalHistoryPanel.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LocalHistoryPanel.xib; sourceTree = "<group>"; };
+ 263A26581EE40BFC005C52B9 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
+ 263A26591EE40BFC005C52B9 /* NetSurf-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "NetSurf-Info.plist"; sourceTree = "<group>"; };
+ 263A265A1EE40BFC005C52B9 /* NetSurf.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = NetSurf.icns; sourceTree = "<group>"; };
+ 263A265B1EE40BFC005C52B9 /* netsurf.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = netsurf.png; sourceTree = "<group>"; };
+ 263A265C1EE40BFC005C52B9 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 263A265D1EE40BFC005C52B9 /* nl */ = {isa = PBXFileReference; lastKnownFileType = file; name = nl; path = nl.lproj/Messages; sourceTree = "<group>"; };
+ 263A265E1EE40BFC005C52B9 /* PreferencesWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PreferencesWindow.xib; sourceTree = "<group>"; };
+ 263A265F1EE40BFC005C52B9 /* quirks.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = quirks.css; sourceTree = "<group>"; };
+ 263A26601EE40BFC005C52B9 /* SearchWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SearchWindow.xib; sourceTree = "<group>"; };
+ 263A26611EE40BFC005C52B9 /* apple_image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apple_image.h; sourceTree = SOURCE_ROOT; };
+ 263A26621EE40BFC005C52B9 /* ArrowBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrowBox.h; sourceTree = SOURCE_ROOT; };
+ 263A26631EE40BFC005C52B9 /* ArrowWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrowWindow.h; sourceTree = SOURCE_ROOT; };
+ 263A26641EE40BFC005C52B9 /* bitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap.h; sourceTree = SOURCE_ROOT; };
+ 263A26661EE40BFC005C52B9 /* BookmarksController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BookmarksController.h; sourceTree = SOURCE_ROOT; };
+ 263A26671EE40BFC005C52B9 /* BrowserView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserView.h; sourceTree = SOURCE_ROOT; };
+ 263A26681EE40BFC005C52B9 /* BrowserViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserViewController.h; sourceTree = SOURCE_ROOT; };
+ 263A26691EE40BFC005C52B9 /* BrowserWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserWindow.h; sourceTree = SOURCE_ROOT; };
+ 263A266A1EE40BFC005C52B9 /* BrowserWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserWindowController.h; sourceTree = SOURCE_ROOT; };
+ 263A266B1EE40BFC005C52B9 /* coordinates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coordinates.h; sourceTree = SOURCE_ROOT; };
+ 263A266C1EE40BFC005C52B9 /* desktop-tree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "desktop-tree.h"; sourceTree = SOURCE_ROOT; };
+ 263A266D1EE40BFC005C52B9 /* DownloadWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadWindowController.h; sourceTree = SOURCE_ROOT; };
+ 263A266E1EE40BFC005C52B9 /* fetch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fetch.h; sourceTree = SOURCE_ROOT; };
+ 263A266F1EE40BFC005C52B9 /* font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font.h; sourceTree = SOURCE_ROOT; };
+ 263A26701EE40BFC005C52B9 /* FormSelectMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FormSelectMenu.h; sourceTree = SOURCE_ROOT; };
+ 263A26711EE40BFC005C52B9 /* gui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gui.h; sourceTree = SOURCE_ROOT; };
+ 263A26721EE40BFC005C52B9 /* HistoryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryView.h; sourceTree = SOURCE_ROOT; };
+ 263A26731EE40BFC005C52B9 /* HistoryWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryWindowController.h; sourceTree = SOURCE_ROOT; };
+ 263A26741EE40BFC005C52B9 /* LocalHistoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalHistoryController.h; sourceTree = SOURCE_ROOT; };
+ 263A26751EE40BFC005C52B9 /* NetsurfApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetsurfApp.h; sourceTree = SOURCE_ROOT; };
+ 263A26761EE40BFC005C52B9 /* NetSurfAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetSurfAppDelegate.h; sourceTree = SOURCE_ROOT; };
+ 263A26771EE40BFC005C52B9 /* plotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = plotter.h; sourceTree = SOURCE_ROOT; };
+ 263A26781EE40BFC005C52B9 /* PreferencesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesWindowController.h; sourceTree = SOURCE_ROOT; };
+ 263A26791EE40BFC005C52B9 /* schedule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = schedule.h; sourceTree = SOURCE_ROOT; };
+ 263A267A1EE40BFC005C52B9 /* ScrollableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollableView.h; sourceTree = SOURCE_ROOT; };
+ 263A267B1EE40BFC005C52B9 /* SearchWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchWindowController.h; sourceTree = SOURCE_ROOT; };
+ 263A267C1EE40BFC005C52B9 /* selection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = selection.h; sourceTree = SOURCE_ROOT; };
+ 263A267D1EE40BFC005C52B9 /* Tree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tree.h; sourceTree = SOURCE_ROOT; };
+ 263A267E1EE40BFC005C52B9 /* TreeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TreeView.h; sourceTree = SOURCE_ROOT; };
+ 263A267F1EE40BFC005C52B9 /* URLFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = URLFieldCell.h; sourceTree = SOURCE_ROOT; };
+ 263A26801EE40BFC005C52B9 /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = SOURCE_ROOT; };
+ 263A26811EE40BFC005C52B9 /* Makefile.defaults */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.defaults; sourceTree = SOURCE_ROOT; };
+ 263A26821EE40BFC005C52B9 /* apple_image.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = apple_image.m; sourceTree = SOURCE_ROOT; };
+ 263A26831EE40BFC005C52B9 /* ArrowBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArrowBox.m; sourceTree = SOURCE_ROOT; };
+ 263A26841EE40BFC005C52B9 /* ArrowWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArrowWindow.m; sourceTree = SOURCE_ROOT; };
+ 263A26851EE40BFC005C52B9 /* bitmap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = bitmap.m; sourceTree = SOURCE_ROOT; };
+ 263A26871EE40BFC005C52B9 /* BookmarksController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BookmarksController.m; sourceTree = SOURCE_ROOT; };
+ 263A26881EE40BFC005C52B9 /* BrowserView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BrowserView.m; sourceTree = SOURCE_ROOT; };
+ 263A26891EE40BFC005C52B9 /* BrowserViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BrowserViewController.m; sourceTree = SOURCE_ROOT; };
+ 263A268A1EE40BFC005C52B9 /* BrowserWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BrowserWindow.m; sourceTree = SOURCE_ROOT; };
+ 263A268B1EE40BFC005C52B9 /* BrowserWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BrowserWindowController.m; sourceTree = SOURCE_ROOT; };
+ 263A268C1EE40BFC005C52B9 /* desktop-tree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "desktop-tree.m"; sourceTree = SOURCE_ROOT; };
+ 263A268D1EE40BFC005C52B9 /* DownloadWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadWindowController.m; sourceTree = SOURCE_ROOT; };
+ 263A268E1EE40BFC005C52B9 /* fetch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fetch.m; sourceTree = SOURCE_ROOT; };
+ 263A268F1EE40BFC005C52B9 /* font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = font.m; sourceTree = SOURCE_ROOT; };
+ 263A26901EE40BFC005C52B9 /* FormSelectMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FormSelectMenu.m; sourceTree = SOURCE_ROOT; };
+ 263A26911EE40BFC005C52B9 /* gui.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = gui.m; sourceTree = SOURCE_ROOT; };
+ 263A26921EE40BFC005C52B9 /* HistoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HistoryView.m; sourceTree = SOURCE_ROOT; };
+ 263A26931EE40BFC005C52B9 /* HistoryWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HistoryWindowController.m; sourceTree = SOURCE_ROOT; };
+ 263A26941EE40BFC005C52B9 /* LocalHistoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalHistoryController.m; sourceTree = SOURCE_ROOT; };
+ 263A26951EE40BFC005C52B9 /* NetsurfApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetsurfApp.m; sourceTree = SOURCE_ROOT; };
+ 263A26961EE40BFC005C52B9 /* NetSurfAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetSurfAppDelegate.m; sourceTree = SOURCE_ROOT; };
+ 263A26971EE40BFC005C52B9 /* plotter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = plotter.m; sourceTree = SOURCE_ROOT; };
+ 263A26981EE40BFC005C52B9 /* PreferencesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesWindowController.m; sourceTree = SOURCE_ROOT; };
+ 263A26991EE40BFC005C52B9 /* schedule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = schedule.m; sourceTree = SOURCE_ROOT; };
+ 263A269A1EE40BFC005C52B9 /* ScrollableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScrollableView.m; sourceTree = SOURCE_ROOT; };
+ 263A269B1EE40BFC005C52B9 /* SearchWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchWindowController.m; sourceTree = SOURCE_ROOT; };
+ 263A269C1EE40BFC005C52B9 /* selection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = selection.m; sourceTree = SOURCE_ROOT; };
+ 263A269D1EE40BFC005C52B9 /* Tree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tree.m; sourceTree = SOURCE_ROOT; };
+ 263A269E1EE40BFC005C52B9 /* TreeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TreeView.m; sourceTree = SOURCE_ROOT; };
+ 263A269F1EE40BFC005C52B9 /* URLFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = URLFieldCell.m; sourceTree = SOURCE_ROOT; };
+ 263A26A21EE40C4A005C52B9 /* backing_store.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = backing_store.h; sourceTree = "<group>"; };
+ 263A26A31EE40C4A005C52B9 /* content.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = content.c; sourceTree = "<group>"; };
+ 263A26A41EE40C4A005C52B9 /* content.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content.h; sourceTree = "<group>"; };
+ 263A26A51EE40C4A005C52B9 /* content_debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content_debug.h; sourceTree = "<group>"; };
+ 263A26A61EE40C4A005C52B9 /* content_factory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = content_factory.c; sourceTree = "<group>"; };
+ 263A26A71EE40C4A005C52B9 /* content_factory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content_factory.h; sourceTree = "<group>"; };
+ 263A26A81EE40C4A005C52B9 /* content_protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content_protected.h; sourceTree = "<group>"; };
+ 263A26A91EE40C4A005C52B9 /* dirlist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dirlist.c; sourceTree = "<group>"; };
+ 263A26AA1EE40C4A005C52B9 /* dirlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dirlist.h; sourceTree = "<group>"; };
+ 263A26AB1EE40C4A005C52B9 /* fetch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fetch.c; sourceTree = "<group>"; };
+ 263A26AC1EE40C4A005C52B9 /* fetch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fetch.h; sourceTree = "<group>"; };
+ 263A26AE1EE40C4A005C52B9 /* about.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = about.c; sourceTree = "<group>"; };
+ 263A26AF1EE40C4A005C52B9 /* about.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = about.h; sourceTree = "<group>"; };
+ 263A26B01EE40C4A005C52B9 /* curl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = curl.c; sourceTree = "<group>"; };
+ 263A26B11EE40C4A005C52B9 /* curl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = curl.h; sourceTree = "<group>"; };
+ 263A26B21EE40C4A005C52B9 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = "<group>"; };
+ 263A26B31EE40C4A005C52B9 /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = "<group>"; };
+ 263A26B41EE40C4A005C52B9 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = "<group>"; };
+ 263A26B51EE40C4A005C52B9 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
+ 263A26B61EE40C4A005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A26B71EE40C4A005C52B9 /* resource.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = resource.c; sourceTree = "<group>"; };
+ 263A26B81EE40C4A005C52B9 /* resource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resource.h; sourceTree = "<group>"; };
+ 263A26B91EE40C4A005C52B9 /* fetchers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fetchers.h; sourceTree = "<group>"; };
+ 263A26BA1EE40C4A005C52B9 /* fs_backing_store.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fs_backing_store.c; sourceTree = "<group>"; };
+ 263A26BD1EE40C4A005C52B9 /* css.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = css.c; sourceTree = "<group>"; };
+ 263A26BE1EE40C4A005C52B9 /* css.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = css.h; sourceTree = "<group>"; };
+ 263A26BF1EE40C4A005C52B9 /* dump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dump.c; sourceTree = "<group>"; };
+ 263A26C01EE40C4A005C52B9 /* dump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dump.h; sourceTree = "<group>"; };
+ 263A26C11EE40C4A005C52B9 /* hints.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hints.c; sourceTree = "<group>"; };
+ 263A26C21EE40C4A005C52B9 /* hints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hints.h; sourceTree = "<group>"; };
+ 263A26C31EE40C4A005C52B9 /* internal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = internal.c; sourceTree = "<group>"; };
+ 263A26C41EE40C4A005C52B9 /* internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal.h; sourceTree = "<group>"; };
+ 263A26C51EE40C4A005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A26C61EE40C4A005C52B9 /* select.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = select.c; sourceTree = "<group>"; };
+ 263A26C71EE40C4A005C52B9 /* select.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = select.h; sourceTree = "<group>"; };
+ 263A26C81EE40C4A005C52B9 /* utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = "<group>"; };
+ 263A26C91EE40C4A005C52B9 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; };
+ 263A26CB1EE40C4A005C52B9 /* bmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bmp.c; sourceTree = "<group>"; };
+ 263A26CC1EE40C4A005C52B9 /* bmp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bmp.h; sourceTree = "<group>"; };
+ 263A26CD1EE40C4A005C52B9 /* gif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gif.c; sourceTree = "<group>"; };
+ 263A26CE1EE40C4A005C52B9 /* gif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gif.h; sourceTree = "<group>"; };
+ 263A26CF1EE40C4A005C52B9 /* ico.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ico.c; sourceTree = "<group>"; };
+ 263A26D01EE40C4A005C52B9 /* ico.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ico.h; sourceTree = "<group>"; };
+ 263A26D11EE40C4A005C52B9 /* image.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = image.c; sourceTree = "<group>"; };
+ 263A26D21EE40C4A005C52B9 /* image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image.h; sourceTree = "<group>"; };
+ 263A26D31EE40C4A005C52B9 /* image_cache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = image_cache.c; sourceTree = "<group>"; };
+ 263A26D41EE40C4A005C52B9 /* image_cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image_cache.h; sourceTree = "<group>"; };
+ 263A26D61EE40C4A005C52B9 /* jpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jpeg.h; sourceTree = "<group>"; };
+ 263A26D71EE40C4A005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A26D91EE40C4A005C52B9 /* nssprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nssprite.h; sourceTree = "<group>"; };
+ 263A26DB1EE40C4A005C52B9 /* png.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = png.h; sourceTree = "<group>"; };
+ 263A26DD1EE40C4A005C52B9 /* rsvg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rsvg.h; sourceTree = "<group>"; };
+ 263A26DF1EE40C4A005C52B9 /* svg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svg.h; sourceTree = "<group>"; };
+ 263A26E11EE40C4A005C52B9 /* video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video.h; sourceTree = "<group>"; };
+ 263A27361EE40C4B005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A27371EE40C4B005C52B9 /* hlcache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hlcache.c; sourceTree = "<group>"; };
+ 263A27381EE40C4B005C52B9 /* hlcache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hlcache.h; sourceTree = "<group>"; };
+ 263A27391EE40C4B005C52B9 /* llcache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = llcache.c; sourceTree = "<group>"; };
+ 263A273A1EE40C4B005C52B9 /* llcache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = llcache.h; sourceTree = "<group>"; };
+ 263A273B1EE40C4B005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A273C1EE40C4B005C52B9 /* mimesniff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mimesniff.c; sourceTree = "<group>"; };
+ 263A273D1EE40C4B005C52B9 /* mimesniff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mimesniff.h; sourceTree = "<group>"; };
+ 263A273E1EE40C4B005C52B9 /* no_backing_store.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = no_backing_store.c; sourceTree = "<group>"; };
+ 263A273F1EE40C4B005C52B9 /* urldb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = urldb.c; sourceTree = "<group>"; };
+ 263A27401EE40C4B005C52B9 /* urldb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = urldb.h; sourceTree = "<group>"; };
+ 263A27421EE40C4B005C52B9 /* browser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = browser.c; sourceTree = "<group>"; };
+ 263A27431EE40C4B005C52B9 /* browser_history.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = browser_history.c; sourceTree = "<group>"; };
+ 263A27441EE40C4B005C52B9 /* browser_history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = browser_history.h; sourceTree = "<group>"; };
+ 263A27451EE40C4B005C52B9 /* browser_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = browser_private.h; sourceTree = "<group>"; };
+ 263A27461EE40C4B005C52B9 /* cookie_manager.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cookie_manager.c; sourceTree = "<group>"; };
+ 263A27471EE40C4B005C52B9 /* cookie_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cookie_manager.h; sourceTree = "<group>"; };
+ 263A27481EE40C4B005C52B9 /* download.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = download.c; sourceTree = "<group>"; };
+ 263A27491EE40C4B005C52B9 /* download.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = download.h; sourceTree = "<group>"; };
+ 263A274A1EE40C4B005C52B9 /* font_haru.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = font_haru.c; sourceTree = "<group>"; };
+ 263A274B1EE40C4B005C52B9 /* font_haru.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font_haru.h; sourceTree = "<group>"; };
+ 263A274C1EE40C4B005C52B9 /* frame_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = frame_types.h; sourceTree = "<group>"; };
+ 263A274D1EE40C4B005C52B9 /* frames.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = frames.c; sourceTree = "<group>"; };
+ 263A274E1EE40C4B005C52B9 /* frames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = frames.h; sourceTree = "<group>"; };
+ 263A274F1EE40C4B005C52B9 /* global_history.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = global_history.c; sourceTree = "<group>"; };
+ 263A27501EE40C4B005C52B9 /* global_history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = global_history.h; sourceTree = "<group>"; };
+ 263A27511EE40C4B005C52B9 /* gui_factory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gui_factory.c; sourceTree = "<group>"; };
+ 263A27521EE40C4B005C52B9 /* gui_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gui_internal.h; sourceTree = "<group>"; };
+ 263A27531EE40C4B005C52B9 /* gui_table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gui_table.h; sourceTree = "<group>"; };
+ 263A27541EE40C4B005C52B9 /* hotlist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hotlist.c; sourceTree = "<group>"; };
+ 263A27551EE40C4B005C52B9 /* hotlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hotlist.h; sourceTree = "<group>"; };
+ 263A27561EE40C4B005C52B9 /* knockout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = knockout.c; sourceTree = "<group>"; };
+ 263A27571EE40C4B005C52B9 /* knockout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = knockout.h; sourceTree = "<group>"; };
+ 263A27581EE40C4B005C52B9 /* local_history.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = local_history.c; sourceTree = "<group>"; };
+ 263A27591EE40C4B005C52B9 /* local_history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = local_history.h; sourceTree = "<group>"; };
+ 263A275A1EE40C4B005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A275B1EE40C4B005C52B9 /* mouse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mouse.c; sourceTree = "<group>"; };
+ 263A275C1EE40C4B005C52B9 /* netsurf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = netsurf.c; sourceTree = "<group>"; };
+ 263A275D1EE40C4B005C52B9 /* options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = "<group>"; };
+ 263A275E1EE40C4B005C52B9 /* plot_style.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = plot_style.c; sourceTree = "<group>"; };
+ 263A275F1EE40C4B005C52B9 /* print.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = print.c; sourceTree = "<group>"; };
+ 263A27601EE40C4B005C52B9 /* print.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = print.h; sourceTree = "<group>"; };
+ 263A27611EE40C4B005C52B9 /* printer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = printer.h; sourceTree = "<group>"; };
+ 263A27621EE40C4B005C52B9 /* save_complete.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = save_complete.c; sourceTree = "<group>"; };
+ 263A27631EE40C4B005C52B9 /* save_complete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = save_complete.h; sourceTree = "<group>"; };
+ 263A27641EE40C4B005C52B9 /* save_pdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = save_pdf.c; sourceTree = "<group>"; };
+ 263A27651EE40C4B005C52B9 /* save_pdf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = save_pdf.h; sourceTree = "<group>"; };
+ 263A27661EE40C4B005C52B9 /* save_text.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = save_text.c; sourceTree = "<group>"; };
+ 263A27671EE40C4B005C52B9 /* save_text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = save_text.h; sourceTree = "<group>"; };
+ 263A27681EE40C4B005C52B9 /* scrollbar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scrollbar.c; sourceTree = "<group>"; };
+ 263A27691EE40C4B005C52B9 /* scrollbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scrollbar.h; sourceTree = "<group>"; };
+ 263A276A1EE40C4B005C52B9 /* search.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = search.c; sourceTree = "<group>"; };
+ 263A276B1EE40C4B005C52B9 /* search.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = search.h; sourceTree = "<group>"; };
+ 263A276C1EE40C4B005C52B9 /* searchweb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = searchweb.c; sourceTree = "<group>"; };
+ 263A276D1EE40C4B005C52B9 /* searchweb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = searchweb.h; sourceTree = "<group>"; };
+ 263A276E1EE40C4B005C52B9 /* selection.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = selection.c; sourceTree = "<group>"; };
+ 263A276F1EE40C4B005C52B9 /* selection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = selection.h; sourceTree = "<group>"; };
+ 263A27701EE40C4B005C52B9 /* sslcert_viewer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sslcert_viewer.c; sourceTree = "<group>"; };
+ 263A27711EE40C4B005C52B9 /* sslcert_viewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sslcert_viewer.h; sourceTree = "<group>"; };
+ 263A27721EE40C4B005C52B9 /* system_colour.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = system_colour.c; sourceTree = "<group>"; };
+ 263A27731EE40C4B005C52B9 /* system_colour.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = system_colour.h; sourceTree = "<group>"; };
+ 263A27741EE40C4B005C52B9 /* textarea.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = textarea.c; sourceTree = "<group>"; };
+ 263A27751EE40C4B005C52B9 /* textarea.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = textarea.h; sourceTree = "<group>"; };
+ 263A27761EE40C4B005C52B9 /* textinput.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = textinput.c; sourceTree = "<group>"; };
+ 263A27771EE40C4B005C52B9 /* textinput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = textinput.h; sourceTree = "<group>"; };
+ 263A27781EE40C4B005C52B9 /* theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = theme.h; sourceTree = "<group>"; };
+ 263A27791EE40C4B005C52B9 /* treeview.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = treeview.c; sourceTree = "<group>"; };
+ 263A277A1EE40C4B005C52B9 /* treeview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = treeview.h; sourceTree = "<group>"; };
+ 263A277B1EE40C4B005C52B9 /* version.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = "<group>"; };
+ 263A277C1EE40C4B005C52B9 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
+ 263A277F1EE40C4B005C52B9 /* bitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap.h; sourceTree = "<group>"; };
+ 263A27801EE40C4B005C52B9 /* browser_window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = browser_window.h; sourceTree = "<group>"; };
+ 263A27811EE40C4B005C52B9 /* clipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clipboard.h; sourceTree = "<group>"; };
+ 263A27821EE40C4B005C52B9 /* content.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content.h; sourceTree = "<group>"; };
+ 263A27831EE40C4B005C52B9 /* content_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content_type.h; sourceTree = "<group>"; };
+ 263A27841EE40C4B005C52B9 /* cookie_db.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cookie_db.h; sourceTree = "<group>"; };
+ 263A27851EE40C4B005C52B9 /* core_window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core_window.h; sourceTree = "<group>"; };
+ 263A27861EE40C4B005C52B9 /* css.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = css.h; sourceTree = "<group>"; };
+ 263A27871EE40C4B005C52B9 /* download.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = download.h; sourceTree = "<group>"; };
+ 263A27881EE40C4B005C52B9 /* fetch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fetch.h; sourceTree = "<group>"; };
+ 263A27891EE40C4B005C52B9 /* form.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = form.h; sourceTree = "<group>"; };
+ 263A278A1EE40C4B005C52B9 /* inttypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inttypes.h; sourceTree = "<group>"; };
+ 263A278B1EE40C4B005C52B9 /* keypress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = keypress.h; sourceTree = "<group>"; };
+ 263A278C1EE40C4B005C52B9 /* layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = layout.h; sourceTree = "<group>"; };
+ 263A278D1EE40C4B005C52B9 /* misc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = misc.h; sourceTree = "<group>"; };
+ 263A278E1EE40C4B005C52B9 /* mouse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mouse.h; sourceTree = "<group>"; };
+ 263A278F1EE40C4B005C52B9 /* netsurf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = netsurf.h; sourceTree = "<group>"; };
+ 263A27901EE40C4B005C52B9 /* plot_style.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = plot_style.h; sourceTree = "<group>"; };
+ 263A27911EE40C4B005C52B9 /* plotters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = plotters.h; sourceTree = "<group>"; };
+ 263A27921EE40C4B005C52B9 /* search.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = search.h; sourceTree = "<group>"; };
+ 263A27931EE40C4B005C52B9 /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = "<group>"; };
+ 263A27941EE40C4B005C52B9 /* url_db.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url_db.h; sourceTree = "<group>"; };
+ 263A27951EE40C4B005C52B9 /* utf8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utf8.h; sourceTree = "<group>"; };
+ 263A27961EE40C4B005C52B9 /* window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = "<group>"; };
+ 263A27981EE40C4B005C52B9 /* box.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = box.c; sourceTree = "<group>"; };
+ 263A27991EE40C4B005C52B9 /* box.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = box.h; sourceTree = "<group>"; };
+ 263A279A1EE40C4B005C52B9 /* box_construct.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = box_construct.c; sourceTree = "<group>"; };
+ 263A279B1EE40C4B005C52B9 /* box_normalise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = box_normalise.c; sourceTree = "<group>"; };
+ 263A279C1EE40C4B005C52B9 /* box_textarea.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = box_textarea.c; sourceTree = "<group>"; };
+ 263A279D1EE40C4B005C52B9 /* box_textarea.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = box_textarea.h; sourceTree = "<group>"; };
+ 263A279E1EE40C4B005C52B9 /* font.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = font.c; sourceTree = "<group>"; };
+ 263A279F1EE40C4B005C52B9 /* font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font.h; sourceTree = "<group>"; };
+ 263A27A01EE40C4B005C52B9 /* form.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = form.c; sourceTree = "<group>"; };
+ 263A27A11EE40C4B005C52B9 /* form_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = form_internal.h; sourceTree = "<group>"; };
+ 263A27A21EE40C4B005C52B9 /* html.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html.c; sourceTree = "<group>"; };
+ 263A27A31EE40C4B005C52B9 /* html.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = html.h; sourceTree = "<group>"; };
+ 263A27A41EE40C4B005C52B9 /* html_css.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_css.c; sourceTree = "<group>"; };
+ 263A27A51EE40C4B005C52B9 /* html_css_fetcher.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_css_fetcher.c; sourceTree = "<group>"; };
+ 263A27A61EE40C4B005C52B9 /* html_forms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_forms.c; sourceTree = "<group>"; };
+ 263A27A71EE40C4B005C52B9 /* html_interaction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_interaction.c; sourceTree = "<group>"; };
+ 263A27A81EE40C4B005C52B9 /* html_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = html_internal.h; sourceTree = "<group>"; };
+ 263A27A91EE40C4B005C52B9 /* html_object.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_object.c; sourceTree = "<group>"; };
+ 263A27AA1EE40C4B005C52B9 /* html_redraw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_redraw.c; sourceTree = "<group>"; };
+ 263A27AB1EE40C4B005C52B9 /* html_redraw_border.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_redraw_border.c; sourceTree = "<group>"; };
+ 263A27AC1EE40C4B005C52B9 /* html_script.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = html_script.c; sourceTree = "<group>"; };
+ 263A27AD1EE40C4B005C52B9 /* imagemap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imagemap.c; sourceTree = "<group>"; };
+ 263A27AE1EE40C4B005C52B9 /* imagemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imagemap.h; sourceTree = "<group>"; };
+ 263A27AF1EE40C4B005C52B9 /* layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = layout.c; sourceTree = "<group>"; };
+ 263A27B01EE40C4B005C52B9 /* layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = layout.h; sourceTree = "<group>"; };
+ 263A27B11EE40C4B005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A27B21EE40C4B005C52B9 /* search.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = search.c; sourceTree = "<group>"; };
+ 263A27B31EE40C4B005C52B9 /* search.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = search.h; sourceTree = "<group>"; };
+ 263A27B41EE40C4B005C52B9 /* table.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = table.c; sourceTree = "<group>"; };
+ 263A27B51EE40C4B005C52B9 /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = "<group>"; };
+ 263A27B61EE40C4B005C52B9 /* textplain.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = textplain.c; sourceTree = "<group>"; };
+ 263A27B71EE40C4B005C52B9 /* textplain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = textplain.h; sourceTree = "<group>"; };
+ 263A27B91EE40C4B005C52B9 /* ascii.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ascii.h; sourceTree = "<group>"; };
+ 263A27BA1EE40C4B005C52B9 /* bloom.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bloom.c; sourceTree = "<group>"; };
+ 263A27BB1EE40C4B005C52B9 /* bloom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bloom.h; sourceTree = "<group>"; };
+ 263A27BC1EE40C4B005C52B9 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
+ 263A27BD1EE40C4B005C52B9 /* corestringlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = corestringlist.h; sourceTree = "<group>"; };
+ 263A27BE1EE40C4B005C52B9 /* corestrings.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = corestrings.c; sourceTree = "<group>"; };
+ 263A27BF1EE40C4B005C52B9 /* corestrings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = corestrings.h; sourceTree = "<group>"; };
+ 263A27C01EE40C4B005C52B9 /* coverity-build.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "coverity-build.sh"; sourceTree = "<group>"; };
+ 263A27C11EE40C4B005C52B9 /* DerivedJoiningType.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DerivedJoiningType.txt; sourceTree = "<group>"; };
+ 263A27C21EE40C4B005C52B9 /* dirent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dirent.h; sourceTree = "<group>"; };
+ 263A27C31EE40C4B005C52B9 /* errors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = errors.h; sourceTree = "<group>"; };
+ 263A27C41EE40C4B005C52B9 /* fetch-transifex.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "fetch-transifex.pl"; sourceTree = "<group>"; };
+ 263A27C51EE40C4B005C52B9 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = "<group>"; };
+ 263A27C61EE40C4B005C52B9 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
+ 263A27C71EE40C4B005C52B9 /* filename.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filename.c; sourceTree = "<group>"; };
+ 263A27C81EE40C4B005C52B9 /* filename.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filename.h; sourceTree = "<group>"; };
+ 263A27C91EE40C4B005C52B9 /* filepath.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filepath.c; sourceTree = "<group>"; };
+ 263A27CA1EE40C4B005C52B9 /* filepath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filepath.h; sourceTree = "<group>"; };
+ 263A27CB1EE40C4B005C52B9 /* git-date.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "git-date.sh"; sourceTree = "<group>"; };
+ 263A27CC1EE40C4B005C52B9 /* git-testament.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "git-testament.pl"; sourceTree = "<group>"; };
+ 263A27CD1EE40C4B005C52B9 /* hashtable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hashtable.c; sourceTree = "<group>"; };
+ 263A27CE1EE40C4B005C52B9 /* hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hashtable.h; sourceTree = "<group>"; };
+ 263A27D01EE40C4B005C52B9 /* challenge.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = challenge.c; sourceTree = "<group>"; };
+ 263A27D11EE40C4B005C52B9 /* challenge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = challenge.h; sourceTree = "<group>"; };
+ 263A27D21EE40C4B005C52B9 /* challenge_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = challenge_internal.h; sourceTree = "<group>"; };
+ 263A27D31EE40C4B005C52B9 /* content-disposition.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "content-disposition.c"; sourceTree = "<group>"; };
+ 263A27D41EE40C4B005C52B9 /* content-disposition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "content-disposition.h"; sourceTree = "<group>"; };
+ 263A27D51EE40C4B005C52B9 /* content-type.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "content-type.c"; sourceTree = "<group>"; };
+ 263A27D61EE40C4B005C52B9 /* content-type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "content-type.h"; sourceTree = "<group>"; };
+ 263A27D71EE40C4B005C52B9 /* generics.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = generics.c; sourceTree = "<group>"; };
+ 263A27D81EE40C4B005C52B9 /* generics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = generics.h; sourceTree = "<group>"; };
+ 263A27D91EE40C4B005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A27DA1EE40C4B005C52B9 /* parameter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parameter.c; sourceTree = "<group>"; };
+ 263A27DB1EE40C4B005C52B9 /* parameter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parameter.h; sourceTree = "<group>"; };
+ 263A27DC1EE40C4B005C52B9 /* parameter_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parameter_internal.h; sourceTree = "<group>"; };
+ 263A27DD1EE40C4B005C52B9 /* primitives.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = primitives.c; sourceTree = "<group>"; };
+ 263A27DE1EE40C4B005C52B9 /* primitives.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = primitives.h; sourceTree = "<group>"; };
+ 263A27DF1EE40C4B005C52B9 /* www-authenticate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "www-authenticate.c"; sourceTree = "<group>"; };
+ 263A27E01EE40C4B005C52B9 /* www-authenticate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "www-authenticate.h"; sourceTree = "<group>"; };
+ 263A27E11EE40C4B005C52B9 /* http.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http.h; sourceTree = "<group>"; };
+ 263A27E21EE40C4B005C52B9 /* idna-derived-props-gen.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "idna-derived-props-gen.pl"; sourceTree = "<group>"; };
+ 263A27E31EE40C4B005C52B9 /* idna-tables-5.2.0-properties.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "idna-tables-5.2.0-properties.csv"; sourceTree = "<group>"; };
+ 263A27E41EE40C4B005C52B9 /* idna.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idna.c; sourceTree = "<group>"; };
+ 263A27E51EE40C4B005C52B9 /* idna.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = idna.h; sourceTree = "<group>"; };
+ 263A27E61EE40C4B005C52B9 /* idna_props.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = idna_props.h; sourceTree = "<group>"; };
+ 263A27E71EE40C4B005C52B9 /* import-messages.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "import-messages.pl"; sourceTree = "<group>"; };
+ 263A27E81EE40C4B005C52B9 /* inet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inet.h; sourceTree = "<group>"; };
+ 263A27E91EE40C4B005C52B9 /* jenkins-build.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "jenkins-build.sh"; sourceTree = "<group>"; };
+ 263A27EA1EE40C4B005C52B9 /* libdom.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libdom.c; sourceTree = "<group>"; };
+ 263A27EB1EE40C4B005C52B9 /* libdom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libdom.h; sourceTree = "<group>"; };
+ 263A27EC1EE40C4B005C52B9 /* log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = log.c; sourceTree = "<group>"; };
+ 263A27ED1EE40C4B005C52B9 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = "<group>"; };
+ 263A27EE1EE40C4B005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A27EF1EE40C4B005C52B9 /* memanalyze.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = memanalyze.pl; sourceTree = "<group>"; };
+ 263A27F01EE40C4B005C52B9 /* merge-messages.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "merge-messages.lua"; sourceTree = "<group>"; };
+ 263A27F11EE40C4B005C52B9 /* messages.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = messages.c; sourceTree = "<group>"; };
+ 263A27F21EE40C4B005C52B9 /* messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = messages.h; sourceTree = "<group>"; };
+ 263A27F31EE40C4B005C52B9 /* nsoption.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nsoption.c; sourceTree = "<group>"; };
+ 263A27F41EE40C4B005C52B9 /* nsoption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nsoption.h; sourceTree = "<group>"; };
+ 263A27F61EE40C4B005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A27F71EE40C4B005C52B9 /* nsurl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nsurl.c; sourceTree = "<group>"; };
+ 263A27F81EE40C4B005C52B9 /* parse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parse.c; sourceTree = "<group>"; };
+ 263A27F91EE40C4B005C52B9 /* private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = private.h; sourceTree = "<group>"; };
+ 263A27FA1EE40C4B005C52B9 /* nsurl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nsurl.h; sourceTree = "<group>"; };
+ 263A27FB1EE40C4B005C52B9 /* punycode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = punycode.c; sourceTree = "<group>"; };
+ 263A27FC1EE40C4B005C52B9 /* punycode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = punycode.h; sourceTree = "<group>"; };
+ 263A27FD1EE40C4B005C52B9 /* ring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ring.h; sourceTree = "<group>"; };
+ 263A27FE1EE40C4B005C52B9 /* split-messages.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "split-messages.pl"; sourceTree = "<group>"; };
+ 263A27FF1EE40C4B005C52B9 /* string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string.h; sourceTree = "<group>"; };
+ 263A28001EE40C4B005C52B9 /* sys_time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sys_time.h; sourceTree = "<group>"; };
+ 263A28011EE40C4B005C52B9 /* talloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = talloc.c; sourceTree = "<group>"; };
+ 263A28021EE40C4B005C52B9 /* talloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = talloc.h; sourceTree = "<group>"; };
+ 263A28031EE40C4B005C52B9 /* test-netsurf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "test-netsurf"; sourceTree = "<group>"; };
+ 263A28041EE40C4B005C52B9 /* time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = time.c; sourceTree = "<group>"; };
+ 263A28051EE40C4B005C52B9 /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = time.h; sourceTree = "<group>"; };
+ 263A28061EE40C4B005C52B9 /* url.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = url.c; sourceTree = "<group>"; };
+ 263A28071EE40C4B005C52B9 /* url.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url.h; sourceTree = "<group>"; };
+ 263A28081EE40C4B005C52B9 /* useragent.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = useragent.c; sourceTree = "<group>"; };
+ 263A28091EE40C4B005C52B9 /* useragent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = useragent.h; sourceTree = "<group>"; };
+ 263A280A1EE40C4B005C52B9 /* utf8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utf8.c; sourceTree = "<group>"; };
+ 263A280B1EE40C4B005C52B9 /* utf8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utf8.h; sourceTree = "<group>"; };
+ 263A280C1EE40C4B005C52B9 /* utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = "<group>"; };
+ 263A280D1EE40C4B005C52B9 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; };
+ 263A280E1EE40C4B005C52B9 /* utsname.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utsname.h; sourceTree = "<group>"; };
+ 263A280F1EE40C4B005C52B9 /* valgrind.supp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = valgrind.supp; sourceTree = "<group>"; };
+ 263A28AB1EE41245005C52B9 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
+ 263A28AD1EE41266005C52B9 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
+ 263A28B01EE412B4005C52B9 /* content.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = content.c; sourceTree = "<group>"; };
+ 263A28B11EE412B4005C52B9 /* content.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = content.h; sourceTree = "<group>"; };
+ 263A28B31EE412B4005C52B9 /* Console.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Console.bnd; sourceTree = "<group>"; };
+ 263A28B41EE412B4005C52B9 /* Document.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Document.bnd; sourceTree = "<group>"; };
+ 263A28B51EE412B4005C52B9 /* duk_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = duk_config.h; sourceTree = "<group>"; };
+ 263A28B61EE412B4005C52B9 /* duk_custom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = duk_custom.h; sourceTree = "<group>"; };
+ 263A28B71EE412B4005C52B9 /* dukky.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dukky.c; sourceTree = "<group>"; };
+ 263A28B81EE412B4005C52B9 /* dukky.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dukky.h; sourceTree = "<group>"; };
+ 263A28B91EE412B4005C52B9 /* duktape.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = duktape.c; sourceTree = "<group>"; };
+ 263A28BA1EE412B4005C52B9 /* duktape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = duktape.h; sourceTree = "<group>"; };
+ 263A28BB1EE412B4005C52B9 /* Element.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Element.bnd; sourceTree = "<group>"; };
+ 263A28BC1EE412B4005C52B9 /* Event.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Event.bnd; sourceTree = "<group>"; };
+ 263A28BD1EE412B4005C52B9 /* EventTarget.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EventTarget.bnd; sourceTree = "<group>"; };
+ 263A28BE1EE412B4005C52B9 /* HTMLAnchorElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLAnchorElement.bnd; sourceTree = "<group>"; };
+ 263A28BF1EE412B4005C52B9 /* HTMLAppletElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLAppletElement.bnd; sourceTree = "<group>"; };
+ 263A28C01EE412B4005C52B9 /* HTMLAreaElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLAreaElement.bnd; sourceTree = "<group>"; };
+ 263A28C11EE412B4005C52B9 /* HTMLBaseElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLBaseElement.bnd; sourceTree = "<group>"; };
+ 263A28C21EE412B4005C52B9 /* HTMLBodyElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLBodyElement.bnd; sourceTree = "<group>"; };
+ 263A28C31EE412B4005C52B9 /* HTMLBRElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLBRElement.bnd; sourceTree = "<group>"; };
+ 263A28C41EE412B4005C52B9 /* HTMLButtonElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLButtonElement.bnd; sourceTree = "<group>"; };
+ 263A28C51EE412B4005C52B9 /* HTMLCollection.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLCollection.bnd; sourceTree = "<group>"; };
+ 263A28C61EE412B4005C52B9 /* HTMLDivElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLDivElement.bnd; sourceTree = "<group>"; };
+ 263A28C71EE412B4005C52B9 /* HTMLElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLElement.bnd; sourceTree = "<group>"; };
+ 263A28C81EE412B4005C52B9 /* HTMLFontElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLFontElement.bnd; sourceTree = "<group>"; };
+ 263A28C91EE412B4005C52B9 /* HTMLFormElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLFormElement.bnd; sourceTree = "<group>"; };
+ 263A28CA1EE412B4005C52B9 /* HTMLFrameElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLFrameElement.bnd; sourceTree = "<group>"; };
+ 263A28CB1EE412B4005C52B9 /* HTMLFrameSetElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLFrameSetElement.bnd; sourceTree = "<group>"; };
+ 263A28CC1EE412B4005C52B9 /* HTMLHeadingElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLHeadingElement.bnd; sourceTree = "<group>"; };
+ 263A28CD1EE412B4005C52B9 /* HTMLHRElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLHRElement.bnd; sourceTree = "<group>"; };
+ 263A28CE1EE412B4005C52B9 /* HTMLHTMLElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLHTMLElement.bnd; sourceTree = "<group>"; };
+ 263A28CF1EE412B4005C52B9 /* HTMLIFrameElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLIFrameElement.bnd; sourceTree = "<group>"; };
+ 263A28D01EE412B4005C52B9 /* HTMLImageElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLImageElement.bnd; sourceTree = "<group>"; };
+ 263A28D11EE412B4005C52B9 /* HTMLInputElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLInputElement.bnd; sourceTree = "<group>"; };
+ 263A28D21EE412B4005C52B9 /* HTMLLabelElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLLabelElement.bnd; sourceTree = "<group>"; };
+ 263A28D31EE412B4005C52B9 /* HTMLLegendElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLLegendElement.bnd; sourceTree = "<group>"; };
+ 263A28D41EE412B4005C52B9 /* HTMLLIElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLLIElement.bnd; sourceTree = "<group>"; };
+ 263A28D51EE412B4005C52B9 /* HTMLLinkElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLLinkElement.bnd; sourceTree = "<group>"; };
+ 263A28D61EE412B4005C52B9 /* HTMLMapElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLMapElement.bnd; sourceTree = "<group>"; };
+ 263A28D71EE412B4005C52B9 /* HTMLMarqueeElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLMarqueeElement.bnd; sourceTree = "<group>"; };
+ 263A28D81EE412B4005C52B9 /* HTMLMenuElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLMenuElement.bnd; sourceTree = "<group>"; };
+ 263A28D91EE412B4005C52B9 /* HTMLMetaElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLMetaElement.bnd; sourceTree = "<group>"; };
+ 263A28DA1EE412B4005C52B9 /* HTMLObjectElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLObjectElement.bnd; sourceTree = "<group>"; };
+ 263A28DB1EE412B4005C52B9 /* HTMLOListElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLOListElement.bnd; sourceTree = "<group>"; };
+ 263A28DC1EE412B4005C52B9 /* HTMLOptionElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLOptionElement.bnd; sourceTree = "<group>"; };
+ 263A28DD1EE412B4005C52B9 /* HTMLParagraphElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLParagraphElement.bnd; sourceTree = "<group>"; };
+ 263A28DE1EE412B4005C52B9 /* HTMLParamElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLParamElement.bnd; sourceTree = "<group>"; };
+ 263A28DF1EE412B4005C52B9 /* HTMLPreElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLPreElement.bnd; sourceTree = "<group>"; };
+ 263A28E01EE412B4005C52B9 /* HTMLQuoteElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLQuoteElement.bnd; sourceTree = "<group>"; };
+ 263A28E11EE412B4005C52B9 /* HTMLScriptElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLScriptElement.bnd; sourceTree = "<group>"; };
+ 263A28E21EE412B4005C52B9 /* HTMLSelectElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLSelectElement.bnd; sourceTree = "<group>"; };
+ 263A28E31EE412B4005C52B9 /* HTMLStyleElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLStyleElement.bnd; sourceTree = "<group>"; };
+ 263A28E41EE412B4005C52B9 /* HTMLTableCaptionElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTableCaptionElement.bnd; sourceTree = "<group>"; };
+ 263A28E51EE412B4005C52B9 /* HTMLTableCellElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTableCellElement.bnd; sourceTree = "<group>"; };
+ 263A28E61EE412B4005C52B9 /* HTMLTableColElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTableColElement.bnd; sourceTree = "<group>"; };
+ 263A28E71EE412B4005C52B9 /* HTMLTableElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTableElement.bnd; sourceTree = "<group>"; };
+ 263A28E81EE412B4005C52B9 /* HTMLTableRowElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTableRowElement.bnd; sourceTree = "<group>"; };
+ 263A28E91EE412B4005C52B9 /* HTMLTableSectionElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTableSectionElement.bnd; sourceTree = "<group>"; };
+ 263A28EA1EE412B4005C52B9 /* HTMLTextAreaElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTextAreaElement.bnd; sourceTree = "<group>"; };
+ 263A28EB1EE412B4005C52B9 /* HTMLTitleElement.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTitleElement.bnd; sourceTree = "<group>"; };
+ 263A28EC1EE412B4005C52B9 /* Location.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Location.bnd; sourceTree = "<group>"; };
+ 263A28ED1EE412B4005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A28EE1EE412B4005C52B9 /* Navigator.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Navigator.bnd; sourceTree = "<group>"; };
+ 263A28EF1EE412B4005C52B9 /* netsurf.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = netsurf.bnd; sourceTree = "<group>"; };
+ 263A28F01EE412B4005C52B9 /* Node.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Node.bnd; sourceTree = "<group>"; };
+ 263A28F11EE412B4005C52B9 /* NodeList.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NodeList.bnd; sourceTree = "<group>"; };
+ 263A28F21EE412B4005C52B9 /* Window.bnd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Window.bnd; sourceTree = "<group>"; };
+ 263A28F31EE412B4005C52B9 /* fetcher.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fetcher.c; sourceTree = "<group>"; };
+ 263A28F41EE412B4005C52B9 /* fetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fetcher.h; sourceTree = "<group>"; };
+ 263A28F51EE412B4005C52B9 /* js.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = js.h; sourceTree = "<group>"; };
+ 263A28F61EE412B4005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A28F81EE412B4005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A28F91EE412B4005C52B9 /* none.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = none.c; sourceTree = "<group>"; };
+ 263A28FB1EE412B4005C52B9 /* console.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = console.idl; sourceTree = "<group>"; };
+ 263A28FC1EE412B4005C52B9 /* cssom.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = cssom.idl; sourceTree = "<group>"; };
+ 263A28FD1EE412B4005C52B9 /* dom-parsing.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "dom-parsing.idl"; sourceTree = "<group>"; };
+ 263A28FE1EE412B4005C52B9 /* dom.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dom.idl; sourceTree = "<group>"; };
+ 263A28FF1EE412B4005C52B9 /* html.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = html.idl; sourceTree = "<group>"; };
+ 263A29001EE412B4005C52B9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ 263A29011EE412B4005C52B9 /* uievents.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = uievents.idl; sourceTree = "<group>"; };
+ 263A29021EE412B4005C52B9 /* urlutils.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = urlutils.idl; sourceTree = "<group>"; };
+ 263A29061EE4196E005C52B9 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ 26890F951EE89C5F00063C30 /* arc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = arc.h; sourceTree = SOURCE_ROOT; };
+ 2689BB2D1EE543E20090B679 /* PSMTabBarControl+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PSMTabBarControl+Private.h"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 263A25EB1EE40BD6005C52B9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 263A29071EE4196E005C52B9 /* Cocoa.framework in Frameworks */,
+ 263A28AE1EE41266005C52B9 /* libz.tbd in Frameworks */,
+ 263A28AC1EE41245005C52B9 /* libiconv.tbd in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 2636299412F699250048542C /* NetSurf.app */,
+ 263A25EE1EE40BD6005C52B9 /* NetSurfBrowser.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 261DB24E1318443500C59F12 /* Tools */ = {
+ isa = PBXGroup;
+ children = (
+ 261DB24F1318444F00C59F12 /* compile-xib.sh */,
+ 261DB2501318444F00C59F12 /* extract-strings.sh */,
+ );
+ name = Tools;
+ sourceTree = SOURCE_ROOT;
+ };
+ 263A25EF1EE40BD6005C52B9 /* Cocoa */ = {
+ isa = PBXGroup;
+ children = (
+ 26890F951EE89C5F00063C30 /* arc.h */,
+ 263A26021EE40BFC005C52B9 /* PSMTabBarControl */,
+ 263A262C1EE40BFC005C52B9 /* res */,
+ 263A26611EE40BFC005C52B9 /* apple_image.h */,
+ 263A26621EE40BFC005C52B9 /* ArrowBox.h */,
+ 263A26631EE40BFC005C52B9 /* ArrowWindow.h */,
+ 263A26641EE40BFC005C52B9 /* bitmap.h */,
+ 263A26661EE40BFC005C52B9 /* BookmarksController.h */,
+ 263A26671EE40BFC005C52B9 /* BrowserView.h */,
+ 263A26681EE40BFC005C52B9 /* BrowserViewController.h */,
+ 263A26691EE40BFC005C52B9 /* BrowserWindow.h */,
+ 263A266A1EE40BFC005C52B9 /* BrowserWindowController.h */,
+ 263A266B1EE40BFC005C52B9 /* coordinates.h */,
+ 263A266C1EE40BFC005C52B9 /* desktop-tree.h */,
+ 263A266D1EE40BFC005C52B9 /* DownloadWindowController.h */,
+ 263A266E1EE40BFC005C52B9 /* fetch.h */,
+ 263A266F1EE40BFC005C52B9 /* font.h */,
+ 263A26701EE40BFC005C52B9 /* FormSelectMenu.h */,
+ 263A26711EE40BFC005C52B9 /* gui.h */,
+ 263A26721EE40BFC005C52B9 /* HistoryView.h */,
+ 263A26731EE40BFC005C52B9 /* HistoryWindowController.h */,
+ 263A26741EE40BFC005C52B9 /* LocalHistoryController.h */,
+ 263A26751EE40BFC005C52B9 /* NetsurfApp.h */,
+ 263A26761EE40BFC005C52B9 /* NetSurfAppDelegate.h */,
+ 263A26771EE40BFC005C52B9 /* plotter.h */,
+ 263A26781EE40BFC005C52B9 /* PreferencesWindowController.h */,
+ 263A26791EE40BFC005C52B9 /* schedule.h */,
+ 263A267A1EE40BFC005C52B9 /* ScrollableView.h */,
+ 263A267B1EE40BFC005C52B9 /* SearchWindowController.h */,
+ 263A267C1EE40BFC005C52B9 /* selection.h */,
+ 263A267D1EE40BFC005C52B9 /* Tree.h */,
+ 263A267E1EE40BFC005C52B9 /* TreeView.h */,
+ 263A267F1EE40BFC005C52B9 /* URLFieldCell.h */,
+ 263A26801EE40BFC005C52B9 /* Prefix.pch */,
+ 263A26811EE40BFC005C52B9 /* Makefile.defaults */,
+ 263A26821EE40BFC005C52B9 /* apple_image.m */,
+ 263A26831EE40BFC005C52B9 /* ArrowBox.m */,
+ 263A26841EE40BFC005C52B9 /* ArrowWindow.m */,
+ 263A26851EE40BFC005C52B9 /* bitmap.m */,
+ 263A26871EE40BFC005C52B9 /* BookmarksController.m */,
+ 263A26881EE40BFC005C52B9 /* BrowserView.m */,
+ 263A26891EE40BFC005C52B9 /* BrowserViewController.m */,
+ 263A268A1EE40BFC005C52B9 /* BrowserWindow.m */,
+ 263A268B1EE40BFC005C52B9 /* BrowserWindowController.m */,
+ 263A268C1EE40BFC005C52B9 /* desktop-tree.m */,
+ 263A268D1EE40BFC005C52B9 /* DownloadWindowController.m */,
+ 263A268E1EE40BFC005C52B9 /* fetch.m */,
+ 263A268F1EE40BFC005C52B9 /* font.m */,
+ 263A26901EE40BFC005C52B9 /* FormSelectMenu.m */,
+ 263A26911EE40BFC005C52B9 /* gui.m */,
+ 263A26921EE40BFC005C52B9 /* HistoryView.m */,
+ 263A26931EE40BFC005C52B9 /* HistoryWindowController.m */,
+ 263A26941EE40BFC005C52B9 /* LocalHistoryController.m */,
+ 263A26951EE40BFC005C52B9 /* NetsurfApp.m */,
+ 263A26961EE40BFC005C52B9 /* NetSurfAppDelegate.m */,
+ 263A26971EE40BFC005C52B9 /* plotter.m */,
+ 263A26981EE40BFC005C52B9 /* PreferencesWindowController.m */,
+ 263A26991EE40BFC005C52B9 /* schedule.m */,
+ 263A269A1EE40BFC005C52B9 /* ScrollableView.m */,
+ 263A269B1EE40BFC005C52B9 /* SearchWindowController.m */,
+ 263A269C1EE40BFC005C52B9 /* selection.m */,
+ 263A269D1EE40BFC005C52B9 /* Tree.m */,
+ 263A269E1EE40BFC005C52B9 /* TreeView.m */,
+ 263A269F1EE40BFC005C52B9 /* URLFieldCell.m */,
+ );
+ name = Cocoa;
+ sourceTree = SOURCE_ROOT;
+ };
+ 263A26021EE40BFC005C52B9 /* PSMTabBarControl */ = {
+ isa = PBXGroup;
+ children = (
+ 263A26031EE40BFC005C52B9 /* Images */,
+ 263A26101EE40BFC005C52B9 /* NSBezierPath_AMShading.h */,
+ 263A26111EE40BFC005C52B9 /* NSBezierPath_AMShading.m */,
+ 263A26121EE40BFC005C52B9 /* NSString_AITruncation.h */,
+ 263A26131EE40BFC005C52B9 /* NSString_AITruncation.m */,
+ 263A26141EE40BFC005C52B9 /* PSMOverflowPopUpButton.h */,
+ 263A26151EE40BFC005C52B9 /* PSMOverflowPopUpButton.m */,
+ 263A26161EE40BFC005C52B9 /* PSMProgressIndicator.h */,
+ 263A26171EE40BFC005C52B9 /* PSMProgressIndicator.m */,
+ 263A26181EE40BFC005C52B9 /* PSMRolloverButton.h */,
+ 263A26191EE40BFC005C52B9 /* PSMRolloverButton.m */,
+ 263A261A1EE40BFC005C52B9 /* PSMTabBarCell.h */,
+ 263A261B1EE40BFC005C52B9 /* PSMTabBarCell.m */,
+ 263A261C1EE40BFC005C52B9 /* PSMTabBarControl.h */,
+ 263A261D1EE40BFC005C52B9 /* PSMTabBarControl.m */,
+ 263A261E1EE40BFC005C52B9 /* PSMTabBarController.h */,
+ 263A261F1EE40BFC005C52B9 /* PSMTabBarController.m */,
+ 263A26201EE40BFC005C52B9 /* PSMTabDragAssistant.h */,
+ 263A26211EE40BFC005C52B9 /* PSMTabDragAssistant.m */,
+ 263A26221EE40BFC005C52B9 /* PSMTabDragView.h */,
+ 263A26231EE40BFC005C52B9 /* PSMTabDragView.m */,
+ 263A26241EE40BFC005C52B9 /* PSMTabDragWindow.h */,
+ 263A26251EE40BFC005C52B9 /* PSMTabDragWindow.m */,
+ 263A26261EE40BFC005C52B9 /* PSMTabDragWindowController.h */,
+ 263A26271EE40BFC005C52B9 /* PSMTabDragWindowController.m */,
+ 263A26281EE40BFC005C52B9 /* PSMTabStyle.h */,
+ 263A26291EE40BFC005C52B9 /* PSMUnifiedTabStyle.h */,
+ 263A262A1EE40BFC005C52B9 /* PSMUnifiedTabStyle.m */,
+ 263A262B1EE40BFC005C52B9 /* ReadMe.rtfd */,
+ 2689BB2D1EE543E20090B679 /* PSMTabBarControl+Private.h */,
+ );
+ path = PSMTabBarControl;
+ sourceTree = "<group>";
+ };
+ 263A26031EE40BFC005C52B9 /* Images */ = {
+ isa = PBXGroup;
+ children = (
+ 263A26041EE40BFC005C52B9 /* AquaTabClose_Front.png */,
+ 263A26051EE40BFC005C52B9 /* AquaTabClose_Front_Pressed.png */,
+ 263A26061EE40BFC005C52B9 /* AquaTabClose_Front_Rollover.png */,
+ 263A26071EE40BFC005C52B9 /* AquaTabCloseDirty_Front.png */,
+ 263A26081EE40BFC005C52B9 /* AquaTabCloseDirty_Front_Pressed.png */,
+ 263A26091EE40BFC005C52B9 /* AquaTabCloseDirty_Front_Rollover.png */,
+ 263A260A1EE40BFC005C52B9 /* AquaTabNew.png */,
+ 263A260B1EE40BFC005C52B9 /* AquaTabNewPressed.png */,
+ 263A260C1EE40BFC005C52B9 /* AquaTabNewRollover.png */,
+ 263A260D1EE40BFC005C52B9 /* overflowImage.png */,
+ 263A260E1EE40BFC005C52B9 /* overflowImagePressed.png */,
+ 263A260F1EE40BFC005C52B9 /* pi.png */,
+ );
+ path = Images;
+ sourceTree = "<group>";
+ };
+ 263A262C1EE40BFC005C52B9 /* res */ = {
+ isa = PBXGroup;
+ children = (
+ 263A262D1EE40BFC005C52B9 /* adblock.css */,
+ 263A262E1EE40BFC005C52B9 /* BookmarksWindow.xib */,
+ 263A262F1EE40BFC005C52B9 /* Browser.xib */,
+ 263A26301EE40BFC005C52B9 /* BrowserWindow.xib */,
+ 263A26311EE40BFC005C52B9 /* ca-bundle */,
+ 263A26321EE40BFC005C52B9 /* BookmarksWindow.xib.strings */,
+ 263A26341EE40BFC005C52B9 /* BrowserWindow.xib.strings */,
+ 263A26361EE40BFC005C52B9 /* DownloadWindow.xib.strings */,
+ 263A26381EE40BFC005C52B9 /* HistoryWindow.xib.strings */,
+ 263A263A1EE40BFC005C52B9 /* Localizable.strings */,
+ 263A263C1EE40BFC005C52B9 /* MainMenu.xib.strings */,
+ 263A263E1EE40BFC005C52B9 /* Messages */,
+ 263A26401EE40BFC005C52B9 /* PreferencesWindow.xib.strings */,
+ 263A26421EE40BFC005C52B9 /* SearchWindow.xib.strings */,
+ 263A26441EE40BFC005C52B9 /* default.css */,
+ 263A26451EE40BFC005C52B9 /* DownloadWindow.xib */,
+ 263A264A1EE40BFC005C52B9 /* HistoryWindow.xib */,
+ 263A264B1EE40BFC005C52B9 /* HomeTemplate.pdf */,
+ 263A264C1EE40BFC005C52B9 /* Icons */,
+ 263A26541EE40BFC005C52B9 /* internal.css */,
+ 263A26571EE40BFC005C52B9 /* LocalHistoryPanel.xib */,
+ 263A26581EE40BFC005C52B9 /* MainMenu.xib */,
+ 263A26591EE40BFC005C52B9 /* NetSurf-Info.plist */,
+ 263A265A1EE40BFC005C52B9 /* NetSurf.icns */,
+ 263A265B1EE40BFC005C52B9 /* netsurf.png */,
+ 263A265E1EE40BFC005C52B9 /* PreferencesWindow.xib */,
+ 263A265F1EE40BFC005C52B9 /* quirks.css */,
+ 263A26601EE40BFC005C52B9 /* SearchWindow.xib */,
+ );
+ path = res;
+ sourceTree = "<group>";
+ };
+ 263A264C1EE40BFC005C52B9 /* Icons */ = {
+ isa = PBXGroup;
+ children = (
+ 263A264D1EE40BFC005C52B9 /* arrow-l.png */,
+ 263A264E1EE40BFC005C52B9 /* content.png */,
+ 263A264F1EE40BFC005C52B9 /* directory.png */,
+ 263A26501EE40BFC005C52B9 /* directory2.png */,
+ 263A26511EE40BFC005C52B9 /* hotlist-add.png */,
+ 263A26521EE40BFC005C52B9 /* hotlist-rmv.png */,
+ 263A26531EE40BFC005C52B9 /* search.png */,
+ );
+ path = Icons;
+ sourceTree = "<group>";
+ };
+ 263A26A01EE40C1E005C52B9 /* Engine */ = {
+ isa = PBXGroup;
+ children = (
+ 263A26A11EE40C4A005C52B9 /* content */,
+ 263A27411EE40C4B005C52B9 /* desktop */,
+ 263A277D1EE40C4B005C52B9 /* include */,
+ 263A27971EE40C4B005C52B9 /* render */,
+ 263A27B81EE40C4B005C52B9 /* utils */,
+ );
+ name = Engine;
+ path = ../..;
+ sourceTree = SOURCE_ROOT;
+ };
+ 263A26A11EE40C4A005C52B9 /* content */ = {
+ isa = PBXGroup;
+ children = (
+ 263A26A21EE40C4A005C52B9 /* backing_store.h */,
+ 263A26A31EE40C4A005C52B9 /* content.c */,
+ 263A26A41EE40C4A005C52B9 /* content.h */,
+ 263A26A51EE40C4A005C52B9 /* content_debug.h */,
+ 263A26A61EE40C4A005C52B9 /* content_factory.c */,
+ 263A26A71EE40C4A005C52B9 /* content_factory.h */,
+ 263A26A81EE40C4A005C52B9 /* content_protected.h */,
+ 263A26A91EE40C4A005C52B9 /* dirlist.c */,
+ 263A26AA1EE40C4A005C52B9 /* dirlist.h */,
+ 263A26AB1EE40C4A005C52B9 /* fetch.c */,
+ 263A26AC1EE40C4A005C52B9 /* fetch.h */,
+ 263A26AD1EE40C4A005C52B9 /* fetchers */,
+ 263A26B91EE40C4A005C52B9 /* fetchers.h */,
+ 263A26BA1EE40C4A005C52B9 /* fs_backing_store.c */,
+ 263A26BB1EE40C4A005C52B9 /* handlers */,
+ 263A27371EE40C4B005C52B9 /* hlcache.c */,
+ 263A27381EE40C4B005C52B9 /* hlcache.h */,
+ 263A27391EE40C4B005C52B9 /* llcache.c */,
+ 263A273A1EE40C4B005C52B9 /* llcache.h */,
+ 263A273B1EE40C4B005C52B9 /* Makefile */,
+ 263A273C1EE40C4B005C52B9 /* mimesniff.c */,
+ 263A273D1EE40C4B005C52B9 /* mimesniff.h */,
+ 263A273E1EE40C4B005C52B9 /* no_backing_store.c */,
+ 263A273F1EE40C4B005C52B9 /* urldb.c */,
+ 263A27401EE40C4B005C52B9 /* urldb.h */,
+ );
+ path = content;
+ sourceTree = "<group>";
+ };
+ 263A26AD1EE40C4A005C52B9 /* fetchers */ = {
+ isa = PBXGroup;
+ children = (
+ 263A26AE1EE40C4A005C52B9 /* about.c */,
+ 263A26AF1EE40C4A005C52B9 /* about.h */,
+ 263A26B01EE40C4A005C52B9 /* curl.c */,
+ 263A26B11EE40C4A005C52B9 /* curl.h */,
+ 263A26B21EE40C4A005C52B9 /* data.c */,
+ 263A26B31EE40C4A005C52B9 /* data.h */,
+ 263A26B41EE40C4A005C52B9 /* file.c */,
+ 263A26B51EE40C4A005C52B9 /* file.h */,
+ 263A26B61EE40C4A005C52B9 /* Makefile */,
+ 263A26B71EE40C4A005C52B9 /* resource.c */,
+ 263A26B81EE40C4A005C52B9 /* resource.h */,
+ );
+ path = fetchers;
+ sourceTree = "<group>";
+ };
+ 263A26BB1EE40C4A005C52B9 /* handlers */ = {
+ isa = PBXGroup;
+ children = (
+ 263A28AF1EE412B4005C52B9 /* javascript */,
+ 263A26BC1EE40C4A005C52B9 /* css */,
+ 263A26CA1EE40C4A005C52B9 /* image */,
+ 263A27361EE40C4B005C52B9 /* Makefile */,
+ );
+ path = handlers;
+ sourceTree = "<group>";
+ };
+ 263A26BC1EE40C4A005C52B9 /* css */ = {
+ isa = PBXGroup;
+ children = (
+ 263A26BD1EE40C4A005C52B9 /* css.c */,
+ 263A26BE1EE40C4A005C52B9 /* css.h */,
+ 263A26BF1EE40C4A005C52B9 /* dump.c */,
+ 263A26C01EE40C4A005C52B9 /* dump.h */,
+ 263A26C11EE40C4A005C52B9 /* hints.c */,
+ 263A26C21EE40C4A005C52B9 /* hints.h */,
+ 263A26C31EE40C4A005C52B9 /* internal.c */,
+ 263A26C41EE40C4A005C52B9 /* internal.h */,
+ 263A26C51EE40C4A005C52B9 /* Makefile */,
+ 263A26C61EE40C4A005C52B9 /* select.c */,
+ 263A26C71EE40C4A005C52B9 /* select.h */,
+ 263A26C81EE40C4A005C52B9 /* utils.c */,
+ 263A26C91EE40C4A005C52B9 /* utils.h */,
+ );
+ path = css;
+ sourceTree = "<group>";
+ };
+ 263A26CA1EE40C4A005C52B9 /* image */ = {
+ isa = PBXGroup;
+ children = (
+ 263A26CB1EE40C4A005C52B9 /* bmp.c */,
+ 263A26CC1EE40C4A005C52B9 /* bmp.h */,
+ 263A26CD1EE40C4A005C52B9 /* gif.c */,
+ 263A26CE1EE40C4A005C52B9 /* gif.h */,
+ 263A26CF1EE40C4A005C52B9 /* ico.c */,
+ 263A26D01EE40C4A005C52B9 /* ico.h */,
+ 263A26D11EE40C4A005C52B9 /* image.c */,
+ 263A26D21EE40C4A005C52B9 /* image.h */,
+ 263A26D31EE40C4A005C52B9 /* image_cache.c */,
+ 263A26D41EE40C4A005C52B9 /* image_cache.h */,
+ 263A26D61EE40C4A005C52B9 /* jpeg.h */,
+ 263A26D71EE40C4A005C52B9 /* Makefile */,
+ 263A26D91EE40C4A005C52B9 /* nssprite.h */,
+ 263A26DB1EE40C4A005C52B9 /* png.h */,
+ 263A26DD1EE40C4A005C52B9 /* rsvg.h */,
+ 263A26DF1EE40C4A005C52B9 /* svg.h */,
+ 263A26E11EE40C4A005C52B9 /* video.h */,
+ );
+ path = image;
+ sourceTree = "<group>";
+ };
+ 263A27411EE40C4B005C52B9 /* desktop */ = {
+ isa = PBXGroup;
+ children = (
+ 263A27421EE40C4B005C52B9 /* browser.c */,
+ 263A27431EE40C4B005C52B9 /* browser_history.c */,
+ 263A27441EE40C4B005C52B9 /* browser_history.h */,
+ 263A27451EE40C4B005C52B9 /* browser_private.h */,
+ 263A27461EE40C4B005C52B9 /* cookie_manager.c */,
+ 263A27471EE40C4B005C52B9 /* cookie_manager.h */,
+ 263A27481EE40C4B005C52B9 /* download.c */,
+ 263A27491EE40C4B005C52B9 /* download.h */,
+ 263A274A1EE40C4B005C52B9 /* font_haru.c */,
+ 263A274B1EE40C4B005C52B9 /* font_haru.h */,
+ 263A274C1EE40C4B005C52B9 /* frame_types.h */,
+ 263A274D1EE40C4B005C52B9 /* frames.c */,
+ 263A274E1EE40C4B005C52B9 /* frames.h */,
+ 263A274F1EE40C4B005C52B9 /* global_history.c */,
+ 263A27501EE40C4B005C52B9 /* global_history.h */,
+ 263A27511EE40C4B005C52B9 /* gui_factory.c */,
+ 263A27521EE40C4B005C52B9 /* gui_internal.h */,
+ 263A27531EE40C4B005C52B9 /* gui_table.h */,
+ 263A27541EE40C4B005C52B9 /* hotlist.c */,
+ 263A27551EE40C4B005C52B9 /* hotlist.h */,
+ 263A27561EE40C4B005C52B9 /* knockout.c */,
+ 263A27571EE40C4B005C52B9 /* knockout.h */,
+ 263A27581EE40C4B005C52B9 /* local_history.c */,
+ 263A27591EE40C4B005C52B9 /* local_history.h */,
+ 263A275A1EE40C4B005C52B9 /* Makefile */,
+ 263A275B1EE40C4B005C52B9 /* mouse.c */,
+ 263A275C1EE40C4B005C52B9 /* netsurf.c */,
+ 263A275D1EE40C4B005C52B9 /* options.h */,
+ 263A275E1EE40C4B005C52B9 /* plot_style.c */,
+ 263A275F1EE40C4B005C52B9 /* print.c */,
+ 263A27601EE40C4B005C52B9 /* print.h */,
+ 263A27611EE40C4B005C52B9 /* printer.h */,
+ 263A27621EE40C4B005C52B9 /* save_complete.c */,
+ 263A27631EE40C4B005C52B9 /* save_complete.h */,
+ 263A27641EE40C4B005C52B9 /* save_pdf.c */,
+ 263A27651EE40C4B005C52B9 /* save_pdf.h */,
+ 263A27661EE40C4B005C52B9 /* save_text.c */,
+ 263A27671EE40C4B005C52B9 /* save_text.h */,
+ 263A27681EE40C4B005C52B9 /* scrollbar.c */,
+ 263A27691EE40C4B005C52B9 /* scrollbar.h */,
+ 263A276A1EE40C4B005C52B9 /* search.c */,
+ 263A276B1EE40C4B005C52B9 /* search.h */,
+ 263A276C1EE40C4B005C52B9 /* searchweb.c */,
+ 263A276D1EE40C4B005C52B9 /* searchweb.h */,
+ 263A276E1EE40C4B005C52B9 /* selection.c */,
+ 263A276F1EE40C4B005C52B9 /* selection.h */,
+ 263A27701EE40C4B005C52B9 /* sslcert_viewer.c */,
+ 263A27711EE40C4B005C52B9 /* sslcert_viewer.h */,
+ 263A27721EE40C4B005C52B9 /* system_colour.c */,
+ 263A27731EE40C4B005C52B9 /* system_colour.h */,
+ 263A27741EE40C4B005C52B9 /* textarea.c */,
+ 263A27751EE40C4B005C52B9 /* textarea.h */,
+ 263A27761EE40C4B005C52B9 /* textinput.c */,
+ 263A27771EE40C4B005C52B9 /* textinput.h */,
+ 263A27781EE40C4B005C52B9 /* theme.h */,
+ 263A27791EE40C4B005C52B9 /* treeview.c */,
+ 263A277A1EE40C4B005C52B9 /* treeview.h */,
+ 263A277B1EE40C4B005C52B9 /* version.c */,
+ 263A277C1EE40C4B005C52B9 /* version.h */,
+ );
+ path = desktop;
+ sourceTree = "<group>";
+ };
+ 263A277D1EE40C4B005C52B9 /* include */ = {
+ isa = PBXGroup;
+ children = (
+ 263A277E1EE40C4B005C52B9 /* netsurf */,
+ );
+ path = include;
+ sourceTree = "<group>";
+ };
+ 263A277E1EE40C4B005C52B9 /* netsurf */ = {
+ isa = PBXGroup;
+ children = (
+ 263A277F1EE40C4B005C52B9 /* bitmap.h */,
+ 263A27801EE40C4B005C52B9 /* browser_window.h */,
+ 263A27811EE40C4B005C52B9 /* clipboard.h */,
+ 263A27821EE40C4B005C52B9 /* content.h */,
+ 263A27831EE40C4B005C52B9 /* content_type.h */,
+ 263A27841EE40C4B005C52B9 /* cookie_db.h */,
+ 263A27851EE40C4B005C52B9 /* core_window.h */,
+ 263A27861EE40C4B005C52B9 /* css.h */,
+ 263A27871EE40C4B005C52B9 /* download.h */,
+ 263A27881EE40C4B005C52B9 /* fetch.h */,
+ 263A27891EE40C4B005C52B9 /* form.h */,
+ 263A278A1EE40C4B005C52B9 /* inttypes.h */,
+ 263A278B1EE40C4B005C52B9 /* keypress.h */,
+ 263A278C1EE40C4B005C52B9 /* layout.h */,
+ 263A278D1EE40C4B005C52B9 /* misc.h */,
+ 263A278E1EE40C4B005C52B9 /* mouse.h */,
+ 263A278F1EE40C4B005C52B9 /* netsurf.h */,
+ 263A27901EE40C4B005C52B9 /* plot_style.h */,
+ 263A27911EE40C4B005C52B9 /* plotters.h */,
+ 263A27921EE40C4B005C52B9 /* search.h */,
+ 263A27931EE40C4B005C52B9 /* types.h */,
+ 263A27941EE40C4B005C52B9 /* url_db.h */,
+ 263A27951EE40C4B005C52B9 /* utf8.h */,
+ 263A27961EE40C4B005C52B9 /* window.h */,
+ );
+ path = netsurf;
+ sourceTree = "<group>";
+ };
+ 263A27971EE40C4B005C52B9 /* render */ = {
+ isa = PBXGroup;
+ children = (
+ 263A27981EE40C4B005C52B9 /* box.c */,
+ 263A27991EE40C4B005C52B9 /* box.h */,
+ 263A279A1EE40C4B005C52B9 /* box_construct.c */,
+ 263A279B1EE40C4B005C52B9 /* box_normalise.c */,
+ 263A279C1EE40C4B005C52B9 /* box_textarea.c */,
+ 263A279D1EE40C4B005C52B9 /* box_textarea.h */,
+ 263A279E1EE40C4B005C52B9 /* font.c */,
+ 263A279F1EE40C4B005C52B9 /* font.h */,
+ 263A27A01EE40C4B005C52B9 /* form.c */,
+ 263A27A11EE40C4B005C52B9 /* form_internal.h */,
+ 263A27A21EE40C4B005C52B9 /* html.c */,
+ 263A27A31EE40C4B005C52B9 /* html.h */,
+ 263A27A41EE40C4B005C52B9 /* html_css.c */,
+ 263A27A51EE40C4B005C52B9 /* html_css_fetcher.c */,
+ 263A27A61EE40C4B005C52B9 /* html_forms.c */,
+ 263A27A71EE40C4B005C52B9 /* html_interaction.c */,
+ 263A27A81EE40C4B005C52B9 /* html_internal.h */,
+ 263A27A91EE40C4B005C52B9 /* html_object.c */,
+ 263A27AA1EE40C4B005C52B9 /* html_redraw.c */,
+ 263A27AB1EE40C4B005C52B9 /* html_redraw_border.c */,
+ 263A27AC1EE40C4B005C52B9 /* html_script.c */,
+ 263A27AD1EE40C4B005C52B9 /* imagemap.c */,
+ 263A27AE1EE40C4B005C52B9 /* imagemap.h */,
+ 263A27AF1EE40C4B005C52B9 /* layout.c */,
+ 263A27B01EE40C4B005C52B9 /* layout.h */,
+ 263A27B11EE40C4B005C52B9 /* Makefile */,
+ 263A27B21EE40C4B005C52B9 /* search.c */,
+ 263A27B31EE40C4B005C52B9 /* search.h */,
+ 263A27B41EE40C4B005C52B9 /* table.c */,
+ 263A27B51EE40C4B005C52B9 /* table.h */,
+ 263A27B61EE40C4B005C52B9 /* textplain.c */,
+ 263A27B71EE40C4B005C52B9 /* textplain.h */,
+ );
+ path = render;
+ sourceTree = "<group>";
+ };
+ 263A27B81EE40C4B005C52B9 /* utils */ = {
+ isa = PBXGroup;
+ children = (
+ 263A27B91EE40C4B005C52B9 /* ascii.h */,
+ 263A27BA1EE40C4B005C52B9 /* bloom.c */,
+ 263A27BB1EE40C4B005C52B9 /* bloom.h */,
+ 263A27BC1EE40C4B005C52B9 /* config.h */,
+ 263A27BD1EE40C4B005C52B9 /* corestringlist.h */,
+ 263A27BE1EE40C4B005C52B9 /* corestrings.c */,
+ 263A27BF1EE40C4B005C52B9 /* corestrings.h */,
+ 263A27C01EE40C4B005C52B9 /* coverity-build.sh */,
+ 263A27C11EE40C4B005C52B9 /* DerivedJoiningType.txt */,
+ 263A27C21EE40C4B005C52B9 /* dirent.h */,
+ 263A27C31EE40C4B005C52B9 /* errors.h */,
+ 263A27C41EE40C4B005C52B9 /* fetch-transifex.pl */,
+ 263A27C51EE40C4B005C52B9 /* file.c */,
+ 263A27C61EE40C4B005C52B9 /* file.h */,
+ 263A27C71EE40C4B005C52B9 /* filename.c */,
+ 263A27C81EE40C4B005C52B9 /* filename.h */,
+ 263A27C91EE40C4B005C52B9 /* filepath.c */,
+ 263A27CA1EE40C4B005C52B9 /* filepath.h */,
+ 263A27CB1EE40C4B005C52B9 /* git-date.sh */,
+ 263A27CC1EE40C4B005C52B9 /* git-testament.pl */,
+ 263A27CD1EE40C4B005C52B9 /* hashtable.c */,
+ 263A27CE1EE40C4B005C52B9 /* hashtable.h */,
+ 263A27CF1EE40C4B005C52B9 /* http */,
+ 263A27E11EE40C4B005C52B9 /* http.h */,
+ 263A27E21EE40C4B005C52B9 /* idna-derived-props-gen.pl */,
+ 263A27E31EE40C4B005C52B9 /* idna-tables-5.2.0-properties.csv */,
+ 263A27E41EE40C4B005C52B9 /* idna.c */,
+ 263A27E51EE40C4B005C52B9 /* idna.h */,
+ 263A27E61EE40C4B005C52B9 /* idna_props.h */,
+ 263A27E71EE40C4B005C52B9 /* import-messages.pl */,
+ 263A27E81EE40C4B005C52B9 /* inet.h */,
+ 263A27E91EE40C4B005C52B9 /* jenkins-build.sh */,
+ 263A27EA1EE40C4B005C52B9 /* libdom.c */,
+ 263A27EB1EE40C4B005C52B9 /* libdom.h */,
+ 263A27EC1EE40C4B005C52B9 /* log.c */,
+ 263A27ED1EE40C4B005C52B9 /* log.h */,
+ 263A27EE1EE40C4B005C52B9 /* Makefile */,
+ 263A27EF1EE40C4B005C52B9 /* memanalyze.pl */,
+ 263A27F01EE40C4B005C52B9 /* merge-messages.lua */,
+ 263A27F11EE40C4B005C52B9 /* messages.c */,
+ 263A27F21EE40C4B005C52B9 /* messages.h */,
+ 263A27F31EE40C4B005C52B9 /* nsoption.c */,
+ 263A27F41EE40C4B005C52B9 /* nsoption.h */,
+ 263A27F51EE40C4B005C52B9 /* nsurl */,
+ 263A27FA1EE40C4B005C52B9 /* nsurl.h */,
+ 263A27FB1EE40C4B005C52B9 /* punycode.c */,
+ 263A27FC1EE40C4B005C52B9 /* punycode.h */,
+ 263A27FD1EE40C4B005C52B9 /* ring.h */,
+ 263A27FE1EE40C4B005C52B9 /* split-messages.pl */,
+ 263A27FF1EE40C4B005C52B9 /* string.h */,
+ 263A28001EE40C4B005C52B9 /* sys_time.h */,
+ 263A28011EE40C4B005C52B9 /* talloc.c */,
+ 263A28021EE40C4B005C52B9 /* talloc.h */,
+ 263A28031EE40C4B005C52B9 /* test-netsurf */,
+ 263A28041EE40C4B005C52B9 /* time.c */,
+ 263A28051EE40C4B005C52B9 /* time.h */,
+ 263A28061EE40C4B005C52B9 /* url.c */,
+ 263A28071EE40C4B005C52B9 /* url.h */,
+ 263A28081EE40C4B005C52B9 /* useragent.c */,
+ 263A28091EE40C4B005C52B9 /* useragent.h */,
+ 263A280A1EE40C4B005C52B9 /* utf8.c */,
+ 263A280B1EE40C4B005C52B9 /* utf8.h */,
+ 263A280C1EE40C4B005C52B9 /* utils.c */,
+ 263A280D1EE40C4B005C52B9 /* utils.h */,
+ 263A280E1EE40C4B005C52B9 /* utsname.h */,
+ 263A280F1EE40C4B005C52B9 /* valgrind.supp */,
+ );
+ path = utils;
+ sourceTree = "<group>";
+ };
+ 263A27CF1EE40C4B005C52B9 /* http */ = {
+ isa = PBXGroup;
+ children = (
+ 263A27D01EE40C4B005C52B9 /* challenge.c */,
+ 263A27D11EE40C4B005C52B9 /* challenge.h */,
+ 263A27D21EE40C4B005C52B9 /* challenge_internal.h */,
+ 263A27D31EE40C4B005C52B9 /* content-disposition.c */,
+ 263A27D41EE40C4B005C52B9 /* content-disposition.h */,
+ 263A27D51EE40C4B005C52B9 /* content-type.c */,
+ 263A27D61EE40C4B005C52B9 /* content-type.h */,
+ 263A27D71EE40C4B005C52B9 /* generics.c */,
+ 263A27D81EE40C4B005C52B9 /* generics.h */,
+ 263A27D91EE40C4B005C52B9 /* Makefile */,
+ 263A27DA1EE40C4B005C52B9 /* parameter.c */,
+ 263A27DB1EE40C4B005C52B9 /* parameter.h */,
+ 263A27DC1EE40C4B005C52B9 /* parameter_internal.h */,
+ 263A27DD1EE40C4B005C52B9 /* primitives.c */,
+ 263A27DE1EE40C4B005C52B9 /* primitives.h */,
+ 263A27DF1EE40C4B005C52B9 /* www-authenticate.c */,
+ 263A27E01EE40C4B005C52B9 /* www-authenticate.h */,
+ );
+ path = http;
+ sourceTree = "<group>";
+ };
+ 263A27F51EE40C4B005C52B9 /* nsurl */ = {
+ isa = PBXGroup;
+ children = (
+ 263A27F61EE40C4B005C52B9 /* Makefile */,
+ 263A27F71EE40C4B005C52B9 /* nsurl.c */,
+ 263A27F81EE40C4B005C52B9 /* parse.c */,
+ 263A27F91EE40C4B005C52B9 /* private.h */,
+ );
+ path = nsurl;
+ sourceTree = "<group>";
+ };
+ 263A28AA1EE41245005C52B9 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 263A29061EE4196E005C52B9 /* Cocoa.framework */,
+ 263A28AD1EE41266005C52B9 /* libz.tbd */,
+ 263A28AB1EE41245005C52B9 /* libiconv.tbd */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 263A28AF1EE412B4005C52B9 /* javascript */ = {
+ isa = PBXGroup;
+ children = (
+ 263A28B01EE412B4005C52B9 /* content.c */,
+ 263A28B11EE412B4005C52B9 /* content.h */,
+ 263A28B21EE412B4005C52B9 /* duktape */,
+ 263A28F31EE412B4005C52B9 /* fetcher.c */,
+ 263A28F41EE412B4005C52B9 /* fetcher.h */,
+ 263A28F51EE412B4005C52B9 /* js.h */,
+ 263A28F61EE412B4005C52B9 /* Makefile */,
+ 263A28F71EE412B4005C52B9 /* none */,
+ 263A28FA1EE412B4005C52B9 /* WebIDL */,
+ );
+ path = javascript;
+ sourceTree = "<group>";
+ };
+ 263A28B21EE412B4005C52B9 /* duktape */ = {
+ isa = PBXGroup;
+ children = (
+ 263A28B31EE412B4005C52B9 /* Console.bnd */,
+ 263A28B41EE412B4005C52B9 /* Document.bnd */,
+ 263A28B51EE412B4005C52B9 /* duk_config.h */,
+ 263A28B61EE412B4005C52B9 /* duk_custom.h */,
+ 263A28B71EE412B4005C52B9 /* dukky.c */,
+ 263A28B81EE412B4005C52B9 /* dukky.h */,
+ 263A28B91EE412B4005C52B9 /* duktape.c */,
+ 263A28BA1EE412B4005C52B9 /* duktape.h */,
+ 263A28BB1EE412B4005C52B9 /* Element.bnd */,
+ 263A28BC1EE412B4005C52B9 /* Event.bnd */,
+ 263A28BD1EE412B4005C52B9 /* EventTarget.bnd */,
+ 263A28BE1EE412B4005C52B9 /* HTMLAnchorElement.bnd */,
+ 263A28BF1EE412B4005C52B9 /* HTMLAppletElement.bnd */,
+ 263A28C01EE412B4005C52B9 /* HTMLAreaElement.bnd */,
+ 263A28C11EE412B4005C52B9 /* HTMLBaseElement.bnd */,
+ 263A28C21EE412B4005C52B9 /* HTMLBodyElement.bnd */,
+ 263A28C31EE412B4005C52B9 /* HTMLBRElement.bnd */,
+ 263A28C41EE412B4005C52B9 /* HTMLButtonElement.bnd */,
+ 263A28C51EE412B4005C52B9 /* HTMLCollection.bnd */,
+ 263A28C61EE412B4005C52B9 /* HTMLDivElement.bnd */,
+ 263A28C71EE412B4005C52B9 /* HTMLElement.bnd */,
+ 263A28C81EE412B4005C52B9 /* HTMLFontElement.bnd */,
+ 263A28C91EE412B4005C52B9 /* HTMLFormElement.bnd */,
+ 263A28CA1EE412B4005C52B9 /* HTMLFrameElement.bnd */,
+ 263A28CB1EE412B4005C52B9 /* HTMLFrameSetElement.bnd */,
+ 263A28CC1EE412B4005C52B9 /* HTMLHeadingElement.bnd */,
+ 263A28CD1EE412B4005C52B9 /* HTMLHRElement.bnd */,
+ 263A28CE1EE412B4005C52B9 /* HTMLHTMLElement.bnd */,
+ 263A28CF1EE412B4005C52B9 /* HTMLIFrameElement.bnd */,
+ 263A28D01EE412B4005C52B9 /* HTMLImageElement.bnd */,
+ 263A28D11EE412B4005C52B9 /* HTMLInputElement.bnd */,
+ 263A28D21EE412B4005C52B9 /* HTMLLabelElement.bnd */,
+ 263A28D31EE412B4005C52B9 /* HTMLLegendElement.bnd */,
+ 263A28D41EE412B4005C52B9 /* HTMLLIElement.bnd */,
+ 263A28D51EE412B4005C52B9 /* HTMLLinkElement.bnd */,
+ 263A28D61EE412B4005C52B9 /* HTMLMapElement.bnd */,
+ 263A28D71EE412B4005C52B9 /* HTMLMarqueeElement.bnd */,
+ 263A28D81EE412B4005C52B9 /* HTMLMenuElement.bnd */,
+ 263A28D91EE412B4005C52B9 /* HTMLMetaElement.bnd */,
+ 263A28DA1EE412B4005C52B9 /* HTMLObjectElement.bnd */,
+ 263A28DB1EE412B4005C52B9 /* HTMLOListElement.bnd */,
+ 263A28DC1EE412B4005C52B9 /* HTMLOptionElement.bnd */,
+ 263A28DD1EE412B4005C52B9 /* HTMLParagraphElement.bnd */,
+ 263A28DE1EE412B4005C52B9 /* HTMLParamElement.bnd */,
+ 263A28DF1EE412B4005C52B9 /* HTMLPreElement.bnd */,
+ 263A28E01EE412B4005C52B9 /* HTMLQuoteElement.bnd */,
+ 263A28E11EE412B4005C52B9 /* HTMLScriptElement.bnd */,
+ 263A28E21EE412B4005C52B9 /* HTMLSelectElement.bnd */,
+ 263A28E31EE412B4005C52B9 /* HTMLStyleElement.bnd */,
+ 263A28E41EE412B4005C52B9 /* HTMLTableCaptionElement.bnd */,
+ 263A28E51EE412B4005C52B9 /* HTMLTableCellElement.bnd */,
+ 263A28E61EE412B4005C52B9 /* HTMLTableColElement.bnd */,
+ 263A28E71EE412B4005C52B9 /* HTMLTableElement.bnd */,
+ 263A28E81EE412B4005C52B9 /* HTMLTableRowElement.bnd */,
+ 263A28E91EE412B4005C52B9 /* HTMLTableSectionElement.bnd */,
+ 263A28EA1EE412B4005C52B9 /* HTMLTextAreaElement.bnd */,
+ 263A28EB1EE412B4005C52B9 /* HTMLTitleElement.bnd */,
+ 263A28EC1EE412B4005C52B9 /* Location.bnd */,
+ 263A28ED1EE412B4005C52B9 /* Makefile */,
+ 263A28EE1EE412B4005C52B9 /* Navigator.bnd */,
+ 263A28EF1EE412B4005C52B9 /* netsurf.bnd */,
+ 263A28F01EE412B4005C52B9 /* Node.bnd */,
+ 263A28F11EE412B4005C52B9 /* NodeList.bnd */,
+ 263A28F21EE412B4005C52B9 /* Window.bnd */,
+ );
+ path = duktape;
+ sourceTree = "<group>";
+ };
+ 263A28F71EE412B4005C52B9 /* none */ = {
+ isa = PBXGroup;
+ children = (
+ 263A28F81EE412B4005C52B9 /* Makefile */,
+ 263A28F91EE412B4005C52B9 /* none.c */,
+ );
+ path = none;
+ sourceTree = "<group>";
+ };
+ 263A28FA1EE412B4005C52B9 /* WebIDL */ = {
+ isa = PBXGroup;
+ children = (
+ 263A28FB1EE412B4005C52B9 /* console.idl */,
+ 263A28FC1EE412B4005C52B9 /* cssom.idl */,
+ 263A28FD1EE412B4005C52B9 /* dom-parsing.idl */,
+ 263A28FE1EE412B4005C52B9 /* dom.idl */,
+ 263A28FF1EE412B4005C52B9 /* html.idl */,
+ 263A29001EE412B4005C52B9 /* Makefile */,
+ 263A29011EE412B4005C52B9 /* uievents.idl */,
+ 263A29021EE412B4005C52B9 /* urlutils.idl */,
+ );
+ path = WebIDL;
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* Untitled */ = {
+ isa = PBXGroup;
+ children = (
+ 263A26A01EE40C1E005C52B9 /* Engine */,
+ 261DB24E1318443500C59F12 /* Tools */,
+ 263A25EF1EE40BD6005C52B9 /* Cocoa */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ 263A28AA1EE41245005C52B9 /* Frameworks */,
+ );
+ name = Untitled;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXLegacyTarget section */
+ 2636298F12F698E00048542C /* NetSurf */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "PREFIX=../build TARGET=cocoa SDKROOT= VARIANT=debug $(ACTION)";
+ buildConfigurationList = 2636299512F699250048542C /* Build configuration list for PBXLegacyTarget "NetSurf" */;
+ buildPhases = (
+ );
+ buildToolPath = make;
+ buildWorkingDirectory = "$(SRCROOT)/../..";
+ dependencies = (
+ );
+ name = NetSurf;
+ passBuildSettingsInEnvironment = 1;
+ productName = NetSurf;
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXNativeTarget section */
+ 263A25ED1EE40BD6005C52B9 /* NetSurfBrowser */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 263A25FF1EE40BD6005C52B9 /* Build configuration list for PBXNativeTarget "NetSurfBrowser" */;
+ buildPhases = (
+ 26890F931EE88CE100063C30 /* Build Testament Header */,
+ 263A25EA1EE40BD6005C52B9 /* Sources */,
+ 263A25EB1EE40BD6005C52B9 /* Frameworks */,
+ 263A25EC1EE40BD6005C52B9 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = NetSurfBrowser;
+ productName = NetSurfBrowser;
+ productReference = 263A25EE1EE40BD6005C52B9 /* NetSurfBrowser.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ LastUpgradeCheck = 0900;
+ TargetAttributes = {
+ 263A25ED1EE40BD6005C52B9 = {
+ CreatedOnToolsVersion = 8.3.2;
+ ProvisioningStyle = Automatic;
+ };
+ };
+ };
+ buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NetSurf" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ de,
+ en,
+ fr,
+ it,
+ nl,
+ Base,
+ );
+ mainGroup = 29B97314FDCFA39411CA2CEA /* Untitled */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 2636298F12F698E00048542C /* NetSurf */,
+ 263A25ED1EE40BD6005C52B9 /* NetSurfBrowser */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 263A25EC1EE40BD6005C52B9 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 263A292D1EE41B53005C52B9 /* AquaTabCloseDirty_Front_Pressed.png in Resources */,
+ 263A29281EE41AA8005C52B9 /* SearchWindow.xib in Resources */,
+ 263A29271EE41AA8005C52B9 /* quirks.css in Resources */,
+ 263A292C1EE41B53005C52B9 /* AquaTabCloseDirty_Front.png in Resources */,
+ 263A29131EE41A96005C52B9 /* Messages in Resources */,
+ 263A290A1EE41A7D005C52B9 /* Browser.xib in Resources */,
+ 263A29211EE41AA4005C52B9 /* internal.css in Resources */,
+ 263A292E1EE41B53005C52B9 /* AquaTabCloseDirty_Front_Rollover.png in Resources */,
+ 263A29241EE41AA8005C52B9 /* NetSurf.icns in Resources */,
+ 263A290D1EE41A8C005C52B9 /* BookmarksWindow.xib.strings in Resources */,
+ 263A29251EE41AA8005C52B9 /* netsurf.png in Resources */,
+ 263A29291EE41B53005C52B9 /* AquaTabClose_Front.png in Resources */,
+ 263A291A1EE41A9F005C52B9 /* arrow-l.png in Resources */,
+ 263A29171EE41A9A005C52B9 /* DownloadWindow.xib in Resources */,
+ 263A29201EE41A9F005C52B9 /* search.png in Resources */,
+ 263A290C1EE41A7D005C52B9 /* ca-bundle in Resources */,
+ 263A29101EE41A8C005C52B9 /* HistoryWindow.xib.strings in Resources */,
+ 263A29301EE41B53005C52B9 /* AquaTabNewPressed.png in Resources */,
+ 263A290F1EE41A8C005C52B9 /* DownloadWindow.xib.strings in Resources */,
+ 263A291E1EE41A9F005C52B9 /* hotlist-add.png in Resources */,
+ 263A29341EE41B53005C52B9 /* pi.png in Resources */,
+ 263A29261EE41AA8005C52B9 /* PreferencesWindow.xib in Resources */,
+ 263A291D1EE41A9F005C52B9 /* directory2.png in Resources */,
+ 263A29121EE41A8F005C52B9 /* MainMenu.xib.strings in Resources */,
+ 263A29111EE41A8C005C52B9 /* Localizable.strings in Resources */,
+ 263A29081EE41A75005C52B9 /* adblock.css in Resources */,
+ 263A290B1EE41A7D005C52B9 /* BrowserWindow.xib in Resources */,
+ 263A29141EE41A96005C52B9 /* PreferencesWindow.xib.strings in Resources */,
+ 263A292F1EE41B53005C52B9 /* AquaTabNew.png in Resources */,
+ 263A291B1EE41A9F005C52B9 /* content.png in Resources */,
+ 263A29321EE41B53005C52B9 /* overflowImage.png in Resources */,
+ 263A29151EE41A96005C52B9 /* SearchWindow.xib.strings in Resources */,
+ 263A29231EE41AA4005C52B9 /* MainMenu.xib in Resources */,
+ 263A292A1EE41B53005C52B9 /* AquaTabClose_Front_Pressed.png in Resources */,
+ 263A29181EE41A9A005C52B9 /* HistoryWindow.xib in Resources */,
+ 263A29091EE41A7D005C52B9 /* BookmarksWindow.xib in Resources */,
+ 263A29221EE41AA4005C52B9 /* LocalHistoryPanel.xib in Resources */,
+ 263A29331EE41B53005C52B9 /* overflowImagePressed.png in Resources */,
+ 263A291F1EE41A9F005C52B9 /* hotlist-rmv.png in Resources */,
+ 263A292B1EE41B53005C52B9 /* AquaTabClose_Front_Rollover.png in Resources */,
+ 263A29311EE41B53005C52B9 /* AquaTabNewRollover.png in Resources */,
+ 263A290E1EE41A8C005C52B9 /* BrowserWindow.xib.strings in Resources */,
+ 263A291C1EE41A9F005C52B9 /* directory.png in Resources */,
+ 263A29161EE41A9A005C52B9 /* default.css in Resources */,
+ 263A29191EE41A9A005C52B9 /* HomeTemplate.pdf in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 26890F931EE88CE100063C30 /* Build Testament Header */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Build Testament Header";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/testament.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "ROOT=\"${SRCROOT}/../..\"\nexec perl -w \"${ROOT}/utils/git-testament.pl\" \"${ROOT}\" \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 263A25EA1EE40BD6005C52B9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 263A289C1EE4121D005C52B9 /* html.c in Sources */,
+ 263A287D1EE4120F005C52B9 /* log.c in Sources */,
+ 263A28471EE40CF2005C52B9 /* internal.c in Sources */,
+ 263A28671EE40CFB005C52B9 /* hotlist.c in Sources */,
+ 263A28291EE40CCF005C52B9 /* font.m in Sources */,
+ 263A286F1EE40CFB005C52B9 /* save_pdf.c in Sources */,
+ 263A281F1EE40CCF005C52B9 /* bitmap.m in Sources */,
+ 263A28381EE40CCF005C52B9 /* TreeView.m in Sources */,
+ 263A289E1EE4121D005C52B9 /* html_css_fetcher.c in Sources */,
+ 263A282E1EE40CCF005C52B9 /* LocalHistoryController.m in Sources */,
+ 263A28941EE41218005C52B9 /* filepath.c in Sources */,
+ 263A28731EE40CFB005C52B9 /* searchweb.c in Sources */,
+ 263A285C1EE40CF2005C52B9 /* mimesniff.c in Sources */,
+ 263A288B1EE41213005C52B9 /* content-type.c in Sources */,
+ 263A285B1EE40CF2005C52B9 /* llcache.c in Sources */,
+ 263A28871EE4120F005C52B9 /* utf8.c in Sources */,
+ 263A28851EE4120F005C52B9 /* url.c in Sources */,
+ 263A282A1EE40CCF005C52B9 /* FormSelectMenu.m in Sources */,
+ 263A28441EE40CF2005C52B9 /* css.c in Sources */,
+ 263A28711EE40CFB005C52B9 /* scrollbar.c in Sources */,
+ 263A289F1EE4121D005C52B9 /* html_forms.c in Sources */,
+ 263A29041EE412BE005C52B9 /* fetcher.c in Sources */,
+ 263A28A01EE4121D005C52B9 /* html_interaction.c in Sources */,
+ 263A288D1EE41213005C52B9 /* parameter.c in Sources */,
+ 263A28191EE40CCF005C52B9 /* PSMTabDragWindow.m in Sources */,
+ 263A28431EE40CF2005C52B9 /* fs_backing_store.c in Sources */,
+ 263A28841EE4120F005C52B9 /* time.c in Sources */,
+ 263A285A1EE40CF2005C52B9 /* hlcache.c in Sources */,
+ 263A28611EE40CFB005C52B9 /* cookie_manager.c in Sources */,
+ 263A281A1EE40CCF005C52B9 /* PSMTabDragWindowController.m in Sources */,
+ 263A284C1EE40CF2005C52B9 /* ico.c in Sources */,
+ 263A288F1EE41213005C52B9 /* www-authenticate.c in Sources */,
+ 263A28161EE40CCF005C52B9 /* PSMTabBarController.m in Sources */,
+ 263A28891EE41213005C52B9 /* challenge.c in Sources */,
+ 263A287C1EE4120F005C52B9 /* libdom.c in Sources */,
+ 263A29051EE412BE005C52B9 /* none.c in Sources */,
+ 263A28261EE40CCF005C52B9 /* desktop-tree.m in Sources */,
+ 263A281B1EE40CCF005C52B9 /* PSMUnifiedTabStyle.m in Sources */,
+ 263A285D1EE40CF2005C52B9 /* no_backing_store.c in Sources */,
+ 263A286B1EE40CFB005C52B9 /* netsurf.c in Sources */,
+ 263A284E1EE40CF2005C52B9 /* image_cache.c in Sources */,
+ 263A286D1EE40CFB005C52B9 /* print.c in Sources */,
+ 263A28391EE40CCF005C52B9 /* URLFieldCell.m in Sources */,
+ 263A28121EE40CCF005C52B9 /* PSMProgressIndicator.m in Sources */,
+ 263A28251EE40CCF005C52B9 /* BrowserWindowController.m in Sources */,
+ 263A288A1EE41213005C52B9 /* content-disposition.c in Sources */,
+ 263A29351EE41B5E005C52B9 /* NSBezierPath_AMShading.m in Sources */,
+ 263A28461EE40CF2005C52B9 /* hints.c in Sources */,
+ 263A28631EE40CFB005C52B9 /* font_haru.c in Sources */,
+ 263A283F1EE40CF2005C52B9 /* curl.c in Sources */,
+ 263A28481EE40CF2005C52B9 /* select.c in Sources */,
+ 263A287E1EE4120F005C52B9 /* messages.c in Sources */,
+ 263A28801EE4120F005C52B9 /* nsurl.c in Sources */,
+ 263A28361EE40CCF005C52B9 /* selection.m in Sources */,
+ 263A28A41EE4121D005C52B9 /* html_script.c in Sources */,
+ 263A28A31EE4121D005C52B9 /* html_redraw_border.c in Sources */,
+ 263A28241EE40CCF005C52B9 /* BrowserWindow.m in Sources */,
+ 263A289B1EE4121D005C52B9 /* form.c in Sources */,
+ 263A285E1EE40CF2005C52B9 /* urldb.c in Sources */,
+ 263A28221EE40CCF005C52B9 /* BrowserView.m in Sources */,
+ 263A282D1EE40CCF005C52B9 /* HistoryWindowController.m in Sources */,
+ 263A28101EE40CCF005C52B9 /* NSString_AITruncation.m in Sources */,
+ 263A28761EE40CFB005C52B9 /* system_colour.c in Sources */,
+ 263A28931EE41218005C52B9 /* filename.c in Sources */,
+ 263A287F1EE4120F005C52B9 /* nsoption.c in Sources */,
+ 263A28791EE40CFB005C52B9 /* treeview.c in Sources */,
+ 263A28A71EE4121D005C52B9 /* search.c in Sources */,
+ 263A28811EE4120F005C52B9 /* parse.c in Sources */,
+ 263A28911EE41218005C52B9 /* corestrings.c in Sources */,
+ 263A282F1EE40CCF005C52B9 /* NetsurfApp.m in Sources */,
+ 263A28151EE40CCF005C52B9 /* PSMTabBarControl.m in Sources */,
+ 263A28451EE40CF2005C52B9 /* dump.c in Sources */,
+ 263A28321EE40CCF005C52B9 /* PreferencesWindowController.m in Sources */,
+ 263A28341EE40CCF005C52B9 /* ScrollableView.m in Sources */,
+ 263A281D1EE40CCF005C52B9 /* ArrowBox.m in Sources */,
+ 263A28831EE4120F005C52B9 /* talloc.c in Sources */,
+ 263A28771EE40CFB005C52B9 /* textarea.c in Sources */,
+ 263A28A81EE4121D005C52B9 /* table.c in Sources */,
+ 263A286E1EE40CFB005C52B9 /* save_complete.c in Sources */,
+ 263A287B1EE4120F005C52B9 /* idna.c in Sources */,
+ 263A284D1EE40CF2005C52B9 /* image.c in Sources */,
+ 263A28621EE40CFB005C52B9 /* download.c in Sources */,
+ 263A28681EE40CFB005C52B9 /* knockout.c in Sources */,
+ 263A28861EE4120F005C52B9 /* useragent.c in Sources */,
+ 263A288C1EE41213005C52B9 /* generics.c in Sources */,
+ 263A28211EE40CCF005C52B9 /* BookmarksController.m in Sources */,
+ 263A28881EE4120F005C52B9 /* utils.c in Sources */,
+ 263A28641EE40CFB005C52B9 /* frames.c in Sources */,
+ 263A28901EE41218005C52B9 /* bloom.c in Sources */,
+ 263A28951EE41218005C52B9 /* hashtable.c in Sources */,
+ 263A289A1EE4121D005C52B9 /* font.c in Sources */,
+ 263A283B1EE40CF2005C52B9 /* content_factory.c in Sources */,
+ 263A283C1EE40CF2005C52B9 /* dirlist.c in Sources */,
+ 263A282C1EE40CCF005C52B9 /* HistoryView.m in Sources */,
+ 263A28A21EE4121D005C52B9 /* html_redraw.c in Sources */,
+ 263A28751EE40CFB005C52B9 /* sslcert_viewer.c in Sources */,
+ 263A285F1EE40CFB005C52B9 /* browser.c in Sources */,
+ 263A28961EE4121D005C52B9 /* box.c in Sources */,
+ 263A28821EE4120F005C52B9 /* punycode.c in Sources */,
+ 263A28111EE40CCF005C52B9 /* PSMOverflowPopUpButton.m in Sources */,
+ 263A28A91EE4121D005C52B9 /* textplain.c in Sources */,
+ 263A28781EE40CFB005C52B9 /* textinput.c in Sources */,
+ 263A28601EE40CFB005C52B9 /* browser_history.c in Sources */,
+ 263A28721EE40CFB005C52B9 /* search.c in Sources */,
+ 263A28281EE40CCF005C52B9 /* fetch.m in Sources */,
+ 263A28691EE40CFB005C52B9 /* local_history.c in Sources */,
+ 263A284B1EE40CF2005C52B9 /* gif.c in Sources */,
+ 263A28371EE40CCF005C52B9 /* Tree.m in Sources */,
+ 263A28651EE40CFB005C52B9 /* global_history.c in Sources */,
+ 263A283D1EE40CF2005C52B9 /* fetch.c in Sources */,
+ 263A281E1EE40CCF005C52B9 /* ArrowWindow.m in Sources */,
+ 263A289D1EE4121D005C52B9 /* html_css.c in Sources */,
+ 263A28661EE40CFB005C52B9 /* gui_factory.c in Sources */,
+ 263A283E1EE40CF2005C52B9 /* about.c in Sources */,
+ 263A281C1EE40CCF005C52B9 /* apple_image.m in Sources */,
+ 263A28921EE41218005C52B9 /* file.c in Sources */,
+ 263A28491EE40CF2005C52B9 /* utils.c in Sources */,
+ 263A286A1EE40CFB005C52B9 /* mouse.c in Sources */,
+ 263A28421EE40CF2005C52B9 /* resource.c in Sources */,
+ 263A28311EE40CCF005C52B9 /* plotter.m in Sources */,
+ 263A29031EE412BE005C52B9 /* content.c in Sources */,
+ 263A28271EE40CCF005C52B9 /* DownloadWindowController.m in Sources */,
+ 263A282B1EE40CCF005C52B9 /* gui.m in Sources */,
+ 263A283A1EE40CF2005C52B9 /* content.c in Sources */,
+ 263A28131EE40CCF005C52B9 /* PSMRolloverButton.m in Sources */,
+ 263A28991EE4121D005C52B9 /* box_textarea.c in Sources */,
+ 263A28141EE40CCF005C52B9 /* PSMTabBarCell.m in Sources */,
+ 263A284A1EE40CF2005C52B9 /* bmp.c in Sources */,
+ 263A28331EE40CCF005C52B9 /* schedule.m in Sources */,
+ 263A28181EE40CCF005C52B9 /* PSMTabDragView.m in Sources */,
+ 263A28401EE40CF2005C52B9 /* data.c in Sources */,
+ 263A288E1EE41213005C52B9 /* primitives.c in Sources */,
+ 263A28A11EE4121D005C52B9 /* html_object.c in Sources */,
+ 263A28351EE40CCF005C52B9 /* SearchWindowController.m in Sources */,
+ 263A286C1EE40CFB005C52B9 /* plot_style.c in Sources */,
+ 263A28411EE40CF2005C52B9 /* file.c in Sources */,
+ 263A28231EE40CCF005C52B9 /* BrowserViewController.m in Sources */,
+ 263A28981EE4121D005C52B9 /* box_normalise.c in Sources */,
+ 263A28701EE40CFB005C52B9 /* save_text.c in Sources */,
+ 263A28A51EE4121D005C52B9 /* imagemap.c in Sources */,
+ 263A28971EE4121D005C52B9 /* box_construct.c in Sources */,
+ 263A28301EE40CCF005C52B9 /* NetSurfAppDelegate.m in Sources */,
+ 263A28A61EE4121D005C52B9 /* layout.c in Sources */,
+ 263A28171EE40CCF005C52B9 /* PSMTabDragAssistant.m in Sources */,
+ 263A287A1EE40CFB005C52B9 /* version.c in Sources */,
+ 263A28741EE40CFB005C52B9 /* selection.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 263A26321EE40BFC005C52B9 /* BookmarksWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A26331EE40BFC005C52B9 /* de */,
+ );
+ name = BookmarksWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 263A26341EE40BFC005C52B9 /* BrowserWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A26351EE40BFC005C52B9 /* de */,
+ );
+ name = BrowserWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 263A26361EE40BFC005C52B9 /* DownloadWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A26371EE40BFC005C52B9 /* de */,
+ );
+ name = DownloadWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 263A26381EE40BFC005C52B9 /* HistoryWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A26391EE40BFC005C52B9 /* de */,
+ );
+ name = HistoryWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 263A263A1EE40BFC005C52B9 /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A263B1EE40BFC005C52B9 /* de */,
+ 263A26461EE40BFC005C52B9 /* en */,
+ 263A26481EE40BFC005C52B9 /* fr */,
+ 263A26551EE40BFC005C52B9 /* it */,
+ 263A265C1EE40BFC005C52B9 /* nl */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
+ 263A263C1EE40BFC005C52B9 /* MainMenu.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A263D1EE40BFC005C52B9 /* de */,
+ );
+ name = MainMenu.xib.strings;
+ sourceTree = "<group>";
+ };
+ 263A263E1EE40BFC005C52B9 /* Messages */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A263F1EE40BFC005C52B9 /* de */,
+ 263A26471EE40BFC005C52B9 /* en */,
+ 263A26491EE40BFC005C52B9 /* fr */,
+ 263A26561EE40BFC005C52B9 /* it */,
+ 263A265D1EE40BFC005C52B9 /* nl */,
+ );
+ name = Messages;
+ sourceTree = "<group>";
+ };
+ 263A26401EE40BFC005C52B9 /* PreferencesWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A26411EE40BFC005C52B9 /* de */,
+ );
+ name = PreferencesWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+ 263A26421EE40BFC005C52B9 /* SearchWindow.xib.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 263A26431EE40BFC005C52B9 /* de */,
+ );
+ name = SearchWindow.xib.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 2636299012F698E10048542C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ MKDIR = "mkdir -p";
+ PKG_CONFIG_PATH = /Users/sven/Projekte/NetSurf/build/lib/pkgconfig;
+ PRODUCT_NAME = NetSurf;
+ };
+ name = Debug;
+ };
+ 2636299112F698E10048542C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ MKDIR = "mkdir -p";
+ PKG_CONFIG_PATH = /Users/sven/Projekte/NetSurf/build/lib/pkgconfig;
+ PRODUCT_NAME = NetSurf;
+ };
+ name = Release;
+ };
+ 263A26001EE40BD6005C52B9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ "'NETSURF_HOMEPAGE=\"about:welcome\"'",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(SRCROOT)/..",
+ "$(NETSURF_ROOT)",
+ "$(NETSURF_LIB_PREFIX)/include",
+ "$(NETSURF_ROOT)/include",
+ "$(OPENSSL_PREFIX)/include",
+ "$(NETSURF_ROOT)/content/handlers",
+ );
+ INFOPLIST_FILE = "$(SRCROOT)/res/NetSurf-Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(OPENSSL_PREFIX)/lib",
+ "$(NETSURF_LIB_PREFIX)/lib",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ OTHER_CFLAGS = (
+ "-Dnscocoa",
+ "-DWITH_CURL",
+ );
+ OTHER_LDFLAGS = (
+ "-lcss",
+ "-ldom",
+ "-lhubbub",
+ "-lnsbmp",
+ "-lnsgif",
+ "-lnslayout",
+ "-lnspsl",
+ "-lnsutils",
+ "-lparserutils",
+ "-lsvgtiny",
+ "-lutf8proc",
+ "-lwapcaplet",
+ "-lssl",
+ "-lcurl",
+ "-lcrypto",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.netsurf-browser.NetSurfBrowser";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ 263A26011EE40BD6005C52B9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_NO_COMMON_BLOCKS = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "'NETSURF_HOMEPAGE=\"about:welcome\"'",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(SRCROOT)/..",
+ "$(NETSURF_ROOT)",
+ "$(NETSURF_LIB_PREFIX)/include",
+ "$(NETSURF_ROOT)/include",
+ "$(OPENSSL_PREFIX)/include",
+ "$(NETSURF_ROOT)/content/handlers",
+ );
+ INFOPLIST_FILE = "$(SRCROOT)/res/NetSurf-Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(OPENSSL_PREFIX)/lib",
+ "$(NETSURF_LIB_PREFIX)/lib",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_CFLAGS = (
+ "-Dnscocoa",
+ "-DWITH_CURL",
+ );
+ OTHER_LDFLAGS = (
+ "-lcss",
+ "-ldom",
+ "-lhubbub",
+ "-lnsbmp",
+ "-lnsgif",
+ "-lnslayout",
+ "-lnspsl",
+ "-lnsutils",
+ "-lparserutils",
+ "-lsvgtiny",
+ "-lutf8proc",
+ "-lwapcaplet",
+ "-lssl",
+ "-lcurl",
+ "-lcrypto",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.netsurf-browser.NetSurfBrowser";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ C01FCF4F08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ NETSURF_LIB_PREFIX = "$(NETSURF_ROOT)/../build";
+ NETSURF_ROOT = "$(SRCROOT)/../..";
+ ONLY_ACTIVE_ARCH = YES;
+ OPENSSL_PREFIX = /usr/local/opt/openssl;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ C01FCF5008A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ NETSURF_LIB_PREFIX = "$(NETSURF_ROOT)/../build";
+ NETSURF_ROOT = "$(SRCROOT)/../..";
+ ONLY_ACTIVE_ARCH = NO;
+ OPENSSL_PREFIX = /usr/local/opt/openssl;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 2636299512F699250048542C /* Build configuration list for PBXLegacyTarget "NetSurf" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 2636299012F698E10048542C /* Debug */,
+ 2636299112F698E10048542C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 263A25FF1EE40BD6005C52B9 /* Build configuration list for PBXNativeTarget "NetSurfBrowser" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 263A26001EE40BD6005C52B9 /* Debug */,
+ 263A26011EE40BD6005C52B9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NetSurf" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4F08A954540054247B /* Debug */,
+ C01FCF5008A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/frontends/cocoa/NetSurf.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/frontends/cocoa/NetSurf.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- a/dev/null
+++ b/frontends/cocoa/NetSurf.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
diff --git a/frontends/cocoa/NetSurfAppDelegate.h b/frontends/cocoa/NetSurfAppDelegate.h
new file mode 100644
index 0000000..37d07a4
--- a/dev/null
+++ b/frontends/cocoa/NetSurfAppDelegate.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@class SearchWindowController;
+@class PreferencesWindowController;
+@class HistoryWindowController;
+
+@interface NetSurfAppDelegate : NSObject
+
+@property (nonatomic) SearchWindowController *search;
+@property (nonatomic) PreferencesWindowController *preferences;
+@property (nonatomic) HistoryWindowController *history;
+
+- (IBAction)showSearchWindow:(id)sender;
+- (IBAction)searchForward:(id)sender;
+- (IBAction)searchBackward:(id)sender;
+
+- (IBAction)showPreferences:(id)sender;
+- (IBAction)showGlobalHistory:(id)sender;
+
+@end
diff --git a/frontends/cocoa/NetSurfAppDelegate.m b/frontends/cocoa/NetSurfAppDelegate.m
new file mode 100644
index 0000000..013b77a
--- a/dev/null
+++ b/frontends/cocoa/NetSurfAppDelegate.m
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "utils/nsoption.h"
+#import "utils/messages.h"
+#import "utils/utils.h"
+#import "utils/nsurl.h"
+#import "netsurf/browser_window.h"
+
+#import "cocoa/gui.h"
+#import "cocoa/NetSurfAppDelegate.h"
+#import "cocoa/SearchWindowController.h"
+#import "cocoa/PreferencesWindowController.h"
+#import "cocoa/HistoryWindowController.h"
+
+@interface NetSurfAppDelegate ()
+
+- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
+
+@end
+
+@implementation NetSurfAppDelegate
+
+- (void)newDocument:(id)sender
+{
+ nsurl *url;
+ nserror error;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ error = nsurl_create(nsoption_charp(homepage_url), &url);
+ } else {
+ error = nsurl_create(NETSURF_HOMEPAGE, &url);
+ }
+
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (void)openDocument:(id)sender
+{
+ nsurl *u;
+ nserror error;
+
+ NSOpenPanel *openPanel = [NSOpenPanel openPanel];
+ [openPanel setAllowsMultipleSelection:YES];
+ if ([openPanel runModalForTypes:nil] == NSModalResponseOK) {
+ for (NSURL *url in [openPanel URLs]) {
+ error = nsurl_create([[url absoluteString] UTF8String], &u);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ u,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(u);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+ }
+ }
+}
+
+- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+{
+ nsurl *url;
+ nserror error;
+ NSString *urlAsString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+
+ error = nsurl_create([urlAsString UTF8String], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+}
+
+- (IBAction)showSearchWindow:(id)sender
+{
+ if (self.search == nil) {
+ self.search = [[SearchWindowController alloc] init];
+ }
+ [self.search.window makeKeyAndOrderFront:self];
+}
+
+- (IBAction)searchForward:(id)sender
+{
+ [self.search search:SearchForward];
+}
+
+- (IBAction)searchBackward:(id)sender
+{
+ [self.search search:SearchBackward];
+}
+
+- (BOOL)validateMenuItem:(id)item
+{
+ SEL action = [item action];
+
+ if (action == @selector(searchForward:)) {
+ return [self.search canGoForward];
+ } else if (action == @selector(searchBackward:)) {
+ return [self.search canGoBack];
+ }
+
+ return YES;
+}
+
+- (IBAction)showPreferences:(id)sender
+{
+ if (self.preferences == nil) {
+ self.preferences = [[PreferencesWindowController alloc] init];
+ }
+ [self.preferences showWindow:sender];
+}
+
+- (IBAction)showGlobalHistory:(id)sender
+{
+ if (self.history == nil) {
+ self.history = [[HistoryWindowController alloc] init];
+ }
+ [self.history showWindow:sender];
+}
+
+// Application delegate methods
+
+- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender
+{
+ [self newDocument:self];
+ return YES;
+}
+
+- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
+{
+ NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
+ [appleEventManager setEventHandler:self
+ andSelector:@selector(handleGetURLEvent:withReplyEvent:)
+ forEventClass:kInternetEventClass
+ andEventID:kAEGetURL];
+}
+
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
+{
+ nsurl *url;
+ nserror error;
+ NSURL *urltxt = [NSURL fileURLWithPath:filename];
+
+ error = nsurl_create([[urltxt absoluteString] UTF8String], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+
+ return YES;
+}
+
+@end
diff --git a/frontends/cocoa/NetsurfApp.h b/frontends/cocoa/NetsurfApp.h
new file mode 100644
index 0000000..03bf454
--- a/dev/null
+++ b/frontends/cocoa/NetsurfApp.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@class BrowserViewController;
+
+@interface NetSurfApp : NSApplication
+
+@property (nonatomic) BrowserViewController *frontTab;
+
+@end
+
+NSString *cocoa_get_user_path(NSString *fileName);
diff --git a/frontends/cocoa/NetsurfApp.m b/frontends/cocoa/NetsurfApp.m
new file mode 100644
index 0000000..4c5442a
--- a/dev/null
+++ b/frontends/cocoa/NetsurfApp.m
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/apple_image.h"
+#import "cocoa/NetsurfApp.h"
+#import "cocoa/gui.h"
+#import "cocoa/plotter.h"
+#import "cocoa/DownloadWindowController.h"
+#import "cocoa/SearchWindowController.h"
+#import "cocoa/selection.h"
+#import "cocoa/fetch.h"
+#import "cocoa/bitmap.h"
+#import "cocoa/font.h"
+
+#import "utils/filename.h"
+#import "utils/log.h"
+#import "utils/messages.h"
+#import "utils/utils.h"
+#import "utils/nsoption.h"
+#import "utils/nsurl.h"
+#import "netsurf/plotters.h"
+#import "netsurf/mouse.h"
+#import "netsurf/netsurf.h"
+#import "netsurf/browser_window.h"
+#import "netsurf/cookie_db.h"
+#import "netsurf/url_db.h"
+#import "desktop/save_complete.h"
+#import "cocoa/desktop-tree.h"
+
+#ifndef NETSURF_HOMEPAGE
+#define NETSURF_HOMEPAGE "http://www.netsurf-browser.org/welcome/"
+#endif
+
+@implementation NetSurfApp
+
+@synthesize frontTab;
+
+static bool cocoa_done = false;
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+static void die(const char *const error)
+{
+ [NSException raise:@"NetsurfDie" format:@"Error: %s", error];
+}
+
+- (void)loadOptions
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ [defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
+ cocoa_get_user_path(@"Cookies"),
+ kCookiesFileOption,
+ cocoa_get_user_path(@"URLs"),
+ kURLsFileOption,
+ [NSString stringWithUTF8String:NETSURF_HOMEPAGE],
+ kHomepageURLOption,
+ nil]];
+
+ nsoption_setnull_charp(cookie_file, strdup([[defaults objectForKey:kCookiesFileOption] UTF8String]));
+
+ nsoption_setnull_charp(cookie_jar, strdup(nsoption_charp(cookie_file)));
+
+ nsoption_setnull_charp(homepage_url, strdup([[defaults objectForKey:kHomepageURLOption] UTF8String]));
+
+ urldb_load([[defaults objectForKey:kURLsFileOption] UTF8String]);
+ urldb_load_cookies(nsoption_charp(cookie_file));
+
+ cocoa_update_scale_factor();
+ LOG("done setup");
+}
+
+- (void)saveOptions
+{
+ urldb_save_cookies(nsoption_charp(cookie_file));
+ urldb_save([[[NSUserDefaults standardUserDefaults] objectForKey:kURLsFileOption] UTF8String]);
+}
+
+- (void)run
+{
+ @autoreleasepool {
+
+ @autoreleasepool {
+
+ [self finishLaunching];
+
+ [self loadOptions];
+ }
+
+ while (!cocoa_done) {
+ @autoreleasepool {
+
+ NSEvent *event =
+ [self nextEventMatchingMask:NSEventMaskAny
+ untilDate:[NSDate distantFuture]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (nil != event) {
+ [self sendEvent:event];
+ [self updateWindows];
+ }
+ }
+ }
+
+ [self saveOptions];
+ }
+}
+
+- (void)terminate:(id)sender
+{
+ [[NSNotificationCenter defaultCenter] postNotificationName:NSApplicationWillTerminateNotification object:self];
+
+ cocoa_done = true;
+ [self postEvent:[NSEvent otherEventWithType:NSEventTypeApplicationDefined
+ location:NSZeroPoint
+ modifierFlags:0
+ timestamp:0
+ windowNumber:0
+ context:NULL
+ subtype:0
+ data1:0
+ data2:0]
+ atStart:YES];
+}
+
+@end
+
+#pragma mark -
+
+static NSString *cocoa_get_preferences_path(void)
+{
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
+ NSCAssert([paths count] >= 1, @"Where is the application support directory?");
+
+ NSString *netsurfPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"NetSurf"];
+
+ NSFileManager *fm = [NSFileManager defaultManager];
+ BOOL isDirectory = NO;
+ BOOL exists = [fm fileExistsAtPath:netsurfPath isDirectory:&isDirectory];
+
+ if (!exists) {
+ exists = [fm createDirectoryAtPath:netsurfPath withIntermediateDirectories:YES attributes:nil error:NULL];
+ isDirectory = YES;
+ }
+ if (!(exists && isDirectory)) {
+ die("Cannot create netsurf preferences directory");
+ }
+
+ return netsurfPath;
+}
+
+NSString *cocoa_get_user_path(NSString *fileName)
+{
+ return [cocoa_get_preferences_path() stringByAppendingPathComponent:fileName];
+}
+
+static const char *cocoa_get_options_file(void)
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ [defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
+ cocoa_get_user_path(@"Options"), kOptionsFileOption,
+ nil]];
+
+ return [[defaults objectForKey:kOptionsFileOption] UTF8String];
+}
+
+static NSApplication *cocoa_prepare_app(void)
+{
+ /* if application instance has already been created return it */
+ if (NSApp != nil) {
+ return NSApp;
+ }
+
+ NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
+
+ /* Obtain principle class of bundle which must implement sharedApplication API */
+ Class principalClass = NSClassFromString([infoDictionary objectForKey:@"NSPrincipalClass"]);
+ NSCAssert([principalClass respondsToSelector:@selector(sharedApplication)],
+ @"Principal class must implement sharedApplication.");
+
+ /* create application instance */
+ [principalClass sharedApplication];
+
+ /* load interface nib */
+ NSString *mainNibName = [infoDictionary objectForKey:@"NSMainNibFile"];
+ NSNib *mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:[NSBundle mainBundle]];
+ [mainNib instantiateNibWithOwner:NSApp topLevelObjects:nil];
+
+ return NSApp;
+}
+
+/**
+ * Set option defaults for cocoa frontend
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ /* Set defaults for absent option strings */
+ const char *const ca_bundle = [[[NSBundle mainBundle] pathForResource:@"ca-bundle" ofType:@""] UTF8String];
+
+ nsoption_setnull_charp(ca_bundle, strdup(ca_bundle));
+
+ return NSERROR_OK;
+}
+
+int main(int argc, char **argv)
+{
+ nsurl *url;
+ nserror error;
+ struct netsurf_table cocoa_table = {
+ .misc = cocoa_misc_table,
+ .window = cocoa_window_table,
+ .clipboard = cocoa_clipboard_table,
+ .download = cocoa_download_table,
+ .fetch = cocoa_fetch_table,
+ .search = cocoa_search_table,
+ .bitmap = cocoa_bitmap_table,
+ .layout = cocoa_layout_table,
+ };
+
+ error = netsurf_register(&cocoa_table);
+ if (error != NSERROR_OK) {
+ die("NetSurf operation table failed registration");
+ }
+
+ const char *const messages = [[[NSBundle mainBundle] pathForResource:@"Messages" ofType:@""] UTF8String];
+ const char *const options = cocoa_get_options_file();
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(NULL, &argc, argv);
+
+ /* user options setup */
+ error = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (error != NSERROR_OK) {
+ die("Options failed to initialise");
+ }
+ nsoption_read(options, NULL);
+ nsoption_commandline(&argc, argv, NULL);
+
+ error = messages_add_from_file(messages);
+
+ /* common initialisation */
+ error = netsurf_init(NULL);
+ if (error != NSERROR_OK) {
+ die("NetSurf failed to initialise");
+ }
+
+ /* Initialise filename allocator */
+ filename_initialise();
+
+ (void)apple_image_init();
+
+ NSApplication *app = cocoa_prepare_app();
+
+ for (int i = 1; i < argc; i++) {
+ /* skip -psn_* and other possible options */
+ if (argv[i][0] == '-')
+ continue;
+
+ error = nsurl_create(argv[i], &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ cocoa_warning(messages_get_errorcode(error), 0);
+ }
+ }
+
+ [app run];
+
+ netsurf_exit();
+
+ return 0;
+}
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front.png
new file mode 100644
index 0000000..77d2205
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Pressed.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Pressed.png
new file mode 100644
index 0000000..197ea95
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Pressed.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Rollover.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Rollover.png
new file mode 100644
index 0000000..2dfe577
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabCloseDirty_Front_Rollover.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front.png
new file mode 100644
index 0000000..02b72d3
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Pressed.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Pressed.png
new file mode 100644
index 0000000..f81125a
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Pressed.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Rollover.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Rollover.png
new file mode 100644
index 0000000..4f6b865
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabClose_Front_Rollover.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabNew.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNew.png
new file mode 100644
index 0000000..10a8370
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNew.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewPressed.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewPressed.png
new file mode 100644
index 0000000..cb4dd10
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewPressed.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewRollover.png b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewRollover.png
new file mode 100644
index 0000000..4d469f8
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/AquaTabNewRollover.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/overflowImage.png b/frontends/cocoa/PSMTabBarControl/Images/overflowImage.png
new file mode 100644
index 0000000..2b76255
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/overflowImage.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/overflowImagePressed.png b/frontends/cocoa/PSMTabBarControl/Images/overflowImagePressed.png
new file mode 100644
index 0000000..b3918b3
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/overflowImagePressed.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/Images/pi.png b/frontends/cocoa/PSMTabBarControl/Images/pi.png
new file mode 100644
index 0000000..4d598dc
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/Images/pi.png
Binary files differ
diff --git a/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.h b/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.h
new file mode 100644
index 0000000..9aa17a9
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.h
@@ -0,0 +1,22 @@
+//
+// NSBezierPath_AMShading.h
+// ------------------------
+//
+// Created by Andreas on 2005-06-01.
+// Copyright 2005 Andreas Mayer. All rights reserved.
+//
+// based on http://www.cocoadev.com/index.pl?GradientFill
+
+#import <Cocoa/Cocoa.h>
+
+@interface NSBezierPath (AMShading)
+
+- (void)customHorizontalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor;
+- (void)customVerticalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor;
+
+- (void)linearGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor;
+- (void)linearVerticalGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor;
+
+- (void)bilinearGradientFillWithOuterColor:(NSColor *)outerColor innerColor:(NSColor *)innerColor;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.m b/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.m
new file mode 100644
index 0000000..dbd1805
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/NSBezierPath_AMShading.m
@@ -0,0 +1,126 @@
+//
+// NSBezierPath_AMShading.m
+// ------------------------
+//
+// Created by Andreas on 2005-06-01.
+// Copyright 2005 Andreas Mayer. All rights reserved.
+//
+
+#import "NSBezierPath_AMShading.h"
+
+@implementation NSBezierPath (AMShading)
+
+static void linearShadedColor(void *info, const CGFloat *in, CGFloat *out)
+{
+ CGFloat *colors = (CGFloat *)info;
+ *out++ = colors[0] + *in * colors[8];
+ *out++ = colors[1] + *in * colors[9];
+ *out++ = colors[2] + *in * colors[10];
+ *out++ = colors[3] + *in * colors[11];
+}
+
+static void bilinearShadedColor(void *info, const CGFloat *in, CGFloat *out)
+{
+ CGFloat *colors = (CGFloat *)info;
+ CGFloat factor = (*in) * 2.0;
+ if (*in > 0.5) {
+ factor = 2 - factor;
+ }
+ *out++ = colors[0] + factor * colors[8];
+ *out++ = colors[1] + factor * colors[9];
+ *out++ = colors[2] + factor * colors[10];
+ *out++ = colors[3] + factor * colors[11];
+}
+
+- (void)linearGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor
+{
+ static const CGFunctionCallbacks callbacks = { 0, &linearShadedColor, NULL };
+
+ [self customHorizontalFillWithCallbacks:callbacks firstColor:startColor secondColor:endColor];
+}
+
+- (void)linearVerticalGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor
+{
+ static const CGFunctionCallbacks callbacks = { 0, &linearShadedColor, NULL };
+
+ [self customVerticalFillWithCallbacks:callbacks firstColor:startColor secondColor:endColor];
+}
+
+- (void)bilinearGradientFillWithOuterColor:(NSColor *)outerColor innerColor:(NSColor *)innerColor
+{
+ static const CGFunctionCallbacks callbacks = { 0, &bilinearShadedColor, NULL };
+
+ [self customHorizontalFillWithCallbacks:callbacks firstColor:innerColor secondColor:outerColor];
+}
+
+- (void)customFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint
+{
+ CGColorSpaceRef colorspace;
+ CGShadingRef shading;
+ CGFunctionRef function;
+ CGFloat colors[12]; // pointer to color values
+
+ // get my context
+ CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+
+ NSColor *deviceDependentFirstColor = [firstColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ NSColor *deviceDependentSecondColor = [secondColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+
+ // set up colors for gradient
+ colors[0] = [deviceDependentFirstColor redComponent];
+ colors[1] = [deviceDependentFirstColor greenComponent];
+ colors[2] = [deviceDependentFirstColor blueComponent];
+ colors[3] = [deviceDependentFirstColor alphaComponent];
+
+ colors[4] = [deviceDependentSecondColor redComponent];
+ colors[5] = [deviceDependentSecondColor greenComponent];
+ colors[6] = [deviceDependentSecondColor blueComponent];
+ colors[7] = [deviceDependentSecondColor alphaComponent];
+
+ // difference between start and end color for each color components
+ colors[8] = (colors[4] - colors[0]);
+ colors[9] = (colors[5] - colors[1]);
+ colors[10] = (colors[6] - colors[2]);
+ colors[11] = (colors[7] - colors[3]);
+
+ // draw gradient
+ colorspace = CGColorSpaceCreateDeviceRGB();
+ size_t components = 1 + CGColorSpaceGetNumberOfComponents(colorspace);
+ static const CGFloat domain[2] = { 0.0, 1.0 };
+ static const CGFloat range[10] = { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 };
+ //static const CGFunctionCallbacks callbacks = {0, &bilinearShadedColor, NULL};
+
+ // Create a CGFunctionRef that describes a function taking 1 input and kChannelsPerColor outputs.
+ function = CGFunctionCreate(colors, 1, domain, components, range, &functionCallbacks);
+
+ shading = CGShadingCreateAxial(colorspace, startPoint, endPoint, function, NO, NO);
+
+ CGContextSaveGState(currentContext);
+ [self addClip];
+ CGContextDrawShading(currentContext, shading);
+ CGContextRestoreGState(currentContext);
+
+ CGShadingRelease(shading);
+ CGFunctionRelease(function);
+ CGColorSpaceRelease(colorspace);
+}
+
+- (void)customHorizontalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor
+{
+ [self customFillWithCallbacks:functionCallbacks
+ firstColor:firstColor
+ secondColor:secondColor
+ startPoint:CGPointMake(0, NSMinY([self bounds]))
+ endPoint:CGPointMake(0, NSMaxY([self bounds]))];
+}
+
+- (void)customVerticalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor
+{
+ [self customFillWithCallbacks:functionCallbacks
+ firstColor:firstColor
+ secondColor:secondColor
+ startPoint:CGPointMake(NSMinX([self bounds]), 0)
+ endPoint:CGPointMake(NSMaxX([self bounds]), 0)];
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.h b/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.h
new file mode 100644
index 0000000..cbcbf2c
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.h
@@ -0,0 +1,12 @@
+//
+// NSString_AITruncation.h
+// PSMTabBarControl
+//
+// Created by Evan Schoenberg on 7/14/07.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface NSString (AITruncation)
+- (NSString *)stringWithEllipsisByTruncatingToLength:(NSUInteger)length;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.m b/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.m
new file mode 100644
index 0000000..9d9ce61
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/NSString_AITruncation.m
@@ -0,0 +1,34 @@
+//
+// NSString_AITruncation.m
+// PSMTabBarControl
+//
+// Created by Evan Schoenberg on 7/14/07.
+// From Adium, which is licensed under the GPL. Used in PSMTabBarControl with permission.
+// The contents of this remain licensed under the GPL.
+//
+
+#import "NSString_AITruncation.h"
+
+@implementation NSString (AITruncation)
+
++ (id)ellipsis
+{
+ return [NSString stringWithUTF8String:"\xE2\x80\xA6"];
+}
+
+- (NSString *)stringWithEllipsisByTruncatingToLength:(NSUInteger)length
+{
+ NSString *returnString;
+
+ if (length < [self length]) {
+ //Truncate and append the ellipsis
+ returnString = [[self substringToIndex:length - 1] stringByAppendingString:[NSString ellipsis]];
+ } else {
+ //We don't need to truncate, so don't append an ellipsis
+ returnString = [self copy];
+ }
+
+ return returnString;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.h b/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.h
new file mode 100644
index 0000000..0c39f32
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.h
@@ -0,0 +1,27 @@
+//
+// PSMOverflowPopUpButton.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 11/4/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface PSMOverflowPopUpButton : NSPopUpButton {
+ NSImage *_PSMTabBarOverflowPopUpImage;
+ NSImage *_PSMTabBarOverflowDownPopUpImage;
+ BOOL _down;
+ BOOL _animatingAlternateImage;
+ NSTimer *_animationTimer;
+ CGFloat _animationValue;
+}
+
+//alternate image display
+- (BOOL)animatingAlternateImage;
+- (void)setAnimatingAlternateImage:(BOOL)flag;
+
+// archiving
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+- (id)initWithCoder:(NSCoder *)aDecoder;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.m b/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.m
new file mode 100644
index 0000000..d589227
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMOverflowPopUpButton.m
@@ -0,0 +1,157 @@
+//
+// PSMOverflowPopUpButton.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 11/4/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import <math.h>
+#import "PSMOverflowPopUpButton.h"
+#import "PSMTabBarControl.h"
+
+#define TIMER_INTERVAL 1.0 / 15.0
+#define ANIMATION_STEP 0.033f
+
+@implementation PSMOverflowPopUpButton
+
+- (id)initWithFrame:(NSRect)frameRect pullsDown:(BOOL)flag
+{
+ if ((self = [super initWithFrame:frameRect pullsDown:YES]) != nil) {
+ [self setBezelStyle:NSRegularSquareBezelStyle];
+ [self setBordered:NO];
+ [self setTitle:@""];
+ [self setPreferredEdge:NSMaxXEdge];
+ _PSMTabBarOverflowPopUpImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"overflowImage"]];
+ _PSMTabBarOverflowDownPopUpImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"overflowImagePressed"]];
+ _animatingAlternateImage = NO;
+ }
+ return self;
+}
+
+- (void)drawRect:(NSRect)rect
+{
+ if (_PSMTabBarOverflowPopUpImage == nil) {
+ [super drawRect:rect];
+ return;
+ }
+
+ NSImage *image = (_down) ? _PSMTabBarOverflowDownPopUpImage : _PSMTabBarOverflowPopUpImage;
+ NSSize imageSize = [image size];
+ NSRect bounds = [self bounds];
+
+ NSPoint drawPoint = NSMakePoint(NSMidX(bounds) - (imageSize.width * 0.5f), NSMidY(bounds) - (imageSize.height * 0.5f));
+
+ if ([self isFlipped]) {
+ drawPoint.y += imageSize.height;
+ }
+
+ [image drawAtPoint:drawPoint fromRect:CGRectZero operation:NSCompositingOperationSourceOver fraction:(_animatingAlternateImage ? 0.7f : 1.0f)];
+
+ if (_animatingAlternateImage) {
+ NSImage *alternateImage = [self alternateImage];
+ NSSize altImageSize = [alternateImage size];
+ drawPoint = NSMakePoint(NSMidX(bounds) - (altImageSize.width * 0.5f), NSMidY(bounds) - (altImageSize.height * 0.5f));
+
+ if ([self isFlipped]) {
+ drawPoint.y += altImageSize.height;
+ }
+
+ [[self alternateImage] drawAtPoint:drawPoint fromRect:CGRectZero operation:NSCompositingOperationSourceOver fraction:sin(_animationValue * M_PI)];
+ }
+}
+
+- (void)mouseDown:(NSEvent *)event
+{
+ _down = YES;
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationReceived:) name:NSMenuDidEndTrackingNotification object:[self menu]];
+ [self setNeedsDisplay:YES];
+ [super mouseDown:event];
+}
+
+- (void)setHidden:(BOOL)value
+{
+ if ([self isHidden] != value) {
+ if (value) {
+ // Stop any animating alternate image if we hide
+ [_animationTimer invalidate], _animationTimer = nil;
+ } else if (_animatingAlternateImage) {
+ // Restart any animating alternate image if we unhide
+ _animationValue = ANIMATION_STEP;
+ _animationTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(animateStep:) userInfo:nil repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode];
+ }
+ }
+
+ [super setHidden:value];
+}
+
+- (void)notificationReceived:(NSNotification *)notification
+{
+ _down = NO;
+ [self setNeedsDisplay:YES];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)setAnimatingAlternateImage:(BOOL)flag
+{
+ if (_animatingAlternateImage != flag) {
+ _animatingAlternateImage = flag;
+
+ if (![self isHidden]) {
+ if (flag) {
+ _animationValue = ANIMATION_STEP;
+ _animationTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(animateStep:) userInfo:nil repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode];
+ } else {
+ [_animationTimer invalidate], _animationTimer = nil;
+ }
+
+ [self setNeedsDisplay:YES];
+ }
+ }
+}
+
+- (BOOL)animatingAlternateImage
+{
+ return _animatingAlternateImage;
+}
+
+- (void)animateStep:(NSTimer *)timer
+{
+ _animationValue += ANIMATION_STEP;
+
+ if (_animationValue >= 1) {
+ _animationValue = ANIMATION_STEP;
+ }
+
+ [self setNeedsDisplay:YES];
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [super encodeWithCoder:aCoder];
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:_PSMTabBarOverflowPopUpImage forKey:@"PSMTabBarOverflowPopUpImage"];
+ [aCoder encodeObject:_PSMTabBarOverflowDownPopUpImage forKey:@"PSMTabBarOverflowDownPopUpImage"];
+ [aCoder encodeBool:_animatingAlternateImage forKey:@"PSMTabBarOverflowAnimatingAlternateImage"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ if ((self = [super initWithCoder:aDecoder])) {
+ if ([aDecoder allowsKeyedCoding]) {
+ _PSMTabBarOverflowPopUpImage =
+ [aDecoder decodeObjectForKey:@"PSMTabBarOverflowPopUpImage"];
+ _PSMTabBarOverflowDownPopUpImage = [aDecoder decodeObjectForKey:@"PSMTabBarOverflowDownPopUpImage"];
+ [self setAnimatingAlternateImage:[aDecoder decodeBoolForKey:@"PSMTabBarOverflowAnimatingAlternateImage"]];
+ }
+ }
+ return self;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.h b/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.h
new file mode 100644
index 0000000..75c81d3
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.h
@@ -0,0 +1,14 @@
+//
+// PSMProgressIndicator.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 2/23/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface PSMProgressIndicator : NSProgressIndicator {
+}
+
+@end \ No newline at end of file
diff --git a/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.m b/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.m
new file mode 100644
index 0000000..4532f31
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMProgressIndicator.m
@@ -0,0 +1,43 @@
+//
+// PSMProgressIndicator.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 2/23/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMProgressIndicator.h"
+#import "PSMTabBarControl.h"
+
+@interface PSMTabBarControl (PSMProgressIndicatorExtensions)
+
+- (void)update;
+
+@end
+
+@implementation PSMProgressIndicator
+
+- (id)initWithFrame:(NSRect)frameRect
+{
+ if ((self = [super initWithFrame:frameRect]) == nil)
+ return nil;
+ [self setControlSize:NSControlSizeSmall];
+ return self;
+}
+
+// overrides to make tab bar control re-layout things if status changes
+- (void)setHidden:(BOOL)flag
+{
+ [super setHidden:flag];
+ [(PSMTabBarControl *)[self superview] update];
+}
+
+- (void)stopAnimation:(id)sender
+{
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(startAnimation:)
+ object:nil];
+ [super stopAnimation:sender];
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.h b/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.h
new file mode 100644
index 0000000..6f2afdb
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.h
@@ -0,0 +1,28 @@
+//
+// PSMOverflowPopUpButton.h
+// NetScrape
+//
+// Created by John Pannell on 8/4/04.
+// Copyright 2004 Positive Spin Media. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface PSMRolloverButton : NSButton {
+ NSImage *_rolloverImage;
+ NSImage *_usualImage;
+ NSTrackingRectTag _myTrackingRectTag;
+}
+
+// the regular image
+- (void)setUsualImage:(NSImage *)newImage;
+- (NSImage *)usualImage;
+
+// the rollover image
+- (void)setRolloverImage:(NSImage *)newImage;
+- (NSImage *)rolloverImage;
+
+// tracking rect for mouse events
+- (void)addTrackingRect;
+- (void)removeTrackingRect;
+@end \ No newline at end of file
diff --git a/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.m b/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.m
new file mode 100644
index 0000000..65df6aa
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMRolloverButton.m
@@ -0,0 +1,180 @@
+//
+// PSMOverflowPopUpButton.m
+// NetScrape
+//
+// Created by John Pannell on 8/4/04.
+// Copyright 2004 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMRolloverButton.h"
+
+@implementation PSMRolloverButton
+
+- (void)awakeFromNib
+{
+ if ([[self superclass] instancesRespondToSelector:@selector(awakeFromNib)]) {
+ [super awakeFromNib];
+ }
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(rolloverFrameDidChange:)
+ name:NSViewFrameDidChangeNotification
+ object:self];
+ [self setPostsFrameChangedNotifications:YES];
+ [self resetCursorRects];
+
+ _myTrackingRectTag = -1;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [self removeTrackingRect];
+}
+
+// the regular image
+- (void)setUsualImage:(NSImage *)newImage
+{
+ _usualImage = newImage;
+
+ [self setImage:_usualImage];
+}
+
+- (NSImage *)usualImage
+{
+ return _usualImage;
+}
+
+- (void)setRolloverImage:(NSImage *)newImage
+{
+ _rolloverImage = newImage;
+}
+
+- (NSImage *)rolloverImage
+{
+ return _rolloverImage;
+}
+
+//Remove old tracking rects when we change superviews
+- (void)viewWillMoveToSuperview:(NSView *)newSuperview
+{
+ [self removeTrackingRect];
+
+ [super viewWillMoveToSuperview:newSuperview];
+}
+
+- (void)viewDidMoveToSuperview
+{
+ [super viewDidMoveToSuperview];
+
+ [self resetCursorRects];
+}
+
+- (void)viewWillMoveToWindow:(NSWindow *)newWindow
+{
+ [self removeTrackingRect];
+
+ [super viewWillMoveToWindow:newWindow];
+}
+
+- (void)viewDidMoveToWindow
+{
+ [super viewDidMoveToWindow];
+
+ [self resetCursorRects];
+}
+
+- (void)rolloverFrameDidChange:(NSNotification *)inNotification
+{
+ [self resetCursorRects];
+}
+
+- (void)addTrackingRect
+{
+ // assign a tracking rect to watch for mouse enter/exit
+ NSRect trackRect = [self bounds];
+ NSPoint localPoint = [self convertPoint:NSPointFromCGPoint([self.window convertRectFromScreen:(NSRect){.origin = NSEvent.mouseLocation, .size = CGSizeMake(1, 1) }].origin) fromView:nil];
+ BOOL mouseInside = NSPointInRect(localPoint, trackRect);
+
+ _myTrackingRectTag = [self addTrackingRect:trackRect owner:self userData:nil assumeInside:mouseInside];
+ if (mouseInside) {
+ //[self mouseEntered:nil];
+ [self setImage:_rolloverImage];
+ } else {
+ //[self mouseExited:nil];
+ [self setImage:_usualImage];
+ }
+}
+
+- (void)removeTrackingRect
+{
+ if (_myTrackingRectTag != -1) {
+ [self removeTrackingRect:_myTrackingRectTag];
+ }
+ _myTrackingRectTag = -1;
+}
+
+// override for rollover effect
+- (void)mouseEntered:(NSEvent *)theEvent
+{
+ // set rollover image
+ [self setImage:_rolloverImage];
+
+ [super mouseEntered:theEvent];
+}
+
+- (void)mouseExited:(NSEvent *)theEvent
+{
+ // restore usual image
+ [self setImage:_usualImage];
+
+ [super mouseExited:theEvent];
+}
+
+- (void)resetCursorRects
+{
+ // called when the button rect has been changed
+ [self removeTrackingRect];
+ [self addTrackingRect];
+}
+
+- (void)setFrame:(NSRect)rect
+{
+ [super setFrame:rect];
+ [self resetCursorRects];
+}
+
+- (void)setBounds:(NSRect)rect
+{
+ [super setBounds:rect];
+ [self resetCursorRects];
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [super encodeWithCoder:aCoder];
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:_rolloverImage forKey:@"rolloverImage"];
+ [aCoder encodeObject:_usualImage forKey:@"usualImage"];
+ [aCoder encodeInt64:_myTrackingRectTag forKey:@"myTrackingRectTag"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super initWithCoder:aDecoder];
+ if (self) {
+ if ([aDecoder allowsKeyedCoding]) {
+ _rolloverImage = [aDecoder decodeObjectForKey:@"rolloverImage"];
+ _usualImage = [aDecoder decodeObjectForKey:@"usualImage"];
+ _myTrackingRectTag = [aDecoder decodeInt64ForKey:@"myTrackingRectTag"];
+ }
+ }
+ return self;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.h b/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.h
new file mode 100644
index 0000000..f5c51b4
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.h
@@ -0,0 +1,115 @@
+//
+// PSMTabBarCell.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 10/13/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PSMTabBarControl.h"
+#import "PSMProgressIndicator.h"
+
+@interface PSMTabBarCell : NSActionCell {
+ // sizing
+ NSRect _frame;
+ NSSize _stringSize;
+ NSInteger _currentStep;
+ BOOL _isPlaceholder;
+
+ // state
+ NSInteger _tabState;
+ NSTrackingRectTag _closeButtonTrackingTag; // left side tracking, if dragging
+ NSTrackingRectTag _cellTrackingTag; // right side tracking, if dragging
+ BOOL _closeButtonOver;
+ BOOL _closeButtonPressed;
+ PSMProgressIndicator *_indicator;
+ BOOL _isInOverflowMenu;
+ BOOL _hasCloseButton;
+ BOOL _isCloseButtonSuppressed;
+ BOOL _hasIcon;
+ BOOL _hasLargeImage;
+ NSInteger _count;
+ NSColor *_countColor;
+ BOOL _isEdited;
+}
+
+// creation/destruction
+- (id)initWithControlView:(PSMTabBarControl *)controlView;
+- (id)initPlaceholderWithFrame:(NSRect)frame expanded:(BOOL)value inControlView:(PSMTabBarControl *)controlView;
+- (void)dealloc;
+
+// accessors
+- (id)controlView;
+- (void)setControlView:(id)view;
+- (NSTrackingRectTag)closeButtonTrackingTag;
+- (void)setCloseButtonTrackingTag:(NSTrackingRectTag)tag;
+- (NSTrackingRectTag)cellTrackingTag;
+- (void)setCellTrackingTag:(NSTrackingRectTag)tag;
+- (CGFloat)width;
+- (NSRect)frame;
+- (void)setFrame:(NSRect)rect;
+- (void)setStringValue:(NSString *)aString;
+- (NSSize)stringSize;
+- (NSAttributedString *)attributedStringValue;
+- (NSInteger)tabState;
+- (void)setTabState:(NSInteger)state;
+- (NSProgressIndicator *)indicator;
+- (BOOL)isInOverflowMenu;
+- (void)setIsInOverflowMenu:(BOOL)value;
+- (BOOL)closeButtonPressed;
+- (void)setCloseButtonPressed:(BOOL)value;
+- (BOOL)closeButtonOver;
+- (void)setCloseButtonOver:(BOOL)value;
+- (BOOL)hasCloseButton;
+- (void)setHasCloseButton:(BOOL)set;
+- (void)setCloseButtonSuppressed:(BOOL)suppress;
+- (BOOL)isCloseButtonSuppressed;
+- (BOOL)hasIcon;
+- (void)setHasIcon:(BOOL)value;
+- (BOOL)hasLargeImage;
+- (void)setHasLargeImage:(BOOL)value;
+- (NSInteger)count;
+- (void)setCount:(NSInteger)value;
+- (NSColor *)countColor;
+- (void)setCountColor:(NSColor *)value;
+- (BOOL)isPlaceholder;
+- (void)setIsPlaceholder:(BOOL)value;
+- (NSInteger)currentStep;
+- (void)setCurrentStep:(NSInteger)value;
+- (BOOL)isEdited;
+- (void)setIsEdited:(BOOL)value;
+
+// component attributes
+- (NSRect)indicatorRectForFrame:(NSRect)cellFrame;
+- (NSRect)closeButtonRectForFrame:(NSRect)cellFrame;
+- (CGFloat)minimumWidthOfCell;
+- (CGFloat)desiredWidthOfCell;
+
+// drawing
+- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
+
+// tracking the mouse
+- (void)mouseEntered:(NSEvent *)theEvent;
+- (void)mouseExited:(NSEvent *)theEvent;
+
+// drag support
+- (NSImage *)dragImage;
+
+// archiving
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+- (id)initWithCoder:(NSCoder *)aDecoder;
+
+@end
+
+@interface PSMTabBarControl (CellAccessors)
+
+- (id<PSMTabStyle>)style;
+
+@end
+
+@interface NSObject (IdentifierAccesors)
+
+- (NSImage *)largeImage;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.m b/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.m
new file mode 100644
index 0000000..5a8edfd
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarCell.m
@@ -0,0 +1,536 @@
+//
+// PSMTabBarCell.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 10/13/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMTabBarCell.h"
+#import "PSMTabBarControl.h"
+#import "PSMTabStyle.h"
+#import "PSMProgressIndicator.h"
+#import "PSMTabDragAssistant.h"
+#import "PSMTabBarControl+Private.h"
+
+@implementation PSMTabBarCell
+
+#pragma mark -
+#pragma mark Creation/Destruction
+- (id)initWithControlView:(PSMTabBarControl *)controlView
+{
+ if ((self = [super init])) {
+ _controlView = controlView;
+ _closeButtonTrackingTag = 0;
+ _cellTrackingTag = 0;
+ _closeButtonOver = NO;
+ _closeButtonPressed = NO;
+ _indicator = [[PSMProgressIndicator alloc] initWithFrame:NSMakeRect(0.0, 0.0, kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth)];
+ [_indicator setStyle:NSProgressIndicatorSpinningStyle];
+ [_indicator setAutoresizingMask:NSViewMinYMargin];
+ _hasCloseButton = YES;
+ _isCloseButtonSuppressed = NO;
+ _count = 0;
+ _countColor = nil;
+ _isEdited = NO;
+ _isPlaceholder = NO;
+ }
+ return self;
+}
+
+- (id)initPlaceholderWithFrame:(NSRect)frame expanded:(BOOL)value inControlView:(PSMTabBarControl *)controlView
+{
+ if ((self = [super init])) {
+ _controlView = controlView;
+ _isPlaceholder = YES;
+ if (!value) {
+ if ([controlView orientation] == PSMTabBarHorizontalOrientation) {
+ frame.size.width = 0.0;
+ } else {
+ frame.size.height = 0.0;
+ }
+ }
+ [self setFrame:frame];
+ _closeButtonTrackingTag = 0;
+ _cellTrackingTag = 0;
+ _closeButtonOver = NO;
+ _closeButtonPressed = NO;
+ _indicator = nil;
+ _hasCloseButton = YES;
+ _isCloseButtonSuppressed = NO;
+ _count = 0;
+ _countColor = nil;
+ _isEdited = NO;
+
+ if (value) {
+ [self setCurrentStep:(kPSMTabDragAnimationSteps - 1)];
+ } else {
+ [self setCurrentStep:0];
+ }
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+
+ [_indicator removeFromSuperviewWithoutNeedingDisplay];
+}
+
+#pragma mark -
+#pragma mark Accessors
+
+- (id)controlView
+{
+ return _controlView;
+}
+
+- (void)setControlView:(id)view
+{
+ // no retain release pattern, as this simply switches a tab to another view.
+ _controlView = view;
+}
+
+- (NSTrackingRectTag)closeButtonTrackingTag
+{
+ return _closeButtonTrackingTag;
+}
+
+- (void)setCloseButtonTrackingTag:(NSTrackingRectTag)tag
+{
+ _closeButtonTrackingTag = tag;
+}
+
+- (NSTrackingRectTag)cellTrackingTag
+{
+ return _cellTrackingTag;
+}
+
+- (void)setCellTrackingTag:(NSTrackingRectTag)tag
+{
+ _cellTrackingTag = tag;
+}
+
+- (CGFloat)width
+{
+ return _frame.size.width;
+}
+
+- (NSRect)frame
+{
+ return _frame;
+}
+
+- (void)setFrame:(NSRect)rect
+{
+ _frame = rect;
+
+ //move the status indicator along with the rest of the cell
+ if (![[self indicator] isHidden] && ![_controlView isTabBarHidden]) {
+ [[self indicator] setFrame:[self indicatorRectForFrame:rect]];
+ }
+}
+
+- (void)setStringValue:(NSString *)aString
+{
+ [super setStringValue:aString];
+ _stringSize = [[self attributedStringValue] size];
+ // need to redisplay now - binding observation was too quick.
+ [_controlView update];
+}
+
+- (NSSize)stringSize
+{
+ return _stringSize;
+}
+
+- (NSAttributedString *)attributedStringValue
+{
+ return [(id<PSMTabStyle>)[(PSMTabBarControl *)_controlView style] attributedStringValueForTabCell:self];
+}
+
+- (NSInteger)tabState
+{
+ return _tabState;
+}
+
+- (void)setTabState:(NSInteger)state
+{
+ _tabState = state;
+}
+
+- (NSProgressIndicator *)indicator
+{
+ return _indicator;
+}
+
+- (BOOL)isInOverflowMenu
+{
+ return _isInOverflowMenu;
+}
+
+- (void)setIsInOverflowMenu:(BOOL)value
+{
+ if (_isInOverflowMenu != value) {
+ _isInOverflowMenu = value;
+ if ([[[self controlView] delegate] respondsToSelector:@selector(tabView:tabViewItem:isInOverflowMenu:)]) {
+ [[[self controlView] delegate] tabView:[self controlView] tabViewItem:[self representedObject] isInOverflowMenu:_isInOverflowMenu];
+ }
+ }
+}
+
+- (BOOL)closeButtonPressed
+{
+ return _closeButtonPressed;
+}
+
+- (void)setCloseButtonPressed:(BOOL)value
+{
+ _closeButtonPressed = value;
+}
+
+- (BOOL)closeButtonOver
+{
+ return (_closeButtonOver && ([_controlView allowsBackgroundTabClosing] || ([self tabState] & PSMTab_SelectedMask) || [[NSApp currentEvent] modifierFlags] & NSEventModifierFlagCommand));
+}
+
+- (void)setCloseButtonOver:(BOOL)value
+{
+ _closeButtonOver = value;
+}
+
+- (BOOL)hasCloseButton
+{
+ return _hasCloseButton;
+}
+
+- (void)setHasCloseButton:(BOOL)set
+{
+ _hasCloseButton = set;
+}
+
+- (void)setCloseButtonSuppressed:(BOOL)suppress
+{
+ _isCloseButtonSuppressed = suppress;
+}
+
+- (BOOL)isCloseButtonSuppressed
+{
+ return _isCloseButtonSuppressed;
+}
+
+- (BOOL)hasIcon
+{
+ return _hasIcon;
+}
+
+- (void)setHasIcon:(BOOL)value
+{
+ _hasIcon = value;
+ //[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
+}
+
+- (BOOL)hasLargeImage
+{
+ return _hasLargeImage;
+}
+
+- (void)setHasLargeImage:(BOOL)value
+{
+ _hasLargeImage = value;
+}
+
+- (NSInteger)count
+{
+ return _count;
+}
+
+- (void)setCount:(NSInteger)value
+{
+ _count = value;
+ //[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
+}
+
+- (NSColor *)countColor
+{
+ return _countColor;
+}
+
+- (void)setCountColor:(NSColor *)color
+{
+ _countColor = color;
+}
+
+- (BOOL)isPlaceholder
+{
+ return _isPlaceholder;
+}
+
+- (void)setIsPlaceholder:(BOOL)value
+{
+ _isPlaceholder = value;
+}
+
+- (NSInteger)currentStep
+{
+ return _currentStep;
+}
+
+- (void)setCurrentStep:(NSInteger)value
+{
+ if (value < 0) {
+ value = 0;
+ }
+
+ if (value > (kPSMTabDragAnimationSteps - 1)) {
+ value = (kPSMTabDragAnimationSteps - 1);
+ }
+
+ _currentStep = value;
+}
+
+- (BOOL)isEdited
+{
+ return _isEdited;
+}
+
+- (void)setIsEdited:(BOOL)value
+{
+ _isEdited = value;
+ //[_controlView update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast
+}
+
+#pragma mark -
+#pragma mark Bindings
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ // the progress indicator, label, icon, or count has changed - redraw the control view
+ //[_controlView update];
+ //I seem to have run into some odd issue with update not being called at the right time. This seems to avoid the problem.
+ [_controlView performSelector:@selector(update) withObject:nil afterDelay:0.0];
+}
+
+#pragma mark -
+#pragma mark Component Attributes
+
+- (NSRect)indicatorRectForFrame:(NSRect)cellFrame
+{
+ return [(id<PSMTabStyle>)[(PSMTabBarControl *)_controlView style] indicatorRectForTabCell:self];
+}
+
+- (NSRect)closeButtonRectForFrame:(NSRect)cellFrame
+{
+ return [(id<PSMTabStyle>)[(PSMTabBarControl *)_controlView style] closeButtonRectForTabCell:self withFrame:cellFrame];
+}
+
+- (CGFloat)minimumWidthOfCell
+{
+ return [(id<PSMTabStyle>)[(PSMTabBarControl *)_controlView style] minimumWidthOfTabCell:self];
+}
+
+- (CGFloat)desiredWidthOfCell
+{
+ return [(id<PSMTabStyle>)[(PSMTabBarControl *)_controlView style] desiredWidthOfTabCell:self];
+}
+
+#pragma mark -
+#pragma mark Drawing
+
+- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
+{
+ if (_isPlaceholder) {
+ [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set];
+ NSRectFillUsingOperation(cellFrame, NSCompositingOperationSourceAtop);
+ return;
+ }
+
+ [(id<PSMTabStyle>)[(PSMTabBarControl *)_controlView style] drawTabCell:self];
+}
+
+#pragma mark -
+#pragma mark Tracking
+
+- (void)mouseEntered:(NSEvent *)theEvent
+{
+ // check for which tag
+ if ([theEvent trackingNumber] == _closeButtonTrackingTag) {
+ _closeButtonOver = YES;
+ }
+ if ([theEvent trackingNumber] == _cellTrackingTag) {
+ [self setHighlighted:YES];
+ [_controlView setNeedsDisplay:NO];
+ }
+
+ // scrubtastic
+ if ([_controlView allowsScrubbing] && ([theEvent modifierFlags] & NSEventModifierFlagOption)) {
+ [_controlView performSelector:@selector(tabClick:) withObject:self];
+ }
+
+ // tell the control we only need to redraw the affected tab
+ [_controlView setNeedsDisplayInRect:NSInsetRect([self frame], -2, -2)];
+}
+
+- (void)mouseExited:(NSEvent *)theEvent
+{
+ // check for which tag
+ if ([theEvent trackingNumber] == _closeButtonTrackingTag) {
+ _closeButtonOver = NO;
+ }
+
+ if ([theEvent trackingNumber] == _cellTrackingTag) {
+ [self setHighlighted:NO];
+ [_controlView setNeedsDisplay:NO];
+ }
+
+ //tell the control we only need to redraw the affected tab
+ [_controlView setNeedsDisplayInRect:NSInsetRect([self frame], -2, -2)];
+}
+
+#pragma mark -
+#pragma mark Drag Support
+
+- (NSImage *)dragImage
+{
+ NSRect cellFrame = [(id<PSMTabStyle>)[(PSMTabBarControl *)_controlView style] dragRectForTabCell:self orientation:(PSMTabBarOrientation)[(PSMTabBarControl *)_controlView orientation]];
+ //NSRect cellFrame = [self frame];
+
+ [_controlView lockFocus];
+ NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:cellFrame];
+ [_controlView unlockFocus];
+ NSImage *image = [[NSImage alloc] initWithSize:[rep size]];
+ [image addRepresentation:rep];
+ NSImage *returnImage = [[NSImage alloc] initWithSize:[rep size]];
+ [returnImage lockFocus];
+ [image drawAtPoint:NSMakePoint(0.0, 0.0) fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
+ [returnImage unlockFocus];
+ if (![[self indicator] isHidden]) {
+ NSImage *pi = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"pi"]];
+ [returnImage lockFocus];
+ NSPoint indicatorPoint = NSMakePoint([self frame].size.width - MARGIN_X - kPSMTabBarIndicatorWidth, MARGIN_Y);
+ [pi drawAtPoint:indicatorPoint fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
+ [returnImage unlockFocus];
+ }
+ return returnImage;
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [super encodeWithCoder:aCoder];
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeRect:_frame forKey:@"frame"];
+ [aCoder encodeSize:_stringSize forKey:@"stringSize"];
+ [aCoder encodeInteger:_currentStep forKey:@"currentStep"];
+ [aCoder encodeBool:_isPlaceholder forKey:@"isPlaceholder"];
+ [aCoder encodeInteger:_tabState forKey:@"tabState"];
+ [aCoder encodeInteger:_closeButtonTrackingTag forKey:@"closeButtonTrackingTag"];
+ [aCoder encodeInteger:_cellTrackingTag forKey:@"cellTrackingTag"];
+ [aCoder encodeBool:_closeButtonOver forKey:@"closeButtonOver"];
+ [aCoder encodeBool:_closeButtonPressed forKey:@"closeButtonPressed"];
+ [aCoder encodeObject:_indicator forKey:@"indicator"];
+ [aCoder encodeBool:_isInOverflowMenu forKey:@"isInOverflowMenu"];
+ [aCoder encodeBool:_hasCloseButton forKey:@"hasCloseButton"];
+ [aCoder encodeBool:_isCloseButtonSuppressed forKey:@"isCloseButtonSuppressed"];
+ [aCoder encodeBool:_hasIcon forKey:@"hasIcon"];
+ [aCoder encodeBool:_hasLargeImage forKey:@"hasLargeImage"];
+ [aCoder encodeInteger:_count forKey:@"count"];
+ [aCoder encodeBool:_isEdited forKey:@"isEdited"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super initWithCoder:aDecoder];
+ if (self) {
+ if ([aDecoder allowsKeyedCoding]) {
+ _frame = [aDecoder decodeRectForKey:@"frame"];
+ _stringSize = [aDecoder decodeSizeForKey:@"stringSize"];
+ _currentStep = [aDecoder decodeIntegerForKey:@"currentStep"];
+ _isPlaceholder = [aDecoder decodeBoolForKey:@"isPlaceholder"];
+ _tabState = [aDecoder decodeIntegerForKey:@"tabState"];
+ _closeButtonTrackingTag = [aDecoder decodeIntegerForKey:@"closeButtonTrackingTag"];
+ _cellTrackingTag = [aDecoder decodeIntegerForKey:@"cellTrackingTag"];
+ _closeButtonOver = [aDecoder decodeBoolForKey:@"closeButtonOver"];
+ _closeButtonPressed = [aDecoder decodeBoolForKey:@"closeButtonPressed"];
+ _indicator = [aDecoder decodeObjectForKey:@"indicator"];
+ _isInOverflowMenu = [aDecoder decodeBoolForKey:@"isInOverflowMenu"];
+ _hasCloseButton = [aDecoder decodeBoolForKey:@"hasCloseButton"];
+ _isCloseButtonSuppressed = [aDecoder decodeBoolForKey:@"isCloseButtonSuppressed"];
+ _hasIcon = [aDecoder decodeBoolForKey:@"hasIcon"];
+ _hasLargeImage = [aDecoder decodeBoolForKey:@"hasLargeImage"];
+ _count = [aDecoder decodeIntegerForKey:@"count"];
+ _isEdited = [aDecoder decodeBoolForKey:@"isEdited"];
+ }
+ }
+ return self;
+}
+
+#pragma mark -
+#pragma mark Accessibility
+
+- (BOOL)accessibilityIsIgnored
+{
+ return NO;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute
+{
+ id attributeValue = nil;
+
+ if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
+ attributeValue = NSAccessibilityButtonRole;
+ } else if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) {
+ if ([[[self controlView] delegate] respondsToSelector:@selector(accessibilityStringForTabView:objectCount:)]) {
+ attributeValue = [NSString stringWithFormat:@"%@, %lu %@", [self stringValue],
+ (unsigned long)[self count],
+ [[[self controlView] delegate] accessibilityStringForTabView:[[self controlView] tabView]
+ objectCount:[self count]]];
+ } else {
+ attributeValue = [self stringValue];
+ }
+ } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+ attributeValue = [NSNumber numberWithBool:([self tabState] == 2)];
+ } else {
+ attributeValue = [super accessibilityAttributeValue:attribute];
+ }
+
+ return attributeValue;
+}
+
+- (NSArray *)accessibilityActionNames
+{
+ static NSArray *actions;
+
+ if (!actions) {
+ actions = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction, nil];
+ }
+ return actions;
+}
+
+- (NSString *)accessibilityActionDescription:(NSString *)action
+{
+ return NSAccessibilityActionDescription(action);
+}
+
+- (void)accessibilityPerformAction:(NSString *)action
+{
+ if ([action isEqualToString:NSAccessibilityPressAction]) {
+ // this tab was selected
+ [_controlView performSelector:@selector(tabClick:) withObject:self];
+ }
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ return NSAccessibilityUnignoredAncestor(self);
+}
+
+- (id)accessibilityFocusedUIElement:(NSPoint)point
+{
+ return NSAccessibilityUnignoredAncestor(self);
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarControl+Private.h b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl+Private.h
new file mode 100644
index 0000000..8df3fd3
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl+Private.h
@@ -0,0 +1,49 @@
+#import "PSMTabBarControl.h"
+
+@interface PSMTabBarControl (Private)
+
+// constructor/destructor
+- (void)initAddedProperties;
+
+// accessors
+- (NSEvent *)lastMouseDownEvent;
+- (void)setLastMouseDownEvent:(NSEvent *)event;
+
+// contents
+- (void)addTabViewItem:(NSTabViewItem *)item;
+- (void)removeTabForCell:(PSMTabBarCell *)cell;
+
+// draw
+- (void)update;
+- (void)update:(BOOL)animate;
+- (void)_setupTrackingRectsForCell:(PSMTabBarCell *)cell;
+- (void)_positionOverflowMenu;
+- (void)_checkWindowFrame;
+
+// actions
+- (void)overflowMenuAction:(id)sender;
+- (void)closeTabClick:(id)sender;
+- (void)tabClick:(id)sender;
+- (void)tabNothing:(id)sender;
+
+// notification handlers
+- (void)frameDidChange:(NSNotification *)notification;
+- (void)windowDidMove:(NSNotification *)aNotification;
+- (void)windowDidUpdate:(NSNotification *)notification;
+
+// NSTabView delegate
+- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
+- (BOOL)tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem;
+- (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem;
+- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)tabView;
+
+// archiving
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+- (id)initWithCoder:(NSCoder *)aDecoder;
+
+// convenience
+- (void)_bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item;
+- (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame;
+
+- (void)_animateCells:(NSTimer *)timer;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.h b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.h
new file mode 100644
index 0000000..26f11de
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.h
@@ -0,0 +1,239 @@
+//
+// PSMTabBarControl.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 10/13/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+/*
+ This view provides a control interface to manage a regular NSTabView. It looks and works like the tabbed browsing interface of many popular browsers.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#define PSMTabDragDidEndNotification @"PSMTabDragDidEndNotification"
+#define PSMTabDragDidBeginNotification @"PSMTabDragDidBeginNotification"
+
+#define kPSMTabBarControlHeight 22
+// internal cell border
+#define MARGIN_X 6
+#define MARGIN_Y 3
+// padding between objects
+#define kPSMTabBarCellPadding 4
+// fixed size objects
+#define kPSMMinimumTitleWidth 30
+#define kPSMTabBarIndicatorWidth 16.0
+#define kPSMTabBarIconWidth 16.0
+#define kPSMHideAnimationSteps 3.0
+
+// Value used in _currentStep to indicate that resizing operation is not in progress
+#define kPSMIsNotBeingResized -1
+
+// Value used in _currentStep when a resizing operation has just been started
+#define kPSMStartResizeAnimation 0
+
+@class PSMOverflowPopUpButton;
+@class PSMRolloverButton;
+@class PSMTabBarCell;
+@class PSMTabBarController;
+@protocol PSMTabStyle;
+
+typedef enum {
+ PSMTabBarHorizontalOrientation,
+ PSMTabBarVerticalOrientation
+} PSMTabBarOrientation;
+
+typedef enum {
+ PSMTabBarTearOffAlphaWindow,
+ PSMTabBarTearOffMiniwindow
+} PSMTabBarTearOffStyle;
+
+enum {
+ PSMTab_SelectedMask = 1 << 1,
+ PSMTab_LeftIsSelectedMask = 1 << 2,
+ PSMTab_RightIsSelectedMask = 1 << 3,
+ PSMTab_PositionLeftMask = 1 << 4,
+ PSMTab_PositionMiddleMask = 1 << 5,
+ PSMTab_PositionRightMask = 1 << 6,
+ PSMTab_PositionSingleMask = 1 << 7,
+};
+
+@interface PSMTabBarControl : NSControl {
+
+ // control basics
+ NSMutableArray *_cells; // the cells that draw the tabs
+ IBOutlet NSTabView *tabView; // the tab view being navigated
+ PSMOverflowPopUpButton *_overflowPopUpButton; // for too many tabs
+ PSMRolloverButton *_addTabButton;
+ PSMTabBarController *_controller;
+
+ // Spring-loading.
+ NSTimer *_springTimer;
+ NSTabViewItem *_tabViewItemWithSpring;
+
+ // drawing style
+ id<PSMTabStyle> style;
+ BOOL _canCloseOnlyTab;
+ BOOL _disableTabClose;
+ BOOL _hideForSingleTab;
+ BOOL _showAddTabButton;
+ BOOL _sizeCellsToFit;
+ BOOL _useOverflowMenu;
+ BOOL _alwaysShowActiveTab;
+ BOOL _allowsScrubbing;
+ NSInteger _resizeAreaCompensation;
+ PSMTabBarOrientation _orientation;
+ BOOL _automaticallyAnimates;
+ NSTimer *_animationTimer;
+ PSMTabBarTearOffStyle _tearOffStyle;
+
+ // behavior
+ BOOL _allowsBackgroundTabClosing;
+ BOOL _selectsTabsOnMouseDown;
+
+ // vertical tab resizing
+ BOOL _allowsResizing;
+ BOOL _resizing;
+
+ // cell width
+ NSInteger _cellMinWidth;
+ NSInteger _cellMaxWidth;
+ NSInteger _cellOptimumWidth;
+
+ // animation for hide/show
+ NSInteger _currentStep;
+ BOOL _isHidden;
+ IBOutlet id partnerView; // gets resized when hide/show
+ BOOL _awakenedFromNib;
+ NSInteger _tabBarWidth;
+ NSTimer *_showHideAnimationTimer;
+
+ // drag and drop
+ NSEvent *_lastMouseDownEvent; // keep this for dragging reference
+ BOOL _didDrag;
+ BOOL _closeClicked;
+
+ // MVC help
+ IBOutlet id delegate;
+}
+
+// control characteristics
++ (NSBundle *)bundle;
+- (CGFloat)availableCellWidth;
+- (NSRect)genericCellRect;
+
+// control configuration
+- (PSMTabBarOrientation)orientation;
+- (void)setOrientation:(PSMTabBarOrientation)value;
+- (BOOL)canCloseOnlyTab;
+- (void)setCanCloseOnlyTab:(BOOL)value;
+- (BOOL)disableTabClose;
+- (void)setDisableTabClose:(BOOL)value;
+- (id<PSMTabStyle>)style;
+- (void)setStyle:(id<PSMTabStyle>)newStyle;
+- (NSString *)styleName;
+- (void)setStyleNamed:(NSString *)name;
+- (BOOL)hideForSingleTab;
+- (void)setHideForSingleTab:(BOOL)value;
+- (BOOL)showAddTabButton;
+- (void)setShowAddTabButton:(BOOL)value;
+- (NSInteger)cellMinWidth;
+- (void)setCellMinWidth:(NSInteger)value;
+- (NSInteger)cellMaxWidth;
+- (void)setCellMaxWidth:(NSInteger)value;
+- (NSInteger)cellOptimumWidth;
+- (void)setCellOptimumWidth:(NSInteger)value;
+- (BOOL)sizeCellsToFit;
+- (void)setSizeCellsToFit:(BOOL)value;
+- (BOOL)useOverflowMenu;
+- (void)setUseOverflowMenu:(BOOL)value;
+- (BOOL)allowsBackgroundTabClosing;
+- (void)setAllowsBackgroundTabClosing:(BOOL)value;
+- (BOOL)allowsResizing;
+- (void)setAllowsResizing:(BOOL)value;
+- (BOOL)selectsTabsOnMouseDown;
+- (void)setSelectsTabsOnMouseDown:(BOOL)value;
+- (BOOL)automaticallyAnimates;
+- (void)setAutomaticallyAnimates:(BOOL)value;
+- (BOOL)alwaysShowActiveTab;
+- (void)setAlwaysShowActiveTab:(BOOL)value;
+- (BOOL)allowsScrubbing;
+- (void)setAllowsScrubbing:(BOOL)value;
+- (PSMTabBarTearOffStyle)tearOffStyle;
+- (void)setTearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle;
+
+// Factory for default style
++ (Class)defaultStyleClass;
+
+// accessors
+- (NSTabView *)tabView;
+- (void)setTabView:(NSTabView *)view;
+- (id)delegate;
+- (void)setDelegate:(id)object;
+- (id)partnerView;
+- (void)setPartnerView:(id)view;
+
+// the buttons
+- (PSMRolloverButton *)addTabButton;
+- (PSMOverflowPopUpButton *)overflowPopUpButton;
+
+// tab information
+- (NSMutableArray *)representedTabViewItems;
+- (NSInteger)numberOfVisibleTabs;
+- (PSMTabBarCell *)lastVisibleTab;
+
+// special effects
+- (void)hideTabBar:(BOOL)hide animate:(BOOL)animate;
+- (BOOL)isTabBarHidden;
+- (BOOL)isAnimating;
+
+// internal bindings methods also used by the tab drag assistant
+- (void)bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item;
+- (void)removeTabForCell:(PSMTabBarCell *)cell;
+
+@end
+
+@interface NSObject (TabBarControlDelegateMethods)
+
+//Standard NSTabView methods
+- (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem;
+- (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//"Spring-loaded" tabs methods
+- (NSArray *)allowedDraggedTypesForTabView:(NSTabView *)aTabView;
+- (void)tabView:(NSTabView *)aTabView acceptedDraggingInfo:(id<NSDraggingInfo>)draggingInfo onTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//Contextual menu method
+- (NSMenu *)tabView:(NSTabView *)aTabView menuForTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//Drag and drop methods
+- (BOOL)tabView:(NSTabView *)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl;
+- (BOOL)tabView:(NSTabView *)aTabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl;
+- (BOOL)tabView:(NSTabView *)aTabView shouldAllowTabViewItem:(NSTabViewItem *)tabViewItem toLeaveTabBar:(PSMTabBarControl *)tabBarControl;
+- (void)tabView:(NSTabView *)aTabView didDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl;
+
+//Tear-off tabs methods
+- (NSImage *)tabView:(NSTabView *)aTabView imageForTabViewItem:(NSTabViewItem *)tabViewItem offset:(NSSize *)offset styleMask:(NSUInteger *)styleMask;
+- (PSMTabBarControl *)tabView:(NSTabView *)aTabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point;
+- (void)tabView:(NSTabView *)aTabView closeWindowForLastTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//Overflow menu validation
+- (BOOL)tabView:(NSTabView *)aTabView validateOverflowMenuItem:(NSMenuItem *)menuItem forTabViewItem:(NSTabViewItem *)tabViewItem;
+- (void)tabView:(NSTabView *)aTabView tabViewItem:(NSTabViewItem *)tabViewItem isInOverflowMenu:(BOOL)inOverflowMenu;
+
+//tab bar hiding methods
+- (void)tabView:(NSTabView *)aTabView tabBarDidHide:(PSMTabBarControl *)tabBarControl;
+- (void)tabView:(NSTabView *)aTabView tabBarDidUnhide:(PSMTabBarControl *)tabBarControl;
+- (CGFloat)desiredWidthForVerticalTabBar:(PSMTabBarControl *)tabBarControl;
+
+//closing
+- (BOOL)tabView:(NSTabView *)aTabView disableTabCloseForTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//tooltips
+- (NSString *)tabView:(NSTabView *)aTabView toolTipForTabViewItem:(NSTabViewItem *)tabViewItem;
+
+//accessibility
+- (NSString *)accessibilityStringForTabView:(NSTabView *)aTabView objectCount:(NSInteger)objectCount;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.m b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.m
new file mode 100644
index 0000000..0d6a2d7
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarControl.m
@@ -0,0 +1,2035 @@
+//
+// PSMTabBarControl.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 10/13/05.
+// Copyright 2005 Positive Spin Media. All rights reserved.
+//
+
+#import <objc/runtime.h>
+
+#import "PSMTabBarControl.h"
+#import "PSMTabBarCell.h"
+#import "PSMOverflowPopUpButton.h"
+#import "PSMRolloverButton.h"
+#import "PSMTabStyle.h"
+#import "PSMUnifiedTabStyle.h"
+#import "PSMTabDragAssistant.h"
+#import "PSMTabBarController.h"
+
+#import "PSMTabBarControl+Private.h"
+
+@protocol PSMTabBarItem
+@optional
+- (BOOL)isProcessing;
+
+@optional
+- (int)objectCount;
+
+@end
+
+@implementation PSMTabBarControl
+
+#pragma mark -
+#pragma mark Characteristics
+
++ (NSBundle *)bundle
+{
+ static NSBundle *bundle = nil;
+ if (!bundle) {
+ bundle = [NSBundle bundleForClass:[PSMTabBarControl class]];
+ }
+ return bundle;
+}
+
+/*!
+ @method availableCellWidth
+ @abstract The number of pixels available for cells
+ @discussion Calculates the number of pixels available for cells based on margins and the window resize badge.
+ @returns Returns the amount of space for cells.
+ */
+
+- (CGFloat)availableCellWidth
+{
+ return [self frame].size.width - [style leftMarginForTabBarControl] - [style rightMarginForTabBarControl] - _resizeAreaCompensation;
+}
+
+/*!
+ @method genericCellRect
+ @abstract The basic rect for a tab cell.
+ @discussion Creates a generic frame for a tab cell based on the current control state.
+ @returns Returns a basic rect for a tab cell.
+ */
+
+- (NSRect)genericCellRect
+{
+ NSRect aRect = [self frame];
+ aRect.origin.x = [style leftMarginForTabBarControl];
+ aRect.origin.y = 0.0;
+ aRect.size.width = [self availableCellWidth];
+ aRect.size.height = [style tabCellHeight];
+ return aRect;
+}
+
+#pragma mark -
+#pragma mark Constructor/destructor
+
+- (void)initAddedProperties
+{
+ _cells = [[NSMutableArray alloc] initWithCapacity:10];
+ _controller = [[PSMTabBarController alloc] initWithTabBarControl:self];
+ _animationTimer = nil;
+
+ // default config
+ _currentStep = kPSMIsNotBeingResized;
+ _orientation = PSMTabBarHorizontalOrientation;
+ _canCloseOnlyTab = NO;
+ _disableTabClose = NO;
+ _showAddTabButton = NO;
+ _hideForSingleTab = NO;
+ _sizeCellsToFit = NO;
+ _isHidden = NO;
+ _awakenedFromNib = NO;
+ _automaticallyAnimates = NO;
+ _useOverflowMenu = YES;
+ _allowsBackgroundTabClosing = YES;
+ _allowsResizing = NO;
+ _selectsTabsOnMouseDown = NO;
+ _alwaysShowActiveTab = NO;
+ _allowsScrubbing = NO;
+ _cellMinWidth = 100;
+ _cellMaxWidth = 280;
+ _cellOptimumWidth = 130;
+ _tearOffStyle = PSMTabBarTearOffAlphaWindow;
+ style = [[[[self class] defaultStyleClass] alloc] init];
+
+ // the overflow button/menu
+ NSRect overflowButtonRect = NSMakeRect([self frame].size.width - [style rightMarginForTabBarControl] + 1, 0, [style rightMarginForTabBarControl] - 1, [self frame].size.height);
+ _overflowPopUpButton = [[PSMOverflowPopUpButton alloc] initWithFrame:overflowButtonRect pullsDown:YES];
+ [_overflowPopUpButton setAutoresizingMask:NSViewNotSizable | NSViewMinXMargin];
+ [_overflowPopUpButton setHidden:YES];
+ [self addSubview:_overflowPopUpButton];
+ [self _positionOverflowMenu];
+
+ // new tab button
+ NSRect addTabButtonRect = NSMakeRect([self frame].size.width - [style rightMarginForTabBarControl] + 1, 3.0, 16.0, 16.0);
+ _addTabButton = [[PSMRolloverButton alloc] initWithFrame:addTabButtonRect];
+ if (_addTabButton) {
+ NSImage *newButtonImage = [style addTabButtonImage];
+ if (newButtonImage) {
+ [_addTabButton setUsualImage:newButtonImage];
+ }
+ newButtonImage = [style addTabButtonPressedImage];
+ if (newButtonImage) {
+ [_addTabButton setAlternateImage:newButtonImage];
+ }
+ newButtonImage = [style addTabButtonRolloverImage];
+ if (newButtonImage) {
+ [_addTabButton setRolloverImage:newButtonImage];
+ }
+ [_addTabButton setTitle:@""];
+ [_addTabButton setImagePosition:NSImageOnly];
+ [_addTabButton setButtonType:NSMomentaryChangeButton];
+ [_addTabButton setBordered:NO];
+ [_addTabButton setBezelStyle:NSShadowlessSquareBezelStyle];
+ [self addSubview:_addTabButton];
+
+ if (_showAddTabButton) {
+ [_addTabButton setHidden:NO];
+ } else {
+ [_addTabButton setHidden:YES];
+ }
+ [_addTabButton setNeedsDisplay:YES];
+ }
+}
+
++ (Class)defaultStyleClass
+{
+ return [PSMUnifiedTabStyle class];
+}
+
+- (id)initWithFrame:(NSRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self) {
+ // Initialization
+ [self initAddedProperties];
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:@"PSMTabBarControlItemPBType", nil]];
+
+ // resize
+ [self setPostsFrameChangedNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameDidChange:) name:NSViewFrameDidChangeNotification object:self];
+ }
+ [self setTarget:self];
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ //stop any animations that may be running
+ [_animationTimer invalidate];
+
+ [_showHideAnimationTimer invalidate];
+
+ //Also unwind the spring, if it's wound.
+ [_springTimer invalidate];
+
+ //unbind all the items to prevent crashing
+ //not sure if this is necessary or not
+ // http://code.google.com/p/maccode/issues/detail?id=35
+ NSEnumerator *enumerator = [[_cells copy] objectEnumerator];
+ PSMTabBarCell *nextCell;
+ while ((nextCell = [enumerator nextObject])) {
+ [self removeTabForCell:nextCell];
+ }
+
+ [self unregisterDraggedTypes];
+}
+
+- (void)awakeFromNib
+{
+ // build cells from existing tab view items
+ NSArray *existingItems = [tabView tabViewItems];
+ NSEnumerator *e = [existingItems objectEnumerator];
+ NSTabViewItem *item;
+ while ((item = [e nextObject])) {
+ if (![[self representedTabViewItems] containsObject:item]) {
+ [self addTabViewItem:item];
+ }
+ }
+}
+
+- (void)viewWillMoveToWindow:(NSWindow *)aWindow
+{
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+ [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
+ [center removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
+ [center removeObserver:self name:NSWindowDidUpdateNotification object:nil];
+ [center removeObserver:self name:NSWindowDidMoveNotification object:nil];
+
+ if (_showHideAnimationTimer) {
+ [_showHideAnimationTimer invalidate];
+ _showHideAnimationTimer = nil;
+ }
+
+ if (aWindow) {
+ [center addObserver:self selector:@selector(windowStatusDidChange:) name:NSWindowDidBecomeKeyNotification object:aWindow];
+ [center addObserver:self selector:@selector(windowStatusDidChange:) name:NSWindowDidResignKeyNotification object:aWindow];
+ [center addObserver:self selector:@selector(windowDidUpdate:) name:NSWindowDidUpdateNotification object:aWindow];
+ [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:aWindow];
+ }
+}
+
+- (void)windowStatusDidChange:(NSNotification *)notification
+{
+ [self setNeedsDisplay:YES];
+}
+
+#pragma mark -
+#pragma mark Accessors
+
+- (NSMutableArray *)cells
+{
+ return _cells;
+}
+
+- (NSEvent *)lastMouseDownEvent
+{
+ return _lastMouseDownEvent;
+}
+
+- (void)setLastMouseDownEvent:(NSEvent *)event
+{
+ _lastMouseDownEvent = event;
+}
+
+- (id)delegate
+{
+ return delegate;
+}
+
+- (void)setDelegate:(id)object
+{
+ delegate = object;
+
+ NSMutableArray *types = [NSMutableArray arrayWithObject:@"PSMTabBarControlItemPBType"];
+
+ //Update the allowed drag types
+ if ([self delegate] && [[self delegate] respondsToSelector:@selector(allowedDraggedTypesForTabView:)]) {
+ [types addObjectsFromArray:[[self delegate] allowedDraggedTypesForTabView:tabView]];
+ }
+ [self unregisterDraggedTypes];
+ [self registerForDraggedTypes:types];
+}
+
+- (NSTabView *)tabView
+{
+ return tabView;
+}
+
+- (void)setTabView:(NSTabView *)view
+{
+ tabView = view;
+}
+
+- (id<PSMTabStyle>)style
+{
+ return style;
+}
+
+- (NSString *)styleName
+{
+ return [style name];
+}
+
+- (void)setStyle:(id<PSMTabStyle>)newStyle
+{
+ if (style != newStyle) {
+
+ // restyle add tab button
+ if (_addTabButton) {
+ NSImage *newButtonImage = [style addTabButtonImage];
+ if (newButtonImage) {
+ [_addTabButton setUsualImage:newButtonImage];
+ }
+
+ newButtonImage = [style addTabButtonPressedImage];
+ if (newButtonImage) {
+ [_addTabButton setAlternateImage:newButtonImage];
+ }
+
+ newButtonImage = [style addTabButtonRolloverImage];
+ if (newButtonImage) {
+ [_addTabButton setRolloverImage:newButtonImage];
+ }
+ }
+
+ [self update];
+ }
+}
+
+- (void)setStyleNamed:(NSString *)name
+{
+
+ Class styleClass = NSClassFromString([NSString stringWithFormat:@"PSM%@TabStyle", [name capitalizedString]]);
+ if (styleClass == Nil) {
+ styleClass = object_getClass([PSMTabBarControl defaultStyleClass]);
+ }
+
+ id<PSMTabStyle> newStyle = [[styleClass alloc] init];
+ [self setStyle:newStyle];
+}
+
+- (PSMTabBarOrientation)orientation
+{
+ return _orientation;
+}
+
+- (void)setOrientation:(PSMTabBarOrientation)value
+{
+ PSMTabBarOrientation lastOrientation = _orientation;
+ _orientation = value;
+
+ if (_tabBarWidth < 10) {
+ _tabBarWidth = 120;
+ }
+
+ if (lastOrientation != _orientation) {
+ [[self style] setOrientation:_orientation];
+
+ [self _positionOverflowMenu]; //move the overflow popup button to the right place
+ [self update:NO];
+ }
+}
+
+- (BOOL)canCloseOnlyTab
+{
+ return _canCloseOnlyTab;
+}
+
+- (void)setCanCloseOnlyTab:(BOOL)value
+{
+ _canCloseOnlyTab = value;
+ if ([_cells count] == 1) {
+ [self update];
+ }
+}
+
+- (BOOL)disableTabClose
+{
+ return _disableTabClose;
+}
+
+- (void)setDisableTabClose:(BOOL)value
+{
+ _disableTabClose = value;
+ [self update];
+}
+
+- (BOOL)hideForSingleTab
+{
+ return _hideForSingleTab;
+}
+
+- (void)setHideForSingleTab:(BOOL)value
+{
+ _hideForSingleTab = value;
+ [self update];
+}
+
+- (BOOL)showAddTabButton
+{
+ return _showAddTabButton;
+}
+
+- (void)setShowAddTabButton:(BOOL)value
+{
+ _showAddTabButton = value;
+ if (!NSIsEmptyRect([_controller addButtonRect])) {
+ [_addTabButton setFrame:[_controller addButtonRect]];
+ }
+
+ [_addTabButton setHidden:!_showAddTabButton];
+ [_addTabButton setNeedsDisplay:YES];
+
+ [self update];
+}
+
+- (NSInteger)cellMinWidth
+{
+ return _cellMinWidth;
+}
+
+- (void)setCellMinWidth:(NSInteger)value
+{
+ _cellMinWidth = value;
+ [self update];
+}
+
+- (NSInteger)cellMaxWidth
+{
+ return _cellMaxWidth;
+}
+
+- (void)setCellMaxWidth:(NSInteger)value
+{
+ _cellMaxWidth = value;
+ [self update];
+}
+
+- (NSInteger)cellOptimumWidth
+{
+ return _cellOptimumWidth;
+}
+
+- (void)setCellOptimumWidth:(NSInteger)value
+{
+ _cellOptimumWidth = value;
+ [self update];
+}
+
+- (BOOL)sizeCellsToFit
+{
+ return _sizeCellsToFit;
+}
+
+- (void)setSizeCellsToFit:(BOOL)value
+{
+ _sizeCellsToFit = value;
+ [self update];
+}
+
+- (BOOL)useOverflowMenu
+{
+ return _useOverflowMenu;
+}
+
+- (void)setUseOverflowMenu:(BOOL)value
+{
+ _useOverflowMenu = value;
+ [self update];
+}
+
+- (PSMRolloverButton *)addTabButton
+{
+ return _addTabButton;
+}
+
+- (PSMOverflowPopUpButton *)overflowPopUpButton
+{
+ return _overflowPopUpButton;
+}
+
+- (BOOL)allowsBackgroundTabClosing
+{
+ return _allowsBackgroundTabClosing;
+}
+
+- (void)setAllowsBackgroundTabClosing:(BOOL)value
+{
+ _allowsBackgroundTabClosing = value;
+}
+
+- (BOOL)allowsResizing
+{
+ return _allowsResizing;
+}
+
+- (void)setAllowsResizing:(BOOL)value
+{
+ _allowsResizing = value;
+}
+
+- (BOOL)selectsTabsOnMouseDown
+{
+ return _selectsTabsOnMouseDown;
+}
+
+- (void)setSelectsTabsOnMouseDown:(BOOL)value
+{
+ _selectsTabsOnMouseDown = value;
+}
+
+- (BOOL)automaticallyAnimates
+{
+ return _automaticallyAnimates;
+}
+
+- (void)setAutomaticallyAnimates:(BOOL)value
+{
+ _automaticallyAnimates = value;
+}
+
+- (BOOL)alwaysShowActiveTab
+{
+ return _alwaysShowActiveTab;
+}
+
+- (void)setAlwaysShowActiveTab:(BOOL)value
+{
+ _alwaysShowActiveTab = value;
+}
+
+- (BOOL)allowsScrubbing
+{
+ return _allowsScrubbing;
+}
+
+- (void)setAllowsScrubbing:(BOOL)value
+{
+ _allowsScrubbing = value;
+}
+
+- (PSMTabBarTearOffStyle)tearOffStyle
+{
+ return _tearOffStyle;
+}
+
+- (void)setTearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle
+{
+ _tearOffStyle = tearOffStyle;
+}
+
+#pragma mark -
+#pragma mark Functionality
+
+- (void)addTabViewItem:(NSTabViewItem *)item
+{
+ // create cell
+ PSMTabBarCell *cell = [[PSMTabBarCell alloc] initWithControlView:self];
+ NSRect cellRect, lastCellFrame;
+ if ([_cells lastObject] != nil) {
+ cellRect = lastCellFrame = [[_cells lastObject] frame];
+ } else {
+ cellRect = lastCellFrame = NSZeroRect;
+ }
+
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ cellRect = [self genericCellRect];
+ cellRect.size.width = 30;
+ cellRect.origin.x = lastCellFrame.origin.x + lastCellFrame.size.width;
+ } else {
+ cellRect = /*lastCellFrame*/ [self genericCellRect];
+ cellRect.size.width = lastCellFrame.size.width;
+ cellRect.size.height = 0;
+ cellRect.origin.y = lastCellFrame.origin.y + lastCellFrame.size.height;
+ }
+
+ [cell setRepresentedObject:item];
+ [cell setFrame:cellRect];
+
+ // bind it up
+ [self bindPropertiesForCell:cell andTabViewItem:item];
+
+ // add to collection
+ [_cells addObject:cell];
+ if ([_cells count] == (NSUInteger)[tabView numberOfTabViewItems]) {
+ [self update]; // don't update unless all are accounted for!
+ }
+}
+
+- (void)removeTabForCell:(PSMTabBarCell *)cell
+{
+ NSTabViewItem *item = [cell representedObject];
+
+ // unbind
+ [[cell indicator] unbind:@"animate"];
+ [[cell indicator] unbind:@"hidden"];
+ [cell unbind:@"hasIcon"];
+ [cell unbind:@"hasLargeImage"];
+ [cell unbind:@"title"];
+ [cell unbind:@"count"];
+ [cell unbind:@"countColor"];
+ [cell unbind:@"isEdited"];
+
+ if ([item identifier] != nil) {
+ if ([[item identifier] respondsToSelector:@selector(isProcessing)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"isProcessing"];
+ }
+ }
+
+ if ([item identifier] != nil) {
+ if ([[item identifier] respondsToSelector:@selector(icon)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"icon"];
+ }
+ }
+
+ if ([item identifier] != nil) {
+ if ([[item identifier] respondsToSelector:@selector(objectCount)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"objectCount"];
+ }
+ }
+
+ if ([item identifier] != nil) {
+ if ([[item identifier] respondsToSelector:@selector(countColor)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"countColor"];
+ }
+ }
+
+ if ([item identifier] != nil) {
+ if ([[item identifier] respondsToSelector:@selector(largeImage)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"largeImage"];
+ }
+ }
+
+ if ([item identifier] != nil) {
+ if ([[item identifier] respondsToSelector:@selector(isEdited)]) {
+ [[item identifier] removeObserver:cell forKeyPath:@"isEdited"];
+ }
+ }
+
+ // stop watching identifier
+ [item removeObserver:self forKeyPath:@"identifier"];
+
+ // remove indicator
+ if ([[self subviews] containsObject:[cell indicator]]) {
+ [[cell indicator] removeFromSuperview];
+ }
+ // remove tracking
+ [[NSNotificationCenter defaultCenter] removeObserver:cell];
+
+ if ([cell closeButtonTrackingTag] != 0) {
+ [self removeTrackingRect:[cell closeButtonTrackingTag]];
+ [cell setCloseButtonTrackingTag:0];
+ }
+ if ([cell cellTrackingTag] != 0) {
+ [self removeTrackingRect:[cell cellTrackingTag]];
+ [cell setCellTrackingTag:0];
+ }
+
+ // pull from collection
+ [_cells removeObject:cell];
+
+ [self update];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ // did the tab's identifier change?
+ if ([keyPath isEqualToString:@"identifier"]) {
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while ((cell = [e nextObject])) {
+ if ([cell representedObject] == object) {
+ [self _bindPropertiesForCell:cell andTabViewItem:object];
+ }
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Hide/Show
+
+- (void)hideTabBar:(BOOL)hide animate:(BOOL)animate
+{
+ if (!_awakenedFromNib || (_isHidden && hide) || (!_isHidden && !hide) || (_currentStep != kPSMIsNotBeingResized)) {
+ return;
+ }
+
+ [[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
+
+ _isHidden = hide;
+ _currentStep = 0;
+ if (!animate) {
+ _currentStep = (NSInteger)kPSMHideAnimationSteps;
+ }
+
+ if (hide) {
+ [_overflowPopUpButton removeFromSuperview];
+ [_addTabButton removeFromSuperview];
+ } else if (!animate) {
+ [self addSubview:_overflowPopUpButton];
+ [self addSubview:_addTabButton];
+ }
+
+ CGFloat partnerOriginalSize, partnerOriginalOrigin, myOriginalSize, myOriginalOrigin, partnerTargetSize, partnerTargetOrigin, myTargetSize, myTargetOrigin;
+
+ // target values for partner
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ // current (original) values
+ myOriginalSize = [self frame].size.height;
+ myOriginalOrigin = [self frame].origin.y;
+ if (partnerView) {
+ partnerOriginalSize = [partnerView frame].size.height;
+ partnerOriginalOrigin = [partnerView frame].origin.y;
+ } else {
+ partnerOriginalSize = [[self window] frame].size.height;
+ partnerOriginalOrigin = [[self window] frame].origin.y;
+ }
+
+ if (partnerView) {
+ // above or below me?
+ if ((myOriginalOrigin - 22) > partnerOriginalOrigin) {
+ // partner is below me
+ if (_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin + 21;
+ myTargetSize = myOriginalSize - 21;
+ partnerTargetOrigin = partnerOriginalOrigin;
+ partnerTargetSize = partnerOriginalSize + 21;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin - 21;
+ myTargetSize = myOriginalSize + 21;
+ partnerTargetOrigin = partnerOriginalOrigin;
+ partnerTargetSize = partnerOriginalSize - 21;
+ }
+ } else {
+ // partner is above me
+ if (_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize - 21;
+ partnerTargetOrigin = partnerOriginalOrigin - 21;
+ partnerTargetSize = partnerOriginalSize + 21;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize + 21;
+ partnerTargetOrigin = partnerOriginalOrigin + 21;
+ partnerTargetSize = partnerOriginalSize - 21;
+ }
+ }
+ } else {
+ // for window movement
+ if (_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize - 21;
+ partnerTargetOrigin = partnerOriginalOrigin + 21;
+ partnerTargetSize = partnerOriginalSize - 21;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize + 21;
+ partnerTargetOrigin = partnerOriginalOrigin - 21;
+ partnerTargetSize = partnerOriginalSize + 21;
+ }
+ }
+ } else { /* vertical */
+ // current (original) values
+ myOriginalSize = [self frame].size.width;
+ myOriginalOrigin = [self frame].origin.x;
+ if (partnerView) {
+ partnerOriginalSize = [partnerView frame].size.width;
+ partnerOriginalOrigin = [partnerView frame].origin.x;
+ } else {
+ partnerOriginalSize = [[self window] frame].size.width;
+ partnerOriginalOrigin = [[self window] frame].origin.x;
+ }
+
+ if (partnerView) {
+ //to the left or right?
+ if (myOriginalOrigin < partnerOriginalOrigin + partnerOriginalSize) {
+ // partner is to the left
+ if (_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = 1;
+ partnerTargetOrigin = partnerOriginalOrigin - myOriginalSize + 1;
+ partnerTargetSize = partnerOriginalSize + myOriginalSize - 1;
+ _tabBarWidth = myOriginalSize;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = myOriginalSize + _tabBarWidth;
+ partnerTargetOrigin = partnerOriginalOrigin + _tabBarWidth;
+ partnerTargetSize = partnerOriginalSize - _tabBarWidth;
+ }
+ } else {
+ // partner is to the right
+ if (_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin + myOriginalSize;
+ myTargetSize = 1;
+ partnerTargetOrigin = partnerOriginalOrigin;
+ partnerTargetSize = partnerOriginalSize + myOriginalSize;
+ _tabBarWidth = myOriginalSize;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin - _tabBarWidth;
+ myTargetSize = myOriginalSize + _tabBarWidth;
+ partnerTargetOrigin = partnerOriginalOrigin;
+ partnerTargetSize = partnerOriginalSize - _tabBarWidth;
+ }
+ }
+ } else {
+ // for window movement
+ if (_isHidden) {
+ // I'm shrinking
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = 1;
+ partnerTargetOrigin = partnerOriginalOrigin + myOriginalSize - 1;
+ partnerTargetSize = partnerOriginalSize - myOriginalSize + 1;
+ _tabBarWidth = myOriginalSize;
+ } else {
+ // I'm growing
+ myTargetOrigin = myOriginalOrigin;
+ myTargetSize = _tabBarWidth;
+ partnerTargetOrigin = partnerOriginalOrigin - _tabBarWidth + 1;
+ partnerTargetSize = partnerOriginalSize + _tabBarWidth - 1;
+ }
+ }
+
+ if (!_isHidden && [[self delegate] respondsToSelector:@selector(desiredWidthForVerticalTabBar:)]) {
+ myTargetSize = [[self delegate] desiredWidthForVerticalTabBar:self];
+ }
+ }
+
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:myOriginalOrigin], @"myOriginalOrigin", [NSNumber numberWithDouble:partnerOriginalOrigin], @"partnerOriginalOrigin", [NSNumber numberWithDouble:myOriginalSize], @"myOriginalSize", [NSNumber numberWithDouble:partnerOriginalSize], @"partnerOriginalSize", [NSNumber numberWithDouble:myTargetOrigin], @"myTargetOrigin", [NSNumber numberWithDouble:partnerTargetOrigin], @"partnerTargetOrigin", [NSNumber numberWithDouble:myTargetSize], @"myTargetSize", [NSNumber numberWithDouble:partnerTargetSize], @"partnerTargetSize", nil];
+ if (_showHideAnimationTimer) {
+ [_showHideAnimationTimer invalidate];
+ }
+ _showHideAnimationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 30.0) target:self selector:@selector(animateShowHide:) userInfo:userInfo repeats:YES];
+}
+
+- (void)animateShowHide:(NSTimer *)timer
+{
+ // moves the frame of the tab bar and window (or partner view) linearly to hide or show the tab bar
+ NSRect myFrame = [self frame];
+ NSDictionary *userInfo = [timer userInfo];
+ CGFloat myCurrentOrigin = ([[userInfo objectForKey:@"myOriginalOrigin"] doubleValue] + (([[userInfo objectForKey:@"myTargetOrigin"] doubleValue] - [[userInfo objectForKey:@"myOriginalOrigin"] doubleValue]) * (_currentStep / kPSMHideAnimationSteps)));
+ CGFloat myCurrentSize = ([[userInfo objectForKey:@"myOriginalSize"] doubleValue] + (([[userInfo objectForKey:@"myTargetSize"] doubleValue] - [[userInfo objectForKey:@"myOriginalSize"] doubleValue]) * (_currentStep / kPSMHideAnimationSteps)));
+ CGFloat partnerCurrentOrigin = ([[userInfo objectForKey:@"partnerOriginalOrigin"] doubleValue] + (([[userInfo objectForKey:@"partnerTargetOrigin"] doubleValue] - [[userInfo objectForKey:@"partnerOriginalOrigin"] doubleValue]) * (_currentStep / kPSMHideAnimationSteps)));
+ CGFloat partnerCurrentSize = ([[userInfo objectForKey:@"partnerOriginalSize"] doubleValue] + (([[userInfo objectForKey:@"partnerTargetSize"] doubleValue] - [[userInfo objectForKey:@"partnerOriginalSize"] doubleValue]) * (_currentStep / kPSMHideAnimationSteps)));
+
+ NSRect myNewFrame;
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ myNewFrame = NSMakeRect(myFrame.origin.x, myCurrentOrigin, myFrame.size.width, myCurrentSize);
+ } else {
+ myNewFrame = NSMakeRect(myCurrentOrigin, myFrame.origin.y, myCurrentSize, myFrame.size.height);
+ }
+
+ if (partnerView) {
+ // resize self and view
+ NSRect resizeRect;
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ resizeRect = NSMakeRect([partnerView frame].origin.x, partnerCurrentOrigin, [partnerView frame].size.width, partnerCurrentSize);
+ } else {
+ resizeRect = NSMakeRect(partnerCurrentOrigin, [partnerView frame].origin.y, partnerCurrentSize, [partnerView frame].size.height);
+ }
+ [partnerView setFrame:resizeRect];
+ [partnerView setNeedsDisplay:YES];
+ [self setFrame:myNewFrame];
+ } else {
+ // resize self and window
+ NSRect resizeRect;
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ resizeRect = NSMakeRect([[self window] frame].origin.x, partnerCurrentOrigin, [[self window] frame].size.width, partnerCurrentSize);
+ } else {
+ resizeRect = NSMakeRect(partnerCurrentOrigin, [[self window] frame].origin.y, partnerCurrentSize, [[self window] frame].size.height);
+ }
+ [[self window] setFrame:resizeRect display:YES];
+ [self setFrame:myNewFrame];
+ }
+
+ // next
+ _currentStep++;
+ if (_currentStep == kPSMHideAnimationSteps + 1) {
+ _currentStep = kPSMIsNotBeingResized;
+ [self viewDidEndLiveResize];
+ [self update:NO];
+
+ //send the delegate messages
+ if (_isHidden) {
+ if ([[self delegate] respondsToSelector:@selector(tabView:tabBarDidHide:)]) {
+ [[self delegate] tabView:[self tabView] tabBarDidHide:self];
+ }
+ } else {
+ [self addSubview:_overflowPopUpButton];
+ [self addSubview:_addTabButton];
+
+ if ([[self delegate] respondsToSelector:@selector(tabView:tabBarDidUnhide:)]) {
+ [[self delegate] tabView:[self tabView] tabBarDidUnhide:self];
+ }
+ }
+
+ [_showHideAnimationTimer invalidate];
+ _showHideAnimationTimer = nil;
+ }
+ [[self window] display];
+}
+
+- (BOOL)isTabBarHidden
+{
+ return _isHidden;
+}
+
+- (BOOL)isAnimating
+{
+ return _animationTimer != nil;
+}
+
+- (id)partnerView
+{
+ return partnerView;
+}
+
+- (void)setPartnerView:(id)view
+{
+ partnerView = view;
+}
+
+#pragma mark -
+#pragma mark Drawing
+
+- (BOOL)isFlipped
+{
+ return YES;
+}
+
+- (void)drawRect:(NSRect)rect
+{
+ [style drawTabBar:self inRect:rect];
+}
+
+- (void)update
+{
+ [self update:_automaticallyAnimates];
+}
+
+- (void)update:(BOOL)animate
+{
+ // make sure all of our tabs are accounted for before updating
+ if ((NSUInteger)[[self tabView] numberOfTabViewItems] != [_cells count]) {
+ return;
+ }
+
+ // hide/show? (these return if already in desired state)
+ if ((_hideForSingleTab) && ([_cells count] <= 1)) {
+ [self hideTabBar:YES animate:YES];
+ return;
+ } else {
+ [self hideTabBar:NO animate:YES];
+ }
+
+ [self removeAllToolTips];
+ [_controller layoutCells]; //eventually we should only have to call this when we know something has changed
+
+ PSMTabBarCell *currentCell;
+
+ NSMenu *overflowMenu = [_controller overflowMenu];
+ [_overflowPopUpButton setHidden:(overflowMenu == nil)];
+ [_overflowPopUpButton setMenu:overflowMenu];
+
+ [_animationTimer invalidate];
+ _animationTimer = nil;
+
+ if (animate) {
+ NSMutableArray *targetFrames = [NSMutableArray arrayWithCapacity:[_cells count]];
+
+ for (NSUInteger i = 0; i < [_cells count]; i++) {
+ currentCell = [_cells objectAtIndex:i];
+
+ //we're going from NSRect -> NSValue -> NSRect -> NSValue here - oh well
+ [targetFrames addObject:[NSValue valueWithRect:[_controller cellFrameAtIndex:i]]];
+ }
+
+ [_addTabButton setHidden:!_showAddTabButton];
+
+ NSAnimation *animation = [[NSAnimation alloc] initWithDuration:0.50 animationCurve:NSAnimationEaseInOut];
+ [animation setAnimationBlockingMode:NSAnimationNonblocking];
+ [animation startAnimation];
+ _animationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0
+ target:self
+ selector:@selector(_animateCells:)
+ userInfo:[NSArray arrayWithObjects:targetFrames, animation, nil]
+ repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode];
+ [self _animateCells:_animationTimer];
+ } else {
+ for (NSUInteger i = 0; i < [_cells count]; i++) {
+ currentCell = [_cells objectAtIndex:i];
+ [currentCell setFrame:[_controller cellFrameAtIndex:i]];
+
+ if (![currentCell isInOverflowMenu]) {
+ [self _setupTrackingRectsForCell:currentCell];
+ }
+ }
+
+ [_addTabButton setFrame:[_controller addButtonRect]];
+ [_addTabButton setHidden:!_showAddTabButton];
+ [self setNeedsDisplay:YES];
+ }
+}
+
+- (void)_animateCells:(NSTimer *)timer
+{
+ NSAnimation *animation = [[timer userInfo] objectAtIndex:1];
+ NSArray *targetFrames = [[timer userInfo] objectAtIndex:0];
+ PSMTabBarCell *currentCell;
+ NSUInteger cellCount = [_cells count];
+
+ if ((cellCount > 0) && [animation isAnimating]) {
+ //compare our target position with the current position and move towards the target
+ for (NSUInteger i = 0; i < [targetFrames count] && i < cellCount; i++) {
+ currentCell = [_cells objectAtIndex:i];
+ NSRect cellFrame = [currentCell frame], targetFrame = [[targetFrames objectAtIndex:i] rectValue];
+ CGFloat sizeChange;
+ CGFloat originChange;
+
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ sizeChange = (targetFrame.size.width - cellFrame.size.width) * [animation currentProgress];
+ originChange = (targetFrame.origin.x - cellFrame.origin.x) * [animation currentProgress];
+ cellFrame.size.width += sizeChange;
+ cellFrame.origin.x += originChange;
+ } else {
+ sizeChange = (targetFrame.size.height - cellFrame.size.height) * [animation currentProgress];
+ originChange = (targetFrame.origin.y - cellFrame.origin.y) * [animation currentProgress];
+ cellFrame.size.height += sizeChange;
+ cellFrame.origin.y += originChange;
+ }
+
+ [currentCell setFrame:cellFrame];
+
+ //highlight the cell if the mouse is over it
+ NSPoint mousePoint = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:nil];
+ NSRect closeRect = [currentCell closeButtonRectForFrame:cellFrame];
+ [currentCell setHighlighted:NSMouseInRect(mousePoint, cellFrame, [self isFlipped])];
+ [currentCell setCloseButtonOver:NSMouseInRect(mousePoint, closeRect, [self isFlipped])];
+ }
+
+ if (_showAddTabButton) {
+ //animate the add tab button
+ NSRect target = [_controller addButtonRect], frame = [_addTabButton frame];
+ frame.origin.x += (target.origin.x - frame.origin.x) * [animation currentProgress];
+ [_addTabButton setFrame:frame];
+ }
+ } else {
+ //put all the cells where they should be in their final position
+ if (cellCount > 0) {
+ for (NSUInteger i = 0; i < [targetFrames count] && i < cellCount; i++) {
+ PSMTabBarCell *currentCell = [_cells objectAtIndex:i];
+ NSRect cellFrame = [currentCell frame], targetFrame = [[targetFrames objectAtIndex:i] rectValue];
+
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ cellFrame.size.width = targetFrame.size.width;
+ cellFrame.origin.x = targetFrame.origin.x;
+ } else {
+ cellFrame.size.height = targetFrame.size.height;
+ cellFrame.origin.y = targetFrame.origin.y;
+ }
+
+ [currentCell setFrame:cellFrame];
+
+ //highlight the cell if the mouse is over it
+ NSPoint mousePoint = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:nil];
+ NSRect closeRect = [currentCell closeButtonRectForFrame:cellFrame];
+ [currentCell setHighlighted:NSMouseInRect(mousePoint, cellFrame, [self isFlipped])];
+ [currentCell setCloseButtonOver:NSMouseInRect(mousePoint, closeRect, [self isFlipped])];
+ }
+ }
+
+ //set the frame for the add tab button
+ if (_showAddTabButton) {
+ NSRect frame = [_addTabButton frame];
+ frame.origin.x = [_controller addButtonRect].origin.x;
+ [_addTabButton setFrame:frame];
+ }
+
+ [_animationTimer invalidate];
+ _animationTimer = nil;
+
+ for (NSUInteger i = 0; i < cellCount; i++) {
+ currentCell = [_cells objectAtIndex:i];
+
+ //we've hit the cells that are in overflow, stop setting up tracking rects
+ if ([currentCell isInOverflowMenu]) {
+ break;
+ }
+
+ [self _setupTrackingRectsForCell:currentCell];
+ }
+ }
+
+ [self setNeedsDisplay:YES];
+}
+
+- (void)_setupTrackingRectsForCell:(PSMTabBarCell *)cell
+{
+ NSInteger tag, index = [_cells indexOfObject:cell];
+ NSRect cellTrackingRect = [_controller cellTrackingRectAtIndex:index];
+ NSPoint mousePoint = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:nil];
+ BOOL mouseInCell = NSMouseInRect(mousePoint, cellTrackingRect, [self isFlipped]);
+
+ //set the cell tracking rect
+ [self removeTrackingRect:[cell cellTrackingTag]];
+ tag = [self addTrackingRect:cellTrackingRect owner:cell userData:nil assumeInside:mouseInCell];
+ [cell setCellTrackingTag:tag];
+ [cell setHighlighted:mouseInCell];
+
+ if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ NSRect closeRect = [_controller closeButtonTrackingRectAtIndex:index];
+ BOOL mouseInCloseRect = NSMouseInRect(mousePoint, closeRect, [self isFlipped]);
+
+ //set the close button tracking rect
+ [self removeTrackingRect:[cell closeButtonTrackingTag]];
+ tag = [self addTrackingRect:closeRect owner:cell userData:nil assumeInside:mouseInCloseRect];
+ [cell setCloseButtonTrackingTag:tag];
+
+ [cell setCloseButtonOver:mouseInCloseRect];
+ }
+
+ //set the tooltip tracking rect
+ [self addToolTipRect:[cell frame] owner:self userData:nil];
+}
+
+- (void)_positionOverflowMenu
+{
+ NSRect cellRect, frame = [self frame];
+ cellRect.size.height = [style tabCellHeight];
+ cellRect.size.width = [style rightMarginForTabBarControl];
+
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ cellRect.origin.y = 0;
+ cellRect.origin.x = frame.size.width - [style rightMarginForTabBarControl] + (_resizeAreaCompensation ? -(_resizeAreaCompensation - 1) : 1);
+ [_overflowPopUpButton setAutoresizingMask:NSViewNotSizable | NSViewMinXMargin];
+ } else {
+ cellRect.origin.x = 0;
+ cellRect.origin.y = frame.size.height - [style tabCellHeight];
+ cellRect.size.width = frame.size.width;
+ [_overflowPopUpButton setAutoresizingMask:NSViewNotSizable | NSViewMinXMargin | NSViewMinYMargin];
+ }
+
+ [_overflowPopUpButton setFrame:cellRect];
+}
+
+- (void)_checkWindowFrame
+{
+ //figure out if the new frame puts the control in the way of the resize widget
+ NSWindow *window = [self window];
+
+ if (window) {
+ NSRect resizeWidgetFrame = [[window contentView] frame];
+ resizeWidgetFrame.origin.x += resizeWidgetFrame.size.width - 22;
+ resizeWidgetFrame.size.width = 22;
+ resizeWidgetFrame.size.height = 22;
+
+ if ([window showsResizeIndicator] && NSIntersectsRect([self frame], resizeWidgetFrame)) {
+ //the resize widgets are larger on metal windows
+ _resizeAreaCompensation = [window styleMask] & NSWindowStyleMaskTexturedBackground ? 20 : 8;
+ } else {
+ _resizeAreaCompensation = 0;
+ }
+
+ [self _positionOverflowMenu];
+ }
+}
+
+#pragma mark -
+#pragma mark Mouse Tracking
+
+- (BOOL)mouseDownCanMoveWindow
+{
+ return NO;
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
+{
+ return YES;
+}
+
+- (void)mouseDown:(NSEvent *)theEvent
+{
+ _didDrag = NO;
+
+ // keep for dragging
+ [self setLastMouseDownEvent:theEvent];
+ // what cell?
+ NSPoint mousePt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+ NSRect frame = [self frame];
+
+ if ([self orientation] == PSMTabBarVerticalOrientation && [self allowsResizing] && partnerView && (mousePt.x > frame.size.width - 3)) {
+ _resizing = YES;
+ }
+
+ NSRect cellFrame;
+ PSMTabBarCell *cell = [self cellForPoint:mousePt cellFrame:&cellFrame];
+ if (cell) {
+ BOOL overClose = NSMouseInRect(mousePt, [cell closeButtonRectForFrame:cellFrame], [self isFlipped]);
+ if (overClose && ![self disableTabClose] && ![cell isCloseButtonSuppressed] && ([self allowsBackgroundTabClosing] || [[cell representedObject] isEqualTo:[tabView selectedTabViewItem]] || [theEvent modifierFlags] & NSEventModifierFlagCommand)) {
+ [cell setCloseButtonOver:NO];
+ [cell setCloseButtonPressed:YES];
+ _closeClicked = YES;
+ } else {
+ [cell setCloseButtonPressed:NO];
+ if (_selectsTabsOnMouseDown) {
+ [self performSelector:@selector(tabClick:) withObject:cell];
+ }
+ }
+ [self setNeedsDisplay:YES];
+ }
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+ if ([self lastMouseDownEvent] == nil) {
+ return;
+ }
+
+ NSPoint currentPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+
+ if (_resizing) {
+ NSRect frame = [self frame];
+ CGFloat resizeAmount = [theEvent deltaX];
+ if ((currentPoint.x > frame.size.width && resizeAmount > 0) || (currentPoint.x < frame.size.width && resizeAmount < 0)) {
+ [[NSCursor resizeLeftRightCursor] push];
+
+ NSRect partnerFrame = [partnerView frame];
+
+ //do some bounds checking
+ if ((frame.size.width + resizeAmount > [self cellMinWidth]) && (frame.size.width + resizeAmount < [self cellMaxWidth])) {
+ frame.size.width += resizeAmount;
+ partnerFrame.size.width -= resizeAmount;
+ partnerFrame.origin.x += resizeAmount;
+
+ [self setFrame:frame];
+ [partnerView setFrame:partnerFrame];
+ [[self superview] setNeedsDisplay:YES];
+ }
+ }
+ return;
+ }
+
+ NSRect cellFrame;
+ NSPoint trackingStartPoint = [self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil];
+ PSMTabBarCell *cell = [self cellForPoint:trackingStartPoint cellFrame:&cellFrame];
+ if (cell) {
+ //check to see if the close button was the target in the clicked cell
+ //highlight/unhighlight the close button as necessary
+ NSRect iconRect = [cell closeButtonRectForFrame:cellFrame];
+
+ if (_closeClicked && NSMouseInRect(trackingStartPoint, iconRect, [self isFlipped]) && ([self allowsBackgroundTabClosing] || [[cell representedObject] isEqualTo:[tabView selectedTabViewItem]])) {
+ [cell setCloseButtonPressed:NSMouseInRect(currentPoint, iconRect, [self isFlipped])];
+ [self setNeedsDisplay:YES];
+ return;
+ }
+
+ CGFloat dx = fabs(currentPoint.x - trackingStartPoint.x);
+ CGFloat dy = fabs(currentPoint.y - trackingStartPoint.y);
+ CGFloat distance = sqrt(dx * dx + dy * dy);
+
+ if (distance >= 10 && !_didDrag && ![[PSMTabDragAssistant sharedDragAssistant] isDragging] &&
+ [self delegate] && [[self delegate] respondsToSelector:@selector(tabView:shouldDragTabViewItem:fromTabBar:)] &&
+ [[self delegate] tabView:tabView
+ shouldDragTabViewItem:[cell representedObject]
+ fromTabBar:self]) {
+ _didDrag = YES;
+ [[PSMTabDragAssistant sharedDragAssistant] startDraggingCell:cell fromTabBar:self withMouseDownEvent:[self lastMouseDownEvent]];
+ }
+ }
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+ if (_resizing) {
+ _resizing = NO;
+ [[NSCursor arrowCursor] set];
+ } else {
+ // what cell?
+ NSPoint mousePt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+ NSRect cellFrame, mouseDownCellFrame;
+ PSMTabBarCell *cell = [self cellForPoint:mousePt cellFrame:&cellFrame];
+ PSMTabBarCell *mouseDownCell = [self cellForPoint:[self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil] cellFrame:&mouseDownCellFrame];
+ if (cell) {
+ NSPoint trackingStartPoint = [self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil];
+ NSRect iconRect = [mouseDownCell closeButtonRectForFrame:mouseDownCellFrame];
+
+ if ((NSMouseInRect(mousePt, iconRect, [self isFlipped])) && ![self disableTabClose] && ![cell isCloseButtonSuppressed] && [mouseDownCell closeButtonPressed]) {
+ if (([[NSApp currentEvent] modifierFlags] & NSEventModifierFlagOption) != 0) {
+ //If the user is holding Option, close all other tabs
+ NSEnumerator *enumerator = [[[self cells] copy] objectEnumerator];
+ PSMTabBarCell *otherCell;
+
+ while ((otherCell = [enumerator nextObject])) {
+ if (otherCell != cell) {
+ [self performSelector:@selector(closeTabClick:) withObject:otherCell];
+ }
+ }
+
+ //Fix the close button for the clicked tab not to be pressed
+ [cell setCloseButtonPressed:NO];
+ } else {
+ //Otherwise, close this tab
+ [self performSelector:@selector(closeTabClick:) withObject:cell];
+ }
+ } else if (NSMouseInRect(mousePt, mouseDownCellFrame, [self isFlipped]) && (!NSMouseInRect(trackingStartPoint, [cell closeButtonRectForFrame:cellFrame], [self isFlipped]) || ![self allowsBackgroundTabClosing] || [self disableTabClose])) {
+ [mouseDownCell setCloseButtonPressed:NO];
+ // If -[self selectsTabsOnMouseDown] is TRUE, we already performed tabClick: on mouseDown.
+ if (![self selectsTabsOnMouseDown]) {
+ [self performSelector:@selector(tabClick:) withObject:cell];
+ }
+ } else {
+ [mouseDownCell setCloseButtonPressed:NO];
+ [self performSelector:@selector(tabNothing:) withObject:cell];
+ }
+ }
+
+ _closeClicked = NO;
+ }
+}
+
+- (NSMenu *)menuForEvent:(NSEvent *)event
+{
+ NSMenu *menu = nil;
+ NSTabViewItem *item = [[self cellForPoint:[self convertPoint:[event locationInWindow] fromView:nil] cellFrame:nil] representedObject];
+
+ if (item && [[self delegate] respondsToSelector:@selector(tabView:menuForTabViewItem:)]) {
+ menu = [[self delegate] tabView:tabView menuForTabViewItem:item];
+ }
+ return menu;
+}
+
+#pragma mark -
+#pragma mark Drag and Drop
+
+- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)theEvent
+{
+ return YES;
+}
+
+// NSDraggingSource
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
+{
+ return (isLocal ? NSDragOperationMove : NSDragOperationNone);
+}
+
+- (BOOL)ignoreModifierKeysWhileDragging
+{
+ return YES;
+}
+
+- (void)draggedImage:(NSImage *)anImage beganAt:(NSPoint)screenPoint
+{
+ [[PSMTabDragAssistant sharedDragAssistant] draggingBeganAt:screenPoint];
+}
+
+- (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenPoint
+{
+ [[PSMTabDragAssistant sharedDragAssistant] draggingMovedTo:screenPoint];
+}
+
+// NSDraggingDestination
+- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
+{
+ if ([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
+ if ([self delegate] && [[self delegate] respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] && ![[self delegate] tabView:[[sender draggingSource] tabView] shouldDropTabViewItem:[[[PSMTabDragAssistant sharedDragAssistant] draggedCell] representedObject] inTabBar:self]) {
+ return NSDragOperationNone;
+ }
+
+ [[PSMTabDragAssistant sharedDragAssistant] draggingEnteredTabBar:self atPoint:[self convertPoint:[sender draggingLocation] fromView:nil]];
+ return NSDragOperationMove;
+ }
+
+ return NSDragOperationNone;
+}
+
+- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
+{
+ PSMTabBarCell *cell = [self cellForPoint:[self convertPoint:[sender draggingLocation] fromView:nil] cellFrame:nil];
+
+ if ([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
+ if ([self delegate] && [[self delegate] respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] && ![[self delegate] tabView:[[sender draggingSource] tabView] shouldDropTabViewItem:[[[PSMTabDragAssistant sharedDragAssistant] draggedCell] representedObject] inTabBar:self]) {
+ return NSDragOperationNone;
+ }
+
+ [[PSMTabDragAssistant sharedDragAssistant] draggingUpdatedInTabBar:self atPoint:[self convertPoint:[sender draggingLocation] fromView:nil]];
+ return NSDragOperationMove;
+ } else if (cell) {
+ //something that was accepted by the delegate was dragged on
+
+ //Test for the space bar (the skip-the-delay key).
+ /*enum { virtualKeycodeForSpace = 49 }; //Source: IM:Tx (Fig. C-2)
+ union {
+ KeyMap keymap;
+ char bits[16];
+ } keymap;
+ GetKeys(keymap.keymap);
+ if ((GetCurrentEventKeyModifiers() == 0) && bit_test(keymap.bits, virtualKeycodeForSpace)) {
+ //The user pressed the space bar. This skips the delay; the user wants to pop the spring on this tab *now*.
+
+ //For some reason, it crashes if I call -fire here. I don't know why. It doesn't crash if I simply set the fire date to now.
+ [_springTimer setFireDate:[NSDate date]];
+ } else {*/
+ //Wind the spring for a spring-loaded drop.
+ //The delay time comes from Finder's defaults, which specifies it in milliseconds.
+ //If the delegate can't handle our spring-loaded drop, we'll abort it when the timer fires. See fireSpring:. This is simpler than constantly (checking for spring-loaded awareness and tearing down/rebuilding the timer) at every delegate change.
+
+ //If the user has dragged to a different tab, reset the timer.
+ if (_tabViewItemWithSpring != [cell representedObject]) {
+ [_springTimer invalidate];
+ _springTimer = nil;
+ _tabViewItemWithSpring = [cell representedObject];
+ }
+ if (!_springTimer) {
+ //Finder's default delay time, as of Tiger, is 668 ms. If the user has never changed it, there's no setting in its defaults, so we default to that amount.
+ NSNumber *delayNumber = (__bridge_transfer NSNumber *)CFPreferencesCopyAppValue((CFStringRef) @"SpringingDelayMilliseconds", (CFStringRef) @"com.apple.finder");
+ NSTimeInterval delaySeconds = delayNumber ? [delayNumber doubleValue] / 1000.0 : 0.668;
+ _springTimer = [NSTimer scheduledTimerWithTimeInterval:delaySeconds
+ target:self
+ selector:@selector(fireSpring:)
+ userInfo:sender
+ repeats:NO];
+ }
+ return NSDragOperationCopy;
+ }
+
+ return NSDragOperationNone;
+}
+
+- (void)draggingExited:(id<NSDraggingInfo>)sender
+{
+ [_springTimer invalidate];
+ _springTimer = nil;
+
+ [[PSMTabDragAssistant sharedDragAssistant] draggingExitedTabBar:self];
+}
+
+- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)sender
+{
+ //validate the drag operation only if there's a valid tab bar to drop into
+ return [[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] == NSNotFound ||
+ [[PSMTabDragAssistant sharedDragAssistant] destinationTabBar] != nil;
+}
+
+- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
+{
+ if ([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
+ [[PSMTabDragAssistant sharedDragAssistant] performDragOperation];
+ } else if ([self delegate] && [[self delegate] respondsToSelector:@selector(tabView:acceptedDraggingInfo:onTabViewItem:)]) {
+ //forward the drop to the delegate
+ [[self delegate] tabView:tabView acceptedDraggingInfo:sender onTabViewItem:[[self cellForPoint:[self convertPoint:[sender draggingLocation] fromView:nil] cellFrame:nil] representedObject]];
+ }
+ return YES;
+}
+
+- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
+{
+ [[PSMTabDragAssistant sharedDragAssistant] draggedImageEndedAt:aPoint operation:operation];
+}
+
+- (void)concludeDragOperation:(id<NSDraggingInfo>)sender
+{
+}
+
+#pragma mark -
+#pragma mark Spring-loading
+
+- (void)fireSpring:(NSTimer *)timer
+{
+ NSAssert1(timer == _springTimer, @"Spring fired by unrecognized timer %@", timer);
+
+ id<NSDraggingInfo> sender = [timer userInfo];
+ PSMTabBarCell *cell = [self cellForPoint:[self convertPoint:[sender draggingLocation] fromView:nil] cellFrame:nil];
+ [tabView selectTabViewItem:[cell representedObject]];
+
+ _tabViewItemWithSpring = nil;
+ [_springTimer invalidate];
+ _springTimer = nil;
+}
+
+#pragma mark -
+#pragma mark Actions
+
+- (void)overflowMenuAction:(id)sender
+{
+ NSTabViewItem *tabViewItem = (NSTabViewItem *)[sender representedObject];
+ [tabView selectTabViewItem:tabViewItem];
+}
+
+- (void)closeTabClick:(id)sender
+{
+ NSTabViewItem *item = [sender representedObject];
+ if (([_cells count] == 1) && (![self canCloseOnlyTab])) {
+ return;
+ }
+
+ if ([[self delegate] respondsToSelector:@selector(tabView:shouldCloseTabViewItem:)]) {
+ if (![[self delegate] tabView:tabView shouldCloseTabViewItem:item]) {
+ // fix mouse downed close button
+ [sender setCloseButtonPressed:NO];
+ return;
+ }
+ }
+
+ [tabView removeTabViewItem:item];
+}
+
+- (void)tabClick:(id)sender
+{
+ [tabView selectTabViewItem:[sender representedObject]];
+}
+
+- (void)tabNothing:(id)sender
+{
+ //[self update]; // takes care of highlighting based on state
+}
+
+- (void)frameDidChange:(NSNotification *)notification
+{
+ [self _checkWindowFrame];
+
+ // trying to address the drawing artifacts for the progress indicators - hackery follows
+ // this one fixes the "blanking" effect when the control hides and shows itself
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while ((cell = [e nextObject])) {
+ [[cell indicator] stopAnimation:self];
+
+ [[cell indicator] performSelector:@selector(startAnimation:)
+ withObject:nil
+ afterDelay:0];
+ }
+
+ [self update:NO];
+}
+
+- (void)viewDidMoveToWindow
+{
+ [self _checkWindowFrame];
+}
+
+- (void)viewWillStartLiveResize
+{
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while ((cell = [e nextObject])) {
+ [[cell indicator] stopAnimation:self];
+ }
+ [self setNeedsDisplay:YES];
+}
+
+- (void)viewDidEndLiveResize
+{
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while ((cell = [e nextObject])) {
+ [[cell indicator] startAnimation:self];
+ }
+
+ [self _checkWindowFrame];
+ [self update:NO];
+}
+
+- (void)resetCursorRects
+{
+ [super resetCursorRects];
+ if ([self orientation] == PSMTabBarVerticalOrientation) {
+ NSRect frame = [self frame];
+ [self addCursorRect:NSMakeRect(frame.size.width - 2, 0, 2, frame.size.height) cursor:[NSCursor resizeLeftRightCursor]];
+ }
+}
+
+- (void)windowDidMove:(NSNotification *)aNotification
+{
+ [self setNeedsDisplay:YES];
+}
+
+- (void)windowDidUpdate:(NSNotification *)notification
+{
+ // hide? must readjust things if I'm not supposed to be showing
+ // this block of code only runs when the app launches
+ if ([self hideForSingleTab] && ([_cells count] <= 1) && !_awakenedFromNib) {
+ // must adjust frames now before display
+ NSRect myFrame = [self frame];
+ if ([self orientation] == PSMTabBarHorizontalOrientation) {
+ if (partnerView) {
+ NSRect partnerFrame = [partnerView frame];
+ // above or below me?
+ if (myFrame.origin.y - 22 > [partnerView frame].origin.y) {
+ // partner is below me
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y + 21, myFrame.size.width, myFrame.size.height - 21)];
+ [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y, partnerFrame.size.width, partnerFrame.size.height + 21)];
+ } else {
+ // partner is above me
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, myFrame.size.width, myFrame.size.height - 21)];
+ [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y - 21, partnerFrame.size.width, partnerFrame.size.height + 21)];
+ }
+ [partnerView setNeedsDisplay:YES];
+ [self setNeedsDisplay:YES];
+ } else {
+ // for window movement
+ NSRect windowFrame = [[self window] frame];
+ [[self window] setFrame:NSMakeRect(windowFrame.origin.x, windowFrame.origin.y + 21, windowFrame.size.width, windowFrame.size.height - 21) display:YES];
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, myFrame.size.width, myFrame.size.height - 21)];
+ }
+ } else {
+ if (partnerView) {
+ NSRect partnerFrame = [partnerView frame];
+ //to the left or right?
+ if (myFrame.origin.x < [partnerView frame].origin.x) {
+ // partner is to the left
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, 1, myFrame.size.height)];
+ [partnerView setFrame:NSMakeRect(partnerFrame.origin.x - myFrame.size.width + 1, partnerFrame.origin.y, partnerFrame.size.width + myFrame.size.width - 1, partnerFrame.size.height)];
+ } else {
+ // partner to the right
+ [self setFrame:NSMakeRect(myFrame.origin.x + myFrame.size.width, myFrame.origin.y, 1, myFrame.size.height)];
+ [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y, partnerFrame.size.width + myFrame.size.width, partnerFrame.size.height)];
+ }
+ _tabBarWidth = myFrame.size.width;
+ [partnerView setNeedsDisplay:YES];
+ [self setNeedsDisplay:YES];
+ } else {
+ // for window movement
+ NSRect windowFrame = [[self window] frame];
+ [[self window] setFrame:NSMakeRect(windowFrame.origin.x + myFrame.size.width - 1, windowFrame.origin.y, windowFrame.size.width - myFrame.size.width + 1, windowFrame.size.height) display:YES];
+ [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, 1, myFrame.size.height)];
+ }
+ }
+
+ _isHidden = YES;
+
+ if ([[self delegate] respondsToSelector:@selector(tabView:tabBarDidHide:)]) {
+ [[self delegate] tabView:[self tabView] tabBarDidHide:self];
+ }
+ }
+
+ _awakenedFromNib = YES;
+ [self setNeedsDisplay:YES];
+
+ //we only need to do this once
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidUpdateNotification object:nil];
+}
+
+#pragma mark -
+#pragma mark Menu Validation
+
+- (BOOL)validateMenuItem:(NSMenuItem *)sender
+{
+ [sender setState:([[sender representedObject] isEqualTo:[tabView selectedTabViewItem]]) ? NSOnState : NSOffState];
+
+ return [[self delegate] respondsToSelector:@selector(tabView:validateOverflowMenuItem:forTabViewItem:)] ? [[self delegate] tabView:[self tabView] validateOverflowMenuItem:sender forTabViewItem:[sender representedObject]] : YES;
+}
+
+#pragma mark -
+#pragma mark NSTabView Delegate
+
+- (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
+{
+ // here's a weird one - this message is sent before the "tabViewDidChangeNumberOfTabViewItems"
+ // message, thus I can end up updating when there are no cells, if no tabs were (yet) present
+ NSUInteger tabIndex = [aTabView indexOfTabViewItem:tabViewItem];
+
+ if ([_cells count] > 0 && tabIndex < [_cells count]) {
+ PSMTabBarCell *thisCell = [_cells objectAtIndex:tabIndex];
+ if (_alwaysShowActiveTab && [thisCell isInOverflowMenu]) {
+ //temporarily disable the delegate in order to move the tab to a different index
+ id tempDelegate = [aTabView delegate];
+ [aTabView setDelegate:nil];
+
+ // move it all around first
+ [aTabView removeTabViewItem:tabViewItem];
+ [aTabView insertTabViewItem:tabViewItem atIndex:0];
+ [_cells removeObjectAtIndex:tabIndex];
+ [_cells insertObject:thisCell atIndex:0];
+ [thisCell setIsInOverflowMenu:NO]; //very important else we get a fun recursive loop going
+ [[_cells objectAtIndex:[_cells count] - 1] setIsInOverflowMenu:YES]; //these 2 lines are pretty uncool and this logic needs to be updated
+
+ [aTabView setDelegate:tempDelegate];
+
+ //reset the selection since removing it changed the selection
+ [aTabView selectTabViewItem:tabViewItem];
+
+ [self update];
+ } else {
+ [_controller setSelectedCell:thisCell];
+ [self setNeedsDisplay:YES];
+ }
+ }
+
+ if ([[self delegate] respondsToSelector:@selector(tabView:didSelectTabViewItem:)]) {
+ [[self delegate] performSelector:@selector(tabView:didSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
+ }
+}
+
+- (BOOL)tabView:(NSTabView *)aTabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem
+{
+ if ([[self delegate] respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)]) {
+ return [[self delegate] tabView:aTabView shouldSelectTabViewItem:tabViewItem];
+ } else {
+ return YES;
+ }
+}
+- (void)tabView:(NSTabView *)aTabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem
+{
+ if ([[self delegate] respondsToSelector:@selector(tabView:willSelectTabViewItem:)]) {
+ [[self delegate] performSelector:@selector(tabView:willSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
+ }
+}
+
+- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)aTabView
+{
+ NSArray *tabItems = [tabView tabViewItems];
+ // go through cells, remove any whose representedObjects are not in [tabView tabViewItems]
+ NSEnumerator *e = [[_cells copy] objectEnumerator];
+ PSMTabBarCell *cell;
+ while ((cell = [e nextObject])) {
+ //remove the observer binding
+ if ([cell representedObject] && ![tabItems containsObject:[cell representedObject]]) {
+ if ([[self delegate] respondsToSelector:@selector(tabView:didCloseTabViewItem:)]) {
+ [[self delegate] tabView:aTabView didCloseTabViewItem:[cell representedObject]];
+ }
+
+ [self removeTabForCell:cell];
+ }
+ }
+
+ // go through tab view items, add cell for any not present
+ NSMutableArray *cellItems = [self representedTabViewItems];
+ NSEnumerator *ex = [tabItems objectEnumerator];
+ NSTabViewItem *item;
+ while ((item = [ex nextObject])) {
+ if (![cellItems containsObject:item]) {
+ [self addTabViewItem:item];
+ }
+ }
+
+ // pass along for other delegate responses
+ if ([[self delegate] respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)]) {
+ [[self delegate] performSelector:@selector(tabViewDidChangeNumberOfTabViewItems:) withObject:aTabView];
+ }
+
+ // reset cursor tracking for the add tab button if one exists
+ if ([self addTabButton]) {
+ [[self addTabButton] resetCursorRects];
+ }
+}
+
+#pragma mark -
+#pragma mark Tooltips
+
+- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)userData
+{
+ if ([[self delegate] respondsToSelector:@selector(tabView:toolTipForTabViewItem:)]) {
+ return [[self delegate] tabView:[self tabView] toolTipForTabViewItem:[[self cellForPoint:point cellFrame:nil] representedObject]];
+ }
+ return nil;
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [super encodeWithCoder:aCoder];
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:_cells forKey:@"PSMcells"];
+ [aCoder encodeObject:tabView forKey:@"PSMtabView"];
+ [aCoder encodeObject:_overflowPopUpButton forKey:@"PSMoverflowPopUpButton"];
+ [aCoder encodeObject:_addTabButton forKey:@"PSMaddTabButton"];
+ [aCoder encodeObject:style forKey:@"PSMstyle"];
+ [aCoder encodeInteger:_orientation forKey:@"PSMorientation"];
+ [aCoder encodeBool:_canCloseOnlyTab forKey:@"PSMcanCloseOnlyTab"];
+ [aCoder encodeBool:_disableTabClose forKey:@"PSMdisableTabClose"];
+ [aCoder encodeBool:_hideForSingleTab forKey:@"PSMhideForSingleTab"];
+ [aCoder encodeBool:_allowsBackgroundTabClosing forKey:@"PSMallowsBackgroundTabClosing"];
+ [aCoder encodeBool:_allowsResizing forKey:@"PSMallowsResizing"];
+ [aCoder encodeBool:_selectsTabsOnMouseDown forKey:@"PSMselectsTabsOnMouseDown"];
+ [aCoder encodeBool:_showAddTabButton forKey:@"PSMshowAddTabButton"];
+ [aCoder encodeBool:_sizeCellsToFit forKey:@"PSMsizeCellsToFit"];
+ [aCoder encodeInteger:_cellMinWidth forKey:@"PSMcellMinWidth"];
+ [aCoder encodeInteger:_cellMaxWidth forKey:@"PSMcellMaxWidth"];
+ [aCoder encodeInteger:_cellOptimumWidth forKey:@"PSMcellOptimumWidth"];
+ [aCoder encodeInteger:_currentStep forKey:@"PSMcurrentStep"];
+ [aCoder encodeBool:_isHidden forKey:@"PSMisHidden"];
+ [aCoder encodeObject:partnerView forKey:@"PSMpartnerView"];
+ [aCoder encodeBool:_awakenedFromNib forKey:@"PSMawakenedFromNib"];
+ [aCoder encodeObject:_lastMouseDownEvent forKey:@"PSMlastMouseDownEvent"];
+ [aCoder encodeObject:delegate forKey:@"PSMdelegate"];
+ [aCoder encodeBool:_useOverflowMenu forKey:@"PSMuseOverflowMenu"];
+ [aCoder encodeBool:_automaticallyAnimates forKey:@"PSMautomaticallyAnimates"];
+ [aCoder encodeBool:_alwaysShowActiveTab forKey:@"PSMalwaysShowActiveTab"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super initWithCoder:aDecoder];
+ if (self) {
+ // Initialization
+ [self initAddedProperties];
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:@"PSMTabBarControlItemPBType", nil]];
+
+ // resize
+ [self setPostsFrameChangedNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameDidChange:) name:NSViewFrameDidChangeNotification object:self];
+ if ([aDecoder allowsKeyedCoding]) {
+ _cells = [aDecoder decodeObjectForKey:@"PSMcells"];
+ tabView = [aDecoder decodeObjectForKey:@"PSMtabView"];
+ _overflowPopUpButton = [aDecoder decodeObjectForKey:@"PSMoverflowPopUpButton"];
+ _addTabButton = [aDecoder decodeObjectForKey:@"PSMaddTabButton"];
+ style = [aDecoder decodeObjectForKey:@"PSMstyle"];
+ _orientation = (PSMTabBarOrientation)[aDecoder decodeIntegerForKey:@"PSMorientation"];
+ _canCloseOnlyTab = [aDecoder decodeBoolForKey:@"PSMcanCloseOnlyTab"];
+ _disableTabClose = [aDecoder decodeBoolForKey:@"PSMdisableTabClose"];
+ _hideForSingleTab = [aDecoder decodeBoolForKey:@"PSMhideForSingleTab"];
+ _allowsBackgroundTabClosing = [aDecoder decodeBoolForKey:@"PSMallowsBackgroundTabClosing"];
+ _allowsResizing = [aDecoder decodeBoolForKey:@"PSMallowsResizing"];
+ _selectsTabsOnMouseDown = [aDecoder decodeBoolForKey:@"PSMselectsTabsOnMouseDown"];
+ _showAddTabButton = [aDecoder decodeBoolForKey:@"PSMshowAddTabButton"];
+ _sizeCellsToFit = [aDecoder decodeBoolForKey:@"PSMsizeCellsToFit"];
+ _cellMinWidth = [aDecoder decodeIntegerForKey:@"PSMcellMinWidth"];
+ _cellMaxWidth = [aDecoder decodeIntegerForKey:@"PSMcellMaxWidth"];
+ _cellOptimumWidth = [aDecoder decodeIntegerForKey:@"PSMcellOptimumWidth"];
+ _currentStep = [aDecoder decodeIntegerForKey:@"PSMcurrentStep"];
+ _isHidden = [aDecoder decodeBoolForKey:@"PSMisHidden"];
+ partnerView = [aDecoder decodeObjectForKey:@"PSMpartnerView"];
+ _awakenedFromNib = [aDecoder decodeBoolForKey:@"PSMawakenedFromNib"];
+ _lastMouseDownEvent = [aDecoder decodeObjectForKey:@"PSMlastMouseDownEvent"];
+ _useOverflowMenu = [aDecoder decodeBoolForKey:@"PSMuseOverflowMenu"];
+ _automaticallyAnimates = [aDecoder decodeBoolForKey:@"PSMautomaticallyAnimates"];
+ _alwaysShowActiveTab = [aDecoder decodeBoolForKey:@"PSMalwaysShowActiveTab"];
+ delegate = [aDecoder decodeObjectForKey:@"PSMdelegate"];
+ }
+ }
+ [self setTarget:self];
+ return self;
+}
+
+#pragma mark -
+#pragma mark IB Palette
+
+- (NSSize)minimumFrameSizeFromKnobPosition:(NSInteger)position
+{
+ return NSMakeSize(100.0, 22.0);
+}
+
+- (NSSize)maximumFrameSizeFromKnobPosition:(NSInteger)knobPosition
+{
+ return NSMakeSize(10000.0, 22.0);
+}
+
+- (void)placeView:(NSRect)newFrame
+{
+ // this is called any time the view is resized in IB
+ [self setFrame:newFrame];
+ [self update:NO];
+}
+
+#pragma mark -
+#pragma mark Convenience
+
+- (void)bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item
+{
+ [self _bindPropertiesForCell:cell andTabViewItem:item];
+
+ // watch for changes in the identifier
+ [item addObserver:self forKeyPath:@"identifier" options:0 context:nil];
+}
+
+- (void)_bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item
+{
+ // bind the indicator to the represented object's status (if it exists)
+ [[cell indicator] setHidden:YES];
+ if ([item identifier] != nil) {
+ if ([[[cell representedObject] identifier] respondsToSelector:@selector(isProcessing)]) {
+ NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
+ [bindingOptions setObject:NSNegateBooleanTransformerName forKey:@"NSValueTransformerName"];
+ [[cell indicator] bind:@"animate" toObject:[item identifier] withKeyPath:@"isProcessing" options:nil];
+ [[cell indicator] bind:@"hidden" toObject:[item identifier] withKeyPath:@"isProcessing" options:bindingOptions];
+ [[item identifier] addObserver:cell forKeyPath:@"isProcessing" options:0 context:nil];
+ }
+ }
+
+ // bind for the existence of an icon
+ [cell setHasIcon:NO];
+ if ([item identifier] != nil) {
+ if ([[[cell representedObject] identifier] respondsToSelector:@selector(icon)]) {
+ NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
+ [bindingOptions setObject:NSIsNotNilTransformerName forKey:@"NSValueTransformerName"];
+ [cell bind:@"hasIcon" toObject:[item identifier] withKeyPath:@"icon" options:bindingOptions];
+ [[item identifier] addObserver:cell forKeyPath:@"icon" options:0 context:nil];
+ }
+ }
+
+ // bind for the existence of a counter
+ [cell setCount:0];
+ if ([item identifier] != nil) {
+ if ([[[cell representedObject] identifier] respondsToSelector:@selector(objectCount)]) {
+ [cell bind:@"count" toObject:[item identifier] withKeyPath:@"objectCount" options:nil];
+ [[item identifier] addObserver:cell forKeyPath:@"objectCount" options:0 context:nil];
+ }
+ }
+
+ // bind for the color of a counter
+ [cell setCountColor:nil];
+ if ([item identifier] != nil) {
+ if ([[[cell representedObject] identifier] respondsToSelector:@selector(countColor)]) {
+ [cell bind:@"countColor" toObject:[item identifier] withKeyPath:@"countColor" options:nil];
+ [[item identifier] addObserver:cell forKeyPath:@"countColor" options:0 context:nil];
+ }
+ }
+
+ // bind for a large image
+ [cell setHasLargeImage:NO];
+ if ([item identifier] != nil) {
+ if ([[[cell representedObject] identifier] respondsToSelector:@selector(largeImage)]) {
+ NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
+ [bindingOptions setObject:NSIsNotNilTransformerName forKey:@"NSValueTransformerName"];
+ [cell bind:@"hasLargeImage" toObject:[item identifier] withKeyPath:@"largeImage" options:bindingOptions];
+ [[item identifier] addObserver:cell forKeyPath:@"largeImage" options:0 context:nil];
+ }
+ }
+
+ [cell setIsEdited:NO];
+ if ([item identifier] != nil) {
+ if ([[[cell representedObject] identifier] respondsToSelector:@selector(isEdited)]) {
+ [cell bind:@"isEdited" toObject:[item identifier] withKeyPath:@"isEdited" options:nil];
+ [[item identifier] addObserver:cell forKeyPath:@"isEdited" options:0 context:nil];
+ }
+ }
+
+ // bind my string value to the label on the represented tab
+ [cell bind:@"title" toObject:item withKeyPath:@"label" options:nil];
+}
+
+- (NSMutableArray *)representedTabViewItems
+{
+ NSMutableArray *temp = [NSMutableArray arrayWithCapacity:[_cells count]];
+ NSEnumerator *e = [_cells objectEnumerator];
+ PSMTabBarCell *cell;
+ while ((cell = [e nextObject])) {
+ if ([cell representedObject]) {
+ [temp addObject:[cell representedObject]];
+ }
+ }
+ return temp;
+}
+
+- (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame
+{
+ if ([self orientation] == PSMTabBarHorizontalOrientation && !NSPointInRect(point, [self genericCellRect])) {
+ return nil;
+ }
+
+ NSInteger i, cnt = [_cells count];
+ for (i = 0; i < cnt; i++) {
+ PSMTabBarCell *cell = [_cells objectAtIndex:i];
+
+ if (NSPointInRect(point, [cell frame])) {
+ if (outFrame) {
+ *outFrame = [cell frame];
+ }
+ return cell;
+ }
+ }
+ return nil;
+}
+
+- (PSMTabBarCell *)lastVisibleTab
+{
+ NSInteger i, cellCount = [_cells count];
+ for (i = 0; i < cellCount; i++) {
+ if ([[_cells objectAtIndex:i] isInOverflowMenu]) {
+ return [_cells objectAtIndex:(i - 1)];
+ }
+ }
+ return [_cells objectAtIndex:(cellCount - 1)];
+}
+
+- (NSInteger)numberOfVisibleTabs
+{
+ NSUInteger i, cellCount = 0;
+ PSMTabBarCell *nextCell;
+
+ for (i = 0; i < [_cells count]; i++) {
+ nextCell = [_cells objectAtIndex:i];
+
+ if ([nextCell isInOverflowMenu]) {
+ break;
+ }
+
+ if (![nextCell isPlaceholder]) {
+ cellCount++;
+ }
+ }
+
+ return cellCount;
+}
+
+#pragma mark -
+#pragma mark Accessibility
+
+- (BOOL)accessibilityIsIgnored
+{
+ return NO;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute
+{
+ id attributeValue = nil;
+ if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
+ attributeValue = NSAccessibilityGroupRole;
+ } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+ attributeValue = NSAccessibilityUnignoredChildren(_cells);
+ } else {
+ attributeValue = [super accessibilityAttributeValue:attribute];
+ }
+ return attributeValue;
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ id hitTestResult = self;
+
+ NSEnumerator *enumerator = [_cells objectEnumerator];
+ PSMTabBarCell *cell = nil;
+ PSMTabBarCell *highlightedCell = nil;
+
+ while (!highlightedCell && (cell = [enumerator nextObject])) {
+ if ([cell isHighlighted]) {
+ highlightedCell = cell;
+ }
+ }
+
+ if (highlightedCell) {
+ hitTestResult = [highlightedCell accessibilityHitTest:point];
+ }
+
+ return hitTestResult;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarController.h b/frontends/cocoa/PSMTabBarControl/PSMTabBarController.h
new file mode 100644
index 0000000..80c94df
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarController.h
@@ -0,0 +1,38 @@
+//
+// PSMTabBarController.h
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 11/24/06.
+// Copyright 2006 Kent Sutherland. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class PSMTabBarControl, PSMTabBarCell;
+
+@interface PSMTabBarController : NSObject
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+ <NSMenuDelegate>
+#endif
+{
+ PSMTabBarControl *_control;
+ NSMutableArray *_cellTrackingRects;
+ NSMutableArray *_closeButtonTrackingRects;
+ NSMutableArray *_cellFrames;
+ NSRect _addButtonRect;
+ NSMenu *_overflowMenu;
+}
+
+- (id)initWithTabBarControl:(PSMTabBarControl *)control;
+
+- (NSRect)addButtonRect;
+- (NSMenu *)overflowMenu;
+- (NSRect)cellTrackingRectAtIndex:(NSUInteger)index;
+- (NSRect)closeButtonTrackingRectAtIndex:(NSUInteger)index;
+- (NSRect)cellFrameAtIndex:(NSUInteger)index;
+
+- (void)setSelectedCell:(PSMTabBarCell *)cell;
+
+- (void)layoutCells;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabBarController.m b/frontends/cocoa/PSMTabBarControl/PSMTabBarController.m
new file mode 100644
index 0000000..232bb49
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabBarController.m
@@ -0,0 +1,648 @@
+//
+// PSMTabBarController.m
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 11/24/06.
+// Copyright 2006 Kent Sutherland. All rights reserved.
+//
+
+#import "PSMTabBarController.h"
+#import "PSMTabBarControl.h"
+#import "PSMTabBarCell.h"
+#import "PSMTabStyle.h"
+#import "NSString_AITruncation.h"
+#import "PSMRolloverButton.h"
+
+#define MAX_OVERFLOW_MENUITEM_TITLE_LENGTH 60
+
+@interface PSMTabBarController (Private)
+- (NSArray *)_generateWidthsFromCells:(NSArray *)cells;
+- (void)_setupCells:(NSArray *)cells withWidths:(NSArray *)widths;
+@end
+
+@implementation PSMTabBarController
+
+/*!
+ @method initWithTabBarControl:
+ @abstract Creates a new PSMTabBarController instance.
+ @discussion Creates a new PSMTabBarController for controlling a PSMTabBarControl. Should only be called by
+ PSMTabBarControl.
+ @param A PSMTabBarControl.
+ @returns A newly created PSMTabBarController instance.
+ */
+
+- (id)initWithTabBarControl:(PSMTabBarControl *)control
+{
+ if ((self = [super init])) {
+ _control = control;
+ _cellTrackingRects = [[NSMutableArray alloc] init];
+ _closeButtonTrackingRects = [[NSMutableArray alloc] init];
+ _cellFrames = [[NSMutableArray alloc] init];
+ _addButtonRect = NSZeroRect;
+ }
+ return self;
+}
+
+/*!
+ @method addButtonRect
+ @abstract Returns the position for the add tab button.
+ @discussion Returns the position for the add tab button.
+ @returns The rect for the add button rect.
+ */
+
+- (NSRect)addButtonRect
+{
+ return _addButtonRect;
+}
+
+/*!
+ @method overflowMenu
+ @abstract Returns current overflow menu or nil if there is none.
+ @discussion Returns current overflow menu or nil if there is none.
+ @returns The current overflow menu.
+ */
+
+- (NSMenu *)overflowMenu
+{
+ return _overflowMenu;
+}
+
+/*!
+ @method cellTrackingRectAtIndex:
+ @abstract Returns the rect for the tracking rect at the requested index.
+ @discussion Returns the rect for the tracking rect at the requested index.
+ @param Index of a cell.
+ @returns The tracking rect of the cell at the requested index.
+ */
+
+- (NSRect)cellTrackingRectAtIndex:(NSUInteger)index
+{
+ NSRect rect;
+ if (index < [_cellTrackingRects count]) {
+ rect = [[_cellTrackingRects objectAtIndex:index] rectValue];
+ } else {
+ NSLog(@"cellTrackingRectAtIndex: Invalid index (%ld)", (long)index);
+ rect = NSZeroRect;
+ }
+ return rect;
+}
+
+/*!
+ @method closeButtonTrackingRectAtIndex:
+ @abstract Returns the tracking rect for the close button at the requested index.
+ @discussion Returns the tracking rect for the close button at the requested index.
+ @param Index of a cell.
+ @returns The close button tracking rect of the cell at the requested index.
+ */
+
+- (NSRect)closeButtonTrackingRectAtIndex:(NSUInteger)index
+{
+ NSRect rect;
+ if (index < [_closeButtonTrackingRects count]) {
+ rect = [[_closeButtonTrackingRects objectAtIndex:index] rectValue];
+ } else {
+ NSLog(@"closeButtonTrackingRectAtIndex: Invalid index (%ld)", (long)index);
+ rect = NSZeroRect;
+ }
+ return rect;
+}
+
+/*!
+ @method cellFrameAtIndex:
+ @abstract Returns the frame for the cell at the requested index.
+ @discussion Returns the frame for the cell at the requested index.
+ @param Index of a cell.
+ @returns The frame of the cell at the requested index.
+ */
+
+- (NSRect)cellFrameAtIndex:(NSUInteger)index
+{
+ NSRect rect;
+
+ if (index < [_cellFrames count]) {
+ rect = [[_cellFrames objectAtIndex:index] rectValue];
+ } else {
+ NSLog(@"cellFrameAtIndex: Invalid index (%ld)", (long)index);
+ rect = NSZeroRect;
+ }
+ return rect;
+}
+
+/*!
+ @method setSelectedCell:
+ @abstract Changes the cell states so the given cell is the currently selected cell.
+ @discussion Makes the given cell the active cell and properly recalculates the tab states for surrounding cells.
+ @param An instance of PSMTabBarCell to make active.
+ */
+
+- (void)setSelectedCell:(PSMTabBarCell *)cell
+{
+ NSArray *cells = [_control cells];
+ NSEnumerator *enumerator = [cells objectEnumerator];
+ PSMTabBarCell *lastCell = nil, *nextCell;
+
+ //deselect the previously selected tab
+ while ((nextCell = [enumerator nextObject]) && ([nextCell state] == NSOffState)) {
+ lastCell = nextCell;
+ }
+
+ [nextCell setState:NSOffState];
+ [nextCell setTabState:PSMTab_PositionMiddleMask];
+
+ if (lastCell && lastCell != [_control lastVisibleTab]) {
+ [lastCell setTabState:~[lastCell tabState] & PSMTab_RightIsSelectedMask];
+ }
+
+ if ((nextCell = [enumerator nextObject])) {
+ [nextCell setTabState:~[lastCell tabState] & PSMTab_LeftIsSelectedMask];
+ }
+
+ [cell setState:NSOnState];
+ [cell setTabState:PSMTab_SelectedMask];
+
+ if (![cell isInOverflowMenu]) {
+ NSUInteger cellIndex = [cells indexOfObject:cell];
+
+ if (cellIndex > 0) {
+ nextCell = [cells objectAtIndex:cellIndex - 1];
+ [nextCell setTabState:[nextCell tabState] | PSMTab_RightIsSelectedMask];
+ }
+
+ if (cellIndex < [cells count] - 1) {
+ nextCell = [cells objectAtIndex:cellIndex + 1];
+ [nextCell setTabState:[nextCell tabState] | PSMTab_LeftIsSelectedMask];
+ }
+ }
+}
+
+/*!
+ @method layoutCells
+ @abstract Recalculates cell positions and states.
+ @discussion This method calculates the proper frame, tabState and overflow menu status for all cells in the
+ tab bar control.
+ */
+
+- (void)layoutCells
+{
+ NSArray *cells = [_control cells];
+ NSInteger cellCount = [cells count];
+
+ // make sure all of our tabs are accounted for before updating
+ if ([[_control tabView] numberOfTabViewItems] != cellCount) {
+ return;
+ }
+
+ [_cellTrackingRects removeAllObjects];
+ [_closeButtonTrackingRects removeAllObjects];
+ [_cellFrames removeAllObjects];
+
+ NSArray *cellWidths = [self _generateWidthsFromCells:cells];
+ [self _setupCells:cells withWidths:cellWidths];
+
+ //set up the rect from the add tab button
+ _addButtonRect = [_control genericCellRect];
+ _addButtonRect.size = [[_control addTabButton] frame].size;
+ if ([_control orientation] == PSMTabBarHorizontalOrientation) {
+ _addButtonRect.origin.y = MARGIN_Y;
+ _addButtonRect.origin.x += [[cellWidths valueForKeyPath:@"@sum.floatValue"] doubleValue] + 2;
+ } else {
+ _addButtonRect.origin.x = 0;
+ _addButtonRect.origin.y = [[cellWidths lastObject] doubleValue];
+ }
+}
+
+/*!
+ * @method _shrinkWidths:towardMinimum:withAvailableWidth:
+ * @abstract Decreases widths in an array toward a minimum until they fit within availableWidth, if possible
+ * @param An array of NSNumbers
+ * @param The target minimum
+ * @param The maximum available width
+ * @returns The amount by which the total array width was shrunk
+ */
+- (NSInteger)_shrinkWidths:(NSMutableArray *)newWidths towardMinimum:(NSInteger)minimum withAvailableWidth:(CGFloat)availableWidth
+{
+ BOOL changed = NO;
+ NSInteger count = [newWidths count];
+ NSInteger totalWidths = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue];
+ NSInteger originalTotalWidths = totalWidths;
+
+ do {
+ changed = NO;
+
+ for (NSInteger q = (count - 1); q >= 0; q--) {
+ CGFloat cellWidth = [[newWidths objectAtIndex:q] doubleValue];
+ if (cellWidth - 1 >= minimum) {
+ cellWidth--;
+ totalWidths--;
+
+ [newWidths replaceObjectAtIndex:q
+ withObject:[NSNumber numberWithDouble:cellWidth]];
+
+ changed = YES;
+ }
+ }
+ } while (changed && (totalWidths > availableWidth));
+
+ return (originalTotalWidths - totalWidths);
+}
+
+/*!
+ * @function potentialMinimumForArray()
+ * @abstract Calculate the minimum total for a given array of widths
+ * @discussion The array is summed using, for each item, the minimum between the current value and the passed minimum value.
+ * This is useful for getting a sum if the array has size-to-fit widths which will be allowed to be less than the
+ * specified minimum.
+ * @param An array of widths
+ * @param The minimum
+ * @returns The smallest possible sum for the array
+ */
+static NSInteger potentialMinimumForArray(NSArray *array, NSInteger minimum)
+{
+ NSInteger runningTotal = 0;
+ NSInteger count = [array count];
+
+ for (NSInteger i = 0; i < count; i++) {
+ NSInteger currentValue = [[array objectAtIndex:i] integerValue];
+ runningTotal += MIN(currentValue, minimum);
+ }
+
+ return runningTotal;
+}
+
+/*!
+ @method _generateWidthsFromCells:
+ @abstract Calculates the width of cells that would be visible.
+ @discussion Calculates the width of cells in the tab bar and returns an array of widths for the cells that would be
+ visible. Uses large blocks of code that were previously in PSMTabBarControl's update method.
+ @param An array of PSMTabBarCells.
+ @returns An array of numbers representing the widths of cells that would be visible.
+ */
+
+- (NSArray *)_generateWidthsFromCells:(NSArray *)cells
+{
+ NSInteger cellCount = [cells count], i, numberOfVisibleCells = ([_control orientation] == PSMTabBarHorizontalOrientation) ? 1 : 0;
+ NSMutableArray *newWidths = [NSMutableArray arrayWithCapacity:cellCount];
+ id<PSMTabStyle> style = [_control style];
+ CGFloat availableWidth = [_control availableCellWidth], currentOrigin = 0, totalOccupiedWidth = 0.0, width;
+ NSRect cellRect = [_control genericCellRect], controlRect = [_control frame];
+ PSMTabBarCell *currentCell;
+
+ if ([_control orientation] == PSMTabBarVerticalOrientation) {
+ currentOrigin = [style topMarginForTabBarControl];
+ }
+
+ //Don't let cells overlap the add tab button if it is visible
+ if ([_control showAddTabButton]) {
+ availableWidth -= [self addButtonRect].size.width;
+ }
+
+ for (i = 0; i < cellCount; i++) {
+ currentCell = [cells objectAtIndex:i];
+
+ // supress close button?
+ [currentCell setCloseButtonSuppressed:((cellCount == 1 && [_control canCloseOnlyTab] == NO) ||
+ [_control disableTabClose] || ([[_control delegate] respondsToSelector:@selector(tabView:disableTabCloseForTabViewItem:)] && [[_control delegate] tabView:[_control tabView]
+ disableTabCloseForTabViewItem:[currentCell representedObject]]))];
+
+ if ([_control orientation] == PSMTabBarHorizontalOrientation) {
+ // Determine cell width
+ if ([_control sizeCellsToFit]) {
+ width = [currentCell desiredWidthOfCell];
+ if (width > [_control cellMaxWidth]) {
+ width = [_control cellMaxWidth];
+ }
+ } else {
+ width = [_control cellOptimumWidth];
+ }
+
+ width = ceil(width);
+
+ //check to see if there is not enough space to place all tabs as preferred
+ if (totalOccupiedWidth + width >= availableWidth) {
+ //There's not enough space to add currentCell at its preferred width!
+
+ //If we're not going to use the overflow menu, cram all the tab cells into the bar regardless of minimum width
+ if (![_control useOverflowMenu]) {
+ NSInteger j, averageWidth = (availableWidth / cellCount);
+
+ numberOfVisibleCells = cellCount;
+ [newWidths removeAllObjects];
+
+ for (j = 0; j < cellCount; j++) {
+ CGFloat desiredWidth = [[cells objectAtIndex:j] desiredWidthOfCell];
+ [newWidths addObject:[NSNumber numberWithDouble:(desiredWidth < averageWidth && [_control sizeCellsToFit]) ? desiredWidth : averageWidth]];
+ }
+
+ totalOccupiedWidth = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue];
+ break;
+ }
+
+ //We'll be using the overflow menu if needed.
+ numberOfVisibleCells = i;
+ if ([_control sizeCellsToFit]) {
+ BOOL remainingCellsMustGoToOverflow = NO;
+
+ totalOccupiedWidth = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue];
+
+ /* Can I squeeze it in without violating min cell width? This is the width we would take up
+ * if every cell so far were at the control minimum size (or their current size if that is less than the control minimum).
+ */
+ if ((potentialMinimumForArray(newWidths, [_control cellMinWidth]) + MIN(width, [_control cellMinWidth])) <= availableWidth) {
+ /* It's definitely possible for cells so far to be visible.
+ * Shrink other cells to allow this one to fit
+ */
+ NSInteger cellMinWidth = [_control cellMinWidth];
+
+ /* Start off adding it to the array; we know that it will eventually fit because
+ * (the potential minimum <= availableWidth)
+ *
+ * This allows average and minimum aggregates on the NSArray to work.
+ */
+ [newWidths addObject:[NSNumber numberWithDouble:width]];
+ numberOfVisibleCells++;
+
+ totalOccupiedWidth += width;
+
+ //First, try to shrink tabs toward the average. Tabs smaller than average won't change
+ totalOccupiedWidth -= [self _shrinkWidths:newWidths
+ towardMinimum:[[newWidths valueForKeyPath:@"@avg.intValue"] integerValue]
+ withAvailableWidth:availableWidth];
+
+ if (totalOccupiedWidth > availableWidth) {
+ //Next, shrink tabs toward the smallest of the existing tabs. The smallest tab won't change.
+ NSInteger smallestTabWidth = [[newWidths valueForKeyPath:@"@min.intValue"] integerValue];
+ if (smallestTabWidth > cellMinWidth) {
+ totalOccupiedWidth -= [self _shrinkWidths:newWidths
+ towardMinimum:smallestTabWidth
+ withAvailableWidth:availableWidth];
+ }
+ }
+
+ if (totalOccupiedWidth > availableWidth) {
+ //Finally, shrink tabs toward the imposed minimum size. All tabs larger than the minimum wll change.
+ totalOccupiedWidth -= [self _shrinkWidths:newWidths
+ towardMinimum:cellMinWidth
+ withAvailableWidth:availableWidth];
+ }
+
+ if (totalOccupiedWidth > availableWidth) {
+ NSLog(@"**** -[PSMTabBarController generateWidthsFromCells:] This is a failure (available %f, total %f, width is %f)",
+ availableWidth, totalOccupiedWidth, width);
+ remainingCellsMustGoToOverflow = YES;
+ }
+
+ if (totalOccupiedWidth < availableWidth) {
+ /* We're not using all available space not but exceeded available width before;
+ * stretch all cells to fully fit the bar
+ */
+ NSInteger leftoverWidth = availableWidth - totalOccupiedWidth;
+ if (leftoverWidth > 0) {
+ NSInteger q;
+ for (q = numberOfVisibleCells - 1; q >= 0; q--) {
+ NSInteger desiredAddition = (NSInteger)leftoverWidth / (q + 1);
+ NSInteger newCellWidth = (NSInteger)[[newWidths objectAtIndex:q] doubleValue] + desiredAddition;
+ [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:newCellWidth]];
+ leftoverWidth -= desiredAddition;
+ totalOccupiedWidth += desiredAddition;
+ }
+ }
+ }
+ } else {
+ // stretch - distribute leftover room among cells, since we can't add this cell
+ NSInteger leftoverWidth = availableWidth - totalOccupiedWidth;
+ NSInteger q;
+ for (q = i - 1; q >= 0; q--) {
+ NSInteger desiredAddition = (NSInteger)leftoverWidth / (q + 1);
+ NSInteger newCellWidth = (NSInteger)[[newWidths objectAtIndex:q] doubleValue] + desiredAddition;
+ [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:newCellWidth]];
+ leftoverWidth -= desiredAddition;
+ }
+
+ remainingCellsMustGoToOverflow = YES;
+ }
+
+ // done assigning widths; remaining cells go in overflow menu
+ if (remainingCellsMustGoToOverflow) {
+ break;
+ }
+ } else {
+ //We're not using size-to-fit
+ NSInteger revisedWidth = availableWidth / (i + 1);
+ if (revisedWidth >= [_control cellMinWidth]) {
+ NSUInteger q;
+ totalOccupiedWidth = 0;
+
+ for (q = 0; q < [newWidths count]; q++) {
+ [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:revisedWidth]];
+ totalOccupiedWidth += revisedWidth;
+ }
+ // just squeezed this one in...
+ [newWidths addObject:[NSNumber numberWithDouble:revisedWidth]];
+ totalOccupiedWidth += revisedWidth;
+ numberOfVisibleCells++;
+ } else {
+ // couldn't fit that last one...
+ break;
+ }
+ }
+ } else {
+ //(totalOccupiedWidth < availableWidth)
+ numberOfVisibleCells = cellCount;
+ [newWidths addObject:[NSNumber numberWithDouble:width]];
+ totalOccupiedWidth += width;
+ }
+ } else {
+ //lay out vertical tabs
+ if (currentOrigin + cellRect.size.height <= controlRect.size.height) {
+ [newWidths addObject:[NSNumber numberWithDouble:currentOrigin]];
+ numberOfVisibleCells++;
+ currentOrigin += cellRect.size.height;
+ } else {
+ //out of room, the remaining tabs go into overflow
+ if ([newWidths count] > 0 && controlRect.size.height - currentOrigin < 17) {
+ [newWidths removeLastObject];
+ numberOfVisibleCells--;
+ }
+ break;
+ }
+ }
+ }
+
+ //make sure there are at least two items in the horizontal tab bar
+ if ([_control orientation] == PSMTabBarHorizontalOrientation) {
+ if (numberOfVisibleCells<2 && [cells count]> 1) {
+ PSMTabBarCell *cell1 = [cells objectAtIndex:0], *cell2 = [cells objectAtIndex:1];
+ NSNumber *cellWidth;
+
+ [newWidths removeAllObjects];
+ totalOccupiedWidth = 0;
+
+ cellWidth = [NSNumber numberWithDouble:[cell1 desiredWidthOfCell] < availableWidth * 0.5f ? [cell1 desiredWidthOfCell] : availableWidth * 0.5f];
+ [newWidths addObject:cellWidth];
+ totalOccupiedWidth += [cellWidth doubleValue];
+
+ cellWidth = [NSNumber numberWithDouble:[cell2 desiredWidthOfCell] < (availableWidth - totalOccupiedWidth) ? [cell2 desiredWidthOfCell] : (availableWidth - totalOccupiedWidth)];
+ [newWidths addObject:cellWidth];
+ totalOccupiedWidth += [cellWidth doubleValue];
+
+ if (totalOccupiedWidth < availableWidth) {
+ [newWidths replaceObjectAtIndex:0 withObject:[NSNumber numberWithDouble:availableWidth - [cellWidth doubleValue]]];
+ }
+
+ numberOfVisibleCells = 2;
+ }
+ }
+
+ return newWidths;
+}
+
+/*!
+ @method _setupCells:withWidths
+ @abstract Creates tracking rect arrays and sets the frames of the visible cells.
+ @discussion Creates tracking rect arrays and sets the cells given in the widths array.
+ */
+
+- (void)_setupCells:(NSArray *)cells withWidths:(NSArray *)widths
+{
+ NSUInteger i, tabState, cellCount = [cells count];
+ NSRect cellRect = [_control genericCellRect];
+ PSMTabBarCell *cell;
+ NSTabViewItem *selectedTabViewItem = [[_control tabView] selectedTabViewItem];
+ NSMenuItem *menuItem;
+
+ _overflowMenu = nil;
+
+ for (i = 0; i < cellCount; i++) {
+ cell = [cells objectAtIndex:i];
+
+ if (i < [widths count]) {
+ tabState = 0;
+
+ // set cell frame
+ if ([_control orientation] == PSMTabBarHorizontalOrientation) {
+ cellRect.size.width = [[widths objectAtIndex:i] doubleValue];
+ } else {
+ cellRect.size.width = [_control frame].size.width;
+ cellRect.origin.y = [[widths objectAtIndex:i] doubleValue];
+ cellRect.origin.x = 0;
+ }
+
+ [_cellFrames addObject:[NSValue valueWithRect:cellRect]];
+
+ //add tracking rects to arrays
+ [_closeButtonTrackingRects addObject:[NSValue valueWithRect:[cell closeButtonRectForFrame:cellRect]]];
+ [_cellTrackingRects addObject:[NSValue valueWithRect:cellRect]];
+
+ if ([[cell representedObject] isEqualTo:selectedTabViewItem]) {
+ [cell setState:NSOnState];
+ tabState |= PSMTab_SelectedMask;
+ // previous cell
+ if (i > 0) {
+ [[cells objectAtIndex:i - 1] setTabState:([(PSMTabBarCell *)[cells objectAtIndex:i - 1] tabState] | PSMTab_RightIsSelectedMask)];
+ }
+ // next cell - see below
+ } else {
+ [cell setState:NSOffState];
+ // see if prev cell was selected
+ if ((i > 0) && ([[cells objectAtIndex:i - 1] state] == NSOnState)) {
+ tabState |= PSMTab_LeftIsSelectedMask;
+ }
+ }
+
+ // more tab states
+ if ([widths count] == 1) {
+ tabState |= PSMTab_PositionLeftMask | PSMTab_PositionRightMask | PSMTab_PositionSingleMask;
+ } else if (i == 0) {
+ tabState |= PSMTab_PositionLeftMask;
+ } else if (i == [widths count] - 1) {
+ tabState |= PSMTab_PositionRightMask;
+ }
+
+ [cell setTabState:tabState];
+ [cell setIsInOverflowMenu:NO];
+
+ // indicator
+ if (![[cell indicator] isHidden] && ![_control isTabBarHidden]) {
+ if (![[_control subviews] containsObject:[cell indicator]]) {
+ [_control addSubview:[cell indicator]];
+ [[cell indicator] startAnimation:self];
+ }
+ }
+
+ // next...
+ cellRect.origin.x += [[widths objectAtIndex:i] doubleValue];
+ } else {
+ [cell setState:NSOffState];
+ [cell setIsInOverflowMenu:YES];
+ [[cell indicator] removeFromSuperview];
+
+ //position the cell well offscreen
+ if ([_control orientation] == PSMTabBarHorizontalOrientation) {
+ cellRect.origin.x += [[_control style] rightMarginForTabBarControl] + 20;
+ } else {
+ cellRect.origin.y = [_control frame].size.height + 2;
+ }
+
+ [_cellFrames addObject:[NSValue valueWithRect:cellRect]];
+
+ if (_overflowMenu == nil) {
+ _overflowMenu = [[NSMenu alloc] init];
+ [_overflowMenu insertItemWithTitle:@"" action:nil keyEquivalent:@"" atIndex:0]; // Because the overflowPupUpButton is a pull down menu
+ [_overflowMenu setDelegate:self];
+ }
+
+ // Each item's title is limited to 60 characters. If more than 60 characters, use an ellipsis to indicate that more exists.
+ menuItem = [_overflowMenu addItemWithTitle:[[[cell attributedStringValue] string] stringWithEllipsisByTruncatingToLength:MAX_OVERFLOW_MENUITEM_TITLE_LENGTH]
+ action:@selector(overflowMenuAction:)
+ keyEquivalent:@""];
+ [menuItem setTarget:_control];
+ [menuItem setRepresentedObject:[cell representedObject]];
+
+ if ([cell count] > 0) {
+ [menuItem setTitle:[[menuItem title] stringByAppendingFormat:@" (%lu)", (unsigned long)[cell count]]];
+ }
+ }
+ }
+}
+
+- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)menuItem atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
+{
+ if (menu == _overflowMenu) {
+ if ([[[menuItem representedObject] identifier] respondsToSelector:@selector(icon)]) {
+ [menuItem setImage:[[[menuItem representedObject] identifier] valueForKey:@"icon"]];
+ }
+ }
+
+ return TRUE;
+}
+
+- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
+{
+ if (menu == _overflowMenu) {
+ return [_overflowMenu numberOfItems];
+ } else {
+ NSLog(@"Warning: Unexpected menu delegate call for menu %@", menu);
+ return 0;
+ }
+}
+
+@end
+
+/*
+ PSMTabBarController will store what the current tab frame state should be like based off the last layout. PSMTabBarControl
+ has to handle fetching the new frame and then changing the tab cell frame.
+ Tab states will probably be changed immediately.
+
+ Tabs that aren't going to be visible need to have their frame set offscreen. Treat them as if they were visible.
+
+ The overflow menu is rebuilt and stored by the controller.
+
+ Arrays of tracking rects will be created here, but not applied.
+ Tracking rects are removed and added by PSMTabBarControl at the end of an animate/display cycle.
+
+ The add tab button frame is handled by this controller. Visibility and location are set by the control.
+
+ isInOverflowMenu should probably be removed in favor of a call that returns yes/no to if a cell is in overflow. (Not yet implemented)
+
+ Still need to rewrite most of the code in PSMTabDragAssistant.
+ */
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.h b/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.h
new file mode 100644
index 0000000..a3a8098
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.h
@@ -0,0 +1,101 @@
+//
+// PSMTabDragAssistant.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 4/10/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+/*
+ This class is a sigleton that manages the details of a tab drag and drop. The details were beginning to overwhelm me when keeping all of this in the control and cells :-)
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "PSMTabBarControl.h"
+
+#define kPSMTabDragAnimationSteps 8
+
+@class PSMTabBarCell, PSMTabDragWindowController;
+
+@interface PSMTabDragAssistant : NSObject {
+ PSMTabBarControl *_sourceTabBar;
+ PSMTabBarControl *_destinationTabBar;
+ NSMutableSet *_participatingTabBars;
+ PSMTabBarCell *_draggedCell;
+ NSUInteger _draggedCellIndex; // for snap back
+ BOOL _isDragging;
+
+ // Support for dragging into new windows
+ PSMTabDragWindowController *_draggedTab;
+ PSMTabDragWindowController *_draggedView;
+ NSSize _dragWindowOffset;
+ NSTimer *_fadeTimer;
+ BOOL _centersDragWindows;
+ PSMTabBarTearOffStyle _currentTearOffStyle;
+
+ // Animation
+ NSTimer *_animationTimer;
+ NSMutableArray *_sineCurveWidths;
+ NSPoint _currentMouseLoc;
+ PSMTabBarCell *_targetCell;
+}
+
+// Creation/destruction
++ (PSMTabDragAssistant *)sharedDragAssistant;
+
+// Accessors
+- (PSMTabBarControl *)sourceTabBar;
+- (void)setSourceTabBar:(PSMTabBarControl *)tabBar;
+- (PSMTabBarControl *)destinationTabBar;
+- (void)setDestinationTabBar:(PSMTabBarControl *)tabBar;
+- (PSMTabBarCell *)draggedCell;
+- (void)setDraggedCell:(PSMTabBarCell *)cell;
+- (NSInteger)draggedCellIndex;
+- (void)setDraggedCellIndex:(NSInteger)value;
+- (BOOL)isDragging;
+- (void)setIsDragging:(BOOL)value;
+- (NSPoint)currentMouseLoc;
+- (void)setCurrentMouseLoc:(NSPoint)point;
+- (PSMTabBarCell *)targetCell;
+- (void)setTargetCell:(PSMTabBarCell *)cell;
+
+// Functionality
+- (void)startDraggingCell:(PSMTabBarCell *)cell fromTabBar:(PSMTabBarControl *)control withMouseDownEvent:(NSEvent *)event;
+- (void)draggingEnteredTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc;
+- (void)draggingUpdatedInTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc;
+- (void)draggingExitedTabBar:(PSMTabBarControl *)control;
+- (void)performDragOperation;
+- (void)draggedImageEndedAt:(NSPoint)aPoint operation:(NSDragOperation)operation;
+- (void)finishDrag;
+
+- (void)draggingBeganAt:(NSPoint)aPoint;
+- (void)draggingMovedTo:(NSPoint)aPoint;
+
+// Animation
+- (void)animateDrag:(NSTimer *)timer;
+- (void)calculateDragAnimationForTabBar:(PSMTabBarControl *)control;
+
+// Placeholder
+- (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control withDraggedCell:(PSMTabBarCell *)cell;
+- (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control;
+- (void)removeAllPlaceholdersFromTabBar:(PSMTabBarControl *)control;
+
+@end
+
+@interface PSMTabBarControl (DragAccessors)
+
+- (id<PSMTabStyle>)style;
+- (NSMutableArray *)cells;
+- (void)setControlView:(id)view;
+- (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame;
+- (PSMTabBarCell *)lastVisibleTab;
+- (NSInteger)numberOfVisibleTabs;
+
+@end
+
+void CGContextCopyWindowCaptureContentsToRect(void *grafport, CGRect rect, NSInteger cid, NSInteger wid, NSInteger zero);
+OSStatus CGSSetWindowTransform(NSInteger cid, NSInteger wid, CGAffineTransform transform);
+
+@interface NSApplication (CoreGraphicsUndocumented)
+- (NSInteger)contextID;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.m b/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.m
new file mode 100644
index 0000000..0027f87
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragAssistant.m
@@ -0,0 +1,850 @@
+//
+// PSMTabDragAssistant.m
+// PSMTabBarControl
+//
+// Created by John Pannell on 4/10/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+#import "PSMTabDragAssistant.h"
+#import "PSMTabBarCell.h"
+#import "PSMTabStyle.h"
+#import "PSMTabDragWindowController.h"
+#import "PSMOverflowPopUpButton.h"
+#import "PSMRolloverButton.h"
+
+#define PI 3.1417
+
+@interface PSMTabBarControl (Private)
+- (void)update:(BOOL)animate;
+@end
+
+@interface PSMTabDragAssistant (Private)
+- (NSImage *)_imageForViewOfCell:(PSMTabBarCell *)cell styleMask:(NSUInteger *)outMask;
+- (NSImage *)_miniwindowImageOfWindow:(NSWindow *)window;
+- (void)_expandWindow:(NSWindow *)window atPoint:(NSPoint)point;
+@end
+
+@implementation PSMTabDragAssistant
+
+static PSMTabDragAssistant *sharedDragAssistant = nil;
+
+#pragma mark -
+#pragma mark Creation/Destruction
+
++ (PSMTabDragAssistant *)sharedDragAssistant
+{
+ if (!sharedDragAssistant) {
+ sharedDragAssistant = [[PSMTabDragAssistant alloc] init];
+ }
+
+ return sharedDragAssistant;
+}
+
+- (id)init
+{
+ if ((self = [super init])) {
+ _sourceTabBar = nil;
+ _destinationTabBar = nil;
+ _participatingTabBars = [[NSMutableSet alloc] init];
+ _draggedCell = nil;
+ _animationTimer = nil;
+ _sineCurveWidths = [[NSMutableArray alloc] initWithCapacity:kPSMTabDragAnimationSteps];
+ _targetCell = nil;
+ _isDragging = NO;
+ }
+
+ return self;
+}
+
+#pragma mark -
+#pragma mark Accessors
+
+- (PSMTabBarControl *)sourceTabBar
+{
+ return _sourceTabBar;
+}
+
+- (void)setSourceTabBar:(PSMTabBarControl *)tabBar
+{
+ _sourceTabBar = tabBar;
+}
+
+- (PSMTabBarControl *)destinationTabBar
+{
+ return _destinationTabBar;
+}
+
+- (void)setDestinationTabBar:(PSMTabBarControl *)tabBar
+{
+ _destinationTabBar = tabBar;
+}
+
+- (PSMTabBarCell *)draggedCell
+{
+ return _draggedCell;
+}
+
+- (void)setDraggedCell:(PSMTabBarCell *)cell
+{
+ _draggedCell = cell;
+}
+
+- (NSInteger)draggedCellIndex
+{
+ return _draggedCellIndex;
+}
+
+- (void)setDraggedCellIndex:(NSInteger)value
+{
+ _draggedCellIndex = value;
+}
+
+- (BOOL)isDragging
+{
+ return _isDragging;
+}
+
+- (void)setIsDragging:(BOOL)value
+{
+ _isDragging = value;
+}
+
+- (NSPoint)currentMouseLoc
+{
+ return _currentMouseLoc;
+}
+
+- (void)setCurrentMouseLoc:(NSPoint)point
+{
+ _currentMouseLoc = point;
+}
+
+- (PSMTabBarCell *)targetCell
+{
+ return _targetCell;
+}
+
+- (void)setTargetCell:(PSMTabBarCell *)cell
+{
+ _targetCell = cell;
+}
+
+#pragma mark -
+#pragma mark Functionality
+
+- (void)startDraggingCell:(PSMTabBarCell *)cell fromTabBar:(PSMTabBarControl *)control withMouseDownEvent:(NSEvent *)event
+{
+ [self setIsDragging:YES];
+ [self setSourceTabBar:control];
+ [self setDestinationTabBar:control];
+ [_participatingTabBars addObject:control];
+ [self setDraggedCell:cell];
+ [self setDraggedCellIndex:[[control cells] indexOfObject:cell]];
+
+ NSRect cellFrame = [cell frame];
+ // list of widths for animation
+ NSInteger i;
+ CGFloat cellStepSize = ([control orientation] == PSMTabBarHorizontalOrientation) ? (cellFrame.size.width + 6) : (cellFrame.size.height + 1);
+ for (i = 0; i < kPSMTabDragAnimationSteps - 1; i++) {
+ NSInteger thisWidth = (NSInteger)(cellStepSize - ((cellStepSize / 2.0) + ((sin((PI / 2.0) + ((CGFloat)i / (CGFloat)kPSMTabDragAnimationSteps) * PI) * cellStepSize) / 2.0)));
+ [_sineCurveWidths addObject:[NSNumber numberWithInteger:thisWidth]];
+ }
+ [_sineCurveWidths addObject:[NSNumber numberWithInteger:([control orientation] == PSMTabBarHorizontalOrientation) ? cellFrame.size.width : cellFrame.size.height]];
+
+ // hide UI buttons
+ [[control overflowPopUpButton] setHidden:YES];
+ [[control addTabButton] setHidden:YES];
+
+ [[NSCursor closedHandCursor] set];
+
+ NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+ NSImage *dragImage = [cell dragImage];
+ [[cell indicator] removeFromSuperview];
+ [self distributePlaceholdersInTabBar:control withDraggedCell:cell];
+
+ if ([control isFlipped]) {
+ cellFrame.origin.y += cellFrame.size.height;
+ }
+ [cell setHighlighted:NO];
+ NSSize offset = NSZeroSize;
+ [pboard declareTypes:[NSArray arrayWithObjects:@"PSMTabBarControlItemPBType", nil] owner:nil];
+ [pboard setString:[[NSNumber numberWithInteger:[[control cells] indexOfObject:cell]] stringValue] forType:@"PSMTabBarControlItemPBType"];
+ _animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 30.0) target:self selector:@selector(animateDrag:) userInfo:nil repeats:YES];
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:PSMTabDragDidBeginNotification object:nil];
+
+ //retain the control in case the drag operation causes the control to be released
+
+ if ([control delegate] && [[control delegate] respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] &&
+ [[control delegate] tabView:[control tabView]
+ shouldDropTabViewItem:[[self draggedCell] representedObject]
+ inTabBar:nil]) {
+ _currentTearOffStyle = [control tearOffStyle];
+ _draggedTab = [[PSMTabDragWindowController alloc] initWithImage:dragImage styleMask:NSWindowStyleMaskBorderless tearOffStyle:_currentTearOffStyle];
+
+ cellFrame.origin.y -= cellFrame.size.height;
+ [control dragImage:[[NSImage alloc] initWithSize:NSMakeSize(1, 1)] at:cellFrame.origin offset:offset event:event pasteboard:pboard source:control slideBack:NO];
+ } else {
+ [control dragImage:dragImage at:cellFrame.origin offset:offset event:event pasteboard:pboard source:control slideBack:YES];
+ }
+}
+
+- (void)draggingEnteredTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc
+{
+ if (_currentTearOffStyle == PSMTabBarTearOffMiniwindow && ![self destinationTabBar]) {
+ [_draggedTab switchImages];
+ }
+
+ [self setDestinationTabBar:control];
+ [self setCurrentMouseLoc:mouseLoc];
+ // hide UI buttons
+ [[control overflowPopUpButton] setHidden:YES];
+ [[control addTabButton] setHidden:YES];
+ if ([[control cells] count] == 0 || ![[[control cells] objectAtIndex:0] isPlaceholder]) {
+ [self distributePlaceholdersInTabBar:control];
+ }
+ [_participatingTabBars addObject:control];
+
+ //tell the drag window to display only the header if there is one
+ if (_currentTearOffStyle == PSMTabBarTearOffAlphaWindow && _draggedView) {
+ if (_fadeTimer) {
+ [_fadeTimer invalidate];
+ }
+
+ [[_draggedTab window] orderFront:nil];
+ _fadeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector(fadeOutDragWindow:) userInfo:nil repeats:YES];
+ }
+}
+
+- (void)draggingUpdatedInTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc
+{
+ if ([self destinationTabBar] != control) {
+ [self setDestinationTabBar:control];
+ }
+ [self setCurrentMouseLoc:mouseLoc];
+}
+
+- (void)draggingExitedTabBar:(PSMTabBarControl *)control
+{
+ if ([[control delegate] respondsToSelector:@selector(tabView:shouldAllowTabViewItem:toLeaveTabBar:)] && ![[control delegate] tabView:[control tabView] shouldAllowTabViewItem:[[self draggedCell] representedObject] toLeaveTabBar:control]) {
+ return;
+ }
+
+ [self setDestinationTabBar:nil];
+ [self setCurrentMouseLoc:NSMakePoint(-1.0, -1.0)];
+
+ if (_fadeTimer) {
+ [_fadeTimer invalidate];
+ _fadeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector(fadeInDragWindow:) userInfo:nil repeats:YES];
+ } else if (_draggedTab) {
+ if (_currentTearOffStyle == PSMTabBarTearOffAlphaWindow) {
+ //create a new floating drag window
+ if (!_draggedView) {
+ NSUInteger styleMask;
+ NSImage *viewImage = [self _imageForViewOfCell:[self draggedCell] styleMask:&styleMask];
+
+ _draggedView = [[PSMTabDragWindowController alloc] initWithImage:viewImage styleMask:styleMask tearOffStyle:PSMTabBarTearOffAlphaWindow];
+ [[_draggedView window] setAlphaValue:0.0];
+ }
+
+ NSPoint windowOrigin = [[control window] frame].origin;
+
+ windowOrigin.x -= _dragWindowOffset.width;
+ windowOrigin.y += _dragWindowOffset.height;
+ [[_draggedView window] setFrameOrigin:windowOrigin];
+ [[_draggedView window] orderWindow:NSWindowBelow relativeTo:[[_draggedTab window] windowNumber]];
+ } else if (_currentTearOffStyle == PSMTabBarTearOffMiniwindow && ![_draggedTab alternateImage]) {
+ NSImage *image;
+ NSSize imageSize;
+ NSUInteger mask; //we don't need this but we can't pass nil in for the style mask, as some delegate implementations will crash
+
+ if (!(image = [self _miniwindowImageOfWindow:[control window]])) {
+ image = [[self _imageForViewOfCell:[self draggedCell] styleMask:&mask] copy];
+ }
+
+ imageSize = [image size];
+ [image setScalesWhenResized:YES];
+
+ if (imageSize.width > imageSize.height) {
+ [image setSize:NSMakeSize(125, 125 * (imageSize.height / imageSize.width))];
+ } else {
+ [image setSize:NSMakeSize(125 * (imageSize.width / imageSize.height), 125)];
+ }
+
+ [_draggedTab setAlternateImage:image];
+ }
+
+ //set the window's alpha mask to zero if the last tab is being dragged
+ //don't fade out the old window if the delegate doesn't respond to the new tab bar method, just to be safe
+ if ([[[self sourceTabBar] tabView] numberOfTabViewItems] == 1 && [self sourceTabBar] == control &&
+ [[[self sourceTabBar] delegate] respondsToSelector:@selector(tabView:newTabBarForDraggedTabViewItem:atPoint:)]) {
+ [[[self sourceTabBar] window] setAlphaValue:0.0];
+
+ if ([_sourceTabBar tearOffStyle] == PSMTabBarTearOffAlphaWindow) {
+ [[_draggedView window] setAlphaValue:kPSMTabDragWindowAlpha];
+ } else {
+ //#warning fix me - what should we do when the last tab is dragged as a miniwindow?
+ }
+ } else {
+ if ([_sourceTabBar tearOffStyle] == PSMTabBarTearOffAlphaWindow) {
+ _fadeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector(fadeInDragWindow:) userInfo:nil repeats:YES];
+ } else {
+ [_draggedTab switchImages];
+ _centersDragWindows = YES;
+ }
+ }
+ }
+}
+
+- (void)performDragOperation
+{
+ // move cell
+ NSUInteger destinationIndex = [[[self destinationTabBar] cells] indexOfObject:[self targetCell]];
+
+ //there is the slight possibility of the targetCell now being set properly, so avoid errors
+ if (destinationIndex >= [[[self destinationTabBar] cells] count]) {
+ destinationIndex = [[[self destinationTabBar] cells] count] - 1;
+ }
+
+ [[[self destinationTabBar] cells] replaceObjectAtIndex:destinationIndex withObject:[self draggedCell]];
+ [[self draggedCell] setControlView:[self destinationTabBar]];
+
+ // move actual NSTabViewItem
+ if ([self sourceTabBar] != [self destinationTabBar]) {
+ //remove the tracking rects and bindings registered on the old tab
+ [[self sourceTabBar] removeTrackingRect:[[self draggedCell] closeButtonTrackingTag]];
+ [[self sourceTabBar] removeTrackingRect:[[self draggedCell] cellTrackingTag]];
+ [[self sourceTabBar] removeTabForCell:[self draggedCell]];
+
+ NSUInteger i, insertIndex;
+ NSArray *cells = [[self destinationTabBar] cells];
+
+ //find the index of where the dragged cell was just dropped
+ for (i = 0, insertIndex = 0; (i < [cells count]) && ([cells objectAtIndex:i] != [self draggedCell]); i++, insertIndex++) {
+ if ([[cells objectAtIndex:i] isPlaceholder]) {
+ insertIndex--;
+ }
+ }
+
+ [[[self sourceTabBar] tabView] removeTabViewItem:[[self draggedCell] representedObject]];
+ [[[self destinationTabBar] tabView] insertTabViewItem:[[self draggedCell] representedObject] atIndex:insertIndex];
+
+ //calculate the position for the dragged cell
+ if ([[self destinationTabBar] automaticallyAnimates]) {
+ if (insertIndex > 0) {
+ NSRect cellRect = [[cells objectAtIndex:insertIndex - 1] frame];
+ cellRect.origin.x += cellRect.size.width;
+ [[self draggedCell] setFrame:cellRect];
+ }
+ }
+
+ //rebind the cell to the new control
+ [[self destinationTabBar] bindPropertiesForCell:[self draggedCell] andTabViewItem:[[self draggedCell] representedObject]];
+
+ //select the newly moved item in the destination tab view
+ [[[self destinationTabBar] tabView] selectTabViewItem:[[self draggedCell] representedObject]];
+ } else {
+ //have to do this before checking the index of a cell otherwise placeholders will be counted
+ [self removeAllPlaceholdersFromTabBar:[self sourceTabBar]];
+
+ //rearrange the tab view items
+ NSTabView *tabView = [[self sourceTabBar] tabView];
+ NSTabViewItem *item = [[self draggedCell] representedObject];
+ BOOL reselect = ([tabView selectedTabViewItem] == item);
+ NSArray *cells = [[self sourceTabBar] cells];
+ NSUInteger index;
+ //find the index of where the dragged cell was just dropped
+ for (index = 0; index < [cells count] && [cells objectAtIndex:index] != [self draggedCell]; index++) {
+ ;
+ }
+
+ //temporarily disable the delegate in order to move the tab to a different index
+ id tempDelegate = [tabView delegate];
+ [tabView setDelegate:nil];
+ [tabView removeTabViewItem:item];
+ [tabView insertTabViewItem:item atIndex:index];
+ if (reselect) {
+ [tabView selectTabViewItem:item];
+ }
+ [tabView setDelegate:tempDelegate];
+ }
+
+ if (([self sourceTabBar] != [self destinationTabBar] || [[[self sourceTabBar] cells] indexOfObject:[self draggedCell]] != _draggedCellIndex) && [[[self sourceTabBar] delegate] respondsToSelector:@selector(tabView:didDropTabViewItem:inTabBar:)]) {
+ [[[self sourceTabBar] delegate] tabView:[[self sourceTabBar] tabView] didDropTabViewItem:[[self draggedCell] representedObject] inTabBar:[self destinationTabBar]];
+ }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:PSMTabDragDidEndNotification object:nil];
+
+ [self finishDrag];
+}
+
+- (void)draggedImageEndedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
+{
+ if ([self isDragging]) { // means there was not a successful drop (performDragOperation)
+ id sourceDelegate = [[self sourceTabBar] delegate];
+
+ //split off the dragged tab into a new window
+ if ([self destinationTabBar] == nil && sourceDelegate && [sourceDelegate respondsToSelector:@selector(tabView:shouldDropTabViewItem:inTabBar:)] &&
+ [sourceDelegate tabView:[[self sourceTabBar] tabView]
+ shouldDropTabViewItem:[[self draggedCell] representedObject]
+ inTabBar:nil]
+ &&
+ [sourceDelegate respondsToSelector:@selector(tabView:newTabBarForDraggedTabViewItem:atPoint:)]) {
+ PSMTabBarControl *control = [sourceDelegate tabView:[[self sourceTabBar] tabView] newTabBarForDraggedTabViewItem:[[self draggedCell] representedObject] atPoint:aPoint];
+
+ if (control) {
+ //add the dragged tab to the new window
+ [[control cells] insertObject:[self draggedCell] atIndex:0];
+
+ //remove the tracking rects and bindings registered on the old tab
+ [[self sourceTabBar] removeTrackingRect:[[self draggedCell] closeButtonTrackingTag]];
+ [[self sourceTabBar] removeTrackingRect:[[self draggedCell] cellTrackingTag]];
+ [[self sourceTabBar] removeTabForCell:[self draggedCell]];
+
+ //rebind the cell to the new control
+ [control bindPropertiesForCell:[self draggedCell] andTabViewItem:[[self draggedCell] representedObject]];
+
+ [[self draggedCell] setControlView:control];
+
+ [[[self sourceTabBar] tabView] removeTabViewItem:[[self draggedCell] representedObject]];
+
+ [[control tabView] addTabViewItem:[[self draggedCell] representedObject]];
+ [control update:NO]; //make sure the new tab is set in the correct position
+
+ if (_currentTearOffStyle == PSMTabBarTearOffAlphaWindow) {
+ [[control window] makeKeyAndOrderFront:nil];
+ } else {
+ //center the window over where we ended dragging
+ [self _expandWindow:[control window] atPoint:[NSEvent mouseLocation]];
+ }
+
+ if ([sourceDelegate respondsToSelector:@selector(tabView:didDropTabViewItem:inTabBar:)]) {
+ [sourceDelegate tabView:[[self sourceTabBar] tabView] didDropTabViewItem:[[self draggedCell] representedObject] inTabBar:control];
+ }
+ } else {
+ NSLog(@"Delegate returned no control to add to.");
+ [[[self sourceTabBar] cells] insertObject:[self draggedCell] atIndex:[self draggedCellIndex]];
+ }
+ } else {
+ // put cell back
+ [[[self sourceTabBar] cells] insertObject:[self draggedCell] atIndex:[self draggedCellIndex]];
+ }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:PSMTabDragDidEndNotification object:nil];
+
+ [self finishDrag];
+ }
+}
+
+- (void)finishDrag
+{
+ if ([[[self sourceTabBar] tabView] numberOfTabViewItems] == 0 && [[[self sourceTabBar] delegate] respondsToSelector:@selector(tabView:closeWindowForLastTabViewItem:)]) {
+ [[[self sourceTabBar] delegate] tabView:[[self sourceTabBar] tabView] closeWindowForLastTabViewItem:[[self draggedCell] representedObject]];
+ }
+
+ if (_draggedTab) {
+ [[_draggedTab window] orderOut:nil];
+ _draggedTab = nil;
+ }
+
+ if (_draggedView) {
+ [[_draggedView window] orderOut:nil];
+ _draggedView = nil;
+ }
+
+ _centersDragWindows = NO;
+
+ [self setIsDragging:NO];
+ [self removeAllPlaceholdersFromTabBar:[self sourceTabBar]];
+ [self setSourceTabBar:nil];
+ [self setDestinationTabBar:nil];
+ NSEnumerator *e = [_participatingTabBars objectEnumerator];
+ PSMTabBarControl *tabBar;
+ while ((tabBar = [e nextObject])) {
+ [self removeAllPlaceholdersFromTabBar:tabBar];
+ }
+ [_participatingTabBars removeAllObjects];
+ [self setDraggedCell:nil];
+ [_animationTimer invalidate];
+ _animationTimer = nil;
+ [_sineCurveWidths removeAllObjects];
+ [self setTargetCell:nil];
+}
+
+- (void)draggingBeganAt:(NSPoint)aPoint
+{
+ if (_draggedTab) {
+ [[_draggedTab window] setFrameTopLeftPoint:aPoint];
+ [[_draggedTab window] orderFront:nil];
+
+ if ([[[self sourceTabBar] tabView] numberOfTabViewItems] == 1) {
+ [self draggingExitedTabBar:[self sourceTabBar]];
+ [[_draggedTab window] setAlphaValue:0.0];
+ }
+ }
+}
+
+- (void)draggingMovedTo:(NSPoint)aPoint
+{
+ if (_draggedTab) {
+ if (_centersDragWindows) {
+ if ([_draggedTab isAnimating]) {
+ return;
+ }
+
+ //Ignore aPoint, as it seems to give wacky values
+ NSRect frame = [[_draggedTab window] frame];
+ frame.origin = [NSEvent mouseLocation];
+ frame.origin.x -= frame.size.width / 2;
+ frame.origin.y -= frame.size.height / 2;
+ [[_draggedTab window] setFrame:frame display:NO];
+ } else {
+ [[_draggedTab window] setFrameTopLeftPoint:aPoint];
+ }
+
+ if (_draggedView) {
+ //move the view representation with the tab
+ //the relative position of the dragged view window will be different
+ //depending on the position of the tab bar relative to the controlled tab view
+
+ aPoint.y -= [[_draggedTab window] frame].size.height;
+ aPoint.x -= _dragWindowOffset.width;
+ aPoint.y += _dragWindowOffset.height;
+ [[_draggedView window] setFrameTopLeftPoint:aPoint];
+ }
+ }
+}
+
+- (void)fadeInDragWindow:(NSTimer *)timer
+{
+ CGFloat value = [[_draggedView window] alphaValue];
+ if (value >= kPSMTabDragWindowAlpha || _draggedTab == nil) {
+ [timer invalidate];
+ _fadeTimer = nil;
+ } else {
+ [[_draggedTab window] setAlphaValue:[[_draggedTab window] alphaValue] - kPSMTabDragAlphaInterval];
+ [[_draggedView window] setAlphaValue:value + kPSMTabDragAlphaInterval];
+ }
+}
+
+- (void)fadeOutDragWindow:(NSTimer *)timer
+{
+ CGFloat value = [[_draggedView window] alphaValue];
+ NSWindow *tabWindow = [_draggedTab window], *viewWindow = [_draggedView window];
+
+ if (value <= 0.0) {
+ [viewWindow setAlphaValue:0.0];
+ [tabWindow setAlphaValue:kPSMTabDragWindowAlpha];
+
+ [timer invalidate];
+ _fadeTimer = nil;
+ } else {
+ if ([tabWindow alphaValue] < kPSMTabDragWindowAlpha) {
+ [tabWindow setAlphaValue:[tabWindow alphaValue] + kPSMTabDragAlphaInterval];
+ }
+ [viewWindow setAlphaValue:value - kPSMTabDragAlphaInterval];
+ }
+}
+
+#pragma mark -
+#pragma mark Private
+
+- (NSImage *)_imageForViewOfCell:(PSMTabBarCell *)cell styleMask:(NSUInteger *)outMask
+{
+ PSMTabBarControl *control = [cell controlView];
+ NSImage *viewImage = nil;
+
+ if (outMask) {
+ *outMask = NSWindowStyleMaskBorderless;
+ }
+
+ if ([control delegate] && [[control delegate] respondsToSelector:@selector(tabView:imageForTabViewItem:offset:styleMask:)]) {
+ //get a custom image representation of the view to drag from the delegate
+ NSImage *tabImage = [_draggedTab image];
+ NSPoint drawPoint;
+ _dragWindowOffset = NSZeroSize;
+ viewImage = [[control delegate] tabView:[control tabView] imageForTabViewItem:[cell representedObject] offset:&_dragWindowOffset styleMask:outMask];
+ [viewImage lockFocus];
+
+ //draw the tab into the returned window, that way we don't have two windows being dragged (this assumes the tab will be on the window)
+ drawPoint = NSMakePoint(_dragWindowOffset.width, [viewImage size].height - _dragWindowOffset.height);
+
+ if ([control orientation] == PSMTabBarHorizontalOrientation) {
+ drawPoint.y += [[control style] tabCellHeight] - [tabImage size].height;
+ _dragWindowOffset.height -= [[control style] tabCellHeight] - [tabImage size].height;
+ } else {
+ drawPoint.x += [control frame].size.width - [tabImage size].width;
+ }
+
+ [tabImage drawAtPoint:drawPoint fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
+
+ [viewImage unlockFocus];
+ } else {
+ //the delegate doesn't give a custom image, so use an image of the view
+ NSView *tabView = [[cell representedObject] view];
+ viewImage = [[NSImage alloc] initWithSize:[tabView frame].size];
+ [viewImage lockFocus];
+ [tabView drawRect:[tabView bounds]];
+ [viewImage unlockFocus];
+ }
+
+ if (*outMask | NSWindowStyleMaskBorderless) {
+ _dragWindowOffset.height += 22;
+ }
+
+ return viewImage;
+}
+
+- (NSImage *)_miniwindowImageOfWindow:(NSWindow *)window
+{
+ NSRect rect = [window frame];
+ NSImage *image = [[NSImage alloc] initWithSize:rect.size];
+ [image lockFocus];
+ rect.origin = NSZeroPoint;
+ CGContextCopyWindowCaptureContentsToRect([[NSGraphicsContext currentContext] graphicsPort], *(CGRect *)&rect, [NSApp contextID], [window windowNumber], 0);
+ [image unlockFocus];
+
+ return image;
+}
+
+- (void)_expandWindow:(NSWindow *)window atPoint:(NSPoint)point
+{
+ NSRect frame = [window frame];
+ [window setFrameTopLeftPoint:NSMakePoint(point.x - frame.size.width / 2, point.y + frame.size.height / 2)];
+ [window setAlphaValue:0.0];
+ [window makeKeyAndOrderFront:nil];
+
+ NSAnimation *animation = [[NSAnimation alloc] initWithDuration:0.25 animationCurve:NSAnimationEaseInOut];
+ [animation setAnimationBlockingMode:NSAnimationNonblocking];
+ [animation setCurrentProgress:0.1];
+ [animation startAnimation];
+ NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector(_expandWindowTimerFired:) userInfo:[NSDictionary dictionaryWithObjectsAndKeys:window, @"Window", animation, @"Animation", nil] repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
+}
+
+- (void)_expandWindowTimerFired:(NSTimer *)timer
+{
+ NSWindow *window = [[timer userInfo] objectForKey:@"Window"];
+ NSAnimation *animation = [[timer userInfo] objectForKey:@"Animation"];
+ CGAffineTransform transform;
+ NSPoint translation;
+ NSRect winFrame = [window frame];
+
+ translation.x = (winFrame.size.width / 2.0);
+ translation.y = (winFrame.size.height / 2.0);
+ transform = CGAffineTransformMakeTranslation(translation.x, translation.y);
+ transform = CGAffineTransformScale(transform, 1.0 / [animation currentValue], 1.0 / [animation currentValue]);
+ transform = CGAffineTransformTranslate(transform, -translation.x, -translation.y);
+
+ translation.x = -winFrame.origin.x;
+ translation.y = winFrame.origin.y + winFrame.size.height - [[NSScreen mainScreen] frame].size.height;
+
+ transform = CGAffineTransformTranslate(transform, translation.x, translation.y);
+
+ CGSSetWindowTransform([NSApp contextID], [window windowNumber], transform);
+
+ [window setAlphaValue:[animation currentValue]];
+
+ if (![animation isAnimating]) {
+ [timer invalidate];
+ }
+}
+
+#pragma mark -
+#pragma mark Animation
+
+- (void)animateDrag:(NSTimer *)timer
+{
+ NSEnumerator *e = [[_participatingTabBars copy] objectEnumerator];
+ PSMTabBarControl *tabBar;
+ while ((tabBar = [e nextObject])) {
+ [self calculateDragAnimationForTabBar:tabBar];
+ [[NSRunLoop currentRunLoop] performSelector:@selector(display) target:tabBar argument:nil order:1 modes:[NSArray arrayWithObjects:@"NSEventTrackingRunLoopMode", @"NSDefaultRunLoopMode", nil]];
+ }
+}
+
+- (void)calculateDragAnimationForTabBar:(PSMTabBarControl *)control
+{
+ BOOL removeFlag = YES;
+ NSMutableArray *cells = [control cells];
+ NSInteger i, cellCount = [cells count];
+ CGFloat position = [control orientation] == PSMTabBarHorizontalOrientation ? [[control style] leftMarginForTabBarControl] : [[control style] topMarginForTabBarControl];
+
+ // identify target cell
+ // mouse at beginning of tabs
+ NSPoint mouseLoc = [self currentMouseLoc];
+ if ([self destinationTabBar] == control) {
+ removeFlag = NO;
+ if (mouseLoc.x < [[control style] leftMarginForTabBarControl]) {
+ [self setTargetCell:[cells objectAtIndex:0]];
+ } else {
+ NSRect overCellRect;
+ PSMTabBarCell *overCell = [control cellForPoint:mouseLoc cellFrame:&overCellRect];
+ if (overCell) {
+ // mouse among cells - placeholder
+ if ([overCell isPlaceholder]) {
+ [self setTargetCell:overCell];
+ } else if ([control orientation] == PSMTabBarHorizontalOrientation) {
+ // non-placeholders - horizontal orientation
+ if (mouseLoc.x < (overCellRect.origin.x + (overCellRect.size.width / 2.0))) {
+ // mouse on left side of cell
+ [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] - 1)]];
+ } else {
+ // mouse on right side of cell
+ [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] + 1)]];
+ }
+ } else {
+ // non-placeholders - vertical orientation
+ if (mouseLoc.y < (overCellRect.origin.y + (overCellRect.size.height / 2.0))) {
+ // mouse on top of cell
+ [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] - 1)]];
+ } else {
+ // mouse on bottom of cell
+ [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] + 1)]];
+ }
+ }
+ } else {
+ // out at end - must find proper cell (could be more in overflow menu)
+ [self setTargetCell:[control lastVisibleTab]];
+ }
+ }
+ } else {
+ [self setTargetCell:nil];
+ }
+
+ for (i = 0; i < cellCount; i++) {
+ PSMTabBarCell *cell = [cells objectAtIndex:i];
+ NSRect newRect = [cell frame];
+ if (![cell isInOverflowMenu]) {
+ if ([cell isPlaceholder]) {
+ if (cell == [self targetCell]) {
+ [cell setCurrentStep:([cell currentStep] + 1)];
+ } else {
+ [cell setCurrentStep:([cell currentStep] - 1)];
+ if ([cell currentStep] > 0) {
+ removeFlag = NO;
+ }
+ }
+
+ if ([control orientation] == PSMTabBarHorizontalOrientation) {
+ newRect.size.width = [[_sineCurveWidths objectAtIndex:[cell currentStep]] integerValue];
+ } else {
+ newRect.size.height = [[_sineCurveWidths objectAtIndex:[cell currentStep]] integerValue];
+ }
+ }
+ } else {
+ break;
+ }
+
+ if ([control orientation] == PSMTabBarHorizontalOrientation) {
+ newRect.origin.x = position;
+ position += newRect.size.width;
+ } else {
+ newRect.origin.y = position;
+ position += newRect.size.height;
+ }
+ [cell setFrame:newRect];
+ if ([cell indicator]) {
+ [[cell indicator] setFrame:[[control style] indicatorRectForTabCell:cell]];
+ }
+ }
+ if (removeFlag) {
+ [_participatingTabBars removeObject:control];
+ [self removeAllPlaceholdersFromTabBar:control];
+ }
+}
+
+#pragma mark -
+#pragma mark Placeholders
+
+- (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control withDraggedCell:(PSMTabBarCell *)cell
+{
+ // called upon first drag - must distribute placeholders
+ [self distributePlaceholdersInTabBar:control];
+
+ NSMutableArray *cells = [control cells];
+
+ // replace dragged cell with a placeholder, and clean up surrounding cells
+ NSInteger cellIndex = [cells indexOfObject:cell];
+ PSMTabBarCell *pc = [[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:YES inControlView:control];
+ [cells replaceObjectAtIndex:cellIndex withObject:pc];
+ [cells removeObjectAtIndex:(cellIndex + 1)];
+ [cells removeObjectAtIndex:(cellIndex - 1)];
+
+ if (cellIndex - 2 >= 0) {
+ pc = [cells objectAtIndex:cellIndex - 2];
+ [pc setTabState:~[pc tabState] & PSMTab_RightIsSelectedMask];
+ }
+}
+
+- (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control
+{
+ NSUInteger i, numVisibleTabs = [control numberOfVisibleTabs];
+ for (i = 0; i < numVisibleTabs; i++) {
+ PSMTabBarCell *pc = [[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:NO inControlView:control];
+ [[control cells] insertObject:pc atIndex:(2 * i)];
+ }
+
+ PSMTabBarCell *pc = [[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:NO inControlView:control];
+ if ([[control cells] count] > (2 * numVisibleTabs)) {
+ [[control cells] insertObject:pc atIndex:(2 * numVisibleTabs)];
+ } else {
+ [[control cells] addObject:pc];
+ }
+}
+
+- (void)removeAllPlaceholdersFromTabBar:(PSMTabBarControl *)control
+{
+ NSInteger i, cellCount = [[control cells] count];
+ for (i = (cellCount - 1); i >= 0; i--) {
+ PSMTabBarCell *cell = [[control cells] objectAtIndex:i];
+ if ([cell isPlaceholder]) {
+ [control removeTabForCell:cell];
+ }
+ }
+ // redraw
+ [control update:NO];
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ //[super encodeWithCoder:aCoder];
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:_sourceTabBar forKey:@"sourceTabBar"];
+ [aCoder encodeObject:_destinationTabBar forKey:@"destinationTabBar"];
+ [aCoder encodeObject:_participatingTabBars forKey:@"participatingTabBars"];
+ [aCoder encodeObject:_draggedCell forKey:@"draggedCell"];
+ [aCoder encodeInteger:_draggedCellIndex forKey:@"draggedCellIndex"];
+ [aCoder encodeBool:_isDragging forKey:@"isDragging"];
+ [aCoder encodeObject:_animationTimer forKey:@"animationTimer"];
+ [aCoder encodeObject:_sineCurveWidths forKey:@"sineCurveWidths"];
+ [aCoder encodePoint:_currentMouseLoc forKey:@"currentMouseLoc"];
+ [aCoder encodeObject:_targetCell forKey:@"targetCell"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ //self = [super initWithCoder:aDecoder];
+ //if (self) {
+ if ([aDecoder allowsKeyedCoding]) {
+ _sourceTabBar = [aDecoder decodeObjectForKey:@"sourceTabBar"];
+ _destinationTabBar = [aDecoder decodeObjectForKey:@"destinationTabBar"];
+ _participatingTabBars = [aDecoder decodeObjectForKey:@"participatingTabBars"];
+ _draggedCell = [aDecoder decodeObjectForKey:@"draggedCell"];
+ _draggedCellIndex = [aDecoder decodeIntegerForKey:@"draggedCellIndex"];
+ _isDragging = [aDecoder decodeBoolForKey:@"isDragging"];
+ _animationTimer = [aDecoder decodeObjectForKey:@"animationTimer"];
+ _sineCurveWidths = [aDecoder decodeObjectForKey:@"sineCurveWidths"];
+ _currentMouseLoc = [aDecoder decodePointForKey:@"currentMouseLoc"];
+ _targetCell = [aDecoder decodeObjectForKey:@"targetCell"];
+ }
+ //}
+ return self;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragView.h b/frontends/cocoa/PSMTabBarControl/PSMTabDragView.h
new file mode 100644
index 0000000..d106f51
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragView.h
@@ -0,0 +1,21 @@
+//
+// PSMTabDragView.h
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/17/07.
+// Copyright 2007 Kent Sutherland. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface PSMTabDragView : NSView {
+ NSImage *_image;
+ NSImage *_alternateImage;
+ CGFloat _alpha;
+}
+- (void)setFadeValue:(CGFloat)value;
+- (NSImage *)image;
+- (void)setImage:(NSImage *)image;
+- (NSImage *)alternateImage;
+- (void)setAlternateImage:(NSImage *)image;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragView.m b/frontends/cocoa/PSMTabBarControl/PSMTabDragView.m
new file mode 100644
index 0000000..4f6ce91
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragView.m
@@ -0,0 +1,60 @@
+//
+// PSMTabDragView.m
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/17/07.
+// Copyright 2007 Kent Sutherland. All rights reserved.
+//
+
+#import "PSMTabDragView.h"
+
+@implementation PSMTabDragView
+
+- (id)initWithFrame:(NSRect)frame
+{
+ if ((self = [super initWithFrame:frame])) {
+ _alpha = 1.0;
+ }
+ return self;
+}
+
+- (void)drawRect:(NSRect)rect
+{
+ //1.0 fade means show the primary image
+ //0.0 fade means show the secondary image
+ CGFloat primaryAlpha = _alpha + 0.001f, alternateAlpha = 1.001f - _alpha;
+ NSRect srcRect;
+ srcRect.origin = NSZeroPoint;
+ srcRect.size = [_image size];
+
+ [_image drawInRect:[self bounds] fromRect:srcRect operation:NSCompositingOperationSourceOver fraction:primaryAlpha];
+ srcRect.size = [_alternateImage size];
+ [_alternateImage drawInRect:[self bounds] fromRect:srcRect operation:NSCompositingOperationSourceOver fraction:alternateAlpha];
+}
+
+- (void)setFadeValue:(CGFloat)value
+{
+ _alpha = value;
+}
+
+- (NSImage *)image
+{
+ return _image;
+}
+
+- (void)setImage:(NSImage *)image
+{
+ _image = image;
+}
+
+- (NSImage *)alternateImage
+{
+ return _alternateImage;
+}
+
+- (void)setAlternateImage:(NSImage *)image
+{
+ _alternateImage = image;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.h b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.h
new file mode 100644
index 0000000..15d0662
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.h
@@ -0,0 +1,20 @@
+//
+// PSMTabDragWindow.h
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/1/06.
+// Copyright 2006 Kent Sutherland. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class PSMTabDragView;
+
+@interface PSMTabDragWindow : NSWindow {
+ PSMTabDragView *_dragView;
+}
++ (PSMTabDragWindow *)dragWindowWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask;
+
+- (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask;
+- (PSMTabDragView *)dragView;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.m b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.m
new file mode 100644
index 0000000..cae9280
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindow.m
@@ -0,0 +1,51 @@
+//
+// PSMTabDragWindow.m
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/1/06.
+// Copyright 2006 Kent Sutherland. All rights reserved.
+//
+
+#import "PSMTabDragWindow.h"
+#import "PSMTabDragView.h"
+
+@implementation PSMTabDragWindow
+
++ (PSMTabDragWindow *)dragWindowWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask
+{
+ return [[PSMTabDragWindow alloc] initWithImage:image styleMask:styleMask];
+}
+
+- (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask
+{
+ NSSize size = [image size];
+
+ if ((self = [super initWithContentRect:NSMakeRect(0, 0, size.width, size.height) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO])) {
+ _dragView = [[PSMTabDragView alloc] initWithFrame:NSMakeRect(0, 0, size.width, size.height)];
+ [self setContentView:_dragView];
+ [self setLevel:NSStatusWindowLevel];
+ [self setIgnoresMouseEvents:YES];
+ [self setOpaque:NO];
+
+ [_dragView setImage:image];
+
+ //Set the size of the window to be the exact size of the drag image
+ NSRect windowFrame = [self frame];
+ windowFrame.origin.y += windowFrame.size.height - size.height;
+ windowFrame.size = size;
+
+ if (styleMask | NSWindowStyleMaskBorderless) {
+ windowFrame.size.height += 22;
+ }
+
+ [self setFrame:windowFrame display:YES];
+ }
+ return self;
+}
+
+- (PSMTabDragView *)dragView
+{
+ return _dragView;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.h b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.h
new file mode 100644
index 0000000..bc67622
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.h
@@ -0,0 +1,33 @@
+//
+// PSMTabDragWindowController.h
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/18/07.
+// Copyright 2007 Kent Sutherland. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PSMTabBarControl.h"
+
+#define kPSMTabDragWindowAlpha 0.75
+#define kPSMTabDragAlphaInterval 0.15
+
+@class PSMTabDragView;
+
+@interface PSMTabDragWindowController : NSWindowController {
+ PSMTabBarTearOffStyle _tearOffStyle;
+ PSMTabDragView *_view;
+ NSAnimation *_animation;
+ NSTimer *_timer;
+
+ BOOL _showingAlternate;
+ NSRect _originalWindowFrame;
+}
+- (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask tearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle;
+
+- (NSImage *)image;
+- (NSImage *)alternateImage;
+- (void)setAlternateImage:(NSImage *)image;
+- (BOOL)isAnimating;
+- (void)switchImages;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.m b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.m
new file mode 100644
index 0000000..78b0fcb
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabDragWindowController.m
@@ -0,0 +1,109 @@
+//
+// PSMTabDragWindowController.m
+// PSMTabBarControl
+//
+// Created by Kent Sutherland on 6/18/07.
+// Copyright 2007 Kent Sutherland. All rights reserved.
+//
+
+#import "PSMTabDragWindowController.h"
+#import "PSMTabDragWindow.h"
+#import "PSMTabDragView.h"
+
+@implementation PSMTabDragWindowController
+
+- (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask tearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle
+{
+ PSMTabDragWindow *window = [PSMTabDragWindow dragWindowWithImage:image styleMask:styleMask];
+ if ((self = [super initWithWindow:window])) {
+ _view = [window dragView];
+ _tearOffStyle = tearOffStyle;
+
+ if (tearOffStyle == PSMTabBarTearOffMiniwindow) {
+ [window setBackgroundColor:[NSColor clearColor]];
+ [window setHasShadow:YES];
+ }
+
+ [window setAlphaValue:kPSMTabDragWindowAlpha];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [_timer invalidate];
+}
+
+- (NSImage *)image
+{
+ return [_view image];
+}
+
+- (NSImage *)alternateImage
+{
+ return [_view alternateImage];
+}
+
+- (void)setAlternateImage:(NSImage *)image
+{
+ [_view setAlternateImage:image];
+}
+
+- (BOOL)isAnimating
+{
+ return _animation != nil;
+}
+
+- (void)switchImages
+{
+ if (_tearOffStyle != PSMTabBarTearOffMiniwindow || ![_view alternateImage]) {
+ return;
+ }
+
+ CGFloat progress = 0;
+ _showingAlternate = !_showingAlternate;
+
+ if (_animation) {
+ //An animation already exists, get the current progress
+ progress = 1.0f - [_animation currentProgress];
+ [_animation stopAnimation];
+ }
+
+ //begin animating
+ _animation = [[NSAnimation alloc] initWithDuration:0.25 animationCurve:NSAnimationEaseInOut];
+ [_animation setAnimationBlockingMode:NSAnimationNonblocking];
+ [_animation setCurrentProgress:progress];
+ [_animation startAnimation];
+
+ _originalWindowFrame = [[self window] frame];
+
+ if (_timer) {
+ [_timer invalidate];
+ }
+ _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f / 30.0f target:self selector:@selector(animateTimer:) userInfo:nil repeats:YES];
+}
+
+- (void)animateTimer:(NSTimer *)timer
+{
+ NSRect frame = _originalWindowFrame;
+ NSImage *currentImage = _showingAlternate ? [_view alternateImage] : [_view image];
+ NSSize size = [currentImage size];
+ NSPoint mousePoint = [NSEvent mouseLocation];
+ CGFloat animationValue = [_animation currentValue];
+
+ frame.size.width = _originalWindowFrame.size.width + (size.width - _originalWindowFrame.size.width) * animationValue;
+ frame.size.height = _originalWindowFrame.size.height + (size.height - _originalWindowFrame.size.height) * animationValue;
+ frame.origin.x = mousePoint.x - (frame.size.width / 2);
+ frame.origin.y = mousePoint.y - (frame.size.height / 2);
+
+ [_view setFadeValue:_showingAlternate ? 1.0f - animationValue : animationValue];
+ [[self window] setFrame:frame display:YES];
+
+ if (![_animation isAnimating]) {
+ _animation = nil;
+ [timer invalidate];
+ _timer = nil;
+ }
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMTabStyle.h b/frontends/cocoa/PSMTabBarControl/PSMTabStyle.h
new file mode 100644
index 0000000..ca37174
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMTabStyle.h
@@ -0,0 +1,57 @@
+//
+// PSMTabStyle.h
+// PSMTabBarControl
+//
+// Created by John Pannell on 2/17/06.
+// Copyright 2006 Positive Spin Media. All rights reserved.
+//
+
+/*
+ Protocol to be observed by all style delegate objects. These objects handle the drawing responsibilities for PSMTabBarCell; once the control has been assigned a style, the background and cells draw consistent with that style. Design pattern and implementation by David Smith, Seth Willits, and Chris Forsythe, all touch up and errors by John P. :-)
+ */
+
+#import "PSMTabBarCell.h"
+#import "PSMTabBarControl.h"
+
+@protocol PSMTabStyle <NSObject>
+
+// identity
+- (NSString *)name;
+
+// control specific parameters
+- (CGFloat)leftMarginForTabBarControl;
+- (CGFloat)rightMarginForTabBarControl;
+- (CGFloat)topMarginForTabBarControl;
+- (void)setOrientation:(PSMTabBarOrientation)value;
+
+// add tab button
+- (NSImage *)addTabButtonImage;
+- (NSImage *)addTabButtonPressedImage;
+- (NSImage *)addTabButtonRolloverImage;
+
+// cell specific parameters
+- (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell orientation:(PSMTabBarOrientation)orientation;
+- (NSRect)closeButtonRectForTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)cellFrame;
+- (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell;
+- (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell;
+- (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell;
+- (CGFloat)minimumWidthOfTabCell:(PSMTabBarCell *)cell;
+- (CGFloat)desiredWidthOfTabCell:(PSMTabBarCell *)cell;
+- (CGFloat)tabCellHeight;
+
+// cell values
+- (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell;
+- (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell;
+
+// drawing
+- (void)drawTabCell:(PSMTabBarCell *)cell;
+- (void)drawBackgroundInRect:(NSRect)rect;
+- (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect;
+
+@end
+
+@interface PSMTabBarControl (StyleAccessors)
+
+- (NSMutableArray *)cells;
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.h b/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.h
new file mode 100644
index 0000000..319fd72
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.h
@@ -0,0 +1,29 @@
+//
+// PSMUnifiedTabStyle.h
+// --------------------
+//
+// Created by Keith Blount on 30/04/2006.
+// Copyright 2006 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PSMTabStyle.h"
+
+@interface PSMUnifiedTabStyle : NSObject <PSMTabStyle> {
+ NSImage *unifiedCloseButton;
+ NSImage *unifiedCloseButtonDown;
+ NSImage *unifiedCloseButtonOver;
+ NSImage *unifiedCloseDirtyButton;
+ NSImage *unifiedCloseDirtyButtonDown;
+ NSImage *unifiedCloseDirtyButtonOver;
+ NSImage *_addTabButtonImage;
+ NSImage *_addTabButtonPressedImage;
+ NSImage *_addTabButtonRolloverImage;
+
+ NSDictionary *_objectCountStringAttributes;
+
+ CGFloat leftMargin;
+ PSMTabBarControl *tabBar;
+}
+- (void)setLeftMarginForTabBarControl:(CGFloat)margin;
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.m b/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.m
new file mode 100644
index 0000000..687c779
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/PSMUnifiedTabStyle.m
@@ -0,0 +1,581 @@
+//
+// PSMUnifiedTabStyle.m
+// --------------------
+//
+// Created by Keith Blount on 30/04/2006.
+// Copyright 2006 __MyCompanyName__. All rights reserved.
+//
+
+#import "PSMUnifiedTabStyle.h"
+#import "PSMTabBarCell.h"
+#import "PSMTabBarControl.h"
+#import "NSBezierPath_AMShading.h"
+
+#define kPSMUnifiedObjectCounterRadius 7.0
+#define kPSMUnifiedCounterMinWidth 20
+
+@interface PSMUnifiedTabStyle (Private)
+- (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView *)controlView;
+@end
+
+@implementation PSMUnifiedTabStyle
+
+- (NSString *)name
+{
+ return @"Unified";
+}
+
+#pragma mark -
+#pragma mark Creation/Destruction
+
+- (id)init
+{
+ if ((self = [super init])) {
+ unifiedCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front"]];
+ unifiedCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Pressed"]];
+ unifiedCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Rollover"]];
+
+ unifiedCloseDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front"]];
+ unifiedCloseDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Pressed"]];
+ unifiedCloseDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Rollover"]];
+
+ _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNew"]];
+ _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewPressed"]];
+ _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewRollover"]];
+
+ _objectCountStringAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSFontManager sharedFontManager] convertFont:[NSFont fontWithName:@"Helvetica" size:11.0] toHaveTrait:NSBoldFontMask], NSFontAttributeName,
+ [[NSColor whiteColor] colorWithAlphaComponent:0.85], NSForegroundColorAttributeName,
+ nil, nil];
+
+ leftMargin = 5.0;
+ }
+ return self;
+}
+
+#pragma mark -
+#pragma mark Control Specific
+
+- (void)setLeftMarginForTabBarControl:(CGFloat)margin
+{
+ leftMargin = margin;
+}
+
+- (CGFloat)leftMarginForTabBarControl
+{
+ return leftMargin;
+}
+
+- (CGFloat)rightMarginForTabBarControl
+{
+ return 24.0f;
+}
+
+- (CGFloat)topMarginForTabBarControl
+{
+ return 10.0f;
+}
+
+- (void)setOrientation:(PSMTabBarOrientation)value
+{
+}
+
+#pragma mark -
+#pragma mark Add Tab Button
+
+- (NSImage *)addTabButtonImage
+{
+ return _addTabButtonImage;
+}
+
+- (NSImage *)addTabButtonPressedImage
+{
+ return _addTabButtonPressedImage;
+}
+
+- (NSImage *)addTabButtonRolloverImage
+{
+ return _addTabButtonRolloverImage;
+}
+
+#pragma mark -
+#pragma mark Cell Specific
+
+- (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell orientation:(PSMTabBarOrientation)orientation
+{
+ NSRect dragRect = [cell frame];
+ dragRect.size.width++;
+ return dragRect;
+}
+
+- (NSRect)closeButtonRectForTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)cellFrame
+{
+ if ([cell hasCloseButton] == NO) {
+ return NSZeroRect;
+ }
+
+ NSRect result;
+ result.size = [unifiedCloseButton size];
+ result.origin.x = cellFrame.origin.x + MARGIN_X;
+ result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
+
+ return result;
+}
+
+- (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell
+{
+ NSRect cellFrame = [cell frame];
+
+ if ([cell hasIcon] == NO) {
+ return NSZeroRect;
+ }
+
+ NSRect result;
+ result.size = NSMakeSize(kPSMTabBarIconWidth, kPSMTabBarIconWidth);
+ result.origin.x = cellFrame.origin.x + MARGIN_X;
+ result.origin.y = cellFrame.origin.y + MARGIN_Y - 1.0;
+
+ if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ result.origin.x += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
+ }
+
+ return result;
+}
+
+- (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell
+{
+ NSRect cellFrame = [cell frame];
+
+ if ([[cell indicator] isHidden]) {
+ return NSZeroRect;
+ }
+
+ NSRect result;
+ result.size = NSMakeSize(kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth);
+ result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - kPSMTabBarIndicatorWidth;
+ result.origin.y = cellFrame.origin.y + MARGIN_Y - 1.0;
+
+ return result;
+}
+
+- (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell
+{
+ NSRect cellFrame = [cell frame];
+
+ if ([cell count] == 0) {
+ return NSZeroRect;
+ }
+
+ CGFloat countWidth = [[self attributedObjectCountValueForTabCell:cell] size].width;
+ countWidth += (2 * kPSMUnifiedObjectCounterRadius - 6.0);
+ if (countWidth < kPSMUnifiedCounterMinWidth) {
+ countWidth = kPSMUnifiedCounterMinWidth;
+ }
+
+ NSRect result;
+ result.size = NSMakeSize(countWidth, 2 * kPSMUnifiedObjectCounterRadius); // temp
+ result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - result.size.width;
+ result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
+
+ if (![[cell indicator] isHidden]) {
+ result.origin.x -= kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding;
+ }
+
+ return result;
+}
+
+- (CGFloat)minimumWidthOfTabCell:(PSMTabBarCell *)cell
+{
+ CGFloat resultWidth = 0.0;
+
+ // left margin
+ resultWidth = MARGIN_X;
+
+ // close button?
+ if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ resultWidth += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
+ }
+
+ // icon?
+ if ([cell hasIcon]) {
+ resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding;
+ }
+
+ // the label
+ resultWidth += kPSMMinimumTitleWidth;
+
+ // object counter?
+ if ([cell count] > 0) {
+ resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding;
+ }
+
+ // indicator?
+ if ([[cell indicator] isHidden] == NO) {
+ resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth;
+ }
+
+ // right margin
+ resultWidth += MARGIN_X;
+
+ return ceil(resultWidth);
+}
+
+- (CGFloat)desiredWidthOfTabCell:(PSMTabBarCell *)cell
+{
+ CGFloat resultWidth = 0.0;
+
+ // left margin
+ resultWidth = MARGIN_X;
+
+ // close button?
+ if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ resultWidth += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
+ }
+
+ // icon?
+ if ([cell hasIcon]) {
+ resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding;
+ }
+
+ // the label
+ resultWidth += [[cell attributedStringValue] size].width;
+
+ // object counter?
+ if ([cell count] > 0) {
+ resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding;
+ }
+
+ // indicator?
+ if ([[cell indicator] isHidden] == NO) {
+ resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth;
+ }
+
+ // right margin
+ resultWidth += MARGIN_X;
+
+ return ceil(resultWidth);
+}
+
+- (CGFloat)tabCellHeight
+{
+ return kPSMTabBarControlHeight;
+}
+
+#pragma mark -
+#pragma mark Cell Values
+
+- (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell
+{
+ NSString *contents = [NSString stringWithFormat:@"%lu", (unsigned long)[cell count]];
+ return [[NSMutableAttributedString alloc] initWithString:contents attributes:_objectCountStringAttributes];
+}
+
+- (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell
+{
+ NSMutableAttributedString *attrStr;
+ NSString *contents = [cell stringValue];
+ attrStr = [[NSMutableAttributedString alloc] initWithString:contents];
+ NSRange range = NSMakeRange(0, [contents length]);
+
+ [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
+
+ // Paragraph Style for Truncating Long Text
+ static NSMutableParagraphStyle *TruncatingTailParagraphStyle = nil;
+ if (!TruncatingTailParagraphStyle) {
+ TruncatingTailParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
+ [TruncatingTailParagraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
+ }
+ [attrStr addAttribute:NSParagraphStyleAttributeName value:TruncatingTailParagraphStyle range:range];
+
+ return attrStr;
+}
+
+#pragma mark -
+#pragma mark---- drawing ----
+
+- (void)drawTabCell:(PSMTabBarCell *)cell
+{
+ NSRect cellFrame = [cell frame];
+
+ NSToolbar *toolbar = [[[cell controlView] window] toolbar];
+ BOOL showsBaselineSeparator = (toolbar && [toolbar respondsToSelector:@selector(showsBaselineSeparator)] && [toolbar showsBaselineSeparator]);
+ if (!showsBaselineSeparator) {
+ cellFrame.origin.y += 1.0;
+ cellFrame.size.height -= 1.0;
+ }
+
+ NSColor *lineColor = nil;
+ NSBezierPath *bezier = [NSBezierPath bezierPath];
+ lineColor = [NSColor colorWithCalibratedWhite:0.576 alpha:1.0];
+
+ if (!showsBaselineSeparator || [cell state] == NSOnState) {
+ // selected tab
+ NSRect aRect = NSMakeRect(cellFrame.origin.x + 0.5, cellFrame.origin.y - 0.5, cellFrame.size.width, cellFrame.size.height);
+
+ // frame
+ CGFloat radius = MIN(6.0, 0.5f * MIN(NSWidth(aRect), NSHeight(aRect)));
+ NSRect rect = NSInsetRect(aRect, radius, radius);
+
+ [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0];
+
+ [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0];
+
+ NSPoint cornerPoint = NSMakePoint(NSMaxX(aRect), NSMaxY(aRect));
+ [bezier appendBezierPathWithPoints:&cornerPoint count:1];
+
+ cornerPoint = NSMakePoint(NSMinX(aRect), NSMaxY(aRect));
+ [bezier appendBezierPathWithPoints:&cornerPoint count:1];
+
+ [bezier closePath];
+
+ //[[NSColor windowBackgroundColor] set];
+ //[bezier fill];
+ if ([NSApp isActive]) {
+ if ([cell state] == NSOnState) {
+ [bezier linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.99 alpha:1.0]
+ endColor:[NSColor colorWithCalibratedWhite:0.941 alpha:1.0]];
+ } else if ([cell isHighlighted]) {
+ [bezier linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.80 alpha:1.0]
+ endColor:[NSColor colorWithCalibratedWhite:0.80 alpha:1.0]];
+ } else {
+ [bezier linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0]
+ endColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]];
+ }
+ }
+
+ [lineColor set];
+ [bezier stroke];
+ } else {
+ // unselected tab
+ NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height);
+ aRect.origin.y += 0.5;
+ aRect.origin.x += 1.5;
+ aRect.size.width -= 1;
+
+ aRect.origin.x -= 1;
+ aRect.size.width += 1;
+
+ // rollover
+ if ([cell isHighlighted]) {
+ [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set];
+ NSRectFillUsingOperation(aRect, NSCompositingOperationSourceAtop);
+ }
+
+ // frame
+
+ [lineColor set];
+ [bezier moveToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y - 0.5)];
+ if (!([cell tabState] & PSMTab_RightIsSelectedMask)) {
+ [bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMaxY(aRect))];
+ }
+
+ [bezier stroke];
+
+ // Create a thin lighter line next to the dividing line for a bezel effect
+ if (!([cell tabState] & PSMTab_RightIsSelectedMask)) {
+ [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set];
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(aRect) + 1.0, aRect.origin.y - 0.5)
+ toPoint:NSMakePoint(NSMaxX(aRect) + 1.0, NSMaxY(aRect) - 2.5)];
+ }
+
+ // If this is the leftmost tab, we want to draw a line on the left, too
+ if ([cell tabState] & PSMTab_PositionLeftMask) {
+ [lineColor set];
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x, aRect.origin.y - 0.5)
+ toPoint:NSMakePoint(aRect.origin.x, NSMaxY(aRect) - 2.5)];
+ [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set];
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x + 1.0, aRect.origin.y - 0.5)
+ toPoint:NSMakePoint(aRect.origin.x + 1.0, NSMaxY(aRect) - 2.5)];
+ }
+ }
+
+ [self drawInteriorWithTabCell:cell inView:[cell controlView]];
+}
+
+- (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView *)controlView
+{
+ NSRect cellFrame = [cell frame];
+ CGFloat labelPosition = cellFrame.origin.x + MARGIN_X;
+
+ // close button
+ if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
+ NSSize closeButtonSize = NSZeroSize;
+ NSRect closeButtonRect = [cell closeButtonRectForFrame:cellFrame];
+ NSImage *closeButton = nil;
+
+ closeButton = [cell isEdited] ? unifiedCloseDirtyButton : unifiedCloseButton;
+
+ if ([cell closeButtonOver]) {
+ closeButton = [cell isEdited] ? unifiedCloseDirtyButtonOver : unifiedCloseButtonOver;
+ }
+ if ([cell closeButtonPressed]) {
+ closeButton = [cell isEdited] ? unifiedCloseDirtyButtonDown : unifiedCloseButtonDown;
+ }
+
+ closeButtonSize = [closeButton size];
+ if ([controlView isFlipped]) {
+ closeButtonRect.origin.y += closeButtonRect.size.height;
+ }
+
+ [closeButton drawAtPoint:closeButtonRect.origin fromRect:CGRectZero operation:NSCompositingOperationSourceOver fraction:1.0];
+
+ // scoot label over
+ labelPosition += closeButtonSize.width + kPSMTabBarCellPadding;
+ }
+
+ // icon
+ if ([cell hasIcon]) {
+ NSRect iconRect = [self iconRectForTabCell:cell];
+ NSImage *icon = [[[cell representedObject] identifier] icon];
+ if ([controlView isFlipped]) {
+ iconRect.origin.y += iconRect.size.height;
+ }
+
+ // center in available space (in case icon image is smaller than kPSMTabBarIconWidth)
+ if ([icon size].width < kPSMTabBarIconWidth) {
+ iconRect.origin.x += (kPSMTabBarIconWidth - [icon size].width) / 2.0;
+ }
+ if ([icon size].height < kPSMTabBarIconWidth) {
+ iconRect.origin.y -= (kPSMTabBarIconWidth - [icon size].height) / 2.0;
+ }
+
+ [icon drawAtPoint:iconRect.origin fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
+
+ // scoot label over
+ labelPosition += iconRect.size.width + kPSMTabBarCellPadding;
+ }
+
+ // label rect
+ NSRect labelRect;
+ labelRect.origin.x = labelPosition;
+ labelRect.size.width = cellFrame.size.width - (labelRect.origin.x - cellFrame.origin.x) - kPSMTabBarCellPadding;
+ NSSize s = [[cell attributedStringValue] size];
+ labelRect.origin.y = cellFrame.origin.y + (cellFrame.size.height - s.height) / 2.0 - 1.0;
+ labelRect.size.height = s.height;
+
+ if (![[cell indicator] isHidden]) {
+ labelRect.size.width -= (kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding);
+ }
+
+ // object counter
+ if ([cell count] > 0) {
+ [[cell countColor] ?: [NSColor colorWithCalibratedWhite:0.3 alpha:0.6] set];
+ NSBezierPath *path = [NSBezierPath bezierPath];
+ NSRect myRect = [self objectCounterRectForTabCell:cell];
+ myRect.origin.y -= 1.0;
+ [path moveToPoint:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y)];
+ [path lineToPoint:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMUnifiedObjectCounterRadius, myRect.origin.y)];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMUnifiedObjectCounterRadius, myRect.origin.y + kPSMUnifiedObjectCounterRadius) radius:kPSMUnifiedObjectCounterRadius startAngle:270.0 endAngle:90.0];
+ [path lineToPoint:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y + myRect.size.height)];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y + kPSMUnifiedObjectCounterRadius) radius:kPSMUnifiedObjectCounterRadius startAngle:90.0 endAngle:270.0];
+ [path fill];
+
+ // draw attributed string centered in area
+ NSRect counterStringRect;
+ NSAttributedString *counterString = [self attributedObjectCountValueForTabCell:cell];
+ counterStringRect.size = [counterString size];
+ counterStringRect.origin.x = myRect.origin.x + ((myRect.size.width - counterStringRect.size.width) / 2.0) + 0.25;
+ counterStringRect.origin.y = myRect.origin.y + ((myRect.size.height - counterStringRect.size.height) / 2.0) + 0.5;
+ [counterString drawInRect:counterStringRect];
+
+ labelRect.size.width -= myRect.size.width + kPSMTabBarCellPadding;
+ }
+
+ // label
+ [[cell attributedStringValue] drawInRect:labelRect];
+}
+
+- (void)drawBackgroundInRect:(NSRect)rect
+{
+ //Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area
+ rect = [tabBar bounds];
+
+ NSRect gradientRect = rect;
+ gradientRect.size.height -= 1.0;
+
+ NSBezierPath *path = [NSBezierPath bezierPathWithRect:gradientRect];
+ [path linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0]
+ endColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]];
+ [[NSColor colorWithCalibratedWhite:0.576 alpha:1.0] set];
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x, NSMaxY(rect) - 0.5)
+ toPoint:NSMakePoint(NSMaxX(rect), NSMaxY(rect) - 0.5)];
+
+ if (![[[tabBar tabView] window] isKeyWindow]) {
+ [[NSColor windowBackgroundColor] set];
+ NSRectFill(gradientRect);
+ }
+}
+
+- (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect
+{
+ tabBar = bar;
+ [self drawBackgroundInRect:rect];
+
+ // no tab view == not connected
+ if (![bar tabView]) {
+ NSRect labelRect = rect;
+ labelRect.size.height -= 4.0;
+ labelRect.origin.y += 4.0;
+ NSMutableAttributedString *attrStr;
+ NSString *contents = @"PSMTabBarControl";
+ attrStr = [[NSMutableAttributedString alloc] initWithString:contents];
+ NSRange range = NSMakeRange(0, [contents length]);
+ [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
+ NSMutableParagraphStyle *centeredParagraphStyle = nil;
+ if (!centeredParagraphStyle) {
+ centeredParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
+ [centeredParagraphStyle setAlignment:NSTextAlignmentCenter];
+ }
+ [attrStr addAttribute:NSParagraphStyleAttributeName value:centeredParagraphStyle range:range];
+ [attrStr drawInRect:labelRect];
+ return;
+ }
+
+ // draw cells
+ NSEnumerator *e = [[bar cells] objectEnumerator];
+ PSMTabBarCell *cell;
+ while ((cell = [e nextObject])) {
+ if ([bar isAnimating] || (![cell isInOverflowMenu] && NSIntersectsRect([cell frame], rect))) {
+ [cell drawWithFrame:[cell frame] inView:bar];
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Archiving
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ //[super encodeWithCoder:aCoder];
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject:unifiedCloseButton forKey:@"unifiedCloseButton"];
+ [aCoder encodeObject:unifiedCloseButtonDown forKey:@"unifiedCloseButtonDown"];
+ [aCoder encodeObject:unifiedCloseButtonOver forKey:@"unifiedCloseButtonOver"];
+ [aCoder encodeObject:unifiedCloseDirtyButton forKey:@"unifiedCloseDirtyButton"];
+ [aCoder encodeObject:unifiedCloseDirtyButtonDown forKey:@"unifiedCloseDirtyButtonDown"];
+ [aCoder encodeObject:unifiedCloseDirtyButtonOver forKey:@"unifiedCloseDirtyButtonOver"];
+ [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"];
+ [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"];
+ [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ // self = [super initWithCoder:aDecoder];
+ //if (self) {
+ if ([aDecoder allowsKeyedCoding]) {
+ unifiedCloseButton = [aDecoder decodeObjectForKey:@"unifiedCloseButton"];
+ unifiedCloseButtonDown = [aDecoder decodeObjectForKey:@"unifiedCloseButtonDown"];
+ unifiedCloseButtonOver = [aDecoder decodeObjectForKey:@"unifiedCloseButtonOver"];
+ unifiedCloseDirtyButton = [aDecoder decodeObjectForKey:@"unifiedCloseDirtyButton"];
+ unifiedCloseDirtyButtonDown = [aDecoder decodeObjectForKey:@"unifiedCloseDirtyButtonDown"];
+ unifiedCloseDirtyButtonOver = [aDecoder decodeObjectForKey:@"unifiedCloseDirtyButtonOver"];
+ _addTabButtonImage = [aDecoder decodeObjectForKey:@"addTabButtonImage"];
+ _addTabButtonPressedImage = [aDecoder decodeObjectForKey:@"addTabButtonPressedImage"];
+ _addTabButtonRolloverImage = [aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"];
+ }
+ //}
+ return self;
+}
+
+@end
diff --git a/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/TXT.rtf b/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/TXT.rtf
new file mode 100644
index 0000000..acd9372
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/TXT.rtf
@@ -0,0 +1,186 @@
+{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf380
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fswiss\fcharset77 Helvetica;\f2\fswiss\fcharset77 Helvetica-Oblique;
+\f3\fnil\fcharset77 Monaco;}
+{\colortbl;\red255\green255\blue255;\red118\green15\blue80;\red0\green0\blue255;\red35\green110\blue37;
+}
+{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid0\'02\'05.;}{\levelnumbers\'01;}}{\listname ;}\listid1}}
+{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}}
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\qc\pardirnatural
+
+\f0\b\fs24 \cf0 \
+PSMTabBarControl (and related classes)\
+
+\f1\b0 developed by John Pannell, Positive Spin Media\
+\
+as seen in the super-cool app...\
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\qc\pardirnatural
+\cf0 {{\NeXTGraphic startpage.gif \width7200 \height2820
+}}\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\qc\pardirnatural
+\cf0 \
+\
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural
+\cf0 This source code and all related materials are released under the BSD license, which is explained at the end of this document, along with some other legalese. I've made my best effort to make everything bug free, but please let me know of any bugs found or suggestions you have: johnp@positivespinmedia.com.\
+\
+
+\f0\b Purpose
+\f1\b0 \
+\
+PSMTabBarControl seeks to provide developers with a high-quality, easy to use GUI to manage an NSTabView (or subclasses) in a manner similar to Safari's tabbed browsing implementation. It attempts to add a few features as well. Here's what you get:\
+\
+
+\f0\b The look:
+\f1\b0 a control/cell architecture that draws the expected tab appearance below a toolbar or similar view. Included styles work consistently in Aqua, Metal, or customized metal variations by basing fills on the window's background color. Includes drawing of a close button, and rollover states for the close button and tab cell. Also provides pop-up button and menu when tabs overflow available space, and support for individual tab progress indicators, icons, and object counters. Tabs can be drawn sized to fit the string content of the label, or uniformly sized.\
+\
+
+\f0\b The functionality:
+\f1\b0 Close button removes tabs, click on a tab cell selects. Indicators start, stop, and hide if things are hooked up correctly.\
+\
+
+\f0\b Extras:
+\f1\b0 Supports multi-window drag-and-drop reordering of the tabs with aqua-licious animation.\
+\
+
+\f0\b Files
+\f1\b0 \
+\
+Your project will need the files in the "Framework" folder of the project. The actual framework packages these (and some images) up nicely for you, if desired. Please look over the "TabBarControlDemo" target of the source code project to see exactly what is needed to get everything to build. Building and playing with the demo is also a good way to get a feel for the features provided by these classes.\
+\
+
+\f0\b Usage
+\f1\b0 \
+\
+Simply drag a custom view object from the views palette in IB, read the PSMTabBarControl class into IB, and set the view's custom class to PSMTabBarControl. Then connect the control's tabview outlet to the tab view being controlled, and make the control the delegate of the tab view. You can also connect the control's "partner view" outlet to another view that will resize in response to the hide/show behavior of the control.\
+\
+Alternately, you can build the Palette subproject and add the built IB palette to Interface Builder. In this case, creating and configuring an instance is as easy and drag, drop, and a few clicks. A demo movie and the built palette are available in a separate download from my website: http://www.positivespinmedia.com/dev/PSMTabBarControl.html\
+\
+
+\f2\i Please read the PSMTabBarControlDoc.html file in the documentation folder of this project. It provides an Apple-ish page describing the interface and usage of this object.
+\f1\i0 \
+\
+
+\f0\b Patterns of Use
+\f1\b0 \
+\
+There are a few random notes I can think of for usage guidelines...\
+\
+- You may see a line between the toolbar and the control in your app; it is part of the toolbar. In Tiger, you can eliminate the appearance of this line:\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f3\fs20 \cf0 \CocoaLigature0 SInt32 MacVersion;\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+\cf2 if\cf0 (Gestalt(gestaltSystemVersion, &MacVersion) == noErr)\{\
+ \cf2 if\cf0 (MacVersion >= \cf3 0x1040\cf0 )\{\
+ \cf4 // this call is Tiger only\cf0 \
+ [toolbar setShowsBaselineSeparator:\cf2 NO\cf0 ];\
+ \}\
+\}\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f1\fs24 \cf0 - In general, there is no reason for your app objects to communicate (outside of configuration) with the PSMTabBarControl at all. Changes made to the NSTabView instance programmatically should be directed at the NSTabView instance itself, and the control will update to reflect the changes made.\
+\
+- Your app might want to receive tab view delegate notifications in order to perform some actions. No problem, simply make the desired object the delegate of the PSMTabBarControl instance... it passes along all tab view notifications. Note that it uses these notifications to make changes itself - read the source code to make sure you aren't tripping over something.\
+\
+- The control creates bindings between each cell's progress indicator and the represented NSTabViewItem's identifier object, if it can. In my app design, I set an instance of NSObjectController as the NSTabViewItem's identifier, and then bind to the "isProcessing" key of the controller's content object. All of this can be seen in the source of the demo app...\
+\
+- The control can be set to hide itself when there is only a single tab, and can also be told to hide/show on demand. It can animate to appear and disappear, and will resize something to compensate for the missing window real estate. By default, it will resize the window, but you can also connect the "partnerView" outlet in IB to specify another view to resize to take up the missing space. Note that this takes some attention to sizing springs and wires to get right, and complex views may need a container view to achieve the desired effect.\
+\
+- The control can be configured to draw an attractive "Add Tab" button at the end of the tab cells. Unfortunately, the button is all looks and no brains - it has no idea what your app wants to do when adding a tab. If you configure your app to show the add tab button, you need to hook up the add tab button to the proper target with the proper selector. Something like this will do nicely in your app controller's awakeFromNib:\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f3\fs20 \cf4 // hook up add tab button\cf0 \
+[[tabBar addTabButton] setTarget:\cf2 self\cf0 ];\
+[[tabBar addTabButton] setAction:\cf2 @selector\cf0 (addNewTab:)];
+\f1\fs24 \
+\
+- The tabs have some sizing options: You can specify the minimum width, maximum width, and optimum width, as well as spcifying if the tabs should size to fit their label or not. The sizing bahavior of the tabs is as follows: If "size to fit" is specified, then tabs will be generated to fit the label, but will never exceed the specified max or min widths. Once the end of the control is reached, the overflow menu will appear as tabs are added; the last tab will squeeze in if it can, or the remaining tabs will stretch to occupy the full control. If "size to fit" is not specified, then all successive tabs will appear at the optimum width. Once the end of the control is reached, adding new tabs will cause all tabs to shrink to accomodate, until the minumum width is reached, and then the overflow menu will be used; max width is ignored in this case. Hopefully that all makes sense :-)\
+\
+- PSMTabBarControl will load the existing tabs from the tabView outlet at startup. However, many of the advanced features (icon display, progress indicator, object count) rely on binding to a controller that is likely not set up in IB. Solution? Nuke the existing tabs in the NSTabView and add new ones, configured the way you like. The demo app does this in the awakeFromNib: method of the app controller.\
+\
+- As a design choice, I elected to keep a cell object around until its tab was closed, instead of "churning" cell objects in each update cycle. Each cell keeps its NSTabViewItem as its representedObject and maintains reference that way, rather than by any index. As a result of this, drag-and-drop reordering of tabs does not change the underlying NSTabView instance at all. All that to say: don't rely on numerical indices if communicating with both the control and the tab view - the indices may not correlate if the user moved some tabs around (and remember - you shouldn't need to communicate with the control anyway :-). The Shiira Project, from which I gained much insight and inspiration from for this UI element, elected to scrap and rebuild the array of cells each time through the update cycle, and rely on indices to correlate between cells and NSTabViewItems. I felt the representedObject route was cleaner, and preferred not to churn objects.\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f0\b \cf0 Improvements?
+\f1\b0 \
+\
+Pipe up if you think of something you'd like to see; here's my current list:\
+\
+- "Pop-up" tabs - like pop-up folders in the finder, in case you want to drag to a destination in another tab.\
+- Support for the
+\f3\fs22 \CocoaLigature1 NSUnifiedTitleAndToolbarWindowMask
+\f1\fs24 \CocoaLigature0 "unified" window appearance. (Help! I really searched around to try to make this work... the color pattern of the title and toolbar seem to be top secret! The new "unified" style is an excellent replication of a unified look, but isn't "built from" the unified appearance like the metal is.)\
+- During multi-window drag, having a "drag window/image" that shows the represented view getting moved to the other window.\
+- During multi-window drag, support for dragging out solo tabs to consolidate in another window, removing the source window in the process.\
+- Support vertical as well as horizontal alignment.\
+\
+
+\f0\b Version History
+\f1\b0 \
+\
+Version 1.3 (May 29, 2006)\
+- new feature: Unified tab style, compliments of Keith Blount\
+- new feature: allow multi-window drag config option (again from Keith).\
+- fixed bug: Palette installation/usage instructions were wrong.\
+- enhancement: exposed the
+\f3\fs20 representedTabViewItems
+\f1\fs24 method, which can be used to retrieve the order of the tabs as displayed in the control, since the underlying NSTabView does not get reordered during drag and drop rearrangement.\
+\
+Version 1.2 (April 20, 2006)\
+- new feature: multi-window drag and drop support.\
+- bug fixed: zombie issue with tabView:didCloseTabViewItem\
+- bugs fixed: some drawing issues around the progress indicators in tabs, and the add tab button.\
+- enhancement: the hide/show animation has been improved with less "flickering" of progress indicators during the hide and show.\
+\
+Version 1.1.2 (April 5, 2006)\
+- fixed bug: tabs of non-integer width resulted in occasional anti-aliased drawing issues of dividers between tabs in the Metal style (Thanks, Kent).\
+- added feature: delegate can now respond to -tabView:shouldCloseTabViewItem: and -tabView:willCloseTabViewItem:, and -tabView:didCloseTabViewItem: messages, so your app can take care of any needed setup/cleanup for these actions.\
+- fixed bug: tab close buttons now show down state when pressed down.\
+\
+Version 1.1.1 (March 16, 2006)\
+- fixed bug: Palette inspector would not reflect state of previously instantiated control. This has been fixed (Thanks, Guillaume).\
+- enhancement: Overflow button now highlights when mouse down (Thanks, Kent).\
+- fixed bug: when set to not close a solo tab, the close button would be hidden for the tab, but could still be closed if you clicked the tab in the right location. This has been fixed (Thanks, malcom).\
+\
+Version 1.1 (March 10, 2006)\
+- Bound the "title" of the cell to the "label" of the source tabview item. Just in case you wanted to change the label on the tab during the running of your application.\
+- PSMTabBarCell factored to support new tab "styles", or appearances in drawing. Now supported are the existing "Metal" style and a new "Aqua" style. Many thanks to David Smith, Seth Willits, and Chris Forsythe for their contributions!\
+- Control can be configured to "Hide for single tab", so it doesn't appear unless there are more than a single tab view present. Features animated show/hide behavior (that can be called anytime, and is called automatically in the case that a single tab exists). The show/hide behavior can also be set up to resize either the window (default) or a selected "partner view" to compensate for the lost height of the tab bar.\
+- Control can be configured for "Can close only tab" behavior. If set to NO, no close button will appear on a lone tab.\
+- Cells can be set to "size to fit", or given uniform min/max/optimum sizes.\
+- Added support for display of an icon and an object count, if the proper app design pattern is followed.\
+- Sweet animated drag-and-drop drawing!\
+- A few drawing bugs surrounding the progress indicators in cells were squished.\
+- New documentation, in case you found this read me a little pithy.\
+\
+Version 1.0 (December 2005)\
+Initial release of safari-like tab implementation.\
+\
+
+\f0\b The standard disavowal of this beautiful mess
+\f1\b0 \
+\
+I should note that portions of this source code were inspired by the Shiira project's implementation of Safari-style tabs. While I made some different design decisions, the drawing and some other aspects are only slight modifications of their excellent work. As such, I note their copyright under their BSD licence:\
+\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f3\fs20 \cf4 Portions of this software Copyright 2004 The Shiira Project. All rights reserved.\
+\pard\tx960\tx1920\tx2880\tx3840\tx4800\tx5760\tx6720\tx7680\tx8640\tx9600\tx10560\tx11520\tx12480\tx13440\tx14400\tx15360\tx16320\tx17280\tx18240\tx19200\tx20160\tx21120\tx22080\tx23040\tx24000\tx24960\tx25920\tx26880\tx27840\tx28800\tx29760\tx30720\tx31680\tx32640\tx33600\tx34560\tx35520\tx36480\tx37440\tx38400\tx39360\tx40320\tx41280\tx42240\tx43200\tx44160\tx45120\tx46080\tx47040\tx48000\tx48960\tx49920\tx50880\tx51840\tx52800\tx53760\tx54720\tx55680\tx56640\tx57600\tx58560\tx59520\tx60480\tx61440\tx62400\tx63360\tx64320\tx65280\tx66240\tx67200\tx68160\tx69120\tx70080\tx71040\tx72000\tx72960\tx73920\tx74880\tx75840\tx76800\tx77760\tx78720\tx79680\tx80640\tx81600\tx82560\tx83520\tx84480\tx85440\tx86400\tx87360\tx88320\tx89280\tx90240\tx91200\tx92160\tx93120\tx94080\tx95040\tx96000\ql\qnatural\pardirnatural
+
+\f1\fs24 \cf0 Check them out at: http://hmdt-web.net/shiira/\
+\
+This source code is provided under BSD license, the conditions of which are listed below. I hope you'll make note somewhere in your about window or ReadMe stating the sweet coding goodness of Positive Spin Media and link to the fascinating and informative website at www.positivespinmedia.com\
+\
+\pard\pardeftab720\sa320\ql\qnatural
+\cf0 \CocoaLigature1 Copyright (c) 2005, Positive Spin Media\uc0\u8232 All rights reserved.\
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\
+\pard\tx220\tx720\pardeftab720\li720\fi-720\ql\qnatural
+\ls1\ilvl0\cf0 {\listtext \'a5 }Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\
+{\listtext \'a5 }Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\
+{\listtext \'a5 }Neither the name of Positive Spin Media nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\
+ \
+\pard\pardeftab720\sa320\ql\qnatural
+\cf0 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\CocoaLigature0 \
+} \ No newline at end of file
diff --git a/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/startpage.gif b/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/startpage.gif
new file mode 100644
index 0000000..8707a77
--- a/dev/null
+++ b/frontends/cocoa/PSMTabBarControl/ReadMe.rtfd/startpage.gif
Binary files differ
diff --git a/frontends/cocoa/PreferencesWindowController.h b/frontends/cocoa/PreferencesWindowController.h
new file mode 100644
index 0000000..02fb530
--- a/dev/null
+++ b/frontends/cocoa/PreferencesWindowController.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@interface PreferencesWindowController : NSWindowController
+
+@property (readwrite, copy, nonatomic) NSString *homepageURL;
+
+- (IBAction)useCurrentPageAsHomepage:(id)sender;
+
+@end
diff --git a/frontends/cocoa/PreferencesWindowController.m b/frontends/cocoa/PreferencesWindowController.m
new file mode 100644
index 0000000..d654253
--- a/dev/null
+++ b/frontends/cocoa/PreferencesWindowController.m
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "utils/nsoption.h"
+#import "utils/nsurl.h"
+#import "netsurf/browser_window.h"
+
+#import "cocoa/PreferencesWindowController.h"
+#import "cocoa/NetsurfApp.h"
+#import "cocoa/gui.h"
+#import "cocoa/BrowserViewController.h"
+
+@implementation PreferencesWindowController
+
+- (instancetype)init
+{
+ if ((self = [super initWithWindowNibName:@"PreferencesWindow"]) == nil)
+ return nil;
+
+ return self;
+}
+
+- (IBAction)useCurrentPageAsHomepage:(id)sender
+{
+ struct browser_window *bw = [[(NetSurfApp *)NSApp frontTab] browser];
+ const char *url = nsurl_access(browser_window_get_url(bw));
+ [self setHomepageURL:[NSString stringWithUTF8String:url]];
+}
+
+- (void)setHomepageURL:(NSString *)newUrl
+{
+ nsoption_set_charp(homepage_url, strdup([newUrl UTF8String]));
+ [[NSUserDefaults standardUserDefaults] setObject:newUrl forKey:kHomepageURLOption];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+- (NSString *)homepageURL
+{
+ return [NSString stringWithUTF8String:nsoption_charp(homepage_url)];
+}
+
+@end
diff --git a/frontends/cocoa/Prefix.pch b/frontends/cocoa/Prefix.pch
new file mode 100644
index 0000000..7fa2c05
--- a/dev/null
+++ b/frontends/cocoa/Prefix.pch
@@ -0,0 +1,11 @@
+#include <Carbon/Carbon.h>
+
+#ifdef __OBJC__
+#import <Cocoa/Cocoa.h>
+#endif
+
+#undef offsetof
+
+#define HISTORY_COLOUR_BACKGROUND 0x000000
+#define HISTORY_COLOUR_FOREGROUND 0xFFFFFF
+#define HISTORY_COLOUR_SELECTED 0xFF6D27 \ No newline at end of file
diff --git a/frontends/cocoa/ScrollableView.h b/frontends/cocoa/ScrollableView.h
new file mode 100644
index 0000000..f8605f1
--- a/dev/null
+++ b/frontends/cocoa/ScrollableView.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@interface ScrollableView : NSView
+
+@property (nonatomic) NSSize minimumSize;
+
+- (void)adjustFrame;
+
+@end
diff --git a/frontends/cocoa/ScrollableView.m b/frontends/cocoa/ScrollableView.m
new file mode 100644
index 0000000..c495b1d
--- a/dev/null
+++ b/frontends/cocoa/ScrollableView.m
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/ScrollableView.h"
+
+@interface ScrollableView ()
+
+@property (weak, nonatomic) NSView *observedSuperview;
+
+- (void)frameChangeNotification:(NSNotification *)note;
+
+@end
+
+@implementation ScrollableView
+
+- (void)setMinimumSize:(NSSize)newSize
+{
+ if (!CGSizeEqualToSize(_minimumSize, newSize)) {
+ _minimumSize = newSize;
+ [self adjustFrame];
+ }
+}
+
+- (void)adjustFrame
+{
+ NSSize frameSize = [[self superview] frame].size;
+ [self setFrameSize:NSMakeSize(MAX(self.minimumSize.width, frameSize.width),
+ MAX(self.minimumSize.height, frameSize.height))];
+}
+
+- (void)frameChangeNotification:(NSNotification *)note
+{
+ [self adjustFrame];
+}
+
+- (void)viewDidMoveToSuperview
+{
+ NSView *oldSuperview = _observedSuperview;
+ _observedSuperview = nil;
+ if (oldSuperview) {
+ [[NSNotificationCenter defaultCenter]
+ removeObserver:self
+ name:NSViewFrameDidChangeNotification
+ object:oldSuperview];
+ }
+
+ NSView *newSuperView = [self superview];
+
+ if (nil != newSuperView) {
+ _observedSuperview = newSuperView;
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(frameChangeNotification:)
+ name:NSViewFrameDidChangeNotification
+ object:newSuperView];
+ newSuperView.postsFrameChangedNotifications = YES;
+ }
+}
+
+@end
diff --git a/frontends/cocoa/SearchWindowController.h b/frontends/cocoa/SearchWindowController.h
new file mode 100644
index 0000000..a1e84c3
--- a/dev/null
+++ b/frontends/cocoa/SearchWindowController.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@class BrowserViewController;
+
+typedef enum {
+ SearchBackward,
+ SearchForward
+} SearchDirection;
+
+@interface SearchWindowController : NSWindowController
+
+@property (nonatomic) BOOL caseSensitive;
+@property (nonatomic) BOOL selectAll;
+@property (nonatomic) BOOL canGoBack;
+@property (nonatomic) BOOL canGoForward;
+@property (copy, nonatomic) NSString *searchString;
+@property (nonatomic) BrowserViewController *browser;
+
+- (IBAction)searchNext:(id)sender;
+- (IBAction)searchPrevious:(id)sender;
+
+- (IBAction)searchStringDidChange:(id)sender;
+
+- (void)search:(SearchDirection)direction;
+
+@end
+
+extern struct gui_search_table *cocoa_search_table;
diff --git a/frontends/cocoa/SearchWindowController.m b/frontends/cocoa/SearchWindowController.m
new file mode 100644
index 0000000..347fb1c
--- a/dev/null
+++ b/frontends/cocoa/SearchWindowController.m
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/SearchWindowController.h"
+#import "cocoa/BrowserViewController.h"
+
+#import "netsurf/search.h"
+#import "netsurf/browser_window.h"
+#import "desktop/search.h"
+
+static void cocoa_search_set_back(bool active, void *p);
+static void cocoa_search_set_forward(bool active, void *p);
+
+static struct gui_search_table search_table = {
+ .forward_state = cocoa_search_set_forward,
+ .back_state = cocoa_search_set_back,
+};
+
+struct gui_search_table *cocoa_search_table = &search_table;
+
+@implementation SearchWindowController
+
+- (instancetype)init
+{
+ if ((self = [super initWithWindowNibName:@"SearchWindow"]) == nil)
+ return nil;
+
+ [self bind:@"browser" toObject:NSApp withKeyPath:@"frontTab" options:nil];
+ _canGoBack = YES;
+ _canGoForward = YES;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [self unbind:@"browser"];
+}
+
+- (IBAction)searchNext:(id)sender
+{
+ [self search:SearchForward];
+}
+
+- (IBAction)searchPrevious:(id)sender
+{
+ [self search:SearchBackward];
+}
+
+- (void)search:(SearchDirection)direction
+{
+ search_flags_t flags = (direction == SearchForward) ? SEARCH_FLAG_FORWARDS : 0;
+
+ if (self.caseSensitive) {
+ flags |= SEARCH_FLAG_CASE_SENSITIVE;
+ }
+
+ if (self.selectAll) {
+ flags |= SEARCH_FLAG_SHOWALL;
+ }
+
+ struct browser_window *bw = self.browser.browser;
+ browser_window_search(bw, (__bridge void *)self, flags, self.searchString.UTF8String);
+}
+
+- (IBAction)searchStringDidChange:(id)sender
+{
+ struct browser_window *bw = self.browser.browser;
+ browser_window_search_clear(bw);
+
+ [self setCanGoBack:YES];
+ [self setCanGoForward:YES];
+}
+
+- (void)setCaseSensitive:(BOOL)newValue
+{
+ if (_caseSensitive != newValue) {
+ _caseSensitive = newValue;
+ self.canGoBack = YES;
+ self.canGoForward = YES;
+ }
+}
+
+- (void)setSelectAll:(BOOL)newValue
+{
+ if (_selectAll != newValue) {
+ _selectAll = newValue;
+ self.canGoBack = YES;
+ self.canGoForward = YES;
+ }
+}
+
+static void cocoa_search_set_back(bool active, void *p)
+{
+ [(__bridge SearchWindowController *)p setCanGoBack:active];
+}
+
+static void cocoa_search_set_forward(bool active, void *p)
+{
+ [(__bridge SearchWindowController *)p setCanGoForward:active];
+}
+
+@end
diff --git a/frontends/cocoa/Tree.h b/frontends/cocoa/Tree.h
new file mode 100644
index 0000000..d898f4e
--- a/dev/null
+++ b/frontends/cocoa/Tree.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+#import "cocoa/desktop-tree.h"
+
+@class Tree;
+
+@protocol TreeDelegate
+
+- (void)tree:(Tree *)tree requestedRedrawInRect:(NSRect)rect;
+- (void)tree:(Tree *)tree resized:(NSSize)size;
+- (void)tree:(Tree *)tree scrollPoint:(NSPoint)point;
+- (NSSize)treeWindowSize:(Tree *)tree;
+
+@end
+
+@interface Tree : NSObject
+
+@property (weak, nonatomic) id<TreeDelegate> delegate;
+@property (readonly) struct tree *tree;
+
+- (instancetype)initWithFlags:(unsigned int)flags;
+
+- (void)setRedrawing:(BOOL)newRedrawing;
+
+@end
+
+@interface Tree (ViewInterface)
+
+- (void)drawRect:(NSRect)rect inView:(NSView *)view;
+- (void)mouseAction:(browser_mouse_state)state atPoint:(NSPoint)point;
+- (void)mouseDragEnd:(browser_mouse_state)state fromPoint:(NSPoint)p0 toPoint:(NSPoint)p1;
+- (void)keyPress:(uint32_t)key;
+
+@end
diff --git a/frontends/cocoa/Tree.m b/frontends/cocoa/Tree.m
new file mode 100644
index 0000000..1b83469
--- a/dev/null
+++ b/frontends/cocoa/Tree.m
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/Tree.h"
+#import "cocoa/coordinates.h"
+#import "cocoa/font.h"
+#import "cocoa/plotter.h"
+
+#import "utils/errors.h"
+#import "netsurf/plotters.h"
+#import "cocoa/desktop-tree.h"
+
+@implementation Tree
+
+static void tree_redraw_request(int x, int y, int w, int h, void *data);
+static void tree_resized(struct tree *tree, int w, int h, void *data);
+static void tree_scroll_visible(int y, int height, void *data);
+static void tree_get_window_dimensions(int *width, int *height, void *data);
+
+static const struct treeview_table cocoa_tree_callbacks = {
+ .redraw_request = tree_redraw_request,
+ .resized = tree_resized,
+ .scroll_visible = tree_scroll_visible,
+ .get_window_dimensions = tree_get_window_dimensions
+};
+
+- (instancetype)initWithFlags:(unsigned int)flags
+{
+ if ((self = [super init]) == nil)
+ return nil;
+
+ _tree = tree_create(flags, &cocoa_tree_callbacks, (__bridge void *)self);
+ if (_tree == NULL) {
+ return nil;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ tree_delete(_tree);
+}
+
+- (void)setRedrawing:(BOOL)newRedrawing
+{
+}
+
+//MARK: -
+//MARK: Callbacks
+
+static void tree_redraw_request(int x, int y, int w, int h, void *data)
+{
+ Tree *tree = (__bridge Tree *)data;
+ [tree.delegate tree:tree requestedRedrawInRect:cocoa_rect_wh(x, y, w, h)];
+}
+
+static void tree_resized(struct tree *tree, int w, int h, void *data)
+{
+ Tree *cocoaTree = (__bridge Tree *)data;
+ [cocoaTree.delegate tree:cocoaTree resized:cocoa_size(w, h)];
+}
+
+static void tree_scroll_visible(int y, int height, void *data)
+{
+ Tree *tree = (__bridge Tree *)data;
+ [tree.delegate tree:tree scrollPoint:cocoa_point(0, y)];
+}
+
+static void tree_get_window_dimensions(int *width, int *height, void *data)
+{
+ Tree *tree = (__bridge Tree *)data;
+ id<TreeDelegate> delegate = tree.delegate;
+
+ if (delegate == nil) {
+ return;
+ }
+
+ NSSize size = [delegate treeWindowSize:tree];
+
+ if (width != NULL) {
+ *width = cocoa_pt_to_px(size.width);
+ }
+
+ if (height != NULL) {
+ *height = cocoa_pt_to_px(size.height);
+ }
+}
+
+@end
+
+@implementation Tree (ViewInterface)
+
+- (void)drawRect:(NSRect)rect inView:(NSView *)view
+{
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &cocoa_plotters
+ };
+
+ tree_draw(self.tree, 0, 0,
+ cocoa_pt_to_px(NSMinX(rect)),
+ cocoa_pt_to_px(NSMinY(rect)),
+ cocoa_pt_to_px(NSWidth(rect)),
+ cocoa_pt_to_px(NSHeight(rect)),
+ &ctx);
+}
+
+- (void)mouseAction:(browser_mouse_state)state atPoint:(NSPoint)point
+{
+ tree_mouse_action(self.tree, state,
+ cocoa_pt_to_px(point.x), cocoa_pt_to_px(point.y));
+}
+
+- (void)mouseDragEnd:(browser_mouse_state)state fromPoint:(NSPoint)p0 toPoint:(NSPoint)p1
+{
+ tree_drag_end(self.tree, state,
+ cocoa_pt_to_px(p0.x), cocoa_pt_to_px(p0.y),
+ cocoa_pt_to_px(p1.x), cocoa_pt_to_px(p1.y));
+}
+
+- (void)keyPress:(uint32_t)key
+{
+ tree_keypress(self.tree, key);
+}
+
+@end
diff --git a/frontends/cocoa/TreeView.h b/frontends/cocoa/TreeView.h
new file mode 100644
index 0000000..518bf5c
--- a/dev/null
+++ b/frontends/cocoa/TreeView.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+#import "cocoa/ScrollableView.h"
+@class Tree;
+
+@interface TreeView : ScrollableView
+
+@property (nonatomic) Tree *tree;
+
+@end
diff --git a/frontends/cocoa/TreeView.m b/frontends/cocoa/TreeView.m
new file mode 100644
index 0000000..8fe3767
--- a/dev/null
+++ b/frontends/cocoa/TreeView.m
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/TreeView.h"
+#import "cocoa/Tree.h"
+
+#import "utils/errors.h"
+
+#import "netsurf/plotters.h"
+#import "netsurf/keypress.h"
+#import "Tree.h"
+
+@interface TreeView () <TreeDelegate>
+
+@property (nonatomic) BOOL isDragging;
+@property (nonatomic) CGPoint dragStart;
+
+@end
+
+@implementation TreeView
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ [self.tree drawRect:dirtyRect inView:self];
+}
+
+- (BOOL)isFlipped
+{
+ return YES;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ return YES;
+}
+
+- (void)setTree:(Tree *)newTree
+{
+ if (_tree != newTree) {
+ _tree.redrawing = NO;
+ _tree.delegate = nil;
+
+ _tree = newTree;
+ _tree.delegate = self;
+ _tree.redrawing = YES;
+
+ [self setNeedsDisplay:YES];
+ }
+}
+
+//MARK: -
+//MARK: Event handlers
+
+- (void)mouseDown:(NSEvent *)event
+{
+ self.isDragging = NO;
+ self.dragStart = [self convertPoint:[event locationInWindow] fromView:nil];
+ [self.tree mouseAction:BROWSER_MOUSE_PRESS_1 atPoint:self.dragStart];
+}
+
+#define squared(x) ((x) * (x))
+#define MinDragDistance (5.0)
+
+- (void)mouseDragged:(NSEvent *)event
+{
+ const NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+ if (!self.isDragging) {
+ const CGFloat distance = squared(self.dragStart.x - point.x) + squared(self.dragStart.y - point.y);
+ if (distance >= squared(MinDragDistance)) {
+ self.isDragging = YES;
+ }
+ }
+}
+
+- (void)mouseUp:(NSEvent *)event
+{
+ const NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+ browser_mouse_state modifierFlags = 0;
+
+ if (self.isDragging) {
+ self.isDragging = NO;
+ [self.tree mouseDragEnd:modifierFlags fromPoint:self.dragStart toPoint:point];
+ } else {
+ modifierFlags |= BROWSER_MOUSE_CLICK_1;
+ if ([event clickCount] == 2) {
+ modifierFlags |= BROWSER_MOUSE_DOUBLE_CLICK;
+ }
+ [self.tree mouseAction:modifierFlags atPoint:point];
+ }
+}
+
+//MARK: Keyboard events
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ [self interpretKeyEvents:@[ theEvent ]];
+}
+
+- (void)insertText:(id)string
+{
+ for (NSUInteger i = 0, length = [string length]; i < length; i++) {
+ unichar ch = [string characterAtIndex:i];
+ [self.tree keyPress:ch];
+ }
+}
+
+- (void)moveLeft:(id)sender
+{
+ [self.tree keyPress:NS_KEY_LEFT];
+}
+
+- (void)moveRight:(id)sender
+{
+ [self.tree keyPress:NS_KEY_RIGHT];
+}
+
+- (void)moveUp:(id)sender
+{
+ [self.tree keyPress:NS_KEY_UP];
+}
+
+- (void)moveDown:(id)sender
+{
+ [self.tree keyPress:NS_KEY_DOWN];
+}
+
+- (void)deleteBackward:(id)sender
+{
+ [self.tree keyPress:NS_KEY_DELETE_LEFT];
+}
+
+- (void)deleteForward:(id)sender
+{
+ [self.tree keyPress:NS_KEY_DELETE_RIGHT];
+}
+
+- (void)cancelOperation:(id)sender
+{
+ [self.tree keyPress:NS_KEY_ESCAPE];
+}
+
+- (void)scrollPageUp:(id)sender
+{
+ [self.tree keyPress:NS_KEY_PAGE_UP];
+}
+
+- (void)scrollPageDown:(id)sender
+{
+ [self.tree keyPress:NS_KEY_PAGE_DOWN];
+}
+
+- (void)insertTab:(id)sender
+{
+ [self.tree keyPress:NS_KEY_TAB];
+}
+
+- (void)insertBacktab:(id)sender
+{
+ [self.tree keyPress:NS_KEY_SHIFT_TAB];
+}
+
+- (void)moveToBeginningOfLine:(id)sender
+{
+ [self.tree keyPress:NS_KEY_LINE_START];
+}
+
+- (void)moveToEndOfLine:(id)sender
+{
+ [self.tree keyPress:NS_KEY_LINE_END];
+}
+
+- (void)moveToBeginningOfDocument:(id)sender
+{
+ [self.tree keyPress:NS_KEY_TEXT_START];
+}
+
+- (void)moveToEndOfDocument:(id)sender
+{
+ [self.tree keyPress:NS_KEY_TEXT_END];
+}
+
+- (void)insertNewline:(id)sender
+{
+ [self.tree keyPress:NS_KEY_NL];
+}
+
+- (void)selectAll:(id)sender
+{
+ [self.tree keyPress:NS_KEY_SELECT_ALL];
+}
+
+- (void)copy:(id)sender
+{
+ [self.tree keyPress:NS_KEY_COPY_SELECTION];
+}
+
+- (void)cut:(id)sender
+{
+ [self.tree keyPress:NS_KEY_CUT_SELECTION];
+}
+
+- (void)paste:(id)sender
+{
+ [self.tree keyPress:NS_KEY_PASTE];
+}
+
+//MARK: -
+//MARK: Tree delegate methods
+
+- (void)tree:(Tree *)t requestedRedrawInRect:(NSRect)rect
+{
+ [self setNeedsDisplayInRect:rect];
+}
+
+- (void)tree:(Tree *)t resized:(NSSize)size
+{
+ self.minimumSize = size;
+}
+
+- (void)tree:(Tree *)t scrollPoint:(NSPoint)point
+{
+ [self scrollPoint:point];
+}
+
+- (NSSize)treeWindowSize:(Tree *)t
+{
+ return self.frame.size;
+}
+
+@end
diff --git a/frontends/cocoa/URLFieldCell.h b/frontends/cocoa/URLFieldCell.h
new file mode 100644
index 0000000..c7e35f0
--- a/dev/null
+++ b/frontends/cocoa/URLFieldCell.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 <Cocoa/Cocoa.h>
+
+@interface URLFieldCell : NSTextFieldCell
+
+@property (nonatomic) SEL refreshAction;
+@property (nonatomic) id refreshTarget;
+@property (nonatomic) NSImage *favicon;
+
+@end
diff --git a/frontends/cocoa/URLFieldCell.m b/frontends/cocoa/URLFieldCell.m
new file mode 100644
index 0000000..54b240d
--- a/dev/null
+++ b/frontends/cocoa/URLFieldCell.m
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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 "cocoa/URLFieldCell.h"
+
+#import "utils/nsurl.h"
+#import "netsurf/url_db.h"
+
+@interface URLFieldCell () <
+ NSPasteboardWriting,
+ NSDraggingSource
+>
+
+@property (nonatomic) NSButtonCell *refreshCell;
+
+- (NSRect)buttonFrame:(NSRect)cellFrame;
+- (NSRect)urlFrame:(NSRect)cellFrame;
+- (NSRect)iconFrame:(NSRect)cellFrame;
+
+@end
+
+@implementation URLFieldCell
+
+@synthesize refreshCell = _refreshCell;
+
+- (void)setFavicon:(NSImage *)newIcon
+{
+ if (_favicon != newIcon) {
+ _favicon = newIcon;
+ [self.controlView setNeedsDisplay:YES];
+ }
+}
+
+#define BUTTON_SIZE 32
+#define PADDING 2
+
+- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
+{
+ [self.favicon drawInRect:[self iconFrame:cellFrame]
+ fromRect:NSZeroRect
+ operation:NSCompositingOperationSourceOver
+ fraction:1.0];
+
+ [super drawInteriorWithFrame:[self urlFrame:cellFrame] inView:controlView];
+
+ [[self refreshCell] drawInteriorWithFrame:[self buttonFrame:cellFrame]
+ inView:controlView];
+}
+
+- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj
+ delegate:(id)anObject
+ start:(NSInteger)selStart
+ length:(NSInteger)selLength
+{
+ const NSRect textFrame = [self urlFrame:aRect];
+ [super selectWithFrame:textFrame
+ inView:controlView
+ editor:textObj
+ delegate:anObject
+ start:selStart
+ length:selLength];
+}
+
+- (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj
+ delegate:(id)anObject
+ event:(NSEvent *)theEvent
+{
+ const NSRect textFrame = [self urlFrame:aRect];
+ [super editWithFrame:textFrame
+ inView:controlView
+ editor:textObj
+ delegate:anObject
+ event:theEvent];
+}
+
+- (void)startDragURLAt:(NSPoint)point inView:(NSView *)view
+{
+ NSString *url = [self stringValue];
+ NSString *title = url;
+ nsurl *nsurl;
+
+ if (nsurl_create([url UTF8String], &nsurl) != NSERROR_OK)
+ return;
+
+ const struct url_data *data = urldb_get_url_data(nsurl);
+
+ nsurl_unref(nsurl);
+
+ if (data && data->title)
+ title = [NSString stringWithUTF8String:data->title];
+
+ NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSDragPboard];
+ [pb declareTypes:[NSArray arrayWithObjects:NSStringPboardType, NSURLPboardType,
+ @"public.url", @"public.url-name", nil]
+ owner:nil];
+ [pb setString:url forType:NSStringPboardType];
+ [pb setString:url forType:@"public.url"];
+ [pb setString:title forType:@"public.url-name"];
+ [[NSURL URLWithString:url] writeToPasteboard:pb];
+
+ NSRect urlBounds = NSZeroRect;
+ urlBounds.size = [title sizeWithAttributes:nil];
+ urlBounds.size.width += urlBounds.size.height + 2;
+
+ NSImage *image = [[NSImage alloc] initWithSize:urlBounds.size];
+
+ [image lockFocus];
+ [self.favicon drawInRect:NSMakeRect(urlBounds.origin.x, urlBounds.origin.y, urlBounds.size.height, urlBounds.size.height)
+ fromRect:NSZeroRect
+ operation:NSCompositingOperationCopy
+ fraction:1.0];
+ urlBounds.origin.x += urlBounds.size.height + 2;
+ [title drawInRect:urlBounds withAttributes:nil];
+ [image unlockFocus];
+
+ point.x -= urlBounds.size.height / 2;
+ point.y += urlBounds.size.height / 2;
+
+ NSDraggingItem *item = [[NSDraggingItem alloc] initWithPasteboardWriter: self];
+ item.imageComponentsProvider = ^NSArray<NSDraggingImageComponent *> * _Nonnull{
+ NSDraggingImageComponent *component = [NSDraggingImageComponent draggingImageComponentWithKey: NSDraggingImageComponentIconKey];
+ component.contents = image;
+ return @[component];
+ };
+
+ [view beginDraggingSessionWithItems:@[item] event:NSApp.currentEvent source:self];
+}
+
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
+{
+ return NSDragOperationCopy | NSDragOperationGeneric;
+}
+
+- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)flag
+{
+ const NSPoint point = [controlView convertPoint:[theEvent locationInWindow] fromView:nil];
+ const NSRect buttonRect = [self buttonFrame:cellFrame];
+ if (NSPointInRect(point, [self iconFrame:cellFrame])) {
+ [self startDragURLAt:point inView:controlView];
+ return NO;
+ } else if (NSPointInRect(point, buttonRect)) {
+ return [[self refreshCell] trackMouse:theEvent
+ inRect:buttonRect
+ ofView:controlView
+ untilMouseUp:flag];
+ } else {
+ cellFrame.size.width -= BUTTON_SIZE + PADDING;
+ return [super trackMouse:theEvent inRect:cellFrame ofView:controlView untilMouseUp:YES];
+ }
+}
+
+- (NSRect)buttonFrame:(NSRect)cellFrame
+{
+ NSRect buttonRect = cellFrame;
+ buttonRect.origin.x = NSMaxX(cellFrame) - BUTTON_SIZE;
+ buttonRect.size.width = BUTTON_SIZE;
+ return buttonRect;
+}
+
+- (NSRect)urlFrame:(NSRect)cellFrame
+{
+ NSRect textFrame = cellFrame;
+ textFrame.origin.x += cellFrame.size.height;
+ textFrame.size.width -= cellFrame.size.height + BUTTON_SIZE + PADDING;
+ return textFrame;
+}
+
+- (NSRect)iconFrame:(NSRect)cellFrame
+{
+ NSRect iconFrame = {
+ .origin = {
+ .x = cellFrame.origin.x + PADDING,
+ .y = cellFrame.origin.y,
+ },
+ .size = NSMakeSize(NSHeight(cellFrame), NSHeight(cellFrame))
+ };
+ return NSInsetRect(iconFrame, 2 * PADDING, 2 * PADDING);
+}
+
+- (NSButtonCell *)refreshCell
+{
+ if (nil == _refreshCell) {
+ _refreshCell = [[NSButtonCell alloc] initImageCell:[NSImage imageNamed:NSImageNameRefreshTemplate]];
+ _refreshCell.buttonType = NSMomentaryPushInButton;
+ _refreshCell.bordered = NO;
+ }
+
+ return _refreshCell;
+}
+
+- (void)setRefreshTarget:(id)newTarget
+{
+ self.refreshCell.target = newTarget;
+}
+
+- (id)refreshTarget
+{
+ return self.refreshCell.target;
+}
+
+- (void)setRefreshAction:(SEL)newAction
+{
+ self.refreshCell.action = newAction;
+}
+
+- (SEL)refreshAction
+{
+ return self.refreshCell.action;
+}
+
+- (nullable id) pasteboardPropertyListForType:(nonnull NSPasteboardType)type {
+ if ([type isEqualToString: NSPasteboardTypeString] || [type isEqualToString: @"public.url"]) {
+ return self.stringValue;
+ } else if ([type isEqualToString: @"public.url-name"]) {
+ nsurl *nsurl;
+
+ if (nsurl_create(self.stringValue.UTF8String, &nsurl) == NSERROR_OK) {
+
+ const struct url_data *data = urldb_get_url_data(nsurl);
+ nsurl_unref(nsurl);
+
+ if (data && data->title) {
+ return @(data->title);
+ }
+ }
+ }
+
+ return nil;
+}
+
+- (nonnull NSArray<NSPasteboardType> *) writableTypesForPasteboard:(nonnull NSPasteboard *)pasteboard {
+ return @[NSPasteboardTypeString, @"public.url", @"public.url-name"];
+}
+
+- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
+ return NSDragOperationCopy;
+}
+
+@end
diff --git a/frontends/cocoa/apple_image.h b/frontends/cocoa/apple_image.h
new file mode 100644
index 0000000..4f3b545
--- a/dev/null
+++ b/frontends/cocoa/apple_image.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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/>.
+ */
+
+#ifndef _NETSURF_COCOA_APPLE_IMAGE_H_
+#define _NETSURF_COCOA_APPLE_IMAGE_H_
+
+#include "utils/config.h"
+#include "utils/errors.h"
+
+#ifdef WITH_APPLE_IMAGE
+
+/**
+ * Initialise apple image handlers instead of generic core ones.
+ */
+nserror apple_image_init(void);
+
+#else
+
+#define apple_image_init() NSERROR_OK
+
+#endif /* WITH_APPLE_IMAGE */
+
+#endif
diff --git a/frontends/cocoa/apple_image.m b/frontends/cocoa/apple_image.m
new file mode 100644
index 0000000..cee9c27
--- a/dev/null
+++ b/frontends/cocoa/apple_image.m
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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/>.
+ */
+
+#ifdef WITH_APPLE_IMAGE
+
+#import "cocoa/apple_image.h"
+
+#include "utils/config.h"
+#include "utils/utils.h"
+#include "netsurf/bitmap.h"
+#include "netsurf/plotters.h"
+#include "netsurf/content.h"
+#include "content/llcache.h"
+#include "content/content_protected.h"
+
+#import "cocoa/schedule.h"
+#import "cocoa/bitmap.h"
+
+typedef struct apple_image_content {
+ struct content base;
+
+ struct bitmap *bitmap; /**< Created NetSurf bitmap */
+
+ NSUInteger frames;
+ NSUInteger currentFrame;
+ int *frameTimes;
+} apple_image_content;
+
+static void *apple_image_get_internal(const struct content *c, void *context)
+{
+ apple_image_content *ai_c = (apple_image_content *)c;
+ return ai_c->bitmap;
+}
+
+static nserror apple_image_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ apple_image_content *ai;
+ nserror error;
+
+ ai = calloc(1, sizeof(apple_image_content));
+ if (ai == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&ai->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(ai);
+ return error;
+ }
+
+ *c = (struct content *)ai;
+
+ return NSERROR_OK;
+}
+
+static void animate_image_cb(void *ptr)
+{
+ struct apple_image_content *ai = ptr;
+ ++ai->currentFrame;
+ if (ai->currentFrame >= ai->frames)
+ ai->currentFrame = 0;
+
+ [(__bridge NSBitmapImageRep *)ai->bitmap setProperty:NSImageCurrentFrame withValue:[NSNumber numberWithUnsignedInteger:ai->currentFrame]];
+ cocoa_bitmap_modified(ai->bitmap);
+
+ union content_msg_data data;
+ data.redraw.full_redraw = true;
+ data.redraw.x = data.redraw.object_x = 0;
+ data.redraw.y = data.redraw.object_y = 0;
+ data.redraw.width = data.redraw.object_width = ai->base.width;
+ data.redraw.height = data.redraw.object_height = ai->base.height;
+ data.redraw.object = &ai->base;
+ content_broadcast(&ai->base, CONTENT_MSG_REDRAW, data);
+
+ cocoa_schedule(ai->frameTimes[ai->currentFrame], animate_image_cb, ai);
+}
+
+/**
+ * Convert a CONTENT_APPLE_IMAGE for display.
+ */
+static bool apple_image_convert(struct content *c)
+{
+ apple_image_content *ai_c = (apple_image_content *)c;
+ unsigned long size;
+ const char *bytes = content__get_source_data(c, &size);
+
+ NSData *data = [NSData dataWithBytesNoCopy:(char *)bytes length:size freeWhenDone:NO];
+ NSBitmapImageRep *image = [NSBitmapImageRep imageRepWithData:data];
+
+ if (image == nil) {
+ union content_msg_data msg_data;
+ msg_data.error = "cannot decode image";
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ c->width = [image pixelsWide];
+ c->height = [image pixelsHigh];
+ ai_c->bitmap = (__bridge_retained void *)image;
+
+ NSString *url = [NSString stringWithUTF8String:nsurl_access(llcache_handle_get_url(content_get_llcache_handle(c)))];
+ NSString *title = [NSString stringWithFormat:@"%@ (%dx%d)", [url lastPathComponent], c->width, c->height];
+ content__set_title(c, [title UTF8String]);
+
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, "");
+
+ struct apple_image_content *ai = (struct apple_image_content *)c;
+ NSUInteger frames = [[image valueForProperty:NSImageFrameCount] unsignedIntegerValue];
+ if (frames > 1) {
+ ai->frames = frames;
+ ai->currentFrame = 0;
+ ai->frameTimes = calloc(ai->frames, sizeof(int));
+ for (NSUInteger i = 0; i < frames; i++) {
+ [image setProperty:NSImageCurrentFrame withValue:[NSNumber numberWithUnsignedInteger:i]];
+ ai->frameTimes[i] = 1000 * [[image valueForProperty:NSImageCurrentFrameDuration] floatValue];
+ }
+ [image setProperty:NSImageCurrentFrame withValue:[NSNumber numberWithUnsignedInteger:0]];
+ cocoa_schedule(ai->frameTimes[0], animate_image_cb, ai);
+ }
+
+ return true;
+}
+
+static void apple_image_destroy(struct content *c)
+{
+ apple_image_content *ai_c = (apple_image_content *)c;
+
+ id bitmap = (__bridge_transfer id)ai_c->bitmap;
+ bitmap = nil;
+
+ ai_c->bitmap = NULL;
+ cocoa_schedule(-1, animate_image_cb, c);
+}
+
+static nserror apple_image_clone(const struct content *old, struct content **newc)
+{
+ apple_image_content *ai;
+ apple_image_content *ai_old = (apple_image_content *)old;
+ nserror error;
+
+ ai = calloc(1, sizeof(apple_image_content));
+ if (ai == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &ai->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&ai->base);
+ return error;
+ }
+
+ if (old->status == CONTENT_STATUS_READY || old->status == CONTENT_STATUS_DONE) {
+ ai->base.width = old->width;
+ ai->base.height = old->height;
+ ai->bitmap = (__bridge_retained void *)((__bridge id)ai_old->bitmap);
+ }
+
+ *newc = (struct content *)ai;
+
+ return NSERROR_OK;
+}
+
+static content_type apple_image_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+/**
+ * Redraw a CONTENT_APPLE_IMAGE with appropriate tiling.
+ */
+static bool apple_image_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ apple_image_content *ai_c = (apple_image_content *)c;
+ bitmap_flags_t flags = BITMAPF_NONE;
+
+ if (data->repeat_x)
+ flags |= BITMAPF_REPEAT_X;
+ if (data->repeat_y)
+ flags |= BITMAPF_REPEAT_Y;
+
+ return ctx->plot->bitmap(ctx, ai_c->bitmap, data->x, data->y, data->width, data->height,
+ data->background_colour, flags) == NSERROR_OK;
+}
+
+static const content_handler apple_image_content_handler = {
+ .create = apple_image_create,
+ .data_complete = apple_image_convert,
+ .destroy = apple_image_destroy,
+ .redraw = apple_image_redraw,
+ .clone = apple_image_clone,
+ .get_internal = apple_image_get_internal,
+ .type = apple_image_content_type,
+ .no_share = false
+};
+
+static nserror register_for_type(NSString *mime)
+{
+ const char *type = [mime UTF8String];
+/* nsgif has priority since it supports animated GIF */
+#ifdef WITH_GIF
+ if (strcmp(type, "image/gif") == 0)
+ return NSERROR_OK;
+#endif
+
+ nserror error = content_factory_register_handler(type, &apple_image_content_handler);
+ if (error != NSERROR_OK)
+ return error;
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in cocoa/apple_image.h */
+nserror apple_image_init(void)
+{
+ NSArray *utis = [NSBitmapImageRep imageTypes];
+ for (NSString *uti in utis) {
+ NSDictionary *declaration = (__bridge_transfer NSDictionary *)UTTypeCopyDeclaration((__bridge CFStringRef)uti);
+ id mimeTypes = [[declaration objectForKey:(NSString *)kUTTypeTagSpecificationKey] objectForKey:(NSString *)kUTTagClassMIMEType];
+
+ if (mimeTypes == nil)
+ continue;
+
+ if (![mimeTypes isKindOfClass:[NSArray class]]) {
+ mimeTypes = [NSArray arrayWithObject:mimeTypes];
+ }
+
+ for (NSString *mime in mimeTypes) {
+ nserror error = register_for_type(mime);
+ if (error != NSERROR_OK)
+ return error;
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+#endif /* WITH_APPLE_IMAGE */
diff --git a/frontends/cocoa/arc.h b/frontends/cocoa/arc.h
new file mode 100644
index 0000000..bfdf31c
--- a/dev/null
+++ b/frontends/cocoa/arc.h
@@ -0,0 +1,22 @@
+#ifndef arc_h
+#define arc_h
+
+/**
+ * Retains an Objective-C object and returns a pointer that can be passed to C.
+ * @param object The object to retain
+ * @return Pointer suitable to be stored in C code.
+ */
+static inline void *arc_retain(id object) {
+ return (__bridge_retained void *)object;
+}
+
+/**
+ * Releases the Objective-C object pointed to by a C pointer.
+ * @param pointer Object pointer to release.
+ */
+static inline void arc_release(void *pointer) {
+ id object = (__bridge_transfer id)pointer;
+ object = nil;
+}
+
+#endif
diff --git a/frontends/cocoa/bitmap.h b/frontends/cocoa/bitmap.h
new file mode 100644
index 0000000..ebc6a20
--- a/dev/null
+++ b/frontends/cocoa/bitmap.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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/>.
+ */
+
+#ifndef COCOA_BITMAP_H
+#define COCOA_BITMAP_H
+
+CGImageRef cocoa_get_cgimage(void *bitmap);
+
+void cocoa_bitmap_modified(void *bitmap);
+
+struct gui_bitmap_table *cocoa_bitmap_table;
+
+#endif
diff --git a/frontends/cocoa/bitmap.m b/frontends/cocoa/bitmap.m
new file mode 100644
index 0000000..089870e
--- a/dev/null
+++ b/frontends/cocoa/bitmap.m
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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/>.
+ */
+
+/**
+ * \file
+ * Cocoa implementation of bitmap operations.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import "netsurf/browser_window.h"
+#import "netsurf/plotters.h"
+#import "netsurf/bitmap.h"
+#import "netsurf/content.h"
+
+#import "cocoa/plotter.h"
+#import "cocoa/bitmap.h"
+
+#import "cocoa/arc.h"
+
+#define BITS_PER_SAMPLE (8)
+#define SAMPLES_PER_PIXEL (4)
+#define BITS_PER_PIXEL (BITS_PER_SAMPLE * SAMPLES_PER_PIXEL)
+#define BYTES_PER_PIXEL (BITS_PER_PIXEL / 8)
+#define RED_OFFSET (0)
+#define GREEN_OFFSET (1)
+#define BLUE_OFFSET (2)
+#define ALPHA_OFFSET (3)
+
+static CGImageRef cocoa_prepare_bitmap(void *bitmap);
+
+static inline NSMapTable *cocoa_get_bitmap_cache(void)
+{
+ static NSMapTable *cache = nil;
+ if (cache == nil) {
+ cache = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0);
+ }
+ return cache;
+}
+
+static inline NSBitmapImageRep *get_bitmap(void *bitmap) {
+ NSCParameterAssert(bitmap);
+ return (__bridge NSBitmapImageRep *)bitmap;
+}
+
+static int bitmap_get_width(void *bitmap)
+{
+ return (int)get_bitmap(bitmap).pixelsWide;
+}
+
+static int bitmap_get_height(void *bitmap)
+{
+ return (int)get_bitmap(bitmap).pixelsHigh;
+}
+
+static bool bitmap_get_opaque(void *bitmap)
+{
+ return get_bitmap(bitmap).isOpaque;
+}
+
+static void bitmap_destroy(void *bitmap)
+{
+ NSCParameterAssert(NULL != bitmap);
+
+ cocoa_bitmap_modified(bitmap);
+ arc_release(bitmap);
+}
+
+static void *bitmap_create(int width, int height, unsigned int state)
+{
+ NSBitmapImageRep *bmp = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:NULL
+ pixelsWide:width
+ pixelsHigh:height
+ bitsPerSample:BITS_PER_SAMPLE
+ samplesPerPixel:SAMPLES_PER_PIXEL
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
+ bytesPerRow:BYTES_PER_PIXEL * width
+ bitsPerPixel:BITS_PER_PIXEL];
+
+ return arc_retain(bmp);
+}
+
+static void bitmap_set_opaque(void *bitmap, bool opaque)
+{
+ get_bitmap(bitmap).opaque = opaque ? YES : NO;
+}
+
+static unsigned char *bitmap_get_buffer(void *bitmap)
+{
+ return get_bitmap(bitmap).bitmapData;
+}
+
+static size_t bitmap_get_rowstride(void *bitmap)
+{
+ return get_bitmap(bitmap).bytesPerRow;
+}
+
+static size_t bitmap_get_bpp(void *bitmap)
+{
+ return get_bitmap(bitmap).bitsPerPixel / 8;
+}
+
+static bool bitmap_test_opaque(void *bitmap)
+{
+ NSCParameterAssert(bitmap_get_bpp(bitmap) == BYTES_PER_PIXEL);
+
+ unsigned char *buf = bitmap_get_buffer(bitmap);
+
+ const size_t height = bitmap_get_height(bitmap);
+ const size_t width = bitmap_get_width(bitmap);
+
+ const size_t line_step = bitmap_get_rowstride(bitmap) - BYTES_PER_PIXEL * width;
+
+ for (size_t y = 0; y < height; y++) {
+ for (size_t x = 0; x < height; x++) {
+ if (buf[ALPHA_OFFSET] != 0xFF)
+ return false;
+ buf += BYTES_PER_PIXEL;
+ }
+ buf += line_step;
+ }
+
+ return true;
+}
+
+static bool bitmap_save(void *bitmap, const char *path, unsigned flags)
+{
+ NSData *tiff = get_bitmap(bitmap).TIFFRepresentation;
+ return [tiff writeToFile:@(path) atomically:YES];
+}
+
+void cocoa_bitmap_modified(void *bitmap)
+{
+ NSMapTable *cache = cocoa_get_bitmap_cache();
+ CGImageRef image = NSMapGet(cache, bitmap);
+ if (NULL != image) {
+ CGImageRelease(image);
+ NSMapRemove(cache, bitmap);
+ }
+}
+
+CGImageRef cocoa_get_cgimage(void *bitmap)
+{
+ NSMapTable *cache = cocoa_get_bitmap_cache();
+
+ CGImageRef result = NSMapGet(cache, bitmap);
+ if (NULL == result) {
+ result = cocoa_prepare_bitmap(bitmap);
+ NSMapInsertKnownAbsent(cache, bitmap, result);
+ }
+
+ return result;
+}
+
+static CGImageRef cocoa_prepare_bitmap(void *bitmap)
+{
+ NSBitmapImageRep *bmp = get_bitmap(bitmap);
+
+ size_t w = bmp.pixelsWide;
+ size_t h = bmp.pixelsHigh;
+
+ CGImageRef original = bmp.CGImage;
+
+ if (h <= 1) {
+ return CGImageRetain(original);
+ }
+
+ void *data = malloc(4 * w * h);
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGContextRef context = CGBitmapContextCreate(data, w, h, BITS_PER_SAMPLE,
+ BYTES_PER_PIXEL * w, colorSpace,
+ [bmp isOpaque] ? kCGImageAlphaNoneSkipLast
+ : kCGImageAlphaPremultipliedLast);
+ CGColorSpaceRelease(colorSpace);
+
+ CGContextTranslateCTM(context, 0.0, h);
+ CGContextScaleCTM(context, 1.0, -1.0);
+
+ CGRect rect = CGRectMake(0, 0, w, h);
+ CGContextClearRect(context, rect);
+ CGContextDrawImage(context, rect, original);
+
+ CGImageRef result = CGBitmapContextCreateImage(context);
+
+ CGContextRelease(context);
+ free(data);
+
+ return result;
+}
+
+static nserror bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content)
+{
+ int bwidth = bitmap_get_width(bitmap);
+ int bheight = bitmap_get_height(bitmap);
+
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &cocoa_plotters
+ };
+
+ CGColorSpaceRef cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ CGContextRef bitmapContext = CGBitmapContextCreate(bitmap_get_buffer(bitmap),
+ bwidth, bheight,
+ bitmap_get_bpp(bitmap) * 8 / 4,
+ bitmap_get_rowstride(bitmap),
+ cspace, kCGImageAlphaNoneSkipLast);
+ CGColorSpaceRelease(cspace);
+
+ int width = MIN(content_get_width(content), 1024);
+ int height = ((width * bheight) + bwidth / 2) / bwidth;
+
+ CGContextTranslateCTM(bitmapContext, 0, bheight);
+ CGContextScaleCTM(bitmapContext, (CGFloat)bwidth / width, -(CGFloat)bheight / height);
+
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:bitmapContext flipped:YES]];
+
+ content_scaled_redraw(content, width, height, &ctx);
+
+ [NSGraphicsContext setCurrentContext:nil];
+ CGContextRelease(bitmapContext);
+
+ cocoa_bitmap_modified(bitmap);
+
+ return NSERROR_OK;
+}
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = bitmap_create,
+ .destroy = bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = bitmap_get_width,
+ .get_height = bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = bitmap_save,
+ .modified = cocoa_bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *cocoa_bitmap_table = &bitmap_table;
diff --git a/frontends/cocoa/compile-xib.sh b/frontends/cocoa/compile-xib.sh
new file mode 100755
index 0000000..576f9bf
--- a/dev/null
+++ b/frontends/cocoa/compile-xib.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# call: compile-xib.sh [xib file] [language] [(optional output nib file)]
+DIR=`dirname "$1"`
+XIB=`basename -s .xib "$1"`
+
+STRINGS_FILE="$DIR/$2.lproj/$XIB.xib.strings"
+TRANSLATE=""
+if [ -f $STRINGS_FILE ]
+then
+ TRANSLATE="--strings-file $STRINGS_FILE"
+fi
+
+OUTPUT="$2.$XIB.nib"
+
+if [ "x$3" != "x" ]
+then
+ OUTPUT="$3"
+fi
+
+exec /usr/bin/ibtool $TRANSLATE --compile $OUTPUT $1
diff --git a/frontends/cocoa/coordinates.h b/frontends/cocoa/coordinates.h
new file mode 100644
index 0000000..516df43
--- a/dev/null
+++ b/frontends/cocoa/coordinates.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2011 Sven Weidauer <sven.weidauer@gmail.com>
+ *
+ * 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/>.
+ */
+
+#ifndef COCOA_COORDINATES_H
+#define COCOA_COORDINATES_H
+
+extern CGFloat cocoa_scale_factor;
+
+static inline CGFloat cocoa_px_to_pt(int location) __attribute__((always_inline, pure));
+static inline CGFloat cocoa_px_to_pt_f(CGFloat location) __attribute__((always_inline, pure));
+
+static inline int cocoa_pt_to_px(CGFloat location) __attribute__((always_inline, pure));
+
+static inline NSPoint cocoa_point(int x, int y) __attribute__((always_inline, pure));
+static inline NSPoint cocoa_scaled_point(CGFloat scale, int x, int y) __attribute__((always_inline, pure));
+
+static inline NSSize cocoa_size(int w, int h) __attribute__((always_inline, pure));
+static inline NSSize cocoa_scaled_size(CGFloat scale, int w, int h) __attribute__((always_inline, pure));
+
+static inline NSRect cocoa_rect(int x0, int y0, int x1, int y1) __attribute__((always_inline, pure));
+static inline NSRect cocoa_rect_wh(int x, int y, int w, int h) __attribute__((always_inline, pure));
+
+static inline NSRect cocoa_scaled_rect(CGFloat scale, int x0, int y0, int x1, int y1) __attribute__((always_inline, pure));
+static inline NSRect cocoa_scaled_rect_wh(CGFloat scale, int x, int y, int w, int h) __attribute__((always_inline, pure));
+
+static inline CGFloat cocoa_px_to_pt(int location)
+{
+ return (CGFloat)location * cocoa_scale_factor;
+}
+
+static inline CGFloat cocoa_px_to_pt_f(CGFloat location)
+{
+ return floor(location) * cocoa_scale_factor;
+}
+
+static inline int cocoa_pt_to_px(CGFloat location)
+{
+ return location / cocoa_scale_factor;
+}
+
+static inline NSPoint cocoa_point(int x, int y)
+{
+ return NSMakePoint(cocoa_px_to_pt(x), cocoa_px_to_pt(y));
+}
+
+static inline NSPoint cocoa_scaled_point(CGFloat scale, int x, int y)
+{
+ return NSMakePoint(cocoa_px_to_pt_f(scale * x), cocoa_px_to_pt_f(scale * y));
+}
+
+static inline NSSize cocoa_size(int w, int h)
+{
+ return NSMakeSize(cocoa_px_to_pt(w), cocoa_px_to_pt(h));
+}
+
+static inline NSSize cocoa_scaled_size(CGFloat scale, int w, int h)
+{
+ return NSMakeSize(cocoa_px_to_pt_f(scale * w), cocoa_px_to_pt_f(scale * h));