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