explorer: support exploring system's files

This commit is contained in:
Le Tan 2018-05-23 20:09:41 +08:00
parent a98757643f
commit 2ff9c607dd
38 changed files with 1431 additions and 208 deletions

View File

@ -922,7 +922,7 @@ VMarkdownTab::VMarkdownTab(QWidget *p_parent)
{
// Default note open mode.
m_openModeCombo = VUtils::getComboBox();
m_openModeCombo->setToolTip(tr("Default mode to open an internal note"));
m_openModeCombo->setToolTip(tr("Default mode to open a file"));
m_openModeCombo->addItem(tr("Read Mode"), (int)OpenFileMode::Read);
m_openModeCombo->addItem(tr("Edit Mode"), (int)OpenFileMode::Edit);
@ -992,7 +992,7 @@ VMarkdownTab::VMarkdownTab(QWidget *p_parent)
m_graphvizDotEdit->setToolTip(tr("Location to the GraphViz dot executable"));
QFormLayout *mainLayout = new QFormLayout();
mainLayout->addRow(tr("Note open mode:"), m_openModeCombo);
mainLayout->addRow(tr("Open mode:"), m_openModeCombo);
mainLayout->addRow(tr("Heading sequence:"), headingSequenceLayout);
mainLayout->addRow(colorColumnLabel, m_colorColumnEdit);
mainLayout->addRow(tr("MathJax configuration:"), m_mathjaxConfigEdit);

View File

@ -1,10 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#000000" d="M399.3,168.9c-0.7-2.9-2-5-3.5-6.8l-83.7-91.7c-1.9-2.1-4.1-3.1-6.6-4.4c-2.9-1.5-6.1-1.6-9.4-1.6H136.2
c-12.4,0-23.7,9.6-23.7,22.9v335.2c0,13.4,11.3,25.9,23.7,25.9h243.1c12.4,0,21.2-12.5,21.2-25.9V178.4
C400.5,174.8,400.1,172.2,399.3,168.9z M305.5,111l58,63.5h-58V111z M144.5,416.5v-320h129v81.7c0,14.8,13.4,28.3,28.1,28.3h66.9
v210H144.5z"/>
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path fill="#000000" d="M269.534,399.201c-9.616,0-17.483,7.867-17.483,17.483s7.867,17.483,17.483,17.483h160.263
c9.616,0,17.483-7.867,17.483-17.483V138.409c0-4.954-2.04-9.616-5.536-12.821L310.62,4.662C307.414,1.748,303.043,0,298.673,0
h-195.23c-9.616,0-17.483,7.867-17.483,17.483v189.402c0,9.616,7.867,17.483,17.483,17.483s17.483-7.867,17.483-17.483V34.967
h139.866v151.522c0,9.616,7.867,17.483,17.483,17.483H409.4c0.874,0,2.04,0,2.914-0.291v195.521H269.534z M409.4,169.005H295.759
V38.463l116.555,107.522v23.311C411.44,169.005,410.274,169.005,409.4,169.005z M16.026,372.977
c0-9.616,7.867-17.483,17.483-17.483h52.45V300.13c0-9.616,7.867-17.483,17.483-17.483s17.483,7.867,17.483,17.483v55.364h58.278
c9.616,0,17.483,7.867,17.483,17.483s-7.867,17.483-17.483,17.483h-58.278v55.364c0,9.616-7.867,17.483-17.483,17.483
s-17.483-7.867-17.483-17.483V390.46H33.51C23.894,390.46,16.026,382.592,16.026,372.977z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 868 B

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,10 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path style="fill:#000000" d="M437.334,144H256.006l-42.668-48H74.666C51.197,96,32,115.198,32,138.667v234.666C32,396.802,51.197,416,74.666,416h362.668
C460.803,416,480,396.802,480,373.333V186.667C480,163.198,460.803,144,437.334,144z M448,373.333
c0,5.782-4.885,10.667-10.666,10.667H74.666C68.884,384,64,379.115,64,373.333V176h373.334c5.781,0,10.666,4.885,10.666,10.667
V373.333z"/>
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path fill="#000000" d="M444.955,31.782H333.208c-17.576,0-37.758,8.931-47.165,31.782c0,0-4.132,29.145-31.782,31.782
H31.782C14.239,95.347,0,109.077,0,126.621v286.551c0,17.544,14.239,31.783,31.782,31.783h413.172
c17.544,0,31.782-14.239,31.782-31.783V63.565C476.737,46.021,462.499,31.782,444.955,31.782z M444.955,126.621v286.551H31.782
V127.13H254.26l3.051-0.159c29.812-2.829,48.246-23.71,56.732-45.163c8.263-20.722,22.661-18.243,22.661-18.243h108.251V126.621
z"/>
<path fill="#000000" d="M317.825,254.26H254.26v-63.565c0-8.772-7.151-15.891-15.891-15.891
c-8.772,0-15.891,7.119-15.891,15.891v63.565h-63.565c-8.772,0-15.891,7.151-15.891,15.891s7.119,15.891,15.891,15.891h63.565
v63.565c0,8.74,7.119,15.891,15.891,15.891c8.74,0,15.891-7.151,15.891-15.891v-63.565h63.565
c8.74,0,15.891-7.151,15.891-15.891S326.565,254.26,317.825,254.26z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 871 B

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<path d="M177.3,76.95v111c0,6.8,5.5,12.3,12.3,12.3h43.2v33.4h-165c-6.8,0-12.3,5.5-12.3,12.3v43.9H12.3
c-6.8,0-12.3,5.5-12.3,12.3v111c0,6.8,5.5,12.3,12.3,12.3h111c6.8,0,12.3-5.5,12.3-12.3v-111c0-6.8-5.5-12.3-12.3-12.3H80v-31.7
h152.7v31.7h-43.2c-6.8,0-12.3,5.5-12.3,12.3v111c0,6.8,5.5,12.3,12.3,12.3h111c6.8,0,12.3-5.5,12.3-12.3v-111
c0-6.8-5.5-12.3-12.3-12.3h-43.2v-31.7H410v31.7h-43.2c-6.8,0-12.3,5.5-12.3,12.3v111c0,6.8,5.5,12.3,12.3,12.3h111
c6.8,0,12.3-5.5,12.3-12.3v-111c0-6.8-5.5-12.3-12.3-12.3h-43.2v-43.9c0-6.8-5.5-12.3-12.3-12.3h-165v-33.4h43.2
c6.8,0,12.3-5.5,12.3-12.3v-111c0-6.8-5.5-12.3-12.3-12.3h-111C182.8,64.65,177.3,70.15,177.3,76.95z M111,400.75H24.6v-86.5H111
V400.75z M288.2,400.75h-86.5v-86.5h86.5V400.75z M465.4,400.75H379v-86.5h86.5v86.5H465.4z M201.8,89.15h86.5v86.5h-86.5
L201.8,89.15L201.8,89.15z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="folder" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 60 60" style="enable-background:new 0 0 60 60;" xml:space="preserve">
<path fill="#000000" d="M57.49,21.5H55v-7h-2v-4H25.086l-3.571-5H2.732C1.226,5.5,0,6.726,0,8.232v43.687l0.006,0
c-0.005,0.563,0.17,1.114,0.522,1.575C1.018,54.134,1.76,54.5,2.565,54.5h44.759c1.156,0,2.174-0.779,2.45-1.813L60,24.649v-0.177
C60,22.75,58.944,21.5,57.49,21.5z M53,21.5h-8v-1c0-0.553-0.448-1-1-1s-1,0.447-1,1v1h-5v-1c0-0.553-0.448-1-1-1s-1,0.447-1,1v1h-5
v-1c0-0.553-0.448-1-1-1s-1,0.447-1,1v1h-5v-1c0-0.553-0.448-1-1-1s-1,0.447-1,1v1h-5v-1c0-0.553-0.448-1-1-1s-1,0.447-1,1v1h-2.269
c-0.143,0-0.284,0.012-0.422,0.035c-0.499,0.083-0.95,0.315-1.309,0.639V16.5h42V21.5z M5,37.793V22.5h5.695
c-0.188,0.243-0.335,0.516-0.414,0.813l-0.317,0.87L5,37.793z M9,20.5H6.414L7,19.914l2-2V20.5z M51,12.5v2H9.586L7,17.086V12.5
h19.515H51z M2,8.232C2,7.828,2.329,7.5,2.732,7.5h17.753l2.143,3H5v8.586l-2,2v22.085l-1,2.728V8.232z M47.869,52.083
c-0.066,0.245-0.291,0.417-0.545,0.417H2.565c-0.243,0-0.385-0.139-0.448-0.222c-0.063-0.082-0.16-0.256-0.123-0.408L3,49.112v0.001
l9.16-25.114l0.026-0.082c0.066-0.245,0.291-0.417,0.545-0.417H55h2.49c0.38,0,0.477,0.546,0.502,0.819L47.869,52.083z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path fill="#000000" d="M468.7,64H43.3c-6,0-11.3,5-11.3,11.1v265.7c0,6.2,5.2,11.1,11.3,11.1h425.4c6,0,11.3-5,11.3-11.1V75.1
C480,69,474.8,64,468.7,64z M448,320H64V96h384V320z"/>
<path fill="#000000" d="M302.5,448c28-0.5,41.5-3.9,29-12.5c-12.5-8.7-28.5-15.3-29-22.5c-0.3-3.7-1.7-45-1.7-45H256h-44.8c0,0-1.5,41.3-1.7,45
c-0.5,7.1-16.5,13.8-29,22.5c-12.5,8.7,1,12,29,12.5H302.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 878 B

View File

@ -2,9 +2,9 @@
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path style="fill:#000000" d="M458,210.409l-145.267-12.476L256,64l-56.743,133.934L54,210.409l110.192,95.524L131.161,448L256,372.686L380.83,448
l-33.021-142.066L458,210.409z M272.531,345.286L256,335.312l-16.53,9.973l-59.988,36.191l15.879-68.296l4.369-18.79l-14.577-12.637
l-52.994-45.939l69.836-5.998l19.206-1.65l7.521-17.75l27.276-64.381l27.27,64.379l7.52,17.751l19.208,1.65l69.846,5.998
l-52.993,45.939l-14.576,12.636l4.367,18.788l15.875,68.299L272.531,345.286z"/>
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#000000" d="M443.6,387.1L312.4,255.4l131.5-130c5.4-5.4,5.4-14.2,0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4
L256,197.8L124.9,68.3c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4L68,105.9c-5.4,5.4-5.4,14.2,0,19.6l131.5,130L68.4,387.1
c-2.6,2.6-4.1,6.1-4.1,9.8c0,3.7,1.4,7.2,4.1,9.8l37.4,37.6c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1L256,313.1l130.7,131.1
c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1l37.4-37.6c2.6-2.6,4.1-6.1,4.1-9.8C447.7,393.2,446.2,389.7,443.6,387.1z"/>
</svg>

Before

Width:  |  Height:  |  Size: 956 B

After

Width:  |  Height:  |  Size: 1005 B

View File

@ -3,10 +3,5 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path style="fill:#000000" d="M468.7,64H43.3c-6,0-11.3,5-11.3,11.1v265.7c0,6.2,5.2,11.1,11.3,11.1h425.4c6,0,11.3-5,11.3-11.1V75.1
C480,69,474.8,64,468.7,64z M448,320H64V96h384V320z"/>
<path style="fill:#000000" d="M302.5,448c28-0.5,41.5-3.9,29-12.5c-12.5-8.7-28.5-15.3-29-22.5c-0.3-3.7-1.7-45-1.7-45H256h-44.8c0,0-1.5,41.3-1.7,45
c-0.5,7.1-16.5,13.8-29,22.5c-12.5,8.7,1,12,29,12.5H302.5z"/>
</g>
<path fill="#000000" d="M496,384V96H16v288h175v16h-64v16h257v-16h-64v-16H496z M32,112h448v256H32V112z"/>
</svg>

Before

Width:  |  Height:  |  Size: 890 B

After

Width:  |  Height:  |  Size: 589 B

View File

@ -1,9 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path style="fill:#000000" d="M458,210.409l-145.267-12.476L256,64l-56.743,133.934L54,210.409l110.192,95.524L131.161,448L256,372.686L380.83,448
l-33.021-142.066L458,210.409z M272.531,345.287L256,335.313l-0.002-189.277l27.27,64.379l7.52,17.751l19.208,1.65l69.846,5.998
l-52.993,45.939l-14.576,12.636l4.367,18.788l15.875,68.299L272.531,345.287z"/>
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;"
xml:space="preserve">
<g>
<path fill="#000000" d="M431.379,0.222h-394.7C16.456,0.222,0,16.671,0,36.895v394.268c0,20.221,16.456,36.677,36.679,36.677h394.7
c20.228,0,36.683-16.456,36.683-36.677V36.895C468.062,16.665,451.606,0.222,431.379,0.222z M406.519,41.966
c8.689,0,15.723,7.04,15.723,15.72c0,8.683-7.033,15.717-15.723,15.717c-8.688,0-15.723-7.04-15.723-15.717
C390.796,49.006,397.83,41.966,406.519,41.966z M350.189,41.966c8.688,0,15.723,7.04,15.723,15.72
c0,8.683-7.034,15.717-15.723,15.717c-8.684,0-15.711-7.04-15.711-15.717C334.479,49.006,341.506,41.966,350.189,41.966z
M41.913,112.426h184.055v313.495H41.913V112.426z M426.148,425.921H242.104V112.426h184.044V425.921z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 834 B

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path style="fill:#000000" d="M458,210.409l-145.267-12.476L256,64l-56.743,133.934L54,210.409l110.192,95.524L131.161,448L256,372.686L380.83,448
l-33.021-142.066L458,210.409z M272.531,345.286L256,335.312l-16.53,9.973l-59.988,36.191l15.879-68.296l4.369-18.79l-14.577-12.637
l-52.994-45.939l69.836-5.998l19.206-1.65l7.521-17.75l27.276-64.381l27.27,64.379l7.52,17.751l19.208,1.65l69.846,5.998
l-52.993,45.939l-14.576,12.636l4.367,18.788l15.875,68.299L272.531,345.286z"/>
</svg>

After

Width:  |  Height:  |  Size: 946 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g>
<polygon fill="#000000" points="256,372.686 380.83,448 347.809,305.934 458,210.409 312.733,197.934 256,64 199.257,197.934 54,210.409
164.192,305.934 131.161,448 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 656 B

View File

@ -7,7 +7,7 @@ mdhl_file=v_moonlight.mdhl
css_file=v_moonlight.css
codeblock_css_file=v_moonlight_codeblock.css
mermaid_css_file=v_moonlight_mermaid.css
version=10
version=11
; This mapping will be used to translate colors when the content of HTML is copied
; without background. You could just specify the foreground colors mapping here.

View File

@ -630,6 +630,24 @@ QLineEdit[VimCommandLine="true"]:hover {
background: @lineedit_hover_bg;
}
QLineEdit[EmbeddedEdit="true"] {
padding: 0px;
margin: 0px;
border: none;
color: @lineedit_fg;
background: transparent;
}
QLineEdit[EmbeddedEdit="true"]:focus {
background: @lineedit_focus_bg;
border: none;
}
QLineEdit[EmbeddedEdit="true"]:hover {
background: @lineedit_hover_bg;
border: none;
}
QLineEdit {
border: 1px solid @lineedit_border;
padding: 3px;

View File

@ -7,7 +7,7 @@ mdhl_file=v_native.mdhl
css_file=v_native.css
codeblock_css_file=v_native_codeblock.css
mermaid_css_file=v_native_mermaid.css
version=10
version=11
[phony]
; Abstract color attributes.

View File

@ -452,6 +452,14 @@ QLineEdit[VimCommandLine="true"] {
background: @lineedit_bg;
}
QLineEdit[EmbeddedEdit="true"] {
padding: 0px;
margin: 0px;
border: none;
color: @lineedit_fg;
background: transparent;
}
VUniversalEntry VMetaWordLineEdit {
border: 1px solid @lineedit_border;
}

View File

@ -7,7 +7,7 @@ mdhl_file=v_pure.mdhl
css_file=v_pure.css
codeblock_css_file=v_pure_codeblock.css
mermaid_css_file=v_pure_mermaid.css
version=10
version=11
[phony]
; Abstract color attributes.

View File

@ -630,6 +630,24 @@ QLineEdit[VimCommandLine="true"]:hover {
border: none;
}
QLineEdit[EmbeddedEdit="true"] {
padding: 0px;
margin: 0px;
border: none;
color: @lineedit_fg;
background: transparent;
}
QLineEdit[EmbeddedEdit="true"]:focus {
background: @lineedit_focus_bg;
border: none;
}
QLineEdit[EmbeddedEdit="true"]:hover {
background: @lineedit_hover_bg;
border: none;
}
QLineEdit {
border: 1px solid @lineedit_border;
padding: 3px;

View File

@ -132,7 +132,8 @@ SOURCES += main.cpp\
vmathjaxpreviewhelper.cpp \
vmathjaxwebdocument.cpp \
vmathjaxinplacepreviewhelper.cpp \
vhistorylist.cpp
vhistorylist.cpp \
vexplorer.cpp
HEADERS += vmainwindow.h \
vdirectorytree.h \
@ -257,7 +258,9 @@ HEADERS += vmainwindow.h \
vmathjaxinplacepreviewhelper.h \
markdownitoption.h \
vhistorylist.h \
vhistoryentry.h
vhistoryentry.h \
vexplorer.h \
vexplorerentry.h
RESOURCES += \
vnote.qrc \

View File

@ -27,6 +27,7 @@
#include <QAction>
#include <QTreeWidgetItem>
#include <QFormLayout>
#include <QInputDialog>
#include "vorphanfile.h"
#include "vnote.h"
@ -1542,3 +1543,40 @@ bool VUtils::inSameDrive(const QString &p_a, const QString &p_b)
return true;
#endif
}
QString VUtils::promptForFileName(const QString &p_title,
const QString &p_label,
const QString &p_default,
const QString &p_dir,
QWidget *p_parent)
{
QString name = p_default;
QString text = p_label;
QDir paDir(p_dir);
while (true) {
bool ok;
name = QInputDialog::getText(p_parent,
p_title,
text,
QLineEdit::Normal,
name,
&ok);
if (!ok || name.isEmpty()) {
return "";
}
if (!VUtils::checkFileNameLegal(name)) {
text = QObject::tr("Illegal name. Please try again:");
continue;
}
if (paDir.exists(name)) {
text = QObject::tr("Name already exists. Please try again:");
continue;
}
break;
}
return name;
}

View File

@ -333,6 +333,12 @@ public:
static bool inSameDrive(const QString &p_a, const QString &p_b);
static QString promptForFileName(const QString &p_title,
const QString &p_label,
const QString &p_default,
const QString &p_dir,
QWidget *p_parent = nullptr);
// Regular expression for image link.
// ![image title]( http://github.com/tamlok/vnote.jpg "alt text" =200x100)
// Captured texts (need to be trimmed):

View File

@ -52,6 +52,7 @@ const QString VConfigManager::c_exportFolderName = QString("vnote_exports");
VConfigManager::VConfigManager(QObject *p_parent)
: QObject(p_parent),
m_noteListViewOrder(-1),
m_explorerCurrentIndex(-1),
m_hasReset(false),
userSettings(NULL),
defaultSettings(NULL),
@ -1218,7 +1219,7 @@ void VConfigManager::setLastOpenedFiles(const QVector<VFileSessionInfo> &p_files
m_sessionSettings->endArray();
}
void VConfigManager::getHistory(QLinkedList<VHistoryEntry> &p_history)
void VConfigManager::getHistory(QLinkedList<VHistoryEntry> &p_history) const
{
p_history.clear();
@ -1254,6 +1255,45 @@ void VConfigManager::setHistory(const QLinkedList<VHistoryEntry> &p_history)
m_sessionSettings->endArray();
}
void VConfigManager::getExplorerEntries(QVector<VExplorerEntry> &p_entries) const
{
p_entries.clear();
int size = m_sessionSettings->beginReadArray("explorer_starred");
for (int i = 0; i < size; ++i) {
m_sessionSettings->setArrayIndex(i);
p_entries.append(VExplorerEntry::fromSettings(m_sessionSettings));
}
m_sessionSettings->endArray();
}
void VConfigManager::setExplorerEntries(const QVector<VExplorerEntry> &p_entries)
{
if (m_hasReset) {
return;
}
const QString section("explorer_starred");
// Clear it first
m_sessionSettings->beginGroup(section);
m_sessionSettings->remove("");
m_sessionSettings->endGroup();
m_sessionSettings->beginWriteArray(section);
int idx = 0;
for (auto const & entry : p_entries) {
if (entry.m_isStarred) {
m_sessionSettings->setArrayIndex(idx);
entry.toSettings(m_sessionSettings);
++idx;
}
}
m_sessionSettings->endArray();
}
QVector<VMagicWord> VConfigManager::getCustomMagicWords()
{
QVector<VMagicWord> words;

View File

@ -17,6 +17,7 @@
#include "utils/vmetawordmanager.h"
#include "markdownitoption.h"
#include "vhistoryentry.h"
#include "vexplorerentry.h"
class QJsonObject;
class QString;
@ -131,6 +132,9 @@ public:
int getCurNotebookIndex() const;
void setCurNotebookIndex(int index);
int getNaviBoxCurrentIndex() const;
void setNaviBoxCurrentIndex(int p_index);
// Read [notebooks] section from settings into @p_notebooks.
void getNotebooks(QVector<VNotebook *> &p_notebooks, QObject *p_parent);
@ -359,12 +363,22 @@ public:
void setLastOpenedFiles(const QVector<VFileSessionInfo> &p_files);
// Read history from [history] of session.ini.
void getHistory(QLinkedList<VHistoryEntry> &p_history);
void getHistory(QLinkedList<VHistoryEntry> &p_history) const;
void setHistory(const QLinkedList<VHistoryEntry> &p_history);
int getHistorySize() const;
// Read explorer's starred entries from [explorer_starred] of session.ini.
void getExplorerEntries(QVector<VExplorerEntry> &p_entries) const;
// Output starred entries to [explorer_starred] of session.ini.
void setExplorerEntries(const QVector<VExplorerEntry> &p_entries);
int getExplorerCurrentIndex() const;
void setExplorerCurrentIndex(int p_idx);
// Read custom magic words from [magic_words] section.
QVector<VMagicWord> getCustomMagicWords();
@ -899,6 +913,9 @@ private:
// View order of note list.
int m_noteListViewOrder;
// Current entry index of explorer entries.
int m_explorerCurrentIndex;
// Whether user has reset the configurations.
bool m_hasReset;
@ -1004,6 +1021,16 @@ inline void VConfigManager::setCurNotebookIndex(int index)
setConfigToSessionSettings("global", "current_notebook", index);
}
inline int VConfigManager::getNaviBoxCurrentIndex() const
{
return getConfigFromSessionSettings("global", "navibox_current_index").toInt();
}
inline void VConfigManager::setNaviBoxCurrentIndex(int p_index)
{
setConfigToSessionSettings("global", "navibox_current_index", p_index);
}
inline void VConfigManager::getNotebooks(QVector<VNotebook *> &p_notebooks,
QObject *p_parent)
{
@ -2326,4 +2353,23 @@ inline void VConfigManager::setNoteListViewOrder(int p_order)
m_noteListViewOrder = p_order;
setConfigToSettings("global", "note_list_view_order", m_noteListViewOrder);
}
inline int VConfigManager::getExplorerCurrentIndex() const
{
if (m_explorerCurrentIndex == -1) {
const_cast<VConfigManager *>(this)->m_explorerCurrentIndex = getConfigFromSessionSettings("global", "explorer_current_entry").toInt();
}
return m_explorerCurrentIndex;
}
inline void VConfigManager::setExplorerCurrentIndex(int p_idx)
{
if (p_idx == m_explorerCurrentIndex) {
return;
}
m_explorerCurrentIndex = p_idx;
setConfigToSessionSettings("global", "explorer_current_entry", m_explorerCurrentIndex);
}
#endif // VCONFIGMANAGER_H

View File

@ -33,6 +33,7 @@ VDirectoryTree::VDirectoryTree(QWidget *parent)
setColumnCount(1);
setHeaderHidden(true);
setContextMenuPolicy(Qt::CustomContextMenu);
setFitContent(true);
initShortcuts();
@ -147,6 +148,8 @@ void VDirectoryTree::updateDirectoryTree()
if (!restoreCurrentItem() && topLevelItemCount() > 0) {
setCurrentItem(topLevelItem(0));
}
resizeColumnToContents(0);
}
bool VDirectoryTree::restoreCurrentItem()
@ -425,8 +428,10 @@ void VDirectoryTree::contextMenuRequested(QPoint pos)
menu.addAction(reloadAct);
if (item) {
QAction *openLocationAct = new QAction(tr("&Open Folder Location"), &menu);
openLocationAct->setToolTip(tr("Open the folder containing this folder in operating system"));
QAction *openLocationAct = new QAction(VIconUtils::menuIcon(":/resources/icons/open_location.svg"),
tr("&Open Folder Location"),
&menu);
openLocationAct->setToolTip(tr("Explore this folder in operating system"));
connect(openLocationAct, &QAction::triggered,
this, &VDirectoryTree::openDirectoryLocation);
menu.addAction(openLocationAct);
@ -645,7 +650,7 @@ void VDirectoryTree::openDirectoryLocation() const
{
QTreeWidgetItem *curItem = currentItem();
V_ASSERT(curItem);
QUrl url = QUrl::fromLocalFile(getVDirectory(curItem)->fetchBasePath());
QUrl url = QUrl::fromLocalFile(getVDirectory(curItem)->fetchPath());
QDesktopServices::openUrl(url);
}

View File

@ -524,8 +524,10 @@ void VEditWindow::tabbarContextMenuRequested(QPoint p_pos)
QDesktopServices::openUrl(url);
});
QAction *openLocationAct = new QAction(tr("Open Note Location"), &menu);
openLocationAct->setToolTip(tr("Open the folder containing this note in operating system"));
QAction *openLocationAct = new QAction(VIconUtils::menuIcon(":/resources/icons/open_location.svg"),
tr("Open Note Location"),
&menu);
openLocationAct->setToolTip(tr("Explore the folder containing this note in operating system"));
connect(openLocationAct, &QAction::triggered,
this, [this](){
int tab = GET_TAB_FROM_SENDER();

737
src/vexplorer.cpp Normal file
View File

@ -0,0 +1,737 @@
#include "vexplorer.h"
#include <QtWidgets>
#include <QFileSystemModel>
#include <QDesktopServices>
#include "utils/viconutils.h"
#include "utils/vutils.h"
#include "vconfigmanager.h"
#include "vmainwindow.h"
#include "vcart.h"
#include "vlineedit.h"
#include "vhistorylist.h"
#include "vorphanfile.h"
extern VMainWindow *g_mainWin;
extern VConfigManager *g_config;
const QString VExplorer::c_infoShortcutSequence = "F2";
VExplorer::VExplorer(QWidget *p_parent)
: QWidget(p_parent),
m_initialized(false),
m_uiInitialized(false),
m_index(-1)
{
}
void VExplorer::setupUI()
{
if (m_uiInitialized) {
return;
}
m_uiInitialized = true;
QLabel *dirLabel = new QLabel(tr("Directory"), this);
m_openBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/dir_item.svg"),
"",
this);
m_openBtn->setToolTip(tr("Open"));
m_openBtn->setProperty("FlatBtn", true);
connect(m_openBtn, &QPushButton::clicked,
this, [this]() {
static QString defaultPath = g_config->getDocumentPathOrHomePath();
QString dirPath = QFileDialog::getExistingDirectory(this,
tr("Select Root Directory To Explore"),
defaultPath,
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
if (!dirPath.isEmpty()) {
defaultPath = VUtils::basePathFromPath(dirPath);
int idx = addEntry(dirPath);
updateDirectoryComboBox();
if (idx != -1) {
setCurrentEntry(idx);
}
}
});
m_openLocationBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/open_location.svg"),
"",
this);
m_openLocationBtn->setToolTip(tr("Open Directory Location"));
m_openLocationBtn->setProperty("FlatBtn", true);
connect(m_openLocationBtn, &QPushButton::clicked,
this, [this]() {
if (checkIndex()) {
QUrl url = QUrl::fromLocalFile(m_entries[m_index].m_directory);
QDesktopServices::openUrl(url);
}
});
m_starBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/star.svg"),
"",
this);
m_starBtn->setToolTip(tr("Star"));
m_starBtn->setProperty("FlatBtn", true);
connect(m_starBtn, &QPushButton::clicked,
this, [this]() {
if (checkIndex()) {
m_entries[m_index].m_isStarred = !m_entries[m_index].m_isStarred;
g_config->setExplorerEntries(m_entries);
updateExplorerEntryIndexInConfig();
updateStarButton();
}
});
QHBoxLayout *dirLayout = new QHBoxLayout();
dirLayout->addWidget(dirLabel);
dirLayout->addStretch();
dirLayout->addWidget(m_openBtn);
dirLayout->addWidget(m_openLocationBtn);
dirLayout->addWidget(m_starBtn);
dirLayout->setContentsMargins(0, 0, 0, 0);
m_dirCB = VUtils::getComboBox(this);
m_dirCB->setEditable(true);
m_dirCB->setLineEdit(new VLineEdit(this));
m_dirCB->setToolTip(tr("Path of the root directory to explore"));
m_dirCB->lineEdit()->setPlaceholderText(tr("Root path to explore"));
m_dirCB->lineEdit()->setProperty("EmbeddedEdit", true);
connect(m_dirCB->lineEdit(), &QLineEdit::editingFinished,
this, [this]() {
QString text = m_dirCB->currentText();
int idx = addEntry(text);
updateDirectoryComboBox();
if (idx != -1) {
setCurrentEntry(idx);
}
});
QCompleter *completer = new QCompleter(this);
QFileSystemModel *fsModel = new QFileSystemModel(completer);
fsModel->setRootPath("");
completer->setModel(fsModel);
// Enable styling the popup list via QListView::item.
completer->popup()->setItemDelegate(new QStyledItemDelegate(this));
completer->setCompletionMode(QCompleter::PopupCompletion);
#if defined(Q_OS_WIN)
completer->setCaseSensitivity(Qt::CaseInsensitive);
#else
completer->setCaseSensitivity(Qt::CaseSensitive);
#endif
m_dirCB->lineEdit()->setCompleter(completer);
connect(m_dirCB, SIGNAL(activated(int)),
this, SLOT(handleEntryActivated(int)));
m_dirCB->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
QLabel *imgLabel = new QLabel(tr("Image Folder"), this);
m_imgFolderEdit = new VLineEdit(this);
m_imgFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
.arg(g_config->getImageFolderExt()));
QString imgFolderTip = tr("Set the path of the image folder to store images "
"of files within the root directory.\nIf absolute path is used, "
"VNote will not manage those images."
"(empty to use global configuration)");
m_imgFolderEdit->setToolTip(imgFolderTip);
connect(m_imgFolderEdit, &QLineEdit::editingFinished,
this, [this]() {
if (checkIndex()) {
QString folder = m_imgFolderEdit->text();
if (folder.isEmpty() || VUtils::checkPathLegal(folder)) {
m_entries[m_index].m_imageFolder = folder;
if (m_entries[m_index].m_isStarred) {
g_config->setExplorerEntries(m_entries);
}
} else {
m_imgFolderEdit->setText(m_entries[m_index].m_imageFolder);
}
}
});
// New file/dir.
m_newFileBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/create_note_tb.svg"),
"",
this);
m_newFileBtn->setToolTip(tr("New File"));
m_newFileBtn->setProperty("FlatBtn", true);
connect(m_newFileBtn, &QPushButton::clicked,
this, &VExplorer::newFile);
m_newDirBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/create_rootdir_tb.svg"),
"",
this);
m_newDirBtn->setToolTip(tr("New Folder"));
m_newDirBtn->setProperty("FlatBtn", true);
connect(m_newDirBtn, &QPushButton::clicked,
this, &VExplorer::newFolder);
QHBoxLayout *btnLayout = new QHBoxLayout();
btnLayout->addStretch();
btnLayout->addWidget(m_newFileBtn);
btnLayout->addWidget(m_newDirBtn);
btnLayout->setContentsMargins(0, 0, 0, 0);
QFileSystemModel *dirModel = new QFileSystemModel(this);
dirModel->setRootPath("");
m_tree = new QTreeView(this);
m_tree->setModel(dirModel);
m_tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_tree->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_tree, &QTreeView::customContextMenuRequested,
this, &VExplorer::handleContextMenuRequested);
connect(m_tree, &QTreeView::activated,
this, [this](const QModelIndex &p_index) {
QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
if (!model->isDir(p_index)) {
QStringList files;
files << model->filePath(p_index);
openFiles(files, g_config->getNoteOpenMode());
}
});
connect(m_tree, &QTreeView::expanded,
this, &VExplorer::resizeTreeToContents);
connect(m_tree, &QTreeView::collapsed,
this, &VExplorer::resizeTreeToContents);
connect(dirModel, &QFileSystemModel::directoryLoaded,
this, &VExplorer::resizeTreeToContents);
m_tree->setHeaderHidden(true);
// Show only the Name column.
for (int i = 1; i < dirModel->columnCount(); ++i) {
m_tree->hideColumn(i);
}
QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->addLayout(dirLayout);
mainLayout->addWidget(m_dirCB);
mainLayout->addWidget(imgLabel);
mainLayout->addWidget(m_imgFolderEdit);
mainLayout->addLayout(btnLayout);
mainLayout->addWidget(m_tree);
mainLayout->setContentsMargins(3, 0, 3, 0);
setLayout(mainLayout);
}
void VExplorer::init()
{
if (m_initialized) {
return;
}
m_initialized = true;
setupUI();
QShortcut *infoShortcut = new QShortcut(QKeySequence(c_infoShortcutSequence), this);
infoShortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(infoShortcut, &QShortcut::activated,
this, [this]() {
});
g_config->getExplorerEntries(m_entries);
m_index = g_config->getExplorerCurrentIndex();
if (m_entries.isEmpty()) {
m_index = -1;
g_config->setExplorerCurrentIndex(m_index);
} else if (m_index < 0 || m_index >= m_entries.size()) {
m_index = 0;
g_config->setExplorerCurrentIndex(m_index);
}
updateDirectoryComboBox();
setCurrentEntry(m_index);
}
void VExplorer::showEvent(QShowEvent *p_event)
{
init();
QWidget::showEvent(p_event);
}
void VExplorer::focusInEvent(QFocusEvent *p_event)
{
init();
QWidget::focusInEvent(p_event);
if (m_index < 0) {
m_openBtn->setFocus();
} else {
m_tree->setFocus();
}
}
void VExplorer::updateUI()
{
}
void VExplorer::updateDirectoryComboBox()
{
m_dirCB->clear();
for (int i = 0; i < m_entries.size(); ++i) {
m_dirCB->addItem(QDir::toNativeSeparators(m_entries[i].m_directory));
m_dirCB->setItemData(i, m_entries[i].m_directory, Qt::ToolTipRole);
}
m_dirCB->setCurrentIndex(m_index);
}
int VExplorer::addEntry(const QString &p_dir)
{
if (p_dir.isEmpty() || !QFileInfo::exists(p_dir) || !QDir::isAbsolutePath(p_dir)) {
return -1;
}
QString dir(QDir::cleanPath(p_dir));
int idx = -1;
for (idx = 0; idx < m_entries.size(); ++idx) {
if (VUtils::equalPath(dir, m_entries[idx].m_directory)) {
break;
}
}
if (idx < m_entries.size()) {
return idx;
}
m_entries.append(VExplorerEntry(QFileInfo(dir).absoluteFilePath(), ""));
return m_entries.size() - 1;
}
void VExplorer::handleEntryActivated(int p_idx)
{
bool valid = false;
QString imgFolder;
if (p_idx >= 0 && p_idx < m_entries.size()) {
valid = true;
imgFolder = m_entries[p_idx].m_imageFolder;
} else {
p_idx = -1;
}
m_index = p_idx;
updateExplorerEntryIndexInConfig();
m_imgFolderEdit->setText(imgFolder);
m_imgFolderEdit->setEnabled(valid);
m_openLocationBtn->setEnabled(valid);
m_newFileBtn->setEnabled(valid);
m_newDirBtn->setEnabled(valid);
updateStarButton();
updateTree();
}
void VExplorer::updateStarButton()
{
// -1 for disabled, 0 for star, 1 for unstar.
int star = -1;
if (checkIndex()) {
star = m_entries[m_index].m_isStarred ? 1 : 0;
}
bool enabled = true;
switch (star) {
default:
enabled = false;
V_FALLTHROUGH;
case 0:
m_starBtn->setEnabled(enabled);
m_starBtn->setIcon(VIconUtils::buttonIcon(":/resources/icons/star.svg"));
m_starBtn->setToolTip(tr("Star"));
break;
case 1:
m_starBtn->setEnabled(enabled);
m_starBtn->setIcon(VIconUtils::buttonIcon(":/resources/icons/unstar.svg"));
m_starBtn->setToolTip(tr("Unstar"));
break;
}
}
void VExplorer::setCurrentEntry(int p_index)
{
m_dirCB->setCurrentIndex(p_index);
handleEntryActivated(p_index);
}
void VExplorer::updateExplorerEntryIndexInConfig()
{
int idx = -1;
if (m_index >= 0 && m_index < m_entries.size()) {
int starIdx = -1;
for (int j = 0; j <= m_index; ++j) {
if (m_entries[j].m_isStarred) {
++starIdx;
}
}
if (starIdx > -1) {
idx = starIdx;
}
}
g_config->setExplorerCurrentIndex(idx);
}
void VExplorer::updateTree()
{
if (checkIndex()) {
QString pa = QDir::cleanPath(m_entries[m_index].m_directory);
QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
model->setRootPath(pa);
const QModelIndex rootIndex = model->index(pa);
if (rootIndex.isValid()) {
m_tree->setRootIndex(rootIndex);
resizeTreeToContents();
return;
} else {
model->setRootPath("");
}
}
m_tree->reset();
resizeTreeToContents();
}
void VExplorer::handleContextMenuRequested(QPoint p_pos)
{
QModelIndex index = m_tree->indexAt(p_pos);
if (!index.isValid()) {
return;
}
QMenu menu(this);
menu.setToolTipsVisible(true);
QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
QModelIndexList selectedIdx = m_tree->selectionModel()->selectedRows();
if (selectedIdx.size() == 1 && model->isDir(selectedIdx[0])) {
QString filePath = model->filePath(selectedIdx[0]);
QAction *setRootAct = new QAction(VIconUtils::menuIcon(":/resources/icons/explore_root.svg"),
tr("Set As Root"),
&menu);
setRootAct->setToolTip(tr("Set current folder as the root directory to explore"));
connect(setRootAct, &QAction::triggered,
this, [this, filePath]() {
int idx = addEntry(filePath);
updateDirectoryComboBox();
if (idx != -1) {
setCurrentEntry(idx);
}
});
menu.addAction(setRootAct);
QAction *openLocationAct = new QAction(VIconUtils::menuIcon(":/resources/icons/open_location.svg"),
tr("Open Folder Location"),
&menu);
openLocationAct->setToolTip(tr("Explore this folder in operating system"));
connect(openLocationAct, &QAction::triggered,
this, [this, filePath]() {
QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
});
menu.addAction(openLocationAct);
menu.addSeparator();
QAction *fileInfoAct = new QAction(VIconUtils::menuIcon(":/resources/icons/note_info.svg"),
tr("&Info\t%1").arg(VUtils::getShortcutText(c_infoShortcutSequence)),
&menu);
fileInfoAct->setToolTip(tr("View and edit current folder's information"));
connect(fileInfoAct, &QAction::triggered,
this, [this, filePath]() {
renameFile(filePath);
});
menu.addAction(fileInfoAct);
menu.exec(m_tree->mapToGlobal(p_pos));
return;
}
bool allFiles = true;
QStringList selectedFiles;
for (auto const & it : selectedIdx) {
if (model->isDir(it)) {
allFiles = false;
break;
}
selectedFiles << model->filePath(it);
}
if (!allFiles) {
return;
}
// Only files are selected.
// We do not support copy/cut/deletion of files.
QAction *openInReadAct = new QAction(VIconUtils::menuIcon(":/resources/icons/reading.svg"),
tr("Open In Read Mode"),
&menu);
openInReadAct->setToolTip(tr("Open selected files in read mode"));
connect(openInReadAct, &QAction::triggered,
this, [this, selectedFiles]() {
openFiles(selectedFiles, OpenFileMode::Read, true);
});
menu.addAction(openInReadAct);
QAction *openInEditAct = new QAction(VIconUtils::menuIcon(":/resources/icons/editing.svg"),
tr("Open In Edit Mode"),
&menu);
openInEditAct->setToolTip(tr("Open selected files in edit mode"));
connect(openInEditAct, &QAction::triggered,
this, [this, selectedFiles]() {
openFiles(selectedFiles, OpenFileMode::Edit, true);
});
menu.addAction(openInEditAct);
menu.addSeparator();
if (selectedFiles.size() == 1) {
QAction *openLocationAct = new QAction(VIconUtils::menuIcon(":/resources/icons/open_location.svg"),
tr("Open File Location"),
&menu);
openLocationAct->setToolTip(tr("Explore the folder containing this file in operating system"));
connect(openLocationAct, &QAction::triggered,
this, [this, filePath = selectedFiles[0]] () {
QDesktopServices::openUrl(QUrl::fromLocalFile(VUtils::basePathFromPath(filePath)));
});
menu.addAction(openLocationAct);
menu.addSeparator();
}
QAction *addToCartAct = new QAction(VIconUtils::menuIcon(":/resources/icons/cart.svg"),
tr("Add To Cart"),
&menu);
addToCartAct->setToolTip(tr("Add selected files to Cart for further processing"));
connect(addToCartAct, &QAction::triggered,
this, [this, selectedFiles]() {
VCart *cart = g_mainWin->getCart();
for (int i = 0; i < selectedFiles.size(); ++i) {
cart->addFile(selectedFiles[i]);
}
g_mainWin->showStatusMessage(tr("%1 %2 added to Cart")
.arg(selectedFiles.size())
.arg(selectedFiles.size() > 1 ? tr("files") : tr("file")));
});
menu.addAction(addToCartAct);
QAction *pinToHistoryAct = new QAction(VIconUtils::menuIcon(":/resources/icons/pin.svg"),
tr("Pin To History"),
&menu);
pinToHistoryAct->setToolTip(tr("Pin selected files to History"));
connect(pinToHistoryAct, &QAction::triggered,
this, [this, selectedFiles]() {
g_mainWin->getHistoryList()->pinFiles(selectedFiles);
g_mainWin->showStatusMessage(tr("%1 %2 pinned to History")
.arg(selectedFiles.size())
.arg(selectedFiles.size() > 1 ? tr("files") : tr("file")));
});
menu.addAction(pinToHistoryAct);
if (selectedFiles.size() == 1) {
QAction *fileInfoAct = new QAction(VIconUtils::menuIcon(":/resources/icons/note_info.svg"),
tr("&Info\t%1").arg(VUtils::getShortcutText(c_infoShortcutSequence)),
&menu);
fileInfoAct->setToolTip(tr("View and edit current file's information"));
connect(fileInfoAct, &QAction::triggered,
this, [this, filePath = selectedFiles[0]]() {
renameFile(filePath);
});
menu.addAction(fileInfoAct);
}
menu.exec(m_tree->mapToGlobal(p_pos));
}
void VExplorer::openFiles(const QStringList &p_files,
OpenFileMode p_mode,
bool p_forceMode)
{
if (!p_files.isEmpty()) {
Q_ASSERT(checkIndex());
QString imgFolder = m_entries[m_index].m_imageFolder;
QVector<VFile *> vfiles = g_mainWin->openFiles(p_files, false, p_mode, p_forceMode, false);
// Set image folder.
for (auto it : vfiles) {
if (it->getType() == FileType::Orphan) {
static_cast<VOrphanFile *>(it)->setImageFolder(imgFolder);
}
}
}
}
void VExplorer::resizeTreeToContents()
{
m_tree->resizeColumnToContents(0);
}
// 1. When only one folder is selected, create a file in that folder.
// 2. Otherwise, create a file in the root directory.
void VExplorer::newFile()
{
Q_ASSERT(checkIndex());
QString parentDir;
QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
QModelIndexList selectedIdx = m_tree->selectionModel()->selectedRows();
if (selectedIdx.size() == 1 && model->isDir(selectedIdx[0])) {
parentDir = model->filePath(selectedIdx[0]);
} else {
parentDir = m_entries[m_index].m_directory;
}
qDebug() << "new file in" << parentDir;
QString name = VUtils::promptForFileName(tr("New File"),
tr("File name (%1):").arg(parentDir),
"",
parentDir,
g_mainWin);
if (name.isEmpty()) {
return;
}
QDir paDir(parentDir);
QString filePath = paDir.filePath(name);
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly)) {
qWarning() << "Fail to create file" << filePath;
VUtils::showMessage(QMessageBox::Warning,
tr("New File"),
tr("Fail to create file <span style=\"%1\">%2</span>.")
.arg(g_config->c_dataTextStyle)
.arg(filePath),
"",
QMessageBox::Ok,
QMessageBox::Ok,
g_mainWin);
return;
}
file.close();
m_tree->setFocus();
// Select the file.
const QModelIndex fileIndex = model->index(filePath);
Q_ASSERT(fileIndex.isValid());
m_tree->scrollTo(fileIndex);
m_tree->clearSelection();
m_tree->setCurrentIndex(fileIndex);
}
// 1. When only one folder is selected, create a folder in that folder.
// 2. Otherwise, create a folder in the root directory.
void VExplorer::newFolder()
{
Q_ASSERT(checkIndex());
QString parentDir;
QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
QModelIndexList selectedIdx = m_tree->selectionModel()->selectedRows();
if (selectedIdx.size() == 1 && model->isDir(selectedIdx[0])) {
parentDir = model->filePath(selectedIdx[0]);
} else {
parentDir = m_entries[m_index].m_directory;
}
qDebug() << "new folder in" << parentDir;
QString name = VUtils::promptForFileName(tr("New Folder"),
tr("Folder name (%1):").arg(parentDir),
"",
parentDir,
g_mainWin);
if (name.isEmpty()) {
return;
}
QDir paDir(parentDir);
QString folderPath = paDir.filePath(name);
if (!paDir.mkdir(name)) {
qWarning() << "Fail to create folder" << folderPath;
VUtils::showMessage(QMessageBox::Warning,
tr("New Folder"),
tr("Fail to create folder <span style=\"%1\">%2</span>.")
.arg(g_config->c_dataTextStyle)
.arg(folderPath),
"",
QMessageBox::Ok,
QMessageBox::Ok,
g_mainWin);
return;
}
m_tree->setFocus();
// Select the folder.
const QModelIndex folderIndex = model->index(folderPath);
Q_ASSERT(folderIndex.isValid());
m_tree->scrollTo(folderIndex);
m_tree->clearSelection();
m_tree->setCurrentIndex(folderIndex);
}
void VExplorer::renameFile(const QString &p_filePath)
{
Q_ASSERT(checkIndex());
QFileInfo fi(p_filePath);
QString parentDir = fi.path();
QString oldName = fi.fileName();
QString name = VUtils::promptForFileName(tr("File Information"),
tr("Rename file (%1):").arg(p_filePath),
oldName,
parentDir,
g_mainWin);
if (name.isEmpty()) {
return;
}
QDir paDir(parentDir);
if (!paDir.rename(oldName, name)) {
qWarning() << "Fail to rename" << p_filePath;
VUtils::showMessage(QMessageBox::Warning,
tr("File Information"),
tr("Fail to rename file <span style=\"%1\">%2</span>.")
.arg(g_config->c_dataTextStyle)
.arg(p_filePath),
"",
QMessageBox::Ok,
QMessageBox::Ok,
g_mainWin);
return;
}
}

95
src/vexplorer.h Normal file
View File

@ -0,0 +1,95 @@
#ifndef VEXPLORER_H
#define VEXPLORER_H
#include <QWidget>
#include <QVector>
#include "vexplorerentry.h"
#include "vconstants.h"
class QPushButton;
class QTreeView;
class QTreeWidgetItem;
class QShowEvent;
class QFocusEvent;
class QComboBox;
class VLineEdit;
class VExplorer : public QWidget
{
Q_OBJECT
public:
explicit VExplorer(QWidget *p_parent = nullptr);
protected:
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
void focusInEvent(QFocusEvent *p_event) Q_DECL_OVERRIDE;
private slots:
void handleEntryActivated(int p_idx);
void handleContextMenuRequested(QPoint p_pos);
private:
void setupUI();
void init();
void updateUI();
void updateDirectoryComboBox();
// Add an entry to m_entries with @p_dir being the path of the entry.
// Return the index of the corresponding entry if succeed; otherwise,
// return -1.
int addEntry(const QString &p_dir);
void setCurrentEntry(int p_index);
bool checkIndex() const;
void updateExplorerEntryIndexInConfig();
void updateStarButton();
void updateTree();
void openFiles(const QStringList &p_files,
OpenFileMode p_mode,
bool p_forceMode = false);
void resizeTreeToContents();
void newFile();
void newFolder();
void renameFile(const QString &p_filePath);
bool m_initialized;
bool m_uiInitialized;
QVector<VExplorerEntry> m_entries;
int m_index;
QPushButton *m_openBtn;
QPushButton *m_openLocationBtn;
QPushButton *m_starBtn;
QComboBox *m_dirCB;
VLineEdit *m_imgFolderEdit;
QPushButton *m_newFileBtn;
QPushButton *m_newDirBtn;
QTreeView *m_tree;
static const QString c_infoShortcutSequence;
};
inline bool VExplorer::checkIndex() const
{
return m_index >= 0 && m_index < m_entries.size();
}
#endif // VEXPLORER_H

51
src/vexplorerentry.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef VEXPLORERENTRY_H
#define VEXPLORERENTRY_H
#include <QSettings>
namespace ExplorerConfig
{
static const QString c_directory = "directory";
static const QString c_imageFolder = "image_folder";
}
class VExplorerEntry
{
public:
VExplorerEntry()
: m_isStarred(false)
{
}
VExplorerEntry(const QString &p_directory,
const QString &p_imageFolder,
bool p_isStarred = false)
: m_directory(p_directory),
m_imageFolder(p_imageFolder),
m_isStarred(p_isStarred)
{
}
static VExplorerEntry fromSettings(const QSettings *p_settings)
{
VExplorerEntry entry;
entry.m_directory = p_settings->value(ExplorerConfig::c_directory).toString();
entry.m_imageFolder = p_settings->value(ExplorerConfig::c_imageFolder).toString();
entry.m_isStarred = true;
return entry;
}
void toSettings(QSettings *p_settings) const
{
p_settings->setValue(ExplorerConfig::c_directory, m_directory);
p_settings->setValue(ExplorerConfig::c_imageFolder, m_imageFolder);
}
QString m_directory;
QString m_imageFolder;
bool m_isStarred;
};
#endif // VEXPLORERENTRY_H

View File

@ -609,8 +609,10 @@ void VFileList::contextMenuRequested(QPoint pos)
if (item) {
menu.addSeparator();
if (selectedSize == 1) {
QAction *openLocationAct = new QAction(tr("&Open Note Location"), &menu);
openLocationAct->setToolTip(tr("Open the folder containing this note in operating system"));
QAction *openLocationAct = new QAction(VIconUtils::menuIcon(":/resources/icons/open_location.svg"),
tr("&Open Note Location"),
&menu);
openLocationAct->setToolTip(tr("Explore the folder containing this note in operating system"));
connect(openLocationAct, &QAction::triggered,
this, &VFileList::openFileLocation);
menu.addAction(openLocationAct);
@ -1359,7 +1361,7 @@ void VFileList::sortFiles(QVector<VNoteFile *> &p_files, ViewOrder p_order)
case ViewOrder::NameReverse:
reverse = true;
V_FALLTHROUGH
V_FALLTHROUGH;
case ViewOrder::Name:
std::sort(p_files.begin(), p_files.end(), [reverse](const VNoteFile *p_a, const VNoteFile *p_b) {
if (reverse) {
@ -1372,7 +1374,7 @@ void VFileList::sortFiles(QVector<VNoteFile *> &p_files, ViewOrder p_order)
case ViewOrder::CreatedTimeReverse:
reverse = true;
V_FALLTHROUGH
V_FALLTHROUGH;
case ViewOrder::CreatedTime:
std::sort(p_files.begin(), p_files.end(), [reverse](const VNoteFile *p_a, const VNoteFile *p_b) {
if (reverse) {
@ -1385,7 +1387,7 @@ void VFileList::sortFiles(QVector<VNoteFile *> &p_files, ViewOrder p_order)
case ViewOrder::ModifiedTimeReverse:
reverse = true;
V_FALLTHROUGH
V_FALLTHROUGH;
case ViewOrder::ModifiedTime:
std::sort(p_files.begin(), p_files.end(), [reverse](const VNoteFile *p_a, const VNoteFile *p_b) {
if (reverse) {

View File

@ -46,6 +46,7 @@
#include "vlistfolderue.h"
#include "dialog/vfixnotebookdialog.h"
#include "vhistorylist.h"
#include "vexplorer.h"
extern VConfigManager *g_config;
@ -280,6 +281,11 @@ void VMainWindow::setupNaviBox()
m_naviBox->addItem(m_historyList,
":/resources/icons/history.svg",
tr("History"));
m_explorer = new VExplorer();
m_naviBox->addItem(m_explorer,
":/resources/icons/explorer.svg",
tr("Explorer"));
}
void VMainWindow::setupNotebookPanel()
@ -930,7 +936,11 @@ void VMainWindow::initFileMenu()
// Update lastPath
lastPath = QFileInfo(files[0]).path();
openFiles(VUtils::filterFilePathsToOpen(files));
openFiles(VUtils::filterFilePathsToOpen(files),
false,
g_config->getNoteOpenMode(),
false,
false);
});
fileMenu->addAction(openAct);
@ -2130,6 +2140,7 @@ void VMainWindow::saveStateAndGeometry()
g_config->setSearchDockChecked(m_searchDock->isVisible());
g_config->setNotebookSplitterState(m_nbSplitter->saveState());
g_config->setMainSplitterState(m_mainSplitter->saveState());
g_config->setNaviBoxCurrentIndex(m_naviBox->currentIndex());
}
void VMainWindow::restoreStateAndGeometry()
@ -2156,6 +2167,8 @@ void VMainWindow::restoreStateAndGeometry()
if (!nbSplitterState.isEmpty()) {
m_nbSplitter->restoreState(nbSplitterState);
}
m_naviBox->setCurrentIndex(g_config->getNaviBoxCurrentIndex());
}
void VMainWindow::handleCurrentDirectoryChanged(const VDirectory *p_dir)
@ -2457,12 +2470,15 @@ bool VMainWindow::tryOpenInternalFile(const QString &p_filePath)
return false;
}
void VMainWindow::openFiles(const QStringList &p_files,
bool p_forceOrphan,
OpenFileMode p_mode,
bool p_forceMode,
bool p_oneByOne)
QVector<VFile *> VMainWindow::openFiles(const QStringList &p_files,
bool p_forceOrphan,
OpenFileMode p_mode,
bool p_forceMode,
bool p_oneByOne)
{
QVector<VFile *> vfiles;
vfiles.reserve(p_files.size());
for (int i = 0; i < p_files.size(); ++i) {
VFile *file = NULL;
if (!p_forceOrphan) {
@ -2474,10 +2490,14 @@ void VMainWindow::openFiles(const QStringList &p_files,
}
m_editArea->openFile(file, p_mode, p_forceMode);
vfiles.append(file);
if (p_oneByOne) {
QCoreApplication::sendPostedEvents();
}
}
return vfiles;
}
void VMainWindow::editOrphanFileInfo(VFile *p_file)
@ -2497,7 +2517,7 @@ void VMainWindow::checkSharedMemory()
QStringList files = m_guard->fetchFilesToOpen();
if (!files.isEmpty()) {
qDebug() << "shared memory fetch files" << files;
openFiles(files);
openFiles(files, false, g_config->getNoteOpenMode(), false, false);
// Eliminate the signal.
m_guard->fetchAskedToShow();
@ -2579,7 +2599,7 @@ void VMainWindow::openStartupPages()
{
QStringList pagesToOpen = VUtils::filterFilePathsToOpen(g_config->getStartupPages());
qDebug() << "open startup pages" << pagesToOpen;
openFiles(pagesToOpen, false, OpenFileMode::Read, false, true);
openFiles(pagesToOpen, false, g_config->getNoteOpenMode(), false, true);
break;
}
@ -3149,7 +3169,7 @@ void VMainWindow::kickOffStartUpTimer(const QStringList &p_files)
promptNewNotebookIfEmpty();
QCoreApplication::sendPostedEvents();
openStartupPages();
openFiles(p_files, false, OpenFileMode::Read, false, true);
openFiles(p_files, false, g_config->getNoteOpenMode(), false, true);
});
}

View File

@ -43,6 +43,7 @@ class VSearcher;
class QPrinter;
class VUniversalEntry;
class VHistoryList;
class VExplorer;
enum class PanelViewState
{
@ -88,11 +89,11 @@ public:
// Open files @p_files as orphan files or internal note files.
// If @p_forceOrphan is false, for each file, VNote will try to find out if
// it is a note inside VNote. If yes, VNote will open it as internal file.
void openFiles(const QStringList &p_files,
bool p_forceOrphan = false,
OpenFileMode p_mode = OpenFileMode::Read,
bool p_forceMode = false,
bool p_oneByOne = false);
QVector<VFile *> openFiles(const QStringList &p_files,
bool p_forceOrphan = false,
OpenFileMode p_mode = OpenFileMode::Read,
bool p_forceMode = false,
bool p_oneByOne = false);
// Try to open @p_filePath as internal note.
bool tryOpenInternalFile(const QString &p_filePath);
@ -449,6 +450,8 @@ private:
VHistoryList *m_historyList;
VExplorer *m_explorer;
// Interval of the shared memory timer in ms.
static const int c_sharedMemTimerInterval;
};

View File

@ -218,5 +218,10 @@
<file>resources/icons/clear_history.svg</file>
<file>resources/icons/pin.svg</file>
<file>resources/icons/view.svg</file>
<file>resources/icons/explorer.svg</file>
<file>resources/icons/star.svg</file>
<file>resources/icons/open_location.svg</file>
<file>resources/icons/unstar.svg</file>
<file>resources/icons/explore_root.svg</file>
</qresource>
</RCC>

View File

@ -50,112 +50,10 @@ VNotebookSelector::VNotebookSelector(QWidget *p_parent)
m_listWidget->viewport()->installEventFilter(this);
m_listWidget->installEventFilter(this);
initActions();
connect(this, SIGNAL(currentIndexChanged(int)),
this, SLOT(handleCurIndexChanged(int)));
}
void VNotebookSelector::initActions()
{
m_deleteNotebookAct = new QAction(VIconUtils::menuDangerIcon(":/resources/icons/delete_notebook.svg"),
tr("&Delete"), this);
m_deleteNotebookAct->setToolTip(tr("Delete current notebook"));
connect(m_deleteNotebookAct, SIGNAL(triggered(bool)),
this, SLOT(deleteNotebook()));
m_notebookInfoAct = new QAction(VIconUtils::menuIcon(":/resources/icons/notebook_info.svg"),
tr("&Info"), this);
m_notebookInfoAct->setToolTip(tr("View and edit current notebook's information"));
connect(m_notebookInfoAct, SIGNAL(triggered(bool)),
this, SLOT(editNotebookInfo()));
m_openLocationAct = new QAction(tr("&Open Notebook Location"), this);
m_openLocationAct->setToolTip(tr("Open the root folder of this notebook in operating system"));
connect(m_openLocationAct, &QAction::triggered,
this, [this]() {
QList<QListWidgetItem *> items = this->m_listWidget->selectedItems();
if (items.isEmpty()) {
return;
}
Q_ASSERT(items.size() == 1);
VNotebook *notebook = getNotebook(items[0]);
QUrl url = QUrl::fromLocalFile(notebook->getPath());
QDesktopServices::openUrl(url);
});
m_recycleBinAct = new QAction(VIconUtils::menuIcon(":/resources/icons/recycle_bin.svg"),
tr("&Recycle Bin"), this);
m_recycleBinAct->setToolTip(tr("Open the recycle bin of this notebook"));
connect(m_recycleBinAct, &QAction::triggered,
this, [this]() {
QList<QListWidgetItem *> items = this->m_listWidget->selectedItems();
if (items.isEmpty()) {
return;
}
Q_ASSERT(items.size() == 1);
VNotebook *notebook = getNotebook(items[0]);
QUrl url = QUrl::fromLocalFile(notebook->getRecycleBinFolderPath());
QDesktopServices::openUrl(url);
});
m_emptyRecycleBinAct = new QAction(VIconUtils::menuDangerIcon(":/resources/icons/empty_recycle_bin.svg"),
tr("&Empty Recycle Bin"), this);
m_emptyRecycleBinAct->setToolTip(tr("Empty the recycle bin of this notebook"));
connect(m_emptyRecycleBinAct, &QAction::triggered,
this, [this]() {
QList<QListWidgetItem *> items = this->m_listWidget->selectedItems();
if (items.isEmpty()) {
return;
}
Q_ASSERT(items.size() == 1);
VNotebook *notebook = getNotebook(items[0]);
QString binPath = notebook->getRecycleBinFolderPath();
int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
tr("Are you sure to empty recycle bin of notebook "
"<span style=\"%1\">%2</span>?")
.arg(g_config->c_dataTextStyle)
.arg(notebook->getName()),
tr("<span style=\"%1\">WARNING</span>: "
"VNote will delete all the files in directory "
"<span style=\"%2\">%3</span>."
"<br>It may be UNRECOVERABLE!")
.arg(g_config->c_warningTextStyle)
.arg(g_config->c_dataTextStyle)
.arg(binPath),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok,
this,
MessageBoxType::Danger);
if (ret == QMessageBox::Ok) {
QString info;
if (VUtils::emptyDirectory(notebook, binPath, true)) {
info = tr("Successfully emptied recycle bin of notebook "
"<span style=\"%1\">%2</span>!")
.arg(g_config->c_dataTextStyle)
.arg(notebook->getName());
} else {
info = tr("Fail to empty recycle bin of notebook "
"<span style=\"%1\">%2</span>!")
.arg(g_config->c_dataTextStyle)
.arg(notebook->getName());
}
VUtils::showMessage(QMessageBox::Information,
tr("Information"),
info,
"",
QMessageBox::Ok,
QMessageBox::Ok,
this);
}
});
}
void VNotebookSelector::updateComboBox()
{
m_muted = true;
@ -479,18 +377,122 @@ void VNotebookSelector::popupListContextMenuRequested(QPoint p_pos)
QMenu menu(this);
menu.setToolTipsVisible(true);
menu.addAction(m_deleteNotebookAct);
QAction *deleteNotebookAct = new QAction(VIconUtils::menuDangerIcon(":/resources/icons/delete_notebook.svg"),
tr("&Delete"),
&menu);
deleteNotebookAct->setToolTip(tr("Delete current notebook"));
connect(deleteNotebookAct, SIGNAL(triggered(bool)),
this, SLOT(deleteNotebook()));
menu.addAction(deleteNotebookAct);
if (nb->isValid()) {
menu.addSeparator();
menu.addAction(m_recycleBinAct);
menu.addAction(m_emptyRecycleBinAct);
QAction *recycleBinAct = new QAction(VIconUtils::menuIcon(":/resources/icons/recycle_bin.svg"),
tr("&Recycle Bin"),
&menu);
recycleBinAct->setToolTip(tr("Open the recycle bin of this notebook"));
connect(recycleBinAct, &QAction::triggered,
this, [this]() {
QList<QListWidgetItem *> items = this->m_listWidget->selectedItems();
if (items.isEmpty()) {
return;
}
Q_ASSERT(items.size() == 1);
VNotebook *notebook = getNotebook(items[0]);
QUrl url = QUrl::fromLocalFile(notebook->getRecycleBinFolderPath());
QDesktopServices::openUrl(url);
});
menu.addAction(recycleBinAct);
QAction *emptyRecycleBinAct = new QAction(VIconUtils::menuDangerIcon(":/resources/icons/empty_recycle_bin.svg"),
tr("&Empty Recycle Bin"),
&menu);
emptyRecycleBinAct->setToolTip(tr("Empty the recycle bin of this notebook"));
connect(emptyRecycleBinAct, &QAction::triggered,
this, [this]() {
QList<QListWidgetItem *> items = this->m_listWidget->selectedItems();
if (items.isEmpty()) {
return;
}
Q_ASSERT(items.size() == 1);
VNotebook *notebook = getNotebook(items[0]);
QString binPath = notebook->getRecycleBinFolderPath();
int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
tr("Are you sure to empty recycle bin of notebook "
"<span style=\"%1\">%2</span>?")
.arg(g_config->c_dataTextStyle)
.arg(notebook->getName()),
tr("<span style=\"%1\">WARNING</span>: "
"VNote will delete all the files in directory "
"<span style=\"%2\">%3</span>."
"<br>It may be UNRECOVERABLE!")
.arg(g_config->c_warningTextStyle)
.arg(g_config->c_dataTextStyle)
.arg(binPath),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok,
this,
MessageBoxType::Danger);
if (ret == QMessageBox::Ok) {
QString info;
if (VUtils::emptyDirectory(notebook, binPath, true)) {
info = tr("Successfully emptied recycle bin of notebook "
"<span style=\"%1\">%2</span>!")
.arg(g_config->c_dataTextStyle)
.arg(notebook->getName());
} else {
info = tr("Fail to empty recycle bin of notebook "
"<span style=\"%1\">%2</span>!")
.arg(g_config->c_dataTextStyle)
.arg(notebook->getName());
}
VUtils::showMessage(QMessageBox::Information,
tr("Information"),
info,
"",
QMessageBox::Ok,
QMessageBox::Ok,
this);
}
});
menu.addAction(emptyRecycleBinAct);
}
menu.addSeparator();
menu.addAction(m_openLocationAct);
QAction *openLocationAct = new QAction(VIconUtils::menuIcon(":/resources/icons/open_location.svg"),
tr("&Open Notebook Location"),
&menu);
openLocationAct->setToolTip(tr("Explore the root folder of this notebook in operating system"));
connect(openLocationAct, &QAction::triggered,
this, [this]() {
QList<QListWidgetItem *> items = this->m_listWidget->selectedItems();
if (items.isEmpty()) {
return;
}
Q_ASSERT(items.size() == 1);
VNotebook *notebook = getNotebook(items[0]);
QUrl url = QUrl::fromLocalFile(notebook->getPath());
QDesktopServices::openUrl(url);
});
menu.addAction(openLocationAct);
if (nb->isValid()) {
menu.addAction(m_notebookInfoAct);
QAction *notebookInfoAct = new QAction(VIconUtils::menuIcon(":/resources/icons/notebook_info.svg"),
tr("&Info"),
&menu);
notebookInfoAct->setToolTip(tr("View and edit current notebook's information"));
connect(notebookInfoAct, SIGNAL(triggered(bool)),
this, SLOT(editNotebookInfo()));
menu.addAction(notebookInfoAct);
}
menu.exec(m_listWidget->mapToGlobal(p_pos));

View File

@ -8,7 +8,6 @@
class VNotebook;
class QListWidget;
class QAction;
class QListWidgetItem;
class QLabel;
@ -66,8 +65,6 @@ private slots:
void editNotebookInfo();
private:
void initActions();
// Update Combox from m_notebooks.
void updateComboBox();
@ -115,13 +112,6 @@ private:
// Whether it is muted from currentIndexChanged().
bool m_muted;
// Actions
QAction *m_deleteNotebookAct;
QAction *m_notebookInfoAct;
QAction *m_openLocationAct;
QAction *m_recycleBinAct;
QAction *m_emptyRecycleBinAct;
QLabel *m_naviLabel;
};

View File

@ -25,36 +25,23 @@ extern VConfigManager *g_config;
VSearcher::VSearcher(QWidget *p_parent)
: QWidget(p_parent),
m_initialized(false),
m_uiInitialized(false),
m_inSearch(false),
m_askedToStop(false),
m_search(this)
{
qRegisterMetaType<QList<QSharedPointer<VSearchResultItem>>>("QList<QSharedPointer<VSearchResultItem>>");
setupUI();
initUIFields();
handleInputChanged();
connect(&m_search, &VSearch::resultItemAdded,
this, [this](const QSharedPointer<VSearchResultItem> &p_item) {
// Not sure if it works.
QCoreApplication::sendPostedEvents(NULL, QEvent::MouseButtonRelease);
m_results->addResultItem(p_item);
});
connect(&m_search, &VSearch::resultItemsAdded,
this, [this](const QList<QSharedPointer<VSearchResultItem> > &p_items) {
// Not sure if it works.
QCoreApplication::sendPostedEvents(NULL, QEvent::MouseButtonRelease);
m_results->addResultItems(p_items);
});
connect(&m_search, &VSearch::finished,
this, &VSearcher::handleSearchFinished);
}
void VSearcher::setupUI()
{
if (m_uiInitialized) {
return;
}
m_uiInitialized = true;
// Search button.
m_searchBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/search.svg"), "", this);
m_searchBtn->setToolTip(tr("Search"));
@ -112,6 +99,7 @@ void VSearcher::setupUI()
m_keywordCB->setLineEdit(new VLineEdit(this));
m_keywordCB->setToolTip(tr("Keywords to search for"));
m_keywordCB->lineEdit()->setPlaceholderText(tr("Supports space, &&, and ||"));
m_keywordCB->lineEdit()->setProperty("EmbeddedEdit", true);
connect(m_keywordCB, &QComboBox::currentTextChanged,
this, &VSearcher::handleInputChanged);
connect(m_keywordCB->lineEdit(), &QLineEdit::returnPressed,
@ -141,6 +129,7 @@ void VSearcher::setupUI()
m_filePatternCB->setEditable(true);
m_filePatternCB->setLineEdit(new VLineEdit(this));
m_filePatternCB->setToolTip(tr("Wildcard pattern to filter the files to be searched"));
m_filePatternCB->lineEdit()->setProperty("EmbeddedEdit", true);
m_filePatternCB->completer()->setCaseSensitivity(Qt::CaseSensitive);
// Engine.
@ -544,19 +533,62 @@ void VSearcher::updateNumLabel(int p_count)
void VSearcher::focusToSearch()
{
init();
m_keywordCB->setFocus(Qt::OtherFocusReason);
}
void VSearcher::showNavigation()
{
setupUI();
VNavigationMode::showNavigation(m_results);
}
bool VSearcher::handleKeyNavigation(int p_key, bool &p_succeed)
{
static bool secondKey = false;
setupUI();
return VNavigationMode::handleKeyNavigation(m_results,
secondKey,
p_key,
p_succeed);
}
void VSearcher::init()
{
if (m_initialized) {
return;
}
m_initialized = true;
setupUI();
initUIFields();
handleInputChanged();
connect(&m_search, &VSearch::resultItemAdded,
this, [this](const QSharedPointer<VSearchResultItem> &p_item) {
// Not sure if it works.
QCoreApplication::sendPostedEvents(NULL, QEvent::MouseButtonRelease);
m_results->addResultItem(p_item);
});
connect(&m_search, &VSearch::resultItemsAdded,
this, [this](const QList<QSharedPointer<VSearchResultItem> > &p_items) {
// Not sure if it works.
QCoreApplication::sendPostedEvents(NULL, QEvent::MouseButtonRelease);
m_results->addResultItems(p_items);
});
connect(&m_search, &VSearch::finished,
this, &VSearcher::handleSearchFinished);
}
void VSearcher::showEvent(QShowEvent *p_event)
{
init();
QWidget::showEvent(p_event);
}

View File

@ -15,6 +15,7 @@ class QLabel;
class VSearchResultTree;
class QProgressBar;
class QPlainTextEdit;
class QShowEvent;
class VSearcher : public QWidget, public VNavigationMode
{
@ -28,6 +29,9 @@ public:
void showNavigation() Q_DECL_OVERRIDE;
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
protected:
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
private slots:
void handleSearchFinished(const QSharedPointer<VSearchResult> &p_result);
@ -36,6 +40,8 @@ private:
void setupUI();
void init();
void initUIFields();
void setProgressVisible(bool p_visible);
@ -98,6 +104,10 @@ private:
QPlainTextEdit *m_consoleEdit;
bool m_initialized;
bool m_uiInitialized;
bool m_inSearch;
bool m_askedToStop;

View File

@ -86,7 +86,11 @@ int VToolBox::addItem(QWidget *p_widget,
void VToolBox::setCurrentIndex(int p_idx, bool p_focus)
{
if (p_idx < 0 || p_idx >= m_items.size()) {
m_currentIndex = -1;
if (m_items.isEmpty()) {
m_currentIndex = -1;
} else {
m_currentIndex = 0;
}
} else {
m_currentIndex = p_idx;
}

View File

@ -27,6 +27,8 @@ public:
void setCurrentWidget(QWidget *p_widget, bool p_focus = true);
int currentIndex() const;
// Implementations for VNavigationMode.
void showNavigation() Q_DECL_OVERRIDE;
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
@ -78,4 +80,9 @@ private:
QVector<ItemInfo> m_items;
};
inline int VToolBox::currentIndex() const
{
return m_currentIndex;
}
#endif // VTOOLBOX_H

View File

@ -53,6 +53,12 @@ VTreeWidget::VTreeWidget(QWidget *p_parent)
resizeColumnToContents(0);
}
});
connect(this, &VTreeWidget::itemCollapsed,
this, [this]() {
if (m_fitContent) {
resizeColumnToContents(0);
}
});
}
void VTreeWidget::keyPressEvent(QKeyEvent *p_event)