📄 addressbook.cpp
字号:
/* Copyright (C) 2001 2002 Chris VineThis program is distributed under the General Public Licence, version 2.For particulars of this and relevant disclaimers see the fileCOPYING distributed with the source files.*/#include <string>#include <fstream>#include <cstdlib>#include <gtkmm/main.h>#include <gdkmm/pixbuf.h>#include <gtkmm/image.h>#include <gdk/gdkkeysyms.h> // the key codes are here#include <gtkmm/stock.h>#include <glibmm/convert.h>#include "addressbook.h"#include "dialogs.h"#include "addressbook_icons.h"#ifdef ENABLE_NLS#include <libintl.h>#endif#define ADDRESS_FILE ".efax-gtk_addressbook"#define ADDRESS_DIVIDER 3int AddressBook::is_address_list = 0;AddressBook::AddressBook(const int size, Gtk::Window& window): Gtk::Window(Gtk::WINDOW_TOPLEVEL), standard_size(size), parent(window), in_exec_loop(false), table(2, 1, false), list_table(5, 2, false), ok_button(Gtk::Stock::OK), cancel_button(Gtk::Stock::CANCEL), button_box(Gtk::BUTTONBOX_END, standard_size/2) { // notify the existence of this object in case I later decide to use this dialog as modeless // by omitting the set_modal() call below is_address_list++; address_list_scroll_window.set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS); address_list_scroll_window.add(tree_view); address_list_scroll_window.set_shadow_type(Gtk::SHADOW_IN); // create the tree model: list_store_r = Gtk::ListStore::create(model_columns); // connect it to the tree view tree_view.set_model(list_store_r); // add the columns of the tree model to tree view tree_view.append_column(gettext("Name"), model_columns.name); tree_view.append_column(gettext("Number"), model_columns.number); // single line selection tree_view.get_selection()->set_mode(Gtk::SELECTION_SINGLE); // make drag and drop work tree_view.set_reorderable(true); // populate the address list read_list(); list_store_r->signal_row_deleted().connect(sigc::mem_fun(*this, &AddressBook::drag_n_drop_slot), true); Gtk::Image* image_p; image_p = manage(new Gtk::Image(Gdk::Pixbuf::create_from_xpm_data(add_xpm))); add_button.add(*image_p); image_p = manage(new Gtk::Image(Gdk::Pixbuf::create_from_xpm_data(delete_xpm))); delete_button.add(*image_p); image_p = manage(new Gtk::Image(Gdk::Pixbuf::create_from_xpm_data(up_arrow_xpm))); up_button.add(*image_p); image_p = manage(new Gtk::Image(Gdk::Pixbuf::create_from_xpm_data(down_arrow_xpm))); down_button.add(*image_p); Gtk::Label* dummy_p = manage(new Gtk::Label); list_table.attach(add_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, standard_size/2, 0); list_table.attach(delete_button, 0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK, standard_size/2, 0); list_table.attach(up_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK, standard_size/2, 0); list_table.attach(down_button, 0, 1, 3, 4, Gtk::SHRINK, Gtk::SHRINK, standard_size/2, 0); list_table.attach(*dummy_p, 0, 1, 4, 5, Gtk::SHRINK, Gtk::EXPAND, 0, 0); list_table.attach(address_list_scroll_window, 1, 2, 0, 5, Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL, 0, 0); tooltips.set_tip(add_button, gettext("Add new address")); tooltips.set_tip(delete_button, gettext("Delete address")); tooltips.set_tip(up_button, gettext("Move address up")); tooltips.set_tip(down_button, gettext("Move address down")); button_box.add(cancel_button); button_box.add(ok_button); table.attach(list_table, 0, 1, 0, 1, Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL, standard_size/3, standard_size/3); table.attach(button_box, 0, 1, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK, standard_size/3, standard_size/3); add_button.signal_clicked().connect(sigc::mem_fun(*this, &AddressBook::add_address_prompt)); delete_button.signal_clicked().connect(sigc::mem_fun(*this, &AddressBook::delete_address_prompt)); ok_button.signal_clicked().connect(sigc::mem_fun(*this, &AddressBook::ok_slot)); cancel_button.signal_clicked().connect(sigc::mem_fun(*this, &AddressBook::finish)); up_button.signal_clicked().connect(sigc::mem_fun(*this, &AddressBook::move_up)); down_button.signal_clicked().connect(sigc::mem_fun(*this, &AddressBook::move_down)); ok_button.set_flags(Gtk::CAN_DEFAULT); cancel_button.set_flags(Gtk::CAN_DEFAULT); add_button.unset_flags(Gtk::CAN_FOCUS); delete_button.unset_flags(Gtk::CAN_FOCUS); up_button.unset_flags(Gtk::CAN_FOCUS); down_button.unset_flags(Gtk::CAN_FOCUS); table.set_border_width(standard_size/3); set_title(gettext("efax-gtk: Address book")); add(table); set_transient_for(parent); set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); parent.set_sensitive(false); set_modal(true); set_position(Gtk::WIN_POS_CENTER_ON_PARENT); cancel_button.grab_focus(); set_icon(prog_config.window_icon_r); set_default_size(standard_size * 15, standard_size * 8); show_all();}AddressBook::~AddressBook(void) { // notify the destruction of this object is_address_list--;}Glib::ustring AddressBook::exec(void) { in_exec_loop = true; Gtk::Main::run(); return result;}void AddressBook::ok_slot(void) { result = get_number(); if (!result.empty()) { accepted(result); finish(); } else beep();}void AddressBook::finish(void) { parent.set_sensitive(true); hide_all(); if (in_exec_loop) Gtk::Main::quit(); // if we have not called exec(), then this dialog is self-owning and it is safe to call `delete this' else delete this;}bool AddressBook::on_delete_event(GdkEventAny*) { finish(); return true; // returning true prevents destroy sig being emitted}Glib::ustring AddressBook::get_number(void) { Glib::ustring number; Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected(); if (row_iter) { number = (*row_iter)[model_columns.number]; } return number;}void AddressBook::add_address_prompt(void) { AddressDialog* dialog_p = new AddressDialog(standard_size, *this); dialog_p->accepted.connect(sigc::mem_fun(*this, &AddressBook::add_address)); // there is no memory leak -- AddressDailog will delete its own memory // when it is closed}void AddressBook::add_address(const std::vector<Glib::ustring>& address) { Gtk::TreeModel::Row row = *(list_store_r->append()); row[model_columns.name] = address[0]; row[model_columns.number] = address[1]; save_list();}void AddressBook::delete_address_prompt(void) { Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected(); if (row_iter) { PromptDialog* dialog_p = new PromptDialog(gettext("Delete selected address?"), gettext("Delete address"), standard_size, *this); dialog_p->accepted.connect(sigc::bind(sigc::mem_fun(*this, &AddressBook::delete_address), row_iter)); // there is no memory leak -- the memory will be deleted when PromptDialog closes } else beep();}void AddressBook::delete_address(Gtk::TreeModel::iterator row_iter) { // delete the address by removing it from the list store list_store_r->erase(row_iter); save_list();}void AddressBook::read_list(void) { std::string filename(prog_config.homedir); filename += "/" ADDRESS_FILE;#ifdef HAVE_IOS_NOCREATE std::ifstream filein(filename.c_str(), std::ios::in | std::ios::nocreate);#else // we must have Std C++ so we probably don't need a ios::nocreate // flag on a read open to ensure uniqueness std::ifstream filein(filename.c_str(), std::ios::in);#endif if (filein) { list_store_r->clear(); std::string line; while (std::getline(filein, line)) { if (!line.empty()) { std::string::size_type pos = line.find(ADDRESS_DIVIDER, 0); // pos now is set to end of name value // get a list store row to insert the number and name Gtk::TreeModel::Row row = *(list_store_r->append()); try { row[model_columns.name] = Glib::locale_to_utf8(line.substr(0, pos)); } catch (Glib::ConvertError&) { write_error("UTF-8 conversion error in AddressBook::read_list()\n"); } pos++; // pos now is set to the beginning of the number value try { row[model_columns.number] = Glib::locale_to_utf8(line.substr(pos, line.size() - pos)); } catch (Glib::ConvertError&) { write_error("UTF-8 conversion error in AddressBook::read_list()\n"); } } } }}void AddressBook::save_list(void) { std::string filename(prog_config.homedir); filename += "/" ADDRESS_FILE; std::ofstream fileout(filename.c_str(), std::ios::out); if (fileout) { std::string line; Gtk::TreeModel::Children children = list_store_r->children(); Gtk::TreeModel::Children::iterator row_iter; for(row_iter = children.begin(); row_iter != children.end(); ++row_iter) { // the try()/catch() blocks here are ultra cautious - something must be // seriously wrong if model_columns.name and model_columns.number are not // in valid UTF-8 format, since they are tested where necessary at input try { line = Glib::locale_from_utf8((*row_iter)[model_columns.name]); line += ADDRESS_DIVIDER; line += Glib::locale_from_utf8((*row_iter)[model_columns.number]); line += '\n'; fileout << line; } catch (Glib::ConvertError&) { write_error("UTF-8 conversion error in AddressBook::save_list()\n"); } } }}void AddressBook::drag_n_drop_slot(const Gtk::TreeModel::Path&) { save_list();}void AddressBook::move_up(void) { Gtk::TreeModel::iterator selected_row_iter = tree_view.get_selection()->get_selected(); if (selected_row_iter && selected_row_iter != list_store_r->children().begin()) { Gtk::TreeModel::Path tree_path(selected_row_iter); tree_path.prev(); // tree_path now refers to the row of the list store // preceding the selected one Gtk::TreeModel::iterator prev_row_iter = list_store_r->get_iter(tree_path); Glib::ustring selected_name = (*selected_row_iter)[model_columns.name]; Glib::ustring selected_number = (*selected_row_iter)[model_columns.number]; Glib::ustring prev_name = (*prev_row_iter)[model_columns.name]; Glib::ustring prev_number = (*prev_row_iter)[model_columns.number]; (*selected_row_iter)[model_columns.name] = prev_name; (*selected_row_iter)[model_columns.number] = prev_number; (*prev_row_iter)[model_columns.name] = selected_name; (*prev_row_iter)[model_columns.number] = selected_number; tree_view.get_selection()->select(prev_row_iter); save_list(); } else beep();}void AddressBook::move_down(void) { Gtk::TreeModel::iterator selected_iter = tree_view.get_selection()->get_selected(); if (selected_iter) { Gtk::TreeModel::iterator succeding_iter = selected_iter; ++succeding_iter; if (succeding_iter != list_store_r->children().end()) { Glib::ustring selected_name = (*selected_iter)[model_columns.name]; Glib::ustring selected_number = (*selected_iter)[model_columns.number]; Glib::ustring succeding_name = (*succeding_iter)[model_columns.name]; Glib::ustring succeding_number = (*succeding_iter)[model_columns.number]; (*selected_iter)[model_columns.name] = succeding_name; (*selected_iter)[model_columns.number] = succeding_number; (*succeding_iter)[model_columns.name] = selected_name; (*succeding_iter)[model_columns.number] = selected_number; tree_view.get_selection()->select(succeding_iter); save_list(); } else beep(); } else beep();}AddressDialog::AddressDialog(const int standard_size, Gtk::Window& window): Gtk::Window(Gtk::WINDOW_TOPLEVEL), in_exec_loop(false), ok_button(Gtk::Stock::OK), cancel_button(Gtk::Stock::CANCEL), button_box(Gtk::BUTTONBOX_END, standard_size/2), name_label(gettext("Name:")), number_label(gettext("Number:")), table(3, 1, false), parent(window) { name_label.set_size_request(standard_size * 2, standard_size); number_label.set_size_request(standard_size * 2, standard_size); name_entry.set_size_request(standard_size * 8, standard_size); number_entry.set_size_request(standard_size * 8, standard_size); name_box.pack_start(name_label, false, false, standard_size/2); name_box.pack_start(name_entry, true, true, 0); number_box.pack_start(number_label, false, false, standard_size/2); number_box.pack_start(number_entry, true, true, 0); button_box.add(cancel_button); button_box.add(ok_button); table.attach(name_box, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL | Gtk::EXPAND, standard_size/2, standard_size/4); table.attach(number_box, 0, 1, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::FILL | Gtk::EXPAND, standard_size/2, standard_size/4); table.attach(button_box, 0, 1, 2, 3, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK, standard_size/2, standard_size/4); ok_button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &AddressDialog::selected), true)); cancel_button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &AddressDialog::selected), false)); add(table); set_title(gettext("Add Address")); set_transient_for(parent); set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); parent.set_sensitive(false); set_modal(true); set_border_width(standard_size/2); name_entry.grab_focus(); set_position(Gtk::WIN_POS_CENTER_ON_PARENT); set_resizable(false); set_icon(prog_config.window_icon_r); show_all();}void AddressDialog::exec(void) { in_exec_loop = true; Gtk::Main::run();}void AddressDialog::selected(bool accept) { // check pre-conditions to closing the dialog if (accept && (!name_entry.get_text_length() || !number_entry.get_text_length())) beep(); else { parent.set_sensitive(true); // do this before we emit accepted() hide_all(); if (accept) { std::vector<Glib::ustring> out_val; out_val.push_back(name_entry.get_text()); out_val.push_back(number_entry.get_text()); accepted(out_val); } if (in_exec_loop) Gtk::Main::quit(); // if we have not called exec(), then this dialog is self-owning and it is safe to call `delete this' else delete this; }}bool AddressDialog::on_delete_event(GdkEventAny*) { selected(false); return true; // returning true prevents destroy sig being emitted}bool AddressDialog::on_key_press_event(GdkEventKey* event_p) { if (event_p->keyval == GDK_Escape) selected(false); else if (event_p->keyval == GDK_Return && !cancel_button.has_focus()) selected(true); else Gtk::Window::on_key_press_event(event_p); return true; // processing ends here}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -