PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/gnome-commander-1.2.8.15/src/gnome-cmd-search-dialog.cc

#
C++ | 993 lines | 667 code | 209 blank | 117 comment | 81 complexity | b7e797435b9682f840e5f09c420c4b5b MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. GNOME Commander - A GNOME based file manager
  3. Copyright (C) 2001-2006 Marcus Bjurman
  4. Copyright (C) 2007-2011 Piotr Eljasiak
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  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. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  16. */
  17. #include <config.h>
  18. #include <sys/types.h>
  19. #include <regex.h>
  20. #include "gnome-cmd-includes.h"
  21. #include "gnome-cmd-data.h"
  22. #include "gnome-cmd-search-dialog.h"
  23. #include "gnome-cmd-dir.h"
  24. #include "gnome-cmd-file-list.h"
  25. #include "gnome-cmd-file-selector.h"
  26. #include "gnome-cmd-main-win.h"
  27. #include "filter.h"
  28. #include "utils.h"
  29. using namespace std;
  30. #if 0
  31. static char *msgs[] = {N_("Search _recursively:"),
  32. N_("_Unlimited depth"),
  33. N_("Current _directory only"),
  34. N_("_Limited depth"),
  35. // N_("Search local directories only"),
  36. N_("Files _not containing text")};
  37. #endif
  38. static GnomeCmdDialogClass *parent_class = NULL;
  39. #define PBAR_MAX 50
  40. #define SEARCH_JUMP_SIZE 4096U
  41. #define SEARCH_BUFFER_SIZE (SEARCH_JUMP_SIZE * 10U)
  42. struct ProtectedData
  43. {
  44. GList *files;
  45. gchar *msg;
  46. GMutex *mutex;
  47. };
  48. struct SearchData
  49. {
  50. const gchar *name_pattern; // the pattern that file names should match to end up in the file list
  51. const gchar *content_pattern; // the pattern that the content of a file should match to end up in the file list
  52. const gchar *dir; // the current dir of the search routine
  53. Filter *name_filter;
  54. regex_t *content_regex;
  55. gboolean content_search; // should we do content search?
  56. gint matches; // the number of matching files
  57. gint context_id; // the context id of the status bar
  58. GnomeCmdSearchDialog *dialog;
  59. gboolean recurse; // should we recurse or just search in the selected directory?
  60. gboolean case_sens;
  61. GList *match_dirs; // the directories which we found matching files in
  62. GnomeCmdDir *start_dir; // the directory to start searching from
  63. GThread *thread;
  64. ProtectedData pdata;
  65. gint update_gui_timeout_id;
  66. gboolean search_done;
  67. gboolean stopped; // stops the search routine if set to TRUE. This is done by the stop_button
  68. gboolean dialog_destroyed; // set when the search dialog is destroyed, also stops the search of course
  69. gchar *search_mem; // memory to search in the content of a file
  70. };
  71. struct SearchFileData
  72. {
  73. gchar *uri_str;
  74. GnomeVFSHandle *handle;
  75. gint offset;
  76. guint len;
  77. };
  78. struct GnomeCmdSearchDialogPrivate
  79. {
  80. SearchData *data; // holds data needed by the search routines
  81. GnomeCmdCon *con;
  82. GtkWidget *pattern_combo;
  83. GtkWidget *dir_browser;
  84. GtkWidget *dir_entry;
  85. GtkWidget *find_text_combo;
  86. GtkWidget *find_text_check;
  87. GnomeCmdFileList *result_list;
  88. GtkWidget *statusbar;
  89. GtkWidget *help_button;
  90. GtkWidget *close_button;
  91. GtkWidget *goto_button;
  92. GtkWidget *stop_button;
  93. GtkWidget *search_button;
  94. GtkWidget *recurse_check;
  95. GtkWidget *case_check;
  96. GtkWidget *pbar;
  97. };
  98. /**
  99. * Puts a string in the statusbar.
  100. *
  101. */
  102. inline void set_statusmsg (SearchData *data, gchar *msg)
  103. {
  104. if (msg)
  105. gtk_statusbar_push (GTK_STATUSBAR (data->dialog->priv->statusbar), data->context_id, msg);
  106. }
  107. inline void search_file_data_free (SearchFileData *searchfile_data)
  108. {
  109. if (searchfile_data->handle)
  110. gnome_vfs_close (searchfile_data->handle);
  111. g_free (searchfile_data->uri_str);
  112. g_free (searchfile_data);
  113. }
  114. /**
  115. * Loads a file in chunks and returns the content.
  116. */
  117. static SearchFileData *read_search_file (SearchData *data, SearchFileData *searchfile_data, GnomeCmdFile *f)
  118. {
  119. g_return_val_if_fail (f != NULL, NULL);
  120. g_return_val_if_fail (f->info != NULL, NULL);
  121. GnomeVFSResult result;
  122. if (!searchfile_data)
  123. {
  124. searchfile_data = g_new0 (SearchFileData, 1);
  125. searchfile_data->uri_str = gnome_cmd_file_get_uri_str (f);
  126. result = gnome_vfs_open (&searchfile_data->handle, searchfile_data->uri_str, GNOME_VFS_OPEN_READ);
  127. if (result != GNOME_VFS_OK)
  128. {
  129. warn_print (_("Failed to open file %s: %s\n"), searchfile_data->uri_str, gnome_vfs_result_to_string (result));
  130. search_file_data_free (searchfile_data);
  131. return NULL;
  132. }
  133. }
  134. // If the stop button was pressed let's abort here
  135. if (data->stopped)
  136. {
  137. search_file_data_free (searchfile_data);
  138. return NULL;
  139. }
  140. if (searchfile_data->len)
  141. {
  142. if ((searchfile_data->offset + searchfile_data->len) >= f->info->size)
  143. { // end, all has been read
  144. search_file_data_free (searchfile_data);
  145. return NULL;
  146. }
  147. else
  148. { // jump a big step backward to give the regex a chance
  149. searchfile_data->offset += searchfile_data->len - SEARCH_JUMP_SIZE;
  150. if (f->info->size < (searchfile_data->offset + (SEARCH_BUFFER_SIZE - 1)))
  151. searchfile_data->len = f->info->size - searchfile_data->offset;
  152. else
  153. searchfile_data->len = SEARCH_BUFFER_SIZE - 1;
  154. }
  155. }
  156. else
  157. { // first time call of this function
  158. if (f->info->size < (SEARCH_BUFFER_SIZE - 1))
  159. searchfile_data->len = f->info->size;
  160. else
  161. searchfile_data->len = SEARCH_BUFFER_SIZE - 1;
  162. }
  163. GnomeVFSFileSize ret;
  164. result = gnome_vfs_seek (searchfile_data->handle, GNOME_VFS_SEEK_START, searchfile_data->offset);
  165. if (result != GNOME_VFS_OK)
  166. {
  167. warn_print (_("Failed to seek in file %s: %s\n"), searchfile_data->uri_str, gnome_vfs_result_to_string (result));
  168. search_file_data_free (searchfile_data);
  169. return NULL;
  170. }
  171. result = gnome_vfs_read (searchfile_data->handle, data->search_mem, searchfile_data->len, &ret);
  172. if (result != GNOME_VFS_OK)
  173. {
  174. warn_print (_("Failed to read from file %s: %s\n"), searchfile_data->uri_str, gnome_vfs_result_to_string (result));
  175. search_file_data_free (searchfile_data);
  176. return NULL;
  177. }
  178. data->search_mem[searchfile_data->len] = '\0';
  179. return searchfile_data;
  180. }
  181. /**
  182. * Determines if the content of a file matches an regexp
  183. *
  184. */
  185. inline gboolean content_matches (GnomeCmdFile *f, SearchData *data)
  186. {
  187. gint ret = REG_NOMATCH;
  188. if (f->info->size > 0)
  189. {
  190. regmatch_t match;
  191. SearchFileData *search_file = NULL;
  192. while ((search_file = read_search_file (data, search_file, f)))
  193. {
  194. ret = regexec (data->content_regex, data->search_mem, 1, &match, 0);
  195. // stop on first match
  196. if (ret != REG_NOMATCH)
  197. break;
  198. }
  199. }
  200. return ret != REG_NOMATCH;
  201. }
  202. /**
  203. * Determines if the name of a file matches an regexp
  204. *
  205. */
  206. inline gboolean name_matches (gchar *name, SearchData *data)
  207. {
  208. return data->name_filter->match(name);
  209. }
  210. /**
  211. * Searches a given directory for files that matches the criteria given by data.
  212. *
  213. */
  214. static void search_dir_r (GnomeCmdDir *dir, SearchData *data)
  215. {
  216. if (!dir)
  217. return;
  218. // update the search status data
  219. if (!data->dialog_destroyed)
  220. {
  221. g_mutex_lock (data->pdata.mutex);
  222. gchar *path = gnome_cmd_file_get_path (GNOME_CMD_FILE (dir));
  223. g_free (data->pdata.msg);
  224. data->pdata.msg = g_strdup_printf (_("Searching in: %s"), path);
  225. g_free (path);
  226. g_mutex_unlock (data->pdata.mutex);
  227. }
  228. // If the stop button was pressed let's abort here
  229. if (data->stopped)
  230. return;
  231. GList *files;
  232. gnome_cmd_dir_list_files (dir, FALSE);
  233. gnome_cmd_dir_get_files (dir, &files);
  234. // Let's iterate through all files
  235. for (GList *tmp=files; tmp; tmp=tmp->next)
  236. {
  237. // If the stop button was pressed let's abort here
  238. if (data->stopped)
  239. return;
  240. GnomeCmdFile *f = (GnomeCmdFile *) tmp->data;
  241. // If the current file is a directory lets continue our recursion
  242. if (GNOME_CMD_IS_DIR (f) && data->recurse)
  243. {
  244. // we don't want to go backwards or follow symlinks
  245. if (!f->is_dotdot && strcmp (f->info->name, ".") != 0 &&
  246. !GNOME_VFS_FILE_INFO_SYMLINK (f->info))
  247. {
  248. GnomeCmdDir *new_dir = GNOME_CMD_DIR (f);
  249. if (new_dir)
  250. {
  251. gnome_cmd_dir_ref (new_dir);
  252. search_dir_r (new_dir, data);
  253. gnome_cmd_dir_unref (new_dir);
  254. }
  255. }
  256. }
  257. // if the file is a regular one it might match the search criteria
  258. else
  259. if (f->info->type == GNOME_VFS_FILE_TYPE_REGULAR)
  260. {
  261. // if the name doesn't match lets go to the next file
  262. if (!name_matches (f->info->name, data))
  263. continue;
  264. // if the user wants to we should do some content matching here
  265. if (data->content_search && !content_matches (f, data))
  266. continue;
  267. // the file matched the search criteria, lets add it to the list
  268. g_mutex_lock (data->pdata.mutex);
  269. data->pdata.files = g_list_append (data->pdata.files, gnome_cmd_file_ref (f));
  270. g_mutex_unlock (data->pdata.mutex);
  271. // also ref each directory that has a matching file
  272. if (g_list_index (data->match_dirs, dir) == -1)
  273. {
  274. gnome_cmd_dir_ref (dir);
  275. data->match_dirs = g_list_append (data->match_dirs, dir);
  276. }
  277. // count the match
  278. data->matches++;
  279. }
  280. }
  281. }
  282. static gpointer perform_search_operation (SearchData *data)
  283. {
  284. // Unref all directories which contained matching files from last search
  285. if (data->match_dirs)
  286. {
  287. g_list_foreach (data->match_dirs, (GFunc) gnome_cmd_dir_unref, NULL);
  288. g_list_free (data->match_dirs);
  289. data->match_dirs = NULL;
  290. }
  291. search_dir_r (data->start_dir, data);
  292. // free regexps
  293. delete data->name_filter;
  294. data->name_filter = NULL;
  295. if (data->content_search)
  296. {
  297. regfree (data->content_regex);
  298. g_free (data->content_regex);
  299. data->content_regex = NULL;
  300. }
  301. gnome_cmd_dir_unref (data->start_dir);
  302. data->start_dir = NULL;
  303. data->dir = NULL;
  304. data->search_done = TRUE;
  305. return NULL;
  306. }
  307. static gboolean update_search_status_widgets (SearchData *data)
  308. {
  309. if (data->pdata.mutex)
  310. g_mutex_lock (data->pdata.mutex);
  311. // Add all files found since last update to the list
  312. for (GList *files = data->pdata.files; files; files = files->next)
  313. data->dialog->priv->result_list->append_file(GNOME_CMD_FILE (files->data));
  314. if (data->pdata.files)
  315. {
  316. gnome_cmd_file_list_free (data->pdata.files);
  317. data->pdata.files = NULL;
  318. }
  319. // Update status bar with the latest message
  320. set_statusmsg (data, data->pdata.msg);
  321. // Update the progress bar
  322. progress_bar_update (data->dialog->priv->pbar, PBAR_MAX);
  323. if (data->pdata.mutex)
  324. g_mutex_unlock (data->pdata.mutex);
  325. if (data->search_done)
  326. {
  327. if (!data->dialog_destroyed)
  328. {
  329. gchar *msg;
  330. if (data->stopped)
  331. msg = g_strdup_printf (
  332. ngettext("Found %d match - search aborted",
  333. "Found %d matches - search aborted",
  334. data->matches),
  335. data->matches);
  336. else
  337. msg = g_strdup_printf (
  338. ngettext(
  339. "Found %d match",
  340. "Found %d matches",
  341. data->matches),
  342. data->matches);
  343. set_statusmsg (data, msg);
  344. g_free (msg);
  345. gtk_widget_set_sensitive (data->dialog->priv->goto_button, data->matches > 0);
  346. gtk_widget_set_sensitive (data->dialog->priv->search_button, TRUE);
  347. gtk_widget_set_sensitive (data->dialog->priv->stop_button, FALSE);
  348. // set focus to result list
  349. gtk_widget_grab_focus (GTK_WIDGET (data->dialog->priv->result_list));
  350. gtk_widget_hide (data->dialog->priv->pbar);
  351. }
  352. // Returning FALSE here stops the timeout callbacks
  353. return FALSE;
  354. }
  355. return TRUE;
  356. }
  357. /*
  358. * This function gets called then the search-dialog is about the be destroyed.
  359. * The function waits for the last search-thread to finish and then frees the
  360. * data structure that has been shared between the search threads and the
  361. * main thread.
  362. */
  363. static gboolean join_thread_func (SearchData *data)
  364. {
  365. if (data->thread)
  366. g_thread_join (data->thread);
  367. if (data->pdata.mutex)
  368. g_mutex_free (data->pdata.mutex);
  369. g_free (data->search_mem);
  370. g_free (data);
  371. return FALSE;
  372. }
  373. static void on_dialog_destroy (GnomeCmdSearchDialog *dialog, gpointer user_data)
  374. {
  375. SearchData *data = dialog->priv->data;
  376. if (data)
  377. {
  378. if (!data->search_done)
  379. g_source_remove (data->update_gui_timeout_id);
  380. // Stop and wait for search thread to exit
  381. data->stopped = TRUE;
  382. data->dialog_destroyed = TRUE;
  383. g_timeout_add (1, (GSourceFunc) join_thread_func, data);
  384. // Unref all directories which contained matching files from last search
  385. if (data->pdata.mutex)
  386. {
  387. g_mutex_lock (data->pdata.mutex);
  388. if (data->match_dirs)
  389. {
  390. g_list_foreach (data->match_dirs, (GFunc) gnome_cmd_dir_unref, NULL);
  391. g_list_free (data->match_dirs);
  392. data->match_dirs = NULL;
  393. }
  394. g_mutex_unlock (data->pdata.mutex);
  395. }
  396. }
  397. }
  398. static void on_dialog_size_allocate (GtkWidget *widget, GtkAllocation *allocation, GnomeCmdSearchDialog *dialog)
  399. {
  400. gnome_cmd_data.search_defaults.width = allocation->width;
  401. gnome_cmd_data.search_defaults.height = allocation->height;
  402. }
  403. static gboolean start_generic_search (GnomeCmdSearchDialog *dialog)
  404. {
  405. SearchData *data = dialog->priv->data;
  406. if (data->thread)
  407. {
  408. g_thread_join (data->thread);
  409. data->thread = NULL;
  410. }
  411. data->dialog = dialog;
  412. data->name_pattern = gtk_combo_box_get_active_text (GTK_COMBO_BOX (dialog->priv->pattern_combo));
  413. data->content_pattern = gtk_combo_box_get_active_text (GTK_COMBO_BOX (dialog->priv->find_text_combo));
  414. data->dir = gtk_entry_get_text (GTK_ENTRY (dialog->priv->dir_entry));
  415. data->context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (data->dialog->priv->statusbar), "info");
  416. data->content_search = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->find_text_check));
  417. data->content_regex = NULL;
  418. data->matches = 0;
  419. data->match_dirs = NULL;
  420. data->stopped = FALSE;
  421. data->dialog_destroyed = FALSE;
  422. data->recurse = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->recurse_check));
  423. data->case_sens = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->case_check));
  424. // Save default settings
  425. gnome_cmd_data.search_defaults.case_sens = data->case_sens;
  426. gnome_cmd_data.search_defaults.recursive = data->recurse;
  427. gnome_cmd_data.search_defaults.name_patterns.add(data->name_pattern);
  428. gnome_cmd_data.search_defaults.directories.add(data->dir);
  429. if (data->content_search)
  430. {
  431. gnome_cmd_data.search_defaults.content_patterns.add(data->content_pattern);
  432. gnome_cmd_data.intviewer_defaults.text_patterns.add(data->content_pattern);
  433. }
  434. dialog->priv->result_list->remove_all_files();
  435. // create an re for file name matching
  436. GtkWidget *regex_radio = lookup_widget (GTK_WIDGET (dialog), "regex_radio");
  437. data->name_filter = new Filter(data->name_pattern, data->case_sens,
  438. gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (regex_radio)) ? Filter::TYPE_REGEX
  439. : Filter::TYPE_FNMATCH);
  440. // if we're going to search through file content create an re for that too
  441. if (data->content_search)
  442. {
  443. data->content_regex = g_new0 (regex_t, 1);
  444. regcomp (data->content_regex, data->content_pattern, data->case_sens ? 0 : REG_ICASE);
  445. }
  446. if (!data->search_mem)
  447. data->search_mem = (gchar *) g_malloc (SEARCH_BUFFER_SIZE);
  448. // start the search
  449. GnomeCmdPath *path = gnome_cmd_con_create_path (dialog->priv->con, data->dir);
  450. data->start_dir = gnome_cmd_dir_new (dialog->priv->con, path);
  451. gnome_cmd_dir_ref (data->start_dir);
  452. data->search_done = FALSE;
  453. if (!data->pdata.mutex)
  454. data->pdata.mutex = g_mutex_new ();
  455. data->thread = g_thread_create ((GThreadFunc) perform_search_operation, data, TRUE, NULL);
  456. gtk_widget_show (data->dialog->priv->pbar);
  457. data->update_gui_timeout_id = g_timeout_add (gnome_cmd_data.gui_update_rate, (GSourceFunc) update_search_status_widgets, data);
  458. return FALSE;
  459. }
  460. /**
  461. * The user has clicked on the search button
  462. *
  463. */
  464. static void on_search (GtkButton *button, GnomeCmdSearchDialog *dialog)
  465. {
  466. g_timeout_add (1, (GSourceFunc) start_generic_search, dialog);
  467. gtk_widget_set_sensitive (dialog->priv->goto_button, FALSE);
  468. gtk_widget_set_sensitive (dialog->priv->stop_button, TRUE);
  469. gtk_widget_set_sensitive (dialog->priv->search_button, FALSE);
  470. }
  471. /**
  472. * The user has clicked on the help button
  473. *
  474. */
  475. static void on_help (GtkButton *button, GnomeCmdSearchDialog *dialog)
  476. {
  477. gnome_cmd_help_display ("gnome-commander.xml", "gnome-commander-search");
  478. }
  479. /**
  480. * The user has clicked on the close button
  481. *
  482. */
  483. static void on_close (GtkButton *button, GnomeCmdSearchDialog *dialog)
  484. {
  485. gtk_widget_destroy (GTK_WIDGET (dialog));
  486. }
  487. /**
  488. * The user has clicked on the stop button
  489. *
  490. */
  491. static void on_stop (GtkButton *button, GnomeCmdSearchDialog *dialog)
  492. {
  493. g_return_if_fail (dialog != NULL);
  494. g_return_if_fail (dialog->priv != NULL);
  495. g_return_if_fail (dialog->priv->data != NULL);
  496. dialog->priv->data->stopped = TRUE;
  497. gtk_widget_set_sensitive (dialog->priv->stop_button, FALSE);
  498. }
  499. // The user has clicked on the "go to" button
  500. static void on_goto (GtkButton *button, GnomeCmdSearchDialog *dialog)
  501. {
  502. GnomeCmdFile *f = dialog->priv->result_list->get_selected_file();
  503. if (!f)
  504. return;
  505. gchar *fpath = gnome_cmd_file_get_path (f);
  506. gchar *dpath = g_path_get_dirname (fpath);
  507. GnomeCmdFileSelector *fs = gnome_cmd_main_win_get_fs (main_win, ACTIVE);
  508. fs->goto_directory(dpath);
  509. fs->file_list()->focus_file(gnome_cmd_file_get_name (f), TRUE);
  510. g_free (fpath);
  511. g_free (dpath);
  512. gtk_widget_destroy (GTK_WIDGET (dialog));
  513. }
  514. inline gboolean handle_list_keypress (GnomeCmdFileList *fl, GdkEventKey *event, GnomeCmdSearchDialog *dialog)
  515. {
  516. switch (event->keyval)
  517. {
  518. case GDK_F3:
  519. gnome_cmd_file_list_view (fl, -1);
  520. return TRUE;
  521. case GDK_F4:
  522. gnome_cmd_file_list_edit (fl);
  523. return TRUE;
  524. }
  525. return FALSE;
  526. }
  527. static gboolean on_list_keypressed (GtkWidget *result_list, GdkEventKey *event, gpointer dialog)
  528. {
  529. if (GNOME_CMD_FILE_LIST (result_list)->key_pressed(event) ||
  530. handle_list_keypress (GNOME_CMD_FILE_LIST (result_list), event, GNOME_CMD_SEARCH_DIALOG (dialog)))
  531. {
  532. stop_kp (GTK_OBJECT (result_list));
  533. return TRUE;
  534. }
  535. return FALSE;
  536. }
  537. // The user has clicked on the "search by content" checkbutton.
  538. static void find_text_toggled (GtkToggleButton *togglebutton, GnomeCmdSearchDialog *dialog)
  539. {
  540. if (gtk_toggle_button_get_active (togglebutton))
  541. {
  542. gtk_widget_set_sensitive (dialog->priv->find_text_combo, TRUE);
  543. gtk_widget_grab_focus (dialog->priv->find_text_combo);
  544. }
  545. else
  546. gtk_widget_set_sensitive (dialog->priv->find_text_combo, FALSE);
  547. }
  548. /*******************************
  549. * Gtk class implementation
  550. *******************************/
  551. static void destroy (GtkObject *object)
  552. {
  553. GnomeCmdSearchDialog *dialog = GNOME_CMD_SEARCH_DIALOG (object);
  554. g_free (dialog->priv);
  555. dialog->priv = NULL;
  556. if (GTK_OBJECT_CLASS (parent_class)->destroy)
  557. (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  558. }
  559. static void map (GtkWidget *widget)
  560. {
  561. if (GTK_WIDGET_CLASS (parent_class)->map != NULL)
  562. GTK_WIDGET_CLASS (parent_class)->map (widget);
  563. }
  564. static void class_init (GnomeCmdSearchDialogClass *klass)
  565. {
  566. GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
  567. GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  568. parent_class = (GnomeCmdDialogClass *) gtk_type_class (gnome_cmd_dialog_get_type ());
  569. object_class->destroy = destroy;
  570. widget_class->map = ::map;
  571. }
  572. /*
  573. * create a label with keyboard shortcut and a widget to activate if shortcut is pressed.
  574. */
  575. inline GtkWidget *create_label_with_mnemonic (GtkWidget *parent, const gchar *text, GtkWidget *for_widget)
  576. {
  577. GtkWidget *label = gtk_label_new_with_mnemonic (text);
  578. if (for_widget)
  579. gtk_label_set_mnemonic_widget (GTK_LABEL (label), for_widget);
  580. gtk_widget_ref (label);
  581. gtk_object_set_data_full (GTK_OBJECT (parent), "label", label, (GtkDestroyNotify) gtk_widget_unref);
  582. gtk_widget_show (label);
  583. gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  584. return label;
  585. }
  586. /*
  587. * create a check_box_button with keyboard shortcut
  588. */
  589. inline GtkWidget *create_check_with_mnemonic (GtkWidget *parent, gchar *text, gchar *name)
  590. {
  591. GtkWidget *btn = gtk_check_button_new_with_mnemonic (text);
  592. gtk_widget_ref (btn);
  593. gtk_object_set_data_full (GTK_OBJECT (parent), name, btn, (GtkDestroyNotify) gtk_widget_unref);
  594. gtk_widget_show (btn);
  595. return btn;
  596. }
  597. /*
  598. * create a GtkRadioButton with keyboard shortcut
  599. */
  600. inline GtkWidget *create_radio_with_mnemonic (GtkWidget *parent, GSList *group, gchar *text, gchar *name)
  601. {
  602. GtkWidget *radio = gtk_radio_button_new_with_mnemonic (group, text);
  603. gtk_widget_ref (radio);
  604. gtk_object_set_data_full (GTK_OBJECT (parent), name, radio, (GtkDestroyNotify) gtk_widget_unref);
  605. gtk_widget_show (radio);
  606. return radio;
  607. }
  608. /*
  609. * create a gtk_combo_box_entry_new_text. gtk_combo is deprecated.
  610. */
  611. inline GtkWidget *create_combo_box_entry (GtkWidget *parent)
  612. {
  613. GtkWidget *combo = gtk_combo_box_entry_new_text ();
  614. gtk_widget_ref (combo);
  615. gtk_object_set_data_full (GTK_OBJECT (parent), "combo", combo, (GtkDestroyNotify) gtk_widget_unref);
  616. gtk_widget_show (combo);
  617. return combo;
  618. }
  619. /*
  620. * callback function for 'g_list_foreach' to add default value to dropdownbox
  621. */
  622. static void combo_box_insert_text (gpointer data, gpointer user_data)
  623. {
  624. gtk_combo_box_append_text (GTK_COMBO_BOX (user_data), (gchar *) data);
  625. }
  626. static void init (GnomeCmdSearchDialog *dialog)
  627. {
  628. GnomeCmdData::SearchConfig &defaults = gnome_cmd_data.search_defaults;
  629. GtkWidget *window;
  630. GtkWidget *vbox;
  631. GtkWidget *hbox;
  632. GtkWidget *table;
  633. GtkWidget *label;
  634. GtkWidget *sw;
  635. GtkWidget *radio;
  636. GtkWidget *pbar;
  637. dialog->priv = g_new0 (GnomeCmdSearchDialogPrivate, 1);
  638. dialog->priv->data = g_new0 (SearchData, 1);
  639. window = GTK_WIDGET (dialog);
  640. gtk_object_set_data (GTK_OBJECT (window), "window", window);
  641. gtk_window_set_title (GTK_WINDOW (window), _("Search..."));
  642. gnome_cmd_dialog_set_resizable (GNOME_CMD_DIALOG (dialog), TRUE);
  643. gtk_window_set_default_size (GTK_WINDOW (window), defaults.width, defaults.height);
  644. vbox = create_vbox (window, FALSE, 0);
  645. gnome_cmd_dialog_add_expanding_category (GNOME_CMD_DIALOG (dialog), vbox);
  646. table = create_table (window, 5, 2);
  647. gtk_table_set_row_spacings (GTK_TABLE (table), 6);
  648. gtk_table_set_col_spacings (GTK_TABLE (table), 6);
  649. gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
  650. // Search for
  651. dialog->priv->pattern_combo = create_combo_box_entry (window);
  652. label = create_label_with_mnemonic (window, _("Search _for: "), dialog->priv->pattern_combo);
  653. table_add (table, label, 0, 0, GTK_FILL);
  654. table_add (table, dialog->priv->pattern_combo, 1, 0, (GtkAttachOptions) (GTK_EXPAND|GTK_FILL));
  655. if (!defaults.name_patterns.empty())
  656. g_list_foreach (defaults.name_patterns.ents, combo_box_insert_text, dialog->priv->pattern_combo);
  657. gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->priv->pattern_combo), 0);
  658. gnome_cmd_dialog_editable_enters (GNOME_CMD_DIALOG (dialog), GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (dialog->priv->pattern_combo))));
  659. // Search in
  660. dialog->priv->dir_browser = create_file_entry (window, "dir_browser", "");
  661. label = create_label_with_mnemonic (window, _("Search _in: "), dialog->priv->dir_browser);
  662. table_add (table, label, 0, 1, GTK_FILL);
  663. table_add (table, dialog->priv->dir_browser, 1, 1, (GtkAttachOptions) (GTK_EXPAND|GTK_FILL));
  664. if (!defaults.directories.empty())
  665. gtk_combo_set_popdown_strings (
  666. GTK_COMBO (gnome_file_entry_gnome_entry (GNOME_FILE_ENTRY (dialog->priv->dir_browser))),
  667. defaults.directories.ents);
  668. dialog->priv->dir_entry = gnome_file_entry_gtk_entry (GNOME_FILE_ENTRY (dialog->priv->dir_browser));
  669. hbox = create_hbox (window, FALSE, 0);
  670. // Recurse check
  671. dialog->priv->recurse_check = create_check_with_mnemonic (window, _("Search _recursively"), "recurse_check");
  672. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->recurse_check), defaults.recursive);
  673. gtk_box_pack_start (GTK_BOX (hbox), dialog->priv->recurse_check, FALSE, FALSE, 0);
  674. // File name matching
  675. radio = create_radio_with_mnemonic (window, NULL, _("Rege_x syntax"), "regex_radio");
  676. gtk_box_pack_end (GTK_BOX (hbox), radio, FALSE, FALSE, 12);
  677. if (gnome_cmd_data.filter_type == Filter::TYPE_REGEX)
  678. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
  679. radio = create_radio_with_mnemonic (window, get_radio_group (radio), _("She_ll syntax"), "shell_radio");
  680. gtk_box_pack_end (GTK_BOX (hbox), radio, FALSE, FALSE, 12);
  681. if (gnome_cmd_data.filter_type == Filter::TYPE_FNMATCH)
  682. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
  683. table_add (table, hbox, 1, 2, GTK_FILL);
  684. // Find text
  685. dialog->priv->find_text_check = create_check_with_mnemonic (window, _("Find _text: "), "find_text");
  686. table_add (table, dialog->priv->find_text_check, 0, 3, GTK_FILL);
  687. dialog->priv->find_text_combo = create_combo_box_entry (window);
  688. table_add (table, dialog->priv->find_text_combo, 1, 3, (GtkAttachOptions) (GTK_EXPAND|GTK_FILL));
  689. gtk_widget_set_sensitive (dialog->priv->find_text_combo, FALSE);
  690. if (!defaults.content_patterns.empty())
  691. g_list_foreach (defaults.content_patterns.ents, combo_box_insert_text, dialog->priv->find_text_combo);
  692. gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->priv->find_text_combo), 0);
  693. gnome_cmd_dialog_editable_enters (GNOME_CMD_DIALOG (dialog), GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (dialog->priv->find_text_combo))));
  694. // Case check
  695. dialog->priv->case_check = create_check_with_mnemonic (window, _("Case sensiti_ve"), "case_check");
  696. gtk_table_attach (GTK_TABLE (table), dialog->priv->case_check, 1, 2, 4, 5,
  697. (GtkAttachOptions) (GTK_FILL),
  698. (GtkAttachOptions) (0), 0, 0);
  699. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->case_check), defaults.case_sens);
  700. dialog->priv->help_button = gnome_cmd_dialog_add_button (GNOME_CMD_DIALOG (dialog), GTK_STOCK_HELP, GTK_SIGNAL_FUNC (on_help), dialog);
  701. dialog->priv->close_button = gnome_cmd_dialog_add_button (GNOME_CMD_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_SIGNAL_FUNC (on_close), dialog);
  702. dialog->priv->goto_button = gnome_cmd_dialog_add_button (GNOME_CMD_DIALOG (dialog), _("_Go to"), GTK_SIGNAL_FUNC (on_goto), dialog);
  703. dialog->priv->stop_button = gnome_cmd_dialog_add_button (GNOME_CMD_DIALOG (dialog), GTK_STOCK_STOP, GTK_SIGNAL_FUNC (on_stop), dialog);
  704. dialog->priv->search_button = gnome_cmd_dialog_add_button (GNOME_CMD_DIALOG (dialog), GTK_STOCK_FIND, GTK_SIGNAL_FUNC (on_search), dialog);
  705. gtk_widget_set_sensitive (dialog->priv->stop_button, FALSE);
  706. gtk_widget_set_sensitive (dialog->priv->goto_button, FALSE);
  707. sw = gtk_scrolled_window_new (NULL, NULL);
  708. gtk_widget_ref (sw);
  709. gtk_object_set_data_full (GTK_OBJECT (window), "sw", sw, (GtkDestroyNotify) gtk_widget_unref);
  710. gtk_widget_show (sw);
  711. gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
  712. gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  713. dialog->priv->result_list = new GnomeCmdFileList;
  714. gtk_widget_ref (GTK_WIDGET (dialog->priv->result_list));
  715. gtk_object_set_data_full (GTK_OBJECT (window), "result_list", GTK_WIDGET (dialog->priv->result_list), (GtkDestroyNotify) gtk_widget_unref);
  716. gtk_widget_set_size_request (GTK_WIDGET (dialog->priv->result_list), -1, 200);
  717. gtk_widget_show (GTK_WIDGET (dialog->priv->result_list));
  718. gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (dialog->priv->result_list));
  719. gtk_container_set_border_width (GTK_CONTAINER (dialog->priv->result_list), 4);
  720. dialog->priv->statusbar = gtk_statusbar_new ();
  721. gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (dialog->priv->statusbar), FALSE);
  722. gtk_widget_ref (dialog->priv->statusbar);
  723. gtk_object_set_data_full (GTK_OBJECT (window), "statusbar", dialog->priv->statusbar,
  724. (GtkDestroyNotify) gtk_widget_unref);
  725. gtk_widget_show (dialog->priv->statusbar);
  726. gtk_box_pack_start (GTK_BOX (vbox), dialog->priv->statusbar, FALSE, TRUE, 0);
  727. pbar = create_progress_bar (window);
  728. gtk_widget_hide (pbar);
  729. gtk_progress_set_show_text (GTK_PROGRESS (pbar), FALSE);
  730. gtk_progress_set_activity_mode (GTK_PROGRESS (pbar), TRUE);
  731. gtk_progress_configure (GTK_PROGRESS (pbar), 0, 0, PBAR_MAX);
  732. gtk_box_pack_start (GTK_BOX (dialog->priv->statusbar), pbar, FALSE, TRUE, 0);
  733. dialog->priv->pbar = pbar;
  734. g_signal_connect (G_OBJECT (dialog), "destroy", G_CALLBACK (on_dialog_destroy), NULL);
  735. g_signal_connect (G_OBJECT (dialog), "size-allocate", G_CALLBACK (on_dialog_size_allocate), NULL);
  736. g_signal_connect (G_OBJECT (dialog->priv->result_list), "key-press-event", G_CALLBACK (on_list_keypressed), dialog);
  737. gtk_signal_connect (GTK_OBJECT (dialog->priv->find_text_check), "toggled", GTK_SIGNAL_FUNC (find_text_toggled), dialog);
  738. gtk_window_set_keep_above (GTK_WINDOW (dialog), FALSE);
  739. gtk_widget_grab_focus (dialog->priv->pattern_combo);
  740. dialog->priv->result_list->update_style();
  741. }
  742. /***********************************
  743. * Public functions
  744. ***********************************/
  745. GtkType gnome_cmd_search_dialog_get_type ()
  746. {
  747. static GtkType dlg_type = 0;
  748. if (dlg_type == 0)
  749. {
  750. GtkTypeInfo dlg_info =
  751. {
  752. "GnomeCmdSearchDialog",
  753. sizeof (GnomeCmdSearchDialog),
  754. sizeof (GnomeCmdSearchDialogClass),
  755. (GtkClassInitFunc) class_init,
  756. (GtkObjectInitFunc) init,
  757. /* reserved_1 */ NULL,
  758. /* reserved_2 */ NULL,
  759. (GtkClassInitFunc) NULL
  760. };
  761. dlg_type = gtk_type_unique (gnome_cmd_dialog_get_type (), &dlg_info);
  762. }
  763. return dlg_type;
  764. }
  765. GtkWidget *gnome_cmd_search_dialog_new (GnomeCmdDir *default_dir)
  766. {
  767. gchar *new_path;
  768. GnomeCmdSearchDialog *dialog = (GnomeCmdSearchDialog *) gtk_type_new (gnome_cmd_search_dialog_get_type ());
  769. gchar *path = gnome_cmd_dir_is_local (default_dir) ? gnome_cmd_file_get_real_path (GNOME_CMD_FILE (default_dir)) : gnome_cmd_file_get_path (GNOME_CMD_FILE (default_dir));
  770. if (path[strlen(path)-1] != '/')
  771. {
  772. new_path = g_strdup_printf ("%s/", path);
  773. g_free (path);
  774. }
  775. else
  776. new_path = path;
  777. gtk_entry_set_text (GTK_ENTRY (dialog->priv->dir_entry), new_path);
  778. g_free (new_path);
  779. dialog->priv->con = gnome_cmd_dir_get_connection (default_dir);
  780. return GTK_WIDGET (dialog);
  781. }