1 #ifdef COPYRIGHT_INFORMATION
2 #include "gplv3.h"
3 #endif
4 /*
5  * Copyright (C) 2002-2012 Edscott Wilson Garcia
6  * EMail: edscott@users.sf.net
7  *
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; */
21 
22 static void *
done_with_rename(gpointer data)23 done_with_rename (gpointer data) {
24     widgets_t *widgets_p = data;
25     if (rfm_get_gtk_thread() != g_thread_self()){
26 	g_warning("done_with_rename() is a main thread function\n");
27         return NULL;
28     }
29     view_t *view_p = widgets_p->view_p;
30     gchar *path;
31     NOOP ("rodent_done_with_rename\n");
32     if(!view_p->widgets.rename) {
33         NOOP ("!view_p->widgets.rename\n");
34         return NULL;
35     }
36     path = g_object_get_data (G_OBJECT (view_p->widgets.rename), "path");
37     g_free (path);
38 
39 
40     NOOP ("gtk_widget_destroy(GTK_WIDGET(view_p->widgets.rename));\n");
41     gtk_widget_destroy (GTK_WIDGET (view_p->widgets.rename));
42 
43     view_p->widgets.rename = NULL;
44     gtk_main_quit();
45     rodent_unselect_all_pixbuf (view_p);
46     return NULL;
47 }
48 
49 
50 /////////////////////////////   ENTRY rename/copy/simlink  /////////////////
51 //
52 
53 static int
entry_rename(widgets_t * widgets_p,const gchar * nfile,const gchar * ofile)54 entry_rename (widgets_t * widgets_p, const gchar * nfile, const gchar * ofile) {
55     fprintf (stderr,"entry_rename: %s --> %s\n", ofile, nfile);//OK
56     if(!widgets_p || !nfile || !ofile || !strlen (nfile) || !strlen (ofile))
57         return FALSE;
58 
59     GList *in_list = NULL;
60     gchar *src = g_strdup(ofile);
61     in_list = g_list_append (in_list, src);
62 
63     plain_cp (widgets_p, TR_MOVE, in_list, nfile, FALSE);
64     g_list_free (in_list);
65     g_free(src);
66     view_t *view_p = widgets_p->view_p;
67     if (!xfdir_monitor_control_greenlight(widgets_p)){
68 	rodent_trigger_reload(view_p);
69     }
70 
71 
72     return TRUE;
73 }
74 
75 static int
entry_duplicate(widgets_t * widgets_p,const gchar * nfile,const gchar * ofile)76 entry_duplicate (widgets_t * widgets_p, const gchar * nfile, const gchar * ofile) {
77     if(!widgets_p || !nfile || !ofile || !strlen (nfile) || !strlen (ofile))
78         return FALSE;
79 
80     GList *in_list = NULL;
81     gchar *src = g_strdup(ofile);
82     in_list = g_list_append (in_list, src);
83     plain_cp (widgets_p, TR_COPY, in_list, nfile, FALSE);
84     g_list_free (in_list);
85     g_free(src);
86     view_t *view_p = widgets_p->view_p;
87     if (!xfdir_monitor_control_greenlight(widgets_p)){
88 	rodent_trigger_reload(view_p);
89     }
90 
91 
92 
93     return TRUE;
94 }
95 
96 static int
entry_symlink(widgets_t * widgets_p,const gchar * nfile,const gchar * ofile)97 entry_symlink (widgets_t * widgets_p, const gchar * nfile, const gchar * ofile) {
98 
99     if(!widgets_p || !nfile || !ofile || !strlen (nfile) || !strlen (ofile))
100         return FALSE;
101 
102 #if 1
103     g_free(widgets_p->workdir);
104     // Relative links, always...
105     widgets_p->workdir=g_path_get_dirname(ofile);
106     gboolean need_sudo = !rfm_write_ok_path(widgets_p->workdir);
107 
108     gchar *b_ofile=g_path_get_basename(ofile);
109     gchar *b_nfile=g_path_get_basename(nfile);
110     gchar *sources[]={"ln", "-s", b_ofile, b_nfile, NULL};
111     if (need_sudo){
112 	gchar *failed=g_strdup_printf(_("Failed to link %s to %s"),
113 			    _("File"), _("Destination"));
114         if (confirm_sudo(widgets_p, widgets_p->workdir, failed, "ln")){
115             RFM_TRY_SUDO (widgets_p, sources, FALSE);
116         }
117         g_free (failed);
118     } else {
119         rfm_thread_run_argv (widgets_p, sources, FALSE);
120     }
121     g_free(b_ofile);
122     g_free(b_nfile);
123 
124 #else
125     // oldway (no sudo)
126     g_free(widgets_p->workdir);
127     // Relative links, always...
128     widgets_p->workdir=g_path_get_dirname(ofile);
129     gchar *b_ofile=g_path_get_basename(ofile);
130     gchar *b_nfile=g_path_get_basename(nfile);
131     gchar *command = g_strdup_printf ("ln -s \"%s\" \"%s\"", b_ofile, b_nfile);
132     RFM_THREAD_RUN2ARGV (widgets_p, command, FALSE);
133     g_free (command);
134     g_free(b_ofile);
135     g_free(b_nfile);
136 #endif
137 
138 
139     view_t *view_p = widgets_p->view_p;
140     if (!xfdir_monitor_control_greenlight(widgets_p)){
141 	rodent_trigger_reload(view_p);
142     }
143 
144 
145 
146     return TRUE;
147 }
148 
149 static void
entry_activate(GtkEntry * entry,view_t * view_p,int caso)150 entry_activate (GtkEntry * entry, view_t * view_p, int caso) {
151     gchar *actual_tag;
152     gchar *tag;
153     gchar *b;
154     int result = FALSE;
155     gchar *path;
156     widgets_t *widgets_p=&(view_p->widgets);
157 
158     if (view_p->widgets.rename) gtk_widget_hide (GTK_WIDGET (view_p->widgets.rename));
159 
160     path = g_object_get_data (G_OBJECT (view_p->widgets.rename), "path");
161     if(!path) return;
162 
163     actual_tag = gtk_editable_get_chars ((GtkEditable *) entry, 0, -1);
164     g_strstrip(actual_tag);
165     tag = g_locale_from_utf8 (actual_tag, -1, NULL, NULL, NULL);
166     g_free (actual_tag);
167     actual_tag = tag;
168     b = g_path_get_basename (path);
169 
170     gchar *d = g_path_get_dirname (path);
171     gchar *p = g_build_filename (d, actual_tag, NULL);
172     g_free (d);
173     switch (caso) {
174     case RENAME_CASO:
175         result = entry_rename (widgets_p, p, path);
176         if(result) {
177             NOOP ("renaming %s to %s\n", path, p);
178         }
179         break;
180     case DUPLICATE_CASO:
181         result = entry_duplicate (widgets_p, p, path);
182         if(result) {
183             NOOP ("duplication %s to %s\n", path, p);
184         }
185         break;
186     case SYMLINK_CASO:
187         result = entry_symlink (widgets_p, p, path);
188         if(result) {
189             NOOP ("symlinking %s to %s\n", path, p);
190         }
191         break;
192     }
193     g_free (p);
194 
195     g_free (b);
196     g_free (actual_tag);
197     done_with_rename (widgets_p);
198 }
199 
200 static void
entry_activate_rename(GtkEntry * entry,gpointer data)201 entry_activate_rename (GtkEntry * entry, gpointer data) {
202     entry_activate (entry, (view_t *) data, 0);
203 }
204 
205 static void
entry_activate_duplicate(GtkEntry * entry,gpointer data)206 entry_activate_duplicate (GtkEntry * entry, gpointer data) {
207     entry_activate (entry, (view_t *) data, 1);
208 }
209 
210 static void
entry_activate_symlink(GtkEntry * entry,gpointer data)211 entry_activate_symlink (GtkEntry * entry, gpointer data) {
212     entry_activate (entry, (view_t *) data, 2);
213 }
214 
215 static void
destroy_dialog(GtkWidget * widget,gpointer data)216 destroy_dialog (GtkWidget * widget, gpointer data) {
217     widgets_t *widgets_p = data;
218     done_with_rename (widgets_p);
219 }
220 
221 static gint
on_key_press(GtkWidget * entry,GdkEventKey * event,gpointer data)222 on_key_press (GtkWidget * entry, GdkEventKey * event, gpointer data) {
223     if(event->keyval == GDK_KEY_Escape) {
224 	widgets_t *widgets_p = data;
225         done_with_rename (widgets_p);
226         //rfm_status (&(view_p->widgets), NULL, _("Omitting"), NULL);
227         return TRUE;
228     }
229     return FALSE;
230 }
231 
232 static gboolean
grab_focus(GtkWidget * widget,GdkEventCrossing * event,gpointer data)233 grab_focus (GtkWidget * widget, GdkEventCrossing * event, gpointer data) {
234     rfm_global_t *rfm_global_p = rfm_global();
235     XSetInputFocus (rfm_global_p->Xdisplay,
236 	    GDK_WINDOW_XID (gtk_widget_get_parent_window (widget)), RevertToParent, CurrentTime);
237     XUngrabPointer (rfm_global_p->Xdisplay, CurrentTime);
238     return TRUE;
239 }
240 
241 static void
entry_aid(view_t * view_p,gint caso,gint labelY)242 entry_aid(view_t *view_p, gint caso, gint labelY){
243     GtkWidget *vpane = g_object_get_data(G_OBJECT(view_p->widgets.paper), "vpane");
244     if (vpane==NULL) return;
245 
246     GtkAllocation allocation;
247     gtk_widget_get_allocation (vpane, &allocation);
248 
249     NOOP( "ENTRY population_p->labelY = %d, position=%lf\n",
250 	    labelY, allocation.height * 0.75);
251     if (labelY < allocation.height * 0.70){
252 	rfm_show_text(&(view_p->widgets));
253     } else return;
254 
255     switch (caso) {
256 	case 0:
257 	    rfm_diagnostics(&(view_p->widgets),"xffm/stock_dialog-info",NULL);
258 
259 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/green",
260 	    _("Rename the selected file"), "--> ", NULL);
261 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/red",
262 	    _("Click"), "\n", NULL);
263 
264 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
265 		    _("Duplicate this path"), "--> ", NULL);
266 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",_
267 		    ("Control"),"+",_("Click"), "\n", NULL);
268 
269 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
270 		    _("Create Symlink"), "--> ", NULL);
271 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
272 		    _("Shift"),"+",_("Control"),"+", _("Click"), "\n", NULL);
273 	    break;
274 	case 1:
275 	    rfm_diagnostics(&(view_p->widgets),"xffm/stock_dialog-info",NULL);
276 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/green",
277 		    _("Duplicate this path"), "--> ", NULL);
278 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/red",_
279 		    ("Control"),"+",_("Click"), "\n", NULL);
280 
281 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
282 	    _("Rename the selected file"), "--> ", NULL);
283 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
284 	    _("Click"), "\n", NULL);
285 
286 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
287 		    _("Create Symlink"), "--> ", NULL);
288 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
289 		    _("Shift"),"+",_("Control"),"+", _("Click"), "\n", NULL);
290 	    break;
291 	case 2:
292 	    rfm_diagnostics(&(view_p->widgets),"xffm/stock_dialog-info",NULL);
293 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/green",
294 		    _("Create Symlink"), "--> ", NULL);
295 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/red",
296 		    _("Shift"),"+",_("Control"),"+", _("Click"), "\n", NULL);
297 
298 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
299 	    _("Rename the selected file"), "--> ", NULL);
300 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
301 	    _("Click"), "\n", NULL);
302 
303 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",
304 		    _("Duplicate this path"), "--> ", NULL);
305 	    rfm_diagnostics(&(view_p->widgets),"xffm_tag/grey",_
306 		    ("Control"),"+",_("Click"), "\n", NULL);
307 
308 	    break;
309     }
310 }
311 
312 /* entry function: */
313 static void *
mk_rename_entry(gpointer data)314 mk_rename_entry (gpointer data) {
315     rfm_global_t *rfm_global_p = rfm_global();
316     void **arg = data;
317     view_t *view_p = arg[0];
318     const population_t *population_p = arg[1];
319     gint caso = GPOINTER_TO_INT(arg[2]);
320     g_free(arg);
321     GdkRectangle label_rect;
322     if (!rfm_get_population_label_rect_full(view_p, population_p, &label_rect)) return NULL;
323 
324     GtkWidget *entry;
325     GtkWidget *hbox;
326 
327     //widgets_t *widgets_p=&(view_p->widgets);
328 
329     GdkRectangle frame_extent;
330     GdkRectangle window_extent;
331 
332     gint delta_y;
333     gint delta_h;
334     gint y_offset;
335     gdk_window_get_position (gtk_widget_get_window(rfm_global_p->window),
336     	&window_extent.x, &window_extent.y);
337     Drawable drawable =
338         GDK_WINDOW_XID(gtk_widget_get_window(rfm_global_p->window));
339     rfm_get_drawable_geometry(drawable, NULL, NULL, &window_extent.width, &window_extent.height, NULL);
340     NOOP("window_geometry:  x=%d,  y=%d, w=%d, h=%d", window_extent.x, window_extent.y, window_extent.width, window_extent.height);
341 
342     gdk_window_get_frame_extents (gtk_widget_get_window(rfm_global_p->window),&frame_extent);
343     NOOP("window_frame_extents: x=%d, y=%d, w=%d, h=%d\n",
344     	frame_extent.x, frame_extent.y, frame_extent.width, frame_extent.height);
345 
346 
347     delta_y = window_extent.y - frame_extent.y;
348     delta_h = frame_extent.height - window_extent.height;
349     y_offset = delta_y + delta_h;
350     NOOP("window: delta_y=%d, delta_h=%d, y_offset=%d\n",
351     	delta_y, delta_h, y_offset);
352 
353 
354     NOOP ("rodent_mk_text_entry...\n");
355     if(!population_p || !population_p->en || !population_p->en->path){
356 	NOOP(stderr, "rodent_mk_text_entry: invalid population\n");
357         return NULL;
358     }
359 
360     /* caso=0, rename; caso=1, duplicate; caso=2, symlink */
361 
362     entry = gtk_entry_new ();
363     hbox = rfm_hbox_new (FALSE, 0);
364     view_p->widgets.rename = gtk_window_new (GTK_WINDOW_POPUP);
365 
366     gint x_coordinate;
367     gint y_coordinate;
368     /* change relative coordinates to absolute coordinates and place entry window */
369     {
370         double sh=0;
371 	GtkScrolledWindow *scrolled_window = g_object_get_data(G_OBJECT(view_p->widgets.paper), "scrolled_window");
372         if(scrolled_window && GTK_IS_SCROLLED_WINDOW (scrolled_window)) {
373             sh = gtk_adjustment_get_value (gtk_scrolled_window_get_vadjustment (scrolled_window));
374         }
375         gtk_window_get_position ((GtkWindow *) rfm_global_p->window, &x_coordinate, &y_coordinate);
376         x_coordinate += ((frame_extent.width - window_extent.width)/2);
377         y_coordinate = label_rect.y + frame_extent.y - sh
378                     + y_offset + TEXTSPACING;
379         gtk_window_move ((GtkWindow *) view_p->widgets.rename,
380                          label_rect.x + x_coordinate,
381 			 y_coordinate);
382 
383     }
384 
385     gtk_window_set_resizable (GTK_WINDOW (view_p->widgets.rename), FALSE);
386     gtk_container_set_border_width (GTK_CONTAINER (view_p->widgets.rename), 0);
387     gtk_window_set_modal (GTK_WINDOW (view_p->widgets.rename), FALSE);
388 
389     gchar *g = NULL;
390     gchar *b = g_path_get_basename (population_p->en->path);
391     gchar *path = g_strdup (population_p->en->path);
392     g_object_set_data (G_OBJECT (view_p->widgets.rename), "path", path);
393     g_object_set_data (G_OBJECT (view_p->widgets.rename), "caso", GINT_TO_POINTER(caso));
394 
395     if(caso == 0) {
396         g = g_strdup (b);
397         rfm_status (&(view_p->widgets), "xffm/stock_dialog-warning", b, ": ", _("Rename"), "...", NULL);
398         g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (entry_activate_rename), view_p);
399 	NOOP(stderr, "Rename...\n");
400     } else if(caso == 1) {
401         gchar *dir = g_path_get_dirname (population_p->en->path);
402         g = g_strdup_printf (_("Copy of %s"), b);
403         g_free (dir);
404         rfm_status (&(view_p->widgets), "xffm/stock_dialog-warning", b, ": ", _("Duplicate"), "...", NULL);
405 	NOOP(stderr, "Duplicate...\n");
406         g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (entry_activate_duplicate), view_p);
407     } else if(caso == 2) {
408         g = g_strdup_printf (_("Link to %s"), b);
409         rfm_status (&(view_p->widgets), "xffm/stock_dialog-warning", b, ": ", _("Create Symbolic Link"), "...", NULL);
410 	NOOP(stderr, "Symlink...\n");
411         g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (entry_activate_symlink), view_p);
412     }
413     gchar *q = rfm_utf_string (g);
414     gchar *p=q;
415     double extra=0;
416 
417     while (p && *p) {  //hack: gtk bug workaround
418     gunichar up=g_utf8_get_char_validated (p, -1);
419     if (up > 0) {
420     if (g_unichar_isupper(up)) extra += 0.3;
421     //if (g_unichar_islower(up)) extra -= 0.1;
422     if (g_unichar_iswide(up)) extra += 1.0;
423     if (g_unichar_ispunct(up)) extra -= 0.2;
424     if (g_unichar_iszerowidth(up)) extra -= 1.0;
425     }
426     p++;
427     }
428     extra += 0.5;
429     int chars = extra + g_utf8_strlen (q, -1);
430     if (chars < 7) chars=7;
431 
432     gtk_entry_set_width_chars ((GtkEntry *)entry, chars);
433     gtk_entry_set_text ((GtkEntry *) entry, q);
434     g_free (q);
435     g_free (b);
436     g_free (g);
437 
438     gtk_editable_set_editable ((GtkEditable *) entry, TRUE);
439 
440     gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
441     gtk_container_add (GTK_CONTAINER (view_p->widgets.rename), hbox);
442 
443     gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
444 
445     g_signal_connect (G_OBJECT (view_p->widgets.rename), "destroy-event", G_CALLBACK (destroy_dialog), &(view_p->widgets));
446     g_signal_connect (G_OBJECT (view_p->widgets.rename), "key_press_event", G_CALLBACK (on_key_press), &(view_p->widgets));
447     g_signal_connect (G_OBJECT (view_p->widgets.rename), "delete-event", G_CALLBACK (destroy_dialog), &(view_p->widgets));
448 
449 
450     g_signal_connect (G_OBJECT (entry), "enter-notify-event", G_CALLBACK (grab_focus), view_p);
451     entry_aid(view_p, caso, label_rect.y);
452     NOOP ("now showing view_p->widgets.rename \n");
453     gtk_widget_show_all (view_p->widgets.rename);
454         gint endpos;
455         g = gtk_editable_get_chars ((GtkEditable *) entry, 0, -1);
456         if(strchr (g, '.')) {
457             gtk_editable_select_region ((GtkEditable *) entry, 0, 0);
458             for(endpos = strlen (g) - 1; g[endpos] >= 0; endpos--) {
459                 if(g[endpos] == '.')
460                     break;
461             }
462             NOOP ("entry box: endpos=%d\n", endpos);
463             gtk_editable_select_region ((GtkEditable *) entry, 0, endpos);
464         }
465         g_free (g);
466 
467     /* this sucks: gtk_widget_grab_focus (view_p->widgets.rename); */
468     XSetInputFocus (rfm_global_p->Xdisplay,
469 	    GDK_WINDOW_XID (gtk_widget_get_parent_window (entry)), RevertToParent, CurrentTime);
470 
471     gtk_window_set_transient_for (GTK_WINDOW (view_p->widgets.rename), GTK_WINDOW (rfm_global_p->window));
472 
473     rfm_get_drawable_geometry(rfm_global_p->root_Xwindow, NULL, NULL, &window_extent.width, &window_extent.height, NULL);
474 
475 
476     //test 1, is the box larger than the root window?
477     GtkAllocation allocation;
478     gtk_widget_get_allocation (view_p->widgets.rename, &allocation);
479 
480     if (allocation.width > window_extent.width) {
481         gtk_widget_set_size_request(entry, window_extent.width-6, -1);
482         gtk_widget_set_size_request(view_p->widgets.rename, window_extent.width, -1);
483         x_coordinate=0;
484         // this does not do what I think it should do (show start of truncated text):
485         //gtk_entry_set_alignment((GtkEntry *)entry, 0);
486     } else { // it fits, try to place it.
487         int center = x_coordinate + label_rect.x;
488         // overspill?
489         if (center + allocation.width > window_extent.width) x_coordinate = window_extent.width - allocation.width;
490         // center is OK
491         else x_coordinate=center;
492     }
493 
494 
495     gtk_window_move ((GtkWindow *) view_p->widgets.rename,
496                 x_coordinate, //
497 		y_coordinate);
498     XGrabPointer (rfm_global_p->Xdisplay,
499                   GDK_WINDOW_XID (gtk_widget_get_parent_window (entry)),
500                   TRUE, 0, GrabModeSync, GrabModeAsync,
501                   GDK_WINDOW_XID (gtk_widget_get_parent_window (entry)),
502                   None, CurrentTime);
503     XUngrabPointer(rfm_global_p->Xdisplay,CurrentTime);
504     gtk_main();
505     return NULL;
506 }
507 
508 
509