diff --git a/build.sh b/build.sh index e6b0224..654721a 100755 --- a/build.sh +++ b/build.sh @@ -72,6 +72,13 @@ install -m 644 wdx/mediainfo/luajit/*.lua release/wdx/mediainfo/ install -m 644 wdx/translitwdx/translitwdx.lua release/wdx/translitwdx/ install -m 644 wdx/translitwdx/readme.txt release/wdx/translitwdx/ +# jsonview +mkdir -p release/wlx/jsonview +make -C wlx/jsonview/src clean all +install -m 644 wlx/jsonview/jsonview_qt6.wlx release/wlx/jsonview/ +cp -r wlx/jsonview/langs release/wlx/jsonview/ +install -m 644 wlx/jsonview/*.md release/wlx/jsonview/ +install -m 644 wlx/jsonview/*.png release/wlx/jsonview/ # logview mkdir -p release/wlx/logview mkdir -p wlx/logview/build diff --git a/sdk/wlxplugin.h b/sdk/wlxplugin.h index 8f33fb2..eebce8e 100644 --- a/sdk/wlxplugin.h +++ b/sdk/wlxplugin.h @@ -9,6 +9,7 @@ #define lc_newparams 2 #define lc_selectall 3 #define lc_setpercent 4 +#define lc_focus 5 #define lcp_wraptext 1 #define lcp_fittowindow 2 diff --git a/wlx/jsonview/README.md b/wlx/jsonview/README.md new file mode 100644 index 0000000..9410463 --- /dev/null +++ b/wlx/jsonview/README.md @@ -0,0 +1,59 @@ +# JSON Tree Viewer Lister Plugin for Double Commander (Linux/Wayland) + +A WLX (Lister) plugin for Double Commander built with Qt6 to visualize, navigate, edit, and export **JSON** (`.json`) files using an interactive, hierarchical Tree view. + +This plugin is a Qt port of the original work by **j2969719**. You can find the original author's repository at [https://github.com/j2969719/doublecmd-plugins](https://github.com/j2969719/doublecmd-plugins). + +--- + +## Screenshots + +### JSON Tree Representation +![JSON Tree View](jsonview1.png) + +### Expanded Structure & Values +![Expanded JSON View](jsonview2.png) + +--- + +## Features + +- **Interactive Tree Hierarchy**: Visualizes nested JSON objects and arrays in a structured, collapsible tree (`QTreeWidget`). +- **Data Columns**: + - **Node**: Keys/labels of objects or indices of arrays. + - **Value**: Element values (strings, numbers, booleans, null). + - **Type**: Automatically shows the data type (Object, Array, String, Integer, Double, Boolean, Null). +- **Inline Editing**: Double-click on any value field (Column 1) to edit the JSON data directly within Lister. +- **Saving Changes**: + - `Ctrl+S` or click **Save** to overwrite the file. + - **Save As...** to save the modified JSON to a new path. +- **Search Support**: Press `F7` (or native Lister search) to search for specific text within node keys, values, and types. Supports case sensitivity. +- **Right-Click Context Menu Actions**: + - **Copy JSONPath**: Copy the dotted path to the selected node (e.g. `store.book[0].title`) directly to the clipboard. + - **Copy Subtree**: Rasterize/serialize the selected subtree back to indented JSON text format and copy it to the clipboard. + - **Copy Key:Value**: Copy the key and value pair to the clipboard. + - **Copy Value**: Copy the exact value of the selected node to the clipboard. + +--- + +## Installation + +1. Switch to the `jsonview` branch and run `./build.sh` to compile the plugin. +2. The binary `jsonview_qt6.wlx` will be built under `release/wlx/jsonview/`. +3. In Double Commander, open **Options** -> **Plugins** -> **WLX**. +4. Click **Add** and select `/path/to/jsonview_qt6.wlx`. +5. Double Commander will register the extension string. Ensure the detect string is configured as: + ``` + (EXT="JSON") & SIZE<30000000 + ``` + +--- + +## Configuration + +The plugin configuration is stored in `j2969719.ini` inside the Double Commander settings directory. You can edit settings under the `[jsonview]` section: +- `resize_columns` (boolean): Automatically resize column widths to fit content. +- `tree_expand` (boolean): Expand all tree nodes on load. +- `column_width` (integer): Default width of columns if auto-resize is off. +- `sorting` (boolean): Enable alphabetical sorting of object keys. +- `show_filename` (boolean): Show the filename as the root node, or default to "Root". diff --git a/wlx/jsonview/jsonview1.png b/wlx/jsonview/jsonview1.png new file mode 100644 index 0000000..a6654c5 Binary files /dev/null and b/wlx/jsonview/jsonview1.png differ diff --git a/wlx/jsonview/jsonview2.png b/wlx/jsonview/jsonview2.png new file mode 100644 index 0000000..0187138 Binary files /dev/null and b/wlx/jsonview/jsonview2.png differ diff --git a/wlx/jsonview/langs/ru/LC_MESSAGES/plugins.mo b/wlx/jsonview/langs/ru/LC_MESSAGES/plugins.mo new file mode 100644 index 0000000..e3ea054 Binary files /dev/null and b/wlx/jsonview/langs/ru/LC_MESSAGES/plugins.mo differ diff --git a/wlx/jsonview/langs/ru/LC_MESSAGES/plugins.po b/wlx/jsonview/langs/ru/LC_MESSAGES/plugins.po new file mode 100644 index 0000000..80889d4 --- /dev/null +++ b/wlx/jsonview/langs/ru/LC_MESSAGES/plugins.po @@ -0,0 +1,565 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-02-07 23:08+0300\n" +"PO-Revision-Date: 2026-02-07 23:13+0300\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"X-Generator: Poedit 3.8\n" + +#: ../wlx/libarchive_cat_crap/src/plugin.c:65 +#: ../wlx/libarchive_cat_qt_crap/src/plugin.cpp:56 +#, c-format +msgid "libarchive: failed to read %s" +msgstr "libarchive: не удалось прочитать %s" + +#: ../wlx/libarchive_cat_crap/src/plugin.c:196 +#: ../wlx/libarchive_cat_qt_crap/src/plugin.cpp:147 +#: ../wlx/md4c_qt/src/plugin.cpp:97 ../wlx/fileinfo_qt/src/plugin.cpp:97 +#: ../wlx/htmlconv_qt_crap/src/plugin.cpp:118 +#: ../wlx/wlxwebkit/src/wlxwebkit.c:128 +#: ../wlx/yet_another_vte_plugin/src/plugin.c:288 +#: ../wlx/gtksourceview/src/gtksourceview.c:720 +#: ../wlx/sqlview_qt/src/plugin.cpp:273 +#: ../wlx/syntax-highlighting_qt/src/plugin.cpp:153 +#: ../wlx/md4c_webkit_qt/src/plugin.cpp:90 ../wlx/csvview_qt/src/plugin.cpp:307 +#: ../wlx/hx_qt_crap/src/plugin.cpp:81 ../wlx/fileinfo/src/fileinfo.c:403 +#: ../wlx/jsonview_qt/src/plugin.cpp:262 +#: ../wlx/mimescript/src/mimescriptwlx.c:309 +#: ../wlx/htmlview_qt_crap/src/plugin.cpp:55 +#: ../wlx/wlxwebkit_qt/src/wlxwebkit.cpp:167 +#: ../wlx/wlxwebkit_qt_crap/src/wlxwebkit.cpp:111 +#, c-format +msgid "\"%s\" not found!" +msgstr "\"%s\" не найден!" + +#: ../wlx/imagemagick/src/wlximagemagick.c:215 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:371 +#: ../wlx/qtpdfview_qt/src/plugin.cpp:161 +#: ../wlx/gtkimgview/src/gtkimgview.c:222 +msgid "Zoom In" +msgstr "Увеличить" + +#: ../wlx/imagemagick/src/wlximagemagick.c:220 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:376 +#: ../wlx/qtpdfview_qt/src/plugin.cpp:172 +#: ../wlx/gtkimgview/src/gtkimgview.c:227 +msgid "Zoom Out" +msgstr "Уменьшить" + +#: ../wlx/imagemagick/src/wlximagemagick.c:225 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:381 +#: ../wlx/qtpdfview_qt/src/plugin.cpp:183 +#: ../wlx/gtkimgview/src/gtkimgview.c:232 +msgid "Original Size" +msgstr "Исходный размер" + +#: ../wlx/imagemagick/src/wlximagemagick.c:230 +#: ../wlx/wlxpview/src/wlxPView.c:469 ../wlx/wlxpview/src/wlxPView.c:470 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:386 +#: ../wlx/qtpdfview_qt/src/plugin.cpp:196 +#: ../wlx/gtkimgview/src/gtkimgview.c:237 +msgid "Fit" +msgstr "Вместить" + +#: ../wlx/imagemagick/src/wlximagemagick.c:237 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:393 +#: ../wlx/gtkimgview/src/gtkimgview.c:244 +msgid "Copy to Clipboard" +msgstr "Копировать в буфер" + +#: ../wlx/imagemagick/src/wlximagemagick.c:240 +#: ../wlx/imagemagick/src/wlximagemagick.c:243 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:396 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:399 +#: ../wlx/gtkimgview/src/gtkimgview.c:247 +#: ../wlx/gtkimgview/src/gtkimgview.c:250 +msgid "Rotate" +msgstr "Повернуть" + +#: ../wlx/imagemagick/src/wlximagemagick.c:246 +#: ../wlx/imagemagick/src/wlximagemagick.c:249 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:402 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:405 +#: ../wlx/gtkimgview/src/gtkimgview.c:253 +#: ../wlx/gtkimgview/src/gtkimgview.c:256 +msgid "Rotate Clockwise" +msgstr "Повернуть по часовой стрелке" + +#: ../wlx/imagemagick/src/wlximagemagick.c:252 +#: ../wlx/imagemagick/src/wlximagemagick.c:255 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:408 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:411 +#: ../wlx/gtkimgview/src/gtkimgview.c:259 +#: ../wlx/gtkimgview/src/gtkimgview.c:262 +msgid "Flip Horizontally" +msgstr "Отразить по горизонтали" + +#: ../wlx/imagemagick/src/wlximagemagick.c:258 +#: ../wlx/imagemagick/src/wlximagemagick.c:261 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:414 +#: ../wlx/gtkimgview_crap/src/gtkimgview.c:417 +#: ../wlx/gtkimgview/src/gtkimgview.c:265 +#: ../wlx/gtkimgview/src/gtkimgview.c:268 +msgid "Flip Vertically" +msgstr "Отразить по вертикали" + +#: ../wlx/dirsize_crap/src/plugin.c:245 +msgid "Name" +msgstr "Название" + +#: ../wlx/dirsize_crap/src/plugin.c:258 +msgid "Size" +msgstr "Размер" + +#: ../wlx/wlxpview/src/wlxPView.c:139 +msgid "Scale ~x" +msgstr "Масштаб ~x" + +#: ../wlx/wlxpview/src/wlxPView.c:183 ../wlx/wlxpview/src/wlxPView.c:438 +#: ../wlx/wlxpview/src/wlxPView.c:439 ../wlx/qtpdfview_qt/src/plugin.cpp:140 +msgid "Go to..." +msgstr "Перейти..." + +#: ../wlx/wlxpview/src/wlxPView.c:193 ../wlx/qtpdfview_qt/src/plugin.cpp:148 +msgid "Page number to go to:" +msgstr "Страница на которую перейти:" + +#: ../wlx/wlxpview/src/wlxPView.c:296 ../wlx/wlxpview/src/wlxPView.c:477 +#: ../wlx/wlxpview/src/wlxPView.c:478 +msgid "Text" +msgstr "Текст" + +#: ../wlx/wlxpview/src/wlxPView.c:324 ../wlx/wlxpview/src/wlxPView.c:483 +#: ../wlx/wlxpview/src/wlxPView.c:484 ../wlx/qtpdfview_qt/src/plugin.cpp:222 +msgid "Info" +msgstr "Информация" + +#: ../wlx/wlxpview/src/wlxPView.c:328 +#, c-format +msgid "" +"Title: %s\n" +"Author: %s\n" +"Subject: %s\n" +"Creator: %s\n" +"Producer: %s\n" +"Keywords: %s\n" +"Version: %s" +msgstr "" +"Название: %s\n" +"Автор: %s\n" +"Тема: %s\n" +"Создатель: %s\n" +"Сегнерировано: %s\n" +"Ключевые слова: %s\n" +"Версия: %s" + +#: ../wlx/wlxpview/src/wlxPView.c:346 +msgid "metadata not found" +msgstr "Метаданные не найдены" + +#: ../wlx/wlxpview/src/wlxPView.c:414 ../wlx/wlxpview/src/wlxPView.c:415 +#: ../wlx/qtpdfview_qt/src/plugin.cpp:60 +msgid "First page" +msgstr "Первая страница" + +#: ../wlx/wlxpview/src/wlxPView.c:420 ../wlx/wlxpview/src/wlxPView.c:421 +#: ../wlx/qtpdfview_qt/src/plugin.cpp:72 +msgid "Previous page" +msgstr "Предыдущая страница" + +#: ../wlx/wlxpview/src/wlxPView.c:426 ../wlx/wlxpview/src/wlxPView.c:427 +#: ../wlx/qtpdfview_qt/src/plugin.cpp:87 +msgid "Next page" +msgstr "Следующая страница" + +#: ../wlx/wlxpview/src/wlxPView.c:432 ../wlx/wlxpview/src/wlxPView.c:433 +#: ../wlx/qtpdfview_qt/src/plugin.cpp:102 +msgid "Last page" +msgstr "Последняя страница" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:208 +msgid "Page Mode" +msgstr "Режим страницы" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:228 +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:54 +msgid "Author" +msgstr "" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:229 +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:50 +msgid "Title" +msgstr "" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:230 +msgid "Subject" +msgstr "" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:231 +msgid "Producer" +msgstr "" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:232 +msgid "Creator" +msgstr "" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:233 +msgid "Keywords" +msgstr "" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:234 +msgid "Creation Date" +msgstr "" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:235 +msgid "Modification Date" +msgstr "" + +#: ../wlx/qtpdfview_qt/src/plugin.cpp:255 +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:468 +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:524 +msgid "no suitable info available" +msgstr "" + +#: ../wlx/sqlview_gtk2/src/plugin.c:124 ../wlx/sqlview_gtk2/src/plugin.c:222 +#: ../wlx/sqlview_qt/src/plugin.cpp:97 +msgid "Query" +msgstr "" + +#: ../wlx/sqlview_gtk2/src/plugin.c:132 ../wlx/sqlview_qt/src/plugin.cpp:131 +msgid "Please enter a new query and press OK to execute it" +msgstr "" + +#: ../wlx/sqlview_gtk2/src/plugin.c:227 ../wlx/sqlview_qt/src/plugin.cpp:89 +msgid "Table:" +msgstr "" + +#: ../wlx/gtksourceview/src/gtksourceview.c:52 +msgid "Failed to load file" +msgstr "Не удалось открыть файл" + +#: ../wlx/gtksourceview/src/gtksourceview.c:53 +#: ../wlx/gtksourceview/src/gtksourceview.c:360 +#: ../wlx/gtksourceview/src/gtksourceview.c:563 +msgid "Default" +msgstr "По умолчанию" + +#: ../wlx/gtksourceview/src/gtksourceview.c:54 +#: ../wlx/gtksourceview/src/gtksourceview.c:380 +msgid "Encoding:" +msgstr "Кодировка:" + +#: ../wlx/gtksourceview/src/gtksourceview.c:200 +#: ../wlx/gtksourceview/src/gtksourceview.c:590 +msgid "Options" +msgstr "Настройки" + +#: ../wlx/gtksourceview/src/gtksourceview.c:207 +msgid "Font" +msgstr "Шрифт" + +#: ../wlx/gtksourceview/src/gtksourceview.c:215 +msgid "Style" +msgstr "Тема" + +#: ../wlx/gtksourceview/src/gtksourceview.c:232 +msgid "Blank space" +msgstr "Отсупы" + +#: ../wlx/gtksourceview/src/gtksourceview.c:238 +msgid "Above paragraphs" +msgstr "Над текстом" + +#: ../wlx/gtksourceview/src/gtksourceview.c:239 +msgid "Below paragraphs" +msgstr "Под текстом" + +#: ../wlx/gtksourceview/src/gtksourceview.c:240 +msgid "Tab width" +msgstr "Ширина табуляции" + +#: ../wlx/gtksourceview/src/gtksourceview.c:260 +msgid "Enca Lang" +msgstr "Локаль Enca" + +#: ../wlx/gtksourceview/src/gtksourceview.c:336 +msgid "Language:" +msgstr "Язык:" + +#: ../wlx/gtksourceview/src/gtksourceview.c:356 +msgid "file seems empty" +msgstr "походу файл пустой" + +#: ../wlx/gtksourceview/src/gtksourceview.c:573 +msgid "Custom encoding" +msgstr "Другая кодировка" + +#: ../wlx/gtksourceview/src/gtksourceview.c:597 +msgid "Draw Spaces" +msgstr "Пробелы" + +#: ../wlx/gtksourceview/src/gtksourceview.c:598 +msgid "Text Cursor" +msgstr "Курсор" + +#: ../wlx/gtksourceview/src/gtksourceview.c:599 +msgid "Line Numbers" +msgstr "Номера строк" + +#: ../wlx/gtksourceview/src/gtksourceview.c:600 +msgid "Highlight Line" +msgstr "Подсветка строки" + +#: ../wlx/gtksourceview/src/gtksourceview.c:601 +msgid "Wrap Line" +msgstr "Разрывы" + +#: ../wlx/sqlview_qt/src/plugin.cpp:25 +msgid "base not valid!" +msgstr "" + +#: ../wlx/sqlview_qt/src/plugin.cpp:143 +msgid "Failed to fetch list of tables. Maybe DB is locked?" +msgstr "" + +#: ../wlx/gtkimgview/src/gtkimgview.c:273 +msgid "Play Animation" +msgstr "Воспроизвести анимацию" + +#: ../wlx/gtkimgview/src/gtkimgview.c:278 +msgid "Stop Animation" +msgstr "Остановить анимацию" + +#: ../wlx/jsonview_gtk2/src/plugin.c:64 ../wlx/jsonview_qt/src/plugin.cpp:57 +#: ../wlx/jsonview_qt/src/plugin.cpp:160 +msgid "Object" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:70 ../wlx/jsonview_qt/src/plugin.cpp:64 +#: ../wlx/jsonview_qt/src/plugin.cpp:165 +msgid "Array" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:78 ../wlx/jsonview_qt/src/plugin.cpp:71 +msgid "String" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:85 ../wlx/jsonview_qt/src/plugin.cpp:83 +msgid "Integer" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:92 ../wlx/jsonview_qt/src/plugin.cpp:88 +msgid "Double" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:96 ../wlx/jsonview_qt/src/plugin.cpp:101 +msgid "True" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:96 ../wlx/jsonview_qt/src/plugin.cpp:103 +msgid "False" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:99 ../wlx/jsonview_qt/src/plugin.cpp:98 +msgid "Boolean" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:102 ../wlx/jsonview_qt/src/plugin.cpp:116 +msgid "Undefined" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:107 ../wlx/jsonview_qt/src/plugin.cpp:110 +msgid "Null" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:164 ../wlx/jsonview_gtk2/src/plugin.c:261 +#: ../wlx/jsonview_qt/src/plugin.cpp:156 +msgid "Root" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:183 ../wlx/jsonview_qt/src/plugin.cpp:183 +msgid "Node" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:195 ../wlx/jsonview_qt/src/plugin.cpp:183 +msgid "Value" +msgstr "" + +#: ../wlx/jsonview_gtk2/src/plugin.c:207 ../wlx/jsonview_qt/src/plugin.cpp:183 +msgid "Type" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:49 +msgid "Artist" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:51 +msgid "Album" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:52 +msgid "Track Number" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:53 +msgid "Album Artist" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:55 +msgid "Composer" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:56 +msgid "Lead Performer" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:57 +msgid "Publisher" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:58 +msgid "Genre" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:59 +msgid "Duration" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:60 +msgid "Description" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:61 +msgid "Copyright" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:62 +msgid "Comment" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:63 +msgid "Resolution" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:64 +msgid "Media Type" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:65 +msgid "Video Codec" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:66 +msgid "Video FrameRate" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:67 +msgid "Video BitRate" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:68 +msgid "Audio Codec" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:69 +msgid "Audio BitRate" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:70 +msgid "Date" +msgstr "" + +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:448 +#: ../wlx/qmediaplayer_qt/src/plugin.cpp:508 +#, c-format +msgid "%'d bps" +msgstr "" + +#: ../wlx/fontview_qt/src/plugin.cpp:16 +msgid "the quick brown fox jumps over the lazy dog" +msgstr "съешь ещё этих мягких французских булок да выпей чаю, belzebub" + +#: ../wlx/fontview_qt/src/plugin.cpp:36 +msgid "Bold" +msgstr "Жирный" + +#: ../wlx/fontview_qt/src/plugin.cpp:37 +msgid "Italic" +msgstr "Курсив" + +#: ../wdx/fewfiles/src/plugin.c:44 +#, c-format +msgid "Permission denied" +msgstr "Доступ запрещен" + +#: ../wdx/fewfiles/src/plugin.c:74 +#, c-format +msgid "Empty directory" +msgstr "Пустой каталог" + +#: ../wfx/trash_crap/src/plugin.c:120 +#, c-format +msgid "Restore %s from trash?" +msgstr "Восстановить %s из корзины?" + +#: ../wfx/trash_crap/src/plugin.c:135 +msgid "Already exists, overwrite?" +msgstr "Уже существует. Перезаписать?" + +#: ../wfx/trash_crap/src/plugin.c:153 ../wfx/trash_crap/src/plugin.c:185 +msgid "Failed to get information about the removed object." +msgstr "Не удалось получить информацию об удаленном объекте." + +#: ../wfx/trash_crap/src/plugin.c:168 +msgid "Original Path:" +msgstr "Оригинальный путь:" + +#: ../wfx/trash_crap/src/plugin.c:169 +msgid "Deletion Date:" +msgstr "Дата удаления:" + +#: ../wfx/trash_crap/src/plugin.c:177 ../wfx/trash_crap/src/plugin.c:178 +#: ../wfx/trash_crap/src/plugin.c:179 ../wfx/trash_crap/src/plugin.c:180 +#: ../wfx/trash_crap/src/plugin.c:181 ../wfx/trash_crap/src/plugin.c:182 +#: ../wfx/trash_crap/src/plugin.c:183 ../wfx/trash_crap/src/plugin.c:184 +msgid "Unknown" +msgstr "Неизвесто" + +#: ../wfx/trash_crap/src/plugin.c:560 +msgid "Path" +msgstr "Путь" + +#: ../wfx/trash_crap/src/plugin.c:568 +msgid "Trash (WFX)" +msgstr "Корзина (WFX)" + +#: ../dsx/git_untracked/src/plugin.c:57 ../dsx/lslocks/src/plugin.c:58 +#: ../dsx/git_ignored/src/plugin.c:57 ../dsx/git_modified/src/plugin.c:58 +msgid "failed to launch command" +msgstr "не удалось выполнить команду" + +#: ../dsx/git_untracked/src/plugin.c:60 +#: ../dsx/tracker_textsearch/src/plugin.c:84 +#: ../dsx/recollq_crap/src/plugin.c:108 ../dsx/locate_crap/src/plugin.c:134 +#: ../dsx/gtkrecent/src/plugin.c:56 ../dsx/lslocks/src/plugin.c:61 +#: ../dsx/tracker3_crap/src/plugin.c:104 ../dsx/git_ignored/src/plugin.c:60 +#: ../dsx/git_modified/src/plugin.c:61 +msgid "not found" +msgstr "не найдено" + +#: ../dsx/recollq_crap/src/plugin.c:79 ../dsx/tracker3_crap/src/plugin.c:78 +msgid "the search text was not specified" +msgstr "искомый текст не указан" diff --git a/wlx/jsonview/src/Makefile b/wlx/jsonview/src/Makefile new file mode 100644 index 0000000..ef43dcd --- /dev/null +++ b/wlx/jsonview/src/Makefile @@ -0,0 +1,31 @@ +CXX = g++ +CXXFLAGS = -shared -fPIC -Wl,--no-as-needed + +libs := +includes := -I../../../sdk + +qt5_libs := `pkg-config --cflags --libs Qt5Widgets` +qt6_libs := `pkg-config --cflags --libs Qt6Widgets Qt6Core Qt6Gui` + +plugdir := $(shell basename '$(realpath ..)') +plugtype := $(shell basename '$(realpath ../..)') +plugname := $(plugdir).$(plugtype) +plugdescr := `grep '$(plugtype)/$(plugdir))' ../../../../plugins.md -1 | tail -1 | sed 's/[[]//' | sed 's/[]][\(][^\)]\+.//' | sed 's/\s[\(].\+[\)]//'` +plugfiles := $(filter-out $(wildcard ../*.$(plugtype)), $(wildcard ../*)) + +detectstring := + + +all: qt6 + +qt6: + $(CXX) $(CXXFLAGS) -o '../$(plugdir)_$@.$(plugtype)' plugin.cpp $(libs) $($@_libs) $(includes) -D'PLUGNAME="'$(plugdir)_$@.$(plugtype)'"' -D'DETECT_STRING="$(detectstring)"' -D'PLUGTARGET="$@"' || echo '$(plugdir)_$@.$(plugtype)' >> ../../../dist/.build_fail.lst + +dist: + test -f '../$(plugdir)_qt6.$(plugtype)' && \ + echo -e "[plugininstall]\ndescription=$(plugdescr)\ntype=$(plugtype)\nfile=$(plugdir)_qt6.$(plugtype)\ndefaultdir=$(plugdir)" > ../pluginst.inf && \ + tar --exclude=../src -h -cvzf '../../../dist/$(plugtype)_$(plugdir)_qt6_$(shell date +%y.%m.%d).tar.gz' ../pluginst.inf '../$(plugdir)_qt6.$(plugtype)' $(plugfiles) && \ + rm ../pluginst.inf || echo $(plugdir)_qt6.$(plugtype) >> ../../../dist/.missing.log + +clean: + $(RM) $(wildcard ../*.$(plugtype)) diff --git a/wlx/jsonview/src/plugin.cpp b/wlx/jsonview/src/plugin.cpp new file mode 100644 index 0000000..98c9d82 --- /dev/null +++ b/wlx/jsonview/src/plugin.cpp @@ -0,0 +1,689 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#define _(STRING) gettext(STRING) +#define GETTEXT_PACKAGE "plugins" + +#include "wlxplugin.h" + +static int g_width = 200; +static bool g_resize = false; +static bool g_expand = true; +static bool g_sorting = false; +static bool g_filename = true; + +class ValueDelegate : public QStyledItemDelegate { +public: + ValueDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { + if (index.column() != 1) return nullptr; // Only allow editing column 1 + return QStyledItemDelegate::createEditor(parent, option, index); + } +}; + +class JsonViewerWidget : public QWidget +{ +public: + explicit JsonViewerWidget(QWidget *parent = nullptr); + ~JsonViewerWidget(); + + bool loadFile(const QString& filePath); + void saveFile(const QString& filePath); + + QTreeWidget* view() const { return m_view; } + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void onSave(); + void onSaveAs(); + void onReload(); + void showContextMenu(const QPoint &pos); + + void installFocusGuard(); + bool isInputWidget(QWidget *w) const; + void restoreFocusToDC(); + + void walk_array(const QJsonArray& array, QTreeWidgetItem *item); + void walk_object(const QJsonObject& object, QTreeWidgetItem *item); + void check_value(const QJsonValue& value, QTreeWidgetItem *item); + QJsonValue treeToJson(QTreeWidgetItem *item); + + QString getJsonPath(QTreeWidgetItem *item); + QString getSubtreeText(QTreeWidgetItem *item); + QString getValueText(QTreeWidgetItem *item); + + QTreeWidget *m_view; + QToolBar *m_toolbar; + QString m_currentFile; + + QPointer m_savedFocusWidget; + QPointer m_activeInput; +}; + +JsonViewerWidget::JsonViewerWidget(QWidget *parent) + : QWidget(parent), m_savedFocusWidget(nullptr), m_activeInput(nullptr) +{ + setFocusPolicy(Qt::NoFocus); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + m_toolbar = new QToolBar(this); + m_toolbar->setFocusPolicy(Qt::NoFocus); + + QAction *actSave = m_toolbar->addAction("Save"); + actSave->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S)); + + QAction *actSaveAs = m_toolbar->addAction("Save As..."); + QAction *actReload = m_toolbar->addAction("Reload"); + + addAction(actSave); + + layout->addWidget(m_toolbar); + + m_view = new QTreeWidget(this); + m_view->setFocusPolicy(Qt::NoFocus); + m_view->setItemDelegate(new ValueDelegate(this)); + m_view->setContextMenuPolicy(Qt::CustomContextMenu); + layout->addWidget(m_view); + + QObject::connect(actSave, &QAction::triggered, this, [this]() { onSave(); }); + QObject::connect(actSaveAs, &QAction::triggered, this, [this]() { onSaveAs(); }); + QObject::connect(actReload, &QAction::triggered, this, [this]() { onReload(); }); + QObject::connect(m_view, &QWidget::customContextMenuRequested, this, [this](const QPoint &pos) { showContextMenu(pos); }); + + installFocusGuard(); +} + +JsonViewerWidget::~JsonViewerWidget() +{ + if (qApp) qApp->removeEventFilter(this); +} + +void JsonViewerWidget::installFocusGuard() +{ + if (qApp) qApp->installEventFilter(this); + const auto children = findChildren(); + for (QWidget *child : children) { + if (!isInputWidget(child)) + child->setFocusPolicy(Qt::NoFocus); + } +} + +bool JsonViewerWidget::isInputWidget(QWidget *w) const +{ + if (!w) return false; + if (w != m_view && m_view->isAncestorOf(w)) return true; + return false; +} + +void JsonViewerWidget::restoreFocusToDC() +{ + if (QWidget *fw = QApplication::focusWidget()) { + if (fw == this || fw->isAncestorOf(this) || this->isAncestorOf(fw)) + fw->clearFocus(); + } + if (m_savedFocusWidget) { + m_savedFocusWidget->setFocus(Qt::OtherFocusReason); + } +} + +bool JsonViewerWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress) { + QWidget *w = qobject_cast(obj); + if (w) { + if (w != this && !this->isAncestorOf(w)) { + m_activeInput = nullptr; + restoreFocusToDC(); + } + } + } + + QWidget *w = qobject_cast(obj); + if (w && (w == this || this->isAncestorOf(w))) { + if (event->type() == QEvent::FocusIn) { + if (isInputWidget(w)) { + m_activeInput = w; + return false; + } + QTimer::singleShot(0, this, [this]() { restoreFocusToDC(); }); + return false; + } + + if (event->type() == QEvent::ChildAdded) { + auto *ce = static_cast(event); + if (auto *childWidget = qobject_cast(ce->child())) { + if (!isInputWidget(childWidget)) + childWidget->setFocusPolicy(Qt::NoFocus); + } + } + + if (event->type() == QEvent::KeyPress) { + auto *ke = static_cast(event); + if ((ke->modifiers() & Qt::ControlModifier) && ke->key() == Qt::Key_S) { + onSave(); + return true; + } + if (ke->key() == Qt::Key_Escape && m_activeInput) { + m_activeInput = nullptr; + restoreFocusToDC(); + return true; + } + if ((ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) && isInputWidget(w)) { + QTimer::singleShot(0, this, [this]() { + m_activeInput = nullptr; + restoreFocusToDC(); + }); + } + } + } + + return QWidget::eventFilter(obj, event); +} + +void JsonViewerWidget::walk_array(const QJsonArray& array, QTreeWidgetItem *item) +{ + for (int i = 0; i < array.count(); i++) + { + QTreeWidgetItem *newitem = new QTreeWidgetItem(item); + newitem->setText(0, QString("[%1]").arg(i)); + newitem->setFlags(newitem->flags() | Qt::ItemIsEditable); + check_value(array.at(i), newitem); + } +} + +void JsonViewerWidget::walk_object(const QJsonObject& object, QTreeWidgetItem *item) +{ + QJsonObject::const_iterator iter; + for (iter = object.constBegin(); iter != object.constEnd(); ++iter) + { + QTreeWidgetItem *newitem = new QTreeWidgetItem(item); + newitem->setText(0, iter.key()); + newitem->setToolTip(0, iter.key()); + newitem->setFlags(newitem->flags() | Qt::ItemIsEditable); + check_value(iter.value(), newitem); + } +} + +void JsonViewerWidget::check_value(const QJsonValue& value, QTreeWidgetItem *item) +{ + double d; + switch (value.type()) + { + case QJsonValue::Object: + item->setText(2, _("Object")); + walk_object(value.toObject(), item); + break; + case QJsonValue::Array: + item->setText(2, _("Array")); + walk_array(value.toArray(), item); + break; + case QJsonValue::String: + item->setText(2, _("String")); + item->setText(1, value.toString()); + item->setToolTip(1, value.toString()); + break; + case QJsonValue::Double: + d = value.toDouble(); + if (trunc(d) == d) + { + item->setText(2, _("Integer")); + item->setText(1, QString::number(d, 'f', 0)); + } + else + { + item->setText(2, _("Double")); + item->setText(1, QString::number(d)); + } + item->setToolTip(1, QString::number(d, 'f', 1)); + break; + case QJsonValue::Bool: + item->setText(2, _("Boolean")); + if (value.toBool()) + item->setText(1, _("True")); + else + item->setText(1, _("False")); + break; + case QJsonValue::Null: + item->setText(2, _("Null")); + break; + default: + item->setText(2, _("Undefined")); + break; + } +} + +QJsonValue JsonViewerWidget::treeToJson(QTreeWidgetItem *item) +{ + QString type = item->text(2); + if (type == _("Object")) { + QJsonObject obj; + for (int i = 0; i < item->childCount(); ++i) { + QTreeWidgetItem *child = item->child(i); + obj.insert(child->text(0), treeToJson(child)); + } + return obj; + } else if (type == _("Array")) { + QJsonArray arr; + for (int i = 0; i < item->childCount(); ++i) { + QTreeWidgetItem *child = item->child(i); + arr.append(treeToJson(child)); + } + return arr; + } else if (type == _("String")) { + return QJsonValue(item->text(1)); + } else if (type == _("Integer") || type == _("Double")) { + bool ok; + double val = item->text(1).toDouble(&ok); + if (ok) return QJsonValue(val); + return QJsonValue(item->text(1)); // Fallback to string + } else if (type == _("Boolean")) { + QString val = item->text(1).toLower(); + return QJsonValue(val == "true" || val == "1"); + } else if (type == _("Null")) { + return QJsonValue(QJsonValue::Null); + } + return QJsonValue(); +} + +bool JsonViewerWidget::loadFile(const QString& filePath) +{ + QWidget *fw = QApplication::focusWidget(); + if (fw && fw != this && !this->isAncestorOf(fw)) { + m_savedFocusWidget = fw; + } + m_currentFile = filePath; + m_activeInput = nullptr; + + QMimeDatabase db; + QMimeType type = db.mimeTypeForFile(filePath); + + if (type.name() != "text/plain" && type.name() != "application/json") + return false; + + QFile file(filePath); + if (!file.open(QFile::ReadOnly | QFile::Text)) + return false; + + QJsonParseError err; + QJsonDocument json = QJsonDocument().fromJson(file.readAll(), &err); + file.close(); + + if (json.isNull() || json.isEmpty()) { + if (err.error != QJsonParseError::NoError) + QMessageBox::critical(this, PLUGNAME, err.errorString()); + return false; + } + + m_view->clear(); + m_view->setColumnCount(3); + + QFileInfo fi(filePath); + QTreeWidgetItem *root = new QTreeWidgetItem(m_view); + + if (g_filename) + root->setText(0, fi.fileName()); + else + root->setText(0, _("Root")); + + root->setFlags(root->flags() | Qt::ItemIsEditable); + + if (json.isObject()) { + root->setText(2, _("Object")); + walk_object(json.object(), root); + } else if (json.isArray()) { + root->setText(2, _("Array")); + walk_array(json.array(), root); + } + + m_view->insertTopLevelItem(0, root); + + if (g_expand) + m_view->expandAll(); + + for (int i = 0; i < 3; i++) { + if (g_resize) + m_view->resizeColumnToContents(i); + else + m_view->setColumnWidth(i, g_width); + } + + QStringList headers; + headers << _("Node") << _("Value") << _("Type"); + m_view->setHeaderLabels(headers); + + m_view->setSelectionMode(QAbstractItemView::SingleSelection); + m_view->setSelectionBehavior(QAbstractItemView::SelectItems); + + if (g_sorting) + m_view->setSortingEnabled(true); + + QTimer::singleShot(0, this, [this]() { restoreFocusToDC(); }); + return true; +} + +void JsonViewerWidget::onSave() +{ + if (QWidget *fw = QApplication::focusWidget()) { + if (m_view->isAncestorOf(fw)) { + fw->clearFocus(); + } + } + saveFile(m_currentFile); + + m_activeInput = nullptr; + restoreFocusToDC(); +} + +void JsonViewerWidget::onSaveAs() +{ + QString path = QFileDialog::getSaveFileName(this, "Save JSON As", m_currentFile); + if (!path.isEmpty()) { + saveFile(path); + } +} + +void JsonViewerWidget::onReload() +{ + if (QWidget *fw = QApplication::focusWidget()) { + if (m_view->isAncestorOf(fw)) { + fw->clearFocus(); + } + } + loadFile(m_currentFile); +} + +void JsonViewerWidget::saveFile(const QString& filePath) +{ + QTreeWidgetItem *root = m_view->topLevelItem(0); + if (!root) return; + + QJsonDocument doc; + QJsonValue rootVal = treeToJson(root); + if (rootVal.isObject()) doc.setObject(rootVal.toObject()); + else if (rootVal.isArray()) doc.setArray(rootVal.toArray()); + + QFile file(filePath); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, "Error", "Could not open file for writing."); + return; + } + + file.write(doc.toJson(QJsonDocument::Indented)); + file.close(); + m_currentFile = filePath; +} + +QString JsonViewerWidget::getJsonPath(QTreeWidgetItem *item) +{ + QString path; + while (item) { + QString key = item->text(0); + if (item->parent()) { + if (item->parent()->text(2) == _("Array")) { + path = key + path; + } else { + path = "." + key + path; + } + } else { + path = key + path; + } + item = item->parent(); + } + return path; +} + +QString JsonViewerWidget::getSubtreeText(QTreeWidgetItem *item) +{ + QJsonValue val = treeToJson(item); + if (item->parent() && item->parent()->text(2) == _("Object")) { + QJsonObject wrapper; + wrapper.insert(item->text(0), val); + QJsonDocument doc(wrapper); + QString jsonStr = QString::fromUtf8(doc.toJson(QJsonDocument::Indented)).trimmed(); + + if (jsonStr.startsWith("{") && jsonStr.endsWith("}")) { + int firstNewline = jsonStr.indexOf('\n'); + int lastNewline = jsonStr.lastIndexOf('\n'); + if (firstNewline != -1 && lastNewline != -1 && firstNewline < lastNewline) { + jsonStr = jsonStr.mid(firstNewline + 1, lastNewline - firstNewline - 1); + } else { + jsonStr = jsonStr.mid(1, jsonStr.length() - 2); + } + } + + QStringList lines = jsonStr.split('\n'); + for (int i = 0; i < lines.size(); ++i) { + if (lines[i].startsWith(" ")) { + lines[i] = lines[i].mid(4); + } + } + return lines.join('\n').trimmed(); + } else if (item->parent() && item->parent()->text(2) == _("Array")) { + QJsonArray wrapper; + wrapper.append(val); + QJsonDocument doc(wrapper); + return QString::fromUtf8(doc.toJson(QJsonDocument::Indented)).trimmed(); + } else { + if (val.isObject()) { + return QString::fromUtf8(QJsonDocument(val.toObject()).toJson(QJsonDocument::Indented)).trimmed(); + } else if (val.isArray()) { + return QString::fromUtf8(QJsonDocument(val.toArray()).toJson(QJsonDocument::Indented)).trimmed(); + } else { + return item->text(1); + } + } +} + +QString JsonViewerWidget::getValueText(QTreeWidgetItem *item) +{ + QJsonValue val = treeToJson(item); + if (val.isObject()) { + return QString::fromUtf8(QJsonDocument(val.toObject()).toJson(QJsonDocument::Indented)).trimmed(); + } else if (val.isArray()) { + return QString::fromUtf8(QJsonDocument(val.toArray()).toJson(QJsonDocument::Indented)).trimmed(); + } else { + return item->text(1); + } +} + +void JsonViewerWidget::showContextMenu(const QPoint &pos) +{ + QTreeWidgetItem *item = m_view->itemAt(pos); + if (!item) return; + + QMenu menu(this); + QAction *actCopyPath = menu.addAction("Copy JSONPath"); + + bool isComplex = (item->text(2) == _("Object") || item->text(2) == _("Array")); + QAction *actCopySubtree = nullptr; + QAction *actCopyKeyValue = nullptr; + + if (isComplex) { + actCopySubtree = menu.addAction("Copy Subtree"); + } else { + actCopyKeyValue = menu.addAction("Copy Key:Value"); + } + + QAction *actCopyValue = menu.addAction("Copy Value"); + + QAction *res = menu.exec(m_view->viewport()->mapToGlobal(pos)); + if (res == actCopyPath) { + QApplication::clipboard()->setText(getJsonPath(item)); + } else if (actCopySubtree && res == actCopySubtree) { + QApplication::clipboard()->setText(getSubtreeText(item)); + } else if (actCopyKeyValue && res == actCopyKeyValue) { + QApplication::clipboard()->setText(getSubtreeText(item)); + } else if (res == actCopyValue) { + QApplication::clipboard()->setText(getValueText(item)); + } +} + +HANDLE DCPCALL ListLoad(HANDLE ParentWin, char* FileToLoad, int ShowFlags) +{ + if (!QApplication::instance()) + return nullptr; + + JsonViewerWidget *widget = new JsonViewerWidget((QWidget*)ParentWin); + if (!widget->loadFile(FileToLoad)) { + delete widget; + return nullptr; + } + + widget->show(); + return widget; +} + +void DCPCALL ListCloseWindow(HANDLE ListWin) +{ + JsonViewerWidget *widget = (JsonViewerWidget*)ListWin; + delete widget; +} + +int DCPCALL ListSendCommand(HWND ListWin, int Command, int Parameter) +{ + JsonViewerWidget *widget = (JsonViewerWidget*)ListWin; + QTreeWidget *view = widget->view(); + + if (Command == lc_copy) + { + QTreeWidgetItem *item = view->currentItem(); + if (item) { + QString text(item->text(view->currentColumn())); + if (!text.isEmpty()) + QApplication::clipboard()->setText(text); + } + return LISTPLUGIN_OK; + } + + return LISTPLUGIN_ERROR; +} + +int DCPCALL ListSearchText(HWND ListWin, char* SearchString, int SearchParameter) +{ + JsonViewerWidget *widget = (JsonViewerWidget*)ListWin; + QTreeWidget *view = widget->view(); + QList list; + Qt::MatchFlags sflags = Qt::MatchContains | Qt::MatchRecursive; + + if (SearchParameter & lcs_matchcase) + sflags |= Qt::MatchCaseSensitive; + + QString needle(SearchString); + QString prev = view->property("needle").value(); + view->setProperty("needle", needle); + + list = view->findItems(QString(SearchString), sflags, view->currentColumn()); + + if (!list.isEmpty()) + { + int i = view->property("findit").value(); + if (needle != prev || SearchParameter & lcs_findfirst) + { + if (SearchParameter & lcs_backwards) + i = list.size() - 1; + else + i = 0; + } + else if (SearchParameter & lcs_backwards) + i--; + else + i++; + + if (i >= 0 && i < list.size() && list.at(i)) + { + view->scrollToItem(list.at(i)); + view->setCurrentItem(list.at(i), view->currentColumn()); + view->setProperty("findit", i); + return LISTPLUGIN_OK; + } + } + + QMessageBox::information(widget, "", QString::asprintf(_("\"%s\" not found!"), SearchString)); + return LISTPLUGIN_ERROR; +} + +void DCPCALL ListGetDetectString(char* DetectString, int maxlen) +{ + snprintf(DetectString, maxlen - 1, "SIZE<30000000"); +} + +void DCPCALL ListSetDefaultParams(ListDefaultParamStruct* dps) +{ + QFileInfo defini(QString::fromStdString(dps->DefaultIniName)); + QString cfgpath = defini.absolutePath() + "/j2969719.ini"; + QSettings settings(cfgpath, QSettings::IniFormat); + + if (!settings.contains(PLUGNAME "/resize_columns")) + settings.setValue(PLUGNAME "/resize_columns", g_resize); + else + g_resize = settings.value(PLUGNAME "/resize_columns").toBool(); + + if (!settings.contains(PLUGNAME "/tree_expand")) + settings.setValue(PLUGNAME "/tree_expand", g_expand); + else + g_expand = settings.value(PLUGNAME "/tree_expand").toBool(); + + if (!settings.contains(PLUGNAME "/column_width")) + settings.setValue(PLUGNAME "/column_width", g_width); + else + { + g_width = settings.value(PLUGNAME "/column_width").toInt(); + + if (g_width < 10) + { + g_width = 10; + settings.setValue(PLUGNAME "/column_width", 10); + } + } + + if (!settings.contains(PLUGNAME "/sorting")) + settings.setValue(PLUGNAME "/sorting", g_sorting); + else + g_sorting = settings.value(PLUGNAME "/sorting").toBool(); + + if (!settings.contains(PLUGNAME "/show_filename")) + settings.setValue(PLUGNAME "/show_filename", g_filename); + else + g_filename = settings.value(PLUGNAME "/show_filename").toBool(); + + Dl_info dlinfo; + static char plg_path[PATH_MAX]; + const char* loc_dir = "langs"; + + memset(&dlinfo, 0, sizeof(dlinfo)); + + if (dladdr(plg_path, &dlinfo) != 0) + { + strncpy(plg_path, dlinfo.dli_fname, PATH_MAX); + char *pos = strrchr(plg_path, '/'); + + if (pos) + strcpy(pos + 1, loc_dir); + + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, plg_path); + textdomain(GETTEXT_PACKAGE); + } +}