1
0
Fork 0
mirror of https://gitlab.gnome.org/GNOME/calls.git synced 2024-05-12 16:19:34 +00:00

Compare commits

...

82 commits

Author SHA1 Message Date
Anton Lazarev b5577d0005 sip-account-widget: Remove set_password_visibility
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:29 -07:00
Anton Lazarev b2e3e8445d calls-contacts-box: Migrate to GListModel
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:29 -07:00
Anton Lazarev 27e6b7a9db treewide: Remove requires glade-isms from ui definitions
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:29 -07:00
Anton Lazarev 1e862ffc67 sip-account-widget: Use GtkPasswordEntry
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:29 -07:00
Anton Lazarev 7cf7fc02d6 build: Specify required GTK version
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:29 -07:00
Anton Lazarev 933655e8e0 new-call-box: Expand to fill horizontal width
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev 51e74ef595 treewide: Ensure memory is not disposed multiple times
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev 8a464224a3 contacts-box: Use AdwToolbarView with search as top bar
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev 9fb5a11c69 contacts-box: Use AdwStatusPage for the placeholder
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev a3a193b77f main-window: Replace AdwViewSwitcherTitle
https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-to-breakpoints.html#replace-adwviewswitchertitle

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev 001d599495 treewide: Parent from AdwBin where possible
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev 4fc1374fe8 call-window: Remove superfluous GtkViewport
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev 52b4aa7769 call-record-row: Fix layout
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev eef5b33ad4 call-record-row: Improve event handling
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev caecf10cc1 treewide: Remove visible=True on adwaita widgets
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev 8ea553bd0c call-selector-item: Remove misc unavailable properties
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev e550d31b60 calls-main-window: Widgets are visible by default
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev afc01b1146 calls-application: subclass AdwApplication
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev b7789a59d1 main-window: Set menu-model directly on the GtkMenuButton
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev 543a55d741 contacts-box: Use title-1 instead of large-title
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:28 -07:00
Anton Lazarev fb93535806 treewide: Use GtkButton:icon-name instead of manual GtkImage child
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 58779b57a3 treewide: image-button style class is automatically handled by GTK
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 87d357729c call-window: receives_default is already true for buttons
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 229bf6f722 treewide: Remove unused placeholder elements
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev c9dc3ceffd treewide: Replace "content" style class with "boxed-list"
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev b83339980b treewide: Use AdwToolbarView for all windows with header bars
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 76697050e8 calls-in-app-notification: migrate to AdwToastOverlay
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 915b855ec8 call-record-row: Accessible role changes
AtkObject has been removed

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev d6918cc946 sip-account-widget: move GtkSizeGroup out of child
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 861ea19a62 calls-account-overview: Use content property for AdwWindow
https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-libhandy-1-4-to-libadwaita.html#adapt-to-gtkcontainer-removal

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev a2cb767bdc history-box: expand to fill available horizontal space
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 2ee3f347ce main-window: Use menu model for app menu popover
https://docs.gtk.org/gtk4/migrating-3to4.html#gtkmenu-gtkmenubar-and-gtkmenuitem-are-gone

Unclear why the invisible entries for `Keyboard shortcuts` and `Help`
are there. I've left them ported but commented out for now.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev b32faf39f8 main-window: Replace vbox internal-child with content_area
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 732388303a main-window: Stop setting ::has-default
https://docs.gtk.org/gtk4/migrating-3to4.html#stop-setting-has-default-and-has-focus-in-ui-files

Oddly, there were previously 2 different default widgets? I just chose
the first one.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 8b57a05836 call-window: Remove no-show-all property
https://docs.gtk.org/gtk4/migrating-3to4.html#widgets-are-now-visible-by-default

Not sure why it was there in the first place; it was never unset.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:27 -07:00
Anton Lazarev 36f19d5e9f treewide: Remove GtkWindow::hide-titlebar-when-maximized
92b0d2e8ea

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev a21b2b7d4a treewide: Replace gtk_widget_hide_on_delete with hide-on-close property
The `delete-event` signal was removed in GTK4.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev ef0b5a9ff8 treewide: Adapt to GtkHeaderBar API changes
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkheaderbar-and-gtkactionbar-api-changes

- `show-close-button` replaced with `show_title_buttons` wherever
  applicable, respecting new default value of `TRUE`
- `title` removed and managed by setting window title if needed
    - NOTE: I'm not 100% sure if the title logic here matches the GTK3
      version. I also just removed the SIP account widget titles
      altogether, since it's I couldn't figure out where the widget gets
      placed.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev a236339809 new-call-box: Stop using GtkButton's image-related API
https://docs.gtk.org/gtk4/migrating-3to4.html#stop-using-gtkbuttons-image-related-api

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev 7c89bbab3d treewide: remove <packing> from Adwaita widgets
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev a656776b19 treewide: Dispose and layout manager implementations
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkcontainer-removal

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev e2a5f04fa0 stylesheet: Don't use -gtk-outline-...-radius
https://docs.gtk.org/gtk4/migrating-3to4.html#dont-use-gtk-outline-radius-in-your-css

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev 1b567131b7 main-window: Adapt to View Switcher API Changes
https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-libhandy-1-4-to-libadwaita.html#adapt-to-view-switcher-api-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev b73016ac8c main-window: Stop using GtkContainer::border-width
https://docs.gtk.org/gtk4/migrating-3to4.html#stop-using-gtkcontainerborder-width

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev fd9378267d ui: gtk4-builder-tool simplify --3to4 --replace *.ui
plus some manual tweaks to restore True/False instead of 1/0

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev 8aea5498d7 calls-record-store: GtkApplicationWindow is not a GListModel
I'm not sure how this worked in GTK3, but it doesn't anymore. The list
model is now composed with the record store instead of inherited.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev 0a3ce1d701 calls-new-call-box: gtk_widget_get_toplevel has been removed
https://docs.gtk.org/gtk4/migrating-3to4.html#gtk_widget_get_toplevel-has-been-removed

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev 5abfae254a calls-call-selector-item: Adapt to GtkBox API changes
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkbox-api-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev 011d8a29b7 calls-contacts-row: AdwAvatar API changes
https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-libhandy-1-4-to-libadwaita.html#adapt-to-adwavatar-api-changes

This is *not* ideal, since it relies on Folks returning a GFileIcon
internally, and it's also blocking. However, better to use something
simple that compiles and works to begin with.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev 27c2f62dc1 calls-call-record-row: Stop using Gtk.Widget::popup-menu
Removed in GTK4... I don't know if there's a replacement for it at the
moment.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:26 -07:00
Anton Lazarev b85d7efc4f calls-call-record-row: migrate button-press-event to GtkGestureClick
https://docs.gtk.org/gtk4/migrating-3to4.html#stop-using-gtkwidget-event-signals

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 669568869c calls-call-record-row: Adapt to event controller API changes
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-event-controller-api-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 78ad3af035 calls-call-window: Adapt to GtkBin removal
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkbin-removal

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 2618620124 calls-call-record-row: Adapt to GtkPopover changes
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkpopover-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev e5c73011e2 calls-account-overview: Adapt to event controller API changes
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-event-controller-api-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev a80001bd0e calls-contacts-box: Stop using GtkShadowType property
https://docs.gtk.org/gtk4/migrating-3to4.html#stop-using-gtkshadowtype-and-gtkrelief-properties

It can probably be replaced later by a CSS style, if necessary

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 3617f7b7cb calls-contacts-box: Adapt to GtkContainer removal
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkcontainer-removal

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 0727996edf calls-account-overview: Adapt to GtkBin/GtkContainer removal
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkbin-removal

https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkcontainer-removal

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev f7354a06c6 calls-call-window: Adapt to GtkContainer removal
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkcontainer-removal

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 50126f05f0 calls-application: Replace GtkClipboard with GdkClipboard
https://docs.gtk.org/gtk4/migrating-3to4.html#replace-gtkclipboard-with-gdkclipboard

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 31d6a9dcf3 calls-new-call-box: adapt to AdwComboRow API changes
https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-libhandy-1-4-to-libadwaita.html#adapt-to-adwcomborow-api-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 47e30c12a7 calls-sip-account-widget: Stop using HdyValueObject and adapt to AdwComboRow API changes
These two are kind of hard to separate.

https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-libhandy-1-4-to-libadwaita.html#stop-using-hdyvalueobject

https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-libhandy-1-4-to-libadwaita.html#adapt-to-adwcomborow-api-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev ca059116b9 calls-application: Stop using GdkScreen
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkwidgets-size-allocation-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev fef4e02b50 calls-main-window: Adapt to GTKWidget's size allocation changes
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkwidgets-size-allocation-changes

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev f6c9ff5c2e calls-main-window: Adapt to GtkContainer removal
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkcontainer-removal

Now using `GtkStackPage` to manage icons.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:25 -07:00
Anton Lazarev 674906f788 calls-application: Unconditionally use AdwStyleManager
> In libadwaita color schemes will be the only way to request dark
appearance.

https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-libhandy-1-4-to-libadwaita.html#use-hdystylemanager-instead-of-gtksettingsgtk-application-prefer-dark-theme

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev 2b972ba035 treewide: gtk_widget_destroy() removal
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtk_widget_destroy-removal

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev 34a7651f44 treewide: Replace hide/show with set_visible
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev 12b78ca5f6 calls-best-match: AdwAvatar API changes
https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/migrating-libhandy-1-4-to-libadwaita.html#adapt-to-adwavatar-api-changes

This is *not* ideal, since it relies on Folks returning a GFileIcon
internally, and it's also blocking. However, better to use something
simple that compiles and works to begin with.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev 52a0963e6c treewide: Remove icon size from constructors
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev 4b0b20eb0a treewide: GtkEntry -> GtkEditable
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev 10b0672f9e treewide: Remove GtkEventBox
https://docs.gtk.org/gtk4/migrating-3to4.html#stop-using-gtkeventbox

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev ee3abc009a treewide: Replace libhandy with libadwaita
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev 4ed1bba0b8 treewide: Subclass custom classes from GtkWidget
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev bc90d6e64f gtklistmodels: remove gtklistmodels polyfills
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev d7504da0d2 ci: replace NO_AT_BRIDGE with GTK_A11Y
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:24 -07:00
Anton Lazarev 6d1cb199d1 libcall-ui: bump version to v0.2.0
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-06 08:15:14 -07:00
Anton Lazarev 98231778ed meson: Bump gtk dependency to 4.0
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-01 18:37:57 -07:00
Anton Lazarev b26faf1b13 calls-new-call-box: compose with GtkBin instead of subclassing GtkBox
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-01 18:37:57 -07:00
Anton Lazarev 9fa0539ad6 sip-account-widget: compose with GtkBin instead of subclassing GtkBox
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-01 18:37:57 -07:00
Anton Lazarev 2141c1ffc2 in-app-notification: compose with GtkBin instead of subclassing GtkRevealer
Note that the GtkOverlay must pass-through interactions for this to
work, meaning it's no longer possible to interact with the
notifications. I'm leaving it like this since I'll need to replace it
with AdwToast later anyways.

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-01 18:37:57 -07:00
Anton Lazarev f1e63c4979 history-box: compose with GtkBin instead of subclassing GtkStack
Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
2024-04-01 18:37:57 -07:00
80 changed files with 916 additions and 6451 deletions

View file

@ -26,7 +26,7 @@ While much of the original codebase was written using [GNU's Coding Style][1],
please try to follow this document for newly written code.
For existing code you should probably try to not mix different code styles too much.
This coding style is heavily inspired/copied from [phosh's Coding Style][2]
which itself is mostly using [libhandy's Coding Style][3].
which itself is mostly using [libadwaita's Coding Style][3].
These are the differences:
@ -124,8 +124,8 @@ individual C files should be structured as (top to bottom of file):
The reason public methods go at the bottom is that they have declarations in
the header file and can thus be referenced from anywhere else in the source
file.
Try to avoid forward declarations where possible.
[1]: https://www.gnu.org/prep/standards/standards.html#Formatting
[2]: https://gitlab.gnome.org/World/Phosh/phosh/-/blob/main/HACKING.md#coding-style
[3]: https://gitlab.gnome.org/GNOME/libhandy/-/blob/main/HACKING.md#coding-style
[3]: https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/HACKING.md#coding-style

2
debian/control vendored
View file

@ -11,6 +11,7 @@ Build-Depends:
gstreamer1.0-plugins-bad,
gstreamer1.0-plugins-good,
gtk-doc-tools <!nodoc>,
libadwaita-1-dev (>= 1.2),
libcallaudio-dev (>= 0.0.5),
libebook-contacts1.2-dev,
libfeedback-dev (>= 0.0.1),
@ -19,7 +20,6 @@ Build-Depends:
libgstreamer1.0-dev,
libgtk-3-dev,
libgtk-3-doc <!nodoc>,
libhandy-1-dev (>= 1.1.90),
libmm-glib-dev (>= 1.12.0),
libpeas-dev,
librsvg2-common,

13
debian/copyright vendored
View file

@ -31,19 +31,6 @@ Comment:
From gnome-control-center,
see https://bugzilla.gnome.org/show_bug.cgi?id=792243
Files:
src/gtklistmodels/*
Copyright:
2018-2019 Benjamin Otte
2019 Matthias Clasen
License: LGPL-2.1+
Files:
src/gtklistmodels/gtkprivate.h
Copyright:
1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
License: LGPL-2+
Files:
data/org.gnome.Calls.metainfo.xml
Copyright:

View file

@ -32,6 +32,11 @@ project(
]
)
gtk_version = '4.8'
gtk_version_arr = gtk_version.split('.')
gtk_major = gtk_version_arr[0]
gtk_minor = gtk_version_arr[1]
calls_id = 'org.gnome.Calls'
calls_homepage = 'https://gitlab.gnome.org/GNOME/calls'
calls_name = meson.project_name()
@ -111,6 +116,8 @@ test_c_args = [
'-Wunused-variable',
'-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_64',
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_70',
'-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor),
'-DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor),
# see https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/332
'-DEDS_DISABLE_DEPRECATED',
# in preparation for the switch to Gtk4 we should make sure not to use deprecated symbols

View file

@ -44,7 +44,7 @@ i18n_plugin = i18n.merge_file(
dummy_deps = [
dependency('gobject-2.0'),
dependency('gtk+-3.0'),
dependency('gtk4', version: '>= @0@'.format(gtk_version)),
dependency('libpeas-1.0'),
]

View file

@ -42,7 +42,7 @@ i18n_plugin = i18n.merge_file(
mm_deps = [
dependency('gobject-2.0'),
dependency('gtk+-3.0'),
dependency('gtk4', version: '>= @0@'.format(gtk_version)),
dependency('ModemManager'),
dependency('mm-glib', version: '>= 1.12.0'),
dependency('libpeas-1.0'),

View file

@ -43,7 +43,7 @@ i18n_plugin = i18n.merge_file(
ofono_deps = [
dependency('gobject-2.0'),
dependency('gtk+-3.0'),
dependency('gtk4', version: '>= @0@'.format(gtk_version)),
dependency('libpeas-1.0'),
]

View file

@ -51,7 +51,9 @@ static GParamSpec *props[PROP_LAST_PROP];
struct _CallsSipAccountWidget {
GtkBox parent;
AdwBin parent;
GtkWidget *child;
/* Header bar */
GtkWidget *header_add;
@ -69,9 +71,9 @@ struct _CallsSipAccountWidget {
GtkEntry *password;
GtkEntry *port;
char *last_port;
HdyComboRow *protocol;
GListStore *protocols_store; /* bound model for protocol HdyComboRow */
HdyComboRow *media_encryption;
AdwComboRow *protocol;
GtkStringList *protocols_store; /* bound model for protocol AdwComboRow */
AdwComboRow *media_encryption;
GListStore *media_encryption_store;
GtkSwitch *tel_switch;
GtkSwitch *auto_connect_switch;
@ -87,7 +89,7 @@ struct _CallsSipAccountWidget {
gboolean port_self_change;
};
G_DEFINE_TYPE (CallsSipAccountWidget, calls_sip_account_widget, GTK_TYPE_BOX)
G_DEFINE_TYPE (CallsSipAccountWidget, calls_sip_account_widget, ADW_TYPE_BIN)
static gboolean
@ -105,22 +107,20 @@ is_form_filled (CallsSipAccountWidget *self)
g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
return
g_strcmp0 (gtk_entry_get_text (self->host), "") != 0 &&
g_strcmp0 (gtk_entry_get_text (self->user), "") != 0 &&
g_strcmp0 (gtk_entry_get_text (self->password), "") != 0 &&
g_strcmp0 (gtk_entry_get_text (self->port), "") != 0;
g_strcmp0 (gtk_editable_get_text (GTK_EDITABLE (self->host)), "") != 0 &&
g_strcmp0 (gtk_editable_get_text (GTK_EDITABLE (self->user)), "") != 0 &&
g_strcmp0 (gtk_editable_get_text (GTK_EDITABLE (self->password)), "") != 0 &&
g_strcmp0 (gtk_editable_get_text (GTK_EDITABLE (self->port)), "") != 0;
}
static const char *
get_selected_protocol (CallsSipAccountWidget *self)
{
g_autoptr (HdyValueObject) obj = NULL;
const char *protocol = NULL;
gint i;
if ((i = hdy_combo_row_get_selected_index (self->protocol)) != -1) {
obj = g_list_model_get_item (G_LIST_MODEL (self->protocols_store), i);
protocol = hdy_value_object_get_string (obj);
if ((i = adw_combo_row_get_selected(self->protocol)) != GTK_INVALID_LIST_POSITION) {
protocol = gtk_string_list_get_string (self->protocols_store, i);
}
return protocol;
}
@ -129,11 +129,11 @@ get_selected_protocol (CallsSipAccountWidget *self)
static SipMediaEncryption
get_selected_media_encryption (CallsSipAccountWidget *self)
{
g_autoptr (HdyValueObject) obj = NULL;
GObject *obj = NULL;
SipMediaEncryption media_encryption = SIP_MEDIA_ENCRYPTION_NONE;
gint i;
if ((i = hdy_combo_row_get_selected_index (self->media_encryption)) != -1) {
if ((i = adw_combo_row_get_selected(self->media_encryption)) != GTK_INVALID_LIST_POSITION) {
obj = g_list_model_get_item (G_LIST_MODEL (self->media_encryption_store), i);
media_encryption = (SipMediaEncryption) GPOINTER_TO_INT (g_object_get_data (G_OBJECT (obj), "value"));
}
@ -158,7 +158,7 @@ update_media_encryption (CallsSipAccountWidget *self)
transport_is_tls | sdes_always_allowed);
if (!transport_is_tls && !sdes_always_allowed)
hdy_combo_row_set_selected_index (self->media_encryption, 0);
adw_combo_row_set_selected (self->media_encryption, 0);
}
@ -179,40 +179,6 @@ on_user_changed (CallsSipAccountWidget *self)
}
static void
set_password_visibility (CallsSipAccountWidget *self, gboolean visible)
{
const char *icon_name;
g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
g_assert (GTK_IS_ENTRY (self->password));
icon_name = visible ?
"view-conceal-symbolic" :
"view-reveal-symbolic";
gtk_entry_set_visibility (self->password, visible);
gtk_entry_set_icon_from_icon_name (self->password, GTK_ENTRY_ICON_SECONDARY,
icon_name);
}
static void
on_password_visibility_changed (CallsSipAccountWidget *self,
GtkEntryIconPosition icon_pos,
GdkEvent *event,
GtkEntry *entry)
{
gboolean visible;
g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
g_assert (GTK_IS_ENTRY (entry));
g_assert (icon_pos == GTK_ENTRY_ICON_SECONDARY);
visible = !gtk_entry_get_visibility (entry);
set_password_visibility (self, visible);
}
/*
* Stop "insert-text" signal emission if any undesired port
* value occurs
@ -251,7 +217,7 @@ on_port_entry_insert_text (CallsSipAccountWidget *self,
gtk_widget_error_bell (GTK_WIDGET (entry));
} else {
g_free (self->last_port);
self->last_port = g_strdup (gtk_entry_get_text (entry));
self->last_port = g_strdup (gtk_editable_get_text (GTK_EDITABLE (entry)));
}
}
@ -274,7 +240,7 @@ get_port (CallsSipAccountWidget *self)
const char *text;
int port = 0;
text = gtk_entry_get_text (self->port);
text = gtk_editable_get_text (GTK_EDITABLE (self->port));
port = (int) g_ascii_strtod (text, NULL);
return port;
@ -293,7 +259,7 @@ on_port_entry_after_insert_text (CallsSipAccountWidget *self,
/* Reset to the old value if new port number is invalid */
if ((port < 0 || port > 65535) && self->last_port) {
self->port_self_change = TRUE;
gtk_entry_set_text (entry, self->last_port);
gtk_editable_set_text (GTK_EDITABLE (entry), self->last_port);
g_idle_add (G_SOURCE_FUNC (update_port_cursor_position), entry);
gtk_widget_error_bell (GTK_WIDGET (entry));
self->port_self_change = FALSE;
@ -306,12 +272,12 @@ update_header (CallsSipAccountWidget *self)
g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
if (self->origin) {
gtk_widget_show (self->header_edit);
gtk_widget_hide (self->header_add);
gtk_widget_set_visible (self->header_edit, TRUE);
gtk_widget_set_visible (self->header_add, FALSE);
} else {
gtk_widget_show (self->header_add);
gtk_widget_hide (self->header_edit);
gtk_widget_set_visible (self->header_add, TRUE);
gtk_widget_set_visible (self->header_edit, FALSE);
}
if (self->connecting) {
@ -335,9 +301,7 @@ find_protocol (CallsSipAccountWidget *self,
len = g_list_model_get_n_items (G_LIST_MODEL (self->protocols_store));
for (guint i = 0; i < len; i++) {
g_autoptr (HdyValueObject) obj =
g_list_model_get_item (G_LIST_MODEL (self->protocols_store), i);
const char *prot = hdy_value_object_get_string (obj);
const char *prot = gtk_string_list_get_string (self->protocols_store, i);
if (g_strcmp0 (protocol, prot) == 0) {
if (index)
@ -363,7 +327,7 @@ find_media_encryption (CallsSipAccountWidget *self,
len = g_list_model_get_n_items (G_LIST_MODEL (self->media_encryption_store));
for (guint i = 0; i < len; i++) {
g_autoptr (HdyValueObject) obj =
GString* obj =
g_list_model_get_item (G_LIST_MODEL (self->media_encryption_store), i);
SipMediaEncryption obj_enc =
(SipMediaEncryption) GPOINTER_TO_INT (g_object_get_data (G_OBJECT (obj), "value"));
@ -385,14 +349,14 @@ clear_form (CallsSipAccountWidget *self)
{
g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
gtk_entry_set_text (self->host, "");
gtk_entry_set_text (self->display_name, "");
gtk_entry_set_text (self->user, "");
gtk_entry_set_text (self->password, "");
gtk_entry_set_text (self->port, "0");
hdy_combo_row_set_selected_index (self->protocol, 0);
gtk_editable_set_text (GTK_EDITABLE (self->host), "");
gtk_editable_set_text (GTK_EDITABLE (self->display_name), "");
gtk_editable_set_text (GTK_EDITABLE (self->user), "");
gtk_editable_set_text (GTK_EDITABLE (self->password), "");
gtk_editable_set_text (GTK_EDITABLE (self->port), "0");
adw_combo_row_set_selected (self->protocol, 0);
gtk_widget_set_sensitive (GTK_WIDGET (self->media_encryption), FALSE);
hdy_combo_row_set_selected_index (self->media_encryption, 0);
adw_combo_row_set_selected (self->media_encryption, 0);
gtk_switch_set_state (self->tel_switch, FALSE);
gtk_switch_set_state (self->auto_connect_switch, TRUE);
@ -458,14 +422,13 @@ edit_form (CallsSipAccountWidget *self,
encryption_index = 0;
/* set UI elements */
gtk_entry_set_text (self->host, host);
gtk_entry_set_text (self->display_name, display_name ?: "");
gtk_entry_set_text (self->user, user);
gtk_entry_set_text (self->password, password);
set_password_visibility (self, FALSE);
gtk_entry_set_text (self->port, port_str);
hdy_combo_row_set_selected_index (self->protocol, protocol_index);
hdy_combo_row_set_selected_index (self->media_encryption, encryption_index);
gtk_editable_set_text (GTK_EDITABLE (self->host), host);
gtk_editable_set_text (GTK_EDITABLE (self->display_name), display_name ?: "");
gtk_editable_set_text (GTK_EDITABLE (self->user), user);
gtk_editable_set_text (GTK_EDITABLE (self->password), password);
gtk_editable_set_text (GTK_EDITABLE (self->port), port_str);
adw_combo_row_set_selected (self->protocol, protocol_index);
adw_combo_row_set_selected (self->media_encryption, encryption_index);
gtk_switch_set_state (self->tel_switch, can_tel);
gtk_switch_set_state (self->auto_connect_switch, auto_connect);
@ -488,10 +451,10 @@ on_login_clicked (CallsSipAccountWidget *self)
origin = calls_sip_provider_add_origin (self->provider,
id,
gtk_entry_get_text (GTK_ENTRY (self->host)),
gtk_entry_get_text (GTK_ENTRY (self->user)),
gtk_entry_get_text (GTK_ENTRY (self->password)),
gtk_entry_get_text (GTK_ENTRY (self->display_name)),
gtk_editable_get_text (GTK_EDITABLE (self->host)),
gtk_editable_get_text (GTK_EDITABLE (self->user)),
gtk_editable_get_text (GTK_EDITABLE (self->password)),
gtk_editable_get_text (GTK_EDITABLE (self->display_name)),
get_selected_protocol (self),
get_port (self),
get_selected_media_encryption (self),
@ -522,10 +485,10 @@ on_apply_clicked (CallsSipAccountWidget *self)
g_debug ("Applying changes to the account");
calls_sip_origin_set_credentials (self->origin,
gtk_entry_get_text (self->host),
gtk_entry_get_text (self->user),
gtk_entry_get_text (self->password),
gtk_entry_get_text (self->display_name),
gtk_editable_get_text (GTK_EDITABLE (self->host)),
gtk_editable_get_text (GTK_EDITABLE (self->user)),
gtk_editable_get_text (GTK_EDITABLE (self->password)),
gtk_editable_get_text (GTK_EDITABLE (self->display_name)),
get_selected_protocol (self),
get_port (self),
get_selected_media_encryption (self),
@ -586,6 +549,10 @@ calls_sip_account_widget_dispose (GObject *object)
{
CallsSipAccountWidget *self = CALLS_SIP_ACCOUNT_WIDGET (object);
GtkWidget *child = GTK_WIDGET (self->child);
g_clear_pointer (&child, gtk_widget_unparent);
g_clear_pointer (&self->last_port, g_free);
g_clear_object (&self->protocols_store);
g_clear_object (&self->media_encryption_store);
@ -621,6 +588,8 @@ calls_sip_account_widget_class_init (CallsSipAccountWidgetClass *klass)
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/sip-account-widget.ui");
gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, child);
gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, header_add);
gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, spinner_add);
gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, header_edit);
@ -643,7 +612,6 @@ calls_sip_account_widget_class_init (CallsSipAccountWidgetClass *klass)
gtk_widget_class_bind_template_callback (widget_class, on_delete_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_apply_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_user_changed);
gtk_widget_class_bind_template_callback (widget_class, on_password_visibility_changed);
gtk_widget_class_bind_template_callback (widget_class, on_port_entry_insert_text);
gtk_widget_class_bind_template_callback (widget_class, on_port_entry_after_insert_text);
}
@ -652,7 +620,7 @@ calls_sip_account_widget_class_init (CallsSipAccountWidgetClass *klass)
static void
calls_sip_account_widget_init (CallsSipAccountWidget *self)
{
HdyValueObject *obj;
GtkStringObject *obj;
self->settings = calls_settings_get_default ();
@ -663,44 +631,30 @@ calls_sip_account_widget_init (CallsSipAccountWidget *self)
gtk_widget_init_template (GTK_WIDGET (self));
self->media_encryption_store = g_list_store_new (HDY_TYPE_VALUE_OBJECT);
self->media_encryption_store = g_list_store_new (GTK_TYPE_STRING_OBJECT);
obj = hdy_value_object_new_string (_("No encryption"));
obj = gtk_string_object_new (_("No encryption"));
g_object_set_data (G_OBJECT (obj),
"value", GINT_TO_POINTER (SIP_MEDIA_ENCRYPTION_NONE));
g_list_store_insert (self->media_encryption_store, 0, obj);
g_clear_object (&obj);
/* TODO Optional encryption */
obj = hdy_value_object_new_string (_("Force encryption"));
obj = gtk_string_object_new (_("Force encryption"));
g_object_set_data (G_OBJECT (obj),
"value", GINT_TO_POINTER (SIP_MEDIA_ENCRYPTION_FORCED));
g_list_store_insert (self->media_encryption_store, 1, obj);
g_clear_object (&obj);
hdy_combo_row_bind_name_model (self->media_encryption,
G_LIST_MODEL (self->media_encryption_store),
(HdyComboRowGetNameFunc) hdy_value_object_dup_string,
NULL, NULL);
adw_combo_row_set_model(self->media_encryption, G_LIST_MODEL (self->media_encryption_store));
self->protocols_store = g_list_store_new (HDY_TYPE_VALUE_OBJECT);
self->protocols_store = gtk_string_list_new (NULL);
obj = hdy_value_object_new_string ("UDP");
g_list_store_insert (self->protocols_store, 0, obj);
g_clear_object (&obj);
gtk_string_list_append (self->protocols_store, "UDP");
gtk_string_list_append (self->protocols_store, "TCP");
gtk_string_list_append (self->protocols_store, "TLS");
obj = hdy_value_object_new_string ("TCP");
g_list_store_insert (self->protocols_store, 1, obj);
g_clear_object (&obj);
obj = hdy_value_object_new_string ("TLS");
g_list_store_insert (self->protocols_store, 2, obj);
g_clear_object (&obj);
hdy_combo_row_bind_name_model (self->protocol,
G_LIST_MODEL (self->protocols_store),
(HdyComboRowGetNameFunc) hdy_value_object_dup_string,
NULL, NULL);
adw_combo_row_set_model(self->protocol, G_LIST_MODEL (self->protocols_store));
}

View file

@ -26,13 +26,13 @@
#include "calls-sip-provider.h"
#include <handy.h>
#include <adwaita.h>
G_BEGIN_DECLS
#define CALLS_TYPE_SIP_ACCOUNT_WIDGET (calls_sip_account_widget_get_type ())
G_DECLARE_FINAL_TYPE (CallsSipAccountWidget, calls_sip_account_widget, CALLS, SIP_ACCOUNT_WIDGET, GtkBox)
G_DECLARE_FINAL_TYPE (CallsSipAccountWidget, calls_sip_account_widget, CALLS, SIP_ACCOUNT_WIDGET, AdwBin)
CallsSipAccountWidget *calls_sip_account_widget_new (CallsSipProvider *provider);
void calls_sip_account_widget_set_origin (CallsSipAccountWidget *self,

View file

@ -45,8 +45,8 @@ i18n_plugin = i18n.merge_file(
sip_deps = [
dependency('gobject-2.0'),
dependency('gstreamer-1.0'),
dependency('gtk+-3.0'),
dependency('libhandy-1', version: '>= 1.1.90'),
dependency('gtk4'),
dependency('libadwaita-1', version: '>= 1.2'),
dependency('libpeas-1.0'),
dependency('sofia-sip-ua-glib'),
dependency('libsecret-1'),

View file

@ -1,228 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="libhandy" version="1.0"/>
<template class="CallsSipAccountWidget" parent="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<template class="CallsSipAccountWidget" parent="AdwBin">
<child>
<object class="HdyHeaderBar" id="header_add">
<property name="title" translatable="yes">Add Account</property>
<property name="show-close-button">True</property>
<child>
<object class="GtkButton" id="login_btn">
<property name="visible">True</property>
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Log In</property>
<signal name="clicked" handler="on_login_clicked" swapped="yes"/>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
<child>
<object class="GtkSpinner" id="spinner_add">
<property name="visible">True</property>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</child>
<child>
<object class="HdyHeaderBar" id="header_edit">
<property name="visible">True</property>
<property name="show-close-button">True</property>
<property name="title" translatable="yes">Manage Account</property>
<child>
<object class="GtkButton" id="apply_btn">
<property name="visible">True</property>
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Apply</property>
<signal name="clicked" handler="on_apply_clicked" swapped="yes"/>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
<child>
<object class="GtkButton" id="delete_btn">
<property name="visible">True</property>
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Delete</property>
<signal name="clicked" handler="on_delete_clicked" swapped="yes"/>
<style>
<class name="destructive-action"/>
</style>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
<child>
<object class="GtkSpinner" id="spinner_edit">
<property name="visible">True</property>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</child>
<child>
<object class="HdyPreferencesPage">
<property name="visible">True</property>
<child>
<object class="HdyPreferencesGroup">
<property name="visible">True</property>
<child>
<object class="HdyActionRow">
<property name="visible">True</property>
<property name="title" translatable="yes">Server</property>
<child>
<object class="GtkEntry" id="host">
<property name="visible">True</property>
<property name="valign">center</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
</object>
</child>
<object class="AdwToolbarView" id="child">
<child type="top">
<object class="AdwHeaderBar" id="header_add">
<child type="end">
<object class="GtkButton" id="login_btn">
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Log In</property>
<signal name="clicked" handler="on_login_clicked" swapped="yes"/>
<style>
<class name="suggested-action"/>
</style>
</object>
</child>
<child type="end">
<object class="GtkSpinner" id="spinner_add"/>
</child>
</object>
</child>
<child>
<object class="HdyPreferencesGroup">
<property name="visible">True</property>
<child>
<object class="HdyActionRow">
<property name="visible">True</property>
<property name="title" translatable="yes">Display Name</property>
<property name="subtitle" translatable="yes">Optional</property>
<child>
<object class="GtkEntry" id="display_name">
<property name="visible">True</property>
<property name="valign">center</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
</object>
</child>
<child type="top">
<object class="AdwHeaderBar" id="header_edit">
<child type="end">
<object class="GtkButton" id="apply_btn">
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Apply</property>
<signal name="clicked" handler="on_apply_clicked" swapped="yes"/>
<style>
<class name="suggested-action"/>
</style>
</object>
</child>
<child type="end">
<object class="GtkButton" id="delete_btn">
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Delete</property>
<signal name="clicked" handler="on_delete_clicked" swapped="yes"/>
<style>
<class name="destructive-action"/>
</style>
</object>
</child>
<child type="end">
<object class="GtkSpinner" id="spinner_edit"/>
</child>
</object>
</child>
<child>
<object class="HdyPreferencesGroup">
<property name="visible">True</property>
<property name="content">
<object class="AdwPreferencesPage">
<child>
<object class="HdyActionRow">
<property name="visible">True</property>
<property name="title" translatable="yes">User ID</property>
<object class="AdwPreferencesGroup">
<child>
<object class="GtkEntry" id="user">
<property name="visible">True</property>
<property name="valign">center</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
<object class="AdwActionRow">
<property name="title" translatable="yes">Server</property>
<child>
<object class="GtkEntry" id="host">
<property name="valign">center</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="HdyActionRow">
<property name="visible">True</property>
<property name="title" translatable="yes">Password</property>
<object class="AdwPreferencesGroup">
<child>
<object class="GtkEntry" id="password">
<property name="visible">True</property>
<property name="valign">center</property>
<property name="input-purpose">password</property>
<property name="visibility">False</property>
<property name="primary_icon_sensitive">False</property>
<property name="secondary_icon_activatable">True</property>
<property name="secondary_icon_name">view-reveal-symbolic</property>
<property name="secondary_icon_sensitive">True</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
<signal name="icon-press" handler="on_password_visibility_changed" swapped="yes"/>
<object class="AdwActionRow">
<property name="title" translatable="yes">Display Name</property>
<property name="subtitle" translatable="yes">Optional</property>
<child>
<object class="GtkEntry" id="display_name">
<property name="valign">center</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">User ID</property>
<child>
<object class="GtkEntry" id="user">
<property name="valign">center</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Password</property>
<child>
<object class="GtkPasswordEntry" id="password">
<property name="valign">center</property>
<property name="show-peek-icon">True</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Port</property>
<child>
<object class="GtkEntry" id="port">
<property name="valign">center</property>
<property name="input-purpose">digits</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
<signal name="insert-text" handler="on_port_entry_insert_text" swapped="yes"/>
<signal name="insert-text" handler="on_port_entry_after_insert_text" swapped="yes" after="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwComboRow" id="protocol">
<property name="title" translatable="yes">Transport</property>
<signal name="notify::selected-index" handler="on_user_changed" swapped="yes"/>
</object>
</child>
<child>
<object class="AdwComboRow" id="media_encryption">
<property name="title" translatable="yes">Media Encryption</property>
<signal name="notify::selected-index" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Use for Phone Calls</property>
<child>
<object class="GtkSwitch" id="tel_switch">
<property name="valign">center</property>
<signal name="notify::active" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Automatically Connect</property>
<child>
<object class="GtkSwitch" id="auto_connect_switch">
<property name="valign">center</property>
<signal name="notify::active" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="HdyPreferencesGroup">
<property name="visible">True</property>
<child>
<object class="HdyActionRow">
<property name="visible">True</property>
<property name="title" translatable="yes">Port</property>
<child>
<object class="GtkEntry" id="port">
<property name="visible">True</property>
<property name="valign">center</property>
<property name="input-purpose">digits</property>
<signal name="changed" handler="on_user_changed" swapped="yes"/>
<signal name="insert-text" handler="on_port_entry_insert_text" swapped="yes"/>
<signal name="insert-text" handler="on_port_entry_after_insert_text" swapped="yes" after="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="HdyComboRow" id="protocol">
<property name="visible">True</property>
<property name="title" translatable="yes">Transport</property>
<signal name="notify::selected-index" handler="on_user_changed" swapped="yes"/>
</object>
</child>
<child>
<object class="HdyComboRow" id="media_encryption">
<property name="visible">True</property>
<property name="title" translatable="yes">Media Encryption</property>
<signal name="notify::selected-index" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="HdyPreferencesGroup">
<property name="visible">True</property>
<child>
<object class="HdyActionRow">
<property name="visible">True</property>
<property name="title" translatable="yes">Use for Phone Calls</property>
<child>
<object class="GtkSwitch" id="tel_switch">
<property name="visible">True</property>
<property name="valign">center</property>
<signal name="notify::active" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="HdyActionRow">
<property name="visible">True</property>
<property name="title" translatable="yes">Automatically Connect</property>
<child>
<object class="GtkSwitch" id="auto_connect_switch">
<property name="visible">True</property>
<property name="valign">center</property>
<signal name="notify::active" handler="on_user_changed" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</child>
</property>
</object>
</child>
</template>
@ -236,4 +178,3 @@
</widgets>
</object>
</interface>

View file

@ -35,18 +35,15 @@
#include "calls-plugin-manager.h"
#include "calls-util.h"
#include "gtkcustomfilter.h"
#include "gtkfilterlistmodel.h"
#include <glib/gi18n-lib.h>
/**
* Section:calls-account-overview
* short_description: A #HdyWindow to manage VoIP accounts
* short_description: A #AdwWindow to manage VoIP accounts
* @Title: CallsAccountOverview
*
* This is a #HdyWindow derived window to display and manage the
* This is a #AdwWindow derived window to display and manage the
* VoIP accounts. Each available #CallsAccount from any #CallsAccountProvider
* will be listed as a #CallsAccountRow.
*/
@ -58,7 +55,7 @@ typedef enum {
struct _CallsAccountOverview {
HdyWindow parent;
AdwWindow parent;
/* UI widgets */
GtkStack *stack;
@ -72,8 +69,6 @@ struct _CallsAccountOverview {
GtkWidget *current_account_widget;
/* models */
GtkFilter *account_provider_filter;
GtkFilter *account_filter;
GListModel *providers;
GListModel *accounts;
@ -81,10 +76,10 @@ struct _CallsAccountOverview {
GtkEventController *key_controller;
GtkEventController *key_controller_account;
CallsAccountOverviewState state;
CallsInAppNotification *in_app_notification;
AdwToastOverlay *toast_overlay;
};
G_DEFINE_TYPE (CallsAccountOverview, calls_account_overview, HDY_TYPE_WINDOW)
G_DEFINE_TYPE (CallsAccountOverview, calls_account_overview, ADW_TYPE_WINDOW)
static void
@ -153,13 +148,9 @@ attach_account_widget (CallsAccountOverview *self,
if (widget == self->current_account_widget)
return;
if (self->current_account_widget)
gtk_container_remove (GTK_CONTAINER (self->account_window),
self->current_account_widget);
adw_window_set_content (ADW_WINDOW (self->account_window), widget);
self->current_account_widget = widget;
if (widget)
gtk_container_add (GTK_CONTAINER (self->account_window), widget);
}
@ -235,7 +226,7 @@ on_account_message (CallsAccount *account,
notification = g_strdup_printf ("%s: %s",
calls_account_get_address (account),
message);
calls_in_app_notification_show (self->in_app_notification, notification);
calls_in_app_notification_show (self->toast_overlay, notification);
}
@ -252,7 +243,7 @@ on_accounts_changed (GListModel *accounts,
GtkListBoxRow *row =
gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->overview), position + i - 1);
gtk_container_remove (GTK_CONTAINER (self->overview), GTK_WIDGET (row));
gtk_list_box_remove (GTK_LIST_BOX (self->overview), GTK_WIDGET (row));
}
for (guint i = 0; i < added; i++) {
@ -288,6 +279,16 @@ on_accounts_changed (GListModel *accounts,
}
/*
* Helper for `on_providers_changed` signal callback
*/
static void
hide_widget (GtkWidget *widget)
{
gtk_widget_set_visible (widget, FALSE);
}
static void
on_providers_changed (GListModel *providers,
guint position,
@ -300,12 +301,12 @@ on_providers_changed (GListModel *providers,
g_list_model_get_item (providers, position + i);
g_signal_connect_swapped (provider, "widget-edit-done",
G_CALLBACK (gtk_widget_hide), self->account_window);
G_CALLBACK (hide_widget), self->account_window);
}
/* Clear any acccount widgets, because they might've gone stale */
attach_account_widget (self, NULL);
gtk_widget_hide (GTK_WIDGET (self->account_window));
gtk_widget_set_visible (GTK_WIDGET (self->account_window), FALSE);
}
@ -317,7 +318,7 @@ on_key_pressed (GtkEventControllerKey *controller,
GtkWidget *widget)
{
if (keyval == GDK_KEY_Escape) {
gtk_widget_hide (widget);
gtk_widget_set_visible (widget, FALSE);
return GDK_EVENT_STOP;
}
@ -330,10 +331,8 @@ calls_account_overview_dispose (GObject *object)
CallsAccountOverview *self = CALLS_ACCOUNT_OVERVIEW (object);
g_clear_object (&self->providers);
g_clear_object (&self->account_provider_filter);
g_clear_object (&self->accounts);
g_clear_object (&self->account_filter);
g_clear_object (&self->key_controller);
g_clear_object (&self->key_controller_account);
@ -360,7 +359,7 @@ calls_account_overview_class_init (CallsAccountOverviewClass *klass)
gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, account_window);
gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, in_app_notification);
gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, toast_overlay);
gtk_widget_class_bind_template_callback (widget_class, on_add_account_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_account_row_activated);
@ -390,6 +389,9 @@ match_account (gpointer item,
static void
calls_account_overview_init (CallsAccountOverview *self)
{
GtkFilter *account_provider_filter;
GtkFilter *account_filter;
GListModel *all_providers =
calls_plugin_manager_get_providers (calls_plugin_manager_get_default ());
GListModel *all_origins =
@ -397,10 +399,9 @@ calls_account_overview_init (CallsAccountOverview *self)
gtk_widget_init_template (GTK_WIDGET (self));
self->account_provider_filter = gtk_custom_filter_new (match_account_provider, NULL, NULL);
account_provider_filter = GTK_FILTER (gtk_custom_filter_new (match_account_provider, NULL, NULL));
self->providers =
G_LIST_MODEL (gtk_filter_list_model_new (all_providers,
self->account_provider_filter));
G_LIST_MODEL (gtk_filter_list_model_new (all_providers, account_provider_filter));
g_signal_connect (self->providers,
"items-changed",
@ -410,10 +411,9 @@ calls_account_overview_init (CallsAccountOverview *self)
0, 0, g_list_model_get_n_items (self->providers),
self);
self->account_filter = gtk_custom_filter_new (match_account, NULL, NULL);
account_filter = GTK_FILTER (gtk_custom_filter_new (match_account, NULL, NULL));
self->accounts =
G_LIST_MODEL (gtk_filter_list_model_new (all_origins,
self->account_filter));
G_LIST_MODEL (gtk_filter_list_model_new (all_origins, account_filter));
g_signal_connect_object (self->accounts,
"items-changed",
G_CALLBACK (on_accounts_changed),
@ -426,19 +426,19 @@ calls_account_overview_init (CallsAccountOverview *self)
-1);
gtk_window_set_transient_for (self->account_window, GTK_WINDOW (self));
self->key_controller = gtk_event_controller_key_new (GTK_WIDGET (self));
self->key_controller = gtk_event_controller_key_new ();
g_signal_connect (self->key_controller,
"key-pressed",
G_CALLBACK (on_key_pressed),
self);
self->key_controller_account = gtk_event_controller_key_new (GTK_WIDGET (self->account_window));
self->key_controller_account = gtk_event_controller_key_new ();
g_signal_connect (self->key_controller_account,
"key-pressed",
G_CALLBACK (on_key_pressed),
self->account_window);
gtk_window_set_title (GTK_WINDOW (self), _("Account overview"));
gtk_window_set_title (GTK_WINDOW (self), _("VoIP Accounts"));
}

View file

@ -24,13 +24,13 @@
#pragma once
#include <handy.h>
#include <adwaita.h>
G_BEGIN_DECLS
#define CALLS_TYPE_ACCOUNT_OVERVIEW (calls_account_overview_get_type ())
G_DECLARE_FINAL_TYPE (CallsAccountOverview, calls_account_overview, CALLS, ACCOUNT_OVERVIEW, HdyWindow)
G_DECLARE_FINAL_TYPE (CallsAccountOverview, calls_account_overview, CALLS, ACCOUNT_OVERVIEW, AdwWindow)
CallsAccountOverview *calls_account_overview_new (void);

View file

@ -30,10 +30,10 @@
/**
* Section:calls-account-row
* short_description: A #HdyActionRow for use in #CallsAccountOverview
* short_description: A #AdwActionRow for use in #CallsAccountOverview
* @Title: CallsAccountRow
*
* This is a #HdyActionRow derived widget representing a #CallsAccount
* This is a #AdwActionRow derived widget representing a #CallsAccount
* for VoIP accounts (currently only SIP).
*/
@ -48,18 +48,18 @@ enum {
static GParamSpec *props[PROP_LAST_PROP];
struct _CallsAccountRow {
HdyActionRow parent;
AdwActionRow parent;
CallsAccountProvider *provider;
CallsAccount *account;
gboolean online;
/* UI elements */
HdyAvatar *avatar;
AdwAvatar *avatar;
GtkSwitch *online_switch;
};
G_DEFINE_TYPE (CallsAccountRow, calls_account_row, HDY_TYPE_ACTION_ROW)
G_DEFINE_TYPE (CallsAccountRow, calls_account_row, ADW_TYPE_ACTION_ROW)
static void

View file

@ -27,14 +27,14 @@
#include "calls-account.h"
#include "calls-account-provider.h"
#include <handy.h>
#include <adwaita.h>
G_BEGIN_DECLS
#define CALLS_TYPE_ACCOUNT_ROW (calls_account_row_get_type ())
G_DECLARE_FINAL_TYPE (CallsAccountRow, calls_account_row, CALLS, ACCOUNT_ROW, HdyActionRow);
G_DECLARE_FINAL_TYPE (CallsAccountRow, calls_account_row, CALLS, ACCOUNT_ROW, AdwActionRow);
CallsAccountRow *calls_account_row_new (CallsAccountProvider *provider,
CallsAccount *account);

View file

@ -44,10 +44,10 @@
#include "calls-ringer.h"
#include "version.h"
#include <adwaita.h>
#include <call-ui.h>
#include <glib/gi18n.h>
#include <glib-unix.h>
#include <handy.h>
#include <libcallaudio.h>
/**
@ -58,7 +58,7 @@
*/
struct _CallsApplication {
GtkApplication parent_instance;
AdwApplication parent_instance;
gboolean daemon;
CallsRinger *ringer;
@ -78,7 +78,7 @@ struct _CallsApplication {
gboolean db_done;
};
G_DEFINE_TYPE (CallsApplication, calls_application, GTK_TYPE_APPLICATION);
G_DEFINE_TYPE (CallsApplication, calls_application, ADW_TYPE_APPLICATION);
static void start_proper (CallsApplication *self);
@ -404,10 +404,10 @@ copy_number (GSimpleAction *action,
gpointer user_data)
{
const char *number = g_variant_get_string (parameter, NULL);
GtkClipboard *clipboard =
gtk_clipboard_get_default (gdk_display_get_default ());
GdkClipboard *clipboard =
gdk_display_get_clipboard (gdk_display_get_default ());
gtk_clipboard_set_text (clipboard, number, -1);
gdk_clipboard_set_text (clipboard, number);
g_debug ("Copied `%s' to clipboard", number);
}
@ -474,20 +474,9 @@ startup (GApplication *application)
{
g_autoptr (GtkCssProvider) provider = NULL;
g_autoptr (GError) error = NULL;
#if HDY_CHECK_VERSION (1, 5, 0)
HdyStyleManager *style_manager;
#endif
G_APPLICATION_CLASS (calls_application_parent_class)->startup (application);
hdy_init ();
#if HDY_CHECK_VERSION (1, 5, 0)
style_manager = hdy_style_manager_get_default ();
hdy_style_manager_set_color_scheme (style_manager, HDY_COLOR_SCHEME_PREFER_LIGHT);
#endif
if (!call_audio_init (&error))
g_warning ("Failed to init libcallaudio: %s", error->message);
@ -511,12 +500,6 @@ startup (GApplication *application)
G_CONNECT_SWAPPED);
manager_state_changed_cb (application);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider, "/org/gnome/Calls/style.css");
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
@ -672,7 +655,7 @@ start_proper (CallsApplication *self)
self->main_window =
calls_main_window_new (gtk_app,
G_LIST_MODEL (self->record_store));
calls_record_store_get_list_model (self->record_store));
g_assert (self->main_window != NULL);
self->call_window = calls_call_window_new (gtk_app);
@ -763,9 +746,9 @@ finalize (GObject *object)
g_clear_handle_id (&self->id_sigint, g_source_remove);
if (self->main_window)
gtk_widget_destroy (GTK_WIDGET (self->main_window));
gtk_window_destroy (GTK_WINDOW (self->main_window));
if (self->call_window)
gtk_widget_destroy (GTK_WIDGET (self->call_window));
gtk_window_destroy (GTK_WINDOW (self->call_window));
g_clear_object (&self->record_store);
g_clear_object (&self->ringer);

View file

@ -25,13 +25,14 @@
#pragma once
#include <adwaita.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CALLS_TYPE_APPLICATION (calls_application_get_type ())
G_DECLARE_FINAL_TYPE (CallsApplication, calls_application, CALLS, APPLICATION, GtkApplication)
G_DECLARE_FINAL_TYPE (CallsApplication, calls_application, CALLS, APPLICATION, AdwApplication)
CallsApplication *calls_application_new (void);

View file

@ -282,7 +282,7 @@ calls_best_match_class_init (CallsBestMatchClass *klass)
g_param_spec_object ("avatar",
"Avatar",
"The avatar of the best match",
G_TYPE_LOADABLE_ICON,
GDK_TYPE_TEXTURE,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
props[PROP_PRIMARY_INFO] =
@ -452,15 +452,32 @@ calls_best_match_get_name (CallsBestMatch *self)
*
* Returns: (nullable): The avatar of a matched contact or %NULL when there's no match.
*/
GLoadableIcon *
GdkTexture *
calls_best_match_get_avatar (CallsBestMatch *self)
{
GdkTexture *output;
GLoadableIcon *loadable_icon;
g_autoptr (GError) error = NULL;
g_return_val_if_fail (CALLS_IS_BEST_MATCH (self), NULL);
if (self->matched_individual)
return folks_avatar_details_get_avatar (FOLKS_AVATAR_DETAILS (self->matched_individual));
else
if (!self->matched_individual)
return NULL;
loadable_icon = folks_avatar_details_get_avatar (FOLKS_AVATAR_DETAILS (self->matched_individual));
if (!G_IS_FILE_ICON (loadable_icon)) {
return NULL;
}
output = gdk_texture_new_from_file (g_file_icon_get_file (G_FILE_ICON (loadable_icon)), &error);
if (error != NULL) {
g_print ("Failed to read avatar icon file: %s", error->message);
return NULL;
}
return output;
}
/**

View file

@ -25,6 +25,7 @@
#ifndef CALLS_BEST_MATCH_H__
#define CALLS_BEST_MATCH_H__
#include <gdk/gdk.h>
#include <gio/gio.h>
G_BEGIN_DECLS
@ -40,7 +41,7 @@ const char *calls_best_match_get_phone_number (CallsBestMatch *self);
void calls_best_match_set_phone_number (CallsBestMatch *self,
const char *phone_number);
const char *calls_best_match_get_name (CallsBestMatch *self);
GLoadableIcon *calls_best_match_get_avatar (CallsBestMatch *self);
GdkTexture *calls_best_match_get_avatar (CallsBestMatch *self);
const char *calls_best_match_get_primary_info (CallsBestMatch *self);
const char *calls_best_match_get_secondary_info (CallsBestMatch *self);

View file

@ -28,10 +28,10 @@
#include "calls-manager.h"
#include "calls-util.h"
#include <adwaita.h>
#include <glib/gi18n.h>
#include <glib-object.h>
#include <glib.h>
#include <handy.h>
#include <sys/time.h>
#include <errno.h>
@ -46,8 +46,6 @@ struct _CallsCallRecordRow {
GtkLabel *time;
GtkButton *button;
GtkPopover *popover;
GtkGesture *gesture;
GtkEventBox *event_box;
GMenu *context_menu;
@ -224,8 +222,7 @@ update_time (CallsCallRecordRow *self,
}
gtk_image_set_from_icon_name (self->type,
get_call_icon_symbolic_name (inbound, !answered),
GTK_ICON_SIZE_MENU);
get_call_icon_symbolic_name (inbound, !answered));
}
@ -358,7 +355,7 @@ setup_contact (CallsCallRecordRow *self)
G_BINDING_SYNC_CREATE);
g_object_bind_property (self->contact, "avatar",
self->avatar, "loadable-icon",
self->avatar, "custom-image",
G_BINDING_SYNC_CREATE);
}
@ -375,10 +372,8 @@ context_menu (GtkWidget *widget,
self = CALLS_CALL_RECORD_ROW (widget);
if (!self->popover) {
self->popover = GTK_POPOVER (gtk_popover_new (widget));
gtk_popover_bind_model (self->popover,
G_MENU_MODEL (self->context_menu),
"row-history");
self->popover = GTK_POPOVER (gtk_popover_menu_new_from_model (G_MENU_MODEL (self->context_menu)));
gtk_widget_set_parent (GTK_WIDGET (self->popover), widget);
}
setup_popover_actions (self);
@ -386,14 +381,6 @@ context_menu (GtkWidget *widget,
}
static gboolean
calls_call_record_row_popup_menu (GtkWidget *self)
{
context_menu (self, NULL);
return TRUE;
}
static void
on_long_pressed (GtkGestureLongPress *gesture,
gdouble x,
@ -404,16 +391,17 @@ on_long_pressed (GtkGestureLongPress *gesture,
}
static gboolean
calls_call_record_row_button_press_event (GtkWidget *self,
GdkEventButton *event)
static void
calls_call_record_row_button_press_event (GtkGestureClick* controller,
gint n_press,
gdouble x,
gdouble y,
GtkWidget *self)
{
if (gdk_event_triggers_context_menu ((GdkEvent *) event)) {
GdkEvent *event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (controller));
if (gdk_event_triggers_context_menu (event)) {
context_menu (self, (GdkEvent *) event);
return TRUE;
}
return GTK_WIDGET_CLASS (calls_call_record_row_parent_class)->button_press_event (self, event);
}
@ -509,7 +497,6 @@ dispose (GObject *object)
g_clear_object (&self->contact);
g_clear_object (&self->action_map);
g_clear_object (&self->gesture);
calls_clear_source (&self->date_change_timeout);
calls_clear_signal (self->record, &self->answered_notify_handler_id);
@ -520,6 +507,19 @@ dispose (GObject *object)
}
static void
finalize (GObject *object)
{
CallsCallRecordRow *self = CALLS_CALL_RECORD_ROW (object);
GtkWidget *popover = GTK_WIDGET (self->popover);
g_clear_pointer (&popover, gtk_widget_unparent);
G_OBJECT_CLASS (calls_call_record_row_parent_class)->dispose (object);
}
static void
calls_call_record_row_class_init (CallsCallRecordRowClass *klass)
{
@ -530,9 +530,7 @@ calls_call_record_row_class_init (CallsCallRecordRowClass *klass)
object_class->constructed = constructed;
object_class->get_property = get_property;
object_class->dispose = dispose;
widget_class->popup_menu = calls_call_record_row_popup_menu;
widget_class->button_press_event = calls_call_record_row_button_press_event;
object_class->finalize = finalize;
props[PROP_RECORD] =
g_param_spec_object ("record",
@ -551,7 +549,6 @@ calls_call_record_row_class_init (CallsCallRecordRowClass *klass)
gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, time);
gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, button);
gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, event_box);
gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, context_menu);
}
@ -631,10 +628,10 @@ new_sms_activated (GSimpleAction *action,
static GActionEntry entries[] =
{
{ "delete-call", delete_call_activated, NULL, NULL, NULL},
{ "copy-number", copy_number_activated, NULL, NULL, NULL},
{ "new-contact", new_contact_activated, NULL, NULL, NULL},
{ "new-sms", new_sms_activated, NULL, NULL, NULL},
{ "delete-call", delete_call_activated },
{ "copy-number", copy_number_activated },
{ "new-contact", new_contact_activated },
{ "new-sms", new_sms_activated },
};
@ -642,6 +639,7 @@ static void
calls_call_record_row_init (CallsCallRecordRow *self)
{
GAction *act;
GtkGesture *gesture;
gtk_widget_init_template (GTK_WIDGET (self));
@ -657,9 +655,15 @@ calls_call_record_row_init (CallsCallRecordRow *self)
act = g_action_map_lookup_action (self->action_map, "delete-call");
g_simple_action_set_enabled (G_SIMPLE_ACTION (act), TRUE);
self->gesture = gtk_gesture_long_press_new (GTK_WIDGET (self->event_box));
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (self->gesture), TRUE);
g_signal_connect (self->gesture, "pressed", G_CALLBACK (on_long_pressed), self);
gesture = gtk_gesture_click_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
g_signal_connect (gesture, "pressed", G_CALLBACK (calls_call_record_row_button_press_event), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
gesture = gtk_gesture_long_press_new ();
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), TRUE);
g_signal_connect (gesture, "pressed", G_CALLBACK (on_long_pressed), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
}

View file

@ -33,7 +33,7 @@
struct _CallsCallSelectorItem {
GtkEventBox parent_instance;
AdwBin parent_instance;
CuiCallDisplay *display;
@ -42,7 +42,7 @@ struct _CallsCallSelectorItem {
GtkLabel *status;
};
G_DEFINE_TYPE (CallsCallSelectorItem, calls_call_selector_item, GTK_TYPE_EVENT_BOX);
G_DEFINE_TYPE (CallsCallSelectorItem, calls_call_selector_item, ADW_TYPE_BIN);
enum {
PROP_0,
@ -66,11 +66,11 @@ static void
set_party (CallsCallSelectorItem *self)
{
CuiCall *call;
// FIXME: use HdyAvatar and the contact avatar
GtkWidget *image = gtk_image_new_from_icon_name ("avatar-default-symbolic", GTK_ICON_SIZE_DIALOG);
// FIXME: use AdwAvatar and the contact avatar
GtkWidget *image = gtk_image_new_from_icon_name ("avatar-default-symbolic");
gtk_box_pack_start (self->main_box, image, TRUE, TRUE, 0);
gtk_widget_show (image);
gtk_box_append (self->main_box, image);
gtk_widget_set_visible (image, TRUE);
call = cui_call_display_get_call (self->display);

View file

@ -25,6 +25,7 @@
#ifndef CALLS_CALL_SELECTOR_ITEM_H__
#define CALLS_CALL_SELECTOR_ITEM_H__
#include <adwaita.h>
#include <call-ui.h>
#include <gtk/gtk.h>
@ -33,7 +34,7 @@ G_BEGIN_DECLS
#define CALLS_TYPE_CALL_SELECTOR_ITEM (calls_call_selector_item_get_type ())
G_DECLARE_FINAL_TYPE (CallsCallSelectorItem, calls_call_selector_item,
CALLS, CALL_SELECTOR_ITEM, GtkEventBox);
CALLS, CALL_SELECTOR_ITEM, AdwBin);
CallsCallSelectorItem *calls_call_selector_item_new (CuiCallDisplay *display);
CuiCallDisplay *calls_call_selector_item_get_display (CallsCallSelectorItem *item);

View file

@ -35,9 +35,9 @@
#include "calls-ui-call-data.h"
#include "calls-util.h"
#include <adwaita.h>
#include <glib/gi18n.h>
#include <glib-object.h>
#include <handy.h>
struct _CallsCallWindow {
@ -45,7 +45,7 @@ struct _CallsCallWindow {
GListStore *calls;
CallsInAppNotification *in_app_notification;
AdwToastOverlay *toast_overlay;
GtkStack *main_stack;
GtkStack *header_bar_stack;
@ -129,7 +129,7 @@ call_selector_child_activated_cb (GtkFlowBox *box,
GtkFlowBoxChild *child,
CallsCallWindow *self)
{
GtkWidget *widget = gtk_bin_get_child (GTK_BIN (child));
GtkWidget *widget = gtk_flow_box_child_get_child (child);
CallsCallSelectorItem *item = CALLS_CALL_SELECTOR_ITEM (widget);
CuiCallDisplay *display = calls_call_selector_item_get_display (item);
@ -218,8 +218,7 @@ remove_call (CallsCallWindow *self,
if (display_call_data == ui_call_data) {
g_list_store_remove (self->calls, i);
gtk_container_remove (GTK_CONTAINER (self->call_stack),
GTK_WIDGET (display));
gtk_stack_remove (self->call_stack, GTK_WIDGET (display));
break;
}
}
@ -231,14 +230,11 @@ remove_call (CallsCallWindow *self,
static void
remove_calls (CallsCallWindow *self)
{
GList *children, *child;
GtkWidget *child;
/* Safely remove the call stack's children. */
children = gtk_container_get_children (GTK_CONTAINER (self->call_stack));
for (child = children; child != NULL; child = child->next)
gtk_container_remove (GTK_CONTAINER (self->call_stack),
GTK_WIDGET (child->data));
g_list_free (children);
while ((child = gtk_stack_get_visible_child (self->call_stack)))
gtk_stack_remove (self->call_stack, child);
g_list_store_remove_all (self->calls);
@ -276,7 +272,7 @@ calls_call_window_init (CallsCallWindow *self)
g_signal_connect_object (calls_manager_get_default (),
"message",
G_CALLBACK (calls_in_app_notification_show),
self->in_app_notification,
self->toast_overlay,
G_CONNECT_SWAPPED);
g_signal_connect_object (calls_manager_get_default (),
@ -322,7 +318,7 @@ calls_call_window_class_init (CallsCallWindowClass *klass)
object_class->dispose = dispose;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/call-window.ui");
gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, in_app_notification);
gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, toast_overlay);
gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, main_stack);
gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, header_bar_stack);
gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, show_calls);

View file

@ -29,21 +29,25 @@
#include <glib/gi18n.h>
#define HANDY_USE_UNSTABLE_API
#include <handy.h>
#include <adwaita.h>
struct _CallsContactsBox {
GtkBin parent_instance;
AdwBin parent_instance;
GtkWidget *child;
GtkWidget *search_entry;
GtkWidget *contacts_frame;
GtkWidget *contacts_listbox;
GtkWidget *placeholder_empty;
GListStore *contacts_list;
GtkCustomSorter *contacts_sorter;
FolksSimpleQuery *search_query;
GtkCustomFilter *search_filter;
};
G_DEFINE_TYPE (CallsContactsBox, calls_contacts_box, GTK_TYPE_BIN);
G_DEFINE_TYPE (CallsContactsBox, calls_contacts_box, ADW_TYPE_BIN);
static void
search_changed_cb (CallsContactsBox *self,
@ -51,19 +55,17 @@ search_changed_cb (CallsContactsBox *self,
{
const gchar *search_text;
search_text = gtk_entry_get_text (entry);
search_text = gtk_editable_get_text (GTK_EDITABLE (entry));
folks_simple_query_set_query_string (self->search_query, search_text);
gtk_list_box_invalidate_filter (GTK_LIST_BOX (self->contacts_listbox));
gtk_filter_changed (GTK_FILTER (self->search_filter), GTK_FILTER_CHANGE_DIFFERENT);
}
static gboolean
contacts_filter_func (CallsContactsRow *row,
contacts_filter_func (FolksIndividual *item,
CallsContactsBox *self)
{
FolksIndividual *item = calls_contacts_row_get_item (row);
return folks_query_is_match (FOLKS_QUERY (self->search_query), item) > 0;
}
@ -74,12 +76,10 @@ adjust_style (CallsContactsBox *self, GtkWidget *widget)
g_return_if_fail (CALLS_IS_CONTACTS_BOX (self));
if (gtk_widget_get_mapped (widget)) {
gtk_frame_set_shadow_type (GTK_FRAME (self->contacts_frame), GTK_SHADOW_NONE);
gtk_widget_set_vexpand (self->contacts_frame, TRUE);
gtk_style_context_add_class (gtk_widget_get_style_context (self->contacts_listbox),
"no-background");
} else {
gtk_frame_set_shadow_type (GTK_FRAME (self->contacts_frame), GTK_SHADOW_ETCHED_IN);
gtk_widget_set_vexpand (self->contacts_frame, FALSE);
gtk_style_context_remove_class (gtk_widget_get_style_context (self->contacts_listbox),
"no-background");
@ -105,18 +105,14 @@ on_favourite_changed (CallsContactsBox *self)
{
g_assert (CALLS_IS_CONTACTS_BOX (self));
gtk_list_box_invalidate_sort (GTK_LIST_BOX (self->contacts_listbox));
gtk_sorter_changed (GTK_SORTER (self->contacts_sorter), GTK_SORTER_CHANGE_DIFFERENT);
}
static void
contacts_provider_added (CallsContactsBox *self,
FolksIndividual *individual)
{
GtkWidget *row;
row = calls_contacts_row_new (individual);
gtk_container_add (GTK_CONTAINER (self->contacts_listbox), row);
g_list_store_append (G_LIST_STORE (self->contacts_list), individual);
g_signal_connect_object (individual,
"notify::is-favourite",
@ -129,30 +125,24 @@ static void
contacts_provider_removed (CallsContactsBox *self,
FolksIndividual *individual)
{
g_autoptr (GList) list = gtk_container_get_children (GTK_CONTAINER (self->contacts_listbox));
GList *l;
for (l = list; l != NULL; l = l->next) {
CallsContactsRow *row = CALLS_CONTACTS_ROW (l->data);
if (calls_contacts_row_get_item (row) == individual)
gtk_container_remove (GTK_CONTAINER (self->contacts_listbox), GTK_WIDGET (row));
guint position;
while (g_list_store_find (G_LIST_STORE (self->contacts_list), individual, &position)) {
g_list_store_remove(G_LIST_STORE (self->contacts_list), position);
}
}
static gint
contacts_sort_func (CallsContactsRow *a,
CallsContactsRow *b,
contacts_sort_func (FolksIndividual *a,
FolksIndividual *b,
gpointer unused)
{
FolksIndividual *individual_a = calls_contacts_row_get_item (a);
FolksIndividual *individual_b = calls_contacts_row_get_item (b);
const char *name_a = folks_individual_get_display_name (individual_a);
const char *name_b = folks_individual_get_display_name (individual_b);
const char *name_a = folks_individual_get_display_name (a);
const char *name_b = folks_individual_get_display_name (b);
gboolean fav_a;
gboolean fav_b;
g_object_get (G_OBJECT (individual_a), "is-favourite", &fav_a, NULL);
g_object_get (G_OBJECT (individual_b), "is-favourite", &fav_b, NULL);
g_object_get (G_OBJECT (a), "is-favourite", &fav_a, NULL);
g_object_get (G_OBJECT (b), "is-favourite", &fav_b, NULL);
if (fav_a == fav_b)
return g_strcmp0 (name_a, name_b);
@ -161,12 +151,27 @@ contacts_sort_func (CallsContactsRow *a,
}
static void
calls_contacts_box_dispose (GObject *object)
{
CallsContactsBox *self = CALLS_CONTACTS_BOX (object);
GtkWidget *child = self->child;
g_clear_pointer (&child, gtk_widget_unparent);
}
static void
calls_contacts_box_class_init (CallsContactsBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = calls_contacts_box_dispose;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/contacts-box.ui");
gtk_widget_class_bind_template_child (widget_class, CallsContactsBox, child);
gtk_widget_class_bind_template_child (widget_class, CallsContactsBox, contacts_listbox);
gtk_widget_class_bind_template_child (widget_class, CallsContactsBox, contacts_frame);
gtk_widget_class_bind_template_child (widget_class, CallsContactsBox, search_entry);
@ -178,6 +183,8 @@ static void
calls_contacts_box_init (CallsContactsBox *self)
{
CallsContactsProvider *contacts_provider;
GtkSortListModel *sorted_contacts;
GtkFilterListModel *filtered_contacts;
g_autoptr (GeeCollection) individuals = NULL;
gchar* query_fields[] = { "alias",
@ -188,23 +195,26 @@ calls_contacts_box_init (CallsContactsBox *self)
gtk_widget_init_template (GTK_WIDGET (self));
self->contacts_list = g_list_store_new(FOLKS_TYPE_INDIVIDUAL);
self->contacts_sorter = gtk_custom_sorter_new ((GCompareDataFunc) contacts_sort_func, NULL, NULL);
sorted_contacts = gtk_sort_list_model_new (G_LIST_MODEL (g_object_ref (self->contacts_list)), GTK_SORTER (g_object_ref (self->contacts_sorter)));
self->search_query = folks_simple_query_new ("", query_fields, G_N_ELEMENTS (query_fields));
self->search_filter = gtk_custom_filter_new ((GtkCustomFilterFunc) contacts_filter_func, self, NULL);
filtered_contacts = gtk_filter_list_model_new (G_LIST_MODEL (sorted_contacts), GTK_FILTER (g_object_ref (self->search_filter)));
gtk_list_box_bind_model (GTK_LIST_BOX (self->contacts_listbox),
G_LIST_MODEL (filtered_contacts),
(GtkListBoxCreateWidgetFunc) calls_contacts_row_new,
NULL,
NULL);
gtk_list_box_set_header_func (GTK_LIST_BOX (self->contacts_listbox),
(GtkListBoxUpdateHeaderFunc) header_cb,
NULL,
NULL);
gtk_list_box_set_sort_func (GTK_LIST_BOX (self->contacts_listbox),
(GtkListBoxSortFunc) contacts_sort_func,
NULL,
NULL);
gtk_list_box_set_filter_func (GTK_LIST_BOX (self->contacts_listbox),
(GtkListBoxFilterFunc) contacts_filter_func,
self,
NULL);
g_signal_connect_swapped (self->placeholder_empty, "map", G_CALLBACK (adjust_style), self);
g_signal_connect_swapped (self->placeholder_empty, "unmap", G_CALLBACK (adjust_style), self);

View file

@ -25,13 +25,14 @@
#ifndef CALLS_CONTACTS_BOX_H__
#define CALLS_CONTACTS_BOX_H__
#include <adwaita.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CALLS_TYPE_CONTACTS_BOX (calls_contacts_box_get_type ())
G_DECLARE_FINAL_TYPE (CallsContactsBox, calls_contacts_box, CALLS, CONTACTS_BOX, GtkBin);
G_DECLARE_FINAL_TYPE (CallsContactsBox, calls_contacts_box, CALLS, CONTACTS_BOX, AdwBin);
GtkWidget *calls_contacts_box_new (void);

View file

@ -10,8 +10,8 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <adwaita.h>
#include <folks/folks.h>
#include <handy.h>
#include "calls-contacts-row.h"
#include "calls-contacts-provider.h"
@ -36,18 +36,18 @@ insert_phonenumber (CallsContactsRow *self,
const gchar *number)
{
GtkWidget *label = gtk_label_new (number);
GtkWidget *button = gtk_button_new_from_icon_name ("call-start-symbolic", GTK_ICON_SIZE_BUTTON);
GtkWidget *button = gtk_button_new_from_icon_name ("call-start-symbolic");
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
gtk_widget_show (label);
gtk_widget_set_visible (label, TRUE);
gtk_grid_attach (GTK_GRID (self->grid), label, 1, self->n_phonenumbers, 1, 1);
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "app.dial");
gtk_actionable_set_action_target (GTK_ACTIONABLE (button), "s", number, NULL);
gtk_widget_show (button);
gtk_widget_set_visible (button, TRUE);
gtk_grid_attach_next_to (GTK_GRID (self->grid),
button,
label,
@ -90,7 +90,9 @@ static void
avatar_changed_cb (CallsContactsRow *self)
{
FolksAvatarDetails *avatar_details;
GLoadableIcon *icon;
GLoadableIcon *loadable_icon;
g_autoptr (GdkTexture) icon = NULL;
g_autoptr (GError) error = NULL;
g_assert (FOLKS_IS_INDIVIDUAL (self->item));
@ -98,12 +100,18 @@ avatar_changed_cb (CallsContactsRow *self)
if (avatar_details == NULL)
return;
icon = folks_avatar_details_get_avatar (avatar_details);
if (icon == NULL)
loadable_icon = folks_avatar_details_get_avatar (avatar_details);
if (!G_IS_FILE_ICON (loadable_icon)) {
return;
}
hdy_avatar_set_loadable_icon (HDY_AVATAR (self->avatar), icon);
icon = gdk_texture_new_from_file (g_file_icon_get_file (G_FILE_ICON (loadable_icon)), &error);
if (icon == NULL) {
g_print ("Failed to load avatar icon: %s", error->message);
return;
}
adw_avatar_set_custom_image (ADW_AVATAR (self->avatar), GDK_PAINTABLE (icon));
}
static void

View file

@ -27,7 +27,6 @@
#include "calls-history-box.h"
#include "calls-call-record.h"
#include "calls-call-record-row.h"
#include "gtklistmodels/gtkmodels.h"
#include "calls-util.h"
#include <glib/gi18n.h>
@ -39,8 +38,9 @@
#define CALLS_HISTORY_INCREASE_N_PAGES_THRESHOLD 2
struct _CallsHistoryBox {
GtkStack parent_instance;
AdwBin parent_instance;
GtkStack *stack;
GtkListBox *history;
GtkScrolledWindow *scrolled_window;
GtkAdjustment *scroll_adjustment;
@ -54,7 +54,7 @@ struct _CallsHistoryBox {
};
G_DEFINE_TYPE (CallsHistoryBox, calls_history_box, GTK_TYPE_STACK);
G_DEFINE_TYPE (CallsHistoryBox, calls_history_box, ADW_TYPE_BIN);
enum {
@ -80,7 +80,7 @@ on_model_changed (GListModel *model,
else
child_name = "history";
gtk_stack_set_visible_child_name (GTK_STACK (self), child_name);
gtk_stack_set_visible_child_name (self->stack, child_name);
}
@ -226,11 +226,14 @@ static void
dispose (GObject *object)
{
CallsHistoryBox *self = CALLS_HISTORY_BOX (object);
GtkWidget *stack = GTK_WIDGET (self->stack);
g_clear_signal_handler (&self->model_changed_handler_id, self->model);
g_clear_object (&self->slice_model);
g_clear_object (&self->model);
g_clear_pointer (&stack, gtk_widget_unparent);
G_OBJECT_CLASS (calls_history_box_parent_class)->dispose (object);
}
@ -256,6 +259,7 @@ calls_history_box_class_init (CallsHistoryBoxClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/history-box.ui");
gtk_widget_class_bind_template_child (widget_class, CallsHistoryBox, stack);
gtk_widget_class_bind_template_child (widget_class, CallsHistoryBox, history);
gtk_widget_class_bind_template_child (widget_class, CallsHistoryBox, scrolled_window);
}

View file

@ -27,13 +27,14 @@
#include "calls-new-call-box.h"
#include <adwaita.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CALLS_TYPE_HISTORY_BOX (calls_history_box_get_type ())
G_DECLARE_FINAL_TYPE (CallsHistoryBox, calls_history_box, CALLS, HISTORY_BOX, GtkStack);
G_DECLARE_FINAL_TYPE (CallsHistoryBox, calls_history_box, CALLS, HISTORY_BOX, AdwBin);
CallsHistoryBox *calls_history_box_new (GListModel *model);

View file

@ -26,148 +26,14 @@
#define DEFAULT_TIMEOUT_SECONDS 3
struct _CallsInAppNotification {
GtkRevealer parent_instance;
GtkLabel *label;
guint timeout;
guint timeout_id;
};
G_DEFINE_TYPE (CallsInAppNotification, calls_in_app_notification, GTK_TYPE_REVEALER)
enum {
PROP_0,
PROP_TIMEOUT,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
static gboolean
timeout_cb (CallsInAppNotification *self)
{
calls_in_app_notification_hide (self);
return G_SOURCE_REMOVE;
}
static void
calls_in_app_notification_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
CallsInAppNotification *self = CALLS_IN_APP_NOTIFICATION (object);
switch (property_id) {
case PROP_TIMEOUT:
g_value_set_uint (value, self->timeout);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
calls_in_app_notification_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
CallsInAppNotification *self = CALLS_IN_APP_NOTIFICATION (object);
switch (property_id) {
case PROP_TIMEOUT:
self->timeout = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
calls_in_app_notification_finalize (GObject *object)
{
CallsInAppNotification *self = CALLS_IN_APP_NOTIFICATION (object);
g_clear_handle_id (&self->timeout_id, g_source_remove);
G_OBJECT_CLASS (calls_in_app_notification_parent_class)->finalize (object);
}
static void
calls_in_app_notification_class_init (CallsInAppNotificationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->get_property = calls_in_app_notification_get_property;
object_class->set_property = calls_in_app_notification_set_property;
object_class->finalize = calls_in_app_notification_finalize;
props[PROP_TIMEOUT] = g_param_spec_uint ("timeout",
"Timeout",
"The time the in-app notifaction should be shown",
1,
G_MAXUINT,
DEFAULT_TIMEOUT_SECONDS,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/in-app-notification.ui");
gtk_widget_class_bind_template_child (widget_class, CallsInAppNotification, label);
gtk_widget_class_bind_template_callback (widget_class, calls_in_app_notification_hide);
}
static void
calls_in_app_notification_init (CallsInAppNotification *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
CallsInAppNotification *
calls_in_app_notification_new (void)
{
return g_object_new (CALLS_TYPE_IN_APP_NOTIFICATION, NULL);
}
void
calls_in_app_notification_show (CallsInAppNotification *self, const gchar *message)
calls_in_app_notification_show (AdwToastOverlay *overlay, const gchar *message)
{
g_return_if_fail (CALLS_IS_IN_APP_NOTIFICATION (self));
AdwToast* toast;
gtk_label_set_text (self->label, message);
g_return_if_fail (ADW_IS_TOAST_OVERLAY (overlay));
if (self->timeout_id)
g_source_remove (self->timeout_id);
gtk_revealer_set_reveal_child (GTK_REVEALER (self), TRUE);
self->timeout_id = g_timeout_add_seconds (self->timeout, (GSourceFunc) timeout_cb, self);
}
void
calls_in_app_notification_hide (CallsInAppNotification *self)
{
g_return_if_fail (CALLS_IS_IN_APP_NOTIFICATION (self));
g_clear_handle_id (&self->timeout_id, g_source_remove);
gtk_revealer_set_reveal_child (GTK_REVEALER (self), FALSE);
toast = adw_toast_new (message);
adw_toast_set_timeout (toast, DEFAULT_TIMEOUT_SECONDS);
adw_toast_overlay_add_toast (overlay, toast);
}

View file

@ -25,17 +25,11 @@
#ifndef CALLS_IN_APP_NOTIFICATION_H__
#define CALLS_IN_APP_NOTIFICATION_H__
#include <adwaita.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CALLS_TYPE_IN_APP_NOTIFICATION (calls_in_app_notification_get_type ())
G_DECLARE_FINAL_TYPE (CallsInAppNotification, calls_in_app_notification, CALLS, IN_APP_NOTIFICATION, GtkRevealer)
CallsInAppNotification * calls_in_app_notification_new (void);
void calls_in_app_notification_show (CallsInAppNotification *self, const gchar *message);
void calls_in_app_notification_hide (CallsInAppNotification *self);
void calls_in_app_notification_show (AdwToastOverlay *overlay, const gchar *message);
G_END_DECLS

View file

@ -36,20 +36,20 @@
#include "calls-util.h"
#include "version.h"
#include <adwaita.h>
#include <glib/gi18n.h>
#include <glib-object.h>
#include <handy.h>
struct _CallsMainWindow {
HdyApplicationWindow parent_instance;
AdwApplicationWindow parent_instance;
GListModel *record_store;
CallsInAppNotification *in_app_notification;
AdwToastOverlay *toast_overlay;
HdyViewSwitcherTitle *title_switcher;
GtkStack *main_stack;
AdwViewSwitcherTitle *title_switcher;
AdwViewStack *main_stack;
GtkRevealer *permanent_error_revealer;
GtkLabel *permanent_error_label;
@ -68,7 +68,7 @@ struct _CallsMainWindow {
GtkButton *ussd_reply_button;
};
G_DEFINE_TYPE (CallsMainWindow, calls_main_window, HDY_TYPE_APPLICATION_WINDOW);
G_DEFINE_TYPE (CallsMainWindow, calls_main_window, ADW_TYPE_APPLICATION_WINDOW);
enum {
PROP_0,
@ -157,9 +157,9 @@ window_update_ussd_state (CallsMainWindow *self,
if (state == CALLS_USSD_STATE_USER_RESPONSE ||
state == CALLS_USSD_STATE_ACTIVE)
gtk_widget_show (GTK_WIDGET (self->ussd_cancel_button));
gtk_widget_set_visible (GTK_WIDGET (self->ussd_cancel_button), TRUE);
else
gtk_widget_show (GTK_WIDGET (self->ussd_close_button));
gtk_widget_set_visible (GTK_WIDGET (self->ussd_close_button), TRUE);
}
static void
@ -177,6 +177,7 @@ window_ussd_added_cb (CallsMainWindow *self,
g_object_set_data_full (G_OBJECT (self->ussd_dialog), "ussd",
g_object_ref (ussd), g_object_unref);
window_update_ussd_state (self, ussd);
gtk_window_set_title (GTK_WINDOW (self->ussd_dialog), _("USSD"));
gtk_window_present (GTK_WINDOW (self->ussd_dialog));
}
@ -205,7 +206,7 @@ window_ussd_entry_changed_cb (CallsMainWindow *self,
g_assert (CALLS_IS_MAIN_WINDOW (self));
g_assert (GTK_IS_ENTRY (entry));
text = gtk_entry_get_text (entry);
text = gtk_editable_get_text (GTK_EDITABLE (entry));
allow_send = text && *text;
gtk_widget_set_sensitive (GTK_WIDGET (self->ussd_reply_button), allow_send);
@ -250,8 +251,8 @@ window_ussd_reply_clicked_cb (CallsMainWindow *self)
ussd = g_object_get_data (G_OBJECT (self->ussd_dialog), "ussd");
g_assert (CALLS_IS_USSD (ussd));
response = g_strdup (gtk_entry_get_text (self->ussd_entry));
gtk_entry_set_text (self->ussd_entry, "");
response = g_strdup (gtk_editable_get_text (GTK_EDITABLE (self->ussd_entry)));
gtk_editable_set_text (GTK_EDITABLE (self->ussd_entry), "");
calls_ussd_respond_async (ussd, response, NULL,
window_ussd_respond_cb, self);
}
@ -328,7 +329,7 @@ constructed (GObject *object)
{
CallsMainWindow *self = CALLS_MAIN_WINDOW (object);
GSimpleActionGroup *simple_action_group;
GtkContainer *main_stack = GTK_CONTAINER (self->main_stack);
AdwViewStackPage *page;
GtkWidget *widget;
CallsHistoryBox *history;
@ -336,7 +337,7 @@ constructed (GObject *object)
g_signal_connect_object (calls_manager_get_default (),
"message",
G_CALLBACK (calls_in_app_notification_show),
self->in_app_notification,
self->toast_overlay,
G_CONNECT_SWAPPED);
g_signal_connect_object (calls_manager_get_default (),
@ -351,36 +352,30 @@ constructed (GObject *object)
G_CONNECT_SWAPPED);
gtk_window_set_transient_for (GTK_WINDOW (self->ussd_dialog), GTK_WINDOW (self));
// Add contacs box
// Add call records
history = calls_history_box_new (self->record_store);
widget = GTK_WIDGET (history);
adw_view_stack_add_titled (self->main_stack, widget,
/* Recent as in "Recent calls" (the call history) */
"recent", _("Recent"));
page = adw_view_stack_get_page (self->main_stack, widget);
adw_view_stack_page_set_icon_name (page, "document-open-recent-symbolic");
adw_view_stack_set_visible_child_name (self->main_stack, "recent");
// Add contacts box
widget = calls_contacts_box_new ();
gtk_stack_add_titled (self->main_stack, widget,
"contacts", _("Contacts"));
gtk_container_child_set (main_stack, widget,
"icon-name", "system-users-symbolic",
NULL);
gtk_widget_set_visible (widget, TRUE);
adw_view_stack_add_titled (self->main_stack, widget,
"contacts", _("Contacts"));
page = adw_view_stack_get_page (self->main_stack, widget);
adw_view_stack_page_set_icon_name (page, "system-users-symbolic");
// Add new call box
self->new_call = calls_new_call_box_new ();
widget = GTK_WIDGET (self->new_call);
gtk_stack_add_titled (self->main_stack, widget,
"dial-pad", _("Dial Pad"));
gtk_container_child_set (main_stack, widget,
"icon-name", "input-dialpad-symbolic",
NULL);
// Add call records
history = calls_history_box_new (self->record_store);
widget = GTK_WIDGET (history);
gtk_stack_add_titled (self->main_stack, widget,
/* Recent as in "Recent calls" (the call history) */
"recent", _("Recent"));
gtk_container_child_set
(main_stack, widget,
"icon-name", "document-open-recent-symbolic",
"position", 0,
NULL);
gtk_widget_set_visible (widget, TRUE);
gtk_stack_set_visible_child_name (self->main_stack, "recent");
adw_view_stack_add_titled (self->main_stack, widget,
"dial-pad", _("Dial Pad"));
page = adw_view_stack_get_page (self->main_stack, widget);
adw_view_stack_page_set_icon_name (page, "input-dialpad-symbolic");
// Add actions
simple_action_group = g_simple_action_group_new ();
@ -414,7 +409,7 @@ dispose (GObject *object)
g_clear_object (&self->record_store);
if (self->account_overview) {
gtk_widget_destroy (GTK_WIDGET (self->account_overview));
gtk_window_destroy (GTK_WINDOW (self->account_overview));
self->account_overview = NULL;
}
@ -422,19 +417,6 @@ dispose (GObject *object)
}
static void
size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
CallsMainWindow *self = CALLS_MAIN_WINDOW (widget);
hdy_view_switcher_title_set_view_switcher_enabled (self->title_switcher,
allocation->width > 400);
GTK_WIDGET_CLASS (calls_main_window_parent_class)->size_allocate (widget, allocation);
}
static void
calls_main_window_class_init (CallsMainWindowClass *klass)
{
@ -455,11 +437,8 @@ calls_main_window_class_init (CallsMainWindowClass *klass)
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
widget_class->size_allocate = size_allocate;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/main-window.ui");
gtk_widget_class_bind_template_child (widget_class, CallsMainWindow, in_app_notification);
gtk_widget_class_bind_template_child (widget_class, CallsMainWindow, title_switcher);
gtk_widget_class_bind_template_child (widget_class, CallsMainWindow, toast_overlay);
gtk_widget_class_bind_template_child (widget_class, CallsMainWindow, main_stack);
gtk_widget_class_bind_template_child (widget_class, CallsMainWindow, permanent_error_revealer);
gtk_widget_class_bind_template_child (widget_class, CallsMainWindow, permanent_error_label);
@ -506,8 +485,8 @@ calls_main_window_dial (CallsMainWindow *self,
const gchar *target)
{
if (calls_number_is_ussd (target)) {
gtk_widget_hide (GTK_WIDGET (self->ussd_cancel_button));
gtk_widget_hide (GTK_WIDGET (self->ussd_reply_button));
gtk_widget_set_visible (GTK_WIDGET (self->ussd_cancel_button), FALSE);
gtk_widget_set_visible (GTK_WIDGET (self->ussd_reply_button), FALSE);
gtk_stack_set_visible_child (self->ussd_stack, GTK_WIDGET (self->ussd_spinner));
gtk_spinner_start (self->ussd_spinner);

View file

@ -25,13 +25,13 @@
#ifndef CALLS_MAIN_WINDOW_H__
#define CALLS_MAIN_WINDOW_H__
#include <handy.h>
#include <adwaita.h>
G_BEGIN_DECLS
#define CALLS_TYPE_MAIN_WINDOW (calls_main_window_get_type ())
G_DECLARE_FINAL_TYPE (CallsMainWindow, calls_main_window, CALLS, MAIN_WINDOW, HdyApplicationWindow);
G_DECLARE_FINAL_TYPE (CallsMainWindow, calls_main_window, CALLS, MAIN_WINDOW, AdwApplicationWindow);
CallsMainWindow *calls_main_window_new (GtkApplication *application,
GListModel *record_store);

View file

@ -40,10 +40,6 @@
#include "enum-types.h"
#include "gtkcustomfilter.h"
#include "gtkfilterlistmodel.h"
#include "gtkflattenlistmodel.h"
#include <glib/gi18n.h>
#include <libpeas/peas.h>
@ -451,12 +447,8 @@ calls_manager_finalize (GObject *object)
{
CallsManager *self = CALLS_MANAGER (object);
g_clear_object (&self->origins);
g_clear_object (&self->contacts_provider);
g_clear_pointer (&self->origins_by_protocol, g_hash_table_unref);
g_clear_pointer (&self->dial_actions_by_protocol, g_hash_table_unref);
G_OBJECT_CLASS (calls_manager_parent_class)->finalize (object);
}
@ -599,7 +591,7 @@ calls_manager_init (CallsManager *self)
self->state_flags = CALLS_MANAGER_FLAGS_UNKNOWN;
self->origins = g_list_store_new (G_TYPE_LIST_MODEL); /* list of lists */
self->origins_flat = gtk_flatten_list_model_new (CALLS_TYPE_ORIGIN, G_LIST_MODEL (self->origins));
self->origins_flat = gtk_flatten_list_model_new (G_LIST_MODEL (self->origins));
providers = calls_plugin_manager_get_providers (plugin_manager);
g_signal_connect_object (providers,
@ -616,8 +608,8 @@ calls_manager_init (CallsManager *self)
g_object_unref);
for (guint i = 0; i < G_N_ELEMENTS (protocols); i++) {
g_autoptr (GtkFilter) filter =
gtk_custom_filter_new (match_origin_supports_protocol, (gpointer) protocols[i], NULL);
GtkFilter* filter =
GTK_FILTER (gtk_custom_filter_new (match_origin_supports_protocol, (gpointer) protocols[i], NULL));
GtkFilterListModel *f_list =
gtk_filter_list_model_new (G_LIST_MODEL (self->origins_flat), filter);

View file

@ -32,9 +32,9 @@
#include "calls-ussd.h"
#include "calls-util.h"
#include <adwaita.h>
#include <call-ui.h>
#include <glib/gi18n.h>
#include <handy.h>
enum {
PROP_0,
@ -44,13 +44,15 @@ enum {
static GParamSpec *props[PROP_LAST_PROP];
struct _CallsNewCallBox {
GtkBox parent_instance;
AdwBin parent_instance;
GtkWidget *child;
GtkListBox *origin_list_box;
HdyComboRow *origin_list;
AdwComboRow *origin_list;
CuiDialpad *dialpad;
GtkEntry *address_entry;
HdyActionRow *result;
AdwActionRow *result;
GtkButton *dial_result;
GList *dial_queue;
@ -58,18 +60,18 @@ struct _CallsNewCallBox {
gboolean numeric_input_only;
};
G_DEFINE_TYPE (CallsNewCallBox, calls_new_call_box, GTK_TYPE_BOX);
G_DEFINE_TYPE (CallsNewCallBox, calls_new_call_box, ADW_TYPE_BIN);
static CallsOrigin *
get_selected_origin (CallsNewCallBox *self)
{
g_autoptr (CallsOrigin) origin = NULL;
GListModel *model = hdy_combo_row_get_model (self->origin_list);
GListModel *model = adw_combo_row_get_model (self->origin_list);
gint index = -1;
if (model)
index = hdy_combo_row_get_selected_index (self->origin_list);
index = adw_combo_row_get_selected (self->origin_list);
if (model && index >= 0)
origin = g_list_model_get_item (model, index);
@ -125,7 +127,7 @@ static void
address_activate_cb (CallsNewCallBox *self)
{
CallsOrigin *origin = get_selected_origin (self);
const char *address = gtk_entry_get_text (self->address_entry);
const char *address = gtk_editable_get_text (GTK_EDITABLE (self->address_entry));
if (origin && !STR_IS_NULL_OR_EMPTY (address))
calls_origin_dial (origin, address);
@ -135,7 +137,7 @@ address_activate_cb (CallsNewCallBox *self)
static void
address_changed_cb (CallsNewCallBox *self)
{
const char *address = gtk_entry_get_text (self->address_entry);
const char *address = gtk_editable_get_text (GTK_EDITABLE (self->address_entry));
gtk_widget_set_visible (GTK_WIDGET (self->result),
!STR_IS_NULL_OR_EMPTY (address));
@ -201,14 +203,14 @@ dialpad_dialed_cb (CuiDialpad *dialpad,
const char *number,
CallsNewCallBox *self)
{
GtkWidget *window;
GtkRoot *root;
g_assert (CALLS_IS_NEW_CALL_BOX (self));
window = gtk_widget_get_toplevel (GTK_WIDGET (self));
root = gtk_widget_get_root (GTK_WIDGET (self));
if (CALLS_IS_MAIN_WINDOW (window))
calls_main_window_dial (CALLS_MAIN_WINDOW (window), number);
if (CALLS_IS_MAIN_WINDOW (root))
calls_main_window_dial (CALLS_MAIN_WINDOW (root), number);
else
calls_new_call_box_dial (self, number);
}
@ -218,7 +220,7 @@ static void
dial_result_clicked_cb (CallsNewCallBox *self)
{
CallsOrigin *origin = get_selected_origin (self);
const char *address = gtk_entry_get_text (self->address_entry);
const char *address = gtk_editable_get_text (GTK_EDITABLE (self->address_entry));
if (origin && address && *address != '\0')
calls_origin_dial (origin, address);
@ -269,15 +271,6 @@ dial_queued (CallsNewCallBox *self)
}
static char *
get_origin_name (gpointer item,
gpointer user_data)
{
g_assert (CALLS_IS_ORIGIN (item));
return calls_origin_get_name (item);
}
static void
origin_count_changed_cb (CallsNewCallBox *self)
{
@ -322,12 +315,15 @@ static void
calls_new_call_box_init (CallsNewCallBox *self)
{
GListModel *origins;
GtkExpression *get_origin_name;
gtk_widget_init_template (GTK_WIDGET (self));
origins = calls_manager_get_origins (calls_manager_get_default ());
hdy_combo_row_bind_name_model (self->origin_list, origins,
get_origin_name, self, NULL);
adw_combo_row_set_model (self->origin_list, origins);
get_origin_name = gtk_property_expression_new (CALLS_TYPE_ORIGIN,
NULL, "name");
adw_combo_row_set_expression(self->origin_list, get_origin_name);
g_signal_connect_object (origins,
"items-changed",
@ -343,8 +339,12 @@ calls_new_call_box_dispose (GObject *object)
{
CallsNewCallBox *self = CALLS_NEW_CALL_BOX (object);
GtkWidget *child = self->child;
clear_dial_queue (self);
g_clear_pointer (&child, gtk_widget_unparent);
G_OBJECT_CLASS (calls_new_call_box_parent_class)->dispose (object);
}
@ -359,6 +359,7 @@ calls_new_call_box_class_init (CallsNewCallBoxClass *klass)
object_class->dispose = calls_new_call_box_dispose;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/new-call-box.ui");
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, child);
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, origin_list_box);
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, origin_list);
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, dialpad);

View file

@ -24,13 +24,14 @@
#pragma once
#include <adwaita.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CALLS_TYPE_NEW_CALL_BOX (calls_new_call_box_get_type ())
G_DECLARE_FINAL_TYPE (CallsNewCallBox, calls_new_call_box, CALLS, NEW_CALL_BOX, GtkBox)
G_DECLARE_FINAL_TYPE (CallsNewCallBox, calls_new_call_box, CALLS, NEW_CALL_BOX, AdwBin)
CallsNewCallBox *calls_new_call_box_new (void);
void calls_new_call_box_dial (CallsNewCallBox *self,

View file

@ -162,8 +162,14 @@ calls_plugin_manager_dispose (GObject *object)
{
CallsPluginManager *self = CALLS_PLUGIN_MANAGER (object);
guint n = g_list_model_get_n_items (G_LIST_MODEL (self->plugins));
for (guint i = 0; i < n; i++) {
// The manager will be disposed immediately; don't let unloaded plugins callback to it
g_autoptr (CallsPlugin) plugin = g_list_model_get_item (G_LIST_MODEL (self->plugins), i);
g_signal_handlers_disconnect_by_func (G_OBJECT (plugin), G_CALLBACK (on_plugin_loaded), self);
}
g_clear_pointer (&self->plugins, unload_and_free_plugins);
g_clear_object (&self->providers);
g_clear_object (&self->plugin_engine);
G_OBJECT_CLASS (calls_plugin_manager_parent_class)->dispose (object);

View file

@ -92,14 +92,16 @@ record_state_to_string (CallsCallRecordState state)
struct _CallsRecordStore {
GtkApplicationWindow parent_instance;
GObject parent_instance;
gchar *filename;
GomAdapter *adapter;
GomRepository *repository;
GListStore *list_store;
};
G_DEFINE_TYPE (CallsRecordStore, calls_record_store, G_TYPE_LIST_STORE);
G_DEFINE_TYPE (CallsRecordStore, calls_record_store, G_TYPE_OBJECT);
static void
@ -191,7 +193,7 @@ load_calls_fetch_cb (GomResourceGroup *group,
self);
}
g_list_store_splice (G_LIST_STORE (self),
g_list_store_splice (G_LIST_STORE (self->list_store),
0,
0,
records,
@ -442,7 +444,7 @@ record_call_save_cb (GomResource *resource,
} else {
g_debug ("Successfully saved new call record to database");
g_list_store_insert (G_LIST_STORE (data->self),
g_list_store_insert (G_LIST_STORE (data->self->list_store),
0,
CALLS_CALL_RECORD (resource));
@ -699,8 +701,6 @@ dispose (GObject *object)
{
CallsRecordStore *self = CALLS_RECORD_STORE (object);
g_list_store_remove_all (G_LIST_STORE (self));
g_clear_object (&self->repository);
G_OBJECT_CLASS (calls_record_store_parent_class)->dispose (object);
@ -780,13 +780,20 @@ calls_record_store_init (CallsRecordStore *self)
self->filename = g_build_filename (used_dir,
RECORD_STORE_FILENAME,
NULL);
self->list_store = g_list_store_new (CALLS_TYPE_CALL_RECORD);
}
CallsRecordStore *
calls_record_store_new (void)
{
return g_object_new (CALLS_TYPE_RECORD_STORE,
"item-type", CALLS_TYPE_CALL_RECORD,
NULL);
return g_object_new (CALLS_TYPE_RECORD_STORE, NULL);
}
GListModel *
calls_record_store_get_list_model (CallsRecordStore *self)
{
return G_LIST_MODEL (self->list_store);
}

View file

@ -31,9 +31,10 @@ G_BEGIN_DECLS
#define CALLS_TYPE_RECORD_STORE (calls_record_store_get_type ())
G_DECLARE_FINAL_TYPE (CallsRecordStore, calls_record_store, CALLS, RECORD_STORE, GListStore);
G_DECLARE_FINAL_TYPE (CallsRecordStore, calls_record_store, CALLS, RECORD_STORE, GObject);
CallsRecordStore *calls_record_store_new (void);
GListModel *calls_record_store_get_list_model (CallsRecordStore *);
G_END_DECLS

View file

@ -138,14 +138,14 @@ calls_ui_call_data_get_can_dtmf (CuiCall *call_data)
}
static GLoadableIcon *
static GdkPaintable *
calls_ui_call_data_get_avatar_icon (CuiCall *call_data)
{
CallsUiCallData *self = (CallsUiCallData *) call_data;
g_return_val_if_fail (CALLS_UI_CALL_DATA (self), NULL);
return calls_best_match_get_avatar (self->best_match);
return GDK_PAINTABLE (calls_best_match_get_avatar (self->best_match));
}

View file

@ -9,7 +9,6 @@
<file preprocess="xml-stripblanks">contacts-box.ui</file>
<file preprocess="xml-stripblanks">contacts-row.ui</file>
<file preprocess="xml-stripblanks">history-box.ui</file>
<file preprocess="xml-stripblanks">in-app-notification.ui</file>
<file preprocess="xml-stripblanks">main-window.ui</file>
<file preprocess="xml-stripblanks">new-call-box.ui</file>
<file preprocess="xml-stripblanks">new-call-header-bar.ui</file>

View file

@ -1,157 +0,0 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
/* #include "calls-config.h" */
#include "gtkcustomfilter.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
/**
* SECTION:gtkcustomfilter
* @Title: GtkCustomFilter
* @Short_description: Filtering with callbacks
*
* #GtkCustomFilter is a #GtkFilter that uses a callback to determine whether
* to include an item or not.
*/
struct _GtkCustomFilter
{
GtkFilter parent_instance;
GtkCustomFilterFunc match_func;
gpointer user_data;
GDestroyNotify user_destroy;
};
G_DEFINE_TYPE (GtkCustomFilter, gtk_custom_filter, GTK_TYPE_FILTER)
static gboolean
gtk_custom_filter_match (GtkFilter *filter,
gpointer item)
{
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
if (!self->match_func)
return TRUE;
return self->match_func (item, self->user_data);
}
static GtkFilterMatch
gtk_custom_filter_get_strictness (GtkFilter *filter)
{
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
if (!self->match_func)
return GTK_FILTER_MATCH_ALL;
return GTK_FILTER_MATCH_SOME;
}
static void
gtk_custom_filter_dispose (GObject *object)
{
GtkCustomFilter *self = GTK_CUSTOM_FILTER (object);
if (self->user_destroy)
self->user_destroy (self->user_data);
G_OBJECT_CLASS (gtk_custom_filter_parent_class)->dispose (object);
}
static void
gtk_custom_filter_class_init (GtkCustomFilterClass *class)
{
GtkFilterClass *filter_class = GTK_FILTER_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
filter_class->match = gtk_custom_filter_match;
filter_class->get_strictness = gtk_custom_filter_get_strictness;
object_class->dispose = gtk_custom_filter_dispose;
}
static void
gtk_custom_filter_init (GtkCustomFilter *self)
{
}
/**
* gtk_custom_filter_new:
* @match_func: (nullable): function to filter items
* @user_data: (nullable): user data to pass to @match_func
* @user_destroy: destory notify
*
* Creates a new filter using the given @match_func to filter
* items.
*
* If the filter func changes its filtering behavior,
* gtk_filter_changed() needs to be called.
*
* Returns: a new #GtkFilter
**/
GtkFilter *
gtk_custom_filter_new (GtkCustomFilterFunc match_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
GtkCustomFilter *result;
result = g_object_new (GTK_TYPE_CUSTOM_FILTER, NULL);
gtk_custom_filter_set_filter_func (result, match_func, user_data, user_destroy);
return GTK_FILTER (result);
}
/**
* gtk_custom_filter_set_filter_func:
* @self: a #GtkCustomFilter
* @match_func: (nullable): function to filter items
* @user_data: (nullable): user data to pass to @match_func
* @user_destroy: destory notify
*
* Sets (or unsets) the function used for filtering items.
*
* If the filter func changes its filtering behavior,
* gtk_filter_changed() needs to be called.
*
* If a previous function was set, its @user_destroy will be
* called now.
**/
void
gtk_custom_filter_set_filter_func (GtkCustomFilter *self,
GtkCustomFilterFunc match_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
g_return_if_fail (GTK_IS_CUSTOM_FILTER (self));
g_return_if_fail (match_func || (user_data == NULL && !user_destroy));
if (self->user_destroy)
self->user_destroy (self->user_data);
self->match_func = match_func;
self->user_data = user_data;
self->user_destroy = user_destroy;
gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_DIFFERENT);
}

View file

@ -1,61 +0,0 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_CUSTOM_FILTER_H__
#define __GTK_CUSTOM_FILTER_H__
#define GTK_COMPILATION
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include "gtkfilter.h"
G_BEGIN_DECLS
/**
* GtkCustomFilterFunc:
* @item: (type GObject): The item to be matched
* @user_data: user data
*
* User function that is called to determine if the @item should be matched.
* If the filter matches the item, this function must return %TRUE. If the
* item should be filtered out, %FALSE must be returned.
*
* Returns: %TRUE to keep the item around
*/
typedef gboolean (* GtkCustomFilterFunc) (gpointer item, gpointer user_data);
#define GTK_TYPE_CUSTOM_FILTER (gtk_custom_filter_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkCustomFilter, gtk_custom_filter, GTK, CUSTOM_FILTER, GtkFilter)
GDK_AVAILABLE_IN_ALL
GtkFilter * gtk_custom_filter_new (GtkCustomFilterFunc match_func,
gpointer user_data,
GDestroyNotify user_destroy);
GDK_AVAILABLE_IN_ALL
void gtk_custom_filter_set_filter_func (GtkCustomFilter *self,
GtkCustomFilterFunc match_func,
gpointer user_data,
GDestroyNotify user_destroy);
G_END_DECLS
#endif /* __GTK_CUSTOM_FILTER_H__ */

View file

@ -1,165 +0,0 @@
/*
* Copyright © 2019 Matthias Clasen
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
/* #include "calls-config.h" */
#include "gtkcustomsorter.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
/**
* SECTION:gtkcustomsorter
* @Title: GtkCustomSorter
* @Short_description: Sorting with a callback
*
* GtkCustomSorter is a #GtkSorter implementation that sorts
* via a traditional #GCompareDataFunc callback.
*/
struct _GtkCustomSorter
{
GtkSorter parent_instance;
GCompareDataFunc sort_func;
gpointer user_data;
GDestroyNotify user_destroy;
};
G_DEFINE_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK_TYPE_SORTER)
static inline GtkOrdering
gtk_ordering_from_cmpfunc (int cmpfunc_result)
{
return (GtkOrdering) ((cmpfunc_result > 0) - (cmpfunc_result < 0));
}
static GtkOrdering
gtk_custom_sorter_compare (GtkSorter *sorter,
gpointer item1,
gpointer item2)
{
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
if (!self->sort_func)
return GTK_ORDERING_EQUAL;
return gtk_ordering_from_cmpfunc (self->sort_func (item1, item2, self->user_data));
}
static GtkSorterOrder
gtk_custom_sorter_get_order (GtkSorter *sorter)
{
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
if (!self->sort_func)
return GTK_SORTER_ORDER_NONE;
return GTK_SORTER_ORDER_PARTIAL;
}
static void
gtk_custom_sorter_dispose (GObject *object)
{
GtkCustomSorter *self = GTK_CUSTOM_SORTER (object);
if (self->user_destroy)
self->user_destroy (self->user_data);
self->sort_func = NULL;
self->user_destroy = NULL;
self->user_data = NULL;
G_OBJECT_CLASS (gtk_custom_sorter_parent_class)->dispose (object);
}
static void
gtk_custom_sorter_class_init (GtkCustomSorterClass *class)
{
GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
sorter_class->compare = gtk_custom_sorter_compare;
sorter_class->get_order = gtk_custom_sorter_get_order;
object_class->dispose = gtk_custom_sorter_dispose;
}
static void
gtk_custom_sorter_init (GtkCustomSorter *self)
{
}
/**
* gtk_custom_sorter_new:
* @sort_func: the #GCompareDataFunc to use for sorting
* @user_data: (nullable): user data to pass to @sort_func
* @user_destroy: (nullable): destroy notify for @user_data
*
* Creates a new #GtkSorter that works by calling
* @sort_func to compare items.
*
* Returns: a new #GTkSorter
*/
GtkSorter *
gtk_custom_sorter_new (GCompareDataFunc sort_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
GtkCustomSorter *sorter;
sorter = g_object_new (GTK_TYPE_CUSTOM_SORTER, NULL);
gtk_custom_sorter_set_sort_func (sorter, sort_func, user_data, user_destroy);
return GTK_SORTER (sorter);
}
/**
* gtk_custom_sorter_set_sort_func:
* @self: a #GtkCustomSorter
* @sort_func: (nullable): function to sort items
* @user_data: (nullable): user data to pass to @match_func
* @user_destroy: destory notify
*
* Sets (or unsets) the function used for sorting items.
*
* If the sort func changes its sorting behavior,
* gtk_sorter_changed() needs to be called.
*
* If a previous function was set, its @user_destroy will be
* called now.
**/
void
gtk_custom_sorter_set_sort_func (GtkCustomSorter *self,
GCompareDataFunc sort_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
g_return_if_fail (GTK_IS_CUSTOM_SORTER (self));
g_return_if_fail (sort_func || (user_data == NULL && !user_destroy));
if (self->user_destroy)
self->user_destroy (self->user_data);
self->sort_func = sort_func;
self->user_data = user_data;
self->user_destroy = user_destroy;
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
}

View file

@ -1,50 +0,0 @@
/*
* Copyright © 2019 Matthias Clasen
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#ifndef __GTK_CUSTOM_SORTER_H__
#define __GTK_CUSTOM_SORTER_H__
#define GTK_COMPILATION
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
/* #include <gtk/gtkexpression.h> */
#include "gtksorter.h"
G_BEGIN_DECLS
#define GTK_TYPE_CUSTOM_SORTER (gtk_custom_sorter_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK, CUSTOM_SORTER, GtkSorter)
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_custom_sorter_new (GCompareDataFunc sort_func,
gpointer user_data,
GDestroyNotify user_destroy);
GDK_AVAILABLE_IN_ALL
void gtk_custom_sorter_set_sort_func (GtkCustomSorter *self,
GCompareDataFunc sort_func,
gpointer user_data,
GDestroyNotify user_destroy);
G_END_DECLS
#endif /* __GTK_CUSTOM_SORTER_H__ */

View file

@ -1,181 +0,0 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
/* #include "calls-config.h" */
#include "gtkfilter.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
/**
* SECTION:gtkfilter
* @Title: GtkFilter
* @Short_description: Filtering items
* @See_also: #GtkFilerListModel
*
* #GtkFilter is the way to describe filters to be used in #GtkFilterListModel.
*
* The model will use a filter to determine if it should filter items or not
* by calling gtk_filter_match() for each item and only keeping the ones
* visible that the function returns %TRUE for.
*
* Filters may change what items they match through their lifetime. In that
* case, they can call gtk_filter_changed() which will emit the #GtkFilter:changed
* signal to notify that previous filter results are no longer valid and that
* items should be checked via gtk_filter_match() again.
*
* GTK provides various premade filter implementations for common filtering
* operations. These filters often include properties that can be linked to
* various widgets to easily allow searches.
*
* However, in particular for large lists or complex search methods, it is
* also possible to subclass #GtkFilter and provide one's own filter.
*/
enum {
CHANGED,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkFilter, gtk_filter, G_TYPE_OBJECT)
static guint signals[LAST_SIGNAL] = { 0 };
static gboolean
gtk_filter_default_match (GtkFilter *self,
gpointer item)
{
g_critical ("Filter of type '%s' does not implement GtkFilter::match", G_OBJECT_TYPE_NAME (self));
return FALSE;
}
static GtkFilterMatch
gtk_filter_default_get_strictness (GtkFilter *self)
{
return GTK_FILTER_MATCH_SOME;
}
static void
gtk_filter_class_init (GtkFilterClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
class->match = gtk_filter_default_match;
class->get_strictness = gtk_filter_default_get_strictness;
/**
* GtkFilter:changed:
* @self: The #GtkFilter
* @change: how the filter changed
*
* This signal is emitted whenever the filter changed. Users of the filter
* should then check items again via gtk_filter_match().
*
* #GtkFilterListModel handles this signal automatically.
*
* Depending on the @change parameter, not all items need to be changed, but
* only some. Refer to the #GtkFilterChange documentation for details.
*/
signals[CHANGED] =
g_signal_new (I_("changed"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__ENUM,
G_TYPE_NONE, 1,
GTK_TYPE_FILTER_CHANGE);
g_signal_set_va_marshaller (signals[CHANGED],
G_TYPE_FROM_CLASS (gobject_class),
g_cclosure_marshal_VOID__ENUMv);
}
static void
gtk_filter_init (GtkFilter *self)
{
}
/**
* gtk_filter_match:
* @self: a #GtkFilter
* @item: (type GObject) (transfer none): The item to check
*
* Checks if the given @item is matched by the filter or not.
*
* Returns: %TRUE if the filter matches the item and a filter model should
* keep it, %FALSE if not.
*/
gboolean
gtk_filter_match (GtkFilter *self,
gpointer item)
{
g_return_val_if_fail (GTK_IS_FILTER (self), FALSE);
g_return_val_if_fail (item != NULL, FALSE);
return GTK_FILTER_GET_CLASS (self)->match (self, item);
}
/**
* gtk_filter_get_strictness:
* @self: a #GtkFilter
*
* Gets the known strictness of @filters. If the strictness is not known,
* %GTK_FILTER_MATCH_SOME is returned.
*
* This value may change after emission of the GtkFilter:changed signal.
*
* This function is meant purely for optimization purposes, filters can
* choose to omit implementing it, but #GtkFilterListModel uses it.
*
* Returns: the strictness of @self
**/
GtkFilterMatch
gtk_filter_get_strictness (GtkFilter *self)
{
g_return_val_if_fail (GTK_IS_FILTER (self), GTK_FILTER_MATCH_SOME);
return GTK_FILTER_GET_CLASS (self)->get_strictness (self);
}
/**
* gtk_filter_changed:
* @self: a #GtkFilter
* @change: How the filter changed
*
* Emits the #GtkFilter:changed signal to notify all users of the filter that
* the filter changed. Users of the filter should then check items again via
* gtk_filter_match().
*
* Depending on the @change parameter, not all items need to be changed, but
* only some. Refer to the #GtkFilterChange documentation for details.
*
* This function is intended for implementors of #GtkFilter subclasses and
* should not be called from other functions.
*/
void
gtk_filter_changed (GtkFilter *self,
GtkFilterChange change)
{
g_return_if_fail (GTK_IS_FILTER (self));
g_signal_emit (self, signals[CHANGED], 0, change);
}

View file

@ -1,122 +0,0 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_FILTER_H__
#define __GTK_FILTER_H__
#define GTK_COMPILATION
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
G_BEGIN_DECLS
/**
* GtkFilterMatch:
* @GTK_FILTER_MATCH_SOME: The filter matches some items,
* gtk_filter_match() may return %TRUE or %FALSE
* @GTK_FILTER_MATCH_NONE: The filter does not match any item,
* gtk_filter_match() will always return %FALSE.
* @GTK_FILTER_MATCH_ALL: The filter matches all items,
* gtk_filter_match() will alays return %TRUE.
*
* Describes the known strictness of a filter.
*
* Note that for filters where the strictness is not known,
* %@GTK_FILTER_MATCH_SOME is always an acceptable value,
* even if a filter does match all or no items.
*/
typedef enum {
GTK_FILTER_MATCH_SOME = 0,
GTK_FILTER_MATCH_NONE,
GTK_FILTER_MATCH_ALL
} GtkFilterMatch;
/**
* GtkFilterChange:
* @GTK_FILTER_CHANGE_DIFFERENT: The filter change cannot be
* described with any of the other enumeration values.
* @GTK_FILTER_CHANGE_LESS_STRICT: The filter is less strict than
* it was before: All items that it used to return %TRUE for
* still return %TRUE, others now may, too.
* @GTK_FILTER_CHANGE_MORE_STRICT: The filter is more strict than
* it was before: All items that it used to return %FALSE for
* still return %FALSE, others now may, too.
*
* Describes changes in a filter in more detail and allows objects
* using the filter to optimize refiltering items.
*
* If you are writing an implementation and are not sure which
* value to pass, @GTK_FILTER_CHANGE_DIFFERENT is always a correct
* choice.
*/
typedef enum {
GTK_FILTER_CHANGE_DIFFERENT = 0,
GTK_FILTER_CHANGE_LESS_STRICT,
GTK_FILTER_CHANGE_MORE_STRICT,
} GtkFilterChange;
#define GTK_TYPE_FILTER (gtk_filter_get_type ())
/**
* GtkFilter:
*
* The object describing a filter.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (GtkFilter, gtk_filter, GTK, FILTER, GObject)
struct _GtkFilterClass
{
GObjectClass parent_class;
gboolean (* match) (GtkFilter *self,
gpointer item);
/* optional */
GtkFilterMatch (* get_strictness) (GtkFilter *self);
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
void (*_gtk_reserved4) (void);
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
void (*_gtk_reserved8) (void);
};
GDK_AVAILABLE_IN_ALL
gboolean gtk_filter_match (GtkFilter *self,
gpointer item);
GDK_AVAILABLE_IN_ALL
GtkFilterMatch gtk_filter_get_strictness (GtkFilter *self);
/* for filter implementations */
GDK_AVAILABLE_IN_ALL
void gtk_filter_changed (GtkFilter *self,
GtkFilterChange change);
G_END_DECLS
#endif /* __GTK_FILTER_H__ */

View file

@ -1,916 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
/* #include "calls-config.h" */
#include "gtkfilterlistmodel.h"
#include "gtkrbtreeprivate.h"
#include "gtkintl.h"
#include "gtkprivate.h"
/**
* SECTION:gtkfilterlistmodel
* @title: GtkFilterListModel
* @short_description: A list model that filters its items
* @see_also: #GListModel, #GtkFilter
*
* #GtkFilterListModel is a list model that filters a given other
* listmodel.
* It hides some elements from the other model according to
* criteria given by a #GtkFilter.
*/
enum {
PROP_0,
PROP_FILTER,
PROP_ITEM_TYPE,
PROP_MODEL,
NUM_PROPERTIES
};
typedef struct _FilterNode FilterNode;
typedef struct _FilterAugment FilterAugment;
struct _FilterNode
{
guint visible : 1;
};
struct _FilterAugment
{
guint n_items;
guint n_visible;
};
struct _GtkFilterListModel
{
GObject parent_instance;
GType item_type;
GListModel *model;
GtkFilter *filter;
GtkFilterMatch strictness;
GtkRbTree *items; /* NULL if strictness != GTK_FILTER_MATCH_SOME */
};
struct _GtkFilterListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static void
gtk_filter_list_model_augment (GtkRbTree *filter,
gpointer _aug,
gpointer _node,
gpointer left,
gpointer right)
{
FilterNode *node = _node;
FilterAugment *aug = _aug;
aug->n_items = 1;
aug->n_visible = node->visible ? 1 : 0;
if (left)
{
FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
aug->n_items += left_aug->n_items;
aug->n_visible += left_aug->n_visible;
}
if (right)
{
FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
aug->n_items += right_aug->n_items;
aug->n_visible += right_aug->n_visible;
}
}
static FilterNode *
gtk_filter_list_model_get_nth_filtered (GtkRbTree *tree,
guint position,
guint *out_unfiltered)
{
FilterNode *node, *tmp;
guint unfiltered;
node = gtk_rb_tree_get_root (tree);
unfiltered = 0;
while (node)
{
tmp = gtk_rb_tree_node_get_left (node);
if (tmp)
{
FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
if (position < aug->n_visible)
{
node = tmp;
continue;
}
position -= aug->n_visible;
unfiltered += aug->n_items;
}
if (node->visible)
{
if (position == 0)
break;
position--;
}
unfiltered++;
node = gtk_rb_tree_node_get_right (node);
}
if (out_unfiltered)
*out_unfiltered = unfiltered;
return node;
}
static FilterNode *
gtk_filter_list_model_get_nth (GtkRbTree *tree,
guint position,
guint *out_filtered)
{
FilterNode *node, *tmp;
guint filtered;
node = gtk_rb_tree_get_root (tree);
filtered = 0;
while (node)
{
tmp = gtk_rb_tree_node_get_left (node);
if (tmp)
{
FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
if (position < aug->n_items)
{
node = tmp;
continue;
}
position -= aug->n_items;
filtered += aug->n_visible;
}
if (position == 0)
break;
position--;
if (node->visible)
filtered++;
node = gtk_rb_tree_node_get_right (node);
}
if (out_filtered)
*out_filtered = filtered;
return node;
}
static GType
gtk_filter_list_model_get_item_type (GListModel *list)
{
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
return self->item_type;
}
static guint
gtk_filter_list_model_get_n_items (GListModel *list)
{
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
FilterAugment *aug;
FilterNode *node;
switch (self->strictness)
{
case GTK_FILTER_MATCH_NONE:
return 0;
case GTK_FILTER_MATCH_ALL:
return g_list_model_get_n_items (self->model);
default:
g_assert_not_reached ();
G_GNUC_FALLTHROUGH;
case GTK_FILTER_MATCH_SOME:
break;
}
node = gtk_rb_tree_get_root (self->items);
if (node == NULL)
return 0;
aug = gtk_rb_tree_get_augment (self->items, node);
return aug->n_visible;
}
static gpointer
gtk_filter_list_model_get_item (GListModel *list,
guint position)
{
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
guint unfiltered;
switch (self->strictness)
{
case GTK_FILTER_MATCH_NONE:
return NULL;
case GTK_FILTER_MATCH_ALL:
unfiltered = position;
break;
default:
g_assert_not_reached ();
G_GNUC_FALLTHROUGH;
case GTK_FILTER_MATCH_SOME:
gtk_filter_list_model_get_nth_filtered (self->items, position, &unfiltered);
break;
}
return g_list_model_get_item (self->model, unfiltered);
}
static void
gtk_filter_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_filter_list_model_get_item_type;
iface->get_n_items = gtk_filter_list_model_get_n_items;
iface->get_item = gtk_filter_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkFilterListModel, gtk_filter_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_filter_list_model_model_init))
static gboolean
gtk_filter_list_model_run_filter (GtkFilterListModel *self,
guint position)
{
gpointer item;
gboolean visible;
/* all other cases should have beeen optimized away */
g_assert (self->strictness == GTK_FILTER_MATCH_SOME);
item = g_list_model_get_item (self->model, position);
visible = gtk_filter_match (self->filter, item);
g_object_unref (item);
return visible;
}
static guint
gtk_filter_list_model_add_items (GtkFilterListModel *self,
FilterNode *after,
guint position,
guint n_items)
{
FilterNode *node;
guint i, n_visible;
n_visible = 0;
for (i = 0; i < n_items; i++)
{
node = gtk_rb_tree_insert_before (self->items, after);
node->visible = gtk_filter_list_model_run_filter (self, position + i);
if (node->visible)
n_visible++;
}
return n_visible;
}
static void
gtk_filter_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkFilterListModel *self)
{
FilterNode *node;
guint i, filter_position, filter_removed, filter_added;
switch (self->strictness)
{
case GTK_FILTER_MATCH_NONE:
return;
case GTK_FILTER_MATCH_ALL:
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
return;
default:
g_assert_not_reached ();
G_GNUC_FALLTHROUGH;
case GTK_FILTER_MATCH_SOME:
break;
}
node = gtk_filter_list_model_get_nth (self->items, position, &filter_position);
filter_removed = 0;
for (i = 0; i < removed; i++)
{
FilterNode *next = gtk_rb_tree_node_get_next (node);
if (node->visible)
filter_removed++;
gtk_rb_tree_remove (self->items, node);
node = next;
}
filter_added = gtk_filter_list_model_add_items (self, node, position, added);
if (filter_removed > 0 || filter_added > 0)
g_list_model_items_changed (G_LIST_MODEL (self), filter_position, filter_removed, filter_added);
}
static void
gtk_filter_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
switch (prop_id)
{
case PROP_FILTER:
gtk_filter_list_model_set_filter (self, g_value_get_object (value));
break;
case PROP_ITEM_TYPE:
self->item_type = g_value_get_gtype (value);
break;
case PROP_MODEL:
gtk_filter_list_model_set_model (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_filter_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
switch (prop_id)
{
case PROP_FILTER:
g_value_set_object (value, self->filter);
break;
case PROP_ITEM_TYPE:
g_value_set_gtype (value, self->item_type);
break;
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_filter_list_model_clear_model (GtkFilterListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_filter_list_model_items_changed_cb, self);
g_clear_object (&self->model);
if (self->items)
gtk_rb_tree_remove_all (self->items);
}
/*<private>
* gtk_filter_list_model_find_filtered:
* @self: a #GtkFilterListModel
* @start: (out) (caller-allocates): number of unfiltered items
* at start of list
* @end: (out) (caller-allocates): number of unfiltered items
* at end of list
* @n_items: (out) (caller-allocates): number of unfiltered items in
* list
*
* Checks if elements in self->items are filtered out and returns
* the range that they occupy.
* This function is intended to be used for GListModel::items-changed
* emissions, so it is called in an intermediate state for @self.
*
* Returns: %TRUE if elements are filtered out, %FALSE if none are
**/
static gboolean
gtk_filter_list_model_find_filtered (GtkFilterListModel *self,
guint *start,
guint *end,
guint *n_items)
{
FilterNode *root, *node, *tmp;
FilterAugment *aug;
if (self->items == NULL || self->model == NULL)
return FALSE;
root = gtk_rb_tree_get_root (self->items);
if (root == NULL)
return FALSE; /* empty parent model */
aug = gtk_rb_tree_get_augment (self->items, root);
if (aug->n_items == aug->n_visible)
return FALSE; /* all items visible */
/* find first filtered */
*start = 0;
*end = 0;
*n_items = aug->n_visible;
node = root;
while (node)
{
tmp = gtk_rb_tree_node_get_left (node);
if (tmp)
{
aug = gtk_rb_tree_get_augment (self->items, tmp);
if (aug->n_visible < aug->n_items)
{
node = tmp;
continue;
}
*start += aug->n_items;
}
if (!node->visible)
break;
(*start)++;
node = gtk_rb_tree_node_get_right (node);
}
/* find last filtered by doing everything the opposite way */
node = root;
while (node)
{
tmp = gtk_rb_tree_node_get_right (node);
if (tmp)
{
aug = gtk_rb_tree_get_augment (self->items, tmp);
if (aug->n_visible < aug->n_items)
{
node = tmp;
continue;
}
*end += aug->n_items;
}
if (!node->visible)
break;
(*end)++;
node = gtk_rb_tree_node_get_left (node);
}
return TRUE;
}
static void
gtk_filter_list_model_refilter (GtkFilterListModel *self);
static void
gtk_filter_list_model_update_strictness_and_refilter (GtkFilterListModel *self)
{
GtkFilterMatch new_strictness;
if (self->model == NULL)
new_strictness = GTK_FILTER_MATCH_NONE;
else if (self->filter == NULL)
new_strictness = GTK_FILTER_MATCH_ALL;
else
new_strictness = gtk_filter_get_strictness (self->filter);
/* don't set self->strictness yet so get_n_items() and friends return old values */
switch (new_strictness)
{
case GTK_FILTER_MATCH_NONE:
{
guint n_before = g_list_model_get_n_items (G_LIST_MODEL (self));
g_clear_pointer (&self->items, gtk_rb_tree_unref);
self->strictness = new_strictness;
if (n_before > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_before, 0);
}
break;
case GTK_FILTER_MATCH_ALL:
switch (self->strictness)
{
case GTK_FILTER_MATCH_NONE:
self->strictness = new_strictness;
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, g_list_model_get_n_items (self->model));
break;
case GTK_FILTER_MATCH_ALL:
self->strictness = new_strictness;
break;
default:
case GTK_FILTER_MATCH_SOME:
{
guint start, end, n_before, n_after;
self->strictness = new_strictness;
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_before))
{
n_after = g_list_model_get_n_items (G_LIST_MODEL (self));
g_clear_pointer (&self->items, gtk_rb_tree_unref);
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
}
else
{
g_clear_pointer (&self->items, gtk_rb_tree_unref);
}
}
break;
}
break;
default:
g_assert_not_reached ();
G_GNUC_FALLTHROUGH;
case GTK_FILTER_MATCH_SOME:
switch (self->strictness)
{
case GTK_FILTER_MATCH_NONE:
{
guint n_after;
self->strictness = new_strictness;
self->items = gtk_rb_tree_new (FilterNode,
FilterAugment,
gtk_filter_list_model_augment,
NULL, NULL);
n_after = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (self->model));
if (n_after > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, n_after);
}
break;
case GTK_FILTER_MATCH_ALL:
{
guint start, end, n_before, n_after;
self->strictness = new_strictness;
self->items = gtk_rb_tree_new (FilterNode,
FilterAugment,
gtk_filter_list_model_augment,
NULL, NULL);
n_before = g_list_model_get_n_items (self->model);
gtk_filter_list_model_add_items (self, NULL, 0, n_before);
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_after))
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
}
break;
default:
case GTK_FILTER_MATCH_SOME:
gtk_filter_list_model_refilter (self);
break;
}
}
}
static void
gtk_filter_list_model_filter_changed_cb (GtkFilter *filter,
GtkFilterChange change,
GtkFilterListModel *self)
{
gtk_filter_list_model_update_strictness_and_refilter (self);
}
static void
gtk_filter_list_model_clear_filter (GtkFilterListModel *self)
{
if (self->filter == NULL)
return;
g_signal_handlers_disconnect_by_func (self->filter, gtk_filter_list_model_filter_changed_cb, self);
g_clear_object (&self->filter);
}
static void
gtk_filter_list_model_dispose (GObject *object)
{
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
gtk_filter_list_model_clear_model (self);
gtk_filter_list_model_clear_filter (self);
g_clear_pointer (&self->items, gtk_rb_tree_unref);
G_OBJECT_CLASS (gtk_filter_list_model_parent_class)->dispose (object);
}
static void
gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_filter_list_model_set_property;
gobject_class->get_property = gtk_filter_list_model_get_property;
gobject_class->dispose = gtk_filter_list_model_dispose;
/**
* GtkFilterListModel:filter:
*
* The filter for this model
*/
properties[PROP_FILTER] =
g_param_spec_object ("filter",
P_("Filter"),
P_("The filter set for this model"),
GTK_TYPE_FILTER,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFilterListModel:item-type:
*
* The #GType for elements of this object
*/
properties[PROP_ITEM_TYPE] =
g_param_spec_gtype ("item-type",
P_("Item type"),
P_("The type of elements of this object"),
G_TYPE_OBJECT,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFilterListModel:model:
*
* The model being filtered
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("The model being filtered"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_filter_list_model_init (GtkFilterListModel *self)
{
self->strictness = GTK_FILTER_MATCH_NONE;
}
/**
* gtk_filter_list_model_new:
* @model: the model to sort
* @filter: (allow-none): filter or %NULL to not filter items
*
* Creates a new #GtkFilterListModel that will filter @model using the given
* @filter.
*
* Returns: a new #GtkFilterListModel
**/
GtkFilterListModel *
gtk_filter_list_model_new (GListModel *model,
GtkFilter *filter)
{
GtkFilterListModel *result;
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
result = g_object_new (GTK_TYPE_FILTER_LIST_MODEL,
"item-type", g_list_model_get_item_type (model),
"model", model,
"filter", filter,
NULL);
return result;
}
/**
* gtk_filter_list_model_new_for_type:
* @item_type: the type of the items that will be returned
*
* Creates a new empty filter list model set up to return items of type @item_type.
* It is up to the application to set a proper filter and model to ensure
* the item type is matched.
*
* Returns: a new #GtkFilterListModel
**/
GtkFilterListModel *
gtk_filter_list_model_new_for_type (GType item_type)
{
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
return g_object_new (GTK_TYPE_FILTER_LIST_MODEL,
"item-type", item_type,
NULL);
}
/**
* gtk_filter_list_model_set_filter:
* @self: a #GtkFilterListModel
* @filter: (allow-none) (transfer none): filter to use or %NULL to not filter items
*
* Sets the filter used to filter items.
**/
void
gtk_filter_list_model_set_filter (GtkFilterListModel *self,
GtkFilter *filter)
{
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
if (self->filter == filter)
return;
gtk_filter_list_model_clear_filter (self);
if (filter)
{
self->filter = g_object_ref (filter);
g_signal_connect (filter, "changed", G_CALLBACK (gtk_filter_list_model_filter_changed_cb), self);
gtk_filter_list_model_filter_changed_cb (filter, GTK_FILTER_CHANGE_DIFFERENT, self);
}
else
{
gtk_filter_list_model_update_strictness_and_refilter (self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILTER]);
}
/**
* gtk_filter_list_model_get_filter:
* @self: a #GtkFilterListModel
*
* Gets the #GtkFilter currently set on @self.
*
* Returns: (nullable) (transfer none): The filter currently in use
* or %NULL if the list isn't filtered
**/
GtkFilter *
gtk_filter_list_model_get_filter (GtkFilterListModel *self)
{
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
return self->filter;
}
/**
* gtk_filter_list_model_set_model:
* @self: a #GtkFilterListModel
* @model: (allow-none): The model to be filtered
*
* Sets the model to be filtered.
*
* Note that GTK makes no effort to ensure that @model conforms to
* the item type of @self. It assumes that the caller knows what they
* are doing and have set up an appropriate filter to ensure that item
* types match.
**/
void
gtk_filter_list_model_set_model (GtkFilterListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
/* Note: We don't check for matching item type here, we just assume the
* filter func takes care of filtering wrong items. */
if (self->model == model)
return;
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
gtk_filter_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_filter_list_model_items_changed_cb), self);
if (removed == 0)
{
self->strictness = GTK_FILTER_MATCH_NONE;
gtk_filter_list_model_update_strictness_and_refilter (self);
added = 0;
}
else if (self->items)
added = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
else
added = g_list_model_get_n_items (model);
}
else
{
self->strictness = GTK_FILTER_MATCH_NONE;
added = 0;
}
if (removed > 0 || added > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_filter_list_model_get_model:
* @self: a #GtkFilterListModel
*
* Gets the model currently filtered or %NULL if none.
*
* Returns: (nullable) (transfer none): The model that gets filtered
**/
GListModel *
gtk_filter_list_model_get_model (GtkFilterListModel *self)
{
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), NULL);
return self->model;
}
static void
gtk_filter_list_model_refilter (GtkFilterListModel *self)
{
FilterNode *node;
guint i, first_change, last_change;
guint n_is_visible, n_was_visible;
gboolean visible;
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
if (self->items == NULL || self->model == NULL)
return;
first_change = G_MAXUINT;
last_change = 0;
n_is_visible = 0;
n_was_visible = 0;
for (i = 0, node = gtk_rb_tree_get_first (self->items);
node != NULL;
i++, node = gtk_rb_tree_node_get_next (node))
{
visible = gtk_filter_list_model_run_filter (self, i);
if (visible == node->visible)
{
if (visible)
{
n_is_visible++;
n_was_visible++;
}
continue;
}
node->visible = visible;
gtk_rb_tree_node_mark_dirty (node);
first_change = MIN (n_is_visible, first_change);
if (visible)
n_is_visible++;
else
n_was_visible++;
last_change = MAX (n_is_visible, last_change);
}
if (first_change <= last_change)
{
g_list_model_items_changed (G_LIST_MODEL (self),
first_change,
last_change - first_change + n_was_visible - n_is_visible,
last_change - first_change);
}
}

View file

@ -1,59 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_FILTER_LIST_MODEL_H__
#define __GTK_FILTER_LIST_MODEL_H__
#define GTK_COMPILATION
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gio/gio.h>
#include "gtkfilter.h"
G_BEGIN_DECLS
#define GTK_TYPE_FILTER_LIST_MODEL (gtk_filter_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkFilterListModel, gtk_filter_list_model, GTK, FILTER_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkFilterListModel * gtk_filter_list_model_new (GListModel *model,
GtkFilter *filter);
GDK_AVAILABLE_IN_ALL
GtkFilterListModel * gtk_filter_list_model_new_for_type (GType item_type);
GDK_AVAILABLE_IN_ALL
void gtk_filter_list_model_set_filter (GtkFilterListModel *self,
GtkFilter *filter);
GDK_AVAILABLE_IN_ALL
GtkFilter * gtk_filter_list_model_get_filter (GtkFilterListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_filter_list_model_set_model (GtkFilterListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_filter_list_model_get_model (GtkFilterListModel *self);
G_END_DECLS
#endif /* __GTK_FILTER_LIST_MODEL_H__ */

View file

@ -1,541 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
/* #include "calls-config.h" */
#include "gtkflattenlistmodel.h"
#include "gtkrbtreeprivate.h"
#include "gtkintl.h"
#include "gtkprivate.h"
/**
* SECTION:gtkflattenlistmodel
* @title: GtkFlattenListModel
* @short_description: A list model that flattens a list of lists
* @see_also: #GListModel
*
* #GtkFlattenListModel is a list model that takes a list model containing
* list models and flattens it into a single model.
*
* Another term for this is concatenation: #GtkFlattenListModel takes a
* list of lists and concatenates them into a single list.
*/
enum {
PROP_0,
PROP_ITEM_TYPE,
PROP_MODEL,
NUM_PROPERTIES
};
typedef struct _FlattenNode FlattenNode;
typedef struct _FlattenAugment FlattenAugment;
struct _FlattenNode
{
GListModel *model;
GtkFlattenListModel *list;
};
struct _FlattenAugment
{
guint n_items;
guint n_models;
};
struct _GtkFlattenListModel
{
GObject parent_instance;
GType item_type;
GListModel *model;
GtkRbTree *items; /* NULL if model == NULL */
};
struct _GtkFlattenListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static FlattenNode *
gtk_flatten_list_model_get_nth (GtkRbTree *tree,
guint position,
guint *model_position)
{
FlattenNode *node, *tmp;
guint model_n_items;
node = gtk_rb_tree_get_root (tree);
while (node)
{
tmp = gtk_rb_tree_node_get_left (node);
if (tmp)
{
FlattenAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
if (position < aug->n_items)
{
node = tmp;
continue;
}
position -= aug->n_items;
}
model_n_items = g_list_model_get_n_items (node->model);
if (position < model_n_items)
break;
position -= model_n_items;
node = gtk_rb_tree_node_get_right (node);
}
if (model_position)
*model_position = node ? position : 0;
return node;
}
static FlattenNode *
gtk_flatten_list_model_get_nth_model (GtkRbTree *tree,
guint position,
guint *items_before)
{
FlattenNode *node, *tmp;
guint before;
node = gtk_rb_tree_get_root (tree);
before = 0;
while (node)
{
tmp = gtk_rb_tree_node_get_left (node);
if (tmp)
{
FlattenAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
if (position < aug->n_models)
{
node = tmp;
continue;
}
position -= aug->n_models;
before += aug->n_items;
}
if (position == 0)
break;
position--;
before += g_list_model_get_n_items (node->model);
node = gtk_rb_tree_node_get_right (node);
}
if (items_before)
*items_before = before;
return node;
}
static GType
gtk_flatten_list_model_get_item_type (GListModel *list)
{
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
return self->item_type;
}
static guint
gtk_flatten_list_model_get_n_items (GListModel *list)
{
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
FlattenAugment *aug;
FlattenNode *node;
if (!self->items)
return 0;
node = gtk_rb_tree_get_root (self->items);
if (node == NULL)
return 0;
aug = gtk_rb_tree_get_augment (self->items, node);
return aug->n_items;
}
static gpointer
gtk_flatten_list_model_get_item (GListModel *list,
guint position)
{
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
FlattenNode *node;
guint model_pos;
if (!self->items)
return NULL;
node = gtk_flatten_list_model_get_nth (self->items, position, &model_pos);
if (node == NULL)
return NULL;
return g_list_model_get_item (node->model, model_pos);
}
static void
gtk_flatten_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_flatten_list_model_get_item_type;
iface->get_n_items = gtk_flatten_list_model_get_n_items;
iface->get_item = gtk_flatten_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkFlattenListModel, gtk_flatten_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_flatten_list_model_model_init))
static void
gtk_flatten_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
gpointer _node)
{
FlattenNode *node = _node, *parent, *left;
GtkFlattenListModel *self = node->list;
guint real_position;
gtk_rb_tree_node_mark_dirty (node);
real_position = position;
left = gtk_rb_tree_node_get_left (node);
if (left)
{
FlattenAugment *aug = gtk_rb_tree_get_augment (self->items, left);
real_position += aug->n_items;
}
for (;
(parent = gtk_rb_tree_node_get_parent (node)) != NULL;
node = parent)
{
left = gtk_rb_tree_node_get_left (parent);
if (left != node)
{
if (left)
{
FlattenAugment *aug = gtk_rb_tree_get_augment (self->items, left);
real_position += aug->n_items;
}
real_position += g_list_model_get_n_items (parent->model);
}
}
g_list_model_items_changed (G_LIST_MODEL (self), real_position, removed, added);
}
static void
gtk_flatten_list_model_clear_node (gpointer _node)
{
FlattenNode *node= _node;
g_signal_handlers_disconnect_by_func (node->model, gtk_flatten_list_model_items_changed_cb, node);
g_object_unref (node->model);
}
static void
gtk_flatten_list_model_augment (GtkRbTree *flatten,
gpointer _aug,
gpointer _node,
gpointer left,
gpointer right)
{
FlattenNode *node = _node;
FlattenAugment *aug = _aug;
aug->n_items = g_list_model_get_n_items (node->model);
aug->n_models = 1;
if (left)
{
FlattenAugment *left_aug = gtk_rb_tree_get_augment (flatten, left);
aug->n_items += left_aug->n_items;
aug->n_models += left_aug->n_models;
}
if (right)
{
FlattenAugment *right_aug = gtk_rb_tree_get_augment (flatten, right);
aug->n_items += right_aug->n_items;
aug->n_models += right_aug->n_models;
}
}
static guint
gtk_flatten_list_model_add_items (GtkFlattenListModel *self,
FlattenNode *after,
guint position,
guint n)
{
FlattenNode *node;
guint added, i;
added = 0;
for (i = 0; i < n; i++)
{
node = gtk_rb_tree_insert_before (self->items, after);
node->model = g_list_model_get_item (self->model, position + i);
g_warn_if_fail (g_type_is_a (g_list_model_get_item_type (node->model), self->item_type));
g_signal_connect (node->model,
"items-changed",
G_CALLBACK (gtk_flatten_list_model_items_changed_cb),
node);
node->list = self;
added +=g_list_model_get_n_items (node->model);
}
return added;
}
static void
gtk_flatten_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
switch (prop_id)
{
case PROP_ITEM_TYPE:
self->item_type = g_value_get_gtype (value);
break;
case PROP_MODEL:
gtk_flatten_list_model_set_model (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_flatten_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
switch (prop_id)
{
case PROP_ITEM_TYPE:
g_value_set_gtype (value, self->item_type);
break;
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_flatten_list_model_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkFlattenListModel *self)
{
FlattenNode *node;
guint i, real_position, real_removed, real_added;
node = gtk_flatten_list_model_get_nth_model (self->items, position, &real_position);
real_removed = 0;
for (i = 0; i < removed; i++)
{
FlattenNode *next = gtk_rb_tree_node_get_next (node);
real_removed += g_list_model_get_n_items (node->model);
gtk_rb_tree_remove (self->items, node);
node = next;
}
real_added = gtk_flatten_list_model_add_items (self, node, position, added);
if (real_removed > 0 || real_added > 0)
g_list_model_items_changed (G_LIST_MODEL (self), real_position, real_removed, real_added);
}
static void
gtk_flatten_list_clear_model (GtkFlattenListModel *self)
{
if (self->model)
{
g_signal_handlers_disconnect_by_func (self->model, gtk_flatten_list_model_model_items_changed_cb, self);
g_clear_object (&self->model);
g_clear_pointer (&self->items, gtk_rb_tree_unref);
}
}
static void
gtk_flatten_list_model_dispose (GObject *object)
{
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
gtk_flatten_list_clear_model (self);
G_OBJECT_CLASS (gtk_flatten_list_model_parent_class)->dispose (object);
}
static void
gtk_flatten_list_model_class_init (GtkFlattenListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_flatten_list_model_set_property;
gobject_class->get_property = gtk_flatten_list_model_get_property;
gobject_class->dispose = gtk_flatten_list_model_dispose;
/**
* GtkFlattenListModel:item-type:
*
* The #GTpe for elements of this object
*/
properties[PROP_ITEM_TYPE] =
g_param_spec_gtype ("item-type",
P_("Item type"),
P_("The type of elements of this object"),
G_TYPE_OBJECT,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFlattenListModel:model:
*
* The model being flattened
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("The model being flattened"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_flatten_list_model_init (GtkFlattenListModel *self)
{
}
/**
* gtk_flatten_list_model_new:
* @item_type: The type of items in the to-be-flattened models
* @model: (nullable) (transfer none): the item to be flattened
*
* Creates a new #GtkFlattenListModel that flattens @list. The
* models returned by @model must conform to the given @item_type,
* either by having an identical type or a subtype.
*
* Returns: a new #GtkFlattenListModel
**/
GtkFlattenListModel *
gtk_flatten_list_model_new (GType item_type,
GListModel *model)
{
GtkFlattenListModel *result;
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
result = g_object_new (GTK_TYPE_FLATTEN_LIST_MODEL,
"item-type", item_type,
"model", model,
NULL);
return result;
}
/**
* gtk_flatten_list_model_set_model:
* @self: a #GtkFlattenListModel
* @model: (nullable) (transfer none): the new model or %NULL
*
* Sets a new model to be flattened. The model must contain items of
* #GtkListModel that conform to the item type of @self.
**/
void
gtk_flatten_list_model_set_model (GtkFlattenListModel *self,
GListModel *model)
{
guint removed, added = 0;
g_return_if_fail (GTK_IS_FLATTEN_LIST_MODEL (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (model)
{
g_return_if_fail (g_type_is_a (g_list_model_get_item_type (model), G_TYPE_LIST_MODEL));
}
if (self->model == model)
return;
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
gtk_flatten_list_clear_model (self);
self->model = model;
if (model)
{
g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_flatten_list_model_model_items_changed_cb), self);
self->items = gtk_rb_tree_new (FlattenNode,
FlattenAugment,
gtk_flatten_list_model_augment,
gtk_flatten_list_model_clear_node,
NULL);
added = gtk_flatten_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
}
if (removed > 0 || added > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_flatten_list_model_get_model:
* @self: a #GtkFlattenListModel
*
* Gets the model set via gtk_flatten_list_model_set_model().
*
* Returns: (nullable) (transfer none): The model flattened by @self
**/
GListModel *
gtk_flatten_list_model_get_model (GtkFlattenListModel *self)
{
g_return_val_if_fail (GTK_IS_FLATTEN_LIST_MODEL (self), NULL);
return self->model;
}

View file

@ -1,51 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_FLATTEN_LIST_MODEL_H__
#define __GTK_FLATTEN_LIST_MODEL_H__
#define GTK_COMPILATION
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
G_BEGIN_DECLS
#define GTK_TYPE_FLATTEN_LIST_MODEL (gtk_flatten_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkFlattenListModel, gtk_flatten_list_model, GTK, FLATTEN_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkFlattenListModel * gtk_flatten_list_model_new (GType item_type,
GListModel *model);
GDK_AVAILABLE_IN_ALL
void gtk_flatten_list_model_set_model (GtkFlattenListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_flatten_list_model_get_model (GtkFlattenListModel *self);
G_END_DECLS
#endif /* __GTK_FLATTEN_LIST_MODEL_H__ */

View file

@ -1,22 +0,0 @@
#ifndef __GTKINTL_H__
#define __GTKINTL_H__
#ifndef GETTEXT_PACKAGE
# define GETTEXT_PACKAGE
#endif
#include <glib/gi18n-lib.h>
#ifndef G_GNUC_FALLTHROUGH
#define G_GNUC_FALLTHROUGH __attribute__((fallthrough))
#endif
#ifdef ENABLE_NLS
#define P_(String) g_dgettext(GETTEXT_PACKAGE "-properties",String)
#else
#define P_(String) (String)
#endif
/* not really I18N-related, but also a string marker macro */
#define I_(string) g_intern_static_string (string)
#endif

View file

@ -1,8 +0,0 @@
#include "gtksorter.h"
#include "gtkcustomsorter.h"
#include "gtkfilter.h"
#include "gtkcustomfilter.h"
#include "gtksortlistmodel.h"
#include "gtkfilterlistmodel.h"
#include "gtkflattenlistmodel.h"
#include "gtkslicelistmodel.h"

View file

@ -1,38 +0,0 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library 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 library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __GTK_PRIVATE_H__
#define __GTK_PRIVATE_H__
#include <glib-object.h>
G_BEGIN_DECLS
#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
#define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
G_END_DECLS
#endif /* __GTK_PRIVATE_H__ */

View file

@ -1,800 +0,0 @@
/* gtkrbtree.c
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/* #include "calls-config.h" */
#include "gtkrbtreeprivate.h"
/* #include "gtkdebug.h" */
/* Define the following to print adds and removals to stdout.
* The format of the printout will be suitable for addition as a new test to
* testsuite/gtk/rbtree-crash.c
* by just grepping the printouts from the relevant rbtree.
*
* This is meant to be a trivial way to add rbtree tests to the testsuite.
*/
#undef DUMP_MODIFICATION
typedef struct _GtkRbNode GtkRbNode;
struct _GtkRbTree
{
guint ref_count;
gsize element_size;
gsize augment_size;
GtkRbTreeAugmentFunc augment_func;
GDestroyNotify clear_func;
GDestroyNotify clear_augment_func;
GtkRbNode *root;
};
struct _GtkRbNode
{
guint red :1;
guint dirty :1;
GtkRbNode *left;
GtkRbNode *right;
/* The difference between tree and parent here is that we OR the tree with 1 and because
* pointers are always multiples of 4, we can know if we've stored a parent or the tree here */
union {
gpointer parent_or_tree;
GtkRbNode *parent;
GtkRbTree *tree;
};
};
#define NODE_FROM_POINTER(ptr) ((GtkRbNode *) ((ptr) ? (((guchar *) (ptr)) - sizeof (GtkRbNode)) : NULL))
#define NODE_TO_POINTER(node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkRbNode)) : NULL))
#define NODE_TO_AUG_POINTER(tree, node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkRbNode) + (tree)->element_size) : NULL))
static inline gboolean
is_root (GtkRbNode *node)
{
return GPOINTER_TO_SIZE (node->parent_or_tree) & 1 ? TRUE : FALSE;
}
static inline GtkRbNode *
parent (GtkRbNode *node)
{
if (is_root (node))
return NULL;
else
return node->parent;
}
static GtkRbTree *
tree (GtkRbNode *node)
{
while (!is_root (node))
node = parent (node);
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (node->tree) & ~1);
}
static void
set_parent (GtkRbTree *tree,
GtkRbNode *node,
GtkRbNode *new_parent)
{
if (new_parent != NULL)
{
node->parent = new_parent;
}
else
{
node->tree = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (tree) | 1);
tree->root = node;
}
}
static inline gsize
gtk_rb_node_get_size (GtkRbTree *tree)
{
return sizeof (GtkRbNode) + tree->element_size + tree->augment_size;
}
static GtkRbNode *
gtk_rb_node_new (GtkRbTree *tree)
{
GtkRbNode *result;
result = g_slice_alloc0 (gtk_rb_node_get_size (tree));
result->red = TRUE;
result->dirty = TRUE;
return result;
}
static void
gtk_rb_node_free (GtkRbTree *tree,
GtkRbNode *node)
{
if (tree->clear_func)
tree->clear_func (NODE_TO_POINTER (node));
if (tree->clear_augment_func)
tree->clear_augment_func (NODE_TO_AUG_POINTER (tree, node));
g_slice_free1 (gtk_rb_node_get_size (tree), node);
}
static void
gtk_rb_node_free_deep (GtkRbTree *tree,
GtkRbNode *node)
{
GtkRbNode *right = node->right;
if (node->left)
gtk_rb_node_free_deep (tree, node->left);
gtk_rb_node_free (tree, node);
if (right)
gtk_rb_node_free_deep (tree, right);
}
static void
gtk_rb_node_mark_dirty (GtkRbNode *node,
gboolean mark_parent)
{
if (node->dirty)
return;
node->dirty = TRUE;
if (mark_parent && parent (node))
gtk_rb_node_mark_dirty (parent (node), TRUE);
}
static void
gtk_rb_node_clean (GtkRbTree *tree,
GtkRbNode *node)
{
if (!node->dirty)
return;
node->dirty = FALSE;
if (tree->augment_func)
tree->augment_func (tree,
NODE_TO_AUG_POINTER (tree, node),
NODE_TO_POINTER (node),
NODE_TO_POINTER (node->left),
NODE_TO_POINTER (node->right));
}
static GtkRbNode *
gtk_rb_node_get_first (GtkRbNode *node)
{
while (node->left)
node = node->left;
return node;
}
static GtkRbNode *
gtk_rb_node_get_last (GtkRbNode *node)
{
while (node->right)
node = node->right;
return node;
}
static GtkRbNode *
gtk_rb_node_get_previous (GtkRbNode *node)
{
GtkRbNode *p;
if (node->left)
return gtk_rb_node_get_last (node->left);
for (p = parent (node); p != NULL; p = parent (node))
{
if (p->right == node)
return p;
node = p;
}
return NULL;
}
static GtkRbNode *
gtk_rb_node_get_next (GtkRbNode *node)
{
GtkRbNode *p;
if (node->right)
return gtk_rb_node_get_first (node->right);
for (p = parent (node); p != NULL; p = parent (node))
{
if (p->left == node)
return p;
node = p;
}
return NULL;
}
#ifdef DUMP_MODIFICATION
static guint
position (GtkRbTree *tree,
GtkRbNode *node)
{
GtkRbNode *n;
guint i;
i = 0;
for (n = gtk_rb_node_get_first (tree->root);
n != node;
n = gtk_rb_node_get_next (n))
i++;
return i;
}
#endif
static void
gtk_rb_node_rotate_left (GtkRbTree *tree,
GtkRbNode *node)
{
GtkRbNode *right, *p;
right = node->right;
p = parent (node);
node->right = right->left;
if (right->left)
set_parent (tree, right->left, node);
set_parent (tree, right, p);
if (p)
{
if (node == p->left)
p->left = right;
else
p->right = right;
}
right->left = node;
set_parent (tree, node, right);
gtk_rb_node_mark_dirty (node, FALSE);
gtk_rb_node_mark_dirty (right, FALSE);
}
static void
gtk_rb_node_rotate_right (GtkRbTree *tree,
GtkRbNode *node)
{
GtkRbNode *left, *p;
left = node->left;
p = parent (node);
node->left = left->right;
if (left->right)
set_parent (tree, left->right, node);
set_parent (tree, left, p);
if (p)
{
if (node == p->right)
p->right = left;
else
p->left = left;
}
/* link node and left */
left->right = node;
set_parent (tree, node, left);
gtk_rb_node_mark_dirty (node, FALSE);
gtk_rb_node_mark_dirty (left, FALSE);
}
static gboolean
is_red (GtkRbNode *node_or_null)
{
if (node_or_null == NULL)
return FALSE;
else
return node_or_null->red;
}
static inline gboolean
is_black (GtkRbNode *node_or_null)
{
return !is_red (node_or_null);
}
static void
set_black (GtkRbNode *node_or_null)
{
if (node_or_null == NULL)
return;
node_or_null->red = FALSE;
}
static void
set_red (GtkRbNode *node_or_null)
{
if (node_or_null == NULL)
return;
node_or_null->red = TRUE;
}
static void
gtk_rb_tree_insert_fixup (GtkRbTree *tree,
GtkRbNode *node)
{
GtkRbNode *p;
/* check Red-Black properties */
for (p = parent (node);
p && is_red (p);
p = parent (node))
{
GtkRbNode *pp = parent (p);
/* we have a violation */
g_assert (pp);
if (p == pp->left)
{
GtkRbNode *uncle = pp->right;
if (is_red (uncle))
{
/* uncle is red */
set_black (p);
set_black (uncle);
set_red (pp);
node = pp;
}
else
{
/* uncle is black */
if (node == p->right)
{
/* make node a left child */
node = p;
gtk_rb_node_rotate_left (tree, node);
p = parent (node);
pp = parent (p);
}
/* recolor and rotate */
set_black (p);
set_red (pp);
gtk_rb_node_rotate_right (tree, pp);
}
}
else
{
/* mirror image of above code */
GtkRbNode *uncle = pp->left;
if (is_red (uncle))
{
/* uncle is red */
set_black (p);
set_black (uncle);
set_red (pp);
node = pp;
}
else
{
/* uncle is black */
if (node == p->left)
{
node = p;
gtk_rb_node_rotate_right (tree, node);
p = parent (node);
pp = parent (p);
}
set_black (p);
set_red (pp);
gtk_rb_node_rotate_left (tree, pp);
}
}
}
set_black (tree->root);
}
static void
gtk_rb_tree_remove_node_fixup (GtkRbTree *tree,
GtkRbNode *node,
GtkRbNode *p)
{
while (node != tree->root && is_black (node))
{
if (node == p->left)
{
GtkRbNode *w = p->right;
if (is_red (w))
{
set_black (w);
set_red (p);
gtk_rb_node_rotate_left (tree, p);
w = p->right;
}
if (is_black (w->left) && is_black (w->right))
{
set_red (w);
node = p;
}
else
{
if (is_black (w->right))
{
set_black (w->left);
set_red (w);
gtk_rb_node_rotate_right (tree, w);
w = p->right;
}
w->red = p->red;
set_black (p);
set_black (w->right);
gtk_rb_node_rotate_left (tree, p);
node = tree->root;
}
}
else
{
GtkRbNode *w = p->left;
if (is_red (w))
{
set_black (w);
set_red (p);
gtk_rb_node_rotate_right (tree, p);
w = p->left;
}
if (is_black (w->right) && is_black (w->left))
{
set_red (w);
node = p;
}
else
{
if (is_black (w->left))
{
set_black (w->right);
set_red (w);
gtk_rb_node_rotate_left (tree, w);
w = p->left;
}
w->red = p->red;
set_black (p);
set_black (w->left);
gtk_rb_node_rotate_right (tree, p);
node = tree->root;
}
}
p = parent (node);
}
set_black (node);
}
GtkRbTree *
gtk_rb_tree_new_for_size (gsize element_size,
gsize augment_size,
GtkRbTreeAugmentFunc augment_func,
GDestroyNotify clear_func,
GDestroyNotify clear_augment_func)
{
GtkRbTree *tree;
tree = g_slice_new0 (GtkRbTree);
tree->ref_count = 1;
tree->element_size = element_size;
tree->augment_size = augment_size;
tree->augment_func = augment_func;
tree->clear_func = clear_func;
tree->clear_augment_func = clear_augment_func;
return tree;
}
GtkRbTree *
gtk_rb_tree_ref (GtkRbTree *tree)
{
tree->ref_count++;
return tree;
}
void
gtk_rb_tree_unref (GtkRbTree *tree)
{
tree->ref_count--;
if (tree->ref_count > 0)
return;
if (tree->root)
gtk_rb_node_free_deep (tree, tree->root);
g_slice_free (GtkRbTree, tree);
}
gpointer
gtk_rb_tree_get_first (GtkRbTree *tree)
{
if (tree->root == NULL)
return NULL;
return NODE_TO_POINTER (gtk_rb_node_get_first (tree->root));
}
gpointer
gtk_rb_tree_get_last (GtkRbTree *tree)
{
if (tree->root == NULL)
return NULL;
return NODE_TO_POINTER (gtk_rb_node_get_last (tree->root));
}
gpointer
gtk_rb_tree_node_get_previous (gpointer node)
{
return NODE_TO_POINTER (gtk_rb_node_get_previous (NODE_FROM_POINTER (node)));
}
gpointer
gtk_rb_tree_node_get_next (gpointer node)
{
return NODE_TO_POINTER (gtk_rb_node_get_next (NODE_FROM_POINTER (node)));
}
gpointer
gtk_rb_tree_get_root (GtkRbTree *tree)
{
return NODE_TO_POINTER (tree->root);
}
gpointer
gtk_rb_tree_node_get_parent (gpointer node)
{
return NODE_TO_POINTER (parent (NODE_FROM_POINTER (node)));
}
gpointer
gtk_rb_tree_node_get_left (gpointer node)
{
return NODE_TO_POINTER (NODE_FROM_POINTER (node)->left);
}
gpointer
gtk_rb_tree_node_get_right (gpointer node)
{
return NODE_TO_POINTER (NODE_FROM_POINTER (node)->right);
}
gpointer
gtk_rb_tree_get_augment (GtkRbTree *tree,
gpointer node)
{
GtkRbNode *rbnode = NODE_FROM_POINTER (node);
gtk_rb_node_clean (tree, rbnode);
return NODE_TO_AUG_POINTER (tree, rbnode);
}
GtkRbTree *
gtk_rb_tree_node_get_tree (gpointer node)
{
return tree (NODE_FROM_POINTER (node));
}
void
gtk_rb_tree_node_mark_dirty (gpointer node)
{
gtk_rb_node_mark_dirty (NODE_FROM_POINTER (node), TRUE);
}
gpointer
gtk_rb_tree_insert_before (GtkRbTree *tree,
gpointer node)
{
GtkRbNode *result;
if (tree->root == NULL)
{
#ifdef DUMP_MODIFICATION
g_print ("add (tree, 0); /* 0x%p */\n", tree);
#endif /* DUMP_MODIFICATION */
g_assert (node == NULL);
result = gtk_rb_node_new (tree);
tree->root = result;
}
else if (node == NULL)
{
return gtk_rb_tree_insert_after (tree, gtk_rb_tree_get_last (tree));
}
else
{
GtkRbNode *current = NODE_FROM_POINTER (node);
#ifdef DUMP_MODIFICATION
g_print ("add (tree, %u); /* 0x%p */\n", position (tree, current), tree);
#endif /* DUMP_MODIFICATION */
/* setup new node */
result = gtk_rb_node_new (tree);
if (current->left)
{
current = gtk_rb_node_get_last (current->left);
current->right = result;
}
else
{
current->left = result;
}
set_parent (tree, result, current);
gtk_rb_node_mark_dirty (current, TRUE);
}
gtk_rb_tree_insert_fixup (tree, result);
return NODE_TO_POINTER (result);
}
gpointer
gtk_rb_tree_insert_after (GtkRbTree *tree,
gpointer node)
{
GtkRbNode *current, *result;
if (node == NULL)
return gtk_rb_tree_insert_before (tree, gtk_rb_tree_get_first (tree));
current = NODE_FROM_POINTER (node);
#ifdef DUMP_MODIFICATION
g_print ("add (tree, %u); /* 0x%p */\n", position (tree, current) + 1, tree);
#endif /* DUMP_MODIFICATION */
/* setup new node */
result = gtk_rb_node_new (tree);
if (current->right)
{
current = gtk_rb_node_get_first (current->right);
current->left = result;
}
else
{
current->right = result;
}
set_parent (tree, result, current);
gtk_rb_node_mark_dirty (current, TRUE);
gtk_rb_tree_insert_fixup (tree, result);
return NODE_TO_POINTER (result);
}
void
gtk_rb_tree_remove (GtkRbTree *tree,
gpointer node)
{
GtkRbNode *x, *y, *p, *real_node;
real_node = NODE_FROM_POINTER (node);
#ifdef DUMP_MODIFICATION
g_print ("delete (tree, %u); /* 0x%p */\n", position (tree, real_node), tree);
#endif /* DUMP_MODIFICATION */
y = real_node;
if (y->left && y->right)
{
y = y->right;
while (y->left)
y = y->left;
}
/* x is y's only child, or nil */
if (y->left)
x = y->left;
else
x = y->right;
/* remove y from the parent chain */
p = parent (y);
if (x != NULL)
set_parent (tree, x, p);
if (p)
{
if (y == p->left)
p->left = x;
else
p->right = x;
gtk_rb_node_mark_dirty (p, TRUE);
}
else
{
if (x == NULL)
tree->root = NULL;
}
/* We need to clean up the validity of the tree.
*/
if (is_black (y))
gtk_rb_tree_remove_node_fixup (tree, x, p);
if (y != real_node)
{
/* Move the node over */
if (is_red (real_node) != is_red (y))
y->red = !y->red;
y->left = real_node->left;
if (y->left)
set_parent (tree, y->left, y);
y->right = real_node->right;
if (y->right)
set_parent (tree, y->right, y);
p = parent (real_node);
set_parent (tree, y, p);
if (p)
{
if (p->left == real_node)
p->left = y;
else
p->right = y;
gtk_rb_node_mark_dirty (p, TRUE);
}
gtk_rb_node_mark_dirty (y, TRUE);
}
gtk_rb_node_free (tree, real_node);
}
void
gtk_rb_tree_remove_all (GtkRbTree *tree)
{
#ifdef DUMP_MODIFICATION
g_print ("delete_all (tree); /* 0x%p */\n", tree);
#endif /* DUMP_MODIFICATION */
if (tree->root)
gtk_rb_node_free_deep (tree, tree->root);
tree->root = NULL;
}

View file

@ -1,75 +0,0 @@
/* gtkrbtree.h
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/* A Red-Black Tree implementation used specifically by GtkTreeView.
*/
#ifndef __GTK_RB_TREE_H__
#define __GTK_RB_TREE_H__
#include <glib.h>
G_BEGIN_DECLS
typedef struct _GtkRbTree GtkRbTree;
typedef void (* GtkRbTreeAugmentFunc) (GtkRbTree *tree,
gpointer node_augment,
gpointer node,
gpointer left,
gpointer right);
GtkRbTree * gtk_rb_tree_new_for_size (gsize element_size,
gsize augment_size,
GtkRbTreeAugmentFunc augment_func,
GDestroyNotify clear_func,
GDestroyNotify clear_augment_func);
#define gtk_rb_tree_new(type, augment_type, augment_func, clear_func, clear_augment_func) \
gtk_rb_tree_new_for_size (sizeof (type), sizeof (augment_type), (augment_func), (clear_func), (clear_augment_func))
GtkRbTree * gtk_rb_tree_ref (GtkRbTree *tree);
void gtk_rb_tree_unref (GtkRbTree *tree);
gpointer gtk_rb_tree_get_root (GtkRbTree *tree);
gpointer gtk_rb_tree_get_first (GtkRbTree *tree);
gpointer gtk_rb_tree_get_last (GtkRbTree *tree);
gpointer gtk_rb_tree_node_get_previous (gpointer node);
gpointer gtk_rb_tree_node_get_next (gpointer node);
gpointer gtk_rb_tree_node_get_parent (gpointer node);
gpointer gtk_rb_tree_node_get_left (gpointer node);
gpointer gtk_rb_tree_node_get_right (gpointer node);
GtkRbTree * gtk_rb_tree_node_get_tree (gpointer node);
void gtk_rb_tree_node_mark_dirty (gpointer node);
gpointer gtk_rb_tree_get_augment (GtkRbTree *tree,
gpointer node);
gpointer gtk_rb_tree_insert_before (GtkRbTree *tree,
gpointer node);
gpointer gtk_rb_tree_insert_after (GtkRbTree *tree,
gpointer node);
void gtk_rb_tree_remove (GtkRbTree *tree,
gpointer node);
void gtk_rb_tree_remove_all (GtkRbTree *tree);
G_END_DECLS
#endif /* __GTK_RB_TREE_H__ */

View file

@ -1,529 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
/* #include "calls-config.h" */
#include "gtkslicelistmodel.h"
#include "gtkintl.h"
#include "gtkprivate.h"
/**
* SECTION:gtkslicelistmodel
* @title: GtkSliceListModel
* @short_description: A list model that presents a slice out of a larger list
* @see_also: #GListModel
*
* #GtkSliceListModel is a list model that takes a list model and presents a slice of
* that model.
*
* This is useful when implementing paging by setting the size to the number of elements
* per page and updating the offset whenever a different page is opened.
*/
#define DEFAULT_SIZE 10
enum {
PROP_0,
PROP_ITEM_TYPE,
PROP_MODEL,
PROP_OFFSET,
PROP_SIZE,
NUM_PROPERTIES
};
struct _GtkSliceListModel
{
GObject parent_instance;
GType item_type;
GListModel *model;
guint offset;
guint size;
guint n_items;
};
struct _GtkSliceListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GType
gtk_slice_list_model_get_item_type (GListModel *list)
{
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
return self->item_type;
}
static guint
gtk_slice_list_model_get_n_items (GListModel *list)
{
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
guint n_items;
if (self->model == NULL)
return 0;
/* XXX: This can be done without calling g_list_model_get_n_items() on the parent model
* by checking if model.get_item(offset + size) != NULL */
n_items = g_list_model_get_n_items (self->model);
if (n_items <= self->offset)
return 0;
n_items -= self->offset;
return MIN (n_items, self->size);
}
static gpointer
gtk_slice_list_model_get_item (GListModel *list,
guint position)
{
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
if (self->model == NULL)
return NULL;
if (position >= self->size)
return NULL;
return g_list_model_get_item (self->model, position + self->offset);
}
static void
gtk_slice_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_slice_list_model_get_item_type;
iface->get_n_items = gtk_slice_list_model_get_n_items;
iface->get_item = gtk_slice_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkSliceListModel, gtk_slice_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_slice_list_model_model_init))
static void
gtk_slice_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkSliceListModel *self)
{
if (position >= self->offset + self->size)
return;
if (position < self->offset)
{
guint skip = MIN (removed, added);
skip = MIN (skip, self->offset - position);
position += skip;
removed -= skip;
added -= skip;
}
if (removed == added)
{
guint changed = removed;
if (changed == 0)
return;
g_assert (position >= self->offset);
position -= self->offset;
changed = MIN (changed, self->size - position);
g_list_model_items_changed (G_LIST_MODEL (self), position, changed, changed);
}
else
{
guint n_after, n_before;
guint skip;
if (position > self->offset)
skip = position - self->offset;
else
skip = 0;
n_after = g_list_model_get_n_items (self->model);
n_before = n_after - added + removed;
n_after = CLAMP (n_after, self->offset, self->offset + self->size) - self->offset;
n_before = CLAMP (n_before, self->offset, self->offset + self->size) - self->offset;
g_list_model_items_changed (G_LIST_MODEL (self), skip, n_before - skip, n_after - skip);
}
}
static void
gtk_slice_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
switch (prop_id)
{
case PROP_ITEM_TYPE:
self->item_type = g_value_get_gtype (value);
break;
case PROP_MODEL:
gtk_slice_list_model_set_model (self, g_value_get_object (value));
break;
case PROP_OFFSET:
gtk_slice_list_model_set_offset (self, g_value_get_uint (value));
break;
case PROP_SIZE:
gtk_slice_list_model_set_size (self, g_value_get_uint (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_slice_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
switch (prop_id)
{
case PROP_ITEM_TYPE:
g_value_set_gtype (value, self->item_type);
break;
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
case PROP_OFFSET:
g_value_set_uint (value, self->offset);
break;
case PROP_SIZE:
g_value_set_uint (value, self->size);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_slice_list_model_clear_model (GtkSliceListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_slice_list_model_items_changed_cb, self);
g_clear_object (&self->model);
}
static void
gtk_slice_list_model_dispose (GObject *object)
{
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
gtk_slice_list_model_clear_model (self);
G_OBJECT_CLASS (gtk_slice_list_model_parent_class)->dispose (object);
};
static void
gtk_slice_list_model_class_init (GtkSliceListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_slice_list_model_set_property;
gobject_class->get_property = gtk_slice_list_model_get_property;
gobject_class->dispose = gtk_slice_list_model_dispose;
/**
* GtkSliceListModel:item-type:
*
* The #GType for elements of this object
*/
properties[PROP_ITEM_TYPE] =
g_param_spec_gtype ("item-type",
P_("Item type"),
P_("The type of elements of this object"),
G_TYPE_OBJECT,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkSliceListModel:model:
*
* Child model to take slice from
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("Child model to take slice from"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkSliceListModel:offset:
*
* Offset of slice
*/
properties[PROP_OFFSET] =
g_param_spec_uint ("offset",
P_("Offset"),
P_("Offset of slice"),
0, G_MAXUINT, 0,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkSliceListModel:size:
*
* Maximum size of slice
*/
properties[PROP_SIZE] =
g_param_spec_uint ("size",
P_("Size"),
P_("Maximum size of slice"),
0, G_MAXUINT, DEFAULT_SIZE,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_slice_list_model_init (GtkSliceListModel *self)
{
self->size = DEFAULT_SIZE;
}
/**
* gtk_slice_list_model_new:
* @model: (transfer none): The model to use
* @offset: the offset of the slice
* @size: maximum size of the slice
*
* Creates a new slice model that presents the slice from @offset to
* @offset + @size our of the given @model.
*
* Returns: A new #GtkSliceListModel
**/
GtkSliceListModel *
gtk_slice_list_model_new (GListModel *model,
guint offset,
guint size)
{
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
return g_object_new (GTK_TYPE_SLICE_LIST_MODEL,
"item-type", g_list_model_get_item_type (model),
"model", model,
"offset", offset,
"size", size,
NULL);
}
/**
* gtk_slice_list_model_new_for_type:
* @item_type: the type of items
*
* Creates a new empty #GtkSliceListModel for the given @item_type that
* can be set up later.
*
* Returns: a new empty #GtkSliceListModel
**/
GtkSliceListModel *
gtk_slice_list_model_new_for_type (GType item_type)
{
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
return g_object_new (GTK_TYPE_SLICE_LIST_MODEL,
"item-type", item_type,
NULL);
}
/**
* gtk_slice_list_model_set_model:
* @self: a #GtkSliceListModel
* @model: (allow-none): The model to be sliced
*
* Sets the model to show a slice of. The model's item type must conform
* to @self's item type.
*
**/
void
gtk_slice_list_model_set_model (GtkSliceListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (self->model == model)
return;
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
gtk_slice_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_slice_list_model_items_changed_cb), self);
added = g_list_model_get_n_items (G_LIST_MODEL (self));
}
else
{
added = 0;
}
if (removed > 0 || added > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_slice_list_model_get_model:
* @self: a #GtkSliceListModel
*
* Gets the model that is curently being used or %NULL if none.
*
* Returns: (nullable) (transfer none): The model in use
**/
GListModel *
gtk_slice_list_model_get_model (GtkSliceListModel *self)
{
g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), NULL);
return self->model;
}
/**
* gtk_slice_list_model_set_offset:
* @self: a #GtkSliceListModel
* @offset: the new offset to use
*
* Sets the offset into the original model for this slice.
*
* If the offset is too large for the sliced model,
* @self will end up empty.
**/
void
gtk_slice_list_model_set_offset (GtkSliceListModel *self,
guint offset)
{
guint before, after;
g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
if (self->offset == offset)
return;
before = g_list_model_get_n_items (G_LIST_MODEL (self));
self->offset = offset;
after = g_list_model_get_n_items (G_LIST_MODEL (self));
if (before > 0 || after > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, before, after);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_OFFSET]);
}
/**
* gtk_slice_list_model_get_offset:
* @self: a #GtkSliceListModel
*
* Gets the offset set via gtk_slice_list_model_set_offset()
*
* Returns: The offset
**/
guint
gtk_slice_list_model_get_offset (GtkSliceListModel *self)
{
g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), 0);
return self->offset;
}
/**
* gtk_slice_list_model_set_size:
* @self: a #GtkSliceListModel
* @size: the maximum size
*
* Sets the maximum size. @self will never have more items
* than @size.
*
* It can however have fewer items if the offset is too large or
* the model sliced from doesn't have enough items.
*/
void
gtk_slice_list_model_set_size (GtkSliceListModel *self,
guint size)
{
guint before, after;
g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
if (self->size == size)
return;
before = g_list_model_get_n_items (G_LIST_MODEL (self));
self->size = size;
after = g_list_model_get_n_items (G_LIST_MODEL (self));
if (before > after)
g_list_model_items_changed (G_LIST_MODEL (self), after, before - after, 0);
else if (before < after)
g_list_model_items_changed (G_LIST_MODEL (self), before, 0, after - before);
/* else nothing */
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIZE]);
}
/**
* gtk_slice_list_model_get_size:
* @self: a #GtkSliceListModel
*
* Gets the size set via gtk_slice_list_model_set_size().
*
* Returns: The size
**/
guint
gtk_slice_list_model_get_size (GtkSliceListModel *self)
{
g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), DEFAULT_SIZE);
return self->size;
}

View file

@ -1,65 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_SLICE_LIST_MODEL_H__
#define __GTK_SLICE_LIST_MODEL_H__
#define GTK_COMPILATION
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gio/gio.h>
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
#define GTK_TYPE_SLICE_LIST_MODEL (gtk_slice_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSliceListModel, gtk_slice_list_model, GTK, SLICE_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkSliceListModel * gtk_slice_list_model_new (GListModel *model,
guint offset,
guint size);
GDK_AVAILABLE_IN_ALL
GtkSliceListModel * gtk_slice_list_model_new_for_type (GType item_type);
GDK_AVAILABLE_IN_ALL
void gtk_slice_list_model_set_model (GtkSliceListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_slice_list_model_get_model (GtkSliceListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_slice_list_model_set_offset (GtkSliceListModel *self,
guint offset);
GDK_AVAILABLE_IN_ALL
guint gtk_slice_list_model_get_offset (GtkSliceListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_slice_list_model_set_size (GtkSliceListModel *self,
guint size);
GDK_AVAILABLE_IN_ALL
guint gtk_slice_list_model_get_size (GtkSliceListModel *self);
G_END_DECLS
#endif /* __GTK_SLICE_LIST_MODEL_H__ */

View file

@ -1,207 +0,0 @@
/*
* Copyright © 2019 Matthias Clasen
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
/* #include "calls-config.h" */
#include "gtksorter.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
/**
* SECTION:gtksorter
* @title: GtkSorter
* @Short_description: Sorting items
* @See_also: #GtkSortListModel
*
* #GtkSorter is the way to describe sorting criteria.
* Its primary user is #GtkSortListModel.
*
* The model will use a sorter to determine the order in which its items should appear
* by calling gtk_sorter_compare() for pairs of items.
*
* Sorters may change their sorting behavior through their lifetime. In that case,
* they call gtk_sorter_changed(), which will emit the #GtkSorter::changed signal to
* notify that the sort order is no longer valid and should be updated by calling
* gtk_sorter_compare() again.
*
* GTK provides various pre-made sorter implementations for common sorting operations.
* #GtkColumnView has built-in support for sorting lists via the #GtkColumnViewColumn:sorter
* property, where the user can change the sorting by clicking on list headers.
*
* Of course, in particular for large lists, it is also possible to subclass #GtkSorter
* and provide one's own sorter.
*/
enum {
CHANGED,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkSorter, gtk_sorter, G_TYPE_OBJECT)
static guint signals[LAST_SIGNAL] = { 0 };
static GtkOrdering
gtk_sorter_default_compare (GtkSorter *self,
gpointer item1,
gpointer item2)
{
g_critical ("Sorter of type '%s' does not implement GtkSorter::compare", G_OBJECT_TYPE_NAME (self));
return GTK_ORDERING_EQUAL;
}
static GtkSorterOrder
gtk_sorter_default_get_order (GtkSorter *self)
{
return GTK_SORTER_ORDER_PARTIAL;
}
static void
gtk_sorter_class_init (GtkSorterClass *class)
{
class->compare = gtk_sorter_default_compare;
class->get_order = gtk_sorter_default_get_order;
/**
* GtkSorter::changed:
* @self: The #GtkSorter
* @change: how the sorter changed
*
* This signal is emitted whenever the sorter changed. Users of the sorter
* should then update the sort order again via gtk_sorter_compare().
*
* #GtkSortListModel handles this signal automatically.
*
* Depending on the @change parameter, it may be possible to update
* the sort order without a full resorting. Refer to the #GtkSorterChange
* documentation for details.
*/
signals[CHANGED] =
g_signal_new (I_("changed"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__ENUM,
G_TYPE_NONE, 1,
GTK_TYPE_SORTER_CHANGE);
g_signal_set_va_marshaller (signals[CHANGED],
G_TYPE_FROM_CLASS (class),
g_cclosure_marshal_VOID__ENUMv);
}
static void
gtk_sorter_init (GtkSorter *self)
{
}
/**
* gtk_sorter_compare:
* @self: a #GtkSorter
* @item1: (type GObject) (transfer none): first item to compare
* @item2: (type GObject) (transfer none): second item to compare
*
* Compares two given items according to the sort order implemented
* by the sorter.
*
* Sorters implement a partial order:
* * It is reflexive, ie a = a
* * It is antisymmetric, ie if a < b and b < a, then a = b
* * It is transitive, ie given any 3 items with a b and b c,
* then a c
*
* The sorter may signal it conforms to additional constraints
* via the return value of gtk_sorter_get_order().
*
* Returns: %GTK_ORDERING_EQUAL if @item1 == @item2,
* %GTK_ORDERING_SMALLER if @item1 < @item2,
* %GTK_ORDERING_LARGER if @item1 > @item2
*/
GtkOrdering
gtk_sorter_compare (GtkSorter *self,
gpointer item1,
gpointer item2)
{
GtkOrdering result;
g_return_val_if_fail (GTK_IS_SORTER (self), GTK_ORDERING_EQUAL);
g_return_val_if_fail (item1 && item2, GTK_ORDERING_EQUAL);
if (item1 == item2)
return GTK_ORDERING_EQUAL;
result = GTK_SORTER_GET_CLASS (self)->compare (self, item1, item2);
#ifdef G_ENABLE_DEBUG
if (result < -1 || result > 1)
{
g_critical ("A sorter of type \"%s\" returned %d, which is not a valid GtkOrdering result.\n"
"Did you forget to call gtk_ordering_from_cmpfunc()?",
G_OBJECT_TYPE_NAME (self), (int) result);
}
#endif
return result;
}
/**
* gtk_sorter_get_order:
* @self: a #GtkSorter
*
* Gets the order that @self conforms to. See #GtkSorterOrder for details
* of the possible return values.
*
* This function is intended to allow optimizations.
*
* Returns: The order
**/
GtkSorterOrder
gtk_sorter_get_order (GtkSorter *self)
{
g_return_val_if_fail (GTK_IS_SORTER (self), GTK_SORTER_ORDER_PARTIAL);
return GTK_SORTER_GET_CLASS (self)->get_order (self);
}
/**
* gtk_sorter_changed:
* @self: a #GtkSorter
* @change: How the sorter changed
*
* Emits the #GtkSorter::changed signal to notify all users of the sorter
* that it has changed. Users of the sorter should then update the sort
* order via gtk_sorter_compare().
*
* Depending on the @change parameter, it may be possible to update
* the sort order without a full resorting. Refer to the #GtkSorterChange
* documentation for details.
*
* This function is intended for implementors of #GtkSorter subclasses and
* should not be called from other functions.
*/
void
gtk_sorter_changed (GtkSorter *self,
GtkSorterChange change)
{
g_return_if_fail (GTK_IS_SORTER (self));
g_signal_emit (self, signals[CHANGED], 0, change);
}

View file

@ -1,131 +0,0 @@
/*
* Copyright © 2019 Matthias Clasen
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#ifndef __GTK_SORTER_H__
#define __GTK_SORTER_H__
#define GTK_COMPILATION
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkenums.h>
G_BEGIN_DECLS
/**
* GtkSorterOrder:
* @GTK_SORTER_ORDER_PARTIAL: A partial order. And #GtkOrdering is possible.
* @GTK_SORTER_ORDER_INVALID: An invalid order. gtk_sorter_compare() will
* always return %GTK_ORDERING_INVALID if both items are unequal.
* @GTK_SORTER_ORDER_NONE: No order, all elements are considered equal.
* gtk_sorter_compare() will only return %GTK_ORDERING_EQUAL or
* %GTK_ORDERING_INVALID.
* @GTK_SORTER_ORDER_TOTAL: A total order. gtk_sorter_compare() will only
* return %GTK_ORDERING_EQUAL if an item is compared with itself. Two
* different items will never cause this value to be returned.
*
* Describes the type of order that a #GtkSorter may describe.
*/
typedef enum {
GTK_SORTER_ORDER_PARTIAL,
GTK_SORTER_ORDER_NONE,
GTK_SORTER_ORDER_TOTAL
} GtkSorterOrder;
typedef enum {
GTK_ORDERING_SMALLER = -1,
GTK_ORDERING_EQUAL = 0,
GTK_ORDERING_LARGER = 1
} GtkOrdering;
/**
* GtkSorterChange:
* @GTK_SORTER_CHANGE_DIFFERENT: The sorter change cannot be described
* by any of the other enumeration values
* @GTK_SORTER_CHANGE_INVERTED: The sort order was inverted. Comparisons
* that returned %GTK_ORDERING_SMALLER now return %GTK_ORDERING_LARGER
* and vice versa. Other comparisons return the same values as before.
* @GTK_SORTER_CHANGE_LESS_STRICT: The sorter is less strict: Comparisons
* may now return %GTK_ORDERING_EQUAL that did not do so before.
* @GTK_SORTER_CHANGE_MORE_STRICT: The sorter is more strict: Comparisons
* that did return %GTK_ORDERING_EQUAL may not do so anymore.
*
* Describes changes in a sorter in more detail and allows users
* to optimize resorting.
*/
typedef enum {
GTK_SORTER_CHANGE_DIFFERENT,
GTK_SORTER_CHANGE_INVERTED,
GTK_SORTER_CHANGE_LESS_STRICT,
GTK_SORTER_CHANGE_MORE_STRICT
} GtkSorterChange;
#define GTK_TYPE_SORTER (gtk_sorter_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (GtkSorter, gtk_sorter, GTK, SORTER, GObject)
/**
* GtkSorterClass
* @compare: Compare two items. See gtk_sorter_compare() for details.
* @get_order: Get the #GtkSorderOrder that applies to the current sorter.
* If unimplemented, it returns %GTK_SORTER_ORDER_PARTIAL.
*
* The virtual table for #GtkSorter.
*/
struct _GtkSorterClass
{
GObjectClass parent_class;
GtkOrdering (* compare) (GtkSorter *self,
gpointer item1,
gpointer item2);
/* optional */
GtkSorterOrder (* get_order) (GtkSorter *self);
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
void (*_gtk_reserved4) (void);
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
void (*_gtk_reserved8) (void);
};
GDK_AVAILABLE_IN_ALL
GtkOrdering gtk_sorter_compare (GtkSorter *self,
gpointer item1,
gpointer item2);
GDK_AVAILABLE_IN_ALL
GtkSorterOrder gtk_sorter_get_order (GtkSorter *self);
/* for sorter implementations */
GDK_AVAILABLE_IN_ALL
void gtk_sorter_changed (GtkSorter *self,
GtkSorterChange change);
G_END_DECLS
#endif /* __GTK_SORTER_H__ */

View file

@ -1,595 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
/* #include "calls-config.h" */
#include "gtksortlistmodel.h"
#include "gtkintl.h"
#include "gtkprivate.h"
/**
* SECTION:gtksortlistmodel
* @title: GtkSortListModel
* @short_description: A list model that sorts its items
* @see_also: #GListModel, #GtkSorter
*
* #GtkSortListModel is a list model that takes a list model and
* sorts its elements according to a #GtkSorter.
*
* #GtkSortListModel is a generic model and because of that it
* cannot take advantage of any external knowledge when sorting.
* If you run into performance issues with #GtkSortListModel, it
* is strongly recommended that you write your own sorting list
* model.
*/
enum {
PROP_0,
PROP_ITEM_TYPE,
PROP_MODEL,
PROP_SORTER,
NUM_PROPERTIES
};
typedef struct _GtkSortListEntry GtkSortListEntry;
struct _GtkSortListModel
{
GObject parent_instance;
GType item_type;
GListModel *model;
GtkSorter *sorter;
GSequence *sorted; /* NULL if known unsorted */
GSequence *unsorted; /* NULL if known unsorted */
};
struct _GtkSortListModelClass
{
GObjectClass parent_class;
};
struct _GtkSortListEntry
{
GSequenceIter *sorted_iter;
GSequenceIter *unsorted_iter;
gpointer item; /* holds ref */
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static void
gtk_sort_list_entry_free (gpointer data)
{
GtkSortListEntry *entry = data;
g_object_unref (entry->item);
g_slice_free (GtkSortListEntry, entry);
}
static GType
gtk_sort_list_model_get_item_type (GListModel *list)
{
GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
return self->item_type;
}
static guint
gtk_sort_list_model_get_n_items (GListModel *list)
{
GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
if (self->model == NULL)
return 0;
return g_list_model_get_n_items (self->model);
}
static gpointer
gtk_sort_list_model_get_item (GListModel *list,
guint position)
{
GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
GSequenceIter *iter;
GtkSortListEntry *entry;
if (self->model == NULL)
return NULL;
if (self->unsorted == NULL)
return g_list_model_get_item (self->model, position);
iter = g_sequence_get_iter_at_pos (self->sorted, position);
if (g_sequence_iter_is_end (iter))
return NULL;
entry = g_sequence_get (iter);
return g_object_ref (entry->item);
}
static void
gtk_sort_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_sort_list_model_get_item_type;
iface->get_n_items = gtk_sort_list_model_get_n_items;
iface->get_item = gtk_sort_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkSortListModel, gtk_sort_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sort_list_model_model_init))
static void
gtk_sort_list_model_remove_items (GtkSortListModel *self,
guint position,
guint n_items,
guint *unmodified_start,
guint *unmodified_end)
{
GSequenceIter *unsorted_iter;
guint i, pos, start, end, length_before;
start = end = length_before = g_sequence_get_length (self->sorted);
unsorted_iter = g_sequence_get_iter_at_pos (self->unsorted, position);
for (i = 0; i < n_items ; i++)
{
GtkSortListEntry *entry;
GSequenceIter *next;
next = g_sequence_iter_next (unsorted_iter);
entry = g_sequence_get (unsorted_iter);
pos = g_sequence_iter_get_position (entry->sorted_iter);
start = MIN (start, pos);
end = MIN (end, length_before - i - 1 - pos);
g_sequence_remove (entry->unsorted_iter);
g_sequence_remove (entry->sorted_iter);
unsorted_iter = next;
}
*unmodified_start = start;
*unmodified_end = end;
}
static int
_sort_func (gconstpointer item1,
gconstpointer item2,
gpointer data)
{
GtkSortListEntry *entry1 = (GtkSortListEntry *) item1;
GtkSortListEntry *entry2 = (GtkSortListEntry *) item2;
GtkOrdering result;
result = gtk_sorter_compare (GTK_SORTER (data), entry1->item, entry2->item);
if (result == GTK_ORDERING_EQUAL)
result = g_sequence_iter_compare (entry1->unsorted_iter, entry2->unsorted_iter);
return result;
}
static void
gtk_sort_list_model_add_items (GtkSortListModel *self,
guint position,
guint n_items,
guint *unmodified_start,
guint *unmodified_end)
{
GSequenceIter *unsorted_end;
guint i, pos, start, end, length_before;
unsorted_end = g_sequence_get_iter_at_pos (self->unsorted, position);
start = end = length_before = g_sequence_get_length (self->sorted);
for (i = 0; i < n_items; i++)
{
GtkSortListEntry *entry = g_slice_new0 (GtkSortListEntry);
entry->item = g_list_model_get_item (self->model, position + i);
entry->unsorted_iter = g_sequence_insert_before (unsorted_end, entry);
entry->sorted_iter = g_sequence_insert_sorted (self->sorted, entry, _sort_func, self->sorter);
if (unmodified_start != NULL || unmodified_end != NULL)
{
pos = g_sequence_iter_get_position (entry->sorted_iter);
start = MIN (start, pos);
end = MIN (end, length_before + i - pos);
}
}
if (unmodified_start)
*unmodified_start = start;
if (unmodified_end)
*unmodified_end = end;
}
static void
gtk_sort_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkSortListModel *self)
{
guint n_items, start, end, start2, end2;
if (removed == 0 && added == 0)
return;
if (self->sorted == NULL)
{
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
return;
}
gtk_sort_list_model_remove_items (self, position, removed, &start, &end);
gtk_sort_list_model_add_items (self, position, added, &start2, &end2);
start = MIN (start, start2);
end = MIN (end, end2);
n_items = g_sequence_get_length (self->sorted) - start - end;
g_list_model_items_changed (G_LIST_MODEL (self), start, n_items - added + removed, n_items);
}
static void
gtk_sort_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
switch (prop_id)
{
case PROP_ITEM_TYPE:
self->item_type = g_value_get_gtype (value);
break;
case PROP_MODEL:
gtk_sort_list_model_set_model (self, g_value_get_object (value));
break;
case PROP_SORTER:
gtk_sort_list_model_set_sorter (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_sort_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
switch (prop_id)
{
case PROP_ITEM_TYPE:
g_value_set_gtype (value, self->item_type);
break;
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
case PROP_SORTER:
g_value_set_object (value, self->sorter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void gtk_sort_list_model_resort (GtkSortListModel *self);
static void
gtk_sort_list_model_sorter_changed_cb (GtkSorter *sorter,
int change,
GtkSortListModel *self)
{
gtk_sort_list_model_resort (self);
}
static void
gtk_sort_list_model_clear_model (GtkSortListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_sort_list_model_items_changed_cb, self);
g_clear_object (&self->model);
g_clear_pointer (&self->sorted, g_sequence_free);
g_clear_pointer (&self->unsorted, g_sequence_free);
}
static void
gtk_sort_list_model_clear_sorter (GtkSortListModel *self)
{
if (self->sorter == NULL)
return;
g_signal_handlers_disconnect_by_func (self->sorter, gtk_sort_list_model_sorter_changed_cb, self);
g_clear_object (&self->sorter);
}
static void
gtk_sort_list_model_dispose (GObject *object)
{
GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
gtk_sort_list_model_clear_model (self);
gtk_sort_list_model_clear_sorter (self);
G_OBJECT_CLASS (gtk_sort_list_model_parent_class)->dispose (object);
};
static void
gtk_sort_list_model_class_init (GtkSortListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_sort_list_model_set_property;
gobject_class->get_property = gtk_sort_list_model_get_property;
gobject_class->dispose = gtk_sort_list_model_dispose;
/**
* GtkSortListModel:sorter:
*
* The sorter for this model
*/
properties[PROP_SORTER] =
g_param_spec_object ("sorter",
P_("Sorter"),
P_("The sorter for this model"),
GTK_TYPE_SORTER,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkSortListModel:item-type:
*
* The #GType for items of this model
*/
properties[PROP_ITEM_TYPE] =
g_param_spec_gtype ("item-type",
P_("Item type"),
P_("The type of items of this list"),
G_TYPE_OBJECT,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkSortListModel:model:
*
* The model being sorted
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("The model being sorted"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_sort_list_model_init (GtkSortListModel *self)
{
}
/**
* gtk_sort_list_model_new:
* @model: the model to sort
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Creates a new sort list model that uses the @sorter to sort @model.
*
* Returns: a new #GtkSortListModel
**/
GtkSortListModel *
gtk_sort_list_model_new (GListModel *model,
GtkSorter *sorter)
{
GtkSortListModel *result;
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
g_return_val_if_fail (sorter == NULL || GTK_IS_SORTER (sorter), NULL);
result = g_object_new (GTK_TYPE_SORT_LIST_MODEL,
"item-type", g_list_model_get_item_type (model),
"model", model,
"sorter", sorter,
NULL);
return result;
}
/**
* gtk_sort_list_model_new_for_type:
* @item_type: the type of the items that will be returned
*
* Creates a new empty sort list model set up to return items of type @item_type.
* It is up to the application to set a proper sort function and model to ensure
* the item type is matched.
*
* Returns: a new #GtkSortListModel
**/
GtkSortListModel *
gtk_sort_list_model_new_for_type (GType item_type)
{
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
return g_object_new (GTK_TYPE_SORT_LIST_MODEL,
"item-type", item_type,
NULL);
}
static void
gtk_sort_list_model_create_sequences (GtkSortListModel *self)
{
if (self->sorter == NULL || self->model == NULL)
return;
self->sorted = g_sequence_new (gtk_sort_list_entry_free);
self->unsorted = g_sequence_new (NULL);
gtk_sort_list_model_add_items (self, 0, g_list_model_get_n_items (self->model), NULL, NULL);
}
/**
* gtk_sort_list_model_set_model:
* @self: a #GtkSortListModel
* @model: (allow-none): The model to be sorted
*
* Sets the model to be sorted. The @model's item type must conform to
* the item type of @self.
**/
void
gtk_sort_list_model_set_model (GtkSortListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (model)
{
g_return_if_fail (g_type_is_a (g_list_model_get_item_type (model), self->item_type));
}
if (self->model == model)
return;
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
gtk_sort_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sort_list_model_items_changed_cb), self);
added = g_list_model_get_n_items (model);
gtk_sort_list_model_create_sequences (self);
}
else
added = 0;
if (removed > 0 || added > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_sort_list_model_get_model:
* @self: a #GtkSortListModel
*
* Gets the model currently sorted or %NULL if none.
*
* Returns: (nullable) (transfer none): The model that gets sorted
**/
GListModel *
gtk_sort_list_model_get_model (GtkSortListModel *self)
{
g_return_val_if_fail (GTK_IS_SORT_LIST_MODEL (self), NULL);
return self->model;
}
static void
gtk_sort_list_model_resort (GtkSortListModel *self)
{
guint n_items;
g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
if (self->sorted == NULL)
return;
n_items = g_list_model_get_n_items (self->model);
if (n_items <= 1)
return;
g_sequence_sort (self->sorted, _sort_func, self->sorter);
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
}
/**
* gtk_sort_list_model_set_sorter:
* @self: a #GtkSortListModel
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Sets a new sorter on @self.
*/
void
gtk_sort_list_model_set_sorter (GtkSortListModel *self,
GtkSorter *sorter)
{
guint n_items;
g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
gtk_sort_list_model_clear_sorter (self);
if (sorter)
{
self->sorter = g_object_ref (sorter);
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sort_list_model_sorter_changed_cb), self);
}
g_clear_pointer (&self->unsorted, g_sequence_free);
g_clear_pointer (&self->sorted, g_sequence_free);
gtk_sort_list_model_create_sequences (self);
n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
if (n_items > 1)
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
}
/**
* gtk_sort_list_model_get_sorter:
* @self: a #GtkSortLisTModel
*
* Gets the sorter that is used to sort @self.
*
* Returns: (nullable) (transfer none): the sorter of #self
*/
GtkSorter *
gtk_sort_list_model_get_sorter (GtkSortListModel *self)
{
g_return_val_if_fail (GTK_IS_SORT_LIST_MODEL (self), NULL);
return self->sorter;
}

View file

@ -1,61 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_SORT_LIST_MODEL_H__
#define __GTK_SORT_LIST_MODEL_H__
#define GTK_COMPILATION
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gio/gio.h>
#include <gtk/gtkwidget.h>
#include "gtksorter.h"
G_BEGIN_DECLS
#define GTK_TYPE_SORT_LIST_MODEL (gtk_sort_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSortListModel, gtk_sort_list_model, GTK, SORT_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkSortListModel * gtk_sort_list_model_new (GListModel *model,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSortListModel * gtk_sort_list_model_new_for_type (GType item_type);
GDK_AVAILABLE_IN_ALL
void gtk_sort_list_model_set_sorter (GtkSortListModel *self,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_sort_list_model_get_sorter (GtkSortListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_sort_list_model_set_model (GtkSortListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_sort_list_model_get_model (GtkSortListModel *self);
G_END_DECLS
#endif /* __GTK_SORT_LIST_MODEL_H__ */

View file

@ -1,89 +0,0 @@
#include <gtk/gtk.h>
#include "gtkprivate.h"
#include "gtktypebuiltins.h"
/* enumerations from "gtksorter.h" */
GType
gtk_sorter_order_get_type (void)
{
static gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
static const GEnumValue values[] = {
{ GTK_SORTER_ORDER_PARTIAL, "GTK_SORTER_ORDER_PARTIAL", "partial" },
{ GTK_SORTER_ORDER_NONE, "GTK_SORTER_ORDER_NONE", "none" },
{ GTK_SORTER_ORDER_TOTAL, "GTK_SORTER_ORDER_TOTAL", "total" },
{ 0, NULL, NULL }
};
GType g_define_type_id =
g_enum_register_static (g_intern_static_string ("GtkSorterOrder"), values);
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
}
GType
gtk_sorter_change_get_type (void)
{
static gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
static const GEnumValue values[] = {
{ GTK_SORTER_CHANGE_DIFFERENT, "GTK_SORTER_CHANGE_DIFFERENT", "different" },
{ GTK_SORTER_CHANGE_INVERTED, "GTK_SORTER_CHANGE_INVERTED", "inverted" },
{ GTK_SORTER_CHANGE_LESS_STRICT, "GTK_SORTER_CHANGE_LESS_STRICT", "less-strict" },
{ GTK_SORTER_CHANGE_MORE_STRICT, "GTK_SORTER_CHANGE_MORE_STRICT", "more-strict" },
{ 0, NULL, NULL }
};
GType g_define_type_id =
g_enum_register_static (g_intern_static_string ("GtkSorterChange"), values);
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
}
/* enumerations from "gtkfilter.h" */
GType
gtk_filter_match_get_type (void)
{
static gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
static const GEnumValue values[] = {
{ GTK_FILTER_MATCH_SOME, "GTK_FILTER_MATCH_SOME", "some" },
{ GTK_FILTER_MATCH_NONE, "GTK_FILTER_MATCH_NONE", "none" },
{ GTK_FILTER_MATCH_ALL, "GTK_FILTER_MATCH_ALL", "all" },
{ 0, NULL, NULL }
};
GType g_define_type_id =
g_enum_register_static (g_intern_static_string ("GtkFilterMatch"), values);
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
}
GType
gtk_filter_change_get_type (void)
{
static gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
static const GEnumValue values[] = {
{ GTK_FILTER_CHANGE_DIFFERENT, "GTK_FILTER_CHANGE_DIFFERENT", "different" },
{ GTK_FILTER_CHANGE_LESS_STRICT, "GTK_FILTER_CHANGE_LESS_STRICT", "less-strict" },
{ GTK_FILTER_CHANGE_MORE_STRICT, "GTK_FILTER_CHANGE_MORE_STRICT", "more-strict" },
{ 0, NULL, NULL }
};
GType g_define_type_id =
g_enum_register_static (g_intern_static_string ("GtkFilterChange"), values);
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
}

View file

@ -1,18 +0,0 @@
#include <glib-object.h>
#include <gdk/gdk.h>
#include "gtksorter.h"
#include "gtkfilter.h"
/* enumerations from "gtksorter.h" */
GDK_AVAILABLE_IN_ALL GType gtk_sorter_order_get_type (void) G_GNUC_CONST;
#define GTK_TYPE_SORTER_ORDER (gtk_sorter_order_get_type ())
GDK_AVAILABLE_IN_ALL GType gtk_sorter_change_get_type (void) G_GNUC_CONST;
#define GTK_TYPE_SORTER_CHANGE (gtk_sorter_change_get_type ())
/* enumerations from "gtkfilter.h" */
GDK_AVAILABLE_IN_ALL GType gtk_filter_match_get_type (void) G_GNUC_CONST;
#define GTK_TYPE_FILTER_MATCH (gtk_filter_match_get_type ())
GDK_AVAILABLE_IN_ALL GType gtk_filter_change_get_type (void) G_GNUC_CONST;
#define GTK_TYPE_FILTER_CHANGE (gtk_filter_change_get_type ())

View file

@ -1,39 +0,0 @@
#
# Copyright (C) 2022 Purism SPC
#
# This file is part of Calls.
#
# Calls is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Calls 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 General Public License
# along with Calls. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Evangelos Ribeiro Tzaras <devrtz@fortysixandtwo.eu>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
gtklistmodel_sources = files([
'gtkmodels.h',
'gtkcustomfilter.c', 'gtkcustomfilter.h',
'gtkcustomsorter.c', 'gtkcustomsorter.h',
'gtkfilter.c', 'gtkfilter.h',
'gtkfilterlistmodel.c', 'gtkfilterlistmodel.h',
'gtkflattenlistmodel.c', 'gtkflattenlistmodel.h',
'gtkintl.h',
'gtkprivate.h',
'gtkrbtree.c',
'gtkrbtreeprivate.h',
'gtkslicelistmodel.c', 'gtkslicelistmodel.h',
'gtksorter.c', 'gtksorter.h',
'gtksortlistmodel.c', 'gtksortlistmodel.h',
'gtktypebuiltins.c', 'gtktypebuiltins.h',
])

View file

@ -26,12 +26,12 @@ gnome = import('gnome')
subdir('dbus')
src_include = include_directories('.', 'gtklistmodels')
src_include = include_directories('.')
calls_includes = [ top_include, src_include ]
calls_deps = [ dependency('gobject-2.0', version: '>= 2.58'),
dependency('gtk+-3.0'),
dependency('libhandy-1', version: '>= 1.4.0'),
dependency('gtk4', version: '>= @0@'.format(gtk_version)),
dependency('libadwaita-1', version: '>= 1.2'),
dependency('libfeedback-0.0'),
dependency('libpeas-1.0'),
dependency('gom-1.0'),
@ -86,8 +86,6 @@ calls_generated_sources = [
generated_dbus_sources,
]
subdir('gtklistmodels')
calls_sources = files([
'calls-account.c', 'calls-account.h',
'calls-account-overview.c', 'calls-account-overview.h',
@ -126,7 +124,7 @@ calls_sources = files([
'calls-ui-call-data.c', 'calls-ui-call-data.h',
'calls-ussd.c', 'calls-ussd.h',
'calls-util.c', 'calls-util.h',
]) + calls_generated_sources + gtklistmodel_sources
]) + calls_generated_sources
calls_config_data = config_data

View file

@ -1,10 +1,9 @@
keypad > grid > button, .dial-button, .delete-button .rounded-button {
border-radius: 9999px;
-gtk-outline-radius: 9999px;
}
.dial-button {
padding: 19px 42px
padding: 19px 42px;
}
.delete-button {

View file

@ -1,122 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="libhandy" version="1.0"/>
<template class="CallsAccountOverview" parent="HdyWindow">
<property name="visible">True</property>
<template class="CallsAccountOverview" parent="AdwWindow">
<property name="default-width">380</property>
<property name="default-height">660</property>
<signal name="delete-event" handler="gtk_widget_hide_on_delete"/>
<property name="hide-on-close">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="HdyHeaderBar">
<property name="title" translatable="yes">VoIP Accounts</property>
<property name="show-close-button">True</property>
<property name="visible">True</property>
<child>
<object class="GtkSpinner" id="spinner">
<property name="visible">True</property>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar">
<child type="end">
<object class="GtkSpinner" id="spinner"/>
</child>
</object>
</child>
<child>
<object class="GtkOverlay">
<property name="visible">True</property>
<child type="overlay">
<object class="CallsInAppNotification" id="in_app_notification">
<property name="visible">True</property>
</object>
</child>
<child>
<property name="content">
<object class="AdwToastOverlay" id="toast_overlay">
<property name="child">
<object class="GtkStack" id="stack">
<property name="visible">True</property>
<property name="vexpand">True</property>
<property name="transition-type">crossfade</property>
<!-- First child type: No accounts present: Show a blurb and a Add button -->
<child>
<object class="HdyStatusPage" id="intro">
<property name="visible">True</property>
<property name="title" translatable="yes">Add VoIP Accounts</property>
<property name="icon-name">system-users-symbolic</property>
<property name="description" translatable="yes">You can add VoIP account here. It will allow you to place and receive VoIP calls using the SIP protocol. This feature is still relatively new and not yet feature complete (i.e. no encrypted media).</property>
<child>
<object class="GtkButton" id="add_btn">
<property name="visible">True</property>
<property name="margin">6</property>
<property name="halign">center</property>
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Add Account</property>
<signal name="clicked" handler="on_add_account_clicked" swapped="yes"/>
<object class="GtkStackPage">
<property name="name">intro-page</property>
<property name="child">
<object class="AdwStatusPage" id="intro">
<property name="title" translatable="yes">Add VoIP Accounts</property>
<property name="icon-name">system-users-symbolic</property>
<property name="description" translatable="yes">You can add VoIP account here. It will allow you to place and receive VoIP calls using the SIP protocol. This feature is still relatively new and not yet feature complete (i.e. no encrypted media).</property>
<child>
<object class="GtkButton" id="add_btn">
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="halign">center</property>
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Add Account</property>
<signal name="clicked" handler="on_add_account_clicked" swapped="yes"/>
<style>
<class name="suggested-action"/>
</style>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">overview-page</property>
<property name="child">
<object class="GtkListBox" id="overview">
<property name="selection-mode">none</property>
<property name="margin-top">18</property>
<property name="margin-bottom">18</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<signal name="row-activated" handler="on_account_row_activated" swapped="no"/>
<style>
<class name="suggested-action"/>
<class name="boxed-list"/>
</style>
</object>
</child>
</property>
</object>
<packing>
<property name="name">intro-page</property>
</packing>
</child>
<!-- Second child type: Some accounts present: Show a Listbox to manage accounts -->
<child>
<object class="GtkListBox" id="overview">
<property name="visible">True</property>
<property name="selection-mode">none</property>
<property name="margin-top">18</property>
<property name="margin-bottom">18</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<signal name="row-activated" handler="on_account_row_activated" swapped="no"/>
<!-- placeholder -->
<child type="placeholder"/>
<style>
<class name="content"/>
</style>
</object>
<packing>
<property name="name">overview-page</property>
</packing>
</child>
</object>
</child>
</property>
</object>
</child>
</property>
</object>
</child>
</template>
<object class="GtkListBoxRow" id="add_row">
<property name="visible">True</property>
<child>
<property name="child">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">_Add Account</property>
<property name="use-underline">True</property>
<property name="margin-top">9</property>
<property name="margin-bottom">9</property>
</object>
</child>
</property>
</object>
<object class="HdyWindow" id="account_window">
<object class="AdwWindow" id="account_window">
<property name="visible">False</property>
<property name="default-width">380</property>
<property name="default-height">660</property>
<signal name="delete-event" handler="gtk_widget_hide_on_delete"/>
<property name="hide-on-close">True</property>
</object>
</interface>
</interface>

View file

@ -1,33 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="libhandy" version="1.0"/>
<template class="CallsAccountRow" parent="HdyActionRow">
<property name="visible">True</property>
<template class="CallsAccountRow" parent="AdwActionRow">
<property name="title">Title</property>
<property name="subtitle">Subtitle</property>
<property name="activatable">True</property>
<child type="prefix">
<object class="HdyAvatar" id="avatar">
<property name="visible">True</property>
<object class="AdwAvatar" id="avatar">
<property name="show-initials">True</property>
<property name="size">48</property>
</object>
</child>
<child>
<object class="GtkSwitch" id="online_switch">
<property name="valign">center</property>
<property name="visible">True</property>
<property name="active">False</property>
<signal name="state-set" handler="on_online_switched" swapped="no"/>
</object>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon_name">go-next-symbolic</property>
</object>
</child>

View file

@ -1,121 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="CallsCallRecordRow" parent="GtkListBoxRow">
<property name="visible">True</property>
<property name="activatable">False</property>
<property name="selectable">False</property>
<child>
<object class="GtkEventBox" id="event_box">
<property name="visible">True</property>
<property name="child">
<object class="GtkBox">
<child>
<object class="AdwAvatar" id="avatar">
<property name="margin-start">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="size">48</property>
<property name="text" bind-source="target" bind-property="label" bind-flags="sync-create"/>
<property name="show-initials">True</property>
</object>
</child>
<child>
<object class="GtkImage" id="type">
<property name="margin-start">8</property>
</object>
</child>
<child>
<object class="GtkLabel" id="target">
<property name="margin-start">10</property>
<property name="ellipsize">middle</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<child>
<object class="HdyAvatar" id="avatar">
<property name="visible">True</property>
<property name="margin_left">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="size">48</property>
<property name="text" bind-source="target" bind-property="label" bind-flags="sync-create"></property>
<property name="show-initials">True</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="type">
<property name="visible">True</property>
<property name="margin_left">8</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="target">
<property name="visible">True</property>
<property name="margin_left">10</property>
<property name="ellipsize">middle</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button">
<property name="visible">True</property>
<property name="margin_left">12</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="halign">center</property>
<property name="valign">center</property>
<style>
<class name="image-button"/>
</style>
<child internal-child="accessible">
<object class="AtkObject" id="a11y-call">
<property name="accessible-name" translatable="yes" comments="Translators: This is a verb, not a noun. Call the number of the currently selected row.">Call</property>
</object>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">call-start-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="time">
<property name="visible">True</property>
<property name="margin_left">8</property>
<property name="justify">right</property>
<style>
<class name="dim-label"/>
</style>
<attributes>
<attribute name="scale" value="0.7"/>
</attributes>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">4</property>
</packing>
</child>
<property name="hexpand">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="time">
<property name="margin-start">8</property>
<property name="justify">right</property>
<style>
<class name="dim-label"/>
</style>
<attributes>
<attribute name="scale" value="0.7"></attribute>
</attributes>
</object>
</child>
<child>
<object class="GtkButton" id="button">
<property name="margin-start">12</property>
<property name="margin-end">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="tooltip-text">Call</property>
<property name="icon-name">call-start-symbolic</property>
</object>
</child>
</object>
</child>
</property>
</template>
<menu id="context_menu">
<section>
<item>
<attribute name="label" translatable="yes">_Delete Call</attribute>
<attribute name="action">delete-call</attribute>
<attribute name="action">row-history.delete-call</attribute>
</item>
<item>
<!-- Translators: This is a phone number -->
<attribute name="label" translatable="yes">_Copy number</attribute>
<attribute name="action">copy-number</attribute>
<attribute name="action">row-history.copy-number</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Add contact</attribute>
<attribute name="action">new-contact</attribute>
<attribute name="action">row-history.new-contact</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Send SMS</attribute>
<attribute name="action">new-sms</attribute>
<attribute name="action">row-history.new-sms</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
</section>

View file

@ -1,64 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="CallsCallSelectorItem" parent="GtkEventBox">
<property name="visible">True</property>
<property name="visible_window">False</property>
<property name="above_child">True</property>
<template class="CallsCallSelectorItem" parent="AdwBin">
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="label_yalign">0</property>
<property name="shadow_type">out</property>
<child>
<property name="child">
<object class="GtkBox" id="main_box">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="name">
<property name="visible">True</property>
<property name="margin_left">3</property>
<property name="hexpand">True</property>
<property name="margin-start">3</property>
<property name="label">+441234567890</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="status">
<property name="visible">True</property>
<property name="margin_right">3</property>
<property name="halign">center</property>
<property name="margin-end">3</property>
<property name="label" translatable="yes">On hold</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</property>
</object>
</child>
</template>

View file

@ -1,34 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="CallsCallWindow" parent="GtkApplicationWindow">
<!-- <property name="decorated">False</property> -->
<property name="hide_titlebar_when_maximized">True</property>
<property name="show_menubar">False</property>
<property name="title" translatable="yes">Calls</property>
<signal name="delete-event" handler="gtk_widget_hide_on_delete"/>
<property name="hide-on-close">True</property>
<child>
<object class="GtkOverlay">
<property name="visible">True</property>
<child type="overlay">
<object class="CallsInAppNotification" id="in_app_notification">
<property name="visible">True</property>
</object>
</child>
<child>
<object class="AdwToastOverlay" id="toast_overlay">
<property name="child">
<object class="GtkStack" id="main_stack">
<property name="visible">True</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="hscrollbar_policy">never</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<child>
<object class="GtkStackPage">
<property name="name">calls</property>
<property name="child">
<object class="GtkScrolledWindow">
<property name="hscrollbar_policy">never</property>
<property name="child">
<object class="GtkFlowBox" id="call_selector">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="homogeneous">True</property>
<property name="column_spacing">12</property>
@ -36,60 +22,51 @@
<property name="selection_mode">none</property>
<signal name="child-activated" handler="call_selector_child_activated_cb" swapped="no"/>
</object>
</child>
</property>
</object>
</child>
</property>
</object>
<packing>
<property name="name">calls</property>
</packing>
</child>
<child>
<object class="GtkStack" id="call_stack">
<property name="visible">True</property>
<property name="expand">True</property>
</object>
<packing>
<object class="GtkStackPage">
<property name="name">active-call</property>
</packing>
<property name="child">
<object class="GtkStack" id="call_stack">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
</property>
</object>
</child>
</object>
</child>
</property>
</object>
</child>
<child type="titlebar">
<object class="GtkStack" id="header_bar_stack">
<property name="visible">True</property>
<property name="visible_child_name" bind-source="main_stack" bind-property="visible_child_name" bind-flags="bidirectional|sync-create"/>
<child>
<object class="GtkHeaderBar">
<property name="visible">True</property>
</object>
<packing>
<object class="GtkStackPage">
<property name="name">calls</property>
</packing>
<property name="child">
<object class="GtkHeaderBar"/>
</property>
</object>
</child>
<child>
<object class="GtkHeaderBar">
<property name="visible">True</property>
<child>
<object class="GtkButton" id="show_calls">
<property name="visible">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<signal name="clicked" handler="show_calls_clicked_cb" swapped="no"/>
<object class="GtkStackPage">
<property name="name">active-call</property>
<property name="child">
<object class="GtkHeaderBar">
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon_name">system-switch-user-symbolic</property>
<object class="GtkButton" id="show_calls">
<property name="icon-name">system-switch-user-symbolic</property>
<signal name="clicked" handler="show_calls_clicked_cb" swapped="no"/>
</object>
</child>
</object>
</child>
</property>
</object>
<packing>
<property name="name">active-call</property>
</packing>
</child>
</object>
</child>

View file

@ -1,70 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="libhandy" version="0.0"/>
<template class="CallsContactsBox" parent="GtkBin">
<property name="visible">True</property>
<template class="CallsContactsBox" parent="AdwBin">
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="expand">True</property>
<child>
<object class="HdyClamp">
<property name="visible">True</property>
<object class="AdwToolbarView" id="child">
<child type="top">
<object class="AdwClamp">
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">32</property>
<property name="margin-left">12</property>
<property name="margin-right">12</property>
<object class="GtkSearchEntry" id="search_entry">
<property name="margin-start">12</property>
<property name="margin-end">12</property>
</object>
</child>
</object>
</child>
<property name="content">
<object class="GtkScrolledWindow">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="AdwClamp">
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkFrame" id="contacts_frame">
<property name="visible">True</property>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">32</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<child>
<object class="GtkListBox" id="contacts_listbox">
<property name="visible">True</property>
<child type="placeholder">
<object class="GtkBox" id="placeholder_empty">
<property name="visible">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="orientation">vertical</property>
<property name="spacing">24</property>
<property name="margin">32</property>
<property name="expand">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="pixel_size">128</property>
<object class="GtkFrame" id="contacts_frame">
<property name="child">
<object class="GtkListBox" id="contacts_listbox">
<child type="placeholder">
<object class="AdwStatusPage" id="placeholder_empty">
<property name="icon_name">edit-find-symbolic</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="justify">center</property>
<property name="label" translatable="yes">No Contacts Found</property>
<style>
<class name="large-title"/>
</style>
<property name="title" translatable="yes">No Contacts Found</property>
</object>
</child>
</object>
</child>
</property>
</object>
</child>
</object>
@ -72,7 +46,7 @@
</object>
</child>
</object>
</child>
</property>
</object>
</child>
</template>

View file

@ -1,48 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="CallsContactsRow" parent="GtkListBoxRow">
<property name="visible">True</property>
<property name="activatable">False</property>
<property name="selectable">False</property>
<child>
<property name="child">
<object class="GtkGrid" id="grid">
<property name="visible">True</property>
<property name="margin">6</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="column-spacing">12</property>
<property name="row-spacing">3</property>
<!-- Avatar -->
<child>
<object class="HdyAvatar" id="avatar">
<property name="visible">True</property>
<object class="AdwAvatar" id="avatar">
<property name="valign">center</property>
<property name="size">36</property>
<property name="show-initials">True</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<!-- Row title -->
<child>
<object class="GtkLabel" id="title">
<property name="visible">True</property>
<property name="ellipsize">end</property>
<property name="halign">start</property>
<property name="expand">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<style>
<class name="bold-label"/>
</style>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
</object>
</child>
</property>
</template>
</interface>

View file

@ -1,42 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<template class="CallsHistoryBox" parent="GtkStack">
<template class="CallsHistoryBox" parent="AdwBin">
<child>
<object class="HdyStatusPage">
<property name="visible">True</property>
<property name="icon-name">call-start-symbolic</property>
<property name="title" translatable="yes">No Recent Calls</property>
</object>
<packing>
<property name="name">empty</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="visible">True</property>
<property name="hscrollbar-policy">never</property>
<object class="GtkStack" id="stack">
<property name="hexpand">True</property>
<child>
<object class="HdyClamp">
<property name="visible">True</property>
<child>
<object class="GtkListBox" id="history">
<property name="visible">True</property>
<property name="margin">12</property>
<property name="valign">start</property>
<style>
<class name="content"/>
</style>
<object class="GtkStackPage">
<property name="name">empty</property>
<property name="child">
<object class="AdwStatusPage">
<property name="icon-name">call-start-symbolic</property>
<property name="title" translatable="yes">No Recent Calls</property>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">history</property>
<property name="child">
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="hscrollbar-policy">never</property>
<child>
<object class="AdwClamp">
<child>
<object class="GtkListBox" id="history">
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<property name="valign">start</property>
<style>
<class name="boxed-list"/>
</style>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
<packing>
<property name="name">history</property>
</packing>
</child>
</template>
</interface>

View file

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="CallsInAppNotification" parent="GtkRevealer">
<property name="reveal-child">False</property>
<property name="transition-type">slide-down</property>
<property name="valign">start</property>
<property name="halign">center</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<child>
<object class="GtkButton">
<property name="visible">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="calls_in_app_notification_hide" swapped="yes"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon_name">window-close-symbolic</property>
</object>
</child>
<style>
<class name="flat"/>
</style>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="wrap">True</property>
<property name="margin-left">24</property>
<property name="margin-right">24</property>
</object>
</child>
<style>
<class name="app-notification"/>
</style>
</object>
</child>
</template>
</interface>

View file

@ -1,145 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="libhandy" version="1.0"/>
<template class="CallsMainWindow" parent="HdyApplicationWindow">
<template class="CallsMainWindow" parent="AdwApplicationWindow">
<property name="title" translatable="yes">Calls</property>
<property name="hide_titlebar_when_maximized">True</property>
<property name="show_menubar">False</property>
<signal name="delete-event" handler="gtk_widget_hide_on_delete"/>
<property name="hide-on-close">True</property>
<property name="width-request">360</property>
<property name="height-request">294</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="HdyHeaderBar">
<property name="visible">True</property>
<property name="centering_policy">strict</property>
<property name="show_close_button">True</property>
<property name="title" bind-source="CallsMainWindow" bind-property="title" bind-flags="sync-create"/>
<object class="AdwBreakpoint">
<condition>max-width: 450sp</condition>
<setter object="header_bar" property="title-widget"/>
<setter object="switcher_bar" property="reveal">True</setter>
</object>
</child>
<child>
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar" id="header_bar">
<child type="title">
<object class="HdyViewSwitcherTitle" id="title_switcher">
<property name="visible">True</property>
<object class="AdwViewSwitcher" id="title_switcher">
<property name="policy">wide</property>
<property name="stack">main_stack</property>
<property name="title" bind-source="CallsMainWindow" bind-property="title" bind-flags="sync-create"/>
</object>
</child>
<child>
<child type="end">
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="popover">menu_popover</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon_name">open-menu-symbolic</property>
</object>
</child>
<property name="menu-model">app-menu</property>
<property name="icon-name">open-menu-symbolic</property>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkOverlay">
<property name="visible">True</property>
<child type="overlay">
<object class="CallsInAppNotification" id="in_app_notification">
<property name="visible">True</property>
</object>
</child>
<child>
<property name="content">
<object class="AdwToastOverlay" id="toast_overlay">
<property name="child">
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkRevealer" id="permanent_error_revealer">
<property name="visible">True</property>
<property name="reveal_child">True</property>
<child>
<property name="child">
<object class="GtkLabel" id="permanent_error_label">
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="visible">True</property>
<style>
<class name="error-state-message"/>
</style>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkStack" id="main_stack">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="expand">True</property>
<object class="AdwViewStack" id="main_stack">
<property name="vhomogeneous">False</property>
<property name="hhomogeneous">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
</child>
<child>
<object class="HdyViewSwitcherBar" id="switcher_bar">
<property name="visible">True</property>
<object class="AdwViewSwitcherBar" id="switcher_bar">
<property name="stack">main_stack</property>
<property name="reveal" bind-source="title_switcher" bind-property="title-visible" bind-flags="sync-create"/>
</object>
</child>
</object>
</child>
</property>
</object>
</child>
</property>
</object>
</child>
</template>
<object class="GtkDialog" id="ussd_dialog">
<property name="can-focus">False</property>
<property name="modal">True</property>
<property name="default-width">240</property>
<property name="default-height">200</property>
<signal name="delete-event" handler="gtk_widget_hide_on_delete" object="ussd_dialog" swapped="yes"/>
<property name="hide-on-close">True</property>
<property name="default-widget">ussd_cancel_button</property>
<child type="titlebar">
<object class="HdyHeaderBar">
<property name="visible">True</property>
<property name="show-close-button">False</property>
<property name="title" translatable="yes">USSD</property>
<child>
<object class="AdwHeaderBar">
<property name="show-start-title-buttons">False</property>
<property name="show-end-title-buttons">False</property>
<child type="start">
<object class="GtkButton" id="ussd_cancel_button">
<property name="visible">True</property>
<property name="sensitive">True</property>
<property name="has-default">True</property>
<property name="can-default">True</property>
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Cancel</property>
<signal name="clicked" handler="window_ussd_cancel_clicked_cb" swapped="yes"/>
</object>
<packing>
<property name="pack_type">start</property>
</packing>
</child>
<child>
<child type="start">
<object class="GtkButton" id="ussd_close_button">
<property name="visible" bind-source="ussd_cancel_button" bind-property="visible"
bind-flags="sync-create|invert-boolean|bidirectional"/>
<property name="visible">True</property>
<property name="sensitive">True</property>
<property name="has-default">True</property>
<property name="can-default">True</property>
<property name="visible" bind-source="ussd_cancel_button" bind-property="visible" bind-flags="sync-create|invert-boolean|bidirectional"/>
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Close</property>
<signal name="clicked" handler="gtk_window_close" object="ussd_dialog" swapped="yes"/>
</object>
<packing>
<property name="pack_type">start</property>
</packing>
</child>
<child>
<child type="end">
<object class="GtkButton" id="ussd_reply_button">
<property name="label" translatable="yes">_Send</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="use-underline">True</property>
<signal name="clicked" handler="window_ussd_reply_clicked_cb" swapped="yes"/>
@ -147,32 +105,25 @@
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
</object>
</child>
<child internal-child="vbox">
<child internal-child="content_area">
<object class="GtkBox">
<property name="visible">True</property>
<property name="expand">True</property>
<property name="border-width">0</property>
<property name="margin">12</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<child>
<object class="GtkStack" id="ussd_stack">
<property name="visible">True</property>
<child>
<object class="GtkBox" id="ussd_content">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="ussd_label">
<property name="visible">True</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
</object>
@ -185,64 +136,37 @@
</child>
</object>
</child>
<child>
<object class="GtkSpinner" id="ussd_spinner">
<property name="valign">center</property>
<property name="vexpand">True</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkPopoverMenu" id="menu_popover">
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="margin">12</property>
<property name="spacing">6</property>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text" translatable="yes">_VoIP Accounts</property>
<property name="action-name">app.accounts</property>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="orientation">vertical</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">False</property>
<property name="text" translatable="yes">_Keyboard shortcuts</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">False</property>
<property name="text" translatable="yes">_Help</property>
</object>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text" translatable="yes" comments="&quot;Calls&quot; is the application name, do not translate">_About Calls</property>
<property name="action-name">win.about</property>
</object>
</child>
</object>
</child>
</object>
<menu id="app-menu">
<section>
<item>
<attribute name="label" translatable="yes">_VoIP Accounts</attribute>
<attribute name="action">app.accounts</attribute>
</item>
</section>
<section>
<!--item>
<attribute name="label" translatable="yes">_Keyboard shortcuts</attribute>
<attribute name="action"></attribute>
</item-->
<!--item>
<attribute name="label" translatable="yes">_Help</attribute>
<attribute name="action"></attribute>
</item-->
<item>
<attribute name="label" translatable="yes" comments="&quot;Calls&quot; is the application name, do not translate">_About Calls</attribute>
<attribute name="action">win.about</attribute>
</item>
</section>
</menu>
</interface>

View file

@ -1,84 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<requires lib="libhandy" version="1.0"/>
<template class="CallsNewCallBox" parent="GtkBox">
<property name="margin_left">24</property>
<property name="margin_right">24</property>
<property name="margin_top">6</property>
<property name="orientation">vertical</property>
<property name="visible">True</property>
<template class="CallsNewCallBox" parent="AdwBin">
<child>
<object class="HdyClamp">
<property name="visible">True</property>
<object class="GtkBox" id="child">
<property name="hexpand">True</property>
<property name="margin-start">24</property>
<property name="margin-end">24</property>
<property name="margin_top">6</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<object class="AdwClamp">
<property name="orientation">vertical</property>
<child>
<object class="GtkListBox" id="origin_list_box">
<property name="visible">True</property>
<property name="selection-mode">none</property>
<property name="halign">center</property>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="HdyComboRow" id="origin_list">
<property name="visible">True</property>
<signal name="notify::selected-index" handler="notify_selected_index_cb" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkEntry" id="address_entry">
<property name="visible" bind-source="CallsNewCallBox" bind-property="numeric-input-only" bind-flags="sync-create|invert-boolean"/>
<property name="xalign">0.5</property>
<property name="placeholder-text" translatable="yes">Enter a VoIP address</property>
<signal name="activate" handler="address_activate_cb" swapped="yes"/>
<signal name="changed" handler="address_changed_cb" swapped="yes"/>
<style>
<class name="address-entry"/>
</style>
</object>
</child>
<child>
<object class="CuiDialpad" id="dialpad">
<property name="visible" bind-source="CallsNewCallBox" bind-property="numeric-input-only" bind-flags="sync-create"/>
<signal name="dialed" handler="dialpad_dialed_cb"/>
</object>
</child>
<child>
<object class="GtkListBox" id="result_list">
<property name="visible" bind-source="CallsNewCallBox" bind-property="numeric-input-only" bind-flags="sync-create|invert-boolean"/>
<property name="margin-top">16</property>
<property name="selection-mode">none</property>
<child>
<object class="HdyActionRow" id="result">
<property name="visible">False</property>
<property name="title" bind-source="address_entry" bind-property="text"/>
<property name="width-request">300</property>
<property name="subtitle" translatable="yes">SIP Account</property>
<child type="prefix">
<object class="HdyAvatar">
<property name="visible">True</property>
<property name="show-initials">True</property>
<property name="size">36</property>
<object class="GtkListBox" id="origin_list_box">
<property name="selection-mode">none</property>
<property name="halign">center</property>
<child>
<object class="AdwComboRow" id="origin_list">
<signal name="notify::selected-index" handler="notify_selected_index_cb" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkEntry" id="address_entry">
<property name="visible" bind-source="CallsNewCallBox" bind-property="numeric-input-only" bind-flags="sync-create|invert-boolean"/>
<property name="xalign">0.5</property>
<property name="placeholder-text" translatable="yes">Enter a VoIP address</property>
<signal name="activate" handler="address_activate_cb" swapped="yes"/>
<signal name="changed" handler="address_changed_cb" swapped="yes"/>
<style>
<class name="address-entry"/>
</style>
</object>
</child>
<child>
<object class="CuiDialpad" id="dialpad">
<property name="visible" bind-source="CallsNewCallBox" bind-property="numeric-input-only" bind-flags="sync-create"/>
<signal name="dialed" handler="dialpad_dialed_cb"/>
</object>
</child>
<child>
<object class="GtkListBox" id="result_list">
<property name="visible" bind-source="CallsNewCallBox" bind-property="numeric-input-only" bind-flags="sync-create|invert-boolean"/>
<property name="margin-top">16</property>
<property name="selection-mode">none</property>
<child>
<object class="GtkButton" id="dial_result_btn">
<property name="visible">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="dial_result_clicked_cb" swapped="yes"/>
<style>
<class name="rounded-button"/>
</style>
<object class="AdwActionRow" id="result">
<property name="visible">False</property>
<property name="title" bind-source="address_entry" bind-property="text"/>
<property name="width-request">300</property>
<property name="subtitle" translatable="yes">SIP Account</property>
<child type="prefix">
<object class="AdwAvatar">
<property name="show-initials">True</property>
<property name="size">36</property>
</object>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<object class="GtkButton" id="dial_result_btn">
<property name="icon-name">call-start-symbolic</property>
<signal name="clicked" handler="dial_result_clicked_cb" swapped="yes"/>
<style>
<class name="rounded-button"/>
</style>
</object>
</child>
</object>

View file

@ -1,40 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.22"/>
<template class="CallsNewCallHeaderBar" parent="GtkHeaderBar">
<property name="show-close-button">True</property>
<property name="title" translatable="yes">New Call</property>
<property name="visible">True</property>
<child>
<child type="start">
<object class="GtkButton" id="back">
<property name="action-name">win.back</property>
<property name="visible">True</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<style>
<class name="image-button"/>
</style>
<property name="icon-name">go-previous-symbolic</property>
<child internal-child="accessible">
<object class="AtkObject" id="a11y-back">
<property name="accessible-name" translatable="yes">Back</property>
</object>
</child>
<child>
<object class="GtkImage" id="back_image">
<property name="visible">True</property>
<property name="icon-name">go-previous-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
</object>
<packing>
<property name="pack-type">start</property>
</packing>
</child>
<child type="title">
<object class="GtkStackSwitcher" id="stack_switcher">
<property name="visible">True</property>
</object>
<object class="GtkStackSwitcher" id="stack_switcher"/>
</child>
</template>
</interface>

@ -1 +1 @@
Subproject commit 6798b38d4d66d069751151b3e9a202c6de8d7f3c
Subproject commit 7ff3e6649a2ef44bc96d3179cd8b1dac2fdd681d

View file

@ -7,7 +7,7 @@ test_env = [
'LC_ALL=C',
'PYTHONDONTWRITEBYTECODE=yes',
'MALLOC_CHECK_=2',
'NO_AT_BRIDGE=1',
'GTK_A11Y=none',
'GSETTINGS_SCHEMA_DIR=@0@/data'.format(meson.project_build_root()),
'CALLS_PLUGIN_DIR=@0@/plugins'.format(meson.project_build_root()),
'FOLKS_BACKENDS_ALLOWED=key-file',