Mercurial > libervia-web
comparison src/browser/sat_browser/richtext.py @ 679:a90cc8fc9605
merged branch frontends_multi_profiles
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 18 Mar 2015 16:15:18 +0100 |
parents | 6d3142b782c3 |
children | 9877607c719a |
comparison
equal
deleted
inserted
replaced
590:1bffc4c244c3 | 679:a90cc8fc9605 |
---|---|
32 from pyjamas.ui.KeyboardListener import KeyboardHandler | 32 from pyjamas.ui.KeyboardListener import KeyboardHandler |
33 from __pyjamas__ import doc | 33 from __pyjamas__ import doc |
34 | 34 |
35 from constants import Const as C | 35 from constants import Const as C |
36 import dialog | 36 import dialog |
37 import base_panels | 37 import base_panel |
38 import editor_widget | |
38 import list_manager | 39 import list_manager |
39 import html_tools | 40 import html_tools |
40 import panels | 41 import blog |
41 | 42 import chat |
42 | 43 |
43 class RichTextEditor(base_panels.BaseTextEditor, FlexTable): | 44 |
45 class RichTextEditor(editor_widget.BaseTextEditor, FlexTable): | |
44 """Panel for the rich text editor.""" | 46 """Panel for the rich text editor.""" |
45 | 47 |
46 def __init__(self, host, content=None, modifiedCb=None, afterEditCb=None, options=None, style=None): | 48 def __init__(self, host, content=None, modifiedCb=None, afterEditCb=None, options=None, style=None): |
47 """ | 49 """ |
48 @param host: the SatWebFrontend instance | 50 @param host: the SatWebFrontend instance |
49 @param content: dict with at least a 'text' key | 51 @param content: dict with at least a 'text' key |
50 @param modifiedCb: method to be called when the text has been modified | 52 @param modifiedCb: method to be called when the text has been modified |
51 @param afterEditCb: method to be called when the edition is done | 53 @param afterEditCb: method to be called when the edition is done |
52 @param options: list of UI options (see self.readOptions) | 54 @param options: list of UI options (see self.readOptions) |
53 """ | 55 """ |
56 FlexTable.__init__(self) # FIXME | |
54 self.host = host | 57 self.host = host |
55 self._debug = False # TODO: don't forget to set it False before commit | 58 self._debug = False # TODO: don't forget to set it False before commit |
56 self.wysiwyg = False | 59 self.wysiwyg = False |
57 self.__readOptions(options) | 60 self.__readOptions(options) |
58 self.style = {'main': 'richTextEditor', | 61 self.style = {'main': 'richTextEditor', |
60 'toolbar': 'richTextToolbar', | 63 'toolbar': 'richTextToolbar', |
61 'textarea': 'richTextArea'} | 64 'textarea': 'richTextArea'} |
62 if isinstance(style, dict): | 65 if isinstance(style, dict): |
63 self.style.update(style) | 66 self.style.update(style) |
64 self._prepareUI() | 67 self._prepareUI() |
65 base_panels.BaseTextEditor.__init__(self, content, None, modifiedCb, afterEditCb) | 68 editor_widget.BaseTextEditor.__init__(self, content, None, modifiedCb, afterEditCb) |
66 | 69 |
67 def __readOptions(self, options): | 70 def __readOptions(self, options): |
68 """Set the internal flags according to the given options.""" | 71 """Set the internal flags according to the given options.""" |
69 if options is None: | 72 if options is None: |
70 options = [] | 73 options = [] |
82 self.content_offset = self.toolbar_offset + (len(composition.RICH_SYNTAXES) if self._debug else 1) | 85 self.content_offset = self.toolbar_offset + (len(composition.RICH_SYNTAXES) if self._debug else 1) |
83 self.command_offset = self.content_offset + 1 | 86 self.command_offset = self.content_offset + 1 |
84 else: | 87 else: |
85 self.title_offset = self.toolbar_offset = self.content_offset = y_offset | 88 self.title_offset = self.toolbar_offset = self.content_offset = y_offset |
86 self.command_offset = self.content_offset + 1 | 89 self.command_offset = self.content_offset + 1 |
87 FlexTable.__init__(self, self.command_offset + (0 if self.no_command else 1), 2) | 90 # FlexTable.__init__(self, rowspan=self.command_offset + (0 if self.no_command else 1), colspan=2) # FIXME |
88 self.addStyleName(self.style['main']) | 91 self.addStyleName(self.style['main']) |
89 | 92 |
90 def addEditListener(self, listener): | 93 def addEditListener(self, listener): |
91 """Add a method to be called whenever the text is edited. | 94 """Add a method to be called whenever the text is edited. |
92 @param listener: method taking two arguments: sender, keycode""" | 95 @param listener: method taking two arguments: sender, keycode""" |
93 base_panels.BaseTextEditor.addEditListener(self, listener) | 96 editor_widget.BaseTextEditor.addEditListener(self, listener) |
94 if hasattr(self, 'display'): | 97 if hasattr(self, 'display'): |
95 self.display.addEditListener(listener) | 98 self.display.addEditListener(listener) |
96 | 99 |
97 def refresh(self, edit=None): | 100 def refresh(self, edit=None): |
98 """Refresh the UI for edition/display mode | 101 """Refresh the UI for edition/display mode |
105 getattr(self, widget).setVisible(edit) | 108 getattr(self, widget).setVisible(edit) |
106 | 109 |
107 if hasattr(self, 'toolbar'): | 110 if hasattr(self, 'toolbar'): |
108 self.toolbar.setVisible(False) | 111 self.toolbar.setVisible(False) |
109 if not hasattr(self, 'display'): | 112 if not hasattr(self, 'display'): |
110 self.display = base_panels.HTMLTextEditor(options={'enhance_display': False, 'listen_keyboard': False}) # for display mode | 113 self.display = editor_widget.HTMLTextEditor(options={'enhance_display': False, 'listen_keyboard': False}) # for display mode |
111 for listener in self.edit_listeners: | 114 for listener in self.edit_listeners: |
112 self.display.addEditListener(listener) | 115 self.display.addEditListener(listener) |
113 if not self.read_only and not hasattr(self, 'textarea'): | 116 if not self.read_only and not hasattr(self, 'textarea'): |
114 self.textarea = EditTextArea(self) # for edition mode | 117 self.textarea = EditTextArea(self) # for edition mode |
115 self.textarea.addStyleName(self.style['textarea']) | 118 self.textarea.addStyleName(self.style['textarea']) |
122 self.setWidget(self.content_offset, 0, self.display) | 125 self.setWidget(self.content_offset, 0, self.display) |
123 if not edit: | 126 if not edit: |
124 return | 127 return |
125 | 128 |
126 if not self.no_title and not hasattr(self, 'title_panel'): | 129 if not self.no_title and not hasattr(self, 'title_panel'): |
127 self.title_panel = base_panels.TitlePanel() | 130 self.title_panel = base_panel.TitlePanel() |
128 self.title_panel.addStyleName(self.style['title']) | 131 self.title_panel.addStyleName(self.style['title']) |
129 self.getFlexCellFormatter().setColSpan(self.title_offset, 0, 2) | 132 self.getFlexCellFormatter().setColSpan(self.title_offset, 0, 2) |
130 self.setWidget(self.title_offset, 0, self.title_panel) | 133 self.setWidget(self.title_offset, 0, self.title_panel) |
131 | 134 |
132 if not self.no_command and not hasattr(self, 'command'): | 135 if not self.no_command and not hasattr(self, 'command'): |
172 @param init: set to True to re-init without switching the widgets.""" | 175 @param init: set to True to re-init without switching the widgets.""" |
173 def setWysiwyg(): | 176 def setWysiwyg(): |
174 self.wysiwyg = wysiwyg | 177 self.wysiwyg = wysiwyg |
175 try: | 178 try: |
176 self.wysiwyg_button.setChecked(wysiwyg) | 179 self.wysiwyg_button.setChecked(wysiwyg) |
177 except TypeError: | 180 except (AttributeError, TypeError): |
178 pass | 181 pass |
179 try: | 182 try: |
180 if wysiwyg: | 183 if wysiwyg: |
181 self.syntax_label.addStyleName('transparent') | 184 self.syntax_label.addStyleName('transparent') |
182 else: | 185 else: |
183 self.syntax_label.removeStyleName('transparent') | 186 self.syntax_label.removeStyleName('transparent') |
184 except TypeError: | 187 except (AttributeError, TypeError): |
185 pass | 188 pass |
186 if not wysiwyg: | 189 if not wysiwyg: |
187 self.display.removeStyleName('richTextWysiwyg') | 190 self.display.removeStyleName('richTextWysiwyg') |
188 | 191 |
189 if init: | 192 if init: |
282 confirmation. When edit is False and abort is True, abortion is actually done. | 285 confirmation. When edit is False and abort is True, abortion is actually done. |
283 @param sync: set to True to cancel the edition after the content has been saved somewhere else | 286 @param sync: set to True to cancel the edition after the content has been saved somewhere else |
284 """ | 287 """ |
285 if not (edit and abort): | 288 if not (edit and abort): |
286 self.refresh(edit) # not when we are asking for a confirmation | 289 self.refresh(edit) # not when we are asking for a confirmation |
287 base_panels.BaseTextEditor.edit(self, edit, abort, sync) # after the UI has been refreshed | 290 editor_widget.BaseTextEditor.edit(self, edit, abort, sync) # after the UI has been refreshed |
288 if (edit and abort): | 291 if (edit and abort): |
289 return # self.abortEdition is called by base_panels.BaseTextEditor.edit | 292 return # self.abortEdition is called by editor_widget.BaseTextEditor.edit |
290 self.setWysiwyg(False, init=True) # after base_panels.BaseTextEditor (it affects self.getContent) | 293 self.setWysiwyg(False, init=True) # after editor_widget.BaseTextEditor (it affects self.getContent) |
291 if sync: | 294 if sync: |
292 return | 295 return |
293 # the following must NOT be done at each UI refresh! | 296 # the following must NOT be done at each UI refresh! |
294 content = self._original_content | 297 content = self._original_content |
295 if edit: | 298 if edit: |
315 # set the display text in XHTML only during init because a new MicroblogEntry instance is created after each modification | 318 # set the display text in XHTML only during init because a new MicroblogEntry instance is created after each modification |
316 self.setDisplayContent() | 319 self.setDisplayContent() |
317 self.display.edit(False) | 320 self.display.edit(False) |
318 | 321 |
319 def setDisplayContent(self): | 322 def setDisplayContent(self): |
320 """Set the content of the base_panels.HTMLTextEditor which is used for display/wysiwyg""" | 323 """Set the content of the editor_widget.HTMLTextEditor which is used for display/wysiwyg""" |
321 content = self._original_content | 324 content = self._original_content |
322 text = content['text'] | 325 text = content['text'] |
323 if 'title' in content and content['title']: | 326 if 'title' in content and content['title']: |
324 text = '<h1>%s</h1>%s' % (html_tools.html_sanitize(content['title']), content['text']) | 327 text = '<h1>%s</h1>%s' % (html_tools.html_sanitize(content['title']), content['text']) |
325 self.display.setContent({'text': text}) | 328 self.display.setContent({'text': text}) |
438 setText = lambda: self.host.uni_box.setText("" if emptyText else self.getContent()['text']) | 441 setText = lambda: self.host.uni_box.setText("" if emptyText else self.getContent()['text']) |
439 if not hasattr(self, 'recipient'): | 442 if not hasattr(self, 'recipient'): |
440 setText() | 443 setText() |
441 return True | 444 return True |
442 if recipients is None: | 445 if recipients is None: |
443 recipients = self.recipient.getContacts() | 446 recipients = self.recipient.getItemsByKey() |
444 target = "" | 447 target = "" |
445 # we could eventually allow more in the future | 448 # we could eventually allow more in the future |
446 allowed = 1 | 449 allowed = 1 |
447 for key in recipients: | 450 for key in recipients: |
448 count = len(recipients[key]) | 451 count = len(recipients[key]) |
455 target = recipients[key][0] | 458 target = recipients[key][0] |
456 setText() | 459 setText() |
457 if target == "": | 460 if target == "": |
458 return True | 461 return True |
459 if target.startswith("@"): | 462 if target.startswith("@"): |
460 _class = panels.MicroblogPanel | 463 _class = blog.MicroblogPanel |
461 target = None if target == "@@" else target[1:] | 464 target = None if target == "@@" else target[1:] |
462 else: | 465 else: |
463 _class = panels.ChatPanel | 466 _class = chat.Chat |
464 self.host.getOrCreateLiberviaWidget(_class, {'item': target}) | 467 self.host.displayWidget(_class, target) |
465 return True | 468 return True |
466 | 469 |
467 def syncFromEditor(self, content): | 470 def syncFromEditor(self, content): |
468 """Synchronize to unibox and close the dialog afterward. Display | 471 """Synchronize to unibox and close the dialog afterward. Display |
469 a message and leave the dialog open if the sync was not possible.""" | 472 a message and leave the dialog open if the sync was not possible.""" |
483 return | 486 return |
484 RichTextEditor.edit(self, edit, abort, sync) | 487 RichTextEditor.edit(self, edit, abort, sync) |
485 | 488 |
486 def __sendMessage(self): | 489 def __sendMessage(self): |
487 """Send the message.""" | 490 """Send the message.""" |
488 recipients = self.recipient.getContacts() | 491 recipients = self.recipient.getItemsByKey() |
489 targets = [] | 492 targets = [] |
490 for addr in recipients: | 493 for addr in recipients: |
491 for recipient in recipients[addr]: | 494 for recipient in recipients[addr]: |
492 if recipient.startswith("@"): | 495 if recipient.startswith("@"): |
493 targets.append(("PUBLIC", None, addr) if recipient == "@@" else ("GROUP", recipient[1:], addr)) | 496 targets.append(("PUBLIC", None, addr) if recipient == "@@" else ("GROUP", recipient[1:], addr)) |
514 # TODO: be sure we also display empty groups and disconnected contacts + their groups | 517 # TODO: be sure we also display empty groups and disconnected contacts + their groups |
515 # store the full list of potential recipients (groups and contacts) | 518 # store the full list of potential recipients (groups and contacts) |
516 list_ = [] | 519 list_ = [] |
517 list_.append("@@") | 520 list_.append("@@") |
518 list_.extend("@%s" % group for group in parent.host.contact_panel.getGroups()) | 521 list_.extend("@%s" % group for group in parent.host.contact_panel.getGroups()) |
519 list_.extend(contact for contact in parent.host.contact_panel.getContacts()) | 522 list_.extend(contact for contact in parent.host.contact_list.roster_entities) |
520 list_manager.ListManager.__init__(self, parent, composition.RECIPIENT_TYPES, list_, {'y': y_offset}) | 523 list_manager.ListManager.__init__(self, parent, composition.RECIPIENT_TYPES, list_, {'y': y_offset}) |
521 | 524 |
522 self.registerPopupMenuPanel(entries=composition.RECIPIENT_TYPES, | 525 self.registerPopupMenuPanel(entries=composition.RECIPIENT_TYPES, |
523 hide=lambda sender, key: self.__children[key]["panel"].isVisible(), | 526 hide=lambda sender, key: self.__children[key]["panel"].isVisible(), |
524 callback=self.setContactPanelVisible) | 527 callback=self.setContactPanelVisible) |