rofi  1.6.1
filebrowser.c
Go to the documentation of this file.
1 
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <gmodule.h>
32 #include <gio/gio.h>
33 
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <dirent.h>
37 
38 #include "mode.h"
39 #include "helper.h"
40 #include "mode-private.h"
41 #include "dialogs/filebrowser.h"
42 #include "rofi.h"
43 #include "history.h"
44 
45 #include <stdint.h>
46 
47 #include "rofi-icon-fetcher.h"
48 
49 #define FILEBROWSER_CACHE_FILE "rofi3.filebrowsercache"
50 
55 {
56  UP,
60 };
61 const char *icon_name[NUM_FILE_TYPES] =
62 {
63  "go-up",
64  "folder",
65  "gtk-file"
66 };
67 typedef struct
68 {
69  char *name;
70  char *path;
71  enum FBFileType type;
72  uint32_t icon_fetch_uid;
73  gboolean link;
74 } FBFile;
75 
76 typedef struct
77 {
78  GFile *current_dir;
80  unsigned int array_length;
82 
84 {
85  for ( unsigned int i = 0; i < pd->array_length; i++ ) {
86  FBFile *fb = &( pd->array[i] );
87  g_free ( fb->name );
88  g_free ( fb->path );
89  }
90  g_free ( pd->array );
91  pd->array = NULL;
92  pd->array_length = 0;
93 }
94 #include <sys/types.h>
95 #include <dirent.h>
96 
97 static gint compare ( gconstpointer a, gconstpointer b, G_GNUC_UNUSED gpointer data )
98 {
99  FBFile *fa = (FBFile*) a;
100  FBFile *fb = (FBFile*) b;
101  if ( fa->type != fb->type ) {
102  return fa->type - fb->type;
103  }
104 
105  return g_strcmp0 ( fa->name, fb->name );
106 }
107 
108 static void get_file_browser ( Mode *sw )
109 {
115  char *cdir = g_file_get_path ( pd->current_dir );
116  DIR *dir = opendir ( cdir );
117  if ( dir ) {
118  struct dirent *rd = NULL;
119  while ( ( rd = readdir ( dir ) ) != NULL ) {
120  if ( g_strcmp0 ( rd->d_name, ".." ) == 0 ) {
121  pd->array = g_realloc ( pd->array, ( pd->array_length + 1 ) * sizeof ( FBFile ) );
122  // Rofi expects utf-8, so lets convert the filename.
123  pd->array[pd->array_length].name = g_strdup ( ".." );
124  pd->array[pd->array_length].path = NULL;
125  pd->array[pd->array_length].type = UP;
126  pd->array[pd->array_length].icon_fetch_uid = 0;
127  pd->array[pd->array_length].link = FALSE;
128  pd->array_length++;
129  continue;
130  }
131  else if ( rd->d_name[0] == '.' ) {
132  continue;
133  }
134 
135  switch ( rd->d_type )
136  {
137  case DT_BLK:
138  case DT_CHR:
139  case DT_FIFO:
140  case DT_UNKNOWN:
141  case DT_SOCK:
142  default:
143  break;
144  case DT_REG:
145  case DT_DIR:
146  pd->array = g_realloc ( pd->array, ( pd->array_length + 1 ) * sizeof ( FBFile ) );
147  // Rofi expects utf-8, so lets convert the filename.
148  pd->array[pd->array_length].name = g_filename_to_utf8 ( rd->d_name, -1, NULL, NULL, NULL );
149  pd->array[pd->array_length].path = g_build_filename ( cdir, rd->d_name, NULL );
150  pd->array[pd->array_length].type = ( rd->d_type == DT_DIR ) ? DIRECTORY : RFILE;
151  pd->array[pd->array_length].icon_fetch_uid = 0;
152  pd->array[pd->array_length].link = FALSE;
153  pd->array_length++;
154  break;
155  case DT_LNK:
156  pd->array = g_realloc ( pd->array, ( pd->array_length + 1 ) * sizeof ( FBFile ) );
157  // Rofi expects utf-8, so lets convert the filename.
158  pd->array[pd->array_length].name = g_filename_to_utf8 ( rd->d_name, -1, NULL, NULL, NULL );
159  pd->array[pd->array_length].path = g_build_filename ( cdir, rd->d_name, NULL );
160  pd->array[pd->array_length].icon_fetch_uid = 0;
161  pd->array[pd->array_length].link = TRUE;
162  // Default to file.
163  pd->array[pd->array_length].type = RFILE;
164  {
165  // If we have link, use a stat to fine out what it is, if we fail, we mark it as file.
166  // TODO have a 'broken link' mode?
167  // Convert full path to right encoding.
168  char *file = g_filename_from_utf8 ( pd->array[pd->array_length].path, -1, NULL, NULL, NULL );
169  if ( file ) {
170  struct stat statbuf;
171  if ( stat ( file, &statbuf ) == 0 ) {
172  if ( S_ISDIR ( statbuf.st_mode ) ) {
173  pd->array[pd->array_length].type = DIRECTORY;
174  }
175  else if ( S_ISREG ( statbuf.st_mode ) ) {
176  pd->array[pd->array_length].type = RFILE;
177  }
178  }
179  else {
180  g_warning ( "Failed to stat file: %s, %s", file, strerror ( errno ) );
181  }
182 
183  g_free ( file );
184  }
185  }
186  pd->array_length++;
187  break;
188  }
189  }
190  closedir ( dir );
191  }
192  g_qsort_with_data ( pd->array, pd->array_length, sizeof ( FBFile ), compare, NULL );
193 }
194 
195 static int file_browser_mode_init ( Mode *sw )
196 {
200  if ( mode_get_private_data ( sw ) == NULL ) {
201  FileBrowserModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
202  mode_set_private_data ( sw, (void *) pd );
203  {
204  char *path = g_build_filename ( cache_dir, FILEBROWSER_CACHE_FILE, NULL );
205  char *file = NULL;
206  if ( g_file_get_contents ( path, &file, NULL, NULL ) ) {
207  if ( g_file_test ( file, G_FILE_TEST_IS_DIR ) ) {
208  pd->current_dir = g_file_new_for_path ( file );
209  }
210  g_free ( file );
211  }
212  // Store it based on the unique identifiers (desktop_id).
213  g_free ( path );
214  if ( pd->current_dir == NULL ) {
215  pd->current_dir = g_file_new_for_path ( g_get_home_dir () );
216  }
217  }
218  // Load content.
219  get_file_browser ( sw );
220  }
221  return TRUE;
222 }
223 static unsigned int file_browser_mode_get_num_entries ( const Mode *sw )
224 {
226  return pd->array_length;
227 }
228 
229 static ModeMode file_browser_mode_result ( Mode *sw, int mretv, char **input, unsigned int selected_line )
230 {
231  ModeMode retv = MODE_EXIT;
233  if ( mretv & MENU_NEXT ) {
234  retv = NEXT_DIALOG;
235  }
236  else if ( mretv & MENU_PREVIOUS ) {
237  retv = PREVIOUS_DIALOG;
238  }
239  else if ( mretv & MENU_QUICK_SWITCH ) {
240  retv = ( mretv & MENU_LOWER_MASK );
241  }
242  else if ( ( mretv & MENU_OK ) ) {
243  if ( selected_line < pd->array_length ) {
244  if ( pd->array[selected_line].type == UP ) {
245  GFile *new = g_file_get_parent ( pd->current_dir );
246  if ( new ) {
247  g_object_unref ( pd->current_dir );
248  pd->current_dir = new;
249  free_list ( pd );
250  get_file_browser ( sw );
251  return RESET_DIALOG;
252  }
253  }
254  else if ( pd->array[selected_line].type == DIRECTORY ) {
255  char *path = g_build_filename ( cache_dir, FILEBROWSER_CACHE_FILE, NULL );
256  g_file_set_contents ( path, pd->array[selected_line].path, -1, NULL );
257  g_free ( path );
258  GFile *new = g_file_new_for_path ( pd->array[selected_line].path );
259  g_object_unref ( pd->current_dir );
260  pd->current_dir = new;
261  free_list ( pd );
262  get_file_browser ( sw );
263  return RESET_DIALOG;
264  }
265  else if ( pd->array[selected_line].type == RFILE ) {
266  char *d = g_filename_from_utf8 ( pd->array[selected_line].path, -1, NULL, NULL, NULL );
267  char *cmd = g_strdup_printf ( "xdg-open '%s'", d );
268  g_free ( d );
269  char *cdir = g_file_get_path ( pd->current_dir );
270  helper_execute_command ( cdir, cmd, FALSE, NULL );
271  g_free ( cdir );
272  g_free ( cmd );
273  return MODE_EXIT;
274  }
275  }
276  retv = RELOAD_DIALOG;
277  }
278  else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input ) {
279  char *p = rofi_expand_path ( *input );
280  char *dir = g_filename_from_utf8 ( p, -1, NULL, NULL, NULL );
281  g_free ( p );
282  if ( g_file_test ( dir, G_FILE_TEST_EXISTS ) ) {
283  if ( g_file_test ( dir, G_FILE_TEST_IS_DIR ) ) {
284  g_object_unref ( pd->current_dir );
285  pd->current_dir = g_file_new_for_path ( dir );
286  g_free ( dir );
287  free_list ( pd );
288  get_file_browser ( sw );
289  return RESET_DIALOG;
290  }
291  }
292  g_free ( dir );
293  retv = RELOAD_DIALOG;
294  }
295  else if ( ( mretv & MENU_ENTRY_DELETE ) == MENU_ENTRY_DELETE ) {
296  retv = RELOAD_DIALOG;
297  }
298  return retv;
299 }
300 
301 static void file_browser_mode_destroy ( Mode *sw )
302 {
304  if ( pd != NULL ) {
305  g_object_unref ( pd->current_dir );
306  free_list ( pd );
307  g_free ( pd );
308  mode_set_private_data ( sw, NULL );
309  }
310 }
311 
312 static char *_get_display_value ( const Mode *sw, unsigned int selected_line, G_GNUC_UNUSED int *state, G_GNUC_UNUSED GList **attr_list, int get_entry )
313 {
315 
316  // Only return the string if requested, otherwise only set state.
317  if ( !get_entry ) {
318  return NULL;
319  }
320  if ( pd->array[selected_line].type == UP ) {
321  return g_strdup ( " .." );
322  }
323  else {
324  if ( pd->array[selected_line].link ) {
325  return g_strconcat ( "@", pd->array[selected_line].name, NULL );
326  }
327  else {
328  return g_strdup ( pd->array[selected_line].name );
329  }
330  }
331  return g_strdup ( "n/a" );
332 }
333 
343 static int file_browser_token_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index )
344 {
346 
347  // Call default matching function.
348  return helper_token_match ( tokens, pd->array[index].name );
349 }
350 
351 static cairo_surface_t *_get_icon ( const Mode *sw, unsigned int selected_line, int height )
352 {
354  g_return_val_if_fail ( pd->array != NULL, NULL );
355  FBFile *dr = &( pd->array[selected_line] );
356  if ( dr->icon_fetch_uid > 0 ) {
357  return rofi_icon_fetcher_get ( dr->icon_fetch_uid );
358  }
359  if ( rofi_icon_fetcher_file_is_image ( dr->path ) ) {
360  dr->icon_fetch_uid = rofi_icon_fetcher_query ( dr->path, height );
361  }
362  else {
363  dr->icon_fetch_uid = rofi_icon_fetcher_query ( icon_name[dr->type], height );
364  }
365  return rofi_icon_fetcher_get ( dr->icon_fetch_uid );
366 }
367 
368 static char * _get_message ( const Mode *sw )
369 {
371  if ( pd->current_dir ) {
372  char *dirname = g_file_get_parse_name ( pd->current_dir );
373  char *str = g_markup_printf_escaped ( "<b>Current directory:</b> %s", dirname );
374  g_free ( dirname );
375  return str;
376  }
377  return "n/a";
378 }
379 
380 static char *_get_completion ( const Mode *sw, unsigned int index )
381 {
383 
384  char *d = g_strescape ( pd->array[index].path, NULL );
385  return d;
386 }
387 
389 {
390  Mode *sw = g_malloc0 ( sizeof ( Mode ) );
391 
392  *sw = file_browser_mode;
393 
394  sw->private_data = NULL;
395  return sw;
396 }
397 
398 #if 0
399 ModeMode file_browser_mode_completer ( Mode *sw, int mretv, char **input, unsigned int selected_line, char **path )
400 {
401  ModeMode retv = MODE_EXIT;
403  if ( mretv & MENU_NEXT ) {
404  retv = NEXT_DIALOG;
405  }
406  else if ( mretv & MENU_PREVIOUS ) {
407  retv = PREVIOUS_DIALOG;
408  }
409  else if ( mretv & MENU_QUICK_SWITCH ) {
410  retv = ( mretv & MENU_LOWER_MASK );
411  }
412  else if ( ( mretv & MENU_OK ) ) {
413  if ( selected_line < pd->array_length ) {
414  if ( pd->array[selected_line].type == UP ) {
415  GFile *new = g_file_get_parent ( pd->current_dir );
416  if ( new ) {
417  g_object_unref ( pd->current_dir );
418  pd->current_dir = new;
419  free_list ( pd );
420  get_file_browser ( sw );
421  return RESET_DIALOG;
422  }
423  }
424  else if ( pd->array[selected_line].type == DIRECTORY ) {
425  GFile *new = g_file_new_for_path ( pd->array[selected_line].path );
426  g_object_unref ( pd->current_dir );
427  pd->current_dir = new;
428  free_list ( pd );
429  get_file_browser ( sw );
430  return RESET_DIALOG;
431  }
432  else if ( pd->array[selected_line].type == RFILE ) {
433  *path = g_strescape ( pd->array[selected_line].path, NULL );
434  return MODE_EXIT;
435  }
436  }
437  retv = RELOAD_DIALOG;
438  }
439  else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input ) {
440  char *p = rofi_expand_path ( *input );
441  char *dir = g_filename_from_utf8 ( p, -1, NULL, NULL, NULL );
442  g_free ( p );
443  if ( g_file_test ( dir, G_FILE_TEST_EXISTS ) ) {
444  if ( g_file_test ( dir, G_FILE_TEST_IS_DIR ) ) {
445  g_object_unref ( pd->current_dir );
446  pd->current_dir = g_file_new_for_path ( dir );
447  g_free ( dir );
448  free_list ( pd );
449  get_file_browser ( sw );
450  return RESET_DIALOG;
451  }
452  }
453  g_free ( dir );
454  retv = RELOAD_DIALOG;
455  }
456  else if ( ( mretv & MENU_ENTRY_DELETE ) == MENU_ENTRY_DELETE ) {
457  retv = RELOAD_DIALOG;
458  }
459  return retv;
460 }
461 #endif
462 
464 {
465  .display_name = NULL,
466  .abi_version = ABI_VERSION,
467  .name = "file-browser",
468  .cfg_name_key = "display-file_browser",
469  ._init = file_browser_mode_init,
470  ._get_num_entries = file_browser_mode_get_num_entries,
471  ._result = file_browser_mode_result,
472  ._destroy = file_browser_mode_destroy,
473  ._token_match = file_browser_token_match,
474  ._get_display_value = _get_display_value,
475  ._get_icon = _get_icon,
476  ._get_message = _get_message,
477  ._get_completion = _get_completion,
478  ._preprocess_input = NULL,
479  .private_data = NULL,
480  .free = NULL,
481 };
static int file_browser_token_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition: filebrowser.c:343
static void free_list(FileBrowserModePrivateData *pd)
Definition: filebrowser.c:83
static cairo_surface_t * _get_icon(const Mode *sw, unsigned int selected_line, int height)
Definition: filebrowser.c:351
static gint compare(gconstpointer a, gconstpointer b, G_GNUC_UNUSED gpointer data)
Definition: filebrowser.c:97
static unsigned int file_browser_mode_get_num_entries(const Mode *sw)
Definition: filebrowser.c:223
static char * _get_message(const Mode *sw)
Definition: filebrowser.c:368
FBFileType
Definition: filebrowser.c:55
@ NUM_FILE_TYPES
Definition: filebrowser.c:59
@ DIRECTORY
Definition: filebrowser.c:57
@ UP
Definition: filebrowser.c:56
@ RFILE
Definition: filebrowser.c:58
static char * _get_completion(const Mode *sw, unsigned int index)
Definition: filebrowser.c:380
static char * _get_display_value(const Mode *sw, unsigned int selected_line, G_GNUC_UNUSED int *state, G_GNUC_UNUSED GList **attr_list, int get_entry)
Definition: filebrowser.c:312
#define FILEBROWSER_CACHE_FILE
Definition: filebrowser.c:49
static void file_browser_mode_destroy(Mode *sw)
Definition: filebrowser.c:301
static void get_file_browser(Mode *sw)
Definition: filebrowser.c:108
static int file_browser_mode_init(Mode *sw)
Definition: filebrowser.c:195
static ModeMode file_browser_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
Definition: filebrowser.c:229
const char * icon_name[NUM_FILE_TYPES]
Definition: filebrowser.c:61
Mode file_browser_mode
Definition: filebrowser.c:463
ModeMode file_browser_mode_completer(Mode *sw, int mretv, char **input, unsigned int selected_line, char **path)
Mode * create_new_file_browser(void)
Definition: filebrowser.c:388
gboolean helper_execute_command(const char *wd, const char *cmd, gboolean run_in_term, RofiHelperExecuteContext *context)
Definition: helper.c:1042
char * rofi_expand_path(const char *input)
Definition: helper.c:711
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
Definition: helper.c:488
gboolean rofi_icon_fetcher_file_is_image(const char *const path)
cairo_surface_t * rofi_icon_fetcher_get(const uint32_t uid)
uint32_t rofi_icon_fetcher_query(const char *name, const int size)
void mode_set_private_data(Mode *mode, void *pd)
Definition: mode.c:145
void * mode_get_private_data(const Mode *mode)
Definition: mode.c:139
ModeMode
Definition: mode.h:50
@ MENU_LOWER_MASK
Definition: mode.h:87
@ MENU_PREVIOUS
Definition: mode.h:83
@ MENU_QUICK_SWITCH
Definition: mode.h:79
@ MENU_ENTRY_DELETE
Definition: mode.h:77
@ MENU_NEXT
Definition: mode.h:73
@ MENU_OK
Definition: mode.h:69
@ MENU_CUSTOM_INPUT
Definition: mode.h:75
@ MODE_EXIT
Definition: mode.h:52
@ NEXT_DIALOG
Definition: mode.h:54
@ RELOAD_DIALOG
Definition: mode.h:56
@ PREVIOUS_DIALOG
Definition: mode.h:58
@ RESET_DIALOG
Definition: mode.h:60
const char * cache_dir
Definition: rofi.c:84
#define ABI_VERSION
Definition: mode-private.h:34
enum FBFileType type
Definition: filebrowser.c:71
gboolean link
Definition: filebrowser.c:73
char * path
Definition: filebrowser.c:70
char * name
Definition: filebrowser.c:69
uint32_t icon_fetch_uid
Definition: filebrowser.c:72
char * display_name
Definition: mode-private.h:158
void * private_data
Definition: mode-private.h:185