#! /usr/bin/python # # REWindowController.py # ReSTedit # # Created by Bill Bumgarner on Sat Feb 14 2004. # Copyright (c) 2004 __MyCompanyName__. All rights reserved. # import tempfile import os from objc import YES, NO, lookUpClass, accessor, selector, ivar from Foundation import * from AppKit import * try: from docutils.core import publish_string from docutils.readers import standalone from docutils.writers import html4css1 from docutils.languages import en except ImportError: raise ImportError, "docutils failed to import. Is it installed? Is it in the app wrapper?" from PyObjCTools import NibClassBuilder RBSplitSubview = lookUpClass("RBSplitSubview") REWindowToolbarIdentifier = "ReSTedit Window Toolbar Identifier" REWindowAutoUpdateToolbarItemIdentifier = "ReSTedit Automatic Update Switch Toolbar Identifier" REWindowRenderNowToolbarItemIdentifier = "ReSTedit Render Now Switch Toolbar Identifier" REWindowOpenInSafariToolbarItemIdentifier = "ReSTedit Open In Browser Toolbar Identifier" REWindowModeSelectorToolbarItemIdentifier = "ReSTedit Mode Selector Toolbar Identifier" REWindowFormatSelectorToolbarItemIdentifier = "ReSTedit Format Selector Toolbar Identifier" REWindowCSSSelectorToolbarItemIdentifier = "ReSTedit CSS Selector Toolbar Identifier" RenderedOnlyMode = 0 CombinationMode = 1 TextOnlyMode = 2 NibClassBuilder.extractClasses("REDocument") class REWindowController(NibClassBuilder.AutoBaseClass): def initWithWindow_(self, aWindow): self = super(REWindowController, self).initWithWindow_(aWindow) if self: self.isDirty = NO self.renderTimer = None self.renderedHTML = None self.displayMode = CombinationMode self.internalBoundsChangeFlag = True self.textViewSubSplitview = None self.htmlViewSubSplitview = None self.flagAutoUpdateEnabled = True return self def autoUpdateEnabled(self): return self.flagAutoUpdateEnabled autoUpdateEnabled = accessor(autoUpdateEnabled) def setAutoUpdateEnabled_(self, flag): self.flagAutoUpdateEnabled = flag setAutoUpdateEnabled_ = accessor(setAutoUpdateEnabled_) def windowNibName(self): return "REDocument" def windowDidLoad(self): super(REWindowController, self).windowDidLoad() self.textView.layoutManager().setDelegate_(self) self.installSplitView() self.initializeToolbarModeSegmentedControl() self.createToolbar() self.setDisplayMode_adjustSplitView_(CombinationMode, YES) self.document().pushContentToWindow() self.renderContent_(None) def layoutManager_didCompleteLayoutForTextContainer_atEnd_(self, lM, tC, atEndFlag): # this avoids the storm of layout change notifs upon initial layout self.internalBoundsChangeFlag = False def openInSafari_(self, sender): if not self.renderedHTML or self.isDirty: self.renderContent_(None) if not self.renderedHTML: return (x, p) = tempfile.mkstemp(prefix="rendered-rest-", suffix=".html") os.write(x, self.renderedHTML) os.close(x) NSWorkspace.sharedWorkspace().openFile_(p) def destroyTimer(self): if self.renderTimer: self.renderTimer.invalidate() self.renderTimer = None def windowWillClose_(self, aNotification): self.destroyTimer() def dealloc(self): self.destroyTimer() super(REWindowController, self).dealloc() def selectModeSegment_(self, sender): clickedSegmentTag = sender.cell().tagForSegment_(sender.selectedSegment()) self.setDisplayMode_adjustSplitView_(clickedSegmentTag, YES) def restSourceChanged_(self, aNotification): self.isDirty = YES self.resetTimer() def updateInterval(self): return 5.0 def resetTimer(self): self.destroyTimer() if self.flagAutoUpdateEnabled: self.renderTimer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( self.updateInterval(), self, "timedRenderContent", None, NO) def timedRenderContent(self): if not self.isDirty: self.resetTimer() return if self.flagAutoUpdateEnabled: self.renderContent_(None) def setTextStorage_(self, someContent): if self.textView: self.internalBoundsChangeFlag = True self.textView.layoutManager().replaceTextStorage_(someContent) nc = NSNotificationCenter.defaultCenter() nc.removeObserver_name_object_(self, NSTextStorageDidProcessEditingNotification, None) nc.addObserver_selector_name_object_(self, "restSourceChanged:", NSTextStorageDidProcessEditingNotification, someContent) nc.removeObserver_name_object_(self, NSViewBoundsDidChangeNotification, None) nc.addObserver_selector_name_object_(self, "clipViewOfTextViewBoundsChangedNotification:", NSViewBoundsDidChangeNotification, None) self.textView.setFont_(NSFont.userFixedPitchFontOfSize_(12.0)) def clipViewOfTextViewBoundsChangedNotification_(self, aNotification): if self.internalBoundsChangeFlag: return textClipView = self.findClipView_(self.textView.enclosingScrollView()) if textClipView and (aNotification.object() is textClipView): self.applyScrollFromClipViewInView_toClipViewInView_(textClipView, self.htmlView) def findClipView_(self, aView): if aView.isKindOfClass_(NSClipView): return aView for v in aView.subviews(): if v.isKindOfClass_(NSClipView): return v else: return self.findClipView_(v) def updateClipView_(self, sender): if self._clipViewTemp: self.internalBoundsChangeFlag = True self._clipViewTemp.setBounds_(self._boundsTemp) self.internalBoundsChangeFlag = False self._boundsTemp = None self._clipViewTemp = None def applyScrollFromClipViewInView_toClipViewInView_(self, sourceView, destView): self._boundsTemp = None self._clipViewTemp = None cV = self.findClipView_(sourceView) vertFactor = None if cV: vertFactor = cV.bounds().origin.y / cV.bounds().size.height if not vertFactor is None: cV = self.findClipView_(destView) b = cV.bounds() b.origin.y = b.size.height * vertFactor self._boundsTemp = b self._clipViewTemp = cV self.performSelector_withObject_afterDelay_("updateClipView:", None, 0.0) def renderContent_(self, sender): renderedHTML = self.document().generateContent() if not renderedHTML: self.resetTimer() return self.renderedHTML = renderedHTML self.htmlView.mainFrame().loadHTMLString_baseURL_(self.renderedHTML, None) self.isDirty = NO self.resetTimer() self.applyScrollFromClipViewInView_toClipViewInView_(self.textView.enclosingScrollView(), self.htmlView) def installSplitView(self): self.splitView.setDivider_(NSImage.imageNamed_("Thumb")) self.splitView.setVertical_(YES) self.splitView.setCanCollapse_(YES) self.splitView.addSubview_(self.textView.enclosingScrollView()) self.textViewSubSplitview = self.splitView.subviewAtPosition_(0) self.splitView.addSubview_(self.htmlView) self.htmlViewSubSplitview = self.splitView.subviewAtPosition_(1) for aView in self.splitView.subviews(): aView.setCanCollapse_(YES) aView.setMinDimension_andMaxDimension_(125, 0) self.splitView.adjustSubviews() def createToolbar(self): toolbar = NSToolbar.alloc().initWithIdentifier_(REWindowToolbarIdentifier) toolbar.setAllowsUserCustomization_(YES) toolbar.setAutosavesConfiguration_(YES) toolbar.setDelegate_(self) self.window().setToolbar_(toolbar) def initializeToolbarModeSegmentedControl(self): ws = NSWorkspace.sharedWorkspace() segControlCell = self.toolbarModeSegmentedControl.cell() txtIcon = ws.iconForFileType_("txt") txtIcon.setSize_( (16,16) ) htmlIcon = ws.iconForFileType_("html") htmlIcon.setSize_( (16,16) ) comboIcon = NSImage.alloc().initWithSize_( (16,16) ) comboIcon.lockFocus() txtIcon.compositeToPoint_fromRect_operation_((0,0), ((0,0),(8,16)), NSCompositeCopy) htmlIcon.compositeToPoint_fromRect_operation_((8,0), ((8,0),(8,16)), NSCompositeCopy) comboIcon.unlockFocus() segControlCell.setTrackingMode_(NSSegmentSwitchTrackingSelectOne) segControlCell.setSegmentCount_(3) segControlCell.setImage_forSegment_(txtIcon, 0) segControlCell.setTag_forSegment_(TextOnlyMode, 0) segControlCell.setImage_forSegment_(comboIcon, 1) segControlCell.setTag_forSegment_(CombinationMode, 1) segControlCell.setImage_forSegment_(htmlIcon, 2) segControlCell.setTag_forSegment_(RenderedOnlyMode, 2) self.toolbarModeSegmentedControl.sizeToFit() self.toolbarModeSegmentedControl.setTarget_(self) self.toolbarModeSegmentedControl.setAction_("selectModeSegment:") segControlCell.setEnabled_(YES) def setDisplayMode_adjustSplitView_(self, aMode, adjustSplitViewFlag): segControlCell = self.toolbarModeSegmentedControl.cell() for segmentToSelect in xrange(0, segControlCell.segmentCount()): if aMode == segControlCell.tagForSegment_(segmentToSelect): break self.toolbarModeSegmentedControl.setSelectedSegment_(segmentToSelect) self.displayMode = aMode if adjustSplitViewFlag: if aMode == RenderedOnlyMode: self.textViewSubSplitview.collapse() self.htmlViewSubSplitview.expand() elif aMode == TextOnlyMode: self.textViewSubSplitview.expand() self.htmlViewSubSplitview.collapse() elif aMode == CombinationMode: self.textViewSubSplitview.expand() self.htmlViewSubSplitview.expand() self.splitView.adjustSubviews() def determineModeFromSplitView(self): if self.textViewSubSplitview.isCollapsed(): self.setDisplayMode_adjustSplitView_(RenderedOnlyMode, NO) elif self.htmlViewSubSplitview.isCollapsed(): self.setDisplayMode_adjustSplitView_(TextOnlyMode, NO) else: self.setDisplayMode_adjustSplitView_(CombinationMode, NO) def splitView_didCollapse_(self, sender, subview): self.determineModeFromSplitView() def splitView_didExpand_(self, sender, subview): self.determineModeFromSplitView() def toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar_(self, aToolbar, anIdentifier, aFlag): if anIdentifier == REWindowAutoUpdateToolbarItemIdentifier: toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(REWindowAutoUpdateToolbarItemIdentifier) toolbarItem.setLabel_("Auto update") toolbarItem.setPaletteLabel_("Auto update switch") toolbarItem.setToolTip_("Enable/disable automatic updating") toolbarItem.setView_(self.toolbarAutoUpdateSwitch) toolbarItem.setMinSize_(self.toolbarAutoUpdateSwitch.frame().size) toolbarItem.setMaxSize_(self.toolbarAutoUpdateSwitch.frame().size) return toolbarItem if anIdentifier == REWindowRenderNowToolbarItemIdentifier: toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(REWindowRenderNowToolbarItemIdentifier) toolbarItem.setLabel_("Render") toolbarItem.setPaletteLabel_("Render") toolbarItem.setToolTip_("Render immediately") toolbarItem.setView_(self.toolbarRenderNowButton) toolbarItem.setMinSize_(self.toolbarRenderNowButton.frame().size) toolbarItem.setMaxSize_(self.toolbarRenderNowButton.frame().size) return toolbarItem if anIdentifier == REWindowOpenInSafariToolbarItemIdentifier: toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(REWindowOpenInSafariToolbarItemIdentifier) toolbarItem.setLabel_("Open HTML in Browser") toolbarItem.setPaletteLabel_("Open HTML in Browser") toolbarItem.setToolTip_("Open HTML in Browser") toolbarItem.setView_(self.toolbarOpenInSafariButton) toolbarItem.setMinSize_(self.toolbarOpenInSafariButton.frame().size) toolbarItem.setMaxSize_(self.toolbarOpenInSafariButton.frame().size) return toolbarItem if anIdentifier == REWindowModeSelectorToolbarItemIdentifier: toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(REWindowModeSelectorToolbarItemIdentifier) toolbarItem.setLabel_("Mode") toolbarItem.setPaletteLabel_("Mode indicator") toolbarItem.setToolTip_("Indicates mode") toolbarItem.setView_(self.toolbarModeSegmentedControl) toolbarItem.setMinSize_(self.toolbarModeSegmentedControl.frame().size) toolbarItem.setMaxSize_(self.toolbarModeSegmentedControl.frame().size) return toolbarItem if anIdentifier == REWindowFormatSelectorToolbarItemIdentifier: toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(REWindowFormatSelectorToolbarItemIdentifier) toolbarItem.setLabel_("Format") toolbarItem.setPaletteLabel_("Format selector") toolbarItem.setToolTip_("Selects format") toolbarItem.setView_(self.toolbarFormatWidget) toolbarItem.setMinSize_(self.toolbarFormatWidget.frame().size) toolbarItem.setMaxSize_(self.toolbarFormatWidget.frame().size) return toolbarItem if anIdentifier == REWindowCSSSelectorToolbarItemIdentifier: toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(REWindowCSSSelectorToolbarItemIdentifier) toolbarItem.setLabel_("CSS Selector") toolbarItem.setPaletteLabel_("CSS selector") toolbarItem.setToolTip_("Selects css used during rendering") toolbarItem.setView_(self.toolbarCSSPopUpButton) toolbarItem.setMinSize_(self.toolbarCSSPopUpButton.frame().size) toolbarItem.setMaxSize_(self.toolbarCSSPopUpButton.frame().size) return toolbarItem def toolbarDefaultItemIdentifiers_(self, aToolbar): return [REWindowModeSelectorToolbarItemIdentifier, REWindowOpenInSafariToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier] def toolbarAllowedItemIdentifiers_(self, aToolbar): return [REWindowModeSelectorToolbarItemIdentifier, REWindowAutoUpdateToolbarItemIdentifier, REWindowRenderNowToolbarItemIdentifier, REWindowOpenInSafariToolbarItemIdentifier, REWindowFormatSelectorToolbarItemIdentifier, REWindowCSSSelectorToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, NSToolbarCustomizeToolbarItemIdentifier, NSToolbarPrintItemIdentifier, NSToolbarShowFontsItemIdentifier, NSToolbarShowColorsItemIdentifier]