~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to osx/plat/frontends/widgets/window.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Miro - an RSS based video player application
 
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
17
#
 
18
# In addition, as a special exception, the copyright holders give
 
19
# permission to link the code of portions of this program with the OpenSSL
 
20
# library.
 
21
#
 
22
# You must obey the GNU General Public License in all respects for all of
 
23
# the code used other than OpenSSL. If you modify file(s) with this
 
24
# exception, you may extend this exception to your version of the file(s),
 
25
# but you are not obligated to do so. If you do not wish to do so, delete
 
26
# this exception statement from your version. If you delete this exception
 
27
# statement from all source files in the program, then also delete it here.
 
28
 
 
29
"""miro.plat.frontends.widgets.window -- Top-level Window class.  """
 
30
 
 
31
from AppKit import *
 
32
from Foundation import *
 
33
from objc import YES, NO, nil
 
34
from PyObjCTools import AppHelper
 
35
 
 
36
from miro import signals
 
37
from miro.frontends.widgets import widgetconst
 
38
from miro.plat.frontends.widgets import osxmenus
 
39
from miro.plat.frontends.widgets import wrappermap
 
40
from miro.plat.frontends.widgets.helpers import NotificationForwarder
 
41
from miro.plat.frontends.widgets.base import Widget, Container, FlippedView
 
42
from miro.plat.frontends.widgets.layout import VBox, HBox, Alignment
 
43
from miro.plat.frontends.widgets.control import Button
 
44
from miro.plat.frontends.widgets.simple import Label
 
45
from miro.plat.frontends.widgets.rect import Rect, NSRectWrapper
 
46
from miro.plat.utils import filename_to_unicode
 
47
 
 
48
# Tracks all windows that haven't been destroyed.  This makes sure there
 
49
# object stay alive as long as the window is alive.
 
50
alive_windows = set()
 
51
 
 
52
class MiroWindow(NSWindow):
 
53
    def handle_keyDown(self, event):
 
54
        key = event.charactersIgnoringModifiers()
 
55
        if len(key) != 1 or not key.isalpha():
 
56
            key = osxmenus.REVERSE_KEYS_MAP.get(key)
 
57
        mods = osxmenus.translate_event_modifiers(event)
 
58
        responder = self.firstResponder()
 
59
        while responder is not None:
 
60
            wrapper = wrappermap.wrapper(responder)
 
61
            if isinstance(wrapper, Widget) or isinstance(wrapper, Window):
 
62
                if wrapper.emit('key-press', key, mods):
 
63
                    return True # signal handler returned True, stop processing
 
64
            responder = responder.nextResponder()
 
65
 
 
66
    def sendEvent_(self, event):
 
67
        if event.type() == NSKeyDown:
 
68
            # We grab the KeyDown event here instead of with a keyDown handler
 
69
            # because some keys (notable Cmd-> and Cmd-< aren't caught by that
 
70
            # method.
 
71
            if self.handle_keyDown(event):
 
72
                return
 
73
        return NSWindow.sendEvent_(self, event)
 
74
 
 
75
class Window(signals.SignalEmitter):
 
76
    """See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
 
77
    def __init__(self, title, rect):
 
78
        signals.SignalEmitter.__init__(self)
 
79
        self.create_signal('active-change')
 
80
        self.create_signal('will-close')
 
81
        self.create_signal('did-move')
 
82
        self.create_signal('key-press')
 
83
        self.create_signal('show')
 
84
        self.create_signal('hide')
 
85
        self.nswindow = MiroWindow.alloc().initWithContentRect_styleMask_backing_defer_(
 
86
                rect.nsrect,
 
87
                self.get_style_mask(),
 
88
                NSBackingStoreBuffered,
 
89
                NO)
 
90
        self.nswindow.setTitle_(title)
 
91
        self.nswindow.setMinSize_(NSSize(800, 600))
 
92
        self.nswindow.setReleasedWhenClosed_(NO)
 
93
        self.content_view = FlippedView.alloc().initWithFrame_(rect.nsrect)
 
94
        self.content_view.setAutoresizesSubviews_(NO)
 
95
        self.nswindow.setContentView_(self.content_view)
 
96
        self.content_widget = None
 
97
        self.view_notifications = NotificationForwarder.create(self.content_view)
 
98
        self.view_notifications.connect(self.on_frame_change, 'NSViewFrameDidChangeNotification')
 
99
        self.window_notifications = NotificationForwarder.create(self.nswindow)
 
100
        self.window_notifications.connect(self.on_activate, 'NSWindowDidBecomeMainNotification')
 
101
        self.window_notifications.connect(self.on_deactivate, 'NSWindowDidResignMainNotification')
 
102
        self.window_notifications.connect(self.on_did_move, 'NSWindowDidMoveNotification')
 
103
        self.window_notifications.connect(self.on_will_close, 'NSWindowWillCloseNotification')
 
104
        wrappermap.add(self.nswindow, self)
 
105
        alive_windows.add(self)
 
106
 
 
107
    def get_style_mask(self):
 
108
        return NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask
 
109
 
 
110
    def set_title(self, title):
 
111
        self.nswindow.setTitle_(title)
 
112
 
 
113
    def on_frame_change(self, notification):
 
114
        self.place_child()
 
115
 
 
116
    def on_activate(self, notification):
 
117
        self.emit('active-change')
 
118
 
 
119
    def on_deactivate(self, notification):
 
120
        self.emit('active-change')
 
121
 
 
122
    def on_did_move(self, notification):
 
123
        self.emit('did-move')
 
124
 
 
125
    def on_will_close(self, notification):
 
126
        self.emit('will-close')
 
127
        self.emit('hide')
 
128
 
 
129
    def is_active(self):
 
130
        return self.nswindow.isMainWindow()
 
131
 
 
132
    def show(self):
 
133
        if self not in alive_windows:
 
134
            raise ValueError("Window destroyed")
 
135
        self.nswindow.makeKeyAndOrderFront_(nil)
 
136
        self.nswindow.makeMainWindow()
 
137
        self.emit('show')
 
138
 
 
139
    def close(self):
 
140
        self.nswindow.close()
 
141
 
 
142
    def destroy(self):
 
143
        self.window_notifications.disconnect()
 
144
        self.view_notifications.disconnect()
 
145
        self.nswindow.setContentView_(nil)
 
146
        wrappermap.remove(self.nswindow)
 
147
        alive_windows.discard(self)
 
148
        self.nswindow = None
 
149
 
 
150
    def place_child(self):
 
151
        rect = self.nswindow.contentRectForFrameRect_(self.nswindow.frame())
 
152
        self.content_widget.place(NSRect(NSPoint(0, 0), rect.size),
 
153
                self.content_view)
 
154
 
 
155
    def hookup_content_widget_signals(self):
 
156
        self.size_req_handler = self.content_widget.connect('size-request-changed',
 
157
                self.on_content_widget_size_request_change)
 
158
 
 
159
    def unhook_content_widget_signals(self):
 
160
        self.content_widget.disconnect(self.size_req_handler)
 
161
        self.size_req_handler = None
 
162
 
 
163
    def on_content_widget_size_request_change(self, widget, old_size):
 
164
        self.update_size_constraints()
 
165
 
 
166
    def set_content_widget(self, widget):
 
167
        if self.content_widget:
 
168
            self.content_widget.remove_viewport()
 
169
            self.unhook_content_widget_signals()
 
170
        self.content_widget = widget
 
171
        self.hookup_content_widget_signals()
 
172
        self.place_child()
 
173
        self.update_size_constraints()
 
174
 
 
175
    def update_size_constraints(self):
 
176
        width, height = self.content_widget.get_size_request()
 
177
        self.nswindow.setContentMinSize_(NSSize(width, height))
 
178
 
 
179
    def get_content_widget(self):
 
180
        return self.content_widget
 
181
        
 
182
    def get_frame(self):
 
183
        frame = self.nswindow.frame()
 
184
        frame.size.height -= 22
 
185
        return NSRectWrapper(frame)
 
186
 
 
187
    def connect_menu_keyboard_shortcuts(self):
 
188
        # All OS X windows are connected to the menu shortcuts
 
189
        pass
 
190
 
 
191
class MainWindow(Window):
 
192
    def __init__(self, title, rect):
 
193
        Window.__init__(self, title, rect)
 
194
        self.nswindow.setReleasedWhenClosed_(NO)
 
195
    def close(self):
 
196
        self.nswindow.orderOut_(nil)
 
197
 
 
198
class DialogBase(object):
 
199
    def __init__(self):
 
200
        self.sheet_parent = None
 
201
    def set_transient_for(self, window):
 
202
        self.sheet_parent = window
 
203
 
 
204
class Dialog(DialogBase):
 
205
    def __init__(self, title, description=None):
 
206
        DialogBase.__init__(self)
 
207
        self.title = title
 
208
        self.description = description
 
209
        self.buttons = []
 
210
        self.extra_widget = None
 
211
        self.window = None
 
212
        self.running = False
 
213
 
 
214
    def add_button(self, text):
 
215
        button = Button(text)
 
216
        button.set_size(widgetconst.SIZE_NORMAL)
 
217
        button.connect('clicked', self.on_button_clicked, len(self.buttons))
 
218
        self.buttons.append(button)
 
219
 
 
220
    def on_button_clicked(self, button, code):
 
221
        if self.sheet_parent is not None:
 
222
            NSApp().endSheet_returnCode_(self.window, code)
 
223
        else:
 
224
            NSApp().stopModalWithCode_(code)
 
225
 
 
226
    def build_text(self):
 
227
        vbox = VBox(spacing=6)
 
228
        if self.description:
 
229
            description_label = Label(self.description, wrap=True)
 
230
            description_label.set_bold(True)
 
231
            description_label.set_size_request(360, -1)
 
232
            vbox.pack_start(description_label)
 
233
        return vbox
 
234
 
 
235
    def build_buttons(self):
 
236
        hbox = HBox(spacing=12)
 
237
        for button in reversed(self.buttons):
 
238
            hbox.pack_start(button)
 
239
        alignment = Alignment(xalign=1.0, yscale=1.0)
 
240
        alignment.add(hbox)
 
241
        return alignment
 
242
 
 
243
    def build_content(self):
 
244
        vbox = VBox(spacing=12)
 
245
        vbox.pack_start(self.build_text())
 
246
        if self.extra_widget:
 
247
            vbox.pack_start(self.extra_widget)
 
248
        vbox.pack_start(self.build_buttons())
 
249
        alignment = Alignment(xscale=1.0, yscale=1.0)
 
250
        alignment.set_padding(12, 12, 17, 17)
 
251
        alignment.add(vbox)
 
252
        return alignment
 
253
 
 
254
    def build_window(self):
 
255
        self.content_widget = self.build_content()
 
256
        width, height = self.content_widget.get_size_request()
 
257
        width = max(width, 400)
 
258
        window = NSPanel.alloc()
 
259
        window.initWithContentRect_styleMask_backing_defer_(
 
260
                NSMakeRect(400, 400, width, height),
 
261
                NSTitledWindowMask, NSBackingStoreBuffered, NO)
 
262
        view = FlippedView.alloc().initWithFrame_(NSMakeRect(0, 0, width,
 
263
            height))
 
264
        window.setContentView_(view)
 
265
        window.setTitle_(self.title)
 
266
        self.content_widget.place(view.frame(), view)
 
267
        if self.buttons:
 
268
            self.buttons[0].make_default()
 
269
        return window
 
270
 
 
271
    def run(self):
 
272
        self.window = self.build_window()
 
273
        self.running = True
 
274
        if self.sheet_parent is None:
 
275
            response = NSApp().runModalForWindow_(self.window)
 
276
        else:
 
277
            delegate = SheetDelegate.alloc().init()
 
278
            NSApp().beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
 
279
                self.window, self.sheet_parent.nswindow, 
 
280
                delegate, 'sheetDidEnd:returnCode:contextInfo:', 0)
 
281
            response = NSApp().runModalForWindow_(self.window)
 
282
            if self.window:
 
283
                # self.window won't be around if we call destroy() to cancel
 
284
                # the dialog
 
285
                self.window.orderOut_(nil)
 
286
        self.running = False
 
287
 
 
288
        if response < 0:
 
289
            return -1
 
290
        return response
 
291
 
 
292
    def destroy(self):
 
293
        if self.running:
 
294
            NSApp().stopModalWithCode_(-1)
 
295
 
 
296
        self.window.setContentView_(None)
 
297
        self.window.close()
 
298
        self.window = None
 
299
        self.buttons = None
 
300
        self.extra_widget = None
 
301
 
 
302
    def set_extra_widget(self, widget):
 
303
        self.extra_widget = widget
 
304
 
 
305
    def get_extra_widget(self):
 
306
        return self.extra_widget
 
307
 
 
308
class SheetDelegate(NSObject):
 
309
    @AppHelper.endSheetMethod
 
310
    def sheetDidEnd_returnCode_contextInfo_(self, sheet, return_code, info):
 
311
        NSApp().stopModalWithCode_(return_code)
 
312
 
 
313
class FileDialogBase(DialogBase):
 
314
    def __init__(self):
 
315
        DialogBase.__init__(self)
 
316
        self._types = None
 
317
        self._filename = None
 
318
        self._directory = None
 
319
        self._filter_on_run = True
 
320
 
 
321
    def run(self):
 
322
        self._panel.setAllowedFileTypes_(self._types)
 
323
        if self.sheet_parent is None:
 
324
            if self._filter_on_run:
 
325
                response = self._panel.runModalForDirectory_file_types_(self._directory, self._filename, self._types)
 
326
            else:
 
327
                response = self._panel.runModalForDirectory_file_(self._directory, self._filename)
 
328
        else:
 
329
            delegate = SheetDelegate.alloc().init()
 
330
            if self._filter_on_run:
 
331
                self._panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
 
332
                    self._directory, self._filename, self._types,
 
333
                    self.sheet_parent.nswindow, delegate, 'sheetDidEnd:returnCode:contextInfo:', 0)
 
334
            else:
 
335
                self._panel.beginSheetForDirectory_file_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
 
336
                    self._directory, self._filename,
 
337
                    self.sheet_parent.nswindow, delegate, 'sheetDidEnd:returnCode:contextInfo:', 0)
 
338
            response = NSApp().runModalForWindow_(self._panel)
 
339
            self._panel.orderOut_(nil)
 
340
        return response
 
341
 
 
342
class FileSaveDialog(FileDialogBase):
 
343
    def __init__(self, title):
 
344
        FileDialogBase.__init__(self)
 
345
        self._title = title
 
346
        self._panel = NSSavePanel.savePanel()
 
347
        self._panel.setCanChooseFiles_(YES)
 
348
        self._panel.setCanChooseDirectories_(NO)
 
349
        self._filename = None
 
350
        self._filter_on_run = False
 
351
 
 
352
    def set_filename(self, s):
 
353
        self._filename = filename_to_unicode(s)
 
354
 
 
355
    def get_filename(self):
 
356
        # Use encode('utf-8') instead of unicode_to_filename, because
 
357
        # unicode_to_filename has code to make sure nextFilename works, but it's
 
358
        # more important here to not change the filename.
 
359
        return self._filename.encode('utf-8')
 
360
 
 
361
    def run(self):
 
362
        response = FileDialogBase.run(self)            
 
363
        if response == NSFileHandlingPanelOKButton:            
 
364
            self._filename = self._panel.filename()
 
365
            return 0
 
366
        self._filename = ""
 
367
 
 
368
    def destroy(self):
 
369
        self._panel = None
 
370
 
 
371
class FileOpenDialog(FileDialogBase):
 
372
    def __init__(self, title):
 
373
        FileDialogBase.__init__(self)
 
374
        self._title = title
 
375
        self._panel = NSOpenPanel.openPanel()
 
376
        self._panel.setCanChooseFiles_(YES)
 
377
        self._panel.setCanChooseDirectories_(NO)
 
378
        self._filenames = None
 
379
 
 
380
    def set_select_multiple(self, value):
 
381
        if value:
 
382
            self._panel.setAllowsMultipleSelection_(YES)
 
383
        else:
 
384
            self._panel.setAllowsMultipleSelection_(NO)
 
385
 
 
386
    def set_directory(self, d):
 
387
        self._directory = filename_to_unicode(d)
 
388
 
 
389
    def set_filename(self, s):
 
390
        self._filename = filename_to_unicode(s)
 
391
 
 
392
    def add_filters(self, filters):
 
393
        self._types = []
 
394
        for _, t in filters:
 
395
            self._types += t
 
396
 
 
397
    def get_filename(self):
 
398
        return self.get_filenames()[0]
 
399
 
 
400
    def get_filenames(self):
 
401
        # Use encode('utf-8') instead of unicode_to_filename, because
 
402
        # unicode_to_filename has code to make sure nextFilename works, but it's
 
403
        # more important here to not change the filename.
 
404
        return [f.encode('utf-8') for f in self._filenames]
 
405
 
 
406
    def run(self):
 
407
        response = FileDialogBase.run(self)            
 
408
        if response == NSFileHandlingPanelOKButton:            
 
409
            self._filenames = self._panel.filenames()
 
410
            return 0
 
411
        self._filename = ''
 
412
        self._filenames = None
 
413
 
 
414
    def destroy(self):
 
415
        self._panel = None
 
416
 
 
417
class DirectorySelectDialog(FileDialogBase):
 
418
    def __init__(self, title):
 
419
        FileDialogBase.__init__(self)
 
420
        self._title = title
 
421
        self._panel = NSOpenPanel.openPanel()
 
422
        self._panel.setCanChooseFiles_(NO)
 
423
        self._panel.setCanChooseDirectories_(YES)
 
424
        self._directory = None
 
425
 
 
426
    def set_directory(self, d):
 
427
        self._directory = filename_to_unicode(d)
 
428
 
 
429
    def get_directory(self):
 
430
        # Use encode('utf-8') instead of unicode_to_filename, because
 
431
        # unicode_to_filename has code to make sure nextFilename
 
432
        # works, but it's more important here to not change the
 
433
        # filename.
 
434
        return self._directory.encode('utf-8')
 
435
 
 
436
    def run(self):
 
437
        response = FileDialogBase.run(self)            
 
438
        if response == NSFileHandlingPanelOKButton:
 
439
            self._directory = self._panel.filenames()[0]
 
440
            return 0
 
441
        self._directory = ""
 
442
 
 
443
    def destroy(self):
 
444
        self._panel = None
 
445
 
 
446
class AboutDialog(DialogBase):
 
447
    def run(self):
 
448
        NSApplication.sharedApplication().orderFrontStandardAboutPanel_(nil)
 
449
    def destroy(self):
 
450
        pass
 
451
 
 
452
class AlertDialog(DialogBase):
 
453
    def __init__(self, title, message, alert_type):
 
454
        DialogBase.__init__(self)
 
455
        self._nsalert = NSAlert.alloc().init();
 
456
        self._nsalert.setMessageText_(title)
 
457
        self._nsalert.setInformativeText_(message)
 
458
        self._nsalert.setAlertStyle_(alert_type)
 
459
    def add_button(self, text):
 
460
        self._nsalert.addButtonWithTitle_(text)
 
461
    def run(self):
 
462
        self._nsalert.runModal()
 
463
    def destroy(self):
 
464
        self._nsalert = nil
 
465
 
 
466
class PreferenceItem (NSToolbarItem):
 
467
 
 
468
    def setPanel_(self, panel):
 
469
        self.panel = panel
 
470
 
 
471
class ToolbarDelegate (NSObject):
 
472
 
 
473
    def initWithPanels_identifiers_window_(self, panels, identifiers, window):
 
474
        self = super(ToolbarDelegate, self).init()
 
475
        self.panels = panels
 
476
        self.identifiers = identifiers
 
477
        self.window = window
 
478
        return self
 
479
 
 
480
    def toolbarAllowedItemIdentifiers_(self, toolbar):
 
481
        return self.identifiers
 
482
 
 
483
    def toolbarDefaultItemIdentifiers_(self, toolbar):
 
484
        return self.identifiers
 
485
 
 
486
    def toolbarSelectableItemIdentifiers_(self, toolbar):
 
487
        return self.identifiers
 
488
 
 
489
    def toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar_(self, toolbar, itemIdentifier, flag):
 
490
        panel = self.panels[itemIdentifier]
 
491
        item = PreferenceItem.alloc().initWithItemIdentifier_(itemIdentifier)
 
492
        item.setLabel_(panel[1])
 
493
        item.setImage_(NSImage.imageNamed_(u"pref_item_%s" % itemIdentifier))
 
494
        item.setAction_("switchPreferenceView:")
 
495
        item.setTarget_(self)
 
496
        item.setPanel_(panel[0])
 
497
        return item
 
498
 
 
499
    def validateToolbarItem_(self, item):
 
500
        return YES
 
501
 
 
502
    def switchPreferenceView_(self, sender):
 
503
        self.window.do_select_panel(sender.panel, YES)
 
504
 
 
505
class PreferencesWindow (Window):
 
506
    def __init__(self, title):
 
507
        Window.__init__(self, title, Rect(0, 0, 640, 440))
 
508
        self.panels = dict()
 
509
        self.identifiers = list()
 
510
        self.first_show = True
 
511
        self.nswindow.setShowsToolbarButton_(NO)
 
512
        self.nswindow.setReleasedWhenClosed_(NO)
 
513
        self.app_notifications = NotificationForwarder.create(NSApp())
 
514
        self.app_notifications.connect(self.on_app_quit, 
 
515
            'NSApplicationWillTerminateNotification')
 
516
 
 
517
    def destroy(self):
 
518
        super(PreferencesWindow, self).destroy()
 
519
        self.app_notifications.disconnect()
 
520
 
 
521
    def get_style_mask(self):
 
522
        return NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask
 
523
 
 
524
    def append_panel(self, name, panel, title, image_name):
 
525
        self.panels[name] = (panel, title)
 
526
        self.identifiers.append(name)
 
527
 
 
528
    def finish_panels(self):
 
529
        self.tbdelegate = ToolbarDelegate.alloc().initWithPanels_identifiers_window_(self.panels, self.identifiers, self)
 
530
        toolbar = NSToolbar.alloc().initWithIdentifier_(u"Preferences")
 
531
        toolbar.setAllowsUserCustomization_(NO)
 
532
        toolbar.setDelegate_(self.tbdelegate)
 
533
 
 
534
        self.nswindow.setToolbar_(toolbar)
 
535
       
 
536
    def select_panel(self, index):
 
537
        panel = self.identifiers[index]
 
538
        self.nswindow.toolbar().setSelectedItemIdentifier_(panel)
 
539
        self.do_select_panel(self.panels[panel][0], NO)
 
540
 
 
541
    def do_select_panel(self, panel, animate):
 
542
        wframe = self.nswindow.frame()
 
543
        vsize = list(panel.get_size_request())
 
544
        if vsize[0] < 500:
 
545
            vsize[0] = 500
 
546
        if vsize[1] < 200:
 
547
            vsize[1] = 200
 
548
 
 
549
        toolbarHeight = wframe.size.height - self.nswindow.contentView().frame().size.height
 
550
        wframe.origin.y += wframe.size.height - vsize[1] - toolbarHeight
 
551
        wframe.size = (vsize[0], vsize[1] + toolbarHeight)
 
552
 
 
553
        self.set_content_widget(panel)
 
554
        self.nswindow.setFrame_display_animate_(wframe, YES, animate)
 
555
 
 
556
    def show(self):
 
557
        if self.first_show:
 
558
            self.nswindow.center()
 
559
            self.first_show = False
 
560
        Window.show(self)
 
561
 
 
562
    def on_app_quit(self, notification):
 
563
        self.close()