├── makespec ├── VERSION └── VERSIONSUFFIX ├── cmake ├── platforms │ ├── windows.cmake │ ├── macos.cmake │ └── linux.cmake ├── LemonDocs.cmake ├── LemonTranslations.cmake ├── lemon-base.cmake ├── lemon-core.cmake ├── lemon-ui.cmake └── deployment.cmake ├── unix ├── test │ ├── hello.sh │ ├── re.c │ ├── tle.c │ ├── hello.c │ ├── mle_static.c │ ├── add.c │ ├── scripts │ │ ├── runtimeerr.py │ │ ├── tle.py │ │ ├── mle_static.py │ │ ├── run.py │ │ ├── unlimit.py │ │ ├── run_sh.py │ │ ├── space.py │ │ ├── redirect.py │ │ ├── symlink_rel.py │ │ └── symlink_abs.py │ └── CMakeLists.txt ├── watcher.qrc └── watcher_linux.cpp ├── src ├── pch.h ├── core │ ├── judgesharedvariables.h │ ├── judgingcontroller.h │ ├── subtaskdependencelib.h │ ├── testcase.h │ ├── judgingcontroller.cpp │ ├── taskjudger.h │ ├── contestant.h │ ├── contest.h │ └── judgingthread.h ├── base │ ├── LemonUtils.cpp │ ├── LemonBaseApplication.hpp │ ├── LemonApplicationInterface.hpp │ ├── LemonApplicationInterface.cpp │ ├── LemonTranslator.hpp │ ├── LemonType.hpp │ ├── LemonConfig.hpp │ ├── LemonMacro.hpp │ ├── LemonBase.hpp │ ├── LemonTranslator.cpp │ ├── LemonConfig.cpp │ └── compiler.h ├── visualsettings.h ├── exttestcasemodifierdialog.h ├── themeeditdialog.h ├── optionsdialog.h ├── newcontestdialog.h ├── filelineedit.h ├── opencontestdialog.h ├── visualmainsettings.h ├── editvariabledialog.h ├── newcontestwidget.h ├── detaildialog.h ├── exttestcasemodifier.h ├── welcomedialog.h ├── environmentvariablesdialog.h ├── themeeditdialog.cpp ├── addtaskdialog.h ├── opencontestwidget.h ├── addcompilerwizard.h ├── resultviewer.h ├── exttestcasemodifierdialog.cpp ├── component │ └── exportutil │ │ └── exportutil.h ├── statisticsbrowser.h ├── compilersettings.h ├── newcontestdialog.cpp ├── forms │ ├── statisticsbrowser.ui │ ├── exttestcasemodifierdialog.ui │ ├── themeeditdialog.ui │ ├── newcontestdialog.ui │ ├── opencontestdialog.ui │ └── detaildialog.ui ├── generalsettings.h ├── testcaseeditwidget.h ├── exttestcasetable.h ├── advancedcompilersettingsdialog.h ├── optionsdialog.cpp ├── summarytree.h ├── editvariabledialog.cpp ├── judgingdialog.h ├── filelineedit.cpp ├── opencontestdialog.cpp ├── addtestcaseswizard.h ├── newcontestwidget.cpp ├── exttestcaseupdaterdialog.h ├── taskeditwidget.h ├── lemon.h ├── welcomedialog.cpp ├── main.cpp ├── visualsettings.cpp └── addtaskdialog.cpp ├── assets ├── lemon.rc ├── lemon-lime.ico ├── pics │ ├── splash.png │ ├── splash2.png │ ├── acrobat.svg │ ├── media-skip-forward.svg │ ├── go-down.svg │ ├── go-up.svg │ ├── list-remove.svg │ ├── media-playback-stop.svg │ ├── edit-delete.svg │ ├── paint-unknown.svg │ ├── application-exit.svg │ ├── list-add.svg │ ├── quickopen-file.svg │ ├── code-function.svg │ ├── dialog-ok-apply.svg │ ├── paint-none.svg │ ├── document-new.svg │ ├── help-about.svg │ ├── document-close.svg │ ├── layer-new.svg │ ├── document-export.svg │ ├── edit-clear.svg │ ├── edit-find.svg │ ├── document-save.svg │ ├── view-sort.svg │ ├── dialog-cancel.svg │ ├── go-parent-folder.svg │ ├── document-edit.svg │ ├── deletecell.svg │ ├── configure.svg │ ├── system-help.svg │ ├── view-refresh.svg │ └── edit-find-replace.svg ├── icons │ ├── lemon-lime.png │ ├── lemon-lime.128.png │ ├── lemon-lime.150.png │ ├── lemon-lime.16.png │ ├── lemon-lime.192.png │ ├── lemon-lime.22.png │ ├── lemon-lime.24.png │ ├── lemon-lime.256.png │ ├── lemon-lime.310.png │ ├── lemon-lime.32.png │ ├── lemon-lime.36.png │ ├── lemon-lime.44.png │ ├── lemon-lime.48.png │ ├── lemon-lime.512.png │ ├── lemon-lime.64.png │ ├── lemon-lime.72.png │ ├── lemon-lime.96.png │ └── lemon-lime.1024.png ├── x-lemon-contest.xml.in ├── lemon-lime.desktop.in ├── lemon-lime.metainfo.xml.in └── MacOSXInfo.plist.in ├── CONTRIBUTING.md ├── manual ├── llmanual.pdf ├── public │ ├── example.zip │ └── favicon.png ├── manual.qrc ├── src │ ├── content │ │ ├── docs │ │ │ ├── pics │ │ │ │ ├── icon.png │ │ │ │ ├── newcontest.png │ │ │ │ ├── statistics.png │ │ │ │ ├── addcompiler.png │ │ │ │ ├── addcompiler2.png │ │ │ │ ├── addtestcases.png │ │ │ │ ├── editproblem.png │ │ │ │ ├── edittestcase.png │ │ │ │ ├── judgingdialog.png │ │ │ │ ├── statistics2.png │ │ │ │ ├── autoaddproblem.png │ │ │ │ ├── generalsettings.png │ │ │ │ ├── visualsettings.png │ │ │ │ ├── compilersettings.png │ │ │ │ ├── compilersettings2.png │ │ │ │ ├── visualmainsettings.png │ │ │ │ ├── exttestcasemodifier.png │ │ │ │ ├── intoexttestcasemodifier.png │ │ │ │ └── visualmainsettings_whenchoosingtheme.png │ │ │ ├── index.mdx │ │ │ ├── install.typ │ │ │ ├── statistics.typ │ │ │ ├── quickstart.typ │ │ │ ├── issue.typ │ │ │ ├── judge.typ │ │ │ └── intro.typ │ │ ├── manual.typ │ │ └── html.typ │ └── content.config.ts ├── tsconfig.json ├── .gitignore ├── package.json ├── README.md └── astro.config.mjs ├── translations └── translations.qrc ├── hooks └── pre-commit ├── .gitattributes ├── .clang-format ├── .gitmodules ├── TODO ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── check_format.yml │ ├── deploy-manual.yml │ ├── sourcefile.yml │ ├── watcher_macos.yml │ ├── watcher_linux.yml │ ├── hashfile.yml │ ├── macos-qt6.yml │ ├── cpack-deb-debian.yml │ ├── build-manual.yml │ └── cpack-deb-ubuntu.yml.bak ├── AUTHORS ├── .gitignore └── resource.qrc /makespec/VERSION: -------------------------------------------------------------------------------- 1 | 0.3.6 -------------------------------------------------------------------------------- /makespec/VERSIONSUFFIX: -------------------------------------------------------------------------------- 1 | .1 -------------------------------------------------------------------------------- /cmake/platforms/windows.cmake: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /unix/test/hello.sh: -------------------------------------------------------------------------------- 1 | echo "Hello World!" -------------------------------------------------------------------------------- /src/pch.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include -------------------------------------------------------------------------------- /assets/lemon.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "lemon-lime.ico" 2 | -------------------------------------------------------------------------------- /unix/test/re.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int *x = (void *)0; 3 | return *x; 4 | } -------------------------------------------------------------------------------- /unix/test/tle.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | while (1) 3 | ; 4 | return 0; 5 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thw master branch is develop branch, 2 | x.y.z branch is the maintenance branch. 3 | -------------------------------------------------------------------------------- /assets/lemon-lime.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/lemon-lime.ico -------------------------------------------------------------------------------- /manual/llmanual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/llmanual.pdf -------------------------------------------------------------------------------- /assets/pics/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/pics/splash.png -------------------------------------------------------------------------------- /assets/pics/splash2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/pics/splash2.png -------------------------------------------------------------------------------- /unix/test/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | printf("Hello World!\n"); 5 | 6 | return 0; 7 | } -------------------------------------------------------------------------------- /manual/public/example.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/public/example.zip -------------------------------------------------------------------------------- /manual/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/public/favicon.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.128.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.150.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.16.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.192.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.22.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.24.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.256.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.310.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.32.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.36.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.44.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.48.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.512.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.64.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.72.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.96.png -------------------------------------------------------------------------------- /assets/icons/lemon-lime.1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/assets/icons/lemon-lime.1024.png -------------------------------------------------------------------------------- /manual/manual.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | llmanual.pdf 4 | 5 | 6 | -------------------------------------------------------------------------------- /unix/watcher.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | watcher_unix 4 | 5 | 6 | -------------------------------------------------------------------------------- /manual/src/content/docs/pics/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/icon.png -------------------------------------------------------------------------------- /manual/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "include": [".astro/types.d.ts", "**/*"], 4 | "exclude": ["dist"] 5 | } 6 | -------------------------------------------------------------------------------- /unix/test/mle_static.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int a[100000000]; 4 | // 400 MB 5 | 6 | int main() { 7 | puts("Hello World!"); 8 | return 0; 9 | } -------------------------------------------------------------------------------- /manual/src/content/docs/pics/newcontest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/newcontest.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/statistics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/statistics.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/addcompiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/addcompiler.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/addcompiler2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/addcompiler2.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/addtestcases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/addtestcases.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/editproblem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/editproblem.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/edittestcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/edittestcase.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/judgingdialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/judgingdialog.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/statistics2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/statistics2.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/autoaddproblem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/autoaddproblem.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/generalsettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/generalsettings.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/visualsettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/visualsettings.png -------------------------------------------------------------------------------- /unix/test/add.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int a, b; 5 | 6 | scanf("%d%d", &a, &b); 7 | printf("%d\n", a + b); 8 | 9 | return 0; 10 | } -------------------------------------------------------------------------------- /manual/src/content/docs/pics/compilersettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/compilersettings.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/compilersettings2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/compilersettings2.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/visualmainsettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/visualmainsettings.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/exttestcasemodifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/exttestcasemodifier.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/intoexttestcasemodifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/intoexttestcasemodifier.png -------------------------------------------------------------------------------- /manual/src/content/docs/pics/visualmainsettings_whenchoosingtheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/HEAD/manual/src/content/docs/pics/visualmainsettings_whenchoosingtheme.png -------------------------------------------------------------------------------- /translations/translations.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | zh_CN.qm 4 | zh_TW.qm 5 | en_US.qm 6 | 7 | 8 | -------------------------------------------------------------------------------- /hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Pre-commit hook - no longer increments build version 3 | # Build version now uses git commit hash instead 4 | echo 'Using git commit hash for build version - no manual increment needed' 5 | -------------------------------------------------------------------------------- /src/core/judgesharedvariables.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | extern bool skipEnabled; 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.txt text eof=lf 2 | *.cpp text eof=lf 3 | *.hpp text eof=lf 4 | *.h text eof=lf 5 | *.qrc text eof=lf 6 | *.cmake text eof=lf 7 | *.in text eof=lf 8 | *.ui text eof=lf 9 | *.md text eof=lf 10 | *.tex text eof=lf 11 | *.ts text eof=lf 12 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | BreakBeforeBraces: Attach 3 | IndentWidth: 4 4 | TabWidth: 4 5 | UseTab: ForIndentation 6 | ColumnLimit: 110 7 | 8 | ContinuationIndentWidth: 4 9 | IndentCaseLabels: true 10 | NamespaceIndentation: All 11 | SpaceAfterLogicalNot: true 12 | -------------------------------------------------------------------------------- /unix/test/scripts/runtimeerr.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | pid = os.getpid() 5 | tmperr = f"_tmperr_{pid}" 6 | 7 | p = subprocess.Popen(["./watcher_unix", "./re", "", "", "", tmperr, "1000", "100", "1000", "100", "", ""], shell=False) 8 | 9 | assert(p.wait() == 2) 10 | -------------------------------------------------------------------------------- /assets/x-lemon-contest.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Lemon Contest File 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /manual/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /assets/pics/acrobat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /cmake/LemonDocs.cmake: -------------------------------------------------------------------------------- 1 | # ================================================================================== 2 | # Lemon Documents 3 | # ================================================================================== 4 | 5 | if(EMBED_DOCS) 6 | add_definitions(-DLEMON_EMBED_DOCS) 7 | set(LEMON_EMBED_DOC_QRC ${CMAKE_SOURCE_DIR}/manual/manual.qrc) 8 | endif() 9 | -------------------------------------------------------------------------------- /unix/test/scripts/tle.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import time 3 | import os 4 | 5 | pid = os.getpid() 6 | tmpout = f"_tmpout_{pid}" 7 | tmperr = f"_tmperr_{pid}" 8 | 9 | p = subprocess.Popen(["./watcher_unix", "./tle", "", "", "", tmperr, "1000", "100", "1000", "100", "", ""], shell=False) 10 | 11 | time.sleep(5) 12 | p.kill() 13 | 14 | assert(p.returncode == 3) 15 | -------------------------------------------------------------------------------- /manual/src/content.config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection } from 'astro:content'; 2 | import { glob } from 'astro/loaders'; 3 | import { docsSchema } from '@astrojs/starlight/schema'; 4 | 5 | export const collections = { 6 | docs: defineCollection({ 7 | loader: glob({ pattern: "**/[^_]*.{typ,md,mdx}", base: "./src/content/docs" }), 8 | schema: docsSchema() 9 | }), 10 | }; -------------------------------------------------------------------------------- /unix/test/scripts/mle_static.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | pid = os.getpid() 5 | tmperr = f"_tmperr_{pid}" 6 | 7 | p = subprocess.Popen(["./watcher_unix", "./mle_static", "", "", "", tmperr, "1000", "380", "1000", "380", "", ""], shell=False, stdout=subprocess.PIPE) 8 | 9 | stdout, _ = p.communicate() 10 | 11 | assert(p.wait() == 4) 12 | assert(stdout[0] == 48) 13 | -------------------------------------------------------------------------------- /assets/pics/media-skip-forward.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/pics/go-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/pics/go-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/SingleApplication"] 2 | path = 3rdparty/SingleApplication 3 | url = https://github.com/itay-grudev/SingleApplication.git 4 | [submodule "assets/Testlib-for-Lemons"] 5 | path = assets/Testlib-for-Lemons 6 | url = https://github.com/GitPinkRabbit/Testlib-for-Lemons.git 7 | [submodule "3rdparty/spdlog"] 8 | path = 3rdparty/spdlog 9 | url = https://github.com/gabime/spdlog/ 10 | branch = v1.x 11 | -------------------------------------------------------------------------------- /assets/pics/list-remove.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /assets/pics/media-playback-stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /unix/test/scripts/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | pid = os.getpid() 5 | tmpout = f"_tmpout_{pid}" 6 | tmperr = f"_tmperr_{pid}" 7 | 8 | p = subprocess.Popen(["./watcher_unix", "./hello", "", "", tmpout, tmperr, "1000", "100", "1000", "100", "", ""], shell=False, stdout=subprocess.PIPE) 9 | 10 | assert(p.wait() == 0) 11 | assert(os.path.exists(tmpout)) 12 | with open(tmpout, 'r') as f: 13 | assert(f.read() == "Hello World!\n") 14 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Improve: 2 | * config: 3 | - refactor to store config in same format on different platform (#69) 4 | 5 | * judge: 6 | - rewrite (maybe a sandbox) (#64) 7 | - WSL support (#81) 8 | - Scorer Support 9 | 10 | * UI: 11 | - tui/cli support (#67) 12 | - QML GUI 13 | 14 | 15 | 16 | Bugfix: 17 | * platform dependent 18 | - crash on macOS Big Sur (#72) 19 | 20 | -------------------------------------------------------------------------------- /unix/test/scripts/unlimit.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | pid = os.getpid() 5 | tmpout = f"_tmpout_{pid}" 6 | tmperr = f"_tmperr_{pid}" 7 | 8 | p = subprocess.Popen(["./watcher_unix", "./mle_static", "", "", tmpout, tmperr, "1000", "-1", "1000", "-1", "", ""], shell=False, stdout=subprocess.PIPE) 9 | 10 | assert(p.wait() == 0) 11 | assert(os.path.exists(tmpout)) 12 | with open(tmpout, 'r') as f: 13 | assert(f.read() == "Hello World!\n") 14 | -------------------------------------------------------------------------------- /cmake/LemonTranslations.cmake: -------------------------------------------------------------------------------- 1 | # ================================================================================== 2 | # Lemon Translations 3 | # ================================================================================== 4 | 5 | find_package(${LEMON_QT_LIBNAME} COMPONENTS LinguistTools REQUIRED) 6 | set(TRANSLATIONS_DIR ${CMAKE_SOURCE_DIR}/translations) 7 | file(GLOB TRANSLATIONS_TS ${TRANSLATIONS_DIR}/**.ts) 8 | qt_add_translation(QM_FILES ${TRANSLATIONS_TS}) 9 | -------------------------------------------------------------------------------- /manual/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LemonLime Manual", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@astrojs/starlight": "^0.36.2", 14 | "astro": "^5.6.1", 15 | "astro-typst": "^0.12.1", 16 | "sharp": "^0.34.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /unix/test/scripts/run_sh.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | pid = os.getpid() 5 | tmpout = f"_tmpout_{pid}" 6 | tmperr = f"_tmperr_{pid}" 7 | 8 | p = subprocess.Popen(["./watcher_unix", "/bin/sh", "hello.sh", "", tmpout, tmperr, "1000", "100", "1000", "100", "", ""], shell=False, stdout=subprocess.PIPE) 9 | 10 | assert(p.wait() == 0) 11 | assert(os.path.exists(tmpout)) 12 | with open(tmpout, 'r') as f: 13 | assert(f.read() == "Hello World!\n") 14 | -------------------------------------------------------------------------------- /assets/pics/edit-delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /unix/test/scripts/space.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import shutil 3 | import os 4 | 5 | pid = os.getpid() 6 | tmpout = f"_tmpout_{pid}" 7 | tmperr = f"_tmperr_{pid}" 8 | 9 | shutil.copy("./hello", "./he llo") 10 | 11 | p = subprocess.Popen(["./watcher_unix", "./he llo", "", "", tmpout, tmperr, "1000", "100", "1000", "100", "", ""], shell=False, stdout=subprocess.PIPE) 12 | 13 | assert(p.wait() == 0) 14 | assert(os.path.exists(tmpout)) 15 | with open(tmpout, 'r') as f: 16 | assert(f.read() == "Hello World!\n") 17 | -------------------------------------------------------------------------------- /assets/pics/paint-unknown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /manual/src/content/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: LemonLime 用户手册 3 | description: 为了 OI 比赛而生的基于 Lemon + LemonPlus 的轻量评测系统 | 三大桌面系统支持 4 | hero: 5 | tagline: 为了 OI 比赛而生的基于 Lemon + LemonPlus 的轻量评测系统 | 三大桌面系统支持 6 | image: 7 | file: ./pics/icon.png 8 | actions: 9 | - text: 阅读用户手册 10 | link: /Project_LemonLime/quickstart 11 | icon: right-arrow 12 | - text: 下载最新版本 13 | link: https://github.com/Project-LemonLime/Project_LemonLime/releases 14 | icon: external 15 | variant: minimal 16 | --- 17 | -------------------------------------------------------------------------------- /cmake/platforms/macos.cmake: -------------------------------------------------------------------------------- 1 | set_target_properties(lemon 2 | PROPERTIES 3 | MACOSX_BUNDLE TRUE 4 | MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/assets/MacOSXInfo.plist.in" 5 | MACOSX_BUNDLE_BUNDLE_NAME "LemonLime" 6 | MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2019-2024 Project LemonLime" 7 | MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STRING} 8 | MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION_STRING} 9 | MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STRING} 10 | MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.lemonlime" 11 | ) 12 | -------------------------------------------------------------------------------- /assets/pics/application-exit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /assets/pics/list-add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /src/base/LemonUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #include "LemonUtils.hpp" 9 | // 10 | 11 | namespace Lemon::common { 12 | 13 | auto GetFileList(const QDir &dir) -> QStringList { 14 | return dir.entryList(QStringList{"*", "*.*"}, QDir::Hidden | QDir::Files); 15 | } 16 | 17 | auto FileExistsIn(const QDir &dir, const QString &fileName) -> bool { 18 | return GetFileList(dir).contains(fileName); 19 | } 20 | 21 | } // namespace Lemon::common 22 | -------------------------------------------------------------------------------- /assets/pics/quickopen-file.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /assets/pics/code-function.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /assets/pics/dialog-ok-apply.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /assets/pics/paint-none.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /unix/test/scripts/redirect.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import time 4 | 5 | pid = os.getpid() 6 | tmpin = f"_tmpin_{pid}" 7 | tmpout = f"_tmpout_{pid}" 8 | tmperr = f"_tmperr_{pid}" 9 | 10 | with open(tmpin, 'w') as f: 11 | f.writelines(['1 1']) 12 | 13 | p = subprocess.Popen(["./watcher_unix", "./add", "", tmpin, tmpout, tmperr, "1000", "100", "1000", "100", "", ""], shell=False) 14 | 15 | time.sleep(2) 16 | p.kill() 17 | 18 | assert(p.returncode == 0) 19 | 20 | assert(os.path.exists(tmpout)) 21 | with open(tmpout, 'r') as f: 22 | assert(f.read() == "2\n") 23 | -------------------------------------------------------------------------------- /unix/test/scripts/symlink_rel.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | pid = os.getpid() 5 | tmpout = f"_tmpout_{pid}" 6 | tmperr = f"_tmperr_{pid}" 7 | 8 | if os.path.exists("hello_s_rel"): 9 | os.remove("hello_s_rel") 10 | 11 | os.symlink("hello", "hello_s_rel") 12 | 13 | p = subprocess.Popen(["./watcher_unix", "./hello_s_rel", "", "", tmpout, tmperr, "1000", "100", "1000", "100", "", ""], shell=False, stdout=subprocess.PIPE) 14 | 15 | assert(p.wait() == 0) 16 | assert(os.path.exists(tmpout)) 17 | with open(tmpout, 'r') as f: 18 | assert(f.read() == "Hello World!\n") 19 | -------------------------------------------------------------------------------- /manual/src/content/docs/install.typ: -------------------------------------------------------------------------------- 1 | #import "../html.typ" : * 2 | #show : html-style 3 | #metadata(( title: "安装" )) 4 | 5 | = 安装 6 | 7 | LemonLime 在上文中提及的 #link("https://github.com/Project-LemonLime/Project_LemonLime")[仓库地址] 不仅用来存储源代码,也用来实时发布软件最新版本。 8 | 9 | == 支持的系统 10 | 11 | LemonLime 支持 Windows、Linux 以及 macOS。 12 | 13 | == 安装方法 14 | 15 | 你可以在 #link("https://github.com/Project-LemonLime/Project_LemonLime/releases")[发行页面] 下载已经编译好的对应系统版本。 16 | 17 | 如果你获得了源码,也可以自行编译 LemonLime,具体方法见 #link("https://github.com/Project-LemonLime/Project_LemonLime/blob/master/BUILD.md")[编译指南]。 18 | -------------------------------------------------------------------------------- /unix/test/scripts/symlink_abs.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | pid = os.getpid() 5 | tmpout = f"_tmpout_{pid}" 6 | tmperr = f"_tmperr_{pid}" 7 | 8 | if os.path.exists("hello_s_abs"): 9 | os.remove("hello_s_abs") 10 | 11 | os.symlink(os.path.join(os.getcwd(), "hello"), "hello_s_abs") 12 | 13 | p = subprocess.Popen(["./watcher_unix", "./hello_s_abs", "", "", tmpout, tmperr, "1000", "100", "1000", "100", "", ""], shell=False, stdout=subprocess.PIPE) 14 | 15 | assert(p.wait() == 0) 16 | assert(os.path.exists(tmpout)) 17 | with open(tmpout, 'r') as f: 18 | assert(f.read() == "Hello World!\n") 19 | -------------------------------------------------------------------------------- /assets/pics/document-new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /assets/pics/help-about.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /assets/pics/document-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /assets/pics/layer-new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /src/visualsettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | #include 12 | 13 | namespace Ui { 14 | class VisualSettings; 15 | } 16 | 17 | class ColorTheme; 18 | 19 | class VisualSettings : public QWidget { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit VisualSettings(QWidget *parent = nullptr); 24 | void resetColorTheme(ColorTheme *); 25 | void refresh(); 26 | ~VisualSettings(); 27 | 28 | private: 29 | Ui::VisualSettings *ui; 30 | ColorTheme *editColorTheme{}; 31 | 32 | private slots: 33 | void themeNameChanged(const QString &); 34 | }; 35 | -------------------------------------------------------------------------------- /src/exttestcasemodifierdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | #include 12 | 13 | namespace Ui { 14 | class ExtTestCaseModifierDialog; 15 | } 16 | 17 | class Task; 18 | class Settings; 19 | 20 | class ExtTestCaseModifierDialog : public QDialog { 21 | Q_OBJECT 22 | 23 | public: 24 | explicit ExtTestCaseModifierDialog(QWidget *parent = nullptr); 25 | ~ExtTestCaseModifierDialog(); 26 | 27 | void init(Task *, const Settings *); 28 | Task *getEditTask(); 29 | 30 | private: 31 | Ui::ExtTestCaseModifierDialog *ui; 32 | 33 | Task *editTask{}; 34 | }; 35 | -------------------------------------------------------------------------------- /assets/pics/document-export.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /manual/src/content/manual.typ: -------------------------------------------------------------------------------- 1 | #import "./lib.typ": * 2 | 3 | #show: project.with( 4 | title: "LemonLime 用户手册", 5 | author: "浮尘ii*, iotang, Coelacanthus", 6 | date: auto, 7 | ) 8 | 9 | #image("./docs/pics/icon.png", width: 30%) 10 | 11 | #align(center)[ 12 | 为了 OI 比赛而生的基于 Lemon + LemonPlus 的轻量评测系统 13 | 14 | 三大桌面系统支持 15 | ] 16 | 17 | #pagebreak() 18 | 19 | #outline() 20 | 21 | #pagebreak() 22 | 23 | #include "./docs/quickstart.typ" 24 | 25 | #include "./docs/intro.typ" 26 | 27 | #include "./docs/install.typ" 28 | 29 | #include "./docs/settings.typ" 30 | 31 | #include "./docs/contest.typ" 32 | 33 | #include "./docs/judge.typ" 34 | 35 | #include "./docs/statistics.typ" 36 | 37 | #include "./docs/issue.typ" 38 | -------------------------------------------------------------------------------- /assets/pics/edit-clear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project | 对这个项目提个建议 4 | title: "[Feature]" 5 | labels: 想要点新功能 | Feature Request 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | **你的功能请求是否与某些问题有关?请描述。** 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when \[...\] 对这些问题的清晰而简明的描述。比如:我想改进 \[…\] 13 | 14 | **Describe the solution you'd like** 15 | **描述您想要的解决方案** 16 | (Optional) A clear and concise description of what you want to happen. 清晰而简明地描述你想要的解决方案。(没有也可) 17 | 18 | **Additional context** 19 | **补充内容** 20 | Add any other context or screenshots about the feature request here. 如果,你还有想说的话…… 21 | -------------------------------------------------------------------------------- /assets/pics/edit-find.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /src/themeeditdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #ifndef THEMEEDITDIALOG_H 9 | #define THEMEEDITDIALOG_H 10 | 11 | #include 12 | 13 | namespace Ui { 14 | class ThemeEditDialog; 15 | } 16 | 17 | class ColorTheme; 18 | 19 | class ThemeEditDialog : public QDialog { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit ThemeEditDialog(QWidget *parent = nullptr); 24 | void resetEditTheme(ColorTheme *); 25 | ColorTheme *getEditTheme(); 26 | 27 | ~ThemeEditDialog(); 28 | 29 | private: 30 | Ui::ThemeEditDialog *ui; 31 | ColorTheme *editTheme{}; 32 | 33 | private slots: 34 | void okayButtonClicked(); 35 | }; 36 | 37 | #endif // THEMEEDITDIALOG_H 38 | -------------------------------------------------------------------------------- /manual/src/content/docs/statistics.typ: -------------------------------------------------------------------------------- 1 | #import "../html.typ" : * 2 | #show : html-style 3 | #metadata(( title: "比赛统计" )) 4 | 5 | = 比赛统计 6 | 7 | 点击"统计"选项卡即可查看这场比赛的统计信息。 8 | 9 | == 总览 10 | 11 | 这个部分显示了这场比赛的总体情况。 12 | 13 | #image("./pics/statistics.png", alt: "Statistics") 14 | 15 | 你可以获得的信息有: 16 | 17 | - 得到某个分数的选手人数以及比例,以及它的前缀和和后缀和。 18 | 19 | - 这场比赛的平均分、分数的标准差和区分度。 20 | 21 | 其中,区分度是一个衡量本场比赛或某个题目的区分度的值。这个值仅供娱乐。 22 | 23 | == 题目 24 | 25 | 每一个题目都有属于自己的一部分。这个部分显示了某个题目的情况。 26 | 27 | #image("./pics/statistics2.png", alt: "Statistics") 28 | 29 | 你可以获得的信息有: 30 | 31 | - 得到某个分数的选手人数以及比例,以及它的前缀和和后缀和。 32 | 33 | - 某个测试点的通过情况。 34 | 35 | - 这个题目的平均分、分数的标准差、区分度和提交人数。 36 | 37 | == 导出统计信息 38 | 39 | 你可以使用控制菜单栏中的 "导出统计信息" 来导出当前比赛的导出统计信息到一个 HTML 文件。 40 | 41 | 这个 HTML 文件的内容和 LemonLime 中的内容将完全一致。 -------------------------------------------------------------------------------- /assets/pics/document-save.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /assets/pics/view-sort.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/optionsdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | namespace Ui { 16 | class OptionsDialog; 17 | } 18 | 19 | class Settings; 20 | 21 | class OptionsDialog : public QDialog { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit OptionsDialog(QWidget *parent = nullptr); 26 | ~OptionsDialog(); 27 | void resetEditSettings(Settings *); 28 | Settings *getEditSettings(); 29 | 30 | private: 31 | Ui::OptionsDialog *ui; 32 | Settings *editSettings; 33 | 34 | private slots: 35 | void okayButtonClicked(); 36 | }; 37 | -------------------------------------------------------------------------------- /src/newcontestdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | 16 | namespace Ui { 17 | class NewContestDialog; 18 | } 19 | 20 | class NewContestDialog : public QDialog { 21 | Q_OBJECT 22 | 23 | public: 24 | explicit NewContestDialog(QWidget *parent = nullptr); 25 | ~NewContestDialog(); 26 | QString getContestTitle(); 27 | QString getSavingName(); 28 | QString getContestPath(); 29 | 30 | private: 31 | Ui::NewContestDialog *ui; 32 | 33 | private slots: 34 | void informationChanged(); 35 | }; 36 | -------------------------------------------------------------------------------- /assets/lemon-lime.desktop.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env xdg-open 2 | [Desktop Entry] 3 | Version=1.0 4 | Name=LemonLime 5 | Type=Application 6 | GenericName=A tiny judging environment for OI contest 7 | GenericName[zh_CN]=为了 OI 比赛而生的轻量评测系统 8 | GenericName[zh_TW]=為了 OI 比賽而生的輕量評測系統 9 | GenericName[ja]=OI競争のための軽量評価システム 10 | Comment=A tiny judging environment for OI contest based on Project_LemonPlus 11 | Comment[zh_CN]=为了 OI 比赛而生的基于 Lemon + LemonPlus 的轻量评测系统 12 | Comment[zh_TW]=為了 OI 比賽而生的基於 Lemon + LemonPlus 的輕量評測系統 13 | Comment[ja]=Lemon + LemonPlusに基づくOI競争用の軽量評価システム 14 | Keywords=OI;Qt;Judge; 15 | Keywords[zh_CN]=OI;Qt;评测; 16 | Keywords[zh_TW]=OI;Qt;評測; 17 | Keywords[ja]=OI;Qt;評価; 18 | Categories=Utility;Education;Competition; 19 | Terminal=false 20 | Path=/usr/bin 21 | Exec=lemon 22 | Icon=lemon-lime 23 | MimeType=application/x-lemon-contest; 24 | -------------------------------------------------------------------------------- /assets/pics/dialog-cancel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /src/base/LemonBaseApplication.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | #include 11 | // 12 | #include "base/LemonApplicationInterface.hpp" 13 | 14 | namespace Lemon { 15 | class LemonBaseApplication : public SingleApplication, public LemonApplicationInterface { 16 | Q_OBJECT 17 | 18 | public: 19 | LemonBaseApplication(int &argc, char *argv[]) 20 | : SingleApplication(argc, argv, true, User | ExcludeAppPath | ExcludeAppVersion), 21 | LemonApplicationInterface(){}; 22 | virtual ~LemonBaseApplication(){}; 23 | 24 | virtual bool Initialize() final; 25 | 26 | private: 27 | bool parseCommandLine(bool *canContinue, QString *errorMessage); 28 | }; 29 | } // namespace Lemon 30 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Current maintainer: 2 | * Coelacanthus 3 | * iotang <> 4 | 5 | Original author: 6 | * Zhipeng Jia 7 | 8 | Contributors: 9 | * iotang <> 10 | * Coelacanthus 11 | * Dust1404 <> 12 | * swift-zym (Yiming Zhang) <> 13 | * memset0 14 | * Alphagocc 15 | * Xeonacid <> 16 | 17 | Image: 18 | * files assets/icons/lemon-lime.png 19 | copyright: iotang <> 20 | license: GPLv3 21 | 22 | Translations authors: 23 | * files: translations/*.ts 24 | * file: assets/lemon-lime.desktop 25 | copyright: 26 | - Chinese (Simplified): iotang <> and Coelacanthus 27 | - Chinese (Traditional): Coelacanthus and KunoiSayami <> 28 | - English: iotang <> and Coelacanthus 29 | license: GPLv3 30 | -------------------------------------------------------------------------------- /src/filelineedit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | class FileLineEdit : public QLineEdit { 19 | Q_OBJECT 20 | public: 21 | explicit FileLineEdit(QWidget *parent = nullptr); 22 | void setFilters(QDir::Filters); 23 | void setFileExtensions(const QStringList &); 24 | void getFiles(const QString &, const QString &, QStringList &); 25 | 26 | private: 27 | QCompleter *completer; 28 | QStringList nameFilters; 29 | QDir::Filters filters; 30 | 31 | public slots: 32 | void refreshFileList(); 33 | }; 34 | -------------------------------------------------------------------------------- /src/opencontestdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace Ui { 18 | class OpenContestDialog; 19 | } 20 | 21 | class OpenContestDialog : public QDialog { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit OpenContestDialog(QWidget *parent = nullptr); 26 | ~OpenContestDialog(); 27 | void setRecentContest(const QStringList &); 28 | const QStringList &getRecentContest() const; 29 | QString getSelectedContest(); 30 | 31 | private: 32 | Ui::OpenContestDialog *ui; 33 | 34 | private slots: 35 | void selectionChanged(); 36 | }; 37 | -------------------------------------------------------------------------------- /src/visualmainsettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #ifndef VISUALMAINSETTINGS_H 9 | #define VISUALMAINSETTINGS_H 10 | 11 | #include 12 | 13 | namespace Ui { 14 | class VisualMainSettings; 15 | } 16 | 17 | class Settings; 18 | 19 | class VisualMainSettings : public QWidget { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit VisualMainSettings(QWidget *parent = nullptr); 24 | ~VisualMainSettings(); 25 | void resetEditSettings(Settings *); 26 | 27 | private: 28 | Ui::VisualMainSettings *ui; 29 | Settings *editSettings{}; 30 | 31 | private slots: 32 | void splashTimeChanged(const QString &); 33 | void themeChanged(const int); 34 | void whenThemeEdit(); 35 | void whenThemeAdd(); 36 | void whenThemeDelete(); 37 | }; 38 | 39 | #endif // VISUALMAINSETTINGS_H 40 | -------------------------------------------------------------------------------- /src/editvariabledialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | 16 | namespace Ui { 17 | class EditVariableDialog; 18 | } 19 | 20 | class EditVariableDialog : public QDialog { 21 | Q_OBJECT 22 | 23 | public: 24 | explicit EditVariableDialog(QWidget *parent = nullptr); 25 | ~EditVariableDialog(); 26 | void setVariableName(const QString &); 27 | void setVariableValue(const QString &); 28 | QString getVariableName() const; 29 | QString getVariableValue() const; 30 | 31 | private: 32 | Ui::EditVariableDialog *ui; 33 | 34 | private slots: 35 | void textChanged(); 36 | }; 37 | -------------------------------------------------------------------------------- /src/newcontestwidget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | namespace Ui { 16 | class NewContestWidget; 17 | } 18 | 19 | class NewContestWidget : public QWidget { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit NewContestWidget(QWidget *parent = nullptr); 24 | ~NewContestWidget(); 25 | QString getContestTitle(); 26 | QString getSavingName(); 27 | QString getContestPath(); 28 | bool checkReady() const; 29 | 30 | private: 31 | Ui::NewContestWidget *ui; 32 | 33 | signals: 34 | void informationChanged(); 35 | 36 | private slots: 37 | void selectContestPath(); 38 | void savingNameChanged(); 39 | }; 40 | -------------------------------------------------------------------------------- /src/core/judgingcontroller.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | #pragma once 8 | 9 | #include "base/LemonType.hpp" 10 | #include "base/settings.h" 11 | #include "taskjudger.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class JudgingController : public QObject { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit JudgingController(Settings *settings, QObject *parent = nullptr); 24 | void addTask(TaskJudger *judger); 25 | 26 | private: 27 | QQueue queuingTasks; 28 | QMap runningTasks; 29 | bool isJudging; 30 | int maxThreads; 31 | public slots: 32 | void stop(); 33 | void taskFinished(); 34 | void assign(); 35 | void start(); 36 | 37 | signals: 38 | void judgeFinished(); 39 | }; 40 | -------------------------------------------------------------------------------- /cmake/lemon-base.cmake: -------------------------------------------------------------------------------- 1 | # ================================================================================== 2 | # Lemon Base 3 | # ================================================================================== 4 | 5 | set(LEMON_BASEDIR_BASE ${CMAKE_SOURCE_DIR}/src/base) 6 | 7 | aux_source_directory(${LEMON_BASEDIR_BASE} LEMON_BASE_SOURCES) 8 | 9 | add_library(lemon-base STATIC 10 | ${LEMON_BASE_SOURCES} 11 | ${SINGLEAPPLICATION_SOURCES} 12 | ) 13 | 14 | target_precompile_headers(lemon-base PUBLIC ${CMAKE_SOURCE_DIR}/src/pch.h) 15 | 16 | target_link_libraries(lemon-base 17 | ${LEMON_QT_LIBS} 18 | ${SINGLEAPPLICATION_LIBRARY} 19 | ) 20 | 21 | target_include_directories(lemon-base PUBLIC 22 | ${CMAKE_BINARY_DIR} 23 | ${LEMON_BASEDIR_BASE} 24 | ${SINGLEAPPLICATION_DIR} 25 | ${spdlog_DIR}/include 26 | ${CMAKE_CURRENT_SOURCE_DIR} 27 | ${CMAKE_CURRENT_SOURCE_DIR}/src 28 | ) 29 | -------------------------------------------------------------------------------- /assets/pics/go-parent-folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /src/detaildialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | 16 | class Contestant; 17 | class Contest; 18 | 19 | namespace Ui { 20 | class DetailDialog; 21 | } 22 | 23 | class DetailDialog : public QDialog { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit DetailDialog(QWidget *parent = nullptr); 28 | ~DetailDialog(); 29 | void refreshViewer(Contest *, Contestant *); 30 | void showDialog(); 31 | 32 | private: 33 | Ui::DetailDialog *ui; 34 | Contest *contest{}; 35 | Contestant *contestant{}; 36 | 37 | private slots: 38 | void anchorClicked(const QUrl &); 39 | 40 | signals: 41 | void rejudgeSignal(); 42 | }; 43 | -------------------------------------------------------------------------------- /assets/pics/document-edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /cmake/lemon-core.cmake: -------------------------------------------------------------------------------- 1 | # ================================================================================== 2 | # Lemon Core 3 | # ================================================================================== 4 | 5 | set(LEMON_BASEDIR_CORE ${CMAKE_SOURCE_DIR}/src/core) 6 | 7 | aux_source_directory(${LEMON_BASEDIR_CORE} LEMON_CORE_SOURCES) 8 | 9 | add_library(lemon-core STATIC 10 | ${LEMON_CORE_SOURCES} 11 | ${SINGLEAPPLICATION_SOURCES} 12 | ) 13 | 14 | target_precompile_headers(lemon-core PUBLIC ${CMAKE_SOURCE_DIR}/src/pch.h) 15 | 16 | target_link_libraries(lemon-core 17 | lemon-base 18 | ${LEMON_QT_LIBS} 19 | ${SINGLEAPPLICATION_LIBRARY} 20 | ) 21 | 22 | target_include_directories(lemon-core PUBLIC 23 | ${CMAKE_BINARY_DIR} 24 | ${LEMON_BASEDIR_CORE} 25 | ${SINGLEAPPLICATION_DIR} 26 | ${spdlog_DIR}/include 27 | ${CMAKE_CURRENT_SOURCE_DIR} 28 | ${CMAKE_CURRENT_SOURCE_DIR}/src 29 | ) 30 | -------------------------------------------------------------------------------- /src/base/LemonApplicationInterface.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | #include 11 | #include 12 | #include 13 | 14 | namespace Lemon { 15 | struct LemonStartupArguments { 16 | enum Argument { 17 | NORMAL = 0, 18 | EXIT = 1, 19 | LEMON_LINK = 2 // Maybe support URL... 20 | }; 21 | QList arguments; 22 | QString version; 23 | int buildVersion; 24 | QString data; 25 | QList links; 26 | QList fullArgs; 27 | // 28 | bool debugLog; 29 | bool exitLemon; 30 | }; 31 | 32 | class LemonApplicationInterface { 33 | 34 | public: 35 | explicit LemonApplicationInterface(); 36 | ~LemonApplicationInterface(); 37 | 38 | Lemon::LemonStartupArguments StartupArguments; 39 | }; 40 | 41 | inline LemonApplicationInterface *LemonCoreApplication = nullptr; 42 | } // namespace Lemon 43 | -------------------------------------------------------------------------------- /assets/pics/deletecell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /src/exttestcasemodifier.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | #include 12 | 13 | namespace Ui { 14 | class ExtTestCaseModifier; 15 | } 16 | 17 | class Task; 18 | class Settings; 19 | 20 | class ExtTestCaseModifier : public QWidget { 21 | Q_OBJECT 22 | 23 | public: 24 | explicit ExtTestCaseModifier(QWidget *parent = nullptr); 25 | ~ExtTestCaseModifier(); 26 | 27 | void refresh(); 28 | void init(Task *, const Settings *); 29 | 30 | private: 31 | Ui::ExtTestCaseModifier *ui; 32 | 33 | Task *editTask{}; 34 | const Settings *editSettings{}; 35 | 36 | private slots: 37 | void whenTestCaseSelectionChanged(); 38 | 39 | void modifySelected(); 40 | void moveUpSelected(); 41 | void moveDownSelected(); 42 | void removeSelected(); 43 | void mergeSelected(); 44 | void splitSelected(); 45 | 46 | void appendNewSub(); 47 | void appendNewCase(); 48 | }; 49 | -------------------------------------------------------------------------------- /src/welcomedialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | namespace Ui { 16 | class WelcomeDialog; 17 | } 18 | 19 | class WelcomeDialog : public QDialog { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit WelcomeDialog(QWidget *parent = nullptr); 24 | ~WelcomeDialog(); 25 | void setRecentContest(const QStringList &); 26 | QString getContestTitle(); 27 | QString getSavingName(); 28 | QString getContestPath(); 29 | const QStringList &getRecentContest() const; 30 | QString getSelectedContest(); 31 | int getCurrentTab() const; 32 | 33 | private: 34 | Ui::WelcomeDialog *ui; 35 | 36 | private slots: 37 | void selectionChanged(); 38 | void informationChanged(); 39 | void tabIndexChanged(int); 40 | }; 41 | -------------------------------------------------------------------------------- /src/environmentvariablesdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | 16 | namespace Ui { 17 | class EnvironmentVariablesDialog; 18 | } 19 | 20 | class EnvironmentVariablesDialog : public QDialog { 21 | Q_OBJECT 22 | 23 | public: 24 | explicit EnvironmentVariablesDialog(QWidget *parent = nullptr); 25 | ~EnvironmentVariablesDialog(); 26 | void setProcessEnvironment(const QProcessEnvironment &); 27 | QProcessEnvironment getProcessEnvironment() const; 28 | 29 | private: 30 | Ui::EnvironmentVariablesDialog *ui; 31 | 32 | private slots: 33 | void addButtonClicked(); 34 | void editButtonClicked(); 35 | void deleteButtonClicked(); 36 | void viewerSelectionChanged(); 37 | }; 38 | -------------------------------------------------------------------------------- /src/themeeditdialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #include "themeeditdialog.h" 9 | #include "settings.h" 10 | #include "ui_themeeditdialog.h" 11 | #include "visualsettings.h" 12 | #include 13 | 14 | ThemeEditDialog::ThemeEditDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ThemeEditDialog) { 15 | ui->setupUi(this); 16 | editTheme = nullptr; 17 | connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, 18 | &ThemeEditDialog::okayButtonClicked); 19 | } 20 | 21 | ThemeEditDialog::~ThemeEditDialog() { delete ui; } 22 | 23 | void ThemeEditDialog::resetEditTheme(ColorTheme *colorTheme) { 24 | editTheme = colorTheme; 25 | ui->ThemeEdit->resetColorTheme(editTheme); 26 | } 27 | 28 | ColorTheme *ThemeEditDialog::getEditTheme() { return editTheme; } 29 | 30 | void ThemeEditDialog::okayButtonClicked() { 31 | ui->ThemeEdit->refresh(); 32 | accept(); 33 | } 34 | -------------------------------------------------------------------------------- /src/addtaskdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | namespace Ui { 16 | class AddTaskDialog; 17 | } 18 | 19 | class AddTaskDialog : public QDialog { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit AddTaskDialog(QWidget *parent = nullptr); 24 | ~AddTaskDialog(); 25 | void addTask(const QString &, int, int, int); 26 | int getFullScore(int) const; 27 | int getTimeLimit(int) const; 28 | int getMemoryLimit(int) const; 29 | 30 | private: 31 | Ui::AddTaskDialog *ui; 32 | QList fullScore; 33 | QList timeLimit; 34 | QList memoryLimit; 35 | 36 | private slots: 37 | void taskBoxIndexChanged(); 38 | void fullScoreChanged(); 39 | void timeLimitChanged(); 40 | void memoryLimitChanged(); 41 | }; 42 | -------------------------------------------------------------------------------- /.github/workflows/check_format.yml: -------------------------------------------------------------------------------- 1 | name: "Check Clang Format" 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | format: 7 | name: "Check Clang Format" 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: "Install clang-format" 12 | run: | 13 | sudo apt-get update 14 | sudo apt-get install clang-format 15 | - name: "Format Codes" 16 | run: clang-format -i src/*.cpp src/*.h src/**/*.cpp src/**/*.h src/**/**/*.cpp src/**/**/*.h src/**/*.hpp 17 | - name: Check diff 18 | run: git diff --exit-code HEAD 19 | - name: Create Pull Request 20 | if: failure() 21 | uses: peter-evans/create-pull-request@v8 22 | with: 23 | commit-message: "style: format codes" 24 | title: "Format codes for ${{ github.ref }}" 25 | labels: "style" 26 | assignees: "${{ github.actor }}" 27 | reviewers: "${{ github.actor }}" 28 | branch: "auto-pr/clang-format/${{ github.ref }}" 29 | -------------------------------------------------------------------------------- /src/core/subtaskdependencelib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | #include "base/LemonType.hpp" 12 | #include 13 | #include 14 | 15 | const int maxDependValue = 1000000; 16 | 17 | inline int stateToStatus(ResultState in, int score, int maxScore) { 18 | if (in == CorrectAnswer) 19 | return maxDependValue; 20 | 21 | if (in == PartlyCorrect && maxScore == 0) 22 | return maxDependValue; 23 | 24 | if (score <= 0) 25 | return -1; 26 | 27 | return 1ll * maxDependValue * score / maxScore; 28 | } 29 | 30 | inline int statusToScore(int ratio, int maxScore) { return 1ll * maxScore * ratio / maxDependValue; } 31 | 32 | inline QString statusRankingText(int ratio) { 33 | if (ratio >= maxDependValue) 34 | return QObject::tr("Pure"); 35 | 36 | if (ratio < 0) 37 | return QObject::tr("Lost"); 38 | 39 | return QString::number(100.00 * ratio / maxDependValue, 'f', 3) + "%"; 40 | } 41 | -------------------------------------------------------------------------------- /assets/pics/configure.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Qt 35 | moc_*.cpp 36 | moc_*.h 37 | ui_*.cpp 38 | ui_*.h 39 | qrc_resource.cpp 40 | lemon.pro.user 41 | 42 | # CMake 43 | .ninja_deps 44 | .ninja_log 45 | CMakeCache.txt 46 | CMakeFiles/ 47 | CMakeLists.txt.user 48 | build.ninja 49 | cmake_install.cmake 50 | lemon 51 | *_autogen/ 52 | watcher_unix 53 | CPackConfig.cmake 54 | CPackSourceConfig.cmake 55 | _CPack_Packages/ 56 | install_manifest.txt 57 | lemon-*-Linux.sh 58 | lemon-*-Linux.tar.Z 59 | lemon-*-Linux.tar.gz 60 | 61 | # KDevelop 62 | .kdev4/ 63 | *.kdev4 64 | .cache/ 65 | 66 | # Extra 67 | build/ 68 | build-debug/ 69 | .idea/ 70 | cmake-build-debug/ 71 | .vscode/ 72 | -------------------------------------------------------------------------------- /manual/src/content/docs/quickstart.typ: -------------------------------------------------------------------------------- 1 | #import "../html.typ" : * 2 | #show : html-style 3 | #metadata(( title: "快速开始" )) 4 | 5 | = 快速开始 6 | 7 | LemonLime 是为了 OI 比赛而生的基于 Lemon + LemonPlus 的轻量评测系统,具有三大桌面系统支持。 8 | 9 | 可以在 #link("https://github.com/Project-LemonLime/Project_LemonLime/releases/")[Github] 上下载最新版本。若因网络问题下载速度较慢,可以自行寻找镜像。 10 | 11 | 在 Windows 系统下可以解压压缩包,双击 `lemon.exe` 运行程序。 12 | 13 | 在 Linux 系统下可以在软件目录下通过终端运行 `chmod +x ./LemonLime-linux-...` 赋予权限,然后直接运行 `./LemonLime-linux-...` 启动。 14 | 15 | 遇到问题可查阅本手册「常见问题及回答」一节,同时请善用搜索引擎。 16 | 17 | 如还未配置编译器(如第一次运行)会弹出编译器配置弹窗。对于 C++,则需要选择 g++ 所在路径。如果 g++ 已经在环境变量中,一般不需要手动选择。建议勾选「添加建议配置」。 18 | 19 | 我们提供了一份 #link("https://Project-LemonLime.github.io/Project_LemonLime/example.zip")[样例],在 LemonLime 中将其打开后,点击左上角的 控制\>自动添加试题,即可看到一道默认的 `plus` 试题。 20 | 21 | #image("./pics/autoaddproblem.png", alt:"Auto Add Problem") 22 | 23 | 添加试题后,点击试题,框选右侧详情中的 定向到标准输入 与 定向到标准输出。切到 选手 页,点击右下角的 刷新,即可自动加载选手。点击 测试全部,即可自动运行全部选手的代码。代码的运行会在沙箱下进行。 24 | 25 | 你可以自由地探索 LemonLime,并尝试添加试题、选手。试题数据需要放在比赛文件夹下的 `data` 目录,选手源代码需要放在比赛文件夹下的 `source` 目录。 -------------------------------------------------------------------------------- /src/opencontestwidget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace Ui { 18 | class OpenContestWidget; 19 | } 20 | 21 | class OpenContestWidget : public QWidget { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit OpenContestWidget(QWidget *parent = nullptr); 26 | ~OpenContestWidget(); 27 | void setRecentContest(const QStringList &); 28 | const QStringList &getRecentContest() const; 29 | int getCurrentRow() const; 30 | 31 | private: 32 | Ui::OpenContestWidget *ui; 33 | QStringList recentContest; 34 | void refreshContestList(); 35 | 36 | private slots: 37 | void addContest(); 38 | void deleteContest(); 39 | void currentRowChanged(); 40 | 41 | signals: 42 | void selectionChanged(); 43 | void rowDoubleClicked(); 44 | }; 45 | -------------------------------------------------------------------------------- /manual/src/content/html.typ: -------------------------------------------------------------------------------- 1 | #let html-style(content) = { 2 | let jsx = s => html.elem("script", attrs: ("data-jsx": s)) 3 | 4 | jsx("import { Image } from 'astro:assets'") 5 | let image-num = state("image num", 0) 6 | show image: it => context { 7 | if target() == "html" { 8 | image-num.update(image-num => image-num + 1) 9 | jsx("import Img" + str(image-num.get()) + " from '" + it.source + "'") 10 | jsx("" + it.alt + "") 11 | } else { 12 | it 13 | } 14 | } 15 | 16 | show raw.where(block: true): it => { 17 | if target() == "html" { 18 | jsx("```" + it.lang + "\n" + it.text + "\n```") 19 | } else { 20 | it 21 | } 22 | } 23 | 24 | show math.equation: it => context { 25 | if target() == "html" { 26 | show: if it.block { it => it } else { box } 27 | html.frame(it) 28 | } else { 29 | it 30 | } 31 | } 32 | 33 | show heading.where(level: 1): it => context { 34 | if target() != "html" { 35 | it 36 | } 37 | } 38 | 39 | content 40 | } -------------------------------------------------------------------------------- /src/base/LemonApplicationInterface.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #include "LemonApplicationInterface.hpp" 9 | // 10 | #include "base/LemonBase.hpp" 11 | #include "base/LemonLog.hpp" 12 | #include "base/LemonTranslator.hpp" 13 | #include "base/settings.h" // TODO: Config Refactor 14 | // 15 | #include 16 | #include 17 | // 18 | #define LEMON_MODULE_NAME "LemonApplicationInterface" 19 | 20 | using namespace Lemon; 21 | 22 | LemonApplicationInterface::LemonApplicationInterface() { 23 | // ConfigObject = new LemonConfigObject; 24 | LemonCoreApplication = this; 25 | LOG("LemonLime", LEMON_VERSION_STRING, "on", QSysInfo::prettyProductName(), 26 | QSysInfo::currentCpuArchitecture()); 27 | DEBUG("LemonLime Start Time: ", QTime::currentTime().msecsSinceStartOfDay()); 28 | DEBUG(LEMON_BUILD_INFO); 29 | DEBUG(LEMON_BUILD_EXTRA_INFO); 30 | } 31 | 32 | LemonApplicationInterface::~LemonApplicationInterface() { 33 | // delete ConfigObject; 34 | LemonCoreApplication = nullptr; 35 | } 36 | -------------------------------------------------------------------------------- /src/addcompilerwizard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 3 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 4 | * 5 | * SPDX-License-Identifier: GPL-3.0-or-later 6 | * 7 | */ 8 | 9 | #pragma once 10 | // 11 | 12 | #include 13 | #include 14 | 15 | namespace Ui { 16 | class AddCompilerWizard; 17 | } 18 | 19 | class Compiler; 20 | 21 | class AddCompilerWizard : public QWizard { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit AddCompilerWizard(QWidget *parent = nullptr); 26 | ~AddCompilerWizard(); 27 | void accept(); 28 | const QList &getCompilerList() const; 29 | 30 | private: 31 | Ui::AddCompilerWizard *ui; 32 | QList compilerList; 33 | int nextId() const; 34 | bool validateCurrentPage(); 35 | 36 | private slots: 37 | void compilerTypeChanged(); 38 | void selectCompilerLocation(); 39 | void selectInterpreterLocation(); 40 | void selectGccPath(); 41 | void selectGppPath(); 42 | void selectFpcPath(); 43 | void selectFbcPath(); 44 | void selectJavacPath(); 45 | void selectJavaPath(); 46 | void selectPythonPath(); 47 | }; 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve | 汇报一个错误让工作人员处理 4 | title: "[BUG]" 5 | labels: 整叉劈了 | Bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | **描述一下这个 Bug** 12 | A clear and concise description of what the bug is. 清楚简明地描述这个 bug 是啥。 13 | 14 | **To Reproduce** 15 | **如何复现** 16 | Steps to reproduce the behavior: 像下面这样写出重现 Bug 的步骤: 17 | 1. Go to '...' 去 "…" 18 | 2. Click on '....' 点击 "…" 19 | 3. Scroll down to '....' 滚动到 "…" 20 | 4. See error 哦唷,崩溃啦! 21 | 22 | **Expected behavior** 23 | **期望获得的结果** 24 | A clear and concise description of what you expected to happen. 清楚简明地描述你认为程序应该做的事情。 25 | 26 | **Screenshots** 27 | **有截图吗?** 28 | If applicable, add screenshots to help explain your problem. 如果可以的话,附上屏幕截图来帮助表现 bug。 29 | 30 | **Environment:** 31 | **环境:** 32 | - Version: (e.g. 0.2.2) 版本代号:(比如 0.2.2) 33 | - OS: 系统: 34 | - \[ \] Windows (Which version? 哪个版本啊?) 35 | - \[ \] macOS 36 | - \[ \] Linux (Which edition and version? 哪个发行版和版本?) 37 | 38 | **Additional context** 39 | **其他信息** 40 | Add any other context about the problem here. 如果,你还有想说的话…… 41 | -------------------------------------------------------------------------------- /manual/README.md: -------------------------------------------------------------------------------- 1 | # LemonLime 用户手册 2 | 3 | 该文件夹储存 LemonLime 用户手册源代码。 4 | 5 | 用户手册使用 Typst 编写,源代码储存于 `manual/src/content` 目录中。 6 | 7 | 用户手册有两种分发模式:线上分发与线下分发。 8 | 9 | 线下分发需要手动编译 Typst 文件,在此之前,你需要下载以下字体: 10 | 11 | - [Noto Serif CJK SC](https://github.com/notofonts/noto-cjk/releases/tag/Serif2.003) 12 | - [Noto Sans CJK SC](https://github.com/notofonts/noto-cjk/releases/tag/Sans2.004) 13 | - [LXGW WenKai](https://github.com/lxgw/LxgwWenKai/releases) 14 | - [Courier Prime Code](https://quoteunquoteapps.com/courierprime/downloads/courier-prime-code.zip) 15 | 16 | 然后使用 Typst CLI 或者 Tinymist Typst 编译 `manual/src/content/manual.typ`,将生成出来的 pdf 重命名并放在 `manual/llmanual.pdf`。注意,需要开启 `--features html`。 17 | 18 | 如果只是小幅度的修改,也可以在网页端的 artifact 内获取由 CI 编译的 pdf 直接替换。 19 | 20 | 线上分发使用使用 astro+[astro-typst](https://github.com/OverflowCat/astro-typst)+[starlight theme](https://starlight.astro.build/) 部署于线上。 21 | 22 | `docs` 文件夹下的文件: 23 | 24 | - `manual.typ`:手册文件,include 了所有文章,供 pdf 导出使用; 25 | - `lib.typ`:手册文件的样式,供 pdf 导出使用; 26 | - `html.typ`:网页文件的一些 hook,劫持了数学公式、图片、标题(网页中不显示标题,因为已经有一个了),其中图片通过调用 JSX 组件 `` 实现,未来 astro-typst 的 API 可能会更换,需要及时更改。由于 `` 必须提供 `alt`,在文件中插入图片时必须包含 `alt`。 -------------------------------------------------------------------------------- /manual/astro.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { defineConfig } from 'astro/config'; 3 | import starlight from '@astrojs/starlight'; 4 | import { typst } from 'astro-typst'; 5 | 6 | // https://astro.build/config 7 | export default defineConfig({ 8 | site: 'https://project-lemonlime.github.io', 9 | base: 'Project_LemonLime', 10 | integrations: [ 11 | starlight({ 12 | title: 'LemonLime Manual', 13 | tableOfContents: false, 14 | favicon: '/favicon.png', 15 | social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/Project-LemonLime/Project_LemonLime' }], 16 | sidebar: [ 17 | { 18 | label: 'Manual', 19 | items: [ 20 | { label: '快速开始', slug: 'quickstart' }, 21 | { label: '简介', slug: 'intro' }, 22 | { label: '安装', slug: 'install' }, 23 | { label: '设置', slug: 'settings' }, 24 | { label: '比赛', slug: 'contest' }, 25 | { label: '测试', slug: 'judge' }, 26 | { label: '比赛统计', slug: 'statistics' }, 27 | { label: '常见问题及回答', slug: 'issue' }, 28 | ], 29 | }, 30 | ], 31 | }), 32 | typst({ 33 | options: { 34 | remPx: 14, 35 | }, 36 | target: "html", 37 | }), 38 | ], 39 | }); 40 | -------------------------------------------------------------------------------- /src/base/LemonTranslator.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2020 Qv2ray Development Group 3 | * 2020-2022 Project LemonLime 4 | * 5 | * SPDX-License-Identifier: GPL-3.0-or-later 6 | * 7 | */ 8 | 9 | #pragma once 10 | // 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace Lemon::common { 17 | class LemonTranslator { 18 | public: 19 | explicit LemonTranslator(); 20 | 21 | public: 22 | /** 23 | * @brief get the available languages. 24 | * @return (if available) languages (zh_CN, en_US, ...) 25 | */ 26 | const inline QStringList GetAvailableLanguages() const { return languages; } 27 | /** 28 | * @brief reload the translation from file 29 | * @param code eg: zh_CN, ... 30 | */ 31 | bool InstallTranslation(const QString &); 32 | 33 | private: 34 | QStringList languages; 35 | QStringList searchPaths; 36 | std::unique_ptr pTranslator; 37 | void refreshTranslations(); 38 | }; 39 | inline std::unique_ptr LemonLimeTranslator; 40 | } // namespace Lemon::common 41 | 42 | using namespace Lemon::common; 43 | -------------------------------------------------------------------------------- /.github/workflows/deploy-manual.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Manual 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: write 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v5 21 | with: 22 | fetch-depth: 0 23 | 24 | - uses: actions/setup-node@v4 25 | with: 26 | node-version: 24 27 | cache: 'npm' 28 | cache-dependency-path: manual/package-lock.json 29 | 30 | - name: Install dependencies 31 | run: npm ci 32 | working-directory: ./manual 33 | 34 | - name: Build 35 | run: npm run build 36 | working-directory: ./manual 37 | 38 | - name: Create nojekyll 39 | run: echo > ./manual/dist/.nojekyll 40 | 41 | - name: Deploy to gh-pages 42 | if: ${{ github.event_name == 'push' }} 43 | uses: peaceiris/actions-gh-pages@v4 44 | with: 45 | github_token: ${{ secrets.GITHUB_TOKEN }} 46 | publish_dir: ./manual/dist 47 | force_orphan: true 48 | -------------------------------------------------------------------------------- /src/resultviewer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | class Contest; 16 | 17 | class ResultViewer : public QTableWidget { 18 | Q_OBJECT 19 | public: 20 | explicit ResultViewer(QWidget *parent = nullptr); 21 | void changeEvent(QEvent *); 22 | void contextMenuEvent(QContextMenuEvent *); 23 | void setContest(Contest *); 24 | 25 | public slots: 26 | void refreshViewer(); 27 | void judgeSelected(); 28 | void judgeAll(); 29 | void judgeUnjudged(); 30 | void judgeGrey(); 31 | void judgeMagenta(); 32 | 33 | private: 34 | Contest *curContest; 35 | QAction *deleteContestantAction; 36 | QAction *detailInformationAction; 37 | QAction *judgeSelectedAction; 38 | QAction *deleteContestantKeyAction; 39 | void clearPath(const QString &); 40 | 41 | private slots: 42 | void deleteContestant(); 43 | void detailInformation(); 44 | 45 | signals: 46 | void contestantDeleted(); 47 | }; 48 | -------------------------------------------------------------------------------- /src/exttestcasemodifierdialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #include "exttestcasemodifierdialog.h" 9 | #include "ui_exttestcasemodifierdialog.h" 10 | // 11 | #include "core/task.h" 12 | // 13 | #include 14 | 15 | ExtTestCaseModifierDialog::ExtTestCaseModifierDialog(QWidget *parent) 16 | : QDialog(parent), ui(new Ui::ExtTestCaseModifierDialog) { 17 | ui->setupUi(this); 18 | setWindowTitle(QString(tr("Advanced Test Case Modifier"))); 19 | connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, 20 | &ExtTestCaseModifierDialog::accept); 21 | connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, 22 | &ExtTestCaseModifierDialog::reject); 23 | } 24 | 25 | ExtTestCaseModifierDialog::~ExtTestCaseModifierDialog() { delete ui; } 26 | 27 | void ExtTestCaseModifierDialog::init(Task *theTask, const Settings *theSettings) { 28 | editTask = new Task; 29 | 30 | theTask->copyTo(editTask); 31 | 32 | ui->widget->init(editTask, theSettings); 33 | } 34 | 35 | auto ExtTestCaseModifierDialog::getEditTask() -> Task * { return editTask; } 36 | -------------------------------------------------------------------------------- /manual/src/content/docs/issue.typ: -------------------------------------------------------------------------------- 1 | #import "../html.typ" : * 2 | #show : html-style 3 | #metadata(( title: "常见问题及回答" )) 4 | 5 | = 常见问题及回答 6 | 7 | 在使用中出现任何问题,可以在 #link("https://github.com/Project-LemonLime/Project_LemonLime")[Github 仓库] 中的 Issue 提出。 8 | 9 | 此节内容长期更新,更多问题欢迎提出。 10 | 11 | == Windows 无法启动 12 | 13 | 尝试安装同目录下的 `vc_redist.x64.exe` 解决。 14 | 15 | == Linux 无法启动 16 | 17 | 如果启动时出现: 18 | 19 | ```bash 20 | error while loading shared libraries: libxcb-cursor.so.0: cannot open shared object file: No such file or directory 21 | ``` 22 | 23 | 你需要执行以下命令安装必要的库: 24 | 25 | ```bash 26 | sudo apt update 27 | sudo apt upgrade -y 28 | sudo apt install libxcb-cursor0 29 | ``` 30 | 31 | 你可以 #link("https://mirrors.ustc.edu.cn/help/ubuntu.html")[换源] 来加速安装过程。 32 | 33 | == WSL 无法启动 34 | 35 | WSL2 的 Kernel 自带的运行库不全,不包含 bubblewrap,无法创建子进程。你需要执行以下命令安装必要的库: 36 | 37 | ```bash 38 | sudo apt install bubblewrap 39 | ``` 40 | 41 | == 测评时使用更多栈空间 42 | 43 | Windows 平台下可以在 `g++` 编译时添加 `-Wl,--stack=2147483647` 命令来开启约 2048 MB 栈空间。 44 | 45 | Linux 平台下的栈空间限制和题目的内存限制相同。 46 | 47 | == 第一个点 TLE 48 | 49 | 测评时请将杀毒软件关闭。 50 | 51 | == Mac 系统随机 TLE 52 | 53 | 详见 #link("https://github.com/Project-LemonLime/Project_LemonLime/issues/120")[issues/120]。 54 | -------------------------------------------------------------------------------- /src/base/LemonType.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | enum CompileState { 12 | CompileSuccessfully, 13 | NoValidSourceFile, 14 | CompileError, 15 | CompileTimeLimitExceeded, 16 | InvalidCompiler, 17 | NoValidGraderFile 18 | }; 19 | 20 | enum ResultState { 21 | CorrectAnswer, 22 | WrongAnswer, 23 | PartlyCorrect, 24 | TimeLimitExceeded, 25 | MemoryLimitExceeded, 26 | CannotStartProgram, 27 | FileError, 28 | RunTimeError, 29 | InvalidSpecialJudge, 30 | SpecialJudgeTimeLimitExceeded, 31 | SpecialJudgeRunTimeError, 32 | Skipped, 33 | InteractorError, 34 | PresentationError, 35 | OutputLimitExceeded, 36 | LastResultState 37 | }; 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | /* struct TaskResult { 44 | CompileState compileState; 45 | QString compileMessage; 46 | QString sourceFile; 47 | QList> resultState; 48 | QList resultMessage; 49 | QList> scores; 50 | QList> timeUsed; 51 | QList> memoryUsed; 52 | QList inputFiles; 53 | }; */ -------------------------------------------------------------------------------- /src/component/exportutil/exportutil.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * 2018-2019 Project LemonPlus, Dust1404 4 | * 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include "base/LemonType.hpp" 14 | #include 15 | 16 | #ifdef ENABLE_XLS_EXPORT 17 | #include 18 | #endif 19 | 20 | class Contest; 21 | class Contestant; 22 | 23 | class ExportUtil : public QObject { 24 | Q_OBJECT 25 | public: 26 | explicit ExportUtil(QObject *parent = nullptr); 27 | static void exportResult(QWidget *, Contest *); 28 | 29 | private: 30 | static QString getContestantHtmlCode(Contest *, Contestant *, int); 31 | static QString getSmallerContestantHtmlCode(Contest *, Contestant *); 32 | static void exportHtml(QWidget *, Contest *, const QString &); 33 | static void exportSmallerHtml(QWidget *, Contest *, const QString &); 34 | static void exportCsv(QWidget *, Contest *, const QString &); 35 | #ifdef ENABLE_XLS_EXPORT 36 | static void exportXls(QWidget *, Contest *, const QString &); 37 | #endif 38 | signals: 39 | 40 | public slots: 41 | }; 42 | -------------------------------------------------------------------------------- /assets/lemon-lime.metainfo.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.lemonlime.LemonLime 4 | GPL-3.0+ 5 | GPL-3.0+ 6 | LemonLime 7 | A tiny judging environment for OI contest 8 | 9 |

10 | A tiny judging environment for OI contest based on Project_LemonPlus and Lemon 11 |

12 |
13 | https://github.com/Project-LemonLime/Project_LemonLime 14 | https://github.com/Project-LemonLime/Project_LemonLime/issues 15 | 16 | Development 17 | Education 18 | Competition 19 | 20 | 21 | 22 | https://raw.githubusercontent.com/Project-LemonLime/Project_LemonLime/master/assets/icons/lemon-lime.png 23 | 24 | 25 | 26 | lemon 27 | 28 | 29 | 30 | 31 |
32 | -------------------------------------------------------------------------------- /assets/MacOSXInfo.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleInfoDictionaryVersion 8 | 6.0 9 | CFBundlePackageType 10 | APPL 11 | 12 | CFBundleName 13 | ${MACOSX_BUNDLE_BUNDLE_NAME} 14 | CFBundleExecutable 15 | ${MACOSX_BUNDLE_EXECUTABLE_NAME} 16 | NSHumanReadableCopyright 17 | ${MACOSX_BUNDLE_COPYRIGHT} 18 | 19 | CFBundleVersion 20 | ${MACOSX_BUNDLE_BUNDLE_VERSION} 21 | CFBundleLongVersionString 22 | ${MACOSX_BUNDLE_LONG_VERSION_STRING} 23 | CFBundleShortVersionString 24 | ${MACOSX_BUNDLE_SHORT_VERSION_STRING} 25 | 26 | CFBundleIdentifier 27 | ${MACOSX_BUNDLE_GUI_IDENTIFIER} 28 | 29 | 30 | NSPrincipalClass 31 | NSApplication 32 | NSHighResolutionCapable 33 | True 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/statisticsbrowser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | #include "base/LemonType.hpp" 12 | #include "core/contestant.h" 13 | #include "core/task.h" 14 | #include "core/testcase.h" 15 | #include 16 | 17 | namespace Ui { 18 | class StatisticsBrowser; 19 | } 20 | 21 | class Contest; 22 | class TestCase; 23 | 24 | static QString nowBrowserText; 25 | 26 | class StatisticsBrowser : public QWidget { 27 | Q_OBJECT 28 | 29 | public: 30 | explicit StatisticsBrowser(QWidget *parent = nullptr); 31 | void setContest(Contest *); 32 | static void exportStatistics(QWidget *, Contest *); 33 | ~StatisticsBrowser(); 34 | 35 | public slots: 36 | void refresh(); 37 | 38 | private: 39 | Ui::StatisticsBrowser *ui; 40 | Contest *curContest; 41 | static bool checkValid(QList, const QList &); 42 | static QString getScoreNormalChart(const QMap &, int, int); 43 | static QString getTestcaseScoreChart(QList, QList>>, 44 | QList>>); 45 | static void exportStatisticsHtml(QWidget *, const QString &); 46 | }; 47 | -------------------------------------------------------------------------------- /src/compilersettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | 16 | namespace Ui { 17 | class CompilerSettings; 18 | } 19 | 20 | class Settings; 21 | class Compiler; 22 | 23 | class CompilerSettings : public QWidget { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit CompilerSettings(QWidget *parent = nullptr); 28 | ~CompilerSettings(); 29 | void resetEditSettings(Settings *); 30 | bool checkValid(); 31 | 32 | private: 33 | Ui::CompilerSettings *ui; 34 | Settings *editSettings{}; 35 | Compiler *curCompiler{}; 36 | QAction *deleteCompilerKeyAction; 37 | void setCurrentCompiler(Compiler *); 38 | void refreshItemState(); 39 | 40 | private slots: 41 | void moveUpCompiler(); 42 | void moveDownCompiler(); 43 | void addCompiler(); 44 | void deleteCompiler(); 45 | void compilerNameChanged(const QString &); 46 | void sourceExtensionsChanged(const QString &); 47 | void compilerListCurrentRowChanged(); 48 | void advancedButtonClicked(); 49 | }; 50 | -------------------------------------------------------------------------------- /src/newcontestdialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "newcontestdialog.h" 11 | #include "ui_newcontestdialog.h" 12 | // 13 | #include 14 | 15 | NewContestDialog::NewContestDialog(QWidget *parent) : QDialog(parent), ui(new Ui::NewContestDialog) { 16 | ui->setupUi(this); 17 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 18 | connect(ui->newContestWidget, &NewContestWidget::informationChanged, this, 19 | &NewContestDialog::informationChanged); 20 | } 21 | 22 | NewContestDialog::~NewContestDialog() { delete ui; } 23 | 24 | auto NewContestDialog::getContestTitle() -> QString { return ui->newContestWidget->getContestTitle(); } 25 | 26 | auto NewContestDialog::getSavingName() -> QString { return ui->newContestWidget->getSavingName(); } 27 | 28 | auto NewContestDialog::getContestPath() -> QString { return ui->newContestWidget->getContestPath(); } 29 | 30 | void NewContestDialog::informationChanged() { 31 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ui->newContestWidget->checkReady()); 32 | } 33 | -------------------------------------------------------------------------------- /assets/pics/system-help.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /src/forms/statisticsbrowser.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | StatisticsBrowser 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 21 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 22 | p, li { white-space: pre-wrap; } 23 | </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> 24 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /cmake/platforms/linux.cmake: -------------------------------------------------------------------------------- 1 | install(TARGETS lemon RUNTIME DESTINATION bin) 2 | 3 | include(GNUInstallDirs) 4 | 5 | set(LEMON_LINUX_ICON_DIMENSIONS 16 22 24 32 36 44 48 64 72 96 128 150 192 256 310 512 1024) 6 | install(FILES assets/lemon-lime.metainfo.xml.in DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo" RENAME lemon-lime.metainfo.xml) 7 | install(FILES assets/x-lemon-contest.xml.in DESTINATION "${CMAKE_INSTALL_DATADIR}/mime/application" RENAME x-lemon-contest.xml) 8 | install(FILES assets/lemon-lime.desktop.in DESTINATION "${CMAKE_INSTALL_DATADIR}/applications" RENAME lemon-lime.desktop) 9 | #install(FILES assets/icons/lemon-lime.svg DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps") 10 | foreach(LEMON_LINUX_ICON_DIMENSION ${LEMON_LINUX_ICON_DIMENSIONS}) 11 | install(FILES assets/icons/lemon-lime.${LEMON_LINUX_ICON_DIMENSION}.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/${LEMON_LINUX_ICON_DIMENSION}x${LEMON_LINUX_ICON_DIMENSION}/apps" RENAME lemon-lime.png) 12 | endforeach(LEMON_LINUX_ICON_DIMENSION) 13 | if(NOT EMBED_TRANSLATIONS) 14 | install(FILES ${QM_FILES} DESTINATION "${CMAKE_INSTALL_DATADIR}/lemon-lime/lang") 15 | endif() 16 | if(NOT EMBED_DOCS) 17 | install(FILES manual/llmanual.pdf DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/lemon-lime") 18 | endif() 19 | install(FILES assets/Testlib-for-Lemons/testlib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} RENAME testlib_for_lemons.h) 20 | -------------------------------------------------------------------------------- /src/generalsettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | namespace Ui { 16 | class GeneralSettings; 17 | } 18 | 19 | class Settings; 20 | 21 | class GeneralSettings : public QWidget { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit GeneralSettings(QWidget *parent = nullptr); 26 | ~GeneralSettings(); 27 | void resetEditSettings(Settings *); 28 | bool checkValid(); 29 | 30 | private: 31 | Ui::GeneralSettings *ui; 32 | Settings *editSettings{}; 33 | 34 | private slots: 35 | void defaultFullScoreChanged(const QString &); 36 | void defaultTimeLimitChanged(const QString &); 37 | void defaultExtraTimeRatioChanged(const QString &); 38 | void defaultMemoryLimitChanged(const QString &); 39 | void compileTimeLimitChanged(const QString &); 40 | void specialJudgeTimeLimitChanged(const QString &); 41 | void fileSizeLimitChanged(const QString &); 42 | void rejudgeTimesChanged(const QString &); 43 | void maxJudgingThreadsChanged(const QString &); 44 | void inputFileExtensionsChanged(const QString &); 45 | void outputFileExtensionsChanged(const QString &); 46 | void onLanguageComboBoxChanged(const QString &); 47 | }; 48 | -------------------------------------------------------------------------------- /src/testcaseeditwidget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace Ui { 19 | class TestCaseEditWidget; 20 | } 21 | 22 | class Settings; 23 | class TestCase; 24 | 25 | class TestCaseEditWidget : public QWidget { 26 | Q_OBJECT 27 | 28 | public: 29 | explicit TestCaseEditWidget(QWidget *parent = nullptr); 30 | ~TestCaseEditWidget(); 31 | void changeEvent(QEvent *); 32 | void setEditTestCase(TestCase *, bool); 33 | void setSettings(Settings *); 34 | 35 | private: 36 | Ui::TestCaseEditWidget *ui; 37 | TestCase *editTestCase; 38 | Settings *settings{}; 39 | QAction *deleteAction; 40 | void refreshFileList(); 41 | 42 | private slots: 43 | void addSingleCase(); 44 | void deleteSingleCase(); 45 | void fullScoreChanged(const QString &); 46 | void timeLimitChanged(const QString &); 47 | void memoryLimitChanged(const QString &); 48 | void subtaskDependenceChanged(); 49 | void subtaskDependenceClear(); 50 | void fileListSelectionChanged(); 51 | void fileListItemChanged(QTableWidgetItem *); 52 | 53 | signals: 54 | void dataPathChanged(); 55 | }; 56 | -------------------------------------------------------------------------------- /src/exttestcasetable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | #include 12 | 13 | class Task; 14 | class TestCase; 15 | 16 | class ExtTestCaseTable : public QTableWidget { 17 | Q_OBJECT 18 | 19 | public: 20 | ExtTestCaseTable(QWidget *parent); 21 | 22 | void refreshTask(Task *); 23 | 24 | bool canModify() const; 25 | bool canAddSub() const; 26 | bool canAddCase() const; 27 | bool canRemove() const; 28 | bool canUp() const; 29 | bool canDown() const; 30 | bool canMerge() const; 31 | bool canSplit() const; 32 | 33 | QList getSelectedHaveSub() const; 34 | QList>> getSelectedResSub() const; 35 | std::pair getSelectRange() const; 36 | void modifySelected(int, int); 37 | 38 | private: 39 | Task *editTask{}; 40 | 41 | void addItem(int row, int column, const QString &text); 42 | void addItem(int row, int column, const QString &text, const QString &tipText); 43 | 44 | QList haveSub; 45 | QList>> resSub; 46 | 47 | bool isCanModify; 48 | bool isCanAddSub; 49 | bool isCanAddCase; 50 | bool isCanRemove; 51 | bool isCanUp; 52 | bool isCanDown; 53 | bool isCanMerge; 54 | bool isCanSplit; 55 | int selectMi{}; 56 | int selectMx{}; 57 | bool noDfs; 58 | 59 | private slots: 60 | void whenItemSelectionChanged(); 61 | 62 | signals: 63 | void testCaseSelectionChanged(); 64 | }; 65 | -------------------------------------------------------------------------------- /src/advancedcompilersettingsdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | class Compiler; 16 | 17 | namespace Ui { 18 | class AdvancedCompilerSettingsDialog; 19 | } 20 | 21 | class AdvancedCompilerSettingsDialog : public QDialog { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit AdvancedCompilerSettingsDialog(QWidget *parent = nullptr); 26 | ~AdvancedCompilerSettingsDialog(); 27 | void resetEditCompiler(Compiler *); 28 | Compiler *getEditCompiler() const; 29 | 30 | private: 31 | Ui::AdvancedCompilerSettingsDialog *ui; 32 | Compiler *editCompiler; 33 | int configCount{}; 34 | 35 | private slots: 36 | void okayButtonClicked(); 37 | void compilerTypeChanged(); 38 | void compilerLocationChanged(); 39 | void interpreterLocationChanged(); 40 | void selectCompilerLocation(); 41 | void selectInterpreterLocation(); 42 | void bytecodeExtensionsChanged(); 43 | void timeLimitRatioChanged(); 44 | void memoryLimitRatioChanged(); 45 | void disableMemoryLimitCheckChanged(); 46 | void interpreterAsWatcherCheckChanged(); 47 | void configurationIndexChanged(); 48 | void configurationTextChanged(); 49 | void deleteConfiguration(); 50 | void compilerArgumentsChanged(); 51 | void interpreterArgumentsChanged(); 52 | void environmentVariablesButtonClicked(); 53 | }; 54 | -------------------------------------------------------------------------------- /src/core/testcase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * 2018-2019 Project LemonPlus, Dust1404 4 | * 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | class TestCase { 16 | public: 17 | explicit TestCase(); 18 | int getFullScore() const; 19 | int getTimeLimit() const; 20 | int getMemoryLimit() const; 21 | const QStringList &getInputFiles() const; 22 | const QStringList &getOutputFiles() const; 23 | const QList &getDependenceSubtask() const; 24 | void setIndex(int); 25 | void setFullScore(int); 26 | void setTimeLimit(int); 27 | void setMemoryLimit(int); 28 | void setInputFiles(int, const QString &); 29 | void setOutputFiles(int, const QString &); 30 | void setDependenceSubtask(const QStringList &); 31 | void setDependenceSubtask(const QList &); 32 | void setDependenceSubtask(const QSet &); 33 | bool checkDependenceSubtask(const QStringList &) const; 34 | void addSingleCase(const QString &, const QString &); 35 | void deleteSingleCase(int); 36 | int writeToJson(QJsonObject &out); 37 | void readFromStream(QDataStream &); 38 | int readFromJson(const QJsonObject &); 39 | void clearDependenceSubtask(); 40 | void swapFiles(int, int); 41 | 42 | private: 43 | QStringList inputFiles; 44 | QStringList outputFiles; 45 | QList dependenceSubtask; 46 | int index{}; 47 | int fullScore{}; 48 | int timeLimit{}; 49 | int memoryLimit{}; 50 | }; 51 | -------------------------------------------------------------------------------- /src/optionsdialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "optionsdialog.h" 11 | #include "ui_optionsdialog.h" 12 | // 13 | #include "base/LemonLog.hpp" 14 | #include "base/settings.h" 15 | // 16 | #include 17 | #define LEMON_MODULE_NAME "OptionsDialog" 18 | 19 | OptionsDialog::OptionsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::OptionsDialog) { 20 | ui->setupUi(this); 21 | editSettings = new Settings(); 22 | connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, 23 | &OptionsDialog::okayButtonClicked); 24 | } 25 | 26 | OptionsDialog::~OptionsDialog() { delete ui; } 27 | 28 | auto OptionsDialog::getEditSettings() -> Settings * { return editSettings; } 29 | 30 | void OptionsDialog::resetEditSettings(Settings *settings) { 31 | editSettings->copyFrom(settings); 32 | ui->generalSettings->resetEditSettings(editSettings); 33 | ui->compilerSettings->resetEditSettings(editSettings); 34 | ui->visualMainSettings->resetEditSettings(editSettings); 35 | ui->tabWidget->setCurrentIndex(0); 36 | } 37 | 38 | void OptionsDialog::okayButtonClicked() { 39 | ui->tabWidget->setCurrentIndex(0); 40 | 41 | if (! ui->generalSettings->checkValid()) 42 | return; 43 | 44 | ui->tabWidget->setCurrentIndex(1); 45 | 46 | if (! ui->compilerSettings->checkValid()) 47 | return; 48 | 49 | accept(); 50 | } 51 | -------------------------------------------------------------------------------- /src/summarytree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class Settings; 20 | class Contest; 21 | 22 | class SummaryTree : public QTreeWidget { 23 | Q_OBJECT 24 | public: 25 | explicit SummaryTree(QWidget *parent = nullptr); 26 | void changeEvent(QEvent *) override; 27 | void setContest(Contest *); 28 | void setSettings(Settings *); 29 | void contextMenuEvent(QContextMenuEvent *) override; 30 | 31 | private: 32 | int addCount; 33 | Contest *curContest; 34 | Settings *settings{}; 35 | QAction *addTaskAction; 36 | QAction *addTestCaseAction; 37 | QAction *addTestCasesAction; 38 | QAction *addTaskKeyAction; 39 | QAction *addTestCaseKeyAction; 40 | QAction *deleteTaskAction; 41 | QAction *deleteTestCaseAction; 42 | QAction *deleteTaskKeyAction; 43 | QAction *deleteTestCaseKeyAction; 44 | QAction *ExtTestCaseModifierAction; 45 | 46 | private slots: 47 | void addTask(); 48 | void addTestCase(); 49 | void addTestCases(); 50 | void deleteTask(); 51 | void deleteTestCase(); 52 | void selectionChanged(); 53 | void itemChanged(QTreeWidgetItem *); 54 | void titleChanged(const QString &); 55 | void launchExtTestCaseModifier(); 56 | 57 | signals: 58 | void taskChanged(); 59 | }; 60 | -------------------------------------------------------------------------------- /.github/workflows/sourcefile.yml: -------------------------------------------------------------------------------- 1 | name: All Source Files 2 | 3 | on: 4 | push: 5 | release: 6 | types: [published, edited] 7 | 8 | jobs: 9 | linux: 10 | name: All Source Files 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Install git 15 | run: | 16 | sudo apt-get update 17 | sudo apt-get install -y git 18 | - name: Get the version 19 | id: get_version 20 | shell: bash 21 | run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3)" >> $GITHUB_OUTPUT 22 | - name: Checking out sources 23 | uses: actions/checkout@v4 24 | with: 25 | submodules: "recursive" 26 | - name: Create 7z Release 27 | uses: DuckSoft/create-7z-action@v1.0 28 | with: 29 | pathSource: ./ 30 | pathTarget: ./Lemon-${{ steps.get_version.outputs.VERSION }}-source-all.7z 31 | - name: Upload artifact 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: Lemon-${{ steps.get_version.outputs.VERSION }}-source-all.7z 35 | path: Lemon-${{ steps.get_version.outputs.VERSION }}-source-all.7z 36 | - name: Upload binaries to release 37 | uses: svenstaro/upload-release-action@v2 38 | if: startsWith(github.event.ref, 'refs/tags/') 39 | with: 40 | repo_token: ${{ secrets.GITHUB_TOKEN }} 41 | file: Lemon-${{ steps.get_version.outputs.VERSION }}-source-all.7z 42 | asset_name: Lemon-${{ steps.get_version.outputs.VERSION }}-source-all.7z 43 | tag: ${{ github.ref }} 44 | overwrite: true 45 | -------------------------------------------------------------------------------- /manual/src/content/docs/judge.typ: -------------------------------------------------------------------------------- 1 | #import "../html.typ" : * 2 | #show : html-style 3 | #metadata(( title: "测试" )) 4 | 5 | = 测试 6 | 7 | 点击"选手"选项卡,点击下面的"刷新"按钮后,就会根据 `source` 目录下的文件夹添加列表中不存在的选手,并从列表中删除已经从 `source` 目录中删除的选手。 8 | 9 | 然后单击 "测试全部" 按钮就能开始测试。注意选手提交的程序名在 Linux 下是大小写敏感的。 10 | 11 | 单击"测试未测试"按钮可以测试所有还没有测试的元素(即某个选手的某个题目)。 12 | 13 | 选中一些元素后按"测试选中"按钮可以仅测试选中的部分。如果选中了某选手的排名、名称、总分、总用时、测试时间的其中某一个,那么将默认测试这个选手的所有题目。 14 | 15 | 在控制菜单中还提供一键测试所有未找到文件的或者所有编译出错的记录。 16 | 17 | 按 `Delete` 键或右键点击删除可以删除选中的选手。 18 | 19 | 测试完成后通过在表头上单击可以按照相应的项目排序。 20 | 21 | 双击一名选手可以查看详细的测试结果并对该选手的某一题进行重测。 22 | 23 | 测试过程中会有 Subtask Skip,即对于一个测试点如果已经确定该测试点会得 0 分,则跳过这个测试点剩下的所有数据,可以大大减少捆绑测试的题目的测试时间。 24 | 25 | == 测试情况对话框 26 | 27 | #image("./pics/judgingdialog.png", alt: "Judging Dialog") 28 | 29 | 这个对话框会实时显示当前评测的情况。 30 | 31 | 各种评测结果的字体颜色各不相同。 32 | 33 | 如果选手在某一个测试点"答案正确"或者"答案部分正确",那么会显示它的得分、用时和所使用的空间。 34 | 35 | 进度条上会显示当前进度和总时间限制。 36 | 37 | == 颜色 38 | 39 | 不同的选手的不同的题目会根据得分和评测状态呈现出不同的颜色。 40 | 41 | 如果想改变颜色,可以到"设置"中改变。 42 | 43 | == 整理文件 44 | 45 | 为每个选手的每一个文件创建它的子文件夹内的文件和子文件夹外的文件,无论它们以前是在子文件夹内还是子文件夹外,并且删除大部分无用文件。 46 | 47 | 如果选手原本子文件夹内外都有文件,子文件夹外的那一个会被覆盖。 48 | 49 | 注意这个功能对于提交答案题的支持还不够完善。最好在整理文件之前备份一份,以防不测。 50 | 51 | == 多线程评测 52 | 53 | 在工具\>选项\>常规中,你可以调整最大评测线程数量,默认为单线程评测,这个功能仍在测试阶段,小心使用! 54 | 55 | == 导出成绩 56 | 57 | 在 "控制" 菜单中选择 "导出成绩" 可以将结果导出成 HTML 文档或表格文件。 58 | 59 | 推荐使用 HTML 格式,可以导出完整的结果信息,导出成表格的话只能导出选手总分和每道题的得分。 60 | 61 | 导出的表格为 csv 格式。csv 格式是逗号分隔符,多数表格编辑软件都能查看。 62 | 63 | 在导出为 HTML 格式的时候,后缀(`.html` 和 `.htm`)会影响内容。其中 `.html` 的内容会更加丰富,而 `.htm` 只提供基本内容,并且文件大小下降很多。 64 | 65 | 导出为 `.html` 的时候可以选择是以当前的颜色风格导出还是以默认的颜色风格导出。如果你使用的是暗色主题,可能需要额外注意这一点。 -------------------------------------------------------------------------------- /src/editvariabledialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "editvariabledialog.h" 11 | #include "ui_editvariabledialog.h" 12 | // 13 | #include 14 | 15 | EditVariableDialog::EditVariableDialog(QWidget *parent) : QDialog(parent), ui(new Ui::EditVariableDialog) { 16 | ui->setupUi(this); 17 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 18 | connect(ui->variableName, &QLineEdit::textChanged, this, &EditVariableDialog::textChanged); 19 | connect(ui->variableValue, &QLineEdit::textChanged, this, &EditVariableDialog::textChanged); 20 | } 21 | 22 | EditVariableDialog::~EditVariableDialog() { delete ui; } 23 | 24 | void EditVariableDialog::setVariableName(const QString &variable) { ui->variableName->setText(variable); } 25 | 26 | void EditVariableDialog::setVariableValue(const QString &value) { ui->variableValue->setText(value); } 27 | 28 | auto EditVariableDialog::getVariableName() const -> QString { return ui->variableName->text(); } 29 | 30 | auto EditVariableDialog::getVariableValue() const -> QString { return ui->variableValue->text(); } 31 | 32 | void EditVariableDialog::textChanged() { 33 | if (! ui->variableName->text().isEmpty() && ! ui->variableValue->text().isEmpty()) { 34 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); 35 | } else { 36 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/forms/exttestcasemodifierdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ExtTestCaseModifierDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 512 11 | 12 | 13 | 14 | 15 | 800 16 | 0 17 | 18 | 19 | 20 | Dialog 21 | 22 | 23 | 24 | :/icon/icon.png:/icon/icon.png 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 10 35 | 36 | 37 | 38 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ExtTestCaseModifier 47 | QWidget 48 |
exttestcasemodifier.h
49 | 1 50 |
51 |
52 | 53 | 54 |
55 | -------------------------------------------------------------------------------- /src/judgingdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include "base/LemonType.hpp" 14 | #include 15 | #include 16 | 17 | class Contest; 18 | 19 | namespace Ui { 20 | class JudgingDialog; 21 | } 22 | 23 | class JudgingDialog : public QDialog { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit JudgingDialog(QWidget *parent = nullptr); 28 | ~JudgingDialog(); 29 | void setContest(Contest *); 30 | void judge(const QList>> &); 31 | void judgeAll(); 32 | void reject(); 33 | 34 | private slots: 35 | void stopJudgingSlot(); 36 | static void skipJudging(); 37 | void sendNotify(QString, QString); 38 | 39 | private: 40 | Ui::JudgingDialog *ui; 41 | Contest *curContest{}; 42 | QTextCursor *cursor; 43 | bool stopJudging{}; 44 | 45 | public slots: 46 | void dialogAlert(const QString &); 47 | void singleCaseFinished(QString, int, int, int, int, int, int, int); 48 | void singleSubtaskDependenceFinished(int, int, int); 49 | void taskJudgingStarted(const QString &); 50 | void taskJudgedDisplay(const QString &, const QList> &, const int); 51 | void contestantJudgingStart(const QString &); 52 | void contestantJudgingFinished(); 53 | void contestantJudgedDisplay(const QString &, const int, const int); 54 | void compileError(int, int); 55 | 56 | signals: 57 | void stopJudgingSignal(); 58 | }; 59 | -------------------------------------------------------------------------------- /unix/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.0) 2 | 3 | project(lemon_watcher_unix_test) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_EXTENSIONS OFF) 8 | 9 | if(APPLE) 10 | add_executable(watcher_unix ${CMAKE_CURRENT_SOURCE_DIR}/../watcher_unix.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../watcher_macos.mm) 11 | else() 12 | add_executable(watcher_unix ${CMAKE_CURRENT_SOURCE_DIR}/../watcher_unix.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../watcher_linux.cpp) 13 | endif() 14 | 15 | add_executable(hello hello.c) 16 | add_executable(mle_static mle_static.c) 17 | file(COPY hello.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 18 | add_executable(tle tle.c) 19 | add_executable(add add.c) 20 | add_executable(re re.c) 21 | 22 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/scripts DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 23 | 24 | enable_testing() 25 | add_test(NAME watcher_run_c_test COMMAND python3 scripts/run.py) 26 | add_test(NAME watcher_run_sh_test COMMAND python3 scripts/run_sh.py) 27 | add_test(NAME watcher_MLE_static_test COMMAND python3 scripts/mle_static.py) 28 | add_test(NAME watcher_unlimit_memory_test COMMAND python3 scripts/unlimit.py) 29 | add_test(NAME watcher_TLE_test COMMAND python3 scripts/tle.py) 30 | add_test(NAME watcher_filename_with_space_test COMMAND python3 scripts/space.py) 31 | add_test(NAME watcher_symlink_abs_test COMMAND python3 scripts/symlink_abs.py) 32 | add_test(NAME watcher_symlink_rel_test COMMAND python3 scripts/symlink_rel.py) 33 | add_test(NAME watcher_redirect_IO_test COMMAND python3 scripts/redirect.py) 34 | add_test(NAME watcher_RE_test COMMAND python3 scripts/runtimeerr.py) 35 | -------------------------------------------------------------------------------- /assets/pics/view-refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/filelineedit.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "filelineedit.h" 11 | // 12 | #include "base/settings.h" 13 | 14 | FileLineEdit::FileLineEdit(QWidget *parent) : QLineEdit(parent) { completer = nullptr; } 15 | 16 | void FileLineEdit::getFiles(const QString &curDir, const QString &prefix, QStringList &files) { 17 | QDir dir(curDir); 18 | 19 | if (! nameFilters.isEmpty()) { 20 | dir.setNameFilters(nameFilters); 21 | } 22 | 23 | QStringList fileList = dir.entryList(filters); 24 | 25 | for (auto &f : fileList) 26 | f = prefix + f; 27 | 28 | files.append(fileList); 29 | 30 | // Get file in Child dir 31 | QStringList dirList = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); 32 | 33 | for (auto &&d : dirList) { 34 | getFiles(curDir + d + QDir::separator(), prefix + d + QDir::separator(), files); 35 | } 36 | } 37 | 38 | void FileLineEdit::setFilters(QDir::Filters _filters) { filters = _filters; } 39 | 40 | void FileLineEdit::setFileExtensions(const QStringList &extensions) { 41 | nameFilters.clear(); 42 | 43 | for (int i = 0; i < extensions.size(); i++) { 44 | nameFilters.append("*." + extensions[i]); 45 | } 46 | 47 | refreshFileList(); 48 | } 49 | 50 | void FileLineEdit::refreshFileList() { 51 | QStringList files; 52 | getFiles(Settings::dataPath(), "", files); 53 | delete completer; 54 | completer = new QCompleter(files, this); 55 | setCompleter(completer); 56 | } 57 | -------------------------------------------------------------------------------- /src/opencontestdialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "opencontestdialog.h" 11 | #include "ui_opencontestdialog.h" 12 | // 13 | #include 14 | 15 | OpenContestDialog::OpenContestDialog(QWidget *parent) : QDialog(parent), ui(new Ui::OpenContestDialog) { 16 | ui->setupUi(this); 17 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 18 | connect(ui->openContestWidget, &OpenContestWidget::selectionChanged, this, 19 | &OpenContestDialog::selectionChanged); 20 | connect(ui->openContestWidget, &OpenContestWidget::rowDoubleClicked, this, &OpenContestDialog::accept); 21 | } 22 | 23 | OpenContestDialog::~OpenContestDialog() { delete ui; } 24 | 25 | void OpenContestDialog::setRecentContest(const QStringList &list) { 26 | ui->openContestWidget->setRecentContest(list); 27 | } 28 | 29 | void OpenContestDialog::selectionChanged() { 30 | if (ui->openContestWidget->getCurrentRow() != -1) { 31 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); 32 | } else { 33 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 34 | } 35 | } 36 | 37 | auto OpenContestDialog::getRecentContest() const -> const QStringList & { 38 | return ui->openContestWidget->getRecentContest(); 39 | } 40 | 41 | auto OpenContestDialog::getSelectedContest() -> QString { 42 | return ui->openContestWidget->getRecentContest().at(ui->openContestWidget->getCurrentRow()); 43 | } 44 | -------------------------------------------------------------------------------- /src/base/LemonConfig.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | #include 12 | // 13 | #include 14 | 15 | class Compiler; 16 | 17 | namespace Lemon::base::config { 18 | 19 | class LemonConfigJudge { 20 | private: 21 | QList compilerList; 22 | int defaultFullScore{}; 23 | int defaultTimeLimit{}; 24 | int defaultMemoryLimit{}; 25 | int compileTimeLimit{}; 26 | int specialJudgeTimeLimit{}; 27 | int fileSizeLimit{}; 28 | int rejudgeTimes{}; 29 | int maxJudgingThreads{}; 30 | QString defaultInputFileExtension; 31 | QString defaultOutputFileExtension; 32 | QStringList inputFileExtensions; 33 | QStringList outputFileExtensions; 34 | QStringList recentContest; 35 | QString diffPath; 36 | 37 | public: 38 | int read(const QJsonObject &json); 39 | void write(QJsonObject &json) const; 40 | }; 41 | 42 | class LemonConfigUI { 43 | private: 44 | QString language = "en_US"; 45 | // Prepare for theme setting 46 | // QString theme = ; 47 | public: 48 | int read(const QJsonObject &json); 49 | void write(QJsonObject &json) const; 50 | }; 51 | 52 | class LemonConfig { 53 | private: 54 | LemonConfigJudge judgeConfig; 55 | LemonConfigUI uiConfig; 56 | int splashTime{}; 57 | 58 | public: 59 | enum SaveFormat { Json, Binary, Yaml }; 60 | void read(const QJsonObject &json); 61 | void write(QJsonObject &json) const; 62 | bool loadConfig(SaveFormat saveFormat); 63 | bool saveConfig(SaveFormat saveFormat) const; 64 | }; 65 | 66 | } // namespace Lemon::base::config 67 | -------------------------------------------------------------------------------- /.github/workflows/watcher_macos.yml: -------------------------------------------------------------------------------- 1 | name: Watcher for MacOS Test 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | paths: ["unix/**"] 7 | pull_request: 8 | branches: ["master"] 9 | paths: ["unix/**"] 10 | workflow_dispatch: 11 | 12 | env: 13 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 14 | BUILD_TYPE: Release 15 | 16 | jobs: 17 | build: 18 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 19 | # You can convert this to a matrix build if you need cross-platform coverage. 20 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 21 | runs-on: macos-13 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Configure CMake 27 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 28 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 29 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} unix/test 30 | 31 | - name: Build 32 | # Build your program with the given configuration 33 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 34 | 35 | - name: Test 36 | working-directory: ${{github.workspace}}/build 37 | # Execute tests defined by the CMake configuration. 38 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 39 | run: ctest --verbose -C ${{env.BUILD_TYPE}} 40 | -------------------------------------------------------------------------------- /.github/workflows/watcher_linux.yml: -------------------------------------------------------------------------------- 1 | name: Watcher for Linux Test 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | paths: ["unix/**"] 7 | pull_request: 8 | branches: ["master"] 9 | paths: ["unix/**"] 10 | workflow_dispatch: 11 | 12 | env: 13 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 14 | BUILD_TYPE: Release 15 | 16 | jobs: 17 | build: 18 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 19 | # You can convert this to a matrix build if you need cross-platform coverage. 20 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Configure CMake 27 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 28 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 29 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} unix/test 30 | 31 | - name: Build 32 | # Build your program with the given configuration 33 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 34 | 35 | - name: Test 36 | working-directory: ${{github.workspace}}/build 37 | # Execute tests defined by the CMake configuration. 38 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 39 | run: ctest --verbose -C ${{env.BUILD_TYPE}} 40 | -------------------------------------------------------------------------------- /.github/workflows/hashfile.yml: -------------------------------------------------------------------------------- 1 | name: Update release files hash 2 | 3 | on: 4 | release: 5 | types: [edited] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - run: echo "VERSION=\"$(echo $GITHUB_REF | cut -d / -f 3)\"" >> $GITHUB_ENV 13 | shell: bash 14 | - run: echo "REPOSITORY_NAME=\"$(echo \"$GITHUB_REPOSITORY\" | awk -F / '{print $2}')\"" >> $GITHUB_ENV 15 | shell: bash 16 | - name: Checking out sources 17 | uses: actions/checkout@v4 18 | with: 19 | ref: master 20 | - name: Hash File 21 | shell: bash 22 | run: | 23 | wget -O release.info https://api.github.com/repos/iotang/${REPOSITORY_NAME}/releases/tags/${VERSION} 24 | cat ./release.info | jq -r ".assets | .[] | .browser_download_url" > download.list 25 | cat ./release.info | jq -r ".assets | .[] | { uploader_id: .uploader.login, asset_name: .name }" > assets.info.json 26 | mkdir files 27 | cd files 28 | for x in $(cat ../download.list); do 29 | wget "$x"; 30 | done; 31 | rm assets.info.json || true 32 | rm sha256.list || true 33 | sha256sum ./* > ../sha256.list 34 | - name: Upload metadata to release 35 | uses: svenstaro/upload-release-action@v2 36 | with: 37 | repo_token: ${{ secrets.GITHUB_TOKEN }} 38 | file: assets.info.json 39 | asset_name: assets.info.json 40 | tag: ${{ github.ref }} 41 | overwrite: true 42 | - name: Upload metadata to release 43 | uses: svenstaro/upload-release-action@v2 44 | with: 45 | repo_token: ${{ secrets.GITHUB_TOKEN }} 46 | file: sha256.list 47 | asset_name: sha256.list 48 | tag: ${{ github.ref }} 49 | overwrite: true 50 | -------------------------------------------------------------------------------- /src/addtestcaseswizard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace Ui { 19 | class AddTestCasesWizard; 20 | } 21 | 22 | class Settings; 23 | 24 | class AddTestCasesWizard : public QWizard { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit AddTestCasesWizard(QWidget *parent = nullptr); 29 | ~AddTestCasesWizard(); 30 | void setSettings(Settings *, bool); 31 | int getFullScore() const; 32 | int getTimeLimit() const; 33 | int getMemoryLimit() const; 34 | const QList &getMatchedInputFiles() const; 35 | const QList &getMatchedOutputFiles() const; 36 | 37 | private: 38 | Ui::AddTestCasesWizard *ui; 39 | Settings *settings{}; 40 | int fullScore{}; 41 | int timeLimit{}; 42 | int memoryLimit{}; 43 | QString inputFilesPattern; 44 | QString outputFilesPattern; 45 | QList matchedInputFiles; 46 | QList matchedOutputFiles; 47 | void refreshButtonState(); 48 | void getFiles(const QString &, const QString &, QStringList &); 49 | QString getFullRegExp(const QString &); 50 | QStringList getMatchedPart(const QString &, const QString &); 51 | void searchMatchedFiles(); 52 | bool validateCurrentPage(); 53 | static bool compareFileName(const QString &, const QString &); 54 | 55 | private slots: 56 | void fullScoreChanged(const QString &); 57 | void timeLimitChanged(const QString &); 58 | void memoryLimitChanged(const QString &); 59 | void inputFilesPatternChanged(const QString &); 60 | void outputFilesPatternChanged(const QString &); 61 | void addArgument(); 62 | void deleteArgument(); 63 | }; 64 | -------------------------------------------------------------------------------- /assets/pics/edit-find-replace.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /cmake/lemon-ui.cmake: -------------------------------------------------------------------------------- 1 | # ================================================================================== 2 | # Lemon UI 3 | # ================================================================================== 4 | 5 | set(LEMON_BASEDIR_UI ${CMAKE_SOURCE_DIR}/src) 6 | 7 | aux_source_directory(${LEMON_BASEDIR_UI} LEMON_UI_SOURCES) 8 | 9 | list(APPEND LEMON_UI_SOURCES ${LEMON_BASEDIR_UI}/component/exportutil/exportutil.cpp) 10 | list(APPEND LEMON_UI_SOURCES ${LEMON_BASEDIR_UI}/component/exportutil/exportutil.h) 11 | 12 | set(LEMON_UI_FORMS 13 | ${LEMON_BASEDIR_UI}/forms/lemon.ui 14 | ${LEMON_BASEDIR_UI}/forms/exttestcasemodifierdialog.ui 15 | ${LEMON_BASEDIR_UI}/forms/exttestcasemodifier.ui 16 | ${LEMON_BASEDIR_UI}/forms/exttestcaseupdaterdialog.ui 17 | ${LEMON_BASEDIR_UI}/forms/taskeditwidget.ui 18 | ${LEMON_BASEDIR_UI}/forms/testcaseeditwidget.ui 19 | ${LEMON_BASEDIR_UI}/forms/generalsettings.ui 20 | ${LEMON_BASEDIR_UI}/forms/compilersettings.ui 21 | ${LEMON_BASEDIR_UI}/forms/addtestcaseswizard.ui 22 | ${LEMON_BASEDIR_UI}/forms/judgingdialog.ui 23 | ${LEMON_BASEDIR_UI}/forms/optionsdialog.ui 24 | ${LEMON_BASEDIR_UI}/forms/detaildialog.ui 25 | ${LEMON_BASEDIR_UI}/forms/newcontestwidget.ui 26 | ${LEMON_BASEDIR_UI}/forms/opencontestwidget.ui 27 | ${LEMON_BASEDIR_UI}/forms/newcontestdialog.ui 28 | ${LEMON_BASEDIR_UI}/forms/opencontestdialog.ui 29 | ${LEMON_BASEDIR_UI}/forms/visualmainsettings.ui 30 | ${LEMON_BASEDIR_UI}/forms/visualsettings.ui 31 | ${LEMON_BASEDIR_UI}/forms/themeeditdialog.ui 32 | ${LEMON_BASEDIR_UI}/forms/welcomedialog.ui 33 | ${LEMON_BASEDIR_UI}/forms/addtaskdialog.ui 34 | ${LEMON_BASEDIR_UI}/forms/advancedcompilersettingsdialog.ui 35 | ${LEMON_BASEDIR_UI}/forms/environmentvariablesdialog.ui 36 | ${LEMON_BASEDIR_UI}/forms/editvariabledialog.ui 37 | ${LEMON_BASEDIR_UI}/forms/addcompilerwizard.ui 38 | ${LEMON_BASEDIR_UI}/forms/statisticsbrowser.ui 39 | ) 40 | -------------------------------------------------------------------------------- /src/forms/themeeditdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ThemeEditDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Qt::Horizontal 24 | 25 | 26 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | VisualSettings 35 | QWidget 36 |
visualsettings.h
37 | 1 38 |
39 |
40 | 41 | 42 | 43 | buttonBox 44 | accepted() 45 | ThemeEditDialog 46 | accept() 47 | 48 | 49 | 248 50 | 254 51 | 52 | 53 | 157 54 | 274 55 | 56 | 57 | 58 | 59 | buttonBox 60 | rejected() 61 | ThemeEditDialog 62 | reject() 63 | 64 | 65 | 316 66 | 260 67 | 68 | 69 | 286 70 | 274 71 | 72 | 73 | 74 | 75 |
76 | -------------------------------------------------------------------------------- /src/newcontestwidget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "newcontestwidget.h" 11 | #include "ui_newcontestwidget.h" 12 | // 13 | #include 14 | 15 | NewContestWidget::NewContestWidget(QWidget *parent) : QWidget(parent), ui(new Ui::NewContestWidget) { 16 | ui->setupUi(this); 17 | connect(ui->selectButton, &QToolButton::clicked, this, &NewContestWidget::selectContestPath); 18 | connect(ui->savingName, &QLineEdit::textChanged, this, &NewContestWidget::savingNameChanged); 19 | connect(ui->contestTitle, &QLineEdit::textChanged, this, &NewContestWidget::informationChanged); 20 | connect(ui->savingName, &QLineEdit::textChanged, this, &NewContestWidget::informationChanged); 21 | connect(ui->contestPath, &QLineEdit::textChanged, this, &NewContestWidget::informationChanged); 22 | } 23 | 24 | NewContestWidget::~NewContestWidget() { delete ui; } 25 | 26 | auto NewContestWidget::getContestTitle() -> QString { return ui->contestTitle->text(); } 27 | 28 | auto NewContestWidget::getSavingName() -> QString { return ui->savingName->text(); } 29 | 30 | auto NewContestWidget::getContestPath() -> QString { return ui->contestPath->text(); } 31 | 32 | auto NewContestWidget::checkReady() const -> bool { 33 | return ! ui->contestTitle->text().isEmpty() && ! ui->contestPath->text().isEmpty() && 34 | ! ui->savingName->text().isEmpty(); 35 | } 36 | 37 | void NewContestWidget::selectContestPath() { 38 | QString path = QFileDialog::getExistingDirectory(this, tr("Select Contest Path"), QDir::homePath()); 39 | 40 | if (! path.isEmpty()) 41 | ui->contestPath->setText(QDir::toNativeSeparators(path)); 42 | } 43 | 44 | void NewContestWidget::savingNameChanged() { 45 | QString path = QDir::homePath(); 46 | path = QDir::toNativeSeparators(path); 47 | path += QDir::separator(); 48 | path += ui->savingName->text(); 49 | ui->contestPath->setText(path); 50 | } 51 | -------------------------------------------------------------------------------- /src/exttestcaseupdaterdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | 11 | #include 12 | 13 | namespace Ui { 14 | class ExtTestCaseUpdaterDialog; 15 | } 16 | 17 | class Task; 18 | class Settings; 19 | 20 | const int NO_EDIT = -400; 21 | const int MAY_EDIT = -300; 22 | const int EDIT_WITH_DEFAULT = -200; 23 | 24 | class ExtTestCaseUpdaterDialog : public QDialog { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit ExtTestCaseUpdaterDialog(QWidget *parent = nullptr, Task *nowTask = nullptr, 29 | const Settings *nowSettings = nullptr, int nowCaseNumber = 0, 30 | int editScore = NO_EDIT, int editData = NO_EDIT, int editTime = NO_EDIT, 31 | int editMemory = NO_EDIT, int editDepend = NO_EDIT, 32 | QList tempDepends = QList()); 33 | ~ExtTestCaseUpdaterDialog(); 34 | 35 | int getScore() const; 36 | QString getInput() const; 37 | QString getOutput() const; 38 | int getTimeLimit() const; 39 | int getMemoryLimit() const; 40 | QStringList getDepends() const; 41 | 42 | int checkDepends(); 43 | 44 | protected: 45 | virtual void accept() override; 46 | 47 | private: 48 | Ui::ExtTestCaseUpdaterDialog *ui; 49 | Task *nowTask; 50 | const Settings *nowSettings; 51 | int nowCaseNumber; 52 | int editScore; 53 | int editData; 54 | int editTime; 55 | int editMemory; 56 | int editDepend; 57 | 58 | int score; 59 | QString input, output; 60 | int timeLimit, memoryLimit; 61 | QStringList depends; 62 | 63 | int defScore, defTimeLimit, defMemoryLimit; 64 | 65 | private slots: 66 | 67 | void whenButtonFindInputClicked(); 68 | void whenButtonFindOutputClicked(); 69 | void fullScoreChanged(const QString &); 70 | void timeLimitChanged(const QString &); 71 | void memoryLimitChanged(const QString &); 72 | void inputFileChanged(const QString &); 73 | void outputFileChanged(const QString &); 74 | void dependsChanged(const QString &); 75 | }; 76 | -------------------------------------------------------------------------------- /src/core/judgingcontroller.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #include "judgingcontroller.h" 9 | #include "core/contestant.h" 10 | 11 | #include 12 | 13 | #define LEMON_MODULE_NAME "JudgingController" 14 | 15 | JudgingController::JudgingController(Settings *settings, QObject *parent) : QObject(parent) { 16 | isJudging = false; 17 | maxThreads = qMax(1, settings->getMaxJudgingThreads()); 18 | } 19 | 20 | void JudgingController::assign() { 21 | if (! isJudging) { 22 | return; 23 | } 24 | if (queuingTasks.empty()) 25 | return; 26 | QThread *thread = new QThread; 27 | auto *taskJudger = queuingTasks.front(); 28 | queuingTasks.pop_front(); 29 | taskJudger->moveToThread(thread); 30 | connect(taskJudger, &TaskJudger::judgingFinished, this, &JudgingController::taskFinished); 31 | runningTasks[taskJudger] = thread; 32 | thread->start(); 33 | QMetaObject::invokeMethod(taskJudger, &TaskJudger::judgeIt); 34 | } 35 | 36 | void JudgingController::taskFinished() { 37 | auto *taskJudger = qobject_cast(sender()); 38 | if (taskJudger == nullptr) { 39 | return; 40 | } 41 | if (runningTasks.count(taskJudger)) { 42 | auto *thread = runningTasks[taskJudger]; 43 | thread->quit(); 44 | thread->wait(); 45 | delete thread; 46 | runningTasks.remove(taskJudger); 47 | delete taskJudger; 48 | } 49 | assign(); 50 | if (runningTasks.empty()) { 51 | isJudging = false; 52 | emit judgeFinished(); 53 | } 54 | } 55 | void JudgingController::start() { 56 | if (queuingTasks.size() == 0) { 57 | emit judgeFinished(); 58 | return; 59 | } 60 | isJudging = true; 61 | while (! queuingTasks.empty() && runningTasks.size() < maxThreads) { 62 | assign(); 63 | } 64 | } 65 | void JudgingController::stop() { 66 | if (! isJudging) 67 | return; 68 | isJudging = false; 69 | for (auto [taskJudger, thread] : runningTasks.toStdMap()) { 70 | QMetaObject::invokeMethod(taskJudger, &TaskJudger::stop); 71 | } 72 | // emit judgeFinished(); 73 | } 74 | void JudgingController::addTask(TaskJudger *taskJudger) { queuingTasks.push_back(taskJudger); } 75 | -------------------------------------------------------------------------------- /cmake/deployment.cmake: -------------------------------------------------------------------------------- 1 | # Packaging 2 | set(CPACK_PACKAGE_NAME "LemonLime") 3 | set(CPACK_PACKAGE_VENDOR "Project LemonLime Developers") 4 | set(CPACK_PACKAGE_DESCRIPTION "A tiny judging environment for OI contest based on Lemon + LemonPlus.") 5 | set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/Project-LemonLime/Project_LemonLime") 6 | set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/assets/lemon-lime.ico") 7 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSES") 8 | 9 | SET(COMMON_IGNORE_FILES "/CMakeFiles/" "_CPack_Packages/" "/Testing/" 10 | ".cmake$" ".directory$" "CMakeCache.txt" 11 | "/.svn/" "/CVS/" "~$" ".swp$" ".log$" ".gz$" 12 | "/src/config.h$") 13 | 14 | SET(CPACK_PACKAGE_IGNORE_FILES ${PRJ_COMMON_IGNORE_FILES} ) 15 | 16 | if(UNIX) 17 | if(BUILD_DEB) 18 | set(CPACK_GENERATOR "DEB") 19 | set(CPACK_DEBIAN_PACKAGE_NAME "LemonLime") 20 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Project LemonLime Developers") 21 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "qt6-base-dev (>= 6.8), qt6-tools-dev, qt6-tools-dev-tools, libspdlog") 22 | endif() 23 | 24 | if(BUILD_RPM) 25 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/makespec/lemonlime.spec.in" "${CMAKE_CURRENT_BINARY_DIR}/lemonlime.spec" @ONLY IMMEDIATE) 26 | #set(CPACK_RPM_USER_BINARY_SPECFILE "${CMAKE_CURRENT_BINARY_DIR}/lemonlime.spec") 27 | set(CPACK_GENERATOR "RPM") 28 | set(CPACK_RPM_PACKAGE_NAME "LemonLime") 29 | set(CPACK_RPM_PACKAGE_VENDOR "Project LemonLime Developers") 30 | if(LSB_RELEASE_ID_SHORT EQUAL "openSUSE") 31 | set(CPACK_RPM_BUILDREQUIRES "cmake >= 3.9, libqt6-qtbase-common-devel >= 6.8, libQt6Core-devel, libQt6Gui-devel, libQt6Network-devel, libQt6Widgets-devel, libQt6Concurrent-devel, libqt6-linguist-devel, libqt6-qtsvg-devel, update-desktop-files, ninja, spdlog-devel") 32 | else() 33 | set(CPACK_RPM_BUILDREQUIRES "cmake >= 3.9, qt6-qtbase-devel >= 6.8, qt6-qttools-devel, qt6-qttools-linguist, qt6-qtsvg-devel, desktop-file-utils, ninja-build, spdlog") 34 | endif() 35 | set(CPACK_RPM_PACKAGE_LICENSE "GPL-3.0-or-later") 36 | endif() 37 | endif() 38 | 39 | # Must Here 40 | include(CPack) 41 | -------------------------------------------------------------------------------- /src/base/LemonMacro.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | 10 | #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) 11 | #define CONCATENATE2(arg1, arg2) arg1##arg2 12 | #define CONCATENATE(x, y) x##y 13 | 14 | #define EXPAND(...) __VA_ARGS__ 15 | #define FOR_EACH_1(what, x, ...) what(x) 16 | #define FOR_EACH_2(what, x, ...) what(x) EXPAND(FOR_EACH_1(what, __VA_ARGS__)) 17 | #define FOR_EACH_3(what, x, ...) what(x) EXPAND(FOR_EACH_2(what, __VA_ARGS__)) 18 | #define FOR_EACH_4(what, x, ...) what(x) EXPAND(FOR_EACH_3(what, __VA_ARGS__)) 19 | #define FOR_EACH_5(what, x, ...) what(x) EXPAND(FOR_EACH_4(what, __VA_ARGS__)) 20 | #define FOR_EACH_6(what, x, ...) what(x) EXPAND(FOR_EACH_5(what, __VA_ARGS__)) 21 | #define FOR_EACH_7(what, x, ...) what(x) EXPAND(FOR_EACH_6(what, __VA_ARGS__)) 22 | #define FOR_EACH_8(what, x, ...) what(x) EXPAND(FOR_EACH_7(what, __VA_ARGS__)) 23 | #define FOR_EACH_9(what, x, ...) what(x) EXPAND(FOR_EACH_8(what, __VA_ARGS__)) 24 | #define FOR_EACH_10(what, x, ...) what(x) EXPAND(FOR_EACH_9(what, __VA_ARGS__)) 25 | #define FOR_EACH_11(what, x, ...) what(x) EXPAND(FOR_EACH_10(what, __VA_ARGS__)) 26 | #define FOR_EACH_12(what, x, ...) what(x) EXPAND(FOR_EACH_11(what, __VA_ARGS__)) 27 | #define FOR_EACH_13(what, x, ...) what(x) EXPAND(FOR_EACH_12(what, __VA_ARGS__)) 28 | #define FOR_EACH_14(what, x, ...) what(x) EXPAND(FOR_EACH_13(what, __VA_ARGS__)) 29 | #define FOR_EACH_15(what, x, ...) what(x) EXPAND(FOR_EACH_14(what, __VA_ARGS__)) 30 | #define FOR_EACH_16(what, x, ...) what(x) EXPAND(FOR_EACH_15(what, __VA_ARGS__)) 31 | 32 | #define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) 33 | #define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__)) 34 | #define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N 35 | #define FOR_EACH_RSEQ_N() 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 36 | 37 | #define FOR_EACH_(N, what, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__)) 38 | #define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__) 39 | #define FOREACH_CALL_FUNC(func, ...) FOR_EACH(func, __VA_ARGS__) 40 | -------------------------------------------------------------------------------- /src/base/LemonBase.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | // 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define LEMON_BUILD_INFO QString(LEMON_BUILD_INFO_STR) 22 | #define LEMON_BUILD_EXTRA_INFO QString(LEMON_BUILD_EXTRA_INFO_STR) 23 | 24 | namespace Lemon { 25 | inline QStringList LemonAssetsPaths(const QString &dirName) { 26 | #define makeAbs(p) QDir(p).absolutePath() 27 | // Configuration Path 28 | QStringList list; 29 | 30 | // This is the default behavior on Windows 31 | list << makeAbs(QCoreApplication::applicationDirPath() + "/" + dirName); 32 | #ifdef LEMON_CONFIG_DIR 33 | list << makeAbs(LEMON_CONFIG_DIR + dirName); 34 | #endif 35 | list << ":/" + dirName; 36 | // 37 | list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, dirName, 38 | QStandardPaths::LocateDirectory); 39 | list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, dirName, 40 | QStandardPaths::LocateDirectory); 41 | 42 | #ifdef Q_OS_LINUX 43 | // Linux platform directories. 44 | list << makeAbs("/lib/lemon-lime/" + dirName); 45 | list << makeAbs("/usr/lib/lemon-lime/" + dirName); 46 | list << makeAbs("/usr/local/lib/lemon-lime/" + dirName); 47 | // 48 | list << makeAbs("/usr/share/lemon-lime/" + dirName); 49 | list << makeAbs("/usr/local/share/lemon-lime/" + dirName); 50 | // For Snap 51 | if (qEnvironmentVariableIsSet("SNAP")) { 52 | list << makeAbs(qEnvironmentVariable("SNAP") + "/usr/share/lemon-lime/" + dirName); 53 | } 54 | if (qEnvironmentVariableIsSet("APPIMAGE")) 55 | list << makeAbs(QCoreApplication::applicationDirPath() + "/../share/lemon-lime/" + dirName); 56 | #elif defined(Q_OS_MAC) 57 | // macOS platform directories. 58 | list << QDir(QCoreApplication::applicationDirPath() + "/../Resources/" + dirName).absolutePath(); 59 | #endif 60 | list.removeDuplicates(); 61 | return list; 62 | #undef makeAbs 63 | } 64 | 65 | } // namespace Lemon 66 | -------------------------------------------------------------------------------- /src/taskeditwidget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | 15 | namespace Ui { 16 | class TaskEditWidget; 17 | } 18 | 19 | class Settings; 20 | class Task; 21 | 22 | class TaskEditWidget : public QWidget { 23 | Q_OBJECT 24 | 25 | public: 26 | explicit TaskEditWidget(QWidget *parent = nullptr); 27 | ~TaskEditWidget(); 28 | void changeEvent(QEvent *); 29 | void setEditTask(Task *); 30 | void setSettings(Settings *); 31 | 32 | private: 33 | Ui::TaskEditWidget *ui; 34 | Settings *settings{}; 35 | Task *editTask; 36 | void refreshWidgetState(); 37 | void addSourceFiles(const QString &, const QString &); 38 | void addGraderFiles(const QString &, const QString &); 39 | void rmSourceFilesAt(int); 40 | void rmGraderFilesAt(int); 41 | void multiFilesRefresh(); 42 | 43 | private slots: 44 | void problemTitleChanged(const QString &); 45 | void setToTraditional(bool); 46 | void setToAnswersOnly(bool); 47 | void setToInteraction(bool); 48 | void setToCommunication(bool); 49 | void setToCommunicationExec(bool); 50 | void sourceFileNameChanged(const QString &); 51 | void subFolderCheckChanged(); 52 | void inputFileNameChanged(const QString &); 53 | void outputFileNameChanged(const QString &); 54 | void standardInputCheckChanged(); 55 | void standardOutputCheckChanged(); 56 | void comparisonModeChanged(); 57 | void diffArgumentsChanged(const QString &); 58 | void realPrecisionChanged(int); 59 | void specialJudgeChanged(const QString &); 60 | void interactorChanged(const QString &); 61 | void interactorNameChanged(const QString &); 62 | void graderChanged(const QString &); 63 | void refreshProblemTitle(const QString &); 64 | void refreshCompilerConfiguration(); 65 | void compilerSelectionChanged(); 66 | void configurationSelectionChanged(); 67 | void answerFileExtensionChanged(const QString &); 68 | void addSourceFileClicked(); 69 | void addGraderFileClicked(); 70 | void rmSourceFileClicked(); 71 | void rmGraderFileClicked(); 72 | 73 | signals: 74 | void dataPathChanged(); 75 | }; 76 | -------------------------------------------------------------------------------- /src/forms/newcontestdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | NewContestDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 450 10 | 320 11 | 12 | 13 | 14 | 15 | 450 16 | 320 17 | 18 | 19 | 20 | New Contest 21 | 22 | 23 | 24 | 25 | 26 | 27 | 10 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 10 37 | 38 | 39 | 40 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | NewContestWidget 49 | QWidget 50 |
newcontestwidget.h
51 | 1 52 |
53 |
54 | 55 | 56 | 57 | buttonBox 58 | accepted() 59 | NewContestDialog 60 | accept() 61 | 62 | 63 | 224 64 | 294 65 | 66 | 67 | 224 68 | 159 69 | 70 | 71 | 72 | 73 | buttonBox 74 | rejected() 75 | NewContestDialog 76 | reject() 77 | 78 | 79 | 224 80 | 294 81 | 82 | 83 | 224 84 | 159 85 | 86 | 87 | 88 | 89 |
90 | -------------------------------------------------------------------------------- /src/base/LemonTranslator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2020 Qv2ray Development Group 3 | * 2020-2022 Project LemonLime 4 | * 5 | * SPDX-License-Identifier: GPL-3.0-or-later 6 | * 7 | */ 8 | 9 | #include "LemonTranslator.hpp" 10 | // 11 | #include 12 | #include 13 | #include 14 | // 15 | 16 | #define LEMON_MODULE_NAME "Translator" 17 | 18 | using namespace Lemon; 19 | 20 | // path searching list. 21 | auto getLanguageSearchPaths() -> QStringList { 22 | // Configuration Path 23 | QStringList list = LemonAssetsPaths("lang"); 24 | #ifdef LEMON_EMBED_TRANSLATIONS 25 | // If the translations have been embedded. 26 | list << QString(":/translation/"); 27 | #endif 28 | #ifdef LEMON_TRANSLATION_PATH 29 | // Platform-specific dir, if specified. 30 | list << QString(LEMON_TRANSLATION_PATH); 31 | #endif 32 | return list; 33 | } 34 | 35 | namespace Lemon::common { 36 | LemonTranslator::LemonTranslator() { refreshTranslations(); } 37 | 38 | void LemonTranslator::refreshTranslations() { 39 | searchPaths = getLanguageSearchPaths(); 40 | languages.clear(); 41 | for (const auto &path : std::as_const(searchPaths)) { 42 | languages << QDir(path).entryList({"*.qm"}, QDir::Hidden | QDir::Files); 43 | } 44 | std::transform(languages.begin(), languages.end(), languages.begin(), 45 | [](QString &fileName) { return fileName.replace(".qm", ""); }); 46 | languages.removeDuplicates(); 47 | DEBUG("Found translations: " + languages.join(" ")); 48 | } 49 | 50 | auto LemonTranslator::InstallTranslation(const QString &code) -> bool { 51 | for (const auto &path : std::as_const(searchPaths)) { 52 | if (FileExistsIn(QDir(path), code + ".qm")) { 53 | DEBUG("Found " + code + " in folder: " + path); 54 | QTranslator *translatorNew = new QTranslator(); 55 | bool success = translatorNew->load(code + ".qm", path); 56 | if (! success) { 57 | LOG("Cannot load translation: " + code); 58 | } 59 | if (pTranslator) { 60 | LOG("Removed translations"); 61 | qApp->removeTranslator(pTranslator.get()); 62 | } 63 | this->pTranslator.reset(translatorNew); 64 | qApp->installTranslator(pTranslator.get()); 65 | LOG("Successfully installed a translator for " + code); 66 | return true; 67 | } 68 | } 69 | return false; 70 | } 71 | } // namespace Lemon::common 72 | -------------------------------------------------------------------------------- /src/forms/opencontestdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | OpenContestDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 450 10 | 320 11 | 12 | 13 | 14 | 15 | 450 16 | 320 17 | 18 | 19 | 20 | Open an Existing Contest 21 | 22 | 23 | 24 | 25 | 26 | 27 | 10 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 10 37 | 38 | 39 | 40 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | OpenContestWidget 49 | QWidget 50 |
opencontestwidget.h
51 | 1 52 |
53 |
54 | 55 | 56 | 57 | buttonBox 58 | accepted() 59 | OpenContestDialog 60 | accept() 61 | 62 | 63 | 224 64 | 294 65 | 66 | 67 | 224 68 | 159 69 | 70 | 71 | 72 | 73 | buttonBox 74 | rejected() 75 | OpenContestDialog 76 | reject() 77 | 78 | 79 | 224 80 | 294 81 | 82 | 83 | 224 84 | 159 85 | 86 | 87 | 88 | 89 |
90 | -------------------------------------------------------------------------------- /src/core/taskjudger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "base/LemonType.hpp" 11 | #include "core/judgingthread.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | class Contestant; 21 | class Settings; 22 | class Task; 23 | 24 | class TaskJudger : public QObject { 25 | Q_OBJECT 26 | public: 27 | TaskJudger(QObject *parent = nullptr); 28 | // void setCheckRejudgeMode(bool); 29 | void setNeedRejudge(const QList> &); 30 | void setSettings(Settings *); 31 | void setTask(Task *); 32 | void setTaskId(int); 33 | void setContestant(Contestant *); 34 | Contestant *getContestant() const; 35 | CompileState getCompileState() const; 36 | // const QList< std::pair >& getNeedRejudge() const; 37 | 38 | private: 39 | // bool checkRejudgeMode; 40 | 41 | const QString commExecGrader = "grader"; 42 | bool interpreterFlag{}; 43 | Settings *settings{}; 44 | Task *task{}; 45 | Contestant *contestant; 46 | CompileState compileState; 47 | QString compileMessage; 48 | QString sourceFile; 49 | QString executableFile; 50 | QString arguments; 51 | QString diffPath; 52 | double compilerTimeLimitRatio{}; 53 | double compilerMemoryLimitRatio{}; 54 | bool disableMemoryLimitCheck{}; 55 | bool interpreterAsWatcher{}; 56 | QProcessEnvironment environment; 57 | QList overallStatus; 58 | QList> timeUsed; 59 | QList> memoryUsed; 60 | QList> score; 61 | QList> result; 62 | QList message; 63 | QList inputFiles; 64 | 65 | QList testCaseScore; 66 | bool isJudging; 67 | int taskId; 68 | bool traditionalTaskPrepare(); 69 | void assign(); 70 | void taskSkipped(const std::pair &); 71 | void makeDialogAlert(QString); 72 | int judge(); 73 | 74 | QTemporaryDir temporaryDir; 75 | 76 | public: 77 | void judgeIt(); 78 | public slots: 79 | void stop(); 80 | signals: 81 | void judgingStarted(QString); 82 | void judgingFinished(); 83 | void dialogAlert(QString); 84 | void singleCaseFinished(QString, int, int, int, int, int, int, int); 85 | void singleSubtaskDependenceFinished(int, int, int); 86 | void compileError(int, int); 87 | void stopJudgingSignal(); 88 | }; 89 | -------------------------------------------------------------------------------- /src/lemon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | 16 | namespace Ui { 17 | class LemonLime; 18 | } 19 | 20 | class Contest; 21 | class Settings; 22 | class OptionsDialog; 23 | 24 | class LemonLime : public QMainWindow { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit LemonLime(QWidget *parent = nullptr); 29 | ~LemonLime(); 30 | void changeEvent(QEvent *); 31 | void closeEvent(QCloseEvent *); 32 | int getSplashTime(); 33 | void welcome(); 34 | 35 | private: 36 | Ui::LemonLime *ui; 37 | Contest *curContest; 38 | Settings *settings; 39 | QFileSystemWatcher *dataDirWatcher; 40 | QString curFile; 41 | QSignalMapper *signalMapper; 42 | QMenu *TaskMenu; 43 | QList TaskList; 44 | QTimer autoSaveTimer; 45 | void judgeExtButtonFlip(bool); 46 | void loadUiLanguage(); 47 | void insertWatchPath(const QString &, QFileSystemWatcher *); 48 | void newContest(const QString &, const QString &, const QString &); 49 | void saveContest(const QString &); 50 | void loadContest(const QString &); 51 | static void getFiles(const QString &, const QStringList &, QMap &); 52 | void addTask(const QString &, const QList> &, int, int, int); 53 | void addTaskWithScoreScale(const QString &, const QList> &, int, int, int); 54 | static bool compareFileName(const std::pair &, const std::pair &); 55 | 56 | private slots: 57 | void summarySelectionChanged(); 58 | void refreshSummary(); 59 | void resetDataWatcher(); 60 | void showOptionsDialog(); 61 | void refreshButtonClicked(); 62 | void cleanupButtonClicked(); 63 | void tabIndexChanged(int); 64 | void moveUpTask(); 65 | void moveDownTask(); 66 | void viewerSelectionChanged(); 67 | void contestantDeleted(); 68 | void newAction(); 69 | void saveAction(); 70 | static void openFolderAction(); 71 | void closeAction(); 72 | void loadAction(); 73 | void addTasksAction(); 74 | void exportResult(); 75 | void exportStatistics(); 76 | void changeContestName(); 77 | void aboutLemon(); 78 | void actionManual(); 79 | static void actionMore(); 80 | 81 | signals: 82 | void dataPathChanged(); 83 | }; 84 | -------------------------------------------------------------------------------- /src/forms/detaildialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | DetailDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 709 10 | 520 11 | 12 | 13 | 14 | Details 15 | 16 | 17 | 18 | 19 | 20 | 21 | 10 22 | 23 | 24 | 25 | Qt::LinksAccessibleByMouse 26 | 27 | 28 | false 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Qt::Horizontal 38 | 39 | 40 | 41 | 40 42 | 20 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 10 52 | 53 | 54 | 55 | &Close 56 | 57 | 58 | 59 | :/icon/paint-none.svg:/icon/paint-none.svg 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | closeButton 73 | clicked() 74 | DetailDialog 75 | accept() 76 | 77 | 78 | 650 79 | 493 80 | 81 | 82 | 354 83 | 259 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/base/LemonConfig.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #include "LemonConfig.hpp" 9 | // 10 | #include "base/LemonUtils.hpp" 11 | #include "base/compiler.h" 12 | 13 | namespace Lemon::base::config { 14 | 15 | int LemonConfigJudge::read(const QJsonObject &json) { 16 | READ_JSON(json, defaultFullScore); 17 | READ_JSON(json, defaultTimeLimit); 18 | READ_JSON(json, defaultMemoryLimit); 19 | READ_JSON(json, compileTimeLimit); 20 | READ_JSON(json, specialJudgeTimeLimit); 21 | READ_JSON(json, fileSizeLimit); 22 | READ_JSON(json, rejudgeTimes); 23 | READ_JSON(json, maxJudgingThreads); 24 | 25 | READ_JSON(json, defaultInputFileExtension); 26 | READ_JSON(json, defaultOutputFileExtension); 27 | READ_JSON(json, diffPath); 28 | 29 | READ_JSON(json, inputFileExtensions); 30 | READ_JSON(json, outputFileExtensions); 31 | READ_JSON(json, recentContest); 32 | 33 | // CompilerList 34 | if (json.contains("compilerList") && json["compilerList"].isArray()) { 35 | QJsonArray _compilerList = json["compilerList"].toArray(); 36 | compilerList.clear(); 37 | compilerList.reserve(_compilerList.size()); 38 | for (int i = 0; i < _compilerList.size(); ++i) { 39 | QJsonObject compilerObject = _compilerList[i].toObject(); 40 | Compiler *compiler = new Compiler; 41 | if (compiler->read(compilerObject) == -1) 42 | return -1; 43 | compilerList.append(compiler); 44 | } 45 | } else 46 | return -1; 47 | return 0; 48 | } 49 | 50 | void LemonConfigJudge::write(QJsonObject &json) const { 51 | WRITE_JSON(json, defaultFullScore); 52 | WRITE_JSON(json, defaultTimeLimit); 53 | WRITE_JSON(json, defaultMemoryLimit); 54 | WRITE_JSON(json, compileTimeLimit); 55 | WRITE_JSON(json, specialJudgeTimeLimit); 56 | WRITE_JSON(json, fileSizeLimit); 57 | WRITE_JSON(json, rejudgeTimes); 58 | WRITE_JSON(json, maxJudgingThreads); 59 | 60 | WRITE_JSON(json, defaultInputFileExtension); 61 | WRITE_JSON(json, defaultOutputFileExtension); 62 | WRITE_JSON(json, diffPath); 63 | 64 | WRITE_JSON(json, inputFileExtensions); 65 | WRITE_JSON(json, outputFileExtensions); 66 | WRITE_JSON(json, recentContest); 67 | 68 | QJsonArray compilerList; 69 | for (const auto compiler : this->compilerList) { 70 | QJsonObject obj; 71 | compiler->write(obj); 72 | compilerList.append(obj); 73 | } 74 | WRITE_JSON(json, compilerList); 75 | } 76 | 77 | } // namespace Lemon::base::config 78 | -------------------------------------------------------------------------------- /.github/workflows/macos-qt6.yml: -------------------------------------------------------------------------------- 1 | name: MacOS Qt6 2 | on: 3 | push: 4 | paths-ignore: 5 | - "README.md" 6 | - "LICENSE" 7 | - "BUILD.md" 8 | pull_request: 9 | paths-ignore: 10 | - "README.md" 11 | - "LICENSE" 12 | - "BUILD.md" 13 | release: 14 | types: [published] 15 | jobs: 16 | build: 17 | name: Build 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | matrix: 21 | os: [macos-latest] 22 | build_type: [Release, RelWithDebInfo] 23 | arch: [x86_64, arm64] 24 | qt_ver: [6.9.3] 25 | qt_arch: [clang_64] 26 | env: 27 | targetName: lemon 28 | steps: 29 | - name: Install Qt 30 | uses: jurplel/install-qt-action@v4 31 | with: 32 | version: ${{ matrix.qt_ver }} 33 | cached: ${{ steps.MacosCacheQt.outputs.cache-hit }} 34 | 35 | - uses: actions/checkout@v4 36 | with: 37 | submodules: true 38 | - name: macOS - ${{ matrix.qt_version }} - Build preparation - Install Packages 39 | run: | 40 | brew install ninja pkg-config 41 | - name: build macos 42 | run: | 43 | cmake . \ 44 | -GNinja \ 45 | -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ 46 | -DLEMON_BUILD_INFO="Build for macOS" \ 47 | -DLEMON_BUILD_EXTRA_INFO="Build on $(uname -sr)" \ 48 | -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \ 49 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.arch }}" 50 | cmake --build . --parallel $(sysctl -n hw.logicalcpu) 51 | # tag 打包 52 | - name: package 53 | env: 54 | Qt6_DIR: ../Qt/${{matrix.qt_ver}}/${{matrix.qt_arch}} 55 | run: | 56 | # 拷贝依赖 57 | #mv ${targetName} ${targetName}.app 58 | export PATH=$Qt6_DIR/bin:$PATH 59 | macdeployqt ${targetName}.app -qmldir=. -verbose=1 -dmg 60 | - name: Upload artifact 61 | uses: actions/upload-artifact@v4 62 | with: 63 | name: ${{ env.targetName }}-Qt${{ matrix.qt_ver }}-${{ matrix.build_type }}-${{ matrix.arch }}.dmg 64 | path: ${{ env.targetName }}.dmg 65 | - name: uploadRelease 66 | if: startsWith(github.event.ref, 'refs/tags/') 67 | uses: svenstaro/upload-release-action@v2 68 | with: 69 | repo_token: ${{ secrets.GITHUB_TOKEN }} 70 | file: ./${{ env.targetName }}.dmg 71 | asset_name: ${{ env.targetName }}-Qt${{ matrix.qt_ver }}-${{ matrix.build_type }}-${{ matrix.arch }}.dmg 72 | tag: ${{ github.ref }} 73 | -------------------------------------------------------------------------------- /src/welcomedialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "welcomedialog.h" 11 | #include "ui_welcomedialog.h" 12 | // 13 | #include 14 | 15 | WelcomeDialog::WelcomeDialog(QWidget *parent) : QDialog(parent), ui(new Ui::WelcomeDialog) { 16 | ui->setupUi(this); 17 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 18 | connect(ui->openContestWidget, &OpenContestWidget::selectionChanged, this, 19 | &WelcomeDialog::selectionChanged); 20 | connect(ui->newContestWidget, &NewContestWidget::informationChanged, this, 21 | &WelcomeDialog::informationChanged); 22 | connect(ui->tabWidget, &QTabWidget::currentChanged, this, &WelcomeDialog::tabIndexChanged); 23 | connect(ui->openContestWidget, &OpenContestWidget::rowDoubleClicked, this, &WelcomeDialog::accept); 24 | } 25 | 26 | WelcomeDialog::~WelcomeDialog() { delete ui; } 27 | 28 | void WelcomeDialog::setRecentContest(const QStringList &list) { 29 | ui->openContestWidget->setRecentContest(list); 30 | } 31 | 32 | auto WelcomeDialog::getContestTitle() -> QString { return ui->newContestWidget->getContestTitle(); } 33 | 34 | auto WelcomeDialog::getSavingName() -> QString { return ui->newContestWidget->getSavingName(); } 35 | 36 | auto WelcomeDialog::getContestPath() -> QString { return ui->newContestWidget->getContestPath(); } 37 | 38 | auto WelcomeDialog::getRecentContest() const -> const QStringList & { 39 | return ui->openContestWidget->getRecentContest(); 40 | } 41 | 42 | auto WelcomeDialog::getSelectedContest() -> QString { 43 | return ui->openContestWidget->getRecentContest().at(ui->openContestWidget->getCurrentRow()); 44 | } 45 | 46 | auto WelcomeDialog::getCurrentTab() const -> int { return ui->tabWidget->currentIndex(); } 47 | 48 | void WelcomeDialog::selectionChanged() { 49 | if (ui->openContestWidget->getCurrentRow() != -1) { 50 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); 51 | } else { 52 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 53 | } 54 | } 55 | 56 | void WelcomeDialog::informationChanged() { 57 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ui->newContestWidget->checkReady()); 58 | } 59 | 60 | void WelcomeDialog::tabIndexChanged(int index) { 61 | if (index == 0) { 62 | selectionChanged(); 63 | } else { 64 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ui->newContestWidget->checkReady()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/core/contestant.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * 2018-2019 Project LemonPlus, Dust1404 4 | * 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | #include "base/LemonType.hpp" 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | class Contestant : public QObject { 19 | Q_OBJECT 20 | public: 21 | explicit Contestant(QObject *parent = nullptr); 22 | 23 | const QString &getContestantName() const; 24 | bool getCheckJudged(int) const; 25 | CompileState getCompileState(int) const; 26 | const QString &getSourceFile(int) const; 27 | const QString &getCompileMessage(int) const; 28 | const QList &getInputFiles(int) const; 29 | const QList> &getResult(int) const; 30 | const QList &getMessage(int) const; 31 | const QList> &getScore(int) const; 32 | const QList> &getTimeUsed(int) const; 33 | const QList> &getMemoryUsed(int) const; 34 | QDateTime getJudingTime() const; 35 | int getTaskScore(int) const; 36 | int getTotalScore() const; 37 | int getTotalUsedTime() const; 38 | 39 | void setContestantName(const QString &); 40 | void setCheckJudged(int, bool); 41 | void setCompileState(int, CompileState); 42 | void setSourceFile(int, const QString &); 43 | void setCompileMessage(int, const QString &); 44 | void setInputFiles(int, const QList &); 45 | void setResult(int, const QList> &); 46 | void setMessage(int, const QList &); 47 | void setScore(int, const QList> &); 48 | void setTimeUsed(int, const QList> &); 49 | void setMemoryUsed(int, const QList> &); 50 | void setJudgingTime(QDateTime); 51 | 52 | int writeToJson(QJsonObject &); 53 | int readFromJson(const QJsonObject &); 54 | void readFromStream(QDataStream &); 55 | 56 | private: 57 | QString contestantName; 58 | QList checkJudged; 59 | QList compileState; 60 | QStringList sourceFile; 61 | QStringList compileMesaage; 62 | QList> inputFiles; 63 | QList>> result; 64 | QList> message; 65 | QList>> score; 66 | QList>> timeUsed; 67 | QList>> memoryUsed; 68 | QDateTime judgingTime; 69 | 70 | // QList taskResults; 71 | signals: 72 | 73 | public slots: 74 | void addTask(); 75 | void deleteTask(int); 76 | void swapTask(int, int); 77 | }; 78 | -------------------------------------------------------------------------------- /src/core/contest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * 2018-2019 Project LemonPlus, Dust1404 4 | * 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include "base/LemonType.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define MagicNumber 0x20111127 21 | 22 | class Task; 23 | class Settings; 24 | class Contestant; 25 | class JudgingController; 26 | 27 | class Contest : public QObject { 28 | Q_OBJECT 29 | public: 30 | explicit Contest(QObject *parent = nullptr); 31 | void setSettings(Settings *); 32 | void copySettings(Settings &); 33 | void setContestTitle(const QString &); 34 | const QString &getContestTitle() const; 35 | Task *getTask(int) const; 36 | void swapTask(int, int); 37 | const QList &getTaskList() const; 38 | Contestant *getContestant(const QString &) const; 39 | QList getContestantList() const; 40 | int getTotalTimeLimit() const; 41 | int getTotalScore() const; 42 | void addTask(Task *); 43 | void deleteTask(int); 44 | void refreshContestantList(); 45 | void deleteContestant(const QString &); 46 | void writeToJson(QJsonObject &); 47 | void readFromStream(QDataStream &); 48 | int readFromJson(const QJsonObject &); 49 | 50 | private: 51 | QString contestTitle; 52 | Settings *settings{}; 53 | QList taskList; 54 | QMap contestantList; 55 | bool stopJudging{}; 56 | void judge(Contestant *); 57 | void judge(const QVector> &); 58 | void clearPath(const QString &); 59 | JudgingController *controller; 60 | 61 | public slots: 62 | void judge(const QList>> &); 63 | void judgeAll(); 64 | // void judgeFinished(); 65 | void stopJudgingSlot(); 66 | 67 | signals: 68 | void taskAddedForContestant(); 69 | void taskDeletedForContestant(int); 70 | void taskAddedForViewer(); 71 | void taskDeletedForViewer(int); 72 | void problemTitleChanged(); 73 | void dialogAlert(QString); 74 | void singleCaseFinished(QString, int, int, int, int, int, int, int); 75 | void singleSubtaskDependenceFinished(int, int, int); 76 | void taskJudgingStarted(QString); 77 | void taskJudgingFinished(); 78 | void taskJudgedDisplay(const QString &, const QList> &, const int); 79 | void contestantJudgingStart(QString); 80 | void contestantJudgingFinished(); 81 | void contestantJudgedDisplay(const QString &, const int, const int); 82 | void compileError(int, int); 83 | void stopJudgingSignal(); 84 | }; 85 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "lemon.h" 11 | #include "spdlog/sinks/stdout_color_sinks.h" 12 | // 13 | #include "base/LemonBase.hpp" 14 | #include "base/LemonBaseApplication.hpp" 15 | #include "base/LemonLog.hpp" 16 | #include "spdlog/sinks/daily_file_sink.h" 17 | // 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define LEMON_MODULE_NAME "Main" 24 | 25 | void initLogger() { 26 | auto console_sink = std::make_shared(); 27 | console_sink->set_level(spdlog::level::warn); 28 | QDir logDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QDir::separator() + 29 | "logs"); 30 | logDir.mkpath("."); 31 | // retain last 30 days logs 32 | auto file_sink = std::make_shared( 33 | (logDir.path() + QDir::separator() + "lemonlime-log.txt").toStdString(), 0, 0, false, 30); 34 | file_sink->set_level(spdlog::level::trace); 35 | Lemon::base::logger = 36 | std::make_shared(spdlog::logger("lemonlime", {console_sink, file_sink})); 37 | spdlog::flush_every(std::chrono::seconds(5)); 38 | } 39 | 40 | int main(int argc, char *argv[]) { 41 | 42 | QCoreApplication::setApplicationName("Lemonlime"); 43 | 44 | initLogger(); 45 | 46 | Lemon::LemonBaseApplication app(argc, argv); 47 | 48 | app.Initialize(); 49 | 50 | if (app.sendMessage("")) { 51 | app.activeWindow(); 52 | return 0; 53 | } 54 | 55 | #ifdef Q_OS_LINUX 56 | // fonts.setFamily("Noto Sans CJK SC"); 57 | #endif 58 | #ifdef Q_OS_WIN32 59 | QFont fonts; 60 | fonts.setFamily("Microsoft YaHei"); 61 | fonts.setHintingPreference(QFont::PreferNoHinting); 62 | SingleApplication::setFont(fonts); 63 | #endif 64 | #ifdef Q_OS_MAC 65 | // fonts.setFamily("PingFangSC-Regular"); 66 | #endif 67 | Q_INIT_RESOURCE(resource); 68 | QPixmap pixmap(":/logo/splash2.png"); 69 | QSplashScreen screen(pixmap.scaled(450, 191, Qt::KeepAspectRatio, Qt::SmoothTransformation)); 70 | LemonLime w; 71 | qint64 startTime = QDateTime::currentMSecsSinceEpoch(); 72 | int splashTime = w.getSplashTime(); 73 | 74 | if (splashTime > 0) { 75 | screen.show(); 76 | 77 | do { 78 | SingleApplication::processEvents(); 79 | } while (QDateTime::currentMSecsSinceEpoch() - startTime <= splashTime); 80 | 81 | screen.finish(&w); 82 | } 83 | 84 | w.activateWindow(); 85 | w.show(); 86 | w.welcome(); 87 | return app.exec(); 88 | } 89 | -------------------------------------------------------------------------------- /src/base/compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include 14 | #include 15 | 16 | class Compiler : public QObject { 17 | Q_OBJECT 18 | public: 19 | enum CompilerType { Typical, InterpretiveWithByteCode, InterpretiveWithoutByteCode }; 20 | Q_ENUM(CompilerType) 21 | 22 | explicit Compiler(QObject *parent = nullptr); 23 | 24 | CompilerType getCompilerType() const; 25 | const QString &getCompilerName() const; 26 | const QStringList &getSourceExtensions() const; 27 | const QString &getCompilerLocation() const; 28 | const QString &getInterpreterLocation() const; 29 | const QStringList &getBytecodeExtensions() const; 30 | const QStringList &getConfigurationNames() const; 31 | const QStringList &getCompilerArguments() const; 32 | const QStringList &getInterpreterArguments() const; 33 | const QProcessEnvironment &getEnvironment() const; 34 | double getTimeLimitRatio() const; 35 | double getMemoryLimitRatio() const; 36 | bool getDisableMemoryLimitCheck() const; 37 | bool getInterpreterAsWatcher() const; 38 | 39 | void setCompilerType(CompilerType); 40 | void setCompilerName(const QString &); 41 | void setSourceExtensions(const QString &); 42 | void setCompilerLocation(const QString &); 43 | void setInterpreterLocation(const QString &); 44 | void setBytecodeExtensions(const QString &); 45 | void setEnvironment(const QProcessEnvironment &); 46 | void setTimeLimitRatio(double); 47 | void setMemoryLimitRatio(double); 48 | void setDisableMemoryLimitCheck(bool); 49 | void setInterpreterAsWatcher(bool); 50 | 51 | void addConfiguration(const QString &, const QString &, const QString &); 52 | void setConfigName(int, const QString &); 53 | void setCompilerArguments(int, const QString &); 54 | void setInterpreterArguments(int, const QString &); 55 | void deleteConfiguration(int); 56 | 57 | void copyFrom(Compiler *); 58 | 59 | int read(const QJsonObject &json); 60 | void write(QJsonObject &json) const; 61 | 62 | private: 63 | CompilerType compilerType; 64 | QString compilerName; 65 | QStringList sourceExtensions; 66 | QString compilerLocation; 67 | QString interpreterLocation; 68 | QStringList bytecodeExtensions; 69 | QStringList configurationNames; 70 | QStringList compilerArguments; 71 | QStringList interpreterArguments; 72 | QProcessEnvironment environment; 73 | double timeLimitRatio; 74 | double memoryLimitRatio; 75 | bool disableMemoryLimitCheck; 76 | bool interpreterAsWatcher; 77 | }; 78 | -------------------------------------------------------------------------------- /unix/watcher_linux.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2019 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2019-2023 Project LemonLime 4 | * 5 | * SPDX-License-Identifier: GPL-3.0-or-later 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | static auto read_elf_ident(int fd, char *e_ident) -> bool { 24 | if (read(fd, e_ident, EI_NIDENT) != EI_NIDENT) { 25 | return false; 26 | } 27 | if (strncmp(e_ident, "\177ELF", 4) != 0) { 28 | return false; 29 | } 30 | return true; 31 | } 32 | 33 | template static auto calculateStaticMemoryUsage(int fd) -> ssize_t { 34 | ssize_t res = 0; 35 | 36 | Ehdr elf_header; 37 | if (lseek64(fd, 0, SEEK_SET) < 0) { 38 | return -1; 39 | } 40 | if (read(fd, &elf_header, sizeof(Ehdr)) != sizeof(Ehdr)) { 41 | return -1; 42 | } 43 | // gcc with PIE will set executable as ET_DYN 44 | // https://stackoverflow.com/questions/34519521 45 | if (elf_header.e_type != ET_EXEC && elf_header.e_type != ET_DYN) { 46 | return -1; 47 | } 48 | if (elf_header.e_phoff == 0) { 49 | return -1; // no program header, not executable 50 | } 51 | if (elf_header.e_phentsize != sizeof(Phdr)) { 52 | return -1; 53 | } 54 | 55 | if (lseek64(fd, elf_header.e_phoff, SEEK_SET) < 0) { 56 | return -1; 57 | } 58 | Phdr program_header; 59 | for (int i = 0; i < elf_header.e_phnum; ++i) { 60 | if (read(fd, &program_header, sizeof(Phdr)) != sizeof(Phdr)) { 61 | return -1; 62 | } 63 | if (program_header.p_type == PT_LOAD) { 64 | res += program_header.p_memsz; 65 | } 66 | } 67 | 68 | return res; 69 | } 70 | 71 | void initWatcher() { return; } 72 | 73 | ssize_t calculateStaticMemoryUsage(const std::string &fileName) { 74 | char e_ident[EI_NIDENT]; 75 | ssize_t staticMemoryUsage = 0; 76 | int fd = open(fileName.c_str(), O_RDONLY); 77 | if (fd < 0) { 78 | return -1; 79 | } 80 | 81 | if (read_elf_ident(fd, e_ident) == false) { 82 | return -1; 83 | } 84 | if (e_ident[EI_CLASS] == ELFCLASS32) { 85 | staticMemoryUsage = calculateStaticMemoryUsage(fd); 86 | } else if (e_ident[EI_CLASS] == ELFCLASS64) { 87 | staticMemoryUsage = calculateStaticMemoryUsage(fd); 88 | } else { 89 | staticMemoryUsage = -1; 90 | } 91 | close(fd); 92 | 93 | return staticMemoryUsage; 94 | } 95 | 96 | ssize_t getMemoryRLimit(ssize_t memoryLimitInMB) { return memoryLimitInMB * 1024 * 1024; } 97 | 98 | size_t getMaxRSSInByte(long ru_maxrss) { return ru_maxrss * 1024; } 99 | -------------------------------------------------------------------------------- /.github/workflows/cpack-deb-debian.yml: -------------------------------------------------------------------------------- 1 | name: CPack - DEB - Debian 2 | 3 | on: 4 | # push代码时触发workflow 5 | push: 6 | # 忽略README.md 7 | paths-ignore: 8 | - "README.md" 9 | - "LICENSE" 10 | - "BUILD.md" 11 | # pull_request时触发workflow 12 | pull_request: 13 | # 忽略README.md 14 | paths-ignore: 15 | - "README.md" 16 | - "LICENSE" 17 | - "BUILD.md" 18 | release: 19 | types: [published] 20 | 21 | jobs: 22 | linux: 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | #distro: [stable, testing, sid] 27 | build_type: [Release, RelWithDebInfo] 28 | distro: [testing, sid] 29 | name: Debian ${{ matrix.distro }} 30 | runs-on: ubuntu-latest 31 | container: debian:${{ matrix.distro }} 32 | 33 | steps: 34 | - name: Install git 35 | run: | 36 | apt-get update 37 | apt-get install -y git 38 | - name: Get the version 39 | id: get_version 40 | shell: bash 41 | run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3)" >> $GITHUB_OUTPUT 42 | - name: Checking out sources 43 | uses: actions/checkout@v4 44 | with: 45 | submodules: "recursive" 46 | - name: Install build dependencies 47 | run: | 48 | apt-get install -y build-essential ninja-build qt6-base-dev qt6-tools-dev qt6-svg-dev qt6-image-formats-plugins qt6-translations-l10n cmake pkgconf bash libspdlog-dev 49 | - name: Build 50 | run: | 51 | mkdir build 52 | cd build 53 | cmake .. -GNinja -DEMBED_TRANSLATIONS=OFF -DEMBED_DOCS=OFF -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DBUILD_DEB=ON -DLEMON_BUILD_INFO="Build for Debian" -DLEMON_BUILD_EXTRA_INFO="Build on $(uname -sr)" 54 | cmake --build . --target package --parallel $(nproc) 55 | - name: Get package name 56 | shell: bash 57 | id: get_package 58 | run: | 59 | echo "NAME=$(basename build/LemonLime-*.deb)" >> $GITHUB_OUTPUT 60 | - name: Upload artifact 61 | uses: actions/upload-artifact@v4 62 | with: 63 | name: LemonLime-${{ steps.get_version.outputs.VERSION }}-debian-${{ matrix.distro }}-${{ matrix.build_type }}.deb 64 | path: build/${{ steps.get_package.outputs.NAME }} 65 | - name: Upload binaries to release 66 | uses: svenstaro/upload-release-action@v2 67 | if: startsWith(github.event.ref, 'refs/tags/') 68 | with: 69 | repo_token: ${{ secrets.GITHUB_TOKEN }} 70 | file: build/${{ steps.get_package.outputs.NAME }} 71 | asset_name: LemonLime-${{ steps.get_version.outputs.VERSION }}-debian-${{ matrix.distro }}-${{ matrix.build_type }}.deb 72 | tag: ${{ github.ref }} 73 | overwrite: true 74 | -------------------------------------------------------------------------------- /src/visualsettings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | * 6 | */ 7 | 8 | #include "visualsettings.h" 9 | #include "ui_visualsettings.h" 10 | // 11 | #include "base/LemonLog.hpp" 12 | #include "base/settings.h" 13 | #define LEMON_MODULE_NAME "VisualSettings" 14 | 15 | VisualSettings::VisualSettings(QWidget *parent) : QWidget(parent), ui(new Ui::VisualSettings) { 16 | ui->setupUi(this); 17 | 18 | editColorTheme = nullptr; 19 | connect(ui->themeName, &QLineEdit::textChanged, this, &VisualSettings::themeNameChanged); 20 | } 21 | 22 | void VisualSettings::resetColorTheme(ColorTheme *colorTheme) { 23 | editColorTheme = colorTheme; 24 | 25 | ui->themeName->setText(editColorTheme->getName()); 26 | ui->spinBoxMxH->setValue(editColorTheme->getMxColor().h); 27 | ui->doubleSpinBoxMxS->setValue(editColorTheme->getMxColor().s); 28 | ui->doubleSpinBoxMxL->setValue(editColorTheme->getMxColor().l); 29 | ui->spinBoxMiH->setValue(editColorTheme->getMiColor().h); 30 | ui->doubleSpinBoxMiS->setValue(editColorTheme->getMiColor().s); 31 | ui->doubleSpinBoxMiL->setValue(editColorTheme->getMiColor().l); 32 | ui->spinBoxNfH->setValue(editColorTheme->getNfColor().h); 33 | ui->doubleSpinBoxNfS->setValue(editColorTheme->getNfColor().s); 34 | ui->doubleSpinBoxNfL->setValue(editColorTheme->getNfColor().l); 35 | ui->spinBoxCeH->setValue(editColorTheme->getCeColor().h); 36 | ui->doubleSpinBoxCeS->setValue(editColorTheme->getCeColor().s); 37 | ui->doubleSpinBoxCeL->setValue(editColorTheme->getCeColor().l); 38 | 39 | ui->grandCompH->setValue(editColorTheme->getGrandComp().h); 40 | ui->grandCompS->setValue(editColorTheme->getGrandComp().s); 41 | ui->grandCompL->setValue(editColorTheme->getGrandComp().l); 42 | ui->grandRateH->setValue(editColorTheme->getGrandRate().h); 43 | ui->grandRateS->setValue(editColorTheme->getGrandRate().s); 44 | ui->grandRateL->setValue(editColorTheme->getGrandRate().l); 45 | } 46 | 47 | void VisualSettings::themeNameChanged(const QString &x) { editColorTheme->setName(x); } 48 | 49 | void VisualSettings::refresh() { 50 | editColorTheme->setColor( 51 | hslTuple(ui->spinBoxMxH->value(), ui->doubleSpinBoxMxS->value(), ui->doubleSpinBoxMxL->value()), 52 | hslTuple(ui->spinBoxMiH->value(), ui->doubleSpinBoxMiS->value(), ui->doubleSpinBoxMiL->value()), 53 | hslTuple(ui->spinBoxNfH->value(), ui->doubleSpinBoxNfS->value(), ui->doubleSpinBoxNfL->value()), 54 | hslTuple(ui->spinBoxCeH->value(), ui->doubleSpinBoxCeS->value(), ui->doubleSpinBoxCeL->value()), 55 | dddTuple(ui->grandCompH->value(), ui->grandCompS->value(), ui->grandCompL->value()), 56 | dddTuple(ui->grandRateH->value(), ui->grandRateS->value(), ui->grandRateL->value())); 57 | } 58 | 59 | VisualSettings::~VisualSettings() { delete ui; } 60 | -------------------------------------------------------------------------------- /src/core/judgingthread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * 2018-2019 Project LemonPlus, Dust1404 4 | * 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #pragma once 11 | // 12 | 13 | #include "base/LemonType.hpp" 14 | #include 15 | #include 16 | 17 | class Task; 18 | 19 | class JudgingThread : public QThread { 20 | Q_OBJECT 21 | public: 22 | explicit JudgingThread(QObject *parent = nullptr); 23 | // void setCheckRejudgeMode(bool); 24 | void setExtraTimeRatio(double); 25 | void setEnvironment(const QProcessEnvironment &); 26 | void setWorkingDirectory(const QString &); 27 | void setSpecialJudgeTimeLimit(int); 28 | void setExecutableFile(const QString &); 29 | void setArguments(const QString &); 30 | void setAnswerFile(const QString &); 31 | void setInputFile(const QString &); 32 | void setOutputFile(const QString &); 33 | void setDiffPath(const QString &); 34 | void setTask(Task *); 35 | void setFullScore(int); 36 | void setTimeLimit(int); 37 | void setRawTimeLimit(int); 38 | void setMemoryLimit(int); 39 | void setRawMemoryLimit(int); 40 | void setInterpreterAsWatcher(bool); 41 | int getTimeUsed() const; 42 | int getMemoryUsed() const; 43 | int getScore() const; 44 | int getFullScore() const; 45 | int getJudgeTimes() const; 46 | ResultState getResult() const; 47 | const QString &getMessage() const; 48 | bool getNeedRejudge() const; 49 | void run(); 50 | 51 | private: 52 | // bool checkRejudgeMode; 53 | 54 | bool needRejudge; 55 | // Control some extra time program used, like kernel time, judge system fluctuation 56 | double extraTimeRatio{}; 57 | QProcessEnvironment environment; 58 | QString workingDirectory; 59 | QString executableFile; 60 | QString arguments; 61 | QString answerFile; 62 | QString inputFile; 63 | QString outputFile; 64 | QString diffPath; 65 | Task *task{}; 66 | int specialJudgeTimeLimit{}; 67 | int fullScore{}; 68 | int timeLimit{}; 69 | int rawTimeLimit{}; 70 | int memoryLimit{}; 71 | int rawMemoryLimit{}; 72 | int timeUsed; 73 | int memoryUsed; 74 | int score{}; 75 | int judgedTimes; 76 | ResultState result; 77 | QString message; 78 | bool stopJudging; 79 | bool interpreterAsWatcher{}; 80 | void compareLineByLine(const QString &); 81 | void compareIgnoreSpaces(const QString &); 82 | void compareWithDiff(const QString &); 83 | void compareRealNumbers(const QString &); 84 | void lemonSpecialJudge(const QString &); 85 | void testlibSpecialJudge(const QString &); 86 | void runProgram(); 87 | void judgeOutput(); 88 | void judgeTraditionalTask(); 89 | void judgeAnswersOnlyTask(); 90 | // void judgeInteractionTask(); 91 | 92 | public slots: 93 | void stopJudgingSlot(); 94 | }; 95 | -------------------------------------------------------------------------------- /.github/workflows/build-manual.yml: -------------------------------------------------------------------------------- 1 | name: Build Typst manual and upload artifact 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - 'manual/**' 9 | pull_request: 10 | branches: 11 | - master 12 | paths: 13 | - 'manual/**' 14 | workflow_dispatch: 15 | 16 | permissions: 17 | contents: write 18 | 19 | env: 20 | FONT_DIR: ".fonts" 21 | FONT_VERSION: "v1" 22 | 23 | jobs: 24 | build-and-upload: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v5 28 | with: 29 | fetch-depth: 0 30 | 31 | - uses: actions/cache@v4 32 | with: 33 | path: ${{ env.FONT_DIR }} 34 | key: fonts-${{ runner.os }}-${{ env.FONT_VERSION }} 35 | 36 | - name: Install dependencies 37 | run: | 38 | sudo apt-get update -y 39 | sudo apt-get install -y curl unzip 40 | 41 | - name: Create font dir 42 | run: | 43 | mkdir -p "${{ env.FONT_DIR }}" 44 | 45 | - name: Download Noto Serif CJK SC 46 | run: | 47 | curl -fL "https://github.com/notofonts/noto-cjk/releases/download/Serif2.003/09_NotoSerifCJKsc.zip" -o "${{ env.FONT_DIR }}/noto-serif-sc.zip" 48 | 49 | - name: Download Noto Sans CJK SC 50 | run: | 51 | curl -fL "https://github.com/notofonts/noto-cjk/releases/download/Sans2.004/08_NotoSansCJKsc.zip" -o "${{ env.FONT_DIR }}/noto-sans-sc.zip" 52 | 53 | - name: Download LXGW WenKai 54 | run: | 55 | curl -fL "https://github.com/lxgw/LxgwWenKai/releases/download/v1.521/lxgw-wenkai-v1.521.zip" -o "${{ env.FONT_DIR }}/lxgw-wenkai.zip" 56 | 57 | - name: Download Courier Prime Code 58 | run: | 59 | curl -fL "https://quoteunquoteapps.com/courierprime/downloads/courier-prime-code.zip" -o "${{ env.FONT_DIR }}/courier-prime-code.zip" 60 | 61 | - name: Install font 62 | run: | 63 | # Unzip everything 64 | for z in "${{ env.FONT_DIR }}"/*.zip; do 65 | [ -f "$z" ] || continue 66 | echo "Unzipping $z" 67 | unzip -o "$z" -d "${{ env.FONT_DIR }}" || true 68 | done 69 | sudo mkdir -p /usr/local/share/fonts/typst/ 70 | sudo cp -r "${{ env.FONT_DIR }}" /usr/local/share/fonts/typst 71 | sudo fc-cache -f -v 72 | 73 | - uses: actions/cache@v4 74 | with: 75 | path: ${{ env.FONT_DIR }} 76 | key: fonts-${{ runner.os }}-${{ env.FONT_VERSION }} 77 | 78 | - uses: typst-community/setup-typst@v4 79 | 80 | - name: Build manual 81 | run: | 82 | typst compile "manual/src/content/manual.typ" "manual/llmanual.pdf" --features html 83 | 84 | - name: Upload PDF 85 | uses: actions/upload-artifact@v4 86 | with: 87 | name: llmanual.pdf 88 | path: manual/llmanual.pdf 89 | -------------------------------------------------------------------------------- /manual/src/content/docs/intro.typ: -------------------------------------------------------------------------------- 1 | #import "../html.typ" : * 2 | #show : html-style 3 | #metadata(( title: "简介" )) 4 | 5 | = 简介 6 | 7 | == 历史 8 | 9 | #link("https://github.com/Project-LemonLime/Project_LemonLime")[LemonLime] 项目是 #link("https://github.com/Dust1404/Project_LemonPlus")[LemonPlus] 的二次开发。 10 | 11 | #v(2em) 12 | 13 | 2011 年,Jia Zhipeng 开发完成了 Lemon,一个开源的评测工具。 14 | 15 | 虽然我们并不知道他确切的动机, 16 | 17 | ——是想弥补 Linux 下没有评测工具的缺陷,还是想造出一个 Cena 的对手, 18 | 19 | 不管怎样,他的愿望达成了,Lemon 的历史也从此开始。 20 | 21 | 现在(2020 年)他已经是一名得克萨斯大学奥斯汀分校的 2 年级博士生了,祝他的学业生涯顺利。 22 | 23 | #v(1em) 24 | 25 | 2018 年,在 浮尘ii\* 的努力下,Lemon 进化为 LemonPlus。 26 | 27 | LemonPlus 成功兼容了 Qt 5, 28 | 29 | 并且新增了适合新时代 OI 的功能,移除了一些不稳定的、过时的功能。 30 | 31 | 奈何 OI 残酷,岁月更迁,物是人非,浮尘ii\* 在 2019 年退役了, 32 | 33 | LemonPlus 的开发也相应地结束了。 34 | 35 | #v(1em) 36 | 37 | 2019 年,iotang 开始魔改 Lemon。 38 | 39 | 起初,是想给 Lemon 增添一点用户体验—— 40 | 41 | 添加更多的颜色、优化界面,以及修改令人崩溃的重测逻辑, 42 | 43 | 但是误打误撞地在 Github 上发现了 LemonPlus。 44 | 45 | 可怜的 iotang 当时还不会用 Pull Request,所以 LemonLime 诞生了。 46 | 47 | LemonLime 继承了 LemonPlus 和 LemonMt(这个大概只有长郡人才会知道)的优点。 48 | 49 | #v(1em) 50 | 51 | 放眼望去,Lemon 的历史就像是传火。 52 | 53 | Lemon 开发要想继续,就必须要燃烧 Oier 的灵魂。 54 | 55 | 2020 年,随着政策巨变,iotang 也在退役的路上了。 56 | 57 | 虽然退役并不代表开发停止,但是开发 Lemon 的热情将再也不能体会到了。 58 | 59 | 也只能可惜 iotang 不够强了吧。 60 | 61 | #v(1em) 62 | 63 | 2020 年,因为功能欠缺的 qmake, 64 | 65 | Ceolacanthus 开始了把 LemonLime 从 QMake 迁移到 CMake 的路程。 66 | 67 | 又因为不想每次都编译,她开始改造 LemonLime 的 CI, 68 | 69 | 试图完成自动编译和打包。 70 | 71 | 2020 年底,她成功的让 LemonLime 兼容了 Qt6。 72 | 73 | 2021 年,Alphagocc 让 LemonLime 重新支持了多线程评测和新的比赛文件格式 74 | 75 | 当然,这一切都还没完成。 76 | 77 | #v(1em) 78 | 79 | 本篇用户手册是仿照 LemonPlus 的用户手册进行编写的。 80 | 81 | == Lemon 与开源意志 82 | 83 | LemonLime 使用 #link("https://www.gnu.org/licenses/gpl-3.0.html")[GPLv3] 协议。也就是说,LemonLime 是自由软件。 84 | 85 | 自由软件是什么?为什么 LemonLime 选择成为自由软件? 86 | 87 | 自由软件意味着使用者有运行、复制、发布、研究、修改和改进该软件的自由。 88 | 89 | 自由软件是权利问题,不是价格问题。 90 | 91 | 要理解这个概念,你应该考虑"free"是"言论自由(free speech)"中的"自由", 92 | 93 | 而不是"免费啤酒(free beer)"中的"免费"。 94 | 95 | 更精确地说,自由软件赋予软件使用者四项基本自由: 96 | 97 | - 不论目的为何,有运行该软件的自由(自由之零)。 98 | 99 | - 有研究该软件如何工作以及按需改写该软件的自由(自由之一)。 100 | 101 | - 有重新发布拷贝的自由,这样你可以借此来敦亲睦邻(自由之二)。 102 | 103 | - 有向公众发布改进版软件的自由(自由之三),这样整个社群都可因此受惠。 104 | 105 | 不管是 Lemon,还是它的后继者 LemonPlus 和 LemonLime, 106 | 107 | 都恪守着自由软件的意志。 108 | 109 | 这是 LemonPlus 和 LemonLime 诞生的保障, 110 | 111 | 也是 Lemon 不断延续自己的生命的保障。 112 | 113 | #align(center)[ 114 | Lemon,以及它的后继者们,拥抱开源。 115 | 116 | 我们希望,Lemon 可以为全世界的 OIer 带来福祉。 117 | 118 | 让我们一起,把 Lemon 变得更好。 119 | ] 120 | 121 | == 版本兼容性 122 | 123 | LemonLime 近乎完全兼容 LemonPlus,出问题的地方在子任务依赖(为了支持 0 分测试点的让步,不过这个问题将会被解决)。不过这个问题在使用对应平台重新测试后就可以自动解决。 124 | 125 | LemonLime 完全兼容 Lemon。 126 | 127 | LemonLime 主要使用 Qt6 编写,目前支持的最低版本为 Qt 6.8。由于 Qt5 的兼容性不再保证,请在编译时开启 `-DLEMON_QT6=ON` 参数,若不开启可能无法编译(该参数计划在未来移除)。 128 | 129 | LemonLime 可以使用 `GCC`, `Clang` 和 `MSVC` 进行编译 -------------------------------------------------------------------------------- /.github/workflows/cpack-deb-ubuntu.yml.bak: -------------------------------------------------------------------------------- 1 | name: CPack - DEB - Ubuntu 2 | 3 | on: 4 | # push代码时触发workflow 5 | push: 6 | # 忽略README.md 7 | paths-ignore: 8 | - 'README.md' 9 | - 'LICENSE' 10 | - 'BUILD.md' 11 | # pull_request时触发workflow 12 | pull_request: 13 | # 忽略README.md 14 | paths-ignore: 15 | - 'README.md' 16 | - 'LICENSE' 17 | - 'BUILD.md' 18 | release: 19 | types: [published] 20 | 21 | jobs: 22 | linux: 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | distro: [20.04] 27 | build_type: [Release, RelWithDebInfo] 28 | #arch: [x86, x64] 29 | name: Ubuntu ${{ matrix.distro }} 30 | runs-on: ubuntu-latest 31 | container: ubuntu:${{ matrix.distro }} 32 | 33 | steps: 34 | - name: Install git 35 | run: | 36 | apt-get update 37 | apt-get install -y software-properties-common 38 | add-apt-repository ppa:git-core/ppa 39 | apt-get update 40 | apt-get install -y git 41 | - name: Get the version 42 | id: get_version 43 | shell: bash 44 | run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3)" >> $GITHUB_OUTPUT 45 | - name: Checking out sources 46 | uses: actions/checkout@v4 47 | with: 48 | submodules: 'recursive' 49 | - name: Install build dependencies 50 | run: | 51 | DEBIAN_FRONTEND=noninteractive apt-get install -y keyboard-configuration 52 | apt-get install -y build-essential ninja-build qt6-base-dev qt6-tools-dev qt6-tools-dev-tools libqt6help6 cmake pkgconf bash 53 | - name: Build 54 | run: | 55 | mkdir build 56 | cd build 57 | cmake .. -GNinja -DEMBED_TRANSLATIONS=OFF -DEMBED_DOCS=OFF -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DBUILD_DEB=ON -DLEMON_BUILD_INFO="Build for Ubuntu" -DLEMON_BUILD_EXTRA_INFO="Build on $(uname -sr)" 58 | cmake --build . --target package 59 | - name: Get package name 60 | shell: bash 61 | id: get_package 62 | run: | 63 | echo "NAME=$(basename build/LemonLime-*.deb)" >> $GITHUB_OUTPUT 64 | - name: Upload artifact 65 | uses: actions/upload-artifact@v4 66 | with: 67 | name: LemonLime-${{ steps.get_version.outputs.VERSION }}-ubuntu-${{ matrix.distro }}-${{ matrix.build_type }}.deb 68 | path: build/${{ steps.get_package.outputs.NAME }} 69 | - name: Upload binaries to release 70 | uses: svenstaro/upload-release-action@v2 71 | if: github.event_name == 'release' 72 | with: 73 | repo_token: ${{ secrets.GITHUB_TOKEN }} 74 | file: build/${{ steps.get_package.outputs.NAME }} 75 | asset_name: LemonLime-${{ steps.get_version.outputs.VERSION }}-ubuntu-${{ matrix.distro }}-${{ matrix.build_type }}.deb 76 | tag: ${{ github.ref }} 77 | overwrite: true 78 | -------------------------------------------------------------------------------- /resource.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | assets/pics/splash2.png 4 | 5 | 6 | makespec/VERSION 7 | makespec/VERSIONSUFFIX 8 | assets/lemon-lime.ico 9 | 10 | 11 | assets/icons/lemon-lime.png 12 | assets/pics/code-function.svg 13 | assets/pics/configure.svg 14 | assets/pics/dialog-cancel.svg 15 | assets/pics/dialog-ok-apply.svg 16 | assets/pics/document-edit.svg 17 | assets/pics/edit-clear.svg 18 | assets/pics/edit-delete.svg 19 | assets/pics/edit-find-replace.svg 20 | assets/pics/edit-find.svg 21 | assets/pics/go-down.svg 22 | assets/pics/go-up.svg 23 | assets/pics/list-add.svg 24 | assets/pics/list-remove.svg 25 | assets/pics/media-playback-stop.svg 26 | assets/pics/media-skip-forward.svg 27 | assets/pics/paint-none.svg 28 | assets/pics/view-refresh.svg 29 | assets/pics/view-sort.svg 30 | assets/pics/application-exit.svg 31 | assets/pics/deletecell.svg 32 | assets/pics/document-close.svg 33 | assets/pics/document-export.svg 34 | assets/pics/document-new.svg 35 | assets/pics/document-save.svg 36 | assets/pics/globe.svg 37 | assets/pics/go-parent-folder.svg 38 | assets/pics/help-about.svg 39 | assets/pics/layer-new.svg 40 | assets/pics/paint-unknown.svg 41 | assets/pics/quickopen-file.svg 42 | assets/pics/system-help.svg 43 | assets/pics/acrobat.svg 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/addtaskdialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011-2018 Project Lemon, Zhipeng Jia 3 | * SPDX-FileCopyrightText: 2018-2019 Project LemonPlus, Dust1404 4 | * SPDX-FileCopyrightText: 2019-2022 Project LemonLime 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include "addtaskdialog.h" 11 | #include "ui_addtaskdialog.h" 12 | // 13 | #include "base/settings.h" 14 | // 15 | 16 | AddTaskDialog::AddTaskDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AddTaskDialog) { 17 | ui->setupUi(this); 18 | ui->fullScore->setValidator(new QIntValidator(1, Settings::upperBoundForFullScore() * 100, this)); 19 | ui->timeLimit->setValidator(new QIntValidator(1, Settings::upperBoundForTimeLimit(), this)); 20 | ui->memoryLimit->setValidator(new QIntValidator(1, Settings::upperBoundForMemoryLimit(), this)); 21 | connect(ui->taskBox, qOverload(&QComboBox::currentIndexChanged), this, 22 | &AddTaskDialog::taskBoxIndexChanged); 23 | connect(ui->fullScore, &QLineEdit::textChanged, this, &AddTaskDialog::fullScoreChanged); 24 | connect(ui->timeLimit, &QLineEdit::textChanged, this, &AddTaskDialog::timeLimitChanged); 25 | connect(ui->memoryLimit, &QLineEdit::textChanged, this, &AddTaskDialog::memoryLimitChanged); 26 | } 27 | 28 | AddTaskDialog::~AddTaskDialog() { delete ui; } 29 | 30 | void AddTaskDialog::addTask(const QString &title, int _fullScore, int _timeLimit, int _memoryLimit) { 31 | fullScore.append(_fullScore); 32 | timeLimit.append(_timeLimit); 33 | memoryLimit.append(_memoryLimit); 34 | ui->taskBox->addItem(title); 35 | ui->taskBox->setCurrentIndex(0); 36 | } 37 | 38 | auto AddTaskDialog::getFullScore(int index) const -> int { 39 | if (0 <= index && index < fullScore.size()) { 40 | return fullScore[index]; 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | auto AddTaskDialog::getTimeLimit(int index) const -> int { 47 | if (0 <= index && index < timeLimit.size()) { 48 | return timeLimit[index]; 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | auto AddTaskDialog::getMemoryLimit(int index) const -> int { 55 | if (0 <= index && index < memoryLimit.size()) { 56 | return memoryLimit[index]; 57 | } 58 | 59 | return 0; 60 | } 61 | 62 | void AddTaskDialog::taskBoxIndexChanged() { 63 | int index = ui->taskBox->currentIndex(); 64 | ui->fullScore->setText(QString("%1").arg(fullScore[index])); 65 | ui->timeLimit->setText(QString("%1").arg(timeLimit[index])); 66 | ui->memoryLimit->setText(QString("%1").arg(memoryLimit[index])); 67 | } 68 | 69 | void AddTaskDialog::fullScoreChanged() { 70 | int index = ui->taskBox->currentIndex(); 71 | fullScore[index] = ui->fullScore->text().toInt(); 72 | } 73 | 74 | void AddTaskDialog::timeLimitChanged() { 75 | int index = ui->taskBox->currentIndex(); 76 | timeLimit[index] = ui->timeLimit->text().toInt(); 77 | } 78 | 79 | void AddTaskDialog::memoryLimitChanged() { 80 | int index = ui->taskBox->currentIndex(); 81 | memoryLimit[index] = ui->memoryLimit->text().toInt(); 82 | } 83 | --------------------------------------------------------------------------------