1# 2# MyPDFDocument.rb 3# PDFKitViewer 4# 5# Created by Laurent Sansonetti on 12/11/06. 6# Copyright (c) 2006 Apple Computer. All rights reserved. 7# 8 9class MyPDFDocument < NSDocument 10 11 ib_outlets :pdfView, :drawer, :outlineView, :noOutlineText, :searchTable, :searchProgress 12 13 def initialize 14 @searchResults = [] 15 end 16 17 def dealloc 18 NSNotificationCenter.defaultCenter.removeObserver(self) 19 super_dealloc 20 end 21 22 def windowNibName 23 'MyPDFDocument' 24 end 25 26 def windowControllerDidLoadNib(controller) 27 # Super. 28 super_windowControllerDidLoadNib(controller) 29 30 # Load PDF. 31 if self.fileName 32 pdfDoc = PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath(self.fileName)) 33 @pdfView.setDocument(pdfDoc) 34 end 35 36 # Page changed notification. 37 notifCenter = NSNotificationCenter.defaultCenter 38 notifCenter.addObserver_selector_name_object(self, 'pageChanged:', PDFViewPageChangedNotification, @pdfView) 39 40 # Find notifications. 41 doc = @pdfView.document 42 notifCenter.addObserver_selector_name_object(self, 'startFind:', PDFDocumentDidBeginFindNotification, doc) 43 notifCenter.addObserver_selector_name_object(self, 'findProgress:', PDFDocumentDidEndPageFindNotification, doc) 44 notifCenter.addObserver_selector_name_object(self, 'endFind:', PDFDocumentDidEndFindNotification, doc) 45 46 # Set self to be delegate (find). 47 doc.setDelegate(self) 48 49 # Get outline. 50 @outline = doc.outlineRoot 51 if @outline 52 # Remove text that says, "No outline." 53 @noOutlineText.removeFromSuperview 54 @noOutlineText = nil 55 56 # Force it to load up. 57 @outlineView.reloadData 58 else 59 # Remove outline view (leaving instead text that says, "No outline."). 60 @outlineView.enclosingScrollView.removeFromSuperview 61 @outlineView = nil 62 end 63 64 # Open drawer. 65 @drawer.open 66 end 67 68 def dataRepresentationOfType(type) 69 nil 70 end 71 72 def loadDataRepresentation_ofType(data, type) 73 true 74 end 75 76 def toggleDrawer(sender) 77 @drawer.toggle(self) 78 end 79 ib_action :toggleDrawer 80 81 def takeDestinationFromOutline(sender) 82 # Get the destination associated with the search result list. Tell the PDFView to go there. 83 item = sender.itemAtRow(sender.selectedRow) 84 @pdfView.goToDestination(item.destination) if item 85 end 86 ib_action :takeDestinationFromOutline 87 88 def displaySinglePage(sender) 89 # Display single page mode. 90 if @pdfView.displayMode > KPDFDisplaySinglePageContinuous 91 @pdfView.setDisplayMode(@pdfView.displayMode - 2) 92 end 93 end 94 ib_action :displaySinglePage 95 96 def displayTwoUp(sender) 97 # Display two-up. 98 if @pdfView.displayMode < KPDFDisplayTwoUp 99 @pdfView.setDisplayMode(@pdfView.displayMode + 2) 100 end 101 end 102 ib_action :displayTwoUp 103 104 def pageChanged(notification) 105 doc = @pdfView.document 106 107 # Skip out if there is no outline. 108 return if doc.outlineRoot.nil? 109 110 # What is the new page number (zero-based). 111 newPageIndex = doc.indexForPage(@pdfView.currentPage) 112 113 # Walk outline view looking for best firstpage number match. 114 newlySelectedRow = -1 115 numRows = @outlineView.numberOfRows 116 numRows.times do |i| 117 # Get the destination of the given row.... 118 outlineItem = @outlineView.itemAtRow(i) 119 120 index = @pdfView.document.indexForPage(outlineItem.destination.page) 121 if index == newPageIndex 122 newlySelectedRow = i 123 @outlineView.selectRow_byExtendingSelection(newlySelectedRow, false) 124 break 125 elsif index < newPageIndex 126 newlySelectedRow = i - 1 127 @outlineView.selectRow_byExtendingSelection(newlySelectedRow, false) 128 break 129 end 130 end 131 132 # Auto-scroll. 133 if newlySelectedRow != -1 134 @outlineView.scrollRowToVisible(newlySelectedRow) 135 end 136 end 137 138 def doFind(sender) 139 doc = @pdfView.document 140 doc.cancelFindString if doc.isFinding 141 doc.beginFindString_withOptions(sender.stringValue, NSCaseInsensitiveSearch) 142 end 143 ib_action :doFind 144 145 def startFind(notification) 146 # Empty arrays. 147 @searchResults.clear 148 149 @searchTable.reloadData 150 @searchProgress.startAnimation(self) 151 end 152 153 def findProgress(notification) 154 pageIndex = notification.userInfo.objectForKey('PDFDocumentPageIndex').doubleValue 155 @searchProgress.setDoubleValue(pageIndex / @pdfView.document.pageCount) 156 end 157 158 # Called when an instance was located. Delegates can instantiate. 159 def didMatchString(instance) 160 # Add page label to our array. 161 @searchResults << instance.copy 162 163 # Force a reload. 164 @searchTable.reloadData 165 end 166 167 def endFind(notification) 168 @searchProgress.stopAnimation(self) 169 @searchProgress.setDoubleValue(0) 170 end 171 172 # The table view is used to hold search results. Column 1 lists the page number for the search result, 173 # column two the section in the PDF (x-ref with the PDF outline) where the result appears. 174 def numberOfRowsInTableView(aTableView) 175 @searchResults ? @searchResults.length : 0 176 end 177 178 def tableView_objectValueForTableColumn_row(aTableView, theColumn, rowIndex) 179 case theColumn.identifier.to_s 180 when 'page' 181 @searchResults[rowIndex].pages.objectAtIndex(0).label 182 when 'section' 183 item = @pdfView.document.outlineItemForSelection(@searchResults[rowIndex]) 184 item ? item.label : nil 185 end 186 end 187 188 def tableViewSelectionDidChange(notification) 189 # What was selected. Skip out if the row has not changed. 190 rowIndex = notification.object.selectedRow 191 if rowIndex >= 0 192 @pdfView.setCurrentSelection(@searchResults[rowIndex]) 193 @pdfView.centerSelectionInVisibleArea(self) 194 end 195 end 196 197 # The outline view is for the PDF outline. Not all PDF's have an outline. 198 def outlineView_numberOfChildrenOfItem(outlineView, item) 199 if item 200 item.numberOfChildren 201 else 202 @outline ? @outline.numberOfChildren : 0 203 end 204 end 205 206 def outlineView_child_ofItem(outlineView, index, item) 207 if item 208 item.childAtIndex(index) 209 else 210 @outline ? @outline.childAtIndex(index) : nil 211 end 212 end 213 214 def outlineView_isItemExpandable(outlineView, item) 215 if item 216 item.numberOfChildren > 0 217 else 218 @outline ? @outline.numberOfChildren > 0 : false 219 end 220 end 221 222 def outlineView_objectValueForTableColumn_byItem(outlineView, tableColumn, item) 223 item.label 224 end 225end 226