from AppKit import *
from Quartz import *
import objc

from PyObjCTools import NibClassBuilder

class MyPDFDocument (NibClassBuilder.AutoBaseClass):
    _outline        = objc.ivar()
    _searchResults  = objc.ivar()


    def dealloc(self):
        NSNotificationCenter.defaultCenter().removeObserver_(self)
        self._searchResults = None
        super(MyPDFDocument, self).dealloc()

    def windowNibName(self):
        return "MyDocument"

    def windowControllerDidLoadNib_(self, controller):
        super(MyPDFDocument, self).windowControllerDidLoadNib_(controller)

        if self.fileName():
            pdfDoc = PDFDocument.alloc().initWithURL_(
                    NSURL.fileURLWithPath_(self.fileName()))
            self._pdfView.setDocument_(pdfDoc)

	# Page changed notification.
        NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
                self, "pageChanged:", PDFViewPageChangedNotification, self._pdfView)

	# Find notifications.
        center = NSNotificationCenter.defaultCenter()
        center.addObserver_selector_name_object_(
                self, 'startFind:', PDFDocumentDidBeginFindNotification,
                self._pdfView.document())
        center.addObserver_selector_name_object_(
                self, 'findProgress:', PDFDocumentDidEndPageFindNotification,
                self._pdfView.document())
        center.addObserver_selector_name_object_(
                self, 'endFind:', PDFDocumentDidEndFindNotification,
                self._pdfView.document())

	# Set self to be delegate (find).
        self._pdfView.document().setDelegate_(self)
	
	# Get outline.
	self._outline = self._pdfView.document().outlineRoot()
        if self._outline is not None:
		# Remove text that says, "No outline."
		self._noOutlineText.removeFromSuperview()
                self._noOutlineText = None
		
		# Force it to load up.
		self._outlineView.reloadData()
	
        else:
		# Remove outline view (leaving instead text that says, 
                # "No outline.").
		self._outlineView.enclosingScrollView().removeFromSuperview()
		self._outlineView = None
	
	# Open drawer.
	self._drawer.open()
	
	# Size the window.
	windowSize = self._pdfView.rowSizeForPage_(
                self._pdfView.currentPage())

        if (self._pdfView.displayMode() & 0x01) and (
                    self._pdfView.document().pageCount() > 1):
            windowSize.width +=  NSScroller.scrollerWidth()
        controller.window().setContentSize_(windowSize)

    def dataRepresentationOfType_(self, aType):
        return None

    def loadDataRepresentation_ofType_(self, data, aType):
        return True

    @objc.IBAction
    def toggleDrawer_(self, sender):
        self._drawer.toggle_(self)

    @objc.IBAction
    def takeDestinationFromOutline_(self, sender):
	# Get the destination associated with the search result list.  
        # Tell the PDFView to go there.
	self._pdfView.goToDestination_(
                sender.itemAtRow_(sender.selectedRow()).destination())

    @objc.IBAction
    def displaySinglePage_(self, sender):
	# Display single page mode.
        if self._pdfView.displayMode() > kPDFDisplaySinglePageContinuous:
            self._pdfView.setDisplayMode_(self._pdfView.displayMode() - 2)

    @objc.IBAction
    def displayTwoUp_(self, sender):
        #  Display two-up.
        if self._pdfView.displayMode() < kPDFDisplayTwoUp:
            self._pdfView.setDisplayMode_(self._pdfView.displayMode() + 2)

    def pageChanged_(self, notification):
	# Skip out if there is no outline.
        if selfl_pdfView.document().outlineRoot() is None:
            return
	
	# What is the new page number (zero-based).
	newPageIndex = self._pdfView.document().indexForPage_(
                self._pdfView.currentPage())
	
	# Walk outline view looking for best firstpage number match.
	newlySelectedRow = -1;
	numRows = self._outlineView.numberOfRows()
        for i in range(numRows):
		outlineItem = self._outlineView.itemAtRow_(i)
		
		if self._pdfView.document().indexForPage_(
                        outlineItem.destination().page()) == newPageIndex:

                    newlySelectedRow = i
                    self._outlineView.selectRow_byExtendingSelection_(
                            newlySelectedRow, False)
                    break
		
                elif self._pdfView.document().indexForPage_(outlineItem.destionation().page()) > newPageIndex:
                    newlySelectedRow = i - 1
                    self._outlineView.selectRow_byExtendingSelection_(
                            newlySelectedRow, False)
                    break
	
	# Auto-scroll.
        if newlySelectedRow != -1:
            self._outlineView.scrollRowToVisible_(newlySelectedRow)


    def doFind_(self, sender):
        if self._pdfView.document().isFinding():
            self._pdfView.document().cancelFindString()


	# Lazily allocate _searchResults.
        if self._searchResults is None:
            self._searchResults = NSMutableArray.arrayWithCapacity_(10)
	
	self._pdfView.document().beginFindString_withOptions_(
                sender.stringValue(), NSCaseInsensitiveSearch)

    def startFind_(self, notification):
	# Empty arrays.
	self._searchResults.removeAllObjects()
        self._searchTable.reloadData()
        self._searchProgress.startAnimation_(self)

    def findProgress_(self, notification): 
	pageIndex = notification.userInfo().objectForKey_(
                "PDFDocumentPageIndex") #.doubleValue()
        self._searchProgress.setDoubleValue_(
                pageIndex / self._pdfView.document().pageCount())

    def didMatchString_(self, instance):
	# Add page label to our array.
	self._searchResults.addObject_(instance.copy())
	self._searchTable.reloadData()

    def endFind_(self, notification):
        self._searchProgress.stopAnimation_(self)
        self._searchProgress.setDoubleValue_(0)

    #  The table view is used to hold search results.  Column 1 lists the 
    # page number for the search result,  column two the section in the PDF 
    # (x-ref with the PDF outline) where the result appears.

    def numberOfRowsInTableView_(self, aTableView): 
        if self._searchResults is None:
            return 0
	return self._searchResults.count()

    def tableView_objectValueForTableColumn_row_(
            self, aTableView, theColumn, rowIndex):

        if theColumn.identifier() == "page":
            return  self._searchResults.objectAtIndex_(rowIndex).pages().objectAtIndex_(0).label()

        elif theColumn.identifier() == 'section':
            value = self._pdfView.document().outlineItemForSelection_(
                    self._searchResults.objectAtIndex_(rowIndex))

            if value is None:
                return None

            return value.label()

        else:
            return None

    def tableViewSelectionDidChange_(self, notification):
	# What was selected.  Skip out if the row has not changed.
	rowIndex = notification.object().selectedRow()
        if rowIndex >= 0:
		self._pdfView.setCurrentSelection_(
                        self._searchResults.objectAtIndex_(rowIndex))
                self._pdfView.centerSelectionInVisibleArea_(self)


    # The outline view is for the PDF outline.  Not all PDF's have an outline.
    def outlineView_numberOfChildrenOfItem_(self, outlineView, item):
        if item is None:
            if self._outline is not None:
                return self._outline.numberOfChildren()
            else:
                return 0

        else:
		return item.numberOfChildren()

    def outlineView_child_ofItem_(self, outlineView, index, item):
        if item is None:
            if self._outline is not None:
                return self._outline.childAtIndex_(index).retain()
            else:
                return None

        else:
            return item.childAtIndex_(index).retain()

    def outlineView_isItemExpandable_(self, outlineView, item):
        if item is None:
            if self._outline:
                return self._outline.numberOfChildren() > 0
            
            else:
                return False

        else:
            return item.numberOfChildren() > 0

    def outlineView_objectValueForTableColumn_byItem_(
            self, outlineView, tableColumn, item):
        return item.label()
