diff --git a/resources/Scribe.lr8 b/resources/Scribe.lr8 --- a/resources/Scribe.lr8 +++ b/resources/Scribe.lr8 @@ -1,5231 +1,5231 @@ - + - + - + - + - - + + - + - + - + - + - + + + - - + - + - + - - - - - - + + + + + + - - + - + - - - - + - diff --git a/resources/resdefs.h b/resources/resdefs.h --- a/resources/resdefs.h +++ b/resources/resdefs.h @@ -1,1285 +1,1284 @@ // Generated by LgiRes // This file generated by LgiRes #define L_FUI_NEW -912 #define L_FUI_LEGEND -911 #define L_FUI_NOT -910 #define L_FUI_OPTIONS -909 #define L_FUI_CONFIGURE -908 #define L_FUI_MOVE_DOWN -907 #define L_FUI_MOVE_UP -906 #define L_FUI_DELETE -905 #define L_FUI_OR -904 #define L_FUI_AND -903 #define L_FUI_NEW_OR -902 #define L_FUI_NEW_AND -901 #define L_FUI_NEW_CONDITION -900 #define L_STORE_RESTART -803 #define L_STORE_MISMATCH -802 #define L_STORE_OS_ERR -801 #define L_STORE_WRITE_ERR -800 #define L_TOOLBAR_SHOW_TEXT -700 #define L_FR_SELECTION_ONLY -608 #define L_FR_REPLACE_WITH -607 #define L_FR_REPLACE_ALL -606 #define L_FR_REPLACE -605 #define L_FR_MATCH_CASE -604 #define L_FR_MATCH_WORD -603 #define L_FR_FIND_NEXT -602 #define L_FR_FIND_WHAT -601 #define L_FR_FIND -600 #define L_COLOUR_NONE -550 #define L_CHANGE_CHARSET -505 #define L_VIEW_IMAGES -504 #define L_VIEW_IN_DEFAULT_BROWSER -503 #define L_COPY_SOURCE -502 #define L_VIEW_SOURCE -501 #define L_COPY_LINK_LOCATION -500 #define L_FONTUI_UNDERLINE -407 #define L_FONTUI_TITLE -406 #define L_FONTUI_STYLE -405 #define L_FONTUI_PTSIZE -404 #define L_FONTUI_PREVIEW -403 #define L_FONTUI_ITALIC -402 #define L_FONTUI_FACE -401 #define L_FONTUI_BOLD -400 #define L_TEXTCTRL_TAB_SIZE -214 #define L_TEXTCTRL_INDENT_SIZE -213 #define L_TEXTCTRL_HARD_TABS -212 #define L_TEXTCTRL_SHOW_WHITESPACE -211 #define L_TEXTCTRL_UNDO -210 #define L_TEXTCTRL_REDO -209 #define L_TEXTCTRL_PASTE -208 #define L_TEXTCTRL_OPENURL -207 #define L_TEXTCTRL_GOTO_LINE -206 #define L_TEXTCTRL_FIXED -205 #define L_TEXTCTRL_EMAIL_TO -204 #define L_TEXTCTRL_CUT -203 #define L_TEXTCTRL_COPYLINK -202 #define L_TEXTCTRL_COPY -201 #define L_TEXTCTRL_AUTO_INDENT -200 #define IDS_ERROR_ESMTP_UNSUPPORTED_AUTHS -101 #define IDS_ERROR_ESMTP_NO_AUTHS -100 #define L_BTN_CANCEL -51 #define L_BTN_OK -50 #define FIELD_FLAGS 1 #define FIELD_TO 2 #define FIELD_CC 3 #define FIELD_FROM 4 #define FIELD_REPLY 5 #define FIELD_SUBJECT 6 #define FIELD_TEXT 7 #define FIELD_MESSAGE_ID 8 #define FIELD_DATE_RECEIVED 9 #define FIELD_INTERNET_HEADER 10 #define FIELD_FIRST_NAME 11 #define FIELD_LAST_NAME 12 #define FIELD_EMAIL 13 #define FIELD_HOME_STREET 14 #define FIELD_HOME_SUBURB 15 #define FIELD_HOME_POSTCODE 16 #define FIELD_HOME_STATE 17 #define FIELD_HOME_COUNTRY 18 #define FIELD_WORK_PHONE 19 #define FIELD_HOME_PHONE 20 #define FIELD_HOME_MOBILE 21 #define FIELD_HOME_IM 22 #define FIELD_HOME_FAX 23 #define FIELD_HOME_WEBPAGE 24 #define FIELD_NICK 25 #define FIELD_SPOUSE 26 #define FIELD_NOTE 27 #define FIELD_PLUGIN_ASSOC 28 #define FIELD_SIZE 29 #define FIELD_DATE_SENT 30 #define FIELD_COLUMN 31 #define FIELD_BCC 32 #define FIELD_MIME_TYPE 33 #define FIELD_PRIORITY 34 #define FIELD_FOLDER_OPEN 35 #define FIELD_CODE_PAGE 36 #define FIELD_MARK_COLOUR 37 #define FIELD_ALTERNATE_HTML 38 #define FIELD_CONTENT_ID 39 #define FIELD_FILTER_NAME 40 #define FIELD_CONDITION 41 #define FIELD_ACTION 42 #define FIELD_COND_FIELD 43 #define FIELD_COND_OPERATOR 44 #define FIELD_COND_VALUE 45 #define FIELD_ACT_TYPE 46 #define FIELD_ACT_ARG 47 #define FIELD_DIGEST_INDEX 48 #define FIELD_COMBINE_OP 49 #define FIELD_FILTER_INDEX 50 #define FIELD_FILTER_INCOMING 55 #define FIELD_FILTER_OUTGOING 56 #define FIELD_FILTER_INTERNAL 59 #define FIELD_CAL_SUBJECT 62 #define FIELD_CAL_LOCATION 63 #define FIELD_CAL_REMINDER_TIME 64 #define FIELD_CAL_REMINDER_ACTION_dep 65 #define FIELD_CAL_REMINDER_ARG_dep 66 #define FIELD_CAL_SHOW_TIME_AS 67 #define FIELD_CAL_RECUR_FREQ 68 #define FIELD_CAL_RECUR_INTERVAL 69 #define FIELD_CAL_RECUR_FILTER_DAYS 70 #define FIELD_CAL_RECUR_FILTER_MONTHS 71 #define FIELD_CAL_RECUR_FILTER_YEARS 72 #define FIELD_CAL_NOTES 73 #define FIELD_CAL_START_UTC 76 #define FIELD_CAL_END_UTC 77 #define FIELD_CAL_RECUR_FILTER_POS 78 #define FIELD_CAL_RECUR_END_DATE 79 #define FIELD_CAL_RECUR_END_COUNT 80 #define FIELD_CAL_RECUR_END_TYPE 81 #define FIELD_CAL_RECUR 82 #define IDC_COLOUR 83 #define FIELD_ATTENDEE_NAME 85 #define FIELD_ATTENDEE_EMAIL 86 #define FIELD_ATTENDEE_ATTENDENCE 87 #define FIELD_ATTENDEE_NOTE 88 #define FIELD_ATTENDEE_RESPONSE 89 #define FIELD_WORK_STREET 90 #define FIELD_WORK_SUBURB 91 #define FIELD_WORK_POSTCODE 92 #define FIELD_WORK_STATE 93 #define FIELD_WORK_COUNTRY 94 #define FIELD_WORK_MOBILE 95 #define FIELD_WORK_IM 96 #define FIELD_WORK_FAX 97 #define FIELD_WORK_WEBPAGE 98 #define FIELD_COMPANY 99 #define FIELD_LABEL 110 #define IDM_SAVE 113 #define FIELD_TITLE 114 #define FIELD_SERVER_UID 117 #define FIELD_GROUP_NAME 120 #define FIELD_GROUP_LIST 123 #define FIELD_FROM_CONTACT_NAME 126 #define IDS_ENCRYPTION_SUPPORT 150 #define FIELD_DATE_MODIFIED 162 #define FIELD_IMAP_SEQ 167 #define FIELD_RECEIVED_DOMAIN 170 #define IDD_FILTER_ITEMS 175 #define IDC_SIGNATURE_TAB 184 #define IDC_RESET_REPLY 186 #define IDC_SIG 187 #define IDC_RESET_FORWARD 189 #define IDS_205 205 #define IDS_SAVE_ITEM 208 #define IDS_DESCRIPTIVE_NAME 209 #define IDS_ERROR_SOFTWARE_UPDATE 210 #define IDS_211 211 #define IDS_235 235 #define IDC_SUB_FOLDERS 275 #define IDM_FEEDBACK 287 #define IDM_SCRIBE_LINK 291 #define IDM_INSCRIBE_LINK 292 #define IDM_FAQ 293 #define IDC_READ 316 #define IDD_SUB_FOLDERS 317 #define IDC_HAS_TEMPLATES 322 #define IDC_INBOX 324 #define IDC_OUTBOX 325 #define IDM_PAGE_SETUP 326 #define IDC_TRASH 327 #define IDC_CONTACT_FLD 328 #define IDS_DEBUG 329 #define IDC_SET_INBOX 330 #define IDC_SET_OUTBOX 331 #define IDC_SET_SENT 332 #define IDC_SET_TRASH 333 #define IDC_SET_CONTACTS 334 #define IDC_SET_FILTERS 335 #define IDC_MAX_SIZE 342 #define IDC_CONFIRM_DEL 343 #define IDS_CONTENT_ID 344 #define IDC_BOLD_UNREAD 346 #define IDC_GRID_LINES 347 #define IDC_TITLE 348 #define IDC_PREVIEW_LINES 349 #define IDC_SET_TEMPLATES 350 #define IDC_351 351 #define IDC_SMTP_AUTH 353 #define IDC_FILTERS_FLD 354 #define IDS_RETRY 355 #define IDS_FOLDER_PROPERTIES_COMPACT 356 #define IDC_HAS_SPAM 357 #define IDS_CALENDAR 359 #define IDS_NO_LOG 360 #define IDC_REC_TYPE 362 #define IDS_FIND 363 #define IDS_NO_ITEMS 364 #define IDS_RECEIVE_ALL_ACCOUNTS 365 #define IDS_NO_ITEMS_IN_FOLDER 366 #define IDS_PREVIEW_ON_SERVER 367 #define IDS_RECEIVE_MAIL 368 #define IDS_NO_TEMPLATES 369 #define IDC_STOP_FILTERING 370 #define IDS_NEW_EMAIL 371 #define IDS_EXIT 372 #define IDS_MARK_ALL_SEND 373 #define IDS_MARK 374 #define IDS_SELECT_MARKED 375 #define IDS_PRIORITY 376 #define IDS_HIGH 377 #define IDS_NORMAL 378 #define IDS_LOW 379 #define IDS_NEW_CONTACT 380 #define IDS_RECEIVE 381 #define IDC_LIMIT_TO 382 #define IDS_PRINT 383 #define IDS_CAL_SDAY_SUN 384 #define IDS_SAVE_CLOSE 385 #define IDS_FILTER 386 #define IDS_PREV_MSG 387 #define IDS_NEXT_MSG 388 #define IDS_CONDITIONS 390 #define IDS_ACTIONS 391 #define IDS_DETAIL 392 #define IDS_FIELDS 393 #define IDS_FILE_NAME 394 #define IDS_MIME_TYPE 395 #define IDS_PREVIEW 396 #define IDS_ERROR_FOLDERS_VERSION 397 #define IDS_ERROR_FOLDERS_STATUS 398 #define IDS_EXPORT 399 #define IDS_STATUS 400 #define IDD_STATUS 401 #define IDS_402 402 #define IDC_ACC_LIST 403 #define IDC_404 404 #define IDC_IN_FOLDER 406 #define IDS_407 407 #define IDS_408 408 #define IDC_STATUS_TXT 409 #define IDC_OP_TXT 410 #define IDS_411 411 #define IDC_EMAIL_TXT 412 #define IDC_EMAIL_PROG 413 #define IDD_MAIL_PROPERTIES 414 #define IDC_STOP 415 #define IDC_SEND_LOG 416 #define IDC_OP_PROG 417 #define IDC_SET_IN 418 #define IDS_HIDE_GNUPG 419 #define IDS_RESIZE 420 #define IDS_COPY_FOLDER 421 #define IDS_GNUPG_ATTACH_PUB_KEY 422 #define IDS_GNUPG_SIGN 423 #define IDS_GNUPG_ENCRYPT 424 #define IDS_GNUPG_DECRYPT 425 #define IDS_GNUPG_ERR_NOMSG 426 #define IDC_CLEAR_KEYWORDS 436 #define IDS_MONTH 437 #define IDC_BAYES_TABLE 439 #define IDS_HIGH_PRIORITY 440 #define IDD_ACCOUNT 441 #define IDS_443 443 #define IDS_444 444 #define IDS_445 445 #define IDC_MAIL_DESCRIPTION 448 #define IDC_SENT 449 #define IDC_RECEIVED 450 #define IDC_FORWARDED 451 #define IDC_REPLIED 452 #define IDC_HAS_ATTACH 453 #define IDC_MARKED 455 #define IDC_READY_SEND 456 #define IDC_OPEN_INSPECTOR 457 #define IDC_CREATED 459 #define IDD_NO_FOLDER 460 #define IDC_MESSAGE 462 #define IDS_470 470 #define IDC_CREATE_FOLDER 471 #define IDS_472 472 #define IDS_475 475 #define IDC_ACCOUNT 476 #define IDC_ACCOUNT_NAME 477 #define IDC_SMTP_PASSWORD 478 #define IDC_EXISTING 486 #define IDC_FOLDER_HISTORY 487 #define IDS_488 488 #define IDS_ASK_TNEF_DECODE 495 #define IDC_RECEIVE_TABLE 500 #define IDC_SEND_SSL 501 #define IDS_OK 502 #define IDC_NEW_FOLDER 503 #define IDC_EXISTING_FOLDER 504 #define IDC_BROWSE_NEW 505 #define IDC_BROWSE_EXISTING 506 #define IDS_YEAR 507 #define IDC_TEXT 508 #define IDC_SEARCH 509 #define IDS_510 510 #define IDC_BAYES_MODE 511 #define IDC_REPEATS 512 #define IDS_YEARS 513 #define IDS_CANCEL 514 #define IDS_SAVE 515 #define IDS_EMAIL 516 #define IDS_REPLY 517 #define IDS_REPLYALL 518 #define IDS_FORWARD 519 #define IDC_SIG_HTML 520 #define IDS_TO 521 #define IDC_DEFAULT_ALT 522 #define IDS_COMPACT_TESTING_DLG 523 #define IDS_FOLDER_COMPACT_ERR_DLG 524 #define IDS_COULDNT_OPEN 525 #define IDC_SEARCH_SUB 526 #define IDC_FIND_CASE 527 #define IDC_FIND_WORD 528 #define IDS_ATTACH_FILE 529 #define IDC_CTRLS_TABLE 530 #define IDC_MAIL_FIELD 531 #define IDC_CONTACT 532 #define IDM_BAYES_STATS 533 #define IDS_EDIT 534 #define IDC_ID_TABLE 535 #define IDC_CONTACT_FIELD 536 #define IDC_ACTION 537 #define IDS_REMOVE 538 #define IDM_UNIT_TESTS 539 #define IDC_LAYOUT 540 #define IDC_TABS 541 #define IDC_275 542 #define IDC_FIRST 543 #define IDS_544 544 #define IDC_OUT_FOLDER 545 #define IDC_SET_OUT 546 #define IDD_PAGE_SETUP 547 #define IDC_PREVIEW 548 #define IDS_ATTACH_WARNING_DLG 549 #define IDC_BROWSE_FONT 550 #define IDS_MAIL_PROPS_DLG 551 #define IDS_552 552 #define IDS_553 553 #define IDS_554 554 #define IDS_555 555 #define IDC_LEFT 556 #define IDC_TOP 557 #define IDC_RIGHT 558 #define IDC_BOTTOM 559 #define IDS_MAIL_PROPS 560 #define IDC_CUSTOM_FIELDS 561 #define IDS_ADD_CONTACTS 562 #define IDS_SUBFLD_NAME_CLASH 563 #define IDC_TREE 564 #define IDS_ERROR_NO_RECIPIENTS 565 #define IDC_USER_PASS_CONFIRM 566 #define IDS_SAVE_ATTACHMENT_AS 567 #define IDS_NO_CONNECTION_TO_SERVER 568 #define IDS_SERVER_ERROR_MSG 569 #define IDS_NO_SMTP_SERVER_SETTINGS 570 #define IDS_ASK_ABOUT_OPTIONS 571 #define IDS_THIS_WEEK 572 #define IDS_UPDATE_PERIOD 573 #define IDS_HEX_LOG 574 #define IDS_OPEN 575 #define IDS_BYTE_LOG 576 #define IDC_CALENDER 577 #define IDS_WINDOWS_SSL_INSTALL 578 #define IDC_KEYWORDS 579 #define IDC_PATH 580 #define IDC_UNREAD 581 #define IDC_LOCATION 582 #define IDS_ADDRESS 583 #define IDS_POPUP 584 #define IDC_PARAM_LABEL 585 #define IDC_DIR 586 #define IDS_LOW_PRIORITY 587 #define IDD_CAL 588 #define IDD_CAL_REMOTE 589 #define IDC_590 590 #define IDS_ADD_CAL 591 #define IDS_ADD_CAL_POPUP 592 #define IDS_SET_READ 593 #define IDS_SET_UNREAD 594 #define IDS_PROPERTIES 595 #define IDS_SUBJECT 596 #define IDS_OPTIONS 597 #define IDS_SMTP 598 #define IDS_SIZE 599 #define IDS_DATE 600 #define IDS_SAVEAS 601 #define IDS_EMPTY 602 #define IDS_ERROR_FOLDERS_DONT_EXIST 603 #define IDS_ERROR_CANT_OPEN_FOLDERS 604 #define IDS_ENTER_FILE_NAME_FOR_FOLDERS 605 #define IDS_SEND_MAIL 606 #define IDS_NEW 607 #define IDS_APPOINTMENT 608 #define IDS_CAL_EVENT 609 #define IDS_CONTACT 610 #define IDS_CREATESUBFOLDER 611 #define IDS_612 612 #define IDS_CAL_VIEW 613 #define IDS_MINUTES 614 #define IDS_HOUR 615 #define IDS_HOURS 616 #define IDS_DAY 617 #define IDS_DAYS 618 #define IDS_FREE 619 #define IDS_TENTATIVE 620 #define IDS_BUSY 621 #define IDS_OUT_OF_OFFICE 622 #define IDS_DOWNLOAD 623 #define IDS_READ_RECEIPT 624 #define IDS_CAL_SDAY_MON 625 #define IDS_RENAMEFOLDER 626 #define IDC_FOLDER_MSG 627 #define IDC_PASS 628 #define IDS_629 629 #define IDS_630 630 #define IDS_631 631 #define IDD_FOLDER_PROPS 632 #define IDS_FIRST 633 #define IDS_LOCAL_CONTACTS 634 #define IDC_ACCOUNT_TBL 635 #define IDS_MOVE_ERROR 636 #define IDS_MESSAGE 637 #define IDS_ACTION 638 #define IDS_MSGS_ERROR 639 #define IDS_CAL_LMONTH_OCT 640 #define IDC_ACCOUNTS_READ 641 #define IDS_CONFIGURE 642 #define IDS_ERROR_FILE_OVERWRITE 643 #define IDC_INTERNAL_FILTERING 644 #define IDC_STORE2_KEEP 645 #define IDS_ERROR_CANT_READ 646 #define IDC_FOLDER_WRITE 647 #define IDS_ERROR_FILE_EXISTS 648 #define IDD_BAYES_SETTINGS 649 #define IDC_USER_LEVEL_PSW 650 #define IDC_USER_PASS 651 #define IDS_ERROR_FOLDERS_ALREADY_EXIST 652 #define IDC_APW_USER 653 #define IDC_COMPACT_MS 654 #define IDD_FIND 655 #define IDC_DEF_SEND 656 #define IDC_TOOLBAR_TEXT 657 #define IDS_ADD 658 #define IDM_SAVEAS 659 #define IDC_URL 660 #define IDC_SUBJECT 661 #define IDD_MAIL_FIELDS 662 #define IDD_KEY 663 #define IDC_LABEL 664 #define IDS_147 665 #define IDC_SENT_DATE 666 #define IDS_148 667 #define IDC_RECEIVED_DATE 668 #define IDD_FILTER 669 #define IDC_PRIVACY_TYPE 670 #define IDS_671 671 #define IDM_NO_IDENTITIES 672 #define IDD_FILTER_ACTION 673 #define IDD_NEWFOLDER_F 674 #define IDD_FILES_IMPORT 675 #define IDS_ADD_LOCAL_CAL_FOLDER 676 #define IDC_UI_FNT_SIZE 677 #define IDC_UI_LANG 678 #define IDS_ERROR_LR8_FAILURE 679 #define IDC_REC_8BIT_CS 680 #define IDC_CLIENT 681 #define IDC_BLINK 682 #define IDC_FOLDER_READ 683 #define IDC_ACCOUNTS_WRITE 684 #define IDC_CUSTOM_TABLE 685 #define IDC_APR_USER 686 #define IDC_NAME_TABLE 687 #define IDC_USER 688 #define IDC_APR_ADMIN 689 #define IDC_APW_NONE 690 #define IDS_ADD_CAL_URL 691 #define IDC_REMINDER_VALUE 692 #define IDS_LIKE 693 #define IDC_PREVIEW_READ 694 #define IDS_ASK_USER_PASS 695 #define IDD_FILTER_SAVE_ATTACH 696 #define IDM_HELP 697 #define IDC_TYPES 698 #define IDC_OTHER_TABLE 699 #define IDC_BROWSE_DIR 700 #define IDC_ONLY_SEND_THIS 701 #define IDC_GROUPS_FLD 702 #define IDC_SET_GROUPS 703 #define IDS_CC 704 #define IDS_RECIPIENTS 705 #define IDS_TEXT 706 #define IDS_ATTACHMENTS 707 #define IDS_INTERNETHEADER 708 #define IDS_FOLDER_PROPERTIES_DLG 709 #define IDS_TOMORROW 710 #define IDS_NEXT_WEEK 711 -#define IDC_FILES 712 -#define IDC_PICK_FILES 713 +#define IDC_SRC 712 #define IDC_USAGE 714 #define IDC_START_TIME 715 #define IDS_ABOUT 716 #define IDC_COLLECT_SUB_MAIL 717 #define IDS_NO_MAIL_TO_FILTER 718 #define IDS_FORWARD_PREFIX 719 #define IDS_REPLY_PREFIX 720 #define IDS_SPAM 721 #define IDS_SEARCH 722 #define IDS_ITEM_FILTER 723 #define IDD_WEBDAV_PROPS 724 #define IDC_725 725 #define IDM_IMPORT_EML 726 #define IDC_CONTACTS_URL 727 #define IDS_DELETE_FOLDER_DLG 728 #define IDS_1035 729 #define IDS_SET_DEFAULT 730 #define IDC_HTML_FONT 731 #define IDC_SET_HTML_FONT 732 #define IDS_APPEARANCE 733 #define IDS_UPDATE 734 #define IDC_EDIT_REPLY 735 #define IDC_EDIT_FORWARD 736 #define IDC_CALENDAR_URL 737 #define IDC_APW_ADMIN 738 #define IDS_ERROR_EXE_FILE 739 #define IDC_USERNAME 740 #define IDS_FROM 741 +#define IDC_ADD 742 #define IDC_DATE_FORMAT 743 #define IDS_1034 744 #define IDS_SORT_SUBFOLDERS 745 #define IDC_DESC 746 #define IDC_948 747 #define IDS_LOADING 748 #define IDS_MOVE_FOLDER 749 #define IDC_FILTER_INDEX 750 #define IDC_FILTER_UP 751 #define IDC_FILTER_DOWN 752 #define IDM_SCRIPT_BREAK_ON_WARN 753 #define IDS_MOVE_ATTACH 754 #define IDS_ERROR_WONT_OVERWRITE_FOLDERS 755 #define IDS_NEXT_FOLDER 756 #define IDC_BAYES_READ 757 #define IDM_DELETE 758 #define IDS_SUB_FOLDER 759 #define IDS_WEEK 760 #define IDS_ERROR_READONLY_FOLDERS 761 #define IDC_TIMEZONE 762 #define IDC_LOCALTIME 763 #define IDC_HIDE_ID 764 #define IDS_SOFTWARE_UPDATE_DOWNLOAD 765 #define IDD_FILTER_SCRIPT 766 #define IDC_SCRIPT 767 #define IDS_ASK_DUPE_CONTACT 768 #define IDS_PERM_DEL_ATTACHMENTS 769 #define IDC_HTTP_PROXY 770 #define IDC_PICK_TZ 771 #define IDS_THREAD 772 #define IDS_SOFTWARE_CURRENT 773 #define IDS_TRANSLATION_AUTHORS 774 #define IDD_CSV_IMPORT 775 #define IDC_THEME 776 #define IDS_BCC 777 #define IDS_CLICK_TO_ADD 778 #define IDS_779 779 #define IDC_BROWSE_THEMES 780 #define IDD_OUTLOOK_EXPORT 781 #define IDS_SUMMARY 782 #define IDC_BROWSE_FOLDER 783 #define IDS_MERGE_TEMPLATE 784 #define IDS_MERGE_FILE 785 #define IDS_COPY 786 #define IDD_SETTINGS 787 #define IDS_DELETE_SYS_FOLDER 788 #define IDS_789 789 #define IDS_CAL_SDAY_TUE 790 #define IDS_791 791 #define IDS_792 792 #define IDS_793 793 #define IDS_794 794 #define IDS_MBOX_IMPORT 795 #define IDC_SET_READ 796 #define IDS_CAL_SDAY_WED 797 #define IDS_CAL_SDAY_THU 798 #define IDS_CAL_SDAY_FRI 799 #define IDS_CAL_SDAY_SAT 800 #define IDS_CAL_LDAY_SUN 801 #define IDS_CAL_LDAY_MON 802 #define IDS_CAL_LDAY_TUE 803 #define IDS_CAL_LDAY_WED 804 #define IDS_CAL_LDAY_THU 805 #define IDS_CAL_LDAY_FRI 806 #define IDS_CAL_LDAY_SAT 807 #define IDS_CAL_SMONTH_JAN 808 #define IDS_CAL_SMONTH_FEB 809 #define IDS_CAL_SMONTH_MAR 810 #define IDS_CAL_SMONTH_APR 811 #define IDS_CHANGED 812 #define IDS_CAL_SMONTH_MAY 813 #define IDS_CAL_SMONTH_JUN 814 #define IDS_CAL_SMONTH_JUL 815 #define IDS_CAL_SMONTH_AUG 816 #define IDS_CAL_SMONTH_SEP 817 #define IDS_CAL_SMONTH_OCT 818 #define IDS_CAL_SMONTH_NOV 819 #define IDS_CAL_SMONTH_DEC 820 #define IDS_CAL_LMONTH_JAN 821 #define IDS_EVENT_DUE 822 #define IDC_BAYES_THRESHOLD 823 #define IDS_CAL_LMONTH_FEB 824 #define IDS_CAL_LMONTH_MAR 825 #define IDS_CAL_LMONTH_APR 826 #define IDS_CAL_LMONTH_MAY 827 #define IDS_CAL_LMONTH_JUN 828 #define IDS_CAL_LMONTH_JUL 829 #define IDS_CAL_LMONTH_AUG 830 #define IDC_PREVIEW_SECONDS 831 #define IDS_CAL_LMONTH_SEP 832 #define IDC_SOCKS5_PASSWORD 833 #define IDS_CAL_LMONTH_NOV 834 #define IDS_CAL_LMONTH_DEC 835 #define IDC_RECEIVE_SSL 836 #define IDC_SEND_CHARSET1 837 #define IDC_SEND_CHARSET2 838 #define IDS_DELETE_ASK 839 #define IDS_DONT_ADJUST_TZ 840 #define IDC_DELETE_AFTER 841 #define IDC_HTML_IMAGES 842 #define IDC_AUTO_DELETE_EXE 843 #define IDD_GROUP 844 #define IDC_SEND_AUTH_TYPE 845 #define IDM_HOMEPAGE 846 #define IDC_GROUP_NAME 847 #define IDC_GROUP_LIST 848 #define IDC_START_DATE 849 #define IDC_850 850 #define IDS_TODAY 851 #define IDC_REC_ASCII_CP 852 #define IDS_FOLDER_OUTBOX 853 #define IDS_FOLDER_SENT 854 #define IDS_780 855 #define IDS_FOLDER_FILTERS 856 #define IDS_FOLDER_CONTACTS 857 #define IDS_ATTACHMENTS_NAME 858 #define IDS_ATTACHMENTS_DATA 859 #define IDS_ANYWHERE 860 #define IDS_TODO 861 #define IDS_FOLDER_TEMPLATES 862 #define IDS_FOLDER_GROUPS 863 #define IDS_FOLDER_CALENDAR 864 #define IDS_DEFAULT_CHARSET_RECEIVE 865 #define IDS_DUE 866 #define IDS_CLICK_TO_CREATE 867 #define IDC_REPEAT 868 #define IDS_1101 869 #define IDS_ERROR_ALREADY_ONLINE 870 #define IDS_NOT_LOADED 871 #define IDC_SHOW_LOCATION 872 #define IDS_ERROR_MAPI_NOT_INSTALLED 873 #define IDC_CALENDAR 874 #define IDS_PLUGINS_NO_CONF 875 #define IDC_TAB1 876 #define IDC_TAB2 877 #define IDC_CHECK_EVERY 878 #define IDS_1116 879 #define IDC_FILTER_ACTIONS 880 #define IDC_LOGIN 881 #define IDC_MSG_STORE 882 #define IDC_SRC_FOLDERS 883 #define IDC_FOLDER 884 #define IDS_ADD_ALL_CONTACTS 885 #define IDS_MAPI_LOGIN_FAILED 886 #define IDS_ACCSTAT_IDLE 887 #define IDS_BROWSE_FOLDER 888 #define IDS_3 889 #define IDS_ASK_ADMIN_PASS 890 #define IDS_LINKS 891 #define IDC_EXTRA_HEADERS 892 #define IDC_FORMAT 893 #define IDM_TABLE2 894 #define IDC_RECEIVE_AUTH_TYPE 895 #define IDS_896 896 #define IDC_SUSPECT_FOLDER 897 #define IDM_SET_UNREAD 898 #define IDS_HTML_TAGS 899 #define IDC_PROGRESS_TBL 900 #define IDS_DELETE 901 #define IDC_SET_SUSPECT_FOLDER 902 #define IDC_SEND_TABLE 903 #define IDS_838 904 #define IDC_TEMPLATE 905 #define IDS_1082 906 #define IDC_TXT_DAYS 907 #define IDC_FOLDERS 908 #define IDD_SECURITY 909 #define IDS_9 910 #define IDS_REMOVE_DUPES 911 #define IDC_SHOW_TOTALS 912 #define IDC_913 913 #define IDC_REMINDER_TYPE 914 #define IDS_ALWAYS_SHOW_REMOTE_CONTENT 915 #define IDC_TXT_SIZE 916 #define IDS_ERROR_FILE_EMPTY 917 #define IDC_REMINDER_PARAM 918 #define IDC_REMINDER_UNIT 919 #define IDD_EML_IMPORT 920 #define IDC_HOME_TABLE 921 #define IDC_APR_NONE 922 #define IDS_OPEN_CONTACT 923 #define IDS_THREAD_SELECT 924 #define IDS_THREAD_DELETE 925 #define IDS_THREAD_IGNORE 926 #define IDS_NONE 927 #define IDS_ALL 928 #define IDS_CONVERT_SELECTION 929 #define IDS_MULTIPLE_ITEMS 930 #define IDC_GROUP 931 #define IDC_MAPPING 932 #define IDS_933 933 #define IDS_934 934 #define IDC_TAB 935 #define IDC_HAS_FILTERS 936 #define IDC_MODE 937 #define IDC_MERGE 938 #define IDC_APPEND 939 #define IDC_CHECK_DEFAULT 940 #define IDC_EVERY 941 #define IDC_LOAD 942 #define IDS_ERROR_CANT_WRITE 943 #define IDS_944 944 #define IDS_SAVE_TO_OUTBOX 945 #define IDS_INC_BETA 946 #define IDS_947 947 #define IDC_TIME_TYPE 948 #define IDS_FOLDER_TRASH 949 #define IDD_FILES_EXPORT 950 #define IDS_1022 951 #define IDC_END_DATE 952 #define IDC_HAM 953 #define IDC_SPAM 954 #define IDC_FALSE_NEG 955 #define IDC_FALSE_POS 956 #define IDC_EFFICIENCY 957 #define IDS_RECEIPT_ASK 958 #define IDS_ERROR_NO_HELP 959 #define IDC_END_TIME 960 #define IDC_869 961 #define IDS_ERROR_CONNECTION 962 #define IDS_CREATE_FOLDER 963 #define IDC_ALL_DAY 964 #define IDC_DEF_REPLYALL_SETTING 965 #define IDS_DEFAULT_CHARSET_SEND 966 #define IDC_WEDNESDAY 967 #define IDS_WARN_NO_SECURITY 968 #define IDC_969 969 #define IDC_LAUNCH_HELP 970 #define IDC_THURSDAY 971 #define IDD_LANG 972 #define IDC_LANG 973 #define IDC_FRIDAY 974 #define IDS_975 975 #define IDS_976 976 #define IDD_FILTER_REPLY 977 #define IDD_FILTER_FORWARD 978 #define IDC_SATURDAY 979 #define IDC_SUNDAY 980 #define IDC_REC_PORT 981 #define IDS_INSPECT 982 #define IDS_FOLDER_INBOX 983 #define IDC_OCCURRENCES 984 #define IDC_CONFIGURE_REMOTE_CONTENT 985 #define IDC_TABLE3 986 #define IDC_BLACKLIST 987 #define IDC_ATTACHMENTS 988 #define IDS_GNUPG_ERR_NO_BOUNDARY 989 #define IDS_GNUPG_ERR_NO_MIME 990 #define IDS_ASK_FORWARD_ATTACHMENTS 991 #define IDC_ALL 992 #define IDC_MARK_REPLIED 993 #define IDC_MARK_FORWARDED 994 #define IDS_BOUNCE 995 #define IDS_REMOVE_WHITELIST 996 #define IDC_UNDELETE 997 #define IDS_ERROR_NO_CONFIG_SEND 998 #define IDS_ERROR_NO_CONFIG_RECEIVE 999 #define IDC_MAIL 1000 #define IDC_CONTACTS 1001 #define IDC_NAME 1002 #define IDC_TYPE 1003 #define IDC_EMAIL 1004 #define IDS_SEND 1005 #define IDC_REPLY_TO 1006 #define IDC_NICK 1007 #define IDM_SCRIBE_FAQ 1008 #define IDC_SPOUSE 1009 #define IDC_HAS_CAL_EVENTS 1010 #define IDC_QUOTE_STR 1011 #define IDC_WRAP_COLS 1012 #define IDS_ASK_DELETE_DUPES 1013 #define IDS_GNUPG_ERR_NO_RECIP 1014 #define IDC_START_IN 1015 #define IDC_SET_START_IN 1016 #define IDM_LAYOUT4 1017 #define IDC_DOWN 1018 #define IDM_REFRESH 1019 #define IDS_ERROR_REG_WRITE 1020 #define IDS_ERROR_NEED_INSTALL 1021 #define IDC_SMTP_SERVER 1022 #define IDC_SMTP_NAME 1023 #define IDC_REC_SERVER 1024 #define IDC_REC_NAME 1025 #define IDC_REC_PASSWORD 1026 #define IDC_REC_CHECK 1027 #define IDC_POP3_LEAVE 1028 -#define IDS_ERROR_SERVER_CONNECT 1029 +#define IDC_DST 1029 #define IDC_LAST 1030 #define IDC_QUOTE 1031 #define IDC_HOME_FAX 1032 #define IDC_HOME_STREET 1033 #define IDC_ACCOUNTS 1034 #define IDC_HOME_SUBURB 1035 #define IDC_FONT 1036 #define IDC_DESCRIPTION 1037 #define IDC_NEW_MAIL_SOUND 1038 #define IDS_MAIL_MESSAGE 1039 #define IDC_SMTP_DOMAIN 1040 #define IDC_HOME_STATE 1041 #define IDC_REPLYWITHSIG 1042 #define IDC_HOME_COUNTRY 1043 #define IDS_POP3 1044 #define IDC_HOME_PHONE 1045 #define IDC_HOME_MOBILE 1046 #define IDC_HOME_IM 1047 #define IDC_HOME_WEBPAGE 1048 #define IDC_NOTE 1049 #define IDC_DEFAULT 1050 #define IDC_TRAY 1051 #define IDS_DELETEFOLDER 1052 #define IDC_CHECKDIALUP 1053 #define IDC_SET_FONT 1054 #define IDC_POP3_ON_START 1055 #define IDC_LIST 1056 #define IDC_START_WEEK 1057 #define IDC_REFRESH 1058 #define IDS_COPY_PATH 1059 #define IDC_SET_SOUND 1060 #define IDS_OE_IMPORT 1061 #define IDS_1062 1062 #define IDM_MENU_1063 1063 #define IDC_DEL_SRC_FOLDER 1064 #define IDC_PICK_FOLDER 1065 #define IDC_WRAP 1066 #define IDC_NO_SPAM_TRASH 1067 -#define IDC_ADD 1068 +#define IDC_PICK_FILES 1068 #define IDC_MSG 1069 #define IDC_KEY 1070 #define IDC_DONT_WARN 1071 #define ID_YES 1072 #define ID_NO 1073 #define IDC_REGISTER_CLIENT 1074 #define IDC_SOCKS5_SERVER 1075 #define IDC_SOCKS5_USER 1076 #define IDC_REMINDER_ADD 1077 #define IDC_USE_TEMPLATE 1078 #define IDC_SET_FOLDER 1079 #define IDC_REMINDERS 1080 #define IDC_AVAILABLE_TYPE 1081 #define IDC_AVAILABLE 1082 #define IDS_ERROR_NO_SPACE 1083 #define IDC_TEMPLATES 1084 #define IDS_RECEIPT_BODY 1085 #define IDS_SPAM_PROB 1086 #define IDS_IS_WHITELIST 1087 #define IDC_BUSY 1088 #define IDS_ERROR_MAPI_DEFAULT 1089 #define IDS_ERROR_SOFTWARE_KEY 1090 #define IDM_MANAGE_MAIL_STORES 1091 #define IDC_PUBLIC 1092 #define IDC_PRIVATE 1093 #define IDC_NEW_FILTER_ACTION 1094 #define IDC_DELETE_FILTER_ACTION 1095 #define IDS_MAIL 1096 #define IDC_RADIO1 1097 #define IDC_RADIO2 1098 #define IDC_DEF_ACTION 1099 #define IDC_WORK_COUNTRY 1100 #define IDC_WORK_PHONE 1101 #define IDC_WORK_MOBILE 1102 #define IDC_WORK_IM 1103 #define IDC_WORK_STATE 1104 #define IDC_WORK_POSTCODE 1105 #define IDC_WORK_STREET 1106 #define IDC_WORK_FAX 1107 #define IDC_WORK_SUBURB 1108 #define IDC_HOME_POSTCODE 1109 #define IDC_COMPANY 1110 #define IDC_WORK_WEBPAGE 1111 #define IDC_SOCKS5 1112 #define IDC_DELETE 1113 #define IDS_MAIL_MERGE_Q 1114 #define IDC_ADD_SRC_FOLDER 1115 #define IDC_PROPERTIES 1116 #define IDD_OUTLOOK_ADD_FLD 1117 #define IDS_ASK_FOLDER_PASS 1118 #define IDC_DEF_SEND_TXT 1119 #define IDS_ERROR_SCRIPT_COMPILE 1120 #define IDS_ANY 1121 #define IDS_ERROR_NOTHING_TO_UPGRADE 1122 #define IDC_PAGE_ALL 1123 #define IDC_SAVE 1124 #define IDC_PAGE_RANGES 1125 #define IDS_PASSWORD_SAVED 1126 #define IDS_RESIZE_JPEG_QUAL 1127 #define IDC_TXT_MIN 1128 #define IDC_TXT_DOWNLOAD 1129 #define IDC_TXT_KBS 1130 #define IDM_SCRIPTING_CONSOLE 1131 #define IDD_PRINT_PREVIEW 1132 #define IDC_SAVE_IMG 1133 #define IDS_ACCSTAT_SETUP 1134 #define IDS_ACCSTAT_CONNECT 1135 #define IDS_ACCSTAT_TRANS 1136 #define IDS_ACCSTAT_WAIT 1137 #define IDS_ACCSTAT_DELETING 1138 #define IDS_ACCSTAT_FINISHED 1139 #define IDS_ACCSTAT_CANCELLED 1140 #define IDS_ERROR 1141 #define IDS_ENTER_KEY 1142 #define IDM_SCRIPT_DEBUG 1143 #define IDC_UPDATE 1144 #define IDC_1145 1145 #define IDS_CONTAINS 1146 #define IDS_STARTS_WITH 1147 #define IDS_ENDS_WITH 1148 #define IDS_SERVER 1149 #define IDS_1150 1150 #define IDM_MENU_1151 1151 #define IDS_TIME 1152 #define IDM_HTML_EDITOR 1153 #define IDS_ERROR_MAPI_INIT_FAILED 1154 #define IDD_CAL_RECUR 1155 #define IDS_WEEKS 1156 -#define IDC_REMOVE_FILES 1157 +#define IDC_DEL 1157 #define IDC_SHOW_EXTRA 1158 #define IDC_IMAGE 1159 #define IDC_GUEST_ENTRY 1160 #define IDC_ADD_GUEST 1161 #define IDS_MISSING_PARENT 1162 #define IDS_ORIGINAL_MESSAGE 1163 #define IDS_PORTABLE_Q 1164 #define IDC_TABLE 1165 #define IDS_NO_EVENTS 1166 #define IDS_UNREAD_MAIL 1167 #define IDS_1168 1168 #define IDC_SHOW_ADDR 1169 #define IDS_AND_OPERATOR 1170 #define IDS_OR_OPERATOR 1171 #define IDS_MEMBER_OF_GROUP 1172 #define IDS_ACTION_MOVE_FOLDER 1173 #define IDS_ACTION_SOUND 1174 #define IDS_ACTION_OPEN 1175 #define IDS_ACTION_EXECUTE 1176 #define IDS_ACTION_SET_COLOUR 1177 #define IDS_ACTION_SET_LABEL 1178 #define IDS_ACTION_EMPTY_FOLDER 1179 #define IDS_ACTION_MARK_SPAM 1180 #define IDS_ACTION_SAVE_ATTACHMENTS 1181 #define IDS_ACTION_DELETE_ATTACHMENTS 1182 #define IDS_ACTION_CREATE_FOLDER 1183 #define IDS_SELECT_FOLDER 1184 #define IDC_BAYES_INCREMENTAL 1185 #define IDC_BAYES_DEBUG 1186 #define IDC_IMG_NOTES_TBL 1187 #define IDC_WORK_TABLE 1188 #define IDC_OUTGOING 1189 #define IDS_ERROR_FILE_DOESNT_EXIST 1190 #define IDS_ERROR_FOLDER_DOESNT_EXIST 1191 #define IDS_IMPORT 1192 #define IDD_SCRIBE_EXPORT 1193 #define IDC_INCOMING 1194 #define IDM_MENU_1195 1195 #define IDC_DEST 1196 #define IDC_SET_DEST 1197 #define IDC_HAS_GROUPS 1198 #define IDC_CAL_URL 1199 #define IDC_SPAM_FLD 1200 #define IDC_SET_SPAM 1201 #define IDC_SPAM_FOLDER 1202 #define IDC_SET_SPAM_FOLDER 1203 #define IDC_PASSWORD 1204 +#define IDS_ERROR_SERVER_CONNECT 1205 #define IDS_ERROR_PRINT_FAILED 1214 #define IDS_ASK_ACCOUNT_PASSWORD 1215 #define IDS_SELECT_IO 1216 #define IDC_GUESTS 1217 #define IDS_DONT_SHOW_AGAIN 1218 #define IDS_INSTALL 1219 #define IDC_1220 1220 #define IDS_EDIT_MAIL_STORES 1224 #define IDS_ERROR_CANT_CREATE_FOLDER 1225 #define IDS_ERROR_CANT_CREATE_DB 1226 #define IDS_SHOW_CONSOLE 1227 #define IDC_STATUS 1228 #define IDS_SELECT_ACCOUNT 1229 #define IDS_1235 1235 #define IDC_MONDAY 1238 #define IDC_TUESDAY 1239 #define IDS_MONTHS 1246 #define IDC_NEVER 1249 #define IDC_1250 1250 #define IDC_AFTER 1251 #define IDC_AFTER_COUNT 1252 #define IDC_1254 1254 #define IDC_ON 1255 #define IDC_ON_DATE 1256 #define IDC_SUMMARY 1258 #define IDS_ALWAYS_SHOW 1260 #define IDS_OPEN_SOURCE 1262 #define IDS_DEFAULT 1263 #define IDS_IMPORT_SETTINGS_Q 1264 #define IDS_IMPORT_SETTINGS_DONE 1265 #define IDS_DELETE_ATTACHMENTS 1266 #define IDS_LANGUAGE 1267 #define IDS_1268 1268 #define IDS_SHOW_REMOTE_CONTENT 1274 #define IDC_DELETE_DAYS 1275 #define IDS_RESIZE_IMGS 1277 #define IDD_REMOTE_CONTENT 1279 #define IDC_WHITELIST 1282 #define IDS_REMOTE_CONTENT_MSG 1287 #define IDS_GNUPG_ERR_SAVE_FAILED 1291 #define IDS_MESSAGES_SENT 1293 #define IDS_MAIL_MERGE_EMPTY 1294 #define IDS_GNUPG_ERR_NO_OUTPUT 1295 #define IDS_FILTER_MAILINGLIST 1299 #define IDC_ADVANCED 1300 #define IDS_ACTION_COPY 1301 #define IDC_PAGE_PARTIAL 1302 #define IDD_CSV_EXPORT 1304 #define IDS_1305 1305 #define IDS_EXPORT_MSG 1312 #define IDS_MARK_ALL_READ 1313 #define IDD_MANAGE_FOLDERS 1314 #define IDC_MS_TBL 1315 #define IDC_OPEN_MS 1316 #define IDC_CLOSE_MS 1317 #define IDC_CREATE_MS 1318 #define IDC_CONVERT_MS 1320 #define IDC_REPAIR_MS 1321 #define IDC_MAIL_STORES 1324 #define IDS_1325 1325 #define IDS_1326 1326 #define IDC_MSG_ID 1327 #define IDC_FILTER 1328 #define IDS_GNUPG_ERR_WRONG_SEGS 1329 #define IDS_RELATIVE_TIMES 1333 #define IDS_1335 1335 #define IDS_CHECKING_OBJECTS 1336 #define IDS_ERROR_FOLDER_NOT_LOADED 1337 #define IDS_1338 1338 #define IDS_1339 1339 #define IDS_1340 1340 #define IDS_1341 1341 #define IDD_REPLICATE 1342 -#define IDC_SRC 1346 -#define IDC_DST 1348 #define IDM_CHECK_UPDATE 1350 #define IDS_1353 1353 #define IDS_1354 1354 #define IDS_ERROR_FONT_SETTINGS 1356 #define IDC_SEC_AUTH 1357 #define IDC_EDIT_CONTROL 1359 #define IDC_DELETE_LARGER 1360 #define IDC_DELETE_SIZE 1361 #define IDC_CLEAR 1364 #define IDS_ADD_TO_CAL 1367 #define IDS_ADD_TO_DICTIONARY 1368 #define IDS_ERROR_CANT_ADD_DICT 1370 #define IDS_SPELL_CHECK 1371 #define IDS_SPELL_DICT 1372 #define IDS_MAILSTORE_UPGRADE_Q 1373 #define IDS_ERROR_IMPORT 1374 #define IDS_ERROR_REPLICATION_FAILED 1375 #define IDS_ERROR_IMPORT_COUNT 1388 #define IDS_GNUPG_ERR_NOT_INSTALLED 1458 #define IDD_FOLDER_SELECT 1469 #define IDS_REPARSE 1471 #define IDD_FOLDER_FORMAT 1472 #define IDS_GNUPG_PSW_PROMPT 1473 #define IDC_V1 1481 #define IDC_V2 1482 #define IDC_UP 1483 #define IDS_GROWL_SUPPORT 1485 #define IDS_GROWL 1486 #define IDC_MSG_DEL_ACTION 1487 #define IDC_MSG_DEL_NEXT 1488 #define IDC_MSG_DEL_CLOSE 1489 #define IDC_MSG_DEL_PREV 1490 #define IDC_RECEIVE_LOG 1514 #define IDS_EMAIL_PROGRESS 1516 #define IDS_COPY_LOG_TO_CLIP 1517 #define IDC_SMTP_PORT 1519 #define IDS_RESIZE_MAX_PX 1522 #define IDS_RESIZING 1523 #define IDS_RESIZE_MAX_SIZE 1524 #define IDS_UPGRADE_KEY 1525 #define IDC_UPGRADE 1526 #define IDS_MAIL2_DEPRECATED 1527 #define IDS_SSL_DEBUG 1528 #define IDS_CONSOLE 1529 #define IDC_SPELL_LANG 1531 #define IDC_WIDTH 1535 #define IDC_SLIDE 1537 #define IDC_BAYES_DELETE_ON_SERVER 1541 #define IDC_BAYES_DELETE_ATTACHMENTS 1543 #define IDC_TENTATIVE 1548 #define IDS_DELETING 1549 #define IDS_DONT_SHOW_EMOJI 1550 #define IDS_HELP 1551 #define IDS_DESKTOP 1552 #define IDS_PORTABLE 1553 #define IDC_BTNS_TABLE 1556 #define IDS_GNUPG_CHECKING 1558 #define IDS_GNUPG_ERR_NO_SIGN_ENC 1561 #define IDS_GNUPG_ERR_CANT_START 1565 #define IDS_GNUPG_ERR_CODE 1568 #define IDS_GNUPG_ERR_NO_CONTENT 1570 #define IDS_GNUPG_ERR_READ_FAIL 1572 #define IDS_GNUPG_ERR_NO_MAIL 1574 #define IDS_GNUPG_ERR_NO_ROOT 1578 #define IDS_GNUPG_ERR_WRONG_MIME 1580 #define IDS_GNUPG_ERR_NO_ENC_MIME 1581 #define IDS_GNUPG_ERR_NO_ID 1582 #define IDS_GNUPG_DECRYPT_CANCEL 1583 #define IDS_GNUPG_ERR_NO_DATA 1584 #define IDS_GNUPG_ERR_DECRYPT_FAIL 1585 #define IDS_GNUPG_ERR_NO_SENDER_EMAIL 1586 #define IDS_GNUPG_ERR_NO_PRIV_KEY 1587 #define IDS_GNUPG_ERR_NO_PUB_KEY 1588 #define IDS_GNUPG_ERR_IMPORT_FAIL 1589 #define IDS_GNUPG_ERR_SIGN_ENC_CANCEL 1590 #define IDS_GNUPG_ERR_TEMP_WRITE 1591 #define IDS_GNUPG_ERR_EXPORT_TEMP 1592 #define IDS_GNUPG_ERR_NEW_ATTACH_FAIL 1593 #define IDS_GNUPG_ERR_REPARENT 1594 #define IDS_GNUPG_ERR_RECIP_NO_EMAIL 1595 #define IDS_GNUPG_ERR_WRITE 1596 #define IDS_GNUPG_ERR_ENCRYPT_FAIL 1597 #define IDS_GNUPG_ERR_ONE_OR_MORE 1598 #define IDS_GNUPG_DECRYPT_OK 1599 #define IDS_GNUPG_ENCRYPTED 1600 #define IDS_GNUPG_ENCRYPTED_AND_SIGNED 1601 #define IDS_GNUPG_SIGNED 1602 #define IDS_GNUPG_GOOD_SIG 1603 #define IDS_GNUPG_BAD_SIG 1604 #define IDS_GNUPG_SIG_MSG 1605 #define IDS_GNUPG_SIG_NO_ID 1606 #define IDS_GNUPG_ERR_INVALID_DECRYPTION 1607 #define IDS_GNUPG_CHECK_MSG 1608 #define IDS_SHOW_SCRIPT_DEBUGGER 1609 #define IDC_ACC_LOG 1613 #define IDC_1615 1615 #define IDC_1616 1616 #define IDS_SHOW_SIZE_IN_KIB 1617 #define IDS_GNUPG_GET_GPG 1619 #define IDC_START_REL 1620 #define IDC_END_REL 1621 #define IDS_YESTERDAY 1622 #define IDC_1626 1626 #define IDS_MBOX_SELECT_FOLDER 2000 #define IDS_MBOX_READING 2001 #define IDS_MBOX_EXPORT 2002 #define IDS_MBOX_EXPORT_FOLDER 2003 #define IDS_MBOX_WRITING 2004 #define IDS_STORE2_ERROR_COMPACT 2005 #define IDS_STORE2_DISCARD_FIX 2006 #define IDS_STORE2_SAVE_DEBUG 2007 #define IDS_NAME 2008 #define IDS_NO_MAIL_TO_SEND 2009 #define IDS_NO_OUTGOING_FOLDER 2010 #define IDS_ERROR_NO_NAME_EMAIL 2011 #define IDS_APPNAME 2012 #define IDS_SURNAME 2013 #define IDC_SET_CALENDER 2014 #define IDC_NEW_MAIL_NOTIFY 2015 #define IDD_NEW_MAIL_NOTIFY 2016 #define IDC_NEW_MAIL 2017 #define IDC_REMEMBER_PSW 2020 #define IDS_36 2021 #define IDS_37 2022 #define IDC_OPEN 2023 #define IDC_GLYPH_SUB 2031 #define IDC_FILTERS 2032 #define IDD_OUTLOOK_IMPORT 2040 #define IDD_WARN_DEFAULT 2041 #define IDC_POP_RECIP 2042 #define IDD_ADDCONTACT 2043 #define IDD_CONTACT 2044 #define IDD_FOLDER_NAME 2045 #define IDD_POPVIEW 2047 #define IDC_FPR_NONE 3000 #define IDC_FPR_USER 3001 #define IDC_FPR_ADMIN 3002 #define IDC_FPW_NONE 3003 #define IDC_FPW_USER 3004 #define IDC_FPW_ADMIN 3005 #define IDM_BOUNCE 40000 #define IDM_SOFTWARE_KEY 40001 #define IDM_UPGRADE_MAIL_STORES 40002 #define IDM_OPTIONS 40004 #define IDM_PRINT 40005 #define IDM_PRINTSETUP 40006 #define IDM_EXIT 40007 #define IDM_NEW_EMAIL 40008 #define IDM_REPLY 40009 #define IDM_REPLY_ALL 40010 #define IDM_FORWARD 40011 #define IDM_SEND_MAIL 40012 #define IDM_NEW_CONTACT 40014 #define IDM_ABOUT 40019 #define IDM_IMPORT_NS_CONTACTS 40020 #define IDM_IMPORT_OUTLOOK_PAB 40025 #define IDM_IMPORT_OUTLOOK_ITEMS 40027 #define IDM_NEW_FILTER 40038 #define IDM_IMPORT_TEXT_MBOX 40047 #define IDM_EXPORT_TEXT_MBOX 40048 #define IDM_IMP_MBX_EMAIL 40049 #define IDM_IMP_DBX_EMAIL 40050 #define IDM_NEW_DRAFT 40060 #define IDM_NO_TEMPLATES 40061 #define IDM_FILTER_CURRENT_FOLDER 41010 #define IDM_IMP_EUDORA_ADDR 41043 #define IDM_IMP_MOZILLA_ADDR 41045 #define IDM_FIND 41055 #define IDM_IMPORT_CSV 41065 #define IDM_WORK_OFFLINE 41075 #define IDM_EXPORT_CSV 41085 #define IDM_NEW_GROUP 41095 #define IDM_SECURITY 41105 #define IDM_BAYES_SETTINGS 41116 #define IDM_BAYES_CHECK 41117 #define IDM_BUILD_BAYES_DB 41118 #define IDM_LOGOUT 41129 #define IDM_FOLDERS_DOWN_LEFT 41142 #define IDM_PREVIEW_ALONG_BOTTOM 41143 #define IDM_LAYOUT1 41144 #define IDM_LAYOUT2 41145 #define IDM_LAYOUT3 41146 #define IDM_EXPORT_OUTLOOK_ITEMS 41147 #define IDM_CRASH 41148 #define IDM_TUTORIALS 41149 #define IDM_DEBUG_INFO 41150 #define IDM_VERSION_HISTORY 41151 #define IDM_MEMECODE 41152 #define IDM_SET_READ 41155 #define IDM_IMP_MOZILLA_MAIL 41156 #define IDM_DEBUG_FILTERS 41157 #define IDM_FILTERS_DISABLE 41158 #define IDM_DUMP_MEM 41159 #define IDM_FILTER_SELECTION 41160 #define IDM_EXPORT_SCRIBE 41161 #define IDM_TOOLS_MENU 41163 #define IDM_REPLICATE 41164 #define IDM_EXPORT 41165 #define IDM_COPY 'copy' #define IDM_CUT 'cut ' #define IDM_PASTE 'past' diff --git a/src/ImpExp_Mbox.cpp b/src/ImpExp_Mbox.cpp --- a/src/ImpExp_Mbox.cpp +++ b/src/ImpExp_Mbox.cpp @@ -1,258 +1,264 @@ #include #include #include #include #include "Scribe.h" #include "lgi/common/NetTools.h" #include "lgi/common/Edit.h" #include "lgi/common/TextLabel.h" #include "lgi/common/TextFile.h" #include "resdefs.h" #include "lgi/common/LgiRes.h" #include "lgi/common/FileSelect.h" /////////////////////////////////////////////////////////////////////////// -ChooseFolderDlg::ChooseFolderDlg +ImportExportDlg::ImportExportDlg ( ScribeWnd *parent, bool IsExport, const char *Title, const char *Msg, char *DefFolder, int FolderType, LString::Array *Files ) { Type = FolderType; SetParent(App = parent); - DestFolder = 0; Export = IsExport; - Lst = 0; if (LoadFromResource(Export ? IDD_FILES_EXPORT : IDD_FILES_IMPORT)) { Name(Title); - if (GetViewById(IDC_FOLDER, Folder)) + if (GetViewById(IDC_SRC, Src)) { - Folder->Name(DefFolder ? DefFolder : (char*)"/"); - Folder->Enabled(false); - } - - SetCtrlName(IDC_MSG, Msg); - - if (GetViewById(IDC_FILES, Lst)) - { + Src->ShowColumnHeader(false); if (Files) { for (unsigned i=0; iLength(); i++) InsertFile((*Files)[i]); } } + + if (GetViewById(IDC_DEST, Dst)) + { + Dst->Name(DefFolder ? DefFolder : (char*)"/"); + Dst->Enabled(false); + } + + SetCtrlName(IDC_MSG, Msg); } MoveToCenter(); } -void ChooseFolderDlg::InsertFile(const char *f) +void ImportExportDlg::InsertFile(const char *f) { - if (!Lst) + if (!Src) return; bool Has = false; - for (auto n : *Lst) + for (auto n : *Src) { - char Path[MAX_PATH_LEN]; - LMakePath(Path, sizeof(Path), n->GetText(0), n->GetText(1)); - if (_stricmp(Path, f) == 0) + auto path = n->GetText(0); + if (Stricmp(path, f) == 0) { Has = true; break; } } if (Has) return; - LListItem *n = new LListItem; + LListItem *n = new LListItem(f); if (!n) return; - - auto parts = LString(f).RSplit(DIR_STR, 1); - if (parts.Length() == 2) - { - n->SetText(parts[0], 0); - n->SetText(parts[1], 1); - Lst->Insert(n); - } - else delete n; + Src->Insert(n); } -int ChooseFolderDlg::OnNotify(LViewI *Ctrl, LNotification n) +int ImportExportDlg::OnNotify(LViewI *Ctrl, LNotification n) { + if (!Src || !DestFolder) + return 0; + switch (Ctrl->GetId()) { - case IDC_FILES: + case IDC_ADD: { - if (Lst && n.Type == LNotifyDeleteKey) + if (Export) + { + // Pick scribe folders to export: + auto Dlg = new FolderDlg(this, App, Type); + Dlg->DoModal([this, Dlg](auto dlg, auto ctrlId) + { + if (ctrlId) + { + auto f = Dlg->Get(); + if (f) + InsertFile(f); + } + delete dlg; + }); + } + else { - List Sel; - if (Lst->GetSelection(Sel)) + // Pick external MBOX files to import: + auto s = new LFileSelect(this); + + s->MultiSelect(true); + s->Type("All Files", LGI_ALL_FILES); + s->Type("MBOX Files", "*.mbx;*.mbox"); + s->Type("Outlook Express Folders", "*.mbx;*.dbx"); + s->Type("Mozilla Address Book", "*.mab"); + s->Type("Eudora Address Book", "NNdbase.txt"); + s->Open([this](auto dlg, auto status) { - Sel.DeleteObjects(); - } + if (status) + { + for (int i=0; iLength(); i++) + InsertFile((*dlg)[i]); + } + delete dlg; + }); } break; } - case IDC_REMOVE_FILES: + case IDC_SRC: { - if (Lst) + if (n.Type == LNotifyDeleteKey) { List Sel; - if (Lst->GetSelection(Sel)) - { + if (Src->GetSelection(Sel)) Sel.DeleteObjects(); - } - } + } + break; + } + case IDC_DEL: + { + List Sel; + if (Src->GetSelection(Sel)) + Sel.DeleteObjects(); break; } - case IDC_PICK_FILES: + case IDC_SET_DEST: { - if (!Lst) - break; - - auto s = new LFileSelect(this); - - s->MultiSelect(!Export); - s->Type("All Files", LGI_ALL_FILES); - s->Type("MBOX Files", "*.mbx;*.mbox"); - s->Type("Outlook Express Folders", "*.mbx;*.dbx"); - s->Type("Mozilla Address Book", "*.mab"); - s->Type("Eudora Address Book", "NNdbase.txt"); - s->Open([this](auto dlg, auto status) + if (Export) { - if (status) - { - if (Export) - Lst->Empty(); + // Pick external folder for writing to: + auto s = new LFileSelect(this); - for (int i=0; iLength(); i++) - InsertFile((*dlg)[i]); - } - delete dlg; - }); - break; - } - case IDC_SET_FOLDER: - { - if (!Folder) - break; - - auto Dlg = new FolderDlg(this, App, Type); - Dlg->DoModal([this, Dlg](auto dlg, auto ctrlId) + s->MultiSelect(!Export); + s->OpenFolder( [this](auto dlg, auto status) + { + if (status) + { + for (int i=0; iLength(); i++) + InsertFile((*dlg)[i]); + } + delete dlg; + }); + } + else { - if (ctrlId) + // Pick scribe folder for writing to: + auto Dlg = new FolderDlg(this, App, Type); + Dlg->DoModal([this, Dlg](auto dlg, auto ctrlId) { - auto f = Dlg->Get(); - if (f) - this->Folder->Name(f); - } - delete dlg; - }); + if (ctrlId) + { + auto f = Dlg->Get(); + if (f && Dst) + Dst->Name(f); + } + delete dlg; + }); + } break; } case IDOK: { - if (Lst) - { - for (auto n : *Lst) - { - char Path[MAX_PATH_LEN]; - LMakePath(Path, sizeof(Path), n->GetText(0), n->GetText(1)); - SrcFiles.Add(Path); - } - } - - if (Folder) - { - DestFolder = NewStr(Folder->Name()); - } + for (auto n : *Src) + SrcFiles.Add(n->GetText()); + DestFolder = Dst->Name(); + IncSubFolders = GetCtrlValue(IDC_SUB_FOLDERS) > 0; EndModal(1); break; } case IDCANCEL: { EndModal(0); break; } } return 0; } /////////////////////////////////////////////////////////////////////////// void Import_UnixMBox(ScribeWnd *Parent) { ScribeFolder *Cur = Parent->GetCurrentFolder(); LString Path; if (Cur) Path = Cur->GetPath(); - auto Dlg = new ChooseFolderDlg(Parent, false, LLoadString(IDS_MBOX_IMPORT), LLoadString(IDS_MBOX_SELECT_FOLDER), Path); - Dlg->DoModal([Dlg, Parent](auto dlg, auto ctrlId) + auto Dlg = new ImportExportDlg(Parent, false, LLoadString(IDS_MBOX_IMPORT), LLoadString(IDS_MBOX_SELECT_FOLDER), Path); + Dlg->DoModal([Dlg, Parent](auto dlg, auto ok) { - if (ctrlId && Dlg->DestFolder) + if (ok && Dlg->DestFolder) { - ScribeFolder *Folder = Parent->GetFolder(Dlg->DestFolder); + auto Folder = Parent->GetFolder(Dlg->DestFolder); if (Folder) { for (auto File: Dlg->SrcFiles) { LAutoPtr F(new LTextFile); if (F->Open(File, O_READ)) Folder->Import(Folder->AutoCast(F), sMimeMbox); } } } delete dlg; }); } void Export_UnixMBox(ScribeWnd *Parent) { ScribeFolder *Cur = Parent->GetCurrentFolder(); LString Path; if (Cur) Path = Cur->GetPath(); - auto Dlg = new ChooseFolderDlg(Parent, + auto Dlg = new ImportExportDlg(Parent, true, LLoadString(IDS_MBOX_EXPORT), LLoadString(IDS_MBOX_EXPORT_FOLDER), Path); Dlg->DoModal([Dlg, Parent](auto dlg, auto ctrlId) { if (ctrlId && Dlg->DestFolder) { ScribeFolder *Folder = Parent->GetFolder(Dlg->DestFolder); if (Folder) { for (auto File: Dlg->SrcFiles) { if (!LFileExists(File) || LgiMsg( Parent, LLoadString(IDS_ERROR_FILE_EXISTS), AppName, MB_YESNO, File.Get()) == IDYES) { LAutoPtr F(new LFile); if (F->Open(File, O_WRITE)) Folder->Export(Folder->AutoCast(F), sMimeMbox); } } } } delete dlg; }); } diff --git a/src/Imp_Eudora.cpp b/src/Imp_Eudora.cpp --- a/src/Imp_Eudora.cpp +++ b/src/Imp_Eudora.cpp @@ -1,180 +1,180 @@ #include "Scribe.h" #include "resdefs.h" #include "lgi/common/LgiRes.h" char EudoraPathKey[] = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Eudora.exe"; bool ImportEudoraAddresss(ScribeWnd *App, ScribeFolder *Folder, char *File) { if (!App || !Folder || !File) return false; LFile f(File); if (!f) return false; auto Text = f.Read(); if (!Text) return false; bool Status = false; char *Alias = 0; char *Address = 0; auto L = Text.SplitDelimit("\r\n"); for (unsigned i=0; i 2) { if (_stricmp(S[0], "alias") == 0) { DeleteArray(Alias); Alias = NewStr(S[1]); DeleteArray(Address); Address = NewStr(S[2]); } else if (_stricmp(S[0], "note") == 0) { if (Alias && Address) { Contact *c = new Contact(App); if (c) { c->App = App; // set basic information c->Set(OPT_Nick, Alias); c->Set(OPT_Email, Address); // parse through all the tags.. char *n = 0; for (char *s = strchr(L[i], '<'); s && *s; s = n) { char *Var = ++s; n = strchr(Var, '>'); if (n) { *n++ = 0; char *Val = strchr(Var, ':'); if (Val) { *Val++ = 0; #define Map(From, To) else if (_stricmp(Var, From) == 0) c->Set(To, Val) if (_stricmp(Var, "first") == 0) { c->Set(OPT_First, Val); } Map("last", OPT_Last); Map("address", OPT_HomeStreet); Map("city", OPT_HomeSuburb); Map("state", OPT_HomeState); Map("country", OPT_HomeCountry); Map("zip", OPT_HomePostcode); Map("phone", OPT_HomePhone); Map("fax", OPT_HomeFax); Map("mobile", OPT_HomeMobile); Map("web", OPT_HomeWebPage); // Map("title", ); Map("company", OPT_Company); Map("address2", OPT_WorkStreet); Map("city2", OPT_WorkSuburb); Map("country2", OPT_WorkCountry); Map("zip2", OPT_WorkPostcode); Map("phone2", OPT_WorkPhone); Map("fax2", OPT_WorkFax); Map("web2", OPT_WorkWebPage); else if (_stricmp(Var, "name") == 0) { char *Sp = strchr(Val, ' '); if (Sp) { *Sp++ = 0; c->Set(OPT_First, Val); c->Set(OPT_Last, Sp); } else { c->Set(OPT_First, Val); } } else if (_stricmp(Var, "otheremail") == 0) { } } } char *Next = strchr(n, '<'); if (!Next && strlen(n) > 0) { c->Set(OPT_Note, n); n = 0; } else n = Next; } Status |= (Folder->WriteThing(c, NULL) != Store3Error); } } } } } return Status; } void Import_EudoraAddressBook(ScribeWnd *App) { char Str[256] = "/"; #ifdef WIN32 HKEY hKey; if (RegOpenKeyA(HKEY_LOCAL_MACHINE, EudoraPathKey, &hKey) == ERROR_SUCCESS) { DWORD Type = 0; DWORD Size = sizeof(Str); RegQueryValueExA(hKey, "Path", 0, &Type, (uchar*)Str, &Size); RegCloseKey(hKey); } #endif LString::Array Files; LMakePath(Str, sizeof(Str), Str, "NNdbase.txt"); if (LFileExists(Str)) { Files.Add(Str); } // Get default path.. char DefaultFolder[256] = "/Contacts"; ScribeFolder *f = App->GetCurrentFolder(); if (!f || f->GetItemType() != MAGIC_CONTACT) { f = App->GetFolder(FOLDER_CONTACTS); } if (f && f->GetItemType() == MAGIC_CONTACT) { auto p = f->GetPath(); if (p) { strcpy_s(DefaultFolder, sizeof(DefaultFolder), p); } } // Ask user... - auto Dlg = new ChooseFolderDlg(App, + auto Dlg = new ImportExportDlg(App, false, "Eudora", LLoadString(IDS_SELECT_IO), DefaultFolder, MAGIC_CONTACT, &Files); Dlg->DoModal([App, Dlg](auto dlg, auto id) { if (id && Dlg->SrcFiles[0]) ImportEudoraAddresss(App, App->GetFolder(Dlg->DestFolder), Dlg->SrcFiles[0]); delete dlg; }); } diff --git a/src/Imp_Mozilla.cpp b/src/Imp_Mozilla.cpp --- a/src/Imp_Mozilla.cpp +++ b/src/Imp_Mozilla.cpp @@ -1,607 +1,607 @@ // #include #include "Scribe.h" #include "lgi/common/Map.h" #include "resdefs.h" #include "lgi/common/TextFile.h" #include "lgi/common/LgiRes.h" #include "lgi/common/FileSelect.h" #include "v3.6.14/sqlite3.h" void ToRecord(LMap &r, char *Str) { char *n; for (char *s=strchr(Str, '('); s && *s; s=n) { n = strchr(s, ')'); if (n) { s++; *n++ = 0; if (*s++ == '^') { int a = htoi(s); s = strchr(s, '^'); if (s) { int b = htoi(++s); r[a] = b; } } n = strchr(n, '('); } } } void ToMap(LMap &m, char *Str) { char *n; for (char *s=strchr(Str, '('); s && *s; s=n) { n = strchr(s, ')'); if (n) { s++; *n++ = 0; char *e = strchr(s, '='); if (e) { *e++ = 0; int Var = htoi(s); if (Var > 0) { m[Var] = e; } } n = strchr(n, '('); } } } char *Decode(char *s) { // static const char *Cp = "iso-8859-1"; if (s && strchr(s, '$')) { char Hex[3] = {0, 0, 0}; LStringPipe p(4 << 10); char *b = s; while (*s) { if (*s == '$') { if (b < s) { p.Push(b, (int) (s - b)); } s++; if (s[0] && s[1]) { Hex[0] = *s++; Hex[1] = *s++; char c = htoi(Hex); p.Push(&c, 1); /* if (*s == '$') { s++; if (s[0] && s[1]) { Hex[0] = *s++; Hex[1] = *s++; char16 c = c1 | (htoi(Hex) << 8); char *Utf8 = WideToUtf8(&c, sizeof(c)); if (Utf8) { p.Push(Utf8); DeleteArray(Utf8); } } } */ b = s; } else break; } else s++; } if (b < s) { p.Push(b, (int) (s - b)); } return p.NewStr(); } return NewStr(s); } struct Sqlite { bool Open; sqlite3 *Db; Sqlite(const char *File) { Open = Check(sqlite3_open(File, &Db)); } ~Sqlite() { if (Open) sqlite3_close(Db); } bool Check(int Code, const char *Sql = NULL) { if (Code == SQLITE_OK || Code == SQLITE_DONE) return true; const char *Err = sqlite3_errmsg(Db); LgiTrace("%s:%i - Sqlite error %i: %s\n%s", _FL, Code, Err, Sql?Sql:(char*)"", Sql?"\n":""); LAssert(!"Db Error"); return false; } }; bool ImportMozillaAddresss(ScribeWnd *App, ScribeFolder *Folder, char *File) { if (!App || !Folder || !File) { LgiTrace("%s:%i - Param error.\n", _FL); return false; } auto Ext = LGetExtension(File); if (!Ext) { LgiTrace("%s:%i - No extension '%s'.\n", _FL, File); return false; } bool Status = false; if (!stricmp(Ext, "mab")) { char *Text = LReadTextFile(File); if (Text) { int Angle = 0; int Square = 0; List Blocks; List Records; char *StartAngle = 0; char *StartSquare = 0; // Parse MAB... for (char *s=Text; s && *s; s++) { if (*s == '<') { if (_strnicmp(s+1, "!--", 3) == 0) { char *e = strstr(s, "-->"); if (e) s = e + 3; else break; } else { Angle++; if (Angle == 1) StartAngle = s + 1; } } else if (*s == '>') { if (Angle > 0) { if (StartAngle && Angle == 1) Blocks.Insert(NewStr(StartAngle, s - StartAngle)); Angle--; } } else if (Angle == 0) { if (*s == '[') { if (++Square == 1) StartSquare = s + 1; } else if (*s == ']') { if (StartSquare && Square == 1) Records.Insert(NewStr(StartSquare, s - StartSquare)); Square--; } } } // Parse blocks.. bool Fields = true; LMap Field; LMap Data; for (auto b: Blocks) { if (Fields) { ToMap(Field, b); Fields = false; } else { ToMap(Data, b); } } for (auto r: Records) { LMap Record; ToRecord(Record, r); int Flds = 0; Contact *c = new Contact(App); if (c) { c->App = App; #define MapField(From, To) \ { \ char *d = Data[Record[Field.Reverse((char*)From)]]; \ if (d) \ { \ char *s = Decode(d); \ if (s) \ { \ c->Set(To, s); \ Flds++; \ DeleteArray(s); \ } \ } \ } MapField("FirstName", OPT_First); MapField("LastName", OPT_Last); MapField("NickName", OPT_Nick); MapField("PrimaryEmail", OPT_Email); MapField("WorkPhone", OPT_WorkPhone); MapField("HomePhone", OPT_HomePhone); MapField("FaxNumber", OPT_HomeFax); MapField("CellularNumber", OPT_HomeMobile); MapField("HomeAddress", OPT_HomeStreet); MapField("HomeAddress2", OPT_HomeSuburb); MapField("HomeState", OPT_HomeState); MapField("HomeZipCode", OPT_HomePostcode); MapField("HomeCountry", OPT_HomeCountry); MapField("WorkAddress", OPT_WorkStreet); MapField("WorkAddress2", OPT_WorkSuburb); MapField("WorkState", OPT_WorkState); MapField("WorkZipCode", OPT_WorkPostcode); MapField("WorkCountry", OPT_WorkCountry); MapField("Company", OPT_Company); MapField("WebPage1", OPT_WorkWebPage); MapField("WebPage2", OPT_HomeWebPage); MapField("CustomFields", OPT_CustomFields); MapField("Notes", OPT_Note); // HomeCity // WorkCity // JobTitle // Department // BirthYear // BirthMonth // BirthDay // LastModifiedDate // RecordKey // AddrCharSet // LastRecordKey // ListName // ListNickName // ListDescription // ListTotalAddresses // LowercaseListName // SecondEmail // PreferMailFormat // PagerNumber #undef MapField if (Flds > 0) { Folder->WriteThing(c, NULL); Status = true; } else { DeleteObj(c); } } } } else { LgiMsg( App, "Couldn't read from '%s'\n" "Is Mozilla still open?", AppName, MB_OK, File); } } else if (!stricmp(Ext, "sqlite")) { Sqlite s(File); if (!s.Open) { LgiTrace("%s:%i - Failed to open '%s'\n", _FL, File); return false; } LString Sql = "select * from properties"; sqlite3_stmt *Stmt = NULL; if (!s.Check(sqlite3_prepare_v2(s.Db, Sql, -1, &Stmt, 0), Sql)) return false; LHashTbl, Contact*> Map; int r; while ((r = sqlite3_step(Stmt)) == SQLITE_ROW) { const char *card = (const char *)sqlite3_column_text(Stmt, 0); auto name = sqlite3_column_text(Stmt, 1); auto value = sqlite3_column_text(Stmt, 2); Contact *c = Map.Find(card); if (!c) { if ((c = new Contact(App))) Map.Add(card, c); } if (!c || !name || !value) continue; printf("%s, %s, %s\n", card, name, value); #define MapField(src, dst) \ if (!stricmp((const char*)name, src)) c->Set(dst, (char*)value) MapField("FirstName", OPT_First); MapField("LastName", OPT_Last); MapField("NickName", OPT_Nick); MapField("PrimaryEmail", OPT_Email); MapField("WorkPhone", OPT_WorkPhone); MapField("HomePhone", OPT_HomePhone); MapField("FaxNumber", OPT_HomeFax); MapField("CellularNumber", OPT_HomeMobile); MapField("HomeAddress", OPT_HomeStreet); MapField("HomeAddress2", OPT_HomeSuburb); MapField("HomeState", OPT_HomeState); MapField("HomeZipCode", OPT_HomePostcode); MapField("HomeCountry", OPT_HomeCountry); MapField("WorkAddress", OPT_WorkStreet); MapField("WorkAddress2", OPT_WorkSuburb); MapField("WorkState", OPT_WorkState); MapField("WorkZipCode", OPT_WorkPostcode); MapField("WorkCountry", OPT_WorkCountry); MapField("Company", OPT_Company); MapField("WebPage1", OPT_WorkWebPage); MapField("WebPage2", OPT_HomeWebPage); MapField("CustomFields", OPT_CustomFields); MapField("Notes", OPT_Note); #undef MapField } s.Check(sqlite3_finalize(Stmt), 0); Status = Map.Length() > 0; for (auto p: Map) Folder->WriteThing(p.value, NULL); } else { LgiTrace("%s:%i - Unsupported address book format '%s'\n", _FL, File); } return Status; } void Import_MozillaAddressBook(ScribeWnd *App) { LArray FindFiles; LArray Ext; LFile::Path Str( #ifdef LINUX LSP_HOME #else LSP_USER_APP_DATA #endif ); #ifdef LINUX Str += ".thunderbird"; #endif Ext.Add("abook.mab"); Ext.Add("abook.sqlite"); LgiTrace("Searching '%s' for thunderbird address book files.\n", Str.GetFull().Get()); LRecursiveFileSearch(Str, &Ext, &FindFiles); // Get default path.. char DefaultFolder[256] = "/Contacts"; ScribeFolder *f = App->GetCurrentFolder(); if (!f || f->GetItemType() != MAGIC_CONTACT) { f = App->GetFolder(FOLDER_CONTACTS); } if (f && f->GetItemType() == MAGIC_CONTACT) { auto p = f->GetPath(); if (p) strcpy_s(DefaultFolder, sizeof(DefaultFolder), p); } LString::Array Files; for (auto f: FindFiles) Files.Add(f); FindFiles.DeleteArrays(); // Ask user... - auto Dlg = new ChooseFolderDlg( App, + auto Dlg = new ImportExportDlg( App, false, AppName, "Select input files and destination directory", DefaultFolder, MAGIC_CONTACT, &Files); Dlg->DoModal([App, Dlg](auto dlg, auto id) { if (id && Dlg->SrcFiles[0]) ImportMozillaAddresss(App, App->GetFolder(Dlg->DestFolder), Dlg->SrcFiles[0]); delete dlg; }); } #ifndef WIN32 int GetPrivateProfileStringA(const char *lpAppName, const char *lpKeyName, const char *lpDefault, char *lpReturnedString, int nSize, const char *lpFileName) { // FIXME LAssert(0); return 0; } #endif void Import_MozillaMail(ScribeWnd *App) { char Path[MAX_PATH_LEN]; if (!LGetSystemPath(LSP_USER_APP_DATA, Path, sizeof(Path))) return; LMakePath(Path, sizeof(Path), Path, "Thunderbird\\profiles.ini"); if (!LFileExists(Path)) { LgiMsg(App, LLoadString(IDS_ERROR_FILE_DOESNT_EXIST), AppName, MB_OK, Path); return; } char s[128]; if (GetPrivateProfileStringA("Profile0", "Path", "", s, sizeof(s), Path) <= 0) return; if (LIsRelativePath(s)) { LTrimDir(Path); LMakePath(Path, sizeof(Path), Path, s); } else { strcpy_s(Path, sizeof(Path), s); } LMakePath(Path, sizeof(Path), Path, "Mail"); if (!LDirExists(Path)) { LgiMsg(App, LLoadString(IDS_ERROR_FOLDER_DOESNT_EXIST), AppName, MB_OK, Path); return; } LArray FindFiles; if (!LRecursiveFileSearch(Path, 0, &FindFiles)) return; // Clear out index files... for (unsigned i=0; iGetCurrentFolder(); LString CurPath; if (Cur) CurPath = Cur->GetPath(); - auto Dlg = new ChooseFolderDlg(App, + auto Dlg = new ImportExportDlg(App, false, "Mozilla/Thunderbird", LLoadString(IDS_IMPORT), CurPath, MAGIC_MAIL, &Files); Dlg->DoModal([App, Dlg](auto dlg, auto id) { if (id && Dlg->DestFolder) { ScribeFolder *Dest = App->GetFolder(Dlg->DestFolder); if (Dest) { for (auto Src: Dlg->SrcFiles) { char *Name = strrchr(Src, DIR_CHAR); if (!Name) Name = Src; else Name++; ScribeFolder *Child = Dest->CreateSubDirectory(Name, MAGIC_MAIL); if (Child) { LAutoPtr f(new LTextFile); if (f->Open(Src, O_READ)) { Child->Import(Child->AutoCast(f), sMimeMbox); } } } Dest->Expanded(true); } else LgiMsg(App, LLoadString(IDS_ERROR_FOLDER_DOESNT_EXIST), AppName, MB_OK, Dlg->DestFolder.Get()); } delete dlg; }); } diff --git a/src/Imp_OutlookExpress.cpp b/src/Imp_OutlookExpress.cpp --- a/src/Imp_OutlookExpress.cpp +++ b/src/Imp_OutlookExpress.cpp @@ -1,719 +1,719 @@ /* ** FILE: Imp_OutlookExpress.cpp ** AUTHOR: Matthew Allen ** DATE: 4/2/2000 ** DESCRIPTION: Scribe importer ** ** Copyright (C) 2000, Matthew Allen ** fret@memecode.com */ #include "Scribe.h" #include "resdefs.h" #include "lgi/common/ProgressDlg.h" #include "lgi/common/LgiRes.h" #include "lgi/common/FileSelect.h" bool ImportMBX(ScribeWnd *Parent, ScribeFolder *ParentFolder, char *FileName) { LFile F; if (FileName && ParentFolder && Parent && F.Open(FileName, O_READ)) { char Magic[4] = {'J', 'M', 'F', '6'}; char Buf[4]; if (F.Read(Buf, 4) == 4 && memcmp(Magic, Buf, 4) == 0) { int32 Msgs; F.Seek(4, SEEK_CUR); F >> Msgs; // Create folder for these messages char n[256]; strcpy_s(n, sizeof(n), FileName); char *Ls = 0; // last slash for (Ls = n+strlen(n)-1; Ls > n; Ls--) { if (*Ls == DIR_CHAR) { Ls++; break; } } char *Dot = strchr(Ls, '.'); if (Dot) { *Dot = 0; } ScribeFolder *Folder = ParentFolder->CreateSubDirectory(Ls, MAGIC_MAIL); if (Folder) { // Skip unknown feilds F.Seek(4 + 4 + 1 + 63, SEEK_CUR); // Read the messages for (int i=0; i> MsgId; int MsgNum = 0; F >> MsgNum; int MsgTotalSize = 0; F >> MsgTotalSize; int MsgTextSize; F >> MsgTextSize; // Create item Mail *m = dynamic_cast(Parent->CreateItem(MAGIC_MAIL, Folder, false)); if (m) { LMemStream Text(&F, 0, MsgTextSize); // Decode email into fields m->OnAfterReceive(&Text); m->SetFlags(MAIL_RECEIVED | MAIL_READ | ((m->HasAttachments()) ? MAIL_ATTACHMENTS : 0) ); } // Seek to the next message F.Seek(MsgStartPos+MsgTotalSize, SEEK_SET); } } else { return false; } return true; } } return false; } #ifdef WIN32 #pragma pack(push, 1) #endif class DbxMessagePtr { public: uint MessagePos; uint TablePos; char Reserved[4]; }; // #define DbxTableSize 0x27c class DbxTable { public: // Header /* uint FilePos; char Reserved1[13]; uint Msgs; char Reserved2[3]; */ uint FilePos; char Reserved1[4]; uint ListPtr; uint NextPtr; char Reserved2[8]; }; class DbxMsgTable { public: uint FilePos; uint MessageLength; char Reserved[17]; uint FirstBlockPos; }; class DbxMsgBlock { public: /* uint FilePos; uint Flags; uint BlockSize; uint NextBlockPos; */ uint FilePos; uint Increase; uint Include; uint Next; uint UseNet; }; #ifdef WIN32 #pragma pack(pop) #endif class ImportDBX { private: ScribeWnd *Parent = NULL; ScribeFolder *ParentFolder = NULL; LFile F; uint64 FileSize = 0; List Used; class MsgInfo { public: int Pos; bool IsNews; MsgInfo(int p, bool n) { Pos = p; IsNews = n; } }; List MsgList; bool IsUsed(ssize_t i) { for (auto n: Used) { if (*n == i) return true; } return false; } void SetUsed(ssize_t i) { if (!IsUsed(i)) { Used.Insert(new ssize_t(i)); } } bool ReadMessage(ssize_t Pos, bool IsNews, ScribeFolder *Folder) { bool Status = false; if (!IsUsed(Pos) && F.Seek(Pos, SEEK_SET)) { LTempStream Text(ScribeTempPath()); SetUsed(Pos); while (Pos > 0) { DbxMsgBlock Msg; ZeroObj(Msg); F.Read(&Msg, sizeof(Msg)-4); if (Pos != Msg.FilePos) { break; } Pos += sizeof(Msg)-4; // int Next = Pos + Msg.Next; auto End = Pos + Msg.Include; char Buf[1024]; while (Pos < End) { ssize_t R = F.Read(Buf, MIN(End-Pos, sizeof(Buf))); Text.Write(Buf, R); Pos += R; } F.Seek(Msg.Next, SEEK_SET); Pos = Msg.Next; } Mail *m = new Mail(Parent); if (m) { m->App = Parent; m->OnAfterReceive(&Text); m->SetFlags(MAIL_RECEIVED | MAIL_READ | // ((MsgTable.Reserved[3] == 3) ? MAIL_READ : 0) | (m->GetAttachments(0) ? MAIL_ATTACHMENTS : 0)); m->SetDirty(); m->Save(Folder); Status = true; } } return Status; } bool ReadMsgHeader(int Pos) { bool Status = false; if (!IsUsed(Pos) && F.Seek(Pos, SEEK_SET)) { SetUsed(Pos); DbxMsgBlock Msg; F.Read(&Msg, sizeof(Msg)); if (Msg.FilePos == Pos) { Pos += sizeof(Msg); int32 Self, MsgPos = 0, NewsPost = false, Count = 0; do { F.Read(&Self, sizeof(Self)); if ((Self & 0xff) == 0x84) { if (!MsgPos) { MsgPos = Self >> 8; } } if ((Self & 0xff) == 0x83) { NewsPost = true; } Count++; } while ((Self & 0x7f) > 0); if (MsgPos) { MsgList.Insert(new MsgInfo(MsgPos, NewsPost != 0)); Status |= true; } else { F.Read(&Self, sizeof(Self)); F.Read(&MsgPos, sizeof(MsgPos)); MsgList.Insert(new MsgInfo(MsgPos, NewsPost != 0)); Status |= true; } } } return Status; } bool ReadTable(ssize_t Pos) { bool Status = false; if (Pos > 0 && (uint64)Pos < FileSize && !IsUsed(Pos)) { DbxTable Tbl; F.Seek(Pos, SEEK_SET); SetUsed(Pos); F.Read(&Tbl, sizeof(Tbl)); if (Tbl.FilePos == Pos) { Pos += sizeof(Tbl); Status |= ReadTable(Tbl.NextPtr); Status |= ReadTable(Tbl.ListPtr); F.Seek(Pos, SEEK_SET); while (true) { bool Actioned = false; DbxMessagePtr Node; F.Seek(Pos, SEEK_SET); Pos += F.Read(&Node, sizeof(Node)); if (Node.MessagePos > 0 && Node.MessagePos < FileSize && Node.MessagePos != Node.TablePos) { Actioned |= ReadMsgHeader(Node.MessagePos); } if (Node.TablePos > 0 && Node.TablePos < FileSize && Node.TablePos != Node.MessagePos) { Actioned |= ReadTable(Node.TablePos); } if (Actioned) { Status = true; } else break; } } } return Status; } void Clean() { Used.DeleteObjects(); MsgList.DeleteObjects(); } public: ImportDBX(ScribeWnd *parent) { Parent = parent; ParentFolder = 0; } ~ImportDBX() { Clean(); } bool Import(ScribeFolder *parentFolder, char *FileName) { Clean(); ParentFolder = parentFolder; if (sizeof(DbxTable) != 24) { // packing is screwed LgiMsg(Parent, "Compiled object 'DbxTable' is the wrong size", AppName, MB_OK); return false; } LProgressDlg Dlg(Parent); Dlg.SetDescription("Reading tables..."); Dlg.SetRange(1); LProgressPane *Import = Dlg.Push(); if (Import) Import->SetDescription("Importing messages..."); if (Parent && ParentFolder && FileName && F.Open(FileName, O_READ)) { FileSize = F.GetSize(); // Create folder for these messages char n[256]; strcpy_s(n, sizeof(n), FileName); char *Ls = strrchr(n, DIR_CHAR); // last slash if (Ls) { Ls++; char *Dot = strchr(Ls, '.'); if (Dot) { *Dot = 0; } // Make sure the path is unique, no overwriting previous folders LStringPipe NewPath(256); auto ParentPath = ParentFolder->GetPath(); NewPath.Print("/%s/%s", ParentPath.Get(), Ls); auto BaseNewPath = NewPath.NewLStr(); LString NewPathStr = BaseNewPath.Get(); while (Parent->GetFolder(NewPathStr)) { char *Num = NewPathStr.Get() + BaseNewPath.Length() - 1; for (; Num>NewPathStr && IsDigit(*Num); Num--) ; if (!IsDigit(*Num)) Num++; int i = atoi(Num); NewPath.Print("%s%i", BaseNewPath.Get(), i+1); NewPathStr = NewPath.NewLStr(); } // Create output folder ScribeFolder *Folder = ParentFolder->CreateSubDirectory(Ls, MAGIC_MAIL); if (Folder) { // Read header F.Seek(0x30, SEEK_SET); uint TableLoc; F >> TableLoc; if (!TableLoc) TableLoc = 0x1e254; ReadTable(TableLoc); // Import msg list Dlg.Value(1); if (Import) { Import->SetRange(MsgList.Length()); Import->SetType("Mail"); } for (auto i: MsgList) { ReadMessage(i->Pos, i->IsNews, Folder); if (Import) Import->Value(Import->Value()+1); } /* List Msgs; while (Tbl) { int Pos = F.GetPosition(); F.Read(Tbl, DbxTableSize); if (Tbl->FilePos == Pos) { for (int i=0; iMsgs; i++) { Msgs.Insert(new int(Tbl->Msg[i].MessagePos)); } } if (Tbl->Msgs < 76 || Tbl->FilePos != Pos) { DeleteArray(Tbl); } } */ // Read in messages /* for (int *MsgPos = Msgs.First(); MsgPos; MsgPos = Msgs.Next()) { bool Read = true; DbxMsgTable MsgTable; F.Seek(*MsgPos, SEEK_SET); F.Read(&MsgTable, sizeof(MsgTable)); if (MsgTable.FilePos == *MsgPos) { // Read in blocks GBytePipe Msg; DbxMsgBlock MsgBlock; MsgBlock.NextBlockPos = MsgTable.FirstBlockPos; while (MsgBlock.NextBlockPos) { int SeekTo = MsgBlock.NextBlockPos & 0xFFFFFF; F.Seek(SeekTo, SEEK_SET); F.Read(&MsgBlock, sizeof(MsgBlock)); if (MsgBlock.FilePos == SeekTo) { uchar *Data = new uchar[MsgBlock.BlockSize]; if (Data) { F.Read(Data, MsgBlock.BlockSize); Msg.Push(Data, MsgBlock.BlockSize); DeleteArray(Data); } } } // Msg contains email... int Size = Msg.Sizeof(); if (Size > 0) { Mail *m = dynamic_cast(Parent->CreateItem(MAGIC_MAIL, Folder, false)); if (m) { m->Text = new char[Size+1]; if (m->Text) { // Read data into item Msg.Pop((uchar*) m->Text, Size); m->Text[Size] = 0; // Decode email into fields m->OnAfterReceive(); m->SetFlags(MAIL_RECEIVED | ((MsgTable.Reserved[3] == 3) ? MAIL_READ : 0) | ((m->Store->GetChild() != 0) ? MAIL_ATTACHMENTS : 0)); m->StoreDirty = true; } } } } } */ return true; } } } return false; } }; struct ImportOe : public LProgressDlg { ScribeWnd *App; bool v5; int Imported = 0, TotalFiles = 0; LString Dir; LArray Files; LArray Ext; LString::Array FileArr; ScribeFolder *Folder = NULL; ImportOe(ScribeWnd *app, bool ver5) : App(app), v5(ver5), LProgressDlg(app) { // Get the base directory Dir = LGetSystemPath(LSP_LOCAL_APP_DATA); if (!Dir) // Just in case Dir = LGetSystemPath(LSP_OS); // Search for the folder files Ext.Add(v5 ? "*.dbx" : "*.mbx"); DoFileSearch(); if (Files.Length() == 0) { if (LgiMsg( App, "%i outlook express data files found in:\n" "%s\n" "Do you want to select a different directory to search?", AppName, MB_YESNO, (int)Files.Length(), Dir.Get()) == IDYES) { auto Select = new LFileSelect(App); Select->OpenFolder([this](auto s, auto ok) { if (ok) { Dir = s->Name(); DoFileSearch(); ProcessFiles(); } delete s; }); } } else ProcessFiles(); } void DoFileSearch() { LRecursiveFileSearch(Dir, &Ext, &Files); } void ProcessFiles() { if (Files.Length() == 0) return; // Strip files for (unsigned i=0; iGetCurrentFolder(); LString CurrentPath; if (Current) CurrentPath = Current->GetPath(); - auto Dlg = new ChooseFolderDlg( App, + auto Dlg = new ImportExportDlg( App, false, AppName, LLoadString(IDS_OE_IMPORT), CurrentPath, MAGIC_MAIL, &FileArr); Dlg->DoModal([this, Dlg](auto dlg, auto id) { LAutoPtr mem(dlg); if (!id) return; ScribeFolder *Folder = App->GetFolder(Dlg->DestFolder); if (!Folder) { LgiMsg(App, "Error locating that folder.", AppName, MB_OK); return; } SetDescription("Importing folders..."); SetRange(FileArr.Length()); FileArr = Dlg->SrcFiles; TotalFiles = (int)FileArr.Length(); SetPulse(100); // Start processing... each timeout allows the message loop to run a bit }); }; void OnFinished() { LgiMsg( App, "%i of %i %s files imported successfully.", AppName, MB_OK, Imported, TotalFiles, (v5) ? (char*)"DBX" : (char*)"MBX"); delete this; } void OnPulse() { if (FileArr.Length()) { auto FileName = FileArr.Last(); FileArr.PopLast(); if (v5) { ImportDBX Filter(App); if (Filter.Import(Folder, FileName)) Imported++; } else { if (ImportMBX(App, Folder, FileName)) Imported++; } (*this)++; // inc progress bar... } else OnFinished(); } }; void Import_OutlookExpress(ScribeWnd *Parent, bool v5) { new ImportOe(Parent, v5); } diff --git a/src/Scribe.h b/src/Scribe.h --- a/src/Scribe.h +++ b/src/Scribe.h @@ -1,2582 +1,2583 @@ /*hdr ** FILE: Scribe.h ** AUTHOR: Matthew Allen ** DATE: 22/10/97 ** DESCRIPTION: Scribe email application ** ** Copyright (C) 1998-2003 Matthew Allen ** fret@memecode.com */ // Includes #include #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/DateTime.h" #include "lgi/common/Password.h" #include "lgi/common/vCard-vCal.h" #include "lgi/common/WordStore.h" #include "lgi/common/SharedMemory.h" #include "lgi/common/XmlTreeUi.h" #include "lgi/common/Mime.h" #include "lgi/common/OptionsFile.h" #include "lgi/common/TextLog.h" #include "lgi/common/Menu.h" #include "lgi/common/ToolBar.h" #include "lgi/common/Combo.h" #include "lgi/common/Printer.h" // Gui controls #include "lgi/common/Panel.h" #include "lgi/common/DocView.h" #include "lgi/common/List.h" #include "lgi/common/Tree.h" #include "lgi/common/ListItemCheckBox.h" // Storage #include "lgi/common/Store3.h" // App Includes #include "ScribeInc.h" #include "ScribeUtils.h" #include "ScribeDefs.h" #include "DomType.h" class ListAddr; // The field definition type struct ItemFieldDef { const char *DisplayText; ScribeDomType Dom; LVariantType Type; int FieldId; // Was 'Id' int CtrlId; const char *Option; bool UtcConvert; }; //////////////////////////////////////////////////////////////////////////////////////////// // Classes class MailTree; class LMailStore; class ScribeWnd; class Thing; class Mail; class Contact; class ThingUi; class MailUi; class ScribeFolder; class ContactUi; class FolderPropertiesDlg; class ScribeAccount; class Filter; class Attachment; class Calendar; class CalendarSource; class AttachmentList; struct ItemFieldDef; class FolderDlg; class Filter; class ContactGroup; class ScribeBehaviour; class AccountletThread; class ThingList; class LSpellCheck; //////////////////////////////////////////////////////////////////////// // Scripting support #include "lgi/common/Scripting.h" /// Script callback types. See 'api.html' in the Scripts folder for more details. enum LScriptCallbackType { LCallbackNull, LToolsMenu, LThingContextMenu, LThingUiToolbar, LApplicationToolbar, LMailOnBeforeSend, // "OnBeforeMailSend" LMailOnAfterReceive, LBeforeInstallBar, LInstallComponent, LFolderContextMenu, LOnTimer, LRenderMail, LOnLoad }; struct LScript; struct LScriptCallback { LScriptCallbackType Type = LCallbackNull; LScript *Script = NULL; LFunctionInfo *Func = NULL; int Param = 0; double fParam = 0.0; LVariant Data; uint64 PrevTs = 0; bool OnSecond = false; }; struct LScript { LAutoPtr Code; LArray Callbacks; }; typedef void (*ConsoleClosingCallback)(class LScriptConsole *Console, void *user_data); class LScriptConsole : public LWindow { ScribeWnd *App; LTextLog *Txt; ConsoleClosingCallback Callback; void *CallbackData; bool OnViewKey(LView *v, LKey &k); public: LScriptConsole(ScribeWnd *app, ConsoleClosingCallback callback, void *callback_data); ~LScriptConsole(); void Write(const char *s, int64 Len); bool OnRequestClose(bool OsShuttingDown); }; /// This class is a wrapper around a user interface element used for /// Scripting. The script engine needs to be able to store information /// pertaining to the menu item's callbacks along with the sub menu. class LScriptUi : public LDom { public: LScriptUi *Parent; LSubMenu *Sub; LToolBar *Toolbar; LArray Callbacks; LArray Subs; LScriptUi() { Parent = 0; Sub = 0; Toolbar = 0; } LScriptUi(LSubMenu *s) { Parent = 0; Toolbar = 0; Sub = s; } LScriptUi(LToolBar *t) { Parent = 0; Toolbar = t; Sub = 0; } ~LScriptUi() { Subs.DeleteObjects(); } bool GetVariant(const char *Name, LVariant &Value, const char *Arr = NULL) override { if (Sub) return Sub->GetVariant(Name, Value, Arr); LDomProperty Method = LStringToDomProp(Name); if (Method == ObjLength) { if (Toolbar) Value = (int64)Toolbar->Length(); } else return false; return true; } bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override { if (Sub) return Sub->CallMethod(MethodName, ReturnValue, Args); return false; } bool SetupCallbacks(ScribeWnd *App, ThingUi *Parent, Thing *t, LScriptCallbackType Type); bool ExecuteCallbacks(ScribeWnd *App, ThingUi *Parent, Thing *t, int Cmd); }; class LScribeScript : public LScriptContext { LScriptEngine *Eng; struct LScribeScriptPriv *d; public: ScribeWnd *App; static LScribeScript *Inst; LScribeScript(ScribeWnd *app); ~LScribeScript(); void ShowScriptingWindow(bool show); LAutoString GetDataFolder() override; LStream *GetLog() override; LHostFunc *GetCommands() override; LString GetIncludeFile(const char *FileName) override; // System void SetEngine(LScriptEngine *eng); bool MsgBox(LScriptArguments &Args); // Paths bool GetSystemPath(LScriptArguments &Args); bool GetScribeTempPath(LScriptArguments &Args); bool JoinPath(LScriptArguments &Args); // Folders bool GetFolder(LScriptArguments &Args); bool GetSourceFolders(LScriptArguments &Args); bool CreateSubFolder(LScriptArguments &Args); bool LoadFolder(LScriptArguments &Args); bool FolderSelect(LScriptArguments &Args); bool BrowseFolder(LScriptArguments &Args); // Things bool CreateThing(LScriptArguments &Args); bool MoveThing(LScriptArguments &Args); bool SaveThing(LScriptArguments &Args); bool DeleteThing(LScriptArguments &Args); bool ShowThingWindow(LScriptArguments &Args); bool FilterDoActions(LScriptArguments &Args); bool LookupContact(LScriptArguments &Args); // Callbacks bool AddToolsMenuItem(LScriptArguments &Args); bool AddCallback(LScriptArguments &Args); // UI bool MenuAddItem(LScriptArguments &Args); bool MenuAddSubmenu(LScriptArguments &Args); bool ToolbarAddItem(LScriptArguments &Args); }; //////////////////////////////////////////////////////////////////////// class ScribePassword { class ScribePasswordPrivate *d; public: ScribePassword(LOptionsFile *p, const char *opt, int check, int pwd, int confirm); ~ScribePassword(); bool IsOk(); bool Load(LView *dlg); bool Save(); void OnNotify(LViewI *c, LNotification &n); }; -class ChooseFolderDlg : public LDialog +class ImportExportDlg : public LDialog { - ScribeWnd *App; - LEdit *Folder; - int Type; - bool Export; - LList *Lst; + ScribeWnd *App = NULL; + int Type = 0; + bool Export = false; + LList *Src = NULL; + LEdit *Dst = NULL; void InsertFile(const char *f); public: LString DestFolder; LString::Array SrcFiles; + bool IncSubFolders = false; - ChooseFolderDlg + ImportExportDlg ( ScribeWnd *parent, bool IsExport, const char *Title, const char *Msg, char *DefFolder = NULL, int FolderType = MAGIC_MAIL, LString::Array *Files = NULL ); int OnNotify(LViewI *Ctrl, LNotification n); }; #define IoProgressImplArgs LAutoPtr stream, const char *mimeType, IoProgressCallback cb #define IoProgressFnArgs IoProgressImplArgs = NULL #define IoProgressError(err) \ { \ IoProgress p(Store3Error, err); \ if (cb) cb(&p, stream); \ return p; \ } #define IoProgressSuccess() \ { \ IoProgress p(Store3Success); \ if (cb) cb(&p, stream); \ return p; \ } #define IoProgressNotImpl() \ { \ IoProgress p(Store3NotImpl); \ if (cb) cb(&p, stream); \ return p; \ } class ScribeClass ThingType : public LDom, public LDataUserI { bool Dirty = false; bool WillDirty = true; bool Loaded = false; protected: bool OnError(const char *File, int Line) { _lgi_assert(false, "Object Missing", File, Line); return false; } // Callbacks struct ThingEventInfo { const char *File = NULL; int Line = 0; std::function Callback; }; LArray OnLoadCallbacks; public: #ifdef _DEBUG bool _debug = false; #endif struct IoProgress; typedef std::function IoProgressCallback; struct IoProgress { // This is the main result to look at: // Store3NotImpl - typically means the mime type is wrong. // Store3Error - an error occured. // Store3Delayed - means the operation will take a long time. // However progress is report via 'prog' if not NULL. // And the 'onComplete' handler will be called at the end. // Store3Success - the operation successfully completed. Store3Status status = Store3NotImpl; // Optional progress for the operation. Really only relevant for // status == Store3Delayed. Progress *prog = NULL; // Optional error message for the operation. Relevant if // status == Store3Error. LString errMsg; IoProgress(Store3Status s, const char *err = NULL) { status = s; if (err) errMsg = err; } operator bool() { return status > Store3Error; } }; template LAutoPtr AutoCast(LAutoPtr ap) { return LAutoPtr(ap.Release()); } static LArray DirtyThings; ScribeWnd *App = NULL; ThingType(); virtual ~ThingType(); virtual Store3ItemTypes Type() { return MAGIC_NONE; } bool GetDirty() { return Dirty; } virtual bool SetDirty(bool b = true); void SetWillDirty(bool c) { WillDirty = c; } virtual bool Save(ScribeFolder *Into) { return false; } virtual void OnProperties(int Tab = -1) {} virtual ScribeFolder *GetFolder() = 0; virtual Store3Status SetFolder(ScribeFolder *f, int Param = -1) = 0; virtual bool IsPlaceHolder() { return false; } // Events void WhenLoaded(const char *file, int line, std::function Callback, int index = -1); bool IsLoaded(int Set = -1); // Printing virtual void OnPrintHeaders(struct ScribePrintContext &Context) { LAssert(!"Impl me."); } virtual void OnPrintText(ScribePrintContext &Context, LPrintPageRanges &Pages) { LAssert(!"Impl me."); } virtual int OnPrintHtml(ScribePrintContext &Context, LPrintPageRanges &Pages, LSurface *RenderedHtml) { LAssert(!"Impl me."); return 0; } }; class MailContainerIter; class ScribeClass MailContainer { friend class MailContainerIter; List Iters; public: virtual ~MailContainer(); virtual size_t Length() { return 0; } virtual ssize_t IndexOf(Mail *m) { return -1; } virtual Mail *operator [](size_t i) { return NULL; } }; class ScribeClass MailContainerIter { friend class MailContainer; protected: MailContainer *Container; public: MailContainerIter(); ~MailContainerIter(); void SetContainer(MailContainer *c); }; class ScribeClass ThingStorage // External storage information { public: int Data; ThingStorage() { Data = 0; } virtual ~ThingStorage() {} }; class ThingUi; class ScribeClass Thing : public ThingType, public LListItem, public LDragDropSource, public LRefCount { friend class ScribeWnd; friend class ScribeFolder; ScribeFolder *_ParentFolder = NULL; protected: LArray FieldArray; LAutoString DropFileName; // This structure allows the app to move objects between // mail stores. After a delayed write to the new mail store // the old item needs to be removed. This keeps track of // where that old item is. Don't assume that the Obj pointer // is valid... check in the folder's items first. struct ThingReference { LString Path; Thing *Obj; ThingReference() { Obj = NULL; } } DeleteOnAdd; public: ThingStorage *Data = NULL; Thing(ScribeWnd *app, LDataI *object = 0); ~Thing(); // Dom bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override; // D'n'd bool GetData(LArray &Data) override; bool GetFormats(LDragFormats &Formats) override; bool OnBeginDrag(LMouse &m) override; // Import / Export virtual bool GetFormats(bool Export, LString::Array &MimeTypes) { return false; } // Anything implementing 2 functions should mostly be using one of these // to "return" an IoProgress and process any callback: // IoProgressError(msg) // IoProgressNotImpl() // IoProgressSuccess() virtual IoProgress Import(IoProgressFnArgs) = 0; virtual IoProgress Export(IoProgressFnArgs) = 0; /// This exports all the selected items void ExportAll(LViewI *Parent, const char *ExportMimeType, std::function Callback); // UI bool OnKey(LKey &k) override; // Thing ScribeFolder *GetFolder() override { return _ParentFolder; } void SetParentFolder(ScribeFolder *f); Store3Status SetFolder(ScribeFolder *f, int Param = -1) override; LDataI *DefaultObject(LDataI *arg = 0); virtual ThingUi *DoUI(MailContainer *c = 0) { return NULL; } virtual ThingUi *GetUI() { return 0; } virtual bool SetUI(ThingUi *ui = 0) { return false; } virtual uint32_t GetFlags() { return 0; } virtual void OnCreate() override; virtual bool OnDelete(); virtual int *GetDefaultFields() { return 0; } virtual const char *GetFieldText(int Field) { return 0; } virtual void DoContextMenu(LMouse &m, LView *Parent = 0) {} virtual Thing &operator =(Thing &c) { LAssert(0); return *this; } virtual char *GetDropFileName() = 0; virtual bool GetDropFiles(LString::Array &Files) { return false; } virtual void OnSerialize(bool Write) {} virtual void Reparse(); // Interfaces virtual Mail *IsMail() { return 0; } virtual Contact *IsContact() { return 0; } virtual ContactGroup *IsGroup() { return 0; } virtual Filter *IsFilter() { return 0; } virtual Attachment *IsAttachment() { return 0; } virtual Calendar *IsCalendar() { return 0; } void SetFieldArray(LArray &i) { FieldArray = i; } void OnMove(); bool SetField(int Field, int n); bool SetField(int Field, double n); bool SetField(int Field, char *n); bool SetField(int Field, LDateTime &n); bool SetDateField(int Field, LVariant &v); bool GetField(int Field, int &n); bool GetField(int Field, double &n); bool GetField(int Field, const char *&n); bool GetField(int Field, LDateTime &n); bool GetDateField(int Field, LVariant &v); bool DeleteField(int Field); }; class ThingUi : public LWindow { friend class MailUiGpg; bool _Dirty; char *_Name; protected: Thing *_Item; bool _Running; void SetItem(Thing *i) { _Item = i; } public: ScribeWnd *App; static LArray All; ThingUi(Thing *item, const char *name); ~ThingUi(); virtual bool SetDirty(bool d, bool ui = true); bool IsDirty() { return _Dirty; } bool OnRequestClose(bool OsShuttingDown); bool OnViewKey(LView *v, LKey &k); virtual void OnDirty(bool Dirty) {} virtual void OnLoad() = 0; virtual void OnSave() = 0; virtual void OnChange() {} virtual AttachmentList *GetAttachments() { return 0; } virtual bool AddRecipient(AddressDescriptor *Addr) { return false; } }; class ThingFilter { public: virtual bool TestThing(Thing *Thing) = 0; }; class ScribeClass Attachment : public Thing { friend class Mail; protected: Mail *Msg; Mail *Owner; bool IsResizing; LString Buf; // D'n'd LAutoString DropSourceFile; bool GetFormats(LDragFormats &Formats) override; bool GetData(LArray &Data) override; void _New(LDataI *object); public: enum Encoding { OCTET_STREAM, PLAIN_TEXT, BASE64, QUOTED_PRINTABLE, }; Attachment(ScribeWnd *App, Attachment *import = 0); Attachment(ScribeWnd *App, LDataI *object, const char *import = 0); ~Attachment(); bool ImportFile(const char *FileName); bool ImportStream(const char *FileName, const char *MimeType, LAutoStreamI Stream); Thing &operator =(Thing &c) override; LDATA_INT64_PROP(Size, FIELD_SIZE); LDATA_STR_PROP(Name, FIELD_NAME); LDATA_STR_PROP(MimeType, FIELD_MIME_TYPE); LDATA_STR_PROP(ContentId, FIELD_CONTENT_ID); LDATA_STR_PROP(Charset, FIELD_CHARSET); LDATA_STR_PROP(InternetHeaders, FIELD_INTERNET_HEADER); // LDom support bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool CallMethod(const char *MethodName, LVariant *Ret, LArray &Args) override; void OnOpen(LView *Parent, char *Dest = 0); void OnDeleteAttachment(LView *Parent, bool Ask); void OnSaveAs(LView *Parent); void OnMouseClick(LMouse &m) override; bool OnKey(LKey &k) override; Store3ItemTypes Type() override { return MAGIC_ATTACHMENT; } bool Get(char **ptr, ssize_t *size); bool Set(char *ptr, ssize_t size); bool Set(LAutoStreamI Stream); Attachment *IsAttachment() override { return this; } LAutoString MakeFileName(); bool GetIsResizing(); void SetIsResizing(bool b); bool IsMailMessage(); bool IsVCalendar(); bool IsVCard(); // The owner is the mail that this is attached to Mail *GetOwner() { return Owner; } void SetOwner(Mail *msg); // The msg is the mail that this message/rfc822 attachment is rendered into Mail *GetMsg(); void SetMsg(Mail *m); LStreamI *GotoObject(const char *file, int line); IoProgress Import(IoProgressFnArgs) override { return Store3Error; } IoProgress Export(IoProgressFnArgs) override { return Store3Error; } bool SaveTo(char *FileName, bool Quite = false, LView *Parent = 0); const char *GetText(int i) override; char *GetDropFileName() override; bool GetDropFiles(LString::Array &Files) override; }; class ScribeClass Contact : public Thing { friend class ContactUi; protected: class ContactPriv *d = NULL; ContactUi *Ui = NULL; public: static List Everyone; static Contact *LookupEmail(const char *Email); static LHashTbl, int> PropMap; static int DefaultContactFields[]; Contact(ScribeWnd *app, LDataI *object = 0); ~Contact(); LDATA_STR_PROP(First, FIELD_FIRST_NAME); LDATA_STR_PROP(Last, FIELD_LAST_NAME); LDATA_STR_PROP(Email, FIELD_EMAIL); bool Get(const char *Opt, const char *&Value); bool Set(const char *Opt, const char *Value); bool Get(const char *Opt, int &Value); bool Set(const char *Opt, int Value); // operators Thing &operator =(Thing &c) override; Contact *IsContact() override { return this; } // Dom bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0) override; bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0) override; bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override; // Events void OnMouseClick(LMouse &m) override; // Printing void OnPrintHeaders(struct ScribePrintContext &Context) override; void OnPrintText(ScribePrintContext &Context, LPrintPageRanges &Pages) override; // Misc Store3ItemTypes Type() override { return MAGIC_CONTACT; } ThingUi *DoUI(MailContainer *c = 0) override; int Compare(LListItem *Arg, ssize_t Field) override; bool IsAssociatedWith(char *PluginName); char *GetLocalTime(const char *TimeZone = 0); // Email address int GetAddrCount(); LString::Array GetEmails(); LString GetAddrAt(int i); bool HasEmail(LString email); // Serialization bool Save(ScribeFolder *Into = 0) override; // ListItem const char *GetText(int i) override; int *GetDefaultFields() override; const char *GetFieldText(int Field) override; int GetImage(int Flags = 0) override { return ICON_CONTACT; } // Import/Export bool GetFormats(bool Export, LString::Array &MimeTypes) override; IoProgress Import(IoProgressFnArgs) override; IoProgress Export(IoProgressFnArgs) override; char *GetDropFileName() override; bool GetDropFiles(LString::Array &Files) override; }; #define ContactGroupObj "ContactGroup" #define ContactGroupName "Name" #define ContactGroupList "List" #define ContactGroupDateModified "DateModified" extern ItemFieldDef GroupFieldDefs[]; class ContactGroup : public Thing { friend class GroupUi; class GroupUi *Ui; class ContactGroupPrivate *d; LString DateCache; public: LDateTime UsedTs; LDATA_STR_PROP(Name, FIELD_GROUP_NAME); ContactGroup(ScribeWnd *app, LDataI *object = 0); ~ContactGroup(); // operators Thing &operator =(Thing &c) override; ContactGroup *IsGroup() override { return this; } // Dom bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override; // Events void OnMouseClick(LMouse &m) override; void OnSerialize(bool Write) override; // Misc Store3ItemTypes Type() override { return MAGIC_GROUP; } ThingUi *DoUI(MailContainer *c = 0) override; int Compare(LListItem *Arg, ssize_t Field) override; bool GetAddresses(List &a); LString::Array GetAddresses(); // Serialization bool Save(ScribeFolder *Into = 0) override; // ListItem const char *GetText(int i) override; int *GetDefaultFields() override; const char *GetFieldText(int Field) override; int GetImage(int Flags = 0) override { return ICON_CONTACT_GROUP; } // Import / Export char *GetDropFileName() override; bool GetDropFiles(LString::Array &Files) override; bool GetFormats(bool Export, LString::Array &MimeTypes) override; IoProgress Import(IoProgressFnArgs) override; IoProgress Export(IoProgressFnArgs) override; }; struct LGroupMapArray : public LArray { LString toString() { LString::Array a; for (auto i: *this) a.Add(i->GetName()); return LString(",").Join(a); } }; class LGroupMap : public LHashTbl,LGroupMapArray*> { ScribeWnd *App; void Index(ContactGroup *grp); public: LGroupMap(ScribeWnd *app); ~LGroupMap(); }; ///////////////////////////////////////////////////////////// // Mail threading // // See: http://www.jwz.org/doc/threading.html struct ThingSortParams { int SortAscend; int SortField; }; class MContainer { int Lines; int Depth; bool Open; bool Next; void Dump(LStream &s, int Depth = 0); public: typedef LHashTbl, MContainer*> ContainerHash; // Container data Mail *Message; MContainer *Parent; LArray Children; List Refs; int Index; // Cache data LString::Array RefCache; // Debug #ifdef _DEBUG LAutoString MsgId; #endif // Methods MContainer(const char *Id, Mail *m = 0); ~MContainer(); void SetMail(Mail *m); Mail *GetTop(); bool HasChild(MContainer *m); void AddChild(MContainer *m); void RemoveChild(MContainer *m); int CountMessages(); void Pour(int &index, int depth, int tree, bool next, ThingSortParams *folder); void OnPaint(LSurface *pDC, LRect &r, LItemColumn *c, LColour Fore, LColour Back, LFont *Font, const char *Txt); static void Prune(int &ParentIndex, LArray &L); static void Thread(List &In, LArray &Out); }; extern int ListItemCompare(LListItem *a, LListItem *b, NativeInt Data); extern int ContainerIndexer(Thing *a, Thing *b, NativeInt Data); extern int GetFolderVersion(const char *Path); extern bool CreateMailHeaders(ScribeWnd *App, LStream &Out, LDataI *Mail, MailProtocol *Protocol); extern void Base36(char *Out, uint64 In); //////////////////////////////////////////////////////////// // Thing sorting // The old way extern int ContainerCompare(MContainer **a, MContainer **b); extern int ThingCompare(Thing *a, Thing *b, NativeInt Data); // The new way extern int ContainerSorter(MContainer *&a, MContainer *&b, ThingSortParams *Params); extern int ThingSorter(Thing *a, Thing *b, ThingSortParams *Data); ///////////////////////////////////////////////////////////// class MailViewOwner : public LCapabilityTarget { public: virtual LDocView *GetDoc(const char *MimeType) = 0; virtual bool SetDoc(LDocView *v, const char *MimeType) = 0; }; ///////////////////////////////////////////////////////////// // The core mail object struct MailPrintContext; class ScribeClass Mail : public Thing, public MailContainer, public LDefaultDocumentEnv { friend class MailUi; friend class MailPropDlg; friend class ScribeFolder; friend class Attachment; friend class ScribeWnd; private: class MailPrivate *d; static LHashTbl,Mail*> MessageIdMap; // List item preview int PreviewCacheX; List PreviewCache; int64_t TotalSizeCache; int64_t FlagsCache; MailUi *Ui; int Cursor; // Stores the cursor position in reply/forward format until the UI needs it Attachment *ParentFile; List Attachments; Mail *PreviousMail; // the mail we are replying to / forwarding void _New(); void _Delete(); bool _GetListItems(List &l, bool All); // All=false is just the selected items void SetListRead(bool Read); void SetFlagsCache(int64_t NewFlags, bool IgnoreReceipt, bool UpdateScreen); // LDocumentEnv impl List Actions; bool OnNavigate(LDocView *Parent, const char *Uri) override; bool AppendItems(LSubMenu *Menu, const char *Param, int Base = 1000) override; bool OnMenu(LDocView *View, int Id, void *Context) override; LoadType GetContent(LoadJob *&j) override; public: static bool PreviewLines; static bool AdjustDateTz; static int DefaultMailFields[]; static bool RunMailPipes; static List NewMailLst; constexpr static float MarkColourMix = 0.9f; uint8_t SendAttempts; enum NewEmailState { NewEmailNone, NewEmailLoading, NewEmailFilter, NewEmailBayes, NewEmailGrowl, NewEmailTray }; NewEmailState NewEmail; Mail(ScribeWnd *app, LDataI *object = 0); ~Mail(); bool SetObject(LDataI *o, bool InDataDestuctor, const char *File, int Line) override; LDATA_STR_PROP(Label, FIELD_LABEL); LDATA_STR_PROP(FwdMsgId, FIELD_FWD_MSG_ID); LDATA_STR_PROP(BounceMsgId, FIELD_BOUNCE_MSG_ID); LDATA_STR_PROP(Subject, FIELD_SUBJECT); LDATA_STR_PROP(Body, FIELD_TEXT); LDATA_STR_PROP(BodyCharset, FIELD_CHARSET); LDATA_STR_PROP(Html, FIELD_ALTERNATE_HTML); LDATA_STR_PROP(HtmlCharset, FIELD_HTML_CHARSET); LDATA_STR_PROP(InternetHeader, FIELD_INTERNET_HEADER); LDATA_INT_TYPE_PROP(EmailPriority, Priority, FIELD_PRIORITY, MAIL_PRIORITY_NORMAL); LDATA_INT32_PROP(AccountId, FIELD_ACCOUNT_ID); LDATA_INT64_PROP(MarkColour, FIELD_COLOUR); LDATA_STR_PROP(References, FIELD_REFERENCES); LDATA_DATE_PROP(DateReceived, FIELD_DATE_RECEIVED); LDATA_DATE_PROP(DateSent, FIELD_DATE_SENT); LDATA_INT_TYPE_PROP(Store3State, Loaded, FIELD_LOADED, Store3Loaded); LVariant GetServerUid(); bool SetServerUid(LVariant &v); const char *GetFromStr(int id) { LDataPropI *From = GetObject() ? GetObject()->GetObj(FIELD_FROM) : 0; return From ? From->GetStr(id) : NULL; } LDataPropI *GetFrom() { return GetObject() ? GetObject()->GetObj(FIELD_FROM) : 0; } LDataPropI *GetReply() { return GetObject() ? GetObject()->GetObj(FIELD_REPLY) : 0; } LDataIt GetTo() { return GetObject() ? GetObject()->GetList(FIELD_TO) : 0; } bool GetAttachmentObjs(LArray &Objs); LDataI *GetFileAttachPoint(); // Operators Mail *IsMail() override { return this; } Thing &operator =(Thing &c) override; OsView Handle(); ThingUi *GetUI() override; bool SetUI(ThingUi *ui) override; // References and ID's MContainer *Container; const char *GetMessageId(bool Create = false); bool SetMessageId(const char *val); LAutoString GetThreadIndex(int TruncateChars = 0); static Mail *GetMailFromId(const char *Id); bool MailMessageIdMap(bool Add = true); bool GetReferences(LString::Array &Ids); void GetThread(List &Thread); LString GetMailRef(); bool ResizeImage(Attachment *a); // MailContainer size_t Length() override; ssize_t IndexOf(Mail *m) override; Mail *operator [](size_t i) override; // Dom bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override; // Events void OnCreate() override; bool OnBeforeSend(struct ScribeEnvelope *Out); void OnAfterSend(); bool OnBeforeReceive(); bool OnAfterReceive(LStreamI *Msg); void OnMouseClick(LMouse &m) override; void OnProperties(int Tab = -1) override; void OnInspect(); void OnReply(Mail *m, bool All, bool MarkOriginal); bool OnForward(Mail *m, bool MarkOriginal, int WithAttachments = -1); bool OnBounce(Mail *m, bool MarkOriginal, int WithAttachments = -1); void OnReceipt(Mail *m); int OnNotify(LViewI *Ctrl, LNotification n) override; // Printing void OnPrintHeaders(ScribePrintContext &Context) override; void OnPrintText(ScribePrintContext &Context, LPrintPageRanges &Pages) override; int OnPrintHtml(ScribePrintContext &Context, LPrintPageRanges &Pages, LSurface *RenderedHtml) override; // Misc uint32_t GetFlags() override; void SetFlags(ulong i, bool IgnoreReceipt = false, bool Update = true); void DeleteAsSpam(LView *View); const char *GetFieldText(int Field) override; LAutoString GetCharSet(); char *GetNewText(int Max = 64 << 10, const char *AsCp = "utf-8"); int *GetDefaultFields() override; Store3ItemTypes Type() override { return MAGIC_MAIL; } void DoContextMenu(LMouse &m, LView *Parent = 0) override; int Compare(LListItem *Arg, ssize_t Field) override; char *GetDropFileName() override; bool GetDropFiles(LString::Array &Files) override; LAutoString GetSig(bool HtmlVersion, ScribeAccount *Account = 0); bool LoadFromFile(char *File); void PrepSend(); void NewRecipient(char *Email, char *Name = 0); void ClearCachedItems(); bool Send(bool Now); void CreateMailHeaders(); bool AddCalendarEvent(LViewI *Parent, bool AddPopupReminder, LString *Msg); LArray GetCalendarAttachments(); void Reparse() override; // UI LDocView *CreateView(MailViewOwner *Owner, LString MimeType, bool Sunken, size_t MaxBytes, bool NoEdit = false); ThingUi *DoUI(MailContainer *c = 0) override; // Alt HTML bool HasAlternateHtml(Attachment **Attach = 0); char *GetAlternateHtml(List *Refs = 0); // dynamically allocated ptr bool WriteAlternateHtml(char *File = NULL, int FileLen = 0); // defaults to TEMP dir // Account stuff void ProcessTextForResponse(Mail *From, LOptionsFile *Options, ScribeAccount *Account); void WrapAndQuote(LStringPipe &Pipe, const char *QuoteStr, int WrapAt = -1); ScribeAccount *GetAccountSentTo(); // Access int64 TotalSizeof(); bool Save(ScribeFolder *Into = 0) override; // Attachments Attachment *AttachFile(LView *Parent, const char *FileName); bool AttachFile(Attachment *File); bool DeleteAttachment(Attachment *File); LArray GetAttachments(); bool GetAttachments(List *Attachments); bool HasAttachments() { return Attachments.Length() > 0; } bool UnloadAttachments(); // Import / Export bool GetFormats(bool Export, LString::Array &MimeTypes) override; IoProgress Import(IoProgressFnArgs) override; IoProgress Export(IoProgressFnArgs) override; // ListItem void Update() override; const char *GetText(int i) override; int GetImage(int SelFlags = 0) override; void OnMeasure(LPoint *Info) override; void OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c) override; void OnPaint(LItem::ItemPaintCtx &Ctx) override; }; inline const char *toString(Mail::NewEmailState s) { #define _(s) case Mail::s: return #s; switch (s) { _(NewEmailNone) _(NewEmailLoading) _(NewEmailFilter) _(NewEmailBayes) _(NewEmailGrowl) _(NewEmailTray) } #undef _ LAssert(0); return "#invalidNewEmailState"; } // this is where the items reside // and it forms a leaf on the mail box tree // to the user it looks like a folder class ScribeClass ScribeFolder : public ThingType, public LTreeItem, public LDragDropSource, public MailContainer { friend class MailTree; friend class FolderPropertiesDlg; friend class ThingList; friend class ScribeWnd; friend class MoveToState; protected: class ScribeFolderPriv *d; ThingList *View(); LString DropFileName; LString GetDropFileName(); // UI cache LAutoString NameCache; int ChildUnRead = 0; LTreeItem *LoadOnDemand = NULL; LAutoPtr Loading; LArray FieldArray; void SerializeFieldWidths(bool Write = false); void EmptyFieldList(); void SetLoadFolder(Thing *t) { if (t) t->SetParentFolder(this); } bool HasFieldId(int Id); void ContinueLoading(int OldUnread, std::function Callback); // Tree item stuff void _PourText(LPoint &Size) override; void _PaintText(LItem::ItemPaintCtx &Ctx) override; int _UnreadChildren(); void UpdateOsUnread(); // Debugging state enum FolderState { FldState_Idle, FldState_Loading, FldState_Populating, } CurState = FldState_Idle; public: List Items; ScribeFolder(); ~ScribeFolder(); // Object LArray &GetFieldArray() { return FieldArray; } LDataFolderI *GetFldObj() { return dynamic_cast(GetObject()); } bool SetObject(LDataI *o, bool InDataDestuctor, const char *File, int Line) override; // ThingType Store3ItemTypes Type() override { return GetObject() ? (Store3ItemTypes)GetObject()->Type() : MAGIC_NONE; } ScribeFolder *GetFolder() override { return dynamic_cast(LTreeItem::GetParent()); } ScribeFolder *GetChildFolder() { return dynamic_cast(LTreeItem::GetChild()); } ScribeFolder *GetNextFolder() { return dynamic_cast(LTreeItem::GetNext()); } Store3Status SetFolder(ScribeFolder *f, int Param = -1) override; ScribeFolder *IsFolder() { return this; } Store3Status CopyTo(ScribeFolder *NewParent, int NewIndex = -1); // MailContainer size_t Length() override; ssize_t IndexOf(Mail *m) override; Mail *operator [](size_t i) override; /// Update the unread count void OnUpdateUnRead ( /// Increments the count, or zero if a child folder is changing. int Offset, /// Re-scan the folder bool ScanItems ); // Methods LDATA_INT32_PROP(UnRead, FIELD_UNREAD); LDATA_INT_TYPE_PROP(Store3ItemTypes, ItemType, FIELD_FOLDER_TYPE, MAGIC_MAIL); LDATA_INT32_PROP(Open, FIELD_FOLDER_OPEN); LDATA_INT32_PROP(SortIndex, FIELD_FOLDER_INDEX); LDATA_INT64_PROP(Items, FIELD_FOLDER_ITEMS); // Cached item count LDATA_INT_TYPE_PROP(ScribePerm, ReadAccess, FIELD_FOLDER_PERM_READ, PermRequireNone); LDATA_INT_TYPE_PROP(ScribePerm, WriteAccess, FIELD_FOLDER_PERM_WRITE, PermRequireNone); LDATA_ENUM_PROP(SystemFolderType, FIELD_SYSTEM_FOLDER, Store3SystemFolder); void SetSort(int Col, bool Ascend, bool CanDirty = true); int GetSortAscend() { return GetObject()->GetInt(FIELD_SORT) > 0; } int GetSortCol() { return abs((int)GetObject()->GetInt(FIELD_SORT)) - 1; } int GetSortField(); void ReSort(); bool Save(ScribeFolder *Into = 0) override; bool ReindexField(int OldIndex, int NewIndex); void CollectSubFolderMail(ScribeFolder *To = 0); bool InsertThing(Thing *Item); void MoveTo(LArray &Items, bool CopyOnly, std::function&)> Callback = NULL); bool Delete(LArray &Items, bool ToTrash); void SetDefaultFields(bool Force = false); bool Thread(); ScribePerm GetFolderPerms(ScribeAccessType Access); void SetFolderPerms(LView *Parent, ScribeAccessType Access, ScribePerm Perm, std::function Callback); bool GetThreaded(); void SetThreaded(bool t); // void Update(); void GetMessageById(const char *Id, std::function Callback); void SetLoadOnDemand(); void SortSubfolders(); void DoContextMenu(LMouse &m); void OnItemType(); bool IsInTrash(); bool SortItems(); /// /// These methods can be used in a synchronous or asynchronous manner: /// sync: Call with 'Callback=NULL' and use the return value. /// If the function needs to show a dialog (like to get permissions from /// the user) then it'll return Store3Delayed immediately. /// async: Call with a valid callback, and the method will possibly wait /// for the user and then either return Store3Error or Store3Success. Store3Status LoadThings(LViewI *Parent = NULL, std::function Callback = NULL); Store3Status WriteThing(Thing *t, std::function Callback = NULL); Store3Status DeleteThing(Thing *t, std::function Callback = NULL); Store3Status DeleteAllThings( std::function Callback = NULL); bool LoadFolders(); bool UnloadThings(); bool IsWriteable() { return true; } bool IsPublicFolders() { return false; } void OnProperties(int Tab = -1) override; ScribeFolder *CreateSubDirectory(const char *Name, int Type); void OnRename(char *NewName); void OnDelete(); LString GetPath(); ScribeFolder *GetSubFolder(const char *Path); void Populate(ThingList *List); bool CanHaveSubFolders(Store3ItemTypes Type = MAGIC_MAIL) { return GetItemType() != MAGIC_ANY; } void OnRethread(); // Name void SetName(const char *Name, bool Encode); LString GetName(bool Decode); // Tree Item const char *GetText(int i=0) override; int GetImage(int Flags = 0) override; void OnExpand(bool b) override; bool OnKey(LKey &k) override; void Update() override; // Drag'n'drop bool GetFormats(LDragFormats &Formats) override; bool OnBeginDrag(LMouse &m) override; void OnEndData() override; bool GetData(LArray &Data) override; void OnReceiveFiles(LArray &Files); // Import/Export bool GetFormats(bool Export, LString::Array &MimeTypes); IoProgress Import(IoProgressFnArgs); IoProgress Export(IoProgressFnArgs); void ExportAsync(LAutoPtr f, const char *MimeType, std::function Callback = NULL); const char *GetStorageMimeType(); // Dom bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override; }; ////////////////////////////////////////////////////////////// class Filter; class FilterCondition { protected: bool TestData(Filter *F, LVariant &v, LStream *Log); public: // Data LAutoString Source; // Data Source (used to be "int Field") LAutoString Value; // Constant char Op; uint8_t Not; // Methods FilterCondition(); bool Set(class LXmlTag *t); // Test condition against email bool Test(Filter *F, Mail *m, LStream *Log); FilterCondition &operator =(FilterCondition &c); // Object ThingUi *DoUI(MailContainer *c = 0); }; class FilterAction : public LListItem, public LDataPropI { LCombo *TypeCbo; LEdit *ArgEdit; LButton *Btn; public: // Data FilterActionTypes Type; LString Arg1; // Methods FilterAction(LDataStoreI *Store); ~FilterAction(); bool Set(LXmlTag *t); bool Get(LXmlTag *t); bool Do(Filter *F, ScribeWnd *App, Mail *&m, LStream *log); void Browse(ScribeWnd *App, LView *Parent); void DescribeHtml(Filter *Flt, LStream &s); LDataPropI &operator =(LDataPropI &p); // List item const char *GetText(int Col = 0); void OnMeasure(LPoint *Info); bool Select(); void Select(bool b); void OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c); int OnNotify(LViewI *c, LNotification n); // Object ThingUi *DoUI(MailContainer *c = 0); // bool Serialize(ObjProperties &f, bool Write); }; class ScribeClass Filter : public Thing { friend class FilterUi; protected: class FilterUi *Ui; class FilterPrivate *d; static int MaxIndex; // Ui LListItemCheckBox *ChkIncoming; LListItemCheckBox *ChkOutgoing; LListItemCheckBox *ChkInternal; bool IgnoreCheckEvents; // Current Mail **Current; // XML I/O LAutoPtr ConditionsCache; LAutoPtr Parse(bool Actions); // Methods bool EvaluateTree(LXmlTag *n, Mail *m, bool &Stop, LStream *Log); bool EvaluateXml(Mail *m, bool &Stop, LStream *Log); public: Filter(ScribeWnd *app, LDataI *object = 0); ~Filter(); LDATA_STR_PROP(Name, FIELD_FILTER_NAME); LDATA_STR_PROP(ConditionsXml, FIELD_FILTER_CONDITIONS_XML); LDATA_STR_PROP(ActionsXml, FIELD_FILTER_ACTIONS_XML); LDATA_STR_PROP(Script, FIELD_FILTER_SCRIPT); LDATA_INT32_PROP(Index, FIELD_FILTER_INDEX); LDATA_INT32_PROP(StopFiltering, FIELD_STOP_FILTERING); LDATA_INT32_PROP(Incoming, FIELD_FILTER_INCOMING); LDATA_INT32_PROP(Outgoing, FIELD_FILTER_OUTGOING); LDATA_INT32_PROP(Internal, FIELD_FILTER_INTERNAL); int Compare(LListItem *Arg, ssize_t Field) override; Thing &operator =(Thing &c) override; Filter *IsFilter() override { return this; } static void Reindex(ScribeFolder *Folder); int *GetDefaultFields() override; const char *GetFieldText(int Field) override; // Methods void Empty(); LAutoString DescribeHtml(); // Import / Export char *GetDropFileName() override; bool GetDropFiles(LString::Array &Files) override; bool GetFormats(bool Export, LString::Array &MimeTypes) override; IoProgress Import(IoProgressFnArgs) override; IoProgress Export(IoProgressFnArgs) override; // Dom bool Evaluate(char *s, LVariant &v); bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override; // Filter bool Test(Mail *m, bool &Stop, LStream *Log = 0); bool DoActions(Mail *&m, bool &Stop, LStream *Log = 0); Mail *GetCurrent() { return Current?*Current:0; } /// This filters all the mail in 'Email'. Anything that is handled by a filter /// is removed from the list, leaving just the unfiltered mail. static int ApplyFilters ( /// [In] The window of the filtering caller LView *Parent, /// [In] List of all the filter to test and/or apply List &Filters, /// [In/Out] The email to filter. After the call anything that has been /// acted on by a filter will be removed from the list. List &Email ); // Object Store3ItemTypes Type() override { return MAGIC_FILTER; } ThingUi *DoUI(MailContainer *c = 0) override; bool Serialize(LFile &f, bool Write); bool Save(ScribeFolder *Into = 0) override; void OnMouseClick(LMouse &m) override; void AddAction(FilterAction *a); // List item const char *GetText(int i) override; int GetImage(int Flags) override; void OnPaint(ItemPaintCtx &Ctx) override; void OnColumnNotify(int Col, int64 Data) override; // Index Filter *GetFilterAt(int Index); }; ////////////////////////////////////////////////////////////////////// class Accountlet; enum AccountThreadState { ThreadIdle, ThreadSetup, ThreadConnecting, ThreadTransfer, ThreadWaiting, ThreadDeleting, ThreadDone, ThreadCancel, ThreadError }; enum ReceiveAction { MailNoop, MailDelete, MailDownloadAndDelete, MailDownload, MailUpload, MailHeaders, }; enum ReceiveStatus { MailReceivedNone, MailReceivedWaiting, // This has been given to the main thread MailReceivedOk, // and one of "Ok" or "Error" has to be MailReceivedError, // set to continue. MailReceivedMax, }; ScribeFunc const char *AccountThreadStateName(AccountThreadState i); ScribeFunc const char *ReceiveActionName(ReceiveAction i); ScribeFunc const char *ReceiveStatusName(ReceiveStatus i); class LScribeMime : public LMime { public: LScribeMime() : LMime(ScribeTempPath()) { } }; class LMimeStream : public LTempStream, public LScribeMime { public: LMimeStream(); bool Parse(); }; class MailTransferEvent { public: // Message LAutoPtr Rfc822Msg; ReceiveAction Action = MailNoop; ReceiveStatus Status = MailReceivedNone; int Index = 0; bool Explicit = false; LString Uid; int64 Size = 0; int64 StartWait = 0; // Header Listing class AccountMessage *Msg = NULL; LList *GetList(); // Sending ScribeEnvelope *Send = NULL; LString OutgoingHeaders; // Other class Accountlet *Account = NULL; MailTransferEvent() { } ~MailTransferEvent() { #ifndef LGI_STATIC // LStackTrace("%p::~MailTransferEvent\n", this); #endif } }; #define RoProp(Type, Name) \ protected: \ Type Name; \ public: \ Type Get##Name() { return Name; } class AccountThread : public LThread, public LCancel { protected: Accountlet *Acc; void OnAfterMain(); public: // Object AccountThread(Accountlet *acc); ~AccountThread(); // Api Accountlet *GetAccountlet() { return Acc; } // 1st phase virtual void Disconnect(); // 2nd phase virtual bool Kill(); }; #define AccStrOption(func, opt) \ LVariant func(const char *Set = 0) { LVariant v; StrOption(opt, v, Set); return v; } #define AccIntOption(name, opt) \ int name(int Set = -1) { LVariant v; IntOption(opt, v, Set); return v.CastInt32(); } class ScribeClass Accountlet : public LStream { friend class ScribeAccount; friend class AccountletThread; friend class AccountThread; public: struct AccountletPriv { LArray Log; }; class AccountletLock { LMutex *l; public: bool Locked; AccountletPriv *d; AccountletLock(AccountletPriv *data, LMutex *lck, const char *file, int line) { d = data; l = lck; Locked = lck->Lock(file, line); } ~AccountletLock() { if (Locked) l->Unlock(); } }; typedef LAutoPtr I; private: AccountletPriv d; LMutex PrivLock; protected: // Data ScribeAccount *Account; LAutoPtr Thread; bool ConnectionStatus; MailProtocol *Client; uint64 LastOnline; LString TempPsw; bool Quiet; LView *Parent; // Pointers ScribeFolder *Root; LDataStoreI *DataStore; LMailStore *MailStore; // this memory is owned by ScribeWnd // Options const char *OptPassword; // Members LSocketI *CreateSocket(bool Sending, LCapabilityClient *Caps, bool RawLFCheck); bool WaitForTransfers(List &Files); void StrOption(const char *Opt, LVariant &v, const char *Set); void IntOption(const char *Opt, LVariant &v, int Set); // LStringPipe Line; ssize_t Write(const void *buf, ssize_t size, int flags); public: MailProtocolProgress Group; MailProtocolProgress Item; Accountlet(ScribeAccount *a); ~Accountlet(); Accountlet &operator =(const Accountlet &a) { LAssert(0); return *this; } I Lock(const char *File, int Line) { I a(new AccountletLock(&d, &PrivLock, File, Line)); if (!a->Locked) a.Reset(); return a; } // Methods bool Connect(LView *Parent, bool Quiet); bool Lock(); void Unlock(); virtual bool IsConfigured() { return false; } bool GetStatus() { return ConnectionStatus; } uint64 GetLastOnline() { return LastOnline; } ScribeAccount* GetAccount() { return Account; } bool IsCancelled(); void IsCancelled(bool b); ScribeWnd* GetApp(); const char* GetStateName(); ScribeFolder* GetRootFolder() { return Root; } RoProp(AccountThreadState, State); bool IsOnline() { if (DataStore) return DataStore->GetInt(FIELD_IS_ONLINE) != 0; return Thread != 0; } void OnEndSession() { if (DataStore) DataStore->SetInt(FIELD_IS_ONLINE, false); } // Commands void Disconnect(); void Kill(); // Data char *OptionName(const char *Opt, char *Dest, int DestLen); void Delete(); LThread *GetThread() { return Thread; } LMailStore *GetMailStore() { return MailStore; } LDataStoreI *GetDataStore() { return DataStore; } // General options virtual int UseSSL(int Set = -1) = 0; AccStrOption(Name, OPT_AccountName); AccIntOption(Disabled, OPT_AccountDisabled); AccIntOption(Id, OPT_AccountUID); AccIntOption(Expanded, OPT_AccountExpanded); bool GetPassword(LPassword *p); void SetPassword(LPassword *p); bool IsCheckDialup(); // Events void OnThreadDone(); void OnOnlineChange(bool Online); virtual void OnBeforeDelete(); // LDom impl bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL); bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL); // Virtuals virtual void Main(AccountletThread *Thread) = 0; virtual LVariant Server(const char *Set = 0) = 0; virtual LVariant UserName(const char *Set = 0) = 0; virtual void Enabled(bool b) = 0; virtual void OnPulse(char *s, int s_len) {} virtual bool IsReceive() { return false; } virtual bool InitMenus() { return false; } virtual void CreateMaps() = 0; virtual ScribeAccountletStatusIcon GetStatusIcon() { return STATUS_ERROR; } }; #undef RoProp class AccountletThread; class AccountIdentity : public Accountlet { public: AccountIdentity(ScribeAccount *a); AccStrOption(Name, OPT_AccIdentName); AccStrOption(Email, OPT_AccIdentEmail); AccStrOption(ReplyTo, OPT_AccIdentReply); AccStrOption(TextSig, OPT_AccIdentTextSig); AccStrOption(HtmlSig, OPT_AccIdentHtmlSig); AccIntOption(Sort, OPT_AccountSort); int UseSSL(int Set = -1) { return 0; } void Main(AccountletThread *Thread) {} LVariant Server(const char *Set = 0) { return LVariant(); } LVariant UserName(const char *Set = 0) { return LVariant(); } void Enabled(bool b) {} void CreateMaps(); bool IsValid(); bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL); bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL); }; struct ScribeEnvelope { LString MsgId; LString SourceFolder; LString From; LArray To; LString References; LString FwdMsgId; LString BounceMsgId; LString Rfc822; }; class SendAccountlet : public Accountlet { friend class ScribeAccount; friend class ScribeWnd; LMenuItem *SendItem; public: LArray Outbox; SendAccountlet(ScribeAccount *a); ~SendAccountlet(); // Methods void Main(AccountletThread *Thread); void Enabled(bool b); bool InitMenus(); void CreateMaps(); ScribeAccountletStatusIcon GetStatusIcon(); // Sending options AccStrOption(Server, OPT_SmtpServer); AccIntOption(Port, OPT_SmtpPort); AccStrOption(Domain, OPT_SmtpDomain); AccStrOption(UserName, OPT_SmtpName); AccIntOption(RequireAuthentication, OPT_SmtpAuth); AccIntOption(AuthType, OPT_SmtpAuthType); AccStrOption(PrefCharset1, OPT_SendCharset1); AccStrOption(PrefCharset2, OPT_SendCharset2); AccStrOption(HotFolder, OPT_SendHotFolder); AccIntOption(OnlySendThroughThisAccount, OPT_OnlySendThroughThis); /// Get/Set the SSL mode /// \sa #SSL_NONE, #SSL_STARTTLS or #SSL_DIRECT AccIntOption(UseSSL, OPT_SmtpSSL); bool IsConfigured() { LVariant hot = HotFolder(); if (hot.Str()) { bool Exists = LDirExists(hot.Str()); printf("%s:%i - '%s' exists = %i\n", _FL, hot.Str(), Exists); return Exists; } return ValidStr(Server().Str()); } bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL); bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL); }; typedef LHashTbl, LXmlTag*> MsgListHash; class MsgList : protected MsgListHash { LOptionsFile *Opts; LString Tag; bool Loaded; bool Load(); LXmlTag *LockId(const char *id, const char *file, int line); void Unlock(); public: typedef MsgListHash Parent; bool Dirty; MsgList(LOptionsFile *Opts, char *Tag); ~MsgList(); // Access methods bool Add(const char *id); bool Delete(const char *id); int Length(); bool Find(const char *id); void Empty(); LString::Array CopyKeys(); // Dates bool SetDate(char *id, LDateTime *dt); bool GetDate(char *id, LDateTime *dt); }; class ReceiveAccountlet : public Accountlet { friend class ScribeAccount; friend class ScribeWnd; friend class ImapThread; friend class ScpThread; LArray Actions; LList *Items; int SecondsTillOnline; LMenuItem *ReceiveItem; LMenuItem *PreviewItem; List *IdTemp; LAutoPtr SettingStore; LAutoPtr Msgs; LAutoPtr Spam; public: ReceiveAccountlet(ScribeAccount *a); ~ReceiveAccountlet(); // Props LList *GetItems() { return Items; } bool SetItems(LList *l); bool SetActions(LArray *a = NULL); bool IsReceive() { return true; } bool IsPersistant(); // Methods void Main(AccountletThread *Thread); void OnPulse(char *s, int s_len); bool OnIdle(); void Enabled(bool b); bool InitMenus(); int GetCheckTimeout(); void CreateMaps(); ScribeAccountletStatusIcon GetStatusIcon(); // Message list bool HasMsg(const char *Id); void AddMsg(const char *Id); void RemoveMsg(const char *Id); void RemoveAllMsgs(); int GetMsgs(); // Spam list void DeleteAsSpam(const char *Id); bool RemoveFromSpamIds(const char *Id); bool IsSpamId(const char *Id, bool Delete = false); // Receive options AccStrOption(Protocol, OPT_Pop3Protocol); ScribeProtocol ProtocolType() { return ProtocolStrToEnum(Protocol().Str()); } AccStrOption(Server, OPT_Pop3Server); AccIntOption(Port, OPT_Pop3Port); AccStrOption(UserName, OPT_Pop3Name); AccStrOption(DestinationFolder, OPT_Pop3Folder); AccIntOption(AutoReceive, OPT_Pop3AutoReceive); AccStrOption(CheckTimeout, OPT_Pop3CheckEvery); AccIntOption(LeaveOnServer, OPT_Pop3LeaveOnServer); AccIntOption(DeleteAfter, OPT_DeleteAfter); AccIntOption(DeleteDays, OPT_DeleteDays); AccIntOption(DeleteLarger, OPT_DeleteIfLarger); AccIntOption(DeleteSize, OPT_DeleteIfLargerSize); AccIntOption(DownloadLimit, OPT_MaxEmailSize); AccStrOption(Assume8BitCharset, OPT_Receive8BitCs); AccStrOption(AssumeAsciiCharset, OPT_ReceiveAsciiCs); AccIntOption(AuthType, OPT_ReceiveAuthType); AccStrOption(HotFolder, OPT_ReceiveHotFolder); AccIntOption(SecureAuth, OPT_ReceiveSecAuth); /// Get/Set the SSL mode /// \sa #SSL_NONE, #SSL_STARTTLS or #SSL_DIRECT AccIntOption(UseSSL, OPT_Pop3SSL); bool IsConfigured() { LVariant hot = HotFolder(); if (hot.Str() && LDirExists(hot.Str())) { return true; } LVariant v = Server(); bool s = ValidStr(v.Str()); v = Protocol(); if (!v.Str() || _stricmp(v.Str(), PROTOCOL_POP_OVER_HTTP) != 0) { v = UserName(); s &= ValidStr(v.Str()); } return s; } bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL); bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL); }; class ScribeAccount : public LDom, public LXmlTreeUi, public LCapabilityClient { friend class ScribeWnd; friend class ScribePopViewer; friend class AccountStatusPanel; friend class Accountlet; friend class SendAccountlet; friend class ReceiveAccountlet; protected: class ScribeAccountPrivate *d; ScribeWnd *Parent; ScribeFolder *&GetRoot(); void SetIndex(int i); public: // Data AccountIdentity Identity; SendAccountlet Send; ReceiveAccountlet Receive; LArray Views; // Object ScribeAccount(ScribeWnd *parent, int index); ~ScribeAccount(); // Lifespan bool IsValid(); bool Create(); bool Delete(); // Properties ScribeWnd *GetApp() { return Parent; } int GetIndex(); bool IsOnline(); void SetCheck(bool c); LMenuItem *GetMenuItem(); void SetMenuItem(LMenuItem *i); void OnEndSession() { Send.OnEndSession(); Receive.OnEndSession(); } // Commands void Stop(); bool Disconnect(); void Kill(); void SetDefaults(); // User interface void InitUI(LView *Parent, int Tab, std::function callback); bool InitMenus(); void SerializeUi(LView *Wnd, bool Load); int OnNotify(LViewI *Ctrl, LNotification &n); // Worker void OnPulse(char *s = NULL, int s_len = 0); void ReIndex(int i); void CreateMaps(); // LDom interface bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override; }; ////////////////////////////////////////////////////////////////////// class ScribeClass ScribeDom : public LDom { ScribeWnd *App; public: Mail *Email; Contact *Con; ContactGroup *Grp; Calendar *Cal; Filter *Fil; ScribeDom(ScribeWnd *a); bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); }; ////////////////////////////////////////////////////////////////////// #include "BayesianFilter.h" #include "Components.h" class LMailStore { public: bool Default, Expanded; LString Name; LString Path; LDataStoreI *Store; ScribeFolder *Root; LMailStore() { Expanded = true; Default = false; Store = NULL; Root = NULL; } bool IsOk() { return Store != NULL && Root != NULL; } int Priority() { int Ver = Store ? (int)Store->GetInt(FIELD_VERSION) : 0; return Ver; } void Empty() { DeleteObj(Store); Name.Empty(); Path.Empty(); Root = NULL; } LMailStore &operator =(LMailStore &a) { LAssert(0); return *this; } }; struct OptionsInfo { LString File; char *Leaf; int Score; uint64 Mod; bool Usual; OptionsInfo(); OptionsInfo &operator =(char *p); LAutoPtr Load(); }; class ScribeClass ScribeWnd : public LWindow, public LDom, public LDataEventsI, public BayesianFilter, public CapabilityInstaller, public LCapabilityTarget { friend class ScribeAccount; friend class Accountlet; friend class SendAccountlet; friend class ReceiveAccountlet; friend class AccountStatusPanel; friend class ScribeFolder; friend class OptionsDlg; friend class LoadWordStoreThread; friend struct ScribeReplicator; friend struct LoadMailStoreState; friend struct UnitTestState; public: enum LayoutMode { OptionsLayout = 0, ///-------------------- /// | | /// Folders | List | /// | | /// |---------| /// | Preview | ///-------------------- FoldersListAndPreview = 1, ///----------------- /// | | /// Folders | List | /// | | ///----------------- /// Preview | ///----------------- PreviewOnBottom, ///----------------- /// | | /// Folders | List | /// | | ///----------------- FoldersAndList, ///--------------------------- /// | | | /// Folders | List | Preview | /// | | | ///--------------------------- ThreeColumn, }; enum AppState { ScribeConstructing, // In Construct1 + Construct2 ScribeConstructed, // Finished Construct2 and ready for Construct3 ScribeInitializing, // In Construct3 ScribeRunning, ScribeExiting, ScribeLoadingFolders, ScribeUnloadingFolders, }; AppState GetScribeState() { return ScribeState; } protected: class ScribeWndPrivate *d = NULL; LTrayIcon TrayIcon; // Ipc LSharedMemory *ScribeIpc = NULL; class ScribeIpcInstance *ThisInst = NULL; bool ShutdownIpc(); // Accounts List Accounts; // New Mail stuff class LNewMailDlg *NewMailDlg = NULL; static AppState ScribeState; DoEvery Ticker; int64 LastDrop = 0; // Static LSubMenu *File = NULL; LSubMenu *ContactsMenu = NULL; LSubMenu *Edit = NULL; LSubMenu *Help = NULL; LToolBar *Commands = NULL; // Dynamic LSubMenu *IdentityMenu = NULL; LMenuItem *DefaultIdentityItem = NULL; LSubMenu *MailMenu = NULL; LSubMenu *SendMenu = NULL, *ReceiveMenu = NULL, *PreviewMenu = NULL; LMenuItem *SendItem = NULL, *ReceiveItem = NULL, *PreviewItem = NULL; LSubMenu *NewTemplateMenu = NULL; LMenuItem *WorkOffline = NULL; // Commands LCommand CmdSend; LCommand CmdReceive; LCommand CmdPreview; // Storage LArray Folders; LArray FolderTasks; List PostValidateFree; // Main view LAutoPtr ImageList; LAutoPtr ToolbarImgs; class LBox *Splitter = NULL; ThingList *MailList = NULL; class DynamicHtml *TitlePage = NULL; class LSearchView *SearchView = NULL; MailTree *Tree = NULL; class LPreviewPanel *PreviewPanel = NULL; class AccountStatusPanel *StatusPanel = NULL; // Security ScribePerm CurrentAuthLevel = PermRequireNone; // Methods void SetupUi(); void SetupAccounts(); int AdjustAllObjectSizes(LDataI *Item); bool CleanFolders(ScribeFolder *f); void LoadFolders(std::function Callback); void LoadMailStores(std::function Callback); bool ProcessFolder(LDataStoreI *&Store, int StoreIdx, char *StoreName); bool UnLoadFolders(); void AddFolderToMru(char *FileName); void AddContactsToMenu(LSubMenu *Menu); bool FindWordDb(char *Out, int OutSize, char *Name); void OnFolderChanged(LDataFolderI *folder); bool ValidateFolder(LMailStore *s, int Id); void GrowlOnMail(Mail *m); void GrowlInfo(LString title, LString text); bool OnTransfer(); ScribeDomType StrToDom(const char *Var) { return ::StrToDom(Var); } const char* DomToStr(ScribeDomType p) { return ::DomToStr(p); } void LoadImageResources(); void DoOnTimer(LScriptCallback *c); public: ScribeWnd(); void Construct1(); void Construct2(); void Construct3(); void SetLanguage(); ~ScribeWnd(); const char *GetClass() override { return "ScribeWnd"; } void DoDebug(char *s); void Validate(LMailStore *s); // Unit testing. static bool IsUnitTest; #ifdef _DEBUG void UnitTests(std::function Callback); #endif // Dom bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override; bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) override; // --------------------------------------------------------------------- // Methods LAutoString GetDataFolder(); LDataStoreI *CreateDataStore(const char *Full, bool CreateIfMissing); Thing *CreateThingOfType(Store3ItemTypes Type, LDataI *obj = 0); Thing *CreateItem(int Type, ScribeFolder *Folder = 0, bool Ui = true); Mail *CreateMail(Contact *c = 0, const char *Email = 0, const char *Name = 0); Mail *LookupMailRef(const char *MsgRef, bool TraceAllUids = false); bool CreateFolders(LAutoString &FileName); bool CompactFolders(LMailStore &Store, bool Interactive = true); void Send(int Which = -1, bool Quiet = false); void Receive(int Which); void Preview(int Which); void OnBeforeConnect(ScribeAccount *Account, bool Receive); void OnAfterConnect(ScribeAccount *Account, bool Receive); bool NeedsCapability(const char *Name, const char *Param = NULL) override; void OnInstall(CapsHash *Caps, bool Status) override; void OnCloseInstaller() override; bool HasFolderTasks() { return FolderTasks.Length() > 0; } int GetEventHandle(); void Update(int What = 0); void UpdateUnRead(ScribeFolder *Folder, int Delta); void ThingPrint(std::function Callback, ThingType *m, LPrinter *Info = NULL, LView *Parent = NULL, int MaxPage = -1); bool OpenAMail(ScribeFolder *Folder); void BuildDynMenus(); LDocView *CreateTextControl(int Id, const char *MimeType, bool Editor, Mail *m = 0); void SetLastDrop() { LastDrop = LCurrentTime(); } void SetListPane(LView *v); void SetLayout(LayoutMode Mode = OptionsLayout); bool IsMyEmail(const char *Email); bool SetItemPreview(LView *v); LOptionsFile::PortableType GetPortableType(); ScribeRemoteContent RemoteContent_GetSenderStatus(const char *Addr); void RemoteContent_ClearCache(); void RemoteContent_AddSender(const char *Addr, bool WhiteList); void SetDefaultHandler(); void OnSetDefaultHandler(bool Error, bool OldAssert); void SetCurrentIdentity(int i=-1); int GetCurrentIdentity(); bool MailReplyTo(Mail *m, bool All = false); bool MailForward(Mail *m); bool MailBounce(Mail *m); void MailMerge(LArray &Recip, const char *FileName, Mail *Source); void OnNewMail(List *NewMailObjs, bool Add = true); void OnNewMailSound(); void OnCommandLine(); void OnTrayClick(LMouse &m) override; void OnTrayMenu(LSubMenu &m) override; void OnTrayMenuResult(int MenuId) override; void OnFolderSelect(ScribeFolder *f); void AddThingSrc(ScribeFolder *src); void RemoveThingSrc(ScribeFolder *src); LArray GetThingSources(Store3ItemTypes Type); bool GetContacts(List &Contacts, ScribeFolder *f = 0, bool Deep = true); List *GetEveryone(); void HashContacts(LHashTbl,Contact*> &Contacts, ScribeFolder *Folder = 0, bool Deep = true); // CapabilityInstaller impl LAutoString GetHttpProxy() override; InstallProgress *StartAction(MissingCapsBar *Bar, LCapabilityTarget::CapsHash *Components, const char *Action) override; class HttpImageThread *GetImageLoader(); int GetMaxPages(); ScribeWnd::LayoutMode GetEffectiveLayoutMode(); ThingList *GetMailList() { return MailList; } // Gets the matching mail store for a given identity LMailStore *GetMailStoreForIdentity ( /// If this is NULL, assume the current identity const char *IdEmail = NULL ); ScribeFolder *GetFolder(int Id, LMailStore *s = NULL, bool Quiet = false); ScribeFolder *GetFolder(int Id, LDataI *s); ScribeFolder *GetFolder(const char *Name, LMailStore *s = NULL); ScribeFolder *GetCurrentFolder(); int GetFolderType(ScribeFolder *f); LImageList *GetIconImgList() { return ImageList; } LAutoPtr &GetToolbarImgList() { return ToolbarImgs; } LString GetResourceFile(SribeResourceType Type); DoEvery *GetTicker() { return &Ticker; } ScribeAccount *GetSendAccount(); ScribeAccount *GetCurrentAccount(); ThingFilter *GetThingFilter(); List *GetAccounts() { return &Accounts; } ScribeAccount *GetAccountById(int Id); ScribeAccount *GetAccountByEmail(const char *Email); LPrinter *GetPrinter(); int GetActiveThreads(); int GetToolbarHeight(); void GetFilters(List &Filters, bool JustIn, bool JustOut, bool JustInternal); bool OnFolderTask(LEventTargetI *Ptr, bool Add); LArray &GetStorageFolders() { return Folders; } LMailStore *GetDefaultMailStore(); LMailStore *GetMailStoreForPath(const char *Path); bool OnMailStore(LMailStore **MailStore, bool Add); ThingList *GetItemList() { return MailList; } LColour GetColour(int i); LMutex *GetLock(); LFont *GetPreviewFont(); LFont *GetBoldFont() { return LSysBold; } LToolBar *LoadToolbar(LViewI *Parent, const char *File, LAutoPtr &Img); class LVmCallback *GetDebuggerCallback(); class GpgConnector *GetGpgConnector(); void GetUserInput(LView *Parent, LString Msg, bool Password, std::function Callback); int GetCalendarSources(LArray &Sources); Store3Status GetAccessLevel(LViewI *Parent, ScribePerm Required, const char *ResourceName, std::function Callback); void GetAccountSettingsAccess(LViewI *Parent, ScribeAccessType AccessType, std::function Callback); const char* EditCtrlMimeType(); LAutoString GetReplyXml(const char *MimeType); LAutoString GetForwardXml(const char *MimeType); bool GetHelpFilesPath(char *Path, int PathSize); bool LaunchHelp(const char *File); LAutoString ProcessSig(Mail *m, char *Xml, const char *MimeType); LString ProcessReplyForwardTemplate(Mail *m, Mail *r, char *Xml, int &Cursor, const char *MimeType); bool LogFilterActivity(); ScribeFolder *FindContainer(LDataFolderI *f); bool SaveDirtyObjects(int TimeLimitMs = 100); class LSpellCheck *CreateSpellObject(); class LSpellCheck *GetSpellThread(bool OverrideOpt = false); bool SetSpellThreadParams(LSpellCheck *Thread); void OnSpellerSettingChange(); bool OnMailTransferEvent(MailTransferEvent *e); LViewI *GetView() { return this; } char *GetUiTags(); struct MailStoreUpgradeParams { LAutoString OldFolders; LAutoString NewFolders; bool Quiet; LStream *Log; MailStoreUpgradeParams() { Quiet = false; Log = 0; } }; // Scripting support bool GetScriptCallbacks(LScriptCallbackType Type, LArray &Callbacks); LScriptCallback GetCallback(const char *CallbackMethodName); bool RegisterCallback(LScriptCallbackType Type, LScriptArguments &Args); LStream *ShowScriptingConsole(); bool ExecuteScriptCallback(LScriptCallback &c, LScriptArguments &Args, bool ReturnArgs = false); LScriptEngine *GetScriptEngine(); // Options LOptionsFile *GetOptions(bool Create = false) override; bool ScanForOptionsFiles(LArray &Inf, LSystemPath PathType); bool LoadOptions(); bool SaveOptions(); bool IsSending() { return false; } bool ShowToolbarText(); bool IsValid(); // Data events from storage back ends bool GetSystemPath(int Folder, LVariant &Path) override; void OnNew(LDataFolderI *parent, LArray &new_items, int pos, bool is_new) override; bool OnDelete(LDataFolderI *parent, LArray &items) override; bool OnMove(LDataFolderI *new_parent, LDataFolderI *old_parent, LArray &Items) override; void SetContext(const char *file, int line) override; bool OnChange(LArray &items, int FieldHint) override; void Post(LDataStoreI *store, void *Param) override { PostEvent(M_STORAGE_EVENT, store->Id, (LMessage::Param)Param); } void OnPropChange(LDataStoreI *store, int Prop, LVariantType Type) override; bool Match(LDataStoreI *store, LDataPropI *Addr, int Type, LArray &Matches) override; ContactGroup *FindGroup(char *Name); bool AddStore3EventHandler(LDataEventsI *callback); bool RemoveStore3EventHandler(LDataEventsI *callback); // --------------------------------------------------------------------- // Events void OnDelete(); int OnNotify(LViewI *Ctrl, LNotification n) override; void OnPaint(LSurface *pDC) override; LMessage::Result OnEvent(LMessage *Msg) override; int OnCommand(int Cmd, int Event, OsView Handle) override; void OnPulse() override; void OnPulseSecond(); bool OnRequestClose(bool OsShuttingDown) override; void OnSelect(List *l = 0, bool ChangeEvent = false); void OnReceiveFiles(LArray &Files) override; void OnUrl(const char *Url) override; void OnZoom(LWindowZoom Action) override; void OnCreate() override; void OnMinute(); void OnHour(); bool OnIdle(); void OnBayesAnalyse(const char *Msg, const char *WhiteListEmail) override; /// \returns true if spam bool OnBayesResult(const char *MailRef, double Rating) override; /// \returns true if spam bool OnBayesResult(Mail *m, double Rating); void OnScriptCompileError(const char *Source, Filter *f); }; //////////////////////////////////////////////////////////////////////////////////// #ifdef SCRIBE_APP #include "ScribePrivate.h" #endif diff --git a/src/ScribeFolder.cpp b/src/ScribeFolder.cpp --- a/src/ScribeFolder.cpp +++ b/src/ScribeFolder.cpp @@ -1,4548 +1,4550 @@ /* ** FILE: ScribeFolder.cpp ** AUTHOR: Matthew Allen ** DATE: 17/1/2000 ** DESCRIPTION: Scribe folder's ** ** Copyright (C) 2000-2002, Matthew Allen ** fret@memecode.com */ // Includes #include "Scribe.h" #include "lgi/common/DropFiles.h" #include "lgi/common/ProgressDlg.h" #include "lgi/common/QuickSort.h" #include "lgi/common/DisplayString.h" #include "lgi/common/TextFile.h" #include "lgi/common/LgiRes.h" #include "lgi/common/FileSelect.h" #include "lgi/common/ClipBoard.h" #include "Calendar.h" #include "resdefs.h" #include "ReplicateDlg.h" #include "FolderTask.h" ////////////////////////////////////////////////////////////////////////////// #if WINNATIVE #include "lgi/common/Com.h" #endif class LDndFilePromise #if defined(WINDOWS) #elif defined(__GTK_H__) #elif defined(MAC) #endif { LStream *Src; #if WINNATIVE FILEGROUPDESCRIPTOR Gd; #elif defined(__GTK_H__) #elif defined(MAC) #endif public: LDndFilePromise(LDragData &dd, LStream *src, LString FileName) { Src = src; #if WINNATIVE ZeroObj(Gd); Gd.cItems = 1; // CLSID Fd.clsid; // SIZEL Fd.sizel; // POINTL Fd.pointl; // FILETIME Fd.ftCreationTime; // FILETIME Fd.ftLastAccessTime; // FILETIME Fd.ftLastWriteTime; FILEDESCRIPTOR &Fd = Gd.fgd[0]; Fd.dwFlags = FD_ATTRIBUTES | FD_PROGRESSUI; Fd.dwFileAttributes = FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_NORMAL; int64 Sz = Src->GetSize(); if (Sz >= 0) { Fd.dwFlags |= FD_FILESIZE; Fd.nFileSizeHigh = Sz >> 32; Fd.nFileSizeLow = Sz & 0xffffffff; } auto Leaf = FileName.RFind(DIR_STR); LAutoWString FnW(Utf8ToWide(Leaf >= 0 ? FileName(Leaf+1,-1) : FileName)); Strcpy(Fd.cFileName, CountOf(Fd.cFileName), FnW.Get()); LVariant &v = dd.Data[0]; v.SetBinary(sizeof(Gd), &Gd); #elif defined(__GTK_H__) LAssert(!"Not impl."); #elif LGI_COCOA LAssert(!"Not impl."); #elif LGI_CARBON // See Apple: Technical Note TN1085 // https://web.archive.org/web/20080725134839/http://developer.apple.com/technotes/tn/tn1085.html // flavorTypePromiseHFS = 'phfs' PromiseHFSFlavor Promise; ZeroObj(Promise); Promise.fileType = 'mbox'; Promise.fileCreator = '****'; Promise.promisedFlavor = kDragPromisedFlavor; // 'fssP' int Sz = sizeof(Promise); LVariant &v1 = dd.Data[0]; v1.SetBinary(sizeof(Promise), &Promise, false); // The Dnd code will add the 2nd part of the promise // There isn't the visibility into that API at this level #else #error "Impl me." #endif } ~LDndFilePromise() { #if defined(WINDOWS) #elif defined(__GTK_H__) #elif defined(MAC) #endif } #if defined(WINDOWS) #elif defined(__GTK_H__) #elif defined(MAC) #endif int AddContents(LDragData &dd) { dd.Data[0] = Src; return 1; } }; ////////////////////////////////////////////////////////////////////////////// #define PROFILE_POPULATE 0 #define PROFILE_LOAD_THINGS 0 int FoldersCurrentlyLoading = 0; char ScribeFolderObject[] = "com.memecode.Folder"; class ScribeFolderPriv { public: int8 IsInbox = -1; bool InUpdateUnread = false; LAutoPtr DsBase; LAutoPtr DsUnread; LAutoPtr FilePromise; }; ////////////////////////////////////////////////////////////////////////////// ScribeFolder::ScribeFolder() { d = new ScribeFolderPriv; } ScribeFolder::~ScribeFolder() { bool IsRoot = !GetParent(); if (CurState != FldState_Idle) { // int Cur = FoldersCurrentlyLoading; #ifdef _DEBUG LAssert(!"Can't delete folder while it's busy."); #else LgiMsg( App, "Can't unload folder while busy. Please report what\n" "you were attempting to do to fret@memecode.com", "Scribe Error", MB_OK); #endif return; } switch (GetItemType()) { case MAGIC_CALENDAR: CalendarSource::FolderDelete(this); // fall through case MAGIC_CONTACT: case MAGIC_FILTER: case MAGIC_GROUP: App->RemoveThingSrc(this); break; default: break; } if (View()) { bool IsMe = View()->GetContainer() == this; // bool IsSelect = Select(); View()->DeletePlaceHolders(); if (IsMe) { View()->SetContainer(0); View()->RemoveAll(); } } Thing *t; int i = 0; while ((t = Items[i])) { if (!t->DecRef()) i++; } Update(); EmptyFieldList(); DeleteObj(d); ScribeFolder *f = GetChildFolder(); DeleteObj(f); if (!IsRoot) { f = GetNextFolder(); DeleteObj(f); } // Don't delete 'Object' here, it's owned by the backend storage object } bool ScribeFolder::SetObject(LDataI *o, bool InDestructor, const char *File, int Line) { if (CurState != FldState_Idle) { LAssert(!"Can't set object while folder is not idle."); return false; } return LDataUserI::SetObject(o, InDestructor, File, Line); } void ScribeFolder::UpdateOsUnread() { #ifdef WIN32 if (App->GetFolder(FOLDER_INBOX) == this) { LHashTbl, int> Un(0, -1); // Pre-populate 'Un' with our email accounts List *Acc = App->GetAccounts(); if (Acc) { for (auto a: *Acc) { LVariant e = a->Identity.Email(); if (e.Str()) Un.Add(e.Str(), 0); } } // Scan folder for unread email... for (auto t : Items) { Mail *m = t->IsMail(); if (m) { if (!TestFlag(m->GetFlags(), MAIL_READ)) { ScribeAccount *a = m->GetAccountSentTo(); if (a) { LVariant Email; Email = a->Identity.Email(); if (Email.Str()) { int Cur = Un.Find(Email.Str()); Un.Add(Email.Str(), Cur + 1); } } } } } #if 0 // Set system email status LArray Ver; int Os = LGetOs(&Ver); if ((Os == LGI_OS_WIN32 || Os == LGI_OS_WIN64) && (Ver[0] > 5 || (Ver[0] == 5 && Ver[1] > 0))) { char e[256]; LgiGetExeFile(e, sizeof(e)); typedef HRESULT (__stdcall *pSHSetUnreadMailCount)(LPCWSTR pszMailAddress, DWORD dwCount,LPCWSTR pszShellExecuteCommand); LLibrary Shell32("shell32"); pSHSetUnreadMailCount SHSetUnreadMailCount = (pSHSetUnreadMailCount) Shell32.GetAddress("SHSetUnreadMailCountW"); if (SHSetUnreadMailCount) { LVariant Exe; Exe = e; char *k; for (int u=Un.First(&k); u>=0; u=Un.Next(&k)) { LVariant Email = k; SHSetUnreadMailCount(Email.WStr(), u, Exe.WStr()); } } } #endif } #endif } void ScribeFolder::SetLoadOnDemand() { if ((LoadOnDemand = new LTreeItem)) { Expanded(false); LoadOnDemand->SetText((char*)LLoadString(IDS_LOADING)); Insert(LoadOnDemand); } } void ScribeFolder::GetMessageById(const char *Id, std::function Callback) { if (!Id) { if (Callback) Callback(NULL); return; } LoadThings(NULL, [this, Id=LString(Id), Callback](auto s) { if (s < Store3Delayed) { if (Callback) Callback(NULL); return; } for (auto t : Items) { Mail *r = t->IsMail(); if (!r) continue; auto rid = r->GetMessageId(); if (!Stricmp(rid, Id.Get())) { if (Callback) Callback(r); return; } } }); } bool ScribeFolder::InsertThing(Thing *t) { bool Status = false; if (t && Select() && View()) { // Filter ThingFilter *Filter = App->GetThingFilter(); t->SetFieldArray(FieldArray); if (!Filter || Filter->TestThing(t)) { if (t->GetList() != View()) { View()->Insert(t); #if WINNATIVE UpdateWindow(View()->Handle()); #endif } } Status = true; } return Status; } bool ScribeFolder::GetThreaded() { return GetObject() ? GetObject()->GetInt(FIELD_FOLDER_THREAD) != 0 : false; } void ScribeFolder::SetThreaded(bool t) { if (GetObject() && GetThreaded() ^ t) { GetObject()->SetInt(FIELD_FOLDER_THREAD, t); SetDirty(); } } ThingList *ScribeFolder::View() { return App ? App->GetMailList() : 0; } bool ScribeFolder::HasFieldId(int Id) { auto o = GetFldObj(); if (o) { for (LDataPropI *f = o->Fields().First(); f; f = o->Fields().Next()) { if (Id == f->GetInt(FIELD_ID)) { return true; } } } return false; } void ScribeFolder::SetFolderPerms(LView *Parent, ScribeAccessType Access, ScribePerm Perm, std::function Callback) { ScribePerm Current = GetFolderPerms(Access); int Field = (Access == ScribeReadAccess) ? FIELD_FOLDER_PERM_READ : FIELD_FOLDER_PERM_WRITE; if (GetObject() && Current != Perm) { App->GetAccessLevel(Parent, Current, GetPath(), [this, Field, Perm, Callback](auto Allow) { if (Allow) { GetObject()->SetInt(Field, Perm); SetDirty(); } if (Callback) Callback(Allow); }); } else { if (Callback) Callback(true); // i.e. not changing } } ScribePerm ScribeFolder::GetFolderPerms(ScribeAccessType Access) { int Field = (Access == ScribeReadAccess) ? FIELD_FOLDER_PERM_READ : FIELD_FOLDER_PERM_WRITE; return GetObject() ? (ScribePerm)GetObject()->GetInt(Field) : PermRequireNone; } Store3Status ScribeFolder::CopyTo(ScribeFolder *NewParent, int NewIndex) { Store3Status Copied = Store3Error; if (NewParent == GetParent()) { NewParent->GetObject()->GetStore(); } else if (GetObject() && GetObject()->GetStore() && NewParent->GetObject() && NewParent->GetObject()->GetStore()) { // Find or create the destination folder LDataFolderI *Dst = 0; auto Name = GetObject()->GetStr(FIELD_FOLDER_NAME); for (ScribeFolder *f = NewParent->GetChildFolder(); f; f = f->GetNextFolder()) { auto n = f->GetObject()->GetStr(FIELD_FOLDER_NAME); if (n && !_stricmp(n, Name)) { Dst = f->GetFldObj(); } } if (!Dst) { // Create sub-folder if ((Dst = dynamic_cast(NewParent->GetObject()->GetStore()->Create(GetObject()->Type())))) { Dst->CopyProps(*GetObject()); if (Dst->Save(NewParent->GetObject()) == Store3Error) return Store3Error; } else return Store3Error; } if (Dst) { Copied = Store3ReplicateFolders(App, Dst, GetFldObj(), true, false, 0); } } else LAssert(!"Pointer error"); return Copied; } Store3Status ScribeFolder::SetFolder(ScribeFolder *f, int Param) { Store3Status Moved = Store3Error; if (f == this) { LAssert(0); } else if (f && GetObject() && GetObject()->GetStore() && f->GetObject() && f->GetObject()->GetStore()) { if (GetObject()->GetStore() == f->GetObject()->GetStore()) { // Simple in storage movement LArray Mv; Mv.Add(GetObject()); Moved = GetObject()->GetStore()->Move(f->GetFldObj(), Mv); if (Moved && Param >= 0) { GetObject()->SetInt(FIELD_FOLDER_INDEX, Param); } } else { // Cross storage movement... // Find or create the destinate folder LDataFolderI *Dst = 0; int Idx = 0; auto Name = GetObject()->GetStr(FIELD_FOLDER_NAME); for (ScribeFolder *c = f->GetChildFolder(); c; c = c->GetNextFolder(), Idx++) { auto n = c->GetObject()->GetStr(FIELD_FOLDER_NAME); if (n && !_stricmp(n, Name)) { Dst = c->GetFldObj(); break; } } if (!Dst) { Dst = dynamic_cast(f->GetObject()->GetStore()->Create(f->Type())); } else { ScribeFolder *c = CastFolder(dynamic_cast(Dst)); if (c) { c->Remove(); Param = Idx; DeleteObj(c); } } if (Dst) { if (View()) View()->RemoveAll(); // Clean up all the items and sub-folders, they will be created by the // replication code calling "OnNew". Thing *t; while ((t = Items[0])) { t->DecRef(); Items.Delete(t); } // Delete the child folders... ScribeFolder *Cf; while ((Cf = GetChildFolder())) { DeleteObj(Cf); } // Copy ourself over... Dst->CopyProps(*GetObject()); LDataFolderI *Old = GetFldObj(); SetObject(Dst, false, _FL); // Save the object to the new store... Store3Status s = GetObject()->Save(f->GetObject()); if (s != Store3Error) { // And replicate all the children objects... Moved = Store3ReplicateFolders(App, GetFldObj(), Old, true, true, 0); } } else LAssert(!"Not a valid folder"); } if (Moved == Store3Success) { // Move LTreeItem node... f->Insert(this, Param); Select(true); LoadFolders(); // Adjust all the other indexes so that it's remembered ScribeFolder *p = GetFolder(); if (p) { int Idx = 0; for (p = p->GetChildFolder(); p; p = p->GetNextFolder()) { p->SetSortIndex(Idx++); p->SetDirty(); } } } } else LAssert(!"Pointer error"); return Moved; } Store3Status ScribeFolder::DeleteAllThings(std::function Callback) { if (!GetFldObj()) return Store3Error; Store3Status r = GetFldObj()->DeleteAllChildren(); if (r == Store3Error) { LAssert(!"DeleteAllChildren failed."); } else if (r == Store3Success) { LAssert(Items.Length() == 0); OnUpdateUnRead(0, true); Update(); } return r; } Store3Status ScribeFolder::DeleteThing(Thing *t, std::function Callback) { Store3Status Status = Store3Error; if (t && t->GetObject()) { if (t->IsAttachment()) { #ifdef _DEBUG Mail *m = #endif t->IsAttachment()->GetOwner(); LAssert(m && Items.HasItem(m)); } if (t->GetObject()->Delete()) { t->SetParentFolder(NULL); if (View()) View()->Remove(t); } } return Status; } Store3Status ScribeFolder::WriteThing(Thing *t, std::function Callback) { if (!t) { if (Callback) Callback(Store3Error); return Store3Error; } auto ParentFolder = GetFolder(); auto Path = ParentFolder ? ParentFolder->GetPath() : NULL; auto OnAllow = [this, t, Callback]() { // Generic thing storage.. bool Create = !t->GetObject(); if (Create) t->SetObject(GetObject()->GetStore()->Create(t->Type()), false, _FL); if (!t->GetObject()) { LAssert(!"No object?"); LgiTrace("%s:%i - No object to save.\n", _FL); if (Callback) Callback(Store3Error); return; } // saving a thing that already has an item on disk auto Obj = GetObject(); auto Status = t->GetObject()->Save(Obj); if (Status != Store3Error) { // The ScribeWnd::OnNew will take care of inserting the item into the // right folder, updating the unread count, any filtering etc. t->OnSerialize(true); if (Callback) Callback(Status); } else { if (Create) t->SetObject(NULL, false, _FL); LgiTrace("%s:%i - Object->Save returned %i.\n", _FL, Status); if (Callback) Callback(Store3Error); } }; if (App) App->GetAccessLevel(App, GetWriteAccess(), Path, [OnAllow](auto Allow) { if (Allow) OnAllow(); }); else OnAllow(); return Store3Success; } int ThingContainerNameCmp(LTreeItem *a, LTreeItem *b, NativeInt d) { ScribeFolder *A = dynamic_cast(a); ScribeFolder *B = dynamic_cast(b); if (A && B) { const char *s1 = A->GetText(); const char *s2 = B->GetText(); const char *Empty = ""; return _stricmp(s1?s1:Empty, s2?s2:Empty); } else LAssert(!"Invalid objects."); return 0; } int ThingContainerIdxCmp(LTreeItem *a, LTreeItem *b, NativeInt d) { ScribeFolder *A = dynamic_cast(a); ScribeFolder *B = dynamic_cast(b); if (A && B) { int Aidx = A->GetSortIndex(); int Bidx = B->GetSortIndex(); if (Aidx >= 0 || Bidx >= 0) { return Aidx - Bidx; } } else LAssert(!"Invalid objects."); return 0; } void ScribeFolder::SortSubfolders() { int i = 0; ScribeFolder *c; LTreeItem::Items.Sort(ThingContainerNameCmp); for (c = GetChildFolder(); c; c = c->GetNextFolder()) { c->SetSortIndex(i++); c->SetDirty(); } GetTree()->UpdateAllItems(); GetTree()->Invalidate(); } void ScribeFolder::DoContextMenu(LMouse &m) { MailTree *mt = dynamic_cast(GetTree()); LScriptUi s(new LSubMenu); List Templates; bool ForceDelete = m.Shift(); bool IsTrash = false; bool IsSystem = false; if (!s.Sub) return; IsTrash = GetItemType() == MAGIC_ANY; IsSystem = App->GetFolderType(this) >= 0; if (App->GetFolderType(this) == FOLDER_OUTBOX) { s.Sub->AppendItem(LLoadString(IDS_MARK_ALL_SEND), IDM_MARK_SEND, true); } if (IsTrash) { s.Sub->AppendItem(LLoadString(IDS_EMPTY), IDM_EMPTY, true); s.Sub->AppendItem(LLoadString(IDS_MARK_ALL_READ), IDM_MARK_ALL_READ, true); } else if (!IsRoot()) { auto *LiteralNew = LLoadString(IDS_NEW); const char *Type = 0; switch (GetItemType()) { case MAGIC_MAIL: Type = LLoadString(IDS_MAIL); break; case MAGIC_CONTACT: Type = LLoadString(IDS_CONTACT); break; case MAGIC_CALENDAR: Type = LLoadString(IDS_CAL_EVENT); break; case MAGIC_FILTER: Type = LLoadString(IDS_FILTER); break; default: break; } if (Type) { char Str[256]; sprintf_s(Str, sizeof(Str), "%s %s", LiteralNew, Type); s.Sub->AppendItem(Str, IDM_NEW_EMAIL, true); } } switch (GetItemType()) { case MAGIC_CONTACT: { char Str[256]; const char *LiteralEmail = LLoadString(IDS_EMAIL); sprintf_s(Str, sizeof(Str), "%s '%s'", LiteralEmail, GetText()); s.Sub->AppendItem(Str, IDM_EMAIL_GROUP, true); ScribeFolder *f = App->GetFolder(FOLDER_TEMPLATES); if (f) { // FIXME f->LoadThings(); auto Merge = s.Sub->AppendSub(LLoadString(IDS_MERGE_TEMPLATE)); if (Merge) { int n = 0; for (auto t: f->Items) { Mail *m = t->IsMail(); if (m) { Templates.Insert(m); Merge->AppendItem(m->GetSubject()?m->GetSubject():(char*)"(no subject)", IDM_MERGE_TEMPLATE_BASE+n++, true); } } } } else { s.Sub->AppendItem(LLoadString(IDS_MERGE_TEMPLATE), 0, false); } s.Sub->AppendItem(LLoadString(IDS_MERGE_FILE), IDM_MERGE_FILE, true); break; } case MAGIC_MAIL: { if (!IsRoot()) { s.Sub->AppendItem(LLoadString(IDC_COLLECT_SUB_MAIL), IDM_COLLECT_MAIL, true); s.Sub->AppendItem(LLoadString(IDS_MARK_ALL_READ), IDM_MARK_ALL_READ, true); if (GetObject() && GetObject()->GetInt(FIELD_STORE_TYPE) == Store3Imap) { s.Sub->AppendItem(LLoadString(IDC_UNDELETE), IDM_UNDELETE, true); s.Sub->AppendItem("Expunge", IDM_EXPUNGE, true); s.Sub->AppendItem(LLoadString(IDC_REFRESH), IDM_REFRESH, true); } } break; } default: break; } if (s.Sub->ItemAt(0)) { s.Sub->AppendSeparator(); } s.Sub->AppendItem(LLoadString(IDS_FIND), IDM_FIND, true); s.Sub->AppendSeparator(); bool HasFolderTask = App->HasFolderTasks(); bool FolderOp = !HasFolderTask && !IsRoot(); s.Sub->AppendItem(LLoadString(IDS_DELETEFOLDER), IDM_DELETE, FolderOp && ((!IsTrash && !IsSystem) || ForceDelete)); s.Sub->AppendItem(LLoadString(IDS_CREATESUBFOLDER), IDM_CREATE_SUB, !HasFolderTask && CanHaveSubFolders(GetItemType())); s.Sub->AppendItem(LLoadString(IDS_RENAMEFOLDER), IDM_RENAME, FolderOp); auto ExportMimeType = GetStorageMimeType(); s.Sub->AppendItem(LLoadString(IDS_EXPORT), IDM_EXPORT, FolderOp && ExportMimeType != NULL); s.Sub->AppendSeparator(); s.Sub->AppendItem(LLoadString(IDS_SORT_SUBFOLDERS), IDM_SORT_SUBFOLDERS, true); s.Sub->AppendItem(LLoadString(IDS_COPY_PATH), IDM_COPY_PATH, true); s.Sub->AppendItem(LLoadString(IDS_PROPERTIES), IDM_PROPERTIES, true); LArray Callbacks; if (App->GetScriptCallbacks(LFolderContextMenu, Callbacks)) { LScriptArguments Args(NULL); Args[0] = new LVariant(App); Args[1] = new LVariant(this); Args[2] = new LVariant(&s); for (auto c: Callbacks) App->ExecuteScriptCallback(*c, Args); Args.DeleteObjects(); } m.ToScreen(); LPoint Scr = _ScrollPos(); m.x -= Scr.x; m.y -= Scr.y; int Msg = s.Sub->Float(GetTree(), m.x, m.y); switch (Msg) { case IDM_NEW_EMAIL: { switch (GetItemType()) { case MAGIC_MAIL: case MAGIC_CONTACT: case MAGIC_CALENDAR: case MAGIC_FILTER: { if (App) App->CreateItem(GetItemType(), this); break; } default: break; } break; } case IDM_MARK_SEND: { for (Thing *i: Items) { Mail *m = i->IsMail(); if (m) { m->SetFlags(MAIL_READY_TO_SEND | MAIL_READ | MAIL_CREATED); m->SetDirty(); m->Update(); } } break; } case IDM_EMAIL_GROUP: { if (App) { Mail *m = dynamic_cast(App->CreateItem(MAGIC_MAIL, NULL, false)); if (m) { MailUi *UI = dynamic_cast(m->DoUI()); if (UI) { List Cts; App->GetContacts(Cts, this); for (auto c: Cts) { UI->AddRecipient(c); } } } } break; } case IDM_FIND: { ScribeFolder *Folder = (ScribeFolder*) GetTree()->Selection(); OpenFinder(App, Folder); break; } case IDM_CREATE_SUB: { if (mt) mt->OnCreateSubDirectory(this); break; } case IDM_DELETE: { if (mt) mt->OnDelete(this, ForceDelete); break; } case IDM_RENAME: { auto Dlg = new FolderNameDlg(mt, GetName(true)); Dlg->DoModal([this, Dlg, mt](auto dlg, auto id) { if (id && ValidStr(Dlg->Name)) { // check for folder name conflicts... ScribeFolder *ParentFolder = GetFolder(); LString Path; if (ParentFolder) Path = ParentFolder->GetPath(); if (Path) { char s[256]; sprintf_s(s, sizeof(s), "%s/%s", Path.Get(), Dlg->Name); if (App->GetFolder(s)) { LgiMsg(mt, LLoadString(IDS_SUBFLD_NAME_CLASH), AppName, MB_OK); return; } } // change the folders name... OnRename(Dlg->Name); } delete dlg; }); break; } case IDM_EXPORT: { LString DropName = LGetLeaf(GetDropFileName()); if (!DropName) { LgiTrace("%s:%i - Failed to create folder name.\n", _FL); break; } auto s = new LFileSelect(mt); s->Name(DropName); s->Save([this, ExportMimeType](auto s, auto ok) { LAutoPtr mem(s); if (ok) { if (LFileExists(s->Name())) { LString a, b; a.Printf(LLoadString(IDS_ERROR_FILE_EXISTS), s->Name()); b.Printf("\n%s\n", LLoadString(IDS_ERROR_FILE_OVERWRITE)); if (LgiMsg(GetTree(), a + b, AppName, MB_YESNO) == IDNO) return; } LAutoPtr f(new LFile); if (!f || !f->Open(s->Name(), O_WRITE)) { LgiTrace("%s:%i - Failed to open '%s' for writing.\n", _FL, s->Name()); return; } f->SetSize(0); LAutoPtr str(f.Release()); ExportAsync(str, ExportMimeType); } }); break; } case IDM_EMPTY: { if (App->GetMailList()) { App->GetMailList()->RemoveAll(); LArray Del; for (ScribeFolder *c = GetChildFolder(); c; c = c->GetNextFolder()) Del.Add(c->GetObject()); if (Del.Length()) GetObject()->GetStore()->Delete(Del, false); DeleteAllThings([mt](auto status) { mt->Invalidate(); }); } break; } case IDM_SORT_SUBFOLDERS: { SortSubfolders(); break; } case IDM_PROPERTIES: { mt->OnProperties(this); break; } case IDM_COPY_PATH: { LClipBoard c(GetTree()); #ifdef WINDOWS LAutoWString w(Utf8ToWide(GetPath())); c.TextW(w); #else c.Text(GetPath()); #endif break; } case IDM_COLLECT_MAIL: { CollectSubFolderMail(); break; } case IDM_MARK_ALL_READ: { LArray Change; for (auto c : Items) { Mail *m = c->IsMail(); if (m) { if (!TestFlag(m->GetFlags(), MAIL_READ)) Change.Add(m->GetObject()); } } LVariant v = MAIL_READ; if (GetObject()->GetStore()->Change(Change, FIELD_FLAGS, v, OpPlusEquals) == Store3Error) { LProgressDlg Prog(GetTree(), 500); Prog.SetRange(Change.Length()); // FIXME!! // Prog.SetYieldTime(200); Prog.SetDescription("Marking email..."); for (auto c : Change) { auto t = CastThing(c); Mail *m = t ? t->IsMail() : NULL; if (m) m->SetFlags(m->GetFlags() | MAIL_READ, false, false); Prog++; if (Prog.IsCancelled()) break; } OnUpdateUnRead(0, true); } break; } case IDM_MERGE_FILE: { auto s = new LFileSelect(mt); s->Type("Email Template", "*.txt;*.eml"); s->Open([this](auto dlg, auto id) { if (id) { LArray Recip; for (auto i: Items) { Recip.Add(new ListAddr(i->IsContact())); } App->MailMerge(Recip, dlg->Name(), 0); Recip.DeleteObjects(); } delete dlg; }); break; } case IDM_UNDELETE: { const char *s = DomToStr(SdUndelete); GetFldObj()->OnCommand(s); break; } case IDM_EXPUNGE: { const char *s = DomToStr(SdExpunge); GetFldObj()->OnCommand(s); break; } case IDM_REFRESH: { const char *s = DomToStr(SdRefresh); GetFldObj()->OnCommand(s); break; } default: { Mail *Template = Templates[Msg - IDM_MERGE_TEMPLATE_BASE]; if (Template) { LArray Recip; for (auto i: Items) { Recip.Add(new ListAddr(i->IsContact())); } App->MailMerge(Recip, 0, Template); Recip.DeleteObjects(); } else { // Handle any installed callbacks for menu items for (unsigned i=0; iExecuteScriptCallback(Cb, Args); } } } break; } } } bool ScribeFolder::IsInTrash() { ScribeFolder *p = this; while ((p = p->GetFolder())) { if (p->GetItemType() == MAGIC_ANY) return true; } return false; } /// This just adds certain folder types as a group ware source at the app level void ScribeFolder::OnItemType() { switch (GetItemType()) { case MAGIC_CONTACT: case MAGIC_FILTER: case MAGIC_CALENDAR: case MAGIC_GROUP: App->AddThingSrc(this); break; default: break; } } bool ScribeFolder::LoadFolders() { // static int64 Last = 0; auto FldObj = GetFldObj(); if (!FldObj) return true; CurState = FldState_Loading; FoldersCurrentlyLoading++; LHashTbl, ScribeFolder*> Loaded; for (ScribeFolder *c = GetChildFolder(); c; c = c->GetNextFolder()) { Loaded.Add(c->GetObject(), c); } LDataFolderI *s; auto &Sub = FldObj->SubFolders(); for (unsigned sIdx = 0; Sub.GetState() == Store3Loaded && sIdx < Sub.Length(); sIdx++) { if (!(s = Sub[sIdx])) break; if (!Loaded.Find(s)) { ScribeFolder *n = new ScribeFolder; if (n) { n->App = App; n->SetObject(s, false, _FL); Insert(n); SetWillDirty(false); Expanded(GetOpen() != 0); SetWillDirty(true); n->LoadFolders(); n->OnItemType(); } #if 0 int64 Now = LCurrentTime(); if (Now - Last > 500) { LYield(); Last = Now; } #endif } } LTreeItem::Items.Sort(ThingContainerIdxCmp); CurState = FldState_Idle; FoldersCurrentlyLoading--; return true; } class LoadProgressItem : public LListItem { int n; int Total; int64 Last; public: LoadProgressItem(int t) { n = 0; Total = t; Last = LCurrentTime(); } void SetPos(int i) { n = i; if (n % 32 == 0) { int64 Now = LCurrentTime(); if (Now > Last + 500) { if (Parent) { Parent->Invalidate(&Pos, true); #ifdef MAC LYield(); #endif } Last = Now; } LSleep(0); } } void OnPaint(ItemPaintCtx &Ctx) { int x = n * 150 / Total; LRect &r = Ctx; Ctx.pDC->Colour(L_FOCUS_SEL_BACK); Ctx.pDC->Rectangle(r.x1, r.y1, r.x1 + x, r.y2); Ctx.pDC->Colour(L_MED); Ctx.pDC->Rectangle(r.x1 + x + 1, r.y1, r.x1 + 150, r.y2); Ctx.pDC->Colour(L_WORKSPACE); Ctx.pDC->Rectangle(r.x1 + 151, r.y1, r.x2, r.y2); } }; class LoadingItem : public LListItem { LDataIterator *Iter; LString s; public: LoadingItem(LDataIterator *it) { _UserPtr = NULL; if ((Iter = it)) { Iter->SetProgressFn([this](ssize_t pos, ssize_t sz) { this->s.Printf("%.1f%%", (double)pos * 100 / sz); this->Update(); }); } } ~LoadingItem() { if (Iter) Iter->SetProgressFn(NULL); } bool SetText(const char *str, int i) { s = str; Update(); return true; } void OnPaint(LItem::ItemPaintCtx &Ctx) { LString msg; auto loading = LLoadString(IDS_LOADING); if (s) msg.Printf("%s %s", loading, s.Get()); else msg = loading; LDisplayString d(LSysFont, msg); LSysFont->Colour(L_TEXT, L_WORKSPACE); LSysFont->Transparent(false); d.Draw(Ctx.pDC, (Ctx.X()-d.X())/2, Ctx.y1, &Ctx); } void Select(bool b) { } }; bool ScribeFolder::UnloadThings() { bool Status = true; if (IsLoaded()) { // LgiTrace("Unloading %s, Items=%i\n", GetPath(), Items.Length()); // Emptying the item list, leave the store nodes around though Thing *t; while ((t = Items[0])) { if (t->GetDirty()) { t->Save(0); } t->SetObject(NULL, false, _FL); t->DecRef(); } Items.Empty(); IsLoaded(false); GetObject()->SetInt(FIELD_LOADED, false); } return Status; } #define DEBUG_PROF_LOAD_THINGS 0 #if DEBUG_PROF_LOAD_THINGS #define PROFILE(str) prof.Add("Add"); .Add(str) #else #define PROFILE(str) #endif void ScribeFolder::ContinueLoading(int OldUnread, std::function Callback) { auto FldObj = GetFldObj(); WhenLoaded(_FL, [this, OldUnread, Callback, FldObj]() { // This is called when all the Store3 objects are loaded int Unread = OldUnread; if (Unread < 0) Unread = GetUnRead(); Loading.Reset(); auto &Children = GetFldObj()->Children(); if (Children.GetState() != Store3Loaded) { LAssert(!"Really should be loaded by now."); return; } for (auto c = Children.First(); c; c = Children.Next()) { auto t = CastThing(c); if (t) { // LAssert(Items.HasItem(t)); } else if ((t = App->CreateThingOfType((Store3ItemTypes) c->Type(), c))) { t->SetObject(c, false, _FL); t->SetParentFolder(this); t->OnSerialize(false); } } int NewUnRead = 0; for (auto t: Items) { if (t->GetFolder() != this) { #ifdef _DEBUG char s[256]; sprintf_s(s, sizeof(s), "%s:%i - Error, thing not parented correctly: this='%s', child='%x'\n", _FL, GetText(0), t->GetObject() ? t->GetObject()->Type() : 0); printf("%s", s); LgiMsg(App, s, AppName); #endif t->SetFolder(this); } Mail *m = t->IsMail(); if (m) NewUnRead += (m->GetFlags() & MAIL_READ) ? 0 : 1; t->SetFieldArray(FieldArray); } if (Unread != NewUnRead) OnUpdateUnRead(NewUnRead - Unread, false); Update(); if (d->IsInbox < 0 && App) { d->IsInbox = App->GetFolder(FOLDER_INBOX) == this; if (d->IsInbox > 0) UpdateOsUnread(); } if (Callback) Callback(Store3Success); }, 0); if (!IsLoaded()) { bool Ui = Tree ? Tree->InThread() : false; if (Ui) Tree->Capture(false); auto &Children = FldObj->Children(); auto Status = Children.GetState(); if (Status != Store3Loaded) { if (View() && Loading.Reset(Ui ? new LoadingItem(&Children) : NULL)) View()->Insert(Loading); return; // Ie deferred or error... } IsLoaded(true); } } Store3Status ScribeFolder::LoadThings(LViewI *Parent, std::function Callback) { int OldUnRead = GetUnRead(); auto FldObj = GetFldObj(); if (!FldObj) { LgiTrace("%s:%i - No folder object.\n", _FL); return Store3Error; } if (!Parent) Parent = App; auto Path = GetPath(); if (!App || !Path) { LAssert(!"We should probably always have an 'App' and 'Path' ptrs..."); return Store3Error; } auto Access = App->GetAccessLevel( Parent, GetReadAccess(), Path, [this, OldUnRead, Callback](auto access) { if (access) ContinueLoading(OldUnRead, Callback); else if (Callback) Callback(Store3NoPermissions); }); if (Access == Store3Error) { // No read access: LgiTrace("%s:%i - Folder read access denied.\n", _FL); // Emptying the item list, leave the store nodes around though for (auto t: Items) t->SetObject(NULL, false, _FL); Items.Empty(); IsLoaded(false); Update(); } return Access; } void ScribeFolder::OnRename(char *NewName) { if (!NewName) return; int FolderType = App->GetFolderType(this); SetName(NewName, true); // Calls update too.. SetDirty(); if (FolderType >= 0) { // it's a system folder so reflect the changes in // the system folder tracking options. char KeyName[32]; sprintf_s(KeyName, sizeof(KeyName), "Folder-%i", FolderType); auto Path = GetPath(); if (Path) { LVariant v = Path.Get(); App->GetOptions()->SetValue(KeyName, v); } } } void ScribeFolder::OnDelete() { if (GetObject()) { char Msg[256]; sprintf_s(Msg, sizeof(Msg), LLoadString(IDS_DELETE_FOLDER_DLG), GetText()); int Result = LgiMsg(App, Msg, AppName, MB_YESNO); if (Result == IDYES) { if (App->GetMailList()) App->GetMailList()->RemoveAll(); ScribeFolder *Parent = GetFolder(); LArray Del; Del.Add(GetObject()); if (GetObject()->GetStore()->Delete(Del, true)) { if (Parent) Parent->Select(true); } } } } ScribeFolder *ScribeFolder::GetSubFolder(const char *Path) { ScribeFolder *Folder = 0; if (Path) { if (*Path == '/') Path++; char Name[256]; char *Sep = strchr((char*)Path, '/'); if (Sep) { ZeroObj(Name); memcpy(Name, Path, Sep-Path); } else { strcpy_s(Name, sizeof(Name), Path); } LTreeItem *Starts[2] = { GetChild(), IsRoot() ? GetNext() : 0 }; for (int s=0; !Folder && sGetNext()) { ScribeFolder *f = dynamic_cast(i); if (f) { auto n = f->GetName(true); if (n.Equals(Name)) { if (Sep) Folder = f->GetSubFolder(Sep+1); else Folder = f; break; } } } } } return Folder; } bool ScribeFolder::ReindexField(int OldIndex, int NewIndex) { if (!GetFldObj()) return false; auto &Flds = GetFldObj()->Fields(); if (GetFldObj()->Fields().Length() <= 0) return false; LDataPropI *f = Flds[OldIndex]; if (!f) return false; Flds.Delete(f); Flds.Insert(f, OldIndex < NewIndex ? NewIndex - 1 : NewIndex); int i=0; FieldArray.Length(0); for (f = Flds.First(); f; f = Flds.Next()) { auto Id = f->GetInt(FIELD_ID); FieldArray[i++] = (int)Id; } for (auto t: Items) t->SetFieldArray(FieldArray); SetDirty(); return true; } int ScribeFolder::_UnreadChildren() { int Status = 0; for (ScribeFolder *f=GetChildFolder(); f; f=f->GetNextFolder()) { int u = f->GetUnRead(); if (u > 0) Status += u; Status += f->_UnreadChildren(); } return Status; } void ScribeFolder::_PourText(LPoint &Size) { if (!d) return; const char *Text = GetText(); Size.x = Size.y = 0; if (Text && !d->DsBase) { if (GetUnRead() > 0 || ChildUnRead) { const char *StartCount = strrchr(Text, '('); ssize_t NameLen = StartCount ? StartCount-Text : strlen(Text); LFont *b = (App->GetBoldFont()) ? App->GetBoldFont() : GetTree()->GetFont(); d->DsBase.Reset(new LDisplayString(b, Text, NameLen)); d->DsUnread.Reset(new LDisplayString(GetTree()->GetFont(), StartCount)); } else { d->DsBase.Reset(new LDisplayString(GetTree()->GetFont(), Text)); } } if (d->DsBase) Size.x += d->DsBase->X(); if (d->DsUnread) Size.x += d->DsUnread->X(); Size.x += 4; Size.y = MAX(16, LSysFont->GetHeight()); } void ScribeFolder::_PaintText(LItem::ItemPaintCtx &Ctx) { if (!d) return; LRect *_Text = _GetRect(TreeItemText); if (d->DsBase) { LFont *f = d->DsBase->GetFont(); int Tab = f->TabSize(); f->TabSize(0); f->Transparent(false); f->Colour(Ctx.Fore, Ctx.TxtBack); d->DsBase->Draw(Ctx.pDC, _Text->x1 + 2, _Text->y1 + 1, _Text); if (d->DsUnread) { f = d->DsUnread->GetFont(); f->Transparent(true); LColour UnreadCol(App->GetColour(L_UNREAD_COUNT)); if ( abs ( (UnreadCol.GetGray() - Ctx.Back.GetGray()) ) < 80 ) { // too close.. use fore f->Colour(Ctx.Fore, Ctx.TxtBack); } else { // contrast ok.. use unread f->Colour(UnreadCol, Ctx.TxtBack); } d->DsUnread->Draw(Ctx.pDC, _Text->x1 + 2 + d->DsBase->X(), _Text->y1 + 1); } f->TabSize(Tab); if (Ctx.x2 > _Text->x2) { Ctx.pDC->Colour(Ctx.Back); Ctx.pDC->Rectangle(_Text->x2 + 1, Ctx.y1, Ctx.x2, Ctx.y2); } } else { Ctx.pDC->Colour(Ctx.Back); Ctx.pDC->Rectangle(&Ctx); } } bool ScribeFolder::Save(ScribeFolder *Into) { bool Status = false; if (GetObject()) { // saving a disk object Store3Status s = GetObject()->Save(); if (s != Store3Error) { Status = true; SetDirty(false); } else { LAssert(0); } } return Status; } void ScribeFolder::OnExpand(bool b) { if (b && LoadOnDemand) { DeleteObj(LoadOnDemand); if (App->ScribeState != ScribeWnd::ScribeExiting) { ScribeWnd::AppState Prev = App->ScribeState; App->ScribeState = ScribeWnd::ScribeLoadingFolders; LoadFolders(); if (App->ScribeState == ScribeWnd::ScribeExiting) { LCloseApp(); return; } App->ScribeState = Prev; } } OnUpdateUnRead(0, false); SetDirty(((uchar)b) != GetOpen()); SetOpen(b); } bool ScribeFolder::OnKey(LKey &k) { #ifndef WINDOWS // This is being done by the VK_APPS key on windows... if (k.IsContextMenu()) { if (k.Down()) { LMouse m; m.x = 5; m.y = 5; m.ViewCoords = true; m.Target = GetTree(); DoContextMenu(m); } return true; } #endif return false; } #define DefField(id, wid) \ { \ LDataPropI *Fld = Fields.Create(GetFldObj()->GetStore()); \ if (Fld) \ { \ Fld->SetInt(FIELD_ID, id); \ Fld->SetInt(FIELD_WIDTH, wid); \ Fields.Insert(Fld); \ } \ } void ScribeFolder::SetDefaultFields(bool Force) { if (!GetFldObj()) return; if (Force || GetFldObj()->Fields().Length() <= 0) { LDataIterator &Fields = GetFldObj()->Fields(); Fields.DeleteObjects(); int FolderType = App ? App->GetFolderType(this) : -1; if (FolderType == FOLDER_OUTBOX || FolderType == FOLDER_SENT) { DefField(FIELD_PRIORITY, 10); DefField(FIELD_FLAGS, 15); DefField(FIELD_TO, 150); DefField(FIELD_SUBJECT, 250); DefField(FIELD_SIZE, 80); DefField(FIELD_DATE_SENT, 100); } else { switch (GetItemType()) { case MAGIC_MAIL: { DefField(FIELD_PRIORITY, 10); DefField(FIELD_FLAGS, 10); DefField(FIELD_FROM, 150); DefField(FIELD_SUBJECT, 250); DefField(FIELD_SIZE, 80); DefField(FIELD_DATE_SENT, 100); break; } case MAGIC_CONTACT: { DefField(FIELD_FIRST_NAME, 200); DefField(FIELD_LAST_NAME, 200); DefField(FIELD_EMAIL, 300); break; } case MAGIC_CALENDAR: { DefField(FIELD_CAL_SUBJECT, 200); DefField(FIELD_CAL_START_UTC, 150); DefField(FIELD_CAL_END_UTC, 150); break; } case MAGIC_FILTER: { for (int i=0; DefaultFilterFields[i]; i++) { DefField(DefaultFilterFields[i], i == 0 ? 200 : 100); } break; } case MAGIC_GROUP: { DefField(FIELD_GROUP_NAME, 300); break; } default: break; } } // set for save SetDirty(Fields.Length() > 0); } else { - // already has fields, so don't interfer with them + // already has fields, so don't interfere with them } } LString ScribeFolder::GetPath() { LString::Array p; ScribeFolder *f = this; while (f) { - p.Add(f->GetName(true)); + auto name = f->GetName(true); + if (name) + p.Add(name); f = dynamic_cast(f->GetParent()); } LString dir = "/"; return dir + dir.Join(p.Reverse()); } void ScribeFolder::SetName(const char *Name, bool Encode) { if (Name) { d->DsBase.Reset(); d->DsUnread.Reset(); NameCache.Reset(); if (GetObject()) GetObject()->SetStr(FIELD_FOLDER_NAME, Name); SetText(Name); // Calls update } } LString ScribeFolder::GetName(bool Decode) { if (!GetObject()) return NULL; auto In = GetObject()->GetStr(FIELD_FOLDER_NAME); if (!In) return NULL; if (Decode) return LUrlDecode(In); else return In; } void ScribeFolder::Update() { if (Tree && !Tree->InThread()) { // LgiTrace("%s:%i - Update not in thread?\n", _FL); return; } d->DsBase.Reset(); d->DsUnread.Reset(); NameCache.Reset(); LTreeItem::Update(); } const char *ScribeFolder::GetText(int i) { if (!NameCache) { LString Name = GetName(true); if (!Name) return "...loading..."; size_t NameLen = strlen(Name) + 40; NameCache.Reset(new char[NameLen]); if (NameCache) { bool ShowTotals = false; LVariant v; if (App->GetOptions()->GetValue(OPT_ShowFolderTotals, v)) ShowTotals = v.CastInt32() != 0; int c = sprintf_s(NameCache, NameLen, "%s", Name.Get()); auto ItemType = GetItemType(); if (ItemType && (ShowTotals || GetUnRead() || ChildUnRead)) { c += sprintf_s(NameCache+c, NameLen-c, " ("); if (ItemType == MAGIC_MAIL || ItemType == MAGIC_ANY) { const char *Ch = ChildUnRead ? "..." : ""; if (ShowTotals) c += sprintf_s(NameCache+c, NameLen-c, "%i%s/", GetUnRead(), Ch); else if (GetUnRead()) c += sprintf_s(NameCache+c, NameLen-c, "%i%s", GetUnRead(), Ch); else if (ChildUnRead) c += sprintf_s(NameCache+c, NameLen-c, "..."); } if (ShowTotals) { if (IsLoaded()) c += sprintf_s(NameCache+c, NameLen-c, "%zi", Items.Length()); else c += sprintf_s(NameCache+c, NameLen-c, "?"); } c += sprintf_s(NameCache+c, NameLen-c, ")"); } } } return NameCache; } int ScribeFolder::GetImage(int Flags) { if (GetItemType() == MAGIC_ANY) { return ICON_TRASH; } if (Flags) { return ICON_OPEN_FOLDER; } switch (GetItemType()) { case MAGIC_MAIL: return ICON_FOLDER_MAIL; case MAGIC_CONTACT: return ICON_FOLDER_CONTACTS; case MAGIC_FILTER: return ICON_FOLDER_FILTERS; case MAGIC_CALENDAR: return ICON_FOLDER_CALENDAR; case MAGIC_NONE: return ICON_MAILBOX; default: return ICON_CLOSED_FOLDER; } } class NullMail : public Mail { Thing &operator =(Thing &c) override { return *this; } public: NullMail(ScribeWnd *App, MContainer *c, ScribeFolder *f) : Mail(App) { SetFlags(MAIL_READ | MAIL_CREATED); Container = c; SetParentFolder(f); SetSubject((char*)LLoadString(IDS_MISSING_PARENT)); } ~NullMail() { SetParentFolder(NULL); } bool IsPlaceHolder() override { return true; } const char *GetText(int i) override { // Clear all the fields return 0; } int Compare(LListItem *Arg, ssize_t Field) override { // Use the first mail to sort into position if (Container) { MContainer *c = Container->Children.Length() ? Container->Children[0] : 0; if (c && c->Message) { return c->Message->Compare(Arg, Field); } } return -1; } void OnMouseClick(LMouse &m) override { // Disable the mouse click } bool SetDirty(bool b = true) override { // Don't set it to dirty, otherwise the dirty object // clean up code will save it into the outbox. return true; } }; template int TrashCompare(T *pa, T *pb, NativeInt Data) { ScribeFolder *f = (ScribeFolder*)Data; Thing *a = dynamic_cast(pa); Thing *b = dynamic_cast(pb); if (!a || !b) return 0; int type = a->Type() - b->Type(); if (type) return type; int col = f->GetSortCol(); int *defs = a->GetDefaultFields(); if (!defs || !defs[col]) return 0; return (f->GetSortAscend() ? 1 : -1) * a->Compare(b, defs[col]); } template int TrashCompare(LListItem *pa, LListItem *pb, NativeInt Data); int ListItemCompare(LListItem *a, LListItem *b, NativeInt Data) { ScribeFolder *f = (ScribeFolder*)Data; return (f->GetSortAscend() ? 1 : -1) * a->Compare(b, f->GetSortField()); } int ThingCompare(Thing *a, Thing *b, NativeInt Data) { ScribeFolder *f = (ScribeFolder*)Data; return (f->GetSortAscend() ? 1 : -1) * a->Compare(b, f->GetSortField()); } int ThingSorter(Thing *a, Thing *b, ThingSortParams *Params) { return (Params->SortAscend ? 1 : -1) * a->Compare(b, Params->SortField); } bool ScribeFolder::Thread() { bool Status = false; if (GetItemType() == MAGIC_MAIL) { int Thread = GetThreaded(); ThingList *Lst = View(); List m; for (auto t: Items) { Mail *e = t->IsMail(); if (e) { if (Thread) { m.Insert(e); } else if (e->Container) { DeleteObj(e->Container); } } } if (Lst && m[0]) { // Thread LArray Containers; MContainer::Thread(m, Containers); LAssert(m.Length() == Items.Length()); // Insert blank items for missing thread parents for (unsigned i=0; iMessage) { LAssert(c->Children.Length() > 1); c->Message = new NullMail(App, c, this); if (c->Message) { Lst->PlaceHolders.Insert(c->Message); if (View() && c->Message) { c->Message->SetFieldArray(FieldArray); Items.Insert(c->Message); } } } } // Sort root list LArray Containers2 = Containers; ThingSortParams Params; Params.SortAscend = GetSortAscend(); Params.SortField = GetSortField(); #if 0 for (int i=0; iMessage ? Containers[i]->Message->GetFieldText(Params.SortField) : NULL; LgiTrace("%p(%s)\n", Containers[i], Str1); } Containers.Sort(ContainerCompare); LQuickSort(Base, Containers2.Length(), ContainerSorter, &Params); for (int i=0; iMessage ? Containers[i]->Message->GetFieldText(Params.SortField) : NULL; char *Str2 = Containers2[i]->Message ? Containers2[i]->Message->GetFieldText(Params.SortField) : NULL; LgiTrace("%p(%s), %p(%s) - %s\n", Containers[i], Str1, Containers2[i], Str2, Containers[i]!=Containers2[i]?"DIFFERENT":""); } #else MContainer **Base = &Containers[0]; if (Containers.Length() > 1) LQuickSort(Base, Containers.Length(), ContainerSorter, &Params); /* for (int i=0; iMessage ? Containers[i]->Message->GetFieldText(Params.SortField) : NULL; LgiTrace("%p(%s)\n", Containers[i]->Message, Str1); } LgiTrace("\n"); */ #endif // Position and index the thread tree int Index = 0; for (unsigned i=0; iPour(Index, 0, 0, i < Containers.Length()-1, &Params); } // Sort all the items by index Items.Sort(ContainerIndexer); Status = true; /* int Idx = 0; for (Thing *t = Items.First(); t; t = Items.Next(), Idx++) { Mail *m = t->IsMail(); if (m) { char *Str = m->GetFieldText(Params.SortField); LgiTrace("%i,%i %p %s\n", Idx, m->Container ? m->Container->Index : -1, m, Str); } } */ } } else { for (auto t: Items) { Mail *e = t->IsMail(); if (e) DeleteObj(e->Container); } } return Status; } struct SortPairInt { Thing *t; uint64 ts; void SetDate(Thing *th, int sf) { t = th; auto obj = th->GetObject(); // Store3State loaded = (Store3State)obj->GetInt(FIELD_LOADED); auto dt = obj->GetDate(sf); ts = dt && dt->Year() ? dt->Ts() : 0; } void SetInt(Thing *th, int sf) { t = th; auto obj = th->GetObject(); // Store3State loaded = (Store3State)obj->GetInt(FIELD_LOADED); ts = obj->GetInt(sf); } static int Compare(SortPairInt *a, SortPairInt *b) { int64_t diff = (int64_t)a->ts - (int64_t)b->ts; if (diff < 0) return -1; return diff > 0 ? 1 : 0; } }; struct SortPairStr { Thing *t; const char *ts; void SetStr(Thing *th, int sf) { t = th; ts = th->GetObject()->GetStr(sf); } static int Compare(SortPairStr *a, SortPairStr *b) { return stricmp(a->ts ? a->ts : "", b->ts ? b->ts : ""); } }; bool ScribeFolder::SortItems() { int sf = GetSortField(); LVariantType type = GV_NULL; auto StartTs = LCurrentTime(); const static int TimeOut = 2/*sec*/ * 1000; switch (sf) { case FIELD_DATE_SENT: case FIELD_DATE_RECEIVED: type = GV_DATETIME; break; case FIELD_SUBJECT: type = GV_STRING; break; default: return false; } LArray intPairs; LArray strPairs; bool intType = type == GV_DATETIME || type == GV_INT32 || type == GV_INT64; if (intType) { intPairs.Length(Items.Length()); int n = 0; auto s = intPairs.AddressOf(); if (type == GV_DATETIME) { for (auto i: Items) { s[n++].SetDate(i, sf); if (n % 50 == 0 && LCurrentTime() - StartTs >= TimeOut) return false; } } else { for (auto i: Items) { s[n++].SetInt(i, sf); if (n % 50 == 0 && LCurrentTime() - StartTs >= TimeOut) return false; } } intPairs.Sort(SortPairInt::Compare); } else if (type == GV_STRING) { strPairs.Length(Items.Length()); int n = 0; auto s = strPairs.AddressOf(); for (auto i: Items) s[n++].SetStr(i, sf); strPairs.Sort(SortPairStr::Compare); } Items.Empty(); if (intType) { if (GetSortAscend()) for (auto i: intPairs) Items.Add(i.t); else for (auto it = intPairs.rbegin(); it != intPairs.end(); it--) Items.Add((*it).t); } else { if (GetSortAscend()) for (auto i: strPairs) Items.Add(i.t); else for (auto it = strPairs.rbegin(); it != strPairs.end(); it--) Items.Add((*it).t); } return true; } void ScribeFolder::Populate(ThingList *list) { LProfile Prof("ScribeFolder::Populate", 1000); App->OnSelect(); if (!GetFldObj() || !list) return; CurState = FldState_Populating; ScribeFolder *Prev = list->GetContainer(); bool Refresh = Prev == this; // Remove old items from list Prof.Add("Delete Placeholders"); list->DeletePlaceHolders(); list->RemoveAll(); if (!Refresh || list->GetColumns() == 0 || GetItemType() == MAGIC_FILTER) { if (Prev) { // save previous folders settings Prev->SerializeFieldWidths(); } Prof.Add("Empty cols"); LVariant GridLines; if (App->GetOptions()->GetValue(OPT_GridLines, GridLines)) { list->DrawGridLines(GridLines.CastInt32() != 0); } list->EmptyColumns(); Prof.Add("Set def fields"); bool ForceDefaultFields = GetItemType() == MAGIC_FILTER; if (GetFldObj()->Fields().Length() <= 0 || ForceDefaultFields) { SetDefaultFields(ForceDefaultFields); } // Add fields to list view int n = 0; LArray Empty; for (auto t: Items) { t->SetFieldArray(Empty); } LRect *Bounds = 0; if (App->GetIconImgList()) { Bounds = App->GetIconImgList()->GetBounds(); } switch (GetItemType()) { case MAGIC_ANY: { list->AddColumn("", 170); list->AddColumn("", 170); list->AddColumn("", 170); list->AddColumn("", 170); break; } default: { n = 0; FieldArray.Length(0); for (LDataPropI *i = GetFldObj()->Fields().First(); i; i = GetFldObj()->Fields().Next()) { int FieldId = (int)i->GetInt(FIELD_ID); const char *FName = LLoadString(FieldId); int Width = (int)i->GetInt(FIELD_WIDTH); const char *FieldText = FName ? FName : i->GetStr(FIELD_NAME); LAssert(FieldText != NULL); LItemColumn *c = list->AddColumn(FieldText, Width); if (c) { switch (i->GetInt(FIELD_ID)) { case FIELD_PRIORITY: { int x = 12; if (Bounds) { x = Bounds[ICON_PRIORITY_BLACK].X() + 7; } c->Width(x); c->Image(ICON_PRIORITY_BLACK); c->Resizable(false); break; } case FIELD_FLAGS: { int x = 14; if (Bounds) { x = Bounds[ICON_FLAGS_BLACK].X() + 7; } c->Width(x); c->Image(ICON_FLAGS_BLACK); c->Resizable(false); break; } case FIELD_SIZE: { c->TextAlign(LCss::Len(LCss::AlignRight)); break; } } } FieldArray[n++] = (int)i->GetInt(FIELD_ID); } break; } } // Add all items to list if (View()) { View()->SetContainer(this); // tell the list who we are if (GetSortCol() >= 0) { // set current sort settings View()->SetSort(GetSortCol(), GetSortAscend()); } } Prof.Add("Load things"); // FIXME: LoadThings(); } // Filter List Is; // Do any threading/sorting static LString SortMsg; if (!Thread() && GetSortField()) { SortMsg.Printf("Sorting " LPrintfInt64 " items", Items.Length()); Prof.Add(SortMsg); if (GetItemType() == MAGIC_ANY) { Items.Sort(TrashCompare, (NativeInt)this); } else { // Sort.. // if (!SortItems()) Items.Sort(ThingCompare, (NativeInt)this); } } // Do any filtering... Prof.Add("Filtering"); ThingFilter *Filter = App->GetThingFilter(); auto FilterStart = LCurrentTime(); size_t Pos = 0; for (auto t: Items) { t->SetFieldArray(FieldArray); if (!Filter || Filter->TestThing(t)) { // Add anyway... because all items are not part of list Is.Insert(t); } if ((LCurrentTime()-FilterStart) > 300) { if (!Loading && Loading.Reset(new LoadingItem(NULL))) list->Insert(Loading, 0); if (Loading) { LString s; s.Printf(LPrintfInt64 " of " LPrintfInt64 ", %.1f%%", Pos, Items.Length(), (double)Pos * 100 / Items.Length()); Loading->SetText(s); } FilterStart = LCurrentTime(); } Pos++; } Prof.Add("Inserting"); if (View() && Is[0]) { View()->Insert(Is, -1, true); } Prof.Add("Deleting"); list->DeletePlaceHolders(); Loading.Reset(); Prof.Add("OnSelect"); GetFldObj()->OnSelect(true); CurState = FldState_Idle; } void ScribeFolder::OnUpdateUnRead(int Offset, bool ScanItems) { if (!d->InUpdateUnread) { d->InUpdateUnread = true; int OldUnRead = GetUnRead(); d->DsBase.Reset(); d->DsUnread.Reset(); NameCache.Reset(); if (ScanItems) { if (GetItemType() == MAGIC_MAIL || GetItemType() == MAGIC_ANY) { size_t Count = 0; for (auto t: Items) { Mail *m = t->IsMail(); if (m && !TestFlag(m->GetFlags(), MAIL_READ)) Count++; } SetUnRead((int32_t)Count); } } else if (Offset != 0) { SetUnRead(GetUnRead() + Offset); } if (GetUnRead() < 0) SetUnRead(0); if (OldUnRead != GetUnRead()) { for (LTreeItem *i = GetParent(); i; i = i->GetParent()) { ScribeFolder *tc = dynamic_cast(i); if (tc && tc->GetParent()) tc->OnUpdateUnRead(0, false); } SetDirty(); if (d->IsInbox > 0) UpdateOsUnread(); } ChildUnRead = Expanded() ? 0 : _UnreadChildren(); Update(); d->InUpdateUnread = false; } } void ScribeFolder::EmptyFieldList() { if (GetFldObj()) GetFldObj()->Fields().DeleteObjects(); } void ScribeFolder::SerializeFieldWidths(bool Write) { if (GetFldObj() && View()) { // LAssert(View()->GetColumns() == Object->Fields().Length()); int Cols = MIN(View()->GetColumns(), (int)GetFldObj()->Fields().Length()); for (int i=0; iColumnAt(i); LDataPropI *f = GetFldObj()->Fields()[i]; if (c && f) { if (f->GetInt(FIELD_WIDTH) != c->Width()) { if (Write) { c->Width((int)f->GetInt(FIELD_WIDTH)); } else { f->SetInt(FIELD_WIDTH, c->Width()); SetDirty(); } } } else LAssert(0); } } } void ScribeFolder::OnProperties(int Tab) { if (!GetObject()) return; SerializeFieldWidths(); if (View()) { SetSort(View()->GetSortCol(), View()->GetSortAscending()); } OpenFolderProperties(this, Tab, [this](auto repop) { if (repop) { SetDirty(); SerializeFieldWidths(true); Populate(View()); } }); } ScribeFolder *ScribeFolder::CreateSubDirectory(const char *Name, int Type) { ScribeFolder *NewFolder = 0; auto ThisObj = dynamic_cast(GetObject()); if (Name && ThisObj && ThisObj->GetStore()) { LDataI *Fld = GetObject()->GetStore()->Create(MAGIC_FOLDER); if (Fld) { LDataFolderI *Obj = dynamic_cast(Fld); if (Obj) { NewFolder = new ScribeFolder; if (NewFolder) { NewFolder->App = App; NewFolder->SetObject(Obj, false, _FL); // Set name and type NewFolder->SetName(Name, true); NewFolder->GetObject()->SetInt(FIELD_FOLDER_TYPE, Type); ThisObj->SubFolders(); if (NewFolder->GetObject()->Save(ThisObj)) { Insert(NewFolder); NewFolder->SetDefaultFields(); NewFolder->OnItemType(); } else { DeleteObj(NewFolder); } } } } } return NewFolder; } bool ScribeFolder::Delete(LArray &Items, bool ToTrash) { if (!App) return false; List NotNew; LArray Del; LDataStoreI *Store = NULL; for (auto i: Items) { if (i->IsPlaceHolder()) { i->DecRef(); } else { Mail *m = i->IsMail(); if (m) NotNew.Insert(m); auto ObjStore = i->GetObject() ? i->GetObject()->GetStore() : NULL; if (!Store) Store = ObjStore; if (Store == ObjStore) Del.Add(i->GetObject()); else LAssert(!"All objects must have the same store."); } } if (NotNew.Length()) App->OnNewMail(&NotNew, false); if (Del.Length() == 0) return true; if (!Store) { LgiTrace("%s:%i - No Store?\n", _FL); return false; } if (!Store->Delete(Del, ToTrash)) { LgiTrace("%s:%i - Store.Delete failed.\n", _FL); return false; } return true; } class MoveToState { ScribeWnd *App = NULL; // Input ScribeFolder *Folder = NULL; LArray Items; bool CopyOnly; std::function&)> Callback; // Output bool Result = false; // Overall success/failure LArray Status; // Per item status // State LArray InStoreMove; // Object in the same store... LDataI *FolderObj = NULL; LDataStoreI *FolderStore = NULL; ScribeMailType NewBayesType = BayesMailUnknown; LHashTbl, int> Map; int NewFolderType = -1; bool BuildDynMenus = false; bool BayesInc = false; size_t Moves = 0; // Returns true if the object is deleted. bool SetStatus(int i, Store3Status s) { LAssert(Status[i] == Store3NotImpl); Status[i] = s; LAssert(Moves > 0); Moves--; if (Moves > 0) return false; OnMovesDone(); return true; } public: MoveToState(ScribeFolder *folder, LArray &items, bool copyOnly, std::function&)> callback) : Folder(folder), Items(items), CopyOnly(copyOnly), Callback(callback) { // Validate parameters if (Folder && (App = Folder->App)) { LVariant v; if (App->GetOptions()->GetValue(OPT_BayesIncremental, v)) BayesInc = v.CastInt32() != 0; } else { delete this; return; } if ((FolderObj = Folder->GetObject())) { FolderStore = Folder->GetObject()->GetStore(); } Status.Length(Moves = Items.Length()); for (auto &s: Status) s = Store3NotImpl; auto FolderItemType = Folder->GetItemType(); auto ThisFolderPath = Folder->GetPath(); NewBayesType = App->BayesTypeFromPath(ThisFolderPath); NewFolderType = App->GetFolderType(Folder); ScribeFolder *TemplatesFolder = App->GetFolder(FOLDER_TEMPLATES); BuildDynMenus = Folder == TemplatesFolder; for (unsigned i=0; iGetObject()) { if (SetStatus(i, Store3Error)) return; continue; } auto ThingItemType = t->Type(); if (FolderItemType != ThingItemType && FolderItemType != MAGIC_ANY) { if (SetStatus(i, Store3Error)) return; continue; } ScribeFolder *Old = t->GetFolder(); LString Path; if (Old) { Path = Old->GetPath(); if (Old == TemplatesFolder) // Moving to or from the templates folder... update the menu BuildDynMenus = true; } if (Old && Path) { bool IsDeleted = false; App->GetAccessLevel(App, Old->GetFolderPerms(ScribeWriteAccess), Path, [this, i, t, &IsDeleted](bool Allow) { if (Allow) IsDeleted = Move(i, t); else IsDeleted = SetStatus(i, Store3NoPermissions); }); // If the callback has already been executed and the object is deleted, exit immediately. if (IsDeleted) return; } else { if (Move(i, t)) return; } } } // This must call SetStatus once and only once for each item it's called with. // Returns true if the SetStatus call indicates deletion. // 'this' will be invalid after SetStatus returns true. bool Move(int i, Thing *t) { ScribeMailType OldBayesType = BayesMailUnknown; if (BayesInc && t->IsMail() && TestFlag(t->IsMail()->GetFlags(), MAIL_READ)) { OldBayesType = App->BayesTypeFromPath(t->IsMail()); } ScribeFolder *Old = t->GetFolder(); Store3Status r = Store3NotImpl; int OldFolderType = Old ? App->GetFolderType(Old) : -1; if ( (OldFolderType == FOLDER_TRASH || OldFolderType == FOLDER_SENT) && NewFolderType == FOLDER_TRASH) { // Delete for good r = Old ? Old->DeleteThing(t, NULL) : Store3Error; } else { // If this folder is currently selected... if (Folder->Select()) { // Insert item into list t->SetFieldArray(Folder->FieldArray); } if (CopyOnly) { LDataI *NewT = FolderStore->Create(t->Type()); if (NewT) { NewT->CopyProps(*t->GetObject()); r = NewT->Save(Folder->GetObject()); } else { r = Store3Error; } } else { if (NewFolderType != FOLDER_TRASH && OldBayesType != NewBayesType) { App->OnBayesianMailEvent(t->IsMail(), OldBayesType, NewBayesType); } // Move to this folder auto o = t->GetObject(); if (o && o->GetStore() == FolderStore) { InStoreMove.Add(o); Map.Add(t, i); r = Store3Delayed; } else { // Out of store more... use the old single object method... for the moment.. r = t->SetFolder(Folder); if (r == Store3Success) { // Remove from the list.. if (Old && Old->Select() && App->GetMailList()) App->GetMailList()->Remove(t); } } } } if (r == Store3Success) t->OnMove(); return SetStatus(i, r); } void OnMovesDone() { if (InStoreMove.Length()) { Store3Status s = Store3NotImpl; auto Fld = dynamic_cast(Folder->GetObject()); if (!Fld) s = Store3Error; else s = FolderStore->Move(Fld, InStoreMove); Result = s >= Store3Delayed; for (auto p: Map) { Status[p.value] = s; if (s == Store3Success) { LAssert(p.key->GetFolder() == Folder); LAssert(Items.HasItem(p.key)); p.key->OnMove(); } } } if (BuildDynMenus) // Moving to or from the templates folder... update the menu App->BuildDynMenus(); if (Callback) Callback(Result, Status); delete this; } }; void ScribeFolder::MoveTo(LArray &Items, bool CopyOnly, std::function&)> Callback) { if (Items.Length() == 0) return; if (!GetObject() || !App) return; new MoveToState(this, Items, CopyOnly, Callback); } int ThingFilterCompare(Thing *a, Thing *b, NativeInt Data) { Filter *A = a->IsFilter(); Filter *B = b->IsFilter(); return (A && B) ? A->GetIndex() - B->GetIndex() : 0; } void ScribeFolder::ReSort() { if (View() && Select()) { View()->SetSort(GetSortCol(), GetSortAscend()); } } void ScribeFolder::SetSort(int Col, bool Ascend, bool CanDirty) { if (GetItemType() == MAGIC_FILTER) { // Remove any holes in the indexing int i = 1; Items.Sort(ThingFilterCompare); for (auto t : Items) { Filter *f = t->IsFilter(); if (f) { if (f->GetIndex() != i) { f->SetIndex(i); } i++; } } } if (GetSortCol() != Col || GetSortAscend() != (uchar)Ascend) { GetObject()->SetInt(FIELD_SORT, (Col + 1) * (Ascend ? 1 : -1)); if (CanDirty) SetDirty(); } } int ScribeFolder::GetSortField() { int Status = 0; int Col = GetSortCol(); if (Col >= 0 && Col < (int)FieldArray.Length()) Status = FieldArray[Col]; return Status; } class FolderStream : public LStream { ScribeFolder *f; // Current index into the thing array int Idx; // Total known size of stream... int64 Sz; // A memory buffer containing just the encoded 'Thing' LMemStream Mem; LString FolderMime; public: FolderStream(ScribeFolder *folder) : Mem(512 << 10) { f = folder; Idx = 0; Sz = 0; switch (f->GetItemType()) { case MAGIC_MAIL: FolderMime = sMimeMbox; break; case MAGIC_CONTACT: FolderMime = sMimeVCard; break; case MAGIC_CALENDAR: FolderMime = sMimeVCalendar; break; default: LAssert(!"Need a mime type?"); break; } } int GetIndex() { return Idx; } int Open(const char *Str = 0,int Int = 0) { return true; } bool IsOpen() { return true; } int Close() { return 0; } int64 GetSize() { return -1; } int64 SetSize(int64 Size) { return -1; } int64 GetPos() { return -1; } int64 SetPos(int64 pos) { // This means that IStream::Seek return E_NOTIMPL, which is important // for windows to support copies of unknown size. return -1; } ssize_t Read(void *Buffer, ssize_t Size, int Flags = 0) { // Read from stream.. int64 Remaining = Mem.GetSize() - Mem.GetPos(); if (Remaining <= 0) { Thing *t = Idx < (ssize_t)f->Items.Length() ? f->Items[Idx++] : NULL; if (t) { Mem.SetSize(0); // We can't allow Export to delete the Mem object, we own it. // So create a proxy object for it. LAutoPtr cp(new LProxyStream(&Mem)); if (t->Export(cp, FolderMime)) Sz += Mem.GetSize(); else LAssert(0); Mem.SetPos(0); } else return 0; } Remaining = Mem.GetSize() - Mem.GetPos(); if (Remaining > 0) { int Common = (int)MIN(Size, Remaining); ssize_t Rd = Mem.Read(Buffer, Common); if (Rd > 0) return Rd; else LAssert(0); } return 0; } ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) { LAssert(0); return 0; } LStreamI *Clone() { LAssert(0); return NULL; } }; // ScribeFolder drag'n'drop bool ScribeFolder::GetFormats(LDragFormats &Formats) { if (GetItemType() == MAGIC_MAIL || GetItemType() == MAGIC_CONTACT || GetItemType() == MAGIC_CALENDAR) { Formats.SupportsFileStreams(); } Formats.Supports(ScribeFolderObject); return Formats.Length() > 0; } bool ScribeFolder::OnBeginDrag(LMouse &m) { if (GetParent()) Drag(Tree, m.Event, DROPEFFECT_MOVE | DROPEFFECT_COPY); return true; } void ScribeFolder::OnEndData() { DropFileName.Empty(); } LString ScribeFolder::GetDropFileName() { LAutoString Fn(MakeFileName(GetObject()->GetStr(FIELD_FOLDER_NAME), 0)); DropFileName = Fn; if (GetItemType() == MAGIC_MAIL) DropFileName += ".mbx"; else if (GetItemType() == MAGIC_CONTACT) DropFileName += ".vcf"; else if (GetItemType() == MAGIC_CALENDAR) DropFileName += ".ics"; else if (GetItemType() == MAGIC_FILTER) DropFileName += ".xml"; return DropFileName; } bool ScribeFolder::GetData(LArray &Data) { ssize_t DataSet = 0; for (unsigned idx=0; idxSetPos(0); auto Fn = GetDropFileName(); auto MimeType = sMimeMbox; auto r = dd.AddFileStream(LGetLeaf(Fn), MimeType, s); LAssert(r); } DataSet = dd.Data.Length(); } else if (dd.IsFormat(LGI_FileDropFormat)) { LMouse m; if (App->GetMouse(m, true)) { LString::Array Files; if (GetDropFileName()) { LAutoPtr f(new LFile); if (f->Open(DropFileName, O_WRITE)) { if (GetItemType() == MAGIC_MAIL) Export(AutoCast(f), sMimeMbox); else if (GetItemType() == MAGIC_CONTACT) Export(AutoCast(f), sMimeVCard); else if (GetItemType() == MAGIC_CALENDAR) Export(AutoCast(f), sMimeVCalendar); } } if (DropFileName) { Files.Add(DropFileName.Get()); if (CreateFileDrop(&dd, m, Files)) { DataSet++; } else LgiTrace("%s:%i - CreateFileDrop failed.\n", _FL); } else LgiTrace("%s:%i - No drop file name.\n", _FL); } else LgiTrace("%s:%i - GetMouse failed.\n", _FL); } else if (dd.IsFormat(ScribeFolderObject)) { ScribeClipboardFmt *Fmt = ScribeClipboardFmt::Alloc(true, 1); if (Fmt) { Fmt->FolderAt(0, this); dd.Data[0].SetBinary(Fmt->Sizeof(), Fmt); DataSet++; free(Fmt); } } } return DataSet > 0; } void ScribeFolder::CollectSubFolderMail(ScribeFolder *To) { if (!To) To = this; LoadThings(NULL, [this, To](auto Status) { LArray Items; for (auto Item: Items) { if (To != this && Item->IsMail()) Items.Add(Item); } To->MoveTo(Items, false, NULL); for (ScribeFolder *f = GetChildFolder(); f; f = f->GetNextFolder()) { f->CollectSubFolderMail(To); } }); } void ScribeFolder::OnReceiveFiles(LArray &Files) { if (Files.Length()) { for (unsigned i=0; i f(new LTextFile); if (f->Open(File, O_READ)) Import(AutoCast(f), MimeType); } } } // Import/export bool ScribeFolder::GetFormats(bool Export, LString::Array &MimeTypes) { MimeTypes.Add(sMimeMbox); if (!Export) MimeTypes.Add(sMimeMessage); return MimeTypes.Length() > 0; } class MboxParser : public LStringPipe { LStreamI *Src = NULL; int Hdrs = 0; bool NewMsg = true; LArray Buf; int64 Pos = 0; bool Eof = false; bool IsMessageHdr(LString c) { // check that it's a from line auto parts = c.SplitDelimit(" \r"); if (parts.Length() >= 7 && parts.Length() <= 9 && parts[0].Equals("From")) { return true; } return false; } struct Blk { uint8_t *ptr; ssize_t size; }; struct Blocks : public LArray { ssize_t Bytes = 0; Blocks(LMemQueue *q) { q->Iterate([this](auto ptr, auto size) { auto &b = New(); b.ptr = ptr; b.size = size; Bytes += size; return true; }); } LString GetLine(size_t idx, size_t offset) { char buf[256]; int ch = 0; for (size_t i = idx; i < Length(); i++) { auto &b = (*this)[i]; auto p = b.ptr + offset; auto end = b.ptr + b.size; while (p < end) { if (*p == '\n' || ch == sizeof(buf)-1) return LString(buf, ch); buf[ch++] = *p++; } } return LString(); } bool ValidateSeparator(LString ln) { auto p = ln.SplitDelimit(); if (p.Length() < 7) return false; if (!p[0].Equals("From")) return false; if (p[1].Find("@") < 0) return false; bool hasYear = false; bool hasTime = false; for (int i=2; i= 1800 && val < 2200) hasYear = true; } else if (s.Find(":") > 0) { int colons = 0, nonDigits = 0; for (auto p = s.Get(); *p; p++) if (*p == ':') colons++; else if (!IsDigit(*p)) nonDigits++; if (colons == 2 && nonDigits == 0) hasTime = true; } } return hasYear && hasTime; } ssize_t FindBoundary(ssize_t start) { const char *key = "\nFrom "; const char *k = key + 1; size_t idx = 0; ssize_t offset = 0; if (Bytes == 0) return -1; if (start < 0 || start >= Bytes) { LAssert(!"Start out of range."); return -1; } // Seek to the right starting block... while (idx < Length()) { auto &b = (*this)[idx]; if (start < b.size) break; start -= b.size; offset += b.size; idx++; } // Start searching for the key... while (idx < Length()) { auto &b = (*this)[idx]; auto end = b.ptr + b.size; for (auto p = b.ptr + start; p < end; p++) { if (*k == *p) { if (*++k == 0) { // Found the "From " part, but lets check the rest of the line. // Should be in the format: // From sender date more-info auto blkAddr = (p - b.ptr) - 4; LString ln = GetLine(idx, blkAddr); if (ln && ValidateSeparator(ln)) return offset + blkAddr; } } else k = key; } offset += b.size; idx++; } return -1; } LRange FindMsg(MboxParser &parser) { auto start = FindBoundary(0); if (start > 0) LgiTrace("%s:%i - Usually the start should be 0, but it's " LPrintfSSizeT "?\n", _FL, start); if (start >= 0) { auto end = FindBoundary(start + 5); if (end > start) { return LRange(start, end - start); } else if (parser.Eof) { return LRange(start, Bytes); } } return LRange(-1, 0); } }; public: MboxParser(LStreamI *s) : LStringPipe(128 << 10) { Src = s; // _debug = true; Buf.Length(128 << 10); } bool ReadSource() { auto rd = Src->Read(Buf.AddressOf(), Buf.Length()); if (rd <= 0) { // Src stream is empty or in an error state.. Eof = true; return false; } auto wr = Write(Buf.AddressOf(), Buf.Length()); if (wr <= 0) { LgiTrace("%s:%i - Failed to write to local buffer.\n", _FL); return false; } return true; } LAutoPtr ReadMessage() { LAutoPtr m; while (true) { Blocks blks(this); auto r = blks.FindMsg(*this); if (r.Start >= 0) { LMemStream *ms = NULL; /* LgiTrace("ReadMsg " LPrintfInt64 " %s\n", Pos, r.GetStr()); auto InitPos = Pos; */ Pos += r.Len; m.Reset(ms = new LMemStream(this, r.Start, r.Len)); /* Debugging... auto key = "The package name is vmware_addons."; auto base = ms->GetBasePtr(); auto result = Strnistr(base, key, m->GetSize()); if (result) LgiTrace("Found the Key @ " LPrintfInt64 "\n", InitPos + (result - base)); */ break; } else if (!ReadSource()) { r = blks.FindMsg(*this); if (r.Start >= 0) m.Reset(new LMemStream(this, r.Start, r.Len)); break; } } return m; } }; class ImportFolderTask : public FolderTask { LDataStoreI::StoreTrans trans; LAutoPtr Parser; public: ImportFolderTask(ScribeFolder *fld, LAutoPtr in, LString mimeType, ThingType::IoProgressCallback cb) : FolderTask(fld, in, mimeType, cb) { SetDescription(LLoadString(IDS_MBOX_READING)); SetType("K"); SetScale(1.0/1024.0); SetRange(Stream->GetSize()); trans = fld->GetObject()->GetStore()->StartTransaction(); } bool TimeSlice() { auto Start = LCurrentTime(); bool Eof = false; if (!Parser) Parser.Reset(new MboxParser(Stream)); while ( Parser && LCurrentTime() - Start < WORK_SLICE_MS && !IsCancelled()) { auto Msg = Parser->ReadMessage(); if (Msg) { Mail *m = dynamic_cast(App->CreateItem(MAGIC_MAIL, Folder, false)); if (m) { m->OnAfterReceive(Msg); m->SetFlags(MAIL_RECEIVED|MAIL_READ); m->Save(); m->Update(); } else { Eof = true; break; } } else { Eof = true; break; } Value(Stream->GetPos()); } return !Eof; } }; ThingType::IoProgress ScribeFolder::Import(IoProgressImplArgs) { if (Stricmp(mimeType, sMimeMbox) == 0 || Stricmp(mimeType, "text/x-mail") == 0) { // Mail box format... ThingType::IoProgress p(Store3Delayed); p.prog = new ImportFolderTask(this, stream, mimeType, cb); return p; } else if (Stricmp(mimeType, sMimeVCard) == 0) { VCard Io; Thing *t; bool Error = false; int Imported = 0; int Idx = 0; while ((t = App->CreateItem(GetItemType(), 0, false))) { Contact *c = t->IsContact(); if (!c) { t->DecRef(); Error = true; break; } if (Io.Import(c->GetObject(), stream)) { const char *First = 0, *Last = 0; c->GetField(FIELD_FIRST_NAME, First); c->GetField(FIELD_LAST_NAME, Last); LgiTrace("Import %i %s %s\n", Idx, First, Last); if (t->Save(this)) { Imported++; } else { Error = true; break; } } else { t->DecRef(); break; } Idx++; } if (Error) { LgiMsg( App, LLoadString(IDS_ERROR_IMPORT_COUNT), AppName, MB_OK, LLoadString(IDC_CONTACTS), Imported); IoProgressError("Contact import error."); } IoProgressSuccess(); } else if (Stricmp(mimeType, sMimeVCalendar) == 0) { VCal Io; Thing *t; while ((t = App->CreateItem(GetItemType(), this, false))) { if (Io.Import(t->GetObject(), stream)) { if (!t->Save(this)) IoProgressError("Contact save failed."); } else { t->DecRef(); break; } } IoProgressSuccess(); } else if (GetObject()) { Thing *t = App->CreateThingOfType(GetItemType(), GetObject()->GetStore()->Create(GetItemType())); if (!t) IoProgressError("Failed to create contact"); if (!t->Import(stream, mimeType)) { t->DecRef(); IoProgressError("Contact import failed."); } if (!t->Save(this)) { t->DecRef(); IoProgressError("Contact save failed."); } IoProgressSuccess(); } IoProgressNotImpl(); } class ExportFolderTask : public FolderTask { int Idx = 0; public: ExportFolderTask( ScribeFolder *folder, LAutoPtr out, LString mimeType, ThingType::IoProgressCallback cb) : FolderTask(folder, out, mimeType, cb) { bool Mbox = _stricmp(MimeType, sMimeMbox) == 0; // Clear the files contents Stream->SetSize(0); // Setup progress UI SetDescription(Mbox ? LLoadString(IDS_MBOX_WRITING) : (char*)"Writing..."); SetRange(Folder->Items.Length()); switch (Folder->GetItemType()) { case MAGIC_MAIL: SetType(LLoadString(IDS_EMAIL)); break; case MAGIC_CALENDAR: SetType(LLoadString(IDS_CALENDAR)); break; case MAGIC_CONTACT: SetType(LLoadString(IDS_CONTACT)); break; case MAGIC_GROUP: SetType("Groups"); break; default: SetType("Objects"); break; } SetPulse(PULSE_MS); SetAlwaysOnTop(true); } bool TimeSlice() { auto Start = LCurrentTime(); while ( LCurrentTime() - Start < WORK_SLICE_MS && !IsCancelled()) { if (Idx >= (ssize_t)Folder->Items.Length()) return false; // Process all the container's items Thing *t = Folder->Items[Idx++]; if (!t) return false; LAutoPtr wrapper(new LProxyStream(Stream)); if (!t->Export(wrapper, MimeType)) { Status.status = Store3Error; Status.errMsg = "Error exporting items."; return false; } Value(Idx); } return true; } /* void OnPulse() { LProgressDlg::OnPulse(); PostEvent(M_EXPORT_NEXT); } LMessage::Result OnEvent(LMessage *Msg) { if (Msg->Msg() == M_EXPORT_NEXT) { auto StartTs = LCurrentTime(); while ( !IsCancelled() && (LCurrentTime() - StartTs) < WORK_SLICE_MS) { if (Idx >= (ssize_t)Folder->Items.Length()) { Quit(); break; } // Process all the container's items Thing *t = Folder->Items[Idx++]; if (t) { LAutoPtr wrapper(new LProxyStream(Out)); if (t->Export(wrapper, MimeType)) { Value(Idx); } else { Status.status = Store3Error; Status.errMsg = "Error exporting items."; if (!onComplete) LgiMsg(this, "%s", AppName, MB_OK, Status.errMsg.Get()); Quit(); } } } return 0; } return LProgressDlg::OnEvent(Msg); } */ }; class FolderExportTask : public LProgressDlg { LAutoPtr Out; ScribeFolder *Folder; LString MimeType; int Idx; bool HasError = false; public: // Minimum amount of time to do work. constexpr static int WORK_SLICE_MS = 130; // This should be larger then WORK_SLICE_MS to allow message loop to process constexpr static int PULSE_MS = 200; FolderExportTask(LAutoPtr out, ScribeFolder *folder, LString mimeType) : LProgressDlg(folder->App) { Out = out; Folder = folder; MimeType = mimeType; Idx = 0; Ts = LCurrentTime(); Folder->App->OnFolderTask(this, true); bool Mbox = _stricmp(MimeType, sMimeMbox) == 0; // Clear the files contents Out->SetSize(0); // Setup progress UI SetDescription(Mbox ? LLoadString(IDS_MBOX_WRITING) : (char*)"Writing..."); SetRange(Folder->Items.Length()); switch (Folder->GetItemType()) { case MAGIC_MAIL: SetType(LLoadString(IDS_EMAIL)); break; case MAGIC_CALENDAR: SetType(LLoadString(IDS_CALENDAR)); break; case MAGIC_CONTACT: SetType(LLoadString(IDS_CONTACT)); break; case MAGIC_GROUP: SetType("Groups"); break; default: SetType("Objects"); break; } SetPulse(PULSE_MS); SetAlwaysOnTop(true); } ~FolderExportTask() { Folder->App->OnFolderTask(this, false); } bool OnRequestClose(bool OsClose) { return true; } void OnPulse() { LProgressDlg::OnPulse(); PostEvent(M_EXPORT_NEXT); } LMessage::Result OnEvent(LMessage *Msg) { if (Msg->Msg() == M_EXPORT_NEXT) { auto StartTs = LCurrentTime(); while ( !IsCancelled() && (LCurrentTime() - StartTs) < WORK_SLICE_MS) { if (Idx >= (ssize_t)Folder->Items.Length()) { Quit(); break; } // Process all the container's items Thing *t = Folder->Items[Idx++]; if (t) { if (t->Export(Out, MimeType, NULL)) { Value(Idx); } else { HasError = true; LgiMsg(this, "Error exporting items.", AppName); Quit(); } } } return 0; } return LProgressDlg::OnEvent(Msg); } }; // This is the mime type used to storage objects on disk const char *ScribeFolder::GetStorageMimeType() { auto Type = GetItemType(); switch (Type) { case MAGIC_MAIL: return sMimeMbox; case MAGIC_CALENDAR: return sMimeVCalendar; case MAGIC_CONTACT: return sMimeVCard; case MAGIC_FILTER: return sMimeXml; default: LgiTrace("%s:%i - Unsupported storage type: %s\n", _FL, Store3ItemTypeName(Type)); break; } return NULL; } void ScribeFolder::ExportAsync(LAutoPtr f, const char *MimeType, std::function Callback) { if (!MimeType) { LAssert(!"No Mimetype"); if (Callback) Callback(NULL); return; } LoadThings( NULL, [ this, Str = f.Release(), MimeType = LString(MimeType), Callback ] (auto Status) { LAutoPtr f(Str); FolderExportTask *Task = NULL; if (Status == Store3Success) Task = new FolderExportTask(f, this, MimeType); if (Callback) Callback(Task); }); } ThingType::IoProgress ScribeFolder::Export(IoProgressImplArgs) { IoProgress ErrStatus(Store3Error); if (!mimeType) { ErrStatus.errMsg = "No mimetype."; if (cb) cb(&ErrStatus, NULL); return ErrStatus; } if (!LoadThings()) { ErrStatus.errMsg = "Failed to load things."; if (cb) cb(&ErrStatus, NULL); return ErrStatus; } IoProgress Status(Store3Delayed); Status.prog = new ExportFolderTask(this, stream, mimeType, cb); return Status; } size_t ScribeFolder::Length() { if (GetItemType() == MAGIC_MAIL) { ThingList *v = View(); if (v) return v->Length(); } if (IsLoaded()) return Items.Length(); return GetItems(); } ssize_t ScribeFolder::IndexOf(Mail *m) { if (GetItemType() == MAGIC_MAIL) { ThingList *v = View(); if (v) return v->IndexOf(m); return Items.IndexOf(m); } return -1; } Mail *ScribeFolder::operator [](size_t i) { if (GetItemType() == MAGIC_MAIL) { ThingList *v = View(); if (v) return dynamic_cast(v->ItemAt(i)); Thing *t = Items[i]; if (t) return t->IsMail(); } return NULL; } bool ScribeFolder::GetVariant(const char *Name, LVariant &Value, const char *Array) { ScribeDomType Fld = StrToDom(Name); switch (Fld) { case SdType: // Type: Int32 { Value = GetObject()->Type(); break; } case SdName: // Type: String { Value = GetName(true).Get(); break; } case SdPath: // Type: String { auto p = GetPath(); if (p) Value = p; else return false; break; } case SdUnread: // Type: Int32 { Value = GetUnRead(); break; } case SdLength: // Type: Int32 { Value = (int32)Items.Length(); break; } case SdItem: // Type: Thing[] { Value.Empty(); LoadThings(); // Use in sync mode, no callback // This call back HAS to set value one way or another... if (Array) { bool IsNumeric = true; for (auto *v = Array; *v; v++) { if (!IsDigit(*v)) { IsNumeric = false; break; } } if (IsNumeric) { int Idx = atoi(Array); if (Idx >= 0 && Idx < (ssize_t)Items.Length()) { Value = (LDom*) Items[Idx]; return true; } } else // Is message ID? { for (auto t : Items) { Mail *m = t->IsMail(); if (!m) break; auto Id = m->GetMessageId(); if (Id && !strcmp(Id, Array)) { Value = (LDom*)t; return true; } } } } else if (Value.SetList()) { for (auto t : Items) Value.Value.Lst->Insert(new LVariant((LDom*)t)); return true; } break; } case SdItemType: // Type: Int32 { Value = GetItemType(); break; } case SdScribe: // Type: ScribeWnd { Value = (LDom*)App; break; } case SdChild: // Type: ScribeFolder { Value = (LDom*)GetChildFolder(); break; } case SdNext: // Type: ScribeFolder { Value = (LDom*)GetNextFolder(); break; } case SdSelected: // Type: Thing[] { if (!Select() || !Value.SetList()) return false; List a; if (!App->GetMailList()->GetSelection(a)) return false; for (auto t: a) { Value.Value.Lst->Insert(new LVariant(t)); } break; } case SdExpanded: // Type: Boolean { Value = Expanded(); break; } default: { return false; } } return true; } bool ScribeFolder::SetVariant(const char *Name, LVariant &Value, const char *Array) { ScribeDomType Fld = StrToDom(Name); switch (Fld) { case SdName: // Type: String { char *s = Value.CastString(); if (ValidStr(s)) OnRename(s); else return false; break; } case SdExpanded: // Type: Boolean { Expanded(Value.CastInt32() != 0); break; } default: return false; } return true; } bool ScribeFolder::CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) { ScribeDomType m = StrToDom(MethodName); switch (m) { case SdLoad: // Type: () { LoadThings(App); // FIXME: Callback for status? return true; } case SdSelect: // Type: () { Select(true); return true; } case SdImport: // Type: (String FileName, String MimeType) { *ReturnValue = false; if (Args.Length() != 2) LgiTrace("%s:%i - Error: expecting 2 arguments to 'Import'.\n", _FL); else { auto FileName = Args[0]->Str(); LAutoPtr f(new LFile); if (f->Open(FileName, O_READ)) { auto p = Import(AutoCast(f), Args[1]->Str()); *ReturnValue = p.status; } else LgiTrace("%s:%i - Error: Can't open '%s' for reading.\n", _FL, FileName); } break; } case SdExport: // Type: (String FileName, String MimeType) { *ReturnValue = false; if (Args.Length() != 2) LgiTrace("%s:%i - Error: expecting 2 arguments to 'Export'.\n", _FL); else { auto FileName = Args[0]->Str(); LAutoPtr f(new LFile); if (f->Open(FileName, O_WRITE)) { auto p = Export(AutoCast(f), Args[1]->Str()); *ReturnValue = p.status; } else LgiTrace("%s:%i - Error: Can't open '%s' for writing.\n", _FL, FileName); } break; } default: break; } return false; } void ScribeFolder::OnRethread() { if (GetThreaded()) { Thread(); } } diff --git a/src/ScribeFolderSelect.cpp b/src/ScribeFolderSelect.cpp --- a/src/ScribeFolderSelect.cpp +++ b/src/ScribeFolderSelect.cpp @@ -1,380 +1,400 @@ /* ** FILE: ScribeFolderSelect.cpp ** AUTHOR: Matthew Allen ** DATE: 1/6/1999 ** DESCRIPTION: Scribe Folder Selection ** ** Copyright (C) 1999, Matthew Allen ** fret@memecode.com */ #include #include #include #include #include "Scribe.h" #include "lgi/common/Button.h" #include "resdefs.h" class FolderDlgPriv; class ScribeFolderTree : public LTree { void AddFolder(FolderDlgPriv *d, LTreeItem *i, ScribeFolder *f); public: ScribeFolderTree(int id, int x, int y, int cx, int cy); void Setup(FolderDlgPriv *d, ScribeFolder *Root, const char *InitSel); LString Get2(); }; class FolderDlgPriv { public: FolderDlg *Dlg = NULL; ScribeWnd *App = NULL; bool CreateNew = false; int LimitTo = MAGIC_NONE; LString Path; LString DefaultNewFolderName; LString Filter; ScribeFolderTree *View = NULL; FolderDlgPriv(ScribeWnd *app, FolderDlg *t, bool create) { CreateNew = create; Dlg = t; App = app; } void OnFilter(); }; ////////////////////////////////////////////////////////////////////////////// struct FolderLeaf : public LTreeItem { FolderDlgPriv *d; ScribeFolder *Folder; + bool isRoot = false; - FolderLeaf(FolderDlgPriv *priv, ScribeFolder *folder) + FolderLeaf(FolderDlgPriv *priv, ScribeFolder *folder, bool isroot = false) { d = priv; Folder = folder; + isRoot = isroot; } bool IsSelectable() { auto fType = Folder ? Folder->GetItemType() : 0; return d->LimitTo == MAGIC_NONE || d->LimitTo == MAGIC_ANY || (Folder && d->LimitTo == fType); } const char *GetText(int i=0) override { - return (Folder) ? Folder->GetText(i) : ""; + if (!Folder) + return ""; + if (isRoot) + return "Folders"; + + return Folder->GetText(i); } int GetImage(int Flags = 0) override { if (IsSelectable()) { return (Flags) ? ICON_OPEN_FOLDER : ICON_CLOSED_FOLDER; } return ICON_DISABLED_FOLDER; } ScribeFolder *GetFolder() { return Folder; } void OnSelect() override { LViewI *Parent = Tree->LView::GetParent(); if (Parent) { Parent->SetCtrlEnabled(IDOK, IsSelectable()); Parent->SetCtrlEnabled(IDC_NEW_FOLDER, IsSelectable() && d->CreateNew); } } }; void FolderDlgPriv::OnFilter() { // Initialize visible View->ForAllItems([vis = Filter ? LCss::DispNone : LCss::DispBlock](auto i) { FolderLeaf *l = dynamic_cast(i); if (!l) return; l->GetCss(true)->Display(vis); }); // Find matching items... View->ForAllItems([this](auto i) { FolderLeaf *l = dynamic_cast(i); if (!l) return; auto nm = l->GetText(); if (Stristr(nm, this->Filter.Get())) { l->GetCss(true)->Display(LCss::DispBlock); for (auto p = l->GetParent(); p; p = p->GetParent()) { FolderLeaf *lp = dynamic_cast(p); if (lp) lp->GetCss(true)->Display(LCss::DispBlock); } } }); View->UpdateAllItems(); View->Invalidate(); } ////////////////////////////////////////////////////////////////////////////// class LFolderCtrlFactory : public LViewFactory { LView *NewView(const char *Class, LRect *Pos, const char *Text) { if (!Stricmp(Class, "ScribeFolderTree")) return new ScribeFolderTree(-1, 0, 0, 100, 100); return 0; } } FolderCtrlFactory; ScribeFolderTree::ScribeFolderTree(int id, int x, int y, int cx, int cy) : LTree(id, x, y, cx, cy, "") { SetObjectName(Res_Custom); Sunken(true); AskImage(true); } void ScribeFolderTree::Setup(FolderDlgPriv *d, ScribeFolder *Root, const char *InitSel) { if (!Root) + { + LAssert(!"No root"); return; + } - FolderLeaf *Leaf = new FolderLeaf(d, Root); - if (Leaf) + auto Leaf = new FolderLeaf(d, Root, true); + if (!Leaf) { - Insert(Leaf); - AddFolder(d, Leaf, Root); + LAssert(!"Alloc failed."); + return; + } + + Insert(Leaf); + AddFolder(d, Leaf, Root); - ScribeFolder *f = Root; - while ((f = f->GetNextFolder())) + ScribeFolder *f = Root; + while ((f = f->GetNextFolder())) + { + Leaf = new FolderLeaf(d, f); + if (Leaf) + { + Insert(Leaf); + AddFolder(d, Leaf, f); + } + } + + if (InitSel) + { + auto Path = LString(InitSel).SplitDelimit("/"); + LTreeNode *n = this; + for (unsigned i=0; i(n->GetChild()); c; + c = dynamic_cast(c->GetNext())) { - Insert(Leaf); - AddFolder(d, Leaf, f); + auto s = c->GetFolder()->GetName(true); + if (s.Equals(Path[i])) + { + Match = c; + break; + } } + + if (Match) + n = Match; + else + break; } - if (InitSel) - { - auto Path = LString(InitSel).SplitDelimit("/"); - LTreeNode *n = this; - for (unsigned i=0; i(n->GetChild()); c; - c = dynamic_cast(c->GetNext())) - { - auto s = c->GetFolder()->GetName(true); - if (s.Equals(Path[i])) - { - Match = c; - break; - } - } + LTreeItem *it = dynamic_cast(n); + if (it) + it->Select(true); + } - if (Match) - n = Match; - else - break; - } - - LTreeItem *it = dynamic_cast(n); - if (it) - it->Select(true); - } - } + UpdateAllItems(); } void ScribeFolderTree::AddFolder(FolderDlgPriv *d, LTreeItem *i, ScribeFolder *f) { - if (i && f) + if (!i || !f) { - for (ScribeFolder *Folder = f->GetChildFolder(); Folder; Folder = Folder->GetNextFolder()) + LAssert(!"Missing param"); + return; + } + + f->LoadFolders(); + + for (ScribeFolder *Folder = f->GetChildFolder(); Folder; Folder = Folder->GetNextFolder()) + { + FolderLeaf *Leaf = new FolderLeaf(d, Folder); + if (Leaf) { - FolderLeaf *Leaf = new FolderLeaf(d, Folder); - if (Leaf) - { - i->Insert(Leaf); - AddFolder(d, Leaf, Folder); - i->Expanded(true); - } + i->Insert(Leaf); + AddFolder(d, Leaf, Folder); + i->Expanded(true); } } } LString ScribeFolderTree::Get2() { LString a; FolderLeaf *Leaf = dynamic_cast(Selection()); if (Leaf) { ScribeFolder *Folder = Leaf->GetFolder(); if (Folder) a = Folder->GetPath(); } return a; } ////////////////////////////////////////////////////////////////////////////// FolderDlg::FolderDlg( LViewI *parent, ScribeWnd *app, int LimitToType, ScribeFolder *Root, const char *InitialSelect, bool AllowCreate, char *DefaultNewFolderName, char *DialogMsg) { d = new FolderDlgPriv(app, this, AllowCreate); d->DefaultNewFolderName = DefaultNewFolderName; d->LimitTo = LimitToType; SetParent(parent); if (LoadFromResource(IDD_FOLDER_SELECT)) { MoveToCenter(); if (GetViewById(IDC_FOLDERS, d->View)) { if (!Root) { LMailStore *Def = d->App->GetDefaultMailStore(); if (Def) { Root = Def->Root; while (Root->GetPrev()) { Root = dynamic_cast(Root->GetPrev()); } } } d->View->Setup(d, Root, InitialSelect); d->View->SetImageList(d->App->GetIconImgList(), false); } LViewI *msg; if (DialogMsg && GetViewById(IDC_FOLDER_MSG, msg)) { msg->Name(DialogMsg); } } SetCtrlEnabled(IDOK, false); SetCtrlEnabled(IDC_NEW_FOLDER, false); } FolderDlg::~FolderDlg() { DeleteObj(d); } char *FolderDlg::Get() { return d->Path; } int FolderDlg::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_FOLDERS: { if (n.Type == LNotifyItemDoubleClick) { if (d->View) d->Path = d->View->Get2(); EndModal(1); } break; } case IDC_NEW_FOLDER: { auto Cur = d->View->Get2(); if (!Cur) break; Store3ItemTypes Type[] = { MAGIC_MAIL, MAGIC_CONTACT, MAGIC_FILTER, MAGIC_CALENDAR, MAGIC_GROUP }; bool Enable[] = {true, true, true, true, true}; CreateSubFolderDlg Dlg(this, 0, Enable, d->DefaultNewFolderName); ScribeFolder *f = d->App->GetFolder(Cur); if (!f) break; ScribeFolder *Sub = f->GetSubFolder(Dlg.SubName); if (!Sub) Sub = f->CreateSubDirectory(Dlg.SubName, Type[Dlg.SubType]); if (!Sub) break; LTreeItem *s = d->View->Selection(); if (!s) break; FolderLeaf *NewLeaf = new FolderLeaf(d, Sub); s->Insert(NewLeaf); NewLeaf->Select(true); NewLeaf->ScrollTo(); break; } case IDC_FILTER: { LString n = Ctrl->Name(); if (d->Filter != n) { d->Filter = n; d->OnFilter(); } break; } case IDC_CLEAR: { d->Filter.Empty(); SetCtrlName(IDC_FILTER, NULL); d->OnFilter(); break; } case IDOK: { if (d->View) d->Path = d->View->Get2(); EndModal(1); break; } case IDCANCEL: { EndModal(0); break; } } return 0; }