aboutsummaryrefslogtreecommitdiffstats
path: root/security/chrootuid/files
ModeNameSize
-rw-r--r--patch-Makefile222logstatsplainblame
-rw-r--r--patch-chrootuid.1133logstatsplainblame
-rw-r--r--patch-chrootuid.c347logstatsplainblame
e-util/e-alert-bar.c390
-rw-r--r--e-util/e-alert-bar.h72
-rw-r--r--e-util/e-alert-dialog.c403
-rw-r--r--e-util/e-alert-dialog.h81
-rw-r--r--e-util/e-alert-sink.c93
-rw-r--r--e-util/e-alert-sink.h63
-rw-r--r--e-util/e-alert.c1003
-rw-r--r--e-util/e-alert.h119
-rw-r--r--e-util/e-attachment-bar.c778
-rw-r--r--e-util/e-attachment-bar.h83
-rw-r--r--e-util/e-attachment-button.c868
-rw-r--r--e-util/e-attachment-button.h91
-rw-r--r--e-util/e-attachment-dialog.c425
-rw-r--r--e-util/e-attachment-dialog.h77
-rw-r--r--e-util/e-attachment-handler-image.c246
-rw-r--r--e-util/e-attachment-handler-image.h69
-rw-r--r--e-util/e-attachment-handler-sendto.c229
-rw-r--r--e-util/e-attachment-handler-sendto.h66
-rw-r--r--e-util/e-attachment-handler.c133
-rw-r--r--e-util/e-attachment-handler.h84
-rw-r--r--e-util/e-attachment-icon-view.c570
-rw-r--r--e-util/e-attachment-icon-view.h71
-rw-r--r--e-util/e-attachment-paned.c904
-rw-r--r--e-util/e-attachment-paned.h99
-rw-r--r--e-util/e-attachment-store.c1280
-rw-r--r--e-util/e-attachment-store.h137
-rw-r--r--e-util/e-attachment-tree-view.c623
-rw-r--r--e-util/e-attachment-tree-view.h70
-rw-r--r--e-util/e-attachment-view.c1906
-rw-r--r--e-util/e-attachment-view.h244
-rw-r--r--e-util/e-attachment.c2882
-rw-r--r--e-util/e-attachment.h159
-rw-r--r--e-util/e-auth-combo-box.c266
-rw-r--r--e-util/e-auth-combo-box.h75
-rw-r--r--e-util/e-autocomplete-selector.c96
-rw-r--r--e-util/e-autocomplete-selector.h68
-rw-r--r--e-util/e-bit-array.c1
-rw-r--r--e-util/e-bit-array.h4
-rw-r--r--e-util/e-book-source-config.c287
-rw-r--r--e-util/e-book-source-config.h71
-rw-r--r--e-util/e-buffer-tagger.c692
-rw-r--r--e-util/e-buffer-tagger.h39
-rw-r--r--e-util/e-cal-source-config.c431
-rw-r--r--e-util/e-cal-source-config.h76
-rw-r--r--e-util/e-calendar-item.c3773
-rw-r--r--e-util/e-calendar-item.h392
-rw-r--r--e-util/e-calendar.c848
-rw-r--r--e-util/e-calendar.h112
-rw-r--r--e-util/e-canvas-background.c279
-rw-r--r--e-util/e-canvas-background.h75
-rw-r--r--e-util/e-canvas-utils.c222
-rw-r--r--e-util/e-canvas-utils.h59
-rw-r--r--e-util/e-canvas-vbox.c410
-rw-r--r--e-util/e-canvas-vbox.h92
-rw-r--r--e-util/e-canvas.c880
-rw-r--r--e-util/e-canvas.h141
-rw-r--r--e-util/e-categories-config.c5
-rw-r--r--e-util/e-categories-config.h4
-rw-r--r--e-util/e-categories-dialog.c155
-rw-r--r--e-util/e-categories-dialog.h73
-rw-r--r--e-util/e-categories-editor.c435
-rw-r--r--e-util/e-categories-editor.h88
-rw-r--r--e-util/e-categories-selector.c587
-rw-r--r--e-util/e-categories-selector.h97
-rw-r--r--e-util/e-category-completion.c505
-rw-r--r--e-util/e-category-completion.h72
-rw-r--r--e-util/e-category-editor.c343
-rw-r--r--e-util/e-category-editor.h81
-rw-r--r--e-util/e-cell-checkbox.c102
-rw-r--r--e-util/e-cell-checkbox.h71
-rw-r--r--e-util/e-cell-combo.c838
-rw-r--r--e-util/e-cell-combo.h89
-rw-r--r--e-util/e-cell-date-edit.c1039
-rw-r--r--e-util/e-cell-date-edit.h124
-rw-r--r--e-util/e-cell-date.c130
-rw-r--r--e-util/e-cell-date.h74
-rw-r--r--e-util/e-cell-hbox.c353
-rw-r--r--e-util/e-cell-hbox.h91
-rw-r--r--e-util/e-cell-number.c95
-rw-r--r--e-util/e-cell-number.h71
-rw-r--r--e-util/e-cell-percent.c160
-rw-r--r--e-util/e-cell-percent.h76
-rw-r--r--e-util/e-cell-pixbuf.c389
-rw-r--r--e-util/e-cell-pixbuf.h75
-rw-r--r--e-util/e-cell-popup.c550
-rw-r--r--e-util/e-cell-popup.h118
-rw-r--r--e-util/e-cell-renderer-color.c243
-rw-r--r--e-util/e-cell-renderer-color.h79
-rw-r--r--e-util/e-cell-size.c112
-rw-r--r--e-util/e-cell-size.h72
-rw-r--r--e-util/e-cell-text.c2810
-rw-r--r--e-util/e-cell-text.h195
-rw-r--r--e-util/e-cell-toggle.c469
-rw-r--r--e-util/e-cell-toggle.h83
-rw-r--r--e-util/e-cell-tree.c880
-rw-r--r--e-util/e-cell-tree.h90
-rw-r--r--e-util/e-cell-vbox.c341
-rw-r--r--e-util/e-cell-vbox.h93
-rw-r--r--e-util/e-cell.c679
-rw-r--r--e-util/e-cell.h299
-rw-r--r--e-util/e-charset-combo-box.c407
-rw-r--r--e-util/e-charset-combo-box.h73
-rw-r--r--e-util/e-charset.h4
-rw-r--r--e-util/e-client-utils.c445
-rw-r--r--e-util/e-client-utils.h64
-rw-r--r--e-util/e-config.h6
-rw-r--r--e-util/e-contact-map-window.c500
-rw-r--r--e-util/e-contact-map-window.h85
-rw-r--r--e-util/e-contact-map.c407
-rw-r--r--e-util/e-contact-map.h110
-rw-r--r--e-util/e-contact-marker.c624
-rw-r--r--e-util/e-contact-marker.h88
-rw-r--r--e-util/e-contact-store.c1370
-rw-r--r--e-util/e-contact-store.h94
-rw-r--r--e-util/e-dateedit.c2497
-rw-r--r--e-util/e-dateedit.h219
-rw-r--r--e-util/e-datetime-format.c5
-rw-r--r--e-util/e-datetime-format.h4
-rw-r--r--e-util/e-destination-store.c751
-rw-r--r--e-util/e-destination-store.h106
-rw-r--r--e-util/e-dialog-utils.h4
-rw-r--r--e-util/e-dialog-widgets.h4
-rw-r--r--e-util/e-event.h6
-rw-r--r--e-util/e-file-request.c2
-rw-r--r--e-util/e-file-request.h4
-rw-r--r--e-util/e-file-utils.h4
-rw-r--r--e-util/e-filter-code.c102
-rw-r--r--e-util/e-filter-code.h72
-rw-r--r--e-util/e-filter-color.c163
-rw-r--r--e-util/e-filter-color.h74
-rw-r--r--e-util/e-filter-datespec.c513
-rw-r--r--e-util/e-filter-datespec.h91
-rw-r--r--e-util/e-filter-element.c446
-rw-r--r--e-util/e-filter-element.h125
-rw-r--r--e-util/e-filter-file.c261
-rw-r--r--e-util/e-filter-file.h78
-rw-r--r--e-util/e-filter-input.c304
-rw-r--r--e-util/e-filter-input.h78
-rw-r--r--e-util/e-filter-int.c230
-rw-r--r--e-util/e-filter-int.h81
-rw-r--r--e-util/e-filter-option.c566
-rw-r--r--e-util/e-filter-option.h101
-rw-r--r--e-util/e-filter-part.c513
-rw-r--r--e-util/e-filter-part.h112
-rw-r--r--e-util/e-filter-rule.c1241
-rw-r--r--e-util/e-filter-rule.h163
-rw-r--r--e-util/e-focus-tracker.c886
-rw-r--r--e-util/e-focus-tracker.h104
-rw-r--r--e-util/e-html-utils.h4
-rw-r--r--e-util/e-icon-factory.h4
-rw-r--r--e-util/e-image-chooser.c562
-rw-r--r--e-util/e-image-chooser.h80
-rw-r--r--e-util/e-import-assistant.c1436
-rw-r--r--e-util/e-import-assistant.h72
-rw-r--r--e-util/e-import.h6
-rw-r--r--e-util/e-interval-chooser.c214
-rw-r--r--e-util/e-interval-chooser.h72
-rw-r--r--e-util/e-mail-identity-combo-box.c385
-rw-r--r--e-util/e-mail-identity-combo-box.h75
-rw-r--r--e-util/e-mail-signature-combo-box.c668
-rw-r--r--e-util/e-mail-signature-combo-box.h95
-rw-r--r--e-util/e-mail-signature-editor.c914
-rw-r--r--e-util/e-mail-signature-editor.h87
-rw-r--r--e-util/e-mail-signature-manager.c708
-rw-r--r--e-util/e-mail-signature-manager.h93
-rw-r--r--e-util/e-mail-signature-preview.c358
-rw-r--r--e-util/e-mail-signature-preview.h84
-rw-r--r--e-util/e-mail-signature-script-dialog.c731
-rw-r--r--e-util/e-mail-signature-script-dialog.h94
-rw-r--r--e-util/e-mail-signature-tree-view.c395
-rw-r--r--e-util/e-mail-signature-tree-view.h80
-rw-r--r--e-util/e-map.c1429
-rw-r--r--e-util/e-map.h155
-rw-r--r--e-util/e-menu-tool-action.c59
-rw-r--r--e-util/e-menu-tool-action.h75
-rw-r--r--e-util/e-menu-tool-button.c273
-rw-r--r--e-util/e-menu-tool-button.h77
-rw-r--r--e-util/e-misc-utils.c (renamed from e-util/e-util.c)306
-rw-r--r--e-util/e-misc-utils.h175
-rw-r--r--e-util/e-mktemp.c3
-rw-r--r--e-util/e-mktemp.h4
-rw-r--r--e-util/e-name-selector-dialog.c1863
-rw-r--r--e-util/e-name-selector-dialog.h100
-rw-r--r--e-util/e-name-selector-entry.c3541
-rw-r--r--e-util/e-name-selector-entry.h124
-rw-r--r--e-util/e-name-selector-list.c790
-rw-r--r--e-util/e-name-selector-list.h82
-rw-r--r--e-util/e-name-selector-model.c663
-rw-r--r--e-util/e-name-selector-model.h108
-rw-r--r--e-util/e-name-selector.c658
-rw-r--r--e-util/e-name-selector.h94
-rw-r--r--e-util/e-online-button.c210
-rw-r--r--e-util/e-online-button.h69
-rw-r--r--e-util/e-paned.c503
-rw-r--r--e-util/e-paned.h82
-rw-r--r--e-util/e-passwords-win32.c1064
-rw-r--r--e-util/e-passwords.c890
-rw-r--r--e-util/e-passwords.h81
-rw-r--r--e-util/e-picture-gallery.c437
-rw-r--r--e-util/e-picture-gallery.h71
-rw-r--r--e-util/e-plugin-ui.c1
-rw-r--r--e-util/e-plugin-ui.h4
-rw-r--r--e-util/e-plugin.h4
-rw-r--r--e-util/e-poolv.h4
-rw-r--r--e-util/e-popup-action.c408
-rw-r--r--e-util/e-popup-action.h96
-rw-r--r--e-util/e-popup-menu.c112
-rw-r--r--e-util/e-popup-menu.h59
-rw-r--r--e-util/e-port-entry.c549
-rw-r--r--e-util/e-port-entry.h91
-rw-r--r--e-util/e-preferences-window.c643
-rw-r--r--e-util/e-preferences-window.h88
-rw-r--r--e-util/e-preview-pane.c322
-rw-r--r--e-util/e-preview-pane.h80
-rw-r--r--e-util/e-print.c2
-rw-r--r--e-util/e-print.h4
-rw-r--r--e-util/e-printable.c226
-rw-r--r--e-util/e-printable.h93
-rw-r--r--e-util/e-reflow-model.c411
-rw-r--r--e-util/e-reflow-model.h129
-rw-r--r--e-util/e-reflow.c1732
-rw-r--r--e-util/e-reflow.h145
-rw-r--r--e-util/e-rule-context.c1026
-rw-r--r--e-util/e-rule-context.h218
-rw-r--r--e-util/e-rule-editor.c920
-rw-r--r--e-util/e-rule-editor.h125
-rw-r--r--e-util/e-search-bar.c771
-rw-r--r--e-util/e-search-bar.h89
-rw-r--r--e-util/e-selectable.c168
-rw-r--r--e-util/e-selectable.h85
-rw-r--r--e-util/e-selection-model-array.c646
-rw-r--r--e-util/e-selection-model-array.h95
-rw-r--r--e-util/e-selection-model-simple.c117
-rw-r--r--e-util/e-selection-model-simple.h70
-rw-r--r--e-util/e-selection-model.c813
-rw-r--r--e-util/e-selection-model.h209
-rw-r--r--e-util/e-selection.c2
-rw-r--r--e-util/e-selection.h4
-rw-r--r--e-util/e-send-options.c767
-rw-r--r--e-util/e-send-options.h131
-rw-r--r--e-util/e-send-options.ui949
-rw-r--r--e-util/e-sorter-array.c3
-rw-r--r--e-util/e-sorter-array.h4
-rw-r--r--e-util/e-sorter.c1
-rw-r--r--e-util/e-sorter.h4
-rw-r--r--e-util/e-source-combo-box.c701
-rw-r--r--e-util/e-source-combo-box.h90
-rw-r--r--e-util/e-source-config-backend.c140
-rw-r--r--e-util/e-source-config-backend.h98
-rw-r--r--e-util/e-source-config-dialog.c394
-rw-r--r--e-util/e-source-config-dialog.h69
-rw-r--r--e-util/e-source-config.c1447
-rw-r--r--e-util/e-source-config.h120
-rw-r--r--e-util/e-source-selector-dialog.c453
-rw-r--r--e-util/e-source-selector-dialog.h85
-rw-r--r--e-util/e-source-selector.c2082
-rw-r--r--e-util/e-source-selector.h141
-rw-r--r--e-util/e-source-util.h6
-rw-r--r--e-util/e-spell-entry.c866
-rw-r--r--e-util/e-spell-entry.h63
-rw-r--r--e-util/e-stock-request.c3
-rw-r--r--e-util/e-stock-request.h4
-rw-r--r--e-util/e-table-click-to-add.c666
-rw-r--r--e-util/e-table-click-to-add.h99
-rw-r--r--e-util/e-table-col-dnd.h43
-rw-r--r--e-util/e-table-col.c222
-rw-r--r--e-util/e-table-col.h115
-rw-r--r--e-util/e-table-column-specification.c157
-rw-r--r--e-util/e-table-column-specification.h93
-rw-r--r--e-util/e-table-config.c1481
-rw-r--r--e-util/e-table-config.h134
-rw-r--r--e-util/e-table-config.ui1594
-rw-r--r--e-util/e-table-defines.h44
-rw-r--r--e-util/e-table-extras.c410
-rw-r--r--e-util/e-table-extras.h94
-rw-r--r--e-util/e-table-field-chooser-dialog.c235
-rw-r--r--e-util/e-table-field-chooser-dialog.h77
-rw-r--r--e-util/e-table-field-chooser-item.c749
-rw-r--r--e-util/e-table-field-chooser-item.h97
-rw-r--r--e-util/e-table-field-chooser.c335
-rw-r--r--e-util/e-table-field-chooser.h84
-rw-r--r--e-util/e-table-group-container.c1667
-rw-r--r--e-util/e-table-group-container.h138
-rw-r--r--e-util/e-table-group-leaf.c816
-rw-r--r--e-util/e-table-group-leaf.h110
-rw-r--r--e-util/e-table-group.c771
-rw-r--r--e-util/e-table-group.h242
-rw-r--r--e-util/e-table-header-item.c2226
-rw-r--r--e-util/e-table-header-item.h148
-rw-r--r--e-util/e-table-header-utils.c282
-rw-r--r--e-util/e-table-header-utils.h53
-rw-r--r--e-util/e-table-header.c1013
-rw-r--r--e-util/e-table-header.h144
-rw-r--r--e-util/e-table-item.c4041
-rw-r--r--e-util/e-table-item.h261
-rw-r--r--e-util/e-table-memory-callbacks.c234
-rw-r--r--e-util/e-table-memory-callbacks.h148
-rw-r--r--e-util/e-table-memory-store.c637
-rw-r--r--e-util/e-table-memory-store.h155
-rw-r--r--e-util/e-table-memory.c271
-rw-r--r--e-util/e-table-memory.h91
-rw-r--r--e-util/e-table-model.c682
-rw-r--r--e-util/e-table-model.h217
-rw-r--r--e-util/e-table-one.c252
-rw-r--r--e-util/e-table-one.h75
-rw-r--r--e-util/e-table-search.c235
-rw-r--r--e-util/e-table-search.h86
-rw-r--r--e-util/e-table-selection-model.c384
-rw-r--r--e-util/e-table-selection-model.h91
-rw-r--r--e-util/e-table-sort-info.c482
-rw-r--r--e-util/e-table-sort-info.h133
-rw-r--r--e-util/e-table-sorted-variable.c235
-rw-r--r--e-util/e-table-sorted-variable.h85
-rw-r--r--e-util/e-table-sorted.c328
-rw-r--r--e-util/e-table-sorted.h84
-rw-r--r--e-util/e-table-sorter.c519
-rw-r--r--e-util/e-table-sorter.h94
-rw-r--r--e-util/e-table-sorting-utils.c492
-rw-r--r--e-util/e-table-sorting-utils.h95
-rw-r--r--e-util/e-table-specification.c435
-rw-r--r--e-util/e-table-specification.h116
-rw-r--r--e-util/e-table-state.c320
-rw-r--r--e-util/e-table-state.h89
-rw-r--r--e-util/e-table-subset-variable.c267
-rw-r--r--e-util/e-table-subset-variable.h105
-rw-r--r--e-util/e-table-subset.c567
-rw-r--r--e-util/e-table-subset.h120
-rw-r--r--e-util/e-table-utils.c224
-rw-r--r--e-util/e-table-utils.h54
-rw-r--r--e-util/e-table-without.c412
-rw-r--r--e-util/e-table-without.h104
-rw-r--r--e-util/e-table.c3626
-rw-r--r--e-util/e-table.h403
-rw-r--r--e-util/e-text-event-processor-emacs-like.c1
-rw-r--r--e-util/e-text-event-processor-emacs-like.h4
-rw-r--r--e-util/e-text-event-processor-types.h4
-rw-r--r--e-util/e-text-event-processor.c1
-rw-r--r--e-util/e-text-event-processor.h4
-rw-r--r--e-util/e-text-model-repos.c74
-rw-r--r--e-util/e-text-model-repos.h58
-rw-r--r--e-util/e-text-model.c642
-rw-r--r--e-util/e-text-model.h112
-rw-r--r--e-util/e-text.c3405
-rw-r--r--e-util/e-text.h236
-rw-r--r--e-util/e-timezone-dialog.c870
-rw-r--r--e-util/e-timezone-dialog.h77
-rw-r--r--e-util/e-timezone-dialog.ui312
-rw-r--r--e-util/e-tree-memory-callbacks.c314
-rw-r--r--e-util/e-tree-memory-callbacks.h182
-rw-r--r--e-util/e-tree-memory.c743
-rw-r--r--e-util/e-tree-memory.h124
-rw-r--r--e-util/e-tree-model-generator.c1345
-rw-r--r--e-util/e-tree-model-generator.h104
-rw-r--r--e-util/e-tree-model.c1177
-rw-r--r--e-util/e-tree-model.h298
-rw-r--r--e-util/e-tree-selection-model.c939
-rw-r--r--e-util/e-tree-selection-model.h95
-rw-r--r--e-util/e-tree-sorted.c1433
-rw-r--r--e-util/e-tree-sorted.h104
-rw-r--r--e-util/e-tree-table-adapter.c1414
-rw-r--r--e-util/e-tree-table-adapter.h138
-rw-r--r--e-util/e-tree.c3956
-rw-r--r--e-util/e-tree.h376
-rw-r--r--e-util/e-ui-manager.c2
-rw-r--r--e-util/e-ui-manager.h4
-rw-r--r--e-util/e-unicode.h4
-rw-r--r--e-util/e-url-entry.c159
-rw-r--r--e-util/e-url-entry.h60
-rw-r--r--e-util/e-util-enums.h4
-rw-r--r--e-util/e-util.h348
-rw-r--r--e-util/e-web-view-gtkhtml.c2317
-rw-r--r--e-util/e-web-view-gtkhtml.h177
-rw-r--r--e-util/e-web-view-preview.c474
-rw-r--r--e-util/e-web-view-preview.h115
-rw-r--r--e-util/e-web-view.c2945
-rw-r--r--e-util/e-web-view.h224
-rw-r--r--e-util/e-xml-utils.c448
-rw-r--r--e-util/e-xml-utils.h93
-rw-r--r--e-util/ea-calendar-cell.c404
-rw-r--r--e-util/ea-calendar-cell.h90
-rw-r--r--e-util/ea-calendar-item.c1373
-rw-r--r--e-util/ea-calendar-item.h71
-rw-r--r--e-util/ea-cell-table.c215
-rw-r--r--e-util/ea-cell-table.h63
-rw-r--r--e-util/ea-factory.h118
-rw-r--r--e-util/ea-widgets.c36
-rw-r--r--e-util/ea-widgets.h36
-rw-r--r--e-util/evolution-source-viewer.c1176
-rw-r--r--e-util/filter.error.xml34
-rw-r--r--e-util/filter.ui591
-rw-r--r--e-util/gal-a11y-e-cell-popup.c153
-rw-r--r--e-util/gal-a11y-e-cell-popup.h65
-rw-r--r--e-util/gal-a11y-e-cell-registry.c151
-rw-r--r--e-util/gal-a11y-e-cell-registry.h75
-rw-r--r--e-util/gal-a11y-e-cell-toggle.c198
-rw-r--r--e-util/gal-a11y-e-cell-toggle.h67
-rw-r--r--e-util/gal-a11y-e-cell-tree.c266
-rw-r--r--e-util/gal-a11y-e-cell-tree.h66
-rw-r--r--e-util/gal-a11y-e-cell-vbox.c235
-rw-r--r--e-util/gal-a11y-e-cell-vbox.h67
-rw-r--r--e-util/gal-a11y-e-cell.c648
-rw-r--r--e-util/gal-a11y-e-cell.h112
-rw-r--r--e-util/gal-a11y-e-table-click-to-add-factory.c108
-rw-r--r--e-util/gal-a11y-e-table-click-to-add-factory.h52
-rw-r--r--e-util/gal-a11y-e-table-click-to-add.c358
-rw-r--r--e-util/gal-a11y-e-table-click-to-add.h58
-rw-r--r--e-util/gal-a11y-e-table-column-header.c243
-rw-r--r--e-util/gal-a11y-e-table-column-header.h59
-rw-r--r--e-util/gal-a11y-e-table-factory.c101
-rw-r--r--e-util/gal-a11y-e-table-factory.h53
-rw-r--r--e-util/gal-a11y-e-table-item-factory.c107
-rw-r--r--e-util/gal-a11y-e-table-item-factory.h52
-rw-r--r--e-util/gal-a11y-e-table-item.c1437
-rw-r--r--e-util/gal-a11y-e-table-item.h62
-rw-r--r--e-util/gal-a11y-e-table.c315
-rw-r--r--e-util/gal-a11y-e-table.h62
-rw-r--r--e-util/gal-a11y-e-text-factory.c103
-rw-r--r--e-util/gal-a11y-e-text-factory.h52
-rw-r--r--e-util/gal-a11y-e-text.c1141
-rw-r--r--e-util/gal-a11y-e-text.h59
-rw-r--r--e-util/gal-a11y-e-tree-factory.c99
-rw-r--r--e-util/gal-a11y-e-tree-factory.h52
-rw-r--r--e-util/gal-a11y-e-tree.c196
-rw-r--r--e-util/gal-a11y-e-tree.h61
-rw-r--r--e-util/gal-a11y-factory.h89
-rw-r--r--e-util/gal-a11y-util.c49
-rw-r--r--e-util/gal-a11y-util.h40
-rw-r--r--e-util/gal-define-views-dialog.c451
-rw-r--r--e-util/gal-define-views-dialog.h77
-rw-r--r--e-util/gal-define-views-model.c352
-rw-r--r--e-util/gal-define-views-model.h70
-rw-r--r--e-util/gal-define-views.ui177
-rw-r--r--e-util/gal-view-collection.c829
-rw-r--r--e-util/gal-view-collection.h150
-rw-r--r--e-util/gal-view-etable.c335
-rw-r--r--e-util/gal-view-etable.h96
-rw-r--r--e-util/gal-view-factory-etable.c195
-rw-r--r--e-util/gal-view-factory-etable.h77
-rw-r--r--e-util/gal-view-factory.c101
-rw-r--r--e-util/gal-view-factory.h85
-rw-r--r--e-util/gal-view-instance-save-as-dialog.c359
-rw-r--r--e-util/gal-view-instance-save-as-dialog.h92
-rw-r--r--e-util/gal-view-instance-save-as-dialog.ui133
-rw-r--r--e-util/gal-view-instance.c502
-rw-r--r--e-util/gal-view-instance.h114
-rw-r--r--e-util/gal-view-new-dialog.c291
-rw-r--r--e-util/gal-view-new-dialog.h81
-rw-r--r--e-util/gal-view-new-dialog.ui177
-rw-r--r--e-util/gal-view.c280
-rw-r--r--e-util/gal-view.h98
-rw-r--r--e-util/test-calendar.c145
-rw-r--r--e-util/test-category-completion.c67
-rw-r--r--e-util/test-contact-store.c145
-rw-r--r--e-util/test-dateedit.c299
-rw-r--r--e-util/test-mail-signatures.c195
-rw-r--r--e-util/test-name-selector.c102
-rw-r--r--e-util/test-preferences-window.c108
-rw-r--r--e-util/test-source-combo-box.c107
-rw-r--r--e-util/test-source-config.c57
-rw-r--r--e-util/test-source-selector.c157
-rw-r--r--e-util/tree-expanded.xpm23
-rw-r--r--e-util/tree-unexpanded.xpm23
-rw-r--r--e-util/widgets.error.xml28
478 files changed, 169300 insertions, 283 deletions
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 4464f93045..15a9bcd8ac 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -1,3 +1,5 @@
+NULL =
+
eutilincludedir = $(privincludedir)/e-util
ecpsdir = $(privdatadir)/ecps
ruledir = $(privdatadir)
@@ -22,133 +24,650 @@ e-marshal.c: e-marshal.list
ENUM_GENERATED = e-util-enumtypes.h e-util-enumtypes.c
MARSHAL_GENERATED = e-marshal.c e-marshal.h
-if OS_WIN32
-PLATFORM_SOURCES = e-win32-reloc.c e-win32-defaults.c e-win32-defaults.h
-endif
+error_DATA = \
+ e-system.error \
+ filter.error \
+ widgets.error \
+ $(NULL)
+errordir = $(privdatadir)/errors
+@EVO_PLUGIN_RULE@
+
+ui_DATA = \
+ e-send-options.ui \
+ e-table-config.ui \
+ e-timezone-dialog.ui \
+ filter.ui \
+ gal-define-views.ui \
+ gal-view-instance-save-as-dialog.ui \
+ gal-view-new-dialog.ui \
+ $(NULL)
+
+xpm_icons = \
+ arrow-down.xpm \
+ arrow-up.xpm \
+ check-empty.xpm \
+ check-filled.xpm \
+ tree-expanded.xpm \
+ tree-unexpanded.xpm \
+ $(NULL)
privsolib_LTLIBRARIES = libeutil.la
-eutilinclude_HEADERS = \
- e-activity.h \
- e-bit-array.h \
- e-categories-config.h \
- e-charset.h \
- e-config.h \
- e-datetime-format.h \
- e-dialog-utils.h \
- e-dialog-widgets.h \
- e-event.h \
- e-file-request.h \
- e-file-utils.h \
- e-html-utils.h \
- e-icon-factory.h \
- e-import.h \
- e-marshal.h \
- e-mktemp.h \
- e-poolv.h \
- e-print.h \
- e-plugin.h \
- e-plugin-ui.h \
- e-selection.h \
- e-sorter.h \
- e-sorter-array.h \
- e-source-util.h \
- e-stock-request.h \
- e-text-event-processor-emacs-like.h \
- e-text-event-processor-types.h \
- e-text-event-processor.h \
- e-ui-manager.h \
- e-util.h \
- e-util-enums.h \
- e-util-enumtypes.h \
- e-unicode.h
-
-libeutil_la_CPPFLAGS = \
- $(AM_CPPFLAGS) \
- -I$(top_srcdir) \
- -I$(top_builddir) \
- -I$(top_srcdir)/widgets \
- -DEVOLUTION_BINDIR=\""$(bindir)"\" \
- -DEVOLUTION_DATADIR=\""$(datadir)"\" \
- -DEVOLUTION_ECPSDIR=\""$(ecpsdir)"\" \
- -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \
- -DEVOLUTION_GALVIEWSDIR=\""$(viewsdir)"\" \
- -DEVOLUTION_HELPDIR=\""$(evolutionhelpdir)"\" \
- -DEVOLUTION_ICONDIR=\""$(icondir)"\" \
- -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \
- -DEVOLUTION_LIBDIR=\""$(datadir)"\" \
- -DEVOLUTION_LIBEXECDIR=\""$(privlibexecdir)"\" \
- -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \
- -DEVOLUTION_MODULEDIR=\""$(moduledir)"\" \
- -DEVOLUTION_PLUGINDIR=\""$(plugindir)"\" \
- -DEVOLUTION_PREFIX=\""$(prefix)"\" \
- -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
- -DEVOLUTION_SOUNDDIR=\""$(soundsdir)"\" \
- -DEVOLUTION_SYSCONFDIR=\""$(sysconfdir)"\" \
- -DEVOLUTION_TOOLSDIR=\""$(privlibexecdir)"\" \
- -DEVOLUTION_UIDIR=\""$(uidir)"\" \
- -DEVOLUTION_RULEDIR=\"$(ruledir)\" \
- -DG_LOG_DOMAIN=\"e-utils\" \
- $(EVOLUTION_DATA_SERVER_CFLAGS) \
- $(GNOME_PLATFORM_CFLAGS)
-
-libeutil_la_SOURCES = \
- $(eutilinclude_HEADERS) \
- e-activity.c \
- e-bit-array.c \
- e-categories-config.c \
- e-charset.c \
- e-config.c \
- e-datetime-format.c \
- e-dialog-utils.c \
- e-dialog-widgets.c \
- e-event.c \
- e-file-request.c \
- e-file-utils.c \
- e-html-utils.c \
- e-icon-factory.c \
- e-import.c \
- e-marshal.c \
- e-mktemp.c \
- e-poolv.c \
- e-plugin.c \
- e-plugin-ui.c \
- e-print.c \
- e-selection.c \
- e-sorter.c \
- e-sorter-array.c \
- e-source-util.c \
- e-stock-request.c \
- e-text-event-processor-emacs-like.c \
- e-text-event-processor.c \
- e-ui-manager.c \
- e-util.c \
- e-unicode.c \
- e-util-enumtypes.c \
- e-util-private.h \
- $(PLATFORM_SOURCES)
+noinst_PROGRAMS = \
+ evolution-source-viewer \
+ test-calendar \
+ test-category-completion \
+ test-contact-store \
+ test-dateedit \
+ test-mail-signatures \
+ test-name-selector \
+ test-preferences-window \
+ test-source-combo-box \
+ test-source-config \
+ test-source-selector \
+ $(NULL)
+
+libeutil_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DLIBEUTIL_COMPILATION \
+ -DEVOLUTION_BINDIR=\""$(bindir)"\" \
+ -DEVOLUTION_DATADIR=\""$(datadir)"\" \
+ -DEVOLUTION_ECPSDIR=\""$(ecpsdir)"\" \
+ -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \
+ -DEVOLUTION_GALVIEWSDIR=\""$(viewsdir)"\" \
+ -DEVOLUTION_HELPDIR=\""$(evolutionhelpdir)"\" \
+ -DEVOLUTION_ICONDIR=\""$(icondir)"\" \
+ -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \
+ -DEVOLUTION_LIBDIR=\""$(datadir)"\" \
+ -DEVOLUTION_LIBEXECDIR=\""$(privlibexecdir)"\" \
+ -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \
+ -DEVOLUTION_MODULEDIR=\""$(moduledir)"\" \
+ -DEVOLUTION_PLUGINDIR=\""$(plugindir)"\" \
+ -DEVOLUTION_PREFIX=\""$(prefix)"\" \
+ -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
+ -DEVOLUTION_SOUNDDIR=\""$(soundsdir)"\" \
+ -DEVOLUTION_SYSCONFDIR=\""$(sysconfdir)"\" \
+ -DEVOLUTION_TOOLSDIR=\""$(privlibexecdir)"\" \
+ -DEVOLUTION_UIDIR=\""$(uidir)"\" \
+ -DEVOLUTION_RULEDIR=\"$(ruledir)\" \
+ -DG_LOG_DOMAIN=\"libeutil\" \
+ $(EVOLUTION_DATA_SERVER_CFLAGS) \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(CHAMPLAIN_CFLAGS) \
+ $(GEO_CFLAGS) \
+ $(GTKHTML_CFLAGS) \
+ $(NULL)
+
+eutilinclude_HEADERS = \
+ e-util.h \
+ e-action-combo-box.h \
+ e-activity-bar.h \
+ e-activity-proxy.h \
+ e-activity.h \
+ e-alarm-selector.h \
+ e-alert-bar.h \
+ e-alert-dialog.h \
+ e-alert-sink.h \
+ e-alert.h \
+ e-attachment-bar.h \
+ e-attachment-button.h \
+ e-attachment-dialog.h \
+ e-attachment-handler-image.h \
+ e-attachment-handler-sendto.h \
+ e-attachment-handler.h \
+ e-attachment-icon-view.h \
+ e-attachment-paned.h \
+ e-attachment-store.h \
+ e-attachment-tree-view.h \
+ e-attachment-view.h \
+ e-attachment.h \
+ e-auth-combo-box.h \
+ e-autocomplete-selector.h \
+ e-bit-array.h \
+ e-book-source-config.h \
+ e-buffer-tagger.h \
+ e-cal-source-config.h \
+ e-calendar-item.h \
+ e-calendar.h \
+ e-canvas-background.h \
+ e-canvas-utils.h \
+ e-canvas-vbox.h \
+ e-canvas.h \
+ e-categories-config.h \
+ e-categories-dialog.h \
+ e-categories-editor.h \
+ e-categories-selector.h \
+ e-category-completion.h \
+ e-category-editor.h \
+ e-cell-checkbox.h \
+ e-cell-combo.h \
+ e-cell-date-edit.h \
+ e-cell-date.h \
+ e-cell-hbox.h \
+ e-cell-number.h \
+ e-cell-percent.h \
+ e-cell-pixbuf.h \
+ e-cell-popup.h \
+ e-cell-renderer-color.h \
+ e-cell-size.h \
+ e-cell-text.h \
+ e-cell-toggle.h \
+ e-cell-tree.h \
+ e-cell-vbox.h \
+ e-cell.h \
+ e-charset-combo-box.h \
+ e-charset.h \
+ e-client-utils.h \
+ e-config.h \
+ e-contact-map-window.h \
+ e-contact-map.h \
+ e-contact-marker.h \
+ e-contact-store.h \
+ e-dateedit.h \
+ e-datetime-format.h \
+ e-destination-store.h \
+ e-dialog-utils.h \
+ e-dialog-widgets.h \
+ e-event.h \
+ e-file-request.h \
+ e-file-utils.h \
+ e-filter-code.h \
+ e-filter-color.h \
+ e-filter-datespec.h \
+ e-filter-element.h \
+ e-filter-file.h \
+ e-filter-input.h \
+ e-filter-int.h \
+ e-filter-option.h \
+ e-filter-part.h \
+ e-filter-rule.h \
+ e-focus-tracker.h \
+ e-html-utils.h \
+ e-icon-factory.h \
+ e-image-chooser.h \
+ e-import-assistant.h \
+ e-import.h \
+ e-interval-chooser.h \
+ e-mail-identity-combo-box.h \
+ e-mail-signature-combo-box.h \
+ e-mail-signature-editor.h \
+ e-mail-signature-manager.h \
+ e-mail-signature-preview.h \
+ e-mail-signature-script-dialog.h \
+ e-mail-signature-tree-view.h \
+ e-map.h \
+ e-marshal.h \
+ e-menu-tool-action.h \
+ e-menu-tool-button.h \
+ e-misc-utils.h \
+ e-mktemp.h \
+ e-name-selector-dialog.h \
+ e-name-selector-entry.h \
+ e-name-selector-list.h \
+ e-name-selector-model.h \
+ e-name-selector.h \
+ e-online-button.h \
+ e-paned.h \
+ e-passwords.h \
+ e-picture-gallery.h \
+ e-plugin-ui.h \
+ e-plugin.h \
+ e-poolv.h \
+ e-popup-action.h \
+ e-popup-menu.h \
+ e-port-entry.h \
+ e-preferences-window.h \
+ e-preview-pane.h \
+ e-print.h \
+ e-printable.h \
+ e-reflow-model.h \
+ e-reflow.h \
+ e-rule-context.h \
+ e-rule-editor.h \
+ e-search-bar.h \
+ e-selectable.h \
+ e-selection-model-array.h \
+ e-selection-model-simple.h \
+ e-selection-model.h \
+ e-selection.h \
+ e-send-options.h \
+ e-sorter-array.h \
+ e-sorter.h \
+ e-source-combo-box.h \
+ e-source-config-backend.h \
+ e-source-config-dialog.h \
+ e-source-config.h \
+ e-source-selector-dialog.h \
+ e-source-selector.h \
+ e-source-util.h \
+ e-spell-entry.h \
+ e-stock-request.h \
+ e-table-click-to-add.h \
+ e-table-col-dnd.h \
+ e-table-col.h \
+ e-table-column-specification.h \
+ e-table-config.h \
+ e-table-defines.h \
+ e-table-extras.h \
+ e-table-field-chooser-dialog.h \
+ e-table-field-chooser-item.h \
+ e-table-field-chooser.h \
+ e-table-group-container.h \
+ e-table-group-leaf.h \
+ e-table-group.h \
+ e-table-header-item.h \
+ e-table-header-utils.h \
+ e-table-header.h \
+ e-table-item.h \
+ e-table-memory-callbacks.h \
+ e-table-memory-store.h \
+ e-table-memory.h \
+ e-table-model.h \
+ e-table-one.h \
+ e-table-search.h \
+ e-table-selection-model.h \
+ e-table-sort-info.h \
+ e-table-sorted-variable.h \
+ e-table-sorted.h \
+ e-table-sorter.h \
+ e-table-sorting-utils.h \
+ e-table-specification.h \
+ e-table-state.h \
+ e-table-subset-variable.h \
+ e-table-subset.h \
+ e-table-utils.h \
+ e-table-without.h \
+ e-table.h \
+ e-text-event-processor-emacs-like.h \
+ e-text-event-processor-types.h \
+ e-text-event-processor.h \
+ e-text-model-repos.h \
+ e-text-model.h \
+ e-text.h \
+ e-timezone-dialog.h \
+ e-tree-memory-callbacks.h \
+ e-tree-memory.h \
+ e-tree-model-generator.h \
+ e-tree-model.h \
+ e-tree-selection-model.h \
+ e-tree-sorted.h \
+ e-tree-table-adapter.h \
+ e-tree.h \
+ e-ui-manager.h \
+ e-unicode.h \
+ e-url-entry.h \
+ e-util-enums.h \
+ e-util-enumtypes.h \
+ e-web-view-gtkhtml.h \
+ e-web-view-preview.h \
+ e-web-view.h \
+ e-xml-utils.h \
+ ea-calendar-cell.h \
+ ea-calendar-item.h \
+ ea-cell-table.h \
+ ea-factory.h \
+ ea-widgets.h \
+ gal-a11y-e-cell-popup.h \
+ gal-a11y-e-cell-registry.h \
+ gal-a11y-e-cell-toggle.h \
+ gal-a11y-e-cell-tree.h \
+ gal-a11y-e-cell-vbox.h \
+ gal-a11y-e-cell.h \
+ gal-a11y-e-table-click-to-add-factory.h \
+ gal-a11y-e-table-click-to-add.h \
+ gal-a11y-e-table-column-header.h \
+ gal-a11y-e-table-factory.h \
+ gal-a11y-e-table-item-factory.h \
+ gal-a11y-e-table-item.h \
+ gal-a11y-e-table.h \
+ gal-a11y-e-text-factory.h \
+ gal-a11y-e-text.h \
+ gal-a11y-e-tree-factory.h \
+ gal-a11y-e-tree.h \
+ gal-a11y-factory.h \
+ gal-a11y-util.h \
+ gal-define-views-dialog.h \
+ gal-define-views-model.h \
+ gal-view-collection.h \
+ gal-view-etable.h \
+ gal-view-factory-etable.h \
+ gal-view-factory.h \
+ gal-view-instance-save-as-dialog.h \
+ gal-view-instance.h \
+ gal-view-new-dialog.h \
+ gal-view.h \
+ $(NULL)
+
+if OS_WIN32
+PLATFORM_SOURCES = \
+ e-win32-reloc.c \
+ e-win32-defaults.c \
+ e-win32-defaults.h \
+ $(NULL)
+endif
+
+libeutil_la_SOURCES = \
+ $(eutilinclude_HEADERS) \
+ e-action-combo-box.c \
+ e-activity-bar.c \
+ e-activity-proxy.c \
+ e-activity.c \
+ e-alarm-selector.c \
+ e-alert-bar.c \
+ e-alert-dialog.c \
+ e-alert-sink.c \
+ e-alert.c \
+ e-attachment-bar.c \
+ e-attachment-button.c \
+ e-attachment-dialog.c \
+ e-attachment-handler-image.c \
+ e-attachment-handler-sendto.c \
+ e-attachment-handler.c \
+ e-attachment-icon-view.c \
+ e-attachment-paned.c \
+ e-attachment-store.c \
+ e-attachment-tree-view.c \
+ e-attachment-view.c \
+ e-attachment.c \
+ e-auth-combo-box.c \
+ e-autocomplete-selector.c \
+ e-bit-array.c \
+ e-book-source-config.c \
+ e-buffer-tagger.c \
+ e-cal-source-config.c \
+ e-calendar-item.c \
+ e-calendar.c \
+ e-canvas-background.c \
+ e-canvas-utils.c \
+ e-canvas-vbox.c \
+ e-canvas.c \
+ e-categories-config.c \
+ e-categories-dialog.c \
+ e-categories-editor.c \
+ e-categories-selector.c \
+ e-category-completion.c \
+ e-category-editor.c \
+ e-cell-checkbox.c \
+ e-cell-combo.c \
+ e-cell-date-edit.c \
+ e-cell-date.c \
+ e-cell-hbox.c \
+ e-cell-number.c \
+ e-cell-percent.c \
+ e-cell-pixbuf.c \
+ e-cell-popup.c \
+ e-cell-renderer-color.c \
+ e-cell-size.c \
+ e-cell-text.c \
+ e-cell-toggle.c \
+ e-cell-tree.c \
+ e-cell-vbox.c \
+ e-cell.c \
+ e-charset-combo-box.c \
+ e-charset.c \
+ e-client-utils.c \
+ e-config.c \
+ e-contact-map-window.c \
+ e-contact-map.c \
+ e-contact-marker.c \
+ e-contact-store.c \
+ e-dateedit.c \
+ e-datetime-format.c \
+ e-destination-store.c \
+ e-dialog-utils.c \
+ e-dialog-widgets.c \
+ e-event.c \
+ e-file-request.c \
+ e-file-utils.c \
+ e-filter-code.c \
+ e-filter-color.c \
+ e-filter-datespec.c \
+ e-filter-element.c \
+ e-filter-file.c \
+ e-filter-input.c \
+ e-filter-int.c \
+ e-filter-option.c \
+ e-filter-part.c \
+ e-filter-rule.c \
+ e-focus-tracker.c \
+ e-html-utils.c \
+ e-icon-factory.c \
+ e-image-chooser.c \
+ e-import-assistant.c \
+ e-import.c \
+ e-interval-chooser.c \
+ e-mail-identity-combo-box.c \
+ e-mail-signature-combo-box.c \
+ e-mail-signature-editor.c \
+ e-mail-signature-manager.c \
+ e-mail-signature-preview.c \
+ e-mail-signature-script-dialog.c \
+ e-mail-signature-tree-view.c \
+ e-map.c \
+ e-marshal.c \
+ e-menu-tool-action.c \
+ e-menu-tool-button.c \
+ e-misc-utils.c \
+ e-mktemp.c \
+ e-name-selector-dialog.c \
+ e-name-selector-entry.c \
+ e-name-selector-list.c \
+ e-name-selector-model.c \
+ e-name-selector.c \
+ e-online-button.c \
+ e-paned.c \
+ e-passwords.c \
+ e-picture-gallery.c \
+ e-plugin-ui.c \
+ e-plugin.c \
+ e-poolv.c \
+ e-popup-action.c \
+ e-popup-menu.c \
+ e-port-entry.c \
+ e-preferences-window.c \
+ e-preview-pane.c \
+ e-print.c \
+ e-printable.c \
+ e-reflow-model.c \
+ e-reflow.c \
+ e-rule-context.c \
+ e-rule-editor.c \
+ e-search-bar.c \
+ e-selectable.c \
+ e-selection-model-array.c \
+ e-selection-model-simple.c \
+ e-selection-model.c \
+ e-selection.c \
+ e-send-options.c \
+ e-sorter-array.c \
+ e-sorter.c \
+ e-source-combo-box.c \
+ e-source-config-backend.c \
+ e-source-config-dialog.c \
+ e-source-config.c \
+ e-source-selector-dialog.c \
+ e-source-selector.c \
+ e-source-util.c \
+ e-spell-entry.c \
+ e-stock-request.c \
+ e-table-click-to-add.c \
+ e-table-col.c \
+ e-table-column-specification.c \
+ e-table-config.c \
+ e-table-extras.c \
+ e-table-field-chooser-dialog.c \
+ e-table-field-chooser-item.c \
+ e-table-field-chooser.c \
+ e-table-group-container.c \
+ e-table-group-leaf.c \
+ e-table-group.c \
+ e-table-header-item.c \
+ e-table-header-utils.c \
+ e-table-header.c \
+ e-table-item.c \
+ e-table-memory-callbacks.c \
+ e-table-memory-store.c \
+ e-table-memory.c \
+ e-table-model.c \
+ e-table-one.c \
+ e-table-search.c \
+ e-table-selection-model.c \
+ e-table-sort-info.c \
+ e-table-sorted-variable.c \
+ e-table-sorted.c \
+ e-table-sorter.c \
+ e-table-sorting-utils.c \
+ e-table-specification.c \
+ e-table-state.c \
+ e-table-subset-variable.c \
+ e-table-subset.c \
+ e-table-utils.c \
+ e-table-without.c \
+ e-table.c \
+ e-text-event-processor-emacs-like.c \
+ e-text-event-processor.c \
+ e-text-model-repos.c \
+ e-text-model.c \
+ e-text.c \
+ e-timezone-dialog.c \
+ e-tree-memory-callbacks.c \
+ e-tree-memory.c \
+ e-tree-model-generator.c \
+ e-tree-model.c \
+ e-tree-selection-model.c \
+ e-tree-sorted.c \
+ e-tree-table-adapter.c \
+ e-tree.c \
+ e-ui-manager.c \
+ e-unicode.c \
+ e-url-entry.c \
+ e-util-enumtypes.c \
+ e-util-private.h \
+ e-web-view-gtkhtml.c \
+ e-web-view-preview.c \
+ e-web-view.c \
+ e-xml-utils.c \
+ ea-calendar-cell.c \
+ ea-calendar-item.c \
+ ea-cell-table.c \
+ ea-widgets.c \
+ gal-a11y-e-cell-popup.c \
+ gal-a11y-e-cell-registry.c \
+ gal-a11y-e-cell-toggle.c \
+ gal-a11y-e-cell-tree.c \
+ gal-a11y-e-cell-vbox.c \
+ gal-a11y-e-cell.c \
+ gal-a11y-e-table-click-to-add-factory.c \
+ gal-a11y-e-table-click-to-add.c \
+ gal-a11y-e-table-column-header.c \
+ gal-a11y-e-table-factory.c \
+ gal-a11y-e-table-item-factory.c \
+ gal-a11y-e-table-item.c \
+ gal-a11y-e-table.c \
+ gal-a11y-e-text-factory.c \
+ gal-a11y-e-text.c \
+ gal-a11y-e-tree-factory.c \
+ gal-a11y-e-tree.c \
+ gal-a11y-util.c \
+ gal-define-views-dialog.c \
+ gal-define-views-model.c \
+ gal-view-collection.c \
+ gal-view-etable.c \
+ gal-view-factory-etable.c \
+ gal-view-factory.c \
+ gal-view-instance-save-as-dialog.c \
+ gal-view-instance.c \
+ gal-view-new-dialog.c \
+ gal-view.c \
+ $(PLATFORM_SOURCES) \
+ $(NULL)
libeutil_la_LDFLAGS = -avoid-version $(NO_UNDEFINED)
-libeutil_la_LIBADD = \
- $(top_builddir)/libevolution-utils/libevolution-utils.la \
- $(ICONV_LIBS) \
- $(EVOLUTION_DATA_SERVER_LIBS) \
- $(GNOME_PLATFORM_LIBS) \
- $(INTLLIBS)
+libeutil_la_LIBADD = \
+ $(top_builddir)/libgnomecanvas/libgnomecanvas.la \
+ $(ICONV_LIBS) \
+ $(EVOLUTION_DATA_SERVER_LIBS) \
+ $(GNOME_PLATFORM_LIBS) \
+ $(CHAMPLAIN_LIBS) \
+ $(GEO_LIBS) \
+ $(GTKHTML_LIBS) \
+ $(INTLLIBS) \
+ $(MATH_LIB) \
+ $(NULL)
-error_DATA = e-system.error
-errordir = $(privdatadir)/errors
-@EVO_PLUGIN_RULE@
+TEST_CPPFLAGS = \
+ $(libeutil_la_CPPFLAGS) \
+ $(NULL)
+
+TEST_LDADD = \
+ $(top_builddir)/e-util/libeutil.la \
+ $(libeutil_la_LIBADD) \
+ $(NULL)
+
+evolution_source_viewer_CPPFLAGS = $(TEST_CPPFLAGS)
+evolution_source_viewer_SOURCES = evolution-source-viewer.c
+evolution_source_viewer_LDADD = $(TEST_LDADD)
+
+test_calendar_CPPFLAGS = $(TEST_CPPFLAGS)
+test_calendar_SOURCES = test-calendar.c
+test_calendar_LDADD = $(TEST_LDADD)
+
+test_category_completion_CPPFLAGS = $(TEST_CPPFLAGS)
+test_category_completion_SOURCES = test-category-completion.c
+test_category_completion_LDADD = $(TEST_LDADD)
+
+test_contact_store_CPPFLAGS = $(TEST_CPPFLAGS)
+test_contact_store_SOURCES = test-contact-store.c
+test_contact_store_LDADD = $(TEST_LDADD)
+
+test_dateedit_CPPFLAGS = $(TEST_CPPFLAGS)
+test_dateedit_SOURCES = test-dateedit.c
+test_dateedit_LDADD = $(TEST_LDADD)
+
+test_mail_signatures_CPPFLAGS = $(TEST_CPPFLAGS)
+test_mail_signatures_SOURCES = test-mail-signatures.c
+test_mail_signatures_LDADD = $(TEST_LDADD)
+
+test_name_selector_CPPFLAGS = $(TEST_CPPFLAGS)
+test_name_selector_SOURCES = test-name-selector.c
+test_name_selector_LDADD = $(TEST_LDADD)
+
+test_preferences_window_CPPFLAGS = $(TEST_CPPFLAGS)
+test_preferences_window_SOURCES = test-preferences-window.c
+test_preferences_window_LDADD = $(TEST_LDADD)
+
+test_source_combo_box_CPPFLAGS = $(TEST_CPPFLAGS)
+test_source_combo_box_SOURCES = test-source-combo-box.c
+test_source_combo_box_LDADD = $(TEST_LDADD)
+
+test_source_config_CPPFLAGS = $(TEST_CPPFLAGS)
+test_source_config_SOURCES = test-source-config.c
+test_source_config_LDADD = $(TEST_LDADD)
+
+test_source_selector_CPPFLAGS = $(TEST_CPPFLAGS)
+test_source_selector_SOURCES = test-source-selector.c
+test_source_selector_LDADD = $(TEST_LDADD)
+
+EXTRA_DIST = \
+ e-util-enumtypes.h.template \
+ e-util-enumtypes.c.template \
+ e-system.error.xml \
+ filter.error.xml \
+ widgets.error.xml \
+ e-marshal.list \
+ $(ui_DATA)
+ $(NULL)
-EXTRA_DIST = \
- e-util-enumtypes.h.template \
- e-util-enumtypes.c.template \
- e-system.error.xml \
- e-marshal.list
+BUILT_SOURCES = \
+ $(ENUM_GENERATED) \
+ $(MARSHAL_GENERATED) \
+ $(error_DATA) \
+ $(NULL)
-BUILT_SOURCES = $(ENUM_GENERATED) $(MARSHAL_GENERATED) $(error_DATA)
-CLEANFILES = $(BUILT_SOURCES)
+CLEANFILES = $(BUILT_SOURCES)
dist-hook:
cd $(distdir); rm -f $(BUILT_SOURCES)
diff --git a/e-util/arrow-down.xpm b/e-util/arrow-down.xpm
new file mode 100644
index 0000000000..f1e6cb4b3c
--- /dev/null
+++ b/e-util/arrow-down.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * arrow_down_xpm[] = {
+"13 16 2 1",
+" c None",
+". c #FF0000",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+".............",
+" ........... ",
+" ......... ",
+" ....... ",
+" ..... ",
+" ... ",
+" . "};
diff --git a/e-util/arrow-up.xpm b/e-util/arrow-up.xpm
new file mode 100644
index 0000000000..0cc5b9a00c
--- /dev/null
+++ b/e-util/arrow-up.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * arrow_up_xpm[] = {
+"13 16 2 1",
+" c None",
+". c #FF0000",
+" . ",
+" ... ",
+" ..... ",
+" ....... ",
+" ......... ",
+" ........... ",
+".............",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... "};
diff --git a/e-util/check-empty.xpm b/e-util/check-empty.xpm
new file mode 100644
index 0000000000..746b20234e
--- /dev/null
+++ b/e-util/check-empty.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * check_empty_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ............ ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" ............ ",
+" ",
+" "};
diff --git a/e-util/check-filled.xpm b/e-util/check-filled.xpm
new file mode 100644
index 0000000000..c0468fc25b
--- /dev/null
+++ b/e-util/check-filled.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * check_filled_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ............ ",
+" . . ",
+" . . . ",
+" . .. . ",
+" . ... . ",
+" . . ... . ",
+" . .. ... . ",
+" . ..... . ",
+" . ... . ",
+" . . . ",
+" . . ",
+" ............ ",
+" ",
+" "};
diff --git a/e-util/e-action-combo-box.c b/e-util/e-action-combo-box.c
new file mode 100644
index 0000000000..0747a6ed27
--- /dev/null
+++ b/e-util/e-action-combo-box.c
@@ -0,0 +1,578 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-action-combo-box.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-action-combo-box.h"
+
+#include <glib/gi18n.h>
+
+#define E_ACTION_COMBO_BOX_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ACTION_COMBO_BOX, EActionComboBoxPrivate))
+
+enum {
+ COLUMN_ACTION,
+ COLUMN_SORT
+};
+
+enum {
+ PROP_0,
+ PROP_ACTION
+};
+
+struct _EActionComboBoxPrivate {
+ GtkRadioAction *action;
+ GtkActionGroup *action_group;
+ GHashTable *index;
+ guint changed_handler_id; /* action::changed */
+ guint group_sensitive_handler_id; /* action-group::sensitive */
+ guint group_visible_handler_id; /* action-group::visible */
+ gboolean group_has_icons : 1;
+};
+
+G_DEFINE_TYPE (
+ EActionComboBox,
+ e_action_combo_box,
+ GTK_TYPE_COMBO_BOX)
+
+static void
+action_combo_box_action_changed_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EActionComboBox *combo_box)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ reference = g_hash_table_lookup (
+ combo_box->priv->index, GINT_TO_POINTER (
+ gtk_radio_action_get_current_value (current)));
+ g_return_if_fail (reference != NULL);
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ valid = gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+ g_return_if_fail (valid);
+
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+}
+
+static void
+action_combo_box_action_group_notify_cb (GtkActionGroup *action_group,
+ GParamSpec *pspec,
+ EActionComboBox *combo_box)
+{
+ g_object_set (
+ combo_box, "sensitive",
+ gtk_action_group_get_sensitive (action_group), "visible",
+ gtk_action_group_get_visible (action_group), NULL);
+}
+
+static void
+action_combo_box_render_pixbuf (GtkCellLayout *layout,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ EActionComboBox *combo_box)
+{
+ GtkRadioAction *action;
+ gchar *icon_name;
+ gchar *stock_id;
+ gboolean sensitive;
+ gboolean visible;
+ gint width;
+
+ gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
+
+ /* Do any of the actions have an icon? */
+ if (!combo_box->priv->group_has_icons)
+ return;
+
+ /* A NULL action means the row is a separator. */
+ if (action == NULL)
+ return;
+
+ g_object_get (
+ G_OBJECT (action),
+ "icon-name", &icon_name,
+ "sensitive", &sensitive,
+ "stock-id", &stock_id,
+ "visible", &visible,
+ NULL);
+
+ /* Keep the pixbuf renderer a fixed size for proper alignment. */
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+
+ /* We can't set both "icon-name" and "stock-id" because setting
+ * one unsets the other. So pick the one that has a non-NULL
+ * value. If both are non-NULL, "stock-id" wins. */
+
+ if (stock_id != NULL)
+ g_object_set (
+ G_OBJECT (renderer),
+ "sensitive", sensitive,
+ "icon-name", NULL,
+ "stock-id", stock_id,
+ "stock-size", GTK_ICON_SIZE_MENU,
+ "visible", visible,
+ "width", width,
+ NULL);
+ else
+ g_object_set (
+ G_OBJECT (renderer),
+ "sensitive", sensitive,
+ "icon-name", icon_name,
+ "stock-id", NULL,
+ "stock-size", GTK_ICON_SIZE_MENU,
+ "visible", visible,
+ "width", width,
+ NULL);
+
+ g_free (icon_name);
+ g_free (stock_id);
+}
+
+static void
+action_combo_box_render_text (GtkCellLayout *layout,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ EActionComboBox *combo_box)
+{
+ GtkRadioAction *action;
+ gchar **strv;
+ gchar *label;
+ gboolean sensitive;
+ gboolean visible;
+ gint xpad;
+
+ gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
+
+ /* A NULL action means the row is a separator. */
+ if (action == NULL)
+ return;
+
+ g_object_get (
+ G_OBJECT (action),
+ "label", &label,
+ "sensitive", &sensitive,
+ "visible", &visible,
+ NULL);
+
+ /* Strip out underscores. */
+ strv = g_strsplit (label, "_", -1);
+ g_free (label);
+ label = g_strjoinv (NULL, strv);
+ g_strfreev (strv);
+
+ xpad = combo_box->priv->group_has_icons ? 3 : 0;
+
+ g_object_set (
+ G_OBJECT (renderer),
+ "sensitive", sensitive,
+ "text", label,
+ "visible", visible,
+ "xpad", xpad,
+ NULL);
+
+ g_free (label);
+}
+
+static gboolean
+action_combo_box_is_row_separator (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkAction *action;
+ gboolean separator;
+
+ /* NULL actions are rendered as separators. */
+ gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
+ separator = (action == NULL);
+ if (action != NULL)
+ g_object_unref (action);
+
+ return separator;
+}
+
+static void
+action_combo_box_update_model (EActionComboBox *combo_box)
+{
+ GtkListStore *list_store;
+ GSList *list;
+
+ g_hash_table_remove_all (combo_box->priv->index);
+
+ if (combo_box->priv->action == NULL) {
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), NULL);
+ return;
+ }
+
+ /* We store values in the sort column as floats so that we can
+ * insert separators in between consecutive integer values and
+ * still maintain the proper ordering. */
+ list_store = gtk_list_store_new (
+ 2, GTK_TYPE_RADIO_ACTION, G_TYPE_FLOAT);
+
+ list = gtk_radio_action_get_group (combo_box->priv->action);
+ combo_box->priv->group_has_icons = FALSE;
+
+ while (list != NULL) {
+ GtkTreeRowReference *reference;
+ GtkRadioAction *action = list->data;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gchar *icon_name;
+ gchar *stock_id;
+ gint value;
+
+ g_object_get (
+ action, "icon-name", &icon_name,
+ "stock-id", &stock_id, NULL);
+ combo_box->priv->group_has_icons |=
+ (icon_name != NULL || stock_id != NULL);
+ g_free (icon_name);
+ g_free (stock_id);
+
+ gtk_list_store_append (list_store, &iter);
+ g_object_get (action, "value", &value, NULL);
+ gtk_list_store_set (
+ list_store, &iter, COLUMN_ACTION,
+ list->data, COLUMN_SORT, (gfloat) value, -1);
+
+ path = gtk_tree_model_get_path (
+ GTK_TREE_MODEL (list_store), &iter);
+ reference = gtk_tree_row_reference_new (
+ GTK_TREE_MODEL (list_store), path);
+ g_hash_table_insert (
+ combo_box->priv->index,
+ GINT_TO_POINTER (value), reference);
+ gtk_tree_path_free (path);
+
+ list = g_slist_next (list);
+ }
+
+ gtk_tree_sortable_set_sort_column_id (
+ GTK_TREE_SORTABLE (list_store),
+ COLUMN_SORT, GTK_SORT_ASCENDING);
+ gtk_combo_box_set_model (
+ GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (list_store));
+
+ action_combo_box_action_changed_cb (
+ combo_box->priv->action,
+ combo_box->priv->action,
+ combo_box);
+}
+
+static void
+action_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTION:
+ e_action_combo_box_set_action (
+ E_ACTION_COMBO_BOX (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+action_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTION:
+ g_value_set_object (
+ value, e_action_combo_box_get_action (
+ E_ACTION_COMBO_BOX (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+action_combo_box_dispose (GObject *object)
+{
+ EActionComboBoxPrivate *priv = E_ACTION_COMBO_BOX_GET_PRIVATE (object);
+
+ if (priv->action != NULL) {
+ g_object_unref (priv->action);
+ priv->action = NULL;
+ }
+
+ if (priv->action_group != NULL) {
+ g_object_unref (priv->action_group);
+ priv->action_group = NULL;
+ }
+
+ g_hash_table_remove_all (priv->index);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_action_combo_box_parent_class)->dispose (object);
+}
+
+static void
+action_combo_box_finalize (GObject *object)
+{
+ EActionComboBoxPrivate *priv = E_ACTION_COMBO_BOX_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->index);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_action_combo_box_parent_class)->finalize (object);
+}
+
+static void
+action_combo_box_constructed (GObject *object)
+{
+ GtkComboBox *combo_box;
+ GtkCellRenderer *renderer;
+
+ combo_box = GTK_COMBO_BOX (object);
+
+ /* This needs to happen after constructor properties are set
+ * so that GtkCellLayout.get_area() returns something valid. */
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (
+ GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
+ gtk_cell_layout_set_cell_data_func (
+ GTK_CELL_LAYOUT (combo_box), renderer,
+ (GtkCellLayoutDataFunc) action_combo_box_render_pixbuf,
+ combo_box, NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (
+ GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
+ gtk_cell_layout_set_cell_data_func (
+ GTK_CELL_LAYOUT (combo_box), renderer,
+ (GtkCellLayoutDataFunc) action_combo_box_render_text,
+ combo_box, NULL);
+
+ gtk_combo_box_set_row_separator_func (
+ combo_box, (GtkTreeViewRowSeparatorFunc)
+ action_combo_box_is_row_separator, NULL, NULL);
+}
+
+static void
+action_combo_box_changed (GtkComboBox *combo_box)
+{
+ GtkRadioAction *action;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gint value;
+
+ /* This method is virtual, so no need to chain up. */
+
+ if (!gtk_combo_box_get_active_iter (combo_box, &iter))
+ return;
+
+ model = gtk_combo_box_get_model (combo_box);
+ gtk_tree_model_get (model, &iter, COLUMN_ACTION, &action, -1);
+ g_object_get (action, "value", &value, NULL);
+ gtk_radio_action_set_current_value (action, value);
+}
+
+static void
+e_action_combo_box_class_init (EActionComboBoxClass *class)
+{
+ GObjectClass *object_class;
+ GtkComboBoxClass *combo_box_class;
+
+ g_type_class_add_private (class, sizeof (EActionComboBoxPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = action_combo_box_set_property;
+ object_class->get_property = action_combo_box_get_property;
+ object_class->dispose = action_combo_box_dispose;
+ object_class->finalize = action_combo_box_finalize;
+ object_class->constructed = action_combo_box_constructed;
+
+ combo_box_class = GTK_COMBO_BOX_CLASS (class);
+ combo_box_class->changed = action_combo_box_changed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTION,
+ g_param_spec_object (
+ "action",
+ "Action",
+ "A GtkRadioAction",
+ GTK_TYPE_RADIO_ACTION,
+ G_PARAM_READWRITE));
+}
+
+static void
+e_action_combo_box_init (EActionComboBox *combo_box)
+{
+ combo_box->priv = E_ACTION_COMBO_BOX_GET_PRIVATE (combo_box);
+
+ combo_box->priv->index = g_hash_table_new_full (
+ g_direct_hash, g_direct_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) gtk_tree_row_reference_free);
+}
+
+GtkWidget *
+e_action_combo_box_new (void)
+{
+ return e_action_combo_box_new_with_action (NULL);
+}
+
+GtkWidget *
+e_action_combo_box_new_with_action (GtkRadioAction *action)
+{
+ return g_object_new (E_TYPE_ACTION_COMBO_BOX, "action", action, NULL);
+}
+
+GtkRadioAction *
+e_action_combo_box_get_action (EActionComboBox *combo_box)
+{
+ g_return_val_if_fail (E_ACTION_IS_COMBO_BOX (combo_box), NULL);
+
+ return combo_box->priv->action;
+}
+
+void
+e_action_combo_box_set_action (EActionComboBox *combo_box,
+ GtkRadioAction *action)
+{
+ g_return_if_fail (E_ACTION_IS_COMBO_BOX (combo_box));
+
+ if (action != NULL)
+ g_return_if_fail (GTK_IS_RADIO_ACTION (action));
+
+ if (combo_box->priv->action != NULL) {
+ g_signal_handler_disconnect (
+ combo_box->priv->action,
+ combo_box->priv->changed_handler_id);
+ g_object_unref (combo_box->priv->action);
+ }
+
+ if (combo_box->priv->action_group != NULL) {
+ g_signal_handler_disconnect (
+ combo_box->priv->action_group,
+ combo_box->priv->group_sensitive_handler_id);
+ g_signal_handler_disconnect (
+ combo_box->priv->action_group,
+ combo_box->priv->group_visible_handler_id);
+ g_object_unref (combo_box->priv->action_group);
+ combo_box->priv->action_group = NULL;
+ }
+
+ if (action != NULL)
+ g_object_get (
+ g_object_ref (action), "action-group",
+ &combo_box->priv->action_group, NULL);
+
+ combo_box->priv->action = action;
+ action_combo_box_update_model (combo_box);
+
+ if (combo_box->priv->action != NULL)
+ combo_box->priv->changed_handler_id = g_signal_connect (
+ combo_box->priv->action, "changed",
+ G_CALLBACK (action_combo_box_action_changed_cb),
+ combo_box);
+
+ if (combo_box->priv->action_group != NULL) {
+ g_object_ref (combo_box->priv->action_group);
+ combo_box->priv->group_sensitive_handler_id =
+ g_signal_connect (
+ combo_box->priv->action_group,
+ "notify::sensitive", G_CALLBACK (
+ action_combo_box_action_group_notify_cb),
+ combo_box);
+ combo_box->priv->group_visible_handler_id =
+ g_signal_connect (
+ combo_box->priv->action_group,
+ "notify::visible", G_CALLBACK (
+ action_combo_box_action_group_notify_cb),
+ combo_box);
+ }
+
+ g_object_notify (G_OBJECT (combo_box), "action");
+}
+
+gint
+e_action_combo_box_get_current_value (EActionComboBox *combo_box)
+{
+ g_return_val_if_fail (E_ACTION_IS_COMBO_BOX (combo_box), 0);
+ g_return_val_if_fail (combo_box->priv->action != NULL, 0);
+
+ return gtk_radio_action_get_current_value (combo_box->priv->action);
+}
+
+void
+e_action_combo_box_set_current_value (EActionComboBox *combo_box,
+ gint current_value)
+{
+ g_return_if_fail (E_ACTION_IS_COMBO_BOX (combo_box));
+ g_return_if_fail (combo_box->priv->action != NULL);
+
+ gtk_radio_action_set_current_value (
+ combo_box->priv->action, current_value);
+}
+
+void
+e_action_combo_box_add_separator_before (EActionComboBox *combo_box,
+ gint action_value)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_if_fail (E_ACTION_IS_COMBO_BOX (combo_box));
+
+ /* NULL actions are rendered as separators. */
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (
+ GTK_LIST_STORE (model), &iter, COLUMN_ACTION,
+ NULL, COLUMN_SORT, (gfloat) action_value - 0.5, -1);
+}
+
+void
+e_action_combo_box_add_separator_after (EActionComboBox *combo_box,
+ gint action_value)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_if_fail (E_ACTION_IS_COMBO_BOX (combo_box));
+
+ /* NULL actions are rendered as separators. */
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (
+ GTK_LIST_STORE (model), &iter, COLUMN_ACTION,
+ NULL, COLUMN_SORT, (gfloat) action_value + 0.5, -1);
+}
diff --git a/e-util/e-action-combo-box.h b/e-util/e-action-combo-box.h
new file mode 100644
index 0000000000..43adeaff7a
--- /dev/null
+++ b/e-util/e-action-combo-box.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-action-combo-box.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ACTION_COMBO_BOX_H
+#define E_ACTION_COMBO_BOX_H
+
+/* This is a GtkComboBox that is driven by a group of GtkRadioActions.
+ * Just plug in a GtkRadioAction and the widget will handle the rest.
+ * (Based on GtkhtmlComboBox.) */
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ACTION_COMBO_BOX \
+ (e_action_combo_box_get_type ())
+#define E_ACTION_COMBO_BOX(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ACTION_COMBO_BOX, EActionComboBox))
+#define E_ACTION_COMBO_BOX_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ACTION_COMBO_BOX, EActionComboBoxClass))
+#define E_ACTION_IS_COMBO_BOX(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ACTION_COMBO_BOX))
+#define E_ACTION_IS_COMBO_BOX_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ACTION_COMBO_BOX))
+#define E_ACTION_COMBO_BOX_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ACTION_COMBO_BOX, EActionComboBoxClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EActionComboBox EActionComboBox;
+typedef struct _EActionComboBoxClass EActionComboBoxClass;
+typedef struct _EActionComboBoxPrivate EActionComboBoxPrivate;
+
+struct _EActionComboBox {
+ GtkComboBox parent;
+ EActionComboBoxPrivate *priv;
+};
+
+struct _EActionComboBoxClass {
+ GtkComboBoxClass parent_class;
+};
+
+GType e_action_combo_box_get_type (void);
+GtkWidget * e_action_combo_box_new (void);
+GtkWidget * e_action_combo_box_new_with_action
+ (GtkRadioAction *action);
+GtkRadioAction *e_action_combo_box_get_action (EActionComboBox *combo_box);
+void e_action_combo_box_set_action (EActionComboBox *combo_box,
+ GtkRadioAction *action);
+gint e_action_combo_box_get_current_value
+ (EActionComboBox *combo_box);
+void e_action_combo_box_set_current_value
+ (EActionComboBox *combo_box,
+ gint current_value);
+void e_action_combo_box_add_separator_before
+ (EActionComboBox *combo_box,
+ gint action_value);
+void e_action_combo_box_add_separator_after
+ (EActionComboBox *combo_box,
+ gint action_value);
+
+G_END_DECLS
+
+#endif /* E_ACTION_COMBO_BOX_H */
diff --git a/e-util/e-activity-bar.c b/e-util/e-activity-bar.c
new file mode 100644
index 0000000000..f85b403bd7
--- /dev/null
+++ b/e-util/e-activity-bar.c
@@ -0,0 +1,366 @@
+/*
+ * e-activity-bar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-activity-bar.h"
+
+#define E_ACTIVITY_BAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBarPrivate))
+
+#define FEEDBACK_PERIOD 1 /* seconds */
+#define COMPLETED_ICON_NAME "emblem-default"
+
+struct _EActivityBarPrivate {
+ EActivity *activity; /* weak reference */
+ GtkWidget *image; /* not referenced */
+ GtkWidget *label; /* not referenced */
+ GtkWidget *cancel; /* not referenced */
+ GtkWidget *spinner; /* not referenced */
+
+ /* If the user clicks the Cancel button, keep the cancelled
+ * EActivity object alive for a short duration so the user
+ * gets some visual feedback that cancellation worked. */
+ guint timeout_id;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVITY
+};
+
+G_DEFINE_TYPE (
+ EActivityBar,
+ e_activity_bar,
+ GTK_TYPE_INFO_BAR)
+
+static void
+activity_bar_feedback (EActivityBar *bar)
+{
+ EActivity *activity;
+ EActivityState state;
+
+ activity = e_activity_bar_get_activity (bar);
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ state = e_activity_get_state (activity);
+ if (state != E_ACTIVITY_CANCELLED && state != E_ACTIVITY_COMPLETED)
+ return;
+
+ if (bar->priv->timeout_id > 0)
+ g_source_remove (bar->priv->timeout_id);
+
+ /* Hold a reference on the EActivity for a short
+ * period so the activity bar stays visible. */
+ bar->priv->timeout_id = g_timeout_add_seconds_full (
+ G_PRIORITY_LOW, FEEDBACK_PERIOD, (GSourceFunc) gtk_false,
+ g_object_ref (activity), (GDestroyNotify) g_object_unref);
+}
+
+static void
+activity_bar_update (EActivityBar *bar)
+{
+ EActivity *activity;
+ EActivityState state;
+ GCancellable *cancellable;
+ const gchar *icon_name;
+ gboolean sensitive;
+ gboolean visible;
+ gchar *description;
+
+ activity = e_activity_bar_get_activity (bar);
+
+ if (activity == NULL) {
+ gtk_widget_hide (GTK_WIDGET (bar));
+ return;
+ }
+
+ cancellable = e_activity_get_cancellable (activity);
+ icon_name = e_activity_get_icon_name (activity);
+ state = e_activity_get_state (activity);
+
+ description = e_activity_describe (activity);
+ gtk_label_set_text (GTK_LABEL (bar->priv->label), description);
+
+ if (state == E_ACTIVITY_CANCELLED) {
+ PangoAttribute *attr;
+ PangoAttrList *attr_list;
+
+ attr_list = pango_attr_list_new ();
+
+ attr = pango_attr_strikethrough_new (TRUE);
+ pango_attr_list_insert (attr_list, attr);
+
+ gtk_label_set_attributes (
+ GTK_LABEL (bar->priv->label), attr_list);
+
+ pango_attr_list_unref (attr_list);
+ } else
+ gtk_label_set_attributes (
+ GTK_LABEL (bar->priv->label), NULL);
+
+ if (state == E_ACTIVITY_COMPLETED)
+ icon_name = COMPLETED_ICON_NAME;
+
+ if (state == E_ACTIVITY_CANCELLED) {
+ gtk_image_set_from_stock (
+ GTK_IMAGE (bar->priv->image),
+ GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_show (bar->priv->image);
+ } else if (icon_name != NULL) {
+ gtk_image_set_from_icon_name (
+ GTK_IMAGE (bar->priv->image),
+ icon_name, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_show (bar->priv->image);
+ } else {
+ gtk_widget_hide (bar->priv->image);
+ }
+
+ visible = (cancellable != NULL);
+ gtk_widget_set_visible (bar->priv->cancel, visible);
+
+ sensitive = (state == E_ACTIVITY_RUNNING);
+ gtk_widget_set_sensitive (bar->priv->cancel, sensitive);
+
+ visible = (description != NULL && *description != '\0');
+ gtk_widget_set_visible (GTK_WIDGET (bar), visible);
+
+ g_free (description);
+}
+
+static void
+activity_bar_cancel (EActivityBar *bar)
+{
+ EActivity *activity;
+ GCancellable *cancellable;
+
+ activity = e_activity_bar_get_activity (bar);
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ cancellable = e_activity_get_cancellable (activity);
+ g_cancellable_cancel (cancellable);
+
+ activity_bar_update (bar);
+}
+
+static void
+activity_bar_weak_notify_cb (EActivityBar *bar,
+ GObject *where_the_object_was)
+{
+ g_return_if_fail (E_IS_ACTIVITY_BAR (bar));
+
+ bar->priv->activity = NULL;
+ e_activity_bar_set_activity (bar, NULL);
+}
+
+static void
+activity_bar_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVITY:
+ e_activity_bar_set_activity (
+ E_ACTIVITY_BAR (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+activity_bar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVITY:
+ g_value_set_object (
+ value, e_activity_bar_get_activity (
+ E_ACTIVITY_BAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+activity_bar_dispose (GObject *object)
+{
+ EActivityBarPrivate *priv;
+
+ priv = E_ACTIVITY_BAR_GET_PRIVATE (object);
+
+ if (priv->timeout_id > 0) {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
+
+ if (priv->activity != NULL) {
+ g_signal_handlers_disconnect_matched (
+ priv->activity, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_weak_unref (
+ G_OBJECT (priv->activity), (GWeakNotify)
+ activity_bar_weak_notify_cb, object);
+ priv->activity = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_activity_bar_parent_class)->dispose (object);
+}
+
+static void
+e_activity_bar_class_init (EActivityBarClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EActivityBarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = activity_bar_set_property;
+ object_class->get_property = activity_bar_get_property;
+ object_class->dispose = activity_bar_dispose;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTIVITY,
+ g_param_spec_object (
+ "activity",
+ NULL,
+ NULL,
+ E_TYPE_ACTIVITY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+e_activity_bar_init (EActivityBar *bar)
+{
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ bar->priv = E_ACTIVITY_BAR_GET_PRIVATE (bar);
+
+ container = gtk_info_bar_get_content_area (GTK_INFO_BAR (bar));
+
+ widget = gtk_hbox_new (FALSE, 12);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->image = widget;
+
+ widget = gtk_spinner_new ();
+ gtk_spinner_start (GTK_SPINNER (widget));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->spinner = widget;
+
+ /* The spinner is only visible when the image is not. */
+ g_object_bind_property (
+ bar->priv->image, "visible",
+ bar->priv->spinner, "visible",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_INVERT_BOOLEAN);
+
+ widget = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ bar->priv->label = widget;
+ gtk_widget_show (widget);
+
+ /* This is only shown if the EActivity has a GCancellable. */
+ widget = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
+ gtk_info_bar_add_action_widget (
+ GTK_INFO_BAR (bar), widget, GTK_RESPONSE_CANCEL);
+ bar->priv->cancel = widget;
+ gtk_widget_hide (widget);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (activity_bar_cancel), bar);
+}
+
+GtkWidget *
+e_activity_bar_new (void)
+{
+ return g_object_new (E_TYPE_ACTIVITY_BAR, NULL);
+}
+
+EActivity *
+e_activity_bar_get_activity (EActivityBar *bar)
+{
+ g_return_val_if_fail (E_IS_ACTIVITY_BAR (bar), NULL);
+
+ return bar->priv->activity;
+}
+
+void
+e_activity_bar_set_activity (EActivityBar *bar,
+ EActivity *activity)
+{
+ g_return_if_fail (E_IS_ACTIVITY_BAR (bar));
+
+ if (activity != NULL)
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ if (bar->priv->timeout_id > 0) {
+ g_source_remove (bar->priv->timeout_id);
+ bar->priv->timeout_id = 0;
+ }
+
+ if (bar->priv->activity != NULL) {
+ g_signal_handlers_disconnect_matched (
+ bar->priv->activity, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, bar);
+ g_object_weak_unref (
+ G_OBJECT (bar->priv->activity),
+ (GWeakNotify) activity_bar_weak_notify_cb, bar);
+ }
+
+ bar->priv->activity = activity;
+
+ if (activity != NULL) {
+ g_object_weak_ref (
+ G_OBJECT (activity), (GWeakNotify)
+ activity_bar_weak_notify_cb, bar);
+
+ g_signal_connect_swapped (
+ activity, "notify::state",
+ G_CALLBACK (activity_bar_feedback), bar);
+
+ g_signal_connect_swapped (
+ activity, "notify",
+ G_CALLBACK (activity_bar_update), bar);
+ }
+
+ activity_bar_update (bar);
+
+ g_object_notify (G_OBJECT (bar), "activity");
+}
diff --git a/e-util/e-activity-bar.h b/e-util/e-activity-bar.h
new file mode 100644
index 0000000000..d56378e2c1
--- /dev/null
+++ b/e-util/e-activity-bar.h
@@ -0,0 +1,71 @@
+/*
+ * e-activity-bar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ACTIVITY_BAR_H
+#define E_ACTIVITY_BAR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-activity.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ACTIVITY_BAR \
+ (e_activity_bar_get_type ())
+#define E_ACTIVITY_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBar))
+#define E_ACTIVITY_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ACTIVITY_BAR, EActivityBarClass))
+#define E_IS_ACTIVITY_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ACTIVITY_BAR))
+#define E_IS_ACTIVITY_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ACTIVITY_BAR))
+#define E_ACTIVITY_BAR_GET_CLASS(obj) \
+ (G_TYPE_CHECK_INSTANCE_GET_TYPE \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EActivityBar EActivityBar;
+typedef struct _EActivityBarClass EActivityBarClass;
+typedef struct _EActivityBarPrivate EActivityBarPrivate;
+
+struct _EActivityBar {
+ GtkInfoBar parent;
+ EActivityBarPrivate *priv;
+};
+
+struct _EActivityBarClass {
+ GtkInfoBarClass parent_class;
+};
+
+GType e_activity_bar_get_type (void);
+GtkWidget * e_activity_bar_new (void);
+EActivity * e_activity_bar_get_activity (EActivityBar *bar);
+void e_activity_bar_set_activity (EActivityBar *bar,
+ EActivity *activity);
+
+G_END_DECLS
+
+#endif /* E_ACTIVITY_BAR_H */
diff --git a/e-util/e-activity-proxy.c b/e-util/e-activity-proxy.c
new file mode 100644
index 0000000000..7547088aac
--- /dev/null
+++ b/e-util/e-activity-proxy.c
@@ -0,0 +1,381 @@
+/*
+ * e-activity-proxy.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-activity-proxy.h"
+
+#include <glib/gi18n.h>
+
+#define E_ACTIVITY_PROXY_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ACTIVITY_PROXY, EActivityProxyPrivate))
+
+#define FEEDBACK_PERIOD 1 /* seconds */
+#define COMPLETED_ICON_NAME "emblem-default"
+
+struct _EActivityProxyPrivate {
+ EActivity *activity; /* weak reference */
+ GtkWidget *image; /* not referenced */
+ GtkWidget *label; /* not referenced */
+ GtkWidget *cancel; /* not referenced */
+ GtkWidget *spinner; /* not referenced */
+
+ /* If the user clicks the Cancel button, keep the cancelled
+ * EActivity object alive for a short duration so the user
+ * gets some visual feedback that cancellation worked. */
+ guint timeout_id;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVITY
+};
+
+G_DEFINE_TYPE (
+ EActivityProxy,
+ e_activity_proxy,
+ GTK_TYPE_FRAME)
+
+static void
+activity_proxy_feedback (EActivityProxy *proxy)
+{
+ EActivity *activity;
+ EActivityState state;
+
+ activity = e_activity_proxy_get_activity (proxy);
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ state = e_activity_get_state (activity);
+ if (state != E_ACTIVITY_CANCELLED)
+ return;
+
+ if (proxy->priv->timeout_id > 0)
+ g_source_remove (proxy->priv->timeout_id);
+
+ /* Hold a reference on the EActivity for a short
+ * period so the activity proxy stays visible. */
+ proxy->priv->timeout_id = g_timeout_add_seconds_full (
+ G_PRIORITY_LOW, FEEDBACK_PERIOD, (GSourceFunc) gtk_false,
+ g_object_ref (activity), (GDestroyNotify) g_object_unref);
+}
+
+static void
+activity_proxy_update (EActivityProxy *proxy)
+{
+ EActivity *activity;
+ EActivityState state;
+ GCancellable *cancellable;
+ const gchar *icon_name;
+ gboolean sensitive;
+ gboolean visible;
+ gchar *description;
+
+ activity = e_activity_proxy_get_activity (proxy);
+
+ if (activity == NULL) {
+ gtk_widget_hide (GTK_WIDGET (proxy));
+ return;
+ }
+
+ cancellable = e_activity_get_cancellable (activity);
+ icon_name = e_activity_get_icon_name (activity);
+ state = e_activity_get_state (activity);
+
+ description = e_activity_describe (activity);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (proxy), description);
+ gtk_label_set_text (GTK_LABEL (proxy->priv->label), description);
+
+ if (state == E_ACTIVITY_CANCELLED) {
+ PangoAttribute *attr;
+ PangoAttrList *attr_list;
+
+ attr_list = pango_attr_list_new ();
+
+ attr = pango_attr_strikethrough_new (TRUE);
+ pango_attr_list_insert (attr_list, attr);
+
+ gtk_label_set_attributes (
+ GTK_LABEL (proxy->priv->label), attr_list);
+
+ pango_attr_list_unref (attr_list);
+ } else
+ gtk_label_set_attributes (
+ GTK_LABEL (proxy->priv->label), NULL);
+
+ if (state == E_ACTIVITY_COMPLETED)
+ icon_name = COMPLETED_ICON_NAME;
+
+ if (state == E_ACTIVITY_CANCELLED) {
+ gtk_image_set_from_stock (
+ GTK_IMAGE (proxy->priv->image),
+ GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_show (proxy->priv->image);
+ } else if (icon_name != NULL) {
+ gtk_image_set_from_icon_name (
+ GTK_IMAGE (proxy->priv->image),
+ icon_name, GTK_ICON_SIZE_MENU);
+ gtk_widget_show (proxy->priv->image);
+ } else {
+ gtk_widget_hide (proxy->priv->image);
+ }
+
+ visible = (cancellable != NULL);
+ gtk_widget_set_visible (proxy->priv->cancel, visible);
+
+ sensitive = (state == E_ACTIVITY_RUNNING);
+ gtk_widget_set_sensitive (proxy->priv->cancel, sensitive);
+
+ visible = (description != NULL && *description != '\0');
+ gtk_widget_set_visible (GTK_WIDGET (proxy), visible);
+
+ g_free (description);
+}
+
+static void
+activity_proxy_cancel (EActivityProxy *proxy)
+{
+ EActivity *activity;
+ GCancellable *cancellable;
+
+ activity = e_activity_proxy_get_activity (proxy);
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ cancellable = e_activity_get_cancellable (activity);
+ g_cancellable_cancel (cancellable);
+
+ activity_proxy_update (proxy);
+}
+
+static void
+activity_proxy_weak_notify_cb (EActivityProxy *proxy,
+ GObject *where_the_object_was)
+{
+ g_return_if_fail (E_IS_ACTIVITY_PROXY (proxy));
+
+ proxy->priv->activity = NULL;
+ e_activity_proxy_set_activity (proxy, NULL);
+}
+
+static void
+activity_proxy_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVITY:
+ e_activity_proxy_set_activity (
+ E_ACTIVITY_PROXY (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+activity_proxy_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVITY:
+ g_value_set_object (
+ value, e_activity_proxy_get_activity (
+ E_ACTIVITY_PROXY (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+activity_proxy_dispose (GObject *object)
+{
+ EActivityProxyPrivate *priv;
+
+ priv = E_ACTIVITY_PROXY_GET_PRIVATE (object);
+
+ if (priv->timeout_id > 0) {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
+
+ if (priv->activity != NULL) {
+ g_signal_handlers_disconnect_matched (
+ priv->activity, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_weak_unref (
+ G_OBJECT (priv->activity), (GWeakNotify)
+ activity_proxy_weak_notify_cb, object);
+ priv->activity = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_activity_proxy_parent_class)->dispose (object);
+}
+
+static void
+e_activity_proxy_class_init (EActivityProxyClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EActivityProxyPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = activity_proxy_set_property;
+ object_class->get_property = activity_proxy_get_property;
+ object_class->dispose = activity_proxy_dispose;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTIVITY,
+ g_param_spec_object (
+ "activity",
+ NULL,
+ NULL,
+ E_TYPE_ACTIVITY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+e_activity_proxy_init (EActivityProxy *proxy)
+{
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ proxy->priv = E_ACTIVITY_PROXY_GET_PRIVATE (proxy);
+
+ gtk_frame_set_shadow_type (GTK_FRAME (proxy), GTK_SHADOW_IN);
+
+ container = GTK_WIDGET (proxy);
+
+ widget = gtk_hbox_new (FALSE, 3);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ proxy->priv->image = widget;
+
+ widget = gtk_spinner_new ();
+ gtk_spinner_start (GTK_SPINNER (widget));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 3);
+ proxy->priv->spinner = widget;
+
+ /* The spinner is only visible when the image is not. */
+ g_object_bind_property (
+ proxy->priv->image, "visible",
+ proxy->priv->spinner, "visible",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_INVERT_BOOLEAN);
+
+ widget = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ proxy->priv->label = widget;
+ gtk_widget_show (widget);
+
+ /* This is only shown if the EActivity has a GCancellable. */
+ widget = gtk_button_new ();
+ gtk_button_set_image (
+ GTK_BUTTON (widget), gtk_image_new_from_stock (
+ GTK_STOCK_CANCEL, GTK_ICON_SIZE_MENU));
+ gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text (widget, _("Cancel"));
+ proxy->priv->cancel = widget;
+ gtk_widget_show (widget);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (activity_proxy_cancel), proxy);
+}
+
+GtkWidget *
+e_activity_proxy_new (EActivity *activity)
+{
+ g_return_val_if_fail (E_IS_ACTIVITY (activity), NULL);
+
+ return g_object_new (
+ E_TYPE_ACTIVITY_PROXY, "activity", activity, NULL);
+}
+
+EActivity *
+e_activity_proxy_get_activity (EActivityProxy *proxy)
+{
+ g_return_val_if_fail (E_IS_ACTIVITY_PROXY (proxy), NULL);
+
+ return proxy->priv->activity;
+}
+
+void
+e_activity_proxy_set_activity (EActivityProxy *proxy,
+ EActivity *activity)
+{
+ g_return_if_fail (E_IS_ACTIVITY_PROXY (proxy));
+
+ if (activity != NULL)
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ if (proxy->priv->timeout_id > 0) {
+ g_source_remove (proxy->priv->timeout_id);
+ proxy->priv->timeout_id = 0;
+ }
+
+ if (proxy->priv->activity != NULL) {
+ g_signal_handlers_disconnect_matched (
+ proxy->priv->activity, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, proxy);
+ g_object_weak_unref (
+ G_OBJECT (proxy->priv->activity),
+ (GWeakNotify) activity_proxy_weak_notify_cb, proxy);
+ }
+
+ proxy->priv->activity = activity;
+
+ if (activity != NULL) {
+ g_object_weak_ref (
+ G_OBJECT (activity), (GWeakNotify)
+ activity_proxy_weak_notify_cb, proxy);
+
+ g_signal_connect_swapped (
+ activity, "notify::state",
+ G_CALLBACK (activity_proxy_feedback), proxy);
+
+ g_signal_connect_swapped (
+ activity, "notify",
+ G_CALLBACK (activity_proxy_update), proxy);
+ }
+
+ activity_proxy_update (proxy);
+
+ g_object_notify (G_OBJECT (proxy), "activity");
+}
diff --git a/e-util/e-activity-proxy.h b/e-util/e-activity-proxy.h
new file mode 100644
index 0000000000..75125351f4
--- /dev/null
+++ b/e-util/e-activity-proxy.h
@@ -0,0 +1,74 @@
+/*
+ * e-activity-proxy.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ACTIVITY_PROXY_H
+#define E_ACTIVITY_PROXY_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-activity.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ACTIVITY_PROXY \
+ (e_activity_proxy_get_type ())
+#define E_ACTIVITY_PROXY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ACTIVITY_PROXY, EActivityProxy))
+#define E_ACTIVITY_PROXY_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ACTIVITY_PROXY, EActivityProxyClass))
+#define E_IS_ACTIVITY_PROXY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ACTIVITY_PROXY))
+#define E_IS_ACTIVITY_PROXY_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ACTIVITY_PROXY))
+#define E_ACTIVITY_PROXY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ACTIVITY_PROXY, EActivityProxyClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EActivityProxy EActivityProxy;
+typedef struct _EActivityProxyClass EActivityProxyClass;
+typedef struct _EActivityProxyPrivate EActivityProxyPrivate;
+
+struct _EActivityProxy {
+ GtkFrame parent;
+ EActivityProxyPrivate *priv;
+};
+
+struct _EActivityProxyClass {
+ GtkFrameClass parent_class;
+};
+
+GType e_activity_proxy_get_type (void);
+GtkWidget * e_activity_proxy_new (EActivity *activity);
+EActivity * e_activity_proxy_get_activity (EActivityProxy *proxy);
+void e_activity_proxy_set_activity (EActivityProxy *proxy,
+ EActivity *activity);
+
+G_END_DECLS
+
+#endif /* E_ACTIVITY_PROXY_H */
diff --git a/e-util/e-activity.c b/e-util/e-activity.c
index cd1c5699b2..5eefb652b0 100644
--- a/e-util/e-activity.c
+++ b/e-util/e-activity.c
@@ -29,8 +29,7 @@
#include <glib/gi18n.h>
#include <camel/camel.h>
-#include "e-util/e-util.h"
-#include "e-util/e-util-enumtypes.h"
+#include "e-util-enumtypes.h"
#define E_ACTIVITY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
diff --git a/e-util/e-activity.h b/e-util/e-activity.h
index 4cc9951fde..ac380a030c 100644
--- a/e-util/e-activity.h
+++ b/e-util/e-activity.h
@@ -19,11 +19,16 @@
*
*/
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
#ifndef E_ACTIVITY_H
#define E_ACTIVITY_H
#include <gtk/gtk.h>
-#include <libevolution-utils/e-alert-sink.h>
+
+#include <e-util/e-alert-sink.h>
#include <e-util/e-util-enums.h>
/* Standard GObject macros */
diff --git a/e-util/e-alarm-selector.c b/e-util/e-alarm-selector.c
new file mode 100644
index 0000000000..bdc1b7e35e
--- /dev/null
+++ b/e-util/e-alarm-selector.c
@@ -0,0 +1,94 @@
+/*
+ * e-alarm-selector.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-alarm-selector.h"
+
+G_DEFINE_TYPE (
+ EAlarmSelector,
+ e_alarm_selector,
+ E_TYPE_SOURCE_SELECTOR)
+
+static gboolean
+alarm_selector_get_source_selected (ESourceSelector *selector,
+ ESource *source)
+{
+ ESourceAlarms *extension;
+ const gchar *extension_name;
+
+ /* Make sure this source is a calendar. */
+ extension_name = e_source_selector_get_extension_name (selector);
+ if (!e_source_has_extension (source, extension_name))
+ return FALSE;
+
+ extension_name = E_SOURCE_EXTENSION_ALARMS;
+ extension = e_source_get_extension (source, extension_name);
+ g_return_val_if_fail (E_IS_SOURCE_ALARMS (extension), FALSE);
+
+ return e_source_alarms_get_include_me (extension);
+}
+
+static void
+alarm_selector_set_source_selected (ESourceSelector *selector,
+ ESource *source,
+ gboolean selected)
+{
+ ESourceAlarms *extension;
+ const gchar *extension_name;
+
+ /* Make sure this source is a calendar. */
+ extension_name = e_source_selector_get_extension_name (selector);
+ if (!e_source_has_extension (source, extension_name))
+ return;
+
+ extension_name = E_SOURCE_EXTENSION_ALARMS;
+ extension = e_source_get_extension (source, extension_name);
+ g_return_if_fail (E_IS_SOURCE_ALARMS (extension));
+
+ if (selected != e_source_alarms_get_include_me (extension)) {
+ e_source_alarms_set_include_me (extension, selected);
+ e_source_selector_queue_write (selector, source);
+ }
+}
+
+static void
+e_alarm_selector_class_init (EAlarmSelectorClass *class)
+{
+ ESourceSelectorClass *source_selector_class;
+
+ source_selector_class = E_SOURCE_SELECTOR_CLASS (class);
+ source_selector_class->get_source_selected =
+ alarm_selector_get_source_selected;
+ source_selector_class->set_source_selected =
+ alarm_selector_set_source_selected;
+}
+
+static void
+e_alarm_selector_init (EAlarmSelector *selector)
+{
+}
+
+GtkWidget *
+e_alarm_selector_new (ESourceRegistry *registry)
+{
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+ return g_object_new (
+ E_TYPE_ALARM_SELECTOR,
+ "extension-name", E_SOURCE_EXTENSION_CALENDAR,
+ "registry", registry, NULL);
+}
diff --git a/e-util/e-alarm-selector.h b/e-util/e-alarm-selector.h
new file mode 100644
index 0000000000..c545a46cf1
--- /dev/null
+++ b/e-util/e-alarm-selector.h
@@ -0,0 +1,67 @@
+/*
+ * e-alarm-selector.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ALARM_SELECTOR_H
+#define E_ALARM_SELECTOR_H
+
+#include <e-util/e-source-selector.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ALARM_SELECTOR \
+ (e_alarm_selector_get_type ())
+#define E_ALARM_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ALARM_SELECTOR, EAlarmSelector))
+#define E_ALARM_SELECTOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ALARM_SELECTOR, EAlarmSelectorClass))
+#define E_IS_ALARM_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ALARM_SELECTOR))
+#define E_IS_ALARM_SELECTOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ALARM_SELECTOR))
+#define E_ALARM_SELECTOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ALARM_SELECTOR, EAlarmSelectorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAlarmSelector EAlarmSelector;
+typedef struct _EAlarmSelectorClass EAlarmSelectorClass;
+typedef struct _EAlarmSelectorPrivate EAlarmSelectorPrivate;
+
+struct _EAlarmSelector {
+ ESourceSelector parent;
+ EAlarmSelectorPrivate *priv;
+};
+
+struct _EAlarmSelectorClass {
+ ESourceSelectorClass parent_class;
+};
+
+GType e_alarm_selector_get_type (void) G_GNUC_CONST;
+GtkWidget * e_alarm_selector_new (ESourceRegistry *registry);
+
+G_END_DECLS
+
+#endif /* E_ALARM_SELECTOR_H */
diff --git a/e-util/e-alert-bar.c b/e-util/e-alert-bar.c
new file mode 100644
index 0000000000..2022af99f1
--- /dev/null
+++ b/e-util/e-alert-bar.c
@@ -0,0 +1,390 @@
+/*
+ * e-alert-bar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-alert-bar.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#define E_ALERT_BAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate))
+
+#define E_ALERT_BAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate))
+
+/* GTK_ICON_SIZE_DIALOG is a tad too big. */
+#define ICON_SIZE GTK_ICON_SIZE_DND
+
+/* Dismiss warnings automatically after 5 minutes. */
+#define WARNING_TIMEOUT_SECONDS (5 * 60)
+
+struct _EAlertBarPrivate {
+ GQueue alerts;
+ GtkWidget *image; /* not referenced */
+ GtkWidget *primary_label; /* not referenced */
+ GtkWidget *secondary_label; /* not referenced */
+};
+
+G_DEFINE_TYPE (
+ EAlertBar,
+ e_alert_bar,
+ GTK_TYPE_INFO_BAR)
+
+static void
+alert_bar_response_close (EAlert *alert)
+{
+ e_alert_response (alert, GTK_RESPONSE_CLOSE);
+}
+
+static void
+alert_bar_show_alert (EAlertBar *alert_bar)
+{
+ GtkImage *image;
+ GtkInfoBar *info_bar;
+ GtkWidget *action_area;
+ GtkWidget *widget;
+ EAlert *alert;
+ GList *actions;
+ GList *children;
+ GtkMessageType message_type;
+ const gchar *primary_text;
+ const gchar *secondary_text;
+ const gchar *stock_id;
+ gboolean have_primary_text;
+ gboolean have_secondary_text;
+ gboolean visible;
+ gint response_id;
+ gchar *markup;
+
+ info_bar = GTK_INFO_BAR (alert_bar);
+ action_area = gtk_info_bar_get_action_area (info_bar);
+
+ alert = g_queue_peek_head (&alert_bar->priv->alerts);
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ /* Remove all buttons from the previous alert. */
+ children = gtk_container_get_children (GTK_CONTAINER (action_area));
+ while (children != NULL) {
+ GtkWidget *child = GTK_WIDGET (children->data);
+ gtk_container_remove (GTK_CONTAINER (action_area), child);
+ children = g_list_delete_link (children, children);
+ }
+
+ /* Add alert-specific buttons. */
+ actions = e_alert_peek_actions (alert);
+ while (actions != NULL) {
+ /* These actions are already wired to trigger an
+ * EAlert::response signal when activated, which
+ * will in turn call gtk_info_bar_response(), so
+ * we can add buttons directly to the action
+ * area without knowning their response IDs. */
+
+ widget = gtk_button_new ();
+
+ gtk_activatable_set_related_action (
+ GTK_ACTIVATABLE (widget),
+ GTK_ACTION (actions->data));
+
+ gtk_box_pack_end (
+ GTK_BOX (action_area), widget, FALSE, FALSE, 0);
+
+ actions = g_list_next (actions);
+ }
+
+ /* Add a dismiss button. */
+ widget = gtk_button_new ();
+ gtk_button_set_image (
+ GTK_BUTTON (widget),
+ gtk_image_new_from_stock (
+ GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
+ gtk_button_set_relief (
+ GTK_BUTTON (widget), GTK_RELIEF_NONE);
+ gtk_widget_set_tooltip_text (
+ widget, _("Close this message"));
+ gtk_box_pack_end (
+ GTK_BOX (action_area), widget, FALSE, FALSE, 0);
+ gtk_button_box_set_child_non_homogeneous (
+ GTK_BUTTON_BOX (action_area), widget, TRUE);
+ gtk_widget_show (widget);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (alert_bar_response_close), alert);
+
+ primary_text = e_alert_get_primary_text (alert);
+ secondary_text = e_alert_get_secondary_text (alert);
+
+ if (primary_text == NULL)
+ primary_text = "";
+
+ if (secondary_text == NULL)
+ secondary_text = "";
+
+ have_primary_text = (*primary_text != '\0');
+ have_secondary_text = (*secondary_text != '\0');
+
+ response_id = e_alert_get_default_response (alert);
+ gtk_info_bar_set_default_response (info_bar, response_id);
+
+ message_type = e_alert_get_message_type (alert);
+ gtk_info_bar_set_message_type (info_bar, message_type);
+
+ widget = alert_bar->priv->primary_label;
+ if (have_primary_text && have_secondary_text)
+ markup = g_markup_printf_escaped (
+ "<b>%s</b>", primary_text);
+ else
+ markup = g_markup_escape_text (primary_text, -1);
+ gtk_label_set_markup (GTK_LABEL (widget), markup);
+ gtk_widget_set_visible (widget, have_primary_text);
+ g_free (markup);
+
+ widget = alert_bar->priv->secondary_label;
+ if (have_primary_text && have_secondary_text)
+ markup = g_markup_printf_escaped (
+ "<small>%s</small>", secondary_text);
+ else
+ markup = g_markup_escape_text (secondary_text, -1);
+ gtk_label_set_markup (GTK_LABEL (widget), markup);
+ gtk_widget_set_visible (widget, have_secondary_text);
+ g_free (markup);
+
+ stock_id = e_alert_get_stock_id (alert);
+ image = GTK_IMAGE (alert_bar->priv->image);
+ gtk_image_set_from_stock (image, stock_id, ICON_SIZE);
+
+ /* Avoid showing an image for one-line alerts,
+ * which are usually questions or informational. */
+ visible = have_primary_text && have_secondary_text;
+ gtk_widget_set_visible (alert_bar->priv->image, visible);
+
+ gtk_widget_show (GTK_WIDGET (alert_bar));
+
+ /* Warnings are generally meant for transient errors.
+ * No need to leave them up indefinitely. Close them
+ * automatically if the user hasn't responded after a
+ * reasonable period of time has elapsed. */
+ if (message_type == GTK_MESSAGE_WARNING)
+ e_alert_start_timer (alert, WARNING_TIMEOUT_SECONDS);
+}
+
+static void
+alert_bar_response_cb (EAlert *alert,
+ gint response_id,
+ EAlertBar *alert_bar)
+{
+ GQueue *queue;
+ EAlert *head;
+ gboolean was_head;
+
+ queue = &alert_bar->priv->alerts;
+ head = g_queue_peek_head (queue);
+ was_head = (alert == head);
+
+ g_signal_handlers_disconnect_by_func (
+ alert, alert_bar_response_cb, alert_bar);
+
+ if (g_queue_remove (queue, alert))
+ g_object_unref (alert);
+
+ if (g_queue_is_empty (queue))
+ gtk_widget_hide (GTK_WIDGET (alert_bar));
+ else if (was_head) {
+ GtkInfoBar *info_bar = GTK_INFO_BAR (alert_bar);
+ gtk_info_bar_response (info_bar, response_id);
+ alert_bar_show_alert (alert_bar);
+ }
+}
+
+static void
+alert_bar_dispose (GObject *object)
+{
+ EAlertBarPrivate *priv;
+
+ priv = E_ALERT_BAR_GET_PRIVATE (object);
+
+ while (!g_queue_is_empty (&priv->alerts)) {
+ EAlert *alert = g_queue_pop_head (&priv->alerts);
+ g_signal_handlers_disconnect_by_func (
+ alert, alert_bar_response_cb, object);
+ g_object_unref (alert);
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_alert_bar_parent_class)->dispose (object);
+}
+
+static void
+alert_bar_constructed (GObject *object)
+{
+ EAlertBarPrivate *priv;
+ GtkInfoBar *info_bar;
+ GtkWidget *action_area;
+ GtkWidget *content_area;
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ priv = E_ALERT_BAR_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_alert_bar_parent_class)->constructed (object);
+
+ g_queue_init (&priv->alerts);
+
+ info_bar = GTK_INFO_BAR (object);
+ action_area = gtk_info_bar_get_action_area (info_bar);
+ content_area = gtk_info_bar_get_content_area (info_bar);
+
+ gtk_orientable_set_orientation (
+ GTK_ORIENTABLE (action_area), GTK_ORIENTATION_HORIZONTAL);
+ gtk_widget_set_valign (action_area, GTK_ALIGN_START);
+
+ container = content_area;
+
+ widget = gtk_image_new ();
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ priv->image = widget;
+ gtk_widget_show (widget);
+
+ widget = gtk_vbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ priv->primary_label = widget;
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ priv->secondary_label = widget;
+ gtk_widget_show (widget);
+
+ container = action_area;
+}
+
+static GtkSizeRequestMode
+alert_bar_get_request_mode (GtkWidget *widget)
+{
+ /* GtkBox does width-for-height by default. But we
+ * want the alert bar to be as short as possible. */
+ return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+}
+
+static void
+e_alert_bar_class_init (EAlertBarClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EAlertBarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = alert_bar_dispose;
+ object_class->constructed = alert_bar_constructed;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->get_request_mode = alert_bar_get_request_mode;
+}
+
+static void
+e_alert_bar_init (EAlertBar *alert_bar)
+{
+ alert_bar->priv = E_ALERT_BAR_GET_PRIVATE (alert_bar);
+}
+
+GtkWidget *
+e_alert_bar_new (void)
+{
+ return g_object_new (E_TYPE_ALERT_BAR, NULL);
+}
+
+void
+e_alert_bar_clear (EAlertBar *alert_bar)
+{
+ GQueue *queue;
+ EAlert *alert;
+
+ g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
+
+ queue = &alert_bar->priv->alerts;
+
+ while ((alert = g_queue_pop_head (queue)) != NULL)
+ alert_bar_response_close (alert);
+}
+
+typedef struct {
+ gboolean found;
+ EAlert *looking_for;
+} DuplicateData;
+
+static void
+alert_bar_find_duplicate_cb (EAlert *alert,
+ DuplicateData *dd)
+{
+ g_return_if_fail (dd->looking_for != NULL);
+
+ dd->found |= (
+ e_alert_get_message_type (alert) ==
+ e_alert_get_message_type (dd->looking_for) &&
+ g_strcmp0 (
+ e_alert_get_primary_text (alert),
+ e_alert_get_primary_text (dd->looking_for)) == 0 &&
+ g_strcmp0 (
+ e_alert_get_secondary_text (alert),
+ e_alert_get_secondary_text (dd->looking_for)) == 0);
+}
+
+void
+e_alert_bar_add_alert (EAlertBar *alert_bar,
+ EAlert *alert)
+{
+ DuplicateData dd;
+
+ g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ dd.found = FALSE;
+ dd.looking_for = alert;
+
+ g_queue_foreach (
+ &alert_bar->priv->alerts,
+ (GFunc) alert_bar_find_duplicate_cb, &dd);
+
+ if (dd.found)
+ return;
+
+ g_signal_connect (
+ alert, "response",
+ G_CALLBACK (alert_bar_response_cb), alert_bar);
+
+ g_queue_push_head (&alert_bar->priv->alerts, g_object_ref (alert));
+
+ alert_bar_show_alert (alert_bar);
+}
diff --git a/e-util/e-alert-bar.h b/e-util/e-alert-bar.h
new file mode 100644
index 0000000000..ae5b315b40
--- /dev/null
+++ b/e-util/e-alert-bar.h
@@ -0,0 +1,72 @@
+/*
+ * e-alert-bar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ALERT_BAR_H
+#define E_ALERT_BAR_H
+
+#include <gtk/gtk.h>
+
+#include <e-util/e-alert.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ALERT_BAR \
+ (e_alert_bar_get_type ())
+#define E_ALERT_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBar))
+#define E_ALERT_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ALERT_BAR, EAlertBarClass))
+#define E_IS_ALERT_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ALERT_BAR))
+#define E_IS_ALERT_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ALERT_BAR))
+#define E_ALERT_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAlertBar EAlertBar;
+typedef struct _EAlertBarClass EAlertBarClass;
+typedef struct _EAlertBarPrivate EAlertBarPrivate;
+
+struct _EAlertBar {
+ GtkInfoBar parent;
+ EAlertBarPrivate *priv;
+};
+
+struct _EAlertBarClass {
+ GtkInfoBarClass parent_class;
+};
+
+GType e_alert_bar_get_type (void);
+GtkWidget * e_alert_bar_new (void);
+void e_alert_bar_clear (EAlertBar *alert_bar);
+void e_alert_bar_add_alert (EAlertBar *alert_bar,
+ EAlert *alert);
+
+G_END_DECLS
+
+#endif /* E_ALERT_BAR_H */
diff --git a/e-util/e-alert-dialog.c b/e-util/e-alert-dialog.c
new file mode 100644
index 0000000000..75650902ae
--- /dev/null
+++ b/e-util/e-alert-dialog.c
@@ -0,0 +1,403 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "e-alert-dialog.h"
+
+#define E_ALERT_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ALERT_DIALOG, EAlertDialogPrivate))
+
+struct _EAlertDialogPrivate {
+ GtkWidget *content_area; /* not referenced */
+ EAlert *alert;
+};
+
+enum {
+ PROP_0,
+ PROP_ALERT
+};
+
+G_DEFINE_TYPE (
+ EAlertDialog,
+ e_alert_dialog,
+ GTK_TYPE_DIALOG)
+
+static void
+alert_dialog_set_alert (EAlertDialog *dialog,
+ EAlert *alert)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+ g_return_if_fail (dialog->priv->alert == NULL);
+
+ dialog->priv->alert = g_object_ref (alert);
+}
+
+static void
+alert_dialog_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ALERT:
+ alert_dialog_set_alert (
+ E_ALERT_DIALOG (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+alert_dialog_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ALERT:
+ g_value_set_object (
+ value, e_alert_dialog_get_alert (
+ E_ALERT_DIALOG (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+alert_dialog_dispose (GObject *object)
+{
+ EAlertDialogPrivate *priv;
+
+ priv = E_ALERT_DIALOG_GET_PRIVATE (object);
+
+ if (priv->alert) {
+ g_signal_handlers_disconnect_matched (
+ priv->alert, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_unref (priv->alert);
+ priv->alert = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_alert_dialog_parent_class)->dispose (object);
+}
+
+static void
+alert_dialog_constructed (GObject *object)
+{
+ EAlert *alert;
+ EAlertDialog *dialog;
+ GtkWidget *action_area;
+ GtkWidget *content_area;
+ GtkWidget *container;
+ GtkWidget *widget;
+ PangoAttribute *attr;
+ PangoAttrList *list;
+ GList *actions;
+ const gchar *primary, *secondary;
+ gint default_response;
+ gint min_width = -1, prefer_width = -1;
+ gint height;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_alert_dialog_parent_class)->constructed (object);
+
+ dialog = E_ALERT_DIALOG (object);
+ alert = e_alert_dialog_get_alert (dialog);
+
+ default_response = e_alert_get_default_response (alert);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), " ");
+
+ action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ gtk_widget_ensure_style (GTK_WIDGET (dialog));
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
+
+ /* Forward EAlert::response signals to GtkDialog::response. */
+ g_signal_connect_swapped (
+ alert, "response",
+ G_CALLBACK (gtk_dialog_response), dialog);
+
+ /* Add buttons from actions. */
+ actions = e_alert_peek_actions (alert);
+ if (!actions) {
+ GtkAction *action;
+
+ /* Make sure there is at least one action, thus the dialog can be closed. */
+ action = gtk_action_new (
+ "alert-response-0", _("_Dismiss"), NULL, NULL);
+ e_alert_add_action (alert, action, GTK_RESPONSE_CLOSE);
+ g_object_unref (action);
+
+ actions = e_alert_peek_actions (alert);
+ }
+
+ while (actions != NULL) {
+ GtkWidget *button;
+ gpointer data;
+
+ /* These actions are already wired to trigger an
+ * EAlert::response signal when activated, which
+ * will in turn call to gtk_dialog_response(),
+ * so we can add buttons directly to the action
+ * area without knowing their response IDs.
+ * (XXX Well, kind of. See below.) */
+
+ button = gtk_button_new ();
+
+ gtk_widget_set_can_default (button, TRUE);
+
+ gtk_activatable_set_related_action (
+ GTK_ACTIVATABLE (button),
+ GTK_ACTION (actions->data));
+
+ gtk_box_pack_end (
+ GTK_BOX (action_area),
+ button, FALSE, FALSE, 0);
+
+ /* This is set in e_alert_add_action(). */
+ data = g_object_get_data (
+ actions->data, "e-alert-response-id");
+
+ /* Normally GtkDialog sets the initial focus widget to
+ * the button corresponding to the default response, but
+ * because the buttons are not directly tied to response
+ * IDs, we have set both the default widget and the
+ * initial focus widget ourselves. */
+ if (GPOINTER_TO_INT (data) == default_response) {
+ gtk_widget_grab_default (button);
+ gtk_widget_grab_focus (button);
+ }
+
+ actions = g_list_next (actions);
+ }
+
+ widget = gtk_hbox_new (FALSE, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
+ gtk_box_pack_start (GTK_BOX (content_area), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_alert_create_image (alert, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_vbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ dialog->priv->content_area = widget;
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ primary = e_alert_get_primary_text (alert);
+ secondary = e_alert_get_secondary_text (alert);
+
+ list = pango_attr_list_new ();
+ attr = pango_attr_scale_new (PANGO_SCALE_LARGE);
+ pango_attr_list_insert (list, attr);
+ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+ pango_attr_list_insert (list, attr);
+
+ widget = gtk_label_new (primary);
+ gtk_label_set_attributes (GTK_LABEL (widget), list);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_set_can_focus (widget, FALSE);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (secondary);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_set_can_focus (widget, FALSE);
+ gtk_widget_show (widget);
+
+ widget = GTK_WIDGET (dialog);
+
+ height = gtk_widget_get_allocated_height (widget);
+ gtk_widget_get_preferred_width_for_height (
+ widget, height, &min_width, &prefer_width);
+ if (min_width < prefer_width)
+ gtk_window_set_default_size (
+ GTK_WINDOW (dialog), MIN (
+ (min_width + prefer_width) / 2,
+ min_width * 5 / 4), -1);
+
+ pango_attr_list_unref (list);
+}
+
+static void
+e_alert_dialog_class_init (EAlertDialogClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EAlertDialogPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = alert_dialog_set_property;
+ object_class->get_property = alert_dialog_get_property;
+ object_class->dispose = alert_dialog_dispose;
+ object_class->constructed = alert_dialog_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ALERT,
+ g_param_spec_object (
+ "alert",
+ "Alert",
+ "Alert to be displayed",
+ E_TYPE_ALERT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_alert_dialog_init (EAlertDialog *dialog)
+{
+ dialog->priv = E_ALERT_DIALOG_GET_PRIVATE (dialog);
+}
+
+GtkWidget *
+e_alert_dialog_new (GtkWindow *parent,
+ EAlert *alert)
+{
+ g_return_val_if_fail (E_IS_ALERT (alert), NULL);
+
+ return g_object_new (
+ E_TYPE_ALERT_DIALOG,
+ "alert", alert, "transient-for", parent, NULL);
+}
+
+GtkWidget *
+e_alert_dialog_new_for_args (GtkWindow *parent,
+ const gchar *tag,
+ ...)
+{
+ GtkWidget *dialog;
+ EAlert *alert;
+ va_list ap;
+
+ g_return_val_if_fail (tag != NULL, NULL);
+
+ va_start (ap, tag);
+ alert = e_alert_new_valist (tag, ap);
+ va_end (ap);
+
+ dialog = e_alert_dialog_new (parent, alert);
+
+ g_object_unref (alert);
+
+ return dialog;
+}
+
+gint
+e_alert_run_dialog (GtkWindow *parent,
+ EAlert *alert)
+{
+ GtkWidget *dialog;
+ gint response;
+
+ g_return_val_if_fail (E_IS_ALERT (alert), 0);
+
+ dialog = e_alert_dialog_new (parent, alert);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ return response;
+}
+
+gint
+e_alert_run_dialog_for_args (GtkWindow *parent,
+ const gchar *tag,
+ ...)
+{
+ EAlert *alert;
+ gint response;
+ va_list ap;
+
+ g_return_val_if_fail (tag != NULL, 0);
+
+ va_start (ap, tag);
+ alert = e_alert_new_valist (tag, ap);
+ va_end (ap);
+
+ response = e_alert_run_dialog (parent, alert);
+
+ g_object_unref (alert);
+
+ return response;
+}
+
+/**
+ * e_alert_dialog_get_alert:
+ * @dialog: an #EAlertDialog
+ *
+ * Returns the #EAlert associated with @dialog.
+ *
+ * Returns: the #EAlert associated with @dialog
+ **/
+EAlert *
+e_alert_dialog_get_alert (EAlertDialog *dialog)
+{
+ g_return_val_if_fail (E_IS_ALERT_DIALOG (dialog), NULL);
+
+ return dialog->priv->alert;
+}
+
+/**
+ * e_alert_dialog_get_content_area:
+ * @dialog: an #EAlertDialog
+ *
+ * Returns the vertical box containing the primary and secondary labels.
+ * Use this to pack additional widgets into the dialog with the proper
+ * horizontal alignment (maintaining the left margin below the image).
+ *
+ * Returns: the content area #GtkBox
+ **/
+GtkWidget *
+e_alert_dialog_get_content_area (EAlertDialog *dialog)
+{
+ g_return_val_if_fail (E_IS_ALERT_DIALOG (dialog), NULL);
+
+ return dialog->priv->content_area;
+}
diff --git a/e-util/e-alert-dialog.h b/e-util/e-alert-dialog.h
new file mode 100644
index 0000000000..3d2662a398
--- /dev/null
+++ b/e-util/e-alert-dialog.h
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ */
+
+#ifndef E_ALERT_DIALOG_H
+#define E_ALERT_DIALOG_H
+
+#include <gtk/gtk.h>
+
+#include <e-util/e-alert.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ALERT_DIALOG \
+ (e_alert_dialog_get_type ())
+#define E_ALERT_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ALERT_DIALOG, EAlertDialog))
+#define E_ALERT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ALERT_DIALOG, EAlertDialogClass))
+#define E_IS_ALERT_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ALERT_DIALOG))
+#define E_IS_ALERT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ALERT_DIALOG))
+#define E_ALERT_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ALERT_DIALOG, EAlertDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAlertDialog EAlertDialog;
+typedef struct _EAlertDialogClass EAlertDialogClass;
+typedef struct _EAlertDialogPrivate EAlertDialogPrivate;
+
+struct _EAlertDialog {
+ GtkDialog parent;
+ EAlertDialogPrivate *priv;
+};
+
+struct _EAlertDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType e_alert_dialog_get_type (void);
+GtkWidget * e_alert_dialog_new (GtkWindow *parent,
+ EAlert *alert);
+GtkWidget * e_alert_dialog_new_for_args (GtkWindow *parent,
+ const gchar *tag,
+ ...) G_GNUC_NULL_TERMINATED;
+gint e_alert_run_dialog (GtkWindow *parent,
+ EAlert *alert);
+gint e_alert_run_dialog_for_args (GtkWindow *parent,
+ const gchar *tag,
+ ...) G_GNUC_NULL_TERMINATED;
+EAlert * e_alert_dialog_get_alert (EAlertDialog *dialog);
+GtkWidget * e_alert_dialog_get_content_area (EAlertDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_ALERT_DIALOG_H */
diff --git a/e-util/e-alert-sink.c b/e-util/e-alert-sink.c
new file mode 100644
index 0000000000..3077261a90
--- /dev/null
+++ b/e-util/e-alert-sink.c
@@ -0,0 +1,93 @@
+/*
+ * e-alert-sink.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-alert-sink
+ * @short_description: an interface to handle alerts
+ * @include: e-util/e-util.h
+ *
+ * A widget that implements #EAlertSink means it can handle #EAlerts,
+ * usually by displaying them to the user.
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-alert-sink.h"
+
+#include "e-alert-dialog.h"
+
+G_DEFINE_INTERFACE (
+ EAlertSink,
+ e_alert_sink,
+ GTK_TYPE_WIDGET)
+
+static void
+alert_sink_fallback (GtkWidget *widget,
+ EAlert *alert)
+{
+ GtkWidget *dialog;
+ gpointer parent;
+
+ parent = gtk_widget_get_toplevel (widget);
+ parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+ dialog = e_alert_dialog_new (parent, alert);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+static void
+alert_sink_submit_alert (EAlertSink *alert_sink,
+ EAlert *alert)
+{
+ /* This is just a lame fallback handler. Implementors
+ * are strongly encouraged to override this method. */
+ alert_sink_fallback (GTK_WIDGET (alert_sink), alert);
+}
+
+static void
+e_alert_sink_default_init (EAlertSinkInterface *interface)
+{
+ interface->submit_alert = alert_sink_submit_alert;
+}
+
+/**
+ * e_alert_sink_submit_alert:
+ * @alert_sink: an #EAlertSink
+ * @alert: an #EAlert
+ *
+ * This function is a place to pass #EAlert objects. Beyond that it has no
+ * well-defined behavior. It's up to the widget implementing the #EAlertSink
+ * interface to decide what to do with them.
+ **/
+void
+e_alert_sink_submit_alert (EAlertSink *alert_sink,
+ EAlert *alert)
+{
+ EAlertSinkInterface *interface;
+
+ g_return_if_fail (E_IS_ALERT_SINK (alert_sink));
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ interface = E_ALERT_SINK_GET_INTERFACE (alert_sink);
+ g_return_if_fail (interface->submit_alert != NULL);
+
+ interface->submit_alert (alert_sink, alert);
+}
diff --git a/e-util/e-alert-sink.h b/e-util/e-alert-sink.h
new file mode 100644
index 0000000000..c8fd5127e7
--- /dev/null
+++ b/e-util/e-alert-sink.h
@@ -0,0 +1,63 @@
+/*
+ * e-alert-sink.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_ALERT_SINK_H
+#define E_ALERT_SINK_H
+
+#include <gtk/gtk.h>
+
+#include <e-util/e-alert.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ALERT_SINK \
+ (e_alert_sink_get_type ())
+#define E_ALERT_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ALERT_SINK, EAlertSink))
+#define E_ALERT_SINK_INTERFACE(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ALERT_SINK, EAlertSinkInterface))
+#define E_IS_ALERT_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ALERT_SINK))
+#define E_IS_ALERT_SINK_INTERFACE(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ALERT_SINK))
+#define E_ALERT_SINK_GET_INTERFACE(obj) \
+ (G_TYPE_INSTANCE_GET_INTERFACE \
+ ((obj), E_TYPE_ALERT_SINK, EAlertSinkInterface))
+
+G_BEGIN_DECLS
+
+typedef struct _EAlertSink EAlertSink;
+typedef struct _EAlertSinkInterface EAlertSinkInterface;
+
+struct _EAlertSinkInterface {
+ GTypeInterface parent_interface;
+
+ void (*submit_alert) (EAlertSink *alert_sink,
+ EAlert *alert);
+};
+
+GType e_alert_sink_get_type (void);
+void e_alert_sink_submit_alert (EAlertSink *alert_sink,
+ EAlert *alert);
+
+G_END_DECLS
+
+#endif /* E_ALERT_SINK_H */
diff --git a/e-util/e-alert.c b/e-util/e-alert.c
new file mode 100644
index 0000000000..5a08e07122
--- /dev/null
+++ b/e-util/e-alert.c
@@ -0,0 +1,1003 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-alert.h"
+#include "e-alert-sink.h"
+
+#define d(x)
+
+#define E_ALERT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ALERT, EAlertPrivate))
+
+typedef struct _EAlertButton EAlertButton;
+
+struct _e_alert {
+ const gchar *id;
+ GtkMessageType message_type;
+ gint default_response;
+ const gchar *primary_text;
+ const gchar *secondary_text;
+ EAlertButton *buttons;
+};
+
+struct _e_alert_table {
+ const gchar *domain;
+ const gchar *translation_domain;
+ GHashTable *alerts;
+};
+
+struct _EAlertButton {
+ EAlertButton *next;
+ const gchar *stock_id;
+ const gchar *label;
+ gint response_id;
+};
+
+static GHashTable *alert_table;
+
+/* ********************************************************************** */
+
+static EAlertButton default_ok_button = {
+ NULL, GTK_STOCK_OK, NULL, GTK_RESPONSE_OK
+};
+
+static struct _e_alert default_alerts[] = {
+ { "error", GTK_MESSAGE_ERROR, GTK_RESPONSE_OK,
+ "{0}", "{1}", &default_ok_button },
+ { "warning", GTK_MESSAGE_WARNING, GTK_RESPONSE_OK,
+ "{0}", "{1}", &default_ok_button }
+};
+
+/* ********************************************************************** */
+
+struct _EAlertPrivate {
+ gchar *tag;
+ GPtrArray *args;
+ gchar *primary_text;
+ gchar *secondary_text;
+ struct _e_alert *definition;
+ GtkMessageType message_type;
+ gint default_response;
+ guint timeout_id;
+
+ /* It may occur to one that we could use a GtkActionGroup here,
+ * but we need to preserve the button order and GtkActionGroup
+ * uses a hash table, which does not preserve order. */
+ GQueue actions;
+};
+
+enum {
+ PROP_0,
+ PROP_ARGS,
+ PROP_TAG,
+ PROP_MESSAGE_TYPE,
+ PROP_PRIMARY_TEXT,
+ PROP_SECONDARY_TEXT
+};
+
+enum {
+ RESPONSE,
+ LAST_SIGNAL
+};
+
+static gulong signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (
+ EAlert,
+ e_alert,
+ G_TYPE_OBJECT)
+
+static gint
+map_response (const gchar *name)
+{
+ GEnumClass *class;
+ GEnumValue *value;
+
+ class = g_type_class_ref (GTK_TYPE_RESPONSE_TYPE);
+ value = g_enum_get_value_by_name (class, name);
+ g_type_class_unref (class);
+
+ return (value != NULL) ? value->value : 0;
+}
+
+static GtkMessageType
+map_type (const gchar *nick)
+{
+ GEnumClass *class;
+ GEnumValue *value;
+
+ class = g_type_class_ref (GTK_TYPE_MESSAGE_TYPE);
+ value = g_enum_get_value_by_nick (class, nick);
+ g_type_class_unref (class);
+
+ return (value != NULL) ? value->value : GTK_MESSAGE_ERROR;
+}
+
+/*
+ * XML format:
+ *
+ * <error id="error-id" type="info|warning|question|error"?
+ * response="default_response"? >
+ * <primary> Primary error text.</primary>?
+ * <secondary> Secondary error text.</secondary>?
+ * <button stock="stock-button-id"? label="button label"?
+ * response="response_id"? /> *
+ * </error>
+ */
+
+static void
+e_alert_load (const gchar *path)
+{
+ xmlDocPtr doc = NULL;
+ xmlNodePtr root, error, scan;
+ struct _e_alert *e;
+ EAlertButton *lastbutton;
+ struct _e_alert_table *table;
+ gchar *tmp;
+
+ d (printf ("loading error file %s\n", path));
+
+ doc = e_xml_parse_file (path);
+ if (doc == NULL) {
+ g_warning ("Error file '%s' not found", path);
+ return;
+ }
+
+ root = xmlDocGetRootElement (doc);
+ if (root == NULL
+ || strcmp ((gchar *) root->name, "error-list") != 0
+ || (tmp = (gchar *) xmlGetProp (root, (const guchar *)"domain")) == NULL) {
+ g_warning ("Error file '%s' invalid format", path);
+ xmlFreeDoc (doc);
+ return;
+ }
+
+ table = g_hash_table_lookup (alert_table, tmp);
+ if (table == NULL) {
+ gchar *tmp2;
+
+ table = g_malloc0 (sizeof (*table));
+ table->domain = g_strdup (tmp);
+ table->alerts = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (alert_table, (gpointer) table->domain, table);
+
+ tmp2 = (gchar *) xmlGetProp (
+ root, (const guchar *) "translation-domain");
+ if (tmp2) {
+ table->translation_domain = g_strdup (tmp2);
+ xmlFree (tmp2);
+
+ tmp2 = (gchar *) xmlGetProp (
+ root, (const guchar *) "translation-localedir");
+ if (tmp2) {
+ bindtextdomain (table->translation_domain, tmp2);
+ xmlFree (tmp2);
+ }
+ }
+ } else
+ g_warning (
+ "Error file '%s', domain '%s' "
+ "already used, merging", path, tmp);
+ xmlFree (tmp);
+
+ for (error = root->children; error; error = error->next) {
+ if (!strcmp ((gchar *) error->name, "error")) {
+ tmp = (gchar *) xmlGetProp (error, (const guchar *)"id");
+ if (tmp == NULL)
+ continue;
+
+ e = g_malloc0 (sizeof (*e));
+ e->id = g_strdup (tmp);
+
+ xmlFree (tmp);
+ lastbutton = (EAlertButton *) &e->buttons;
+
+ tmp = (gchar *) xmlGetProp (error, (const guchar *)"type");
+ e->message_type = map_type (tmp);
+ if (tmp)
+ xmlFree (tmp);
+
+ tmp = (gchar *) xmlGetProp (error, (const guchar *)"default");
+ if (tmp) {
+ e->default_response = map_response (tmp);
+ xmlFree (tmp);
+ }
+
+ for (scan = error->children; scan; scan = scan->next) {
+ if (!strcmp ((gchar *) scan->name, "primary")) {
+ if ((tmp = (gchar *) xmlNodeGetContent (scan))) {
+ e->primary_text = g_strdup (
+ dgettext (table->
+ translation_domain, tmp));
+ xmlFree (tmp);
+ }
+ } else if (!strcmp ((gchar *) scan->name, "secondary")) {
+ if ((tmp = (gchar *) xmlNodeGetContent (scan))) {
+ e->secondary_text = g_strdup (
+ dgettext (table->
+ translation_domain, tmp));
+ xmlFree (tmp);
+ }
+ } else if (!strcmp ((gchar *) scan->name, "button")) {
+ EAlertButton *button;
+ gchar *label = NULL;
+ gchar *stock_id = NULL;
+
+ button = g_new0 (EAlertButton, 1);
+ tmp = (gchar *) xmlGetProp (scan, (const guchar *)"stock");
+ if (tmp) {
+ stock_id = g_strdup (tmp);
+ button->stock_id = stock_id;
+ xmlFree (tmp);
+ }
+ tmp = (gchar *) xmlGetProp (
+ scan, (xmlChar *) "label");
+ if (tmp) {
+ label = g_strdup (
+ dgettext (table->
+ translation_domain,
+ tmp));
+ button->label = label;
+ xmlFree (tmp);
+ }
+ tmp = (gchar *) xmlGetProp (
+ scan, (xmlChar *) "response");
+ if (tmp) {
+ button->response_id =
+ map_response (tmp);
+ xmlFree (tmp);
+ }
+
+ if (stock_id == NULL && label == NULL) {
+ g_warning (
+ "Error file '%s': "
+ "missing button "
+ "details in error "
+ "'%s'", path, e->id);
+ g_free (stock_id);
+ g_free (label);
+ g_free (button);
+ } else {
+ lastbutton->next = button;
+ lastbutton = button;
+ }
+ }
+ }
+
+ g_hash_table_insert (table->alerts, (gpointer) e->id, e);
+ }
+ }
+
+ xmlFreeDoc (doc);
+}
+
+static void
+e_alert_load_tables (void)
+{
+ GDir *dir;
+ const gchar *d;
+ gchar *base;
+ struct _e_alert_table *table;
+ gint i;
+
+ if (alert_table != NULL)
+ return;
+
+ alert_table = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* setup system alert types */
+ table = g_malloc0 (sizeof (*table));
+ table->domain = "builtin";
+ table->alerts = g_hash_table_new (g_str_hash, g_str_equal);
+ for (i = 0; i < G_N_ELEMENTS (default_alerts); i++)
+ g_hash_table_insert (
+ table->alerts, (gpointer)
+ default_alerts[i].id, &default_alerts[i]);
+ g_hash_table_insert (alert_table, (gpointer) table->domain, table);
+
+ /* look for installed alert tables */
+ base = g_build_filename (EVOLUTION_PRIVDATADIR, "errors", NULL);
+ dir = g_dir_open (base, 0, NULL);
+ if (dir == NULL) {
+ g_free (base);
+ return;
+ }
+
+ while ((d = g_dir_read_name (dir))) {
+ gchar *path;
+
+ if (d[0] == '.')
+ continue;
+
+ path = g_build_filename (base, d, NULL);
+ e_alert_load (path);
+ g_free (path);
+ }
+
+ g_dir_close (dir);
+ g_free (base);
+}
+
+static void
+alert_action_activate (EAlert *alert,
+ GtkAction *action)
+{
+ GObject *object;
+ gpointer data;
+
+ object = G_OBJECT (action);
+ data = g_object_get_data (object, "e-alert-response-id");
+ e_alert_response (alert, GPOINTER_TO_INT (data));
+}
+
+static gchar *
+alert_format_string (const gchar *format,
+ GPtrArray *args)
+{
+ GString *string;
+ const gchar *end, *newstart;
+ gint id;
+
+ string = g_string_sized_new (strlen (format));
+
+ while (format
+ && (newstart = strchr (format, '{'))
+ && (end = strchr (newstart + 1, '}'))) {
+ g_string_append_len (string, format, newstart - format);
+ id = atoi (newstart + 1);
+ if (id < args->len) {
+ g_string_append (string, args->pdata[id]);
+ } else
+ g_warning (
+ "Error references argument %d "
+ "not supplied by caller", id);
+ format = end + 1;
+ }
+
+ g_string_append (string, format);
+
+ return g_string_free (string, FALSE);
+}
+
+static void
+alert_set_tag (EAlert *alert,
+ const gchar *tag)
+{
+ struct _e_alert *definition;
+ struct _e_alert_table *table;
+ gchar *domain, *id;
+
+ alert->priv->tag = g_strdup (tag);
+
+ g_return_if_fail (alert_table);
+
+ domain = g_alloca (strlen (tag) + 1);
+ strcpy (domain, tag);
+ id = strchr (domain, ':');
+ if (id)
+ *id++ = 0;
+ else {
+ g_warning ("Alert tag '%s' is missing a domain", tag);
+ return;
+ }
+
+ table = g_hash_table_lookup (alert_table, domain);
+ g_return_if_fail (table);
+
+ definition = g_hash_table_lookup (table->alerts, id);
+ g_warn_if_fail (definition);
+
+ alert->priv->definition = definition;
+}
+
+static gboolean
+alert_timeout_cb (EAlert *alert)
+{
+ e_alert_response (alert, alert->priv->default_response);
+
+ return FALSE;
+}
+
+static void
+alert_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EAlert *alert = (EAlert *) object;
+
+ switch (property_id) {
+ case PROP_TAG:
+ alert_set_tag (
+ E_ALERT (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_ARGS:
+ alert->priv->args = g_value_dup_boxed (value);
+ return;
+
+ case PROP_MESSAGE_TYPE:
+ e_alert_set_message_type (
+ E_ALERT (object),
+ g_value_get_enum (value));
+ return;
+
+ case PROP_PRIMARY_TEXT:
+ e_alert_set_primary_text (
+ E_ALERT (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_SECONDARY_TEXT:
+ e_alert_set_secondary_text (
+ E_ALERT (object),
+ g_value_get_string (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+alert_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EAlert *alert = (EAlert *) object;
+
+ switch (property_id) {
+ case PROP_TAG:
+ g_value_set_string (value, alert->priv->tag);
+ return;
+
+ case PROP_ARGS:
+ g_value_set_boxed (value, alert->priv->args);
+ return;
+
+ case PROP_MESSAGE_TYPE:
+ g_value_set_enum (
+ value, e_alert_get_message_type (
+ E_ALERT (object)));
+ return;
+
+ case PROP_PRIMARY_TEXT:
+ g_value_set_string (
+ value, e_alert_get_primary_text (
+ E_ALERT (object)));
+ return;
+
+ case PROP_SECONDARY_TEXT:
+ g_value_set_string (
+ value, e_alert_get_secondary_text (
+ E_ALERT (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+alert_dispose (GObject *object)
+{
+ EAlert *alert = E_ALERT (object);
+
+ if (alert->priv->timeout_id > 0) {
+ g_source_remove (alert->priv->timeout_id);
+ alert->priv->timeout_id = 0;
+ }
+
+ while (!g_queue_is_empty (&alert->priv->actions)) {
+ GtkAction *action;
+
+ action = g_queue_pop_head (&alert->priv->actions);
+ g_signal_handlers_disconnect_by_func (
+ action, G_CALLBACK (alert_action_activate), object);
+ g_object_unref (action);
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_alert_parent_class)->dispose (object);
+}
+
+static void
+alert_finalize (GObject *object)
+{
+ EAlertPrivate *priv;
+
+ priv = E_ALERT_GET_PRIVATE (object);
+
+ g_free (priv->tag);
+ g_free (priv->primary_text);
+ g_free (priv->secondary_text);
+
+ g_ptr_array_free (priv->args, TRUE);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_alert_parent_class)->finalize (object);
+}
+
+static void
+alert_constructed (GObject *object)
+{
+ EAlert *alert;
+ EAlertButton *button;
+ struct _e_alert *definition;
+ gint ii = 0;
+
+ alert = E_ALERT (object);
+ definition = alert->priv->definition;
+ g_return_if_fail (definition != NULL);
+
+ e_alert_set_message_type (alert, definition->message_type);
+ e_alert_set_default_response (alert, definition->default_response);
+
+ /* Build actions out of the button definitions. */
+ button = definition->buttons;
+ while (button != NULL) {
+ GtkAction *action;
+ gchar *action_name;
+
+ action_name = g_strdup_printf ("alert-response-%d", ii++);
+
+ if (button->stock_id != NULL) {
+ action = gtk_action_new (
+ action_name, NULL, NULL, button->stock_id);
+ e_alert_add_action (
+ alert, action, button->response_id);
+ g_object_unref (action);
+
+ } else if (button->label != NULL) {
+ action = gtk_action_new (
+ action_name, button->label, NULL, NULL);
+ e_alert_add_action (
+ alert, action, button->response_id);
+ g_object_unref (action);
+ }
+
+ g_free (action_name);
+
+ button = button->next;
+ }
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_alert_parent_class)->constructed (object);
+}
+
+static void
+e_alert_class_init (EAlertClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ g_type_class_add_private (class, sizeof (EAlertPrivate));
+
+ object_class->set_property = alert_set_property;
+ object_class->get_property = alert_get_property;
+ object_class->dispose = alert_dispose;
+ object_class->finalize = alert_finalize;
+ object_class->constructed = alert_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ARGS,
+ g_param_spec_boxed (
+ "args",
+ "Arguments",
+ "Arguments for formatting the alert",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_TAG,
+ g_param_spec_string (
+ "tag",
+ "alert tag",
+ "A tag describing the alert",
+ "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MESSAGE_TYPE,
+ g_param_spec_enum (
+ "message-type",
+ NULL,
+ NULL,
+ GTK_TYPE_MESSAGE_TYPE,
+ GTK_MESSAGE_ERROR,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PRIMARY_TEXT,
+ g_param_spec_string (
+ "primary-text",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SECONDARY_TEXT,
+ g_param_spec_string (
+ "secondary-text",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ signals[RESPONSE] = g_signal_new (
+ "response",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EAlertClass, response),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1,
+ G_TYPE_INT);
+
+ e_alert_load_tables ();
+}
+
+static void
+e_alert_init (EAlert *alert)
+{
+ alert->priv = E_ALERT_GET_PRIVATE (alert);
+
+ g_queue_init (&alert->priv->actions);
+}
+
+/**
+ * e_alert_new:
+ * @tag: alert identifier
+ * @arg0: The first argument for the alert formatter. The list must
+ * be NULL terminated.
+ *
+ * Creates a new EAlert. The @tag argument is used to determine
+ * which alert to use, it is in the format domain:alert-id. The NULL
+ * terminated list of arguments, starting with @arg0 is used to fill
+ * out the alert definition.
+ *
+ * Returns: a new #EAlert
+ **/
+EAlert *
+e_alert_new (const gchar *tag,
+ ...)
+{
+ EAlert *e;
+ va_list va;
+
+ va_start (va, tag);
+ e = e_alert_new_valist (tag, va);
+ va_end (va);
+
+ return e;
+}
+
+EAlert *
+e_alert_new_valist (const gchar *tag,
+ va_list va)
+{
+ EAlert *alert;
+ GPtrArray *args;
+ gchar *tmp;
+
+ args = g_ptr_array_new_with_free_func (g_free);
+
+ tmp = va_arg (va, gchar *);
+ while (tmp) {
+ g_ptr_array_add (args, g_strdup (tmp));
+ tmp = va_arg (va, gchar *);
+ }
+
+ alert = e_alert_new_array (tag, args);
+
+ g_ptr_array_unref (args);
+
+ return alert;
+}
+
+EAlert *
+e_alert_new_array (const gchar *tag,
+ GPtrArray *args)
+{
+ return g_object_new (E_TYPE_ALERT, "tag", tag, "args", args, NULL);
+}
+
+gint
+e_alert_get_default_response (EAlert *alert)
+{
+ g_return_val_if_fail (E_IS_ALERT (alert), 0);
+
+ return alert->priv->default_response;
+}
+
+void
+e_alert_set_default_response (EAlert *alert,
+ gint response_id)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ alert->priv->default_response = response_id;
+}
+
+GtkMessageType
+e_alert_get_message_type (EAlert *alert)
+{
+ g_return_val_if_fail (E_IS_ALERT (alert), GTK_MESSAGE_OTHER);
+
+ return alert->priv->message_type;
+}
+
+void
+e_alert_set_message_type (EAlert *alert,
+ GtkMessageType message_type)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ if (alert->priv->message_type == message_type)
+ return;
+
+ alert->priv->message_type = message_type;
+
+ g_object_notify (G_OBJECT (alert), "message-type");
+}
+
+const gchar *
+e_alert_get_primary_text (EAlert *alert)
+{
+ g_return_val_if_fail (E_IS_ALERT (alert), NULL);
+
+ if (alert->priv->primary_text != NULL)
+ goto exit;
+
+ if (alert->priv->definition == NULL)
+ goto exit;
+
+ if (alert->priv->definition->primary_text == NULL)
+ goto exit;
+
+ if (alert->priv->args == NULL)
+ goto exit;
+
+ alert->priv->primary_text = alert_format_string (
+ alert->priv->definition->primary_text,
+ alert->priv->args);
+
+exit:
+ return alert->priv->primary_text;
+}
+
+void
+e_alert_set_primary_text (EAlert *alert,
+ const gchar *primary_text)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ if (g_strcmp0 (alert->priv->primary_text, primary_text) == 0)
+ return;
+
+ g_free (alert->priv->primary_text);
+ alert->priv->primary_text = g_strdup (primary_text);
+
+ g_object_notify (G_OBJECT (alert), "primary-text");
+}
+
+const gchar *
+e_alert_get_secondary_text (EAlert *alert)
+{
+ g_return_val_if_fail (E_IS_ALERT (alert), NULL);
+
+ if (alert->priv->secondary_text != NULL)
+ goto exit;
+
+ if (alert->priv->definition == NULL)
+ goto exit;
+
+ if (alert->priv->definition->secondary_text == NULL)
+ goto exit;
+
+ if (alert->priv->args == NULL)
+ goto exit;
+
+ alert->priv->secondary_text = alert_format_string (
+ alert->priv->definition->secondary_text,
+ alert->priv->args);
+
+exit:
+ return alert->priv->secondary_text;
+}
+
+void
+e_alert_set_secondary_text (EAlert *alert,
+ const gchar *secondary_text)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ if (g_strcmp0 (alert->priv->secondary_text, secondary_text) == 0)
+ return;
+
+ g_free (alert->priv->secondary_text);
+ alert->priv->secondary_text = g_strdup (secondary_text);
+
+ g_object_notify (G_OBJECT (alert), "secondary-text");
+}
+
+const gchar *
+e_alert_get_stock_id (EAlert *alert)
+{
+ const gchar *stock_id;
+
+ g_return_val_if_fail (E_IS_ALERT (alert), NULL);
+
+ switch (e_alert_get_message_type (alert)) {
+ case GTK_MESSAGE_INFO:
+ stock_id = GTK_STOCK_DIALOG_INFO;
+ break;
+ case GTK_MESSAGE_WARNING:
+ stock_id = GTK_STOCK_DIALOG_WARNING;
+ break;
+ case GTK_MESSAGE_QUESTION:
+ stock_id = GTK_STOCK_DIALOG_QUESTION;
+ break;
+ case GTK_MESSAGE_ERROR:
+ stock_id = GTK_STOCK_DIALOG_ERROR;
+ break;
+ default:
+ stock_id = GTK_STOCK_MISSING_IMAGE;
+ g_warn_if_reached ();
+ break;
+ }
+
+ return stock_id;
+}
+
+void
+e_alert_add_action (EAlert *alert,
+ GtkAction *action,
+ gint response_id)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+ g_return_if_fail (GTK_ACTION (action));
+
+ g_object_set_data (
+ G_OBJECT (action), "e-alert-response-id",
+ GINT_TO_POINTER (response_id));
+
+ g_signal_connect_swapped (
+ action, "activate",
+ G_CALLBACK (alert_action_activate), alert);
+
+ g_queue_push_tail (&alert->priv->actions, g_object_ref (action));
+}
+
+GList *
+e_alert_peek_actions (EAlert *alert)
+{
+ g_return_val_if_fail (E_IS_ALERT (alert), NULL);
+
+ return g_queue_peek_head_link (&alert->priv->actions);
+}
+
+GtkWidget *
+e_alert_create_image (EAlert *alert,
+ GtkIconSize size)
+{
+ const gchar *stock_id;
+
+ g_return_val_if_fail (E_IS_ALERT (alert), NULL);
+
+ stock_id = e_alert_get_stock_id (alert);
+
+ return gtk_image_new_from_stock (stock_id, size);
+}
+
+void
+e_alert_response (EAlert *alert,
+ gint response_id)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ g_signal_emit (alert, signals[RESPONSE], 0, response_id);
+}
+
+/**
+ * e_alert_start_timer:
+ * @alert: an #EAlert
+ * @seconds: seconds until timeout occurs
+ *
+ * Starts an internal timer for @alert. When the timer expires, @alert
+ * will emit the default response. There is only one timer per #EAlert,
+ * so calling this function repeatedly on the same #EAlert will restart
+ * its timer each time. If @seconds is zero, the timer is cancelled and
+ * no response will be emitted.
+ **/
+void
+e_alert_start_timer (EAlert *alert,
+ guint seconds)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ if (alert->priv->timeout_id > 0) {
+ g_source_remove (alert->priv->timeout_id);
+ alert->priv->timeout_id = 0;
+ }
+
+ if (seconds > 0)
+ alert->priv->timeout_id = g_timeout_add_seconds (
+ seconds, (GSourceFunc) alert_timeout_cb, alert);
+}
+
+void
+e_alert_submit (EAlertSink *alert_sink,
+ const gchar *tag,
+ ...)
+{
+ va_list va;
+
+ va_start (va, tag);
+ e_alert_submit_valist (alert_sink, tag, va);
+ va_end (va);
+}
+
+void
+e_alert_submit_valist (EAlertSink *alert_sink,
+ const gchar *tag,
+ va_list va)
+{
+ EAlert *alert;
+
+ g_return_if_fail (E_IS_ALERT_SINK (alert_sink));
+ g_return_if_fail (tag != NULL);
+
+ alert = e_alert_new_valist (tag, va);
+ e_alert_sink_submit_alert (alert_sink, alert);
+ g_object_unref (alert);
+}
diff --git a/e-util/e-alert.h b/e-util/e-alert.h
new file mode 100644
index 0000000000..f62e612235
--- /dev/null
+++ b/e-util/e-alert.h
@@ -0,0 +1,119 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+#ifndef E_ALERT_H
+#define E_ALERT_H
+
+#include <stdarg.h>
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ALERT \
+ (e_alert_get_type ())
+#define E_ALERT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ALERT, EAlert))
+#define E_ALERT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ALERT, EAlertClass))
+#define E_IS_ALERT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ALERT))
+#define E_IS_ALERT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ALERT))
+#define E_ALERT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ALERT, EAlertClass))
+
+/* takes filename, returns OK if yes */
+#define E_ALERT_ASK_FILE_EXISTS_OVERWRITE \
+ "system:ask-save-file-exists-overwrite"
+/* takes filename, reason */
+#define E_ALERT_NO_SAVE_FILE "system:no-save-file"
+/* takes filename, reason */
+#define E_ALERT_NO_LOAD_FILE "system:no-save-file"
+
+G_BEGIN_DECLS
+
+struct _EAlertSink;
+
+typedef struct _EAlert EAlert;
+typedef struct _EAlertClass EAlertClass;
+typedef struct _EAlertPrivate EAlertPrivate;
+
+struct _EAlert {
+ GObject parent;
+ EAlertPrivate *priv;
+};
+
+struct _EAlertClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*response) (EAlert *alert,
+ gint response_id);
+};
+
+GType e_alert_get_type (void);
+EAlert * e_alert_new (const gchar *tag,
+ ...) G_GNUC_NULL_TERMINATED;
+EAlert * e_alert_new_valist (const gchar *tag,
+ va_list va);
+EAlert * e_alert_new_array (const gchar *tag,
+ GPtrArray *args);
+gint e_alert_get_default_response (EAlert *alert);
+void e_alert_set_default_response (EAlert *alert,
+ gint response_id);
+GtkMessageType e_alert_get_message_type (EAlert *alert);
+void e_alert_set_message_type (EAlert *alert,
+ GtkMessageType message_type);
+const gchar * e_alert_get_primary_text (EAlert *alert);
+void e_alert_set_primary_text (EAlert *alert,
+ const gchar *primary_text);
+const gchar * e_alert_get_secondary_text (EAlert *alert);
+void e_alert_set_secondary_text (EAlert *alert,
+ const gchar *secondary_text);
+const gchar * e_alert_get_stock_id (EAlert *alert);
+void e_alert_add_action (EAlert *alert,
+ GtkAction *action,
+ gint response_id);
+GList * e_alert_peek_actions (EAlert *alert);
+GtkWidget * e_alert_create_image (EAlert *alert,
+ GtkIconSize size);
+void e_alert_response (EAlert *alert,
+ gint response_id);
+void e_alert_start_timer (EAlert *alert,
+ guint seconds);
+
+void e_alert_submit (struct _EAlertSink *alert_sink,
+ const gchar *tag,
+ ...) G_GNUC_NULL_TERMINATED;
+void e_alert_submit_valist (struct _EAlertSink *alert_sink,
+ const gchar *tag,
+ va_list va);
+
+G_END_DECLS
+
+#endif /* E_ALERT_H */
diff --git a/e-util/e-attachment-bar.c b/e-util/e-attachment-bar.c
new file mode 100644
index 0000000000..3fc4753055
--- /dev/null
+++ b/e-util/e-attachment-bar.c
@@ -0,0 +1,778 @@
+/*
+ * e-attachment-bar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-bar.h"
+
+#include <glib/gi18n.h>
+
+#include "e-attachment-store.h"
+#include "e-attachment-icon-view.h"
+#include "e-attachment-tree-view.h"
+
+#define E_ATTACHMENT_BAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBarPrivate))
+
+#define NUM_VIEWS 2
+
+struct _EAttachmentBarPrivate {
+ GtkTreeModel *model;
+ GtkWidget *vbox;
+ GtkWidget *expander;
+ GtkWidget *combo_box;
+ GtkWidget *icon_view;
+ GtkWidget *tree_view;
+ GtkWidget *icon_frame;
+ GtkWidget *tree_frame;
+ GtkWidget *status_icon;
+ GtkWidget *status_label;
+ GtkWidget *save_all_button;
+ GtkWidget *save_one_button;
+
+ gint active_view;
+ guint expanded : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVE_VIEW,
+ PROP_DRAGGING,
+ PROP_EDITABLE,
+ PROP_EXPANDED,
+ PROP_STORE
+};
+
+/* Forward Declarations */
+static void e_attachment_bar_interface_init
+ (EAttachmentViewInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EAttachmentBar,
+ e_attachment_bar,
+ GTK_TYPE_VBOX,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_ATTACHMENT_VIEW,
+ e_attachment_bar_interface_init))
+
+static void
+attachment_bar_update_status (EAttachmentBar *bar)
+{
+ EAttachmentStore *store;
+ GtkActivatable *activatable;
+ GtkAction *action;
+ GtkLabel *label;
+ gint num_attachments;
+ guint64 total_size;
+ gchar *display_size;
+ gchar *markup;
+
+ store = E_ATTACHMENT_STORE (bar->priv->model);
+ label = GTK_LABEL (bar->priv->status_label);
+
+ num_attachments = e_attachment_store_get_num_attachments (store);
+ total_size = e_attachment_store_get_total_size (store);
+ display_size = g_format_size (total_size);
+
+ if (total_size > 0)
+ markup = g_strdup_printf (
+ "<b>%d</b> %s (%s)", num_attachments, ngettext (
+ "Attachment", "Attachments", num_attachments),
+ display_size);
+ else
+ markup = g_strdup_printf (
+ "<b>%d</b> %s", num_attachments, ngettext (
+ "Attachment", "Attachments", num_attachments));
+ gtk_label_set_markup (label, markup);
+ g_free (markup);
+
+ activatable = GTK_ACTIVATABLE (bar->priv->save_all_button);
+ action = gtk_activatable_get_related_action (activatable);
+ gtk_action_set_visible (action, (num_attachments > 1));
+
+ activatable = GTK_ACTIVATABLE (bar->priv->save_one_button);
+ action = gtk_activatable_get_related_action (activatable);
+ gtk_action_set_visible (action, (num_attachments == 1));
+
+ g_free (display_size);
+}
+
+static void
+attachment_bar_set_store (EAttachmentBar *bar,
+ EAttachmentStore *store)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+ bar->priv->model = g_object_ref (store);
+
+ gtk_icon_view_set_model (
+ GTK_ICON_VIEW (bar->priv->icon_view),
+ bar->priv->model);
+ gtk_tree_view_set_model (
+ GTK_TREE_VIEW (bar->priv->tree_view),
+ bar->priv->model);
+
+ g_signal_connect_object (
+ bar->priv->model, "notify::num-attachments",
+ G_CALLBACK (attachment_bar_update_status), bar,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (
+ bar->priv->model, "notify::total-size",
+ G_CALLBACK (attachment_bar_update_status), bar,
+ G_CONNECT_SWAPPED);
+
+ /* Initialize */
+ attachment_bar_update_status (bar);
+}
+
+static void
+attachment_bar_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVE_VIEW:
+ e_attachment_bar_set_active_view (
+ E_ATTACHMENT_BAR (object),
+ g_value_get_int (value));
+ return;
+
+ case PROP_DRAGGING:
+ e_attachment_view_set_dragging (
+ E_ATTACHMENT_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_EDITABLE:
+ e_attachment_view_set_editable (
+ E_ATTACHMENT_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_EXPANDED:
+ e_attachment_bar_set_expanded (
+ E_ATTACHMENT_BAR (object),
+ g_value_get_boolean (value));
+ return;
+ case PROP_STORE:
+ attachment_bar_set_store (
+ E_ATTACHMENT_BAR (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_bar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVE_VIEW:
+ g_value_set_int (
+ value,
+ e_attachment_bar_get_active_view (
+ E_ATTACHMENT_BAR (object)));
+ return;
+
+ case PROP_DRAGGING:
+ g_value_set_boolean (
+ value,
+ e_attachment_view_get_dragging (
+ E_ATTACHMENT_VIEW (object)));
+ return;
+
+ case PROP_EDITABLE:
+ g_value_set_boolean (
+ value,
+ e_attachment_view_get_editable (
+ E_ATTACHMENT_VIEW (object)));
+ return;
+
+ case PROP_EXPANDED:
+ g_value_set_boolean (
+ value,
+ e_attachment_bar_get_expanded (
+ E_ATTACHMENT_BAR (object)));
+ return;
+ case PROP_STORE:
+ g_value_set_object (
+ value,
+ e_attachment_bar_get_store (
+ E_ATTACHMENT_BAR (object)));
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_bar_dispose (GObject *object)
+{
+ EAttachmentBarPrivate *priv;
+
+ priv = E_ATTACHMENT_BAR_GET_PRIVATE (object);
+
+ if (priv->model != NULL) {
+ g_object_unref (priv->model);
+ priv->model = NULL;
+ }
+
+ if (priv->vbox != NULL) {
+ g_object_unref (priv->vbox);
+ priv->vbox = NULL;
+ }
+
+ if (priv->expander != NULL) {
+ g_object_unref (priv->expander);
+ priv->expander = NULL;
+ }
+
+ if (priv->combo_box != NULL) {
+ g_object_unref (priv->combo_box);
+ priv->combo_box = NULL;
+ }
+
+ if (priv->icon_view != NULL) {
+ g_object_unref (priv->icon_view);
+ priv->icon_view = NULL;
+ }
+
+ if (priv->tree_view != NULL) {
+ g_object_unref (priv->tree_view);
+ priv->tree_view = NULL;
+ }
+
+ if (priv->icon_frame != NULL) {
+ g_object_unref (priv->icon_frame);
+ priv->icon_frame = NULL;
+ }
+
+ if (priv->tree_frame != NULL) {
+ g_object_unref (priv->tree_frame);
+ priv->tree_frame = NULL;
+ }
+
+ if (priv->status_icon != NULL) {
+ g_object_unref (priv->status_icon);
+ priv->status_icon = NULL;
+ }
+
+ if (priv->status_label != NULL) {
+ g_object_unref (priv->status_label);
+ priv->status_label = NULL;
+ }
+
+ if (priv->save_all_button != NULL) {
+ g_object_unref (priv->save_all_button);
+ priv->save_all_button = NULL;
+ }
+
+ if (priv->save_one_button != NULL) {
+ g_object_unref (priv->save_one_button);
+ priv->save_one_button = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_attachment_bar_parent_class)->dispose (object);
+}
+
+static void
+attachment_bar_constructed (GObject *object)
+{
+ EAttachmentBarPrivate *priv;
+ GSettings *settings;
+
+ priv = E_ATTACHMENT_BAR_GET_PRIVATE (object);
+
+ /* Set up property-to-property bindings. */
+
+ g_object_bind_property (
+ object, "active-view",
+ priv->combo_box, "active",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "dragging",
+ priv->icon_view, "dragging",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "dragging",
+ priv->tree_view, "dragging",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "editable",
+ priv->icon_view, "editable",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "editable",
+ priv->tree_view, "editable",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "expanded",
+ priv->expander, "expanded",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "expanded",
+ priv->combo_box, "visible",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "expanded",
+ priv->vbox, "visible",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ /* Set up property-to-GSettings bindings. */
+ settings = g_settings_new ("org.gnome.evolution.shell");
+ g_settings_bind (
+ settings, "attachment-view",
+ object, "active-view",
+ G_SETTINGS_BIND_DEFAULT);
+ g_object_unref (settings);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_attachment_bar_parent_class)->constructed (object);
+}
+
+static EAttachmentViewPrivate *
+attachment_bar_get_private (EAttachmentView *view)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ return e_attachment_view_get_private (view);
+}
+
+static GtkTreePath *
+attachment_bar_get_path_at_pos (EAttachmentView *view,
+ gint x,
+ gint y)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ return e_attachment_view_get_path_at_pos (view, x, y);
+}
+
+static EAttachmentStore *
+attachment_bar_get_store (EAttachmentView *view)
+{
+ return e_attachment_bar_get_store (E_ATTACHMENT_BAR (view));
+}
+
+static GList *
+attachment_bar_get_selected_paths (EAttachmentView *view)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ return e_attachment_view_get_selected_paths (view);
+}
+
+static gboolean
+attachment_bar_path_is_selected (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ return e_attachment_view_path_is_selected (view, path);
+}
+
+static void
+attachment_bar_select_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ e_attachment_view_select_path (view, path);
+}
+
+static void
+attachment_bar_unselect_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ e_attachment_view_unselect_path (view, path);
+}
+
+static void
+attachment_bar_select_all (EAttachmentView *view)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ e_attachment_view_select_all (view);
+}
+
+static void
+attachment_bar_unselect_all (EAttachmentView *view)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ e_attachment_view_unselect_all (view);
+}
+
+static void
+attachment_bar_update_actions (EAttachmentView *view)
+{
+ EAttachmentBar *bar;
+
+ bar = E_ATTACHMENT_BAR (view);
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+ e_attachment_view_update_actions (view);
+}
+
+static void
+e_attachment_bar_class_init (EAttachmentBarClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EAttachmentBarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_bar_set_property;
+ object_class->get_property = attachment_bar_get_property;
+ object_class->dispose = attachment_bar_dispose;
+ object_class->constructed = attachment_bar_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTIVE_VIEW,
+ g_param_spec_int (
+ "active-view",
+ "Active View",
+ NULL,
+ 0,
+ NUM_VIEWS,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EXPANDED,
+ g_param_spec_boolean (
+ "expanded",
+ "Expanded",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_STORE,
+ g_param_spec_object (
+ "store",
+ "Attachment Store",
+ NULL,
+ E_TYPE_ATTACHMENT_STORE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_override_property (
+ object_class, PROP_DRAGGING, "dragging");
+
+ g_object_class_override_property (
+ object_class, PROP_EDITABLE, "editable");
+}
+
+static void
+e_attachment_bar_interface_init (EAttachmentViewInterface *interface)
+{
+ interface->get_private = attachment_bar_get_private;
+ interface->get_store = attachment_bar_get_store;
+ interface->get_path_at_pos = attachment_bar_get_path_at_pos;
+ interface->get_selected_paths = attachment_bar_get_selected_paths;
+ interface->path_is_selected = attachment_bar_path_is_selected;
+ interface->select_path = attachment_bar_select_path;
+ interface->unselect_path = attachment_bar_unselect_path;
+ interface->select_all = attachment_bar_select_all;
+ interface->unselect_all = attachment_bar_unselect_all;
+ interface->update_actions = attachment_bar_update_actions;
+}
+
+static void
+e_attachment_bar_init (EAttachmentBar *bar)
+{
+ EAttachmentView *view;
+ GtkSizeGroup *size_group;
+ GtkWidget *container;
+ GtkWidget *widget;
+ GtkAction *action;
+
+ bar->priv = E_ATTACHMENT_BAR_GET_PRIVATE (bar);
+
+ gtk_box_set_spacing (GTK_BOX (bar), 6);
+
+ /* Keep the expander label and save button the same height. */
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
+ /* Construct the Attachment Views */
+
+ container = GTK_WIDGET (bar);
+
+ widget = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->vbox = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = bar->priv->vbox;
+
+ widget = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ bar->priv->icon_frame = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_attachment_icon_view_new ();
+ gtk_widget_set_can_focus (widget, TRUE);
+ gtk_icon_view_set_model (GTK_ICON_VIEW (widget), bar->priv->model);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ bar->priv->icon_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = bar->priv->vbox;
+
+ widget = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ bar->priv->tree_frame = g_object_ref (widget);
+ gtk_widget_hide (widget);
+
+ container = widget;
+
+ widget = e_attachment_tree_view_new ();
+ gtk_widget_set_can_focus (widget, TRUE);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (widget), bar->priv->model);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ bar->priv->tree_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Construct the Controls */
+
+ container = GTK_WIDGET (bar);
+
+ widget = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_expander_new (NULL);
+ gtk_expander_set_spacing (GTK_EXPANDER (widget), 0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->expander = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* The "Save All" button proxies the "save-all" action from
+ * one of the two attachment views. Doesn't matter which. */
+ widget = gtk_button_new ();
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+ action = e_attachment_view_get_action (view, "save-all");
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->save_all_button = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Same deal with the "Save" button. */
+ widget = gtk_button_new ();
+ view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+ action = e_attachment_view_get_action (view, "save-one");
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->save_one_button = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_combo_box_text_new ();
+ gtk_size_group_add_widget (size_group, widget);
+ gtk_combo_box_text_append_text (
+ GTK_COMBO_BOX_TEXT (widget), _("Icon View"));
+ gtk_combo_box_text_append_text (
+ GTK_COMBO_BOX_TEXT (widget), _("List View"));
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ bar->priv->combo_box = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = bar->priv->expander;
+
+ widget = gtk_hbox_new (FALSE, 6);
+ gtk_size_group_add_widget (size_group, widget);
+ gtk_expander_set_label_widget (GTK_EXPANDER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_image_new_from_icon_name (
+ "mail-attachment", GTK_ICON_SIZE_MENU);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->status_icon = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->status_label = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ g_object_unref (size_group);
+}
+
+GtkWidget *
+e_attachment_bar_new (EAttachmentStore *store)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+
+ return g_object_new (
+ E_TYPE_ATTACHMENT_BAR,
+ "editable", FALSE,
+ "store", store, NULL);
+}
+
+gint
+e_attachment_bar_get_active_view (EAttachmentBar *bar)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), 0);
+
+ return bar->priv->active_view;
+}
+
+void
+e_attachment_bar_set_active_view (EAttachmentBar *bar,
+ gint active_view)
+{
+ EAttachmentView *source;
+ EAttachmentView *target;
+
+ g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
+ g_return_if_fail (active_view >= 0 && active_view < NUM_VIEWS);
+
+ if (active_view == bar->priv->active_view)
+ return;
+
+ bar->priv->active_view = active_view;
+
+ if (active_view == 0) {
+ gtk_widget_show (bar->priv->icon_frame);
+ gtk_widget_hide (bar->priv->tree_frame);
+ } else {
+ gtk_widget_hide (bar->priv->icon_frame);
+ gtk_widget_show (bar->priv->tree_frame);
+ }
+
+ /* Synchronize the item selection of the view we're
+ * switching TO with the view we're switching FROM. */
+ if (active_view == 0) {
+ /* from tree view to icon view */
+ source = E_ATTACHMENT_VIEW (bar->priv->tree_view);
+ target = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+ } else {
+ /* from icon view to tree view */
+ source = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+ target = E_ATTACHMENT_VIEW (bar->priv->tree_view);
+ }
+
+ e_attachment_view_sync_selection (source, target);
+
+ g_object_notify (G_OBJECT (bar), "active-view");
+}
+
+gboolean
+e_attachment_bar_get_expanded (EAttachmentBar *bar)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), FALSE);
+
+ return bar->priv->expanded;
+}
+
+void
+e_attachment_bar_set_expanded (EAttachmentBar *bar,
+ gboolean expanded)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
+
+ if (bar->priv->expanded == expanded)
+ return;
+
+ bar->priv->expanded = expanded;
+
+ g_object_notify (G_OBJECT (bar), "expanded");
+}
+
+EAttachmentStore *
+e_attachment_bar_get_store (EAttachmentBar *bar)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL);
+
+ return E_ATTACHMENT_STORE (bar->priv->model);
+}
diff --git a/e-util/e-attachment-bar.h b/e-util/e-attachment-bar.h
new file mode 100644
index 0000000000..9f35ae2aba
--- /dev/null
+++ b/e-util/e-attachment-bar.h
@@ -0,0 +1,83 @@
+/*
+ * e-attachment-bar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ATTACHMENT_BAR_H
+#define E_ATTACHMENT_BAR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-attachment-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_BAR \
+ (e_attachment_bar_get_type ())
+#define E_ATTACHMENT_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBar))
+#define E_ATTACHMENT_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_BAR, EAttachmentBarClass))
+#define E_IS_ATTACHMENT_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_BAR))
+#define E_IS_ATTACHMENT_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_BAR))
+#define E_ATTACHMENT_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentBar EAttachmentBar;
+typedef struct _EAttachmentBarClass EAttachmentBarClass;
+typedef struct _EAttachmentBarPrivate EAttachmentBarPrivate;
+
+struct _EAttachmentBar {
+ GtkBox parent;
+ EAttachmentBarPrivate *priv;
+};
+
+struct _EAttachmentBarClass {
+ GtkBoxClass parent_class;
+};
+
+GType e_attachment_bar_get_type (void);
+GtkWidget * e_attachment_bar_new (EAttachmentStore *store);
+gint e_attachment_bar_get_active_view
+ (EAttachmentBar *bar);
+void e_attachment_bar_set_active_view
+ (EAttachmentBar *bar,
+ gint active_view);
+gboolean e_attachment_bar_get_expanded
+ (EAttachmentBar *bar);
+void e_attachment_bar_set_expanded
+ (EAttachmentBar *bar,
+ gboolean expanded);
+EAttachmentStore *
+ e_attachment_bar_get_store (EAttachmentBar *bar);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_BAR_H */
diff --git a/e-util/e-attachment-button.c b/e-util/e-attachment-button.c
new file mode 100644
index 0000000000..a2057e3354
--- /dev/null
+++ b/e-util/e-attachment-button.c
@@ -0,0 +1,868 @@
+/*
+ * e-attachment-button.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/* Much of the popup menu logic here was ripped from GtkMenuToolButton. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-button.h"
+
+#define E_ATTACHMENT_BUTTON_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonPrivate))
+
+struct _EAttachmentButtonPrivate {
+
+ EAttachmentView *view;
+ EAttachment *attachment;
+ gulong reference_handler_id;
+
+ GBinding *can_show_binding;
+ GBinding *shown_binding;
+
+ GtkWidget *expand_button;
+ GtkWidget *toggle_button;
+ GtkWidget *cell_view;
+ GtkWidget *popup_menu;
+
+ guint expandable : 1;
+ guint expanded : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_ATTACHMENT,
+ PROP_EXPANDABLE,
+ PROP_EXPANDED,
+ PROP_VIEW
+};
+
+G_DEFINE_TYPE (
+ EAttachmentButton,
+ e_attachment_button,
+ GTK_TYPE_HBOX)
+
+static void
+attachment_button_menu_deactivate_cb (EAttachmentButton *button)
+{
+ EAttachmentView *view;
+ GtkActionGroup *action_group;
+ GtkToggleButton *toggle_button;
+
+ view = e_attachment_button_get_view (button);
+ action_group = e_attachment_view_get_action_group (view, "inline");
+ toggle_button = GTK_TOGGLE_BUTTON (button->priv->toggle_button);
+
+ gtk_toggle_button_set_active (toggle_button, FALSE);
+
+ gtk_action_group_set_visible (action_group, FALSE);
+}
+
+static void
+attachment_button_menu_position (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ EAttachmentButton *button)
+{
+ GtkRequisition menu_requisition;
+ GtkTextDirection direction;
+ GtkAllocation allocation;
+ GdkRectangle monitor;
+ GdkScreen *screen;
+ GdkWindow *window;
+ GtkWidget *widget;
+ GtkWidget *toggle_button;
+ gint monitor_num;
+
+ widget = GTK_WIDGET (button);
+ toggle_button = button->priv->toggle_button;
+ gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_requisition, NULL);
+
+ window = gtk_widget_get_parent_window (widget);
+ screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+ monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+ if (monitor_num < 0)
+ monitor_num = 0;
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gdk_window_get_origin (window, x, y);
+ *x += allocation.x;
+ *y += allocation.y;
+
+ direction = gtk_widget_get_direction (widget);
+ if (direction == GTK_TEXT_DIR_LTR)
+ *x += MAX (allocation.width - menu_requisition.width, 0);
+ else if (menu_requisition.width > allocation.width)
+ *x -= menu_requisition.width - allocation.width;
+
+ gtk_widget_get_allocation (toggle_button, &allocation);
+
+ if ((*y + allocation.height +
+ menu_requisition.height) <= monitor.y + monitor.height)
+ *y += allocation.height;
+ else if ((*y - menu_requisition.height) >= monitor.y)
+ *y -= menu_requisition.height;
+ else if (monitor.y + monitor.height -
+ (*y + allocation.height) > *y)
+ *y += allocation.height;
+ else
+ *y -= menu_requisition.height;
+
+ *push_in = FALSE;
+}
+
+static void
+attachment_button_select_path (EAttachmentButton *button)
+{
+ EAttachmentView *view;
+ EAttachment *attachment;
+ GtkTreeRowReference *reference;
+ GtkTreePath *path;
+
+ attachment = e_attachment_button_get_attachment (button);
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ reference = e_attachment_get_reference (attachment);
+ g_return_if_fail (gtk_tree_row_reference_valid (reference));
+
+ view = e_attachment_button_get_view (button);
+ path = gtk_tree_row_reference_get_path (reference);
+
+ e_attachment_view_unselect_all (view);
+ e_attachment_view_select_path (view, path);
+
+ gtk_tree_path_free (path);
+}
+
+static void
+attachment_button_show_popup_menu (EAttachmentButton *button,
+ GdkEventButton *event)
+{
+ EAttachmentView *view;
+ GtkActionGroup *action_group;
+ GtkToggleButton *toggle_button;
+
+ view = e_attachment_button_get_view (button);
+ action_group = e_attachment_view_get_action_group (view, "inline");
+ toggle_button = GTK_TOGGLE_BUTTON (button->priv->toggle_button);
+
+ attachment_button_select_path (button);
+ gtk_toggle_button_set_active (toggle_button, TRUE);
+
+ e_attachment_view_show_popup_menu (
+ view, event, (GtkMenuPositionFunc)
+ attachment_button_menu_position, button);
+
+ gtk_action_group_set_visible (action_group, TRUE);
+}
+
+static void
+attachment_button_update_cell_view (EAttachmentButton *button)
+{
+ GtkCellView *cell_view;
+ EAttachment *attachment;
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model = NULL;
+ GtkTreePath *path = NULL;
+
+ cell_view = GTK_CELL_VIEW (button->priv->cell_view);
+
+ attachment = e_attachment_button_get_attachment (button);
+ if (attachment == NULL)
+ goto exit;
+
+ reference = e_attachment_get_reference (attachment);
+ if (reference == NULL)
+ goto exit;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+
+exit:
+ gtk_cell_view_set_model (cell_view, model);
+ gtk_cell_view_set_displayed_row (cell_view, path);
+
+ if (path != NULL)
+ gtk_tree_path_free (path);
+}
+
+static void
+attachment_button_update_pixbufs (EAttachmentButton *button)
+{
+ GtkCellLayout *cell_layout;
+ GtkCellRenderer *renderer;
+ GdkPixbuf *pixbuf_expander_open;
+ GdkPixbuf *pixbuf_expander_closed;
+ GList *list;
+
+ /* Grab the first cell renderer. */
+ cell_layout = GTK_CELL_LAYOUT (button->priv->cell_view);
+ list = gtk_cell_layout_get_cells (cell_layout);
+ renderer = GTK_CELL_RENDERER (list->data);
+ g_list_free (list);
+
+ pixbuf_expander_open = gtk_widget_render_icon (
+ GTK_WIDGET (button), GTK_STOCK_GO_DOWN,
+ GTK_ICON_SIZE_BUTTON, NULL);
+
+ pixbuf_expander_closed = gtk_widget_render_icon (
+ GTK_WIDGET (button), GTK_STOCK_GO_FORWARD,
+ GTK_ICON_SIZE_BUTTON, NULL);
+
+ g_object_set (
+ renderer,
+ "pixbuf-expander-open", pixbuf_expander_open,
+ "pixbuf-expander-closed", pixbuf_expander_closed,
+ NULL);
+
+ g_object_unref (pixbuf_expander_open);
+ g_object_unref (pixbuf_expander_closed);
+}
+
+static void
+attachment_button_expand_clicked_cb (EAttachmentButton *button)
+{
+ gboolean expanded;
+
+ expanded = e_attachment_button_get_expanded (button);
+ e_attachment_button_set_expanded (button, !expanded);
+}
+
+static void
+attachment_button_expand_drag_begin_cb (EAttachmentButton *button,
+ GdkDragContext *context)
+{
+ EAttachmentView *view;
+
+ view = e_attachment_button_get_view (button);
+
+ attachment_button_select_path (button);
+ e_attachment_view_drag_begin (view, context);
+}
+
+static void
+attachment_button_expand_drag_data_get_cb (EAttachmentButton *button,
+ GdkDragContext *context,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ EAttachmentView *view;
+
+ if (button->priv->attachment) {
+ gchar *mime_type;
+
+ mime_type = e_attachment_get_mime_type (
+ button->priv->attachment);
+
+ if (mime_type) {
+ gboolean processed = FALSE;
+ GdkAtom atom;
+ gchar *atom_name;
+
+ atom = gtk_selection_data_get_target (selection);
+ atom_name = gdk_atom_name (atom);
+
+ if (g_strcmp0 (atom_name, mime_type) == 0) {
+ CamelMimePart *mime_part;
+
+ mime_part = e_attachment_get_mime_part (
+ button->priv->attachment);
+
+ if (CAMEL_IS_MIME_PART (mime_part)) {
+ CamelDataWrapper *wrapper;
+ CamelStream *stream;
+ GByteArray *buffer;
+
+ buffer = g_byte_array_new ();
+ stream = camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (
+ CAMEL_STREAM_MEM (stream),
+ buffer);
+ wrapper = camel_medium_get_content (
+ CAMEL_MEDIUM (mime_part));
+ camel_data_wrapper_decode_to_stream_sync (
+ wrapper, stream, NULL, NULL);
+ g_object_unref (stream);
+
+ gtk_selection_data_set (
+ selection, atom, 8,
+ buffer->data, buffer->len);
+ processed = TRUE;
+
+ g_byte_array_free (buffer, TRUE);
+ }
+ }
+
+ g_free (atom_name);
+ g_free (mime_type);
+
+ if (processed)
+ return;
+ }
+ }
+
+ view = e_attachment_button_get_view (button);
+
+ e_attachment_view_drag_data_get (
+ view, context, selection, info, time);
+}
+
+static void
+attachment_button_expand_drag_end_cb (EAttachmentButton *button,
+ GdkDragContext *context)
+{
+ EAttachmentView *view;
+
+ view = e_attachment_button_get_view (button);
+
+ e_attachment_view_drag_end (view, context);
+}
+
+static gboolean
+attachment_button_toggle_button_press_event_cb (EAttachmentButton *button,
+ GdkEventButton *event)
+{
+ if (event->button == 1) {
+ attachment_button_show_popup_menu (button, event);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+attachment_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ATTACHMENT:
+ e_attachment_button_set_attachment (
+ E_ATTACHMENT_BUTTON (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_EXPANDABLE:
+ e_attachment_button_set_expandable (
+ E_ATTACHMENT_BUTTON (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_EXPANDED:
+ e_attachment_button_set_expanded (
+ E_ATTACHMENT_BUTTON (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_VIEW:
+ e_attachment_button_set_view (
+ E_ATTACHMENT_BUTTON (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ATTACHMENT:
+ g_value_set_object (
+ value,
+ e_attachment_button_get_attachment (
+ E_ATTACHMENT_BUTTON (object)));
+ return;
+
+ case PROP_EXPANDABLE:
+ g_value_set_boolean (
+ value,
+ e_attachment_button_get_expandable (
+ E_ATTACHMENT_BUTTON (object)));
+ return;
+
+ case PROP_EXPANDED:
+ g_value_set_boolean (
+ value,
+ e_attachment_button_get_expanded (
+ E_ATTACHMENT_BUTTON (object)));
+ return;
+
+ case PROP_VIEW:
+ g_value_set_object (
+ value,
+ e_attachment_button_get_view (
+ E_ATTACHMENT_BUTTON (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_button_dispose (GObject *object)
+{
+ EAttachmentButtonPrivate *priv;
+
+ priv = E_ATTACHMENT_BUTTON_GET_PRIVATE (object);
+
+ if (priv->view != NULL) {
+ g_object_unref (priv->view);
+ priv->view = NULL;
+ }
+
+ if (priv->attachment != NULL) {
+ g_signal_handler_disconnect (
+ priv->attachment,
+ priv->reference_handler_id);
+ g_object_unref (priv->attachment);
+ priv->attachment = NULL;
+ }
+
+ if (priv->expand_button != NULL) {
+ g_object_unref (priv->expand_button);
+ priv->expand_button = NULL;
+ }
+
+ if (priv->toggle_button != NULL) {
+ g_object_unref (priv->toggle_button);
+ priv->toggle_button = NULL;
+ }
+
+ if (priv->cell_view != NULL) {
+ g_object_unref (priv->cell_view);
+ priv->cell_view = NULL;
+ }
+
+ if (priv->popup_menu != NULL) {
+ g_signal_handlers_disconnect_matched (
+ priv->popup_menu, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_unref (priv->popup_menu);
+ priv->popup_menu = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_attachment_button_parent_class)->dispose (object);
+}
+
+static void
+attachment_button_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ EAttachmentButton *button;
+
+ /* Chain up to parent's style_set() method. */
+ GTK_WIDGET_CLASS (e_attachment_button_parent_class)->
+ style_set (widget, previous_style);
+
+ button = E_ATTACHMENT_BUTTON (widget);
+ attachment_button_update_pixbufs (button);
+}
+
+static void
+e_attachment_button_class_init (EAttachmentButtonClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ g_type_class_add_private (class, sizeof (EAttachmentButtonPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_button_set_property;
+ object_class->get_property = attachment_button_get_property;
+ object_class->dispose = attachment_button_dispose;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->style_set = attachment_button_style_set;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ATTACHMENT,
+ g_param_spec_object (
+ "attachment",
+ "Attachment",
+ NULL,
+ E_TYPE_ATTACHMENT,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EXPANDABLE,
+ g_param_spec_boolean (
+ "expandable",
+ "Expandable",
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EXPANDED,
+ g_param_spec_boolean (
+ "expanded",
+ "Expanded",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_VIEW,
+ g_param_spec_object (
+ "view",
+ "View",
+ NULL,
+ E_TYPE_ATTACHMENT_VIEW,
+ G_PARAM_READWRITE));
+}
+
+static void
+e_attachment_button_init (EAttachmentButton *button)
+{
+ GtkCellRenderer *renderer;
+ GtkCellLayout *cell_layout;
+ GtkTargetEntry *targets;
+ GtkTargetList *list;
+ GtkWidget *container;
+ GtkWidget *widget;
+ GtkStyleContext *context;
+ gint n_targets;
+
+ button->priv = E_ATTACHMENT_BUTTON_GET_PRIVATE (button);
+
+ /* Configure Widgets */
+
+ container = GTK_WIDGET (button);
+ context = gtk_widget_get_style_context (container);
+ gtk_style_context_add_class (context, "linked");
+
+ widget = gtk_button_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ button->priv->expand_button = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ g_object_bind_property (
+ button, "expandable",
+ widget, "sensitive",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ widget = gtk_toggle_button_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ button->priv->toggle_button = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = button->priv->expand_button;
+
+ widget = gtk_cell_view_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ button->priv->cell_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = button->priv->toggle_button;
+
+ widget = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ /* Configure Renderers */
+
+ cell_layout = GTK_CELL_LAYOUT (button->priv->cell_view);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (renderer, "is-expander", TRUE, NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+ g_object_bind_property (
+ button, "expanded",
+ renderer, "is-expanded",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "gicon",
+ E_ATTACHMENT_STORE_COLUMN_ICON);
+
+ /* Configure Drag and Drop */
+
+ list = gtk_target_list_new (NULL, 0);
+ gtk_target_list_add_uri_targets (list, 0);
+ targets = gtk_target_table_new_from_list (list, &n_targets);
+
+ gtk_drag_source_set (
+ button->priv->expand_button, GDK_BUTTON1_MASK,
+ targets, n_targets, GDK_ACTION_COPY);
+
+ gtk_drag_source_set (
+ button->priv->toggle_button, GDK_BUTTON1_MASK,
+ targets, n_targets, GDK_ACTION_COPY);
+
+ gtk_target_table_free (targets, n_targets);
+ gtk_target_list_unref (list);
+
+ /* Configure Signal Handlers */
+
+ g_signal_connect_swapped (
+ button->priv->expand_button, "clicked",
+ G_CALLBACK (attachment_button_expand_clicked_cb), button);
+
+ g_signal_connect_swapped (
+ button->priv->expand_button, "drag-begin",
+ G_CALLBACK (attachment_button_expand_drag_begin_cb),
+ button);
+
+ g_signal_connect_swapped (
+ button->priv->expand_button, "drag-data-get",
+ G_CALLBACK (attachment_button_expand_drag_data_get_cb),
+ button);
+
+ g_signal_connect_swapped (
+ button->priv->expand_button, "drag-end",
+ G_CALLBACK (attachment_button_expand_drag_end_cb),
+ button);
+
+ g_signal_connect_swapped (
+ button->priv->toggle_button, "button-press-event",
+ G_CALLBACK (attachment_button_toggle_button_press_event_cb),
+ button);
+
+ g_signal_connect_swapped (
+ button->priv->toggle_button, "drag-begin",
+ G_CALLBACK (attachment_button_expand_drag_begin_cb),
+ button);
+
+ g_signal_connect_swapped (
+ button->priv->toggle_button, "drag-data-get",
+ G_CALLBACK (attachment_button_expand_drag_data_get_cb),
+ button);
+
+ g_signal_connect_swapped (
+ button->priv->toggle_button, "drag-end",
+ G_CALLBACK (attachment_button_expand_drag_end_cb),
+ button);
+}
+
+GtkWidget *
+e_attachment_button_new ()
+{
+ return g_object_new (
+ E_TYPE_ATTACHMENT_BUTTON, NULL);
+}
+
+EAttachmentView *
+e_attachment_button_get_view (EAttachmentButton *button)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), NULL);
+
+ return button->priv->view;
+}
+
+void
+e_attachment_button_set_view (EAttachmentButton *button,
+ EAttachmentView *view)
+{
+ GtkWidget *popup_menu;
+
+ g_return_if_fail (button->priv->view == NULL);
+
+ g_object_ref (view);
+ if (button->priv->view)
+ g_object_unref (button->priv->view);
+ button->priv->view = view;
+
+ popup_menu = e_attachment_view_get_popup_menu (view);
+
+ g_signal_connect_swapped (
+ popup_menu, "deactivate",
+ G_CALLBACK (attachment_button_menu_deactivate_cb), button);
+
+ /* Keep a reference to the popup menu so we can
+ * disconnect the signal handler in dispose(). */
+ if (button->priv->popup_menu)
+ g_object_unref (button->priv->popup_menu);
+ button->priv->popup_menu = g_object_ref (popup_menu);
+}
+
+EAttachment *
+e_attachment_button_get_attachment (EAttachmentButton *button)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), NULL);
+
+ return button->priv->attachment;
+}
+
+void
+e_attachment_button_set_attachment (EAttachmentButton *button,
+ EAttachment *attachment)
+{
+ GtkTargetEntry *targets;
+ GtkTargetList *list;
+ gint n_targets;
+
+ g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+ if (attachment != NULL) {
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_object_ref (attachment);
+ }
+
+ if (button->priv->attachment != NULL) {
+ g_object_unref (button->priv->can_show_binding);
+ button->priv->can_show_binding = NULL;
+ g_object_unref (button->priv->shown_binding);
+ button->priv->shown_binding = NULL;
+ g_signal_handler_disconnect (
+ button->priv->attachment,
+ button->priv->reference_handler_id);
+ g_object_unref (button->priv->attachment);
+ }
+
+ button->priv->attachment = attachment;
+
+ if (attachment != NULL) {
+ GBinding *binding;
+ gulong handler_id;
+
+ binding = g_object_bind_property (
+ attachment, "can-show",
+ button, "expandable",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+ button->priv->can_show_binding = binding;
+
+ binding = g_object_bind_property (
+ attachment, "shown",
+ button, "expanded",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+ button->priv->shown_binding = binding;
+
+ handler_id = g_signal_connect_swapped (
+ attachment, "notify::reference",
+ G_CALLBACK (attachment_button_update_cell_view),
+ button);
+ button->priv->reference_handler_id = handler_id;
+
+ attachment_button_update_cell_view (button);
+ attachment_button_update_pixbufs (button);
+ }
+
+ /* update drag sources */
+ list = gtk_target_list_new (NULL, 0);
+ gtk_target_list_add_uri_targets (list, 0);
+
+ if (attachment) {
+ gchar *simple_type;
+
+ simple_type = e_attachment_get_mime_type (attachment);
+ if (simple_type) {
+ GtkTargetEntry attach_entry[] = { { NULL, 0, 2 } };
+
+ attach_entry[0].target = simple_type;
+
+ gtk_target_list_add_table (
+ list, attach_entry,
+ G_N_ELEMENTS (attach_entry));
+
+ g_free (simple_type);
+ }
+ }
+
+ targets = gtk_target_table_new_from_list (list, &n_targets);
+
+ gtk_drag_source_set (
+ button->priv->expand_button, GDK_BUTTON1_MASK,
+ targets, n_targets, GDK_ACTION_COPY);
+
+ gtk_drag_source_set (
+ button->priv->toggle_button, GDK_BUTTON1_MASK,
+ targets, n_targets, GDK_ACTION_COPY);
+
+ gtk_target_table_free (targets, n_targets);
+ gtk_target_list_unref (list);
+
+ g_object_notify (G_OBJECT (button), "attachment");
+}
+
+gboolean
+e_attachment_button_get_expandable (EAttachmentButton *button)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE);
+
+ return button->priv->expandable;
+}
+
+void
+e_attachment_button_set_expandable (EAttachmentButton *button,
+ gboolean expandable)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+ if (button->priv->expandable == expandable)
+ return;
+
+ button->priv->expandable = expandable;
+
+ if (!expandable)
+ e_attachment_button_set_expanded (button, FALSE);
+
+ g_object_notify (G_OBJECT (button), "expandable");
+}
+
+gboolean
+e_attachment_button_get_expanded (EAttachmentButton *button)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE);
+
+ return button->priv->expanded;
+}
+
+void
+e_attachment_button_set_expanded (EAttachmentButton *button,
+ gboolean expanded)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+ if (button->priv->expanded == expanded)
+ return;
+
+ button->priv->expanded = expanded;
+
+ g_object_notify (G_OBJECT (button), "expanded");
+}
diff --git a/e-util/e-attachment-button.h b/e-util/e-attachment-button.h
new file mode 100644
index 0000000000..abe5fa4dc9
--- /dev/null
+++ b/e-util/e-attachment-button.h
@@ -0,0 +1,91 @@
+/*
+ * e-attachment-button.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ATTACHMENT_BUTTON_H
+#define E_ATTACHMENT_BUTTON_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-attachment.h>
+#include <e-util/e-attachment-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_BUTTON \
+ (e_attachment_button_get_type ())
+#define E_ATTACHMENT_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButton))
+#define E_ATTACHMENT_BUTTON_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonClass))
+#define E_IS_ATTACHMENT_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_BUTTON))
+#define E_IS_ATTACHMENT_BUTTON_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_BUTTON))
+#define E_ATTACHMENT_BUTTON_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentButton EAttachmentButton;
+typedef struct _EAttachmentButtonClass EAttachmentButtonClass;
+typedef struct _EAttachmentButtonPrivate EAttachmentButtonPrivate;
+
+struct _EAttachmentButton {
+ GtkBox parent;
+ EAttachmentButtonPrivate *priv;
+};
+
+struct _EAttachmentButtonClass {
+ GtkBoxClass parent_class;
+};
+
+GType e_attachment_button_get_type (void);
+GtkWidget * e_attachment_button_new (void);
+EAttachmentView *
+ e_attachment_button_get_view (EAttachmentButton *button);
+void e_attachment_button_set_view (EAttachmentButton *button,
+ EAttachmentView *view);
+EAttachment * e_attachment_button_get_attachment
+ (EAttachmentButton *button);
+void e_attachment_button_set_attachment
+ (EAttachmentButton *button,
+ EAttachment *attachment);
+gboolean e_attachment_button_get_expandable
+ (EAttachmentButton *button);
+void e_attachment_button_set_expandable
+ (EAttachmentButton *button,
+ gboolean expandable);
+gboolean e_attachment_button_get_expanded
+ (EAttachmentButton *button);
+void e_attachment_button_set_expanded
+ (EAttachmentButton *button,
+ gboolean expanded);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_BUTTON_H */
diff --git a/e-util/e-attachment-dialog.c b/e-util/e-attachment-dialog.c
new file mode 100644
index 0000000000..9a9a1e7942
--- /dev/null
+++ b/e-util/e-attachment-dialog.c
@@ -0,0 +1,425 @@
+/*
+ * e-attachment-dialog.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-dialog.h"
+
+#include <glib/gi18n.h>
+
+#define E_ATTACHMENT_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogPrivate))
+
+struct _EAttachmentDialogPrivate {
+ EAttachment *attachment;
+ GtkWidget *display_name_entry;
+ GtkWidget *description_entry;
+ GtkWidget *content_type_label;
+ GtkWidget *disposition_checkbox;
+};
+
+enum {
+ PROP_0,
+ PROP_ATTACHMENT
+};
+
+G_DEFINE_TYPE (
+ EAttachmentDialog,
+ e_attachment_dialog,
+ GTK_TYPE_DIALOG)
+
+static void
+attachment_dialog_update (EAttachmentDialog *dialog)
+{
+ EAttachment *attachment;
+ GFileInfo *file_info;
+ GtkWidget *widget;
+ const gchar *content_type;
+ const gchar *display_name;
+ const gchar *description;
+ const gchar *disposition;
+ gchar *type_description = NULL;
+ gboolean sensitive;
+ gboolean active;
+
+ attachment = e_attachment_dialog_get_attachment (dialog);
+
+ if (attachment != NULL) {
+ file_info = e_attachment_get_file_info (attachment);
+ description = e_attachment_get_description (attachment);
+ disposition = e_attachment_get_disposition (attachment);
+ } else {
+ file_info = NULL;
+ description = NULL;
+ disposition = NULL;
+ }
+
+ if (file_info != NULL) {
+ content_type = g_file_info_get_content_type (file_info);
+ display_name = g_file_info_get_display_name (file_info);
+ } else {
+ content_type = NULL;
+ display_name = NULL;
+ }
+
+ if (content_type != NULL) {
+ gchar *comment;
+ gchar *mime_type;
+
+ comment = g_content_type_get_description (content_type);
+ mime_type = g_content_type_get_mime_type (content_type);
+
+ type_description =
+ g_strdup_printf ("%s (%s)", comment, mime_type);
+
+ g_free (comment);
+ g_free (mime_type);
+ }
+
+ sensitive = G_IS_FILE_INFO (file_info);
+
+ gtk_dialog_set_response_sensitive (
+ GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive);
+
+ widget = dialog->priv->display_name_entry;
+ gtk_widget_set_sensitive (widget, sensitive);
+ if (display_name != NULL)
+ gtk_entry_set_text (GTK_ENTRY (widget), display_name);
+
+ widget = dialog->priv->description_entry;
+ gtk_widget_set_sensitive (widget, sensitive);
+ if (description != NULL)
+ gtk_entry_set_text (GTK_ENTRY (widget), description);
+
+ widget = dialog->priv->content_type_label;
+ gtk_label_set_text (GTK_LABEL (widget), type_description);
+
+ active = (g_strcmp0 (disposition, "inline") == 0);
+ widget = dialog->priv->disposition_checkbox;
+ gtk_widget_set_sensitive (widget, sensitive);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), active);
+
+ g_free (type_description);
+}
+
+static void
+attachment_dialog_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ATTACHMENT:
+ e_attachment_dialog_set_attachment (
+ E_ATTACHMENT_DIALOG (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_dialog_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ATTACHMENT:
+ g_value_set_object (
+ value, e_attachment_dialog_get_attachment (
+ E_ATTACHMENT_DIALOG (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_dialog_dispose (GObject *object)
+{
+ EAttachmentDialogPrivate *priv;
+
+ priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (object);
+
+ if (priv->attachment != NULL) {
+ g_object_unref (priv->attachment);
+ priv->attachment = NULL;
+ }
+
+ if (priv->display_name_entry != NULL) {
+ g_object_unref (priv->display_name_entry);
+ priv->display_name_entry = NULL;
+ }
+
+ if (priv->description_entry != NULL) {
+ g_object_unref (priv->description_entry);
+ priv->description_entry = NULL;
+ }
+
+ if (priv->content_type_label != NULL) {
+ g_object_unref (priv->content_type_label);
+ priv->content_type_label = NULL;
+ }
+
+ if (priv->disposition_checkbox != NULL) {
+ g_object_unref (priv->disposition_checkbox);
+ priv->disposition_checkbox = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_attachment_dialog_parent_class)->dispose (object);
+}
+
+static void
+attachment_dialog_map (GtkWidget *widget)
+{
+ GtkWidget *action_area;
+ GtkWidget *content_area;
+
+ /* Chain up to parent's map() method. */
+ GTK_WIDGET_CLASS (e_attachment_dialog_parent_class)->map (widget);
+
+ /* XXX Override GtkDialog's broken style property defaults. */
+ action_area = gtk_dialog_get_action_area (GTK_DIALOG (widget));
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (widget));
+
+ gtk_box_set_spacing (GTK_BOX (content_area), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 12);
+}
+
+static void
+attachment_dialog_response (GtkDialog *dialog,
+ gint response_id)
+{
+ EAttachmentDialogPrivate *priv;
+ EAttachment *attachment;
+ GtkToggleButton *button;
+ GFileInfo *file_info;
+ CamelMimePart *mime_part;
+ const gchar *attribute;
+ const gchar *text;
+ gboolean active;
+
+ if (response_id != GTK_RESPONSE_OK)
+ return;
+
+ priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog);
+ g_return_if_fail (E_IS_ATTACHMENT (priv->attachment));
+ attachment = priv->attachment;
+
+ file_info = e_attachment_get_file_info (attachment);
+ g_return_if_fail (G_IS_FILE_INFO (file_info));
+
+ mime_part = e_attachment_get_mime_part (attachment);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+ text = gtk_entry_get_text (GTK_ENTRY (priv->display_name_entry));
+ g_file_info_set_attribute_string (file_info, attribute, text);
+
+ if (mime_part != NULL)
+ camel_mime_part_set_filename (mime_part, text);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ text = gtk_entry_get_text (GTK_ENTRY (priv->description_entry));
+ g_file_info_set_attribute_string (file_info, attribute, text);
+
+ if (mime_part != NULL)
+ camel_mime_part_set_description (mime_part, text);
+
+ button = GTK_TOGGLE_BUTTON (priv->disposition_checkbox);
+ active = gtk_toggle_button_get_active (button);
+ text = active ? "inline" : "attachment";
+ e_attachment_set_disposition (attachment, text);
+
+ if (mime_part != NULL)
+ camel_mime_part_set_disposition (mime_part, text);
+
+ g_object_notify (G_OBJECT (attachment), "file-info");
+}
+
+static void
+e_attachment_dialog_class_init (EAttachmentDialogClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkDialogClass *dialog_class;
+
+ g_type_class_add_private (class, sizeof (EAttachmentDialogPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_dialog_set_property;
+ object_class->get_property = attachment_dialog_get_property;
+ object_class->dispose = attachment_dialog_dispose;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->map = attachment_dialog_map;
+
+ dialog_class = GTK_DIALOG_CLASS (class);
+ dialog_class->response = attachment_dialog_response;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ATTACHMENT,
+ g_param_spec_object (
+ "attachment",
+ "Attachment",
+ NULL,
+ E_TYPE_ATTACHMENT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+e_attachment_dialog_init (EAttachmentDialog *dialog)
+{
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ dialog->priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog);
+
+ gtk_dialog_add_button (
+ GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (
+ GTK_DIALOG (dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+ gtk_window_set_icon_name (
+ GTK_WINDOW (dialog), "mail-attachment");
+ gtk_window_set_title (
+ GTK_WINDOW (dialog), _("Attachment Properties"));
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ widget = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (widget), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (widget), 6);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ dialog->priv->display_name_entry = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new_with_mnemonic (_("F_ilename:"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->display_name_entry);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ dialog->priv->description_entry = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new_with_mnemonic (_("_Description:"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->description_entry);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 1, 2, 2, 3, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ dialog->priv->content_type_label = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (_("MIME Type:"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_check_button_new_with_mnemonic (
+ _("_Suggest automatic display of attachment"));
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 0, 2, 3, 4, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ dialog->priv->disposition_checkbox = g_object_ref (widget);
+ gtk_widget_show (widget);
+}
+
+GtkWidget *
+e_attachment_dialog_new (GtkWindow *parent,
+ EAttachment *attachment)
+{
+ if (parent != NULL)
+ g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
+ if (attachment != NULL)
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+ return g_object_new (
+ E_TYPE_ATTACHMENT_DIALOG,
+ "transient-for", parent, "attachment", attachment, NULL);
+}
+
+EAttachment *
+e_attachment_dialog_get_attachment (EAttachmentDialog *dialog)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_DIALOG (dialog), NULL);
+
+ return dialog->priv->attachment;
+}
+
+void
+e_attachment_dialog_set_attachment (EAttachmentDialog *dialog,
+ EAttachment *attachment)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_DIALOG (dialog));
+
+ if (attachment != NULL) {
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_object_ref (attachment);
+ }
+
+ if (dialog->priv->attachment != NULL)
+ g_object_unref (dialog->priv->attachment);
+
+ dialog->priv->attachment = attachment;
+
+ attachment_dialog_update (dialog);
+
+ g_object_notify (G_OBJECT (dialog), "attachment");
+}
diff --git a/e-util/e-attachment-dialog.h b/e-util/e-attachment-dialog.h
new file mode 100644
index 0000000000..af7141190e
--- /dev/null
+++ b/e-util/e-attachment-dialog.h
@@ -0,0 +1,77 @@
+/*
+ * e-attachment-dialog.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ATTACHMENT_DIALOG_H
+#define E_ATTACHMENT_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-attachment.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_DIALOG \
+ (e_attachment_dialog_get_type ())
+#define E_ATTACHMENT_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialog))
+#define E_ATTACHMENT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogClass))
+#define E_IS_ATTACHMENT_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_DIALOG))
+#define E_IS_ATTACHMENT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_DIALOG))
+#define E_ATTACHMENT_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentDialog EAttachmentDialog;
+typedef struct _EAttachmentDialogClass EAttachmentDialogClass;
+typedef struct _EAttachmentDialogPrivate EAttachmentDialogPrivate;
+
+struct _EAttachmentDialog {
+ GtkDialog parent;
+ EAttachmentDialogPrivate *priv;
+};
+
+struct _EAttachmentDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType e_attachment_dialog_get_type (void);
+GtkWidget * e_attachment_dialog_new (GtkWindow *parent,
+ EAttachment *attachment);
+EAttachment * e_attachment_dialog_get_attachment
+ (EAttachmentDialog *dialog);
+void e_attachment_dialog_set_attachment
+ (EAttachmentDialog *dialog,
+ EAttachment *attachment);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_DIALOG_H */
diff --git a/e-util/e-attachment-handler-image.c b/e-util/e-attachment-handler-image.c
new file mode 100644
index 0000000000..36c3a83614
--- /dev/null
+++ b/e-util/e-attachment-handler-image.c
@@ -0,0 +1,246 @@
+/*
+ * e-attachment-handler-image.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-handler-image.h"
+
+#include <glib/gi18n.h>
+#include <gdesktop-enums.h>
+
+#define E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImagePrivate))
+
+struct _EAttachmentHandlerImagePrivate {
+ gint placeholder;
+};
+
+static const gchar *ui =
+"<ui>"
+" <popup name='context'>"
+" <placeholder name='custom-actions'>"
+" <menuitem action='image-set-as-background'/>"
+" </placeholder>"
+" </popup>"
+"</ui>";
+
+G_DEFINE_TYPE (
+ EAttachmentHandlerImage,
+ e_attachment_handler_image,
+ E_TYPE_ATTACHMENT_HANDLER)
+
+static void
+action_image_set_as_background_saved_cb (EAttachment *attachment,
+ GAsyncResult *result,
+ EAttachmentHandler *handler)
+{
+ GDesktopBackgroundStyle style;
+ EAttachmentView *view;
+ GSettings *settings;
+ GtkWidget *dialog;
+ GFile *file;
+ gpointer parent;
+ gchar *uri;
+ GError *error = NULL;
+
+ view = e_attachment_handler_get_view (handler);
+ settings = g_settings_new ("org.gnome.desktop.background");
+
+ file = e_attachment_save_finish (attachment, result, &error);
+
+ if (error != NULL)
+ goto error;
+
+ uri = g_file_get_uri (file);
+ g_settings_set_string (settings, "picture-uri", uri);
+ g_free (uri);
+
+ style = g_settings_get_enum (settings, "picture-options");
+ if (style == G_DESKTOP_BACKGROUND_STYLE_NONE)
+ g_settings_set_enum (
+ settings, "picture-options",
+ G_DESKTOP_BACKGROUND_STYLE_WALLPAPER);
+
+ g_object_unref (file);
+
+ goto exit;
+
+error:
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+ dialog = gtk_message_dialog_new_with_markup (
+ parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>",
+ _("Could not set as background"));
+
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+ g_error_free (error);
+
+exit:
+ g_object_unref (settings);
+ g_object_unref (handler);
+}
+
+static void
+action_image_set_as_background_cb (GtkAction *action,
+ EAttachmentHandler *handler)
+{
+ EAttachmentView *view;
+ EAttachment *attachment;
+ GFile *destination;
+ GList *selected;
+ const gchar *path;
+
+ view = e_attachment_handler_get_view (handler);
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = E_ATTACHMENT (selected->data);
+
+ /* Save the image under the user's Pictures directory. */
+ path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
+ destination = g_file_new_for_path (path);
+ g_mkdir_with_parents (path, 0755);
+
+ e_attachment_save_async (
+ attachment, destination, (GAsyncReadyCallback)
+ action_image_set_as_background_saved_cb,
+ g_object_ref (handler));
+
+ g_object_unref (destination);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+ { "image-set-as-background",
+ NULL,
+ N_("Set as _Background"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_image_set_as_background_cb) }
+};
+
+static void
+attachment_handler_image_update_actions_cb (EAttachmentView *view,
+ EAttachmentHandler *handler)
+{
+ EAttachment *attachment;
+ GFileInfo *file_info;
+ GtkActionGroup *action_group;
+ const gchar *content_type;
+ gchar *mime_type;
+ GList *selected;
+ gboolean visible = FALSE;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+
+ if (g_list_length (selected) != 1)
+ goto exit;
+
+ attachment = E_ATTACHMENT (selected->data);
+ file_info = e_attachment_get_file_info (attachment);
+
+ if (file_info == NULL)
+ goto exit;
+
+ if (e_attachment_get_loading (attachment))
+ goto exit;
+
+ if (e_attachment_get_saving (attachment))
+ goto exit;
+
+ content_type = g_file_info_get_content_type (file_info);
+
+ mime_type = g_content_type_get_mime_type (content_type);
+ visible = (g_ascii_strncasecmp (mime_type, "image/", 6) == 0);
+ g_free (mime_type);
+
+exit:
+ action_group = e_attachment_view_get_action_group (view, "image");
+ gtk_action_group_set_visible (action_group, visible);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+attachment_handler_image_constructed (GObject *object)
+{
+ EAttachmentHandler *handler;
+ EAttachmentView *view;
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+ GError *error = NULL;
+
+ handler = E_ATTACHMENT_HANDLER (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_attachment_handler_image_parent_class)->constructed (object);
+
+ view = e_attachment_handler_get_view (handler);
+
+ action_group = e_attachment_view_add_action_group (view, "image");
+ gtk_action_group_add_actions (
+ action_group, standard_entries,
+ G_N_ELEMENTS (standard_entries), object);
+
+ ui_manager = e_attachment_view_get_ui_manager (view);
+ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_signal_connect (
+ view, "update-actions",
+ G_CALLBACK (attachment_handler_image_update_actions_cb),
+ object);
+}
+
+static void
+e_attachment_handler_image_class_init (EAttachmentHandlerImageClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EAttachmentHandlerImagePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = attachment_handler_image_constructed;
+}
+
+static void
+e_attachment_handler_image_init (EAttachmentHandlerImage *handler)
+{
+ handler->priv = E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE (handler);
+}
diff --git a/e-util/e-attachment-handler-image.h b/e-util/e-attachment-handler-image.h
new file mode 100644
index 0000000000..e0e0cb3b23
--- /dev/null
+++ b/e-util/e-attachment-handler-image.h
@@ -0,0 +1,69 @@
+/*
+ * e-attachment-handler-image.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ATTACHMENT_HANDLER_IMAGE_H
+#define E_ATTACHMENT_HANDLER_IMAGE_H
+
+#include <e-util/e-attachment-handler.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER_IMAGE \
+ (e_attachment_handler_image_get_type ())
+#define E_ATTACHMENT_HANDLER_IMAGE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImage))
+#define E_ATTACHMENT_HANDLER_IMAGE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImageClass))
+#define E_IS_ATTACHMENT_HANDLER_IMAGE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE))
+#define E_IS_ATTACHMENT_HANDLER_IMAGE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_IMAGE))
+#define E_ATTACHMENT_HANDLER_IMAGE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImageClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandlerImage EAttachmentHandlerImage;
+typedef struct _EAttachmentHandlerImageClass EAttachmentHandlerImageClass;
+typedef struct _EAttachmentHandlerImagePrivate EAttachmentHandlerImagePrivate;
+
+struct _EAttachmentHandlerImage {
+ EAttachmentHandler parent;
+ EAttachmentHandlerImagePrivate *priv;
+};
+
+struct _EAttachmentHandlerImageClass {
+ EAttachmentHandlerClass parent_class;
+};
+
+GType e_attachment_handler_image_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_IMAGE_H */
diff --git a/e-util/e-attachment-handler-sendto.c b/e-util/e-attachment-handler-sendto.c
new file mode 100644
index 0000000000..f0fe698713
--- /dev/null
+++ b/e-util/e-attachment-handler-sendto.c
@@ -0,0 +1,229 @@
+/*
+ * e-attachment-handler-sendto.c
+ *
+ * Copyright (C) 2009 Matthew Barnes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-handler-sendto.h"
+
+#include <errno.h>
+
+#include <glib/gi18n-lib.h>
+
+static const gchar *ui =
+"<ui>"
+" <popup name='context'>"
+" <placeholder name='custom-actions'>"
+" <menuitem action='sendto'/>"
+" </placeholder>"
+" </popup>"
+"</ui>";
+
+G_DEFINE_TYPE (
+ EAttachmentHandlerSendto,
+ e_attachment_handler_sendto,
+ E_TYPE_ATTACHMENT_HANDLER)
+
+static void
+sendto_save_finished_cb (EAttachment *attachment,
+ GAsyncResult *result,
+ EAttachmentHandler *handler)
+{
+ EAttachmentView *view;
+ EAttachmentStore *store;
+ GtkWidget *dialog;
+ gchar **uris;
+ gpointer parent;
+ gchar *arguments;
+ gchar *command_line;
+ guint n_uris = 1;
+ GError *error = NULL;
+
+ view = e_attachment_handler_get_view (handler);
+ store = e_attachment_view_get_store (view);
+
+ uris = e_attachment_store_get_uris_finish (store, result, &error);
+
+ if (uris != NULL)
+ n_uris = g_strv_length (uris);
+
+ if (error != NULL)
+ goto error;
+
+ arguments = g_strjoinv (" ", uris);
+ command_line = g_strdup_printf ("nautilus-sendto %s", arguments);
+
+ g_message ("Command: %s", command_line);
+ g_spawn_command_line_async (command_line, &error);
+
+ g_free (command_line);
+ g_free (arguments);
+
+ if (error != NULL)
+ goto error;
+
+ goto exit;
+
+error:
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+ dialog = gtk_message_dialog_new_with_markup (
+ parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>",
+ ngettext ("Could not send attachment",
+ "Could not send attachments", n_uris));
+
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+ g_error_free (error);
+
+exit:
+ g_object_unref (handler);
+ g_strfreev (uris);
+}
+
+static void
+action_sendto_cb (GtkAction *action,
+ EAttachmentHandler *handler)
+{
+ EAttachmentView *view;
+ EAttachmentStore *store;
+ GList *selected;
+
+ view = e_attachment_handler_get_view (handler);
+ store = e_attachment_view_get_store (view);
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (selected != NULL);
+
+ e_attachment_store_get_uris_async (
+ store, selected, (GAsyncReadyCallback)
+ sendto_save_finished_cb, g_object_ref (handler));
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+ { "sendto",
+ "document-send",
+ N_("_Send To..."),
+ NULL,
+ N_("Send the selected attachments somewhere"),
+ G_CALLBACK (action_sendto_cb) }
+};
+
+static void
+attachment_handler_sendto_update_actions_cb (EAttachmentView *view,
+ EAttachmentHandler *handler)
+{
+ GtkActionGroup *action_group;
+ GList *selected, *iter;
+ gboolean visible = FALSE;
+ gchar *program;
+
+ program = g_find_program_in_path ("nautilus-sendto");
+ selected = e_attachment_view_get_selected_attachments (view);
+
+ if (program == NULL || selected == NULL)
+ goto exit;
+
+ /* Make sure no file transfers are in progress. */
+ for (iter = selected; iter != NULL; iter = iter->next) {
+ EAttachment *attachment = iter->data;
+
+ if (e_attachment_get_loading (attachment))
+ goto exit;
+
+ if (e_attachment_get_saving (attachment))
+ goto exit;
+ }
+
+ visible = TRUE;
+
+exit:
+ action_group = e_attachment_view_get_action_group (view, "sendto");
+ gtk_action_group_set_visible (action_group, visible);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+
+ g_free (program);
+}
+
+static void
+attachment_handler_sendto_constructed (GObject *object)
+{
+ EAttachmentHandler *handler;
+ EAttachmentView *view;
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+ GError *error = NULL;
+
+ handler = E_ATTACHMENT_HANDLER (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_attachment_handler_sendto_parent_class)->constructed (object);
+
+ view = e_attachment_handler_get_view (handler);
+ ui_manager = e_attachment_view_get_ui_manager (view);
+
+ action_group = gtk_action_group_new ("sendto");
+ gtk_action_group_set_translation_domain (
+ action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (
+ action_group, standard_entries,
+ G_N_ELEMENTS (standard_entries), object);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_signal_connect (
+ view, "update-actions",
+ G_CALLBACK (attachment_handler_sendto_update_actions_cb),
+ object);
+}
+
+static void
+e_attachment_handler_sendto_class_init (EAttachmentHandlerSendtoClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = attachment_handler_sendto_constructed;
+}
+
+static void
+e_attachment_handler_sendto_init (EAttachmentHandlerSendto *handler)
+{
+}
diff --git a/e-util/e-attachment-handler-sendto.h b/e-util/e-attachment-handler-sendto.h
new file mode 100644
index 0000000000..17115c4104
--- /dev/null
+++ b/e-util/e-attachment-handler-sendto.h
@@ -0,0 +1,66 @@
+/*
+ * e-attachment-handler-sendto.h
+ *
+ * Copyright (C) 2009 Matthew Barnes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ATTACHMENT_HANDLER_SENDTO_H
+#define E_ATTACHMENT_HANDLER_SENDTO_H
+
+#include <e-util/e-attachment-handler.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER_SENDTO \
+ (e_attachment_handler_sendto_get_type ())
+#define E_ATTACHMENT_HANDLER_SENDTO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_SENDTO, EAttachmentHandlerSendto))
+#define E_ATTACHMENT_HANDLER_SENDTO_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_SENDTO, EAttachmentHandlerSendtoClass))
+#define E_IS_ATTACHMENT_HANDLER_SENDTO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_SENDTO))
+#define E_IS_ATTACHMENT_HANDLER_SENDTO_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_SENDTO))
+#define E_ATTACHMENT_HANDLER_SENDTO_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_SENDTO, EAttachmentHandlerSendtoClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandlerSendto EAttachmentHandlerSendto;
+typedef struct _EAttachmentHandlerSendtoClass EAttachmentHandlerSendtoClass;
+
+struct _EAttachmentHandlerSendto {
+ EAttachmentHandler parent;
+};
+
+struct _EAttachmentHandlerSendtoClass {
+ EAttachmentHandlerClass parent_class;
+};
+
+GType e_attachment_handler_sendto_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_SENDTO_H */
diff --git a/e-util/e-attachment-handler.c b/e-util/e-attachment-handler.c
new file mode 100644
index 0000000000..87b9abddb5
--- /dev/null
+++ b/e-util/e-attachment-handler.c
@@ -0,0 +1,133 @@
+/*
+ * e-attachment-handler.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-handler.h"
+
+#define E_ATTACHMENT_HANDLER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerPrivate))
+
+struct _EAttachmentHandlerPrivate {
+ gpointer placeholder;
+};
+
+G_DEFINE_TYPE (
+ EAttachmentHandler,
+ e_attachment_handler,
+ E_TYPE_EXTENSION)
+
+static void
+attachment_handler_constructed (GObject *object)
+{
+ EAttachmentView *view;
+ EAttachmentHandler *handler;
+ GdkDragAction drag_actions;
+ GtkTargetList *target_list;
+ const GtkTargetEntry *targets;
+ guint n_targets;
+
+ handler = E_ATTACHMENT_HANDLER (object);
+ drag_actions = e_attachment_handler_get_drag_actions (handler);
+ targets = e_attachment_handler_get_target_table (handler, &n_targets);
+
+ view = e_attachment_handler_get_view (handler);
+
+ target_list = e_attachment_view_get_target_list (view);
+ gtk_target_list_add_table (target_list, targets, n_targets);
+
+ e_attachment_view_add_drag_actions (view, drag_actions);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_attachment_handler_parent_class)->constructed (object);
+}
+
+static void
+e_attachment_handler_class_init (EAttachmentHandlerClass *class)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ g_type_class_add_private (class, sizeof (EAttachmentHandlerPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = attachment_handler_constructed;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_ATTACHMENT_VIEW;
+}
+
+static void
+e_attachment_handler_init (EAttachmentHandler *handler)
+{
+ handler->priv = E_ATTACHMENT_HANDLER_GET_PRIVATE (handler);
+}
+
+EAttachmentView *
+e_attachment_handler_get_view (EAttachmentHandler *handler)
+{
+ EExtensible *extensible;
+
+ /* This is purely a convenience function. */
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), NULL);
+
+ extensible = e_extension_get_extensible (E_EXTENSION (handler));
+
+ return E_ATTACHMENT_VIEW (extensible);
+}
+
+GdkDragAction
+e_attachment_handler_get_drag_actions (EAttachmentHandler *handler)
+{
+ EAttachmentHandlerClass *class;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), 0);
+
+ class = E_ATTACHMENT_HANDLER_GET_CLASS (handler);
+
+ if (class->get_drag_actions != NULL)
+ return class->get_drag_actions (handler);
+
+ return 0;
+}
+
+const GtkTargetEntry *
+e_attachment_handler_get_target_table (EAttachmentHandler *handler,
+ guint *n_targets)
+{
+ EAttachmentHandlerClass *class;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), NULL);
+
+ class = E_ATTACHMENT_HANDLER_GET_CLASS (handler);
+
+ if (class->get_target_table != NULL)
+ return class->get_target_table (handler, n_targets);
+
+ if (n_targets != NULL)
+ *n_targets = 0;
+
+ return NULL;
+}
diff --git a/e-util/e-attachment-handler.h b/e-util/e-attachment-handler.h
new file mode 100644
index 0000000000..086ba8ff6a
--- /dev/null
+++ b/e-util/e-attachment-handler.h
@@ -0,0 +1,84 @@
+/*
+ * e-attachment-handler.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ATTACHMENT_HANDLER_H
+#define E_ATTACHMENT_HANDLER_H
+
+#include <libebackend/libebackend.h>
+
+#include <e-util/e-attachment-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER \
+ (e_attachment_handler_get_type ())
+#define E_ATTACHMENT_HANDLER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandler))
+#define E_ATTACHMENT_HANDLER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerClass))
+#define E_IS_ATTACHMENT_HANDLER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER))
+#define E_IS_ATTACHMENT_HANDLER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER))
+#define E_ATTACHMENT_HANDLER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandler EAttachmentHandler;
+typedef struct _EAttachmentHandlerClass EAttachmentHandlerClass;
+typedef struct _EAttachmentHandlerPrivate EAttachmentHandlerPrivate;
+
+struct _EAttachmentHandler {
+ EExtension parent;
+ EAttachmentHandlerPrivate *priv;
+};
+
+struct _EAttachmentHandlerClass {
+ EExtensionClass parent_class;
+
+ GdkDragAction (*get_drag_actions) (EAttachmentHandler *handler);
+ const GtkTargetEntry *
+ (*get_target_table) (EAttachmentHandler *handler,
+ guint *n_targets);
+};
+
+GType e_attachment_handler_get_type (void);
+EAttachmentView *
+ e_attachment_handler_get_view (EAttachmentHandler *handler);
+GdkDragAction e_attachment_handler_get_drag_actions
+ (EAttachmentHandler *handler);
+const GtkTargetEntry *
+ e_attachment_handler_get_target_table
+ (EAttachmentHandler *handler,
+ guint *n_targets);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_H */
diff --git a/e-util/e-attachment-icon-view.c b/e-util/e-attachment-icon-view.c
new file mode 100644
index 0000000000..2be8009e8a
--- /dev/null
+++ b/e-util/e-attachment-icon-view.c
@@ -0,0 +1,570 @@
+/*
+ * e-attachment-icon-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-icon-view.h"
+
+#include <glib/gi18n.h>
+#include <libebackend/libebackend.h>
+
+#include "e-attachment.h"
+#include "e-attachment-store.h"
+#include "e-attachment-view.h"
+
+#define E_ATTACHMENT_ICON_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconViewPrivate))
+
+struct _EAttachmentIconViewPrivate {
+ EAttachmentViewPrivate view_priv;
+};
+
+enum {
+ PROP_0,
+ PROP_DRAGGING,
+ PROP_EDITABLE
+};
+
+static gint icon_size = GTK_ICON_SIZE_DIALOG;
+
+/* Forward Declarations */
+static void e_attachment_icon_view_interface_init
+ (EAttachmentViewInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EAttachmentIconView,
+ e_attachment_icon_view,
+ GTK_TYPE_ICON_VIEW,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_ATTACHMENT_VIEW,
+ e_attachment_icon_view_interface_init)
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_EXTENSIBLE, NULL))
+
+void
+e_attachment_icon_view_set_default_icon_size (gint size)
+{
+ icon_size = size;
+}
+
+static void
+attachment_icon_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_DRAGGING:
+ e_attachment_view_set_dragging (
+ E_ATTACHMENT_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_EDITABLE:
+ e_attachment_view_set_editable (
+ E_ATTACHMENT_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_icon_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_DRAGGING:
+ g_value_set_boolean (
+ value, e_attachment_view_get_dragging (
+ E_ATTACHMENT_VIEW (object)));
+ return;
+
+ case PROP_EDITABLE:
+ g_value_set_boolean (
+ value, e_attachment_view_get_editable (
+ E_ATTACHMENT_VIEW (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_icon_view_dispose (GObject *object)
+{
+ e_attachment_view_dispose (E_ATTACHMENT_VIEW (object));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_attachment_icon_view_parent_class)->dispose (object);
+}
+
+static void
+attachment_icon_view_finalize (GObject *object)
+{
+ e_attachment_view_finalize (E_ATTACHMENT_VIEW (object));
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_attachment_icon_view_parent_class)->finalize (object);
+}
+
+static void
+attachment_icon_view_constructed (GObject *object)
+{
+ GtkCellLayout *cell_layout;
+ GtkCellRenderer *renderer;
+
+ cell_layout = GTK_CELL_LAYOUT (object);
+
+ /* This needs to happen after constructor properties are set
+ * so that GtkCellLayout.get_area() returns something valid. */
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (renderer, "stock-size", icon_size, NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "gicon",
+ E_ATTACHMENT_STORE_COLUMN_ICON);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (
+ renderer, "alignment", PANGO_ALIGN_CENTER,
+ "wrap-mode", PANGO_WRAP_WORD, "wrap-width", 150,
+ "yalign", 0.0, NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "text",
+ E_ATTACHMENT_STORE_COLUMN_CAPTION);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Loading"), NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_LOADING);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Saving"), NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_SAVING);
+
+ e_extensible_load_extensions (E_EXTENSIBLE (object));
+}
+
+static gboolean
+attachment_icon_view_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (e_attachment_view_button_press_event (view, event))
+ return TRUE;
+
+ /* Chain up to parent's button_press_event() method. */
+ return GTK_WIDGET_CLASS (e_attachment_icon_view_parent_class)->
+ button_press_event (widget, event);
+}
+
+static gboolean
+attachment_icon_view_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (e_attachment_view_button_release_event (view, event))
+ return TRUE;
+
+ /* Chain up to parent's button_release_event() method. */
+ return GTK_WIDGET_CLASS (e_attachment_icon_view_parent_class)->
+ button_release_event (widget, event);
+}
+
+static gboolean
+attachment_icon_view_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (e_attachment_view_motion_notify_event (view, event))
+ return TRUE;
+
+ /* Chain up to parent's motion_notify_event() method. */
+ return GTK_WIDGET_CLASS (e_attachment_icon_view_parent_class)->
+ motion_notify_event (widget, event);
+}
+
+static gboolean
+attachment_icon_view_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (e_attachment_view_key_press_event (view, event))
+ return TRUE;
+
+ /* Chain up to parent's key_press_event() method. */
+ return GTK_WIDGET_CLASS (e_attachment_icon_view_parent_class)->
+ key_press_event (widget, event);
+}
+
+static void
+attachment_icon_view_drag_begin (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ /* Chain up to parent's drag_begin() method. */
+ GTK_WIDGET_CLASS (e_attachment_icon_view_parent_class)->
+ drag_begin (widget, context);
+
+ e_attachment_view_drag_begin (view, context);
+}
+
+static void
+attachment_icon_view_drag_end (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ /* Chain up to parent's drag_end() method. */
+ GTK_WIDGET_CLASS (e_attachment_icon_view_parent_class)->
+ drag_end (widget, context);
+
+ e_attachment_view_drag_end (view, context);
+}
+
+static void
+attachment_icon_view_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ e_attachment_view_drag_data_get (
+ view, context, selection, info, time);
+}
+
+static gboolean
+attachment_icon_view_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ return e_attachment_view_drag_motion (view, context, x, y, time);
+}
+
+static gboolean
+attachment_icon_view_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (!e_attachment_view_drag_drop (view, context, x, y, time))
+ return FALSE;
+
+ /* Chain up to parent's drag_drop() method. */
+ return GTK_WIDGET_CLASS (e_attachment_icon_view_parent_class)->
+ drag_drop (widget, context, x, y, time);
+}
+
+static void
+attachment_icon_view_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ e_attachment_view_drag_data_received (
+ view, context, x, y, selection, info, time);
+}
+
+static gboolean
+attachment_icon_view_popup_menu (GtkWidget *widget)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ e_attachment_view_show_popup_menu (view, NULL, NULL, NULL);
+
+ return TRUE;
+}
+
+static void
+attachment_icon_view_item_activated (GtkIconView *icon_view,
+ GtkTreePath *path)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (icon_view);
+
+ e_attachment_view_open_path (view, path, NULL);
+}
+
+static EAttachmentViewPrivate *
+attachment_icon_view_get_private (EAttachmentView *view)
+{
+ EAttachmentIconViewPrivate *priv;
+
+ priv = E_ATTACHMENT_ICON_VIEW_GET_PRIVATE (view);
+
+ return &priv->view_priv;
+}
+
+static EAttachmentStore *
+attachment_icon_view_get_store (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+ GtkTreeModel *model;
+
+ icon_view = GTK_ICON_VIEW (view);
+ model = gtk_icon_view_get_model (icon_view);
+
+ return E_ATTACHMENT_STORE (model);
+}
+
+static GtkTreePath *
+attachment_icon_view_get_path_at_pos (EAttachmentView *view,
+ gint x,
+ gint y)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ return gtk_icon_view_get_path_at_pos (icon_view, x, y);
+}
+
+static GList *
+attachment_icon_view_get_selected_paths (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ return gtk_icon_view_get_selected_items (icon_view);
+}
+
+static gboolean
+attachment_icon_view_path_is_selected (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ return gtk_icon_view_path_is_selected (icon_view, path);
+}
+
+static void
+attachment_icon_view_select_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_select_path (icon_view, path);
+}
+
+static void
+attachment_icon_view_unselect_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_unselect_path (icon_view, path);
+}
+
+static void
+attachment_icon_view_select_all (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_select_all (icon_view);
+}
+
+static void
+attachment_icon_view_unselect_all (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_unselect_all (icon_view);
+}
+
+static void
+attachment_icon_view_drag_source_set (EAttachmentView *view,
+ GdkModifierType start_button_mask,
+ const GtkTargetEntry *targets,
+ gint n_targets,
+ GdkDragAction actions)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_enable_model_drag_source (
+ icon_view, start_button_mask, targets, n_targets, actions);
+}
+
+static void
+attachment_icon_view_drag_dest_set (EAttachmentView *view,
+ const GtkTargetEntry *targets,
+ gint n_targets,
+ GdkDragAction actions)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_enable_model_drag_dest (
+ icon_view, targets, n_targets, actions);
+}
+
+static void
+attachment_icon_view_drag_source_unset (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_unset_model_drag_source (icon_view);
+}
+
+static void
+attachment_icon_view_drag_dest_unset (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_unset_model_drag_dest (icon_view);
+}
+
+static void
+e_attachment_icon_view_class_init (EAttachmentIconViewClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkIconViewClass *icon_view_class;
+
+ g_type_class_add_private (class, sizeof (EAttachmentViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_icon_view_set_property;
+ object_class->get_property = attachment_icon_view_get_property;
+ object_class->dispose = attachment_icon_view_dispose;
+ object_class->finalize = attachment_icon_view_finalize;
+ object_class->constructed = attachment_icon_view_constructed;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->button_press_event = attachment_icon_view_button_press_event;
+ widget_class->button_release_event = attachment_icon_view_button_release_event;
+ widget_class->motion_notify_event = attachment_icon_view_motion_notify_event;
+ widget_class->key_press_event = attachment_icon_view_key_press_event;
+ widget_class->drag_begin = attachment_icon_view_drag_begin;
+ widget_class->drag_end = attachment_icon_view_drag_end;
+ widget_class->drag_data_get = attachment_icon_view_drag_data_get;
+ widget_class->drag_motion = attachment_icon_view_drag_motion;
+ widget_class->drag_drop = attachment_icon_view_drag_drop;
+ widget_class->drag_data_received = attachment_icon_view_drag_data_received;
+ widget_class->popup_menu = attachment_icon_view_popup_menu;
+
+ icon_view_class = GTK_ICON_VIEW_CLASS (class);
+ icon_view_class->item_activated = attachment_icon_view_item_activated;
+
+ g_object_class_override_property (
+ object_class, PROP_DRAGGING, "dragging");
+
+ g_object_class_override_property (
+ object_class, PROP_EDITABLE, "editable");
+}
+
+static void
+e_attachment_icon_view_init (EAttachmentIconView *icon_view)
+{
+ icon_view->priv = E_ATTACHMENT_ICON_VIEW_GET_PRIVATE (icon_view);
+
+ e_attachment_view_init (E_ATTACHMENT_VIEW (icon_view));
+
+ gtk_icon_view_set_selection_mode (
+ GTK_ICON_VIEW (icon_view), GTK_SELECTION_MULTIPLE);
+}
+
+static void
+e_attachment_icon_view_interface_init (EAttachmentViewInterface *interface)
+{
+ interface->get_private = attachment_icon_view_get_private;
+ interface->get_store = attachment_icon_view_get_store;
+
+ interface->get_path_at_pos = attachment_icon_view_get_path_at_pos;
+ interface->get_selected_paths = attachment_icon_view_get_selected_paths;
+ interface->path_is_selected = attachment_icon_view_path_is_selected;
+ interface->select_path = attachment_icon_view_select_path;
+ interface->unselect_path = attachment_icon_view_unselect_path;
+ interface->select_all = attachment_icon_view_select_all;
+ interface->unselect_all = attachment_icon_view_unselect_all;
+
+ interface->drag_source_set = attachment_icon_view_drag_source_set;
+ interface->drag_dest_set = attachment_icon_view_drag_dest_set;
+ interface->drag_source_unset = attachment_icon_view_drag_source_unset;
+ interface->drag_dest_unset = attachment_icon_view_drag_dest_unset;
+}
+
+GtkWidget *
+e_attachment_icon_view_new (void)
+{
+ return g_object_new (E_TYPE_ATTACHMENT_ICON_VIEW, NULL);
+}
diff --git a/e-util/e-attachment-icon-view.h b/e-util/e-attachment-icon-view.h
new file mode 100644
index 0000000000..bd3d2109db
--- /dev/null
+++ b/e-util/e-attachment-icon-view.h
@@ -0,0 +1,71 @@
+/*
+ * e-attachment-icon-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ATTACHMENT_ICON_VIEW_H
+#define E_ATTACHMENT_ICON_VIEW_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_ICON_VIEW \
+ (e_attachment_icon_view_get_type ())
+#define E_ATTACHMENT_ICON_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconView))
+#define E_ATTACHMENT_ICON_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconView))
+#define E_IS_ATTACHMENT_ICON_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_ICON_VIEW))
+#define E_IS_ATTACHMENT_ICON_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_ICON_VIEW))
+#define E_ATTACHMENT_ICON_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_ICON_VIEW))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentIconView EAttachmentIconView;
+typedef struct _EAttachmentIconViewClass EAttachmentIconViewClass;
+typedef struct _EAttachmentIconViewPrivate EAttachmentIconViewPrivate;
+
+struct _EAttachmentIconView {
+ GtkIconView parent;
+ EAttachmentIconViewPrivate *priv;
+};
+
+struct _EAttachmentIconViewClass {
+ GtkIconViewClass parent_class;
+};
+
+GType e_attachment_icon_view_get_type (void);
+GtkWidget * e_attachment_icon_view_new (void);
+void e_attachment_icon_view_set_default_icon_size
+ (gint size);
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_ICON_VIEW_H */
diff --git a/e-util/e-attachment-paned.c b/e-util/e-attachment-paned.c
new file mode 100644
index 0000000000..a3c4efb187
--- /dev/null
+++ b/e-util/e-attachment-paned.c
@@ -0,0 +1,904 @@
+/*
+ * e-attachment-paned.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-paned.h"
+
+#include <glib/gi18n.h>
+
+#include "e-attachment-view.h"
+#include "e-attachment-store.h"
+#include "e-attachment-icon-view.h"
+#include "e-attachment-tree-view.h"
+
+#define E_ATTACHMENT_PANED_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedPrivate))
+
+#define NUM_VIEWS 2
+
+/* Initial height of the lower pane. */
+static gint initial_height = 150;
+
+struct _EAttachmentPanedPrivate {
+ GtkTreeModel *model;
+ GtkWidget *expander;
+ GtkWidget *notebook;
+ GtkWidget *combo_box;
+ GtkWidget *controls_container;
+ GtkWidget *icon_view;
+ GtkWidget *tree_view;
+ GtkWidget *show_hide_label;
+ GtkWidget *status_icon;
+ GtkWidget *status_label;
+ GtkWidget *content_area;
+
+ gint active_view;
+ gboolean expanded;
+ gboolean resize_toplevel;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVE_VIEW,
+ PROP_DRAGGING,
+ PROP_EDITABLE,
+ PROP_EXPANDED,
+ PROP_RESIZE_TOPLEVEL
+};
+
+/* Forward Declarations */
+static void e_attachment_paned_interface_init
+ (EAttachmentViewInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EAttachmentPaned,
+ e_attachment_paned,
+ GTK_TYPE_VPANED,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_ATTACHMENT_VIEW,
+ e_attachment_paned_interface_init))
+
+void
+e_attachment_paned_set_default_height (gint height)
+{
+ initial_height = height;
+}
+
+static void
+attachment_paned_notify_cb (EAttachmentPaned *paned,
+ GParamSpec *pspec,
+ GtkExpander *expander)
+{
+ GtkAllocation toplevel_allocation;
+ GtkWidget *toplevel;
+ GtkWidget *child;
+ GtkLabel *label;
+ const gchar *text;
+
+ label = GTK_LABEL (paned->priv->show_hide_label);
+
+ /* Update the expander label. */
+ if (gtk_expander_get_expanded (expander))
+ text = _("Hide Attachment _Bar");
+ else
+ text = _("Show Attachment _Bar");
+
+ gtk_label_set_text_with_mnemonic (label, text);
+
+ /* Resize the top-level window if required conditions are met.
+ * This is based on gtk_expander_resize_toplevel(), but adapted
+ * to the fact our GtkExpander has no direct child widget. */
+
+ if (!e_attachment_paned_get_resize_toplevel (paned))
+ return;
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (paned)))
+ return;
+
+ child = gtk_paned_get_child2 (GTK_PANED (paned));
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned));
+
+ if (toplevel == NULL)
+ return;
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (toplevel)))
+ return;
+
+ gtk_widget_get_allocation (toplevel, &toplevel_allocation);
+
+ if (gtk_expander_get_expanded (expander)) {
+ GtkRequisition child_requisition;
+
+ gtk_widget_get_preferred_size (
+ child, &child_requisition, NULL);
+
+ toplevel_allocation.height += child_requisition.height;
+ } else {
+ GtkAllocation child_allocation;
+
+ gtk_widget_get_allocation (child, &child_allocation);
+
+ toplevel_allocation.height -= child_allocation.height;
+ }
+
+ gtk_window_resize (
+ GTK_WINDOW (toplevel),
+ toplevel_allocation.width,
+ toplevel_allocation.height);
+}
+
+static void
+attachment_paned_update_status (EAttachmentPaned *paned)
+{
+ EAttachmentView *view;
+ EAttachmentStore *store;
+ GtkExpander *expander;
+ GtkLabel *label;
+ guint num_attachments;
+ guint64 total_size;
+ gchar *display_size;
+ gchar *markup;
+
+ view = E_ATTACHMENT_VIEW (paned);
+ store = e_attachment_view_get_store (view);
+ expander = GTK_EXPANDER (paned->priv->expander);
+ label = GTK_LABEL (paned->priv->status_label);
+
+ num_attachments = e_attachment_store_get_num_attachments (store);
+ total_size = e_attachment_store_get_total_size (store);
+ display_size = g_format_size (total_size);
+
+ if (total_size > 0)
+ markup = g_strdup_printf (
+ "<b>%d</b> %s (%s)", num_attachments, ngettext (
+ "Attachment", "Attachments", num_attachments),
+ display_size);
+ else
+ markup = g_strdup_printf (
+ "<b>%d</b> %s", num_attachments, ngettext (
+ "Attachment", "Attachments", num_attachments));
+ gtk_label_set_markup (label, markup);
+ g_free (markup);
+
+ g_free (display_size);
+
+ if (num_attachments > 0) {
+ gtk_widget_show (paned->priv->status_icon);
+ gtk_widget_show (paned->priv->status_label);
+ gtk_expander_set_expanded (expander, TRUE);
+ } else {
+ gtk_widget_hide (paned->priv->status_icon);
+ gtk_widget_hide (paned->priv->status_label);
+ gtk_expander_set_expanded (expander, FALSE);
+ }
+}
+
+static void
+attachment_paned_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVE_VIEW:
+ e_attachment_paned_set_active_view (
+ E_ATTACHMENT_PANED (object),
+ g_value_get_int (value));
+ return;
+
+ case PROP_DRAGGING:
+ e_attachment_view_set_dragging (
+ E_ATTACHMENT_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_EDITABLE:
+ e_attachment_view_set_editable (
+ E_ATTACHMENT_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_EXPANDED:
+ e_attachment_paned_set_expanded (
+ E_ATTACHMENT_PANED (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_RESIZE_TOPLEVEL:
+ e_attachment_paned_set_resize_toplevel (
+ E_ATTACHMENT_PANED (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_paned_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVE_VIEW:
+ g_value_set_int (
+ value,
+ e_attachment_paned_get_active_view (
+ E_ATTACHMENT_PANED (object)));
+ return;
+
+ case PROP_DRAGGING:
+ g_value_set_boolean (
+ value,
+ e_attachment_view_get_dragging (
+ E_ATTACHMENT_VIEW (object)));
+ return;
+
+ case PROP_EDITABLE:
+ g_value_set_boolean (
+ value,
+ e_attachment_view_get_editable (
+ E_ATTACHMENT_VIEW (object)));
+ return;
+
+ case PROP_EXPANDED:
+ g_value_set_boolean (
+ value,
+ e_attachment_paned_get_expanded (
+ E_ATTACHMENT_PANED (object)));
+ return;
+
+ case PROP_RESIZE_TOPLEVEL:
+ g_value_set_boolean (
+ value,
+ e_attachment_paned_get_resize_toplevel (
+ E_ATTACHMENT_PANED (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_paned_dispose (GObject *object)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (object);
+
+ if (priv->model != NULL) {
+ e_attachment_store_remove_all (E_ATTACHMENT_STORE (priv->model));
+ g_object_unref (priv->model);
+ priv->model = NULL;
+ }
+
+ if (priv->expander != NULL) {
+ g_object_unref (priv->expander);
+ priv->expander = NULL;
+ }
+
+ if (priv->notebook != NULL) {
+ g_object_unref (priv->notebook);
+ priv->notebook = NULL;
+ }
+
+ if (priv->combo_box != NULL) {
+ g_object_unref (priv->combo_box);
+ priv->combo_box = NULL;
+ }
+
+ if (priv->icon_view != NULL) {
+ g_object_unref (priv->icon_view);
+ priv->icon_view = NULL;
+ }
+
+ if (priv->tree_view != NULL) {
+ g_object_unref (priv->tree_view);
+ priv->tree_view = NULL;
+ }
+
+ if (priv->show_hide_label != NULL) {
+ g_object_unref (priv->show_hide_label);
+ priv->show_hide_label = NULL;
+ }
+
+ if (priv->status_icon != NULL) {
+ g_object_unref (priv->status_icon);
+ priv->status_icon = NULL;
+ }
+
+ if (priv->status_label != NULL) {
+ g_object_unref (priv->status_label);
+ priv->status_label = NULL;
+ }
+
+ if (priv->content_area != NULL) {
+ g_object_unref (priv->content_area);
+ priv->content_area = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_attachment_paned_parent_class)->dispose (object);
+}
+
+static void
+attachment_paned_constructed (GObject *object)
+{
+ EAttachmentPanedPrivate *priv;
+ GSettings *settings;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (object);
+
+ settings = g_settings_new ("org.gnome.evolution.shell");
+
+ /* Set up property-to-property bindings. */
+
+ g_object_bind_property (
+ object, "active-view",
+ priv->combo_box, "active",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "active-view",
+ priv->notebook, "page",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "dragging",
+ priv->icon_view, "dragging",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "dragging",
+ priv->tree_view, "dragging",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "editable",
+ priv->icon_view, "editable",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "editable",
+ priv->tree_view, "editable",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "expanded",
+ priv->expander, "expanded",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "expanded",
+ priv->combo_box, "sensitive",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ g_object_bind_property (
+ object, "expanded",
+ priv->notebook, "visible",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ /* Set up property-to-GSettings bindings. */
+ g_settings_bind (
+ settings, "attachment-view",
+ object, "active-view",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_object_unref (settings);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_attachment_paned_parent_class)->constructed (object);
+}
+
+static EAttachmentViewPrivate *
+attachment_paned_get_private (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_get_private (view);
+}
+
+static EAttachmentStore *
+attachment_paned_get_store (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_get_store (view);
+}
+
+static GtkTreePath *
+attachment_paned_get_path_at_pos (EAttachmentView *view,
+ gint x,
+ gint y)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_get_path_at_pos (view, x, y);
+}
+
+static GList *
+attachment_paned_get_selected_paths (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_get_selected_paths (view);
+}
+
+static gboolean
+attachment_paned_path_is_selected (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_path_is_selected (view, path);
+}
+
+static void
+attachment_paned_select_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_select_path (view, path);
+}
+
+static void
+attachment_paned_unselect_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_unselect_path (view, path);
+}
+
+static void
+attachment_paned_select_all (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_select_all (view);
+}
+
+static void
+attachment_paned_unselect_all (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_unselect_all (view);
+}
+
+static void
+attachment_paned_update_actions (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_update_actions (view);
+}
+
+static void
+e_attachment_paned_class_init (EAttachmentPanedClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EAttachmentPanedPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_paned_set_property;
+ object_class->get_property = attachment_paned_get_property;
+ object_class->dispose = attachment_paned_dispose;
+ object_class->constructed = attachment_paned_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTIVE_VIEW,
+ g_param_spec_int (
+ "active-view",
+ "Active View",
+ NULL,
+ 0,
+ NUM_VIEWS,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_override_property (
+ object_class, PROP_DRAGGING, "dragging");
+
+ g_object_class_override_property (
+ object_class, PROP_EDITABLE, "editable");
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EXPANDED,
+ g_param_spec_boolean (
+ "expanded",
+ "Expanded",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_RESIZE_TOPLEVEL,
+ g_param_spec_boolean (
+ "resize-toplevel",
+ "Resize-Toplevel",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_attachment_paned_init (EAttachmentPaned *paned)
+{
+ EAttachmentView *view;
+ GtkSizeGroup *size_group;
+ GtkWidget *container;
+ GtkWidget *widget;
+ GtkAction *action;
+
+ paned->priv = E_ATTACHMENT_PANED_GET_PRIVATE (paned);
+ paned->priv->model = e_attachment_store_new ();
+
+ /* Keep the expander label and combo box the same height. */
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
+ /* Construct the Attachment Views */
+
+ container = GTK_WIDGET (paned);
+
+ widget = gtk_notebook_new ();
+ gtk_widget_set_size_request (widget, -1, initial_height);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
+ gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE);
+ paned->priv->notebook = g_object_ref (widget);
+ gtk_widget_hide (widget);
+
+ container = paned->priv->notebook;
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_attachment_icon_view_new ();
+ gtk_widget_set_can_focus (widget, TRUE);
+ gtk_icon_view_set_model (GTK_ICON_VIEW (widget), paned->priv->model);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ paned->priv->icon_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = paned->priv->notebook;
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_attachment_tree_view_new ();
+ gtk_widget_set_can_focus (widget, TRUE);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (widget), paned->priv->model);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ paned->priv->tree_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Construct the Controls */
+
+ container = GTK_WIDGET (paned);
+
+ widget = gtk_vbox_new (FALSE, 6);
+ gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE);
+ paned->priv->content_area = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ paned->priv->controls_container = widget;
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_expander_new (NULL);
+ gtk_expander_set_spacing (GTK_EXPANDER (widget), 0);
+ gtk_expander_set_label_fill (GTK_EXPANDER (widget), TRUE);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ paned->priv->expander = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* The "Add Attachment" button proxies the "add" action from
+ * one of the two attachment views. Doesn't matter which. */
+ widget = gtk_button_new ();
+ view = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+ action = e_attachment_view_get_action (view, "add");
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_combo_box_text_new ();
+ gtk_size_group_add_widget (size_group, widget);
+ gtk_combo_box_text_append_text (
+ GTK_COMBO_BOX_TEXT (widget), _("Icon View"));
+ gtk_combo_box_text_append_text (
+ GTK_COMBO_BOX_TEXT (widget), _("List View"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ paned->priv->combo_box = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = paned->priv->expander;
+
+ widget = gtk_hbox_new (FALSE, 6);
+ gtk_size_group_add_widget (size_group, widget);
+ gtk_expander_set_label_widget (GTK_EXPANDER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("Show Attachment _Bar"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ paned->priv->show_hide_label = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_alignment_new (0.5, 0.5, 0.0, 1.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_hbox_new (FALSE, 6);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_image_new_from_icon_name (
+ "mail-attachment", GTK_ICON_SIZE_MENU);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ paned->priv->status_icon = g_object_ref (widget);
+ gtk_widget_hide (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ paned->priv->status_label = g_object_ref (widget);
+ gtk_widget_hide (widget);
+
+ g_signal_connect_swapped (
+ paned->priv->expander, "notify::expanded",
+ G_CALLBACK (attachment_paned_notify_cb), paned);
+
+ g_signal_connect_swapped (
+ paned->priv->model, "notify::num-attachments",
+ G_CALLBACK (attachment_paned_update_status), paned);
+
+ g_signal_connect_swapped (
+ paned->priv->model, "notify::total-size",
+ G_CALLBACK (attachment_paned_update_status), paned);
+
+ g_object_unref (size_group);
+}
+
+static void
+e_attachment_paned_interface_init (EAttachmentViewInterface *interface)
+{
+ interface->get_private = attachment_paned_get_private;
+ interface->get_store = attachment_paned_get_store;
+ interface->get_path_at_pos = attachment_paned_get_path_at_pos;
+ interface->get_selected_paths = attachment_paned_get_selected_paths;
+ interface->path_is_selected = attachment_paned_path_is_selected;
+ interface->select_path = attachment_paned_select_path;
+ interface->unselect_path = attachment_paned_unselect_path;
+ interface->select_all = attachment_paned_select_all;
+ interface->unselect_all = attachment_paned_unselect_all;
+ interface->update_actions = attachment_paned_update_actions;
+}
+
+GtkWidget *
+e_attachment_paned_new (void)
+{
+ return g_object_new (E_TYPE_ATTACHMENT_PANED, NULL);
+}
+
+GtkWidget *
+e_attachment_paned_get_content_area (EAttachmentPaned *paned)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), NULL);
+
+ return paned->priv->content_area;
+}
+
+gint
+e_attachment_paned_get_active_view (EAttachmentPaned *paned)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), 0);
+
+ return paned->priv->active_view;
+}
+
+void
+e_attachment_paned_set_active_view (EAttachmentPaned *paned,
+ gint active_view)
+{
+ EAttachmentView *source;
+ EAttachmentView *target;
+
+ g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+ g_return_if_fail (active_view >= 0 && active_view < NUM_VIEWS);
+
+ if (active_view == paned->priv->active_view)
+ return;
+
+ paned->priv->active_view = active_view;
+
+ /* Synchronize the item selection of the view we're
+ * switching TO with the view we're switching FROM. */
+ if (active_view == 0) {
+ /* from tree view to icon view */
+ source = E_ATTACHMENT_VIEW (paned->priv->tree_view);
+ target = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+ } else {
+ /* from icon view to tree view */
+ source = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+ target = E_ATTACHMENT_VIEW (paned->priv->tree_view);
+ }
+
+ e_attachment_view_sync_selection (source, target);
+
+ g_object_notify (G_OBJECT (paned), "active-view");
+}
+
+gboolean
+e_attachment_paned_get_expanded (EAttachmentPaned *paned)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), FALSE);
+
+ return paned->priv->expanded;
+}
+
+void
+e_attachment_paned_set_expanded (EAttachmentPaned *paned,
+ gboolean expanded)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+
+ if (paned->priv->expanded == expanded)
+ return;
+
+ paned->priv->expanded = expanded;
+
+ g_object_notify (G_OBJECT (paned), "expanded");
+}
+
+gboolean
+e_attachment_paned_get_resize_toplevel (EAttachmentPaned *paned)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), FALSE);
+
+ return paned->priv->resize_toplevel;
+}
+
+void
+e_attachment_paned_set_resize_toplevel (EAttachmentPaned *paned,
+ gboolean resize_toplevel)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+
+ if (paned->priv->resize_toplevel == resize_toplevel)
+ return;
+
+ paned->priv->resize_toplevel = resize_toplevel;
+
+ g_object_notify (G_OBJECT (paned), "resize-toplevel");
+}
+
+void
+e_attachment_paned_drag_data_received (EAttachmentPaned *paned,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+
+ /* XXX Dirty hack for forwarding drop events. */
+ g_signal_emit_by_name (
+ paned->priv->icon_view, "drag-data-received",
+ context, x, y, selection, info, time);
+}
+
+GtkWidget *
+e_attachment_paned_get_controls_container (EAttachmentPaned *paned)
+{
+ return paned->priv->controls_container;
+}
+
+GtkWidget *
+e_attachment_paned_get_view_combo (EAttachmentPaned *paned)
+{
+ return paned->priv->combo_box;
+}
+
diff --git a/e-util/e-attachment-paned.h b/e-util/e-attachment-paned.h
new file mode 100644
index 0000000000..af44cd6d67
--- /dev/null
+++ b/e-util/e-attachment-paned.h
@@ -0,0 +1,99 @@
+/*
+ * e-attachment-paned.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ATTACHMENT_PANED_H
+#define E_ATTACHMENT_PANED_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_PANED \
+ (e_attachment_paned_get_type ())
+#define E_ATTACHMENT_PANED(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPaned))
+#define E_ATTACHMENT_PANED_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedClass))
+#define E_IS_ATTACHMENT_PANED(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_PANED))
+#define E_IS_ATTACHMENT_PANED_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_PANED))
+#define E_ATTACHMENT_PANED_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentPaned EAttachmentPaned;
+typedef struct _EAttachmentPanedClass EAttachmentPanedClass;
+typedef struct _EAttachmentPanedPrivate EAttachmentPanedPrivate;
+
+struct _EAttachmentPaned {
+ GtkVPaned parent;
+ EAttachmentPanedPrivate *priv;
+};
+
+struct _EAttachmentPanedClass {
+ GtkVPanedClass parent_class;
+};
+
+GType e_attachment_paned_get_type (void);
+GtkWidget * e_attachment_paned_new (void);
+GtkWidget * e_attachment_paned_get_content_area
+ (EAttachmentPaned *paned);
+gint e_attachment_paned_get_active_view
+ (EAttachmentPaned *paned);
+void e_attachment_paned_set_active_view
+ (EAttachmentPaned *paned,
+ gint active_view);
+gboolean e_attachment_paned_get_expanded (EAttachmentPaned *paned);
+void e_attachment_paned_set_expanded (EAttachmentPaned *paned,
+ gboolean expanded);
+gboolean e_attachment_paned_get_resize_toplevel
+ (EAttachmentPaned *paned);
+void e_attachment_paned_set_resize_toplevel
+ (EAttachmentPaned *paned,
+ gboolean resize_toplevel);
+void e_attachment_paned_drag_data_received
+ (EAttachmentPaned *paned,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+GtkWidget * e_attachment_paned_get_controls_container
+ (EAttachmentPaned *paned);
+GtkWidget * e_attachment_paned_get_view_combo
+ (EAttachmentPaned *paned);
+void e_attachment_paned_set_default_height
+ (gint height);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_PANED_H */
diff --git a/e-util/e-attachment-store.c b/e-util/e-attachment-store.c
new file mode 100644
index 0000000000..f434f5e81c
--- /dev/null
+++ b/e-util/e-attachment-store.c
@@ -0,0 +1,1280 @@
+/*
+ * e-attachment-store.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-attachment-store.h"
+
+#include <errno.h>
+#include <glib/gi18n.h>
+
+#include "e-mktemp.h"
+
+#define E_ATTACHMENT_STORE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStorePrivate))
+
+struct _EAttachmentStorePrivate {
+ GHashTable *attachment_index;
+
+ guint ignore_row_changed : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_NUM_ATTACHMENTS,
+ PROP_NUM_LOADING,
+ PROP_TOTAL_SIZE
+};
+
+G_DEFINE_TYPE (
+ EAttachmentStore,
+ e_attachment_store,
+ GTK_TYPE_LIST_STORE)
+
+static void
+attachment_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_NUM_ATTACHMENTS:
+ g_value_set_uint (
+ value,
+ e_attachment_store_get_num_attachments (
+ E_ATTACHMENT_STORE (object)));
+ return;
+
+ case PROP_NUM_LOADING:
+ g_value_set_uint (
+ value,
+ e_attachment_store_get_num_loading (
+ E_ATTACHMENT_STORE (object)));
+ return;
+
+ case PROP_TOTAL_SIZE:
+ g_value_set_uint64 (
+ value,
+ e_attachment_store_get_total_size (
+ E_ATTACHMENT_STORE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_store_dispose (GObject *object)
+{
+ e_attachment_store_remove_all (E_ATTACHMENT_STORE (object));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_attachment_store_parent_class)->dispose (object);
+}
+
+static void
+attachment_store_finalize (GObject *object)
+{
+ EAttachmentStorePrivate *priv;
+
+ priv = E_ATTACHMENT_STORE_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->attachment_index);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_attachment_store_parent_class)->finalize (object);
+}
+
+static void
+e_attachment_store_class_init (EAttachmentStoreClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EAttachmentStorePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->get_property = attachment_store_get_property;
+ object_class->dispose = attachment_store_dispose;
+ object_class->finalize = attachment_store_finalize;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_NUM_ATTACHMENTS,
+ g_param_spec_uint (
+ "num-attachments",
+ "Num Attachments",
+ NULL,
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_NUM_LOADING,
+ g_param_spec_uint (
+ "num-loading",
+ "Num Loading",
+ NULL,
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_TOTAL_SIZE,
+ g_param_spec_uint64 (
+ "total-size",
+ "Total Size",
+ NULL,
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_READABLE));
+}
+
+static void
+e_attachment_store_init (EAttachmentStore *store)
+{
+ GType types[E_ATTACHMENT_STORE_NUM_COLUMNS];
+ GHashTable *attachment_index;
+ gint column = 0;
+
+ attachment_index = g_hash_table_new_full (
+ g_direct_hash, g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) gtk_tree_row_reference_free);
+
+ store->priv = E_ATTACHMENT_STORE_GET_PRIVATE (store);
+ store->priv->attachment_index = attachment_index;
+
+ types[column++] = E_TYPE_ATTACHMENT; /* COLUMN_ATTACHMENT */
+ types[column++] = G_TYPE_STRING; /* COLUMN_CAPTION */
+ types[column++] = G_TYPE_STRING; /* COLUMN_CONTENT_TYPE */
+ types[column++] = G_TYPE_STRING; /* COLUMN_DESCRIPTION */
+ types[column++] = G_TYPE_ICON; /* COLUMN_ICON */
+ types[column++] = G_TYPE_BOOLEAN; /* COLUMN_LOADING */
+ types[column++] = G_TYPE_INT; /* COLUMN_PERCENT */
+ types[column++] = G_TYPE_BOOLEAN; /* COLUMN_SAVING */
+ types[column++] = G_TYPE_UINT64; /* COLUMN_SIZE */
+
+ g_assert (column == E_ATTACHMENT_STORE_NUM_COLUMNS);
+
+ gtk_list_store_set_column_types (
+ GTK_LIST_STORE (store), G_N_ELEMENTS (types), types);
+}
+
+GtkTreeModel *
+e_attachment_store_new (void)
+{
+ return g_object_new (E_TYPE_ATTACHMENT_STORE, NULL);
+}
+
+void
+e_attachment_store_add_attachment (EAttachmentStore *store,
+ EAttachment *attachment)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ gtk_list_store_append (GTK_LIST_STORE (store), &iter);
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (store), &iter,
+ E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, attachment, -1);
+
+ model = GTK_TREE_MODEL (store);
+ path = gtk_tree_model_get_path (model, &iter);
+ reference = gtk_tree_row_reference_new (model, path);
+ gtk_tree_path_free (path);
+
+ g_hash_table_insert (
+ store->priv->attachment_index,
+ g_object_ref (attachment), reference);
+
+ /* This lets the attachment tell us when to update. */
+ e_attachment_set_reference (attachment, reference);
+
+ g_object_freeze_notify (G_OBJECT (store));
+ g_object_notify (G_OBJECT (store), "num-attachments");
+ g_object_notify (G_OBJECT (store), "total-size");
+ g_object_thaw_notify (G_OBJECT (store));
+}
+
+gboolean
+e_attachment_store_remove_attachment (EAttachmentStore *store,
+ EAttachment *attachment)
+{
+ GtkTreeRowReference *reference;
+ GHashTable *hash_table;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+ hash_table = store->priv->attachment_index;
+ reference = g_hash_table_lookup (hash_table, attachment);
+
+ if (reference == NULL)
+ return FALSE;
+
+ if (!gtk_tree_row_reference_valid (reference)) {
+ g_hash_table_remove (hash_table, attachment);
+ return FALSE;
+ }
+
+ e_attachment_cancel (attachment);
+ e_attachment_set_reference (attachment, NULL);
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
+ g_hash_table_remove (hash_table, attachment);
+
+ g_object_freeze_notify (G_OBJECT (store));
+ g_object_notify (G_OBJECT (store), "num-attachments");
+ g_object_notify (G_OBJECT (store), "total-size");