1 // ePDFView - A lightweight PDF Viewer.
2 // Copyright (C) 2006, 2007, 2009 Emma's Software.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 
18 #include "epdfview.h"
19 
20 using namespace ePDFView;
21 
22 G_LOCK_DEFINE_STATIC (cancelJob);
23 G_LOCK_DEFINE_STATIC (freeResults);
24 
25 ///
26 /// @brief Constructs a new FindPter object.
27 ///
28 /// @param document The document to find text from.
29 ///
FindPter(IDocument * document)30 FindPter::FindPter (IDocument *document)
31 {
32     g_assert (NULL != document && "Tried to set a NULL document.");
33 
34     m_CurrentMatch = NULL;
35     m_Document = document;
36     m_FindPage = 0;
37     m_FindResults = NULL;
38     m_Job = NULL;
39     m_View = NULL;
40 }
41 
42 ///
43 /// @brief Destroys all dynamically allocated memory for FindPter.
44 ///
~FindPter()45 FindPter::~FindPter ()
46 {
47     m_Document = NULL;
48     // This also deletes the find results.
49     cancelJob ();
50 }
51 
52 ///
53 /// @brief Cancels the find job.
54 ///
55 void
cancelJob()56 FindPter::cancelJob ()
57 {
58     G_LOCK (cancelJob);
59     if ( NULL != m_Job )
60     {
61         m_Job->cancel ();
62         m_Job->enqueue ();
63         // When the job is cancelled, it will just delete itself.
64         m_Job = NULL;
65     }
66     freeFindResults ();
67     G_UNLOCK (cancelJob);
68 }
69 
70 ///
71 /// @brief The Close button has been clicked.
72 ///
73 void
closeActivated()74 FindPter::closeActivated ()
75 {
76     getView ().hide ();
77     cancelJob ();
78     m_Document->notifyFindChanged (m_FindPage, NULL);
79 }
80 
81 ///
82 /// @brief The Find Next button has been clicked.
83 ///
84 void
findNextActivated()85 FindPter::findNextActivated ()
86 {
87     m_CurrentMatch = g_list_next (m_CurrentMatch);
88     if ( NULL != m_CurrentMatch )
89     {
90         DocumentRectangle *rect = ((DocumentRectangle *)m_CurrentMatch->data);
91         m_Document->notifyFindChanged (m_FindPage, rect);
92         m_Document->goToPage (m_FindPage);
93     }
94     else
95     {
96         G_LOCK (cancelJob);
97         if ( NULL != m_Job )
98         {
99             if ( FIND_DIRECTION_BACKWARDS == m_Job->getDirection () )
100             {
101                 m_Job->setCurrentPage (m_Job->getCurrentPage () + 2);
102             }
103             m_Job->setDirection (FIND_DIRECTION_FORWARDS);
104             m_Job->enqueue ();
105         }
106         G_UNLOCK (cancelJob);
107     }
108 }
109 
110 ///
111 /// @brief The Find Previous button has been clicked.
112 ///
113 void
findPreviousActivated()114 FindPter::findPreviousActivated ()
115 {
116     m_CurrentMatch = g_list_previous (m_CurrentMatch);
117     if ( NULL != m_CurrentMatch )
118     {
119         DocumentRectangle *rect = ((DocumentRectangle *)m_CurrentMatch->data);
120         m_Document->notifyFindChanged (m_FindPage, rect);
121         m_Document->goToPage (m_FindPage);
122     }
123     else
124     {
125         G_LOCK (cancelJob);
126         if ( NULL != m_Job )
127         {
128             if ( FIND_DIRECTION_FORWARDS == m_Job->getDirection () )
129             {
130                 m_Job->setCurrentPage (m_Job->getCurrentPage () - 2);
131             }
132             m_Job->setDirection (FIND_DIRECTION_BACKWARDS);
133             m_Job->enqueue ();
134         }
135         G_UNLOCK (cancelJob);
136     }
137 }
138 
139 ///
140 /// @brief Deletes all saved results.
141 ///
142 void
freeFindResults()143 FindPter::freeFindResults ()
144 {
145     G_LOCK (freeResults);
146     for ( GList *item = g_list_first (m_FindResults) ; NULL != item ;
147           item = g_list_next (item) )
148     {
149         DocumentRectangle *rect = (DocumentRectangle *)item->data;
150         delete rect;
151     }
152     g_list_free (m_FindResults);
153     m_FindResults = NULL;
154     G_UNLOCK (freeResults);
155 }
156 
157 ///
158 /// @brief Gets the view that the presenter is controlling.
159 ///
160 /// @return The view that the presenter is controlling.
161 ///
162 IFindView &
getView()163 FindPter::getView ()
164 {
165     g_assert (NULL != m_View && "Tried to get a NULL view.");
166 
167     return *m_View;
168 }
169 
170 ///
171 /// @brief The find has searched on all pages.
172 ///
173 void
notifyFindFinished(gboolean endOfSearch)174 FindPter::notifyFindFinished (gboolean endOfSearch )
175 {
176     G_LOCK (cancelJob);
177     if ( NULL != m_Job )
178     {
179         IFindView &view = getView ();
180         if ( endOfSearch && NULL == m_FindResults )
181         {
182             view.sensitiveFindNext (FALSE);
183             view.sensitiveFindPrevious (FALSE);
184             G_UNLOCK (cancelJob);
185             cancelJob ();
186             G_LOCK (cancelJob);
187             view.setInformationText (_("No Results Found!"));
188             m_Document->notifyFindFinished ();
189             // Clear any previously set rectangle.
190             m_Document->notifyFindChanged (m_FindPage, NULL);
191         }
192         else
193         {
194             gchar *infoText =
195                 g_strdup_printf (_("Searching in page %d of %d..."),
196                                  m_Job->getCurrentPage (),
197                                  m_Document->getNumPages ());
198             view.setInformationText (infoText);
199             g_free (infoText);
200             m_Job->enqueue ();
201         }
202     }
203     G_UNLOCK (cancelJob);
204 }
205 
206 ///
207 /// @brief The find found results.
208 ///
209 /// @param pageNum The number of the page that found results.
210 /// @param results The results that found.
211 /// @param direction The direction it was searching pages when found results.
212 ///
213 void
notifyFindResults(gint pageNum,GList * results,FindDirection direction)214 FindPter::notifyFindResults (gint pageNum, GList *results,
215                              FindDirection direction)
216 {
217     m_FindPage = pageNum;
218     freeFindResults ();
219     G_LOCK (freeResults);
220     m_FindResults = results;
221     G_UNLOCK (freeResults);
222     if ( FIND_DIRECTION_FORWARDS == direction )
223     {
224         m_CurrentMatch = g_list_first (m_FindResults);
225     }
226     else
227     {
228         m_CurrentMatch = g_list_last (m_FindResults);
229     }
230     DocumentRectangle *rect = (DocumentRectangle *)m_CurrentMatch->data;
231     m_Document->notifyFindChanged (m_FindPage, rect);
232     m_Document->goToPage (m_FindPage);
233 
234     IFindView &view = getView ();
235     view.setInformationText ("");
236     view.sensitiveFindNext (TRUE);
237     view.sensitiveFindPrevious (TRUE);
238 }
239 
240 ///
241 /// @brief Sets the view that the presenter will control.
242 ///
243 /// @param view The view that the presenter will control.
244 ///
245 void
setView(IFindView * view)246 FindPter::setView (IFindView *view)
247 {
248     g_assert ( NULL != view && "Tried to set a NULL view.");
249 
250     m_View = view;
251     m_View->setPresenter (this);
252     m_View->sensitiveFindNext (FALSE);
253     m_View->sensitiveFindPrevious (FALSE);
254     m_View->setInformationText ("");
255 }
256 
257 ///
258 /// @brief The text to find has been changed.
259 ///
260 /// This is called by the view when the text to find has been modified. This
261 /// function checks if the text is empty and sets the sensitivity of Find Next
262 /// and Find Previous button according.
263 ///
264 void
textToFindChanged()265 FindPter::textToFindChanged ()
266 {
267     cancelJob ();
268 
269     const gchar *textToFind = getView ().getTextToFind ();
270     if ( 0 != g_ascii_strcasecmp ("", textToFind ) )
271     {
272         // Inform all observers.
273         m_Document->notifyFindStarted ();
274 
275         // Queue a find job.
276         m_Job = new JobFind ();
277         m_Job->setDocument (m_Document);
278         m_Job->setStartingPage (m_Document->getCurrentPageNum ());
279         m_Job->setTextToFind (textToFind);
280         m_Job->setFindPter (this);
281         m_Job->enqueue ();
282     }
283     else
284     {
285         IFindView &view = getView ();
286         view.sensitiveFindNext (FALSE);
287         view.sensitiveFindPrevious (FALSE);
288         // Clear any previously set rectangle.
289         m_Document->notifyFindChanged (m_FindPage, NULL);
290     }
291 }
292