class SearchDialog(Dialog): READY = 0 SEARCHING = 1 CANCELLED = 2 def __init__(self, widget, notebook, page, navigation): Dialog.__init__( self, widget, _('Search'), # T: Dialog title buttons=Gtk.ButtonsType.CLOSE, help='Help:Searching', defaultwindowsize=(400, 300)) self.page = page hbox = Gtk.HBox(spacing=5) self.vbox.pack_start(hbox, False, True, 0) hbox.pack_start(Gtk.Label(_('Search') + ': '), False, True, 0) # T: input label self.query_entry = InputEntry() hbox.add(self.query_entry) self.search_button = Gtk.Button.new_with_mnemonic( _('_Find')) # T: Button label hbox.pack_start(self.search_button, False, True, 0) self.spinner = Gtk.Spinner() hbox.pack_start(self.spinner, False, True, 0) self.cancel_button = Gtk.Button.new_with_mnemonic( _('_Cancel')) # T: Button label hbox.pack_start(self.cancel_button, False, True, 0) self._set_state(self.READY) help_text = _('For advanced search you can use operators like\n' 'AND, OR and NOT. See the help page for more details.' ) # T: help text for the search dialog self.query_entry.set_tooltip_text(help_text) self.namespacecheckbox = Gtk.CheckButton.new_with_mnemonic( _('Limit search to the current page and sub-pages')) # T: checkbox option in search dialog if page is not None: self.vbox.pack_start(self.namespacecheckbox, False, True, 0) # TODO advanced query editor # TODO checkbox _('Match c_ase') # TODO checkbox _('Whole _word') self.results_treeview = SearchResultsTreeView(notebook, navigation) self.vbox.pack_start(ScrolledWindow(self.results_treeview), True, True, 0) self.search_button.connect_object('clicked', self.__class__._search, self) self.cancel_button.connect_object('clicked', self.__class__._cancel, self) self.query_entry.connect_object('activate', self.__class__._search, self) def search(self, query): '''Trigger a search to be performed. Because search can take a long time to execute it is best to call this method after the dialog is shown. @param query: the query as string ''' self.query_entry.set_text(query) self._search() def _search(self): string = self.query_entry.get_text() if self.namespacecheckbox.get_active(): assert self.page is not None string = 'Section: "%s" ' % self.page.name + string #~ print('!! QUERY: ' + string) self._set_state(self.SEARCHING) try: self.results_treeview.search(string) except Exception as error: ErrorDialog(self, error).run() if not self.results_treeview.cancelled: self._set_state(self.READY) else: self._set_state(self.CANCELLED) def _cancel(self): self.results_treeview.cancelled = True def _set_state(self, state): # TODO set cursor for treeview part # TODO set label or something ? def hide(button): button.hide() button.set_no_show_all(True) def show(button): button.set_no_show_all(False) button.show_all() if state in (self.READY, self.CANCELLED): self.query_entry.set_sensitive(True) hide(self.cancel_button) if self.spinner: self.spinner.stop() hide(self.spinner) show(self.search_button) elif state == self.SEARCHING: self.query_entry.set_sensitive(False) hide(self.search_button) if self.spinner: show(self.spinner) self.spinner.start() show(self.cancel_button) else: assert False, 'BUG: invalid state'
class ServerWindow(Gtk.Window): def __init__(self, notebookinfo=None, port=8080, public=True, **opts): '''Constructor @param notebookinfo: the notebook location @param port: the http port to serve on @param public: allow connections to the server from other computers - if C{False} can only connect from localhost @param opts: options for L{WWWInterface.__init__()} ''' GObject.GObject.__init__(self) self.set_title('Zim - ' + _('Web Server')) # T: Window title self.set_border_width(10) self.interface_opts = opts self.httpd = None self._source_id = None # Widgets self.status_label = Gtk.Label() self.status_label.set_markup('<i>' + _('Server not started') + '</i>') # T: Status in web server gui self.start_button = IconButton('gtk-media-play') self.start_button.connect('clicked', lambda o: self.start()) self.stop_button = IconButton('gtk-media-stop') self.stop_button.connect('clicked', lambda o: self.stop()) self.stop_button.set_sensitive(False) self.link_button = Gtk.LinkButton('') self.link_button.set_sensitive(False) self.notebookcombobox = NotebookComboBox(current=notebookinfo) self.open_button = IconButton('gtk-index') self.open_button.connect('clicked', lambda *a: NotebookDialog(self).run()) self.portentry = Gtk.SpinButton() self.portentry.set_numeric(True) self.portentry.set_range(80, 10000) self.portentry.set_increments(1, 80) self.portentry.set_value(port) self.public_checkbox = Gtk.CheckButton.new_with_mnemonic( _('Allow public access')) # T: Checkbox in web server gui self.public_checkbox.set_active(public) self.templatecombobox = Gtk.ComboBoxText.new() template_names = [name for name, _ in list_templates('html')] for name in template_names: self.templatecombobox.append_text(name) self.templatecombobox.set_active(template_names.index('Default')) self.auth_checkbox = Gtk.CheckButton.new_with_mnemonic( _('Require authentication')) # T: checkbox in dialog for starting webserver self.username_input = InputEntry() self.password_input = InputEntry() self.password_input.set_visibility(False) # Build the interface vbox = Gtk.VBox() self.add(vbox) hbox = Gtk.HBox(spacing=12) hbox.pack_start(self.start_button, False, True, 0) hbox.pack_start(self.stop_button, False, True, 0) hbox.pack_start(self.status_label, False, True, 0) vbox.pack_start(hbox, False, False, 0) table = input_table_factory(( (_('Notebook'), self.notebookcombobox, self.open_button), # T: Field in web server gui (_('Port'), self.portentry), # T: Field in web server gui for HTTP port (e.g. port 80) (_('Template'), self.templatecombobox), # T: Field in web server gui for webpage template self.public_checkbox, self.auth_checkbox, (_('Username'), self.username_input), # T: Field in web server gui (_('Password'), self.password_input) # T: Field in web server gui )) vbox.pack_start(table, False, False, 0) if self.link_button: hbox = Gtk.HBox() hbox.pack_end(self.link_button, False, True, 0) vbox.pack_start(hbox, False, False, 0) def open_notebook(self, notebook): '''Sets the notebook in the combobox This method is called by the NotebookDialog when a notebook is opened. ''' self.notebookcombobox.set_notebook(notebook) def start(self): # Start server try: uri = self.notebookcombobox.get_notebook() if uri: notebook, x = build_notebook(NotebookInfo(uri)) if not notebook: return else: return if not notebook.index.is_uptodate: for info in notebook.index.update_iter(): #logger.info('Indexing %s', info) pass # TODO meaningful info for above message port = int(self.portentry.get_value()) public = self.public_checkbox.get_active() self.interface_opts[ 'template'] = self.templatecombobox.get_active_text() require_auth = self.auth_checkbox.get_active() auth_creds = None if require_auth: auth_creds = (self.username_input.get_text(), self.password_input.get_text()) self.httpd = make_server(notebook, port, public, auth_creds=auth_creds, **self.interface_opts) if sys.platform == 'win32': # GObject io watch conflicts with socket use on windows.. # idle handler uses a bit to much CPU for my taste, # timeout every 0.5 sec is better self.httpd.timeout = 0.1 # 100 ms self._source_id = GObject.timeout_add(500, self.do_serve_on_poll) else: self.httpd.timeout = 3 # if no response after 3 sec, drop it self._source_id = GObject.io_add_watch( self.httpd.fileno(), GObject.IO_IN | GObject.IO_OUT | GObject.IO_ERR | GObject.IO_HUP | GObject.IO_PRI, # any event.. self.do_serve_on_io) logger.info("Serving HTTP on %s port %i...", self.httpd.server_name, self.httpd.server_port) except Exception as error: ErrorDialog(self, error).run() return # Update UI self.notebookcombobox.set_sensitive(False) self.portentry.set_sensitive(False) self.public_checkbox.set_sensitive(False) self.templatecombobox.set_sensitive(False) self.open_button.set_sensitive(False) self.start_button.set_sensitive(False) self.stop_button.set_sensitive(True) self.auth_checkbox.set_sensitive(False) self.username_input.set_sensitive(False) self.password_input.set_sensitive(False) self.status_label.set_markup('<i>' + _('Server started') + '</i>') # T: Status in web server gui #if self.public_checkbox.get_active(): # url = 'http://%s:%i' % (self.httpd.server_name, self.httpd.server_port) #else: # url = 'http://localhost:%i' % self.httpd.server_port url = 'http://localhost:%i' % self.httpd.server_port if self.link_button: self.link_button.set_uri(url) self.link_button.set_label(url) self.link_button.set_sensitive(True) def do_serve_on_io(self, fd, event): try: if event & GObject.IO_HUP: self.stop() raise Exception('Socket disconnected') else: self.httpd.handle_request() except: logger.exception('Exception while handling IO request:') return True # keep event running def do_serve_on_poll(self): self.httpd.handle_request() return True # keep event running def stop(self): # Stop server logger.debug('Stop server') if self._source_id is not None: GObject.source_remove(self._source_id) self._source_id = None if self.httpd: self.httpd.socket.close() # There is also a httpd.server_close(), but undocumented (!?) self.httpd = None # Update UI self.status_label.set_markup('<i>' + _('Server stopped') + '</i>') # T: Status in web server gui if self.link_button: self.link_button.set_sensitive(False) self.notebookcombobox.set_sensitive(True) self.portentry.set_sensitive(True) self.public_checkbox.set_sensitive(True) self.templatecombobox.set_sensitive(True) self.open_button.set_sensitive(True) self.stop_button.set_sensitive(False) self.start_button.set_sensitive(True) self.auth_checkbox.set_sensitive(True) self.username_input.set_sensitive(True) self.password_input.set_sensitive(True)
class SearchDialog(Dialog): READY = 0 SEARCHING = 1 CANCELLED = 2 def __init__(self, window): Dialog.__init__( self, window, _('Search'), # T: Dialog title buttons=gtk.BUTTONS_CLOSE, help='Help:Searching', defaultwindowsize=(400, 300)) self.app_window = window hbox = gtk.HBox(spacing=5) self.vbox.pack_start(hbox, False) hbox.pack_start(gtk.Label(_('Search') + ': '), False) # T: input label self.query_entry = InputEntry() hbox.add(self.query_entry) self.search_button = gtk.Button(stock=gtk.STOCK_FIND) hbox.pack_start(self.search_button, False) if gtk.gtk_version >= (2, 20) \ and gtk.pygtk_version >= (2, 22): # update in pygtk was later self.spinner = gtk.Spinner() hbox.pack_start(self.spinner, False) else: self.spinner = None self.cancel_button = gtk.Button(stock=gtk.STOCK_STOP) hbox.pack_start(self.cancel_button, False) self._set_state(self.READY) help_text = _('For advanced search you can use operators like\n' 'AND, OR and NOT. See the help page for more details.' ) # T: help text for the search dialog if gtk.gtk_version >= (2, 12) \ and gtk.pygtk_version >= (2, 12): self.query_entry.set_tooltip_text(help_text) else: tooltips = gtk.Tooltips() tooltips.set_tip(self.query_entry, help_text) self.namespacecheckbox = gtk.CheckButton( _('Limit search to the current page and sub-pages')) # T: checkbox option in search dialog self.vbox.pack_start(self.namespacecheckbox, False) # TODO advanced query editor # TODO checkbox _('Match c_ase') # TODO checkbox _('Whole _word') self.results_treeview = SearchResultsTreeView(self.app_window) self.vbox.add(ScrolledWindow(self.results_treeview)) self.search_button.connect_object('clicked', self.__class__._search, self) self.cancel_button.connect_object('clicked', self.__class__._cancel, self) self.query_entry.connect_object('activate', self.__class__._search, self) def search(self, query): '''Trigger a search to be performed. Because search can take a long time to execute it is best to call this method after the dialog is shown. @param query: the query as string ''' self.query_entry.set_text(query) self._search() def _search(self): string = self.query_entry.get_text() if self.namespacecheckbox.get_active(): string = 'Section: "%s" ' % self.app_window.ui.page.name + string # XXX #~ print '!! QUERY: ' + string self._set_state(self.SEARCHING) try: self.results_treeview.search(string) except Exception as error: ErrorDialog(self, error).run() if not self.results_treeview.cancelled: self._set_state(self.READY) else: self._set_state(self.CANCELLED) def _cancel(self): self.results_treeview.cancelled = True def _set_state(self, state): # TODO set cursor for treeview part # TODO set label or something ? def hide(button): button.hide_all() button.set_no_show_all(True) def show(button): button.set_no_show_all(False) button.show_all() if state in (self.READY, self.CANCELLED): self.query_entry.set_sensitive(True) hide(self.cancel_button) if self.spinner: self.spinner.stop() hide(self.spinner) show(self.search_button) elif state == self.SEARCHING: self.query_entry.set_sensitive(False) hide(self.search_button) if self.spinner: show(self.spinner) self.spinner.start() show(self.cancel_button) else: assert False, 'BUG: invalid state'