├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── desktop-directories ├── audio-video.directory ├── development.directory ├── game.directory ├── graphics.directory ├── internet-and-network.directory ├── office.directory ├── other.directory ├── system-tools.directory └── utility.directory ├── go.mod ├── go.sum ├── main.go ├── menu-start.css ├── nwg-menu ├── tools.go └── uicomponents.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: nwg-piotr 2 | liberapay: nwg 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Binaries built with make 15 | bin 16 | nwg-menu 17 | 18 | /.idea 19 | 20 | # Dependency directories (remove the comment below to include it) 21 | # vendor/ 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Piotr Miller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | DESTDIR ?= 3 | 4 | get: 5 | go get github.com/gotk3/gotk3 6 | go get github.com/gotk3/gotk3/gdk 7 | go get github.com/gotk3/gotk3/glib 8 | go get github.com/dlasky/gotk3-layershell/layershell 9 | go get github.com/joshuarubin/go-sway 10 | go get github.com/allan-simon/go-singleinstance 11 | 12 | build: 13 | go build -v -o bin/nwg-menu *.go 14 | 15 | install: 16 | mkdir -p $(DESTDIR)$(PREFIX)/share/nwg-menu 17 | mkdir -p $(DESTDIR)$(PREFIX)/bin 18 | cp -r desktop-directories $(DESTDIR)$(PREFIX)/share/nwg-menu 19 | cp menu-start.css $(DESTDIR)$(PREFIX)/share/nwg-menu 20 | cp bin/nwg-menu $(DESTDIR)$(PREFIX)/bin/nwg-menu 21 | 22 | uninstall: 23 | rm -f $(DESTDIR)$(PREFIX)/bin/nwg-menu 24 | rm -fr $(DESTDIR)$(PREFIX)/share/nwg-menu 25 | 26 | run: 27 | go run *.go 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nwg-menu 2 | 3 | This code provides the [MenuStart plugin](https://github.com/nwg-piotr/nwg-panel/wiki/plugins:-MenuStart) 4 | to [nwg-panel](https://github.com/nwg-piotr/nwg-panel). It also may be used standalone, however, with a little help from command line arguments. 5 | 6 | The `nwg-menu` command displays the system menu with simplified [freedesktop main categories](https://specifications.freedesktop.org/menu-spec/latest/apa.html) (8 instead of 13). 7 | It also provides the search entry, to look for installed application on the basis of .desktop files, and for files in 8 | XDG user directories. 9 | 10 | You may pin applications by right-clicking them. Pinned items will appear above the categories list. Right-click 11 | a pinned item to unpin it. The pinned items cache is shared with the `nwggrid` command, which is a part of 12 | [nwg-launchers](https://github.com/nwg-piotr/nwg-launchers). 13 | 14 | In the bottom-right corner of the window you'll also see a set of buttons: logout, lock screen, restart and shutdown. 15 | The commands attached to them may be defined in the nwg-panel settings or given as the arguments. 16 | 17 | Screenshot
18 | 19 | To use the menu standalone (e.g. with another panel/bar or with a key binding), take a look at arguments: 20 | 21 | ```text 22 | $ nwg-menu -h 23 | Usage of nwg-menu: 24 | -cmd-lock string 25 | screen lock command (default "swaylock -f -c 000000") 26 | -cmd-logout string 27 | logout command (default "swaymsg exit") 28 | -cmd-restart string 29 | reboot command (default "systemctl reboot") 30 | -cmd-shutdown string 31 | shutdown command (default "systemctl -i poweroff") 32 | -d auto-hiDe: close window when left 33 | -fm string 34 | File Manager (default "thunar") 35 | -ha string 36 | Horizontal Alignment: "left" or "right" (default "left") 37 | -height int 38 | window height 39 | -isl int 40 | Icon Size Large (default 32) 41 | -iss int 42 | Icon Size Small (default 16) 43 | -lang string 44 | force lang, e.g. "en", "pl" 45 | -mb int 46 | Margin Bottom 47 | -ml int 48 | Margin Left 49 | -mr int 50 | Margin Right 51 | -mt int 52 | Margin Top 53 | -o string 54 | name of the Output to display the menu on 55 | -padding uint 56 | vertical item padding (default 2) 57 | -s string 58 | Styling: css file name (default "menu-start.css") 59 | -term string 60 | Terminal emulator (default "alacritty") 61 | -v display Version information 62 | -va string 63 | Vertical Alignment: "bottom" or "top" (default "bottom") 64 | -width int 65 | window width 66 | -wm string 67 | use swaymsg exec (with 'sway' argument) or hyprctl dispatch exec (with 'hyprland') or riverctl spawn (with 'river') to launch programs 68 | ``` 69 | 70 | ## Installation 71 | 72 | [![Packaging status](https://repology.org/badge/vertical-allrepos/nwg-menu.svg)](https://repology.org/project/nwg-menu/versions) 73 | 74 | ### Dependencies 75 | 76 | - go 1.16 (just to build) 77 | - gtk3 78 | - gtk-layer-shell 79 | 80 | Optional (recommended): 81 | 82 | - thunar 83 | - alacritty 84 | 85 | You may use another file manager and terminal emulator, but for now the program has not yet been tested with anything 86 | but the two mentioned above. 87 | 88 | ### Steps 89 | 90 | 1. Clone the repository, cd into it. 91 | 2. Install necessary golang libraries with `make get`. 92 | 3. `make build` 93 | 4. `sudo make install` 94 | 95 | ## Running 96 | 97 | Plugin integration and the config GUI has been available in the nwg-panel since the 0.3.1 version, see 98 | [Wiki](https://github.com/nwg-piotr/nwg-panel/wiki/plugins:-MenuStart). You may also start the menu from another panel 99 | or a key binding. 100 | 101 | ## Styling 102 | 103 | Edit `~/.config/nwg-panel/menu-start.css` to your taste. 104 | 105 | ## Credits 106 | 107 | This program uses some great libraries: 108 | 109 | - [gotk3](https://github.com/gotk3/gotk3) Copyright (c) 2013-2014 Conformal Systems LLC, 110 | Copyright (c) 2015-2018 gotk3 contributors 111 | - [gotk3-layershell](https://github.com/dlasky/gotk3-layershell) by [@dlasky](https://github.com/dlasky/gotk3-layershell/commits?author=dlasky) - many thanks for writing this software, and for patience with my requests! 112 | - [go-sway](https://github.com/joshuarubin/go-sway) Copyright (c) 2019 Joshua Rubin 113 | - [go-singleinstance](github.com/allan-simon/go-singleinstance) Copyright (c) 2015 Allan Simon 114 | 115 | The nwg-shell logo (which is also the menu button graphics) created by [SGSE](https://github.com/sgse), licensed 116 | under the terms of the Creative Commons license [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.en). 117 | -------------------------------------------------------------------------------- /desktop-directories/audio-video.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Sound & Video 3 | Name[af]=Klank & video 4 | Name[ar]=الصّوتيات والمرئيّات 5 | Name[as]=শব্দ ও ভিডিও 6 | Name[ast]=Soníu y Vídeu 7 | Name[be]=Аўдыё і відэа 8 | Name[be@latin]=Aŭdyjo j videa 9 | Name[bg]=Видео и звук 10 | Name[bn]=শব্দ ও ভিডিও 11 | Name[bn_IN]=শব্দ ও ভিডিও 12 | Name[br]=Son ha video 13 | Name[ca]=So i Vídeo 14 | Name[cs]=Zvuk a video 15 | Name[cy]=Sain a Fideo 16 | Name[da]=Lyd og video 17 | Name[de]=Unterhaltungsmedien 18 | Name[dz]=སྒྲ་སྐད་དང་གཟུགས་བརྙན། 19 | Name[el]=Ήχος & βίντεο 20 | Name[en_CA]=Sound & Video 21 | Name[en_GB]=Sound & Video 22 | Name[eo]=Sono kaj video 23 | Name[es]=Sonido y vídeo 24 | Name[es_VE]=Sonido y vídeo 25 | Name[et]=Audio ja video 26 | Name[eu]=Soinua eta bideoa 27 | Name[fa]=صدا و تصویر 28 | Name[fi]=Ääni & video 29 | Name[fo]=Ljóð og video 30 | Name[fr]=Son et vidéo 31 | Name[frp]=Son é Videó 32 | Name[fur]=Audio e video 33 | Name[ga]=Fuaim & Fís 34 | Name[gl]=Son e vídeo 35 | Name[gn]=Jaexaa, Porai Nhaendua 36 | Name[gu]=સાઉન્ડ & વીડિયો 37 | Name[he]=צליל ווידאו 38 | Name[hi]=ध्वनि व वीडियो 39 | Name[hr]=Zvuk & Video 40 | Name[hu]=Hang és videó 41 | Name[hy]=Ձայն և Վիդեո 42 | Name[id]=Suara & Video 43 | Name[io]=Sono ed Video 44 | Name[is]=Hljóð og mynd 45 | Name[it]=Audio e video 46 | Name[ja]=サウンドとビデオ 47 | Name[ka]=ხმა და ვიდეო 48 | Name[kk]=Аудио және видео 49 | Name[km]=សម្លេង & វីដេអូ 50 | Name[kn]=ಧ್ವನಿ ಹಾಗು ದೃಶ್ಯ 51 | Name[ko]=음악과 비디오 52 | Name[ku]=Deng & Vîdeo 53 | Name[ky]=Аудио и видео 54 | Name[lg]=Ddoboozi & Vidiyo 55 | Name[lt]=Garsas ir vaizdas 56 | Name[lv]=Skaņas un Video 57 | Name[mai]=ध्वनि आ वीडियो 58 | Name[mg]=Feo sy sarimihetsika 59 | Name[mk]=Звук и видео 60 | Name[ml]=ശബ്ദവും ചലച്ചിത്രവും 61 | Name[mn]=Дуу & Видео 62 | Name[mr]=आवाज व चलचित्र 63 | Name[ms]=Bunyi & Video 64 | Name[nb]=Lyd og bilde 65 | Name[ne]=ध्वनि र भिडियो 66 | Name[nl]=Muziek en video 67 | Name[nn]=Lyd og film 68 | Name[oc]=Son e vidèo 69 | Name[or]=ଧ୍ବନି ଏବଂ ଭିଡିଓ 70 | Name[pa]=ਸਾਊਂਡ ਅਤੇ ਵੀਡਿਓ 71 | Name[pl]=Dźwięk i wideo 72 | Name[ps]=غږ او وېډيو 73 | Name[pt]=Som e vídeo 74 | Name[pt_BR]=Multimídia 75 | Name[ro]=Multimedia 76 | Name[ru]=Аудио и видео 77 | Name[si]=ශබ්ද සහ දෘශ්‍ය 78 | Name[sk]=Zvuk a video 79 | Name[sl]=Zvok in video 80 | Name[sq]=Zë & Video 81 | Name[sr]=Звук и покретне слике 82 | Name[sr@latin]=Zvuk i pokretne slike 83 | Name[sv]=Ljud och video 84 | Name[ta]=ஒலி மற்றும் படக்காட்சி 85 | Name[te]=ధ్వని మరియు దృశ్యం 86 | Name[th]=เสียงและวีดิทัศน์ 87 | Name[tr]=Ses ve Video 88 | Name[tt_RU]=Аудио һәм видео 89 | Name[ug]=ئۈن ۋە سىن 90 | Name[uk]=Звук та відео 91 | Name[ur]=آواز اور ویڈیو 92 | Name[ur_PK]=آواز اور ویڈیو 93 | Name[uz@cyrillic]=Товуш ва видео 94 | Name[vi]=Âm thanh và Ảnh động 95 | Name[xh]=Isandi neVidiyo 96 | Name[zh_CN]=影音 97 | Name[zh_HK]=影音 98 | Name[zh_TW]=影音 99 | Comment=Multimedia menu 100 | Comment[af]=Multimediakieslys 101 | Comment[ar]=قائمة الوسائط المتعدّدة 102 | Comment[as]=মাল্টি-মিডিয়া তালিকা 103 | Comment[ast]=Menú multimedia 104 | Comment[be]=Мультымедыя 105 | Comment[be@latin]=Multymedyi 106 | Comment[bg]=Меню за мултимедия 107 | Comment[bn]=মাল্টিমিডিয়া মেনু 108 | Comment[bn_IN]=মাল্টি-মিডিয়া মেনু 109 | Comment[br]=Liesvedia 110 | Comment[ca]=Menú multimèdia 111 | Comment[cs]=Nabídka multimédií 112 | Comment[cy]=Dewislen amlgyfrwng 113 | Comment[da]=Multimediemenu 114 | Comment[de]=Multimedia-Menü 115 | Comment[dz]=སྣ་མང་བརྡ་ལམ་དཀར་ཆག། 116 | Comment[el]=Μενού πολυμέσων 117 | Comment[en_CA]=Multimedia menu 118 | Comment[en_GB]=Multimedia menu 119 | Comment[eo]=Plurmedia menuo 120 | Comment[es]=Menú multimedia 121 | Comment[es_VE]=Menú multimedia 122 | Comment[et]=Multimeediamenüü 123 | Comment[eu]=Multimedia menua 124 | Comment[fa]=منوی چندرسانه‌ای 125 | Comment[fi]=Multimediavalikko 126 | Comment[fo]=Margmiðla-valmynd 127 | Comment[fr]=Multimédia 128 | Comment[frp]=Menû multimedia 129 | Comment[fur]=Menu multimediâl 130 | Comment[ga]=Roghchlár ilmheán 131 | Comment[gl]=Menú multimedia 132 | Comment[gn]=Baemo Jaexaa, Nhaendua 133 | Comment[gu]=મલ્ટીમીડિયા મેનુ 134 | Comment[he]=תפריט מולטימדיה 135 | Comment[hi]=मल्टीमीडिया मेनू 136 | Comment[hr]=Multimedijski izbornik 137 | Comment[hu]=Multimédia menü 138 | Comment[hy]=Համասփյուռ ծրագրերի ցանկ 139 | Comment[id]=Menu multimedia 140 | Comment[is]=Margmiðlun 141 | Comment[it]=Menu multimedia 142 | Comment[ja]=マルチメディア関連のプログラムです 143 | Comment[ka]=მულტიმედიის მენუ 144 | Comment[kk]=Мультимедиа 145 | Comment[km]=ម៉ឺនុយពហុព័ត៌មាន 146 | Comment[kn]=ಮಲ್ಟಿಮೀಡಿಯಾ ಅಂಶಪಟ್ಟಿ 147 | Comment[ko]=멀티미디어 메뉴 148 | Comment[ku]=Pêşeka multîmedya 149 | Comment[ky]=Мультимедиа 150 | Comment[lg]=Menyu eya mediya y'ekintabuli 151 | Comment[lt]=Daugialypės terpės meniu 152 | Comment[lv]=Multimediju izvēlne 153 | Comment[mai]=मल्टीमीडिया मेनू 154 | Comment[mg]=Karazan-tsafidin'ny haino aman-jery 155 | Comment[mk]=Мени со мултимедија 156 | Comment[ml]=മള്‍ട്ടീമീഡിയ മെനു 157 | Comment[mn]=Мультимедиа цэс 158 | Comment[mr]=विविध-वाहिन्यांचा मेनु 159 | Comment[ms]=Menu Multimedia 160 | Comment[nb]=Multimedia 161 | Comment[ne]=मल्टिमिडिया मेनु 162 | Comment[nl]=Multimedia-menu 163 | Comment[nn]=Multimedieprogram 164 | Comment[oc]=Multimèdia 165 | Comment[or]=ବହୁମାଧ୍ଯମ ତାଲିକା 166 | Comment[pa]=ਮਲਟੀਮੀਡਿਆ ਮੇਨੂ 167 | Comment[pl]=Menu multimediów 168 | Comment[ps]=ګڼرسنۍ غورنۍ 169 | Comment[pt]=Menu multimédia 170 | Comment[pt_BR]=Menu multimídia 171 | Comment[ro]=Meniul multimedia 172 | Comment[ru]=Мультимедиа 173 | Comment[si]=බහුමාද්‍ය මෙනුව 174 | Comment[sk]=Ponuka multimédií 175 | Comment[sl]=Predstavni meni 176 | Comment[sq]=Menu multimediale 177 | Comment[sr]=Мени за мултимедију 178 | Comment[sr@latin]=Meni za multimediju 179 | Comment[sv]=Multimediameny 180 | Comment[ta]=பல்முனை ஊடகப் பட்டியல் 181 | Comment[te]=బహుళమాధ్యమాల జాబితా 182 | Comment[th]=เมนูระบบสื่อผสม 183 | Comment[tr]=Çokluortam menüsü 184 | Comment[tt_RU]=Мультимедия менюсы 185 | Comment[ug]=كۆپ ۋاسىتە تىزىملىكى 186 | Comment[uk]=Меню мультимедіа 187 | Comment[ur]=ملٹی میڈیا فہرست 188 | Comment[ur_PK]=ملٹی میڈیا فہرست 189 | Comment[uz@cyrillic]=Мултимедиа менюси 190 | Comment[vi]=Trình đơn đa phương tiện 191 | Comment[xh]=Imenu Yezixhobo eziphathekayo zokugcina ulwazi ngokuphinda-phindeneyo 192 | Comment[zh_CN]=多媒体菜单 193 | Comment[zh_HK]=多媒體選單 194 | Comment[zh_TW]=多媒體選單 195 | Icon=applications-multimedia 196 | Type=Directory 197 | -------------------------------------------------------------------------------- /desktop-directories/development.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Programming 3 | Name[af]=Programmering 4 | Name[ar]=البرمجة 5 | Name[as]=প্ৰোগ্ৰামিং 6 | Name[ast]=Programación 7 | Name[be]=Праграмаванне 8 | Name[be@latin]=Prahramavańnie 9 | Name[bg]=Разработка 10 | Name[bn]=প্রোগ্রামিং 11 | Name[bn_IN]=প্রোগ্রামিং 12 | Name[br]=Gouleviñ 13 | Name[ca]=Programació 14 | Name[cs]=Programování 15 | Name[cy]=Rhaglennu 16 | Name[da]=Programmering 17 | Name[de]=Entwicklung 18 | Name[dz]=ལས་རིམ་བཟོ་བ། 19 | Name[el]=Προγραμματισμός 20 | Name[en_CA]=Programming 21 | Name[en_GB]=Programming 22 | Name[eo]=Programado 23 | Name[es]=Programación 24 | Name[es_VE]=Programación 25 | Name[et]=Programmeerimine 26 | Name[eu]=Programazioa 27 | Name[fa]=برنامه‌سازی 28 | Name[fi]=Ohjelmointi 29 | Name[fo]=Forritan 30 | Name[fr]=Programmation 31 | Name[frp]=Programacion 32 | Name[fur]=Programazion 33 | Name[ga]=Ríomhchlárú 34 | Name[gl]=Programación 35 | Name[gn]=Jaja Poa 36 | Name[gu]=પ્રોગ્રામીંગ 37 | Name[he]=תכנות 38 | Name[hi]=प्रोग्रामिंग 39 | Name[hr]=Programiranje 40 | Name[hu]=Programozás 41 | Name[hy]=Ծրագրավորում 42 | Name[id]=Pemrograman 43 | Name[io]=Programado 44 | Name[is]=Forritun 45 | Name[it]=Programmazione 46 | Name[ja]=プログラミング 47 | Name[ka]=პროგრამირება 48 | Name[kk]=Бағдарламалау 49 | Name[km]=ការធ្វើកម្មវិធី 50 | Name[kn]=ಪ್ರೊಗ್ರಾಮಿಂಗ್ 51 | Name[ko]=개발 52 | Name[ku]=Bernamekirin 53 | Name[ky]=Программалоо 54 | Name[lg]=Kuwandika puloguramu 55 | Name[lt]=Programavimas 56 | Name[lv]=Programmēšanas 57 | Name[mai]=प्रोग्रामिंग 58 | Name[mg]=Famolavolavolana 59 | Name[mk]=Програмирање 60 | Name[ml]=പ്രോഗ്രാമിങ് 61 | Name[mn]=Програмчилал 62 | Name[mr]=प्रोग्रामिंग 63 | Name[ms]=Pengaturcaraan 64 | Name[nb]=Utvikling 65 | Name[ne]=प्रोग्रामिङ 66 | Name[nl]=Programmeren 67 | Name[nn]=Programmering 68 | Name[oc]=Desvolopament 69 | Name[or]=ପ୍ରୋଗ୍ରାମିଙ୍ଗ 70 | Name[pa]=ਪਰੋਗਰਾਮਿੰਗ 71 | Name[pl]=Programowanie 72 | Name[ps]=پروګرامونه 73 | Name[pt]=Desenvolvimento 74 | Name[pt_BR]=Desenvolvimento 75 | Name[ro]=Programare 76 | Name[ru]=Программирование 77 | Name[si]=කේතරචනය 78 | Name[sk]=Programovanie 79 | Name[sl]=Programiranje 80 | Name[sq]=Programim 81 | Name[sr]=Програмирање 82 | Name[sr@latin]=Programiranje 83 | Name[sv]=Programmering 84 | Name[ta]=நிரலாக்கம் 85 | Name[te]=ప్రోగ్రామింగ్ 86 | Name[th]=เขียนโปรแกรม 87 | Name[tr]=Programlama 88 | Name[tt_RU]=Программалау 89 | Name[ug]=پروگراممىچىلىق 90 | Name[uk]=Програмування 91 | Name[ur]=پروگرامنگ 92 | Name[ur_PK]=پروگرامنگ 93 | Name[uz@cyrillic]=Дастурлаш 94 | Name[vi]=Lập trình 95 | Name[xh]=Ukwenziwa kweenkqubo 96 | Name[zh_CN]=编程 97 | Name[zh_HK]=軟件開發 98 | Name[zh_TW]=軟體開發 99 | Comment=Tools for software development 100 | Comment[af]=Gereedskap vir sagtewareontwikkeling 101 | Comment[ar]=أدوات لتطوير البرامج 102 | Comment[as]=চালনাজ্ঞান উন্নয়নৰ সামগ্ৰী 103 | Comment[ast]=Ferramientes pa desendolcu de programes 104 | Comment[be]=Інструменты для распрацоўкі праграм 105 | Comment[be@latin]=Pryłady prahramavańnia 106 | Comment[bg]=Инструменти за разработка на софтуер 107 | Comment[bn]=সফটওয়্যার ডেভেলপমেন্টের টুল 108 | Comment[bn_IN]=সফ্টওয়্যার ডিভেলপমেন্টের সামগ্রী 109 | Comment[br]=Ar binvioù da ziorren ar poelladoù 110 | Comment[ca]=Eines per a desenvolupament de programari 111 | Comment[cs]=Nástroje na vývoj softwaru 112 | Comment[cy]=Offer ar gyfer datblygu meddalwedd 113 | Comment[da]=Værktøjer til programudvikling 114 | Comment[de]=Werkzeuge zur Software-Entwicklung 115 | Comment[dz]=མཉེན་ཆས་བཟོ་ནིའི་ལག་ཆས། 116 | Comment[el]=Εργαλεία για ανάπτυξη λογισμικού 117 | Comment[en_CA]=Tools for software development 118 | Comment[en_GB]=Tools for software development 119 | Comment[eo]=Iloj por programado 120 | Comment[es]=Herramientas para el desarrollo del software 121 | Comment[es_VE]=Herramientas para el desarrollo del software 122 | Comment[et]=Tarkvaraarenduse tööriistad 123 | Comment[eu]=Softwarea garatzeko tresnak 124 | Comment[fa]=ابزارهای تولید نرم‌افزار 125 | Comment[fi]=Työkaluja sovelluskehitykseen 126 | Comment[fo]=Amboð til ritbúnaðarmenning 127 | Comment[fr]=Outils de développement logiciel 128 | Comment[frp]=Utils de developament de programos 129 | Comment[fur]=Imprescj par il svilup dal software 130 | Comment[ga]=Uirlisí d'fhorbairt bogearraí 131 | Comment[gl]=Ferramentas para o desenvolvemento de software 132 | Comment[gn]=Jaja Poa 133 | Comment[gu]=સોફ્ટવેર વિકાસ સાધનો 134 | Comment[he]=כלים לפיתוח תוכנה 135 | Comment[hi]=सॉफ्टवेयर विकास के औजार 136 | Comment[hr]=Alati za razvoj softvera 137 | Comment[hu]=Szoftverfejlesztési eszközök 138 | Comment[hy]=Ծրագրերի մշակման գործիքներ 139 | Comment[id]=Perkakas untuk pengembangan perangkat lunak 140 | Comment[is]=Þróunarverkfæri 141 | Comment[it]=Strumenti per lo sviluppo software 142 | Comment[ja]=ソフトウェア開発者向けのツールです 143 | Comment[ka]=პროგრამირების ხელსაწყოები 144 | Comment[kk]=Бағдарламаларды жасауға арналған құралдар 145 | Comment[km]=ឧបករណ៍សំរាប់អភិវឌ្ឍន៍កម្មវិធីផ្នែកទន់ 146 | Comment[kn]=ತಂತ್ರಾಂಶ ಅಭಿವೃದ್ದಿಗೆ ಉಪಕರಣಗಳು 147 | Comment[ko]=소프트웨어 개발을 위한 도구 148 | Comment[ku]=Amûrên ji bo pêşdebirina nivîsbariyê 149 | Comment[ky]=Программа иштеп чыгуу аспаптары 150 | Comment[lg]=Ebikozesebwa mu ku kokola sofutiweya 151 | Comment[lt]=Programinės įrangos kūrimo įrankiai 152 | Comment[lv]=Programmatūras izstrādes rīki 153 | Comment[mai]=साफ्टवेयर विकासक लेल अओजार 154 | Comment[mg]=Fitaovana ho an'ny mpamolavola rindran'asa 155 | Comment[mk]=Алатки за развој на софтвер 156 | Comment[ml]=സോഫ്റ്റ്‌വെയര്‍ വികസനത്തിനു് ആവശ്യമുളള പണിയായുധങ്ങള്‍ 157 | Comment[mn]=Програм хөгжүүлэлийн хэрэгслүүд 158 | Comment[mr]=सॉफ्टवेयर बनवण्यासाटीचे अौजार 159 | Comment[ms]=Alatan untuk pembangunan perisian 160 | Comment[nb]=Verktøy for programvareutvikling 161 | Comment[ne]=सफ्टवेर विकासका लागि उपकरण 162 | Comment[nl]=Gereedschap voor programmatuurontwikkeling 163 | Comment[nn]=Verktøy for programutvikling 164 | Comment[oc]=Espleches per desvolopar de logicials 165 | Comment[or]=ସଫଟୱେର ବିକାଶ ପାଇଁ ଉପକରଣ 166 | Comment[pa]=ਸਾਫਟਵੇਅਰ ਦੇ ਵਿਕਾਸ ਲਈ ਸੰਦ 167 | Comment[pl]=Narzędzia do tworzenia oprogramowania 168 | Comment[ps]=د ساوترو جوړولو لپاره توکي 169 | Comment[pt]=Ferramentas para desenvolvimento de aplicações 170 | Comment[pt_BR]=Ferramentas para desenvolvimento de software 171 | Comment[ro]=Unelte pentru dezvoltarea de software 172 | Comment[ru]=Средства для разработки программ 173 | Comment[si]=මෘදුකාංග සංවර්ධන මෙවලම් 174 | Comment[sk]=Nástroje na vývoj softvéru 175 | Comment[sl]=Orodja za razvijanje programske opreme 176 | Comment[sq]=Vegla për zhvillim programesh 177 | Comment[sr]=Алати за развој софтвера 178 | Comment[sr@latin]=Alati za razvoj softvera 179 | Comment[sv]=Verktyg för programutveckling 180 | Comment[ta]=மென்பொருள் உற்பத்திக்கான கருவிகள் 181 | Comment[te]=సాఫ్ట్‍వేర్ అభివృద్ధిచేయుట కొరకు పనిముట్లు 182 | Comment[th]=เครื่องมือพัฒนาซอฟต์แวร์ 183 | Comment[tr]=Yazılım geliştirme için araçlar 184 | Comment[tt_RU]=Кушымталар төзү өчен кораллар 185 | Comment[ug]=يۇمشاق دېتال ئىجادىيەت قوراللىرى 186 | Comment[uk]=Засоби розробки програм 187 | Comment[ur]=اوزار برائے سوفٹ ویئر ترقی 188 | Comment[ur_PK]=اوزار برائے سوفٹ ویئر ترقی 189 | Comment[uz@cyrillic]=Дастурлаш учун воситалар 190 | Comment[vi]=Công cụ phát triển phần mềm 191 | Comment[xh]=Izixhobo zokuphuhlisa ubucukubhede bekhompyutha 192 | Comment[zh_CN]=软件开发工具 193 | Comment[zh_HK]=軟件開發工具 194 | Comment[zh_TW]=軟體開發工具 195 | Icon=applications-development 196 | Type=Directory 197 | -------------------------------------------------------------------------------- /desktop-directories/game.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Games 3 | Name[af]=Speletjies 4 | Name[ar]=الألعاب 5 | Name[as]=খেলা 6 | Name[ast]=Xuegos 7 | Name[be]=Гульні 8 | Name[be@latin]=Hulni 9 | Name[bg]=Игри 10 | Name[bn]=খেলা 11 | Name[bn_IN]=খেলা 12 | Name[br]=C'hoarioù 13 | Name[ca]=Jocs 14 | Name[cs]=Hry 15 | Name[cy]=Gemau 16 | Name[da]=Spil 17 | Name[de]=Spiele 18 | Name[dz]=རྩེད་རིགས། 19 | Name[el]=Παιχνίδια 20 | Name[en_CA]=Games 21 | Name[en_GB]=Games 22 | Name[eo]=Ludoj 23 | Name[es]=Juegos 24 | Name[es_VE]=Juegos 25 | Name[et]=Mängud 26 | Name[eu]=Jokoak 27 | Name[fa]=بازی‌ها 28 | Name[fi]=Pelit 29 | Name[fo]=Spøl 30 | Name[fr]=Jeux 31 | Name[frp]=Joys 32 | Name[fur]=Zûcs 33 | Name[ga]=Cluichí 34 | Name[gl]=Xogos 35 | Name[gn]=Jaugaa 36 | Name[gu]=રમતો 37 | Name[he]=משחקים 38 | Name[hi]=खेल 39 | Name[hr]=Igre 40 | Name[hu]=Játékok 41 | Name[hy]=Խաղեր 42 | Name[id]=Permainan 43 | Name[io]=Ludi 44 | Name[is]=Leikir 45 | Name[it]=Giochi 46 | Name[ja]=ゲーム 47 | Name[ka]=თამაშები 48 | Name[kk]=Ойындар 49 | Name[km]=ល្បែង 50 | Name[kn]=ಆಟಗಳು 51 | Name[ko]=게임 52 | Name[ku]=Lîstik 53 | Name[ky]=Оюндар 54 | Name[lg]=Mizannyo 55 | Name[lt]=Žaidimai 56 | Name[lv]=Spēles 57 | Name[mai]=खेल 58 | Name[mg]=Lalao 59 | Name[mk]=Игри 60 | Name[ml]=കളികള്‍ 61 | Name[mn]=Тоглоом 62 | Name[mr]=खेळ 63 | Name[ms]=Permainan 64 | Name[nb]=Spill 65 | Name[ne]=खेल 66 | Name[nl]=Spelletjes 67 | Name[nn]=Spel 68 | Name[oc]=Jòcs 69 | Name[or]=ଖେଳ 70 | Name[pa]=ਖੇਡਾਂ 71 | Name[pl]=Gry 72 | Name[ps]=لوبې 73 | Name[pt]=Jogos 74 | Name[pt_BR]=Jogos 75 | Name[ro]=Jocuri 76 | Name[ru]=Игры 77 | Name[si]=ක්‍රිඩා 78 | Name[sk]=Hry 79 | Name[sl]=Igre 80 | Name[sq]=Lojra 81 | Name[sr]=Игре 82 | Name[sr@latin]=Igre 83 | Name[sv]=Spel 84 | Name[ta]=விளையாட்டுகள் 85 | Name[te]=ఆటలు 86 | Name[th]=เกม 87 | Name[tr]=Oyunlar 88 | Name[tt_RU]=Уеннар 89 | Name[ug]=ئويۇنلار 90 | Name[uk]=Ігри 91 | Name[ur]=گیم 92 | Name[ur_PK]=گیم 93 | Name[uz@cyrillic]=Ўйинлар 94 | Name[vi]=Trò chơi 95 | Name[xh]=Imidlalo 96 | Name[zh_CN]=游戏 97 | Name[zh_HK]=遊戲 98 | Name[zh_TW]=遊戲 99 | Comment=Games and amusements 100 | Comment[af]=Speletjies en vermaak 101 | Comment[ar]=الألعاب والتّسلية 102 | Comment[as]=খেলা ও বিনোদন 103 | Comment[ast]=Xuegos y entretenimientu 104 | Comment[be]=Гульні і забавы 105 | Comment[be@latin]=Hulni j zabavy 106 | Comment[bg]=Игри и забавления 107 | Comment[bn]=খেলা ও বিনোদন 108 | Comment[bn_IN]=খেলা ও বিনোদন 109 | Comment[br]=C'hoarioù ha diduelloù 110 | Comment[ca]=Jocs i entreteniments 111 | Comment[cs]=Hry a zábava 112 | Comment[cy]=Gemau a difyrrwch 113 | Comment[da]=Spil og underholdning 114 | Comment[de]=Spiel und Spaß 115 | Comment[dz]=རྩེད་རིགས་དང་ དགོད་བྲ་ཚུ། 116 | Comment[el]=Παιχνίδια και διασκέδαση 117 | Comment[en_CA]=Games and amusements 118 | Comment[en_GB]=Games and Amusements 119 | Comment[eo]=Ludoj kaj amuzoj 120 | Comment[es]=Juegos y distracciones 121 | Comment[es_VE]=Juegos y distracciones 122 | Comment[et]=Mängud ja meelelahutus 123 | Comment[eu]=Jokoak eta denbora-pasak 124 | Comment[fa]=بازی و سرگرمی 125 | Comment[fi]=Pelit ja viihde 126 | Comment[fo]=Spøl og skemt 127 | Comment[fr]=Jeux et divertissements 128 | Comment[frp]=Joys é amusaments 129 | Comment[fur]=Zûcs e golosets 130 | Comment[ga]=Cluichí agus siamsaíochtaí 131 | Comment[gl]=Xogos e pasatempos 132 | Comment[gn]=Jaugaty 133 | Comment[gu]=રમતો અને મનોરંજકો 134 | Comment[he]=משחקים ושעשועים 135 | Comment[hi]=खेल व मनोरंजन 136 | Comment[hr]=Igre i zabava 137 | Comment[hu]=Játék és szórakozás 138 | Comment[hy]=Խաղեր և զվարճություններ 139 | Comment[id]=Permainan dan hiburan 140 | Comment[io]=Ludi ed amuzi 141 | Comment[is]=Leikir og gaman 142 | Comment[it]=Giochi e passatempi 143 | Comment[ja]=気晴しにゲームをどうぞ 144 | Comment[ka]=თამაშები და სხვა 145 | Comment[kk]=Ойын-сауық 146 | Comment[km]=ល្បែង និងការកំសាន្ដ 147 | Comment[kn]=ಆಟಗಳು ಹಾಗು ರಂಜನೆಗಳು 148 | Comment[ko]=게임 메뉴 149 | Comment[ku]=Lîstik û demxweşî 150 | Comment[ky]=Оюндар жана эс алуу 151 | Comment[lg]=Mizannyo n'eby'okwesanyusa 152 | Comment[lt]=Žaidimai ir pramogos 153 | Comment[lv]=Spēles un izklaides programmas 154 | Comment[mai]=खेल आ मनोरंजन 155 | Comment[mg]=lalao sy fialam-boly 156 | Comment[mk]=Игри и забава 157 | Comment[ml]=കളികളും വിനോദങ്ങളും 158 | Comment[mn]=Тоглоом, зугаа цэнгэл 159 | Comment[mr]=खेळ व मनोरंजन 160 | Comment[ms]=Permainan dan hiburan 161 | Comment[nb]=Spill og underholdning 162 | Comment[ne]=खेल र मनोरञ्जन 163 | Comment[nl]=Spelletjes en vermaak 164 | Comment[nn]=Spel og underhaldning 165 | Comment[oc]=Jòcs e divertiments 166 | Comment[or]=ଖେଳ ଏବଂ ମନୋରଞ୍ଜନ 167 | Comment[pa]=ਖੇਡਾਂ ਅਤੇ ਮਨੋਰੰਜਨ 168 | Comment[pl]=Gry i rozrywka 169 | Comment[ps]=لوبې او مهالتيري 170 | Comment[pt]=Jogos e diversões 171 | Comment[pt_BR]=Jogos e diversões 172 | Comment[ro]=Jocuri și amuzamente 173 | Comment[ru]=Игры и развлечения 174 | Comment[si]=ක්‍රිඩා සහ විනොදාශ්වාද 175 | Comment[sk]=Hry a zábava 176 | Comment[sl]=Igre in zabava 177 | Comment[sq]=Lojra dhe argëtime 178 | Comment[sr]=Игре и забава 179 | Comment[sr@latin]=Igre i zabava 180 | Comment[sv]=Spel och underhållning 181 | Comment[ta]=விளையாட்டுகள் மற்றும் பொழுதுப்போக்குகள் 182 | Comment[te]=ఆటలు మరియు వినోదకాలు 183 | Comment[th]=เกมและความบันเทิง 184 | Comment[tr]=Oyun ve eğlencelikler 185 | Comment[tt_RU]=Уеннар һәм күңел ачу 186 | Comment[ug]=ئويۇن ۋە كۆڭۈل ئېچىش پروگراممىلىرى 187 | Comment[uk]=Ігри та розваги 188 | Comment[ur]=گیم اور کھیل 189 | Comment[ur_PK]=گیم اور کھیل 190 | Comment[uz@cyrillic]=Ўйинлар ва кўнгилочар дастурлар 191 | Comment[vi]=Trò chơi và giải trí 192 | Comment[xh]=Imidlalo nokuzihlekisa 193 | Comment[zh_CN]=游戏和休闲 194 | Comment[zh_HK]=遊戲及娛樂 195 | Comment[zh_TW]=遊戲及娛樂 196 | Icon=applications-games 197 | Type=Directory 198 | -------------------------------------------------------------------------------- /desktop-directories/graphics.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Graphics 3 | Name[af]=Grafika 4 | Name[ar]=الرّسوميّات 5 | Name[as]=গ্ৰাফিক্স 6 | Name[ast]=Gráficos 7 | Name[be]=Графіка 8 | Name[be@latin]=Hrafika 9 | Name[bg]=Графика 10 | Name[bn]=গ্রাফিক্স 11 | Name[bn_IN]=গ্রাফিক্স 12 | Name[br]=Grafikoù 13 | Name[ca]=Gràfics 14 | Name[cs]=Grafika 15 | Name[cy]=Graffeg 16 | Name[da]=Grafik 17 | Name[de]=Grafik 18 | Name[dz]=ཚད་རིས། 19 | Name[el]=Γραφικά 20 | Name[en_CA]=Graphics 21 | Name[en_GB]=Graphics 22 | Name[eo]=Grafikoj 23 | Name[es]=Gráficos 24 | Name[es_VE]=Gráficos 25 | Name[et]=Graafika 26 | Name[eu]=Irudiak 27 | Name[fa]=گرافیک 28 | Name[fi]=Grafiikka 29 | Name[fo]=Teknindi 30 | Name[fr]=Graphisme 31 | Name[frp]=Grafics 32 | Name[fur]=Grafiche 33 | Name[ga]=Grafaic 34 | Name[gl]=Gráficos 35 | Name[gn]=Baemo Ra'angaa Jajapoa 36 | Name[gu]=ગ્રાફિક્સ 37 | Name[he]=גרפיקה 38 | Name[hi]=आलेखी 39 | Name[hr]=Grafika 40 | Name[hu]=Grafika 41 | Name[hy]=Գրաֆիկա 42 | Name[id]=Grafis 43 | Name[io]=Grafikarti 44 | Name[is]=Myndefni 45 | Name[it]=Grafica 46 | Name[ja]=グラフィックス 47 | Name[ka]=გრაფიკა 48 | Name[kk]=Бейнелеу 49 | Name[km]=ក្រាហ្វិក 50 | Name[kn]=ಗ್ರಾಫಿಕ್ಸ್ 51 | Name[ko]=그래픽 52 | Name[ku]=Grafîk 53 | Name[ky]=Графика 54 | Name[lg]=Eby'ebifaananyi 55 | Name[lt]=Grafika 56 | Name[lv]=Grafikas 57 | Name[mai]=आलेखी 58 | Name[mg]=Sary 59 | Name[mk]=Графика 60 | Name[ml]=ഗ്രാഫിക്സ് 61 | Name[mn]=График 62 | Name[mr]=चित्र-विज्ञान 63 | Name[ms]=Grafik 64 | Name[nb]=Grafikk 65 | Name[ne]=ग्राफिक्स 66 | Name[nl]=Grafisch 67 | Name[nn]=Bilete 68 | Name[oc]=Grafisme 69 | Name[or]=ଆଲେଖୀ 70 | Name[pa]=ਗਰਾਫ਼ਿਕਸ 71 | Name[pl]=Grafika 72 | Name[ps]=کښنيزونه 73 | Name[pt]=Gráficos 74 | Name[pt_BR]=Gráficos 75 | Name[ro]=Grafică 76 | Name[ru]=Графика 77 | Name[rw]=Ibishushanyo 78 | Name[si]=චිත්‍ර 79 | Name[sk]=Grafika 80 | Name[sl]=Grafika 81 | Name[sq]=Grafikë 82 | Name[sr]=Графика 83 | Name[sr@latin]=Grafika 84 | Name[sv]=Grafik 85 | Name[ta]=வரைகலை 86 | Name[te]=గ్రాఫిక్స్ 87 | Name[th]=รูปภาพ 88 | Name[tr]=Grafik 89 | Name[tt_RU]=Графика 90 | Name[ug]=گرافىك 91 | Name[uk]=Графіка 92 | Name[ur]=گریفکس 93 | Name[ur_PK]=گریفکس 94 | Name[uz@cyrillic]=Графика 95 | Name[vi]=Đồ họa 96 | Name[xh]=Iimo zezibonakalisi-nkqubo 97 | Name[zh_CN]=图像 98 | Name[zh_HK]=美工繪圖 99 | Name[zh_TW]=美工繪圖 100 | Comment=Graphics applications 101 | Comment[af]=Grafika-toepassings 102 | Comment[ar]=تطبيقات الرّسوم 103 | Comment[as]=চাত্ৰাঙ্কিত অনুপ্ৰয়োগ 104 | Comment[ast]=Aplicaciones gráfiques 105 | Comment[be]=Графічныя праграмы 106 | Comment[be@latin]=Hrafičnyja aplikacyi 107 | Comment[bg]=Програми за работа с графика 108 | Comment[bn]=গ্রাফিক্স অ্যাপ্লিকেশন 109 | Comment[bn_IN]=গ্রাফিক্স অ্যাপ্লিকেশন 110 | Comment[br]=Poelladoù grafikoù 111 | Comment[ca]=Aplicacions gràfiques 112 | Comment[cs]=Aplikace pro grafiku 113 | Comment[cy]=Rhaglennu graffeg 114 | Comment[da]=Grafikprogrammer 115 | Comment[de]=Anwendungen zur Grafikbearbeitung 116 | Comment[dz]=ཚད་རིས་ཀྱི་གློག་རིམ། 117 | Comment[el]=Εφαρμογές γραφικών 118 | Comment[en_CA]=Graphics applications 119 | Comment[en_GB]=Graphics applications 120 | Comment[eo]=Grafikaj aplikaĵoj 121 | Comment[es]=Aplicaciones gráficas 122 | Comment[es_VE]=Aplicaciones gráficas 123 | Comment[et]=Graafikatöötlusprogrammid 124 | Comment[eu]=Irudi aplikazioak 125 | Comment[fa]=برنامه‌های گرافیکی 126 | Comment[fi]=Grafiikkasovellukset 127 | Comment[fo]=Teknindis nýtsluskipanir 128 | Comment[fr]=Applications graphiques 129 | Comment[frp]=Applicacions grafiques 130 | Comment[fur]=Aplicazions di grafiche 131 | Comment[ga]=Feidhmchláir grafaice 132 | Comment[gl]=Aplicacións de gráficos 133 | Comment[gn]=Baemo Ra'angaa Jajapoa Oia 134 | Comment[gu]=ગ્રાફિક્સ કાર્યક્રમો 135 | Comment[he]=יישומים גרפיים 136 | Comment[hi]=आलेखी अनुप्रयोग 137 | Comment[hr]=Grafičke aplikacije 138 | Comment[hu]=Grafikai alkalmazások 139 | Comment[hy]=Գրաֆիկական ծրագրեր 140 | Comment[id]=Aplikasi grafis 141 | Comment[io]=Programi pri grafikarti 142 | Comment[is]=Myndefnisforrit 143 | Comment[it]=Applicazioni grafiche 144 | Comment[ja]=グラフィックス関連のアプリケーションです 145 | Comment[ka]=გრაფიკული პროგრამები 146 | Comment[kk]=Графикалық қолданбалары 147 | Comment[km]=កម្មវិធីក្រាហ្វិក 148 | Comment[kn]=ಗ್ರಾಫಿಕ್ಸ್ ಅನ್ವಯಗಳು 149 | Comment[ko]=그래픽 응용프로그램 150 | Comment[ku]=Sepanên grafîkê 151 | Comment[ky]=Графикалык иштемелер 152 | Comment[lg]=Puloguramu ezikola ku bifaananyi 153 | Comment[lt]=Grafinės programos 154 | Comment[lv]=Grafikas programmas 155 | Comment[mai]=आलेखी अनुप्रयोगसभ 156 | Comment[mg]=Rindran'asa fanaovan-tsary 157 | Comment[mk]=Графички апликации 158 | Comment[ml]=ഗ്രാഫിക്സ് പ്രയോഗങ്ങള്‍ 159 | Comment[mn]=График програмууд 160 | Comment[mr]=चित्र-विज्ञान उपकरणं 161 | Comment[ms]=Aplikasi Grafik 162 | Comment[nb]=Grafiske programmer 163 | Comment[ne]=ग्राफिक्स अनुप्रयोग 164 | Comment[nl]=Grafische toepassingen 165 | Comment[nn]=Biletprogram 166 | Comment[oc]=Logicials grafics 167 | Comment[or]=ଆଲେଖୀ ପ୍ରୟୋଗ 168 | Comment[pa]=ਗਰਾਫ਼ਿਕਸ ਕਾਰਜ 169 | Comment[pl]=Programy graficzne 170 | Comment[ps]=کښنيزونو کاريالونه 171 | Comment[pt]=Aplicações gráficas 172 | Comment[pt_BR]=Aplicativos gráficos 173 | Comment[ro]=Programe de grafică 174 | Comment[ru]=Графические приложения 175 | Comment[si]=චිත්‍රක යෙදුම් 176 | Comment[sk]=Grafické aplikácie 177 | Comment[sl]=Grafični programi 178 | Comment[sq]=Programe grafiku 179 | Comment[sr]=Графички програми 180 | Comment[sr@latin]=Grafički programi 181 | Comment[sv]=Grafikprogram 182 | Comment[ta]=வரைகலை பயன்பாடுகள் 183 | Comment[te]=గ్రాఫిక్స్ అనువర్తనాలు 184 | Comment[th]=โปรแกรมสำหรับรูปภาพ 185 | Comment[tr]=Grafik uygulamaları 186 | Comment[tt_RU]=График кушымталар 187 | Comment[ug]=گرافىك پروگراممىلىرى 188 | Comment[uk]=Графічні програми 189 | Comment[ur]=گریفکس اطلاقیہ جات 190 | Comment[ur_PK]=گریفکس اطلاقیہ جات 191 | Comment[uz@cyrillic]=Графика дастурлари 192 | Comment[vi]=Ứng dụng đồ họa 193 | Comment[xh]=Iinkqubo ngeemo zezibonakalisi-nkqubo 194 | Comment[zh_CN]=图像应用程序 195 | Comment[zh_HK]=繪圖工具 196 | Comment[zh_TW]=繪圖工具 197 | Icon=applications-graphics 198 | Type=Directory 199 | -------------------------------------------------------------------------------- /desktop-directories/internet-and-network.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Internet and Network 3 | Name[af]=Internet en netwerk 4 | Name[ar]=الإنترنت والشّبكة 5 | Name[as]=ইন্টাৰনেট ও নেটৱৰ্ক 6 | Name[ast]=Internet y rede 7 | Name[be]=Інтэрнэт і сетка 8 | Name[be@latin]=Internet i sietka 9 | Name[bg]=Интернет и мрежа 10 | Name[bn]=ইন্টারনেট এবং নেটওয়ার্ক 11 | Name[bn_IN]=ইন্টারনেট ও নেটওয়ার্ক 12 | Name[ca]=Internet i xarxa 13 | Name[cs]=Internet a síť 14 | Name[da]=Internet og netværk 15 | Name[de]=Internet und Netzwerk 16 | Name[dz]=ཨིན་ཊར་ནེཊི་དང་ཡོངས་འབྲེལ་ 17 | Name[el]=Διαδίκτυο και δίκτυο 18 | Name[en_GB]=Internet and Network 19 | Name[eo]=Interreto kaj reto 20 | Name[es]=Internet y red 21 | Name[es_VE]=Internet y red 22 | Name[et]=Internet ja võrk 23 | Name[eu]=Internet eta sarea 24 | Name[fa]=اینترنت و شبکه 25 | Name[fi]=Internet ja verkko 26 | Name[fo]=Alnet og net 27 | Name[fr]=Internet et réseau 28 | Name[frp]=Internet é Téla 29 | Name[fur]=Internet e Rêt 30 | Name[ga]=Idirlíon agus Líonra 31 | Name[gl]=Internet e rede 32 | Name[gu]=ઈન્ટરનેટ અને નેટવર્ક 33 | Name[he]=אינטרט ורשת 34 | Name[hi]=इंटरनेट व संजाल 35 | Name[hr]=Internet i Mreža 36 | Name[hu]=Internet és hálózat 37 | Name[hy]=Ինտերնետ և Ցանց 38 | Name[id]=Internet dan Jaringan 39 | Name[io]=Interreto e Reto 40 | Name[is]=Internet og netkerfi 41 | Name[it]=Internet e rete 42 | Name[ja]=インターネットとネットワーク 43 | Name[kk]=Интернет және желі 44 | Name[km]=អ៊ីនធឺណិត និងបណ្ដាញ 45 | Name[kn]=ಅಂತರ್ಜಾಲ ಹಾಗು ಜಾಲಬಂಧ 46 | Name[ko]=인터넷과 네트워크 47 | Name[ku]=Înternet û Tor 48 | Name[lg]=Ebya Yintaneti n'okuwulirizagana ne kompyuta ndala 49 | Name[lt]=Internetas ir tinklas 50 | Name[lv]=Internets un tīkli 51 | Name[mk]=Интернет и мрежа 52 | Name[ml]=ഇന്റര്‍നെറ്റും ശൃംഖലയും 53 | Name[mr]=महाजाळ व संजाळ 54 | Name[ms]=Internet dan Rangkaian 55 | Name[nb]=Internett og nettverk 56 | Name[ne]=इन्टरनेट र सञ्जाल 57 | Name[nl]=Internet en netwerk 58 | Name[nn]=Internett og nettverk 59 | Name[oc]=Internet e ret 60 | Name[or]=ଇନ୍ଟରନେଟ ଏବଂ ନେଟୱର୍କ 61 | Name[pa]=ਇੰਟਰਨੈੱਟ ਅਤੇ ਨੈੱਟਵਰਕ 62 | Name[pl]=Internet i sieć 63 | Name[ps]=اېنټرنېټ او ځال 64 | Name[pt]=Internet e rede 65 | Name[pt_BR]=Internet e rede 66 | Name[ro]=Internet și rețea 67 | Name[ru]=Интернет и сеть 68 | Name[si]=අන්තර්ජාලය සහ ජාලය 69 | Name[sk]=Internet a sieť 70 | Name[sl]=Internet in omrežje 71 | Name[sq]=Internet dhe Rrjet 72 | Name[sr]=Интернет и мрежа 73 | Name[sr@latin]=Internet i mreža 74 | Name[sv]=Internet och nätverk 75 | Name[ta]=இணையம் மற்றும் வலையமைப்பு 76 | Name[te]=అంతర్జాలం మరియు నెట్‌వర్క్ 77 | Name[th]=อินเทอร์เน็ตและเครือข่าย 78 | Name[tr]=İnternet ve Ağ 79 | Name[tt_RU]=Интернет һәм Челтәр 80 | Name[ug]=ئىنتېرنېت ۋە تور 81 | Name[uk]=Інтернет та мережа 82 | Name[ur]=انٹرنیٹ اور نیٹ ورک 83 | Name[ur_PK]=انٹرنیٹ اور نیٹ ورک 84 | Name[uz@cyrillic]=Интернет ва тармоқ 85 | Name[vi]=Internet và mạng 86 | Name[zh_CN]=互联网和网络 87 | Name[zh_HK]=互聯網及網絡 88 | Name[zh_TW]=網際網路及網路 89 | Comment=Network-related settings 90 | Comment[af]=Netwerkverwante instellings 91 | Comment[ar]=الإعدادات المتعلّقة بالشّبكة 92 | Comment[as]=নেটৱৰ্ক সংক্ৰান্ত বৈশিষ্ট্য 93 | Comment[ast]=Opciones rellacionaes col trabayu en rede 94 | Comment[be]=Настаўленні сеткі 95 | Comment[be@latin]=Sietkavyja nałady 96 | Comment[bg]=Настройки за мрежата 97 | Comment[bn]=নেটওয়ার্ক সম্পর্কিত সেটিং 98 | Comment[bn_IN]=নেটওয়ার্ক সংক্রান্ত বৈশিষ্ট্য 99 | Comment[ca]=Paràmetres de xarxa 100 | Comment[cs]=Nastavení související se sítí 101 | Comment[da]=Netværksrelaterede indstillinger 102 | Comment[de]=Netzwerkbezogene Einstellungen 103 | Comment[dz]=ཡོང་འབྲེལ་དང་འབྲེལ་བའི་སྒྲིག་སྟངས་ 104 | Comment[el]=Ρυθμίσεις για το δίκτυο 105 | Comment[en_GB]=Network-related settings 106 | Comment[eo]=Ret-rilataj agordoj 107 | Comment[es]=Propiedades relacionadas con la red 108 | Comment[es_VE]=Propiedades relacionadas con la red 109 | Comment[et]=Võrguga seotud sätted 110 | Comment[eu]=Sareko ezarpenak 111 | Comment[fa]=تنظیمات مربوط به شبکه 112 | Comment[fi]=Verkkoon liittyvät asetukset 113 | Comment[fo]=Net tengdar setingar 114 | Comment[fr]=Paramètres concernant le réseau 115 | Comment[frp]=Configuracion da Téla 116 | Comment[fur]=Impostazions de rêt 117 | Comment[ga]=Socruithe líonra-gaolta 118 | Comment[gl]=Configuracións relacionadas coa rede 119 | Comment[gu]=નેટવર્ક-સંબંધિત સુયોજનો 120 | Comment[he]=הגדרות רשת 121 | Comment[hi]=संजाल संबंधित सेटिंग 122 | Comment[hr]=Mrežne postavke 123 | Comment[hu]=Hálózattal kapcsolatos beállítások 124 | Comment[hy]=Ցանցային հատկություններ 125 | Comment[id]=Penataan terkait jaringan 126 | Comment[io]=Retala setuesi 127 | Comment[is]=Netkerfistengdar stillingar 128 | Comment[it]=Impostazioni relative alla rete 129 | Comment[ja]=ネットワークに関連する設定を行います 130 | Comment[kk]=Желіге байланысты баптаулар 131 | Comment[km]=ការកំណត់ដែលទាក់ទងជាមួយនឹងបណ្ដាញ 132 | Comment[kn]=ಜಾಲಬಂಧ-ಸಂಬಂಧಿತ ಸಂಯೋಜನೆಗಳು 133 | Comment[ko]=네트워크와 관련된 설정을 합니다 134 | Comment[ku]=Mîhengên têkildarî torê 135 | Comment[lg]=Entegeka ezifuga okuwulirizagana ne kompyuta ndala 136 | Comment[lt]=Su tinklu susiję nustatymai 137 | Comment[lv]=Ar tīklu saistīti iestatījumi 138 | Comment[mk]=Поставувања кои се однесуваат на мрежа 139 | Comment[ml]=ശൃംഖലയുമായി ബന്ധപ്പെട്ട സജ്ജീകരണങ്ങള്‍ 140 | Comment[mr]=संजाळ-संबंधी संयोजना 141 | Comment[ms]=Tetapan rangkaian-berkaitan 142 | Comment[nb]=Nettverksrelaterte innstillinger 143 | Comment[ne]=सञ्जाल सम्बन्धि सेटिङ 144 | Comment[nl]=Netwerkgerelateerde instellingen 145 | Comment[nn]=Nettverksinnstillingar 146 | Comment[oc]=Paramètres en relacion amb la ret 147 | Comment[or]=ନେଟୱର୍କ ସମ୍ପର୍କୀୟ ବିନ୍ୟାସ 148 | Comment[pa]=ਨੈੱਟਵਰਕ ਨਾਲ ਸਬੰਧਤ ਸੈਟਿੰਗ 149 | Comment[pl]=Ustawienia związane z siecią 150 | Comment[ps]=ځال پورې تړلې امستنې 151 | Comment[pt]=Definições de rede 152 | Comment[pt_BR]=Configurações de rede 153 | Comment[ro]=Configurări pentru rețea 154 | Comment[ru]=Параметры, относящиеся к настройке сети 155 | Comment[si]=ජාලය සම්බන්ද සැකසුම් 156 | Comment[sk]=Nastavenia súvisiace so sieťou 157 | Comment[sl]=Nastavitve omrežja 158 | Comment[sq]=Rregullime lidhur me Rrjetin 159 | Comment[sr]=Мрежна подешавања 160 | Comment[sr@latin]=Mrežna podešavanja 161 | Comment[sv]=Nätverksrelaterade inställningar 162 | Comment[ta]=வலையமைப்பு சார்ந்த வடிவமைப்புகள் 163 | Comment[te]=నెట్‌వర్క్-సంబంధిత అమరికలు 164 | Comment[th]=ตั้งค่าเกี่ยวกับเครือข่าย 165 | Comment[tr]=Ağ ile ilgili ayarlar 166 | Comment[tt_RU]=Челтәргә бәйләнешле көйләүләр 167 | Comment[ug]=تورغا مۇناسىۋەتلىك تەڭشەك 168 | Comment[uk]=Мережні налаштування 169 | Comment[ur]=نیٹ ورک سے متعلقہ ترتیبات 170 | Comment[ur_PK]=نیٹ ورک سے متعلقہ ترتیبات 171 | Comment[uz@cyrillic]=Тармоққа оид мосламалар 172 | Comment[vi]=Thiết lập mạng 173 | Comment[zh_CN]=网络相关的设置 174 | Comment[zh_HK]=網絡相關設定 175 | Comment[zh_TW]=網路相關設定 176 | Icon=preferences-system-network 177 | Type=Directory 178 | -------------------------------------------------------------------------------- /desktop-directories/office.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Office 3 | Name[af]=Kantoor 4 | Name[ar]=المكتب 5 | Name[as]=কাৰ্যালয় 6 | Name[ast]=Oficina 7 | Name[be]=Офіс 8 | Name[be@latin]=Ofis 9 | Name[bg]=Офис 10 | Name[bn]=অফিস 11 | Name[bn_IN]=অফিস 12 | Name[br]=Bureveg 13 | Name[ca]=Oficina 14 | Name[cs]=Kancelář 15 | Name[cy]=Swyddfa 16 | Name[da]=Kontor 17 | Name[de]=Büro 18 | Name[dz]=ཡིག་ཚང་། 19 | Name[el]=Γραφείο 20 | Name[en_CA]=Office 21 | Name[en_GB]=Office 22 | Name[eo]=Oficejo 23 | Name[es]=Oficina 24 | Name[es_VE]=Oficina 25 | Name[et]=Kontor 26 | Name[eu]=Bulegoa 27 | Name[fa]=اداری 28 | Name[fi]=Toimisto 29 | Name[fo]=Skrivstova 30 | Name[fr]=Bureautique 31 | Name[frp]=Oficio 32 | Name[fur]=Ufici 33 | Name[ga]=Oifig 34 | Name[gl]=Oficina 35 | Name[gn]=Nhamboparaa Regua 36 | Name[gu]=ઓફિસ 37 | Name[he]=משרד 38 | Name[hi]=कार्यालय 39 | Name[hr]=Ured 40 | Name[hu]=Iroda 41 | Name[hy]=Օֆիս 42 | Name[id]=Perkantoran 43 | Name[io]=Kontoro 44 | Name[is]=Skrifstofa 45 | Name[it]=Ufficio 46 | Name[ja]=オフィス 47 | Name[ka]=ოფისი 48 | Name[kk]=Кеңселік 49 | Name[km]=ការិយាល័យ 50 | Name[kn]=ಆಫೀಸ್ 51 | Name[ko]=오피스 52 | Name[ku]=Nivîsgeh 53 | Name[ky]=Офис 54 | Name[lg]=Puloguramu ez'omu wofiisi 55 | Name[lt]=Raštinė 56 | Name[lv]=Biroja 57 | Name[mai]=कार्यालय 58 | Name[mg]=Office 59 | Name[mk]=Канцелариски 60 | Name[ml]=ഓഫീസ് 61 | Name[mn]=Бичиг хэрэг, офис 62 | Name[mr]=कार्यालय 63 | Name[ms]=Pejabat 64 | Name[nb]=Kontorstøtte 65 | Name[ne]=कार्यालय 66 | Name[nl]=Kantoor 67 | Name[nn]=Kontor 68 | Name[oc]=Burèu 69 | Name[or]=କାର୍ଯ୍ଯାଳୟ 70 | Name[pa]=ਦਫ਼ਤਰ 71 | Name[pl]=Biuro 72 | Name[ps]=افس 73 | Name[pt]=Produtividade 74 | Name[pt_BR]=Escritório 75 | Name[ro]=Birou 76 | Name[ru]=Офис 77 | Name[rw]=Ofise 78 | Name[si]=කාර්‍යයාලීය 79 | Name[sk]=Kancelária 80 | Name[sl]=Pisarna 81 | Name[sq]=Zyrë 82 | Name[sr]=Канцеларија 83 | Name[sr@latin]=Kancelarija 84 | Name[sv]=Kontor 85 | Name[ta]=அலுவலகம் 86 | Name[te]=కార్యాలయం 87 | Name[th]=สำนักงาน 88 | Name[tr]=Ofis 89 | Name[tt_RU]=Офис 90 | Name[ug]=ئىشخانا 91 | Name[uk]=Офіс 92 | Name[ur]=آفس 93 | Name[ur_PK]=آفس 94 | Name[uz@cyrillic]=Офис 95 | Name[vi]=Văn phòng 96 | Name[xh]=i-Ofisi 97 | Name[zh_CN]=办公 98 | Name[zh_HK]=辦公 99 | Name[zh_TW]=辦公 100 | Comment=Office Applications 101 | Comment[af]=Kantoortoepassings 102 | Comment[ar]=التّطبيقات المكتبيّة 103 | Comment[as]=কাৰ্যালয়ৰ অনুপ্ৰয়োগ 104 | Comment[ast]=Aplicaciones d'Oficina 105 | Comment[be]=Офісныя праграмы 106 | Comment[be@latin]=Ofisnyja prahramy 107 | Comment[bg]=Офисни програми 108 | Comment[bn]=অফিস অ্যাপলিকেশন 109 | Comment[bn_IN]=অফিস অ্যাপলিকেশন 110 | Comment[br]=Arloadoù bureveg all 111 | Comment[ca]=Aplicacions d'oficina 112 | Comment[cs]=Kancelářské aplikace 113 | Comment[cy]=Rhaglenni Swyddfa 114 | Comment[da]=Kontorprogrammer 115 | Comment[de]=Büroanwendungen 116 | Comment[dz]=ཡིག་ཚང་གི་གློག་རིམ། 117 | Comment[el]=Εφαρμογές γραφείου 118 | Comment[en_CA]=Office Applications 119 | Comment[en_GB]=Office Applications 120 | Comment[eo]=Oficejaj aplikaĵoj 121 | Comment[es]=Aplicaciones de oficina 122 | Comment[es_VE]=Aplicaciones de oficina 123 | Comment[et]=Kontoritarkvara 124 | Comment[eu]=Bulegorako aplikazioak 125 | Comment[fa]=برنامه‌های اداری 126 | Comment[fi]=Toimistosovellukset 127 | Comment[fo]=Skrivstovu nýtsluskipanir 128 | Comment[fr]=Applications bureautiques 129 | Comment[frp]=Aplicacions d'oficio 130 | Comment[fur]=Aplicazions di ufici 131 | Comment[ga]=Feidhmchláir Oifige 132 | Comment[gl]=Aplicacións de oficina 133 | Comment[gn]=Jaekaa Omboparaa 134 | Comment[gu]=ઓફિસ કાર્યક્રમો 135 | Comment[he]=יישומים משרדיים 136 | Comment[hi]=कार्यालय अनुप्रयोग 137 | Comment[hr]=Uredske Aplikacije 138 | Comment[hu]=Irodai alkalmazások 139 | Comment[hy]=Օֆիսային Ծրագրեր 140 | Comment[id]=Aplikasi Perkantoran 141 | Comment[io]=Kontorala Programi 142 | Comment[is]=Skrifstofuforrit 143 | Comment[it]=Applicazioni da ufficio 144 | Comment[ja]=オフィス・アプリケーションです 145 | Comment[ka]=საოფისე პროგრამები 146 | Comment[kk]=Кеңселік қолданбалар 147 | Comment[km]=កម្មវិធីការិយាល័យ 148 | Comment[kn]=ಆಫೀಸ್ ಅನ್ವಯಗಳು 149 | Comment[ko]=오피스 응용프로그램 150 | Comment[ku]=Sepandinên Ofîsê 151 | Comment[ky]=Офистик иштемелер 152 | Comment[lg]=Pulogurmau ez'omu wofiisi 153 | Comment[lt]=Raštinės programos 154 | Comment[lv]=Biroja aplikācijas 155 | Comment[mai]=कार्यालयक अनुप्रयोग 156 | Comment[mg]=Rindran'asa Office 157 | Comment[mk]=Канцелариски апликации 158 | Comment[ml]=ഓഫീസ് പ്രയോഗങ്ങള്‍ 159 | Comment[mn]=Офисын програмууд 160 | Comment[mr]=कार्यालय संबंधित उपकरणं 161 | Comment[ms]=Aplikasi Pejabat 162 | Comment[nb]=Kontorstøtteprogrammer 163 | Comment[ne]=कार्यालय अनुप्रयोग 164 | Comment[nl]=Kantoortoepassingen 165 | Comment[nn]=Kontorprogramvare 166 | Comment[oc]=Logicials pel burèu 167 | Comment[or]=କାର୍ଯ୍ଯାଳୟ ପ୍ରୟୋଗ 168 | Comment[pa]=ਦਫ਼ਤਰ ਕਾਰਜ 169 | Comment[pl]=Programy biurowe 170 | Comment[ps]=د افس کاريالونه 171 | Comment[pt]=Aplicações de produtividade 172 | Comment[pt_BR]=Aplicativos de escritório 173 | Comment[ro]=Programe pentru birou 174 | Comment[ru]=Офисные приложения 175 | Comment[si]=කාර්‍යාලිය යෙදුම් 176 | Comment[sk]=Kancelárske aplikácie 177 | Comment[sl]=Pisarniški programi 178 | Comment[sq]=Programe Zyre 179 | Comment[sr]=Програми за канцеларију 180 | Comment[sr@latin]=Programi za kancelariju 181 | Comment[sv]=Kontorsprogram 182 | Comment[ta]=அலுவலகப் பயன்பாடுகள் 183 | Comment[te]=కార్యాలయ అనువర్తనాలు 184 | Comment[th]=โปรแกรมสำหรับสำนักงาน 185 | Comment[tr]=Ofis Uygulamaları 186 | Comment[tt_RU]=Офис кушымталары 187 | Comment[ug]=ئىشخانا پروگراممىلىرى 188 | Comment[uk]=Офісні програми 189 | Comment[ur]=آفس اطلاقیہ جات 190 | Comment[ur_PK]=آفس اطلاقیہ جات 191 | Comment[uz@cyrillic]=Офис дастурлари 192 | Comment[vi]=Ứng dụng văn phòng 193 | Comment[xh]=Iinkqubo ze-Ofisi 194 | Comment[zh_CN]=办公应用程序 195 | Comment[zh_HK]=辦公室軟件 196 | Comment[zh_TW]=辦公室軟體 197 | Icon=applications-office 198 | Type=Directory 199 | -------------------------------------------------------------------------------- /desktop-directories/other.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Other 3 | Name[af]=Ander 4 | Name[ar]=أخرى 5 | Name[as]=অন্যান্য 6 | Name[ast]=Otres 7 | Name[be]=Іншыя 8 | Name[be@latin]=Inšyja 9 | Name[bg]=Други 10 | Name[bn]=অন্যান্য 11 | Name[bn_IN]=অন্যান্য 12 | Name[br]=All 13 | Name[ca]=Altres 14 | Name[cs]=Ostatní 15 | Name[cy]=Eraill 16 | Name[da]=Andre 17 | Name[de]=Sonstige 18 | Name[dz]=གཞན། 19 | Name[el]=Άλλα 20 | Name[en_CA]=Other 21 | Name[en_GB]=Other 22 | Name[eo]=Alia 23 | Name[es]=Otras 24 | Name[es_VE]=Otras 25 | Name[et]=Muu 26 | Name[eu]=Bestelakoak 27 | Name[fa]=غیره 28 | Name[fi]=Muut 29 | Name[fo]=Annað 30 | Name[fr]=Autre 31 | Name[frp]=Autres 32 | Name[fur]=Altri 33 | Name[ga]=Eile 34 | Name[gl]=Outros 35 | Name[gn]=Amboae 36 | Name[gu]=અન્ય 37 | Name[he]=אחר 38 | Name[hi]=अन्य 39 | Name[hr]=Ostalo 40 | Name[hu]=Egyéb 41 | Name[hy]=Այլ 42 | Name[id]=Lainnya 43 | Name[io]=Altra 44 | Name[is]=Aðrir 45 | Name[it]=Altro 46 | Name[ja]=その他 47 | Name[ka]=სხვა 48 | Name[kk]=Басқалар 49 | Name[km]=ផ្សេងៗ 50 | Name[kn]=ಇತರೆ 51 | Name[ko]=기타 52 | Name[ku]=Yên din 53 | Name[ky]=Башкалар 54 | Name[lg]=Ebirala 55 | Name[lt]=Kitos 56 | Name[lv]=Citas programmas 57 | Name[mai]=आन 58 | Name[mg]=Hafa 59 | Name[mk]=Други 60 | Name[ml]=മറ്റുളളവ 61 | Name[mn]=Бусад 62 | Name[mr]=अन्य 63 | Name[ms]=Lain-lain 64 | Name[nb]=Annet 65 | Name[ne]=अन्य 66 | Name[nl]=Overig 67 | Name[nn]=Andre 68 | Name[oc]=Autre 69 | Name[or]=ଅନ୍ଯାନ୍ଯ 70 | Name[pa]=ਹੋਰ 71 | Name[pl]=Inne 72 | Name[ps]=نور 73 | Name[pt]=Outras 74 | Name[pt_BR]=Outros 75 | Name[ro]=Altele 76 | Name[ru]=Прочие 77 | Name[rw]=Ikindi 78 | Name[si]=වෙනත් 79 | Name[sk]=Ostatné 80 | Name[sl]=Drugo 81 | Name[sq]=Tjetër 82 | Name[sr]=Остало 83 | Name[sr@latin]=Ostalo 84 | Name[sv]=Övriga 85 | Name[ta]=மற்றவை 86 | Name[te]=ఇతర 87 | Name[th]=อื่นๆ 88 | Name[tr]=Diğer 89 | Name[tt_RU]=Башкалар 90 | Name[ug]=باشقا 91 | Name[uk]=Інші 92 | Name[ur]=دیگر 93 | Name[ur_PK]=دیگر 94 | Name[uz@cyrillic]=Бошқа 95 | Name[vi]=Khác 96 | Name[xh]=Ezinye 97 | Name[zh_CN]=其它 98 | Name[zh_HK]=其它 99 | Name[zh_TW]=其他 100 | Comment=Applications that did not fit in other categories 101 | Comment[af]=Toepassings wat nie in ander kategorieë pas nie 102 | Comment[ar]=تطبيقات لا تندرج تحت الفئات الأخرى 103 | Comment[as]=অন্যান্য শ্ৰেণীত অন্তৰ্গত নকৰা অনুপ্ৰয়োগ 104 | Comment[ast]=Aplicaciones que nun encaxen n'otres estayes 105 | Comment[be]=Праграмы, якія не трапілі ў іншыя катэгорыі 106 | Comment[be@latin]=Prahramy, jakija nie ŭvajšli ŭ inšyja kategoryi 107 | Comment[bg]=Програми, които не принадлежат към друга категория 108 | Comment[bn]=অ্যাপ্লিকেশন যেগুলো অন্য কোন শ্রেণীভূক্ত নয় 109 | Comment[bn_IN]=অন্যান্য শ্রেণীর মধ্যে অন্তর্গত না করা অ্যাপ্লিকেশন 110 | Comment[br]=An arloadoù n'int ket staget ouzh ur rummad bennak all 111 | Comment[ca]=Aplicacions que no encaixen en altres categories 112 | Comment[cs]=Aplikace nepatřící do jiných kategorií 113 | Comment[cy]=Rhaglenni nad ydynt yn ffitio yng nghategorïau eraill 114 | Comment[da]=Programmer som ikke passer i andre katagorier 115 | Comment[de]=Anwendungen, die in keine andere Kategorie eingeordnet werden können 116 | Comment[dz]=དབྱེ་ཁག་གཞན་ནང་ ཚུད་སྒྲིག་མེད་པའི་གློག་རིམ། 117 | Comment[el]=Εφαρμογές που δεν ταιριάζουν στις άλλες κατηγορίες 118 | Comment[en_CA]=Applications that do not fit in other categories 119 | Comment[en_GB]=Applications that do not fit in other categories 120 | Comment[eo]=Aplikaĵoj neagordaj aliajn kategoriojn 121 | Comment[es]=Aplicaciones que no entran en otras categorías 122 | Comment[es_VE]=Aplicaciones que no entran en otras categorías 123 | Comment[et]=Rakendused, mis ei sobinud teistesse kategooriatesse 124 | Comment[eu]=Aurreko kategorietan sartzen ez diren aplikazioak 125 | Comment[fa]=برنامه‌هایی که در مقولات دیگر جا نمی‌گیرند 126 | Comment[fi]=Muihin luokkiin sopimattomat sovellukset 127 | Comment[fo]=Nýtsluskipanir ið ikki passa í aðrar bólkar 128 | Comment[fr]=Applications qui n'entraient dans aucune autre catégorie 129 | Comment[frp]=Aplicacions que n'ant pâs trovâ de categorie 130 | Comment[fur]=Aplicazions ch'a no jentrin ta chês altres categoriis 131 | Comment[ga]=Feidhmchláir nár oiriúnaigh i gcatagóirí eile 132 | Comment[gl]=Aplicacións que non se axustan a outras categorías 133 | Comment[gn]=Amboae Regua Jaekaa 134 | Comment[gu]=કાર્યક્રમો કે જે અન્ય વર્ગોમાં નહિં બંધબેસે 135 | Comment[he]=יישומים שלא התאימו בקטגוריות אחרות 136 | Comment[hi]=अनुप्रयोग जो अन्य श्रेणी में सटीक नहीं बैठती है 137 | Comment[hr]=Aplikacije koje ne pripadaju ostalim kategorijama 138 | Comment[hu]=Más kategóriákba be nem sorolható alkalmazások 139 | Comment[hy]=Ծրագրեր, որոնք այլ կատեգորիաներին չեն համապատասխանում 140 | Comment[id]=Aplikasi yang tidak termasuk dalam kategori lain 141 | Comment[is]=Forrit sem passa ekki í aðra flokka 142 | Comment[it]=Applicazioni che non rientrano in altre categorie 143 | Comment[ja]=他のカテゴリにあてはまらないアプリケーション 144 | Comment[ka]=პროგრამები რომლებიც არც ერთ კატეგორიაში არ შედიან 145 | Comment[kk]=Басқа санаттарға жатпайтын қолданбалар 146 | Comment[km]=កម្មវិធីដែលមិនសម នឹងប្រភេទដទៃទៀត 147 | Comment[kn]=ಬೇರೆ ವರ್ಗಗಳಿಗೆ ಸೇರದೇ ಇರುವಂತಹ ಅನ್ವಯಗಳು 148 | Comment[ko]=어떤 범주에도 해당되지 않는 프로그램 149 | Comment[ku]=Sepanên ku nakevin kategoriyên din 150 | Comment[ky]=Эч бир категорияга туура келбеген иштемелер 151 | Comment[lg]=Puloguramu ezitalina kiti mwe zigwa 152 | Comment[lt]=Programos, kurios netiko kitose kategorijose 153 | Comment[lv]=Aplikācijas, kas neiederas nevienā citā kategorijā 154 | Comment[mai]=अनुप्रयोगसभ जे आन श्रेणीमे सटीक नहि छला 155 | Comment[mg]=Rindran'asa izay tsy tafiditra anaty sokajy hafa 156 | Comment[mk]=Апликации кои што не влегуваат во ниедна категорија 157 | Comment[ml]=മറ്റു് വിഭാഗങ്ങളില്‍ ഉള്‍‌പ്പെടാത്ത പ്രയോഗങ്ങള്‍ 158 | Comment[mn]=Бусад ангилалд тохирохгүй програмууд 159 | Comment[mr]=उपकरणं जे दुसरया कुठल्याही गटांत बसले नाही 160 | Comment[ms]=Aplikasi yang tak sesuai dalam kategori lain 161 | Comment[nb]=Programmer som ikke passer i andre kategorier 162 | Comment[ne]=अन्य कोटिमा नमिलेका अनुप्रयोग 163 | Comment[nl]=Toepassingen die niet passen in andere categorieën 164 | Comment[nn]=Program som ikkje passar i andre kategoriar 165 | Comment[oc]=Logicials que dintran pas dins las autras categorias 166 | Comment[or]=ପ୍ରୟୋଗ ଯାହାକି ଅନ୍ଯ କୌଣସି ଶ୍ରେଣୀ ସହିତ ଖାପ ଖାଇଲା ନାହିଁ 167 | Comment[pa]=ਕਾਰਜ, ਜੋ ਕਿ ਕਿਸੇ ਵਰਗ ਵਿੱਚ ਨਹੀਂ ਆਉਦੇ। 168 | Comment[pl]=Programy, które nie pasują do innych kategorii 169 | Comment[ps]=هغه کاريالونه چې په نورو ټولېو کې نه راځي 170 | Comment[pt]=Aplicações que não se enquadram em nenhuma outra categoria 171 | Comment[pt_BR]=Aplicativos que não se encaixam em outras categorias 172 | Comment[ro]=Programe ce nu se încadrează în alte categorii 173 | Comment[ru]=Приложения, не подпадающие ни под какие категории 174 | Comment[si]=වෙනත් ප්‍රභේද වලට අයත් නොවු යෙදුම් 175 | Comment[sk]=Aplikácie, ktoré nepatria do ostatných kategórií 176 | Comment[sl]=Programi, ki ne sodijo v druge kategorije 177 | Comment[sq]=Programe që nuk gjejnë vend në kategori të tjera 178 | Comment[sr]=Програми који не припадају осталим категоријама 179 | Comment[sr@latin]=Programi koji ne pripadaju ostalim kategorijama 180 | Comment[sv]=Program som inte passar in i någon annan kategori 181 | Comment[ta]=இதர இனங்களில் பொருந்தாத பயன்பாடுகள் 182 | Comment[te]=ఇతర విభాగాలలో ఇమడని అనువర్తనాలు 183 | Comment[th]=โปรแกรมที่ไม่อยู่ในหมวดอื่นๆ 184 | Comment[tr]=Diğer sınıflandırmalara girmeyen uygulamalar 185 | Comment[tt_RU]=Калган төркемнәргә кермәгән кушымталар 186 | Comment[ug]=باشقا تۈرلەرگە تەۋە بولمىغان پروگراممىلار 187 | Comment[uk]=Програми, що не входять до жодної категорії 188 | Comment[ur]=وہ اطلاقیہ جات جو کسی دوسرے زمرے میں نہیں آتے 189 | Comment[ur_PK]=وہ اطلاقیہ جات جو کسی دوسرے زمرے میں نہیں آتے 190 | Comment[uz@cyrillic]=Ҳеч қайси туркумга кирмайдиган дастурлар 191 | Comment[vi]=Chương trình chưa phân loại 192 | Comment[xh]=Iinkqubo ezingangeniyo kwezinye iindidi 193 | Comment[zh_CN]=无法分入其它类别的应用程序 194 | Comment[zh_HK]=不符合其它分類的軟件 195 | Comment[zh_TW]=不符合其他分類的軟體 196 | Icon=applications-other 197 | Type=Directory 198 | -------------------------------------------------------------------------------- /desktop-directories/system-tools.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=System Tools 3 | Name[af]=Stelselgereedskap 4 | Name[ar]=أدوات النّظام 5 | Name[as]=ব্যৱস্থাপ্ৰণালীৰ বিভিন্ন সৰঞ্জাম 6 | Name[ast]=Ferramientes del Sistema 7 | Name[be]=Сістэмныя інструменты 8 | Name[be@latin]=Systemnaje pryładździe 9 | Name[bg]=Системни инструменти 10 | Name[bn]=সিস্টেম টুল 11 | Name[bn_IN]=সিস্টেমের বিভিন্ন সরঞ্জাম 12 | Name[br]=Binvioù reizhiad 13 | Name[ca]=Eines de sistema 14 | Name[cs]=Systémové nástroje 15 | Name[cy]=Offer System 16 | Name[da]=Systemværktøjer 17 | Name[de]=Systemwerkzeuge 18 | Name[dz]=རིམ་ལུགས་ལག་ཆས། 19 | Name[el]=Εργαλεία συστήματος 20 | Name[en_CA]=System Tools 21 | Name[en_GB]=System Tools 22 | Name[eo]=Sistemaj iloj 23 | Name[es]=Herramientas del sistema 24 | Name[es_VE]=Herramientas del sistema 25 | Name[et]=Süsteemi tööriistad 26 | Name[eu]=Sistemaren tresnak 27 | Name[fa]=ابزارهای سیستم 28 | Name[fi]=Järjestelmätyökalut 29 | Name[fo]=Kervis amboð 30 | Name[fr]=Outils système 31 | Name[frp]=Utils du sistemo 32 | Name[fur]=Imprescj di sisteme 33 | Name[ga]=Uirlisí Córais 34 | Name[gl]=Ferramentas do sistema 35 | Name[gu]=સિસ્ટમ સાધનો 36 | Name[he]=כלי מערכת 37 | Name[hi]=सिस्टम उपकरण 38 | Name[hr]=Alati Sustava 39 | Name[hu]=Rendszereszközök 40 | Name[hy]=Համակարգի գործիքներ 41 | Name[id]=Peralatan Sistem 42 | Name[io]=Sistemala Utensili 43 | Name[is]=Kerfistól 44 | Name[it]=Strumenti di sistema 45 | Name[ja]=システムツール 46 | Name[ka]=სისტემური ხელსაწყოები 47 | Name[kk]=Жүйелік саймандар 48 | Name[km]=ឧបករណ៍ប្រព័ន្ធ 49 | Name[kn]=ಗಣಕ ಉಪಕರಣಗಳು 50 | Name[ko]=시스템 도구 51 | Name[ku]=Amûrên Pergalê 52 | Name[ky]=Системалык аспаптар 53 | Name[lg]=Ebiyamba okufuga sisitemu 54 | Name[lt]=Sistemos įrankiai 55 | Name[lv]=Sistēma rīki 56 | Name[mai]=सिस्टम अओजार 57 | Name[mg]=Fitaovan'ny rafitra 58 | Name[mk]=Системски алатки 59 | Name[ml]=സിസ്റ്റത്തിലെ പണിയായുധങ്ങള്‍ 60 | Name[mn]=Системийн хэрэгслүүд 61 | Name[mr]=यंत्रणे संबंधित अौजार 62 | Name[ms]=Alatan Sistem 63 | Name[nb]=Systemverktøy 64 | Name[ne]=प्रणाली उपकरण 65 | Name[nl]=Systeemgereedschap 66 | Name[nn]=Systemverktøy 67 | Name[oc]=Espleches sistèma 68 | Name[or]=ତନ୍ତ୍ର ଉପକରଣ 69 | Name[pa]=ਸਿਸਟਮ ਸੰਦ 70 | Name[pl]=Narzędzia systemowe 71 | Name[ps]=غونډال توکي 72 | Name[pt]=Ferramentas de sistema 73 | Name[pt_BR]=Sistema 74 | Name[ro]=Unelte de sistem 75 | Name[ru]=Системные 76 | Name[si]=පද්දති මෙවලම් 77 | Name[sk]=Systémové nástroje 78 | Name[sl]=Sistemska orodja 79 | Name[sq]=Vegla Sistemi 80 | Name[sr]=Системски алати 81 | Name[sr@latin]=Sistemski alati 82 | Name[sv]=Systemverktyg 83 | Name[ta]=கணிப்பொறிக் கருவிகள் 84 | Name[te]=వ్యవస్థ పనిముట్లు 85 | Name[th]=เครื่องมือระบบ 86 | Name[tr]=Sistem Araçları 87 | Name[tt_RU]=Система кораллары 88 | Name[ug]=سىستېما قوراللىرى 89 | Name[uk]=Системні утиліти 90 | Name[ur]=نظام اوزار 91 | Name[ur_PK]=نظام اوزار 92 | Name[uz@cyrillic]=Тизим воситалари 93 | Name[vi]=Công cụ hệ thống 94 | Name[xh]=Izixhobo Zeenkqubo 95 | Name[zh_CN]=系统工具 96 | Name[zh_HK]=系統工具 97 | Name[zh_TW]=系統工具 98 | Comment=System configuration and monitoring 99 | Comment[af]=Stelselopstelling en monitering 100 | Comment[ar]=ضبط النّظام ومراقبته 101 | Comment[as]=ব্যৱস্থাপ্ৰণালীৰ বৈশিষ্ট্যৰ বিন্যাস এবং নিৰীক্ষণ 102 | Comment[ast]=Configuración y monitorización del sistema 103 | Comment[be]=Канфігурацыя сістэмы і маніторынг 104 | Comment[be@latin]=Manitorynh i kanfihuracyja systemy 105 | Comment[bg]=Следене и настройване на системата 106 | Comment[bn]=সিস্টেম কনফিগারেশন এবং মনিটরিং 107 | Comment[bn_IN]=সিস্টেমের বৈশিষ্ট্য কনফিগারেশন এবং নিরীক্ষণ 108 | Comment[br]=Neuziadur hag eveshaerezh ar reizhiad 109 | Comment[ca]=Configuració i monitorització del sistema 110 | Comment[cs]=Nastavení a sledování systému 111 | Comment[cy]=Cyflunio a monitro system 112 | Comment[da]=Systemkonfiguration og -overvågning 113 | Comment[de]=Systemkonfiguration und -überwachung 114 | Comment[dz]=རིམ་ལུགས་རིམ་སྒྲིག་དང་ལྟ་རྟོག། 115 | Comment[el]=Ρύθμιση και έλεγχος συστήματος 116 | Comment[en_CA]=System configuration and monitoring 117 | Comment[en_GB]=System configuration and monitoring 118 | Comment[eo]=Agordi kaj kontroli sistemon 119 | Comment[es]=Configuración y monitorización del sistema 120 | Comment[es_VE]=Configuración y monitorización del sistema 121 | Comment[et]=Süsteemi seadistamine ja jälgimine 122 | Comment[eu]=Sistemaren konfigurazioa eta monitorizazioa 123 | Comment[fa]=پیکربندی و پایشگری سیستم 124 | Comment[fi]=Järjestelmäasetukset ja -seuranta 125 | Comment[fo]=Kervis samanseting og vøka 126 | Comment[fr]=Configuration et surveillance système 127 | Comment[fur]=Configurazion e monitoragjo di sisteme 128 | Comment[ga]=Cumraíocht agus monatóireacht an chórais 129 | Comment[gl]=Configuración e monitorización do sistema 130 | Comment[gu]=સિસ્ટમ રૂપરેખાંકન અને મોનિટરીંગ 131 | Comment[he]=הגדרת המערכת וניהול מעקב אחריה 132 | Comment[hi]=सिस्टम विन्यास व निरीक्षण 133 | Comment[hr]=Konfiguracija i nadzor sustava 134 | Comment[hu]=Rendszerbeállítás és megfigyelés 135 | Comment[hy]=Համակարգի կարգավորում և վերահսկում 136 | Comment[id]=Konfigurasi sistem dan pemantauan 137 | Comment[is]=Kerfisstillingar og vöktun 138 | Comment[it]=Configurazione e monitoraggio del sistema 139 | Comment[ja]=システムの設定と監視を行うプログラムです 140 | Comment[ka]=სისტემის კონფიგურაცია და მონიტორინგი 141 | Comment[kk]=Жүйелік баптаулар мен бақылау 142 | Comment[km]=ការកំណត់រចនាសម្ព័ន្ធ និង ការត្រួតពិនិត្យ 143 | Comment[kn]=ಗಣಕ ಸಂರಚನೆ ಹಾಗು ಪರಿವೀಕ್ಷಣೆ 144 | Comment[ko]=시스템 설정과 감시 145 | Comment[ku]=Avakirin û şopandina pergalê 146 | Comment[ky]=Системаны конфигурациялоо жана аны байкоо аспаптары 147 | Comment[lg]=Eby'okutegeka n'okukebera embeera ya sisitemu 148 | Comment[lt]=Sistemos konfigūravimas ir stebėjimas 149 | Comment[lv]=Sistēmas konfigurācija un pārraudzība 150 | Comment[mai]=सिस्टम बिन्यास आ निरीक्षण 151 | Comment[mg]=Fanefena sy fanaraha-maso ny rafitra 152 | Comment[mk]=Конфигурација и надгледување на системот 153 | Comment[ml]=സിസ്റ്റത്തിന്റെ ക്രമീകരണവും നിരീക്ഷണവും 154 | Comment[mn]=Системийн тохиргоо болон хяналт 155 | Comment[mr]=यंत्रणेचे सुसुत्रीकरण व संचलन 156 | Comment[ms]=Konfigurasi dan pemantauan sistem 157 | Comment[nb]=Systemkonfigurasjon og overvåking 158 | Comment[ne]=प्रणाली कन्फिगरेसन र अनुगमन 159 | Comment[nl]=Systeemconfiguratie en -controle 160 | Comment[nn]=Oppsett og overvaking av systemet 161 | Comment[oc]=Configuracion e susvelhança del sistèma 162 | Comment[or]=ତନ୍ତ୍ର ବିନ୍ଯାସ ଏବଂ ନିରୀକ୍ଷଣ 163 | Comment[pa]=ਸਿਸਟਮ ਸੰਰਚਨਾ ਅਤੇ ਨਿਗਰਾਨੀ 164 | Comment[pl]=Monitorowanie i konfiguracja systemu 165 | Comment[ps]=د غونډال سازونه او ليدنه 166 | Comment[pt]=Configuração de sistema e monitorização 167 | Comment[pt_BR]=Configuração e monitoramento do sistema 168 | Comment[ro]=Configurarea și monitorizarea sistemului 169 | Comment[ru]=Средства конфигурации и мониторинга системы 170 | Comment[si]=පද්දති සුසර කිරිම සහ නිරික්ෂණය 171 | Comment[sk]=Konfigurácia a sledovanie systému 172 | Comment[sl]=Nastavitve in nadzor nad sistemom 173 | Comment[sq]=Konfigurim dhe vëzhgim sistemi 174 | Comment[sr]=Подешавање и праћење система 175 | Comment[sr@latin]=Podešavanje i praćenje sistema 176 | Comment[sv]=Systemkonfiguration och systemövervakning 177 | Comment[ta]=கணிப்பொறி அமைப்பு மற்றும் கண்காணித்தல் 178 | Comment[te]=వ్యవస్థ స్వరూపణం మరియు పర్యవేక్షణ 179 | Comment[th]=ตั้งค่าและเฝ้าดูระบบ 180 | Comment[tr]=Sistem yapılandırma ve izleme 181 | Comment[tt_RU]=Системаны көйләү һәм күзәтү 182 | Comment[ug]=سىستېما سەپلەش ۋە كۆزىتىش 183 | Comment[uk]=Засоби налаштовування та контролю системи 184 | Comment[ur]=نظام کی وضع کاریاں اور معائنہ 185 | Comment[ur_PK]=نظام کی وضع کاریاں اور معائنہ 186 | Comment[uz@cyrillic]=Тизимни мослаш ва кузатиш 187 | Comment[vi]=Cấu hình và theo dõi hệ thống 188 | Comment[xh]=Umiselo-nkqubo lwendlela emisiweyo kunye nokuhlola 189 | Comment[zh_CN]=系统配置和监视 190 | Comment[zh_HK]=系統配置及監察 191 | Comment[zh_TW]=系統配置及監察 192 | Icon=applications-system 193 | Type=Directory 194 | -------------------------------------------------------------------------------- /desktop-directories/utility.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Accessories 3 | Name[af]=Toebehore 4 | Name[ar]=الملحقات 5 | Name[as]=আনুষঙ্গিক 6 | Name[ast]=Accesorios 7 | Name[be]=Інструменты 8 | Name[be@latin]=Aksesuary 9 | Name[bg]=Помощни програми 10 | Name[bn]=আনুসাঙ্গিক 11 | Name[bn_IN]=আনুষঙ্গিক 12 | Name[br]=Mavegoù 13 | Name[ca]=Accessoris 14 | Name[cs]=Příslušenství 15 | Name[cy]=Ategolion 16 | Name[da]=Tilbehør 17 | Name[de]=Zubehör 18 | Name[dz]=ཡན་ལག་ཅ་ཆས། 19 | Name[el]=Βοηθήματα 20 | Name[en_CA]=Accessories 21 | Name[en_GB]=Accessories 22 | Name[eo]=Utilaĵoj 23 | Name[es]=Accesorios 24 | Name[es_VE]=Accesorios 25 | Name[et]=Tarvikud 26 | Name[eu]=Gehigarriak 27 | Name[fa]=لوازم 28 | Name[fi]=Apuohjelmat 29 | Name[fo]=Tilhoyr 30 | Name[fr]=Accessoires 31 | Name[frp]=Accesòres 32 | Name[fur]=Robutis 33 | Name[ga]=Oiriúintí 34 | Name[gl]=Accesorios 35 | Name[gn]=Nhamba'eapoa Pygua 36 | Name[gu]=સહાયક કાર્યક્રમો 37 | Name[he]=עזרים 38 | Name[hi]=संलग्नक 39 | Name[hr]=Pomagala 40 | Name[hu]=Kellékek 41 | Name[hy]=Աքսեսուարներ 42 | Name[id]=Aksesoris 43 | Name[io]=Acesori 44 | Name[is]=Aukahlutir 45 | Name[it]=Accessori 46 | Name[ja]=アクセサリ 47 | Name[ka]=აქსესუარები 48 | Name[kk]=Қалыпты 49 | Name[km]=ប្រដាប់ប្រដា 50 | Name[kn]=ಸಲಕರಣೆಗಳು 51 | Name[ko]=보조 프로그램 52 | Name[ku]=Bernameyên Alîkar 53 | Name[ky]=Стандарттык иштемелер 54 | Name[lg]=Ebigonz'emirimu 55 | Name[lt]=Reikmenys 56 | Name[lv]=Piederumi 57 | Name[mai]=संलग्नक 58 | Name[mg]=Fiasana 59 | Name[mk]=Услужни 60 | Name[ml]=ഉപകരണങ്ങള്‍ 61 | Name[mn]=Нэмэлт төхөөрөмж 62 | Name[mr]=शिवाय उपकरणं 63 | Name[ms]=Aksesori 64 | Name[nb]=Tilbehør 65 | Name[ne]=सहायक उपकरण 66 | Name[nl]=Hulpmiddelen 67 | Name[nn]=Tilbehøyr 68 | Name[oc]=Accessòris 69 | Name[or]=ସହାୟକ ବସ୍ତୁ 70 | Name[pa]=ਸਹਾਇਕ 71 | Name[pl]=Akcesoria 72 | Name[ps]=ملتوکي 73 | Name[pt]=Acessórios 74 | Name[pt_BR]=Acessórios 75 | Name[ro]=Accesorii 76 | Name[ru]=Стандартные 77 | Name[si]=උපාංග 78 | Name[sk]=Príslušenstvo 79 | Name[sl]=Pripomočki 80 | Name[sq]=Aksesorë 81 | Name[sr]=Алатке 82 | Name[sr@latin]=Alatke 83 | Name[sv]=Tillbehör 84 | Name[ta]=துணைப்பொருள்கள் 85 | Name[te]=సహాయకాలు 86 | Name[th]=เครื่องใช้ไม้สอย 87 | Name[tr]=Donatılar 88 | Name[tt_RU]=Үрнәк кушымталар 89 | Name[ug]=قوشۇمچە دېتاللار 90 | Name[uk]=Стандартні 91 | Name[ur]=لوازمات 92 | Name[ur_PK]=لوازمات 93 | Name[vi]=Bổ trợ 94 | Name[xh]=Izinto ezongezelelwayo 95 | Name[zh_CN]=附件 96 | Name[zh_HK]=附屬應用程式 97 | Name[zh_TW]=附屬應用程式 98 | Comment=Desktop accessories 99 | Comment[af]=Werkskermtoebehore 100 | Comment[ar]=ملحقات سطح المكتب 101 | Comment[as]=ডেস্কটপৰ ব্যৱহৃত আনুষঙ্গিক বস্তু 102 | Comment[ast]=Accesorios d'Escritoriu 103 | Comment[be]=Інструменты працоўнага асяроддзя 104 | Comment[be@latin]=Aksesuary stała 105 | Comment[bg]=Набор от помощни програми 106 | Comment[bn]=ডেস্কটপের আনুসাঙ্গিক অ্যাপ্লিকেশন 107 | Comment[bn_IN]=ডেস্কটপের ব্যবহৃত আনুষঙ্গিক বস্তু 108 | Comment[br]=Mavegoù ar burev 109 | Comment[ca]=Accessoris d'escriptori 110 | Comment[cs]=Příslušenství prostředí pracovní plochy 111 | Comment[cy]=Ategolion penbwrdd 112 | Comment[da]=Skrivebordstilbehør 113 | Comment[de]=Desktop-Zubehör 114 | Comment[dz]=ཌེཀསི་ཊོཔ་གི་ ཡན་ལག་ཅ་ཆས། 115 | Comment[el]=Βοηθήματα επιφάνειας εργασίας 116 | Comment[en_CA]=Desktop accessories 117 | Comment[en_GB]=Desktop accessories 118 | Comment[eo]=Labortablaj utilaĵoj 119 | Comment[es]=Accesorios del escritorio 120 | Comment[es_VE]=Accesorios del escritorio 121 | Comment[et]=Töölaua tarvikud 122 | Comment[eu]=Mahaigainaren gehigarriak 123 | Comment[fa]=لوازم رومیزی 124 | Comment[fi]=Työpöydän apuohjelmat 125 | Comment[fo]=Skriviborðs-tilhoyr 126 | Comment[fr]=Accessoires du bureau 127 | Comment[frp]=Accesòres do Bureu 128 | Comment[fur]=Robutis dal desktop 129 | Comment[ga]=Oiriúintí deisce 130 | Comment[gl]=Accesorios do escritorio 131 | Comment[gn]=Nhamba'eapoa Regua 132 | Comment[gu]=ડેસ્કટોપ સહાયક કાર્યક્રમો 133 | Comment[he]=עזרי שולחן עבודה 134 | Comment[hi]=डेस्कटॉप संलग्नक 135 | Comment[hr]=Pomagala radne površine 136 | Comment[hu]=Asztali kellékek 137 | Comment[hy]=Աշխատասեղանի աքսեսուարներ 138 | Comment[id]=Aksesoris desktop 139 | Comment[io]=Pupitrosuprala acesori 140 | Comment[is]=Skjáborðs aukahlutir 141 | Comment[it]=Accessori del desktop 142 | Comment[ja]=デスクトップ用のアクセサリーです 143 | Comment[ka]=სამუშაო მაგიდის აქსესუარები 144 | Comment[kk]=Қалыпты қолданбалар 145 | Comment[km]=ប្រដាប់ប្រដាផ្ទៃតុ 146 | Comment[kn]=ಗಣಕತೆರೆಯ ಸಲಕರಣೆಗಳು 147 | Comment[ko]=바탕환경의 보조 프로그램 148 | Comment[ku]=Bernameyên alîkar ên sermaseyê 149 | Comment[ky]=Иш столунун иштемелери 150 | Comment[lg]=Ebigonz'emirimu awakolerwa 151 | Comment[lt]=Darbo aplinkos reikmenys 152 | Comment[lv]=Darbvirsmas piederumi 153 | Comment[mai]=डेस्कटाप संलग्नकसभ 154 | Comment[mg]=Fiasan'ny desktop 155 | Comment[mk]=Алатки за работната површина 156 | Comment[ml]=പണിയിടോപകരണങ്ങള്‍ 157 | Comment[mn]=Дэлгэцийн нэмэлт төхөөрөмжүүд 158 | Comment[mr]=डेस्कटॉपशी निगडित शिवाय उपकरणं 159 | Comment[ms]=Aksesori Desktop 160 | Comment[nb]=Tilbehør for skrivebordet 161 | Comment[ne]=डेस्कटप सहायक उपकरण 162 | Comment[nl]=Bureaubladhulpmiddelen 163 | Comment[nn]=Tilbehøyr til skrivebordet 164 | Comment[oc]=Accessòris del burèu 165 | Comment[or]=ଡେସ୍କଟପ୍ ସହାୟକ ବସ୍ତୁ 166 | Comment[pa]=ਡੈਸਕਟਾਪ ਸਹਾਇਕ 167 | Comment[pl]=Akcesoria pulpitu 168 | Comment[ps]=د سرپاڼې ملتوکي 169 | Comment[pt]=Acessórios de área de trabalho 170 | Comment[pt_BR]=Acessórios da área de trabalho 171 | Comment[ro]=Accesorii pentru desktop 172 | Comment[ru]=Стандартные приложения 173 | Comment[si]=මූලික තිරය උපාංග 174 | Comment[sk]=Príslušenstvo pracovnej plochy 175 | Comment[sl]=Namizni pripomočki 176 | Comment[sq]=Aksesorë hapësire pune 177 | Comment[sr]=Алатке окружења 178 | Comment[sr@latin]=Alatke okruženja 179 | Comment[sv]=Skrivbordstillbehör 180 | Comment[ta]=மேஜை துணைப்பொருள்கள் 181 | Comment[te]=డెస్క్‍టాప్ సహాయకాలు 182 | Comment[th]=โปรแกรมเครื่องใช้ไม้สอยบนเดสก์ท็อป 183 | Comment[tr]=Masaüstü donatıları 184 | Comment[tt_RU]=Эш өстәле өчен үрнәк кушымталар 185 | Comment[ug]=ئۈستەلئۈستى قوشۇمچە دېتاللىرى 186 | Comment[uk]=Стандартні програми 187 | Comment[ur]=ڈیسک ٹاپ لوازمات 188 | Comment[ur_PK]=ڈیسک ٹاپ لوازمات 189 | Comment[vi]=Bổ trợ môi trường 190 | Comment[xh]=Izinto ezongezelelwayo ze-Desktop 191 | Comment[zh_CN]=桌面附件 192 | Comment[zh_HK]=桌面附屬應用程式 193 | Comment[zh_TW]=桌面附屬應用程式 194 | Icon=applications-accessories 195 | Type=Directory 196 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/nwg-piotr/nwg-menu 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.2 6 | 7 | require ( 8 | github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37 9 | github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774 10 | github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56 11 | github.com/joshuarubin/go-sway v1.2.0 12 | github.com/sirupsen/logrus v1.9.3 13 | ) 14 | 15 | require ( 16 | github.com/joshuarubin/lifecycle v1.1.4 // indirect 17 | go.uber.org/multierr v1.11.0 // indirect 18 | golang.org/x/sync v0.14.0 // indirect 19 | golang.org/x/sys v0.33.0 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37 h1:28uU3TtuvQ6KRndxg9TrC868jBWmSKgh0GTXkACCXmA= 2 | github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37/go.mod h1:6AXRstqK+32jeFmw89QGL2748+dj34Av4xc/I9oo9BY= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774 h1:o87OVL4olQBlVwN3+NSVQpS6gj9FWUYtxOfHXWZigUE= 7 | github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774/go.mod h1:JHLx2Wz4mAPVwn4PFhC69ydwyHP4A3wQvlg7HKVVc1U= 8 | github.com/gotk3/gotk3 v0.6.1/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q= 9 | github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56 h1:eR+xxC8qqKuPMTucZqaklBxLIT7/4L7dzhlwKMrDbj8= 10 | github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q= 11 | github.com/joshuarubin/go-sway v1.2.0 h1:t3eqW504//uj9PDwFf0+IVfkD+WoOGaDX5gYIe0BHyM= 12 | github.com/joshuarubin/go-sway v1.2.0/go.mod h1:qcDd6f25vJ0++wICwA1BainIcRC67p2Mb4lsrZ0k3/k= 13 | github.com/joshuarubin/lifecycle v1.0.0/go.mod h1:sRy++ATvR9Ee21tkRdFkQeywAWvDsue66V70K0Dnl54= 14 | github.com/joshuarubin/lifecycle v1.1.4 h1:9ZjvYSsWax9DC3Jpz6vGf/0KnU8FNMjh0/vJ3SpSBRQ= 15 | github.com/joshuarubin/lifecycle v1.1.4/go.mod h1:QqHrqwMPMA9dbJY3XgIyVLhzHMSGOFrcCAQ59bke1mo= 16 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 18 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 19 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 21 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 22 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 23 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 24 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 25 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 26 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 27 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 28 | golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 29 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 30 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 31 | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= 32 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 33 | golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= 34 | golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 35 | golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= 36 | golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 37 | golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= 38 | golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 39 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= 40 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 41 | golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= 42 | golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 43 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 44 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 45 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 46 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 47 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= 48 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 49 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 50 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 51 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 52 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 53 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 54 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 55 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "path/filepath" 9 | "strconv" 10 | "strings" 11 | "syscall" 12 | "time" 13 | 14 | log "github.com/sirupsen/logrus" 15 | 16 | "github.com/allan-simon/go-singleinstance" 17 | "github.com/dlasky/gotk3-layershell/layershell" 18 | "github.com/gotk3/gotk3/gdk" 19 | "github.com/gotk3/gotk3/glib" 20 | "github.com/gotk3/gotk3/gtk" 21 | ) 22 | 23 | const version = "0.1.8" 24 | 25 | var ( 26 | appDirs []string 27 | configDirectory string 28 | pinnedFile string 29 | pinned []string 30 | leftBox *gtk.Box 31 | rightBox *gtk.Box 32 | hyprlandMonitors []monitor 33 | src glib.SourceHandle 34 | id2entry map[string]desktopEntry 35 | ) 36 | 37 | var categoryNames = [...]string{ 38 | "utility", 39 | "development", 40 | "game", 41 | "graphics", 42 | "internet-and-network", 43 | "office", 44 | "audio-video", 45 | "system-tools", 46 | "other", 47 | } 48 | 49 | type category struct { 50 | Name string 51 | DisplayName string 52 | Icon string 53 | } 54 | 55 | var categories []category 56 | 57 | type desktopEntry struct { 58 | DesktopID string 59 | Name string 60 | NameLoc string 61 | Comment string 62 | CommentLoc string 63 | Icon string 64 | Exec string 65 | Terminal bool 66 | NoDisplay bool 67 | } 68 | 69 | // slices below will hold DesktopID strings 70 | var ( 71 | listUtility []string 72 | listDevelopment []string 73 | listGame []string 74 | listGraphics []string 75 | listInternetAndNetwork []string 76 | listOffice []string 77 | listAudioVideo []string 78 | listSystemTools []string 79 | listOther []string 80 | ) 81 | 82 | var desktopEntries []desktopEntry 83 | 84 | // UI elements 85 | var ( 86 | categoriesListBox *gtk.ListBox 87 | userDirsListBox *gtk.ListBox 88 | pinnedListBox *gtk.ListBox 89 | resultWrapper *gtk.Box 90 | resultWindow *gtk.ScrolledWindow 91 | fileSearchResults map[string]string 92 | fileSearchResultWindow *gtk.ScrolledWindow 93 | backButton *gtk.Box 94 | searchEntry *gtk.SearchEntry 95 | phrase string 96 | resultListBox *gtk.ListBox 97 | fileSearchResultListBox *gtk.ListBox 98 | buttonsWrapper *gtk.Box 99 | buttonBox *gtk.EventBox 100 | confirmationBox *gtk.Box 101 | userDirsMap map[string]string 102 | ) 103 | 104 | // Flags 105 | var cssFileName = flag.String("s", "menu-start.css", "Styling: css file name") 106 | var targetOutput = flag.String("o", "", "name of the Output to display the menu on") 107 | var displayVersion = flag.Bool("v", false, "display Version information") 108 | var autohide = flag.Bool("d", false, "auto-hiDe: close window when left") 109 | var valign = flag.String("va", "bottom", "Vertical Alignment: \"bottom\" or \"top\"") 110 | var halign = flag.String("ha", "left", "Horizontal Alignment: \"left\" or \"right\"") 111 | var marginTop = flag.Int("mt", 0, "Margin Top") 112 | var marginLeft = flag.Int("ml", 0, "Margin Left") 113 | var marginRight = flag.Int("mr", 0, "Margin Right") 114 | var marginBottom = flag.Int("mb", 0, "Margin Bottom") 115 | var iconSizeLarge = flag.Int("isl", 32, "Icon Size Large") 116 | var iconSizeSmall = flag.Int("iss", 16, "Icon Size Small") 117 | var sLen = flag.Int("slen", 80, "Search result length Limit") 118 | var itemPadding = flag.Uint("padding", 2, "vertical item padding") 119 | var lang = flag.String("lang", "", "force lang, e.g. \"en\", \"pl\"") 120 | var fileManager = flag.String("fm", "thunar", "File Manager") 121 | var term = flag.String("term", "foot", "Terminal emulator") 122 | var wm = flag.String("wm", "", "use swaymsg exec (with 'sway' argument) or hyprctl dispatch exec (with 'hyprland') or riverctl spawn (with 'river') to launch programs") 123 | var cmdLock = flag.String("cmd-lock", "swaylock -f -c 000000", "screen lock command") 124 | var cmdLogout = flag.String("cmd-logout", "swaymsg exit", "logout command") 125 | var cmdRestart = flag.String("cmd-restart", "systemctl reboot", "reboot command") 126 | var cmdShutdown = flag.String("cmd-shutdown", "systemctl -i poweroff", "shutdown command") 127 | var debug = flag.Bool("debug", false, "turn on Debug messages") 128 | var hover = flag.Bool("t", false, "hovering caTegories opens submenus") 129 | 130 | func main() { 131 | timeStart := time.Now() 132 | flag.Parse() 133 | 134 | if *debug { 135 | log.SetLevel(log.DebugLevel) 136 | } 137 | 138 | if *displayVersion { 139 | fmt.Printf("nwg-menu version %s\n", version) 140 | os.Exit(0) 141 | } 142 | 143 | // Gentle SIGTERM handler thanks to reiki4040 https://gist.github.com/reiki4040/be3705f307d3cd136e85 144 | signalChan := make(chan os.Signal, 1) 145 | signal.Notify(signalChan, syscall.SIGTERM) 146 | go func() { 147 | for { 148 | s := <-signalChan 149 | if s == syscall.SIGTERM { 150 | println("SIGTERM received, bye bye!") 151 | gtk.MainQuit() 152 | } 153 | } 154 | }() 155 | 156 | // We want the same key/mouse binding to turn the dock off: kill the running instance and exit. 157 | lockFileDir := runtimeDir() 158 | if lockFileDir == "" { 159 | lockFileDir = tempDir() 160 | } 161 | lockFilePath := fmt.Sprintf("%s/nwg-menu.lock", lockFileDir) 162 | lockFile, err := singleinstance.CreateLockFile(lockFilePath) 163 | if err != nil { 164 | pid, err := readTextFile(lockFilePath) 165 | if err == nil { 166 | i, err := strconv.Atoi(pid) 167 | if err == nil { 168 | /*if !*autohide { 169 | println("Running instance found, sending SIGTERM and exiting...") 170 | syscall.Kill(i, syscall.SIGTERM) 171 | } else { 172 | println("Already running") 173 | }*/ 174 | log.Info("Running instance found, sending SIGTERM and exiting...") 175 | syscall.Kill(i, syscall.SIGTERM) 176 | } 177 | } 178 | os.Exit(0) 179 | } 180 | defer lockFile.Close() 181 | 182 | // LANGUAGE 183 | if *lang == "" && os.Getenv("LANG") != "" { 184 | *lang = strings.Split(os.Getenv("LANG"), ".")[0] 185 | } 186 | log.Infof("lang: %s", *lang) 187 | 188 | // ENVIRONMENT 189 | configDirectory = configDir() 190 | 191 | if !pathExists(filepath.Join(configDirectory, "menu-start.css")) { 192 | copyFile("/usr/share/nwg-menu/menu-start.css", filepath.Join(configDirectory, "menu-start.css")) 193 | } 194 | 195 | cacheDirectory := cacheDir() 196 | if cacheDirectory == "" { 197 | log.Panic("Couldn't determine cache directory location") 198 | } 199 | 200 | // DATA 201 | pinnedFile = filepath.Join(cacheDirectory, "nwg-pin-cache") 202 | pinned, err = loadTextFile(pinnedFile) 203 | if err != nil { 204 | pinned = nil 205 | } 206 | 207 | cssFile := filepath.Join(configDirectory, *cssFileName) 208 | 209 | appDirs = getAppDirs() 210 | 211 | setUpCategories() 212 | 213 | desktopFiles := listDesktopFiles() 214 | log.Infof("Found %v desktop files", len(desktopFiles)) 215 | 216 | parseDesktopFiles(desktopFiles) 217 | 218 | // USER INTERFACE 219 | gtk.Init(nil) 220 | 221 | cssProvider, _ := gtk.CssProviderNew() 222 | 223 | err = cssProvider.LoadFromPath(cssFile) 224 | if err != nil { 225 | log.Warnf("ERROR: %s css file not found or erroneous. Using GTK styling.", cssFile) 226 | } else { 227 | log.Infof("Using style from %s", cssFile) 228 | screen, _ := gdk.ScreenGetDefault() 229 | gtk.AddProviderForScreen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) 230 | } 231 | 232 | win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL) 233 | if err != nil { 234 | log.Fatal("Unable to create window:", err) 235 | } 236 | 237 | layershell.InitForWindow(win) 238 | 239 | var output2mon map[string]*gdk.Monitor 240 | if *targetOutput != "" { 241 | // We want to assign layershell to a monitor, but we only know the output name! 242 | output2mon, err = mapOutputs() 243 | if err == nil { 244 | monitor := output2mon[*targetOutput] 245 | layershell.SetMonitor(win, monitor) 246 | log.Infof("Assigning window to output %s", *targetOutput) 247 | 248 | } else { 249 | log.Warnf("Couldn't list available outputs, %s", err) 250 | } 251 | } 252 | 253 | if *valign == "bottom" { 254 | layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_BOTTOM, true) 255 | } else { 256 | layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_TOP, true) 257 | } 258 | 259 | if *halign == "left" { 260 | layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_LEFT, true) 261 | } else { 262 | layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_RIGHT, true) 263 | } 264 | 265 | layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_TOP) 266 | 267 | layershell.SetMargin(win, layershell.LAYER_SHELL_EDGE_TOP, *marginTop) 268 | layershell.SetMargin(win, layershell.LAYER_SHELL_EDGE_LEFT, *marginLeft) 269 | layershell.SetMargin(win, layershell.LAYER_SHELL_EDGE_RIGHT, *marginRight) 270 | layershell.SetMargin(win, layershell.LAYER_SHELL_EDGE_BOTTOM, *marginBottom) 271 | 272 | layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND) 273 | 274 | win.Connect("destroy", func() { 275 | gtk.MainQuit() 276 | }) 277 | 278 | win.Connect("key-release-event", func(window *gtk.Window, event *gdk.Event) { 279 | key := &gdk.EventKey{Event: event} 280 | if key.KeyVal() == gdk.KEY_Escape { 281 | s, _ := searchEntry.GetText() 282 | if s != "" { 283 | clearSearchResult() 284 | searchEntry.GrabFocus() 285 | searchEntry.SetText("") 286 | } else { 287 | if resultWindow == nil || !resultWindow.GetVisible() { 288 | gtk.MainQuit() 289 | } else { 290 | clearSearchResult() 291 | } 292 | } 293 | } 294 | }) 295 | 296 | // Close the window on leave, but not immediately, to avoid accidental closes 297 | win.Connect("leave-notify-event", func() { 298 | if *autohide { 299 | src = glib.TimeoutAdd(uint(1000), func() bool { 300 | gtk.MainQuit() 301 | return false 302 | }) 303 | } 304 | }) 305 | 306 | win.Connect("enter-notify-event", func() { 307 | cancelClose() 308 | }) 309 | 310 | outerBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 311 | win.Add(outerBox) 312 | 313 | alignmentBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) 314 | //alignmentBox.SetHomogeneous(true) 315 | outerBox.PackStart(alignmentBox, true, true, 0) 316 | 317 | leftBox, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) 318 | alignmentBox.PackStart(leftBox, false, false, 10) 319 | 320 | leftColumn, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 321 | leftBox.PackStart(leftColumn, false, false, 0) 322 | 323 | searchEntry = setUpSearchEntry() 324 | if *valign == "top" { 325 | leftColumn.PackStart(searchEntry, false, false, 10) 326 | } 327 | 328 | if *valign == "bottom" { 329 | pinnedListBox = setUpPinnedListBox() 330 | leftColumn.PackStart(pinnedListBox, false, false, 10) 331 | } 332 | 333 | categoriesListBox = setUpCategoriesListBox() 334 | leftColumn.PackStart(categoriesListBox, false, false, 10) 335 | 336 | if *valign == "top" { 337 | pinnedListBox = setUpPinnedListBox() 338 | leftColumn.PackStart(pinnedListBox, false, false, 10) 339 | } 340 | 341 | if *valign != "top" { 342 | leftColumn.PackEnd(searchEntry, false, false, 10) 343 | } 344 | 345 | rightBox, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) 346 | alignmentBox.PackStart(rightBox, true, true, 10) 347 | 348 | rightColumn, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 349 | 350 | rightBox.PackStart(rightColumn, true, true, 0) 351 | 352 | userDirsListBox = setUpUserDirsList() 353 | rightColumn.PackStart(userDirsListBox, false, true, 10) 354 | 355 | backButton = setUpBackButton() 356 | rightColumn.PackStart(backButton, false, false, 10) 357 | 358 | resultWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 359 | rightColumn.PackStart(resultWrapper, true, true, 0) 360 | 361 | buttonsWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 362 | 363 | buttonBox = setUpButtonBox() 364 | buttonsWrapper.PackStart(buttonBox, false, false, 10) 365 | rightColumn.PackEnd(buttonsWrapper, false, true, 0) 366 | 367 | //win.SetSizeRequest(0, *windowHeigth) 368 | 369 | win.ShowAll() 370 | 371 | backButton.Hide() 372 | 373 | pinnedListBox.UnselectAll() 374 | categoriesListBox.UnselectAll() 375 | searchEntry.GrabFocus() 376 | t := time.Now() 377 | //println(fmt.Sprintf("UI created in %v ms. Thanks for watching.", t.Sub(timeStart).Milliseconds())) 378 | log.Infof("UI created in %v ms. Thanks for watching.", t.Sub(timeStart).Milliseconds()) 379 | gtk.Main() 380 | } 381 | -------------------------------------------------------------------------------- /menu-start.css: -------------------------------------------------------------------------------- 1 | window { 2 | background-color: rgba (36, 47, 79, 0.92); 3 | color: #eeeeee 4 | } 5 | 6 | list { 7 | background: none; 8 | border-radius: 15px 9 | } 10 | 11 | entry { 12 | background-color: rgba (0, 0, 0, 0.2) 13 | } 14 | 15 | button { 16 | background: none; 17 | border: none 18 | } 19 | 20 | button:hover { 21 | background-color: rgba (255, 255, 255, 0.1) 22 | } -------------------------------------------------------------------------------- /nwg-menu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwg-piotr/nwg-menu/7cd1702c862b9a52897beb973c58ef2465dc91b5/nwg-menu -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/joshuarubin/go-sway" 9 | log "github.com/sirupsen/logrus" 10 | "io" 11 | "io/fs" 12 | "net" 13 | "os" 14 | "os/exec" 15 | "path/filepath" 16 | "sort" 17 | "strings" 18 | "time" 19 | 20 | "github.com/gotk3/gotk3/gdk" 21 | "github.com/gotk3/gotk3/glib" 22 | "github.com/gotk3/gotk3/gtk" 23 | ) 24 | 25 | type monitor struct { 26 | Id int `json:"id"` 27 | Name string `json:"name"` 28 | Description string `json:"description"` 29 | Make string `json:"make"` 30 | Model string `json:"model"` 31 | Serial string `json:"serial"` 32 | Width int `json:"width"` 33 | Height int `json:"height"` 34 | RefreshRate float64 `json:"refreshRate"` 35 | X int `json:"x"` 36 | Y int `json:"y"` 37 | ActiveWorkspace struct { 38 | Id int `json:"id"` 39 | Name string `json:"name"` 40 | } `json:"activeWorkspace"` 41 | Reserved []int `json:"reserved"` 42 | Scale float64 `json:"scale"` 43 | Transform int `json:"transform"` 44 | Focused bool `json:"focused"` 45 | DpmsStatus bool `json:"dpmsStatus"` 46 | Vrr bool `json:"vrr"` 47 | } 48 | 49 | /* 50 | Window on-leave-notify event hides the window with glib Timeout 1000 ms. 51 | We might have left the window by accident, so let's clear the timeout if window re-entered. 52 | Furthermore - hovering a widget triggers window on-leave-notify event, and the timeout 53 | needs to be cleared as well. 54 | */ 55 | func cancelClose() { 56 | if src > 0 { 57 | glib.SourceRemove(src) 58 | src = 0 59 | } 60 | } 61 | 62 | func createPixbuf(icon string, size int) (*gdk.Pixbuf, error) { 63 | iconTheme, err := gtk.IconThemeGetDefault() 64 | if err != nil { 65 | log.Fatal("Couldn't get default theme: ", err) 66 | } 67 | 68 | if strings.Contains(icon, "/") { 69 | pixbuf, err := gdk.PixbufNewFromFileAtSize(icon, size, size) 70 | if err != nil { 71 | println(err) 72 | return nil, err 73 | } 74 | return pixbuf, nil 75 | 76 | } else if strings.HasSuffix(icon, ".svg") || strings.HasSuffix(icon, ".png") || strings.HasSuffix(icon, ".xpm") { 77 | // for enties like "Icon=netflix-desktop.svg" 78 | icon = strings.Split(icon, ".")[0] 79 | } 80 | 81 | pixbuf, err := iconTheme.LoadIcon(icon, size, gtk.ICON_LOOKUP_FORCE_SIZE) 82 | 83 | if err != nil { 84 | if strings.HasPrefix(icon, "/") { 85 | pixbuf, err := gdk.PixbufNewFromFileAtSize(icon, size, size) 86 | if err != nil { 87 | return nil, err 88 | } 89 | return pixbuf, nil 90 | } 91 | 92 | pixbuf, err := iconTheme.LoadIcon(icon, size, gtk.ICON_LOOKUP_FORCE_SIZE) 93 | if err != nil { 94 | return nil, err 95 | } 96 | return pixbuf, nil 97 | } 98 | return pixbuf, nil 99 | } 100 | 101 | func mapXdgUserDirs() map[string]string { 102 | result := make(map[string]string) 103 | home := os.Getenv("HOME") 104 | 105 | result["home"] = home 106 | result["documents"] = filepath.Join(home, "Documents") 107 | result["downloads"] = filepath.Join(home, "Downloads") 108 | result["music"] = filepath.Join(home, "Music") 109 | result["pictures"] = filepath.Join(home, "Pictures") 110 | result["videos"] = filepath.Join(home, "Videos") 111 | 112 | userDirsFile := filepath.Join(home, ".config/user-dirs.dirs") 113 | if pathExists(userDirsFile) { 114 | log.Infof("Using XDG user dirs from %s", userDirsFile) 115 | lines, _ := loadTextFile(userDirsFile) 116 | for _, l := range lines { 117 | if strings.HasPrefix(l, "XDG_DOCUMENTS_DIR") { 118 | result["documents"] = getUserDir(home, l) 119 | continue 120 | } 121 | if strings.HasPrefix(l, "XDG_DOWNLOAD_DIR") { 122 | result["downloads"] = getUserDir(home, l) 123 | continue 124 | } 125 | if strings.HasPrefix(l, "XDG_MUSIC_DIR") { 126 | result["music"] = getUserDir(home, l) 127 | continue 128 | } 129 | if strings.HasPrefix(l, "XDG_PICTURES_DIR") { 130 | result["pictures"] = getUserDir(home, l) 131 | continue 132 | } 133 | if strings.HasPrefix(l, "XDG_VIDEOS_DIR") { 134 | result["videos"] = getUserDir(home, l) 135 | } 136 | } 137 | } else { 138 | println(fmt.Sprintf("%s file not found, using defaults", userDirsFile)) 139 | } 140 | 141 | return result 142 | } 143 | 144 | func getUserDir(home, line string) string { 145 | // line is supposed to look like XDG_DOCUMENTS_DIR="$HOME/Dokumenty" 146 | result := strings.Split(line, "=")[1] 147 | result = strings.Replace(result, "$HOME", home, 1) 148 | 149 | // trim "" 150 | return result[1 : len(result)-1] 151 | } 152 | 153 | func cacheDir() string { 154 | if os.Getenv("XDG_CACHE_HOME") != "" { 155 | return os.Getenv("XDG_CONFIG_HOME") 156 | } 157 | if os.Getenv("HOME") != "" && pathExists(filepath.Join(os.Getenv("HOME"), ".cache")) { 158 | p := filepath.Join(os.Getenv("HOME"), ".cache") 159 | return p 160 | } 161 | return "" 162 | } 163 | 164 | func runtimeDir() string { 165 | return os.Getenv("XDG_RUNTIME_DIR") 166 | } 167 | 168 | func tempDir() string { 169 | if os.Getenv("TMPDIR") != "" { 170 | return os.Getenv("TMPDIR") 171 | } else if os.Getenv("TEMP") != "" { 172 | return os.Getenv("TEMP") 173 | } else if os.Getenv("TMP") != "" { 174 | return os.Getenv("TMP") 175 | } 176 | return "/tmp" 177 | } 178 | 179 | func readTextFile(path string) (string, error) { 180 | bytes, err := os.ReadFile(path) 181 | if err != nil { 182 | return "", err 183 | } 184 | 185 | return string(bytes), nil 186 | } 187 | 188 | func configDir() string { 189 | if os.Getenv("XDG_CONFIG_HOME") != "" { 190 | dir := fmt.Sprintf("%s/nwg-panel", os.Getenv("XDG_CONFIG_HOME")) 191 | createDir(dir) 192 | return (fmt.Sprintf("%s/nwg-panel", os.Getenv("XDG_CONFIG_HOME"))) 193 | } 194 | dir := fmt.Sprintf("%s/.config/nwg-panel", os.Getenv("HOME")) 195 | createDir(dir) 196 | return dir 197 | } 198 | 199 | func createDir(dir string) { 200 | if _, err := os.Stat(dir); os.IsNotExist(err) { 201 | err := os.MkdirAll(dir, os.ModePerm) 202 | if err == nil { 203 | fmt.Println("Creating dir:", dir) 204 | } 205 | } 206 | } 207 | 208 | func copyFile(src, dst string) error { 209 | fmt.Println("Copying file:", dst) 210 | 211 | var err error 212 | var srcfd *os.File 213 | var dstfd *os.File 214 | var srcinfo os.FileInfo 215 | 216 | if srcfd, err = os.Open(src); err != nil { 217 | return err 218 | } 219 | defer srcfd.Close() 220 | 221 | if dstfd, err = os.Create(dst); err != nil { 222 | return err 223 | } 224 | defer dstfd.Close() 225 | 226 | if _, err = io.Copy(dstfd, srcfd); err != nil { 227 | return err 228 | } 229 | if srcinfo, err = os.Stat(src); err != nil { 230 | return err 231 | } 232 | return os.Chmod(dst, srcinfo.Mode()) 233 | } 234 | 235 | func getAppDirs() []string { 236 | var dirs []string 237 | xdgDataDirs := "" 238 | 239 | home := os.Getenv("HOME") 240 | xdgDataHome := os.Getenv("XDG_DATA_HOME") 241 | if os.Getenv("XDG_DATA_DIRS") != "" { 242 | xdgDataDirs = os.Getenv("XDG_DATA_DIRS") 243 | } else { 244 | xdgDataDirs = "/usr/local/share/:/usr/share/" 245 | } 246 | if xdgDataHome != "" { 247 | dirs = append(dirs, filepath.Join(xdgDataHome, "applications")) 248 | } else if home != "" { 249 | dirs = append(dirs, filepath.Join(home, ".local/share/applications")) 250 | } 251 | for _, d := range strings.Split(xdgDataDirs, ":") { 252 | dirs = append(dirs, filepath.Join(d, "applications")) 253 | } 254 | flatpakDirs := []string{filepath.Join(home, ".local/share/flatpak/exports/share/applications"), 255 | "/var/lib/flatpak/exports/share/applications"} 256 | 257 | for _, d := range flatpakDirs { 258 | if !isIn(dirs, d) { 259 | dirs = append(dirs, d) 260 | } 261 | } 262 | return dirs 263 | } 264 | 265 | func listFiles(dir string) ([]fs.DirEntry, error) { 266 | files, err := os.ReadDir(dir) 267 | if err == nil { 268 | return files, nil 269 | } 270 | return nil, err 271 | } 272 | 273 | func listDesktopFiles() []string { 274 | var paths []string 275 | for _, dir := range appDirs { 276 | dirs, err := listFiles(dir) 277 | if err == nil { 278 | for _, file := range dirs { 279 | parts := strings.Split(file.Name(), ".") 280 | if parts[len(parts)-1] == "desktop" { 281 | paths = append(paths, filepath.Join(dir, file.Name())) 282 | } 283 | } 284 | } 285 | } 286 | return paths 287 | } 288 | 289 | func setUpCategories() { 290 | path := "/usr/share/nwg-menu/desktop-directories" 291 | var other category 292 | 293 | for _, cName := range categoryNames { 294 | fileName := fmt.Sprintf("%s.directory", cName) 295 | lines, err := loadTextFile(filepath.Join(path, fileName)) 296 | if err == nil { 297 | var cat category 298 | cat.Name = cName 299 | 300 | name := "" 301 | nameLoc := "" 302 | icon := "" 303 | 304 | for _, l := range lines { 305 | if strings.HasPrefix(l, "Name=") { 306 | name = strings.Split(l, "=")[1] 307 | continue 308 | } 309 | if strings.HasPrefix(l, fmt.Sprintf("Name[%s]=", strings.Split(*lang, "_")[0])) { 310 | nameLoc = strings.Split(l, "=")[1] 311 | continue 312 | } 313 | if strings.HasPrefix(l, "Icon=") { 314 | icon = strings.Split(l, "=")[1] 315 | continue 316 | } 317 | } 318 | 319 | if nameLoc == "" { 320 | for _, l := range lines { 321 | if strings.HasPrefix(l, fmt.Sprintf("Name[%s]=", *lang)) { 322 | nameLoc = strings.Split(l, "=")[1] 323 | break 324 | } 325 | } 326 | } 327 | if nameLoc != "" { 328 | cat.DisplayName = nameLoc 329 | } else { 330 | cat.DisplayName = name 331 | } 332 | cat.Icon = icon 333 | 334 | // We want "other" to be the last one. Let's append it when already sorted 335 | if fileName != "other.directory" { 336 | categories = append(categories, cat) 337 | } else { 338 | other = cat 339 | } 340 | } 341 | } 342 | sort.Slice(categories, func(i, j int) bool { 343 | return categories[i].DisplayName < categories[j].DisplayName 344 | }) 345 | categories = append(categories, other) 346 | } 347 | 348 | func parseDesktopFiles(desktopFiles []string) { 349 | id2entry = make(map[string]desktopEntry) 350 | var added []string 351 | skipped := 0 352 | hidden := 0 353 | for _, file := range desktopFiles { 354 | lines, err := loadTextFile(file) 355 | if err == nil { 356 | parts := strings.Split(file, "/") 357 | desktopID := parts[len(parts)-1] 358 | name := "" 359 | nameLoc := "" 360 | comment := "" 361 | commentLoc := "" 362 | icon := "" 363 | exec := "" 364 | terminal := false 365 | noDisplay := false 366 | 367 | categories := "" 368 | 369 | for _, l := range lines { 370 | if strings.HasPrefix(l, "[") && l != "[Desktop Entry]" { 371 | break 372 | } 373 | if strings.HasPrefix(l, "Name=") { 374 | name = strings.Split(l, "=")[1] 375 | continue 376 | } 377 | if strings.HasPrefix(l, fmt.Sprintf("Name[%s]=", strings.Split(*lang, "_")[0])) { 378 | nameLoc = strings.Split(l, "=")[1] 379 | continue 380 | } 381 | if strings.HasPrefix(l, "Comment=") { 382 | comment = strings.Split(l, "=")[1] 383 | continue 384 | } 385 | if strings.HasPrefix(l, fmt.Sprintf("Comment[%s]=", strings.Split(*lang, "_")[0])) { 386 | commentLoc = strings.Split(l, "=")[1] 387 | continue 388 | } 389 | if strings.HasPrefix(l, "Icon=") { 390 | icon = strings.Split(l, "=")[1] 391 | continue 392 | } 393 | if strings.HasPrefix(l, "Exec=") { 394 | exec = strings.Split(l, "Exec=")[1] 395 | disallowed := [2]string{"\"", "'"} 396 | for _, char := range disallowed { 397 | exec = strings.Replace(exec, char, "", -1) 398 | } 399 | continue 400 | } 401 | if strings.HasPrefix(l, "Categories=") { 402 | categories = strings.Split(l, "Categories=")[1] 403 | continue 404 | } 405 | if l == "Terminal=true" { 406 | terminal = true 407 | continue 408 | } 409 | if l == "NoDisplay=true" { 410 | noDisplay = true 411 | hidden++ 412 | continue 413 | } 414 | } 415 | 416 | // if name[ln] not found, let's try to find name[ln_LN] 417 | if nameLoc == "" { 418 | nameLoc = name 419 | } 420 | if commentLoc == "" { 421 | commentLoc = comment 422 | } 423 | 424 | if !isIn(added, desktopID) { 425 | added = append(added, desktopID) 426 | 427 | var entry desktopEntry 428 | entry.DesktopID = desktopID 429 | entry.Name = name 430 | entry.NameLoc = nameLoc 431 | entry.Comment = comment 432 | entry.CommentLoc = commentLoc 433 | entry.Icon = icon 434 | entry.Exec = exec 435 | entry.Terminal = terminal 436 | entry.NoDisplay = noDisplay 437 | desktopEntries = append(desktopEntries, entry) 438 | 439 | id2entry[entry.DesktopID] = entry 440 | 441 | assignToLists(entry.DesktopID, categories) 442 | 443 | } else { 444 | skipped++ 445 | } 446 | } 447 | } 448 | log.Infof("Skipped %v duplicates; %v .desktop entries hidden by \"NoDisplay=true\"", skipped, hidden) 449 | } 450 | 451 | // freedesktop Main Categories list consists of 13 entries. Let's contract it to 8+1 ("Other"). 452 | func assignToLists(desktopID, categories string) { 453 | cats := strings.Split(categories, ";") 454 | assigned := false 455 | for _, cat := range cats { 456 | if cat == "Utility" && !isIn(listUtility, desktopID) { 457 | listUtility = append(listUtility, desktopID) 458 | assigned = true 459 | continue 460 | } 461 | if cat == "Development" && !isIn(listDevelopment, desktopID) { 462 | listDevelopment = append(listDevelopment, desktopID) 463 | assigned = true 464 | continue 465 | } 466 | if cat == "Game" && !isIn(listGame, desktopID) { 467 | listGame = append(listGame, desktopID) 468 | assigned = true 469 | continue 470 | } 471 | if cat == "Graphics" && !isIn(listGraphics, desktopID) { 472 | listGraphics = append(listGraphics, desktopID) 473 | assigned = true 474 | continue 475 | } 476 | if cat == "Network" && !isIn(listInternetAndNetwork, desktopID) { 477 | listInternetAndNetwork = append(listInternetAndNetwork, desktopID) 478 | assigned = true 479 | continue 480 | } 481 | if isIn([]string{"Office", "Science", "Education"}, cat) && !isIn(listOffice, desktopID) { 482 | listOffice = append(listOffice, desktopID) 483 | assigned = true 484 | continue 485 | } 486 | if isIn([]string{"AudioVideo", "Audio", "Video"}, cat) && !isIn(listAudioVideo, desktopID) { 487 | listAudioVideo = append(listAudioVideo, desktopID) 488 | assigned = true 489 | continue 490 | } 491 | if isIn([]string{"Settings", "System", "DesktopSettings", "PackageManager"}, cat) && !isIn(listSystemTools, desktopID) { 492 | listSystemTools = append(listSystemTools, desktopID) 493 | assigned = true 494 | continue 495 | } 496 | } 497 | if categories != "" && !assigned && !isIn(listOther, desktopID) { 498 | listOther = append(listOther, desktopID) 499 | } 500 | } 501 | 502 | func isIn(slice []string, val string) bool { 503 | for _, item := range slice { 504 | if item == val { 505 | return true 506 | } 507 | } 508 | return false 509 | } 510 | 511 | func pathExists(name string) bool { 512 | if _, err := os.Stat(name); err != nil { 513 | if os.IsNotExist(err) { 514 | return false 515 | } 516 | } 517 | return true 518 | } 519 | 520 | func loadTextFile(path string) ([]string, error) { 521 | bytes, err := os.ReadFile(path) 522 | if err != nil { 523 | return nil, err 524 | } 525 | lines := strings.Split(string(bytes), "\n") 526 | var output []string 527 | for _, line := range lines { 528 | line = strings.TrimSpace(line) 529 | if line != "" { 530 | output = append(output, line) 531 | } 532 | 533 | } 534 | return output, nil 535 | } 536 | 537 | func pinItem(itemID string) { 538 | for _, item := range pinned { 539 | if item == itemID { 540 | println(item, "already pinned") 541 | return 542 | } 543 | } 544 | pinned = append(pinned, itemID) 545 | savePinned() 546 | println(itemID, "pinned") 547 | 548 | row := setUpPinnedListBoxRow(itemID) 549 | pinnedListBox.Add(row) 550 | pinnedListBox.ShowAll() 551 | } 552 | 553 | func unpinItem(itemID string) { 554 | if isIn(pinned, itemID) { 555 | pinned = remove(pinned, itemID) 556 | savePinned() 557 | println(itemID, "unpinned") 558 | } 559 | } 560 | 561 | func remove(s []string, r string) []string { 562 | for i, v := range s { 563 | if v == r { 564 | return append(s[:i], s[i+1:]...) 565 | } 566 | } 567 | return s 568 | } 569 | 570 | func savePinned() { 571 | f, err := os.OpenFile(pinnedFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) 572 | if err != nil { 573 | log.Fatal(err) 574 | } 575 | 576 | defer f.Close() 577 | 578 | for _, line := range pinned { 579 | if line != "" { 580 | entry := id2entry[line] 581 | if entry.DesktopID == "" { 582 | log.Debugf("Pinned item doesn't seem to exist, removing: %s", line) 583 | continue 584 | } 585 | 586 | _, err := f.WriteString(line + "\n") 587 | 588 | if err != nil { 589 | println("Error saving pinned", err) 590 | } 591 | } 592 | } 593 | } 594 | 595 | func launch(command string, terminal bool) { 596 | // trim % and everything afterwards 597 | if strings.Contains(command, "%") { 598 | cutAt := strings.Index(command, "%") 599 | if cutAt != -1 { 600 | command = command[:cutAt-1] 601 | } 602 | } 603 | 604 | var elements = []string{"/usr/bin/env", "-S", command} 605 | 606 | cmd := exec.Command(elements[0], elements[1:]...) 607 | 608 | if terminal { 609 | var prefixCommand = *term 610 | var args []string 611 | if prefixCommand != "foot" { 612 | args = []string{"-e", command} 613 | } else { 614 | args = elements 615 | } 616 | cmd = exec.Command(prefixCommand, args...) 617 | } else if *wm == "sway" { 618 | cmd = exec.Command("swaymsg", "exec", strings.Join(elements, " ")) 619 | } else if *wm == "hyprland" || *wm == "Hyprland" { 620 | cmd = exec.Command("hyprctl", "dispatch", "exec", strings.Join(elements, " ")) 621 | } else if *wm == "river" { 622 | cmd = exec.Command("riverctl", "spawn", strings.Join(elements, " ")) 623 | } 624 | 625 | msg := fmt.Sprintf("command: %q; args: %q\n", cmd.Args[0], cmd.Args[1:]) 626 | println(msg) 627 | 628 | go cmd.Run() 629 | 630 | glib.TimeoutAdd(uint(150), func() bool { 631 | gtk.MainQuit() 632 | return false 633 | }) 634 | } 635 | 636 | func open(filePath string) { 637 | cmd := exec.Command(*fileManager, filePath) 638 | go cmd.Run() 639 | 640 | glib.TimeoutAdd(uint(150), func() bool { 641 | gtk.MainQuit() 642 | return false 643 | }) 644 | } 645 | 646 | // Returns map output name -> gdk.Monitor 647 | func mapOutputs() (map[string]*gdk.Monitor, error) { 648 | result := make(map[string]*gdk.Monitor) 649 | 650 | if os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "" { 651 | err := listHyprlandMonitors() 652 | if err == nil { 653 | 654 | display, err := gdk.DisplayGetDefault() 655 | if err != nil { 656 | return nil, err 657 | } 658 | 659 | num := display.GetNMonitors() 660 | for i := 0; i < num; i++ { 661 | mon, _ := display.GetMonitor(i) 662 | output := hyprlandMonitors[i] 663 | result[output.Name] = mon 664 | } 665 | } else { 666 | return nil, err 667 | } 668 | 669 | } else if os.Getenv("SWAYSOCK") != "" { 670 | ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) 671 | defer cancel() 672 | 673 | client, err := sway.New(ctx) 674 | if err != nil { 675 | return nil, err 676 | } 677 | 678 | outputs, err := client.GetOutputs(ctx) 679 | if err != nil { 680 | return nil, err 681 | } 682 | 683 | display, err := gdk.DisplayGetDefault() 684 | if err != nil { 685 | return nil, err 686 | } 687 | 688 | num := display.GetNMonitors() 689 | for i := 0; i < num; i++ { 690 | mon, _ := display.GetMonitor(i) 691 | output := outputs[i] 692 | result[output.Name] = mon 693 | } 694 | } else { 695 | return nil, errors.New("output assignment only supported on sway and Hyprland") 696 | } 697 | 698 | return result, nil 699 | } 700 | 701 | func hyprctl(cmd string) ([]byte, error) { 702 | his := os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") 703 | xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") 704 | hyprDir := "" 705 | if xdgRuntimeDir != "" { 706 | hyprDir = fmt.Sprintf("%s/hypr", xdgRuntimeDir) 707 | } else { 708 | hyprDir = "/tmp/hypr" 709 | } 710 | 711 | socketFile := fmt.Sprintf("%s/%s/.socket.sock", hyprDir, his) 712 | conn, err := net.Dial("unix", socketFile) 713 | if err != nil { 714 | return nil, err 715 | } 716 | 717 | message := []byte(cmd) 718 | _, err = conn.Write(message) 719 | if err != nil { 720 | return nil, err 721 | } 722 | 723 | reply := make([]byte, 102400) 724 | n, err := conn.Read(reply) 725 | if err != nil { 726 | return nil, err 727 | } 728 | 729 | defer conn.Close() 730 | 731 | return reply[:n], nil 732 | } 733 | 734 | func listHyprlandMonitors() error { 735 | reply, err := hyprctl("j/monitors") 736 | if err != nil { 737 | return err 738 | } else { 739 | err = json.Unmarshal([]byte(reply), &hyprlandMonitors) 740 | } 741 | return err 742 | } 743 | -------------------------------------------------------------------------------- /uicomponents.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | log "github.com/sirupsen/logrus" 6 | "io/fs" 7 | "path/filepath" 8 | "sort" 9 | "strings" 10 | 11 | "github.com/gotk3/gotk3/gdk" 12 | "github.com/gotk3/gotk3/gtk" 13 | ) 14 | 15 | func setUpPinnedListBox() *gtk.ListBox { 16 | listBox, _ := gtk.ListBoxNew() 17 | 18 | if len(pinned) > 0 { 19 | for _, desktopID := range pinned { 20 | entry := id2entry[desktopID] 21 | if entry.DesktopID == "" { 22 | log.Debugf("Pinned item doesn't seem to exist: %s", desktopID) 23 | continue 24 | } 25 | row := setUpPinnedListBoxRow(desktopID) 26 | listBox.Add(row) 27 | } 28 | } 29 | 30 | listBox.Connect("enter-notify-event", func() { 31 | cancelClose() 32 | restoreButtonBox() 33 | }) 34 | 35 | return listBox 36 | } 37 | 38 | func setUpPinnedListBoxRow(desktopID string) *gtk.ListBoxRow { 39 | entry := id2entry[desktopID] 40 | 41 | row, _ := gtk.ListBoxRowNew() 42 | row.SetSelectable(false) 43 | row.SetCanFocus(false) 44 | vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 45 | 46 | // We need gtk.EventBox to detect mouse event 47 | eventBox, _ := gtk.EventBoxNew() 48 | hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 6) 49 | eventBox.Add(hBox) 50 | vBox.PackStart(eventBox, false, false, *itemPadding) 51 | 52 | pixbuf, _ := createPixbuf(entry.Icon, *iconSizeLarge) 53 | img, err := gtk.ImageNewFromPixbuf(pixbuf) 54 | if err != nil { 55 | println(err, entry.Icon) 56 | } 57 | hBox.PackStart(img, false, false, 0) 58 | lbl, _ := gtk.LabelNew("") 59 | name := "" 60 | if entry.NameLoc != "" { 61 | name = entry.NameLoc 62 | } else { 63 | name = entry.Name 64 | } 65 | if len(name) > 35 { 66 | name = fmt.Sprintf("%s...", name[:32]) 67 | } 68 | lbl.SetText(name) 69 | hBox.PackStart(lbl, false, false, 0) 70 | row.Add(vBox) 71 | 72 | row.Connect("activate", func() { 73 | launch(entry.Exec, entry.Terminal) 74 | }) 75 | 76 | eventBox.Connect("button-release-event", func(row *gtk.ListBoxRow, e *gdk.Event) bool { 77 | btnEvent := gdk.EventButtonNewFromEvent(e) 78 | if btnEvent.Button() == 1 { 79 | launch(entry.Exec, entry.Terminal) 80 | return true 81 | } else if btnEvent.Button() == 3 { 82 | unpinItem(entry.DesktopID) 83 | row.Destroy() 84 | return true 85 | } 86 | return false 87 | }) 88 | 89 | if *hover { 90 | eventBox.Connect("enter-notify-event", func(row *gtk.ListBoxRow, e *gdk.Event) bool { 91 | clearSearchResult() 92 | return true 93 | }) 94 | } 95 | 96 | return row 97 | } 98 | 99 | func setUpCategoriesListBox() *gtk.ListBox { 100 | listBox, _ := gtk.ListBoxNew() 101 | for _, cat := range categories { 102 | if isSupposedToShowUp(cat.Name) { 103 | row, _ := gtk.ListBoxRowNew() 104 | row.SetCanFocus(false) 105 | row.SetSelectable(false) 106 | vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 107 | eventBox, _ := gtk.EventBoxNew() 108 | hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 6) 109 | eventBox.Add(hBox) 110 | vBox.PackStart(eventBox, false, false, *itemPadding) 111 | 112 | connectCategoryListBox(cat.Name, eventBox, row) 113 | 114 | pixbuf, _ := createPixbuf(cat.Icon, *iconSizeLarge) 115 | img, _ := gtk.ImageNewFromPixbuf(pixbuf) 116 | hBox.PackStart(img, false, false, 0) 117 | 118 | lbl, _ := gtk.LabelNew(cat.DisplayName) 119 | hBox.PackStart(lbl, false, false, 0) 120 | 121 | pixbuf, _ = createPixbuf("pan-end-symbolic", *iconSizeSmall) 122 | img, _ = gtk.ImageNewFromPixbuf(pixbuf) 123 | hBox.PackEnd(img, false, false, 0) 124 | 125 | row.Add(vBox) 126 | listBox.Add(row) 127 | } 128 | } 129 | listBox.Connect("enter-notify-event", func() { 130 | cancelClose() 131 | restoreButtonBox() 132 | }) 133 | return listBox 134 | } 135 | 136 | func isSupposedToShowUp(catName string) bool { 137 | result := catName == "utility" && notEmpty(listUtility) || 138 | catName == "development" && notEmpty(listDevelopment) || 139 | catName == "game" && notEmpty(listGame) || 140 | catName == "graphics" && notEmpty(listGraphics) || 141 | catName == "internet-and-network" && notEmpty(listInternetAndNetwork) || 142 | catName == "office" && notEmpty(listOffice) || 143 | catName == "audio-video" && notEmpty(listAudioVideo) || 144 | catName == "system-tools" && notEmpty(listSystemTools) || 145 | catName == "other" && notEmpty(listOther) 146 | 147 | return result 148 | } 149 | 150 | func notEmpty(listCategory []string) bool { 151 | if len(listCategory) == 0 { 152 | return false 153 | } 154 | for _, desktopID := range listCategory { 155 | entry := id2entry[desktopID] 156 | if !entry.NoDisplay { 157 | return true 158 | } 159 | } 160 | return false 161 | } 162 | 163 | func connectCategoryListBox(catName string, eventBox *gtk.EventBox, row *gtk.ListBoxRow) { 164 | var listCategory []string 165 | 166 | switch catName { 167 | case "utility": 168 | listCategory = listUtility 169 | case "development": 170 | listCategory = listDevelopment 171 | case "game": 172 | listCategory = listGame 173 | case "graphics": 174 | listCategory = listGraphics 175 | case "internet-and-network": 176 | listCategory = listInternetAndNetwork 177 | case "office": 178 | listCategory = listOffice 179 | case "audio-video": 180 | listCategory = listAudioVideo 181 | case "system-tools": 182 | listCategory = listSystemTools 183 | default: 184 | listCategory = listOther 185 | } 186 | 187 | eventBox.Connect("button-release-event", func(eb *gtk.EventBox, e *gdk.Event) bool { 188 | btnEvent := gdk.EventButtonNewFromEvent(e) 189 | if btnEvent.Button() == 1 { 190 | searchEntry.SetText("") 191 | clearSearchResult() 192 | row.SetSelectable(true) 193 | row.SetCanFocus(false) 194 | categoriesListBox.SelectRow(row) 195 | listBox := setUpCategoryListBox(listCategory) 196 | if resultWindow != nil { 197 | resultWindow.Destroy() 198 | } 199 | resultWindow, _ = gtk.ScrolledWindowNew(nil, nil) 200 | resultWindow.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) 201 | resultWindow.Connect("enter-notify-event", func() { 202 | cancelClose() 203 | }) 204 | resultWrapper.PackStart(resultWindow, true, true, 0) 205 | resultWindow.Add(listBox) 206 | 207 | userDirsListBox.Hide() 208 | resultWindow.ShowAll() 209 | 210 | return true 211 | } 212 | return false 213 | }) 214 | 215 | if *hover { 216 | eventBox.Connect("enter-notify-event", func(eb *gtk.EventBox, e *gdk.Event) bool { 217 | searchEntry.SetText("") 218 | clearSearchResult() 219 | row.SetSelectable(true) 220 | row.SetCanFocus(false) 221 | //categoriesListBox.SelectRow(row) 222 | listBox := setUpCategoryListBox(listCategory) 223 | if resultWindow != nil { 224 | resultWindow.Destroy() 225 | } 226 | resultWindow, _ = gtk.ScrolledWindowNew(nil, nil) 227 | resultWindow.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) 228 | resultWindow.Connect("enter-notify-event", func() { 229 | cancelClose() 230 | }) 231 | resultWrapper.PackStart(resultWindow, true, true, 0) 232 | resultWindow.Add(listBox) 233 | 234 | userDirsListBox.Hide() 235 | resultWindow.ShowAll() 236 | 237 | return true 238 | }) 239 | } 240 | } 241 | 242 | func setUpBackButton() *gtk.Box { 243 | vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 244 | hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 10) 245 | vBox.PackStart(hBox, false, false, 0) 246 | button, _ := gtk.ButtonNew() 247 | button.SetCanFocus(false) 248 | pixbuf, _ := createPixbuf("arrow-left", *iconSizeLarge) 249 | image, _ := gtk.ImageNewFromPixbuf(pixbuf) 250 | button.SetImage(image) 251 | button.SetAlwaysShowImage(true) 252 | button.Connect("enter-notify-event", func() { 253 | cancelClose() 254 | }) 255 | button.Connect("clicked", func(btn *gtk.Button) { 256 | clearSearchResult() 257 | searchEntry.GrabFocus() 258 | searchEntry.SetText("") 259 | }) 260 | hBox.PackEnd(button, false, true, 0) 261 | 262 | return vBox 263 | } 264 | 265 | func setUpCategoryListBox(listCategory []string) *gtk.ListBox { 266 | listBox, _ := gtk.ListBoxNew() 267 | 268 | for _, desktopID := range listCategory { 269 | entry := id2entry[desktopID] 270 | name := entry.NameLoc 271 | if name == "" { 272 | name = entry.Name 273 | } 274 | if len(name) > 30 { 275 | name = fmt.Sprintf("%s...", name[:27]) 276 | } 277 | if !entry.NoDisplay { 278 | row, _ := gtk.ListBoxRowNew() 279 | row.SetSelectable(false) 280 | vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 10) 281 | eventBox, _ := gtk.EventBoxNew() 282 | hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 6) 283 | eventBox.Add(hBox) 284 | vBox.PackStart(eventBox, false, false, *itemPadding) 285 | 286 | ID := entry.DesktopID 287 | eventBox.Connect("button-release-event", func(row *gtk.ListBoxRow, e *gdk.Event) bool { 288 | btnEvent := gdk.EventButtonNewFromEvent(e) 289 | if btnEvent.Button() == 1 { 290 | launch(entry.Exec, entry.Terminal) 291 | return true 292 | } else if btnEvent.Button() == 3 { 293 | pinItem(ID) 294 | } 295 | return false 296 | }) 297 | 298 | pixbuf, _ := createPixbuf(entry.Icon, *iconSizeLarge) 299 | img, _ := gtk.ImageNewFromPixbuf(pixbuf) 300 | hBox.PackStart(img, false, false, 0) 301 | 302 | lbl, _ := gtk.LabelNew(name) 303 | hBox.PackStart(lbl, false, false, 0) 304 | 305 | row.Add(vBox) 306 | listBox.Add(row) 307 | } 308 | } 309 | backButton.Show() 310 | return listBox 311 | } 312 | 313 | func setUpCategorySearchResult(searchPhrase string) *gtk.ListBox { 314 | listBox, _ := gtk.ListBoxNew() 315 | 316 | resultWindow, _ = gtk.ScrolledWindowNew(nil, nil) 317 | resultWindow.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) 318 | resultWindow.Connect("enter-notify-event", func() { 319 | cancelClose() 320 | restoreButtonBox() 321 | }) 322 | resultWrapper.PackStart(resultWindow, true, true, 0) 323 | 324 | counter := 0 325 | for _, entry := range desktopEntries { 326 | if len(searchPhrase) == 1 && counter > 9 { 327 | break 328 | } else if len(searchPhrase) == 2 && counter > 14 { 329 | break 330 | } 331 | if !entry.NoDisplay && (strings.Contains(strings.ToLower(entry.NameLoc), strings.ToLower(searchPhrase)) || 332 | strings.Contains(strings.ToLower(entry.CommentLoc), strings.ToLower(searchPhrase)) || 333 | strings.Contains(strings.ToLower(entry.Comment), strings.ToLower(searchPhrase)) || 334 | strings.Contains(strings.ToLower(entry.Exec), strings.ToLower(searchPhrase))) { 335 | 336 | counter++ 337 | 338 | row, _ := gtk.ListBoxRowNew() 339 | row.SetSelectable(false) 340 | vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 10) 341 | eventBox, _ := gtk.EventBoxNew() 342 | hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 6) 343 | eventBox.Add(hBox) 344 | vBox.PackStart(eventBox, false, false, *itemPadding) 345 | 346 | exec := entry.Exec 347 | term := entry.Terminal 348 | ID := entry.DesktopID 349 | row.Connect("activate", func() { 350 | launch(exec, term) 351 | }) 352 | eventBox.Connect("button-release-event", func(row *gtk.EventBox, e *gdk.Event) bool { 353 | btnEvent := gdk.EventButtonNewFromEvent(e) 354 | if btnEvent.Button() == 1 { 355 | launch(exec, term) 356 | return true 357 | } else if btnEvent.Button() == 3 { 358 | pinItem(ID) 359 | } 360 | return false 361 | }) 362 | 363 | pixbuf, _ := createPixbuf(entry.Icon, *iconSizeLarge) 364 | img, _ := gtk.ImageNewFromPixbuf(pixbuf) 365 | hBox.PackStart(img, false, false, 0) 366 | 367 | name := entry.NameLoc 368 | if len(name) > *sLen { 369 | name = fmt.Sprintf("%s…", name[:*sLen-2]) 370 | } 371 | 372 | lbl, _ := gtk.LabelNew(name) 373 | hBox.PackStart(lbl, false, false, 0) 374 | 375 | row.Add(vBox) 376 | listBox.Add(row) 377 | 378 | } 379 | } 380 | resultWindow.Add(listBox) 381 | resultWindow.ShowAll() 382 | return listBox 383 | } 384 | 385 | func setUpFileSearchResult() *gtk.ListBox { 386 | listBox, _ := gtk.ListBoxNew() 387 | if fileSearchResultWindow != nil { 388 | fileSearchResultWindow.Destroy() 389 | } 390 | fileSearchResultWindow, _ = gtk.ScrolledWindowNew(nil, nil) 391 | fileSearchResultWindow.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) 392 | fileSearchResultWindow.Connect("enter-notify-event", func() { 393 | cancelClose() 394 | restoreButtonBox() 395 | }) 396 | resultWrapper.PackStart(fileSearchResultWindow, true, true, 0) 397 | 398 | fileSearchResultWindow.Add(listBox) 399 | fileSearchResultWindow.ShowAll() 400 | 401 | return listBox 402 | } 403 | 404 | func walk(path string, d fs.DirEntry, e error) error { 405 | if e != nil { 406 | return e 407 | } 408 | if !d.IsDir() { 409 | parts := strings.Split(path, "/") 410 | fileName := parts[len(parts)-1] 411 | if strings.Contains(strings.ToLower(fileName), strings.ToLower(phrase)) { 412 | fileSearchResults[fileName] = path 413 | } 414 | } else { 415 | if strings.Contains(strings.ToLower(path), strings.ToLower(phrase)) { 416 | fileSearchResults[path] = path 417 | } 418 | } 419 | return nil 420 | } 421 | 422 | func setUpSearchEntry() *gtk.SearchEntry { 423 | searchEntry, _ := gtk.SearchEntryNew() 424 | searchEntry.Connect("enter-notify-event", func() { 425 | cancelClose() 426 | restoreButtonBox() 427 | }) 428 | //if *hover { 429 | // searchEntry.Connect("enter-notify-event", func(se *gtk.SearchEntry, e *gdk.Event) bool { 430 | // clearSearchResult() 431 | // return true 432 | // }) 433 | //} 434 | 435 | searchEntry.Connect("search-changed", func() { 436 | phrase, _ = searchEntry.GetText() 437 | if len(phrase) > 0 { 438 | userDirsListBox.Hide() 439 | backButton.Show() 440 | 441 | if resultWindow != nil { 442 | resultWindow.Destroy() 443 | } 444 | resultListBox = setUpCategorySearchResult(phrase) 445 | if resultListBox.GetChildren().Length() == 0 { 446 | resultWindow.Hide() 447 | } 448 | 449 | if len(phrase) > 2 { 450 | if fileSearchResultWindow != nil { 451 | fileSearchResultWindow.Destroy() 452 | } 453 | fileSearchResultListBox = setUpFileSearchResult() 454 | for key := range userDirsMap { 455 | if key != "home" { 456 | searchUserDir(key) 457 | } 458 | } 459 | if fileSearchResultListBox.GetChildren().Length() == 0 { 460 | fileSearchResultWindow.Hide() 461 | } 462 | } else { 463 | if fileSearchResultWindow != nil { 464 | fileSearchResultWindow.Destroy() 465 | } 466 | } 467 | 468 | } else { 469 | clearSearchResult() 470 | userDirsListBox.ShowAll() 471 | } 472 | 473 | }) 474 | searchEntry.Connect("focus-in-event", func() { 475 | searchEntry.SetText("") 476 | }) 477 | 478 | return searchEntry 479 | } 480 | 481 | func searchUserDir(dir string) { 482 | fileSearchResults = make(map[string]string) 483 | filepath.WalkDir(userDirsMap[dir], walk) 484 | if len(fileSearchResults) > 0 { 485 | row := setUpUserDirsListRow(fmt.Sprintf("folder-%s", dir), "", dir, userDirsMap) 486 | fileSearchResultListBox.Add(row) 487 | fileSearchResultListBox.ShowAll() 488 | 489 | // The fileSearchResults map is unordered, but we need an order here. 490 | // Let's create a slice of file names, sort it, and read the map in this order. 491 | 492 | fileNames := make([]string, 0, len(fileSearchResults)) 493 | for k := range fileSearchResults { 494 | fileNames = append(fileNames, k) 495 | } 496 | sort.Strings(fileNames) 497 | 498 | for _, name := range fileNames { 499 | path := fileSearchResults[name] 500 | isDir := false 501 | if strings.HasPrefix(name, userDirsMap[dir]) { 502 | isDir = true 503 | } 504 | row := setUpUserFileSearchResultRow(path, userDirsMap[dir], isDir) 505 | fileSearchResultListBox.Add(row) 506 | } 507 | 508 | fileSearchResultListBox.ShowAll() 509 | } 510 | } 511 | 512 | func setUpUserDirsList() *gtk.ListBox { 513 | listBox, _ := gtk.ListBoxNew() 514 | userDirsMap = mapXdgUserDirs() 515 | 516 | row := setUpUserDirsListRow("folder-home", "Home", "home", userDirsMap) 517 | listBox.Add(row) 518 | row = setUpUserDirsListRow("folder-documents", "", "documents", userDirsMap) 519 | listBox.Add(row) 520 | row = setUpUserDirsListRow("folder-downloads", "", "downloads", userDirsMap) 521 | listBox.Add(row) 522 | row = setUpUserDirsListRow("folder-music", "", "music", userDirsMap) 523 | listBox.Add(row) 524 | row = setUpUserDirsListRow("folder-pictures", "", "pictures", userDirsMap) 525 | listBox.Add(row) 526 | row = setUpUserDirsListRow("folder-videos", "", "videos", userDirsMap) 527 | listBox.Add(row) 528 | 529 | listBox.Connect("enter-notify-event", func() { 530 | cancelClose() 531 | restoreButtonBox() 532 | }) 533 | 534 | return listBox 535 | } 536 | 537 | func setUpUserDirsListRow(iconName, displayName, entryName string, userDirsMap map[string]string) *gtk.ListBoxRow { 538 | if displayName == "" { 539 | parts := strings.Split(userDirsMap[entryName], "/") 540 | displayName = parts[(len(parts) - 1)] 541 | } 542 | row, _ := gtk.ListBoxRowNew() 543 | row.SetSelectable(false) 544 | vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 545 | eventBox, _ := gtk.EventBoxNew() 546 | hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 6) 547 | eventBox.Add(hBox) 548 | vBox.PackStart(eventBox, false, false, *itemPadding*3) 549 | 550 | img, _ := gtk.ImageNewFromIconName(iconName, gtk.ICON_SIZE_DND) 551 | hBox.PackStart(img, false, false, 0) 552 | 553 | if len(displayName) > *sLen { 554 | displayName = fmt.Sprintf("%s…", displayName[:*sLen-2]) 555 | } 556 | lbl, _ := gtk.LabelNew(displayName) 557 | hBox.PackStart(lbl, false, false, 0) 558 | row.Add(vBox) 559 | 560 | row.Connect("activate", func() { 561 | launch(fmt.Sprintf("%s %s", *fileManager, userDirsMap[entryName]), false) 562 | }) 563 | 564 | eventBox.Connect("button-release-event", func(row *gtk.ListBoxRow, e *gdk.Event) bool { 565 | btnEvent := gdk.EventButtonNewFromEvent(e) 566 | if btnEvent.Button() == 1 { 567 | launch(fmt.Sprintf("%s %s", *fileManager, userDirsMap[entryName]), false) 568 | return true 569 | } 570 | return false 571 | }) 572 | 573 | return row 574 | } 575 | 576 | func setUpUserFileSearchResultRow(filePath, userDirPath string, isDir bool) *gtk.ListBoxRow { 577 | row, _ := gtk.ListBoxRowNew() 578 | 579 | row.SetSelectable(false) 580 | vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 581 | eventBox, _ := gtk.EventBoxNew() 582 | hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) 583 | eventBox.Add(hBox) 584 | vBox.PackStart(eventBox, false, false, *itemPadding) 585 | 586 | // split the leading part we don't want to see 587 | fileName := strings.Split(filePath, userDirPath)[1] 588 | if len(fileName) > 0 { 589 | // split leading slash 590 | fileName = fileName[1:] 591 | } 592 | 593 | if isDir { 594 | if len(fileName) > 0 { 595 | img, _ := gtk.ImageNewFromIconName("folder", gtk.ICON_SIZE_MENU) 596 | hBox.PackStart(img, false, false, 0) 597 | } 598 | } 599 | 600 | if len(fileName) > *sLen { 601 | fileName = fmt.Sprintf("%s…", fileName[:*sLen-2]) 602 | } 603 | if fileName != "" { 604 | lbl, _ := gtk.LabelNew(fileName) 605 | hBox.PackStart(lbl, false, false, 0) 606 | } 607 | 608 | row.Add(vBox) 609 | 610 | row.Connect("activate", func() { 611 | open(filePath) 612 | }) 613 | 614 | eventBox.Connect("button-release-event", func(row *gtk.ListBoxRow, e *gdk.Event) bool { 615 | btnEvent := gdk.EventButtonNewFromEvent(e) 616 | if btnEvent.Button() == 1 { 617 | open(filePath) 618 | return true 619 | } 620 | return false 621 | }) 622 | 623 | return row 624 | } 625 | 626 | func setUpButtonBox() *gtk.EventBox { 627 | eventBox, _ := gtk.EventBoxNew() 628 | wrapperHbox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) 629 | box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) 630 | wrapperHbox.PackStart(box, false, true, 10) 631 | eventBox.Add(wrapperHbox) 632 | 633 | btn, _ := gtk.ButtonNew() 634 | pixbuf, _ := createPixbuf("system-lock-screen", *iconSizeLarge) 635 | img, _ := gtk.ImageNewFromPixbuf(pixbuf) 636 | btn.SetImage(img) 637 | btn.SetCanFocus(false) 638 | box.PackStart(btn, true, true, 6) 639 | btn.Connect("clicked", func() { 640 | launch(*cmdLock, false) 641 | //confirmationBox = setUpConfirmationBox("system-lock-screen", *cmdLock) 642 | buttonBox.Hide() 643 | }) 644 | 645 | btn, _ = gtk.ButtonNew() 646 | pixbuf, _ = createPixbuf("system-log-out", *iconSizeLarge) 647 | img, _ = gtk.ImageNewFromPixbuf(pixbuf) 648 | btn.SetImage(img) 649 | btn.SetCanFocus(false) 650 | box.PackStart(btn, true, true, 6) 651 | btn.Connect("clicked", func() { 652 | confirmationBox = setUpConfirmationBox("system-log-out", *cmdLogout) 653 | buttonBox.Hide() 654 | }) 655 | 656 | btn, _ = gtk.ButtonNew() 657 | pixbuf, _ = createPixbuf("system-reboot", *iconSizeLarge) 658 | img, _ = gtk.ImageNewFromPixbuf(pixbuf) 659 | btn.SetImage(img) 660 | btn.SetCanFocus(false) 661 | box.PackStart(btn, true, true, 6) 662 | btn.Connect("clicked", func() { 663 | confirmationBox = setUpConfirmationBox("system-reboot", *cmdRestart) 664 | buttonBox.Hide() 665 | }) 666 | 667 | btn, _ = gtk.ButtonNew() 668 | pixbuf, _ = createPixbuf("system-shutdown", *iconSizeLarge) 669 | img, _ = gtk.ImageNewFromPixbuf(pixbuf) 670 | btn.SetImage(img) 671 | btn.SetCanFocus(false) 672 | box.PackStart(btn, true, true, 6) 673 | btn.Connect("clicked", func() { 674 | confirmationBox = setUpConfirmationBox("system-shutdown", *cmdShutdown) 675 | buttonBox.Hide() 676 | }) 677 | 678 | eventBox.Connect("enter-notify-event", func() { 679 | cancelClose() 680 | }) 681 | 682 | return eventBox 683 | } 684 | 685 | func setUpConfirmationBox(icon string, command string) *gtk.Box { 686 | box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) 687 | 688 | btn, _ := gtk.ButtonNew() 689 | pixbuf, _ := createPixbuf(icon, *iconSizeLarge) 690 | img, _ := gtk.ImageNewFromPixbuf(pixbuf) 691 | btn.SetImage(img) 692 | btn.SetCanFocus(false) 693 | box.PackEnd(btn, false, false, 16) 694 | btn.Connect("clicked", func() { 695 | defer restoreButtonBox() 696 | launch(command, false) 697 | 698 | }) 699 | btn.Connect("enter-notify-event", func() { 700 | cancelClose() 701 | }) 702 | 703 | btn, _ = gtk.ButtonNew() 704 | pixbuf, _ = createPixbuf("dialog-cancel", *iconSizeLarge) 705 | img, _ = gtk.ImageNewFromPixbuf(pixbuf) 706 | btn.SetImage(img) 707 | btn.SetCanFocus(false) 708 | box.PackEnd(btn, false, false, 6) 709 | btn.Connect("clicked", func() { 710 | restoreButtonBox() 711 | }) 712 | btn.Connect("enter-notify-event", func() { 713 | cancelClose() 714 | }) 715 | 716 | buttonsWrapper.PackEnd(box, false, false, 10) 717 | 718 | box.ShowAll() 719 | w := buttonBox.GetAllocatedWidth() 720 | h := buttonBox.GetAllocatedHeight() 721 | box.SetSizeRequest(w, h) 722 | box.SetHExpand(false) 723 | 724 | return box 725 | } 726 | 727 | func restoreButtonBox() { 728 | if confirmationBox != nil { 729 | confirmationBox.Destroy() 730 | } 731 | if !buttonBox.IsVisible() { 732 | buttonBox.Show() 733 | } 734 | } 735 | 736 | func clearSearchResult() { 737 | if resultWindow != nil { 738 | resultWindow.Destroy() 739 | } 740 | if fileSearchResultWindow != nil { 741 | fileSearchResultWindow.Destroy() 742 | } 743 | if userDirsListBox != nil { 744 | userDirsListBox.ShowAll() 745 | } 746 | if categoriesListBox != nil { 747 | sr := categoriesListBox.GetSelectedRow() 748 | if sr != nil { 749 | categoriesListBox.GetSelectedRow().SetSelectable(false) 750 | } 751 | categoriesListBox.UnselectAll() 752 | } 753 | backButton.Hide() 754 | 755 | } 756 | --------------------------------------------------------------------------------