Compare commits

...

115 Commits

Author SHA1 Message Date
Matthias Clasen
35b0974000 Add GtkFileDialog
Add an async API for picking a file, together with
a builder object for it. This is meant to replace direct
use of GtkFileChooserDialog.
2022-10-26 07:59:39 -04:00
Matthias Clasen
4c18b1f345 Add GtkChoice
This is an auxiliary object for use with
file chooser choices.
2022-10-26 07:54:23 -04:00
Matthias Clasen
48aa551a72 Add GtkInfoDialog
This is replacing GtkMessageDialog with an
async API for showing informational messages.
2022-10-26 07:54:23 -04:00
Matthias Clasen
c20749e2d8 wip: builder: Accept string lists for strvs
Accept
  <property name="my-strv>
    <object class="GtkStringList">
      <items>
        <item translatable="yes">One</item>
        <item translatable="yes">Two</item>
        ...
      </items>
    </object>
  </property>

Ideally, we would want to parse this without the
<object> tag, but that is much more annoying to
implement.
2022-10-26 07:54:23 -04:00
Matthias Clasen
3e8a5bbb92 Add GtkFontDialogButton
This is like GtkColorDialogButton, but for fonts.
2022-10-26 07:54:23 -04:00
Matthias Clasen
056022a54b Add GtkFontDialog
This is similar to GtkColorDialog, but for fonts.
2022-10-26 07:54:23 -04:00
Matthias Clasen
1765bdc590 Add GtkColorDialogButton
This is a new color button implementation
built around GtkColorDialog.
2022-10-26 07:54:23 -04:00
Matthias Clasen
bcd9130548 Add GtkColorDialog
Add an async API for picking a color, together with
a builder object for it. This is meant to replace direct
use of GtkColorChooserDialog.
2022-10-26 07:54:22 -04:00
Мирослав Николић
a95cfb1c45 Update Serbian translation
(cherry picked from commit b760f57ae1)
2022-10-23 09:21:37 +00:00
Matthias Clasen
799fb41937 Merge branch 'deprecation-cleanups' into 'main'
gtk-demo: Replace combo boxes in font features

See merge request GNOME/gtk!5164
2022-10-22 19:23:45 +00:00
Matthias Clasen
e6f4c596fa gtk-demo: Use global style in font_features 2022-10-22 11:38:46 -04:00
Matthias Clasen
3d7fb26588 gtk-demo: Replace combo boxes in font features
This is a slightly more complicated combo box,
so we need an auxiliary object.
2022-10-22 11:35:35 -04:00
Matthias Clasen
93f9b2d519 Merge branch 'deprecation-cleanups' into 'main'
gtk-demo: Remove deprecations from dnd

See merge request GNOME/gtk!5160
2022-10-22 02:32:23 +00:00
Matthias Clasen
6f3613a06f bloatpad: Stop using a combobox
Replace GtkComboBoxText with GtkDropDown+GtkStringList.
2022-10-21 22:03:17 -04:00
Matthias Clasen
3723778f42 fontchooser: Stop using deprecated harfbuzz api
The replacement is very straightforward.
2022-10-21 21:54:38 -04:00
Matthias Clasen
3fc3f8d2ff gtk-demo: Replace combobox in font rendering
Out with the combobox, in with the dropdown.
2022-10-21 21:48:39 -04:00
Matthias Clasen
2868f1b509 placesview: Drop deprecation guards
We got rid of all deprecated api here.
2022-10-21 21:48:39 -04:00
Matthias Clasen
f44570e71a filechooserwidget: Drop deprecation guards
We got rid of all deprecated api here.
2022-10-21 21:48:39 -04:00
Matthias Clasen
15d39dddc5 filesystemmodel: Drop outdated comments
Drop a long comment describing an implementation
that no longer exists.
2022-10-21 21:48:39 -04:00
Matthias Clasen
1028449bcc filesystemmodel: Drop unneded deprecation guards
Nothing deprecated is used here anymore.
2022-10-21 21:48:39 -04:00
Matthias Clasen
01cca279f9 searchenginemodel: Drop unneeded deprecation guards
Nothing deprecated is used here anymore.
2022-10-21 21:48:39 -04:00
Matthias Clasen
a7cae4bffa gtk-demo: Remove deprecations from characters
Make the characters demo use global style providers.
2022-10-21 21:48:39 -04:00
Matthias Clasen
3e2e29a1dc gtk-demo: Remove deprecations from sizegroup demo
Out with the comboboxes, in with the dropdowns.
2022-10-21 21:48:39 -04:00
Matthias Clasen
de069e15c2 gtk-demo: Remove deprecation from textview demo
Use a dropdown instead of a combobox for the widget
embedding. It's better!
2022-10-21 21:48:39 -04:00
Matthias Clasen
f8edefd7c0 gtk-demo: Remove deprecations from gltransitions
Make the GL transitions demo use global style providers.
2022-10-21 21:48:39 -04:00
Matthias Clasen
24a15b51fe gtk-demo: Remove deprecations from solitaire
Make the solitaire game use global style providers.
2022-10-21 21:48:39 -04:00
Matthias Clasen
1f7aba345d gtk=demo: Drop unneeded deprecation guards
Nothing deprecated in the pixbuf paintable demo.
2022-10-21 21:48:39 -04:00
Matthias Clasen
230212c59c gtk-demo: Remove deprecations from dnd
Make the dnd demo use global style providers.
2022-10-21 21:48:39 -04:00
Matthias Clasen
86f7c19b91 Merge branch 'wip/corey/file-chooser-popup' into 'main'
filechooserwidget: Fix popup issues

See merge request GNOME/gtk!5161
2022-10-22 00:36:26 +00:00
Corey Berla
e5cea4e39d filechooserwidget: Show popup when clicking on empty space
Add gesture to right click or long press in the empty space
in the FileChooserWidget.  Only show the global settings when
clicking on the empty space.
2022-10-21 13:57:21 -07:00
Corey Berla
22aca8952f filechoosercell: Select item when right click or long press 2022-10-21 13:57:15 -07:00
Corey Berla
c0e437d163 filechoosercell: Only use long press when in touch mode 2022-10-21 13:57:11 -07:00
Corey Berla
ffe97a453f filechoosercell: Claim sequence on click 2022-10-21 13:57:06 -07:00
Matthias Clasen
c09ba28b06 Merge branch 'gbsneto/filechooser-column-view' into 'main'
Port filechooser to GtkColumnView

See merge request GNOME/gtk!5108
2022-10-21 19:42:24 +00:00
Matthias Clasen
81de43eba5 Merge branch 'matthiasc/for-main' into 'main'
Bump the GLib requirement

See merge request GNOME/gtk!5158
2022-10-21 16:26:57 +00:00
Matthias Clasen
788be8b945 filesystemmodel: Fix a compiler warning 2022-10-21 12:15:36 -04:00
Matthias Clasen
0370979225 theme: Fix up padding for the file chooser
We need the padding inside the filelistcell, so that
its event controllers cover the whole area.

Introduce a .complex style class for columnviews that
achieves that, and make the filechooser use it.
2022-10-21 08:28:58 -04:00
Matthias Clasen
943014074c Bump the GLib requirement
We now use GSignalGroup, which was introduced
in GLib 2.72.
2022-10-21 07:59:47 -04:00
Matthias Clasen
c0e8523a01 filesystemmodel: Fix a compiler warning 2022-10-21 07:44:13 -04:00
Matthias Clasen
8374ebe4c4 Merge branch 'gtkcompostable-c-fix-c4013' into 'main'
gtkcomposetable.c: Fix build on non-X11

See merge request GNOME/gtk!5156
2022-10-21 11:40:52 +00:00
Chun-wei Fan
611d14108d gtkcomposetable.c: Fix build on non-X11
The build breaks with a C4013 warning/error on Visual Studio because we don't
have a prototype defined for _gtk_get_datadir(), so include gtkprivate.h.

The vs2017-x64 CI did not catch this error because it is building GLib as a
fallback subproject, causing the msvc_recommended_pragmas.h header not to be
found, which is used to detect problems like this.
2022-10-21 12:29:19 +08:00
Matthias Clasen
25cf702345 filechooser: Make search results show up
The tracker search engine implementation was not
setting all the custom attributes that we require
now.

The quartz search engine will need similar fixes.
2022-10-20 22:34:41 -04:00
Matthias Clasen
1d9c61cada filechooserwidget: Remember sort columns and order
These settings existed before, we keep using them.
This loses some information about sorting by multiple
columns, but it is sufficient to get the same primary
sort column back.
2022-10-20 22:34:41 -04:00
Corey Berla
71adb26a0d filechooserwidget: Fix time_sort_func 2022-10-20 22:34:41 -04:00
Corey Berla
e27fa33690 filechooser: Add show-time property
The "Show Time" setting does not take immediate effect (only after
changing folders) because it's set as a single call to
column_view_get_time_visible() on the FileChooserCell creation.
Instead create a bind a show-time property that gets updated
as the setting is changed.
2022-10-20 22:34:41 -04:00
Corey Berla
f7dfead861 filechoosercell: Add a GtkDragSource
Allow dragging one or more items.  If the item dragged is not part of
the current selection, only drag that item.
2022-10-20 22:34:41 -04:00
Corey Berla
aac04a0995 filechoosercell: Bind "item" and "selected" to cell 2022-10-20 22:34:41 -04:00
Corey Berla
8af191e52d filechooserwidget: Expose selection model
FileChooserCell may need to change the selection during a DND.
2022-10-20 22:34:41 -04:00
Corey Berla
06382ff836 filechooserwidget: Rename and expose get_selected_files()
GtkFileChooserCell will need this function
2022-10-20 22:34:41 -04:00
Matthias Clasen
cd469788c3 filechooserwidget: Make rename popover work
Make the rename popover operate on the file that
the context menu was opened for, and pop up at
the same position.
2022-10-20 22:34:40 -04:00
Matthias Clasen
44b5076cf2 filechooserwidget: Make context menus mostly work
Move the gestures to the individual cells, and
make them trigger the context menu via an action
that takes item position and coordinates.

The semantics are changed slightly: the menu actions
now operate on the clicked item, not on the selection.

Still to do: Fix up keyboard activation.
2022-10-20 22:34:40 -04:00
Matthias Clasen
e5be9e1035 filesystemmodel: Drop the model types
We are no longer storing extra values, so no need
to take their types in the api.
2022-10-20 22:34:40 -04:00
Matthias Clasen
b831d01f0d filesystemmodel: Drop unused arguments
Drop the unused get_func arguments in constructors.

Update all callers.
2022-10-20 22:34:40 -04:00
Matthias Clasen
278bcf2680 filechooserentry: Simplify model construction
Don't pass a get_value callback when creating the
filesystem model. It isn't called anymore, and things
continue to work.
2022-10-20 22:34:40 -04:00
Matthias Clasen
c19d0dc9ea filesystemmodel: Drop more unused api
Drop all apis that operate on tree iters.
We are no longer a tree model.
2022-10-20 22:34:40 -04:00
Matthias Clasen
edd5ed169e filesystemmodel: Drop _gtk_file_system_model_get_value
This function is unused, so drop it, and its callback.
2022-10-20 22:34:40 -04:00
Matthias Clasen
8ec9c3fdc6 filechooser: Some work on popup menus
This is a start towards placing menus properly.

We once again center keyboard-triggered menus
on the file list.
2022-10-20 22:34:40 -04:00
Matthias Clasen
e95022040f filechooser: Drop deprecated includes
We no longer use cell renderers and tree models
here. Yay.
2022-10-20 22:34:40 -04:00
Matthias Clasen
3a2ce620e2 filesystemmodel: Fix a possible problem
If the async query fails to reproduce a file info,
we still need to thaw the model, otherwise it ends
up frozen forever.

This was deduced by reading the code, I haven't
actually seen it happen.
2022-10-20 22:34:40 -04:00
Matthias Clasen
df49bf9eec filechooserwidget: Use a string sorter for names
We can use the new collation property of GtkStringSorter,
and get the benefit of sort key caching. This commit
also fixes an accidental leak of all sorters, and
removes the sorter from the location column - we never
show that column when individual colummns are sortable.
2022-10-20 22:34:40 -04:00
Matthias Clasen
53327f3aea filechooser: Drop unnecessary theme tracking
The widgets we are using handle theme changes
themselves, so there is no need for the filechooser
to do anything.
2022-10-20 22:34:40 -04:00
Matthias Clasen
c0c3d75062 filechooser: Bring back sorting
Add a sort model between the filter model and the
file system model, and set it up to sort according
to the circumstances.
2022-10-20 22:34:40 -04:00
Corey Berla
d0d0409f9d filechooserwidget: Reimplement DnD Drop Target
This reverts commit 34752a15a71597d00a8d08befc545ac1c178b81b.

Leaving out the drag source portion as that needs a total
reimplementation.  The GtkDropTarget only required minor
modifications.
2022-10-20 22:34:40 -04:00
Matthias Clasen
3a7d094542 filechooserwidget: Reinstate show_and_select_files
Now that we have information about visible and
filtered-out status of items, we can make this
function work again.
2022-10-20 22:34:40 -04:00
Matthias Clasen
280adcbb8c filechooserwidget: Add a filter model
Put a filter model between the selection model and
the filesystem model, and make it filter on the
filechooser::visible attribute. This makes the filer
combo in the filterchooser and the 'show hidden files'
item work. But we need to prod the filter to trigger
a refiltering every now and then.
2022-10-20 22:34:40 -04:00
Matthias Clasen
83431a999c filesystemmodel: Make filtering info available
Provide the filtered-out and visible bits as a file attributes
under the names filechooser::filtered-out and filechooser::visible,
so that we can filter on it.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
eeeee99aa8 filechooserwidget: Connect to the right model
When recreating the browse_files_model model, connect to that
model's item-changed signal, instead of connecting to the selection
model.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
0f503e0ba2 filechooserwidget: Connect to items-changed when changing model
Just like previous commit, we need to call list_items_changed()
even if we change from single selection to multi selection or
vice-versa.
2022-10-20 22:34:40 -04:00
Matthias Clasen
3e4ae62b44 filesystemmodel: Drop an unused include 2022-10-20 22:34:40 -04:00
Matthias Clasen
ec4f814bd4 filechooserwidget: Listen to items-changed too
To track changes of the selected items in a selection
model, we need to listen to both ::selection-changed
and ::items-changed.

This fixes the open button not turning sensitive
when initially loading a new folder.
2022-10-20 22:34:40 -04:00
Matthias Clasen
0141ba4e00 filechooser: Prevent recursion when activating items
When a list item is activated, we activate the default widget.
Unfortunately, due to some other bug, sometimes the open button
is not made sensitive, and then default.activate falls back
to activating the focus widget (which is the item we are just
coming from). Boom
2022-10-20 22:34:40 -04:00
Matthias Clasen
52ef16c21b actionmuxer: Add debug spew for action activation
This helps tracking down whe activation goes wrong.
2022-10-20 22:34:40 -04:00
Matthias Clasen
6ca7104015 filesystemmodel: Drop the tree model implementation
This is no longer used.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
07d6166ab8 filechooserwidget: Use GListModel API for post-renaming selection 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
dc651c3dc3 filechooserentry: Use separate GtkTreeStore for completion
Soon GtkFileSystemModel will not be a GtkTreeModel implementation,
so preemptively remove any usage of this interface. Populate the
list store using the GListModel's 'items-changed' signal.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
f520801626 filesystemmodel: Retire GtkFileSystemItem
This has to be the shortest-living object in GTK history!

It helped us greatly during the transition to GtkColumnView, but
now we can remove it in favour of GFileInfo directly. Perhaps I
could have never introduced GtkFileSystemItem in the first place,
but we're 30 commits deep and it's too late to just redo the whole
thing that will get us exactly here anyway.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
4e3fbd0b0f filesystemmodel: Always set standard::file attribute
This will help us greatly when porting GtkFileSystemModel to
GtkDirectoryList.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
8a24fdea38 filesystemmodel: Trivial cleanup
Use g_set_object() which does exactly what the code there does.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
b26222b6ba filechooserutils: Add helper to get GFile from info
This will be used extensively starting from next commit!
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
c8d291ab86 filechooserentry: Trivial cleanups
Use g_clear_object() in a couple of places. No functional changes.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
0a87438432 searchenginemodel: Use GListModel API to filter
We now start a mini-series of commits that will ultimately remove
the GtkTreeModel implementation of GtkFileSystemModel.

As a first step, port GtkSearchEngineModel iter through the files
using GListModel API.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
ddf1cd6678 filechooserwidget: Remove treeview
Now that most of the treeview usage is gone, remove the remaining
code that uses it - mostly event handling code, which for now won't
work, but will be fixed by next commits - and drop the tree view
entirely.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
9c6d5e2ca5 filechooserwidget: Use GtkSelectionModel for selection
So far, GtkFileChooserWidget has relied on GtkTreeView's selection
management. This commit moves it away from GtkTreeView, and that's
a massive surgery - sorry :(

The most important aspect of this commit is that 'selection_model'
is now the main model we deal with. Changing between directories,
recent files, and search, all sets the selection_model's model.

Selections are entirely handled by GtkSelectionModel now.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
1f2561b08e filesystemmodel: Add more GtkFileSystemItem getters
They'll help us further remove GtkTreeModel code.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
d5b31a30fd filechooserwidget: Stop centering on selected files 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
39c678988d filechooserwidget: Don't queue redraw / resize on tree view 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
9052c191cb filechooserwidget: Compare focus against column view
This effectively doesn't work, but focus will be reworked at some
point, and this gets us a tiny bit closer to that.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
766f442636 gtkfilechooserwidget: Don't set size request 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
27a3d2d09e filesystemmodel: Don't implement GtkTreeDragSource 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
3a3482e41e filechooserwidget: Remove DnD code
Another case where we'll reimplement it later.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
44c37fa34a filesystemmodel: Drop GtkTreeSortable interface
Stop implementing this interface. We'll be able to reimplement
sorting once we fully transition to GListModel.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
5c41dbfaa5 filechooserwidget: Remove sorting
This will be reimplemented later using list models. For now, let's
remove it so we can untangle all this code properly.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
c9d2ff7a0f filechooserwidget: Stop autosizing treeview 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
174af596c5 filechooserwidget: Use column view scrolled window
One less hook to treeview widgetry.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
de2624faa7 filechooserwidget: Move tooltip text to column view
Use a closure binding to query the tooltip.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
cda784a5f4 filechooserwidget: Move folder navigation to column view
React to column view's 'activate' signal, instead of treeview's
'row-activated'. It doesn't handle file sensitivity yet, but that
will probably be dropped later.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
cc33dbef03 filechooserwidget: Move keynav handling to column view 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
ce36611c4e filechooserwidget: Set rubberbanding on column view
And stop setting it on the tree view.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
038cb31d27 filesystemmodel: Don't expose cache
With this commit we start slowly cleaning up the sattelite code
surrounding GtkFileChooserWidget, so that we eventually drop it
all.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
3b53a6b0a9 filechooserwidget: Remove extra action bar 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
619ff2338e filechooserwidget: Move date and time to column view
Because this is the last treeview column, some pending tasks
are marked as TODO. We're getting close to dropping the tree
view!
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
2bcd6949df filechooserwidget: Move file type to column view 2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
abd78ce5f5 filechooserwidget: Move size to column view
This was one of the easier ones. We merely delegate the hard work
to g_format_size(), like we already do for treeview.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
7899fc03c4 filechooserwidget: Move location to column view
Move the entire location column, which only contains the location
renderer, to the column view. The code to generate locations from
the current folder is essentially intact.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
9984f00781 filechooserwidget: Drop name column from treeview
It is now entirely handled by the column view.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
a0d3bdc911 filechooserwidget: Move file icon to column view
This commit moves the icon loading code into a new private
widget called GtkFileThumbnail, which is bound to the GFileInfo
of the model, and asynchronously loads the file icon from that.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
b96317b872 filechooserwidget: Move file name to column view
And remove it from the tree view. Next commits will look a lot like
this, until all columns are moved.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
6c84958b9f filechooserwidget: Replace 'list' page with column view
Replace the 'list' page of the main stack with another page, this
one containing a GtkColumnView. This, again, is the very minimal
code to achieve a column view - and validate the GListModel code
introduced in the previous commit - but there's a long way until
this column view covers the full range of features of the file
chooser.

The tree view still lives in an unused 'list2' page. From now on,
commits will "cannibalize" the treeview, each commit porting any
particular feature - be it a column, an event controller, etc -
to the column view, and dropping the corresponding feature from
the treeview.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
f2559b1dbb filesystemmodel: Implement GListModel
This is a trivial implementation of the GListModel interface. It
does not do anything fancy, like filtering out hidden files, nor
sorting.

The purpose of this minimal implementation is to bootstrap the
initial work to port GtkFileChooserWidget to GtkColumnView.
2022-10-20 22:34:40 -04:00
Georges Basile Stavracas Neto
ed2ade4801 filesystemmodel: Use g_clear_* on finalize
Trivial cleanup, no functional changes.
2022-10-20 22:34:40 -04:00
Matthias Clasen
4549172825 Merge branch 'matthiasc/for-main' into 'main'
inspector: Fix accessibility calls

See merge request GNOME/gtk!5154
2022-10-21 02:28:02 +00:00
Matthias Clasen
b877804eb6 Merge branch 'completion' into 'main'
entrycompletion: Fix inserted-text handler

See merge request GNOME/gtk!4892
2022-10-21 02:13:42 +00:00
Matthias Clasen
5310abbcf7 filechooserentry: Fix completions
Before we can drop them, lets make them work.

This was broken since 4.0, I assume :(
2022-10-20 21:41:18 -04:00
Matthias Clasen
490d4b0b9e inspector: Fix accessibility calls
The argument list of gtk_accessible_update_property
is -1-terminated, not NULL-terminated.
2022-10-20 21:19:13 -04:00
Corey Berla
230188fb30 entrycompletion: Use GSignalGroup on the entry's buffer
We connect to the inserted-text signal for the entry's buffer.
During the lifetime of the entry, the buffer changes.  This is
literally the example used for GSignalGroup in the docs.
2022-10-03 10:40:27 -07:00
55 changed files with 7731 additions and 3736 deletions

View File

@@ -11,7 +11,6 @@
#include <gtk/gtk.h>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
G_DECLARE_FINAL_TYPE (CanvasItem, canvas_item, CANVAS, ITEM, GtkWidget)
@@ -26,6 +25,9 @@ struct _CanvasItem {
double delta;
GtkWidget *editor;
GtkStyleProvider *provider;
char *css_class;
};
struct _CanvasItemClass {
@@ -36,32 +38,41 @@ G_DEFINE_TYPE (CanvasItem, canvas_item, GTK_TYPE_WIDGET)
static int n_items = 0;
static void
unstyle_item (CanvasItem *item)
{
if (item->provider)
{
gtk_style_context_remove_provider_for_display (gtk_widget_get_display (item->label), item->provider);
g_clear_object (&item->provider);
}
if (item->css_class)
{
gtk_widget_remove_css_class (item->label, item->css_class);
g_clear_pointer (&item->css_class, g_free);
}
}
static void
set_color (CanvasItem *item,
GdkRGBA *color)
{
char *css;
char *str;
GtkStyleContext *context;
GtkCssProvider *provider;
const char *old_class;
const char *name;
unstyle_item (item);
str = gdk_rgba_to_string (color);
css = g_strdup_printf ("* { background: %s; }", str);
context = gtk_widget_get_style_context (item->label);
provider = g_object_get_data (G_OBJECT (context), "style-provider");
if (provider)
gtk_style_context_remove_provider (context, GTK_STYLE_PROVIDER (provider));
old_class = (const char *)g_object_get_data (G_OBJECT (item->label), "css-class");
if (old_class)
gtk_widget_remove_css_class (item->label, old_class);
name = gtk_widget_get_name (item->label);
css = g_strdup_printf ("#%s { background: %s; }", name, str);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, css, -1);
gtk_style_context_add_provider (gtk_widget_get_style_context (item->label), GTK_STYLE_PROVIDER (provider), 800);
g_object_set_data_full (G_OBJECT (context), "style-provider", provider, g_object_unref);
gtk_style_context_add_provider_for_display (gtk_widget_get_display (item->label), GTK_STYLE_PROVIDER (provider), 700);
item->provider = GTK_STYLE_PROVIDER (provider);
g_free (str);
g_free (css);
@@ -71,21 +82,10 @@ static void
set_css (CanvasItem *item,
const char *class)
{
GtkStyleContext *context;
GtkCssProvider *provider;
const char *old_class;
unstyle_item (item);
context = gtk_widget_get_style_context (item->label);
provider = g_object_get_data (G_OBJECT (context), "style-provider");
if (provider)
gtk_style_context_remove_provider (context, GTK_STYLE_PROVIDER (provider));
old_class = (const char *)g_object_get_data (G_OBJECT (item->label), "css-class");
if (old_class)
gtk_widget_remove_css_class (item->label, old_class);
g_object_set_data_full (G_OBJECT (item->label), "css-class", g_strdup (class), g_free);
gtk_widget_add_css_class (item->label, class);
item->css_class = g_strdup (class);
}
static gboolean
@@ -724,6 +724,7 @@ do_dnd (GtkWidget *do_widget)
int i;
int x, y;
GtkCssProvider *provider;
GString *css;
button = gtk_color_button_new ();
g_object_unref (g_object_ref_sink (button));
@@ -735,6 +736,18 @@ do_dnd (GtkWidget *do_widget)
800);
g_object_unref (provider);
css = g_string_new ("");
for (i = 0; colors[i]; i++)
g_string_append_printf (css, ".canvasitem.%s { background: %s; }\n", colors[i], colors[i]);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, css->str, css->len);
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
800);
g_object_unref (provider);
g_string_free (css, TRUE);
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));

View File

@@ -21,7 +21,75 @@
#include "script-names.h"
#include "language-names.h"
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
/* {{{ ScriptLang object */
G_DECLARE_FINAL_TYPE (ScriptLang, script_lang, SCRIPT, LANG, GObject)
struct _ScriptLang
{
GObject parent;
char *langname;
unsigned int script_index;
unsigned int lang_index;
hb_tag_t lang_tag;
};
struct _ScriptLangClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE (ScriptLang, script_lang, G_TYPE_OBJECT)
static void
script_lang_init (ScriptLang *self)
{
}
static void
script_lang_finalize (GObject *object)
{
ScriptLang *self = SCRIPT_LANG (object);
g_free (self->langname);
G_OBJECT_CLASS (script_lang_parent_class)->finalize (object);
}
static void
script_lang_class_init (ScriptLangClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = script_lang_finalize;
}
static ScriptLang *
script_lang_new (const char *langname,
unsigned int script_index,
unsigned int lang_index,
hb_tag_t lang_tag)
{
ScriptLang *self;
self = g_object_new (script_lang_get_type (), NULL);
self->langname = g_strdup (langname);
self->script_index = script_index;
self->lang_index = lang_index;
self->lang_tag = lang_tag;
return self;
}
static char *
script_lang_get_langname (ScriptLang *self)
{
return g_strdup (self->langname);
}
/* }}} */
#define MAKE_TAG(a,b,c,d) (unsigned int)(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
@@ -95,6 +163,10 @@ demo_free (gpointer data)
g_clear_pointer (&demo->axes, g_hash_table_unref);
g_clear_pointer (&demo->text, g_free);
gtk_style_context_remove_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (demo->provider));
g_object_unref (demo->provider);
g_free (demo);
}
@@ -472,8 +544,6 @@ update_display (void)
GString *s;
char *text;
gboolean has_feature;
GtkTreeIter iter;
GtkTreeModel *model;
PangoFontDescription *desc;
GList *l;
PangoAttrList *attrs;
@@ -576,14 +646,13 @@ update_display (void)
features = g_string_free (s, FALSE);
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (demo->script_lang), &iter))
if (gtk_drop_down_get_selected (GTK_DROP_DOWN (demo->script_lang)) != 0)
{
hb_tag_t lang_tag;
ScriptLang *selected;
model = gtk_combo_box_get_model (GTK_COMBO_BOX (demo->script_lang));
gtk_tree_model_get (model, &iter, 3, &lang_tag, -1);
selected = gtk_drop_down_get_selected_item (GTK_DROP_DOWN (demo->script_lang));
lang = pango_language_from_string (hb_language_to_string (hb_ot_tag_to_language (lang_tag)));
lang = pango_language_from_string (hb_language_to_string (hb_ot_tag_to_language (selected->lang_tag)));
}
else
lang = NULL;
@@ -740,40 +809,30 @@ tag_pair_equal (gconstpointer a, gconstpointer b)
return pair_a->script_tag == pair_b->script_tag && pair_a->lang_tag == pair_b->lang_tag;
}
static int
script_sort_func (GtkTreeModel *model,
GtkTreeIter *a,
GtkTreeIter *b,
gpointer user_data)
static GtkOrdering
script_sort (const void *item1,
const void *item2,
void *data)
{
char *sa, *sb;
int ret;
ScriptLang *a = (ScriptLang *)item1;
ScriptLang *b = (ScriptLang *)item2;
gtk_tree_model_get (model, a, 0, &sa, -1);
gtk_tree_model_get (model, b, 0, &sb, -1);
ret = strcmp (sa, sb);
g_free (sa);
g_free (sb);
return ret;
return strcmp (a->langname, b->langname);
}
static void
update_script_combo (void)
{
GtkListStore *store;
GListStore *store;
GtkSortListModel *sortmodel;
hb_font_t *hb_font;
int i, j, k;
PangoFont *pango_font;
GHashTable *tags;
GHashTableIter iter;
TagPair *pair;
char *lang;
hb_tag_t active;
GtkTreeIter active_iter;
gboolean have_active = FALSE;
lang = gtk_font_chooser_get_language (GTK_FONT_CHOOSER (demo->font));
@@ -783,7 +842,7 @@ update_script_combo (void)
g_free (lang);
store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
store = g_list_store_new (script_lang_get_type ());
pango_font = get_pango_font ();
hb_font = pango_font_get_hb_font (pango_font);
@@ -807,19 +866,19 @@ update_script_combo (void)
hb_face = hb_font_get_face (hb_font);
for (i= 0; i < 2; i++)
for (guint i = 0; i < 2; i++)
{
hb_tag_t scripts[80];
unsigned int script_count = G_N_ELEMENTS (scripts);
hb_ot_layout_table_get_script_tags (hb_face, tables[i], 0, &script_count, scripts);
for (j = 0; j < script_count; j++)
for (guint j = 0; j < script_count; j++)
{
hb_tag_t languages[80];
unsigned int language_count = G_N_ELEMENTS (languages);
hb_ot_layout_script_get_language_tags (hb_face, tables[i], j, 0, &language_count, languages);
for (k = 0; k < language_count; k++)
for (guint k = 0; k < language_count; k++)
{
pair = g_new (TagPair, 1);
pair->script_tag = scripts[j];
@@ -839,7 +898,6 @@ update_script_combo (void)
{
const char *langname;
char langbuf[5];
GtkTreeIter tree_iter;
if (pair->lang_tag == 0 && pair->script_tag == 0)
langname = NC_("Language", "None");
@@ -856,31 +914,31 @@ update_script_combo (void)
}
}
gtk_list_store_insert_with_values (store, &tree_iter, -1,
0, langname,
1, pair->script_index,
2, pair->lang_index,
3, pair->lang_tag,
-1);
if (pair->lang_tag == active)
{
have_active = TRUE;
active_iter = tree_iter;
}
g_list_store_append (store, script_lang_new (langname,
pair->script_index,
pair->lang_index,
pair->lang_tag));
}
g_hash_table_destroy (tags);
gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store),
script_sort_func, NULL, NULL);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
GTK_SORT_ASCENDING);
gtk_combo_box_set_model (GTK_COMBO_BOX (demo->script_lang), GTK_TREE_MODEL (store));
if (have_active)
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (demo->script_lang), &active_iter);
else
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (demo->script_lang), 0);
sortmodel = gtk_sort_list_model_new (G_LIST_MODEL (store),
GTK_SORTER (gtk_custom_sorter_new (script_sort, NULL, NULL)));
gtk_drop_down_set_model (GTK_DROP_DOWN (demo->script_lang), G_LIST_MODEL (sortmodel));
for (guint i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (sortmodel)); i++)
{
ScriptLang *item = g_list_model_get_item (G_LIST_MODEL (sortmodel), i);
g_object_unref (item);
if (item->lang_tag == active)
{
gtk_drop_down_set_selected (GTK_DROP_DOWN (demo->script_lang), i);
break;
}
}
g_object_unref (sortmodel);
}
static char *
@@ -905,27 +963,19 @@ static void
update_features (void)
{
int i, j;
GtkTreeModel *model;
GtkTreeIter iter;
guint script_index, lang_index;
hb_tag_t lang_tag;
PangoFont *pango_font;
hb_font_t *hb_font;
GList *l;
ScriptLang *selected;
/* set feature presence checks from the font features */
if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (demo->script_lang), &iter))
if (gtk_drop_down_get_selected (GTK_DROP_DOWN (demo->script_lang)) == 0)
return;
model = gtk_combo_box_get_model (GTK_COMBO_BOX (demo->script_lang));
gtk_tree_model_get (model, &iter,
1, &script_index,
2, &lang_index,
3, &lang_tag,
-1);
selected = gtk_drop_down_get_selected_item (GTK_DROP_DOWN (demo->script_lang));
if (lang_tag == 0) /* None is selected */
if (selected->lang_tag == 0) /* None is selected */
{
for (l = demo->feature_items; l; l = l->next)
{
@@ -966,8 +1016,8 @@ update_features (void)
hb_ot_layout_language_get_feature_tags (hb_face,
tables[i],
script_index,
lang_index,
selected->script_index,
selected->lang_index,
0,
&count,
features);
@@ -989,8 +1039,8 @@ update_features (void)
hb_ot_layout_language_find_feature (hb_face,
tables[i],
script_index,
lang_index,
selected->script_index,
selected->lang_index,
features[j],
&feature_index);
@@ -1322,10 +1372,9 @@ free_instance (gpointer data)
}
static void
add_instance (hb_face_t *face,
unsigned int index,
GtkWidget *combo,
int pos)
add_instance (hb_face_t *face,
unsigned int index,
GtkStringList *strings)
{
Instance *instance;
hb_ot_name_id_t name_id;
@@ -1341,20 +1390,20 @@ add_instance (hb_face_t *face,
instance->index = index;
g_hash_table_add (demo->instances, instance);
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), instance->name);
gtk_string_list_append (GTK_STRING_LIST (strings), instance->name);
}
static void
unset_instance (GtkAdjustment *adjustment)
{
if (demo->instance_combo)
gtk_combo_box_set_active (GTK_COMBO_BOX (demo->instance_combo), 0);
gtk_drop_down_set_selected (GTK_DROP_DOWN (demo->instance_combo), 0);
}
static void
instance_changed (GtkComboBox *combo)
instance_changed (GtkDropDown *combo)
{
char *text;
const char *text;
Instance *instance;
Instance ikey;
int i;
@@ -1366,11 +1415,12 @@ instance_changed (GtkComboBox *combo)
hb_font_t *hb_font;
hb_face_t *hb_face;
text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo));
text = gtk_string_list_get_string (GTK_STRING_LIST (gtk_drop_down_get_model (combo)),
gtk_drop_down_get_selected (combo));
if (text[0] == '\0')
goto out;
ikey.name = text;
ikey.name = (char *) text;
instance = g_hash_table_lookup (demo->instances, &ikey);
if (!instance)
{
@@ -1411,7 +1461,6 @@ instance_changed (GtkComboBox *combo)
}
out:
g_free (text);
g_clear_object (&pango_font);
g_free (ai);
g_free (coords);
@@ -1521,6 +1570,7 @@ update_font_variations (void)
{
GtkWidget *label;
GtkWidget *combo;
GtkStringList *strings;
label = gtk_label_new ("Instance");
gtk_label_set_xalign (GTK_LABEL (label), 0);
@@ -1528,26 +1578,28 @@ update_font_variations (void)
gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);
gtk_grid_attach (GTK_GRID (demo->variations_grid), label, 0, -1, 1, 1);
combo = gtk_combo_box_text_new ();
strings = gtk_string_list_new (NULL);
combo = gtk_drop_down_new (G_LIST_MODEL (strings), NULL);
gtk_widget_set_halign (combo, GTK_ALIGN_START);
gtk_widget_set_valign (combo, GTK_ALIGN_BASELINE);
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "");
gtk_string_list_append (strings, "");
for (i = 0; i < hb_ot_var_get_named_instance_count (hb_face); i++)
add_instance (hb_face, i, combo, i);
add_instance (hb_face, i, strings);
for (i = 0; i < hb_ot_var_get_named_instance_count (hb_face); i++)
{
if (matches_instance (hb_face, i, n_axes, design_coords))
{
gtk_combo_box_set_active (GTK_COMBO_BOX (combo), i + 1);
gtk_drop_down_set_selected (GTK_DROP_DOWN (combo), i + 1);
break;
}
}
gtk_grid_attach (GTK_GRID (demo->variations_grid), combo, 1, -1, 3, 1);
g_signal_connect (combo, "changed", G_CALLBACK (instance_changed), NULL);
g_signal_connect (combo, "notify::selecte", G_CALLBACK (instance_changed), NULL);
demo->instance_combo = combo;
}
@@ -1694,6 +1746,7 @@ do_font_features (GtkWidget *do_widget)
GtkBuilder *builder;
GtkBuilderScope *scope;
GtkEventController *controller;
GtkExpression *expression;
builder = gtk_builder_new ();
@@ -1727,6 +1780,9 @@ do_font_features (GtkWidget *do_widget)
demo->description = GTK_WIDGET (gtk_builder_get_object (builder, "description"));
demo->font = GTK_WIDGET (gtk_builder_get_object (builder, "font"));
demo->script_lang = GTK_WIDGET (gtk_builder_get_object (builder, "script_lang"));
expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, 0, NULL, G_CALLBACK (script_lang_get_langname), NULL, NULL);
gtk_drop_down_set_expression (GTK_DROP_DOWN (demo->script_lang), expression);
gtk_expression_unref (expression);
demo->feature_list = GTK_WIDGET (gtk_builder_get_object (builder, "feature_list"));
demo->stack = GTK_WIDGET (gtk_builder_get_object (builder, "stack"));
demo->entry = GTK_WIDGET (gtk_builder_get_object (builder, "entry"));
@@ -1745,8 +1801,8 @@ do_font_features (GtkWidget *do_widget)
demo->swin = GTK_WIDGET (gtk_builder_get_object (builder, "swin"));
demo->provider = gtk_css_provider_new ();
gtk_style_context_add_provider (gtk_widget_get_style_context (demo->swin),
GTK_STYLE_PROVIDER (demo->provider), 800);
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (demo->provider), 800);
basic_value_changed (demo->size_adjustment, demo->size_entry);
basic_value_changed (demo->letterspacing_adjustment, demo->letterspacing_entry);
@@ -1824,3 +1880,5 @@ do_font_features (GtkWidget *do_widget)
return window;
}
/* vim:set foldmethod=marker expandtab: */

View File

@@ -259,16 +259,10 @@
<object class="GtkBox" id="feature_list">
<property name="orientation">vertical</property>
<child>
<object class="GtkComboBox" id="script_lang">
<object class="GtkDropDown" id="script_lang">
<property name="tooltip-text" translatable="yes">Language System</property>
<property name="margin-top">10</property>
<signal name="changed" handler="font_features_script_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
<signal name="notify::selected" handler="font_features_script_changed" swapped="no"/>
</object>
</child>
</object>

View File

@@ -10,8 +10,6 @@
#include <gtk/gtk.h>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static GtkWidget *window = NULL;
static GtkWidget *font_button = NULL;
static GtkWidget *entry = NULL;
@@ -45,7 +43,6 @@ update_image (void)
cairo_t *cr;
GdkPixbuf *pixbuf;
GdkPixbuf *pixbuf2;
const char *hint;
cairo_font_options_t *fopt;
cairo_hint_style_t hintstyle;
cairo_hint_metrics_t hintmetrics;
@@ -60,18 +57,23 @@ update_image (void)
fopt = cairo_font_options_copy (pango_cairo_context_get_font_options (context));
hint = gtk_combo_box_get_active_id (GTK_COMBO_BOX (hinting));
hintstyle = CAIRO_HINT_STYLE_DEFAULT;
if (hint)
switch (gtk_drop_down_get_selected (GTK_DROP_DOWN (hinting)))
{
if (strcmp (hint, "none") == 0)
hintstyle = CAIRO_HINT_STYLE_NONE;
else if (strcmp (hint, "slight") == 0)
hintstyle = CAIRO_HINT_STYLE_SLIGHT;
else if (strcmp (hint, "medium") == 0)
hintstyle = CAIRO_HINT_STYLE_MEDIUM;
else if (strcmp (hint, "full") == 0)
hintstyle = CAIRO_HINT_STYLE_FULL;
case 0:
hintstyle = CAIRO_HINT_STYLE_NONE;
break;
case 1:
hintstyle = CAIRO_HINT_STYLE_SLIGHT;
break;
case 2:
hintstyle = CAIRO_HINT_STYLE_MEDIUM;
break;
case 3:
hintstyle = CAIRO_HINT_STYLE_FULL;
break;
default:
hintstyle = CAIRO_HINT_STYLE_DEFAULT;
break;
}
cairo_font_options_set_hint_style (fopt, hintstyle);
@@ -420,7 +422,7 @@ do_fontrendering (GtkWidget *do_widget)
g_signal_connect (down_button, "clicked", G_CALLBACK (scale_down), NULL);
g_signal_connect (entry, "notify::text", G_CALLBACK (update_image), NULL);
g_signal_connect (font_button, "notify::font-desc", G_CALLBACK (update_image), NULL);
g_signal_connect (hinting, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (hinting, "notify::selected", G_CALLBACK (update_image), NULL);
g_signal_connect (anti_alias, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (hint_metrics, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (text_radio, "notify::active", G_CALLBACK (update_image), NULL);

View File

@@ -116,15 +116,18 @@
</object>
</child>
<child>
<object class="GtkComboBoxText" id="hinting">
<property name="active">0</property>
<object class="GtkDropDown" id="hinting">
<property name="valign">center</property>
<items>
<item translatable="yes" id="none">None</item>
<item translatable="yes" id="slight">Slight</item>
<item translatable="yes" id="medium">Medium</item>
<item translatable="yes" id="full">Full</item>
</items>
<property name="model">
<object class="GtkStringList">
<items>
<item translatable="yes">None</item>
<item translatable="yes">Slight</item>
<item translatable="yes">Medium</item>
<item translatable="yes">Full</item>
</items>
</object>
</property>
</object>
</child>
<layout>

View File

@@ -20,8 +20,6 @@
#include "gtkshadertoy.h"
#include "gskshaderpaintable.h"
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static GtkWidget *demo_window = NULL;
static void
@@ -146,7 +144,6 @@ make_shader_stack (const char *name,
GtkTextBuffer *buffer;
GBytes *bytes;
GtkEventController *controller;
GtkCssProvider *provider;
GdkPaintable *paintable;
stack = gtk_shader_stack_new ();
@@ -237,12 +234,6 @@ make_shader_stack (const char *name,
g_signal_connect (buffer, "changed", G_CALLBACK (text_changed), button);
g_object_set_data (G_OBJECT (button), "the-stack", stack);
g_signal_connect (button, "clicked", G_CALLBACK (apply_text), buffer);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "button.small { padding: 0; }", -1);
gtk_style_context_add_provider (gtk_widget_get_style_context (button),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
gtk_widget_set_halign (button, GTK_ALIGN_CENTER);
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
gtk_widget_add_css_class (button, "small");
@@ -276,11 +267,21 @@ make_shader_stack (const char *name,
return vbox;
}
static void
remove_provider (gpointer data)
{
GtkStyleProvider *provider = GTK_STYLE_PROVIDER (data);
gtk_style_context_remove_provider_for_display (gdk_display_get_default (), provider);
g_object_unref (provider);
}
static GtkWidget *
create_gltransition_window (GtkWidget *do_widget)
{
GtkWidget *window, *headerbar, *scale, *outer_grid, *grid, *background;
GdkPaintable *paintable;
GtkCssProvider *provider;
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
@@ -335,6 +336,14 @@ create_gltransition_window (GtkWidget *do_widget)
make_shader_stack ("Kaleidoscope", "/gltransition/kaleidoscope.glsl", 3, scale),
1, 1, 1, 1);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "button.small { padding: 0; }", -1);
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_set_data_full (G_OBJECT (window), "provider", provider, remove_provider);
return window;
}

View File

@@ -10,8 +10,6 @@
#include "script-names.h"
#include "unicode-names.h"
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
#define UCD_TYPE_ITEM (ucd_item_get_type ())
G_DECLARE_FINAL_TYPE (UcdItem, ucd_item, UCD, ITEM, GObject)
@@ -339,6 +337,15 @@ create_ucd_view (GtkWidget *label)
static GtkWidget *window;
static void
remove_provider (gpointer data)
{
GtkStyleProvider *provider = GTK_STYLE_PROVIDER (data);
gtk_style_context_remove_provider_for_display (gdk_display_get_default (), provider);
g_object_unref (provider);
}
GtkWidget *
do_listview_ucd (GtkWidget *do_widget)
{
@@ -361,7 +368,7 @@ do_listview_ucd (GtkWidget *do_widget)
gtk_widget_add_css_class (label, "enormous");
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "label.enormous { font-size: 80px; }", -1);
gtk_style_context_add_provider (gtk_widget_get_style_context (label), GTK_STYLE_PROVIDER (provider), 800);
gtk_style_context_add_provider_for_display (gdk_display_get_default (), GTK_STYLE_PROVIDER (provider), 800);
gtk_widget_set_hexpand (label, TRUE);
gtk_box_append (GTK_BOX (box), label);
@@ -371,6 +378,8 @@ do_listview_ucd (GtkWidget *do_widget)
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);
gtk_box_prepend (GTK_BOX (box), sw);
gtk_window_set_child (GTK_WINDOW (window), box);
g_object_set_data_full (G_OBJECT (window), "provider", provider, remove_provider);
}
if (!gtk_widget_get_visible (window))

View File

@@ -8,7 +8,6 @@
#include "config.h"
#include <gtk/gtk.h>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
/* Create an object for the pegs that get moved around in the game.
*
@@ -361,6 +360,15 @@ drop_drop (GtkDropTarget *target,
return TRUE;
}
static void
remove_provider (gpointer data)
{
GtkStyleProvider *provider = GTK_STYLE_PROVIDER (data);
gtk_style_context_remove_provider_for_display (gdk_display_get_default (), provider);
g_object_unref (provider);
}
static void
create_board (GtkWidget *window)
{
@@ -377,6 +385,9 @@ create_board (GtkWidget *window)
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, css, -1);
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
800);
grid = gtk_grid_new ();
gtk_widget_set_halign (grid, GTK_ALIGN_CENTER);
@@ -395,9 +406,6 @@ create_board (GtkWidget *window)
continue;
image = gtk_image_new ();
gtk_style_context_add_provider (gtk_widget_get_style_context (image),
GTK_STYLE_PROVIDER (provider),
800);
gtk_widget_add_css_class (image, "solitaire-field");
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
if (x != 3 || y != 3)
@@ -441,7 +449,7 @@ create_board (GtkWidget *window)
}
}
g_object_unref (provider);
g_object_set_data_full (G_OBJECT (window), "provider", provider, remove_provider);
}
static void

View File

@@ -16,7 +16,6 @@ enum {
NUM_PROPERTIES
};
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
static void
pixbuf_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
@@ -37,7 +36,6 @@ pixbuf_paintable_snapshot (GdkPaintable *paintable,
g_object_unref (texture);
}
G_GNUC_END_IGNORE_DEPRECATIONS;
static int
pixbuf_paintable_get_intrinsic_width (GdkPaintable *paintable)

View File

@@ -16,26 +16,6 @@
#include <glib/gi18n.h>
#include <gtk/gtk.h>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
/* Convenience function to create a combo box holding a number of strings
*/
GtkWidget *
create_combo_box (const char **strings)
{
GtkWidget *combo_box;
const char **str;
combo_box = gtk_combo_box_text_new ();
for (str = strings; *str; str++)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), *str);
gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
return combo_box;
}
static void
add_row (GtkGrid *table,
int row,
@@ -43,7 +23,7 @@ add_row (GtkGrid *table,
const char *label_text,
const char **options)
{
GtkWidget *combo_box;
GtkWidget *dropdown;
GtkWidget *label;
label = gtk_label_new_with_mnemonic (label_text);
@@ -52,12 +32,12 @@ add_row (GtkGrid *table,
gtk_widget_set_hexpand (label, TRUE);
gtk_grid_attach (table, label, 0, row, 1, 1);
combo_box = create_combo_box (options);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo_box);
gtk_widget_set_halign (combo_box, GTK_ALIGN_END);
gtk_widget_set_valign (combo_box, GTK_ALIGN_BASELINE);
gtk_size_group_add_widget (size_group, combo_box);
gtk_grid_attach (table, combo_box, 1, row, 1, 1);
dropdown = gtk_drop_down_new_from_strings (options);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), dropdown);
gtk_widget_set_halign (dropdown, GTK_ALIGN_END);
gtk_widget_set_valign (dropdown, GTK_ALIGN_BASELINE);
gtk_size_group_add_widget (size_group, dropdown);
gtk_grid_attach (table, dropdown, 1, row, 1, 1);
}
static void

View File

@@ -11,7 +11,6 @@
#include <stdlib.h> /* for exit() */
#include "paintable.h"
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static void easter_egg_callback (GtkWidget *button, gpointer data);
@@ -431,11 +430,11 @@ attach_widgets (GtkTextView *text_view)
}
else if (i == 1)
{
widget = gtk_combo_box_text_new ();
const char *options[] = {
"Option 1", "Option 2", "Option 3", NULL
};
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Option 1");
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Option 2");
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Option 3");
widget = gtk_drop_down_new_from_strings (options);
}
else if (i == 2)
{

View File

@@ -1,8 +1,6 @@
#include <stdlib.h>
#include <gtk/gtk.h>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct
{
GtkApplication parent_instance;
@@ -352,7 +350,8 @@ quit_activated (GSimpleAction *action,
}
static void
combo_changed (GtkComboBox *combo,
combo_changed (GtkDropDown *combo,
GParamSpec *pspec,
gpointer user_data)
{
GtkDialog *dialog = user_data;
@@ -361,7 +360,7 @@ combo_changed (GtkComboBox *combo,
char **accels;
char *str;
action = gtk_combo_box_get_active_id (combo);
action = gtk_string_object_get_string (GTK_STRING_OBJECT (gtk_drop_down_get_selected_item (combo)));
if (!action)
return;
@@ -390,7 +389,7 @@ response (GtkDialog *dialog,
gpointer user_data)
{
GtkEntry *entry = g_object_get_data (user_data, "entry");
GtkComboBox *combo = g_object_get_data (user_data, "combo");
GtkDropDown *combo = g_object_get_data (user_data, "combo");
const char *action;
const char *str;
char **accels;
@@ -401,7 +400,7 @@ response (GtkDialog *dialog,
return;
}
action = gtk_combo_box_get_active_id (combo);
action = gtk_string_object_get_string (GTK_STRING_OBJECT (gtk_drop_down_get_selected_item (combo)));
if (!action)
return;
@@ -426,6 +425,7 @@ edit_accels (GSimpleAction *action,
char **actions;
GtkWidget *dialog;
int i;
GtkStringList *strings;
dialog = gtk_dialog_new_with_buttons ("Accelerators",
NULL,
@@ -437,7 +437,8 @@ edit_accels (GSimpleAction *action,
gtk_window_set_application (GTK_WINDOW (dialog), app);
actions = gtk_application_list_action_descriptions (app);
combo = gtk_combo_box_text_new ();
strings = gtk_string_list_new (NULL);
combo = gtk_drop_down_new (G_LIST_MODEL (strings), NULL);
g_object_set (gtk_dialog_get_content_area (GTK_DIALOG (dialog)),
"margin-top", 10,
"margin-bottom", 10,
@@ -448,8 +449,8 @@ edit_accels (GSimpleAction *action,
gtk_box_append (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), combo);
for (i = 0; actions[i]; i++)
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), actions[i], actions[i]);
g_signal_connect (combo, "changed", G_CALLBACK (combo_changed), dialog);
gtk_string_list_append (strings, actions[i]);
g_signal_connect (combo, "notify::selected", G_CALLBACK (combo_changed), dialog);
entry = gtk_entry_new ();
gtk_widget_set_hexpand (entry, TRUE);
@@ -460,7 +461,7 @@ edit_accels (GSimpleAction *action,
g_object_set_data (G_OBJECT (dialog), "combo", combo);
g_object_set_data (G_OBJECT (dialog), "entry", entry);
gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
gtk_drop_down_set_selected (GTK_DROP_DOWN (combo), 0);
gtk_widget_show (dialog);
}

View File

@@ -436,6 +436,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
completion->inline_selection = FALSE;
completion->filter_model = NULL;
completion->insert_text_signal_group = NULL;
}
static gboolean
@@ -1384,13 +1385,12 @@ gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
{
int len;
GtkText *text = gtk_entry_get_text_widget (GTK_ENTRY (completion->entry));
GtkEntryBuffer *buffer = gtk_text_get_buffer (text);
if (completion->changed_id > 0)
g_signal_handler_block (text, completion->changed_id);
if (completion->insert_text_id > 0)
g_signal_handler_block (buffer, completion->insert_text_id);
if (completion->insert_text_signal_group != NULL)
g_signal_group_block (completion->insert_text_signal_group);
gtk_editable_set_text (GTK_EDITABLE (completion->entry), new_text);
@@ -1400,8 +1400,8 @@ gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
if (completion->changed_id > 0)
g_signal_handler_unblock (text, completion->changed_id);
if (completion->insert_text_id > 0)
g_signal_handler_unblock (buffer, completion->insert_text_id);
if (completion->insert_text_signal_group != NULL)
g_signal_group_unblock (completion->insert_text_signal_group);
}
static gboolean
@@ -1440,11 +1440,9 @@ gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion)
gboolean done;
char *prefix;
GtkText *text = gtk_entry_get_text_widget (GTK_ENTRY (completion->entry));
GtkEntryBuffer *buffer = gtk_text_get_buffer (text);
if (completion->insert_text_id > 0)
g_signal_handler_block (buffer, completion->insert_text_id);
if (completion->insert_text_signal_group != NULL)
g_signal_group_block (completion->insert_text_signal_group);
prefix = gtk_entry_completion_compute_prefix (completion,
gtk_editable_get_text (GTK_EDITABLE (completion->entry)));
@@ -1456,8 +1454,8 @@ gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion)
g_free (prefix);
}
if (completion->insert_text_id > 0)
g_signal_handler_unblock (buffer, completion->insert_text_id);
if (completion->insert_text_signal_group != NULL)
g_signal_group_unblock (completion->insert_text_signal_group);
}
/**
@@ -2098,7 +2096,6 @@ connect_completion_signals (GtkEntryCompletion *completion)
{
GtkEventController *controller;
GtkText *text = gtk_entry_get_text_widget (GTK_ENTRY (completion->entry));
GtkEntryBuffer *buffer = gtk_text_get_buffer (text);
controller = completion->entry_key_controller = gtk_event_controller_key_new ();
gtk_event_controller_set_static_name (controller, "gtk-entry-completion");
@@ -2113,8 +2110,10 @@ connect_completion_signals (GtkEntryCompletion *completion)
completion->changed_id =
g_signal_connect (text, "changed", G_CALLBACK (gtk_entry_completion_changed), completion);
completion->insert_text_id =
g_signal_connect (buffer, "inserted-text", G_CALLBACK (completion_inserted_text_callback), completion);
completion->insert_text_signal_group = g_signal_group_new (GTK_TYPE_ENTRY_BUFFER);
g_signal_group_connect (completion->insert_text_signal_group, "inserted-text", G_CALLBACK (completion_inserted_text_callback), completion);
g_object_bind_property (text, "buffer", completion->insert_text_signal_group, "target", G_BINDING_SYNC_CREATE);
g_signal_connect (text, "notify", G_CALLBACK (clear_completion_callback), completion);
g_signal_connect_swapped (text, "activate", G_CALLBACK (accept_completion_callback), completion);
}
@@ -2123,7 +2122,6 @@ static void
disconnect_completion_signals (GtkEntryCompletion *completion)
{
GtkText *text = gtk_entry_get_text_widget (GTK_ENTRY (completion->entry));
GtkEntryBuffer *buffer = gtk_text_get_buffer (text);
gtk_widget_remove_controller (GTK_WIDGET (text), completion->entry_key_controller);
gtk_widget_remove_controller (GTK_WIDGET (text), completion->entry_focus_controller);
@@ -2134,12 +2132,9 @@ disconnect_completion_signals (GtkEntryCompletion *completion)
g_signal_handler_disconnect (text, completion->changed_id);
completion->changed_id = 0;
}
if (completion->insert_text_id > 0 &&
g_signal_handler_is_connected (buffer, completion->insert_text_id))
{
g_signal_handler_disconnect (buffer, completion->insert_text_id);
completion->insert_text_id = 0;
}
g_clear_object (&completion->insert_text_signal_group);
g_signal_handlers_disconnect_by_func (text, G_CALLBACK (clear_completion_callback), completion);
g_signal_handlers_disconnect_by_func (text, G_CALLBACK (accept_completion_callback), completion);
}

View File

@@ -78,16 +78,20 @@
#include <gtk/gtkcenterbox.h>
#include <gtk/gtkcenterlayout.h>
#include <gtk/gtkcheckbutton.h>
#include <gtk/gtkchoice.h>
#include <gtk/gtkcolorbutton.h>
#include <gtk/gtkcolorchooser.h>
#include <gtk/gtkcolorchooserdialog.h>
#include <gtk/gtkcolorchooserwidget.h>
#include <gtk/gtkcolordialog.h>
#include <gtk/gtkcolordialogbutton.h>
#include <gtk/gtkcolorutils.h>
#include <gtk/gtkcolumnview.h>
#include <gtk/gtkcolumnviewcolumn.h>
#include <gtk/gtkcolumnviewsorter.h>
#include <gtk/deprecated/gtkcombobox.h>
#include <gtk/deprecated/gtkcomboboxtext.h>
#include <gtk/gtkchoice.h>
#include <gtk/gtkconstraintlayout.h>
#include <gtk/gtkconstraint.h>
#include <gtk/gtkcssprovider.h>
@@ -124,6 +128,7 @@
#include <gtk/gtkfilechooserdialog.h>
#include <gtk/gtkfilechoosernative.h>
#include <gtk/gtkfilechooserwidget.h>
#include <gtk/gtkfiledialog.h>
#include <gtk/gtkfilefilter.h>
#include <gtk/gtkfilter.h>
#include <gtk/gtkfilterlistmodel.h>
@@ -134,6 +139,8 @@
#include <gtk/gtkfontchooser.h>
#include <gtk/gtkfontchooserdialog.h>
#include <gtk/gtkfontchooserwidget.h>
#include <gtk/gtkfontdialog.h>
#include <gtk/gtkfontdialogbutton.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkgesture.h>
#include <gtk/gtkgestureclick.h>
@@ -157,6 +164,7 @@
#include <gtk/gtkimcontextsimple.h>
#include <gtk/gtkimmulticontext.h>
#include <gtk/gtkinfobar.h>
#include <gtk/gtkinfodialog.h>
#include <gtk/gtkinscription.h>
#include <gtk/gtklabel.h>
#include <gtk/gtklayoutmanager.h>

View File

@@ -27,6 +27,8 @@
#include "gtkmarshalers.h"
#include "gtkwidgetprivate.h"
#include "gsettings-mapping.h"
#include "gtkdebug.h"
#include "gtkprivate.h"
#include <string.h>
@@ -846,9 +848,15 @@ gtk_action_muxer_activate_action (GtkActionMuxer *muxer,
if (!_gtk_bitmask_get (muxer->widget_actions_disabled, position))
{
if (action->activate)
action->activate (muxer->widget, action->name, parameter);
{
GTK_DEBUG (ACTIONS, "%s: activate action", action->name);
action->activate (muxer->widget, action->name, parameter);
}
else if (action->pspec)
prop_action_activate (muxer->widget, action, parameter);
{
GTK_DEBUG (ACTIONS, "%s: activate prop action", action->pspec->name);
prop_action_activate (muxer->widget, action, parameter);
}
}
return;

View File

@@ -225,6 +225,7 @@
#include "gtkicontheme.h"
#include "gtkiconthemeprivate.h"
#include "gtkdebug.h"
#include "gtkstringlist.h"
static void gtk_builder_finalize (GObject *object);
@@ -551,6 +552,19 @@ gtk_builder_get_parameters (GtkBuilder *builder,
if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == GTK_TYPE_EXPRESSION)
gtk_value_set_expression (&property_value, prop->value);
else if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == G_TYPE_STRV)
{
GStrvBuilder *strv = g_strv_builder_new ();
for (guint j = 0; j < g_list_model_get_n_items (G_LIST_MODEL (prop->value)); j++)
{
GtkStringObject *s = g_list_model_get_item (G_LIST_MODEL (prop->value), j);
g_strv_builder_add (strv, gtk_string_object_get_string (s));
g_object_unref (s);
}
g_value_set_boxed (&property_value, g_strv_builder_end (strv));
}
else
g_assert_not_reached ();
}

View File

@@ -1039,6 +1039,10 @@ free_property_info (PropertyInfo *info)
{
if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == GTK_TYPE_EXPRESSION)
gtk_expression_unref (info->value);
else if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == G_TYPE_STRV)
{
/* info->value is in the hash table of objects */
}
else
g_assert_not_reached();
}
@@ -1723,8 +1727,11 @@ parse_custom (GtkBuildableParseContext *context,
object = ((ObjectInfo*)child_info->parent)->object;
child = child_info->object;
}
else
return FALSE;
else if (parent_info->tag_type == TAG_PROPERTY)
{
g_print ("custom markup in <property>\n");
return FALSE;
}
if (!gtk_buildable_custom_tag_start (GTK_BUILDABLE (object),
data->builder,
@@ -1947,7 +1954,10 @@ end_element (GtkBuildableParseContext *context,
if (child_info)
child_info->object = object_info->object;
if (prop_info)
g_string_assign (prop_info->text, object_info->id);
{
g_string_assign (prop_info->text, object_info->id);
prop_info->value = object_info->object;
}
if (GTK_IS_BUILDABLE (object_info->object) &&
GTK_BUILDABLE_GET_IFACE (object_info->object)->parser_finished)
@@ -2074,7 +2084,7 @@ end_element (GtkBuildableParseContext *context,
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_UNHANDLED_TAG,
"Unhandled tag: <%s>", element_name);
"Unhandled end tag: </%s>", element_name);
_gtk_builder_prefix_error (data->builder, context, error);
}
}

93
gtk/gtkchoice.c Normal file
View File

@@ -0,0 +1,93 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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 "config.h"
#include "gtkchoice.h"
struct _GtkChoice
{
GObject parent_instance;
char *label;
char **options;
};
G_DEFINE_TYPE (GtkChoice, gtk_choice, G_TYPE_OBJECT)
static void
gtk_choice_init (GtkChoice *self)
{
}
static void
gtk_choice_finalize (GObject *object)
{
GtkChoice *self = GTK_CHOICE (object);
g_free (self->label);
g_strfreev (self->options);
G_OBJECT_CLASS (gtk_choice_parent_class)->finalize (object);
}
static void
gtk_choice_class_init (GtkChoiceClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_choice_finalize;
}
/* }}} */
/* {{{ Public API */
GtkChoice *
gtk_choice_new (const char *label,
const char * const *options)
{
GtkChoice *self;
self = g_object_new (GTK_TYPE_CHOICE, NULL);
self->label = g_strdup (label);
self->options = g_strdupv ((char **)options);
return self;
}
const char *
gtk_choice_get_label (GtkChoice *choice)
{
g_return_val_if_fail (GTK_IS_CHOICE (choice), NULL);
return choice->label;
}
const char * const *
gtk_choice_get_options (GtkChoice *choice)
{
g_return_val_if_fail (GTK_IS_CHOICE (choice), NULL);
return (const char * const *)choice->options;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

45
gtk/gtkchoice.h Normal file
View File

@@ -0,0 +1,45 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* 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/>.
*/
#pragma once
#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_CHOICE (gtk_choice_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkChoice, gtk_choice, GTK, CHOICE, GObject)
GDK_AVAILABLE_IN_4_10
GtkChoice * gtk_choice_new (const char *label,
const char * const *options);
GDK_AVAILABLE_IN_4_10
const char * gtk_choice_get_label (GtkChoice *choice);
GDK_AVAILABLE_IN_4_10
const char * const *
gtk_choice_get_options (GtkChoice *choice);
G_END_DECLS

475
gtk/gtkcolordialog.c Normal file
View File

@@ -0,0 +1,475 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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 "config.h"
#include "gtkcolordialog.h"
#include "gtkcolorchooserdialog.h"
#include "gtkcolorchooser.h"
#include "gtkbutton.h"
#include <glib/gi18n-lib.h>
/**
* GtkColorDialog:
*
* A `GtkColorDialog` object collects the arguments that
* are needed to present a color chooser dialog to the
* user, such as a title for the dialog and whether it
* should be modal.
*
* The dialog is shown with the [function@Gtk.ColorDialog.choose_rgba]
* function. This API follows the GIO async pattern, and the
* result can be obtained by calling
* [function@Gtk.ColorDialog.choose_rgba_finish].
*
* See [class@Gtk.ColorDialogButton] for a convenient control
* that uses `GtkColorDialog` and presents the results.
*
* `GtkColorDialog was added in GTK 4.10.
*/
/* {{{ GObject implementation */
struct _GtkColorDialog
{
GObject parent_instance;
char *title;
unsigned int modal : 1;
unsigned int with_alpha : 1;
};
enum
{
PROP_TITLE = 1,
PROP_MODAL,
PROP_WITH_ALPHA,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkColorDialog, gtk_color_dialog, G_TYPE_OBJECT)
static void
gtk_color_dialog_init (GtkColorDialog *self)
{
self->title = g_strdup (_("Pick a Color"));
self->modal = TRUE;
self->with_alpha = TRUE;
}
static void
gtk_color_dialog_finalize (GObject *object)
{
GtkColorDialog *self = GTK_COLOR_DIALOG (object);
g_free (self->title);
G_OBJECT_CLASS (gtk_color_dialog_parent_class)->finalize (object);
}
static void
gtk_color_dialog_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
GtkColorDialog *self = GTK_COLOR_DIALOG (object);
switch (property_id)
{
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_WITH_ALPHA:
g_value_set_boolean (value, self->with_alpha);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_color_dialog_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColorDialog *self = GTK_COLOR_DIALOG (object);
switch (prop_id)
{
case PROP_TITLE:
gtk_color_dialog_set_title (self, g_value_get_string (value));
break;
case PROP_MODAL:
gtk_color_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_WITH_ALPHA:
gtk_color_dialog_set_with_alpha (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_color_dialog_class_init (GtkColorDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_color_dialog_finalize;
object_class->get_property = gtk_color_dialog_get_property;
object_class->set_property = gtk_color_dialog_set_property;
/**
* GtkColorDialog:title: (attributes org.gtk.Property.get=gtk_color_dialog_get_title org.gtk.Property.set=gtk_color_dialog_set_title)
*
* A title that may be shown on the color chooser
* dialog that is presented by [function@Gtk.ColorDialog.choose_rgba].
*
* Since: 4.10
*/
properties[PROP_TITLE] =
g_param_spec_string ("title", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkColorDialog:modal: (attributes org.gtk.Property.get=gtk_color_dialog_get_modal org.gtk.Property.set=gtk_color_dialog_set_modal)
*
* Whether the color chooser dialog is modal.
*
* Since: 4.10
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkColorDialog:with-alpha: (attributes org.gtk.Property.get=gtk_color_dialog_get_with_alpha org.gtk.Property.set=gtk_color_dialog_set_with_alpha)
*
* Whether colors may have alpha (translucency).
*
* When with-alpha is %FALSE, the color that is selected
* will be forced to have alpha == 1.
*
* Since: 4.10
*/
properties[PROP_WITH_ALPHA] =
g_param_spec_boolean ("with-alpha", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ Constructor */
/**
* gtk_color_dialog_new:
*
* Creates a new `GtkColorDialog` object.
*
* Returns: the new `GtkColorDialog`
*
* Since: 4.10
*/
GtkColorDialog *
gtk_color_dialog_new (void)
{
return g_object_new (GTK_TYPE_COLOR_DIALOG, NULL);
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_color_dialog_get_title:
* @self: a `GtkColorDialog`
*
* Returns the title that will be shown on the
* color chooser dialog.
*
* Returns: the title
*
* Since: 4.10
*/
const char *
gtk_color_dialog_get_title (GtkColorDialog *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG (self), NULL);
return self->title;
}
/**
* gtk_color_dialog_set_title:
* @self: a `GtkColorDialog`
* @title: the new title
*
* Sets the title that will be shown on the
* color chooser dialog.
*
* Since: 4.10
*/
void
gtk_color_dialog_set_title (GtkColorDialog *self,
const char *title)
{
char *new_title;
g_return_if_fail (GTK_IS_COLOR_DIALOG (self));
g_return_if_fail (title != NULL);
if (g_str_equal (self->title, title))
return;
new_title = g_strdup (title);
g_free (self->title);
self->title = new_title;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_color_dialog_get_modal:
* @self: a `GtkColorDialog`
*
* Returns whether the color chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Returns: `TRUE` if the color chooser dialog is modal
*
* Since: 4.10
*/
gboolean
gtk_color_dialog_get_modal (GtkColorDialog *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_color_dialog_set_modal:
* @self: a `GtkColorDialog`
* @modal: the new value
*
* Sets whether the color chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Since: 4.10
*/
void
gtk_color_dialog_set_modal (GtkColorDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_COLOR_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_color_dialog_get_with_alpha:
* @self: a `GtkColorDialog`
*
* Returns whether colors may have alpha.
*
* Returns: `TRUE` if colors may have alpha
*
* Since: 4.10
*/
gboolean
gtk_color_dialog_get_with_alpha (GtkColorDialog *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG (self), TRUE);
return self->with_alpha;
}
/**
* gtk_color_dialog_set_with_alpha:
* @self: a `GtkColorDialog`
* @with_alpha: the new value
*
* Sets whether colors may have alpha.
*
* Since: 4.10
*/
void
gtk_color_dialog_set_with_alpha (GtkColorDialog *self,
gboolean with_alpha)
{
g_return_if_fail (GTK_IS_COLOR_DIALOG (self));
if (self->with_alpha == with_alpha)
return;
self->with_alpha = with_alpha;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WITH_ALPHA]);
}
/* }}} */
/* {{{ Async API */
static void response_cb (GTask *task,
int response);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
response_cb (task, GTK_RESPONSE_CANCEL);
}
static void
response_cb (GTask *task,
int response)
{
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response == GTK_RESPONSE_OK)
{
GtkColorChooserDialog *window;
GdkRGBA color;
window = GTK_COLOR_CHOOSER_DIALOG (g_task_get_task_data (task));
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (window), &color);
g_task_return_pointer (task, &color, NULL);
}
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
g_object_unref (task);
}
/**
* gtk_color_dialog_choose_rgba:
* @self: a `GtkColorDialog`
* @parent: (nullable): the parent `GtkWindow`
* @initial_color: (nullable): the color to select initially
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function initiates a color choice operation by
* presenting a color chooser dialog to the user.
*
* The @callback will be called when the dialog is dismissed.
* It should call [function@Gtk.ColorDialog.choose_rgba_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_color_dialog_choose_rgba (GtkColorDialog *self,
GtkWindow *parent,
const GdkRGBA *initial_color,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkWidget *window;
GTask *task;
g_return_if_fail (GTK_IS_COLOR_DIALOG (self));
window = gtk_color_chooser_dialog_new (self->title, parent);
if (initial_color)
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (window), initial_color);
gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (window), self->with_alpha);
gtk_window_set_modal (GTK_WINDOW (window), self->modal);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_color_dialog_choose_rgba);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect_swapped (window, "response", G_CALLBACK (response_cb), task);
gtk_window_present (GTK_WINDOW (window));
}
/**
* gtk_color_dialog_choose_rgba_finish:
* @self: a `GtkColorDialog`
* @result: a `GAsyncResult`
* @color: (out caller-allocates): return location for the color
* @error: return location for an error
*
* Finishes the [function@Gtk.ColorDialog.choose_rgba] call and
* returns the resulting color.
*
* Returns: `TRUE` if a color was selected. Otherwise,
* `FALSE` is returned and @error is set
*
* Since: 4.10
*/
gboolean
gtk_color_dialog_choose_rgba_finish (GtkColorDialog *self,
GAsyncResult *result,
GdkRGBA *color,
GError **error)
{
GdkRGBA *ret;
ret = g_task_propagate_pointer (G_TASK (result), error);
if (ret)
{
*color = *ret;
return TRUE;
}
return FALSE;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

73
gtk/gtkcolordialog.h Normal file
View File

@@ -0,0 +1,73 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* 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/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLOR_DIALOG (gtk_color_dialog_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkColorDialog, gtk_color_dialog, GTK, COLOR_DIALOG, GObject)
GDK_AVAILABLE_IN_4_10
GtkColorDialog *gtk_color_dialog_new (void);
GDK_AVAILABLE_IN_4_10
const char * gtk_color_dialog_get_title (GtkColorDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_set_title (GtkColorDialog *self,
const char *title);
GDK_AVAILABLE_IN_4_10
gboolean gtk_color_dialog_get_modal (GtkColorDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_set_modal (GtkColorDialog *self,
gboolean modal);
GDK_AVAILABLE_IN_4_10
gboolean gtk_color_dialog_get_with_alpha (GtkColorDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_set_with_alpha (GtkColorDialog *self,
gboolean with_alpha);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_choose_rgba (GtkColorDialog *self,
GtkWindow *parent,
const GdkRGBA *initial_color,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_color_dialog_choose_rgba_finish (GtkColorDialog *self,
GAsyncResult *result,
GdkRGBA *color,
GError **error);
G_END_DECLS

483
gtk/gtkcolordialogbutton.c Normal file
View File

@@ -0,0 +1,483 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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 "config.h"
#include "gtkcolordialogbutton.h"
#include "gtkbinlayout.h"
#include "gtkbutton.h"
#include "gtkcolorswatchprivate.h"
#include "gtkdragsource.h"
#include "gtkdroptarget.h"
#include <glib/gi18n-lib.h>
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkprivate.h"
#include "gtksnapshot.h"
#include "gtkwidgetprivate.h"
static gboolean drop (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
GtkColorDialogButton *self);
static GdkContentProvider *
drag_prepare (GtkDragSource *source,
double x,
double y,
GtkColorDialogButton *self);
static void button_clicked (GtkColorDialogButton *self);
/**
* GtkColorDialogButton:
*
* The `GtkColorDialogButton` is a wrapped around a [class@Gtk.ColorDialog]
* and allows to open a color chooser dialog to change the color.
*
* ![An example GtkColorDialogButton](color-button.png)
*
* It is suitable widget for selecting a color in a preference dialog.
*
* # CSS nodes
*
* ```
* colorbutton
* ╰── button.color
* ╰── [content]
* ```
*
* `GtkColorDialogButton` has a single CSS node with name colorbutton which
* contains a button node. To differentiate it from a plain `GtkButton`,
* it gets the .color style class.
*/
/* {{{ GObject implementation */
struct _GtkColorDialogButton
{
GtkWidget parent_instance;
GtkWidget *button;
GtkWidget *swatch;
GtkColorDialog *dialog;
GdkRGBA color;
};
/* Properties */
enum
{
PROP_DIALOG = 1,
PROP_COLOR,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkColorDialogButton, gtk_color_dialog_button, GTK_TYPE_WIDGET)
static void
gtk_color_dialog_button_init (GtkColorDialogButton *self)
{
PangoLayout *layout;
PangoRectangle rect;
GtkDragSource *source;
GtkDropTarget *dest;
self->button = gtk_button_new ();
g_signal_connect_swapped (self->button, "clicked", G_CALLBACK (button_clicked), self);
gtk_widget_set_parent (self->button, GTK_WIDGET (self));
self->swatch = g_object_new (GTK_TYPE_COLOR_SWATCH,
"accessible-role", GTK_ACCESSIBLE_ROLE_IMG,
"selectable", FALSE,
"has-menu", FALSE,
"can-drag", FALSE,
NULL);
gtk_widget_set_can_focus (self->swatch, FALSE);
gtk_widget_remove_css_class (self->swatch, "activatable");
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "Black");
pango_layout_get_pixel_extents (layout, NULL, &rect);
g_object_unref (layout);
gtk_widget_set_size_request (self->swatch, rect.width, rect.height);
gtk_button_set_child (GTK_BUTTON (self->button), self->swatch);
dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY);
g_signal_connect (dest, "drop", G_CALLBACK (drop), self);
gtk_widget_add_controller (GTK_WIDGET (self->button), GTK_EVENT_CONTROLLER (dest));
source = gtk_drag_source_new ();
g_signal_connect (source, "prepare", G_CALLBACK (drag_prepare), self);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (source),
GTK_PHASE_CAPTURE);
gtk_widget_add_controller (self->button, GTK_EVENT_CONTROLLER (source));
gtk_widget_add_css_class (self->button, "color");
}
static void
gtk_color_dialog_button_set_property (GObject *object,
unsigned int param_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColorDialogButton *self = GTK_COLOR_DIALOG_BUTTON (object);
switch (param_id)
{
case PROP_DIALOG:
gtk_color_dialog_button_set_dialog (self, g_value_get_object (value));
break;
case PROP_COLOR:
gtk_color_dialog_button_set_color (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_color_dialog_button_get_property (GObject *object,
unsigned int param_id,
GValue *value,
GParamSpec *pspec)
{
GtkColorDialogButton *self = GTK_COLOR_DIALOG_BUTTON (object);
switch (param_id)
{
case PROP_DIALOG:
g_value_set_object (value, self->dialog);
break;
case PROP_COLOR:
g_value_set_boxed (value, &self->color);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_color_dialog_button_dispose (GObject *object)
{
GtkColorDialogButton *self = GTK_COLOR_DIALOG_BUTTON (object);
g_clear_pointer (&self->button, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_color_dialog_button_parent_class)->dispose (object);
}
static void
gtk_color_dialog_button_finalize (GObject *object)
{
GtkColorDialogButton *self = GTK_COLOR_DIALOG_BUTTON (object);
g_clear_object (&self->dialog);
G_OBJECT_CLASS (gtk_color_dialog_button_parent_class)->finalize (object);
}
static void
gtk_color_dialog_button_class_init (GtkColorDialogButtonClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->get_property = gtk_color_dialog_button_get_property;
object_class->set_property = gtk_color_dialog_button_set_property;
object_class->dispose = gtk_color_dialog_button_dispose;
object_class->finalize = gtk_color_dialog_button_finalize;
widget_class->grab_focus = gtk_widget_grab_focus_child;
widget_class->focus = gtk_widget_focus_child;
/**
* GtkColorDialogButton:dialog: (attributes org.gtk.Property.get=gtk_color_dialog_button_get_dialog org.gtk.Property.set=gtk_color_dialog_button_set_dialog)
*
* The `GtkColorDialog` that contains parameters for
* the color chooser dialog.
*
* Since: 4.10
*/
properties[PROP_DIALOG] =
g_param_spec_object ("dialog", NULL, NULL,
GTK_TYPE_COLOR_DIALOG,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkColorDialogButton:color: (attributes org.gtk.Property.get=gtk_color_dialog_button_get_color org.gtk.Property.set=gtk_color_dialog_button_set_color)
*
* The selected color.
*
* This property can be set to give the button its initial
* color, and it will be updated to reflect the users choice
* in the color chooser dialog.
*
* Listen to `notify::color` to get informed about changes
* to the buttons color.
*
* Since: 4.10
*/
properties[PROP_COLOR] =
g_param_spec_boxed ("color", NULL, NULL,
GDK_TYPE_RGBA,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (widget_class, "colorbutton");
}
/* }}} */
/* {{{ Private API, callbacks */
static guint
scale_round (double value,
double scale)
{
value = floor (value * scale + 0.5);
value = CLAMP (value, 0, scale);
return (guint)value;
}
static char *
accessible_color_name (const GdkRGBA *color)
{
if (color->alpha < 1.0)
return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%, Alpha %d%%"),
scale_round (color->red, 100),
scale_round (color->green, 100),
scale_round (color->blue, 100),
scale_round (color->alpha, 100));
else
return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%"),
scale_round (color->red, 100),
scale_round (color->green, 100),
scale_round (color->blue, 100));
}
static gboolean
drop (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
GtkColorDialogButton *self)
{
GdkRGBA *color = g_value_get_boxed (value);
gtk_color_dialog_button_set_color (self, color);
return TRUE;
}
static GdkContentProvider *
drag_prepare (GtkDragSource *source,
double x,
double y,
GtkColorDialogButton *self)
{
GdkRGBA color;
gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (self->swatch), &color);
return gdk_content_provider_new_typed (GDK_TYPE_RGBA, &color);
}
static void
color_chosen (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkColorDialogButton *self = data;
GdkRGBA color;
GError *error = NULL;
if (gtk_color_dialog_choose_rgba_finish (self->dialog, result, &color, &error))
{
gtk_color_dialog_button_set_color (self, &color);
}
else
{
g_print ("%s\n", error->message);
g_error_free (error);
}
}
static void
button_clicked (GtkColorDialogButton *self)
{
GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (self));
GtkWindow *parent = NULL;
if (GTK_IS_WINDOW (root))
parent = GTK_WINDOW (root);
gtk_color_dialog_choose_rgba (self->dialog, parent, &self->color,
NULL, color_chosen, self);
}
/* }}} */
/* {{{ Constructor */
/**
* gtk_color_dialog_button_new:
* @dialog: (nullable) (transfer full): the `GtkColorDialog` to use
*
* Creates a new `GtkColorDialogButton` with the
* given `GtkColorDialog`.
*
* You can pass `NULL` to this function and set a `GtkColorDialog`
* later. The button will be insensitive until that happens.
*
* Returns: the new `GtkColorDialogButton`
*
* Since: 4.10
*/
GtkWidget *
gtk_color_dialog_button_new (GtkColorDialog *dialog)
{
GtkWidget *self;
g_return_val_if_fail (GTK_IS_COLOR_DIALOG (dialog), NULL);
self = g_object_new (GTK_TYPE_COLOR_DIALOG_BUTTON,
"dialog", dialog,
NULL);
g_clear_object (&dialog);
return self;
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_color_dialog_button_get_dialog:
* @self: a `GtkColorDialogButton`
*
* Returns the `GtkColorDialog` of @self.
*
* Returns: (transfer none) (nullable): the `GtkColorDialog`
*
* Since: 4.10
*/
GtkColorDialog *
gtk_color_dialog_button_get_dialog (GtkColorDialogButton *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG_BUTTON (self), NULL);
return self->dialog;
}
/**
* gtk_color_dialog_button_set_dialog:
* @self: a `GtkColorDialogButton`
* @dialog: the new `GtkColorDialog`
*
* Sets a `GtkColorDialog` object to use for
* creating the color chooser dialog that is
* presented when the user clicks the button.
*
* Since: 4.10
*/
void
gtk_color_dialog_button_set_dialog (GtkColorDialogButton *self,
GtkColorDialog *dialog)
{
g_return_if_fail (GTK_IS_COLOR_DIALOG_BUTTON (self));
g_return_if_fail (GTK_IS_COLOR_DIALOG (dialog));
if (!g_set_object (&self->dialog, dialog))
return;
gtk_widget_set_sensitive (self->button, dialog != NULL);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DIALOG]);
}
/**
* gtk_color_dialog_button_get_color:
* @self: a `GtkColorDialogButton`
*
* Returns the color of the button.
*
* This function is what should be used to obtain
* the color that was choosen by the user. To get
* informed about changes, listen to "notify::color".
*
* Returns: the color
*
* Since: 4.10
*/
const GdkRGBA *
gtk_color_dialog_button_get_color (GtkColorDialogButton *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG_BUTTON (self), NULL);
return &self->color;
}
/**
* gtk_color_dialog_button_set_color:
* @self: a `GtkColorDialogButton`
* @color: the new color
*
* Sets the color of the button.
*
* Since: 4.10
*/
void
gtk_color_dialog_button_set_color (GtkColorDialogButton *self,
const GdkRGBA *color)
{
char *text;
g_return_if_fail (GTK_IS_COLOR_DIALOG_BUTTON (self));
g_return_if_fail (color != NULL);
if (gdk_rgba_equal (&self->color, color))
return;
self->color = *color;
gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (self->swatch), color);
text = accessible_color_name (color);
gtk_accessible_update_property (GTK_ACCESSIBLE (self->swatch),
GTK_ACCESSIBLE_PROPERTY_LABEL, text,
-1);
g_free (text);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR]);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -0,0 +1,53 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkbutton.h>
#include <gtk/gtkcolordialog.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLOR_DIALOG_BUTTON (gtk_color_dialog_button_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkColorDialogButton, gtk_color_dialog_button, GTK, COLOR_DIALOG_BUTTON, GtkWidget)
GDK_AVAILABLE_IN_4_10
GtkWidget * gtk_color_dialog_button_new (GtkColorDialog *dialog);
GDK_AVAILABLE_IN_4_10
GtkColorDialog *gtk_color_dialog_button_get_dialog (GtkColorDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_button_set_dialog (GtkColorDialogButton *self,
GtkColorDialog *dialog);
GDK_AVAILABLE_IN_4_10
const GdkRGBA * gtk_color_dialog_button_get_color (GtkColorDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_button_set_color (GtkColorDialogButton *self,
const GdkRGBA *color);
G_END_DECLS

View File

@@ -25,6 +25,7 @@
#include "gtkcomposetable.h"
#include "gtkimcontextsimple.h"
#include "gtkprivate.h"
#define GTK_COMPOSE_TABLE_MAGIC "GtkComposeTable"

View File

@@ -61,7 +61,8 @@ struct _GtkEntryCompletion
gulong completion_timeout;
gulong changed_id;
gulong insert_text_id;
GSignalGroup *insert_text_signal_group;
int current_selected;

304
gtk/gtkfilechoosercell.c Normal file
View File

@@ -0,0 +1,304 @@
/*
* Copyright © 2022 Red Hat, Inc.
*
* 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 "config.h"
#include "gtkfilechoosercellprivate.h"
#include "gtkprivate.h"
#include "gtkbinlayout.h"
#include "gtkdragsource.h"
#include "gtkgestureclick.h"
#include "gtkgesturelongpress.h"
#include "gtkicontheme.h"
#include "gtklistitem.h"
#include "gtkselectionmodel.h"
#include "gtkfilechooserutils.h"
#include "gtkfilechooserwidget.h"
#include "gtkfilechooserwidgetprivate.h"
struct _GtkFileChooserCell
{
GtkWidget parent_instance;
GFileInfo *item;
gboolean selected;
guint position;
gboolean show_time;
};
struct _GtkFileChooserCellClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkFileChooserCell, gtk_file_chooser_cell, GTK_TYPE_WIDGET)
enum
{
PROP_POSITION = 1,
PROP_SELECTED,
PROP_ITEM,
PROP_SHOW_TIME,
};
#define ICON_SIZE 16
static void
popup_menu (GtkFileChooserCell *self,
double x,
double y)
{
GtkWidget *widget = GTK_WIDGET (self);
GtkSelectionModel *model;
GtkWidget *impl;
double xx, yy;
impl = gtk_widget_get_ancestor (widget, GTK_TYPE_FILE_CHOOSER_WIDGET);
model = gtk_file_chooser_widget_get_selection_model (GTK_FILE_CHOOSER_WIDGET (impl));
gtk_selection_model_select_item (model, self->position, TRUE);
gtk_widget_translate_coordinates (widget, GTK_WIDGET (impl),
x, y, &xx, &yy);
gtk_widget_activate_action (widget, "item.popup-file-list-menu",
"(udd)", self->position, xx, yy);
}
static void
file_chooser_cell_clicked (GtkEventController *controller,
int n_press,
double x,
double y)
{
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (widget);
gtk_gesture_set_state (GTK_GESTURE (controller), GTK_EVENT_SEQUENCE_CLAIMED);
popup_menu (self, x, y);
}
static void
file_chooser_cell_long_pressed (GtkEventController *controller,
double x,
double y)
{
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (widget);
gtk_gesture_set_state (GTK_GESTURE (controller), GTK_EVENT_SEQUENCE_CLAIMED);
popup_menu (self, x, y);
}
static GdkContentProvider *
drag_prepare_cb (GtkDragSource *source,
double x,
double y,
gpointer user_data)
{
GdkContentProvider *provider;
GSList *selection;
GtkFileChooserWidget *impl;
GtkIconTheme *icon_theme;
GIcon *icon;
int scale;
GtkIconPaintable *paintable;
GtkFileChooserCell *self = user_data;
impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (GTK_WIDGET (self),
GTK_TYPE_FILE_CHOOSER_WIDGET));
if (!self->selected)
{
gtk_selection_model_select_item (gtk_file_chooser_widget_get_selection_model (impl),
self->position, TRUE);
}
selection = gtk_file_chooser_widget_get_selected_files (impl);
if (!selection)
return NULL;
scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (self)));
icon = _gtk_file_info_get_icon (self->item, ICON_SIZE, scale, icon_theme);
paintable = gtk_icon_theme_lookup_by_gicon (icon_theme,icon, ICON_SIZE, scale, GTK_TEXT_DIR_NONE, 0);
gtk_drag_source_set_icon (source, GDK_PAINTABLE (paintable), x, y);
provider = gdk_content_provider_new_typed (GDK_TYPE_FILE_LIST, selection);
g_slist_free_full (selection, g_object_unref);
g_object_unref (paintable);
g_object_unref (icon);
return provider;
}
static void
gtk_file_chooser_cell_realize (GtkWidget *widget)
{
GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (widget);
GtkFileChooserWidget *impl;
impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (GTK_WIDGET (self),
GTK_TYPE_FILE_CHOOSER_WIDGET));
g_object_bind_property (impl, "show-time", self, "show-time", G_BINDING_SYNC_CREATE);
}
static void
gtk_file_chooser_cell_init (GtkFileChooserCell *self)
{
GtkGesture *gesture;
GtkDragSource *drag_source;
gesture = gtk_gesture_click_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
g_signal_connect (gesture, "pressed", G_CALLBACK (file_chooser_cell_clicked), NULL);
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 (file_chooser_cell_long_pressed), NULL);
drag_source = gtk_drag_source_new ();
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag_source));
g_signal_connect (drag_source, "prepare", G_CALLBACK (drag_prepare_cb), self);
g_signal_connect (self, "realize", G_CALLBACK (gtk_file_chooser_cell_realize), NULL);
}
static void
gtk_file_chooser_cell_dispose (GObject *object)
{
GtkWidget *child;
while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
gtk_widget_unparent (child);
G_OBJECT_CLASS (gtk_file_chooser_cell_parent_class)->dispose (object);
}
static void
gtk_file_chooser_cell_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (object);
switch (prop_id)
{
case PROP_POSITION:
self->position = g_value_get_uint (value);
break;
case PROP_SELECTED:
self->selected = g_value_get_boolean (value);
break;
case PROP_ITEM:
self->item = g_value_get_object (value);
break;
case PROP_SHOW_TIME:
self->show_time = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_file_chooser_cell_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (object);
switch (prop_id)
{
case PROP_POSITION:
g_value_set_uint (value, self->position);
break;
case PROP_SELECTED:
g_value_set_boolean (value, self->selected);
break;
case PROP_ITEM:
g_value_set_object (value, self->item);
break;
case PROP_SHOW_TIME:
g_value_set_boolean (value, self->show_time);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_file_chooser_cell_class_init (GtkFileChooserCellClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_file_chooser_cell_dispose;
object_class->set_property = gtk_file_chooser_cell_set_property;
object_class->get_property = gtk_file_chooser_cell_get_property;
g_object_class_install_property (object_class, PROP_POSITION,
g_param_spec_uint ("position", NULL, NULL,
0, G_MAXUINT, 0,
GTK_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_SELECTED,
g_param_spec_boolean ("selected", NULL, NULL,
FALSE,
GTK_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_ITEM,
g_param_spec_object ("item", NULL, NULL,
G_TYPE_FILE_INFO,
GTK_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_SHOW_TIME,
g_param_spec_boolean ("show-time", NULL, NULL,
FALSE,
GTK_PARAM_READWRITE));
gtk_widget_class_set_css_name (widget_class, I_("filelistcell"));
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
}
GtkFileChooserCell *
gtk_file_chooser_cell_new (void)
{
return g_object_new (GTK_TYPE_FILE_CHOOSER_CELL, NULL);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright © 2022 Red Hat, Inc.
*
* 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
*/
#ifndef __GTK_FILE_CHOOSER_CELL_PRIVATE_H__
#define __GTK_FILE_CHOOSER_CELL_PRIVATE_H__
#include <gtk/gtkwidget.h>
#include <gtk/gtkexpression.h>
G_BEGIN_DECLS
#define GTK_TYPE_FILE_CHOOSER_CELL (gtk_file_chooser_cell_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkFileChooserCell, gtk_file_chooser_cell, GTK, FILE_CHOOSER_CELL, GtkWidget)
GtkFileChooserCell * gtk_file_chooser_cell_new (void);
G_END_DECLS
#endif /* __GTK_FILE_CHOOSER_CELL_PRIVATE_H__ */

View File

@@ -60,7 +60,8 @@ struct _GtkFileChooserEntry
char *dir_part;
char *file_part;
GtkTreeModel *completion_store;
GtkTreeStore *completion_store;
GtkFileSystemModel *model;
GtkFileFilter *current_filter;
guint current_folder_loaded : 1;
@@ -71,6 +72,7 @@ struct _GtkFileChooserEntry
enum
{
FILE_INFO_COLUMN,
DISPLAY_NAME_COLUMN,
FULL_PATH_COLUMN,
N_COLUMNS
@@ -197,20 +199,21 @@ match_func (GtkEntryCompletion *compl,
* current file filter (e.g. just jpg files) here. */
if (chooser_entry->current_filter != NULL)
{
GFile *file;
GFileInfo *info;
file = _gtk_file_system_model_get_file (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
iter);
info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
iter);
gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store),
iter,
FILE_INFO_COLUMN, &info,
-1);
g_assert (info != NULL);
g_object_unref (info);
/* We always allow navigating into subfolders, so don't ever filter directories */
if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
return TRUE;
if (!g_file_info_has_attribute (info, "standard::file"))
g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
g_assert (g_file_info_has_attribute (info, "standard::file"));
return gtk_filter_match (GTK_FILTER (chooser_entry->current_filter), info);
}
@@ -428,7 +431,7 @@ explicitly_complete (GtkFileChooserEntry *chooser_entry)
{
chooser_entry->complete_on_load = FALSE;
if (chooser_entry->completion_store)
if (chooser_entry->model)
{
char *completion, *text;
gsize completion_len, text_len;
@@ -539,77 +542,93 @@ update_inline_completion (GtkFileChooserEntry *chooser_entry)
static void
discard_completion_store (GtkFileChooserEntry *chooser_entry)
{
if (!chooser_entry->completion_store)
if (!chooser_entry->model)
return;
g_assert (chooser_entry->completion_store != NULL);
gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), NULL);
update_inline_completion (chooser_entry);
g_object_unref (chooser_entry->completion_store);
chooser_entry->completion_store = NULL;
g_clear_object (&chooser_entry->completion_store);
g_clear_object (&chooser_entry->model);
}
static gboolean
completion_store_set (GtkFileSystemModel *model,
GFile *file,
GFileInfo *info,
int column,
GValue *value,
gpointer data)
static void
model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkFileChooserEntry *self)
{
GtkFileChooserEntry *chooser_entry = data;
const char *prefix = "";
const char *suffix = "";
switch (column)
if (removed > 0)
{
case FULL_PATH_COLUMN:
prefix = chooser_entry->dir_part;
G_GNUC_FALLTHROUGH;
case DISPLAY_NAME_COLUMN:
GtkTreeIter iter;
if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->completion_store),
&iter,
NULL,
position))
{
while (removed--)
gtk_tree_store_remove (self->completion_store, &iter);
}
}
while (added-- > 0)
{
GtkTreeIter iter;
GFileInfo *info;
const char *suffix = NULL;
char *full_path;
char *display_name;
info = g_list_model_get_item (model, position);
if (_gtk_file_info_consider_as_directory (info))
suffix = G_DIR_SEPARATOR_S;
g_value_take_string (value,
g_strconcat (prefix,
g_file_info_get_display_name (info),
suffix,
NULL));
break;
default:
g_assert_not_reached ();
break;
}
display_name = g_strconcat (g_file_info_get_display_name (info), suffix, NULL);
full_path = g_strconcat (self->dir_part, display_name, NULL);
return TRUE;
gtk_tree_store_insert_with_values (self->completion_store,
&iter, NULL,
position,
FILE_INFO_COLUMN, info,
FULL_PATH_COLUMN, full_path,
DISPLAY_NAME_COLUMN, display_name,
-1);
g_clear_object (&info);
position++;
}
}
/* Fills the completion store from the contents of the current folder */
static void
populate_completion_store (GtkFileChooserEntry *chooser_entry)
{
chooser_entry->completion_store = GTK_TREE_MODEL (
chooser_entry->completion_store = gtk_tree_store_new (N_COLUMNS,
G_TYPE_FILE_INFO,
G_TYPE_STRING,
G_TYPE_STRING);
chooser_entry->model =
_gtk_file_system_model_new_for_directory (chooser_entry->current_folder_file,
"standard::name,standard::display-name,standard::type,"
"standard::content-type",
completion_store_set,
chooser_entry,
N_COLUMNS,
G_TYPE_STRING,
G_TYPE_STRING));
g_signal_connect (chooser_entry->completion_store, "finished-loading",
"standard::content-type");
g_signal_connect (chooser_entry->model, "items-changed",
G_CALLBACK (model_items_changed_cb), chooser_entry);
g_signal_connect (chooser_entry->model, "finished-loading",
G_CALLBACK (finished_loading_cb), chooser_entry);
_gtk_file_system_model_set_filter_folders (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
TRUE);
_gtk_file_system_model_set_show_files (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
_gtk_file_system_model_set_filter_folders (chooser_entry->model, TRUE);
_gtk_file_system_model_set_show_files (chooser_entry->model,
chooser_entry->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SAVE);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (chooser_entry->completion_store),
DISPLAY_NAME_COLUMN, GTK_SORT_ASCENDING);
gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)),
chooser_entry->completion_store);
GTK_TREE_MODEL (chooser_entry->completion_store));
}
/* Callback when the current folder finishes loading */
@@ -637,7 +656,7 @@ finished_loading_cb (GtkFileSystemModel *model,
completion = gtk_entry_get_completion (GTK_ENTRY (chooser_entry));
update_inline_completion (chooser_entry);
if (gtk_widget_has_focus (GTK_WIDGET (chooser_entry)))
if (gtk_widget_has_focus (GTK_WIDGET (gtk_entry_get_text_widget (GTK_ENTRY (chooser_entry)))))
{
gtk_entry_completion_complete (completion);
gtk_entry_completion_insert_prefix (completion);
@@ -658,11 +677,7 @@ set_completion_folder (GtkFileChooserEntry *chooser_entry,
return;
}
if (chooser_entry->current_folder_file)
{
g_object_unref (chooser_entry->current_folder_file);
chooser_entry->current_folder_file = NULL;
}
g_clear_object (&chooser_entry->current_folder_file);
g_free (chooser_entry->dir_part);
chooser_entry->dir_part = g_strdup (dir_part);
@@ -710,7 +725,7 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry)
g_free (dir_part);
if (chooser_entry->completion_store &&
if (chooser_entry->model &&
(g_strcmp0 (old_file_part, chooser_entry->file_part) != 0))
{
GtkFileFilter *filter;
@@ -720,8 +735,7 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry)
pattern = g_strconcat (chooser_entry->file_part, "*", NULL);
gtk_file_filter_add_pattern (filter, pattern);
_gtk_file_system_model_set_filter (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
filter);
_gtk_file_system_model_set_filter (chooser_entry->model, filter);
g_free (pattern);
g_object_unref (filter);
@@ -940,8 +954,8 @@ _gtk_file_chooser_entry_set_action (GtkFileChooserEntry *chooser_entry,
break;
}
if (chooser_entry->completion_store)
_gtk_file_system_model_set_show_files (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
if (chooser_entry->model)
_gtk_file_system_model_set_show_files (chooser_entry->model,
action == GTK_FILE_CHOOSER_ACTION_OPEN ||
action == GTK_FILE_CHOOSER_ACTION_SAVE);
@@ -971,17 +985,14 @@ gboolean
_gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry,
GFile *file)
{
GtkTreeIter iter;
GFileInfo *info;
if (chooser_entry->completion_store == NULL ||
!_gtk_file_system_model_get_iter_for_file (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
&iter,
file))
if (chooser_entry->model == NULL)
return FALSE;
info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
&iter);
info = _gtk_file_system_model_get_info_for_file (chooser_entry->model, file);
if (!info)
return FALSE;
return _gtk_file_info_consider_as_directory (info);
}

View File

@@ -479,3 +479,12 @@ _gtk_file_info_get_icon (GFileInfo *info,
icon = g_themed_icon_new ("text-x-generic");
return icon;
}
GFile *
_gtk_file_info_get_file (GFileInfo *info)
{
g_assert (G_IS_FILE_INFO (info));
g_assert (g_file_info_has_attribute (info, "standard::file"));
return G_FILE (g_file_info_get_attribute_object (info, "standard::file"));
}

View File

@@ -58,6 +58,8 @@ GIcon * _gtk_file_info_get_icon (GFileInfo *info,
int scale,
GtkIconTheme *icon_theme);
GFile * _gtk_file_info_get_file (GFileInfo *info);
G_END_DECLS
#endif /* __GTK_FILE_CHOOSER_UTILS_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@
#include <glib.h>
#include "gtkfilechooserwidget.h"
#include "gtkselectionmodel.h"
G_BEGIN_DECLS
@@ -36,6 +37,12 @@ gtk_file_chooser_widget_should_respond (GtkFileChooserWidget *chooser);
void
gtk_file_chooser_widget_initial_focus (GtkFileChooserWidget *chooser);
GSList *
gtk_file_chooser_widget_get_selected_files (GtkFileChooserWidget *impl);
GtkSelectionModel *
gtk_file_chooser_widget_get_selection_model (GtkFileChooserWidget *chooser);
G_END_DECLS
#endif /* __GTK_FILE_CHOOSER_WIDGET_PRIVATE_H__ */

914
gtk/gtkfiledialog.c Normal file
View File

@@ -0,0 +1,914 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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 "config.h"
#include "gtkfiledialog.h"
#include "gtk/gtkchoice.h"
#include "gtkfilechooserdialog.h"
#include <glib/gi18n-lib.h>
/**
* GtkFileDialog:
*
* A `GtkFileDialog` object collects the arguments that
* are needed to present a file chooser dialog to the
* user, such as a title for the dialog and whether it
* should be modal.
*
* The dialog is shown with the [function@Gtk.FileDialog.choose_rgba]
* function. This API follows the GIO async pattern, and the
* result can be obtained by calling
* [function@Gtk.FileDialog.choose_rgba_finish].
*
* See [class@Gtk.FileDialogButton] for a convenient control
* that uses `GtkFileDialog` and presents the results.
*
* `GtkFileDialog was added in GTK 4.10.
*/
/* {{{ GObject implementation */
struct _GtkFileDialog
{
GObject parent_instance;
char *title;
unsigned int modal : 1;
unsigned int select_multiple : 1;
unsigned int create_folders : 1;
GListModel *filters;
GListModel *shortcuts;
GListModel *choices;
};
enum
{
PROP_TITLE = 1,
PROP_MODAL,
PROP_SELECT_MULTIPLE,
PROP_CREATE_FOLDERS,
PROP_FILTERS,
PROP_SHORTCUTS,
PROP_CHOICES,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkFileDialog, gtk_file_dialog, G_TYPE_OBJECT)
static void
gtk_file_dialog_init (GtkFileDialog *self)
{
self->title = g_strdup (_("Pick a File"));
self->modal = TRUE;
self->create_folders = TRUE;
}
static void
gtk_file_dialog_finalize (GObject *object)
{
GtkFileDialog *self = GTK_FILE_DIALOG (object);
g_free (self->title);
g_clear_object (&self->filters);
g_clear_object (&self->shortcuts);
g_clear_object (&self->choices);
G_OBJECT_CLASS (gtk_file_dialog_parent_class)->finalize (object);
}
static void
gtk_file_dialog_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
GtkFileDialog *self = GTK_FILE_DIALOG (object);
switch (property_id)
{
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_SELECT_MULTIPLE:
g_value_set_boolean (value, self->select_multiple);
break;
case PROP_CREATE_FOLDERS:
g_value_set_boolean (value, self->create_folders);
break;
case PROP_FILTERS:
g_value_set_object (value, self->filters);
break;
case PROP_SHORTCUTS:
g_value_set_object (value, self->shortcuts);
break;
case PROP_CHOICES:
g_value_set_object (value, self->choices);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_file_dialog_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFileDialog *self = GTK_FILE_DIALOG (object);
switch (prop_id)
{
case PROP_TITLE:
gtk_file_dialog_set_title (self, g_value_get_string (value));
break;
case PROP_MODAL:
gtk_file_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_SELECT_MULTIPLE:
gtk_file_dialog_set_select_multiple (self, g_value_get_boolean (value));
break;
case PROP_CREATE_FOLDERS:
gtk_file_dialog_set_create_folders (self, g_value_get_boolean (value));
break;
case PROP_FILTERS:
gtk_file_dialog_set_filters (self, g_value_get_object (value));
break;
case PROP_SHORTCUTS:
gtk_file_dialog_set_shortcuts (self, g_value_get_object (value));
break;
case PROP_CHOICES:
gtk_file_dialog_set_choices (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_file_dialog_class_init (GtkFileDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_file_dialog_finalize;
object_class->get_property = gtk_file_dialog_get_property;
object_class->set_property = gtk_file_dialog_set_property;
/**
* GtkFileDialog:title: (attributes org.gtk.Property.get=gtk_file_dialog_get_title org.gtk.Property.set=gtk_color_dialog_set_title)
*
* A title that may be shown on the file chooser
* dialog that is presented by [function@Gtk.FileDialog.choose_rgba].
*
* Since: 4.10
*/
properties[PROP_TITLE] =
g_param_spec_string ("title", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFileDialog:modal: (attributes org.gtk.Property.get=gtk_file_dialog_get_modal org.gtk.Property.set=gtk_color_dialog_set_modal)
*
* Whether the file chooser dialog is modal.
*
* Since: 4.10
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFileDialog:select-multiple: (attributes org.gtk.Property.get=gtk_file_dialog_get_select_multiple org.gtk.Property.set=gtk_color_dialog_set_select_multiple)
*
* Whether the file chooser dialog allows to select more than one file.
*
* Since: 4.10
*/
properties[PROP_SELECT_MULTIPLE] =
g_param_spec_boolean ("select-multiple", NULL, NULL,
FALSE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFileDialog:create-folders: (attributes org.gtk.Property.get=gtk_file_dialog_get_create_folders org.gtk.Property.set=gtk_color_dialog_set_create_folders)
*
* Whether the file chooser dialog will allow to create new folders.
*
* Since: 4.10
*/
properties[PROP_CREATE_FOLDERS] =
g_param_spec_boolean ("create-folders", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_FILTERS] =
g_param_spec_object ("filters", NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_SHORTCUTS] =
g_param_spec_object ("shortcuts", NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_CHOICES] =
g_param_spec_object ("choices", NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ Utilities */
static void
file_chooser_set_filters (GtkFileChooser *chooser,
GListModel *filters)
{
if (!filters)
return;
for (unsigned int i = 0; i < g_list_model_get_n_items (filters); i++)
{
GtkFileFilter *filter = g_list_model_get_item (filters, i);
gtk_file_chooser_add_filter (chooser, filter);
g_object_unref (filter);
}
}
static void
file_chooser_set_shortcuts (GtkFileChooser *chooser,
GListModel *shortcuts)
{
if (!shortcuts)
return;
for (unsigned int i = 0; i < g_list_model_get_n_items (shortcuts); i++)
{
GFile *shortcut = g_list_model_get_item (shortcuts, i);
gtk_file_chooser_add_shortcut_folder (chooser, shortcut, NULL);
g_object_unref (shortcut);
}
}
static void
file_chooser_set_choices (GtkFileChooser *chooser,
GListModel *choices)
{
if (!choices)
return;
for (unsigned int i = 0; i < g_list_model_get_n_items (choices); i++)
{
GtkChoice *choice = g_list_model_get_item (choices, i);
char *id;
const char *label;
char **options;
const char * const *option_labels;
label = gtk_choice_get_label (choice);
option_labels = gtk_choice_get_options (choice);
id = g_strdup_printf ("choice %u", i);
if (option_labels)
{
GStrvBuilder *strv = g_strv_builder_new ();
for (unsigned int j = 0; option_labels[j]; j++)
{
char *option = g_strdup_printf ("option %u", j);
g_strv_builder_add (strv, option);
g_free (option);
}
options = g_strv_builder_end (strv);
}
else
options = NULL;
gtk_file_chooser_add_choice (chooser, id, label, (const char **)options, (const char **)option_labels);
g_free (id);
g_strfreev (options);
g_object_unref (choice);
}
}
static char **
file_chooser_get_options (GtkFileChooser *chooser,
GListModel *choices)
{
GStrvBuilder *strv;
if (!choices)
return NULL;
strv = g_strv_builder_new ();
for (unsigned int i = 0; i < g_list_model_get_n_items (choices); i++)
{
GtkChoice *choice = g_list_model_get_item (choices, i);
char *id;
const char *option;
unsigned int pos;
const char *option_label;
id = g_strdup_printf ("choice %u", i);
option = gtk_file_chooser_get_choice (chooser, id);
pos = (unsigned int) g_ascii_strtoull (option + strlen ("option "), NULL, 10);
option_label = gtk_choice_get_options (choice)[pos];
g_strv_builder_add (strv, option_label);
g_free (id);
g_object_unref (choice);
}
return g_strv_builder_end (strv);
}
/* }}} */
/* {{{ Public API */
/* {{{ Constructor */
/**
* gtk_file_dialog_new:
*
* Creates a new `GtkFileDialog` object.
*
* Returns: the new `GtkFileDialog`
*
* Since: 4.10
*/
GtkFileDialog *
gtk_file_dialog_new (void)
{
return g_object_new (GTK_TYPE_FILE_DIALOG, NULL);
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_file_dialog_get_title:
* @self: a `GtkFileDialog`
*
* Returns the title that will be shown on the
* file chooser dialog.
*
* Returns: the title
*
* Since: 4.10
*/
const char *
gtk_file_dialog_get_title (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), NULL);
return self->title;
}
/**
* gtk_file_dialog_set_title:
* @self: a `GtkFileDialog`
* @title: the new title
*
* Sets the title that will be shown on the
* file chooser dialog.
*
* Since: 4.10
*/
void
gtk_file_dialog_set_title (GtkFileDialog *self,
const char *title)
{
char *new_title;
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
g_return_if_fail (title != NULL);
if (g_strcmp0 (self->title, title) == 0)
return;
new_title = g_strdup (title);
g_free (self->title);
self->title = new_title;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_file_dialog_get_modal:
* @self: a `GtkFileDialog`
*
* Returns whether the file chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Returns: `TRUE` if the file chooser dialog is modal
*
* Since: 4.10
*/
gboolean
gtk_file_dialog_get_modal (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_file_dialog_set_modal:
* @self: a `GtkFileDialog`
* @modal: the new value
*
* Sets whether the file chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Since: 4.10
*/
void
gtk_file_dialog_set_modal (GtkFileDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_file_dialog_get_select_multiple:
* @self: a `GtkFileDialog`
*
* Returns whether the file chooser dialog
* allows to select multiple files.
*
* Returns: `TRUE` if the file chooser dialog allows multi-selection
*
* Since: 4.10
*/
gboolean
gtk_file_dialog_get_select_multiple (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), FALSE);
return self->select_multiple;
}
/**
* gtk_file_dialog_set_select_multiple:
* @self: a `GtkFileDialog`
* @modal: the new value
*
* Sets whether the file chooser dialog
* allows to select multiple files.
*
* Since: 4.10
*/
void
gtk_file_dialog_set_select_multiple (GtkFileDialog *self,
gboolean select_multiple)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
if (self->select_multiple == select_multiple)
return;
self->select_multiple = select_multiple;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECT_MULTIPLE]);
}
/**
* gtk_file_dialog_get_create_folders:
* @self: a `GtkFileDialog`
*
* Returns whether the file chooser dialog
* allows to create folders.
*
* Returns: `TRUE` if the file chooser dialog allows folder creation
*
* Since: 4.10
*/
gboolean
gtk_file_dialog_get_create_folders (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), FALSE);
return self->create_folders;
}
/**
* gtk_file_dialog_set_create_folders:
* @self: a `GtkFileDialog`
* @modal: the new value
*
* Sets whether the file chooser dialog
* allows to create folders.
*
* Since: 4.10
*/
void
gtk_file_dialog_set_create_folders (GtkFileDialog *self,
gboolean create_folders)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
if (self->create_folders == create_folders)
return;
self->create_folders = create_folders;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CREATE_FOLDERS]);
}
void
gtk_file_dialog_set_filters (GtkFileDialog *self,
GListModel *filters)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
g_return_if_fail (G_IS_LIST_MODEL (filters));
g_return_if_fail (g_list_model_get_item_type (G_LIST_MODEL (filters)) == GTK_TYPE_FILE_FILTER);
if (!g_set_object (&self->filters, filters))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILTERS]);
}
GListModel *
gtk_file_dialog_get_filters (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), NULL);
return self->filters;
}
void
gtk_file_dialog_set_shortcuts (GtkFileDialog *self,
GListModel *shortcuts)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
g_return_if_fail (G_IS_LIST_MODEL (shortcuts));
g_return_if_fail (g_list_model_get_item_type (G_LIST_MODEL (shortcuts)) == G_TYPE_FILE);
if (!g_set_object (&self->shortcuts, shortcuts))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHORTCUTS]);
}
GListModel *
gtk_file_dialog_get_shortcuts (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), NULL);
return self->shortcuts;
}
void
gtk_file_dialog_set_choices (GtkFileDialog *self,
GListModel *choices)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
g_return_if_fail (G_IS_LIST_MODEL (choices));
g_return_if_fail (g_list_model_get_item_type (G_LIST_MODEL (choices)) == GTK_TYPE_CHOICE);
if (!g_set_object (&self->choices, choices))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHOICES]);
}
GListModel *
gtk_file_dialog_get_choices (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), NULL);
return self->choices;
}
/* }}} */
/* {{{ Async API */
typedef struct
{
GListModel *files;
char **options;
} FileResult;
static void response_cb (GTask *task,
int response);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
response_cb (task, GTK_RESPONSE_CANCEL);
}
static void
response_cb (GTask *task,
int response)
{
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response == GTK_RESPONSE_OK)
{
GtkFileDialog *self;
GtkFileChooserDialog *window;
FileResult file_result;
self = GTK_FILE_DIALOG (g_task_get_source_object (task));
window = GTK_FILE_CHOOSER_DIALOG (g_task_get_task_data (task));
file_result.files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (window));
file_result.options = file_chooser_get_options (GTK_FILE_CHOOSER (window),
gtk_file_dialog_get_choices (self));
g_task_return_pointer (task, &file_result, NULL);
}
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
g_object_unref (task);
}
static void
dialog_response (GtkDialog *dialog,
int response,
GTask *task)
{
response_cb (task, response);
}
static GtkFileChooserDialog *
create_file_chooser_dialog (GtkFileDialog *self,
GtkWindow *parent,
GtkFileChooserAction action)
{
GtkWidget *window;
const char *accept[] = {
N_("_Open"), N_("_Save"), N_("_Select")
};
window = gtk_file_chooser_dialog_new (self->title, parent,
action,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_(accept[action]), GTK_RESPONSE_OK,
NULL);
gtk_window_set_modal (GTK_WINDOW (window), self->modal);
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (window), self->select_multiple);
gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER (window), self->create_folders);
file_chooser_set_filters (GTK_FILE_CHOOSER (window), self->filters);
file_chooser_set_shortcuts (GTK_FILE_CHOOSER (window), self->shortcuts);
file_chooser_set_choices (GTK_FILE_CHOOSER (window), self->choices);
return GTK_FILE_CHOOSER_DIALOG (window);
}
static gboolean
finish_file_op (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error)
{
FileResult *ret;
ret = g_task_propagate_pointer (G_TASK (result), error);
if (ret)
{
*files = ret->files;
if (options)
*options = ret->options;
else
g_strfreev (ret->options);
return TRUE;
}
return FALSE;
}
/**
* gtk_file_dialog_open:
* @self: a `GtkFileDialog`
* @parent: (nullable): the parent `GtkWindow`
* @initial_file: (nullable): the file to select initially
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function initiates a file selection operation by
* presenting a file chooser dialog to the user.
*
* The @callback will be called when the dialog is dismissed.
* It should call [function@Gtk.FileDialog.open_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_file_dialog_open (GtkFileDialog *self,
GtkWindow *parent,
GFile *initial_file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkFileChooserDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
window = create_file_chooser_dialog (self, parent, GTK_FILE_CHOOSER_ACTION_OPEN);
if (initial_file)
gtk_file_chooser_set_file (GTK_FILE_CHOOSER (window), initial_file, NULL);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_file_dialog_open);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
/**
* gtk_file_dialog_open_finish:
* @self: a `GtkFileDialog`
* @result: a `GAsyncResult`
* @files: (out caller-allocates): return location for the selected files
* @options: (out caller-allocates): return location for choices
* @error: return location for an error
*
* Finishes the [function@Gtk.FileDialog.open] call and
* returns the resulting files as a `GListModel` of `GFiles`.
*
* Returns: `TRUE` if a file was selected. Otherwise,
* `FALSE` is returned and @error is set
*
* Since: 4.10
*/
gboolean
gtk_file_dialog_open_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error)
{
return finish_file_op (self, result, files, options, error);
}
void
gtk_file_dialog_select_folder (GtkFileDialog *self,
GtkWindow *parent,
GFile *current_folder,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkFileChooserDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
window = create_file_chooser_dialog (self, parent, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
if (current_folder)
gtk_file_chooser_set_file (GTK_FILE_CHOOSER (window), current_folder, NULL);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_file_dialog_select_folder);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
gboolean
gtk_file_dialog_select_folder_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error)
{
return finish_file_op (self, result, files, options, error);
}
void
gtk_file_dialog_save (GtkFileDialog *self,
GtkWindow *parent,
GFile *current_folder,
const char *current_name,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkFileChooserDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
window = create_file_chooser_dialog (self, parent, GTK_FILE_CHOOSER_ACTION_SAVE);
if (current_folder)
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (window), current_folder, NULL);
if (current_name)
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (window), current_name);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_file_dialog_save);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
gboolean
gtk_file_dialog_save_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error)
{
return finish_file_op (self, result, files, options, error);
}
/* }}} */
/* }}} */
/* vim:set foldmethod=marker expandtab: */

130
gtk/gtkfiledialog.h Normal file
View File

@@ -0,0 +1,130 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* 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/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
G_BEGIN_DECLS
#define GTK_TYPE_FILE_DIALOG (gtk_file_dialog_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkFileDialog, gtk_file_dialog, GTK, FILE_DIALOG, GObject)
GDK_AVAILABLE_IN_4_10
GtkFileDialog * gtk_file_dialog_new (void);
GDK_AVAILABLE_IN_4_10
const char * gtk_file_dialog_get_title (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_title (GtkFileDialog *self,
const char *title);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_get_modal (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_modal (GtkFileDialog *self,
gboolean modal);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_get_select_multiple (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_select_multiple (GtkFileDialog *self,
gboolean select_multiple);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_get_create_folders (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_create_folders (GtkFileDialog *self,
gboolean create_folders);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_filters (GtkFileDialog *self,
GListModel *filters);
GDK_AVAILABLE_IN_4_10
GListModel * gtk_file_dialog_get_filters (GtkFileDialog *dialog);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_shortcuts (GtkFileDialog *self,
GListModel *filters);
GDK_AVAILABLE_IN_4_10
GListModel * gtk_file_dialog_get_shortcuts (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_choices (GtkFileDialog *self,
GListModel *filters);
GDK_AVAILABLE_IN_4_10
GListModel * gtk_file_dialog_get_choices (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_open (GtkFileDialog *self,
GtkWindow *parent,
GFile *current_file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_open_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_select_folder (GtkFileDialog *self,
GtkWindow *parent,
GFile *current_folder,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_select_folder_finish
(GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_save (GtkFileDialog *dialog,
GtkWindow *parent,
GFile *current_folder,
const char *current_name,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_save_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***choices,
GError **error);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,6 @@
#include <gio/gio.h>
#include <gtk/gtkfilefilter.h>
#include <gtk/deprecated/gtktreemodel.h>
G_BEGIN_DECLS
@@ -33,39 +32,13 @@ typedef struct _GtkFileSystemModel GtkFileSystemModel;
GType _gtk_file_system_model_get_type (void) G_GNUC_CONST;
typedef gboolean (*GtkFileSystemModelGetValue) (GtkFileSystemModel *model,
GFile *file,
GFileInfo *info,
int column,
GValue *value,
gpointer user_data);
GtkFileSystemModel *_gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
gpointer get_data,
guint n_columns,
...);
GtkFileSystemModel *_gtk_file_system_model_new_for_directory(GFile * dir,
const char * attributes,
GtkFileSystemModelGetValue get_func,
gpointer get_data,
guint n_columns,
...);
GtkFileSystemModel *_gtk_file_system_model_new (void);
GtkFileSystemModel *_gtk_file_system_model_new_for_directory(GFile *dir,
const char *attributes);
GFile * _gtk_file_system_model_get_directory (GtkFileSystemModel *model);
GCancellable * _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model);
gboolean _gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model,
GtkTreeIter *iter);
gboolean _gtk_file_system_model_iter_is_filtered_out (GtkFileSystemModel *model,
GtkTreeIter *iter);
GFileInfo * _gtk_file_system_model_get_info (GtkFileSystemModel *model,
GtkTreeIter *iter);
gboolean _gtk_file_system_model_get_iter_for_file(GtkFileSystemModel *model,
GtkTreeIter *iter,
GFileInfo * _gtk_file_system_model_get_info_for_file(GtkFileSystemModel *model,
GFile *file);
GFile * _gtk_file_system_model_get_file (GtkFileSystemModel *model,
GtkTreeIter *iter);
const GValue * _gtk_file_system_model_get_value (GtkFileSystemModel *model,
GtkTreeIter * iter,
int column);
void _gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model,
GFile *file,
@@ -88,8 +61,6 @@ void _gtk_file_system_model_set_show_files (GtkFileSystemModel
gboolean show_files);
void _gtk_file_system_model_set_filter_folders (GtkFileSystemModel *model,
gboolean show_folders);
void _gtk_file_system_model_clear_cache (GtkFileSystemModel *model,
int column);
void _gtk_file_system_model_set_filter (GtkFileSystemModel *model,
GtkFileFilter *filter);

255
gtk/gtkfilethumbnail.c Normal file
View File

@@ -0,0 +1,255 @@
/* gtkfilethumbnail.c
*
* Copyright 2022 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This file 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 3 of the
* License, or (at your option) any later version.
*
* This file 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 program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "config.h"
#include "gtkfilethumbnail.h"
#include "gtkbinlayout.h"
#include "gtkfilechooserutils.h"
#include "gtkimage.h"
#include "gtkprivate.h"
#include "gtkwidget.h"
#define ICON_SIZE 16
struct _GtkFileThumbnail
{
GtkWidget parent;
GtkWidget *image;
GCancellable *cancellable;
GFileInfo *info;
};
typedef struct
{
GtkWidgetClass parent;
} GtkFileThumbnailClass;
G_DEFINE_FINAL_TYPE (GtkFileThumbnail, _gtk_file_thumbnail, GTK_TYPE_WIDGET)
enum {
PROP_0,
PROP_INFO,
N_PROPS,
};
static GParamSpec *properties [N_PROPS];
static void
copy_attribute (GFileInfo *to,
GFileInfo *from,
const char *attribute)
{
GFileAttributeType type;
gpointer value;
if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
g_file_info_set_attribute (to, attribute, type, value);
}
static gboolean
update_image (GtkFileThumbnail *self)
{
GtkIconTheme *icon_theme;
GIcon *icon;
int scale;
if (!g_file_info_has_attribute (self->info, G_FILE_ATTRIBUTE_STANDARD_ICON))
return FALSE;
scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (self)));
icon = _gtk_file_info_get_icon (self->info, ICON_SIZE, scale, icon_theme);
gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon);
g_object_unref (icon);
return TRUE;
}
static void
thumbnail_queried_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GtkFileThumbnail *self = user_data; /* might be unreffed if operation was cancelled */
GFile *file = G_FILE (object);
GFileInfo *queried;
queried = g_file_query_info_finish (file, result, NULL);
if (queried == NULL)
return;
copy_attribute (self->info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
copy_attribute (self->info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
copy_attribute (self->info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
update_image (self);
g_clear_object (&queried);
g_clear_object (&self->cancellable);
}
static void
cancel_thumbnail (GtkFileThumbnail *self)
{
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
}
static void
get_thumbnail (GtkFileThumbnail *self)
{
if (!self->info)
return;
if (!update_image (self))
{
GFile *file;
if (g_file_info_has_attribute (self->info, "filechooser::queried"))
return;
g_assert (self->cancellable == NULL);
self->cancellable = g_cancellable_new ();
file = _gtk_file_info_get_file (self->info);
g_file_info_set_attribute_boolean (self->info, "filechooser::queried", TRUE);
g_file_query_info_async (file,
G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
G_FILE_ATTRIBUTE_STANDARD_ICON,
G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT,
self->cancellable,
thumbnail_queried_cb,
self);
}
}
static void
_gtk_file_thumbnail_dispose (GObject *object)
{
GtkFileThumbnail *self = (GtkFileThumbnail *)object;
_gtk_file_thumbnail_set_info (self, NULL);
g_clear_pointer (&self->image, gtk_widget_unparent);
G_OBJECT_CLASS (_gtk_file_thumbnail_parent_class)->dispose (object);
}
static void
_gtk_file_thumbnail_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkFileThumbnail *self = GTK_FILE_THUMBNAIL (object);
switch (prop_id)
{
case PROP_INFO:
g_value_set_object (value, self->info);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
_gtk_file_thumbnail_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFileThumbnail *self = GTK_FILE_THUMBNAIL (object);
switch (prop_id)
{
case PROP_INFO:
_gtk_file_thumbnail_set_info (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
_gtk_file_thumbnail_class_init (GtkFileThumbnailClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = _gtk_file_thumbnail_dispose;
object_class->get_property = _gtk_file_thumbnail_get_property;
object_class->set_property = _gtk_file_thumbnail_set_property;
properties[PROP_INFO] =
g_param_spec_object ("file-info", NULL, NULL,
G_TYPE_FILE_INFO,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, properties);
gtk_widget_class_set_css_name (widget_class, I_("filethumbnail"));
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
}
static void
_gtk_file_thumbnail_init (GtkFileThumbnail *self)
{
self->image = gtk_image_new ();
gtk_widget_set_parent (self->image, GTK_WIDGET (self));
}
GFileInfo *
_gtk_file_thumbnail_get_info (GtkFileThumbnail *self)
{
g_assert (GTK_IS_FILE_THUMBNAIL (self));
return self->info;
}
void
_gtk_file_thumbnail_set_info (GtkFileThumbnail *self,
GFileInfo *info)
{
g_assert (GTK_IS_FILE_THUMBNAIL (self));
g_assert (info == NULL || G_IS_FILE_INFO (info));
if (g_set_object (&self->info, info))
{
cancel_thumbnail (self);
get_thumbnail (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INFO]);
}
}

46
gtk/gtkfilethumbnail.h Normal file
View File

@@ -0,0 +1,46 @@
/* gtkfilethumbnail.h
*
* Copyright 2022 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This file 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 3 of the
* License, or (at your option) any later version.
*
* This file 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 program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef __GTK_FILE_THUMBNAIL_H__
#define __GTK_FILE_THUMBNAIL_H__
#include <gio/gio.h>
#include "gtkfilesystemmodel.h"
G_BEGIN_DECLS
#define GTK_TYPE_FILE_THUMBNAIL (_gtk_file_thumbnail_get_type ())
#define GTK_FILE_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_THUMBNAIL, GtkFileThumbnail))
#define GTK_IS_FILE_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_THUMBNAIL))
typedef struct _GtkFileThumbnail GtkFileThumbnail;
GType _gtk_file_thumbnail_get_type (void) G_GNUC_CONST;
GFileInfo *_gtk_file_thumbnail_get_info (GtkFileThumbnail *self);
void _gtk_file_thumbnail_set_info (GtkFileThumbnail *self,
GFileInfo *info);
G_END_DECLS
#endif /* __GTK_FILE_THUMBNAIL_H__ */

View File

@@ -1870,9 +1870,7 @@ find_affected_text (GtkFontChooserWidget *fontchooser,
hb_ot_layout_table_find_script (hb_face, HB_OT_TAG_GSUB, script_tag, &script_index);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
hb_ot_layout_script_find_language (hb_face, HB_OT_TAG_GSUB, script_index, lang_tag, &lang_index);
G_GNUC_END_IGNORE_DEPRECATIONS
hb_ot_layout_script_select_language (hb_face, HB_OT_TAG_GSUB, script_index, 1, &lang_tag, &lang_index);
if (hb_ot_layout_language_find_feature (hb_face,
HB_OT_TAG_GSUB,
@@ -2013,9 +2011,7 @@ update_feature_label (GtkFontChooserWidget *fontchooser,
hb_ot_layout_table_find_script (hb_face, HB_OT_TAG_GSUB, script_tag, &script_index);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
hb_ot_layout_script_find_language (hb_face, HB_OT_TAG_GSUB, script_index, lang_tag, &lang_index);
G_GNUC_END_IGNORE_DEPRECATIONS
hb_ot_layout_script_select_language (hb_face, HB_OT_TAG_GSUB, script_index, 1, &lang_tag, &lang_index);
if (hb_ot_layout_language_find_feature (hb_face, HB_OT_TAG_GSUB, script_index, lang_index, item->tag, &feature_index))
{
@@ -2565,9 +2561,7 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
{
hb_ot_layout_table_find_script (hb_face, table[i], script_tag, &script_index);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
hb_ot_layout_script_find_language (hb_face, table[i], script_index, lang_tag, &lang_index);
G_GNUC_END_IGNORE_DEPRECATIONS
hb_ot_layout_script_select_language (hb_face, table[i], script_index, 1, &lang_tag, &lang_index);
feat = features + n_features;
count = G_N_ELEMENTS (features) - n_features;

671
gtk/gtkfontdialog.c Normal file
View File

@@ -0,0 +1,671 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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 "config.h"
#include "gtkfontdialog.h"
#include "gtkfontchooserdialog.h"
#include "gtkbutton.h"
#include "gtktypebuiltins.h"
#include <glib/gi18n-lib.h>
/**
* GtkFontDialog:
*
* A `GtkFontDialog` object collects the arguments that
* are needed to present a font chooser dialog to the
* user, such as a title for the dialog and whether it
* should be modal.
*
* The dialog is shown with the [function@Gtk.FontDialog.choose_font]
* function. This API follows the GIO async pattern, and the
* result can be obtained by calling
* [function@Gtk.FontDialog.choose_font_finish].
*
* See [class@Gtk.FontDialogButton] for a convenient control
* that uses `GtkFontDialog` and presents the results.
*
* `GtkFontDialog was added in GTK 4.10.
*/
/* {{{ GObject implementation */
struct _GtkFontDialog
{
GObject parent_instance;
char *title;
GtkFontChooserLevel level;
PangoLanguage *language;
PangoFontMap *fontmap;
unsigned int modal : 1;
GtkFontFilterFunc filter;
gpointer filter_data;
GDestroyNotify filter_data_destroy;
};
enum
{
PROP_TITLE = 1,
PROP_MODAL,
PROP_LEVEL,
PROP_LANGUAGE,
PROP_FONTMAP,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkFontDialog, gtk_font_dialog, G_TYPE_OBJECT)
#define DEFAULT_LEVEL (GTK_FONT_CHOOSER_LEVEL_FAMILY| \
GTK_FONT_CHOOSER_LEVEL_STYLE| \
GTK_FONT_CHOOSER_LEVEL_SIZE)
static void
gtk_font_dialog_init (GtkFontDialog *self)
{
self->title = g_strdup (_("Pick a Font"));
self->modal = TRUE;
self->level = DEFAULT_LEVEL;
self->language = pango_language_get_default ();
}
static void
gtk_font_dialog_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkFontDialog *self = GTK_FONT_DIALOG (object);
switch (property_id)
{
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_LEVEL:
g_value_set_flags (value, self->level);
break;
case PROP_LANGUAGE:
g_value_set_boxed (value, self->language);
break;
case PROP_FONTMAP:
g_value_set_object (value, self->fontmap);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_font_dialog_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFontDialog *self = GTK_FONT_DIALOG (object);
switch (property_id)
{
case PROP_TITLE:
gtk_font_dialog_set_title (self, g_value_get_string (value));
break;
case PROP_MODAL:
gtk_font_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_LEVEL:
gtk_font_dialog_set_level (self, g_value_get_flags (value));
break;
case PROP_LANGUAGE:
gtk_font_dialog_set_language (self, g_value_get_boxed (value));
break;
case PROP_FONTMAP:
gtk_font_dialog_set_fontmap (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_font_dialog_finalize (GObject *object)
{
GtkFontDialog *self = GTK_FONT_DIALOG (object);
g_free (self->title);
g_clear_object (&self->fontmap);
g_clear_pointer (&self->filter_data, self->filter_data_destroy);
G_OBJECT_CLASS (gtk_font_dialog_parent_class)->finalize (object);
}
static void
gtk_font_dialog_class_init (GtkFontDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->get_property = gtk_font_dialog_get_property;
object_class->set_property = gtk_font_dialog_set_property;
object_class->finalize = gtk_font_dialog_finalize;
/**
* GtkFontDialog:title: (attributes org.gtk.Property.get=gtk_font_dialog_get_title org.gtk.Property.set=gtk_font_dialog_set_title)
*
* A title that may be shown on the font chooser
* dialog that is presented by [function@Gtk.FontDialog.choose_font].
*
* Since: 4.10
*/
properties[PROP_TITLE] =
g_param_spec_string ("title", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialog:modal: (attributes org.gtk.Property.get=gtk_font_dialog_get_modal org.gtk.Property.set=gtk_font_dialog_set_modal)
*
* Whether the font chooser dialog is modal.
*
* Since: 4.10
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialog:level: (attributes org.gtk.Property.get=gtk_font_dialog_get_level org.gtk.Property.set=gtk_font_dialog_set_level)
*
* The level of granularity to offer for selecting fonts.
*
* Since: 4.10
*/
properties[PROP_LEVEL] =
g_param_spec_flags ("level", NULL, NULL,
GTK_TYPE_FONT_CHOOSER_LEVEL,
DEFAULT_LEVEL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialog:language: (attributes org.gtk.Property.get=gtk_font_dialog_get_language org.gtk.Property.set=gtk_font_dialog_set_language)
*
* The language for which the font features are selected.
*
* Since: 4.10
*/
properties[PROP_LANGUAGE] =
g_param_spec_boxed ("language", NULL, NULL,
PANGO_TYPE_LANGUAGE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialog:fontmap: (attributes org.gtk.Property.get=gtk_font_dialog_get_fontmap org.gtk.Property.set=gtk_font_dialog_set_fontmap)
*
* Sets a custom font map to select fonts from.
*
* A custom font map can be used to present application-specific
* fonts instead of or in addition to the normal system fonts.
*
* Since: 4.10
*/
properties[PROP_FONTMAP] =
g_param_spec_object ("fontmap", NULL, NULL,
PANGO_TYPE_FONT_MAP,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ Constructor */
/**
* gtk_font_dialog_new:
*
* Creates a new `GtkFontDialog` object.
*
* Returns: the new `GtkFontDialog`
*
* Since: 4.10
*/
GtkFontDialog *
gtk_font_dialog_new (void)
{
return g_object_new (GTK_TYPE_FONT_DIALOG, NULL);
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_font_dialog_get_title:
* @self: a `GtkFontDialog`
*
* Returns the title that will be shown on the
* font chooser dialog.
*
* Returns: the title
*
* Since: 4.10
*/
const char *
gtk_font_dialog_get_title (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), NULL);
return self->title;
}
/**
* gtk_font_dialog_set_title:
* @self: a `GtkFontDialog`
* @title: the new title
*
* Sets the title that will be shown on the
* font chooser dialog.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_title (GtkFontDialog *self,
const char *title)
{
char *new_title;
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
g_return_if_fail (title != NULL);
if (g_str_equal (self->title, title))
return;
new_title = g_strdup (title);
g_free (self->title);
self->title = new_title;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_font_dialog_get_modal:
* @self: a `GtkFontDialog`
*
* Returns whether the font chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Returns: `TRUE` if the font chooser dialog is modal
*
* Since: 4.10
*/
gboolean
gtk_font_dialog_get_modal (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_font_dialog_set_modal:
* @self: a `GtkFontDialog`
* @modal: the new value
*
* Sets whether the font chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_modal (GtkFontDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_font_dialog_get_level:
* @self: a `GtkFontDialog`
*
* Returns the level of granularity for selecting fonts.
*
* Returns: the level of granularity
*
* Since: 4.10
*/
GtkFontChooserLevel
gtk_font_dialog_get_level (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), DEFAULT_LEVEL);
return self->level;
}
/**
* gtk_font_dialog_set_level:
* @self: a `GtkFontDialog`
* @level: the new value
*
* Sets the level of granularity for selecting fonts.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_level (GtkFontDialog *self,
GtkFontChooserLevel level)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
if (self->level == level)
return;
self->level = level;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LEVEL]);
}
/**
* gtk_font_dialog_get_language:
* @self: a `GtkFontDialog`
*
* Returns the language for which font features are applied.
*
* Returns: (nullable): the language for font features
*
* Since: 4.10
*/
PangoLanguage *
gtk_font_dialog_get_language (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), NULL);
return self->language;
}
/**
* gtk_font_dialog_set_language:
* @self: a `GtkFontDialog`
* @language: the language for font features
*
* Sets the language for which font features are applied.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_language (GtkFontDialog *self,
PangoLanguage *language)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
if (self->language == language)
return;
self->language = language;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]);
}
/**
* gtk_font_dialog_get_fontmap
* @self: a `GtkFontDialog`
*
* Returns the fontmap from which fonts are selected,
* or `NULL` for the default fontmap.
*
* Returns: (nullable) (transfer none): the fontmap
*
* Since: 4.10
*/
PangoFontMap *
gtk_font_dialog_get_fontmap (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), NULL);
return self->fontmap;
}
/**
* gtk_font_dialog_set_fontmap:
* @self: a `GtkFontDialog`
* @fontmap: (nullable): the fontmap
*
* Sets the fontmap from which fonts are selected.
*
* If @fontmap is `NULL`, the default fontmap is used.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_fontmap (GtkFontDialog *self,
PangoFontMap *fontmap)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
if (g_set_object (&self->fontmap, fontmap))
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FONTMAP]);
}
/**
* gtk_font_dialog_set_filter:
* @self: a `GtkFontDialog`
* @filter: (nullable): a `GtkFontFilterFunc`
* @user_data: (closure): data to pass to @filter
* @destroy: function to call to free @data when it is no longer needed
*
* Adds a filter function that decides which fonts to display
* in the font chooser dialog.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_filter (GtkFontDialog *self,
GtkFontFilterFunc filter,
gpointer filter_data,
GDestroyNotify filter_data_destroy)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
g_clear_pointer (&self->filter_data, self->filter_data_destroy);
self->filter = filter;
self->filter_data = filter_data;
self->filter_data_destroy = filter_data_destroy;
}
/* }}} */
/* {{{ Async API */
static void response_cb (GTask *task,
int response);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
response_cb (task, GTK_RESPONSE_CANCEL);
}
typedef struct
{
PangoFontDescription *font_desc;
char *font_features;
} FontResult;
static void
response_cb (GTask *task,
int response)
{
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response == GTK_RESPONSE_OK)
{
GtkFontChooserDialog *window;
FontResult font_result;
window = GTK_FONT_CHOOSER_DIALOG (g_task_get_task_data (task));
font_result.font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (window));
font_result.font_features = gtk_font_chooser_get_font_features (GTK_FONT_CHOOSER (window));
g_task_return_pointer (task, &font_result, NULL);
}
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
g_object_unref (task);
}
static void
dialog_response (GtkDialog *dialog,
int response,
GTask *task)
{
response_cb (task, response);
}
/**
* gtk_font_dialog_choose_font:
* @self: a `GtkFontDialog`
* @parent: (nullable): the parent `GtkWindow`
* @initial_font: (nullable): the font to select initially
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function initiates a font selection operation by
* presenting a font chooser dialog to the user.
*
* The @callback will be called when the dialog is dismissed.
* It should call [function@Gtk.FontDialog.choose_font_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_font_dialog_choose_font (GtkFontDialog *self,
GtkWindow *parent,
PangoFontDescription *initial_font,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkFontChooserDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
window = GTK_FONT_CHOOSER_DIALOG (gtk_font_chooser_dialog_new (self->title, parent));
gtk_font_chooser_set_level (GTK_FONT_CHOOSER (window), self->level);
if (self->language)
gtk_font_chooser_set_language (GTK_FONT_CHOOSER (window), pango_language_to_string (self->language));
if (self->fontmap)
gtk_font_chooser_set_font_map (GTK_FONT_CHOOSER (window), self->fontmap);
if (initial_font)
gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (window), initial_font);
if (self->filter)
gtk_font_chooser_set_filter_func (GTK_FONT_CHOOSER (window),
self->filter,
self->filter_data,
self->filter_data_destroy);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_font_dialog_choose_font);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
/**
* gtk_font_dialog_choose_font_finish:
* @self: a `GtkFontDialog`
* @result: a `GAsyncResult`
* @font_desc: (out caller-allocates): return location for font description
* @font_features: (out caller-allocates): return location for font features
* @error: return location for an error
*
* Finishes the [function@Gtk.FontDialog.choose_font] call and
* returns the resulting font description and font features.
*
* Returns: `TRUE` if a font was selected. Otherwise,
* `FALSE` is returned and @error is set
*
* Since: 4.10
*/
gboolean
gtk_font_dialog_choose_font_finish (GtkFontDialog *self,
GAsyncResult *result,
PangoFontDescription **font_desc,
char **font_features,
GError **error)
{
FontResult *font_result;
font_result = g_task_propagate_pointer (G_TASK (result), error);
if (font_result)
{
*font_desc = font_result->font_desc;
*font_features = font_result->font_features;
return TRUE;
}
return FALSE;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

97
gtk/gtkfontdialog.h Normal file
View File

@@ -0,0 +1,97 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* 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/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkfontchooser.h>
G_BEGIN_DECLS
#define GTK_TYPE_FONT_DIALOG (gtk_font_dialog_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkFontDialog, gtk_font_dialog, GTK, FONT_DIALOG, GObject)
GDK_AVAILABLE_IN_4_10
GtkFontDialog * gtk_font_dialog_new (void);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_title (GtkFontDialog *self,
const char *title);
GDK_AVAILABLE_IN_4_10
gboolean gtk_font_dialog_get_modal (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_modal (GtkFontDialog *self,
gboolean modal);
GDK_AVAILABLE_IN_4_10
const char * gtk_font_dialog_get_title (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_level (GtkFontDialog *self,
GtkFontChooserLevel level);
GDK_AVAILABLE_IN_4_10
GtkFontChooserLevel
gtk_font_dialog_get_level (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_language (GtkFontDialog *self,
PangoLanguage *language);
GDK_AVAILABLE_IN_4_10
PangoLanguage * gtk_font_dialog_get_language (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
PangoFontMap * gtk_font_dialog_get_fontmap (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_fontmap (GtkFontDialog *self,
PangoFontMap *fontmap);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_filter (GtkFontDialog *self,
GtkFontFilterFunc filter,
gpointer user_data,
GDestroyNotify destroy);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_choose_font (GtkFontDialog *self,
GtkWindow *parent,
PangoFontDescription *initial_font,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_font_dialog_choose_font_finish
(GtkFontDialog *self,
GAsyncResult *result,
PangoFontDescription **font_desc,
char **font_features,
GError **error);
G_END_DECLS

778
gtk/gtkfontdialogbutton.c Normal file
View File

@@ -0,0 +1,778 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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 "config.h"
#include "gtkfontdialogbutton.h"
#include "gtkbinlayout.h"
#include "gtkbox.h"
#include "gtkseparator.h"
#include "gtkbutton.h"
#include "gtklabel.h"
#include <glib/gi18n-lib.h>
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkwidgetprivate.h"
static void button_clicked (GtkFontDialogButton *self);
/**
* GtkFontDialogButton:
*
* The `GtkFontDialogButton` is wrapped around a [class@Gtk.FontDialog]
* and allows to open a font chooser dialog to change the font.
*
* ![An example GtkFontDialogButton](font-button.png)
*
* It is suitable widget for selecting a font in a preference dialog.
*
* # CSS nodes
*
* ```
* fontbutton
* ╰── button.font
* ╰── [content]
* ```
*
* `GtkFontDialogButton` has a single CSS node with name fontbutton which
* contains a button node with the .font style class.
*/
/* {{{ GObject implementation */
struct _GtkFontDialogButton
{
GtkWidget parent_instance;
GtkWidget *button;
GtkWidget *font_label;
GtkWidget *size_label;
GtkWidget *font_size_box;
guint use_font : 1;
guint use_size : 1;
GtkFontDialog *dialog;
PangoFontDescription *font_desc;
char *font_features;
PangoFontFamily *font_family;
PangoFontFace *font_face;
};
/* Properties */
enum
{
PROP_DIALOG = 1,
PROP_FONT_DESC,
PROP_FONT_FEATURES,
PROP_USE_FONT,
PROP_USE_SIZE,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkFontDialogButton, gtk_font_dialog_button, GTK_TYPE_WIDGET)
static void
gtk_font_dialog_button_init (GtkFontDialogButton *self)
{
GtkWidget *box;
PangoFontDescription *font_desc;
self->button = gtk_button_new ();
g_signal_connect_swapped (self->button, "clicked", G_CALLBACK (button_clicked), self);
self->font_label = gtk_label_new (_("Font"));
gtk_widget_set_hexpand (self->font_label, TRUE);
self->size_label = gtk_label_new ("14");
self->font_size_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (box), self->font_label);
gtk_box_append (GTK_BOX (self->font_size_box), gtk_separator_new (GTK_ORIENTATION_VERTICAL));
gtk_box_append (GTK_BOX (self->font_size_box), self->size_label);
gtk_box_append (GTK_BOX (box), self->font_size_box);
gtk_button_set_child (GTK_BUTTON (self->button), box);
gtk_widget_set_parent (self->button, GTK_WIDGET (self));
self->use_font = FALSE;
self->use_size = FALSE;
font_desc = pango_font_description_from_string ("Sans 12");
gtk_font_dialog_button_set_font_desc (self, font_desc);
pango_font_description_free (font_desc);
gtk_widget_add_css_class (self->button, "font");
}
static void
gtk_font_dialog_button_set_property (GObject *object,
unsigned int param_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFontDialogButton *self = GTK_FONT_DIALOG_BUTTON (object);
switch (param_id)
{
case PROP_DIALOG:
gtk_font_dialog_button_set_dialog (self, g_value_get_object (value));
break;
case PROP_FONT_DESC:
gtk_font_dialog_button_set_font_desc (self, g_value_get_boxed (value));
break;
case PROP_FONT_FEATURES:
gtk_font_dialog_button_set_font_features (self, g_value_get_string (value));
break;
case PROP_USE_FONT:
gtk_font_dialog_button_set_use_font (self, g_value_get_boolean (value));
break;
case PROP_USE_SIZE:
gtk_font_dialog_button_set_use_size (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_font_dialog_button_get_property (GObject *object,
unsigned int param_id,
GValue *value,
GParamSpec *pspec)
{
GtkFontDialogButton *self = GTK_FONT_DIALOG_BUTTON (object);
switch (param_id)
{
case PROP_DIALOG:
g_value_set_object (value, self->dialog);
break;
case PROP_FONT_DESC:
g_value_set_boxed (value, self->font_desc);
break;
case PROP_FONT_FEATURES:
g_value_set_string (value, self->font_features);
break;
case PROP_USE_FONT:
g_value_set_boolean (value, self->use_font);
break;
case PROP_USE_SIZE:
g_value_set_boolean (value, self->use_size);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_font_dialog_button_dispose (GObject *object)
{
GtkFontDialogButton *self = GTK_FONT_DIALOG_BUTTON (object);
g_clear_pointer (&self->button, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_font_dialog_button_parent_class)->dispose (object);
}
static void
gtk_font_dialog_button_finalize (GObject *object)
{
GtkFontDialogButton *self = GTK_FONT_DIALOG_BUTTON (object);
g_clear_object (&self->dialog);
pango_font_description_free (self->font_desc);
g_clear_object (&self->font_family);
g_clear_object (&self->font_face);
g_free (self->font_features);
G_OBJECT_CLASS (gtk_font_dialog_button_parent_class)->finalize (object);
}
static void
gtk_font_dialog_button_class_init (GtkFontDialogButtonClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->get_property = gtk_font_dialog_button_get_property;
object_class->set_property = gtk_font_dialog_button_set_property;
object_class->dispose = gtk_font_dialog_button_dispose;
object_class->finalize = gtk_font_dialog_button_finalize;
widget_class->grab_focus = gtk_widget_grab_focus_child;
widget_class->focus = gtk_widget_focus_child;
/**
* GtkFontDialogButton:dialog: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_dialog org.gtk.Property.set=gtk_font_dialog_button_set_dialog)
*
* The `GtkFontDialog` that contains parameters for
* the font chooser dialog.
*
* Since: 4.10
*/
properties[PROP_DIALOG] =
g_param_spec_object ("dialog", NULL, NULL,
GTK_TYPE_FONT_DIALOG,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialogButton:font-desc: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_font_desc org.gtk.Property.set=gtk_font_dialog_button_set_font_desc)
*
* The selected font.
*
* This property can be set to give the button its initial
* font, and it will be updated to reflect the users choice
* in the font chooser dialog.
*
* Listen to `notify::font-desc` to get informed about changes
* to the buttons font.
*
* Since: 4.10
*/
properties[PROP_FONT_DESC] =
g_param_spec_boxed ("font-desc", NULL, NULL,
PANGO_TYPE_FONT_DESCRIPTION,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialogButton:font-features: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_font_features org.gtk.Property.set=gtk_font_dialog_button_set_font_features)
*
* The selected font features.
*
* This property will be updated to reflect the users choice
* in the font chooser dialog.
*
* Listen to `notify::font-features` to get informed about changes
* to the buttons font features.
*
* Since: 4.10
*/
properties[PROP_FONT_FEATURES] =
g_param_spec_string ("font-features", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialogButton:use-font: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_use_font org.gtk.Property.set=gtk_font_dialog_button_set_use_font)
*
* Whether the buttons label will be drawn in the selected font.
*/
properties[PROP_USE_FONT] =
g_param_spec_boolean ("use-font", NULL, NULL,
FALSE,
GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialogButton:use-size: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_use_size org.gtk.Property.set=gtk_font_dialog_button_set_use_size)
*
* Whether the buttons label will use the selected font size.
*/
properties[PROP_USE_SIZE] =
g_param_spec_boolean ("use-size", NULL, NULL,
FALSE,
GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (widget_class, "fontbutton");
}
/* }}} */
/* {{{ Private API, callbacks */
static void
font_chosen (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkFontDialogButton *self = data;
PangoFontDescription *desc;
char *features;
GError *error = NULL;
if (gtk_font_dialog_choose_font_finish (self->dialog, result, &desc, &features, &error))
{
gtk_font_dialog_button_set_font_desc (self, desc);
gtk_font_dialog_button_set_font_features (self, features);
}
else
{
g_print ("%s\n", error->message);
g_error_free (error);
}
}
static void
button_clicked (GtkFontDialogButton *self)
{
GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (self));
GtkWindow *parent = NULL;
if (GTK_IS_WINDOW (root))
parent = GTK_WINDOW (root);
gtk_font_dialog_choose_font (self->dialog, parent, self->font_desc,
NULL, font_chosen, self);
}
static gboolean
font_description_style_equal (const PangoFontDescription *a,
const PangoFontDescription *b)
{
return (pango_font_description_get_weight (a) == pango_font_description_get_weight (b) &&
pango_font_description_get_style (a) == pango_font_description_get_style (b) &&
pango_font_description_get_stretch (a) == pango_font_description_get_stretch (b) &&
pango_font_description_get_variant (a) == pango_font_description_get_variant (b));
}
static void
update_font_data (GtkFontDialogButton *self)
{
PangoFontMap *fontmap = NULL;
const char *family_name;
g_assert (self->font_desc != NULL);
g_clear_object (&self->font_family);
g_clear_object (&self->font_face);
family_name = pango_font_description_get_family (self->font_desc);
if (family_name == NULL)
return;
if (self->dialog)
fontmap = gtk_font_dialog_get_fontmap (self->dialog);
if (!fontmap)
fontmap = pango_cairo_font_map_get_default ();
for (unsigned int i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (fontmap)); i++)
{
PangoFontFamily *family = g_list_model_get_item (G_LIST_MODEL (fontmap), i);
const char *name = pango_font_family_get_name (family);
g_object_unref (family);
if (g_ascii_strcasecmp (name, family_name) == 0)
{
g_set_object (&self->font_family, family);
break;
}
}
for (unsigned i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->font_family)); i++)
{
PangoFontFace *face = g_list_model_get_item (G_LIST_MODEL (self->font_family), i);
PangoFontDescription *tmp_desc = pango_font_face_describe (face);
g_object_unref (face);
if (font_description_style_equal (tmp_desc, self->font_desc))
{
g_set_object (&self->font_face, face);
pango_font_description_free (tmp_desc);
break;
}
else
pango_font_description_free (tmp_desc);
}
}
static void
update_font_info (GtkFontDialogButton *self)
{
const char *fam_name;
const char *face_name;
char *family_style;
GtkFontChooserLevel level;
if (self->font_family)
fam_name = pango_font_family_get_name (self->font_family);
else
fam_name = C_("font", "None");
if (self->font_face)
face_name = pango_font_face_get_face_name (self->font_face);
else
face_name = "";
if (self->dialog)
level = gtk_font_dialog_get_level (self->dialog);
else
level = GTK_FONT_CHOOSER_LEVEL_STYLE|GTK_FONT_CHOOSER_LEVEL_SIZE;
if ((level & GTK_FONT_CHOOSER_LEVEL_STYLE) != 0)
family_style = g_strconcat (fam_name, " ", face_name, NULL);
else
family_style = g_strdup (fam_name);
gtk_label_set_text (GTK_LABEL (self->font_label), family_style);
g_free (family_style);
if ((level & GTK_FONT_CHOOSER_LEVEL_SIZE) != 0)
{
/* mirror Pango, which doesn't translate this either */
char *size = g_strdup_printf ("%2.4g%s",
pango_font_description_get_size (self->font_desc) / (double)PANGO_SCALE,
pango_font_description_get_size_is_absolute (self->font_desc) ? "px" : "");
gtk_label_set_text (GTK_LABEL (self->size_label), size);
g_free (size);
gtk_widget_show (self->font_size_box);
}
else
gtk_widget_hide (self->font_size_box);
}
static void
apply_use_font (GtkFontDialogButton *self)
{
if (!self->use_font)
gtk_label_set_attributes (GTK_LABEL (self->font_label), NULL);
else
{
PangoFontDescription *desc;
PangoAttrList *attrs;
PangoLanguage *language = NULL;
desc = pango_font_description_copy (self->font_desc);
if (!self->use_size)
pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
attrs = pango_attr_list_new ();
/* Prevent font fallback */
pango_attr_list_insert (attrs, pango_attr_fallback_new (FALSE));
/* Force current font and features */
pango_attr_list_insert (attrs, pango_attr_font_desc_new (desc));
if (self->font_features)
pango_attr_list_insert (attrs, pango_attr_font_features_new (self->font_features));
if (self->dialog)
language = gtk_font_dialog_get_language (self->dialog);
if (language)
pango_attr_list_insert (attrs, pango_attr_language_new (language));
gtk_label_set_attributes (GTK_LABEL (self->font_label), attrs);
pango_attr_list_unref (attrs);
pango_font_description_free (desc);
}
}
/* }}} */
/* {{{ Public API */
/* {{{ Constructor */
/**
* gtk_font_dialog_button_new:
* @dialog: (nullable) (transfer full): the `GtkFontDialog` to use
*
* Creates a new `GtkFontDialogButton` with the
* given `GtkFontDialog`.
*
* You can pass `NULL` to this function and set a `GtkFontDialog`
* later. The button will be insensitive until that happens.
*
* Returns: the new `GtkFontDialogButton`
*
* Since: 4.10
*/
GtkWidget *
gtk_font_dialog_button_new (GtkFontDialog *dialog)
{
GtkWidget *self;
g_return_val_if_fail (GTK_IS_FONT_DIALOG (dialog), NULL);
self = g_object_new (GTK_TYPE_FONT_DIALOG_BUTTON,
"dialog", dialog,
NULL);
g_clear_object (&dialog);
return self;
}
/* }}} */
/* {{{ Setters and Getters */
/**
* gtk_font_dialog_button_set_dialog:
* @self: a `GtkFontDialogButton`
* @dialog: the new `GtkFontDialog`
*
* Sets a `GtkFontDialog` object to use for
* creating the font chooser dialog that is
* presented when the user clicks the button.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_dialog (GtkFontDialogButton *self,
GtkFontDialog *dialog)
{
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
g_return_if_fail (dialog == NULL || GTK_IS_FONT_DIALOG (dialog));
if (!g_set_object (&self->dialog, dialog))
return;
gtk_widget_set_sensitive (self->button, dialog != NULL);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DIALOG]);
}
/**
* gtk_font_dialog_button_get_dialog:
* @self: a `GtkFontDialogButton`
*
* Returns the `GtkFontDialog` of @self.
*
* Returns: (nullable) (transfer none): the `GtkFontDialog`
*
* Since: 4.10
*/
GtkFontDialog *
gtk_font_dialog_button_get_dialog (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), NULL);
return self->dialog;
}
/**
* gtk_font_dialog_button_set_font_desc:
* @self: a `GtkFontDialogButton`
* @font_desc: the new font
*
* Sets the font of the button.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_font_desc (GtkFontDialogButton *self,
PangoFontDescription *font_desc)
{
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
g_return_if_fail (font_desc != NULL);
if (self->font_desc == font_desc ||
(self->font_desc && font_desc &&
pango_font_description_equal (self->font_desc, font_desc)))
return;
if (self->font_desc)
pango_font_description_free (self->font_desc);
self->font_desc = pango_font_description_copy (font_desc);
update_font_data (self);
update_font_info (self);
apply_use_font (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FONT_DESC]);
}
/**
* gtk_font_dialog_button_get_font_desc:
* @self: a `GtkFontDialogButton`
*
* Returns the font of the button.
*
* This function is what should be used to obtain
* the font that was choosen by the user. To get
* informed about changes, listen to "notify::font-desc".
*
* Returns: (transfer none) (nullable): the font
*
* Since: 4.10
*/
PangoFontDescription *
gtk_font_dialog_button_get_font_desc (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), NULL);
return self->font_desc;
}
/**
* gtk_font_dialog_button_set_font_features:
* @self: a `GtkFontDialogButton`
* @font_features: (nullable): the font features
*
* Sets the font features of the button.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_font_features (GtkFontDialogButton *self,
const char *font_features)
{
char *new_features;
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
if (g_strcmp0 (self->font_features, font_features) == 0)
return;
new_features = g_strdup (font_features);
g_free (self->font_features);
self->font_features = new_features;
apply_use_font (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FONT_FEATURES]);
}
/**
* gtk_font_dialog_button_get_font_features:
* @self: a `GtkFontDialogButton`
*
* Returns the font features of the button.
*
* This function is what should be used to obtain
* the font features that were choosen by the user.
* To get informed about changes, listen to
* "notify::font-features".
*
* Returns: (transfer none) (nullable): the font features
*
* Since: 4.10
*/
const char *
gtk_font_dialog_button_get_font_features (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), NULL);
return self->font_features;
}
/**
* gtk_font_dialog_button_set_use_font:
* @self: a `GtkFontDialogButton`
* @use_font: If `TRUE`, font name will be written using
* the chosen font
*
* If @use_font is `TRUE`, the font name will be written
* using the selected font.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_use_font (GtkFontDialogButton *self,
gboolean use_font)
{
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
if (self->use_font == use_font)
return;
self->use_font = use_font;
apply_use_font (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USE_FONT]);
}
/**
* gtk_font_dialog_button_get_use_font:
* @self: a `GtkFontDialogButton`
*
* Returns whether the selected font is used in the label.
*
* Returns: whether the selected font is used in the label
*
* Since: 4.10
*/
gboolean
gtk_font_dialog_button_get_use_font (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), FALSE);
return self->use_font;
}
/**
* gtk_font_dialog_button_set_use_size:
* @self: a `GtkFontDialogButton`
* @use_size: If `TRUE`, font name will be written using
* the chosen font size
*
* If @use_size is `TRUE`, the font name will be written
* using the selected font size.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_use_size (GtkFontDialogButton *self,
gboolean use_size)
{
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
if (self->use_size == use_size)
return;
self->use_size = use_size;
apply_use_font (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USE_SIZE]);
}
/**
* gtk_font_dialog_button_get_use_size:
* @self: a `GtkFontDialogButton`
*
* Returns whether the selected font size is used in the label.
*
* Returns: whether the selected font size is used in the label
*
* Since: 4.10
*/
gboolean
gtk_font_dialog_button_get_use_size (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), FALSE);
return self->use_size;
}
/* }}} */
/* }}} */
/* vim:set foldmethod=marker expandtab: */

76
gtk/gtkfontdialogbutton.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkbutton.h>
#include <gtk/gtkfontdialog.h>
G_BEGIN_DECLS
#define GTK_TYPE_FONT_DIALOG_BUTTON (gtk_font_dialog_button_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkFontDialogButton, gtk_font_dialog_button, GTK, FONT_DIALOG_BUTTON, GtkWidget)
GDK_AVAILABLE_IN_4_10
GtkWidget * gtk_font_dialog_button_new (GtkFontDialog *dialog);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_dialog (GtkFontDialogButton *self,
GtkFontDialog *dialog);
GDK_AVAILABLE_IN_4_10
GtkFontDialog * gtk_font_dialog_button_get_dialog (GtkFontDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_font_desc (GtkFontDialogButton *self,
PangoFontDescription *font_desc);
GDK_AVAILABLE_IN_4_10
PangoFontDescription *
gtk_font_dialog_button_get_font_desc (GtkFontDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_font_features
(GtkFontDialogButton *self,
const char *font_features);
GDK_AVAILABLE_IN_4_10
const char * gtk_font_dialog_button_get_font_features (GtkFontDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_use_font (GtkFontDialogButton *self,
gboolean use_font);
GDK_AVAILABLE_IN_4_10
gboolean gtk_font_dialog_button_get_use_font (GtkFontDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_use_size (GtkFontDialogButton *self,
gboolean use_size);
GDK_AVAILABLE_IN_4_10
gboolean gtk_font_dialog_button_get_use_size (GtkFontDialogButton *self);
G_END_DECLS

715
gtk/gtkinfodialog.c Normal file
View File

@@ -0,0 +1,715 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* 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 "config.h"
#include "gtkinfodialog.h"
#include "gtkbutton.h"
#include "gtkmessagedialog.h"
#include <glib/gi18n-lib.h>
/**
* GtkInfoDialog:
*
* A `GtkInfoDialog` object collects the arguments that
* are needed to present a info chooser dialog to the
* user, such as a title for the dialog and whether it
* should be modal.
*
* The dialog is shown with the [function@Gtk.InfoDialog.present]
* function. This API follows the GIO async pattern, and the
* result can be obtained by calling
* [function@Gtk.InfoDialog.present_finish].
*
* `GtkInfoDialog was added in GTK 4.10.
*/
/* {{{ GObject implementation */
struct _GtkInfoDialog
{
GObject parent_instance;
char *heading;
char *body;
char **buttons;
unsigned int modal : 1;
unsigned int heading_use_markup: 1;
unsigned int body_use_markup: 1;
};
enum
{
PROP_MODAL = 1,
PROP_HEADING,
PROP_HEADING_USE_MARKUP,
PROP_BODY,
PROP_BODY_USE_MARKUP,
PROP_BUTTONS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkInfoDialog, gtk_info_dialog, G_TYPE_OBJECT)
static void
gtk_info_dialog_init (GtkInfoDialog *self)
{
self->modal = TRUE;
}
static void
gtk_info_dialog_finalize (GObject *object)
{
GtkInfoDialog *self = GTK_INFO_DIALOG (object);
g_free (self->heading);
g_free (self->body);
g_strfreev (self->buttons);
G_OBJECT_CLASS (gtk_info_dialog_parent_class)->finalize (object);
}
static void
gtk_info_dialog_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
GtkInfoDialog *self = GTK_INFO_DIALOG (object);
switch (property_id)
{
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_HEADING:
g_value_set_string (value, self->heading);
break;
case PROP_HEADING_USE_MARKUP:
g_value_set_boolean (value, self->heading_use_markup);
break;
case PROP_BODY:
g_value_set_string (value, self->body);
break;
case PROP_BODY_USE_MARKUP:
g_value_set_boolean (value, self->body_use_markup);
break;
case PROP_BUTTONS:
g_value_set_boxed (value, self->buttons);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_info_dialog_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkInfoDialog *self = GTK_INFO_DIALOG (object);
switch (prop_id)
{
case PROP_MODAL:
gtk_info_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_HEADING:
gtk_info_dialog_set_heading (self, g_value_get_string (value));
break;
case PROP_HEADING_USE_MARKUP:
gtk_info_dialog_set_heading_use_markup (self, g_value_get_boolean (value));
break;
case PROP_BODY:
gtk_info_dialog_set_body (self, g_value_get_string (value));
break;
case PROP_BODY_USE_MARKUP:
gtk_info_dialog_set_body_use_markup (self, g_value_get_boolean (value));
break;
case PROP_BUTTONS:
gtk_info_dialog_set_buttons (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_info_dialog_class_init (GtkInfoDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_info_dialog_finalize;
object_class->get_property = gtk_info_dialog_get_property;
object_class->set_property = gtk_info_dialog_set_property;
/**
* GtkInfoDialog:modal: (attributes org.gtk.Property.get=gtk_info_dialog_get_modal org.gtk.Property.set=gtk_color_dialog_set_modal)
*
* Whether the info chooser dialog is modal.
*
* Since: 4.10
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:heading: (attributes org.gtk.Property.get=gtk_info_dialog_get_heading org.gtk.Property.set=gtk_color_dialog_set_heading)
*
* The heading for the dialog that is presented
* by [function@Gtk.InfoDialog.present].
*
* Since: 4.10
*/
properties[PROP_HEADING] =
g_param_spec_string ("heading", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:heading-use-markup: (attributes org.gtk.Property.get=gtk_info_dialog_get_heading_use_markup org.gtk.Property.set=gtk_color_dialog_set_heading_use_markup)
*
* Whether the heading uses markup.
*
* Since: 4.10
*/
properties[PROP_HEADING_USE_MARKUP] =
g_param_spec_boolean ("heading-use-markup", NULL, NULL,
FALSE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:body: (attributes org.gtk.Property.get=gtk_info_dialog_get_body org.gtk.Property.set=gtk_color_dialog_set_body)
*
* The body text for the dialog that is presented
* by [function@Gtk.InfoDialog.present].
*
* Since: 4.10
*/
properties[PROP_BODY] =
g_param_spec_string ("body", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:body-use-markup: (attributes org.gtk.Property.get=gtk_info_dialog_get_body_use_markup org.gtk.Property.set=gtk_color_dialog_set_body_use_markup)
*
* Whether the body text uses markup.
*
* Since: 4.10
*/
properties[PROP_BODY_USE_MARKUP] =
g_param_spec_boolean ("body-use-markup", NULL, NULL,
FALSE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:buttons: (attributes org.gtk.Property.get=gtk_info_dialog_get_buttons org.gtk.Property.set=gtk_color_dialog_set_buttons)
*
* The labels for buttons to show in the dialog that is presented
* by [function@Gtk.InfoDialog.present].
*
* The labels should be translated and may contain a _ to indicate
* mnemonic characters.
*
* Since: 4.10
*/
properties[PROP_BUTTONS] =
g_param_spec_boxed ("buttons", NULL, NULL,
G_TYPE_STRV,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ Constructor */
/**
* gtk_info_dialog_new:
*
* Creates a new `GtkInfoDialog` object.
*
* Returns: the new `GtkInfoDialog`
*
* Since: 4.10
*/
GtkInfoDialog *
gtk_info_dialog_new (void)
{
return g_object_new (GTK_TYPE_INFO_DIALOG, NULL);
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_info_dialog_get_modal:
* @self: a `GtkInfoDialog`
*
* Returns whether the info chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Returns: `TRUE` if the info chooser dialog is modal
*
* Since: 4.10
*/
gboolean
gtk_info_dialog_get_modal (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_info_dialog_set_modal:
* @self: a `GtkInfoDialog`
* @modal: the new value
*
* Sets whether the info chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_modal (GtkInfoDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_info_dialog_get_heading:
* @self: a `GtkInfoDialog`
*
* Returns the heading that will be shown in the
* info dialog.
*
* Returns: the heading
*
* Since: 4.10
*/
const char *
gtk_info_dialog_get_heading (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), NULL);
return self->heading;
}
/**
* gtk_info_dialog_set_heading:
* @self: a `GtkInfoDialog`
* @text: the new heading
*
* Sets the heading that will be shown in the
* info dialog.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_heading (GtkInfoDialog *self,
const char *text)
{
char *new_text;
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
g_return_if_fail (text != NULL);
if (g_strcmp0 (self->heading, text) == 0)
return;
new_text = g_strdup (text);
g_free (self->heading);
self->heading = new_text;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEADING]);
}
/**
* gtk_info_dialog_get_heading_use_markup:
* @self: a `GtkInfoDialog`
*
* Returns whether the heading uses markup.
*
* Returns: `TRUE` if the heading uses markup
*
* Since: 4.10
*/
gboolean
gtk_info_dialog_get_heading_use_markup (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), FALSE);
return self->heading_use_markup;
}
/**
* gtk_info_dialog_set_heading_use_markup:
* @self: a `GtkInfoDialog`
* @use_markup: the new value
*
* Sets whether the heading uses markup.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_heading_use_markup (GtkInfoDialog *self,
gboolean use_markup)
{
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
if (self->heading_use_markup == use_markup)
return;
self->heading_use_markup = use_markup;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEADING_USE_MARKUP]);
}
/**
* gtk_info_dialog_get_body:
* @self: a `GtkInfoDialog`
*
* Returns the body text that will be shown
* in the info dialog.
*
* Returns: the body text
*
* Since: 4.10
*/
const char *
gtk_info_dialog_get_body (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), NULL);
return self->body;
}
/**
* gtk_info_dialog_set_body:
* @self: a `GtkInfoDialog`
* @text: the new text
*
* Sets the body text that will be shown
* in the info dialog.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_body (GtkInfoDialog *self,
const char *text)
{
char *new_text;
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
g_return_if_fail (text != NULL);
if (g_strcmp0 (self->body, text) == 0)
return;
new_text = g_strdup (text);
g_free (self->body);
self->body = new_text;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BODY]);
}
/**
* gtk_info_dialog_get_body_use_markup:
* @self: a `GtkInfoDialog`
*
* Returns whether the body text uses markup.
*
* Returns: `TRUE` if body text uses markup
*
* Since: 4.10
*/
gboolean
gtk_info_dialog_get_body_use_markup (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), FALSE);
return self->body_use_markup;
}
/**
* gtk_info_dialog_set_body_use_markup:
* @self: a `GtkInfoDialog`
* @use_markup: the new value
*
* Sets whether the body text uses markup.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_body_use_markup (GtkInfoDialog *self,
gboolean use_markup)
{
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
if (self->body_use_markup == use_markup)
return;
self->body_use_markup = use_markup;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BODY_USE_MARKUP]);
}
/**
* gtk_info_dialog_get_buttons:
* @self: a `GtkInfoDialog`
*
* Returns the button labels for the info dialog.
*
* Returns: (nullable) (transfer none): the button labels
*
* Since: 4.10
*/
const char * const *
gtk_info_dialog_get_buttons (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), NULL);
return (const char * const *) self->buttons;
}
/**
* gtk_info_dialog_set_buttons:
* @self: a `GtkInfoDialog`
* @labels: the new button labels
*
* Sets the button labels for the info dialog.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_buttons (GtkInfoDialog *self,
const char * const *labels)
{
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
g_return_if_fail (labels != NULL);
g_strfreev (self->buttons);
self->buttons = g_strdupv ((char **)labels);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BUTTONS]);
}
/* }}} */
/* {{{ Convenience API */
/**
* gtk_info_dialog_set_heading_markup:
* @self: a `GtkInfoDialog`
* @text: the new heading
*
* Sets the heading that will be shown in the
* info dialog, and marks it as using markup.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_heading_markup (GtkInfoDialog *self,
const char *text)
{
g_object_freeze_notify (G_OBJECT (self));
gtk_info_dialog_set_heading (self, text);
gtk_info_dialog_set_heading_use_markup (self, TRUE);
g_object_thaw_notify (G_OBJECT (self));
}
/**
* gtk_info_dialog_set_body_markup:
* @self: a `GtkInfoDialog`
* @text: the new heading
*
* Sets the body text that will be shown in the
* info dialog, and marks it as using markup.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_body_markup (GtkInfoDialog *self,
const char *text)
{
g_object_freeze_notify (G_OBJECT (self));
gtk_info_dialog_set_body (self, text);
gtk_info_dialog_set_body_use_markup (self, TRUE);
g_object_thaw_notify (G_OBJECT (self));
}
/* }}} */
/* {{{ Async API */
static void response_cb (GTask *task,
int response);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
response_cb (task, -1);
}
static void
response_cb (GTask *task,
int response)
{
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response >= 0)
g_task_return_int (task, response);
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
g_object_unref (task);
}
static void
dialog_response (GtkDialog *dialog,
int response,
GTask *task)
{
response_cb (task, response);
}
/**
* gtk_info_dialog_present:
* @self: a `GtkInfoDialog`
* @parent: (nullable): the parent `GtkWindow`
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function presents an info dialog to the user.
*
* The @callback will be called when the dialog is dismissed.
* It should call [function@Gtk.InfoDialog.present_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_info_dialog_present (GtkInfoDialog *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkMessageDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
window = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
"transient-for", parent,
"destroy-with-parent", TRUE,
"modal", self->modal,
"text", self->heading,
"use-markup", self->heading_use_markup,
"secondary-text", self->body,
"secondary-use-markup", self->body_use_markup,
NULL);
if (self->buttons)
{
for (int i = 0; self->buttons[i]; i++)
gtk_dialog_add_button (GTK_DIALOG (window), self->buttons[i], i);
}
else
gtk_dialog_add_button (GTK_DIALOG (window), _("Close"), 0);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_info_dialog_present);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
/**
* gtk_info_dialog_present_finish:
* @self: a `GtkInfoDialog`
* @result: a `GAsyncResult`
* @button: (out caller-allocates): return location for button
* @error: return location for an error
*
* Finishes the [function@Gtk.InfoDialog.present] call
* and returns the button that was clicked.
*
* Returns: `TRUE` if a button was clicked.
* Otherwise, `FALSE` is returned and @error is set
*
* Since: 4.10
*/
gboolean
gtk_info_dialog_present_finish (GtkInfoDialog *self,
GAsyncResult *result,
int *button,
GError **error)
{
*button = (int) g_task_propagate_int (G_TASK (result), error);
return ! (error && *error);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

104
gtk/gtkinfodialog.h Normal file
View File

@@ -0,0 +1,104 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* 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/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
G_BEGIN_DECLS
#define GTK_TYPE_INFO_DIALOG (gtk_info_dialog_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkInfoDialog, gtk_info_dialog, GTK, INFO_DIALOG, GObject)
GDK_AVAILABLE_IN_4_10
GtkInfoDialog * gtk_info_dialog_new (void);
GDK_AVAILABLE_IN_4_10
gboolean gtk_info_dialog_get_modal (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_modal (GtkInfoDialog *self,
gboolean modal);
GDK_AVAILABLE_IN_4_10
const char * gtk_info_dialog_get_heading (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_heading (GtkInfoDialog *self,
const char *text);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_heading_markup (GtkInfoDialog *self,
const char *text);
GDK_AVAILABLE_IN_4_10
gboolean gtk_info_dialog_get_heading_use_markup
(GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_heading_use_markup
(GtkInfoDialog *self,
gboolean use_markup);
GDK_AVAILABLE_IN_4_10
const char * gtk_info_dialog_get_body (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_body (GtkInfoDialog *self,
const char *text);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_body_markup (GtkInfoDialog *self,
const char *text);
GDK_AVAILABLE_IN_4_10
gboolean gtk_info_dialog_get_body_use_markup (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_body_use_markup (GtkInfoDialog *self,
gboolean use_markup);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_buttons (GtkInfoDialog *self,
const char * const *labels);
GDK_AVAILABLE_IN_4_10
const char * const *
gtk_info_dialog_get_buttons (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_present (GtkInfoDialog *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_info_dialog_present_finish (GtkInfoDialog *self,
GAsyncResult *result,
int *button,
GError **error);
G_END_DECLS

View File

@@ -520,8 +520,6 @@ on_remove_server_button_clicked (RemoveServerData *data)
populate_servers (data->view);
}
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static void
populate_servers (GtkPlacesView *view)
{
@@ -617,8 +615,6 @@ populate_servers (GtkPlacesView *view)
g_bookmark_file_free (server_list);
}
G_GNUC_END_IGNORE_DEPRECATIONS
static void
update_view_mode (GtkPlacesView *view)
{

View File

@@ -24,12 +24,11 @@
#include <gdk/gdk.h>
#include "gtksearchenginemodelprivate.h"
#include "gtkfilechooserutils.h"
#include "gtkprivate.h"
#include <string.h>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
struct _GtkSearchEngineModel
{
GtkSearchEngine parent;
@@ -81,37 +80,33 @@ static gboolean
do_search (gpointer data)
{
GtkSearchEngineModel *model = data;
GtkTreeIter iter;
GList *hits = NULL;
gboolean got_results = FALSE;
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model->model), &iter))
for (guint i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model->model)); i++)
{
do
GFileInfo *info = g_list_model_get_item (G_LIST_MODEL (model->model), i);
if (info_matches_query (model->query, info))
{
GFileInfo *info;
GFile *file;
GtkSearchHit *hit;
info = _gtk_file_system_model_get_info (model->model, &iter);
if (info_matches_query (model->query, info))
{
GFile *file;
GtkSearchHit *hit;
file = _gtk_file_system_model_get_file (model->model, &iter);
hit = g_new (GtkSearchHit, 1);
hit->file = g_object_ref (file);
hit->info = g_object_ref (info);
hits = g_list_prepend (hits, hit);
}
file = _gtk_file_info_get_file (info);
hit = g_new (GtkSearchHit, 1);
hit->file = g_object_ref (file);
hit->info = g_object_ref (info);
hits = g_list_prepend (hits, hit);
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model->model), &iter));
if (hits)
{
_gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (model), hits);
g_list_free_full (hits, (GDestroyNotify)_gtk_search_hit_free);
got_results = TRUE;
}
g_clear_object (&info);
}
if (hits)
{
_gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (model), hits);
g_list_free_full (hits, (GDestroyNotify)_gtk_search_hit_free);
got_results = TRUE;
}
model->idle = 0;

View File

@@ -114,7 +114,8 @@ free_hit (gpointer data)
}
static GFileInfo *
create_file_info (TrackerSparqlCursor *cursor)
create_file_info (GFile *file,
TrackerSparqlCursor *cursor)
{
GFileInfo *info;
const char *str;
@@ -140,6 +141,10 @@ create_file_info (TrackerSparqlCursor *cursor)
g_date_time_unref (creation);
}
g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
g_file_info_set_attribute_boolean (info, "filechooser::filtered-out", FALSE);
g_file_info_set_attribute_boolean (info, "filechooser::visible", TRUE);
return info;
}
@@ -175,7 +180,7 @@ query_callback (TrackerSparqlStatement *statement,
url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
hit = g_slice_new0 (GtkSearchHit);
hit->file = g_file_new_for_uri (url);
hit->info = create_file_info (cursor);
hit->info = create_file_info (hit->file, cursor);
hits = g_list_prepend (hits, hit);
}

View File

@@ -1259,13 +1259,13 @@ property_editor (GObject *object,
g_strdup_printf ("%s: %s",
self->name,
gtk_label_get_text (GTK_LABEL (prop_edit))),
NULL);
-1);
}
else
{
gtk_accessible_update_property (GTK_ACCESSIBLE (prop_edit),
GTK_ACCESSIBLE_PROPERTY_LABEL, self->name,
NULL);
-1);
}
return prop_edit;

View File

@@ -72,7 +72,7 @@ add_string (GtkInspectorStrvEditor *editor,
gtk_editable_set_text (GTK_EDITABLE (entry), str);
gtk_accessible_update_property (GTK_ACCESSIBLE (entry),
GTK_ACCESSIBLE_PROPERTY_LABEL, _("Value"),
NULL);
-1);
gtk_widget_show (entry);
gtk_box_append (GTK_BOX (box), entry);
g_object_set_data (G_OBJECT (box), "entry", entry);
@@ -83,7 +83,7 @@ add_string (GtkInspectorStrvEditor *editor,
gtk_accessible_update_property (GTK_ACCESSIBLE (button),
GTK_ACCESSIBLE_PROPERTY_LABEL,
g_strdup_printf (_("Remove %s"), str),
NULL);
-1);
gtk_widget_show (button);
gtk_box_append (GTK_BOX (box), button);
g_signal_connect (button, "clicked", G_CALLBACK (remove_string), editor);
@@ -116,7 +116,7 @@ gtk_inspector_strv_editor_init (GtkInspectorStrvEditor *editor)
gtk_widget_set_halign (editor->button, GTK_ALIGN_END);
gtk_accessible_update_property (GTK_ACCESSIBLE (editor->button),
GTK_ACCESSIBLE_PROPERTY_LABEL, _("Add"),
NULL);
-1);
gtk_widget_show (editor->button);
g_signal_connect (editor->button, "clicked", G_CALLBACK (add_cb), editor);

View File

@@ -108,7 +108,10 @@ gtk_private_sources = files([
'gtkfilechoosererrorstack.c',
'gtkfilechoosernativeportal.c',
'gtkfilechooserutils.c',
'gtkfilechoosercell.c',
'gtkfiledialog.c',
'gtkfilesystemmodel.c',
'gtkfilethumbnail.c',
'gtkgizmo.c',
'gtkiconcache.c',
'gtkiconcachevalidator.c',
@@ -180,10 +183,13 @@ gtk_public_sources = files([
'gtkcenterbox.c',
'gtkcenterlayout.c',
'gtkcheckbutton.c',
'gtkchoice.c',
'gtkcolorbutton.c',
'gtkcolorchooser.c',
'gtkcolorchooserdialog.c',
'gtkcolorchooserwidget.c',
'gtkcolordialog.c',
'gtkcolordialogbutton.c',
'gtkcolorutils.c',
'gtkcolumnview.c',
'gtkcolumnviewcolumn.c',
@@ -235,6 +241,8 @@ gtk_public_sources = files([
'gtkfontchooserdialog.c',
'gtkfontchooserutils.c',
'gtkfontchooserwidget.c',
'gtkfontdialog.c',
'gtkfontdialogbutton.c',
'gtkframe.c',
'gtkgesture.c',
'gtkgesturedrag.c',
@@ -259,6 +267,7 @@ gtk_public_sources = files([
'gtkimmodule.c',
'gtkimmulticontext.c',
'gtkinfobar.c',
'gtkinfodialog.c',
'gtkinscription.c',
'gtklabel.c',
'gtklayoutchild.c',
@@ -436,10 +445,13 @@ gtk_public_headers = files([
'gtkcenterbox.h',
'gtkcenterlayout.h',
'gtkcheckbutton.h',
'gtkchoice.h',
'gtkcolorbutton.h',
'gtkcolorchooser.h',
'gtkcolorchooserdialog.h',
'gtkcolorchooserwidget.h',
'gtkcolordialog.h',
'gtkcolordialogbutton.h',
'gtkcolorutils.h',
'gtkcolumnview.h',
'gtkcolumnviewcolumn.h',
@@ -479,6 +491,7 @@ gtk_public_headers = files([
'gtkfilechooserdialog.h',
'gtkfilechoosernative.h',
'gtkfilechooserwidget.h',
'gtkfiledialog.h',
'gtkfilefilter.h',
'gtkfilter.h',
'gtkfilterlistmodel.h',
@@ -490,6 +503,8 @@ gtk_public_headers = files([
'gtkfontchooser.h',
'gtkfontchooserdialog.h',
'gtkfontchooserwidget.h',
'gtkfontdialog.h',
'gtkfontdialogbutton.h',
'gtkframe.h',
'gtkgesture.h',
'gtkgesturedrag.h',
@@ -513,6 +528,7 @@ gtk_public_headers = files([
'gtkimmodule.h',
'gtkimmulticontext.h',
'gtkinfobar.h',
'gtkinfodialog.h',
'gtkinscription.h',
'gtklabel.h',
'gtklayoutchild.h',

View File

@@ -3369,6 +3369,30 @@ columnview row:not(:selected) cell editablelabel.editing text selection {
}
}
/********************************************************
* Complex Lists *
* Put padding on the cell content so event controllers *
* can cover the whole area. *
********************************************************/
columnview.complex {
> listview > row > cell {
padding: 0;
> * {
padding: 8px 6px;
}
}
// shrink vertically for .data-table
&.data-table > listview > row > cell {
padding: 0;
> * {
padding-top: 2px;
padding-bottom: 2px;
}
}
}
/*********************
* App Notifications *
*********************/

View File

@@ -140,103 +140,278 @@
<property name="hscrollbar-policy">2</property>
<property name="vexpand">1</property>
<child>
<object class="GtkTreeView" id="browse_files_tree_view">
<property name="has-tooltip">1</property>
<property name="enable-search">0</property>
<object class="GtkColumnView" id="browse_files_column_view">
<style>
<class name="complex"/>
</style>
<signal name="activate" handler="column_view_row_activated_cb" swapped="no"/>
<signal name="keynav-failed" handler="browse_files_column_view_keynav_failed_cb"/>
<child>
<object class="GtkGestureLongPress">
<property name="touch-only">1</property>
<signal name="pressed" handler="long_press_cb" swapped="no"/>
</object>
</child>
<child>
<object class="GtkGestureClick">
<property name="button">3</property>
<signal name="pressed" handler="click_cb" swapped="no"/>
</object>
</child>
<signal name="query-tooltip" handler="file_list_query_tooltip_cb" swapped="no"/>
<signal name="row-activated" handler="list_row_activated" swapped="no"/>
<signal name="keynav-failed" handler="browse_files_tree_view_keynav_failed_cb"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection2">
<signal name="changed" handler="list_selection_changed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="list_name_column">
<object class="GtkColumnViewColumn" id="column_view_name_column">
<property name="title" translatable="yes">Name</property>
<property name="resizable">1</property>
<property name="expand">1</property>
<child>
<object class="GtkCellRendererPixbuf" id="list_pixbuf_renderer">
<property name="xpad">6</property>
<property name="resizable">1</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkFileChooserCell">
<binding name="position">
<lookup name="position">GtkListItem</lookup>
</binding>
<binding name="item">
<lookup name="item">GtkListItem</lookup>
</binding>
<binding name="selected">
<lookup name="selected">GtkListItem</lookup>
</binding>
<child>
<object class="GtkBox">
<binding name="tooltip-text">
<closure type="gchararray" function="column_view_get_tooltip_text">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
<child>
<object class="GtkFileThumbnail">
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<binding name="file-info">
<lookup name="item">GtkListItem</lookup>
</binding>
</object>
</child>
<child>
<object class="GtkInscription">
<property name="hexpand">1</property>
<property name="xalign">0</property>
<property name="min-chars">10</property>
<binding name="text">
<closure type="gchararray" function="column_view_get_file_display_name">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</child>
<child>
<object class="GtkCellRendererText" id="list_name_renderer">
<property name="width-chars">10</property>
<property name="ellipsize">3</property>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="list_location_column">
<object class="GtkColumnViewColumn" id="column_view_location_column">
<property name="title" translatable="yes">Location</property>
<property name="resizable">1</property>
<property name="visible">0</property>
<property name="expand">1</property>
<child>
<object class="GtkCellRendererText" id="list_location_renderer">
<property name="xalign">0</property>
<property name="width-chars">10</property>
<property name="ellipsize">1</property>
<property name="xpad">6</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkFileChooserCell">
<binding name="position">
<lookup name="position">GtkListItem</lookup>
</binding>
<binding name="item">
<lookup name="item">GtkListItem</lookup>
</binding>
<binding name="selected">
<lookup name="selected">GtkListItem</lookup>
</binding>
<child>
<object class="GtkInscription">
<property name="hexpand">1</property>
<property name="xalign">0</property>
<property name="min-chars">10</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<binding name="text">
<closure type="gchararray" function="column_view_get_location">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
<binding name="tooltip-text">
<closure type="gchararray" function="column_view_get_tooltip_text">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="list_size_column">
<object class="GtkColumnViewColumn" id="column_view_size_column">
<property name="title" translatable="yes">Size</property>
<property name="sizing">2</property>
<child>
<object class="GtkCellRendererText" id="list_size_renderer">
<property name="xalign">0</property>
<property name="xpad">6</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkFileChooserCell">
<binding name="position">
<lookup name="position">GtkListItem</lookup>
</binding>
<binding name="item">
<lookup name="item">GtkListItem</lookup>
</binding>
<binding name="selected">
<lookup name="selected">GtkListItem</lookup>
</binding>
<child>
<object class="GtkLabel">
<property name="hexpand">1</property>
<property name="xalign">0</property>
<binding name="label">
<closure type="gchararray" function="column_view_get_size">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
<binding name="tooltip-text">
<closure type="gchararray" function="column_view_get_tooltip_text">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="list_type_column">
<object class="GtkColumnViewColumn" id="column_view_type_column">
<property name="title" translatable="yes">Type</property>
<property name="resizable">1</property>
<child>
<object class="GtkCellRendererText" id="list_type_renderer">
<property name="xalign">0</property>
<property name="xpad">6</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkFileChooserCell">
<binding name="position">
<lookup name="position">GtkListItem</lookup>
</binding>
<binding name="item">
<lookup name="item">GtkListItem</lookup>
</binding>
<binding name="selected">
<lookup name="selected">GtkListItem</lookup>
</binding>
<child>
<object class="GtkLabel">
<property name="hexpand">1</property>
<property name="xalign">0</property>
<binding name="label">
<closure type="gchararray" function="column_view_get_file_type">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
<binding name="tooltip-text">
<closure type="gchararray" function="column_view_get_tooltip_text">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="list_time_column">
<object class="GtkColumnViewColumn" id="column_view_time_column">
<property name="title" translatable="yes">Modified</property>
<property name="sizing">2</property>
<child>
<object class="GtkCellRendererText" id="list_date_renderer">
<property name="xpad">6</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkFileChooserCell" id="file_chooser_cell">
<binding name="position">
<lookup name="position">GtkListItem</lookup>
</binding>
<binding name="item">
<lookup name="item">GtkListItem</lookup>
</binding>
<binding name="selected">
<lookup name="selected">GtkListItem</lookup>
</binding>
<child>
<object class="GtkBox">
<property name="spacing">6</property>
<binding name="tooltip-text">
<closure type="gchararray" function="column_view_get_tooltip_text">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
<child>
<object class="GtkLabel">
<binding name="label">
<closure type="gchararray" function="column_view_get_file_date">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="visible" bind-source="file_chooser_cell" bind-property="show-time" bind-flags="sync-create"/>
<binding name="label">
<closure type="gchararray" function="column_view_get_file_time">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</child>
<child>
<object class="GtkCellRendererText" id="list_time_renderer">
<property name="xpad">6</property>
</object>
</child>
</property>
</object>
</child>
</object>
</child>
</object>

View File

@@ -10,7 +10,7 @@ project('gtk', 'c',
meson_version : '>= 0.60.0',
license: 'LGPL-2.1-or-later')
glib_req = '>= 2.66.0'
glib_req = '>= 2.72.0'
pango_req = '>= 1.50.0' # keep this in sync with .gitlab-ci/test-msys.sh
harfbuzz_req = '>= 2.6.0'
fribidi_req = '>= 0.19.7'

808
po/sr.po

File diff suppressed because it is too large Load Diff