1 
2 /*
3  * Copyright (C) 2002-2012 Edscott Wilson Garcia
4  * EMail: edscott@users.sf.net
5  *
6  *
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 3 of the License, or
10  * (at your option) any later version.
11  *
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.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; .
19  */
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include "rodent.h"
25 #include "rfm_modules.h"
26 #include "rodent_tip.i"
27 /*************************************************************************/
28 /*************************************************************************/
29 
30 /**************************************************************/
31 /////////////////////   tip_event_t    /////////////////////////
32 /**************************************************************/
33 static GMutex *
get_tip_mutex(void)34 get_tip_mutex(void){
35     static GMutex *mutex = NULL;
36     static gsize initialized = 0;
37     if (g_once_init_enter (&initialized)){
38 	rfm_mutex_init(mutex);
39       g_once_init_leave (&initialized, 1);
40     }
41     return mutex;
42 }
43 
44 static GThreadPool *tip_queue;
45 
46 typedef struct tip_event_t {
47     gboolean 		tooltip_active;		// tooltip status
48     GtkWindow		*tooltip_window;
49     record_entry_t	*tooltip_entry;  	// tooltip record_entry
50     GdkPixbuf		*tooltip_pixbuf;	// tooltip image preview
51     gchar 		*tooltip_text;		// tooltip status
52 }tip_event_t;
53 //static gint current_tip_serial=0;
54 static tip_event_t tip_event ={FALSE, NULL, NULL, NULL, NULL};
55 
56 #define MAX_TIP_THREADS MAX_PREVIEW_THREADS
57 
58 static void *
trigger_tooltip_f(gpointer data)59 trigger_tooltip_f(gpointer data){
60     NOOP(stderr, "context tooltip trigget\n");
61     rfm_global_t *rfm_global_p = rfm_global();
62     gtk_widget_trigger_tooltip_query(rfm_global_p->window);
63     return NULL;
64 }
65 
66 static void
trigger_tooltip(void)67 trigger_tooltip(void){
68     TRACE("trigger_tooltip...\n");
69     rfm_context_function (trigger_tooltip_f, NULL);
70 }
71 
72 
73 gboolean
rodent_tip_get_active(void)74 rodent_tip_get_active (void) {
75     GMutex *tip_mutex = get_tip_mutex();
76     g_mutex_lock(tip_mutex);
77     gboolean retval = tip_event.tooltip_active;
78     g_mutex_unlock(tip_mutex);
79     return retval;
80 }
81 
82 void
rodent_hide_tip(void)83 rodent_hide_tip (void) {
84     rfm_global_t *rfm_global_p = rfm_global();
85     if (!rfm_rw_lock_reader_trylock (&(rfm_global_p->setup_lock))) return;
86     rfm_rw_lock_reader_unlock (&(rfm_global_p->setup_lock));
87     GMutex *tip_mutex = get_tip_mutex();
88     g_mutex_lock(tip_mutex);
89     tip_event.tooltip_active = FALSE;
90     g_mutex_unlock(tip_mutex);
91     NOOP("rodent_hide_tip...\n");
92 }
93 
94 gboolean
rodent_tip_function(GtkWidget * window,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer user_data)95 rodent_tip_function (
96         GtkWidget * window,
97         gint x,
98         gint y,
99         gboolean keyboard_mode,
100         GtkTooltip * tooltip,
101         gpointer user_data
102 ) {
103 #ifdef DEBUG_TRACE
104     static gint c=0; TRACE( "rodent_tip_function %d ... \n", c++);
105 #endif
106     rfm_global_t *rfm_global_p = rfm_global();
107     if (!rfm_rw_lock_reader_trylock (&(rfm_global_p->setup_lock))){
108 	TRACE("rodent_tip_function(): unable to lockrfm_global_p->setup_lock!\n");
109 	return FALSE;
110     }
111     rfm_rw_lock_reader_unlock (&(rfm_global_p->setup_lock));
112     g_mutex_lock(rfm_global_p->status_mutex);
113     gint status = rfm_global_p->status;
114     g_mutex_unlock(rfm_global_p->status_mutex);
115     if (status == STATUS_EXIT) {
116 	TRACE("rodent_tip_function(): rfm_global_p->status == STATUS_EXIT!\n");
117 	return FALSE;
118     }
119 
120     widgets_t *widgets_p = rfm_get_widget ("widgets_p");
121     if (!widgets_p || !widgets_p->view_p) return FALSE;
122 
123     view_t *view_p = widgets_p->view_p;
124     if (!rfm_view_list_lock(view_p, "rodent_tip_function")) return FALSE;
125     // We now have a read lock on the view list
126     gboolean retval = FALSE;
127     status = view_p->flags.status;
128     if (view_p->flags.status == STATUS_EXIT) {
129 	TRACE("rodent_tip_function(): view_p->flags.status == STATUS_EXIT!\n");
130         goto done;
131     }
132 
133     GMutex *tip_mutex = get_tip_mutex();
134     if (!g_mutex_trylock(tip_mutex))  {
135 	TRACE("rodent_tip_function(): failed to lock tip_mutex!\n");
136         goto done;
137     }
138 
139 
140 
141     record_entry_t *en = tip_event.tooltip_entry;
142     gboolean tip_not_ok = FALSE;
143     if (!tip_event.tooltip_active) {
144 	tip_not_ok = TRUE;
145 	TRACE("rodent_tip_function(): xx !tip_event.tooltip_active\n");
146     }
147     if (!en) {
148 	tip_not_ok = TRUE;
149 	TRACE("rodent_tip_function(): xx !en\n");
150     }
151     else if (IS_UP_TYPE (en->type) && !g_path_is_absolute(en->path)) {
152 	tip_not_ok = TRUE;
153 	TRACE("rodent_tip_function(): xx IS_UP_TYPE (en->type) && !g_path_is_absolute(en->path)\n");
154     }
155     if (g_object_get_data(G_OBJECT(rfm_global_p->window), "tip_widgets_p")
156 	    != (void *)widgets_p) {
157 	tip_not_ok = TRUE;
158 	TRACE("rodent_tip_function(): xx tip_widgets_p != (void *)widgets_p\n");
159     }
160 #if 10
161     //hacks
162     if (!view_p->mouse_event.saturated_p && !view_p->mouse_event.label_p) {
163 	tip_not_ok = TRUE;
164 	TRACE("rodent_tip_function(): xx !view_p->mouse_event.saturated_p && !view_p->mouse_event.label_p 0x%x 0x%x\n",
165 		GPOINTER_TO_INT(view_p->mouse_event.saturated_p),
166 		GPOINTER_TO_INT(view_p->mouse_event.label_p));
167     }
168     if (view_p->mouse_event.label_p && !tip_event.tooltip_text) {
169 	tip_not_ok = TRUE;
170 	TRACE("rodent_tip_function(): xx view_p->mouse_event.label_p && !tip_event.tooltip_text\n");
171     }
172 #endif
173 
174     if (tip_not_ok){
175 	g_mutex_unlock(tip_mutex);
176 	goto done;
177     }
178 
179     NOOP("TIP function proceeding: %s\n", en->path);
180 
181      if(tip_event.tooltip_pixbuf) {
182 	 if (tip_event.tooltip_text) {
183 	     gboolean create = (tip_event.tooltip_window == NULL
184 		     ||
185 		     g_object_get_data(G_OBJECT(tip_event.tooltip_window), "normal_entry") != en);
186 	     if (create) {
187 		gchar *basename = NULL;
188 		if (g_path_is_absolute(en->path)) {
189                     gchar *g = g_path_get_basename(en->path);
190                     gchar *v = rfm_utf_string(g);
191                     basename = g_markup_escape_text(v, -1);
192                     g_free(g);
193                     g_free(v);
194                 } else {
195                     gchar *v = rfm_utf_string(en->path);
196                     basename = g_markup_escape_text(en->path, -1);
197                     g_free(v);
198                 }
199 
200 
201 		tip_event.tooltip_window =
202 		    GTK_WINDOW(rfm_create_tooltip_window(rfm_global_p->window,
203                             GTK_WIDGET(tip_event.tooltip_window),
204 			    tip_event.tooltip_pixbuf,
205 			    tip_event.tooltip_text,
206 			    basename));
207 		g_free(basename);
208 		g_object_set_data(G_OBJECT(tip_event.tooltip_window),
209 			"normal_entry", en);
210 	     }
211 	 } else {
212 	    gint width = 0;
213 	    gint height = 0;
214 	    GdkPixbuf *pixbuf = NULL;
215 	    gint p_width = -1;
216 	    gint p_height = -1;
217 	    if (tip_event.tooltip_window) {
218 		width = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tip_event.tooltip_window), "width"));
219 		height = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tip_event.tooltip_window), "height"));
220 		pixbuf = g_object_get_data(G_OBJECT(tip_event.tooltip_window), "pixbuf");
221 		if (tip_event.tooltip_pixbuf) {
222 		    p_width = gdk_pixbuf_get_width(tip_event.tooltip_pixbuf);
223 		    p_height = gdk_pixbuf_get_height(tip_event.tooltip_pixbuf);
224 		}
225 	    }
226 	    if (tip_event.tooltip_window &&
227 		    pixbuf == tip_event.tooltip_pixbuf &&
228 		    p_width == width && p_height == height){
229 		// Option 1: window size has not changed. Use the same window.
230 		GtkWidget *image = g_object_get_data(G_OBJECT(tip_event.tooltip_window), "image");
231 		gtk_image_set_from_pixbuf (GTK_IMAGE (image), tip_event.tooltip_pixbuf);
232 	    } else {
233 		TRACE("creating new tip window...\n");
234 		tip_event.tooltip_window =
235 		    GTK_WINDOW(rfm_create_tooltip_window(rfm_global_p->window,
236                             GTK_WIDGET(tip_event.tooltip_window),
237 			    tip_event.tooltip_pixbuf,
238 			    NULL,
239 			    NULL));
240 	    }
241         }
242 	NOOP(stderr, "tip_function TRUE ... \n");
243 	retval = TRUE;
244     } else {
245 	NOOP(stderr, "tip_function FALSE ... en=0x%x, pixbuf=0x%x, tooltip_active=%d\n",
246 		GPOINTER_TO_INT(en),
247 		GPOINTER_TO_INT(tip_event.tooltip_pixbuf),
248 		tip_event.tooltip_active);
249     }
250     gchar *path = g_strdup(tip_event.tooltip_entry->path);
251     g_mutex_unlock(tip_mutex);
252     if (rfm_population_try_read_lock(view_p, "rodent_tip_function")){
253 	population_t **population_pp = view_p->population_pp;
254 	for (; population_pp && *population_pp; population_pp++){
255 	    if (!(*population_pp)->en || !(*population_pp)->en->path) continue;
256 	    if (strcmp((*population_pp)->en->path, path)==0){
257 		(*population_pp)->flags &= (POPULATION_TIP_BUSY ^ 0xffffffff);
258 		break;
259 	    }
260 	}
261 	rfm_population_read_unlock(view_p, "rodent_tip_function");
262     }
263     g_free(path);
264 done:
265     rfm_view_list_unlock("rodent_tip_function");
266     return retval;
267 }
268 
269 static void
signal_pool_f(void * data,void * pool_data)270 signal_pool_f(void *data, void *pool_data){
271     // This is a stop light for initial setup:
272     rfm_global_t *rfm_global_p = rfm_global();
273     rfm_rw_lock_reader_lock (&(rfm_global_p->setup_lock));
274     rfm_rw_lock_reader_unlock (&(rfm_global_p->setup_lock));
275 
276     void **arg = data;
277     view_t *view_p = arg[1];
278     void *function_data = arg[2];
279     g_free(arg);
280 
281     g_mutex_lock(rfm_global_p->status_mutex);
282     gint status = rfm_global_p->status;
283     g_mutex_unlock(rfm_global_p->status_mutex);
284     if (status == STATUS_EXIT) return;
285 
286     const gchar *dbg_text = "signal_pool_f (tip)";
287 
288 
289     if (view_p->flags.status == STATUS_EXIT) {
290         return;
291     }
292 
293     rfm_thread_reference(view_p, g_thread_self(), dbg_text);
294     rodent_activate_tip_f(view_p, function_data);
295     rfm_thread_unreference(view_p, g_thread_self());
296     return;
297 
298 }
299 
300 
301 typedef struct activate_tip_t {
302     view_t *view_p;
303     population_t *thread_population_p;
304     gint saturation_serial;
305     gboolean normal_tip;
306 } activate_tip_t;
307 
free_activate_tip(activate_tip_t * activate_tip_p)308 static void free_activate_tip(activate_tip_t *activate_tip_p){
309     rfm_destroy_entry(activate_tip_p->thread_population_p->en);
310     if (GDK_IS_PIXBUF(activate_tip_p->thread_population_p->preview_pixbuf)){
311 	g_object_unref(activate_tip_p->thread_population_p->preview_pixbuf);
312     }
313 
314     g_free(activate_tip_p->thread_population_p->icon_id);
315     g_free(activate_tip_p->thread_population_p);
316     g_free(activate_tip_p);
317 }
318 
319 
320 /*************************************************************************/
321 /******************* tip functions ***************************************/
322 /*************************************************************************/
323 
324 
325 //static
326 //void show_tip (population_t * population_p);
327 
328 static gchar *
path_info(const population_t * population_p,record_entry_t * en,const gchar * warning)329 path_info (const population_t *population_p, record_entry_t *en, const gchar *warning) {
330     gchar *s1 = NULL, *s2 = NULL;
331     gchar *info = NULL;
332     if(!en || !en->path)
333         return NULL;
334     if(IS_ROOT_TYPE (en->type) &&
335 	    !rfm_g_file_test (en->path, G_FILE_TEST_EXISTS))
336         return NULL;
337 
338 
339     //gchar *b = g_path_get_basename (en->path);
340     //rfm_chop_excess (b);
341     //g_free (b);
342     if(IS_LOCAL_TYPE(en->type) && IS_SLNK (en->type)) {
343 	NOOP(stderr, "local lnk  type...\n");
344         gchar lpath[_POSIX_PATH_MAX + 1];
345         memset (lpath, 0, _POSIX_PATH_MAX + 1);
346         if(readlink (en->path, lpath, _POSIX_PATH_MAX) > 0) {
347             gchar *v = rfm_utf_string(lpath);
348             gchar *escaped_markup = g_markup_escape_text(v, -1);
349             g_free(v);
350             gchar *q = rfm_utf_string (escaped_markup);
351             g_free(escaped_markup);
352             gchar *linkto=g_strdup_printf (_("Link to %s"), q);
353             s1 = g_strdup_printf ("%s\n<i>%s</i>\n\n", linkto, warning);
354             g_free(linkto);
355             g_free (q);
356         }
357     } else if (population_p) {
358         gchar *p = g_strdup_printf ("<i>%s</i>\n\n", warning);
359         if(POPULATION_MODULE(population_p)) {
360 	    gchar *plugin_info=rfm_void(PLUGIN_DIR, POPULATION_MODULE(population_p), "plugin_info");
361 	    if (plugin_info) {
362 		s1 = g_strconcat (p, plugin_info, NULL);
363 		g_free (p);
364 		g_free(plugin_info);
365 	    } else {
366 		gchar *q = g_strdup_printf ("%s: %s", _("Installed Plugins"),
367 			POPULATION_MODULE(population_p));
368 		s1 = g_strconcat (p, q, NULL);
369 		g_free (p);
370 		g_free (q);
371 	    }
372         } else {
373             s1 = p;
374         }
375     }
376 
377     gchar *s12 = NULL;
378 
379 
380 
381     rfm_set_mime_dtype(en);
382     if (!en->mimetype) {
383 	NOOP(stderr, "getting mimetype: %s\n", en->path);
384 	en->mimetype = MIME_type(en->path, en->st);
385     }
386 
387     if (IS_LOCAL_TYPE(en->type)){
388 	if (!en->mimemagic || strcmp(en->mimemagic, _("unknown"))==0) {
389 	    gchar *old = en->mimemagic;
390 	    NOOP(stderr, "getting magic type: %s\n", en->path);
391 	    en->mimemagic = rfm_rational(RFM_MODULE_DIR, "mime", en, "mime_magic", "mime_function");
392 	    g_free(old);
393 
394 	    if (!en->mimemagic) en->mimemagic = g_strdup(_("unknown"));
395 	}
396 	if (!en->filetype || strcmp(en->filetype, _("unknown"))==0) {
397 	    NOOP(stderr, "getting file type: %s\n", en->path);
398 	    gchar *old = en->filetype;
399 	    en->filetype = rfm_rational(RFM_MODULE_DIR, "mime", en, "mime_file", "mime_function");
400 	    g_free(old);
401 	    if (!en->filetype) en->filetype = g_strdup(_("unknown"));
402 	}
403 	if (!en->encoding || strcmp(en->encoding, _("unknown"))==0) {
404 	    gchar *old = en->encoding;
405 	    NOOP(stderr, "getting file encoding: %s\n", en->path);
406 	    en->encoding = rfm_rational(RFM_MODULE_DIR, "mime", en, "mime_encoding", "mime_function");
407 	    g_free(old);
408 	    if (!en->encoding) en->encoding = g_strdup(_("unknown"));
409 	}
410 
411     }
412     else {
413 	NOOP(stderr, "Not a local type: %s\n", en->path);
414     }
415     if ((en->mimetype && strstr(en->mimetype, "x-trash")) ||
416 	en->path[strlen(en->path)-1] =='~' ||
417 	en->path[strlen(en->path)-1] =='%' ) {
418 	g_free(en->filetype);
419 	en->filetype = g_strdup(_("Backup file"));
420     }
421     s12 = g_strdup_printf("<b>%s</b>: %s\n<b>%s</b> (freedesktop): %s\n<b>%s</b> (libmagic): %s\n<b>%s</b>: %s\n\n",
422 	    _("File Type"), en->filetype,
423 	    _("MIME Type"), (en->mimetype)?en->mimetype:_("unknown"),
424 	    _("MIME Type"), en->mimemagic,
425 	    _("Encoding"), en->encoding);
426 
427 
428     if(en->st) {
429         gchar *grupo=rfm_group_string(en->st);
430         gchar *owner=rfm_user_string(en->st);
431         gchar *tag = rfm_sizetag ((off_t) en->st->st_size, -1);
432 
433         //    gchar *ss= rfm_time_to_string(en->st->st_mtime);
434 
435         gchar *t = g_path_get_dirname (en->path);
436         gchar *v = rfm_utf_string(t);
437         gchar *escaped_markup = g_markup_escape_text(v, -1);
438         g_free(v);
439         gchar *dirname = rfm_utf_string (escaped_markup);
440         g_free(t);
441         g_free(escaped_markup);
442 	gchar *mode_string=rfm_mode_string (en->st->st_mode);
443         s2 = g_strdup_printf (
444                 "<b>%s/%s</b>: %s/%s\n<b>%s</b>: %s\n<b>%s</b>: %s\n\n<b>%s</b>: %s",
445                  _("Owner"),_("Group"), owner, grupo,
446                 _("Permissions"), mode_string,
447                 _("Folder"), dirname,
448                 _("Size"),  tag);
449 
450         //    g_free(q);
451         g_free (owner);
452         g_free (grupo);
453         g_free (tag);
454         g_free (dirname);
455         g_free (mode_string);
456 
457 	gchar buf[1024];
458 
459 	gchar *date_string=rfm_date_string(en->st->st_ctime);
460 
461         sprintf (buf, "<b>%s :</b> %s", _("Status Change"), date_string);
462 	g_free(date_string);
463 
464         gchar *s3 = g_strconcat (s2, "\n", buf, NULL);
465         g_free (s2);
466         s2 = s3;
467 
468 	date_string=rfm_date_string(en->st->st_mtime);
469         sprintf (buf, "<b>%s</b> %s", _("Modification Time :"), date_string);
470 	g_free(date_string);
471 
472 
473         s3 = g_strconcat (s2, "\n", buf, NULL);
474         g_free (s2);
475         s2 = s3;
476 
477 	date_string=rfm_date_string(en->st->st_atime);
478         sprintf (buf, "<b>%s</b> %s", _("Access Time :"), date_string);
479 	g_free(date_string);
480 
481         s3 = g_strconcat (s2, "\n", buf, NULL);
482         g_free (s2);
483         s2 = s3;
484 
485 	gchar *hard_links = g_strconcat(_("Links")," (", _("hard"), ")", NULL);
486         s3 = g_strdup_printf ("%s\n\n<b>%s</b>: %ld\n<b>%s</b>: %ld",
487 		s2, hard_links,
488 		(long)en->st->st_nlink, _("Inode"), (long)en->st->st_ino);
489 	g_free(hard_links);
490 
491         g_free (s2);
492         s2 = s3;
493     }
494 
495     if(!s1)
496         s1 = g_strdup ("");
497     if(!s2)
498         s2 = g_strdup ("");
499 #ifdef CORE
500     info = g_strdup_printf("%s%s%s\n<b>Icon id:</b> %s",
501 	    s1, s12, s2,
502 	    (population_p)?population_p->icon_id:"See label tooltip");
503 #else
504     info = g_strconcat (s1, s12, s2, NULL);
505 #endif
506     g_free (s1);
507     g_free (s2);
508     g_free (s12);
509     return info;
510 }
511 
512 
513 gchar *
rodent_get_tip_text(view_t * view_p,const population_t * population_p,record_entry_t * en,const gchar * prepend_txt)514 rodent_get_tip_text (view_t * view_p, const population_t * population_p, record_entry_t *en, const gchar * prepend_txt) {
515     const gchar *warning = "";
516     if (view_p->en && view_p->en->module == NULL){
517 	if (IS_LOCAL_TYPE(en->type)){
518 	    warning = _("Local File");
519 	} else {
520 	    warning = _("Remote File");
521 	}
522     } else {
523 	warning = _("Plugin services");
524     }
525     gchar *g=NULL;
526     if (view_p->en && view_p->en->module == NULL) {
527 	if (!IS_LOCAL_TYPE(en->type)){
528 	    gchar *info = path_info (population_p, en, warning);
529 	    g = g_strdup ((info)?info:"");
530 	    g_free (info);
531 	} else if(en && IS_SDIR (en->type)) {
532 	    gint files = xfdir_count_files (en->path);
533 	    gint hidden = xfdir_count_hidden_files (en->path);
534 
535 	    if(files) {
536 		warning = _("Local Directory");
537 		gchar *files_string = g_strdup_printf (ngettext (" (containing %'d item)", " (containing %'d items)", files),files);
538 
539 		gchar *plural_string =
540 		    g_strdup_printf(ngettext ("%'u item","%'u items",hidden), hidden);
541 		gchar *hidden_string =
542 		    g_strdup_printf ("%s: %s.",_("Hidden"), plural_string);
543 		g_free(plural_string);
544 
545 		g = g_strdup_printf ("%s\n%s\n%s", warning, files_string, hidden_string);
546 		g_free(hidden_string);
547 		g_free (files_string);
548 		gchar *info = path_info (population_p, en, g);
549 		g_free(g);
550 		g = info;
551 	    } else if (rfm_g_file_test(en->path, G_FILE_TEST_EXISTS)){
552 		warning = _("Local Directory");
553 		g = g_strdup_printf ("%s\n(%s)", warning, _("The location is empty."));
554 		gchar *info = path_info (population_p, en, g);
555 		g_free(g);
556 		g = info;
557 	    } else {
558 		warning = _("Local Directory");
559 		gchar *info = path_info (population_p, en, warning);
560 		g = info;
561 	    }
562 	} else {
563 	    gchar *info = path_info (population_p, en, warning);
564 	    g = g_strdup ((info)?info:"");
565 	    g_free (info);
566 	}
567     } else if (view_p->en == NULL
568 	    && en && !en->module) {
569         gchar *v = NULL;
570 	if (en->tag) {
571             v = rfm_utf_string(en->tag);
572         } else {
573             v = rfm_utf_string(en->path);
574         }
575         g = g_markup_escape_text(v, -1);
576         g_free(v);
577     }
578 
579 
580     // modules can prepend text to the tooltip
581     if(prepend_txt) {
582         gchar *v = rfm_utf_string(prepend_txt);
583         gchar *pt = g_markup_escape_text(v, -1);
584         g_free(v);
585         gchar *gg = g_strconcat (prepend_txt, "\n", (g)?g:"", NULL);
586         g_free (pt);
587         g_free (g);
588         g = gg;
589     }
590     return g;
591 }
592 
593 static gboolean
is_normal_condition(population_t * population_p)594 is_normal_condition(population_t *population_p){
595     if (!population_p) return FALSE;
596     view_t *view_p=population_p->view_p;
597 
598     // Preview already done.
599     if (population_p->preview_pixbuf) {
600 	return FALSE;
601     }
602 
603     // User does not want previews in the directory.
604     if (!SHOWS_IMAGES(view_p->flags.preferences)) {
605 	 NOOP("normal tip; !SHOWS_IMAGES(view_p->flags.preferences)\n");
606 	return TRUE;
607     }
608     // Short circuit.
609     if (population_p->flags & POPULATION_NORMAL_TIP) {
610 	 NOOP(stderr,  "* normal tip; short circuit:%s \n", population_p->en->path);
611 	return TRUE;
612     }
613 
614     NOOP(stderr,  "* FAILED short circuit:%s \n", population_p->en->path);
615 
616     if (population_p->en==NULL || population_p->en->st==NULL) {
617 	 NOOP("normal tip; population_p->en==NULL || population_p->en->st==NULL\n");
618 	return TRUE;
619     }
620 
621     // Modules will have normal tip.
622     if (POPULATION_MODULE(population_p)) {
623 	 NOOP("normal tip; POPULATION_MODULE\n");
624 	return TRUE;
625     }
626 
627 
628     // For some reason Rodent has previously determined that preview
629     // is not possible for the population element.
630     if(g_object_get_data (G_OBJECT (view_p->widgets.paper), "normal_tip")) {
631 	 NOOP("normal tip; g_object_get_data (G_OBJECT (view_p->widgets.paper), normal_tip) is set for population item\n");
632 	return TRUE;
633     }
634 
635     gboolean retval = TRUE;
636     // Directories are now set to preview.
637     if (IS_SDIR(population_p->en->type) ) retval = FALSE;
638 
639     // Empty files are easy to preview with a blank page.
640     // (This, of course, if the stat we're looking at is not an empty stat)
641     else if (population_p->en->st->st_size == 0){
642 	if (population_p->en->st->st_ino != 0) retval = FALSE;
643 	else{
644 	     NOOP("normal tip; (population_p->en->st->st_size == 0\n");
645 	    retval = TRUE;
646 	}
647     }
648 
649     // Application/xxx mimetypes are now set to preview.
650     else if (population_p->en->mimetype || population_p->en->mimemagic){
651 	const gchar *type = population_p->en->mimetype;
652 	if (!type || strcmp(type, _("unknown"))==0) type = population_p->en->mimemagic;
653 	if (!type || strcmp(type, _("unknown"))==0) {
654 	     NOOP("normal tip; strcmp(type, _(\"unknown\"))==0\n");
655 	    return TRUE;
656 	}
657 	// XXX I'm not sure at this time whether desktop files
658 	// should or should not have previews...
659 	//if(strcmp(population_p->en->mimetype, "application/x-desktop")==0){
660 	//    return TRUE;
661 	//}
662 	if(strcmp(type, "application/x-executable")==0){
663 	     NOOP("normal tip; application/x-executable\n");
664 	    return TRUE;
665 	}
666 	if (strncmp(type, "application/",strlen("application/"))==0){
667 	    retval = FALSE;
668 	}
669 	else if (strncmp(type,"image/",strlen("image/"))==0){
670 	    retval = FALSE;
671 	}
672 	else if (strncmp(type, "text/",strlen("text/"))==0){
673 	    retval = FALSE;
674 	}
675     }
676 
677     // Images should preview, even if mimetype not correctly set.
678     else if (rfm_entry_is_image (population_p->en)) retval = FALSE;
679 
680     // Open documents should preview, even if mimetype not correctly set.
681     else if (population_p->en->mimemagic && strstr(population_p->en->mimemagic, "opendocument")) {
682 	retval = FALSE;
683     }
684 
685     // Are we capable of doing the preview?
686     if(rfm_void(RFM_MODULE_DIR, "mime",  "module_active") == NULL) {
687 	 NOOP( "* capable of doing the preview is false: %s\n",
688 		population_p->en->path);
689 	return TRUE;
690     }
691 
692     // User wants previews in the directory.
693     if (SHOWS_IMAGES(view_p->flags.preferences)) {
694 	NOOP("normal tip; ! (view_p->flags.preferences & __SHOW_IMAGES)\n");
695 	return FALSE;
696     }
697     // Default is the normal tip (for the time being).
698      NOOP( "* Default is the normal tip: %s\n",
699 		population_p->en->path);
700     return retval;
701 }
702 
703 static gboolean
set_tooltip_info(view_t * view_p,const gchar * text,const record_entry_t * en,GdkPixbuf * tip_pixbuf)704 set_tooltip_info(view_t *view_p, const gchar *text, const record_entry_t *en, GdkPixbuf *tip_pixbuf){
705     if (!rfm_population_try_read_lock(view_p, "set_tooltip_info")) {
706 	NOOP(stderr, "set_tooltip_info !rfm_population_try_read_lock(view_p)) \n");
707 	return FALSE;
708     }
709 
710     rfm_global_t *rfm_global_p = rfm_global();
711     if(tip_pixbuf) g_object_ref(tip_pixbuf);
712     record_entry_t *tip_entry = (en)?rfm_copy_entry (en):NULL;
713     gchar *tip_text = (text)?g_strdup(text):NULL;
714 
715     GMutex *tip_mutex = get_tip_mutex();
716     g_mutex_lock(tip_mutex);
717     // assign new tip values.
718     gchar *oldtext=tip_event.tooltip_text;
719     tip_event.tooltip_text = tip_text;
720 
721     record_entry_t *olden=tip_event.tooltip_entry;
722     tip_event.tooltip_entry = tip_entry;
723 
724     GdkPixbuf *oldpixbuf = tip_event.tooltip_pixbuf;
725     tip_event.tooltip_pixbuf = tip_pixbuf;
726 
727     tip_event.tooltip_active = TRUE;
728     const gchar *path = (tip_event.tooltip_entry)?(tip_event.tooltip_entry->path):NULL;
729     g_object_set_data(G_OBJECT(rfm_global_p->window),
730 	    "tip_widgets_p", rfm_get_widget("widgets_p"));
731     g_mutex_unlock(tip_mutex);
732 
733     g_free (oldtext);
734     population_t **population_pp = view_p->population_pp;
735     for (; population_pp && *population_pp; population_pp++){
736         if (!(*population_pp)->en || !(*population_pp)->en->path) continue;
737         if (path && strcmp((*population_pp)->en->path, path)==0){
738             (*population_pp)->flags &= (POPULATION_TIP_BUSY ^ 0xffffffff);
739             break;
740         }
741     }
742     rfm_population_read_unlock(view_p, "set_tooltip_info");
743 
744     rfm_destroy_entry(olden);
745     if (oldpixbuf && G_IS_OBJECT(oldpixbuf)) g_object_unref(oldpixbuf);
746 
747     return TRUE;
748 }
749 
750 static gboolean
normal_tip(const population_t * population_p)751 normal_tip(const population_t * population_p){
752     view_t *view_p = population_p->view_p;
753     gchar *module_txt = NULL;
754     if (!view_p->en && POPULATION_MODULE(population_p)) {
755 	module_txt =
756 	    rfm_void (PLUGIN_DIR, POPULATION_MODULE(population_p), "module_entry_tip");
757         if (!module_txt) {
758             module_txt = g_strdup_printf("FIXME: module_entry_tip() in plugin %s returns null.",
759                     POPULATION_MODULE(population_p));
760         }
761     } else if(POPULATION_MODULE(population_p)) {
762         module_txt =
763 	    rfm_natural (PLUGIN_DIR, POPULATION_MODULE(population_p),
764 			population_p->en, "item_entry_tip");
765     }
766     NOOP(stderr, "Not a module determined tip...\n");
767 
768     GdkPixbuf *tip_pixbuf=NULL;
769     gchar *tip_text=NULL;
770     //record_entry_t *tip_entry;
771     if (module_txt){
772 	tip_text = g_strdup(module_txt);
773     } else {
774 	tip_text = rodent_get_tip_text (view_p, population_p, population_p->en, module_txt);
775     }
776     g_free (module_txt);
777 
778     // This would put the preview instead of the icon in the text
779     // tip window. This seemed like a good idea at first, but later
780     // on I became disenchanted.
781     //gboolean icon_tips = (getenv ("RFM_ENABLE_TIPS")!=NULL && strlen (getenv ("RFM_ENABLE_TIPS")));
782     //if (!icon_tips)
783     //   tip_pixbuf = rfm_natural(RFM_MODULE_DIR, "mime",
784     //		(void *)population_p, "mime_preview"); // refs
785 
786 
787     if (!tip_pixbuf) {
788 	const gchar *icon_id = population_p->icon_id;
789 	if (!icon_id && population_p->en) icon_id = population_p->en->mimetype;
790 	if (!icon_id){
791 	    tip_pixbuf = rfm_get_pixbuf ("rodent", BIG_ICON_SIZE); //refs
792 	} else {
793 	    tip_pixbuf = rfm_get_pixbuf (icon_id, BIG_ICON_SIZE); //refs
794 	}
795     }
796 
797     NOOP(stderr, "iconid=%s\n", population_p->icon_id);
798     gboolean retval = set_tooltip_info(view_p, tip_text, population_p->en, tip_pixbuf);
799     if(tip_pixbuf && G_IS_OBJECT(tip_pixbuf)) g_object_unref(tip_pixbuf);
800 
801     g_free(tip_text);
802     return retval;
803 }
804 
805 
806 static gboolean
set_tooltip_pixbuf(view_t * view_p,population_t * population_p)807 set_tooltip_pixbuf(view_t *view_p, population_t *population_p){
808     return set_tooltip_info(view_p, NULL, population_p->en, population_p->preview_pixbuf);
809 }
810 
811 static population_t*
get_population_p(activate_tip_t * activate_tip_p)812 get_population_p(activate_tip_t *activate_tip_p){
813     // This needs a readlock...
814     if (!rfm_population_read_lock(activate_tip_p->view_p, "get_population_p")){
815 	DBG("get_population_p() does not get population readlock\n");
816 	return NULL;
817     }
818     // find population item
819     population_t **pp=activate_tip_p->view_p->population_pp;
820     gboolean found =FALSE;
821     population_t *population_p=*pp;
822     for (; pp && *pp; pp++){
823 	 population_p=*pp;
824 	if (!population_p->en) continue;
825 	if (strcmp(population_p->en->path, activate_tip_p->thread_population_p->en->path)==0)
826 	{
827 	    found=TRUE;
828 	    break;
829 	}
830     }
831 
832     rfm_population_read_unlock(activate_tip_p->view_p, "get_population_p");
833     if (found) return population_p;
834     return NULL;
835 }
836 
837 static gboolean
tip_preview(gpointer data)838 tip_preview (gpointer data) {
839     gboolean retval = FALSE;
840     gboolean status = FALSE;
841      NOOP("TIP: tip_preview NOW...\n");
842     activate_tip_t *activate_tip_p=data;
843     view_t *view_p = activate_tip_p->view_p;
844 
845     // Do the real preview.
846     GdkPixbuf *result=rfm_natural(RFM_MODULE_DIR, "mime",
847 	    activate_tip_p->thread_population_p, "mime_preview"); //refs
848 
849     if (!rfm_view_list_lock(view_p, "tip_preview")) return FALSE;
850 
851     if (result && GDK_IS_PIXBUF(result)) {
852  	if (view_p->mouse_event.saturated_p && view_p->flags.saturation_serial == activate_tip_p->saturation_serial)
853            NOOP("rodent_mouse: SHOW_TIP: population_p->preview_pixbuf OK!\n");
854 	{
855 	    retval =
856 		set_tooltip_info(view_p, NULL, activate_tip_p->thread_population_p->en, result);
857 	}
858 	if (!retval) goto done;
859     }
860 
861     if (!rfm_population_read_lock(view_p, "tip_preview")) goto done;
862     population_t *population_p=get_population_p(activate_tip_p);
863 
864     if (population_p) {
865 	if (!result) {
866 	    // normal tip is set to short circuit further attempts.
867 	    population_p->flags |= POPULATION_NORMAL_TIP;
868 	} else {
869 	    if (population_p->preview_pixbuf
870 		    && G_IS_OBJECT(population_p->preview_pixbuf)) {
871 		g_object_unref(population_p->preview_pixbuf);
872 	    }
873 	    population_p->preview_pixbuf=result;
874 	    g_object_ref(population_p->preview_pixbuf);
875 	    population_p->flags |= POPULATION_PREVIEW_DONE;
876 	}
877     }
878 
879 
880     NOOP(stderr, "rodent_mouse: SHOW_TIP: tip_preview DONE...\n");
881     status = TRUE;
882     rfm_population_read_unlock(view_p, "tip_preview");
883 done:
884     rfm_view_list_unlock("tip_preview");
885     // eliminate the reference which comes from mime-mouse-magic.i
886     if(result && G_IS_OBJECT(result))  g_object_unref(result);
887     return status;
888 }
889 
890 
891 void *
rodent_activate_tip_f(view_t * view_p,void * data)892 rodent_activate_tip_f(view_t *view_p, void *data){
893     // stop other unused threads. This will effectively
894     // leave a single threadpool thread running, at least.
895     if (g_thread_pool_get_num_unused_threads() > 2) {
896 	g_thread_pool_stop_unused_threads();
897     }
898     if (!rfm_view_list_lock(view_p, "rodent_activate_tip_f")) return NULL;
899 
900     TRACE("rodent_activate_tip_f\n");
901     activate_tip_t *activate_tip_p=data;
902     if (activate_tip_p->thread_population_p == NULL
903 	    ||activate_tip_p->thread_population_p->en == NULL){
904 	g_error("rodent_activate_tip_f(): !population_p || !population_p->en\n");
905     }
906 
907 
908 
909     NOOP(stderr, "TIP: activate_tip_thread: 0x%x: %s\n", GPOINTER_TO_INT(g_thread_self()),
910 	    activate_tip_p->thread_population_p->en->path);
911 
912     if (activate_tip_p->normal_tip || is_normal_condition(activate_tip_p->thread_population_p)) {
913 	NOOP(stderr, "normal tip\n");
914         if (normal_tip(activate_tip_p->thread_population_p)){
915 	    NOOP(stderr, "normal tip triggered\n");
916 	    trigger_tooltip();
917 	}
918     }
919     else {
920 	NOOP(stderr, "preview tip\n");
921 	 // is the work already done?
922 	gboolean preview_ok = (activate_tip_p->thread_population_p->preview_pixbuf != NULL);
923 
924 	if(preview_ok) {
925 	    if (set_tooltip_pixbuf(view_p, activate_tip_p->thread_population_p)){
926 		trigger_tooltip();
927 	    }
928 	    TRACE( "DONE: image tip from cache 0x%x: %s\n", GPOINTER_TO_INT(g_thread_self()), activate_tip_p->thread_population_p->en->path);
929 	} else {
930 	    NOOP(stderr, "entering tip_preview NOW\n");
931 	    if (tip_preview((gpointer) activate_tip_p)){
932 		TRACE( "DONE: image tip 0x%x\n", GPOINTER_TO_INT(g_thread_self()));
933 		trigger_tooltip();
934 	    }
935 	}
936     }
937 
938     // This needs a readlock...
939     while (!rfm_population_read_lock(view_p, "rodent_activate_tip_f")) rfm_threadwait();
940     population_t *population_p=get_population_p(activate_tip_p);
941     if (population_p){
942 	population_p->flags &= (POPULATION_PREVIEW_BUSY ^ 0xffffffff);
943     }
944     rfm_population_read_unlock(view_p, "rodent_activate_tip_f");
945     free_activate_tip(activate_tip_p);
946     rfm_view_list_unlock("rodent_activate_tip_f");
947     return NULL;
948 }
949 
950 
951 
952 void
rodent_activate_tip(view_t * view_p,population_t * population_p,gboolean image_tip)953 rodent_activate_tip(view_t *view_p, population_t *population_p, gboolean image_tip){
954     if(population_p == NULL || !population_p->en) return;
955 
956     rfm_global_t *rfm_global_p = rfm_global();
957     if (!rfm_rw_lock_reader_trylock (&(rfm_global_p->setup_lock)))
958 	return;
959     rfm_rw_lock_reader_unlock (&(rfm_global_p->setup_lock));
960 
961     // if item has yet to be stat'ed, don't do a preview
962     // since that would give you an empty file preview
963     // which would be incorrect.
964     // Probability that the inode number is actually zero
965     // is practically nil.
966 
967     if (population_p->en && population_p->en->st && g_file_test( population_p->en->path, G_FILE_TEST_EXISTS) && !population_p->en->st->st_ino){
968 	TRACE("skipping preview for %s until item is stat'ed\n",
969 		population_p->en->path);
970 	return;
971     }
972 
973     // These two tests are done later down the line, since a new
974     // preview will be returned if conditions determine that
975     // the actual preview is out of date:
976     /*if (population_p->preview_pixbuf) {
977 	NOOP(stderr, "return on population_p->preview_pixbuf\n");
978 	return;
979     }*/
980     /*if ((population_p->flags  & POPULATION_PREVIEW_DONE)) {
981 	NOOP(stderr, "return on POPULATION_PREVIEW_DONE\n");
982 	return;
983     }*/
984 
985     if ((population_p->flags  & POPULATION_TIP_BUSY)) {
986 	NOOP(stderr, "return on POPULATION_TIP_BUSY\n");
987 	return;
988     }
989     if ((population_p->flags  & POPULATION_PREVIEW_BUSY)) {
990 	NOOP(stderr, "return on POPULATION_PREVIEW_BUSY\n");
991 	return;
992     }
993     widgets_t *widgets_p = &(view_p->widgets);
994 
995     population_p->flags |= POPULATION_TIP_BUSY;
996     population_p->flags |= POPULATION_PREVIEW_BUSY;
997 
998 
999     if (!tip_queue) {
1000 	tip_queue = rfm_thread_queue_new(signal_pool_f, NULL, MAX_TIP_THREADS);
1001     }
1002 
1003 
1004     activate_tip_t *activate_tip_p=
1005 	(activate_tip_t *)malloc(sizeof(activate_tip_t));
1006     if (!activate_tip_p) g_error("malloc: %s", strerror(errno));
1007     activate_tip_p->view_p=view_p;
1008     activate_tip_p->saturation_serial=view_p->flags.saturation_serial;
1009     activate_tip_p->thread_population_p =
1010 	(population_t *)malloc(sizeof(population_t));
1011     if (!activate_tip_p->thread_population_p)
1012 	g_error("malloc: %s", strerror(errno));
1013     memcpy(activate_tip_p->thread_population_p, population_p, sizeof(population_t));
1014     activate_tip_p->thread_population_p->icon_id = g_strdup(population_p->icon_id);
1015 
1016     if (!image_tip) activate_tip_p->normal_tip = TRUE;
1017     else activate_tip_p->normal_tip = FALSE;
1018 
1019     /*if (!image_tip){
1020 	activate_tip_p->thread_population_p->flags |= POPULATION_NORMAL_TIP;
1021     } else if (!(population_p->flags & POPULATION_NORMAL_TIP)){
1022 	activate_tip_p->thread_population_p->flags &=
1023 	    (POPULATION_NORMAL_TIP ^ 0xffffffff);
1024     }*/
1025 
1026 
1027     rfm_set_mime_dtype(population_p->en);
1028     if (population_p->en && !population_p->en->mimetype) {
1029 	population_p->en->mimetype = rfm_rational(RFM_MODULE_DIR, "mime", population_p->en, "mime_type", "mime_function");
1030 	if (!population_p->en->mimetype) {
1031 	    population_p->en->mimetype = g_strdup(_("unknown"));
1032 	}
1033     }
1034     if (population_p->en && IS_LOCAL_TYPE(population_p->en->type)
1035 		    && !population_p->en->mimemagic){
1036 		population_p->en->mimemagic =
1037 		    rfm_rational(RFM_MODULE_DIR, "mime", population_p->en,
1038 			    "mime_magic", "mime_function");
1039     }
1040     if (population_p->en && !population_p->en->mimemagic){
1041 	population_p->en->mimemagic = g_strdup(_("unknown"));
1042     }
1043     activate_tip_p->thread_population_p->en =
1044 	rfm_copy_entry(population_p->en);
1045 
1046     if (GDK_IS_PIXBUF(population_p->preview_pixbuf)){
1047 	g_object_ref(population_p->preview_pixbuf);
1048     }
1049     g_object_set_data(G_OBJECT(rfm_global_p->window), "tip_widgets_p", widgets_p);
1050 
1051     rfm_threadqueue_push(tip_queue, 0, view_p, activate_tip_p);
1052 
1053 }
1054 
1055 void
rodent_clear_tooltip(void)1056 rodent_clear_tooltip(void){
1057     GMutex *tip_mutex = get_tip_mutex();
1058     g_mutex_lock(tip_mutex);
1059     // assign new tip values.
1060     g_free(tip_event.tooltip_text);
1061     rfm_destroy_entry(tip_event.tooltip_entry);
1062     if (tip_event.tooltip_pixbuf && G_IS_OBJECT(tip_event.tooltip_pixbuf)) g_object_unref(tip_event.tooltip_pixbuf);
1063     g_mutex_unlock(tip_mutex);
1064 }
1065