class BookSelector(ManagedWindow): """ Interface into a dialog setting up the book. Allows the user to add/remove/reorder/setup items for the current book and to clear/load/save/edit whole books. """ def __init__(self, dbstate, uistate): self._db = dbstate.db self.dbstate = dbstate self.uistate = uistate self.title = _('Manage Books') self.file = "books.xml" ManagedWindow.__init__(self, uistate, [], self.__class__) self.xml = Glade('book.glade', toplevel="top") window = self.xml.toplevel title_label = self.xml.get_object('title') self.set_window(window, title_label, self.title) window.show() self.xml.connect_signals({ "on_add_clicked" : self.on_add_clicked, "on_remove_clicked" : self.on_remove_clicked, "on_up_clicked" : self.on_up_clicked, "on_down_clicked" : self.on_down_clicked, "on_setup_clicked" : self.on_setup_clicked, "on_clear_clicked" : self.on_clear_clicked, "on_save_clicked" : self.on_save_clicked, "on_open_clicked" : self.on_open_clicked, "on_edit_clicked" : self.on_edit_clicked, "on_book_ok_clicked" : self.on_book_ok_clicked, "destroy_passed_object" : self.on_close_clicked, # Insert dummy handlers for second top level in the glade file "on_booklist_ok_clicked" : lambda _: None, "on_booklist_delete_clicked" : lambda _: None, "on_booklist_cancel_clicked" : lambda _: None, "on_booklist_ok_clicked" : lambda _: None, "on_booklist_ok_clicked" : lambda _: None, }) self.avail_tree = self.xml.get_object("avail_tree") self.book_tree = self.xml.get_object("book_tree") self.avail_tree.connect('button-press-event', self.avail_button_press) self.book_tree.connect('button-press-event', self.book_button_press) self.name_entry = self.xml.get_object("name_entry") self.name_entry.set_text(_('New Book')) avail_label = self.xml.get_object('avail_label') avail_label.set_text("<b>%s</b>" % _("_Available items")) avail_label.set_use_markup(True) avail_label.set_use_underline(True) book_label = self.xml.get_object('book_label') book_label.set_text("<b>%s</b>" % _("Current _book")) book_label.set_use_underline(True) book_label.set_use_markup(True) avail_titles = [(_('Name'), 0, 230), (_('Type'), 1, 80), ('', -1, 0)] book_titles = [(_('Item name'), -1, 230), (_('Type'), -1, 80), ('', -1, 0), (_('Subject'), -1, 50)] self.avail_nr_cols = len(avail_titles) self.book_nr_cols = len(book_titles) self.avail_model = ListModel(self.avail_tree, avail_titles) self.book_model = ListModel(self.book_tree, book_titles) self.draw_avail_list() self.book = Book() self.book_list = BookList(self.file, self._db) self.book_list.set_needs_saving(False) # just read in: no need to save def build_menu_names(self, obj): return (_("Book selection list"), self.title) def draw_avail_list(self): """ Draw the list with the selections available for the book. The selections are read from the book item registry. """ pmgr = GuiPluginManager.get_instance() regbi = pmgr.get_reg_bookitems() if not regbi: return available_reports = [] for pdata in regbi: category = _UNSUPPORTED if pdata.supported and pdata.category in book_categories: category = book_categories[pdata.category] available_reports.append([pdata.name, category, pdata.id]) for data in sorted(available_reports): new_iter = self.avail_model.add(data) self.avail_model.connect_model() if new_iter: self.avail_model.selection.select_iter(new_iter) path = self.avail_model.model.get_path(new_iter) col = self.avail_tree.get_column(0) self.avail_tree.scroll_to_cell(path, col, 1, 1, 0.0) def open_book(self, book): """ Open the book: set the current set of selections to this book's items. book: the book object to load. """ if book.get_paper_name(): self.book.set_paper_name(book.get_paper_name()) if book.get_orientation() is not None: # 0 is legal self.book.set_orientation(book.get_orientation()) if book.get_paper_metric() is not None: # 0 is legal self.book.set_paper_metric(book.get_paper_metric()) if book.get_custom_paper_size(): self.book.set_custom_paper_size(book.get_custom_paper_size()) if book.get_margins(): self.book.set_margins(book.get_margins()) if book.get_format_name(): self.book.set_format_name(book.get_format_name()) if book.get_output(): self.book.set_output(book.get_output()) if book.get_dbname() != self._db.get_save_path(): WarningDialog( # parent-OK _('Different database'), _('This book was created with the references to database ' '%s.\n\n This makes references to the central person ' 'saved in the book invalid.\n\n' 'Therefore, the central person for each item is being set ' 'to the active person of the currently opened database.' ) % book.get_dbname(), parent=self.window) self.book.clear() self.book_model.clear() for saved_item in book.get_item_list(): name = saved_item.get_name() item = BookItem(self._db, name) item.option_class = saved_item.option_class # The option values were loaded magically by the book parser. # But they still need to be applied to the menu options. opt_dict = item.option_class.handler.options_dict menu = item.option_class.menu for optname in opt_dict: menu_option = menu.get_option_by_name(optname) if menu_option: menu_option.set_value(opt_dict[optname]) _initialize_options(item.option_class, self.dbstate, self.uistate) item.set_style_name(saved_item.get_style_name()) self.book.append_item(item) data = [item.get_translated_name(), item.get_category(), item.get_name()] data[2] = item.option_class.get_subject() self.book_model.add(data) def on_add_clicked(self, obj): """ Add an item to the current selections. Use the selected available item to get the item's name in the registry. """ store, the_iter = self.avail_model.get_selected() if not the_iter: return data = self.avail_model.get_data(the_iter, list(range(self.avail_nr_cols))) item = BookItem(self._db, data[2]) _initialize_options(item.option_class, self.dbstate, self.uistate) data[2] = item.option_class.get_subject() self.book_model.add(data) self.book.append_item(item) def on_remove_clicked(self, obj): """ Remove the item from the current list of selections. """ store, the_iter = self.book_model.get_selected() if not the_iter: return row = self.book_model.get_selected_row() self.book.pop_item(row) self.book_model.remove(the_iter) def on_clear_clicked(self, obj): """ Clear the whole current book. """ self.book_model.clear() self.book.clear() def on_up_clicked(self, obj): """ Move the currently selected item one row up in the selection list. """ row = self.book_model.get_selected_row() if not row or row == -1: return store, the_iter = self.book_model.get_selected() data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) self.book_model.remove(the_iter) self.book_model.insert(row-1, data, None, 1) item = self.book.pop_item(row) self.book.insert_item(row-1, item) def on_down_clicked(self, obj): """ Move the currently selected item one row down in the selection list. """ row = self.book_model.get_selected_row() if row + 1 >= self.book_model.count or row == -1: return store, the_iter = self.book_model.get_selected() data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) self.book_model.remove(the_iter) self.book_model.insert(row+1, data, None, 1) item = self.book.pop_item(row) self.book.insert_item(row+1, item) def on_setup_clicked(self, obj): """ Configure currently selected item. """ store, the_iter = self.book_model.get_selected() if not the_iter: WarningDialog(_('No selected book item'), # parent-OK _('Please select a book item to configure.'), parent=self.window) return row = self.book_model.get_selected_row() item = self.book.get_item(row) option_class = item.option_class option_class.handler.set_default_stylesheet_name(item.get_style_name()) item.is_from_saved_book = bool(self.book.get_name()) item_dialog = BookItemDialog(self.dbstate, self.uistate, item, self.track) while True: response = item_dialog.window.run() if response == Gtk.ResponseType.OK: # dialog will be closed by connect, now continue work while # rest of dialog is unresponsive, release when finished style = option_class.handler.get_default_stylesheet_name() item.set_style_name(style) subject = option_class.get_subject() self.book_model.model.set_value(the_iter, 2, subject) self.book.set_item(row, item) item_dialog.close() break elif response == Gtk.ResponseType.CANCEL: item_dialog.close() break elif response == Gtk.ResponseType.DELETE_EVENT: #just stop, in ManagedWindow, delete-event is already coupled to #correct action. break opt_dict = option_class.handler.options_dict for optname in opt_dict: menu_option = option_class.menu.get_option_by_name(optname) if menu_option: menu_option.set_value(opt_dict[optname]) def book_button_press(self, obj, event): """ Double-click on the current book selection is the same as setup. Right click evokes the context menu. """ if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: self.on_setup_clicked(obj) elif is_right_click(event): self.build_book_context_menu(event) def avail_button_press(self, obj, event): """ Double-click on the available selection is the same as add. Right click evokes the context menu. """ if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: self.on_add_clicked(obj) elif is_right_click(event): self.build_avail_context_menu(event) def build_book_context_menu(self, event): """Builds the menu with item-centered and book-centered options.""" store, the_iter = self.book_model.get_selected() if the_iter: sensitivity = 1 else: sensitivity = 0 entries = [ (_('_Up'), 'go-up', self.on_up_clicked, sensitivity), (_('_Down'), 'go-down', self.on_down_clicked, sensitivity), (_("Setup"), None, self.on_setup_clicked, sensitivity), (_('_Remove'), 'list-remove', self.on_remove_clicked, sensitivity), ('', None, None, 0), (_('_Clear'), 'edit-clear', self.on_clear_clicked, 1), (_('_Save'), 'document-save', self.on_save_clicked, 1), (_('_Open'), 'document-open', self.on_open_clicked, 1), (_("Edit"), None, self.on_edit_clicked, 1), ] self.menu1 = Gtk.Menu() # TODO could this be just a local "menu ="? self.menu1.set_title(_('Book Menu')) for title, icon_name, callback, sensitivity in entries: if icon_name: item = Gtk.ImageMenuItem.new_with_mnemonic(title) img = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU) item.set_image(img) else: item = Gtk.MenuItem.new_with_mnemonic(title) if callback: item.connect("activate", callback) item.set_sensitive(sensitivity) item.show() self.menu1.append(item) self.menu1.popup(None, None, None, None, event.button, event.time) def build_avail_context_menu(self, event): """Builds the menu with the single Add option.""" store, the_iter = self.avail_model.get_selected() if the_iter: sensitivity = 1 else: sensitivity = 0 entries = [ (_('_Add'), 'list-add', self.on_add_clicked, sensitivity), ] self.menu2 = Gtk.Menu() # TODO could this be just a local "menu ="? self.menu2.set_title(_('Available Items Menu')) for title, icon_name, callback, sensitivity in entries: if icon_name: item = Gtk.ImageMenuItem.new_with_mnemonic(title) img = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU) item.set_image(img) else: item = Gtk.MenuItem.new_with_mnemonic(title) if callback: item.connect("activate", callback) item.set_sensitive(sensitivity) item.show() self.menu2.append(item) self.menu2.popup(None, None, None, None, event.button, event.time) def on_close_clicked(self, obj): """ close the BookSelector dialog, saving any changes if needed """ if self.book_list.get_needs_saving(): self.book_list.save() ManagedWindow.close(self, *obj) def on_book_ok_clicked(self, obj): """ Run final BookDialog with the current book. """ if self.book.item_list: old_paper_name = self.book.get_paper_name() # from books.xml old_orientation = self.book.get_orientation() old_paper_metric = self.book.get_paper_metric() old_custom_paper_size = self.book.get_custom_paper_size() old_margins = self.book.get_margins() old_format_name = self.book.get_format_name() old_output = self.book.get_output() BookDialog(self.dbstate, self.uistate, self.book, BookOptions) new_paper_name = self.book.get_paper_name() new_orientation = self.book.get_orientation() new_paper_metric = self.book.get_paper_metric() new_custom_paper_size = self.book.get_custom_paper_size() new_margins = self.book.get_margins() new_format_name = self.book.get_format_name() new_output = self.book.get_output() # only books in the booklist have a name (not "ad hoc" ones) if (self.book.get_name() and (old_paper_name != new_paper_name or old_orientation != new_orientation or old_paper_metric != new_paper_metric or old_custom_paper_size != new_custom_paper_size or old_margins != new_margins or old_format_name != new_format_name or old_output != new_output)): self.book.set_dbname(self._db.get_save_path()) self.book_list.set_book(self.book.get_name(), self.book) self.book_list.set_needs_saving(True) if self.book_list.get_needs_saving(): self.book_list.save() else: WarningDialog(_('No items'), # parent-OK _('This book has no items.'), parent=self.window) return self.close() def on_save_clicked(self, obj): """ Save the current book in the xml booklist file. """ name = str(self.name_entry.get_text()) if not name: WarningDialog( # parent-OK _('No book name'), _('You are about to save away a book with no name.\n\n' 'Please give it a name before saving it away.'), parent=self.window) return if name in self.book_list.get_book_names(): qqq = QuestionDialog2( # parent-OK _('Book name already exists'), _('You are about to save away a ' 'book with a name which already exists.'), _('Proceed'), _('Cancel'), parent=self.window) if not qqq.run(): return # previously, the same book could be added to the booklist # under multiple names, which became different books once the # booklist was saved into a file so everything was fine, but # this created a problem once the paper settings were added # to the Book object in the BookDialog, since those settings # were retrieved from the Book object in Book.save, so mutiple # books (differentiated by their names) were assigned the # same paper values, so the solution is to make each Book be # unique in the booklist, so if multiple copies are saved away # only the last one will get the paper values assigned to it # (although when the earlier books are then eventually run, # they'll be assigned paper values also) self.book.set_name(name) self.book.set_dbname(self._db.get_save_path()) self.book_list.set_book(name, self.book) self.book_list.set_needs_saving(True) # user clicked on save self.book = Book(self.book, exact_copy=False) # regenerate old items self.book.set_name(name) self.book.set_dbname(self._db.get_save_path()) def on_open_clicked(self, obj): """ Run the BookListDisplay dialog to present the choice of books to open. """ booklistdisplay = BookListDisplay(self.book_list, nodelete=True, dosave=False) booklistdisplay.top.destroy() book = booklistdisplay.selection if book: self.open_book(book) self.name_entry.set_text(book.get_name()) self.book.set_name(book.get_name()) def on_edit_clicked(self, obj): """ Run the BookListDisplay dialog to present the choice of books to delete. """ booklistdisplay = BookListDisplay(self.book_list, nodelete=False, dosave=True) booklistdisplay.top.destroy() book = booklistdisplay.selection if book: self.open_book(book) self.name_entry.set_text(book.get_name()) self.book.set_name(book.get_name())
class BookSelector(ManagedWindow): """ Interface into a dialog setting up the book. Allows the user to add/remove/reorder/setup items for the current book and to clear/load/save/edit whole books. """ def __init__(self, dbstate, uistate): self._db = dbstate.db self.dbstate = dbstate self.uistate = uistate self.title = _('Manage Books') self.file = "books.xml" ManagedWindow.__init__(self, uistate, [], self.__class__) self.xml = Glade('book.glade', toplevel="top") window = self.xml.toplevel title_label = self.xml.get_object('title') self.set_window(window, title_label, self.title) self.setup_configs('interface.bookselector', 700, 600) self.show() self.xml.connect_signals({ "on_add_clicked" : self.on_add_clicked, "on_remove_clicked" : self.on_remove_clicked, "on_up_clicked" : self.on_up_clicked, "on_down_clicked" : self.on_down_clicked, "on_setup_clicked" : self.on_setup_clicked, "on_clear_clicked" : self.on_clear_clicked, "on_save_clicked" : self.on_save_clicked, "on_open_clicked" : self.on_open_clicked, "on_edit_clicked" : self.on_edit_clicked, "on_book_ok_clicked" : self.on_book_ok_clicked, "destroy_passed_object" : self.on_close_clicked, # Insert dummy handlers for second top level in the glade file "on_booklist_ok_clicked" : lambda _: None, "on_booklist_delete_clicked" : lambda _: None, "on_booklist_cancel_clicked" : lambda _: None, "on_booklist_ok_clicked" : lambda _: None, "on_booklist_ok_clicked" : lambda _: None, }) self.avail_tree = self.xml.get_object("avail_tree") self.book_tree = self.xml.get_object("book_tree") self.avail_tree.connect('button-press-event', self.avail_button_press) self.book_tree.connect('button-press-event', self.book_button_press) self.name_entry = self.xml.get_object("name_entry") self.name_entry.set_text(_('New Book')) avail_label = self.xml.get_object('avail_label') avail_label.set_text("<b>%s</b>" % _("_Available items")) avail_label.set_use_markup(True) avail_label.set_use_underline(True) book_label = self.xml.get_object('book_label') book_label.set_text("<b>%s</b>" % _("Current _book")) book_label.set_use_underline(True) book_label.set_use_markup(True) avail_titles = [(_('Name'), 0, 230), (_('Type'), 1, 80), ('', -1, 0)] book_titles = [(_('Item name'), -1, 230), (_('Type'), -1, 80), ('', -1, 0), (_('Subject'), -1, 50)] self.avail_nr_cols = len(avail_titles) self.book_nr_cols = len(book_titles) self.avail_model = ListModel(self.avail_tree, avail_titles) self.book_model = ListModel(self.book_tree, book_titles) self.draw_avail_list() self.book = Book() self.book_list = BookList(self.file, self._db) self.book_list.set_needs_saving(False) # just read in: no need to save def build_menu_names(self, obj): return (_("Book selection list"), self.title) def draw_avail_list(self): """ Draw the list with the selections available for the book. The selections are read from the book item registry. """ pmgr = GuiPluginManager.get_instance() regbi = pmgr.get_reg_bookitems() if not regbi: return available_reports = [] for pdata in regbi: category = _UNSUPPORTED if pdata.supported and pdata.category in book_categories: category = book_categories[pdata.category] available_reports.append([pdata.name, category, pdata.id]) for data in sorted(available_reports): new_iter = self.avail_model.add(data) self.avail_model.connect_model() if new_iter: self.avail_model.selection.select_iter(new_iter) path = self.avail_model.model.get_path(new_iter) col = self.avail_tree.get_column(0) self.avail_tree.scroll_to_cell(path, col, 1, 1, 0.0) def open_book(self, book): """ Open the book: set the current set of selections to this book's items. book: the book object to load. """ if book.get_paper_name(): self.book.set_paper_name(book.get_paper_name()) if book.get_orientation() is not None: # 0 is legal self.book.set_orientation(book.get_orientation()) if book.get_paper_metric() is not None: # 0 is legal self.book.set_paper_metric(book.get_paper_metric()) if book.get_custom_paper_size(): self.book.set_custom_paper_size(book.get_custom_paper_size()) if book.get_margins(): self.book.set_margins(book.get_margins()) if book.get_format_name(): self.book.set_format_name(book.get_format_name()) if book.get_output(): self.book.set_output(book.get_output()) if book.get_dbname() != self._db.get_save_path(): WarningDialog( _('Different database'), _('This book was created with the references to database ' '%s.\n\n This makes references to the central person ' 'saved in the book invalid.\n\n' 'Therefore, the central person for each item is being set ' 'to the active person of the currently opened database.' ) % book.get_dbname(), parent=self.window) self.book.clear() self.book_model.clear() for saved_item in book.get_item_list(): name = saved_item.get_name() item = BookItem(self._db, name) # The option values were loaded magically by the book parser. # But they still need to be applied to the menu options. opt_dict = item.option_class.handler.options_dict orig_opt_dict = saved_item.option_class.handler.options_dict menu = item.option_class.menu for optname in opt_dict: opt_dict[optname] = orig_opt_dict[optname] menu_option = menu.get_option_by_name(optname) if menu_option: menu_option.set_value(opt_dict[optname]) _initialize_options(item.option_class, self.dbstate, self.uistate) item.set_style_name(saved_item.get_style_name()) self.book.append_item(item) data = [item.get_translated_name(), item.get_category(), item.get_name()] data[2] = item.option_class.get_subject() self.book_model.add(data) def on_add_clicked(self, obj): """ Add an item to the current selections. Use the selected available item to get the item's name in the registry. """ store, the_iter = self.avail_model.get_selected() if not the_iter: return data = self.avail_model.get_data(the_iter, list(range(self.avail_nr_cols))) item = BookItem(self._db, data[2]) _initialize_options(item.option_class, self.dbstate, self.uistate) data[2] = item.option_class.get_subject() self.book_model.add(data) self.book.append_item(item) def on_remove_clicked(self, obj): """ Remove the item from the current list of selections. """ store, the_iter = self.book_model.get_selected() if not the_iter: return row = self.book_model.get_selected_row() self.book.pop_item(row) self.book_model.remove(the_iter) def on_clear_clicked(self, obj): """ Clear the whole current book. """ self.book_model.clear() self.book.clear() def on_up_clicked(self, obj): """ Move the currently selected item one row up in the selection list. """ row = self.book_model.get_selected_row() if not row or row == -1: return store, the_iter = self.book_model.get_selected() data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) self.book_model.remove(the_iter) self.book_model.insert(row-1, data, None, 1) item = self.book.pop_item(row) self.book.insert_item(row-1, item) def on_down_clicked(self, obj): """ Move the currently selected item one row down in the selection list. """ row = self.book_model.get_selected_row() if row + 1 >= self.book_model.count or row == -1: return store, the_iter = self.book_model.get_selected() data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) self.book_model.remove(the_iter) self.book_model.insert(row+1, data, None, 1) item = self.book.pop_item(row) self.book.insert_item(row+1, item) def on_setup_clicked(self, obj): """ Configure currently selected item. """ store, the_iter = self.book_model.get_selected() if not the_iter: WarningDialog(_('No selected book item'), _('Please select a book item to configure.'), parent=self.window) return row = self.book_model.get_selected_row() item = self.book.get_item(row) option_class = item.option_class option_class.handler.set_default_stylesheet_name(item.get_style_name()) item.is_from_saved_book = bool(self.book.get_name()) item_dialog = BookItemDialog(self.dbstate, self.uistate, item, self.track) while True: response = item_dialog.window.run() if response == Gtk.ResponseType.OK: # dialog will be closed by connect, now continue work while # rest of dialog is unresponsive, release when finished style = option_class.handler.get_default_stylesheet_name() item.set_style_name(style) subject = option_class.get_subject() self.book_model.model.set_value(the_iter, 2, subject) self.book.set_item(row, item) item_dialog.close() break elif response == Gtk.ResponseType.CANCEL: item_dialog.close() break elif response == Gtk.ResponseType.DELETE_EVENT: #just stop, in ManagedWindow, delete-event is already coupled to #correct action. break opt_dict = option_class.handler.options_dict for optname in opt_dict: menu_option = option_class.menu.get_option_by_name(optname) if menu_option: menu_option.set_value(opt_dict[optname]) def book_button_press(self, obj, event): """ Double-click on the current book selection is the same as setup. Right click evokes the context menu. """ if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: self.on_setup_clicked(obj) elif is_right_click(event): self.build_book_context_menu(event) def avail_button_press(self, obj, event): """ Double-click on the available selection is the same as add. Right click evokes the context menu. """ if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: self.on_add_clicked(obj) elif is_right_click(event): self.build_avail_context_menu(event) def build_book_context_menu(self, event): """Builds the menu with item-centered and book-centered options.""" store, the_iter = self.book_model.get_selected() if the_iter: sensitivity = 1 else: sensitivity = 0 entries = [ (_('_Up'), self.on_up_clicked, sensitivity), (_('_Down'), self.on_down_clicked, sensitivity), (_("Setup"), self.on_setup_clicked, sensitivity), (_('_Remove'), self.on_remove_clicked, sensitivity), ('', None, 0), (_('Clear the book'), self.on_clear_clicked, 1), (_('_Save'), self.on_save_clicked, 1), (_('_Open'), self.on_open_clicked, 1), (_("_Edit"), self.on_edit_clicked, 1), ] self.menu1 = Gtk.Menu() # TODO could this be just a local "menu ="? self.menu1.set_reserve_toggle_size(False) for title, callback, sensitivity in entries: item = Gtk.MenuItem.new_with_mnemonic(title) Gtk.Label.new_with_mnemonic if callback: item.connect("activate", callback) else: item = Gtk.SeparatorMenuItem() item.set_sensitive(sensitivity) item.show() self.menu1.append(item) self.menu1.popup(None, None, None, None, event.button, event.time) def build_avail_context_menu(self, event): """Builds the menu with the single Add option.""" store, the_iter = self.avail_model.get_selected() if the_iter: sensitivity = 1 else: sensitivity = 0 entries = [ (_('_Add'), self.on_add_clicked, sensitivity), ] self.menu2 = Gtk.Menu() # TODO could this be just a local "menu ="? self.menu2.set_reserve_toggle_size(False) for title, callback, sensitivity in entries: item = Gtk.MenuItem.new_with_mnemonic(title) if callback: item.connect("activate", callback) item.set_sensitive(sensitivity) item.show() self.menu2.append(item) self.menu2.popup(None, None, None, None, event.button, event.time) def on_close_clicked(self, obj): """ close the BookSelector dialog, saving any changes if needed """ if self.book_list.get_needs_saving(): self.book_list.save() ManagedWindow.close(self, *obj) def on_book_ok_clicked(self, obj): """ Run final BookDialog with the current book. """ if self.book.get_item_list(): old_paper_name = self.book.get_paper_name() # from books.xml old_orientation = self.book.get_orientation() old_paper_metric = self.book.get_paper_metric() old_custom_paper_size = self.book.get_custom_paper_size() old_margins = self.book.get_margins() old_format_name = self.book.get_format_name() old_output = self.book.get_output() BookDialog(self.dbstate, self.uistate, self.book, BookOptions) new_paper_name = self.book.get_paper_name() new_orientation = self.book.get_orientation() new_paper_metric = self.book.get_paper_metric() new_custom_paper_size = self.book.get_custom_paper_size() new_margins = self.book.get_margins() new_format_name = self.book.get_format_name() new_output = self.book.get_output() # only books in the booklist have a name (not "ad hoc" ones) if (self.book.get_name() and (old_paper_name != new_paper_name or old_orientation != new_orientation or old_paper_metric != new_paper_metric or old_custom_paper_size != new_custom_paper_size or old_margins != new_margins or old_format_name != new_format_name or old_output != new_output)): self.book.set_dbname(self._db.get_save_path()) self.book_list.set_book(self.book.get_name(), self.book) self.book_list.set_needs_saving(True) if self.book_list.get_needs_saving(): self.book_list.save() else: WarningDialog(_('No items'), _('This book has no items.'), parent=self.window) return self.close() def on_save_clicked(self, obj): """ Save the current book in the xml booklist file. """ if not self.book.get_item_list(): WarningDialog(_('No items'), _('This book has no items.'), parent=self.window) return name = str(self.name_entry.get_text()) if not name: WarningDialog( _('No book name'), _('You are about to save away a book with no name.\n\n' 'Please give it a name before saving it away.'), parent=self.window) return if name in self.book_list.get_book_names(): qqq = QuestionDialog2( _('Book name already exists'), _('You are about to save away a ' 'book with a name which already exists.'), _('Proceed'), _('Cancel'), parent=self.window) if not qqq.run(): return # previously, the same book could be added to the booklist # under multiple names, which became different books once the # booklist was saved into a file so everything was fine, but # this created a problem once the paper settings were added # to the Book object in the BookDialog, since those settings # were retrieved from the Book object in BookList.save, so mutiple # books (differentiated by their names) were assigned the # same paper values, so the solution is to make each Book be # unique in the booklist, so if multiple copies are saved away # only the last one will get the paper values assigned to it # (although when the earlier books are then eventually run, # they'll be assigned paper values also) self.book.set_name(name) self.book.set_dbname(self._db.get_save_path()) self.book_list.set_book(name, self.book) self.book_list.set_needs_saving(True) # user clicked on save self.book = Book(self.book, exact_copy=False) # regenerate old items self.book.set_name(name) self.book.set_dbname(self._db.get_save_path()) def on_open_clicked(self, obj): """ Run the BookListDisplay dialog to present the choice of books to open. """ booklistdisplay = BookListDisplay(self.book_list, nodelete=True, dosave=False, parent=self.window) booklistdisplay.top.destroy() book = booklistdisplay.selection if book: self.open_book(book) self.name_entry.set_text(book.get_name()) self.book.set_name(book.get_name()) def on_edit_clicked(self, obj): """ Run the BookListDisplay dialog to present the choice of books to delete. """ booklistdisplay = BookListDisplay(self.book_list, nodelete=False, dosave=True, parent=self.window) booklistdisplay.top.destroy() book = booklistdisplay.selection if book: self.open_book(book) self.name_entry.set_text(book.get_name()) self.book.set_name(book.get_name())
class BookSelector(ManagedWindow): """ Interface into a dialog setting up the book. Allows the user to add/remove/reorder/setup items for the current book and to clear/load/save/edit whole books. """ def __init__(self, dbstate, uistate): self.db = dbstate.db self.dbstate = dbstate self.uistate = uistate self.title = _('Manage Books') self.file = "books.xml" ManagedWindow.__init__(self, uistate, [], self.__class__) self.xml = Glade('book.glade', toplevel="top") window = self.xml.toplevel title_label = self.xml.get_object('title') self.set_window(window, title_label, self.title) window.show() self.xml.connect_signals({ "on_add_clicked" : self.on_add_clicked, "on_remove_clicked" : self.on_remove_clicked, "on_up_clicked" : self.on_up_clicked, "on_down_clicked" : self.on_down_clicked, "on_setup_clicked" : self.on_setup_clicked, "on_clear_clicked" : self.on_clear_clicked, "on_save_clicked" : self.on_save_clicked, "on_open_clicked" : self.on_open_clicked, "on_edit_clicked" : self.on_edit_clicked, "on_book_ok_clicked" : self.on_book_ok_clicked, "destroy_passed_object" : self.close, # Insert dummy handlers for second top level in the glade file "on_booklist_ok_clicked" : lambda _:None, "on_booklist_delete_clicked": lambda _:None, "on_booklist_cancel_clicked": lambda _:None, "on_booklist_ok_clicked" : lambda _:None, "on_booklist_ok_clicked" : lambda _:None, }) self.avail_tree = self.xml.get_object("avail_tree") self.book_tree = self.xml.get_object("book_tree") self.avail_tree.connect('button-press-event', self.avail_button_press) self.book_tree.connect('button-press-event', self.book_button_press) self.name_entry = self.xml.get_object("name_entry") self.name_entry.set_text(_('New Book')) avail_label = self.xml.get_object('avail_label') avail_label.set_text("<b>%s</b>" % _("_Available items")) avail_label.set_use_markup(True) avail_label.set_use_underline(True) book_label = self.xml.get_object('book_label') book_label.set_text("<b>%s</b>" % _("Current _book")) book_label.set_use_underline(True) book_label.set_use_markup(True) avail_titles = [ (_('Name'), 0, 230), (_('Type'), 1, 80 ), ( '' , -1, 0 ) ] book_titles = [ (_('Item name'), -1, 230), (_('Type'), -1, 80 ), ( '', -1, 0 ), (_('Subject'), -1, 50 ) ] self.avail_nr_cols = len(avail_titles) self.book_nr_cols = len(book_titles) self.avail_model = ListModel(self.avail_tree, avail_titles) self.book_model = ListModel(self.book_tree, book_titles) self.draw_avail_list() self.book = Book() def build_menu_names(self, obj): return (_("Book selection list"), self.title) def draw_avail_list(self): """ Draw the list with the selections available for the book. The selections are read from the book item registry. """ pmgr = GuiPluginManager.get_instance() regbi = pmgr.get_reg_bookitems() if not regbi: return available_reports = [] for pdata in regbi: category = _UNSUPPORTED if pdata.supported and pdata.category in book_categories: category = book_categories[pdata.category] available_reports.append([ pdata.name, category, pdata.id ]) for data in sorted(available_reports): new_iter = self.avail_model.add(data) self.avail_model.connect_model() if new_iter: self.avail_model.selection.select_iter(new_iter) path = self.avail_model.model.get_path(new_iter) col = self.avail_tree.get_column(0) self.avail_tree.scroll_to_cell(path, col, 1, 1, 0.0) def open_book(self, book): """ Open the book: set the current set of selections to this book's items. book: the book object to load. """ if book.get_paper_name(): self.book.set_paper_name(book.get_paper_name()) if book.get_orientation() is not None: # 0 is legal self.book.set_orientation(book.get_orientation()) if book.get_paper_metric() is not None: # 0 is legal self.book.set_paper_metric(book.get_paper_metric()) if book.get_custom_paper_size(): self.book.set_custom_paper_size(book.get_custom_paper_size()) if book.get_margins(): self.book.set_margins(book.get_margins()) if book.get_format_name(): self.book.set_format_name(book.get_format_name()) if book.get_output(): self.book.set_output(book.get_output()) if book.get_dbname() == self.db.get_save_path(): same_db = 1 else: same_db = 0 WarningDialog(_('Different database'), _( 'This book was created with the references to database ' '%s.\n\n This makes references to the central person ' 'saved in the book invalid.\n\n' 'Therefore, the central person for each item is being set ' 'to the active person of the currently opened database.') % book.get_dbname(), parent=self.window) self.book.clear() self.book_model.clear() for saved_item in book.get_item_list(): name = saved_item.get_name() item = BookItem(self.db, name) item.option_class = saved_item.option_class # The option values were loaded magically by the book parser. # But they still need to be applied to the menu options. opt_dict = item.option_class.handler.options_dict menu = item.option_class.menu for optname in opt_dict: menu_option = menu.get_option_by_name(optname) if menu_option: menu_option.set_value(opt_dict[optname]) _initialize_options(item.option_class, self.dbstate, self.uistate) item.set_style_name(saved_item.get_style_name()) self.book.append_item(item) data = [ item.get_translated_name(), item.get_category(), item.get_name() ] data[2] = _get_subject(item.option_class, self.db) self.book_model.add(data) def on_add_clicked(self, obj): """ Add an item to the current selections. Use the selected available item to get the item's name in the registry. """ store, the_iter = self.avail_model.get_selected() if not the_iter: return data = self.avail_model.get_data(the_iter, list(range(self.avail_nr_cols))) item = BookItem(self.db, data[2]) _initialize_options(item.option_class, self.dbstate, self.uistate) data[2] = _get_subject(item.option_class, self.db) self.book_model.add(data) self.book.append_item(item) def on_remove_clicked(self, obj): """ Remove the item from the current list of selections. """ store, the_iter = self.book_model.get_selected() if not the_iter: return row = self.book_model.get_selected_row() self.book.pop_item(row) self.book_model.remove(the_iter) def on_clear_clicked(self, obj): """ Clear the whole current book. """ self.book_model.clear() self.book.clear() def on_up_clicked(self, obj): """ Move the currently selected item one row up in the selection list. """ row = self.book_model.get_selected_row() if not row or row == -1: return store, the_iter = self.book_model.get_selected() data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) self.book_model.remove(the_iter) self.book_model.insert(row-1, data, None, 1) item = self.book.pop_item(row) self.book.insert_item(row-1, item) def on_down_clicked(self, obj): """ Move the currently selected item one row down in the selection list. """ row = self.book_model.get_selected_row() if row + 1 >= self.book_model.count or row == -1: return store, the_iter = self.book_model.get_selected() data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) self.book_model.remove(the_iter) self.book_model.insert(row+1, data, None, 1) item = self.book.pop_item(row) self.book.insert_item(row+1, item) def on_setup_clicked(self, obj): """ Configure currently selected item. """ store, the_iter = self.book_model.get_selected() if not the_iter: WarningDialog(_('No selected book item'), _('Please select a book item to configure.'), parent=self.window) return data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) row = self.book_model.get_selected_row() item = self.book.get_item(row) option_class = item.option_class option_class.handler.set_default_stylesheet_name(item.get_style_name()) item.is_from_saved_book = bool(self.book.get_name()) item_dialog = BookItemDialog(self.dbstate, self.uistate, item, self.track) while True: response = item_dialog.window.run() if response == Gtk.ResponseType.OK: # dialog will be closed by connect, now continue work while # rest of dialog is unresponsive, release when finished style = option_class.handler.get_default_stylesheet_name() item.set_style_name(style) subject = _get_subject(option_class, self.db) self.book_model.model.set_value(the_iter, 2, subject) self.book.set_item(row, item) item_dialog.close() break elif response == Gtk.ResponseType.CANCEL: item_dialog.close() break elif response == Gtk.ResponseType.DELETE_EVENT: #just stop, in ManagedWindow, delete-event is already coupled to #correct action. break def book_button_press(self, obj, event): """ Double-click on the current book selection is the same as setup. Right click evokes the context menu. """ if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: self.on_setup_clicked(obj) elif is_right_click(event): self.build_book_context_menu(event) def avail_button_press(self, obj, event): """ Double-click on the available selection is the same as add. Right click evokes the context menu. """ if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: self.on_add_clicked(obj) elif is_right_click(event): self.build_avail_context_menu(event) def build_book_context_menu(self, event): """Builds the menu with item-centered and book-centered options.""" store, the_iter = self.book_model.get_selected() if the_iter: sensitivity = 1 else: sensitivity = 0 entries = [ (_('_Up'), 'go-up', self.on_up_clicked, sensitivity), (_('_Down'), 'go-down', self.on_down_clicked, sensitivity), (_("Setup"), None, self.on_setup_clicked, sensitivity), (_('_Remove'), 'list-remove', self.on_remove_clicked, sensitivity), ('', None, None, 0), (_('_Clear'), 'edit-clear', self.on_clear_clicked, 1), (_('_Save'), 'document-save', self.on_save_clicked, 1), (_('_Open'), 'document-open', self.on_open_clicked, 1), (_("Edit"), None, self.on_edit_clicked, 1), ] self.menu = Gtk.Menu() self.menu.set_title(_('Book Menu')) for title, icon_name, callback, sensitivity in entries: if icon_name: item = Gtk.ImageMenuItem.new_with_mnemonic(title) img = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU) item.set_image(img) else: item = Gtk.MenuItem.new_with_mnemonic(title) if callback: item.connect("activate", callback) item.set_sensitive(sensitivity) item.show() self.menu.append(item) self.menu.popup(None, None, None, None, event.button, event.time) def build_avail_context_menu(self, event): """Builds the menu with the single Add option.""" store, the_iter = self.avail_model.get_selected() if the_iter: sensitivity = 1 else: sensitivity = 0 entries = [ (_('_Add'), 'list-add', self.on_add_clicked, sensitivity), ] self.menu = Gtk.Menu() self.menu.set_title(_('Available Items Menu')) for title, icon_name, callback, sensitivity in entries: if icon_name: item = Gtk.ImageMenuItem.new_with_mnemonic(title) img = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU) item.set_image(img) else: item = Gtk.MenuItem.new_with_mnemonic(title) if callback: item.connect("activate", callback) item.set_sensitive(sensitivity) item.show() self.menu.append(item) self.menu.popup(None, None, None, None, event.button, event.time) def on_book_ok_clicked(self, obj): """ Run final BookDialog with the current book. """ if self.book.item_list: BookDialog(self.dbstate, self.uistate, self.book, BookOptions) else: WarningDialog(_('No items'), _('This book has no items.'), parent=self.window) return self.close() def on_save_clicked(self, obj): """ Save the current book in the xml booklist file. """ self.book_list = BookList(self.file, self.db) name = str(self.name_entry.get_text()) if not name: WarningDialog(_('No book name'), _( 'You are about to save away a book with no name.\n\n' 'Please give it a name before saving it away.'), parent=self.window) return if name in self.book_list.get_book_names(): from ...dialog import QuestionDialog2 q = QuestionDialog2( _('Book name already exists'), _('You are about to save away a ' 'book with a name which already exists.' ), _('Proceed'), _('Cancel'), parent=self.window) if q.run(): self.book.set_name(name) else: return else: self.book.set_name(name) self.book.set_dbname(self.db.get_save_path()) self.book_list.set_book(name, self.book) self.book_list.save() def on_open_clicked(self, obj): """ Run the BookListDisplay dialog to present the choice of books to open. """ self.book_list = BookList(self.file, self.db) booklistdisplay = BookListDisplay(self.book_list, 1, 0) booklistdisplay.top.destroy() book = booklistdisplay.selection if book: self.open_book(book) self.name_entry.set_text(book.get_name()) self.book.set_name(book.get_name()) def on_edit_clicked(self, obj): """ Run the BookListDisplay dialog to present the choice of books to delete. """ self.book_list = BookList(self.file, self.db) booklistdisplay = BookListDisplay(self.book_list, 0, 1) booklistdisplay.top.destroy() book = booklistdisplay.selection if book: self.open_book(book) self.name_entry.set_text(book.get_name()) self.book.set_name(book.get_name())
class BookSelector(ManagedWindow): """ Interface into a dialog setting up the book. Allows the user to add/remove/reorder/setup items for the current book and to clear/load/save/edit whole books. """ def __init__(self, dbstate, uistate): self.db = dbstate.db self.dbstate = dbstate self.uistate = uistate self.title = _('Book') self.file = "books.xml" ManagedWindow.__init__(self, uistate, [], self.__class__) self.xml = Glade('book.glade', toplevel="top") window = self.xml.toplevel title_label = self.xml.get_object('title') self.set_window(window, title_label, self.title) window.show() self.xml.connect_signals({ "on_add_clicked": self.on_add_clicked, "on_remove_clicked": self.on_remove_clicked, "on_up_clicked": self.on_up_clicked, "on_down_clicked": self.on_down_clicked, "on_setup_clicked": self.on_setup_clicked, "on_clear_clicked": self.on_clear_clicked, "on_save_clicked": self.on_save_clicked, "on_open_clicked": self.on_open_clicked, "on_edit_clicked": self.on_edit_clicked, "on_book_ok_clicked": self.on_book_ok_clicked, "destroy_passed_object": self.close, # Insert dummy handlers for second top level in the glade file "on_booklist_ok_clicked": lambda _: None, "on_booklist_delete_clicked": lambda _: None, "on_booklist_cancel_clicked": lambda _: None, "on_booklist_ok_clicked": lambda _: None, "on_booklist_ok_clicked": lambda _: None, }) self.avail_tree = self.xml.get_object("avail_tree") self.book_tree = self.xml.get_object("book_tree") self.avail_tree.connect('button-press-event', self.avail_button_press) self.book_tree.connect('button-press-event', self.book_button_press) self.name_entry = self.xml.get_object("name_entry") self.name_entry.set_text(_('New Book')) avail_label = self.xml.get_object('avail_label') avail_label.set_text("<b>%s</b>" % _("_Available items")) avail_label.set_use_markup(True) avail_label.set_use_underline(True) book_label = self.xml.get_object('book_label') book_label.set_text("<b>%s</b>" % _("Current _book")) book_label.set_use_underline(True) book_label.set_use_markup(True) avail_titles = [(_('Name'), 0, 230), (_('Type'), 1, 80), ('', -1, 0)] book_titles = [(_('Item name'), -1, 230), (_('Type'), -1, 80), ('', -1, 0), (_('Subject'), -1, 50)] self.avail_nr_cols = len(avail_titles) self.book_nr_cols = len(book_titles) self.avail_model = ListModel(self.avail_tree, avail_titles) self.book_model = ListModel(self.book_tree, book_titles) self.draw_avail_list() self.book = Book() def build_menu_names(self, obj): return (_("Book selection list"), self.title) def draw_avail_list(self): """ Draw the list with the selections available for the book. The selections are read from the book item registry. """ pmgr = GuiPluginManager.get_instance() regbi = pmgr.get_reg_bookitems() if not regbi: return available_reports = [] for pdata in regbi: category = _UNSUPPORTED if pdata.supported and pdata.category in book_categories: category = book_categories[pdata.category] available_reports.append([pdata.name, category, pdata.id]) for data in sorted(available_reports): new_iter = self.avail_model.add(data) self.avail_model.connect_model() if new_iter: self.avail_model.selection.select_iter(new_iter) path = self.avail_model.model.get_path(new_iter) col = self.avail_tree.get_column(0) self.avail_tree.scroll_to_cell(path, col, 1, 1, 0.0) def open_book(self, book): """ Open the book: set the current set of selections to this book's items. book: the book object to load. """ if book.get_paper_name(): self.book.set_paper_name(book.get_paper_name()) if book.get_orientation() is not None: # 0 is legal self.book.set_orientation(book.get_orientation()) if book.get_paper_metric() is not None: # 0 is legal self.book.set_paper_metric(book.get_paper_metric()) if book.get_custom_paper_size(): self.book.set_custom_paper_size(book.get_custom_paper_size()) if book.get_margins(): self.book.set_margins(book.get_margins()) if book.get_format_name(): self.book.set_format_name(book.get_format_name()) if book.get_output(): self.book.set_output(book.get_output()) if book.get_dbname() == self.db.get_save_path(): same_db = 1 else: same_db = 0 WarningDialog( _('Different database'), _('This book was created with the references to database ' '%s.\n\n This makes references to the central person ' 'saved in the book invalid.\n\n' 'Therefore, the central person for each item is being set ' 'to the active person of the currently opened database.') % book.get_dbname(), parent=self.window) self.book.clear() self.book_model.clear() for saved_item in book.get_item_list(): name = saved_item.get_name() item = BookItem(self.db, name) item.option_class = saved_item.option_class # The option values were loaded magically by the book parser. # But they still need to be applied to the menu options. opt_dict = item.option_class.handler.options_dict menu = item.option_class.menu for optname in opt_dict: menu_option = menu.get_option_by_name(optname) if menu_option: menu_option.set_value(opt_dict[optname]) _initialize_options(item.option_class, self.dbstate, self.uistate) item.set_style_name(saved_item.get_style_name()) self.book.append_item(item) data = [ item.get_translated_name(), item.get_category(), item.get_name() ] data[2] = _get_subject(item.option_class, self.db) self.book_model.add(data) def on_add_clicked(self, obj): """ Add an item to the current selections. Use the selected available item to get the item's name in the registry. """ store, the_iter = self.avail_model.get_selected() if not the_iter: return data = self.avail_model.get_data(the_iter, list(range(self.avail_nr_cols))) item = BookItem(self.db, data[2]) _initialize_options(item.option_class, self.dbstate, self.uistate) data[2] = _get_subject(item.option_class, self.db) self.book_model.add(data) self.book.append_item(item) def on_remove_clicked(self, obj): """ Remove the item from the current list of selections. """ store, the_iter = self.book_model.get_selected() if not the_iter: return row = self.book_model.get_selected_row() self.book.pop_item(row) self.book_model.remove(the_iter) def on_clear_clicked(self, obj): """ Clear the whole current book. """ self.book_model.clear() self.book.clear() def on_up_clicked(self, obj): """ Move the currently selected item one row up in the selection list. """ row = self.book_model.get_selected_row() if not row or row == -1: return store, the_iter = self.book_model.get_selected() data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) self.book_model.remove(the_iter) self.book_model.insert(row - 1, data, None, 1) item = self.book.pop_item(row) self.book.insert_item(row - 1, item) def on_down_clicked(self, obj): """ Move the currently selected item one row down in the selection list. """ row = self.book_model.get_selected_row() if row + 1 >= self.book_model.count or row == -1: return store, the_iter = self.book_model.get_selected() data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) self.book_model.remove(the_iter) self.book_model.insert(row + 1, data, None, 1) item = self.book.pop_item(row) self.book.insert_item(row + 1, item) def on_setup_clicked(self, obj): """ Configure currently selected item. """ store, the_iter = self.book_model.get_selected() if not the_iter: WarningDialog(_('No selected book item'), _('Please select a book item to configure.'), parent=self.window) return data = self.book_model.get_data(the_iter, list(range(self.book_nr_cols))) row = self.book_model.get_selected_row() item = self.book.get_item(row) option_class = item.option_class option_class.handler.set_default_stylesheet_name(item.get_style_name()) item.is_from_saved_book = bool(self.book.get_name()) item_dialog = BookItemDialog(self.dbstate, self.uistate, item, self.track) while True: response = item_dialog.window.run() if response == Gtk.ResponseType.OK: # dialog will be closed by connect, now continue work while # rest of dialog is unresponsive, release when finished style = option_class.handler.get_default_stylesheet_name() item.set_style_name(style) subject = _get_subject(option_class, self.db) self.book_model.model.set_value(the_iter, 2, subject) self.book.set_item(row, item) item_dialog.close() break elif response == Gtk.ResponseType.CANCEL: item_dialog.close() break elif response == Gtk.ResponseType.DELETE_EVENT: #just stop, in ManagedWindow, delete-event is already coupled to #correct action. break def book_button_press(self, obj, event): """ Double-click on the current book selection is the same as setup. Right click evokes the context menu. """ if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: self.on_setup_clicked(obj) elif is_right_click(event): self.build_book_context_menu(event) def avail_button_press(self, obj, event): """ Double-click on the available selection is the same as add. Right click evokes the context menu. """ if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: self.on_add_clicked(obj) elif is_right_click(event): self.build_avail_context_menu(event) def build_book_context_menu(self, event): """Builds the menu with item-centered and book-centered options.""" store, the_iter = self.book_model.get_selected() if the_iter: sensitivity = 1 else: sensitivity = 0 entries = [ (Gtk.STOCK_GO_UP, self.on_up_clicked, sensitivity), (Gtk.STOCK_GO_DOWN, self.on_down_clicked, sensitivity), (_("Setup"), self.on_setup_clicked, sensitivity), (Gtk.STOCK_REMOVE, self.on_remove_clicked, sensitivity), (None, None, 0), (Gtk.STOCK_CLEAR, self.on_clear_clicked, 1), (Gtk.STOCK_SAVE, self.on_save_clicked, 1), (Gtk.STOCK_OPEN, self.on_open_clicked, 1), (_("Edit"), self.on_edit_clicked, 1), ] menu = Gtk.Menu() menu.set_title(_('Book Menu')) for stock_id, callback, sensitivity in entries: item = Gtk.ImageMenuItem(stock_id) if callback: item.connect("activate", callback) item.set_sensitive(sensitivity) item.show() menu.append(item) menu.popup(None, None, None, None, event.button, event.time) def build_avail_context_menu(self, event): """Builds the menu with the single Add option.""" store, the_iter = self.avail_model.get_selected() if the_iter: sensitivity = 1 else: sensitivity = 0 entries = [ (Gtk.STOCK_ADD, self.on_add_clicked, sensitivity), ] menu = Gtk.Menu() menu.set_title(_('Available Items Menu')) for stock_id, callback, sensitivity in entries: item = Gtk.ImageMenuItem(stock_id) if callback: item.connect("activate", callback) item.set_sensitive(sensitivity) item.show() menu.append(item) menu.popup(None, None, None, None, event.button, event.time) def on_book_ok_clicked(self, obj): """ Run final BookDialog with the current book. """ if self.book.item_list: BookDialog(self.dbstate, self.uistate, self.book, BookOptions) else: WarningDialog(_('No items'), _('This book has no items.'), parent=self.window) return self.close() def on_save_clicked(self, obj): """ Save the current book in the xml booklist file. """ self.book_list = BookList(self.file, self.db) name = str(self.name_entry.get_text()) if not name: WarningDialog( _('No book name'), _('You are about to save away a book with no name.\n\n' 'Please give it a name before saving it away.'), parent=self.window) return if name in self.book_list.get_book_names(): from ...dialog import QuestionDialog2 q = QuestionDialog2(_('Book name already exists'), _('You are about to save away a ' 'book with a name which already exists.'), _('Proceed'), _('Cancel'), parent=self.window) if q.run(): self.book.set_name(name) else: return else: self.book.set_name(name) self.book.set_dbname(self.db.get_save_path()) self.book_list.set_book(name, self.book) self.book_list.save() def on_open_clicked(self, obj): """ Run the BookListDisplay dialog to present the choice of books to open. """ self.book_list = BookList(self.file, self.db) booklistdisplay = BookListDisplay(self.book_list, 1, 0) booklistdisplay.top.destroy() book = booklistdisplay.selection if book: self.open_book(book) self.name_entry.set_text(book.get_name()) self.book.set_name(book.get_name()) def on_edit_clicked(self, obj): """ Run the BookListDisplay dialog to present the choice of books to delete. """ self.book_list = BookList(self.file, self.db) booklistdisplay = BookListDisplay(self.book_list, 0, 1) booklistdisplay.top.destroy() book = booklistdisplay.selection if book: self.open_book(book) self.name_entry.set_text(book.get_name()) self.book.set_name(book.get_name())