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