Example #1
0
class SearchDialog(Dialog):

    READY = 0
    SEARCHING = 1
    CANCELLED = 2

    def __init__(self, ui):
        Dialog.__init__(
            self,
            ui,
            _('Search'),  # T: Dialog title
            buttons=gtk.BUTTONS_CLOSE,
            help='Help:Searching',
            defaultwindowsize=(400, 300))

        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.ui)
        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.ui.page.name + string
        #~ print '!! QUERY: ' + string

        self._set_state(self.SEARCHING)
        try:
            self.results_treeview.search(string)
        except Exception, error:
            ErrorDialog(self, error).run()

        if not self.results_treeview.cancelled:
            self._set_state(self.READY)
        else:
            self._set_state(self.CANCELLED)
class GoogletasksNewTaskDialog(Dialog):

    def __init__(self, *args, task=None, controller=None, **kwargs):
        self.input_title = None
        self.input_due = None
        self.input_notes = None
        self.label_object = None
        self.label_due = None
        self.task = task
        self.controller = controller
        super().__init__(*args, **kwargs)

    # noinspection PyArgumentList
    def setup(self):
        self.resize(300, 100)  # reset size

        # title
        self.label_object = Gtk.Label(label='Update Google task' if self.task.get("id", "") else 'Create Google tasks')
        self.label_object.set_size_request(300, -1)
        self.vbox.pack_start(self.label_object, expand=False, fill=True, padding=0)

        # editable text fields (title and notes)
        self.input_title = InputEntry(allow_empty=False, placeholder_text="task title")
        self.vbox.pack_start(self.input_title, expand=False, fill=True, padding=0)
        self.input_notes = Gtk.TextView()

        # date field
        self.input_due = InputEntry(allow_empty=False)
        try:
            s = self.controller.get_time(mode="date-only", from_string=self.task["due"], past_dates=False)
        except (ValueError, KeyError):  # task["due"] is not set or is in past
            s = self.controller.get_time(add_days=1, mode="date-only")
        self.input_due.set_text(s)
        self.input_due.connect('changed', self.update_date)
        self.label_due = Gtk.Label(self.controller.get_time(add_days=1, mode="day"))
        hbox = Gtk.HBox(spacing=1)
        hbox.pack_start(self.input_due, expand=True, fill=True, padding=0)
        hbox.pack_start(self.label_due, expand=True, fill=True, padding=0)
        self.vbox.pack_start(hbox, expand=False, fill=True, padding=0)

        # we cant tab out from notes textarea field, hence its placed under date
        self.vbox.pack_start(self.input_notes, expand=False, fill=True, padding=0)

        # arbitrary postponing buttons
        hbox = Gtk.HBox()

        def butt(label=None, date=None, days=None):
            """ Call either with label and date are with days."""
            if days:
                label = f"{'_' if days < 10 else ''}{days} {self.controller.get_time(add_days=days, mode='day')}"
                date = self.controller.get_time(add_days=days, mode="date-only")
            b = Gtk.Button.new_with_mnemonic(label=label)
            b.connect("clicked", self.postpone(date))
            b.set_tooltip_text(date)
            hbox.pack_start(b, expand=False, fill=True, padding=0)

        if self.controller.preferences["button_monday"]:
            butt("_Monday", self._slippy_date(next_monday=True))
        if self.controller.preferences["button_next_monday"]:
            butt("_Next Monday", self._slippy_date(next_monday=True, relative_delta={"days": 7}))
        if self.controller.preferences["button_next_month"]:
            butt("Mon_th", self._slippy_date(relative_delta={"months": 1, "day": 1}))
        if self.controller.preferences["button_next_year"]:
            butt("_Year", self._slippy_date(relative_delta={"years": 1, "month": 2, "day": 1}))

        if hbox.get_children():  # there were some buttons to display
            self.vbox.add(hbox)

        # 9 (or more) postponing buttons
        days = self.controller.preferences["postponing_days"]
        if days:
            hbox = Gtk.HBox()
            self.vbox.add(hbox)
            hbox.pack_start(Gtk.Label(label='Days: '), expand=False, fill=True, padding=0)
            for i in range(1, days + 1):
                butt(days=i)
                if not i % 9:  # put every 9 buttons to another line, do not expand the row to infinity
                    hbox = Gtk.HBox()
                    self.vbox.add(hbox)

        # predefined fields
        if "title" in self.task:
            self.input_title.set_text(self.task["title"])
        if "notes" in self.task:
            self.input_notes.get_buffer().set_text(self.task["notes"])
        if "due" in self.task:
            logger.error("Not yet implemented")  # XX

        # display window
        self.show_all()
        return self

    def _slippy_date(self, add_days=0, next_monday=False, relative_delta=None):

        def next_weekday(d, weekday):
            days_ahead = weekday - d.weekday()
            if days_ahead <= 0:  # Target day already happened this week
                days_ahead += 7
            return d + datetime.timedelta(days_ahead)

        d = self.controller.get_time(add_days=add_days, mode="object")

        if next_monday:
            d = next_weekday(d, 0)  # 0 = Monday, 1=Tuesday, 2=Wednesday...
        if relative_delta:
            d += relativedelta(**relative_delta)
        return self.controller.get_time(use_date=d, mode="date-only")

    def update_date(self, _):
        try:
            day = self.controller.get_time(from_string=self.input_due.get_text(), mode="day", past_dates=False)
        except ValueError:
            day = INVALID_DAY
        self.label_due.set_text(day)

    def _load_task(self):
        # noinspection PyBroadException
        try:
            o = self.input_notes.get_buffer()
            self.task["notes"] = o.get_text(*o.get_bounds(), include_hidden_chars=True)
            self.task["title"] = self.input_title.get_text()
            if self.input_due.get_text():
                try:
                    self.task["due"] = self.controller.get_time(from_string=self.input_due.get_text(), mode="morning")
                except Exception:
                    logger.error("Failed due date parsing")
                    raise
        except Exception:
            pass

    def do_response(self, id_):
        """ we cant use parent function because Esc is not handled as cancel """
        if id_ == Gtk.ResponseType.OK:
            self.do_response_ok()
        else:
            self.do_response_cancel()

    def do_response_ok(self):
        if self.label_due.get_text() == INVALID_DAY:
            return False
        self._load_task()
        self.destroy()  # immediately close (so that we wont hit Ok twice)
        if not self.controller.submit_task(task=self.task):
            self.do_response_cancel()
        return True

    def do_response_cancel(self):
        """ something failed, restore original text in the zim-page """
        text = self.controller.get_task_text(self.task, self.controller.preferences["include_start_date"])
        if text:
            buffer = self.controller.window.pageview.textview.get_buffer()
            buffer.insert_parsetree_at_cursor(Parser().parse(text))
        self.destroy()

    # def postpone(self, _, number):
    #     self.input_due.set_text(self.controller.get_time(add_days=number, mode="date-only"))
    #     self.do_response_ok()
    def postpone(self, date):

        def _(_):
            self.input_due.set_text(date)
            self.do_response_ok()

        return _
Example #3
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'
Example #4
0
class SearchDialog(Dialog):

	READY = 0
	SEARCHING = 1
	CANCELLED = 2

	def __init__(self, ui):
		Dialog.__init__(self, ui, _('Search'), # T: Dialog title
			buttons=gtk.BUTTONS_CLOSE, help='Help:Searching',
			defaultwindowsize=(400, 300)
		)

		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):
			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, 0):
			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 current namespace'))
			# 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.ui)
		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 = 'Namespace: "%s" ' % self.ui.page.name + string
		#~ print '!! QUERY: ' + string

		self._set_state(self.SEARCHING)
		try:
			self.results_treeview.search(string)
		except Exception, error:
			ErrorDialog(self, error).run()

		if not self.results_treeview.cancelled:
			self._set_state(self.READY)
		else:
			self._set_state(self.CANCELLED)
Example #5
0
class FileRenameDialog(Dialog):
    """ A dialog for renaming a file. """
    def __init__(self, parent, file):
        title = _('Rename file')  # T: dialog title
        Dialog.__init__(self, parent, title)

        assert isinstance(file,
                          LocalFile) and not isinstance(file, LocalFolder)
        self.old_file = file
        self.new_file = file

        # Add field for entering new filename.
        self.txt_filename = InputEntry()
        self.txt_filename.set_text(self.old_file.basename)
        self.txt_filename.set_activates_default(
            True)  # Make ENTER key press trigger the OK button.
        self.txt_filename.connect("changed", self.do_validate, parent)

        # Add field for showing hints when an error occurs (e.g. filename already exists).
        self.txt_error = Gtk.Label()
        self.txt_error.set_visible(False)

        # Add ok button.
        self.btn_ok = self.get_widget_for_response(
            response_id=Gtk.ResponseType.OK)
        self.btn_ok.set_can_default(True)
        self.btn_ok.grab_default()
        self.btn_ok.set_sensitive(False)

        # Configure dialog.
        self.set_modal(True)
        self.set_default_size(380, 100)
        self.vbox.pack_start(self.txt_filename, False, True, 0)
        self.vbox.pack_start(self.txt_error, False, True, 0)

        # Set focus to search field
        self.txt_filename.grab_focus()

    def do_validate(self, widget, data):
        """ Validating new file name, show error when validation fails and enable/disable ok button. """
        def is_filename(filename):
            """ Returns True when filename does not contain any path declaration. """
            return filename == os.path.basename(filename)

        def does_file_already_exist(filename):
            """ Checks whether the new filename is already taken. """
            return self.old_file.parent().file(filename).exists()

        def show_error(msg):
            """ Displays an error and disables the OK button. """
            self.txt_error.set_text(msg)
            self.txt_error.set_visible(True)
            self.btn_ok.set_sensitive(False)
            return False

        if not widget.get_text():
            return show_error(
                _('File name should not be blank.'))  # T: Error message

        if does_file_already_exist(widget.get_text()):
            return show_error(
                _('A file with that name already exists.'))  # T: Error message

        if not is_filename(widget.get_text()):
            return show_error(
                _('File name should not contain path declaration.')
            )  # T: Error message

        # No errors, hide error label and enable OK button.
        self.txt_error.hide()
        self.btn_ok.set_sensitive(True)
        return True

    def do_response_ok(self):
        self.new_file = self.old_file.parent().file(
            self.txt_filename.get_text())
        self.result = Gtk.ResponseType.OK
        self.close()