aboutsummaryrefslogtreecommitdiffstats
Commit message (Expand)AuthorAgeFilesLines
* Check error messages for assembly tests.chriseth2017-02-171-29/+67
* Merge pull request #1627 from ethereum/asmfunctionschriseth2017-02-177-41/+162
|\
| * More tests.chriseth2017-02-161-0/+10
| * Assembly printer for functions.chriseth2017-02-162-0/+20
| * Parsing function calls.chriseth2017-02-165-37/+69
| * Parsing function definitions.chriseth2017-02-165-6/+65
* | Merge pull request #1701 from ethereum/fixFatalErrorschriseth2017-02-177-73/+103
|\ \ | |/ |/|
| * Fix early exist for fatal errors.chriseth2017-02-166-73/+87
| * Test case.chriseth2017-02-161-0/+16
|/
* Merge pull request #1674 from ethereum/assemblyPrinterchriseth2017-02-155-4/+271
|\
| * Test for unicode string literals.chriseth2017-02-151-0/+11
| * Assembly printing fixes.chriseth2017-02-141-5/+5
| * Tests for printing assembly.chriseth2017-02-141-0/+56
| * Review changes.chriseth2017-02-143-4/+4
| * Assembly printer.chriseth2017-02-144-4/+204
|/
* Merge pull request #1620 from ethereum/refactorEntryYoichi Hirai2017-02-1413-87/+150
|\
| * Review comments.chriseth2017-02-143-7/+7
| * Allow different entry scope for registerDeclarations.chriseth2017-02-143-9/+22
| * Extract scopes into compiler stack.chriseth2017-02-147-6/+19
| * Refactor type system to allow multiple entry points.chriseth2017-02-144-15/+27
| * Refactor NameAndTypeResolver and SyntaxChecker to allow other entry points.chriseth2017-02-147-62/+87
* | Merge pull request #1697 from ethereum/test-script-killAlex Beregszaszi2017-02-141-3/+4
|\ \
| * | Kill the right eth process in testschriseth2017-02-141-3/+4
|/ /
* | Merge pull request #1692 from ethereum/rpc-mining-timechriseth2017-02-142-11/+12
|\ \ | |/ |/|
| * Use std::chrono and not boost::posix_TimeAlex Beregszaszi2017-02-132-3/+4
| * Simplify mining loopAlex Beregszaszi2017-02-131-8/+3
| * Use maxMiningTime in mining as opposed to poll counterAlex Beregszaszi2017-02-132-4/+9
* | Merge pull request #1684 from federicobond/sphinx-highlightAlex Beregszaszi2017-02-141-15/+9
|\ \
| * | Update the sphinx highlighting rulesFederico Bond2017-02-141-15/+9
* | | Merge pull request #1693 from ethereum/nodejsversionchriseth2017-02-141-0/+2
|\ \ \ | |_|/ |/| |
| * | Use version 6chriseth2017-02-141-2/+2
| * | Install nvmchriseth2017-02-141-0/+2
* | | Merge pull request #1661 from ethereum/asm-revertchriseth2017-02-1317-8/+67
|\ \ \
| * | | Use the revert opcode in assert()Alex Beregszaszi2017-02-111-2/+8
| * | | Check for state changes in revert() testsAlex Beregszaszi2017-02-111-0/+5
| * | | Add tests for revert()Alex Beregszaszi2017-02-112-0/+24
| * | | Document revert()Alex Beregszaszi2017-02-113-3/+6
| * | | Use the REVERT opcode for throw;Alex Beregszaszi2017-02-111-1/+3
| * | | Document revert()Alex Beregszaszi2017-02-112-0/+4
| * | | Support revert()Alex Beregszaszi2017-02-114-1/+10
| * | | Add REVERT to libevmasmAlex Beregszaszi2017-02-115-1/+7
* | | | Merge pull request #1676 from ethereum/test-modifytimestampchriseth2017-02-135-5/+30
|\ \ \ \
| * | | | Compare start/end timestampAlex Beregszaszi2017-02-101-1/+4
| * | | | Create getBlockByNumber RPC methodAlex Beregszaszi2017-02-103-4/+11
| * | | | Do not use modifyTimestamp where not neededAlex Beregszaszi2017-02-101-2/+5
| * | | | Add blockNumber and blockTimestamp to ExecutionFrameworkAlex Beregszaszi2017-02-104-0/+12
* | | | | Merge pull request #1691 from ethereum/nodejsversionAlex Beregszaszi2017-02-131-1/+2
|\ \ \ \ \ | | |_|/ / | |/| | |
| * | | | Try different nodejs version request formatting.chriseth2017-02-131-1/+2
|/ / / /
* | | | Merge pull request #1690 from ethereum/failifdockerdeployfalidechriseth2017-02-131-11/+16
|\ \ \ \
| * | | | Fix test expressions.chriseth2017-02-131-11/+16
* | | | | Merge pull request #1689 from ethereum/failifdockerdeployfalidechriseth2017-02-131-0/+2
|\| | | |
| * | | | Fail if docker deploy failed.chriseth2017-02-131-0/+2
|/ / / /
* | | | Merge pull request #1680 from VoR0220/fixEmscriptenDeploymentAlex Beregszaszi2017-02-121-1/+1
|\ \ \ \
| * | | | should fix all the emscripten errors and problems associated with itRJ Catalano2017-02-121-1/+1
|/ / / /
* | | | Merge pull request #1683 from ethereum/travis-fixAlex Beregszaszi2017-02-121-17/+21
|\ \ \ \ | |_|/ / |/| | |
| * | | Fix macOS buildsAlex Beregszaszi2017-02-121-5/+9
| * | | Run every travis script in a subshellAlex Beregszaszi2017-02-121-12/+12
|/ / /
* | | Merge pull request #1678 from ethereum/assertAlex Beregszaszi2017-02-118-6/+44
|\ \ \
| * | | Use different wording for assertAlex Beregszaszi2017-02-112-2/+2
| * | | Cleanup is not needed for assert()Alex Beregszaszi2017-02-101-1/+1
| * | | Document user provided exceptionsAlex Beregszaszi2017-02-101-0/+4
| * | | Document assert()Alex Beregszaszi2017-02-102-2/+5
| * | | Add tests for assert()Alex Beregszaszi2017-02-101-0/+19
| * | | Implement assert as a global functionAlex Beregszaszi2017-02-104-4/+16
| |/ /
* | | Merge pull request #1616 from VoR0220/DockerReleaseAutomationchriseth2017-02-105-66/+99
|\ \ \ | |/ / |/| |
| * | Check for tag in travis.chriseth2017-02-101-2/+2
| * | clarify branches for docker to push on and clarify where to find cmakelists.txtRJ Catalano2017-02-101-4/+3
| * | minor fixupsRJ Catalano2017-02-102-2/+2
| * | edit the documentation for the travis fileRJ Catalano2017-02-101-3/+4
| * | defeat race conditionRJ Catalano2017-02-101-2/+2
| * | create automated docker deploymentRJ Catalano2017-02-102-9/+30
| * | reorganize deps installationRJ Catalano2017-02-101-20/+17
| * | reorganize travisRJ Catalano2017-02-101-35/+50
| |/
* | Merge pull request #1669 from ethereum/ipc-cleanupchriseth2017-02-102-32/+23
|\ \ | |/ |/|
| * Ensure that the whole message was written on Windows IPCAlex Beregszaszi2017-02-091-1/+1
| * Simplify the Windows IPC codeAlex Beregszaszi2017-02-091-6/+3
| * Detect closed sockets in IPCAlex Beregszaszi2017-02-091-1/+2
| * Do not expect a new line, rather buffer up the response in IPCAlex Beregszaszi2017-02-092-20/+17
| * Use only send/recv in IPCAlex Beregszaszi2017-02-092-8/+4
* | Merge pull request #1675 from ethereum/debug-macAlex Beregszaszi2017-02-091-1/+1
|\ \
| * | Do not use -Og for debug mode (won't work on Mac/clang)Alex Beregszaszi2017-02-091-1/+1
| |/
* | Merge pull request #1672 from ethereum/nodeversionchriseth2017-02-091-1/+1
|\ \ | |/ |/|
| * Use nodejs 6chriseth2017-02-091-1/+1
* | Merge pull request #1668 from ethereum/build-test-progresschriseth2017-02-093-6/+3
|\ \ | |/ |/|
| * Do not log dots in soltest on windowsAlex Beregszaszi2017-02-091-3/+0
| * Include --show-progress in soltestAlex Beregszaszi2017-02-092-3/+3
* | Merge pull request #1387 from ethereum/json-interfacechriseth2017-02-093-39/+267
|\ \ | |/ |/|
| * Simplify the compiler API section (and remove pseudo code)Alex Beregszaszi2017-02-091-18/+6
| * Drop the legacy assembly outputAlex Beregszaszi2017-02-091-9/+6
| * Include language field in the JSONAlex Beregszaszi2017-02-091-0/+3
| * Move file into sourceLocationAlex Beregszaszi2017-02-091-4/+4
| * Update the AST outputAlex Beregszaszi2017-02-091-9/+11
| * Explain every contract output fieldAlex Beregszaszi2017-02-091-11/+38
| * Support linkReferences with length specifiedAlex Beregszaszi2017-02-091-1/+4
| * Error list should have sourceLocationAlex Beregszaszi2017-02-091-6/+4
| * Updated EVM outputAlex Beregszaszi2017-02-091-13/+16
| * Make contracts output two-levelAlex Beregszaszi2017-02-091-14/+16
| * Update errors outputAlex Beregszaszi2017-02-091-17/+20
| * Include pseudo-code of compiler APIAlex Beregszaszi2017-02-091-1/+13
| * Change layout and include API, Input, Output sectionsAlex Beregszaszi2017-02-091-11/+17
| * Add metadata.useLiteralContent optionAlex Beregszaszi2017-02-091-0/+5
| * Update output selectionAlex Beregszaszi2017-02-091-27/+40
| * Update settings sectionAlex Beregszaszi2017-02-091-8/+21
| * Update sources definition based on the metadataAlex Beregszaszi2017-02-091-9/+25
| * Place into a code blockAlex Beregszaszi2017-02-091-0/+3
| * The metadata section has been moved, make only a reference to itAlex Beregszaszi2017-02-091-95/+5
| * Update the metadata JSON specAlex Beregszaszi2017-02-091-10/+23
| * Update the metadata JSON specAlex Beregszaszi2017-02-091-1/+7
| * Add missing fieldsAlex Beregszaszi2017-02-091-1/+4
| * Describe the ABI output fieldAlex Beregszaszi2017-02-091-1/+3
| * Update with https://pad.riseup.net/p/7x3G896a3NLAAlex Beregszaszi2017-02-091-27/+121
| * Add language and some minor corrections and clarifications.chriseth2017-02-091-6/+9
| * Document input description and metadata output.chriseth2017-02-093-39/+142
|/
* Merge pull request #1662 from ethereum/ipc-safety-checksAlex Beregszaszi2017-02-091-7/+11
|\
| * Use BOOST_REQUIRE() and stop at the first failureAlex Beregszaszi2017-02-091-5/+5
| * Check the return value of RPC callsAlex Beregszaszi2017-02-091-6/+8
| * Avoid crash if fdopen failed in IPCAlex Beregszaszi2017-02-091-0/+2
| * Ensure that a valid RPC response is received through IPCAlex Beregszaszi2017-02-091-1/+1
|/
* Merge pull request #1667 from ethereum/build-fixAlex Beregszaszi2017-02-091-6/+12
|\
| * Ensure that all commands succed and move back to rootAlex Beregszaszi2017-02-091-6/+12
* | Merge pull request #1643 from federicobond/patch-1chriseth2017-02-091-1/+2
|\ \ | |/ |/|
| * grammar.txt: Add rule for tuple destructuringFederico Bond2017-02-091-1/+2
* | Merge pull request #1663 from ethereum/mac-tests-fixchriseth2017-02-081-1/+1
|\ \
| * | Fix tests on mac (wc produces whitespace)Alex Beregszaszi2017-02-081-1/+1
|/ /
* | Merge pull request #1653 from ethereum/node-versionchriseth2017-02-071-2/+2
|\ \
| * | Require node.js >= 5Alex Beregszaszi2017-02-071-2/+2
* | | Merge pull request #1655 from ethereum/docs-versionchriseth2017-02-071-2/+8
|\ \ \
| * \ \ Merge branch 'develop' into docs-versionchriseth2017-02-079-11/+17
| |\ \ \ | |/ / / |/| | |
* | | | Merge pull request #1660 from ethereum/asm-selfdestructchriseth2017-02-078-11/+11
|\ \ \ \
| * \ \ \ Merge branch 'develop' into asm-selfdestructchriseth2017-02-071-0/+6
| |\ \ \ \ | |/ / / / |/| | | |
* | | | | Merge pull request #1659 from ethereum/docs-archlinuxchriseth2017-02-071-0/+6
|\ \ \ \ \ | |_|_|/ / |/| | | |
| * | | | Add archlinux installation instructionsAlex Beregszaszi2017-02-071-0/+6
|/ / / /
| * / / Rename SUICIDE opcode to SELFDESTRUCT in libevmasmAlex Beregszaszi2017-02-078-11/+11
|/ / /
| * / Take documentation version numbers from CMakeAlex Beregszaszi2017-02-071-2/+8
|/ /
* | Merge pull request #1649 from ethereum/solc-fix-mkdirAlex Beregszaszi2017-02-062-1/+4
|\ \
| * | Do not create directories . and ..Alex Beregszaszi2017-02-062-1/+4
|/ /
* | Merge pull request #1648 from ethereum/escape-filenameschriseth2017-02-062-7/+8
|\ \
| * | Always escape filenames in solcAlex Beregszaszi2017-02-062-7/+8
| |/
* | Merge pull request #1640 from ethereum/docs-esotericchriseth2017-02-061-25/+0
|\ \ | |/ |/|
| * Remove obsolete esoteric features sectionAlex Beregszaszi2017-02-031-25/+0
|/
* Merge pull request #1642 from VoR0220/implementBuildScriptRJ Catalano2017-02-033-7/+27
|\
| * clarified binaries installationRJ Catalano2017-02-031-1/+2
| * implement a build scriptRJ Catalano2017-02-033-7/+26
|/
* Merge pull request #1641 from ethereum/docs-gettersRJ Catalano2017-02-037-24/+24
|\
| * Rename accessor to getterAlex Beregszaszi2017-02-037-24/+24
|/
* Merge pull request #1638 from willnwhite/patch-1Alex Beregszaszi2017-02-031-2/+1
|\
| * Other contracts can't write to a public variableWill White2017-02-021-2/+1
|/
* Merge pull request #1634 from ethereum/swap-assertAlex Beregszaszi2017-02-022-4/+5
|\
| * Warn early when exhausting stackAlex Beregszaszi2017-02-021-0/+1
| * More verbose log of when using invalid instructionsAlex Beregszaszi2017-02-021-4/+4
|/
* Merge pull request #1636 from ethereum/docs-solcjsAlex Beregszaszi2017-02-021-7/+8
|\
| * Explain the difference between solc and solcjsAlex Beregszaszi2017-02-021-7/+8
|/
* Merge pull request #1633 from ethereum/negative-length-arrayschriseth2017-02-024-0/+18
|\
| * Merge branch 'develop' into negative-length-arrayschriseth2017-02-0229-116/+116
| |\ | |/ |/|
* | Merge pull request #1635 from ethereum/licensing-headerchriseth2017-02-0229-116/+116
|\ \
| * | Replace cpp-ethereum with solidity in the license headersAlex Beregszaszi2017-02-0229-116/+116
|/ /
| * Disallow arrays with negative lengthAlex Beregszaszi2017-02-023-0/+15
| * Add isNegative to RationalNumberTypeAlex Beregszaszi2017-02-021-0/+3
|/
* Merge pull request #1630 from ethereum/function-to-addressAlex Beregszaszi2017-02-027-1/+85
|\
| * Cover both failure casesAlex Beregszaszi2017-02-021-1/+13
| * Add changelog for function types to address conversionAlex Beregszaszi2017-02-011-0/+3
| * Only capture function type to address conversionAlex Beregszaszi2017-02-011-8/+10
| * Add more tests for function type conversionAlex Beregszaszi2017-02-012-1/+32
| * Add tests for explicity fuction type to address castingAlex Beregszaszi2017-02-011-0/+12
| * Be more verbose on the stack-mismatch errorsAlex Beregszaszi2017-02-011-1/+1
| * Support explicit conversion of external function type to addressAlex Beregszaszi2017-02-014-1/+25
|/
* Merge pull request #1625 from ethereum/asmdocschriseth2017-02-012-490/+30
|\
| * Integrate missed changes.chriseth2017-02-012-490/+30
* | Merge pull request #1623 from ethereum/versionRJ Catalano2017-02-013-3/+5
|\ \ | |/ |/|
| * Version updatechriseth2017-02-013-3/+5
|/
* Release date for 0.4.9chriseth2017-02-011-1/+1
* Merge pull request #1594 from ethereum/fixStdTokenchriseth2017-01-312-14/+34
|\
| * Fix transferFrom.chriseth2017-01-231-7/+11
| * Make token constructible.chriseth2017-01-211-12/+24
| * Test that all constructible std contracts produce bytecode.chriseth2017-01-211-0/+4
* | Merge pull request #1621 from VoR0220/fixLinkerForBinariesWithImportschriseth2017-01-311-1/+3
|\ \
| * | fix for linker wrt binaries generated with import statementsVoR02202017-01-311-1/+3
|/ /
* | Merge pull request #1619 from ethereum/changelogchriseth2017-01-304-1/+24
|\ \
| * | Add tests for invalid instructionAlex Beregszaszi2017-01-282-0/+20
| * | Mention invalid in docsAlex Beregszaszi2017-01-271-0/+2
| * | Mention in changelog that invalid as an opcode is valid inline assemblyAlex Beregszaszi2017-01-271-1/+2
|/ /
* | Merge pull request #1598 from wuestholz/developchriseth2017-01-2714-23/+85
|\ \
| * | Changelog and review suggestions.chriseth2017-01-273-2/+4
| * | Document special case of zero-initialized internal function.chriseth2017-01-261-1/+6
| * | Uninitialized internal function should call INVALID.chriseth2017-01-261-1/+3
| * | Allow inserting low-level functions without calling them.chriseth2017-01-262-5/+26
| * | Split line.chriseth2017-01-261-1/+3
| * | Fix optimizer with regards to INVALID instruction.chriseth2017-01-262-0/+2
| * | Change code for INVALID opcode to 0xfe.chriseth2017-01-262-4/+4
| * | Address feedback from code review.Valentin Wüstholz2017-01-265-16/+12
| * | Change translation of implicit throws (issue #1589).Valentin Wüstholz2017-01-2610-17/+49
* | | Merge pull request #1451 from LefterisJP/fix_buildchriseth2017-01-273-19/+123
|\ \ \
| * | | Changelog entry.chriseth2017-01-271-0/+1
| * | | Tests for natspect parsing failure casesLefteris Karapetsas2017-01-271-0/+42
| * | | Minor changes.chriseth2017-01-271-19/+17
| * | | More verbose function namingLefteris Karapetsas2017-01-271-2/+2
| * | | Doc tags followed by newline are now parsed properlyLefteris Karapetsas2017-01-272-3/+3
| * | | Fix and better output for testsLefteris Karapetsas2017-01-252-1/+7
| * | | Accept any kind of whitespace after natspec tagsLefteris Karapetsas2017-01-252-13/+70
* | | | Merge pull request #1613 from ethereum/refactorJsonAlex Beregszaszi2017-01-275-51/+49
|\ \ \ \
| * | | | Refactor json return type generation.chriseth2017-01-275-51/+49
|/ / / /
* | | | Merge pull request #1608 from LefterisJP/archlinux_add_ethereum_gitchriseth2017-01-271-0/+2
|\ \ \ \ | |_|/ / |/| | |
| * | | Add ethereum-git to archlinux dependenciesLefteris Karapetsas2017-01-261-0/+2
| |/ /
* | | Merge pull request #1604 from ethereum/checksumsAlex Beregszaszi2017-01-2615-18/+294
|\ \ \
| * | | Simplify length rule.chriseth2017-01-261-2/+2
| * | | Add some more tests for the checksum routine.chriseth2017-01-251-0/+83
| * | | Documentation.chriseth2017-01-251-0/+13
| * | | Also check library addresses.chriseth2017-01-254-23/+49
| * | | Tests for library checksums.chriseth2017-01-252-14/+53
| * | | Warn about invalid checksums of addresses.chriseth2017-01-258-2/+117
| |/ /
* | | Merge pull request #1610 from ethereum/assemblyModeErrorAlex Beregszaszi2017-01-262-26/+33
|\ \ \
| * | | Header cleanup.chriseth2017-01-262-26/+29
| * | | Proper error reporting for assembly mode.chriseth2017-01-261-0/+4
| |/ /
* | | Merge pull request #1611 from ethereum/metadata-literal-sourceschriseth2017-01-264-5/+18
|\ \ \
| * | | Add literal metadata sources to changelogAlex Beregszaszi2017-01-261-0/+1
| * | | Add option to solc to use literal sources in metadtaAlex Beregszaszi2017-01-261-1/+6
| * | | Add option to store literal sources in metadataAlex Beregszaszi2017-01-262-4/+11
|/ / /
* | | Merge pull request #1606 from ethereum/inlineasm-instr-shadowingAlex Beregszaszi2017-01-265-4/+33
|\ \ \ | |/ / |/| |
| * | Update error message.chriseth2017-01-262-2/+2
| * | Disallow assignment to non-identifiers.chriseth2017-01-262-0/+8
| * | Disallow instructions in assignment.chriseth2017-01-261-6/+5
| * | Add test for assignment.chriseth2017-01-261-1/+7
| * | Renamed function.chriseth2017-01-262-5/+5
| * | Moved test.chriseth2017-01-263-15/+7
| * | Rewording in changelog.chriseth2017-01-261-1/+1
| * | Add test for shadowing inline assembly instructionAlex Beregszaszi2017-01-251-0/+13
| * | Do not allow shadowing inline assembly instructions with variablesAlex Beregszaszi2017-01-253-1/+12
|/ /
* | Merge pull request #1595 from ethereum/noDuplicatedContractsRJ Catalano2017-01-252-14/+47
|\ \
| * | Include creation code only once.chriseth2017-01-251-14/+18
| * | Test double inclusion of bytecode.chriseth2017-01-251-0/+29
|/ /
* | Merge pull request #1588 from ethereum/fixrecursivestructschriseth2017-01-258-457/+607
|\ \
| * | Use int arithmetics for stack adjustment.chriseth2017-01-241-1/+1
| * | Change clearStorageLoop to TypePointer.chriseth2017-01-242-16/+18
| * | Changelog entry.chriseth2017-01-241-0/+4
| * | Use shared_ptrs to enable shared_from_this.chriseth2017-01-241-4/+4
| * | Convert ArrayUtils routines into low-level functions.chriseth2017-01-241-228/+251
| * | Move some util functions to low-level functions.chriseth2017-01-245-244/+320
| * | Low level named functions for CompilerContext.chriseth2017-01-242-0/+23
| * | Test for initializing recursive structs.chriseth2017-01-241-0/+22
* | | Merge pull request #1600 from ethereum/functionalAsmOutchriseth2017-01-254-17/+94
|\ \ \ | |/ / |/| |
| * | Create functional assembly output, if possible.chriseth2017-01-244-17/+94
* | | Merge pull request #1245 from ethereum/1215chriseth2017-01-245-7/+177
|\ \ \ | |/ / |/| |
| * | analysis: disallow overloading functions with eventsYoichi Hirai2017-01-231-1/+6
| * | test: add tests about functions and events of the same nameYoichi Hirai2017-01-231-0/+47
| * | analysis: fix formatYoichi Hirai2017-01-231-4/+7
| * | test: add a test case about inheriting multiple events of the same nameYoichi Hirai2017-01-231-0/+52
| * | Changelog: move an item upwardsYoichi Hirai2017-01-231-1/+1
| * | test: somehow log counting system has changedYoichi Hirai2017-01-231-10/+10
| * | analysis: Resolve event overloadingYoichi Hirai2017-01-232-14/+11
| * | test: check the results of function calls in the test for multiple events of ...Yoichi Hirai2017-01-231-6/+9
="color:#2e8b57">static void em_folder_tree_get_folder_info__get (struct _mail_msg *mm) { struct _EMFolderTreeGetFolderInfo *m = (struct _EMFolderTreeGetFolderInfo *) mm; guint32 flags = m->flags; if (camel_store_supports_subscriptions (m->store)) flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED; m->fi = camel_store_get_folder_info (m->store, m->top, flags, &mm->ex); } static void em_folder_tree_get_folder_info__got (struct _mail_msg *mm) { struct _EMFolderTreeGetFolderInfo *m = (struct _EMFolderTreeGetFolderInfo *) mm; struct _EMFolderTreePrivate *priv = m->emft->priv; struct _EMFolderTreeModelStoreInfo *si; GtkTreeIter root, iter; CamelFolderInfo *fi; GtkTreeStore *model; GtkTreePath *path; gboolean load; /* check that we haven't been destroyed */ if (priv->treeview == NULL) return; /* check that our parent folder hasn't been deleted/unsubscribed */ if (!gtk_tree_row_reference_valid (m->root)) return; if (!(si = g_hash_table_lookup (priv->model->store_hash, m->store))) { /* store has been removed in the interim - do nothing */ return; } model = (GtkTreeStore *) gtk_tree_view_get_model (priv->treeview); path = gtk_tree_row_reference_get_path (m->root); gtk_tree_model_get_iter ((GtkTreeModel *) model, &root, path); gtk_tree_path_free (path); /* make sure we still need to load the tree subfolders... */ gtk_tree_model_get ((GtkTreeModel *) model, &root, COL_BOOL_LOAD_SUBDIRS, &load, -1); if (!load) return; /* get the first child (which will be a dummy node) */ gtk_tree_model_iter_children ((GtkTreeModel *) model, &iter, &root); /* FIXME: camel's IMAP code is totally on crack here, @top's * folder info should be @fi and fi->child should be what we * want to fill our tree with... *sigh* */ if (m->top && !strcmp (m->fi->full_name, m->top)) { if (!(fi = m->fi->child)) fi = m->fi->sibling; } else fi = m->fi; if (fi == NULL) { /* no children afterall... remove the "Loading..." placeholder node */ gtk_tree_store_remove (model, &iter); } else { do { em_folder_tree_model_set_folder_info (priv->model, &iter, si, fi); if ((fi = fi->sibling) != NULL) gtk_tree_store_append (model, &iter, &root); } while (fi != NULL); } gtk_tree_store_set (model, &root, COL_BOOL_LOAD_SUBDIRS, FALSE, -1); if (m->select_uri) em_folder_tree_set_selected (m->emft, m->select_uri); em_folder_tree_queue_save_state (m->emft); } static void em_folder_tree_get_folder_info__free (struct _mail_msg *mm) { struct _EMFolderTreeGetFolderInfo *m = (struct _EMFolderTreeGetFolderInfo *) mm; camel_store_free_folder_info (m->store, m->fi); gtk_tree_row_reference_free (m->root); camel_object_unref (m->store); g_free (m->select_uri); g_free (m->top); } static struct _mail_msg_op get_folder_info_op = { NULL, em_folder_tree_get_folder_info__get, em_folder_tree_get_folder_info__got, em_folder_tree_get_folder_info__free, }; static void update_model_expanded_state (struct _EMFolderTreePrivate *priv, GtkTreeIter *iter, gboolean expanded) { struct _EMFolderTreeModelStoreInfo *si; CamelStore *store; EAccount *account; char *path, *key; gtk_tree_model_get ((GtkTreeModel *) priv->model, iter, COL_STRING_FOLDER_PATH, &path, COL_POINTER_CAMEL_STORE, &store, -1); si = g_hash_table_lookup (priv->model->store_hash, store); if ((account = mail_config_get_account_by_name (si->display_name))) { key = g_strdup_printf ("%s:%s", account->uid, path); } else { key = g_strdup_printf ("%s:%s", si->display_name, path); } em_folder_tree_model_set_expanded (priv->model, key, expanded); g_free (key); } static void tree_row_expanded (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *tree_path, EMFolderTree *emft) { struct _EMFolderTreePrivate *priv = emft->priv; struct _EMFolderTreeGetFolderInfo *m; GtkTreeModel *model; CamelStore *store; const char *top; gboolean load; char *path; model = gtk_tree_view_get_model (treeview); gtk_tree_model_get (model, root, COL_STRING_FOLDER_PATH, &path, COL_POINTER_CAMEL_STORE, &store, COL_BOOL_LOAD_SUBDIRS, &load, -1); update_model_expanded_state (priv, root, TRUE); if (!load) { em_folder_tree_queue_save_state (emft); return; } if (!path || !strcmp (path, "/")) top = NULL; else top = path + 1; m = mail_msg_new (&get_folder_info_op, NULL, sizeof (struct _EMFolderTreeGetFolderInfo)); m->root = gtk_tree_row_reference_new (model, tree_path); camel_object_ref (store); m->store = store; m->emft = emft; m->top = g_strdup (top); m->flags = 0; m->select_uri = NULL; e_thread_put (mail_thread_new, (EMsg *) m); } static void tree_row_collapsed (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *tree_path, EMFolderTree *emft) { update_model_expanded_state (emft->priv, root, FALSE); em_folder_tree_queue_save_state (emft); } #if 0 static void emft_popup_view (GtkWidget *item, EMFolderTree *emft) { } static void emft_popup_open_new (GtkWidget *item, EMFolderTree *emft) { } #endif struct _EMCopyFolders { struct _mail_msg msg; /* input data */ CamelStore *fromstore; CamelStore *tostore; char *frombase; char *tobase; int delete; }; static void em_copy_folders__copy (struct _mail_msg *mm) { struct _EMCopyFolders *m = (struct _EMCopyFolders *) mm; guint32 flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE; GList *pending = NULL, *deleting = NULL, *l; GString *fromname, *toname; CamelFolderInfo *fi; const char *tmp; int fromlen; if (camel_store_supports_subscriptions (m->fromstore)) flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED; if (!(fi = camel_store_get_folder_info (m->fromstore, m->frombase, flags, &mm->ex))) return; pending = g_list_append (pending, fi); toname = g_string_new (""); fromname = g_string_new (""); tmp = strrchr (m->frombase, '/'); if (tmp == NULL) fromlen = 0; else fromlen = tmp - m->frombase + 1; d(printf ("top name is '%s'\n", fi->full_name)); while (pending) { CamelFolderInfo *info = pending->data; pending = g_list_remove_link (pending, pending); while (info) { CamelFolder *fromfolder, *tofolder; GPtrArray *uids; if (info->child) pending = g_list_append (pending, info->child); if (m->tobase[0]) g_string_printf (toname, "%s/%s", m->tobase, info->full_name + fromlen); else g_string_printf (toname, "%s", info->full_name + fromlen); d(printf ("Copying from '%s' to '%s'\n", info->full_name, toname->str)); /* This makes sure we create the same tree, e.g. from a nonselectable source */ /* Not sure if this is really the 'right thing', e.g. for spool stores, but it makes the ui work */ if ((info->flags & CAMEL_FOLDER_NOSELECT) == 0) { d(printf ("this folder is selectable\n")); if (!(fromfolder = camel_store_get_folder (m->fromstore, info->full_name, 0, &mm->ex))) goto exception; if (!(tofolder = camel_store_get_folder (m->tostore, toname->str, CAMEL_STORE_FOLDER_CREATE, &mm->ex))) { camel_object_unref (fromfolder); goto exception; } if (camel_store_supports_subscriptions (m->tostore) && !camel_store_folder_subscribed (m->tostore, toname->str)) camel_store_subscribe_folder (m->tostore, toname->str, NULL); uids = camel_folder_get_uids (fromfolder); camel_folder_transfer_messages_to (fromfolder, uids, tofolder, NULL, m->delete, &mm->ex); camel_folder_free_uids (fromfolder, uids); camel_object_unref (fromfolder); camel_object_unref (tofolder); } if (camel_exception_is_set (&mm->ex)) goto exception; else if (m->delete) deleting = g_list_prepend (deleting, info); info = info->sibling; } } /* delete the folders in reverse order from how we copyied them, if we are deleting any */ l = deleting; while (l) { CamelFolderInfo *info = l->data; d(printf ("deleting folder '%s'\n", info->full_name)); if (camel_store_supports_subscriptions (m->fromstore)) camel_store_unsubscribe_folder (m->fromstore, info->full_name, NULL); camel_store_delete_folder (m->fromstore, info->full_name, NULL); l = l->next; } exception: camel_store_free_folder_info (m->fromstore, fi); g_list_free (deleting); g_string_free (toname, TRUE); g_string_free (fromname, TRUE); } static void em_copy_folders__free (struct _mail_msg *mm) { struct _EMCopyFolders *m = (struct _EMCopyFolders *) mm; camel_object_unref (m->fromstore); camel_object_unref (m->tostore); g_free (m->frombase); g_free (m->tobase); } static struct _mail_msg_op copy_folders_op = { NULL, em_copy_folders__copy, NULL, em_copy_folders__free, }; static void em_copy_folders (CamelStore *tostore, const char *tobase, CamelStore *fromstore, const char *frombase, int delete) { struct _EMCopyFolders *m; m = mail_msg_new (&copy_folders_op, NULL, sizeof (struct _EMCopyFolders)); camel_object_ref (fromstore); m->fromstore = fromstore; camel_object_ref (tostore); m->tostore = tostore; m->frombase = g_strdup (frombase); m->tobase = g_strdup (tobase); m->delete = delete; e_thread_put (mail_thread_new, (EMsg *) m); } struct _copy_folder_data { EMFolderTree *emft; gboolean delete; }; static void emft_popup_copy_folder_selected (const char *uri, void *data) { struct _copy_folder_data *cfd = data; struct _EMFolderTreePrivate *priv; CamelStore *fromstore, *tostore; char *tobase, *frombase; CamelException ex; GtkWidget *dialog; CamelURL *url; if (uri == NULL) { g_free (cfd); return; } priv = cfd->emft->priv; d(printf ("copying folder '%s' to '%s'\n", priv->selected_path, uri)); camel_exception_init (&ex); if (!(fromstore = camel_session_get_store (session, priv->selected_uri, &ex))) goto exception; frombase = priv->selected_path + 1; if (!(tostore = camel_session_get_store (session, uri, &ex))) { camel_object_unref (fromstore); goto exception; } url = camel_url_new (uri, NULL); if (url->fragment) tobase = url->fragment; else if (url->path && url->path[0]) tobase = url->path + 1; else tobase = ""; em_copy_folders (tostore, tobase, fromstore, frombase, cfd->delete); camel_url_free (url); g_free (cfd); return; exception: dialog = gtk_message_dialog_new ((GtkWindow *) cfd->emft, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("%s"), ex.desc); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); camel_exception_clear (&ex); gtk_widget_show (dialog); g_free (cfd); } static void emft_popup_copy (GtkWidget *item, EMFolderTree *emft) { struct _copy_folder_data *cfd; cfd = g_malloc (sizeof (*cfd)); cfd->emft = emft; cfd->delete = FALSE; em_select_folder (NULL, _("Select folder"), NULL, emft_popup_copy_folder_selected, cfd); } static void emft_popup_move (GtkWidget *item, EMFolderTree *emft) { struct _copy_folder_data *cfd; cfd = g_malloc (sizeof (*cfd)); cfd->emft = emft; cfd->delete = TRUE; em_select_folder (NULL, _("Select folder"), NULL, emft_popup_copy_folder_selected, cfd); } gboolean em_folder_tree_create_folder (EMFolderTree *emft, const char *path, const char *uri) { struct _EMFolderTreePrivate *priv = emft->priv; struct _EMFolderTreeModelStoreInfo *si; const char *parent, *full_name; char *name, *namebuf = NULL; GtkWidget *dialog; CamelStore *store; CamelException ex; d(printf ("Creating folder: %s (%s)\n", path, uri)); camel_exception_init (&ex); if (!(store = (CamelStore *) camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex))) goto exception; if (!(si = g_hash_table_lookup (priv->model->store_hash, store))) { camel_object_unref (store); g_assert_not_reached (); goto exception; } camel_object_unref (store); full_name = path[0] == '/' ? path + 1 : path; namebuf = g_strdup (full_name); if (!(name = strrchr (namebuf, '/'))) { name = namebuf; parent = ""; } else { *name++ = '\0'; parent = namebuf; } d(printf ("creating folder parent='%s' name='%s' path='%s'\n", parent, name, path)); camel_store_create_folder (si->store, parent, name, &ex); if (camel_exception_is_set (&ex)) { goto exception; } else if (camel_store_supports_subscriptions (si->store)) { camel_store_subscribe_folder (si->store, full_name, &ex); if (camel_exception_is_set (&ex)) goto exception; } g_free (namebuf); return TRUE; exception: dialog = gtk_message_dialog_new ((GtkWindow *) emft, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("%s"), ex.desc); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); camel_exception_clear (&ex); g_free (namebuf); gtk_widget_show (dialog); return FALSE; } static void emft_popup_new_folder_response (EMFolderSelector *emfs, int response, EMFolderTree *emft) { const char *uri, *path; if (response != GTK_RESPONSE_OK) { gtk_widget_destroy ((GtkWidget *) emfs); return; } uri = em_folder_selector_get_selected_uri (emfs); path = em_folder_selector_get_selected_path (emfs); if (em_folder_tree_create_folder (emfs->emft, path, uri)) gtk_widget_destroy ((GtkWidget *) emfs); } static void emft_popup_new_folder (GtkWidget *item, EMFolderTree *emft) { EMFolderTree *folder_tree; GtkWidget *dialog; folder_tree = (EMFolderTree *) em_folder_tree_new_with_model (emft->priv->model); dialog = em_folder_selector_create_new (folder_tree, 0, _("Create folder"), _("Specify where to create the folder:")); em_folder_selector_set_selected ((EMFolderSelector *) dialog, emft->priv->selected_uri); g_signal_connect (dialog, "response", G_CALLBACK (emft_popup_new_folder_response), emft); gtk_widget_show (dialog); } static void emft_popup_delete_rec (CamelStore *store, CamelFolderInfo *fi, CamelException *ex) { while (fi) { CamelFolder *folder; if (fi->child) { emft_popup_delete_rec (store, fi->child, ex); if (camel_exception_is_set (ex)) return; } d(printf ("deleting folder '%s'\n", fi->full_name)); /* shouldn't camel do this itself? */ if (camel_store_supports_subscriptions (store)) camel_store_unsubscribe_folder (store, fi->full_name, NULL); folder = camel_store_get_folder (store, fi->full_name, 0, NULL); if (folder) { GPtrArray *uids = camel_folder_get_uids (folder); int i; camel_folder_freeze (folder); for (i = 0; i < uids->len; i++) camel_folder_delete_message (folder, uids->pdata[i]); camel_folder_sync (folder, TRUE, NULL); camel_folder_thaw (folder); camel_folder_free_uids (folder, uids); } camel_store_delete_folder (store, fi->full_name, ex); if (camel_exception_is_set (ex)) return; fi = fi->sibling; } } static void emft_popup_delete_folders (CamelStore *store, const char *path, CamelException *ex) { guint32 flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE; const char *full_name; CamelFolderInfo *fi; if (camel_store_supports_subscriptions (store)) flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED; full_name = path[0] == '/' ? path + 1 : path; fi = camel_store_get_folder_info (store, full_name, flags, ex); if (camel_exception_is_set (ex)) return; emft_popup_delete_rec (store, fi, ex); camel_store_free_folder_info (store, fi); } static void emft_popup_delete_response (GtkWidget *dialog, guint response, EMFolderTree *emft) { struct _EMFolderTreePrivate *priv = emft->priv; GtkTreeSelection *selection; GtkTreeModel *model; CamelStore *store; CamelException ex; GtkTreeIter iter; char *path; gtk_widget_destroy (dialog); if (response != GTK_RESPONSE_OK) return; selection = gtk_tree_view_get_selection (priv->treeview); gtk_tree_selection_get_selected (selection, &model, &iter); gtk_tree_model_get (model, &iter, COL_STRING_FOLDER_PATH, &path, COL_POINTER_CAMEL_STORE, &store, -1); camel_exception_init (&ex); emft_popup_delete_folders (store, path, &ex); if (camel_exception_is_set (&ex)) { e_notice (NULL, GTK_MESSAGE_ERROR, _("Could not delete folder: %s"), ex.desc); camel_exception_clear (&ex); } } static void emft_popup_delete_folder (GtkWidget *item, EMFolderTree *emft) { struct _EMFolderTreePrivate *priv = emft->priv; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *dialog; char *title, *path; selection = gtk_tree_view_get_selection (priv->treeview); gtk_tree_selection_get_selected (selection, &model, &iter); gtk_tree_model_get (model, &iter, COL_STRING_FOLDER_PATH, &path, -1); dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Really delete folder \"%s\" and all of its subfolders?"), path); gtk_dialog_add_button ((GtkDialog *) dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); gtk_dialog_add_button ((GtkDialog *) dialog, GTK_STOCK_DELETE, GTK_RESPONSE_OK); gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_OK); gtk_container_set_border_width ((GtkContainer *) dialog, 6); gtk_box_set_spacing ((GtkBox *) ((GtkDialog *) dialog)->vbox, 6); title = g_strdup_printf (_("Delete \"%s\""), path); gtk_window_set_title ((GtkWindow *) dialog, title); g_free (title); g_signal_connect (dialog, "response", G_CALLBACK (emft_popup_delete_response), emft); gtk_widget_show (dialog); } static void emft_popup_rename_folder (GtkWidget *item, EMFolderTree *emft) { struct _EMFolderTreePrivate *priv = emft->priv; char *prompt, *folder_path, *name, *new_name, *uri; GtkTreeSelection *selection; const char *full_name, *p; gboolean done = FALSE; GtkTreeModel *model; CamelStore *store; GtkTreeIter iter; size_t base_len; selection = gtk_tree_view_get_selection (priv->treeview); gtk_tree_selection_get_selected (selection, &model, &iter); gtk_tree_model_get (model, &iter, COL_STRING_FOLDER_PATH, &folder_path, COL_STRING_DISPLAY_NAME, &name, COL_POINTER_CAMEL_STORE, &store, COL_STRING_URI, &uri, -1); full_name = folder_path[0] == '/' ? folder_path + 1 : folder_path; if ((p = strrchr (full_name, '/'))) base_len = (size_t) (p - full_name); else base_len = 0; prompt = g_strdup_printf (_("Rename the \"%s\" folder to:"), name); while (!done) { new_name = e_request_string (NULL, _("Rename Folder"), prompt, name); if (new_name == NULL || !strcmp (name, new_name)) { /* old name == new name */ done = TRUE; } else { CamelFolderInfo *fi; CamelException ex; char *path, *p; if (base_len > 0) { path = g_malloc (base_len + strlen (new_name) + 2); memcpy (path, full_name, base_len); p = path + base_len; *p++ = '/'; strcpy (p, new_name); } else { path = g_strdup (new_name); } camel_exception_init (&ex); if ((fi = camel_store_get_folder_info (store, path, CAMEL_STORE_FOLDER_INFO_FAST, &ex)) != NULL) { camel_store_free_folder_info (store, fi); e_notice (NULL, GTK_MESSAGE_ERROR, _("A folder named \"%s\" already exists. Please use a different name."), new_name); } else { const char *oldpath, *newpath; oldpath = full_name; newpath = path; d(printf ("renaming %s to %s\n", oldpath, newpath)); camel_exception_clear (&ex); camel_store_rename_folder (store, oldpath, newpath, &ex); if (camel_exception_is_set (&ex)) { e_notice (NULL, GTK_MESSAGE_ERROR, _("Could not rename folder: %s"), ex.desc); camel_exception_clear (&ex); } done = TRUE; } g_free (path); } g_free (new_name); } } struct _prop_data { void *object; CamelArgV *argv; GtkWidget **widgets; }; static void emft_popup_properties_response (GtkWidget *dialog, int response, struct _prop_data *prop_data) { CamelArgV *argv = prop_data->argv; int i; if (response != GTK_RESPONSE_OK) { gtk_widget_destroy (dialog); return; } for (i = 0; i < argv->argc; i++) { CamelArg *arg = &argv->argv[i]; switch (arg->tag & CAMEL_ARG_TYPE) { case CAMEL_ARG_BOO: arg->ca_int = gtk_toggle_button_get_active ((GtkToggleButton *) prop_data->widgets[i]); break; case CAMEL_ARG_STR: g_free (arg->ca_str); arg->ca_str = (char *) gtk_entry_get_text ((GtkEntry *) prop_data->widgets[i]); break; default: g_assert_not_reached (); break; } } camel_object_setv (prop_data->object, NULL, argv); gtk_widget_destroy (dialog); } static void emft_popup_properties_free (void *data) { struct _prop_data *prop_data = data; int i; for (i = 0; i < prop_data->argv->argc; i++) { if ((prop_data->argv->argv[i].tag & CAMEL_ARG_TYPE) == CAMEL_ARG_STR) g_free (prop_data->argv->argv[i].ca_str); } camel_object_unref (prop_data->object); g_free (prop_data->argv); g_free (prop_data); } static void emft_popup_properties_got_folder (char *uri, CamelFolder *folder, void *data) { GtkWidget *dialog, *w, *table, *label; struct _prop_data *prop_data; CamelArgGetV *arggetv; CamelArgV *argv; GSList *list, *l; gint32 count, i; char *name; int row = 1; if (folder == NULL) return; camel_object_get (folder, NULL, CAMEL_FOLDER_PROPERTIES, &list, CAMEL_FOLDER_NAME, &name, NULL); dialog = gtk_dialog_new_with_buttons (_("Folder properties"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); /* TODO: maybe we want some basic properties here, like message counts/approximate size/etc */ w = gtk_frame_new (_("Properties")); gtk_widget_show (w); gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dialog)->vbox, w, TRUE, TRUE, 6); table = gtk_table_new (g_slist_length (list) + 1, 2, FALSE); gtk_widget_show (table); gtk_container_add ((GtkContainer *) w, table); label = gtk_label_new (_("Folder Name")); gtk_widget_show (label); gtk_misc_set_alignment ((GtkMisc *) label, 1.0, 0.5); gtk_table_attach ((GtkTable *) table, label, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND, 0, 3, 0); label = gtk_label_new (name); gtk_widget_show (label); gtk_misc_set_alignment ((GtkMisc *) label, 0.0, 0.5); gtk_table_attach ((GtkTable *) table, label, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 3, 0); /* build an arggetv/argv to retrieve/store the results */ count = g_slist_length (list); arggetv = g_malloc0 (sizeof (*arggetv) + (count - CAMEL_ARGV_MAX) * sizeof (arggetv->argv[0])); arggetv->argc = count; argv = g_malloc0 (sizeof (*argv) + (count - CAMEL_ARGV_MAX) * sizeof (argv->argv[0])); argv->argc = count; i = 0; l = list; while (l) { CamelProperty *prop = l->data; argv->argv[i].tag = prop->tag; arggetv->argv[i].tag = prop->tag; arggetv->argv[i].ca_ptr = &argv->argv[i].ca_ptr; l = l->next; i++; } camel_object_getv (folder, NULL, arggetv); g_free (arggetv); prop_data = g_malloc0 (sizeof (*prop_data)); prop_data->widgets = g_malloc0 (sizeof (prop_data->widgets[0]) * count); prop_data->argv = argv; /* setup the ui with the values retrieved */ l = list; i = 0; while (l) { CamelProperty *prop = l->data; switch (prop->tag & CAMEL_ARG_TYPE) { case CAMEL_ARG_BOO: w = gtk_check_button_new_with_label (prop->description); gtk_toggle_button_set_active ((GtkToggleButton *) w, argv->argv[i].ca_int != 0); gtk_widget_show (w); gtk_table_attach ((GtkTable *) table, w, 0, 2, row, row + 1, 0, 0, 3, 3); prop_data->widgets[i] = w; break; case CAMEL_ARG_STR: label = gtk_label_new (prop->description); gtk_misc_set_alignment ((GtkMisc *) label, 1.0, 0.5); gtk_widget_show (label); gtk_table_attach ((GtkTable *) table, label, 0, 1, row, row + 1, GTK_FILL | GTK_EXPAND, 0, 3, 3); w = gtk_entry_new (); gtk_widget_show (w); if (argv->argv[i].ca_str) { gtk_entry_set_text ((GtkEntry *) w, argv->argv[i].ca_str); camel_object_free (folder, argv->argv[i].tag, argv->argv[i].ca_str); argv->argv[i].ca_str = NULL; } gtk_table_attach ((GtkTable *) table, w, 1, 2, row, row + 1, GTK_FILL, 0, 3, 3); prop_data->widgets[i] = w; break; default: g_assert_not_reached (); break; } row++; l = l->next; } prop_data->object = folder; camel_object_ref (folder); camel_object_free (folder, CAMEL_FOLDER_PROPERTIES, list); camel_object_free (folder, CAMEL_FOLDER_NAME, name); /* we do 'apply on ok' ... since instant apply may apply some very long running tasks */ g_signal_connect (dialog, "response", G_CALLBACK (emft_popup_properties_response), prop_data); g_object_set_data_full ((GObject *) dialog, "e-prop-data", prop_data, emft_popup_properties_free); gtk_widget_show (dialog); } static void emft_popup_properties (GtkWidget *item, EMFolderTree *emft) { struct _EMFolderTreePrivate *priv = emft->priv; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; char *uri; selection = gtk_tree_view_get_selection (priv->treeview); gtk_tree_selection_get_selected (selection, &model, &iter); gtk_tree_model_get (model, &iter, COL_STRING_URI, &uri, -1); mail_get_folder (uri, 0, emft_popup_properties_got_folder, emft, mail_thread_new); } static EMPopupItem emft_popup_menu[] = { #if 0 { EM_POPUP_ITEM, "00.emc.00", N_("_View"), G_CALLBACK (emft_popup_view), NULL, NULL, 0 }, { EM_POPUP_ITEM, "00.emc.01", N_("Open in _New Window"), G_CALLBACK (emft_popup_open_new), NULL, NULL, 0 }, { EM_POPUP_BAR, "10.emc" }, #endif { EM_POPUP_ITEM, "10.emc.00", N_("_Copy"), G_CALLBACK (emft_popup_copy), NULL, "folder-copy-16.png", 0 }, { EM_POPUP_ITEM, "10.emc.01", N_("_Move"), G_CALLBACK (emft_popup_move), NULL, "folder-move-16.png", 0 }, { EM_POPUP_BAR, "20.emc" }, { EM_POPUP_ITEM, "20.emc.00", N_("_New Folder..."), G_CALLBACK (emft_popup_new_folder), NULL, "folder-mini.png", 0 }, { EM_POPUP_ITEM, "20.emc.01", N_("_Delete"), G_CALLBACK (emft_popup_delete_folder), NULL, "evolution-trash-mini.png", 0 }, { EM_POPUP_ITEM, "20.emc.01", N_("_Rename"), G_CALLBACK (emft_popup_rename_folder), NULL, NULL, 0 }, { EM_POPUP_BAR, "80.emc" }, { EM_POPUP_ITEM, "80.emc.00", N_("_Properties..."), G_CALLBACK (emft_popup_properties), NULL, "configure_16_folder.xpm", 0 }, }; static gboolean tree_button_press (GtkWidget *treeview, GdkEventButton *event, EMFolderTree *emft) { GSList *menus = NULL; GtkMenu *menu; EMPopup *emp; int i; if (event->button != 3) return FALSE; /* handle right-click by opening a context menu */ emp = em_popup_new ("com.ximian.mail.storageset.popup.select"); for (i = 0; i < sizeof (emft_popup_menu) / sizeof (emft_popup_menu[0]); i++) { EMPopupItem *item = &emft_popup_menu[i]; item->activate_data = emft; menus = g_slist_prepend (menus, item); } em_popup_add_items (emp, menus, (GDestroyNotify) g_slist_free); menu = em_popup_create_menu_once (emp, NULL, 0, 0); if (event == NULL || event->type == GDK_KEY_PRESS) { /* FIXME: menu pos function */ gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, event->time); } else { gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button, event->time); } return TRUE; } static void tree_selection_changed (GtkTreeSelection *selection, EMFolderTree *emft) { struct _EMFolderTreePrivate *priv = emft->priv; GtkTreeModel *model; GtkTreeIter iter; char *path, *uri; if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, COL_STRING_FOLDER_PATH, &path, COL_STRING_URI, &uri, -1); g_free (priv->selected_uri); priv->selected_uri = g_strdup (uri); g_free (priv->selected_path); priv->selected_path = g_strdup (path); g_signal_emit (emft, signals[FOLDER_SELECTED], 0, path, uri); } void em_folder_tree_set_selected (EMFolderTree *emft, const char *uri) { struct _EMFolderTreeModelStoreInfo *si; struct _EMFolderTreeGetFolderInfo *m; struct _EMFolderTreePrivate *priv; GtkTreeRowReference *row = NULL; GtkTreeSelection *selection; GtkTreePath *tree_path; CamelStore *store; CamelException ex; const char *top; char *path, *p; CamelURL *url; g_return_if_fail (EM_IS_FOLDER_TREE (emft)); priv = emft->priv; camel_exception_init (&ex); if (!(store = (CamelStore *) camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex))) { camel_exception_clear (&ex); return; } if (!(si = g_hash_table_lookup (priv->model->store_hash, store))) { camel_object_unref (store); return; } if (!(url = camel_url_new (uri, NULL))) { camel_object_unref (store); return; } if (((CamelService *) store)->provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH) path = g_strdup_printf ("/%s", url->fragment ? url->fragment : ""); else path = g_strdup (url->path ? url->path : "/"); top = path[0] == '/' ? path + 1 : path; camel_url_free (url); if (!strcmp (path, "/")) row = si->row; if (row || (row = g_hash_table_lookup (si->path_hash, path))) { /* the folder-info node has already been loaded */ tree_path = gtk_tree_row_reference_get_path (row); gtk_tree_view_expand_to_path (priv->treeview, tree_path); selection = gtk_tree_view_get_selection (priv->treeview); gtk_tree_selection_select_path (selection, tree_path); gtk_tree_path_free (tree_path); camel_object_unref (store); g_free (path); return; } /* look for the first of our parent folders that has already been loaded */ p = path + strlen (path); while (p > path) { if (*p == '/') { *p = '\0'; if ((row = g_hash_table_lookup (si->path_hash, path))) break; } p--; } if (row == NULL) { /* none of the folders of the desired store have been loaded yet */ row = si->row; top = NULL; } /* FIXME: this gets all the subfolders of our first loaded * parent folder - ideally we'd only get what we needed, but * it's probably not worth the effort */ m = mail_msg_new (&get_folder_info_op, NULL, sizeof (struct _EMFolderTreeGetFolderInfo)); m->root = gtk_tree_row_reference_copy (row); m->store = store; m->emft = emft; m->top = top ? g_strdup (top) : NULL; m->flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE; m->select_uri = g_strdup (uri); g_free (path); e_thread_put (mail_thread_new, (EMsg *) m); } const char * em_folder_tree_get_selected_uri (EMFolderTree *emft) { g_return_val_if_fail (EM_IS_FOLDER_TREE (emft), NULL); return emft->priv->selected_uri; } const char * em_folder_tree_get_selected_path (EMFolderTree *emft) { g_return_val_if_fail (EM_IS_FOLDER_TREE (emft), NULL); return emft->priv->selected_path; } EMFolderTreeModel * em_folder_tree_get_model (EMFolderTree *emft) { g_return_val_if_fail (EM_IS_FOLDER_TREE (emft), NULL); return emft->priv->model; } static gboolean em_folder_tree_save_state (EMFolderTree *emft) { struct _EMFolderTreePrivate *priv = emft->priv; em_folder_tree_model_save_expanded (priv->model); priv->save_state_id = 0; return FALSE; } static void em_folder_tree_queue_save_state (EMFolderTree *emft) { struct _EMFolderTreePrivate *priv = emft->priv; if (priv->save_state_id != 0) return; priv->save_state_id = g_timeout_add (1000, (GSourceFunc) em_folder_tree_save_state, emft); }