1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Defines a component that represents an application (browser native) window
17 or a sub window."""
18
19 from urlparse import urljoin
20
21 from muntjac.event.shortcut_listener import ShortcutListener
22 from muntjac.terminal.uri_handler import IUriHandler
23 from muntjac.terminal.gwt.client.ui.v_view import VView
24 from muntjac.terminal.parameter_handler import IParameterHandler
25 from muntjac.terminal.sizeable import ISizeable
26
27 from muntjac.ui.client_widget import LoadStyle
28 from muntjac.ui.panel import Panel
29 from muntjac.ui.component import Event as ComponentEvent
30 from muntjac.ui.abstract_component import AbstractComponent
31
32 from muntjac.event.field_events import \
33 (IFocusNotifier, IBlurNotifier, FocusEvent, BlurEvent, IBlurListener,
34 IFocusListener)
35
36 from muntjac.util import OrderedSet
37
38
40 """An interface used for listening to Window close events. Add the
41 ICloseListener to a browser level window or a sub window and
42 L{ICloseListener.windowClose} will be called whenever
43 the user closes the window.
44
45 Removing windows using L{removeWindow} does now fire the ICloseListener.
46 """
47
49 """Called when the user closes a window. Use
50 L{CloseEvent.getWindow} to get a reference to the
51 L{Window} that was closed.
52
53 @param e: Event containing
54 """
55 raise NotImplementedError
56
57
58 _WINDOW_CLOSE_METHOD = getattr(ICloseListener, 'windowClose')
59
60
62 """Listener for window resize events.
63
64 @see: L{ResizeEvent}
65 """
66
68 raise NotImplementedError
69
70
71 _WINDOW_RESIZE_METHOD = getattr(IResizeListener, 'windowResized')
72
73
74 -class Window(Panel, IUriHandler, IParameterHandler, IFocusNotifier,
75 IBlurNotifier):
76 """A component that represents an application (browser native) window or
77 a sub window.
78
79 If the window is a application window or a sub window depends on how it
80 is added to the application. Adding a C{Window} to a C{Window}
81 using L{Window.addWindow} makes it a sub window and adding a
82 C{Window} to the C{Application} using
83 L{Application.addWindow} makes it an application window.
84
85 An application window is the base of any view in a Muntjac application. All
86 applications contain a main application window (set using
87 L{ApplicationsetMainWindow} which is what is initially shown
88 to the user. The contents of a window is set using
89 L{setContent}. The contents can in turn contain
90 other components. For multi-tab applications there is one window instance
91 per opened tab.
92
93 A sub window is floating popup style window that can be added to an
94 application window. Like the application window its content is set using
95 L{setContent}. A sub window can be positioned on
96 the screen using absolute coordinates (pixels). The default content of the
97 Window is set to be suitable for application windows. For sub windows it
98 might be necessary to set the size of the content to work as expected.
99
100 Window caption is displayed in the browser title bar for application level
101 windows and in the window header for sub windows.
102
103 Certain methods in this class are only meaningful for sub windows and other
104 parts only for application windows. These are marked using B{Sub window
105 only} and B{Application window only} respectively in the API doc.
106
107 @author: Vaadin Ltd.
108 @author: Richard Lincoln
109 @version: 1.1.2
110 """
111
112 CLIENT_WIDGET = None
113
114
115
116 BORDER_NONE = 0
117
118
119
120 BORDER_MINIMAL = 1
121
122
123
124 BORDER_DEFAULT = 2
125
126
127 - def __init__(self, caption='', content=None):
128 """Creates a new unnamed window with the given content and title.
129
130 @param caption:
131 the title of the window.
132 @param content:
133 the contents of the window
134 """
135
136
137 self._terminal = None
138
139
140
141 self._application = None
142
143
144
145 self._uriHandlerList = None
146
147
148
149 self._parameterHandlerList = None
150
151
152
153 self._subwindows = OrderedSet()
154
155
156
157 self._theme = None
158
159
160
161
162 self._openList = list()
163
164
165
166 self._name = None
167
168
169 self._border = self.BORDER_DEFAULT
170
171
172
173 self._positionY = -1
174
175
176
177 self._positionX = -1
178
179
180
181
182 self._notifications = None
183
184
185 self._modal = False
186
187
188
189 self._resizable = True
190
191
192
193 self._draggable = True
194
195
196
197 self._centerRequested = False
198
199
200 self._resizeLazy = False
201
202
203
204 self._pendingFocus = None
205
206
207
208
209 self._jsExecQueue = None
210
211
212
213 self._scrollIntoView = None
214
215 super(Window, self).__init__(caption, content)
216
217 self.setScrollable(True)
218
219 self.setSizeUndefined()
220
221 self._bringToFront = None
222
223
224
225
226
227
228 self._bringToFrontSequence = 0
229
230 self.closeShortcut = None
231
232
234 if isinstance(c, Window):
235 raise ValueError, ('Window cannot be added to another '
236 'via addComponent. Use addWindow(Window) instead.')
237 super(Window, self).addComponent(c)
238
239
241 """B{Application window only}. Gets the user terminal.
242
243 @return: the user terminal
244 """
245 return self._terminal
246
247
249 """Gets the parent window of the component.
250
251 This is always the window itself.
252
253 B{This method is not meant to be overridden.}
254
255 @see: L{IComponent.getWindow}
256 @return: the window itself
257 """
258 return self
259
260
266
267
269 """Gets the parent component of the window.
270
271 The parent of an application window is always null. The parent of a
272 sub window is the application window the sub window is attached to.
273
274 B{This method is not meant to be overridden.}
275
276 @return: the parent window
277 @see: L{IComponent.getParent}
278 """
279 return super(Window, self).getParent()
280
281
283 """B{Application window only}. Adds a new URI handler to this
284 window. If this is a sub window the URI handler is attached to the
285 parent application window.
286
287 @param handler:
288 the URI handler to add.
289 """
290 if self.getParent() is not None:
291
292
293 mainWindow = self.getParent()
294 mainWindow.addURIHandler(handler)
295 else:
296 if self._uriHandlerList is None:
297 self._uriHandlerList = list()
298 if handler not in self._uriHandlerList:
299 self._uriHandlerList.append(handler)
300
301
303 """B{Application window only}. Removes the URI handler from this
304 window. If this is a sub window the URI handler is removed from the
305 parent application window.
306
307 @param handler:
308 the URI handler to remove.
309 """
310 if self.getParent() is not None:
311
312 mainWindow = self.getParent()
313 mainWindow.removeURIHandler(handler)
314 else:
315 if handler is None or self._uriHandlerList is None:
316 return
317 self._uriHandlerList.remove(handler)
318 if len(self._uriHandlerList) == 0:
319 self._uriHandlerList = None
320
321
323 """B{Application window only}. Handles an URI by passing the URI
324 to all URI handlers defined using L{addURIHandler}.
325 All URI handlers are called for each URI but no more than one handler
326 may return a L{DownloadStream}. If more than one stream is
327 returned a C{RuntimeException} is thrown.
328
329 @param context:
330 The URL of the application
331 @param relativeUri:
332 The URI relative to C{context}
333 @return: A C{DownloadStream} that one of the URI handlers
334 returned, null if no C{DownloadStream} was returned.
335 """
336 result = None
337
338 if self._uriHandlerList is not None:
339 handlers = list(self._uriHandlerList)
340
341 for handler in handlers:
342 ds = handler.handleURI(context, relativeUri)
343 if ds is not None:
344 if result is not None:
345 raise RuntimeError(('handleURI for ' + context
346 + ' uri: \'' + relativeUri
347 + '\' returns ambigious result.'))
348 result = ds
349
350 return result
351
352
354 """B{Application window only}. Adds a new parameter handler to
355 this window. If this is a sub window the parameter handler is attached
356 to the parent application window.
357
358 @param handler:
359 the parameter handler to add.
360 """
361 if self.getParent() is not None:
362
363
364 mainWindow = self.getParent()
365 mainWindow.addParameterHandler(handler)
366 else:
367 if self._parameterHandlerList is None:
368 self._parameterHandlerList = list()
369 if handler not in self._parameterHandlerList:
370 self._parameterHandlerList.append(handler)
371
372
374 """B{Application window only}. Removes the parameter handler from
375 this window. If this is a sub window the parameter handler is removed
376 from the parent application window.
377
378 @param handler:
379 the parameter handler to remove.
380 """
381 if self.getParent() is not None:
382
383 mainWindow = self.getParent()
384 mainWindow.removeParameterHandler(handler)
385 else:
386 if handler is None or self._parameterHandlerList is None:
387 return
388 self._parameterHandlerList.remove(handler)
389 if len(self._parameterHandlerList) == 0:
390 self._parameterHandlerList = None
391
392
394 """B{Application window only}. Handles parameters by passing the
395 parameters to all C{IParameterHandler}s defined using
396 L{addParameterHandler}. All C{IParameterHandler}s are called for
397 each set of parameters.
398
399 @param parameters:
400 a map containing the parameter names and values
401 @see: L{IParameterHandler.handleParameters}
402 """
403 if self._parameterHandlerList is not None:
404 handlers = list(self._parameterHandlerList)
405 for handler in handlers:
406 handler.handleParameters(parameters)
407
408
410 """B{Application window only}. Gets the theme for this window.
411
412 If the theme for this window is not explicitly set, the application
413 theme name is returned. If the window is not attached to an
414 application, the terminal default theme name is returned. If the theme
415 name cannot be determined, null is returned
416
417 Subwindows do not support themes and return the theme used by the
418 parent window
419
420 @return: the name of the theme used for the window
421 """
422 if self.getParent() is not None:
423 return self.getParent().getTheme()
424
425 if self._theme is not None:
426 return self._theme
427
428 if self._application is not None \
429 and self._application.getTheme() is not None:
430 return self._application.getTheme()
431
432 if self._terminal is not None:
433 return self._terminal.getDefaultTheme()
434
435 return None
436
437
439 """B{Application window only}. Sets the name of the theme to
440 use for this window. Changing the theme will cause the page to be
441 reloaded.
442
443 @param theme:
444 the name of the new theme for this window or null to
445 use the application theme.
446 """
447 if self.getParent() is not None:
448 raise NotImplementedError, \
449 'Setting theme for sub-windows is not supported.'
450 self._theme = theme
451 self.requestRepaint()
452
453
454 - def paintContent(self, target):
455
456 name = self.getName()
457 target.addAttribute('name', '' if name is None else name)
458
459
460 theme = self.getTheme()
461 target.addAttribute('theme', '' if theme is None else theme)
462
463 if self._modal:
464 target.addAttribute('modal', True)
465
466 if self._resizable:
467 target.addAttribute('resizable', True)
468
469 if self._resizeLazy:
470 target.addAttribute(VView.RESIZE_LAZY, self._resizeLazy)
471
472 if not self._draggable:
473
474
475 target.addAttribute('fixedposition', True)
476
477 if self._bringToFront is not None:
478 target.addAttribute('bringToFront', int(self._bringToFront))
479 self._bringToFront = None
480
481 if self._centerRequested:
482 target.addAttribute('center', True)
483 self._centerRequested = False
484
485 if self._scrollIntoView is not None:
486 target.addAttribute('scrollTo', self._scrollIntoView)
487 self._scrollIntoView = None
488
489
490 if (self.getApplication() is not None
491 and self == self.getApplication().getMainWindow()):
492 target.addAttribute('main', True)
493
494 if self.getContent() is not None:
495 if (self.getContent().getHeightUnits()
496 == ISizeable.UNITS_PERCENTAGE):
497 target.addAttribute('layoutRelativeHeight', True)
498
499 if (self.getContent().getWidthUnits()
500 == ISizeable.UNITS_PERCENTAGE):
501 target.addAttribute('layoutRelativeWidth', True)
502
503
504 if len(self._openList) > 0:
505 for ol in self._openList:
506 ol.paintContent(target)
507 del self._openList[:]
508
509
510 super(Window, self).paintContent(target)
511
512
513 if self._jsExecQueue is not None:
514 for script in self._jsExecQueue:
515 target.startTag('execJS')
516 target.addAttribute('script', script)
517 target.endTag('execJS')
518 self._jsExecQueue = None
519
520
521 target.addVariable(self, 'positionx', self.getPositionX())
522 target.addVariable(self, 'positiony', self.getPositionY())
523
524
525 target.addVariable(self, 'close', False)
526
527 if self.getParent() is None:
528
529 for w in self._subwindows:
530 w.paint(target)
531 else:
532
533 target.addAttribute('sub', True)
534
535
536 if self._notifications is not None:
537 target.startTag('notifications')
538 for n in self._notifications:
539 target.startTag('notification')
540 if n.getCaption() is not None:
541 target.addAttribute('caption', n.getCaption())
542
543 if n.getMessage() is not None:
544 target.addAttribute('message', n.getMessage())
545
546 if n.getIcon() is not None:
547 target.addAttribute('icon', n.getIcon())
548
549 if not n.isHtmlContentAllowed():
550 target.addAttribute(
551 VView.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED, True)
552
553 target.addAttribute('position', n.getPosition())
554 target.addAttribute('delay', n.getDelayMsec())
555
556 if n.getStyleName() is not None:
557 target.addAttribute('style', n.getStyleName())
558
559 target.endTag('notification')
560
561 target.endTag('notifications')
562 self._notifications = None
563
564 if self._pendingFocus is not None:
565
566 if (self._pendingFocus.getWindow() == self
567 or self._pendingFocus.getWindow() is not None
568 and self._pendingFocus.getWindow().getParent() == self):
569 target.addAttribute('focused', self._pendingFocus)
570
571 self._pendingFocus = None
572
573
589
590
591 - def open(self, resource, windowName=None, width=-1, height=-1,
592 border=None):
593 """Opens the given resource in a window with the given size, border and
594 name.
595
596 The supplied C{windowName} is used as the target name in a
597 window.open call in the client. This means that special values such as
598 "_blank", "_self", "_top", "_parent" have special meaning. An empty or
599 C{None} window name is also a special case.
600
601 "", null and "_self" as C{windowName} all causes the resource to
602 be opened in the current window, replacing any old contents. For
603 downloadable content you should avoid "_self" as "_self" causes the
604 client to skip rendering of any other changes as it considers them
605 irrelevant (the page will be replaced by the resource). This can speed
606 up the opening of a resource, but it might also put the client side
607 into an inconsistent state if the window content is not completely
608 replaced e.g., if the resource is downloaded instead of displayed in
609 the browser.
610
611 "_blank" as C{windowName} causes the resource to always be opened
612 in a new window or tab (depends on the browser and browser settings).
613
614 "_top" and "_parent" as C{windowName} works as specified by the
615 HTML standard.
616
617 Any other C{windowName} will open the resource in a window with
618 that name, either by opening a new window/tab in the browser or by
619 replacing the contents of an existing window with that name.
620
621 @param resource:
622 the resource.
623 @param windowName:
624 the name of the window.
625 @param width:
626 the width of the window in pixels
627 @param height:
628 the height of the window in pixels
629 @param border:
630 the border style of the window. See
631 L{Window.BORDER_* constants<BORDER_NONE>}
632 """
633 if border is None:
634 border = self.BORDER_DEFAULT
635
636 if resource not in self._openList:
637 r = OpenResource(resource, windowName, width, height, border)
638 self._openList.append(r)
639
640 self.requestRepaint()
641
642
644 """Gets the full URL of the window. The returned URL is window
645 specific and can be used to directly refer to the window.
646
647 Note! This method can not be used for portlets.
648
649 @return: the URL of the window or null if the window is not attached
650 to an application
651 """
652 if self._application is None:
653 return None
654
655 try:
656
657 return urljoin(self._application.getURL(), self.getName() + '/')
658 except Exception:
659 raise RuntimeError, \
660 'Internal problem getting window URL, please report'
661
662
664 """B{Application window only}. Gets the unique name of the window.
665 The name of the window is used to uniquely identify it.
666
667 The name also determines the URL that can be used for direct access to
668 a window. All windows can be accessed through
669 C{http://host:port/app/win} where C{http://host:port/app} is
670 the application URL (as returned by L{Application.getURL} and
671 C{win} is the window name.
672
673 @return: the name of the Window.
674 """
675 return self._name
676
677
679 """Returns the border style of the window.
680
681 @see: L{setBorder}
682 @return: the border style for the window
683 """
684 return self._border
685
686
688 """Sets the border style for this window. Valid values are
689 L{Window.BORDER_NONE}, L{Window.BORDER_MINIMAL},
690 L{Window.BORDER_DEFAULT}.
691
692 B{Note!} Setting this seems to currently have no effect
693 whatsoever on the window.
694
695 @param border:
696 the border style to set
697 """
698 self._border = border
699
700
702 """Sets the application this window is attached to.
703
704 This method is called by the framework and should not be called
705 directly from application code. L{Application.addWindow}
706 should be used to add the window to an application and
707 L{Application.removeWindow} to remove the window from the application.
708
709 This method invokes L{IComponent.attach} and
710 L{IComponent.detach} methods when necessary.
711
712 @param application:
713 the application the window is attached to
714 """
715
716 if application == self._application:
717 return
718
719
720 if self._application is not None:
721 self.detach()
722
723
724 self._application = application
725
726
727 if application is not None:
728 self.attach()
729
730
732 """B{Application window only}. Sets the unique name of the window.
733 The name of the window is used to uniquely identify it inside the
734 application.
735
736 The name also determines the URL that can be used for direct access to
737 a window. All windows can be accessed through
738 C{http://host:port/app/win} where C{http://host:port/app} is
739 the application URL (as returned by L{Application.getURL} and
740 C{win} is the window name.
741
742 This method can only be called before the window is added to an
743 application.
744
745 @param name:
746 the new name for the window or null if the application
747 should automatically assign a name to it
748 @raise ValueError:
749 if the window is attached to an application
750 """
751
752 if self.getApplication() is not None:
753 raise ValueError, ('Window name can not be changed while '
754 'the window is in application')
755 self._name = name
756
757
759 """Sets the user terminal. Used by the terminal adapter, should never
760 be called from application code.
761
762 @param typ:
763 the terminal to set.
764 """
765 self._terminal = typ
766
767
815
816
818 """Method that handles window closing (from UI).
819
820 By default, sub-windows are removed from their respective parent
821 windows and thus visually closed on browser-side. Browser-level windows
822 also closed on the client-side, but they are not implicitly removed
823 from the application.
824
825 To explicitly close a sub-window, use L{removeWindow}.
826 To react to a window being closed (after it is closed), register a
827 L{ICloseListener}.
828 """
829 parent = self.getParent()
830 if parent is None:
831 self.fireClose()
832 else:
833
834 parent.focus()
835
836
837 parent.removeWindow(self)
838
839
841 """Gets the distance of Window left border in pixels from left border
842 of the containing (main window).
843
844 @return: the Distance of Window left border in pixels from left border
845 of the containing (main window). or -1 if unspecified.
846 """
847 return self._positionX
848
849
851 """Sets the distance of Window left border in pixels from left border
852 of the containing (main window).
853
854 @param positionX:
855 the distance of window left border in pixels from
856 left border of the containing (main window). or -1
857 if unspecified.
858 @param repaintRequired:
859 true if the window needs to be repainted, false otherwise
860 """
861 self._positionX = positionX
862 self._centerRequested = False
863 if repaintRequired:
864 self.requestRepaint()
865
866
868 """Gets the distance of Window top border in pixels from top border
869 of the containing (main window).
870
871 @return: Distance of Window top border in pixels from top border of
872 the containing (main window). or -1 if unspecified.
873 """
874 return self._positionY
875
876
878 """Sets the distance of Window top border in pixels from top border
879 of the containing (main window).
880
881 @param positionY:
882 the distance of window top border in pixels from top border
883 of the containing (main window). or -1 if unspecified
884 @param repaintRequired:
885 true if the window needs to be repainted, false otherwise
886 """
887 self._positionY = positionY
888 self._centerRequested = False
889 if repaintRequired:
890 self.requestRepaint()
891
892
894 """Adds a close/resize/focus/blur listener to the window.
895
896 For a sub window the ICloseListener is fired when the user closes it
897 (clicks on the close button).
898
899 For a browser level window the ICloseListener is fired when the
900 browser level window is closed. Note that closing a browser level
901 window does not mean it will be destroyed.
902
903 Note, that focus/blur listeners in Window class are only supported by
904 sub windows. Also note that Window is not considered focused if its
905 contained component currently has focus.
906
907 @param listener:
908 the listener to add.
909 @see: L{IFocusNotifier.addListener}
910 @see: L{IBlurNotifier.addListener}
911 """
912 if (isinstance(listener, IBlurListener) and
913 (iface is None or issubclass(iface, IBlurListener))):
914 self.registerListener(BlurEvent.EVENT_ID,
915 BlurEvent, listener, IBlurListener.blurMethod)
916
917 if (isinstance(listener, ICloseListener) and
918 (iface is None or issubclass(iface, ICloseListener))):
919 self.registerListener(CloseEvent,
920 listener, _WINDOW_CLOSE_METHOD)
921
922 if (isinstance(listener, IFocusListener) and
923 (iface is None or issubclass(iface, IFocusListener))):
924 self.registerListener(FocusEvent.EVENT_ID,
925 FocusEvent, listener, IFocusListener.focusMethod)
926
927 if (isinstance(listener, IResizeListener) and
928 (iface is None or issubclass(iface, IResizeListener))):
929 self.registerListener(ResizeEvent, listener, _WINDOW_RESIZE_METHOD)
930
931 super(Window, self).addListener(listener, iface)
932
933
934 - def addCallback(self, callback, eventType=None, *args):
935 if eventType is None:
936 eventType = callback._eventType
937
938 if issubclass(eventType, BlurEvent):
939 self.registerCallback(BlurEvent, callback,
940 BlurEvent.EVENT_ID, *args)
941
942 elif issubclass(eventType, CloseEvent):
943 self.registerCallback(CloseEvent, callback, None, *args)
944
945 elif issubclass(eventType, FocusEvent):
946 self.registerCallback(FocusEvent, callback,
947 FocusEvent.EVENT_ID, *args)
948
949 elif issubclass(eventType, ResizeEvent):
950 self.registerCallback(ResizeEvent, callback, None, *args)
951
952 else:
953 super(Window, self).addCallback(callback, eventType, *args)
954
955
957 """Removes the close/resize from the window.
958
959 For more information on CloseListeners see L{ICloseListener}.
960
961 @param listener:
962 the listener to remove.
963 """
964 if (isinstance(listener, IBlurListener) and
965 (iface is None or issubclass(iface, IBlurListener))):
966 self.withdrawListener(BlurEvent.EVENT_ID,
967 BlurEvent, listener)
968
969 if (isinstance(listener, ICloseListener) and
970 (iface is None or issubclass(iface, ICloseListener))):
971 self.withdrawListener(CloseEvent, listener,
972 _WINDOW_CLOSE_METHOD)
973
974 if (isinstance(listener, IFocusListener) and
975 (iface is None or issubclass(iface, IFocusListener))):
976 self.withdrawListener(FocusEvent.EVENT_ID,
977 FocusEvent, listener)
978
979 if (isinstance(listener, IResizeListener) and
980 (iface is None or issubclass(iface, IResizeListener))):
981 self.withdrawListener(ResizeEvent, listener)
982
983 super(Window, self).removeListener(listener, iface)
984
985
987 if eventType is None:
988 eventType = callback._eventType
989
990 if issubclass(eventType, BlurEvent):
991 self.withdrawCallback(BlurEvent, callback, BlurEvent.EVENT_ID)
992
993 elif issubclass(eventType, CloseEvent):
994 self.withdrawCallback(CloseEvent, callback)
995
996 elif issubclass(eventType, FocusEvent):
997 self.withdrawCallback(FocusEvent, callback, FocusEvent.EVENT_ID)
998
999 elif issubclass(eventType, ResizeEvent):
1000 self.withdrawCallback(ResizeEvent, callback)
1001
1002 else:
1003 super(Window, self).removeCallback(callback, eventType)
1004
1005
1009
1010
1014
1015
1020
1021
1023 """Adds a window inside another window.
1024
1025 Adding windows inside another window creates "subwindows". These
1026 windows should not be added to application directly and are not
1027 accessible directly with any url. Addding windows implicitly sets
1028 their parents.
1029
1030 Only one level of subwindows are supported. Thus you can add windows
1031 inside such windows whose parent is C{None}.
1032
1033 @raise ValueError:
1034 if a window is added inside non-application level window.
1035 """
1036 if window is None:
1037 raise ValueError, 'Argument must not be null'
1038
1039 if window.getApplication() is not None:
1040 raise ValueError, ('Window was already added to application'
1041 ' - it can not be added to another window also.')
1042
1043 elif self.getParent() is not None:
1044 raise ValueError, ('You can only add windows inside '
1045 'application-level windows.')
1046
1047 elif len(window._subwindows) > 0:
1048 raise ValueError, 'Only one level of subwindows are supported.'
1049
1050 self.attachWindow(window)
1051
1052
1054 """Remove the given subwindow from this window.
1055
1056 L{ICloseListener}s are called also when explicitly removing a window
1057 by calling this method.
1058
1059 Returns a boolean indicating if the window was removed or not.
1060
1061 @param window:
1062 Window to be removed.
1063 @return: true if the subwindow was removed, false otherwise
1064 """
1065 if window not in self._subwindows:
1066
1067 return False
1068 else:
1069 self._subwindows.remove(window)
1070
1071 window.setParent(None)
1072 window.fireClose()
1073 self.requestRepaint()
1074
1075 return True
1076
1077
1079 """If there are currently several sub windows visible, calling this
1080 method makes this window topmost.
1081
1082 This method can only be called if this window is a sub window and
1083 connected a top level window. Else an illegal state exception is
1084 thrown. Also if there are modal windows and this window is not modal,
1085 and illegal state exception is thrown.
1086
1087 B{Note, this API works on sub windows only. Browsers can't
1088 reorder OS windows.}
1089 """
1090 parent = self.getParent()
1091 if parent is None:
1092 raise ValueError, ('Window must be attached to parent '
1093 'before calling bringToFront method.')
1094
1095 for w in parent.getChildWindows():
1096 if w.isModal() and not self.isModal():
1097 raise ValueError, ('There are modal windows currently '
1098 'visible, non-modal window cannot be brought to front.')
1099
1100 self._bringToFront = self.getParent().bringToFrontSequence
1101
1102 self.getParent().bringToFrontSequence = \
1103 self.getParent().bringToFrontSequence + 1
1104
1105 self.requestRepaint()
1106
1107
1109 """Get the set of all child windows.
1110
1111 @return: Set of child windows.
1112 """
1113 return set(self._subwindows)
1114
1115
1117 """Sets sub-window modal, so that widgets behind it cannot be
1118 accessed.
1119 B{Note:} affects sub-windows only.
1120
1121 @param modality:
1122 true if modality is to be turned on
1123 """
1124 self._modal = modality
1125 self.center()
1126 self.requestRepaint()
1127
1128
1130 """@return: true if this window is modal."""
1131 return self._modal
1132
1133
1135 """Sets sub-window resizable. B{Note:} affects sub-windows only.
1136
1137 @param resizable:
1138 true if resizability is to be turned on
1139 """
1140 self._resizable = resizable
1141 self.requestRepaint()
1142
1143
1145 """@return: true if window is resizable by the end-user, otherwise
1146 false."""
1147 return self._resizable
1148
1149
1151 """@return: true if a delay is used before recalculating sizes,
1152 false if sizes are recalculated immediately.
1153 """
1154 return self._resizeLazy
1155
1156
1158 """Should resize operations be lazy, i.e. should there be a delay
1159 before layout sizes are recalculated. Speeds up resize operations
1160 in slow UIs with the penalty of slightly decreased usability.
1161
1162 Note, some browser send false resize events for the browser window
1163 and are therefore always lazy.
1164
1165 @param resizeLazy:
1166 true to use a delay before recalculating sizes, false to
1167 calculate immediately.
1168 """
1169 self._resizeLazy = resizeLazy
1170 self.requestRepaint()
1171
1172
1174 """Request to center this window on the screen. B{Note:} affects
1175 sub-windows only.
1176 """
1177 self._centerRequested = True
1178 self.requestRepaint()
1179
1180
1182 """Shows a notification message the window. The position and behavior
1183 of the message depends on the type, which is one of the basic types
1184 defined in L{Notification}, for instance
1185 Notification.TYPE_WARNING_MESSAGE, defaults to "humanized".
1186
1187 Care should be taken to to avoid XSS vulnerabilities as the caption is
1188 rendered as html.
1189
1190 @param args: tuple of the form
1191 - (caption)
1192 1. The message
1193 - (caption, type)
1194 1. The message
1195 2. The message type
1196 - (caption, description)
1197 1. The message
1198 2. The message description
1199 - (caption, description, type)
1200 1. The message
1201 2. The message description
1202 3. The message type
1203 - (notification)
1204 1. The notification message to show
1205 - (caption, description, type, htmlContentAllowed)
1206 1. The message
1207 2. The message description
1208 3. The message type
1209 4. Whether html in the caption and description should be
1210 displayed as html or as plain text
1211 """
1212 args = args
1213 nargs = len(args)
1214 if nargs == 1:
1215 if isinstance(args[0], Notification):
1216 notification, = args
1217 self.addNotification(notification)
1218 else:
1219 caption, = args
1220 self.addNotification( Notification(caption) )
1221 elif nargs == 2:
1222 if isinstance(args[1], int):
1223 caption, typ = args
1224 self.addNotification( Notification(caption, typ) )
1225 else:
1226 caption, description = args
1227 self.addNotification( Notification(caption, description) )
1228 elif nargs == 3:
1229 caption, description, typ = args
1230 self.addNotification( Notification(caption, description, typ) )
1231 elif nargs == 4:
1232 caption, description, typ, htmlContentAllowed = args
1233 n = Notification(caption, description, typ, htmlContentAllowed)
1234 self.addNotification(n)
1235 else:
1236 raise ValueError, 'invalid number of arguments'
1237
1238
1240 if self._notifications is None:
1241 self._notifications = list()
1242
1243 self._notifications.append(notification)
1244 self.requestRepaint()
1245
1246
1248 """This method is used by Component.Focusable objects to request focus
1249 to themselves. Focus renders must be handled at window level (instead
1250 of IFocusable) due we want the last focused component to be
1251 focused in client too. Not the one that is rendered last (the case
1252 we'd get if implemented in Focusable only).
1253
1254 To focus component from Muntjac application, use IFocusable.focus().
1255 See L{IFocusable}.
1256
1257 @param focusable:
1258 to be focused on next paint
1259 """
1260 if self.getParent() is not None:
1261
1262 self.getParent().setFocusedComponent(focusable)
1263 else:
1264 self._pendingFocus = focusable
1265 self.requestRepaint()
1266
1267
1269 """Executes JavaScript in this window.
1270
1271 This method allows one to inject javascript from the server to client.
1272 A client implementation is not required to implement this
1273 functionality, but currently all web-based clients do implement this.
1274
1275 Executing javascript this way often leads to cross-browser
1276 compatibility issues and regressions that are hard to resolve. Use of
1277 this method should be avoided and instead it is recommended to create
1278 new widgets with GWT.
1279
1280 @param script:
1281 JavaScript snippet that will be executed.
1282 """
1283 if self.getParent() is not None:
1284 raise NotImplementedError, ('Only application level '
1285 'windows can execute javascript.')
1286
1287 if self._jsExecQueue is None:
1288 self._jsExecQueue = list()
1289
1290 self._jsExecQueue.append(script)
1291
1292 self.requestRepaint()
1293
1294
1296 """Returns the closable status of the sub window. If a sub window is
1297 closable it typically shows an X in the upper right corner. Clicking
1298 on the X sends a close event to the server. Setting closable to false
1299 will remove the X from the sub window and prevent the user from
1300 closing the window.
1301
1302 Note! For historical reasons readonly controls the closability of the
1303 sub window and therefore readonly and closable affect each other.
1304 Setting readonly to true will set closable to false and vice versa.
1305
1306 Closable only applies to sub windows, not to browser level windows.
1307
1308 @return: true if the sub window can be closed by the user.
1309 """
1310 return not self.isReadOnly()
1311
1312
1314 """Sets the closable status for the sub window. If a sub window is
1315 closable it typically shows an X in the upper right corner. Clicking
1316 on the X sends a close event to the server. Setting closable to false
1317 will remove the X from the sub window and prevent the user from
1318 closing the window.
1319
1320 Note! For historical reasons readonly controls the closability of the
1321 sub window and therefore readonly and closable affect each other.
1322 Setting readonly to true will set closable to false and vice versa.
1323
1324 Closable only applies to sub windows, not to browser level windows.
1325
1326 @param closable:
1327 determines if the sub window can be closed by the user.
1328 """
1329 self.setReadOnly(not closable)
1330
1331
1333 """Indicates whether a sub window can be dragged or not. By default
1334 a sub window is draggable.
1335
1336 Draggable only applies to sub windows, not to browser level windows.
1337 """
1338 return self._draggable
1339
1340
1342 """Enables or disables that a sub window can be dragged (moved) by
1343 the user. By default a sub window is draggable.
1344
1345 Draggable only applies to sub windows, not to browser level windows.
1346
1347 @param draggable:
1348 true if the sub window can be dragged by the user
1349 """
1350 self._draggable = draggable
1351 self.requestRepaint()
1352
1353
1355 """Makes is possible to close the window by pressing the given
1356 L{KeyCode} and (optional) L{ModifierKey}s.
1357
1358 Note that this shortcut only reacts while the window has focus,
1359 closing itself - if you want to close a subwindow from a parent
1360 window, use L{addAction} of the parent window instead.
1361
1362 @param keyCode:
1363 the keycode for invoking the shortcut
1364 @param modifiers:
1365 the (optional) modifiers for invoking the shortcut,
1366 null for none
1367 """
1368 if self.closeShortcut is not None:
1369 self.removeAction(self.closeShortcut)
1370
1371 self.closeShortcut = CloseShortcut(self, keyCode, modifiers)
1372
1373 self.addAction(self.closeShortcut)
1374
1375
1377 """Removes the keyboard shortcut previously set with
1378 L{setCloseShortcut}.
1379 """
1380 if self.closeShortcut is not None:
1381 self.removeAction(self.closeShortcut)
1382 self.closeShortcut = None
1383
1384
1386 """If the window is a sub-window focusing will cause the sub-window
1387 to be brought on top of other sub-windows on gain keyboard focus.
1388 """
1389 if self.getParent() is not None:
1390
1391
1392
1393 self._bringToFront()
1394 else:
1395 super(Window, self).focus()
1396
1397
1399 """Private class for storing properties related to opening resources."""
1400
1401 - def __init__(self, resource, name, width, height, border):
1402 """Creates a new open resource.
1403
1404 @param resource:
1405 The resource to open
1406 @param name:
1407 The name of the target window
1408 @param width:
1409 The width of the target window
1410 @param height:
1411 The height of the target window
1412 @param border:
1413 The border style of the target window
1414 """
1415 self._resource = resource
1416
1417
1418 self._name = name
1419
1420
1421 self._width = width
1422
1423
1424 self._height = height
1425
1426
1427 self._border = border
1428
1429
1430 - def paintContent(self, target):
1431 """Paints the open request. Should be painted inside the window.
1432
1433 @param target:
1434 the paint target
1435 @raise PaintException:
1436 if the paint operation fails
1437 """
1438 target.startTag('open')
1439 target.addAttribute('src', self._resource)
1440 if self._name is not None and len(self._name) > 0:
1441 target.addAttribute('name', self._name)
1442
1443 if self._width >= 0:
1444 target.addAttribute('width', self._width)
1445
1446 if self._height >= 0:
1447 target.addAttribute('height', self._height)
1448
1449 if self._border == Window.BORDER_MINIMAL:
1450 target.addAttribute('border', 'minimal')
1451 elif self._border == Window.BORDER_NONE:
1452 target.addAttribute('border', 'none')
1453
1454 target.endTag('open')
1455
1456
1458
1461
1462
1464 """Gets the Window.
1465
1466 @return: the window.
1467 """
1468 return self.getSource()
1469
1470
1472 """Resize events are fired whenever the client-side fires a resize-event
1473 (e.g. the browser window is resized). The frequency may vary across
1474 browsers.
1475 """
1476
1479
1480
1482 """Get the window form which this event originated
1483
1484 @return: the window
1485 """
1486 return self.getSource()
1487
1488
1490 """A notification message, used to display temporary messages to the user -
1491 for example "Document saved", or "Save failed".
1492
1493 The notification message can consist of several parts: caption,
1494 description and icon. It is usually used with only caption - one should
1495 be wary of filling the notification with too much information.
1496
1497 The notification message tries to be as unobtrusive as possible, while
1498 still drawing needed attention. There are several basic types of messages
1499 that can be used in different situations:
1500
1501 - TYPE_HUMANIZED_MESSAGE fades away quickly as soon as the user uses
1502 the mouse or types something. It can be used to show fairly unimportant
1503 messages, such as feedback that an operation succeeded ("Document
1504 Saved") - the kind of messages the user ignores once the application
1505 is familiar.
1506
1507 - TYPE_WARNING_MESSAGE is shown for a short while after the user uses
1508 the mouse or types something. It's default style is also more
1509 noticeable than the humanized message. It can be used for messages that
1510 do not contain a lot of important information, but should be noticed by
1511 the user. Despite the name, it does not have to be a warning, but can
1512 be used instead of the humanized message whenever you want to make the
1513 message a little more noticeable.
1514
1515 - TYPE_ERROR_MESSAGE requires to user to click it before disappearing,
1516 and can be used for critical messages.
1517
1518 - TYPE_TRAY_NOTIFICATION is shown for a while in the lower left corner
1519 of the window, and can be used for "convenience notifications" that do
1520 not have to be noticed immediately, and should not interfere with the
1521 current task - for instance to show "You have a new message in your
1522 inbox" while the user is working in some other area of the application.
1523
1524 In addition to the basic pre-configured types, a Notification can also be
1525 configured to show up in a custom position, for a specified time (or
1526 until clicked), and with a custom stylename. An icon can also be added.
1527 """
1528
1529 TYPE_HUMANIZED_MESSAGE = 1
1530 TYPE_WARNING_MESSAGE = 2
1531 TYPE_ERROR_MESSAGE = 3
1532 TYPE_TRAY_NOTIFICATION = 4
1533
1534 POSITION_CENTERED = 1
1535 POSITION_CENTERED_TOP = 2
1536 POSITION_CENTERED_BOTTOM = 3
1537 POSITION_TOP_LEFT = 4
1538 POSITION_TOP_RIGHT = 5
1539 POSITION_BOTTOM_LEFT = 6
1540 POSITION_BOTTOM_RIGHT = 7
1541
1542 DELAY_FOREVER = -1
1543 DELAY_NONE = 0
1544
1546 """Creates a notification message.
1547
1548 Care should be taken to to avoid XSS vulnerabilities as the caption
1549 and description are by default rendered as html.
1550
1551 @param args: tuple of the form
1552 - (caption)
1553 1. The message to show
1554 - (caption, type)
1555 1. The message to show
1556 2. The type of message
1557 - (caption, description)
1558 1. The message caption
1559 2. The message description
1560 - (caption, description, type)
1561 1. The message caption
1562 2. The message description
1563 3. The type of message
1564 - (caption, description, type, htmlContentAllowed)
1565 1. The message caption
1566 2. The message description
1567 3. The type of message
1568 4. Whether html in the caption and description should be
1569 displayed as html or as plain text
1570 """
1571 self._caption = None
1572 self._description = None
1573 self._icon = None
1574 self._position = self.POSITION_CENTERED
1575 self._delayMsec = 0
1576 self._styleName = None
1577 self._htmlContentAllowed = True
1578
1579 nargs = len(args)
1580 if nargs == 1:
1581 caption, = args
1582 Notification.__init__(self, caption, None,
1583 self.TYPE_HUMANIZED_MESSAGE)
1584 elif nargs == 2:
1585 if isinstance(args[1], int):
1586 caption, typ = args
1587 Notification.__init__(self, caption, None, typ)
1588 else:
1589 caption, description = args
1590 Notification.__init__(self, caption, description,
1591 self.TYPE_HUMANIZED_MESSAGE)
1592 elif nargs == 3:
1593 caption, description, typ = args
1594 self._caption = caption
1595 self._description = description
1596 self.setType(typ)
1597 elif nargs == 4:
1598 caption, description, typ, htmlContentAllowed = args
1599 self._caption = caption
1600 self._description = description
1601 self._htmlContentAllowed = htmlContentAllowed
1602 self.setType(typ)
1603 else:
1604 raise ValueError, 'invalid number of arguments'
1605
1606
1622
1623
1625 """Gets the caption part of the notification message.
1626
1627 @return: The message caption
1628 """
1629 return self._caption
1630
1631
1633 """Sets the caption part of the notification message
1634
1635 @param caption:
1636 The message caption
1637 """
1638 self._caption = caption
1639
1640
1642 """@deprecated: Use L{getDescription} instead.
1643 """
1644 return self._description
1645
1646
1648 """@deprecated: Use L{setDescription} instead.
1649 """
1650 self._description = description
1651
1652
1654 """Gets the description part of the notification message.
1655
1656 @return: The message description.
1657 """
1658 return self._description
1659
1660
1662 """Sets the description part of the notification message.
1663 """
1664 self._description = description
1665
1666
1668 """Gets the position of the notification message.
1669
1670 @return: The position
1671 """
1672 return self._position
1673
1674
1676 """Sets the position of the notification message.
1677
1678 @param position:
1679 The desired notification position
1680 """
1681 self._position = position
1682
1683
1685 """Gets the icon part of the notification message.
1686
1687 @return: The message icon
1688 """
1689 return self._icon
1690
1691
1693 """Sets the icon part of the notification message.
1694
1695 @param icon:
1696 The desired message icon
1697 """
1698 self._icon = icon
1699
1700
1702 """Gets the delay before the notification disappears.
1703
1704 @return: the delay in msec, -1 indicates the message has to be
1705 clicked.
1706 """
1707 return self._delayMsec
1708
1709
1711 """Sets the delay before the notification disappears.
1712
1713 @param delayMsec:
1714 the desired delay in msec, -1 to require the user to click
1715 the message
1716 """
1717 self._delayMsec = delayMsec
1718
1719
1721 """Sets the style name for the notification message.
1722
1723 @param styleName:
1724 The desired style name.
1725 """
1726 self._styleName = styleName
1727
1728
1730 """Gets the style name for the notification message.
1731 """
1732 return self._styleName
1733
1734
1735 - def setHtmlContentAllowed(self, htmlContentAllowed):
1736 """Sets whether html is allowed in the caption and description. If set
1737 to true, the texts are passed to the browser as html and the
1738 developer is responsible for ensuring no harmful html is used. If set
1739 to false, the texts are passed to the browser as plain text.
1740
1741 @param htmlContentAllowed:
1742 true if the texts are used as html, false if used as plain
1743 text
1744 """
1745 self._htmlContentAllowed = htmlContentAllowed
1746
1747
1749 """Checks whether caption and description are interpreted as html or
1750 plain text.
1751
1752 @return: true if the texts are used as html, false if used as plain
1753 text
1754 @see: L{setHtmlContentAllowed}
1755 """
1756 return self._htmlContentAllowed
1757
1758
1760 """A L{ShortcutListener} specifically made to define a keyboard
1761 shortcut that closes the window::
1762
1763 # within the window using helper
1764 subWindow.setCloseShortcut(KeyCode.ESCAPE, NOne)
1765
1766 # or globally
1767 getWindow().addAction(CloseShortcut(subWindow, KeyCode.ESCAPE))
1768 """
1769
1771 """Creates a keyboard shortcut for closing the given window using
1772 the shorthand notation defined in L{ShortcutAction} or the
1773 given L{KeyCode} and L{ModifierKey}s.
1774
1775 @param args: tuple of the form
1776 - (window, shorthandCaption)
1777 1. to be closed when the shortcut is invoked
1778 2. the caption with shortcut keycode and modifiers indicated
1779 - (window, keyCode, modifiers)
1780 1. to be closed when the shortcut is invoked
1781 2. KeyCode to react to
1782 3. optional modifiers for shortcut
1783 - (window, keyCode)
1784 1. to be closed when the shortcut is invoked
1785 2. KeyCode to react to
1786 """
1787 self.window = None
1788
1789 nargs = len(args)
1790 if nargs == 2:
1791 if isinstance(args[1], int):
1792 window, keyCode = args
1793 CloseShortcut.__init__(self, window, keyCode, None)
1794 else:
1795 window, shorthandCaption = args
1796 super(CloseShortcut, self).__init__(shorthandCaption)
1797 self.window = window
1798 elif nargs == 3:
1799 window, keyCode = args[:2]
1800 modifiers = args[2:]
1801 super(CloseShortcut, self).__init__(None, keyCode, modifiers)
1802 self.window = window
1803 else:
1804 raise ValueError, 'invalid number of arguments'
1805
1806
1809