helpers.lua 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. -- @author cedlemo
  2. local naughty= require("naughty")
  3. local lgi = require("lgi")
  4. local cairo = lgi.cairo
  5. local string = require("string")
  6. local os = require('os')
  7. local awful = require('awful')
  8. local wibox = require('wibox')
  9. local math = math
  10. local table = table
  11. local print = print
  12. ---Functions used in blingbling.
  13. --@module blingbling.helpers
  14. local helpers={}
  15. ---Display values of variables in an awesome popup.
  16. --Each variables in vars is separated by a "|"
  17. --@param vars a table of variable
  18. function helpers.dbg(vars)
  19. local text = ""
  20. for i=1, #vars do text = text .. vars[i] .. " | " end
  21. naughty.notify({ text = text, timeout = 15 })
  22. end
  23. ---Convert an hexadecimal color to rgba color.
  24. --It convert a string variable "#rrggbb" or "#rrggbbaa" (with r,g,b and a which are hexadecimal value) to r, g, b a=1 or r,g,b,a (with r,g,b,a floated value from 0 to 1.
  25. --The function returns 4 variables.
  26. --@param my_color a string "#rrggbb" or "#rrggbbaa"
  27. function helpers.hexadecimal_to_rgba_percent(my_color)
  28. --check if color is a valid hex color else return white
  29. if string.find(my_color,"#[0-f][0-f][0-f][0-f][0-f]") then
  30. --delete #
  31. my_color=string.gsub(my_color,"^#","")
  32. r=string.format("%d", "0x"..string.sub(my_color,1,2))
  33. v=string.format("%d", "0x"..string.sub(my_color,3,4))
  34. b=string.format("%d", "0x"..string.sub(my_color,5,6))
  35. if string.sub(my_color,7,8) == "" then
  36. a=255
  37. else
  38. a=string.format("%d", "0x"..string.sub(my_color,7,8))
  39. end
  40. else
  41. r=255
  42. v=255
  43. b=255
  44. a=255
  45. end
  46. return r/255,v/255,b/255,a/255
  47. end
  48. ---Get red green blue value in parameters and return hexadecimal string
  49. function helpers.rgb(red, green, blue)
  50. if type(red) == "number" or type(green) == "number" or type(blue) == "number" then
  51. return "#"..string.format("%02x",red)..string.format("%02x",green)..string.format("%02x",blue)
  52. else
  53. return nil
  54. end
  55. end
  56. ---Get red green blue and alpha value in parameters and return hexadecimal string.
  57. function helpers.rgba(red, green, blue, alpha)
  58. if type(red) == "number" or type(green) == "number" or type(blue) == "number" or type(alpha) == "number" then
  59. return "#"..string.format("%02x",red)..string.format("%02x",green)..string.format("%02x",blue)..string.format("%02x",alpha * 255)
  60. else
  61. return nil
  62. end
  63. end
  64. ---Check if an hexadecimal color is fully transparent.
  65. --Returns true or false
  66. --@param my_color a string "#rrggbb" or "#rrggbbaa"
  67. function helpers.is_transparent(my_color)
  68. --check if color is a valid hex color else return white
  69. if string.find(my_color,"#[0-f][0-f][0-f][0-f][0-f]") then
  70. --delete #
  71. local my_color=string.gsub(my_color,"^#","")
  72. if string.sub(my_color,7,8) == "" then
  73. return false
  74. else
  75. local alpha=string.format("%d", "0x"..string.sub(my_color,7,8))
  76. if alpha/1 == 0 then
  77. return true
  78. else
  79. return false
  80. end
  81. end
  82. else
  83. return false
  84. end
  85. end
  86. ---Split string in different parts which are returned in a table. The delimiter of each part is a pattern given in argument.
  87. --@param str the string to split
  88. --@param pat the pattern delimiter
  89. function helpers.split(str, pat)
  90. local t = {} -- NOTE: use {n = 0} in Lua-5.0
  91. local fpat = "(.-)" .. pat
  92. local last_end = 1
  93. local s, e, cap = string.find(str,fpat, 1)
  94. while s do
  95. if s ~= 1 or cap ~= "" then
  96. table.insert(t,cap)
  97. end
  98. last_end = e+1
  99. s, e, cap = string.find(str,fpat, last_end)
  100. end
  101. if last_end <= #str then
  102. cap = string.sub(str,last_end)
  103. table.insert(t, cap)
  104. end
  105. return t
  106. end
  107. ---Draw tiles in a cairo context.
  108. --@param cr a cairo context.
  109. --@param height the height of the surface on which we want tiles
  110. --@param v_margin value used to define top margin and/or bottom margin (tiles are not drawn on the margins)
  111. --@param width the width of the surface on which we want tiles
  112. --@param h_margin value used to define left margin and/or right margin.
  113. function helpers.draw_background_tiles(cr, height, v_margin , width, h_margin)
  114. --tiles: width 4 px height 2px horizontal separator=1 px vertical separator=2px
  115. -- v_separator
  116. -- _______\ /_______
  117. -- |_______| |_______|
  118. -- _______ _______ <--h_separator
  119. -- |_______| |_______| <--tiles_height
  120. -- / \
  121. -- tiles_width
  122. tiles_width=4
  123. tiles_height=2
  124. h_separator=1
  125. v_separator=2
  126. --find nb max horizontal lignes we can display with 2 pix squarre and 1 px separator (3px)
  127. local max_line=math.floor((height - v_margin*2) /(tiles_height+h_separator))
  128. --what to do with the rest of the height:
  129. local h_rest=(height - v_margin*2) - (max_line * (tiles_height+h_separator))
  130. if h_rest >= (tiles_height) then
  131. max_line= max_line + 1
  132. h_rest= h_rest - tiles_height
  133. end
  134. if h_rest > 0 then
  135. h_rest =h_rest / 2
  136. end
  137. --find nb columns we can draw with tile of 4px width and 2 px separator (6px) and center them horizontaly
  138. local max_column=math.floor((width - h_margin*2)/6)
  139. local v_rest=(width- h_margin*2)-(max_column*( tiles_width + v_separator))
  140. if v_rest >= (tiles_width) then
  141. max_column= max_column + 1
  142. v_rest= v_rest - tiles_width
  143. end
  144. if v_rest > 0 then
  145. h_rest =h_rest / 2
  146. end
  147. x=width-(tiles_width + v_rest)
  148. y=height -(v_margin +tiles_height + h_rest)
  149. for i=1,max_column do
  150. for j=1,max_line do
  151. cr:rectangle(x,y,4,2)
  152. y= y-(tiles_height + h_separator)
  153. end
  154. y=height -(v_margin + tiles_height + h_rest)
  155. x=x-(tiles_width + v_separator)
  156. end
  157. end
  158. ---Draw text on a rectangle which width and height depend on the text width and height.
  159. --@param cr a cairo context already initialised with oocairo.context_create( )
  160. --@param text the text to display
  161. --@param x the x coordinate of the left of the text
  162. --@param y the y coordinate of the bottom of the text
  163. --@param text_background_color a string "#rrggbb" or "#rrggbbaa" for the rectangle color
  164. --@param text_color a string "#rrggbb" or "#rrggbbaa" for the text color
  165. --@param show_text_centered_on_x a boolean value not mandatory (false by default) if true, x parameter is the coordinate of the middle of the text
  166. --@param show_text_centered_on_y a boolean value not mandatory (false by default) if true, y parameter is the coordinate of the middle of the text
  167. --@param show_text_on_left_of_x a boolean value not mandatory (false by default) if true, x parameter is the right of the text
  168. --@param show_text_on_bottom_of_y a boolean value not mandatory (false by default) if true, y parameter is the top of the text
  169. function helpers.draw_text_and_background(cr, text, x, y, text_background_color, text_color, show_text_centered_on_x, show_text_centered_on_y, show_text_on_left_of_x, show_text_on_bottom_of_y)
  170. --Text background
  171. ext=cr:text_extents(text)
  172. x_modif = 0
  173. y_modif = 0
  174. if show_text_centered_on_x == true then
  175. x_modif = ((ext.width + ext.x_bearing) / 2) + ext.x_bearing / 2
  176. show_text_on_left_of_x = false
  177. else
  178. if show_text_on_left_of_x == true then
  179. x_modif = ext.width + 2 *ext.x_bearing
  180. else
  181. x_modif = x_modif
  182. end
  183. end
  184. if show_text_centered_on_y == true then
  185. y_modif = ((ext.height +ext.y_bearing)/2 ) + ext.y_bearing / 2
  186. show_text_on_left_of_y = false
  187. else
  188. if show_text_on_bottom_of_y == true then
  189. y_modif = ext.height + 2 *ext.y_bearing
  190. else
  191. y_modif = y_modif
  192. end
  193. end
  194. cr:rectangle(x + ext.x_bearing - x_modif,y + ext.y_bearing - y_modif,ext.width, ext.height)
  195. r,g,b,a=helpers.hexadecimal_to_rgba_percent(text_background_color)
  196. cr:set_source_rgba(r,g,b,a)
  197. cr:fill()
  198. --Text
  199. cr:new_path()
  200. cr:move_to(x-x_modif,y-y_modif)
  201. r,g,b,a=helpers.hexadecimal_to_rgba_percent(text_color)
  202. cr:set_source_rgba(r, g, b, a)
  203. cr:show_text(text)
  204. end
  205. ---Drawn one foreground arrow with a background arrow that depend on a value.
  206. --If the value is egal to 0 then the foreground arrow is not drawn.
  207. --@param cr a cairo context already initialised with oocairo.context_create( )
  208. --@param x the x coordinate in the cairo context where the arrow start
  209. --@param y_bottom the bottom corrdinate of the arrows
  210. --@param y_top the top coordinate of the arrows
  211. --@param value a number
  212. --@param background_arrow_color the color of the background arrow, a string "#rrggbb" or "#rrggbbaa"
  213. --@param arrow_color the color of the foreground arrow, a string "#rrggbb" or "#rrggbbaa"
  214. --@param arrow_line_color the color of the outline of the foreground arrow , a string "#rrggbb" or "#rrggbbaa"
  215. --@param up boolean value if false draw a down arrow, if true draw a up arrow
  216. function helpers.draw_up_down_arrows(cr,x,y_bottom,y_top,value,background_arrow_color, arrow_color, arrow_line_color,up)
  217. if up ~= false then
  218. invert = 1
  219. else
  220. invert= -1
  221. end
  222. --Draw the background arrow
  223. cr:move_to(x,y_bottom)
  224. cr:line_to(x,y_top )
  225. cr:line_to(x-(6 * invert), y_top + (6 * invert))
  226. cr:line_to(x-(3*invert), y_top + (6 * invert))
  227. cr:line_to(x-(3*invert), y_bottom)
  228. cr:line_to(x,y_bottom)
  229. cr:close_path()
  230. r,g,b,a = helpers.hexadecimal_to_rgba_percent(background_arrow_color)
  231. cr:set_source_rgba(r, g, b, a)
  232. cr:fill()
  233. --Draw the arrow if value is > 0
  234. if value > 0 then
  235. cr:move_to(x,y_bottom)
  236. cr:line_to(x,y_top )
  237. cr:line_to(x-(6*invert), y_top + (6 * invert))
  238. cr:line_to(x-(3*invert), y_top + (6 * invert))
  239. cr:line_to(x-(3*invert), y_bottom)
  240. cr:line_to(x,y_bottom)
  241. cr:close_path()
  242. r,g,b,a = helpers.hexadecimal_to_rgba_percent(arrow_color)
  243. cr:set_source_rgba(r, g, b, a)
  244. cr:fill()
  245. cr:move_to(x,y_bottom)
  246. cr:line_to(x,y_top )
  247. cr:line_to(x-(6*invert), y_top + (6 * invert))
  248. cr:line_to(x-(3*invert), y_top + (6 * invert))
  249. cr:line_to(x-(3*invert), y_bottom)
  250. cr:line_to(x,y_bottom)
  251. cr:close_path()
  252. r,g,b,a = helpers.hexadecimal_to_rgba_percent(arrow_line_color)
  253. cr:set_source_rgba(r, g, b, a)
  254. cr:set_line_width(1)
  255. cr:stroke()
  256. end
  257. end
  258. ---Draw a vertical bar with gradient color, so it looks like a cylinder, and it's height depends on a value.
  259. --@param cr a cairo context already initialised with oocairo.context_create( )
  260. --@param h_margin the left and right margin of the bar in the cr
  261. --@param v_margin the top and bottom margin of the bar in the cr
  262. --@param width the width used to display the left margin, the bar and the right margin
  263. --@param height the height used to display the top margin, the bar and the bottom margin
  264. --@param represent a table {background_bar_color = "#rrggbb" or "#rrggbbaa", color = "#rrggbb" or "#rrggbbaa", value =the value used to calculate the height of the bar}
  265. function helpers.draw_vertical_bar(cr,h_margin,v_margin, width,height, represent)
  266. x=h_margin
  267. bar_width=width - 2*h_margin
  268. bar_height=height - 2*v_margin
  269. y=v_margin
  270. if represent["background_bar_color"] == nil then
  271. r,g,b,a = helpers.hexadecimal_to_rgba_percent("#000000")
  272. else
  273. r,g,b,a = helpers.hexadecimal_to_rgba_percent(represent["background_bar_color"])
  274. end
  275. cr:rectangle(x,y,bar_width ,bar_height)
  276. gradient=cairo.pattern_create_linear(h_margin, height/2, width-h_margin, height/2)
  277. gradient:add_color_stop_rgba(0, r, g, b, 0.5)
  278. gradient:add_color_stop_rgba(0.5, 1, 1, 1, 0.5)
  279. gradient:add_color_stop_rgba(1, r, g, b, 0.5)
  280. cr:set_source(gradient)
  281. cr:fill()
  282. if represent["value"] ~= nil and represent["color"] ~= nil then
  283. x=h_margin
  284. bar_width=width - 2*h_margin
  285. bar_height=height - 2*v_margin
  286. if represent["invert"] == true then
  287. y=v_margin
  288. else
  289. y=height - (bar_height*represent["value"] + v_margin )
  290. end
  291. cr:rectangle(x,y,bar_width,bar_height*represent["value"])
  292. r,g,b,a = helpers.hexadecimal_to_rgba_percent(represent["color"])
  293. gradient=cairo.pattern_create_linear(0, height/2,width, height/2)
  294. gradient:add_color_stop_rgba(0, r, g, b, 0.1)
  295. gradient:add_color_stop_rgba(0.5, r, g, b, 1)
  296. gradient:add_color_stop_rgba(1, r, g, b, 0.1)
  297. cr:set_source(gradient)
  298. cr:fill()
  299. end
  300. end
  301. ---Draw an horizontal bar with gradient color, so it looks like a cylinder, and it's height depends on a value.
  302. --@param cr a cairo context already initialised with oocairo.context_create( )
  303. --@param h_margin the left and right margin of the bar in the cr
  304. --@param v_margin the top and bottom margin of the bar in the cr
  305. --@param width the width used to display the left margin, the bar and the right margin
  306. --@param height the height used to display the top margin, the bar and the bottom margin
  307. --@param represent a table {background_bar_color = "#rrggbb" or "#rrggbbaa", color = "#rrggbb" or "#rrggbbaa", value =the value used to calculate the width of the bar}
  308. function helpers.draw_horizontal_bar( cr,h_margin,v_margin, width, height, represent)
  309. x=h_margin
  310. bar_width=width - 2*h_margin
  311. bar_height=height - 2*v_margin
  312. y=v_margin
  313. if represent["background_bar_color"] == nil then
  314. r,g,b,a = helpers.hexadecimal_to_rgba_percent("#000000")
  315. else
  316. r,g,b,a = helpers.hexadecimal_to_rgba_percent(represent["background_bar_color"])
  317. end
  318. cr:rectangle(x,y,bar_width,bar_height)
  319. gradient=cairo.pattern_create_linear( width /2,v_margin , width/2, height - v_margin)
  320. gradient:add_color_stop_rgba(0, r, g, b, 0.5)
  321. gradient:add_color_stop_rgba(0.5, 1, 1, 1, 0.5)
  322. gradient:add_color_stop_rgba(1, r, g, b, 0.5)
  323. cr:set_source(gradient)
  324. cr:fill()
  325. if represent["value"] ~= nil and represent["color"] ~= nil then
  326. x=h_margin
  327. bar_width=width - 2*h_margin
  328. bar_height=height - 2*v_margin
  329. if represent["invert"] == true then
  330. x=width - (h_margin + bar_width*represent["value"] )
  331. else
  332. x=h_margin
  333. end
  334. cr:rectangle(x,y,bar_width*represent["value"],bar_height)
  335. r,g,b,a = helpers.hexadecimal_to_rgba_percent(represent["color"])
  336. gradient=cairo.pattern_create_linear(width /2,0 , width/2, height)
  337. gradient:add_color_stop_rgba(0, r, g, b, 0.1)
  338. gradient:add_color_stop_rgba(0.5, r, g, b, 1)
  339. gradient:add_color_stop_rgba(1, r, g, b, 0.1)
  340. cr:set_source(gradient)
  341. cr:fill()
  342. end
  343. end
  344. ---Draw a rectangle width rounded corners.
  345. --@param cr a cairo context already initialised with oocairo.context_create( )
  346. --@param x the x coordinate of the left top corner
  347. --@param y the y corrdinate of the left top corner
  348. --@param width the width of the rectangle
  349. --@param height the height of the rectangle
  350. --@param color a string "#rrggbb" or "#rrggbbaa" for the color of the rectangle
  351. --@param rounded_size a float value from 0 to 1 (0 is no rounded corner) or a table of float value
  352. function helpers.draw_rounded_corners_rectangle(cr,x,y,width, height, color, rounded_size)
  353. --if rounded_size =0 it is a classical rectangle (whooooo!)
  354. local height = height
  355. local width = width
  356. local x = x
  357. local y = y
  358. local rounded_sizes = {}
  359. if type(rounded_size) == "number" then
  360. rounded_sizes[1]=rounded_size or 0
  361. rounded_sizes[2]=rounded_size or 0
  362. rounded_sizes[3]=rounded_size or 0
  363. rounded_sizes[4]=rounded_size or 0
  364. elseif type(rounded_size) == "table" then
  365. rounded_sizes[1]=rounded_size[1] or 0
  366. rounded_sizes[2]=rounded_size[2] or 0
  367. rounded_sizes[3]=rounded_size[3] or 0
  368. rounded_sizes[4]=rounded_size[4] or 0
  369. end
  370. local rounded_size = rounded_size or 0
  371. if height > width then
  372. radius=0.5 * width
  373. else
  374. radius=0.5 * height
  375. end
  376. PI = 2*math.asin(1)
  377. r,g,b,a=helpers.hexadecimal_to_rgba_percent(color)
  378. cr:set_source_rgba(r,g,b,a)
  379. --top left corner
  380. cr:arc(x + radius*rounded_sizes[1],y + radius*rounded_sizes[1], radius*rounded_sizes[1],PI, PI * 1.5)
  381. --top right corner
  382. cr:arc(width - radius*rounded_sizes[2],y + radius*rounded_sizes[2], radius*rounded_sizes[2],PI*1.5, PI * 2)
  383. --bottom right corner
  384. cr:arc(width - radius*rounded_sizes[3],height - radius*rounded_sizes[3], radius*rounded_sizes[3],PI*0, PI * 0.5)
  385. --bottom left corner
  386. cr:arc(x + radius*rounded_sizes[4],height - radius*rounded_sizes[4], radius*rounded_sizes[4],PI*0.5, PI * 1)
  387. cr:close_path()
  388. cr:fill()
  389. -- if border ~= nil then
  390. -- cr:set_line_width(1)
  391. --
  392. -- r,g,b,a=helpers.hexadecimal_to_rgba_percent(border)
  393. -- cr:set_source_rgba(r,g,b,a)
  394. -- --top left corner
  395. -- cr:arc(x +1 + radius*rounded_sizes[1],y+1 + radius*rounded_sizes[1], radius*rounded_sizes[1],PI, PI * 1.5)
  396. -- --top right corner
  397. -- cr:arc(width -1 - radius*rounded_sizes[2],y +1+ radius*rounded_sizes[2], radius*rounded_sizes[2],PI*1.5, PI * 2)
  398. -- --bottom right corner
  399. -- cr:arc(width -1 - radius*rounded_sizes[3],height -1 - radius*rounded_sizes[3], radius*rounded_sizes[3],PI*0, PI * 0.5)
  400. -- --bottom left corner
  401. -- cr:arc(x +1 + radius*rounded_sizes[4],height -1 - radius*rounded_sizes[4], radius*rounded_sizes[4],PI*0.5, PI * 1)
  402. -- cr:close_path()
  403. -- cr:stroke()
  404. -- end
  405. end
  406. ---Set a rectangle width rounded corners that define the area to draw.
  407. --@param cr a cairo context already initialised with oocairo.context_create( )
  408. --@param x the x coordinate of the left top corner
  409. --@param y the y corrdinate of the left top corner
  410. --@param width the width of the rectangle
  411. --@param height the height of the rectangle
  412. --@param rounded_size a float value from 0 to 1 (0 is no rounded corner)
  413. function helpers.clip_rounded_corners_rectangle(cr,x,y,width, height, rounded_size)
  414. --if rounded_size =0 it is a classical rectangle (whooooo!)
  415. local height = height
  416. local width = width
  417. local x = x
  418. local y = y
  419. local rounded_size = rounded_size or 0.4
  420. if height > width then
  421. radius=0.5 * width
  422. else
  423. radius=0.5 * height
  424. end
  425. PI = 2*math.asin(1)
  426. --top left corner
  427. cr:arc(x + radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI, PI * 1.5)
  428. --top right corner
  429. cr:arc(width - radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI*1.5, PI * 2)
  430. --bottom right corner
  431. cr:arc(width - radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0, PI * 0.5)
  432. --bottom left corner
  433. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  434. cr:close_path()
  435. cr:clip()
  436. end
  437. ---Draw a foreground rounded corners rectangle which width depends on a value, and a background rounded corners rectangle.
  438. --@param cr a cairo context already initialised with oocairo.context_create( )
  439. --@param x the x coordinate of the left top corner
  440. --@param y the y corrdinate of the left top corner
  441. --@param width the width of the background rectangle and the maximal width of th foreground rectangle
  442. --@param height the height of the background and the foreground rectangles
  443. --@param background_color a string "#rrggbb" or "#rrggbbaa" for the color of the background rectangle
  444. --@param graph_color a string "#rrggbb" or "#rrggbbaa" for the color of the foreground rectangle
  445. --@param rounded_size a float value from 0 to 1 (0 is no rounded corner)
  446. --@param value_to_represent the percent of the max width used to calculate the width of the foreground rectangle
  447. --@param graph_line_color a string "#rrggbb" or "#rrggbbaa" for the outiline color of the background rectangle
  448. function helpers.draw_rounded_corners_horizontal_graph(cr,x,y,width, height, background_color, graph_color, rounded_size, value_to_represent, graph_line_color)
  449. --if rounded_size =0 it is a classical rectangle (whooooo!)
  450. local height = height
  451. local width = width
  452. local x = x
  453. local y = y
  454. local rounded_size = rounded_size or 0.4
  455. if height > width then
  456. radius=0.5 * width
  457. else
  458. radius=0.5 * height
  459. end
  460. PI = 2*math.asin(1)
  461. --draw the background
  462. r,g,b,a=helpers.hexadecimal_to_rgba_percent(background_color)
  463. cr:set_source_rgba(r,g,b,a)
  464. --top left corner
  465. cr:arc(x + radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI, PI * 1.5)
  466. --top right corner
  467. cr:arc(width - radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI*1.5, PI * 2)
  468. --bottom right corner
  469. cr:arc(width - radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0, PI * 0.5)
  470. --bottom left corner
  471. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  472. cr:close_path()
  473. cr:fill()
  474. --represent the value
  475. -- value in 0 -> 1
  476. -- radius*rounded_size | width - 2*( radius*rounded) | radius * rounded_size
  477. -- | | |
  478. -- | _________| _______________________|
  479. -- | | |
  480. -- v ____v_________ v
  481. -- /| |\
  482. -- | | | | (... and yes I don't have a job)
  483. -- \|______________|/
  484. --
  485. --1 => width/ width
  486. --limit_2 => width -radius / width
  487. --limit_1 => radius /width
  488. value = value_to_represent
  489. limit_2 = (width -(radius * rounded_size)) / width
  490. limit_1 = radius* rounded_size /width
  491. r,g,b,a=helpers.hexadecimal_to_rgba_percent(graph_color)
  492. cr:set_source_rgba(r,g,b,a)
  493. if value <= 1 and value > limit_2 then
  494. cr:arc(x + radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI, PI * 1.5)
  495. ratio = (value - limit_2) / (1 - limit_2)
  496. cr:arc(width - radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI*1.5, PI *(1.5 +(0.5 * ratio)))
  497. cr:arc(width - radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*(0.5 - (0.5 * ratio)) , PI * 0.5)
  498. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  499. cr:close_path()
  500. cr:fill()
  501. elseif value <= limit_2 and value > limit_1 then
  502. cr:arc(x + radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI, PI * 1.5)
  503. ratio = value / limit_2
  504. cr:line_to(limit_2*width*ratio,y)
  505. cr:line_to(limit_2*width*ratio,height)
  506. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  507. cr:close_path()
  508. cr:fill()
  509. elseif value <= limit_1 and value > 0 then
  510. ratio = value / limit_1
  511. cr:arc(x + radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI, PI * (1+ (0.5*ratio)))
  512. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*(1-(0.5 * ratio)) , PI * 1)
  513. cr:close_path()
  514. cr:fill()
  515. end
  516. if graph_line_color then
  517. r,g,b,a=helpers.hexadecimal_to_rgba_percent(graph_line_color)
  518. cr:set_source_rgba(r,g,b,a)
  519. cr:set_line_width(1)
  520. if value <= 1 and value > limit_2 then
  521. cr:arc(x +1+ radius*rounded_size,y+1 + radius*rounded_size, radius*rounded_size,PI, PI * 1.5)
  522. ratio = (value - limit_2) / (1 - limit_2)
  523. cr:arc(width-1 - radius*rounded_size,y+1 + radius*rounded_size, radius*rounded_size,PI*1.5, PI *(1.5 +(0.5 * ratio)))
  524. cr:arc(width-1 - radius*rounded_size,height-1 - radius*rounded_size, radius*rounded_size,PI*(0.5 - (0.5 * ratio)) , PI * 0.5)
  525. cr:arc(x+1 + radius*rounded_size,height-1 - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  526. cr:close_path()
  527. cr:stroke()
  528. elseif value <= limit_2 and value > limit_1 then
  529. cr:arc(x +1+ radius*rounded_size,y+1 + radius*rounded_size, radius*rounded_size,PI, PI * 1.5)
  530. ratio = value / limit_2
  531. cr:line_to(limit_2*width*ratio -1 ,y +1)
  532. cr:line_to(limit_2*width*ratio -1 ,height -1 )
  533. cr:arc(x +1 + radius*rounded_size,height -1 - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  534. cr:close_path()
  535. cr:stroke()
  536. elseif value <= limit_1 and value > 0 then
  537. ratio = value / limit_1
  538. cr:arc(x +1 + radius*rounded_size,y +1 + radius*rounded_size, radius*rounded_size,PI, PI * (1+ (0.5*ratio)))
  539. cr:arc(x +1 + radius*rounded_size,height +1 - radius*rounded_size, radius*rounded_size,PI*(1-(0.5 * ratio)) , PI * 1)
  540. cr:close_path()
  541. cr:stroke()
  542. end
  543. end
  544. end
  545. ---Draw a foreground rounded corners rectangle which height depends on a value, and a background rounded corners rectangle.
  546. --@param cr a cairo context already initialised with oocairo.context_create( )
  547. --@param x the x coordinate of the left top corner
  548. --@param y the y corrdinate of the left top corner
  549. --@param width the width of the background and the foreground rectangles
  550. --@param height the height of the background rectangle and the maximal height of the foreground rectangle
  551. --@param background_color a string "#rrggbb" or "#rrggbbaa" for the color of the background rectangle
  552. --@param graph_color a string "#rrggbb" or "#rrggbbaa" for the color of the foreground rectangle
  553. --@param rounded_size a float value from 0 to 1 (0 is no rounded corner)
  554. --@param value_to_represent the percent of the max height used to calculate the height of the foreground rectangle
  555. --@param graph_line_color a string "#rrggbb" or "#rrggbbaa" for the outiline color of the background rectangle
  556. function helpers.draw_rounded_corners_vertical_graph(cr,x,y,width, height, background_color, graph_color, rounded_size, value_to_represent, graph_line_color)
  557. --if rounded_size =0 it is a classical rectangle (whooooo!)
  558. local height = height
  559. local width = width
  560. local x = x
  561. local y = y
  562. if rounded_size == nil or rounded_size == 0 then
  563. --draw the background:
  564. r,g,b,a=helpers.hexadecimal_to_rgba_percent(background_color)
  565. cr:set_source_rgba(r,g,b,a)
  566. cr:move_to(x,y)
  567. cr:line_to(x,height)
  568. cr:line_to(width,height)
  569. cr:line_to(width,y)
  570. cr:close_path()
  571. cr:fill()
  572. --draw the graph:
  573. r,g,b,a=helpers.hexadecimal_to_rgba_percent(graph_color)
  574. cr:set_source_rgba(r,g,b,a)
  575. cr:move_to(x,height)
  576. cr:line_to(x, height -((height -y)* value_to_represent) )
  577. cr:line_to(width,height -((height - y)*value_to_represent) )
  578. cr:line_to(width,height)
  579. cr:close_path()
  580. cr:fill()
  581. if graph_line_color then
  582. r,g,b,a=helpers.hexadecimal_to_rgba_percent(graph_line_color)
  583. cr:set_source_rgba(r,g,b,a)
  584. cr:move_to(x,height)
  585. cr:line_to(x,height -((height -y)* value_to_represent) )
  586. cr:line_to(width,height -((height -y)*value_to_represent) )
  587. cr:line_to(width,height)
  588. cr:close_path()
  589. cr:set_line_width(1)
  590. cr:stroke()
  591. end
  592. else
  593. local rounded_size = rounded_size or 0.4
  594. if height > width then
  595. radius=0.5 * width
  596. else
  597. radius=0.5 * height
  598. end
  599. PI = 2*math.asin(1)
  600. --draw the background
  601. r,g,b,a=helpers.hexadecimal_to_rgba_percent(background_color)
  602. cr:set_source_rgba(r,g,b,a)
  603. --top left corner
  604. cr:arc(x + radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI, PI * 1.5)
  605. --top right corner
  606. cr:arc(width - radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI*1.5, PI * 2)
  607. --bottom right corner
  608. cr:arc(width - radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0, PI * 0.5)
  609. --bottom left corner
  610. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  611. cr:close_path()
  612. cr:fill()
  613. --represent the value
  614. -- value in 0 -> 1
  615. -- radius*rounded_size | height - 2*( radius*rounded) | radius * rounded_size
  616. -- | | |
  617. -- | ____| _______________________|
  618. -- |_______ | |
  619. -- ___ | | |
  620. -- /___\ <- | |
  621. -- | | | |
  622. -- | |<---- |
  623. -- |_____| |
  624. -- \___/<------------
  625. --
  626. --1 => height/ height
  627. --limit_2 => height -radius / height
  628. --limit_1 => radius /height
  629. value = value_to_represent
  630. limit_2 = (height -(radius * rounded_size)) / height
  631. limit_1 = radius* rounded_size /height
  632. --dbg({value, limit_2, limit_1})
  633. r,g,b,a=helpers.hexadecimal_to_rgba_percent(graph_color)
  634. cr:set_source_rgba(r,g,b,a)
  635. if value <= 1 and value > limit_2 then
  636. ratio = (value - limit_2) / (1 - limit_2)
  637. cr:arc(width - radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0 , PI * 0.5)
  638. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  639. cr:arc(x + radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI, PI * (1+(0.5* ratio)) )
  640. cr:arc(width - radius*rounded_size,y + radius*rounded_size, radius*rounded_size,PI*(2 -(0.5* ratio)), PI *2)
  641. cr:close_path()
  642. cr:fill()
  643. elseif value <= limit_2 and value > limit_1 then
  644. ratio = value / limit_2
  645. cr:arc(width - radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0 , PI * 0.5)
  646. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  647. cr:line_to(x,y + height - (height * ratio*limit_2) )
  648. cr:line_to(width,y+ height - (height * ratio*limit_2) )
  649. cr:close_path()
  650. cr:fill()
  651. elseif value <= limit_1 and value > 0 then
  652. ratio = value / limit_1
  653. cr:arc(width - radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*(0.5-( 0.5*ratio)) , PI * 0.5)
  654. cr:arc(x + radius*rounded_size,height - radius*rounded_size, radius*rounded_size,PI*0.5, PI *(0.5+ (0.5*ratio)))
  655. cr:close_path()
  656. cr:fill()
  657. end
  658. if graph_line_color then
  659. r,g,b,a=helpers.hexadecimal_to_rgba_percent(graph_line_color)
  660. cr:set_source_rgba(r,g,b,a)
  661. cr:set_line_width(1)
  662. if value <= 1 and value > limit_2 then
  663. ratio = (value - limit_2) / (1 - limit_2)
  664. cr:arc(width -1 - radius*rounded_size,height -1 - radius*rounded_size, radius*rounded_size,PI*0 , PI * 0.5)
  665. cr:arc(x+1 + radius*rounded_size,height -1 - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  666. cr:arc(x+1 + radius*rounded_size,y+1 + radius*rounded_size, radius*rounded_size,PI, PI * (1+(0.5* ratio)) )
  667. cr:arc(width -1 - radius*rounded_size,y+1 + radius*rounded_size, radius*rounded_size,PI*(2 -(0.5* ratio)), PI *2)
  668. cr:close_path()
  669. cr:stroke()
  670. elseif value <= limit_2 and value > limit_1 then
  671. ratio = value / limit_2
  672. cr:arc(width -1 - radius*rounded_size,height -1 - radius*rounded_size, radius*rounded_size,PI*0 , PI * 0.5)
  673. cr:arc(x+1 + radius*rounded_size,height -1 - radius*rounded_size, radius*rounded_size,PI*0.5, PI * 1)
  674. cr:line_to(x +1 ,y +1 + height - (height * ratio*limit_2) )
  675. cr:line_to(width - 1,y +1 + height - (height * ratio*limit_2) )
  676. cr:close_path()
  677. cr:stroke()
  678. elseif value <= limit_1 and value > 0 then
  679. ratio = value / limit_1
  680. cr:arc(width -1 - radius*rounded_size,height -1 - radius*rounded_size, radius*rounded_size,PI*(0.5-( 0.5*ratio)) , PI * 0.5)
  681. cr:arc(x +1 + radius*rounded_size,height -1 - radius*rounded_size, radius*rounded_size,PI*0.5, PI *(0.5+ (0.5*ratio)))
  682. cr:close_path()
  683. cr:stroke()
  684. end
  685. end
  686. end
  687. end
  688. ---Generate a text in front of a centered rectangle with rounded corners (or not) in a cairo context.
  689. --It returns a table ={ width = the width of the image, height = the height of the image}
  690. --@param cr a cairo context already initialised with oocairo.context_create( )
  691. --@param width the width of the widget
  692. --@param height the height of the widget
  693. --@param text the text to display
  694. --@param padding the left/right/top/bottom padding used to center the text in the background rectangle
  695. --@param background_color a string "#rrggbb" or "#rrggbbaa" for the color of the background rectangle
  696. --@param text_color a string "#rrggbb" or "#rrggbbaa" for the color of the text
  697. --@param font_size define the size of the font
  698. --@param rounded_size a float value from 0 to 1 (0 is no rounded corner)
  699. --@param border a color as a string "#rrggbb" or "#rrggbbaa"
  700. function helpers.generate_rounded_rectangle_with_text(cr, width, height, text, padding, background_color, text_color, font_size, rounded_size, border)
  701. local data={}
  702. local padding = padding or 2
  703. --find the height and width of the image:
  704. cr:set_font_size(font_size)
  705. local ext = cr:text_extents(text)
  706. data.height = (font_size + 2* padding) > height and (font_size + 2* padding) or height
  707. data.width = (ext.width +ext.x_bearing*2 + 2*padding) > width and (ext.width +ext.x_bearing *2 + 2*padding) or width
  708. --draw the background
  709. draw_rounded_corners_rectangle(cr,0,0,data.width, data.height, background_color, rounded_size, border)
  710. --draw the text
  711. cr:move_to((data.width/2) -((ext.width+ext.x_bearing*2)/2), (data.height)/2 + (font_size/2))
  712. r,g,b,a=helpers.hexadecimal_to_rgba_percent(text_color)
  713. cr:set_font_size(font_size)
  714. cr:set_source_rgba(r,g,b,a)
  715. cr:show_text(text)
  716. return data
  717. end
  718. ---Remove an element from a table using key.
  719. --@param hash the table
  720. --@param key the key to remove
  721. function helpers.hash_remove(hash,key)
  722. local element = hash[key]
  723. hash[key] = nil
  724. return element
  725. end
  726. ---Functions for date and calendar
  727. local function is_leap_year(year)
  728. return year % 4 == 0 and (year % 100 ~= 0 or year % 400 == 0)
  729. end
  730. local days_in_m = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
  731. ---Get the number of days in a given month of a year.
  732. --iT returns a number
  733. --@param month the month we focus on ( 1 to 12 )
  734. --@param year a number YYYY used to check if it's a leap year.
  735. function helpers.get_days_in_month(month, year)
  736. if month == 2 and is_leap_year(year) then
  737. return 29
  738. else
  739. return days_in_m[month]
  740. end
  741. end
  742. ---Find the weeks numbers of a given month.
  743. --Implementation as per the ISO 8601 definition (http://en.wikipedia.org/wiki/ISO_week_date)
  744. --Fully compatible with original, returns table with 6 week numbers
  745. --@param month the month
  746. --@param year the year
  747. function helpers.get_ISO8601_weeks_number_of_month(month, year)
  748. local wday = os.date("*t", os.time{year=year,month=month,day=1}).wday-1
  749. wday = wday == 0 and 7 or wday
  750. local yday = os.date("*t", os.time{year=year,month=month,day=1}).yday
  751. local nweeks = is_leap_year(month == 12 and year+1 or year) and 53 or 52 -- Make a correction for leap weeks!
  752. local week = math.floor((yday-wday+10)/7) -- First week of the month
  753. if (week < 1) then week = nweeks+week end
  754. if (week > nweeks) then week = week-nweeks end
  755. local t = {week}
  756. for i = 1, 5 do -- Calculate the next 5 weeks, correct where necessary
  757. t[i+1] = ((week+i) > nweeks and (week+i)-nweeks or week+i)
  758. end
  759. return t
  760. end
  761. ---Get the number of cpu cores
  762. --@return a number
  763. function helpers.get_nb_cores()
  764. local f=io.open("/proc/stat")
  765. local nb=0
  766. for line in f:lines() do
  767. if string.find(line,"cpu%d+%s") then nb = nb + 1 end
  768. end
  769. return nb
  770. end
  771. ---Get the cpu name
  772. --@return a string describing the cpu
  773. function helpers.get_cpu_name()
  774. local file = io.open("/proc/cpuinfo")
  775. for line in file:lines() do
  776. local cpu = string.match(line,"model%sname%s:%s*(.*)")
  777. if cpu then return cpu end
  778. end
  779. end
  780. ---Get all the currently mounted devices
  781. --@return an indexed table from 1 to n, where each element is a table with the "mnt", and "dev" key.
  782. function helpers.get_mounted_devices()
  783. local buf = awful.util.pread("mount")
  784. buf = helpers.split(buf, "\n")
  785. local devices = {}
  786. local n = 0 --devices will be an array of table {dev, mnt} (allowing to keep an alphabetical order on /dev/sxxx
  787. for i=1,#buf do
  788. local dev, mnt = string.match(buf[i],"(/dev/[^%s]*)%s+on%s+([^%s]+).*")
  789. if dev and mnt then
  790. n=n+1
  791. devices[n]={mnt=mnt, dev=dev}
  792. end
  793. end
  794. table.sort(devices, function(a,b) return a.dev < b.dev end)
  795. return devices
  796. end
  797. ---Get the total amount of RAM in kb
  798. --@return a number
  799. function helpers.get_total_mem_in_kb()
  800. local file = io.open("/proc/meminfo")
  801. for line in file:lines() do
  802. local mem = string.match(line,"MemTotal:%s*(%d*)")
  803. if mem then return mem end
  804. end
  805. end
  806. ---Get the input device names
  807. --@return an table with "keyboard" and "mouse" keys
  808. function helpers.get_input_devices()
  809. local file = io.open("/proc/bus/input/devices")
  810. local devices={}
  811. local i= 0
  812. for line in file:lines() do
  813. local name, ev, handlers = nil
  814. if string.match(line,"I:%s") then
  815. devices[#devices+1]={}
  816. end
  817. name=string.match(line,"N:%sName=\"(.*)\"")
  818. if (name) then
  819. devices[#devices].name=name
  820. end
  821. ev=string.match(line,"B:%sEV=(.*)")
  822. if (ev) then
  823. devices[#devices].ev=ev
  824. end
  825. handlers=string.match(line,"H:%sHandlers=(.*)")
  826. if (handlers) then
  827. devices[#devices].handlers=handlers
  828. end
  829. end
  830. local inputs={}
  831. for _,device in ipairs(devices) do
  832. if string.match(device.ev, "120013") then
  833. inputs.keyboard= device.name
  834. end
  835. if device.handlers and string.match(device.handlers, "mouse") then
  836. inputs.mouse=device.name
  837. end
  838. end
  839. file:close()
  840. return inputs
  841. end
  842. ---Get the current graphic card
  843. --@return a string
  844. function helpers.get_graphic_card()
  845. local buf = awful.util.pread("lspci | grep VGA")
  846. local graph_card = string.match(buf,"[^%s]*%s+VGA%s+compatible%s+controller:%s+(.*)")
  847. return graph_card
  848. end
  849. --- Get OS related informations from /etc/os-release
  850. --@return a key/value table
  851. function helpers.get_os_release_informations()
  852. local file = io.open("/etc/os-release")
  853. local infos = {}
  854. for line in file:lines() do
  855. local key,value = string.match(line,"([^%s]+)=%s*\"?([^\"]*)\"?")
  856. if key and value then
  857. infos[#infos +1]={key=key,value = value}
  858. end
  859. end
  860. return infos
  861. end
  862. --- Function used in order to have a tasklist with icons only
  863. --The classical usage of it is:
  864. --awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, mytasklist.buttons,nil,icons_only_tasklist)
  865. function helpers.icons_only_tasklist(w, buttons, label, data, objects)
  866. w:reset()
  867. local l = wibox.layout.fixed.horizontal()
  868. for i, o in ipairs(objects) do
  869. local cache = data[o]
  870. if cache then
  871. ib = cache.ib
  872. else
  873. local common = require("awful.widget.common")
  874. ib = wibox.widget.imagebox()
  875. ib:buttons(common.create_buttons(buttons, o))
  876. data[o] = {
  877. ib = ib
  878. }
  879. end
  880. local text, bg, bg_image, icon = label(o)
  881. ib:set_image(icon)
  882. l:add(ib)
  883. end
  884. w:add(l)
  885. end
  886. return helpers