2018-08-14 11:13:38 +03:00
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE . md
* Copyright ( C ) 2018 K . Lange
*
2018-11-20 04:07:30 +03:00
* file - browser - Graphical file manager .
2018-08-14 11:13:38 +03:00
*
2018-11-20 04:07:30 +03:00
* Based on the original Python implementation and inspired by
* Nautilus and Thunar . Also provides a " wallpaper " mode for
* managing the desktop backgrond .
2018-08-14 11:13:38 +03:00
*/
2018-05-21 02:12:02 +03:00
# include <stdio.h>
2018-06-04 06:35:46 +03:00
# include <unistd.h>
2018-08-10 07:57:24 +03:00
# include <dirent.h>
# include <time.h>
2018-08-12 14:03:37 +03:00
# include <math.h>
2018-11-18 04:25:24 +03:00
# include <libgen.h>
2018-11-19 15:14:39 +03:00
# include <signal.h>
2018-12-23 11:24:26 +03:00
# include <ctype.h>
2018-08-10 07:57:24 +03:00
# include <sys/stat.h>
# include <sys/time.h>
2018-11-19 13:26:40 +03:00
# include <sys/wait.h>
2018-12-05 10:32:43 +03:00
# include <sys/fswait.h>
2018-05-21 02:12:02 +03:00
# include <toaru/yutani.h>
# include <toaru/graphics.h>
# include <toaru/decorations.h>
# include <toaru/menu.h>
2018-08-10 07:57:24 +03:00
# include <toaru/icon_cache.h>
# include <toaru/list.h>
# include <toaru/sdf.h>
2018-11-24 07:58:46 +03:00
# include <toaru/button.h>
2018-12-05 07:01:16 +03:00
# include <toaru/jpeg.h>
2018-05-21 02:12:02 +03:00
# define APPLICATION_TITLE "File Browser"
2018-11-20 04:07:30 +03:00
# define SCROLL_AMOUNT 120
2018-12-05 07:01:16 +03:00
# define WALLPAPER_PATH " / usr / share / wallpaper.jpg"
2018-05-21 02:12:02 +03:00
2018-11-20 04:07:30 +03:00
struct File {
char name [ 256 ] ; /* Displayed name (icon label) */
char icon [ 256 ] ; /* Icon identifier */
char link [ 256 ] ; /* Link target for symlinks */
char launcher [ 256 ] ; /* Launcher spec */
char filename [ 256 ] ; /* Actual filename for launchers */
2018-12-26 03:53:18 +03:00
char filetype [ 256 ] ; /* Textual description of file type */
uint64_t size ; /* File size */
2018-11-20 04:07:30 +03:00
int type ; /* File type: 0 = normal, 1 = directory, 2 = launcher */
int selected ; /* Selection status */
} ;
2018-11-18 04:25:24 +03:00
2018-05-21 02:12:02 +03:00
static yutani_t * yctx ;
static yutani_window_t * main_window ;
static gfx_context_t * ctx ;
2018-11-20 04:07:30 +03:00
static int application_running = 1 ; /* Big loop exit condition */
static int show_hidden = 0 ; /* Whether or not show hidden files */
static int scroll_offset = 0 ; /* How far the icon view should be scrolled */
static int available_height = 0 ; /* How much space is available in the main window for the icon view */
static int is_desktop_background = 0 ; /* If we're in desktop background mode */
2018-11-24 07:58:46 +03:00
static int menu_bar_height = MENU_BAR_HEIGHT + 36 ; /* Height of the menu bar, if present - it's not in desktop mode */
2018-11-20 04:07:30 +03:00
static sprite_t * wallpaper_buffer = NULL ; /* Prebaked wallpaper texture */
2018-12-05 10:32:43 +03:00
static sprite_t * wallpaper_old = NULL ;
static uint64_t timer = 0 ;
2018-12-05 10:50:23 +03:00
static int restart = 0 ;
2018-11-20 04:07:30 +03:00
static char title [ 512 ] ; /* Application title bar */
static int FILE_HEIGHT = 80 ; /* Height of one row of icons */
static int FILE_WIDTH = 100 ; /* Width of one column of icons */
static int FILE_PTR_WIDTH = 1 ; /* How many icons wide the display should be */
static sprite_t * contents_sprite = NULL ; /* Icon view rendering context */
static gfx_context_t * contents = NULL ; /* Icon view rendering context */
static char * current_directory = NULL ; /* Current directory path */
static int hilighted_offset = - 1 ; /* Which file is hovered by the mouse */
static struct File * * file_pointers = NULL ; /* List of file pointers */
static ssize_t file_pointers_len = 0 ; /* How many files are in the current list */
static uint64_t last_click = 0 ; /* For double click */
static int last_click_offset = - 1 ; /* So that clicking two different things quickly doesn't count as a double click */
2018-12-23 11:16:20 +03:00
static char nav_bar [ 512 ] = { 0 } ;
2018-12-27 07:01:12 +03:00
static int nav_bar_cursor = 0 ;
static int nav_bar_cursor_x = 0 ;
2018-12-23 11:16:20 +03:00
static int nav_bar_focused = 0 ;
2019-01-03 09:46:37 +03:00
static char window_status [ 512 ] = { 0 } ;
2018-11-24 08:11:02 +03:00
static int _button_hilights [ 4 ] = { 3 , 3 , 3 , 3 } ;
static int _button_disabled [ 4 ] = { 1 , 1 , 0 , 0 } ;
static int _button_hover = - 1 ;
2018-11-20 04:07:30 +03:00
/* Menu bar entries */
2018-05-21 02:12:02 +03:00
static struct menu_bar menu_bar = { 0 } ;
static struct menu_bar_entries menu_entries [ ] = {
2018-12-23 10:16:42 +03:00
{ " File " , " file " } , /* 0 */
{ " Edit " , " edit " } , /* 1 */
{ " View " , " view " } , /* 2 */
{ " Go " , " go " } , /* 3 */
{ " Help " , " help " } , /* 4 */
2018-05-21 02:12:02 +03:00
{ NULL , NULL } ,
} ;
2018-11-18 03:38:47 +03:00
static struct MenuList * context_menu = NULL ;
2018-11-20 04:07:30 +03:00
/**
* Accurate time comparison .
*
* These methods were taken from the compositor and
* allow us to time double - clicks accurately .
*/
static uint64_t precise_current_time ( void ) {
struct timeval t ;
gettimeofday ( & t , NULL ) ;
time_t sec_diff = t . tv_sec ;
suseconds_t usec_diff = t . tv_usec ;
2018-12-12 06:38:08 +03:00
return ( uint64_t ) ( ( uint64_t ) sec_diff * 1000LL + usec_diff / 1000 ) ;
2018-11-20 04:07:30 +03:00
}
static uint64_t precise_time_since ( uint64_t start_time ) {
2018-12-12 06:38:08 +03:00
uint64_t now = precise_current_time ( ) ;
uint64_t diff = now - start_time ; /* Milliseconds */
2018-11-20 04:07:30 +03:00
return diff ;
2018-05-21 02:12:02 +03:00
}
2018-11-20 04:07:30 +03:00
/**
* When in desktop mode , we fake decoration boundaries to
* position the icon view correctly . When in normal mode ,
* we just passt through the actual bounds .
*/
2018-11-19 15:14:39 +03:00
static int _decor_get_bounds ( yutani_window_t * win , struct decor_bounds * bounds ) {
if ( is_desktop_background ) {
memset ( bounds , 0 , sizeof ( struct decor_bounds ) ) ;
2018-11-24 07:58:46 +03:00
bounds - > top_height = 54 ;
2018-11-19 15:14:39 +03:00
bounds - > left_width = 20 ;
return 0 ;
}
return decor_get_bounds ( win , bounds ) ;
}
2018-11-20 04:07:30 +03:00
/**
* This should probably be in a yutani core library . . .
*
* If a down and up event were close enough together to be considered a click .
*/
2018-08-12 14:03:37 +03:00
static int _close_enough ( struct yutani_msg_window_mouse_event * me ) {
if ( me - > command = = YUTANI_MOUSE_EVENT_RAISE & & sqrt ( pow ( me - > new_x - me - > old_x , 2 ) + pow ( me - > new_y - me - > old_y , 2 ) ) < 10 ) {
return 1 ;
}
return 0 ;
}
2018-08-10 07:57:24 +03:00
2018-12-26 03:53:18 +03:00
/**
* Select view mode .
*/
# define VIEW_MODE_ICONS 0
# define VIEW_MODE_TILES 1
# define VIEW_MODE_LIST 2
static int view_mode = VIEW_MODE_ICONS ;
2018-11-20 04:07:30 +03:00
/**
* Clear out the space for an icon .
* We clear to transparent so that the desktop background can be shown in desktop mode .
*/
2018-08-12 14:03:37 +03:00
static void clear_offset ( int offset ) {
2018-11-20 04:07:30 +03:00
/* From the flat array offset, figure out the x/y offset. */
2018-11-18 12:36:03 +03:00
int offset_y = offset / FILE_PTR_WIDTH ;
int offset_x = offset % FILE_PTR_WIDTH ;
2018-11-19 15:14:39 +03:00
draw_rectangle_solid ( contents , offset_x * FILE_WIDTH , offset_y * FILE_HEIGHT , FILE_WIDTH , FILE_HEIGHT , rgba ( 0 , 0 , 0 , 0 ) ) ;
2018-08-12 14:03:37 +03:00
}
2018-08-10 07:57:24 +03:00
2019-01-03 09:46:37 +03:00
static int print_human_readable_size ( char * _out , uint64_t s ) {
if ( s > = 1LL < < 30 ) {
size_t t = s / ( 1LL < < 30 ) ;
return sprintf ( _out , " %d.%1d GiB " , ( int ) t , ( int ) ( ( int ) ( s - t * ( 1LL < < 30 ) ) / ( ( 1LL < < 30 ) / 10 ) ) ) ;
} else if ( s > = 1 < < 20 ) {
2018-12-26 03:53:18 +03:00
size_t t = s / ( 1 < < 20 ) ;
return sprintf ( _out , " %d.%1d MiB " , ( int ) t , ( int ) ( s - t * ( 1 < < 20 ) ) / ( ( 1 < < 20 ) / 10 ) ) ;
} else if ( s > = 1 < < 10 ) {
size_t t = s / ( 1 < < 10 ) ;
return sprintf ( _out , " %d.%1d KiB " , ( int ) t , ( int ) ( s - t * ( 1 < < 10 ) ) / ( ( 1 < < 10 ) / 10 ) ) ;
} else {
return sprintf ( _out , " %d B " , ( int ) s ) ;
}
}
# define HILIGHT_BORDER_TOP rgb(54,128,205)
# define HILIGHT_GRADIENT_TOP rgb(93,163,236)
# define HILIGHT_GRADIENT_BOTTOM rgb(56,137,220)
# define HILIGHT_BORDER_BOTTOM rgb(47,106,167)
2018-11-20 04:07:30 +03:00
/**
* Draw an icon view entry
*/
2018-08-12 14:03:37 +03:00
static void draw_file ( struct File * f , int offset ) {
2018-11-20 04:07:30 +03:00
/* From the flat array offset, figure out the x/y offset. */
2018-11-18 12:36:03 +03:00
int offset_y = offset / FILE_PTR_WIDTH ;
int offset_x = offset % FILE_PTR_WIDTH ;
int x = offset_x * FILE_WIDTH ;
int y = offset_y * FILE_HEIGHT ;
2018-11-20 04:07:30 +03:00
/* Load the icon sprite from the cache */
2018-12-26 03:53:18 +03:00
if ( view_mode = = VIEW_MODE_ICONS ) {
sprite_t * icon = icon_get_48 ( f - > icon ) ;
/* If the display name is too long to fit, cut it with an ellipsis. */
int len = strlen ( f - > name ) ;
char * name = malloc ( len + 4 ) ;
memcpy ( name , f - > name , len + 1 ) ;
int name_width ;
while ( ( name_width = draw_sdf_string_width ( name , 16 , SDF_FONT_THIN ) ) > FILE_WIDTH - 8 /* Padding */ ) {
len - - ;
name [ len + 0 ] = ' . ' ;
name [ len + 1 ] = ' . ' ;
name [ len + 2 ] = ' . ' ;
name [ len + 3 ] = ' \0 ' ;
}
2018-11-18 12:36:03 +03:00
2018-12-26 03:53:18 +03:00
/* Draw the icon */
int center_x_icon = ( FILE_WIDTH - icon - > width ) / 2 ;
int center_x_text = ( FILE_WIDTH - name_width ) / 2 ;
draw_sprite ( contents , icon , center_x_icon + x , y + 2 ) ;
2018-11-18 12:36:03 +03:00
2018-12-26 03:53:18 +03:00
if ( f - > selected ) {
/* If this file is selected, paint the icon blue... */
if ( main_window - > focused ) {
draw_sprite_alpha_paint ( contents , icon , center_x_icon + x , y + 2 , 0.5 , rgb ( 72 , 167 , 255 ) ) ;
}
/* And draw the name with a blue background and white text */
draw_rounded_rectangle ( contents , center_x_text + x - 2 , y + 54 , name_width + 6 , 20 , 3 , rgb ( 72 , 167 , 255 ) ) ;
draw_sdf_string ( contents , center_x_text + x , y + 54 , name , 16 , rgb ( 255 , 255 , 255 ) , SDF_FONT_THIN ) ;
} else {
if ( is_desktop_background ) {
/* If this is the desktop view, white text with a drop shadow */
draw_sdf_string_stroke ( contents , center_x_text + x + 1 , y + 55 , name , 16 , rgba ( 0 , 0 , 0 , 120 ) , SDF_FONT_THIN , 1.7 , 0.5 ) ;
draw_sdf_string ( contents , center_x_text + x , y + 54 , name , 16 , rgb ( 255 , 255 , 255 ) , SDF_FONT_THIN ) ;
} else {
/* Otherwise, black text */
draw_sdf_string ( contents , center_x_text + x , y + 54 , name , 16 , rgb ( 0 , 0 , 0 ) , SDF_FONT_THIN ) ;
}
}
2018-11-20 04:07:30 +03:00
2018-12-26 03:53:18 +03:00
if ( offset = = hilighted_offset ) {
/* The hovered icon should have some added brightness, so paint it white */
draw_sprite_alpha_paint ( contents , icon , center_x_icon + x , y + 2 , 0.3 , rgb ( 255 , 255 , 255 ) ) ;
2018-11-22 12:23:25 +03:00
}
2018-12-26 03:53:18 +03:00
if ( f - > link [ 0 ] ) {
/* For symlinks, draw an indicator */
sprite_t * arrow = icon_get_16 ( " forward " ) ;
draw_sprite ( contents , arrow , center_x_icon + 32 + x , y + 32 ) ;
}
free ( name ) ;
} else if ( view_mode = = VIEW_MODE_TILES ) {
sprite_t * icon = icon_get_48 ( f - > icon ) ;
uint32_t text_color = rgb ( 0 , 0 , 0 ) ;
/* If selected, draw background box */
if ( f - > selected ) {
struct gradient_definition edge = { FILE_HEIGHT - 4 , y + 2 , HILIGHT_BORDER_TOP , HILIGHT_BORDER_BOTTOM } ;
struct gradient_definition body = { FILE_HEIGHT - 6 , y + 3 , HILIGHT_GRADIENT_TOP , HILIGHT_GRADIENT_BOTTOM } ;
draw_rounded_rectangle_pattern ( contents , x + 2 , y + 2 , FILE_WIDTH - 4 , FILE_HEIGHT - 4 , 3 , gfx_vertical_gradient_pattern , & edge ) ;
draw_rounded_rectangle_pattern ( contents , x + 3 , y + 3 , FILE_WIDTH - 6 , FILE_HEIGHT - 6 , 4 , gfx_vertical_gradient_pattern , & body ) ;
text_color = rgb ( 255 , 255 , 255 ) ;
}
draw_sprite ( contents , icon , x + 11 , y + 11 ) ;
if ( offset = = hilighted_offset ) {
/* The hovered icon should have some added brightness, so paint it white */
draw_sprite_alpha_paint ( contents , icon , x + 11 , y + 11 , 0.3 , rgb ( 255 , 255 , 255 ) ) ;
}
int len = strlen ( f - > name ) ;
char * name = malloc ( len + 4 ) ;
memcpy ( name , f - > name , len + 1 ) ;
int name_width ;
while ( ( name_width = draw_sdf_string_width ( name , 16 , SDF_FONT_BOLD ) ) > FILE_WIDTH - 81 ) {
len - - ;
name [ len + 0 ] = ' . ' ;
name [ len + 1 ] = ' . ' ;
name [ len + 2 ] = ' . ' ;
name [ len + 3 ] = ' \0 ' ;
}
if ( f - > type = = 0 ) {
draw_sdf_string ( contents , x + 70 , y + 8 , name , 16 , text_color , SDF_FONT_BOLD ) ;
draw_sdf_string ( contents , x + 70 , y + 25 , f - > filetype , 16 , text_color , SDF_FONT_THIN ) ;
char line_three [ 48 ] = { 0 } ;
if ( * f - > link ) {
sprintf ( line_three , " Symbolic link " ) ;
} else {
print_human_readable_size ( line_three , f - > size ) ;
}
draw_sdf_string ( contents , x + 70 , y + 42 , line_three , 16 , text_color , SDF_FONT_THIN ) ;
2018-11-19 15:14:39 +03:00
} else {
2018-12-26 03:53:18 +03:00
draw_sdf_string ( contents , x + 70 , y + 15 , name , 16 , text_color , SDF_FONT_BOLD ) ;
draw_sdf_string ( contents , x + 70 , y + 32 , f - > filetype , 16 , text_color , SDF_FONT_THIN ) ;
2018-11-19 15:14:39 +03:00
}
2018-11-18 12:36:03 +03:00
2018-12-26 03:53:18 +03:00
free ( name ) ;
} else if ( view_mode = = VIEW_MODE_LIST ) {
sprite_t * icon = icon_get_16 ( f - > icon ) ;
uint32_t text_color = rgb ( 0 , 0 , 0 ) ;
if ( f - > selected ) {
struct gradient_definition edge = { FILE_HEIGHT - 4 , y + 2 , HILIGHT_BORDER_TOP , HILIGHT_BORDER_BOTTOM } ;
struct gradient_definition body = { FILE_HEIGHT - 6 , y + 3 , HILIGHT_GRADIENT_TOP , HILIGHT_GRADIENT_BOTTOM } ;
draw_rounded_rectangle_pattern ( contents , x + 2 , y + 2 , FILE_WIDTH - 4 , FILE_HEIGHT - 4 , 3 , gfx_vertical_gradient_pattern , & edge ) ;
draw_rounded_rectangle_pattern ( contents , x + 3 , y + 3 , FILE_WIDTH - 6 , FILE_HEIGHT - 6 , 4 , gfx_vertical_gradient_pattern , & body ) ;
text_color = rgb ( 255 , 255 , 255 ) ;
} else if ( offset = = hilighted_offset ) {
draw_rounded_rectangle ( contents , x + 2 , y + 2 , FILE_WIDTH - 4 , FILE_HEIGHT - 4 , 3 , rgb ( 180 , 180 , 180 ) ) ;
draw_rounded_rectangle ( contents , x + 3 , y + 3 , FILE_WIDTH - 6 , FILE_HEIGHT - 6 , 4 , rgb ( 255 , 255 , 255 ) ) ;
}
2018-11-18 12:59:29 +03:00
2018-12-26 03:53:18 +03:00
if ( icon - > width ! = 16 | | icon - > height ! = 16 ) {
draw_sprite_scaled ( contents , icon , x + 4 , y + 4 , 16 , 16 ) ;
} else {
draw_sprite ( contents , icon , x + 4 , y + 4 ) ;
}
int len = strlen ( f - > name ) ;
char * name = malloc ( len + 4 ) ;
memcpy ( name , f - > name , len + 1 ) ;
int name_width ;
while ( ( name_width = draw_sdf_string_width ( name , 16 , SDF_FONT_THIN ) ) > FILE_WIDTH - 26 ) {
len - - ;
name [ len + 0 ] = ' . ' ;
name [ len + 1 ] = ' . ' ;
name [ len + 2 ] = ' . ' ;
name [ len + 3 ] = ' \0 ' ;
}
2018-11-19 13:26:40 +03:00
2018-12-26 03:53:18 +03:00
draw_sdf_string ( contents , x + 24 , y + 2 , name , 16 , text_color , SDF_FONT_THIN ) ;
}
2018-08-12 14:03:37 +03:00
}
2018-08-10 07:57:24 +03:00
2018-11-20 04:07:30 +03:00
/**
* Get file from array offset , with bounds check
*/
2018-08-12 14:03:37 +03:00
static struct File * get_file_at_offset ( int offset ) {
2018-11-20 04:07:30 +03:00
if ( offset > = 0 & & offset < file_pointers_len ) {
return file_pointers [ offset ] ;
2018-08-12 14:03:37 +03:00
}
return NULL ;
}
2018-11-20 04:07:30 +03:00
/**
* Redraw all icon view entries
*/
2018-08-12 14:03:37 +03:00
static void redraw_files ( void ) {
2018-11-22 12:23:25 +03:00
/* Fill to blank */
draw_fill ( contents , rgba ( 0 , 0 , 0 , 0 ) ) ;
2018-08-12 14:03:37 +03:00
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
draw_file ( file_pointers [ i ] , i ) ;
2018-08-10 07:57:24 +03:00
}
}
2018-11-20 04:07:30 +03:00
/**
* Set the application title .
*/
2018-11-18 04:25:24 +03:00
static void set_title ( char * directory ) {
2018-11-20 04:07:30 +03:00
/* Do nothing in desktop mode to avoid advertisement. */
2018-11-19 15:14:39 +03:00
if ( is_desktop_background ) return ;
2018-11-20 04:07:30 +03:00
/* If the directory name is set... */
2018-11-18 04:25:24 +03:00
if ( directory ) {
sprintf ( title , " %s - " APPLICATION_TITLE , directory ) ;
} else {
2018-11-20 04:07:30 +03:00
/* Otherwise, just "File Browser" */
2018-11-18 04:25:24 +03:00
sprintf ( title , APPLICATION_TITLE ) ;
}
2018-11-20 04:07:30 +03:00
/* Advertise to the panel */
2018-11-18 04:25:24 +03:00
yutani_window_advertise_icon ( yctx , main_window , title , " folder " ) ;
}
2018-11-20 04:07:30 +03:00
/**
* Check if a file name ends with an extension .
*
* Can also be used for exact matches .
*/
2018-11-19 13:26:40 +03:00
static int has_extension ( struct File * f , char * extension ) {
int i = strlen ( f - > name ) ;
int j = strlen ( extension ) ;
do {
if ( f - > name [ i ] ! = ( extension ) [ j ] ) break ;
if ( j = = 0 ) return 1 ;
if ( i = = 0 ) break ;
i - - ;
j - - ;
} while ( 1 ) ;
return 0 ;
}
2018-11-24 07:58:46 +03:00
static list_t * history_back ;
static list_t * history_forward ;
2019-01-03 09:46:37 +03:00
static void update_status ( void ) {
uint64_t total_size = 0 ;
uint64_t selected_size = 0 ;
int selected_count = 0 ;
struct File * selected = NULL ;
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
total_size + = file_pointers [ i ] - > size ;
if ( file_pointers [ i ] - > selected ) {
selected_count + = 1 ;
selected_size + = file_pointers [ i ] - > size ;
selected = file_pointers [ i ] ;
}
}
char tmp_size [ 50 ] ;
if ( selected_count = = 0 ) {
print_human_readable_size ( tmp_size , total_size ) ;
sprintf ( window_status , " %d item%s (%s) " , file_pointers_len , file_pointers_len = = 1 ? " " : " s " , tmp_size ) ;
} else if ( selected_count = = 1 ) {
print_human_readable_size ( tmp_size , selected - > size ) ;
sprintf ( window_status , " \" %s \" (%s) %s " , selected - > name , tmp_size , selected - > filetype ) ;
} else {
print_human_readable_size ( tmp_size , selected_size ) ;
sprintf ( window_status , " %d items selected (%s) " , selected_count , tmp_size ) ;
}
}
2018-11-20 04:07:30 +03:00
/**
* Read the contents of a directory into the icon view .
*/
2018-11-24 07:58:46 +03:00
static void load_directory ( const char * path , int modifies_history ) {
2018-11-20 04:07:30 +03:00
/* Free the current icon view entries */
2018-08-12 14:03:37 +03:00
if ( file_pointers ) {
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
free ( file_pointers [ i ] ) ;
}
free ( file_pointers ) ;
2018-08-10 07:57:24 +03:00
}
DIR * dirp = opendir ( path ) ;
if ( ! dirp ) {
2018-11-20 04:07:30 +03:00
/**
* TODO : This should probably show a dialog and then reload the current directory ,
* or maybe we should be checking this before clearing the current file pointers .
*/
2018-08-12 14:03:37 +03:00
file_pointers = NULL ;
file_pointers_len = 0 ;
2018-08-10 07:57:24 +03:00
return ;
}
2018-11-24 07:58:46 +03:00
if ( modifies_history ) {
/* Clear forward history */
list_destroy ( history_forward ) ;
list_free ( history_forward ) ;
free ( history_forward ) ;
history_forward = list_create ( ) ;
/* Append current pointer */
if ( current_directory ) {
list_insert ( history_back , strdup ( current_directory ) ) ;
}
}
2018-11-20 04:07:30 +03:00
if ( current_directory ) {
free ( current_directory ) ;
2018-08-12 14:03:37 +03:00
}
2018-11-24 08:11:02 +03:00
_button_disabled [ 0 ] = ! ( history_back - > length ) ;
_button_disabled [ 1 ] = ! ( history_forward - > length ) ;
_button_disabled [ 2 ] = 0 ;
_button_disabled [ 3 ] = 0 ;
2018-11-19 15:14:39 +03:00
char * home = getenv ( " HOME " ) ;
if ( home & & ! strcmp ( path , home ) ) {
2018-11-20 04:07:30 +03:00
/* If the current directory is the user's homedir, present it that way in the title */
2018-11-19 15:14:39 +03:00
set_title ( " Home " ) ;
2018-11-24 08:11:02 +03:00
_button_disabled [ 3 ] = 1 ;
} else if ( ! strcmp ( path , " / " ) ) {
set_title ( " File System " ) ;
_button_disabled [ 2 ] = 1 ;
2018-11-19 15:14:39 +03:00
} else {
2018-11-20 04:07:30 +03:00
/* Otherwise use just the directory base name */
2018-11-19 15:14:39 +03:00
char * tmp = strdup ( path ) ;
char * base = basename ( tmp ) ;
set_title ( base ) ;
free ( tmp ) ;
}
2018-11-18 04:25:24 +03:00
2018-11-20 04:07:30 +03:00
/* If we ended up in a path with //two/initial/slashes, fix that. */
2018-11-18 13:27:35 +03:00
if ( path [ 0 ] = = ' / ' & & path [ 1 ] = = ' / ' ) {
2018-11-20 04:07:30 +03:00
current_directory = strdup ( path + 1 ) ;
2018-11-18 13:27:35 +03:00
} else {
2018-11-20 04:07:30 +03:00
current_directory = strdup ( path ) ;
2018-11-18 13:27:35 +03:00
}
2018-08-12 14:03:37 +03:00
2018-12-23 11:16:20 +03:00
strcpy ( nav_bar , current_directory ) ;
2018-11-20 04:07:30 +03:00
/* TODO: Show relative time informaton... */
2018-08-10 07:57:24 +03:00
#if 0
2018-11-20 04:07:30 +03:00
/* Get the current time */
2018-08-10 07:57:24 +03:00
struct tm * timeinfo ;
struct timeval now ;
gettimeofday ( & now , NULL ) ; //time(NULL);
timeinfo = localtime ( ( time_t * ) & now . tv_sec ) ;
int this_year = timeinfo - > tm_year ;
# endif
2018-08-12 14:03:37 +03:00
list_t * file_list = list_create ( ) ;
2018-08-10 07:57:24 +03:00
struct dirent * ent = readdir ( dirp ) ;
while ( ent ! = NULL ) {
2018-08-12 14:14:07 +03:00
if ( ent - > d_name [ 0 ] = = ' . ' & &
( ent - > d_name [ 1 ] = = ' \0 ' | |
( ent - > d_name [ 1 ] = = ' . ' & &
ent - > d_name [ 2 ] = = ' \0 ' ) ) ) {
/* skip . and .. */
ent = readdir ( dirp ) ;
continue ;
}
2018-08-10 07:57:24 +03:00
if ( show_hidden | | ( ent - > d_name [ 0 ] ! = ' . ' ) ) {
2018-08-12 14:14:07 +03:00
2018-11-20 04:07:30 +03:00
/* Set display name from file name */
2018-08-10 07:57:24 +03:00
struct File * f = malloc ( sizeof ( struct File ) ) ;
sprintf ( f - > name , " %s " , ent - > d_name ) ; /* snprintf? copy min()? */
struct stat statbuf ;
2018-11-19 13:26:40 +03:00
struct stat statbufl ;
2018-08-10 07:57:24 +03:00
2018-11-20 04:07:30 +03:00
/* Calculate absolute path to file */
2018-08-10 07:57:24 +03:00
char tmp [ strlen ( path ) + strlen ( ent - > d_name ) + 2 ] ;
sprintf ( tmp , " %s/%s " , path , ent - > d_name ) ;
lstat ( tmp , & statbuf ) ;
2018-11-19 13:26:40 +03:00
2018-12-26 03:53:18 +03:00
f - > size = statbuf . st_size ;
2018-11-20 04:07:30 +03:00
/* Read link target for symlinks */
2018-08-10 07:57:24 +03:00
if ( S_ISLNK ( statbuf . st_mode ) ) {
2018-11-19 13:26:40 +03:00
memcpy ( & statbufl , & statbuf , sizeof ( struct stat ) ) ;
stat ( tmp , & statbuf ) ;
readlink ( tmp , f - > link , 256 ) ;
} else {
f - > link [ 0 ] = ' \0 ' ;
2018-08-10 07:57:24 +03:00
}
2018-11-19 13:26:40 +03:00
2018-11-19 15:14:39 +03:00
f - > launcher [ 0 ] = ' \0 ' ;
2018-12-26 03:53:18 +03:00
f - > filetype [ 0 ] = ' \0 ' ;
2018-11-20 04:07:30 +03:00
f - > selected = 0 ;
2018-08-10 07:57:24 +03:00
if ( S_ISDIR ( statbuf . st_mode ) ) {
2018-11-20 04:07:30 +03:00
/* Directory */
2018-08-10 07:57:24 +03:00
sprintf ( f - > icon , " folder " ) ;
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " Directory " ) ;
2018-08-12 14:03:37 +03:00
f - > type = 1 ;
2018-08-10 07:57:24 +03:00
} else {
2018-11-20 04:07:30 +03:00
/* Regular file */
/* Default regular files to open in bim */
2018-11-19 15:14:39 +03:00
sprintf ( f - > launcher , " exec terminal bim " ) ;
2018-11-20 04:07:30 +03:00
2018-11-19 15:14:39 +03:00
if ( is_desktop_background & & has_extension ( f , " .launcher " ) ) {
2018-11-20 04:07:30 +03:00
/* In desktop mode, read launchers specially */
2018-11-19 15:14:39 +03:00
FILE * file = fopen ( tmp , " r " ) ;
char tbuf [ 1024 ] ;
while ( ! feof ( file ) ) {
fgets ( tbuf , 1024 , file ) ;
char * nl = strchr ( tbuf , ' \n ' ) ;
if ( nl ) * nl = ' \0 ' ;
char * eq = strchr ( tbuf , ' = ' ) ;
if ( ! eq ) continue ;
* eq = ' \0 ' ; eq + + ;
if ( ! strcmp ( tbuf , " icon " ) ) {
sprintf ( f - > icon , " %s " , eq ) ;
} else if ( ! strcmp ( tbuf , " run " ) ) {
sprintf ( f - > launcher , " %s # " , eq ) ;
} else if ( ! strcmp ( tbuf , " title " ) ) {
sprintf ( f - > name , eq ) ;
}
}
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " Launcher " ) ;
2018-11-21 09:28:43 +03:00
sprintf ( f - > filename , " %s " , ent - > d_name ) ;
2018-11-19 15:14:39 +03:00
f - > type = 2 ;
2018-11-19 13:26:40 +03:00
} else {
2018-11-20 04:07:30 +03:00
/* Handle various file types */
2018-11-19 15:14:39 +03:00
if ( has_extension ( f , " .c " ) ) {
sprintf ( f - > icon , " c " ) ;
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " C Source " ) ;
2018-11-19 15:14:39 +03:00
} else if ( has_extension ( f , " .h " ) ) {
sprintf ( f - > icon , " h " ) ;
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " C Header " ) ;
} else if ( has_extension ( f , " .bmp " ) ) {
sprintf ( f - > icon , " image " ) ;
sprintf ( f - > launcher , " exec imgviewer " ) ;
sprintf ( f - > filetype , " Bitmap Image " ) ;
} else if ( has_extension ( f , " .jpg " ) | | has_extension ( f , " .jpeg " ) ) {
2018-11-19 15:14:39 +03:00
sprintf ( f - > icon , " image " ) ;
sprintf ( f - > launcher , " exec imgviewer " ) ;
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " JPEG Image " ) ;
} else if ( has_extension ( f , " .sdf " ) ) {
2018-11-21 15:32:24 +03:00
sprintf ( f - > icon , " font " ) ;
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " SDF Font " ) ;
2018-11-21 15:32:24 +03:00
/* TODO: Font viewer for SDF and TrueType */
2018-12-26 03:53:18 +03:00
} else if ( has_extension ( f , " .ttf " ) ) {
sprintf ( f - > icon , " font " ) ;
sprintf ( f - > filetype , " TrueType Font " ) ;
} else if ( has_extension ( f , " .tgz " ) | | has_extension ( f , " .tar.gz " ) ) {
sprintf ( f - > icon , " package " ) ;
sprintf ( f - > filetype , " Compressed Archive File " ) ;
} else if ( has_extension ( f , " .tar " ) ) {
2018-11-21 15:22:27 +03:00
sprintf ( f - > icon , " package " ) ;
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " Archive File " ) ;
2018-11-21 15:22:27 +03:00
} else if ( has_extension ( f , " .sh " ) ) {
sprintf ( f - > icon , " sh " ) ;
if ( statbuf . st_mode & 0111 ) {
/* Make executable */
sprintf ( f - > launcher , " SELF " ) ;
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " Executable Shell Script " ) ;
} else {
sprintf ( f - > filetype , " Shell Script " ) ;
2018-11-21 15:22:27 +03:00
}
2018-12-26 03:53:18 +03:00
} else if ( has_extension ( f , " .ko " ) ) {
sprintf ( f - > icon , " file " ) ;
sprintf ( f - > filetype , " Kernel Module " ) ;
} else if ( has_extension ( f , " .o " ) ) {
sprintf ( f - > icon , " file " ) ;
sprintf ( f - > filetype , " Object File " ) ;
} else if ( has_extension ( f , " .so " ) ) {
sprintf ( f - > icon , " file " ) ;
sprintf ( f - > filetype , " Shared Object File " ) ;
} else if ( has_extension ( f , " .S " ) ) {
sprintf ( f - > icon , " file " ) ;
sprintf ( f - > filetype , " Assembly Source " ) ;
} else if ( has_extension ( f , " .ld " ) ) {
sprintf ( f - > icon , " file " ) ;
sprintf ( f - > filetype , " Linker Script " ) ;
2018-12-26 13:07:15 +03:00
} else if ( statbuf . st_mode & 0111 ) {
/* Executable files - use their name for their icon, and launch themselves. */
sprintf ( f - > icon , " %s " , f - > name ) ;
sprintf ( f - > launcher , " SELF " ) ;
sprintf ( f - > filetype , " Executable " ) ;
2018-11-19 15:14:39 +03:00
} else {
sprintf ( f - > icon , " file " ) ;
2018-12-26 03:53:18 +03:00
sprintf ( f - > filetype , " File " ) ;
2018-11-19 15:14:39 +03:00
}
f - > type = 0 ;
2018-11-19 13:26:40 +03:00
}
2018-08-10 07:57:24 +03:00
}
list_insert ( file_list , f ) ;
}
ent = readdir ( dirp ) ;
}
closedir ( dirp ) ;
2018-08-12 07:59:00 +03:00
2018-11-20 04:07:30 +03:00
/* Store the entries in a flat array. */
2018-08-12 14:03:37 +03:00
file_pointers = malloc ( sizeof ( struct File * ) * file_list - > length ) ;
file_pointers_len = file_list - > length ;
int i = 0 ;
foreach ( node , file_list ) {
file_pointers [ i ] = node - > value ;
i + + ;
}
2019-01-03 09:46:37 +03:00
update_status ( ) ;
2018-11-20 04:07:30 +03:00
/* Free our temporary linked list */
2018-08-12 14:03:37 +03:00
list_free ( file_list ) ;
free ( file_list ) ;
/* Sort files */
int comparator ( const void * c1 , const void * c2 ) {
const struct File * f1 = * ( const struct File * * ) ( c1 ) ;
const struct File * f2 = * ( const struct File * * ) ( c2 ) ;
2018-11-20 04:07:30 +03:00
/* Launchers before directories before files */
2018-11-19 15:14:39 +03:00
if ( f1 - > type > f2 - > type ) return - 1 ;
if ( f2 - > type > f1 - > type ) return 1 ;
2018-11-20 04:07:30 +03:00
/* Launchers sorted by filename, not by display name */
2018-11-19 15:14:39 +03:00
if ( f1 - > type = = 2 & & f2 - > type = = 2 ) {
return strcmp ( f1 - > filename , f2 - > filename ) ;
}
2018-11-20 04:07:30 +03:00
/* Files sorted by name */
2018-08-12 14:03:37 +03:00
return strcmp ( f1 - > name , f2 - > name ) ;
}
qsort ( file_pointers , file_pointers_len , sizeof ( struct File * ) , comparator ) ;
2018-11-20 04:07:30 +03:00
/* Reset scroll offset when navigating */
2018-08-12 07:59:00 +03:00
scroll_offset = 0 ;
2018-08-10 07:57:24 +03:00
}
2018-11-20 04:07:30 +03:00
/**
* Resize and redraw the icon view */
2018-08-10 07:57:24 +03:00
static void reinitialize_contents ( void ) {
2018-11-20 04:07:30 +03:00
/* If there already is a context, free it. */
2018-08-10 07:57:24 +03:00
if ( contents ) {
free ( contents ) ;
}
2018-11-20 04:07:30 +03:00
/* If there already is a context buffer, free it. */
2018-08-10 07:57:24 +03:00
if ( contents_sprite ) {
sprite_free ( contents_sprite ) ;
}
2018-11-20 04:07:30 +03:00
/* Get window bounds to determine how wide we can make our icon view */
2018-09-12 06:53:08 +03:00
struct decor_bounds bounds ;
2018-11-19 15:14:39 +03:00
_decor_get_bounds ( main_window , & bounds ) ;
2018-09-12 06:53:08 +03:00
2018-11-19 15:14:39 +03:00
if ( is_desktop_background ) {
2018-11-20 04:07:30 +03:00
/**
* TODO : Actually calculate an optimal FILE_PTR_WIDTH or fix this to
* work properly with vertical rows of files
*/
2018-11-19 15:14:39 +03:00
FILE_PTR_WIDTH = 1 ;
2018-12-26 03:53:18 +03:00
} else if ( view_mode = = VIEW_MODE_LIST ) {
FILE_PTR_WIDTH = 1 ;
FILE_WIDTH = ( ctx - > width - bounds . width ) ;
2018-11-19 15:14:39 +03:00
} else {
FILE_PTR_WIDTH = ( ctx - > width - bounds . width ) / FILE_WIDTH ;
}
2018-11-20 04:07:30 +03:00
/* Calculate required height to fit files */
2018-11-18 12:59:29 +03:00
int calculated_height = ( file_pointers_len / FILE_PTR_WIDTH + 1 ) * FILE_HEIGHT ;
2018-11-18 12:36:03 +03:00
2018-11-20 04:07:30 +03:00
/* Create buffer */
contents_sprite = create_sprite ( FILE_PTR_WIDTH * FILE_WIDTH , calculated_height , ALPHA_EMBEDDED ) ;
2018-08-10 07:57:24 +03:00
contents = init_graphics_sprite ( contents_sprite ) ;
/* Draw file entries */
redraw_files ( ) ;
}
2018-12-23 11:16:20 +03:00
# define BUTTON_SPACE 34
# define BUTTON_COUNT 4
static void _draw_buttons ( struct decor_bounds bounds ) {
uint32_t gradient_top = rgb ( 59 , 59 , 59 ) ;
uint32_t gradient_bot = rgb ( 40 , 40 , 40 ) ;
2018-12-26 06:56:28 +03:00
for ( int i = 0 ; i < 36 ; + + i ) {
2018-12-23 11:16:20 +03:00
uint32_t c = interp_colors ( gradient_top , gradient_bot , i * 255 / 36 ) ;
draw_rectangle ( ctx , bounds . left_width , bounds . top_height + MENU_BAR_HEIGHT + i ,
BUTTON_SPACE * BUTTON_COUNT , 1 , c ) ;
//ctx->width - bounds.width, 1, c);
}
int x = 0 ;
int i = 0 ;
# define draw_button(label) do { \
struct TTKButton _up = { bounds . left_width + 2 + x , bounds . top_height + MENU_BAR_HEIGHT + 2 , 32 , 32 , " \033 " label , _button_hilights [ i ] | ( _button_disabled [ i ] < < 8 ) } ; \
ttk_button_draw ( ctx , & _up ) ; \
x + = BUTTON_SPACE ; i + + ; } while ( 0 )
draw_button ( " back " ) ;
draw_button ( " forward " ) ;
draw_button ( " up " ) ;
draw_button ( " home " ) ;
}
2018-12-27 07:01:12 +03:00
static void _figure_out_navbar_cursor ( int x , struct decor_bounds bounds ) {
x = x - bounds . left_width - 2 - BUTTON_SPACE * BUTTON_COUNT - 5 ;
if ( x < = 0 ) {
nav_bar_cursor_x = 0 ;
return ;
}
char * tmp = strdup ( nav_bar ) ;
int candidate = 0 ;
while ( * tmp & & x + 2 < ( candidate = draw_sdf_string_width ( tmp , 16 , SDF_FONT_THIN ) ) ) {
tmp [ strlen ( tmp ) - 1 ] = ' \0 ' ;
}
nav_bar_cursor_x = candidate + 2 ;
nav_bar_cursor = strlen ( tmp ) ;
free ( tmp ) ;
}
static void _recalculate_nav_bar_cursor ( void ) {
if ( nav_bar_cursor < 0 ) {
nav_bar_cursor = 0 ;
}
if ( nav_bar_cursor > ( int ) strlen ( nav_bar ) ) {
nav_bar_cursor = strlen ( nav_bar ) ;
}
char * tmp = strdup ( nav_bar ) ;
tmp [ nav_bar_cursor ] = ' \0 ' ;
nav_bar_cursor_x = draw_sdf_string_width ( tmp , 16 , SDF_FONT_THIN ) + 2 ;
free ( tmp ) ;
}
2018-12-23 11:16:20 +03:00
static void _draw_nav_bar ( struct decor_bounds bounds ) {
uint32_t gradient_top = rgb ( 59 , 59 , 59 ) ;
uint32_t gradient_bot = rgb ( 40 , 40 , 40 ) ;
int x = BUTTON_SPACE * BUTTON_COUNT ;
2018-12-26 06:56:28 +03:00
for ( int i = 0 ; i < 36 ; + + i ) {
2018-12-23 11:16:20 +03:00
uint32_t c = interp_colors ( gradient_top , gradient_bot , i * 255 / 36 ) ;
draw_rectangle ( ctx , bounds . left_width + BUTTON_SPACE * BUTTON_COUNT , bounds . top_height + MENU_BAR_HEIGHT + i ,
ctx - > width - bounds . width - BUTTON_SPACE * BUTTON_COUNT , 1 , c ) ;
}
if ( nav_bar_focused ) {
struct gradient_definition edge = { 28 , bounds . top_height + MENU_BAR_HEIGHT + 3 , rgb ( 0 , 120 , 220 ) , rgb ( 0 , 120 , 220 ) } ;
draw_rounded_rectangle_pattern ( ctx , bounds . left_width + 2 + x + 1 , bounds . top_height + MENU_BAR_HEIGHT + 4 , main_window - > width - bounds . width - x - 6 , 26 , 4 , gfx_vertical_gradient_pattern , & edge ) ;
draw_rounded_rectangle ( ctx , bounds . left_width + 2 + x + 3 , bounds . top_height + MENU_BAR_HEIGHT + 6 , main_window - > width - bounds . width - x - 10 , 22 , 3 , rgb ( 250 , 250 , 250 ) ) ;
} else {
struct gradient_definition edge = { 28 , bounds . top_height + MENU_BAR_HEIGHT + 3 , rgb ( 90 , 90 , 90 ) , rgb ( 110 , 110 , 110 ) } ;
draw_rounded_rectangle_pattern ( ctx , bounds . left_width + 2 + x + 1 , bounds . top_height + MENU_BAR_HEIGHT + 4 , main_window - > width - bounds . width - x - 6 , 26 , 4 , gfx_vertical_gradient_pattern , & edge ) ;
draw_rounded_rectangle ( ctx , bounds . left_width + 2 + x + 2 , bounds . top_height + MENU_BAR_HEIGHT + 5 , main_window - > width - bounds . width - x - 8 , 24 , 3 , rgb ( 250 , 250 , 250 ) ) ;
}
2018-12-27 07:01:12 +03:00
/* Draw the nav bar text */
2018-12-23 11:16:20 +03:00
int max_width = main_window - > width - bounds . width - x - 12 ;
int len = strlen ( nav_bar ) ;
2018-12-23 11:28:01 +03:00
char * name = malloc ( len + 5 ) ;
2018-12-23 11:16:20 +03:00
memcpy ( name , nav_bar , len + 1 ) ;
int name_width ;
while ( ( name_width = draw_sdf_string_width ( name , 16 , SDF_FONT_THIN ) ) > max_width ) {
len - - ;
name [ len + 0 ] = ' . ' ;
name [ len + 1 ] = ' . ' ;
name [ len + 2 ] = ' . ' ;
name [ len + 3 ] = ' \0 ' ;
}
draw_sdf_string ( ctx , bounds . left_width + 2 + x + 5 , bounds . top_height + MENU_BAR_HEIGHT + 8 , name , 16 , rgb ( 0 , 0 , 0 ) , SDF_FONT_THIN ) ;
2018-12-27 07:01:12 +03:00
if ( nav_bar_focused ) {
/* Draw cursor indicator at cursor_x */
draw_line ( ctx ,
bounds . left_width + 2 + x + 5 + nav_bar_cursor_x ,
bounds . left_width + 2 + x + 5 + nav_bar_cursor_x ,
bounds . top_height + MENU_BAR_HEIGHT + 8 ,
bounds . top_height + MENU_BAR_HEIGHT + 8 + 15 ,
rgb ( 0 , 0 , 0 ) ) ;
}
2018-12-23 11:16:20 +03:00
}
2019-01-03 09:46:37 +03:00
# define STATUS_HEIGHT 24
static void _draw_status ( struct decor_bounds bounds ) {
2019-01-03 12:00:56 +03:00
uint32_t gradient_top = rgb ( 80 , 80 , 80 ) ;
uint32_t gradient_bot = rgb ( 59 , 59 , 59 ) ;
2019-01-03 09:46:37 +03:00
draw_rectangle ( ctx , bounds . left_width , ctx - > height - bounds . bottom_height - STATUS_HEIGHT ,
2019-01-03 12:00:56 +03:00
ctx - > width - bounds . width , 1 , rgb ( 110 , 110 , 110 ) ) ;
for ( int i = 1 ; i < STATUS_HEIGHT ; + + i ) {
uint32_t c = interp_colors ( gradient_top , gradient_bot , i * 255 / STATUS_HEIGHT ) ;
draw_rectangle ( ctx , bounds . left_width , ctx - > height - bounds . bottom_height - STATUS_HEIGHT + i ,
ctx - > width - bounds . width , 1 , c ) ;
}
2019-01-03 13:36:29 +03:00
{
sprite_t * _tmp_s = create_sprite ( ctx - > width - bounds . width - 4 , STATUS_HEIGHT - 3 , ALPHA_EMBEDDED ) ;
gfx_context_t * _tmp = init_graphics_sprite ( _tmp_s ) ;
draw_fill ( _tmp , rgba ( 0 , 0 , 0 , 0 ) ) ;
draw_sdf_string ( _tmp , 1 , 1 , window_status , 16 , rgb ( 0 , 0 , 0 ) , SDF_FONT_THIN ) ;
blur_context_box ( _tmp , 4 ) ;
draw_sdf_string ( _tmp , 0 , 0 , window_status , 16 , rgb ( 255 , 255 , 255 ) , SDF_FONT_THIN ) ;
free ( _tmp ) ;
draw_sprite ( ctx , _tmp_s , bounds . left_width + 4 , ctx - > height - bounds . bottom_height - STATUS_HEIGHT + 3 ) ;
sprite_free ( _tmp_s ) ;
}
2019-01-03 09:46:37 +03:00
}
2018-12-23 11:16:20 +03:00
static void _redraw_nav_bar ( void ) {
struct decor_bounds bounds ;
_decor_get_bounds ( main_window , & bounds ) ;
_draw_nav_bar ( bounds ) ;
flip ( ctx ) ;
yutani_flip ( yctx , main_window ) ;
}
2018-12-27 07:01:12 +03:00
static void nav_bar_backspace_word ( void ) {
2018-12-27 07:10:11 +03:00
if ( ! * nav_bar ) return ;
2018-12-27 07:01:12 +03:00
if ( nav_bar_cursor = = 0 ) return ;
char * after = strdup ( & nav_bar [ nav_bar_cursor ] ) ;
if ( nav_bar [ nav_bar_cursor - 1 ] = = ' / ' ) {
nav_bar [ nav_bar_cursor - 1 ] = ' \0 ' ;
nav_bar_cursor - - ;
}
while ( nav_bar_cursor & & nav_bar [ nav_bar_cursor - 1 ] ! = ' / ' ) {
nav_bar [ nav_bar_cursor - 1 ] = ' \0 ' ;
nav_bar_cursor - - ;
}
strcat ( nav_bar , after ) ;
free ( after ) ;
_recalculate_nav_bar_cursor ( ) ;
_redraw_nav_bar ( ) ;
}
static void nav_bar_backspace ( void ) {
if ( nav_bar_cursor = = 0 ) return ;
char * after = strdup ( & nav_bar [ nav_bar_cursor ] ) ;
nav_bar [ nav_bar_cursor - 1 ] = ' \0 ' ;
nav_bar_cursor - - ;
strcat ( nav_bar , after ) ;
free ( after ) ;
_recalculate_nav_bar_cursor ( ) ;
_redraw_nav_bar ( ) ;
}
static void nav_bar_insert_char ( char c ) {
char * tmp = strdup ( nav_bar ) ;
tmp [ nav_bar_cursor ] = ' \0 ' ;
char * after = strdup ( & nav_bar [ nav_bar_cursor ] ) ;
sprintf ( nav_bar , " %s%c%s " , tmp , c , after ) ;
free ( tmp ) ;
free ( after ) ;
nav_bar_cursor + = 1 ;
_recalculate_nav_bar_cursor ( ) ;
_redraw_nav_bar ( ) ;
}
static void nav_bar_cursor_left ( void ) {
nav_bar_cursor - - ;
_recalculate_nav_bar_cursor ( ) ;
_redraw_nav_bar ( ) ;
}
static void nav_bar_cursor_right ( void ) {
nav_bar_cursor + + ;
_recalculate_nav_bar_cursor ( ) ;
_redraw_nav_bar ( ) ;
}
2018-11-20 04:07:30 +03:00
/**
* Redraw the entire window .
*/
2018-11-19 15:14:39 +03:00
static void redraw_window ( void ) {
if ( ! is_desktop_background ) {
2018-11-20 04:07:30 +03:00
/* Clear to white and draw decorations */
2018-11-19 15:14:39 +03:00
draw_fill ( ctx , rgb ( 255 , 255 , 255 ) ) ;
render_decorations ( main_window , ctx , title ) ;
} else {
2018-11-20 04:07:30 +03:00
/* Draw wallpaper in desktop mode */
2018-12-05 10:32:43 +03:00
if ( wallpaper_old ) {
draw_sprite ( ctx , wallpaper_old , 0 , 0 ) ;
uint64_t ellapsed = precise_time_since ( timer ) ;
if ( ellapsed > 1000 ) {
free ( wallpaper_old ) ;
wallpaper_old = NULL ;
draw_sprite ( ctx , wallpaper_buffer , 0 , 0 ) ;
2018-12-05 10:50:23 +03:00
restart = 1 ; /* quietly restart */
2018-12-05 10:32:43 +03:00
} else {
draw_sprite_alpha ( ctx , wallpaper_buffer , 0 , 0 , ( float ) ellapsed / 1000.0 ) ;
}
} else {
draw_sprite ( ctx , wallpaper_buffer , 0 , 0 ) ;
}
2018-11-19 15:14:39 +03:00
}
2018-05-21 02:12:02 +03:00
2018-09-12 06:53:08 +03:00
struct decor_bounds bounds ;
2018-11-19 15:14:39 +03:00
_decor_get_bounds ( main_window , & bounds ) ;
if ( ! is_desktop_background ) {
2018-11-20 04:07:30 +03:00
/* Position, size, and draw the menu bar */
2018-11-19 15:14:39 +03:00
menu_bar . x = bounds . left_width ;
menu_bar . y = bounds . top_height ;
menu_bar . width = ctx - > width - bounds . width ;
menu_bar . window = main_window ;
menu_bar_render ( & menu_bar , ctx ) ;
2018-11-24 07:58:46 +03:00
/* Draw toolbar */
2018-12-23 11:16:20 +03:00
_draw_buttons ( bounds ) ;
_draw_nav_bar ( bounds ) ;
2018-11-24 07:58:46 +03:00
2019-01-03 09:46:37 +03:00
_draw_status ( bounds ) ;
2018-11-19 15:14:39 +03:00
}
2018-05-21 02:12:02 +03:00
2018-11-20 04:07:30 +03:00
/* Draw the icon view, clipped to the viewport and scrolled appropriately. */
2018-08-10 07:57:24 +03:00
gfx_clear_clip ( ctx ) ;
2018-11-19 15:14:39 +03:00
gfx_add_clip ( ctx , bounds . left_width , bounds . top_height + menu_bar_height , ctx - > width - bounds . width , available_height ) ;
draw_sprite ( ctx , contents_sprite , bounds . left_width , bounds . top_height + menu_bar_height - scroll_offset ) ;
2018-08-10 07:57:24 +03:00
gfx_clear_clip ( ctx ) ;
gfx_add_clip ( ctx , 0 , 0 , ctx - > width , ctx - > height ) ;
2018-11-20 04:07:30 +03:00
/* Flip graphics context and inform compositor */
2018-05-21 02:12:02 +03:00
flip ( ctx ) ;
yutani_flip ( yctx , main_window ) ;
}
2018-11-20 04:07:30 +03:00
/**
* Loads and bakes the wallpaper to the appropriate size .
*/
static void draw_background ( int width , int height ) {
/* If the wallpaper is already loaded, free it. */
if ( wallpaper_buffer ) {
2018-12-05 10:32:43 +03:00
if ( wallpaper_old ) {
free ( wallpaper_old ) ;
}
wallpaper_old = wallpaper_buffer ;
timer = precise_current_time ( ) ;
2018-11-20 04:07:30 +03:00
}
/* Open the wallpaper */
sprite_t * wallpaper = malloc ( sizeof ( sprite_t ) ) ;
2018-12-05 07:26:09 +03:00
char * wallpaper_path = WALLPAPER_PATH ;
int free_it = 0 ;
char * home = getenv ( " HOME " ) ;
if ( home ) {
char tmp [ 512 ] ;
sprintf ( tmp , " %s/.wallpaper.conf " , home ) ;
FILE * c = fopen ( tmp , " r " ) ;
if ( c ) {
char line [ 1024 ] ;
while ( ! feof ( c ) ) {
fgets ( line , 1024 , c ) ;
char * nl = strchr ( line , ' \n ' ) ;
if ( nl ) * nl = ' \0 ' ;
if ( line [ 0 ] = = ' ; ' ) {
continue ;
}
if ( strstr ( line , " wallpaper= " ) = = line ) {
free_it = 1 ;
wallpaper_path = strdup ( line + strlen ( " wallpaper= " ) ) ;
break ;
}
}
fclose ( c ) ;
}
}
load_sprite_jpg ( wallpaper , wallpaper_path ) ;
if ( free_it ) {
free ( wallpaper_path ) ;
}
2018-11-20 04:07:30 +03:00
/* Create a new buffer to hold the baked wallpaper */
wallpaper_buffer = create_sprite ( width , height , 0 ) ;
gfx_context_t * ctx = init_graphics_sprite ( wallpaper_buffer ) ;
/* Calculate the appropriate scaled size to fit the screen. */
float x = ( float ) width / ( float ) wallpaper - > width ;
float y = ( float ) height / ( float ) wallpaper - > height ;
int nh = ( int ) ( x * ( float ) wallpaper - > height ) ;
int nw = ( int ) ( y * ( float ) wallpaper - > width ) ;
/* Clear to black to avoid odd transparency issues along edges */
draw_fill ( ctx , rgb ( 0 , 0 , 0 ) ) ;
/* Scale the wallpaper into the buffer. */
if ( nw = = wallpaper - > width & & nh = = wallpaper - > height ) {
/* No scaling necessary */
draw_sprite ( ctx , wallpaper , 0 , 0 ) ;
} else if ( nw > = width ) {
/* Scaled wallpaper is wider, height should match. */
draw_sprite_scaled ( ctx , wallpaper , ( ( int ) width - nw ) / 2 , 0 , nw + 2 , height ) ;
} else {
/* Scaled wallpaper is taller, width should match. */
draw_sprite_scaled ( ctx , wallpaper , 0 , ( ( int ) height - nh ) / 2 , width + 2 , nh ) ;
}
/* Free the original wallpaper. */
sprite_free ( wallpaper ) ;
free ( ctx ) ;
}
/**
* Resize window when asked by the compositor .
*/
2018-05-21 02:12:02 +03:00
static void resize_finish ( int w , int h ) {
2018-11-24 16:38:39 +03:00
if ( w < 300 | | h < 300 ) {
yutani_window_resize_offer ( yctx , main_window , w < 300 ? 300 : w , h < 300 ? 300 : h ) ;
return ;
}
2018-08-12 07:59:00 +03:00
int width_changed = ( main_window - > width ! = ( unsigned int ) w ) ;
2018-08-10 07:57:24 +03:00
2018-05-21 02:12:02 +03:00
yutani_window_resize_accept ( yctx , main_window , w , h ) ;
reinit_graphics_yutani ( ctx , main_window ) ;
2018-09-12 06:53:08 +03:00
struct decor_bounds bounds ;
2018-11-19 15:14:39 +03:00
_decor_get_bounds ( main_window , & bounds ) ;
2018-09-12 06:53:08 +03:00
2018-11-20 04:07:30 +03:00
/* Recalculate available size */
2019-01-03 09:46:37 +03:00
available_height = ctx - > height - menu_bar_height - bounds . height - ( is_desktop_background ? 0 : STATUS_HEIGHT ) ;
2019-01-03 12:00:56 +03:00
fprintf ( stderr , " available_height = %d; bounds.bottom_height = %d, (isd...) = %d \n " , available_height , bounds . bottom_height , ( is_desktop_background ? 0 : STATUS_HEIGHT ) ) ;
2018-08-12 07:59:00 +03:00
2018-11-20 04:07:30 +03:00
/* If the width changed, we need to rebuild the icon view */
2018-08-12 07:59:00 +03:00
if ( width_changed ) {
2018-08-10 07:57:24 +03:00
reinitialize_contents ( ) ;
}
2018-11-20 04:07:30 +03:00
/* Make sure we're not scrolled weirdly after resizing */
2018-08-12 07:59:00 +03:00
if ( available_height > contents - > height ) {
scroll_offset = 0 ;
} else {
if ( scroll_offset > contents - > height - available_height ) {
scroll_offset = contents - > height - available_height ;
}
}
2018-11-20 04:07:30 +03:00
/* If the desktop background changes size, we have to reload and rescale the wallpaper */
2018-11-19 15:14:39 +03:00
if ( is_desktop_background ) {
draw_background ( w , h ) ;
}
2018-11-20 04:07:30 +03:00
/* Redraw */
2018-05-21 02:12:02 +03:00
redraw_window ( ) ;
yutani_window_resize_done ( yctx , main_window ) ;
yutani_flip ( yctx , main_window ) ;
}
2018-11-20 04:07:30 +03:00
/* TODO: We don't have an input box yet. */
2018-08-14 11:13:38 +03:00
#if 0
2018-05-21 02:12:02 +03:00
static void _menu_action_input_path ( struct MenuEntry * entry ) {
}
2018-08-14 11:13:38 +03:00
# endif
2018-05-21 02:12:02 +03:00
2018-11-20 04:07:30 +03:00
/* File > Exit */
static void _menu_action_exit ( struct MenuEntry * entry ) {
application_running = 0 ;
}
/* Go > ... generic handler */
2018-05-21 02:12:02 +03:00
static void _menu_action_navigate ( struct MenuEntry * entry ) {
/* go to entry->action */
2018-08-12 07:59:00 +03:00
struct MenuEntry_Normal * _entry = ( void * ) entry ;
2018-11-24 07:58:46 +03:00
load_directory ( _entry - > action , 1 ) ;
2018-08-12 07:59:00 +03:00
reinitialize_contents ( ) ;
redraw_window ( ) ;
2018-05-21 02:12:02 +03:00
}
2018-11-20 04:07:30 +03:00
/* Go > Up */
2018-05-21 02:12:02 +03:00
static void _menu_action_up ( struct MenuEntry * entry ) {
/* go up */
2018-11-20 04:07:30 +03:00
char * tmp = strdup ( current_directory ) ;
2018-11-18 04:25:24 +03:00
char * dir = dirname ( tmp ) ;
2018-11-24 07:58:46 +03:00
load_directory ( dir , 1 ) ;
2018-08-12 14:03:37 +03:00
reinitialize_contents ( ) ;
redraw_window ( ) ;
2018-05-21 02:12:02 +03:00
}
2018-11-20 04:07:30 +03:00
/* [Context] > Refresh */
2018-11-19 15:14:39 +03:00
static void _menu_action_refresh ( struct MenuEntry * entry ) {
2018-11-20 04:07:30 +03:00
char * tmp = strdup ( current_directory ) ;
2018-11-24 07:58:46 +03:00
load_directory ( tmp , 0 ) ;
2018-11-19 15:14:39 +03:00
reinitialize_contents ( ) ;
redraw_window ( ) ;
}
2018-11-20 04:07:30 +03:00
/* Help > Contents */
2018-05-21 02:12:02 +03:00
static void _menu_action_help ( struct MenuEntry * entry ) {
/* show help documentation */
2018-08-12 07:24:34 +03:00
system ( " help-browser file-browser.trt & " ) ;
2018-06-04 12:50:36 +03:00
redraw_window ( ) ;
2018-05-21 02:12:02 +03:00
}
2018-11-20 04:07:30 +03:00
/* [Context] > Copy */
2018-11-18 13:16:22 +03:00
static void _menu_action_copy ( struct MenuEntry * entry ) {
size_t output_size = 0 ;
2018-11-20 04:07:30 +03:00
/* Calculate required space for the clipboard */
int base_is_root = ! strcmp ( current_directory , " / " ) ; /* avoid redundant slash */
2018-11-18 13:16:22 +03:00
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
if ( file_pointers [ i ] - > selected ) {
2018-11-20 04:07:30 +03:00
output_size + = strlen ( current_directory ) + ! base_is_root + strlen ( file_pointers [ i ] - > type = = 2 ? file_pointers [ i ] - > filename : file_pointers [ i ] - > name ) + 1 ; /* base / file \n */
2018-11-18 13:16:22 +03:00
}
}
2018-11-20 04:07:30 +03:00
/* Nothing to copy? */
2018-11-18 13:42:31 +03:00
if ( ! output_size ) return ;
2018-11-20 04:07:30 +03:00
/* Create the clipboard contents as a LF-separated list of absolute paths */
2018-12-05 07:47:25 +03:00
char * clipboard = malloc ( output_size + 1 ) ; /* last nil */
2018-11-18 13:16:22 +03:00
clipboard [ 0 ] = ' \0 ' ;
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
if ( file_pointers [ i ] - > selected ) {
2018-11-20 04:07:30 +03:00
strcat ( clipboard , current_directory ) ;
2018-11-18 13:16:22 +03:00
if ( ! base_is_root ) { strcat ( clipboard , " / " ) ; }
2018-11-19 15:14:39 +03:00
strcat ( clipboard , file_pointers [ i ] - > type = = 2 ? file_pointers [ i ] - > filename : file_pointers [ i ] - > name ) ;
2018-11-18 13:16:22 +03:00
strcat ( clipboard , " \n " ) ;
}
}
2018-11-18 13:42:31 +03:00
if ( clipboard [ output_size - 1 ] = = ' \n ' ) {
/* Remove trailing line feed */
clipboard [ output_size - 1 ] = ' \0 ' ;
}
2018-12-05 07:47:25 +03:00
2018-11-18 13:16:22 +03:00
yutani_set_clipboard ( yctx , clipboard ) ;
free ( clipboard ) ;
}
2018-11-21 09:20:48 +03:00
static void _menu_action_paste ( struct MenuEntry * entry ) {
yutani_special_request ( yctx , NULL , YUTANI_SPECIAL_REQUEST_CLIPBOARD ) ;
}
2018-11-20 04:07:30 +03:00
/* Help > About File Browser */
2018-05-21 02:12:02 +03:00
static void _menu_action_about ( struct MenuEntry * entry ) {
/* Show About dialog */
2018-08-12 07:24:34 +03:00
char about_cmd [ 1024 ] = " \0 " ;
2018-12-20 08:25:25 +03:00
strcat ( about_cmd , " about \" About File Browser \" /usr/share/icons/48/folder.bmp \" ToaruOS File Browser \" \" (C) 2018 K. Lange \n - \n Part of ToaruOS, which is free software \n released under the NCSA/University of Illinois \n license. \n - \n %https://toaruos.org \n %https://github.com/klange/toaruos \" " ) ;
2018-08-12 07:24:34 +03:00
char coords [ 100 ] ;
sprintf ( coords , " %d %d & " , ( int ) main_window - > x + ( int ) main_window - > width / 2 , ( int ) main_window - > y + ( int ) main_window - > height / 2 ) ;
strcat ( about_cmd , coords ) ;
system ( about_cmd ) ;
2018-06-04 12:50:36 +03:00
redraw_window ( ) ;
2018-05-21 02:12:02 +03:00
}
2018-11-20 04:07:30 +03:00
/**
* Generic application launcher - like system ( ) , but without the wait .
* Also sets the working directory to the currently - opened directory .
*/
2018-11-19 13:26:40 +03:00
static void launch_application ( char * app ) {
if ( ! fork ( ) ) {
2018-11-20 04:07:30 +03:00
if ( current_directory ) chdir ( current_directory ) ;
2018-11-19 13:26:40 +03:00
char * tmp = malloc ( strlen ( app ) + 10 ) ;
2018-11-19 15:14:39 +03:00
sprintf ( tmp , " %s " , app ) ;
2018-11-19 13:26:40 +03:00
char * args [ ] = { " /bin/sh " , " -c " , tmp , NULL } ;
execvp ( args [ 0 ] , args ) ;
exit ( 1 ) ;
}
}
2018-11-20 04:07:30 +03:00
/* Generic handler for various launcher menus */
2018-11-19 13:26:40 +03:00
static void launch_application_menu ( struct MenuEntry * self ) {
struct MenuEntry_Normal * _self = ( void * ) self ;
launch_application ( ( char * ) _self - > action ) ;
}
2018-11-20 04:07:30 +03:00
/**
* Perform the appropriate action to open a File
*/
2018-11-19 13:26:40 +03:00
static void open_file ( struct File * f ) {
if ( f - > type = = 1 ) {
char tmp [ 1024 ] ;
2018-11-19 15:14:39 +03:00
if ( is_desktop_background ) {
2018-11-20 04:07:30 +03:00
/* Always open directories in new file browser windows when launched from desktop */
sprintf ( tmp , " file-browser \" %s/%s \" " , current_directory , f - > name ) ;
2018-11-19 15:14:39 +03:00
launch_application ( tmp ) ;
} else {
2018-11-20 04:07:30 +03:00
/* In normal mode, navigate to this directory. */
sprintf ( tmp , " %s/%s " , current_directory , f - > name ) ;
2018-11-24 07:58:46 +03:00
load_directory ( tmp , 1 ) ;
2018-11-19 15:14:39 +03:00
reinitialize_contents ( ) ;
redraw_window ( ) ;
}
} else if ( f - > launcher [ 0 ] ) {
2018-11-19 13:26:40 +03:00
char tmp [ 4096 ] ;
if ( ! strcmp ( f - > launcher , " SELF " ) ) {
2018-11-20 04:07:30 +03:00
/* "SELF" launchers are for binaries. */
2018-11-19 15:14:39 +03:00
sprintf ( tmp , " exec ./%s " , f - > name ) ;
2018-11-19 13:26:40 +03:00
} else {
2018-11-20 04:07:30 +03:00
/* Other launchers shouuld take file names as arguments.
* NOTE : If you don ' t want the file name , you can append # to your launcher .
* Since it ' s parsed by the shell , this will yield a comment .
*/
2018-11-19 13:26:40 +03:00
sprintf ( tmp , " %s \" %s \" " , f - > launcher , f - > name ) ;
}
launch_application ( tmp ) ;
}
}
2018-11-20 04:07:30 +03:00
/* [Context] > Open */
2018-11-19 13:26:40 +03:00
static void _menu_action_open ( struct MenuEntry * self ) {
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
if ( file_pointers [ i ] - > selected ) {
open_file ( file_pointers [ i ] ) ;
}
}
}
2018-11-20 04:07:30 +03:00
/* [Context] > Edit in Bim */
2018-11-20 02:32:44 +03:00
static void _menu_action_edit ( struct MenuEntry * self ) {
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
if ( file_pointers [ i ] - > selected ) {
char tmp [ 1024 ] ;
sprintf ( tmp , " exec terminal bim \" %s \" " , file_pointers [ i ] - > type = = 2 ? file_pointers [ i ] - > filename : file_pointers [ i ] - > name ) ;
launch_application ( tmp ) ;
}
}
}
2018-11-20 04:07:30 +03:00
/* View > (Show/Hide) Hidden Files */
static void _menu_action_toggle_hidden ( struct MenuEntry * self ) {
show_hidden = ! show_hidden ;
menu_update_title ( self , show_hidden ? " Hide Hidden Files " : " Show Hidden Files " ) ;
_menu_action_refresh ( NULL ) ;
}
2018-11-25 06:32:30 +03:00
static void _menu_action_select_all ( struct MenuEntry * self ) {
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
file_pointers [ i ] - > selected = 1 ;
}
reinitialize_contents ( ) ;
2019-01-03 09:46:37 +03:00
update_status ( ) ;
2018-11-25 06:32:30 +03:00
redraw_window ( ) ;
}
2018-12-26 03:53:18 +03:00
static void set_view_mode ( int mode ) {
switch ( mode ) {
default :
case VIEW_MODE_ICONS :
FILE_HEIGHT = 80 ;
FILE_WIDTH = 100 ;
view_mode = VIEW_MODE_ICONS ;
break ;
case VIEW_MODE_TILES :
FILE_HEIGHT = 70 ;
FILE_WIDTH = 260 ;
view_mode = VIEW_MODE_TILES ;
break ;
case VIEW_MODE_LIST :
FILE_HEIGHT = 24 ;
FILE_WIDTH = 100 ; /* Readjusts elsewhere */
view_mode = VIEW_MODE_LIST ;
break ;
}
reinitialize_contents ( ) ;
redraw_window ( ) ;
}
static void _menu_action_view_mode ( struct MenuEntry * entry ) {
struct MenuEntry_Normal * _entry = ( void * ) entry ;
int mode = VIEW_MODE_ICONS ;
if ( ! strcmp ( _entry - > action , " icons " ) ) {
mode = VIEW_MODE_ICONS ;
} else if ( ! strcmp ( _entry - > action , " tiles " ) ) {
mode = VIEW_MODE_TILES ;
} else if ( ! strcmp ( _entry - > action , " list " ) ) {
mode = VIEW_MODE_LIST ;
}
set_view_mode ( mode ) ;
}
2018-11-21 09:20:48 +03:00
static void handle_clipboard ( char * contents ) {
fprintf ( stderr , " Received clipboard: \n %s \n " , contents ) ;
char * file = contents ;
while ( file & & * file ) {
char * next_file = strchr ( file , ' \n ' ) ;
if ( next_file ) {
* next_file = ' \0 ' ;
next_file + + ;
}
/* determine if the destination already exists */
char * cheap_basename = strrchr ( file , ' / ' ) ;
if ( ! cheap_basename ) cheap_basename = file ;
else cheap_basename + + ;
char destination [ 4096 ] ;
sprintf ( destination , " %s/%s " , current_directory , cheap_basename ) ;
struct stat statbuf ;
if ( ! stat ( destination , & statbuf ) ) {
char message [ 4096 ] ;
sprintf ( message , " showdialog \" File Browser \" /usr/share/icons/48/folder.bmp \" Not overwriting file '%s'. \" " , cheap_basename ) ;
launch_application ( message ) ;
} else {
char cp [ 1024 ] ;
sprintf ( cp , " cp -r \" %s \" \" %s \" " , file , current_directory ) ;
if ( system ( cp ) ) {
char message [ 4096 ] ;
sprintf ( message , " showdialog \" File Browser \" /usr/share/icons/48/folder.bmp \" Error copying file '%s'. \" " , cheap_basename ) ;
launch_application ( message ) ;
}
}
file = next_file ;
}
_menu_action_refresh ( NULL ) ;
}
2018-11-20 04:07:30 +03:00
/**
* Toggle the selected status of the highlighted icon .
*
* When Ctrl is held , the current selection is maintained .
*/
2018-11-19 13:26:40 +03:00
static void toggle_selected ( int hilighted_offset , int modifiers ) {
struct File * f = get_file_at_offset ( hilighted_offset ) ;
2018-11-20 04:07:30 +03:00
/* No file at this offset, do nothing. */
2018-11-19 13:26:40 +03:00
if ( ! f ) return ;
2018-11-20 04:07:30 +03:00
/* Toggle selection of the current file */
2018-11-19 13:26:40 +03:00
f - > selected = ! f - > selected ;
2018-11-20 04:07:30 +03:00
/* If Ctrl wasn't held, unselect everything else. */
2018-12-07 05:05:21 +03:00
if ( ! ( modifiers & YUTANI_KEY_MODIFIER_CTRL ) ) {
2018-11-19 13:26:40 +03:00
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
if ( file_pointers [ i ] ! = f & & file_pointers [ i ] - > selected ) {
file_pointers [ i ] - > selected = 0 ;
clear_offset ( i ) ;
draw_file ( file_pointers [ i ] , i ) ;
}
}
}
2018-11-20 04:07:30 +03:00
2019-01-03 09:46:37 +03:00
update_status ( ) ;
2018-11-20 04:07:30 +03:00
/* Redraw the file */
2018-11-19 13:26:40 +03:00
clear_offset ( hilighted_offset ) ;
draw_file ( f , hilighted_offset ) ;
2018-11-19 15:14:39 +03:00
2018-11-20 04:07:30 +03:00
/* And repaint the window */
redraw_window ( ) ;
2018-11-19 15:14:39 +03:00
}
2018-11-24 07:58:46 +03:00
static int _down_button = - 1 ;
static void _set_hilight ( int index , int hilight ) {
2018-12-03 10:41:34 +03:00
int _update = 0 ;
2018-11-24 07:58:46 +03:00
if ( _button_hover ! = index | | ( _button_hover = = index & & index ! = - 1 & & _button_hilights [ index ] ! = hilight ) ) {
2018-12-03 10:41:34 +03:00
if ( _button_hover ! = - 1 & & _button_hilights [ _button_hover ] ! = 3 ) {
2018-11-24 07:58:46 +03:00
_button_hilights [ _button_hover ] = 3 ;
2018-12-03 10:41:34 +03:00
_update = 1 ;
2018-11-24 07:58:46 +03:00
}
_button_hover = index ;
2018-11-24 08:11:02 +03:00
if ( index ! = - 1 & & ! _button_disabled [ index ] ) {
2018-11-24 07:58:46 +03:00
_button_hilights [ _button_hover ] = hilight ;
2018-12-03 10:41:34 +03:00
_update = 1 ;
}
if ( _update ) {
redraw_window ( ) ;
2018-11-24 07:58:46 +03:00
}
}
}
static void _handle_button_press ( int index ) {
2018-11-24 08:11:02 +03:00
if ( index ! = - 1 & & _button_disabled [ index ] ) return ; /* can't click disabled buttons */
2018-11-24 07:58:46 +03:00
switch ( index ) {
case 0 :
/* Back */
if ( history_back - > length ) {
list_insert ( history_forward , strdup ( current_directory ) ) ;
node_t * next = list_pop ( history_back ) ;
load_directory ( next - > value , 0 ) ;
free ( next - > value ) ;
free ( next ) ;
reinitialize_contents ( ) ;
redraw_window ( ) ;
}
break ;
case 1 :
/* Forward */
if ( history_forward - > length ) {
list_insert ( history_back , strdup ( current_directory ) ) ;
node_t * next = list_pop ( history_forward ) ;
load_directory ( next - > value , 0 ) ;
free ( next - > value ) ;
free ( next ) ;
reinitialize_contents ( ) ;
redraw_window ( ) ;
}
break ;
case 2 :
/* Up */
_menu_action_up ( NULL ) ;
break ;
case 3 :
/* Home */
{
struct MenuEntry_Normal _fake = { . action = getenv ( " HOME " ) } ;
_menu_action_navigate ( & _fake ) ;
}
break ;
default :
/* ??? */
break ;
}
}
2018-12-08 06:35:15 +03:00
static void _scroll_up ( void ) {
scroll_offset - = SCROLL_AMOUNT ;
if ( scroll_offset < 0 ) {
scroll_offset = 0 ;
}
}
static void _scroll_down ( void ) {
if ( available_height > contents - > height ) {
scroll_offset = 0 ;
} else {
scroll_offset + = SCROLL_AMOUNT ;
if ( scroll_offset > contents - > height - available_height ) {
scroll_offset = contents - > height - available_height ;
}
}
}
2018-11-27 14:27:01 +03:00
/**
* Desktop mode responsds to sig_usr2 by returning to
* the bottom of the Z - order stack .
*/
static void sig_usr2 ( int sig ) {
yutani_set_stack ( yctx , main_window , YUTANI_ZORDER_BOTTOM ) ;
_menu_action_refresh ( NULL ) ;
2018-12-05 07:26:09 +03:00
signal ( SIGUSR2 , sig_usr2 ) ;
}
static void sig_usr1 ( int sig ) {
yutani_window_resize_offer ( yctx , main_window , yctx - > display_width , yctx - > display_height ) ;
signal ( SIGUSR1 , sig_usr1 ) ;
2018-11-27 14:27:01 +03:00
}
2018-12-23 10:16:42 +03:00
static void arrow_select ( int x , int y ) {
if ( ! file_pointers_len ) return ;
/* Find first selected */
int selected = - 1 ;
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
if ( file_pointers [ i ] - > selected ) {
selected = i ;
}
file_pointers [ i ] - > selected = 0 ;
}
if ( selected = = - 1 ) {
selected = 0 ;
} else {
int offset_y = selected / FILE_PTR_WIDTH ;
int offset_x = selected % FILE_PTR_WIDTH ;
offset_y + = y ;
offset_x + = x ;
if ( offset_x > = FILE_PTR_WIDTH ) {
offset_x = FILE_PTR_WIDTH - 1 ;
}
if ( offset_x < 0 ) {
offset_x = 0 ;
}
if ( offset_y < 0 ) {
offset_y = 0 ;
}
selected = offset_y * FILE_PTR_WIDTH + offset_x ;
if ( selected > = file_pointers_len ) selected = file_pointers_len - 1 ;
if ( selected < 0 ) selected = 0 ;
}
int offset_y = selected / FILE_PTR_WIDTH ;
if ( offset_y * FILE_HEIGHT < scroll_offset ) {
scroll_offset = offset_y * FILE_HEIGHT ;
}
if ( offset_y * FILE_HEIGHT + FILE_HEIGHT > scroll_offset + available_height ) {
scroll_offset = offset_y * FILE_HEIGHT + FILE_HEIGHT - available_height ;
}
file_pointers [ selected ] - > selected = 1 ;
2019-01-03 09:46:37 +03:00
update_status ( ) ;
2018-12-23 10:16:42 +03:00
reinitialize_contents ( ) ;
redraw_window ( ) ;
}
2018-05-21 02:12:02 +03:00
int main ( int argc , char * argv [ ] ) {
yctx = yutani_init ( ) ;
init_decorations ( ) ;
2018-11-19 15:14:39 +03:00
2018-11-21 11:35:26 +03:00
int arg_ind = 1 ;
2018-11-19 15:14:39 +03:00
if ( argc > 1 & & ! strcmp ( argv [ 1 ] , " --wallpaper " ) ) {
is_desktop_background = 1 ;
2018-11-24 07:58:46 +03:00
menu_bar_height = 0 ;
2018-12-05 07:26:09 +03:00
signal ( SIGUSR1 , sig_usr1 ) ;
2018-11-19 15:14:39 +03:00
signal ( SIGUSR2 , sig_usr2 ) ;
draw_background ( yctx - > display_width , yctx - > display_height ) ;
2018-12-05 10:50:23 +03:00
main_window = yutani_window_create_flags ( yctx , yctx - > display_width , yctx - > display_height , YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS ) ;
2018-11-19 15:14:39 +03:00
yutani_window_move ( yctx , main_window , 0 , 0 ) ;
yutani_set_stack ( yctx , main_window , YUTANI_ZORDER_BOTTOM ) ;
2018-11-21 11:35:26 +03:00
arg_ind + + ;
2018-11-27 14:27:01 +03:00
FILE * f = fopen ( " /var/run/.wallpaper.pid " , " w " ) ;
fprintf ( f , " %d \n " , getpid ( ) ) ;
fclose ( f ) ;
2018-11-19 15:14:39 +03:00
} else {
main_window = yutani_window_create ( yctx , 800 , 600 ) ;
yutani_window_move ( yctx , main_window , yctx - > display_width / 2 - main_window - > width / 2 , yctx - > display_height / 2 - main_window - > height / 2 ) ;
}
2018-11-21 11:35:26 +03:00
if ( arg_ind < argc ) {
chdir ( argv [ arg_ind ] ) ;
}
2018-05-21 02:12:02 +03:00
ctx = init_graphics_yutani_double_buffer ( main_window ) ;
2018-09-12 06:53:08 +03:00
struct decor_bounds bounds ;
2018-11-19 15:14:39 +03:00
_decor_get_bounds ( main_window , & bounds ) ;
2018-09-12 06:53:08 +03:00
2018-11-18 04:25:24 +03:00
set_title ( NULL ) ;
2018-05-21 02:12:02 +03:00
menu_bar . entries = menu_entries ;
menu_bar . redraw_callback = redraw_window ;
menu_bar . set = menu_set_create ( ) ;
struct MenuList * m = menu_create ( ) ; /* File */
menu_insert ( m , menu_create_normal ( " exit " , NULL , " Exit " , _menu_action_exit ) ) ;
menu_set_insert ( menu_bar . set , " file " , m ) ;
2018-11-20 04:07:30 +03:00
m = menu_create ( ) ;
2018-11-25 06:32:30 +03:00
menu_insert ( m , menu_create_normal ( NULL , NULL , " Copy " , _menu_action_copy ) ) ;
menu_insert ( m , menu_create_normal ( NULL , NULL , " Paste " , _menu_action_paste ) ) ;
menu_insert ( m , menu_create_separator ( ) ) ;
menu_insert ( m , menu_create_normal ( NULL , NULL , " Select all " , _menu_action_select_all ) ) ;
menu_set_insert ( menu_bar . set , " edit " , m ) ;
m = menu_create ( ) ;
menu_insert ( m , menu_create_normal ( " refresh " , NULL , " Refresh " , _menu_action_refresh ) ) ;
menu_insert ( m , menu_create_separator ( ) ) ;
2018-12-26 03:53:18 +03:00
menu_insert ( m , menu_create_normal ( NULL , " icons " , " Show Icons " , _menu_action_view_mode ) ) ;
menu_insert ( m , menu_create_normal ( NULL , " tiles " , " Show Tiles " , _menu_action_view_mode ) ) ;
menu_insert ( m , menu_create_normal ( NULL , " list " , " Show List " , _menu_action_view_mode ) ) ;
menu_insert ( m , menu_create_separator ( ) ) ;
2018-11-20 04:07:30 +03:00
menu_insert ( m , menu_create_normal ( NULL , NULL , " Show Hidden Files " , _menu_action_toggle_hidden ) ) ;
menu_set_insert ( menu_bar . set , " view " , m ) ;
2018-05-21 02:12:02 +03:00
m = menu_create ( ) ; /* Go */
2018-08-14 11:13:38 +03:00
/* TODO implement input dialog for Path... */
#if 0
2018-05-21 02:12:02 +03:00
menu_insert ( m , menu_create_normal ( " open " , NULL , " Path... " , _menu_action_input_path ) ) ;
menu_insert ( m , menu_create_separator ( ) ) ;
2018-08-14 11:13:38 +03:00
# endif
2018-05-21 02:12:02 +03:00
menu_insert ( m , menu_create_normal ( " home " , getenv ( " HOME " ) , " Home " , _menu_action_navigate ) ) ;
menu_insert ( m , menu_create_normal ( NULL , " / " , " File System " , _menu_action_navigate ) ) ;
menu_insert ( m , menu_create_normal ( " up " , NULL , " Up " , _menu_action_up ) ) ;
menu_set_insert ( menu_bar . set , " go " , m ) ;
m = menu_create ( ) ;
menu_insert ( m , menu_create_normal ( " help " , NULL , " Contents " , _menu_action_help ) ) ;
menu_insert ( m , menu_create_separator ( ) ) ;
menu_insert ( m , menu_create_normal ( " star " , NULL , " About " APPLICATION_TITLE , _menu_action_about ) ) ;
menu_set_insert ( menu_bar . set , " help " , m ) ;
2019-01-03 09:46:37 +03:00
available_height = ctx - > height - menu_bar_height - bounds . height - ( is_desktop_background ? 0 : STATUS_HEIGHT ) ;
2019-01-03 12:00:56 +03:00
fprintf ( stderr , " available_height = %d \n " , available_height ) ;
2018-11-18 03:38:47 +03:00
context_menu = menu_create ( ) ; /* Right-click menu */
2018-11-19 13:26:40 +03:00
menu_insert ( context_menu , menu_create_normal ( NULL , NULL , " Open " , _menu_action_open ) ) ;
2018-11-20 02:32:44 +03:00
menu_insert ( context_menu , menu_create_normal ( NULL , NULL , " Edit in Bim " , _menu_action_edit ) ) ;
2018-11-18 13:16:22 +03:00
menu_insert ( context_menu , menu_create_separator ( ) ) ;
menu_insert ( context_menu , menu_create_normal ( NULL , NULL , " Copy " , _menu_action_copy ) ) ;
2018-11-21 09:20:48 +03:00
menu_insert ( context_menu , menu_create_normal ( NULL , NULL , " Paste " , _menu_action_paste ) ) ;
2018-11-19 13:26:40 +03:00
menu_insert ( context_menu , menu_create_separator ( ) ) ;
2018-11-19 15:14:39 +03:00
if ( ! is_desktop_background ) {
menu_insert ( context_menu , menu_create_normal ( " up " , NULL , " Up " , _menu_action_up ) ) ;
}
menu_insert ( context_menu , menu_create_normal ( " refresh " , NULL , " Refresh " , _menu_action_refresh ) ) ;
2018-11-19 13:26:40 +03:00
menu_insert ( context_menu , menu_create_normal ( " utilities-terminal " , " terminal " , " Open Terminal " , launch_application_menu ) ) ;
2018-11-18 03:38:47 +03:00
2018-11-24 07:58:46 +03:00
history_back = list_create ( ) ;
history_forward = list_create ( ) ;
2018-11-20 04:07:30 +03:00
/* Load the current working directory */
2018-11-19 13:26:40 +03:00
char tmp [ 1024 ] ;
getcwd ( tmp , 1024 ) ;
2018-11-24 07:58:46 +03:00
load_directory ( tmp , 1 ) ;
2018-11-20 04:07:30 +03:00
/* Draw files */
2018-08-10 07:57:24 +03:00
reinitialize_contents ( ) ;
2018-05-21 02:12:02 +03:00
redraw_window ( ) ;
while ( application_running ) {
2018-11-19 13:26:40 +03:00
waitpid ( - 1 , NULL , WNOHANG ) ;
2018-12-05 10:32:43 +03:00
int fds [ 1 ] = { fileno ( yctx - > sock ) } ;
int index = fswait2 ( 1 , fds , wallpaper_old ? 10 : 200 ) ;
2018-12-05 10:50:23 +03:00
if ( restart ) {
execvp ( argv [ 0 ] , argv ) ;
return 1 ;
}
2018-12-05 10:32:43 +03:00
if ( index = = 1 ) {
if ( wallpaper_old ) {
redraw_window ( ) ;
}
continue ;
}
2018-05-21 02:12:02 +03:00
yutani_msg_t * m = yutani_poll ( yctx ) ;
while ( m ) {
2018-12-05 10:32:43 +03:00
int redraw = 0 ;
2018-07-21 19:24:22 +03:00
if ( menu_process_event ( yctx , m ) ) {
2018-12-05 10:32:43 +03:00
redraw = 1 ;
2018-07-21 19:24:22 +03:00
}
2018-05-21 02:12:02 +03:00
switch ( m - > type ) {
2018-11-19 15:14:39 +03:00
case YUTANI_MSG_WELCOME :
if ( is_desktop_background ) {
yutani_window_resize_offer ( yctx , main_window , yctx - > display_width , yctx - > display_height ) ;
}
break ;
2018-05-21 02:12:02 +03:00
case YUTANI_MSG_KEY_EVENT :
{
struct yutani_msg_key_event * ke = ( void * ) m - > data ;
2018-12-23 10:16:42 +03:00
if ( ke - > event . action = = KEY_ACTION_DOWN & & ke - > wid = = main_window - > wid ) {
2018-12-23 11:16:20 +03:00
if ( nav_bar_focused ) {
2018-12-23 11:24:26 +03:00
switch ( ke - > event . key ) {
2018-12-23 11:16:20 +03:00
case KEY_ESCAPE :
nav_bar_focused = 0 ;
redraw_window ( ) ;
break ;
case KEY_BACKSPACE :
2018-12-27 07:01:12 +03:00
nav_bar_backspace ( ) ;
2018-12-23 11:16:20 +03:00
break ;
2018-12-23 11:24:26 +03:00
case KEY_CTRL_W :
2018-12-27 07:01:12 +03:00
nav_bar_backspace_word ( ) ;
2018-12-23 11:24:26 +03:00
break ;
2018-12-23 11:16:20 +03:00
case ' \n ' :
nav_bar_focused = 0 ;
char * tmp = strdup ( nav_bar ) ;
load_directory ( tmp , 1 ) ;
reinitialize_contents ( ) ;
redraw_window ( ) ;
break ;
default :
2018-12-23 11:24:26 +03:00
if ( isgraph ( ke - > event . key ) ) {
2018-12-27 07:01:12 +03:00
nav_bar_insert_char ( ke - > event . key ) ;
} else {
switch ( ke - > event . keycode ) {
case KEY_ARROW_LEFT :
nav_bar_cursor_left ( ) ;
break ;
case KEY_ARROW_RIGHT :
nav_bar_cursor_right ( ) ;
break ;
}
2018-12-23 11:16:20 +03:00
}
break ;
}
break ;
}
2018-12-08 06:35:15 +03:00
switch ( ke - > event . keycode ) {
case KEY_PAGE_UP :
_scroll_up ( ) ;
redraw = 1 ;
break ;
case KEY_PAGE_DOWN :
_scroll_down ( ) ;
redraw = 1 ;
break ;
2018-12-23 10:16:42 +03:00
/* if not focused on anything focusable */
case KEY_ARROW_DOWN :
arrow_select ( 0 , 1 ) ;
break ;
case KEY_ARROW_UP :
arrow_select ( 0 , - 1 ) ;
break ;
case KEY_ARROW_LEFT :
arrow_select ( - 1 , 0 ) ;
break ;
case KEY_ARROW_RIGHT :
arrow_select ( 1 , 0 ) ;
break ;
case KEY_BACKSPACE :
_menu_action_up ( NULL ) ;
break ;
case ' \n ' :
_menu_action_open ( NULL ) ;
break ;
2018-12-23 11:16:20 +03:00
case ' l ' :
if ( ke - > event . modifiers & YUTANI_KEY_MODIFIER_CTRL & & ! is_desktop_background ) {
nav_bar_focused = 1 ;
redraw_window ( ) ;
}
break ;
2018-12-23 10:16:42 +03:00
case ' f ' :
2018-12-23 12:03:21 +03:00
if ( ke - > event . modifiers & YUTANI_KEY_MODIFIER_ALT & & ! is_desktop_background ) {
2018-12-23 10:16:42 +03:00
menu_bar_show_menu ( yctx , main_window , & menu_bar , - 1 , & menu_entries [ 0 ] ) ;
}
break ;
case ' e ' :
2018-12-23 12:03:21 +03:00
if ( ke - > event . modifiers & YUTANI_KEY_MODIFIER_ALT & & ! is_desktop_background ) {
2018-12-23 10:16:42 +03:00
menu_bar_show_menu ( yctx , main_window , & menu_bar , - 1 , & menu_entries [ 1 ] ) ;
}
break ;
case ' v ' :
2018-12-23 12:03:21 +03:00
if ( ke - > event . modifiers & YUTANI_KEY_MODIFIER_ALT & & ! is_desktop_background ) {
2018-12-23 10:16:42 +03:00
menu_bar_show_menu ( yctx , main_window , & menu_bar , - 1 , & menu_entries [ 2 ] ) ;
}
break ;
case ' g ' :
2018-12-23 12:03:21 +03:00
if ( ke - > event . modifiers & YUTANI_KEY_MODIFIER_ALT & & ! is_desktop_background ) {
2018-12-23 10:16:42 +03:00
menu_bar_show_menu ( yctx , main_window , & menu_bar , - 1 , & menu_entries [ 3 ] ) ;
}
break ;
case ' h ' :
2018-12-23 12:03:21 +03:00
if ( ke - > event . modifiers & YUTANI_KEY_MODIFIER_ALT & & ! is_desktop_background ) {
2018-12-23 10:16:42 +03:00
menu_bar_show_menu ( yctx , main_window , & menu_bar , - 1 , & menu_entries [ 4 ] ) ;
}
break ;
2018-12-08 06:35:15 +03:00
case ' q ' :
if ( ! is_desktop_background ) {
_menu_action_exit ( NULL ) ;
}
break ;
default :
break ;
2018-11-20 04:07:30 +03:00
}
2018-05-21 02:12:02 +03:00
}
}
break ;
case YUTANI_MSG_WINDOW_FOCUS_CHANGE :
{
struct yutani_msg_window_focus_change * wf = ( void * ) m - > data ;
yutani_window_t * win = hashmap_get ( yctx - > windows , ( void * ) wf - > wid ) ;
2018-07-21 19:24:22 +03:00
if ( win = = main_window ) {
2018-07-24 03:20:11 +03:00
win - > focused = wf - > focused ;
2018-11-22 12:23:25 +03:00
redraw_files ( ) ;
2018-12-05 10:32:43 +03:00
redraw = 1 ;
2018-05-21 02:12:02 +03:00
}
}
break ;
case YUTANI_MSG_RESIZE_OFFER :
{
struct yutani_msg_window_resize * wr = ( void * ) m - > data ;
if ( wr - > wid = = main_window - > wid ) {
resize_finish ( wr - > width , wr - > height ) ;
}
}
break ;
2018-11-21 09:20:48 +03:00
case YUTANI_MSG_CLIPBOARD :
{
struct yutani_msg_clipboard * cb = ( void * ) m - > data ;
char * selection_text ;
if ( * cb - > content = = ' \002 ' ) {
int size = atoi ( & cb - > content [ 2 ] ) ;
FILE * clipboard = yutani_open_clipboard ( yctx ) ;
selection_text = malloc ( size + 1 ) ;
fread ( selection_text , 1 , size , clipboard ) ;
selection_text [ size ] = ' \0 ' ;
fclose ( clipboard ) ;
} else {
selection_text = malloc ( cb - > size + 1 ) ;
memcpy ( selection_text , cb - > content , cb - > size ) ;
selection_text [ cb - > size ] = ' \0 ' ;
}
handle_clipboard ( selection_text ) ;
free ( selection_text ) ;
}
break ;
2018-05-21 02:12:02 +03:00
case YUTANI_MSG_WINDOW_MOUSE_EVENT :
{
struct yutani_msg_window_mouse_event * me = ( void * ) m - > data ;
yutani_window_t * win = hashmap_get ( yctx - > windows , ( void * ) me - > wid ) ;
2018-09-12 06:53:08 +03:00
struct decor_bounds bounds ;
2018-11-19 15:14:39 +03:00
_decor_get_bounds ( win , & bounds ) ;
2018-05-21 02:12:02 +03:00
if ( win = = main_window ) {
int result = decor_handle_event ( yctx , m ) ;
switch ( result ) {
case DECOR_CLOSE :
_menu_action_exit ( NULL ) ;
break ;
case DECOR_RIGHT :
/* right click in decoration, show appropriate menu */
decor_show_default_menu ( main_window , main_window - > x + me - > new_x , main_window - > y + me - > new_y ) ;
break ;
default :
/* Other actions */
break ;
}
/* Menu bar */
menu_bar_mouse_event ( yctx , main_window , & menu_bar , me , me - > new_x , me - > new_y ) ;
2018-08-12 07:59:00 +03:00
2018-11-24 07:58:46 +03:00
if ( menu_bar_height & &
me - > new_y > ( int ) ( bounds . top_height + menu_bar_height - 36 ) & &
me - > new_y < ( int ) ( bounds . top_height + menu_bar_height ) & &
me - > new_x > ( int ) ( bounds . left_width ) & &
me - > new_x < ( int ) ( main_window - > width - bounds . right_width ) ) {
int x = me - > new_x - bounds . left_width - 2 ;
if ( x > = 0 ) {
int i = x / 34 ;
if ( i < 4 ) {
if ( me - > command = = YUTANI_MOUSE_EVENT_DOWN ) {
_set_hilight ( i , 2 ) ;
2018-12-23 11:16:20 +03:00
nav_bar_focused = 0 ;
2018-11-24 07:58:46 +03:00
_down_button = i ;
} else if ( me - > command = = YUTANI_MOUSE_EVENT_RAISE | | me - > command = = YUTANI_MOUSE_EVENT_CLICK ) {
if ( _down_button ! = - 1 & & _down_button = = i ) {
_handle_button_press ( i ) ;
_set_hilight ( i , 1 ) ;
}
_down_button = - 1 ;
} else {
if ( ! ( me - > buttons & YUTANI_MOUSE_BUTTON_LEFT ) ) {
_set_hilight ( i , 1 ) ;
} else {
if ( _down_button = = i ) {
_set_hilight ( i , 2 ) ;
} else if ( _down_button ! = - 1 ) {
_set_hilight ( _down_button , 3 ) ;
}
}
}
} else {
_set_hilight ( - 1 , 0 ) ;
2018-12-23 11:16:20 +03:00
if ( me - > command = = YUTANI_MOUSE_EVENT_DOWN ) {
2018-12-27 07:01:12 +03:00
nav_bar_focused = 1 ;
_figure_out_navbar_cursor ( me - > new_x , bounds ) ;
redraw = 1 ;
2018-12-23 11:16:20 +03:00
}
2018-11-24 07:58:46 +03:00
}
}
} else {
2018-12-23 11:16:20 +03:00
if ( me - > command = = YUTANI_MOUSE_EVENT_DOWN ) {
if ( nav_bar_focused ) {
nav_bar_focused = 0 ;
redraw = 1 ;
}
}
2018-11-24 07:58:46 +03:00
if ( _button_hover ! = - 1 ) {
_button_hilights [ _button_hover ] = 3 ;
_button_hover = - 1 ;
2018-12-05 10:32:43 +03:00
redraw = 1 ; /* Double redraw ??? */
2018-11-24 07:58:46 +03:00
}
}
2019-01-03 09:46:37 +03:00
if ( ! is_desktop_background & & me - > new_y > ( int ) ( main_window - > height - bounds . bottom_height - STATUS_HEIGHT ) ) {
} else if ( me - > new_y > ( int ) ( bounds . top_height + menu_bar_height ) & &
2018-09-12 06:53:08 +03:00
me - > new_y < ( int ) ( main_window - > height - bounds . bottom_height ) & &
me - > new_x > ( int ) ( bounds . left_width ) & &
2018-11-22 12:23:25 +03:00
me - > new_x < ( int ) ( main_window - > width - bounds . right_width ) & &
me - > command ! = YUTANI_MOUSE_EVENT_LEAVE ) {
2018-08-12 07:59:00 +03:00
if ( me - > buttons & YUTANI_MOUSE_SCROLL_UP ) {
/* Scroll up */
2018-12-08 06:35:15 +03:00
_scroll_up ( ) ;
2018-12-05 10:32:43 +03:00
redraw = 1 ;
2018-08-12 07:59:00 +03:00
} else if ( me - > buttons & YUTANI_MOUSE_SCROLL_DOWN ) {
2018-12-08 06:35:15 +03:00
_scroll_down ( ) ;
2018-12-05 10:32:43 +03:00
redraw = 1 ;
2018-08-12 07:59:00 +03:00
}
2018-08-12 14:03:37 +03:00
/* Get offset into contents */
2018-11-19 15:14:39 +03:00
int y_into = me - > new_y - bounds . top_height - menu_bar_height + scroll_offset ;
2018-11-18 12:36:03 +03:00
int x_into = me - > new_x - bounds . left_width ;
int offset = ( y_into / FILE_HEIGHT ) * FILE_PTR_WIDTH + x_into / FILE_WIDTH ;
if ( x_into > FILE_PTR_WIDTH * FILE_WIDTH ) {
offset = - 1 ;
}
2018-08-12 14:03:37 +03:00
if ( offset ! = hilighted_offset ) {
int old_offset = hilighted_offset ;
hilighted_offset = offset ;
if ( old_offset ! = - 1 ) {
clear_offset ( old_offset ) ;
struct File * f = get_file_at_offset ( old_offset ) ;
if ( f ) {
clear_offset ( old_offset ) ;
draw_file ( f , old_offset ) ;
}
}
struct File * f = get_file_at_offset ( hilighted_offset ) ;
if ( f ) {
clear_offset ( hilighted_offset ) ;
draw_file ( f , hilighted_offset ) ;
}
2018-12-05 10:32:43 +03:00
redraw = 1 ;
2018-08-12 14:03:37 +03:00
}
if ( me - > command = = YUTANI_MOUSE_EVENT_CLICK | | _close_enough ( me ) ) {
struct File * f = get_file_at_offset ( hilighted_offset ) ;
2018-11-18 12:59:29 +03:00
if ( f ) {
2018-11-18 13:02:25 +03:00
if ( last_click_offset = = hilighted_offset & & precise_time_since ( last_click ) < 400 ) {
2018-11-19 13:26:40 +03:00
open_file ( f ) ;
2018-11-18 12:59:29 +03:00
last_click = 0 ;
} else {
last_click = precise_current_time ( ) ;
2018-11-18 13:02:25 +03:00
last_click_offset = hilighted_offset ;
2018-12-07 05:05:21 +03:00
toggle_selected ( hilighted_offset , me - > modifiers ) ;
2018-11-18 12:59:29 +03:00
}
} else {
2018-12-07 05:05:21 +03:00
if ( ! ( me - > modifiers & YUTANI_KEY_MODIFIER_CTRL ) ) {
2018-11-18 12:59:29 +03:00
for ( int i = 0 ; i < file_pointers_len ; + + i ) {
if ( file_pointers [ i ] - > selected ) {
file_pointers [ i ] - > selected = 0 ;
2019-01-03 09:46:37 +03:00
update_status ( ) ;
2018-11-18 12:59:29 +03:00
clear_offset ( i ) ;
draw_file ( file_pointers [ i ] , i ) ;
}
}
2018-12-05 10:32:43 +03:00
redraw = 1 ;
2018-11-18 12:59:29 +03:00
}
2018-08-12 14:03:37 +03:00
}
2018-11-18 03:38:47 +03:00
} else if ( me - > buttons & YUTANI_MOUSE_BUTTON_RIGHT ) {
if ( ! context_menu - > window ) {
2018-11-19 13:26:40 +03:00
struct File * f = get_file_at_offset ( hilighted_offset ) ;
if ( f & & ! f - > selected ) {
2018-12-07 05:05:21 +03:00
toggle_selected ( hilighted_offset , me - > modifiers ) ;
2018-11-19 13:26:40 +03:00
}
2018-11-18 03:38:47 +03:00
menu_show ( context_menu , main_window - > ctx ) ;
yutani_window_move ( main_window - > ctx , context_menu - > window , me - > new_x + main_window - > x , me - > new_y + main_window - > y ) ;
}
2018-08-12 14:03:37 +03:00
}
} else {
int old_offset = hilighted_offset ;
hilighted_offset = - 1 ;
if ( old_offset ! = - 1 ) {
clear_offset ( old_offset ) ;
struct File * f = get_file_at_offset ( old_offset ) ;
if ( f ) {
clear_offset ( old_offset ) ;
draw_file ( f , old_offset ) ;
}
2018-12-05 10:32:43 +03:00
redraw = 1 ;
2018-08-12 14:03:37 +03:00
}
2018-08-12 07:59:00 +03:00
}
2018-05-21 02:12:02 +03:00
}
}
break ;
case YUTANI_MSG_WINDOW_CLOSE :
case YUTANI_MSG_SESSION_END :
_menu_action_exit ( NULL ) ;
break ;
default :
break ;
}
2018-12-05 10:32:43 +03:00
if ( redraw | | wallpaper_old ) {
redraw_window ( ) ;
}
2018-05-21 02:12:02 +03:00
free ( m ) ;
m = yutani_poll_async ( yctx ) ;
}
}
}