Giant blob of minor changes
[dotfiles/.git] / .config / awesome / freedesktop / desktop.lua
1 --[[
2
3      Awesome-Freedesktop
4      Freedesktop.org compliant desktop entries and menu
5
6      Desktop section
7
8      Licensed under GNU General Public License v2
9       * (c) 2016,      Luke Bonham
10       * (c) 2009-2015, Antonio Terceiro
11
12 --]]
13
14 local awful  = require("awful")
15 local theme  = require("beautiful")
16 local utils  = require("menubar.utils")
17 local wibox  = require("wibox")
18
19 local capi   = capi
20 local io     = io
21 local ipairs = ipairs
22 local mouse  = mouse
23 local os     = os
24 local string = string
25 local table  = table
26
27 -- Desktop icons
28 -- freedesktop.desktop
29 local desktop = {
30     -- Default desktop basic icons
31     baseicons = {
32         [1] = {
33             label = "This PC",
34             icon  = "computer",
35             onclick = "computer://"
36         },
37         [2] = {
38             label = "Home",
39             icon  = "user-home",
40             onclick = os.getenv("HOME")
41         },
42         [3] = {
43             label = "Trash",
44             icon  = "user-trash",
45             onclick = "trash://"
46         }
47     },
48     -- Default parameters
49     iconsize   = { width = 48,  height = 48 },
50     labelsize  = { width = 140, height = 20 },
51     margin     = { x = 20, y = 20 },
52 }
53
54 -- MIME types list
55 local mime_types = {}
56
57 -- Icons positioning
58 local desktop_current_pos = {}
59
60 -- @return iterator on input pipe
61 local function pipelines(...)
62     local f = assert(io.popen(...))
63     return function ()
64         local data = f:read()
65         if data == nil then f:close() end
66         return data
67     end
68 end
69
70 -- Adds an icon to desktop
71 -- @param args settings from desktop.add_icons
72 -- @param label icon string label
73 -- @param icon icon string file path
74 -- @param onclick function to execute on click
75 function desktop.add_single_icon(args, label, icon, onclick)
76     local s = args.screen
77
78     -- define icon dimensions and position
79     if not desktop_current_pos[s] then
80         desktop_current_pos[s] = { x = (capi.screen[s].geometry.x + args.iconsize.width + args.margin.x), y = 40 }
81     end
82
83     local totheight = (icon and args.iconsize.height or 0) + (label and args.labelsize.height or 0)
84     if totheight == 0 then return end
85
86     if desktop_current_pos[s].y + totheight > capi.screen[s].geometry.height - 40 then
87         desktop_current_pos[s].x = desktop_current_pos[s].x + args.labelsize.width + args.iconsize.width + args.margin.x
88         desktop_current_pos[s].y = 40
89     end
90
91     local common = { screen = s, bg = "#00000000", visible = true, type = "desktop" }
92
93     -- create icon container
94     if icon then
95         common.width = args.iconsize.width
96         common.height = args.iconsize.height
97         common.x = desktop_current_pos[s].x
98         common.y = desktop_current_pos[s].y
99
100         icon = wibox.widget {
101             image = icon,
102             resize = false,
103             widget = wibox.widget.imagebox
104         }
105
106         icon:buttons(awful.button({ }, 1, nil, onclick))
107
108         icon_container = wibox(common)
109         icon_container:set_widget(icon)
110
111         desktop_current_pos[s].y = desktop_current_pos[s].y + args.iconsize.height + 5
112     end
113
114     -- create label container
115     if label then
116         common.width = args.labelsize.width
117         common.height = args.labelsize.height
118         common.x = desktop_current_pos[s].x - (args.labelsize.width/2) + args.iconsize.width/2
119         common.y = desktop_current_pos[s].y
120
121         caption = wibox.widget {
122             text          = label,
123             align         = "center",
124             forced_width  = common.width,
125             forced_height = common.height,
126             ellipsize     = "middle",
127             widget        = wibox.widget.textbox
128         }
129
130         caption:buttons(awful.button({ }, 1, onclick))
131         caption_container = wibox(common)
132         caption_container:set_widget(caption)
133     end
134
135     desktop_current_pos[s].y = desktop_current_pos[s].y + args.labelsize.height + args.margin.y
136 end
137
138 -- Adds base icons (This PC, Trash, etc) to desktop
139 -- @param args settings from desktop.add_icons
140 function desktop.add_base_icons(args)
141     for _,base in ipairs(args.baseicons) do
142         desktop.add_single_icon(args, base.label, utils.lookup_icon(base.icon), function()
143             awful.spawn(string.format("%s '%s'", args.open_with, base.onclick))
144         end)
145     end
146 end
147
148 -- Looks up a suitable icon for filename
149 -- @param filename string file name
150 -- @return icon file path (string)
151 function desktop.lookup_file_icon(filename)
152     -- load system MIME types
153     if #mime_types == 0 then
154         for line in io.lines("/etc/mime.types") do
155             if not line:find("^#") then
156                 local parsed = {}
157                 for w in line:gmatch("[^%s]+") do
158                     table.insert(parsed, w)
159                 end
160                 if #parsed > 1 then
161                     for i = 2, #parsed do
162                         mime_types[parsed[i]] = parsed[1]:gsub("/", "-")
163                     end
164                 end
165             end
166         end
167     end
168
169     -- try to search a possible icon among standards
170     local extension = filename:match("%a+$")
171     local mime = mime_types[extension] or ""
172     local mime_family = mime:match("^%a+") or ""
173
174     local possible_filenames = {
175         mime, "gnome-mime-" .. mime,
176         mime_family, "gnome-mime-" .. mime_family,
177         extension
178     }
179
180     for i, filename in ipairs(possible_filenames) do
181         local icon = utils.lookup_icon(filename)
182         if icon then return icon end
183     end
184
185     -- if we don"t find ad icon, then pretend is a plain text file
186     return utils.lookup_icon("text-x-generic")
187 end
188
189 -- Parse subdirectories and files list from input directory
190 -- @input dir directory to parse (string)
191 -- @return files table with found entries
192 function desktop.parse_dirs_and_files(dir)
193     local files = {}
194     local paths = pipelines('find '..dir..' -maxdepth 1 -type d | tail -1')
195     for path in paths do
196         if path:match("[^/]+$") then
197             local file = {}
198             file.filename = path:match("[^/]+$")
199             file.path = path
200             file.show = true
201             file.icon = utils.lookup_icon("folder")
202             table.insert(files, file)
203         end
204     end
205     local paths = pipelines('find '..dir..' -maxdepth 1 -type f')
206     for path in paths do
207         if not path:find("%.desktop$") then
208             local file = {}
209             file.filename = path:match("[^/]+$")
210             file.path = path
211             file.show = true
212             file.icon = desktop.lookup_file_icon(file.filename)
213             table.insert(files, file)
214         end
215     end
216     return files
217 end
218
219 -- Adds subdirectories and files icons from args.dir
220 -- @param args settings from desktop.add_icons
221 function desktop.add_dirs_and_files_icons(args)
222     for _, file in ipairs(desktop.parse_dirs_and_files(args.dir)) do
223         if file.show then
224             local label = args.showlabels and file.filename or nil
225             local onclick = function () awful.spawn(string.format("%s '%s'", args.open_with, file.path)) end
226             desktop.add_single_icon(args, label, file.icon, onclick)
227         end
228     end
229 end
230
231 -- Main function, adds base, directory and files icons
232 -- @param args user defined settings, with fallback on defaults
233 function desktop.add_icons(args)
234     args            = args or {}
235     args.screen     = args.screen or mouse.screen
236     args.dir        = args.dir or os.getenv("HOME") .. "/Desktop"
237     args.showlabels = args.showlabel or true
238     args.open_with  = args.open_with or "xdg_open"
239     args.baseicons  = args.baseicons or desktop.baseicons
240     args.iconsize   = args.iconsize or desktop.iconsize
241     args.labelsize  = args.labelsize or desktop.labelsize
242     args.margin     = args.margin or desktop.margin
243
244     -- trying to fallback on Adwaita if theme.icon_theme is not defined
245     -- if Adwaita is missing too, no icons will be shown
246     if not theme.icon_theme then
247         theme.icon_theme = args.icon_theme or "Adwaita"
248     end
249
250     desktop.add_base_icons(args)
251     desktop.add_dirs_and_files_icons(args)
252 end
253
254 return desktop