Esempio n. 1
0
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'
Esempio n. 2
0
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)
Esempio n. 3
0
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'