├── debug └── .gitignore ├── release └── .gitignore ├── debian ├── compat ├── source │ ├── format │ └── options ├── docs ├── install ├── menu ├── changelog ├── control ├── rules └── copyright ├── tests └── parser │ ├── debug │ └── .gitignore │ ├── release │ └── .gitignore │ ├── tests │ ├── parseBlocks_001.src │ ├── parseBlocks_001.tgt │ ├── parseBlocks_002.src │ ├── parseBlocks_007.src │ ├── parseBlocks_005.src │ ├── parseBlocks_006.src │ ├── parseBlocks_014.src │ ├── parseBlocks_003.src │ ├── parseBlocks_015.src │ ├── parseBlocks_004.src │ ├── parseBlocks_017.src │ ├── parseBlocks_008.src │ ├── parseBlocks_013.src │ ├── parseBlocks_010.src │ ├── parseBlocks_011.src │ ├── parseBlocks_018.src │ ├── parseBlocks_012.src │ ├── parseBlocks_019.src │ ├── parseBlocks_020.src │ ├── parseBlocks_016.src │ ├── parseBlocks_009.src │ ├── parseBlocks_007.tgt │ ├── parseBlocks_002.tgt │ ├── parseBlocks_013.tgt │ ├── parseBlocks_004.tgt │ ├── parseBlocks_017.tgt │ ├── parseBlocks_003.tgt │ ├── parseBlocks_020.tgt │ ├── parseBlocks_005.tgt │ ├── parseBlocks_008.tgt │ ├── parseBlocks_006.tgt │ ├── parseBlocks_014.tgt │ ├── parseBlocks_015.tgt │ ├── parseBlocks_010.tgt │ ├── parseBlocks_011.tgt │ ├── parseBlocks_019.tgt │ ├── parseBlocks_012.tgt │ ├── parseBlocks_018.tgt │ ├── parseBlocks_009.tgt │ └── parseBlocks_016.tgt │ ├── test.sh │ ├── .gitignore │ ├── avalon_test.pro │ └── parser_test.h ├── src ├── icons │ ├── main.ico │ ├── main.png │ ├── bold24.png │ ├── char24.png │ ├── code24.png │ ├── copy16.png │ ├── date16.png │ ├── date22.png │ ├── date24.png │ ├── date32.png │ ├── date48.png │ ├── date64.png │ ├── edit16.png │ ├── edit22.png │ ├── edit32.png │ ├── edit48.png │ ├── edit64.png │ ├── exit16.png │ ├── google.ico │ ├── help16.png │ ├── new128.png │ ├── new16.png │ ├── new22.png │ ├── new24.png │ ├── new32.png │ ├── new48.png │ ├── new64.png │ ├── open16.png │ ├── rate_1.png │ ├── rate_2.png │ ├── rate_3.png │ ├── rsdn16.png │ ├── yandex.ico │ ├── avalon.icns │ ├── date128.png │ ├── delete16.png │ ├── delete24.png │ ├── draft24.png │ ├── forward24.png │ ├── image24.png │ ├── italic24.png │ ├── moderate.png │ ├── myunread.png │ ├── quote24.png │ ├── rate_plus.png │ ├── reply16.png │ ├── reply22.png │ ├── reply32.png │ ├── reply48.png │ ├── smile24.png │ ├── storage16.png │ ├── update16.png │ ├── wikipedia.ico │ ├── backward24.png │ ├── childunread.png │ ├── compress16.png │ ├── compress24.png │ ├── compress256.png │ ├── compress32.png │ ├── compress48.png │ ├── download128.png │ ├── download16.png │ ├── download22.png │ ├── download24.png │ ├── download32.png │ ├── download48.png │ ├── download64.png │ ├── hyperlink24.png │ ├── messageread.png │ ├── rate_cross.png │ ├── rate_minus.png │ ├── rate_plus_1.png │ ├── rate_smile.png │ ├── settings128.png │ ├── settings16.png │ ├── settings22.png │ ├── settings24.png │ ├── settings32.png │ ├── settings48.png │ ├── settings64.png │ ├── show_topic.png │ ├── subscribe16.png │ ├── subscribe22.png │ ├── subscribe32.png │ ├── subscribe48.png │ ├── subscribe64.png │ ├── messageunread.png │ ├── slovari.yandex.ico │ ├── synchronize16.png │ ├── synchronize24.png │ ├── synchronize256.png │ ├── synchronize32.png │ ├── synchronize48.png │ ├── unsubscribe16.png │ ├── viewsource16.png │ ├── viewsource22.png │ ├── viewsource32.png │ ├── viewsource48.png │ ├── viewsource64.png │ ├── google.translate.ico │ ├── markallasread16.png │ ├── markallasunread16.png │ ├── markpatrialasread16.png │ ├── nextunreadarticle16.png │ ├── nextunreadarticle24.png │ ├── nextunreadforum16.png │ ├── nextunreadforum24.png │ ├── nextunreadthread16.png │ ├── nextunreadthread24.png │ └── markpatrialasunread16.png ├── smiles │ ├── cry.png │ ├── poo.png │ ├── sad.png │ ├── angel.png │ ├── angry.png │ ├── beer.png │ ├── blink.png │ ├── blush.png │ ├── bomb.png │ ├── cool.png │ ├── devil.png │ ├── drool.png │ ├── grin.png │ ├── happy.png │ ├── kiss.png │ ├── music.png │ ├── pouty.png │ ├── rose.png │ ├── shock.png │ ├── sick.png │ ├── sleep.png │ ├── smile.png │ ├── stop.png │ ├── tired.png │ ├── wacko.png │ ├── wink.png │ ├── wrong.png │ ├── biggrin.png │ ├── getlost.png │ ├── in_love.png │ ├── kissed.png │ ├── kissing.png │ ├── laughing.png │ ├── rolleyes.png │ ├── shocked.png │ ├── shut_up.png │ ├── sideways.png │ ├── teethy.png │ ├── tongue.png │ ├── thumb_down.png │ └── thumbs_up.png ├── translations │ └── qt_ru_RU.qm ├── version.h ├── model │ ├── all.h │ ├── row_version.h │ ├── forum_group.h │ ├── group.h │ ├── user.h │ ├── moderate.h │ ├── commit.h │ ├── forum.h │ └── rating.h ├── interfaces.h ├── icon_effect.h ├── icon_effect.cpp ├── storage │ ├── storage_factory.h │ ├── storage_factory.cpp │ ├── database_error.cpp │ ├── database_error.h │ ├── query.h │ ├── query.cpp │ ├── database.cpp │ ├── database.h │ ├── mysql_database.h │ ├── sqlite_database.h │ ├── sqlite_database.cpp │ ├── mysql_storage.h │ ├── sqlite_storage.h │ └── mysql_database.cpp ├── forms │ ├── form_input.cpp │ ├── form_source.h │ ├── form_source.cpp │ ├── form_input.h │ ├── form_source_ui.h │ ├── form_date.h │ ├── form_subscribe.h │ ├── form_date_ui.h │ ├── form_request_ui.h │ ├── form_subscribe_ui.h │ ├── form_date.cpp │ ├── form_input_ui.h │ ├── form_settings.h │ ├── iform_main.h │ ├── form_moderate.h │ ├── form_input_ui.cpp │ ├── form_request_ui.cpp │ ├── form_date_ui.cpp │ ├── form_source_ui.cpp │ ├── form_subscribe_ui.cpp │ ├── form_message.h │ ├── form_moderate_ui.h │ ├── form_request.h │ ├── form_main.h │ ├── form_moderate.cpp │ └── form_main_ui.h ├── iprogress.h ├── widgets │ ├── imessage_view.h │ ├── spell_edit.h │ ├── message_view.h │ ├── iforum_tree.h │ ├── imessage_tree.h │ ├── web_view.h │ ├── spell_edit.cpp │ ├── message_view.cpp │ └── forum_tree.h ├── resource.rc ├── global.h ├── version.cpp ├── main.cpp ├── global.cpp ├── spellchecker.h ├── style.css ├── formatter.h ├── webservice.h └── resource.qrc ├── README.md ├── avalon.desktop ├── .gitignore ├── .travis.yml └── avalon.spec /debug/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /release/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /tests/parser/debug/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/parser/release/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_001.src: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_001.tgt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_002.src: -------------------------------------------------------------------------------- 1 | Тест простого текста без тэгов 2 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_007.src: -------------------------------------------------------------------------------- 1 | [code]однострочный код[/code] 2 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_005.src: -------------------------------------------------------------------------------- 1 | Многострочный тест 2 | без квот 3 | -------------------------------------------------------------------------------- /src/icons/main.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/main.ico -------------------------------------------------------------------------------- /src/icons/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/main.png -------------------------------------------------------------------------------- /src/smiles/cry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/cry.png -------------------------------------------------------------------------------- /src/smiles/poo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/poo.png -------------------------------------------------------------------------------- /src/smiles/sad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/sad.png -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_006.src: -------------------------------------------------------------------------------- 1 | А>Многострочный тест 2 | А>>Б> с квотами 3 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_014.src: -------------------------------------------------------------------------------- 1 | [cut] 2 | Вырезанный блок 3 | [/cut] 4 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | src/sql/avalon.mysql.sql 3 | src/sql/avalon.sqlite.sql 4 | -------------------------------------------------------------------------------- /src/icons/bold24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/bold24.png -------------------------------------------------------------------------------- /src/icons/char24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/char24.png -------------------------------------------------------------------------------- /src/icons/code24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/code24.png -------------------------------------------------------------------------------- /src/icons/copy16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/copy16.png -------------------------------------------------------------------------------- /src/icons/date16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/date16.png -------------------------------------------------------------------------------- /src/icons/date22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/date22.png -------------------------------------------------------------------------------- /src/icons/date24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/date24.png -------------------------------------------------------------------------------- /src/icons/date32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/date32.png -------------------------------------------------------------------------------- /src/icons/date48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/date48.png -------------------------------------------------------------------------------- /src/icons/date64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/date64.png -------------------------------------------------------------------------------- /src/icons/edit16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/edit16.png -------------------------------------------------------------------------------- /src/icons/edit22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/edit22.png -------------------------------------------------------------------------------- /src/icons/edit32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/edit32.png -------------------------------------------------------------------------------- /src/icons/edit48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/edit48.png -------------------------------------------------------------------------------- /src/icons/edit64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/edit64.png -------------------------------------------------------------------------------- /src/icons/exit16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/exit16.png -------------------------------------------------------------------------------- /src/icons/google.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/google.ico -------------------------------------------------------------------------------- /src/icons/help16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/help16.png -------------------------------------------------------------------------------- /src/icons/new128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/new128.png -------------------------------------------------------------------------------- /src/icons/new16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/new16.png -------------------------------------------------------------------------------- /src/icons/new22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/new22.png -------------------------------------------------------------------------------- /src/icons/new24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/new24.png -------------------------------------------------------------------------------- /src/icons/new32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/new32.png -------------------------------------------------------------------------------- /src/icons/new48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/new48.png -------------------------------------------------------------------------------- /src/icons/new64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/new64.png -------------------------------------------------------------------------------- /src/icons/open16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/open16.png -------------------------------------------------------------------------------- /src/icons/rate_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rate_1.png -------------------------------------------------------------------------------- /src/icons/rate_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rate_2.png -------------------------------------------------------------------------------- /src/icons/rate_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rate_3.png -------------------------------------------------------------------------------- /src/icons/rsdn16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rsdn16.png -------------------------------------------------------------------------------- /src/icons/yandex.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/yandex.ico -------------------------------------------------------------------------------- /src/smiles/angel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/angel.png -------------------------------------------------------------------------------- /src/smiles/angry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/angry.png -------------------------------------------------------------------------------- /src/smiles/beer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/beer.png -------------------------------------------------------------------------------- /src/smiles/blink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/blink.png -------------------------------------------------------------------------------- /src/smiles/blush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/blush.png -------------------------------------------------------------------------------- /src/smiles/bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/bomb.png -------------------------------------------------------------------------------- /src/smiles/cool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/cool.png -------------------------------------------------------------------------------- /src/smiles/devil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/devil.png -------------------------------------------------------------------------------- /src/smiles/drool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/drool.png -------------------------------------------------------------------------------- /src/smiles/grin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/grin.png -------------------------------------------------------------------------------- /src/smiles/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/happy.png -------------------------------------------------------------------------------- /src/smiles/kiss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/kiss.png -------------------------------------------------------------------------------- /src/smiles/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/music.png -------------------------------------------------------------------------------- /src/smiles/pouty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/pouty.png -------------------------------------------------------------------------------- /src/smiles/rose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/rose.png -------------------------------------------------------------------------------- /src/smiles/shock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/shock.png -------------------------------------------------------------------------------- /src/smiles/sick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/sick.png -------------------------------------------------------------------------------- /src/smiles/sleep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/sleep.png -------------------------------------------------------------------------------- /src/smiles/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/smile.png -------------------------------------------------------------------------------- /src/smiles/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/stop.png -------------------------------------------------------------------------------- /src/smiles/tired.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/tired.png -------------------------------------------------------------------------------- /src/smiles/wacko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/wacko.png -------------------------------------------------------------------------------- /src/smiles/wink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/wink.png -------------------------------------------------------------------------------- /src/smiles/wrong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/wrong.png -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_003.src: -------------------------------------------------------------------------------- 1 | A> Тест простого текста без тэгов с квотой 2 | -------------------------------------------------------------------------------- /src/icons/avalon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/avalon.icns -------------------------------------------------------------------------------- /src/icons/date128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/date128.png -------------------------------------------------------------------------------- /src/icons/delete16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/delete16.png -------------------------------------------------------------------------------- /src/icons/delete24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/delete24.png -------------------------------------------------------------------------------- /src/icons/draft24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/draft24.png -------------------------------------------------------------------------------- /src/icons/forward24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/forward24.png -------------------------------------------------------------------------------- /src/icons/image24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/image24.png -------------------------------------------------------------------------------- /src/icons/italic24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/italic24.png -------------------------------------------------------------------------------- /src/icons/moderate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/moderate.png -------------------------------------------------------------------------------- /src/icons/myunread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/myunread.png -------------------------------------------------------------------------------- /src/icons/quote24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/quote24.png -------------------------------------------------------------------------------- /src/icons/rate_plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rate_plus.png -------------------------------------------------------------------------------- /src/icons/reply16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/reply16.png -------------------------------------------------------------------------------- /src/icons/reply22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/reply22.png -------------------------------------------------------------------------------- /src/icons/reply32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/reply32.png -------------------------------------------------------------------------------- /src/icons/reply48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/reply48.png -------------------------------------------------------------------------------- /src/icons/smile24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/smile24.png -------------------------------------------------------------------------------- /src/icons/storage16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/storage16.png -------------------------------------------------------------------------------- /src/icons/update16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/update16.png -------------------------------------------------------------------------------- /src/icons/wikipedia.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/wikipedia.ico -------------------------------------------------------------------------------- /src/smiles/biggrin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/biggrin.png -------------------------------------------------------------------------------- /src/smiles/getlost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/getlost.png -------------------------------------------------------------------------------- /src/smiles/in_love.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/in_love.png -------------------------------------------------------------------------------- /src/smiles/kissed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/kissed.png -------------------------------------------------------------------------------- /src/smiles/kissing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/kissing.png -------------------------------------------------------------------------------- /src/smiles/laughing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/laughing.png -------------------------------------------------------------------------------- /src/smiles/rolleyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/rolleyes.png -------------------------------------------------------------------------------- /src/smiles/shocked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/shocked.png -------------------------------------------------------------------------------- /src/smiles/shut_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/shut_up.png -------------------------------------------------------------------------------- /src/smiles/sideways.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/sideways.png -------------------------------------------------------------------------------- /src/smiles/teethy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/teethy.png -------------------------------------------------------------------------------- /src/smiles/tongue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/tongue.png -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_015.src: -------------------------------------------------------------------------------- 1 | [cut=Развернуть] 2 | Вырезанный блок 3 | [/cut] 4 | -------------------------------------------------------------------------------- /src/icons/backward24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/backward24.png -------------------------------------------------------------------------------- /src/icons/childunread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/childunread.png -------------------------------------------------------------------------------- /src/icons/compress16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/compress16.png -------------------------------------------------------------------------------- /src/icons/compress24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/compress24.png -------------------------------------------------------------------------------- /src/icons/compress256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/compress256.png -------------------------------------------------------------------------------- /src/icons/compress32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/compress32.png -------------------------------------------------------------------------------- /src/icons/compress48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/compress48.png -------------------------------------------------------------------------------- /src/icons/download128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/download128.png -------------------------------------------------------------------------------- /src/icons/download16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/download16.png -------------------------------------------------------------------------------- /src/icons/download22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/download22.png -------------------------------------------------------------------------------- /src/icons/download24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/download24.png -------------------------------------------------------------------------------- /src/icons/download32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/download32.png -------------------------------------------------------------------------------- /src/icons/download48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/download48.png -------------------------------------------------------------------------------- /src/icons/download64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/download64.png -------------------------------------------------------------------------------- /src/icons/hyperlink24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/hyperlink24.png -------------------------------------------------------------------------------- /src/icons/messageread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/messageread.png -------------------------------------------------------------------------------- /src/icons/rate_cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rate_cross.png -------------------------------------------------------------------------------- /src/icons/rate_minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rate_minus.png -------------------------------------------------------------------------------- /src/icons/rate_plus_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rate_plus_1.png -------------------------------------------------------------------------------- /src/icons/rate_smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/rate_smile.png -------------------------------------------------------------------------------- /src/icons/settings128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/settings128.png -------------------------------------------------------------------------------- /src/icons/settings16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/settings16.png -------------------------------------------------------------------------------- /src/icons/settings22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/settings22.png -------------------------------------------------------------------------------- /src/icons/settings24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/settings24.png -------------------------------------------------------------------------------- /src/icons/settings32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/settings32.png -------------------------------------------------------------------------------- /src/icons/settings48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/settings48.png -------------------------------------------------------------------------------- /src/icons/settings64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/settings64.png -------------------------------------------------------------------------------- /src/icons/show_topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/show_topic.png -------------------------------------------------------------------------------- /src/icons/subscribe16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/subscribe16.png -------------------------------------------------------------------------------- /src/icons/subscribe22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/subscribe22.png -------------------------------------------------------------------------------- /src/icons/subscribe32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/subscribe32.png -------------------------------------------------------------------------------- /src/icons/subscribe48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/subscribe48.png -------------------------------------------------------------------------------- /src/icons/subscribe64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/subscribe64.png -------------------------------------------------------------------------------- /src/smiles/thumb_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/thumb_down.png -------------------------------------------------------------------------------- /src/smiles/thumbs_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/smiles/thumbs_up.png -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_004.src: -------------------------------------------------------------------------------- 1 | Тест [i]текста[u] с[/i] простыми[/u] [strike]тэгами[/s] 2 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_017.src: -------------------------------------------------------------------------------- 1 | Это текст без [cut=развернуть] закрытия тэга врезки 2 | -------------------------------------------------------------------------------- /src/icons/messageunread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/messageunread.png -------------------------------------------------------------------------------- /src/icons/slovari.yandex.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/slovari.yandex.ico -------------------------------------------------------------------------------- /src/icons/synchronize16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/synchronize16.png -------------------------------------------------------------------------------- /src/icons/synchronize24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/synchronize24.png -------------------------------------------------------------------------------- /src/icons/synchronize256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/synchronize256.png -------------------------------------------------------------------------------- /src/icons/synchronize32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/synchronize32.png -------------------------------------------------------------------------------- /src/icons/synchronize48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/synchronize48.png -------------------------------------------------------------------------------- /src/icons/unsubscribe16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/unsubscribe16.png -------------------------------------------------------------------------------- /src/icons/viewsource16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/viewsource16.png -------------------------------------------------------------------------------- /src/icons/viewsource22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/viewsource22.png -------------------------------------------------------------------------------- /src/icons/viewsource32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/viewsource32.png -------------------------------------------------------------------------------- /src/icons/viewsource48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/viewsource48.png -------------------------------------------------------------------------------- /src/icons/viewsource64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/viewsource64.png -------------------------------------------------------------------------------- /src/translations/qt_ru_RU.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/translations/qt_ru_RU.qm -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_008.src: -------------------------------------------------------------------------------- 1 | [code] 2 | многострочный код 3 | на разных строках[/code] 4 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_013.src: -------------------------------------------------------------------------------- 1 | A> 2 | A> 3 | A> 4 | A>пустой квотинг 5 | A> 6 | A> 7 | A> 8 | -------------------------------------------------------------------------------- /src/icons/google.translate.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/google.translate.ico -------------------------------------------------------------------------------- /src/icons/markallasread16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/markallasread16.png -------------------------------------------------------------------------------- /src/icons/markallasunread16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/markallasunread16.png -------------------------------------------------------------------------------- /src/icons/markpatrialasread16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/markpatrialasread16.png -------------------------------------------------------------------------------- /src/icons/nextunreadarticle16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/nextunreadarticle16.png -------------------------------------------------------------------------------- /src/icons/nextunreadarticle24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/nextunreadarticle24.png -------------------------------------------------------------------------------- /src/icons/nextunreadforum16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/nextunreadforum16.png -------------------------------------------------------------------------------- /src/icons/nextunreadforum24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/nextunreadforum24.png -------------------------------------------------------------------------------- /src/icons/nextunreadthread16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/nextunreadthread16.png -------------------------------------------------------------------------------- /src/icons/nextunreadthread24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/nextunreadthread24.png -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_010.src: -------------------------------------------------------------------------------- 1 | [code] 2 | [ccode] 3 | тест блока в блоке 4 | [/ccode] 5 | [/code] 6 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_011.src: -------------------------------------------------------------------------------- 1 | [code] 2 | [ccode] 3 | тест блока 4 | без закрытия тэга 5 | [/ccode] 6 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_018.src: -------------------------------------------------------------------------------- 1 | Это текст с[cut=развернуть]закрытием тэга врезки[/cut]на той же строке 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Актуальная документация по сборке и настройке находится в [wiki](https://github.com/rsdn/avalon/wiki). 2 | -------------------------------------------------------------------------------- /src/icons/markpatrialasunread16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsdn/avalon/HEAD/src/icons/markpatrialasunread16.png -------------------------------------------------------------------------------- /tests/parser/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | qmake avalon_test.pro 4 | make clean 5 | make debug 6 | debug/avalon_test 7 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_012.src: -------------------------------------------------------------------------------- 1 | [code] 2 | [ccode] 3 | тест незакрытого блока 4 | с экранированием 5 | [[/ccode] 6 | -------------------------------------------------------------------------------- /debian/install: -------------------------------------------------------------------------------- 1 | avalon /usr/bin 2 | avalon.desktop /usr/share/applications 3 | src/icons/avalon.xpm /usr/share/pixmaps 4 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_019.src: -------------------------------------------------------------------------------- 1 | Первая строка 2 | 3 | Вторая строка с отступом 4 | 5 | 6 | Третья строка с двумя отступами 7 | 8 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_020.src: -------------------------------------------------------------------------------- 1 | [url=http://www.youtube.com/watch?v=IJNR2EpS0jw]http://www.youtube.com/watch?v=IJNR2EpS0jw[/url] 2 | -------------------------------------------------------------------------------- /tests/parser/.gitignore: -------------------------------------------------------------------------------- 1 | # временные файлы сборки 2 | debug/* 3 | release/* 4 | 5 | # файлы, генерируемые qmake 6 | Makefile 7 | Makefile.Debug 8 | Makefile.Release 9 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_016.src: -------------------------------------------------------------------------------- 1 | [cut=Развернуть 1] 2 | Начало вырезанного блока 1 3 | [cut=Развернуть 2] 4 | Вырезанный блок 2 5 | [/cut] 6 | Окончание вырезанного блока 1 7 | [/cut] 8 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_009.src: -------------------------------------------------------------------------------- 1 | простой текст без квоты 2 | 3 | 4 | A>простой текст сообщения 5 | A> 6 | A>[code] 7 | A>многострочный код 8 | A>на разных строках 9 | A>с квотой 10 | A>[/code] 11 | -------------------------------------------------------------------------------- /debian/menu: -------------------------------------------------------------------------------- 1 | ?package(avalon): \ 2 | needs="X11" \ 3 | section="Applications/Network/Communication" \ 4 | title="Avalon" \ 5 | hints="RSDN offline client" \ 6 | icon="/usr/share/pixmaps/avalon.xpm" \ 7 | command="/usr/bin/avalon" 8 | -------------------------------------------------------------------------------- /avalon.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Encoding=UTF-8 4 | Type=Application 5 | Categories=Network;Feed;Internet;News;Qt; 6 | Name=Avalon 7 | GenericName=RSDN offline client 8 | Exec=avalon 9 | Icon=avalon 10 | Terminal=false 11 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Шаблон файла версии 4 | */ 5 | 6 | #ifndef _avalon_version_h_ 7 | #define _avalon_version_h_ 8 | 9 | /*! 10 | * \brief Номер билда 11 | */ 12 | #define AVALON_BUILD 442 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/model/all.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Список всех используемых моделей 4 | */ 5 | 6 | #ifndef _avalon_model_all_h_ 7 | #define _avalon_model_all_h_ 8 | 9 | #include "user.h" 10 | #include "commit.h" 11 | #include "message.h" 12 | #include "forum_group.h" 13 | #include "row_version.h" 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_007.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtCode 6 | SubType = pbstSourceCode 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = однострочный код 13 | ) 14 | ) 15 | ) 16 | ) 17 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_002.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = Тест простого текста без тэгов 13 | ) 14 | ) 15 | ) 16 | ) 17 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_013.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 1 12 | QuoteText = A> 13 | Data = пустой квотинг 14 | ) 15 | ) 16 | ) 17 | ) 18 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_004.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = Тест [i]текста[u] с[/i] простыми[/u] [s]тэгами[/s] 13 | ) 14 | ) 15 | ) 16 | ) 17 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_017.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = Это текст без [cut=развернуть] закрытия тэга врезки 13 | ) 14 | ) 15 | ) 16 | ) 17 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_003.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 1 12 | QuoteText = A> 13 | Data = Тест простого текста без тэгов с квотой 14 | ) 15 | ) 16 | ) 17 | ) 18 | -------------------------------------------------------------------------------- /src/interfaces.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Заголовочный файл для объединения заголовочных файлов интерфейсов в один 4 | */ 5 | 6 | #ifndef _avalon_interfaces_h_ 7 | #define _avalon_interfaces_h_ 8 | 9 | #include "forms/iform_main.h" 10 | #include "widgets/iforum_tree.h" 11 | #include "widgets/imessage_tree.h" 12 | #include "widgets/imessage_view.h" 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_020.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = [url=http://www.youtube.com/watch?v=IJNR2EpS0jw]http://www.youtube.com/watch?v=IJNR2EpS0jw[/url] 13 | ) 14 | ) 15 | ) 16 | ) 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # выходной бинарник 2 | avalon 3 | 4 | # временные файлы сборки 5 | debug/* 6 | release/* 7 | 8 | # файлы, генерируемые qmake 9 | avalon.pro 10 | Makefile 11 | Makefile.Debug 12 | Makefile.Release 13 | 14 | # файлы, генерируемые qmake под windows 15 | object_script.avalon.Debug 16 | object_script.avalon.Release 17 | 18 | # подсветка синтаксиса (см. http://softwaremaniacs.org/soft/highlight/) 19 | highlight 20 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_005.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = Многострочный тест 13 | ) 14 | [1] = AQuotedString 15 | ( 16 | QuoteLevel = 0 17 | Data = без квот 18 | ) 19 | ) 20 | ) 21 | ) 22 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_008.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtCode 6 | SubType = pbstSourceCode 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = многострочный код 13 | ) 14 | [1] = AQuotedString 15 | ( 16 | QuoteLevel = 0 17 | Data = на разных строках 18 | ) 19 | ) 20 | ) 21 | ) 22 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_006.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 1 12 | QuoteText = А> 13 | Data = Многострочный тест 14 | ) 15 | [1] = AQuotedString 16 | ( 17 | QuoteLevel = 3 18 | QuoteText = А>>Б> 19 | Data = с квотами 20 | ) 21 | ) 22 | ) 23 | ) 24 | -------------------------------------------------------------------------------- /tests/parser/avalon_test.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = avalon_test 3 | CONFIG += debug_and_release warn_on 4 | LIBS += -lcppunit 5 | QT += network sql webkit 6 | DEPENDPATH += . 7 | INCLUDEPATH += . 8 | 9 | build_pass:CONFIG(debug, debug|release) { 10 | DESTDIR = debug 11 | } 12 | 13 | build_pass:CONFIG(release, debug|release) { 14 | DESTDIR = release 15 | } 16 | 17 | HEADERS += parser_test.h 18 | SOURCES += parser_test.cpp ../../src/parser.cpp 19 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_014.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtCut 6 | SubType = pbstBlock 7 | SubBlocks = AParsedBlockList 8 | ( 9 | [0] = AParsedBlock 10 | ( 11 | Type = pbtText 12 | SubType = pbstText 13 | Strings = AQuotedStringList 14 | ( 15 | [0] = AQuotedString 16 | ( 17 | QuoteLevel = 0 18 | Data = Вырезанный блок 19 | ) 20 | ) 21 | ) 22 | ) 23 | ) 24 | ) 25 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_015.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtCut 6 | SubType = pbstBlock 7 | Title = Развернуть 8 | SubBlocks = AParsedBlockList 9 | ( 10 | [0] = AParsedBlock 11 | ( 12 | Type = pbtText 13 | SubType = pbstText 14 | Strings = AQuotedStringList 15 | ( 16 | [0] = AQuotedString 17 | ( 18 | QuoteLevel = 0 19 | Data = Вырезанный блок 20 | ) 21 | ) 22 | ) 23 | ) 24 | ) 25 | ) 26 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_010.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtCode 6 | SubType = pbstSourceCode 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = [ccode] 13 | ) 14 | [1] = AQuotedString 15 | ( 16 | QuoteLevel = 0 17 | Data = тест блока в блоке 18 | ) 19 | [2] = AQuotedString 20 | ( 21 | QuoteLevel = 0 22 | Data = [/ccode] 23 | ) 24 | ) 25 | ) 26 | ) 27 | -------------------------------------------------------------------------------- /debian/source/options: -------------------------------------------------------------------------------- 1 | compression = "bzip2" 2 | compression-level = 9 3 | tar-ignore = .git 4 | tar-ignore = .gitignore 5 | tar-ignore = .travis.yml 6 | tar-ignore = build 7 | tar-ignore = debug 8 | tar-ignore = highlight 9 | tar-ignore = release 10 | tar-ignore = tests 11 | tar-ignore = avalon.spec 12 | tar-ignore = build.linux.sh 13 | tar-ignore = build.mingw.bat 14 | tar-ignore = build.msvc.bat 15 | tar-ignore = update.txt 16 | tar-ignore = src/resource.rc 17 | tar-ignore = src/icons/avalon.icns 18 | tar-ignore = src/icons/main.ico 19 | -------------------------------------------------------------------------------- /src/model/row_version.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Модели для работы с версиями данных (строк) 4 | */ 5 | 6 | #ifndef _avalon_row_version_info_h_ 7 | #define _avalon_row_version_info_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Описатель версий строк 13 | */ 14 | typedef struct ARowVersion 15 | { 16 | QString User; /*!< \brief Пользователи */ 17 | QString Message; /*!< \brief Сообщения */ 18 | QString Rating; /*!< \brief Рейтинг */ 19 | QString Moderate; /*!< \brief Модерилки */ 20 | } ARowVersion; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/icon_effect.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Класс для работы эффектами иконок 4 | */ 5 | 6 | #ifndef _avalon_icon_effect_h_ 7 | #define _avalon_icon_effect_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Класс для работы эффектами иконок 13 | */ 14 | class AIconEffect 15 | { 16 | public: 17 | 18 | /*! 19 | * \brief Объединение 2-х иконок 20 | * \param file1 Иконка-подложка 21 | * \param file2 Иконка-наложение 22 | * \return Иконка с наложением 23 | */ 24 | static QIcon unionIcons (const QString& file1, const QString& file2); 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | avalon (1.0.442) stable; urgency=low 2 | 3 | * Технический релиз для обновления подписи и версии qt. 4 | 5 | -- Anton Batenev Wed, 26 Nov 2014 01:13:00 +0300 6 | 7 | avalon (1.0.441) stable; urgency=low 8 | 9 | * Исправление пути до пользовательского словаря - словарь перемещен 10 | в директорию данных приложения или в текущий путь приложения. 11 | 12 | -- Anton Batenev Thu, 20 Nov 2014 21:51:00 +0300 13 | 14 | avalon (1.0.440) stable; urgency=low 15 | 16 | * Дебианизация. 17 | 18 | -- Anton Batenev Thu, 16 Oct 2014 02:00:00 +0400 19 | -------------------------------------------------------------------------------- /src/icon_effect.cpp: -------------------------------------------------------------------------------- 1 | #include "icon_effect.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | QIcon AIconEffect::unionIcons (const QString& file1, const QString& file2) 5 | { 6 | QImage img_1 = QImage(file1); 7 | QImage img_2 = QImage(file2); 8 | 9 | QPainter painter(&img_1); 10 | 11 | painter.setCompositionMode(QPainter::CompositionMode_SourceOver); 12 | 13 | painter.drawImage(0, 0, img_2); 14 | 15 | painter.end(); 16 | 17 | return QIcon(QPixmap::fromImage(img_1)); 18 | } 19 | //---------------------------------------------------------------------------------------------- 20 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: avalon 2 | Section: news 3 | Priority: optional 4 | Maintainer: Anton Batenev 5 | Build-Depends: debhelper (>= 7.0.50~), libqtwebkit-dev | libqt4-dev (>= 4.4), zlib1g-dev, libaspell-dev 6 | Standards-Version: 3.9.4 7 | Vcs-Git: https://github.com/rsdn/avalon.git 8 | Vcs-Browser: https://github.com/rsdn/avalon 9 | Homepage: https://github.com/rsdn/avalon 10 | 11 | Package: avalon 12 | Architecture: any 13 | Suggests: graphviz 14 | Recommends: aspell-ru, aspell-en 15 | Depends: ${misc:Depends}, ${shlibs:Depends} 16 | Description: RSDN offline client 17 | Offile client for Russian Software Developer Network 18 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_011.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = [code] 13 | ) 14 | ) 15 | ) 16 | [1] = AParsedBlock 17 | ( 18 | Type = pbtC 19 | SubType = pbstSourceCode 20 | Strings = AQuotedStringList 21 | ( 22 | [0] = AQuotedString 23 | ( 24 | QuoteLevel = 0 25 | Data = тест блока 26 | ) 27 | [1] = AQuotedString 28 | ( 29 | QuoteLevel = 0 30 | Data = без закрытия тэга 31 | ) 32 | ) 33 | ) 34 | ) 35 | -------------------------------------------------------------------------------- /src/model/forum_group.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Модели для работы с группами форумов 4 | */ 5 | 6 | #ifndef _avalon_forum_group_info_h_ 7 | #define _avalon_forum_group_info_h_ 8 | 9 | #include "forum.h" 10 | #include "group.h" 11 | 12 | /*! 13 | * \brief Группа описателей форумов 14 | */ 15 | typedef struct AForumGroupInfo 16 | { 17 | /*! 18 | * \brief Описатель группы форумов 19 | */ 20 | AGroupInfo Group; 21 | 22 | /*! 23 | * \brief Список описателей форумов в группе 24 | */ 25 | AForumInfoList Forums; 26 | } AForumGroupInfo; 27 | 28 | /*! 29 | * \brief Дерево форумов 30 | */ 31 | typedef QList AForumGroupInfoList; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/storage/storage_factory.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Работа с хранилищами данных 4 | */ 5 | 6 | #ifndef _avalon_storage_factory_h_ 7 | #define _avalon_storage_factory_h_ 8 | 9 | #include "istorage.h" 10 | 11 | /*! 12 | * \brief Фабрика хранилищ данных 13 | * Предназначена для создания экземпляров классов для работы с хранилищем установленного в настройках приложения типа 14 | */ 15 | class AStorageFactory 16 | { 17 | public: 18 | 19 | /*! 20 | * \brief Получение хранилища типа определенного в настройках 21 | * \return хранилище типа определенного в настройках или NULL, если тип не определен 22 | */ 23 | static IAStorage* getStorage (); 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/model/group.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Модели для работы группами форумов 4 | */ 5 | 6 | #ifndef _avalon_group_info_h_ 7 | #define _avalon_group_info_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Описатель группы форумов 13 | */ 14 | typedef struct AGroupInfo 15 | { 16 | int ID; /*!< \brief ID группы */ 17 | QString Name; /*!< \brief Наименование группы */ 18 | int SortOrder; /*!< \brief Порядок сортировки */ 19 | } AGroupInfo; 20 | 21 | /*! 22 | * \brief Список групп 23 | */ 24 | typedef QList AGroupInfoList; 25 | 26 | /*! 27 | * \brief ID группы для служебных сообщений ("локальные") 28 | */ 29 | const int SPECIAL_ID_GROUP = -1; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_019.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = Первая строка 13 | ) 14 | [1] = AQuotedString 15 | ( 16 | QuoteLevel = 0 17 | Data = 18 | ) 19 | [2] = AQuotedString 20 | ( 21 | QuoteLevel = 0 22 | Data = Вторая строка с отступом 23 | ) 24 | [3] = AQuotedString 25 | ( 26 | QuoteLevel = 0 27 | Data = 28 | ) 29 | [4] = AQuotedString 30 | ( 31 | QuoteLevel = 0 32 | Data = Третья строка с двумя отступами 33 | ) 34 | ) 35 | ) 36 | ) 37 | -------------------------------------------------------------------------------- /src/forms/form_input.cpp: -------------------------------------------------------------------------------- 1 | #include "form_input.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | FormInput::FormInput (QWidget* parent, const QString& header, const QString& message, const QString& text) : FormInputUI (parent, header, message, text) 5 | { 6 | } 7 | //---------------------------------------------------------------------------------------------- 8 | 9 | FormInput::~FormInput () 10 | { 11 | } 12 | //---------------------------------------------------------------------------------------------- 13 | 14 | QString FormInput::text () 15 | { 16 | return m_text->text(); 17 | } 18 | //---------------------------------------------------------------------------------------------- 19 | -------------------------------------------------------------------------------- /src/iprogress.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Интерфейс для отображения прогресса 4 | */ 5 | 6 | #ifndef _avalon_iprogress_h_ 7 | #define _avalon_iprogress_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Интерфейс для отображения прогресса выполнения к-л операции 13 | */ 14 | class IProgress 15 | { 16 | public: 17 | 18 | /*! 19 | * \brief Прогресс 20 | * \param percent Количество выполненых процентов 0..100 21 | */ 22 | virtual void onProgress (int percent) = 0; 23 | 24 | /*! 25 | * \brief Прогресс 26 | * \param percent Количество выполненых процентов 0..100 27 | * \param status Состояние операции 28 | */ 29 | virtual void onProgress (int percent, const QString& status) = 0; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/forms/form_source.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Форма просмотра сообщения в raw 4 | */ 5 | 6 | #ifndef _avalon_form_source_h_ 7 | #define _avalon_form_source_h_ 8 | 9 | #include "form_source_ui.h" 10 | 11 | /*! 12 | * \brief Форма просмотра сообщения в raw 13 | */ 14 | class FormSource : public FormSourceUI 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | /*! 21 | * \brief Конструктор 22 | * \param parent Родительский виджет (форма) 23 | * \param body RAW текст сообщения 24 | */ 25 | FormSource (QWidget* parent, const QString& body); 26 | ~FormSource (); 27 | 28 | protected: 29 | 30 | /*! 31 | * \brief Событие закрытия формы (см. Qt::QDialog). 32 | */ 33 | void closeEvent (QCloseEvent* event); 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_012.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = [code] 13 | ) 14 | ) 15 | ) 16 | [1] = AParsedBlock 17 | ( 18 | Type = pbtC 19 | SubType = pbstSourceCode 20 | Strings = AQuotedStringList 21 | ( 22 | [0] = AQuotedString 23 | ( 24 | QuoteLevel = 0 25 | Data = тест незакрытого блока 26 | ) 27 | [1] = AQuotedString 28 | ( 29 | QuoteLevel = 0 30 | Data = с экранированием 31 | ) 32 | [2] = AQuotedString 33 | ( 34 | QuoteLevel = 0 35 | Data = [ 36 | ) 37 | ) 38 | ) 39 | ) 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - gcc 5 | 6 | install: 7 | - sudo apt-get -y install libqt4-dev libqtwebkit-dev zlib1g-dev libaspell-dev 8 | 9 | script: 10 | - ./build.linux.sh 11 | 12 | env: 13 | global: 14 | - secure: "Zv8INB/fGwVKZzqzTPEDvsrZ+Eihi2moFOLkYvVfg2kfD9jnhZCIpSdwgzmBIHvuY793GpiBysoR/GyKlQ5hf82mOhAwfDnpFkV9GfNMaRQaK9wsAQS9CYo6j8dHSkSi/9zZXNhrwBGfxYxGxjLALozgJcMt7NMfF3e1ZNiCTtc=" 15 | 16 | addons: 17 | coverity_scan: 18 | project: 19 | name: "rsdn/avalon" 20 | description: "Build submitted via Travis CI" 21 | notification_email: antonbatenev@yandex.ru 22 | build_command_prepend: true 23 | build_command: ./build.linux.sh 24 | branch_pattern: master 25 | 26 | branches: 27 | only: 28 | - master 29 | -------------------------------------------------------------------------------- /src/storage/storage_factory.cpp: -------------------------------------------------------------------------------- 1 | #include "storage_factory.h" 2 | //---------------------------------------------------------------------------------------------- 3 | #include "mysql_storage.h" 4 | #include "sqlite_storage.h" 5 | //---------------------------------------------------------------------------------------------- 6 | 7 | IAStorage* AStorageFactory::getStorage () 8 | { 9 | // чтение типа хранилища из конфига 10 | QSettings settings; 11 | 12 | QString type = settings.value("storage/type", "SQLite").toString(); 13 | 14 | if (type == "MySQL") 15 | return new AMySQLStorage(); 16 | else if (type == "SQLite") 17 | return new ASQLiteStorage(); 18 | 19 | return NULL; 20 | } 21 | //---------------------------------------------------------------------------------------------- 22 | -------------------------------------------------------------------------------- /src/widgets/imessage_view.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Интерфейс для работы с областью отображения HTML сообщения извне 4 | */ 5 | 6 | #ifndef _avalon_imessage_view_h_ 7 | #define _avalon_imessage_view_h_ 8 | 9 | #include "model/all.h" 10 | 11 | /*! 12 | * \brief Интерфейс для работы с областью отображения HTML сообщения извне 13 | */ 14 | class IMessageView 15 | { 16 | public: 17 | 18 | /*! 19 | * \brief Очистить область (когда нечего отображать) 20 | */ 21 | virtual void clear () = 0; 22 | 23 | /*! 24 | * \brief Отобразить сообщение 25 | * \param message Дескриптор сообщения 26 | * \param forum Дескриптор форума (не спец-форума) 27 | */ 28 | virtual void setMessage (const AMessageInfo& message, const AForumInfo* forum = NULL) = 0; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/forms/form_source.cpp: -------------------------------------------------------------------------------- 1 | #include "form_source.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | FormSource::FormSource (QWidget* parent, const QString& body) : FormSourceUI (parent) 5 | { 6 | m_text_source->setPlainText(body); 7 | } 8 | //---------------------------------------------------------------------------------------------- 9 | 10 | FormSource::~FormSource () 11 | { 12 | } 13 | //---------------------------------------------------------------------------------------------- 14 | 15 | void FormSource::closeEvent (QCloseEvent* event) 16 | { 17 | // сохранение layout 18 | save(); 19 | 20 | event->accept(); 21 | 22 | deleteLater(); 23 | } 24 | //---------------------------------------------------------------------------------------------- 25 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | override_dh_auto_clean: 5 | if [ -f Makefile ]; then make clean; fi 6 | rm -f Makefile 7 | rm -f Makefile.Debug 8 | rm -f Makefile.Release 9 | rm -f avalon.pro 10 | rm -f avalon 11 | 12 | override_dh_auto_build: 13 | qmake -project -recursive -Wall -nopwd -o avalon.pro \ 14 | "CONFIG += debug_and_release" \ 15 | "QT += network sql webkit" \ 16 | "LIBS += -laspell -lz" \ 17 | "DEFINES += AVALON_PACKAGE" \ 18 | "QMAKE_CPPFLAGS *= $(shell dpkg-buildflags --get CPPFLAGS)" \ 19 | "QMAKE_CFLAGS *= $(shell dpkg-buildflags --get CFLAGS)" \ 20 | "QMAKE_CXXFLAGS *= $(shell dpkg-buildflags --get CXXFLAGS)" \ 21 | "QMAKE_LFLAGS *= $(shell dpkg-buildflags --get LDFLAGS)" \ 22 | src 23 | qmake avalon.pro 24 | make 25 | 26 | %: 27 | dh $@ 28 | -------------------------------------------------------------------------------- /src/forms/form_input.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief GUI формы запроса ввода пользователя (аналог InputBox) 4 | */ 5 | 6 | #ifndef _avalon_form_input_h_ 7 | #define _avalon_form_input_h_ 8 | 9 | #include "form_input_ui.h" 10 | 11 | /*! 12 | * \brief GUI формы запроса ввода пользователя (аналог InputBox) 13 | */ 14 | class FormInput : public FormInputUI 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | /*! 21 | * \brief Конструктор 22 | * \param parent Родительский виджет (форма) 23 | * \param header Заголовок окна 24 | * \param message Текст приглашения 25 | * \param text Текст ввода 26 | */ 27 | FormInput (QWidget* parent, const QString& header = QString(), const QString& message = QString(), const QString& text = QString()); 28 | ~FormInput (); 29 | 30 | QString text (); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/forms/form_source_ui.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief GUI формы просмотра сообщения в raw 4 | */ 5 | 6 | #ifndef _avalon_form_source_ui_h_ 7 | #define _avalon_form_source_ui_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief GUI формы просмотра сообщения в raw 13 | */ 14 | class FormSourceUI : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | FormSourceUI (QWidget* parent); 21 | ~FormSourceUI (); 22 | 23 | protected: 24 | 25 | /*! 26 | * \brief Функция сохранения расположения и размеров элементов управления формы <при выходе> 27 | */ 28 | void save (); 29 | 30 | /*! 31 | * \brief Функция восстановления расположения и размеров элементов управления формы <при старте> 32 | */ 33 | void restore (); 34 | 35 | QVBoxLayout* m_layout; /*!< \brief Общий layout */ 36 | QTextEdit* m_text_source; /*!< \brief Raw текст сообщения */ 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/model/user.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Модели для работы с пользователями 4 | */ 5 | 6 | #ifndef _avalon_user_info_h_ 7 | #define _avalon_user_info_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Описатель пользователя 13 | */ 14 | typedef struct AUserInfo 15 | { 16 | int ID; /*!< \brief ID */ 17 | QString Name; /*!< \brief Логин пользователя */ 18 | QString Nick; /*!< \brief Ник */ 19 | QString RealName; /*!< \brief Настоящее имя */ 20 | QString Email; /*!< \brief Мыло */ 21 | QString Homepage; /*!< \brief Домашняя страница */ 22 | QString Specialization; /*!< \brief Специализация */ 23 | QString WhereFrom; /*!< \brief Откуда */ 24 | QString Origin; /*!< \brief Подпись */ 25 | } AUserInfo; 26 | 27 | /*! 28 | * \brief Список пользователей 29 | */ 30 | typedef QList AUserInfoList; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/resource.rc: -------------------------------------------------------------------------------- 1 | #include "winver.h" 2 | 3 | MAINICON ICON "icons/main.ico" 4 | 5 | VS_VERSION_INFO VERSIONINFO 6 | PRODUCTVERSION 1,0,0,0 7 | FILEVERSION 1,0,442,0 8 | FILEFLAGSMASK 0x17L 9 | #ifdef _DEBUG 10 | FILEFLAGS 0x1L 11 | #else 12 | FILEFLAGS 0x0L 13 | #endif 14 | FILEOS 0x4L 15 | FILETYPE 0x0L 16 | FILESUBTYPE 0x0L 17 | BEGIN 18 | BLOCK "StringFileInfo" 19 | BEGIN 20 | BLOCK "041904b0" 21 | BEGIN 22 | VALUE "ProductName", "avalon" 23 | VALUE "ProductVersion", "1.0.0.0" 24 | VALUE "FileVersion", "1.0.442" 25 | VALUE "FileDescription", "avalon 1.0" 26 | VALUE "InternalName", "avalon" 27 | VALUE "LegalCopyright", "2008-2014, Anton Batenev, RSDN team" 28 | VALUE "OriginalFilename", "avalon.exe" 29 | END 30 | END 31 | BLOCK "VarFileInfo" 32 | BEGIN 33 | VALUE "Translation", 0x419, 1200 34 | END 35 | END 36 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_018.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = Это текст с 13 | ) 14 | ) 15 | ) 16 | [1] = AParsedBlock 17 | ( 18 | Type = pbtCut 19 | SubType = pbstBlock 20 | Title = развернуть 21 | SubBlocks = AParsedBlockList 22 | ( 23 | [0] = AParsedBlock 24 | ( 25 | Type = pbtText 26 | SubType = pbstText 27 | Strings = AQuotedStringList 28 | ( 29 | [0] = AQuotedString 30 | ( 31 | QuoteLevel = 0 32 | Data = закрытием тэга врезки 33 | ) 34 | ) 35 | ) 36 | ) 37 | ) 38 | [2] = AParsedBlock 39 | ( 40 | Type = pbtText 41 | SubType = pbstText 42 | Strings = AQuotedStringList 43 | ( 44 | [0] = AQuotedString 45 | ( 46 | QuoteLevel = 0 47 | Data = на той же строке 48 | ) 49 | ) 50 | ) 51 | ) 52 | -------------------------------------------------------------------------------- /src/forms/form_date.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Форма выбора даты/времени 4 | */ 5 | 6 | #ifndef _avalon_form_date_h_ 7 | #define _avalon_form_date_h_ 8 | 9 | #include "form_date_ui.h" 10 | 11 | /*! 12 | * \brief Форма выбора даты/времени 13 | */ 14 | class FormDate : public FormDateUI 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | /*! 21 | * \brief Конструктор 22 | * \param parent Родительский виджет-форма 23 | * \param as_read true для смены иконки на "как прочитанное" 24 | */ 25 | FormDate (QWidget* parent, bool as_read); 26 | ~FormDate (); 27 | 28 | /*! 29 | * \brief Возвращает выбранную дату/время 30 | * \return Дата и время 31 | */ 32 | QDateTime SelectedDate () 33 | { 34 | return m_selected_datetime; 35 | } 36 | 37 | private: 38 | 39 | /*! 40 | * \brief Выбранные дата и время 41 | */ 42 | QDateTime m_selected_datetime; 43 | 44 | private slots: 45 | 46 | /*! 47 | * \brief OK 48 | */ 49 | void button_ok_clicked (); 50 | 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/global.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Заголовочный файл для глобальных настроек 4 | */ 5 | 6 | #ifndef _avalon_global_h_ 7 | #define _avalon_global_h_ 8 | 9 | #include "model/user.h" 10 | 11 | /*! 12 | * \brief Глобальные настройки 13 | */ 14 | class AGlobal 15 | { 16 | private: 17 | 18 | AGlobal (); 19 | 20 | public: 21 | 22 | /*! 23 | * \brief Стандартный singleton 24 | */ 25 | static AGlobal* getInstance (); 26 | 27 | ~AGlobal (); 28 | 29 | /*! 30 | * \brief Перезагрузка настроек 31 | */ 32 | void reload (); 33 | 34 | // 35 | // Общие переменные 36 | // 37 | 38 | AUserInfo Me; /*!< \brief Текущий пользователь avalon */ 39 | #ifdef AVALON_USE_ZLIB 40 | bool Compression; /*!< \brief Сжатие тел сообщений */ 41 | #endif 42 | 43 | // 44 | // Константы форматирования 45 | // 46 | 47 | QString AnonymousName; /*!< \brief Отображаемое имя анонима */ 48 | QString DateFormat; /*!< \brief Формат даты/времени */ 49 | }; 50 | 51 | #endif // _avalon_global_h_ 52 | -------------------------------------------------------------------------------- /src/forms/form_subscribe.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Форма подписки на форумы 4 | */ 5 | 6 | #ifndef _avalon_form_subscribe_h_ 7 | #define _avalon_form_subscribe_h_ 8 | 9 | #include "model/all.h" 10 | #include "form_subscribe_ui.h" 11 | 12 | /*! 13 | * \brief Форма подписки на форумы 14 | */ 15 | class FormSubscribe : public FormSubscribeUI 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | 21 | FormSubscribe (QWidget* parent); 22 | ~FormSubscribe (); 23 | 24 | private: 25 | 26 | /*! 27 | * \brief Cписок подписаных форумов при последней загрузке 28 | */ 29 | ASubscribedForumInfoList m_last_subscribed; 30 | 31 | /*! 32 | * \brief Перезагрузка дерева форумов из хранилища 33 | */ 34 | void reload (); 35 | 36 | private slots: 37 | 38 | void button_ok_clicked (); /*!< \brief OK */ 39 | void button_refresh_clicked (); /*!< \brief Отмена */ 40 | void button_select_all_clicked (); /*!< \brief Выбрать все */ 41 | void button_select_none_clicked (); /*!< \brief Отменить все */ 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_009.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtText 6 | SubType = pbstText 7 | Strings = AQuotedStringList 8 | ( 9 | [0] = AQuotedString 10 | ( 11 | QuoteLevel = 0 12 | Data = простой текст без квоты 13 | ) 14 | [1] = AQuotedString 15 | ( 16 | QuoteLevel = 0 17 | Data = 18 | ) 19 | [2] = AQuotedString 20 | ( 21 | QuoteLevel = 1 22 | QuoteText = A> 23 | Data = простой текст сообщения 24 | ) 25 | ) 26 | ) 27 | [1] = AParsedBlock 28 | ( 29 | Type = pbtCode 30 | SubType = pbstSourceCode 31 | Strings = AQuotedStringList 32 | ( 33 | [0] = AQuotedString 34 | ( 35 | QuoteLevel = 1 36 | QuoteText = A> 37 | Data = многострочный код 38 | ) 39 | [1] = AQuotedString 40 | ( 41 | QuoteLevel = 1 42 | QuoteText = A> 43 | Data = на разных строках 44 | ) 45 | [2] = AQuotedString 46 | ( 47 | QuoteLevel = 1 48 | QuoteText = A> 49 | Data = с квотой 50 | ) 51 | ) 52 | ) 53 | ) 54 | -------------------------------------------------------------------------------- /src/forms/form_date_ui.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief GUI формы выбора даты/времени 4 | */ 5 | 6 | #ifndef _avalon_form_date_ui_h_ 7 | #define _avalon_form_date_ui_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief GUI формы выбора даты/времени 13 | */ 14 | class FormDateUI : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | FormDateUI (QWidget* parent); 21 | ~FormDateUI (); 22 | 23 | protected: 24 | 25 | QHBoxLayout* m_layout; /*!< \brief Общий layout */ 26 | QVBoxLayout* m_calendar_layout; /*!< \brief Layout для календаря */ 27 | QCalendarWidget* m_text_date; /*!< \brief Календарь */ 28 | QTimeEdit* m_text_time; /*!< \brief Время */ 29 | QVBoxLayout* m_button_layout; /*!< \brief Layout для кнопок */ 30 | QPushButton* m_button_ok; /*!< \brief OK */ 31 | QPushButton* m_button_cancel; /*!< \brief Отмена */ 32 | QSpacerItem* m_spacer_button; /*!< \brief Выравнивание кнопок */ 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/storage/database_error.cpp: -------------------------------------------------------------------------------- 1 | #include "database_error.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | ADatabaseError::ADatabaseError () 5 | { 6 | } 7 | //---------------------------------------------------------------------------------------------- 8 | 9 | ADatabaseError::~ADatabaseError () 10 | { 11 | } 12 | //---------------------------------------------------------------------------------------------- 13 | 14 | void ADatabaseError::showError (QWidget* parent) 15 | { 16 | QMessageBox::critical(parent, QString::fromUtf8("Ошибка!"), m_error_message); 17 | } 18 | //---------------------------------------------------------------------------------------------- 19 | 20 | QString ADatabaseError::getLastError () const 21 | { 22 | return m_error_message; 23 | } 24 | //---------------------------------------------------------------------------------------------- 25 | 26 | void ADatabaseError::setLastError (const QString& message) 27 | { 28 | m_error_message = message; 29 | } 30 | //---------------------------------------------------------------------------------------------- 31 | -------------------------------------------------------------------------------- /src/storage/database_error.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Обертка для контроля ошибок при работе с БД 4 | */ 5 | 6 | #ifndef _avalon_database_error_h_ 7 | #define _avalon_database_error_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Обертка для контроля и отображения ошибок при работе с БД 13 | */ 14 | class ADatabaseError 15 | { 16 | public: 17 | 18 | ADatabaseError (); 19 | ~ADatabaseError (); 20 | 21 | /*! 22 | * \brief Возвращает текст последней ошибки 23 | * \return Форматированный текст последней ошибки 24 | */ 25 | QString getLastError () const; 26 | 27 | /*! 28 | * \brief Отображает диалоговое окно с информацией о последней ошибке 29 | * \param parent Родительский виджет для диалогового окна 30 | */ 31 | void showError (QWidget* parent); 32 | 33 | protected: 34 | 35 | /*! 36 | * \brief Устанавливает текст последней ошибки 37 | * \param message Текст ошибки 38 | */ 39 | void setLastError (const QString& message); 40 | 41 | private: 42 | 43 | /*! 44 | * \brief Текст последней ошибки 45 | */ 46 | QString m_error_message; 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/forms/form_request_ui.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief GUI формы для отображения прогресса работы и для HTTP запросов 4 | */ 5 | 6 | #ifndef _avalon_form_request_ui_h_ 7 | #define _avalon_form_request_ui_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief GUI формы для отображения прогресса работы и для HTTP запросов 13 | */ 14 | class FormRequestUI : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | FormRequestUI (QWidget* parent); 21 | ~FormRequestUI (); 22 | 23 | protected: 24 | 25 | QVBoxLayout* m_layout; /*!< \brief Основной layout */ 26 | QVBoxLayout* m_layout_form; /*!< \brief Layout для прогресса и списка событий */ 27 | QProgressBar* m_progress_bar; /*!< \brief Прогресс события */ 28 | QListWidget* m_list_progress; /*!< \brief Список событий */ 29 | QHBoxLayout* m_layout_button; /*!< \brief Layout для кнопки */ 30 | QPushButton* m_button_cancel; /*!< \brief Отмена */ 31 | }; 32 | 33 | #endif // _avalon_form_request_ui_h_ 34 | -------------------------------------------------------------------------------- /src/forms/form_subscribe_ui.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief GUI формы подписки на форумы 4 | */ 5 | 6 | #ifndef _avalon_form_subscribe_ui_h_ 7 | #define _avalon_form_subscribe_ui_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief GUI формы подписки на форумы 13 | */ 14 | class FormSubscribeUI : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | FormSubscribeUI (QWidget* parent); 21 | ~FormSubscribeUI (); 22 | 23 | protected: 24 | 25 | QTreeWidget* m_tree_forum; /*!< \brief Дерево форумов */ 26 | 27 | QPushButton* m_button_ok; /*!< \brief OK */ 28 | QPushButton* m_button_refresh; /*!< \brief Обновить */ 29 | QPushButton* m_button_select_all; /*!< \brief Выбрать все */ 30 | QPushButton* m_button_select_none; /*!< \brief Отменить все */ 31 | QPushButton* m_button_cancel; /*!< \brief Отмена */ 32 | 33 | QHBoxLayout* m_layout_form; /*!< \brief Layout формы */ 34 | QVBoxLayout* m_layout_tree; /*!< \brief Layout дерева */ 35 | QVBoxLayout* m_layout_button; /*!< \brief Layout кнопок */ 36 | 37 | QSpacerItem* m_spacer_button; /*!< \brief Выравнивание кнопок вверх */ 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/forms/form_date.cpp: -------------------------------------------------------------------------------- 1 | #include "form_date.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | FormDate::FormDate (QWidget* parent, bool /*as_read*/) : FormDateUI (parent) 5 | { 6 | QDateTime now = QDateTime::currentDateTime(); 7 | m_selected_datetime = QDateTime(now.date(), QTime(now.time().hour(), now.time().minute())); 8 | 9 | m_text_date->setSelectedDate(m_selected_datetime.date()); 10 | m_text_time->setTime(m_selected_datetime.time()); 11 | 12 | connect(m_button_ok, SIGNAL(clicked()), this, SLOT(button_ok_clicked())); 13 | connect(m_button_cancel, SIGNAL(clicked()), this, SLOT(reject())); 14 | } 15 | //---------------------------------------------------------------------------------------------- 16 | 17 | FormDate::~FormDate () 18 | { 19 | } 20 | //---------------------------------------------------------------------------------------------- 21 | 22 | void FormDate::button_ok_clicked () 23 | { 24 | m_selected_datetime.setDate(m_text_date->selectedDate()); 25 | m_selected_datetime.setTime(m_text_time->time()); 26 | 27 | accept(); 28 | } 29 | //---------------------------------------------------------------------------------------------- 30 | -------------------------------------------------------------------------------- /src/storage/query.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Обертка для работы с SQL запросами 4 | */ 5 | 6 | #ifndef _avalon_query_h_ 7 | #define _avalon_query_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Обертка для работы с SQL запросами 13 | */ 14 | class AQuery : public QSqlQuery 15 | { 16 | public: 17 | 18 | /*! 19 | * \brief Создание объекта запроса 20 | * \param database Qt объект работы с БД 21 | * \param sql текст SQL запроса 22 | */ 23 | AQuery (QSqlDatabase* database, const QString& sql = QString()); 24 | ~AQuery (); 25 | 26 | /*! 27 | * \brief Получение текста последней ошибки 28 | * \return возвращает строку с последней ошибкой на основе информации из драйвера 29 | */ 30 | QString getLastError (); 31 | 32 | /*! 33 | * \brief Отображение текста последней ошибки в диалоговом окне 34 | * \param parent родительский виджет для диалогового окна 35 | */ 36 | void showError (QWidget* parent); 37 | 38 | /*! 39 | * \brief Перегрузка QSqlQuery::exec для возможности вызова не prepared запросов 40 | */ 41 | bool exec (); 42 | 43 | private: 44 | 45 | /*! 46 | * \brief Текст запроса для не prepared запросов (текст из конструктора) 47 | */ 48 | QString m_sql; 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /tests/parser/tests/parseBlocks_016.tgt: -------------------------------------------------------------------------------- 1 | AParsedBlockList 2 | ( 3 | [0] = AParsedBlock 4 | ( 5 | Type = pbtCut 6 | SubType = pbstBlock 7 | Title = Развернуть 1 8 | SubBlocks = AParsedBlockList 9 | ( 10 | [0] = AParsedBlock 11 | ( 12 | Type = pbtText 13 | SubType = pbstText 14 | Strings = AQuotedStringList 15 | ( 16 | [0] = AQuotedString 17 | ( 18 | QuoteLevel = 0 19 | Data = Начало вырезанного блока 1 20 | ) 21 | ) 22 | ) 23 | [1] = AParsedBlock 24 | ( 25 | Type = pbtCut 26 | SubType = pbstBlock 27 | Title = Развернуть 2 28 | SubBlocks = AParsedBlockList 29 | ( 30 | [0] = AParsedBlock 31 | ( 32 | Type = pbtText 33 | SubType = pbstText 34 | Strings = AQuotedStringList 35 | ( 36 | [0] = AQuotedString 37 | ( 38 | QuoteLevel = 0 39 | Data = Вырезанный блок 2 40 | ) 41 | ) 42 | ) 43 | ) 44 | ) 45 | [2] = AParsedBlock 46 | ( 47 | Type = pbtText 48 | SubType = pbstText 49 | Strings = AQuotedStringList 50 | ( 51 | [0] = AQuotedString 52 | ( 53 | QuoteLevel = 0 54 | Data = Окончание вырезанного блока 1 55 | ) 56 | ) 57 | ) 58 | ) 59 | ) 60 | ) 61 | -------------------------------------------------------------------------------- /src/storage/query.cpp: -------------------------------------------------------------------------------- 1 | #include "query.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | AQuery::AQuery (QSqlDatabase* database, const QString& sql) : QSqlQuery (QString(), *database) 5 | { 6 | m_sql = sql; 7 | } 8 | //---------------------------------------------------------------------------------------------- 9 | 10 | AQuery::~AQuery () 11 | { 12 | } 13 | //---------------------------------------------------------------------------------------------- 14 | 15 | QString AQuery::getLastError () 16 | { 17 | return lastError().databaseText() + "\n" + lastError().driverText() + "\n" + QString::fromUtf8("Ошибка: ") + QString::number(lastError().number()); 18 | } 19 | //---------------------------------------------------------------------------------------------- 20 | 21 | void AQuery::showError (QWidget* parent) 22 | { 23 | QMessageBox::critical(parent, QString::fromUtf8("Ошибка!"), getLastError()); 24 | } 25 | //---------------------------------------------------------------------------------------------- 26 | 27 | bool AQuery::exec () 28 | { 29 | if (m_sql.length() != 0) 30 | return QSqlQuery::exec(m_sql); 31 | 32 | return QSqlQuery::exec(); 33 | } 34 | //---------------------------------------------------------------------------------------------- 35 | -------------------------------------------------------------------------------- /src/version.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Обрпаботка шаблона файла версии, который меняется при каждом билде 4 | */ 5 | //---------------------------------------------------------------------------------------------- 6 | #include "version.h" 7 | #include "sysheaders.h" 8 | //---------------------------------------------------------------------------------------------- 9 | 10 | int getBuildNumber () 11 | { 12 | return AVALON_BUILD; 13 | } 14 | //---------------------------------------------------------------------------------------------- 15 | 16 | QString getVersionString () 17 | { 18 | return QString("1.0.") + QString::number(getBuildNumber()); 19 | } 20 | //---------------------------------------------------------------------------------------------- 21 | 22 | QString getAgentString () 23 | { 24 | return QString("avalon/") + getVersionString() + " (https://github.com/rsdn/avalon)"; 25 | } 26 | //---------------------------------------------------------------------------------------------- 27 | 28 | QString getTagline () 29 | { 30 | QSettings settings; 31 | 32 | QString tagline = settings.value("ui/tagline", "%%version%%").toString(); 33 | 34 | tagline = tagline.replace("%%version%%", "[url=https://github.com/rsdn/avalon/wiki]avalon/" + getVersionString() + "[/url]"); 35 | 36 | return tagline; 37 | } 38 | //---------------------------------------------------------------------------------------------- 39 | -------------------------------------------------------------------------------- /src/forms/form_input_ui.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief GUI формы запроса ввода пользователя (аналог InputBox) 4 | */ 5 | 6 | #ifndef _avalon_form_input_ui_h_ 7 | #define _avalon_form_input_ui_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief GUI формы запроса ввода пользователя (аналог InputBox) 13 | */ 14 | class FormInputUI : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | /*! 21 | * \brief Конструктор 22 | * \param parent Родительский виджет (форма) 23 | * \param header Заголовок окна 24 | * \param message Текст приглашения 25 | * \param text Текст ввода 26 | */ 27 | FormInputUI (QWidget* parent, const QString& header = QString(), const QString& message = QString(), const QString& text = QString()); 28 | ~FormInputUI (); 29 | 30 | protected: 31 | 32 | QHBoxLayout* m_layout; /*!< \brief Основной layout */ 33 | QVBoxLayout* m_form_layout; /*!< \brief Layout формы */ 34 | QLabel* m_label; /*!< \brief Надпись */ 35 | QLineEdit* m_text; /*!< \brief Текст */ 36 | QVBoxLayout* m_button_layout; /*!< \brief Layout кнопок */ 37 | QPushButton* m_button_ok; /*!< \brief OK */ 38 | QPushButton* m_button_cancel; /*!< \brief Отмена */ 39 | QSpacerItem* m_spacer_button; /*!< \brief Выравнивание кнопок вверх */ 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/forms/form_settings.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Форма настроек программы 4 | */ 5 | 6 | #ifndef _avalon_form_settings_h_ 7 | #define _avalon_form_settings_h_ 8 | 9 | #include "form_settings_ui.h" 10 | 11 | /*! 12 | * \brief Форма настроек программы 13 | */ 14 | class FormSettings : public FormSettingsUI 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | 20 | FormSettings (QWidget* parent); 21 | ~FormSettings (); 22 | 23 | private: 24 | 25 | /*! 26 | * \brief Сохранение настроек 27 | */ 28 | void save (); 29 | 30 | /*! 31 | * \brief Восстановление настроек 32 | */ 33 | void restore (); 34 | 35 | /*! 36 | * \brief Создание базы SQLite 37 | */ 38 | void createSQLiteDatabase(); 39 | 40 | /*! 41 | * \brief Создание базы MySQL 42 | */ 43 | void createMySQLDatabase(); 44 | 45 | private slots: 46 | 47 | /*! 48 | * \brief OK 49 | */ 50 | void button_ok_clicked (); 51 | 52 | /*! 53 | * \brief Включение / отключение прокси-сервера 54 | */ 55 | void check_use_proxy_state_changed (int state); 56 | 57 | /*! 58 | * \brief Выбор файла для файловых хранилищ 59 | */ 60 | void button_database_file_clicked (); 61 | 62 | /*! 63 | * \brief Выбор файла для создания нового хранилища 64 | */ 65 | void button_database_create_clicked (); 66 | 67 | /*! 68 | * \brief Смена типа БД 69 | */ 70 | void combo_database_type_current_index_changed (const QString& text); 71 | }; 72 | 73 | #endif // _avalon_form_settings_h_ 74 | -------------------------------------------------------------------------------- /src/model/moderate.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Модели для работы с модерилками 4 | */ 5 | 6 | #ifndef _avalon_moderate_info_h_ 7 | #define _avalon_moderate_info_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Описатель модерилки 13 | */ 14 | typedef struct AModerateInfo 15 | { 16 | int IDMessage; /*!< \brief ID сообщения */ 17 | int IDTopic; /*!< \brief ID топика */ 18 | int IDUser; /*!< \brief ID пользователя */ 19 | int IDForum; /*!< \brief ID форума */ 20 | QDateTime Created; /*!< \brief Дата */ 21 | } AModerateInfo; 22 | 23 | /*! 24 | * \brief Список модерилок 25 | */ 26 | typedef QList AModerateInfoList; 27 | 28 | /*! 29 | * \brief Описатель модерилки к отправке 30 | */ 31 | typedef struct AModerate2Send 32 | { 33 | int ID; /*!< \brief ID локальный */ 34 | int IDMessage; /*!< \brief ID сообщения */ 35 | QString Action; /*!< \brief Действие {MoveMessage, DeleteMessage, DeleteThread, DeleteErrorMessage, SplitThread, CloseTopic, OpenTopic} */ 36 | int IDForum; /*!< \brief ID форума для перемещения */ 37 | QString Description; /*!< \brief Описание */ 38 | bool AsModerator; /*!< \brief Как администратор */ 39 | QDateTime Date; /*!< \brief Дата */ 40 | } AModerate2Send; 41 | 42 | /*! 43 | * \brief Список модерилок к отправке 44 | */ 45 | typedef QList AModerate2SendList; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/forms/iform_main.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Интерфейс для работы с главной формой приложения 4 | */ 5 | 6 | #ifndef _avalon_iform_main_h_ 7 | #define _avalon_iform_main_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Перечисление действий/меню для включения отключения 13 | */ 14 | typedef enum AvalonActions 15 | { 16 | aaNewMessage, /*!< \brief Действие создания сообщения */ 17 | aaViewSource, /*!< \brief Действие просмотра источника */ 18 | aaPrevNextUnreadArticle, /*!< \brief Действие показать предыдущее/следующее непрочитанное сообщение */ 19 | aaPrevNextUnreadThread, /*!< \brief Действие показать предыдущую/следующую непрочитанную ветку */ 20 | aaPrevNextUnreadForum, /*!< \brief Действие показать предыдущий/следующий непрочитанный форум */ 21 | aaBackward, /*!< \brief Действие "Назад" */ 22 | aaForward /*!< \brief Действие "Вперед" */ 23 | } AvalonActions; 24 | 25 | /*! 26 | * \brief Интерфейс для взаимодействия с главной формой 27 | */ 28 | class IFormMain 29 | { 30 | public: 31 | 32 | /*! 33 | * \brief Отображение строки статуса в статус-баре 34 | * \param value строка для отображения 35 | */ 36 | virtual void showStatus (const QString& value) = 0; 37 | 38 | /*! 39 | * \brief Блокировка/разблокировка кнопок на тулбаре и элементов меню 40 | * \param action кнопка/действие (см. AvalonActions) 41 | * \param enabled флаг включения или отключения группы элементов 42 | */ 43 | virtual void setEnabledAction (AvalonActions action, bool enabled) = 0; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/widgets/spell_edit.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Класс QTextEdit с проверкой орфографии 4 | */ 5 | 6 | #ifndef _avalon_spell_edit_h_ 7 | #define _avalon_spell_edit_h_ 8 | 9 | #include "spellchecker.h" 10 | 11 | #ifdef AVALON_USE_ASPELL 12 | 13 | /*! 14 | * \brief Класс QTextEdit с проверкой орфографии 15 | */ 16 | class ASpellTextEdit : public QTextEdit 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | 22 | /*! 23 | * \brief Стандартный конструктор QTextEdit 24 | */ 25 | ASpellTextEdit (QWidget* parent); 26 | ~ASpellTextEdit (); 27 | 28 | protected: 29 | 30 | /*! 31 | * \brief Перегрузка стандартного метода вызова контекстного меню QTextEdit 32 | * для предложения вариантов исправления слов 33 | */ 34 | virtual void contextMenuEvent (QContextMenuEvent* event); 35 | 36 | private: 37 | 38 | /*! 39 | * \brief Класс спеллчекера для предложений вариантов исправления слов 40 | */ 41 | ASpellChecker* m_spellchecker; 42 | 43 | /*! 44 | * \brief Список вариантов для исправления слова 45 | */ 46 | QStringList m_suggest_list; 47 | 48 | private slots: 49 | 50 | /*! 51 | * \brief Событие контекстного меню (на замену слова) 52 | */ 53 | void menu_triggered (QAction* action); 54 | 55 | /*! 56 | * \brief Событие контекстного меню на добавление слова в словарь 57 | */ 58 | void menu_add_triggered (); 59 | }; 60 | 61 | #else // AVALON_USE_ASPELL 62 | 63 | /*! 64 | * \brief Класс QTextEdit без проверки орфографии 65 | */ 66 | typedef QTextEdit ASpellTextEdit; 67 | 68 | #endif // AVALON_USE_ASPELL 69 | 70 | #endif // _avalon_spell_edit_h_ 71 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * Точка входа 4 | */ 5 | 6 | #include "forms/form_main.h" 7 | #include "global.h" 8 | 9 | /*! 10 | * (no comments) 11 | * \param argc (no comments) 12 | * \param argv (no comments) 13 | * \return (no comments) 14 | */ 15 | int main (int argc, char* argv[]) 16 | { 17 | // инициализация ресурсов (необязательный пункт) 18 | Q_INIT_RESOURCE(resource); 19 | 20 | QApplication app(argc, argv); 21 | 22 | #ifndef Q_WS_MAC 23 | // отображать иконки в меню для всех, кроме MacOS 24 | app.setAttribute(Qt::AA_DontShowIconsInMenus, false); 25 | #endif 26 | 27 | // установка разделов для хранения конфигурации приложения 28 | QCoreApplication::setOrganizationName("rsdn.ru"); 29 | QCoreApplication::setApplicationName("avalon"); 30 | QCoreApplication::setApplicationVersion(getVersionString()); 31 | 32 | // переводчик стандартных сообщений и диалогов 33 | QString tr_file = "qt_" + QLocale::system().name(); 34 | QString tr_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath); 35 | 36 | if (tr_file == "qt_ru_RU" && QFileInfo (tr_path + "/" + tr_file + ".qm").exists() == false) 37 | { 38 | tr_file = ":translations/qt_ru_RU.qm"; 39 | tr_path = ""; 40 | } 41 | 42 | QTranslator qt_translator; 43 | qt_translator.load(tr_file, tr_path); 44 | app.installTranslator(&qt_translator); 45 | 46 | // глобальные настройки 47 | std::auto_ptr global(AGlobal::getInstance()); 48 | 49 | // главная форма 50 | AFormMain* form = new AFormMain(); 51 | 52 | form->show(); 53 | 54 | app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); 55 | 56 | return app.exec(); 57 | } 58 | -------------------------------------------------------------------------------- /src/storage/database.cpp: -------------------------------------------------------------------------------- 1 | #include "database.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | ADatabase::ADatabase (const QString& type) : ADatabaseError(), QSqlDatabase (type) 5 | { 6 | } 7 | //---------------------------------------------------------------------------------------------- 8 | 9 | ADatabase::~ADatabase () 10 | { 11 | close(); 12 | } 13 | //---------------------------------------------------------------------------------------------- 14 | 15 | void ADatabase::setLastError () 16 | { 17 | ADatabaseError::setLastError(QSqlDatabase::lastError().databaseText() + "\n" + QSqlDatabase::lastError().driverText() + "\n" + QString::fromUtf8("Ошибка: ") + QString::number(QSqlDatabase::lastError().number())); 18 | } 19 | //---------------------------------------------------------------------------------------------- 20 | 21 | AQuery* ADatabase::createQuery (const QString& sql) 22 | { 23 | return new AQuery(this, sql); 24 | } 25 | //---------------------------------------------------------------------------------------------- 26 | 27 | AQuery* ADatabase::createPreparedQuery (const QString& sql) 28 | { 29 | AQuery* query = new AQuery(this); 30 | 31 | if (query->prepare(sql) == false) 32 | { 33 | ADatabaseError::setLastError(query->lastError().databaseText() + "\n" + query->lastError().driverText() + "\n" + QString::fromUtf8("Ошибка: ") + QString::number(query->lastError().number())); 34 | 35 | delete query; 36 | 37 | return NULL; 38 | } 39 | 40 | return query; 41 | } 42 | //---------------------------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /src/forms/form_moderate.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Форма для ввода информации о модерировании 4 | */ 5 | 6 | #ifndef _avalon_form_moderate_h_ 7 | #define _avalon_form_moderate_h_ 8 | 9 | #include "form_moderate_ui.h" 10 | #include "widgets/iforum_tree.h" 11 | 12 | /*! 13 | * \brief Форма для ввода информации о модерировании 14 | */ 15 | class FormModerate : public FormModerateUI 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | 21 | /*! 22 | * \brief Конструктор 23 | * \param parent Родительский виджет / форма 24 | * \param id_message ID сообщения, для которого ставится модерилка 25 | * \param edit_id ID редактирования существующей модерилки к отправке 26 | */ 27 | FormModerate (QWidget* parent, int id_message, int edit_id = 0); 28 | ~FormModerate (); 29 | 30 | /*! 31 | * \brief Установка интерфейса для взаимодействия с деревом форумов 32 | * \param itf Указатель на интерфейс 33 | */ 34 | void setForumTree (IForumTree* itf); 35 | 36 | protected: 37 | 38 | /*! 39 | * \brief Событие закрытия формы (см. Qt::QDialog). 40 | */ 41 | virtual void closeEvent (QCloseEvent* event); 42 | 43 | private: 44 | 45 | /*! 46 | * \brief Интерфейс для взаимодействия с деревом форумов 47 | */ 48 | IForumTree* m_forum_tree; 49 | 50 | /*! 51 | * \brief ID сообщения 52 | */ 53 | int m_id_message; 54 | 55 | /*! 56 | * \brief ID редактируемой модерилки 57 | */ 58 | int m_id_edit; 59 | 60 | private slots: 61 | 62 | /*! 63 | * \brief OK 64 | */ 65 | void button_ok_clicked (); 66 | 67 | /*! 68 | * \brief Отмена 69 | */ 70 | void button_cancel_clicked (); 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/storage/database.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Обертка для работы с БД 4 | */ 5 | 6 | #ifndef _avalon_database_h_ 7 | #define _avalon_database_h_ 8 | 9 | #include "query.h" 10 | #include "database_error.h" 11 | 12 | /*! 13 | * \brief Класс-обертка для работы с БД 14 | */ 15 | class ADatabase : 16 | public ADatabaseError, // общий класс контроля ошибок 17 | public QSqlDatabase // Qt класс для работы с БД 18 | { 19 | public: 20 | 21 | /*! 22 | * \brief Конструктор объекта для работы с БД 23 | * \param type Тип БД, для MySQL type = "QMYSQL" (см. документацию Qt) 24 | */ 25 | ADatabase (const QString& type); 26 | ~ADatabase (); 27 | 28 | /*! 29 | * \brief Создание объекта для запроса данных 30 | * \param sql Строка запроса 31 | * \return Объект для запроса данных 32 | */ 33 | AQuery* createQuery (const QString& sql = QString()); 34 | 35 | /*! 36 | * \brief Создание объекта для запроса данных с параметрами 37 | * \param sql Строка запроса с параметрами, параметры задаются в виде ":имя_параметра" 38 | * \return Объект для запроса данных 39 | */ 40 | AQuery* createPreparedQuery (const QString& sql); 41 | 42 | protected: 43 | 44 | /*! 45 | * \brief Формирует строку с последней ошибкой на основании информации из драйвера БД (см. ADatabaseError) 46 | */ 47 | void setLastError (); 48 | 49 | private: 50 | 51 | /*! 52 | * \brief Перегрузка соответствующего метода QSqlDatabase::close 53 | * Запрещено к вызову вследствии того, что при наличии prepared запросов 54 | * закрытие соединения приводит к неопределенному поведению (в частности, 55 | * утечкам памяти). Вызывается из деструктора - к этому моменту экземпляров 56 | * AQuery оставаться в теории не должно. 57 | */ 58 | void close () { QSqlDatabase::close(); } 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/widgets/message_view.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Виджет отображения сообщений 4 | */ 5 | 6 | #ifndef _avalon_message_view_h_ 7 | #define _avalon_message_view_h_ 8 | 9 | #include "web_view.h" 10 | #include "interfaces.h" 11 | 12 | /*! 13 | * \brief Виджет отображения сообщений 14 | */ 15 | class AMessageView : 16 | public AWebViewWidget, 17 | public IMessageView // интерфес для взаимодействия со списком сообщений и форумов 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | 23 | AMessageView (QWidget* parent); 24 | ~AMessageView (); 25 | 26 | /*! 27 | * \brief Установка интерфейса для работы с деревом форумов 28 | * \param itf Интерфейс 29 | */ 30 | void setForumTree (IForumTree* itf); 31 | 32 | /*! 33 | * \brief Установка интерфейса для работы с деревом сообщений 34 | * \param itf Интерфейс 35 | */ 36 | void setMessageTree (IMessageTree* itf); 37 | 38 | /*! 39 | * \brief Установка интерфейса для работы с главной формой 40 | * \param itf Интерфейс 41 | */ 42 | void setMainForm (IFormMain* itf); 43 | 44 | private: 45 | 46 | IForumTree* m_forum_tree; /*!< \brief Интерфейс дерева форумов */ 47 | IMessageTree* m_message_tree; /*!< \brief Интерфейс дерева сообщений */ 48 | IFormMain* m_main_form; /*!< \brief Интерфейс главной формы */ 49 | 50 | // IMessageView 51 | private: 52 | 53 | void clear (); 54 | void setMessage (const AMessageInfo& message, const AForumInfo* forum = NULL); 55 | 56 | private slots: 57 | 58 | /*! 59 | * \brief Клик по линку 60 | * \param url URL линка 61 | */ 62 | void link_clicked (const QUrl& url); 63 | 64 | /*! 65 | * \brief Перемещение курсора над линком (см. Qt::QWebView) 66 | */ 67 | void link_hover (const QString& link, const QString& title, const QString& textContent); 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: avalon 3 | Source: https://github.com/rsdn/avalon 4 | 5 | Files: * 6 | Copyright: 2008-2014, Anton Batenev , RSDN team 7 | License: BSD 8 | 9 | License: BSD 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are met: 12 | . 13 | 1. Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | 2. Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | . 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | . 30 | The views and conclusions contained in the software and documentation are those 31 | of the authors and should not be interpreted as representing official policies, 32 | either expressed or implied, of the FreeBSD Project. 33 | -------------------------------------------------------------------------------- /src/storage/mysql_database.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Обертка для работы с MySQL 4 | */ 5 | 6 | #ifndef _avalon_mysql_database_h_ 7 | #define _avalon_mysql_database_h_ 8 | 9 | #include "database.h" 10 | 11 | /*! 12 | * \brief Обертка для работы с MySQL 13 | */ 14 | class AMySQLDatabase : public ADatabase 15 | { 16 | public: 17 | 18 | AMySQLDatabase (); 19 | ~AMySQLDatabase (); 20 | 21 | /*! 22 | * \brief Проверяет открыто ли соединение, если нет, то пытается его открыть 23 | * \return true - успешное открытие, иначе см. описание ADatabaseError 24 | */ 25 | bool checkDatabase (); 26 | 27 | /*! 28 | * \brief Начало транзакции с автооткрытием соединения 29 | * \return true - успешное, иначе см. описание ADatabaseError 30 | */ 31 | bool transaction (); 32 | 33 | /*! 34 | * \brief Принятие транзакции 35 | * Важно! В случае неудачи операции commit внутри вызывается автоматический rollback 36 | * (т.е. дополнительный rollback в случае неудачи commit не требуется). 37 | * \return true - успешное, иначе см. описание ADatabaseError 38 | */ 39 | bool commit (); 40 | 41 | /*! 42 | * \brief Откат транзакции 43 | * \return true - успешный, иначе см. описание ADatabaseError 44 | */ 45 | bool rollback (); 46 | 47 | /*! 48 | * \brief Cоздание объекта запроса 49 | * \param sql SQL текст запроса 50 | * \param prepared true, если запрос с параметрами и false для простых запросов 51 | * \return объект или NULL в случае ошибки (см. описание ADatabaseError) 52 | */ 53 | AQuery* createQuery (const QString& sql, bool prepared = true); 54 | 55 | private: 56 | 57 | /*! 58 | * \brief Флаг наличия транзакции (true - транзакция открыта) 59 | */ 60 | bool m_transaction; 61 | 62 | /*! 63 | * \brief Открытие соединения 64 | * \return true - успешное, иначе см. описание ADatabaseError 65 | */ 66 | bool openDatabase (); 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/storage/sqlite_database.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Обертка для работы с SQLite 4 | */ 5 | 6 | #ifndef _avalon_sqlite_database_h_ 7 | #define _avalon_sqlite_database_h_ 8 | 9 | #include "database.h" 10 | 11 | /*! 12 | * \brief Обертка для работы с SQLite 13 | */ 14 | class ASQLiteDatabase : public ADatabase 15 | { 16 | public: 17 | 18 | ASQLiteDatabase (); 19 | ~ASQLiteDatabase (); 20 | 21 | /*! 22 | * \brief Проверяет открыто ли соединение, если нет, то пытается его открыть 23 | * \return true - успешное открытие, иначе см. описание ADatabaseError 24 | */ 25 | bool checkDatabase (); 26 | 27 | /*! 28 | * \brief Начало транзакции с автооткрытием соединения 29 | * \return true - успешное, иначе см. описание ADatabaseError 30 | */ 31 | bool transaction (); 32 | 33 | /*! 34 | * \brief Принятие транзакции 35 | * Важно! В случае неудачи операции commit внутри вызывается автоматический rollback 36 | * (т.е. дополнительный rollback в случае неудачи commit не требуется). 37 | * \return true - успешное, иначе см. описание ADatabaseError 38 | */ 39 | bool commit (); 40 | 41 | /*! 42 | * \brief Откат транзакции 43 | * \return true - успешный, иначе см. описание ADatabaseError 44 | */ 45 | bool rollback (); 46 | 47 | /*! 48 | * \brief Cоздание объекта запроса 49 | * \param sql SQL текст запроса 50 | * \param prepared true, если запрос с параметрами и false для простых запросов 51 | * \return объект или NULL в случае ошибки (см. описание ADatabaseError) 52 | */ 53 | AQuery* createQuery (const QString& sql, bool prepared = true); 54 | 55 | private: 56 | 57 | /*! 58 | * \brief Флаг наличия транзакции (true - транзакция открыта) 59 | */ 60 | bool m_transaction; 61 | 62 | /*! 63 | * \brief Открытие соединения 64 | * \return true - успешное, иначе см. описание ADatabaseError 65 | */ 66 | bool openDatabase (); 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /avalon.spec: -------------------------------------------------------------------------------- 1 | Name: avalon 2 | Version: 1.0.442 3 | Release: 1 4 | Summary: RSDN offline client 5 | Group: Applications/Internet 6 | License: BSD-2-clause 7 | URL: https://github.com/rsdn/avalon 8 | Requires: libqt4 >= 4.4, aspell, zlib 9 | BuildRequires: libqt4-devel >= 4.4, aspell-devel, zlib-devel 10 | 11 | %if 0%{?suse_version} 12 | Requires: libQtWebKit4 13 | BuildRequires: libQtWebKit-devel 14 | %endif 15 | 16 | %if 0%{?fedora} 17 | Requires: qtwebkit 18 | BuildRequires: qtwebkit-devel 19 | %endif 20 | 21 | %define qmake qmake 22 | %if 0%{?fedora} 23 | %define qmake qmake-qt4 24 | %endif 25 | 26 | Source0: https://build.opensuse.org/source/home:antonbatenev:avalon/avalon/avalon_%{version}.tar.bz2 27 | BuildRoot: %{_tmppath}/%{name}-%{version}-build 28 | 29 | 30 | %description 31 | Offile client for Russian Software Developer Network 32 | 33 | 34 | %prep 35 | %setup -q -n avalon 36 | 37 | 38 | %build 39 | %{qmake} -project -recursive -Wall -nopwd -o avalon.pro \ 40 | "CONFIG += debug_and_release" \ 41 | "QT += network sql webkit" \ 42 | "LIBS += -laspell -lz" \ 43 | "DEFINES += AVALON_PACKAGE" \ 44 | src 45 | %{qmake} avalon.pro 46 | make 47 | 48 | 49 | %install 50 | install -d %{buildroot}%{_bindir} 51 | install -d %{buildroot}%{_datadir}/pixmaps 52 | install -d %{buildroot}%{_datadir}/applications 53 | 54 | install -m755 avalon %{buildroot}%{_bindir}/avalon 55 | install -m644 src/icons/avalon.xpm %{buildroot}%{_datadir}/pixmaps/avalon.xpm 56 | install -m644 avalon.desktop %{buildroot}%{_datadir}/applications/avalon.desktop 57 | 58 | 59 | %clean 60 | rm -rf %{buildroot} 61 | 62 | 63 | %files 64 | %defattr(-,root,root,-) 65 | %doc README.md src/sql/avalon.mysql.sql src/sql/avalon.sqlite.sql 66 | %{_bindir}/avalon 67 | %{_datadir}/pixmaps/avalon.xpm 68 | %{_datadir}/applications/avalon.desktop 69 | 70 | 71 | %changelog 72 | * Thu Nov 26 2014 Anton Batenev 1.0.442-1 73 | - Initial RPM release 74 | -------------------------------------------------------------------------------- /src/forms/form_input_ui.cpp: -------------------------------------------------------------------------------- 1 | #include "form_input_ui.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | FormInputUI::FormInputUI (QWidget* parent, const QString& header, const QString& message, const QString& text) : QDialog (parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint 5 | #if QT_VERSION >= 0x040500 6 | | Qt::WindowCloseButtonHint 7 | #endif 8 | ) 9 | { 10 | resize(450, 78); 11 | setFixedSize(width(), height()); 12 | 13 | setWindowTitle(header); 14 | 15 | m_layout = new QHBoxLayout(this); 16 | 17 | m_form_layout = new QVBoxLayout(); 18 | 19 | m_label = new QLabel(this); 20 | m_label->setText(message); 21 | m_label->setMinimumSize(350, 0); 22 | m_form_layout->addWidget(m_label); 23 | 24 | m_text = new QLineEdit(this); 25 | m_text->setText(text); 26 | m_form_layout->addWidget(m_text); 27 | 28 | m_button_layout = new QVBoxLayout(); 29 | 30 | m_button_ok = new QPushButton(this); 31 | m_button_ok->setText(QString::fromUtf8("OK")); 32 | m_button_ok->setShortcut(QKeySequence("Return")); 33 | m_button_ok->setDefault(true); 34 | m_button_layout->addWidget(m_button_ok); 35 | 36 | m_button_cancel = new QPushButton(this); 37 | m_button_cancel->setText(QString::fromUtf8("Отмена")); 38 | m_button_cancel->setShortcut(QKeySequence("Esc")); 39 | m_button_layout->addWidget(m_button_cancel); 40 | 41 | m_spacer_button = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); 42 | m_button_layout->addItem(m_spacer_button); 43 | 44 | m_layout->addLayout(m_form_layout); 45 | m_layout->addLayout(m_button_layout); 46 | 47 | connect(m_button_ok, SIGNAL(clicked()), this, SLOT(accept())); 48 | connect(m_button_cancel, SIGNAL(clicked()), this, SLOT(reject())); 49 | 50 | m_text->setFocus(); 51 | } 52 | //---------------------------------------------------------------------------------------------- 53 | 54 | FormInputUI::~FormInputUI () 55 | { 56 | } 57 | //---------------------------------------------------------------------------------------------- 58 | -------------------------------------------------------------------------------- /src/widgets/iforum_tree.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Интерфейс для работы с деревом форумов 4 | */ 5 | 6 | #ifndef _avalon_iforum_tree_h_ 7 | #define _avalon_iforum_tree_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Интерфейс для работы с деревом форумов 13 | */ 14 | class IForumTree 15 | { 16 | public: 17 | 18 | virtual ~IForumTree () {} 19 | 20 | /*! 21 | * \brief Перезагрузка дерева форумов 22 | */ 23 | virtual void reload () = 0; 24 | 25 | /*! 26 | * \brief Перезагрузка числа непрочитаных сообщений 27 | * \param reload_message_tree необходимость перезагрузки дерева сообщений 28 | */ 29 | virtual void reloadUnread (bool reload_message_tree) = 0; 30 | 31 | /*! 32 | * \brief Изменение числа непрочитанных сообщений для текущего форума в дереве форумов 33 | * \param count дельта числа непрочитанных сообщений, 34 | * положительное значение увеличивает число на count, 35 | * отрицательное значение уменьшает число на count, 36 | * проверка соответствия итогового значения с реальным числом непрочитанных сообщений в дереве сообщений не производится. 37 | * \param count_my дельта числа непрочитанных сообщений мне (по аналогии с параметром count) 38 | * \param count_topics дельта числа непрочитанных топиков (по аналогии с параметром count) 39 | * \param id_forum идентификатор требуемого форума (0 - текущий) 40 | */ 41 | virtual void changeUnreadCount (int count, int count_my, int count_topics, int id_forum = 0) = 0; 42 | 43 | /*! 44 | * \brief Создание нового сообщение в текущем форуме 45 | */ 46 | virtual void newMessage () = 0; 47 | 48 | /*! 49 | * \brief Перейти к следующему непрочитанному форуму 50 | */ 51 | virtual void gotoNextUnreadForum () = 0; 52 | 53 | /*! 54 | * \brief Перемещение выделения на форум с требуемым id 55 | * \param id форум для смены выделения 56 | * \return true, если есть выделение, false если заданный форум не найден 57 | */ 58 | virtual bool selectForum (int id) = 0; 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/forms/form_request_ui.cpp: -------------------------------------------------------------------------------- 1 | #include "form_request_ui.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | FormRequestUI::FormRequestUI (QWidget* parent) : QDialog (parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint 5 | #if QT_VERSION >= 0x040500 6 | | Qt::WindowCloseButtonHint 7 | #endif 8 | ) 9 | { 10 | setWindowTitle(QString::fromUtf8("HTTP")); 11 | 12 | QIcon icon; 13 | icon.addFile(":/icons/synchronize16.png", QSize(16, 16)); 14 | icon.addFile(":/icons/synchronize24.png", QSize(24, 24)); 15 | icon.addFile(":/icons/synchronize32.png", QSize(32, 32)); 16 | icon.addFile(":/icons/synchronize48.png", QSize(48, 48)); 17 | icon.addFile(":/icons/synchronize256.png", QSize(256, 256)); 18 | setWindowIcon(icon); 19 | 20 | resize(400, 300); 21 | setFixedSize(width(), height()); 22 | 23 | m_layout = new QVBoxLayout(this); 24 | 25 | m_layout_form = new QVBoxLayout(); 26 | m_layout->addLayout(m_layout_form); 27 | 28 | m_progress_bar = new QProgressBar(this); 29 | m_progress_bar->setValue(0); 30 | m_progress_bar->setMinimum(0); 31 | m_progress_bar->setMaximum(100); 32 | m_layout_form->addWidget(m_progress_bar); 33 | 34 | m_list_progress = new QListWidget(this); 35 | m_list_progress->setFocusPolicy(Qt::NoFocus); 36 | 37 | #ifndef Q_WS_WIN 38 | m_list_progress->setFrameShadow(QFrame::Plain); 39 | #endif 40 | 41 | m_layout_form->addWidget(m_list_progress); 42 | 43 | m_layout_button = new QHBoxLayout(); 44 | m_layout->addLayout(m_layout_button); 45 | 46 | m_layout_button->addStretch(); 47 | 48 | m_button_cancel = new QPushButton(this); 49 | m_button_cancel->setText(QString::fromUtf8("Отмена")); 50 | m_button_cancel->setShortcut(QKeySequence("Esc")); 51 | m_layout_button->addWidget(m_button_cancel); 52 | 53 | m_layout_button->addStretch(); 54 | } 55 | //---------------------------------------------------------------------------------------------- 56 | 57 | FormRequestUI::~FormRequestUI () 58 | { 59 | delete m_layout_button; 60 | delete m_layout_form; 61 | } 62 | //---------------------------------------------------------------------------------------------- 63 | -------------------------------------------------------------------------------- /src/widgets/imessage_tree.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Интерфейс для работы с деревом сообщений 4 | */ 5 | 6 | #ifndef _avalon_imessage_tree_h_ 7 | #define _avalon_imessage_tree_h_ 8 | 9 | #include "model/all.h" 10 | 11 | /*! 12 | * \brief Интерфейс для работы с деревом сообщений 13 | */ 14 | class IMessageTree 15 | { 16 | public: 17 | 18 | virtual ~IMessageTree () {} 19 | 20 | /*! 21 | * \brief Cмена форума, NULL - очистить список (указатель на группе) 22 | * \param info Описатель форума 23 | */ 24 | virtual void changeForum (const AForumInfo* info) = 0; 25 | 26 | /*! 27 | * \brief Отображение сырого текста выделенного сообщения 28 | */ 29 | virtual void showSource () = 0; 30 | 31 | /*! 32 | * \brief Установка оценки/модерилки и обработка других спец-линков 33 | * \param url Строка URL запроса из тела сообщения 34 | */ 35 | virtual void processUrl (const QString& url) = 0; 36 | 37 | /*! 38 | * \brief Перейти к следующему непрочитанному сообщению 39 | */ 40 | virtual void gotoNextUnreadArticle () = 0; 41 | 42 | /*! 43 | * \brief Перейти к следующей непрочитанной ветке 44 | */ 45 | virtual void gotoNextUnreadThread () = 0; 46 | 47 | /*! 48 | * \brief Возвращает путь к текущему выделенному сообщению от корня (см. selectByPath) 49 | * \param path Список, который заполняется ID сообщений от корня (корневой ID в начале списка) 50 | */ 51 | virtual void getSelectedPath (QList& path) = 0; 52 | 53 | /*! 54 | * \brief Переместить текущее выделение на сообщение с заданным путем 55 | * \param path Путь к сообщению от корня (список ID сообщений, корневой элемент первый в списке) 56 | * \return true, если есть выделение, false если сообщение с заданным путем не найдено 57 | * или находится вне зоны загруженных сообщений - в этом случае будет выделено максимально близкое по дереву 58 | */ 59 | virtual bool selectByPath (const QList* path) = 0; 60 | 61 | /*! 62 | * \brief Вернуться назад 63 | */ 64 | virtual void gotoBackward () = 0; 65 | 66 | /*! 67 | * \brief Перейти вперед 68 | */ 69 | virtual void gotoForward () = 0; 70 | 71 | /*! 72 | * \brief Ответить на сообщение 73 | */ 74 | virtual void reply () = 0; 75 | }; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/model/commit.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Модели для коммита информации отосланой на сервер 4 | */ 5 | 6 | #ifndef _avalon_commit_info_h_ 7 | #define _avalon_commit_info_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Описатель исключения коммита при отправке сообщений/оценок/модерилок на сервер 13 | * 14 | * После отправки данных (сообщений/модерилок/оценок) на сервер 15 | * (см. AWebservice::postChange_WebserviceQuery и AWebservice::postChange_WebserviceParse), 16 | * необходимо проверить результат отправки, для чего вызывается операция коммита отправленных данных 17 | * (см. AWebservice::postChangeCommit_WebserviceQuery) и анализируется результат 18 | * (см. AWebservice::postChangeCommit_WebserviceParse). 19 | * 20 | * Результатом является структура ACommitInfo со списком структур ACommitExceptionInfo 21 | * для каждого отправленного сообщения/оценки/модерилки, которые не прошли операцию коммита. 22 | * Анализируя списки исключений можно судить об успешности отправки каждого элемента. 23 | */ 24 | typedef struct ACommitExceptionInfo 25 | { 26 | QString Exception; /*!< \brief Текст исключения */ 27 | int ID; /*!< \brief Локальный id элемента в хранилище */ 28 | QString Info; /*!< \brief Дополнительная информация */ 29 | } ACommitExceptionInfo; 30 | 31 | /*! 32 | * \brief Список исключений 33 | */ 34 | typedef QList ACommitExceptionInfoList; 35 | 36 | /*! 37 | * \brief Описатель коммита 38 | * За подробностями см. ACommitExceptionInfo 39 | */ 40 | typedef struct ACommitInfo 41 | { 42 | QList Messages; /*!< \brief Список ID успешно закоммиченых сообщений */ 43 | ACommitExceptionInfoList MessagesExceptions; /*!< \brief Список исключений для неуспешно закомиченных сообщений */ 44 | QList Rating; /*!< \brief Список ID успешно закоммиченых рейтингов */ 45 | ACommitExceptionInfoList RatingExceptions; /*!< \brief Список исключений для неуспешно закомиченных рейтингов */ 46 | QList Moderate; /*!< \brief Список ID успешно закоммиченых модерилок */ 47 | ACommitExceptionInfoList ModerateExceptions; /*!< \brief Список исключений для неуспешно закомиченных модерилок */ 48 | } ACommitInfo; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/widgets/web_view.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Cабклассинг QWebView, т.к. он криво масштабируется и установка дефолтного поведения 4 | */ 5 | 6 | #ifndef _avalon_web_view_h_ 7 | #define _avalon_web_view_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Cабклассинг QWebPage для установки User-Agent ресурсов, загружаемых из сообщений 13 | */ 14 | class AWebPage : public QWebPage 15 | { 16 | public: 17 | 18 | AWebPage(QObject* parent = NULL) : QWebPage(parent) {} 19 | 20 | protected: 21 | 22 | QString userAgentForUrl (const QUrl& /*url*/) const { return getAgentString(); } 23 | }; 24 | 25 | /*! 26 | * \brief Cабклассинг QWebView для установки дефолтного поведения 27 | */ 28 | class AWebView : public QWebView 29 | { 30 | Q_OBJECT 31 | 32 | public: 33 | 34 | AWebView (QWidget* parent); 35 | ~AWebView () {} 36 | 37 | /*! 38 | * \brief Флаг того, что мышь находится над ссылкой 39 | * необходим для выдачи соответствующего QAction для возможности копирования ссылки, а не текста 40 | * устанавливается в дочерних классах 41 | */ 42 | bool LinkHovered; 43 | 44 | protected: 45 | 46 | /*! 47 | * \brief Кастомизация контекстного меню 48 | */ 49 | virtual void contextMenuEvent (QContextMenuEvent* event); 50 | 51 | /*! 52 | * \brief Обработчик клавиш для реализации копирования по хоткею 53 | */ 54 | virtual void keyPressEvent (QKeyEvent* event); 55 | 56 | private slots: 57 | 58 | // меню 59 | void menu_yandex_triggered (); /*!< \brief Поиск Яндекс */ 60 | void menu_wikipedia_triggered (); /*!< \brief Поиск Википедии */ 61 | void menu_slovari_yandex_triggered (); /*!< \brief Поиск Яндекс.Словари */ 62 | void menu_google_triggered (); /*!< \brief Поиск Google */ 63 | void menu_google_translate_triggered (); /*!< \brief Переводчик Google */ 64 | void menu_rsdn_triggered (); /*!< \brief Поиск RSDN */ 65 | }; 66 | 67 | /*! 68 | * \brief Cабклассинг AWebView, т.к. он криво масштабируется 69 | */ 70 | class AWebViewWidget : public QFrame 71 | { 72 | Q_OBJECT 73 | 74 | public: 75 | 76 | AWebViewWidget (QWidget* parent); 77 | ~AWebViewWidget (); 78 | 79 | /*! 80 | * \brief Объект сабклассинга 81 | */ 82 | AWebView* View; 83 | 84 | protected: 85 | 86 | /*! 87 | * \brief Масштабирование 88 | */ 89 | void resizeEvent (QResizeEvent* event); 90 | }; 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /src/forms/form_date_ui.cpp: -------------------------------------------------------------------------------- 1 | #include "form_date_ui.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | FormDateUI::FormDateUI (QWidget* parent) : QDialog (parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint 5 | #if QT_VERSION >= 0x040500 6 | | Qt::WindowCloseButtonHint 7 | #endif 8 | ) 9 | { 10 | setWindowTitle(QString::fromUtf8("дата / время")); 11 | 12 | QIcon icon; 13 | icon.addFile(":/icons/date16.png", QSize(16, 16)); 14 | icon.addFile(":/icons/date22.png", QSize(22, 22)); 15 | icon.addFile(":/icons/date24.png", QSize(24, 24)); 16 | icon.addFile(":/icons/date32.png", QSize(32, 32)); 17 | icon.addFile(":/icons/date48.png", QSize(48, 48)); 18 | icon.addFile(":/icons/date64.png", QSize(64, 64)); 19 | icon.addFile(":/icons/date128.png", QSize(128, 128)); 20 | setWindowIcon(icon); 21 | 22 | resize(300, 220); 23 | setFixedSize(width(), height()); 24 | 25 | m_layout = new QHBoxLayout(this); 26 | 27 | m_calendar_layout = new QVBoxLayout(); 28 | m_layout->addLayout(m_calendar_layout); 29 | 30 | m_button_layout = new QVBoxLayout(); 31 | m_layout->addLayout(m_button_layout); 32 | 33 | m_text_date = new QCalendarWidget(this); 34 | m_text_date->setFirstDayOfWeek(Qt::Monday); 35 | m_text_date->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader); 36 | m_text_date->setGridVisible(true); 37 | m_calendar_layout->addWidget(m_text_date); 38 | 39 | m_text_time = new QTimeEdit(this); 40 | m_text_time->setButtonSymbols(QAbstractSpinBox::UpDownArrows); 41 | m_text_time->setDisplayFormat("hh:mm"); 42 | m_calendar_layout->addWidget(m_text_time); 43 | 44 | m_button_ok = new QPushButton(this); 45 | m_button_ok->setText(QString::fromUtf8("OK")); 46 | m_button_ok->setShortcut(QKeySequence("Return")); 47 | m_button_ok->setDefault(true); 48 | m_button_layout->addWidget(m_button_ok); 49 | 50 | m_button_cancel = new QPushButton(this); 51 | m_button_cancel->setText(QString::fromUtf8("Отмена")); 52 | m_button_cancel->setShortcut(QKeySequence("Esc")); 53 | m_button_layout->addWidget(m_button_cancel); 54 | 55 | m_spacer_button = new QSpacerItem(1, height(), QSizePolicy::Maximum, QSizePolicy::Expanding); 56 | m_button_layout->addItem(m_spacer_button); 57 | } 58 | //---------------------------------------------------------------------------------------------- 59 | 60 | FormDateUI::~FormDateUI () 61 | { 62 | } 63 | //---------------------------------------------------------------------------------------------- 64 | -------------------------------------------------------------------------------- /src/model/forum.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Модели для работы с форумами 4 | */ 5 | 6 | #ifndef _avalon_forum_info_h_ 7 | #define _avalon_forum_info_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Описатель форума 13 | */ 14 | typedef struct AForumInfo 15 | { 16 | int ID; /*!< \brief ID форума */ 17 | int IDGroup; /*!< \brief ID группы */ 18 | QString ShortName; /*!< \brief Короткое имя */ 19 | QString Name; /*!< \brief Полное имя */ 20 | bool Rated; /*!< \brief Оценивается ли форум */ 21 | bool InTop; /*!< \brief Участвует ли оценки этого форума в топе */ 22 | int RateLimit; /*!< \brief Лимит оценки в форуме */ 23 | } AForumInfo; 24 | 25 | /*! 26 | * \brief Список форумов 27 | */ 28 | typedef QList AForumInfoList; 29 | 30 | /*! 31 | * \brief Описатель id подписанного форума 32 | */ 33 | typedef struct ASubscribedForumInfo 34 | { 35 | int IDForum; /*!< \brief ID форума */ 36 | bool IsFirst; /*!< \brief true - первая подписка, иначе false */ 37 | } ASubscribedForumInfo; 38 | 39 | /*! 40 | * \brief Список id подписаных форумов 41 | */ 42 | typedef QList ASubscribedForumInfoList; 43 | 44 | /*! 45 | * \brief Описатель количества непрочитаных сообщений в форуме 46 | */ 47 | typedef struct AUnreadForumCountInfo 48 | { 49 | int IDForum; /*!< \brief ID форума */ 50 | int Topics; /*!< \brief Количество непрочитанных (новых) топиков */ 51 | int Count; /*!< \brief Количество непрочитанных сообщений */ 52 | int CountMy; /*!< \brief Количество непрочитанных сообщений мне */ 53 | } AUnreadForumCountInfo; 54 | 55 | /*! 56 | * \brief Список количества непрочитаных сообщений 57 | */ 58 | typedef QList AUnreadForumCountInfoList; 59 | 60 | /*! 61 | * \brief Константа для спец-форума "сообщения к отправке" 62 | */ 63 | const int SPECIAL_ID_FORUM_MESSAGE2SEND = -1; 64 | 65 | /*! 66 | * \brief Константа для спец-форума "рейтинг к отправке" 67 | */ 68 | const int SPECIAL_ID_FORUM_RATING2SEND = -2; 69 | 70 | /*! 71 | * \brief Константа для спец-форума "модерилки к отправке" 72 | */ 73 | const int SPECIAL_ID_FORUM_MODERATE2SEND = -3; 74 | 75 | /*! 76 | * \brief Константа для спец-форума "черновики" 77 | */ 78 | const int SPECIAL_ID_FORUM_DRAFTS = -4; 79 | 80 | /*! 81 | * \brief Константа для спец-форума "мои сообщения" 82 | */ 83 | const int SPECIAL_ID_FORUM_MY_MESSAGES = -5; 84 | 85 | /*! 86 | * \brief Константа для спец-форума "ответы мне" 87 | */ 88 | const int SPECIAL_ID_FORUM_ANSWERS_TO_ME = -6; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /src/widgets/spell_edit.cpp: -------------------------------------------------------------------------------- 1 | #include "spell_edit.h" 2 | //---------------------------------------------------------------------------------------------- 3 | #ifdef AVALON_USE_ASPELL 4 | //---------------------------------------------------------------------------------------------- 5 | 6 | ASpellTextEdit::ASpellTextEdit (QWidget* parent) : QTextEdit (parent) 7 | { 8 | m_spellchecker = NULL; 9 | 10 | if (QSettings().value("ui/spellchecker", true).toInt() == true) 11 | m_spellchecker = new ASpellChecker(document()); 12 | } 13 | //---------------------------------------------------------------------------------------------- 14 | 15 | ASpellTextEdit::~ASpellTextEdit () 16 | { 17 | delete m_spellchecker; 18 | } 19 | //---------------------------------------------------------------------------------------------- 20 | 21 | void ASpellTextEdit::contextMenuEvent (QContextMenuEvent* event) 22 | { 23 | QMenu* menu = createStandardContextMenu(); 24 | 25 | if (m_spellchecker != NULL) 26 | { 27 | QString selected_text = textCursor().selectedText(); 28 | 29 | if (selected_text.length() > 0) 30 | { 31 | m_suggest_list.clear(); 32 | 33 | if (m_spellchecker->spellWord(selected_text, &m_suggest_list) == 1) 34 | { 35 | if (m_suggest_list.count() > 0) 36 | { 37 | menu->addSeparator(); 38 | 39 | for (int i = 0; i < m_suggest_list.count() && i < 10; i++) 40 | { 41 | QAction* action = menu->addAction(m_suggest_list[i]); 42 | action->setMenuRole(QAction::ApplicationSpecificRole); 43 | } 44 | 45 | connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_triggered(QAction*))); 46 | 47 | menu->addSeparator(); 48 | 49 | QAction* action = menu->addAction(QString::fromUtf8("Добавить в словарь")); 50 | connect(action, SIGNAL(triggered()), this, SLOT(menu_add_triggered())); 51 | } 52 | } 53 | } 54 | } 55 | 56 | menu->exec(event->globalPos()); 57 | 58 | delete menu; 59 | } 60 | //---------------------------------------------------------------------------------------------- 61 | 62 | void ASpellTextEdit::menu_triggered (QAction* action) 63 | { 64 | if (action->menuRole() == QAction::ApplicationSpecificRole) 65 | textCursor().insertText(action->text()); 66 | } 67 | //---------------------------------------------------------------------------------------------- 68 | 69 | void ASpellTextEdit::menu_add_triggered () 70 | { 71 | QString selected_text = textCursor().selectedText(); 72 | 73 | if (m_spellchecker->addWord(selected_text) == true) 74 | m_spellchecker->rehighlight(); 75 | } 76 | //---------------------------------------------------------------------------------------------- 77 | #endif // AVALON_USE_ASPELL 78 | //---------------------------------------------------------------------------------------------- 79 | -------------------------------------------------------------------------------- /src/forms/form_source_ui.cpp: -------------------------------------------------------------------------------- 1 | #include "form_source_ui.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | FormSourceUI::FormSourceUI (QWidget* parent) : QDialog (parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint 5 | #if QT_VERSION >= 0x040500 6 | | Qt::WindowCloseButtonHint 7 | #endif 8 | ) 9 | { 10 | setWindowTitle(QString::fromUtf8("просмотр источника")); 11 | 12 | QIcon icon; 13 | icon.addFile(":/icons/viewsource16.png", QSize(16, 16)); 14 | icon.addFile(":/icons/viewsource22.png", QSize(22, 22)); 15 | icon.addFile(":/icons/viewsource32.png", QSize(32, 32)); 16 | icon.addFile(":/icons/viewsource48.png", QSize(48, 48)); 17 | icon.addFile(":/icons/viewsource64.png", QSize(64, 64)); 18 | setWindowIcon(icon); 19 | 20 | resize(400, 300); 21 | setMinimumSize(QSize(400, 300)); 22 | 23 | m_layout = new QVBoxLayout(this); 24 | 25 | m_text_source = new QTextEdit(this); 26 | 27 | #ifndef Q_WS_WIN 28 | m_text_source->setFrameShadow(QFrame::Plain); 29 | #endif 30 | 31 | m_text_source->setReadOnly(true); 32 | 33 | m_layout->addWidget(m_text_source); 34 | 35 | // восстановление layout 36 | restore(); 37 | } 38 | //---------------------------------------------------------------------------------------------- 39 | 40 | FormSourceUI::~FormSourceUI () 41 | { 42 | } 43 | //---------------------------------------------------------------------------------------------- 44 | 45 | void FormSourceUI::save () 46 | { 47 | QSettings settings; 48 | 49 | if (windowState() == Qt::WindowNoState) 50 | { 51 | QRect rect = geometry(); 52 | 53 | settings.setValue("form_source/left", rect.left()); 54 | settings.setValue("form_source/top", rect.top()); 55 | settings.setValue("form_source/width", rect.width()); 56 | settings.setValue("form_source/height", rect.height()); 57 | } 58 | 59 | settings.setValue("form_source/state", static_cast(windowState())); 60 | } 61 | //---------------------------------------------------------------------------------------------- 62 | 63 | void FormSourceUI::restore () 64 | { 65 | QSettings settings; 66 | 67 | QRect screen = QDesktopWidget().screenGeometry(this); 68 | 69 | int w = settings.value("form_source/width", width()).toInt(); 70 | int h = settings.value("form_source/height", height()).toInt(); 71 | int x = settings.value("form_source/left", (screen.width() - w) / 2).toInt(); 72 | int y = settings.value("form_source/top", (screen.height() - h) / 2).toInt(); 73 | 74 | // setGeometry как-то некорректно работает :( 75 | resize(w, h); 76 | move(x, y); 77 | 78 | Qt::WindowStates state = static_cast(settings.value("form_source/state", Qt::WindowNoState).toInt()); 79 | 80 | setWindowState(state); 81 | } 82 | //---------------------------------------------------------------------------------------------- 83 | -------------------------------------------------------------------------------- /tests/parser/parser_test.h: -------------------------------------------------------------------------------- 1 | #ifndef _avalon_parser_test_h_ 2 | #define _avalon_parser_test_h_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../../src/parser.h" 19 | 20 | class AParser_Test : public CppUnit::TestFixture 21 | { 22 | CPPUNIT_TEST_SUITE(AParser_Test); 23 | CPPUNIT_TEST(parseQuote_test); 24 | CPPUNIT_TEST(parseQuotes_test); 25 | CPPUNIT_TEST(parseBlocks_test); 26 | CPPUNIT_TEST(isURL_test); 27 | CPPUNIT_TEST_SUITE_END(); 28 | 29 | public: 30 | 31 | // тест вычисления квоты строки 32 | void parseQuote_test (); 33 | // тест вычисления квот нескольких строк 34 | void parseQuotes_test (); 35 | // тест парсинга блоков 36 | void parseBlocks_test (); 37 | // тест ссылки 38 | void isURL_test (); 39 | 40 | private: 41 | 42 | std::string std_string (const QString& source) { return std::string(source.toUtf8().constData()); } 43 | std::string std_string (const char* source) { return std::string(source); } 44 | std::string make_msg (const char* source1, const char* source2) { return std::string(source1) + std::string(source2); } 45 | std::string make_msg (const char* source1, const QString& source2) { return std::string(source1) + std_string(source2); } 46 | std::string make_msg (const QString& source1, const QString& source2) { return std_string(source1) + std_string(source2); } 47 | QString make_special (const char* source) { return make_special(QString::fromUtf8(source)); } 48 | QString make_special (const QString& source) 49 | { 50 | QString data = source; 51 | 52 | data.replace("&", "&"); 53 | data.replace(">", ">"); 54 | data.replace("<", "<"); 55 | 56 | data.replace("&#", "&#"); 57 | 58 | return data; 59 | } 60 | 61 | /*! 62 | * \brief Отладочная функция преобразования списка монолитного блока в строку 63 | * \param block Исходный монолитный блок 64 | * \return Отладочная строка с содержимым блока 65 | */ 66 | QString ParsedBlockToString (const AParsedBlock& block, const QString& pad = ""); 67 | 68 | /*! 69 | * \brief Отладочная функция преобразования списка монолитных блоков в строку 70 | * \param list Исходный список монолитных блоков 71 | * \return Отладочная строка с содержимым блоков 72 | */ 73 | QString ParsedBlockListToString (const AParsedBlockList& list, const QString& pad = ""); 74 | }; 75 | 76 | #endif // _avalon_parser_test_h_ 77 | -------------------------------------------------------------------------------- /src/forms/form_subscribe_ui.cpp: -------------------------------------------------------------------------------- 1 | #include "form_subscribe_ui.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | FormSubscribeUI::FormSubscribeUI (QWidget* parent) : QDialog (parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint 5 | #if QT_VERSION >= 0x040500 6 | | Qt::WindowCloseButtonHint 7 | #endif 8 | ) 9 | { 10 | setWindowTitle(QString::fromUtf8("подписка")); 11 | 12 | QIcon icon; 13 | icon.addFile(":/icons/subscribe16.png", QSize(16, 16)); 14 | icon.addFile(":/icons/subscribe22.png", QSize(22, 22)); 15 | icon.addFile(":/icons/subscribe32.png", QSize(32, 32)); 16 | icon.addFile(":/icons/subscribe48.png", QSize(48, 48)); 17 | icon.addFile(":/icons/subscribe64.png", QSize(64, 64)); 18 | setWindowIcon(icon); 19 | 20 | resize(600, 500); 21 | setFixedSize(width(), height()); 22 | 23 | m_layout_form = new QHBoxLayout(this); 24 | 25 | m_layout_tree = new QVBoxLayout(); 26 | m_layout_form->addLayout(m_layout_tree); 27 | 28 | m_layout_button = new QVBoxLayout(); 29 | m_layout_form->addLayout(m_layout_button); 30 | 31 | m_tree_forum = new QTreeWidget(); 32 | 33 | #ifndef Q_WS_WIN 34 | m_tree_forum->setFrameShadow(QFrame::Plain); 35 | #endif 36 | 37 | m_tree_forum->setSelectionMode(QAbstractItemView::NoSelection); 38 | m_tree_forum->setColumnCount(1); 39 | m_tree_forum->header()->hide(); 40 | m_layout_tree->addWidget(m_tree_forum); 41 | 42 | m_button_ok = new QPushButton(this); 43 | m_button_ok->setText(QString::fromUtf8("OK")); 44 | m_button_ok->setShortcut(QKeySequence("Return")); 45 | m_button_ok->setDefault(true); 46 | m_layout_button->addWidget(m_button_ok); 47 | 48 | m_button_refresh = new QPushButton(this); 49 | m_button_refresh->setText(QString::fromUtf8("Обновить")); 50 | m_button_refresh->setShortcut(QKeySequence("F5")); 51 | m_layout_button->addWidget(m_button_refresh); 52 | 53 | m_button_select_all = new QPushButton(this); 54 | m_button_select_all->setText(QString::fromUtf8("Выбрать все")); 55 | m_layout_button->addWidget(m_button_select_all); 56 | 57 | m_button_select_none = new QPushButton(this); 58 | m_button_select_none->setText(QString::fromUtf8("Отменить все")); 59 | m_layout_button->addWidget(m_button_select_none); 60 | 61 | m_spacer_button = new QSpacerItem(1, height(), QSizePolicy::Maximum, QSizePolicy::Expanding); 62 | m_layout_button->addItem(m_spacer_button); 63 | 64 | m_button_cancel = new QPushButton(this); 65 | m_button_cancel->setText(QString::fromUtf8("Отмена")); 66 | m_button_cancel->setShortcut(QKeySequence("Esc")); 67 | m_layout_button->addWidget(m_button_cancel); 68 | } 69 | //---------------------------------------------------------------------------------------------- 70 | 71 | FormSubscribeUI::~FormSubscribeUI () 72 | { 73 | } 74 | //---------------------------------------------------------------------------------------------- 75 | -------------------------------------------------------------------------------- /src/model/rating.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Модели для работы с рейтингом 4 | */ 5 | 6 | #ifndef _avalon_rating_info_h_ 7 | #define _avalon_rating_info_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Описатель рейтинга 13 | * 14 | * Не смотря на то, что для поля Rate определено 7 возможных значений, 15 | * на практике оказывается, что веб-сервис далеко не всегда отдает значения в "корректном" диапазоне 16 | * и за имеющуюся историю оценок (на 10.03.2009) было выставлено 10 оценок не входящих в диапазон, 17 | * а так же, оценка "+1" отсутствует как класс :\ 18 | * 19 | * Проектное решение состоит в сохранении в хранилище любой оценки вне зависимости от ее значения 20 | * (следуя принципу максимально длительного невмешательства в поток данных), при выборке оценок 21 | * (см. IAStorage::getMessageRatingList) хранилище так же обязуется вернуть значения рейтингов "как есть" 22 | * Фильтрация диапазона производится при формировании отображения сообщения (см. AFormatter::parse). 23 | */ 24 | typedef struct ARatingInfo 25 | { 26 | int IDMessage; /*!< \brief ID сообщения */ 27 | int IDTopic; /*!< \brief ID топика */ 28 | int IDUser; /*!< \brief ID пользователя */ 29 | int UserRating; /*!< \brief Рейтинг пользователя */ 30 | int Rate; /*!< \brief "+1" = -3, "1" = 1, "2" = 2, "3" = 3, "+" = -4, "-" = 0, ";)" = -2 */ 31 | QDateTime RateDate; /*!< \brief Дата */ 32 | } ARatingInfo; 33 | 34 | /*! 35 | * \brief Список рейтингов 36 | */ 37 | typedef QList ARatingInfoList; 38 | 39 | /*! 40 | * \brief Описатель рейтинга к отправке 41 | */ 42 | typedef struct ARating2Send 43 | { 44 | int ID; /*!< \brief ID локальный */ 45 | int IDMessage; /*!< \brief ID сообщения */ 46 | int Rate; /*!< \brief Оценка */ 47 | QDateTime Date; /*!< \brief Дата */ 48 | } ARating2Send; 49 | 50 | /*! 51 | * \brief Список рейтингов к отправке 52 | */ 53 | typedef QList ARating2SendList; 54 | 55 | /*! 56 | * \brief Описатель оценки сообщения/топика 57 | */ 58 | typedef struct AMessageRating 59 | { 60 | int IDUser; /*!< \brief ID пользователя */ 61 | QString Name; /*!< \brief Логин пользователя */ 62 | QString Nick; /*!< \brief Ник */ 63 | int UserRating; /*!< \brief Рейтинг пользователя */ 64 | int Rate; /*!< \brief "+1" = -3, "1" = 1, "2" = 2, "3" = 3, "+" = -4, "-" = 0, ";)" = -2 (дополнительно см. примечания к ARatingInfo) */ 65 | } AMessageRating; 66 | 67 | /*! 68 | * \brief Список оценок для сообщения/топика 69 | */ 70 | typedef QList AMessageRatingList; 71 | 72 | /*! 73 | * \brief Константа для типа оценки "Смайл" 74 | */ 75 | const int SPECIAL_RATE_TYPE_SMILE = 0; 76 | 77 | /*! 78 | * \brief Константа для типа оценки "Согласен" / "Не согласен" 79 | */ 80 | const int SPECIAL_RATE_TYPE_PLUS_MINUS = 1; 81 | 82 | /*! 83 | * \brief Константа для числового типа оценки 84 | */ 85 | const int SPECIAL_RATE_TYPE_NUMBER = 2; 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/forms/form_message.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Форма создания сообщения или ответа на сообщение 4 | */ 5 | 6 | #ifndef _avalon_form_message_h_ 7 | #define _avalon_form_message_h_ 8 | 9 | #include "model/all.h" 10 | #include "interfaces.h" 11 | #include "form_message_ui.h" 12 | 13 | /*! 14 | * \brief Форма создания сообщения или ответа на сообщение. 15 | */ 16 | class FormMessage : public FormMessageUI 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | 22 | /*! 23 | * \brief Конструктор. 24 | * \param parent Родительская форма. 25 | * \param is_reply true, если форма создана для ответа на сообщение. 26 | * \param info Описатель с информацией о сообщении. 27 | * \param edit_id ID редактирования существующего сообщения к отправке 28 | */ 29 | FormMessage (QWidget* parent, bool is_reply, const AMessageInfoGUI& info, int edit_id = 0); 30 | ~FormMessage (); 31 | 32 | /*! 33 | * \brief Установка интерфейса для взаимодействия со списком форумов. 34 | * \param itf Указатель на интерфейс 35 | */ 36 | void setForumTree (IForumTree* itf); 37 | 38 | /*! 39 | * \brief Установка интерфейса для работы с главной формой 40 | * \param itf Интерфейс 41 | */ 42 | void setMainForm (IFormMain* itf); 43 | 44 | private: 45 | 46 | IForumTree* m_forum_tree; /*!< \brief Интерфейс для взаимодействия со списком форумов */ 47 | IFormMain* m_main_form; /*!< \brief Интерфейс главной формы */ 48 | 49 | /*! 50 | * \brief Флаг изменения текста 51 | */ 52 | bool m_text_changed; 53 | 54 | /*! 55 | * \brief Текущее редактируемое сообщение. 56 | */ 57 | AMessageInfoGUI m_info; 58 | 59 | /*! 60 | * \brief Форум, для которого предназначено сообщение 61 | */ 62 | AForumInfo m_forum_info; 63 | 64 | /*! 65 | * \brief ID редактирования уже существующего сообщения к отправке 66 | */ 67 | int m_edit_id; 68 | 69 | /*! 70 | * \brief Событие закрытия формы (см. Qt::QDialog). 71 | */ 72 | void closeEvent (QCloseEvent* event); 73 | 74 | /*! 75 | * \brief Поместить сообщение к отправке. 76 | * \param draft - флаг черновика 77 | */ 78 | void sendMessage (bool draft); 79 | 80 | private slots: 81 | 82 | /*! 83 | * \brief Смена табов межу редактированием и предпросмотром. 84 | */ 85 | void tab_changed (int tab_index); 86 | 87 | /*! 88 | * \brief Отправить. 89 | */ 90 | void button_send_triggered (); 91 | 92 | /*! 93 | * \brief В черновики. 94 | */ 95 | void button_draft_triggered (); 96 | 97 | /*! 98 | * \brief Смена текста темы сообщения. 99 | */ 100 | void text_subject_text_changed (const QString& text); 101 | 102 | /*! 103 | * \brief Событие клика по линку 104 | */ 105 | void link_clicked (const QUrl& url); 106 | 107 | /*! 108 | * \brief Событие нахождение мыши над линком 109 | */ 110 | void link_hover (const QString& link, const QString& title, const QString& textContent); 111 | 112 | /*! 113 | * \brief Событие изменения текста сообщения 114 | */ 115 | void text_changed (); 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /src/storage/sqlite_database.cpp: -------------------------------------------------------------------------------- 1 | #include "sqlite_database.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | ASQLiteDatabase::ASQLiteDatabase () : ADatabase ("QSQLITE") 5 | { 6 | m_transaction = false; 7 | } 8 | //---------------------------------------------------------------------------------------------- 9 | 10 | ASQLiteDatabase::~ASQLiteDatabase () 11 | { 12 | } 13 | //---------------------------------------------------------------------------------------------- 14 | 15 | bool ASQLiteDatabase::checkDatabase () 16 | { 17 | if (ADatabase::isOpen() == true) 18 | return true; 19 | 20 | return openDatabase(); 21 | } 22 | //---------------------------------------------------------------------------------------------- 23 | 24 | bool ASQLiteDatabase::openDatabase () 25 | { 26 | if (ADatabase::isOpen() == true) 27 | return true; 28 | 29 | // получение настроек соединения из конфига 30 | QSettings settings; 31 | 32 | QString database_file = settings.value("sqlite/file").toString(); 33 | 34 | ADatabase::setDatabaseName(database_file); 35 | 36 | m_transaction = false; 37 | 38 | if (ADatabase::open() == false) 39 | { 40 | setLastError(); 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | //---------------------------------------------------------------------------------------------- 47 | 48 | bool ASQLiteDatabase::transaction () 49 | { 50 | if (m_transaction == true || checkDatabase() == false) 51 | return false; 52 | 53 | if (ADatabase::transaction() == false) 54 | { 55 | setLastError(); 56 | return false; 57 | } 58 | 59 | m_transaction = true; 60 | 61 | return true; 62 | } 63 | //---------------------------------------------------------------------------------------------- 64 | 65 | bool ASQLiteDatabase::commit () 66 | { 67 | if (ADatabase::isOpen() == false || m_transaction == false) 68 | return false; 69 | 70 | if (ADatabase::commit() == false) 71 | { 72 | setLastError(); 73 | 74 | ADatabase::rollback(); 75 | 76 | m_transaction = false; 77 | 78 | return false; 79 | } 80 | 81 | m_transaction = false; 82 | 83 | return true; 84 | } 85 | //---------------------------------------------------------------------------------------------- 86 | 87 | bool ASQLiteDatabase::rollback () 88 | { 89 | if (ADatabase::isOpen() == false || m_transaction == false) 90 | return false; 91 | 92 | if (ADatabase::rollback() == false) 93 | setLastError(); 94 | 95 | m_transaction = false; 96 | 97 | return true; 98 | } 99 | //---------------------------------------------------------------------------------------------- 100 | 101 | AQuery* ASQLiteDatabase::createQuery (const QString& sql, bool prepared) 102 | { 103 | if (checkDatabase() == false) 104 | return NULL; 105 | 106 | AQuery* query = 0; 107 | 108 | if (prepared == true) 109 | query = ADatabase::createPreparedQuery(sql); 110 | else 111 | query = ADatabase::createQuery(sql); 112 | 113 | if (query == NULL) 114 | { 115 | if (m_transaction == true) 116 | rollback(); 117 | } 118 | 119 | return query; 120 | } 121 | //---------------------------------------------------------------------------------------------- 122 | -------------------------------------------------------------------------------- /src/global.cpp: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | //---------------------------------------------------------------------------------------------- 3 | #include "storage/storage_factory.h" 4 | //---------------------------------------------------------------------------------------------- 5 | AGlobal* g_global = NULL; 6 | //---------------------------------------------------------------------------------------------- 7 | 8 | AGlobal* AGlobal::getInstance () 9 | { 10 | if (g_global != NULL) 11 | return g_global; 12 | 13 | return new AGlobal(); 14 | } 15 | //---------------------------------------------------------------------------------------------- 16 | 17 | AGlobal::AGlobal () 18 | { 19 | g_global = this; 20 | 21 | // предпочитаемый список шифров 22 | // $ openssl ciphers -tls1 'HIGH:!TLSv1.2:!aNULL:!MD5:!3DES:!CAMELLIA:!SRP:!PSK:@STRENGTH' 23 | static const char* AVALON_CIPHERS[] = 24 | { 25 | "ECDHE-RSA-AES256-SHA", 26 | "ECDHE-ECDSA-AES256-SHA", 27 | "DHE-RSA-AES256-SHA", 28 | "DHE-DSS-AES256-SHA", 29 | "ECDH-RSA-AES256-SHA", 30 | "ECDH-ECDSA-AES256-SHA", 31 | "AES256-SHA", 32 | "ECDHE-RSA-AES128-SHA", 33 | "ECDHE-ECDSA-AES128-SHA", 34 | "DHE-RSA-AES128-SHA", 35 | "DHE-DSS-AES128-SHA", 36 | "ECDH-RSA-AES128-SHA", 37 | "ECDH-ECDSA-AES128-SHA", 38 | "AES128-SHA", 39 | NULL 40 | }; 41 | 42 | // поддерживаемый список шифров 43 | QList cipher_list; 44 | 45 | const char** ciphers = AVALON_CIPHERS; 46 | while ((*ciphers) != NULL) 47 | { 48 | QSslCipher cipher(*ciphers, QSsl::SslV3); 49 | if (cipher.isNull() == false) 50 | cipher_list.append(cipher); 51 | 52 | ciphers++; 53 | } 54 | 55 | // задание конфигурации ssl по умолчанию 56 | QSslConfiguration ssl_default = QSslConfiguration::defaultConfiguration(); 57 | 58 | ssl_default.setProtocol(QSsl::TlsV1); 59 | ssl_default.setCiphers(cipher_list); 60 | 61 | QSslConfiguration::setDefaultConfiguration(ssl_default); 62 | 63 | // значения по умолчанию 64 | AnonymousName = QString::fromUtf8("Аноним"); 65 | DateFormat = "dd.MM.yyyy hh:mm:ss"; 66 | 67 | Me.ID = 0; 68 | 69 | reload(); 70 | } 71 | //---------------------------------------------------------------------------------------------- 72 | 73 | AGlobal::~AGlobal () 74 | { 75 | g_global = NULL; 76 | } 77 | //---------------------------------------------------------------------------------------------- 78 | 79 | void AGlobal::reload () 80 | { 81 | QSettings settings; 82 | 83 | #ifdef AVALON_USE_ZLIB 84 | Compression = settings.value("storage/compression", false).toInt(); 85 | #endif 86 | 87 | // информация о текущем пользователе avalon 88 | Me.ID = -1; 89 | Me.Name = "(n/a)"; 90 | Me.Nick = "(n/a)"; 91 | Me.RealName = "(n/a)"; 92 | Me.Email = "(n/a)"; 93 | Me.Homepage = "(n/a)"; 94 | Me.Specialization = "(n/a)"; 95 | Me.WhereFrom = "(n/a)"; 96 | Me.Origin = "(n/a)"; 97 | 98 | std::auto_ptr storage(AStorageFactory::getStorage()); 99 | 100 | if (storage.get() != NULL) 101 | { 102 | Me.Name = settings.value("rsdn/login", "").toString(); 103 | storage->whoAmI(Me); 104 | } 105 | } 106 | //---------------------------------------------------------------------------------------------- 107 | -------------------------------------------------------------------------------- /src/forms/form_moderate_ui.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief GUI формы для ввода информации о модерировании 4 | */ 5 | 6 | #ifndef _avalon_form_moderate_ui_h_ 7 | #define _avalon_form_moderate_ui_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | /*! 12 | * \brief Описатель действия для списка действий при создании модерилки 13 | */ 14 | typedef struct AModerateAction 15 | { 16 | QString Action; /*!< \brief Внутреннее описание */ 17 | QString Description; /*!< \brief Человекочитаемое описание */ 18 | bool ForumValid; /*!< \brief Валидность ID форума для действия */ 19 | } AModerateAction; 20 | 21 | /*! 22 | * \brief Описатель форума для перемещения при создании модерилки 23 | */ 24 | typedef struct AModerateForum 25 | { 26 | int ID; /*!< \brief ID */ 27 | QString Name; /*!< \brief Человекочитаемое описание */ 28 | } AModerateForum; 29 | 30 | /*! 31 | * \brief GUI формы для ввода информации о модерировании 32 | */ 33 | class FormModerateUI : public QDialog 34 | { 35 | Q_OBJECT 36 | 37 | public: 38 | 39 | /*! 40 | * \brief Конструктор 41 | * \param parent Родительский виджет 42 | */ 43 | FormModerateUI (QWidget* parent); 44 | ~FormModerateUI (); 45 | 46 | protected: 47 | 48 | /*! 49 | * \brief Функция сохранения расположения и размеров элементов управления формы <при выходе> 50 | */ 51 | void save (); 52 | 53 | /*! 54 | * \brief Функция восстановления расположения и размеров элементов управления формы <при старте> 55 | */ 56 | void restore (); 57 | 58 | /*! 59 | * \brief Список возможных действий 60 | */ 61 | QList m_actions; 62 | 63 | /*! 64 | * \brief Список форумов 65 | */ 66 | QList m_forums; 67 | 68 | QLabel* m_label_action; /*!< \brief Действие */ 69 | QLabel* m_label_forum; /*!< \brief Форум */ 70 | QLabel* m_label_description; /*!< \brief Описание */ 71 | 72 | QComboBox* m_combo_action; /*!< \brief Действие */ 73 | QComboBox* m_combo_forum; /*!< \brief Форум */ 74 | 75 | QTextEdit* m_text_description; /*!< \brief Описание */ 76 | 77 | QPushButton* m_button_ok; /*!< \brief OK */ 78 | QPushButton* m_button_cancel; /*!< \brief Отмена */ 79 | 80 | QHBoxLayout* m_layout; /*!< \brief Общий layout */ 81 | QVBoxLayout* m_layout_left; /*!< \brief Layout левой части от кнопок */ 82 | QHBoxLayout* m_layout_combo_label; /*!< \brief Layout для подписей и списков */ 83 | QVBoxLayout* m_layout_label; /*!< \brief Layout для подписей */ 84 | QVBoxLayout* m_layout_combo; /*!< \brief Layout для списков */ 85 | QVBoxLayout* m_layout_description; /*!< \brief Layout для описания */ 86 | QVBoxLayout* m_layout_button; /*!< \brief Layout для кнопок */ 87 | 88 | QSpacerItem* m_spacer_button; /*!< \brief Удерживает кнопки вверху */ 89 | QSpacerItem* m_spacer_combo; /*!< \brief Устанавливает ширину списков */ 90 | 91 | private slots: 92 | 93 | /*! 94 | * \brief Смена списка с действиями 95 | * \param text Текущий текст в списке 96 | */ 97 | void combo_action_current_index_changed (const QString& text); 98 | }; 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/spellchecker.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Класс подсветки синтаксиса орфографии 4 | */ 5 | 6 | #ifndef _avalon_spellchecker_h_ 7 | #define _avalon_spellchecker_h_ 8 | 9 | #include "sysheaders.h" 10 | 11 | #ifdef AVALON_USE_ASPELL 12 | 13 | /*! 14 | * \brief Класс подсветки синтаксиса орфографии 15 | */ 16 | class ASpellChecker : public QSyntaxHighlighter 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | 22 | /*! 23 | * \brief Стандартный конструктор QSyntaxHighlighter 24 | */ 25 | ASpellChecker (QTextDocument* parent); 26 | ~ASpellChecker (); 27 | 28 | /*! 29 | * \brief Метод проверки слова 30 | * \param text Слово для проверки (в случае, если слово не одно, проверка всегда положительна) 31 | * \param suggest_list Список предложений, если слово неправильно 32 | * \return -1 ошибка, 0 - ОК, 1 - в слове ошибка, в списке suggest варианты исправления 33 | */ 34 | int spellWord (const QString& text, QStringList* suggest_list); 35 | 36 | /*! 37 | * \brief Метод добавления слова в словарь 38 | * \param text Слово для добавления 39 | * \return false - ошибка сохранения словаря 40 | */ 41 | bool addWord (const QString& text); 42 | 43 | protected: 44 | 45 | /*! 46 | * \brief Метод QSyntaxHighlighter для подсветки 47 | */ 48 | virtual void highlightBlock (const QString& text); 49 | 50 | private: 51 | 52 | /*! 53 | * \brief Метод подсветки блока текста 54 | * \param correct Флаг корректности слова (true - слово правильное) 55 | * \param start Начало слова 56 | * \param length Длина слова 57 | */ 58 | void markWord (bool correct, int start, int length); 59 | 60 | /*! 61 | * \brief Метод получения списка вариантов для замены ошибочного слова 62 | * \param speller Класс проверки орфографии для требуемого языка 63 | * \param word Ошибочное слово в кодировке UTF-8 64 | * \param size Длина слова в байтах 65 | * \param suggest_list Список вариантов 66 | */ 67 | void suggestList (AspellSpeller* speller, const char* word, int size, QStringList* suggest_list); 68 | 69 | /*! 70 | * \brief Метод определения слов со "сбитым" регистром (эти слова исключаются из проверки) 71 | * Используется только для латиницы, чтобы игнорировать составные идентификаторы в участках кода 72 | * \param text слово для проверки 73 | * \return true, если слово с естественным регистром букв, 74 | * false, если слово со "сбитым" регистром букв: 75 | * 76 | * country - true - слово в нижнем регистре 77 | * USSR - true - аббревиатура 78 | * Russia - true - имена собственные могут начинаться с заглавной 79 | * camelCase - false - заглавная буква в середине слова 80 | * TUrl - false - более одной заглавной буквы при наличии строчных 81 | * 82 | */ 83 | bool isNativeCase (const QString& text); 84 | 85 | /*! 86 | * \brief Получение имени файла для словаря 87 | * \return Имя файла словаря 88 | */ 89 | QString dictFile (); 90 | 91 | AspellConfig* m_aspell_config_en; /*!< \brief Конфиг aspell для проверки английского */ 92 | AspellConfig* m_aspell_config_ru; /*!< \brief Конфиг aspell для проверки русского */ 93 | 94 | AspellSpeller* m_aspell_speller_en; /*!< \brief Спеллер aspell для проверки английского */ 95 | AspellSpeller* m_aspell_speller_ru; /*!< \brief Спеллер aspell для проверки русского */ 96 | 97 | QSet m_stop_words; /*!< \brief Стоп-слова (rsdn specific) */ 98 | }; 99 | 100 | #endif // AVALON_USE_ASPELL 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /src/forms/form_request.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Форма для отображения прогресса работы и для HTTP запросов 4 | */ 5 | 6 | #ifndef _avalon_form_request_h_ 7 | #define _avalon_form_request_h_ 8 | 9 | #include "iprogress.h" 10 | #include "form_request_ui.h" 11 | 12 | /*! 13 | * \brief Форма для отображения прогресса работы и для HTTP запросов 14 | */ 15 | class FormRequest : 16 | public FormRequestUI, 17 | public IProgress 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | 23 | /*! 24 | * \brief Конструктор для простого прогресса 25 | * \param parent Родительский виджет (форма) 26 | */ 27 | FormRequest (QWidget* parent); 28 | 29 | /*! 30 | * \brief Конструктор для HTTP запроса с прогрессом 31 | * \param parent Родительский виджет (форма) 32 | * \param host Адрес хоста 33 | * \param port Номер порта 34 | * \param header Заголовок HTTP 35 | * \param data POST данные для HTTP 36 | */ 37 | FormRequest (QWidget* parent, const QString& host, quint16 port, const QString& header, const QString& data); 38 | 39 | ~FormRequest (); 40 | 41 | /*! 42 | * \brief Получение HTTP ответа 43 | * \param error - флаг ошибки если тело ответа не может быть раскодированно - если true, то результат есть описание ошибки 44 | * \return Тело HTTP ответа 45 | */ 46 | QString getResponse (bool &error); 47 | 48 | /*! 49 | * \brief Получение заголовка HTTP ответа 50 | * \return Тело заголовка HTTP ответа 51 | */ 52 | QString getResponseHeader (); 53 | 54 | /*! 55 | * \brief Возвращает прокси-сервер по умолчанию 56 | * \param webkit Флаг запроса прокси для области отображения сообщений 57 | * \return Описатель прокси-сервера 58 | */ 59 | static QNetworkProxy defaultProxy (bool webkit = false); 60 | 61 | private: 62 | 63 | /*! 64 | * \brief Обработчик HTTP 65 | */ 66 | QHttp m_http; 67 | 68 | /*! 69 | * \brief Наименование протокола (HTTP/HTTPS) 70 | */ 71 | QString m_proto; 72 | 73 | /*! 74 | * \brief Байт к отправке 75 | */ 76 | int m_to_send; 77 | 78 | /*! 79 | * \brief Хак, чтобы не отображалось две попытки соединения 80 | */ 81 | int m_hack; 82 | 83 | /*! 84 | * \brief Красивое форматирование байт в KB/MB/GB 85 | * \param size Количество байт 86 | * \return Строка с числом и единицей измерения 87 | */ 88 | QString formatPrettyBytes (qint64 size); 89 | 90 | /*! 91 | * \brief Обработчик прогресса с пустым статусом 92 | * \param percent [0..100] процент выполнения 93 | */ 94 | void onProgress (int percent); 95 | 96 | /*! 97 | * \brief Обработчик прогресса 98 | * \param percent [0..100] процент выполнения 99 | * \param status Строка состояния 100 | */ 101 | void onProgress (int percent, const QString& status); 102 | 103 | private slots: 104 | 105 | // 106 | // события HTTP обработчика 107 | // 108 | 109 | void process_state_changed (int state); /*!< \brief Смена состояния */ 110 | void process_data_read_progress (int done, int total); /*!< \brief Прогресс чтения данных */ 111 | void process_data_send_progress (int done, int total); /*!< \brief Прогресс отправки данных */ 112 | void process_response_header_received (const QHttpResponseHeader& resp); /*!< \brief Получен заголовок ответа */ 113 | void process_request_started (int id); /*!< \brief Начало выполнения запроса */ 114 | void process_request_finished (int id, bool error); /*!< \brief Окончание выполнения запроса */ 115 | void process_ssl_errors (const QList &errors); /*!< \brief Ошибка SSL */ 116 | }; 117 | 118 | #endif // _avalon_form_request_h_ 119 | -------------------------------------------------------------------------------- /src/widgets/message_view.cpp: -------------------------------------------------------------------------------- 1 | #include "message_view.h" 2 | //---------------------------------------------------------------------------------------------- 3 | #include "global.h" 4 | #include "formatter.h" 5 | #include "storage/storage_factory.h" 6 | //---------------------------------------------------------------------------------------------- 7 | 8 | AMessageView::AMessageView (QWidget* parent) : AWebViewWidget (parent), IMessageView () 9 | { 10 | m_main_form = 0; 11 | m_forum_tree = 0; 12 | m_message_tree = 0; 13 | 14 | connect(View->page(), SIGNAL(linkClicked(const QUrl&)), this, SLOT(link_clicked(const QUrl&))); 15 | connect(View->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), this, SLOT(link_hover(const QString&, const QString&, const QString&))); 16 | } 17 | //---------------------------------------------------------------------------------------------- 18 | 19 | AMessageView::~AMessageView () 20 | { 21 | } 22 | //---------------------------------------------------------------------------------------------- 23 | 24 | void AMessageView::setMainForm (IFormMain* itf) 25 | { 26 | m_main_form = itf; 27 | } 28 | //---------------------------------------------------------------------------------------------- 29 | 30 | void AMessageView::setForumTree (IForumTree* itf) 31 | { 32 | m_forum_tree = itf; 33 | } 34 | //---------------------------------------------------------------------------------------------- 35 | 36 | void AMessageView::setMessageTree (IMessageTree* itf) 37 | { 38 | m_message_tree = itf; 39 | } 40 | //---------------------------------------------------------------------------------------------- 41 | 42 | void AMessageView::setMessage (const AMessageInfo& message, const AForumInfo* forum) 43 | { 44 | Q_ASSERT(forum != NULL && forum->IDGroup == SPECIAL_ID_GROUP); 45 | 46 | QString body; 47 | 48 | // загрузка рейтингов (если требуется) 49 | if (forum != NULL && forum->Rated == true && message.IDUser != AGlobal::getInstance()->Me.ID) 50 | { 51 | // "тихий" контроль ошибок, т.к. не страшно, если пузомерка не загрузится 52 | std::auto_ptr storage(AStorageFactory::getStorage()); 53 | 54 | if (storage.get() != NULL) 55 | { 56 | AMessageRatingList list; 57 | 58 | if (storage->getMessageRatingList(message.ID, list) != false) 59 | body = AFormatter::formatMessage(message, forum, true, (list.count() > 0 ? &list : NULL)); 60 | else 61 | body = AFormatter::formatMessage(message, forum, true); 62 | } 63 | else 64 | body = AFormatter::formatMessage(message, forum, true); 65 | } 66 | else 67 | body = AFormatter::formatMessage(message, forum); 68 | 69 | View->setHtml(body); 70 | } 71 | //---------------------------------------------------------------------------------------------- 72 | 73 | void AMessageView::clear () 74 | { 75 | View->setHtml(QString()); 76 | } 77 | //---------------------------------------------------------------------------------------------- 78 | 79 | void AMessageView::link_clicked (const QUrl& url) 80 | { 81 | QString special = url.toString(); 82 | 83 | if (m_message_tree != NULL) 84 | m_message_tree->processUrl(special); 85 | else 86 | QDesktopServices::openUrl(url); 87 | } 88 | //---------------------------------------------------------------------------------------------- 89 | 90 | void AMessageView::link_hover (const QString& link, const QString& /*title*/, const QString& /*textContent*/) 91 | { 92 | bool avalon_link = link.indexOf("avalon:") == 0; 93 | 94 | if (link.length() != 0 && avalon_link == false) 95 | View->LinkHovered = true; 96 | else 97 | View->LinkHovered = false; 98 | 99 | if (avalon_link == false) 100 | m_main_form->showStatus(link); 101 | } 102 | //---------------------------------------------------------------------------------------------- 103 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin : 0; 3 | padding : 0; 4 | } 5 | 6 | body { 7 | width : 100%; 8 | font-family : verdana, geneva, sans-serif; 9 | font-size : 12pt; 10 | } 11 | 12 | #message { 13 | width : 100%; 14 | } 15 | 16 | #header { 17 | width : 100%; 18 | height : 24px; 19 | background : #E6F2E6; 20 | } 21 | 22 | #title { 23 | } 24 | 25 | #move_to_thread 26 | { 27 | float : left; 28 | margin-top : 4px; 29 | margin-left : 4px; 30 | } 31 | 32 | #subject { 33 | float : left; 34 | font-size : 14px; 35 | font-weight : bold; 36 | margin-top : 3px; 37 | margin-left : 5px; 38 | color : black; 39 | text-decoration : none; 40 | } 41 | 42 | #rate { 43 | float : right; 44 | margin-right : 4px; 45 | margin-top : 4px; 46 | } 47 | 48 | #rate_plus_1 { 49 | margin-right : 5px; 50 | } 51 | 52 | #rate_1 { 53 | } 54 | 55 | #rate_2 { 56 | } 57 | 58 | #rate_3 { 59 | } 60 | 61 | #rate_cross { 62 | margin-left : 5px; 63 | } 64 | 65 | #rate_smile { 66 | margin-left : 5px; 67 | margin-right : 5px; 68 | } 69 | 70 | #rate_plus { 71 | } 72 | 73 | #rate_minus { 74 | } 75 | 76 | #moderate { 77 | margin-left : 5px; 78 | } 79 | 80 | #info { 81 | background : #FFFFF6; 82 | margin-left : 5px; 83 | margin-right : 5px; 84 | } 85 | 86 | .info_left { 87 | float : left; 88 | width : 80px; 89 | font-weight : bold; 90 | } 91 | 92 | .info_right { 93 | float : left; 94 | } 95 | 96 | .info_right #text { 97 | margin-bottom : 3px; 98 | } 99 | 100 | #rating { 101 | cursor : pointer; 102 | color : black; 103 | text-decoration : none; 104 | } 105 | 106 | #rating_plus_1 { 107 | } 108 | 109 | #rating_1 { 110 | } 111 | 112 | #rating_2 { 113 | } 114 | 115 | #rating_3 { 116 | } 117 | 118 | #rating_smile { 119 | } 120 | 121 | #rating_plus { 122 | } 123 | 124 | #rating_minus { 125 | } 126 | 127 | #text { 128 | margin-left : 5px; 129 | margin-right : 5px; 130 | } 131 | 132 | #text h1 { 133 | color : #4580A0; 134 | } 135 | 136 | #text h2 { 137 | color : #4580A0; 138 | } 139 | 140 | #text h3 { 141 | color : #4580A0; 142 | } 143 | 144 | #text h4 { 145 | color : #4580A0; 146 | } 147 | 148 | #text h5 { 149 | color : #4580A0; 150 | } 151 | 152 | #text h6 { 153 | color : #4580A0; 154 | } 155 | 156 | #text p { 157 | margin-top : 10px; 158 | } 159 | 160 | #text p.quote_black { 161 | } 162 | 163 | #text p.quote_blue { 164 | color : darkblue; 165 | } 166 | 167 | #text p.quote_red { 168 | color : darkred; 169 | } 170 | 171 | #text p.quote_green { 172 | color : darkgreen; 173 | } 174 | 175 | #text span.alert { 176 | color : red; 177 | } 178 | 179 | #text blockquote { 180 | margin-top : 10px; 181 | background-color : #FFFFE7; 182 | } 183 | 184 | #text pre { 185 | font : 10pt Courier-New; 186 | margin-top : 10px; 187 | } 188 | 189 | #text table { 190 | margin : 5px; 191 | } 192 | 193 | #text table tbody th { 194 | background-color : #D4D4D4; 195 | } 196 | 197 | #text table tbody td { 198 | background-color : #F4F4F4; 199 | } 200 | 201 | #text table tbody td ol { 202 | margin-left : 50px; 203 | } 204 | 205 | #text table tbody td ul { 206 | margin-left : 50px; 207 | } 208 | 209 | #text .moderator { 210 | background : none repeat scroll 0 0 #FFCCCC; 211 | border-top : 1px solid #FF0000; 212 | color : #7F0000; 213 | margin-top : 10px; 214 | } 215 | 216 | #text .moderator p { 217 | margin : 2px; 218 | } 219 | 220 | #text .tagline { 221 | background : none repeat scroll 0 0 #F6F6F6; 222 | border-top : 1px solid #DDDDDD; 223 | margin-top : 10px; 224 | } 225 | 226 | #text .tagline p { 227 | margin : 2px; 228 | } 229 | 230 | #footer { 231 | margin-top : 20px; 232 | width : 100%; 233 | height : 20px; 234 | background : #E6F2E6; 235 | } 236 | -------------------------------------------------------------------------------- /src/storage/mysql_storage.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Хранилище на базе MySQL 4 | */ 5 | 6 | #ifndef _avalon_mysql_storage_h_ 7 | #define _avalon_mysql_storage_h_ 8 | 9 | #include "istorage.h" 10 | #include "mysql_database.h" 11 | 12 | /*! 13 | * \brief Хранилище на базе MySQL 14 | */ 15 | class AMySQLStorage : 16 | public AMySQLDatabase, // обертка для работы с базами MySQL 17 | public IAStorage // интерфейс, который возвращает фабрика хранилищ при работе 18 | { 19 | public: 20 | 21 | AMySQLStorage (); 22 | ~AMySQLStorage (); 23 | 24 | private: 25 | 26 | /*! 27 | * \brief Устанавливает IAStorage::SetLastError("OK") 28 | * \return Всегда true 29 | */ 30 | bool returnSuccess (); 31 | 32 | /*! 33 | * \brief Устанавливает IAStorage::SetLastError(message) 34 | * \param message Сообщение об ошибке 35 | * \return Всегда false 36 | */ 37 | bool returnError (const QString& message); 38 | 39 | // IAStorage (см. istorage.h) 40 | private: 41 | 42 | bool ping (); 43 | bool createDatabase (); 44 | bool whoAmI (AUserInfo& info); 45 | bool getRowVersion (ARowVersion& list); 46 | bool getForumList (AForumGroupInfoList& list, bool subscribed_only); 47 | bool setForumList (const AForumGroupInfoList& list); 48 | bool getSubscribedForumList (ASubscribedForumInfoList& list); 49 | bool setSubscribedForumList (const ASubscribedForumInfoList& list); 50 | bool getForumInfo (int id_forum, AForumInfo& info); 51 | bool setUserList (const AUserInfoList& list, const QString& row_version); 52 | bool getMaxIDMessage (int& max_id); 53 | bool getMinIDMessage (int& min_id); 54 | bool getMessageQuery (ADataQuery& query); 55 | bool setMessageList (const ADataList& list, const ARowVersion& row_version, bool save_row_version); 56 | bool getUnreadCount (AUnreadForumCountInfoList& list, int id_me); 57 | bool getForumTopicList (int id_forum, int count, QList& list); 58 | bool getTopicInfoList (int id_forum, AMessageInfoGUIPtrList& list, int id_me); 59 | bool getTopicMessageList (int id_forum, int id_topic, AMessageInfoGUIPtrList& list, IMessageInfoGUIFactory* factory); 60 | bool getMessageBody (int id_message, QString& body); 61 | bool setIDsAsRead (const QList& list, AIDSet type, bool read, QDateTime date); 62 | bool unsubscribe (const QList& list, AIDSet type, bool clean); 63 | bool getMessageIds (int from_id, int to_id, QList& list); 64 | bool addMessage2Send (const AMessage2Send& info); 65 | bool getMessage2SendList (AMessageInfoList& list, bool drafts); 66 | bool getModerate2SendInfo (int id, AModerate2Send& info); 67 | bool addRating2Send (const ARating2Send& info); 68 | bool getRating2SendList (AMessageInfoList& message_list, ARating2SendList& rating_list); 69 | bool getRating2SendList (ARating2SendList& list); 70 | bool changeRating (int id, int new_rate); 71 | bool addModerate2Send (const AModerate2Send& info); 72 | bool getModerate2SendList (AMessageInfoList& message_list, AModerate2SendList& moderate_list); 73 | bool getModerate2SendList (AModerate2SendList& list); 74 | bool setCommitResult (const ACommitInfo& info); 75 | bool deleteSpecial (const QList& ids, int id_special); 76 | bool getMessageRatingList (int id_message, AMessageRatingList& list); 77 | bool compressStorage (IProgress* progress); 78 | bool uncompressStorage (IProgress* progress); 79 | bool getMessagePath (int id_message, int& id_forum, QList& path); 80 | bool addBroken (int id_message, bool is_topic); 81 | bool hasBroken (bool& result); 82 | bool getMyMessageList (int id_me, int count, QList& list); 83 | bool getAnswers2MeList (int id_me, int count, QList& list); 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/storage/sqlite_storage.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Хранилище на базе SQLite 4 | */ 5 | 6 | #ifndef _avalon_sqlite_storage_h_ 7 | #define _avalon_sqlite_storage_h_ 8 | 9 | #include "istorage.h" 10 | #include "sqlite_database.h" 11 | 12 | /*! 13 | * \brief Хранилище на базе SQLite 14 | */ 15 | class ASQLiteStorage : 16 | public ASQLiteDatabase, // обертка для работы с базами SQLite 17 | public IAStorage // интерфейс, который возвращает фабрика хранилищ при работе 18 | { 19 | public: 20 | 21 | ASQLiteStorage (); 22 | ~ASQLiteStorage (); 23 | 24 | private: 25 | 26 | /*! 27 | * \brief Устанавливает IAStorage::SetLastError("OK") 28 | * \return Всегда true 29 | */ 30 | bool returnSuccess (); 31 | 32 | /*! 33 | * \brief Устанавливает IAStorage::SetLastError(message) 34 | * \param message Сообщение об ошибке 35 | * \return Всегда false 36 | */ 37 | bool returnError (const QString& message); 38 | 39 | // IAStorage (см. istorage.h) 40 | private: 41 | 42 | bool ping (); 43 | bool createDatabase (); 44 | bool whoAmI (AUserInfo& info); 45 | bool getRowVersion (ARowVersion& list); 46 | bool getForumList (AForumGroupInfoList& list, bool subscribed_only); 47 | bool setForumList (const AForumGroupInfoList& list); 48 | bool getSubscribedForumList (ASubscribedForumInfoList& list); 49 | bool setSubscribedForumList (const ASubscribedForumInfoList& list); 50 | bool getForumInfo (int id_forum, AForumInfo& info); 51 | bool setUserList (const AUserInfoList& list, const QString& row_version); 52 | bool getMaxIDMessage (int& max_id); 53 | bool getMinIDMessage (int& min_id); 54 | bool getMessageQuery (ADataQuery& query); 55 | bool setMessageList (const ADataList& list, const ARowVersion& row_version, bool save_row_version); 56 | bool getUnreadCount (AUnreadForumCountInfoList& list, int id_me); 57 | bool getForumTopicList (int id_forum, int count, QList& list); 58 | bool getTopicInfoList (int id_forum, AMessageInfoGUIPtrList& list, int id_me); 59 | bool getTopicMessageList (int id_forum, int id_topic, AMessageInfoGUIPtrList& list, IMessageInfoGUIFactory* factory); 60 | bool getMessageBody (int id_message, QString& body); 61 | bool setIDsAsRead (const QList& list, AIDSet type, bool read, QDateTime date); 62 | bool unsubscribe (const QList& list, AIDSet type, bool clean); 63 | bool getMessageIds (int from_id, int to_id, QList& list); 64 | bool addMessage2Send (const AMessage2Send& info); 65 | bool getMessage2SendList (AMessageInfoList& list, bool drafts); 66 | bool getModerate2SendInfo (int id, AModerate2Send& info); 67 | bool addRating2Send (const ARating2Send& info); 68 | bool getRating2SendList (AMessageInfoList& message_list, ARating2SendList& rating_list); 69 | bool getRating2SendList (ARating2SendList& list); 70 | bool changeRating (int id, int new_rate); 71 | bool addModerate2Send (const AModerate2Send& info); 72 | bool getModerate2SendList (AMessageInfoList& message_list, AModerate2SendList& moderate_list); 73 | bool getModerate2SendList (AModerate2SendList& list); 74 | bool setCommitResult (const ACommitInfo& info); 75 | bool deleteSpecial (const QList& ids, int id_special); 76 | bool getMessageRatingList (int id_message, AMessageRatingList& list); 77 | bool compressStorage (IProgress* progress); 78 | bool uncompressStorage (IProgress* progress); 79 | bool getMessagePath (int id_message, int& id_forum, QList& path); 80 | bool addBroken (int id_message, bool is_topic); 81 | bool hasBroken (bool& result); 82 | bool getMyMessageList (int id_me, int count, QList& list); 83 | bool getAnswers2MeList (int id_me, int count, QList& list); 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/forms/form_main.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Главная форма приложения 4 | */ 5 | 6 | #ifndef _avalon_form_main_h_ 7 | #define _avalon_form_main_h_ 8 | 9 | #include "form_main_ui.h" 10 | 11 | /*! 12 | * \brief Класс главной формы приложения 13 | */ 14 | class AFormMain : 15 | public AFormMainUI, 16 | public IFormMain 17 | { 18 | Q_OBJECT; 19 | 20 | public: 21 | 22 | AFormMain (); 23 | ~AFormMain (); 24 | 25 | private: 26 | 27 | /*! 28 | * \brief Таймер для периодической синхронизации 29 | */ 30 | QTimer m_timer; 31 | 32 | /*! 33 | * \brief Обновление статуса по умолчанию 34 | */ 35 | void setDefaultStatus (); 36 | 37 | /*! 38 | * \brief Получение интервала периодической синхронизации 39 | * \return число ms между синхронизациями 40 | */ 41 | int synchronizeInterval (); 42 | 43 | private slots: 44 | 45 | // 46 | // обработчики меню 47 | // 48 | 49 | // 50 | // Меню "Файл" 51 | // 52 | 53 | void menu_file_exit_triggered (); /*!< \brief Файл / Выход */ 54 | 55 | // 56 | // Меню "Вид" 57 | // 58 | 59 | void menu_view_source_triggered (); /*!< \brief Вид / Просмотр исходника */ 60 | 61 | // 62 | // Меню "Перейти" 63 | // 64 | 65 | void menu_goto_next_unread_article_triggered (); /*!< \brief К следующему непрочитанному сообщению */ 66 | void menu_goto_next_unread_thread_triggered (); /*!< \brief К следующей непрочитанной ветке */ 67 | void menu_goto_next_unread_forum_triggered (); /*!< \brief К следующему непрочитанному форуму */ 68 | void menu_goto_by_id_triggered (); /*!< \brief К сообщению по ID/ссылке */ 69 | 70 | // 71 | // Меню "Сервис" 72 | // 73 | 74 | void menu_service_synchronize_triggered (); /*!< \brief Сервис / Синхронизировать */ 75 | void menu_service_download_triggered (); /*!< \brief Сервис / Загрузить сообщение / ветку */ 76 | void menu_service_new_message_triggered (); /*!< \brief Сервис / Новое сообщение */ 77 | void menu_service_reply_triggered (); /*!< \brief Сервис / Ответить */ 78 | void menu_service_mark_all_as_read_triggered (); /*!< \brief Сервис / Пометить все как прочитанные */ 79 | void menu_service_mark_patrial_as_read_triggered (); /*!< \brief Сервис / Пометить до даты как прочитанные */ 80 | void menu_service_mark_all_as_unread_triggered (); /*!< \brief Сервис / Пометить все как непрочитанные */ 81 | void menu_service_mark_patrial_as_unread_triggered (); /*!< \brief Сервис / Пометить после даты как непрочитанные */ 82 | void menu_service_subscribe_triggered (); /*!< \brief Сервис / Подписка на форумы */ 83 | void menu_service_settings_triggered (); /*!< \brief Сервис / Настройки */ 84 | 85 | #ifdef AVALON_USE_ZLIB 86 | void menu_service_storage_compress_triggered (); /*!< \brief Сервис / Хранилище / Упаковать */ 87 | void menu_service_storage_uncompress_triggered (); /*!< \brief Сервис / Хранилище / Распаковать */ 88 | #endif 89 | 90 | // 91 | // Меню "?" 92 | // 93 | 94 | void menu_q_yandex_url_triggered (); /*!< \brief ? / Перейти на Яндекс */ 95 | void menu_q_wikipedia_url_triggered (); /*!< \brief ? / Перейти на Википедию */ 96 | void menu_q_google_url_triggered (); /*!< \brief ? / Перейти на Google */ 97 | void menu_q_rsdn_url_triggered (); /*!< \brief ? / Перейти на RSDN */ 98 | void menu_q_about_triggered (); /*!< \brief ? / О программе */ 99 | 100 | // 101 | // Тулбар 102 | // 103 | 104 | void tool_bar_backward_triggered (); /*!< \brief "Назад" */ 105 | void tool_bar_forward_triggered (); /*!< \brief "Вперед" */ 106 | 107 | /*! 108 | * \brief Обработчик таймера для периодической синхронизации с RSDN 109 | */ 110 | void timer_on_timer (); 111 | 112 | // IFormMain 113 | private: 114 | 115 | void showStatus (const QString& value); 116 | 117 | void setEnabledAction (AvalonActions actions, bool enabled); 118 | }; 119 | 120 | #endif // _avalon_form_main_h_ 121 | -------------------------------------------------------------------------------- /src/formatter.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Форматтер текста сообщения для отображения 4 | */ 5 | 6 | #ifndef _avalon_formatter_h_ 7 | #define _avalon_formatter_h_ 8 | 9 | #include "parser.h" 10 | #include "model/all.h" 11 | 12 | /*! 13 | * \brief Форматтер текста сообщения для отображения 14 | */ 15 | class AFormatter 16 | { 17 | public: 18 | 19 | /*! 20 | * \brief Форматтер HTML сообщения из текстового 21 | * \param message Дескриптор сообщения 22 | * \param forum Дескриптор форума сообщения (не спец-форума) 23 | * \param rated Флаг того, что форум оценивается 24 | * \param rating_list Список рейтингов (если есть) 25 | * \return Строка с HTML кодом для отображения 26 | */ 27 | static QString formatMessage (const AMessageInfo& message, const AForumInfo* forum = NULL, bool rated = false, const AMessageRatingList* rating_list = NULL); 28 | 29 | /*! 30 | * \brief Нормализация тела сообщения - выправление криворукого квотинга, ссылок, приветствий 31 | * \param body Тело сообщения 32 | * \param nick Ник автора 33 | * \return Строка с нормализованным телом сообщения 34 | */ 35 | static QString normalizeBody (const QString& body, const QString& nick); 36 | 37 | private: 38 | 39 | /*! 40 | * \brief Блок заголовочного html / head / body 41 | * \return Строка html для вставки 42 | */ 43 | static QString headHTML (); 44 | 45 | /*! 46 | * \brief Блок темы сообщения 47 | * \param message Дескриптор сообщения 48 | * \param forum Дескриптор форума 49 | * \return Строка html для вставки 50 | */ 51 | static QString subjectHTML (const AMessageInfo& message, const AForumInfo* forum = NULL); 52 | 53 | /*! 54 | * \brief Блок возможности выставлять оценки 55 | * \param rated Флаг генерации блока для выставления оценок 56 | * \param moderated Флаг генерации блока для модерилок 57 | * \return Строка html для вставки 58 | */ 59 | static QString rateHTML (bool rated, bool moderated); 60 | 61 | /*! 62 | * \brief Блок информации об авторе сообщения 63 | * \param id ID пользователя 64 | * \param nick Ник пользователя 65 | * \return Строка html для вставки 66 | */ 67 | static QString authorHTML (int id, const QString nick); 68 | 69 | /*! 70 | * \brief Блок поставленных оценок 71 | * \param id ID сообщения 72 | * \param rating_list Список оценок 73 | * \return Строка html для вставки 74 | */ 75 | static QString ratingHTML (int id, const AMessageRatingList* rating_list); 76 | 77 | /*! 78 | * \brief Блок подвала html 79 | * \return Строка html для вставки 80 | */ 81 | static QString footerHTML (); 82 | 83 | /*! 84 | * \brief Форматирование блока текста 85 | * \param block Описатель блока 86 | * \return Строка html для вставки 87 | */ 88 | static QString formatParsedBlock (const AParsedBlock& block); 89 | 90 | /*! 91 | * \brief Форматирование списка квотированных строк 92 | * \param list Список строк 93 | * \param type Тип блока строк 94 | * \param sub_type Подтип блока строк 95 | * \return Строка html для вставки 96 | */ 97 | static QString formatQuotedStringList (const AQuotedStringList& list, AParsedBlockType type, AParsedBlockSubType sub_type); 98 | 99 | /*! 100 | * \brief Форматирование блока текста 101 | * \param text Текст 102 | * \return Строка html для вставки 103 | */ 104 | static QString formatSimpleText (const QString& text); 105 | 106 | /*! 107 | * \brief Форматирование гиперссылок в блоке текста 108 | * \param text Текст 109 | * \return Строка html для вставки 110 | */ 111 | static QString formatHyperlinks (const QString& text); 112 | 113 | /*! 114 | * \brief Создание аббриветауры ника из полного имени 115 | * \param nick Ник 116 | * \return 1-3 символа 117 | */ 118 | static QString nick3 (const QString& nick); 119 | 120 | /*! 121 | * \brief Форматтер HTML сообщения из куска простого текста (без блоков кода) 122 | * \param text Текст блока 123 | * \param message Описатель сообщения 124 | * \return Строка с HTML кодом для отображения 125 | */ 126 | static QString processSimpleText (const QString& text, const AMessageInfo& message); 127 | }; 128 | 129 | #endif // _avalon_formatter_h_ 130 | -------------------------------------------------------------------------------- /src/forms/form_moderate.cpp: -------------------------------------------------------------------------------- 1 | #include "form_moderate.h" 2 | //---------------------------------------------------------------------------------------------- 3 | #include "storage/storage_factory.h" 4 | //---------------------------------------------------------------------------------------------- 5 | 6 | FormModerate::FormModerate (QWidget* parent, int id_message, int edit_id) : FormModerateUI (parent) 7 | { 8 | m_forum_tree = 0; 9 | 10 | m_id_edit = edit_id; 11 | m_id_message = id_message; 12 | 13 | if (m_id_edit != 0) 14 | { 15 | m_button_ok->setEnabled(false); 16 | 17 | // получение информации о модерилке 18 | std::auto_ptr storage(AStorageFactory::getStorage()); 19 | 20 | if (storage.get() == NULL) 21 | { 22 | QMessageBox::critical(parent, QString::fromUtf8("Ошибка!"), QString::fromUtf8("Не выбрано хранилище данных")); 23 | return; 24 | } 25 | 26 | AModerate2Send info; 27 | 28 | if (storage->getModerate2SendInfo(m_id_edit, info) == false) 29 | { 30 | storage->showError(parent); 31 | return; 32 | } 33 | 34 | // установка значений контролов 35 | 36 | // действие 37 | for (int i = 0; i < m_actions.count(); i++) 38 | if (m_actions[i].Action == info.Action) 39 | { 40 | m_combo_action->setCurrentIndex(i); 41 | break; 42 | } 43 | 44 | // форум 45 | if (info.IDForum != 0) 46 | { 47 | for (int i = 0; i < m_forums.count(); i++) 48 | if (m_forums[i].ID == info.IDForum) 49 | { 50 | m_combo_forum->setCurrentIndex(i); 51 | break; 52 | } 53 | } 54 | 55 | // описание 56 | m_text_description->setText(info.Description); 57 | 58 | m_button_ok->setEnabled(true); 59 | } 60 | 61 | connect(m_button_ok, SIGNAL(clicked()), this, SLOT(button_ok_clicked())); 62 | connect(m_button_cancel, SIGNAL(clicked()), this, SLOT(button_cancel_clicked())); 63 | } 64 | //---------------------------------------------------------------------------------------------- 65 | 66 | FormModerate::~FormModerate () 67 | { 68 | } 69 | //---------------------------------------------------------------------------------------------- 70 | 71 | void FormModerate::closeEvent (QCloseEvent* event) 72 | { 73 | // сохранение layout 74 | save(); 75 | 76 | event->accept(); 77 | 78 | deleteLater(); 79 | } 80 | //---------------------------------------------------------------------------------------------- 81 | 82 | void FormModerate::setForumTree (IForumTree* itf) 83 | { 84 | m_forum_tree = itf; 85 | } 86 | //---------------------------------------------------------------------------------------------- 87 | 88 | void FormModerate::button_ok_clicked () 89 | { 90 | AModerate2Send info; 91 | 92 | int action_idx = m_combo_action->currentIndex(); 93 | 94 | info.IDMessage = m_id_message; 95 | info.Action = m_actions[action_idx].Action; 96 | info.IDForum = 0; 97 | info.Description = m_text_description->toPlainText(); 98 | info.AsModerator = false; 99 | info.Date = QDateTime::currentDateTime(); 100 | 101 | if (m_id_edit == 0) 102 | info.ID = info.Date.toTime_t(); // это локальный ID 103 | else 104 | info.ID = m_id_edit; 105 | 106 | if (m_actions[action_idx].ForumValid) 107 | info.IDForum = m_forums[m_combo_forum->currentIndex()].ID; 108 | 109 | // получение хранилища 110 | std::auto_ptr storage(AStorageFactory::getStorage()); 111 | 112 | if (storage.get() == NULL) 113 | { 114 | QMessageBox::critical(this, QString::fromUtf8("Ошибка!"), QString::fromUtf8("Не выбрано хранилище данных")); 115 | return; 116 | } 117 | 118 | if (storage->addModerate2Send(info) == false) 119 | { 120 | storage->showError(this); 121 | return; 122 | } 123 | 124 | // обновление в дереве форумов количества непрочитаных сообщений 125 | if (m_forum_tree != NULL) 126 | m_forum_tree->reloadUnread(false); 127 | 128 | // сохранение layout 129 | save(); 130 | 131 | // OK 132 | accept(); 133 | } 134 | //---------------------------------------------------------------------------------------------- 135 | 136 | void FormModerate::button_cancel_clicked () 137 | { 138 | // сохранение layout 139 | save(); 140 | 141 | // Отмена 142 | reject(); 143 | } 144 | //---------------------------------------------------------------------------------------------- 145 | -------------------------------------------------------------------------------- /src/webservice.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Класс для работы с вебсервисами RSDN 4 | */ 5 | 6 | #ifndef _avalon_webservice_h_ 7 | #define _avalon_webservice_h_ 8 | 9 | #include "iprogress.h" 10 | #include "model/all.h" 11 | 12 | /*! 13 | * \brief Класс для работы с вебсервисами RSDN 14 | */ 15 | class AWebservice 16 | { 17 | public: 18 | 19 | /*! 20 | * \brief Возвращает заголовок и данные для POST запроса получения списка форумов 21 | * \param header Заголовок HTTP запроса 22 | * \param data Данные для POST запроса 23 | * \param progress Прогресс 24 | */ 25 | static void getForumList_WebserviceQuery (QString& header, QString& data, IProgress* progress = NULL); 26 | 27 | /*! 28 | * \brief Парсит ответ со списком форумов и заносит результат в список 29 | * \param data Строка с данными HTTP ответа 30 | * \param list Список групп/форумов 31 | * \param progress Прогресс 32 | */ 33 | static void getForumList_WebserviceParse (const QString& data, AForumGroupInfoList& list, IProgress* progress = NULL); 34 | 35 | /*! 36 | * \brief Возвращает заголовок и данные для POST запроса получения списка форумов 37 | * \param header Заголовок HTTP запроса 38 | * \param data Данные для POST запроса 39 | * \param last_row_version Версия строк (снимка) 40 | * \param progress Прогресс 41 | */ 42 | static void getUserList_WebserviceQuery (QString& header, QString& data, const QString& last_row_version, IProgress* progress = NULL); 43 | 44 | /*! 45 | * \brief Парсит ответ со списком пользователей и заносит результат в список 46 | * \param data Строка с данными HTTP ответа 47 | * \param list Список пользователей 48 | * \param row_version Версия строк (снимка) 49 | * \param progress Прогресс 50 | * \return Пустая строка, или сообщение об ошибке 51 | */ 52 | static QString getUserList_WebserviceParse (const QString& data, AUserInfoList& list, QString& row_version, IProgress* progress = NULL); 53 | 54 | /*! 55 | * \brief Возвращает заголовок и данные для POST запроса получения списка сообщений 56 | * \param header Заголовок HTTP запроса 57 | * \param data Данные для POST запроса 58 | * \param row_version Версия строк (снимка) 59 | * \param query Описатель запроса данных (поломанные топики, сообщения на докачку, список форумов) 60 | * \param progress Прогресс 61 | */ 62 | static void getMessageList_WebserviceQuery (QString& header, QString& data, const ARowVersion& row_version, const ADataQuery& query, IProgress* progress = NULL); 63 | 64 | /*! 65 | * \brief Парсит ответ со списком сообщений и заносит результат в список 66 | * \param data Строка с данными HTTP ответа 67 | * \param list Список сообщений 68 | * \param row_version Версия строк (снимка) 69 | * \param progress Прогресс 70 | * \return Пустая строка, или сообщение об ошибке 71 | */ 72 | static QString getMessageList_WebserviceParse (const QString& data, ADataList& list, ARowVersion& row_version, IProgress* progress = NULL); 73 | 74 | /*! 75 | * \brief Возвращает заголовок и данные для POST запроса отправки данных 76 | * \param header Заголовок HTTP запроса 77 | * \param data Данные для POST запроса 78 | * \param list_messages Список сообщений к отправке 79 | * \param list_rating Список рейтингов к отправке 80 | * \param list_moderate Список модерилок к отправке 81 | * \param progress Прогресс 82 | */ 83 | static void postChange_WebserviceQuery (QString& header, QString& data, const AMessage2SendList& list_messages, const ARating2SendList& list_rating, const AModerate2SendList& list_moderate, IProgress* progress = NULL); 84 | 85 | /*! 86 | * \brief Парсит ответ отправки данных и возвращает куку для сессии 87 | * \param header Строка с данными HTTP заголовка ответа 88 | * \param cookie Кука 89 | * \param progress Прогресс 90 | * \return Пустая строка, или сообщение об ошибке 91 | */ 92 | static QString postChange_WebserviceParse (const QString& header, QString& cookie, IProgress* progress = NULL); 93 | 94 | /*! 95 | * \brief Возвращает заголовок и данные для POST запроса коммита данных 96 | * \param header Заголовок HTTP запроса 97 | * \param data Данные для POST запроса 98 | * \param cookie Кука 99 | * \param progress Прогресс 100 | */ 101 | static void postChangeCommit_WebserviceQuery (QString& header, QString& data, const QString& cookie, IProgress* progress = NULL); 102 | 103 | /*! 104 | * \brief Парсит ответ со списком закоммиченых данных и заносит результат в список 105 | * \param data Строка с данными HTTP ответа 106 | * \param commit_info Описатель результата коммита 107 | * \param progress Прогресс 108 | * \return Пустая строка, или сообщение об ошибке 109 | */ 110 | static QString postChangeCommit_WebserviceParse (const QString& data, ACommitInfo& commit_info, IProgress* progress = NULL); 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/storage/mysql_database.cpp: -------------------------------------------------------------------------------- 1 | #include "mysql_database.h" 2 | //---------------------------------------------------------------------------------------------- 3 | 4 | AMySQLDatabase::AMySQLDatabase () : ADatabase 5 | ( 6 | #ifdef Q_WS_WIN 7 | "QODBC" 8 | #else 9 | "QMYSQL" 10 | #endif 11 | ) 12 | { 13 | m_transaction = false; 14 | } 15 | //---------------------------------------------------------------------------------------------- 16 | 17 | AMySQLDatabase::~AMySQLDatabase () 18 | { 19 | } 20 | //---------------------------------------------------------------------------------------------- 21 | 22 | bool AMySQLDatabase::checkDatabase () 23 | { 24 | if (ADatabase::isOpen() == true) 25 | return true; 26 | 27 | return openDatabase(); 28 | } 29 | //---------------------------------------------------------------------------------------------- 30 | 31 | bool AMySQLDatabase::openDatabase () 32 | { 33 | if (ADatabase::isOpen() == true) 34 | return true; 35 | 36 | // получение настроек соединения из конфига 37 | QSettings settings; 38 | 39 | QString database_host = settings.value("mysql/host" ).toString(); 40 | QString database_port = settings.value("mysql/port" ).toString(); 41 | QString database_name = settings.value("mysql/name" ).toString(); 42 | QString database_login = settings.value("mysql/login" ).toString(); 43 | QString database_password = settings.value("mysql/password").toString(); 44 | 45 | bool is_port_number = false; 46 | int database_port_int = database_port.toInt(&is_port_number); 47 | 48 | #ifdef Q_WS_WIN 49 | // http://dev.mysql.com/doc/refman/5.1/en/connector-odbc-configuration-connection-parameters.html 50 | QString connection_string; 51 | 52 | connection_string += "driver={MySQL ODBC 5.1 Driver};"; 53 | connection_string += "charset=UTF8;"; 54 | connection_string += "option=" + QString(8 /* FLAG_BIG_PACKETS */ + 2048 /* FLAG_COMPRESSED_PROTO */) + ";"; 55 | connection_string += "server=" + database_host + ";"; 56 | connection_string += "database=" + database_name + ";"; 57 | connection_string += "user=" + database_login + ";"; 58 | connection_string += "password=" + database_password + ";"; 59 | 60 | if (is_port_number == true) 61 | { 62 | if (database_port_int != 0) 63 | connection_string += "port=" + QString(database_port_int) + ";"; 64 | } 65 | else if (database_port.length() != 0) 66 | connection_string += "socket=" + database_port + ";"; 67 | 68 | ADatabase::setDatabaseName(connection_string); 69 | #else 70 | ADatabase::setHostName(database_host); 71 | ADatabase::setDatabaseName(database_name); 72 | ADatabase::setUserName(database_login); 73 | ADatabase::setPassword(database_password); 74 | 75 | if (is_port_number == true) 76 | { 77 | if (database_port_int != 0) 78 | ADatabase::setPort(database_port_int); 79 | 80 | ADatabase::setConnectOptions("CLIENT_COMPRESS=1"); 81 | } 82 | else if (database_port.length() != 0) 83 | ADatabase::setConnectOptions("UNIX_SOCKET=" + database_port); 84 | #endif 85 | 86 | m_transaction = false; 87 | 88 | if (ADatabase::open() == false) 89 | { 90 | setLastError(); 91 | return false; 92 | } 93 | 94 | return true; 95 | } 96 | //---------------------------------------------------------------------------------------------- 97 | 98 | bool AMySQLDatabase::transaction () 99 | { 100 | if (m_transaction == true || checkDatabase() == false) 101 | return false; 102 | 103 | if (ADatabase::transaction() == false) 104 | { 105 | setLastError(); 106 | return false; 107 | } 108 | 109 | m_transaction = true; 110 | 111 | return true; 112 | } 113 | //---------------------------------------------------------------------------------------------- 114 | 115 | bool AMySQLDatabase::commit () 116 | { 117 | if (ADatabase::isOpen() == false || m_transaction == false) 118 | return false; 119 | 120 | if (ADatabase::commit() == false) 121 | { 122 | setLastError(); 123 | 124 | ADatabase::rollback(); 125 | 126 | m_transaction = false; 127 | 128 | return false; 129 | } 130 | 131 | m_transaction = false; 132 | 133 | return true; 134 | } 135 | //---------------------------------------------------------------------------------------------- 136 | 137 | bool AMySQLDatabase::rollback () 138 | { 139 | if (ADatabase::isOpen() == false || m_transaction == false) 140 | return false; 141 | 142 | if (ADatabase::rollback() == false) 143 | setLastError(); 144 | 145 | m_transaction = false; 146 | 147 | return true; 148 | } 149 | //---------------------------------------------------------------------------------------------- 150 | 151 | AQuery* AMySQLDatabase::createQuery (const QString& sql, bool prepared) 152 | { 153 | if (checkDatabase() == false) 154 | return NULL; 155 | 156 | AQuery* query = 0; 157 | 158 | if (prepared == true) 159 | query = ADatabase::createPreparedQuery(sql); 160 | else 161 | query = ADatabase::createQuery(sql); 162 | 163 | if (query == NULL) 164 | { 165 | if (m_transaction == true) 166 | rollback(); 167 | } 168 | 169 | return query; 170 | } 171 | //---------------------------------------------------------------------------------------------- 172 | -------------------------------------------------------------------------------- /src/widgets/forum_tree.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief Виджет дерева форумов 4 | */ 5 | 6 | #ifndef _avalon_forum_tree_h_ 7 | #define _avalon_forum_tree_h_ 8 | 9 | #include "interfaces.h" 10 | 11 | /*! 12 | * \brief Виджет дерева форумов 13 | */ 14 | class AForumTree : 15 | public QTreeWidget, 16 | public IForumTree 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | 22 | /*! 23 | * \param parent Родительский виджет 24 | * \param form Главная форма приложения для отображения сообщений об ошибках 25 | */ 26 | AForumTree (QWidget* parent, QWidget* form); 27 | ~AForumTree (); 28 | 29 | /*! 30 | * \brief Сохранение размеров дерева форумов 31 | */ 32 | void save (); 33 | 34 | /*! 35 | * \brief Восстановление размеров дерева форумов 36 | */ 37 | void restore (); 38 | 39 | /*! 40 | * \brief Установка интерфейса дерева сообщений 41 | */ 42 | void setMessageTree (IMessageTree* itf); 43 | 44 | /*! 45 | * \brief Установка интерфейса виджета отображения сообщения 46 | */ 47 | void setMessageView (IMessageView* itf); 48 | 49 | /*! 50 | * \brief Установка интерфейса главной формы 51 | */ 52 | void setMainForm (IFormMain* itf); 53 | 54 | private: 55 | 56 | /*! 57 | * \brief Главная форма приложения для отображения ошибок 58 | */ 59 | QWidget* m_parent; 60 | 61 | IMessageTree* m_message_tree; /*!< \brief Интерфейс виджета дерева сообщений */ 62 | IMessageView* m_message_view; /*!< \brief Интерфейс виджета для отображения сообщения */ 63 | IFormMain* m_main_form; /*!< \brief Интерфейс главной формы приложения */ 64 | 65 | int m_total_unread_count; /*!< \brief Общее количество непрочитанных сообщений в форумах исключая спецфорумы */ 66 | 67 | int m_specail_group_count; /*!< \brief Общее количество спец-групп форумов */ 68 | 69 | // 70 | // иконки для отображения статусов сообщений 71 | // 72 | 73 | QIcon m_forum_read; /*!< \brief Все сообщения прочитанны */ 74 | QIcon m_forum_unread_topics; /*!< \brief Есть непрочитанные (новые) топики */ 75 | QIcon m_forum_unread_child; /*!< \brief Есть непрочитанные сообщения и нет новых топиков или сообщений для меня */ 76 | QIcon m_forum_unread_child_my; /*!< \brief Есть непрочитанные сообщения для меня и нет новых (непрочитанных) топиков */ 77 | QIcon m_forum_unread_topics_my; /*!< \brief Есть непрочитанные (новые) топики И непрочитанные сообщения для меня */ 78 | 79 | /*! 80 | * \brief Функция проверки установки выделения, в случае отсутствия выделения, выделяет самый верхний элемент 81 | * \return true, если есть выделение, false при отсутствии элементов 82 | */ 83 | bool checkCurrentItem (); 84 | 85 | /*! 86 | * \brief Функция проверки элемента дерева на то, что этот элемент принадлежит группе форумов 87 | * \param item элемент дерева 88 | * \return true, если элемент является элементом группы, иначе false 89 | */ 90 | bool isGroup (QTreeWidgetItem* item); 91 | 92 | /*! 93 | * \brief Функция для получения текущего выделенного элемента форума в дереве 94 | * \return текущий выделенный элемент форума или NULL, если элемент является группой 95 | */ 96 | QTreeWidgetItem* getSelectedForumItem (); 97 | 98 | /*! 99 | * \brief Возвращает количество непрочитанных сообщений в текущем форуме 100 | * \return количество сообщений в текущем форуме или -1, если форум не выбран 101 | */ 102 | int getSelectedUnreadCount (); 103 | 104 | /*! 105 | * \brief Всплывающее контекстное меню 106 | */ 107 | QMenu* m_menu; 108 | 109 | QAction* m_menu_mark_all_as_read; /*!< \brief Пометить все как прочитанные */ 110 | QAction* m_menu_mark_patrial_as_read; /*!< \brief Пометить до даты как прочитанные */ 111 | QAction* m_menu_mark_all_as_unread; /*!< \brief Пометить все как непрочитанные */ 112 | QAction* m_menu_mark_patrial_as_unread; /*!< \brief Пометить после даты как непрочитанное */ 113 | QAction* m_menu_unsubscribe; /*!< \brief Отписаться от форума/группы */ 114 | 115 | // IForumTree 116 | public: 117 | 118 | void reload (); 119 | 120 | void reloadUnread (bool reload_message_tree); 121 | 122 | void changeUnreadCount (int count, int count_my, int count_topics, int id_forum); 123 | 124 | void newMessage (); 125 | 126 | void gotoNextUnreadForum (); 127 | 128 | bool selectForum (int id); 129 | 130 | private slots: 131 | 132 | /*! 133 | * \brief Смена выделения в дереве 134 | */ 135 | void current_item_changed (QTreeWidgetItem* current, QTreeWidgetItem* previous); 136 | 137 | /*! 138 | * \brief Запрос контекстного меню 139 | * \param pos Точка 140 | */ 141 | void context_menu_request (const QPoint& pos); 142 | 143 | void menu_mark_all_as_read_triggered (); /*!< \brief Пометить все как прочитанные */ 144 | void menu_mark_patrial_as_read_triggered (); /*!< \brief Пометить до даты как прочитанные */ 145 | void menu_mark_all_as_unread_triggered (); /*!< \brief Пометить все как непрочитанные */ 146 | void menu_mark_patrial_as_unread_triggered (); /*!< \brief Пометить после даты как непрочитанное */ 147 | void menu_unsubscribe_triggered (); /*!< \brief Отписаться от форума/группы */ 148 | }; 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /src/resource.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | icons/main.png 5 | 6 | icons/childunread.png 7 | icons/messageread.png 8 | icons/messageunread.png 9 | icons/myunread.png 10 | 11 | icons/exit16.png 12 | icons/markallasread16.png 13 | icons/markpatrialasread16.png 14 | icons/markallasunread16.png 15 | icons/markpatrialasunread16.png 16 | icons/help16.png 17 | icons/update16.png 18 | icons/unsubscribe16.png 19 | icons/copy16.png 20 | icons/open16.png 21 | icons/storage16.png 22 | icons/nextunreadarticle16.png 23 | icons/nextunreadthread16.png 24 | icons/nextunreadforum16.png 25 | 26 | icons/forward24.png 27 | icons/backward24.png 28 | icons/draft24.png 29 | icons/nextunreadarticle24.png 30 | icons/nextunreadthread24.png 31 | icons/nextunreadforum24.png 32 | 33 | icons/rate_plus_1.png 34 | icons/rate_1.png 35 | icons/rate_2.png 36 | icons/rate_3.png 37 | icons/rate_cross.png 38 | icons/rate_smile.png 39 | icons/rate_plus.png 40 | icons/rate_minus.png 41 | 42 | icons/moderate.png 43 | 44 | icons/delete16.png 45 | icons/delete24.png 46 | 47 | icons/smile24.png 48 | icons/bold24.png 49 | icons/italic24.png 50 | icons/quote24.png 51 | icons/hyperlink24.png 52 | icons/image24.png 53 | icons/code24.png 54 | icons/char24.png 55 | 56 | smiles/beer.png 57 | smiles/biggrin.png 58 | smiles/wacko.png 59 | smiles/angry.png 60 | smiles/thumb_down.png 61 | smiles/sad.png 62 | smiles/sideways.png 63 | smiles/devil.png 64 | smiles/stop.png 65 | smiles/blush.png 66 | smiles/smile.png 67 | smiles/pouty.png 68 | smiles/thumbs_up.png 69 | smiles/cool.png 70 | smiles/music.png 71 | smiles/wink.png 72 | smiles/shock.png 73 | smiles/wrong.png 74 | 75 | icons/show_topic.png 76 | 77 | icons/yandex.ico 78 | icons/slovari.yandex.ico 79 | icons/google.ico 80 | icons/rsdn16.png 81 | icons/wikipedia.ico 82 | icons/google.translate.ico 83 | 84 | icons/new16.png 85 | icons/new22.png 86 | icons/new24.png 87 | icons/new32.png 88 | icons/new48.png 89 | icons/new64.png 90 | icons/new128.png 91 | 92 | icons/settings16.png 93 | icons/settings22.png 94 | icons/settings24.png 95 | icons/settings32.png 96 | icons/settings48.png 97 | icons/settings64.png 98 | icons/settings128.png 99 | 100 | icons/date16.png 101 | icons/date22.png 102 | icons/date24.png 103 | icons/date32.png 104 | icons/date48.png 105 | icons/date64.png 106 | icons/date128.png 107 | 108 | icons/synchronize16.png 109 | icons/synchronize24.png 110 | icons/synchronize32.png 111 | icons/synchronize48.png 112 | icons/synchronize256.png 113 | 114 | icons/viewsource16.png 115 | icons/viewsource22.png 116 | icons/viewsource32.png 117 | icons/viewsource48.png 118 | icons/viewsource64.png 119 | 120 | icons/reply16.png 121 | icons/reply22.png 122 | icons/reply32.png 123 | icons/reply48.png 124 | 125 | icons/edit16.png 126 | icons/edit22.png 127 | icons/edit32.png 128 | icons/edit48.png 129 | icons/edit64.png 130 | 131 | icons/subscribe16.png 132 | icons/subscribe22.png 133 | icons/subscribe32.png 134 | icons/subscribe48.png 135 | icons/subscribe64.png 136 | 137 | icons/compress16.png 138 | icons/compress24.png 139 | icons/compress32.png 140 | icons/compress48.png 141 | icons/compress256.png 142 | 143 | icons/download16.png 144 | icons/download22.png 145 | icons/download24.png 146 | icons/download32.png 147 | icons/download48.png 148 | icons/download64.png 149 | icons/download128.png 150 | 151 | translations/qt_ru_RU.qm 152 | 153 | sql/avalon.mysql.sql 154 | sql/avalon.sqlite.sql 155 | 156 | style.css 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /src/forms/form_main_ui.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file 3 | * \brief GUI главной формы приложения 4 | */ 5 | 6 | #ifndef _avalon_form_main_ui_h_ 7 | #define _avalon_form_main_ui_h_ 8 | 9 | #include "widgets/forum_tree.h" 10 | #include "widgets/message_tree.h" 11 | #include "widgets/message_view.h" 12 | 13 | /*! 14 | * \brief Класс для генерации GUI части главной формы 15 | */ 16 | class AFormMainUI : public QMainWindow 17 | { 18 | Q_OBJECT; 19 | 20 | public: 21 | 22 | AFormMainUI (); 23 | 24 | virtual ~AFormMainUI (); 25 | 26 | /*! 27 | * \brief Функция сохранения расположения и размеров элементов управления формы <при выходе> 28 | */ 29 | void save (); 30 | 31 | /*! 32 | * \brief Функция восстановления расположения и размеров элементов управления формы <при старте> 33 | */ 34 | void restore (); 35 | 36 | protected: 37 | 38 | /*! 39 | * \brief Функция отработки закрытия формы (производится вызов AFormMainUI::Save() для сохранения расположения и размеров элементов формы) 40 | */ 41 | void virtual closeEvent (QCloseEvent* event); 42 | 43 | protected: 44 | 45 | /*! 46 | * \brief Главное меню 47 | */ 48 | QMenuBar* m_menubar; 49 | QMenu* m_menu_file; /*!< \brief Файл / ... */ 50 | QAction* m_menu_file_exit; /*!< \brief Файл / Выход */ 51 | QMenu* m_menu_view; /*!< \brief Вид / ... */ 52 | QAction* m_menu_view_source; /*!< \brief Вид / Просмотр исходника */ 53 | QMenu* m_menu_goto; /*!< \brief Перейти / ... */ 54 | QAction* m_menu_goto_next_unread_article; /*!< \brief Перейти / Следующая непрочитаная статья */ 55 | QAction* m_menu_goto_next_unread_thread; /*!< \brief Перейти / Следующая непрочитаная ветка */ 56 | QAction* m_menu_goto_next_unread_forum; /*!< \brief Перейти / Следующий непрочитаный форум */ 57 | QAction* m_menu_goto_by_id; /*!< \brief Перейти / К сообщению по ID/ссылке */ 58 | QMenu* m_menu_service; /*!< \brief Сервис / ... */ 59 | QAction* m_menu_service_synchronize; /*!< \brief Сервис / Синхронизировать */ 60 | QAction* m_menu_service_download; /*!< \brief Сервис / Загрузить сообщение/ветку */ 61 | QAction* m_menu_service_new_message; /*!< \brief Сервис / Новое сообщение */ 62 | QAction* m_menu_service_reply; /*!< \brief Сервис / Ответить на сообщение */ 63 | QAction* m_menu_service_mark_all_as_read; /*!< \brief Сервис / Пометить все как непрочитанные */ 64 | QAction* m_menu_service_mark_patrial_as_read; /*!< \brief Сервис / Пометить до даты как прочитанные */ 65 | QAction* m_menu_service_mark_all_as_unread; /*!< \brief Сервис / Пометить все как непрочитанные */ 66 | QAction* m_menu_service_mark_patrial_as_unread; /*!< \brief Сервис / Пометить после даты как непрочитанные */ 67 | QAction* m_menu_service_subscribe; /*!< \brief Сервис / Подписка на форумы */ 68 | QAction* m_menu_service_settings; /*!< \brief Сервис / Настройка */ 69 | #ifdef AVALON_USE_ZLIB 70 | QMenu* m_menu_service_storage; /*!< \brief Сервис / Хранилище */ 71 | QAction* m_menu_service_storage_compress; /*!< \brief Сервис / Хранилище / Упаковать */ 72 | QAction* m_menu_service_storage_uncompress; /*!< \brief Сервис / Хранилище / Распаковать */ 73 | #endif 74 | QMenu* m_menu_q; /*!< \brief ? / ... */ 75 | QAction* m_menu_q_yandex_url; /*!< \brief ? / URL Яндекс */ 76 | QAction* m_menu_q_wikipedia_url; /*!< \brief ? / URL Википедия */ 77 | QAction* m_menu_q_google_url; /*!< \brief ? / URL Google */ 78 | QAction* m_menu_q_rsdn_url; /*!< \brief ? / URL RSDN */ 79 | QAction* m_menu_q_about; /*!< \brief ? / О программе */ 80 | 81 | /*! 82 | * \brief Тулбар 83 | */ 84 | QToolBar* m_tool_bar; 85 | QAction* m_tool_bar_synchronize; /*!< \brief Синхронизировать */ 86 | QAction* m_tool_bar_new_message; /*!< \brief Новое сообщение */ 87 | QAction* m_tool_bar_backward; /*!< \brief Назад */ 88 | QAction* m_tool_bar_forward; /*!< \brief Вперед */ 89 | 90 | /*! 91 | * \brief Центральный вертикальный сплиттер, разделяющий дерево форумов и дерево сообщений с областью просмотра) 92 | */ 93 | QSplitter* m_splitter; 94 | AForumTree* m_forum_tree; /*!< \brief Дерево подписаных форумов */ 95 | QSplitter* m_splitter_right; /*!< \brief Правый горизонтальный сплиттер, разделяющий дерево сообщений и отображение сообщения */ 96 | AMessageTree* m_message_tree; /*!< \brief Дерево статей/сообщений */ 97 | AMessageView* m_message_view; /*!< \brief Виджет для отображения сообщения */ 98 | 99 | /*! 100 | * \brief Статусбар 101 | */ 102 | QStatusBar* m_status_bar; 103 | }; 104 | 105 | #endif // _avalon_form_main_ui_h_ 106 | --------------------------------------------------------------------------------