PageRenderTime 32ms CodeModel.GetById 10ms app.highlight 18ms RepoModel.GetById 0ms app.codeStats 0ms

/GUI/Cocoa/Applications.py

https://bitbucket.org/alsh/pygui-mirror
Python | 403 lines | 359 code | 12 blank | 32 comment | 1 complexity | a92d134962343b896822d85cf3fbb27f MD5 | raw file
  1#------------------------------------------------------------------------------
  2#
  3#		Python GUI - Application class - PyObjC
  4#
  5#------------------------------------------------------------------------------
  6
  7import os, sys, traceback
  8import objc
  9#from ExceptionHandling import \
 10#	NSExceptionHandler, NSLogUncaughtExceptionMask, NSLogAndHandleEveryExceptionMask
 11from Foundation import NSObject, NSBundle, NSDefaultRunLoopMode
 12import AppKit
 13from AppKit import NSApplication, NSResponder, NSScreen, NSMenu, NSMenuItem, \
 14    NSKeyDown, NSKeyUp, NSMouseMoved, NSLeftMouseDown, NSSystemDefined, \
 15    NSCommandKeyMask, NSPasteboard, NSStringPboardType, NSModalPanelRunLoopMode, \
 16    NSAnyEventMask
 17import Globals
 18import GApplications
 19from Globals import application
 20from GApplications import Application as GApplication
 21from Events import Event
 22#from Exceptions import Cancel, Quit
 23from Menus import _ns_standard_actions
 24
 25#------------------------------------------------------------------------------
 26
 27Globals.ns_screen_height = None
 28Globals.ns_last_mouse_moved_event = None
 29ns_application = None
 30
 31#------------------------------------------------------------------------------
 32
 33#def test_menus(app):
 34#	print "Testing menus"
 35#	foomenu = NSMenu.alloc().initWithTitle_("Foo")
 36#	foomenu.addItemWithTitle_action_keyEquivalent_("Blarg", None, "B")
 37#	feemenu = NSMenu.alloc().initWithTitle_("Fee")
 38#	feemenu.addItemWithTitle_action_keyEquivalent_("Blork", None, "K")
 39#	menubar = NSMenu.alloc().initWithTitle_("Main")
 40#	fooitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("Foo", "", "")
 41#	menubar.addItem_(fooitem)
 42#	menubar.setSubmenu_forItem_(foomenu, fooitem)
 43#	feeitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("Fee", "", "")
 44#	menubar.addItem_(feeitem)
 45#	menubar.setSubmenu_forItem_(feemenu, feeitem)
 46#	app.setMainMenu_(menubar)
 47#	app.setAppleMenu_(foomenu)
 48#	#app.run()
 49#	print "Finished testing menus"
 50
 51#------------------------------------------------------------------------------
 52
 53class Application(GApplication):
 54    #  _ns_app          _PyGui_NSApplication
 55    #  _ns_pasteboard   NSPasteboard
 56    #  _ns_key_window   Window
 57    
 58    _ns_menubar_update_pending = False
 59
 60    def __init__(self, **kwds):
 61        #print "Application.__init__: argv =", sys.argv ###
 62        create_ns_application()
 63        self._ns_app = ns_application
 64        self._ns_app.pygui_app = self
 65        self._ns_init_standard_menu_items()
 66        self._ns_pasteboard = NSPasteboard.generalPasteboard()
 67        self._ns_key_window = None
 68        GApplication.__init__(self, **kwds)
 69        ns_application.init_application_name()
 70    
 71    def destroy(self):
 72        del self.menus[:]
 73        import Windows
 74        Windows._ns_zombie_window = None
 75        self._ns_app.pygui_app = None
 76        self._ns_app = None
 77        self._ns_pasteboard = None
 78        GApplication.destroy(self)
 79    
 80    def set_menus(self, menu_list):
 81        GApplication.set_menus(self, menu_list)
 82        self._update_menubar()
 83    
 84    def _update_menubar(self):
 85        ns_app = self._ns_app
 86        ns_menubar = NSMenu.alloc().initWithTitle_("")
 87        menu_list = self._effective_menus()
 88        for menu in menu_list:
 89            ns_item = NSMenuItem.alloc()
 90            ns_item.initWithTitle_action_keyEquivalent_(menu.title, '', "")
 91            ns_menubar.addItem_(ns_item)
 92            ns_menu = menu._ns_menu
 93            #  An NSMenu can only be a submenu of one menu at a time, so
 94            #  remove it from the old menubar if necessary.
 95            old_supermenu = ns_menu.supermenu()
 96            if old_supermenu:
 97                i = old_supermenu.indexOfItemWithSubmenu_(ns_menu)
 98                old_supermenu.removeItemAtIndex_(i)
 99            ns_menubar.setSubmenu_forItem_(ns_menu, ns_item)
100        # The menu you pass to setAppleMenu_ must *also* be a member of the
101        # main menu.
102        ns_app.setMainMenu_(ns_menubar)
103        ns_app_menu = menu_list[0]._ns_menu
104        ns_app.setAppleMenu_(ns_app_menu)
105
106#	def handle_events(self):
107#		#print "Application.handle_events: entering NS run loop" ###
108#		#try:
109#			self._ns_app.run()
110#		#finally:
111#			#print "Application.handle_events: exiting NS run loop" ###
112
113    def handle_next_event(self, modal_window = None):
114        #print "Application.handle_next_event" ###
115        ns_app = self._ns_app
116        if modal_window:
117            ns_mode = NSModalPanelRunLoopMode
118            ns_modal_window = modal_window._ns_window
119        else:
120            ns_mode = NSDefaultRunLoopMode
121            ns_modal_window = None
122        ns_event = ns_app.nextEventMatchingMask_untilDate_inMode_dequeue_(
123            NSAnyEventMask, None, ns_mode, True)
124        if ns_event:
125            ns_window = ns_event.window()
126            if not ns_window or not ns_modal_window or ns_window == ns_modal_window:
127                ns_app.sendEvent_(ns_event)
128    
129    def get_target_window(self):
130        #  NSApplication.keyWindow() isn't reliable enough. We keep track
131        #  of the key window ourselves.
132        return self._ns_key_window
133
134    def zero_windows_allowed(self):
135        return 1
136    
137    def query_clipboard(self):
138        pb = self._ns_pasteboard
139        pb_types = pb.types()
140        return NSStringPboardType in pb_types
141    
142    def get_clipboard(self):
143        raise NotImplementedError("TODO: Application.get_clipboard")
144    
145    def set_clipboard(self, data):
146        raise NotImplementedError("TODO: Application.set_clipboard")
147    
148    def _ns_init_standard_menu_items(self):
149        self._ns_standard_menu_items = {}
150        for (cmd_name, ns_selector) in _ns_standard_actions.iteritems():
151            self._ns_standard_menu_items[cmd_name] = NSMenuItem.alloc().\
152                    initWithTitle_action_keyEquivalent_("", ns_selector, "")
153    
154    def _dispatch_menu_setup(self, m):
155        for (cmd_name, ns_menu_item) in self._ns_standard_menu_items.iteritems():
156            ns_selector = ns_menu_item.action()
157            target = self._ns_app.targetForAction_(ns_selector)
158            if target and target.respondsToSelector_('validateMenuItem:'):
159                valid = target.validateMenuItem_(ns_menu_item)
160                m[cmd_name].enabled = valid
161        GApplication._dispatch_menu_setup(self, m)
162    
163    def setup_menus(self, m):
164        if not self._ns_app.modalWindow():
165            GApplication.setup_menus(self, m)
166    
167    def process_args(self, args):
168        #  Note: When using py2app, argv_emulation should be disabled.
169        if args and args[0].startswith("-psn"):
170            # Launched from MacOSX Finder -- wait for file open/app launch messages
171            pass
172        else:
173            # Not launched from Finder or using argv emulation
174            self._ns_app.using_clargs = True
175            GApplication.process_args(self, args)
176
177    def run(self, fast_exit = True):
178        #print "Application.run" ###
179        try:
180            GApplication.run(self)
181        except (KeyboardInterrupt, SystemExit):
182            pass
183        except:
184            traceback.print_exc()
185        #  A py2app bundled application seems to crash on exit if we don't
186        #  bail out really quickly here (Python 2.3, PyObjC 1.3.7, py2app 0.2.1,
187        #  MacOSX 10.4.4)
188        if fast_exit:
189            os._exit(0)
190
191    def event_loop(self):
192        #print "Application.event_loop" ###
193        self._ns_app.run()
194        #print "Exit Application.event_loop" ###
195    
196#	def _exit_event_loop(self):
197#		self._ns_app.stop_(self)
198
199    def _quit(self):
200        #print "Application._quit" ###
201        self._quit_flag = True
202        self._ns_app.stop_(self._ns_app)
203        #print "Exit Application._quit" ###
204    
205#------------------------------------------------------------------------------
206
207_ns_key_event_mask = AppKit.NSKeyDownMask | AppKit.NSKeyUpMask
208
209#------------------------------------------------------------------------------
210
211class _PyGui_NSApplication(NSApplication):
212
213    pygui_app = None
214    files_opened = False
215    using_clargs = False
216    pygui_menus_updated = False
217
218    def sendEvent_(self, ns_event):
219        #  Perform special processing of key events.
220        #  Perform menu setup when menu bar is clicked.
221        #  Remember the most recent mouse-moved event to use as the
222        #  location of event types which do not have a location themselves.
223        if pending_exception:
224            raise_pending_exception()
225        ns_type = ns_event.type()
226        #print "sendEvent_:", ns_event ###
227        self.pygui_menus_updated = False
228        if (1 << ns_type) & _ns_key_event_mask:
229            self.process_key_event(ns_event)
230        else:
231            if ns_type == NSMouseMoved:
232                Globals.ns_last_mouse_moved_event = ns_event
233                ns_window = ns_event.window()
234                if ns_window:
235                    ns_view = ns_window.contentView().hitTest_(ns_event.locationInWindow())
236                    if ns_view:
237                        ns_view.mouseMoved_(ns_event)
238            else:
239                NSApplication.sendEvent_(self, ns_event)
240    
241    def process_key_event(self, ns_event):
242        #  Perform menu setup before command-key events.
243        #  Send non-command key events to associated window if any,
244        #  otherwise pass them to the pygui application. This is necessary
245        #  because otherwise there is no way of receiving key events when
246        #  there are no windows.
247        #print "_PyGui_NSApplication.process_key_event:", ns_event ###
248        if ns_event.modifierFlags() & NSCommandKeyMask:
249            #self.perform_menu_setup()
250            NSApplication.sendEvent_(self, ns_event)
251        else:
252            ns_window = ns_event.window()
253            #print "_PyGui_NSApplication.process_key_event: ns_window =", ns_window ###
254            if ns_window:
255                ns_window.sendEvent_(ns_event)
256            else:
257                self.pass_event_to_application(ns_event)
258    
259    def menuNeedsUpdate_(self, ns_menu):
260        #print "_PyGui_NSApplication.menuNeedsUpdate_:", object.__repr__(ns_menu) ###
261        if not self.pygui_menus_updated:
262            #print "...updating menus" ###
263            self.perform_menu_setup()
264            self.pygui_menus_updated = True
265
266    def perform_menu_setup(self):
267        #print "_PyGui_NSApplication.perform_menu_setup" ###
268        app = self.pygui_app
269        if app:
270            app._perform_menu_setup()
271    
272    def pass_event_to_application(self, ns_event):
273        app = self.pygui_app
274        if app:
275            event = Event(ns_event)
276            app.handle(event.kind, event)
277
278#	def sendAction_to_from_(self, action, target, sender):
279#		print "_PyGui_NSApplication.sendAction_to_from_",  action, target, sender ###
280#		return NSApplication.sendAction_to_from_(self, action, target, sender)
281
282    def menuSelection_(self, ns_menu_item):
283        command = ns_menu_item.representedObject()
284        index = ns_menu_item.tag()
285        if index >= 0:
286            self.dispatch_to_app(command, index)
287        else:
288            self.dispatch_to_app(command)
289    
290    def dispatch_to_app(self, *args):
291        app = self.pygui_app
292        if app:
293            app.dispatch(*args)
294    
295    def validateMenuItem_(self, item):
296        return False
297    
298    def undo_(self, sender):
299        self.dispatch_to_app('undo_cmd')
300
301    def redo_(self, sender):
302        self.dispatch_to_app('redo_cmd')
303
304    def cut_(self, sender):
305        self.dispatch_to_app('cut_cmd')
306
307    def copy_(self, sender):
308        self.dispatch_to_app('copy_cmd')
309
310    def paste_(self, sender):
311        self.dispatch_to_app('paste_cmd')
312
313    def clear_(self, sender):
314        self.dispatch_to_app('clear_cmd')
315
316    def select_all_(self, sender):
317        self.dispatch_to_app('select_all_cmd')
318
319    def init_application_name(self):
320        #  Arrange for the application name to be used as the title
321        #  of the application menu.
322        ns_bundle = NSBundle.mainBundle()
323        if ns_bundle:
324            ns_info = ns_bundle.localizedInfoDictionary()
325            if ns_info:
326                if ns_info['CFBundleName'] == "Python":
327                    ns_info['CFBundleName'] = Globals.application_name
328                return
329        #raise RuntimeError("No bundle information found. "
330        #	"Perhaps the application was not run with pythonw?")
331    
332    def application_openFile_(self, ns_app, path):
333        if self.using_clargs:
334            return True
335        #print "PyGUI_NSApplication.application_openFile_:", path ###
336        # Bizarrely, argv[0] gets passed to application_openFile_ under
337        # some circumstances. We don't want to try to open it!
338        if path == sys.argv[0]:
339            return True
340        self.files_opened = True
341        try:
342            self.pygui_app.open_path(path)
343            return True
344        except Exception, e:
345            self.pygui_app.report_error()
346            return False
347    
348    def applicationDidFinishLaunching_(self, notification):
349        if self.using_clargs:
350            return
351        #print "PyGUI_NSApplication.applicationDidFinishLaunching_"
352        try:
353            if not self.files_opened:
354                self.pygui_app.open_app()
355        except Exception, e:
356            self.pygui_app.report_error()
357            return False
358
359#------------------------------------------------------------------------------
360
361pending_exception = None
362
363def raise_pending_exception():
364    global pending_exception
365    exc_type, exc_value, exc_tb = pending_exception
366    pending_exception = None
367    raise exc_type, exc_value, exc_tb
368
369def create_ns_application():
370    global ns_application
371    ns_application = _PyGui_NSApplication.sharedApplication()
372    ns_application.setDelegate_(ns_application)
373
374Globals.ns_screen_height = NSScreen.mainScreen().frame().size.height
375
376#------------------------------------------------------------------------------
377
378# Disable this for now, since MachSignals.signal segfaults. :-(
379#
380#def _install_sigint_handler():
381#		print "_install_sigint_handler" ###
382#		from Foundation import NSRunLoop
383#		run_loop = NSRunLoop.currentRunLoop()
384#		if not run_loop:
385#			print "...No current run loop" ###
386#			sys.exit(1) ###
387#		MachSignals.signal(signal.SIGINT, _sigint_handler)
388#		#from PyObjCTools.AppHelper import installMachInterrupt
389#		#installMachInterrupt()
390#		print "...done" ###
391#
392#def _sigint_handler(signum):
393#	print "_sigint_handler" ###
394#	raise KeyboardInterrupt
395
396#def _install_sigint_handler():
397#	import signal
398#	signal.signal(signal.SIGINT, _raise_keyboard_interrupt)
399#
400#def _raise_keyboard_interrupt(signum, frame):
401#	raise KeyboardInterrupt
402
403#_install_sigint_handler()