Compare commits
No commits in common. "screenshots" and "master" have entirely different histories.
screenshot
...
master
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/previous.tar
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[submodule "themes/blueish"]
|
||||||
|
path = themes/blueish
|
||||||
|
url = https://github.com/surferlul/blueish
|
||||||
|
[submodule "vicious"]
|
||||||
|
path = vicious
|
||||||
|
url = https://github.com/vicious-widgets/vicious.git
|
339
LICENSE
Normal file
339
LICENSE
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# My AwesomeWM configurations
|
||||||
|
|
||||||
|
### !!! any images found in this repository are made by others and may be subject to licenses other than the GPL !!!
|
||||||
|
|
||||||
|
### Screenshots (Blur/Tranceparency is not present anymore in the current version of blueish)
|
||||||
|
|
||||||
|
![](https://raw.githubusercontent.com/Surferlul/awesome/screenshots/desktop_1_1.0.png)
|
||||||
|
![](https://raw.githubusercontent.com/Surferlul/awesome/screenshots/desktop_2_1.0.png)
|
5
TODO
Normal file
5
TODO
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Option for animated wallpapers
|
||||||
|
|
||||||
|
Rename theme
|
||||||
|
|
||||||
|
Make menubar clickable
|
Binary file not shown.
Before Width: | Height: | Size: 986 KiB |
Binary file not shown.
Before Width: | Height: | Size: 403 KiB |
23
install.sh
Executable file
23
install.sh
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
deps="7z git tar curl"
|
||||||
|
for i in $deps; do
|
||||||
|
if [ -z "$(command -v $i)" ] ; then
|
||||||
|
echo "Dependency $i NOT met!"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||||
|
CACHE_DIR="/tmp/surferlul_awesome"
|
||||||
|
[ -d "$CACHE_DIR" ] && echo "Removing previous cache" && rm -rf /tmp/surferlul_awesome
|
||||||
|
mkdir $CACHE_DIR
|
||||||
|
echo "Downloading AwesomeWM Configs"
|
||||||
|
git clone https://github.com/Surferlul/awesome.git $CACHE_DIR
|
||||||
|
[ -d "$HOME/.config/awesome" ] && echo "Backing up previous configs" && tar cf $CACHE_DIR/previous.tar $HOME/.config/awesome/ && echo "Removing previous configs" && rm -rf $HOME/.config/awesome/
|
||||||
|
echo "Downloading Vicious widgets for AwesomeWM"
|
||||||
|
git -C $CACHE_DIR submodule init vicious
|
||||||
|
git -C $CACHE_DIR submodule update
|
||||||
|
echo "Moving configs to install location"
|
||||||
|
mv $CACHE_DIR $HOME/.config/awesome
|
||||||
|
[ "$1" != "--preserve-git" ] && echo "Removing git from configs" && rm -rf $HOME/.config/awesome/.git
|
||||||
|
[ ! -d "$HOME/.fonts" ] && echo "Creating ~/.fonts directory" && mkdir $HOME/.fonts
|
||||||
|
[ -z "$(ls $HOME/.fonts | grep '[Ii]ndie.*[Ff]lower')" ] && echo "Indie Flower doesn't exist, installing" && mkdir $CACHE_DIR && curl 'https://google-webfonts-helper.herokuapp.com/api/fonts/indie-flower?download=zip&subsets=latin&variants=regular' > $CACHE_DIR/IndieFlower.zip && 7z e -y $CACHE_DIR/IndieFlower.zip -o$CACHE_DIR/ && cp $CACHE_DIR/*.ttf $HOME/.fonts && rm -rf $CACHE_DIR
|
253
menubar/icon_theme.lua
Normal file
253
menubar/icon_theme.lua
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
---------------------------------------------------------------------------
|
||||||
|
--- (Deprecated) class module for icon lookup for menubar
|
||||||
|
--
|
||||||
|
-- @author Kazunobu Kuriyama
|
||||||
|
-- @copyright 2015 Kazunobu Kuriyama
|
||||||
|
-- @classmod menubar.icon_theme
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- This implementation is based on the specifications:
|
||||||
|
-- Icon Theme Specification 0.12
|
||||||
|
-- http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-0.12.html
|
||||||
|
|
||||||
|
local beautiful = require("beautiful")
|
||||||
|
local gfs = require("gears.filesystem")
|
||||||
|
local GLib = require("lgi").GLib
|
||||||
|
local index_theme = require("menubar.index_theme")
|
||||||
|
|
||||||
|
local ipairs = ipairs
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local string = string
|
||||||
|
local table = table
|
||||||
|
local math = math
|
||||||
|
|
||||||
|
local get_pragmatic_base_directories = function()
|
||||||
|
local dirs = {}
|
||||||
|
|
||||||
|
local dir = GLib.build_filenamev({GLib.get_home_dir(), ".icons"})
|
||||||
|
if gfs.dir_readable(dir) then
|
||||||
|
table.insert(dirs, dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
dir = GLib.build_filenamev({GLib.get_user_data_dir(), "icons"})
|
||||||
|
if gfs.dir_readable(dir) then
|
||||||
|
table.insert(dirs, dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs(GLib.get_system_data_dirs()) do
|
||||||
|
dir = GLib.build_filenamev({v, "icons"})
|
||||||
|
if gfs.dir_readable(dir) then
|
||||||
|
table.insert(dirs, dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local need_usr_share_pixmaps = true
|
||||||
|
for _, v in ipairs(GLib.get_system_data_dirs()) do
|
||||||
|
dir = GLib.build_filenamev({v, "pixmaps"})
|
||||||
|
if gfs.dir_readable(dir) then
|
||||||
|
table.insert(dirs, dir)
|
||||||
|
end
|
||||||
|
if dir == "/usr/share/pixmaps" then
|
||||||
|
need_usr_share_pixmaps = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
dir = "/usr/share/pixmaps"
|
||||||
|
if need_usr_share_pixmaps and gfs.dir_readable(dir) then
|
||||||
|
table.insert(dirs, dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
return dirs
|
||||||
|
end
|
||||||
|
|
||||||
|
local get_default_icon_theme_name = function()
|
||||||
|
local icon_theme_names = { "Adwaita", "gnome", "hicolor" }
|
||||||
|
for _, dir in ipairs(get_pragmatic_base_directories()) do
|
||||||
|
for _, icon_theme_name in ipairs(icon_theme_names) do
|
||||||
|
local filename = string.format("%s/%s/index.theme", dir, icon_theme_name)
|
||||||
|
if gfs.file_readable(filename) then
|
||||||
|
return icon_theme_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return "hicolor"
|
||||||
|
end
|
||||||
|
|
||||||
|
local icon_theme = { mt = {} }
|
||||||
|
|
||||||
|
local index_theme_cache = {}
|
||||||
|
|
||||||
|
--- Class constructor of `icon_theme`
|
||||||
|
-- @deprecated menubar.icon_theme.new
|
||||||
|
-- @tparam string icon_theme_name Internal name of icon theme
|
||||||
|
-- @tparam table base_directories Paths used for lookup
|
||||||
|
-- @treturn table An instance of the class `icon_theme`
|
||||||
|
icon_theme.new = function(icon_theme_name, base_directories)
|
||||||
|
icon_theme_name = icon_theme_name or beautiful.icon_theme or get_default_icon_theme_name()
|
||||||
|
base_directories = base_directories or get_pragmatic_base_directories()
|
||||||
|
|
||||||
|
local self = {}
|
||||||
|
self.icon_theme_name = icon_theme_name
|
||||||
|
self.base_directories = base_directories
|
||||||
|
self.extensions = { "png", "svg", "xpm" }
|
||||||
|
|
||||||
|
-- Instantiate index_theme (cached).
|
||||||
|
if not index_theme_cache[self.icon_theme_name] then
|
||||||
|
index_theme_cache[self.icon_theme_name] = {}
|
||||||
|
end
|
||||||
|
local cache_key = table.concat(self.base_directories, ':')
|
||||||
|
if not index_theme_cache[self.icon_theme_name][cache_key] then
|
||||||
|
index_theme_cache[self.icon_theme_name][cache_key] = index_theme(
|
||||||
|
self.icon_theme_name,
|
||||||
|
self.base_directories)
|
||||||
|
end
|
||||||
|
self.index_theme = index_theme_cache[self.icon_theme_name][cache_key]
|
||||||
|
|
||||||
|
return setmetatable(self, { __index = icon_theme })
|
||||||
|
end
|
||||||
|
|
||||||
|
local directory_matches_size = function(self, subdirectory, icon_size)
|
||||||
|
local kind, size, min_size, max_size, threshold = self.index_theme:get_per_directory_keys(subdirectory)
|
||||||
|
|
||||||
|
if kind == "Fixed" then
|
||||||
|
return icon_size == size
|
||||||
|
elseif kind == "Scalable" then
|
||||||
|
return icon_size >= min_size and icon_size <= max_size
|
||||||
|
elseif kind == "Threshold" then
|
||||||
|
return icon_size >= size - threshold and icon_size <= size + threshold
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local directory_size_distance = function(self, subdirectory, icon_size)
|
||||||
|
local kind, size, min_size, max_size, threshold = self.index_theme:get_per_directory_keys(subdirectory)
|
||||||
|
|
||||||
|
if kind == "Fixed" then
|
||||||
|
return math.abs(icon_size - size)
|
||||||
|
elseif kind == "Scalable" then
|
||||||
|
if icon_size < min_size then
|
||||||
|
return min_size - icon_size
|
||||||
|
elseif icon_size > max_size then
|
||||||
|
return icon_size - max_size
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
elseif kind == "Threshold" then
|
||||||
|
if icon_size < size - threshold then
|
||||||
|
return min_size - icon_size
|
||||||
|
elseif icon_size > size + threshold then
|
||||||
|
return icon_size - max_size
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0xffffffff -- Any large number will do.
|
||||||
|
end
|
||||||
|
|
||||||
|
local lookup_icon = function(self, icon_name, icon_size)
|
||||||
|
local checked_already = {}
|
||||||
|
for _, subdir in ipairs(self.index_theme:get_subdirectories()) do
|
||||||
|
for _, basedir in ipairs(self.base_directories) do
|
||||||
|
for _, ext in ipairs(self.extensions) do
|
||||||
|
if directory_matches_size(self, subdir, icon_size) then
|
||||||
|
local filename = string.format("%s/%s/%s/%s.%s",
|
||||||
|
basedir, self.icon_theme_name, subdir,
|
||||||
|
icon_name, ext)
|
||||||
|
if gfs.file_readable(filename) then
|
||||||
|
return filename
|
||||||
|
else
|
||||||
|
checked_already[filename] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local minimal_size = 0xffffffff -- Any large number will do.
|
||||||
|
local closest_filename = nil
|
||||||
|
for _, subdir in ipairs(self.index_theme:get_subdirectories()) do
|
||||||
|
local dist = directory_size_distance(self, subdir, icon_size)
|
||||||
|
if dist < minimal_size then
|
||||||
|
for _, basedir in ipairs(self.base_directories) do
|
||||||
|
for _, ext in ipairs(self.extensions) do
|
||||||
|
local filename = string.format("%s/%s/%s/%s.%s",
|
||||||
|
basedir, self.icon_theme_name, subdir,
|
||||||
|
icon_name, ext)
|
||||||
|
if not checked_already[filename] then
|
||||||
|
if gfs.file_readable(filename) then
|
||||||
|
closest_filename = filename
|
||||||
|
minimal_size = dist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return closest_filename
|
||||||
|
end
|
||||||
|
|
||||||
|
local find_icon_path_helper -- Gets called recursively.
|
||||||
|
find_icon_path_helper = function(self, icon_name, icon_size)
|
||||||
|
local filename = lookup_icon(self, icon_name, icon_size)
|
||||||
|
if filename then
|
||||||
|
return filename
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, parent in ipairs(self.index_theme:get_inherits()) do
|
||||||
|
local parent_icon_theme = icon_theme(parent, self.base_directories)
|
||||||
|
filename = find_icon_path_helper(parent_icon_theme, icon_name, icon_size)
|
||||||
|
if filename then
|
||||||
|
return filename
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local lookup_fallback_icon = function(self, icon_name)
|
||||||
|
for _, dir in ipairs(self.base_directories) do
|
||||||
|
for _, ext in ipairs(self.extensions) do
|
||||||
|
local filename = string.format("%s/%s.%s",
|
||||||
|
dir,
|
||||||
|
icon_name, ext)
|
||||||
|
if gfs.file_readable(filename) then
|
||||||
|
return filename
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Look up an image file based on a given icon name and/or a preferable size.
|
||||||
|
-- @deprecated menubar.icon_theme:find_icon_path
|
||||||
|
-- @tparam string icon_name Icon name to be looked up
|
||||||
|
-- @tparam number icon_size Prefereable icon size
|
||||||
|
-- @treturn string Absolute path to the icon file, or nil if not found
|
||||||
|
function icon_theme:find_icon_path(icon_name, icon_size)
|
||||||
|
icon_size = icon_size or 16
|
||||||
|
if not icon_name or icon_name == "" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local filename = find_icon_path_helper(self, icon_name, icon_size)
|
||||||
|
if filename then
|
||||||
|
return filename
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.icon_theme_name ~= "hicolor" then
|
||||||
|
filename = find_icon_path_helper(icon_theme("hicolor", self.base_directories), icon_name, icon_size)
|
||||||
|
if filename then
|
||||||
|
return filename
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return lookup_fallback_icon(self, icon_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
icon_theme.mt.__call = function(_, ...)
|
||||||
|
return icon_theme.new(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(icon_theme, icon_theme.mt)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
168
menubar/index_theme.lua
Normal file
168
menubar/index_theme.lua
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
---------------------------------------------------------------------------
|
||||||
|
--- (Deprecated) class module for parsing an index.theme file
|
||||||
|
--
|
||||||
|
-- @author Kazunobu Kuriyama
|
||||||
|
-- @copyright 2015 Kazunobu Kuriyama
|
||||||
|
-- @classmod menubar.index_theme
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- This implementation is based on the specifications:
|
||||||
|
-- Icon Theme Specification 0.12
|
||||||
|
-- http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-0.12.html
|
||||||
|
|
||||||
|
local ipairs = ipairs
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local string = string
|
||||||
|
local table = table
|
||||||
|
local io = io
|
||||||
|
|
||||||
|
-- index.theme groups
|
||||||
|
local ICON_THEME = "Icon Theme"
|
||||||
|
-- index.theme keys
|
||||||
|
local DIRECTORIES = "Directories"
|
||||||
|
local INHERITS = "Inherits"
|
||||||
|
-- per-directory subkeys
|
||||||
|
local TYPE = "Type"
|
||||||
|
local SIZE = "Size"
|
||||||
|
local MINSIZE = "MinSize"
|
||||||
|
local MAXSIZE = "MaxSize"
|
||||||
|
local THRESHOLD = "Threshold"
|
||||||
|
|
||||||
|
local index_theme = { mt = {} }
|
||||||
|
|
||||||
|
--- Class constructor of `index_theme`
|
||||||
|
-- @deprecated menubar.index_theme.new
|
||||||
|
-- @tparam table cls Metatable that will be used. Should always be `index_theme.mt`.
|
||||||
|
-- @tparam string icon_theme_name Internal name of icon theme
|
||||||
|
-- @tparam table base_directories Paths used for lookup
|
||||||
|
-- @treturn table An instance of the class `index_theme`
|
||||||
|
index_theme.new = function(cls, icon_theme_name, base_directories)
|
||||||
|
local self = {}
|
||||||
|
setmetatable(self, { __index = cls })
|
||||||
|
|
||||||
|
-- Initialize the fields
|
||||||
|
self.icon_theme_name = icon_theme_name
|
||||||
|
self.base_directory = nil
|
||||||
|
self[DIRECTORIES] = {}
|
||||||
|
self[INHERITS] = {}
|
||||||
|
self.per_directory_keys = {}
|
||||||
|
|
||||||
|
-- base_directory
|
||||||
|
local basedir = nil
|
||||||
|
local handler = nil
|
||||||
|
for _, dir in ipairs(base_directories) do
|
||||||
|
basedir = dir .. "/" .. self.icon_theme_name
|
||||||
|
handler = io.open(basedir .. "/index.theme", "r")
|
||||||
|
if handler then
|
||||||
|
-- Use the index.theme which is found first.
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not handler then
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
self.base_directory = basedir
|
||||||
|
|
||||||
|
-- Parse index.theme.
|
||||||
|
while true do
|
||||||
|
local line = handler:read()
|
||||||
|
if not line then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local group_header = "^%[(.+)%]$"
|
||||||
|
local group = line:match(group_header)
|
||||||
|
if group then
|
||||||
|
if group == ICON_THEME then
|
||||||
|
while true do
|
||||||
|
local item = handler:read()
|
||||||
|
if not item then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if item:match(group_header) then
|
||||||
|
handler:seek("cur", -string.len(item) - 1)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local k, v = item:match("^(%w+)=(.*)$")
|
||||||
|
if k == DIRECTORIES or k == INHERITS then
|
||||||
|
string.gsub(v, "([^,]+),?", function(match)
|
||||||
|
table.insert(self[k], match)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- This must be a 'per-directory keys' group
|
||||||
|
local keys = {}
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local item = handler:read()
|
||||||
|
if not item then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if item:match(group_header) then
|
||||||
|
handler:seek("cur", -string.len(item) - 1)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local k, v = item:match("^(%w+)=(%w+)$")
|
||||||
|
if k == SIZE or k == MINSIZE or k == MAXSIZE or k == THRESHOLD then
|
||||||
|
keys[k] = tonumber(v)
|
||||||
|
elseif k == TYPE then
|
||||||
|
keys[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Size is a must. Other keys are optional.
|
||||||
|
if keys[SIZE] then
|
||||||
|
-- Set unset keys to the default values.
|
||||||
|
if not keys[TYPE] then keys[TYPE] = THRESHOLD end
|
||||||
|
if not keys[MINSIZE] then keys[MINSIZE] = keys[SIZE] end
|
||||||
|
if not keys[MAXSIZE] then keys[MAXSIZE] = keys[SIZE] end
|
||||||
|
if not keys[THRESHOLD] then keys[THRESHOLD] = 2 end
|
||||||
|
|
||||||
|
self.per_directory_keys[group] = keys
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
handler:close()
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Table of the values of the `Directories` key
|
||||||
|
-- @deprecated menubar.index_theme:get_subdirectories
|
||||||
|
-- @treturn table Values of the `Directories` key
|
||||||
|
index_theme.get_subdirectories = function(self)
|
||||||
|
return self[DIRECTORIES]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Table of the values of the `Inherits` key
|
||||||
|
-- @deprecated menubar.index_theme:get_inherits
|
||||||
|
-- @treturn table Values of the `Inherits` key
|
||||||
|
index_theme.get_inherits = function(self)
|
||||||
|
return self[INHERITS]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Query (part of) per-directory keys of a given subdirectory name.
|
||||||
|
-- @deprecated menubar.index_theme:get_per_directory_keys
|
||||||
|
-- @tparam table subdirectory Icon theme's subdirectory
|
||||||
|
-- @treturn string Value of the `Type` key
|
||||||
|
-- @treturn number Value of the `Size` key
|
||||||
|
-- @treturn number VAlue of the `MinSize` key
|
||||||
|
-- @treturn number Value of the `MaxSize` key
|
||||||
|
-- @treturn number Value of the `Threshold` key
|
||||||
|
function index_theme:get_per_directory_keys(subdirectory)
|
||||||
|
local keys = self.per_directory_keys[subdirectory]
|
||||||
|
return keys[TYPE], keys[SIZE], keys[MINSIZE], keys[MAXSIZE], keys[THRESHOLD]
|
||||||
|
end
|
||||||
|
|
||||||
|
index_theme.mt.__call = function(cls, icon_theme_name, base_directories)
|
||||||
|
return index_theme.new(cls, icon_theme_name, base_directories)
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(index_theme, index_theme.mt)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
607
menubar/init.lua
Normal file
607
menubar/init.lua
Normal file
@ -0,0 +1,607 @@
|
|||||||
|
---------------------------------------------------------------------------
|
||||||
|
--- Menubar module, which aims to provide a freedesktop menu alternative.
|
||||||
|
--
|
||||||
|
-- List of menubar keybindings:
|
||||||
|
-- ---
|
||||||
|
--
|
||||||
|
-- <table class='widget_list' border=1>
|
||||||
|
-- <tr style='font-weight: bold;'>
|
||||||
|
-- <th align='center'>Keybinding</th>
|
||||||
|
-- <th align='center'>Description</th>
|
||||||
|
-- </tr> </td></tr>
|
||||||
|
-- <tr><td><kbd>Left</kbd><kbd>C-j</kbd></td><td> select an item on the left </td></tr>
|
||||||
|
-- <tr><td><kbd>Right</kbd><kbd>C-k</kbd></td><td> select an item on the right </td></tr>
|
||||||
|
-- <tr><td><kbd>Backspace </kbd></td><td> exit the current category if we are in any </td></tr>
|
||||||
|
-- <tr><td><kbd>Escape </kbd></td><td> exit the current directory or exit menubar </td></tr>
|
||||||
|
-- <tr><td><kbd>Home </kbd></td><td> select the first item </td></tr>
|
||||||
|
-- <tr><td><kbd>End </kbd></td><td> select the last </td></tr>
|
||||||
|
-- <tr><td><kbd>Return </kbd></td><td> execute the entry </td></tr>
|
||||||
|
-- <tr><td><kbd>C-Return </kbd></td><td> execute the command with awful.spawn </td></tr>
|
||||||
|
-- <tr><td><kbd>C-M-Return </kbd></td><td> execute the command in a terminal </td></tr>
|
||||||
|
-- </table>
|
||||||
|
--
|
||||||
|
-- @author Alexander Yakushev <yakushev.alex@gmail.com>
|
||||||
|
-- @copyright 2011-2012 Alexander Yakushev
|
||||||
|
-- @popupmod menubar
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Grab environment we need
|
||||||
|
local capi = {
|
||||||
|
client = client,
|
||||||
|
mouse = mouse,
|
||||||
|
screen = screen
|
||||||
|
}
|
||||||
|
local gmath = require("gears.math")
|
||||||
|
local awful = require("awful")
|
||||||
|
local gfs = require("gears.filesystem")
|
||||||
|
local common = require("awful.widget.common")
|
||||||
|
local theme = require("beautiful")
|
||||||
|
local wibox = require("wibox")
|
||||||
|
local gcolor = require("gears.color")
|
||||||
|
local gstring = require("gears.string")
|
||||||
|
local gdebug = require("gears.debug")
|
||||||
|
|
||||||
|
local function get_screen(s)
|
||||||
|
return s and capi.screen[s]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Menubar normal text color.
|
||||||
|
-- @beautiful beautiful.menubar_fg_normal
|
||||||
|
-- @param color
|
||||||
|
|
||||||
|
--- Menubar normal background color.
|
||||||
|
-- @beautiful beautiful.menubar_bg_normal
|
||||||
|
-- @param color
|
||||||
|
|
||||||
|
--- Menubar border width.
|
||||||
|
-- @beautiful beautiful.menubar_border_width
|
||||||
|
-- @tparam[opt=0] number menubar_border_width
|
||||||
|
|
||||||
|
--- Menubar border color.
|
||||||
|
-- @beautiful beautiful.menubar_border_color
|
||||||
|
-- @param color
|
||||||
|
|
||||||
|
--- Menubar selected item text color.
|
||||||
|
-- @beautiful beautiful.menubar_fg_focus
|
||||||
|
-- @param color
|
||||||
|
|
||||||
|
--- Menubar selected item background color.
|
||||||
|
-- @beautiful beautiful.menubar_bg_focus
|
||||||
|
-- @param color
|
||||||
|
|
||||||
|
--- Menubar font.
|
||||||
|
-- @beautiful beautiful.menubar_font
|
||||||
|
-- @param[opt=beautiful.font] font
|
||||||
|
|
||||||
|
|
||||||
|
-- menubar
|
||||||
|
local menubar = { menu_entries = {} }
|
||||||
|
menubar.menu_gen = require("menubar.menu_gen")
|
||||||
|
menubar.utils = require("menubar.utils")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local current_page = {}
|
||||||
|
-- Options section
|
||||||
|
|
||||||
|
--- When true the .desktop files will be reparsed only when the
|
||||||
|
-- extension is initialized. Use this if menubar takes much time to
|
||||||
|
-- open.
|
||||||
|
-- @tfield[opt=true] boolean cache_entries
|
||||||
|
menubar.cache_entries = true
|
||||||
|
|
||||||
|
--- When true the categories will be shown alongside application
|
||||||
|
-- entries.
|
||||||
|
-- @tfield[opt=true] boolean show_categories
|
||||||
|
menubar.show_categories = true
|
||||||
|
|
||||||
|
--- When false will hide results if the current query is empty
|
||||||
|
-- @tfield[opt=true] boolean match_empty
|
||||||
|
menubar.match_empty = true
|
||||||
|
|
||||||
|
--- Specifies the geometry of the menubar. This is a table with the keys
|
||||||
|
-- x, y, width and height. Missing values are replaced via the screen's
|
||||||
|
-- geometry. However, missing height is replaced by the font size.
|
||||||
|
-- @table geometry
|
||||||
|
-- @tfield number geometry.x A forced horizontal position
|
||||||
|
-- @tfield number geometry.y A forced vertical position
|
||||||
|
-- @tfield number geometry.width A forced width
|
||||||
|
-- @tfield number geometry.height A forced height
|
||||||
|
menubar.geometry = { width = nil,
|
||||||
|
height = nil,
|
||||||
|
x = nil,
|
||||||
|
y = nil }
|
||||||
|
|
||||||
|
menubar.geometry_override = { width = nil,
|
||||||
|
height = nil,
|
||||||
|
x = nil,
|
||||||
|
y = nil }
|
||||||
|
--- Width of blank space left in the right side.
|
||||||
|
-- @tfield number right_margin
|
||||||
|
menubar.right_margin = theme.xresources.apply_dpi(8)
|
||||||
|
|
||||||
|
--- Label used for "Next page", default "▶▶".
|
||||||
|
-- @tfield[opt="▶▶"] string right_label
|
||||||
|
menubar.right_label = "▶▶ "
|
||||||
|
|
||||||
|
--- Label used for "Previous page", default "◀◀".
|
||||||
|
-- @tfield[opt="◀◀"] string left_label
|
||||||
|
menubar.left_label = "◀◀ "
|
||||||
|
|
||||||
|
-- awful.widget.common.list_update adds spacing of dpi(4) between items.
|
||||||
|
-- @tfield number list_spacing
|
||||||
|
local list_spacing = theme.xresources.apply_dpi(4)
|
||||||
|
|
||||||
|
--- Allows user to specify custom parameters for prompt.run function
|
||||||
|
-- (like colors). This will merge with the default parameters, overriding affected values.
|
||||||
|
-- @see awful.prompt
|
||||||
|
menubar.prompt_args = {}
|
||||||
|
|
||||||
|
-- Private section
|
||||||
|
local current_item = 1
|
||||||
|
local previous_item = nil
|
||||||
|
local current_category = nil
|
||||||
|
local shownitems = nil
|
||||||
|
local instance = nil
|
||||||
|
|
||||||
|
local common_args = { w = wibox.layout.fixed.vertical(),
|
||||||
|
data = setmetatable({}, { __mode = 'kv' }) }
|
||||||
|
|
||||||
|
--- Wrap the text with the color span tag.
|
||||||
|
-- @param s The text.
|
||||||
|
-- @param c The desired text color.
|
||||||
|
-- @return the text wrapped in a span tag.
|
||||||
|
local function colortext(s, c)
|
||||||
|
return "<span color='" .. gcolor.ensure_pango_color(c) .. "'>" .. s .. "</span>"
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get how the menu item should be displayed.
|
||||||
|
-- @param o The menu item.
|
||||||
|
-- @return item name, item background color, background image, item icon, item args.
|
||||||
|
local function label(o)
|
||||||
|
local fg_color = theme.menubar_fg_normal or theme.menu_fg_normal or theme.fg_normal
|
||||||
|
local bg_color = theme.menubar_tag_bg_normal or theme.menu_tag_bg_normal or theme.tag_bg_normal or "#00000000" -- option to set tag background color different than background color. Needed because the normal way would make tags darker than the background anyways -> was annoying
|
||||||
|
if o.focused then
|
||||||
|
fg_color = theme.menubar_fg_focus or theme.menu_fg_focus or theme.fg_focus
|
||||||
|
bg_color = theme.menubar_bg_focus or theme.menu_bg_focus or theme.bg_focus
|
||||||
|
end
|
||||||
|
return colortext(gstring.xml_escape(o.name), fg_color),
|
||||||
|
bg_color,
|
||||||
|
nil,
|
||||||
|
o.icon,
|
||||||
|
o.icon and {icon_size=20} -- TODO: dirty fix
|
||||||
|
end
|
||||||
|
|
||||||
|
local function load_count_table()
|
||||||
|
if instance.count_table then
|
||||||
|
return instance.count_table
|
||||||
|
end
|
||||||
|
instance.count_table = {}
|
||||||
|
local count_file_name = gfs.get_cache_dir() .. "/menu_count_file"
|
||||||
|
local count_file = io.open (count_file_name, "r")
|
||||||
|
if count_file then
|
||||||
|
for line in count_file:lines() do
|
||||||
|
local name, count = string.match(line, "([^;]+);([^;]+)")
|
||||||
|
if name ~= nil and count ~= nil then
|
||||||
|
instance.count_table[name] = count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
count_file:close()
|
||||||
|
end
|
||||||
|
return instance.count_table
|
||||||
|
end
|
||||||
|
|
||||||
|
local function write_count_table(count_table)
|
||||||
|
count_table = count_table or instance.count_table
|
||||||
|
local count_file_name = gfs.get_cache_dir() .. "/menu_count_file"
|
||||||
|
local count_file = assert(io.open(count_file_name, "w"))
|
||||||
|
for name, count in pairs(count_table) do
|
||||||
|
local str = string.format("%s;%d\n", name, count)
|
||||||
|
count_file:write(str)
|
||||||
|
end
|
||||||
|
count_file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Perform an action for the given menu item.
|
||||||
|
-- @param o The menu item.
|
||||||
|
-- @return if the function processed the callback, new awful.prompt command, new awful.prompt prompt text.
|
||||||
|
local function perform_action(o)
|
||||||
|
if not o then return end
|
||||||
|
if o.key then
|
||||||
|
current_category = o.key
|
||||||
|
local new_prompt = shownitems[current_item].name .. ": "
|
||||||
|
previous_item = current_item
|
||||||
|
current_item = 1
|
||||||
|
return true, "", new_prompt
|
||||||
|
elseif shownitems[current_item].cmdline then
|
||||||
|
awful.spawn(shownitems[current_item].cmdline)
|
||||||
|
-- load count_table from cache file
|
||||||
|
local count_table = load_count_table()
|
||||||
|
-- increase count
|
||||||
|
local curname = shownitems[current_item].name
|
||||||
|
count_table[curname] = (count_table[curname] or 0) + 1
|
||||||
|
-- write updated count table to cache file
|
||||||
|
write_count_table(count_table)
|
||||||
|
-- Let awful.prompt execute dummy exec_callback and
|
||||||
|
-- done_callback to stop the keygrabber properly.
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cut item list to return only current page.
|
||||||
|
-- @tparam table all_items All items list.
|
||||||
|
-- @tparam str query Search query.
|
||||||
|
-- @tparam number|screen scr Screen
|
||||||
|
-- @return table List of items for current page.
|
||||||
|
local function get_current_page(all_items, query, scr)
|
||||||
|
|
||||||
|
local compute_text_width = function(text, s)
|
||||||
|
return wibox.widget.textbox.get_markup_geometry(text, s, instance.font)['width']
|
||||||
|
end
|
||||||
|
|
||||||
|
scr = get_screen(scr)
|
||||||
|
if not instance.prompt.width then
|
||||||
|
instance.prompt.width = compute_text_width(instance.prompt.prompt, scr)
|
||||||
|
end
|
||||||
|
if not menubar.left_label_width then
|
||||||
|
menubar.left_label_width = compute_text_width(menubar.left_label, scr)
|
||||||
|
end
|
||||||
|
if not menubar.right_label_width then
|
||||||
|
menubar.right_label_width = compute_text_width(menubar.right_label, scr)
|
||||||
|
end
|
||||||
|
local border_width = theme.menubar_border_width or theme.menu_border_width or 0
|
||||||
|
--local available_space = instance.geometry.width - menubar.right_margin -
|
||||||
|
-- menubar.right_label_width - menubar.left_label_width -
|
||||||
|
-- compute_text_width(query..' ', scr) - instance.prompt.width - border_width * 2
|
||||||
|
-- space character is added as input cursor placeholder
|
||||||
|
local extra_width = menubar.left_label_width
|
||||||
|
local subtracted = false
|
||||||
|
local item_height = 24
|
||||||
|
local available_space = menubar.geometry.height - item_height * 2 -- TODO: dirty fix
|
||||||
|
local width_sum = 0
|
||||||
|
current_page = {}
|
||||||
|
for i, item in ipairs(all_items) do
|
||||||
|
item.width = item.width or (
|
||||||
|
compute_text_width(label(item), scr) +
|
||||||
|
(item.icon and (item_height + list_spacing) or 0)
|
||||||
|
) -- TODO: 20 = dirty fix
|
||||||
|
local total_height = item_height * math.floor(item.width / menubar.geometry.width + 1)
|
||||||
|
if width_sum + total_height > available_space then -- TODO: 20 = dirty fix
|
||||||
|
if current_item < i then
|
||||||
|
table.insert(current_page, { name = menubar.right_label, ncon = nil })
|
||||||
|
break
|
||||||
|
end
|
||||||
|
current_page = { { name = menubar.left_label, icon = nil }, item, }
|
||||||
|
width_sum = total_height * 2 -- TODO: 20 = dirty fix
|
||||||
|
else
|
||||||
|
table.insert(current_page, item)
|
||||||
|
width_sum = width_sum + total_height -- TODO: 20 = dirty fix
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return current_page
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update the menubar according to the command entered by user.
|
||||||
|
-- @tparam number|screen scr Screen
|
||||||
|
local function menulist_update(scr)
|
||||||
|
local query = instance.query or ""
|
||||||
|
shownitems = {}
|
||||||
|
local pattern = gstring.query_to_pattern(query)
|
||||||
|
|
||||||
|
-- All entries are added to a list that will be sorted
|
||||||
|
-- according to the priority (first) and weight (second) of its
|
||||||
|
-- entries.
|
||||||
|
-- If categories are used in the menu, we add the entries matching
|
||||||
|
-- the current query with high priority as to ensure they are
|
||||||
|
-- displayed first. Afterwards the non-category entries are added.
|
||||||
|
-- All entries are weighted according to the number of times they
|
||||||
|
-- have been executed previously (stored in count_table).
|
||||||
|
local count_table = load_count_table()
|
||||||
|
local command_list = {}
|
||||||
|
|
||||||
|
local PRIO_NONE = 0
|
||||||
|
local PRIO_CATEGORY_MATCH = 2
|
||||||
|
|
||||||
|
-- Add the categories
|
||||||
|
if menubar.show_categories then
|
||||||
|
for _, v in pairs(menubar.menu_gen.all_categories) do
|
||||||
|
v.focused = false
|
||||||
|
if not current_category and v.use then
|
||||||
|
|
||||||
|
-- check if current query matches a category
|
||||||
|
if string.match(v.name, pattern) then
|
||||||
|
|
||||||
|
v.weight = 0
|
||||||
|
v.prio = PRIO_CATEGORY_MATCH
|
||||||
|
|
||||||
|
-- get use count from count_table if present
|
||||||
|
-- and use it as weight
|
||||||
|
if string.len(pattern) > 0 and count_table[v.name] ~= nil then
|
||||||
|
v.weight = tonumber(count_table[v.name])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for prefix match
|
||||||
|
if string.match(v.name, "^" .. pattern) then
|
||||||
|
-- increase default priority
|
||||||
|
v.prio = PRIO_CATEGORY_MATCH + 1
|
||||||
|
else
|
||||||
|
v.prio = PRIO_CATEGORY_MATCH
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert (command_list, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add the applications according to their name and cmdline
|
||||||
|
local add_entry = function(entry)
|
||||||
|
entry.focused = false
|
||||||
|
if not current_category or entry.category == current_category then
|
||||||
|
|
||||||
|
-- check if the query matches either the name or the commandline
|
||||||
|
-- of some entry
|
||||||
|
if string.match(entry.name, pattern)
|
||||||
|
or string.match(entry.cmdline, pattern) then
|
||||||
|
|
||||||
|
entry.weight = 0
|
||||||
|
entry.prio = PRIO_NONE
|
||||||
|
|
||||||
|
-- get use count from count_table if present
|
||||||
|
-- and use it as weight
|
||||||
|
if string.len(pattern) > 0 and count_table[entry.name] ~= nil then
|
||||||
|
entry.weight = tonumber(count_table[entry.name])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for prefix match
|
||||||
|
if string.match(entry.name, "^" .. pattern)
|
||||||
|
or string.match(entry.cmdline, "^" .. pattern) then
|
||||||
|
-- increase default priority
|
||||||
|
entry.prio = PRIO_NONE + 1
|
||||||
|
else
|
||||||
|
entry.prio = PRIO_NONE
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert (command_list, entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add entries if required
|
||||||
|
if query ~= "" or menubar.match_empty then
|
||||||
|
for _, v in ipairs(menubar.menu_entries) do
|
||||||
|
add_entry(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function compare_counts(a, b)
|
||||||
|
if a.prio == b.prio then
|
||||||
|
return a.weight > b.weight
|
||||||
|
end
|
||||||
|
return a.prio > b.prio
|
||||||
|
end
|
||||||
|
|
||||||
|
-- sort command_list by weight (highest first)
|
||||||
|
table.sort(command_list, compare_counts)
|
||||||
|
-- copy into showitems
|
||||||
|
shownitems = command_list
|
||||||
|
|
||||||
|
if #shownitems > 0 then
|
||||||
|
-- Insert a run item value as the last choice
|
||||||
|
table.insert(shownitems, { name = "Exec: " .. query, cmdline = query, icon = nil })
|
||||||
|
|
||||||
|
if current_item > #shownitems then
|
||||||
|
current_item = #shownitems
|
||||||
|
end
|
||||||
|
shownitems[current_item].focused = true
|
||||||
|
else
|
||||||
|
table.insert(shownitems, { name = "", cmdline = query, icon = nil })
|
||||||
|
end
|
||||||
|
|
||||||
|
common.list_update(common_args.w, nil, label,
|
||||||
|
common_args.data,
|
||||||
|
get_current_page(shownitems, query, scr))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Refresh menubar's cache by reloading .desktop files.
|
||||||
|
-- @tparam[opt] screen scr Screen.
|
||||||
|
-- @staticfct menubar.refresh
|
||||||
|
function menubar.refresh(scr)
|
||||||
|
scr = get_screen(scr or awful.screen.focused() or 1)
|
||||||
|
menubar.menu_gen.generate(function(entries)
|
||||||
|
menubar.menu_entries = entries
|
||||||
|
if instance then
|
||||||
|
menulist_update(scr)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Awful.prompt keypressed callback to be used when the user presses a key.
|
||||||
|
-- @param mod Table of key combination modifiers (Control, Shift).
|
||||||
|
-- @param key The key that was pressed.
|
||||||
|
-- @param comm The current command in the prompt.
|
||||||
|
-- @return if the function processed the callback, new awful.prompt command, new awful.prompt prompt text.
|
||||||
|
local function prompt_keypressed_callback(mod, key, comm)
|
||||||
|
if key == "Up" or (mod.Control and key == "j") then
|
||||||
|
current_item = math.max(current_item - 1, 1)
|
||||||
|
return true
|
||||||
|
elseif key == "Down" or (mod.Control and key == "k") then
|
||||||
|
current_item = current_item + 1
|
||||||
|
return true
|
||||||
|
elseif key == "Left" or key == "Right" then
|
||||||
|
local tmp_sum = 0
|
||||||
|
local index = nil
|
||||||
|
local index_gen = function(tbl)
|
||||||
|
local idx = {}
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
idx[v] = k
|
||||||
|
end
|
||||||
|
return idx
|
||||||
|
end
|
||||||
|
if current_page[1]["name"] == menubar.left_label then
|
||||||
|
tmp_sum = 1
|
||||||
|
if key == "Left" then
|
||||||
|
if not index then
|
||||||
|
index = index_gen(current_page)
|
||||||
|
end
|
||||||
|
tmp_sum = #current_page - index[shownitems[current_item]]
|
||||||
|
end
|
||||||
|
elseif key == "Left" then
|
||||||
|
tmp_sum = #current_page - current_item
|
||||||
|
end
|
||||||
|
tmp_sum = tmp_sum + 1
|
||||||
|
if current_page[#current_page]["name"] == menubar.right_label and key == "Right" then
|
||||||
|
if not index then
|
||||||
|
index = index_gen(current_page)
|
||||||
|
end
|
||||||
|
tmp_sum = index[shownitems[current_item]]
|
||||||
|
end
|
||||||
|
current_item = current_item + (#current_page - tmp_sum) * (key == "Right" and 1 or -1)
|
||||||
|
return true
|
||||||
|
elseif key == "BackSpace" then
|
||||||
|
if comm == "" and current_category then
|
||||||
|
current_category = nil
|
||||||
|
current_item = previous_item
|
||||||
|
return true, nil, "Run: "
|
||||||
|
end
|
||||||
|
elseif key == "Escape" then
|
||||||
|
if current_category then
|
||||||
|
current_category = nil
|
||||||
|
current_item = previous_item
|
||||||
|
return true, nil, "Run: "
|
||||||
|
end
|
||||||
|
elseif key == "Home" then
|
||||||
|
current_item = 1
|
||||||
|
return true
|
||||||
|
elseif key == "End" then
|
||||||
|
current_item = #shownitems
|
||||||
|
return true
|
||||||
|
elseif key == "Return" or key == "KP_Enter" then
|
||||||
|
if mod.Control then
|
||||||
|
current_item = #shownitems
|
||||||
|
if mod.Mod1 then
|
||||||
|
-- add a terminal to the cmdline
|
||||||
|
shownitems[current_item].cmdline = menubar.utils.terminal
|
||||||
|
.. " -e " .. shownitems[current_item].cmdline
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return perform_action(shownitems[current_item])
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Show the menubar on the given screen.
|
||||||
|
-- @param[opt] scr Screen.
|
||||||
|
-- @staticfct menubar.show
|
||||||
|
function menubar.show(scr)
|
||||||
|
scr = get_screen(scr or awful.screen.focused() or 1)
|
||||||
|
local fg_color = theme.menubar_fg_normal or theme.menu_fg_normal or theme.fg_normal
|
||||||
|
local bg_color = theme.menubar_bg_normal or theme.menu_bg_normal or theme.bg_normal
|
||||||
|
local border_width = theme.menubar_border_width or theme.menu_border_width or 0
|
||||||
|
local border_color = theme.menubar_border_color or theme.menu_border_color
|
||||||
|
local font = theme.menubar_font or theme.font or "Monospace 10"
|
||||||
|
|
||||||
|
if not instance then
|
||||||
|
-- Add to each category the name of its key in all_categories
|
||||||
|
for k, v in pairs(menubar.menu_gen.all_categories) do
|
||||||
|
v.key = k
|
||||||
|
end
|
||||||
|
|
||||||
|
if menubar.cache_entries then
|
||||||
|
menubar.refresh(scr)
|
||||||
|
end
|
||||||
|
|
||||||
|
instance = {
|
||||||
|
wibox = wibox{
|
||||||
|
ontop = true,
|
||||||
|
bg = bg_color,
|
||||||
|
fg = fg_color,
|
||||||
|
border_width = border_width,
|
||||||
|
border_color = border_color,
|
||||||
|
font = font,
|
||||||
|
},
|
||||||
|
widget = common_args.w,
|
||||||
|
prompt = awful.widget.prompt(),
|
||||||
|
query = nil,
|
||||||
|
count_table = nil,
|
||||||
|
font = font,
|
||||||
|
}
|
||||||
|
local layout = wibox.layout.fixed.vertical()
|
||||||
|
layout:add(instance.prompt)
|
||||||
|
layout:add(instance.widget)
|
||||||
|
instance.wibox:set_widget(layout)
|
||||||
|
end
|
||||||
|
|
||||||
|
if instance.wibox.visible then -- Menu already shown, exit
|
||||||
|
return
|
||||||
|
elseif not menubar.cache_entries then
|
||||||
|
menubar.refresh(scr)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set position and size
|
||||||
|
local scrgeom = scr.workarea
|
||||||
|
menubar.geometry = { height = menubar.geometry_override.height or awful.screen.focused().geometry.height, width = menubar.geometry_override.width or 200, x = (menubar.geometry_override.x or 0) + awful.screen.focused().geometry.x , y = menubar.geometry_override.y}
|
||||||
|
local geometry = menubar.geometry
|
||||||
|
instance.geometry = {x = geometry.x or scrgeom.x,
|
||||||
|
y = geometry.y or scrgeom.y,
|
||||||
|
height = geometry.height or gmath.round(theme.get_font_height(font) * 1.5),
|
||||||
|
width = (geometry.width or scrgeom.width) - border_width * 2}
|
||||||
|
instance.wibox:geometry(instance.geometry)
|
||||||
|
|
||||||
|
current_item = 1
|
||||||
|
current_category = nil
|
||||||
|
menulist_update(scr)
|
||||||
|
|
||||||
|
local default_prompt_args = {
|
||||||
|
prompt = "Run: ",
|
||||||
|
textbox = instance.prompt.widget,
|
||||||
|
completion_callback = awful.completion.shell,
|
||||||
|
history_path = gfs.get_cache_dir() .. "/history_menu",
|
||||||
|
done_callback = menubar.hide,
|
||||||
|
changed_callback = function(query)
|
||||||
|
instance.query = query
|
||||||
|
menulist_update(scr)
|
||||||
|
end,
|
||||||
|
keypressed_callback = prompt_keypressed_callback
|
||||||
|
}
|
||||||
|
default_prompt_args.textbox.forced_height = 18
|
||||||
|
|
||||||
|
awful.prompt.run(setmetatable(menubar.prompt_args, {__index=default_prompt_args}))
|
||||||
|
|
||||||
|
|
||||||
|
instance.wibox.visible = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Hide the menubar.
|
||||||
|
-- @staticfct menubar.hide
|
||||||
|
function menubar.hide()
|
||||||
|
if instance then
|
||||||
|
instance.wibox.visible = false
|
||||||
|
instance.query = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a menubar wibox.
|
||||||
|
-- @tparam[opt] screen scr Screen.
|
||||||
|
-- @return menubar wibox.
|
||||||
|
-- @deprecated get
|
||||||
|
function menubar.get(scr)
|
||||||
|
gdebug.deprecate("Use menubar.show() instead", { deprecated_in = 5 })
|
||||||
|
menubar.refresh(scr)
|
||||||
|
-- Add to each category the name of its key in all_categories
|
||||||
|
for k, v in pairs(menubar.menu_gen.all_categories) do
|
||||||
|
v.key = k
|
||||||
|
end
|
||||||
|
return common_args.w
|
||||||
|
end
|
||||||
|
|
||||||
|
local mt = {}
|
||||||
|
function mt.__call(_, ...)
|
||||||
|
return menubar.get(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(menubar, mt)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
136
menubar/menu_gen.lua
Normal file
136
menubar/menu_gen.lua
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
---------------------------------------------------------------------------
|
||||||
|
--- Menu generation module for menubar
|
||||||
|
--
|
||||||
|
-- @author Antonio Terceiro
|
||||||
|
-- @copyright 2009, 2011-2012 Antonio Terceiro, Alexander Yakushev
|
||||||
|
-- @module menubar.menu_gen
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Grab environment
|
||||||
|
local gtable = require("gears.table")
|
||||||
|
local gfilesystem = require("gears.filesystem")
|
||||||
|
local utils = require("menubar.utils")
|
||||||
|
local pairs = pairs
|
||||||
|
local ipairs = ipairs
|
||||||
|
local table = table
|
||||||
|
|
||||||
|
local menu_gen = {}
|
||||||
|
|
||||||
|
-- Options section
|
||||||
|
|
||||||
|
--- Get the path to the directories where XDG menu applications are installed.
|
||||||
|
local function get_xdg_menu_dirs()
|
||||||
|
local dirs = gfilesystem.get_xdg_data_dirs()
|
||||||
|
table.insert(dirs, 1, gfilesystem.get_xdg_data_home())
|
||||||
|
return gtable.map(function(dir) return dir .. 'applications/' end, dirs)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Specifies all directories where menubar should look for .desktop
|
||||||
|
-- files. The search is recursive.
|
||||||
|
menu_gen.all_menu_dirs = get_xdg_menu_dirs()
|
||||||
|
|
||||||
|
--- Specify the mapping of .desktop Categories section to the
|
||||||
|
-- categories in the menubar. If "use" flag is set to false then any of
|
||||||
|
-- the applications that fall only to this category will not be shown.
|
||||||
|
menu_gen.all_categories = {
|
||||||
|
multimedia = { app_type = "AudioVideo", name = "Multimedia",
|
||||||
|
icon_name = "applications-multimedia", use = true },
|
||||||
|
development = { app_type = "Development", name = "Development",
|
||||||
|
icon_name = "applications-development", use = true },
|
||||||
|
education = { app_type = "Education", name = "Education",
|
||||||
|
icon_name = "applications-science", use = true },
|
||||||
|
games = { app_type = "Game", name = "Games",
|
||||||
|
icon_name = "applications-games", use = true },
|
||||||
|
graphics = { app_type = "Graphics", name = "Graphics",
|
||||||
|
icon_name = "applications-graphics", use = true },
|
||||||
|
office = { app_type = "Office", name = "Office",
|
||||||
|
icon_name = "applications-office", use = true },
|
||||||
|
internet = { app_type = "Network", name = "Internet",
|
||||||
|
icon_name = "applications-internet", use = true },
|
||||||
|
science = { app_type = "Science", name="Science",
|
||||||
|
icon_name = "applications-science", use = true },
|
||||||
|
settings = { app_type = "Settings", name = "Settings",
|
||||||
|
icon_name = "applications-utilities", use = true },
|
||||||
|
tools = { app_type = "System", name = "System Tools",
|
||||||
|
icon_name = "applications-system", use = true },
|
||||||
|
utility = { app_type = "Utility", name = "Accessories",
|
||||||
|
icon_name = "applications-accessories", use = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Find icons for category entries.
|
||||||
|
-- @staticfct menubar.menu_gen.lookup_category_icons
|
||||||
|
function menu_gen.lookup_category_icons()
|
||||||
|
for _, v in pairs(menu_gen.all_categories) do
|
||||||
|
v.icon = utils.lookup_icon(v.icon_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get category key name and whether it is used by its app_type.
|
||||||
|
-- @param app_type Application category as written in .desktop file.
|
||||||
|
-- @return category key name in all_categories, whether the category is used
|
||||||
|
local function get_category_name_and_usage_by_type(app_type)
|
||||||
|
for k, v in pairs(menu_gen.all_categories) do
|
||||||
|
if app_type == v.app_type then
|
||||||
|
return k, v.use
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Generate an array of all visible menu entries.
|
||||||
|
-- @tparam function callback Will be fired when all menu entries were parsed
|
||||||
|
-- with the resulting list of menu entries as argument.
|
||||||
|
-- @tparam table callback.entries All menu entries.
|
||||||
|
-- @staticfct menubar.menu_gen.generate
|
||||||
|
function menu_gen.generate(callback)
|
||||||
|
-- Update icons for category entries
|
||||||
|
menu_gen.lookup_category_icons()
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
local unique_entries = {}
|
||||||
|
local dirs_parsed = 0
|
||||||
|
|
||||||
|
for _, dir in ipairs(menu_gen.all_menu_dirs) do
|
||||||
|
utils.parse_dir(dir, function(entries)
|
||||||
|
entries = entries or {}
|
||||||
|
for _, entry in ipairs(entries) do
|
||||||
|
-- Check whether to include program in the menu
|
||||||
|
if entry.show and entry.Name and entry.cmdline then
|
||||||
|
local unique_key = entry.Name .. '\0' .. entry.cmdline
|
||||||
|
if not unique_entries[unique_key] then
|
||||||
|
local target_category = nil
|
||||||
|
-- Check if the program falls into at least one of the
|
||||||
|
-- usable categories. Set target_category to be the id
|
||||||
|
-- of the first category it finds.
|
||||||
|
if entry.categories then
|
||||||
|
for _, category in pairs(entry.categories) do
|
||||||
|
local cat_key, cat_use =
|
||||||
|
get_category_name_and_usage_by_type(category)
|
||||||
|
if cat_key and cat_use then
|
||||||
|
target_category = cat_key
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = utils.rtrim(entry.Name) or ""
|
||||||
|
local cmdline = utils.rtrim(entry.cmdline) or ""
|
||||||
|
local icon = entry.icon_path or nil
|
||||||
|
table.insert(result, { name = name,
|
||||||
|
cmdline = cmdline,
|
||||||
|
icon = icon,
|
||||||
|
category = target_category })
|
||||||
|
unique_entries[unique_key] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dirs_parsed = dirs_parsed + 1
|
||||||
|
if dirs_parsed == #menu_gen.all_menu_dirs then
|
||||||
|
callback(result)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return menu_gen
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
430
menubar/utils.lua
Normal file
430
menubar/utils.lua
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
---------------------------------------------------------------------------
|
||||||
|
--- Utility module for menubar
|
||||||
|
--
|
||||||
|
-- @author Antonio Terceiro
|
||||||
|
-- @copyright 2009, 2011-2012 Antonio Terceiro, Alexander Yakushev
|
||||||
|
-- @module menubar.utils
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Grab environment
|
||||||
|
local table = table
|
||||||
|
local ipairs = ipairs
|
||||||
|
local string = string
|
||||||
|
local screen = screen
|
||||||
|
local gfs = require("gears.filesystem")
|
||||||
|
local theme = require("beautiful")
|
||||||
|
local lgi = require("lgi")
|
||||||
|
local gio = lgi.Gio
|
||||||
|
local glib = lgi.GLib
|
||||||
|
local w_textbox = require("wibox.widget.textbox")
|
||||||
|
local gdebug = require("gears.debug")
|
||||||
|
local protected_call = require("gears.protected_call")
|
||||||
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||||
|
|
||||||
|
local utils = {}
|
||||||
|
|
||||||
|
-- NOTE: This icons/desktop files module was written according to the
|
||||||
|
-- following freedesktop.org specifications:
|
||||||
|
-- Icons: http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-0.11.html
|
||||||
|
-- Desktop files: http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html
|
||||||
|
|
||||||
|
-- Options section
|
||||||
|
|
||||||
|
--- Terminal which applications that need terminal would open in.
|
||||||
|
-- @param[opt="xterm"] string
|
||||||
|
utils.terminal = 'xterm'
|
||||||
|
|
||||||
|
--- The default icon for applications that don't provide any icon in
|
||||||
|
-- their .desktop files.
|
||||||
|
local default_icon = nil
|
||||||
|
|
||||||
|
--- Name of the WM for the OnlyShowIn entry in the .desktop file.
|
||||||
|
-- @param[opt="awesome"] string
|
||||||
|
utils.wm_name = "awesome"
|
||||||
|
|
||||||
|
-- Maps keys in desktop entries to suitable getter function.
|
||||||
|
-- The order of entries is as in the spec.
|
||||||
|
-- https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s05.html
|
||||||
|
local keys_getters
|
||||||
|
do
|
||||||
|
local function get_string(kf, key)
|
||||||
|
return kf:get_string("Desktop Entry", key)
|
||||||
|
end
|
||||||
|
local function get_strings(kf, key)
|
||||||
|
return kf:get_string_list("Desktop Entry", key, nil)
|
||||||
|
end
|
||||||
|
local function get_localestring(kf, key)
|
||||||
|
return kf:get_locale_string("Desktop Entry", key, nil)
|
||||||
|
end
|
||||||
|
local function get_localestrings(kf, key)
|
||||||
|
return kf:get_locale_string_list("Desktop Entry", key, nil, nil)
|
||||||
|
end
|
||||||
|
local function get_boolean(kf, key)
|
||||||
|
return kf:get_boolean("Desktop Entry", key)
|
||||||
|
end
|
||||||
|
|
||||||
|
keys_getters = {
|
||||||
|
Type = get_string,
|
||||||
|
Version = get_string,
|
||||||
|
Name = get_localestring,
|
||||||
|
GenericName = get_localestring,
|
||||||
|
NoDisplay = get_boolean,
|
||||||
|
Comment = get_localestring,
|
||||||
|
Icon = get_localestring,
|
||||||
|
Hidden = get_boolean,
|
||||||
|
OnlyShowIn = get_strings,
|
||||||
|
NotShowIn = get_strings,
|
||||||
|
DBusActivatable = get_boolean,
|
||||||
|
TryExec = get_string,
|
||||||
|
Exec = get_string,
|
||||||
|
Path = get_string,
|
||||||
|
Terminal = get_boolean,
|
||||||
|
Actions = get_strings,
|
||||||
|
MimeType = get_strings,
|
||||||
|
Categories = get_strings,
|
||||||
|
Implements = get_strings,
|
||||||
|
Keywords = get_localestrings,
|
||||||
|
StartupNotify = get_boolean,
|
||||||
|
StartupWMClass = get_string,
|
||||||
|
URL = get_string,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Private section
|
||||||
|
|
||||||
|
local do_protected_call, call_callback
|
||||||
|
do
|
||||||
|
-- Lua 5.1 cannot yield across a protected call. Instead of hardcoding a
|
||||||
|
-- check, we check for this problem: The following coroutine yields true on
|
||||||
|
-- success (so resume() returns true, true). On failure, pcall returns
|
||||||
|
-- false and a message, so resume() returns true, false, message.
|
||||||
|
local _, has_yieldable_pcall = coroutine.resume(coroutine.create(function()
|
||||||
|
return pcall(coroutine.yield, true)
|
||||||
|
end))
|
||||||
|
if has_yieldable_pcall then
|
||||||
|
do_protected_call = protected_call.call
|
||||||
|
call_callback = function(callback, ...)
|
||||||
|
return callback(...)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
do_protected_call = function(f, ...)
|
||||||
|
return f(...)
|
||||||
|
end
|
||||||
|
call_callback = protected_call.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local all_icon_sizes = {
|
||||||
|
'scalable',
|
||||||
|
'128x128',
|
||||||
|
'96x96',
|
||||||
|
'72x72',
|
||||||
|
'64x64',
|
||||||
|
'48x48',
|
||||||
|
'36x36',
|
||||||
|
'32x32',
|
||||||
|
'24x24',
|
||||||
|
'22x22',
|
||||||
|
'16x16'
|
||||||
|
}
|
||||||
|
|
||||||
|
--- List of supported icon exts.
|
||||||
|
local supported_icon_file_exts = { png = 1, xpm = 2, svg = 3 }
|
||||||
|
|
||||||
|
local icon_lookup_path = nil
|
||||||
|
|
||||||
|
--- Get a list of icon lookup paths.
|
||||||
|
-- @treturn table A list of directories, without trailing slash.
|
||||||
|
local function get_icon_lookup_path()
|
||||||
|
if icon_lookup_path then return icon_lookup_path end
|
||||||
|
|
||||||
|
local function ensure_args(t, paths)
|
||||||
|
if type(paths) == 'string' then paths = { paths } end
|
||||||
|
return t or {}, paths
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_if_readable(t, paths)
|
||||||
|
t, paths = ensure_args(t, paths)
|
||||||
|
|
||||||
|
for _, path in ipairs(paths) do
|
||||||
|
if gfs.dir_readable(path) then
|
||||||
|
table.insert(t, path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_with_dir(t, paths, dir)
|
||||||
|
t, paths = ensure_args(t, paths)
|
||||||
|
dir = { nil, dir }
|
||||||
|
|
||||||
|
for _, path in ipairs(paths) do
|
||||||
|
dir[1] = path
|
||||||
|
table.insert(t, glib.build_filenamev(dir))
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
icon_lookup_path = {}
|
||||||
|
local theme_priority = { 'hicolor' }
|
||||||
|
if theme.icon_theme then table.insert(theme_priority, 1, theme.icon_theme) end
|
||||||
|
|
||||||
|
local paths = add_with_dir({}, glib.get_home_dir(), '.icons')
|
||||||
|
add_with_dir(paths, {
|
||||||
|
glib.get_user_data_dir(), -- $XDG_DATA_HOME, typically $HOME/.local/share
|
||||||
|
unpack(glib.get_system_data_dirs()) -- $XDG_DATA_DIRS, typically /usr/{,local/}share
|
||||||
|
}, 'icons')
|
||||||
|
add_with_dir(paths, glib.get_system_data_dirs(), 'pixmaps')
|
||||||
|
|
||||||
|
local icon_theme_paths = {}
|
||||||
|
for _, theme_dir in ipairs(theme_priority) do
|
||||||
|
add_if_readable(icon_theme_paths,
|
||||||
|
add_with_dir({}, paths, theme_dir))
|
||||||
|
end
|
||||||
|
|
||||||
|
local app_in_theme_paths = {}
|
||||||
|
for _, icon_theme_directory in ipairs(icon_theme_paths) do
|
||||||
|
for _, size in ipairs(all_icon_sizes) do
|
||||||
|
table.insert(app_in_theme_paths,
|
||||||
|
glib.build_filenamev({ icon_theme_directory,
|
||||||
|
size, 'apps' }))
|
||||||
|
table.insert(app_in_theme_paths,
|
||||||
|
glib.build_filenamev({ icon_theme_directory,
|
||||||
|
size, 'categories' }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
add_if_readable(icon_lookup_path, app_in_theme_paths)
|
||||||
|
|
||||||
|
return add_if_readable(icon_lookup_path, paths)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove CR newline from the end of the string.
|
||||||
|
-- @param s string to trim
|
||||||
|
-- @staticfct menubar.utils.rtrim
|
||||||
|
function utils.rtrim(s)
|
||||||
|
if not s then return end
|
||||||
|
if string.byte(s, #s) == 13 then
|
||||||
|
return string.sub(s, 1, #s - 1)
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Lookup an icon in different folders of the filesystem.
|
||||||
|
-- @tparam string icon_file Short or full name of the icon.
|
||||||
|
-- @treturn string|boolean Full name of the icon, or false on failure.
|
||||||
|
-- @staticfct menubar.utils.lookup_icon_uncached
|
||||||
|
function utils.lookup_icon_uncached(icon_file)
|
||||||
|
if not icon_file or icon_file == "" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local icon_file_ext = icon_file:match(".+%.(.*)$")
|
||||||
|
if icon_file:sub(1, 1) == '/' and supported_icon_file_exts[icon_file_ext] then
|
||||||
|
-- If the path to the icon is absolute do not perform a lookup [nil if unsupported ext or missing]
|
||||||
|
return gfs.file_readable(icon_file) and icon_file or nil
|
||||||
|
else
|
||||||
|
-- Look for the requested file in the lookup path
|
||||||
|
for _, directory in ipairs(get_icon_lookup_path()) do
|
||||||
|
local possible_file = directory .. "/" .. icon_file
|
||||||
|
-- Check to see if file exists if requested with a valid extension
|
||||||
|
if supported_icon_file_exts[icon_file_ext] and gfs.file_readable(possible_file) then
|
||||||
|
return possible_file
|
||||||
|
else
|
||||||
|
-- Find files with any supported extension if icon specified without, eg: 'firefox'
|
||||||
|
for ext, _ in pairs(supported_icon_file_exts) do
|
||||||
|
local possible_file_new_ext = possible_file .. "." .. ext
|
||||||
|
if gfs.file_readable(possible_file_new_ext) then
|
||||||
|
return possible_file_new_ext
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- No icon found
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local lookup_icon_cache = {}
|
||||||
|
--- Lookup an icon in different folders of the filesystem (cached).
|
||||||
|
-- @param icon Short or full name of the icon.
|
||||||
|
-- @return full name of the icon.
|
||||||
|
-- @staticfct menubar.utils.lookup_icon
|
||||||
|
function utils.lookup_icon(icon)
|
||||||
|
if not lookup_icon_cache[icon] and lookup_icon_cache[icon] ~= false then
|
||||||
|
lookup_icon_cache[icon] = utils.lookup_icon_uncached(icon)
|
||||||
|
end
|
||||||
|
return lookup_icon_cache[icon] or default_icon
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Parse a .desktop file.
|
||||||
|
-- @param file The .desktop file.
|
||||||
|
-- @return A table with file entries.
|
||||||
|
-- @staticfct menubar.utils.parse_desktop_file
|
||||||
|
function utils.parse_desktop_file(file)
|
||||||
|
local program = { show = true, file = file }
|
||||||
|
|
||||||
|
-- Parse the .desktop file.
|
||||||
|
-- We are interested in [Desktop Entry] group only.
|
||||||
|
local keyfile = glib.KeyFile()
|
||||||
|
if not keyfile:load_from_file(file, glib.KeyFileFlags.NONE) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- In case [Desktop Entry] was not found
|
||||||
|
if not keyfile:has_group("Desktop Entry") then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, key in pairs(keyfile:get_keys("Desktop Entry")) do
|
||||||
|
local getter = keys_getters[key] or function(kf, k)
|
||||||
|
return kf:get_string("Desktop Entry", k)
|
||||||
|
end
|
||||||
|
program[key] = getter(keyfile, key)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- In case the (required) 'Name' entry was not found
|
||||||
|
if not program.Name or program.Name == '' then return nil end
|
||||||
|
|
||||||
|
-- Don't show program if NoDisplay attribute is true
|
||||||
|
if program.NoDisplay then
|
||||||
|
program.show = false
|
||||||
|
else
|
||||||
|
-- Only check these values is NoDisplay is true (or non-existent)
|
||||||
|
|
||||||
|
-- Only show the program if there is no OnlyShowIn attribute
|
||||||
|
-- or if it contains wm_name or wm_name is empty
|
||||||
|
if utils.wm_name ~= "" then
|
||||||
|
if program.OnlyShowIn then
|
||||||
|
program.show = false -- Assume false until found
|
||||||
|
for _, wm in ipairs(program.OnlyShowIn) do
|
||||||
|
if wm == utils.wm_name then
|
||||||
|
program.show = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
program.show = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Only need to check NotShowIn if the program is being shown
|
||||||
|
if program.show and program.NotShowIn then
|
||||||
|
for _, wm in ipairs(program.NotShowIn) do
|
||||||
|
if wm == utils.wm_name then
|
||||||
|
program.show = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Look up for a icon.
|
||||||
|
if program.Icon then
|
||||||
|
program.icon_path = utils.lookup_icon(program.Icon)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Make the variable lower-case like the rest of them
|
||||||
|
if program.Categories then
|
||||||
|
program.categories = program.Categories
|
||||||
|
end
|
||||||
|
|
||||||
|
if program.Exec then
|
||||||
|
-- Substitute Exec special codes as specified in
|
||||||
|
-- http://standards.freedesktop.org/desktop-entry-spec/1.1/ar01s06.html
|
||||||
|
if program.Name == nil then
|
||||||
|
program.Name = '['.. file:match("([^/]+)%.desktop$") ..']'
|
||||||
|
end
|
||||||
|
local cmdline = program.Exec:gsub('%%c', program.Name)
|
||||||
|
cmdline = cmdline:gsub('%%[fuFU]', '')
|
||||||
|
cmdline = cmdline:gsub('%%k', program.file)
|
||||||
|
if program.icon_path then
|
||||||
|
cmdline = cmdline:gsub('%%i', '--icon ' .. program.icon_path)
|
||||||
|
else
|
||||||
|
cmdline = cmdline:gsub('%%i', '')
|
||||||
|
end
|
||||||
|
if program.Terminal == true then
|
||||||
|
cmdline = utils.terminal .. ' -e ' .. cmdline
|
||||||
|
end
|
||||||
|
program.cmdline = cmdline
|
||||||
|
end
|
||||||
|
|
||||||
|
return program
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Parse a directory with .desktop files recursively.
|
||||||
|
-- @tparam string dir_path The directory path.
|
||||||
|
-- @tparam function callback Will be fired when all the files were parsed
|
||||||
|
-- with the resulting list of menu entries as argument.
|
||||||
|
-- @tparam table callback.programs Paths of found .desktop files.
|
||||||
|
-- @staticfct menubar.utils.parse_dir
|
||||||
|
function utils.parse_dir(dir_path, callback)
|
||||||
|
|
||||||
|
local function get_readable_path(file)
|
||||||
|
return file:get_path() or file:get_uri()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parser(file, programs)
|
||||||
|
-- Except for "NONE" there is also NOFOLLOW_SYMLINKS
|
||||||
|
local query = gio.FILE_ATTRIBUTE_STANDARD_NAME .. "," .. gio.FILE_ATTRIBUTE_STANDARD_TYPE
|
||||||
|
local enum, err = file:async_enumerate_children(query, gio.FileQueryInfoFlags.NONE)
|
||||||
|
if not enum then
|
||||||
|
gdebug.print_warning(get_readable_path(file) .. ": " .. tostring(err))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local files_per_call = 100 -- Actual value is not that important
|
||||||
|
while true do
|
||||||
|
local list, enum_err = enum:async_next_files(files_per_call)
|
||||||
|
if enum_err then
|
||||||
|
gdebug.print_error(get_readable_path(file) .. ": " .. tostring(enum_err))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, info in ipairs(list) do
|
||||||
|
local file_type = info:get_file_type()
|
||||||
|
local file_child = enum:get_child(info)
|
||||||
|
if file_type == 'REGULAR' then
|
||||||
|
local path = file_child:get_path()
|
||||||
|
if path then
|
||||||
|
local success, program = pcall(utils.parse_desktop_file, path)
|
||||||
|
if not success then
|
||||||
|
gdebug.print_error("Error while reading '" .. path .. "': " .. program)
|
||||||
|
elseif program then
|
||||||
|
table.insert(programs, program)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif file_type == 'DIRECTORY' then
|
||||||
|
parser(file_child, programs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #list == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
enum:async_close()
|
||||||
|
end
|
||||||
|
|
||||||
|
gio.Async.start(do_protected_call)(function()
|
||||||
|
local result = {}
|
||||||
|
parser(gio.File.new_for_path(dir_path), result)
|
||||||
|
call_callback(callback, result)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- luacov: disable
|
||||||
|
|
||||||
|
function utils.compute_textbox_width(textbox, s)
|
||||||
|
gdebug.deprecate("Use 'width, _ = textbox:get_preferred_size(s)' directly.", {deprecated_in=4})
|
||||||
|
s = screen[s or mouse.screen]
|
||||||
|
local w, _ = textbox:get_preferred_size(s)
|
||||||
|
return w
|
||||||
|
end
|
||||||
|
|
||||||
|
function utils.compute_text_width(text, s, font)
|
||||||
|
gdebug.deprecate("Use 'width = textbox.get_markup_geometry(text, s, font)['width']'.", {deprecated_in=4})
|
||||||
|
return w_textbox.get_markup_geometry(text, s, font)['width']
|
||||||
|
end
|
||||||
|
|
||||||
|
-- luacov: enable
|
||||||
|
|
||||||
|
return utils
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
10
startup.sh
Executable file
10
startup.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
XCURSOR_SIZE = 22
|
||||||
|
xinput --set-prop "SYNA7DB5:01 06CB:CD41 Touchpad" $(xinput list-props $(xinput list | grep 'Touchpad' | cut -f 2 | cut -d'=' -f 2) | grep "Disable While Typing Enabled (" | cut -d'(' -f 2 | cut -d')' -f 1) 0
|
||||||
|
xinput --set-prop "SYNA7DB5:01 06CB:CD41 Touchpad" $(xinput list-props $(xinput list | grep 'Touchpad' | cut -f 2 | cut -d'=' -f 2) | grep "Tapping Enabled (" | cut -d'(' -f 2 | cut -d')' -f 1) 1
|
||||||
|
xinput --set-prop "SYNA7DB5:01 06CB:CD41 Touchpad" $(xinput list-props $(xinput list | grep 'Touchpad' | cut -f 2 | cut -d'=' -f 2) | grep "Natural Scrolling Enabled (" | cut -d'(' -f 2 | cut -d')' -f 1) 1
|
||||||
|
xinput --set-prop "SYNA7DB5:01 06CB:CD41 Touchpad" $(xinput list-props $(xinput list | grep 'Touchpad' | cut -f 2 | cut -d'=' -f 2) | grep "Middle Emulation Enabled (" | cut -d'(' -f 2 | cut -d')' -f 1) 1
|
||||||
|
#python ~/data/projects/startup/presence.py &
|
||||||
|
#picom --experimental-backends --backend glx &
|
||||||
|
#xsetroot -cursor_name qogir-manjaro
|
||||||
|
cp ~/.gtkrc-2.0_default ~/.gtkrc-2.0
|
1
themes/blueish
Submodule
1
themes/blueish
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 27b186b8f25496c456221088300b178de5a4ef99
|
1
vicious
Submodule
1
vicious
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 150864a76d4b18c97da1459f355c6a86540a0863
|
Loading…
Reference in New Issue
Block a user