~ubuntu-branches/ubuntu/trusty/gnote/trusty-proposed

« back to all changes in this revision

Viewing changes to src/recentchanges.cpp

  • Committer: Package Import Robot
  • Author(s): Vincent Cheng
  • Date: 2013-05-26 04:29:32 UTC
  • mfrom: (1.3.16) (19.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20130526042932-g4zl8zuunan3rm6f
Tags: 3.8.1-1
* Upload to unstable.
* New upstream release.
* Add build dependency on:
  - libgtkspell3-3-dev (for spell checking support)
  - desktop-file-utils (for desktop-file-validate)
* Bump build dependency required versions for autoconf (>= 2.5.9),
  libgtk-3-dev (>= 3.6), libgtkmm-3.0-dev (>= 3.6).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 * gnote
3
3
 *
4
 
 * Copyright (C) 2010-2012 Aurimas Cernius
 
4
 * Copyright (C) 2010-2013 Aurimas Cernius
5
5
 * Copyright (C) 2010 Debarshi Ray
6
6
 * Copyright (C) 2009 Hubert Figuiere
7
7
 *
23
23
#include <config.h>
24
24
#endif
25
25
 
26
 
#include <boost/format.hpp>
27
 
 
 
26
#include <boost/bind.hpp>
28
27
#include <glibmm/i18n.h>
 
28
#include <gtkmm/alignment.h>
29
29
#include <gtkmm/image.h>
30
 
#include <gtkmm/linkbutton.h>
31
 
#include <gtkmm/main.h>
 
30
#include <gtkmm/separatormenuitem.h>
32
31
#include <gtkmm/stock.h>
33
 
#include <gtkmm/table.h>
34
 
#include <gtkmm/treestore.h>
35
32
 
36
 
#include "sharp/datetime.hpp"
37
 
#include "sharp/string.hpp"
38
 
#include "actionmanager.hpp"
39
33
#include "debug.hpp"
40
 
#include "gnote.hpp"
 
34
#include "iactionmanager.hpp"
 
35
#include "ignote.hpp"
 
36
#include "iconmanager.hpp"
41
37
#include "note.hpp"
42
38
#include "notemanager.hpp"
43
39
#include "notewindow.hpp"
44
40
#include "recentchanges.hpp"
45
 
#include "recenttreeview.hpp"
46
 
#include "search.hpp"
47
 
#include "tagmanager.hpp"
48
 
#include "utils.hpp"
49
 
#include "notebooks/notebookmanager.hpp"
50
 
#include "notebooks/notebookstreeview.hpp"
 
41
#include "sharp/string.hpp"
51
42
 
52
43
 
53
44
namespace gnote {
54
45
 
55
 
  bool        NoteRecentChanges::s_static_inited = false;
56
 
  Glib::RefPtr<Gdk::Pixbuf> NoteRecentChanges::s_note_icon;
57
 
  Glib::RefPtr<Gdk::Pixbuf> NoteRecentChanges::s_all_notes_icon;
58
 
  Glib::RefPtr<Gdk::Pixbuf> NoteRecentChanges::s_unfiled_notes_icon;
59
 
  Glib::RefPtr<Gdk::Pixbuf> NoteRecentChanges::s_notebook_icon;
60
 
  std::list<std::string>    NoteRecentChanges::s_previous_searches;
61
 
  NoteRecentChanges        *NoteRecentChanges::s_instance = NULL;
62
 
 
63
 
 
64
 
 
65
 
  NoteRecentChanges *NoteRecentChanges::get_instance()
66
 
  {
67
 
    return s_instance;
68
 
  }
69
 
 
70
 
 
71
 
  NoteRecentChanges *NoteRecentChanges::get_instance(NoteManager& m)
72
 
  {
73
 
    if(!s_instance) {
74
 
      s_instance = new NoteRecentChanges(m);
75
 
    }
76
 
    return s_instance;
77
 
  }
78
 
 
79
 
 
80
 
  void NoteRecentChanges::_init_static()
81
 
  {
82
 
    if(s_static_inited) {
83
 
      return;
84
 
    }
85
 
    s_note_icon = utils::get_icon ("note", 22);
86
 
    s_all_notes_icon = utils::get_icon ("filter-note-all", 22);
87
 
    s_unfiled_notes_icon = utils::get_icon ("filter-note-unfiled", 22);
88
 
    s_notebook_icon = utils::get_icon ("notebook", 22);
89
 
    s_static_inited = true;
90
 
  }
91
 
 
92
46
  NoteRecentChanges::NoteRecentChanges(NoteManager& m)
93
 
    : utils::ForcedPresentWindow(_("Search All Notes"))
94
 
    , m_manager(m)
95
 
    , m_menubar(NULL)
96
 
    , m_find_combo(Glib::RefPtr<Gtk::TreeModel>::cast_static(Gtk::ListStore::create(m_find_combo_columns)), true)
97
 
    , m_clear_search_button(Gtk::Stock::CLEAR)
 
47
    : MainWindow(_("Notes"))
 
48
    , m_note_manager(m)
 
49
    , m_search_notes_widget(m)
98
50
    , m_content_vbox(false, 0)
99
 
    , m_matches_column(NULL)
100
 
    , m_no_matches_box(NULL)
101
 
    , m_tree(NULL)
 
51
    , m_mapped(false)
102
52
    , m_entry_changed_timeout(NULL)
103
 
    , m_clickX(0), m_clickY(0)
 
53
    , m_window_menu_search(NULL)
 
54
    , m_window_menu_note(NULL)
 
55
    , m_window_menu_default(NULL)
104
56
  {
105
 
    Gnote::obj().add_window(this);
106
 
    _init_static();
107
 
//    get_window()->set_icon_name("gnote");
108
57
    set_default_size(450,400);
109
58
    set_resizable(true);
110
 
 
111
 
    add_accel_group(ActionManager::obj().get_ui()->get_accel_group());
112
 
 
113
 
    m_menubar = create_menu_bar ();
114
 
 
115
 
    Gtk::Label *label = manage(new Gtk::Label (_("_Search:"), true));
116
 
    label->property_xalign() = 1;
117
 
 
118
 
    label->set_mnemonic_widget(m_find_combo);
119
 
    m_find_combo.set_entry_text_column(0);
120
 
    m_find_combo.signal_changed()
121
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_entry_changed));
122
 
    m_find_combo.get_entry()->set_activates_default(false);
123
 
    m_find_combo.get_entry()->signal_activate()
124
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_entry_activated));
125
 
 
126
 
    Glib::RefPtr<Gtk::ListStore> model 
127
 
      = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(m_find_combo.get_model());
128
 
    for(std::list<std::string>::const_iterator liter = s_previous_searches.begin();
129
 
        liter != s_previous_searches.end(); ++liter) {
130
 
      Gtk::TreeIter iter = model->append();
131
 
      iter->set_value(0, *liter);
 
59
    set_hide_titlebar_when_maximized(true);
 
60
    if(Preferences::obj().get_schema_settings(Preferences::SCHEMA_GNOTE)->get_boolean(
 
61
         Preferences::MAIN_WINDOW_MAXIMIZED)) {
 
62
      maximize();
132
63
    }
133
64
 
134
 
    m_clear_search_button.set_sensitive(false);
135
 
    m_clear_search_button.signal_clicked()
136
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::clear_search_clicked));
137
 
    m_clear_search_button.show ();
138
 
 
139
 
    Gtk::Table *table = manage(new Gtk::Table (2, 3, false));
140
 
    table->attach (*label, 0, 1, 0, 1, Gtk::SHRINK, (Gtk::AttachOptions)0, 0, 0);
141
 
    table->attach (m_find_combo, 1, 2, 0, 1);
142
 
    table->attach (m_clear_search_button,
143
 
                  2, 3, 0, 1,
144
 
                  Gtk::SHRINK, (Gtk::AttachOptions)0, 0, 0);
145
 
    table->property_column_spacing() = 4;
146
 
    table->show_all ();
147
 
 
148
 
    Gtk::HBox *hbox = manage(new Gtk::HBox (false, 2));
149
 
    hbox->pack_start (*table, true, true, 0);
150
 
    hbox->show_all ();
151
 
 
152
 
    // Notebooks Pane
153
 
    Gtk::Widget *notebooksPane = Gtk::manage(make_notebooks_pane ());
154
 
    notebooksPane->show ();
155
 
                    
156
 
    make_recent_tree ();
157
 
    m_tree = manage(m_tree);
158
 
    m_tree->show ();
159
 
 
160
65
    set_has_resize_grip(true);
161
 
    m_status_bar.show();
162
 
 
163
 
    // Update on changes to notes
164
 
    m.signal_note_deleted.connect(sigc::mem_fun(*this, &NoteRecentChanges::on_note_deleted));
165
 
    m.signal_note_added.connect(sigc::mem_fun(*this, &NoteRecentChanges::on_note_added));
166
 
    m.signal_note_renamed.connect(sigc::mem_fun(*this, &NoteRecentChanges::on_note_renamed));
167
 
    m.signal_note_saved.connect(sigc::mem_fun(*this, &NoteRecentChanges::on_note_saved));
168
 
 
169
 
    // List all the current notes
170
 
    update_results ();
171
 
 
172
 
    m_matches_window.set_shadow_type(Gtk::SHADOW_IN);
173
 
 
174
 
    m_matches_window.property_hscrollbar_policy() = Gtk::POLICY_AUTOMATIC;
175
 
    m_matches_window.property_vscrollbar_policy() = Gtk::POLICY_AUTOMATIC;
176
 
    m_matches_window.add (*m_tree);
177
 
    m_matches_window.show ();
178
 
 
179
 
    m_hpaned.set_position(150);
180
 
    m_hpaned.add1 (*notebooksPane);
181
 
    m_hpaned.add2 (m_matches_window);
182
 
    m_hpaned.show ();
183
 
 
184
 
    restore_position ();
185
 
 
186
 
    Gtk::VBox *vbox = manage(new Gtk::VBox (false, 8));
187
 
    vbox->set_border_width(6);
188
 
    vbox->pack_start (*hbox, false, false, 0);
189
 
    vbox->pack_start (m_hpaned, true, true, 0);
190
 
    vbox->pack_start (m_status_bar, false, false, 0);
191
 
    vbox->show ();
192
 
 
193
 
    // Use another VBox to place the MenuBar
194
 
    // right at thetop of the window.
195
 
    m_content_vbox.pack_start (*manage(m_menubar), false, false, 0);
196
 
    m_content_vbox.pack_start (*vbox, true, true, 0);
 
66
 
 
67
    m_search_notes_widget.signal_open_note
 
68
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_open_note));
 
69
    m_search_notes_widget.signal_open_note_new_window
 
70
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_open_note_new_window));
 
71
 
 
72
    Gtk::Box *toolbar = make_toolbar();
 
73
    m_content_vbox.pack_start(*toolbar, false, false, 0);
 
74
    m_content_vbox.pack_start(m_embed_box, true, true, 0);
 
75
    m_embed_box.show();
197
76
    m_content_vbox.show ();
198
77
 
199
78
    add (m_content_vbox);
200
79
    signal_delete_event().connect(sigc::mem_fun(*this, &NoteRecentChanges::on_delete));
201
80
    signal_key_press_event()
202
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_key_pressed)); // For Escape
203
 
                        
204
 
    // Watch when notes are added to notebooks so the search
205
 
    // results will be updated immediately instead of waiting
206
 
    // until the note's queue_save () kicks in.
207
 
    notebooks::NotebookManager::instance().signal_note_added_to_notebook()
208
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_note_added_to_notebook));
209
 
    notebooks::NotebookManager::instance().signal_note_removed_from_notebook()
210
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_note_removed_from_notebook));
211
 
 
212
 
    // Set the focus chain for the top-most containers
213
 
    std::vector<Gtk::Widget*> focus_chain;
214
 
    focus_chain.push_back(hbox);
215
 
    focus_chain.push_back(&m_hpaned);
216
 
    vbox->set_focus_chain(focus_chain);
217
 
 
218
 
    // Set focus chain for sub widgets of first top-most container
219
 
    focus_chain.clear();
220
 
    focus_chain.push_back(&m_find_combo);
221
 
    focus_chain.push_back(&m_matches_window);
222
 
    hbox->set_focus_chain(focus_chain);
223
 
 
224
 
    // set focus chain for sub widgets of second top-most container
225
 
    focus_chain.clear();
226
 
    focus_chain.push_back(&m_matches_window);
227
 
    focus_chain.push_back(notebooksPane);
228
 
    m_hpaned.set_focus_chain(focus_chain);
229
 
 
230
 
    // get back to the beginning of the focus chain
231
 
    focus_chain.clear();
232
 
    focus_chain.push_back(m_tree);
233
 
    m_matches_window.set_focus_chain(focus_chain);
234
 
                        
235
 
    Gnote::obj().signal_quit.connect(sigc::mem_fun(*this, &NoteRecentChanges::on_exiting_event));
236
 
 
 
81
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_key_pressed));
 
82
    IGnote::obj().signal_quit
 
83
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_close_window));// to save size/pos
 
84
 
 
85
    embed_widget(m_search_notes_widget);
 
86
 
 
87
    IActionManager::obj().signal_main_window_search_actions_changed
 
88
      .connect(boost::bind(sigc::mem_fun(*this, &NoteRecentChanges::on_main_window_actions_changed),
 
89
                           &m_window_menu_search));
 
90
    IActionManager::obj().signal_main_window_note_actions_changed
 
91
      .connect(boost::bind(sigc::mem_fun(*this, &NoteRecentChanges::on_main_window_actions_changed),
 
92
                           &m_window_menu_note));
237
93
  }
238
94
 
239
95
 
240
96
  NoteRecentChanges::~NoteRecentChanges()
241
97
  {
 
98
    while(m_embedded_widgets.size()) {
 
99
      unembed_widget(**m_embedded_widgets.begin());
 
100
    }
242
101
    if(m_entry_changed_timeout) {
243
102
      delete m_entry_changed_timeout;
244
103
    }
245
 
    Gnote::obj().remove_window(this);
246
 
  }
247
 
 
248
 
 
249
 
  Gtk::MenuBar *NoteRecentChanges::create_menu_bar ()
250
 
  {
251
 
    ActionManager &am(ActionManager::obj());
252
 
    Gtk::MenuBar *menubar = dynamic_cast<Gtk::MenuBar*>(am.get_widget ("/MainWindowMenubar"));
253
 
 
254
 
    am ["OpenNoteAction"]->signal_activate()
255
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_open_note));
256
 
    am ["DeleteNoteAction"]->signal_activate()
257
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_delete_note));
258
 
    // from notebook addin
259
 
    am ["NewNotebookNoteAction"]->signal_activate()
260
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_new_notebook_note));
261
 
    am ["OpenNotebookTemplateNoteAction"]->signal_activate()
262
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_open_notebook_template_note));
263
 
    am ["NewNotebookAction"]->signal_activate()
264
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_new_notebook));
265
 
    am ["DeleteNotebookAction"]->signal_activate()
266
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_delete_notebook));
267
 
    // end notebook addin
268
 
    am ["CloseWindowAction"]->signal_activate()
269
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_close_window));
270
 
    if (Gnote::obj().tray_icon_showing() == false)
271
 
      am ["CloseWindowAction"]->set_visible(false);
272
 
 
273
 
    // Allow Escape to close the window as well as <Control>W
274
 
    // Should be able to add Escape to the CloseAction.  Can't do that
275
 
    // until someone fixes AccelGroup.Connect:
276
 
    //     http://bugzilla.ximian.com/show_bug.cgi?id=76988)
277
 
    //
278
 
    // am.UI.AccelGroup.Connect ((uint) Gdk.Key.Escape,
279
 
    //   0,
280
 
    //   Gtk::AccelFlags.Mask,
281
 
    //   OnCloseWindow);
282
 
 
283
 
    return menubar;
284
 
  }
285
 
 
286
 
  Gtk::Widget *NoteRecentChanges::make_notebooks_pane()
287
 
  {
288
 
    m_notebooksTree = Gtk::manage(
289
 
      new notebooks::NotebooksTreeView (notebooks::NotebookManager::instance()
290
 
                                        .get_notebooks_with_special_items()));
291
 
 
292
 
    m_notebooksTree->get_selection()->set_mode(Gtk::SELECTION_SINGLE);
293
 
    m_notebooksTree->set_headers_visible(true);
294
 
    m_notebooksTree->set_rules_hint(false);
295
 
      
296
 
    Gtk::CellRenderer *renderer;
297
 
      
298
 
    Gtk::TreeViewColumn *column = manage(new Gtk::TreeViewColumn ());
299
 
    column->set_title(_("Notebooks"));
300
 
    column->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
301
 
    column->set_resizable(false);
302
 
      
303
 
    renderer = manage(new Gtk::CellRendererPixbuf ());
304
 
    column->pack_start (*renderer, false);
305
 
    column->set_cell_data_func (*renderer,
306
 
                                sigc::mem_fun(*this, &NoteRecentChanges::notebook_pixbuf_cell_data_func));
307
 
      
308
 
    Gtk::CellRendererText *text_renderer = manage(new Gtk::CellRendererText ());
309
 
    text_renderer->property_editable() = true;
310
 
    column->pack_start (*text_renderer, true);
311
 
    column->set_cell_data_func (*text_renderer,
312
 
                                sigc::mem_fun(*this, &NoteRecentChanges::notebook_text_cell_data_func));
313
 
    text_renderer->signal_edited().connect(sigc::mem_fun(*this, &NoteRecentChanges::on_notebook_row_edited));
314
 
 
315
 
    m_notebooksTree->append_column (*column);
316
 
 
317
 
    m_notebooksTree->signal_row_activated()
318
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_notebook_row_activated));
319
 
    m_on_notebook_selection_changed_cid = m_notebooksTree->get_selection()->signal_changed()
320
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_notebook_selection_changed));
321
 
    m_notebooksTree->signal_button_press_event()
322
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_notebooks_tree_button_pressed), false);
323
 
    m_notebooksTree->signal_key_press_event()
324
 
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_notebooks_key_pressed));
325
 
      
326
 
    m_notebooksTree->show ();
327
 
    Gtk::ScrolledWindow *sw = new Gtk::ScrolledWindow ();
328
 
    sw->property_hscrollbar_policy() = Gtk::POLICY_AUTOMATIC;
329
 
    sw->property_vscrollbar_policy() = Gtk::POLICY_AUTOMATIC;
330
 
    sw->set_shadow_type(Gtk::SHADOW_IN);
331
 
    sw->add (*m_notebooksTree);
332
 
    sw->show ();
333
 
      
334
 
    return sw;
335
 
  }
336
 
 
337
 
  
338
 
  void NoteRecentChanges::make_recent_tree ()
339
 
  {
340
 
    m_targets.push_back(Gtk::TargetEntry ("STRING",
341
 
                                          Gtk::TARGET_SAME_APP,
342
 
                                          0));
343
 
    m_targets.push_back(Gtk::TargetEntry ("text/plain",
344
 
                                    Gtk::TARGET_SAME_APP,
345
 
                                          0));
346
 
    m_targets.push_back(Gtk::TargetEntry ("text/uri-list",
347
 
                                    Gtk::TARGET_SAME_APP,
348
 
                                          1));
349
 
 
350
 
    m_tree = Gtk::manage(new RecentTreeView ());
351
 
    m_tree->set_headers_visible(true);
352
 
    m_tree->set_rules_hint(true);
353
 
    m_tree->signal_row_activated().connect(
354
 
      sigc::mem_fun(*this, &NoteRecentChanges::on_row_activated));
355
 
    m_tree->get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
356
 
    m_tree->get_selection()->signal_changed().connect(
357
 
      sigc::mem_fun(*this, &NoteRecentChanges::on_selection_changed));
358
 
    m_tree->signal_button_press_event().connect(
359
 
      sigc::mem_fun(*this, &NoteRecentChanges::on_treeview_button_pressed), false);
360
 
    m_tree->signal_motion_notify_event().connect(
361
 
      sigc::mem_fun(*this, &NoteRecentChanges::on_treeview_motion_notify), false);
362
 
    m_tree->signal_button_release_event().connect(
363
 
      sigc::mem_fun(*this, &NoteRecentChanges::on_treeview_button_released));
364
 
    m_tree->signal_key_press_event().connect(
365
 
      sigc::mem_fun(*this,
366
 
                    &NoteRecentChanges::on_treeview_key_pressed), false);
367
 
    m_tree->signal_drag_data_get().connect(
368
 
      sigc::mem_fun(*this, &NoteRecentChanges::on_treeview_drag_data_get));
369
 
 
370
 
    m_tree->enable_model_drag_source(m_targets,
371
 
      Gdk::BUTTON1_MASK | Gdk::BUTTON3_MASK, Gdk::ACTION_MOVE);
372
 
 
373
 
    Gtk::CellRenderer *renderer;
374
 
 
375
 
    Gtk::TreeViewColumn *title = manage(new Gtk::TreeViewColumn ());
376
 
    title->set_title(_("Note"));
377
 
    title->set_min_width(150);
378
 
    title->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
379
 
    title->set_expand(true);
380
 
    title->set_resizable(true);
381
 
 
382
 
    renderer = manage(new Gtk::CellRendererPixbuf ());
383
 
    title->pack_start (*renderer, false);
384
 
    title->add_attribute (*renderer, "pixbuf", 0 /* icon */);
385
 
 
386
 
    renderer = manage(new Gtk::CellRendererText ());
387
 
    static_cast<Gtk::CellRendererText*>(renderer)->property_ellipsize() = Pango::ELLIPSIZE_END;
388
 
    title->pack_start (*renderer, true);
389
 
    title->add_attribute (*renderer, "text", 1 /* title */);
390
 
    title->set_sort_column(1); /* title */
391
 
    title->set_sort_indicator(false);
392
 
    title->set_reorderable(false);
393
 
    title->set_sort_order(Gtk::SORT_ASCENDING);
394
 
 
395
 
    m_tree->append_column (*title);
396
 
 
397
 
    Gtk::TreeViewColumn *change = manage(new Gtk::TreeViewColumn ());
398
 
    change->set_title(_("Last Changed"));
399
 
    change->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
400
 
    change->set_resizable(false);
401
 
 
402
 
    renderer = manage(new Gtk::CellRendererText ());
403
 
    renderer->property_xalign() = 1.0;
404
 
    change->pack_start (*renderer, false);
405
 
    change->add_attribute (*renderer, "text", 2 /* change date */);
406
 
    change->set_sort_column(2); /* change date */
407
 
    change->set_sort_indicator(false);
408
 
    change->set_reorderable(false);
409
 
    change->set_sort_order(Gtk::SORT_DESCENDING);
410
 
 
411
 
    m_tree->append_column (*change);
412
 
  }
413
 
 
414
 
  void NoteRecentChanges::add_note(const Note::Ptr & note)
415
 
  {
416
 
    std::string nice_date =
417
 
      utils::get_pretty_print_date(note->change_date(), true);
418
 
    Gtk::TreeIter iter = m_store->append();
419
 
    iter->set_value(m_column_types.icon, s_note_icon);
420
 
    iter->set_value(m_column_types.title, note->get_title());
421
 
    iter->set_value(m_column_types.change_date, nice_date);
422
 
    iter->set_value(m_column_types.note, note);
423
 
  }
424
 
 
425
 
  void NoteRecentChanges::delete_note(const Note::Ptr & note)
426
 
  {
427
 
    Gtk::TreeModel::Children rows = m_store->children();
428
 
 
429
 
    for (Gtk::TreeModel::iterator iter = rows.begin();
430
 
         rows.end() != iter; iter++) {
431
 
      if (note == iter->get_value(m_column_types.note)) {
432
 
        m_store->erase(iter);
433
 
        break;
434
 
      }
435
 
    }
436
 
  }
437
 
 
438
 
  void NoteRecentChanges::rename_note(const Note::Ptr & note)
439
 
  {
440
 
    Gtk::TreeModel::Children rows = m_store->children();
441
 
 
442
 
    for (Gtk::TreeModel::iterator iter = rows.begin();
443
 
         rows.end() != iter; iter++) {
444
 
      if (note == iter->get_value(m_column_types.note)) {
445
 
        iter->set_value(m_column_types.title, note->get_title());
446
 
        break;
447
 
      }
448
 
    }
449
 
  }
450
 
 
451
 
  void NoteRecentChanges::update_results()
452
 
  {
453
 
    // Save the currently selected notes
454
 
    Note::List selected_notes = get_selected_notes ();
455
 
 
456
 
    int sort_column = 2; /* change date */
457
 
    Gtk::SortType sort_type = Gtk::SORT_DESCENDING;
458
 
    if (m_store_sort)
459
 
      m_store_sort->get_sort_column_id (sort_column, sort_type);
460
 
 
461
 
    m_store = Gtk::ListStore::create (m_column_types);
462
 
 
463
 
    m_store_filter = Gtk::TreeModelFilter::create (m_store);
464
 
    m_store_filter->set_visible_func(sigc::mem_fun(*this, &NoteRecentChanges::filter_notes));
465
 
    m_store_sort = Gtk::TreeModelSort::create (m_store_filter);
466
 
    m_store_sort->set_sort_func (1 /* title */,
467
 
                                sigc::mem_fun(*this, &NoteRecentChanges::compare_titles));
468
 
    m_store_sort->set_sort_func (2 /* change date */,
469
 
                                sigc::mem_fun(*this, &NoteRecentChanges::compare_dates));
470
 
 
471
 
    int cnt = 0;
472
 
 
473
 
    for(Note::List::const_iterator note_iter = m_manager.get_notes().begin();
474
 
        note_iter != m_manager.get_notes().end(); ++note_iter) {
475
 
      const Note::Ptr & note(*note_iter);
476
 
      std::string nice_date =
477
 
        utils::get_pretty_print_date (note->change_date(), true);
478
 
 
479
 
      Gtk::TreeIter iter = m_store->append();
480
 
      iter->set_value(0, s_note_icon);  /* icon */
481
 
      iter->set_value(1, note->get_title()); /* title */
482
 
      iter->set_value(2, nice_date);  /* change date */
483
 
      iter->set_value(3, note);      /* note */
484
 
      cnt++;
485
 
    }
486
 
 
487
 
    m_tree->set_model(m_store_sort);
488
 
 
489
 
    perform_search ();
490
 
 
491
 
    if (sort_column >= 0) {
492
 
      // Set the sort column after loading data, since we
493
 
      // don't want to resort on every append.
494
 
      m_store_sort->set_sort_column(sort_column, sort_type);
495
 
    }
496
 
 
497
 
    // Restore the previous selection
498
 
    if (!selected_notes.empty()) {
499
 
      select_notes (selected_notes);
500
 
    }
501
 
 
502
 
  }
503
 
  
504
 
 
505
 
  void NoteRecentChanges::select_notes(const Note::List & notes)
506
 
  {
507
 
    Gtk::TreeIter iter = m_store_sort->children().begin();
508
 
 
509
 
    if (!iter)
510
 
      return;
511
 
 
512
 
    do {
513
 
      Note::Ptr iter_note = (*iter)[m_column_types.note];
514
 
      if (find(notes.begin(), notes.end(), iter_note) != notes.end()) {
515
 
        // Found one
516
 
        m_tree->get_selection()->select(iter);
517
 
        //ScrollToIter (tree, iter);
518
 
      }
519
 
    } while (++iter);
520
 
  }
521
 
 
522
 
  void NoteRecentChanges::scroll_to_iter (Gtk::TreeView & tree, const Gtk::TreeIter & iter)
523
 
  {
524
 
    Gtk::TreePath path = tree.get_model()->get_path(iter);
525
 
    if (!path.empty())
526
 
      tree.scroll_to_row (path);
527
 
  }
528
 
 
529
 
 
530
 
  void NoteRecentChanges::perform_search ()
531
 
  {
532
 
      // For some reason, the matches column must be rebuilt
533
 
      // every time because otherwise, it's not sortable.
534
 
      remove_matches_column ();
535
 
      Search search(m_manager);
536
 
 
537
 
      std::string text = get_search_text();
538
 
      if (text.empty()) {
539
 
        m_current_matches.clear ();
540
 
        m_store_filter->refilter ();
541
 
        update_total_note_count (m_store_sort->children().size());
542
 
        if (m_tree->get_realized()) {
543
 
          m_tree->scroll_to_point (0, 0);
544
 
        }
545
 
        return;
546
 
      }
547
 
      text = sharp::string_to_lower(text);
548
 
 
549
 
      m_current_matches.clear ();
550
 
      
551
 
      // Search using the currently selected notebook
552
 
      notebooks::Notebook::Ptr selected_notebook = get_selected_notebook ();
553
 
      if (std::tr1::dynamic_pointer_cast<notebooks::SpecialNotebook>(selected_notebook)) {
554
 
        selected_notebook = notebooks::Notebook::Ptr();
555
 
      }
556
 
 
557
 
      Search::ResultsPtr results = search.search_notes(text, false, selected_notebook);
558
 
      // if no results found in current notebook ask user whether
559
 
      // to search in all notebooks
560
 
      if(results->size() == 0 && selected_notebook != NULL) {
561
 
        no_matches_found_action();
562
 
      }
563
 
      else {
564
 
        for(Search::Results::const_reverse_iterator iter = results->rbegin();
565
 
            iter != results->rend(); iter++) {
566
 
          m_current_matches[iter->second->uri()] = iter->first;
567
 
        }
568
 
      
569
 
        add_matches_column ();
570
 
        m_store_filter->refilter ();
571
 
        m_tree->scroll_to_point (0, 0);
572
 
        update_match_note_count (m_current_matches.size());
573
 
      }
574
 
  }
575
 
 
576
 
 
577
 
  void NoteRecentChanges::add_matches_column ()
578
 
  {
579
 
    if (!m_matches_column) {
580
 
      Gtk::CellRenderer *renderer;
581
 
 
582
 
      m_matches_column = manage(new Gtk::TreeViewColumn ());
583
 
      m_matches_column->set_title(_("Matches"));
584
 
      m_matches_column->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
585
 
      m_matches_column->set_resizable(false);
586
 
 
587
 
      renderer = manage(new Gtk::CellRendererText ());
588
 
      renderer->property_width() = 75;
589
 
      m_matches_column->pack_start (*renderer, false);
590
 
      m_matches_column->set_cell_data_func (
591
 
        *renderer,
592
 
        sigc::mem_fun(*this, &NoteRecentChanges::matches_column_data_func));
593
 
      m_matches_column->set_sort_column(4);
594
 
      m_matches_column->set_sort_indicator(true);
595
 
      m_matches_column->set_reorderable(false);
596
 
      m_matches_column->set_sort_order(Gtk::SORT_DESCENDING);
597
 
      m_matches_column->set_clickable(true);
598
 
      m_store_sort->set_sort_func(4 /* matches */,
599
 
                                  sigc::mem_fun(*this, &NoteRecentChanges::compare_search_hits));
600
 
 
601
 
      m_tree->append_column (*m_matches_column);
602
 
      m_store_sort->set_sort_column(4, Gtk::SORT_DESCENDING);
603
 
    }
604
 
  }
605
 
 
606
 
 
607
 
  void NoteRecentChanges::remove_matches_column ()
608
 
  {
609
 
    if (m_matches_column == NULL)
610
 
      return;
611
 
    
612
 
    m_tree->remove_column (*m_matches_column);
613
 
    m_matches_column = NULL;
614
 
 
615
 
    m_store_sort->set_sort_column(2, Gtk::SORT_DESCENDING);
616
 
  }
617
 
 
618
 
 
619
 
  void NoteRecentChanges::matches_column_data_func(Gtk::CellRenderer * cell, 
620
 
                                                   const Gtk::TreeIter & iter)
621
 
  {
622
 
    Gtk::CellRendererText *crt = dynamic_cast<Gtk::CellRendererText*>(cell);
623
 
    if (!crt)
624
 
      return;
625
 
 
626
 
    std::string match_str = "";
627
 
 
628
 
    Note::Ptr note = (*iter)[m_column_types.note];
629
 
    if (note) {
630
 
      int match_count;
631
 
      std::map<std::string, int>::const_iterator miter 
632
 
        = m_current_matches.find(note->uri());
633
 
      if (miter != m_current_matches.end()) {
634
 
        match_count = miter->second;
635
 
        if(match_count == INT_MAX) {
636
 
          //TRANSLATORS: search found a match in note title
637
 
          match_str = _("Title match");
638
 
        }
639
 
        else if (match_count > 0) {
640
 
          const char * fmt;
641
 
          fmt = ngettext("%1% match", "%1% matches", match_count);
642
 
          match_str = str(boost::format(fmt) % match_count);
643
 
        }
644
 
      }
645
 
    }
646
 
 
647
 
    crt->property_text() = match_str;
648
 
  }
649
 
 
650
 
 
651
 
  void NoteRecentChanges::update_total_note_count (int total)
652
 
  {
653
 
    try {
654
 
      const char * fmt;
655
 
      fmt = ngettext("Total: %1% note", "Total: %1% notes", total);
656
 
      std::string status = str(boost::format (fmt) % total);
657
 
      m_status_bar.pop(0);
658
 
      m_status_bar.push(status, 0);
659
 
    } 
660
 
    catch(const std::exception & e)
661
 
    {
662
 
      ERR_OUT("exception: %s", e.what());
663
 
    }
664
 
  }
665
 
                
666
 
  
667
 
  void NoteRecentChanges::update_match_note_count (int matches)
668
 
  {
669
 
    try {
670
 
      const char * fmt;
671
 
      fmt = ngettext("Matches: %1% note", "Matches: %1% notes", matches);
672
 
      std::string status = str(boost::format (fmt) % matches);
673
 
      m_status_bar.pop(0);
674
 
      m_status_bar.push(status, 0);
675
 
    }
676
 
    catch(const std::exception & e)
677
 
    {
678
 
      ERR_OUT("exception: %s", e.what());
679
 
    }
680
 
  }
681
 
 
682
 
  // called when no search results are found in the selected notebook
683
 
  void NoteRecentChanges::no_matches_found_action()
684
 
  {
685
 
    m_hpaned.remove(m_matches_window);
686
 
    if(!m_no_matches_box) {
687
 
      Glib::ustring message = _("No results found in the selected notebook.\nClick here to search across all notes.");
688
 
      Gtk::LinkButton *link_button = manage(new Gtk::LinkButton("", message));
689
 
      link_button->signal_activate_link()
690
 
        .connect(sigc::mem_fun(*this, &NoteRecentChanges::show_all_search_results));
691
 
      link_button->set_tooltip_text(_("Click here to search across all notebooks"));
692
 
      link_button->show();
693
 
      Gtk::Table *no_matches_found_table = manage(new Gtk::Table(1, 3, false));
694
 
      no_matches_found_table->attach(*link_button, 1, 2, 0, 1,
695
 
        Gtk::FILL | Gtk::SHRINK,
696
 
        Gtk::SHRINK,
697
 
        0, 0
698
 
      );
699
 
 
700
 
      no_matches_found_table->set_col_spacings(4);
701
 
      no_matches_found_table->show_all();
702
 
      m_no_matches_box = manage(new Gtk::HBox(false, 0));
703
 
      m_no_matches_box->pack_start(*no_matches_found_table, true, true, 0);
704
 
      m_no_matches_box->show();
705
 
    }
706
 
    m_hpaned.add2(*m_no_matches_box);
707
 
  }
708
 
 
709
 
  void NoteRecentChanges::restore_matches_window()
710
 
  {
711
 
    if(m_no_matches_box && m_hpaned.get_child2() == m_no_matches_box) {
712
 
      m_hpaned.remove(*m_no_matches_box);
713
 
      m_hpaned.add2(m_matches_window);
714
 
      restore_position();
715
 
    }   
716
 
  }
717
 
 
718
 
  bool NoteRecentChanges::show_all_search_results()
719
 
  {
720
 
    Gtk::TreeIter iter = m_notebooksTree->get_model()->children().begin();
721
 
    m_notebooksTree->get_selection()->select(iter);
722
 
    return false;
723
 
  }
724
 
                
725
 
 
726
 
  /// <summary>
727
 
  /// Filter out notes based on the current search string
728
 
  /// and selected tags.  Also prevent template notes from
729
 
  /// appearing.
730
 
  /// </summary>
731
 
  bool NoteRecentChanges::filter_notes(const Gtk::TreeIter & iter)
732
 
  {
733
 
    Note::Ptr note = (*iter)[m_column_types.note];
734
 
    if (!note)
735
 
      return false;
736
 
                        
737
 
    // Don't show the template notes in the list
738
 
    Tag::Ptr template_tag = TagManager::obj().get_or_create_system_tag (TagManager::TEMPLATE_NOTE_SYSTEM_TAG);
739
 
    if (note->contains_tag (template_tag)) {
740
 
      return false;
741
 
    }
742
 
                        
743
 
    notebooks::Notebook::Ptr selected_notebook = get_selected_notebook ();
744
 
    if (std::tr1::dynamic_pointer_cast<notebooks::UnfiledNotesNotebook>(selected_notebook)) {
745
 
      // If the note belongs to a notebook, return false
746
 
      // since the only notes that should be shown in this
747
 
      // case are notes that are unfiled (not in a notebook).
748
 
      if (notebooks::NotebookManager::instance().get_notebook_from_note (note))
749
 
        return false;
750
 
    }
751
 
 
752
 
    bool passes_search_filter = filter_by_search (note);
753
 
    if (passes_search_filter == false)
754
 
      return false; // don't waste time checking tags if it's already false
755
 
 
756
 
    bool passes_tag_filter = filter_by_tag (note);
757
 
 
758
 
    // Must pass both filters to appear in the list
759
 
    return passes_tag_filter && passes_search_filter;
760
 
    // return true;
761
 
  }
762
 
    
763
 
#if 0 // TODO seems to be unused
764
 
  bool NoteRecentChanges::filter_tags(const Gtk::TreeIter & iter)
765
 
  {
766
 
      Tag t = model.GetValue (iter, 0 /* note */) as Tag;
767
 
            if(t.IsProperty || t.IsSystem)
768
 
              return false;
769
 
            
770
 
            return true;
771
 
 
772
 
  }
773
 
#endif
774
 
  // <summary>
775
 
  // Return true if the specified note should be shown in the list
776
 
  // based on the current selection of tags.  If no tags are selected,
777
 
  // all notes should be allowed.
778
 
  // </summary>
779
 
  bool NoteRecentChanges::filter_by_tag (const Note::Ptr & note)
780
 
  {
781
 
    if (m_selected_tags.empty())
782
 
      return true;
783
 
 
784
 
    //   // FIXME: Ugh!  NOT an O(1) operation.  Is there a better way?
785
 
    std::list<Tag::Ptr> tags;
786
 
    note->get_tags(tags);
787
 
    for(std::list<Tag::Ptr>::const_iterator iter = tags.begin();
788
 
        iter != tags.end(); ++iter) {
789
 
      if(m_selected_tags.find(*iter) != m_selected_tags.end()) {
790
 
        return true;
791
 
      }
792
 
    }
793
 
 
794
 
    return false;
795
 
  }
796
 
 
797
 
  // <summary>
798
 
  // Return true if the specified note should be shown in the list
799
 
  // based on the search string.  If no search string is specified,
800
 
  // all notes should be allowed.
801
 
  // </summary>
802
 
  bool NoteRecentChanges::filter_by_search(const Note::Ptr & note)
803
 
  {
804
 
    if (get_search_text().empty())
805
 
      return true;
806
 
 
807
 
    if (m_current_matches.empty())
808
 
      return false;
809
 
 
810
 
    return note && (m_current_matches.find(note->uri()) != m_current_matches.end());
811
 
  }
812
 
 
813
 
 
814
 
  void NoteRecentChanges::on_case_sensitive_toggled()
815
 
  {
816
 
    perform_search ();
817
 
  }
818
 
 
819
 
  void NoteRecentChanges::on_note_added(const Note::Ptr & note)
820
 
  {
821
 
    restore_matches_window();
822
 
    add_note(note);
823
 
  }
824
 
 
825
 
  void NoteRecentChanges::on_note_deleted(const Note::Ptr & note)
826
 
  {
827
 
    restore_matches_window();
828
 
    delete_note(note);
829
 
  }
830
 
 
831
 
  void NoteRecentChanges::on_note_renamed(const Note::Ptr & note,
832
 
                                          const std::string &)
833
 
  {
834
 
    restore_matches_window();
835
 
    rename_note(note);
836
 
  }
837
 
 
838
 
  void NoteRecentChanges::on_note_saved(const Note::Ptr&)
839
 
  {
840
 
    restore_matches_window();
841
 
    update_results ();
842
 
  }
843
 
 
844
 
  void NoteRecentChanges::on_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext> &,
845
 
                                                    Gtk::SelectionData &selection_data,
846
 
                                                    guint, guint)
847
 
  {
848
 
    Note::List selected_notes = get_selected_notes ();
849
 
    if (selected_notes.empty()) {
850
 
      return;
851
 
    }
852
 
                  
853
 
    std::vector<Glib::ustring> uris;
854
 
    for(Note::List::const_iterator iter = selected_notes.begin();
855
 
        iter != selected_notes.end(); ++iter) {
856
 
 
857
 
      uris.push_back((*iter)->uri());
858
 
    }
859
 
    
860
 
    selection_data.set_uris(uris);
861
 
                  
862
 
    if (selected_notes.size() == 1) {
863
 
      selection_data.set_text(selected_notes.front()->get_title());
864
 
    }
865
 
    else {
866
 
      selection_data.set_text(_("Notes"));
867
 
    }
868
 
  }
869
 
 
870
 
  void NoteRecentChanges::on_notebook_row_edited(const Glib::ustring& /*tree_path*/,
871
 
                                                 const Glib::ustring& new_text)
872
 
  {
873
 
    if (notebooks::NotebookManager::instance().notebook_exists(new_text) ||
874
 
        new_text == "") {
875
 
      return;
876
 
    }
877
 
    notebooks::Notebook::Ptr old_notebook = this->get_selected_notebook ();
878
 
    if (std::tr1::dynamic_pointer_cast<notebooks::SpecialNotebook>(old_notebook)) {
879
 
      return;
880
 
    }
881
 
    notebooks::Notebook::Ptr new_notebook = notebooks::NotebookManager::instance()
882
 
      .get_or_create_notebook (new_text);
883
 
    DBG_OUT("Renaming notebook '{%s}' to '{%s}'", old_notebook->get_name().c_str(),
884
 
            new_text.c_str());
885
 
    std::list<Note *> notes;
886
 
    old_notebook->get_tag()->get_notes(notes);
887
 
    for(std::list<Note *>::const_iterator note = notes.begin(); note != notes.end(); ++note) {
888
 
      notebooks::NotebookManager::instance().move_note_to_notebook (
889
 
          (*note)->shared_from_this(), new_notebook);
890
 
    }
891
 
    notebooks::NotebookManager::instance().delete_notebook (old_notebook);
892
 
    Gtk::TreeIter iter;
893
 
    if (notebooks::NotebookManager::instance().get_notebook_iter (new_notebook, iter)) {
894
 
      m_notebooksTree->get_selection()->select(iter);
895
 
    }
896
 
  }
897
 
 
898
 
  void NoteRecentChanges::on_selection_changed()
899
 
  {
900
 
    Note::List selected_notes = get_selected_notes ();
901
 
    ActionManager &am(ActionManager::obj());
902
 
 
903
 
    if (selected_notes.empty()) {
904
 
      am ["OpenNoteAction"]->property_sensitive() = false;
905
 
      am ["DeleteNoteAction"]->property_sensitive() = false;
906
 
    } 
907
 
    else if (selected_notes.size()) {
908
 
      am ["OpenNoteAction"]->property_sensitive() = true;
909
 
      am ["DeleteNoteAction"]->property_sensitive() = true;
910
 
    } 
911
 
    else {
912
 
      // Many notes are selected
913
 
      am ["OpenNoteAction"]->property_sensitive() = false;
914
 
      am ["DeleteNoteAction"]->property_sensitive() = true;
915
 
    }
916
 
  }
917
 
 
918
 
 
919
 
  bool NoteRecentChanges::on_treeview_button_pressed(GdkEventButton *ev)
920
 
  {
921
 
    if (ev->window != m_tree->get_bin_window()->gobj()) {
922
 
      return false;
923
 
    }
924
 
                  
925
 
    Gtk::TreePath dest_path;
926
 
    Gtk::TreeViewColumn *column = NULL;
927
 
    int unused;
928
 
                  
929
 
    m_tree->get_path_at_pos (ev->x, ev->y,
930
 
                             dest_path, column, unused, unused);
931
 
 
932
 
    m_clickX = ev->x;
933
 
    m_clickY = ev->y;
934
 
                  
935
 
    bool retval = false;
936
 
 
937
 
    switch (ev->type) {
938
 
    case GDK_2BUTTON_PRESS:
939
 
      if (ev->button != 1 || (ev->state &
940
 
                             (Gdk::CONTROL_MASK | Gdk::SHIFT_MASK)) != 0) {
941
 
        break;
942
 
      }
943
 
                    
944
 
      m_tree->get_selection()->unselect_all ();
945
 
      m_tree->get_selection()->select(dest_path);
946
 
      // apparently Gtk::TreeView::row_activated() require a dest_path
947
 
      // while get_path_at_pos() can return a NULL pointer.
948
 
      // See Gtkmm bug 586438
949
 
      // https://bugzilla.gnome.org/show_bug.cgi?id=586438
950
 
      gtk_tree_view_row_activated(m_tree->gobj(), dest_path.gobj(), 
951
 
                                  column?column->gobj():NULL);
952
 
      break;
953
 
    case GDK_BUTTON_PRESS:
954
 
      if (ev->button == 3) {
955
 
        const Glib::RefPtr<Gtk::TreeSelection> selection
956
 
          = m_tree->get_selection();
957
 
 
958
 
        if(selection->get_selected_rows().size() <= 1) {
959
 
          Gtk::TreeViewColumn * col = 0; // unused
960
 
          Gtk::TreePath p;
961
 
          int cell_x, cell_y;            // unused
962
 
          if (m_tree->get_path_at_pos(ev->x, ev->y, p, col,
963
 
                                      cell_x, cell_y)) {
964
 
            selection->unselect_all();
965
 
            selection->select(p);
966
 
          }
967
 
        }
968
 
        Gtk::Menu *menu = dynamic_cast<Gtk::Menu*>(
969
 
          ActionManager::obj().get_widget("/MainWindowContextMenu"));
970
 
        popup_context_menu_at_location (menu, ev->x, ev->y);
971
 
                      
972
 
        // Return true so that the base handler won't
973
 
        // run, which causes the selection to change to
974
 
        // the row that was right-clicked.
975
 
        retval = true;
976
 
        break;
977
 
      }
978
 
                    
979
 
      if (m_tree->get_selection()->is_selected(dest_path) 
980
 
          && (ev->state & (Gdk::CONTROL_MASK | Gdk::SHIFT_MASK)) == 0) {
981
 
        if (column && (ev->button == 1)) {
982
 
          Gtk::CellRenderer *renderer = column->get_first_cell();
983
 
          Gdk::Rectangle background_area;
984
 
          m_tree->get_background_area (dest_path, *column, background_area);
985
 
          Gdk::Rectangle cell_area;
986
 
          m_tree->get_cell_area (dest_path, *column, cell_area);
987
 
                        
988
 
          renderer->activate ((GdkEvent*)ev, *m_tree,
989
 
                              dest_path.to_string (),
990
 
                              background_area, cell_area,
991
 
                              Gtk::CELL_RENDERER_SELECTED);
992
 
                        
993
 
          Gtk::TreeIter iter = m_tree->get_model()->get_iter (dest_path);
994
 
          if (iter) {
995
 
            m_tree->get_model()->row_changed(dest_path, iter);
996
 
          }
997
 
        }
998
 
                      
999
 
        retval = true;
1000
 
      }
1001
 
                    
1002
 
      break;
1003
 
    default:
1004
 
      retval = false;
1005
 
      break;
1006
 
    }
1007
 
    return retval;
1008
 
  }
1009
 
                
1010
 
  bool NoteRecentChanges::on_treeview_motion_notify(GdkEventMotion *ev)
1011
 
  {
1012
 
    if ((ev->state & Gdk::BUTTON1_MASK) == 0) {
1013
 
      return false;
1014
 
    } 
1015
 
    else if (ev->window != m_tree->get_bin_window()->gobj()) {
1016
 
      return false;
1017
 
    }
1018
 
                  
1019
 
    bool retval = true;
1020
 
                  
1021
 
    if (!m_tree->drag_check_threshold(m_clickX, m_clickY, ev->x, ev->y)) {
1022
 
      return retval;
1023
 
    }
1024
 
                  
1025
 
    Gtk::TreePath dest_path;
1026
 
    Gtk::TreeViewColumn * col = NULL; // unused
1027
 
    int cell_x, cell_y;               // unused
1028
 
    if (!m_tree->get_path_at_pos (ev->x, ev->y, dest_path, col, cell_x, cell_y)) {
1029
 
      return retval;
1030
 
    }
1031
 
                  
1032
 
    m_tree->drag_begin (Gtk::TargetList::create (m_targets),
1033
 
                        Gdk::ACTION_MOVE, 1, (GdkEvent*)ev);
1034
 
    return retval;
1035
 
  }
1036
 
 
1037
 
  
1038
 
  bool NoteRecentChanges::on_treeview_button_released(GdkEventButton *ev)
1039
 
  {
1040
 
    if (!m_tree->drag_check_threshold(m_clickX, m_clickY, 
1041
 
                                      ev->x, ev->y) &&
1042
 
        ((ev->state & (Gdk::CONTROL_MASK | Gdk::SHIFT_MASK)) == 0) &&
1043
 
        m_tree->get_selection()->count_selected_rows () > 1) {
1044
 
                    
1045
 
      Gtk::TreePath dest_path;
1046
 
      Gtk::TreeViewColumn * col = NULL; // unused
1047
 
      int cell_x, cell_y;               // unused
1048
 
      m_tree->get_path_at_pos (ev->x, ev->y, dest_path, col, cell_x, cell_y);
1049
 
      m_tree->get_selection()->unselect_all ();
1050
 
      m_tree->get_selection()->select (dest_path);
1051
 
    }
1052
 
    return false;
1053
 
  }
1054
 
                
1055
 
  bool NoteRecentChanges::on_treeview_key_pressed(GdkEventKey * ev)
1056
 
  {
1057
 
    switch (ev->keyval) {
1058
 
    case GDK_KEY_Menu:
1059
 
    {
1060
 
      // Pop up the context menu if a note is selected
1061
 
      Note::List selected_notes = get_selected_notes();
1062
 
      if (!selected_notes.empty()) {
1063
 
        ActionManager & manager = ActionManager::obj();
1064
 
        Gtk::Menu * const menu
1065
 
          = dynamic_cast<Gtk::Menu*>(manager.get_widget(
1066
 
                                       "/MainWindowContextMenu"));
1067
 
        popup_context_menu_at_location(menu, 0, 0);
1068
 
      }
1069
 
      break;
1070
 
    }
1071
 
    case GDK_KEY_Return:
1072
 
    case GDK_KEY_KP_Enter:
1073
 
      // Open all selected notes
1074
 
      on_open_note();
1075
 
      return true;
1076
 
    default:
1077
 
      break;
1078
 
    }
1079
 
 
1080
 
    return false; // Let Escape be handled by the window.
1081
 
  }
1082
 
 
1083
 
  void NoteRecentChanges::popup_context_menu_at_location(Gtk::Menu *menu, int x, int y)
1084
 
  {
1085
 
    menu->show_all ();
1086
 
 
1087
 
    // Set up the funtion to position the context menu
1088
 
    // if we were called by the keyboard Gdk.Key.Menu.
1089
 
    if ((x == 0) && (y == 0)) {
1090
 
      menu->popup (sigc::mem_fun(*this, &NoteRecentChanges::position_context_menu),
1091
 
                0, gtk_get_current_event_time());
1092
 
    }
1093
 
    else {
1094
 
      menu->popup (0, gtk_get_current_event_time());
1095
 
    }
1096
 
  }
1097
 
 
1098
 
  // This is needed for when the user opens
1099
 
  // the context menu with the keyboard.
1100
 
  void NoteRecentChanges::position_context_menu(int & x, int & y, bool & push_in)
1101
 
  {
1102
 
    // Set default "return" values
1103
 
    push_in = false; // not used
1104
 
    x = 0;
1105
 
    y = 0;
1106
 
 
1107
 
    Gtk::Widget * const focus_widget = this->get_focus();
1108
 
    if (!focus_widget)
1109
 
      return;
1110
 
    focus_widget->get_window()->get_origin(x, y);
1111
 
 
1112
 
    Gtk::TreeView * const tree = dynamic_cast<Gtk::TreeView*>(
1113
 
                                   focus_widget);
1114
 
    if (!tree)
1115
 
      return;
1116
 
    const Glib::RefPtr<Gdk::Window> tree_area
1117
 
                                      = tree->get_bin_window();
1118
 
    if (!tree_area)
1119
 
      return;
1120
 
    tree_area->get_origin(x, y);
1121
 
 
1122
 
    const Glib::RefPtr<Gtk::TreeSelection> selection
1123
 
                                             = tree->get_selection();
1124
 
    const std::vector<Gtk::TreePath> selected_rows
1125
 
                                     = selection->get_selected_rows();
1126
 
    if (selected_rows.empty())
1127
 
      return;
1128
 
 
1129
 
    const Gtk::TreePath & dest_path = selected_rows.front();
1130
 
    const std::vector<Gtk::TreeViewColumn *> columns
1131
 
                                             = tree->get_columns();
1132
 
    Gdk::Rectangle cell_rect;
1133
 
    tree->get_cell_area (dest_path, *columns.front(), cell_rect);
1134
 
 
1135
 
    x += cell_rect.get_x();
1136
 
    y += cell_rect.get_y();
1137
 
  }
1138
 
 
1139
 
 
1140
 
  Note::List NoteRecentChanges::get_selected_notes()
1141
 
  {
1142
 
    Note::List selected_notes;
1143
 
          
1144
 
    std::vector<Gtk::TreePath> selected_rows =
1145
 
      m_tree->get_selection()->get_selected_rows ();
1146
 
    for(std::vector<Gtk::TreePath>::const_iterator iter
1147
 
          = selected_rows.begin(); iter != selected_rows.end(); ++iter) {
1148
 
      Note::Ptr note = get_note (*iter);
1149
 
      if (!note) {
1150
 
        continue;
1151
 
      }
1152
 
            
1153
 
      selected_notes.push_back(note);
1154
 
    }
1155
 
          
1156
 
    return selected_notes;
1157
 
  }
1158
 
 
1159
 
  Note::Ptr NoteRecentChanges::get_note(const Gtk::TreePath & p)
1160
 
  {
1161
 
    Gtk::TreeIter iter = m_store_sort->get_iter(p); 
1162
 
    if(iter) {
1163
 
      return (*iter)[m_column_types.note];
1164
 
    }
1165
 
    return Note::Ptr();
1166
 
  }
1167
 
 
1168
 
  void NoteRecentChanges::on_open_note()
1169
 
  {
1170
 
    Note::List selected_notes = get_selected_notes ();
1171
 
    for(Note::List::iterator iter = selected_notes.begin(); iter != selected_notes.end(); ++iter) {
1172
 
      (*iter)->get_window()->present();
1173
 
    }
 
104
    if(m_window_menu_search) {
 
105
      delete m_window_menu_search;
 
106
    }
 
107
    if(m_window_menu_note) {
 
108
      delete m_window_menu_note;
 
109
    }
 
110
    if(m_window_menu_default) {
 
111
      delete m_window_menu_default;
 
112
    }
 
113
  }
 
114
 
 
115
  Gtk::Box *NoteRecentChanges::make_toolbar()
 
116
  {
 
117
    Gtk::Box *toolbar = manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
 
118
    toolbar->set_border_width(5);
 
119
    m_all_notes_button = manage(new Gtk::Button);
 
120
    m_all_notes_button->set_image(*manage(new Gtk::Image(Gtk::Stock::FIND, Gtk::ICON_SIZE_BUTTON)));
 
121
    m_all_notes_button->set_always_show_image(true);
 
122
    m_all_notes_button->set_label(_("All Notes"));
 
123
    m_all_notes_button->signal_clicked().connect(sigc::mem_fun(*this, &NoteRecentChanges::present_search));
 
124
    m_all_notes_button->show_all();
 
125
    toolbar->pack_start(*m_all_notes_button, false, false);
 
126
 
 
127
    Gtk::Button *button = manage(new Gtk::Button);
 
128
    button->set_image(*manage(new Gtk::Image(IconManager::obj().get_icon(IconManager::NOTE_NEW, 24))));
 
129
    button->set_always_show_image(true);
 
130
    button->set_label(_("New"));
 
131
    button->signal_clicked().connect(sigc::mem_fun(m_search_notes_widget, &SearchNotesWidget::new_note));
 
132
    button->show_all();
 
133
    toolbar->pack_start(*button, false, false);
 
134
 
 
135
    m_search_entry.set_activates_default(false);
 
136
    m_search_entry.set_size_request(300);
 
137
    m_search_entry.signal_changed()
 
138
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_entry_changed));
 
139
    m_search_entry.signal_activate()
 
140
      .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_entry_activated));
 
141
    Gtk::Alignment *alignment = manage(new Gtk::Alignment(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER, 0));
 
142
    alignment->add(m_search_entry);
 
143
    alignment->show_all();
 
144
    toolbar->pack_start(*alignment, true, true);
 
145
 
 
146
    button = manage(new Gtk::Button);
 
147
    button->set_image(*manage(new Gtk::Image(IconManager::obj().get_icon("emblem-system-symbolic", 24))));
 
148
    button->set_always_show_image(true);
 
149
    button->signal_clicked().connect(
 
150
      boost::bind(sigc::mem_fun(*this, &NoteRecentChanges::on_show_window_menu), button));
 
151
    button->show_all();
 
152
    toolbar->pack_end(*button, false, false);
 
153
 
 
154
    toolbar->show();
 
155
    return toolbar;
 
156
  }
 
157
 
 
158
  void NoteRecentChanges::present_search()
 
159
  {
 
160
    utils::EmbeddableWidget *current = currently_embedded();
 
161
    if(&m_search_notes_widget == dynamic_cast<SearchNotesWidget*>(current)) {
 
162
      return;
 
163
    }
 
164
    if(current) {
 
165
      background_embedded(*current);
 
166
    }
 
167
    foreground_embedded(m_search_notes_widget);
 
168
  }
 
169
 
 
170
  void NoteRecentChanges::present_note(const Note::Ptr & note)
 
171
  {
 
172
    embed_widget(*note->get_window());
 
173
  }
 
174
 
 
175
 
 
176
  void NoteRecentChanges::new_note()
 
177
  {
 
178
    std::vector<Gtk::Widget*> current = m_embed_box.get_children();
 
179
    SearchNotesWidget *search_wgt = dynamic_cast<SearchNotesWidget*>(current.size() > 0 ? current[0] : NULL);
 
180
    if(search_wgt) {
 
181
      search_wgt->new_note();
 
182
    }
 
183
    else {
 
184
      present_note(m_note_manager.create());
 
185
    }
 
186
  }
 
187
 
 
188
 
 
189
  void NoteRecentChanges::on_open_note(const Note::Ptr & note)
 
190
  {
 
191
    present_note(note);
 
192
  }
 
193
 
 
194
  void NoteRecentChanges::on_open_note_new_window(const Note::Ptr & note)
 
195
  {
 
196
    MainWindow & window = IGnote::obj().new_main_window();
 
197
    window.present();
 
198
    window.present_note(note);
1174
199
  }
1175
200
 
1176
201
  void NoteRecentChanges::on_delete_note()
1177
202
  {
1178
 
    Note::List selected_notes = get_selected_notes ();
1179
 
    if (selected_notes.empty()) {
1180
 
      return;
1181
 
    }
1182
 
                  
1183
 
    noteutils::show_deletion_dialog(selected_notes, this);
 
203
    m_search_notes_widget.delete_selected_notes();
1184
204
  }
1185
205
 
1186
206
 
1187
207
 
1188
208
  void NoteRecentChanges::on_close_window()
1189
209
  {
1190
 
#if 0
1191
 
    // Disconnect external signal handlers to prevent bloweup
1192
 
    manager.NoteDeleted -= OnNotesChanged;
1193
 
    manager.NoteAdded -= OnNotesChanged;
1194
 
    manager.NoteRenamed -= OnNoteRenamed;
1195
 
    manager.NoteSaved -= OnNoteSaved;
1196
 
                        
1197
 
    Notebooks.NotebookManager.NoteAddedToNotebook -= OnNoteAddedToNotebook;
1198
 
    Notebooks.NotebookManager.NoteRemovedFromNotebook -= OnNoteRemovedFromNotebook;
1199
 
#endif
1200
 
    // The following code has to be done for the MenuBar to
1201
 
    // appear properly the next time this window is opened.
1202
 
    if (m_menubar) {
1203
 
      if(Gnote::obj().windowed()) {
1204
 
        m_content_vbox.remove (*m_menubar);
 
210
    Preferences::obj().get_schema_settings(Preferences::SCHEMA_GNOTE)->set_boolean(
 
211
        Preferences::MAIN_WINDOW_MAXIMIZED,
 
212
        get_window()->get_state() & Gdk::WINDOW_STATE_MAXIMIZED);
 
213
    std::vector<Gtk::Widget*> current = m_embed_box.get_children();
 
214
    for(std::vector<Gtk::Widget*>::iterator iter = current.begin();
 
215
        iter != current.end(); ++iter) {
 
216
      utils::EmbeddableWidget *widget = dynamic_cast<utils::EmbeddableWidget*>(*iter);
 
217
      if(widget) {
 
218
        background_embedded(*widget);
1205
219
      }
1206
 
#if 0
1207
 
      am ["OpenNoteAction"].Activated -= OnOpenNote;
1208
 
      am ["DeleteNoteAction"].Activated -= OnDeleteNote;
1209
 
      am ["NewNotebookAction"].Activated -= OnNewNotebook;
1210
 
      am ["DeleteNotebookAction"].Activated -= OnDeleteNotebook;
1211
 
      am ["NewNotebookNoteAction"].Activated -= OnNewNotebookNote;
1212
 
      am ["OpenNotebookTemplateNoteAction"].Activated -= OnOpenNotebookTemplateNote;
1213
 
      am ["CloseWindowAction"].Activated -= OnCloseWindow;
1214
 
#endif
1215
220
    }
1216
 
                        
1217
 
    save_position ();
1218
 
//    Tomboy.ExitingEvent -= OnExitingEvent;
1219
221
 
1220
 
    hide ();
1221
 
    if(Gnote::obj().windowed()) {
1222
 
      delete s_instance;
1223
 
      s_instance = NULL;
1224
 
    }
 
222
    hide();
1225
223
  }
1226
224
 
1227
225
 
1236
234
    switch (ev->keyval) {
1237
235
    case GDK_KEY_Escape:
1238
236
      // Allow Escape to close the window
1239
 
      on_close_window ();
 
237
      if(&m_search_notes_widget == dynamic_cast<SearchNotesWidget*>(currently_embedded())) {
 
238
        on_close_window();
 
239
      }
 
240
      else {
 
241
        utils::EmbeddableWidget *current_item = currently_embedded();
 
242
        if(current_item) {
 
243
          background_embedded(*current_item);
 
244
        }
 
245
        foreground_embedded(m_search_notes_widget);
 
246
      }
 
247
      break;
 
248
    case GDK_KEY_F1:
 
249
      utils::show_help("gnote", "", get_screen()->gobj(), this);
1240
250
      break;
1241
251
    default:
1242
252
      break;
1248
258
  void NoteRecentChanges::on_show()
1249
259
  {
1250
260
    // Select "All Notes" in the notebooks list
1251
 
    select_all_notes_notebook ();
1252
 
      
1253
 
    m_find_combo.get_entry()->grab_focus ();
1254
 
    utils::ForcedPresentWindow::on_show();
1255
 
  }
1256
 
 
1257
 
  
1258
 
  int NoteRecentChanges::compare_titles(const Gtk::TreeIter & a, const Gtk::TreeIter & b)
1259
 
  {
1260
 
    std::string title_a = (*a)[m_column_types.title];
1261
 
    std::string title_b = (*b)[m_column_types.title];
1262
 
 
1263
 
    if (title_a.empty() || title_b.empty())
1264
 
      return -1;
1265
 
 
1266
 
    return title_a.compare(title_b);
1267
 
  }
1268
 
 
1269
 
  int NoteRecentChanges::compare_dates(const Gtk::TreeIter & a, const Gtk::TreeIter & b)
1270
 
  {
1271
 
    Note::Ptr note_a = (*a)[m_column_types.note];
1272
 
    Note::Ptr note_b = (*b)[m_column_types.note];
1273
 
 
1274
 
    if (!note_a || !note_b) {
1275
 
      return -1;
1276
 
    }
1277
 
    else {
1278
 
      return sharp::DateTime::compare (note_a->change_date(), note_b->change_date());
1279
 
    }
1280
 
  }
1281
 
 
1282
 
  int NoteRecentChanges::compare_search_hits(const Gtk::TreeIter & a, const Gtk::TreeIter & b)
1283
 
  {
1284
 
    Note::Ptr note_a = (*a)[m_column_types.note];
1285
 
    Note::Ptr note_b = (*b)[m_column_types.note];
1286
 
 
1287
 
    if (!note_a || !note_b) {
1288
 
      return -1;
1289
 
    }
1290
 
 
1291
 
    int matches_a;
1292
 
    int matches_b;
1293
 
    std::map<std::string, int>::iterator iter_a = m_current_matches.find(note_a->uri());
1294
 
    std::map<std::string, int>::iterator iter_b = m_current_matches.find(note_b->uri());
1295
 
    bool has_matches_a = (iter_a != m_current_matches.end());
1296
 
    bool has_matches_b = (iter_b != m_current_matches.end());
1297
 
 
1298
 
    if (!has_matches_a || !has_matches_b) {
1299
 
      if (has_matches_a) {
1300
 
        return 1;
1301
 
      }
1302
 
 
1303
 
      return -1;
1304
 
    }
1305
 
 
1306
 
    matches_a = iter_a->second;
1307
 
    matches_b = iter_b->second;
1308
 
    int result = matches_a - matches_b;
1309
 
    if (result == 0) {
1310
 
      // Do a secondary sort by note title in alphabetical order
1311
 
      result = compare_titles (a, b);
1312
 
 
1313
 
      // Make sure to always sort alphabetically
1314
 
      if (result != 0) {
1315
 
        int sort_col_id;
1316
 
        Gtk::SortType sort_type;
1317
 
        if (m_store_sort->get_sort_column_id (sort_col_id,
1318
 
                                              sort_type)) {
1319
 
          if (sort_type == Gtk::SORT_DESCENDING)
1320
 
            result = -result; // reverse sign
1321
 
        }
1322
 
      }
1323
 
 
1324
 
      return result;
1325
 
    }
1326
 
 
1327
 
    return result;
1328
 
  }
1329
 
 
1330
 
 
1331
 
  void NoteRecentChanges::on_row_activated(const Gtk::TreePath & p, Gtk::TreeViewColumn*)
1332
 
  {
1333
 
    Gtk::TreeIter iter = m_store_sort->get_iter (p);
1334
 
    if (!iter)
1335
 
      return;
1336
 
 
1337
 
    Note::Ptr note = (*iter)[m_column_types.note];
1338
 
 
1339
 
    note->get_window()->present ();
1340
 
 
1341
 
    // Tell the new window to highlight the matches and
1342
 
    // prepopulate the Firefox-style search
1343
 
    if (!get_search_text().empty()) {
1344
 
      NoteFindBar & find(note->get_window()->get_find_bar());
1345
 
      find.show_all ();
1346
 
      find.property_visible() = true;
1347
 
      find.set_search_text(get_search_text());
1348
 
    }
1349
 
  }
1350
 
 
1351
 
  void NoteRecentChanges::on_entry_activated()
1352
 
  {
1353
 
    if (m_entry_changed_timeout) {
1354
 
      m_entry_changed_timeout->cancel ();
1355
 
    }
1356
 
 
1357
 
    entry_changed_timeout();
 
261
    m_search_notes_widget.select_all_notes_notebook();
 
262
 
 
263
    if(m_embed_box.get_children().size() == 0 && m_embedded_widgets.size() > 0) {
 
264
      foreground_embedded(**m_embedded_widgets.rbegin());
 
265
    }
 
266
    std::vector<Gtk::Widget*> embedded = m_embed_box.get_children();
 
267
    if(embedded.size() == 1 && embedded.front() == &m_search_notes_widget) {
 
268
      m_search_entry.grab_focus();
 
269
    }
 
270
    MainWindow::on_show();
 
271
  }
 
272
 
 
273
  void NoteRecentChanges::set_search_text(const std::string & value)
 
274
  {
 
275
    m_search_entry.set_text(value);
 
276
  }
 
277
 
 
278
  void NoteRecentChanges::embed_widget(utils::EmbeddableWidget & widget)
 
279
  {
 
280
    if(std::find(m_embedded_widgets.begin(), m_embedded_widgets.end(), &widget) == m_embedded_widgets.end()) {
 
281
      widget.embed(this);
 
282
      m_embedded_widgets.push_back(&widget);
 
283
    }
 
284
    utils::EmbeddableWidget *current = currently_embedded();
 
285
    if(current && current != &widget) {
 
286
      background_embedded(*current);
 
287
    }
 
288
    foreground_embedded(widget);
 
289
  }
 
290
 
 
291
  void NoteRecentChanges::unembed_widget(utils::EmbeddableWidget & widget)
 
292
  {
 
293
    bool show_other = false;
 
294
    std::list<utils::EmbeddableWidget*>::iterator iter = std::find(
 
295
      m_embedded_widgets.begin(), m_embedded_widgets.end(), &widget);
 
296
    if(iter != m_embedded_widgets.end()) {
 
297
      if(is_foreground(**iter)) {
 
298
        background_embedded(widget);
 
299
        show_other = true;
 
300
      }
 
301
      m_embedded_widgets.erase(iter);
 
302
      widget.unembed();
 
303
    }
 
304
    if(show_other) {
 
305
      if(m_embedded_widgets.size()) {
 
306
        foreground_embedded(**m_embedded_widgets.rbegin());
 
307
      }
 
308
      else if(get_visible()) {
 
309
        on_close_window();
 
310
      }
 
311
    }
 
312
  }
 
313
 
 
314
  void NoteRecentChanges::foreground_embedded(utils::EmbeddableWidget & widget)
 
315
  {
 
316
    try {
 
317
      if(currently_embedded() == &widget) {
 
318
        return;
 
319
      }
 
320
      Gtk::Widget &wid = dynamic_cast<Gtk::Widget&>(widget);
 
321
      m_embed_box.pack_start(wid, true, true, 0);
 
322
      widget.foreground();
 
323
      wid.show();
 
324
      update_toolbar(widget);
 
325
      on_embedded_name_changed(widget.get_name());
 
326
      m_current_embedded_name_slot = widget.signal_name_changed
 
327
        .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_embedded_name_changed));
 
328
    }
 
329
    catch(std::bad_cast&) {
 
330
    }
 
331
  }
 
332
 
 
333
  void NoteRecentChanges::background_embedded(utils::EmbeddableWidget & widget)
 
334
  {
 
335
    try {
 
336
      if(currently_embedded() != &widget) {
 
337
        return;
 
338
      }
 
339
      m_current_embedded_name_slot.disconnect();
 
340
      Gtk::Widget &wid = dynamic_cast<Gtk::Widget&>(widget);
 
341
      widget.background();
 
342
      m_embed_box.remove(wid);
 
343
    }
 
344
    catch(std::bad_cast&) {
 
345
    }
 
346
  }
 
347
 
 
348
  bool NoteRecentChanges::is_foreground(utils::EmbeddableWidget & widget)
 
349
  {
 
350
    std::vector<Gtk::Widget*> current = m_embed_box.get_children();
 
351
    for(std::vector<Gtk::Widget*>::iterator iter = current.begin();
 
352
        iter != current.end(); ++iter) {
 
353
      if(dynamic_cast<utils::EmbeddableWidget*>(*iter) == &widget) {
 
354
        return true;
 
355
      }
 
356
    }
 
357
 
 
358
    return false;
 
359
  }
 
360
 
 
361
  utils::EmbeddableWidget *NoteRecentChanges::currently_embedded()
 
362
  {
 
363
    std::vector<Gtk::Widget*> children = m_embed_box.get_children();
 
364
    return children.size() ? dynamic_cast<utils::EmbeddableWidget*>(children[0]) : NULL;
 
365
  }
 
366
 
 
367
  bool NoteRecentChanges::on_map_event(GdkEventAny *evt)
 
368
  {
 
369
    bool res = MainWindow::on_map_event(evt);
 
370
    m_mapped = true;
 
371
    return res;
 
372
  }
 
373
 
 
374
  void NoteRecentChanges::on_embedded_name_changed(const std::string & name)
 
375
  {
 
376
    std::string title;
 
377
    if(name != "") {
 
378
      title = "[" + name + "] - ";
 
379
    }
 
380
    title += _("Notes");
 
381
    set_title(title);
1358
382
  }
1359
383
 
1360
384
  void NoteRecentChanges::on_entry_changed()
1361
385
  {
1362
 
    if (m_entry_changed_timeout == NULL) {
1363
 
      m_entry_changed_timeout = new utils::InterruptableTimeout ();
 
386
    if(m_entry_changed_timeout == NULL) {
 
387
      m_entry_changed_timeout = new utils::InterruptableTimeout();
1364
388
      m_entry_changed_timeout->signal_timeout
1365
389
        .connect(sigc::mem_fun(*this, &NoteRecentChanges::entry_changed_timeout));
1366
390
    }
1367
391
 
1368
 
    if (get_search_text().empty()) {
1369
 
      m_clear_search_button.set_sensitive(false);
1370
 
      perform_search ();
1371
 
    } 
 
392
    std::string search_text = get_search_text();
 
393
    if(search_text.empty()) {
 
394
      m_search_notes_widget.perform_search(search_text);
 
395
    }
1372
396
    else {
1373
 
      m_entry_changed_timeout->reset (500);
1374
 
      m_clear_search_button.set_sensitive(true);
1375
 
    }
1376
 
 
1377
 
    restore_matches_window();
1378
 
  }
1379
 
 
1380
 
  // Called in after .5 seconds of typing inactivity, or on
1381
 
  // explicit activate.  Redo the search, and update the
1382
 
  // results...
 
397
      m_entry_changed_timeout->reset(500);
 
398
    }
 
399
  }
 
400
 
 
401
  void NoteRecentChanges::on_entry_activated()
 
402
  {
 
403
    if(m_entry_changed_timeout) {
 
404
      m_entry_changed_timeout->cancel();
 
405
    }
 
406
 
 
407
    entry_changed_timeout();
 
408
  }
 
409
 
1383
410
  void NoteRecentChanges::entry_changed_timeout()
1384
411
  {
1385
 
    if (get_search_text().empty())
1386
 
      return;
1387
 
    
1388
 
    perform_search ();
1389
 
    add_to_previous_searches (get_search_text());
1390
 
  }
1391
 
 
1392
 
 
1393
 
  void NoteRecentChanges::add_to_previous_searches(const std::string & text)
1394
 
  {
1395
 
    // Update previous searches, by adding a new term to the
1396
 
    // list, or shuffling an existing term to the top...
1397
 
    bool repeat = false;
1398
 
 
1399
 
    std::string lower = sharp::string_to_lower(text);
1400
 
    for(std::list<std::string>::const_iterator iter = s_previous_searches.begin();
1401
 
        iter != s_previous_searches.end(); ++iter) {
1402
 
      if (sharp::string_to_lower(*iter) == lower) {
1403
 
        repeat = true;
1404
 
      }
1405
 
    }
1406
 
 
1407
 
    if (!repeat) {
1408
 
      s_previous_searches.push_front(text);
1409
 
      Gtk::TreeIter iter 
1410
 
        = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(m_find_combo.get_model())->prepend();
1411
 
      iter->set_value(0, text);
1412
 
    }
1413
 
  }
1414
 
 
1415
 
  void NoteRecentChanges::clear_search_clicked()
1416
 
  {
1417
 
    m_find_combo.get_entry()->set_text("");
1418
 
    m_find_combo.get_entry()->grab_focus ();
1419
 
  }
1420
 
 
1421
 
 
1422
 
  void NoteRecentChanges::notebook_pixbuf_cell_data_func(Gtk::CellRenderer * renderer, 
1423
 
                                                         const Gtk::TreeIter & iter)
1424
 
  {
1425
 
    notebooks::Notebook::Ptr notebook;
1426
 
    iter->get_value(0, notebook);
1427
 
    if (!notebook)
1428
 
      return;
1429
 
      
1430
 
    Gtk::CellRendererPixbuf *crp = dynamic_cast<Gtk::CellRendererPixbuf*>(renderer);
1431
 
    if (std::tr1::dynamic_pointer_cast<notebooks::AllNotesNotebook>(notebook)) {
1432
 
      crp->property_pixbuf() = s_all_notes_icon;
1433
 
    } 
1434
 
    else if (std::tr1::dynamic_pointer_cast<notebooks::UnfiledNotesNotebook>(notebook)) {
1435
 
      crp->property_pixbuf() = s_unfiled_notes_icon;
1436
 
    } 
1437
 
    else {
1438
 
      crp->property_pixbuf() = s_notebook_icon;
1439
 
    }
1440
 
  }
1441
 
 
1442
 
  void NoteRecentChanges::notebook_text_cell_data_func(Gtk::CellRenderer * renderer, 
1443
 
                                                       const Gtk::TreeIter & iter)
1444
 
  {
1445
 
    Gtk::CellRendererText *crt = dynamic_cast<Gtk::CellRendererText*>(renderer);
1446
 
    crt->property_ellipsize() = Pango::ELLIPSIZE_END;
1447
 
    notebooks::Notebook::Ptr notebook;
1448
 
    iter->get_value(0, notebook);
1449
 
    if (!notebook) {
1450
 
      crt->property_text() = "";
1451
 
      return;
1452
 
    }
1453
 
      
1454
 
    crt->property_text() = notebook->get_name();
1455
 
 
1456
 
    if (std::tr1::dynamic_pointer_cast<notebooks::SpecialNotebook>(notebook)) {
1457
 
      // Bold the "Special" Notebooks
1458
 
      crt->property_markup() = str(boost::format("<span weight=\"bold\">%1%</span>") 
1459
 
                                   % notebook->get_name());
1460
 
    } 
1461
 
    else {
1462
 
      crt->property_text() = notebook->get_name();
1463
 
    }
1464
 
  }
1465
 
 
1466
 
 
1467
 
  void NoteRecentChanges::on_notebook_selection_changed()
1468
 
  {
1469
 
    restore_matches_window();
1470
 
    notebooks::Notebook::Ptr notebook = get_selected_notebook ();
1471
 
    ActionManager & am(ActionManager::obj());
1472
 
    if (!notebook) {
1473
 
      // Clear out the currently selected tags so that no notebook is selected
1474
 
      m_selected_tags.clear ();
1475
 
 
1476
 
        
1477
 
      // Select the "All Notes" item without causing
1478
 
      // this handler to be called again
1479
 
      m_on_notebook_selection_changed_cid.block();
1480
 
      select_all_notes_notebook ();
1481
 
      am["DeleteNotebookAction"]->set_sensitive(false);
1482
 
      m_on_notebook_selection_changed_cid.unblock();
1483
 
    } 
1484
 
    else {
1485
 
      m_selected_tags.clear ();
1486
 
      if (notebook->get_tag()) {
1487
 
        m_selected_tags.insert(notebook->get_tag());
1488
 
      }
1489
 
      bool allow_edit = false;
1490
 
      if (std::tr1::dynamic_pointer_cast<notebooks::SpecialNotebook>(notebook)) {
1491
 
        am["DeleteNotebookAction"]->set_sensitive(false);
1492
 
      } 
1493
 
      else {
1494
 
        am["DeleteNotebookAction"]->set_sensitive(true);
1495
 
        allow_edit = true;
1496
 
      }
1497
 
 
1498
 
      std::vector<Gtk::CellRenderer*> renderers = m_notebooksTree->get_column(0)->get_cells();
1499
 
      for (std::vector<Gtk::CellRenderer*>::iterator renderer = renderers.begin();
1500
 
           renderer != renderers.end(); ++renderer) {
1501
 
        Gtk::CellRendererText *text_rederer = dynamic_cast<Gtk::CellRendererText*>(*renderer);
1502
 
        if (text_rederer) {
1503
 
          text_rederer->property_editable() = allow_edit;
1504
 
        }
1505
 
      }
1506
 
    }
1507
 
      
1508
 
    update_results ();
1509
 
  }
1510
 
    
1511
 
  void NoteRecentChanges::on_new_notebook()
1512
 
  {
1513
 
    notebooks::NotebookManager::prompt_create_new_notebook (this);
1514
 
  }
1515
 
    
1516
 
  void NoteRecentChanges::on_delete_notebook()
1517
 
  {
1518
 
    notebooks::Notebook::Ptr notebook = get_selected_notebook ();
1519
 
    if (!notebook)
1520
 
      return;
1521
 
      
1522
 
    notebooks::NotebookManager::prompt_delete_notebook (this, notebook);
1523
 
  }
1524
 
 
1525
 
 
1526
 
// Create a new note in the notebook when activated
1527
 
  void NoteRecentChanges::on_notebook_row_activated(const Gtk::TreePath & , 
1528
 
                                                    Gtk::TreeViewColumn*)
1529
 
  {
1530
 
    on_new_notebook_note();
1531
 
  }
1532
 
  
1533
 
  void NoteRecentChanges::on_new_notebook_note()
1534
 
  {
1535
 
    notebooks::Notebook::Ptr notebook = get_selected_notebook ();
1536
 
    if (!notebook || std::tr1::dynamic_pointer_cast<notebooks::SpecialNotebook>(notebook)) {
1537
 
      // Just create a standard note (not in a notebook)
1538
 
      ActionManager::obj()["NewNoteAction"]->activate ();
1539
 
      return;
1540
 
    }
1541
 
      
1542
 
    // Look for the template note and create a new note
1543
 
    Note::Ptr note = notebook->create_notebook_note ();
1544
 
    note->get_window()->show ();
1545
 
  }
1546
 
    
1547
 
  void NoteRecentChanges::on_open_notebook_template_note()
1548
 
  {
1549
 
    notebooks::Notebook::Ptr notebook = get_selected_notebook ();
1550
 
    if (!notebook)
1551
 
      return;
1552
 
      
1553
 
    Note::Ptr templateNote = notebook->get_template_note ();
1554
 
    if (!templateNote)
1555
 
      return; // something seriously went wrong
1556
 
      
1557
 
    templateNote->get_window()->present ();
1558
 
  }
1559
 
    
1560
 
  notebooks::Notebook::Ptr NoteRecentChanges::get_selected_notebook ()
1561
 
  {
1562
 
    Gtk::TreeIter iter;
1563
 
      
1564
 
    Glib::RefPtr<Gtk::TreeSelection> selection = m_notebooksTree->get_selection();
1565
 
    if (!selection) {
1566
 
      return notebooks::Notebook::Ptr();
1567
 
    }
1568
 
    iter = selection->get_selected();
1569
 
    if(!iter) {
1570
 
      return notebooks::Notebook::Ptr(); // Nothing selected
1571
 
    }
1572
 
      
1573
 
    notebooks::Notebook::Ptr notebook;
1574
 
    iter->get_value(0, notebook);
1575
 
    return notebook;
1576
 
  }
1577
 
    
1578
 
  void NoteRecentChanges::select_all_notes_notebook ()
1579
 
  {
1580
 
    Glib::RefPtr<Gtk::TreeModel> model = m_notebooksTree->get_model();
1581
 
    DBG_ASSERT(model, "model is NULL");
1582
 
    if(!model) {
1583
 
      return;
1584
 
    }
1585
 
    Gtk::TreeIter iter = model->children().begin();
1586
 
    if (iter) {
1587
 
      m_notebooksTree->get_selection()->select(iter);
1588
 
    }
1589
 
  }
1590
 
 
1591
 
 
1592
 
  bool NoteRecentChanges::on_notebooks_tree_button_pressed(GdkEventButton *ev)
1593
 
  {
1594
 
    if(ev->button == 3) {
1595
 
      // third mouse button (right-click)
1596
 
      Gtk::TreeViewColumn * col = 0; // unused
1597
 
      Gtk::TreePath p;
1598
 
      int cell_x, cell_y;            // unused
1599
 
      const Glib::RefPtr<Gtk::TreeSelection> selection
1600
 
        = m_notebooksTree->get_selection();
1601
 
 
1602
 
      if (m_notebooksTree->get_path_at_pos(ev->x, ev->y, p, col,
1603
 
                                           cell_x, cell_y)) {
1604
 
        selection->select(p);
1605
 
      }
1606
 
 
1607
 
      notebooks::Notebook::Ptr notebook = get_selected_notebook ();
1608
 
      if (!notebook)
1609
 
        return true; // Don't pop open a submenu
1610
 
 
1611
 
      Gtk::Menu *menu = dynamic_cast<Gtk::Menu*>(ActionManager::obj().get_widget (
1612
 
                                                 "/NotebooksTreeContextMenu"));
1613
 
 
1614
 
      popup_context_menu_at_location (menu,
1615
 
                                      ev->x, ev->y);
1616
 
      return true;
1617
 
    }
1618
 
    return false;
1619
 
  }
1620
 
 
1621
 
 
1622
 
  bool NoteRecentChanges::on_notebooks_key_pressed(GdkEventKey * ev)
1623
 
  {
1624
 
    switch (ev->keyval) {
1625
 
    case GDK_KEY_Menu:
1626
 
    {
1627
 
      // Pop up the context menu if a notebook is selected
1628
 
      notebooks::Notebook::Ptr notebook = get_selected_notebook ();
1629
 
      if (!notebook || std::tr1::dynamic_pointer_cast<notebooks::SpecialNotebook>(notebook))
1630
 
        break; // Don't pop open a submenu
1631
 
          
1632
 
      Gtk::Menu *menu = dynamic_cast<Gtk::Menu *>(ActionManager::obj().get_widget (
1633
 
                                                    "/NotebooksTreeContextMenu"));
1634
 
      popup_context_menu_at_location (menu, 0, 0);
1635
 
 
1636
 
      break;
1637
 
    }
1638
 
    default:
1639
 
      break;
1640
 
    }
1641
 
 
1642
 
    return false; // Let Escape be handled by the window.
1643
 
  }
1644
 
    
1645
 
  void NoteRecentChanges::on_note_added_to_notebook (const Note & , 
1646
 
                                                     const notebooks::Notebook::Ptr & )
1647
 
  {
1648
 
    restore_matches_window();
1649
 
    update_results ();
1650
 
  }
1651
 
    
1652
 
  void NoteRecentChanges::on_note_removed_from_notebook (const Note & , 
1653
 
                                                       const notebooks::Notebook::Ptr & )
1654
 
  {
1655
 
    restore_matches_window();
1656
 
    update_results ();
1657
 
  }
1658
 
 
 
412
    std::string search_text = get_search_text();
 
413
    if(search_text.empty()) {
 
414
      return;
 
415
    }
 
416
 
 
417
    m_search_notes_widget.perform_search(search_text);
 
418
  }
1659
419
 
1660
420
  std::string NoteRecentChanges::get_search_text()
1661
421
  {
1662
 
    // Entry may be null if search window closes
1663
 
    // early (bug #544996).
1664
 
    if (m_find_combo.get_entry() == NULL) {
1665
 
      return NULL;
1666
 
    }
1667
 
    std::string text = m_find_combo.get_entry()->get_text();
 
422
    std::string text = m_search_entry.get_text();
1668
423
    text = sharp::string_trim(text);
1669
424
    return text;
1670
425
  }
1671
426
 
1672
 
  void NoteRecentChanges::set_search_text(const std::string & value)
1673
 
  {
1674
 
    if (!value.empty()) {
1675
 
      m_find_combo.get_entry()->set_text(value);
1676
 
    }
1677
 
  }
1678
 
  /// <summary>
1679
 
  /// Save the position and size of the RecentChanges window
1680
 
  /// </summary>
1681
 
  void NoteRecentChanges::save_position ()
1682
 
  {
1683
 
    int x;
1684
 
    int y;
1685
 
    int width;
1686
 
    int height;
1687
 
 
1688
 
    get_position(x, y);
1689
 
    get_size(width, height);
1690
 
 
1691
 
    Glib::RefPtr<Gio::Settings> settings = Preferences::obj()
1692
 
      .get_schema_settings(Preferences::SCHEMA_GNOTE);
1693
 
    settings->set_int(Preferences::SEARCH_WINDOW_X_POS, x);
1694
 
    settings->set_int(Preferences::SEARCH_WINDOW_Y_POS, y);
1695
 
    settings->set_int(Preferences::SEARCH_WINDOW_WIDTH, width);
1696
 
    settings->set_int(Preferences::SEARCH_WINDOW_HEIGHT, height);
1697
 
    settings->set_int(Preferences::SEARCH_WINDOW_SPLITTER_POS, m_hpaned.get_position());
1698
 
  }
1699
 
        
1700
 
   
1701
 
  void NoteRecentChanges::restore_position()
1702
 
  {
1703
 
    Glib::RefPtr<Gio::Settings> settings = Preferences::obj()
1704
 
      .get_schema_settings(Preferences::SCHEMA_GNOTE);
1705
 
    int x = settings->get_int(Preferences::SEARCH_WINDOW_X_POS);
1706
 
    int y = settings->get_int(Preferences::SEARCH_WINDOW_Y_POS);
1707
 
    int width = settings->get_int(Preferences::SEARCH_WINDOW_WIDTH);
1708
 
    int height = settings->get_int(Preferences::SEARCH_WINDOW_HEIGHT);
1709
 
    int pos = settings->get_int(Preferences::SEARCH_WINDOW_SPLITTER_POS);
1710
 
 
1711
 
    if((width == 0) || (height == 0)) {
1712
 
      return;
1713
 
    }
1714
 
          
1715
 
    set_default_size(width, height);
1716
 
    move (x, y);
1717
 
    if(pos) {
1718
 
      m_hpaned.set_position(pos);
1719
 
    }
1720
 
  }
1721
 
 
1722
 
 
1723
 
  void NoteRecentChanges::on_exiting_event()
1724
 
  {
1725
 
    save_position();
 
427
  void NoteRecentChanges::update_toolbar(utils::EmbeddableWidget & widget)
 
428
  {
 
429
    bool search = dynamic_cast<SearchNotesWidget*>(&widget) == &m_search_notes_widget;
 
430
    m_all_notes_button->set_sensitive(!search);
 
431
    m_search_entry.set_visible(search);
 
432
  }
 
433
 
 
434
  void NoteRecentChanges::on_show_window_menu(Gtk::Button *button)
 
435
  {
 
436
    std::vector<Gtk::MenuItem*> items;
 
437
    if(dynamic_cast<SearchNotesWidget*>(currently_embedded()) == &m_search_notes_widget) {
 
438
      if(!m_window_menu_search) {
 
439
        m_window_menu_search = make_window_menu(button,
 
440
            make_menu_items(items, IActionManager::obj().get_main_window_search_actions()));
 
441
      }
 
442
      utils::popup_menu(*m_window_menu_search, NULL);
 
443
    }
 
444
    else if(dynamic_cast<NoteWindow*>(currently_embedded())) {
 
445
      if(!m_window_menu_note) {
 
446
        m_window_menu_note = make_window_menu(button,
 
447
            make_menu_items(items, IActionManager::obj().get_main_window_note_actions()));
 
448
      }
 
449
      utils::popup_menu(*m_window_menu_note, NULL);
 
450
    }
 
451
    else {
 
452
      if(!m_window_menu_default) {
 
453
        m_window_menu_default = make_window_menu(button, items);
 
454
      }
 
455
      utils::popup_menu(*m_window_menu_default, NULL);
 
456
    }
 
457
  }
 
458
 
 
459
  Gtk::Menu *NoteRecentChanges::make_window_menu(Gtk::Button *button, const std::vector<Gtk::MenuItem*> & items)
 
460
  {
 
461
    Gtk::Menu *menu = new Gtk::Menu;
 
462
    for(std::vector<Gtk::MenuItem*>::const_iterator iter = items.begin(); iter != items.end(); ++iter) {
 
463
      menu->append(**iter);
 
464
    }
 
465
    if(items.size()) {
 
466
      menu->append(*manage(new Gtk::SeparatorMenuItem));
 
467
    }
 
468
    Gtk::MenuItem *item = manage(new Gtk::MenuItem(_("_Close"), true));
 
469
    item->signal_activate().connect(sigc::mem_fun(*this, &NoteRecentChanges::on_close_window));
 
470
    menu->append(*item);
 
471
    menu->property_attach_widget() = button;
 
472
    menu->show_all();
 
473
    return menu;
 
474
  }
 
475
 
 
476
  std::vector<Gtk::MenuItem*> & NoteRecentChanges::make_menu_items(std::vector<Gtk::MenuItem*> & items,
 
477
    const std::vector<Glib::RefPtr<Gtk::Action> > & actions)
 
478
  {
 
479
    for(std::vector<Glib::RefPtr<Gtk::Action> >::const_iterator iter = actions.begin(); iter != actions.end(); ++iter) {
 
480
      Gtk::MenuItem *item = manage(new Gtk::MenuItem);
 
481
      item->set_related_action(*iter);
 
482
      items.push_back(item);
 
483
    }
 
484
    return items;
 
485
  }
 
486
 
 
487
  void NoteRecentChanges::on_main_window_actions_changed(Gtk::Menu **menu)
 
488
  {
 
489
    if(*menu) {
 
490
      delete *menu;
 
491
      *menu = NULL;
 
492
    }
1726
493
  }
1727
494
 
1728
495
}