1999-09-09 Federico Mena Quintero <federico@redhat.com>
[midnight-commander.git] / src / find.c
blobb513a653accd0769f9b9c761b494e769ae69bbbc
1 /* Find file command for the Midnight Commander
2 Copyright (C) The Free Software Foundation
3 Written 1995 by Miguel de Icaza
5 Complete rewrote.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include <config.h>
22 #include "tty.h"
23 #include <string.h>
24 #include <stdio.h>
25 #ifdef NEEDS_IO_H
26 # include <io.h>
27 #endif
28 #include <sys/stat.h>
29 #include <sys/param.h>
30 #include <fcntl.h>
31 #include <ctype.h>
32 #include "global.h"
33 #include "win.h"
34 #include "color.h"
35 #include "global.h"
36 #include "setup.h"
37 #include "find.h"
39 /* Dialog manager and widgets */
40 #include "dlg.h"
41 #include "widget.h"
43 #include "dialog.h" /* For do_refresh() */
44 #define DIR_H_INCLUDE_HANDLE_DIRENT
45 #include "dir.h"
46 #include "panel.h" /* current_panel */
47 #include "main.h" /* do_cd, try_to_select */
48 #include "wtools.h"
49 #include "tree.h"
50 #include "cmd.h" /* view_file_at_line */
51 #include "../vfs/vfs.h"
53 #ifndef PORT_HAS_FLUSH_EVENTS
54 # define x_flush_events()
55 #endif
57 /* Size of the find parameters window */
58 #define FIND_Y 12
59 static int FIND_X = 50;
61 /* Size of the find window */
62 #define FIND2_Y LINES-4
63 static int FIND2_X = 64;
65 #ifdef HAVE_X
66 # define FIND2_X_USE 35
67 #else
68 # define FIND2_X_USE FIND2_X-20
69 #endif
71 /* A couple of extra messages we need */
72 enum {
73 B_STOP = B_USER + 1,
74 B_AGAIN,
75 B_PANELIZE,
76 B_TREE,
77 B_VIEW
80 /* A list of directories to be ignores, separated with ':' */
81 char *find_ignore_dirs = 0;
83 static WInput *in_start; /* Start path */
84 static WInput *in_name; /* Pattern to search */
85 static WInput *in_with; /* text inside filename */
86 static int running = 0; /* nice flag */
87 static char *find_pattern; /* Pattern to search */
88 static char *content_pattern; /* pattern to search inside files */
89 static int count; /* Number of files displayed */
90 static int matches; /* Number of matches */
91 static int is_start; /* Status of the start/stop toggle button */
92 int max_loops_in_idle = 10;
93 static char *old_dir;
95 static Dlg_head *find_dlg; /* The dialog */
97 #ifdef HAVE_GNOME
98 static GtkWidget *g_find_dlg;
99 static GtkWidget *g_status_label;
100 static GtkWidget *g_clist;
101 static GtkWidget *g_start_stop;
102 static GtkWidget *g_start_stop_label;
103 static GtkWidget *g_view, *g_edit;
104 static GtkWidget *g_panelize;
105 static int current_row;
106 static int idle_tag;
107 static int stop;
108 #else
109 static WButton *stop_button; /* pointer to the stop button */
110 static WLabel *status_label; /* Finished, Searching etc. */
111 static WListbox *find_list; /* Listbox with the file list */
112 #endif
114 /* For nice updating */
115 static char *rotating_dash = "|/-\\";
117 /* This keeps track of the directory stack */
118 typedef struct dir_stack {
119 char *name;
120 struct dir_stack *prev;
121 } dir_stack ;
123 dir_stack *dir_stack_base = 0;
125 static struct {
126 char* text;
127 int len; /* length including space and brackets */
128 int x;
129 } fbuts [] = {
130 { N_("&Suspend"), 11, 29 },
131 { N_("Con&tinue"), 12, 29 },
132 { N_("&Chdir"), 11, 3 },
133 { N_("&Again"), 9, 17 },
134 { N_("&Quit"), 8, 43 },
135 { N_("Pane&lize"), 12, 3 },
136 { N_("&View - F3"), 13, 20 },
137 { N_("&Edit - F4"), 13, 38 }
140 static char *add_to_list (char *text, void *closure);
141 static void stop_idle (void *data);
142 static void status_update (char *text);
143 static void get_list_info (char **file, char **dir);
146 * find_parameters: gets information from the user
148 * If the return value is true, then the following holds:
150 * START_DIR and PATTERN are pointers to char * and upon return they
151 * contain the information provided by the user.
153 * CONTENT holds a strdup of the contents specified by the user if he
154 * asked for them or 0 if not (note, this is different from the
155 * behavior for the other two parameters.
159 static int
160 find_parameters (char **start_dir, char **pattern, char **content)
162 int return_value;
163 #ifndef HAVE_GNOME
164 char *temp_dir;
165 #endif
166 static char *in_contents = NULL;
167 static char *in_start_dir = NULL;
168 static char *in_start_name = NULL;
170 static char* labs[] = {N_("Start at:"), N_("Filename:"), N_("Content: ")};
171 static char* buts[] = {N_("&Ok"), N_("&Tree"), N_("&Cancel")};
172 static int ilen = 30, istart = 14;
173 static int b0 = 3, b1 = 16, b2 = 36;
175 #ifdef ENABLE_NLS
176 static int i18n_flag = 0;
178 if (!i18n_flag)
180 register int i = sizeof(labs)/sizeof(labs[0]);
181 int l1, maxlen = 0;
183 while (i--)
185 l1 = strlen (labs [i] = _(labs [i]));
186 if (l1 > maxlen)
187 maxlen = l1;
189 i = maxlen + ilen + 7;
190 if (i > FIND_X)
191 FIND_X = i;
193 for (i = sizeof(buts)/sizeof(buts[0]), l1 = 0; i--; )
195 l1 += strlen (buts [i] = _(buts [i]));
197 l1 += 21;
198 if (l1 > FIND_X)
199 FIND_X = l1;
201 ilen = FIND_X - 7 - maxlen; /* for the case of very long buttons :) */
202 istart = FIND_X - 3 - ilen;
204 b1 = b0 + strlen(buts[0]) + 7;
205 b2 = FIND_X - (strlen(buts[2]) + 6);
207 i18n_flag = 1;
210 #endif /* ENABLE_NLS */
212 find_par_start:
213 if (!in_start_dir)
214 in_start_dir = g_strdup (".");
215 if (!in_start_name)
216 in_start_name = g_strdup (easy_patterns ? "*" : ".");
217 if (!in_contents)
218 in_contents = g_strdup ("");
220 find_dlg = create_dlg (0, 0, FIND_Y, FIND_X, dialog_colors,
221 common_dialog_callback, "[Find File]", "findfile",
222 DLG_CENTER | DLG_GRID);
223 x_set_dialog_title (find_dlg, _("Find File"));
225 add_widgetl (find_dlg, button_new (9, b2, B_CANCEL, NORMAL_BUTTON,
226 buts[2], 0 ,0, "cancel"), XV_WLAY_RIGHTOF);
227 #ifndef HAVE_GNOME
228 add_widgetl (find_dlg, button_new (9, b1, B_TREE, NORMAL_BUTTON,
229 buts[1], 0, 0, "tree"), XV_WLAY_RIGHTOF);
230 #endif
231 add_widgetl (find_dlg, button_new (9, b0, B_ENTER, DEFPUSH_BUTTON,
232 buts[0], 0, 0, "ok"), XV_WLAY_CENTERROW);
234 in_with = input_new (7, istart, INPUT_COLOR, ilen, in_contents, "content");
235 add_widgetl (find_dlg, in_with, XV_WLAY_BELOWOF);
237 in_name = input_new (5, istart, INPUT_COLOR, ilen, in_start_name, "name");
238 add_widgetl (find_dlg, in_name, XV_WLAY_BELOWOF);
240 in_start = input_new (3, istart, INPUT_COLOR, ilen, in_start_dir, "start");
241 add_widgetl (find_dlg, in_start, XV_WLAY_NEXTCOLUMN);
243 add_widgetl (find_dlg, label_new (7, 3, labs[2], "label-cont"), XV_WLAY_BELOWOF);
244 add_widgetl (find_dlg, label_new (5, 3, labs[1], "label-file"), XV_WLAY_BELOWOF);
245 add_widgetl (find_dlg, label_new (3, 3, labs[0], "label-start"), XV_WLAY_NEXTCOLUMN);
247 run_dlg (find_dlg);
249 switch (find_dlg->ret_value){
250 case B_CANCEL:
251 return_value = 0;
252 break;
254 #ifndef HAVE_GNOME
255 case B_TREE:
256 temp_dir = g_strdup (in_start->buffer);
257 destroy_dlg (find_dlg);
258 g_free (in_start_dir);
259 if (strcmp (temp_dir, ".") == 0){
260 g_free (temp_dir);
261 temp_dir = g_strdup (cpanel->cwd);
263 in_start_dir = tree (temp_dir);
264 if (in_start_dir)
265 g_free (temp_dir);
266 else
267 in_start_dir = temp_dir;
268 /* Warning: Dreadful goto */
269 goto find_par_start;
270 break;
271 #endif
273 default:
274 return_value = 1;
275 *start_dir = g_strdup (in_start->buffer);
276 *pattern = g_strdup (in_name->buffer);
278 g_free (in_contents);
279 if (in_with->buffer [0]){
280 *content = g_strdup (in_with->buffer);
281 in_contents = g_strdup (*content);
282 } else
283 *content = in_contents = NULL;
285 g_free (in_start_dir);
286 in_start_dir = g_strdup (*start_dir);
287 g_free (in_start_name);
288 in_start_name = g_strdup (*pattern);
291 destroy_dlg (find_dlg);
293 return return_value;
296 static void
297 push_directory (char *dir)
299 dir_stack *new;
301 new = g_new (dir_stack, 1);
302 new->name = g_strdup (dir);
303 new->prev = dir_stack_base;
304 dir_stack_base = new;
307 static char*
308 pop_directory (void)
310 char *name;
311 dir_stack *next;
313 if (dir_stack_base){
314 name = dir_stack_base->name;
315 next = dir_stack_base->prev;
316 g_free (dir_stack_base);
317 dir_stack_base = next;
318 return name;
319 } else
320 return 0;
323 static void
324 insert_file (char *dir, char *file)
326 char *tmp_name;
327 static char *dirname;
328 int i;
330 if (dir [0] == PATH_SEP && dir [1] == PATH_SEP)
331 dir++;
332 i = strlen (dir);
333 if (i){
334 if (dir [i - 1] != PATH_SEP){
335 dir [i] = PATH_SEP;
336 dir [i + 1] = 0;
340 if (old_dir){
341 if (strcmp (old_dir, dir)){
342 g_free (old_dir);
343 old_dir = g_strdup (dir);
344 dirname = add_to_list (dir, NULL);
346 } else {
347 old_dir = g_strdup (dir);
348 dirname = add_to_list (dir, NULL);
351 tmp_name = g_strconcat (" ", file, NULL);
352 add_to_list (tmp_name, dirname);
353 g_free (tmp_name);
356 static void
357 find_add_match (Dlg_head *h, char *dir, char *file)
359 int p = ++matches & 7;
361 insert_file (dir, file);
363 #ifndef HAVE_GNOME
364 /* Scroll nicely */
365 if (!p)
366 listbox_select_last (find_list, 1);
367 else
368 listbox_select_last (find_list, 0);
369 #endif
371 #ifndef HAVE_X
372 /* Updates the current listing */
373 send_message (h, &find_list->widget, WIDGET_DRAW, 0);
374 if (p == 7)
375 mc_refresh ();
376 #endif
379 static char *
380 locate_egrep (void)
382 /* commented out because mc runs egrep via execvp() which searches PATH
383 itself. It is likely that egrep in PATH is better than in /bin */
384 #if 0
385 char *paths [] = {
386 "/bin/egrep",
387 "/usr/bin/egrep",
388 "/sbin/egrep",
389 "/usr/sbin/egrep",
390 NULL
392 struct stat s;
393 char **p;
395 for (p = &paths [0]; *p; p++){
396 if (stat (*p, &s) == 0)
397 return *p;
399 #endif
400 return "egrep";
404 * search_content:
406 * Search with egrep the global (FIXME) content_pattern string in the
407 * DIRECTORY/FILE. It will add the found entries to the find listbox.
409 static void
410 search_content (Dlg_head *h, char *directory, char *filename)
412 struct stat s;
413 char buffer [BUF_SMALL];
414 char *fname, *p;
415 int file_fd, pipe, ignoring;
416 char c;
417 int i;
418 pid_t pid;
419 static char *egrep_path;
421 fname = concat_dir_and_file (directory, filename);
423 if (mc_stat (fname, &s) != 0 && !S_ISREG (s.st_mode)){
424 g_free (fname);
425 return;
427 if (!S_ISREG (s.st_mode)){
428 g_free (fname);
429 return;
432 file_fd = mc_open (fname, O_RDONLY);
433 g_free (fname);
435 if (file_fd == -1)
436 return;
438 if (!egrep_path)
439 egrep_path = locate_egrep ();
441 #ifndef GREP_STDIN
442 pipe = mc_doublepopen (file_fd, -1, &pid, egrep_path, egrep_path, "-n", content_pattern, NULL);
443 #else /* GREP_STDIN */
444 pipe = mc_doublepopen (file_fd, -1, &pid, egrep_path, egrep_path, "-n", content_pattern, "-", NULL);
445 #endif /* GREP STDIN */
447 if (pipe == -1){
448 mc_close (file_fd);
449 return;
452 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), name_trunc (filename, FIND2_X_USE));
454 status_update (buffer);
455 mc_refresh ();
456 p = buffer;
457 ignoring = 0;
459 enable_interrupt_key ();
460 got_interrupt ();
461 while (1){
462 i = read (pipe, &c, 1);
463 if (i != 1)
464 break;
466 if (c == '\n'){
467 p = buffer;
468 ignoring = 0;
470 if (ignoring)
471 continue;
473 if (c == ':'){
474 char *the_name;
476 *p = 0;
477 ignoring = 1;
478 the_name = g_strconcat (buffer, ":", filename, NULL);
479 find_add_match (h, directory, the_name);
480 g_free (the_name);
481 } else {
482 if (p - buffer < (sizeof (buffer)-1) && ISASCII (c) && isdigit (c))
483 *p++ = c;
484 else
485 *p = 0;
488 disable_interrupt_key ();
489 if (i == -1)
490 message (1, _(" Find/read "), _(" Problem reading from child "));
492 mc_doublepclose (pipe, pid);
493 mc_close (file_fd);
496 static int
497 do_search (struct Dlg_head *h)
499 static struct dirent *dp = 0;
500 static DIR *dirp = 0;
501 static char directory [MC_MAXPATHLEN+2];
502 struct stat tmp_stat;
503 #ifndef HAVE_X
504 static int pos;
505 #endif
506 static int subdirs_left = 0;
507 char *tmp_name; /* For bulding file names */
509 if (!h) { /* someone forces me to close dirp */
510 if (dirp) {
511 mc_closedir (dirp);
512 dirp = 0;
514 dp = 0;
515 return 1;
517 #ifndef HAVE_X
518 do_search_begin:
519 #endif
520 while (!dp){
522 if (dirp){
523 mc_closedir (dirp);
524 dirp = 0;
527 while (!dirp){
528 char *tmp;
530 #ifndef HAVE_X
531 attrset (REVERSE_COLOR);
532 #endif
533 while (1) {
534 tmp = pop_directory ();
535 if (!tmp){
536 running = 0;
537 status_update (_("Finished"));
538 stop_idle (h);
539 return 0;
541 if (find_ignore_dirs){
542 int found;
543 char *temp_dir = g_strconcat (":", tmp, ":", NULL);
545 found = strstr (find_ignore_dirs, temp_dir) != 0;
546 g_free (temp_dir);
547 if (found)
548 g_free (tmp);
549 else
550 break;
551 } else
552 break;
555 strcpy (directory, tmp);
556 g_free (tmp);
558 if (verbose){
559 char buffer [BUF_SMALL];
561 g_snprintf (buffer, sizeof (buffer), _("Searching %s"), name_trunc (directory, FIND2_X_USE));
562 status_update (buffer);
564 /* mc_stat should not be called after mc_opendir
565 because vfs_s_opendir modifies the st_nlink
567 mc_stat (directory, &tmp_stat);
568 subdirs_left = tmp_stat.st_nlink - 2;
569 /* Commented out as unnecessary
570 if (subdirs_left < 0)
571 subdirs_left = MAXINT;
573 dirp = mc_opendir (directory);
575 dp = mc_readdir (dirp);
578 if (strcmp (dp->d_name, ".") == 0 ||
579 strcmp (dp->d_name, "..") == 0){
580 dp = mc_readdir (dirp);
581 return 1;
584 tmp_name = concat_dir_and_file (directory, dp->d_name);
586 if (subdirs_left){
587 mc_lstat (tmp_name, &tmp_stat);
588 if (S_ISDIR (tmp_stat.st_mode)){
589 push_directory (tmp_name);
590 subdirs_left--;
594 if (regexp_match (find_pattern, dp->d_name, match_file)){
595 if (content_pattern)
596 search_content (h, directory, dp->d_name);
597 else
598 find_add_match (h, directory, dp->d_name);
601 g_free (tmp_name);
602 dp = mc_readdir (dirp);
604 /* Displays the nice dot */
605 count++;
606 if (!(count & 31)){
607 if (verbose){
608 #ifndef HAVE_X
609 pos = (pos + 1) % 4;
610 attrset (NORMALC);
611 dlg_move (h, FIND2_Y-6, FIND2_X - 4);
612 addch (rotating_dash [pos]);
613 mc_refresh ();
615 } else
616 goto do_search_begin;
617 #else
620 #endif
621 x_flush_events ();
622 return 1;
625 static void
626 init_find_vars (void)
628 char *dir;
630 if (old_dir){
631 g_free (old_dir);
632 old_dir = 0;
634 count = 0;
635 matches = 0;
637 /* Remove all the items in the stack */
638 while ((dir = pop_directory ()) != NULL)
639 g_free (dir);
642 static void
643 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
645 char *fullname, *filename;
646 int line;
648 if (content_pattern){
649 filename = strchr (file + 4, ':') + 1;
650 line = atoi (file + 4);
651 } else {
652 filename = file + 4;
653 line = 0;
655 if (dir [0] == '.' && dir [1] == 0)
656 fullname = g_strdup (filename);
657 else if (dir [0] == '.' && dir [1] == PATH_SEP)
658 fullname = concat_dir_and_file (dir+2, filename);
659 else
660 fullname = concat_dir_and_file (dir, filename);
662 if (edit)
663 do_edit_at_line (fullname, line);
664 else
665 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
666 g_free (fullname);
669 #ifdef HAVE_GNOME
670 static void
671 select_row (GtkCList *clist, gint row, gint column, GdkEvent *event)
673 gtk_widget_set_sensitive (g_edit, TRUE);
674 gtk_widget_set_sensitive (g_view, TRUE);
675 current_row = row;
678 static void
679 find_do_chdir (void)
681 gtk_idle_remove (idle_tag);
682 idle_tag = 0;
683 stop = B_ENTER;
684 gtk_main_quit ();
687 static void
688 find_do_again (void)
690 gtk_idle_remove (idle_tag);
691 idle_tag = 0;
692 stop = B_AGAIN;
693 gtk_main_quit ();
696 static void
697 find_do_panelize (void)
699 gtk_idle_remove (idle_tag);
700 idle_tag = 0;
701 stop = B_PANELIZE;
702 gtk_main_quit ();}
705 static void
706 find_start_stop (void)
709 if (is_start){
710 idle_tag = gtk_idle_add ((GtkFunction)do_search, g_find_dlg);
711 } else {
712 gtk_idle_remove (idle_tag);
713 idle_tag = 0;
716 gtk_label_set_text (GTK_LABEL (g_start_stop_label),
717 is_start ? _("Suspend") : _("Restart"));
718 is_start = !is_start;
719 status_update (is_start ? _("Stopped") : _("Searching"));
723 static void
724 find_do_view (void)
726 char *file, *dir;
728 get_list_info (&file, &dir);
730 find_do_view_edit (0, 0, dir, file);
733 static void
734 find_do_edit (void)
736 char *file, *dir;
738 get_list_info (&file, &dir);
740 find_do_view_edit (0, 1, dir, file);
743 static void
744 setup_gui (void)
746 GtkWidget *sw, *b1, *b2, *b3;
747 GtkWidget *box, *box2;
749 g_find_dlg = gnome_dialog_new (
750 _("Find file"),
751 GNOME_STOCK_BUTTON_OK,
752 NULL);
754 /* The buttons */
755 b1 = gtk_button_new_with_label (_("Change to this directory"));
756 b2 = gtk_button_new_with_label (_("Search again"));
757 g_start_stop_label = gtk_label_new (_("Suspend"));
758 g_start_stop = gtk_button_new ();
759 gtk_container_add (GTK_CONTAINER (g_start_stop), g_start_stop_label);
761 g_view = gtk_button_new_with_label (_("View this file"));
762 g_edit = gtk_button_new_with_label (_("Edit this file"));
763 g_panelize = gtk_button_new_with_label (_("Send the results to a Panel"));
765 box = gtk_hbox_new (TRUE, GNOME_PAD);
766 gtk_box_pack_start (GTK_BOX (box), b1, 0, 1, 0);
767 gtk_box_pack_start (GTK_BOX (box), b2, 0, 1, 0);
768 gtk_box_pack_start (GTK_BOX (box), g_start_stop, 0, 1, 0);
770 /* RECOONECT _("Panelize contents"), */
771 /* _("View"),
772 _("Edit"), */
774 sw = gtk_scrolled_window_new (NULL, NULL);
775 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
776 GTK_POLICY_NEVER,
777 GTK_POLICY_AUTOMATIC);
778 g_clist = gtk_clist_new (1);
779 gtk_clist_set_selection_mode (GTK_CLIST (g_clist), GTK_SELECTION_SINGLE);
780 gtk_widget_set_usize (g_clist, -1, 200);
781 gtk_container_add (GTK_CONTAINER (sw), g_clist);
782 gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (g_find_dlg)->vbox),
783 sw, TRUE, TRUE, GNOME_PAD_SMALL);
785 current_row = -1;
786 stop = 0;
787 gtk_signal_connect (GTK_OBJECT (g_clist), "select_row",
788 GTK_SIGNAL_FUNC (select_row), NULL);
791 * Connect the buttons
793 gtk_signal_connect (
794 GTK_OBJECT (b1), "clicked", GTK_SIGNAL_FUNC (find_do_chdir), NULL);
795 gtk_signal_connect (
796 GTK_OBJECT (b2), "clicked", GTK_SIGNAL_FUNC (find_do_again), NULL);
797 gtk_signal_connect (
798 GTK_OBJECT (g_start_stop), "clicked", GTK_SIGNAL_FUNC (find_start_stop), NULL);
799 gtk_signal_connect (
800 GTK_OBJECT (g_panelize), "clicked", GTK_SIGNAL_FUNC (find_do_panelize), NULL);
803 * View/edit buttons
805 gtk_signal_connect (
806 GTK_OBJECT (g_view), "clicked", GTK_SIGNAL_FUNC (find_do_view), NULL);
807 gtk_signal_connect (
808 GTK_OBJECT (g_edit), "clicked", GTK_SIGNAL_FUNC (find_do_edit), NULL);
810 gtk_widget_set_sensitive (g_view, FALSE);
811 gtk_widget_set_sensitive (g_edit, FALSE);
812 box2 = gtk_hbox_new (1, GNOME_PAD + GNOME_PAD);
813 gtk_box_pack_start (GTK_BOX (box2), g_view, 0, 0, 0);
814 gtk_box_pack_start (GTK_BOX (box2), g_edit, 0, 0, 0);
816 gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (g_find_dlg)->vbox),
817 box, TRUE, TRUE, GNOME_PAD_SMALL);
818 gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (g_find_dlg)->vbox),
819 box2, TRUE, TRUE, GNOME_PAD_SMALL);
821 g_status_label = gtk_label_new (_("Searching"));
822 gtk_misc_set_alignment (GTK_MISC (g_status_label), 0.0, 0.5);
823 gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (g_find_dlg)->vbox),
824 g_status_label, TRUE, TRUE, GNOME_PAD_SMALL);
826 gtk_widget_show_all (g_find_dlg);
827 gtk_widget_hide (GTK_WIDGET (g_view));
828 gtk_widget_hide (GTK_WIDGET (g_edit));
831 static int
832 run_process ()
834 idle_tag = gtk_idle_add ((GtkFunction)do_search, g_find_dlg);
836 gnome_dialog_run (GNOME_DIALOG (g_find_dlg));
837 g_start_stop = NULL;
839 return stop;
842 static void
843 kill_gui ()
845 gtk_object_destroy (GTK_OBJECT (g_find_dlg));
848 static void
849 stop_idle (void *data)
851 if (g_start_stop)
852 gtk_widget_set_sensitive (GTK_WIDGET (g_start_stop), FALSE);
855 static void
856 status_update (char *text)
858 gtk_label_set_text (GTK_LABEL (g_status_label), text);
859 x_flush_events ();
862 static char *
863 add_to_list (char *text, void *data)
865 int row;
866 char *texts [1];
868 texts [0] = text;
870 row = gtk_clist_append (GTK_CLIST (g_clist), texts);
871 gtk_clist_set_row_data (GTK_CLIST (g_clist), row, data);
872 #if 1
873 if (gtk_clist_row_is_visible (GTK_CLIST (g_clist), row) != GTK_VISIBILITY_FULL)
874 gtk_clist_moveto (GTK_CLIST (g_clist), row, 0, 0.5, 0.0);
875 #endif
876 return text;
879 static void
880 get_list_info (char **file, char **dir)
882 if (current_row == -1)
883 *file = *dir = NULL;
884 gtk_clist_get_text (GTK_CLIST (g_clist), current_row, 0, file);
885 *dir = gtk_clist_get_row_data (GTK_CLIST (g_clist), current_row);
887 #else
889 static void
890 get_list_info (char **file, char **dir)
892 listbox_get_current (find_list, file, dir);
895 static char *
896 add_to_list (char *text, void *data)
898 return listbox_add_item (find_list, 0, 0, text, data);
901 static void
902 stop_idle (void *data)
904 set_idle_proc (data, 0);
907 static int
908 view_edit_currently_selected_file (int unparsed_view, int edit)
910 WLEntry *entry = find_list->current;
911 char *dir;
913 if (!entry)
914 return MSG_NOT_HANDLED;
916 dir = entry->data;
918 if (!entry->text || !dir)
919 return MSG_NOT_HANDLED;
921 find_do_view_edit (unparsed_view, edit, dir, entry->text);
922 return MSG_HANDLED;
925 static int
926 find_callback (struct Dlg_head *h, int id, int Msg)
928 switch (Msg){
929 #ifndef HAVE_X
930 case DLG_DRAW:
931 common_dialog_repaint (h);
932 break;
933 #endif
935 case DLG_KEY:
936 if (id == KEY_F(3) || id == KEY_F(13)){
937 int unparsed_view = (id == KEY_F(13));
938 return view_edit_currently_selected_file (unparsed_view, 0);
940 if (id == KEY_F(4)){
941 return view_edit_currently_selected_file (0, 1);
943 return MSG_NOT_HANDLED;
945 case DLG_IDLE:
946 do_search (h);
947 break;
949 return 0;
952 /* Handles the Stop/Start button in the find window */
953 static int
954 start_stop (int button, void *extra)
956 running = is_start;
957 set_idle_proc (find_dlg, running);
958 is_start = !is_start;
960 status_update (is_start ? _("Stopped") : _("Searching"));
961 button_set_text (stop_button, fbuts [is_start].text);
963 return 0;
966 /* Handle view command, when invoked as a button */
967 static int
968 find_do_view_file (int button, void *extra)
970 view_edit_currently_selected_file (0, 0);
971 return 0;
974 /* Handle edit command, when invoked as a button */
975 static int
976 find_do_edit_file (int button, void *extra)
978 view_edit_currently_selected_file (0, 1);
979 return 0;
982 static void
983 setup_gui (void)
985 #ifdef ENABLE_NLS
986 static int i18n_flag = 0;
987 if (!i18n_flag)
989 register int i = sizeof (fbuts) / sizeof (fbuts[0]);
990 while (i--)
991 fbuts [i].len = strlen (fbuts [i].text = _(fbuts [i].text)) + 3;
992 fbuts [2].len += 2; /* DEFPUSH_BUTTON */
993 i18n_flag = 1;
995 #endif /* ENABLE_NLS */
998 * Dynamically place buttons centered within current window size
1001 int l0 = max (fbuts[0].len, fbuts[1].len);
1002 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1003 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1004 int r1, r2;
1006 FIND2_X = COLS - 16;
1008 /* Check, if both button rows fit within FIND2_X */
1009 if (l1 + 9 > FIND2_X) FIND2_X = l1 + 9;
1010 if (l2 + 8 > FIND2_X) FIND2_X = l2 + 8;
1012 /* compute amount of space between buttons for each row */
1013 r1 = (FIND2_X - 4 - l1) % 5;
1014 l1 = (FIND2_X - 4 - l1) / 5;
1015 r2 = (FIND2_X - 4 - l2) % 4;
1016 l2 = (FIND2_X - 4 - l2) / 4;
1018 /* ...and finally, place buttons */
1019 fbuts [2].x = 2 + r1/2 + l1;
1020 fbuts [3].x = fbuts [2].x + fbuts [2].len + l1;
1021 fbuts [0].x = fbuts [3].x + fbuts [3].len + l1;
1022 fbuts [4].x = fbuts [0].x + l0 + l1;
1023 fbuts [5].x = 2 + r2/2 + l2;
1024 fbuts [6].x = fbuts [5].x + fbuts [5].len + l2;
1025 fbuts [7].x = fbuts [6].x + fbuts [6].len + l2;
1028 find_dlg = create_dlg (0, 0, FIND2_Y, FIND2_X, dialog_colors,
1029 find_callback, "[Find File]", "mfind", DLG_CENTER | DLG_GRID);
1031 x_set_dialog_title (find_dlg, _("Find file"));
1033 add_widgetl (find_dlg,
1034 button_new (FIND2_Y-3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1035 fbuts[7].text, find_do_edit_file, find_dlg, "button-edit"), 0);
1036 add_widgetl (find_dlg,
1037 button_new (FIND2_Y-3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1038 fbuts[6].text, find_do_view_file, find_dlg, "button-view"), 0);
1039 add_widgetl (find_dlg,
1040 button_new (FIND2_Y-3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON,
1041 fbuts[5].text, 0, 0, "button-panelize"), XV_WLAY_CENTERROW);
1043 add_widgetl (find_dlg,
1044 button_new (FIND2_Y-4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON,
1045 fbuts[4].text, 0, 0, "button-quit"), XV_WLAY_RIGHTOF);
1046 stop_button = button_new (FIND2_Y-4, fbuts[0].x, B_STOP, NORMAL_BUTTON,
1047 fbuts[0].text, start_stop, find_dlg, "start-stop");
1048 add_widgetl (find_dlg, stop_button, XV_WLAY_RIGHTOF);
1049 add_widgetl (find_dlg,
1050 button_new (FIND2_Y-4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON,
1051 fbuts[3].text, 0, 0, "button-again"), XV_WLAY_RIGHTOF);
1052 add_widgetl (find_dlg,
1053 button_new (FIND2_Y-4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON,
1054 fbuts[2].text, 0, 0, "button-chdir"), XV_WLAY_CENTERROW);
1056 status_label = label_new (FIND2_Y-6, 4, _("Searching"), "label-search");
1057 add_widgetl (find_dlg, status_label, XV_WLAY_BELOWOF);
1059 find_list = listbox_new (2, 2, FIND2_X-4, FIND2_Y-9, listbox_finish, 0, "listbox");
1060 add_widgetl (find_dlg, find_list, XV_WLAY_EXTENDWIDTH);
1063 static int
1064 run_process (void)
1066 set_idle_proc (find_dlg, 1);
1067 run_dlg (find_dlg);
1068 return find_dlg->ret_value;
1071 static void
1072 status_update (char *text)
1074 label_set_text (status_label, text);
1077 static void
1078 kill_gui (void)
1080 set_idle_proc (find_dlg, 0);
1081 destroy_dlg (find_dlg);
1083 #endif
1085 static int
1086 find_file (char *start_dir, char *pattern, char *content, char **dirname, char **filename)
1088 int return_value = 0;
1089 char *dir;
1090 char *dir_tmp, *file_tmp;
1092 setup_gui ();
1094 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1095 find_pattern = pattern;
1096 content_pattern = content;
1098 init_find_vars ();
1099 push_directory (start_dir);
1101 return_value = run_process ();
1103 /* Remove all the items in the stack */
1104 while ((dir = pop_directory ()) != NULL)
1105 g_free (dir);
1107 get_list_info (&file_tmp, &dir_tmp);
1109 if (dir_tmp)
1110 *dirname = g_strdup (dir_tmp);
1111 if (file_tmp)
1112 *filename = g_strdup (file_tmp);
1114 #ifndef HAVE_GNOME
1115 if (return_value == B_PANELIZE && *filename){
1116 int status, link_to_dir, stalled_link;
1117 int next_free = 0;
1118 int i;
1119 struct stat buf;
1120 WLEntry *entry = find_list->list;
1121 dir_list *list = &cpanel->dir;
1122 char *dir, *name;
1124 for (i = 0; entry && i < find_list->count; entry = entry->next, i++){
1125 char *filename;
1127 if (content_pattern)
1128 filename = strchr (entry->text+4, ':')+1;
1129 else
1130 filename = entry->text+4;
1132 if (!entry->text || !entry->data)
1133 continue;
1134 dir = entry->data;
1135 if (dir [0] == '.' && dir [1] == 0)
1136 name = g_strdup (filename);
1137 else if (dir [0] == '.' && dir [1] == PATH_SEP)
1138 name = concat_dir_and_file (dir + 2, filename);
1139 else
1140 name = concat_dir_and_file (dir, filename);
1141 status = handle_path (list, name, &buf, next_free, &link_to_dir,
1142 &stalled_link);
1143 if (status == 0) {
1144 g_free (name);
1145 continue;
1147 if (status == -1) {
1148 g_free (name);
1149 break;
1152 /* don't add files more than once to the panel */
1153 if (content_pattern && next_free > 0){
1154 if (strcmp (list->list [next_free-1].fname, name) == 0) {
1155 g_free (name);
1156 continue;
1160 if (!next_free) /* first turn i.e clean old list */
1161 panel_clean_dir (cpanel);
1162 list->list [next_free].fnamelen = strlen (name);
1163 list->list [next_free].fname = name;
1164 file_mark (cpanel, next_free, 0);
1165 list->list [next_free].f.link_to_dir = link_to_dir;
1166 list->list [next_free].f.stalled_link = stalled_link;
1167 list->list [next_free].f.dir_size_computed = 0;
1168 list->list [next_free].buf = buf;
1169 next_free++;
1170 if (!(next_free & 15))
1171 rotate_dash ();
1173 if (next_free){
1174 cpanel->count = next_free;
1175 cpanel->is_panelized = 1;
1176 /* Done by panel_clean_dir a few lines above
1177 cpanel->dirs_marked = 0;
1178 cpanel->marked = 0;
1179 cpanel->total = 0;
1180 cpanel->top_file = 0;
1181 cpanel->selected = 0;*/
1183 if (start_dir [0] == PATH_SEP){
1184 strcpy (cpanel->cwd, PATH_SEP_STR);
1185 chdir (PATH_SEP_STR);
1189 #endif
1191 kill_gui ();
1192 do_search (0); /* force do_search to release resources */
1193 if (old_dir){
1194 g_free (old_dir);
1195 old_dir = 0;
1197 return return_value;
1200 void
1201 do_find (void)
1203 char *start_dir, *pattern, *content;
1204 char *filename, *dirname;
1205 int v, dir_and_file_set;
1206 int done = 0;
1208 while (!done){
1209 if (!find_parameters (&start_dir, &pattern, &content))
1210 break;
1212 dirname = filename = NULL;
1213 is_start = 0;
1214 v = find_file (start_dir, pattern, content, &dirname, &filename);
1215 g_free (start_dir);
1216 g_free (pattern);
1218 if (v == B_ENTER){
1219 if (dirname || filename){
1220 if (dirname){
1221 do_cd (dirname, cd_exact);
1222 if (filename)
1223 try_to_select (cpanel, filename + (content ?
1224 (strchr (filename + 4, ':') - filename + 1) : 4) );
1225 } else if (filename)
1226 do_cd (filename, cd_exact);
1227 paint_panel (cpanel);
1228 select_item (cpanel);
1230 if (dirname)
1231 g_free (dirname);
1232 if (filename)
1233 g_free (filename);
1234 break;
1236 if (content)
1237 g_free (content);
1238 dir_and_file_set = dirname && filename;
1239 if (dirname) g_free (dirname);
1240 if (filename) g_free (filename);
1241 if (v == B_CANCEL)
1242 break;
1244 if (v == B_PANELIZE){
1245 if (dir_and_file_set){
1246 try_to_select (cpanel, NULL);
1247 paint_panel (cpanel);
1249 break;